github.com/tetratelabs/wazero@v1.7.3-0.20240513003603-48f702e154b5/internal/engine/wazevo/backend/compiler_lower_test.go (about) 1 package backend 2 3 import ( 4 "context" 5 "testing" 6 7 "github.com/tetratelabs/wazero/internal/engine/wazevo/backend/regalloc" 8 "github.com/tetratelabs/wazero/internal/engine/wazevo/ssa" 9 "github.com/tetratelabs/wazero/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 argResultInts: []regalloc.RealReg{regalloc.RealReg(0), regalloc.RealReg(1)}, 54 argResultFloats: []regalloc.RealReg{ 55 regalloc.RealReg(2), regalloc.RealReg(3), regalloc.RealReg(4), regalloc.RealReg(5), 56 }, 57 } 58 59 c := newCompiler(context.Background(), m, builder) 60 c.ssaValueDefinitions = []SSAValueDefinition{{Instr: i1}, {Instr: i2}, {Instr: f1}, {Instr: f2}} 61 c.ssaValueToVRegs = []regalloc.VReg{0, 1, 2, 3, 4, 5, 6, 7} 62 return c, []ssa.Value{i1.Return(), i2.Return(), f1.Return(), f2.Return()}, succ, func(t *testing.T) { 63 require.Equal(t, 4, len(insertedConstInstructions)) 64 require.Equal(t, i1, insertedConstInstructions[0].instr) 65 require.Equal(t, regalloc.VReg(4), insertedConstInstructions[0].target) 66 require.Equal(t, i2, insertedConstInstructions[1].instr) 67 require.Equal(t, regalloc.VReg(5), insertedConstInstructions[1].target) 68 require.Equal(t, f1, insertedConstInstructions[2].instr) 69 require.Equal(t, regalloc.VReg(6), insertedConstInstructions[2].target) 70 require.Equal(t, f2, insertedConstInstructions[3].instr) 71 require.Equal(t, regalloc.VReg(7), insertedConstInstructions[3].target) 72 } 73 }, 74 }, 75 { 76 name: "overlap", 77 setup: func(builder ssa.Builder) (*compiler, []ssa.Value, ssa.BasicBlock, func(t *testing.T)) { 78 blk := builder.AllocateBasicBlock() 79 v1 := blk.AddParam(builder, ssa.TypeI32) 80 v2 := blk.AddParam(builder, ssa.TypeI32) 81 v3 := blk.AddParam(builder, ssa.TypeF32) 82 83 var insertMoves []struct{ src, dst regalloc.VReg } 84 m := &mockMachine{insertMove: func(dst, src regalloc.VReg) { 85 insertMoves = append(insertMoves, struct{ src, dst regalloc.VReg }{src: src, dst: dst}) 86 }} 87 c := newCompiler(context.Background(), m, builder) 88 c.ssaValueDefinitions = []SSAValueDefinition{{}, {}, {}, {}} 89 c.ssaValueToVRegs = []regalloc.VReg{0, 1, 2, 3} 90 c.nextVRegID = 100 // Temporary reg should start with 100. 91 return c, []ssa.Value{v2, v1, v3 /* Swaps v1, v2 and pass v3 as-is. */}, blk, func(t *testing.T) { 92 require.Equal(t, 6, len(insertMoves)) // Three values are overlapped. 93 mov1, mov2, mov3, mov4, mov5, mov6 := insertMoves[0], insertMoves[1], insertMoves[2], insertMoves[3], insertMoves[4], insertMoves[5] 94 // Save the values to the temporary registers. 95 require.Equal(t, regalloc.VRegID(1), mov1.src.ID()) 96 require.Equal(t, regalloc.VRegID(100), mov1.dst.ID()) 97 require.Equal(t, regalloc.VRegID(0), mov2.src.ID()) 98 require.Equal(t, regalloc.VRegID(101), mov2.dst.ID()) 99 require.Equal(t, regalloc.VRegID(2), mov3.src.ID()) 100 require.Equal(t, regalloc.VRegID(102), mov3.dst.ID()) 101 // Then move back to the original place. 102 require.Equal(t, regalloc.VRegID(100), mov4.src.ID()) 103 require.Equal(t, regalloc.VRegID(0), mov4.dst.ID()) 104 require.Equal(t, regalloc.VRegID(101), mov5.src.ID()) 105 require.Equal(t, regalloc.VRegID(1), mov5.dst.ID()) 106 require.Equal(t, regalloc.VRegID(102), mov6.src.ID()) 107 require.Equal(t, regalloc.VRegID(2), mov6.dst.ID()) 108 } 109 }, 110 }, 111 { 112 name: "no overlap", 113 setup: func(builder ssa.Builder) (*compiler, []ssa.Value, ssa.BasicBlock, func(t *testing.T)) { 114 blk := builder.AllocateBasicBlock() 115 builder.SetCurrentBlock(blk) 116 i32 := blk.AddParam(builder, ssa.TypeI32) 117 add := builder.AllocateInstruction() 118 add.AsIadd(i32, i32) 119 builder.InsertInstruction(add) 120 121 var insertMoves []struct{ src, dst regalloc.VReg } 122 m := &mockMachine{insertMove: func(dst, src regalloc.VReg) { 123 insertMoves = append(insertMoves, struct{ src, dst regalloc.VReg }{src: src, dst: dst}) 124 }} 125 c := newCompiler(context.Background(), m, builder) 126 c.ssaValueDefinitions = []SSAValueDefinition{{}, {}} 127 c.ssaValueToVRegs = []regalloc.VReg{0, 1} 128 return c, []ssa.Value{add.Return()}, blk, func(t *testing.T) { 129 require.Equal(t, 1, len(insertMoves)) 130 require.Equal(t, regalloc.VRegID(1), insertMoves[0].src.ID()) 131 require.Equal(t, regalloc.VRegID(0), insertMoves[0].dst.ID()) 132 } 133 }, 134 }, 135 } { 136 t.Run(tc.name, func(t *testing.T) { 137 builder := ssa.NewBuilder() 138 c, args, succ, verify := tc.setup(builder) 139 c.lowerBlockArguments(args, succ) 140 verify(t) 141 }) 142 } 143 }