Add Sxtb/Sxth instructions to ARM integrated assembler.

Refactors code to take advantage of these instructions with
Uxtb/Uxth.

Note. These instructions are used by Subzero, but not Dart.

BUG= https://bugs.chromium.org/p/nativeclient/issues/detail?id=4334
R=stichnot@chromium.org

Review URL: https://codereview.chromium.org/1503273002 .
diff --git a/src/IceAssemblerARM32.cpp b/src/IceAssemblerARM32.cpp
index d06ee7b..0d0537c 100644
--- a/src/IceAssemblerARM32.cpp
+++ b/src/IceAssemblerARM32.cpp
@@ -835,21 +835,6 @@
   emitInst(Encoding);
 }
 
-void AssemblerARM32::emitUxt(CondARM32::Cond Cond, IValueT Opcode, IValueT Rd,
-                             IValueT Rn, IValueT Rm, RotationValue Rotation,
-                             const char *InstName) {
-  verifyCondDefined(Cond, InstName);
-  IValueT Rot = encodeRotation(Rotation);
-  if (!Utils::IsUint(2, Rot))
-    llvm::report_fatal_error(std::string(InstName) +
-                             ": Illegal rotation value");
-  AssemblerBuffer::EnsureCapacity ensured(&Buffer);
-  IValueT Encoding = (encodeCondition(Cond) << kConditionShift) | Opcode |
-                     (Rn << kRnShift) | (Rd << kRdShift) |
-                     (Rot << kRotationShift) | B6 | B5 | B4 | (Rm << kRmShift);
-  emitInst(Encoding);
-}
-
 void AssemblerARM32::emitMultiMemOp(CondARM32::Cond Cond,
                                     BlockAddressMode AddressMode, bool IsLoad,
                                     IValueT BaseReg, IValueT Registers,
@@ -867,6 +852,51 @@
   emitInst(Encoding);
 }
 
+void AssemblerARM32::emitSignExtend(CondARM32::Cond Cond, IValueT Opcode,
+                                    const Operand *OpRd, const Operand *OpSrc0,
+                                    const char *InstName) {
+  IValueT Rd = encodeRegister(OpRd, "Rd", InstName);
+  IValueT Rm = encodeRegister(OpSrc0, "Rm", InstName);
+  // Note: For the moment, we assume no rotation is specified.
+  RotationValue Rotation = kRotateNone;
+  constexpr IValueT Rn = RegARM32::Encoded_Reg_pc;
+  switch (typeWidthInBytes(OpSrc0->getType())) {
+  default:
+    llvm::report_fatal_error(std::string(InstName) +
+                             ": Type of Rm not understood");
+    break;
+  case 1: {
+    // SXTB/UXTB - Arm sections A8.8.233 and A8.8.274, encoding A1:
+    //   sxtb<c> <Rd>, <Rm>{, <rotate>}
+    //   uxtb<c> <Rd>, <Rm>{, <rotate>}
+    //
+    // ccccxxxxxxxx1111ddddrr000111mmmm where cccc=Cond, xxxxxxxx<<20=Opcode,
+    // dddd=Rd, mmmm=Rm, and rr defined (RotationValue) rotate.
+    break;
+  }
+  case 2: {
+    // SXTH/UXTH - ARM sections A8.8.235 and A8.8.276, encoding A1:
+    //   uxth<c> <Rd>< <Rm>{, <rotate>}
+    //
+    // cccc01101111nnnnddddrr000111mmmm where cccc=Cond, dddd=Rd, mmmm=Rm, and
+    // rr defined (RotationValue) rotate.
+    Opcode |= B20;
+    break;
+  }
+  }
+
+  verifyCondDefined(Cond, InstName);
+  IValueT Rot = encodeRotation(Rotation);
+  if (!Utils::IsUint(2, Rot))
+    llvm::report_fatal_error(std::string(InstName) +
+                             ": Illegal rotation value");
+  AssemblerBuffer::EnsureCapacity ensured(&Buffer);
+  IValueT Encoding = (encodeCondition(Cond) << kConditionShift) | Opcode |
+                     (Rn << kRnShift) | (Rd << kRdShift) |
+                     (Rot << kRotationShift) | B6 | B5 | B4 | (Rm << kRmShift);
+  emitInst(Encoding);
+}
+
 void AssemblerARM32::adc(const Operand *OpRd, const Operand *OpRn,
                          const Operand *OpSrc1, bool SetFlags,
                          CondARM32::Cond Cond) {
@@ -1510,6 +1540,13 @@
              RsbName);
 }
 
+void AssemblerARM32::sxt(const Operand *OpRd, const Operand *OpSrc0,
+                         CondARM32::Cond Cond) {
+  constexpr const char *SxtName = "sxt";
+  constexpr IValueT SxtOpcode = B26 | B25 | B23 | B21;
+  emitSignExtend(Cond, SxtOpcode, OpRd, OpSrc0, SxtName);
+}
+
 void AssemblerARM32::sub(const Operand *OpRd, const Operand *OpRn,
                          const Operand *OpSrc1, bool SetFlags,
                          CondARM32::Cond Cond) {
@@ -1597,35 +1634,8 @@
 void AssemblerARM32::uxt(const Operand *OpRd, const Operand *OpSrc0,
                          CondARM32::Cond Cond) {
   constexpr const char *UxtName = "uxt";
-  IValueT Rd = encodeRegister(OpRd, "Rd", UxtName);
-  IValueT Rm = encodeRegister(OpSrc0, "Rm", UxtName);
-  // Note: For the moment, we assume no rotation is specified.
-  RotationValue Rotation = kRotateNone;
-  constexpr IValueT Rn = RegARM32::Encoded_Reg_pc;
-  switch (typeWidthInBytes(OpSrc0->getType())) {
-  default:
-    llvm::report_fatal_error("Type of Rm not understood: uxt");
-  case 1: {
-    // UXTB - ARM section A8.8.274, encoding A1:
-    //   uxtb<c> <Rd>, <Rm>{, <rotate>}
-    //
-    // cccc011011101111ddddrr000111mmmm where cccc=Cond, dddd=Rd, mmmm=Rm, and
-    // rr defined (RotationValue) rotate.
-    constexpr IValueT UxtOpcode = B26 | B25 | B23 | B22 | B21;
-    emitUxt(Cond, UxtOpcode, Rd, Rn, Rm, Rotation, UxtName);
-    return;
-  }
-  case 2: {
-    // UXTH - ARM section A8.8.276, encoding A1:
-    //   uxth<c> <Rd>< <Rm>{, <rotate>}
-    //
-    // cccc01101111nnnnddddrr000111mmmm where cccc=Cond, dddd=Rd, mmmm=Rm, and
-    // rr defined (RotationValue) rotate.
-    constexpr IValueT UxtOpcode = B26 | B25 | B23 | B22 | B21 | B20;
-    emitUxt(Cond, UxtOpcode, Rd, Rn, Rm, Rotation, UxtName);
-    return;
-  }
-  }
+  constexpr IValueT UxtOpcode = B26 | B25 | B23 | B22 | B21;
+  emitSignExtend(Cond, UxtOpcode, OpRd, OpSrc0, UxtName);
 }
 
 } // end of namespace ARM32
diff --git a/src/IceAssemblerARM32.h b/src/IceAssemblerARM32.h
index 705bfe0..0ac8bc7 100644
--- a/src/IceAssemblerARM32.h
+++ b/src/IceAssemblerARM32.h
@@ -273,6 +273,9 @@
   void sub(const Operand *OpRd, const Operand *OpRn, const Operand *OpSrc1,
            bool SetFlags, CondARM32::Cond Cond);
 
+  // Implements sxtb/sxth depending on type of OpSrc0.
+  void sxt(const Operand *OpRd, const Operand *OpSrc0, CondARM32::Cond Cond);
+
   void tst(const Operand *OpRn, const Operand *OpSrc1, CondARM32::Cond Cond);
 
   void udiv(const Operand *OpRd, const Operand *OpRn, const Operand *OpSrc1,
@@ -387,11 +390,11 @@
                  const Operand *OpRm, const Operand *OpSrc1,
                  const bool SetFlags, const char *InstName);
 
-  // Implements various forms of Unsigned extend value, using pattern
+  // Implements various forms of signed/unsigned extend value, using pattern
   // ccccxxxxxxxxnnnnddddrr000111mmmm where cccc=Cond, xxxxxxxx<<20=Opcode,
   // nnnn=Rn, dddd=Rd, rr=Rotation, and mmmm=Rm.
-  void emitUxt(CondARM32::Cond, IValueT Opcode, IValueT Rd, IValueT Rn,
-               IValueT Rm, RotationValue Rotation, const char *InstName);
+  void emitSignExtend(CondARM32::Cond, IValueT Opcode, const Operand *OpRd,
+                      const Operand *OpSrc0, const char *InstName);
 
   // Pattern cccctttxxxxnnnn0000iiiiiiiiiiii where cccc=Cond, nnnn=Rn,
   // ttt=Instruction type (derived from OpSrc1), iiiiiiiiiiii is derived from
diff --git a/src/IceInstARM32.cpp b/src/IceInstARM32.cpp
index 2590a10..7fc5883 100644
--- a/src/IceInstARM32.cpp
+++ b/src/IceInstARM32.cpp
@@ -1185,6 +1185,14 @@
     emitUsingTextFixup(Func);
 }
 
+template <> void InstARM32Sxt::emitIAS(const Cfg *Func) const {
+  assert(getSrcSize() == 1);
+  auto *Asm = Func->getAssembler<ARM32::AssemblerARM32>();
+  Asm->sxt(getDest(), getSrc(0), getPredicate());
+  if (Asm->needsTextFixup())
+    emitUsingTextFixup(Func);
+}
+
 template <> void InstARM32Uxt::emitIAS(const Cfg *Func) const {
   assert(getSrcSize() == 1);
   auto *Asm = Func->getAssembler<ARM32::AssemblerARM32>();
diff --git a/tests_lit/assembler/arm32/int-extend.ll b/tests_lit/assembler/arm32/int-extend.ll
new file mode 100644
index 0000000..5ea1365
--- /dev/null
+++ b/tests_lit/assembler/arm32/int-extend.ll
@@ -0,0 +1,113 @@
+; Tests signed/unsigned extend to 32 bits.
+
+; Show that we know how to translate add.
+
+; 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
+
+; NOTE: We use -O2 to get rid of memory stores.
+
+; 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 @testUxtb(i32 %v) {
+; ASM-LABEL:testUxtb:
+; DIS-LABEL:00000000 <testUxtb>:
+; IASM-LABEL:testUxtb:
+
+entry:
+; ASM-NEXT:.LtestUxtb$entry:
+; IASM-NEXT:.LtestUxtb$entry:
+
+  %v.b = trunc i32 %v to i8
+  %res = zext i8 %v.b to i32
+
+; ASM-NEXT:     uxtb    r0, r0
+; DIS-NEXT:   0:        e6ef0070
+; IASM-NEXT:    .byte 0x70
+; IASM-NEXT:    .byte 0x0
+; IASM-NEXT:    .byte 0xef
+; IASM-NEXT:    .byte 0xe6
+
+  ret i32 %res
+}
+
+define internal i32 @testSxtb(i32 %v) {
+; ASM-LABEL:testSxtb:
+; DIS-LABEL:00000010 <testSxtb>:
+; IASM-LABEL:testSxtb:
+
+entry:
+; ASM-NEXT:.LtestSxtb$entry:
+; IASM-NEXT:.LtestSxtb$entry:
+
+  %v.b = trunc i32 %v to i8
+  %res = sext i8 %v.b to i32
+
+; ASM-NEXT:     sxtb    r0, r0
+; DIS-NEXT:  10:        e6af0070
+; IASM-NEXT:    .byte 0x70
+; IASM-NEXT:    .byte 0x0
+; IASM-NEXT:    .byte 0xaf
+; IASM-NEXT:    .byte 0xe6
+
+  ret i32 %res
+}
+
+define internal i32 @testUxth(i32 %v) {
+; ASM-LABEL:testUxth:
+; DIS-LABEL:00000020 <testUxth>:
+; IASM-LABEL:testUxth:
+
+entry:
+; ASM-NEXT:.LtestUxth$entry:
+; IASM-NEXT:.LtestUxth$entry:
+
+  %v.h = trunc i32 %v to i16
+  %res = zext i16 %v.h to i32
+
+; ASM-NEXT:     uxth    r0, r0
+; DIS-NEXT:  20:        e6ff0070
+; IASM-NEXT:    .byte 0x70
+; IASM-NEXT:    .byte 0x0
+; IASM-NEXT:    .byte 0xff
+; IASM-NEXT:    .byte 0xe6
+
+  ret i32 %res
+}
+
+define internal i32 @testSxth(i32 %v) {
+; ASM-LABEL:testSxth:
+; DIS-LABEL:00000030 <testSxth>:
+; IASM-LABEL:testSxth:
+
+entry:
+; ASM-NEXT:.LtestSxth$entry:
+; IASM-NEXT:.LtestSxth$entry:
+
+  %v.h = trunc i32 %v to i16
+  %res = sext i16 %v.h to i32
+
+; ASM-NEXT:     sxth    r0, r0
+; DIS-NEXT:  30:        e6bf0070
+; IASM-NEXT:    .byte 0x70
+; IASM-NEXT:    .byte 0x0
+; IASM-NEXT:    .byte 0xbf
+; IASM-NEXT:    .byte 0xe6
+
+  ret i32 %res
+}