...

Source file src/golang.org/x/arch/x86/x86avxgen/generate.go

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

     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 main
     6  
     7  import (
     8  	"bytes"
     9  	"log"
    10  	"strings"
    11  )
    12  
    13  // ytab is ytabList element.
    14  type ytab struct {
    15  	Zcase   string
    16  	Zoffset int
    17  	ArgList string // Ytypes that are matched by this ytab.
    18  }
    19  
    20  // ytabList is a named set of ytab objects.
    21  // In asm6.go represented as []ytab.
    22  type ytabList struct {
    23  	Name  string
    24  	Ytabs []ytab
    25  }
    26  
    27  // optab describes instruction encodings for specific opcode.
    28  type optab struct {
    29  	Opcode   string
    30  	YtabList *ytabList
    31  	OpLines  []string
    32  }
    33  
    34  type generator struct {
    35  	ctx       *context
    36  	ytabLists map[string]*ytabList
    37  }
    38  
    39  // generateOptabs fills ctx.optabs and ctx.ytabLists with objects created
    40  // from decoded instructions.
    41  func generateOptabs(ctx *context) {
    42  	gen := generator{ctx: ctx, ytabLists: make(map[string]*ytabList)}
    43  	optabs := make(map[string]*optab)
    44  	for _, g := range ctx.groups {
    45  		optabs[g.opcode] = gen.GenerateGroup(g)
    46  	}
    47  	ctx.optabs = optabs
    48  	ctx.ytabLists = gen.ytabLists
    49  }
    50  
    51  // GenerateGroup converts g into optab.
    52  // Populates internal ytab list map.
    53  func (gen *generator) GenerateGroup(g *instGroup) *optab {
    54  	var opLines []string
    55  	for _, inst := range g.list {
    56  		opLines = append(opLines, gen.generateOpLine(inst))
    57  	}
    58  	return &optab{
    59  		Opcode:   "A" + g.opcode,
    60  		OpLines:  opLines,
    61  		YtabList: gen.internYtabList(g),
    62  	}
    63  }
    64  
    65  // generateOpLine returns string that describes opBytes for single instruction form.
    66  func (gen *generator) generateOpLine(inst *instruction) string {
    67  	parts := []string{gen.prefixExpr(inst)}
    68  	if inst.pset.Is("EVEX") {
    69  		parts = append(parts, gen.evexPrefixExpr(inst))
    70  	}
    71  	parts = append(parts, inst.enc.opbyte)
    72  	if inst.enc.opdigit != "" {
    73  		parts = append(parts, inst.enc.opdigit)
    74  	}
    75  	return strings.Join(parts, ", ")
    76  }
    77  
    78  func (gen *generator) prefixExpr(inst *instruction) string {
    79  	enc := inst.enc
    80  	return gen.joinPrefixParts([]string{
    81  		// Special constant that makes AVX byte different from 0x0F,
    82  		// making it unnecessary to check for both VEX+EVEX when
    83  		// assigning dealing with legacy instructions that skip it
    84  		// without advancing "z" counter.
    85  		"avxEscape",
    86  		enc.vex.L,
    87  		enc.vex.P,
    88  		enc.vex.M,
    89  		enc.vex.W,
    90  	})
    91  }
    92  
    93  func (gen *generator) evexPrefixExpr(inst *instruction) string {
    94  	enc := inst.enc
    95  	parts := []string{
    96  		enc.evexScale,
    97  		enc.evexBcstScale,
    98  	}
    99  	if enc.evex.SAE {
   100  		parts = append(parts, "evexSaeEnabled")
   101  	}
   102  	if enc.evex.Rounding {
   103  		parts = append(parts, "evexRoundingEnabled")
   104  	}
   105  	if enc.evex.Zeroing {
   106  		parts = append(parts, "evexZeroingEnabled")
   107  	}
   108  	return gen.joinPrefixParts(parts)
   109  }
   110  
   111  // joinPrefixParts returns the Go OR-expression for every non-empty name.
   112  // If every name is empty, returns "0".
   113  func (gen *generator) joinPrefixParts(names []string) string {
   114  	filterEmptyStrings := func(xs []string) []string {
   115  		ys := xs[:0]
   116  		for _, x := range xs {
   117  			if x != "" {
   118  				ys = append(ys, x)
   119  			}
   120  		}
   121  		return ys
   122  	}
   123  
   124  	names = filterEmptyStrings(names)
   125  	if len(names) == 0 {
   126  		return "0"
   127  	}
   128  	return strings.Join(names, "|")
   129  }
   130  
   131  // internYtabList returns ytabList for given group.
   132  //
   133  // Returned ytab lists are interned.
   134  // Same ytab list can be returned for different groups.
   135  func (gen *generator) internYtabList(g *instGroup) *ytabList {
   136  	var key string
   137  	{
   138  		var buf bytes.Buffer
   139  		for _, inst := range g.list {
   140  			buf.WriteString(inst.zform)
   141  			buf.WriteByte('=')
   142  			buf.WriteString(inst.YtypeListString())
   143  			buf.WriteByte(';')
   144  		}
   145  		key = buf.String()
   146  	}
   147  	if ylist := gen.ytabLists[key]; ylist != nil {
   148  		return ylist
   149  	}
   150  
   151  	var ytabs []ytab
   152  	for _, inst := range g.list {
   153  		zoffset := 2
   154  		if inst.pset.Is("EVEX") {
   155  			zoffset++ // Always at least 3 bytes
   156  		}
   157  		if inst.enc.opdigit != "" {
   158  			zoffset++
   159  		}
   160  
   161  		if inst.mask != nil {
   162  			ytabs = append(ytabs, gen.makeMaskYtabs(zoffset, inst)...)
   163  		} else {
   164  			ytabs = append(ytabs, gen.makeYtab(zoffset, inst.zform, inst.args))
   165  		}
   166  	}
   167  	ylist := &ytabList{
   168  		Name:  "_y" + strings.ToLower(g.opcode),
   169  		Ytabs: ytabs,
   170  	}
   171  	gen.ytabLists[key] = ylist
   172  	return ylist
   173  }
   174  
   175  var zcaseByZform = map[string]string{
   176  	"evex imm8 reg kmask reg/mem":          "Zevex_i_r_k_rm",
   177  	"evex imm8 reg reg/mem":                "Zevex_i_r_rm",
   178  	"evex imm8 reg/mem kmask reg":          "Zevex_i_rm_k_r",
   179  	"evex imm8 reg/mem kmask regV opdigit": "Zevex_i_rm_k_vo",
   180  	"evex imm8 reg/mem reg":                "Zevex_i_rm_r",
   181  	"evex imm8 reg/mem regV opdigit":       "Zevex_i_rm_vo",
   182  	"evex imm8 reg/mem regV kmask reg":     "Zevex_i_rm_v_k_r",
   183  	"evex imm8 reg/mem regV reg":           "Zevex_i_rm_v_r",
   184  	"evex kmask reg/mem opdigit":           "Zevex_k_rmo",
   185  	"evex reg kmask reg/mem":               "Zevex_r_k_rm",
   186  	"evex reg reg/mem":                     "Zevex_r_v_rm",
   187  	"evex reg regV kmask reg/mem":          "Zevex_r_v_k_rm",
   188  	"evex reg regV reg/mem":                "Zevex_r_v_rm",
   189  	"evex reg/mem kmask reg":               "Zevex_rm_k_r",
   190  	"evex reg/mem reg":                     "Zevex_rm_v_r",
   191  	"evex reg/mem regV kmask reg":          "Zevex_rm_v_k_r",
   192  	"evex reg/mem regV reg":                "Zevex_rm_v_r",
   193  
   194  	"":                          "Zvex",
   195  	"imm8 reg reg/mem":          "Zvex_i_r_rm",
   196  	"imm8 reg/mem reg":          "Zvex_i_rm_r",
   197  	"imm8 reg/mem regV opdigit": "Zvex_i_rm_vo",
   198  	"imm8 reg/mem regV reg":     "Zvex_i_rm_v_r",
   199  	"reg reg/mem":               "Zvex_r_v_rm",
   200  	"reg regV reg/mem":          "Zvex_r_v_rm",
   201  	"reg/mem opdigit":           "Zvex_rm_v_ro",
   202  	"reg/mem reg":               "Zvex_rm_v_r",
   203  	"reg/mem regV opdigit":      "Zvex_rm_r_vo",
   204  	"reg/mem regV reg":          "Zvex_rm_v_r",
   205  	"reg/mem":                   "Zvex_rm_v_r",
   206  	"regIH reg/mem regV reg":    "Zvex_hr_rm_v_r",
   207  	"regV reg/mem reg":          "Zvex_v_rm_r",
   208  }
   209  
   210  func (gen *generator) makeYtab(zoffset int, zform string, args []*argument) ytab {
   211  	var ytypes []string
   212  	for _, arg := range args {
   213  		if arg.ytype != "Ynone" {
   214  			ytypes = append(ytypes, arg.ytype)
   215  		}
   216  	}
   217  	argList := strings.Join(ytypes, ", ")
   218  	zcase := zcaseByZform[zform]
   219  	if zcase == "" {
   220  		log.Fatalf("no zcase for %q", zform)
   221  	}
   222  	return ytab{
   223  		Zcase:   zcase,
   224  		Zoffset: zoffset,
   225  		ArgList: argList,
   226  	}
   227  }
   228  
   229  // makeMaskYtabs returns 2 ytabs created from instruction with MASK1() argument.
   230  //
   231  // This is required due to how masking is implemented in asm6.
   232  // Single MASK1() instruction produces 2 ytabs, for example:
   233  //  1. OP xmm, mem     | Yxr, Yxm         | Does not permit K arguments (K0 implied)
   234  //  2. OP xmm, K2, mem | Yxr, Yknot0, Yxm | Does not permit K0 argument
   235  //
   236  // This function also exploits that both ytab entries have same opbytes,
   237  // hence it is efficient to emit only one opbytes line and 0 Z-offset
   238  // for first ytab object.
   239  func (gen *generator) makeMaskYtabs(zoffset int, inst *instruction) []ytab {
   240  	var k0 ytab
   241  	{
   242  		zform := strings.Replace(inst.zform, "MASK1() ", "", 1)
   243  		inst.mask.ytype = "Ynone"
   244  		k0 = gen.makeYtab(0, zform, inst.args)
   245  	}
   246  	var knot0 ytab
   247  	{
   248  		zform := strings.Replace(inst.zform, "MASK1() ", "kmask ", 1)
   249  		inst.mask.ytype = "Yknot0"
   250  		knot0 = gen.makeYtab(zoffset, zform, inst.args)
   251  	}
   252  
   253  	inst.mask.ytype = "MASK1()" // Restore Y-type
   254  	return []ytab{k0, knot0}
   255  }
   256  

View as plain text