Bug 939581 part 1. Factor out MCallDOMNative from MCall. r=efaust,jandem
authorBoris Zbarsky <bzbarsky@mit.edu>
Wed, 20 Nov 2013 17:44:56 -0500
changeset 162165 dce4127679642bf6cac37d0306ac2e9011f92fc7
parent 162164 ff96fec5d59393260c05b2d92cb93780cedc45dd
child 162166 98a783dfcef5f5c7d301cc35641b51dd489c5600
push id1
push userroot
push dateMon, 20 Oct 2014 17:29:22 +0000
reviewersefaust, jandem
bugs939581
milestone29.0a1
Bug 939581 part 1. Factor out MCallDOMNative from MCall. r=efaust,jandem
js/src/jit/CodeGenerator.cpp
js/src/jit/IonBuilder.cpp
js/src/jit/Lowering.cpp
js/src/jit/MIR.cpp
js/src/jit/MIR.h
js/src/jit/ParallelSafetyAnalysis.cpp
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -1776,17 +1776,17 @@ CodeGenerator::visitCallNative(LCallNati
 
 bool
 CodeGenerator::visitCallDOMNative(LCallDOMNative *call)
 {
     JSFunction *target = call->getSingleTarget();
     JS_ASSERT(target);
     JS_ASSERT(target->isNative());
     JS_ASSERT(target->jitInfo());
-    JS_ASSERT(call->mir()->isDOMFunction());
+    JS_ASSERT(call->mir()->isCallDOMNative());
 
     int callargslot = call->argslot();
     int unusedStack = StackOffsetOfPassedArg(callargslot);
 
     // Registers used for callWithABI() argument-passing.
     const Register argJSContext = ToRegister(call->getArgJSContext());
     const Register argObj       = ToRegister(call->getArgObj());
     const Register argPrivate   = ToRegister(call->getArgPrivate());
--- a/js/src/jit/IonBuilder.cpp
+++ b/js/src/jit/IonBuilder.cpp
@@ -5165,18 +5165,32 @@ IonBuilder::makeCallHelper(JSFunction *t
 
     uint32_t targetArgs = callInfo.argc();
 
     // Collect number of missing arguments provided that the target is
     // scripted. Native functions are passed an explicit 'argc' parameter.
     if (target && !target->isNative())
         targetArgs = Max<uint32_t>(target->nargs(), callInfo.argc());
 
-    MCall *call =
-        MCall::New(alloc(), target, targetArgs + 1, callInfo.argc(), callInfo.constructing());
+    bool isDOMCall = false;
+    if (target && !callInfo.constructing()) {
+        // We know we have a single call target.  Check whether the "this" types
+        // are DOM types and our function a DOM function, and if so flag the
+        // MCall accordingly.
+        types::TemporaryTypeSet *thisTypes = callInfo.thisArg()->resultTypeSet();
+        if (thisTypes &&
+            thisTypes->isDOMClass() &&
+            testShouldDOMCall(thisTypes, target, JSJitInfo::Method))
+        {
+            isDOMCall = true;
+        }
+    }
+
+    MCall *call = MCall::New(alloc(), target, targetArgs + 1, callInfo.argc(),
+                             callInfo.constructing(), isDOMCall);
     if (!call)
         return nullptr;
 
     // Explicitly pad any missing arguments with |undefined|.
     // This permits skipping the argumentsRectifier.
     for (int i = targetArgs; i > (int)callInfo.argc(); i--) {
         JS_ASSERT_IF(target, !target->isNative());
         MConstant *undef = constant(UndefinedValue());
@@ -5206,29 +5220,16 @@ IonBuilder::makeCallHelper(JSFunction *t
 
     // Add a callsite clone IC for multiple targets which all should be
     // callsite cloned, or bake in the clone for a single target.
     if (cloneAtCallsite) {
         MDefinition *fun = makeCallsiteClone(target, callInfo.fun());
         callInfo.setFun(fun);
     }
 
-    if (target && JSOp(*pc) == JSOP_CALL) {
-        // We know we have a single call target.  Check whether the "this" types
-        // are DOM types and our function a DOM function, and if so flag the
-        // MCall accordingly.
-        types::TemporaryTypeSet *thisTypes = thisArg->resultTypeSet();
-        if (thisTypes &&
-            thisTypes->isDOMClass() &&
-            testShouldDOMCall(thisTypes, target, JSJitInfo::Method))
-        {
-            call->setDOMFunction();
-        }
-    }
-
     if (target && !testNeedsArgumentCheck(target, callInfo))
         call->disableArgCheck();
 
     call->initFunction(callInfo.fun());
 
     current->add(call);
     return call;
 }
@@ -5263,17 +5264,17 @@ IonBuilder::makeCall(JSFunction *target,
         return false;
 
     current->push(call);
     if (!resumeAfter(call))
         return false;
 
     types::TemporaryTypeSet *types = bytecodeTypes(pc);
 
-    if (call->isDOMFunction())
+    if (call->isCallDOMNative())
         return pushDOMTypeBarrier(call, types, call->getSingleTarget());
 
     return pushTypeBarrier(call, types, true);
 }
 
 bool
 IonBuilder::jsop_eval(uint32_t argc)
 {
--- a/js/src/jit/Lowering.cpp
+++ b/js/src/jit/Lowering.cpp
@@ -403,17 +403,17 @@ LIRGenerator::visitCall(MCall *call)
     if (!lowerCallArguments(call))
         return false;
 
     // Height of the current argument vector.
     uint32_t argslot = call->numStackArgs();
     JSFunction *target = call->getSingleTarget();
 
     // Call DOM functions.
-    if (call->isDOMFunction()) {
+    if (call->isCallDOMNative()) {
         JS_ASSERT(target && target->isNative());
         Register cxReg, objReg, privReg, argsReg;
         GetTempRegForIntArg(0, 0, &cxReg);
         GetTempRegForIntArg(1, 0, &objReg);
         GetTempRegForIntArg(2, 0, &privReg);
         mozilla::DebugOnly<bool> ok = GetTempRegForIntArg(3, 0, &argsReg);
         MOZ_ASSERT(ok, "How can we not have four temp registers?");
         LCallDOMNative *lir = new(alloc()) LCallDOMNative(argslot, tempFixed(cxReg),
--- a/js/src/jit/MIR.cpp
+++ b/js/src/jit/MIR.cpp
@@ -643,25 +643,74 @@ MParameter::congruentTo(MDefinition *ins
     if (!ins->isParameter())
         return false;
 
     return ins->toParameter()->index() == index_;
 }
 
 MCall *
 MCall::New(TempAllocator &alloc, JSFunction *target, size_t maxArgc, size_t numActualArgs,
-           bool construct)
+           bool construct, bool isDOMCall)
 {
     JS_ASSERT(maxArgc >= numActualArgs);
-    MCall *ins = new(alloc) MCall(target, numActualArgs, construct);
+    MCall *ins;
+    if (isDOMCall) {
+        JS_ASSERT(!construct);
+        ins = new(alloc) MCallDOMNative(target, numActualArgs);
+    } else {
+        ins = new(alloc) MCall(target, numActualArgs, construct);
+    }
     if (!ins->init(alloc, maxArgc + NumNonArgumentOperands))
         return nullptr;
     return ins;
 }
 
+AliasSet
+MCallDOMNative::getAliasSet() const
+{
+    JS_ASSERT(getSingleTarget() && getSingleTarget()->isNative());
+
+    const JSJitInfo *jitInfo = getSingleTarget()->jitInfo();
+    JS_ASSERT(jitInfo);
+
+    JS_ASSERT(jitInfo->aliasSet != JSJitInfo::AliasNone);
+    // If we don't know anything about the types of our arguments, we have to
+    // assume that type-coercions can have side-effects, so we need to alias
+    // everything.
+    if (jitInfo->aliasSet != JSJitInfo::AliasDOMSets || !jitInfo->argTypes)
+        return AliasSet::Store(AliasSet::Any);
+
+    uint32_t argIndex = 0;
+    for (const JSJitInfo::ArgType *argType = jitInfo->argTypes;
+         *argType != JSJitInfo::ArgTypeListEnd;
+         ++argType, ++argIndex)
+    {
+        if (argIndex >= numActualArgs()) {
+            // Passing through undefined can't have side-effects
+            continue;
+        }
+        // getArg(0) is "this", so skip it
+        MDefinition *arg = getArg(argIndex+1);
+        MIRType actualType = arg->type();
+        // The only way to get side-effects is if we're passing in
+        // something that might be an object to an argument that
+        // expects a numeric, string, or boolean value.
+        if ((actualType == MIRType_Value || actualType == MIRType_Object) &&
+            (*argType &
+             (JSJitInfo::Boolean | JSJitInfo::String | JSJitInfo::Numeric)))
+         {
+             return AliasSet::Store(AliasSet::Any);
+         }
+    }
+
+    // We checked all the args, and they check out.  So we only
+    // alias DOM mutations.
+    return AliasSet::Load(AliasSet::DOMProperty);
+}
+
 MApplyArgs *
 MApplyArgs::New(TempAllocator &alloc, JSFunction *target, MDefinition *fun, MDefinition *argc,
                 MDefinition *self)
 {
     return new(alloc) MApplyArgs(target, fun, argc, self);
 }
 
 MDefinition*
--- a/js/src/jit/MIR.h
+++ b/js/src/jit/MIR.h
@@ -54,18 +54,16 @@ MIRType MIRTypeFromValue(const js::Value
     _(Lowered)       /* (Debug only) has a virtual register */                  \
     _(Guard)         /* Not removable if uses == 0 */                           \
     _(Folded)        /* Has constant folded uses not reflected in SSA */        \
                                                                                 \
     /* The instruction has been marked dead for lazy removal from resume
      * points.
      */                                                                         \
     _(Unused)                                                                   \
-    _(DOMFunction)   /* Contains or uses a common DOM method function */        \
-                                                                                \
     /* Marks if an instruction has fewer uses than the original code.
      * E.g. UCE can remove code.
      * Every instruction where an use is/was removed from an instruction and
      * as a result the number of operands doesn't equal the original code
      * need to get marked as UseRemoved. This is important for truncation
      * analysis to know, since if all original uses are still present,
      * it can ignore resumepoints.
      * Currently this is done for every pass after IonBuilder and before
@@ -1801,17 +1799,17 @@ class MCall
         needsArgCheck_(true)
     {
         setResultType(MIRType_Value);
     }
 
   public:
     INSTRUCTION_HEADER(Call)
     static MCall *New(TempAllocator &alloc, JSFunction *target, size_t maxArgc, size_t numActualArgs,
-                      bool construct);
+                      bool construct, bool isDOMCall);
 
     void initFunction(MDefinition *func) {
         return setOperand(FunctionOperandIndex, func);
     }
 
     bool needsArgCheck() const {
         return needsArgCheck_;
     }
@@ -1866,59 +1864,47 @@ class MCall
     // Does not include |this|.
     uint32_t numActualArgs() const {
         return numActualArgs_;
     }
 
     TypePolicy *typePolicy() {
         return this;
     }
-    AliasSet getAliasSet() const {
-        if (isDOMFunction()) {
-            JS_ASSERT(getSingleTarget() && getSingleTarget()->isNative());
-
-            const JSJitInfo* jitInfo = getSingleTarget()->jitInfo();
-            JS_ASSERT(jitInfo);
-
-            JS_ASSERT(jitInfo->aliasSet != JSJitInfo::AliasNone);
-            if (jitInfo->aliasSet == JSJitInfo::AliasDOMSets &&
-                jitInfo->argTypes) {
-                uint32_t argIndex = 0;
-                for (const JSJitInfo::ArgType* argType = jitInfo->argTypes;
-                     *argType != JSJitInfo::ArgTypeListEnd;
-                     ++argType, ++argIndex)
-                {
-                    if (argIndex >= numActualArgs()) {
-                        // Passing through undefined can't have side-effects
-                        continue;
-                    }
-                    // getArg(0) is "this", so skip it
-                    MDefinition *arg = getArg(argIndex+1);
-                    MIRType actualType = arg->type();
-                    // The only way to get side-effects is if we're passing in
-                    // something that might be an object to an argument that
-                    // expects a numeric, string, or boolean value.
-                    if ((actualType == MIRType_Value || actualType == MIRType_Object) &&
-                        (*argType &
-                         (JSJitInfo::Boolean | JSJitInfo::String | JSJitInfo::Numeric)))
-                    {
-                        return AliasSet::Store(AliasSet::Any);
-                    }
-                }
-                // We checked all the args, and they check out.  So we only
-                // alias DOM mutations.
-                return AliasSet::Load(AliasSet::DOMProperty);
-            }
-        }
-        return AliasSet::Store(AliasSet::Any);
-    }
 
     bool possiblyCalls() const {
         return true;
     }
+
+    virtual bool isCallDOMNative() const {
+        return false;
+    }
+};
+
+class MCallDOMNative : public MCall
+{
+    // A helper class for MCalls for DOM natives.  Note that this is NOT
+    // actually a separate MIR op from MCall, because all sorts of places use
+    // isCall() to check for calls and all we really want is to overload a few
+    // virtual things from MCall.
+  protected:
+    MCallDOMNative(JSFunction *target, uint32_t numActualArgs)
+        : MCall(target, numActualArgs, false)
+    {
+    }
+
+    friend MCall *MCall::New(TempAllocator &alloc, JSFunction *target, size_t maxArgc,
+                             size_t numActualArgs, bool construct, bool isDOMCall);
+
+  public:
+    virtual AliasSet getAliasSet() const MOZ_OVERRIDE;
+
+    virtual bool isCallDOMNative() const MOZ_OVERRIDE {
+        return true;
+    }
 };
 
 // fun.apply(self, arguments)
 class MApplyArgs
   : public MAryInstruction<3>,
     public MixPolicy<ObjectPolicy<0>, MixPolicy<IntPolicy<1>, BoxPolicy<2> > >
 {
   protected:
--- a/js/src/jit/ParallelSafetyAnalysis.cpp
+++ b/js/src/jit/ParallelSafetyAnalysis.cpp
@@ -692,17 +692,17 @@ ParallelSafetyVisitor::insertWriteGuard(
 //
 // We only support calls to interpreted functions that that have already been
 // Ion compiled. If a function has no IonScript, we bail out.
 
 bool
 ParallelSafetyVisitor::visitCall(MCall *ins)
 {
     // DOM? Scary.
-    if (ins->isDOMFunction()) {
+    if (ins->isCallDOMNative()) {
         SpewMIR(ins, "call to dom function");
         return markUnsafe();
     }
 
     JSFunction *target = ins->getSingleTarget();
     if (target) {
         // Non-parallel native? Scary
         if (target->isNative() && !target->hasParallelNative()) {