Bug 902508 - Try to recover type sets for the initial values of arguments during Ion compilation, r=jandem.
authorBrian Hackett <bhackett1024@gmail.com>
Tue, 13 Aug 2013 12:04:40 -0600
changeset 155432 01bc7a4d74b5955759f9e8875c3b4e2d6fe44c2c
parent 155431 5a9443a00d476348b776991dd6311dc7a9fe2fe6
child 155433 2146da4d1864111c5af7da2498f1638362d62bba
push id2961
push userlsblakk@mozilla.com
push dateMon, 28 Oct 2013 21:59:28 +0000
treeherdermozilla-beta@73ef4f13486f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjandem
bugs902508
milestone26.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 902508 - Try to recover type sets for the initial values of arguments during Ion compilation, r=jandem.
js/src/jit/BaselineCompiler.cpp
js/src/jit/BaselineCompiler.h
js/src/jit/BaselineJIT.h
js/src/jit/IonBuilder.cpp
js/src/vm/Interpreter.cpp
--- a/js/src/jit/BaselineCompiler.cpp
+++ b/js/src/jit/BaselineCompiler.cpp
@@ -17,17 +17,18 @@
 #include "jsscriptinlines.h"
 
 #include "vm/Interpreter-inl.h"
 
 using namespace js;
 using namespace js::ion;
 
 BaselineCompiler::BaselineCompiler(JSContext *cx, HandleScript script)
-  : BaselineCompilerSpecific(cx, script)
+  : BaselineCompilerSpecific(cx, script),
+    modifiesArguments_(false)
 {
 }
 
 bool
 BaselineCompiler::init()
 {
     if (!analysis_.init(cx))
         return false;
@@ -183,16 +184,19 @@ BaselineCompiler::compile()
         label.fixup(&masm);
         size_t icEntry = icLoadLabels_[i].icEntry;
         ICEntry *entryAddr = &(baselineScript->icEntry(icEntry));
         Assembler::patchDataWithValueCheck(CodeLocationLabel(code, label),
                                            ImmWord(uintptr_t(entryAddr)),
                                            ImmWord(uintptr_t(-1)));
     }
 
+    if (modifiesArguments_)
+        baselineScript->setModifiesArguments();
+
     // All barriers are emitted off-by-default, toggle them on if needed.
     if (cx->zone()->needsBarrier())
         baselineScript->toggleBarriers(true);
 
     // All SPS instrumentation is emitted toggled off.  Toggle them on if needed.
     if (cx->runtime()->spsProfiler.enabled())
         baselineScript->toggleSPS(true);
 
@@ -2187,16 +2191,18 @@ bool
 BaselineCompiler::emit_JSOP_CALLARG()
 {
     return emit_JSOP_GETARG();
 }
 
 bool
 BaselineCompiler::emit_JSOP_SETARG()
 {
+    modifiesArguments_ = true;
+
     uint32_t arg = GET_SLOTNO(pc);
     return emitFormalArgAccess(arg, /* get = */ false);
 }
 
 bool
 BaselineCompiler::emitCall()
 {
     JS_ASSERT(IsCallPC(pc));
--- a/js/src/jit/BaselineCompiler.h
+++ b/js/src/jit/BaselineCompiler.h
@@ -180,16 +180,19 @@ class BaselineCompiler : public Baseline
     NonAssertingLabel           return_;
 #ifdef JSGC_GENERATIONAL
     NonAssertingLabel           postBarrierSlot_;
 #endif
 
     // Native code offset right before the scope chain is initialized.
     CodeOffsetLabel prologueOffset_;
 
+    // Whether any on stack arguments are modified.
+    bool modifiesArguments_;
+
     Label *labelOf(jsbytecode *pc) {
         return &labels_[pc - script->code];
     }
 
   public:
     BaselineCompiler(JSContext *cx, HandleScript script);
     bool init();
 
--- a/js/src/jit/BaselineJIT.h
+++ b/js/src/jit/BaselineJIT.h
@@ -119,17 +119,21 @@ struct BaselineScript
   public:
     enum Flag {
         // Flag set by JSScript::argumentsOptimizationFailed. Similar to
         // JSScript::needsArgsObj_, but can be read from JIT code.
         NEEDS_ARGS_OBJ = 1 << 0,
 
         // Flag set when discarding JIT code, to indicate this script is
         // on the stack and should not be discarded.
-        ACTIVE         = 1 << 1
+        ACTIVE = 1 << 1,
+
+        // Flag set when the script contains any writes to its on-stack
+        // (rather than call object stored) arguments.
+        MODIFIES_ARGUMENTS = 1 << 2
     };
 
   private:
     uint32_t flags_;
 
   private:
     void trace(JSTracer *trc);
 
@@ -176,16 +180,23 @@ struct BaselineScript
     void resetActive() {
         flags_ &= ~ACTIVE;
     }
 
     void setNeedsArgsObj() {
         flags_ |= NEEDS_ARGS_OBJ;
     }
 
+    void setModifiesArguments() {
+        flags_ |= MODIFIES_ARGUMENTS;
+    }
+    bool modifiesArguments() {
+        return flags_ & MODIFIES_ARGUMENTS;
+    }
+
     uint32_t prologueOffset() const {
         return prologueOffset_;
     }
     uint8_t *prologueEntryAddr() const {
         return method_->raw() + prologueOffset_;
     }
 
     ICEntry *icEntryList() {
--- a/js/src/jit/IonBuilder.cpp
+++ b/js/src/jit/IonBuilder.cpp
@@ -798,23 +798,37 @@ IonBuilder::rewriteParameters()
 }
 
 bool
 IonBuilder::initParameters()
 {
     if (!info().fun())
         return true;
 
-    MParameter *param = MParameter::New(MParameter::THIS_SLOT,
-                                        cloneTypeSet(types::TypeScript::ThisTypes(script())));
+    // If we are doing OSR on a frame which initially executed in the
+    // interpreter and didn't accumulate type information, try to use that OSR
+    // frame to determine possible initial types for 'this' and parameters.
+
+    types::StackTypeSet *thisTypes = types::TypeScript::ThisTypes(script());
+    if (thisTypes->empty() && baselineFrame_)
+        thisTypes->addType(cx, types::GetValueType(cx, baselineFrame_->thisValue()));
+
+    MParameter *param = MParameter::New(MParameter::THIS_SLOT, cloneTypeSet(thisTypes));
     current->add(param);
     current->initSlot(info().thisSlot(), param);
 
     for (uint32_t i = 0; i < info().nargs(); i++) {
-        param = MParameter::New(i, cloneTypeSet(types::TypeScript::ArgTypes(script(), i)));
+        types::StackTypeSet *argTypes = types::TypeScript::ArgTypes(script(), i);
+        if (argTypes->empty() && baselineFrame_ &&
+            !script_->baselineScript()->modifiesArguments())
+        {
+            argTypes->addType(cx, types::GetValueType(cx, baselineFrame_->argv()[i]));
+        }
+
+        param = MParameter::New(i, cloneTypeSet(argTypes));
         current->add(param);
         current->initSlot(info().argSlotUnchecked(i), param);
     }
 
     return true;
 }
 
 bool
@@ -1274,17 +1288,45 @@ IonBuilder::inspectOpcode(JSOp op)
         if (info().argsObjAliasesFormals()) {
             current->add(MSetArgumentsObjectArg::New(current->argumentsObject(), GET_SLOTNO(pc),
                                                      current->peek(-1)));
         } else {
             // TODO: if hasArguments() is true, and the script has a JSOP_SETARG, then
             // convert all arg accesses to go through the arguments object.
             if (info().hasArguments())
                 return abort("NYI: arguments & setarg.");
-            current->setArg(GET_SLOTNO(pc));
+
+            int32_t arg = GET_SLOTNO(pc);
+
+            // If this assignment is at the start of the function and is coercing
+            // the original value for the argument which was passed in, loosen
+            // the type information for that original argument if it is currently
+            // empty due to originally executing in the interpreter.
+            MDefinition *value = current->peek(-1);
+            if (graph().numBlocks() == 1 &&
+                (value->isBitOr() || value->isBitAnd() || value->isMul() /* for JSOP_POS */))
+             {
+                 for (size_t i = 0; i < value->numOperands(); i++) {
+                    MDefinition *op = value->getOperand(i);
+                    if (op->isParameter() &&
+                        op->toParameter()->index() == arg &&
+                        op->resultTypeSet() &&
+                        op->resultTypeSet()->empty())
+                    {
+                        types::TypeSet *argTypes = types::TypeScript::ArgTypes(script(), arg);
+
+                        // During parallel compilation the parameter's type set
+                        // will be a clone of the actual argument type set.
+                        argTypes->addType(cx, types::Type::UnknownType());
+                        op->resultTypeSet()->addType(cx, types::Type::UnknownType());
+                    }
+                }
+            }
+
+            current->setArg(arg);
         }
         return true;
 
       case JSOP_GETLOCAL:
       case JSOP_CALLLOCAL:
         current->pushLocal(GET_SLOTNO(pc));
         return true;
 
--- a/js/src/vm/Interpreter.cpp
+++ b/js/src/vm/Interpreter.cpp
@@ -2489,16 +2489,18 @@ BEGIN_CASE(JSOP_FUNCALL)
         regs.sp = newsp;
         len = JSOP_CALL_LENGTH;
         goto advanceAndDoOp;
     }
 
     InitialFrameFlags initial = construct ? INITIAL_CONSTRUCT : INITIAL_NONE;
     bool newType = cx->typeInferenceEnabled() && UseNewType(cx, script, regs.pc);
 
+    TypeMonitorCall(cx, args, construct);
+
 #ifdef JS_ION
     InvokeState state(cx, args, initial);
     if (newType)
         state.setUseNewType();
 
     if (!newType && ion::IsEnabled(cx)) {
         ion::MethodStatus status = ion::CanEnter(cx, state);
         if (status == ion::Method_Error)
@@ -2521,18 +2523,16 @@ BEGIN_CASE(JSOP_FUNCALL)
             CHECK_BRANCH();
             regs.sp = args.spAfterCall();
             interpReturnOK = !IsErrorStatus(exec);
             goto jit_return;
         }
     }
 #endif
 
-    TypeMonitorCall(cx, args, construct);
-
     funScript = fun->nonLazyScript();
     if (!activation.pushInlineFrame(args, funScript, initial))
         goto error;
 
     if (newType)
         regs.fp()->setUseNewType();
 
     SET_SCRIPT(regs.fp()->script());