Add LSL (register, immediate) to ARM integrated assembler.
Also does some clean up on emitType01 methods (making optional argument
explicit, and moving rule checks to the lowest level).
BUG= https://bugs.chromium.org/p/nativeclient/issues/detail?id=4334
R=jpp@chromium.org
Review URL: https://codereview.chromium.org/1456783003 .
diff --git a/src/DartARM32/assembler_arm.cc b/src/DartARM32/assembler_arm.cc
index 8f1a3b4..e99f70b 100644
--- a/src/DartARM32/assembler_arm.cc
+++ b/src/DartARM32/assembler_arm.cc
@@ -2596,7 +2596,8 @@
}
}
-
+#if 0
+// Moved to ARM32::AssemblerARM32::lsl()
void Assembler::Lsl(Register rd, Register rm, const Operand& shift_imm,
Condition cond) {
ASSERT(shift_imm.type() == 1);
@@ -2604,10 +2605,11 @@
mov(rd, Operand(rm, LSL, shift_imm.encoding()), cond);
}
-
+// Moved to ARM32::AssemblerARM32::lsl()
void Assembler::Lsl(Register rd, Register rm, Register rs, Condition cond) {
mov(rd, Operand(rm, LSL, rs), cond);
}
+#endif
void Assembler::Lsr(Register rd, Register rm, const Operand& shift_imm,
diff --git a/src/DartARM32/assembler_arm.h b/src/DartARM32/assembler_arm.h
index fd4c000..688104b 100644
--- a/src/DartARM32/assembler_arm.h
+++ b/src/DartARM32/assembler_arm.h
@@ -158,7 +158,7 @@
encoding_ = static_cast<uint32_t>(rm);
}
- // Moved to encodeShiftRotateImm5()
+ // Moved to encodeShiftRotateImm5() in IceAssemblerARM32.cpp
// Data-processing operands - Logical shift/rotate by immediate.
Operand(Register rm, Shift shift, uint32_t shift_imm) {
ASSERT(shift_imm < (1 << kShiftImmBits));
@@ -167,8 +167,8 @@
static_cast<uint32_t>(shift) << kShiftShift |
static_cast<uint32_t>(rm);
}
-#endif
+ // Moved to encodeShiftRotateReg() in IceAssemblerARM32.cpp
// Data-processing operands - Logical shift/rotate by register.
Operand(Register rm, Shift shift, Register rs) {
type_ = 0;
@@ -177,7 +177,6 @@
static_cast<uint32_t>(rm);
}
-#if 0
// Already defined as ARM32::OperandARM32FlexImm::canHoldImm().
static bool CanHold(uint32_t immediate, Operand* o) {
// Avoid the more expensive test for frequent small immediate values.
@@ -956,9 +955,13 @@
// Convenience shift instructions. Use mov instruction with shifter operand
// for variants setting the status flags.
+#if 0
+ // Moved to ARM32::AssemblerARM32::lsl()
void Lsl(Register rd, Register rm, const Operand& shift_imm,
Condition cond = AL);
+ // Moved to ARM32::AssemblerARM32::lsl()
void Lsl(Register rd, Register rm, Register rs, Condition cond = AL);
+#endif
void Lsr(Register rd, Register rm, const Operand& shift_imm,
Condition cond = AL);
void Lsr(Register rd, Register rm, Register rs, Condition cond = AL);
diff --git a/src/IceAssemblerARM32.cpp b/src/IceAssemblerARM32.cpp
index a428664..08ec155 100644
--- a/src/IceAssemblerARM32.cpp
+++ b/src/IceAssemblerARM32.cpp
@@ -100,6 +100,7 @@
// Type of instruction encoding (bits 25-27). See ARM section A5.1
static constexpr IValueT kInstTypeDataRegister = 0; // i.e. 000
+static constexpr IValueT kInstTypeDataRegShift = 0; // i.e. 000
static constexpr IValueT kInstTypeDataImmediate = 1; // i.e. 001
static constexpr IValueT kInstTypeMemImmediate = 2; // i.e. 010
static constexpr IValueT kInstTypeRegisterShift = 3; // i.e. 011
@@ -195,6 +196,8 @@
// kind, p=1 if pre-indexed addressing, u=1 if offset positive, and w=1 if
// writeback to Rn.
DecodedAsShiftRotateImm5,
+ // i.e. 000000000000000000000iiiii0000000 iiii defines Imm5 value to shift.
+ DecodedAsShiftImm5,
// Value is 32bit integer constant.
DecodedAsConstI32
};
@@ -215,13 +218,23 @@
return (imm5 << kShiftImmShift) | (encodeShift(Shift) << kShiftShift) | Rm;
}
+// Encodes mmmmtt01ssss for data-processing (2nd) operands where mmmm=Rm,
+// ssss=Rs, and tt=Shift.
+IValueT encodeShiftRotateReg(IValueT Rm, OperandARM32::ShiftKind Shift,
+ IValueT Rs) {
+ return (Rs << kRsShift) | (encodeShift(Shift) << kShiftShift) | B4 |
+ (Rm << kRmShift);
+}
+
DecodedResult decodeOperand(const Operand *Opnd, IValueT &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)) {
+ return CantDecode;
+ }
+ if (const auto *FlexImm = llvm::dyn_cast<OperandARM32FlexImm>(Opnd)) {
const IValueT Immed8 = FlexImm->getImm();
const IValueT Rotate = FlexImm->getRotateAmt();
if (!((Rotate < (1 << kRotateBits)) && (Immed8 < (1 << kImmed8Bits))))
@@ -233,6 +246,12 @@
Value = Const->getValue();
return DecodedAsConstI32;
}
+ if (const auto *ShImm = llvm::dyn_cast<OperandARM32ShAmtImm>(Opnd)) {
+ const IValueT Immed5 = ShImm->getShAmtImm();
+ assert(Immed5 < (1 << kShiftImmBits));
+ Value = (Immed5 << kShiftImmShift);
+ return DecodedAsShiftImm5;
+ }
return CantDecode;
}
@@ -423,22 +442,33 @@
}
void AssemblerARM32::emitType01(CondARM32::Cond Cond, IValueT Type,
- IValueT Opcode, bool SetCc, IValueT Rn,
- IValueT Rd, IValueT Imm12) {
+ IValueT Opcode, bool SetFlags, IValueT Rn,
+ IValueT Rd, IValueT Imm12,
+ EmitChecks RuleChecks) {
+ switch (RuleChecks) {
+ case NoChecks:
+ break;
+ case RdIsPcAndSetFlags:
+ if (((Rd == RegARM32::Encoded_Reg_pc) && SetFlags))
+ // Conditions of rule violated.
+ return setNeedsTextFixup();
+ break;
+ }
+
if (!isGPRRegisterDefined(Rd) || !isConditionDefined(Cond))
return setNeedsTextFixup();
AssemblerBuffer::EnsureCapacity ensured(&Buffer);
const IValueT Encoding = (encodeCondition(Cond) << kConditionShift) |
(Type << kTypeShift) | (Opcode << kOpcodeShift) |
- (encodeBool(SetCc) << kSShift) | (Rn << kRnShift) |
- (Rd << kRdShift) | Imm12;
+ (encodeBool(SetFlags) << kSShift) |
+ (Rn << kRnShift) | (Rd << kRdShift) | Imm12;
emitInst(Encoding);
}
void AssemblerARM32::emitType01(IValueT Opcode, const Operand *OpRd,
const Operand *OpRn, const Operand *OpSrc1,
bool SetFlags, CondARM32::Cond Cond,
- Type01Checks RuleChecks) {
+ EmitChecks RuleChecks) {
IValueT Rd;
if (decodeOperand(OpRd, Rd) != DecodedAsRegister)
return setNeedsTextFixup();
@@ -450,7 +480,7 @@
void AssemblerARM32::emitType01(IValueT Opcode, IValueT Rd, IValueT Rn,
const Operand *OpSrc1, bool SetFlags,
- CondARM32::Cond Cond, Type01Checks RuleChecks) {
+ CondARM32::Cond Cond, EmitChecks RuleChecks) {
switch (RuleChecks) {
case NoChecks:
break;
@@ -474,8 +504,8 @@
// mmmm=Rm, iiiii=Shift, tt=ShiftKind, and s=SetFlags.
constexpr IValueT Imm5 = 0;
Src1Value = encodeShiftRotateImm5(Src1Value, OperandARM32::kNoShift, Imm5);
- emitType01(Cond, kInstTypeDataRegister, Opcode, SetFlags, Rn, Rd,
- Src1Value);
+ emitType01(Cond, kInstTypeDataRegister, Opcode, SetFlags, Rn, Rd, Src1Value,
+ RuleChecks);
return;
}
case DecodedAsConstI32: {
@@ -494,7 +524,7 @@
// cccc0010100snnnnddddiiiiiiiiiiii where cccc=Cond, dddd=Rd, nnnn=Rn,
// s=SetFlags and iiiiiiiiiiii=Src1Value defining RotatedImm8.
emitType01(Cond, kInstTypeDataImmediate, Opcode, SetFlags, Rn, Rd,
- Src1Value);
+ Src1Value, RuleChecks);
return;
}
}
@@ -577,14 +607,15 @@
}
void AssemblerARM32::emitMulOp(CondARM32::Cond Cond, IValueT Opcode, IValueT Rd,
- IValueT Rn, IValueT Rm, IValueT Rs, bool SetCc) {
+ IValueT Rn, IValueT Rm, IValueT Rs,
+ bool SetFlags) {
if (!isGPRRegisterDefined(Rd) || !isGPRRegisterDefined(Rn) ||
!isGPRRegisterDefined(Rm) || !isGPRRegisterDefined(Rs) ||
!isConditionDefined(Cond))
return setNeedsTextFixup();
AssemblerBuffer::EnsureCapacity ensured(&Buffer);
IValueT Encoding = Opcode | (encodeCondition(Cond) << kConditionShift) |
- (encodeBool(SetCc) << kSShift) | (Rn << kRnShift) |
+ (encodeBool(SetFlags) << kSShift) | (Rn << kRnShift) |
(Rd << kRdShift) | (Rs << kRsShift) | B7 | B4 |
(Rm << kRmShift);
emitInst(Encoding);
@@ -631,7 +662,7 @@
// cccc0010101snnnnddddiiiiiiiiiiii where cccc=Cond, dddd=Rd, nnnn=Rn,
// s=SetFlags and iiiiiiiiiiii=Src1Value defining RotatedImm8.
constexpr IValueT Adc = B2 | B0; // 0101
- emitType01(Adc, OpRd, OpRn, OpSrc1, SetFlags, Cond);
+ emitType01(Adc, OpRd, OpRn, OpSrc1, SetFlags, Cond, RdIsPcAndSetFlags);
}
void AssemblerARM32::add(const Operand *OpRd, const Operand *OpRn,
@@ -653,7 +684,7 @@
// cccc0010100snnnnddddiiiiiiiiiiii where cccc=Cond, dddd=Rd, nnnn=Rn,
// s=SetFlags and iiiiiiiiiiii=Src1Value defining RotatedImm8.
constexpr IValueT Add = B2; // 0100
- emitType01(Add, OpRd, OpRn, OpSrc1, SetFlags, Cond);
+ emitType01(Add, OpRd, OpRn, OpSrc1, SetFlags, Cond, RdIsPcAndSetFlags);
}
void AssemblerARM32::and_(const Operand *OpRd, const Operand *OpRn,
@@ -671,7 +702,7 @@
// cccc0010100snnnnddddiiiiiiiiiiii where cccc=Cond, dddd=Rd, nnnn=Rn,
// s=SetFlags and iiiiiiiiiiii=Src1Value defining RotatedImm8.
constexpr IValueT And = 0; // 0000
- emitType01(And, OpRd, OpRn, OpSrc1, SetFlags, Cond);
+ emitType01(And, OpRd, OpRn, OpSrc1, SetFlags, Cond, RdIsPcAndSetFlags);
}
void AssemblerARM32::b(Label *L, CondARM32::Cond Cond) {
@@ -704,7 +735,7 @@
// cccc0011110snnnnddddiiiiiiiiiiii where cccc=Cond, dddd=Rn, nnnn=Rn,
// s=SetFlags, and iiiiiiiiiiii=Src1Value defining RotatedImm8.
IValueT Opcode = B3 | B2 | B1; // i.e. 1110
- emitType01(Opcode, OpRd, OpRn, OpSrc1, SetFlags, Cond);
+ emitType01(Opcode, OpRd, OpRn, OpSrc1, SetFlags, Cond, RdIsPcAndSetFlags);
}
void AssemblerARM32::bl(const ConstantRelocatable *Target) {
@@ -785,7 +816,7 @@
// cccc0010001snnnnddddiiiiiiiiiiii where cccc=Cond, dddd=Rd, nnnn=Rn,
// s=SetFlags and iiiiiiiiiiii=Src1Value defining RotatedImm8.
constexpr IValueT Eor = B0; // 0001
- emitType01(Eor, OpRd, OpRn, OpSrc1, SetFlags, Cond);
+ emitType01(Eor, OpRd, OpRn, OpSrc1, SetFlags, Cond, RdIsPcAndSetFlags);
}
void AssemblerARM32::ldr(const Operand *OpRt, const Operand *OpAddress,
@@ -868,6 +899,51 @@
}
}
+void AssemblerARM32::lsl(const Operand *OpRd, const Operand *OpRm,
+ const Operand *OpSrc1, bool SetFlags,
+ CondARM32::Cond Cond) {
+ constexpr IValueT Lsl = B3 | B2 | B0; // 1101
+ constexpr IValueT Rn = 0; // Rn field is not used.
+ IValueT Rd;
+ if (decodeOperand(OpRd, Rd) != DecodedAsRegister)
+ return setNeedsTextFixup();
+ IValueT Rm;
+ if (decodeOperand(OpRm, Rm) != DecodedAsRegister)
+ return setNeedsTextFixup();
+ IValueT Value;
+ switch (decodeOperand(OpSrc1, Value)) {
+ default:
+ return setNeedsTextFixup();
+ case DecodedAsShiftImm5: {
+ // LSL (immediate) - ARM section A8.8.94, encoding A1:
+ // lsl{s}<c> <Rd>, <Rm>, #imm5
+ //
+ // cccc0001101s0000ddddiiiii000mmmm where cccc=Cond, s=SetFlags, dddd=Rd,
+ // iiiii=imm5, and mmmm=Rm.
+ Value = Value | (Rm << kRmShift);
+ emitType01(Cond, kInstTypeDataRegShift, Lsl, SetFlags, Rn, Rd, Value,
+ RdIsPcAndSetFlags);
+ return;
+ }
+ case DecodedAsRegister: {
+ // LSL (register) - ARM section A8.8.95, encoding A1:
+ // lsl{S}<c> <Rd>, <Rm>, <Rs>
+ //
+ // cccc0001101s0000ddddssss0001mmmm where cccc=Cond, s=SetFlags, dddd=Rd,
+ // mmmm=Rm, and ssss=Rs.
+ IValueT Rs;
+ if (decodeOperand(OpSrc1, Rs) != DecodedAsRegister)
+ return setNeedsTextFixup();
+ if ((Rd == RegARM32::Encoded_Reg_pc) || (Rm == RegARM32::Encoded_Reg_pc) ||
+ (Rs == RegARM32::Encoded_Reg_pc))
+ setNeedsTextFixup();
+ emitType01(Cond, kInstTypeDataRegShift, Lsl, SetFlags, Rn, Rd,
+ encodeShiftRotateReg(Rm, OperandARM32::kNoShift, Rs), NoChecks);
+ return;
+ }
+ }
+}
+
void AssemblerARM32::mov(const Operand *OpRd, const Operand *OpSrc,
CondARM32::Cond Cond) {
// MOV (register) - ARM section A8.8.104, encoding A1:
@@ -888,7 +964,7 @@
constexpr bool SetFlags = false;
constexpr IValueT Rn = 0;
constexpr IValueT Mov = B3 | B2 | B0; // 1101.
- emitType01(Mov, Rd, Rn, OpSrc, SetFlags, Cond);
+ emitType01(Mov, Rd, Rn, OpSrc, SetFlags, Cond, RdIsPcAndSetFlags);
}
void AssemblerARM32::emitMovw(IValueT Opcode, IValueT Rd, IValueT Imm16,
@@ -986,7 +1062,7 @@
// cccc0010110snnnnddddiiiiiiiiiiii where cccc=Cond, dddd=Rd, nnnn=Rn,
// s=SetFlags and iiiiiiiiiiii=Src1Value defining RotatedImm8.
constexpr IValueT Sbc = B2 | B1; // 0110
- emitType01(Sbc, OpRd, OpRn, OpSrc1, SetFlags, Cond);
+ emitType01(Sbc, OpRd, OpRn, OpSrc1, SetFlags, Cond, RdIsPcAndSetFlags);
}
void AssemblerARM32::sdiv(const Operand *OpRd, const Operand *OpRn,
@@ -1064,7 +1140,7 @@
// cccc0001100snnnnddddiiiiiiiiiiii where cccc=Cond, dddd=Rd, nnnn=Rn,
// s=SetFlags and iiiiiiiiiiii=Src1Value defining RotatedImm8.
constexpr IValueT Orr = B3 | B2; // i.e. 1100
- emitType01(Orr, OpRd, OpRn, OpSrc1, SetFlags, Cond);
+ emitType01(Orr, OpRd, OpRn, OpSrc1, SetFlags, Cond, RdIsPcAndSetFlags);
}
void AssemblerARM32::pop(const Operand *OpRt, CondARM32::Cond Cond) {
@@ -1218,7 +1294,7 @@
// cccc0010010snnnnddddiiiiiiiiiiii where cccc=Cond, dddd=Rd, nnnn=Rn,
// s=SetFlags and iiiiiiiiiiii=Src1Value defining RotatedImm8
constexpr IValueT Sub = B1; // 0010
- emitType01(Sub, OpRd, OpRn, OpSrc1, SetFlags, Cond);
+ emitType01(Sub, OpRd, OpRn, OpSrc1, SetFlags, Cond, RdIsPcAndSetFlags);
}
void AssemblerARM32::tst(const Operand *OpRn, const Operand *OpSrc1,
diff --git a/src/IceAssemblerARM32.h b/src/IceAssemblerARM32.h
index 8de4e25..4372085 100644
--- a/src/IceAssemblerARM32.h
+++ b/src/IceAssemblerARM32.h
@@ -223,6 +223,9 @@
ldr(OpRt, OpAddress, Cond, TInfo);
}
+ void lsl(const Operand *OpRd, const Operand *OpRn, const Operand *OpSrc1,
+ bool SetFlags, CondARM32::Cond Cond);
+
void mov(const Operand *OpRd, const Operand *OpSrc, CondARM32::Cond Cond);
void movw(const Operand *OpRd, const Operand *OpSrc, CondARM32::Cond Cond);
@@ -312,28 +315,27 @@
void emitInst(IValueT Value) { Buffer.emit<IValueT>(Value); }
- // Pattern cccctttoooosnnnnddddiiiiiiiiiiii where cccc=Cond, ttt=Type,
- // oooo=Opcode, nnnn=Rn, dddd=Rd, iiiiiiiiiiii=imm12 (See ARM section A5.2.3).
- void emitType01(CondARM32::Cond Cond, IValueT Type, IValueT Opcode,
- bool SetCc, IValueT Rn, IValueT Rd, IValueT imm12);
-
// List of possible checks to apply when calling emitType01() (below).
- enum Type01Checks {
- NoChecks,
- RdIsPcAndSetFlags,
- };
+ enum EmitChecks { NoChecks, RdIsPcAndSetFlags };
+
+ // Pattern cccctttoooosnnnnddddiiiiiiiiiiii where cccc=Cond, ttt=Type,
+ // s=SetFlags, oooo=Opcode, nnnn=Rn, dddd=Rd, iiiiiiiiiiii=imm12 (See ARM
+ // section A5.2.3).
+ void emitType01(CondARM32::Cond Cond, IValueT Type, IValueT Opcode,
+ bool SetFlags, IValueT Rn, IValueT Rd, IValueT imm12,
+ EmitChecks RuleChecks);
// Converts appropriate representation on a data operation, and then calls
// emitType01 above.
void emitType01(IValueT Opcode, const Operand *OpRd, const Operand *OpRn,
const Operand *OpSrc1, bool SetFlags, CondARM32::Cond Cond,
- Type01Checks RuleChecks = RdIsPcAndSetFlags);
+ EmitChecks RuleChecks);
// Same as above, but the value for Rd and Rn have already been converted
// into instruction values.
void emitType01(IValueT Opcode, IValueT OpRd, IValueT OpRn,
const Operand *OpSrc1, bool SetFlags, CondARM32::Cond Cond,
- Type01Checks RuleChecks = RdIsPcAndSetFlags);
+ EmitChecks RuleChecks);
void emitType05(CondARM32::Cond COnd, int32_t Offset, bool Link);
@@ -356,9 +358,9 @@
IValueT Rm);
// Pattern ccccxxxxxxxfnnnnddddssss1001mmmm where cccc=Cond, dddd=Rd, nnnn=Rn,
- // mmmm=Rm, ssss=Rs, f=SetCc, and xxxxxxx=Opcode.
+ // mmmm=Rm, ssss=Rs, f=SetFlags and xxxxxxx=Opcode.
void emitMulOp(CondARM32::Cond Cond, IValueT Opcode, IValueT Rd, IValueT Rn,
- IValueT Rm, IValueT Rs, bool SetCc);
+ IValueT Rm, IValueT Rs, bool SetFlags);
// Implements various forms of Unsigned extend value, using pattern
// ccccxxxxxxxxnnnnddddrr000111mmmm where cccc=Cond, xxxxxxxx<<20=Opcode,
diff --git a/src/IceInstARM32.cpp b/src/IceInstARM32.cpp
index 401b381..76fceb1 100644
--- a/src/IceInstARM32.cpp
+++ b/src/IceInstARM32.cpp
@@ -485,6 +485,13 @@
emitUsingTextFixup(Func);
}
+template <> void InstARM32Lsl::emitIAS(const Cfg *Func) const {
+ auto *Asm = Func->getAssembler<ARM32::AssemblerARM32>();
+ Asm->lsl(getDest(), getSrc(0), getSrc(1), SetFlags, getPredicate());
+ if (Asm->needsTextFixup())
+ emitUsingTextFixup(Func);
+}
+
template <> void InstARM32Orr::emitIAS(const Cfg *Func) const {
auto *Asm = Func->getAssembler<ARM32::AssemblerARM32>();
Asm->orr(getDest(), getSrc(0), getSrc(1), SetFlags, getPredicate());
diff --git a/tests_lit/assembler/arm32/lsl.ll b/tests_lit/assembler/arm32/lsl.ll
new file mode 100644
index 0000000..956c47c
--- /dev/null
+++ b/tests_lit/assembler/arm32/lsl.ll
@@ -0,0 +1,83 @@
+; Show that we know how to translate lsl.
+
+; NOTE: We use -O2 to get rid of memory stores.
+
+; REQUIRES: allow_dump
+
+; Compile using standalone assembler.
+; RUN: %p2i --filetype=asm -i %s --target=arm32 --args -O2 \
+; RUN: | FileCheck %s --check-prefix=ASM
+
+; Show bytes in assembled standalone code.
+; RUN: %p2i --filetype=asm -i %s --target=arm32 --assemble --disassemble \
+; RUN: --args -O2 | FileCheck %s --check-prefix=DIS
+
+; Compile using integrated assembler.
+; RUN: %p2i --filetype=iasm -i %s --target=arm32 --args -O2 \
+; RUN: | FileCheck %s --check-prefix=IASM
+
+; Show bytes in assembled integrated code.
+; RUN: %p2i --filetype=iasm -i %s --target=arm32 --assemble --disassemble \
+; RUN: --args -O2 | FileCheck %s --check-prefix=DIS
+
+define internal i32 @_Z8testUdivhh(i32 %a, i32 %b) {
+
+; ASM-LABEL:_Z8testUdivhh:
+; DIS-LABEL:00000000 <_Z8testUdivhh>:
+; IASM-LABEL:_Z8testUdivhh:
+
+entry:
+
+; ASM-NEXT:.L_Z8testUdivhh$entry:
+; ASM-NEXT: push {lr}
+; DIS-NEXT: 0: e52de004
+; IASM-NEXT:.L_Z8testUdivhh$entry:
+; IASM-NEXT: .byte 0x4
+; IASM-NEXT: .byte 0xe0
+; IASM-NEXT: .byte 0x2d
+; IASM-NEXT: .byte 0xe5
+
+ %b.arg_trunc = trunc i32 %b to i8
+ %a.arg_trunc = trunc i32 %a to i8
+ %div3 = udiv i8 %a.arg_trunc, %b.arg_trunc
+
+; ASM-NEXT: sub sp, sp, #12
+; DIS-NEXT: 4: e24dd00c
+; IASM-NEXT: .byte 0xc
+; IASM-NEXT: .byte 0xd0
+; IASM-NEXT: .byte 0x4d
+; IASM-NEXT: .byte 0xe2
+
+; ASM-NEXT: lsls r2, r1, #24
+; DIS-NEXT: 8: e1b02c01
+; IASM-NEXT: .byte 0x1
+; IASM-NEXT: .byte 0x2c
+; IASM-NEXT: .byte 0xb0
+; IASM-NEXT: .byte 0xe1
+
+ %div3.ret_ext = zext i8 %div3 to i32
+ ret i32 %div3.ret_ext
+}
+
+define internal i32 @_Z7testShljj(i32 %a, i32 %b) {
+
+; ASM-LABEL:_Z7testShljj:
+; DIS-LABEL:00000030 <_Z7testShljj>:
+; IASM-LABEL:_Z7testShljj:
+
+entry:
+
+; ASM-NEXT:.L_Z7testShljj$entry:
+; IASM-NEXT:.L_Z7testShljj$entry:
+
+ %shl = shl i32 %a, %b
+
+; ASM-NEXT: lsl r0, r0, r1
+; DIS-NEXT: 30: e1a00110
+; IASM-NEXT: .byte 0x10
+; IASM-NEXT: .byte 0x1
+; IASM-NEXT: .byte 0xa0
+; IASM-NEXT: .byte 0xe1
+
+ ret i32 %shl
+}