github.com/tetratelabs/wazero@v1.7.3-0.20240513003603-48f702e154b5/internal/engine/wazevo/backend/isa/arm64/lower_instr_operands_test.go (about) 1 package arm64 2 3 import ( 4 "strings" 5 "testing" 6 7 "github.com/tetratelabs/wazero/internal/engine/wazevo/backend" 8 "github.com/tetratelabs/wazero/internal/engine/wazevo/backend/regalloc" 9 "github.com/tetratelabs/wazero/internal/engine/wazevo/ssa" 10 "github.com/tetratelabs/wazero/internal/testing/require" 11 ) 12 13 func Test_asImm12(t *testing.T) { 14 v, shift, ok := asImm12(0xfff) 15 require.True(t, ok) 16 require.True(t, shift == 0) 17 require.Equal(t, uint16(0xfff), v) 18 19 _, _, ok = asImm12(0xfff << 1) 20 require.False(t, ok) 21 22 v, shift, ok = asImm12(0xabc << 12) 23 require.True(t, ok) 24 require.True(t, shift == 1) 25 require.Equal(t, uint16(0xabc), v) 26 27 _, _, ok = asImm12(0xabc<<12 | 0b1) 28 require.False(t, ok) 29 } 30 31 func TestMachine_getOperand_NR(t *testing.T) { 32 for _, tc := range []struct { 33 name string 34 setup func(*mockCompiler, ssa.Builder, *machine) (def *backend.SSAValueDefinition, mode extMode) 35 exp operand 36 instructions []string 37 }{ 38 { 39 name: "block param - no extend", 40 setup: func(ctx *mockCompiler, builder ssa.Builder, m *machine) (def *backend.SSAValueDefinition, mode extMode) { 41 blk := builder.CurrentBlock() 42 v := blk.AddParam(builder, ssa.TypeI64) 43 def = &backend.SSAValueDefinition{BlkParamVReg: regToVReg(x4), BlockParamValue: v} 44 return def, extModeZeroExtend64 45 }, 46 exp: operandNR(regToVReg(x4)), 47 }, 48 { 49 name: "block param - zero extend", 50 setup: func(ctx *mockCompiler, builder ssa.Builder, m *machine) (def *backend.SSAValueDefinition, mode extMode) { 51 blk := builder.CurrentBlock() 52 v := blk.AddParam(builder, ssa.TypeI32) 53 def = &backend.SSAValueDefinition{BlkParamVReg: regToVReg(x4), BlockParamValue: v} 54 return def, extModeZeroExtend64 55 }, 56 exp: operandNR(regalloc.VReg(1).SetRegType(regalloc.RegTypeInt)), 57 instructions: []string{"uxtw x1?, w4"}, 58 }, 59 { 60 name: "block param - sign extend", 61 setup: func(ctx *mockCompiler, builder ssa.Builder, m *machine) (def *backend.SSAValueDefinition, mode extMode) { 62 blk := builder.CurrentBlock() 63 v := blk.AddParam(builder, ssa.TypeI32) 64 def = &backend.SSAValueDefinition{BlkParamVReg: regToVReg(x4), BlockParamValue: v} 65 return def, extModeSignExtend64 66 }, 67 exp: operandNR(regalloc.VReg(1).SetRegType(regalloc.RegTypeInt)), 68 instructions: []string{"sxtw x1?, w4"}, 69 }, 70 { 71 name: "const instr", 72 setup: func(ctx *mockCompiler, builder ssa.Builder, m *machine) (def *backend.SSAValueDefinition, mode extMode) { 73 instr := builder.AllocateInstruction() 74 instr.AsIconst32(0xf00000f) 75 builder.InsertInstruction(instr) 76 def = &backend.SSAValueDefinition{Instr: instr, N: 0} 77 ctx.vRegCounter = 99 78 return 79 }, 80 exp: operandNR(regalloc.VReg(100).SetRegType(regalloc.RegTypeInt)), 81 instructions: []string{ 82 "movz w100?, #0xf, lsl 0", 83 "movk w100?, #0xf00, lsl 16", 84 }, 85 }, 86 { 87 name: "non const instr", 88 setup: func(ctx *mockCompiler, builder ssa.Builder, m *machine) (def *backend.SSAValueDefinition, mode extMode) { 89 c := builder.AllocateInstruction() 90 sig := &ssa.Signature{Results: []ssa.Type{ssa.TypeI64, ssa.TypeF64, ssa.TypeF64}} 91 builder.DeclareSignature(sig) 92 c.AsCall(ssa.FuncRef(0), sig, ssa.ValuesNil) 93 builder.InsertInstruction(c) 94 _, rs := c.Returns() 95 ctx.vRegMap[rs[1]] = regalloc.VReg(50) 96 def = &backend.SSAValueDefinition{Instr: c, N: 2} 97 return 98 }, 99 exp: operandNR(regalloc.VReg(50)), 100 }, 101 } { 102 t.Run(tc.name, func(t *testing.T) { 103 ctx, b, m := newSetupWithMockContext() 104 def, mode := tc.setup(ctx, b, m) 105 actual := m.getOperand_NR(def, mode) 106 require.Equal(t, tc.exp, actual) 107 require.Equal(t, strings.Join(tc.instructions, "\n"), formatEmittedInstructionsInCurrentBlock(m)) 108 }) 109 } 110 } 111 112 func TestMachine_getOperand_SR_NR(t *testing.T) { 113 ishlWithConstAmount := func( 114 ctx *mockCompiler, builder ssa.Builder, m *machine, 115 ) (def *backend.SSAValueDefinition, mode extMode, verify func(t *testing.T)) { 116 blk := builder.CurrentBlock() 117 // (p1+p2) << amount 118 p1 := blk.AddParam(builder, ssa.TypeI64) 119 p2 := blk.AddParam(builder, ssa.TypeI64) 120 add := builder.AllocateInstruction() 121 add.AsIadd(p1, p2) 122 builder.InsertInstruction(add) 123 addResult := add.Return() 124 125 amount := builder.AllocateInstruction() 126 amount.AsIconst32(14) 127 builder.InsertInstruction(amount) 128 129 amountVal := amount.Return() 130 131 ishl := builder.AllocateInstruction() 132 ishl.AsIshl(addResult, amountVal) 133 builder.InsertInstruction(ishl) 134 135 ctx.definitions[p1] = &backend.SSAValueDefinition{BlkParamVReg: regalloc.VReg(1), BlockParamValue: p1} 136 ctx.definitions[p2] = &backend.SSAValueDefinition{BlkParamVReg: regalloc.VReg(2), BlockParamValue: p2} 137 ctx.definitions[addResult] = &backend.SSAValueDefinition{Instr: add, N: 0} 138 ctx.definitions[amountVal] = &backend.SSAValueDefinition{Instr: amount, N: 0} 139 140 ctx.vRegMap[addResult] = regalloc.VReg(1234) 141 ctx.vRegMap[ishl.Return()] = regalloc.VReg(10) 142 def = &backend.SSAValueDefinition{Instr: ishl, N: 0} 143 mode = extModeNone 144 verify = func(t *testing.T) { 145 require.True(t, ishl.Lowered()) 146 require.True(t, amount.Lowered()) 147 } 148 return 149 } 150 151 for _, tc := range []struct { 152 name string 153 setup func(*mockCompiler, ssa.Builder, *machine) (def *backend.SSAValueDefinition, mode extMode, verify func(t *testing.T)) 154 exp operand 155 instructions []string 156 }{ 157 { 158 name: "block param", 159 setup: func(ctx *mockCompiler, builder ssa.Builder, m *machine) (def *backend.SSAValueDefinition, mode extMode, verify func(t *testing.T)) { 160 blk := builder.CurrentBlock() 161 v := blk.AddParam(builder, ssa.TypeI64) 162 def = &backend.SSAValueDefinition{BlkParamVReg: regToVReg(x4), BlockParamValue: v} 163 return def, extModeNone, func(t *testing.T) {} 164 }, 165 exp: operandNR(regToVReg(x4)), 166 }, 167 { 168 name: "ishl but not const amount", 169 setup: func(ctx *mockCompiler, builder ssa.Builder, m *machine) (def *backend.SSAValueDefinition, mode extMode, verify func(t *testing.T)) { 170 blk := builder.CurrentBlock() 171 // (p1+p2) << p3 172 p1 := blk.AddParam(builder, ssa.TypeI64) 173 p2 := blk.AddParam(builder, ssa.TypeI64) 174 p3 := blk.AddParam(builder, ssa.TypeI64) 175 add := builder.AllocateInstruction() 176 add.AsIadd(p1, p2) 177 builder.InsertInstruction(add) 178 addResult := add.Return() 179 180 ishl := builder.AllocateInstruction() 181 ishl.AsIshl(addResult, p3) 182 builder.InsertInstruction(ishl) 183 184 ctx.definitions[p1] = &backend.SSAValueDefinition{BlkParamVReg: regalloc.VReg(1), BlockParamValue: p1} 185 ctx.definitions[p2] = &backend.SSAValueDefinition{BlkParamVReg: regalloc.VReg(2), BlockParamValue: p2} 186 ctx.definitions[p3] = &backend.SSAValueDefinition{BlkParamVReg: regalloc.VReg(3), BlockParamValue: p3} 187 ctx.definitions[addResult] = &backend.SSAValueDefinition{Instr: add, N: 0} 188 ctx.vRegMap[addResult] = regalloc.VReg(1234) // whatever is fine. 189 ctx.vRegMap[ishl.Return()] = regalloc.VReg(10) 190 def = &backend.SSAValueDefinition{Instr: ishl, N: 0} 191 return def, extModeNone, func(t *testing.T) {} 192 }, 193 exp: operandNR(regalloc.VReg(10)), 194 }, 195 { 196 name: "ishl with const amount", 197 setup: ishlWithConstAmount, 198 exp: operandSR(regalloc.VReg(1234), 14, shiftOpLSL), 199 }, 200 { 201 name: "ishl with const amount with i32", 202 setup: func( 203 ctx *mockCompiler, builder ssa.Builder, m *machine, 204 ) (def *backend.SSAValueDefinition, mode extMode, verify func(t *testing.T)) { 205 blk := builder.CurrentBlock() 206 // (p1+p2) << amount 207 p1 := blk.AddParam(builder, ssa.TypeI32) 208 p2 := blk.AddParam(builder, ssa.TypeI32) 209 add := builder.AllocateInstruction() 210 add.AsIadd(p1, p2) 211 builder.InsertInstruction(add) 212 addResult := add.Return() 213 214 amount := builder.AllocateInstruction() 215 amount.AsIconst32(45) // should be taken modulo by 31. 216 builder.InsertInstruction(amount) 217 218 amountVal := amount.Return() 219 220 ishl := builder.AllocateInstruction() 221 ishl.AsIshl(addResult, amountVal) 222 builder.InsertInstruction(ishl) 223 224 ctx.definitions[p1] = &backend.SSAValueDefinition{BlkParamVReg: regalloc.VReg(1), BlockParamValue: p1} 225 ctx.definitions[p2] = &backend.SSAValueDefinition{BlkParamVReg: regalloc.VReg(2), BlockParamValue: p2} 226 ctx.definitions[addResult] = &backend.SSAValueDefinition{Instr: add, N: 0} 227 ctx.definitions[amountVal] = &backend.SSAValueDefinition{Instr: amount, N: 0} 228 229 ctx.vRegMap[addResult] = regalloc.VReg(1234) 230 ctx.vRegMap[ishl.Return()] = regalloc.VReg(10) 231 def = &backend.SSAValueDefinition{Instr: ishl, N: 0} 232 mode = extModeNone 233 verify = func(t *testing.T) { 234 require.True(t, ishl.Lowered()) 235 require.True(t, amount.Lowered()) 236 } 237 return 238 }, 239 exp: operandSR(regalloc.VReg(1234), 13, shiftOpLSL), 240 }, 241 { 242 name: "ishl with const amount with const shift target", 243 setup: func( 244 ctx *mockCompiler, builder ssa.Builder, m *machine, 245 ) (def *backend.SSAValueDefinition, mode extMode, verify func(t *testing.T)) { 246 const nextVReg = 100 247 ctx.vRegCounter = nextVReg - 1 248 249 target := builder.AllocateInstruction().AsIconst64(0xff).Insert(builder) 250 amount := builder.AllocateInstruction().AsIconst32(14).Insert(builder) 251 targetVal, amountVal := target.Return(), amount.Return() 252 253 ishl := builder.AllocateInstruction() 254 ishl.AsIshl(target.Return(), amountVal) 255 builder.InsertInstruction(ishl) 256 257 ctx.definitions[targetVal] = &backend.SSAValueDefinition{Instr: target, N: 0} 258 ctx.definitions[amountVal] = &backend.SSAValueDefinition{Instr: amount, N: 0} 259 ctx.vRegMap[targetVal] = regalloc.VReg(1234) 260 ctx.vRegMap[ishl.Return()] = regalloc.VReg(10) 261 def = &backend.SSAValueDefinition{Instr: ishl, N: 0} 262 mode = extModeNone 263 verify = func(t *testing.T) { 264 require.True(t, ishl.Lowered()) 265 require.True(t, amount.Lowered()) 266 } 267 return 268 }, 269 exp: operandSR(regalloc.VReg(100).SetRegType(regalloc.RegTypeInt), 14, shiftOpLSL), 270 instructions: []string{"orr x100?, xzr, #0xff"}, 271 }, 272 { 273 name: "ishl with const amount but group id is different", 274 setup: func(ctx *mockCompiler, builder ssa.Builder, m *machine) (def *backend.SSAValueDefinition, mode extMode, verify func(t *testing.T)) { 275 def, mode, _ = ishlWithConstAmount(ctx, builder, m) 276 ctx.currentGID = 1230 277 return 278 }, 279 exp: operandNR(regalloc.VReg(10)), 280 }, 281 { 282 name: "ishl with const amount but ref count is larger than 1", 283 setup: func(ctx *mockCompiler, builder ssa.Builder, m *machine) (def *backend.SSAValueDefinition, mode extMode, verify func(t *testing.T)) { 284 def, mode, _ = ishlWithConstAmount(ctx, builder, m) 285 def.RefCount = 10 286 return 287 }, 288 exp: operandNR(regalloc.VReg(10)), 289 }, 290 } { 291 t.Run(tc.name, func(t *testing.T) { 292 ctx, b, m := newSetupWithMockContext() 293 def, mode, verify := tc.setup(ctx, b, m) 294 actual := m.getOperand_SR_NR(def, mode) 295 require.Equal(t, tc.exp, actual) 296 if verify != nil { 297 verify(t) 298 } 299 require.Equal(t, strings.Join(tc.instructions, "\n"), formatEmittedInstructionsInCurrentBlock(m)) 300 }) 301 } 302 } 303 304 func TestMachine_getOperand_ER_SR_NR(t *testing.T) { 305 const nextVReg = 100 306 type testCase struct { 307 setup func(*mockCompiler, ssa.Builder, *machine) (def *backend.SSAValueDefinition, mode extMode, verify func(t *testing.T)) 308 exp operand 309 instructions []string 310 } 311 runner := func(tc testCase) { 312 ctx, b, m := newSetupWithMockContext() 313 ctx.vRegCounter = nextVReg - 1 314 def, mode, verify := tc.setup(ctx, b, m) 315 actual := m.getOperand_ER_SR_NR(def, mode) 316 require.Equal(t, tc.exp, actual) 317 verify(t) 318 require.Equal(t, strings.Join(tc.instructions, "\n"), formatEmittedInstructionsInCurrentBlock(m)) 319 } 320 321 t.Run("block param", func(t *testing.T) { 322 runner(testCase{ 323 setup: func(ctx *mockCompiler, builder ssa.Builder, m *machine) (def *backend.SSAValueDefinition, mode extMode, verify func(t *testing.T)) { 324 blk := builder.CurrentBlock() 325 v := blk.AddParam(builder, ssa.TypeI64) 326 def = &backend.SSAValueDefinition{BlkParamVReg: regToVReg(x4), BlockParamValue: v} 327 return def, extModeZeroExtend64, func(t *testing.T) {} 328 }, 329 exp: operandNR(regToVReg(x4)), 330 }) 331 }) 332 333 t.Run("mode none", func(t *testing.T) { 334 for _, c := range []struct { 335 from, to byte 336 signed bool 337 exp operand 338 }{ 339 {from: 8, to: 32, signed: true, exp: operandER(regalloc.VReg(10), extendOpSXTB, 32)}, 340 {from: 16, to: 32, signed: true, exp: operandER(regalloc.VReg(10), extendOpSXTH, 32)}, 341 {from: 8, to: 32, signed: false, exp: operandER(regalloc.VReg(10), extendOpUXTB, 32)}, 342 {from: 16, to: 32, signed: false, exp: operandER(regalloc.VReg(10), extendOpUXTH, 32)}, 343 {from: 8, to: 64, signed: true, exp: operandER(regalloc.VReg(10), extendOpSXTB, 64)}, 344 {from: 16, to: 64, signed: true, exp: operandER(regalloc.VReg(10), extendOpSXTH, 64)}, 345 {from: 32, to: 64, signed: true, exp: operandER(regalloc.VReg(10), extendOpSXTW, 64)}, 346 {from: 8, to: 64, signed: false, exp: operandER(regalloc.VReg(10), extendOpUXTB, 64)}, 347 {from: 16, to: 64, signed: false, exp: operandER(regalloc.VReg(10), extendOpUXTH, 64)}, 348 {from: 32, to: 64, signed: false, exp: operandER(regalloc.VReg(10), extendOpUXTW, 64)}, 349 } { 350 runner(testCase{ 351 setup: func(ctx *mockCompiler, builder ssa.Builder, m *machine) (def *backend.SSAValueDefinition, mode extMode, verify func(t *testing.T)) { 352 blk := builder.CurrentBlock() 353 v := blk.AddParam(builder, ssa.TypeI64) 354 ext := builder.AllocateInstruction() 355 if c.signed { 356 ext.AsSExtend(v, c.from, c.to) 357 } else { 358 ext.AsUExtend(v, c.from, c.to) 359 } 360 builder.InsertInstruction(ext) 361 extArg := ext.Arg() 362 ctx.vRegMap[ext.Arg()] = regalloc.VReg(10) 363 ctx.definitions[v] = &backend.SSAValueDefinition{BlkParamVReg: regalloc.VReg(10), BlockParamValue: extArg} 364 def = &backend.SSAValueDefinition{Instr: ext, N: 0} 365 return def, extModeNone, func(t *testing.T) { 366 require.True(t, ext.Lowered()) 367 } 368 }, 369 exp: c.exp, 370 }) 371 } 372 }) 373 374 t.Run("valid mode", func(t *testing.T) { 375 const argVReg, resultVReg = regalloc.VReg(10), regalloc.VReg(11) 376 for _, c := range []struct { 377 name string 378 from, to byte 379 signed bool 380 mode extMode 381 exp operand 382 lowered bool 383 extArgConst bool 384 instructions []string 385 }{ 386 { 387 name: "8->16->32: signed", 388 from: 8, to: 16, signed: true, mode: extModeSignExtend32, 389 exp: operandER(argVReg, extendOpSXTB, 32), 390 lowered: true, 391 }, 392 { 393 name: "8->16->32: unsigned", 394 from: 8, to: 16, signed: false, mode: extModeZeroExtend32, 395 exp: operandER(argVReg, extendOpUXTB, 32), 396 lowered: true, 397 }, 398 { 399 name: "8->32->64: signed", 400 from: 8, to: 32, signed: true, mode: extModeSignExtend64, 401 exp: operandER(argVReg, extendOpSXTB, 64), 402 lowered: true, 403 }, 404 { 405 name: "16->32->64: signed", 406 from: 16, to: 32, signed: true, mode: extModeSignExtend64, 407 exp: operandER(argVReg, extendOpSXTH, 64), 408 lowered: true, 409 }, 410 { 411 name: "8->32->64: unsigned", 412 from: 8, to: 32, signed: false, mode: extModeZeroExtend64, 413 exp: operandER(argVReg, extendOpUXTB, 64), 414 lowered: true, 415 }, 416 { 417 name: "16->32->64: unsigned", 418 from: 16, to: 32, signed: false, mode: extModeZeroExtend64, 419 exp: operandER(argVReg, extendOpUXTH, 64), 420 lowered: true, 421 }, 422 { 423 name: "8->16->64: signed", 424 from: 8, to: 16, signed: true, mode: extModeSignExtend64, 425 exp: operandER(argVReg, extendOpSXTB, 64), 426 lowered: true, 427 }, 428 { 429 name: "8->16->64: unsigned", 430 from: 8, to: 16, signed: false, mode: extModeZeroExtend64, 431 exp: operandER(argVReg, extendOpUXTB, 64), 432 lowered: true, 433 }, 434 { 435 name: "8(VReg)->64->64: unsigned", 436 from: 8, to: 64, signed: false, mode: extModeZeroExtend64, 437 exp: operandER(argVReg, extendOpUXTB, 64), 438 lowered: true, 439 }, 440 { 441 name: "8(VReg,Const)->64->64: unsigned", 442 from: 8, to: 64, signed: false, mode: extModeZeroExtend64, 443 exp: operandER(regalloc.VReg(nextVReg).SetRegType(regalloc.RegTypeInt), extendOpUXTB, 64), 444 lowered: true, 445 extArgConst: true, 446 instructions: []string{"movz w100?, #0xffff, lsl 0"}, 447 }, 448 { 449 name: "16(VReg)->64->64: unsigned", 450 from: 16, to: 64, signed: false, mode: extModeZeroExtend64, 451 exp: operandER(argVReg, extendOpUXTH, 64), 452 lowered: true, 453 }, 454 { 455 name: "16(VReg,Const)->64->64: unsigned", 456 from: 16, to: 64, signed: false, mode: extModeZeroExtend64, 457 exp: operandER(regalloc.VReg(nextVReg).SetRegType(regalloc.RegTypeInt), extendOpUXTH, 64), 458 lowered: true, 459 extArgConst: true, 460 instructions: []string{"movz w100?, #0xffff, lsl 0"}, 461 }, 462 { 463 name: "32(VReg)->64->64: unsigned", 464 from: 32, to: 64, signed: false, mode: extModeZeroExtend64, 465 exp: operandER(argVReg, extendOpUXTW, 64), 466 lowered: true, 467 }, 468 { 469 name: "32(VReg,Const)->64->64: unsigned", 470 from: 32, to: 64, signed: false, mode: extModeZeroExtend64, 471 exp: operandER(regalloc.VReg(nextVReg).SetRegType(regalloc.RegTypeInt), extendOpUXTW, 64), 472 lowered: true, 473 extArgConst: true, 474 instructions: []string{"movz w100?, #0xffff, lsl 0"}, 475 }, 476 { 477 name: "8(VReg)->64->64: signed", 478 from: 8, to: 64, signed: true, mode: extModeZeroExtend64, 479 exp: operandER(argVReg, extendOpSXTB, 64), 480 lowered: true, 481 }, 482 { 483 name: "8(VReg,Const)->64->64: signed", 484 from: 8, to: 64, signed: true, mode: extModeZeroExtend64, 485 exp: operandER(regalloc.VReg(nextVReg).SetRegType(regalloc.RegTypeInt), extendOpSXTB, 64), 486 lowered: true, 487 extArgConst: true, 488 instructions: []string{"movz w100?, #0xffff, lsl 0"}, 489 }, 490 { 491 name: "16(VReg)->64->64: signed", 492 from: 16, to: 64, signed: true, mode: extModeZeroExtend64, 493 exp: operandER(argVReg, extendOpSXTH, 64), 494 lowered: true, 495 }, 496 { 497 name: "16(VReg,Const)->64->64: signed", 498 from: 16, to: 64, signed: true, mode: extModeZeroExtend64, 499 exp: operandER(regalloc.VReg(nextVReg).SetRegType(regalloc.RegTypeInt), extendOpSXTH, 64), 500 lowered: true, 501 extArgConst: true, 502 instructions: []string{"movz w100?, #0xffff, lsl 0"}, 503 }, 504 { 505 name: "32(VReg)->64->64: signed", 506 from: 32, to: 64, signed: true, mode: extModeZeroExtend64, 507 exp: operandER(argVReg, extendOpSXTW, 64), 508 lowered: true, 509 }, 510 { 511 name: "32(VReg,Const)->64->64: signed", 512 from: 32, to: 64, signed: true, mode: extModeZeroExtend64, 513 exp: operandER(regalloc.VReg(nextVReg).SetRegType(regalloc.RegTypeInt), extendOpSXTW, 64), 514 lowered: true, 515 extArgConst: true, 516 instructions: []string{"movz w100?, #0xffff, lsl 0"}, 517 }, 518 519 // Not lowered cases. 520 { 521 name: "8-signed->16-zero->64", 522 from: 8, to: 16, signed: true, mode: extModeZeroExtend64, 523 exp: operandER(resultVReg, extendOpUXTH, 64), 524 lowered: false, 525 }, 526 { 527 name: "8-signed->32-zero->64", 528 from: 8, to: 32, signed: true, mode: extModeZeroExtend64, 529 exp: operandER(resultVReg, extendOpUXTW, 64), 530 lowered: false, 531 }, 532 { 533 name: "16-signed->32-zero->64", 534 from: 16, to: 32, signed: true, mode: extModeZeroExtend64, 535 exp: operandER(resultVReg, extendOpUXTW, 64), 536 lowered: false, 537 }, 538 { 539 name: "8-zero->16-signed->64", 540 from: 8, to: 16, signed: false, mode: extModeSignExtend64, 541 exp: operandER(resultVReg, extendOpSXTH, 64), 542 lowered: false, 543 }, 544 { 545 name: "8-zero->32-signed->64", 546 from: 8, to: 32, signed: false, mode: extModeSignExtend64, 547 exp: operandER(resultVReg, extendOpSXTW, 64), 548 lowered: false, 549 }, 550 { 551 name: "16-zero->32-signed->64", 552 from: 16, to: 32, signed: false, mode: extModeSignExtend64, 553 exp: operandER(resultVReg, extendOpSXTW, 64), 554 lowered: false, 555 }, 556 } { 557 t.Run(c.name, func(t *testing.T) { 558 runner(testCase{ 559 setup: func(ctx *mockCompiler, builder ssa.Builder, m *machine) (def *backend.SSAValueDefinition, mode extMode, verify func(t *testing.T)) { 560 blk := builder.CurrentBlock() 561 v := blk.AddParam(builder, ssa.TypeI64) 562 ext := builder.AllocateInstruction() 563 if c.signed { 564 ext.AsSExtend(v, c.from, c.to) 565 } else { 566 ext.AsUExtend(v, c.from, c.to) 567 } 568 builder.InsertInstruction(ext) 569 extArg := ext.Arg() 570 ctx.vRegMap[extArg] = argVReg 571 ctx.vRegMap[ext.Return()] = resultVReg 572 if c.extArgConst { 573 iconst := builder.AllocateInstruction().AsIconst32(0xffff).Insert(builder) 574 m.compiler.(*mockCompiler).definitions[extArg] = &backend.SSAValueDefinition{ 575 Instr: iconst, 576 } 577 } else { 578 m.compiler.(*mockCompiler).definitions[extArg] = &backend.SSAValueDefinition{ 579 BlkParamVReg: argVReg, 580 BlockParamValue: extArg, 581 } 582 } 583 def = &backend.SSAValueDefinition{Instr: ext, N: 0} 584 return def, c.mode, func(t *testing.T) { 585 require.Equal(t, c.lowered, ext.Lowered()) 586 } 587 }, 588 exp: c.exp, 589 instructions: c.instructions, 590 }) 591 }) 592 } 593 }) 594 } 595 596 func TestMachine_getOperand_Imm12_ER_SR_NR(t *testing.T) { 597 for _, tc := range []struct { 598 name string 599 setup func(*mockCompiler, ssa.Builder, *machine) (def *backend.SSAValueDefinition, mode extMode) 600 exp operand 601 instructions []string 602 }{ 603 { 604 name: "block param", 605 setup: func(ctx *mockCompiler, builder ssa.Builder, m *machine) (def *backend.SSAValueDefinition, mode extMode) { 606 blk := builder.CurrentBlock() 607 v := blk.AddParam(builder, ssa.TypeI64) 608 def = &backend.SSAValueDefinition{BlkParamVReg: regToVReg(x4), BlockParamValue: v} 609 return def, extModeZeroExtend64 610 }, 611 exp: operandNR(regToVReg(x4)), 612 }, 613 { 614 name: "const imm 12 no shift", 615 setup: func(ctx *mockCompiler, builder ssa.Builder, m *machine) (def *backend.SSAValueDefinition, mode extMode) { 616 cinst := builder.AllocateInstruction() 617 cinst.AsIconst32(0xfff) 618 builder.InsertInstruction(cinst) 619 def = &backend.SSAValueDefinition{Instr: cinst, N: 0} 620 ctx.currentGID = 0xff // const can be merged anytime, regardless of the group id. 621 return 622 }, 623 exp: operandImm12(0xfff, 0), 624 }, 625 { 626 name: "const imm 12 with shift bit", 627 setup: func(ctx *mockCompiler, builder ssa.Builder, m *machine) (def *backend.SSAValueDefinition, mode extMode) { 628 cinst := builder.AllocateInstruction() 629 cinst.AsIconst32(0xabc_000) 630 builder.InsertInstruction(cinst) 631 def = &backend.SSAValueDefinition{Instr: cinst, N: 0} 632 ctx.currentGID = 0xff // const can be merged anytime, regardless of the group id. 633 return 634 }, 635 exp: operandImm12(0xabc, 1), 636 }, 637 } { 638 t.Run(tc.name, func(t *testing.T) { 639 ctx, b, m := newSetupWithMockContext() 640 def, mode := tc.setup(ctx, b, m) 641 actual := m.getOperand_Imm12_ER_SR_NR(def, mode) 642 require.Equal(t, tc.exp, actual) 643 require.Equal(t, strings.Join(tc.instructions, "\n"), formatEmittedInstructionsInCurrentBlock(m)) 644 }) 645 } 646 } 647 648 func TestMachine_getOperand_MaybeNegatedImm12_ER_SR_NR(t *testing.T) { 649 for _, tc := range []struct { 650 name string 651 setup func(*mockCompiler, ssa.Builder, *machine) (def *backend.SSAValueDefinition, mode extMode) 652 exp operand 653 expNegated bool 654 instructions []string 655 }{ 656 { 657 name: "block param", 658 setup: func(ctx *mockCompiler, builder ssa.Builder, m *machine) (def *backend.SSAValueDefinition, mode extMode) { 659 blk := builder.CurrentBlock() 660 v := blk.AddParam(builder, ssa.TypeI64) 661 def = &backend.SSAValueDefinition{BlkParamVReg: regToVReg(x4), BlockParamValue: v} 662 return def, extModeZeroExtend64 663 }, 664 exp: operandNR(regToVReg(x4)), 665 }, 666 { 667 name: "const imm 12 no shift", 668 setup: func(ctx *mockCompiler, builder ssa.Builder, m *machine) (def *backend.SSAValueDefinition, mode extMode) { 669 cinst := builder.AllocateInstruction() 670 cinst.AsIconst32(0xfff) 671 builder.InsertInstruction(cinst) 672 def = &backend.SSAValueDefinition{Instr: cinst, N: 0} 673 ctx.currentGID = 0xff // const can be merged anytime, regardless of the group id. 674 return 675 }, 676 exp: operandImm12(0xfff, 0), 677 }, 678 { 679 name: "const negative imm 12 no shift", 680 setup: func(ctx *mockCompiler, builder ssa.Builder, m *machine) (def *backend.SSAValueDefinition, mode extMode) { 681 c := int32(-1) 682 cinst := builder.AllocateInstruction() 683 cinst.AsIconst32(uint32(c)) 684 builder.InsertInstruction(cinst) 685 def = &backend.SSAValueDefinition{Instr: cinst, N: 0} 686 ctx.currentGID = 0xff // const can be merged anytime, regardless of the group id. 687 return 688 }, 689 exp: operandImm12(1, 0), 690 expNegated: true, 691 }, 692 { 693 name: "const imm 12 with shift bit", 694 setup: func(ctx *mockCompiler, builder ssa.Builder, m *machine) (def *backend.SSAValueDefinition, mode extMode) { 695 cinst := builder.AllocateInstruction() 696 cinst.AsIconst32(0xabc_000) 697 builder.InsertInstruction(cinst) 698 def = &backend.SSAValueDefinition{Instr: cinst, N: 0} 699 ctx.currentGID = 0xff // const can be merged anytime, regardless of the group id. 700 return 701 }, 702 exp: operandImm12(0xabc, 1), 703 }, 704 { 705 name: "const negated imm 12 with shift bit", 706 setup: func(ctx *mockCompiler, builder ssa.Builder, m *machine) (def *backend.SSAValueDefinition, mode extMode) { 707 c := int32(-0xabc_000) 708 cinst := builder.AllocateInstruction() 709 cinst.AsIconst32(uint32(c)) 710 builder.InsertInstruction(cinst) 711 def = &backend.SSAValueDefinition{Instr: cinst, N: 0} 712 ctx.currentGID = 0xff // const can be merged anytime, regardless of the group id. 713 return 714 }, 715 exp: operandImm12(0xabc, 1), 716 expNegated: true, 717 }, 718 } { 719 t.Run(tc.name, func(t *testing.T) { 720 ctx, b, m := newSetupWithMockContext() 721 def, mode := tc.setup(ctx, b, m) 722 actual, negated := m.getOperand_MaybeNegatedImm12_ER_SR_NR(def, mode) 723 require.Equal(t, tc.exp, actual) 724 require.Equal(t, tc.expNegated, negated) 725 require.Equal(t, strings.Join(tc.instructions, "\n"), formatEmittedInstructionsInCurrentBlock(m)) 726 }) 727 } 728 }