[SubZero] Implement lowerSwitch for MIPS

The patch implements lowerSwitch for i32 and i64 types.

R=stichnot@chromium.org

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

Patch from Jaydeep Patil <jaydeep.patil@imgtec.com>.
diff --git a/src/IceInstMIPS32.cpp b/src/IceInstMIPS32.cpp
index d238e29..f86c8b2 100644
--- a/src/IceInstMIPS32.cpp
+++ b/src/IceInstMIPS32.cpp
@@ -456,8 +456,11 @@
   Ostream &Str = Func->getContext()->getStrEmit();
   Str << "\t"
          "b" << InstMIPS32CondAttributes[Predicate].EmitString << "\t";
-  if (Label) {
-    Str << Label->getLabelName();
+  if (Label != nullptr) {
+    getSrc(0)->emit(Func);
+    Str << ", ";
+    getSrc(1)->emit(Func);
+    Str << ", " << Label->getLabelName();
   } else {
     if (isUnconditionalBranch()) {
       Str << getTargetFalse()->getAsmName();
@@ -501,8 +504,11 @@
   Str << "\t"
          "b" << InstMIPS32CondAttributes[Predicate].EmitString << "\t";
 
-  if (Label) {
-    Str << Label->getLabelName();
+  if (Label != nullptr) {
+    getSrc(0)->dump(Func);
+    Str << ", ";
+    getSrc(1)->dump(Func);
+    Str << ", " << Label->getLabelName();
   } else {
     if (isUnconditionalBranch()) {
       Str << getTargetFalse()->getAsmName();
diff --git a/src/IceInstMIPS32.h b/src/IceInstMIPS32.h
index fb3a311..5dac527 100644
--- a/src/IceInstMIPS32.h
+++ b/src/IceInstMIPS32.h
@@ -671,6 +671,14 @@
         InstMIPS32Br(Func, TargetTrue, TargetFalse, Src0, NoLabel, Cond);
   }
 
+  static InstMIPS32Br *create(Cfg *Func, CfgNode *TargetTrue,
+                              CfgNode *TargetFalse, Operand *Src0,
+                              Operand *Src1, const InstMIPS32Label *Label,
+                              CondMIPS32::Cond Cond) {
+    return new (Func->allocate<InstMIPS32Br>())
+        InstMIPS32Br(Func, TargetTrue, TargetFalse, Src0, Src1, Label, Cond);
+  }
+
   const CfgNode *getTargetTrue() const { return TargetTrue; }
   const CfgNode *getTargetFalse() const { return TargetFalse; }
   CondMIPS32::Cond getPredicate() const { return Predicate; }
diff --git a/src/IceTargetLoweringMIPS32.cpp b/src/IceTargetLoweringMIPS32.cpp
index 7a32714..47b1fcb 100644
--- a/src/IceTargetLoweringMIPS32.cpp
+++ b/src/IceTargetLoweringMIPS32.cpp
@@ -2318,7 +2318,38 @@
 void TargetMIPS32::doAddressOptStore() { UnimplementedError(getFlags()); }
 
 void TargetMIPS32::lowerSwitch(const InstSwitch *Instr) {
-  UnimplementedLoweringError(this, Instr);
+  Operand *Src = Instr->getComparison();
+  SizeT NumCases = Instr->getNumCases();
+  if (Src->getType() == IceType_i64) {
+    Src = legalizeUndef(Src);
+    Variable *Src0Lo = legalizeToReg(loOperand(Src));
+    Variable *Src0Hi = legalizeToReg(hiOperand(Src));
+    for (SizeT I = 0; I < NumCases; ++I) {
+      Operand *ValueLo = Ctx->getConstantInt32(Instr->getValue(I));
+      Operand *ValueHi = Ctx->getConstantInt32(Instr->getValue(I) >> 32);
+      CfgNode *TargetTrue = Instr->getLabel(I);
+      constexpr CfgNode *NoTarget = nullptr;
+      ValueHi = legalizeToReg(ValueHi);
+      InstMIPS32Label *IntraLabel = InstMIPS32Label::create(Func, this);
+      _br(NoTarget, NoTarget, Src0Hi, ValueHi, IntraLabel,
+          CondMIPS32::Cond::NE);
+      ValueLo = legalizeToReg(ValueLo);
+      _br(NoTarget, TargetTrue, Src0Lo, ValueLo, CondMIPS32::Cond::EQ);
+      Context.insert(IntraLabel);
+    }
+    _br(Instr->getLabelDefault());
+    return;
+  }
+  Variable *SrcVar = legalizeToReg(Src);
+  assert(SrcVar->mustHaveReg());
+  for (SizeT I = 0; I < NumCases; ++I) {
+    Operand *Value = Ctx->getConstantInt32(Instr->getValue(I));
+    CfgNode *TargetTrue = Instr->getLabel(I);
+    constexpr CfgNode *NoTargetFalse = nullptr;
+    Value = legalizeToReg(Value);
+    _br(NoTargetFalse, TargetTrue, SrcVar, Value, CondMIPS32::Cond::EQ);
+  }
+  _br(Instr->getLabelDefault());
 }
 
 void TargetMIPS32::lowerBreakpoint(const InstBreakpoint *Instr) {
diff --git a/src/IceTargetLoweringMIPS32.h b/src/IceTargetLoweringMIPS32.h
index 70fed4e..5d5a98e 100644
--- a/src/IceTargetLoweringMIPS32.h
+++ b/src/IceTargetLoweringMIPS32.h
@@ -178,6 +178,13 @@
     Context.insert<InstMIPS32Br>(TargetTrue, TargetFalse, Src0, Condition);
   }
 
+  void _br(CfgNode *TargetTrue, CfgNode *TargetFalse, Operand *Src0,
+           Operand *Src1, const InstMIPS32Label *Label,
+           CondMIPS32::Cond Condition) {
+    Context.insert<InstMIPS32Br>(TargetTrue, TargetFalse, Src0, Src1, Label,
+                                 Condition);
+  }
+
   void _ret(Variable *RA, Variable *Src0 = nullptr) {
     Context.insert<InstMIPS32Ret>(RA, Src0);
   }
diff --git a/tests_lit/llvm2ice_tests/switch-opt.ll b/tests_lit/llvm2ice_tests/switch-opt.ll
index 268fd25..e3001e5 100644
--- a/tests_lit/llvm2ice_tests/switch-opt.ll
+++ b/tests_lit/llvm2ice_tests/switch-opt.ll
@@ -11,6 +11,14 @@
 ; RUN:   | %if --need=target_ARM32 --need=allow_dump \
 ; RUN:     --command FileCheck --check-prefix ARM32 %s
 
+; TODO(jaydeep.patil): Using --skip-unimplemented for MIPS32
+; RUN: %if --need=target_MIPS32 --need=allow_dump \
+; RUN:   --command %p2i --filetype=asm --assemble --disassemble \
+; RUN:   --target mips32 -i %s --args -Om1 --skip-unimplemented \
+; RUN:   -allow-externally-defined-symbols \
+; RUN:   | %if --need=target_MIPS32 --need=allow_dump \
+; RUN:     --command FileCheck --check-prefix MIPS32 %s
+
 define internal i32 @testSwitch(i32 %a) {
 entry:
   switch i32 %a, label %sw.default [
@@ -40,6 +48,35 @@
   ret i32 %result.1
 }
 
+; MIPS32-LABEL: testSwitch
+; MIPS32: li	{{.*}},1
+; MIPS32: li	{{.*}},17
+; MIPS32: li	{{.*}},1
+; MIPS32: beq	{{.*}},{{.*}},{{.*}} <[[SW_EPILOG:.*]]>
+; MIPS32: li	{{.*}},2
+; MIPS32: beq	{{.*}},{{.*}},{{.*}} <[[SW_EPILOG]]>
+; MIPS32: li	{{.*}},3
+; MIPS32: beq	{{.*}},{{.*}},{{.*}} <[[SW_EPILOG]]>
+; MIPS32: li	{{.*}},7
+; MIPS32: beq	{{.*}},{{.*}},{{.*}} <[[SW_BB1:.*]]>
+; MIPS32: li	{{.*}},8
+; MIPS32: beq	{{.*}},{{.*}},{{.*}} <[[SW_BB1]]>
+; MIPS32: li	{{.*}},15
+; MIPS32: beq	{{.*}},{{.*}},{{.*}} <[[SW_BB2:.*]]>
+; MIPS32: li	{{.*}},14
+; MIPS32: beq	{{.*}},{{.*}},{{.*}} <[[SW_BB2]]>
+; MIPS32: b	{{.*}} <[[SW_DEFAULT:.*]]>
+; MIPS32: <[[SW_DEFAULT]]>
+; MIPS32: li	{{.*}},27
+; MIPS32: b	{{.*}} <[[SW_EPILOG]]>
+; MIPS32: <[[SW_BB1]]>
+; MIPS32: li	{{.*}},21
+; MIPS32: b	{{.*}} <[[SW_BB2]]>
+; MIPS32: <[[SW_BB2]]>
+; MIPS32: b	{{.*}} <[[SW_EPILOG]]>
+; MIPS32: <[[SW_EPILOG]]>
+; MIPS32: jr	ra
+
 ; Check for a valid addressing mode when the switch operand is an
 ; immediate.  It's important that there is exactly one case, because
 ; for two or more cases the source operand is legalized into a
@@ -58,6 +95,14 @@
 ; ARM32-NEXT: beq
 ; ARM32-NEXT: b
 
+; MIPS32-LABEL: testSwitchImm
+; MIPS32: li	{{.*}},10
+; MIPS32: li	{{.*}},1
+; MIPS32: beq	{{.*}},{{.*}},{{.*}} <.LtestSwitchImm$sw.default>
+; MIPS32: .LtestSwitchImm$sw.default
+; MIPS32: li	v0,20
+; MIPS32: jr	ra
+
 ; Test for correct 64-bit lowering.
 define internal i32 @testSwitch64(i64 %a) {
 entry:
@@ -102,6 +147,43 @@
 ; ARM32-NEXT: beq
 ; ARM32-NEXT: b
 
+; MIPS32-LABEL: testSwitch64
+; MIPS32: bne	{{.*}},{{.*}},{{.*}} <.LtestSwitch64$local$__0>
+; MIPS32: li	{{.*}},123
+; MIPS32: beq	{{.*}},{{.*}},{{.*}} <.LtestSwitch64$return>
+; MIPS32: .LtestSwitch64$local$__0
+; MIPS32: li	{{.*}},0
+; MIPS32: bne	{{.*}},{{.*}},{{.*}} <.LtestSwitch64$local$__1>
+; MIPS32: li	{{.*}},234
+; MIPS32: beq	{{.*}},{{.*}},{{.*}} <.LtestSwitch64$sw.bb1>
+; MIPS32: .LtestSwitch64$local$__1
+; MIPS32: li	{{.*}},0
+; MIPS32: bne	{{.*}},{{.*}},{{.*}} <.LtestSwitch64$local$__2>
+; MIPS32: li	{{.*}},345
+; MIPS32: beq	{{.*}},{{.*}},{{.*}} <.LtestSwitch64$sw.bb2>
+; MIPS32: .LtestSwitch64$local$__2
+; MIPS32: li	{{.*}},18
+; MIPS32: bne	{{.*}},{{.*}},{{.*}} <.LtestSwitch64$local$__3>
+; MIPS32: lui	{{.*}},0x3456
+; MIPS32: ori	{{.*}},{{.*}},0x7890
+; MIPS32: beq	{{.*}},{{.*}},{{.*}} <.LtestSwitch64$sw.bb3>
+; MIPS32: .LtestSwitch64$local$__3
+; MIPS32: b	{{.*}} <.LtestSwitch64$sw.default>
+; MIPS32: .LtestSwitch64$sw.bb1
+; MIPS32: li	{{.*}},2
+; MIPS32: b	{{.*}} <.LtestSwitch64$return>
+; MIPS32: .LtestSwitch64$sw.bb2
+; MIPS32: li	{{.*}},3
+; MIPS32: b	{{.*}} <.LtestSwitch64$return>
+; MIPS32: .LtestSwitch64$sw.bb3
+; MIPS32: li	{{.*}},4
+; MIPS32: b	{{.*}} <.LtestSwitch64$return>
+; MIPS32: .LtestSwitch64$sw.default
+; MIPS32: li	{{.*}},5
+; MIPS32: b	{{.*}} <.LtestSwitch64$return>
+; MIPS32: .LtestSwitch64$return
+; MIPS32: jr	ra
+
 ; Similar to testSwitchImm, make sure proper addressing modes are
 ; used.  In reality, this is tested by running the output through the
 ; assembler.
@@ -120,6 +202,19 @@
 ; ARM32-NEXT: beq [[ADDR:[0-9a-f]+]]
 ; ARM32-NEXT: b [[ADDR]]
 
+; MIPS32-LABEL: testSwitchImm64
+; MIPS32: li	{{.*}},10
+; MIPS32: li	{{.*}},0
+; MIPS32: li	{{.*}},0
+; MIPS32: bne	{{.*}},{{.*}},{{.*}} <.LtestSwitchImm64$local$__0>
+; MIPS32: li	{{.*}},1
+; MIPS32: beq	{{.*}},{{.*}},{{.*}} <.LtestSwitchImm64$sw.default>
+; MIPS32: .LtestSwitchImm64$local$__0
+; MIPS32: b	{{.*}} <.LtestSwitchImm64$sw.default>
+; MIPS32: .LtestSwitchImm64$sw.default
+; MIPS32: li	{{.*}},20
+; MIPS32: jr	ra
+
 define internal i32 @testSwitchUndef64() {
 entry:
   switch i64 undef, label %sw.default [
@@ -132,3 +227,16 @@
 ; ARM32-LABEL: testSwitchUndef64
 ; ARM32: mov {{.*}}, #0
 ; ARM32: mov {{.*}}, #0
+
+; MIPS32-LABEL: testSwitchUndef64
+; MIPS32: li	{{.*}},0
+; MIPS32: li	{{.*}},0
+; MIPS32: li	{{.*}},0
+; MIPS32: bne	{{.*}},{{.*}},{{.*}} <.LtestSwitchUndef64$local$__0>
+; MIPS32: li	{{.*}},1
+; MIPS32: beq	{{.*}},{{.*}},{{.*}} <.LtestSwitchUndef64$sw.default>
+; MIPS32: .LtestSwitchUndef64$local$__0
+; MIPS32: b	{{.*}} <.LtestSwitchUndef64$sw.default>
+; MIPS32: .LtestSwitchUndef64$sw.default
+; MIPS32: li	{{.*}},20
+; MIPS32: jr	ra