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 }