...

Source file src/golang.org/x/arch/x86/x86avxgen/main.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  	"flag"
     9  	"fmt"
    10  	"log"
    11  	"os"
    12  	"sort"
    13  	"strings"
    14  
    15  	"golang.org/x/arch/x86/xeddata"
    16  )
    17  
    18  // instGroup holds a list of instructions with same opcode.
    19  type instGroup struct {
    20  	opcode string
    21  	list   []*instruction
    22  }
    23  
    24  // context is x86avxgen program execution state.
    25  type context struct {
    26  	db *xeddata.Database
    27  
    28  	groups []*instGroup
    29  
    30  	optabs    map[string]*optab
    31  	ytabLists map[string]*ytabList
    32  
    33  	// Command line arguments:
    34  
    35  	xedPath string
    36  }
    37  
    38  func main() {
    39  	log.SetPrefix("x86avxgen: ")
    40  	log.SetFlags(log.Lshortfile)
    41  
    42  	var ctx context
    43  
    44  	runSteps(&ctx,
    45  		parseFlags,
    46  		openDatabase,
    47  		buildTables,
    48  		printTables)
    49  }
    50  
    51  func buildTables(ctx *context) {
    52  	// Order of steps is significant.
    53  	runSteps(ctx,
    54  		decodeGroups,
    55  		mergeRegMem,
    56  		addGoSuffixes,
    57  		mergeWIG,
    58  		assignZforms,
    59  		sortGroups,
    60  		generateOptabs)
    61  }
    62  
    63  func runSteps(ctx *context, steps ...func(*context)) {
    64  	for _, f := range steps {
    65  		f(ctx)
    66  	}
    67  }
    68  
    69  func parseFlags(ctx *context) {
    70  	flag.StringVar(&ctx.xedPath, "xedPath", "./xedpath",
    71  		"XED datafiles location")
    72  
    73  	flag.Parse()
    74  }
    75  
    76  func openDatabase(ctx *context) {
    77  	db, err := xeddata.NewDatabase(ctx.xedPath)
    78  	if err != nil {
    79  		log.Fatalf("open database: %v", err)
    80  	}
    81  	ctx.db = db
    82  }
    83  
    84  // mergeRegMem merges reg-only with mem-only instructions.
    85  // For example: {MOVQ reg, mem} + {MOVQ reg, reg} = {MOVQ reg, reg/mem}.
    86  func mergeRegMem(ctx *context) {
    87  	mergeKey := func(inst *instruction) string {
    88  		return strings.Join([]string{
    89  			fmt.Sprint(len(inst.args)),
    90  			inst.enc.opbyte,
    91  			inst.enc.opdigit,
    92  			inst.enc.vex.P,
    93  			inst.enc.vex.L,
    94  			inst.enc.vex.M,
    95  			inst.enc.vex.W,
    96  		}, " ")
    97  	}
    98  
    99  	for _, g := range ctx.groups {
   100  		regOnly := make(map[string]*instruction)
   101  		memOnly := make(map[string]*instruction)
   102  		list := g.list[:0]
   103  		for _, inst := range g.list {
   104  			switch {
   105  			case inst.pset.Is("RegOnly"):
   106  				regOnly[mergeKey(inst)] = inst
   107  			case inst.pset.Is("MemOnly"):
   108  				memOnly[mergeKey(inst)] = inst
   109  			default:
   110  				if len(inst.args) == 0 {
   111  					list = append(list, inst)
   112  					continue
   113  				}
   114  				log.Fatalf("%s: unexpected MOD value", inst)
   115  			}
   116  		}
   117  
   118  		for k, m := range memOnly {
   119  			r := regOnly[k]
   120  			if r != nil {
   121  				index := m.ArgIndexByZkind("reg/mem")
   122  				arg := m.args[index]
   123  				switch ytype := r.args[index].ytype; ytype {
   124  				case "Yrl":
   125  					arg.ytype = "Yml"
   126  				case "Yxr":
   127  					arg.ytype = "Yxm"
   128  				case "YxrEvex":
   129  					arg.ytype = "YxmEvex"
   130  				case "Yyr":
   131  					arg.ytype = "Yym"
   132  				case "YyrEvex":
   133  					arg.ytype = "YymEvex"
   134  				case "Yzr":
   135  					arg.ytype = "Yzm"
   136  				case "Yk":
   137  					arg.ytype = "Ykm"
   138  				default:
   139  					log.Fatalf("%s: unexpected register type: %s", r, ytype)
   140  				}
   141  				// Merge EVEX flags into m.
   142  				m.enc.evex.SAE = m.enc.evex.SAE || r.enc.evex.SAE
   143  				m.enc.evex.Rounding = m.enc.evex.Rounding || r.enc.evex.Rounding
   144  				m.enc.evex.Zeroing = m.enc.evex.Zeroing || r.enc.evex.Zeroing
   145  				delete(regOnly, k)
   146  			}
   147  			list = append(list, m)
   148  		}
   149  		for _, r := range regOnly {
   150  			list = append(list, r)
   151  		}
   152  
   153  		g.list = list
   154  	}
   155  }
   156  
   157  // mergeWIG merges [E]VEX.W0 + [E]VEX.W1 into [E]VEX.WIG.
   158  func mergeWIG(ctx *context) {
   159  	mergeKey := func(inst *instruction) string {
   160  		return strings.Join([]string{
   161  			fmt.Sprint(len(inst.args)),
   162  			inst.enc.opbyte,
   163  			inst.enc.opdigit,
   164  			inst.enc.vex.P,
   165  			inst.enc.vex.L,
   166  			inst.enc.vex.M,
   167  		}, " ")
   168  	}
   169  
   170  	for _, g := range ctx.groups {
   171  		w0map := make(map[string]*instruction)
   172  		w1map := make(map[string]*instruction)
   173  		list := g.list[:0]
   174  		for _, inst := range g.list {
   175  			switch w := inst.enc.vex.W; w {
   176  			case "evexW0", "vexW0":
   177  				w0map[mergeKey(inst)] = inst
   178  			case "evexW1", "vexW1":
   179  				w1map[mergeKey(inst)] = inst
   180  			default:
   181  				log.Fatalf("%s: unexpected vex.W: %s", inst, w)
   182  			}
   183  		}
   184  
   185  		for k, w0 := range w0map {
   186  			w1 := w1map[k]
   187  			if w1 != nil {
   188  				w0.enc.vex.W = strings.Replace(w0.enc.vex.W, "W0", "WIG", 1)
   189  				delete(w1map, k)
   190  			}
   191  			list = append(list, w0)
   192  		}
   193  		for _, w1 := range w1map {
   194  			list = append(list, w1)
   195  		}
   196  
   197  		g.list = list
   198  	}
   199  }
   200  
   201  // assignZforms initializes zform field of every instruction in ctx.
   202  func assignZforms(ctx *context) {
   203  	for _, g := range ctx.groups {
   204  		for _, inst := range g.list {
   205  			var parts []string
   206  			if inst.pset.Is("EVEX") {
   207  				parts = append(parts, "evex")
   208  			}
   209  			for _, arg := range inst.args {
   210  				parts = append(parts, arg.zkind)
   211  			}
   212  			if inst.enc.opdigit != "" {
   213  				parts = append(parts, "opdigit")
   214  			}
   215  			inst.zform = strings.Join(parts, " ")
   216  		}
   217  	}
   218  }
   219  
   220  // sortGroups sorts each instruction group by opcode as well as instructions
   221  // inside groups by special rules (see below).
   222  //
   223  // The order of instructions inside group determine ytab
   224  // elements order inside ytabList.
   225  //
   226  // We want these rules to be satisfied:
   227  //   - EVEX-encoded entries go after VEX-encoded entries.
   228  //     This way, VEX forms are selected over EVEX variants.
   229  //   - EVEX forms with SAE/RC must go before forms without them.
   230  //     This helps to avoid problems with reg-reg instructions
   231  //     that encode either of them in ModRM.R/M which causes
   232  //     ambiguity in ytabList (more than 1 ytab can match args).
   233  //     If first matching ytab has SAE/RC, problem will not occur.
   234  //   - Memory argument position affects order.
   235  //     Required to be in sync with XED encoder when there
   236  //     are multiple choices of how to encode instruction.
   237  func sortGroups(ctx *context) {
   238  	sort.SliceStable(ctx.groups, func(i, j int) bool {
   239  		return ctx.groups[i].opcode < ctx.groups[j].opcode
   240  	})
   241  
   242  	for _, g := range ctx.groups {
   243  		sortInstList(g.list)
   244  	}
   245  }
   246  
   247  func sortInstList(insts []*instruction) {
   248  	// Use strings for sorting to get reliable transitive "less".
   249  	order := make(map[*instruction]string)
   250  	for _, inst := range insts {
   251  		encTag := 'a'
   252  		if inst.pset.Is("EVEX") {
   253  			encTag = 'b'
   254  		}
   255  		memTag := 'a'
   256  		if index := inst.ArgIndexByZkind("reg/mem"); index != -1 {
   257  			memTag = 'z' - rune(index)
   258  		}
   259  		rcsaeTag := 'a'
   260  		if !(inst.enc.evex.SAE || inst.enc.evex.Rounding) {
   261  			rcsaeTag = 'b'
   262  		}
   263  		order[inst] = fmt.Sprintf("%c%c%c %s",
   264  			encTag, memTag, rcsaeTag, inst.YtypeListString())
   265  	}
   266  
   267  	sort.SliceStable(insts, func(i, j int) bool {
   268  		return order[insts[i]] < order[insts[j]]
   269  	})
   270  }
   271  
   272  // addGoSuffixes splits some groups into several groups by introducing a suffix.
   273  // For example, ANDN group becomes ANDNL and ANDNQ (ANDN becomes empty itself).
   274  // Empty groups are removed.
   275  func addGoSuffixes(ctx *context) {
   276  	var opcodeSuffixMatchers map[string][]string
   277  	{
   278  		opXY := []string{"VL=0", "X", "VL=1", "Y"}
   279  		opXYZ := []string{"VL=0", "X", "VL=1", "Y", "VL=2", "Z"}
   280  		opQ := []string{"REXW=1", "Q"}
   281  		opLQ := []string{"REXW=0", "L", "REXW=1", "Q"}
   282  
   283  		opcodeSuffixMatchers = map[string][]string{
   284  			"VCVTPD2DQ":   opXY,
   285  			"VCVTPD2PS":   opXY,
   286  			"VCVTTPD2DQ":  opXY,
   287  			"VCVTQQ2PS":   opXY,
   288  			"VCVTUQQ2PS":  opXY,
   289  			"VCVTPD2UDQ":  opXY,
   290  			"VCVTTPD2UDQ": opXY,
   291  
   292  			"VFPCLASSPD": opXYZ,
   293  			"VFPCLASSPS": opXYZ,
   294  
   295  			"VCVTSD2SI":  opQ,
   296  			"VCVTTSD2SI": opQ,
   297  			"VCVTTSS2SI": opQ,
   298  			"VCVTSS2SI":  opQ,
   299  
   300  			"VCVTSD2USI":  opLQ,
   301  			"VCVTSS2USI":  opLQ,
   302  			"VCVTTSD2USI": opLQ,
   303  			"VCVTTSS2USI": opLQ,
   304  			"VCVTUSI2SD":  opLQ,
   305  			"VCVTUSI2SS":  opLQ,
   306  			"VCVTSI2SD":   opLQ,
   307  			"VCVTSI2SS":   opLQ,
   308  			"ANDN":        opLQ,
   309  			"BEXTR":       opLQ,
   310  			"BLSI":        opLQ,
   311  			"BLSMSK":      opLQ,
   312  			"BLSR":        opLQ,
   313  			"BZHI":        opLQ,
   314  			"MULX":        opLQ,
   315  			"PDEP":        opLQ,
   316  			"PEXT":        opLQ,
   317  			"RORX":        opLQ,
   318  			"SARX":        opLQ,
   319  			"SHLX":        opLQ,
   320  			"SHRX":        opLQ,
   321  		}
   322  	}
   323  
   324  	newGroups := make(map[string][]*instruction)
   325  	for _, g := range ctx.groups {
   326  		kv := opcodeSuffixMatchers[g.opcode]
   327  		if kv == nil {
   328  			continue
   329  		}
   330  
   331  		list := g.list[:0]
   332  		for _, inst := range g.list {
   333  			newOp := inst.opcode + inst.pset.Match(kv...)
   334  			if newOp != inst.opcode {
   335  				inst.opcode = newOp
   336  				newGroups[newOp] = append(newGroups[newOp], inst)
   337  			} else {
   338  				list = append(list, inst)
   339  			}
   340  		}
   341  		g.list = list
   342  	}
   343  	groups := ctx.groups[:0] // Filled with non-empty groups
   344  	// Some groups may become empty due to opcode split.
   345  	for _, g := range ctx.groups {
   346  		if len(g.list) != 0 {
   347  			groups = append(groups, g)
   348  		}
   349  	}
   350  	for op, insts := range newGroups {
   351  		groups = append(groups, &instGroup{
   352  			opcode: op,
   353  			list:   insts,
   354  		})
   355  	}
   356  	ctx.groups = groups
   357  }
   358  
   359  func printTables(ctx *context) {
   360  	writeTables(os.Stdout, ctx)
   361  }
   362  

View as plain text