[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