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
+}