| // Copyright 2018 The Go Authors. All rights reserved. | |
| // Use of this source code is governed by a BSD-style | |
| // license that can be found in the LICENSE file. | |
| package x86 | |
| import ( | |
| "cmd/internal/obj" | |
| "cmd/internal/objabi" | |
| "testing" | |
| ) | |
| type oclassTest struct { | |
| arg *obj.Addr | |
| want int // Expected oclass return value for a given arg | |
| } | |
| // Filled inside init, because it's easier to do with helper functions. | |
| var ( | |
| oclassTestsAMD64 []*oclassTest | |
| oclassTests386 []*oclassTest | |
| ) | |
| func init() { | |
| // Required for tests that access any of | |
| // opindex/ycover/reg/regrex global tables. | |
| var ctxt obj.Link | |
| instinit(&ctxt) | |
| regAddr := func(reg int16) *obj.Addr { | |
| return &obj.Addr{Type: obj.TYPE_REG, Reg: reg} | |
| } | |
| immAddr := func(v int64) *obj.Addr { | |
| return &obj.Addr{Type: obj.TYPE_CONST, Offset: v} | |
| } | |
| regListAddr := func(regFrom, regTo int16) *obj.Addr { | |
| return &obj.Addr{Type: obj.TYPE_REGLIST, Offset: EncodeRegisterRange(regFrom, regTo)} | |
| } | |
| memAddr := func(base, index int16) *obj.Addr { | |
| return &obj.Addr{Type: obj.TYPE_MEM, Reg: base, Index: index} | |
| } | |
| // TODO(quasilyte): oclass doesn't return Yxxx for X/Y regs with | |
| // ID higher than 7. We don't encode such instructions, but this | |
| // behavior seems inconsistent. It should probably either | |
| // never check for arch or do it in all cases. | |
| oclassTestsCommon := []*oclassTest{ | |
| {&obj.Addr{Type: obj.TYPE_NONE}, Ynone}, | |
| {&obj.Addr{Type: obj.TYPE_BRANCH}, Ybr}, | |
| {&obj.Addr{Type: obj.TYPE_TEXTSIZE}, Ytextsize}, | |
| {&obj.Addr{Type: obj.TYPE_INDIR, Name: obj.NAME_EXTERN}, Yindir}, | |
| {&obj.Addr{Type: obj.TYPE_INDIR, Name: obj.NAME_GOTREF}, Yindir}, | |
| {&obj.Addr{Type: obj.TYPE_ADDR, Name: obj.NAME_AUTO}, Yiauto}, | |
| {&obj.Addr{Type: obj.TYPE_ADDR, Name: obj.NAME_PARAM}, Yiauto}, | |
| {&obj.Addr{Type: obj.TYPE_ADDR, Name: obj.NAME_EXTERN}, Yiauto}, | |
| {&obj.Addr{Type: obj.TYPE_ADDR, Sym: &obj.LSym{Name: "runtime.duff"}}, Yi32}, | |
| {&obj.Addr{Type: obj.TYPE_ADDR, Offset: 4}, Yu7}, | |
| {&obj.Addr{Type: obj.TYPE_ADDR, Offset: 255}, Yu8}, | |
| {immAddr(0), Yi0}, | |
| {immAddr(1), Yi1}, | |
| {immAddr(2), Yu2}, | |
| {immAddr(3), Yu2}, | |
| {immAddr(4), Yu7}, | |
| {immAddr(86), Yu7}, | |
| {immAddr(127), Yu7}, | |
| {immAddr(128), Yu8}, | |
| {immAddr(200), Yu8}, | |
| {immAddr(255), Yu8}, | |
| {immAddr(-1), Yi8}, | |
| {immAddr(-100), Yi8}, | |
| {immAddr(-128), Yi8}, | |
| {regAddr(REG_AL), Yal}, | |
| {regAddr(REG_AX), Yax}, | |
| {regAddr(REG_DL), Yrb}, | |
| {regAddr(REG_DH), Yrb}, | |
| {regAddr(REG_BH), Yrb}, | |
| {regAddr(REG_CL), Ycl}, | |
| {regAddr(REG_CX), Ycx}, | |
| {regAddr(REG_DX), Yrx}, | |
| {regAddr(REG_BX), Yrx}, | |
| {regAddr(REG_F0), Yf0}, | |
| {regAddr(REG_F3), Yrf}, | |
| {regAddr(REG_F7), Yrf}, | |
| {regAddr(REG_M0), Ymr}, | |
| {regAddr(REG_M3), Ymr}, | |
| {regAddr(REG_M7), Ymr}, | |
| {regAddr(REG_X0), Yxr0}, | |
| {regAddr(REG_X6), Yxr}, | |
| {regAddr(REG_X13), Yxr}, | |
| {regAddr(REG_X20), YxrEvex}, | |
| {regAddr(REG_X31), YxrEvex}, | |
| {regAddr(REG_Y0), Yyr}, | |
| {regAddr(REG_Y6), Yyr}, | |
| {regAddr(REG_Y13), Yyr}, | |
| {regAddr(REG_Y20), YyrEvex}, | |
| {regAddr(REG_Y31), YyrEvex}, | |
| {regAddr(REG_Z0), Yzr}, | |
| {regAddr(REG_Z6), Yzr}, | |
| {regAddr(REG_K0), Yk0}, | |
| {regAddr(REG_K5), Yknot0}, | |
| {regAddr(REG_K7), Yknot0}, | |
| {regAddr(REG_CS), Ycs}, | |
| {regAddr(REG_SS), Yss}, | |
| {regAddr(REG_DS), Yds}, | |
| {regAddr(REG_ES), Yes}, | |
| {regAddr(REG_FS), Yfs}, | |
| {regAddr(REG_GS), Ygs}, | |
| {regAddr(REG_TLS), Ytls}, | |
| {regAddr(REG_GDTR), Ygdtr}, | |
| {regAddr(REG_IDTR), Yidtr}, | |
| {regAddr(REG_LDTR), Yldtr}, | |
| {regAddr(REG_MSW), Ymsw}, | |
| {regAddr(REG_TASK), Ytask}, | |
| {regAddr(REG_CR0), Ycr0}, | |
| {regAddr(REG_CR5), Ycr5}, | |
| {regAddr(REG_CR8), Ycr8}, | |
| {regAddr(REG_DR0), Ydr0}, | |
| {regAddr(REG_DR5), Ydr5}, | |
| {regAddr(REG_DR7), Ydr7}, | |
| {regAddr(REG_TR0), Ytr0}, | |
| {regAddr(REG_TR5), Ytr5}, | |
| {regAddr(REG_TR7), Ytr7}, | |
| {regListAddr(REG_X0, REG_X3), YxrEvexMulti4}, | |
| {regListAddr(REG_X4, REG_X7), YxrEvexMulti4}, | |
| {regListAddr(REG_Y0, REG_Y3), YyrEvexMulti4}, | |
| {regListAddr(REG_Y4, REG_Y7), YyrEvexMulti4}, | |
| {regListAddr(REG_Z0, REG_Z3), YzrMulti4}, | |
| {regListAddr(REG_Z4, REG_Z7), YzrMulti4}, | |
| {memAddr(REG_AL, REG_NONE), Ym}, | |
| {memAddr(REG_AL, REG_SI), Ym}, | |
| {memAddr(REG_SI, REG_CX), Ym}, | |
| {memAddr(REG_DI, REG_X0), Yxvm}, | |
| {memAddr(REG_DI, REG_X7), Yxvm}, | |
| {memAddr(REG_DI, REG_Y0), Yyvm}, | |
| {memAddr(REG_DI, REG_Y7), Yyvm}, | |
| {memAddr(REG_DI, REG_Z0), Yzvm}, | |
| {memAddr(REG_DI, REG_Z7), Yzvm}, | |
| } | |
| oclassTestsAMD64 = []*oclassTest{ | |
| {immAddr(-200), Ys32}, | |
| {immAddr(500), Ys32}, | |
| {immAddr(0x7FFFFFFF), Ys32}, | |
| {immAddr(0x7FFFFFFF + 1), Yi32}, | |
| {immAddr(0xFFFFFFFF), Yi32}, | |
| {immAddr(0xFFFFFFFF + 1), Yi64}, | |
| {regAddr(REG_BPB), Yrb}, | |
| {regAddr(REG_SIB), Yrb}, | |
| {regAddr(REG_DIB), Yrb}, | |
| {regAddr(REG_R8B), Yrb}, | |
| {regAddr(REG_R12B), Yrb}, | |
| {regAddr(REG_R8), Yrl}, | |
| {regAddr(REG_R13), Yrl}, | |
| {regAddr(REG_R15), Yrl}, | |
| {regAddr(REG_SP), Yrl}, | |
| {regAddr(REG_SI), Yrl}, | |
| {regAddr(REG_DI), Yrl}, | |
| {regAddr(REG_Z13), Yzr}, | |
| {regAddr(REG_Z20), Yzr}, | |
| {regAddr(REG_Z31), Yzr}, | |
| {regListAddr(REG_X10, REG_X13), YxrEvexMulti4}, | |
| {regListAddr(REG_X24, REG_X27), YxrEvexMulti4}, | |
| {regListAddr(REG_Y10, REG_Y13), YyrEvexMulti4}, | |
| {regListAddr(REG_Y24, REG_Y27), YyrEvexMulti4}, | |
| {regListAddr(REG_Z10, REG_Z13), YzrMulti4}, | |
| {regListAddr(REG_Z24, REG_Z27), YzrMulti4}, | |
| {memAddr(REG_DI, REG_X20), YxvmEvex}, | |
| {memAddr(REG_DI, REG_X27), YxvmEvex}, | |
| {memAddr(REG_DI, REG_Y20), YyvmEvex}, | |
| {memAddr(REG_DI, REG_Y27), YyvmEvex}, | |
| {memAddr(REG_DI, REG_Z20), Yzvm}, | |
| {memAddr(REG_DI, REG_Z27), Yzvm}, | |
| } | |
| oclassTests386 = []*oclassTest{ | |
| {&obj.Addr{Type: obj.TYPE_ADDR, Name: obj.NAME_EXTERN, Sym: &obj.LSym{}}, Yi32}, | |
| {immAddr(-200), Yi32}, | |
| {regAddr(REG_SP), Yrl32}, | |
| {regAddr(REG_SI), Yrl32}, | |
| {regAddr(REG_DI), Yrl32}, | |
| } | |
| // Add tests that are arch-independent for all sets. | |
| oclassTestsAMD64 = append(oclassTestsAMD64, oclassTestsCommon...) | |
| oclassTests386 = append(oclassTests386, oclassTestsCommon...) | |
| } | |
| func TestOclass(t *testing.T) { | |
| runTest := func(t *testing.T, ctxt *obj.Link, tests []*oclassTest) { | |
| var p obj.Prog | |
| for _, test := range tests { | |
| have := oclass(ctxt, &p, test.arg) | |
| if have != test.want { | |
| t.Errorf("oclass(%q):\nhave: %d\nwant: %d", | |
| obj.Dconv(&p, test.arg), have, test.want) | |
| } | |
| } | |
| } | |
| // TODO(quasilyte): test edge cases for Hsolaris, etc? | |
| t.Run("linux/AMD64", func(t *testing.T) { | |
| ctxtAMD64 := obj.Linknew(&Linkamd64) | |
| ctxtAMD64.Headtype = objabi.Hlinux // See #32028 | |
| runTest(t, ctxtAMD64, oclassTestsAMD64) | |
| }) | |
| t.Run("linux/386", func(t *testing.T) { | |
| ctxt386 := obj.Linknew(&Link386) | |
| ctxt386.Headtype = objabi.Hlinux // See #32028 | |
| runTest(t, ctxt386, oclassTests386) | |
| }) | |
| } | |
| func TestRegisterListEncDec(t *testing.T) { | |
| tests := []struct { | |
| printed string | |
| reg0 int16 | |
| reg1 int16 | |
| }{ | |
| {"[R10-R13]", REG_R10, REG_R13}, | |
| {"[X0-AX]", REG_X0, REG_AX}, | |
| {"[X0-X3]", REG_X0, REG_X3}, | |
| {"[X21-X24]", REG_X21, REG_X24}, | |
| {"[Y0-Y3]", REG_Y0, REG_Y3}, | |
| {"[Y21-Y24]", REG_Y21, REG_Y24}, | |
| {"[Z0-Z3]", REG_Z0, REG_Z3}, | |
| {"[Z21-Z24]", REG_Z21, REG_Z24}, | |
| } | |
| for _, test := range tests { | |
| enc := EncodeRegisterRange(test.reg0, test.reg1) | |
| reg0, reg1 := decodeRegisterRange(enc) | |
| if int16(reg0) != test.reg0 { | |
| t.Errorf("%s reg0 mismatch: have %d, want %d", | |
| test.printed, reg0, test.reg0) | |
| } | |
| if int16(reg1) != test.reg1 { | |
| t.Errorf("%s reg1 mismatch: have %d, want %d", | |
| test.printed, reg1, test.reg1) | |
| } | |
| wantPrinted := test.printed | |
| if rlconv(enc) != wantPrinted { | |
| t.Errorf("%s string mismatch: have %s, want %s", | |
| test.printed, rlconv(enc), wantPrinted) | |
| } | |
| } | |
| } | |
| func TestRegIndex(t *testing.T) { | |
| tests := []struct { | |
| regFrom int | |
| regTo int | |
| }{ | |
| {REG_AL, REG_R15B}, | |
| {REG_AX, REG_R15}, | |
| {REG_M0, REG_M7}, | |
| {REG_K0, REG_K7}, | |
| {REG_X0, REG_X31}, | |
| {REG_Y0, REG_Y31}, | |
| {REG_Z0, REG_Z31}, | |
| } | |
| for _, test := range tests { | |
| for index, reg := 0, test.regFrom; reg <= test.regTo; index, reg = index+1, reg+1 { | |
| have := regIndex(int16(reg)) | |
| want := index | |
| if have != want { | |
| regName := rconv(int(reg)) | |
| t.Errorf("regIndex(%s):\nhave: %d\nwant: %d", | |
| regName, have, want) | |
| } | |
| } | |
| } | |
| } |