blob: 5b9c32de985581ed257592a9752d255035b19180 [file] [log] [blame]
/*
* 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_SRC_ASSEMBLER_ARM_H_
#define ART_SRC_ASSEMBLER_ARM_H_
#include "assembler.h"
#include "constants.h"
#include "managed_register_arm.h"
#include "logging.h"
#include "offsets.h"
#include "utils.h"
#include <vector>
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<uint32_t>(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<uint32_t>(shift) << kShiftShift |
static_cast<uint32_t>(rm);
}
// Data-processing operands - Logical shift/rotate by register
ShifterOperand(Register rm, Shift shift, Register rs) {
type_ = 0;
encoding_ = static_cast<uint32_t>(rs) << kShiftRegisterShift |
static_cast<uint32_t>(shift) << kShiftShift | (1 << 4) |
static_cast<uint32_t>(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<uint32_t>(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<ManagedRegister>& callee_save_regs);
// Emit code that will remove an activation from the stack
virtual void RemoveFrame(size_t frame_size,
const std::vector<ManagedRegister>& 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);
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);
// 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()->suspend_count_ is non-zero
// and branch to a SuspendSlowPath if it is. The SuspendSlowPath will continue
// at the next instruction.
virtual void SuspendPoll(ManagedRegister scratch, ManagedRegister return_reg,
FrameOffset return_save_location,
size_t return_size);
// Generate code to check if Thread::Current()->exception_ is non-null
// and branch to a ExceptionSlowPath if it is.
virtual void ExceptionPoll(ManagedRegister scratch);
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) : scratch_(scratch) {}
virtual void Emit(Assembler *sp_asm);
private:
const ArmManagedRegister scratch_;
};
// Slowpath entered when Thread::Current()->_suspend_count is non-zero
class ArmSuspendCountSlowPath : public SlowPath {
public:
ArmSuspendCountSlowPath(ArmManagedRegister return_reg,
FrameOffset return_save_location,
size_t return_size) :
return_register_(return_reg), return_save_location_(return_save_location),
return_size_(return_size) {}
virtual void Emit(Assembler *sp_asm);
private:
// Remember how to save the return value
const ArmManagedRegister return_register_;
const FrameOffset return_save_location_;
const size_t return_size_;
};
} // namespace arm
} // namespace art
#endif // ART_SRC_ASSEMBLER_ARM_H_