[Subzero][MIPS32] Implement sext, zext and trunc

This patch adds support for sext, zext, trunc operations on i8, i16, i32 source operand types. Support for i1 source operand type will follow.

R=stichnot@chromium.org

Review URL: https://codereview.chromium.org/1948093002 .

Patch from Sagar Thakur <sagar.thakur@imgtec.com>.
diff --git a/src/IceInstMIPS32.cpp b/src/IceInstMIPS32.cpp
index 9ba8777..30d48e3 100644
--- a/src/IceInstMIPS32.cpp
+++ b/src/IceInstMIPS32.cpp
@@ -56,6 +56,7 @@
 template <> const char *InstMIPS32Add::Opcode = "add";
 template <> const char *InstMIPS32Addu::Opcode = "addu";
 template <> const char *InstMIPS32And::Opcode = "and";
+template <> const char *InstMIPS32Andi::Opcode = "andi";
 template <> const char *InstMIPS32Mfhi::Opcode = "mfhi";
 template <> const char *InstMIPS32Mflo::Opcode = "mflo";
 template <> const char *InstMIPS32Mthi::Opcode = "mthi";
@@ -65,10 +66,12 @@
 template <> const char *InstMIPS32Multu::Opcode = "multu";
 template <> const char *InstMIPS32Or::Opcode = "or";
 template <> const char *InstMIPS32Ori::Opcode = "ori";
+template <> const char *InstMIPS32Sll::Opcode = "sll";
 template <> const char *InstMIPS32Slt::Opcode = "slt";
 template <> const char *InstMIPS32Slti::Opcode = "slti";
 template <> const char *InstMIPS32Sltiu::Opcode = "sltiu";
 template <> const char *InstMIPS32Sltu::Opcode = "sltu";
+template <> const char *InstMIPS32Sra::Opcode = "sra";
 template <> const char *InstMIPS32Sub::Opcode = "sub";
 template <> const char *InstMIPS32Subu::Opcode = "subu";
 template <> const char *InstMIPS32Xor::Opcode = "xor";
diff --git a/src/IceInstMIPS32.h b/src/IceInstMIPS32.h
index e364f7f..4247e7e 100644
--- a/src/IceInstMIPS32.h
+++ b/src/IceInstMIPS32.h
@@ -121,6 +121,7 @@
     Addiu,
     Addu,
     And,
+    Andi,
     Br,
     Call,
     La,
@@ -137,10 +138,12 @@
     Or,
     Ori,
     Ret,
+    Sll,
     Slt,
     Slti,
     Sltiu,
     Sltu,
+    Sra,
     Sub,
     Subu,
     Xor,
@@ -462,6 +465,7 @@
 using InstMIPS32Addu = InstMIPS32ThreeAddrGPR<InstMIPS32::Addu>;
 using InstMIPS32Addiu = InstMIPS32Imm16<InstMIPS32::Addiu, true>;
 using InstMIPS32And = InstMIPS32ThreeAddrGPR<InstMIPS32::And>;
+using InstMIPS32Andi = InstMIPS32Imm16<InstMIPS32::Andi>;
 using InstMIPS32Lui = InstMIPS32Imm16<InstMIPS32::Lui>;
 using InstMIPS32La = InstMIPS32UnaryopGPR<InstMIPS32::La>;
 using InstMIPS32Mfhi = InstMIPS32UnaryopGPR<InstMIPS32::Mfhi>;
@@ -473,10 +477,12 @@
 using InstMIPS32Multu = InstMIPS32ThreeAddrGPR<InstMIPS32::Multu>;
 using InstMIPS32Or = InstMIPS32ThreeAddrGPR<InstMIPS32::Or>;
 using InstMIPS32Ori = InstMIPS32Imm16<InstMIPS32::Ori>;
+using InstMIPS32Sll = InstMIPS32Imm16<InstMIPS32::Sll>;
 using InstMIPS32Slt = InstMIPS32ThreeAddrGPR<InstMIPS32::Slt>;
 using InstMIPS32Slti = InstMIPS32Imm16<InstMIPS32::Slti>;
 using InstMIPS32Sltiu = InstMIPS32Imm16<InstMIPS32::Sltiu>;
 using InstMIPS32Sltu = InstMIPS32ThreeAddrGPR<InstMIPS32::Sltu>;
+using InstMIPS32Sra = InstMIPS32Imm16<InstMIPS32::Sra>;
 using InstMIPS32Sub = InstMIPS32ThreeAddrGPR<InstMIPS32::Sub>;
 using InstMIPS32Subu = InstMIPS32ThreeAddrGPR<InstMIPS32::Subu>;
 using InstMIPS32Ori = InstMIPS32Imm16<InstMIPS32::Ori>;
diff --git a/src/IceTargetLoweringMIPS32.cpp b/src/IceTargetLoweringMIPS32.cpp
index cbfad69..79f02fb 100644
--- a/src/IceTargetLoweringMIPS32.cpp
+++ b/src/IceTargetLoweringMIPS32.cpp
@@ -1,4 +1,3 @@
-//===- subzero/src/IceTargetLoweringMIPS32.cpp - MIPS32 lowering ----------===//
 //
 //                        The Subzero Code Generator
 //
@@ -905,20 +904,80 @@
 
 void TargetMIPS32::lowerCast(const InstCast *Instr) {
   InstCast::OpKind CastKind = Instr->getCastKind();
+  Variable *Dest = Instr->getDest();
+  Operand *Src0 = legalizeUndef(Instr->getSrc(0));
+  const Type DestTy = Dest->getType();
+  const Type Src0Ty = Src0->getType();
+  const uint32_t ShiftAmount =
+      INT32_BITS - (CHAR_BITS * typeWidthInBytes(Src0Ty));
+  const uint32_t Mask = (1 << (CHAR_BITS * typeWidthInBytes(Src0Ty))) - 1;
+
+  if (isVectorType(DestTy) || Src0->getType() == IceType_i1) {
+    UnimplementedLoweringError(this, Instr);
+    return;
+  }
   switch (CastKind) {
   default:
     Func->setError("Cast type not supported");
     return;
   case InstCast::Sext: {
-    UnimplementedLoweringError(this, Instr);
+    if (DestTy == IceType_i64) {
+      auto *DestLo = llvm::cast<Variable>(loOperand(Dest));
+      auto *DestHi = llvm::cast<Variable>(hiOperand(Dest));
+      Variable *Src0R = legalizeToReg(Src0);
+      Variable *T_Lo = I32Reg();
+      if (Src0Ty == IceType_i32) {
+        _mov(DestLo, Src0R);
+      } else if (Src0Ty == IceType_i8 || Src0Ty == IceType_i16) {
+        _sll(T_Lo, Src0R, ShiftAmount);
+        _sra(DestLo, T_Lo, ShiftAmount);
+      }
+      _sra(DestHi, DestLo, INT32_BITS - 1);
+    } else {
+      Variable *Src0R = legalizeToReg(Src0);
+      Variable *T = makeReg(DestTy);
+      if (Src0Ty == IceType_i8 || Src0Ty == IceType_i16) {
+        _sll(T, Src0R, ShiftAmount);
+        _sra(Dest, T, ShiftAmount);
+      }
+    }
     break;
   }
   case InstCast::Zext: {
-    UnimplementedLoweringError(this, Instr);
+    if (DestTy == IceType_i64) {
+      auto *DestLo = llvm::cast<Variable>(loOperand(Dest));
+      auto *DestHi = llvm::cast<Variable>(hiOperand(Dest));
+      Variable *Src0R = legalizeToReg(Src0);
+
+      switch (Src0Ty) {
+      default: { assert(Src0Ty != IceType_i64); } break;
+      case IceType_i32:
+        _mov(DestLo, Src0R);
+        break;
+      case IceType_i8:
+      case IceType_i16:
+        _andi(DestLo, Src0R, Mask);
+        break;
+      }
+
+      auto *Zero = getZero();
+      _addiu(DestHi, Zero, 0);
+    } else {
+      Variable *Src0R = legalizeToReg(Src0);
+      Variable *T = makeReg(DestTy);
+      if (Src0Ty == IceType_i8 || Src0Ty == IceType_i16)
+        _andi(T, Src0R, Mask);
+      _mov(Dest, T);
+    }
     break;
   }
   case InstCast::Trunc: {
-    UnimplementedLoweringError(this, Instr);
+    if (Src0Ty == IceType_i64)
+      Src0 = loOperand(Src0);
+    Variable *Src0R = legalizeToReg(Src0);
+    Variable *T = makeReg(DestTy);
+    _mov(T, Src0R);
+    _mov(Dest, T);
     break;
   }
   case InstCast::Fptrunc:
diff --git a/src/IceTargetLoweringMIPS32.h b/src/IceTargetLoweringMIPS32.h
index 5802052..0454f30 100644
--- a/src/IceTargetLoweringMIPS32.h
+++ b/src/IceTargetLoweringMIPS32.h
@@ -150,6 +150,10 @@
     Context.insert<InstMIPS32And>(Dest, Src0, Src1);
   }
 
+  void _andi(Variable *Dest, Variable *Src, uint32_t Imm) {
+    Context.insert<InstMIPS32Andi>(Dest, Src, Imm);
+  }
+
   void _br(CfgNode *Target) { Context.insert<InstMIPS32Br>(Target); }
 
   void _ret(Variable *RA, Variable *Src0 = nullptr) {
@@ -216,6 +220,10 @@
     Context.insert<InstMIPS32Ori>(Dest, Src, Imm);
   }
 
+  void _sll(Variable *Dest, Variable *Src, uint32_t Imm) {
+    Context.insert<InstMIPS32Sll>(Dest, Src, Imm);
+  }
+
   void _slt(Variable *Dest, Variable *Src0, Variable *Src1) {
     Context.insert<InstMIPS32Slt>(Dest, Src0, Src1);
   }
@@ -232,6 +240,10 @@
     Context.insert<InstMIPS32Sltu>(Dest, Src0, Src1);
   }
 
+  void _sra(Variable *Dest, Variable *Src, uint32_t Imm) {
+    Context.insert<InstMIPS32Sra>(Dest, Src, Imm);
+  }
+
   void _sub(Variable *Dest, Variable *Src0, Variable *Src1) {
     Context.insert<InstMIPS32Sub>(Dest, Src0, Src1);
   }
@@ -346,6 +358,8 @@
   static SmallBitVector RegisterAliases[RegMIPS32::Reg_NUM];
   SmallBitVector RegsUsed;
   VarList PhysicalRegisters[IceType_NUM];
+  static constexpr uint32_t CHAR_BITS = 8;
+  static constexpr uint32_t INT32_BITS = 32;
 
 private:
   ENABLE_MAKE_UNIQUE;
diff --git a/tests_lit/llvm2ice_tests/64bit.pnacl.ll b/tests_lit/llvm2ice_tests/64bit.pnacl.ll
index b19fa2a..aa0f26d 100644
--- a/tests_lit/llvm2ice_tests/64bit.pnacl.ll
+++ b/tests_lit/llvm2ice_tests/64bit.pnacl.ll
@@ -799,6 +799,9 @@
 ; ARM32-LABEL: trunc64To32Signed
 ; ARM32: mov r0, r2
 
+; MIPS32-LABEL: trunc64To32Signed
+; MIPS32: move v0,a2
+
 define internal i32 @trunc64To16Signed(i64 %a) {
 entry:
   %conv = trunc i64 %a to i16
@@ -816,6 +819,11 @@
 ; ARM32-LABEL: trunc64To16Signed
 ; ARM32: sxth r0, r0
 
+; MIPS32-LABEL: trunc64To16Signed
+; MIPS32: sll a0,a0,0x10
+; MIPS32: sra a0,a0,0x10
+; MIPS32: move v0,a0
+
 define internal i32 @trunc64To8Signed(i64 %a) {
 entry:
   %conv = trunc i64 %a to i8
@@ -833,6 +841,11 @@
 ; ARM32-LABEL: trunc64To8Signed
 ; ARM32: sxtb r0, r0
 
+; MIPS32-LABEL: trunc64To8Signed
+; MIPS32: sll a0,a0,0x18
+; MIPS32: sra a0,a0,0x18
+; MIPS32: move v0,a0
+
 define internal i32 @trunc64To32SignedConst() {
 entry:
   %conv = trunc i64 12345678901234 to i32
@@ -848,6 +861,10 @@
 ; ARM32: movw r0, #12274 ; 0x2ff2
 ; ARM32: movt r0, #29646 ; 0x73ce
 
+; MIPS32-LABEL: trunc64To32SignedConst
+; MIPS32: lui v0,0x73ce
+; MIPS32: ori v0,v0,0x2ff2
+
 define internal i32 @trunc64To16SignedConst() {
 entry:
   %conv = trunc i64 12345678901234 to i16
@@ -867,6 +884,12 @@
 ; ARM32: movt r0, #29646 ; 0x73ce
 ; ARM32: sxth r0, r0
 
+; MIPS32-LABEL: trunc64To16SignedConst
+; MIPS32: lui v0,0x73ce
+; MIPS32: ori v0,v0,0x2ff2
+; MIPS32: sll v0,v0,0x10
+; MIPS32: sra v0,v0,0x10
+
 define internal i32 @trunc64To32Unsigned(i64 %padding, i64 %a) {
 entry:
   %conv = trunc i64 %a to i32
@@ -881,6 +904,9 @@
 ; ARM32-LABEL: trunc64To32Unsigned
 ; ARM32: mov r0, r2
 
+; MIPS32-LABEL: trunc64To32Unsigned
+; MIPS32: move v0,a2
+
 define internal i32 @trunc64To16Unsigned(i64 %a) {
 entry:
   %conv = trunc i64 %a to i16
@@ -898,6 +924,10 @@
 ; ARM32-LABEL: trunc64To16Unsigned
 ; ARM32: uxth
 
+; MIPS32-LABEL: trunc64To16Unsigned
+; MIPS32: andi a0,a0,0xffff
+; MIPS32: move v0,a0
+
 define internal i32 @trunc64To8Unsigned(i64 %a) {
 entry:
   %conv = trunc i64 %a to i8
@@ -915,6 +945,10 @@
 ; ARM32-LABEL: trunc64To8Unsigned
 ; ARM32: uxtb
 
+; MIPS32-LABEL: trunc64To8Unsigned
+; MIPS32: andi a0,a0,0xff
+; MIPS32: move v0,a0
+
 define internal i32 @trunc64To1(i64 %a) {
 entry:
 ;  %tobool = icmp ne i64 %a, 0
@@ -952,6 +986,10 @@
 ; ARM32-LABEL: sext32To64
 ; ARM32: asr {{.*}}, #31
 
+; MIPS32-LABEL: sext32To64
+; MIPS32-LABEL: sra v1,a0,0x1f
+; MIPS32-LABEL: move v0,a0
+
 define internal i64 @sext16To64(i32 %a) {
 entry:
   %a.arg_trunc = trunc i32 %a to i16
@@ -970,6 +1008,12 @@
 ; ARM32: sxth
 ; ARM32: asr {{.*}}, #31
 
+; MIPS32-LABEL: sext16To64
+; MIPS32: sll a0,a0,0x10
+; MIPS32: sra a0,a0,0x10
+; MIPS32: sra v1,a0,0x1f
+; MIPS32: move v0,a0
+
 define internal i64 @sext8To64(i32 %a) {
 entry:
   %a.arg_trunc = trunc i32 %a to i8
@@ -988,6 +1032,12 @@
 ; ARM32: sxtb
 ; ARM32: asr {{.*}}, #31
 
+; MIPS32-LABEL: sext8To64
+; MIPS32: sll a0,a0,0x18
+; MIPS32: sra a0,a0,0x18
+; MIPS32: sra v1,a0,0x1f
+; MIPS32: move v0,a0
+
 define internal i64 @sext1To64(i32 %a) {
 entry:
   %a.arg_trunc = trunc i32 %a to i1
@@ -1026,6 +1076,10 @@
 ; ARM32-LABEL: zext32To64
 ; ARM32: mov {{.*}}, #0
 
+; MIPS32-LABEL: zext32To64
+; MIPS32: li v1,0
+; MIPS32: move v0,a0
+
 define internal i64 @zext16To64(i32 %a) {
 entry:
   %a.arg_trunc = trunc i32 %a to i16
@@ -1044,6 +1098,11 @@
 ; ARM32: uxth
 ; ARM32: mov {{.*}}, #0
 
+; MIPS32-LABEL: zext16To64
+; MIPS32: andi a0,a0,0xffff
+; MIPS32: li v1,0
+; MIPS32: move v0,a0
+
 define internal i64 @zext8To64(i32 %a) {
 entry:
   %a.arg_trunc = trunc i32 %a to i8
@@ -1062,6 +1121,11 @@
 ; ARM32: uxtb
 ; ARM32: mov {{.*}}, #0
 
+; MIPS32-LABEL: zext8To64
+; MIPS32: andi a0,a0,0xff
+; MIPS32: li v1,0
+; MIPS32: move v0,a0
+
 define internal i64 @zext1To64(i32 %a) {
 entry:
   %a.arg_trunc = trunc i32 %a to i1