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  }