diff options
author | Ayke van Laethem <[email protected]> | 2021-04-05 22:29:53 +0200 |
---|---|---|
committer | Ron Evans <[email protected]> | 2021-04-08 11:40:59 +0200 |
commit | 04d12bf2ba85358534d67a4cbe3402a1f25b7437 (patch) | |
tree | 3d1d80eaee75aac4686bde332bf8e379fe1a9cf4 | |
parent | 0b7957d61249ce4b745f78405e36f07245eb8da3 (diff) | |
download | tinygo-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.go | 9 | ||||
-rw-r--r-- | interp/interpreter.go | 19 | ||||
-rw-r--r-- | interp/testdata/basic.ll | 25 | ||||
-rw-r--r-- | interp/testdata/basic.out.ll | 23 |
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 +} |