github.com/wasilibs/wazerox@v0.0.0-20240124024944-4923be63ab5f/internal/engine/wazevo/backend/compiler_lower_test.go (about) 1 package backend 2 3 import ( 4 "context" 5 "testing" 6 7 "github.com/wasilibs/wazerox/internal/engine/wazevo/backend/regalloc" 8 "github.com/wasilibs/wazerox/internal/engine/wazevo/ssa" 9 "github.com/wasilibs/wazerox/internal/testing/require" 10 ) 11 12 func TestCompiler_lowerBlockArguments(t *testing.T) { 13 for _, tc := range []struct { 14 name string 15 setup func(builder ssa.Builder) (c *compiler, args []ssa.Value, succ ssa.BasicBlock, verify func(t *testing.T)) 16 }{ 17 { 18 name: "all consts", 19 setup: func(builder ssa.Builder) (*compiler, []ssa.Value, ssa.BasicBlock, func(t *testing.T)) { 20 entryBlk := builder.AllocateBasicBlock() 21 builder.SetCurrentBlock(entryBlk) 22 i1 := builder.AllocateInstruction() 23 i1.AsIconst32(1) 24 i2 := builder.AllocateInstruction() 25 i2.AsIconst64(2) 26 f1 := builder.AllocateInstruction() 27 f1.AsF32const(3.0) 28 f2 := builder.AllocateInstruction() 29 f2.AsF64const(4.0) 30 31 builder.InsertInstruction(i1) 32 builder.InsertInstruction(i2) 33 builder.InsertInstruction(f1) 34 builder.InsertInstruction(f2) 35 36 succ := builder.AllocateBasicBlock() 37 succ.AddParam(builder, ssa.TypeI32) 38 succ.AddParam(builder, ssa.TypeI64) 39 succ.AddParam(builder, ssa.TypeF32) 40 succ.AddParam(builder, ssa.TypeF64) 41 42 var insertedConstInstructions []struct { 43 instr *ssa.Instruction 44 target regalloc.VReg 45 } 46 m := &mockMachine{ 47 insertLoadConstant: func(instr *ssa.Instruction, vr regalloc.VReg) { 48 insertedConstInstructions = append(insertedConstInstructions, struct { 49 instr *ssa.Instruction 50 target regalloc.VReg 51 }{instr: instr, target: vr}) 52 }, 53 } 54 55 c := newCompiler(context.Background(), m, builder) 56 c.ssaValueDefinitions = []SSAValueDefinition{{Instr: i1}, {Instr: i2}, {Instr: f1}, {Instr: f2}} 57 c.ssaValueToVRegs = []regalloc.VReg{0, 1, 2, 3, 4, 5, 6, 7} 58 return c, []ssa.Value{i1.Return(), i2.Return(), f1.Return(), f2.Return()}, succ, func(t *testing.T) { 59 require.Equal(t, 4, len(insertedConstInstructions)) 60 require.Equal(t, i1, insertedConstInstructions[0].instr) 61 require.Equal(t, regalloc.VReg(4), insertedConstInstructions[0].target) 62 require.Equal(t, i2, insertedConstInstructions[1].instr) 63 require.Equal(t, regalloc.VReg(5), insertedConstInstructions[1].target) 64 require.Equal(t, f1, insertedConstInstructions[2].instr) 65 require.Equal(t, regalloc.VReg(6), insertedConstInstructions[2].target) 66 require.Equal(t, f2, insertedConstInstructions[3].instr) 67 require.Equal(t, regalloc.VReg(7), insertedConstInstructions[3].target) 68 } 69 }, 70 }, 71 { 72 name: "overlap", 73 setup: func(builder ssa.Builder) (*compiler, []ssa.Value, ssa.BasicBlock, func(t *testing.T)) { 74 blk := builder.AllocateBasicBlock() 75 v1 := blk.AddParam(builder, ssa.TypeI32) 76 v2 := blk.AddParam(builder, ssa.TypeI32) 77 v3 := blk.AddParam(builder, ssa.TypeF32) 78 79 var insertMoves []struct{ src, dst regalloc.VReg } 80 m := &mockMachine{insertMove: func(dst, src regalloc.VReg) { 81 insertMoves = append(insertMoves, struct{ src, dst regalloc.VReg }{src: src, dst: dst}) 82 }} 83 c := newCompiler(context.Background(), m, builder) 84 c.ssaValueDefinitions = []SSAValueDefinition{{}, {}, {}, {}} 85 c.ssaValueToVRegs = []regalloc.VReg{0, 1, 2, 3} 86 c.nextVRegID = 100 // Temporary reg should start with 100. 87 return c, []ssa.Value{v2, v1, v3 /* Swaps v1, v2 and pass v3 as-is. */}, blk, func(t *testing.T) { 88 require.Equal(t, 6, len(insertMoves)) // Three values are overlapped. 89 mov1, mov2, mov3, mov4, mov5, mov6 := insertMoves[0], insertMoves[1], insertMoves[2], insertMoves[3], insertMoves[4], insertMoves[5] 90 // Save the values to the temporary registers. 91 require.Equal(t, regalloc.VRegID(1), mov1.src.ID()) 92 require.Equal(t, regalloc.VRegID(100), mov1.dst.ID()) 93 require.Equal(t, regalloc.VRegID(0), mov2.src.ID()) 94 require.Equal(t, regalloc.VRegID(101), mov2.dst.ID()) 95 require.Equal(t, regalloc.VRegID(2), mov3.src.ID()) 96 require.Equal(t, regalloc.VRegID(102), mov3.dst.ID()) 97 // Then move back to the original place. 98 require.Equal(t, regalloc.VRegID(100), mov4.src.ID()) 99 require.Equal(t, regalloc.VRegID(0), mov4.dst.ID()) 100 require.Equal(t, regalloc.VRegID(101), mov5.src.ID()) 101 require.Equal(t, regalloc.VRegID(1), mov5.dst.ID()) 102 require.Equal(t, regalloc.VRegID(102), mov6.src.ID()) 103 require.Equal(t, regalloc.VRegID(2), mov6.dst.ID()) 104 } 105 }, 106 }, 107 { 108 name: "no overlap", 109 setup: func(builder ssa.Builder) (*compiler, []ssa.Value, ssa.BasicBlock, func(t *testing.T)) { 110 blk := builder.AllocateBasicBlock() 111 builder.SetCurrentBlock(blk) 112 i32 := blk.AddParam(builder, ssa.TypeI32) 113 add := builder.AllocateInstruction() 114 add.AsIadd(i32, i32) 115 builder.InsertInstruction(add) 116 117 var insertMoves []struct{ src, dst regalloc.VReg } 118 m := &mockMachine{insertMove: func(dst, src regalloc.VReg) { 119 insertMoves = append(insertMoves, struct{ src, dst regalloc.VReg }{src: src, dst: dst}) 120 }} 121 c := newCompiler(context.Background(), m, builder) 122 c.ssaValueDefinitions = []SSAValueDefinition{{}, {}} 123 c.ssaValueToVRegs = []regalloc.VReg{0, 1} 124 return c, []ssa.Value{add.Return()}, blk, func(t *testing.T) { 125 require.Equal(t, 1, len(insertMoves)) 126 require.Equal(t, regalloc.VRegID(1), insertMoves[0].src.ID()) 127 require.Equal(t, regalloc.VRegID(0), insertMoves[0].dst.ID()) 128 } 129 }, 130 }, 131 } { 132 t.Run(tc.name, func(t *testing.T) { 133 builder := ssa.NewBuilder() 134 c, args, succ, verify := tc.setup(builder) 135 c.lowerBlockArguments(args, succ) 136 verify(t) 137 }) 138 } 139 }