Files
wazero/internal/engine/wazevo/backend/compiler_lower_test.go
2023-09-05 10:37:36 +09:00

140 lines
5.5 KiB
Go

package backend
import (
"context"
"testing"
"github.com/tetratelabs/wazero/internal/engine/wazevo/backend/regalloc"
"github.com/tetratelabs/wazero/internal/engine/wazevo/ssa"
"github.com/tetratelabs/wazero/internal/testing/require"
)
func TestCompiler_lowerBlockArguments(t *testing.T) {
for _, tc := range []struct {
name string
setup func(builder ssa.Builder) (c *compiler, args []ssa.Value, succ ssa.BasicBlock, verify func(t *testing.T))
}{
{
name: "all consts",
setup: func(builder ssa.Builder) (*compiler, []ssa.Value, ssa.BasicBlock, func(t *testing.T)) {
entryBlk := builder.AllocateBasicBlock()
builder.SetCurrentBlock(entryBlk)
i1 := builder.AllocateInstruction()
i1.AsIconst32(1)
i2 := builder.AllocateInstruction()
i2.AsIconst64(2)
f1 := builder.AllocateInstruction()
f1.AsF32const(3.0)
f2 := builder.AllocateInstruction()
f2.AsF64const(4.0)
builder.InsertInstruction(i1)
builder.InsertInstruction(i2)
builder.InsertInstruction(f1)
builder.InsertInstruction(f2)
succ := builder.AllocateBasicBlock()
succ.AddParam(builder, ssa.TypeI32)
succ.AddParam(builder, ssa.TypeI64)
succ.AddParam(builder, ssa.TypeF32)
succ.AddParam(builder, ssa.TypeF64)
var insertedConstInstructions []struct {
instr *ssa.Instruction
target regalloc.VReg
}
m := &mockMachine{
insertLoadConstant: func(instr *ssa.Instruction, vr regalloc.VReg) {
insertedConstInstructions = append(insertedConstInstructions, struct {
instr *ssa.Instruction
target regalloc.VReg
}{instr: instr, target: vr})
},
}
c := newCompiler(context.Background(), m, builder)
c.ssaValueDefinitions = []SSAValueDefinition{{Instr: i1}, {Instr: i2}, {Instr: f1}, {Instr: f2}}
c.ssaValueToVRegs = []regalloc.VReg{0, 1, 2, 3, 4, 5, 6, 7}
return c, []ssa.Value{i1.Return(), i2.Return(), f1.Return(), f2.Return()}, succ, func(t *testing.T) {
require.Equal(t, 4, len(insertedConstInstructions))
require.Equal(t, i1, insertedConstInstructions[0].instr)
require.Equal(t, regalloc.VReg(4), insertedConstInstructions[0].target)
require.Equal(t, i2, insertedConstInstructions[1].instr)
require.Equal(t, regalloc.VReg(5), insertedConstInstructions[1].target)
require.Equal(t, f1, insertedConstInstructions[2].instr)
require.Equal(t, regalloc.VReg(6), insertedConstInstructions[2].target)
require.Equal(t, f2, insertedConstInstructions[3].instr)
require.Equal(t, regalloc.VReg(7), insertedConstInstructions[3].target)
}
},
},
{
name: "overlap",
setup: func(builder ssa.Builder) (*compiler, []ssa.Value, ssa.BasicBlock, func(t *testing.T)) {
blk := builder.AllocateBasicBlock()
v1 := blk.AddParam(builder, ssa.TypeI32)
v2 := blk.AddParam(builder, ssa.TypeI32)
v3 := blk.AddParam(builder, ssa.TypeF32)
var insertMoves []struct{ src, dst regalloc.VReg }
m := &mockMachine{insertMove: func(dst, src regalloc.VReg) {
insertMoves = append(insertMoves, struct{ src, dst regalloc.VReg }{src: src, dst: dst})
}}
c := newCompiler(context.Background(), m, builder)
c.ssaValueDefinitions = []SSAValueDefinition{{}, {}, {}, {}}
c.ssaValueToVRegs = []regalloc.VReg{0, 1, 2, 3}
c.nextVRegID = 100 // Temporary reg should start with 100.
return c, []ssa.Value{v2, v1, v3 /* Swaps v1, v2 and pass v3 as-is. */}, blk, func(t *testing.T) {
require.Equal(t, 6, len(insertMoves)) // Three values are overlapped.
mov1, mov2, mov3, mov4, mov5, mov6 := insertMoves[0], insertMoves[1], insertMoves[2], insertMoves[3], insertMoves[4], insertMoves[5]
// Save the values to the temporary registers.
require.Equal(t, regalloc.VRegID(1), mov1.src.ID())
require.Equal(t, regalloc.VRegID(100), mov1.dst.ID())
require.Equal(t, regalloc.VRegID(0), mov2.src.ID())
require.Equal(t, regalloc.VRegID(101), mov2.dst.ID())
require.Equal(t, regalloc.VRegID(2), mov3.src.ID())
require.Equal(t, regalloc.VRegID(102), mov3.dst.ID())
// Then move back to the original place.
require.Equal(t, regalloc.VRegID(100), mov4.src.ID())
require.Equal(t, regalloc.VRegID(0), mov4.dst.ID())
require.Equal(t, regalloc.VRegID(101), mov5.src.ID())
require.Equal(t, regalloc.VRegID(1), mov5.dst.ID())
require.Equal(t, regalloc.VRegID(102), mov6.src.ID())
require.Equal(t, regalloc.VRegID(2), mov6.dst.ID())
}
},
},
{
name: "no overlap",
setup: func(builder ssa.Builder) (*compiler, []ssa.Value, ssa.BasicBlock, func(t *testing.T)) {
blk := builder.AllocateBasicBlock()
builder.SetCurrentBlock(blk)
i32 := blk.AddParam(builder, ssa.TypeI32)
add := builder.AllocateInstruction()
add.AsIadd(i32, i32)
builder.InsertInstruction(add)
var insertMoves []struct{ src, dst regalloc.VReg }
m := &mockMachine{insertMove: func(dst, src regalloc.VReg) {
insertMoves = append(insertMoves, struct{ src, dst regalloc.VReg }{src: src, dst: dst})
}}
c := newCompiler(context.Background(), m, builder)
c.ssaValueDefinitions = []SSAValueDefinition{{}, {}}
c.ssaValueToVRegs = []regalloc.VReg{0, 1}
return c, []ssa.Value{add.Return()}, blk, func(t *testing.T) {
require.Equal(t, 1, len(insertMoves))
require.Equal(t, regalloc.VRegID(1), insertMoves[0].src.ID())
require.Equal(t, regalloc.VRegID(0), insertMoves[0].dst.ID())
}
},
},
} {
t.Run(tc.name, func(t *testing.T) {
builder := ssa.NewBuilder()
c, args, succ, verify := tc.setup(builder)
c.lowerBlockArguments(args, succ)
verify(t)
})
}
}