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  }