Bug 1142669 part 3 - Limit the total inlined bytecode size to avoid excessive inlining. r=h4writer
authorJan de Mooij <jdemooij@mozilla.com>
Fri, 20 Mar 2015 13:45:36 +0100
changeset 263582 157929ef51b3b8e63bc3dae44e881297d7003616
parent 263581 310b3af47e938a352a28bb3b31ebbaf8d9c30967
child 263583 f7299a88c59c702bfec43e98ced672af61352147
push id4718
push userraliiev@mozilla.com
push dateMon, 11 May 2015 18:39:53 +0000
treeherdermozilla-beta@c20c4ef55f08 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersh4writer
bugs1142669
milestone39.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 1142669 part 3 - Limit the total inlined bytecode size to avoid excessive inlining. r=h4writer
js/public/TrackedOptimizationInfo.h
js/src/jit/IonBuilder.cpp
js/src/jit/IonBuilder.h
js/src/jit/IonOptimizationLevels.cpp
js/src/jit/IonOptimizationLevels.h
--- a/js/public/TrackedOptimizationInfo.h
+++ b/js/public/TrackedOptimizationInfo.h
@@ -200,16 +200,18 @@ namespace JS {
     _(CantInlineDebuggee,                                               \
       "can't inline: debuggee")                                         \
     _(CantInlineUnknownProps,                                           \
       "can't inline: type has unknown properties")                      \
     _(CantInlineExceededDepth,                                          \
       "can't inline: exceeded inlining depth")                          \
     _(CantInlineBigLoop,                                                \
       "can't inline: big function with a loop")                         \
+    _(CantInlineExceededTotalBytecodeLength,                            \
+      "can't inline: exceeded max total bytecode length")               \
     _(CantInlineBigCaller,                                              \
       "can't inline: big caller")                                       \
     _(CantInlineBigCallee,                                              \
       "can't inline: big callee")                                       \
     _(CantInlineNotHot,                                                 \
       "can't inline: not hot enough")                                   \
     _(CantInlineNotInDispatch,                                          \
       "can't inline: not in dispatch table")                            \
--- a/js/src/jit/IonBuilder.cpp
+++ b/js/src/jit/IonBuilder.cpp
@@ -141,16 +141,17 @@ IonBuilder::IonBuilder(JSContext *analys
     cfgStack_(*temp),
     loops_(*temp),
     switches_(*temp),
     labels_(*temp),
     iterators_(*temp),
     loopHeaders_(*temp),
     inspector(inspector),
     inliningDepth_(inliningDepth),
+    inlinedBytecodeLength_(0),
     numLoopRestarts_(0),
     failedBoundsCheck_(info->script()->failedBoundsCheck()),
     failedShapeGuard_(info->script()->failedShapeGuard()),
     nonStringIteration_(false),
     lazyArguments_(nullptr),
     inlineCallInfo_(nullptr),
     maybeFallbackFunctionGetter_(nullptr)
 {
@@ -4906,20 +4907,32 @@ IonBuilder::makeInliningDecision(JSObjec
         info().analysisMode() != Analysis_DefiniteProperties)
     {
         trackOptimizationOutcome(TrackedOutcome::CantInlineNotHot);
         JitSpew(JitSpew_Inlining, "Cannot inline %s:%" PRIuSIZE ": callee is insufficiently hot.",
                 targetScript->filename(), targetScript->lineno());
         return InliningDecision_WarmUpCountTooLow;
     }
 
+    IonBuilder *outerBuilder = outermostBuilder();
+
+    // Cap the total bytecode length we inline under a single script, to avoid
+    // excessive inlining in pathological cases.
+    size_t totalBytecodeLength = outerBuilder->inlinedBytecodeLength_ + targetScript->length();
+    if (totalBytecodeLength > optimizationInfo().inlineMaxTotalBytecodeLength()) {
+        trackOptimizationOutcome(TrackedOutcome::CantInlineExceededTotalBytecodeLength);
+        return DontInline(targetScript, "Vetoed: exceeding max total bytecode length");
+    }
+
     // TI calls ObjectStateChange to trigger invalidation of the caller.
     TypeSet::ObjectKey *targetKey = TypeSet::ObjectKey::get(target);
     targetKey->watchStateChangeForInlinedCall(constraints());
 
+    outerBuilder->inlinedBytecodeLength_ += targetScript->length();
+
     return InliningDecision_Inline;
 }
 
 bool
 IonBuilder::selectInliningTargets(const ObjectVector &targets, CallInfo &callInfo, BoolVector &choiceSet,
                                   uint32_t *numInlineable)
 {
     *numInlineable = 0;
--- a/js/src/jit/IonBuilder.h
+++ b/js/src/jit/IonBuilder.h
@@ -1089,16 +1089,20 @@ class IonBuilder
     Vector<ControlFlowInfo, 0, JitAllocPolicy> switches_;
     Vector<ControlFlowInfo, 2, JitAllocPolicy> labels_;
     Vector<MInstruction *, 2, JitAllocPolicy> iterators_;
     Vector<LoopHeader, 0, JitAllocPolicy> loopHeaders_;
     BaselineInspector *inspector;
 
     size_t inliningDepth_;
 
+    // Total bytecode length of all inlined scripts. Only tracked for the
+    // outermost builder.
+    size_t inlinedBytecodeLength_;
+
     // Cutoff to disable compilation if excessive time is spent reanalyzing
     // loop bodies to compute a fixpoint of the types for loop variables.
     static const size_t MAX_LOOP_RESTARTS = 40;
     size_t numLoopRestarts_;
 
     // True if script->failedBoundsCheck is set for the current script or
     // an outer script.
     bool failedBoundsCheck_;
--- a/js/src/jit/IonOptimizationLevels.cpp
+++ b/js/src/jit/IonOptimizationLevels.cpp
@@ -34,16 +34,17 @@ OptimizationInfo::initNormalOptimization
     rangeAnalysis_ = true;
     loopUnrolling_ = true;
     autoTruncate_ = true;
     sink_ = true;
     registerAllocator_ = RegisterAllocator_Backtracking;
 
     inlineMaxBytecodePerCallSiteMainThread_ = 500;
     inlineMaxBytecodePerCallSiteOffThread_ = 1000;
+    inlineMaxTotalBytecodeLength_ = 80000;
     inliningMaxCallerBytecodeLength_ = 10000;
     maxInlineDepth_ = 3;
     scalarReplacement_ = true;
     smallFunctionMaxInlineDepth_ = 10;
     compilerWarmUpThreshold_ = CompilerWarmupThreshold;
     inliningWarmUpThresholdFactor_ = 0.125;
     inliningRecompileThresholdFactor_ = 4;
 }
--- a/js/src/jit/IonOptimizationLevels.h
+++ b/js/src/jit/IonOptimizationLevels.h
@@ -89,16 +89,19 @@ class OptimizationInfo
     IonRegisterAllocator registerAllocator_;
 
     // The maximum total bytecode size of an inline call site. We use a lower
     // value if off-thread compilation is not available, to avoid stalling the
     // main thread.
     uint32_t inlineMaxBytecodePerCallSiteOffThread_;
     uint32_t inlineMaxBytecodePerCallSiteMainThread_;
 
+    // The maximum bytecode length we'll inline in a single compilation.
+    uint32_t inlineMaxTotalBytecodeLength_;
+
     // The maximum bytecode length the caller may have,
     // before we stop inlining large functions in that caller.
     uint32_t inliningMaxCallerBytecodeLength_;
 
     // The maximum inlining depth.
     uint32_t maxInlineDepth_;
 
     // Toggles whether scalar replacement is used.
@@ -213,16 +216,20 @@ class OptimizationInfo
     }
 
     uint32_t inlineMaxBytecodePerCallSite(bool offThread) const {
         return (offThread || !js_JitOptions.limitScriptSize)
                ? inlineMaxBytecodePerCallSiteOffThread_
                : inlineMaxBytecodePerCallSiteMainThread_;
     }
 
+    uint32_t inlineMaxTotalBytecodeLength() const {
+        return inlineMaxTotalBytecodeLength_;
+    }
+
     uint32_t inliningMaxCallerBytecodeLength() const {
         return inliningMaxCallerBytecodeLength_;
     }
 
     uint32_t inliningWarmUpThreshold() const {
         uint32_t compilerWarmUpThreshold = compilerWarmUpThreshold_;
         if (js_JitOptions.forcedDefaultIonWarmUpThreshold.isSome())
             compilerWarmUpThreshold = js_JitOptions.forcedDefaultIonWarmUpThreshold.ref();