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

     1  package arm64
     2  
     3  import (
     4  	"fmt"
     5  	"math"
     6  	"strings"
     7  	"testing"
     8  
     9  	"github.com/tetratelabs/wazero/internal/engine/wazevo/backend"
    10  	"github.com/tetratelabs/wazero/internal/engine/wazevo/backend/regalloc"
    11  	"github.com/tetratelabs/wazero/internal/engine/wazevo/ssa"
    12  	"github.com/tetratelabs/wazero/internal/engine/wazevo/wazevoapi"
    13  	"github.com/tetratelabs/wazero/internal/testing/require"
    14  )
    15  
    16  func TestAddressMode_format(t *testing.T) {
    17  	t.Run("addressModeKindRegScaledExtended", func(t *testing.T) {
    18  		require.Equal(t,
    19  			"[x1, w0, UXTW #0x1]",
    20  			addressMode{
    21  				kind:  addressModeKindRegScaledExtended,
    22  				rn:    regalloc.FromRealReg(x1, regalloc.RegTypeInt),
    23  				rm:    regalloc.FromRealReg(x0, regalloc.RegTypeInt),
    24  				extOp: extendOpUXTW,
    25  				imm:   0,
    26  			}.format(16),
    27  		)
    28  		require.Equal(t,
    29  			"[x1, w0, SXTW #0x1]",
    30  			addressMode{
    31  				kind:  addressModeKindRegScaledExtended,
    32  				rn:    regalloc.FromRealReg(x1, regalloc.RegTypeInt),
    33  				rm:    regalloc.FromRealReg(x0, regalloc.RegTypeInt),
    34  				extOp: extendOpSXTW,
    35  				imm:   0,
    36  			}.format(16),
    37  		)
    38  		require.Equal(t,
    39  			"[x1, w0, UXTW #0x2]",
    40  			addressMode{
    41  				kind:  addressModeKindRegScaledExtended,
    42  				rn:    regalloc.FromRealReg(x1, regalloc.RegTypeInt),
    43  				rm:    regalloc.FromRealReg(x0, regalloc.RegTypeInt),
    44  				extOp: extendOpUXTW,
    45  				imm:   0,
    46  			}.format(32),
    47  		)
    48  		require.Equal(t,
    49  			"[x1, w0, SXTW #0x2]",
    50  			addressMode{
    51  				kind:  addressModeKindRegScaledExtended,
    52  				rn:    regalloc.FromRealReg(x1, regalloc.RegTypeInt),
    53  				rm:    regalloc.FromRealReg(x0, regalloc.RegTypeInt),
    54  				extOp: extendOpSXTW,
    55  				imm:   0,
    56  			}.format(32),
    57  		)
    58  		require.Equal(t,
    59  			"[x1, w0, UXTW #0x3]",
    60  			addressMode{
    61  				kind:  addressModeKindRegScaledExtended,
    62  				rn:    regalloc.FromRealReg(x1, regalloc.RegTypeInt),
    63  				rm:    regalloc.FromRealReg(x0, regalloc.RegTypeInt),
    64  				extOp: extendOpUXTW,
    65  				imm:   0,
    66  			}.format(64),
    67  		)
    68  		require.Equal(t,
    69  			"[x1, w0, SXTW #0x3]",
    70  			addressMode{
    71  				kind:  addressModeKindRegScaledExtended,
    72  				rn:    regalloc.FromRealReg(x1, regalloc.RegTypeInt),
    73  				rm:    regalloc.FromRealReg(x0, regalloc.RegTypeInt),
    74  				extOp: extendOpSXTW,
    75  				imm:   0,
    76  			}.format(64),
    77  		)
    78  	})
    79  	t.Run("addressModeKindRegScaled", func(t *testing.T) {
    80  		require.Equal(t,
    81  			"[x1, w0, lsl #0x1]",
    82  			addressMode{
    83  				kind:  addressModeKindRegScaled,
    84  				rn:    regalloc.FromRealReg(x1, regalloc.RegTypeInt),
    85  				rm:    regalloc.FromRealReg(x0, regalloc.RegTypeInt),
    86  				extOp: extendOpUXTW,
    87  				imm:   0,
    88  			}.format(16),
    89  		)
    90  		require.Equal(t,
    91  			"[x1, w0, lsl #0x1]",
    92  			addressMode{
    93  				kind:  addressModeKindRegScaled,
    94  				rn:    regalloc.FromRealReg(x1, regalloc.RegTypeInt),
    95  				rm:    regalloc.FromRealReg(x0, regalloc.RegTypeInt),
    96  				extOp: extendOpSXTW,
    97  				imm:   0,
    98  			}.format(16),
    99  		)
   100  		require.Equal(t,
   101  			"[x1, w0, lsl #0x2]",
   102  			addressMode{
   103  				kind:  addressModeKindRegScaled,
   104  				rn:    regalloc.FromRealReg(x1, regalloc.RegTypeInt),
   105  				rm:    regalloc.FromRealReg(x0, regalloc.RegTypeInt),
   106  				extOp: extendOpUXTW,
   107  				imm:   0,
   108  			}.format(32),
   109  		)
   110  		require.Equal(t,
   111  			"[x1, w0, lsl #0x2]",
   112  			addressMode{
   113  				kind:  addressModeKindRegScaled,
   114  				rn:    regalloc.FromRealReg(x1, regalloc.RegTypeInt),
   115  				rm:    regalloc.FromRealReg(x0, regalloc.RegTypeInt),
   116  				extOp: extendOpSXTW,
   117  				imm:   0,
   118  			}.format(32),
   119  		)
   120  		require.Equal(t,
   121  			"[x1, w0, lsl #0x3]",
   122  			addressMode{
   123  				kind:  addressModeKindRegScaled,
   124  				rn:    regalloc.FromRealReg(x1, regalloc.RegTypeInt),
   125  				rm:    regalloc.FromRealReg(x0, regalloc.RegTypeInt),
   126  				extOp: extendOpUXTW,
   127  				imm:   0,
   128  			}.format(64),
   129  		)
   130  		require.Equal(t,
   131  			"[x1, w0, lsl #0x3]",
   132  			addressMode{
   133  				kind:  addressModeKindRegScaled,
   134  				rn:    regalloc.FromRealReg(x1, regalloc.RegTypeInt),
   135  				rm:    regalloc.FromRealReg(x0, regalloc.RegTypeInt),
   136  				extOp: extendOpSXTW,
   137  				imm:   0,
   138  			}.format(64),
   139  		)
   140  	})
   141  	t.Run("addressModeKindRegExtended", func(t *testing.T) {
   142  		require.Equal(t,
   143  			"[x1, w0, UXTW]",
   144  			addressMode{
   145  				kind:  addressModeKindRegExtended,
   146  				rn:    regalloc.FromRealReg(x1, regalloc.RegTypeInt),
   147  				rm:    regalloc.FromRealReg(x0, regalloc.RegTypeInt),
   148  				extOp: extendOpUXTW,
   149  				imm:   0,
   150  			}.format(16),
   151  		)
   152  		require.Equal(t,
   153  			"[x1, w0, SXTW]",
   154  			addressMode{
   155  				kind:  addressModeKindRegExtended,
   156  				rn:    regalloc.FromRealReg(x1, regalloc.RegTypeInt),
   157  				rm:    regalloc.FromRealReg(x0, regalloc.RegTypeInt),
   158  				extOp: extendOpSXTW,
   159  				imm:   0,
   160  			}.format(64),
   161  		)
   162  	})
   163  	t.Run("addressModeKindRegReg", func(t *testing.T) {
   164  		require.Equal(t,
   165  			"[x1, w29]",
   166  			addressMode{
   167  				kind:  addressModeKindRegReg,
   168  				rn:    regalloc.FromRealReg(x1, regalloc.RegTypeInt),
   169  				rm:    regalloc.FromRealReg(x29, regalloc.RegTypeInt),
   170  				extOp: extendOpUXTW, // To indicate that the index reg is 32-bit.
   171  				imm:   0,
   172  			}.format(64),
   173  		)
   174  		require.Equal(t,
   175  			"[x1, x29]",
   176  			addressMode{
   177  				kind:  addressModeKindRegReg,
   178  				rn:    regalloc.FromRealReg(x1, regalloc.RegTypeInt),
   179  				rm:    regalloc.FromRealReg(x29, regalloc.RegTypeInt),
   180  				extOp: extendOpUXTX, // To indicate that the index reg is 64-bit.
   181  				imm:   0,
   182  			}.format(64),
   183  		)
   184  	})
   185  	t.Run("addressModeKindRegSignedImm9", func(t *testing.T) {
   186  		require.Equal(t,
   187  			"[x1]",
   188  			addressMode{
   189  				kind: addressModeKindRegSignedImm9,
   190  				rn:   regalloc.FromRealReg(x1, regalloc.RegTypeInt),
   191  				imm:  0,
   192  			}.format(64),
   193  		)
   194  		require.Equal(t,
   195  			"[x1, #-0x100]",
   196  			addressMode{
   197  				kind: addressModeKindRegSignedImm9,
   198  				rn:   regalloc.FromRealReg(x1, regalloc.RegTypeInt),
   199  				imm:  math.MinInt8 << 1,
   200  			}.format(64),
   201  		)
   202  		require.Equal(t,
   203  			"[x1, #0xff]",
   204  			addressMode{
   205  				kind: addressModeKindRegSignedImm9,
   206  				rn:   regalloc.FromRealReg(x1, regalloc.RegTypeInt),
   207  				imm:  (math.MaxInt8 << 1) + 1,
   208  			}.format(64),
   209  		)
   210  	})
   211  
   212  	t.Run("addressModeKindRegUnsignedImm12", func(t *testing.T) {
   213  		require.Equal(t,
   214  			"[x1]",
   215  			addressMode{
   216  				kind: addressModeKindRegUnsignedImm12,
   217  				rn:   regalloc.FromRealReg(x1, regalloc.RegTypeInt),
   218  				imm:  0,
   219  			}.format(64),
   220  		)
   221  		require.Equal(t,
   222  			"[x1, #0xfff]",
   223  			addressMode{
   224  				kind: addressModeKindRegUnsignedImm12,
   225  				rn:   regalloc.FromRealReg(x1, regalloc.RegTypeInt),
   226  				imm:  4095,
   227  			}.format(64),
   228  		)
   229  	})
   230  }
   231  
   232  func Test_offsetFitsInAddressModeKindRegUnsignedImm12(t *testing.T) {
   233  	for _, tc := range []struct {
   234  		dstSizeInBits byte
   235  		offset        int64
   236  		exp           bool
   237  	}{
   238  		{dstSizeInBits: 8, offset: -1, exp: false},
   239  		{dstSizeInBits: 8, offset: 0, exp: false},
   240  		{dstSizeInBits: 8, offset: 1, exp: true},
   241  		{dstSizeInBits: 8, offset: 2, exp: true},
   242  		{dstSizeInBits: 8, offset: 3, exp: true},
   243  		{dstSizeInBits: 8, offset: 4095, exp: true},
   244  		{dstSizeInBits: 8, offset: 4096, exp: false},
   245  		{dstSizeInBits: 16, offset: -2, exp: false},
   246  		{dstSizeInBits: 16, offset: -1, exp: false},
   247  		{dstSizeInBits: 16, offset: 0, exp: false},
   248  		{dstSizeInBits: 16, offset: 1, exp: false},
   249  		{dstSizeInBits: 16, offset: 2, exp: true},
   250  		{dstSizeInBits: 16, offset: 3, exp: false},
   251  		{dstSizeInBits: 16, offset: 4095, exp: false},
   252  		{dstSizeInBits: 16, offset: 4096, exp: true},
   253  		{dstSizeInBits: 16, offset: 4095 * 2, exp: true},
   254  		{dstSizeInBits: 16, offset: 4095*2 + 1, exp: false},
   255  		{dstSizeInBits: 32, offset: -4, exp: false},
   256  		{dstSizeInBits: 32, offset: -1, exp: false},
   257  		{dstSizeInBits: 32, offset: 0, exp: false},
   258  		{dstSizeInBits: 32, offset: 1, exp: false},
   259  		{dstSizeInBits: 32, offset: 2, exp: false},
   260  		{dstSizeInBits: 32, offset: 3, exp: false},
   261  		{dstSizeInBits: 32, offset: 4, exp: true},
   262  		{dstSizeInBits: 32, offset: 4095, exp: false},
   263  		{dstSizeInBits: 32, offset: 4096, exp: true},
   264  		{dstSizeInBits: 32, offset: 4095 * 4, exp: true},
   265  		{dstSizeInBits: 32, offset: 4095*4 + 1, exp: false},
   266  		{dstSizeInBits: 64, offset: -8, exp: false},
   267  		{dstSizeInBits: 64, offset: -1, exp: false},
   268  		{dstSizeInBits: 64, offset: 0, exp: false},
   269  		{dstSizeInBits: 64, offset: 1, exp: false},
   270  		{dstSizeInBits: 64, offset: 2, exp: false},
   271  		{dstSizeInBits: 64, offset: 3, exp: false},
   272  		{dstSizeInBits: 64, offset: 4, exp: false},
   273  		{dstSizeInBits: 64, offset: 8, exp: true},
   274  		{dstSizeInBits: 64, offset: 4095, exp: false},
   275  		{dstSizeInBits: 64, offset: 4096, exp: true},
   276  		{dstSizeInBits: 64, offset: 4095 * 8, exp: true},
   277  		{dstSizeInBits: 64, offset: 4095*8 + 1, exp: false},
   278  	} {
   279  		require.Equal(
   280  			t, tc.exp,
   281  			offsetFitsInAddressModeKindRegUnsignedImm12(tc.dstSizeInBits, tc.offset),
   282  			fmt.Sprintf("dstSizeInBits=%d, offset=%d", tc.dstSizeInBits, tc.offset),
   283  		)
   284  	}
   285  }
   286  
   287  func Test_offsetFitsInAddressModeKindRegSignedImm9(t *testing.T) {
   288  	require.Equal(t, true, offsetFitsInAddressModeKindRegSignedImm9(0))
   289  	require.Equal(t, false, offsetFitsInAddressModeKindRegSignedImm9(-257))
   290  	require.Equal(t, true, offsetFitsInAddressModeKindRegSignedImm9(-256))
   291  	require.Equal(t, true, offsetFitsInAddressModeKindRegSignedImm9(255))
   292  	require.Equal(t, false, offsetFitsInAddressModeKindRegSignedImm9(256))
   293  }
   294  
   295  func TestMachine_collectAddends(t *testing.T) {
   296  	v1000, v2000 := regalloc.VReg(1000).SetRegType(regalloc.RegTypeInt), regalloc.VReg(2000).SetRegType(regalloc.RegTypeInt)
   297  	addParam := func(ctx *mockCompiler, b ssa.Builder, typ ssa.Type) ssa.Value {
   298  		p := b.CurrentBlock().AddParam(b, typ)
   299  		ctx.definitions[p] = &backend.SSAValueDefinition{BlockParamValue: p, BlkParamVReg: v1000}
   300  		return p
   301  	}
   302  	insertI32Const := func(m *mockCompiler, b ssa.Builder, v uint32) *ssa.Instruction {
   303  		inst := b.AllocateInstruction()
   304  		inst.AsIconst32(v)
   305  		b.InsertInstruction(inst)
   306  		m.definitions[inst.Return()] = &backend.SSAValueDefinition{Instr: inst}
   307  		return inst
   308  	}
   309  	insertI64Const := func(m *mockCompiler, b ssa.Builder, v uint64) *ssa.Instruction {
   310  		inst := b.AllocateInstruction()
   311  		inst.AsIconst64(v)
   312  		b.InsertInstruction(inst)
   313  		m.definitions[inst.Return()] = &backend.SSAValueDefinition{Instr: inst}
   314  		return inst
   315  	}
   316  	insertIadd := func(m *mockCompiler, b ssa.Builder, lhs, rhs ssa.Value) *ssa.Instruction {
   317  		inst := b.AllocateInstruction()
   318  		inst.AsIadd(lhs, rhs)
   319  		b.InsertInstruction(inst)
   320  		m.definitions[inst.Return()] = &backend.SSAValueDefinition{Instr: inst}
   321  		return inst
   322  	}
   323  	insertExt := func(m *mockCompiler, b ssa.Builder, v ssa.Value, from, to byte, signed bool) *ssa.Instruction {
   324  		inst := b.AllocateInstruction()
   325  		if signed {
   326  			inst.AsSExtend(v, from, to)
   327  		} else {
   328  			inst.AsUExtend(v, from, to)
   329  		}
   330  		b.InsertInstruction(inst)
   331  		m.definitions[inst.Return()] = &backend.SSAValueDefinition{Instr: inst}
   332  		return inst
   333  	}
   334  
   335  	for _, tc := range []struct {
   336  		name   string
   337  		setup  func(*mockCompiler, ssa.Builder, *machine) (ptr ssa.Value, verify func(t *testing.T))
   338  		exp32s []addend32
   339  		exp64s []regalloc.VReg
   340  		offset int64
   341  	}{
   342  		{
   343  			name: "non merged",
   344  			setup: func(ctx *mockCompiler, b ssa.Builder, m *machine) (ptr ssa.Value, verify func(t *testing.T)) {
   345  				ptr = addParam(ctx, b, ssa.TypeI64)
   346  				return ptr, func(t *testing.T) {}
   347  			},
   348  			exp64s: []regalloc.VReg{v1000},
   349  		},
   350  		{
   351  			name: "i32 constant folded",
   352  			setup: func(ctx *mockCompiler, b ssa.Builder, m *machine) (ptr ssa.Value, verify func(t *testing.T)) {
   353  				minus1 := int32(-1)
   354  				c1, c2, c3, c4 := insertI32Const(ctx, b, 1), insertI32Const(ctx, b, 2), insertI32Const(ctx, b, 3), insertI32Const(ctx, b, uint32(minus1))
   355  				iadd1, iadd2 := insertIadd(ctx, b, c1.Return(), c2.Return()), insertIadd(ctx, b, c3.Return(), c4.Return())
   356  				iadd3 := insertIadd(ctx, b, iadd1.Return(), iadd2.Return())
   357  				return iadd3.Return(), func(t *testing.T) {
   358  					for _, instr := range []*ssa.Instruction{iadd1, iadd2, iadd3} {
   359  						require.True(t, instr.Lowered())
   360  					}
   361  				}
   362  			},
   363  			offset: 1 + 2 + 3 - 1,
   364  		},
   365  		{
   366  			name: "i64 constant folded",
   367  			setup: func(ctx *mockCompiler, b ssa.Builder, m *machine) (ptr ssa.Value, verify func(t *testing.T)) {
   368  				minus1 := int32(-1)
   369  				c1, c2, c3, c4 := insertI64Const(ctx, b, 1), insertI64Const(ctx, b, 2), insertI64Const(ctx, b, 3), insertI64Const(ctx, b, uint64(minus1))
   370  				iadd1, iadd2 := insertIadd(ctx, b, c1.Return(), c2.Return()), insertIadd(ctx, b, c3.Return(), c4.Return())
   371  				iadd3 := insertIadd(ctx, b, iadd1.Return(), iadd2.Return())
   372  				return iadd3.Return(), func(t *testing.T) {
   373  					for _, instr := range []*ssa.Instruction{iadd1, iadd2, iadd3} {
   374  						require.True(t, instr.Lowered())
   375  					}
   376  				}
   377  			},
   378  			offset: 1 + 2 + 3 - 1,
   379  		},
   380  		{
   381  			name: "constant folded with one 32 value",
   382  			setup: func(ctx *mockCompiler, b ssa.Builder, m *machine) (ptr ssa.Value, verify func(t *testing.T)) {
   383  				param := addParam(ctx, b, ssa.TypeI32)
   384  				minus1 := int32(-1)
   385  				c1, c2, c3, c4 := insertI32Const(ctx, b, 1), insertI32Const(ctx, b, 2), insertI32Const(ctx, b, 3), insertI32Const(ctx, b, uint32(minus1))
   386  				iadd1, iadd2 := insertIadd(ctx, b, c1.Return(), c2.Return()), insertIadd(ctx, b, c3.Return(), c4.Return())
   387  				iadd3 := insertIadd(ctx, b, iadd1.Return(), iadd2.Return())
   388  				iadd4 := insertIadd(ctx, b, param, iadd3.Return())
   389  
   390  				return iadd4.Return(), func(t *testing.T) {
   391  					for _, instr := range []*ssa.Instruction{iadd1, iadd2, iadd3, iadd4} {
   392  						require.True(t, instr.Lowered())
   393  					}
   394  					// Param must be zero-extended.
   395  					require.Equal(t, "uxtw x1?, w1000?", formatEmittedInstructionsInCurrentBlock(m))
   396  				}
   397  			},
   398  			exp64s: []regalloc.VReg{regalloc.VReg(1).SetRegType(regalloc.RegTypeInt)},
   399  			offset: 1 + 2 + 3 - 1,
   400  		},
   401  		{
   402  			name: "one 64 value + sign-extended (32->64) instr",
   403  			setup: func(ctx *mockCompiler, b ssa.Builder, m *machine) (ptr ssa.Value, verify func(t *testing.T)) {
   404  				param := addParam(ctx, b, ssa.TypeI64)
   405  				c1, c2 := insertI32Const(ctx, b, 1), insertI32Const(ctx, b, 2)
   406  				iadd1 := insertIadd(ctx, b, c1.Return(), c2.Return())
   407  				ext := insertExt(ctx, b, iadd1.Return(), 32, 64, true)
   408  				ctx.vRegMap[ext.Arg()] = v2000
   409  				iadd4 := insertIadd(ctx, b, param, ext.Return())
   410  				return iadd4.Return(), func(t *testing.T) {
   411  					for _, instr := range []*ssa.Instruction{ext, iadd4} {
   412  						require.True(t, instr.Lowered())
   413  					}
   414  				}
   415  			},
   416  			exp64s: []regalloc.VReg{v1000 /* == param */},
   417  			exp32s: []addend32{{v2000, extendOpSXTW} /* sign-extended iadd1 */},
   418  		},
   419  		{
   420  			name: "one 64 value + sign-extended (32->64) const",
   421  			setup: func(ctx *mockCompiler, b ssa.Builder, m *machine) (ptr ssa.Value, verify func(t *testing.T)) {
   422  				param := addParam(ctx, b, ssa.TypeI64)
   423  				minus1 := int32(-1)
   424  				c1 := insertI32Const(ctx, b, uint32(minus1))
   425  				ext := insertExt(ctx, b, c1.Return(), 32, 64, true)
   426  				ctx.vRegMap[ext.Arg()] = v2000
   427  				iadd4 := insertIadd(ctx, b, param, ext.Return())
   428  				return iadd4.Return(), func(t *testing.T) {
   429  					for _, instr := range []*ssa.Instruction{ext, iadd4} {
   430  						require.True(t, instr.Lowered())
   431  					}
   432  				}
   433  			},
   434  			exp64s: []regalloc.VReg{v1000 /* == param */},
   435  			offset: -1,
   436  		},
   437  		{
   438  			name: "one 64 value + zero-extended (32->64) const",
   439  			setup: func(ctx *mockCompiler, b ssa.Builder, m *machine) (ptr ssa.Value, verify func(t *testing.T)) {
   440  				param := addParam(ctx, b, ssa.TypeI64)
   441  				minus1 := int32(-1)
   442  				c1 := insertI32Const(ctx, b, uint32(minus1))
   443  				ext := insertExt(ctx, b, c1.Return(), 32, 64, false)
   444  				ctx.vRegMap[ext.Arg()] = v2000
   445  				iadd4 := insertIadd(ctx, b, param, ext.Return())
   446  				return iadd4.Return(), func(t *testing.T) {
   447  					for _, instr := range []*ssa.Instruction{ext, iadd4} {
   448  						require.True(t, instr.Lowered())
   449  					}
   450  				}
   451  			},
   452  			exp64s: []regalloc.VReg{v1000 /* == param */},
   453  			offset: math.MaxUint32, // zero-extended -1
   454  		},
   455  	} {
   456  		tc := tc
   457  		t.Run(tc.name, func(t *testing.T) {
   458  			ctx, b, m := newSetupWithMockContext()
   459  			ptr, verify := tc.setup(ctx, b, m)
   460  			actual32sQ, actual64sQ, actualOffset := m.collectAddends(ptr)
   461  			require.Equal(t, tc.exp32s, actual32sQ.Data)
   462  			require.Equal(t, tc.exp64s, actual64sQ.Data)
   463  			require.Equal(t, tc.offset, actualOffset)
   464  			verify(t)
   465  		})
   466  	}
   467  }
   468  
   469  func TestMachine_addConstToReg64(t *testing.T) {
   470  	const nextVRegID = 100
   471  	t.Run("positive imm12", func(t *testing.T) {
   472  		c := int64(0xaaa)
   473  		ctx, _, m := newSetupWithMockContext()
   474  		ctx.vRegCounter = nextVRegID - 1
   475  		m.addConstToReg64(regalloc.FromRealReg(x15, regalloc.RegTypeInt), c)
   476  		require.Equal(t, `add x100?, x15, #0xaaa`, formatEmittedInstructionsInCurrentBlock(m))
   477  	})
   478  	t.Run("negative imm12", func(t *testing.T) {
   479  		c := int64(-0xaaa)
   480  		ctx, _, m := newSetupWithMockContext()
   481  		ctx.vRegCounter = nextVRegID - 1
   482  		m.addConstToReg64(regalloc.FromRealReg(x15, regalloc.RegTypeInt), c)
   483  		require.Equal(t, `sub x100?, x15, #0xaaa`, formatEmittedInstructionsInCurrentBlock(m))
   484  	})
   485  	t.Run("non imm12", func(t *testing.T) {
   486  		c := int64(1<<32 | 1)
   487  		ctx, _, m := newSetupWithMockContext()
   488  		ctx.vRegCounter = nextVRegID - 1
   489  		m.addConstToReg64(regalloc.FromRealReg(x15, regalloc.RegTypeInt), c)
   490  		require.Equal(t, `movz x101?, #0x1, lsl 0
   491  movk x101?, #0x1, lsl 32
   492  add x100?, x15, x101?`, formatEmittedInstructionsInCurrentBlock(m))
   493  	})
   494  }
   495  
   496  func TestMachine_addReg64ToReg64(t *testing.T) {
   497  	const nextVRegID = 100
   498  	for _, tc := range []struct {
   499  		exp    string
   500  		rn, rm regalloc.VReg
   501  	}{
   502  		{
   503  			exp: "add x100?, x0, x1",
   504  			rn:  regalloc.FromRealReg(x0, regalloc.RegTypeInt),
   505  			rm:  regalloc.FromRealReg(x1, regalloc.RegTypeInt),
   506  		},
   507  		{
   508  			exp: "add x100?, x10, x12",
   509  			rn:  regalloc.FromRealReg(x10, regalloc.RegTypeInt),
   510  			rm:  regalloc.FromRealReg(x12, regalloc.RegTypeInt),
   511  		},
   512  	} {
   513  		tc := tc
   514  		t.Run(tc.exp, func(t *testing.T) {
   515  			ctx, _, m := newSetupWithMockContext()
   516  			ctx.vRegCounter = nextVRegID - 1
   517  			rd := m.addReg64ToReg64(tc.rn, tc.rm)
   518  			require.Equal(t, tc.exp, formatEmittedInstructionsInCurrentBlock(m))
   519  			require.Equal(t, rd, regalloc.VReg(nextVRegID).SetRegType(regalloc.RegTypeInt))
   520  		})
   521  	}
   522  }
   523  
   524  func TestMachine_addRegToReg64Ext(t *testing.T) {
   525  	const nextVRegID = 100
   526  	for _, tc := range []struct {
   527  		exp    string
   528  		rn, rm regalloc.VReg
   529  		ext    extendOp
   530  	}{
   531  		{
   532  			exp: "add x100?, x0, w1 UXTW",
   533  			rn:  regalloc.FromRealReg(x0, regalloc.RegTypeInt),
   534  			rm:  regalloc.FromRealReg(x1, regalloc.RegTypeInt),
   535  			ext: extendOpUXTW,
   536  		},
   537  		{
   538  			exp: "add x100?, x0, w1 SXTW",
   539  			rn:  regalloc.FromRealReg(x0, regalloc.RegTypeInt),
   540  			rm:  regalloc.FromRealReg(x1, regalloc.RegTypeInt),
   541  			ext: extendOpSXTW,
   542  		},
   543  	} {
   544  		tc := tc
   545  		t.Run(tc.exp, func(t *testing.T) {
   546  			ctx, _, m := newSetupWithMockContext()
   547  			ctx.vRegCounter = nextVRegID - 1
   548  			rd := m.addRegToReg64Ext(tc.rn, tc.rm, tc.ext)
   549  			require.Equal(t, tc.exp, formatEmittedInstructionsInCurrentBlock(m))
   550  			require.Equal(t, rd, regalloc.VReg(nextVRegID).SetRegType(regalloc.RegTypeInt))
   551  		})
   552  	}
   553  }
   554  
   555  func TestMachine_lowerToAddressModeFromAddends(t *testing.T) {
   556  	x1, x2, x3 := regalloc.FromRealReg(x1, regalloc.RegTypeInt), regalloc.FromRealReg(x2, regalloc.RegTypeInt), regalloc.FromRealReg(x3, regalloc.RegTypeInt)
   557  	x4, x5, x6 := regalloc.FromRealReg(x4, regalloc.RegTypeInt), regalloc.FromRealReg(x5, regalloc.RegTypeInt), regalloc.FromRealReg(x6, regalloc.RegTypeInt)
   558  
   559  	nextVReg, nextNextVeg := regalloc.VReg(100).SetRegType(regalloc.RegTypeInt), regalloc.VReg(101).SetRegType(regalloc.RegTypeInt)
   560  	for _, tc := range []struct {
   561  		name          string
   562  		a32s          []addend32
   563  		a64s          []regalloc.VReg
   564  		dstSizeInBits byte
   565  		offset        int64
   566  		exp           addressMode
   567  		insts         []string
   568  	}{
   569  		{
   570  			name:   "only offset",
   571  			offset: 4095,
   572  			insts:  []string{"orr x100?, xzr, #0xfff"},
   573  			exp:    addressMode{kind: addressModeKindRegUnsignedImm12, rn: nextVReg, imm: 0},
   574  		},
   575  		{
   576  			name:   "only offset",
   577  			offset: 4095 << 12,
   578  			insts:  []string{"orr x100?, xzr, #0xfff000"},
   579  			exp:    addressMode{kind: addressModeKindRegUnsignedImm12, rn: nextVReg, imm: 0},
   580  		},
   581  		{
   582  			name:          "one a64 with imm12",
   583  			a64s:          []regalloc.VReg{x1},
   584  			offset:        4095,
   585  			dstSizeInBits: 8,
   586  			exp:           addressMode{kind: addressModeKindRegUnsignedImm12, rn: x1, imm: 4095},
   587  		},
   588  		{
   589  			name:          "one a64 with imm12",
   590  			a64s:          []regalloc.VReg{x1},
   591  			offset:        4095 * 2,
   592  			dstSizeInBits: 16,
   593  			exp:           addressMode{kind: addressModeKindRegUnsignedImm12, rn: x1, imm: 4095 * 2},
   594  		},
   595  		{
   596  			name:          "one a64 with imm12",
   597  			a64s:          []regalloc.VReg{x1},
   598  			offset:        4095 * 4,
   599  			dstSizeInBits: 32,
   600  			exp:           addressMode{kind: addressModeKindRegUnsignedImm12, rn: x1, imm: 4095 * 4},
   601  		},
   602  		{
   603  			name:          "one a64 with imm12",
   604  			a64s:          []regalloc.VReg{x1},
   605  			offset:        4095 * 8,
   606  			dstSizeInBits: 64,
   607  			exp:           addressMode{kind: addressModeKindRegUnsignedImm12, rn: x1, imm: 4095 * 8},
   608  		},
   609  		{
   610  			name:          "one a64 with imm9",
   611  			a64s:          []regalloc.VReg{x1},
   612  			dstSizeInBits: 64,
   613  			exp:           addressMode{kind: addressModeKindRegSignedImm9, rn: x1, imm: 0},
   614  		},
   615  		{
   616  			name:          "one a64 with imm9",
   617  			a64s:          []regalloc.VReg{x1},
   618  			offset:        -256,
   619  			dstSizeInBits: 64,
   620  			exp:           addressMode{kind: addressModeKindRegSignedImm9, rn: x1, imm: -256},
   621  		},
   622  		{
   623  			name:          "one a64 with offset not fitting",
   624  			a64s:          []regalloc.VReg{x1},
   625  			offset:        1 << 16,
   626  			dstSizeInBits: 64,
   627  			insts:         []string{"add x100?, x1, #0x10000"},
   628  			exp:           addressMode{kind: addressModeKindRegUnsignedImm12, rn: nextVReg, imm: 0},
   629  		},
   630  		{
   631  			name:          "two a64 with imm12",
   632  			a64s:          []regalloc.VReg{x1, x2},
   633  			offset:        4095,
   634  			dstSizeInBits: 8,
   635  			insts:         []string{"add x100?, x1, x2"},
   636  			exp:           addressMode{kind: addressModeKindRegUnsignedImm12, rn: nextVReg, imm: 4095},
   637  		},
   638  		{
   639  			name:          "two a64 with imm12",
   640  			a64s:          []regalloc.VReg{x1, x2},
   641  			offset:        4095 * 2,
   642  			dstSizeInBits: 16,
   643  			insts:         []string{"add x100?, x1, x2"},
   644  			exp:           addressMode{kind: addressModeKindRegUnsignedImm12, rn: nextVReg, imm: 4095 * 2},
   645  		},
   646  		{
   647  			name:          "two a64 with imm12",
   648  			a64s:          []regalloc.VReg{x1, x2},
   649  			offset:        4095 * 4,
   650  			dstSizeInBits: 32,
   651  			insts:         []string{"add x100?, x1, x2"},
   652  			exp:           addressMode{kind: addressModeKindRegUnsignedImm12, rn: nextVReg, imm: 4095 * 4},
   653  		},
   654  		{
   655  			name:          "two a64 with imm12",
   656  			a64s:          []regalloc.VReg{x1, x2},
   657  			offset:        4095 * 8,
   658  			dstSizeInBits: 64,
   659  			insts:         []string{"add x100?, x1, x2"},
   660  			exp:           addressMode{kind: addressModeKindRegUnsignedImm12, rn: nextVReg, imm: 4095 * 8},
   661  		},
   662  		{
   663  			name:          "two a64 with imm9",
   664  			a64s:          []regalloc.VReg{x1, x2},
   665  			dstSizeInBits: 64,
   666  			insts:         []string{"add x100?, x1, x2"},
   667  			exp:           addressMode{kind: addressModeKindRegSignedImm9, rn: nextVReg, imm: 0},
   668  		},
   669  		{
   670  			name:          "two a64 with imm9",
   671  			a64s:          []regalloc.VReg{x1, x2},
   672  			offset:        -256,
   673  			dstSizeInBits: 64,
   674  			insts:         []string{"add x100?, x1, x2"},
   675  			exp:           addressMode{kind: addressModeKindRegSignedImm9, rn: nextVReg, imm: -256},
   676  		},
   677  		{
   678  			name:          "two a64 with offset not fitting",
   679  			a64s:          []regalloc.VReg{x1, x2},
   680  			offset:        1 << 16,
   681  			dstSizeInBits: 64,
   682  			insts:         []string{"add x100?, x1, #0x10000"},
   683  			exp:           addressMode{kind: addressModeKindRegReg, rn: nextVReg, rm: x2, extOp: extendOpUXTX},
   684  		},
   685  		{
   686  			name:          "three a64 with imm12",
   687  			a64s:          []regalloc.VReg{x1, x2, x3},
   688  			offset:        4095 * 2,
   689  			dstSizeInBits: 16,
   690  			insts: []string{
   691  				"add x100?, x1, x2",
   692  				"add x101?, x100?, x3",
   693  			},
   694  			exp: addressMode{kind: addressModeKindRegUnsignedImm12, rn: nextNextVeg, imm: 4095 * 2},
   695  		},
   696  		{
   697  			name:          "three a64 with imm12",
   698  			a64s:          []regalloc.VReg{x1, x2, x3},
   699  			offset:        4095 * 4,
   700  			dstSizeInBits: 32,
   701  			insts: []string{
   702  				"add x100?, x1, x2",
   703  				"add x101?, x100?, x3",
   704  			},
   705  			exp: addressMode{kind: addressModeKindRegUnsignedImm12, rn: nextNextVeg, imm: 4095 * 4},
   706  		},
   707  		{
   708  			name:          "three a64 with imm12",
   709  			a64s:          []regalloc.VReg{x1, x2, x3},
   710  			offset:        4095 * 8,
   711  			dstSizeInBits: 64,
   712  			insts: []string{
   713  				"add x100?, x1, x2",
   714  				"add x101?, x100?, x3",
   715  			},
   716  			exp: addressMode{kind: addressModeKindRegUnsignedImm12, rn: nextNextVeg, imm: 4095 * 8},
   717  		},
   718  		{
   719  			name:          "three a64 with imm9",
   720  			a64s:          []regalloc.VReg{x1, x2, x3},
   721  			dstSizeInBits: 64,
   722  			insts: []string{
   723  				"add x100?, x1, x2",
   724  				"add x101?, x100?, x3",
   725  			},
   726  			exp: addressMode{kind: addressModeKindRegSignedImm9, rn: nextNextVeg, imm: 0},
   727  		},
   728  		{
   729  			name:          "three a64 with imm9",
   730  			a64s:          []regalloc.VReg{x1, x2, x3},
   731  			offset:        -256,
   732  			dstSizeInBits: 64,
   733  			insts: []string{
   734  				"add x100?, x1, x2",
   735  				"add x101?, x100?, x3",
   736  			},
   737  			exp: addressMode{kind: addressModeKindRegSignedImm9, rn: nextNextVeg, imm: -256},
   738  		},
   739  		{
   740  			name:          "three a64 with offset not fitting",
   741  			a64s:          []regalloc.VReg{x1, x2, x3},
   742  			offset:        1 << 16,
   743  			dstSizeInBits: 64,
   744  			insts: []string{
   745  				"add x100?, x1, #0x10000",
   746  				"add x101?, x100?, x3",
   747  			},
   748  			exp: addressMode{kind: addressModeKindRegReg, rn: nextNextVeg, rm: x2, extOp: extendOpUXTX},
   749  		},
   750  		{
   751  			name:          "three a32/a64 with offset",
   752  			a64s:          []regalloc.VReg{x1, x2, x3},
   753  			a32s:          []addend32{{r: x4, ext: extendOpSXTW}, {r: x5, ext: extendOpUXTW}, {r: x6, ext: extendOpSXTW}},
   754  			offset:        1 << 16,
   755  			dstSizeInBits: 64,
   756  			insts: []string{
   757  				"add x100?, x1, #0x10000",
   758  				"add x101?, x100?, x2",
   759  				"add x102?, x101?, x3",
   760  				"add x103?, x102?, w5 UXTW",
   761  				"add x104?, x103?, w6 SXTW",
   762  			},
   763  			exp: addressMode{
   764  				kind: addressModeKindRegExtended,
   765  				rn:   regalloc.VReg(104).SetRegType(regalloc.RegTypeInt),
   766  				rm:   x4, extOp: extendOpSXTW,
   767  			},
   768  		},
   769  		{
   770  			name:   "one a32 with offset",
   771  			a32s:   []addend32{{r: x1, ext: extendOpSXTW}},
   772  			offset: 1 << 16,
   773  			insts: []string{
   774  				"sxtw x100?, w1",
   775  				"add x101?, x100?, #0x10000",
   776  			},
   777  			exp: addressMode{kind: addressModeKindRegUnsignedImm12, rn: nextNextVeg, imm: 0},
   778  		},
   779  		{
   780  			name:   "two a32s with offset",
   781  			a32s:   []addend32{{r: x1, ext: extendOpSXTW}, {r: x2, ext: extendOpUXTW}},
   782  			offset: 1 << 16,
   783  			insts: []string{
   784  				"sxtw x100?, w1",
   785  				"add x101?, x100?, #0x10000",
   786  			},
   787  			exp: addressMode{
   788  				kind:  addressModeKindRegExtended,
   789  				rn:    regalloc.VReg(101).SetRegType(regalloc.RegTypeInt),
   790  				rm:    x2,
   791  				imm:   0,
   792  				extOp: extendOpUXTW,
   793  			},
   794  		},
   795  	} {
   796  		t.Run(tc.name, func(t *testing.T) {
   797  			ctx, _, m := newSetupWithMockContext()
   798  			ctx.vRegCounter = int(nextVReg.ID()) - 1
   799  
   800  			var a32s wazevoapi.Queue[addend32]
   801  			var a64s wazevoapi.Queue[regalloc.VReg]
   802  			for _, a32 := range tc.a32s {
   803  				a32s.Enqueue(a32)
   804  			}
   805  			for _, a64 := range tc.a64s {
   806  				a64s.Enqueue(a64)
   807  			}
   808  			actual := m.lowerToAddressModeFromAddends(&a32s, &a64s, tc.dstSizeInBits, tc.offset)
   809  			require.Equal(t, strings.Join(tc.insts, "\n"), formatEmittedInstructionsInCurrentBlock(m))
   810  			require.Equal(t, tc.exp, actual, actual.format(tc.dstSizeInBits))
   811  		})
   812  	}
   813  }
   814  
   815  func Test_extLoadSizeSign(t *testing.T) {
   816  	for _, tc := range []struct {
   817  		op      ssa.Opcode
   818  		expSize byte
   819  		signed  bool
   820  	}{
   821  		{op: ssa.OpcodeUload8, expSize: 8, signed: false},
   822  		{op: ssa.OpcodeUload16, expSize: 16, signed: false},
   823  		{op: ssa.OpcodeUload32, expSize: 32, signed: false},
   824  		{op: ssa.OpcodeSload8, expSize: 8, signed: true},
   825  		{op: ssa.OpcodeSload16, expSize: 16, signed: true},
   826  		{op: ssa.OpcodeSload32, expSize: 32, signed: true},
   827  	} {
   828  		size, signed := extLoadSignSize(tc.op)
   829  		require.Equal(t, tc.expSize, size)
   830  		require.Equal(t, tc.signed, signed)
   831  	}
   832  }