| Index: src/IceAssemblerARM32.cpp
|
| diff --git a/src/IceAssemblerARM32.cpp b/src/IceAssemblerARM32.cpp
|
| index cbb5bdc71a3289ebb91684064331349ad89b6341..ca9cbc1c9b39e82517877deb7ac4356f5523fa7b 100644
|
| --- a/src/IceAssemblerARM32.cpp
|
| +++ b/src/IceAssemblerARM32.cpp
|
| @@ -100,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;
|
| @@ -177,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 getEncodedGPRegNum(const Variable *Var) {
|
| + 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 {
|
| @@ -187,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:
|
| + //
|
| + // ***** DefaultOpEncoding *****
|
| + //
|
| // 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,
|
| + //
|
| + // ***** 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,
|
| + //
|
| + // ***** 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
|
| @@ -240,7 +273,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 = getEncodedGPRegNum(Var);
|
| return EncodedAsRegister;
|
| }
|
| return CantEncode;
|
| @@ -316,14 +349,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.
|
| @@ -341,34 +379,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 = getEncodedGPRegNum(Var);
|
| if (Mem->isRegReg()) {
|
| const Variable *Index = Mem->getIndex();
|
| if (Var == nullptr)
|
| return CantEncode;
|
| Value = (Rn << kRnShift) | Mem->getAddrMode() |
|
| - encodeShiftRotateImm5(Index->getRegNum(), Mem->getShiftOp(),
|
| - Mem->getShiftAmt());
|
| + encodeShiftRotateImm5(getEncodedGPRegNum(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;
|
| }
|
| @@ -799,7 +832,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>]
|
| @@ -906,12 +939,14 @@ void AssemblerARM32::emitSignExtend(CondARM32::Cond Cond, IValueT Opcode,
|
| // Note: For the moment, we assume no rotation is specified.
|
| RotationValue Rotation = kRotateNone;
|
| constexpr IValueT Rn = RegARM32::Encoded_Reg_pc;
|
| - switch (typeWidthInBytes(OpSrc0->getType())) {
|
| + const Type Ty = OpSrc0->getType();
|
| + switch (Ty) {
|
| default:
|
| - llvm::report_fatal_error(std::string(InstName) +
|
| - ": Type of Rm not understood");
|
| + llvm::report_fatal_error(std::string(InstName) + ": Type " +
|
| + typeString(Ty) + " not allowed");
|
| break;
|
| - case 1: {
|
| + case IceType_i1:
|
| + case IceType_i8: {
|
| // SXTB/UXTB - Arm sections A8.8.233 and A8.8.274, encoding A1:
|
| // sxtb<c> <Rd>, <Rm>{, <rotate>}
|
| // uxtb<c> <Rd>, <Rm>{, <rotate>}
|
| @@ -920,7 +955,7 @@ void AssemblerARM32::emitSignExtend(CondARM32::Cond Cond, IValueT Opcode,
|
| // dddd=Rd, mmmm=Rm, and rr defined (RotationValue) rotate.
|
| break;
|
| }
|
| - case 2: {
|
| + case IceType_i16: {
|
| // SXTH/UXTH - ARM sections A8.8.235 and A8.8.276, encoding A1:
|
| // uxth<c> <Rd>< <Rm>{, <rotate>}
|
| //
|
| @@ -1163,17 +1198,18 @@ void AssemblerARM32::ldr(const Operand *OpRt, const Operand *OpAddress,
|
| constexpr bool IsLoad = true;
|
| IValueT Rt = encodeRegister(OpRt, "Rt", LdrName);
|
| const Type Ty = OpRt->getType();
|
| - switch (typeWidthInBytesLog2(Ty)) {
|
| - case 3:
|
| - // LDRD is not implemented because target lowering handles i64 and double by
|
| - // using two (32-bit) load instructions. Note: Intenionally drop to default
|
| - // case.
|
| + switch (Ty) {
|
| + case IceType_i64:
|
| + // LDRD is not implemented because target lowering handles i64 and double by
|
| + // using two (32-bit) load instructions. Note: Intentionally drop to default
|
| + // case.
|
| + llvm::report_fatal_error(std::string("ldr : Type ") + typeString(Ty) +
|
| + " not implemented");
|
| default:
|
| llvm::report_fatal_error(std::string("ldr : Type ") + typeString(Ty) +
|
| - " not implementable\n");
|
| - case 0: {
|
| - // Handles i1 and i8 loads.
|
| - //
|
| + " not allowed");
|
| + case IceType_i1:
|
| + case IceType_i8: {
|
| // LDRB (immediate) - ARM section A8.8.68, encoding A1:
|
| // ldrb<c> <Rt>, [<Rn>{, #+/-<imm12>}] ; p=1, w=0
|
| // ldrb<c> <Rt>, [<Rn>], #+/-<imm12> ; p=1, w=1
|
| @@ -1193,9 +1229,7 @@ void AssemblerARM32::ldr(const Operand *OpRt, const Operand *OpAddress,
|
| emitMemOp(Cond, IsLoad, IsByte, Rt, OpAddress, TInfo, LdrName);
|
| return;
|
| }
|
| - case 1: {
|
| - // Handles i16 loads.
|
| - //
|
| + case IceType_i16: {
|
| // LDRH (immediate) - ARM section A8.8.80, encoding A1:
|
| // ldrh<c> <Rt>, [<Rn>{, #+/-<Imm8>}]
|
| // ldrh<c> <Rt>, [<Rn>], #+/-<Imm8>
|
| @@ -1208,10 +1242,7 @@ void AssemblerARM32::ldr(const Operand *OpRt, const Operand *OpAddress,
|
| emitMemOpEnc3(Cond, L | B7 | B5 | B4, Rt, OpAddress, TInfo, Ldrh);
|
| return;
|
| }
|
| - case 2: {
|
| - // Note: Handles i32 and float loads. Target lowering handles i64 and
|
| - // double by using two (32 bit) load instructions.
|
| - //
|
| + case IceType_i32: {
|
| // LDR (immediate) - ARM section A8.8.63, encoding A1:
|
| // ldr<c> <Rt>, [<Rn>{, #+/-<imm12>}] ; p=1, w=0
|
| // ldr<c> <Rt>, [<Rn>], #+/-<imm12> ; p=1, w=1
|
| @@ -1233,6 +1264,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 (Ty) {
|
| + default:
|
| + llvm::report_fatal_error(std::string(InstName) + ": Type " +
|
| + typeString(Ty) + " not allowed");
|
| + case IceType_i1:
|
| + case IceType_i8:
|
| + MemExOpcode |= B2;
|
| + break;
|
| + case IceType_i16:
|
| + MemExOpcode |= B2 | B1;
|
| + break;
|
| + case IceType_i32:
|
| + break;
|
| + case IceType_i64:
|
| + MemExOpcode |= B1;
|
| + }
|
| + 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,
|
| @@ -1447,17 +1546,18 @@ void AssemblerARM32::str(const Operand *OpRt, const Operand *OpAddress,
|
| constexpr bool IsLoad = false;
|
| IValueT Rt = encodeRegister(OpRt, "Rt", StrName);
|
| const Type Ty = OpRt->getType();
|
| - switch (typeWidthInBytesLog2(Ty)) {
|
| - case 3:
|
| - // STRD is not implemented because target lowering handles i64 and double by
|
| - // using two (32-bit) store instructions. Note: Intenionally drop to
|
| - // default case.
|
| - default:
|
| + switch (Ty) {
|
| + case IceType_i64:
|
| + // STRD is not implemented because target lowering handles i64 and double by
|
| + // using two (32-bit) store instructions. Note: Intentionally drop to
|
| + // default case.
|
| llvm::report_fatal_error(std::string(StrName) + ": Type " + typeString(Ty) +
|
| " not implemented");
|
| - case 0: {
|
| - // Handles i1 and i8 stores.
|
| - //
|
| + default:
|
| + llvm::report_fatal_error(std::string(StrName) + ": Type " + typeString(Ty) +
|
| + " not allowed");
|
| + case IceType_i1:
|
| + case IceType_i8: {
|
| // STRB (immediate) - ARM section A8.8.207, encoding A1:
|
| // strb<c> <Rt>, [<Rn>{, #+/-<imm12>}] ; p=1, w=0
|
| // strb<c> <Rt>, [<Rn>], #+/-<imm12> ; p=1, w=1
|
| @@ -1469,9 +1569,7 @@ void AssemblerARM32::str(const Operand *OpRt, const Operand *OpAddress,
|
| emitMemOp(Cond, IsLoad, IsByte, Rt, OpAddress, TInfo, StrName);
|
| return;
|
| }
|
| - case 1: {
|
| - // Handles i16 stores.
|
| - //
|
| + case IceType_i16: {
|
| // STRH (immediate) - ARM section A8.*.217, encoding A1:
|
| // strh<c> <Rt>, [<Rn>{, #+/-<Imm8>}]
|
| // strh<c> <Rt>, [<Rn>], #+/-<Imm8>
|
| @@ -1484,7 +1582,7 @@ void AssemblerARM32::str(const Operand *OpRt, const Operand *OpAddress,
|
| emitMemOpEnc3(Cond, B7 | B5 | B4, Rt, OpAddress, TInfo, Strh);
|
| return;
|
| }
|
| - case 2: {
|
| + case IceType_i32: {
|
| // Note: Handles i32 and float stores. Target lowering handles i64 and
|
| // double by using two (32 bit) store instructions.
|
| //
|
| @@ -1502,6 +1600,40 @@ 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);
|
| +}
|
| +
|
| void AssemblerARM32::orr(const Operand *OpRd, const Operand *OpRn,
|
| const Operand *OpSrc1, bool SetFlags,
|
| CondARM32::Cond Cond) {
|
|
|