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  }