...

Source file src/golang.org/x/arch/x86/xeddata/xeddata_test.go

Documentation: golang.org/x/arch/x86/xeddata

     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 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  // Small database to generate state/xtype/width input files and validate parse results.
    20  //
    21  // Tests should use only those symbols that are defined inside test maps.
    22  // For example, if {"foo"=>"bar"} element is not in statesMap, tests
    23  // can't expect that "foo" get's replaced by "bar".
    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  // newStatesSource returns a reader that mocks "all-state.txt" file.
    75  // Input content is generated based on statesMap.
    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  // newWidthsSource returns a reader that mocks "all-widths.txt" file.
    94  // Input content is generated based on widthsMap.
    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  // newXtypesSource returns a reader that mocks "all-element-types.txt" file.
   122  // Input content is generated based on xtypesMap.
   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  		// Simple cases.
   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  		// Optional fields and visibility.
   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  		// Ambiguity: xtypes that look like widths.
   299  		{
   300  			"REG0=XMM_R():w:dq:i64",
   301  			Operand{Name: "REG0=XMM_R()", Action: "w", Width: "dq", Xtype: "i64"},
   302  		},
   303  
   304  		// TXT=X field.
   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  		// Multi-source.
   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  		// Multi-source + EVEX.b context.
   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  				// i - index inside array of JSON objects.
   422  				// k - i'th object key (example: "Iclass").
   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