wa-lang.org/wazero@v1.0.2/internal/asm/amd64/impl_staticconst_test.go (about)

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