| author | Kannan Vijayan <kvijayan@mozilla.com> |
| Mon, 23 Sep 2013 10:50:30 -0400 | |
| changeset 148329 | cd646a300ffee771ea4a94e3e4910e1901a043da |
| parent 148328 | 7a733fffc6e92351b64b1775e91b41edd9f7bade |
| child 148330 | a8ba4fdc133fdc8c09a4c7f5bc3a0f6cb7387f4b |
| push id | 25336 |
| push user | ryanvm@gmail.com |
| push date | Mon, 23 Sep 2013 21:01:41 +0000 |
| treeherder | mozilla-central@8b28b4bed72c [default view] [failures only] |
| reviewers | h4writer |
| bugs | 918405 |
| milestone | 27.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/jit/BaselineFrame.h +++ b/js/src/jit/BaselineFrame.h @@ -153,18 +153,18 @@ class BaselineFrame Value &unaliasedVar(unsigned i, MaybeCheckAliasing checkAliasing = CHECK_ALIASING) const { JS_ASSERT_IF(checkAliasing, !script()->varIsAliased(i)); JS_ASSERT(i < script()->nfixed); return *valueSlot(i); } Value &unaliasedFormal(unsigned i, MaybeCheckAliasing checkAliasing = CHECK_ALIASING) const { JS_ASSERT(i < numFormalArgs()); - JS_ASSERT_IF(checkAliasing, !script()->argsObjAliasesFormals()); - JS_ASSERT_IF(checkAliasing, !script()->formalIsAliased(i)); + JS_ASSERT_IF(checkAliasing, !script()->argsObjAliasesFormals() && + !script()->formalIsAliased(i)); return argv()[i]; } Value &unaliasedActual(unsigned i, MaybeCheckAliasing checkAliasing = CHECK_ALIASING) const { JS_ASSERT(i < numActualArgs()); JS_ASSERT_IF(checkAliasing, !script()->argsObjAliasesFormals()); JS_ASSERT_IF(checkAliasing && i < numFormalArgs(), !script()->formalIsAliased(i)); return argv()[i];
--- a/js/src/jit/BaselineIC.cpp +++ b/js/src/jit/BaselineIC.cpp @@ -842,19 +842,23 @@ PrepareOsrTempData(JSContext *cx, ICUseC // // Initialize the fake StackFrame. // // Copy formal args and thisv. memcpy(stackFrameStart, frame->argv() - 1, (numFormalArgs + 1) * sizeof(Value)); - // Initialize ScopeChain, Exec, and Flags fields in StackFrame struct. + // Initialize ScopeChain, Exec, ArgsObj, and Flags fields in StackFrame struct. uint8_t *stackFrame = info->stackFrame; *((JSObject **) (stackFrame + StackFrame::offsetOfScopeChain())) = frame->scopeChain(); + if (frame->script()->needsArgsObj()) { + JS_ASSERT(frame->hasArgsObj()); + *((JSObject **) (stackFrame + StackFrame::offsetOfArgumentsObject())) = &frame->argsObj(); + } if (frame->isFunctionFrame()) { // Store the function in exec field, and StackFrame::FUNCTION for flags. *((JSFunction **) (stackFrame + StackFrame::offsetOfExec())) = frame->fun(); *((uint32_t *) (stackFrame + StackFrame::offsetOfFlags())) = StackFrame::FUNCTION; } else { *((JSScript **) (stackFrame + StackFrame::offsetOfExec())) = frame->script(); *((uint32_t *) (stackFrame + StackFrame::offsetOfFlags())) = 0; }
--- a/js/src/jit/CodeGenerator.cpp +++ b/js/src/jit/CodeGenerator.cpp @@ -1020,16 +1020,28 @@ CodeGenerator::visitOsrScopeChain(LOsrSc const ptrdiff_t frameOffset = StackFrame::offsetOfScopeChain(); masm.loadPtr(Address(ToRegister(frame), frameOffset), ToRegister(object)); return true; } bool +CodeGenerator::visitOsrArgumentsObject(LOsrArgumentsObject *lir) +{ + const LAllocation *frame = lir->getOperand(0); + const LDefinition *object = lir->getDef(0); + + const ptrdiff_t frameOffset = StackFrame::offsetOfArgumentsObject(); + + masm.loadPtr(Address(ToRegister(frame), frameOffset), ToRegister(object)); + return true; +} + +bool CodeGenerator::visitStackArgT(LStackArgT *lir) { const LAllocation *arg = lir->getArgument(); MIRType argType = lir->mir()->getArgument()->type(); uint32_t argslot = lir->argslot(); int32_t stack_offset = StackOffsetOfPassedArg(argslot); Address dest(StackPointer, stack_offset);
--- a/js/src/jit/CodeGenerator.h +++ b/js/src/jit/CodeGenerator.h @@ -61,16 +61,17 @@ class CodeGenerator : public CodeGenerat bool visitParameter(LParameter *lir); bool visitCallee(LCallee *lir); bool visitStart(LStart *lir); bool visitReturn(LReturn *ret); bool visitDefVar(LDefVar *lir); bool visitDefFun(LDefFun *lir); bool visitOsrEntry(LOsrEntry *lir); bool visitOsrScopeChain(LOsrScopeChain *lir); + bool visitOsrArgumentsObject(LOsrArgumentsObject *lir); bool visitStackArgT(LStackArgT *lir); bool visitStackArgV(LStackArgV *lir); bool visitMoveGroup(LMoveGroup *group); bool visitValueToInt32(LValueToInt32 *lir); bool visitValueToDouble(LValueToDouble *lir); bool visitValueToFloat32(LValueToFloat32 *lir); bool visitFloat32ToDouble(LFloat32ToDouble *lir); bool visitDoubleToFloat32(LDoubleToFloat32 *lir);
--- a/js/src/jit/Ion.cpp +++ b/js/src/jit/Ion.cpp @@ -1698,22 +1698,16 @@ CheckScript(JSContext *cx, JSScript *scr return false; } if (!script->analyzedArgsUsage() && !script->ensureRanAnalysis(cx)) { IonSpew(IonSpew_Abort, "OOM under ensureRanAnalysis"); return false; } - if (osr && script->needsArgsObj()) { - // OSR-ing into functions with arguments objects is not supported. - IonSpew(IonSpew_Abort, "OSR script has argsobj"); - return false; - } - if (!script->compileAndGo) { IonSpew(IonSpew_Abort, "not compile-and-go"); return false; } return true; }
--- a/js/src/jit/IonBuilder.cpp +++ b/js/src/jit/IonBuilder.cpp @@ -18,16 +18,18 @@ #include "jit/BaselineInspector.h" #include "jit/ExecutionModeInlines.h" #include "jit/Ion.h" #include "jit/IonAnalysis.h" #include "jit/IonSpewer.h" #include "jit/Lowering.h" #include "jit/MIRGraph.h" +#include "vm/ArgumentsObject.h" + #include "jsinferinlines.h" #include "jsobjinlines.h" #include "jsscriptinlines.h" #include "jit/CompileInfo-inl.h" using namespace js; using namespace js::jit; @@ -1028,17 +1030,17 @@ IonBuilder::maybeAddOsrTypeBarriers() for (uint32_t i = info().startArgSlot(); i < osrBlock->stackDepth(); i++, headerPhi++) { // Aliased slots are never accessed, since they need to go through // the callobject. The typebarriers are added there and can be // discared here. if (info().isSlotAliased(i)) continue; - MInstruction *def = osrBlock->getSlot(i)->toOsrValue(); + MInstruction *def = osrBlock->getSlot(i)->toInstruction(); JS_ASSERT(headerPhi->slot() == i); MPhi *preheaderPhi = preheader->getSlot(i)->toPhi(); MIRType type = headerPhi->type(); types::TemporaryTypeSet *typeSet = headerPhi->resultTypeSet(); if (!addOsrValueTypeBarrier(i, &def, type, typeSet)) @@ -5749,44 +5751,63 @@ IonBuilder::newOsrPreheader(MBasicBlock // slot. scopev = MConstant::New(UndefinedValue()); } osrBlock->add(scopev); osrBlock->initSlot(slot, scopev); } - // Initialize arguments object. Ion will not allow OSR-ing into scripts - // with |needsArgsObj| set, so this can be undefined. - JS_ASSERT(!info().needsArgsObj()); + // Initialize arguments object. + bool needsArgsObj = info().needsArgsObj(); + MInstruction *argsObj = NULL; if (info().hasArguments()) { - MInstruction *argsObj = MConstant::New(UndefinedValue()); + if (needsArgsObj) + argsObj = MOsrArgumentsObject::New(entry); + else + argsObj = MConstant::New(UndefinedValue()); osrBlock->add(argsObj); osrBlock->initSlot(info().argsObjSlot(), argsObj); } if (info().fun()) { // Initialize |this| parameter. uint32_t slot = info().thisSlot(); ptrdiff_t offset = StackFrame::offsetOfThis(info().fun()); MOsrValue *thisv = MOsrValue::New(entry, offset); osrBlock->add(thisv); osrBlock->initSlot(slot, thisv); // Initialize arguments. for (uint32_t i = 0; i < info().nargs(); i++) { - // NB: Ion does not OSR into any function which |needsArgsObj|, so - // using argSlot() here instead of argSlotUnchecked() is ok. - uint32_t slot = info().argSlot(i); - ptrdiff_t offset = StackFrame::offsetOfFormalArg(info().fun(), i); - - MOsrValue *osrv = MOsrValue::New(entry, offset); - osrBlock->add(osrv); - osrBlock->initSlot(slot, osrv); + uint32_t slot = needsArgsObj ? info().argSlotUnchecked(i) : info().argSlot(i); + + if (needsArgsObj) { + JS_ASSERT(argsObj && argsObj->isOsrArgumentsObject()); + // If this is an aliased formal, then the arguments object + // contains a hole at this index. Any references to this + // variable in the jitcode will come from JSOP_*ALIASEDVAR + // opcodes, so the slot itself can be set to undefined. If + // it's not aliased, it must be retrieved from the arguments + // object. + MInstruction *osrv; + if (script()->formalIsAliased(i)) + osrv = MConstant::New(UndefinedValue()); + else + osrv = MGetArgumentsObjectArg::New(argsObj, i); + + osrBlock->add(osrv); + osrBlock->initSlot(slot, osrv); + } else { + ptrdiff_t offset = StackFrame::offsetOfFormalArg(info().fun(), i); + MOsrValue *osrv = MOsrValue::New(entry, offset); + osrBlock->add(osrv); + osrBlock->initSlot(slot, osrv); + } } } // Initialize locals. for (uint32_t i = 0; i < info().nlocals(); i++) { uint32_t slot = info().localSlot(i); ptrdiff_t offset = StackFrame::offsetOfFixed(i); @@ -5829,17 +5850,17 @@ IonBuilder::newOsrPreheader(MBasicBlock // Treat the OSR values as having the same type as the existing values // coming in to the loop. These will be fixed up with appropriate // unboxing and type barriers in finishLoop, once the possible types // at the loop header are known. for (uint32_t i = info().startArgSlot(); i < osrBlock->stackDepth(); i++) { MDefinition *existing = current->getSlot(i); MDefinition *def = osrBlock->getSlot(i); - JS_ASSERT(def->type() == MIRType_Value); + JS_ASSERT_IF(!needsArgsObj || !info().isSlotAliased(i), def->type() == MIRType_Value); // Aliased slots are never accessed, since they need to go through // the callobject. No need to type them here. if (info().isSlotAliased(i)) continue; def->setResultType(existing->type()); def->setResultTypeSet(existing->resultTypeSet()); @@ -5871,38 +5892,42 @@ IonBuilder::newPendingLoopHeader(MBasicB // header. The OSR frame may have unexpected types due to type changes // within the loop body or due to incomplete profiling information, // in which case this may avoid restarts of loop analysis or bailouts // during the OSR itself. // Unbox the MOsrValue if it is known to be unboxable. for (uint32_t i = info().startArgSlot(); i < block->stackDepth(); i++) { - // The value of aliased slots are in the callobject. So we can't + // The value of aliased args and slots are in the callobject. So we can't // the value from the baseline frame. if (info().isSlotAliased(i)) continue; // Don't bother with expression stack values. The stack should be // empty except for let variables (not Ion-compiled) or iterators. if (i >= info().firstStackSlot()) continue; MPhi *phi = block->getSlot(i)->toPhi(); // Get the value from the baseline frame. Value existingValue; uint32_t arg = i - info().firstArgSlot(); uint32_t var = i - info().firstLocalSlot(); - if (info().fun() && i == info().thisSlot()) + if (info().fun() && i == info().thisSlot()) { existingValue = baselineFrame_->thisValue(); - else if (arg < info().nargs()) - existingValue = baselineFrame_->unaliasedFormal(arg); - else + } else if (arg < info().nargs()) { + if (info().needsArgsObj()) + existingValue = baselineFrame_->argsObj().arg(arg); + else + existingValue = baselineFrame_->unaliasedFormal(arg); + } else { existingValue = baselineFrame_->unaliasedVar(var); + } // Extract typeset from value. MIRType type = existingValue.isDouble() ? MIRType_Double : MIRTypeFromValueType(existingValue.extractNonDoubleType()); types::Type ntype = types::GetValueType(existingValue); types::TemporaryTypeSet *typeSet = GetIonContext()->temp->lifoAlloc()->new_<types::TemporaryTypeSet>(ntype);
--- a/js/src/jit/LIR-Common.h +++ b/js/src/jit/LIR-Common.h @@ -2796,16 +2796,32 @@ class LOsrScopeChain : public LInstructi setOperand(0, entry); } const MOsrScopeChain *mir() { return mir_->toOsrScopeChain(); } }; +// Materialize a JSObject ArgumentsObject stored in an interpreter frame for OSR. +class LOsrArgumentsObject : public LInstructionHelper<1, 1, 0> +{ + public: + LIR_HEADER(OsrArgumentsObject) + + LOsrArgumentsObject(const LAllocation &entry) + { + setOperand(0, entry); + } + + const MOsrArgumentsObject *mir() { + return mir_->toOsrArgumentsObject(); + } +}; + class LRegExp : public LCallInstructionHelper<1, 0, 0> { public: LIR_HEADER(RegExp) const MRegExp *mir() const { return mir_->toRegExp(); }
--- a/js/src/jit/LOpcodes.h +++ b/js/src/jit/LOpcodes.h @@ -129,16 +129,17 @@ _(DoubleToInt32) \ _(TruncateDToInt32) \ _(IntToString) \ _(DoubleToString) \ _(Start) \ _(OsrEntry) \ _(OsrValue) \ _(OsrScopeChain) \ + _(OsrArgumentsObject) \ _(RegExp) \ _(RegExpTest) \ _(Lambda) \ _(LambdaForSingleton) \ _(LambdaPar) \ _(ImplicitThis) \ _(Slots) \ _(Elements) \
--- a/js/src/jit/Lowering.cpp +++ b/js/src/jit/Lowering.cpp @@ -1542,16 +1542,23 @@ LIRGenerator::visitOsrValue(MOsrValue *v bool LIRGenerator::visitOsrScopeChain(MOsrScopeChain *object) { LOsrScopeChain *lir = new LOsrScopeChain(useRegister(object->entry())); return define(lir, object); } bool +LIRGenerator::visitOsrArgumentsObject(MOsrArgumentsObject *object) +{ + LOsrArgumentsObject *lir = new LOsrArgumentsObject(useRegister(object->entry())); + return define(lir, object); +} + +bool LIRGenerator::visitToDouble(MToDouble *convert) { MDefinition *opd = convert->input(); mozilla::DebugOnly<MToDouble::ConversionKind> conversion = convert->conversion(); switch (opd->type()) { case MIRType_Value: {
--- a/js/src/jit/Lowering.h +++ b/js/src/jit/Lowering.h @@ -150,16 +150,17 @@ class LIRGenerator : public LIRGenerator bool visitConcatPar(MConcatPar *ins); bool visitCharCodeAt(MCharCodeAt *ins); bool visitFromCharCode(MFromCharCode *ins); bool visitStart(MStart *start); bool visitOsrEntry(MOsrEntry *entry); bool visitNop(MNop *nop); bool visitOsrValue(MOsrValue *value); bool visitOsrScopeChain(MOsrScopeChain *object); + bool visitOsrArgumentsObject(MOsrArgumentsObject *object); bool visitToDouble(MToDouble *convert); bool visitToFloat32(MToFloat32 *convert); bool visitToInt32(MToInt32 *convert); bool visitTruncateToInt32(MTruncateToInt32 *truncate); bool visitToString(MToString *convert); bool visitRegExp(MRegExp *ins); bool visitRegExpTest(MRegExpTest *ins); bool visitLambda(MLambda *ins);
--- a/js/src/jit/MIR.h +++ b/js/src/jit/MIR.h @@ -4281,16 +4281,38 @@ class MOsrScopeChain : public MUnaryInst return new MOsrScopeChain(entry); } MOsrEntry *entry() { return getOperand(0)->toOsrEntry(); } }; +// MIR representation of a JSObject ArgumentsObject pointer on the OSR StackFrame. +// The pointer is indexed off of OsrFrameReg. +class MOsrArgumentsObject : public MUnaryInstruction +{ + private: + MOsrArgumentsObject(MOsrEntry *entry) + : MUnaryInstruction(entry) + { + setResultType(MIRType_Object); + } + + public: + INSTRUCTION_HEADER(OsrArgumentsObject) + static MOsrArgumentsObject *New(MOsrEntry *entry) { + return new MOsrArgumentsObject(entry); + } + + MOsrEntry *entry() { + return getOperand(0)->toOsrEntry(); + } +}; + // Check the current frame for over-recursion past the global stack limit. class MCheckOverRecursed : public MNullaryInstruction { public: INSTRUCTION_HEADER(CheckOverRecursed) }; // Check the current frame for over-recursion past the global stack limit.
--- a/js/src/jit/MIRGraph.cpp +++ b/js/src/jit/MIRGraph.cpp @@ -451,19 +451,30 @@ MBasicBlock::linkOsrValues(MStart *start MResumePoint *res = start->resumePoint(); for (uint32_t i = 0; i < stackDepth(); i++) { MDefinition *def = slots_[i]; if (i == info().scopeChainSlot()) { if (def->isOsrScopeChain()) def->toOsrScopeChain()->setResumePoint(res); } else if (info().hasArguments() && i == info().argsObjSlot()) { - JS_ASSERT(def->isConstant() && def->toConstant()->value() == UndefinedValue()); + JS_ASSERT(def->isConstant() || def->isOsrArgumentsObject()); + JS_ASSERT_IF(def->isConstant(), def->toConstant()->value() == UndefinedValue()); + if (def->isOsrArgumentsObject()) + def->toOsrArgumentsObject()->setResumePoint(res); } else { - def->toOsrValue()->setResumePoint(res); + JS_ASSERT(def->isOsrValue() || def->isGetArgumentsObjectArg() || def->isConstant()); + // A constant Undefined can show up here for an argument slot when the function uses + // a heavyweight argsobj, but the argument in question is stored on the scope chain. + JS_ASSERT_IF(def->isConstant(), def->toConstant()->value() == UndefinedValue()); + + if (def->isOsrValue()) + def->toOsrValue()->setResumePoint(res); + else if (def->isGetArgumentsObjectArg()) + def->toGetArgumentsObjectArg()->setResumePoint(res); } } } void MBasicBlock::setSlot(uint32_t slot, MDefinition *ins) { slots_[slot] = ins;
--- a/js/src/jit/MOpcodes.h +++ b/js/src/jit/MOpcodes.h @@ -19,16 +19,17 @@ namespace jit { _(Test) \ _(TypeObjectDispatch) \ _(FunctionDispatch) \ _(Compare) \ _(Phi) \ _(Beta) \ _(OsrValue) \ _(OsrScopeChain) \ + _(OsrArgumentsObject) \ _(ReturnFromCtor) \ _(CheckOverRecursed) \ _(DefVar) \ _(DefFun) \ _(CreateThis) \ _(CreateThisWithProto) \ _(CreateThisWithTemplate) \ _(CreateArgumentsObject) \
--- a/js/src/jit/ParallelSafetyAnalysis.cpp +++ b/js/src/jit/ParallelSafetyAnalysis.cpp @@ -111,16 +111,17 @@ class ParallelSafetyVisitor : public MIn SAFE_OP(TableSwitch) SAFE_OP(Goto) SAFE_OP(Test) SAFE_OP(Compare) SAFE_OP(Phi) SAFE_OP(Beta) UNSAFE_OP(OsrValue) UNSAFE_OP(OsrScopeChain) + UNSAFE_OP(OsrArgumentsObject) UNSAFE_OP(ReturnFromCtor) CUSTOM_OP(CheckOverRecursed) UNSAFE_OP(DefVar) UNSAFE_OP(DefFun) UNSAFE_OP(CreateThis) UNSAFE_OP(CreateThisWithTemplate) UNSAFE_OP(CreateThisWithProto) UNSAFE_OP(CreateArgumentsObject)
--- a/js/src/vm/Stack.h +++ b/js/src/vm/Stack.h @@ -939,16 +939,20 @@ class StackFrame static size_t offsetOfNumActual() { return offsetof(StackFrame, u.nactual); } static size_t offsetOfScopeChain() { return offsetof(StackFrame, scopeChain_); } + static size_t offsetOfArgumentsObject() { + return offsetof(StackFrame, argsObj_); + } + static ptrdiff_t offsetOfThis(JSFunction *fun) { return fun == NULL ? -1 * ptrdiff_t(sizeof(Value)) : -(fun->nargs + 1) * ptrdiff_t(sizeof(Value)); } static ptrdiff_t offsetOfFormalArg(JSFunction *fun, unsigned i) { JS_ASSERT(i < fun->nargs);