...

Source file src/golang.org/x/arch/x86/xeddata/database.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  	"errors"
    10  	"fmt"
    11  	"io"
    12  	"io/ioutil"
    13  	"os"
    14  	"path/filepath"
    15  	"regexp"
    16  	"strings"
    17  )
    18  
    19  // Types for XED enum-like constants.
    20  type (
    21  	// OperandSizeMode describes operand size mode (66H prefix).
    22  	OperandSizeMode int
    23  
    24  	// AddressSizeMode describes address size mode (67H prefix).
    25  	AddressSizeMode int
    26  
    27  	// CPUMode describes availability in certain CPU mode.
    28  	CPUMode int
    29  )
    30  
    31  // Possible operand size modes. XED calls it OSZ.
    32  const (
    33  	OpSize16 OperandSizeMode = iota
    34  	OpSize32
    35  	OpSize64
    36  )
    37  
    38  // Possible address size modes. XED calls it ASZ.
    39  const (
    40  	AddrSize16 AddressSizeMode = iota
    41  	AddrSize32
    42  	AddrSize64
    43  )
    44  
    45  // Possible CPU modes. XED calls it MODE.
    46  const (
    47  	Mode16 CPUMode = iota
    48  	Mode32
    49  	Mode64
    50  )
    51  
    52  var sizeStrings = [...]string{"16", "32", "64"}
    53  
    54  // sizeString maps size enumeration value to it's string representation.
    55  func sizeString(size int) string {
    56  	// Panic more gracefully than with "index out of range".
    57  	// If client code specified invalid size enumeration,
    58  	// this is programming error that should be fixed, not "handled".
    59  	if size >= len(sizeStrings) {
    60  		panic(fmt.Sprintf("illegal size value: %d", size))
    61  	}
    62  	return sizeStrings[size]
    63  }
    64  
    65  // String returns osz bit size string. Panics on illegal enumerations.
    66  func (osz OperandSizeMode) String() string { return sizeString(int(osz)) }
    67  
    68  // String returns asz bit size string. Panics on illegal enumerations.
    69  func (asz AddressSizeMode) String() string { return sizeString(int(asz)) }
    70  
    71  // Database holds information that is required to
    72  // properly handle XED datafiles.
    73  type Database struct {
    74  	widths map[string]*width // all-widths.txt
    75  	states map[string]string // all-state.txt
    76  	xtypes map[string]*xtype // all-element-types.txt
    77  }
    78  
    79  // width is a "all-width.txt" record.
    80  type width struct {
    81  	// Default xtype name (examples: int, i8, f32).
    82  	xtype string
    83  
    84  	// 16, 32 and 64 bit sizes (all may have same value).
    85  	sizes [3]string
    86  }
    87  
    88  // xtype is a "all-element-type.txt" record.
    89  type xtype struct {
    90  	// Name is xtype identifier.
    91  	name string
    92  
    93  	// baseType specifies xtype base type.
    94  	// See "all-element-type-base.txt".
    95  	baseType string
    96  
    97  	// Size is an operand data size in bits.
    98  	size string
    99  }
   100  
   101  // NewDatabase returns Database that loads everything
   102  // it can find in xedPath.
   103  // Missing lookup file is not an error, but error during
   104  // parsing of found file is.
   105  //
   106  // Lookup:
   107  //
   108  //	"$xedPath/all-state.txt" => db.LoadStates()
   109  //	"$xedPath/all-widths.txt" => db.LoadWidths()
   110  //	"$xedPath/all-element-types.txt" => db.LoadXtypes()
   111  //
   112  // $xedPath is the interpolated value of function argument.
   113  //
   114  // The call NewDatabase("") is valid and returns empty database.
   115  // Load methods can be used to read lookup files one-by-one.
   116  func NewDatabase(xedPath string) (*Database, error) {
   117  	var db Database
   118  
   119  	stat, err := os.Stat(xedPath)
   120  	if err != nil {
   121  		return nil, err
   122  	}
   123  	if !stat.IsDir() {
   124  		return nil, errors.New("xedPath is not directory")
   125  	}
   126  
   127  	states, err := os.Open(filepath.Join(xedPath, "all-state.txt"))
   128  	if err == nil {
   129  		err = db.LoadStates(states)
   130  		if err != nil {
   131  			return &db, err
   132  		}
   133  	}
   134  
   135  	widths, err := os.Open(filepath.Join(xedPath, "all-widths.txt"))
   136  	if err == nil {
   137  		err = db.LoadWidths(widths)
   138  		if err != nil {
   139  			return &db, err
   140  		}
   141  	}
   142  
   143  	xtypes, err := os.Open(filepath.Join(xedPath, "all-element-types.txt"))
   144  	if err == nil {
   145  		err = db.LoadXtypes(xtypes)
   146  		if err != nil {
   147  			return &db, err
   148  		}
   149  	}
   150  
   151  	return &db, nil
   152  }
   153  
   154  // LoadWidths reads XED widths definitions from r and updates db.
   155  // "widths" are 16/32/64 bit mode type sizes.
   156  // See "$XED/obj/dgen/all-widths.txt".
   157  func (db *Database) LoadWidths(r io.Reader) error {
   158  	var err error
   159  	db.widths, err = parseWidths(r)
   160  	return err
   161  }
   162  
   163  // LoadStates reads XED states definitions from r and updates db.
   164  // "states" are simple macro substitutions without parameters.
   165  // See "$XED/obj/dgen/all-state.txt".
   166  func (db *Database) LoadStates(r io.Reader) error {
   167  	var err error
   168  	db.states, err = parseStates(r)
   169  	return err
   170  }
   171  
   172  // LoadXtypes reads XED xtypes definitions from r and updates db.
   173  // "xtypes" are low-level XED type names.
   174  // See "$XED/obj/dgen/all-element-types.txt".
   175  // See "$XED/obj/dgen/all-element-type-base.txt".
   176  func (db *Database) LoadXtypes(r io.Reader) error {
   177  	var err error
   178  	db.xtypes, err = parseXtypes(r)
   179  	return err
   180  }
   181  
   182  // WidthSize translates width string to size string using desired
   183  // SizeMode m. For some widths output is the same for any valid value of m.
   184  func (db *Database) WidthSize(width string, m OperandSizeMode) string {
   185  	info := db.widths[width]
   186  	if info == nil {
   187  		return ""
   188  	}
   189  	return info.sizes[m]
   190  }
   191  
   192  func parseWidths(r io.Reader) (map[string]*width, error) {
   193  	data, err := ioutil.ReadAll(r)
   194  	if err != nil {
   195  		return nil, fmt.Errorf("parse widths: %v", err)
   196  	}
   197  
   198  	// Lines have two forms:
   199  	// 1. name xtype size [# comment]
   200  	// 2. name xtype size16, size32, size64 [# comment]
   201  	reLine := regexp.MustCompile(`(^\s*\w+\s+\w+\s+\w+\s+\w+\s+\w+)|(^\s*\w+\s+\w+\s+\w+)`)
   202  
   203  	widths := make(map[string]*width, 128)
   204  	for _, l := range bytes.Split(data, []byte("\n")) {
   205  		var name, xtype, size16, size32, size64 string
   206  
   207  		if m := reLine.FindSubmatch(l); m != nil {
   208  			var f [][]byte
   209  			if m[1] != nil {
   210  				f = bytes.Fields(m[1])
   211  			} else {
   212  				f = bytes.Fields(m[2])
   213  			}
   214  
   215  			name = string(f[0])
   216  			xtype = string(f[1])
   217  			if len(f) > 3 {
   218  				size16 = string(f[2])
   219  				size32 = string(f[3])
   220  				size64 = string(f[4])
   221  			} else {
   222  				size16 = string(f[2])
   223  				size32 = size16
   224  				size64 = size16
   225  			}
   226  		}
   227  		if name != "" {
   228  			widths[name] = &width{
   229  				xtype: xtype,
   230  				sizes: [3]string{size16, size32, size64},
   231  			}
   232  		}
   233  	}
   234  
   235  	return widths, nil
   236  }
   237  
   238  func parseStates(r io.Reader) (map[string]string, error) {
   239  	data, err := ioutil.ReadAll(r)
   240  	if err != nil {
   241  		return nil, fmt.Errorf("parse states: %v", err)
   242  	}
   243  
   244  	// Lines have form of "name ...replacements [# comment]".
   245  	// This regexp captures the name and everything until line end or comment.
   246  	lineRE := regexp.MustCompile(`^\s*(\w+)\s+([^#]+)`)
   247  
   248  	states := make(map[string]string, 128)
   249  	for _, l := range strings.Split(string(data), "\n") {
   250  		if m := lineRE.FindStringSubmatch(l); m != nil {
   251  			name, replacements := m[1], m[2]
   252  			states[name] = strings.TrimSpace(replacements)
   253  		}
   254  	}
   255  
   256  	return states, nil
   257  }
   258  
   259  func parseXtypes(r io.Reader) (map[string]*xtype, error) {
   260  	data, err := ioutil.ReadAll(r)
   261  	if err != nil {
   262  		return nil, fmt.Errorf("parse xtypes: %v", err)
   263  	}
   264  
   265  	// Lines have form of "name baseType size [# comment]".
   266  	lineRE := regexp.MustCompile(`^\s*(\w+)\s+(\w+)\s*(\d+)`)
   267  
   268  	xtypes := make(map[string]*xtype)
   269  	for _, l := range strings.Split(string(data), "\n") {
   270  		if m := lineRE.FindStringSubmatch(l); m != nil {
   271  			name, baseType, size := m[1], m[2], m[3]
   272  			xtypes[name] = &xtype{
   273  				name:     name,
   274  				baseType: baseType,
   275  				size:     size,
   276  			}
   277  		}
   278  	}
   279  
   280  	return xtypes, nil
   281  }
   282  

View as plain text