From 04d12bf2ba85358534d67a4cbe3402a1f25b7437 Mon Sep 17 00:00:00 2001 From: Ayke van Laethem Date: Mon, 5 Apr 2021 22:29:53 +0200 Subject: [PATCH] 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. --- interp/compiler.go | 9 +++++++++ interp/interpreter.go | 19 ++++++++++++++++++- interp/testdata/basic.ll | 25 +++++++++++++++++++++++++ interp/testdata/basic.out.ll | 23 +++++++++++++++++++++++ 4 files changed, 75 insertions(+), 1 deletion(-) diff --git a/interp/compiler.go b/interp/compiler.go index e45df8c1..e9bf36c3 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 c0dc45d9..bdaee4e9 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 b223b840..e3ff974e 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 9a16c85e..e873b1c8 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 +}