Skip to content

Instantly share code, notes, and snippets.

@syg
Last active December 14, 2015 17:19
Show Gist options
  • Save syg/5121853 to your computer and use it in GitHub Desktop.
Save syg/5121853 to your computer and use it in GitHub Desktop.
diff --git a/js/src/ion/IonCaches.cpp b/js/src/ion/IonCaches.cpp
index e62c2c0..24a77c1 100644
@@ -334,401 +369,429 @@ IsCacheableGetPropCallPropertyOp(JSObject *obj, JSObject *holder, UnrootedShape
return true;
}
-struct GetNativePropertyStub
+class StubRepatchHelper
{
+ MacroAssembler &masm_;
+ RepatchLabel failures_;
+
+ public:
+ StubRepatchHelper(MacroAssembler &masm)
+ : masm_(masm)
+ { }
+
+ // Output values.
CodeOffsetJump exitOffset;
CodeOffsetJump rejoinOffset;
CodeOffsetLabel stubCodePatchOffset;
- void generateReadSlot(JSContext *cx, MacroAssembler &masm, JSObject *obj, PropertyName *propName,
- JSObject *holder, HandleShape shape, Register object, TypedOrValueRegister output,
- RepatchLabel *failures, Label *nonRepatchFailures = NULL)
- {
- // If there's a single jump to |failures|, we can patch the shape guard
- // jump directly. Otherwise, jump to the end of the stub, so there's a
- // common point to patch.
- bool multipleFailureJumps = (nonRepatchFailures != NULL) && nonRepatchFailures->used();
- exitOffset = masm.branchPtrWithPatch(Assembler::NotEqual,
- Address(object, JSObject::offsetOfShape()),
- ImmGCPtr(obj->lastProperty()),
- failures);
-
- bool restoreScratch = false;
- Register scratchReg = Register::FromCode(0); // Quell compiler warning.
-
- // If we need a scratch register, use either an output register or the object
- // register (and restore it afterwards). After this point, we cannot jump
- // directly to |failures| since we may still have to pop the object register.
- Label prototypeFailures;
- if (obj != holder || !holder->isFixedSlot(shape->slot())) {
- if (output.hasValue()) {
- scratchReg = output.valueReg().scratchReg();
- } else if (output.type() == MIRType_Double) {
- scratchReg = object;
- masm.push(scratchReg);
- restoreScratch = true;
- } else {
- scratchReg = output.typedReg().gpr();
- }
- }
+ void branchExit(Assembler::Condition cond, Address addr, ImmGCPtr ptr) {
+ exitOffset = masm_.branchPtrWithPatch(cond, addr, ptr, &failures_);
+ }
+
+ void jumpRejoin() {
+ RepatchLabel rejoin_;
+ rejoinOffset = masm_.jumpWithPatch(&rejoin_);
+ masm_.bind(&rejoin_);
+ }
+
+ void jumpExit() {
+ RepatchLabel exit_;
+ exitOffset = masm_.jumpWithPatch(&exit_);
+ masm_.bind(&exit_);
+ }
+
+ void bindFailures() {
+ masm_.bind(&failures_);
+ }
+
+ void pushStubCodePatch(const ImmWord &word) {
+ stubCodePatchOffset = masm_.PushWithPatch(STUB_ADDR);
+ }
+};
- // Generate prototype guards.
- Register holderReg;
- if (obj != holder) {
- // Note: this may clobber the object register if it's used as scratch.
- GeneratePrototypeGuards(cx, masm, obj, holder, object, scratchReg, &prototypeFailures);
+template <class Helper>
+void
+ReadSlotImpl::generateReadSlot(JSContext *cx, MacroAssembler &masm, JSObject *obj,
+ PropertyName *propName, JSObject *holder, HandleShape shape,
+ Register object, TypedOrValueRegister output,
+ Helper &helper, Label *nonRepatchFailures)
+{
+ // If there's a single jump to |failures|, we can patch the shape guard
+ // jump directly. Otherwise, jump to the end of the stub, so there's a
+ // common point to patch.
+ bool multipleFailureJumps = (nonRepatchFailures != NULL) && nonRepatchFailures->used();
+ helper.branchExit(Assembler::NotEqual,
+ Address(object, JSObject::offsetOfShape()),
+ ImmGCPtr(obj->lastProperty()));
+
+ bool restoreScratch = false;
+ Register scratchReg = Register::FromCode(0); // Quell compiler warning.
+
+ // If we need a scratch register, use either an output register or the object
+ // register (and restore it afterwards). After this point, we cannot jump
+ // directly to |failures| since we may still have to pop the object register.
+ Label prototypeFailures;
+ if (obj != holder || !holder->isFixedSlot(shape->slot())) {
+ if (output.hasValue()) {
+ scratchReg = output.valueReg().scratchReg();
+ } else if (output.type() == MIRType_Double) {
+ scratchReg = object;
+ masm.push(scratchReg);
+ restoreScratch = true;
+ } else {
+ scratchReg = output.typedReg().gpr();
+ }
+ }
- if (holder) {
- // Guard on the holder's shape.
- holderReg = scratchReg;
- masm.movePtr(ImmGCPtr(holder), holderReg);
+ // Generate prototype guards.
+ Register holderReg;
+ if (obj != holder) {
+ // Note: this may clobber the object register if it's used as scratch.
+ GeneratePrototypeGuards(cx, masm, obj, holder, object, scratchReg, &prototypeFailures);
+
+ if (holder) {
+ // Guard on the holder's shape.
+ holderReg = scratchReg;
+ masm.movePtr(ImmGCPtr(holder), holderReg);
+ masm.branchPtr(Assembler::NotEqual,
+ Address(holderReg, JSObject::offsetOfShape()),
+ ImmGCPtr(holder->lastProperty()),
+ &prototypeFailures);
+ } else {
+ // The property does not exist. Guard on everything in the
+ // prototype chain.
+ JSObject *proto = obj->getTaggedProto().toObjectOrNull();
+ Register lastReg = object;
+ JS_ASSERT(scratchReg != object);
+ while (proto) {
+ Address addrType(lastReg, JSObject::offsetOfType());
+ masm.loadPtr(addrType, scratchReg);
+ Address addrProto(scratchReg, offsetof(types::TypeObject, proto));
+ masm.loadPtr(addrProto, scratchReg);
+ Address addrShape(scratchReg, JSObject::offsetOfShape());
+
+ // Guard the shape of the current prototype.
masm.branchPtr(Assembler::NotEqual,
- Address(holderReg, JSObject::offsetOfShape()),
- ImmGCPtr(holder->lastProperty()),
+ Address(scratchReg, JSObject::offsetOfShape()),
+ ImmGCPtr(proto->lastProperty()),
&prototypeFailures);
- } else {
- // The property does not exist. Guard on everything in the
- // prototype chain.
- JSObject *proto = obj->getTaggedProto().toObjectOrNull();
- Register lastReg = object;
- JS_ASSERT(scratchReg != object);
- while (proto) {
- Address addrType(lastReg, JSObject::offsetOfType());
- masm.loadPtr(addrType, scratchReg);
- Address addrProto(scratchReg, offsetof(types::TypeObject, proto));
- masm.loadPtr(addrProto, scratchReg);
- Address addrShape(scratchReg, JSObject::offsetOfShape());
-
- // Guard the shape of the current prototype.
- masm.branchPtr(Assembler::NotEqual,
- Address(scratchReg, JSObject::offsetOfShape()),
- ImmGCPtr(proto->lastProperty()),
- &prototypeFailures);
-
- proto = proto->getProto();
- lastReg = scratchReg;
- }
-
- holderReg = InvalidReg;
+
+ proto = proto->getProto();
+ lastReg = scratchReg;
}
- } else {
- holderReg = object;
+
+ holderReg = InvalidReg;
}
+ } else {
+ holderReg = object;
+ }
- // Slot access.
- if (holder && holder->isFixedSlot(shape->slot())) {
- Address addr(holderReg, JSObject::getFixedSlotOffset(shape->slot()));
- masm.loadTypedOrValue(addr, output);
- } else if (holder) {
- masm.loadPtr(Address(holderReg, JSObject::offsetOfSlots()), scratchReg);
+ // Slot access.
+ if (holder && holder->isFixedSlot(shape->slot())) {
+ Address addr(holderReg, JSObject::getFixedSlotOffset(shape->slot()));
+ masm.loadTypedOrValue(addr, output);
+ } else if (holder) {
+ masm.loadPtr(Address(holderReg, JSObject::offsetOfSlots()), scratchReg);
- Address addr(scratchReg, holder->dynamicSlotIndex(shape->slot()) * sizeof(Value));
- masm.loadTypedOrValue(addr, output);
- } else {
- JS_ASSERT(!holder);
- masm.moveValue(UndefinedValue(), output.valueReg());
- }
+ Address addr(scratchReg, holder->dynamicSlotIndex(shape->slot()) * sizeof(Value));
+ masm.loadTypedOrValue(addr, output);
+ } else {
+ JS_ASSERT(!holder);
+ masm.moveValue(UndefinedValue(), output.valueReg());
+ }
+ if (restoreScratch)
+ masm.pop(scratchReg);
+
+ helper.jumpRejoin();
+
+ if (obj != holder || multipleFailureJumps) {
+ masm.bind(&prototypeFailures);
if (restoreScratch)
masm.pop(scratchReg);
+ helper.bindFailures();
+ if (multipleFailureJumps)
+ masm.bind(nonRepatchFailures);
+ helper.jumpExit();
+ } else {
+ helper.bindFailures();
+ }
+}
- RepatchLabel rejoin_;
- rejoinOffset = masm.jumpWithPatch(&rejoin_);
- masm.bind(&rejoin_);
-
- if (obj != holder || multipleFailureJumps) {
- masm.bind(&prototypeFailures);
- if (restoreScratch)
- masm.pop(scratchReg);
- masm.bind(failures);
- if (multipleFailureJumps)
- masm.bind(nonRepatchFailures);
- RepatchLabel exit_;
- exitOffset = masm.jumpWithPatch(&exit_);
- masm.bind(&exit_);
+bool
+GetPropertyIC::attachReadSlot(JSContext *cx, IonScript *ion, JSObject *obj, JSObject *holder,
+ HandleShape shape)
+{
+ MacroAssembler masm(cx);
+
+ StubRepatchHelper helper(masm);
+ generateReadSlot(cx, masm, obj, name(), holder, shape, object(), output(), helper);
+
+ const char *attachKind = "non idempotent reading";
+ if (idempotent())
+ attachKind = "idempotent reading";
+ return linkAndAttachStub(cx, masm, ion, attachKind, helper.rejoinOffset, &helper.exitOffset);
+}
+
+template <class Helper>
+bool
+GetPropertyIC::generateCallGetter(JSContext *cx, MacroAssembler &masm, JSObject *obj,
+ PropertyName *propName, JSObject *holder, HandleShape shape,
+ RegisterSet &liveRegs, Register object,
+ TypedOrValueRegister output, void *returnAddr, jsbytecode *pc,
+ Helper &helper, Label *nonRepatchFailures)
+{
+ // Initial shape check.
+ Label stubFailure;
+ masm.branchPtr(Assembler::NotEqual, Address(object, JSObject::offsetOfShape()),
+ ImmGCPtr(obj->lastProperty()), &stubFailure);
+
+ // If this is a stub for a ListBase object, guard the following:
+ // 1. The object is a ListBase.
+ // 2. The object does not have expando properties, or has an expando
+ // which is known to not have the desired property.
+ if (IsCacheableListBase(obj)) {
+ Address handlerAddr(object, JSObject::getFixedSlotOffset(JSSLOT_PROXY_HANDLER));
+ Address expandoAddr(object, JSObject::getFixedSlotOffset(GetListBaseExpandoSlot()));
+
+ // Check that object is a ListBase.
+ masm.branchPrivatePtr(Assembler::NotEqual, handlerAddr, ImmWord(GetProxyHandler(obj)), &stubFailure);
+
+ // For the remaining code, we need to reserve some registers to load a value.
+ // This is ugly, but unvaoidable.
+ RegisterSet listBaseRegSet(RegisterSet::All());
+ listBaseRegSet.take(AnyRegister(object));
+ ValueOperand tempVal = listBaseRegSet.takeValueOperand();
+ masm.pushValue(tempVal);
+
+ Label failListBaseCheck;
+ Label listBaseOk;
+
+ Value expandoVal = obj->getFixedSlot(GetListBaseExpandoSlot());
+ JSObject *expando = expandoVal.isObject() ? &(expandoVal.toObject()) : NULL;
+ JS_ASSERT_IF(expando, expando->isNative() && expando->getProto() == NULL);
+
+ masm.loadValue(expandoAddr, tempVal);
+ if (expando && expando->nativeLookup(cx, propName)) {
+ // Reference object has an expando that doesn't define the name.
+ // Check incoming object's expando and make sure it's an object.
+
+ // If checkExpando is true, we'll temporarily use register(s) for a ValueOperand.
+ // If we do that, we save the register(s) on stack before use and pop them
+ // on both exit paths.
+
+ masm.branchTestObject(Assembler::NotEqual, tempVal, &failListBaseCheck);
+ masm.extractObject(tempVal, tempVal.scratchReg());
+ masm.branchPtr(Assembler::Equal,
+ Address(tempVal.scratchReg(), JSObject::offsetOfShape()),
+ ImmGCPtr(expando->lastProperty()),
+ &listBaseOk);
} else {
- masm.bind(failures);
+ // Reference object has no expando. Check incoming object and ensure
+ // it has no expando.
+ masm.branchTestUndefined(Assembler::Equal, tempVal, &listBaseOk);
}
- }
- bool generateCallGetter(JSContext *cx, MacroAssembler &masm, JSObject *obj,
- PropertyName *propName, JSObject *holder, HandleShape shape,
- RegisterSet &liveRegs, Register object, TypedOrValueRegister output,
- void *returnAddr, jsbytecode *pc,
- RepatchLabel *failures, Label *nonRepatchFailures = NULL)
- {
- // Initial shape check.
- Label stubFailure;
- masm.branchPtr(Assembler::NotEqual, Address(object, JSObject::offsetOfShape()),
- ImmGCPtr(obj->lastProperty()), &stubFailure);
-
- // If this is a stub for a ListBase object, guard the following:
- // 1. The object is a ListBase.
- // 2. The object does not have expando properties, or has an expando
- // which is known to not have the desired property.
- if (IsCacheableListBase(obj)) {
- Address handlerAddr(object, JSObject::getFixedSlotOffset(JSSLOT_PROXY_HANDLER));
- Address expandoAddr(object, JSObject::getFixedSlotOffset(GetListBaseExpandoSlot()));
-
- // Check that object is a ListBase.
- masm.branchPrivatePtr(Assembler::NotEqual, handlerAddr, ImmWord(GetProxyHandler(obj)), &stubFailure);
-
- // For the remaining code, we need to reserve some registers to load a value.
- // This is ugly, but unvaoidable.
- RegisterSet listBaseRegSet(RegisterSet::All());
- listBaseRegSet.take(AnyRegister(object));
- ValueOperand tempVal = listBaseRegSet.takeValueOperand();
- masm.pushValue(tempVal);
-
- Label failListBaseCheck;
- Label listBaseOk;
-
- Value expandoVal = obj->getFixedSlot(GetListBaseExpandoSlot());
- JSObject *expando = expandoVal.isObject() ? &(expandoVal.toObject()) : NULL;
- JS_ASSERT_IF(expando, expando->isNative() && expando->getProto() == NULL);
-
- masm.loadValue(expandoAddr, tempVal);
- if (expando && expando->nativeLookup(cx, propName)) {
- // Reference object has an expando that doesn't define the name.
- // Check incoming object's expando and make sure it's an object.
-
- // If checkExpando is true, we'll temporarily use register(s) for a ValueOperand.
- // If we do that, we save the register(s) on stack before use and pop them
- // on both exit paths.
-
- masm.branchTestObject(Assembler::NotEqual, tempVal, &failListBaseCheck);
- masm.extractObject(tempVal, tempVal.scratchReg());
- masm.branchPtr(Assembler::Equal,
- Address(tempVal.scratchReg(), JSObject::offsetOfShape()),
- ImmGCPtr(expando->lastProperty()),
- &listBaseOk);
- } else {
- // Reference object has no expando. Check incoming object and ensure
- // it has no expando.
- masm.branchTestUndefined(Assembler::Equal, tempVal, &listBaseOk);
- }
+ // Failure case: restore the tempVal registers and jump to failures.
+ masm.bind(&failListBaseCheck);
+ masm.popValue(tempVal);
+ masm.jump(&stubFailure);
- // Failure case: restore the tempVal registers and jump to failures.
- masm.bind(&failListBaseCheck);
- masm.popValue(tempVal);
- masm.jump(&stubFailure);
+ // Success case: restore the tempval and proceed.
+ masm.bind(&listBaseOk);
+ masm.popValue(tempVal);
+ }
- // Success case: restore the tempval and proceed.
- masm.bind(&listBaseOk);
- masm.popValue(tempVal);
- }
+ // Reserve scratch register for prototype guards.
+ bool restoreScratch = false;
+ Register scratchReg = Register::FromCode(0); // Quell compiler warning.
- // Reserve scratch register for prototype guards.
- bool restoreScratch = false;
- Register scratchReg = Register::FromCode(0); // Quell compiler warning.
+ // If we need a scratch register, use either an output register or the object
+ // register (and restore it afterwards). After this point, we cannot jump
+ // directly to |stubFailure| since we may still have to pop the object register.
+ Label prototypeFailures;
+ JS_ASSERT(output.hasValue());
+ scratchReg = output.valueReg().scratchReg();
- // If we need a scratch register, use either an output register or the object
- // register (and restore it afterwards). After this point, we cannot jump
- // directly to |stubFailure| since we may still have to pop the object register.
- Label prototypeFailures;
- JS_ASSERT(output.hasValue());
- scratchReg = output.valueReg().scratchReg();
+ // Note: this may clobber the object register if it's used as scratch.
+ if (obj != holder)
+ GeneratePrototypeGuards(cx, masm, obj, holder, object, scratchReg, &prototypeFailures);
- // Note: this may clobber the object register if it's used as scratch.
- if (obj != holder)
- GeneratePrototypeGuards(cx, masm, obj, holder, object, scratchReg, &prototypeFailures);
+ // Guard on the holder's shape.
+ Register holderReg = scratchReg;
+ masm.movePtr(ImmGCPtr(holder), holderReg);
+ masm.branchPtr(Assembler::NotEqual,
+ Address(holderReg, JSObject::offsetOfShape()),
+ ImmGCPtr(holder->lastProperty()),
+ &prototypeFailures);
- // Guard on the holder's shape.
- Register holderReg = scratchReg;
- masm.movePtr(ImmGCPtr(holder), holderReg);
- masm.branchPtr(Assembler::NotEqual,
- Address(holderReg, JSObject::offsetOfShape()),
- ImmGCPtr(holder->lastProperty()),
- &prototypeFailures);
+ if (restoreScratch)
+ masm.pop(scratchReg);
- if (restoreScratch)
- masm.pop(scratchReg);
+ // Now we're good to go to invoke the native call.
- // Now we're good to go to invoke the native call.
+ // saveLive()
+ masm.PushRegsInMask(liveRegs);
- // saveLive()
- masm.PushRegsInMask(liveRegs);
+ // Remaining registers should basically be free, but we need to use |object| still
+ // so leave it alone.
+ RegisterSet regSet(RegisterSet::All());
+ regSet.take(AnyRegister(object));
- // Remaining registers should basically be free, but we need to use |object| still
- // so leave it alone.
- RegisterSet regSet(RegisterSet::All());
- regSet.take(AnyRegister(object));
-
- // This is a slower stub path, and we're going to be doing a call anyway. Don't need
- // to try so hard to not use the stack. Scratch regs are just taken from the register
- // set not including the input, current value saved on the stack, and restored when
- // we're done with it.
- scratchReg = regSet.takeGeneral();
- Register argJSContextReg = regSet.takeGeneral();
- Register argUintNReg = regSet.takeGeneral();
- Register argVpReg = regSet.takeGeneral();
-
- // Shape has a getter function.
- bool callNative = IsCacheableGetPropCallNative(obj, holder, shape);
- JS_ASSERT_IF(!callNative, IsCacheableGetPropCallPropertyOp(obj, holder, shape));
-
- // TODO: ensure stack is aligned?
- DebugOnly<uint32_t> initialStack = masm.framePushed();
-
- Label success, exception;
-
- // Push the IonCode pointer for the stub we're generating.
- // WARNING:
- // WARNING: If IonCode ever becomes relocatable, the following code is incorrect.
- // WARNING: Note that we're not marking the pointer being pushed as an ImmGCPtr.
- // WARNING: This is not a marking issue since the stub IonCode won't be collected
- // WARNING: between the time it's called and when we get here, but it would fail
- // WARNING: if the IonCode object ever moved, since we'd be rooting a nonsense
- // WARNING: value here.
- // WARNING:
- stubCodePatchOffset = masm.PushWithPatch(STUB_ADDR);
-
- if (callNative) {
- JS_ASSERT(shape->hasGetterValue() && shape->getterValue().isObject() &&
- shape->getterValue().toObject().isFunction());
- JSFunction *target = shape->getterValue().toObject().toFunction();
-
- JS_ASSERT(target);
- JS_ASSERT(target->isNative());
-
- // Native functions have the signature:
- // bool (*)(JSContext *, unsigned, Value *vp)
- // Where vp[0] is space for an outparam, vp[1] is |this|, and vp[2] onward
- // are the function arguments.
-
- // Construct vp array:
- // Push object value for |this|
- masm.Push(TypedOrValueRegister(MIRType_Object, AnyRegister(object)));
- // Push callee/outparam.
- masm.Push(ObjectValue(*target));
-
- // Preload arguments into registers.
- masm.loadJSContext(argJSContextReg);
- masm.move32(Imm32(0), argUintNReg);
- masm.movePtr(StackPointer, argVpReg);
-
- if (!masm.buildOOLFakeExitFrame(returnAddr))
- return false;
- masm.enterFakeExitFrame(ION_FRAME_OOL_NATIVE_GETTER);
-
- // Construct and execute call.
- masm.setupUnalignedABICall(3, scratchReg);
- masm.passABIArg(argJSContextReg);
- masm.passABIArg(argUintNReg);
- masm.passABIArg(argVpReg);
- masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, target->native()));
-
- // Test for failure.
- masm.branchTest32(Assembler::Zero, ReturnReg, ReturnReg, &exception);
-
- // Load the outparam vp[0] into output register(s).
- masm.loadValue(
- Address(StackPointer, IonOOLNativeGetterExitFrameLayout::offsetOfResult()),
- JSReturnOperand);
- } else {
- Register argObjReg = argUintNReg;
- Register argIdReg = regSet.takeGeneral();
+ // This is a slower stub path, and we're going to be doing a call anyway. Don't need
+ // to try so hard to not use the stack. Scratch regs are just taken from the register
+ // set not including the input, current value saved on the stack, and restored when
+ // we're done with it.
+ scratchReg = regSet.takeGeneral();
+ Register argJSContextReg = regSet.takeGeneral();
+ Register argUintNReg = regSet.takeGeneral();
+ Register argVpReg = regSet.takeGeneral();
- PropertyOp target = shape->getterOp();
- JS_ASSERT(target);
- // JSPropertyOp: JSBool fn(JSContext *cx, JSHandleObject obj, JSHandleId id, JSMutableHandleValue vp)
+ // Shape has a getter function.
+ bool callNative = IsCacheableGetPropCallNative(obj, holder, shape);
+ JS_ASSERT_IF(!callNative, IsCacheableGetPropCallPropertyOp(obj, holder, shape));
- // Push args on stack first so we can take pointers to make handles.
- masm.Push(UndefinedValue());
- masm.movePtr(StackPointer, argVpReg);
+ // TODO: ensure stack is aligned?
+ DebugOnly<uint32_t> initialStack = masm.framePushed();
- // push canonical jsid from shape instead of propertyname.
- RootedId propId(cx);
- if (!shape->getUserId(cx, &propId))
- return false;
- masm.Push(propId, scratchReg);
- masm.movePtr(StackPointer, argIdReg);
+ Label success, exception;
- masm.Push(object);
- masm.movePtr(StackPointer, argObjReg);
+ // Push the IonCode pointer for the stub we're generating.
+ // WARNING:
+ // WARNING: If IonCode ever becomes relocatable, the following code is incorrect.
+ // WARNING: Note that we're not marking the pointer being pushed as an ImmGCPtr.
+ // WARNING: This is not a marking issue since the stub IonCode won't be collected
+ // WARNING: between the time it's called and when we get here, but it would fail
+ // WARNING: if the IonCode object ever moved, since we'd be rooting a nonsense
+ // WARNING: value here.
+ // WARNING:
+ helper.pushStubCodePatch(STUB_ADDR);
- masm.loadJSContext(argJSContextReg);
+ if (callNative) {
+ JS_ASSERT(shape->hasGetterValue() && shape->getterValue().isObject() &&
+ shape->getterValue().toObject().isFunction());
+ JSFunction *target = shape->getterValue().toObject().toFunction();
- if (!masm.buildOOLFakeExitFrame(returnAddr))
- return false;
- masm.enterFakeExitFrame(ION_FRAME_OOL_PROPERTY_OP);
-
- // Make the call.
- masm.setupUnalignedABICall(4, scratchReg);
- masm.passABIArg(argJSContextReg);
- masm.passABIArg(argObjReg);
- masm.passABIArg(argIdReg);
- masm.passABIArg(argVpReg);
- masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, target));
-
- // Test for failure.
- masm.branchTest32(Assembler::Zero, ReturnReg, ReturnReg, &exception);
-
- // Load the outparam vp[0] into output register(s).
- masm.loadValue(
- Address(StackPointer, IonOOLPropertyOpExitFrameLayout::offsetOfResult()),
- JSReturnOperand);
- }
+ JS_ASSERT(target);
+ JS_ASSERT(target->isNative());
- // If generating getter call stubs, then return type MUST have been generalized
- // to MIRType_Value.
- masm.jump(&success);
+ // Native functions have the signature:
+ // bool (*)(JSContext *, unsigned, Value *vp)
+ // Where vp[0] is space for an outparam, vp[1] is |this|, and vp[2] onward
+ // are the function arguments.
- // Handle exception case.
- masm.bind(&exception);
- masm.handleException();
+ // Construct vp array:
+ // Push object value for |this|
+ masm.Push(TypedOrValueRegister(MIRType_Object, AnyRegister(object)));
+ // Push callee/outparam.
+ masm.Push(ObjectValue(*target));
- // Handle success case.
- masm.bind(&success);
- masm.storeCallResultValue(output);
+ // Preload arguments into registers.
+ masm.loadJSContext(argJSContextReg);
+ masm.move32(Imm32(0), argUintNReg);
+ masm.movePtr(StackPointer, argVpReg);
- // The next instruction is removing the footer of the exit frame, so there
- // is no need for leaveFakeExitFrame.
+ if (!masm.buildOOLFakeExitFrame(returnAddr))
+ return false;
+ masm.enterFakeExitFrame(ION_FRAME_OOL_NATIVE_GETTER);
+
+ // Construct and execute call.
+ masm.setupUnalignedABICall(3, scratchReg);
+ masm.passABIArg(argJSContextReg);
+ masm.passABIArg(argUintNReg);
+ masm.passABIArg(argVpReg);
+ masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, target->native()));
+
+ // Test for failure.
+ masm.branchTest32(Assembler::Zero, ReturnReg, ReturnReg, &exception);
+
+ // Load the outparam vp[0] into output register(s).
+ masm.loadValue(
+ Address(StackPointer, IonOOLNativeGetterExitFrameLayout::offsetOfResult()),
+ JSReturnOperand);
+ } else {
+ Register argObjReg = argUintNReg;
+ Register argIdReg = regSet.takeGeneral();
- // Move the StackPointer back to its original location, unwinding the native exit frame.
- if (callNative)
- masm.adjustStack(IonOOLNativeGetterExitFrameLayout::Size());
- else
- masm.adjustStack(IonOOLPropertyOpExitFrameLayout::Size());
- JS_ASSERT(masm.framePushed() == initialStack);
+ PropertyOp target = shape->getterOp();
+ JS_ASSERT(target);
+ // JSPropertyOp: JSBool fn(JSContext *cx, JSHandleObject obj, JSHandleId id, JSMutableHandleValue vp)
- // restoreLive()
- masm.PopRegsInMask(liveRegs);
+ // Push args on stack first so we can take pointers to make handles.
+ masm.Push(UndefinedValue());
+ masm.movePtr(StackPointer, argVpReg);
- // Rejoin jump.
- RepatchLabel rejoin_;
- rejoinOffset = masm.jumpWithPatch(&rejoin_);
- masm.bind(&rejoin_);
+ // push canonical jsid from shape instead of propertyname.
+ RootedId propId(cx);
+ if (!shape->getUserId(cx, &propId))
+ return false;
+ masm.Push(propId, scratchReg);
+ masm.movePtr(StackPointer, argIdReg);
- // Exit jump.
- masm.bind(&prototypeFailures);
- if (restoreScratch)
- masm.pop(scratchReg);
- masm.bind(&stubFailure);
- if (nonRepatchFailures)
- masm.bind(nonRepatchFailures);
- RepatchLabel exit_;
- exitOffset = masm.jumpWithPatch(&exit_);
- masm.bind(&exit_);
+ masm.Push(object);
+ masm.movePtr(StackPointer, argObjReg);
- return true;
+ masm.loadJSContext(argJSContextReg);
+
+ if (!masm.buildOOLFakeExitFrame(returnAddr))
+ return false;
+ masm.enterFakeExitFrame(ION_FRAME_OOL_PROPERTY_OP);
+
+ // Make the call.
+ masm.setupUnalignedABICall(4, scratchReg);
+ masm.passABIArg(argJSContextReg);
+ masm.passABIArg(argObjReg);
+ masm.passABIArg(argIdReg);
+ masm.passABIArg(argVpReg);
+ masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, target));
+
+ // Test for failure.
+ masm.branchTest32(Assembler::Zero, ReturnReg, ReturnReg, &exception);
+
+ // Load the outparam vp[0] into output register(s).
+ masm.loadValue(
+ Address(StackPointer, IonOOLPropertyOpExitFrameLayout::offsetOfResult()),
+ JSReturnOperand);
}
-};
-bool
-GetPropertyIC::attachReadSlot(JSContext *cx, IonScript *ion, JSObject *obj, JSObject *holder,
- HandleShape shape)
-{
- MacroAssembler masm(cx);
- RepatchLabel failures;
+ // If generating getter call stubs, then return type MUST have been generalized
+ // to MIRType_Value.
+ masm.jump(&success);
- GetNativePropertyStub getprop;
- getprop.generateReadSlot(cx, masm, obj, name(), holder, shape, object(), output(), &failures);
+ // Handle exception case.
+ masm.bind(&exception);
+ masm.handleException();
- const char *attachKind = "non idempotent reading";
- if (idempotent())
- attachKind = "idempotent reading";
- return linkAndAttachStub(cx, masm, ion, attachKind, getprop.rejoinOffset, &getprop.exitOffset);
+ // Handle success case.
+ masm.bind(&success);
+ masm.storeCallResultValue(output);
+
+ // The next instruction is removing the footer of the exit frame, so there
+ // is no need for leaveFakeExitFrame.
+
+ // Move the StackPointer back to its original location, unwinding the native exit frame.
+ if (callNative)
+ masm.adjustStack(IonOOLNativeGetterExitFrameLayout::Size());
+ else
+ masm.adjustStack(IonOOLPropertyOpExitFrameLayout::Size());
+ JS_ASSERT(masm.framePushed() == initialStack);
+
+ // restoreLive()
+ masm.PopRegsInMask(liveRegs);
+
+ // Rejoin jump.
+ helper.jumpRejoin();
+
+ // Exit jump.
+ masm.bind(&prototypeFailures);
+ if (restoreScratch)
+ masm.pop(scratchReg);
+ masm.bind(&stubFailure);
+ if (nonRepatchFailures)
+ masm.bind(nonRepatchFailures);
+ helper.jumpExit();
+
+ return true;
}
bool
@@ -737,7 +800,6 @@ GetPropertyIC::attachCallGetter(JSContext *cx, IonScript *ion, JSObject *obj,
const SafepointIndex *safepointIndex, void *returnAddr)
{
MacroAssembler masm(cx);
- RepatchLabel failures;
JS_ASSERT(!idempotent());
JS_ASSERT(allowGetters());
@@ -746,9 +808,9 @@ GetPropertyIC::attachCallGetter(JSContext *cx, IonScript *ion, JSObject *obj,
// properly constructed.
masm.setFramePushed(ion->frameSize());
- GetNativePropertyStub getprop;
- if (!getprop.generateCallGetter(cx, masm, obj, name(), holder, shape, liveRegs_,
- object(), output(), returnAddr, pc, &failures))
+ StubRepatchHelper helper(masm);
+ if (!generateCallGetter(cx, masm, obj, name(), holder, shape, liveRegs_,
+ object(), output(), returnAddr, pc, helper))
{
return false;
}
@@ -756,8 +818,8 @@ GetPropertyIC::attachCallGetter(JSContext *cx, IonScript *ion, JSObject *obj,
const char *attachKind = "non idempotent calling";
if (idempotent())
attachKind = "idempotent calling";
- return linkAndAttachStub(cx, masm, ion, attachKind, getprop.rejoinOffset, &getprop.exitOffset,
- &getprop.stubCodePatchOffset);
+ return linkAndAttachStub(cx, masm, ion, attachKind, helper.rejoinOffset, &helper.exitOffset,
+ &helper.stubCodePatchOffset);
}
bool
@@ -1691,7 +1753,6 @@ GetElementIC::attachGetProp(JSContext *cx, IonScript *ion, HandleObject obj,
JS_ASSERT(idval.isString());
- RepatchLabel failures;
Label nonRepatchFailures;
MacroAssembler masm(cx);
@@ -1699,11 +1760,11 @@ GetElementIC::attachGetProp(JSContext *cx, IonScript *ion, HandleObject obj,
ValueOperand val = index().reg().valueReg();
masm.branchTestValue(Assembler::NotEqual, val, idval, &nonRepatchFailures);
- GetNativePropertyStub getprop;
- getprop.generateReadSlot(cx, masm, obj, name, holder, shape, object(), output(), &failures,
- &nonRepatchFailures);
+ StubRepatchHelper helper(masm);
+ generateReadSlot(cx, masm, obj, name, holder, shape, object(), output(), helper,
+ &nonRepatchFailures);
- return linkAndAttachStub(cx, masm, ion, "property", getprop.rejoinOffset, &getprop.exitOffset);
+ return linkAndAttachStub(cx, masm, ion, "property", helper.rejoinOffset, &helper.exitOffset);
}
bool
diff --git a/js/src/ion/IonCaches.h b/js/src/ion/IonCaches.h
index 984e0d8..ef3228f 100644
--- a/js/src/ion/IonCaches.h
+++ b/js/src/ion/IonCaches.h
@@ -303,7 +310,17 @@ class IonCache
// Subclasses of IonCache for the various kinds of caches. These do not define
// new data members; all caches must be of the same size.
-class GetPropertyIC : public IonCache
+// Shared base class by GetPropertyIC and GetElementIC
+class ReadSlotImpl
+{
+ protected:
+ template <class Helper>
+ void generateReadSlot(JSContext *cx, MacroAssembler &masm, JSObject *obj, PropertyName *propName,
+ JSObject *holder, HandleShape shape, Register object, TypedOrValueRegister output,
+ Helper &helper, Label *nonRepatchFailures = NULL);
+};
+
+class GetPropertyIC : public IonCache, public ReadSlotImpl
{
protected:
// Registers live after the cache, excluding output registers. The initial
@@ -317,6 +334,13 @@ class GetPropertyIC : public IonCache
bool hasArrayLengthStub_ : 1;
bool hasTypedArrayLengthStub_ : 1;
+ template <class Helper>
+ bool generateCallGetter(JSContext *cx, MacroAssembler &masm, JSObject *obj,
+ PropertyName *propName, JSObject *holder, HandleShape shape,
+ RegisterSet &liveRegs, Register object, TypedOrValueRegister output,
+ void *returnAddr, jsbytecode *pc,
+ Helper &helper, Label *nonRepatchFailures = NULL);
+
public:
GetPropertyIC(RegisterSet liveRegs,
Register object, PropertyName *name,
@@ -417,7 +441,7 @@ class SetPropertyIC : public IonCache
update(JSContext *cx, size_t cacheIndex, HandleObject obj, HandleValue value);
};
-class GetElementIC : public IonCache
+class GetElementIC : public IonCache, public ReadSlotImpl
{
protected:
Register object_;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment