Chromium Code Reviews| Index: src/IceAssemblerARM32.cpp |
| diff --git a/src/IceAssemblerARM32.cpp b/src/IceAssemblerARM32.cpp |
| index 2ef7e2f8a0174071af5b8e2c86007761c2e86eb5..8e7c5cd87b312f7d847915914f9d86c234349bed 100644 |
| --- a/src/IceAssemblerARM32.cpp |
| +++ b/src/IceAssemblerARM32.cpp |
| @@ -41,6 +41,10 @@ static constexpr IValueT B4 = 1 << 4; |
| static constexpr IValueT B5 = 1 << 5; |
| static constexpr IValueT B6 = 1 << 6; |
| static constexpr IValueT B7 = 1 << 7; |
| +static constexpr IValueT B8 = 1 << 8; |
| +static constexpr IValueT B9 = 1 << 9; |
| +static constexpr IValueT B10 = 1 << 10; |
| +static constexpr IValueT B11 = 1 << 11; |
| static constexpr IValueT B12 = 1 << 12; |
| static constexpr IValueT B13 = 1 << 13; |
| static constexpr IValueT B14 = 1 << 14; |
| @@ -96,6 +100,9 @@ static constexpr IValueT kImm12Shift = 0; |
| // Rotation instructions (uxtb etc.). |
| static constexpr IValueT kRotationShift = 10; |
| +// MemEx instructions. |
| +static constexpr IValueT kMemExOpcodeShift = 20; |
| + |
| // Div instruction register field encodings. |
| static constexpr IValueT kDivRdShift = 16; |
| static constexpr IValueT kDivRmShift = 8; |
| @@ -173,6 +180,25 @@ RegARM32::GPRRegister getGPRReg(IValueT Shift, IValueT Value) { |
| return decodeGPRRegister((Value >> Shift) & 0xF); |
| } |
| +// Defines alternate layouts of instruction operands, should the (common) |
| +// default pattern not be used. |
| +enum OpEncoding { |
| + // No alternate layout specified. |
| + DefaultOpEncoding, |
| + // Alternate encoding 3 for memory operands (like in strb, strh, ldrb, and |
| + // ldrh. |
| + OpEncoding3, |
| + // Alternate encoding for memory operands for ldrex and strex, which only |
| + // actually expect a register. |
| + OpEncodingMemEx |
| +}; |
| + |
| +IValueT getVarRegNum(const Variable *Var) { |
|
Jim Stichnoth
2015/12/11 17:24:57
This function name should be changed, to reflect t
Karl
2015/12/11 18:05:08
Changed to getEncodedGPRegNum.
|
| + int32_t Reg = Var->getRegNum(); |
| + return llvm::isa<Variable64On32>(Var) ? RegARM32::getI64PairFirstGPRNum(Reg) |
| + : RegARM32::getEncodedGPR(Reg); |
| +} |
| + |
| // The way an operand is encoded into a sequence of bits in functions |
| // encodeOperand and encodeAddress below. |
| enum EncodedOperand { |
| @@ -183,14 +209,25 @@ enum EncodedOperand { |
| // Value=rrrriiiiiiii where rrrr is the rotation, and iiiiiiii is the imm8 |
| // value. |
| EncodedAsRotatedImm8, |
| + // EncodedAsImmRegOffset is a memory operand that can take three forms, based |
| + // on OpEncoding: |
| + // |
| + // case DefaultOpEncoding: |
|
Jim Stichnoth
2015/12/11 17:24:57
This style of comment made me think you had some s
Karl
2015/12/11 18:05:08
Ok. Done.
|
| + // |
| // Value=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 defines the rotated Imm8 value. |
| - EncodedAsImmRegOffset, |
| + // |
| + // case OpEncoding3: |
| + // |
| // Value=00000000pu0w0nnnn0000iiii0000jjjj where nnnn=Rn, iiiijjjj=Imm8, p=1 |
| // if pre-indexed addressing, u=1 if offset positive, and w=1 if writeback to |
| // Rn. |
| - EncodedAsImmRegOffsetEnc3, |
| + // |
| + // case OpEncodingMemEx: |
| + // |
| + // Value=000000000000nnnn0000000000000000 where nnnn=Rn. |
| + EncodedAsImmRegOffset, |
| // Value=0000000pu0w00nnnnttttiiiiiss0mmmm where nnnn is the base register Rn, |
| // mmmm is the index register Rm, iiiii is the shift amount, ss is the shift |
| // kind, p=1 if pre-indexed addressing, u=1 if offset positive, and w=1 if |
| @@ -234,7 +271,7 @@ EncodedOperand encodeOperand(const Operand *Opnd, IValueT &Value) { |
| Value = 0; // Make sure initialized. |
| if (const auto *Var = llvm::dyn_cast<Variable>(Opnd)) { |
| if (Var->hasReg()) { |
| - Value = Var->getRegNum(); |
| + Value = getVarRegNum(Var); |
| return EncodedAsRegister; |
| } |
| return CantEncode; |
| @@ -296,14 +333,19 @@ IValueT encodeImmRegOffsetEnc3(IValueT Rn, IOffsetT Imm8, |
| return Value; |
| } |
| -// Defines alternate layouts of instruction operands, should the (common) |
| -// default pattern not be used. |
| -enum OpEncoding { |
| - // No alternate layout specified. |
| - DefaultOpEncoding, |
| - // Alternate encoding 3. |
| - OpEncoding3 |
| -}; |
| +IValueT encodeImmRegOffset(OpEncoding AddressEncoding, IValueT Reg, |
| + IOffsetT Offset, OperandARM32Mem::AddrMode Mode) { |
| + switch (AddressEncoding) { |
| + case DefaultOpEncoding: |
| + return encodeImmRegOffset(Reg, Offset, Mode); |
| + case OpEncoding3: |
| + return encodeImmRegOffsetEnc3(Reg, Offset, Mode); |
| + case OpEncodingMemEx: |
| + assert(Offset == 0); |
| + assert(Mode == OperandARM32Mem::Offset); |
| + return Reg << kRnShift; |
| + } |
| +} |
| // Encodes memory address Opnd, and encodes that information into Value, based |
| // on how ARM represents the address. Returns how the value was encoded. |
| @@ -321,34 +363,29 @@ EncodedOperand encodeAddress(const Operand *Opnd, IValueT &Value, |
| int32_t BaseRegNum = Var->getBaseRegNum(); |
| if (BaseRegNum == Variable::NoRegister) |
| BaseRegNum = TInfo.FrameOrStackReg; |
| - Value = encodeImmRegOffset(BaseRegNum, Offset, OperandARM32Mem::Offset); |
| + Value = encodeImmRegOffset(AddressEncoding, BaseRegNum, Offset, |
| + OperandARM32Mem::Offset); |
| return EncodedAsImmRegOffset; |
| } |
| if (const auto *Mem = llvm::dyn_cast<OperandARM32Mem>(Opnd)) { |
| Variable *Var = Mem->getBase(); |
| if (!Var->hasReg()) |
| return CantEncode; |
| - IValueT Rn = Var->getRegNum(); |
| + IValueT Rn = getVarRegNum(Var); |
| if (Mem->isRegReg()) { |
| const Variable *Index = Mem->getIndex(); |
| if (Var == nullptr) |
| return CantEncode; |
| Value = (Rn << kRnShift) | Mem->getAddrMode() | |
| - encodeShiftRotateImm5(Index->getRegNum(), Mem->getShiftOp(), |
| + encodeShiftRotateImm5(getVarRegNum(Index), Mem->getShiftOp(), |
| Mem->getShiftAmt()); |
| return EncodedAsShiftRotateImm5; |
| } |
| // Encoded as immediate register offset. |
| ConstantInteger32 *Offset = Mem->getOffset(); |
| - switch (AddressEncoding) { |
| - case DefaultOpEncoding: |
| - Value = encodeImmRegOffset(Rn, Offset->getValue(), Mem->getAddrMode()); |
| - return EncodedAsImmRegOffset; |
| - case OpEncoding3: |
| - Value = |
| - encodeImmRegOffsetEnc3(Rn, Offset->getValue(), Mem->getAddrMode()); |
| - return EncodedAsImmRegOffsetEnc3; |
| - } |
| + Value = encodeImmRegOffset(AddressEncoding, Rn, Offset->getValue(), |
| + Mem->getAddrMode()); |
| + return EncodedAsImmRegOffset; |
| } |
| return CantEncode; |
| } |
| @@ -768,7 +805,7 @@ void AssemblerARM32::emitMemOpEnc3(CondARM32::Cond Cond, IValueT Opcode, |
| default: |
| llvm::report_fatal_error(std::string(InstName) + |
| ": Memory address not understood"); |
| - case EncodedAsImmRegOffsetEnc3: { |
| + case EncodedAsImmRegOffset: { |
| // XXXH (immediate) |
| // xxxh<c> <Rt>, [<Rn>{, #+-<Imm8>}] |
| // xxxh<c> <Rt>, [<Rn>, #+/-<Imm8>] |
| @@ -1139,7 +1176,7 @@ void AssemblerARM32::ldr(const Operand *OpRt, const Operand *OpAddress, |
| // case. |
| default: |
| llvm::report_fatal_error(std::string("ldr : Type ") + typeString(Ty) + |
| - " not implementable\n"); |
| + " not implementable"); |
| case 0: { |
| // Handles i1 and i8 loads. |
| // |
| @@ -1202,6 +1239,74 @@ void AssemblerARM32::ldr(const Operand *OpRt, const Operand *OpAddress, |
| } |
| } |
| +void AssemblerARM32::emitMemExOp(CondARM32::Cond Cond, Type Ty, bool IsLoad, |
| + const Operand *OpRd, IValueT Rt, |
| + const Operand *OpAddress, |
| + const TargetInfo &TInfo, |
| + const char *InstName) { |
| + IValueT Rd = encodeRegister(OpRd, "Rd", InstName); |
| + IValueT MemExOpcode = IsLoad ? B0 : 0; |
| + switch (typeWidthInBytesLog2(Ty)) { |
|
John
2015/12/11 16:59:09
Why did you switch on the type width? can you swit
Karl
2015/12/11 18:05:08
I did it this way for the following reasons:
1) I
John
2015/12/11 18:10:46
IceType_i[8|16|32|64] are declared sequentially in
Karl
2015/12/11 21:00:12
OK, I'll change it, but you are slightly incorrect
|
| + default: |
| + llvm::report_fatal_error(std::string(InstName) + ": Type " + |
| + typeString(Ty) + " not implementable"); |
| + case 0: |
| + MemExOpcode |= B2; |
| + break; |
| + case 1: |
| + MemExOpcode |= B2 | B1; |
| + break; |
| + case 2: |
| + break; |
| + case 3: |
| + MemExOpcode |= B1; |
| + } |
| + // emitMemExOp(Cond, MemExOpcode, Rd, Rt, OpAddress, TInfo, InstName); |
|
Jim Stichnoth
2015/12/11 17:24:57
Remove this comment?
Karl
2015/12/11 18:05:08
Really? I like useless comments. Removing...
|
| + IValueT AddressRn; |
| + if (encodeAddress(OpAddress, AddressRn, TInfo, OpEncodingMemEx) != |
| + EncodedAsImmRegOffset) |
| + llvm::report_fatal_error(std::string(InstName) + |
| + ": Can't extract Rn from address"); |
| + assert(Utils::IsAbsoluteUint(3, MemExOpcode)); |
| + verifyRegDefined(Rd, "Rd", InstName); |
| + verifyRegDefined(Rt, "Rt", InstName); |
| + verifyCondDefined(Cond, InstName); |
| + AssemblerBuffer::EnsureCapacity ensured(&Buffer); |
| + IValueT Encoding = (Cond << kConditionShift) | B24 | B23 | B11 | B10 | B9 | |
| + B8 | B7 | B4 | (MemExOpcode << kMemExOpcodeShift) | |
| + AddressRn | (Rd << kRdShift) | (Rt << kRmShift); |
| + emitInst(Encoding); |
| + return; |
| +} |
| + |
| +void AssemblerARM32::ldrex(const Operand *OpRt, const Operand *OpAddress, |
| + CondARM32::Cond Cond, const TargetInfo &TInfo) { |
| + // LDREXB - ARM section A8.8.76, encoding A1: |
| + // ldrexb<c> <Rt>, [<Rn>] |
| + // |
| + // cccc00011101nnnntttt111110011111 where cccc=Cond, tttt=Rt, and nnnn=Rn. |
| + // |
| + // LDREXH - ARM section A8.8.78, encoding A1: |
| + // ldrexh<c> <Rt>, [<Rn>] |
| + // |
| + // cccc00011111nnnntttt111110011111 where cccc=Cond, tttt=Rt, and nnnn=Rn. |
| + // |
| + // LDREX - ARM section A8.8.75, encoding A1: |
| + // ldrex<c> <Rt>, [<Rn>] |
| + // |
| + // cccc00011001nnnntttt111110011111 where cccc=Cond, tttt=Rt, and nnnn=Rn. |
| + // |
| + // LDREXD - ARM section A8. |
| + // ldrexd<c> <Rt>, [<Rn>] |
| + // |
| + // cccc00011001nnnntttt111110011111 where cccc=Cond, tttt=Rt, and nnnn=Rn. |
| + constexpr const char *LdrexName = "ldrex"; |
| + const Type Ty = OpRt->getType(); |
| + constexpr bool IsLoad = true; |
| + constexpr IValueT Rm = RegARM32::Encoded_Reg_pc; |
| + emitMemExOp(Cond, Ty, IsLoad, OpRt, Rm, OpAddress, TInfo, LdrexName); |
| +} |
| + |
| void AssemblerARM32::emitShift(const CondARM32::Cond Cond, |
| const OperandARM32::ShiftKind Shift, |
| const Operand *OpRd, const Operand *OpRm, |
| @@ -1471,6 +1576,41 @@ void AssemblerARM32::str(const Operand *OpRt, const Operand *OpAddress, |
| } |
| } |
| +void AssemblerARM32::strex(const Operand *OpRd, const Operand *OpRt, |
| + const Operand *OpAddress, CondARM32::Cond Cond, |
| + const TargetInfo &TInfo) { |
| + // STREXB - ARM section A8.8.213, encoding A1: |
| + // strexb<c> <Rd>, <Rt>, [<Rn>] |
| + // |
| + // cccc00011100nnnndddd11111001tttt where cccc=Cond, dddd=Rd, tttt=Rt, and |
| + // nnnn=Rn. |
| + // |
| + // STREXH - ARM section A8.8.215, encoding A1: |
| + // strexh<c> <Rd>, <Rt>, [<Rn>] |
| + // |
| + // cccc00011110nnnndddd11111001tttt where cccc=Cond, dddd=Rd, tttt=Rt, and |
| + // nnnn=Rn. |
| + // |
| + // STREX - ARM section A8.8.212, encoding A1: |
| + // strex<c> <Rd>, <Rt>, [<Rn>] |
| + // |
| + // cccc00011000nnnndddd11111001tttt where cccc=Cond, dddd=Rd, tttt=Rt, and |
| + // nnnn=Rn. |
| + // |
| + // STREXD - ARM section A8.8.214, encoding A1: |
| + // strexd<c> <Rd>, <Rt>, [<Rn>] |
| + // |
| + // cccc00011010nnnndddd11111001tttt where cccc=Cond, dddd=Rd, tttt=Rt, and |
| + // nnnn=Rn. |
| + constexpr const char *StrexName = "strex"; |
| + // Note: Rt uses Rm shift in encoding. |
| + IValueT Rt = encodeRegister(OpRt, "Rt", StrexName); |
| + const Type Ty = OpRt->getType(); |
| + constexpr bool IsLoad = true; |
| + emitMemExOp(Cond, Ty, !IsLoad, OpRd, Rt, OpAddress, TInfo, StrexName); |
| + return; |
|
Jim Stichnoth
2015/12/11 17:24:57
Remove this explicit return.
Karl
2015/12/11 18:05:08
Done.
|
| +} |
| + |
| void AssemblerARM32::orr(const Operand *OpRd, const Operand *OpRn, |
| const Operand *OpSrc1, bool SetFlags, |
| CondARM32::Cond Cond) { |