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 +}