| Index: src/IceAssemblerARM32.cpp
|
| diff --git a/src/IceAssemblerARM32.cpp b/src/IceAssemblerARM32.cpp
|
| index 9972b44f29506f286618f931909fffdcca5422d9..c99737d81731c85ad26d29254c64ca200123d3ce 100644
|
| --- a/src/IceAssemblerARM32.cpp
|
| +++ b/src/IceAssemblerARM32.cpp
|
| @@ -21,6 +21,7 @@
|
| //===----------------------------------------------------------------------===//
|
|
|
| #include "IceAssemblerARM32.h"
|
| +#include "IceUtils.h"
|
|
|
| namespace {
|
|
|
| @@ -39,6 +40,14 @@ static constexpr uint32_t B24 = 1 << 24;
|
|
|
| // Constants used for the decoding or encoding of the individual fields of
|
| // instructions. Based on ARM section A5.1.
|
| +static constexpr uint32_t L = 1 << 20; // load (or store)
|
| +static constexpr uint32_t W = 1 << 21; // writeback base register (or leave
|
| + // unchanged)
|
| +static constexpr uint32_t B = 1 << 22; // unsigned byte (or word)
|
| +static constexpr uint32_t U = 1 << 23; // positive (or negative) offset/index
|
| +static constexpr uint32_t P = 1 << 24; // offset/pre-indexed addressing (or
|
| + // post-indexed addressing)
|
| +
|
| static constexpr uint32_t kConditionShift = 28;
|
| static constexpr uint32_t kOpcodeShift = 21;
|
| static constexpr uint32_t kRdShift = 12;
|
| @@ -53,6 +62,9 @@ static constexpr uint32_t kImmed8Shift = 0;
|
| static constexpr uint32_t kRotateBits = 4;
|
| static constexpr uint32_t kRotateShift = 8;
|
|
|
| +static constexpr uint32_t kImmed12Bits = 12;
|
| +static constexpr uint32_t kImm12Shift = 0;
|
| +
|
| inline uint32_t encodeBool(bool b) { return b ? 1 : 0; }
|
|
|
| inline uint32_t encodeGPRRegister(RegARM32::GPRRegister Rn) {
|
| @@ -75,22 +87,46 @@ inline uint32_t encodeCondition(CondARM32::Cond Cond) {
|
| return static_cast<uint32_t>(Cond);
|
| }
|
|
|
| -// The way an operand was decoded in function decode below.
|
| +// Returns the bits in the corresponding masked value.
|
| +inline uint32_t mask(uint32_t Value, uint32_t Shift, uint32_t Bits) {
|
| + return (Value >> Shift) & ((1 << Bits) - 1);
|
| +}
|
| +
|
| +// Extract out a Bit in Value.
|
| +inline bool isBitSet(uint32_t Bit, uint32_t Value) {
|
| + return (Value & Bit) == Bit;
|
| +}
|
| +
|
| +// Returns the GPR register at given Shift in Value.
|
| +inline RegARM32::GPRRegister getGPRReg(uint32_t Shift, uint32_t Value) {
|
| + return static_cast<RegARM32::GPRRegister>((Value >> Shift) & 0xF);
|
| +}
|
| +
|
| +// The way an operand was decoded in functions decodeOperand and decodeAddress
|
| +// below.
|
| enum DecodedResult {
|
| - CantDecode = 0, // I.e. will fail in test.
|
| + // Unable to decode, value left undefined.
|
| + CantDecode = 0,
|
| + // Value is register found.
|
| DecodedAsRegister,
|
| - DecodedAsRotatedImm8
|
| + // Value=rrrriiiiiiii where rrrr is the rotation, and iiiiiiii is the imm8
|
| + // value.
|
| + DecodedAsRotatedImm8,
|
| + // i.e. 0000000pu0w0nnnn0000iiiiiiiiiiii where nnnn is the base register Rn,
|
| + // p=1 if pre-indexed addressing, u=1 if offset positive, w=1 if writeback to
|
| + // Rn should be used, and iiiiiiiiiiii is the offset.
|
| + DecodedAsImmRegOffset
|
| };
|
|
|
| -DecodedResult decode(const Operand *Opnd, uint32_t &Value) {
|
| +DecodedResult decodeOperand(const Operand *Opnd, uint32_t &Value) {
|
| if (const auto *Var = llvm::dyn_cast<Variable>(Opnd)) {
|
| if (Var->hasReg()) {
|
| Value = Var->getRegNum();
|
| return DecodedAsRegister;
|
| }
|
| } else if (const auto *FlexImm = llvm::dyn_cast<OperandARM32FlexImm>(Opnd)) {
|
| - uint32_t Immed8 = FlexImm->getImm();
|
| - uint32_t Rotate = FlexImm->getRotateAmt();
|
| + const uint32_t Immed8 = FlexImm->getImm();
|
| + const uint32_t Rotate = FlexImm->getRotateAmt();
|
| assert((Rotate < (1 << kRotateBits)) && (Immed8 < (1 << kImmed8Bits)));
|
| Value = (Rotate << kRotateShift) | (Immed8 << kImmed8Shift);
|
| return DecodedAsRotatedImm8;
|
| @@ -98,6 +134,34 @@ DecodedResult decode(const Operand *Opnd, uint32_t &Value) {
|
| return CantDecode;
|
| }
|
|
|
| +uint32_t decodeImmRegOffset(RegARM32::GPRRegister Reg, int32_t Offset,
|
| + OperandARM32Mem::AddrMode Mode) {
|
| + uint32_t Value = Mode | (encodeGPRRegister(Reg) << kRnShift);
|
| + if (Offset < 0) {
|
| + Value = (Value ^ U) | -Offset; // Flip U to adjust sign.
|
| + } else {
|
| + Value |= Offset;
|
| + }
|
| + return Value;
|
| +}
|
| +
|
| +// Decodes memory address Opnd, and encodes that information into Value,
|
| +// based on how ARM represents the address. Returns how the value was encoded.
|
| +DecodedResult decodeAddress(const Operand *Opnd, uint32_t &Value) {
|
| + if (const auto *Var = llvm::dyn_cast<Variable>(Opnd)) {
|
| + // Should be a stack variable, with an offset.
|
| + if (Var->hasReg())
|
| + return CantDecode;
|
| + const int32_t Offset = Var->getStackOffset();
|
| + if (!Utils::IsAbsoluteUint(12, Offset))
|
| + return CantDecode;
|
| + Value = decodeImmRegOffset(RegARM32::Encoded_Reg_sp, Offset,
|
| + OperandARM32Mem::Offset);
|
| + return DecodedAsImmRegOffset;
|
| + }
|
| + return CantDecode;
|
| +}
|
| +
|
| } // end of anonymous namespace
|
|
|
| namespace Ice {
|
| @@ -140,10 +204,22 @@ void ARM32::AssemblerARM32::emitType01(CondARM32::Cond Cond, uint32_t Type,
|
| assert(isGPRRegisterDefined(Rd));
|
| assert(Cond != CondARM32::kNone);
|
| AssemblerBuffer::EnsureCapacity ensured(&Buffer);
|
| - uint32_t Encoding = encodeCondition(Cond) << kConditionShift |
|
| - (Type << kTypeShift) | (Opcode << kOpcodeShift) |
|
| - (encodeBool(SetCc) << kSShift) | (Rn << kRnShift) |
|
| - (Rd << kRdShift) | Imm12;
|
| + const uint32_t Encoding = (encodeCondition(Cond) << kConditionShift) |
|
| + (Type << kTypeShift) | (Opcode << kOpcodeShift) |
|
| + (encodeBool(SetCc) << kSShift) | (Rn << kRnShift) |
|
| + (Rd << kRdShift) | Imm12;
|
| + emitInst(Encoding);
|
| +}
|
| +
|
| +void ARM32::AssemblerARM32::emitMemOp(CondARM32::Cond Cond, uint32_t InstType,
|
| + bool IsLoad, bool IsByte, uint32_t Rt,
|
| + uint32_t Address) {
|
| + assert(isGPRRegisterDefined(Rt));
|
| + assert(Cond != CondARM32::kNone);
|
| + AssemblerBuffer::EnsureCapacity ensured(&Buffer);
|
| + const uint32_t Encoding = (encodeCondition(Cond) << kConditionShift) |
|
| + (InstType << kTypeShift) | (IsLoad ? L : 0) |
|
| + (IsByte ? B : 0) | (Rt << kRdShift) | Address;
|
| emitInst(Encoding);
|
| }
|
|
|
| @@ -153,14 +229,14 @@ void ARM32::AssemblerARM32::add(const Operand *OpRd, const Operand *OpRn,
|
| // Note: Loop is used so that we can short circuit using break;
|
| do {
|
| uint32_t Rd;
|
| - if (decode(OpRd, Rd) != DecodedAsRegister)
|
| + if (decodeOperand(OpRd, Rd) != DecodedAsRegister)
|
| break;
|
| uint32_t Rn;
|
| - if (decode(OpRn, Rn) != DecodedAsRegister)
|
| + if (decodeOperand(OpRn, Rn) != DecodedAsRegister)
|
| break;
|
| uint32_t Src1Value;
|
| // TODO(kschimpf) Other possible decodings of add.
|
| - if (decode(OpSrc1, Src1Value) == DecodedAsRotatedImm8) {
|
| + if (decodeOperand(OpSrc1, Src1Value) == DecodedAsRotatedImm8) {
|
| // ADD (Immediate): See ARM section A8.8.5, rule A1.
|
| // cccc0010100snnnnddddiiiiiiiiiiii where cccc=Cond, dddd=Rd, nnnn=Rn,
|
| // s=SetFlags and iiiiiiiiiiii=Src1Value
|
| @@ -168,8 +244,8 @@ void ARM32::AssemblerARM32::add(const Operand *OpRd, const Operand *OpRn,
|
| (Rn == RegARM32::Reg_lr) || (Rn == RegARM32::Reg_pc && SetFlags))
|
| // Conditions of rule violated.
|
| break;
|
| - uint32_t Add = B2; // 0100
|
| - uint32_t InstType = 1;
|
| + constexpr uint32_t Add = B2; // 0100
|
| + constexpr uint32_t InstType = 1;
|
| emitType01(Cond, InstType, Add, SetFlags, Rn, Rd, Src1Value);
|
| return;
|
| }
|
| @@ -179,8 +255,8 @@ void ARM32::AssemblerARM32::add(const Operand *OpRd, const Operand *OpRn,
|
|
|
| void ARM32::AssemblerARM32::bkpt(uint16_t imm16) {
|
| AssemblerBuffer::EnsureCapacity ensured(&Buffer);
|
| - uint32_t Encoding = (CondARM32::AL << kConditionShift) | B24 | B21 |
|
| - ((imm16 >> 4) << 8) | B6 | B5 | B4 | (imm16 & 0xf);
|
| + const uint32_t Encoding = (CondARM32::AL << kConditionShift) | B24 | B21 |
|
| + ((imm16 >> 4) << 8) | B6 | B5 | B4 | (imm16 & 0xf);
|
| emitInst(Encoding);
|
| }
|
|
|
| @@ -190,30 +266,62 @@ void ARM32::AssemblerARM32::bx(RegARM32::GPRRegister Rm, CondARM32::Cond Cond) {
|
| assert(isGPRRegisterDefined(Rm));
|
| assert(isConditionDefined(Cond));
|
| AssemblerBuffer::EnsureCapacity ensured(&Buffer);
|
| - uint32_t Encoding = (encodeCondition(Cond) << kConditionShift) | B24 | B21 |
|
| - (0xfff << 8) | B4 | (encodeGPRRegister(Rm) << kRmShift);
|
| + const uint32_t Encoding = (encodeCondition(Cond) << kConditionShift) | B24 |
|
| + B21 | (0xfff << 8) | B4 |
|
| + (encodeGPRRegister(Rm) << kRmShift);
|
| emitInst(Encoding);
|
| }
|
|
|
| +void ARM32::AssemblerARM32::ldr(const Operand *OpRt, const Operand *OpAddress,
|
| + CondARM32::Cond Cond) {
|
| + // Note: Loop is used so that we can short ciruit using break;
|
| + do {
|
| + uint32_t Rt;
|
| + if (decodeOperand(OpRt, Rt) != DecodedAsRegister)
|
| + break;
|
| + uint32_t Address;
|
| + if (decodeAddress(OpAddress, Address) != DecodedAsImmRegOffset)
|
| + break;
|
| + // cccc010pu0w1nnnnttttiiiiiiiiiiii (ARM section A8.8.63, encoding A1; and
|
| + // section A8.6.68, encoding A1).
|
| + constexpr uint32_t InstType = B1; // 010
|
| + constexpr bool IsLoad = true;
|
| + const Type Ty = OpRt->getType();
|
| + if (!(Ty == IceType_i32 || Ty == IceType_i8)) // TODO(kschimpf) Expand?
|
| + break;
|
| + const bool IsByte = typeWidthInBytes(Ty) == 1;
|
| + if ((getGPRReg(kRnShift, Address) == RegARM32::Encoded_Reg_pc) ||
|
| + (!IsByte && !isBitSet(P, Address) && isBitSet(W, Address)) ||
|
| + ((getGPRReg(kRnShift, Address) == RegARM32::Encoded_Reg_sp) &&
|
| + !isBitSet(P, Address) &&
|
| + isBitSet(U, Address) & !isBitSet(W, Address) &&
|
| + (mask(Address, kImm12Shift, kImmed12Bits) == 0x8 /* 000000000100 */)))
|
| + break;
|
| + emitMemOp(Cond, InstType, IsLoad, IsByte, Rt, Address);
|
| + return;
|
| + } while (0);
|
| + UnimplementedError(Ctx->getFlags());
|
| +}
|
| +
|
| void ARM32::AssemblerARM32::mov(const Operand *OpRd, const Operand *OpSrc,
|
| CondARM32::Cond Cond) {
|
| // Note: Loop is used so that we can short ciruit using break;
|
| do {
|
| uint32_t Rd;
|
| - if (decode(OpRd, Rd) != DecodedAsRegister)
|
| + if (decodeOperand(OpRd, Rd) != DecodedAsRegister)
|
| break;
|
| uint32_t Src;
|
| // TODO(kschimpf) Handle other forms of mov.
|
| - if (decode(OpSrc, Src) == DecodedAsRotatedImm8) {
|
| + if (decodeOperand(OpSrc, Src) == DecodedAsRotatedImm8) {
|
| // cccc0011101s0000ddddiiiiiiiiiiii (ARM section A8.8.102, encoding A1)
|
| // Note: We don't use movs in this assembler.
|
| constexpr bool SetFlags = false;
|
| if (!isConditionDefined(Cond) || (Rd == RegARM32::Reg_pc && SetFlags))
|
| // Conditions of rule violated.
|
| break;
|
| - uint32_t Rn = 0;
|
| - uint32_t Mov = B3 | B2 | B0; // 1101.
|
| - uint32_t InstType = 1;
|
| + constexpr uint32_t Rn = 0;
|
| + constexpr uint32_t Mov = B3 | B2 | B0; // 1101.
|
| + constexpr uint32_t InstType = 1;
|
| emitType01(Cond, InstType, Mov, SetFlags, Rn, Rd, Src);
|
| return;
|
| }
|
| @@ -221,20 +329,53 @@ void ARM32::AssemblerARM32::mov(const Operand *OpRd, const Operand *OpSrc,
|
| UnimplementedError(Ctx->getFlags());
|
| }
|
|
|
| +void ARM32::AssemblerARM32::str(const Operand *OpRt, const Operand *OpAddress,
|
| + CondARM32::Cond Cond) {
|
| + // Note: Loop is used so that we can short ciruit using break;
|
| + do {
|
| + uint32_t Rt;
|
| + if (decodeOperand(OpRt, Rt) != DecodedAsRegister)
|
| + break;
|
| + uint32_t Address;
|
| + if (decodeAddress(OpAddress, Address) != DecodedAsImmRegOffset)
|
| + break;
|
| + // cccc010pub0nnnnttttiiiiiiiiiiii (ARM section A8.8.204, encoding A1; and
|
| + // section 18.8.207, encoding A1).
|
| + constexpr uint32_t InstType = B1; // 010
|
| + constexpr bool IsLoad = false;
|
| + const Type Ty = OpRt->getType();
|
| + if (!(Ty == IceType_i32 || Ty == IceType_i8)) // TODO(kschimpf) Expand?
|
| + break;
|
| + const bool IsByte = typeWidthInBytes(Ty) == 1;
|
| + // Check for rule violations.
|
| + if ((getGPRReg(kRnShift, Address) == RegARM32::Encoded_Reg_pc) ||
|
| + (!isBitSet(P, Address) && isBitSet(W, Address)) ||
|
| + (!IsByte &&
|
| + (getGPRReg(kRnShift, Address) == RegARM32::Encoded_Reg_sp) &&
|
| + isBitSet(P, Address) && !isBitSet(U, Address) &&
|
| + isBitSet(W, Address) &&
|
| + (mask(Address, kImm12Shift, kImmed12Bits) == 0x8 /* 000000000100 */)))
|
| + break;
|
| + emitMemOp(Cond, InstType, IsLoad, IsByte, Rt, Address);
|
| + return;
|
| + } while (0);
|
| + UnimplementedError(Ctx->getFlags());
|
| +}
|
| +
|
| void ARM32::AssemblerARM32::sub(const Operand *OpRd, const Operand *OpRn,
|
| const Operand *OpSrc1, bool SetFlags,
|
| CondARM32::Cond Cond) {
|
| // Note: Loop is used so that we can short circuit using break;
|
| do {
|
| uint32_t Rd;
|
| - if (decode(OpRd, Rd) != DecodedAsRegister)
|
| + if (decodeOperand(OpRd, Rd) != DecodedAsRegister)
|
| break;
|
| uint32_t Rn;
|
| - if (decode(OpRn, Rn) != DecodedAsRegister)
|
| + if (decodeOperand(OpRn, Rn) != DecodedAsRegister)
|
| break;
|
| uint32_t Src1Value;
|
| // TODO(kschimpf) Other possible decodings of add.
|
| - if (decode(OpSrc1, Src1Value) == DecodedAsRotatedImm8) {
|
| + if (decodeOperand(OpSrc1, Src1Value) == DecodedAsRotatedImm8) {
|
| // Sub (Immediate): See ARM section A8.8.222, rule A1.
|
| // cccc0010010snnnnddddiiiiiiiiiiii where cccc=Cond, dddd=Rd, nnnn=Rn,
|
| // s=SetFlags and iiiiiiiiiiii=Src1Value
|
| @@ -242,8 +383,8 @@ void ARM32::AssemblerARM32::sub(const Operand *OpRd, const Operand *OpRn,
|
| (Rn == RegARM32::Reg_lr) || (Rn == RegARM32::Reg_pc && SetFlags))
|
| // Conditions of rule violated.
|
| break;
|
| - uint32_t Add = B1; // 0010
|
| - uint32_t InstType = 1;
|
| + constexpr uint32_t Add = B1; // 0010
|
| + constexpr uint32_t InstType = 1;
|
| emitType01(Cond, InstType, Add, SetFlags, Rn, Rd, Src1Value);
|
| return;
|
| }
|
|
|