Chromium Code Reviews| Index: src/IceAssemblerARM32.cpp |
| diff --git a/src/IceAssemblerARM32.cpp b/src/IceAssemblerARM32.cpp |
| index 9972b44f29506f286618f931909fffdcca5422d9..6ae6397d9ec71c8c9f1c1988c75561c8b56b60e6 100644 |
| --- a/src/IceAssemblerARM32.cpp |
| +++ b/src/IceAssemblerARM32.cpp |
| @@ -39,6 +39,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 +61,22 @@ 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; |
| + |
| +template <typename T> static inline bool isUint(int N, T Value) { |
|
Jim Stichnoth
2015/10/15 20:41:29
Can you just use the (inappropriately named) IsUin
Karl
2015/10/15 22:03:33
Done.
|
| + assert((0 < N) && (static_cast<unsigned>(N) < (CHAR_BIT * sizeof(Value)))); |
| + T Limit = static_cast<T>(1) << N; |
| + return (0 <= Value) && (Value < Limit); |
| +} |
| + |
| +template <typename T> inline bool isAbsoluteUint(int N, T Value) { |
| + assert(N < 30 && static_cast<unsigned>(N) < (CHAR_BIT * sizeof(T))); |
| + if (Value < 0) |
| + Value = -Value; |
| + return isUint(N, Value); |
| +} |
| + |
| inline uint32_t encodeBool(bool b) { return b ? 1 : 0; } |
| inline uint32_t encodeGPRRegister(RegARM32::GPRRegister Rn) { |
| @@ -75,14 +99,38 @@ 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 int the corresponding masked value |
|
Jim Stichnoth
2015/10/15 20:41:29
"int" ? Also, end sentence with period.
Karl
2015/10/15 22:03:33
Done.
|
| +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(); |
| @@ -98,6 +146,37 @@ 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) { |
| + // Note: Loop is used so that we can short ciruit using break; |
|
Jim Stichnoth
2015/10/15 20:41:29
Would it make sense here to just "return CantDecod
Karl
2015/10/15 22:03:33
Good idea. Removing loop as well.
|
| + do { |
| + if (const auto *Var = llvm::dyn_cast<Variable>(Opnd)) { |
| + // Should be a stack variable, with an offset. |
| + if (Var->hasReg()) |
| + break; |
| + int32_t Offset = Var->getStackOffset(); |
| + if (!isAbsoluteUint(12, Offset)) |
| + break; |
| + Value = decodeImmRegOffset(RegARM32::Encoded_Reg_sp, Offset, |
| + OperandARM32Mem::Offset); |
| + return DecodedAsImmRegOffset; |
| + } |
| + } while (0); |
| + return CantDecode; |
| +} |
| + |
| } // end of anonymous namespace |
| namespace Ice { |
| @@ -140,27 +219,39 @@ 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 | |
| + 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); |
| + uint32_t Encoding = (encodeCondition(Cond) << kConditionShift) | |
| + (InstType << kTypeShift) | (IsLoad ? L : 0) | |
| + (IsByte ? B : 0) | (Rt << kRdShift) | Address; |
| + emitInst(Encoding); |
| +} |
| + |
| void ARM32::AssemblerARM32::add(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) { |
| // ADD (Immediate): See ARM section A8.8.5, rule A1. |
| // cccc0010100snnnnddddiiiiiiiiiiii where cccc=Cond, dddd=Rd, nnnn=Rn, |
| // s=SetFlags and iiiiiiiiiiii=Src1Value |
| @@ -195,16 +286,47 @@ void ARM32::AssemblerARM32::bx(RegARM32::GPRRegister Rm, CondARM32::Cond Cond) { |
| 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). |
| + uint32_t InstType = B1; // 010 |
| + bool IsLoad = true; |
| + Type Ty = OpRt->getType(); |
| + if (!(Ty == IceType_i32 || Ty == IceType_i8)) // TODO(kschimpf) Expand? |
| + break; |
| + bool IsByte = Ty == IceType_i8; |
| + 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; |
| @@ -221,20 +343,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). |
| + uint32_t InstType = B1; // 010 |
| + bool IsLoad = false; |
| + Type Ty = OpRt->getType(); |
| + if (!(Ty == IceType_i32 || Ty == IceType_i8)) // TODO(kschimpf) Expand? |
| + break; |
| + bool IsByte = Ty == IceType_i8; |
|
Jim Stichnoth
2015/10/15 20:41:29
IceType_i1 should also qualify as IsByte, since st
John
2015/10/15 20:59:25
const bool IsByte = ...
also, please assert that
Karl
2015/10/15 22:03:33
Using typeWidthInBytes to resolve if is byte. Note
|
| + // 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 |