github.com/tetratelabs/wazero@v1.7.3-0.20240513003603-48f702e154b5/internal/engine/wazevo/backend/isa/amd64/lower_mem_test.go (about) 1 package amd64 2 3 import ( 4 "runtime" 5 "strings" 6 "testing" 7 8 "github.com/tetratelabs/wazero/internal/engine/wazevo/backend" 9 "github.com/tetratelabs/wazero/internal/engine/wazevo/backend/regalloc" 10 "github.com/tetratelabs/wazero/internal/engine/wazevo/ssa" 11 "github.com/tetratelabs/wazero/internal/testing/require" 12 ) 13 14 func TestMachine_lowerToAddressMode(t *testing.T) { 15 _, _, m := newSetupWithMockContext() 16 defer func() { 17 runtime.KeepAlive(m) 18 }() 19 newAmodeImmReg := m.newAmodeImmReg 20 newAmodeRegRegShift := m.newAmodeRegRegShift 21 22 nextVReg := regalloc.VReg(100).SetRegType(regalloc.RegTypeInt) 23 for _, tc := range []struct { 24 name string 25 in func(*mockCompiler, ssa.Builder, *machine) (ptr ssa.Value, offset uint32) 26 insts []string 27 am *amode 28 }{ 29 { 30 name: "iadd const, const; offset != 0", 31 in: func(ctx *mockCompiler, b ssa.Builder, m *machine) (ptr ssa.Value, offset uint32) { 32 iconst1 := b.AllocateInstruction().AsIconst32(1).Insert(b) 33 iconst2 := b.AllocateInstruction().AsIconst32(2).Insert(b) 34 iadd := b.AllocateInstruction().AsIadd(iconst1.Return(), iconst2.Return()).Insert(b) 35 ptr = iadd.Return() 36 offset = 3 37 ctx.definitions[iconst1.Return()] = &backend.SSAValueDefinition{Instr: iconst1} 38 ctx.definitions[iconst2.Return()] = &backend.SSAValueDefinition{Instr: iconst2} 39 ctx.definitions[ptr] = &backend.SSAValueDefinition{Instr: iadd} 40 return 41 }, 42 insts: []string{ 43 "movabsq $6, %r100?", 44 }, 45 am: newAmodeImmReg(0, nextVReg), 46 }, 47 { 48 name: "iadd const, param; offset != 0", 49 in: func(ctx *mockCompiler, b ssa.Builder, m *machine) (ptr ssa.Value, offset uint32) { 50 iconst1 := b.AllocateInstruction().AsIconst32(1).Insert(b) 51 p := b.CurrentBlock().AddParam(b, ssa.TypeI64) 52 iadd := b.AllocateInstruction().AsIadd(iconst1.Return(), p).Insert(b) 53 ptr = iadd.Return() 54 offset = 3 55 ctx.definitions[iconst1.Return()] = &backend.SSAValueDefinition{Instr: iconst1} 56 ctx.definitions[p] = &backend.SSAValueDefinition{BlockParamValue: p, BlkParamVReg: raxVReg} 57 ctx.definitions[ptr] = &backend.SSAValueDefinition{Instr: iadd} 58 return 59 }, 60 am: newAmodeImmReg(1+3, raxVReg), 61 }, 62 { 63 name: "iadd param, param; offset != 0", 64 in: func(ctx *mockCompiler, b ssa.Builder, m *machine) (ptr ssa.Value, offset uint32) { 65 p1 := b.CurrentBlock().AddParam(b, ssa.TypeI64) 66 p2 := b.CurrentBlock().AddParam(b, ssa.TypeI64) 67 iadd := b.AllocateInstruction().AsIadd(p1, p2).Insert(b) 68 ptr = iadd.Return() 69 offset = 3 70 ctx.definitions[p1] = &backend.SSAValueDefinition{BlockParamValue: p1, BlkParamVReg: raxVReg} 71 ctx.definitions[p2] = &backend.SSAValueDefinition{BlockParamValue: p2, BlkParamVReg: rcxVReg} 72 ctx.definitions[ptr] = &backend.SSAValueDefinition{Instr: iadd} 73 return 74 }, 75 am: newAmodeRegRegShift(3, raxVReg, rcxVReg, 0), 76 }, 77 { 78 name: "huge offset", 79 in: func(ctx *mockCompiler, b ssa.Builder, m *machine) (ptr ssa.Value, offset uint32) { 80 ptr = b.CurrentBlock().AddParam(b, ssa.TypeI64) 81 offset = 1 << 31 82 ctx.definitions[ptr] = &backend.SSAValueDefinition{BlockParamValue: ptr, BlkParamVReg: raxVReg} 83 return 84 }, 85 insts: []string{ 86 "movabsq $2147483648, %r100?", 87 }, 88 am: newAmodeRegRegShift(0, nextVReg, raxVReg, 0), 89 }, 90 91 // The other iadd cases are covered by TestMachine_lowerAddendsToAmode. 92 { 93 name: "uextend const32", 94 in: func(ctx *mockCompiler, b ssa.Builder, m *machine) (ptr ssa.Value, offset uint32) { 95 iconst32 := b.AllocateInstruction().AsIconst32(123).Insert(b) 96 uextend := b.AllocateInstruction().AsUExtend(iconst32.Return(), 32, 64).Insert(b) 97 ctx.definitions[iconst32.Return()] = &backend.SSAValueDefinition{Instr: iconst32} 98 ctx.definitions[uextend.Return()] = &backend.SSAValueDefinition{Instr: uextend} 99 return uextend.Return(), 0 100 }, 101 insts: []string{ 102 "movabsq $123, %r100?", 103 }, 104 am: newAmodeImmReg(0, nextVReg), 105 }, 106 { 107 name: "Ishl param64, const", 108 in: func(ctx *mockCompiler, b ssa.Builder, m *machine) (ptr ssa.Value, offset uint32) { 109 p := b.CurrentBlock().AddParam(b, ssa.TypeI64) 110 iconst64 := b.AllocateInstruction().AsIconst64(2).Insert(b) 111 ishl := b.AllocateInstruction().AsIshl(p, iconst64.Return()).Insert(b) 112 ctx.definitions[p] = &backend.SSAValueDefinition{BlockParamValue: p, BlkParamVReg: raxVReg} 113 ctx.definitions[iconst64.Return()] = &backend.SSAValueDefinition{Instr: iconst64} 114 ctx.definitions[ishl.Return()] = &backend.SSAValueDefinition{Instr: ishl} 115 return ishl.Return(), 1 << 30 116 }, 117 insts: []string{ 118 "xor %r100?, %r100?", 119 }, 120 am: newAmodeRegRegShift(1<<30, nextVReg, raxVReg, 2), 121 }, 122 { 123 name: "add Iconst, (Ishl param64, const)", 124 in: func(ctx *mockCompiler, b ssa.Builder, m *machine) (ptr ssa.Value, offset uint32) { 125 p1 := b.CurrentBlock().AddParam(b, ssa.TypeI64) 126 p2 := b.CurrentBlock().AddParam(b, ssa.TypeI64) 127 const2 := b.AllocateInstruction().AsIconst64(2).Insert(b) 128 ishl := b.AllocateInstruction().AsIshl(p1, const2.Return()).Insert(b) 129 iadd := b.AllocateInstruction().AsIadd(p2, ishl.Return()).Insert(b) 130 ctx.definitions[p1] = &backend.SSAValueDefinition{BlockParamValue: p1, BlkParamVReg: raxVReg} 131 ctx.definitions[p2] = &backend.SSAValueDefinition{BlockParamValue: p2, BlkParamVReg: rcxVReg} 132 ctx.definitions[const2.Return()] = &backend.SSAValueDefinition{Instr: const2} 133 ctx.definitions[ishl.Return()] = &backend.SSAValueDefinition{Instr: ishl} 134 ctx.definitions[iadd.Return()] = &backend.SSAValueDefinition{Instr: iadd} 135 return iadd.Return(), 1 << 30 136 }, 137 am: newAmodeRegRegShift(1<<30, rcxVReg, raxVReg, 2), 138 }, 139 } { 140 t.Run(tc.name, func(t *testing.T) { 141 ctx, b, m := newSetupWithMockContext() 142 ctx.vRegCounter = int(nextVReg.ID()) - 1 143 ptr, offset := tc.in(ctx, b, m) 144 actual := m.lowerToAddressMode(ptr, offset) 145 require.Equal(t, strings.Join(tc.insts, "\n"), formatEmittedInstructionsInCurrentBlock(m)) 146 require.Equal(t, tc.am, actual, actual.String()) 147 }) 148 } 149 } 150 151 func TestMachine_lowerAddendFromInstr(t *testing.T) { 152 for _, tc := range []struct { 153 name string 154 in func(*mockCompiler, ssa.Builder, *machine) *ssa.Instruction 155 exp addend 156 }{ 157 { 158 name: "iconst64", 159 in: func(ctx *mockCompiler, b ssa.Builder, m *machine) *ssa.Instruction { 160 return b.AllocateInstruction().AsIconst64(123 << 32).Insert(b) 161 }, 162 exp: addend{regalloc.VRegInvalid, 123 << 32, 0}, 163 }, 164 { 165 name: "iconst32", 166 in: func(ctx *mockCompiler, b ssa.Builder, m *machine) *ssa.Instruction { 167 return b.AllocateInstruction().AsIconst32(123).Insert(b) 168 }, 169 exp: addend{regalloc.VRegInvalid, 123, 0}, 170 }, 171 { 172 name: "uextend const32", 173 in: func(ctx *mockCompiler, b ssa.Builder, m *machine) *ssa.Instruction { 174 iconst32 := b.AllocateInstruction().AsIconst32(123).Insert(b) 175 ctx.definitions[iconst32.Return()] = &backend.SSAValueDefinition{Instr: iconst32} 176 return b.AllocateInstruction().AsUExtend(iconst32.Return(), 32, 64).Insert(b) 177 }, 178 exp: addend{regalloc.VRegInvalid, 123, 0}, 179 }, 180 { 181 name: "uextend const64", 182 in: func(ctx *mockCompiler, b ssa.Builder, m *machine) *ssa.Instruction { 183 p := b.CurrentBlock().AddParam(b, ssa.TypeI32) 184 ctx.definitions[p] = &backend.SSAValueDefinition{BlkParamVReg: raxVReg, BlockParamValue: p} 185 return b.AllocateInstruction().AsUExtend(p, 32, 64).Insert(b) 186 }, 187 exp: addend{raxVReg, 0, 0}, 188 }, 189 { 190 name: "uextend param i32", 191 in: func(ctx *mockCompiler, b ssa.Builder, m *machine) *ssa.Instruction { 192 p := b.CurrentBlock().AddParam(b, ssa.TypeI32) 193 ctx.definitions[p] = &backend.SSAValueDefinition{BlkParamVReg: raxVReg, BlockParamValue: p} 194 return b.AllocateInstruction().AsUExtend(p, 32, 64).Insert(b) 195 }, 196 exp: addend{raxVReg, 0, 0}, 197 }, 198 { 199 name: "sextend const32", 200 in: func(ctx *mockCompiler, b ssa.Builder, m *machine) *ssa.Instruction { 201 iconst32 := b.AllocateInstruction().AsIconst32(123).Insert(b) 202 ctx.definitions[iconst32.Return()] = &backend.SSAValueDefinition{Instr: iconst32} 203 return b.AllocateInstruction().AsSExtend(iconst32.Return(), 32, 64).Insert(b) 204 }, 205 exp: addend{regalloc.VRegInvalid, 123, 0}, 206 }, 207 { 208 name: "sextend const64", 209 in: func(ctx *mockCompiler, b ssa.Builder, m *machine) *ssa.Instruction { 210 p := b.CurrentBlock().AddParam(b, ssa.TypeI32) 211 ctx.definitions[p] = &backend.SSAValueDefinition{BlkParamVReg: raxVReg, BlockParamValue: p} 212 return b.AllocateInstruction().AsSExtend(p, 32, 64).Insert(b) 213 }, 214 exp: addend{raxVReg, 0, 0}, 215 }, 216 { 217 name: "sextend param i32", 218 in: func(ctx *mockCompiler, b ssa.Builder, m *machine) *ssa.Instruction { 219 p := b.CurrentBlock().AddParam(b, ssa.TypeI32) 220 ctx.definitions[p] = &backend.SSAValueDefinition{BlkParamVReg: raxVReg, BlockParamValue: p} 221 return b.AllocateInstruction().AsSExtend(p, 32, 64).Insert(b) 222 }, 223 exp: addend{raxVReg, 0, 0}, 224 }, 225 } { 226 t.Run(tc.name, func(t *testing.T) { 227 ctx, b, m := newSetupWithMockContext() 228 a := m.lowerAddendFromInstr(tc.in(ctx, b, m)) 229 require.Equal(t, tc.exp, a) 230 }) 231 } 232 } 233 234 func TestMachine_lowerAddendsToAmode(t *testing.T) { 235 _, _, m := newSetupWithMockContext() 236 defer func() { 237 runtime.KeepAlive(m) 238 }() 239 newAmodeImmReg := m.newAmodeImmReg 240 newAmodeRegRegShift := m.newAmodeRegRegShift 241 242 x1, x2 := raxVReg, rcxVReg 243 244 nextVReg, nextNextVReg := regalloc.VReg(100).SetRegType(regalloc.RegTypeInt), regalloc.VReg(101).SetRegType(regalloc.RegTypeInt) 245 _ = nextNextVReg 246 for _, tc := range []struct { 247 name string 248 x, y addend 249 offset uint32 250 exp *amode 251 insts []string 252 }{ 253 { 254 name: "only offset", 255 x: addend{r: regalloc.VRegInvalid}, y: addend{r: regalloc.VRegInvalid}, 256 offset: 4095, 257 insts: []string{"movabsq $4095, %r100?"}, 258 exp: newAmodeImmReg(0, nextVReg), 259 }, 260 { 261 name: "only offset, offx, offy", 262 x: addend{r: regalloc.VRegInvalid, off: 1}, y: addend{r: regalloc.VRegInvalid, off: 2}, 263 offset: 4095, 264 insts: []string{"movabsq $4098, %r100?"}, 265 exp: newAmodeImmReg(0, nextVReg), 266 }, 267 { 268 name: "only offset, offx, offy; not fitting", 269 x: addend{r: regalloc.VRegInvalid, off: 1 << 30}, y: addend{r: regalloc.VRegInvalid, off: 2 << 30}, 270 offset: 4095, 271 insts: []string{"movabsq $3221229567, %r100?"}, 272 exp: newAmodeImmReg(0, nextVReg), 273 }, 274 { 275 name: "one a64 with imm32", 276 x: addend{r: x1}, y: addend{r: regalloc.VRegInvalid}, 277 offset: 4095, 278 exp: newAmodeImmReg(4095, x1), 279 }, 280 { 281 name: "one a64 with imm32", 282 x: addend{r: x1}, y: addend{r: regalloc.VRegInvalid}, 283 offset: 1 << 16, 284 exp: newAmodeImmReg(1<<16, x1), 285 }, 286 { 287 name: "two a64 with imm32", 288 x: addend{r: x1}, y: addend{r: x2}, 289 offset: 1 << 16, 290 exp: newAmodeRegRegShift(1<<16, x1, x2, 0), 291 }, 292 { 293 name: "two a64 with offset fitting", 294 x: addend{r: x1}, y: addend{r: x2}, 295 offset: 1 << 30, 296 exp: newAmodeRegRegShift(1<<30, x1, x2, 0), 297 }, 298 { 299 name: "rx with offset not fitting", 300 x: addend{r: x1}, y: addend{r: regalloc.VRegInvalid, off: 1 << 30}, 301 offset: 1 << 30, 302 insts: []string{ 303 "movabsq $2147483648, %r100?", 304 }, 305 exp: newAmodeRegRegShift(0, x1, nextVReg, 0), 306 }, 307 { 308 name: "ry with offset not fitting", 309 x: addend{r: regalloc.VRegInvalid, off: 1 << 30}, y: addend{r: x1}, 310 offset: 1 << 30, 311 insts: []string{ 312 "movabsq $2147483648, %r100?", 313 }, 314 exp: newAmodeRegRegShift(0, nextVReg, x1, 0), 315 }, 316 { 317 name: "rx with shift, ry with shift, offset != 0", 318 x: addend{r: x1, shift: 2}, y: addend{r: x2, shift: 3}, 319 offset: 1 << 30, 320 insts: []string{ 321 "shlq $2, %rax", 322 }, 323 exp: newAmodeRegRegShift(1<<30, x1, x2, 3), 324 }, 325 { 326 name: "rx, ry with shift, offset != 0", 327 x: addend{r: x1}, y: addend{r: x2, shift: 3}, 328 offset: 1 << 30, 329 exp: newAmodeRegRegShift(1<<30, x1, x2, 3), 330 }, 331 { 332 name: "rx with shift, ry, offset != 0", 333 x: addend{r: x1, shift: 3}, y: addend{r: x2}, 334 offset: 1 << 30, 335 exp: newAmodeRegRegShift(1<<30, x2, x1, 3), 336 }, 337 { 338 name: "rx with shift, ry invalid, offset != 0", 339 x: addend{r: x1, shift: 3}, y: addend{r: regalloc.VRegInvalid}, 340 offset: 1 << 30, 341 insts: []string{ 342 "xor %r100?, %r100?", 343 }, 344 exp: newAmodeRegRegShift(1<<30, nextVReg, x1, 3), 345 }, 346 { 347 name: "rx invalid, rx with shift, offset != 0", 348 x: addend{r: regalloc.VRegInvalid}, y: addend{r: x1, shift: 3}, 349 offset: 1 << 30, 350 insts: []string{ 351 "xor %r100?, %r100?", 352 }, 353 exp: newAmodeRegRegShift(1<<30, nextVReg, x1, 3), 354 }, 355 { 356 name: "huge offset", 357 x: addend{r: regalloc.VRegInvalid}, y: addend{r: x1, shift: 3}, 358 offset: 1 << 31, 359 insts: []string{ 360 "movabsq $2147483648, %r100?", 361 }, 362 exp: newAmodeRegRegShift(0, nextVReg, x1, 3), 363 }, 364 { 365 name: "huge offset", 366 x: addend{r: regalloc.VRegInvalid, off: 1 << 31}, y: addend{r: x1, shift: 3}, 367 offset: 0, 368 insts: []string{ 369 "movabsq $2147483648, %r100?", 370 }, 371 exp: newAmodeRegRegShift(0, nextVReg, x1, 3), 372 }, 373 } { 374 t.Run(tc.name, func(t *testing.T) { 375 ctx, _, m := newSetupWithMockContext() 376 ctx.vRegCounter = int(nextVReg.ID()) - 1 377 actual := m.lowerAddendsToAmode(tc.x, tc.y, tc.offset) 378 require.Equal(t, strings.Join(tc.insts, "\n"), formatEmittedInstructionsInCurrentBlock(m)) 379 require.Equal(t, tc.exp, actual, actual.String()) 380 }) 381 } 382 }