golang.org/x/arch@v0.17.0/x86/xeddata/operand.go (about)

     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  	"errors"
     9  	"strings"
    10  )
    11  
    12  // OperandVisibility describes operand visibility in XED terms.
    13  type OperandVisibility int
    14  
    15  const (
    16  	// VisExplicit is a default operand visibility.
    17  	// Explicit operand is "real" kind of operands that
    18  	// is shown in syntax and can be specified by the programmer.
    19  	VisExplicit OperandVisibility = iota
    20  
    21  	// VisImplicit is for fixed arg (like EAX); usually shown in syntax.
    22  	VisImplicit
    23  
    24  	// VisSuppressed is like VisImplicit, but not shown in syntax.
    25  	// In some very rare exceptions, they are also shown in syntax string.
    26  	VisSuppressed
    27  
    28  	// VisEcond is encoder-only conditions. Can be ignored.
    29  	VisEcond
    30  )
    31  
    32  // Operand holds data that is encoded inside
    33  // instruction's "OPERANDS" field.
    34  //
    35  // Use NewOperand function to decode operand fields into Operand object.
    36  type Operand struct {
    37  	// Name is an ID with optional nonterminal name part.
    38  	//
    39  	// Possible values: "REG0=GPRv_B", "REG1", "MEM0", ...
    40  	//
    41  	// If nonterminal part is present, name
    42  	// can be split into LHS and RHS with NonTerminalName method.
    43  	Name string
    44  
    45  	// Action describes argument types.
    46  	//
    47  	// Possible values: "r", "w", "rw", "cr", "cw", "crw".
    48  	// Optional "c" prefix represents conditional access.
    49  	Action string
    50  
    51  	// Width descriptor. It can express simple width like "w" (word, 16bit)
    52  	// or meta-width like "v", which corresponds to {16, 32, 64} bits.
    53  	//
    54  	// The first column in all-widths.txt lists all possible widths.
    55  	//
    56  	// To deterine the size given a width string and a mode, use
    57  	// [Database.WidthSize].
    58  	//
    59  	// Possible values: "", "q", "ds", "dq", ...
    60  	// Optional.
    61  	Width string
    62  
    63  	// Xtype holds XED-specific type information.
    64  	//
    65  	// Possible values: "", "f64", "i32", ...
    66  	// Optional.
    67  	Xtype string
    68  
    69  	// Attributes serves as container for all other properties.
    70  	//
    71  	// Possible values:
    72  	//   EVEX.b context {
    73  	//     TXT=ZEROSTR  - zeroing
    74  	//     TXT=SAESTR   - suppress all exceptions
    75  	//     TXT=ROUNDC   - rounding
    76  	//     TXT=BCASTSTR - broadcasting
    77  	//   }
    78  	//   MULTISOURCE4 - 4FMA multi-register operand.
    79  	//
    80  	// Optional. For most operands, it's nil.
    81  	Attributes map[string]bool
    82  
    83  	// Visibility tells if operand is explicit, implicit or suspended.
    84  	Visibility OperandVisibility
    85  }
    86  
    87  var xedVisibilities = map[string]OperandVisibility{
    88  	"EXPL":  VisExplicit,
    89  	"IMPL":  VisImplicit,
    90  	"SUPP":  VisSuppressed,
    91  	"ECOND": VisEcond,
    92  }
    93  
    94  // NewOperand decodes operand string.
    95  //
    96  // See "$XED/pysrc/opnds.py" to learn about fields format
    97  // and valid combinations.
    98  //
    99  // Requires database with xtypes, widths, and extraWidths info.
   100  func NewOperand(db *Database, s string) (*Operand, error) {
   101  	if db.widths == nil {
   102  		return nil, errors.New("Database.widths is nil")
   103  	}
   104  	if db.extraWidths == nil {
   105  		return nil, errors.New("Database.extraWidths is nil")
   106  	}
   107  
   108  	fields := strings.Split(s, ":")
   109  	switch len(fields) {
   110  	case 0:
   111  		return nil, errors.New("empty operand fields string")
   112  	case 1:
   113  		return &Operand{Name: fields[0]}, nil
   114  	}
   115  	var op Operand
   116  
   117  	// First two fields are fixed.
   118  	op.Name = fields[0]
   119  	op.Action = fields[1]
   120  
   121  	// Optional fields.
   122  	var w string
   123  	for _, f := range fields[2:] {
   124  		if db.widths[f] != nil && w == "" {
   125  			w = f
   126  		} else if vis, ok := xedVisibilities[f]; ok {
   127  			op.Visibility = vis
   128  		} else if xtype := db.xtypes[f]; xtype != nil {
   129  			op.Xtype = f
   130  		} else {
   131  			if op.Attributes == nil {
   132  				op.Attributes = make(map[string]bool)
   133  			}
   134  			op.Attributes[f] = true
   135  		}
   136  	}
   137  
   138  	// Get default width from operand type.
   139  	if w == "" {
   140  		if op.NonterminalName() {
   141  			if strings.HasPrefix(op.NameLHS(), "REG") {
   142  				rhs := op.NameRHS()
   143  				if strings.HasPrefix(rhs, "XED_REG_") {
   144  					// Register
   145  					w = db.extraWidths[rhs]
   146  				} else if strings.HasSuffix(rhs, "()") {
   147  					// Non-terminal
   148  					w = db.extraWidths[rhs]
   149  				}
   150  			}
   151  		} else {
   152  			// Try as an immediate.
   153  			w = db.extraWidths[op.Name]
   154  		}
   155  	}
   156  
   157  	if w != "" {
   158  		op.Width = w
   159  		// If operand did not specify an xtype, get the default from the width
   160  		if op.Xtype == "" && db.widths[w] != nil {
   161  			op.Xtype = db.widths[w].xtype
   162  		}
   163  	}
   164  
   165  	return &op, nil
   166  }
   167  
   168  // NonterminalName returns true if op.Name consist
   169  // of LHS and RHS parts.
   170  //
   171  // RHS is non-terminal name lookup function expression.
   172  // Example: "REG0=GPRv()" has "GPRv()" name lookup function.
   173  func (op *Operand) NonterminalName() bool {
   174  	return strings.Contains(op.Name, "=")
   175  }
   176  
   177  // NameLHS returns left hand side part of the non-terminal name.
   178  // Example: NameLHS("REG0=GPRv()") => "REG0".
   179  func (op *Operand) NameLHS() string {
   180  	lhs, _, _ := strings.Cut(op.Name, "=")
   181  	return lhs
   182  }
   183  
   184  // NameRHS returns right hand side part of the non-terminal name.
   185  // Example: NameLHS("REG0=GPRv()") => "GPRv()".
   186  func (op *Operand) NameRHS() string {
   187  	_, rhs, _ := strings.Cut(op.Name, "=")
   188  	return rhs
   189  }
   190  
   191  // IsVisible returns true for operands that are usually
   192  // shown in syntax strings.
   193  func (op *Operand) IsVisible() bool {
   194  	return op.Visibility == VisExplicit ||
   195  		op.Visibility == VisImplicit
   196  }