diff --git a/js/src/ion/CodeGenerator.cpp b/js/src/ion/CodeGenerator.cpp index a537698efb62..9d622ad9b814 100644 --- a/js/src/ion/CodeGenerator.cpp +++ b/js/src/ion/CodeGenerator.cpp @@ -2160,13 +2160,31 @@ CodeGenerator::visitArgumentsLength(LArgumentsLength *lir) { // read number of actual arguments from the JS frame. Register argc = ToRegister(lir->output()); - Address ptr(StackPointer, masm.framePushed() + IonJSFrameLayout::offsetOfNumActualArgs()); + Address ptr(StackPointer, frameSize() + IonJSFrameLayout::offsetOfNumActualArgs()); - JS_ASSERT(masm.framePushed() == frameSize()); masm.movePtr(ptr, argc); return true; } +bool +CodeGenerator::visitGetArgument(LGetArgument *lir) +{ + ValueOperand result = GetValueOutput(lir); + const LAllocation *index = lir->index(); + size_t argvOffset = frameSize() + IonJSFrameLayout::offsetOfActualArgs(); + + if (index->isConstant()) { + int32 i = index->toConstant()->toInt32(); + Address argPtr(StackPointer, sizeof(Value*) * i + argvOffset); + masm.loadValue(argPtr, result); + } else { + Register i = ToRegister(index); + BaseIndex argPtr(StackPointer, i, ScaleFromShift(sizeof(Value*)), argvOffset); + masm.loadValue(argPtr, result); + } + return true; +} + bool CodeGenerator::generate() { diff --git a/js/src/ion/CodeGenerator.h b/js/src/ion/CodeGenerator.h index 7360c7b53736..fb13a6bd1e62 100644 --- a/js/src/ion/CodeGenerator.h +++ b/js/src/ion/CodeGenerator.h @@ -176,6 +176,7 @@ class CodeGenerator : public CodeGeneratorSpecific bool visitIteratorMore(LIteratorMore *lir); bool visitIteratorEnd(LIteratorEnd *lir); bool visitArgumentsLength(LArgumentsLength *lir); + bool visitGetArgument(LGetArgument *lir); bool visitCallSetProperty(LCallSetProperty *ins); bool visitCallDeleteProperty(LCallDeleteProperty *lir); bool visitBitNotV(LBitNotV *lir); diff --git a/js/src/ion/CompileInfo.h b/js/src/ion/CompileInfo.h index 0072a91a224b..f050ce0b9038 100644 --- a/js/src/ion/CompileInfo.h +++ b/js/src/ion/CompileInfo.h @@ -160,6 +160,10 @@ class CompileInfo return firstStackSlot() + i; } + bool hasArguments() { + return script()->argumentsHasLocalBinding(); + } + private: JSScript *script_; JSFunction *fun_; diff --git a/js/src/ion/IonBuilder.cpp b/js/src/ion/IonBuilder.cpp index d56738fe4b3b..bdd596ac9573 100644 --- a/js/src/ion/IonBuilder.cpp +++ b/js/src/ion/IonBuilder.cpp @@ -823,6 +823,13 @@ IonBuilder::inspectOpcode(JSOp op) return true; case JSOP_SETARG: + // To handle this case, we should spill the arguments to the space where + // actual arguments are stored. The tricky part is that if we add a MIR + // to wrap the spilling action, we don't want the spilling to be + // captured by the GETARG and by the resume point, only by + // MGetArgument. + if (info().hasArguments()) + return abort("NYI: arguments & setarg."); current->setArg(GET_SLOTNO(pc)); return true; @@ -2707,6 +2714,10 @@ IonBuilder::jsop_notearg() MDefinition *def = current->pop(); MPassArg *arg = MPassArg::New(def); + // We do not support giving the argument object as argument yet. + if (def->type() == MIRType_ArgObj) + return abort("NYI: escaping of the argument object."); + current->add(arg); current->push(arg); return true; @@ -3048,14 +3059,8 @@ IonBuilder::jsop_funcall(uint32 argc) // If |Function.prototype.call| may be overridden, don't optimize callsite. RootedFunction native(cx, getSingleCallTarget(argc, pc)); - if (native && native->isNative()) { - if (native->native() == &js_fun_apply) - return abort("NYI: fun.apply with arguments."); - if (native->native() != &js_fun_call) - return makeCall(native, argc, false); - } else { + if (!native || !native->isNative() || native->native() != &js_fun_call) return makeCall(native, argc, false); - } // Extract call target. types::TypeSet *funTypes = oracle->getCallArg(script, argc, 0, pc); @@ -4475,9 +4480,30 @@ IonBuilder::jsop_arguments_length() bool IonBuilder::jsop_arguments_getelem() { + types::TypeSet *barrier = oracle->propertyReadBarrier(script, pc); + types::TypeSet *types = oracle->propertyRead(script, pc); + MDefinition *idx = current->pop(); - MDefinition *obj = current->pop(); - return abort("NYI arguments[]"); + MDefinition *args = current->pop(); + + // To ensure that we are not looking above the number of actual arguments. + MArgumentsLength *length = MArgumentsLength::New(args); + current->add(length); + + // Ensure idx is an integer. + MToInt32 *index = MToInt32::New(idx); + current->add(index); + + // Bailouts if we read more than the number of actual arguments. + MBoundsCheck *check = MBoundsCheck::New(index, length); + current->add(check); + + // Load the argument from the actual arguments. + MGetArgument *load = MGetArgument::New(index); + current->add(load); + current->push(load); + + return pushTypeBarrier(load, types, barrier); } bool diff --git a/js/src/ion/LIR-Common.h b/js/src/ion/LIR-Common.h index 23a923a206bb..0873f4ebdfa8 100644 --- a/js/src/ion/LIR-Common.h +++ b/js/src/ion/LIR-Common.h @@ -2632,6 +2632,21 @@ class LArgumentsLength : public LInstructionHelper<1, 0, 0> } }; +// Load a value from the actual arguments. +class LGetArgument : public LInstructionHelper +{ + public: + LIR_HEADER(GetArgument); + BOX_OUTPUT_ACCESSORS(); + + LGetArgument(const LAllocation &index) { + setOperand(0, index); + } + const LAllocation *index() { + return getOperand(0); + } +}; + // Guard that a value is in a TypeSet. class LTypeBarrier : public LInstructionHelper { diff --git a/js/src/ion/LOpcodes.h b/js/src/ion/LOpcodes.h index a39bdd697a1d..a9e569e96e24 100644 --- a/js/src/ion/LOpcodes.h +++ b/js/src/ion/LOpcodes.h @@ -173,6 +173,7 @@ _(TypedArrayElements) \ _(StringLength) \ _(ArgumentsLength) \ + _(GetArgument) \ _(TypeOfV) \ _(ToIdV) \ _(Floor) \ diff --git a/js/src/ion/Lowering.cpp b/js/src/ion/Lowering.cpp index d1965e68ca22..a2e93bdb5856 100644 --- a/js/src/ion/Lowering.cpp +++ b/js/src/ion/Lowering.cpp @@ -847,10 +847,18 @@ LIRGenerator::visitToInt32(MToInt32 *convert) return assignSnapshot(lir) && define(lir, convert); } + case MIRType_String: + // Strings are complicated - we don't handle them yet. + IonSpew(IonSpew_Abort, "String to Int32 not supported yet."); + break; + + case MIRType_Object: + // Objects might be effectful. + IonSpew(IonSpew_Abort, "Object to Int32 not supported yet."); + break; + default: // Undefined coerces to NaN, not int32. - // Objects might be effectful. - // Strings are complicated - we don't handle them yet. JS_NOT_REACHED("unexpected type"); } @@ -1578,6 +1586,13 @@ LIRGenerator::visitArgumentsLength(MArgumentsLength *ins) return define(new LArgumentsLength(), ins); } +bool +LIRGenerator::visitGetArgument(MGetArgument *ins) +{ + LGetArgument *lir = new LGetArgument(useRegisterOrConstant(ins->index())); + return defineBox(lir, ins); +} + bool LIRGenerator::visitThrow(MThrow *ins) { diff --git a/js/src/ion/Lowering.h b/js/src/ion/Lowering.h index 942d8e7c4823..fb68e4c8a605 100644 --- a/js/src/ion/Lowering.h +++ b/js/src/ion/Lowering.h @@ -200,6 +200,7 @@ class LIRGenerator : public LIRGeneratorSpecific bool visitIteratorEnd(MIteratorEnd *ins); bool visitStringLength(MStringLength *ins); bool visitArgumentsLength(MArgumentsLength *ins); + bool visitGetArgument(MGetArgument *ins); bool visitThrow(MThrow *ins); bool visitInstanceOf(MInstanceOf *ins); }; diff --git a/js/src/ion/MIR.cpp b/js/src/ion/MIR.cpp index f8e3f10787d1..fbac04a997e9 100644 --- a/js/src/ion/MIR.cpp +++ b/js/src/ion/MIR.cpp @@ -343,6 +343,9 @@ MConstant::printOpcode(FILE *fp) case MIRType_String: fprintf(fp, "string %p", (void *)value().toString()); break; + case MIRType_Magic: + fprintf(fp, "magic"); + break; case MIRType_ArgObj: fprintf(fp, "lazy arguments"); break; diff --git a/js/src/ion/MIR.h b/js/src/ion/MIR.h index 85e580415e95..0343273ab403 100644 --- a/js/src/ion/MIR.h +++ b/js/src/ion/MIR.h @@ -4404,19 +4404,52 @@ class MArgumentsLength return new MArgumentsLength(arguments); } - TypePolicy *typePolicy() { - return this; - } - MDefinition *arguments() const { return getOperand(0); } + + TypePolicy *typePolicy() { + return this; + } + bool congruentTo(MDefinition *const &ins) const { + return congruentIfOperandsEqual(ins); + } + AliasSet getAliasSet() const { + // Arguments |length| cannot be mutated by Ion Code. + return AliasSet::None(); + } +}; + +// This MIR instruction is used to get an argument from the actual arguments. +class MGetArgument + : public MUnaryInstruction, + public IntPolicy<0> +{ + MGetArgument(MDefinition *idx) + : MUnaryInstruction(idx) + { + setResultType(MIRType_Value); + setMovable(); + } + + public: + INSTRUCTION_HEADER(GetArgument); + + static MGetArgument *New(MDefinition *idx) { + return new MGetArgument(idx); + } + + MDefinition *index() const { + return getOperand(0); + } + + TypePolicy *typePolicy() { + return this; + } bool congruentTo(MDefinition *const &ins) const { return congruentIfOperandsEqual(ins); } AliasSet getAliasSet() const { - // Arguments |length| cannot be mutated by Ion Code or any the the - // called functions. return AliasSet::None(); } }; diff --git a/js/src/ion/MOpcodes.h b/js/src/ion/MOpcodes.h index 11ad1024c22a..da965fd8da3b 100644 --- a/js/src/ion/MOpcodes.h +++ b/js/src/ion/MOpcodes.h @@ -147,6 +147,7 @@ namespace ion { _(IteratorEnd) \ _(StringLength) \ _(ArgumentsLength) \ + _(GetArgument) \ _(Floor) \ _(Round) \ _(InstanceOf) \ diff --git a/js/src/ion/shared/IonFrames-x86-shared.h b/js/src/ion/shared/IonFrames-x86-shared.h index 00e55b146d49..350769c7817e 100644 --- a/js/src/ion/shared/IonFrames-x86-shared.h +++ b/js/src/ion/shared/IonFrames-x86-shared.h @@ -96,6 +96,11 @@ class IonJSFrameLayout : public IonCommonFrameLayout static size_t offsetOfNumActualArgs() { return offsetof(IonJSFrameLayout, numActualArgs_); } + static size_t offsetOfActualArgs() { + IonJSFrameLayout *base = NULL; + // +1 to skip |this|. + return reinterpret_cast(&base->argv()[1]); + } Value *argv() { return (Value *)(this + 1);