author | Sean Stangl <sstangl@mozilla.com> |
Thu, 28 Jun 2012 18:58:51 -0700 | |
changeset 106486 | f79d7ca811e626f9f302ef92436c28d9cb91146d |
parent 106485 | 0cb6898fc0266e964938ee4895722e9c8dc52cc2 |
child 106487 | 97e97b5b6db1b540aaca6f837083c771b5348c70 |
push id | 23447 |
push user | danderson@mozilla.com |
push date | Tue, 11 Sep 2012 17:34:27 +0000 |
treeherder | mozilla-central@fdfaef738a00 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | dvander |
bugs | 768270 |
milestone | 16.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
|
--- a/js/src/ion/CodeGenerator.cpp +++ b/js/src/ion/CodeGenerator.cpp @@ -1241,16 +1241,41 @@ CodeGenerator::visitOutOfLineCreateThis( { if (!visitCreateThisVMCall(ool->lir())) return false; masm.jump(ool->rejoin()); return true; } bool +CodeGenerator::visitReturnFromCtor(LReturnFromCtor *lir) +{ + ValueOperand value = ToValue(lir, LReturnFromCtor::ValueIndex); + Register obj = ToRegister(lir->getObject()); + Register output = ToRegister(lir->output()); + + Label valueIsObject, end; + + masm.branchTestObject(Assembler::Equal, value, &valueIsObject); + + // Value is not an object. Return that other object. + masm.movePtr(obj, output); + masm.jump(&end); + + // Value is an object. Return unbox(Value). + masm.bind(&valueIsObject); + Register payload = masm.extractObject(value, output); + if (payload != output) + masm.movePtr(payload, output); + + masm.bind(&end); + return true; +} + +bool CodeGenerator::visitArrayLength(LArrayLength *lir) { Address length(ToRegister(lir->elements()), ObjectElements::offsetOfLength()); masm.load32(length, ToRegister(lir->output())); return true; } bool
--- a/js/src/ion/CodeGenerator.h +++ b/js/src/ion/CodeGenerator.h @@ -115,16 +115,17 @@ class CodeGenerator : public CodeGenerat bool visitNewObjectVMCall(LNewObject *lir); bool visitNewObject(LNewObject *lir); bool visitOutOfLineNewObject(OutOfLineNewObject *ool); bool visitNewCallObject(LNewCallObject *lir); bool visitInitProp(LInitProp *lir); bool visitCreateThisVMCall(LCreateThis *lir); bool visitCreateThis(LCreateThis *lir); bool visitOutOfLineCreateThis(OutOfLineCreateThis *ool); + bool visitReturnFromCtor(LReturnFromCtor *lir); bool visitArrayLength(LArrayLength *lir); bool visitTypedArrayLength(LTypedArrayLength *lir); bool visitTypedArrayElements(LTypedArrayElements *lir); bool visitStringLength(LStringLength *lir); bool visitInitializedLength(LInitializedLength *lir); bool visitSetInitializedLength(LSetInitializedLength *lir); bool visitNotV(LNotV *ins); bool visitBoundsCheck(LBoundsCheck *lir);
--- a/js/src/ion/IonBuilder.cpp +++ b/js/src/ion/IonBuilder.cpp @@ -357,49 +357,54 @@ IonBuilder::processIterators() return true; } bool IonBuilder::buildInline(IonBuilder *callerBuilder, MResumePoint *callerResumePoint, MDefinition *thisDefn, MDefinitionVector &argv) { + IonSpew(IonSpew_MIR, "Inlining script %s:%d (%p)", + script->filename, script->lineno, (void *)script); + + callerBuilder_ = callerBuilder; + callerResumePoint_ = callerResumePoint; + + // Generate single entrance block. current = newBlock(pc); if (!current) return false; - IonSpew(IonSpew_MIR, "Inlining script %s:%d (%p)", - script->filename, script->lineno, (void *) script); - - callerBuilder_ = callerBuilder; - callerResumePoint_ = callerResumePoint; + current->setCallerResumePoint(callerResumePoint); + + // Connect the entrance block to the last block in the caller's graph. MBasicBlock *predecessor = callerResumePoint->block(); predecessor->end(MGoto::New(current)); if (!current->addPredecessorWithoutPhis(predecessor)) return false; + JS_ASSERT(predecessor->numSuccessors() == 1); JS_ASSERT(current->numPredecessors() == 1); - current->setCallerResumePoint(callerResumePoint); - - // Fill in any missing arguments with undefined. + + // Explicitly pass Undefined for missing arguments. const size_t numActualArgs = argv.length() - 1; const size_t nargs = info().nargs(); - IonSpew(IonSpew_MIR, "Inlining with %s of arguments.", - (numActualArgs == nargs) ? "right number": ((numActualArgs < nargs) ? "underflow" : "overflow")); + if (numActualArgs < nargs) { - for (size_t i = 0, missing = nargs - numActualArgs; i < missing; ++i) { + const size_t missing = nargs - numActualArgs; + + for (size_t i = 0; i < missing; i++) { MConstant *undef = MConstant::New(UndefinedValue()); current->add(undef); if (!argv.append(undef)) return false; } } - // The oracle ensures that the inlined script does not use the scope chain, so we - // just initialize its slot to |undefined|. + // The Oracle ensures that the inlined script does not use the scope chain. JS_ASSERT(!script->analysis()->usesScopeChain()); MInstruction *scope = MConstant::New(UndefinedValue()); current->add(scope); current->initSlot(info().scopeChainSlot(), scope); current->initSlot(info().thisSlot(), thisDefn); IonSpew(IonSpew_Inlining, "Initializing %u arg slots", nargs); @@ -420,17 +425,17 @@ IonBuilder::buildInline(IonBuilder *call MConstant *undef = MConstant::New(UndefinedValue()); current->add(undef); current->initSlot(info().localSlot(i), undef); } IonSpew(IonSpew_Inlining, "Inline entry block MResumePoint %p, %u operands", (void *) current->entryResumePoint(), current->entryResumePoint()->numOperands()); - // Note: +2 for the scope chain and |this|. + // +2 for the scope chain and |this|. JS_ASSERT(current->entryResumePoint()->numOperands() == nargs + info().nlocals() + 2); return traverseBytecode(); } // Apply Type Inference information to parameters early on, unboxing them if // they have a definitive type. The actual guards will be emitted by the code // generator, explicitly, as part of the function prologue. @@ -2719,80 +2724,100 @@ IonBuilder::jsop_notearg() return abort("NYI: escaping of the argument object."); current->add(arg); current->push(arg); return true; } bool -IonBuilder::jsop_call_inline(JSFunction *callee, uint32 argc, IonBuilder &inlineBuilder) +IonBuilder::jsop_call_inline(IonBuilder &inlineBuilder, HandleFunction callee, + uint32 argc, bool constructing) { #ifdef DEBUG uint32 origStackDepth = current->stackDepth(); #endif // |top| jumps into the callee subgraph -- save it for later use. MBasicBlock *top = current; // Add the function constant before the resume point which map call // arguments. MConstant *constFun = MConstant::New(ObjectValue(*callee)); current->add(constFun); // This resume point collects outer variables only. It is used to recover // the stack state before the current bytecode. - MResumePoint *inlineResumePoint = MResumePoint::New(top, pc, callerResumePoint_, - MResumePoint::Outer); + MResumePoint *inlineResumePoint = + MResumePoint::New(top, pc, callerResumePoint_, MResumePoint::Outer); if (!inlineResumePoint) return false; // We do not inline JSOP_FUNCALL for now. JS_ASSERT(argc == GET_ARGC(inlineResumePoint->pc())); // Gather up the arguments and |this| to the inline function. // Note that we leave the callee on the simulated stack for the // duration of the call. MDefinitionVector argv; - - // Arguments are popped right-to-left so we have to fill |args| backwards. if (!discardCallArgs(argc, argv, top)) return false; // Replace the potential object load by the corresponding constant version // which is inlined here. We do this to ensure that on a bailout, we can get // back to the caller by iterating over the stack. inlineResumePoint->replaceOperand(inlineResumePoint->numOperands() - (argc + 2), constFun); current->pop(); current->push(constFun); - MDefinition *thisDefn = argv[0]; + // Create |this| on the caller-side for inlined constructors. + MDefinition *thisDefn = NULL; + if (constructing) { + thisDefn = createThis(callee, constFun); + if (!thisDefn) + return false; + } else { + thisDefn = argv[0]; + } // Build the graph. if (!inlineBuilder.buildInline(this, inlineResumePoint, thisDefn, argv)) return false; MIRGraphExits &exits = *inlineBuilder.graph().exitAccumulator(); // Create a |bottom| block for all the callee subgraph exits to jump to. JS_ASSERT(types::IsInlinableCall(pc)); jsbytecode *postCall = GetNextPc(pc); MBasicBlock *bottom = newBlock(NULL, postCall); bottom->setCallerResumePoint(callerResumePoint_); - // Link graph exits to |bottom| via MGotos, replacing MReturns. + // Replace all MReturns with MGotos, and remember the MDefinition that + // would have been returned. Vector<MDefinition *, 8, IonAllocPolicy> retvalDefns; for (MBasicBlock **it = exits.begin(), **end = exits.end(); it != end; ++it) { MBasicBlock *exitBlock = *it; - JS_ASSERT(exitBlock->lastIns()->op() == MDefinition::Op_Return); - - if (!retvalDefns.append(exitBlock->lastIns()->toReturn()->getOperand(0))) + + MDefinition *rval = exitBlock->lastIns()->toReturn()->getOperand(0); + exitBlock->discardLastIns(); + + // Inlined constructors return |this| unless overridden by another Object. + if (constructing) { + if (rval->type() == MIRType_Value) { + MReturnFromCtor *filter = MReturnFromCtor::New(rval, thisDefn); + rval->block()->add(filter); + rval = filter; + } else if (rval->type() != MIRType_Object) { + rval = thisDefn; + } + } + + if (!retvalDefns.append(rval)) return false; - exitBlock->discardLastIns(); MGoto *replacement = MGoto::New(bottom); exitBlock->end(replacement); if (!bottom->addPredecessorWithoutPhis(exitBlock)) return false; } JS_ASSERT(!retvalDefns.empty()); if (!bottom->inheritNonPredecessor(top)) @@ -2854,17 +2879,17 @@ static bool IsSmallFunction(JSFunction * JSScript *script = target->script(); if(script->length > js_IonOptions.smallFunctionMaxBytecodeLength) return false; return true; } bool -IonBuilder::makeInliningDecision(JSFunction *target) +IonBuilder::makeInliningDecision(HandleFunction target) { static const size_t INLINING_LIMIT = 3; if (inliningDepth >= INLINING_LIMIT) return false; // For "small" functions, we should be more aggressive about inlining. // This is based on the following intuition: @@ -2893,17 +2918,17 @@ IonBuilder::makeInliningDecision(JSFunct IonSpew(IonSpew_Inlining, "Decided not to inline"); return false; } return true; } bool -IonBuilder::inlineScriptedCall(JSFunction *target, uint32 argc) +IonBuilder::inlineScriptedCall(HandleFunction target, uint32 argc, bool constructing) { IonSpew(IonSpew_Inlining, "Recursively building"); // Compilation information is allocated for the duration of the current tempLifoAlloc // lifetime. CompileInfo *info = cx->tempLifoAlloc().new_<CompileInfo>(target->script(), target, (jsbytecode *)NULL); if (!info) @@ -2915,17 +2940,17 @@ IonBuilder::inlineScriptedCall(JSFunctio TypeInferenceOracle oracle; if (!oracle.init(cx, target->script())) return false; RootedObject scopeChain(NULL); IonBuilder inlineBuilder(cx, scopeChain, temp(), graph(), &oracle, *info, inliningDepth + 1, loopDepth_); - return jsop_call_inline(target, argc, inlineBuilder); + return jsop_call_inline(inlineBuilder, target, argc, constructing); } void IonBuilder::copyFormalIntoCallObj(MDefinition *callObj, MDefinition *slots, unsigned formal) { // Note that in the case of using dynamic slots, RESERVED_SLOTS == numFixedSlots. MDefinition *param = current->getSlot(info().argSlot(formal)); if (slots->type() == MIRType_Slots) @@ -3153,18 +3178,18 @@ IonBuilder::jsop_call_fun_barrier(Handle return true; case InliningStatus_Error: return false; case InliningStatus_NotInlined: break; } } - if (!constructing && makeInliningDecision(target)) - return inlineScriptedCall(target, argc); + if (makeInliningDecision(target)) + return inlineScriptedCall(target, argc, constructing); } return makeCallBarrier(target, argc, constructing, types, barrier); } bool IonBuilder::jsop_call(uint32 argc, bool constructing) {
--- a/js/src/ion/IonBuilder.h +++ b/js/src/ion/IonBuilder.h @@ -192,18 +192,18 @@ class IonBuilder : public MIRGenerator static int CmpSuccessors(const void *a, const void *b); public: IonBuilder(JSContext *cx, HandleObject scopeChain, TempAllocator &temp, MIRGraph &graph, TypeOracle *oracle, CompileInfo &info, size_t inliningDepth = 0, uint32 loopDepth = 0); bool build(); - bool buildInline(IonBuilder *callerBuilder, MResumePoint *callerResumePoint, MDefinition *thisDefn, - MDefinitionVector &args); + bool buildInline(IonBuilder *callerBuilder, MResumePoint *callerResumePoint, + MDefinition *thisDefn, MDefinitionVector &args); private: bool traverseBytecode(); ControlStatus snoopControlFlow(JSOp op); void markPhiBytecodeUses(jsbytecode *pc); bool processIterators(); bool inspectOpcode(JSOp op); uint32 readIndex(jsbytecode *pc); @@ -400,19 +400,20 @@ class IonBuilder : public MIRGenerator // String natives. InliningStatus inlineStrCharCodeAt(uint32 argc, bool constructing); InliningStatus inlineStrFromCharCode(uint32 argc, bool constructing); InliningStatus inlineStrCharAt(uint32 argc, bool constructing); InliningStatus inlineNativeCall(JSNative native, uint32 argc, bool constructing); - bool jsop_call_inline(JSFunction *callee, uint32 argc, IonBuilder &inlineBuilder); - bool inlineScriptedCall(JSFunction *target, uint32 argc); - bool makeInliningDecision(JSFunction *target); + bool jsop_call_inline(IonBuilder &inlineBuilder, HandleFunction callee, + uint32 argc, bool constructing); + bool inlineScriptedCall(HandleFunction target, uint32 argc, bool constructing); + bool makeInliningDecision(HandleFunction target); bool jsop_call_fun_barrier(HandleFunction target, uint32 argc, bool constructing, types::TypeSet *types, types::TypeSet *barrier); bool makeCallBarrier(HandleFunction target, uint32 argc, bool constructing, types::TypeSet *types, types::TypeSet *barrier);
--- a/js/src/ion/LIR-Common.h +++ b/js/src/ion/LIR-Common.h @@ -426,16 +426,40 @@ class LCreateThis : public LInstructionH return getDef(0); } MCreateThis *mir() const { return mir_->toCreateThis(); } }; +// If the Value is an Object, return unbox(Value). +// Otherwise, return the other Object. +class LReturnFromCtor : public LInstructionHelper<1, BOX_PIECES + 1, 0> +{ + public: + LIR_HEADER(ReturnFromCtor); + + LReturnFromCtor(const LAllocation &object) + { + // Value set by useBox() during lowering. + setOperand(LReturnFromCtor::ObjectIndex, object); + } + + const LAllocation *getObject() { + return getOperand(LReturnFromCtor::ObjectIndex); + } + const LDefinition *output() { + return getDef(0); + } + + static const size_t ValueIndex = 0; + static const size_t ObjectIndex = BOX_PIECES; +}; + // Writes an argument for a function call to the frame's argument vector. class LStackArg : public LInstructionHelper<0, BOX_PIECES, 0> { uint32 argslot_; // Index into frame-scope argument vector. public: LIR_HEADER(StackArg);
--- a/js/src/ion/LOpcodes.h +++ b/js/src/ion/LOpcodes.h @@ -63,16 +63,17 @@ _(CheckOverRecursed) \ _(RecompileCheck) \ _(DefVar) \ _(CallGeneric) \ _(CallNative) \ _(CallConstructor) \ _(StackArg) \ _(CreateThis) \ + _(ReturnFromCtor) \ _(BitNotI) \ _(BitNotV) \ _(BitOpI) \ _(BitOpV) \ _(ShiftOp) \ _(Return) \ _(Throw) \ _(Phi) \
--- a/js/src/ion/Lowering.cpp +++ b/js/src/ion/Lowering.cpp @@ -227,16 +227,26 @@ LIRGenerator::visitCreateThis(MCreateThi { LCreateThis *lir = new LCreateThis(useRegister(ins->getCallee()), useRegister(ins->getPrototype())); return define(lir, ins) && assignSafepoint(lir, ins); } bool +LIRGenerator::visitReturnFromCtor(MReturnFromCtor *ins) +{ + LReturnFromCtor *lir = new LReturnFromCtor(useRegister(ins->getObject())); + if (!useBox(lir, LReturnFromCtor::ValueIndex, ins->getValue())) + return false; + + return define(lir, ins); +} + +bool LIRGenerator::visitCall(MCall *call) { JS_ASSERT(CallTempReg0 != CallTempReg1); JS_ASSERT(CallTempReg0 != ArgumentsRectifierReg); JS_ASSERT(CallTempReg1 != ArgumentsRectifierReg); JS_ASSERT(call->getFunction()->type() == MIRType_Object); // Height of the current argument vector.
--- a/js/src/ion/Lowering.h +++ b/js/src/ion/Lowering.h @@ -116,16 +116,17 @@ class LIRGenerator : public LIRGenerator bool visitNewObject(MNewObject *ins); bool visitNewCallObject(MNewCallObject *ins); bool visitInitProp(MInitProp *ins); bool visitCheckOverRecursed(MCheckOverRecursed *ins); bool visitDefVar(MDefVar *ins); bool visitPrepareCall(MPrepareCall *ins); bool visitPassArg(MPassArg *arg); bool visitCreateThis(MCreateThis *ins); + bool visitReturnFromCtor(MReturnFromCtor *ins); bool visitCall(MCall *call); bool visitTest(MTest *test); bool visitCompare(MCompare *comp); bool visitTypeOf(MTypeOf *ins); bool visitToId(MToId *ins); bool visitBitNot(MBitNot *ins); bool visitBitAnd(MBitAnd *ins); bool visitBitOr(MBitOr *ins);
--- a/js/src/ion/MIR.h +++ b/js/src/ion/MIR.h @@ -1516,16 +1516,52 @@ class MCreateThis AliasSet getAliasSet() const { return AliasSet::None(); } TypePolicy *typePolicy() { return this; } }; +// Given a MIRType_Value A and a MIRType_Object B: +// If the Value may be safely unboxed to an Object, return Object(A). +// Otherwise, return B. +// Used to implement return behavior for inlined constructors. +class MReturnFromCtor + : public MAryInstruction<2>, + public MixPolicy<BoxPolicy<0>, ObjectPolicy<1> > +{ + MReturnFromCtor(MDefinition *value, MDefinition *object) { + initOperand(0, value); + initOperand(1, object); + setResultType(MIRType_Object); + } + + public: + INSTRUCTION_HEADER(ReturnFromCtor); + static MReturnFromCtor *New(MDefinition *value, MDefinition *object) + { + return new MReturnFromCtor(value, object); + } + + MDefinition *getValue() const { + return getOperand(0); + } + MDefinition *getObject() const { + return getOperand(1); + } + + AliasSet getAliasSet() const { + return AliasSet::None(); + } + TypePolicy *typePolicy() { + return this; + } +}; + // Passes an MDefinition to an MCall. Must occur between an MPrepareCall and // MCall. Boxes the input and stores it to the correct location on stack. // // Arguments are *not* simply pushed onto a call stack: they are evaluated // left-to-right, but stored in the arg vector in C-style, right-to-left. class MPassArg : public MUnaryInstruction, public BoxInputsPolicy
--- a/js/src/ion/MOpcodes.h +++ b/js/src/ion/MOpcodes.h @@ -52,16 +52,17 @@ namespace ion { _(Callee) \ _(TableSwitch) \ _(Goto) \ _(Test) \ _(Compare) \ _(Phi) \ _(OsrValue) \ _(OsrScopeChain) \ + _(ReturnFromCtor) \ _(CheckOverRecursed) \ _(RecompileCheck) \ _(DefVar) \ _(CreateThis) \ _(PrepareCall) \ _(PassArg) \ _(Call) \ _(BitNot) \
--- a/js/src/jsinferinlines.h +++ b/js/src/jsinferinlines.h @@ -154,26 +154,30 @@ TypeIdString(jsid id) { #ifdef DEBUG return TypeIdStringImpl(id); #else return "(missing)"; #endif } -/* Assert code to know which PCs are reasonable to be considering inlining on */ +/* Assert code to know which PCs are reasonable to be considering inlining on. */ inline bool IsInlinableCall(jsbytecode *pc) { JSOp op = JSOp(*pc); // CALL, FUNCALL, FUNAPPLY (Standard callsites) + // NEW (IonMonkey-only callsite) // GETPROP, CALLPROP, and LENGTH. (Inlined Getters) // SETPROP, SETNAME, SETGNAME (Inlined Setters) return op == JSOP_CALL || op == JSOP_FUNCALL || op == JSOP_FUNAPPLY || +#ifdef JS_ION + op == JSOP_NEW || +#endif op == JSOP_GETPROP || op == JSOP_CALLPROP || op == JSOP_LENGTH || op == JSOP_SETPROP || op == JSOP_SETGNAME || op == JSOP_SETNAME; } /* * Structure for type inference entry point functions. All functions which can * change type information must use this, and functions which depend on