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