Bug 823884: Make call uninlineable when aborting during inlining, r=nbp
authorHannes Verschore <hv1989@gmail.com>
Mon, 07 Jan 2013 19:25:59 +0100
changeset 117968 4dcb5963e1ce2ab89186333eccca1676e7b8abdb
parent 117967 b51c3d8193648a181d50ac5b665f6effe1be143f
child 117969 cb938ddc272e4628a84a79df0dbf5543ab0f1109
push id20763
push userhv1989@gmail.com
push dateMon, 07 Jan 2013 18:26:20 +0000
treeherdermozilla-inbound@4dcb5963e1ce [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersnbp
bugs823884
milestone20.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 823884: Make call uninlineable when aborting during inlining, r=nbp
js/src/ion/Ion.cpp
js/src/ion/Ion.h
js/src/ion/IonBuilder.cpp
js/src/ion/IonBuilder.h
js/src/jsanalyze.h
js/src/methodjit/StubCalls.cpp
--- a/js/src/ion/Ion.cpp
+++ b/js/src/ion/Ion.cpp
@@ -1067,18 +1067,18 @@ class AutoDestroyAllocator
 };
 
 class SequentialCompileContext {
 public:
     ExecutionMode executionMode() {
         return SequentialExecution;
     }
 
-    bool compile(IonBuilder *builder, MIRGraph *graph,
-                 AutoDestroyAllocator &autoDestroy);
+    AbortReason compile(IonBuilder *builder, MIRGraph *graph,
+                        AutoDestroyAllocator &autoDestroy);
 };
 
 void
 AttachFinishedCompilations(JSContext *cx)
 {
 #ifdef JS_THREADSAFE
     AssertCanGC();
     IonCompartment *ion = cx->compartment->ionCompartment();
@@ -1134,86 +1134,88 @@ AttachFinishedCompilations(JSContext *cx
 
     compilations.clear();
 #endif
 }
 
 static const size_t BUILDER_LIFO_ALLOC_PRIMARY_CHUNK_SIZE = 1 << 12;
 
 template <typename CompileContext>
-static bool
+static AbortReason
 IonCompile(JSContext *cx, HandleScript script, HandleFunction fun, jsbytecode *osrPc, bool constructing,
            CompileContext &compileContext)
 {
     AssertCanGC();
 #if JS_TRACE_LOGGING
     AutoTraceLog logger(TraceLogging::defaultLogger(),
                         TraceLogging::ION_COMPILE_START,
                         TraceLogging::ION_COMPILE_STOP,
                         script);
 #endif
 
     LifoAlloc *alloc = cx->new_<LifoAlloc>(BUILDER_LIFO_ALLOC_PRIMARY_CHUNK_SIZE);
     if (!alloc)
-        return false;
+        return AbortReason_Alloc;
 
     AutoDestroyAllocator autoDestroy(alloc);
 
     TempAllocator *temp = alloc->new_<TempAllocator>(alloc);
     if (!temp)
-        return false;
+        return AbortReason_Alloc;
 
     IonContext ictx(cx, cx->compartment, temp);
 
     if (!cx->compartment->ensureIonCompartmentExists(cx))
-        return false;
+        return AbortReason_Alloc;
 
     MIRGraph *graph = alloc->new_<MIRGraph>(temp);
     ExecutionMode executionMode = compileContext.executionMode();
     CompileInfo *info = alloc->new_<CompileInfo>(script, fun, osrPc, constructing,
                                                  executionMode);
     if (!info)
-        return false;
+        return AbortReason_Alloc;
 
     types::AutoEnterTypeInference enter(cx, true);
     TypeInferenceOracle oracle;
 
     if (!oracle.init(cx, script))
-        return false;
+        return AbortReason_Disable;
 
     AutoFlushCache afc("IonCompile");
 
     types::AutoEnterCompilation enterCompiler(cx, CompilerOutputKind(executionMode));
     if (!enterCompiler.init(script, false, 0))
-        return false;
+        return AbortReason_Disable;
 
     AutoTempAllocatorRooter root(cx, temp);
 
     IonBuilder *builder = alloc->new_<IonBuilder>(cx, temp, graph, &oracle, info);
-    if (!compileContext.compile(builder, graph, autoDestroy)) {
+    if (!builder)
+        return AbortReason_Alloc;
+
+    AbortReason abortReason  = compileContext.compile(builder, graph, autoDestroy);
+    if (abortReason != AbortReason_NoAbort)
         IonSpew(IonSpew_Abort, "IM Compilation failed.");
-        return false;
-    }
 
-    return true;
+    return abortReason;
 }
 
-bool
+AbortReason
 SequentialCompileContext::compile(IonBuilder *builder, MIRGraph *graph,
                                   AutoDestroyAllocator &autoDestroy)
 {
     JS_ASSERT(!builder->script()->ion);
     JSContext *cx = GetIonContext()->cx;
 
     RootedScript builderScript(cx, builder->script());
     IonSpewNewFunction(graph, builderScript);
 
     if (!builder->build()) {
         IonSpew(IonSpew_Abort, "Builder failed to build.");
-        return false;
+        return builder->abortReason();
     }
     builder->clearForBackEnd();
 
     // Try to compile the script off thread, if possible. Compilation cannot be
     // performed off thread during an incremental GC, as doing so may trip
     // incremental read barriers. Also skip off thread compilation if script
     // execution is being profiled, as CodeGenerator::maybeCreateScriptCounts
     // will not attach script profiles when running off thread.
@@ -1221,50 +1223,61 @@ SequentialCompileContext::compile(IonBui
         OffThreadCompilationAvailable(cx) &&
         cx->runtime->gcIncrementalState == gc::NO_INCREMENTAL &&
         !cx->runtime->profilingScripts)
     {
         builder->script()->ion = ION_COMPILING_SCRIPT;
 
         if (!StartOffThreadIonCompile(cx, builder)) {
             IonSpew(IonSpew_Abort, "Unable to start off-thread ion compilation.");
-            return false;
+            return AbortReason_Alloc;
         }
 
         // The allocator and associated data will be destroyed after being
         // processed in the finishedOffThreadCompilations list.
         autoDestroy.cancel();
 
-        return true;
+        return AbortReason_NoAbort;
     }
 
     CodeGenerator *codegen = CompileBackEnd(builder);
     if (!codegen) {
         IonSpew(IonSpew_Abort, "Failed during back-end compilation.");
-        return false;
+        return AbortReason_Disable;
     }
 
     bool success = codegen->link();
     js_delete(codegen);
 
     IonSpewEndFunction();
 
-    return success;
+    return success ? AbortReason_NoAbort : AbortReason_Disable;
 }
 
-bool
+MethodStatus
 TestIonCompile(JSContext *cx, HandleScript script, HandleFunction fun, jsbytecode *osrPc, bool constructing)
 {
     SequentialCompileContext compileContext;
-    if (!IonCompile(cx, script, fun, osrPc, constructing, compileContext)) {
+
+    AbortReason reason = IonCompile(cx, script, fun, osrPc, constructing, compileContext);
+
+    if (reason == AbortReason_Alloc)
+        return Method_Skipped;
+
+    if (reason == AbortReason_Inlining)
+        return Method_Skipped;
+
+    if (reason == AbortReason_Disable) {
         if (!cx->isExceptionPending())
             ForbidCompilation(cx, script);
-        return false;
+        return Method_CantCompile;
     }
-    return true;
+
+    JS_ASSERT(reason == AbortReason_NoAbort);
+    return Method_Compiled;
 }
 
 static bool
 CheckFrame(StackFrame *fp)
 {
     if (fp->isEvalFrame()) {
         // Eval frames are not yet supported. Supporting this will require new
         // logic in pushBailoutFrame to deal with linking prev.
@@ -1370,20 +1383,22 @@ Compile(JSContext *cx, HandleScript scri
         if (script->getUseCount() < js_IonOptions.usesBeforeCompile)
             return Method_Skipped;
     } else {
         if (script->incUseCount() < js_IonOptions.usesBeforeCompileNoJaeger)
             return Method_Skipped;
     }
 
     SequentialCompileContext compileContext;
-    if (!IonCompile(cx, script, fun, osrPc, constructing, compileContext))
+
+    AbortReason reason = IonCompile(cx, script, fun, osrPc, constructing, compileContext);
+    if (reason == AbortReason_Disable)
         return Method_CantCompile;
 
-    // Compilation succeeded, but we invalidated right away.
+    // Compilation succeeded or we invalidated right away or an inlining/alloc abort
     return script->hasIonScript() ? Method_Compiled : Method_Skipped;
 }
 
 } // namespace ion
 } // namespace js
 
 // Decide if a transition from interpreter execution to Ion code should occur.
 // May compile or recompile the target JSScript.
--- a/js/src/ion/Ion.h
+++ b/js/src/ion/Ion.h
@@ -206,16 +206,23 @@ struct IonOptions
 enum MethodStatus
 {
     Method_Error,
     Method_CantCompile,
     Method_Skipped,
     Method_Compiled
 };
 
+enum AbortReason {
+    AbortReason_Alloc,
+    AbortReason_Inlining,
+    AbortReason_Disable,
+    AbortReason_NoAbort
+};
+
 // An Ion context is needed to enter into either an Ion method or an instance
 // of the Ion compiler. It points to a temporary allocator and the active
 // JSContext, either of which may be NULL, and the active compartment, which
 // will not be NULL.
 
 class IonContext
 {
   public:
@@ -291,17 +298,17 @@ void ToggleBarriers(JSCompartment *comp,
 
 class IonBuilder;
 class MIRGenerator;
 class CodeGenerator;
 
 CodeGenerator *CompileBackEnd(MIRGenerator *mir);
 void AttachFinishedCompilations(JSContext *cx);
 void FinishOffThreadBuilder(IonBuilder *builder);
-bool TestIonCompile(JSContext *cx, HandleScript script, HandleFunction fun, jsbytecode *osrPc, bool constructing);
+MethodStatus TestIonCompile(JSContext *cx, HandleScript script, HandleFunction fun, jsbytecode *osrPc, bool constructing);
 
 static inline bool IsEnabled(JSContext *cx)
 {
     return cx->hasRunOption(JSOPTION_ION) && cx->typeInferenceEnabled();
 }
 
 void ForbidCompilation(JSContext *cx, UnrootedScript script);
 uint32_t UsesBeforeIonRecompile(UnrootedScript script, jsbytecode *pc);
--- a/js/src/ion/IonBuilder.cpp
+++ b/js/src/ion/IonBuilder.cpp
@@ -30,16 +30,17 @@ using namespace js::ion;
 using mozilla::DebugOnly;
 
 IonBuilder::IonBuilder(JSContext *cx, TempAllocator *temp, MIRGraph *graph,
                        TypeOracle *oracle, CompileInfo *info, size_t inliningDepth, uint32_t loopDepth)
   : MIRGenerator(cx->compartment, temp, graph, info),
     backgroundCodegen_(NULL),
     recompileInfo(cx->compartment->types.compiledInfo),
     cx(cx),
+    abortReason_(AbortReason_Disable),
     loopDepth_(loopDepth),
     callerResumePoint_(NULL),
     callerBuilder_(NULL),
     oracle(oracle),
     inliningDepth(inliningDepth),
     failedBoundsCheck_(info->script()->failedBoundsCheck),
     failedShapeGuard_(info->script()->failedShapeGuard),
     lazyArguments_(NULL)
@@ -341,16 +342,17 @@ IonBuilder::build()
 
     if (!traverseBytecode())
         return false;
 
     if (!processIterators())
         return false;
 
     JS_ASSERT(loopDepth_ == 0);
+    abortReason_ = AbortReason_NoAbort;
     return true;
 }
 
 bool
 IonBuilder::processIterators()
 {
     // Find phis that must directly hold an iterator live.
     Vector<MPhi *, 0, SystemAllocPolicy> worklist;
@@ -2905,18 +2907,26 @@ IonBuilder::jsop_call_inline(HandleFunct
         thisDefn = createThis(callee, constFun);
         if (!thisDefn)
             return false;
     } else {
         thisDefn = argv[0];
     }
 
     // Build the graph.
-    if (!inlineBuilder.buildInline(this, inlineResumePoint, thisDefn, argv))
+    if (!inlineBuilder.buildInline(this, inlineResumePoint, thisDefn, argv)) {
+        JS_ASSERT(calleeScript->hasAnalysis());
+
+        // Inlining the callee failed. Disable inlining the function
+        if (inlineBuilder.abortReason_ == AbortReason_Disable)
+            calleeScript->analysis()->setIonUninlineable();
+
+        abortReason_ = AbortReason_Inlining;
         return false;
+    }
 
     MIRGraphExits &exits = *inlineBuilder.graph().exitAccumulator();
 
     // Replace all MReturns with MGotos, and remember the MDefinition that
     // would have been returned.
     for (MBasicBlock **it = exits.begin(), **end = exits.end(); it != end; ++it) {
         MBasicBlock *exitBlock = *it;
 
--- a/js/src/ion/IonBuilder.h
+++ b/js/src/ion/IonBuilder.h
@@ -470,18 +470,21 @@ class IonBuilder : public MIRGenerator
 
     void clearForBackEnd();
 
     UnrootedScript script() const { return script_.get(); }
 
     CodeGenerator *backgroundCodegen() const { return backgroundCodegen_; }
     void setBackgroundCodegen(CodeGenerator *codegen) { backgroundCodegen_ = codegen; }
 
+    AbortReason abortReason() { return abortReason_; }
+
   private:
     JSContext *cx;
+    AbortReason abortReason_;
 
     jsbytecode *pc;
     MBasicBlock *current;
     uint32_t loopDepth_;
 
     /* Information used for inline-call builders. */
     MResumePoint *callerResumePoint_;
     jsbytecode *callerPC() {
--- a/js/src/jsanalyze.h
+++ b/js/src/jsanalyze.h
@@ -879,16 +879,17 @@ class ScriptAnalysis
 
     /* Analyze the effect of invoking 'new' on script. */
     void analyzeTypesNew(JSContext *cx);
 
     bool OOM() const { return outOfMemory; }
     bool failed() const { return hadFailure; }
     bool ionInlineable() const { return isIonInlineable; }
     bool ionInlineable(uint32_t argc) const { return isIonInlineable && argc == script_->function()->nargs; }
+    void setIonUninlineable() { isIonInlineable = false; }
     bool jaegerInlineable() const { return isJaegerInlineable; }
     bool jaegerInlineable(uint32_t argc) const { return isJaegerInlineable && argc == script_->function()->nargs; }
     bool jaegerCompileable() { return isJaegerCompileable; }
 
     /* Number of property read opcodes in the script. */
     uint32_t numPropertyReads() const { return numPropertyReads_; }
 
     /* Whether there are POPV/SETRVAL bytecodes which can write to the frame's rval. */
--- a/js/src/methodjit/StubCalls.cpp
+++ b/js/src/methodjit/StubCalls.cpp
@@ -834,17 +834,20 @@ stubs::TriggerIonCompile(VMFrame &f)
         if (!script->canIonCompile() || script->isIonCompilingOffThread())
             return;
 
         jsbytecode *osrPC = f.regs.pc;
         if (*osrPC != JSOP_LOOPENTRY)
             osrPC = NULL;
 
         RootedFunction scriptFunction(f.cx, script->function());
-        if (!ion::TestIonCompile(f.cx, script, scriptFunction, osrPC, f.fp()->isConstructing())) {
+        ion::MethodStatus compileStatus =
+            ion::TestIonCompile(f.cx, script, scriptFunction, osrPC, f.fp()->isConstructing());
+
+        if (compileStatus != ion::Method_Compiled) {
             if (f.cx->isExceptionPending())
                 THROW();
         }
 
         return;
     }
 
     ExpandInlineFrames(f.cx->compartment);