hphp/vixl/a64/macro-assembler-a64.h (931 lines of code) (raw):

// Copyright 2013, ARM Limited // All rights reserved. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are met: // // * Redistributions of source code must retain the above copyright notice, // this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above copyright notice, // this list of conditions and the following disclaimer in the documentation // and/or other materials provided with the distribution. // * Neither the name of ARM Limited nor the names of its contributors may be // used to endorse or promote products derived from this software without // specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS CONTRIBUTORS "AS IS" AND // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #ifndef VIXL_A64_MACRO_ASSEMBLER_A64_H_ #define VIXL_A64_MACRO_ASSEMBLER_A64_H_ #include "hphp/vixl/globals.h" #include "hphp/vixl/a64/assembler-a64.h" #include "hphp/vixl/a64/debugger-a64.h" #define LS_MACRO_LIST(V) \ V(Ldrb, Register&, rt, LDRB_w) \ V(Strb, Register&, rt, STRB_w) \ V(Ldrsb, Register&, rt, rt.Is64Bits() ? LDRSB_x : LDRSB_w) \ V(Ldrh, Register&, rt, LDRH_w) \ V(Strh, Register&, rt, STRH_w) \ V(Ldrsh, Register&, rt, rt.Is64Bits() ? LDRSH_x : LDRSH_w) \ V(Ldr, CPURegister&, rt, LoadOpFor(rt)) \ V(Str, CPURegister&, rt, StoreOpFor(rt)) \ V(Ldrsw, Register&, rt, LDRSW_x) namespace vixl { class MacroAssembler : public Assembler { public: explicit MacroAssembler(HPHP::CodeBlock& cb) : Assembler(cb), #ifndef NDEBUG allow_macro_instructions_(true), #endif sp_(sp), tmp0_(ip0), tmp1_(ip1), fptmp0_(d31) {} // Logical macros. void And(const Register& rd, const Register& rn, const Operand& operand, FlagsUpdate S = LeaveFlags); void Bic(const Register& rd, const Register& rn, const Operand& operand, FlagsUpdate S = LeaveFlags); void Orr(const Register& rd, const Register& rn, const Operand& operand); void Orn(const Register& rd, const Register& rn, const Operand& operand); void Eor(const Register& rd, const Register& rn, const Operand& operand); void Eon(const Register& rd, const Register& rn, const Operand& operand); void Tst(const Register& rn, const Operand& operand); void LogicalMacro(const Register& rd, const Register& rn, const Operand& operand, LogicalOp op); // Add and sub macros. void Add(const Register& rd, const Register& rn, const Operand& operand, FlagsUpdate S = LeaveFlags); void Sub(const Register& rd, const Register& rn, const Operand& operand, FlagsUpdate S = LeaveFlags); void Cmn(const Register& rn, const Operand& operand); void Cmp(const Register& rn, const Operand& operand); void Neg(const Register& rd, const Operand& operand, FlagsUpdate S = LeaveFlags); void AddSubMacro(const Register& rd, const Register& rn, const Operand& operand, FlagsUpdate S, AddSubOp op); // Add/sub with carry macros. void Adc(const Register& rd, const Register& rn, const Operand& operand, FlagsUpdate S = LeaveFlags); void Sbc(const Register& rd, const Register& rn, const Operand& operand, FlagsUpdate S = LeaveFlags); void Ngc(const Register& rd, const Operand& operand, FlagsUpdate S = LeaveFlags); void AddSubWithCarryMacro(const Register& rd, const Register& rn, const Operand& operand, FlagsUpdate S, AddSubWithCarryOp op); // Move macros. void Mov(const Register& rd, uint64_t imm); void Mov(const Register& rd, const Operand& operand); template<typename T> void Mov(const Register& rd, T* imm) { static_assert(sizeof(T*) == sizeof(uint64_t), ""); Mov(rd, reinterpret_cast<uint64_t>(imm)); } void Mvn(const Register& rd, uint64_t imm) { Mov(rd, ~imm); }; void Mvn(const Register& rd, const Operand& operand); bool IsImmMovn(uint64_t imm, unsigned reg_size); bool IsImmMovz(uint64_t imm, unsigned reg_size); // Conditional compare macros. void Ccmp(const Register& rn, const Operand& operand, StatusFlags nzcv, Condition cond); void Ccmn(const Register& rn, const Operand& operand, StatusFlags nzcv, Condition cond); void ConditionalCompareMacro(const Register& rn, const Operand& operand, StatusFlags nzcv, Condition cond, ConditionalCompareOp op); // Load/store macros. #define DECLARE_FUNCTION(FN, REGTYPE, REG, OP) \ void FN(const REGTYPE REG, const MemOperand& addr); LS_MACRO_LIST(DECLARE_FUNCTION) #undef DECLARE_FUNCTION void LoadStoreMacro(const CPURegister& rt, const MemOperand& addr, LoadStoreOp op); // Push or pop up to 4 registers of the same width to or from the stack, // using the current stack pointer as set by SetStackPointer. // // If an argument register is 'NoReg', all further arguments are also assumed // to be 'NoReg', and are thus not pushed or popped. // // Arguments are ordered such that "Push(a, b);" is functionally equivalent // to "Push(a); Push(b);". // // It is valid to push the same register more than once, and there is no // restriction on the order in which registers are specified. // // It is not valid to pop into the same register more than once in one // operation, not even into the zero register. // // If the current stack pointer (as set by SetStackPointer) is sp, then it // must be aligned to 16 bytes on entry and the total size of the specified // registers must also be a multiple of 16 bytes. // // Even if the current stack pointer is not the system stack pointer (sp), // Push (and derived methods) will still modify the system stack pointer in // order to comply with ABI rules about accessing memory below the system // stack pointer. // // Other than the registers passed into Pop, the stack pointer and (possibly) // the system stack pointer, these methods do not modify any other registers. // Scratch registers such as Tmp0() and Tmp1() are preserved. void Push(const CPURegister& src0, const CPURegister& src1 = NoReg, const CPURegister& src2 = NoReg, const CPURegister& src3 = NoReg); void Pop(const CPURegister& dst0, const CPURegister& dst1 = NoReg, const CPURegister& dst2 = NoReg, const CPURegister& dst3 = NoReg); // Alternative forms of Push and Pop, taking a RegList or CPURegList that // specifies the registers that are to be pushed or popped. Higher-numbered // registers are associated with higher memory addresses (as in the A32 push // and pop instructions). // // (Push|Pop)SizeRegList allow you to specify the register size as a // parameter. Only kXRegSize, kWRegSize, kDRegSize and kSRegSize are // supported. // // Otherwise, (Push|Pop)(CPU|X|W|D|S)RegList is preferred. void PushCPURegList(CPURegList registers); void PopCPURegList(CPURegList registers); void PushSizeRegList(RegList registers, unsigned reg_size, CPURegister::RegisterType type = CPURegister::kRegister) { PushCPURegList(CPURegList(type, reg_size, registers)); } void PopSizeRegList(RegList registers, unsigned reg_size, CPURegister::RegisterType type = CPURegister::kRegister) { PopCPURegList(CPURegList(type, reg_size, registers)); } void PushXRegList(RegList regs) { PushSizeRegList(regs, kXRegSize); } void PopXRegList(RegList regs) { PopSizeRegList(regs, kXRegSize); } void PushWRegList(RegList regs) { PushSizeRegList(regs, kWRegSize); } void PopWRegList(RegList regs) { PopSizeRegList(regs, kWRegSize); } inline void PushDRegList(RegList regs) { PushSizeRegList(regs, kDRegSize, CPURegister::kFPRegister); } inline void PopDRegList(RegList regs) { PopSizeRegList(regs, kDRegSize, CPURegister::kFPRegister); } inline void PushSRegList(RegList regs) { PushSizeRegList(regs, kSRegSize, CPURegister::kFPRegister); } inline void PopSRegList(RegList regs) { PopSizeRegList(regs, kSRegSize, CPURegister::kFPRegister); } // Push the specified register 'count' times. void PushMultipleTimes(int count, Register src); // Poke 'src' onto the stack. The offset is in bytes. // // If the current stack pointer (as set by SetStackPointer) is sp, then sp // must be aligned to 16 bytes. void Poke(const Register& src, const Operand& offset); // Peek at a value on the stack, and put it in 'dst'. The offset is in bytes. // // If the current stack pointer (as set by SetStackPointer) is sp, then sp // must be aligned to 16 bytes. void Peek(const Register& dst, const Operand& offset); // Claim or drop stack space without actually accessing memory. // // If the current stack pointer (as set by SetStackPointer) is sp, then it // must be aligned to 16 bytes and the size claimed or dropped must be a // multiple of 16 bytes. void Claim(const Operand& size); void Drop(const Operand& size); // Preserve the callee-saved registers (as defined by AAPCS64). // // Higher-numbered registers are pushed before lower-numbered registers, and // thus get higher addresses. // Floating-point registers are pushed before general-purpose registers, and // thus get higher addresses. // // This method must not be called unless StackPointer() is sp, and it is // aligned to 16 bytes. void PushCalleeSavedRegisters(); // Restore the callee-saved registers (as defined by AAPCS64). // // Higher-numbered registers are popped after lower-numbered registers, and // thus come from higher addresses. // Floating-point registers are popped after general-purpose registers, and // thus come from higher addresses. // // This method must not be called unless StackPointer() is sp, and it is // aligned to 16 bytes. void PopCalleeSavedRegisters(); // Remaining instructions are simple pass-through calls to the assembler. void Adr(const Register& rd, Label* label) { assert(allow_macro_instructions_); assert(!rd.IsZero()); adr(rd, label); } void Adrp(const Register& rd, Label* label) { assert(allow_macro_instructions_); assert(!rd.IsZero()); adrp(rd, label); } void Asr(const Register& rd, const Register& rn, unsigned shift) { assert(allow_macro_instructions_); assert(!rd.IsZero()); assert(!rn.IsZero()); asr(rd, rn, shift); } void Asr(const Register& rd, const Register& rn, const Register& rm) { assert(allow_macro_instructions_); assert(!rd.IsZero()); assert(!rn.IsZero()); assert(!rm.IsZero()); asrv(rd, rn, rm); } void B(Label* label) { b(label); } void B(Label* label, Condition cond) { assert(allow_macro_instructions_); assert((cond != al) && (cond != nv)); b(label, cond); } void Bfi(const Register& rd, const Register& rn, unsigned lsb, unsigned width) { assert(allow_macro_instructions_); assert(!rd.IsZero()); assert(!rn.IsZero()); bfi(rd, rn, lsb, width); } void Bfxil(const Register& rd, const Register& rn, unsigned lsb, unsigned width) { assert(allow_macro_instructions_); assert(!rd.IsZero()); assert(!rn.IsZero()); bfxil(rd, rn, lsb, width); } void Bind(Label* label) { assert(allow_macro_instructions_); bind(label); } void Bl(Label* label) { assert(allow_macro_instructions_); bl(label); } void Blr(const Register& xn) { assert(allow_macro_instructions_); assert(!xn.IsZero()); blr(xn); } void Br(const Register& xn) { assert(allow_macro_instructions_); assert(!xn.IsZero()); br(xn); } void Brk(int code = 0) { assert(allow_macro_instructions_); brk(code); } void Cbnz(const Register& rt, Label* label) { assert(allow_macro_instructions_); assert(!rt.IsZero()); cbnz(rt, label); } void Cbz(const Register& rt, Label* label) { assert(allow_macro_instructions_); assert(!rt.IsZero()); cbz(rt, label); } void Cinc(const Register& rd, const Register& rn, Condition cond) { assert(allow_macro_instructions_); assert(!rd.IsZero()); assert(!rn.IsZero()); cinc(rd, rn, cond); } void Cinv(const Register& rd, const Register& rn, Condition cond) { assert(allow_macro_instructions_); assert(!rd.IsZero()); assert(!rn.IsZero()); cinv(rd, rn, cond); } void Cls(const Register& rd, const Register& rn) { assert(allow_macro_instructions_); assert(!rd.IsZero()); assert(!rn.IsZero()); cls(rd, rn); } void Clz(const Register& rd, const Register& rn) { assert(allow_macro_instructions_); assert(!rd.IsZero()); assert(!rn.IsZero()); clz(rd, rn); } void Cneg(const Register& rd, const Register& rn, Condition cond) { assert(allow_macro_instructions_); assert(!rd.IsZero()); assert(!rn.IsZero()); cneg(rd, rn, cond); } void Csel(const Register& rd, const Register& rn, const Register& rm, Condition cond) { assert(allow_macro_instructions_); assert(!rd.IsZero()); assert(!rn.IsZero()); assert((cond != al) && (cond != nv)); csel(rd, rn, rm, cond); } void Cset(const Register& rd, Condition cond) { assert(allow_macro_instructions_); assert(!rd.IsZero()); cset(rd, cond); } void Csetm(const Register& rd, Condition cond) { assert(allow_macro_instructions_); assert(!rd.IsZero()); csetm(rd, cond); } void Csinc(const Register& rd, const Register& rn, const Register& rm, Condition cond) { assert(allow_macro_instructions_); assert(!rd.IsZero()); assert((cond != al) && (cond != nv)); csinc(rd, rn, rm, cond); } void Csinv(const Register& rd, const Register& rn, const Register& rm, Condition cond) { assert(allow_macro_instructions_); assert(!rd.IsZero()); assert(!rn.IsZero()); assert(!rm.IsZero()); assert((cond != al) && (cond != nv)); csinv(rd, rn, rm, cond); } void Csneg(const Register& rd, const Register& rn, const Register& rm, Condition cond) { assert(allow_macro_instructions_); assert(!rd.IsZero()); assert(!rn.IsZero()); assert(!rm.IsZero()); assert((cond != al) && (cond != nv)); csneg(rd, rn, rm, cond); } void Extr(const Register& rd, const Register& rn, const Register& rm, unsigned lsb) { assert(allow_macro_instructions_); assert(!rd.IsZero()); assert(!rn.IsZero()); assert(!rm.IsZero()); extr(rd, rn, rm, lsb); } void Fabs(const FPRegister& fd, const FPRegister& fn) { assert(allow_macro_instructions_); fabs(fd, fn); } void Fadd(const FPRegister& fd, const FPRegister& fn, const FPRegister& fm) { assert(allow_macro_instructions_); fadd(fd, fn, fm); } void Fccmp(const FPRegister& fn, const FPRegister& fm, StatusFlags nzcv, Condition cond) { assert(allow_macro_instructions_); assert((cond != al) && (cond != nv)); fccmp(fn, fm, nzcv, cond); } void Fcmp(const FPRegister& fn, const FPRegister& fm) { assert(allow_macro_instructions_); fcmp(fn, fm); } void Fcmp(const FPRegister& fn, double value) { assert(allow_macro_instructions_); if (value != 0.0) { FPRegister tmp = AppropriateTempFor(fn); Fmov(tmp, value); fcmp(fn, tmp); } else { fcmp(fn, value); } } void Fcsel(const FPRegister& fd, const FPRegister& fn, const FPRegister& fm, Condition cond) { assert(allow_macro_instructions_); assert((cond != al) && (cond != nv)); fcsel(fd, fn, fm, cond); } void Fcvt(const FPRegister& fd, const FPRegister& fn) { assert(allow_macro_instructions_); fcvt(fd, fn); } void Fcvtms(const Register& rd, const FPRegister& fn) { assert(allow_macro_instructions_); assert(!rd.IsZero()); fcvtms(rd, fn); } void Fcvtmu(const Register& rd, const FPRegister& fn) { assert(allow_macro_instructions_); assert(!rd.IsZero()); fcvtmu(rd, fn); } void Fcvtns(const Register& rd, const FPRegister& fn) { assert(allow_macro_instructions_); assert(!rd.IsZero()); fcvtns(rd, fn); } void Fcvtnu(const Register& rd, const FPRegister& fn) { assert(allow_macro_instructions_); assert(!rd.IsZero()); fcvtnu(rd, fn); } void Fcvtzs(const Register& rd, const FPRegister& fn) { assert(allow_macro_instructions_); assert(!rd.IsZero()); fcvtzs(rd, fn); } void Fcvtzu(const Register& rd, const FPRegister& fn) { assert(allow_macro_instructions_); assert(!rd.IsZero()); fcvtzu(rd, fn); } void Fdiv(const FPRegister& fd, const FPRegister& fn, const FPRegister& fm) { assert(allow_macro_instructions_); fdiv(fd, fn, fm); } void Fmax(const FPRegister& fd, const FPRegister& fn, const FPRegister& fm) { assert(allow_macro_instructions_); fmax(fd, fn, fm); } void Fmin(const FPRegister& fd, const FPRegister& fn, const FPRegister& fm) { assert(allow_macro_instructions_); fmin(fd, fn, fm); } void Fmov(FPRegister fd, FPRegister fn) { assert(allow_macro_instructions_); // Only emit an instruction if fd and fn are different, and they are both D // registers. fmov(s0, s0) is not a no-op because it clears the top word of // d0. Technically, fmov(d0, d0) is not a no-op either because it clears // the top of q0, but FPRegister does not currently support Q registers. if (!fd.Is(fn) || !fd.Is64Bits()) { fmov(fd, fn); } } void Fmov(FPRegister fd, Register rn) { assert(allow_macro_instructions_); fmov(fd, rn); } void Fmov(FPRegister fd, double imm) { assert(allow_macro_instructions_); fmov(fd, imm); } void Fmov(Register rd, FPRegister fn) { assert(allow_macro_instructions_); assert(!rd.IsZero()); fmov(rd, fn); } void Fmul(const FPRegister& fd, const FPRegister& fn, const FPRegister& fm) { assert(allow_macro_instructions_); fmul(fd, fn, fm); } void Fmsub(const FPRegister& fd, const FPRegister& fn, const FPRegister& fm, const FPRegister& fa) { assert(allow_macro_instructions_); fmsub(fd, fn, fm, fa); } void Fneg(const FPRegister& fd, const FPRegister& fn) { assert(allow_macro_instructions_); fneg(fd, fn); } void Frintn(const FPRegister& fd, const FPRegister& fn) { assert(allow_macro_instructions_); frintn(fd, fn); } void Frintz(const FPRegister& fd, const FPRegister& fn) { assert(allow_macro_instructions_); frintz(fd, fn); } void Fsqrt(const FPRegister& fd, const FPRegister& fn) { assert(allow_macro_instructions_); fsqrt(fd, fn); } void Fsub(const FPRegister& fd, const FPRegister& fn, const FPRegister& fm) { assert(allow_macro_instructions_); fsub(fd, fn, fm); } void Hint(SystemHint code) { assert(allow_macro_instructions_); hint(code); } void Hlt(int code) { assert(allow_macro_instructions_); hlt(code); } void Ldnp(const CPURegister& rt, const CPURegister& rt2, const MemOperand& src) { assert(allow_macro_instructions_); ldnp(rt, rt2, src); } void Ldp(const CPURegister& rt, const CPURegister& rt2, const MemOperand& src) { assert(allow_macro_instructions_); ldp(rt, rt2, src); } void Ldpsw(const Register& rt, const Register& rt2, const MemOperand& src) { assert(allow_macro_instructions_); ldpsw(rt, rt2, src); } void Ldr(const FPRegister& ft, double imm) { assert(allow_macro_instructions_); ldr(ft, imm); } void Ldr(const Register& rt, uint64_t imm) { assert(allow_macro_instructions_); assert(!rt.IsZero()); ldr(rt, imm); } void Ldr(const Register& rt, Label* label) { ldr(rt, label); } void Lsl(const Register& rd, const Register& rn, unsigned shift) { assert(allow_macro_instructions_); assert(!rd.IsZero()); assert(!rn.IsZero()); lsl(rd, rn, shift); } void Lsl(const Register& rd, const Register& rn, const Register& rm) { assert(allow_macro_instructions_); assert(!rd.IsZero()); assert(!rn.IsZero()); assert(!rm.IsZero()); lslv(rd, rn, rm); } void Lsr(const Register& rd, const Register& rn, unsigned shift) { assert(allow_macro_instructions_); assert(!rd.IsZero()); assert(!rn.IsZero()); lsr(rd, rn, shift); } void Lsr(const Register& rd, const Register& rn, const Register& rm) { assert(allow_macro_instructions_); assert(!rd.IsZero()); assert(!rn.IsZero()); assert(!rm.IsZero()); lsrv(rd, rn, rm); } void Madd(const Register& rd, const Register& rn, const Register& rm, const Register& ra) { assert(allow_macro_instructions_); assert(!rd.IsZero()); assert(!rn.IsZero()); assert(!rm.IsZero()); assert(!ra.IsZero()); madd(rd, rn, rm, ra); } void Mneg(const Register& rd, const Register& rn, const Register& rm) { assert(allow_macro_instructions_); assert(!rd.IsZero()); assert(!rn.IsZero()); assert(!rm.IsZero()); mneg(rd, rn, rm); } void Mov(const Register& rd, const Register& rn) { assert(allow_macro_instructions_); mov(rd, rn); } void Mrs(const Register& rt, SystemRegister sysreg) { assert(allow_macro_instructions_); assert(!rt.IsZero()); mrs(rt, sysreg); } void Msr(SystemRegister sysreg, const Register& rt) { assert(allow_macro_instructions_); assert(!rt.IsZero()); msr(sysreg, rt); } void Msub(const Register& rd, const Register& rn, const Register& rm, const Register& ra) { assert(allow_macro_instructions_); assert(!rd.IsZero()); assert(!rn.IsZero()); assert(!rm.IsZero()); assert(!ra.IsZero()); msub(rd, rn, rm, ra); } void Mul(const Register& rd, const Register& rn, const Register& rm) { assert(allow_macro_instructions_); assert(!rd.IsZero()); assert(!rn.IsZero()); assert(!rm.IsZero()); mul(rd, rn, rm); } void Nop() { assert(allow_macro_instructions_); nop(); } void Rbit(const Register& rd, const Register& rn) { assert(allow_macro_instructions_); assert(!rd.IsZero()); assert(!rn.IsZero()); rbit(rd, rn); } void Ret(const Register& xn = lr) { assert(allow_macro_instructions_); assert(!xn.IsZero()); ret(xn); } void Rev(const Register& rd, const Register& rn) { assert(allow_macro_instructions_); assert(!rd.IsZero()); assert(!rn.IsZero()); rev(rd, rn); } void Rev16(const Register& rd, const Register& rn) { assert(allow_macro_instructions_); assert(!rd.IsZero()); assert(!rn.IsZero()); rev16(rd, rn); } void Rev32(const Register& rd, const Register& rn) { assert(allow_macro_instructions_); assert(!rd.IsZero()); assert(!rn.IsZero()); rev32(rd, rn); } void Ror(const Register& rd, const Register& rs, unsigned shift) { assert(allow_macro_instructions_); assert(!rd.IsZero()); assert(!rs.IsZero()); ror(rd, rs, shift); } void Ror(const Register& rd, const Register& rn, const Register& rm) { assert(allow_macro_instructions_); assert(!rd.IsZero()); assert(!rn.IsZero()); assert(!rm.IsZero()); rorv(rd, rn, rm); } void Sbfiz(const Register& rd, const Register& rn, unsigned lsb, unsigned width) { assert(allow_macro_instructions_); assert(!rd.IsZero()); assert(!rn.IsZero()); sbfiz(rd, rn, lsb, width); } void Sbfx(const Register& rd, const Register& rn, unsigned lsb, unsigned width) { assert(allow_macro_instructions_); assert(!rd.IsZero()); assert(!rn.IsZero()); sbfx(rd, rn, lsb, width); } void Scvtf(const FPRegister& fd, const Register& rn, unsigned fbits = 0) { assert(allow_macro_instructions_); assert(!rn.IsZero()); scvtf(fd, rn, fbits); } void Sdiv(const Register& rd, const Register& rn, const Register& rm) { assert(allow_macro_instructions_); assert(!rd.IsZero()); assert(!rn.IsZero()); assert(!rm.IsZero()); sdiv(rd, rn, rm); } void Smaddl(const Register& rd, const Register& rn, const Register& rm, const Register& ra) { assert(allow_macro_instructions_); assert(!rd.IsZero()); assert(!rn.IsZero()); assert(!rm.IsZero()); assert(!ra.IsZero()); smaddl(rd, rn, rm, ra); } void Smsubl(const Register& rd, const Register& rn, const Register& rm, const Register& ra) { assert(allow_macro_instructions_); assert(!rd.IsZero()); assert(!rn.IsZero()); assert(!rm.IsZero()); assert(!ra.IsZero()); smsubl(rd, rn, rm, ra); } void Smull(const Register& rd, const Register& rn, const Register& rm) { assert(allow_macro_instructions_); assert(!rd.IsZero()); assert(!rn.IsZero()); assert(!rm.IsZero()); smull(rd, rn, rm); } void Smulh(const Register& xd, const Register& xn, const Register& xm) { assert(allow_macro_instructions_); assert(!xd.IsZero()); assert(!xn.IsZero()); assert(!xm.IsZero()); smulh(xd, xn, xm); } void Stnp(const CPURegister& rt, const CPURegister& rt2, const MemOperand& dst) { assert(allow_macro_instructions_); stnp(rt, rt2, dst); } void Stp(const CPURegister& rt, const CPURegister& rt2, const MemOperand& dst) { assert(allow_macro_instructions_); stp(rt, rt2, dst); } void Sxtb(const Register& rd, const Register& rn) { assert(allow_macro_instructions_); assert(!rd.IsZero()); assert(!rn.IsZero()); sxtb(rd, rn); } void Sxth(const Register& rd, const Register& rn) { assert(allow_macro_instructions_); assert(!rd.IsZero()); assert(!rn.IsZero()); sxth(rd, rn); } void Sxtw(const Register& rd, const Register& rn) { assert(allow_macro_instructions_); assert(!rd.IsZero()); assert(!rn.IsZero()); sxtw(rd, rn); } void Tbnz(const Register& rt, unsigned bit_pos, Label* label) { assert(allow_macro_instructions_); assert(!rt.IsZero()); tbnz(rt, bit_pos, label); } void Tbz(const Register& rt, unsigned bit_pos, Label* label) { assert(allow_macro_instructions_); assert(!rt.IsZero()); tbz(rt, bit_pos, label); } void Ubfiz(const Register& rd, const Register& rn, unsigned lsb, unsigned width) { assert(allow_macro_instructions_); assert(!rd.IsZero()); assert(!rn.IsZero()); ubfiz(rd, rn, lsb, width); } void Ubfx(const Register& rd, const Register& rn, unsigned lsb, unsigned width) { assert(allow_macro_instructions_); assert(!rd.IsZero()); assert(!rn.IsZero()); ubfx(rd, rn, lsb, width); } void Ucvtf(const FPRegister& fd, const Register& rn, unsigned fbits = 0) { assert(allow_macro_instructions_); assert(!rn.IsZero()); ucvtf(fd, rn, fbits); } void Udiv(const Register& rd, const Register& rn, const Register& rm) { assert(allow_macro_instructions_); assert(!rd.IsZero()); assert(!rn.IsZero()); assert(!rm.IsZero()); udiv(rd, rn, rm); } void Umaddl(const Register& rd, const Register& rn, const Register& rm, const Register& ra) { assert(allow_macro_instructions_); assert(!rd.IsZero()); assert(!rn.IsZero()); assert(!rm.IsZero()); assert(!ra.IsZero()); umaddl(rd, rn, rm, ra); } void Umsubl(const Register& rd, const Register& rn, const Register& rm, const Register& ra) { assert(allow_macro_instructions_); assert(!rd.IsZero()); assert(!rn.IsZero()); assert(!rm.IsZero()); assert(!ra.IsZero()); umsubl(rd, rn, rm, ra); } void Unreachable() { assert(allow_macro_instructions_); #ifdef USE_SIMULATOR hlt(kUnreachableOpcode); #else // Branch to 0 to generate a segfault. // lr - kInstructionSize is the address of the offending instruction. blr(xzr); #endif } void Uxtb(const Register& rd, const Register& rn) { assert(allow_macro_instructions_); assert(!rd.IsZero()); assert(!rn.IsZero()); uxtb(rd, rn); } void Uxth(const Register& rd, const Register& rn) { assert(allow_macro_instructions_); assert(!rd.IsZero()); assert(!rn.IsZero()); uxth(rd, rn); } void Uxtw(const Register& rd, const Register& rn) { assert(allow_macro_instructions_); assert(!rd.IsZero()); assert(!rn.IsZero()); uxtw(rd, rn); } // Push the system stack pointer (sp) down to allow the same to be done to // the current stack pointer (according to StackPointer()). This must be // called _before_ accessing the memory. // // This is necessary when pushing or otherwise adding things to the stack, to // satisfy the AAPCS64 constraint that the memory below the system stack // pointer is not accessed. // // This method asserts that StackPointer() is not sp, since the call does // not make sense in that context. // // TODO: This method can only accept values of 'space' that can be encoded in // one instruction. Refer to the implementation for details. void BumpSystemStackPointer(const Operand& space); #ifndef NDEBUG void SetAllowMacroInstructions(bool value) { allow_macro_instructions_ = value; } bool AllowMacroInstructions() const { return allow_macro_instructions_; } #endif // Set the current stack pointer, but don't generate any code. // Note that this does not directly affect LastStackPointer(). void SetStackPointer(const Register& stack_pointer) { assert(!AreAliased(stack_pointer, Tmp0(), Tmp1())); sp_ = stack_pointer; } // Return the current stack pointer, as set by SetStackPointer. const Register& StackPointer() const { return sp_; } // Set the registers used internally by the MacroAssembler as scratch // registers. These registers are used to implement behaviours which are not // directly supported by A64, and where an intermediate result is required. // // Both tmp0 and tmp1 may be set to any X register except for xzr, sp, // and StackPointer(). Also, they must not be the same register (though they // may both be NoReg). // // It is valid to set either or both of these registers to NoReg if you don't // want the MacroAssembler to use any scratch registers. In a debug build, the // Assembler will assert that any registers it uses are valid. Be aware that // this check is not present in release builds. If this is a problem, use the // Assembler directly. void SetScratchRegisters(const Register& tmp0, const Register& tmp1) { assert(!AreAliased(xzr, sp, tmp0, tmp1)); assert(!AreAliased(StackPointer(), tmp0, tmp1)); tmp0_ = tmp0; tmp1_ = tmp1; } const Register& Tmp0() const { return tmp0_; } const Register& Tmp1() const { return tmp1_; } void SetFPScratchRegister(const FPRegister& fptmp0) { fptmp0_ = fptmp0; } const FPRegister& FPTmp0() const { return fptmp0_; } const Register AppropriateTempFor( const Register& target, const CPURegister& forbidden = NoCPUReg) const { Register candidate = forbidden.Is(Tmp0()) ? Tmp1() : Tmp0(); assert(!candidate.Is(target)); return Register(candidate.code(), target.size()); } const FPRegister AppropriateTempFor( const FPRegister& target, const CPURegister& forbidden = NoCPUReg) const { USE(forbidden); FPRegister candidate = FPTmp0(); assert(!candidate.Is(forbidden)); assert(!candidate.Is(target)); return FPRegister(candidate.code(), target.size()); } // Like printf, but print at run-time from generated code. // // The caller must ensure that arguments for floating-point placeholders // (such as %e, %f or %g) are FPRegisters, and that arguments for integer // placeholders are Registers. // // A maximum of four arguments may be given to any single Printf call. The // arguments must be of the same type, but they do not need to have the same // size. // // The following registers cannot be printed: // Tmp0(), Tmp1(), StackPointer(), sp. // // This function automatically preserves caller-saved registers so that // calling code can use Printf at any point without having to worry about // corruption. The preservation mechanism generates a lot of code. If this is // a problem, preserve the important registers manually and then call // PrintfNoPreserve. Callee-saved registers are not used by Printf, and are // implicitly preserved. // // This function assumes (and asserts) that the current stack pointer is // callee-saved, not caller-saved. This is most likely the case anyway, as a // caller-saved stack pointer doesn't make a lot of sense. void Printf(const char * format, const CPURegister& arg0 = NoCPUReg, const CPURegister& arg1 = NoCPUReg, const CPURegister& arg2 = NoCPUReg, const CPURegister& arg3 = NoCPUReg); // Like Printf, but don't preserve any caller-saved registers, not even 'lr'. // // The return code from the system printf call will be returned in x0. void PrintfNoPreserve(const char * format, const CPURegister& arg0 = NoCPUReg, const CPURegister& arg1 = NoCPUReg, const CPURegister& arg2 = NoCPUReg, const CPURegister& arg3 = NoCPUReg); // Trace control when running the debug simulator. // // For example: // // __ Trace(LOG_REGS, TRACE_ENABLE); // Will add registers to the trace if it wasn't already the case. // // __ Trace(LOG_DISASM, TRACE_DISABLE); // Will stop logging disassembly. It has no effect if the disassembly wasn't // already being logged. void Trace(TraceParameters parameters, TraceCommand command); // Log the requested data independently of what is being traced. // // For example: // // __ Log(LOG_FLAGS) // Will output the flags. void Log(TraceParameters parameters); // Enable or disable instrumentation when an Instrument visitor is attached to // the simulator. void EnableInstrumentation(); void DisableInstrumentation(); // Add a marker to the instrumentation data produced by an Instrument visitor. // The name is a two character string that will be attached to the marker in // the output data. void AnnotateInstrumentation(const char* marker_name); // Pseudo-instruction that will call a function made of host machine code // using host calling conventions. It will take the function address from x16 // (inter-procedural scratch reg). Arguments will be taken from simulated // registers in the usual sequence, and the return value will be put in // simulated x0. All caller-saved registers will be smashed. void HostCall(uint8_t argc); private: // The actual Push and Pop implementations. These don't generate any code // other than that required for the push or pop. This allows // (Push|Pop)CPURegList to bundle together setup code for a large block of // registers. // // Note that size is per register, and is specified in bytes. void PushHelper(int count, int size, const CPURegister& src0, const CPURegister& src1, const CPURegister& src2, const CPURegister& src3); void PopHelper(int count, int size, const CPURegister& dst0, const CPURegister& dst1, const CPURegister& dst2, const CPURegister& dst3); // Perform necessary maintenance operations before a push or pop. // // Note that size is per register, and is specified in bytes. void PrepareForPush(int count, int size); void PrepareForPop(int count, int size); #ifndef NDEBUG // Tell whether any of the macro instruction can be used. When false the // MacroAssembler will assert if a method which can emit a variable number // of instructions is called. bool allow_macro_instructions_; #endif // The register to use as a stack pointer for stack operations. Register sp_; // Scratch registers used internally by the MacroAssembler. Register tmp0_; Register tmp1_; FPRegister fptmp0_; }; // Use this scope when you need a one-to-one mapping between methods and // instructions. This scope prevents the MacroAssembler from being called and // literal pools from being emitted. It also asserts the number of instructions // emitted is what you specified when creating the scope. class InstructionAccurateScope { public: explicit InstructionAccurateScope(MacroAssembler* masm) : masm_(masm) { masm_->BlockLiteralPool(); #ifndef NDEBUG old_allow_macro_instructions_ = masm_->AllowMacroInstructions(); masm_->SetAllowMacroInstructions(false); #endif } InstructionAccurateScope(MacroAssembler* masm, int count) : masm_(masm) { masm_->BlockLiteralPool(); #ifndef NDEBUG size_ = count * kInstructionSize; masm_->bind(&start_); old_allow_macro_instructions_ = masm_->AllowMacroInstructions(); masm_->SetAllowMacroInstructions(false); #endif } ~InstructionAccurateScope() { masm_->ReleaseLiteralPool(); #ifndef NDEBUG if (start_.IsBound()) { assert(masm_->SizeOfCodeGeneratedSince(&start_) == size_); } masm_->SetAllowMacroInstructions(old_allow_macro_instructions_); #endif } private: MacroAssembler* masm_; #ifndef NDEBUG uint64_t size_{0}; Label start_; bool old_allow_macro_instructions_; #endif }; } // namespace vixl #endif // VIXL_A64_MACRO_ASSEMBLER_A64_H_