1
2
3
4
5 package xeddata
6
7 import (
8 "bytes"
9 "encoding/json"
10 "fmt"
11 "io"
12 "io/ioutil"
13 "path"
14 "reflect"
15 "strings"
16 "testing"
17 )
18
19
20
21
22
23
24 var (
25 statesMap = map[string]string{
26 "not64": "MODE!=2",
27 "mode64": "MODE=2",
28 "mode32": "MODE=1",
29 "mode16": "MODE=0",
30 "rexw_prefix": "REXW=1 SKIP_OSZ=1",
31 "norexw_prefix": "REXW=0 SKIP_OSZ=1",
32 "W1": "REXW=1 SKIP_OSZ=1",
33 "W0": "REXW=0 SKIP_OSZ=1",
34 "VV1": "VEXVALID=1",
35 "V66": "VEX_PREFIX=1",
36 "VF2": "VEX_PREFIX=2",
37 "VF3": "VEX_PREFIX=3",
38 "V0F": "MAP=1",
39 "V0F38": "MAP=2",
40 "V0F3A": "MAP=3",
41 "VL128": "VL=0",
42 "VL256": "VL=1",
43 }
44
45 xtypesMap = map[string]*xtype{
46 "int": {name: "int", baseType: "INT", size: "0"},
47 "i8": {name: "i8", baseType: "INT", size: "8"},
48 "i64": {name: "i64", baseType: "INT", size: "64"},
49 "i32": {name: "i32", baseType: "INT", size: "32"},
50 "u8": {name: "u8", baseType: "UINT", size: "8"},
51 "f32": {name: "f32", baseType: "SIGNLE", size: "32"},
52 "f64": {name: "f64", baseType: "DOUBLE", size: "64"},
53 "var": {name: "var", baseType: "VARIABLE", size: "0"},
54 }
55
56 widthsMap = map[string]*width{
57 "q": {xtype: "i64", sizes: [3]string{"8", "8", "8"}},
58 "z": {xtype: "int", sizes: [3]string{"2", "4", "4"}},
59 "b": {xtype: "u8", sizes: [3]string{"1", "1", "1"}},
60 "d": {xtype: "i32", sizes: [3]string{"4", "4", "4"}},
61 "ps": {xtype: "f32", sizes: [3]string{"16", "16", "16"}},
62 "dq": {xtype: "i32", sizes: [3]string{"16", "16", "16"}},
63 "i32": {xtype: "i32", sizes: [3]string{"4", "4", "4"}},
64 "i64": {xtype: "i64", sizes: [3]string{"8", "8", "8"}},
65 "vv": {xtype: "var", sizes: [3]string{"0", "0", "0"}},
66 "mskw": {xtype: "i1", sizes: [3]string{"64bits", "64bits", "64bits"}},
67 "zf32": {xtype: "f32", sizes: [3]string{"512bits", "512bits", "512bits"}},
68 "zf64": {xtype: "f64", sizes: [3]string{"512bits", "512bits", "512bits"}},
69 "mem80real": {xtype: "f80", sizes: [3]string{"10", "10", "10"}},
70 "mfpxenv": {xtype: "struct", sizes: [3]string{"512", "512", "512"}},
71 }
72 )
73
74
75
76 func newStatesSource() io.Reader {
77 var buf bytes.Buffer
78 i := 0
79 for k, v := range statesMap {
80 buf.WriteString("# Line comment\n")
81 buf.WriteString("#\n\n\n")
82 fmt.Fprintf(&buf, "\t%-20s%s", k, v)
83 if i%3 == 0 {
84 buf.WriteString("\t# Trailing comment")
85 }
86 buf.WriteByte('\n')
87 i++
88 }
89
90 return &buf
91 }
92
93
94
95 func newWidthsSource() io.Reader {
96 var buf bytes.Buffer
97 i := 0
98 for name, width := range widthsMap {
99 buf.WriteString("# Line comment\n")
100 buf.WriteString("#\n\n\n")
101 eqSizes := width.sizes[0] == width.sizes[1] &&
102 width.sizes[0] == width.sizes[2]
103 if i%2 == 0 && eqSizes {
104 fmt.Fprintf(&buf, "\t%-16s%-12s%-8s",
105 name, width.xtype, width.sizes[0])
106 } else {
107 fmt.Fprintf(&buf, "\t%-16s%-12s%-8s%-8s%-8s",
108 name, width.xtype,
109 width.sizes[0], width.sizes[1], width.sizes[2])
110 }
111 if i%3 == 0 {
112 buf.WriteString("\t# Trailing comment")
113 }
114 buf.WriteByte('\n')
115 i++
116 }
117
118 return &buf
119 }
120
121
122
123 func newXtypesSource() io.Reader {
124 var buf bytes.Buffer
125 i := 0
126 for _, v := range xtypesMap {
127 buf.WriteString("# Line comment\n")
128 buf.WriteString("#\n\n\n")
129
130 fmt.Fprintf(&buf, "\t%s %s %s",
131 v.name, v.baseType, v.size)
132
133 if i%3 == 0 {
134 buf.WriteString("\t# Trailing comment")
135 }
136 buf.WriteByte('\n')
137 i++
138 }
139
140 return &buf
141 }
142
143 func newTestDatabase(t *testing.T) *Database {
144 var db Database
145 err := db.LoadStates(newStatesSource())
146 if err != nil {
147 t.Fatal(err)
148 }
149 err = db.LoadWidths(newWidthsSource())
150 if err != nil {
151 t.Fatal(err)
152 }
153 err = db.LoadXtypes(newXtypesSource())
154 if err != nil {
155 t.Fatal(err)
156 }
157 return &db
158 }
159
160 func TestContainsWord(t *testing.T) {
161 tests := []struct {
162 attrs string
163 attrName string
164 output bool
165 }{
166 {"ATT1", "ATT1", true},
167 {" ATT1", "ATT1", true},
168 {"ATT1 ", "ATT1", true},
169 {" ATT1 ", "ATT1", true},
170 {"ATT1 ATT2 ATT3", "ATT1", true},
171 {"ATT1 ATT2 ATT3", "ATT2", true},
172 {"ATT1 ATT2 ATT3", "ATT2", true},
173 {"ATT1 ATT2 ATT3", "ATT4", false},
174 {"ATT1ATT1", "ATT1", false},
175 {".ATT1", "ATT1", false},
176 {".ATT1.", "ATT1", false},
177 {"ATT1.", "ATT1", false},
178 {"", "ATT1", false},
179 {"AT", "ATT1", false},
180 {"ATT 1", "ATT1", false},
181 {" ATT1 ", "TT", false},
182 {" ATT1 ", "T1", false},
183 {" ATT1 ", "AT", false},
184 }
185
186 for _, test := range tests {
187 output := containsWord(test.attrs, test.attrName)
188 if output != test.output {
189 t.Errorf("containsWord(%q, %q)):\nhave: %v\nwant: %v",
190 test.attrs, test.attrName, output, test.output)
191 }
192 }
193 }
194
195 func TestParseWidths(t *testing.T) {
196 have, err := parseWidths(newWidthsSource())
197 if err != nil {
198 t.Fatal(err)
199 }
200 for k := range widthsMap {
201 if have[k] == nil {
202 t.Fatalf("missing key %s", k)
203 }
204 if *have[k] != *widthsMap[k] {
205 t.Fatalf("key %s:\nhave: %#v\nwant: %#v",
206 k, have[k], widthsMap[k])
207 }
208 }
209 if !reflect.DeepEqual(have, widthsMap) {
210 t.Errorf("widths output mismatch:\nhave: %#v\nwant: %#v",
211 have, widthsMap)
212 }
213 }
214
215 func TestParseStates(t *testing.T) {
216 have, err := parseStates(newStatesSource())
217 if err != nil {
218 t.Fatal(err)
219 }
220 want := statesMap
221 if !reflect.DeepEqual(have, want) {
222 t.Errorf("states output mismatch:\nhave: %v\nwant: %v", have, want)
223 }
224 }
225
226 func TestParseXtypes(t *testing.T) {
227 have, err := parseXtypes(newXtypesSource())
228 if err != nil {
229 t.Fatal(err)
230 }
231 for k := range xtypesMap {
232 if have[k] == nil {
233 t.Fatalf("missing key %s", k)
234 }
235 if *have[k] != *xtypesMap[k] {
236 t.Fatalf("key %s:\nhave: %#v\nwant: %#v",
237 k, have[k], xtypesMap[k])
238 }
239 }
240 if !reflect.DeepEqual(have, xtypesMap) {
241 t.Fatalf("xtype maps are not equal")
242 }
243 }
244
245 func TestNewOperand(t *testing.T) {
246 tests := []struct {
247 input string
248 op Operand
249 }{
250
251 {
252 "REG0=XMM_R():r",
253 Operand{Name: "REG0=XMM_R()", Action: "r"},
254 },
255 {
256 "REG0=XMM_R:w",
257 Operand{Name: "REG0=XMM_R", Action: "w"},
258 },
259 {
260 "MEM0:rw:q",
261 Operand{Name: "MEM0", Action: "rw", Width: "q"},
262 },
263 {
264 "REG0=XMM_R():rcw:ps:f32",
265 Operand{Name: "REG0=XMM_R()", Action: "rcw", Width: "ps", Xtype: "f32"},
266 },
267 {
268 "IMM0:r:z",
269 Operand{Name: "IMM0", Action: "r", Width: "z"},
270 },
271 {
272 "IMM1:cw:b:i8",
273 Operand{Name: "IMM1", Action: "cw", Width: "b", Xtype: "i8"},
274 },
275
276
277 {
278 "REG2:r:EXPL",
279 Operand{Name: "REG2", Action: "r", Visibility: VisExplicit},
280 },
281 {
282 "MEM1:w:d:IMPL",
283 Operand{Name: "MEM1", Action: "w", Width: "d", Visibility: VisImplicit},
284 },
285 {
286 "MEM1:w:IMPL:d",
287 Operand{Name: "MEM1", Action: "w", Width: "d", Visibility: VisImplicit},
288 },
289 {
290 "MEM1:w:d:SUPP:i32",
291 Operand{Name: "MEM1", Action: "w", Width: "d", Visibility: VisSuppressed, Xtype: "i32"},
292 },
293 {
294 "MEM1:w:SUPP:d:i32",
295 Operand{Name: "MEM1", Action: "w", Width: "d", Visibility: VisSuppressed, Xtype: "i32"},
296 },
297
298
299 {
300 "REG0=XMM_R():w:dq:i64",
301 Operand{Name: "REG0=XMM_R()", Action: "w", Width: "dq", Xtype: "i64"},
302 },
303
304
305 {
306 "REG1=MASK1():r:mskw:TXT=ZEROSTR",
307 Operand{Name: "REG1=MASK1()", Action: "r", Width: "mskw",
308 Attributes: map[string]bool{"TXT=ZEROSTR": true}},
309 },
310 {
311 "MEM0:r:vv:f64:TXT=BCASTSTR",
312 Operand{Name: "MEM0", Action: "r", Width: "vv", Xtype: "f64",
313 Attributes: map[string]bool{"TXT=BCASTSTR": true}},
314 },
315 {
316 "REG0=ZMM_R3():w:zf32:TXT=SAESTR",
317 Operand{Name: "REG0=ZMM_R3()", Action: "w", Width: "zf32",
318 Attributes: map[string]bool{"TXT=SAESTR": true}},
319 },
320 {
321 "REG0=ZMM_R3():w:zf64:TXT=ROUNDC",
322 Operand{Name: "REG0=ZMM_R3()", Action: "w", Width: "zf64",
323 Attributes: map[string]bool{"TXT=ROUNDC": true}},
324 },
325
326
327 {
328 "REG2=ZMM_N3():r:zf32:MULTISOURCE4",
329 Operand{Name: "REG2=ZMM_N3()", Action: "r", Width: "zf32",
330 Attributes: map[string]bool{"MULTISOURCE4": true}},
331 },
332
333
334 {
335 "REG2=ZMM_N3():r:zf32:MULTISOURCE4:TXT=SAESTR",
336 Operand{Name: "REG2=ZMM_N3()", Action: "r", Width: "zf32",
337 Attributes: map[string]bool{"MULTISOURCE4": true, "TXT=SAESTR": true}},
338 },
339 }
340
341 db := newTestDatabase(t)
342 for _, test := range tests {
343 op, err := NewOperand(db, test.input)
344 if err != nil {
345 t.Fatal(err)
346 }
347 if !reflect.DeepEqual(*op, test.op) {
348 t.Errorf("parse(`%s`): output mismatch\nhave: %#v\nwant: %#v",
349 test.input, op, test.op,
350 )
351 }
352 }
353 }
354
355 func TestReader(t *testing.T) {
356 type test struct {
357 name string
358 input string
359 output string
360 }
361
362 var tests []test
363 {
364 b, err := ioutil.ReadFile(path.Join("testdata", "xed_objects.txt"))
365 if err != nil {
366 t.Fatal(err)
367 }
368 cases := strings.Split(string(b), "------")[1:]
369 for _, c := range cases {
370 name := c[:strings.Index(c, "\n")]
371 parts := strings.Split(c[len(name):], "====")
372
373 tests = append(tests, test{
374 name: strings.TrimSpace(name),
375 input: strings.TrimSpace(parts[0]),
376 output: strings.TrimSpace(parts[1]),
377 })
378 }
379 }
380
381 for _, test := range tests {
382 r := NewReader(strings.NewReader(test.input))
383 objects, err := r.ReadAll()
384 if strings.Contains(test.name, "INVALID") {
385 if err == nil {
386 t.Errorf("%s: expected non-nil error", test.name)
387 continue
388 }
389 if err.Error() != test.output {
390 t.Errorf("%s: error mismatch\nhave: `%s`\nwant: `%s`\n",
391 test.name, err.Error(), test.output)
392 }
393 t.Logf("PASS: %s", test.name)
394 continue
395 }
396 if err != nil {
397 t.Fatal(err)
398 }
399
400 var have []map[string]string
401 for _, o := range objects {
402 for _, inst := range o.Insts {
403 var result map[string]string
404 err := json.Unmarshal([]byte(inst.String()), &result)
405 if err != nil {
406 t.Fatal(err)
407 }
408 have = append(have, result)
409 }
410 }
411 var want []map[string]string
412 err = json.Unmarshal([]byte(test.output), &want)
413 if err != nil {
414 t.Fatal(err)
415 }
416 for i := range want {
417 for k := range want[i] {
418 if want[i][k] == have[i][k] {
419 continue
420 }
421
422
423 t.Errorf("%s: insts[%d].%s mismatch\nhave: `%s`\nwant: `%s`",
424 test.name, i, k, have[i][k], want[i][k])
425 }
426 }
427 if !t.Failed() {
428 t.Logf("PASS: %s", test.name)
429 }
430 }
431 }
432
433 func TestMacroExpand(t *testing.T) {
434 tests := [...]struct {
435 input string
436 output string
437 }{
438 0: {
439 "a not64 b c",
440 "a MODE!=2 b c",
441 },
442 1: {
443 "mode16 W0",
444 "MODE=0 REXW=0 SKIP_OSZ=1",
445 },
446 2: {
447 "W1 mode32",
448 "REXW=1 SKIP_OSZ=1 MODE=1",
449 },
450 3: {
451 "W1 W1",
452 "REXW=1 SKIP_OSZ=1 REXW=1 SKIP_OSZ=1",
453 },
454 4: {
455 "W1W1",
456 "W1W1",
457 },
458 5: {
459 "mode64 1 2 3 rexw_prefix",
460 "MODE=2 1 2 3 REXW=1 SKIP_OSZ=1",
461 },
462 6: {
463 "a b c",
464 "a b c",
465 },
466 7: {
467 "mode16 mode32 mode16 mode16",
468 "MODE=0 MODE=1 MODE=0 MODE=0",
469 },
470 8: {
471 "V0F38 V0FV0F V0FV0F38",
472 "MAP=2 V0FV0F V0FV0F38",
473 },
474 9: {
475 "VV1 0x2E V66 V0F38 VL128 norexw_prefix MOD[mm] MOD!=3 REG[rrr] RM[nnn] MODRM()",
476 "VEXVALID=1 0x2E VEX_PREFIX=1 MAP=2 VL=0 REXW=0 SKIP_OSZ=1 MOD[mm] MOD!=3 REG[rrr] RM[nnn] MODRM()",
477 },
478 }
479
480 db := newTestDatabase(t)
481 for id, test := range tests {
482 have := ExpandStates(db, test.input)
483 if test.output != have {
484 t.Errorf("test %d: output mismatch:\nhave: `%s`\nwant: `%s`",
485 id, have, test.output)
486 }
487 }
488 }
489
View as plain text