Bug 1233925 - Treat functions with rest more like functions with lazy arguments. r=nbp
authorJan de Mooij <jdemooij@mozilla.com>
Wed, 06 Jan 2016 21:05:21 +0100
changeset 278859 dc55c41b6331
parent 278858 3ac8d03d63db
child 278860 80a318392bb0
push id29860
push usercbook@mozilla.com
push dateThu, 07 Jan 2016 10:51:20 +0000
treeherdermozilla-central@e0bcd16e1d4b [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersnbp
bugs1233925
milestone46.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 1233925 - Treat functions with rest more like functions with lazy arguments. r=nbp
js/src/jit/BacktrackingAllocator.cpp
js/src/jit/CompileInfo.h
js/src/jit/JitFrames.cpp
js/src/jsscript.cpp
js/src/jsscript.h
--- a/js/src/jit/BacktrackingAllocator.cpp
+++ b/js/src/jit/BacktrackingAllocator.cpp
@@ -887,20 +887,19 @@ BacktrackingAllocator::tryMergeBundles(L
     // constructor calling convention.
     if (IsThisSlotDefinition(reg0.def()) || IsThisSlotDefinition(reg1.def())) {
         if (*reg0.def()->output() != *reg1.def()->output())
             return true;
     }
 
     // Registers which might spill to the frame's argument slots can only be
     // grouped with other such registers if the frame might access those
-    // arguments through a lazy arguments object.
+    // arguments through a lazy arguments object or rest parameter.
     if (IsArgumentSlotDefinition(reg0.def()) || IsArgumentSlotDefinition(reg1.def())) {
-        JSScript* script = graph.mir().entryBlock()->info().script();
-        if (script && script->argumentsHasVarBinding()) {
+        if (graph.mir().entryBlock()->info().mayReadFrameArgsDirectly()) {
             if (*reg0.def()->output() != *reg1.def()->output())
                 return true;
         }
     }
 
     // Limit the number of times we compare ranges if there are many ranges in
     // one of the bundles, to avoid quadratic behavior.
     static const size_t MAX_RANGES = 200;
--- a/js/src/jit/CompileInfo.h
+++ b/js/src/jit/CompileInfo.h
@@ -196,16 +196,17 @@ class CompileInfo
 {
   public:
     CompileInfo(JSScript* script, JSFunction* fun, jsbytecode* osrPc, bool constructing,
                 AnalysisMode analysisMode, bool scriptNeedsArgsObj,
                 InlineScriptTree* inlineScriptTree)
       : script_(script), fun_(fun), osrPc_(osrPc), constructing_(constructing),
         analysisMode_(analysisMode), scriptNeedsArgsObj_(scriptNeedsArgsObj),
         hadOverflowBailout_(script->hadOverflowBailout()),
+        mayReadFrameArgsDirectly_(script->mayReadFrameArgsDirectly()),
         inlineScriptTree_(inlineScriptTree)
     {
         MOZ_ASSERT_IF(osrPc, JSOp(*osrPc) == JSOP_LOOPENTRY);
 
         // The function here can flow in from anywhere so look up the canonical
         // function to ensure that we do not try to embed a nursery pointer in
         // jit-code. Precisely because it can flow in from anywhere, it's not
         // guaranteed to be non-lazy. Hence, don't access its script!
@@ -224,17 +225,17 @@ class CompileInfo
         fixedLexicalBegin_ = script->fixedLexicalBegin();
         nstack_ = Max<unsigned>(script->nslots() - script->nfixed(), MinJITStackSize);
         nslots_ = nimplicit_ + nargs_ + nlocals_ + nstack_;
     }
 
     explicit CompileInfo(unsigned nlocals)
       : script_(nullptr), fun_(nullptr), osrPc_(nullptr), osrStaticScope_(nullptr),
         constructing_(false), analysisMode_(Analysis_None), scriptNeedsArgsObj_(false),
-        inlineScriptTree_(nullptr)
+        mayReadFrameArgsDirectly_(false), inlineScriptTree_(nullptr)
     {
         nimplicit_ = 0;
         nargs_ = 0;
         nbodyfixed_ = 0;
         nlocals_ = nlocals;
         nstack_ = 1;  /* For FunctionCompiler::pushPhiInput/popPhiOutput */
         nslots_ = nlocals_ + nstack_;
         fixedLexicalBegin_ = nlocals;
@@ -549,16 +550,19 @@ class CompileInfo
         return true;
     }
 
     // Check previous bailout states to prevent doing the same bailout in the
     // next compilation.
     bool hadOverflowBailout() const {
         return hadOverflowBailout_;
     }
+    bool mayReadFrameArgsDirectly() const {
+        return mayReadFrameArgsDirectly_;
+    }
 
   private:
     unsigned nimplicit_;
     unsigned nargs_;
     unsigned nbodyfixed_;
     unsigned nlocals_;
     unsigned nstack_;
     unsigned nslots_;
@@ -574,15 +578,17 @@ class CompileInfo
     // since the arguments optimization could be marked as failed on the main
     // thread, so cache a value here and use it throughout for consistency.
     bool scriptNeedsArgsObj_;
 
     // Record the state of previous bailouts in order to prevent compiling the
     // same function identically the next time.
     bool hadOverflowBailout_;
 
+    bool mayReadFrameArgsDirectly_;
+
     InlineScriptTree* inlineScriptTree_;
 };
 
 } // namespace jit
 } // namespace js
 
 #endif /* jit_CompileInfo_h */
--- a/js/src/jit/JitFrames.cpp
+++ b/js/src/jit/JitFrames.cpp
@@ -1043,32 +1043,33 @@ ReadAllocation(const JitFrameIterator& f
 }
 #endif
 
 static void
 MarkThisAndArguments(JSTracer* trc, const JitFrameIterator& frame)
 {
     // Mark |this| and any extra actual arguments for an Ion frame. Marking of
     // formal arguments is taken care of by the frame's safepoint/snapshot,
-    // except when the script might have lazy arguments, in which case we mark
-    // them as well. We also have to mark formals if we have a LazyLink frame.
+    // except when the script might have lazy arguments or rest, in which case
+    // we mark them as well. We also have to mark formals if we have a LazyLink
+    // frame.
 
     JitFrameLayout* layout = frame.isExitFrameLayout<LazyLinkExitFrameLayout>()
                              ? frame.exitFrame()->as<LazyLinkExitFrameLayout>()->jsFrame()
                              : frame.jsFrame();
 
     if (!CalleeTokenIsFunction(layout->calleeToken()))
         return;
 
     size_t nargs = layout->numActualArgs();
     size_t nformals = 0;
 
     JSFunction* fun = CalleeTokenToFunction(layout->calleeToken());
     if (!frame.isExitFrameLayout<LazyLinkExitFrameLayout>() &&
-        !fun->nonLazyScript()->argumentsHasVarBinding())
+        !fun->nonLazyScript()->mayReadFrameArgsDirectly())
     {
         nformals = fun->nargs();
     }
 
     size_t newTargetOffset = Max(nargs, fun->nargs());
 
     Value* argv = layout->argv();
 
--- a/js/src/jsscript.cpp
+++ b/js/src/jsscript.cpp
@@ -4428,16 +4428,22 @@ JSScript::hasLoops()
     JSTryNote* tnlimit = tn + trynotes()->length;
     for (; tn < tnlimit; tn++) {
         if (tn->kind == JSTRY_FOR_IN || tn->kind == JSTRY_LOOP)
             return true;
     }
     return false;
 }
 
+bool
+JSScript::mayReadFrameArgsDirectly()
+{
+    return argumentsHasVarBinding() || (function_ && function_->hasRest());
+}
+
 static inline void
 LazyScriptHash(uint32_t lineno, uint32_t column, uint32_t begin, uint32_t end,
                HashNumber hashes[3])
 {
     HashNumber hash = lineno;
     hash = RotateLeft(hash, 4) ^ column;
     hash = RotateLeft(hash, 4) ^ begin;
     hash = RotateLeft(hash, 4) ^ end;
--- a/js/src/jsscript.h
+++ b/js/src/jsscript.h
@@ -1626,16 +1626,20 @@ class JSScript : public js::gc::TenuredC
         return module_;
     }
     inline void setModule(js::ModuleObject* module);
 
     bool isGlobalCode() const {
         return !function_ && !module_;
     }
 
+    // Returns true if the script may read formal arguments on the stack
+    // directly, via lazy arguments or a rest parameter.
+    bool mayReadFrameArgsDirectly();
+
     JSFlatString* sourceData(JSContext* cx);
 
     static bool loadSource(JSContext* cx, js::ScriptSource* ss, bool* worked);
 
     void setSourceObject(JSObject* object);
     JSObject* sourceObject() const {
         return sourceObject_;
     }