github.com/bananabytelabs/wazero@v0.0.0-20240105073314-54b22a776da8/internal/asm/amd64/impl_staticconst_test.go (about)

     1  package amd64
     2  
     3  import (
     4  	"encoding/hex"
     5  	"testing"
     6  
     7  	"github.com/bananabytelabs/wazero/internal/asm"
     8  	"github.com/bananabytelabs/wazero/internal/testing/require"
     9  )
    10  
    11  func TestAssemblerImpl_CompileStaticConstToRegister(t *testing.T) {
    12  	a := NewAssembler()
    13  	t.Run("odd count of bytes", func(t *testing.T) {
    14  		err := a.CompileStaticConstToRegister(MOVDQU, asm.NewStaticConst([]byte{1}), RegAX)
    15  		require.Error(t, err)
    16  	})
    17  	t.Run("ok", func(t *testing.T) {
    18  		cons := asm.NewStaticConst([]byte{1, 2, 3, 4})
    19  		err := a.CompileStaticConstToRegister(MOVDQU, cons, RegAX)
    20  		require.NoError(t, err)
    21  		actualNode := a.current
    22  		require.Equal(t, MOVDQU, actualNode.instruction)
    23  		require.Equal(t, operandTypesStaticConstToRegister, actualNode.types)
    24  		require.Equal(t, cons, actualNode.staticConst)
    25  	})
    26  }
    27  
    28  func TestAssemblerImpl_CompileRegisterToStaticConst(t *testing.T) {
    29  	a := NewAssembler()
    30  	t.Run("odd count of bytes", func(t *testing.T) {
    31  		err := a.CompileRegisterToStaticConst(MOVDQU, RegAX, asm.NewStaticConst([]byte{1}))
    32  		require.Error(t, err)
    33  	})
    34  	t.Run("ok", func(t *testing.T) {
    35  		cons := asm.NewStaticConst([]byte{1, 2, 3, 4})
    36  		err := a.CompileRegisterToStaticConst(MOVDQU, RegAX, cons)
    37  		require.NoError(t, err)
    38  		actualNode := a.current
    39  		require.Equal(t, MOVDQU, actualNode.instruction)
    40  		require.Equal(t, operandTypesRegisterToStaticConst, actualNode.types)
    41  		require.Equal(t, cons, actualNode.staticConst)
    42  	})
    43  }
    44  
    45  func TestAssemblerImpl_maybeFlushConstants(t *testing.T) {
    46  	t.Run("no consts", func(t *testing.T) {
    47  		code := asm.CodeSegment{}
    48  		defer func() { require.NoError(t, code.Unmap()) }()
    49  
    50  		a := NewAssembler()
    51  		// Invoking maybeFlushConstants before encoding consts usage should not panic.
    52  		a.maybeFlushConstants(code.NextCodeSection(), false)
    53  		a.maybeFlushConstants(code.NextCodeSection(), true)
    54  	})
    55  
    56  	largeData := make([]byte, 256)
    57  
    58  	tests := []struct {
    59  		name                    string
    60  		endOfFunction           bool
    61  		dummyBodyBeforeFlush    []byte
    62  		firstUseOffsetInBinary  uint64
    63  		consts                  [][]byte
    64  		expectedOffsetForConsts []uint64
    65  		exp                     []byte
    66  		maxDisplacement         int
    67  	}{
    68  		{
    69  			name:                    "end of function",
    70  			endOfFunction:           true,
    71  			dummyBodyBeforeFlush:    []byte{'?', '?', '?', '?'},
    72  			consts:                  [][]byte{{1, 2, 3, 4, 5, 6, 7, 8}, {10, 11, 12, 13}},
    73  			expectedOffsetForConsts: []uint64{4, 4 + 8}, // 4 = len(dummyBodyBeforeFlush)
    74  			firstUseOffsetInBinary:  0,
    75  			exp:                     []byte{'?', '?', '?', '?', 1, 2, 3, 4, 5, 6, 7, 8, 10, 11, 12, 13},
    76  			maxDisplacement:         1 << 31, // large displacement will emit the consts at the end of function.
    77  		},
    78  		{
    79  			name:                   "not flush",
    80  			endOfFunction:          false,
    81  			dummyBodyBeforeFlush:   []byte{'?', '?', '?', '?'},
    82  			consts:                 [][]byte{{1, 2, 3, 4, 5, 6, 7, 8}, {10, 11, 12, 13}},
    83  			firstUseOffsetInBinary: 0,
    84  			exp:                    []byte{'?', '?', '?', '?'},
    85  			maxDisplacement:        1 << 31, // large displacement will emit the consts at the end of function.
    86  		},
    87  		{
    88  			name:                    "not end of function but flush - short jump",
    89  			endOfFunction:           false,
    90  			dummyBodyBeforeFlush:    []byte{'?', '?', '?', '?'},
    91  			consts:                  [][]byte{{1, 2, 3, 4, 5, 6, 7, 8}, {10, 11, 12, 13}},
    92  			expectedOffsetForConsts: []uint64{4 + 2, 4 + 2 + 8}, // 4 = len(dummyBodyBeforeFlush), 2 = the size of jump
    93  			firstUseOffsetInBinary:  0,
    94  			exp: []byte{
    95  				'?', '?', '?', '?',
    96  				0xeb, 0x0c, // short jump with offset = len(consts[0]) + len(consts[1]) = 12 = 0xc.
    97  				1, 2, 3, 4, 5, 6, 7, 8, 10, 11, 12, 13,
    98  			},
    99  			maxDisplacement: 0, // small displacement flushes the const immediately, not at the end of function.
   100  		},
   101  		{
   102  			name:                    "not end of function but flush - long jump",
   103  			endOfFunction:           false,
   104  			dummyBodyBeforeFlush:    []byte{'?', '?', '?', '?'},
   105  			consts:                  [][]byte{largeData},
   106  			expectedOffsetForConsts: []uint64{4 + 5}, // 4 = len(dummyBodyBeforeFlush), 5 = the size of jump
   107  			firstUseOffsetInBinary:  0,
   108  			exp: append([]byte{
   109  				'?', '?', '?', '?',
   110  				0xe9, 0x0, 0x1, 0x0, 0x0, // short jump with offset = 256 = 0x0, 0x1, 0x0, 0x0 (in Little Endian).
   111  			}, largeData...),
   112  			maxDisplacement: 0, // small displacement flushes the const immediately, not at the end of function.
   113  		},
   114  	}
   115  
   116  	for _, tc := range tests {
   117  		t.Run(tc.name, func(t *testing.T) {
   118  			code := asm.CodeSegment{}
   119  			defer func() { require.NoError(t, code.Unmap()) }()
   120  
   121  			a := NewAssembler()
   122  			a.MaxDisplacementForConstantPool = tc.maxDisplacement
   123  
   124  			buf := code.NextCodeSection()
   125  			buf.AppendBytes(tc.dummyBodyBeforeFlush)
   126  
   127  			for i, c := range tc.consts {
   128  				sc := asm.NewStaticConst(c)
   129  				a.pool.AddConst(sc, 100)
   130  				i := i
   131  				sc.AddOffsetFinalizedCallback(func(offsetOfConstInBinary uint64) {
   132  					require.Equal(t, tc.expectedOffsetForConsts[i], offsetOfConstInBinary)
   133  				})
   134  			}
   135  
   136  			a.pool.FirstUseOffsetInBinary = tc.firstUseOffsetInBinary
   137  			a.maybeFlushConstants(buf, tc.endOfFunction)
   138  
   139  			require.Equal(t, tc.exp, buf.Bytes())
   140  		})
   141  	}
   142  }
   143  
   144  func TestAssemblerImpl_encodeRegisterToStaticConst(t *testing.T) {
   145  	tests := []struct {
   146  		name            string
   147  		ins             asm.Instruction
   148  		c               []byte
   149  		reg             asm.Register
   150  		ud2sBeforeConst int
   151  		exp             []byte
   152  	}{
   153  		{
   154  			name:            "cmp r12d, dword ptr [rip + 0x14]",
   155  			ins:             CMPL,
   156  			c:               []byte{0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8},
   157  			reg:             RegR12,
   158  			ud2sBeforeConst: 10,
   159  			exp: []byte{
   160  				// cmp r12d, dword ptr [rip + 0x14]
   161  				// where rip = 0x7, therefore [rip + 0x14] = [0x1b]
   162  				0x44, 0x3b, 0x25, 0x14, 0x0, 0x0, 0x0,
   163  				// UD2 * ud2sBeforeConst
   164  				0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb,
   165  				// 0x1b: consts
   166  				0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8,
   167  			},
   168  		},
   169  		{
   170  			name:            "cmp eax, dword ptr [rip + 0x14]",
   171  			ins:             CMPL,
   172  			c:               []byte{0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8},
   173  			reg:             RegAX,
   174  			ud2sBeforeConst: 10,
   175  			exp: []byte{
   176  				// cmp eax, dword ptr [rip + 0x14]
   177  				// where rip = 0x6, therefore [rip + 0x14] = [0x1a]
   178  				0x3b, 0x5, 0x14, 0x0, 0x0, 0x0,
   179  				// UD2 * ud2sBeforeConst
   180  				0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb,
   181  				// 0x1a: consts
   182  				0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8,
   183  			},
   184  		},
   185  		{
   186  			name:            "cmp r12, qword ptr [rip]",
   187  			ins:             CMPQ,
   188  			c:               []byte{0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8},
   189  			reg:             RegR12,
   190  			ud2sBeforeConst: 0,
   191  			exp: []byte{
   192  				// cmp r12, qword ptr [rip]
   193  				// where rip points to the end of this instruction == the const.
   194  				0x4c, 0x3b, 0x25, 0x0, 0x0, 0x0, 0x0,
   195  				0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8,
   196  			},
   197  		},
   198  		{
   199  			name:            "cmp rsp, qword ptr [rip + 0xa]",
   200  			ins:             CMPQ,
   201  			c:               []byte{0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8},
   202  			reg:             RegSP,
   203  			ud2sBeforeConst: 5,
   204  			exp: []byte{
   205  				// cmp rsp, qword ptr [rip + 0xa]
   206  				// where rip = 0x6, therefore [rip + 0xa] = [0x11]
   207  				0x48, 0x3b, 0x25, 0xa, 0x0, 0x0, 0x0,
   208  				// UD2 * ud2sBeforeConst
   209  				0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb,
   210  				// 0x11:
   211  				0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8,
   212  			},
   213  		},
   214  	}
   215  
   216  	for _, tc := range tests {
   217  		tc := tc
   218  		t.Run(tc.name, func(t *testing.T) {
   219  			code := asm.CodeSegment{}
   220  			defer func() { require.NoError(t, code.Unmap()) }()
   221  
   222  			a := NewAssembler()
   223  
   224  			err := a.CompileRegisterToStaticConst(tc.ins, tc.reg, asm.NewStaticConst(tc.c))
   225  			require.NoError(t, err)
   226  
   227  			for i := 0; i < tc.ud2sBeforeConst; i++ {
   228  				a.CompileStandAlone(UD2)
   229  			}
   230  
   231  			buf := code.NextCodeSection()
   232  			err = a.Assemble(buf)
   233  			require.NoError(t, err)
   234  
   235  			actual := buf.Bytes()
   236  			require.Equal(t, tc.exp, actual, hex.EncodeToString(actual))
   237  		})
   238  	}
   239  }
   240  
   241  func TestAssemblerImpl_encodeStaticConstToRegister(t *testing.T) {
   242  	tests := []struct {
   243  		name            string
   244  		ins             asm.Instruction
   245  		c               []byte
   246  		reg             asm.Register
   247  		ud2sBeforeConst int
   248  		exp             []byte
   249  	}{
   250  		{
   251  			name: "movdqu xmm14, xmmword ptr [rip + 0xa]",
   252  			ins:  MOVDQU,
   253  			c: []byte{
   254  				0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8,
   255  				0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80,
   256  			},
   257  			reg:             RegX14,
   258  			ud2sBeforeConst: 5,
   259  			exp: []byte{
   260  				// movdqu xmm14, xmmword ptr [rip + 0xa]
   261  				// where rip = 0x9, therefore [rip + 0xa] = [0x13]
   262  				0xf3, 0x44, 0xf, 0x6f, 0x35, 0xa, 0x0, 0x0, 0x0,
   263  				// UD2 * ud2sBeforeConst
   264  				0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb,
   265  				// 0x13:
   266  				0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8,
   267  				0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80,
   268  			},
   269  		},
   270  		{
   271  			name: "movupd xmm1, xmmword ptr [rip + 0xa]",
   272  			ins:  MOVUPD,
   273  			c: []byte{
   274  				0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8,
   275  				0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80,
   276  			},
   277  			reg:             RegX1,
   278  			ud2sBeforeConst: 5,
   279  			exp: []byte{
   280  				// movdqu xmm14, xmmword ptr [rip + 0xa]
   281  				// where rip = 0x8, therefore [rip + 0xa] = [0x12]
   282  				0x66, 0xf, 0x10, 0xd, 0xa, 0x0, 0x0, 0x0,
   283  				// UD2 * ud2sBeforeConst
   284  				0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb,
   285  				// 0x12:
   286  				0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8,
   287  				0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80,
   288  			},
   289  		},
   290  		{
   291  			name: "lea  r11, [rip + 0x14]",
   292  			ins:  LEAQ,
   293  			c: []byte{
   294  				0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8,
   295  				0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80,
   296  			},
   297  			reg:             RegR11,
   298  			ud2sBeforeConst: 10,
   299  			exp: []byte{
   300  				// lea  r11, [rip + 0x14]
   301  				// where rip = 0x7, therefore [rip + 0x14] = [0x1b]
   302  				0x4c, 0x8d, 0x1d, 0x14, 0x0, 0x0, 0x0,
   303  				// UD2 * ud2sBeforeConst
   304  				0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb,
   305  				0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb,
   306  				// 0x1b:
   307  				0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8,
   308  				0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80,
   309  			},
   310  		},
   311  		{
   312  			name: "mov  r11d, dword ptr [rip + 0x3c]",
   313  			ins:  MOVL,
   314  			c: []byte{
   315  				0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8,
   316  				0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80,
   317  			},
   318  			reg:             RegR11,
   319  			ud2sBeforeConst: 30,
   320  			exp: []byte{
   321  				// mov  r11d, dword ptr [rip + 0x3c]
   322  				// where rip = 0x7, therefore [rip + 0x3c] = [0x43]
   323  				0x44, 0x8b, 0x1d, 0x3c, 0x0, 0x0, 0x0,
   324  				// UD2 * ud2sBeforeConst
   325  				0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb,
   326  				0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb,
   327  				0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb,
   328  				0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb,
   329  				0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb,
   330  				0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb,
   331  				// 0x43:
   332  				0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8,
   333  				0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80,
   334  			},
   335  		},
   336  		{
   337  			name: "movd xmm14, dword ptr [rip + 0x3c]",
   338  			ins:  MOVL,
   339  			c: []byte{
   340  				0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8,
   341  				0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80,
   342  			},
   343  			reg:             RegX14,
   344  			ud2sBeforeConst: 30,
   345  			exp: []byte{
   346  				// movd xmm14, dword ptr [rip + 0x3c]
   347  				// where rip = 0x9, therefore [rip + 0x3c] = [0x45]
   348  				0x66, 0x44, 0xf, 0x6e, 0x35, 0x3c, 0x0, 0x0, 0x0,
   349  				// UD2 * ud2sBeforeConst
   350  				0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb,
   351  				0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb,
   352  				0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb,
   353  				0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb,
   354  				0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb,
   355  				0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb,
   356  				// 0x45:
   357  				0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8,
   358  				0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80,
   359  			},
   360  		},
   361  		{
   362  			name: "mov  rsp, qword ptr [rip + 0x3c]",
   363  			ins:  MOVQ,
   364  			c: []byte{
   365  				0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8,
   366  				0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80,
   367  			},
   368  			reg:             RegSP,
   369  			ud2sBeforeConst: 30,
   370  			exp: []byte{
   371  				// mov  rsp, qword ptr [rip + 0x3c]
   372  				// where rip = 0x7, therefore [rip + 0x3c] = [0x43]
   373  				0x48, 0x8b, 0x25, 0x3c, 0x0, 0x0, 0x0,
   374  				// UD2 * ud2sBeforeConst
   375  				0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb,
   376  				0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb,
   377  				0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb,
   378  				0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb,
   379  				0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb,
   380  				0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb,
   381  				// 0x43:
   382  				0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8,
   383  				0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80,
   384  			},
   385  		},
   386  		{
   387  			name: "movq xmm1, qword ptr [rip + 0x3c]",
   388  			ins:  MOVQ,
   389  			c: []byte{
   390  				0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8,
   391  				0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80,
   392  			},
   393  			reg:             RegX1,
   394  			ud2sBeforeConst: 30,
   395  			exp: []byte{
   396  				// movq xmm1, qword ptr [rip + 0x3c]
   397  				// where rip = 0x8, therefore [rip + 0x3c] = [0x44]
   398  				0xf3, 0xf, 0x7e, 0xd, 0x3c, 0x0, 0x0, 0x0,
   399  				// UD2 * ud2sBeforeConst
   400  				0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb,
   401  				0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb,
   402  				0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb,
   403  				0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb,
   404  				0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb,
   405  				0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb,
   406  				// 0x44:
   407  				0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8,
   408  				0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80,
   409  			},
   410  		},
   411  		{
   412  			name:            "ucomisd xmm15, qword ptr [rip + 6]",
   413  			ins:             UCOMISD,
   414  			c:               []byte{0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8},
   415  			reg:             RegX15,
   416  			ud2sBeforeConst: 3,
   417  			exp: []byte{
   418  				// ucomisd xmm15, qword ptr [rip + 6]
   419  				// where rip = 0x9, therefore [rip + 6] = [0xf]
   420  				0x66, 0x44, 0xf, 0x2e, 0x3d, 0x6, 0x0, 0x0, 0x0,
   421  				// UD2 * ud2sBeforeConst
   422  				0xf, 0xb, 0xf, 0xb, 0xf, 0xb,
   423  				// 0xf:
   424  				0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8,
   425  			},
   426  		},
   427  		{
   428  			name:            "ucomiss xmm15, dword ptr [rip + 6]",
   429  			ins:             UCOMISS,
   430  			c:               []byte{0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8},
   431  			reg:             RegX15,
   432  			ud2sBeforeConst: 3,
   433  			exp: []byte{
   434  				// ucomiss xmm15, dword ptr [rip + 6]
   435  				// where rip = 0x8, therefore [rip + 6] = [0xe]
   436  				0x44, 0xf, 0x2e, 0x3d, 0x6, 0x0, 0x0, 0x0,
   437  				// UD2 * ud2sBeforeConst
   438  				0xf, 0xb, 0xf, 0xb, 0xf, 0xb,
   439  				// 0xe:
   440  				0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8,
   441  			},
   442  		},
   443  		{
   444  			name:            "subss xmm13, dword ptr [rip + 0xa]",
   445  			ins:             SUBSS,
   446  			c:               []byte{0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8},
   447  			reg:             RegX13,
   448  			ud2sBeforeConst: 5,
   449  			exp: []byte{
   450  				// subss xmm13, dword ptr [rip + 0xa]
   451  				// where rip = 0x9, therefore [rip + 0xa] = [0x13]
   452  				0xf3, 0x44, 0xf, 0x5c, 0x2d, 0xa, 0x0, 0x0, 0x0,
   453  				// UD2 * ud2sBeforeConst
   454  				0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb,
   455  				// 0x12:
   456  				0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8,
   457  			},
   458  		},
   459  		{
   460  			name:            "subsd xmm1, qword ptr [rip + 0xa]",
   461  			ins:             SUBSD,
   462  			c:               []byte{0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8},
   463  			reg:             RegX1,
   464  			ud2sBeforeConst: 5,
   465  			exp: []byte{
   466  				// subsd xmm1, qword ptr [rip + 0xa]
   467  				// where rip = 0x8, therefore [rip + 0xa] = [0x12]
   468  				0xf2, 0xf, 0x5c, 0xd, 0xa, 0x0, 0x0, 0x0,
   469  				// UD2 * ud2sBeforeConst
   470  				0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb,
   471  				// 0x12:
   472  				0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8,
   473  			},
   474  		},
   475  		{
   476  			name:            "cmp dword ptr [rip + 0x14], r12d",
   477  			ins:             CMPL,
   478  			c:               []byte{0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8},
   479  			reg:             RegR12,
   480  			ud2sBeforeConst: 10,
   481  			exp: []byte{
   482  				// cmp dword ptr [rip + 0x14], r12d
   483  				// where rip = 0x7, therefore [rip + 0x14] = [0x1b]
   484  				0x44, 0x39, 0x25, 0x14, 0x0, 0x0, 0x0,
   485  				// UD2 * ud2sBeforeConst
   486  				0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb,
   487  				// 0x1b: consts
   488  				0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8,
   489  			},
   490  		},
   491  		{
   492  			name:            "cmp dword ptr [rip + 0x14], eax",
   493  			ins:             CMPL,
   494  			c:               []byte{0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8},
   495  			reg:             RegAX,
   496  			ud2sBeforeConst: 10,
   497  			exp: []byte{
   498  				// cmp dword ptr [rip + 0x14], eax
   499  				// where rip = 0x6, therefore [rip + 0x14] = [0x1a]
   500  				0x39, 0x5, 0x14, 0x0, 0x0, 0x0,
   501  				// UD2 * ud2sBeforeConst
   502  				0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb,
   503  				// 0x1a: consts
   504  				0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8,
   505  			},
   506  		},
   507  		{
   508  			name:            "cmp qword ptr [rip], r12",
   509  			ins:             CMPQ,
   510  			c:               []byte{0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8},
   511  			reg:             RegR12,
   512  			ud2sBeforeConst: 0,
   513  			exp: []byte{
   514  				// cmp qword ptr [rip], r12
   515  				// where rip points to the end of this instruction == the const.
   516  				0x4c, 0x39, 0x25, 0x0, 0x0, 0x0, 0x0,
   517  				0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8,
   518  			},
   519  		},
   520  		{
   521  			name:            "cmp qword ptr [rip + 0xa], rsp",
   522  			ins:             CMPQ,
   523  			c:               []byte{0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8},
   524  			reg:             RegSP,
   525  			ud2sBeforeConst: 5,
   526  			exp: []byte{
   527  				// cmp qword ptr [rip + 0xa], rsp
   528  				// where rip = 0x6, therefore [rip + 0xa] = [0x11]
   529  				0x48, 0x39, 0x25, 0xa, 0x0, 0x0, 0x0,
   530  				// UD2 * ud2sBeforeConst
   531  				0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb,
   532  				// 0x11:
   533  				0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8,
   534  			},
   535  		},
   536  		{
   537  			name:            "ucomiss xmm15, dword ptr [rip + 6]",
   538  			ins:             UCOMISS,
   539  			c:               []byte{0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8},
   540  			reg:             RegX15,
   541  			ud2sBeforeConst: 3,
   542  			exp: []byte{
   543  				// ucomiss xmm15, dword ptr [rip + 6]
   544  				// where rip = 0x8, therefore [rip + 6] = [0xe]
   545  				0x44, 0xf, 0x2e, 0x3d, 0x6, 0x0, 0x0, 0x0,
   546  				// UD2 * ud2sBeforeConst
   547  				0xf, 0xb, 0xf, 0xb, 0xf, 0xb,
   548  				// 0xe:
   549  				0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8,
   550  			},
   551  		},
   552  		{
   553  			name:            "subss xmm13, dword ptr [rip + 0xa]",
   554  			ins:             SUBSS,
   555  			c:               []byte{0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8},
   556  			reg:             RegX13,
   557  			ud2sBeforeConst: 5,
   558  			exp: []byte{
   559  				// subss xmm13, dword ptr [rip + 0xa]
   560  				// where rip = 0x9, therefore [rip + 0xa] = [0x13]
   561  				0xf3, 0x44, 0xf, 0x5c, 0x2d, 0xa, 0x0, 0x0, 0x0,
   562  				// UD2 * ud2sBeforeConst
   563  				0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb,
   564  				// 0x12:
   565  				0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8,
   566  			},
   567  		},
   568  		{
   569  			name:            "subsd xmm1, qword ptr [rip + 0xa]",
   570  			ins:             SUBSD,
   571  			c:               []byte{0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8},
   572  			reg:             RegX1,
   573  			ud2sBeforeConst: 5,
   574  			exp: []byte{
   575  				// subsd xmm1, qword ptr [rip + 0xa]
   576  				// where rip = 0x8, therefore [rip + 0xa] = [0x12]
   577  				0xf2, 0xf, 0x5c, 0xd, 0xa, 0x0, 0x0, 0x0,
   578  				// UD2 * ud2sBeforeConst
   579  				0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb,
   580  				// 0x12:
   581  				0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8,
   582  			},
   583  		},
   584  		{
   585  			name:            "add eax, dword ptr [rip + 0xa]",
   586  			ins:             ADDL,
   587  			c:               []byte{0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8},
   588  			reg:             RegAX,
   589  			ud2sBeforeConst: 5,
   590  			exp: []byte{
   591  				// add eax, dword ptr [rip + 0xa]
   592  				// where rip = 0x6, therefore [rip + 0xa] = [0x10]
   593  				0x3, 0x5, 0xa, 0x0, 0x0, 0x0,
   594  				// UD2 * ud2sBeforeConst
   595  				0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb,
   596  				// 0x10:
   597  				0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8,
   598  			},
   599  		},
   600  		{
   601  			name:            "add rax, qword ptr [rip + 0xa]",
   602  			ins:             ADDQ,
   603  			c:               []byte{0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8},
   604  			reg:             RegAX,
   605  			ud2sBeforeConst: 5,
   606  			exp: []byte{
   607  				// add rax, dword ptr [rip + 0xa]
   608  				// where rip = 0x7, therefore [rip + 0xa] = [0x11]
   609  				0x48, 0x3, 0x5, 0xa, 0x0, 0x0, 0x0,
   610  				// UD2 * ud2sBeforeConst
   611  				0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb, 0xf, 0xb,
   612  				// 0x11:
   613  				0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8,
   614  			},
   615  		},
   616  	}
   617  
   618  	for _, tc := range tests {
   619  		tc := tc
   620  		t.Run(tc.name, func(t *testing.T) {
   621  			code := asm.CodeSegment{}
   622  			defer func() { require.NoError(t, code.Unmap()) }()
   623  
   624  			a := NewAssembler()
   625  
   626  			err := a.CompileStaticConstToRegister(tc.ins, asm.NewStaticConst(tc.c), tc.reg)
   627  			require.NoError(t, err)
   628  
   629  			for i := 0; i < tc.ud2sBeforeConst; i++ {
   630  				a.CompileStandAlone(UD2)
   631  			}
   632  
   633  			buf := code.NextCodeSection()
   634  			err = a.Assemble(buf)
   635  			require.NoError(t, err)
   636  
   637  			actual := buf.Bytes()
   638  			require.Equal(t, tc.exp, actual, hex.EncodeToString(actual))
   639  		})
   640  	}
   641  }