michael@0: /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- michael@0: * vim: set ts=8 sts=4 et sw=4 tw=99: michael@0: * This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #ifndef jit_x86_MacroAssembler_x86_h michael@0: #define jit_x86_MacroAssembler_x86_h michael@0: michael@0: #include "jscompartment.h" michael@0: michael@0: #include "jit/IonFrames.h" michael@0: #include "jit/MoveResolver.h" michael@0: #include "jit/shared/MacroAssembler-x86-shared.h" michael@0: michael@0: namespace js { michael@0: namespace jit { michael@0: michael@0: class MacroAssemblerX86 : public MacroAssemblerX86Shared michael@0: { michael@0: // Number of bytes the stack is adjusted inside a call to C. Calls to C may michael@0: // not be nested. michael@0: bool inCall_; michael@0: uint32_t args_; michael@0: uint32_t passedArgs_; michael@0: uint32_t stackForCall_; michael@0: bool dynamicAlignment_; michael@0: bool enoughMemory_; michael@0: michael@0: struct Double { michael@0: double value; michael@0: AbsoluteLabel uses; michael@0: Double(double value) : value(value) {} michael@0: }; michael@0: Vector doubles_; michael@0: struct Float { michael@0: float value; michael@0: AbsoluteLabel uses; michael@0: Float(float value) : value(value) {} michael@0: }; michael@0: Vector floats_; michael@0: michael@0: typedef HashMap, SystemAllocPolicy> DoubleMap; michael@0: DoubleMap doubleMap_; michael@0: typedef HashMap, SystemAllocPolicy> FloatMap; michael@0: FloatMap floatMap_; michael@0: michael@0: Double *getDouble(double d); michael@0: Float *getFloat(float f); michael@0: michael@0: protected: michael@0: MoveResolver moveResolver_; michael@0: michael@0: private: michael@0: Operand payloadOf(const Address &address) { michael@0: return Operand(address.base, address.offset); michael@0: } michael@0: Operand tagOf(const Address &address) { michael@0: return Operand(address.base, address.offset + 4); michael@0: } michael@0: Operand tagOf(const BaseIndex &address) { michael@0: return Operand(address.base, address.index, address.scale, address.offset + 4); michael@0: } michael@0: michael@0: void setupABICall(uint32_t args); michael@0: michael@0: public: michael@0: using MacroAssemblerX86Shared::Push; michael@0: using MacroAssemblerX86Shared::Pop; michael@0: using MacroAssemblerX86Shared::callWithExitFrame; michael@0: using MacroAssemblerX86Shared::branch32; michael@0: using MacroAssemblerX86Shared::load32; michael@0: using MacroAssemblerX86Shared::store32; michael@0: using MacroAssemblerX86Shared::call; michael@0: michael@0: MacroAssemblerX86() michael@0: : inCall_(false), michael@0: enoughMemory_(true) michael@0: { michael@0: } michael@0: michael@0: // The buffer is about to be linked, make sure any constant pools or excess michael@0: // bookkeeping has been flushed to the instruction stream. michael@0: void finish(); michael@0: michael@0: bool oom() const { michael@0: return MacroAssemblerX86Shared::oom() || !enoughMemory_; michael@0: } michael@0: michael@0: ///////////////////////////////////////////////////////////////// michael@0: // X86-specific interface. michael@0: ///////////////////////////////////////////////////////////////// michael@0: michael@0: Operand ToPayload(Operand base) { michael@0: return base; michael@0: } michael@0: Address ToPayload(Address base) { michael@0: return base; michael@0: } michael@0: Operand ToType(Operand base) { michael@0: switch (base.kind()) { michael@0: case Operand::MEM_REG_DISP: michael@0: return Operand(Register::FromCode(base.base()), base.disp() + sizeof(void *)); michael@0: michael@0: case Operand::MEM_SCALE: michael@0: return Operand(Register::FromCode(base.base()), Register::FromCode(base.index()), michael@0: base.scale(), base.disp() + sizeof(void *)); michael@0: michael@0: default: michael@0: MOZ_ASSUME_UNREACHABLE("unexpected operand kind"); michael@0: } michael@0: } michael@0: Address ToType(Address base) { michael@0: return ToType(Operand(base)).toAddress(); michael@0: } michael@0: void moveValue(const Value &val, Register type, Register data) { michael@0: jsval_layout jv = JSVAL_TO_IMPL(val); michael@0: movl(Imm32(jv.s.tag), type); michael@0: if (val.isMarkable()) michael@0: movl(ImmGCPtr(reinterpret_cast(val.toGCThing())), data); michael@0: else michael@0: movl(Imm32(jv.s.payload.i32), data); michael@0: } michael@0: void moveValue(const Value &val, const ValueOperand &dest) { michael@0: moveValue(val, dest.typeReg(), dest.payloadReg()); michael@0: } michael@0: void moveValue(const ValueOperand &src, const ValueOperand &dest) { michael@0: Register s0 = src.typeReg(), d0 = dest.typeReg(), michael@0: s1 = src.payloadReg(), d1 = dest.payloadReg(); michael@0: michael@0: // Either one or both of the source registers could be the same as a michael@0: // destination register. michael@0: if (s1 == d0) { michael@0: if (s0 == d1) { michael@0: // If both are, this is just a swap of two registers. michael@0: xchgl(d0, d1); michael@0: return; michael@0: } michael@0: // If only one is, copy that source first. michael@0: mozilla::Swap(s0, s1); michael@0: mozilla::Swap(d0, d1); michael@0: } michael@0: michael@0: if (s0 != d0) michael@0: movl(s0, d0); michael@0: if (s1 != d1) michael@0: movl(s1, d1); michael@0: } michael@0: michael@0: ///////////////////////////////////////////////////////////////// michael@0: // X86/X64-common interface. michael@0: ///////////////////////////////////////////////////////////////// michael@0: void storeValue(ValueOperand val, Operand dest) { michael@0: movl(val.payloadReg(), ToPayload(dest)); michael@0: movl(val.typeReg(), ToType(dest)); michael@0: } michael@0: void storeValue(ValueOperand val, const Address &dest) { michael@0: storeValue(val, Operand(dest)); michael@0: } michael@0: template michael@0: void storeValue(JSValueType type, Register reg, const T &dest) { michael@0: storeTypeTag(ImmTag(JSVAL_TYPE_TO_TAG(type)), Operand(dest)); michael@0: storePayload(reg, Operand(dest)); michael@0: } michael@0: template michael@0: void storeValue(const Value &val, const T &dest) { michael@0: jsval_layout jv = JSVAL_TO_IMPL(val); michael@0: storeTypeTag(ImmTag(jv.s.tag), Operand(dest)); michael@0: storePayload(val, Operand(dest)); michael@0: } michael@0: void storeValue(ValueOperand val, BaseIndex dest) { michael@0: storeValue(val, Operand(dest)); michael@0: } michael@0: void loadValue(Operand src, ValueOperand val) { michael@0: Operand payload = ToPayload(src); michael@0: Operand type = ToType(src); michael@0: michael@0: // Ensure that loading the payload does not erase the pointer to the michael@0: // Value in memory or the index. michael@0: Register baseReg = Register::FromCode(src.base()); michael@0: Register indexReg = (src.kind() == Operand::MEM_SCALE) ? Register::FromCode(src.index()) : InvalidReg; michael@0: michael@0: if (baseReg == val.payloadReg() || indexReg == val.payloadReg()) { michael@0: JS_ASSERT(baseReg != val.typeReg()); michael@0: JS_ASSERT(indexReg != val.typeReg()); michael@0: michael@0: movl(type, val.typeReg()); michael@0: movl(payload, val.payloadReg()); michael@0: } else { michael@0: JS_ASSERT(baseReg != val.payloadReg()); michael@0: JS_ASSERT(indexReg != val.payloadReg()); michael@0: michael@0: movl(payload, val.payloadReg()); michael@0: movl(type, val.typeReg()); michael@0: } michael@0: } michael@0: void loadValue(Address src, ValueOperand val) { michael@0: loadValue(Operand(src), val); michael@0: } michael@0: void loadValue(const BaseIndex &src, ValueOperand val) { michael@0: loadValue(Operand(src), val); michael@0: } michael@0: void tagValue(JSValueType type, Register payload, ValueOperand dest) { michael@0: JS_ASSERT(dest.typeReg() != dest.payloadReg()); michael@0: if (payload != dest.payloadReg()) michael@0: movl(payload, dest.payloadReg()); michael@0: movl(ImmType(type), dest.typeReg()); michael@0: } michael@0: void pushValue(ValueOperand val) { michael@0: push(val.typeReg()); michael@0: push(val.payloadReg()); michael@0: } michael@0: void popValue(ValueOperand val) { michael@0: pop(val.payloadReg()); michael@0: pop(val.typeReg()); michael@0: } michael@0: void pushValue(const Value &val) { michael@0: jsval_layout jv = JSVAL_TO_IMPL(val); michael@0: push(Imm32(jv.s.tag)); michael@0: if (val.isMarkable()) michael@0: push(ImmGCPtr(reinterpret_cast(val.toGCThing()))); michael@0: else michael@0: push(Imm32(jv.s.payload.i32)); michael@0: } michael@0: void pushValue(JSValueType type, Register reg) { michael@0: push(ImmTag(JSVAL_TYPE_TO_TAG(type))); michael@0: push(reg); michael@0: } michael@0: void pushValue(const Address &addr) { michael@0: push(tagOf(addr)); michael@0: push(payloadOf(addr)); michael@0: } michael@0: void Push(const ValueOperand &val) { michael@0: pushValue(val); michael@0: framePushed_ += sizeof(Value); michael@0: } michael@0: void Pop(const ValueOperand &val) { michael@0: popValue(val); michael@0: framePushed_ -= sizeof(Value); michael@0: } michael@0: void storePayload(const Value &val, Operand dest) { michael@0: jsval_layout jv = JSVAL_TO_IMPL(val); michael@0: if (val.isMarkable()) michael@0: movl(ImmGCPtr((gc::Cell *)jv.s.payload.ptr), ToPayload(dest)); michael@0: else michael@0: movl(Imm32(jv.s.payload.i32), ToPayload(dest)); michael@0: } michael@0: void storePayload(Register src, Operand dest) { michael@0: movl(src, ToPayload(dest)); michael@0: } michael@0: void storeTypeTag(ImmTag tag, Operand dest) { michael@0: movl(tag, ToType(dest)); michael@0: } michael@0: michael@0: void movePtr(const Register &src, const Register &dest) { michael@0: movl(src, dest); michael@0: } michael@0: void movePtr(const Register &src, const Operand &dest) { michael@0: movl(src, dest); michael@0: } michael@0: michael@0: // Returns the register containing the type tag. michael@0: Register splitTagForTest(const ValueOperand &value) { michael@0: return value.typeReg(); michael@0: } michael@0: michael@0: Condition testUndefined(Condition cond, const Register &tag) { michael@0: JS_ASSERT(cond == Equal || cond == NotEqual); michael@0: cmpl(tag, ImmTag(JSVAL_TAG_UNDEFINED)); michael@0: return cond; michael@0: } michael@0: Condition testBoolean(Condition cond, const Register &tag) { michael@0: JS_ASSERT(cond == Equal || cond == NotEqual); michael@0: cmpl(tag, ImmTag(JSVAL_TAG_BOOLEAN)); michael@0: return cond; michael@0: } michael@0: Condition testInt32(Condition cond, const Register &tag) { michael@0: JS_ASSERT(cond == Equal || cond == NotEqual); michael@0: cmpl(tag, ImmTag(JSVAL_TAG_INT32)); michael@0: return cond; michael@0: } michael@0: Condition testDouble(Condition cond, const Register &tag) { michael@0: JS_ASSERT(cond == Assembler::Equal || cond == Assembler::NotEqual); michael@0: Condition actual = (cond == Equal) ? Below : AboveOrEqual; michael@0: cmpl(tag, ImmTag(JSVAL_TAG_CLEAR)); michael@0: return actual; michael@0: } michael@0: Condition testNull(Condition cond, const Register &tag) { michael@0: JS_ASSERT(cond == Equal || cond == NotEqual); michael@0: cmpl(tag, ImmTag(JSVAL_TAG_NULL)); michael@0: return cond; michael@0: } michael@0: Condition testString(Condition cond, const Register &tag) { michael@0: JS_ASSERT(cond == Equal || cond == NotEqual); michael@0: cmpl(tag, ImmTag(JSVAL_TAG_STRING)); michael@0: return cond; michael@0: } michael@0: Condition testObject(Condition cond, const Register &tag) { michael@0: JS_ASSERT(cond == Equal || cond == NotEqual); michael@0: cmpl(tag, ImmTag(JSVAL_TAG_OBJECT)); michael@0: return cond; michael@0: } michael@0: Condition testNumber(Condition cond, const Register &tag) { michael@0: JS_ASSERT(cond == Equal || cond == NotEqual); michael@0: cmpl(tag, ImmTag(JSVAL_UPPER_INCL_TAG_OF_NUMBER_SET)); michael@0: return cond == Equal ? BelowOrEqual : Above; michael@0: } michael@0: Condition testGCThing(Condition cond, const Register &tag) { michael@0: JS_ASSERT(cond == Equal || cond == NotEqual); michael@0: cmpl(tag, ImmTag(JSVAL_LOWER_INCL_TAG_OF_GCTHING_SET)); michael@0: return cond == Equal ? AboveOrEqual : Below; michael@0: } michael@0: Condition testGCThing(Condition cond, const Address &address) { michael@0: JS_ASSERT(cond == Equal || cond == NotEqual); michael@0: cmpl(tagOf(address), ImmTag(JSVAL_LOWER_INCL_TAG_OF_GCTHING_SET)); michael@0: return cond == Equal ? AboveOrEqual : Below; michael@0: } michael@0: Condition testMagic(Condition cond, const Address &address) { michael@0: JS_ASSERT(cond == Equal || cond == NotEqual); michael@0: cmpl(tagOf(address), ImmTag(JSVAL_TAG_MAGIC)); michael@0: return cond; michael@0: } michael@0: Condition testMagic(Condition cond, const Register &tag) { michael@0: JS_ASSERT(cond == Equal || cond == NotEqual); michael@0: cmpl(tag, ImmTag(JSVAL_TAG_MAGIC)); michael@0: return cond; michael@0: } michael@0: Condition testMagic(Condition cond, const Operand &operand) { michael@0: JS_ASSERT(cond == Equal || cond == NotEqual); michael@0: cmpl(ToType(operand), ImmTag(JSVAL_TAG_MAGIC)); michael@0: return cond; michael@0: } michael@0: Condition testPrimitive(Condition cond, const Register &tag) { michael@0: JS_ASSERT(cond == Equal || cond == NotEqual); michael@0: cmpl(tag, ImmTag(JSVAL_UPPER_EXCL_TAG_OF_PRIMITIVE_SET)); michael@0: return cond == Equal ? Below : AboveOrEqual; michael@0: } michael@0: Condition testError(Condition cond, const Register &tag) { michael@0: return testMagic(cond, tag); michael@0: } michael@0: Condition testInt32(Condition cond, const Operand &operand) { michael@0: JS_ASSERT(cond == Equal || cond == NotEqual); michael@0: cmpl(ToType(operand), ImmTag(JSVAL_TAG_INT32)); michael@0: return cond; michael@0: } michael@0: Condition testInt32(Condition cond, const Address &address) { michael@0: JS_ASSERT(cond == Equal || cond == NotEqual); michael@0: return testInt32(cond, Operand(address)); michael@0: } michael@0: Condition testDouble(Condition cond, const Operand &operand) { michael@0: JS_ASSERT(cond == Equal || cond == NotEqual); michael@0: Condition actual = (cond == Equal) ? Below : AboveOrEqual; michael@0: cmpl(ToType(operand), ImmTag(JSVAL_TAG_CLEAR)); michael@0: return actual; michael@0: } michael@0: Condition testDouble(Condition cond, const Address &address) { michael@0: JS_ASSERT(cond == Equal || cond == NotEqual); michael@0: return testDouble(cond, Operand(address)); michael@0: } michael@0: michael@0: michael@0: Condition testUndefined(Condition cond, const Operand &operand) { michael@0: JS_ASSERT(cond == Equal || cond == NotEqual); michael@0: cmpl(ToType(operand), ImmTag(JSVAL_TAG_UNDEFINED)); michael@0: return cond; michael@0: } michael@0: Condition testUndefined(Condition cond, const Address &addr) { michael@0: return testUndefined(cond, Operand(addr)); michael@0: } michael@0: michael@0: michael@0: Condition testUndefined(Condition cond, const ValueOperand &value) { michael@0: return testUndefined(cond, value.typeReg()); michael@0: } michael@0: Condition testBoolean(Condition cond, const ValueOperand &value) { michael@0: return testBoolean(cond, value.typeReg()); michael@0: } michael@0: Condition testInt32(Condition cond, const ValueOperand &value) { michael@0: return testInt32(cond, value.typeReg()); michael@0: } michael@0: Condition testDouble(Condition cond, const ValueOperand &value) { michael@0: return testDouble(cond, value.typeReg()); michael@0: } michael@0: Condition testNull(Condition cond, const ValueOperand &value) { michael@0: return testNull(cond, value.typeReg()); michael@0: } michael@0: Condition testString(Condition cond, const ValueOperand &value) { michael@0: return testString(cond, value.typeReg()); michael@0: } michael@0: Condition testObject(Condition cond, const ValueOperand &value) { michael@0: return testObject(cond, value.typeReg()); michael@0: } michael@0: Condition testMagic(Condition cond, const ValueOperand &value) { michael@0: return testMagic(cond, value.typeReg()); michael@0: } michael@0: Condition testError(Condition cond, const ValueOperand &value) { michael@0: return testMagic(cond, value); michael@0: } michael@0: Condition testNumber(Condition cond, const ValueOperand &value) { michael@0: return testNumber(cond, value.typeReg()); michael@0: } michael@0: Condition testGCThing(Condition cond, const ValueOperand &value) { michael@0: return testGCThing(cond, value.typeReg()); michael@0: } michael@0: Condition testPrimitive(Condition cond, const ValueOperand &value) { michael@0: return testPrimitive(cond, value.typeReg()); michael@0: } michael@0: michael@0: michael@0: Condition testUndefined(Condition cond, const BaseIndex &address) { michael@0: JS_ASSERT(cond == Equal || cond == NotEqual); michael@0: cmpl(tagOf(address), ImmTag(JSVAL_TAG_UNDEFINED)); michael@0: return cond; michael@0: } michael@0: Condition testNull(Condition cond, const BaseIndex &address) { michael@0: JS_ASSERT(cond == Equal || cond == NotEqual); michael@0: cmpl(tagOf(address), ImmTag(JSVAL_TAG_NULL)); michael@0: return cond; michael@0: } michael@0: Condition testBoolean(Condition cond, const BaseIndex &address) { michael@0: JS_ASSERT(cond == Equal || cond == NotEqual); michael@0: cmpl(tagOf(address), ImmTag(JSVAL_TAG_BOOLEAN)); michael@0: return cond; michael@0: } michael@0: Condition testString(Condition cond, const BaseIndex &address) { michael@0: JS_ASSERT(cond == Equal || cond == NotEqual); michael@0: cmpl(tagOf(address), ImmTag(JSVAL_TAG_STRING)); michael@0: return cond; michael@0: } michael@0: Condition testInt32(Condition cond, const BaseIndex &address) { michael@0: JS_ASSERT(cond == Equal || cond == NotEqual); michael@0: cmpl(tagOf(address), ImmTag(JSVAL_TAG_INT32)); michael@0: return cond; michael@0: } michael@0: Condition testObject(Condition cond, const BaseIndex &address) { michael@0: JS_ASSERT(cond == Equal || cond == NotEqual); michael@0: cmpl(tagOf(address), ImmTag(JSVAL_TAG_OBJECT)); michael@0: return cond; michael@0: } michael@0: Condition testDouble(Condition cond, const BaseIndex &address) { michael@0: JS_ASSERT(cond == Equal || cond == NotEqual); michael@0: Condition actual = (cond == Equal) ? Below : AboveOrEqual; michael@0: cmpl(tagOf(address), ImmTag(JSVAL_TAG_CLEAR)); michael@0: return actual; michael@0: } michael@0: Condition testMagic(Condition cond, const BaseIndex &address) { michael@0: JS_ASSERT(cond == Equal || cond == NotEqual); michael@0: cmpl(tagOf(address), ImmTag(JSVAL_TAG_MAGIC)); michael@0: return cond; michael@0: } michael@0: Condition testGCThing(Condition cond, const BaseIndex &address) { michael@0: JS_ASSERT(cond == Equal || cond == NotEqual); michael@0: cmpl(tagOf(address), ImmTag(JSVAL_LOWER_INCL_TAG_OF_GCTHING_SET)); michael@0: return cond == Equal ? AboveOrEqual : Below; michael@0: } michael@0: michael@0: michael@0: michael@0: void branchTestValue(Condition cond, const ValueOperand &value, const Value &v, Label *label); michael@0: void branchTestValue(Condition cond, const Address &valaddr, const ValueOperand &value, michael@0: Label *label) michael@0: { michael@0: JS_ASSERT(cond == Equal || cond == NotEqual); michael@0: // Check payload before tag, since payload is more likely to differ. michael@0: if (cond == NotEqual) { michael@0: branchPtr(NotEqual, payloadOf(valaddr), value.payloadReg(), label); michael@0: branchPtr(NotEqual, tagOf(valaddr), value.typeReg(), label); michael@0: michael@0: } else { michael@0: Label fallthrough; michael@0: branchPtr(NotEqual, payloadOf(valaddr), value.payloadReg(), &fallthrough); michael@0: branchPtr(Equal, tagOf(valaddr), value.typeReg(), label); michael@0: bind(&fallthrough); michael@0: } michael@0: } michael@0: michael@0: void testNullSet(Condition cond, const ValueOperand &value, Register dest) { michael@0: cond = testNull(cond, value); michael@0: emitSet(cond, dest); michael@0: } michael@0: void testUndefinedSet(Condition cond, const ValueOperand &value, Register dest) { michael@0: cond = testUndefined(cond, value); michael@0: emitSet(cond, dest); michael@0: } michael@0: michael@0: void cmpPtr(Register lhs, const ImmWord rhs) { michael@0: cmpl(lhs, Imm32(rhs.value)); michael@0: } michael@0: void cmpPtr(Register lhs, const ImmPtr imm) { michael@0: cmpPtr(lhs, ImmWord(uintptr_t(imm.value))); michael@0: } michael@0: void cmpPtr(Register lhs, const ImmGCPtr rhs) { michael@0: cmpl(lhs, rhs); michael@0: } michael@0: void cmpPtr(Register lhs, const Imm32 rhs) { michael@0: cmpl(lhs, rhs); michael@0: } michael@0: void cmpPtr(const Operand &lhs, const ImmWord rhs) { michael@0: cmpl(lhs, rhs); michael@0: } michael@0: void cmpPtr(const Operand &lhs, const ImmPtr imm) { michael@0: cmpPtr(lhs, ImmWord(uintptr_t(imm.value))); michael@0: } michael@0: void cmpPtr(const Operand &lhs, const ImmGCPtr rhs) { michael@0: cmpl(lhs, rhs); michael@0: } michael@0: void cmpPtr(const Operand &lhs, const Imm32 rhs) { michael@0: cmpl(lhs, rhs); michael@0: } michael@0: void cmpPtr(const Address &lhs, Register rhs) { michael@0: cmpl(Operand(lhs), rhs); michael@0: } michael@0: void cmpPtr(const Address &lhs, const ImmWord rhs) { michael@0: cmpl(Operand(lhs), rhs); michael@0: } michael@0: void cmpPtr(const Address &lhs, const ImmPtr rhs) { michael@0: cmpPtr(lhs, ImmWord(uintptr_t(rhs.value))); michael@0: } michael@0: void cmpPtr(Register lhs, Register rhs) { michael@0: cmpl(lhs, rhs); michael@0: } michael@0: void testPtr(Register lhs, Register rhs) { michael@0: testl(lhs, rhs); michael@0: } michael@0: michael@0: template michael@0: void cmpPtrSet(Assembler::Condition cond, T1 lhs, T2 rhs, const Register &dest) michael@0: { michael@0: cmpPtr(lhs, rhs); michael@0: emitSet(cond, dest); michael@0: } michael@0: michael@0: ///////////////////////////////////////////////////////////////// michael@0: // Common interface. michael@0: ///////////////////////////////////////////////////////////////// michael@0: void reserveStack(uint32_t amount) { michael@0: if (amount) michael@0: subl(Imm32(amount), StackPointer); michael@0: framePushed_ += amount; michael@0: } michael@0: void freeStack(uint32_t amount) { michael@0: JS_ASSERT(amount <= framePushed_); michael@0: if (amount) michael@0: addl(Imm32(amount), StackPointer); michael@0: framePushed_ -= amount; michael@0: } michael@0: void freeStack(Register amount) { michael@0: addl(amount, StackPointer); michael@0: } michael@0: michael@0: void addPtr(const Register &src, const Register &dest) { michael@0: addl(src, dest); michael@0: } michael@0: void addPtr(Imm32 imm, const Register &dest) { michael@0: addl(imm, dest); michael@0: } michael@0: void addPtr(ImmWord imm, const Register &dest) { michael@0: addl(Imm32(imm.value), dest); michael@0: } michael@0: void addPtr(ImmPtr imm, const Register &dest) { michael@0: addPtr(ImmWord(uintptr_t(imm.value)), dest); michael@0: } michael@0: void addPtr(Imm32 imm, const Address &dest) { michael@0: addl(imm, Operand(dest)); michael@0: } michael@0: void addPtr(Imm32 imm, const Operand &dest) { michael@0: addl(imm, dest); michael@0: } michael@0: void addPtr(const Address &src, const Register &dest) { michael@0: addl(Operand(src), dest); michael@0: } michael@0: void subPtr(Imm32 imm, const Register &dest) { michael@0: subl(imm, dest); michael@0: } michael@0: void subPtr(const Register &src, const Register &dest) { michael@0: subl(src, dest); michael@0: } michael@0: void subPtr(const Address &addr, const Register &dest) { michael@0: subl(Operand(addr), dest); michael@0: } michael@0: void subPtr(const Register &src, const Address &dest) { michael@0: subl(src, Operand(dest)); michael@0: } michael@0: michael@0: void branch32(Condition cond, const AbsoluteAddress &lhs, Imm32 rhs, Label *label) { michael@0: cmpl(Operand(lhs), rhs); michael@0: j(cond, label); michael@0: } michael@0: void branch32(Condition cond, const AbsoluteAddress &lhs, Register rhs, Label *label) { michael@0: cmpl(Operand(lhs), rhs); michael@0: j(cond, label); michael@0: } michael@0: michael@0: // Specialization for AsmJSAbsoluteAddress. michael@0: void branchPtr(Condition cond, AsmJSAbsoluteAddress lhs, Register ptr, Label *label) { michael@0: cmpl(lhs, ptr); michael@0: j(cond, label); michael@0: } michael@0: michael@0: template michael@0: void branchPtr(Condition cond, T lhs, S ptr, Label *label) { michael@0: cmpl(Operand(lhs), ptr); michael@0: j(cond, label); michael@0: } michael@0: michael@0: void branchPrivatePtr(Condition cond, const Address &lhs, ImmPtr ptr, Label *label) { michael@0: branchPtr(cond, lhs, ptr, label); michael@0: } michael@0: michael@0: void branchPrivatePtr(Condition cond, const Address &lhs, Register ptr, Label *label) { michael@0: branchPtr(cond, lhs, ptr, label); michael@0: } michael@0: michael@0: template michael@0: void branchPtr(Condition cond, T lhs, S ptr, RepatchLabel *label) { michael@0: cmpl(Operand(lhs), ptr); michael@0: j(cond, label); michael@0: } michael@0: michael@0: CodeOffsetJump jumpWithPatch(RepatchLabel *label) { michael@0: jump(label); michael@0: return CodeOffsetJump(size()); michael@0: } michael@0: michael@0: CodeOffsetJump jumpWithPatch(RepatchLabel *label, Assembler::Condition cond) { michael@0: j(cond, label); michael@0: return CodeOffsetJump(size()); michael@0: } michael@0: michael@0: template michael@0: CodeOffsetJump branchPtrWithPatch(Condition cond, S lhs, T ptr, RepatchLabel *label) { michael@0: branchPtr(cond, lhs, ptr, label); michael@0: return CodeOffsetJump(size()); michael@0: } michael@0: void branchPtr(Condition cond, Register lhs, Register rhs, RepatchLabel *label) { michael@0: cmpl(lhs, rhs); michael@0: j(cond, label); michael@0: } michael@0: void branchPtr(Condition cond, Register lhs, Register rhs, Label *label) { michael@0: cmpl(lhs, rhs); michael@0: j(cond, label); michael@0: } michael@0: void branchTestPtr(Condition cond, Register lhs, Register rhs, Label *label) { michael@0: testl(lhs, rhs); michael@0: j(cond, label); michael@0: } michael@0: void branchTestPtr(Condition cond, Register lhs, Imm32 imm, Label *label) { michael@0: testl(lhs, imm); michael@0: j(cond, label); michael@0: } michael@0: void branchTestPtr(Condition cond, const Address &lhs, Imm32 imm, Label *label) { michael@0: testl(Operand(lhs), imm); michael@0: j(cond, label); michael@0: } michael@0: void decBranchPtr(Condition cond, const Register &lhs, Imm32 imm, Label *label) { michael@0: subPtr(imm, lhs); michael@0: j(cond, label); michael@0: } michael@0: michael@0: void movePtr(ImmWord imm, Register dest) { michael@0: movl(Imm32(imm.value), dest); michael@0: } michael@0: void movePtr(ImmPtr imm, Register dest) { michael@0: movl(imm, dest); michael@0: } michael@0: void movePtr(AsmJSImmPtr imm, Register dest) { michael@0: mov(imm, dest); michael@0: } michael@0: void movePtr(ImmGCPtr imm, Register dest) { michael@0: movl(imm, dest); michael@0: } michael@0: void loadPtr(const Address &address, Register dest) { michael@0: movl(Operand(address), dest); michael@0: } michael@0: void loadPtr(const Operand &src, Register dest) { michael@0: movl(src, dest); michael@0: } michael@0: void loadPtr(const BaseIndex &src, Register dest) { michael@0: movl(Operand(src), dest); michael@0: } michael@0: void loadPtr(const AbsoluteAddress &address, Register dest) { michael@0: movl(Operand(address), dest); michael@0: } michael@0: void loadPrivate(const Address &src, Register dest) { michael@0: movl(payloadOf(src), dest); michael@0: } michael@0: void load32(const AbsoluteAddress &address, Register dest) { michael@0: movl(Operand(address), dest); michael@0: } michael@0: void storePtr(ImmWord imm, const Address &address) { michael@0: movl(Imm32(imm.value), Operand(address)); michael@0: } michael@0: void storePtr(ImmPtr imm, const Address &address) { michael@0: storePtr(ImmWord(uintptr_t(imm.value)), address); michael@0: } michael@0: void storePtr(ImmGCPtr imm, const Address &address) { michael@0: movl(imm, Operand(address)); michael@0: } michael@0: void storePtr(Register src, const Address &address) { michael@0: movl(src, Operand(address)); michael@0: } michael@0: void storePtr(Register src, const Operand &dest) { michael@0: movl(src, dest); michael@0: } michael@0: void storePtr(Register src, const AbsoluteAddress &address) { michael@0: movl(src, Operand(address)); michael@0: } michael@0: void store32(Register src, const AbsoluteAddress &address) { michael@0: movl(src, Operand(address)); michael@0: } michael@0: michael@0: void setStackArg(const Register ®, uint32_t arg) { michael@0: movl(reg, Operand(esp, arg * sizeof(intptr_t))); michael@0: } michael@0: michael@0: // Type testing instructions can take a tag in a register or a michael@0: // ValueOperand. michael@0: template michael@0: void branchTestUndefined(Condition cond, const T &t, Label *label) { michael@0: cond = testUndefined(cond, t); michael@0: j(cond, label); michael@0: } michael@0: template michael@0: void branchTestInt32(Condition cond, const T &t, Label *label) { michael@0: cond = testInt32(cond, t); michael@0: j(cond, label); michael@0: } michael@0: template michael@0: void branchTestBoolean(Condition cond, const T &t, Label *label) { michael@0: cond = testBoolean(cond, t); michael@0: j(cond, label); michael@0: } michael@0: template michael@0: void branchTestDouble(Condition cond, const T &t, Label *label) { michael@0: cond = testDouble(cond, t); michael@0: j(cond, label); michael@0: } michael@0: template michael@0: void branchTestNull(Condition cond, const T &t, Label *label) { michael@0: cond = testNull(cond, t); michael@0: j(cond, label); michael@0: } michael@0: template michael@0: void branchTestString(Condition cond, const T &t, Label *label) { michael@0: cond = testString(cond, t); michael@0: j(cond, label); michael@0: } michael@0: template michael@0: void branchTestObject(Condition cond, const T &t, Label *label) { michael@0: cond = testObject(cond, t); michael@0: j(cond, label); michael@0: } michael@0: template michael@0: void branchTestNumber(Condition cond, const T &t, Label *label) { michael@0: cond = testNumber(cond, t); michael@0: j(cond, label); michael@0: } michael@0: template michael@0: void branchTestGCThing(Condition cond, const T &t, Label *label) { michael@0: cond = testGCThing(cond, t); michael@0: j(cond, label); michael@0: } michael@0: template michael@0: void branchTestPrimitive(Condition cond, const T &t, Label *label) { michael@0: cond = testPrimitive(cond, t); michael@0: j(cond, label); michael@0: } michael@0: template michael@0: void branchTestMagic(Condition cond, const T &t, Label *label) { michael@0: cond = testMagic(cond, t); michael@0: j(cond, label); michael@0: } michael@0: void branchTestMagicValue(Condition cond, const ValueOperand &val, JSWhyMagic why, michael@0: Label *label) michael@0: { michael@0: JS_ASSERT(cond == Equal || cond == NotEqual); michael@0: if (cond == Equal) { michael@0: // Test for magic michael@0: Label notmagic; michael@0: Condition testCond = testMagic(Equal, val); michael@0: j(InvertCondition(testCond), ¬magic); michael@0: // Test magic value michael@0: branch32(Equal, val.payloadReg(), Imm32(static_cast(why)), label); michael@0: bind(¬magic); michael@0: } else { michael@0: Condition testCond = testMagic(NotEqual, val); michael@0: j(testCond, label); michael@0: branch32(NotEqual, val.payloadReg(), Imm32(static_cast(why)), label); michael@0: } michael@0: } michael@0: michael@0: // Note: this function clobbers the source register. michael@0: void boxDouble(const FloatRegister &src, const ValueOperand &dest) { michael@0: movd(src, dest.payloadReg()); michael@0: psrldq(Imm32(4), src); michael@0: movd(src, dest.typeReg()); michael@0: } michael@0: void boxNonDouble(JSValueType type, const Register &src, const ValueOperand &dest) { michael@0: if (src != dest.payloadReg()) michael@0: movl(src, dest.payloadReg()); michael@0: movl(ImmType(type), dest.typeReg()); michael@0: } michael@0: void unboxInt32(const ValueOperand &src, const Register &dest) { michael@0: movl(src.payloadReg(), dest); michael@0: } michael@0: void unboxInt32(const Address &src, const Register &dest) { michael@0: movl(payloadOf(src), dest); michael@0: } michael@0: void unboxDouble(const Address &src, const FloatRegister &dest) { michael@0: loadDouble(Operand(src), dest); michael@0: } michael@0: void unboxBoolean(const ValueOperand &src, const Register &dest) { michael@0: movl(src.payloadReg(), dest); michael@0: } michael@0: void unboxBoolean(const Address &src, const Register &dest) { michael@0: movl(payloadOf(src), dest); michael@0: } michael@0: void unboxObject(const ValueOperand &src, const Register &dest) { michael@0: if (src.payloadReg() != dest) michael@0: movl(src.payloadReg(), dest); michael@0: } michael@0: void unboxDouble(const ValueOperand &src, const FloatRegister &dest) { michael@0: JS_ASSERT(dest != ScratchFloatReg); michael@0: if (Assembler::HasSSE41()) { michael@0: movd(src.payloadReg(), dest); michael@0: pinsrd(src.typeReg(), dest); michael@0: } else { michael@0: movd(src.payloadReg(), dest); michael@0: movd(src.typeReg(), ScratchFloatReg); michael@0: unpcklps(ScratchFloatReg, dest); michael@0: } michael@0: } michael@0: void unboxDouble(const Operand &payload, const Operand &type, michael@0: const Register &scratch, const FloatRegister &dest) { michael@0: JS_ASSERT(dest != ScratchFloatReg); michael@0: if (Assembler::HasSSE41()) { michael@0: movl(payload, scratch); michael@0: movd(scratch, dest); michael@0: movl(type, scratch); michael@0: pinsrd(scratch, dest); michael@0: } else { michael@0: movl(payload, scratch); michael@0: movd(scratch, dest); michael@0: movl(type, scratch); michael@0: movd(scratch, ScratchFloatReg); michael@0: unpcklps(ScratchFloatReg, dest); michael@0: } michael@0: } michael@0: void unboxString(const ValueOperand &src, const Register &dest) { michael@0: movl(src.payloadReg(), dest); michael@0: } michael@0: void unboxString(const Address &src, const Register &dest) { michael@0: movl(payloadOf(src), dest); michael@0: } michael@0: void unboxValue(const ValueOperand &src, AnyRegister dest) { michael@0: if (dest.isFloat()) { michael@0: Label notInt32, end; michael@0: branchTestInt32(Assembler::NotEqual, src, ¬Int32); michael@0: convertInt32ToDouble(src.payloadReg(), dest.fpu()); michael@0: jump(&end); michael@0: bind(¬Int32); michael@0: unboxDouble(src, dest.fpu()); michael@0: bind(&end); michael@0: } else { michael@0: if (src.payloadReg() != dest.gpr()) michael@0: movl(src.payloadReg(), dest.gpr()); michael@0: } michael@0: } michael@0: void unboxPrivate(const ValueOperand &src, Register dest) { michael@0: if (src.payloadReg() != dest) michael@0: movl(src.payloadReg(), dest); michael@0: } michael@0: michael@0: void notBoolean(const ValueOperand &val) { michael@0: xorl(Imm32(1), val.payloadReg()); michael@0: } michael@0: michael@0: // Extended unboxing API. If the payload is already in a register, returns michael@0: // that register. Otherwise, provides a move to the given scratch register, michael@0: // and returns that. michael@0: Register extractObject(const Address &address, Register scratch) { michael@0: movl(payloadOf(address), scratch); michael@0: return scratch; michael@0: } michael@0: Register extractObject(const ValueOperand &value, Register scratch) { michael@0: return value.payloadReg(); michael@0: } michael@0: Register extractInt32(const ValueOperand &value, Register scratch) { michael@0: return value.payloadReg(); michael@0: } michael@0: Register extractBoolean(const ValueOperand &value, Register scratch) { michael@0: return value.payloadReg(); michael@0: } michael@0: Register extractTag(const Address &address, Register scratch) { michael@0: movl(tagOf(address), scratch); michael@0: return scratch; michael@0: } michael@0: Register extractTag(const ValueOperand &value, Register scratch) { michael@0: return value.typeReg(); michael@0: } michael@0: michael@0: void boolValueToDouble(const ValueOperand &operand, const FloatRegister &dest) { michael@0: convertInt32ToDouble(operand.payloadReg(), dest); michael@0: } michael@0: void boolValueToFloat32(const ValueOperand &operand, const FloatRegister &dest) { michael@0: convertInt32ToFloat32(operand.payloadReg(), dest); michael@0: } michael@0: void int32ValueToDouble(const ValueOperand &operand, const FloatRegister &dest) { michael@0: convertInt32ToDouble(operand.payloadReg(), dest); michael@0: } michael@0: void int32ValueToFloat32(const ValueOperand &operand, const FloatRegister &dest) { michael@0: convertInt32ToFloat32(operand.payloadReg(), dest); michael@0: } michael@0: michael@0: void loadConstantDouble(double d, const FloatRegister &dest); michael@0: void addConstantDouble(double d, const FloatRegister &dest); michael@0: void loadConstantFloat32(float f, const FloatRegister &dest); michael@0: void addConstantFloat32(float f, const FloatRegister &dest); michael@0: michael@0: void branchTruncateDouble(const FloatRegister &src, const Register &dest, Label *fail) { michael@0: cvttsd2si(src, dest); michael@0: michael@0: // cvttsd2si returns 0x80000000 on failure. Test for it by michael@0: // subtracting 1 and testing overflow (this permits the use of a michael@0: // smaller immediate field). michael@0: cmpl(dest, Imm32(1)); michael@0: j(Assembler::Overflow, fail); michael@0: } michael@0: void branchTruncateFloat32(const FloatRegister &src, const Register &dest, Label *fail) { michael@0: cvttss2si(src, dest); michael@0: michael@0: // cvttss2si returns 0x80000000 on failure. Test for it by michael@0: // subtracting 1 and testing overflow (this permits the use of a michael@0: // smaller immediate field). michael@0: cmpl(dest, Imm32(1)); michael@0: j(Assembler::Overflow, fail); michael@0: } michael@0: michael@0: Condition testInt32Truthy(bool truthy, const ValueOperand &operand) { michael@0: testl(operand.payloadReg(), operand.payloadReg()); michael@0: return truthy ? NonZero : Zero; michael@0: } michael@0: void branchTestInt32Truthy(bool truthy, const ValueOperand &operand, Label *label) { michael@0: Condition cond = testInt32Truthy(truthy, operand); michael@0: j(cond, label); michael@0: } michael@0: void branchTestBooleanTruthy(bool truthy, const ValueOperand &operand, Label *label) { michael@0: testl(operand.payloadReg(), operand.payloadReg()); michael@0: j(truthy ? NonZero : Zero, label); michael@0: } michael@0: Condition testStringTruthy(bool truthy, const ValueOperand &value) { michael@0: Register string = value.payloadReg(); michael@0: Operand lengthAndFlags(string, JSString::offsetOfLengthAndFlags()); michael@0: michael@0: size_t mask = (0xFFFFFFFF << JSString::LENGTH_SHIFT); michael@0: testl(lengthAndFlags, Imm32(mask)); michael@0: return truthy ? Assembler::NonZero : Assembler::Zero; michael@0: } michael@0: void branchTestStringTruthy(bool truthy, const ValueOperand &value, Label *label) { michael@0: Condition cond = testStringTruthy(truthy, value); michael@0: j(cond, label); michael@0: } michael@0: michael@0: void loadInt32OrDouble(const Operand &operand, const FloatRegister &dest) { michael@0: Label notInt32, end; michael@0: branchTestInt32(Assembler::NotEqual, operand, ¬Int32); michael@0: convertInt32ToDouble(ToPayload(operand), dest); michael@0: jump(&end); michael@0: bind(¬Int32); michael@0: loadDouble(operand, dest); michael@0: bind(&end); michael@0: } michael@0: michael@0: template michael@0: void loadUnboxedValue(const T &src, MIRType type, AnyRegister dest) { michael@0: if (dest.isFloat()) michael@0: loadInt32OrDouble(Operand(src), dest.fpu()); michael@0: else michael@0: movl(Operand(src), dest.gpr()); michael@0: } michael@0: michael@0: void rshiftPtr(Imm32 imm, Register dest) { michael@0: shrl(imm, dest); michael@0: } michael@0: void lshiftPtr(Imm32 imm, Register dest) { michael@0: shll(imm, dest); michael@0: } michael@0: void xorPtr(Imm32 imm, Register dest) { michael@0: xorl(imm, dest); michael@0: } michael@0: void xorPtr(Register src, Register dest) { michael@0: xorl(src, dest); michael@0: } michael@0: void orPtr(Imm32 imm, Register dest) { michael@0: orl(imm, dest); michael@0: } michael@0: void orPtr(Register src, Register dest) { michael@0: orl(src, dest); michael@0: } michael@0: void andPtr(Imm32 imm, Register dest) { michael@0: andl(imm, dest); michael@0: } michael@0: void andPtr(Register src, Register dest) { michael@0: andl(src, dest); michael@0: } michael@0: michael@0: void loadInstructionPointerAfterCall(const Register &dest) { michael@0: movl(Operand(StackPointer, 0x0), dest); michael@0: } michael@0: michael@0: // Note: this function clobbers the source register. michael@0: void convertUInt32ToDouble(const Register &src, const FloatRegister &dest) { michael@0: // src is [0, 2^32-1] michael@0: subl(Imm32(0x80000000), src); michael@0: michael@0: // Now src is [-2^31, 2^31-1] - int range, but not the same value. michael@0: convertInt32ToDouble(src, dest); michael@0: michael@0: // dest is now a double with the int range. michael@0: // correct the double value by adding 0x80000000. michael@0: addConstantDouble(2147483648.0, dest); michael@0: } michael@0: michael@0: // Note: this function clobbers the source register. michael@0: void convertUInt32ToFloat32(const Register &src, const FloatRegister &dest) { michael@0: convertUInt32ToDouble(src, dest); michael@0: convertDoubleToFloat32(dest, dest); michael@0: } michael@0: michael@0: void inc64(AbsoluteAddress dest) { michael@0: addl(Imm32(1), Operand(dest)); michael@0: Label noOverflow; michael@0: j(NonZero, &noOverflow); michael@0: addl(Imm32(1), Operand(dest.offset(4))); michael@0: bind(&noOverflow); michael@0: } michael@0: michael@0: michael@0: // If source is a double, load it into dest. If source is int32, michael@0: // convert it to double. Else, branch to failure. michael@0: void ensureDouble(const ValueOperand &source, FloatRegister dest, Label *failure) { michael@0: Label isDouble, done; michael@0: branchTestDouble(Assembler::Equal, source.typeReg(), &isDouble); michael@0: branchTestInt32(Assembler::NotEqual, source.typeReg(), failure); michael@0: michael@0: convertInt32ToDouble(source.payloadReg(), dest); michael@0: jump(&done); michael@0: michael@0: bind(&isDouble); michael@0: unboxDouble(source, dest); michael@0: michael@0: bind(&done); michael@0: } michael@0: michael@0: // Setup a call to C/C++ code, given the number of general arguments it michael@0: // takes. Note that this only supports cdecl. michael@0: // michael@0: // In order for alignment to work correctly, the MacroAssembler must have a michael@0: // consistent view of the stack displacement. It is okay to call "push" michael@0: // manually, however, if the stack alignment were to change, the macro michael@0: // assembler should be notified before starting a call. michael@0: void setupAlignedABICall(uint32_t args); michael@0: michael@0: // Sets up an ABI call for when the alignment is not known. This may need a michael@0: // scratch register. michael@0: void setupUnalignedABICall(uint32_t args, const Register &scratch); michael@0: michael@0: // Arguments must be assigned to a C/C++ call in order. They are moved michael@0: // in parallel immediately before performing the call. This process may michael@0: // temporarily use more stack, in which case esp-relative addresses will be michael@0: // automatically adjusted. It is extremely important that esp-relative michael@0: // addresses are computed *after* setupABICall(). Furthermore, no michael@0: // operations should be emitted while setting arguments. michael@0: void passABIArg(const MoveOperand &from, MoveOp::Type type); michael@0: void passABIArg(const Register ®); michael@0: void passABIArg(const FloatRegister ®, MoveOp::Type type); michael@0: michael@0: private: michael@0: void callWithABIPre(uint32_t *stackAdjust); michael@0: void callWithABIPost(uint32_t stackAdjust, MoveOp::Type result); michael@0: michael@0: public: michael@0: // Emits a call to a C/C++ function, resolving all argument moves. michael@0: void callWithABI(void *fun, MoveOp::Type result = MoveOp::GENERAL); michael@0: void callWithABI(AsmJSImmPtr fun, MoveOp::Type result = MoveOp::GENERAL); michael@0: void callWithABI(const Address &fun, MoveOp::Type result = MoveOp::GENERAL); michael@0: michael@0: // Used from within an Exit frame to handle a pending exception. michael@0: void handleFailureWithHandler(void *handler); michael@0: void handleFailureWithHandlerTail(); michael@0: michael@0: void makeFrameDescriptor(Register frameSizeReg, FrameType type) { michael@0: shll(Imm32(FRAMESIZE_SHIFT), frameSizeReg); michael@0: orl(Imm32(type), frameSizeReg); michael@0: } michael@0: michael@0: // Save an exit frame (which must be aligned to the stack pointer) to michael@0: // ThreadData::ionTop of the main thread. michael@0: void linkExitFrame() { michael@0: movl(StackPointer, Operand(AbsoluteAddress(GetIonContext()->runtime->addressOfIonTop()))); michael@0: } michael@0: michael@0: void callWithExitFrame(JitCode *target, Register dynStack) { michael@0: addPtr(Imm32(framePushed()), dynStack); michael@0: makeFrameDescriptor(dynStack, JitFrame_IonJS); michael@0: Push(dynStack); michael@0: call(target); michael@0: } michael@0: void call(const CallSiteDesc &desc, AsmJSImmPtr target) { michael@0: call(target); michael@0: appendCallSite(desc); michael@0: } michael@0: void callExit(AsmJSImmPtr target, uint32_t stackArgBytes) { michael@0: call(CallSiteDesc::Exit(), target); michael@0: } michael@0: michael@0: // Save an exit frame to the thread data of the current thread, given a michael@0: // register that holds a PerThreadData *. michael@0: void linkParallelExitFrame(const Register &pt) { michael@0: movl(StackPointer, Operand(pt, offsetof(PerThreadData, ionTop))); michael@0: } michael@0: michael@0: #ifdef JSGC_GENERATIONAL michael@0: void branchPtrInNurseryRange(Register ptr, Register temp, Label *label); michael@0: void branchValueIsNurseryObject(ValueOperand value, Register temp, Label *label); michael@0: #endif michael@0: }; michael@0: michael@0: typedef MacroAssemblerX86 MacroAssemblerSpecific; michael@0: michael@0: } // namespace jit michael@0: } // namespace js michael@0: michael@0: #endif /* jit_x86_MacroAssembler_x86_h */