Browse Source

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.
pull/1782/head
Ayke van Laethem 4 years ago
committed by Ron Evans
parent
commit
04d12bf2ba
  1. 9
      interp/compiler.go
  2. 19
      interp/interpreter.go
  3. 25
      interp/testdata/basic.ll
  4. 23
      interp/testdata/basic.out.ll

9
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()

19
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 {

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

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

Loading…
Cancel
Save