diff --git a/js/src/ion/CodeGenerator.cpp b/js/src/ion/CodeGenerator.cpp index cc1de196c85..f9d05c77f03 100644 --- a/js/src/ion/CodeGenerator.cpp +++ b/js/src/ion/CodeGenerator.cpp @@ -2741,6 +2741,10 @@ class OutOfLineNewObject : public OutOfLineCodeBase typedef JSObject *(*NewInitObjectFn)(JSContext *, HandleObject); static const VMFunction NewInitObjectInfo = FunctionInfo(NewInitObject); +typedef JSObject *(*NewInitObjectWithClassPrototypeFn)(JSContext *, HandleObject); +static const VMFunction NewInitObjectWithClassPrototypeInfo = + FunctionInfo(NewInitObjectWithClassPrototype); + bool CodeGenerator::visitNewObjectVMCall(LNewObject *lir) { @@ -2752,8 +2756,17 @@ CodeGenerator::visitNewObjectVMCall(LNewObject *lir) saveLive(lir); pushArg(ImmGCPtr(lir->mir()->templateObject())); - if (!callVM(NewInitObjectInfo, lir)) + + // If we're making a new object with a class prototype (that is, an object + // that derives its class from its prototype instead of being + // ObjectClass'd) from self-hosted code, we need a different init + // function. + if (lir->mir()->templateObjectIsClassPrototype()) { + if (!callVM(NewInitObjectWithClassPrototypeInfo, lir)) + return false; + } else if (!callVM(NewInitObjectInfo, lir)) { return false; + } if (ReturnReg != objReg) masm.movePtr(ReturnReg, objReg); @@ -6828,6 +6841,22 @@ CodeGenerator::visitOutOfLinePropagateParallelAbort(OutOfLinePropagateParallelAb return true; } +bool +CodeGenerator::visitHaveSameClass(LHaveSameClass *ins) +{ + Register lhs = ToRegister(ins->lhs()); + Register rhs = ToRegister(ins->rhs()); + Register temp = ToRegister(ins->getTemp(0)); + Register output = ToRegister(ins->output()); + + masm.loadObjClass(lhs, temp); + masm.loadObjClass(rhs, output); + masm.cmpPtr(temp, output); + masm.emitSet(Assembler::Equal, output); + + return true; +} + bool CodeGenerator::visitAsmJSCall(LAsmJSCall *ins) { diff --git a/js/src/ion/CodeGenerator.h b/js/src/ion/CodeGenerator.h index 42723813614..6df3c9d724c 100644 --- a/js/src/ion/CodeGenerator.h +++ b/js/src/ion/CodeGenerator.h @@ -238,6 +238,7 @@ class CodeGenerator : public CodeGeneratorSpecific bool visitCallDOMNative(LCallDOMNative *lir); bool visitCallGetIntrinsicValue(LCallGetIntrinsicValue *lir); bool visitIsCallable(LIsCallable *lir); + bool visitHaveSameClass(LHaveSameClass *lir); bool visitAsmJSCall(LAsmJSCall *lir); bool visitAsmJSParameter(LAsmJSParameter *lir); bool visitAsmJSReturn(LAsmJSReturn *ret); diff --git a/js/src/ion/IonBuilder.cpp b/js/src/ion/IonBuilder.cpp index a0c411ab38a..376449c2061 100644 --- a/js/src/ion/IonBuilder.cpp +++ b/js/src/ion/IonBuilder.cpp @@ -5228,7 +5228,8 @@ IonBuilder::jsop_newobject(HandleObject baseObj) templateObject->setType(type); } - MNewObject *ins = MNewObject::New(templateObject); + MNewObject *ins = MNewObject::New(templateObject, + /* templateObjectIsClassPrototype = */ false); current->add(ins); current->push(ins); diff --git a/js/src/ion/IonBuilder.h b/js/src/ion/IonBuilder.h index 96d04b5f7da..472da619bd4 100644 --- a/js/src/ion/IonBuilder.h +++ b/js/src/ion/IonBuilder.h @@ -509,6 +509,8 @@ class IonBuilder : public MIRGenerator // Utility intrinsics. InliningStatus inlineThrowError(CallInfo &callInfo); InliningStatus inlineIsCallable(CallInfo &callInfo); + InliningStatus inlineNewObjectWithClassPrototype(CallInfo &callInfo); + InliningStatus inlineHaveSameClass(CallInfo &callInfo); InliningStatus inlineToObject(CallInfo &callInfo); InliningStatus inlineDump(CallInfo &callInfo); diff --git a/js/src/ion/LIR-Common.h b/js/src/ion/LIR-Common.h index 3bbfe0039ba..c69957a2217 100644 --- a/js/src/ion/LIR-Common.h +++ b/js/src/ion/LIR-Common.h @@ -4567,6 +4567,28 @@ class LIsCallable : public LInstructionHelper<1, 1, 0> } }; +class LHaveSameClass : public LInstructionHelper<1, 2, 1> +{ + public: + LIR_HEADER(HaveSameClass); + LHaveSameClass(const LAllocation &left, const LAllocation &right, + const LDefinition &temp) { + setOperand(0, left); + setOperand(1, right); + setTemp(0, temp); + } + + const LAllocation *lhs() { + return getOperand(0); + } + const LAllocation *rhs() { + return getOperand(1); + } + MHaveSameClass *mir() const { + return mir_->toHaveSameClass(); + } +}; + class LAsmJSLoadHeap : public LInstructionHelper<1, 1, 0> { public: diff --git a/js/src/ion/LOpcodes.h b/js/src/ion/LOpcodes.h index cf75fdc575f..385b64953b8 100644 --- a/js/src/ion/LOpcodes.h +++ b/js/src/ion/LOpcodes.h @@ -226,6 +226,7 @@ _(SetDOMProperty) \ _(CallDOMNative) \ _(IsCallable) \ + _(HaveSameClass) \ _(AsmJSLoadHeap) \ _(AsmJSStoreHeap) \ _(AsmJSLoadGlobalVar) \ diff --git a/js/src/ion/Lowering.cpp b/js/src/ion/Lowering.cpp index cdfb35c03bc..4d471dcc7f0 100644 --- a/js/src/ion/Lowering.cpp +++ b/js/src/ion/Lowering.cpp @@ -2636,6 +2636,18 @@ LIRGenerator::visitIsCallable(MIsCallable *ins) return define(new LIsCallable(useRegister(ins->object())), ins); } +bool +LIRGenerator::visitHaveSameClass(MHaveSameClass *ins) +{ + MDefinition *lhs = ins->lhs(); + MDefinition *rhs = ins->rhs(); + + JS_ASSERT(lhs->type() == MIRType_Object); + JS_ASSERT(rhs->type() == MIRType_Object); + + return define(new LHaveSameClass(useRegister(lhs), useRegister(rhs), temp()), ins); +} + bool LIRGenerator::visitAsmJSLoadHeap(MAsmJSLoadHeap *ins) { diff --git a/js/src/ion/Lowering.h b/js/src/ion/Lowering.h index 2547e987521..bd8b6ae3908 100644 --- a/js/src/ion/Lowering.h +++ b/js/src/ion/Lowering.h @@ -231,6 +231,7 @@ class LIRGenerator : public LIRGeneratorSpecific bool visitCallInstanceOf(MCallInstanceOf *ins); bool visitFunctionBoundary(MFunctionBoundary *ins); bool visitIsCallable(MIsCallable *ins); + bool visitHaveSameClass(MHaveSameClass *ins); bool visitAsmJSLoadHeap(MAsmJSLoadHeap *ins); bool visitAsmJSLoadGlobalVar(MAsmJSLoadGlobalVar *ins); bool visitAsmJSStoreGlobalVar(MAsmJSStoreGlobalVar *ins); diff --git a/js/src/ion/MCallOptimize.cpp b/js/src/ion/MCallOptimize.cpp index 25d6c66e607..217cb678a24 100644 --- a/js/src/ion/MCallOptimize.cpp +++ b/js/src/ion/MCallOptimize.cpp @@ -114,6 +114,10 @@ IonBuilder::inlineNativeCall(CallInfo &callInfo, JSNative native) return inlineThrowError(callInfo); if (native == intrinsic_IsCallable) return inlineIsCallable(callInfo); + if (native == intrinsic_NewObjectWithClassPrototype) + return inlineNewObjectWithClassPrototype(callInfo); + if (native == intrinsic_HaveSameClass) + return inlineHaveSameClass(callInfo); if (native == intrinsic_ToObject) return inlineToObject(callInfo); #ifdef DEBUG @@ -1351,6 +1355,64 @@ IonBuilder::inlineUnsafeGetReservedSlot(CallInfo &callInfo) return InliningStatus_Inlined; } +IonBuilder::InliningStatus +IonBuilder::inlineNewObjectWithClassPrototype(CallInfo &callInfo) +{ + if (callInfo.argc() != 1 || callInfo.constructing()) + return InliningStatus_NotInlined; + if (callInfo.getArg(0)->type() != MIRType_Object) + return InliningStatus_NotInlined; + + MDefinition *arg = callInfo.getArg(0)->toPassArg()->getArgument(); + if (!arg->isConstant()) + return InliningStatus_NotInlined; + JSObject *proto = &arg->toConstant()->value().toObject(); + + JSObject *templateObject = NewObjectWithGivenProto(cx, proto->getClass(), proto, cx->global()); + if (!templateObject) + return InliningStatus_Error; + + MNewObject *newObj = MNewObject::New(templateObject, + /* templateObjectIsClassPrototype = */ true); + current->add(newObj); + current->push(newObj); + + if (!resumeAfter(newObj)) + return InliningStatus_Error; + + return InliningStatus_Inlined; +} + +IonBuilder::InliningStatus +IonBuilder::inlineHaveSameClass(CallInfo &callInfo) +{ + if (callInfo.argc() != 2 || callInfo.constructing()) + return InliningStatus_NotInlined; + if (callInfo.getArg(0)->type() != MIRType_Object) + return InliningStatus_NotInlined; + if (callInfo.getArg(1)->type() != MIRType_Object) + return InliningStatus_NotInlined; + + types::StackTypeSet *arg1Types = callInfo.getArg(0)->resultTypeSet(); + types::StackTypeSet *arg2Types = callInfo.getArg(1)->resultTypeSet(); + Class *arg1Clasp = arg1Types ? arg1Types->getKnownClass() : NULL; + Class *arg2Clasp = arg2Types ? arg1Types->getKnownClass() : NULL; + if (arg1Clasp && arg2Clasp) { + MConstant *constant = MConstant::New(BooleanValue(arg1Clasp == arg2Clasp)); + current->add(constant); + current->push(constant); + return InliningStatus_Inlined; + } + + callInfo.unwrapArgs(); + + MHaveSameClass *sameClass = MHaveSameClass::New(callInfo.getArg(0), callInfo.getArg(1)); + current->add(sameClass); + current->push(sameClass); + + return InliningStatus_Inlined; +} + IonBuilder::InliningStatus IonBuilder::inlineThrowError(CallInfo &callInfo) { diff --git a/js/src/ion/MIR.h b/js/src/ion/MIR.h index 8b5f52853f4..fce5dcdef05 100644 --- a/js/src/ion/MIR.h +++ b/js/src/ion/MIR.h @@ -1184,10 +1184,13 @@ class MNewArray : public MNullaryInstruction class MNewObject : public MNullaryInstruction { CompilerRootObject templateObject_; + bool templateObjectIsClassPrototype_; - MNewObject(JSObject *templateObject) - : templateObject_(templateObject) + MNewObject(JSObject *templateObject, bool templateObjectIsClassPrototype) + : templateObject_(templateObject), + templateObjectIsClassPrototype_(templateObjectIsClassPrototype) { + JS_ASSERT_IF(templateObjectIsClassPrototype, !shouldUseVM()); setResultType(MIRType_Object); setResultTypeSet(MakeSingletonTypeSet(templateObject)); } @@ -1195,14 +1198,18 @@ class MNewObject : public MNullaryInstruction public: INSTRUCTION_HEADER(NewObject) - static MNewObject *New(JSObject *templateObject) { - return new MNewObject(templateObject); + static MNewObject *New(JSObject *templateObject, bool templateObjectIsClassPrototype) { + return new MNewObject(templateObject, templateObjectIsClassPrototype); } // Returns true if the code generator should call through to the // VM rather than the fast path. bool shouldUseVM() const; + bool templateObjectIsClassPrototype() const { + return templateObjectIsClassPrototype_; + } + JSObject *templateObject() const { return templateObject_; } @@ -7847,6 +7854,35 @@ class MIsCallable MDefinition *object() const { return getOperand(0); } + AliasSet getAliasSet() const { + return AliasSet::None(); + } +}; + +class MHaveSameClass + : public MBinaryInstruction, + public MixPolicy, ObjectPolicy<1> > +{ + MHaveSameClass(MDefinition *left, MDefinition *right) + : MBinaryInstruction(left, right) + { + setResultType(MIRType_Boolean); + setMovable(); + } + + public: + INSTRUCTION_HEADER(HaveSameClass); + + static MHaveSameClass *New(MDefinition *left, MDefinition *right) { + return new MHaveSameClass(left, right); + } + + TypePolicy *typePolicy() { + return this; + } + AliasSet getAliasSet() const { + return AliasSet::None(); + } }; class MAsmJSNeg : public MUnaryInstruction diff --git a/js/src/ion/MOpcodes.h b/js/src/ion/MOpcodes.h index d6917fc092c..1b305d688e5 100644 --- a/js/src/ion/MOpcodes.h +++ b/js/src/ion/MOpcodes.h @@ -168,6 +168,7 @@ namespace ion { _(GetDOMProperty) \ _(SetDOMProperty) \ _(IsCallable) \ + _(HaveSameClass) \ _(AsmJSNeg) \ _(AsmJSUDiv) \ _(AsmJSUMod) \ diff --git a/js/src/ion/ParallelArrayAnalysis.cpp b/js/src/ion/ParallelArrayAnalysis.cpp index 84b40624a1f..f8ce772a1a3 100644 --- a/js/src/ion/ParallelArrayAnalysis.cpp +++ b/js/src/ion/ParallelArrayAnalysis.cpp @@ -276,6 +276,7 @@ class ParallelArrayVisitor : public MInstructionVisitor SAFE_OP(FunctionDispatch) SAFE_OP(TypeObjectDispatch) SAFE_OP(IsCallable) + SAFE_OP(HaveSameClass) UNSAFE_OP(EffectiveAddress) UNSAFE_OP(AsmJSUnsignedToDouble) UNSAFE_OP(AsmJSNeg) diff --git a/js/src/ion/VMFunctions.cpp b/js/src/ion/VMFunctions.cpp index ec8583b8e8c..14925fde3ba 100644 --- a/js/src/ion/VMFunctions.cpp +++ b/js/src/ion/VMFunctions.cpp @@ -289,6 +289,23 @@ NewInitObject(JSContext *cx, HandleObject templateObject) return obj; } +JSObject * +NewInitObjectWithClassPrototype(JSContext *cx, HandleObject templateObject) +{ + JS_ASSERT(!templateObject->hasSingletonType()); + + JSObject *obj = NewObjectWithGivenProto(cx, + templateObject->getClass(), + templateObject->getProto(), + cx->global()); + if (!obj) + return NULL; + + obj->setType(templateObject->type()); + + return obj; +} + bool ArrayPopDense(JSContext *cx, HandleObject obj, MutableHandleValue rval) { diff --git a/js/src/ion/VMFunctions.h b/js/src/ion/VMFunctions.h index 8e4ca693010..c014dec3af9 100644 --- a/js/src/ion/VMFunctions.h +++ b/js/src/ion/VMFunctions.h @@ -557,6 +557,7 @@ bool IteratorMore(JSContext *cx, HandleObject obj, JSBool *res); JSObject *NewInitParallelArray(JSContext *cx, HandleObject templateObj); JSObject *NewInitArray(JSContext *cx, uint32_t count, types::TypeObject *type); JSObject *NewInitObject(JSContext *cx, HandleObject templateObject); +JSObject *NewInitObjectWithClassPrototype(JSContext *cx, HandleObject templateObject); bool ArrayPopDense(JSContext *cx, HandleObject obj, MutableHandleValue rval); bool ArrayPushDense(JSContext *cx, HandleObject obj, HandleValue v, uint32_t *length); diff --git a/js/src/jscntxt.h b/js/src/jscntxt.h index 415d0bf72e8..18ecf62df7c 100644 --- a/js/src/jscntxt.h +++ b/js/src/jscntxt.h @@ -2363,6 +2363,7 @@ JSBool intrinsic_NewDenseArray(JSContext *cx, unsigned argc, Value *vp); JSBool intrinsic_UnsafeSetElement(JSContext *cx, unsigned argc, Value *vp); JSBool intrinsic_UnsafeSetReservedSlot(JSContext *cx, unsigned argc, Value *vp); JSBool intrinsic_UnsafeGetReservedSlot(JSContext *cx, unsigned argc, Value *vp); +JSBool intrinsic_NewObjectWithClassPrototype(JSContext *cx, unsigned argc, Value *vp); JSBool intrinsic_HaveSameClass(JSContext *cx, unsigned argc, Value *vp); JSBool intrinsic_ShouldForceSequential(JSContext *cx, unsigned argc, Value *vp); diff --git a/js/src/vm/SelfHosting.cpp b/js/src/vm/SelfHosting.cpp index 944d1bfe788..3822ff837aa 100644 --- a/js/src/vm/SelfHosting.cpp +++ b/js/src/vm/SelfHosting.cpp @@ -505,8 +505,8 @@ intrinsic_NewClassPrototype(JSContext *cx, unsigned argc, Value *vp) return true; } -static JSBool -intrinsic_NewObjectWithClassPrototype(JSContext *cx, unsigned argc, Value *vp) +JSBool +js::intrinsic_NewObjectWithClassPrototype(JSContext *cx, unsigned argc, Value *vp) { CallArgs args = CallArgsFromVp(argc, vp); JS_ASSERT(args.length() == 1);