aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorAyke van Laethem <[email protected]>2021-04-05 22:29:53 +0200
committerRon Evans <[email protected]>2021-04-08 11:40:59 +0200
commit04d12bf2ba85358534d67a4cbe3402a1f25b7437 (patch)
tree3d1d80eaee75aac4686bde332bf8e379fe1a9cf4
parent0b7957d61249ce4b745f78405e36f07245eb8da3 (diff)
downloadtinygo-04d12bf2ba85358534d67a4cbe3402a1f25b7437.tar.gz
tinygo-04d12bf2ba85358534d67a4cbe3402a1f25b7437.zip
interp: add support for switch statement
A switch statement is not normally emitted by the compiler package, but LLVM function passes may convert a series of if/else pairs to a switch statement. A future change will run function passes in the package compile phase, so the interp package (which is also run after all modules are merged together) will need to deal with these new switch statements.
-rw-r--r--interp/compiler.go9
-rw-r--r--interp/interpreter.go19
-rw-r--r--interp/testdata/basic.ll25
-rw-r--r--interp/testdata/basic.out.ll23
4 files changed, 75 insertions, 1 deletions
diff --git a/interp/compiler.go b/interp/compiler.go
index e45df8c13..e9bf36c3e 100644
--- a/interp/compiler.go
+++ b/interp/compiler.go
@@ -135,6 +135,15 @@ func (r *runner) compileFunction(llvmFn llvm.Value) *function {
default:
panic("unknown number of operands")
}
+ case llvm.Switch:
+ // A switch is an array of (value, label) pairs, of which the
+ // first one indicates the to-switch value and the default
+ // label.
+ numOperands := llvmInst.OperandsCount()
+ for i := 0; i < numOperands; i += 2 {
+ inst.operands = append(inst.operands, r.getValue(llvmInst.Operand(i)))
+ inst.operands = append(inst.operands, literalValue{uint32(blockIndices[llvmInst.Operand(i+1)])})
+ }
case llvm.PHI:
inst.name = llvmInst.Name()
incomingCount := inst.llvmInst.IncomingCount()
diff --git a/interp/interpreter.go b/interp/interpreter.go
index c0dc45d93..bdaee4e97 100644
--- a/interp/interpreter.go
+++ b/interp/interpreter.go
@@ -103,7 +103,24 @@ func (r *runner) run(fn *function, params []value, parentMem *memoryView, indent
default:
panic("unknown operands length")
}
- break // continue with next block
+ case llvm.Switch:
+ // Switch statement: [value, defaultLabel, case0, label0, case1, label1, ...]
+ value := operands[0].Uint()
+ targetLabel := operands[1].Uint() // default label
+ // Do a lazy switch by iterating over all cases.
+ for i := 2; i < len(operands); i += 2 {
+ if value == operands[i].Uint() {
+ targetLabel = operands[i+1].Uint()
+ break
+ }
+ }
+ lastBB = currentBB
+ currentBB = int(targetLabel)
+ bb = fn.blocks[currentBB]
+ instIndex = -1 // start at 0 the next cycle
+ if r.debug {
+ fmt.Fprintln(os.Stderr, indent+"switch", operands, "->", currentBB)
+ }
case llvm.PHI:
var result value
for i := 0; i < len(inst.operands); i += 2 {
diff --git a/interp/testdata/basic.ll b/interp/testdata/basic.ll
index b223b8404..e3ff974e2 100644
--- a/interp/testdata/basic.ll
+++ b/interp/testdata/basic.ll
@@ -65,6 +65,12 @@ entry:
call void @modifyExternal(i32* bitcast (void ()* @willModifyGlobal to i32*))
store i16 7, i16* @main.exposedValue2
+ ; Test switch statement.
+ %switch1 = call i64 @testSwitch(i64 1) ; 1 returns 6
+ %switch2 = call i64 @testSwitch(i64 9) ; 9 returns the default value -1
+ call void @runtime.printint64(i64 %switch1)
+ call void @runtime.printint64(i64 %switch2)
+
ret void
}
@@ -87,3 +93,22 @@ entry:
store i16 8, i16* @main.exposedValue2
ret void
}
+
+define i64 @testSwitch(i64 %val) {
+entry:
+ ; Test switch statement.
+ switch i64 %val, label %otherwise [ i64 0, label %zero
+ i64 1, label %one
+ i64 2, label %two ]
+zero:
+ ret i64 5
+
+one:
+ ret i64 6
+
+two:
+ ret i64 7
+
+otherwise:
+ ret i64 -1
+}
diff --git a/interp/testdata/basic.out.ll b/interp/testdata/basic.out.ll
index 9a16c85eb..e873b1c8a 100644
--- a/interp/testdata/basic.out.ll
+++ b/interp/testdata/basic.out.ll
@@ -25,6 +25,8 @@ entry:
store i16 5, i16* @main.exposedValue1
call void @modifyExternal(i32* bitcast (void ()* @willModifyGlobal to i32*))
store i16 7, i16* @main.exposedValue2
+ call void @runtime.printint64(i64 6)
+ call void @runtime.printint64(i64 -1)
ret void
}
@@ -44,3 +46,24 @@ entry:
store i16 8, i16* @main.exposedValue2
ret void
}
+
+define i64 @testSwitch(i64 %val) local_unnamed_addr {
+entry:
+ switch i64 %val, label %otherwise [
+ i64 0, label %zero
+ i64 1, label %one
+ i64 2, label %two
+ ]
+
+zero: ; preds = %entry
+ ret i64 5
+
+one: ; preds = %entry
+ ret i64 6
+
+two: ; preds = %entry
+ ret i64 7
+
+otherwise: ; preds = %entry
+ ret i64 -1
+}