Commit 4175d45e by Karl Schimpf

Add various forms of LDREX/STREX to ARM integrated assembler.

parent 65f80d72
......@@ -555,9 +555,8 @@ void Assembler::stm(BlockAddressMode am, Register base, RegList regs,
ASSERT(regs != 0);
EmitMultiMemOp(cond, am, false, base, regs);
}
#endif
// Moved to ARM::AssemblerARM32::ldrex();
void Assembler::ldrex(Register rt, Register rn, Condition cond) {
ASSERT(TargetCPUFeatures::arm_version() != ARMv5TE);
ASSERT(rn != kNoRegister);
......@@ -573,7 +572,7 @@ void Assembler::ldrex(Register rt, Register rn, Condition cond) {
Emit(encoding);
}
// Moved to ARM::AssemblerARM32::strex();
void Assembler::strex(Register rd, Register rt, Register rn, Condition cond) {
ASSERT(TargetCPUFeatures::arm_version() != ARMv5TE);
ASSERT(rn != kNoRegister);
......@@ -589,7 +588,7 @@ void Assembler::strex(Register rd, Register rt, Register rn, Condition cond) {
(static_cast<int32_t>(rt) << kStrExRtShift);
Emit(encoding);
}
#endif
void Assembler::clrex() {
ASSERT(TargetCPUFeatures::arm_version() != ARMv5TE);
......
......@@ -588,10 +588,12 @@ class Assembler : public ValueObject {
// (and doesn't implement ARM STM instruction).
void stm(BlockAddressMode am, Register base,
RegList regs, Condition cond = AL);
#endif
// Moved to ARM::AssemblerARM32::ldrex();
void ldrex(Register rd, Register rn, Condition cond = AL);
// Moved to ARM::AssemblerARM32::strex();
void strex(Register rd, Register rt, Register rn, Condition cond = AL);
#endif
// Miscellaneous instructions.
void clrex();
......
......@@ -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;
}
......@@ -800,7 +833,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>]
......@@ -907,12 +940,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>}
......@@ -921,7 +956,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>}
//
......@@ -1188,17 +1223,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
......@@ -1218,9 +1254,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>
......@@ -1233,10 +1267,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
......@@ -1258,6 +1289,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,
......@@ -1472,17 +1571,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
......@@ -1494,9 +1594,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>
......@@ -1509,7 +1607,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.
//
......@@ -1527,6 +1625,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) {
......
......@@ -225,6 +225,15 @@ public:
ldr(OpRt, OpAddress, Cond, TInfo);
}
void ldrex(const Operand *OpRt, const Operand *OpAddress,
CondARM32::Cond Cond, const TargetInfo &TInfo);
void ldrex(const Operand *OpRt, const Operand *OpAddress,
CondARM32::Cond Cond, const TargetLowering *Lowering) {
const TargetInfo TInfo(Lowering);
ldrex(OpRt, OpAddress, Cond, TInfo);
}
void lsl(const Operand *OpRd, const Operand *OpRn, const Operand *OpSrc1,
bool SetFlags, CondARM32::Cond Cond);
......@@ -283,6 +292,15 @@ public:
str(OpRt, OpAddress, Cond, TInfo);
}
void strex(const Operand *OpRd, const Operand *OpRt, const Operand *OpAddress,
CondARM32::Cond Cond, const TargetInfo &TInfo);
void strex(const Operand *OpRd, const Operand *OpRt, const Operand *OpAddress,
CondARM32::Cond Cond, const TargetLowering *Lowering) {
const TargetInfo TInfo(Lowering);
strex(OpRd, OpRt, OpAddress, Cond, TInfo);
}
void sub(const Operand *OpRd, const Operand *OpRn, const Operand *OpSrc1,
bool SetFlags, CondARM32::Cond Cond);
......@@ -378,6 +396,12 @@ private:
const Operand *OpAddress, const TargetInfo &TInfo,
const char *InstName);
// Emit cccc00011xxlnnnndddd11111001tttt where cccc=Cond, xx encodes type
// size, l=IsLoad, nnnn=Rn (as defined by OpAddress), and tttt=Rt.
void emitMemExOp(CondARM32::Cond, Type Ty, bool IsLoad, const Operand *OpRd,
IValueT Rt, const Operand *OpAddress,
const TargetInfo &TInfo, const char *InstName);
// Pattern cccc100aaaalnnnnrrrrrrrrrrrrrrrr where cccc=Cond,
// aaaa<<21=AddressMode, l=IsLoad, nnnn=BaseReg, and
// rrrrrrrrrrrrrrrr is bitset of Registers.
......
......@@ -1181,6 +1181,17 @@ template <> void InstARM32Ldrex::emit(const Cfg *Func) const {
getSrc(0)->emit(Func);
}
template <> void InstARM32Ldrex::emitIAS(const Cfg *Func) const {
assert(getSrcSize() == 1);
assert(getDest()->hasReg());
Variable *Dest = getDest();
assert(isScalarIntegerType(Dest->getType()));
auto *Asm = Func->getAssembler<ARM32::AssemblerARM32>();
Asm->ldrex(Dest, getSrc(0), getPredicate(), Func->getTarget());
if (Asm->needsTextFixup())
emitUsingTextFixup(Func);
}
template <InstARM32::InstKindARM32 K>
void InstARM32TwoAddrGPR<K>::emitIAS(const Cfg *Func) const {
emitUsingTextFixup(Func);
......@@ -1590,6 +1601,16 @@ void InstARM32Strex::emit(const Cfg *Func) const {
emitSources(Func);
}
void InstARM32Strex::emitIAS(const Cfg *Func) const {
assert(getSrcSize() == 2);
const Operand *Src0 = getSrc(0);
assert(isScalarIntegerType(Src0->getType()));
auto *Asm = Func->getAssembler<ARM32::AssemblerARM32>();
Asm->strex(Dest, Src0, getSrc(1), getPredicate(), Func->getTarget());
if (Asm->needsTextFixup())
emitUsingTextFixup(Func);
}
void InstARM32Strex::dump(const Cfg *Func) const {
if (!BuildDefs::dump())
return;
......
......@@ -1166,6 +1166,7 @@ public:
InstARM32Strex(Func, Dest, Value, Mem, Predicate);
}
void emit(const Cfg *Func) const override;
void emitIAS(const Cfg *Func) const override;
void dump(const Cfg *Func) const override;
static bool classof(const Inst *Inst) { return isClassof(Inst, Strex); }
......
; Tests assembly of ldrex and strex instructions
; REQUIRES: allow_dump
; Compile using standalone assembler.
; RUN: %p2i --filetype=asm -i %s --target=arm32 --args -Om1 \
; RUN: | FileCheck %s --check-prefix=ASM
; Show bytes in assembled standalone code.
; RUN: %p2i --filetype=asm -i %s --target=arm32 --assemble --disassemble \
; RUN: --args -Om1 | FileCheck %s --check-prefix=DIS
; Compile using integrated assembler.
; RUN: %p2i --filetype=iasm -i %s --target=arm32 --args -Om1 \
; RUN: | FileCheck %s --check-prefix=IASM
; Show bytes in assembled integrated code.
; RUN: %p2i --filetype=iasm -i %s --target=arm32 --assemble --disassemble \
; RUN: --args -Om1 | FileCheck %s --check-prefix=DIS
declare i8 @llvm.nacl.atomic.rmw.i8(i32, i8*, i8, i32)
declare i16 @llvm.nacl.atomic.rmw.i16(i32, i16*, i16, i32)
declare i32 @llvm.nacl.atomic.rmw.i32(i32, i32*, i32, i32) #0
declare i64 @llvm.nacl.atomic.rmw.i64(i32, i64*, i64, i32) #0
define internal i32 @testI8Form(i32 %ptr, i32 %a) {
; ASM-LABEL:testI8Form:
; DIS-LABEL:00000000 <testI8Form>:
; IASM-LABEL:testI8Form:
entry:
; ASM-NEXT:.LtestI8Form$entry:
; IASM-NEXT:.LtestI8Form$entry:
; ASM-NEXT: sub sp, sp, #28
; DIS-NEXT: 0: e24dd01c
; IASM-NEXT: .byte 0x1c
; IASM-NEXT: .byte 0xd0
; IASM-NEXT: .byte 0x4d
; IASM-NEXT: .byte 0xe2
; ASM-NEXT: str r0, [sp, #24]
; ASM-NEXT: # [sp, #24] = def.pseudo
; DIS-NEXT: 4: e58d0018
; IASM-NEXT: .byte 0x18
; IASM-NEXT: .byte 0x0
; IASM-NEXT: .byte 0x8d
; IASM-NEXT: .byte 0xe5
; ASM-NEXT: str r1, [sp, #20]
; ASM-NEXT: # [sp, #20] = def.pseudo
; DIS-NEXT: 8: e58d1014
; IASM-NEXT: .byte 0x14
; IASM-NEXT: .byte 0x10
; IASM-NEXT: .byte 0x8d
; IASM-NEXT: .byte 0xe5
%ptr.asptr = inttoptr i32 %ptr to i8*
%a.arg_trunc = trunc i32 %a to i8
; ASM-NEXT: ldr r0, [sp, #20]
; DIS-NEXT: c: e59d0014
; IASM-NEXT: .byte 0x14
; IASM-NEXT: .byte 0x0
; IASM-NEXT: .byte 0x9d
; IASM-NEXT: .byte 0xe5
; ASM-NEXT: strb r0, [sp, #16]
; DIS-NEXT: 10: e5cd0010
; ASM-NEXT: # [sp, #16] = def.pseudo
; IASM-NEXT: .byte 0x10
; IASM-NEXT: .byte 0x0
; IASM-NEXT: .byte 0xcd
; IASM-NEXT: .byte 0xe5
%v = call i8 @llvm.nacl.atomic.rmw.i8(i32 1, i8* %ptr.asptr,
i8 %a.arg_trunc, i32 6)
; ASM-NEXT: ldrb r0, [sp, #16]
; DIS-NEXT: 14: e5dd0010
; IASM-NEXT: .byte 0x10
; IASM-NEXT: .byte 0x0
; IASM-NEXT: .byte 0xdd
; IASM-NEXT: .byte 0xe5
; ASM-NEXT: strb r0, [sp, #4]
; ASM-NEXT: # [sp, #4] = def.pseudo
; DIS-NEXT: 18: e5cd0004
; IASM-NEXT: .byte 0x4
; IASM-NEXT: .byte 0x0
; IASM-NEXT: .byte 0xcd
; IASM-NEXT: .byte 0xe5
; ASM-NEXT: ldr r0, [sp, #24]
; DIS-NEXT: 1c: e59d0018
; IASM-NEXT: .byte 0x18
; IASM-NEXT: .byte 0x0
; IASM-NEXT: .byte 0x9d
; IASM-NEXT: .byte 0xe5
; ASM-NEXT: str r0, [sp]
; ASM-NEXT: # [sp] = def.pseudo
; DIS-NEXT: 20: e58d0000
; IASM-NEXT: .byte 0x0
; IASM-NEXT: .byte 0x0
; IASM-NEXT: .byte 0x8d
; IASM-NEXT: .byte 0xe5
; ASM-NEXT: dmb sy
; DIS-NEXT: 24: f57ff05f
; IASM-NEXT: .byte 0x5f
; IASM-NEXT: .byte 0xf0
; IASM-NEXT: .byte 0x7f
; IASM-NEXT: .byte 0xf5
; ASM-NEXT:.LtestI8Form$local$__0:
; IASM-NEXT:.LtestI8Form$local$__0:
; ASM-NEXT: ldr r0, [sp]
; DIS-NEXT: 28: e59d0000
; IASM-NEXT: .byte 0x0
; IASM-NEXT: .byte 0x0
; IASM-NEXT: .byte 0x9d
; IASM-NEXT: .byte 0xe5
; ASM-NEXT: ldrb r1, [sp, #4]
; DIS-NEXT: 2c: e5dd1004
; IASM-NEXT: .byte 0x4
; IASM-NEXT: .byte 0x10
; IASM-NEXT: .byte 0xdd
; IASM-NEXT: .byte 0xe5
; ASM-NEXT: uxtb r1, r1
; DIS-NEXT: 30: e6ef1071
; IASM-NEXT: .byte 0x71
; IASM-NEXT: .byte 0x10
; IASM-NEXT: .byte 0xef
; IASM-NEXT: .byte 0xe6
; ***** Example of ldrexb *****
; ASM-NEXT: ldrexb r2, [r0]
; DIS-NEXT: 34: e1d02f9f
; IASM-NEXT: .byte 0x9f
; IASM-NEXT: .byte 0x2f
; IASM-NEXT: .byte 0xd0
; IASM-NEXT: .byte 0xe1
; ASM-NEXT: add r1, r2, r1
; ASM-NEXT: # r3 = def.pseudo
; DIS-NEXT: 38: e0821001
; IASM-NEXT: .byte 0x1
; IASM-NEXT: .byte 0x10
; IASM-NEXT: .byte 0x82
; IASM-NEXT: .byte 0xe0
; ***** Example of strexb *****
; ASM-NEXT: strexb r3, r1, [r0]
; DIS-NEXT: 3c: e1c03f91
; IASM-NEXT: .byte 0x91
; IASM-NEXT: .byte 0x3f
; IASM-NEXT: .byte 0xc0
; IASM-NEXT: .byte 0xe1
%retval = zext i8 %v to i32
ret i32 %retval
}
define internal i32 @testI16Form(i32 %ptr, i32 %a) {
; ASM-LABEL:testI16Form:
; DIS-LABEL:00000070 <testI16Form>:
; IASM-LABEL:testI16Form:
entry:
; ASM-NEXT:.LtestI16Form$entry:
; IASM-NEXT:.LtestI16Form$entry:
; ASM-NEXT: sub sp, sp, #28
; DIS-NEXT: 70: e24dd01c
; IASM-NEXT: .byte 0x1c
; IASM-NEXT: .byte 0xd0
; IASM-NEXT: .byte 0x4d
; IASM-NEXT: .byte 0xe2
; ASM-NEXT: str r0, [sp, #24]
; ASM-NEXT: # [sp, #24] = def.pseudo
; DIS-NEXT: 74: e58d0018
; IASM-NEXT: .byte 0x18
; IASM-NEXT: .byte 0x0
; IASM-NEXT: .byte 0x8d
; IASM-NEXT: .byte 0xe5
; ASM-NEXT: str r1, [sp, #20]
; ASM-NEXT: # [sp, #20] = def.pseudo
; DIS-NEXT: 78: e58d1014
; IASM-NEXT: .byte 0x14
; IASM-NEXT: .byte 0x10
; IASM-NEXT: .byte 0x8d
; IASM-NEXT: .byte 0xe5
%ptr.asptr = inttoptr i32 %ptr to i16*
%a.arg_trunc = trunc i32 %a to i16
; ASM-NEXT: ldr r0, [sp, #20]
; DIS-NEXT: 7c: e59d0014
; IASM-NEXT: .byte 0x14
; IASM-NEXT: .byte 0x0
; IASM-NEXT: .byte 0x9d
; IASM-NEXT: .byte 0xe5
; ASM-NEXT: strh r0, [sp, #16]
; ASM-NEXT: # [sp, #16] = def.pseudo
; DIS-NEXT: 80: e1cd01b0
; IASM-NEXT: .byte 0xb0
; IASM-NEXT: .byte 0x1
; IASM-NEXT: .byte 0xcd
; IASM-NEXT: .byte 0xe1
%v = call i16 @llvm.nacl.atomic.rmw.i16(i32 1, i16* %ptr.asptr,
i16 %a.arg_trunc, i32 6)
; ASM-NEXT: ldrh r0, [sp, #16]
; DIS-NEXT: 84: e1dd01b0
; IASM-NEXT: .byte 0xb0
; IASM-NEXT: .byte 0x1
; IASM-NEXT: .byte 0xdd
; IASM-NEXT: .byte 0xe1
; ASM-NEXT: strh r0, [sp, #4]
; ASM-NEXT: # [sp, #4] = def.pseudo
; DIS-NEXT: 88: e1cd00b4
; IASM-NEXT: .byte 0xb4
; IASM-NEXT: .byte 0x0
; IASM-NEXT: .byte 0xcd
; IASM-NEXT: .byte 0xe1
; ASM-NEXT: ldr r0, [sp, #24]
; DIS-NEXT: 8c: e59d0018
; IASM-NEXT: .byte 0x18
; IASM-NEXT: .byte 0x0
; IASM-NEXT: .byte 0x9d
; IASM-NEXT: .byte 0xe5
; ASM-NEXT: str r0, [sp]
; ASM-NEXT: # [sp] = def.pseudo
; DIS-NEXT: 90: e58d0000
; IASM-NEXT: .byte 0x0
; IASM-NEXT: .byte 0x0
; IASM-NEXT: .byte 0x8d
; IASM-NEXT: .byte 0xe5
; ASM-NEXT: dmb sy
; DIS-NEXT: 94: f57ff05f
; IASM-NEXT: .byte 0x5f
; IASM-NEXT: .byte 0xf0
; IASM-NEXT: .byte 0x7f
; IASM-NEXT: .byte 0xf5
; ASM-NEXT:.LtestI16Form$local$__0:
; IASM-NEXT:.LtestI16Form$local$__0:
; ASM-NEXT: ldr r0, [sp]
; DIS-NEXT: 98: e59d0000
; IASM-NEXT: .byte 0x0
; IASM-NEXT: .byte 0x0
; IASM-NEXT: .byte 0x9d
; IASM-NEXT: .byte 0xe5
; ASM-NEXT: ldrh r1, [sp, #4]
; DIS-NEXT: 9c: e1dd10b4
; IASM-NEXT: .byte 0xb4
; IASM-NEXT: .byte 0x10
; IASM-NEXT: .byte 0xdd
; IASM-NEXT: .byte 0xe1
; ASM-NEXT: uxth r1, r1
; DIS-NEXT: a0: e6ff1071
; IASM-NEXT: .byte 0x71
; IASM-NEXT: .byte 0x10
; IASM-NEXT: .byte 0xff
; IASM-NEXT: .byte 0xe6
; ***** Example of ldrexh *****
; ASM-NEXT: ldrexh r2, [r0]
; DIS-NEXT: a4: e1f02f9f
; IASM-NEXT: .byte 0x9f
; IASM-NEXT: .byte 0x2f
; IASM-NEXT: .byte 0xf0
; IASM-NEXT: .byte 0xe1
; ASM-NEXT: add r1, r2, r1
; ASM-NEXT: # r3 = def.pseudo
; DIS-NEXT: a8: e0821001
; IASM-NEXT: .byte 0x1
; IASM-NEXT: .byte 0x10
; IASM-NEXT: .byte 0x82
; IASM-NEXT: .byte 0xe0
; ***** Example of strexh *****
; ASM-NEXT: strexh r3, r1, [r0]
; DIS-NEXT: ac: e1e03f91
; IASM-NEXT: .byte 0x91
; IASM-NEXT: .byte 0x3f
; IASM-NEXT: .byte 0xe0
; IASM-NEXT: .byte 0xe1
%retval = zext i16 %v to i32
ret i32 %retval
}
define internal i32 @testI32Form(i32 %ptr, i32 %a) {
; ASM-LABEL:testI32Form:
; DIS-LABEL:000000e0 <testI32Form>:
; IASM-LABEL:testI32Form:
entry:
; ASM-NEXT:.LtestI32Form$entry:
; IASM-NEXT:.LtestI32Form$entry:
; ASM-NEXT: sub sp, sp, #20
; DIS-NEXT: e0: e24dd014
; IASM-NEXT: .byte 0x14
; IASM-NEXT: .byte 0xd0
; IASM-NEXT: .byte 0x4d
; IASM-NEXT: .byte 0xe2
; ASM-NEXT: str r0, [sp, #16]
; ASM-NEXT: # [sp, #16] = def.pseudo
; DIS-NEXT: e4: e58d0010
; IASM-NEXT: .byte 0x10
; IASM-NEXT: .byte 0x0
; IASM-NEXT: .byte 0x8d
; IASM-NEXT: .byte 0xe5
; ASM-NEXT: str r1, [sp, #12]
; ASM-NEXT: # [sp, #12] = def.pseudo
; DIS-NEXT: e8: e58d100c
; IASM-NEXT: .byte 0xc
; IASM-NEXT: .byte 0x10
; IASM-NEXT: .byte 0x8d
; IASM-NEXT: .byte 0xe5
%ptr.asptr = inttoptr i32 %ptr to i32*
%v = call i32 @llvm.nacl.atomic.rmw.i32(i32 1, i32* %ptr.asptr,
i32 %a, i32 6)
; ASM-NEXT: ldr r0, [sp, #12]
; DIS-NEXT: ec: e59d000c
; IASM-NEXT: .byte 0xc
; IASM-NEXT: .byte 0x0
; IASM-NEXT: .byte 0x9d
; IASM-NEXT: .byte 0xe5
; ASM-NEXT: str r0, [sp, #4]
; ASM-NEXT: # [sp, #4] = def.pseudo
; DIS-NEXT: f0: e58d0004
; IASM-NEXT: .byte 0x4
; IASM-NEXT: .byte 0x0
; IASM-NEXT: .byte 0x8d
; IASM-NEXT: .byte 0xe5
; ASM-NEXT: ldr r0, [sp, #16]
; DIS-NEXT: f4: e59d0010
; IASM-NEXT: .byte 0x10
; IASM-NEXT: .byte 0x0
; IASM-NEXT: .byte 0x9d
; IASM-NEXT: .byte 0xe5
; ASM-NEXT: str r0, [sp]
; ASM-NEXT: # [sp] = def.pseudo
; DIS-NEXT: f8: e58d0000
; IASM-NEXT: .byte 0x0
; IASM-NEXT: .byte 0x0
; IASM-NEXT: .byte 0x8d
; IASM-NEXT: .byte 0xe5
; ASM-NEXT: dmb sy
; DIS-NEXT: fc: f57ff05f
; IASM-NEXT: .byte 0x5f
; IASM-NEXT: .byte 0xf0
; IASM-NEXT: .byte 0x7f
; IASM-NEXT: .byte 0xf5
; ASM-NEXT:.LtestI32Form$local$__0:
; IASM-NEXT:.LtestI32Form$local$__0:
; ASM-NEXT: ldr r0, [sp]
; DIS-NEXT: 100: e59d0000
; IASM-NEXT: .byte 0x0
; IASM-NEXT: .byte 0x0
; IASM-NEXT: .byte 0x9d
; IASM-NEXT: .byte 0xe5
; ASM-NEXT: ldr r1, [sp, #4]
; DIS-NEXT: 104: e59d1004
; IASM-NEXT: .byte 0x4
; IASM-NEXT: .byte 0x10
; IASM-NEXT: .byte 0x9d
; IASM-NEXT: .byte 0xe5
; ***** Example of ldrex *****
; ASM-NEXT: ldrex r2, [r0]
; DIS-NEXT: 108: e1902f9f
; IASM-NEXT: .byte 0x9f
; IASM-NEXT: .byte 0x2f
; IASM-NEXT: .byte 0x90
; IASM-NEXT: .byte 0xe1
; ASM-NEXT: add r1, r2, r1
; ASM-NEXT: # r3 = def.pseudo
; DIS-NEXT: 10c: e0821001
; IASM-NEXT: .byte 0x1
; IASM-NEXT: .byte 0x10
; IASM-NEXT: .byte 0x82
; IASM-NEXT: .byte 0xe0
; ***** Example of strex *****
; ASM-NEXT: strex r3, r1, [r0]
; DIS-NEXT: 110: e1803f91
; IASM-NEXT: .byte 0x91
; IASM-NEXT: .byte 0x3f
; IASM-NEXT: .byte 0x80
; IASM-NEXT: .byte 0xe1
ret i32 %v
}
define internal i64 @testI64Form(i32 %ptr, i64 %a) {
; ASM-LABEL:testI64Form:
; DIS-LABEL:00000130 <testI64Form>:
; IASM-LABEL:testI64Form:
entry:
; ASM-NEXT:.LtestI64Form$entry:
; IASM-NEXT:.LtestI64Form$entry:
; ASM-NEXT: push {r4, r5}
; DIS-NEXT: 130: e92d0030
; IASM-NEXT: .byte 0x30
; IASM-NEXT: .byte 0x0
; IASM-NEXT: .byte 0x2d
; IASM-NEXT: .byte 0xe9
; ASM-NEXT: sub sp, sp, #32
; DIS-NEXT: 134: e24dd020
; IASM-NEXT: .byte 0x20
; IASM-NEXT: .byte 0xd0
; IASM-NEXT: .byte 0x4d
; IASM-NEXT: .byte 0xe2
; ASM-NEXT: str r0, [sp, #28]
; ASM-NEXT: # [sp, #28] = def.pseudo
; DIS-NEXT: 138: e58d001c
; IASM-NEXT: .byte 0x1c
; IASM-NEXT: .byte 0x0
; IASM-NEXT: .byte 0x8d
; IASM-NEXT: .byte 0xe5
; ASM-NEXT: mov r0, r2
; DIS-NEXT: 13c: e1a00002
; IASM-NEXT: .byte 0x2
; IASM-NEXT: .byte 0x0
; IASM-NEXT: .byte 0xa0
; IASM-NEXT: .byte 0xe1
; ASM-NEXT: str r0, [sp, #24]
; ASM-NEXT: # [sp, #24] = def.pseudo
; DIS-NEXT: 140: e58d0018
; IASM-NEXT: .byte 0x18
; IASM-NEXT: .byte 0x0
; IASM-NEXT: .byte 0x8d
; IASM-NEXT: .byte 0xe5
; ASM-NEXT: mov r0, r3
; DIS-NEXT: 144: e1a00003
; IASM-NEXT: .byte 0x3
; IASM-NEXT: .byte 0x0
; IASM-NEXT: .byte 0xa0
; IASM-NEXT: .byte 0xe1
; ASM-NEXT: str r0, [sp, #20]
; ASM-NEXT: # [sp, #20] = def.pseudo
; ASM-NEXT: # [sp] = def.pseudo
; DIS-NEXT: 148: e58d0014
; IASM-NEXT: .byte 0x14
; IASM-NEXT: .byte 0x0
; IASM-NEXT: .byte 0x8d
; IASM-NEXT: .byte 0xe5
%ptr.asptr = inttoptr i32 %ptr to i64*
%v = call i64 @llvm.nacl.atomic.rmw.i64(i32 1, i64* %ptr.asptr,
i64 %a, i32 6)
; ASM-NEXT: ldr r0, [sp, #24]
; DIS-NEXT: 14c: e59d0018
; IASM-NEXT: .byte 0x18
; IASM-NEXT: .byte 0x0
; IASM-NEXT: .byte 0x9d
; IASM-NEXT: .byte 0xe5
; ASM-NEXT: str r0, [sp, #8]
; ASM-NEXT: # [sp, #8] = def.pseudo
; DIS-NEXT: 150: e58d0008
; IASM-NEXT: .byte 0x8
; IASM-NEXT: .byte 0x0
; IASM-NEXT: .byte 0x8d
; IASM-NEXT: .byte 0xe5
; ASM-NEXT: ldr r0, [sp, #20]
; DIS-NEXT: 154: e59d0014
; IASM-NEXT: .byte 0x14
; IASM-NEXT: .byte 0x0
; IASM-NEXT: .byte 0x9d
; IASM-NEXT: .byte 0xe5
; ASM-NEXT: str r0, [sp, #4]
; ASM-NEXT: # [sp, #4] = def.pseudo
; DIS-NEXT: 158: e58d0004
; IASM-NEXT: .byte 0x4
; IASM-NEXT: .byte 0x0
; IASM-NEXT: .byte 0x8d
; IASM-NEXT: .byte 0xe5
; ASM-NEXT: ldr r0, [sp, #28]
; DIS-NEXT: 15c: e59d001c
; IASM-NEXT: .byte 0x1c
; IASM-NEXT: .byte 0x0
; IASM-NEXT: .byte 0x9d
; IASM-NEXT: .byte 0xe5
; ASM-NEXT: str r0, [sp]
; ASM-NEXT: # [sp] = def.pseudo
; DIS-NEXT: 160: e58d0000
; IASM-NEXT: .byte 0x0
; IASM-NEXT: .byte 0x0
; IASM-NEXT: .byte 0x8d
; IASM-NEXT: .byte 0xe5
; ASM-NEXT: dmb sy
; DIS-NEXT: 164: f57ff05f
; IASM-NEXT: .byte 0x5f
; IASM-NEXT: .byte 0xf0
; IASM-NEXT: .byte 0x7f
; IASM-NEXT: .byte 0xf5
; ASM-NEXT:.LtestI64Form$local$__0:
; IASM-NEXT:.LtestI64Form$local$__0:
; ASM-NEXT: ldr r0, [sp]
; ASM-NEXT: # r2, r3 = def.pseudo [sp]
; DIS-NEXT: 168: e59d0000
; IASM-NEXT: .byte 0x0
; IASM-NEXT: .byte 0x0
; IASM-NEXT: .byte 0x9d
; IASM-NEXT: .byte 0xe5
; ASM-NEXT: ldr r1, [sp, #8]
; DIS-NEXT: 16c: e59d1008
; IASM-NEXT: .byte 0x8
; IASM-NEXT: .byte 0x10
; IASM-NEXT: .byte 0x9d
; IASM-NEXT: .byte 0xe5
; ASM-NEXT: mov r2, r1
; DIS-NEXT: 170: e1a02001
; IASM-NEXT: .byte 0x1
; IASM-NEXT: .byte 0x20
; IASM-NEXT: .byte 0xa0
; IASM-NEXT: .byte 0xe1
; ASM-NEXT: ldr r1, [sp, #4]
; DIS-NEXT: 174: e59d1004
; IASM-NEXT: .byte 0x4
; IASM-NEXT: .byte 0x10
; IASM-NEXT: .byte 0x9d
; IASM-NEXT: .byte 0xe5
; ASM-NEXT: mov r3, r1
; DIS-NEXT: 178: e1a03001
; IASM-NEXT: .byte 0x1
; IASM-NEXT: .byte 0x30
; IASM-NEXT: .byte 0xa0
; IASM-NEXT: .byte 0xe1
; ***** Example of ldrexd *****
; ASM-NEXT: ldrexd r4, r5, [r0]
; ASM-NEXT: # r4 = def.pseudo r4, r5
; ASM-NEXT: # r5 = def.pseudo r4, r5
; ASM-NEXT: # r2, r3 = def.pseudo r2, r3
; DIS-NEXT: 17c: e1b04f9f
; IASM-NEXT: .byte 0x9f
; IASM-NEXT: .byte 0x4f
; IASM-NEXT: .byte 0xb0
; IASM-NEXT: .byte 0xe1
; ASM-NEXT: adds r2, r4, r2
; DIS-NEXT: 180: e0942002
; IASM-NEXT: .byte 0x2
; IASM-NEXT: .byte 0x20
; IASM-NEXT: .byte 0x94
; IASM-NEXT: .byte 0xe0
; ASM-NEXT: adc r3, r5, r3
; ASM-NEXT: # r1 = def.pseudo
; DIS-NEXT: 184: e0a53003
; IASM-NEXT: .byte 0x3
; IASM-NEXT: .byte 0x30
; IASM-NEXT: .byte 0xa5
; IASM-NEXT: .byte 0xe0
; ***** Example of strexd *****
; ASM-NEXT: strexd r1, r2, r3, [r0]
; DIS-NEXT: 188: e1a01f92
; IASM-NEXT: .byte 0x92
; IASM-NEXT: .byte 0x1f
; IASM-NEXT: .byte 0xa0
; IASM-NEXT: .byte 0xe1
ret i64 %v
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment