/* * Copyright (C) 2011 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef ART_COMPILER_UTILS_ARM_ASSEMBLER_ARM_H_ #define ART_COMPILER_UTILS_ARM_ASSEMBLER_ARM_H_ #include #include "base/logging.h" #include "constants_arm.h" #include "utils/arm/managed_register_arm.h" #include "utils/assembler.h" #include "offsets.h" #include "utils.h" namespace art { namespace arm { // Encodes Addressing Mode 1 - Data-processing operands defined in Section 5.1. class ShifterOperand { public: // Data-processing operands - Uninitialized ShifterOperand() { type_ = -1; } // Data-processing operands - Immediate explicit ShifterOperand(uint32_t immediate) { CHECK(immediate < (1 << kImmed8Bits)); type_ = 1; encoding_ = immediate; } // Data-processing operands - Rotated immediate ShifterOperand(uint32_t rotate, uint32_t immed8) { CHECK((rotate < (1 << kRotateBits)) && (immed8 < (1 << kImmed8Bits))); type_ = 1; encoding_ = (rotate << kRotateShift) | (immed8 << kImmed8Shift); } // Data-processing operands - Register explicit ShifterOperand(Register rm) { type_ = 0; encoding_ = static_cast(rm); } // Data-processing operands - Logical shift/rotate by immediate ShifterOperand(Register rm, Shift shift, uint32_t shift_imm) { CHECK(shift_imm < (1 << kShiftImmBits)); type_ = 0; encoding_ = shift_imm << kShiftImmShift | static_cast(shift) << kShiftShift | static_cast(rm); } // Data-processing operands - Logical shift/rotate by register ShifterOperand(Register rm, Shift shift, Register rs) { type_ = 0; encoding_ = static_cast(rs) << kShiftRegisterShift | static_cast(shift) << kShiftShift | (1 << 4) | static_cast(rm); } static bool CanHold(uint32_t immediate, ShifterOperand* shifter_op) { // Avoid the more expensive test for frequent small immediate values. if (immediate < (1 << kImmed8Bits)) { shifter_op->type_ = 1; shifter_op->encoding_ = (0 << kRotateShift) | (immediate << kImmed8Shift); return true; } // Note that immediate must be unsigned for the test to work correctly. for (int rot = 0; rot < 16; rot++) { uint32_t imm8 = (immediate << 2*rot) | (immediate >> (32 - 2*rot)); if (imm8 < (1 << kImmed8Bits)) { shifter_op->type_ = 1; shifter_op->encoding_ = (rot << kRotateShift) | (imm8 << kImmed8Shift); return true; } } return false; } private: bool is_valid() const { return (type_ == 0) || (type_ == 1); } uint32_t type() const { CHECK(is_valid()); return type_; } uint32_t encoding() const { CHECK(is_valid()); return encoding_; } uint32_t type_; // Encodes the type field (bits 27-25) in the instruction. uint32_t encoding_; friend class ArmAssembler; #ifdef SOURCE_ASSEMBLER_SUPPORT friend class BinaryAssembler; #endif }; enum LoadOperandType { kLoadSignedByte, kLoadUnsignedByte, kLoadSignedHalfword, kLoadUnsignedHalfword, kLoadWord, kLoadWordPair, kLoadSWord, kLoadDWord }; enum StoreOperandType { kStoreByte, kStoreHalfword, kStoreWord, kStoreWordPair, kStoreSWord, kStoreDWord }; // Load/store multiple addressing mode. enum BlockAddressMode { // bit encoding P U W DA = (0|0|0) << 21, // decrement after IA = (0|4|0) << 21, // increment after DB = (8|0|0) << 21, // decrement before IB = (8|4|0) << 21, // increment before DA_W = (0|0|1) << 21, // decrement after with writeback to base IA_W = (0|4|1) << 21, // increment after with writeback to base DB_W = (8|0|1) << 21, // decrement before with writeback to base IB_W = (8|4|1) << 21 // increment before with writeback to base }; class Address { public: // Memory operand addressing mode enum Mode { // bit encoding P U W Offset = (8|4|0) << 21, // offset (w/o writeback to base) PreIndex = (8|4|1) << 21, // pre-indexed addressing with writeback PostIndex = (0|4|0) << 21, // post-indexed addressing with writeback NegOffset = (8|0|0) << 21, // negative offset (w/o writeback to base) NegPreIndex = (8|0|1) << 21, // negative pre-indexed with writeback NegPostIndex = (0|0|0) << 21 // negative post-indexed with writeback }; explicit Address(Register rn, int32_t offset = 0, Mode am = Offset) { CHECK(IsAbsoluteUint(12, offset)); if (offset < 0) { encoding_ = (am ^ (1 << kUShift)) | -offset; // Flip U to adjust sign. } else { encoding_ = am | offset; } encoding_ |= static_cast(rn) << kRnShift; } static bool CanHoldLoadOffset(LoadOperandType type, int offset); static bool CanHoldStoreOffset(StoreOperandType type, int offset); private: uint32_t encoding() const { return encoding_; } // Encoding for addressing mode 3. uint32_t encoding3() const { const uint32_t offset_mask = (1 << 12) - 1; uint32_t offset = encoding_ & offset_mask; CHECK_LT(offset, 256u); return (encoding_ & ~offset_mask) | ((offset & 0xf0) << 4) | (offset & 0xf); } // Encoding for vfp load/store addressing. uint32_t vencoding() const { const uint32_t offset_mask = (1 << 12) - 1; uint32_t offset = encoding_ & offset_mask; CHECK(IsAbsoluteUint(10, offset)); // In the range -1020 to +1020. CHECK_ALIGNED(offset, 2); // Multiple of 4. int mode = encoding_ & ((8|4|1) << 21); CHECK((mode == Offset) || (mode == NegOffset)); uint32_t vencoding = (encoding_ & (0xf << kRnShift)) | (offset >> 2); if (mode == Offset) { vencoding |= 1 << 23; } return vencoding; } uint32_t encoding_; friend class ArmAssembler; }; class ArmAssembler : public Assembler { public: ArmAssembler() {} virtual ~ArmAssembler() {} // Data-processing instructions. void and_(Register rd, Register rn, ShifterOperand so, Condition cond = AL); void eor(Register rd, Register rn, ShifterOperand so, Condition cond = AL); void sub(Register rd, Register rn, ShifterOperand so, Condition cond = AL); void subs(Register rd, Register rn, ShifterOperand so, Condition cond = AL); void rsb(Register rd, Register rn, ShifterOperand so, Condition cond = AL); void rsbs(Register rd, Register rn, ShifterOperand so, Condition cond = AL); void add(Register rd, Register rn, ShifterOperand so, Condition cond = AL); void adds(Register rd, Register rn, ShifterOperand so, Condition cond = AL); void adc(Register rd, Register rn, ShifterOperand so, Condition cond = AL); void sbc(Register rd, Register rn, ShifterOperand so, Condition cond = AL); void rsc(Register rd, Register rn, ShifterOperand so, Condition cond = AL); void tst(Register rn, ShifterOperand so, Condition cond = AL); void teq(Register rn, ShifterOperand so, Condition cond = AL); void cmp(Register rn, ShifterOperand so, Condition cond = AL); void cmn(Register rn, ShifterOperand so, Condition cond = AL); void orr(Register rd, Register rn, ShifterOperand so, Condition cond = AL); void orrs(Register rd, Register rn, ShifterOperand so, Condition cond = AL); void mov(Register rd, ShifterOperand so, Condition cond = AL); void movs(Register rd, ShifterOperand so, Condition cond = AL); void bic(Register rd, Register rn, ShifterOperand so, Condition cond = AL); void mvn(Register rd, ShifterOperand so, Condition cond = AL); void mvns(Register rd, ShifterOperand so, Condition cond = AL); // Miscellaneous data-processing instructions. void clz(Register rd, Register rm, Condition cond = AL); void movw(Register rd, uint16_t imm16, Condition cond = AL); void movt(Register rd, uint16_t imm16, Condition cond = AL); // Multiply instructions. void mul(Register rd, Register rn, Register rm, Condition cond = AL); void mla(Register rd, Register rn, Register rm, Register ra, Condition cond = AL); void mls(Register rd, Register rn, Register rm, Register ra, Condition cond = AL); void umull(Register rd_lo, Register rd_hi, Register rn, Register rm, Condition cond = AL); // Load/store instructions. void ldr(Register rd, Address ad, Condition cond = AL); void str(Register rd, Address ad, Condition cond = AL); void ldrb(Register rd, Address ad, Condition cond = AL); void strb(Register rd, Address ad, Condition cond = AL); void ldrh(Register rd, Address ad, Condition cond = AL); void strh(Register rd, Address ad, Condition cond = AL); void ldrsb(Register rd, Address ad, Condition cond = AL); void ldrsh(Register rd, Address ad, Condition cond = AL); void ldrd(Register rd, Address ad, Condition cond = AL); void strd(Register rd, Address ad, Condition cond = AL); void ldm(BlockAddressMode am, Register base, RegList regs, Condition cond = AL); void stm(BlockAddressMode am, Register base, RegList regs, Condition cond = AL); void ldrex(Register rd, Register rn, Condition cond = AL); void strex(Register rd, Register rt, Register rn, Condition cond = AL); // Miscellaneous instructions. void clrex(); void nop(Condition cond = AL); // Note that gdb sets breakpoints using the undefined instruction 0xe7f001f0. void bkpt(uint16_t imm16); void svc(uint32_t imm24); // Floating point instructions (VFPv3-D16 and VFPv3-D32 profiles). void vmovsr(SRegister sn, Register rt, Condition cond = AL); void vmovrs(Register rt, SRegister sn, Condition cond = AL); void vmovsrr(SRegister sm, Register rt, Register rt2, Condition cond = AL); void vmovrrs(Register rt, Register rt2, SRegister sm, Condition cond = AL); void vmovdrr(DRegister dm, Register rt, Register rt2, Condition cond = AL); void vmovrrd(Register rt, Register rt2, DRegister dm, Condition cond = AL); void vmovs(SRegister sd, SRegister sm, Condition cond = AL); void vmovd(DRegister dd, DRegister dm, Condition cond = AL); // Returns false if the immediate cannot be encoded. bool vmovs(SRegister sd, float s_imm, Condition cond = AL); bool vmovd(DRegister dd, double d_imm, Condition cond = AL); void vldrs(SRegister sd, Address ad, Condition cond = AL); void vstrs(SRegister sd, Address ad, Condition cond = AL); void vldrd(DRegister dd, Address ad, Condition cond = AL); void vstrd(DRegister dd, Address ad, Condition cond = AL); void vadds(SRegister sd, SRegister sn, SRegister sm, Condition cond = AL); void vaddd(DRegister dd, DRegister dn, DRegister dm, Condition cond = AL); void vsubs(SRegister sd, SRegister sn, SRegister sm, Condition cond = AL); void vsubd(DRegister dd, DRegister dn, DRegister dm, Condition cond = AL); void vmuls(SRegister sd, SRegister sn, SRegister sm, Condition cond = AL); void vmuld(DRegister dd, DRegister dn, DRegister dm, Condition cond = AL); void vmlas(SRegister sd, SRegister sn, SRegister sm, Condition cond = AL); void vmlad(DRegister dd, DRegister dn, DRegister dm, Condition cond = AL); void vmlss(SRegister sd, SRegister sn, SRegister sm, Condition cond = AL); void vmlsd(DRegister dd, DRegister dn, DRegister dm, Condition cond = AL); void vdivs(SRegister sd, SRegister sn, SRegister sm, Condition cond = AL); void vdivd(DRegister dd, DRegister dn, DRegister dm, Condition cond = AL); void vabss(SRegister sd, SRegister sm, Condition cond = AL); void vabsd(DRegister dd, DRegister dm, Condition cond = AL); void vnegs(SRegister sd, SRegister sm, Condition cond = AL); void vnegd(DRegister dd, DRegister dm, Condition cond = AL); void vsqrts(SRegister sd, SRegister sm, Condition cond = AL); void vsqrtd(DRegister dd, DRegister dm, Condition cond = AL); void vcvtsd(SRegister sd, DRegister dm, Condition cond = AL); void vcvtds(DRegister dd, SRegister sm, Condition cond = AL); void vcvtis(SRegister sd, SRegister sm, Condition cond = AL); void vcvtid(SRegister sd, DRegister dm, Condition cond = AL); void vcvtsi(SRegister sd, SRegister sm, Condition cond = AL); void vcvtdi(DRegister dd, SRegister sm, Condition cond = AL); void vcvtus(SRegister sd, SRegister sm, Condition cond = AL); void vcvtud(SRegister sd, DRegister dm, Condition cond = AL); void vcvtsu(SRegister sd, SRegister sm, Condition cond = AL); void vcvtdu(DRegister dd, SRegister sm, Condition cond = AL); void vcmps(SRegister sd, SRegister sm, Condition cond = AL); void vcmpd(DRegister dd, DRegister dm, Condition cond = AL); void vcmpsz(SRegister sd, Condition cond = AL); void vcmpdz(DRegister dd, Condition cond = AL); void vmstat(Condition cond = AL); // VMRS APSR_nzcv, FPSCR // Branch instructions. void b(Label* label, Condition cond = AL); void bl(Label* label, Condition cond = AL); void blx(Register rm, Condition cond = AL); void bx(Register rm, Condition cond = AL); // Macros. // Add signed constant value to rd. May clobber IP. void AddConstant(Register rd, int32_t value, Condition cond = AL); void AddConstant(Register rd, Register rn, int32_t value, Condition cond = AL); void AddConstantSetFlags(Register rd, Register rn, int32_t value, Condition cond = AL); void AddConstantWithCarry(Register rd, Register rn, int32_t value, Condition cond = AL); // Load and Store. May clobber IP. void LoadImmediate(Register rd, int32_t value, Condition cond = AL); void LoadSImmediate(SRegister sd, float value, Condition cond = AL); void LoadDImmediate(DRegister dd, double value, Register scratch, Condition cond = AL); void MarkExceptionHandler(Label* label); void LoadFromOffset(LoadOperandType type, Register reg, Register base, int32_t offset, Condition cond = AL); void StoreToOffset(StoreOperandType type, Register reg, Register base, int32_t offset, Condition cond = AL); void LoadSFromOffset(SRegister reg, Register base, int32_t offset, Condition cond = AL); void StoreSToOffset(SRegister reg, Register base, int32_t offset, Condition cond = AL); void LoadDFromOffset(DRegister reg, Register base, int32_t offset, Condition cond = AL); void StoreDToOffset(DRegister reg, Register base, int32_t offset, Condition cond = AL); void Push(Register rd, Condition cond = AL); void Pop(Register rd, Condition cond = AL); void PushList(RegList regs, Condition cond = AL); void PopList(RegList regs, Condition cond = AL); void Mov(Register rd, Register rm, Condition cond = AL); // Convenience shift instructions. Use mov instruction with shifter operand // for variants setting the status flags or using a register shift count. void Lsl(Register rd, Register rm, uint32_t shift_imm, Condition cond = AL); void Lsr(Register rd, Register rm, uint32_t shift_imm, Condition cond = AL); void Asr(Register rd, Register rm, uint32_t shift_imm, Condition cond = AL); void Ror(Register rd, Register rm, uint32_t shift_imm, Condition cond = AL); void Rrx(Register rd, Register rm, Condition cond = AL); // Encode a signed constant in tst instructions, only affecting the flags. void EncodeUint32InTstInstructions(uint32_t data); // ... and decode from a pc pointing to the start of encoding instructions. static uint32_t DecodeUint32FromTstInstructions(uword pc); static bool IsInstructionForExceptionHandling(uword pc); // Emit data (e.g. encoded instruction or immediate) to the // instruction stream. void Emit(int32_t value); void Bind(Label* label); // // Overridden common assembler high-level functionality // // Emit code that will create an activation on the stack virtual void BuildFrame(size_t frame_size, ManagedRegister method_reg, const std::vector& callee_save_regs, const std::vector& entry_spills); // Emit code that will remove an activation from the stack virtual void RemoveFrame(size_t frame_size, const std::vector& callee_save_regs); virtual void IncreaseFrameSize(size_t adjust); virtual void DecreaseFrameSize(size_t adjust); // Store routines virtual void Store(FrameOffset offs, ManagedRegister src, size_t size); virtual void StoreRef(FrameOffset dest, ManagedRegister src); virtual void StoreRawPtr(FrameOffset dest, ManagedRegister src); virtual void StoreImmediateToFrame(FrameOffset dest, uint32_t imm, ManagedRegister scratch); virtual void StoreImmediateToThread(ThreadOffset dest, uint32_t imm, ManagedRegister scratch); virtual void StoreStackOffsetToThread(ThreadOffset thr_offs, FrameOffset fr_offs, ManagedRegister scratch); virtual void StoreStackPointerToThread(ThreadOffset thr_offs); virtual void StoreSpanning(FrameOffset dest, ManagedRegister src, FrameOffset in_off, ManagedRegister scratch); // Load routines virtual void Load(ManagedRegister dest, FrameOffset src, size_t size); virtual void Load(ManagedRegister dest, ThreadOffset src, size_t size); virtual void LoadRef(ManagedRegister dest, FrameOffset src); virtual void LoadRef(ManagedRegister dest, ManagedRegister base, MemberOffset offs); virtual void LoadRawPtr(ManagedRegister dest, ManagedRegister base, Offset offs); virtual void LoadRawPtrFromThread(ManagedRegister dest, ThreadOffset offs); // Copying routines virtual void Move(ManagedRegister dest, ManagedRegister src, size_t size); virtual void CopyRawPtrFromThread(FrameOffset fr_offs, ThreadOffset thr_offs, ManagedRegister scratch); virtual void CopyRawPtrToThread(ThreadOffset thr_offs, FrameOffset fr_offs, ManagedRegister scratch); virtual void CopyRef(FrameOffset dest, FrameOffset src, ManagedRegister scratch); virtual void Copy(FrameOffset dest, FrameOffset src, ManagedRegister scratch, size_t size); virtual void Copy(FrameOffset dest, ManagedRegister src_base, Offset src_offset, ManagedRegister scratch, size_t size); virtual void Copy(ManagedRegister dest_base, Offset dest_offset, FrameOffset src, ManagedRegister scratch, size_t size); virtual void Copy(FrameOffset dest, FrameOffset src_base, Offset src_offset, ManagedRegister scratch, size_t size); virtual void Copy(ManagedRegister dest, Offset dest_offset, ManagedRegister src, Offset src_offset, ManagedRegister scratch, size_t size); virtual void Copy(FrameOffset dest, Offset dest_offset, FrameOffset src, Offset src_offset, ManagedRegister scratch, size_t size); virtual void MemoryBarrier(ManagedRegister scratch); // Sign extension virtual void SignExtend(ManagedRegister mreg, size_t size); // Zero extension virtual void ZeroExtend(ManagedRegister mreg, size_t size); // Exploit fast access in managed code to Thread::Current() virtual void GetCurrentThread(ManagedRegister tr); virtual void GetCurrentThread(FrameOffset dest_offset, ManagedRegister scratch); // Set up out_reg to hold a Object** into the SIRT, or to be NULL if the // value is null and null_allowed. in_reg holds a possibly stale reference // that can be used to avoid loading the SIRT entry to see if the value is // NULL. virtual void CreateSirtEntry(ManagedRegister out_reg, FrameOffset sirt_offset, ManagedRegister in_reg, bool null_allowed); // Set up out_off to hold a Object** into the SIRT, or to be NULL if the // value is null and null_allowed. virtual void CreateSirtEntry(FrameOffset out_off, FrameOffset sirt_offset, ManagedRegister scratch, bool null_allowed); // src holds a SIRT entry (Object**) load this into dst virtual void LoadReferenceFromSirt(ManagedRegister dst, ManagedRegister src); // Heap::VerifyObject on src. In some cases (such as a reference to this) we // know that src may not be null. virtual void VerifyObject(ManagedRegister src, bool could_be_null); virtual void VerifyObject(FrameOffset src, bool could_be_null); // Call to address held at [base+offset] virtual void Call(ManagedRegister base, Offset offset, ManagedRegister scratch); virtual void Call(FrameOffset base, Offset offset, ManagedRegister scratch); virtual void Call(ThreadOffset offset, ManagedRegister scratch); // Generate code to check if Thread::Current()->exception_ is non-null // and branch to a ExceptionSlowPath if it is. virtual void ExceptionPoll(ManagedRegister scratch, size_t stack_adjust); private: void EmitType01(Condition cond, int type, Opcode opcode, int set_cc, Register rn, Register rd, ShifterOperand so); void EmitType5(Condition cond, int offset, bool link); void EmitMemOp(Condition cond, bool load, bool byte, Register rd, Address ad); void EmitMemOpAddressMode3(Condition cond, int32_t mode, Register rd, Address ad); void EmitMultiMemOp(Condition cond, BlockAddressMode am, bool load, Register base, RegList regs); void EmitShiftImmediate(Condition cond, Shift opcode, Register rd, Register rm, ShifterOperand so); void EmitShiftRegister(Condition cond, Shift opcode, Register rd, Register rm, ShifterOperand so); void EmitMulOp(Condition cond, int32_t opcode, Register rd, Register rn, Register rm, Register rs); void EmitVFPsss(Condition cond, int32_t opcode, SRegister sd, SRegister sn, SRegister sm); void EmitVFPddd(Condition cond, int32_t opcode, DRegister dd, DRegister dn, DRegister dm); void EmitVFPsd(Condition cond, int32_t opcode, SRegister sd, DRegister dm); void EmitVFPds(Condition cond, int32_t opcode, DRegister dd, SRegister sm); void EmitBranch(Condition cond, Label* label, bool link); static int32_t EncodeBranchOffset(int offset, int32_t inst); static int DecodeBranchOffset(int32_t inst); int32_t EncodeTstOffset(int offset, int32_t inst); int DecodeTstOffset(int32_t inst); // Returns whether or not the given register is used for passing parameters. static int RegisterCompare(const Register* reg1, const Register* reg2) { return *reg1 - *reg2; } }; // Slowpath entered when Thread::Current()->_exception is non-null class ArmExceptionSlowPath : public SlowPath { public: explicit ArmExceptionSlowPath(ArmManagedRegister scratch, size_t stack_adjust) : scratch_(scratch), stack_adjust_(stack_adjust) { } virtual void Emit(Assembler *sp_asm); private: const ArmManagedRegister scratch_; const size_t stack_adjust_; }; } // namespace arm } // namespace art #endif // ART_COMPILER_UTILS_ARM_ASSEMBLER_ARM_H_