github.com/tetratelabs/wazero@v1.7.3-0.20240513003603-48f702e154b5/internal/engine/wazevo/backend/isa/arm64/machine_regalloc_test.go (about)

     1  package arm64
     2  
     3  import (
     4  	"testing"
     5  
     6  	"github.com/tetratelabs/wazero/internal/engine/wazevo/backend/regalloc"
     7  	"github.com/tetratelabs/wazero/internal/engine/wazevo/ssa"
     8  	"github.com/tetratelabs/wazero/internal/testing/require"
     9  )
    10  
    11  func TestRegAllocFunctionImpl_ReloadRegisterAfter(t *testing.T) {
    12  	ctx, _, m := newSetupWithMockContext()
    13  
    14  	ctx.typeOf = map[regalloc.VRegID]ssa.Type{x1VReg.ID(): ssa.TypeI64, v1VReg.ID(): ssa.TypeF64}
    15  	i1, i2 := m.allocateNop(), m.allocateNop()
    16  	i1.next = i2
    17  	i2.prev = i1
    18  
    19  	m.InsertReloadRegisterAt(x1VReg, i1, true)
    20  	m.InsertReloadRegisterAt(v1VReg, i1, true)
    21  
    22  	require.NotEqual(t, i1, i2.prev)
    23  	require.NotEqual(t, i1.next, i2)
    24  	fload, iload := i1.next, i1.next.next
    25  	require.Equal(t, fload.prev, i1)
    26  	require.Equal(t, i1, fload.prev)
    27  	require.Equal(t, iload.next, i2)
    28  	require.Equal(t, iload, i2.prev)
    29  
    30  	require.Equal(t, iload.kind, uLoad64)
    31  	require.Equal(t, fload.kind, fpuLoad64)
    32  
    33  	m.executableContext.RootInstr = i1
    34  	require.Equal(t, `
    35  	ldr d1, [sp, #0x18]
    36  	ldr x1, [sp, #0x10]
    37  `, m.Format())
    38  }
    39  
    40  func TestRegAllocFunctionImpl_StoreRegisterBefore(t *testing.T) {
    41  	ctx, _, m := newSetupWithMockContext()
    42  
    43  	ctx.typeOf = map[regalloc.VRegID]ssa.Type{x1VReg.ID(): ssa.TypeI64, v1VReg.ID(): ssa.TypeF64}
    44  	i1, i2 := m.allocateNop(), m.allocateNop()
    45  	i1.next = i2
    46  	i2.prev = i1
    47  
    48  	m.InsertStoreRegisterAt(x1VReg, i2, false)
    49  	m.InsertStoreRegisterAt(v1VReg, i2, false)
    50  
    51  	require.NotEqual(t, i1, i2.prev)
    52  	require.NotEqual(t, i1.next, i2)
    53  	iload, fload := i1.next, i1.next.next
    54  	require.Equal(t, iload.prev, i1)
    55  	require.Equal(t, i1, iload.prev)
    56  	require.Equal(t, fload.next, i2)
    57  	require.Equal(t, fload, i2.prev)
    58  
    59  	require.Equal(t, iload.kind, store64)
    60  	require.Equal(t, fload.kind, fpuStore64)
    61  
    62  	m.executableContext.RootInstr = i1
    63  	require.Equal(t, `
    64  	str x1, [sp, #0x10]
    65  	str d1, [sp, #0x18]
    66  `, m.Format())
    67  }
    68  
    69  func TestMachine_insertStoreRegisterAt(t *testing.T) {
    70  	for _, tc := range []struct {
    71  		spillSlotSize int64
    72  		expected      string
    73  	}{
    74  		{
    75  			spillSlotSize: 0,
    76  			expected: `
    77  	udf
    78  	str x1, [sp, #0x10]
    79  	str d1, [sp, #0x18]
    80  	exit_sequence x30
    81  `,
    82  		},
    83  		{
    84  			spillSlotSize: 0xffff,
    85  			expected: `
    86  	udf
    87  	movz x27, #0xf, lsl 0
    88  	movk x27, #0x1, lsl 16
    89  	str x1, [sp, x27]
    90  	movz x27, #0x17, lsl 0
    91  	movk x27, #0x1, lsl 16
    92  	str d1, [sp, x27]
    93  	exit_sequence x30
    94  `,
    95  		},
    96  		{
    97  			spillSlotSize: 0xffff_00,
    98  			expected: `
    99  	udf
   100  	movz x27, #0xff10, lsl 0
   101  	movk x27, #0xff, lsl 16
   102  	str x1, [sp, x27]
   103  	movz x27, #0xff18, lsl 0
   104  	movk x27, #0xff, lsl 16
   105  	str d1, [sp, x27]
   106  	exit_sequence x30
   107  `,
   108  		},
   109  	} {
   110  		t.Run(tc.expected, func(t *testing.T) {
   111  			ctx, _, m := newSetupWithMockContext()
   112  			m.spillSlotSize = tc.spillSlotSize
   113  
   114  			for _, after := range []bool{false, true} {
   115  				var name string
   116  				if after {
   117  					name = "after"
   118  				} else {
   119  					name = "before"
   120  				}
   121  				t.Run(name, func(t *testing.T) {
   122  					ctx.typeOf = map[regalloc.VRegID]ssa.Type{x1VReg.ID(): ssa.TypeI64, v1VReg.ID(): ssa.TypeF64}
   123  					i1, i2 := m.allocateInstr().asUDF(), m.allocateInstr().asExitSequence(x30VReg)
   124  					i1.next = i2
   125  					i2.prev = i1
   126  
   127  					if after {
   128  						m.InsertStoreRegisterAt(v1VReg, i1, after)
   129  						m.InsertStoreRegisterAt(x1VReg, i1, after)
   130  					} else {
   131  						m.InsertStoreRegisterAt(x1VReg, i2, after)
   132  						m.InsertStoreRegisterAt(v1VReg, i2, after)
   133  					}
   134  					m.executableContext.RootInstr = i1
   135  					require.Equal(t, tc.expected, m.Format())
   136  				})
   137  			}
   138  		})
   139  	}
   140  }
   141  
   142  func TestMachine_insertReloadRegisterAt(t *testing.T) {
   143  	for _, tc := range []struct {
   144  		spillSlotSize int64
   145  		expected      string
   146  	}{
   147  		{
   148  			spillSlotSize: 0,
   149  			expected: `
   150  	udf
   151  	ldr x1, [sp, #0x10]
   152  	ldr d1, [sp, #0x18]
   153  	exit_sequence x30
   154  `,
   155  		},
   156  		{
   157  			spillSlotSize: 0xffff,
   158  			expected: `
   159  	udf
   160  	movz x27, #0xf, lsl 0
   161  	movk x27, #0x1, lsl 16
   162  	ldr x1, [sp, x27]
   163  	movz x27, #0x17, lsl 0
   164  	movk x27, #0x1, lsl 16
   165  	ldr d1, [sp, x27]
   166  	exit_sequence x30
   167  `,
   168  		},
   169  		{
   170  			spillSlotSize: 0xffff_00,
   171  			expected: `
   172  	udf
   173  	movz x27, #0xff10, lsl 0
   174  	movk x27, #0xff, lsl 16
   175  	ldr x1, [sp, x27]
   176  	movz x27, #0xff18, lsl 0
   177  	movk x27, #0xff, lsl 16
   178  	ldr d1, [sp, x27]
   179  	exit_sequence x30
   180  `,
   181  		},
   182  	} {
   183  		t.Run(tc.expected, func(t *testing.T) {
   184  			ctx, _, m := newSetupWithMockContext()
   185  			m.spillSlotSize = tc.spillSlotSize
   186  
   187  			for _, after := range []bool{false, true} {
   188  				var name string
   189  				if after {
   190  					name = "after"
   191  				} else {
   192  					name = "before"
   193  				}
   194  				t.Run(name, func(t *testing.T) {
   195  					ctx.typeOf = map[regalloc.VRegID]ssa.Type{x1VReg.ID(): ssa.TypeI64, v1VReg.ID(): ssa.TypeF64}
   196  					i1, i2 := m.allocateInstr().asUDF(), m.allocateInstr().asExitSequence(x30VReg)
   197  					i1.next = i2
   198  					i2.prev = i1
   199  
   200  					if after {
   201  						m.InsertReloadRegisterAt(v1VReg, i1, after)
   202  						m.InsertReloadRegisterAt(x1VReg, i1, after)
   203  					} else {
   204  						m.InsertReloadRegisterAt(x1VReg, i2, after)
   205  						m.InsertReloadRegisterAt(v1VReg, i2, after)
   206  					}
   207  					m.executableContext.RootInstr = i1
   208  
   209  					require.Equal(t, tc.expected, m.Format())
   210  				})
   211  			}
   212  		})
   213  	}
   214  }
   215  
   216  func TestRegMachine_ClobberedRegisters(t *testing.T) {
   217  	_, _, m := newSetupWithMockContext()
   218  	m.ClobberedRegisters([]regalloc.VReg{v19VReg, v19VReg, v19VReg, v19VReg})
   219  	require.Equal(t, []regalloc.VReg{v19VReg, v19VReg, v19VReg, v19VReg}, m.clobberedRegs)
   220  }
   221  
   222  func TestMachineMachineswap(t *testing.T) {
   223  	for _, tc := range []struct {
   224  		x1, x2, tmp regalloc.VReg
   225  		expected    string
   226  	}{
   227  		{
   228  			x1:  x18VReg,
   229  			x2:  x19VReg,
   230  			tmp: x20VReg,
   231  			expected: `
   232  	udf
   233  	mov x20, x18
   234  	mov x18, x19
   235  	mov x19, x20
   236  	exit_sequence x30
   237  `,
   238  		},
   239  		{
   240  			x1: x18VReg,
   241  			x2: x19VReg,
   242  			// Tmp not given.
   243  			expected: `
   244  	udf
   245  	mov x27, x18
   246  	mov x18, x19
   247  	mov x19, x27
   248  	exit_sequence x30
   249  `,
   250  		},
   251  		{
   252  			x1:  v18VReg,
   253  			x2:  v19VReg,
   254  			tmp: v11VReg,
   255  			expected: `
   256  	udf
   257  	mov v11.16b, v18.16b
   258  	mov v18.16b, v19.16b
   259  	mov v19.16b, v11.16b
   260  	exit_sequence x30
   261  `,
   262  		},
   263  		{
   264  			x1: v18VReg,
   265  			x2: v19VReg,
   266  			// Tmp not given.
   267  			expected: `
   268  	udf
   269  	str d18, [sp, #0x10]
   270  	mov v18.16b, v19.16b
   271  	ldr d19, [sp, #0x10]
   272  	exit_sequence x30
   273  `,
   274  		},
   275  	} {
   276  		t.Run(tc.expected, func(t *testing.T) {
   277  			ctx, _, m := newSetupWithMockContext()
   278  
   279  			ctx.typeOf = map[regalloc.VRegID]ssa.Type{
   280  				x18VReg.ID(): ssa.TypeI64, x19VReg.ID(): ssa.TypeI64,
   281  				v18VReg.ID(): ssa.TypeF64, v19VReg.ID(): ssa.TypeF64,
   282  			}
   283  			cur, i2 := m.allocateInstr().asUDF(), m.allocateInstr().asExitSequence(x30VReg)
   284  			cur.next = i2
   285  			i2.prev = cur
   286  
   287  			m.Swap(cur, tc.x1, tc.x2, tc.tmp)
   288  			m.executableContext.RootInstr = cur
   289  
   290  			require.Equal(t, tc.expected, m.Format())
   291  		})
   292  	}
   293  }