Bug 977181 - Don't push/assume IonContext when executing JIT code (r=jandem)
authorLuke Wagner <luke@mozilla.com>
Wed, 26 Feb 2014 17:38:24 -0600
changeset 171187 2fb5b3b138d72d15119eadd6641e80776c0dca56
parent 171186 8e126867f92618c084416eceac6d1ef3c4bd3dd8
child 171188 1cec924f373467fa6ae86d0177ba74678bb91090
push id270
push userpvanderbeken@mozilla.com
push dateThu, 06 Mar 2014 09:24:21 +0000
reviewersjandem
bugs977181
milestone30.0a1
Bug 977181 - Don't push/assume IonContext when executing JIT code (r=jandem)
js/src/jit/AsmJSLink.cpp
js/src/jit/Bailouts.cpp
js/src/jit/BaselineBailouts.cpp
js/src/jit/BaselineFrame.cpp
js/src/jit/BaselineIC.cpp
js/src/jit/BaselineJIT.cpp
js/src/jit/Ion.cpp
js/src/jit/IonFrames.cpp
js/src/vm/Interpreter-inl.h
js/src/vm/Runtime.h
--- a/js/src/jit/AsmJSLink.cpp
+++ b/js/src/jit/AsmJSLink.cpp
@@ -421,17 +421,16 @@ CallAsmJS(JSContext *cx, unsigned argc, 
         // pushed on a stack maintained by the runtime. This record is used for
         // to handle a variety of exceptional things that can happen in asm.js
         // code.
         AsmJSActivation activation(cx, module);
 
         // Eagerly push an IonContext+JitActivation so that the optimized
         // asm.js-to-Ion FFI call path (which we want to be very fast) can
         // avoid doing so.
-        jit::IonContext ictx(cx, nullptr);
         JitActivation jitActivation(cx, /* firstFrameIsConstructing = */ false, /* active */ false);
 
         // Call the per-exported-function trampoline created by GenerateEntry.
         AsmJSModule::CodePtr enter = module.entryTrampoline(func);
         if (!CALL_GENERATED_ASMJS(enter, coercedArgs.begin(), module.globalData()))
             return false;
     }
 
--- a/js/src/jit/Bailouts.cpp
+++ b/js/src/jit/Bailouts.cpp
@@ -44,33 +44,34 @@ SnapshotIterator::SnapshotIterator(const
     ionScript_(iter.ionScript())
 {
 }
 
 void
 IonBailoutIterator::dump() const
 {
     if (type_ == IonFrame_OptimizedJS) {
-        InlineFrameIterator frames(GetIonContext()->cx, this);
+        InlineFrameIterator frames(GetJSContextFromJitCode(), this);
         for (;;) {
             frames.dump();
             if (!frames.more())
                 break;
             ++frames;
         }
     } else {
         IonFrameIterator::dump();
     }
 }
 
 uint32_t
 jit::Bailout(BailoutStack *sp, BaselineBailoutInfo **bailoutInfo)
 {
+    JSContext *cx = GetJSContextFromJitCode();
     JS_ASSERT(bailoutInfo);
-    JSContext *cx = GetIonContext()->cx;
+
     // We don't have an exit frame.
     cx->mainThread().ionTop = nullptr;
     JitActivationIterator jitActivations(cx->runtime());
     IonBailoutIterator iter(jitActivations, sp);
     JitActivation *activation = jitActivations.activation()->asJit();
 
     IonSpew(IonSpew_Bailouts, "Took bailout! Snapshot offset: %d", iter.snapshotOffset());
 
@@ -90,17 +91,17 @@ jit::Bailout(BailoutStack *sp, BaselineB
 }
 
 uint32_t
 jit::InvalidationBailout(InvalidationBailoutStack *sp, size_t *frameSizeOut,
                          BaselineBailoutInfo **bailoutInfo)
 {
     sp->checkInvariants();
 
-    JSContext *cx = GetIonContext()->cx;
+    JSContext *cx = GetJSContextFromJitCode();
 
     // We don't have an exit frame.
     cx->mainThread().ionTop = nullptr;
     JitActivationIterator jitActivations(cx->runtime());
     IonBailoutIterator iter(jitActivations, sp);
     JitActivation *activation = jitActivations.activation()->asJit();
 
     IonSpew(IonSpew_Bailouts, "Took invalidation bailout! Snapshot offset: %d", iter.snapshotOffset());
--- a/js/src/jit/BaselineBailouts.cpp
+++ b/js/src/jit/BaselineBailouts.cpp
@@ -1411,17 +1411,17 @@ HandleBaselineInfoBailout(JSContext *cx,
     return Invalidate(cx, outerScript);
 }
 
 uint32_t
 jit::FinishBailoutToBaseline(BaselineBailoutInfo *bailoutInfo)
 {
     // The caller pushes R0 and R1 on the stack without rooting them.
     // Since GC here is very unlikely just suppress it.
-    JSContext *cx = GetIonContext()->cx;
+    JSContext *cx = GetJSContextFromJitCode();
     js::gc::AutoSuppressGC suppressGC(cx);
 
     IonSpew(IonSpew_BaselineBailouts, "  Done restoring frames");
 
     // Check that we can get the current script's PC.
 #ifdef DEBUG
     jsbytecode *pc;
     cx->currentScript(&pc);
--- a/js/src/jit/BaselineFrame.cpp
+++ b/js/src/jit/BaselineFrame.cpp
@@ -178,17 +178,17 @@ BaselineFrame::initForOsr(StackFrame *fp
         BaselineFrame::Size() +
         numStackValues * sizeof(Value);
 
     JS_ASSERT(numValueSlots() == numStackValues);
 
     for (uint32_t i = 0; i < numStackValues; i++)
         *valueSlot(i) = fp->slots()[i];
 
-    JSContext *cx = GetIonContext()->cx;
+    JSContext *cx = GetJSContextFromJitCode();
     if (cx->compartment()->debugMode()) {
         // In debug mode, update any Debugger.Frame objects for the StackFrame to
         // point to the BaselineFrame.
 
         // The caller pushed a fake return address. ScriptFrameIter, used by the
         // debugger, wants a valid return address, but it's okay to just pick one.
         // In debug mode there's always at least 1 ICEntry (since there are always
         // debug prologue/epilogue calls).
--- a/js/src/jit/BaselineIC.cpp
+++ b/js/src/jit/BaselineIC.cpp
@@ -572,16 +572,17 @@ ICStubCompiler::getStubCode()
 
     // Check for existing cached stubcode.
     uint32_t stubKey = getKey();
     JitCode *stubCode = comp->getStubCode(stubKey);
     if (stubCode)
         return stubCode;
 
     // Compile new stubcode.
+    IonContext ictx(cx, nullptr);
     MacroAssembler masm;
 #ifdef JS_CODEGEN_ARM
     masm.setSecondScratchReg(BaselineSecondScratchReg);
 #endif
 
     AutoFlushCache afc("ICStubCompiler::getStubCode", cx->runtime()->jitRuntime());
     if (!generateStubCode(masm))
         return nullptr;
--- a/js/src/jit/BaselineJIT.cpp
+++ b/js/src/jit/BaselineJIT.cpp
@@ -103,17 +103,16 @@ EnterBaseline(JSContext *cx, EnterJitDat
     EnterJitCode enter = cx->runtime()->jitRuntime()->enterBaseline();
 
     // Caller must construct |this| before invoking the Ion function.
     JS_ASSERT_IF(data.constructing, data.maxArgv[0].isObject());
 
     data.result.setInt32(data.numActualArgs);
     {
         AssertCompartmentUnchanged pcc(cx);
-        IonContext ictx(cx, nullptr);
         JitActivation activation(cx, data.constructing);
         JSAutoResolveFlags rf(cx, RESOLVE_INFER);
         AutoFlushInhibitor afi(cx->runtime()->jitRuntime());
 
         if (data.osrFrame)
             data.osrFrame->setRunningInJit();
 
         JS_ASSERT_IF(data.osrFrame, !IsJSDEnabled(cx));
--- a/js/src/jit/Ion.cpp
+++ b/js/src/jit/Ion.cpp
@@ -2222,17 +2222,16 @@ EnterIon(JSContext *cx, EnterJitData &da
     EnterJitCode enter = cx->runtime()->jitRuntime()->enterIon();
 
     // Caller must construct |this| before invoking the Ion function.
     JS_ASSERT_IF(data.constructing, data.maxArgv[0].isObject());
 
     data.result.setInt32(data.numActualArgs);
     {
         AssertCompartmentUnchanged pcc(cx);
-        IonContext ictx(cx, nullptr);
         JitActivation activation(cx, data.constructing);
         JSAutoResolveFlags rf(cx, RESOLVE_INFER);
         AutoFlushInhibitor afi(cx->runtime()->jitRuntime());
 
         CALL_GENERATED_CODE(enter, data.jitcode, data.maxArgc, data.maxArgv, /* osrFrame = */nullptr, data.calleeToken,
                             /* scopeChain = */ nullptr, 0, data.result.address());
     }
 
--- a/js/src/jit/IonFrames.cpp
+++ b/js/src/jit/IonFrames.cpp
@@ -566,17 +566,17 @@ HandleExceptionBaseline(JSContext *cx, c
         }
     }
 
 }
 
 void
 HandleException(ResumeFromException *rfe)
 {
-    JSContext *cx = GetIonContext()->cx;
+    JSContext *cx = GetJSContextFromJitCode();
 
     rfe->kind = ResumeFromException::RESUME_ENTRY_FRAME;
 
     IonSpew(IonSpew_Invalidate, "handling exception");
 
     // Clear any Ion return override that's been set.
     // This may happen if a callVM function causes an invalidation (setting the
     // override), and then fails, bypassing the bailout handlers that would
@@ -1646,17 +1646,17 @@ MachineState::FromBailout(mozilla::Array
 }
 
 template <AllowGC allowGC>
 bool
 InlineFrameIteratorMaybeGC<allowGC>::isConstructing() const
 {
     // Skip the current frame and look at the caller's.
     if (more()) {
-        InlineFrameIteratorMaybeGC<allowGC> parent(GetIonContext()->cx, this);
+        InlineFrameIteratorMaybeGC<allowGC> parent(GetJSContextFromJitCode(), this);
         ++parent;
 
         // Inlined Getters and Setters are never constructing.
         if (IsGetPropPC(parent.pc()) || IsSetPropPC(parent.pc()))
             return false;
 
         // In the case of a JS frame, look up the pc from the snapshot.
         JS_ASSERT(IsCallPC(parent.pc()));
@@ -1676,17 +1676,17 @@ IonFrameIterator::isConstructing() const
 
     // Skip the current frame and look at the caller's.
     do {
         ++parent;
     } while (!parent.done() && !parent.isScripted());
 
     if (parent.isOptimizedJS()) {
         // In the case of a JS frame, look up the pc from the snapshot.
-        InlineFrameIterator inlinedParent(GetIonContext()->cx, &parent);
+        InlineFrameIterator inlinedParent(GetJSContextFromJitCode(), &parent);
 
         //Inlined Getters and Setters are never constructing.
         if (IsGetPropPC(inlinedParent.pc()) || IsSetPropPC(inlinedParent.pc()))
             return false;
 
         JS_ASSERT(IsCallPC(inlinedParent.pc()));
 
         return (JSOp)*inlinedParent.pc() == JSOP_NEW;
@@ -1756,17 +1756,17 @@ IonFrameIterator::dumpBaseline() const
 #endif
     } else {
         fprintf(stderr, "  global frame, no callee\n");
     }
 
     fprintf(stderr, "  file %s line %u\n",
             script()->filename(), (unsigned) script()->lineno());
 
-    JSContext *cx = GetIonContext()->cx;
+    JSContext *cx = GetJSContextFromJitCode();
     RootedScript script(cx);
     jsbytecode *pc;
     baselineScriptAndPc(script.address(), &pc);
 
     fprintf(stderr, "  script = %p, pc = %p (offset %u)\n", (void *)script, pc, uint32_t(script->pcToOffset(pc)));
     fprintf(stderr, "  current op: %s\n", js_CodeName[*pc]);
 
     fprintf(stderr, "  actual args: %d\n", numActualArgs());
@@ -1824,17 +1824,17 @@ InlineFrameIteratorMaybeGC<allowGC>::dum
                 fprintf(stderr, "  scope chain: ");
             else if (i == 1)
                 fprintf(stderr, "  this: ");
             else if (i - 2 < callee()->nargs())
                 fprintf(stderr, "  formal (arg %d): ", i - 2);
             else {
                 if (i - 2 == callee()->nargs() && numActualArgs() > callee()->nargs()) {
                     DumpOp d(callee()->nargs());
-                    forEachCanonicalActualArg(GetIonContext()->cx, d, d.i_, numActualArgs() - d.i_);
+                    forEachCanonicalActualArg(GetJSContextFromJitCode(), d, d.i_, numActualArgs() - d.i_);
                 }
 
                 fprintf(stderr, "  slot %d: ", int(i - 2 - callee()->nargs()));
             }
         } else
             fprintf(stderr, "  slot %u: ", i);
 #ifdef DEBUG
         js_DumpValue(si.maybeRead());
@@ -1861,17 +1861,17 @@ IonFrameIterator::dump() const
         break;
       case IonFrame_BaselineStub:
       case IonFrame_Unwound_BaselineStub:
         fprintf(stderr, " Baseline stub frame\n");
         fprintf(stderr, "  Frame size: %u\n", unsigned(current()->prevFrameLocalSize()));
         break;
       case IonFrame_OptimizedJS:
       {
-        InlineFrameIterator frames(GetIonContext()->cx, this);
+        InlineFrameIterator frames(GetJSContextFromJitCode(), this);
         for (;;) {
             frames.dump();
             if (!frames.more())
                 break;
             ++frames;
         }
         break;
       }
--- a/js/src/vm/Interpreter-inl.h
+++ b/js/src/vm/Interpreter-inl.h
@@ -654,17 +654,16 @@ ReportIfNotFunction(JSContext *cx, Handl
 class FastInvokeGuard
 {
     InvokeArgs args_;
     RootedFunction fun_;
     RootedScript script_;
 #ifdef JS_ION
     // Constructing an IonContext is pretty expensive due to the TLS access,
     // so only do this if we have to.
-    mozilla::Maybe<jit::IonContext> ictx_;
     bool useIon_;
 #endif
 
   public:
     FastInvokeGuard(JSContext *cx, const Value &fval)
       : args_(cx)
       , fun_(cx)
       , script_(cx)
@@ -691,18 +690,16 @@ class FastInvokeGuard
     bool invoke(JSContext *cx) {
 #ifdef JS_ION
         if (useIon_ && fun_) {
             if (!script_) {
                 script_ = fun_->getOrCreateScript(cx);
                 if (!script_)
                     return false;
             }
-            if (ictx_.empty())
-                ictx_.construct(cx, (js::jit::TempAllocator *)nullptr);
             JS_ASSERT(fun_->nonLazyScript() == script_);
 
             jit::MethodStatus status = jit::CanEnterUsingFastInvoke(cx, script_, args_.length());
             if (status == jit::Method_Error)
                 return false;
             if (status == jit::Method_Compiled) {
                 jit::IonExecStatus result = jit::FastInvoke(cx, fun_, args_);
                 if (IsErrorStatus(result))
--- a/js/src/vm/Runtime.h
+++ b/js/src/vm/Runtime.h
@@ -1765,16 +1765,29 @@ struct JSRuntime : public JS::shadow::Ru
             return nullptr;
         largeAllocationFailureCallback();
         return onOutOfMemory(p, bytes);
     }
 };
 
 namespace js {
 
+// When entering JIT code, the calling JSContext* is stored into the thread's
+// PerThreadData. This function retrieves the JSContext with the pre-condition
+// that the caller is JIT code or C++ called directly from JIT code. This
+// function should not be called from arbitrary locations since the JSContext
+// may be the wrong one.
+static inline JSContext *
+GetJSContextFromJitCode()
+{
+    JSContext *cx = TlsPerThreadData.get()->ionJSContext;
+    JS_ASSERT(cx);
+    return cx;
+}
+
 /*
  * Flags accompany script version data so that a) dynamically created scripts
  * can inherit their caller's compile-time properties and b) scripts can be
  * appropriately compared in the eval cache across global option changes. An
  * example of the latter is enabling the top-level-anonymous-function-is-error
  * option: subsequent evals of the same, previously-valid script text may have
  * become invalid.
  */