github.com/go-asm/go@v1.21.1-0.20240213172139-40c5ead50c48/cmd/obj/x86/asm_test.go (about)

     1  // Copyright 2018 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package x86
     6  
     7  import (
     8  	"os"
     9  	"path/filepath"
    10  	"regexp"
    11  	"testing"
    12  
    13  	"github.com/go-asm/go/cmd/obj"
    14  	"github.com/go-asm/go/cmd/objabi"
    15  	"github.com/go-asm/go/testenv"
    16  )
    17  
    18  type oclassTest struct {
    19  	arg  *obj.Addr
    20  	want int // Expected oclass return value for a given arg
    21  }
    22  
    23  // Filled inside init, because it's easier to do with helper functions.
    24  var (
    25  	oclassTestsAMD64 []*oclassTest
    26  	oclassTests386   []*oclassTest
    27  )
    28  
    29  func init() {
    30  	// Required for tests that access any of
    31  	// opindex/ycover/reg/regrex global tables.
    32  	var ctxt obj.Link
    33  	instinit(&ctxt)
    34  
    35  	regAddr := func(reg int16) *obj.Addr {
    36  		return &obj.Addr{Type: obj.TYPE_REG, Reg: reg}
    37  	}
    38  	immAddr := func(v int64) *obj.Addr {
    39  		return &obj.Addr{Type: obj.TYPE_CONST, Offset: v}
    40  	}
    41  	regListAddr := func(regFrom, regTo int16) *obj.Addr {
    42  		return &obj.Addr{Type: obj.TYPE_REGLIST, Offset: EncodeRegisterRange(regFrom, regTo)}
    43  	}
    44  	memAddr := func(base, index int16) *obj.Addr {
    45  		return &obj.Addr{Type: obj.TYPE_MEM, Reg: base, Index: index}
    46  	}
    47  
    48  	// TODO(quasilyte): oclass doesn't return Yxxx for X/Y regs with
    49  	// ID higher than 7. We don't encode such instructions, but this
    50  	// behavior seems inconsistent. It should probably either
    51  	// never check for arch or do it in all cases.
    52  
    53  	oclassTestsCommon := []*oclassTest{
    54  		{&obj.Addr{Type: obj.TYPE_NONE}, Ynone},
    55  		{&obj.Addr{Type: obj.TYPE_BRANCH}, Ybr},
    56  		{&obj.Addr{Type: obj.TYPE_TEXTSIZE}, Ytextsize},
    57  
    58  		{&obj.Addr{Type: obj.TYPE_INDIR, Name: obj.NAME_EXTERN}, Yindir},
    59  		{&obj.Addr{Type: obj.TYPE_INDIR, Name: obj.NAME_GOTREF}, Yindir},
    60  
    61  		{&obj.Addr{Type: obj.TYPE_ADDR, Name: obj.NAME_AUTO}, Yiauto},
    62  		{&obj.Addr{Type: obj.TYPE_ADDR, Name: obj.NAME_PARAM}, Yiauto},
    63  		{&obj.Addr{Type: obj.TYPE_ADDR, Name: obj.NAME_EXTERN}, Yiauto},
    64  		{&obj.Addr{Type: obj.TYPE_ADDR, Sym: &obj.LSym{Name: "runtime.duff"}}, Yi32},
    65  		{&obj.Addr{Type: obj.TYPE_ADDR, Offset: 4}, Yu7},
    66  		{&obj.Addr{Type: obj.TYPE_ADDR, Offset: 255}, Yu8},
    67  
    68  		{immAddr(0), Yi0},
    69  		{immAddr(1), Yi1},
    70  		{immAddr(2), Yu2},
    71  		{immAddr(3), Yu2},
    72  		{immAddr(4), Yu7},
    73  		{immAddr(86), Yu7},
    74  		{immAddr(127), Yu7},
    75  		{immAddr(128), Yu8},
    76  		{immAddr(200), Yu8},
    77  		{immAddr(255), Yu8},
    78  		{immAddr(-1), Yi8},
    79  		{immAddr(-100), Yi8},
    80  		{immAddr(-128), Yi8},
    81  
    82  		{regAddr(REG_AL), Yal},
    83  		{regAddr(REG_AX), Yax},
    84  		{regAddr(REG_DL), Yrb},
    85  		{regAddr(REG_DH), Yrb},
    86  		{regAddr(REG_BH), Yrb},
    87  		{regAddr(REG_CL), Ycl},
    88  		{regAddr(REG_CX), Ycx},
    89  		{regAddr(REG_DX), Yrx},
    90  		{regAddr(REG_BX), Yrx},
    91  		{regAddr(REG_F0), Yf0},
    92  		{regAddr(REG_F3), Yrf},
    93  		{regAddr(REG_F7), Yrf},
    94  		{regAddr(REG_M0), Ymr},
    95  		{regAddr(REG_M3), Ymr},
    96  		{regAddr(REG_M7), Ymr},
    97  		{regAddr(REG_X0), Yxr0},
    98  		{regAddr(REG_X6), Yxr},
    99  		{regAddr(REG_X13), Yxr},
   100  		{regAddr(REG_X20), YxrEvex},
   101  		{regAddr(REG_X31), YxrEvex},
   102  		{regAddr(REG_Y0), Yyr},
   103  		{regAddr(REG_Y6), Yyr},
   104  		{regAddr(REG_Y13), Yyr},
   105  		{regAddr(REG_Y20), YyrEvex},
   106  		{regAddr(REG_Y31), YyrEvex},
   107  		{regAddr(REG_Z0), Yzr},
   108  		{regAddr(REG_Z6), Yzr},
   109  		{regAddr(REG_K0), Yk0},
   110  		{regAddr(REG_K5), Yknot0},
   111  		{regAddr(REG_K7), Yknot0},
   112  		{regAddr(REG_CS), Ycs},
   113  		{regAddr(REG_SS), Yss},
   114  		{regAddr(REG_DS), Yds},
   115  		{regAddr(REG_ES), Yes},
   116  		{regAddr(REG_FS), Yfs},
   117  		{regAddr(REG_GS), Ygs},
   118  		{regAddr(REG_TLS), Ytls},
   119  		{regAddr(REG_GDTR), Ygdtr},
   120  		{regAddr(REG_IDTR), Yidtr},
   121  		{regAddr(REG_LDTR), Yldtr},
   122  		{regAddr(REG_MSW), Ymsw},
   123  		{regAddr(REG_TASK), Ytask},
   124  		{regAddr(REG_CR0), Ycr0},
   125  		{regAddr(REG_CR5), Ycr5},
   126  		{regAddr(REG_CR8), Ycr8},
   127  		{regAddr(REG_DR0), Ydr0},
   128  		{regAddr(REG_DR5), Ydr5},
   129  		{regAddr(REG_DR7), Ydr7},
   130  		{regAddr(REG_TR0), Ytr0},
   131  		{regAddr(REG_TR5), Ytr5},
   132  		{regAddr(REG_TR7), Ytr7},
   133  
   134  		{regListAddr(REG_X0, REG_X3), YxrEvexMulti4},
   135  		{regListAddr(REG_X4, REG_X7), YxrEvexMulti4},
   136  		{regListAddr(REG_Y0, REG_Y3), YyrEvexMulti4},
   137  		{regListAddr(REG_Y4, REG_Y7), YyrEvexMulti4},
   138  		{regListAddr(REG_Z0, REG_Z3), YzrMulti4},
   139  		{regListAddr(REG_Z4, REG_Z7), YzrMulti4},
   140  
   141  		{memAddr(REG_AL, REG_NONE), Ym},
   142  		{memAddr(REG_AL, REG_SI), Ym},
   143  		{memAddr(REG_SI, REG_CX), Ym},
   144  		{memAddr(REG_DI, REG_X0), Yxvm},
   145  		{memAddr(REG_DI, REG_X7), Yxvm},
   146  		{memAddr(REG_DI, REG_Y0), Yyvm},
   147  		{memAddr(REG_DI, REG_Y7), Yyvm},
   148  		{memAddr(REG_DI, REG_Z0), Yzvm},
   149  		{memAddr(REG_DI, REG_Z7), Yzvm},
   150  	}
   151  
   152  	oclassTestsAMD64 = []*oclassTest{
   153  		{immAddr(-200), Ys32},
   154  		{immAddr(500), Ys32},
   155  		{immAddr(0x7FFFFFFF), Ys32},
   156  		{immAddr(0x7FFFFFFF + 1), Yi32},
   157  		{immAddr(0xFFFFFFFF), Yi32},
   158  		{immAddr(0xFFFFFFFF + 1), Yi64},
   159  
   160  		{regAddr(REG_BPB), Yrb},
   161  		{regAddr(REG_SIB), Yrb},
   162  		{regAddr(REG_DIB), Yrb},
   163  		{regAddr(REG_R8B), Yrb},
   164  		{regAddr(REG_R12B), Yrb},
   165  		{regAddr(REG_R8), Yrl},
   166  		{regAddr(REG_R13), Yrl},
   167  		{regAddr(REG_R15), Yrl},
   168  		{regAddr(REG_SP), Yrl},
   169  		{regAddr(REG_SI), Yrl},
   170  		{regAddr(REG_DI), Yrl},
   171  		{regAddr(REG_Z13), Yzr},
   172  		{regAddr(REG_Z20), Yzr},
   173  		{regAddr(REG_Z31), Yzr},
   174  
   175  		{regListAddr(REG_X10, REG_X13), YxrEvexMulti4},
   176  		{regListAddr(REG_X24, REG_X27), YxrEvexMulti4},
   177  		{regListAddr(REG_Y10, REG_Y13), YyrEvexMulti4},
   178  		{regListAddr(REG_Y24, REG_Y27), YyrEvexMulti4},
   179  		{regListAddr(REG_Z10, REG_Z13), YzrMulti4},
   180  		{regListAddr(REG_Z24, REG_Z27), YzrMulti4},
   181  
   182  		{memAddr(REG_DI, REG_X20), YxvmEvex},
   183  		{memAddr(REG_DI, REG_X27), YxvmEvex},
   184  		{memAddr(REG_DI, REG_Y20), YyvmEvex},
   185  		{memAddr(REG_DI, REG_Y27), YyvmEvex},
   186  		{memAddr(REG_DI, REG_Z20), Yzvm},
   187  		{memAddr(REG_DI, REG_Z27), Yzvm},
   188  	}
   189  
   190  	oclassTests386 = []*oclassTest{
   191  		{&obj.Addr{Type: obj.TYPE_ADDR, Name: obj.NAME_EXTERN, Sym: &obj.LSym{}}, Yi32},
   192  
   193  		{immAddr(-200), Yi32},
   194  
   195  		{regAddr(REG_SP), Yrl32},
   196  		{regAddr(REG_SI), Yrl32},
   197  		{regAddr(REG_DI), Yrl32},
   198  	}
   199  
   200  	// Add tests that are arch-independent for all sets.
   201  	oclassTestsAMD64 = append(oclassTestsAMD64, oclassTestsCommon...)
   202  	oclassTests386 = append(oclassTests386, oclassTestsCommon...)
   203  }
   204  
   205  func TestOclass(t *testing.T) {
   206  	runTest := func(t *testing.T, ctxt *obj.Link, tests []*oclassTest) {
   207  		var p obj.Prog
   208  		for _, test := range tests {
   209  			have := oclass(ctxt, &p, test.arg)
   210  			if have != test.want {
   211  				t.Errorf("oclass(%q):\nhave: %d\nwant: %d",
   212  					obj.Dconv(&p, test.arg), have, test.want)
   213  			}
   214  		}
   215  	}
   216  
   217  	// TODO(quasilyte): test edge cases for Hsolaris, etc?
   218  
   219  	t.Run("linux/AMD64", func(t *testing.T) {
   220  		ctxtAMD64 := obj.Linknew(&Linkamd64)
   221  		ctxtAMD64.Headtype = objabi.Hlinux // See #32028
   222  		runTest(t, ctxtAMD64, oclassTestsAMD64)
   223  	})
   224  
   225  	t.Run("linux/386", func(t *testing.T) {
   226  		ctxt386 := obj.Linknew(&Link386)
   227  		ctxt386.Headtype = objabi.Hlinux // See #32028
   228  		runTest(t, ctxt386, oclassTests386)
   229  	})
   230  }
   231  
   232  func TestRegisterListEncDec(t *testing.T) {
   233  	tests := []struct {
   234  		printed string
   235  		reg0    int16
   236  		reg1    int16
   237  	}{
   238  		{"[R10-R13]", REG_R10, REG_R13},
   239  		{"[X0-AX]", REG_X0, REG_AX},
   240  
   241  		{"[X0-X3]", REG_X0, REG_X3},
   242  		{"[X21-X24]", REG_X21, REG_X24},
   243  
   244  		{"[Y0-Y3]", REG_Y0, REG_Y3},
   245  		{"[Y21-Y24]", REG_Y21, REG_Y24},
   246  
   247  		{"[Z0-Z3]", REG_Z0, REG_Z3},
   248  		{"[Z21-Z24]", REG_Z21, REG_Z24},
   249  	}
   250  
   251  	for _, test := range tests {
   252  		enc := EncodeRegisterRange(test.reg0, test.reg1)
   253  		reg0, reg1 := decodeRegisterRange(enc)
   254  
   255  		if int16(reg0) != test.reg0 {
   256  			t.Errorf("%s reg0 mismatch: have %d, want %d",
   257  				test.printed, reg0, test.reg0)
   258  		}
   259  		if int16(reg1) != test.reg1 {
   260  			t.Errorf("%s reg1 mismatch: have %d, want %d",
   261  				test.printed, reg1, test.reg1)
   262  		}
   263  		wantPrinted := test.printed
   264  		if rlconv(enc) != wantPrinted {
   265  			t.Errorf("%s string mismatch: have %s, want %s",
   266  				test.printed, rlconv(enc), wantPrinted)
   267  		}
   268  	}
   269  }
   270  
   271  func TestRegIndex(t *testing.T) {
   272  	tests := []struct {
   273  		regFrom int
   274  		regTo   int
   275  	}{
   276  		{REG_AL, REG_R15B},
   277  		{REG_AX, REG_R15},
   278  		{REG_M0, REG_M7},
   279  		{REG_K0, REG_K7},
   280  		{REG_X0, REG_X31},
   281  		{REG_Y0, REG_Y31},
   282  		{REG_Z0, REG_Z31},
   283  	}
   284  
   285  	for _, test := range tests {
   286  		for index, reg := 0, test.regFrom; reg <= test.regTo; index, reg = index+1, reg+1 {
   287  			have := regIndex(int16(reg))
   288  			want := index
   289  			if have != want {
   290  				regName := rconv(int(reg))
   291  				t.Errorf("regIndex(%s):\nhave: %d\nwant: %d",
   292  					regName, have, want)
   293  			}
   294  		}
   295  	}
   296  }
   297  
   298  // TestPCALIGN verifies the correctness of the PCALIGN by checking if the
   299  // code can be aligned to the alignment value.
   300  func TestPCALIGN(t *testing.T) {
   301  	testenv.MustHaveGoBuild(t)
   302  	dir := t.TempDir()
   303  	tmpfile := filepath.Join(dir, "test.s")
   304  	tmpout := filepath.Join(dir, "test.o")
   305  
   306  	var testCases = []struct {
   307  		name string
   308  		code string
   309  		out  string
   310  	}{
   311  		{
   312  			name: "8-byte alignment",
   313  			code: "TEXT ·foo(SB),$0-0\nMOVQ $0, AX\nPCALIGN $8\nMOVQ $1, BX\nRET\n",
   314  			out:  `0x0008\s00008\s\(.*\)\tMOVQ\t\$1,\sBX`,
   315  		},
   316  		{
   317  			name: "16-byte alignment",
   318  			code: "TEXT ·foo(SB),$0-0\nMOVQ $0, AX\nPCALIGN $16\nMOVQ $2, CX\nRET\n",
   319  			out:  `0x0010\s00016\s\(.*\)\tMOVQ\t\$2,\sCX`,
   320  		},
   321  	}
   322  
   323  	for _, test := range testCases {
   324  		if err := os.WriteFile(tmpfile, []byte(test.code), 0644); err != nil {
   325  			t.Fatal(err)
   326  		}
   327  		cmd := testenv.Command(t, testenv.GoToolPath(t), "tool", "asm", "-S", "-o", tmpout, tmpfile)
   328  		cmd.Env = append(os.Environ(), "GOARCH=amd64", "GOOS=linux")
   329  		out, err := cmd.CombinedOutput()
   330  		if err != nil {
   331  			t.Errorf("The %s build failed: %v, output: %s", test.name, err, out)
   332  			continue
   333  		}
   334  
   335  		matched, err := regexp.MatchString(test.out, string(out))
   336  		if err != nil {
   337  			t.Fatal(err)
   338  		}
   339  		if !matched {
   340  			t.Errorf("The %s testing failed!\ninput: %s\noutput: %s\n", test.name, test.code, out)
   341  		}
   342  	}
   343  }