Bug 867471 - Part 2: Compile rest parameter in Ion for sequential execution. (r=djvj)
authorShu-yu Guo <shu@rfrn.org>
Tue, 21 May 2013 23:52:45 -0700
changeset 132612 b2216a10f95b8a5ed121be06182961f1bfce3acd
parent 132611 ddd20f8bcb1c4f0ae3cb8b9e8e7405c7a5c7890c
child 132613 2f7967db9d254dd9210fe1cead768c5f2d5022b9
push id24711
push userryanvm@gmail.com
push dateWed, 22 May 2013 16:29:12 +0000
treeherdermozilla-central@00b264c7cced [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersdjvj
bugs867471
milestone24.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 867471 - Part 2: Compile rest parameter in Ion for sequential execution. (r=djvj)
js/src/ion/CodeGenerator.cpp
js/src/ion/CodeGenerator.h
js/src/ion/IonBuilder.cpp
js/src/ion/IonBuilder.h
js/src/ion/LIR-Common.h
js/src/ion/LOpcodes.h
js/src/ion/Lowering.cpp
js/src/ion/Lowering.h
js/src/ion/MIR.h
js/src/ion/MOpcodes.h
js/src/ion/ParallelArrayAnalysis.cpp
js/src/ion/VMFunctions.cpp
js/src/ion/VMFunctions.h
js/src/jsanalyze.cpp
--- a/js/src/ion/CodeGenerator.cpp
+++ b/js/src/ion/CodeGenerator.cpp
@@ -4854,16 +4854,66 @@ CodeGenerator::visitGetArgument(LGetArgu
     } else {
         Register i = ToRegister(index);
         BaseIndex argPtr(StackPointer, i, ScaleFromElemWidth(sizeof(Value)), argvOffset);
         masm.loadValue(argPtr, result);
     }
     return true;
 }
 
+typedef JSObject *(*InitRestParameterFn)(JSContext *, uint32_t, Value *, HandleObject,
+                                         HandleObject);
+static const VMFunction InitRestParameterInfo =
+    FunctionInfo<InitRestParameterFn>(InitRestParameter);
+
+bool
+CodeGenerator::visitRest(LRest *lir)
+{
+    Register numActuals = ToRegister(lir->numActuals());
+    Register temp1 = ToRegister(lir->getTemp(0));
+    Register temp2 = ToRegister(lir->getTemp(1));
+    Register temp3 = ToRegister(lir->getTemp(2));
+
+    // Try to allocate the rest array inline.
+    Label failAlloc, joinAlloc;
+    JSObject *templateObject = lir->mir()->templateObject();
+    masm.newGCThing(temp3, templateObject, &failAlloc);
+    masm.initGCThing(temp3, templateObject);
+    masm.jump(&joinAlloc);
+    {
+        masm.bind(&failAlloc);
+        masm.movePtr(ImmWord((void *)NULL), temp3);
+    }
+    masm.bind(&joinAlloc);
+
+    // Compute actuals() + numFormals.
+    unsigned numFormals = lir->mir()->numFormals();
+    size_t actualsOffset = frameSize() + IonJSFrameLayout::offsetOfActualArgs();
+    masm.movePtr(StackPointer, temp2);
+    masm.addPtr(Imm32(sizeof(Value) * numFormals + actualsOffset), temp2);
+
+    // Compute numActuals - numFormals.
+    Label emptyLength, joinLength;
+    masm.movePtr(numActuals, temp1);
+    masm.branch32(Assembler::LessThanOrEqual, temp1, Imm32(numFormals), &emptyLength);
+    masm.sub32(Imm32(numFormals), temp1);
+    masm.jump(&joinLength);
+    {
+        masm.bind(&emptyLength);
+        masm.move32(Imm32(0), temp1);
+    }
+    masm.bind(&joinLength);
+
+    pushArg(temp3);
+    pushArg(ImmGCPtr(templateObject));
+    pushArg(temp2);
+    pushArg(temp1);
+    return callVM(InitRestParameterInfo, lir);
+}
+
 bool
 CodeGenerator::generateAsmJS()
 {
     // The caller (either another asm.js function or the external-entry
     // trampoline) has placed all arguments in registers and on the stack
     // according to the system ABI. The MAsmJSParameters which represent these
     // parameters have been useFixed()ed to these ABI-specified positions.
     // Thus, there is nothing special to do in the prologue except (possibly)
--- a/js/src/ion/CodeGenerator.h
+++ b/js/src/ion/CodeGenerator.h
@@ -206,16 +206,17 @@ class CodeGenerator : public CodeGenerat
     bool visitOutOfLineLoadTypedArray(OutOfLineLoadTypedArray *ool);
     bool visitCallIteratorStart(LCallIteratorStart *lir);
     bool visitIteratorStart(LIteratorStart *lir);
     bool visitIteratorNext(LIteratorNext *lir);
     bool visitIteratorMore(LIteratorMore *lir);
     bool visitIteratorEnd(LIteratorEnd *lir);
     bool visitArgumentsLength(LArgumentsLength *lir);
     bool visitGetArgument(LGetArgument *lir);
+    bool visitRest(LRest *lir);
     bool visitCallSetProperty(LCallSetProperty *ins);
     bool visitCallDeleteProperty(LCallDeleteProperty *lir);
     bool visitBitNotV(LBitNotV *lir);
     bool visitBitOpV(LBitOpV *lir);
     bool emitInstanceOf(LInstruction *ins, JSObject *prototypeObject);
     bool visitIn(LIn *ins);
     bool visitInArray(LInArray *ins);
     bool visitInstanceOfO(LInstanceOfO *ins);
--- a/js/src/ion/IonBuilder.cpp
+++ b/js/src/ion/IonBuilder.cpp
@@ -1179,16 +1179,19 @@ IonBuilder::inspectOpcode(JSOp op)
         return pushConstant(BooleanValue(false));
 
       case JSOP_TRUE:
         return pushConstant(BooleanValue(true));
 
       case JSOP_ARGUMENTS:
         return jsop_arguments();
 
+      case JSOP_REST:
+        return jsop_rest();
+
       case JSOP_NOTEARG:
         return jsop_notearg();
 
       case JSOP_GETARG:
       case JSOP_CALLARG:
         if (info().argsObjAliasesFormals()) {
             MGetArgumentsObjectArg *getArg = MGetArgumentsObjectArg::New(current->argumentsObject(),
                                                                          GET_SLOTNO(pc));
@@ -6859,16 +6862,72 @@ IonBuilder::jsop_arguments_getelem()
 }
 
 bool
 IonBuilder::jsop_arguments_setelem(MDefinition *object, MDefinition *index, MDefinition *value)
 {
     return abort("NYI arguments[]=");
 }
 
+bool
+IonBuilder::jsop_rest()
+{
+    // We don't know anything about the callee.
+    if (inliningDepth_ == 0) {
+        // Get an empty template array.
+        JSObject *templateObject = getNewArrayTemplateObject(0);
+        if (!templateObject)
+            return false;
+
+        MArgumentsLength *numActuals = MArgumentsLength::New();
+        current->add(numActuals);
+
+        // Pass in the number of actual arguments, the number of formals (not
+        // including the rest parameter slot itself), and the template object.
+        MRest *rest = MRest::New(numActuals, info().nargs() - 1, templateObject);
+        current->add(rest);
+        current->push(rest);
+        return true;
+    }
+
+    // We know the exact number of arguments the callee pushed.
+    unsigned numActuals = inlinedArguments_.length();
+    unsigned numFormals = info().nargs() - 1;
+    unsigned numRest = numActuals - numFormals;
+    JSObject *templateObject = getNewArrayTemplateObject(numRest);
+
+    MNewArray *array = new MNewArray(numRest, templateObject, MNewArray::NewArray_Allocating);
+    current->add(array);
+
+    if (numFormals >= numActuals) {
+        current->push(array);
+        return true;
+    }
+
+    MElements *elements = MElements::New(array);
+    current->add(elements);
+
+    // Unroll the argument copy loop. We don't need to do any bounds or hole
+    // checking here.
+    MConstant *index;
+    for (unsigned i = numFormals; i < numActuals; i++) {
+        index = MConstant::New(Int32Value(i));
+        current->add(index);
+        MStoreElement *store = MStoreElement::New(elements, index, inlinedArguments_[i],
+                                                  /* needsHoleCheck = */ false);
+        current->add(store);
+    }
+
+    MSetInitializedLength *initLength = MSetInitializedLength::New(elements, index);
+    current->add(initLength);
+
+    current->push(array);
+    return true;
+}
+
 inline types::HeapTypeSet *
 GetDefiniteSlot(JSContext *cx, types::StackTypeSet *types, JSAtom *atom)
 {
     if (!types || types->unknownObject() || types->getObjectCount() != 1)
         return NULL;
 
     types::TypeObject *type = types->getTypeObject(0);
     if (!type || type->unknownProperties())
--- a/js/src/ion/IonBuilder.h
+++ b/js/src/ion/IonBuilder.h
@@ -401,16 +401,17 @@ class IonBuilder : public MIRGenerator
     bool jsop_setelem_typed_static(MDefinition *object, MDefinition *index, MDefinition *value,
                                    bool *psucceeded);
     bool jsop_length();
     bool jsop_length_fastPath();
     bool jsop_arguments();
     bool jsop_arguments_length();
     bool jsop_arguments_getelem();
     bool jsop_arguments_setelem(MDefinition *object, MDefinition *index, MDefinition *value);
+    bool jsop_rest();
     bool jsop_not();
     bool jsop_getprop(HandlePropertyName name);
     bool jsop_setprop(HandlePropertyName name);
     bool jsop_delprop(HandlePropertyName name);
     bool jsop_newarray(uint32_t count);
     bool jsop_newobject(HandleObject baseObj);
     bool jsop_initelem();
     bool jsop_initelem_array();
--- a/js/src/ion/LIR-Common.h
+++ b/js/src/ion/LIR-Common.h
@@ -4101,16 +4101,37 @@ class LGetArgument : public LInstruction
     LGetArgument(const LAllocation &index) {
         setOperand(0, index);
     }
     const LAllocation *index() {
         return getOperand(0);
     }
 };
 
+// Create the rest parameter.
+class LRest : public LCallInstructionHelper<1, 1, 3>
+{
+  public:
+    LIR_HEADER(Rest)
+
+    LRest(const LAllocation &numActuals, const LDefinition &temp1, const LDefinition &temp2,
+          const LDefinition &temp3) {
+        setOperand(0, numActuals);
+        setTemp(0, temp1);
+        setTemp(1, temp2);
+        setTemp(2, temp3);
+    }
+    const LAllocation *numActuals() {
+        return getOperand(0);
+    }
+    MRest *mir() const {
+        return mir_->toRest();
+    }
+};
+
 class LParWriteGuard : public LCallInstructionHelper<0, 2, 1>
 {
   public:
     LIR_HEADER(ParWriteGuard);
 
     LParWriteGuard(const LAllocation &parSlice,
                    const LAllocation &object,
                    const LDefinition &temp1) {
--- a/js/src/ion/LOpcodes.h
+++ b/js/src/ion/LOpcodes.h
@@ -198,16 +198,17 @@
     _(IteratorMore)                 \
     _(IteratorEnd)                  \
     _(ArrayLength)                  \
     _(TypedArrayLength)             \
     _(TypedArrayElements)           \
     _(StringLength)                 \
     _(ArgumentsLength)              \
     _(GetArgument)                  \
+    _(Rest)                         \
     _(TypeOfV)                      \
     _(ToIdV)                        \
     _(Floor)                        \
     _(Round)                        \
     _(In)                           \
     _(InArray)                      \
     _(InstanceOfO)                  \
     _(InstanceOfV)                  \
--- a/js/src/ion/Lowering.cpp
+++ b/js/src/ion/Lowering.cpp
@@ -2442,16 +2442,28 @@ LIRGenerator::visitArgumentsLength(MArgu
 bool
 LIRGenerator::visitGetArgument(MGetArgument *ins)
 {
     LGetArgument *lir = new LGetArgument(useRegisterOrConstant(ins->index()));
     return defineBox(lir, ins);
 }
 
 bool
+LIRGenerator::visitRest(MRest *ins)
+{
+    JS_ASSERT(ins->numActuals()->type() == MIRType_Int32);
+
+    LRest *lir = new LRest(useFixed(ins->numActuals(), CallTempReg0),
+                           tempFixed(CallTempReg1),
+                           tempFixed(CallTempReg2),
+                           tempFixed(CallTempReg3));
+    return defineReturn(lir, ins) && assignSafepoint(lir, ins);
+}
+
+bool
 LIRGenerator::visitThrow(MThrow *ins)
 {
     MDefinition *value = ins->getOperand(0);
     JS_ASSERT(value->type() == MIRType_Value);
 
     LThrow *lir = new LThrow;
     if (!useBoxAtStart(lir, LThrow::Value, value))
         return false;
--- a/js/src/ion/Lowering.h
+++ b/js/src/ion/Lowering.h
@@ -213,16 +213,17 @@ class LIRGenerator : public LIRGenerator
     bool visitCallSetProperty(MCallSetProperty *ins);
     bool visitIteratorStart(MIteratorStart *ins);
     bool visitIteratorNext(MIteratorNext *ins);
     bool visitIteratorMore(MIteratorMore *ins);
     bool visitIteratorEnd(MIteratorEnd *ins);
     bool visitStringLength(MStringLength *ins);
     bool visitArgumentsLength(MArgumentsLength *ins);
     bool visitGetArgument(MGetArgument *ins);
+    bool visitRest(MRest *ins);
     bool visitThrow(MThrow *ins);
     bool visitIn(MIn *ins);
     bool visitInArray(MInArray *ins);
     bool visitInstanceOf(MInstanceOf *ins);
     bool visitCallInstanceOf(MCallInstanceOf *ins);
     bool visitFunctionBoundary(MFunctionBoundary *ins);
     bool visitIsCallable(MIsCallable *ins);
     bool visitAsmJSLoadHeap(MAsmJSLoadHeap *ins);
--- a/js/src/ion/MIR.h
+++ b/js/src/ion/MIR.h
@@ -7019,17 +7019,58 @@ class MGetArgument
     TypePolicy *typePolicy() {
         return this;
     }
     bool congruentTo(MDefinition *const &ins) const {
         return congruentIfOperandsEqual(ins);
     }
     AliasSet getAliasSet() const {
         return AliasSet::None();
-   }
+    }
+};
+
+class MRest
+  : public MUnaryInstruction,
+    public IntPolicy<0>
+{
+    unsigned numFormals_;
+    CompilerRootObject templateObject_;
+
+    MRest(MDefinition *numActuals, unsigned numFormals, JSObject *templateObject)
+      : MUnaryInstruction(numActuals),
+        numFormals_(numFormals),
+        templateObject_(templateObject)
+    {
+        setResultType(MIRType_Object);
+        setResultTypeSet(MakeSingletonTypeSet(templateObject));
+    }
+
+  public:
+    INSTRUCTION_HEADER(Rest);
+
+    static MRest *New(MDefinition *numActuals, unsigned numFormals, JSObject *templateObject) {
+        return new MRest(numActuals, numFormals, templateObject);
+    }
+
+    MDefinition *numActuals() const {
+        return getOperand(0);
+    }
+    unsigned numFormals() const {
+        return numFormals_;
+    }
+    JSObject *templateObject() const {
+        return templateObject_;
+    }
+
+    TypePolicy *typePolicy() {
+        return this;
+    }
+    AliasSet getAliasSet() const {
+        return AliasSet::None();
+    }
 };
 
 class MParWriteGuard
   : public MBinaryInstruction,
     public ObjectPolicy<1>
 {
     MParWriteGuard(MDefinition *parThreadContext,
                    MDefinition *obj)
--- a/js/src/ion/MOpcodes.h
+++ b/js/src/ion/MOpcodes.h
@@ -148,16 +148,17 @@ namespace ion {
     _(SetPropertyCache)                                                     \
     _(IteratorStart)                                                        \
     _(IteratorNext)                                                         \
     _(IteratorMore)                                                         \
     _(IteratorEnd)                                                          \
     _(StringLength)                                                         \
     _(ArgumentsLength)                                                      \
     _(GetArgument)                                                          \
+    _(Rest)                                                                 \
     _(Floor)                                                                \
     _(Round)                                                                \
     _(In)                                                                   \
     _(InstanceOf)                                                           \
     _(CallInstanceOf)                                                       \
     _(InterruptCheck)                                                       \
     _(FunctionBoundary)                                                     \
     _(GetDOMProperty)                                                       \
--- a/js/src/ion/ParallelArrayAnalysis.cpp
+++ b/js/src/ion/ParallelArrayAnalysis.cpp
@@ -233,16 +233,17 @@ class ParallelArrayVisitor : public MIns
     UNSAFE_OP(SetPropertyCache)
     UNSAFE_OP(IteratorStart)
     UNSAFE_OP(IteratorNext)
     UNSAFE_OP(IteratorMore)
     UNSAFE_OP(IteratorEnd)
     SAFE_OP(StringLength)
     UNSAFE_OP(ArgumentsLength)
     UNSAFE_OP(GetArgument)
+    SAFE_OP(Rest)
     SAFE_OP(Floor)
     SAFE_OP(Round)
     UNSAFE_OP(InstanceOf)
     CUSTOM_OP(InterruptCheck)
     SAFE_OP(ParSlice)
     SAFE_OP(ParNew)
     SAFE_OP(ParNewDenseArray)
     SAFE_OP(ParNewCallObject)
--- a/js/src/ion/VMFunctions.cpp
+++ b/js/src/ion/VMFunctions.cpp
@@ -680,16 +680,44 @@ NewArgumentsObject(JSContext *cx, Baseli
 {
     ArgumentsObject *obj = ArgumentsObject::createExpected(cx, frame);
     if (!obj)
         return false;
     res.setObject(*obj);
     return true;
 }
 
+JSObject *
+InitRestParameter(JSContext *cx, uint32_t length, Value *rest, HandleObject templateObj,
+                  HandleObject res)
+{
+    if (res) {
+        JS_ASSERT(res->isArray());
+        JS_ASSERT(!res->getDenseInitializedLength());
+        JS_ASSERT(res->type() == templateObj->type());
+
+        // Fast path: we managed to allocate the array inline; initialize the
+        // slots.
+        if (length) {
+            if (!res->ensureElements(cx, length))
+                return NULL;
+            res->setDenseInitializedLength(length);
+            res->initDenseElements(0, rest, length);
+            res->setArrayLengthInt32(length);
+        }
+        return res;
+    }
+
+    JSObject *obj = NewDenseCopiedArray(cx, length, rest, NULL);
+    if (!obj)
+        return NULL;
+    obj->setType(templateObj->type());
+    return obj;
+}
+
 bool
 HandleDebugTrap(JSContext *cx, BaselineFrame *frame, uint8_t *retAddr, JSBool *mustReturn)
 {
     *mustReturn = false;
 
     RootedScript script(cx, frame->script());
     jsbytecode *pc = script->baselineScript()->icEntryFromReturnAddress(retAddr).pc(script);
 
--- a/js/src/ion/VMFunctions.h
+++ b/js/src/ion/VMFunctions.h
@@ -536,16 +536,19 @@ uint32_t GetIndexFromString(JSString *st
 bool DebugPrologue(JSContext *cx, BaselineFrame *frame, JSBool *mustReturn);
 bool DebugEpilogue(JSContext *cx, BaselineFrame *frame, JSBool ok);
 
 bool StrictEvalPrologue(JSContext *cx, BaselineFrame *frame);
 bool HeavyweightFunPrologue(JSContext *cx, BaselineFrame *frame);
 
 bool NewArgumentsObject(JSContext *cx, BaselineFrame *frame, MutableHandleValue res);
 
+JSObject *InitRestParameter(JSContext *cx, uint32_t length, Value *rest, HandleObject templateObj,
+                            HandleObject res);
+
 bool HandleDebugTrap(JSContext *cx, BaselineFrame *frame, uint8_t *retAddr, JSBool *mustReturn);
 bool OnDebuggerStatement(JSContext *cx, BaselineFrame *frame, jsbytecode *pc, JSBool *mustReturn);
 
 bool EnterBlock(JSContext *cx, BaselineFrame *frame, Handle<StaticBlockObject *> block);
 bool LeaveBlock(JSContext *cx, BaselineFrame *frame);
 
 bool InitBaselineFrameForOsr(BaselineFrame *frame, StackFrame *interpFrame,
                              uint32_t numStackValues);
--- a/js/src/jsanalyze.cpp
+++ b/js/src/jsanalyze.cpp
@@ -519,16 +519,17 @@ ScriptAnalysis::analyzeBytecode(JSContex
           case JSOP_UINT24:
           case JSOP_GETXPROP:
           case JSOP_INT8:
           case JSOP_INT32:
           case JSOP_HOLE:
           case JSOP_LOOPHEAD:
           case JSOP_LOOPENTRY:
           case JSOP_NOTEARG:
+          case JSOP_REST:
             break;
 
           default:
             isJaegerCompileable = false;
             isJaegerInlineable = isIonInlineable = false;
             break;
         }