...

Source file src/golang.org/x/arch/x86/x86avxgen/decode.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  	"fmt"
     9  	"log"
    10  	"regexp"
    11  	"strings"
    12  
    13  	"golang.org/x/arch/x86/xeddata"
    14  )
    15  
    16  // encoding is decoded XED instruction pattern.
    17  type encoding struct {
    18  	// opbyte is opcode byte (one that follows [E]VEX prefix).
    19  	// It's called "opcode" in Intel manual, but we use that for
    20  	// instruction name (iclass in XED terms).
    21  	opbyte string
    22  
    23  	// opdigit is ModRM.Reg field used to encode opcode extension.
    24  	// In Intel manual, "/digit" notation is used.
    25  	opdigit string
    26  
    27  	// vex represents [E]VEX fields that are used in a first [E]VEX
    28  	// opBytes element (see prefixExpr function).
    29  	vex struct {
    30  		P string // 66/F2/F3
    31  		L string // 128/256/512
    32  		M string // 0F/0F38/0F3A
    33  		W string // W0/W1
    34  	}
    35  
    36  	// evexScale is a scaling factor used to calculate compact disp-8.
    37  	evexScale string
    38  
    39  	// evexBcstScale is like evexScale, but used during broadcasting.
    40  	// Empty for optab entries that do not have broadcasting support.
    41  	evexBcstScale string
    42  
    43  	// evex describes which features of EVEX can be used by optab entry.
    44  	// All flags are "false" for VEX-encoded insts.
    45  	evex struct {
    46  		// There is no "broadcast" flag because it's inferred
    47  		// from non-empty evexBcstScale.
    48  
    49  		SAE      bool // EVEX.b controls SAE for reg-reg insts
    50  		Rounding bool // EVEX.b + EVEX.RC (VL) control rounding for FP insts
    51  		Zeroing  bool // Instruction can use zeroing.
    52  	}
    53  }
    54  
    55  type decoder struct {
    56  	ctx   *context
    57  	insts []*instruction
    58  }
    59  
    60  // decodeGroups fills ctx.groups with decoded instruction groups.
    61  //
    62  // Reads XED objects from ctx.xedPath.
    63  func decodeGroups(ctx *context) {
    64  	d := decoder{ctx: ctx}
    65  	groups := make(map[string][]*instruction)
    66  	for _, inst := range d.DecodeAll() {
    67  		groups[inst.opcode] = append(groups[inst.opcode], inst)
    68  	}
    69  	for op, insts := range groups {
    70  		ctx.groups = append(ctx.groups, &instGroup{
    71  			opcode: op,
    72  			list:   insts,
    73  		})
    74  	}
    75  }
    76  
    77  // DecodeAll decodes every XED instruction.
    78  func (d *decoder) DecodeAll() []*instruction {
    79  	err := xeddata.WalkInsts(d.ctx.xedPath, func(inst *xeddata.Inst) {
    80  		inst.Pattern = xeddata.ExpandStates(d.ctx.db, inst.Pattern)
    81  		pset := xeddata.NewPatternSet(inst.Pattern)
    82  
    83  		opcode := inst.Iclass
    84  
    85  		switch {
    86  		case inst.HasAttribute("AMDONLY") || inst.Extension == "XOP":
    87  			return // Only VEX and EVEX are supported
    88  		case !pset.Is("VEX") && !pset.Is("EVEX"):
    89  			return // Skip non-AVX instructions
    90  		case inst.RealOpcode == "N":
    91  			return // Skip unstable instructions
    92  		}
    93  
    94  		// Expand some patterns to simplify decodePattern.
    95  		pset.Replace("FIX_ROUND_LEN128()", "VL=0")
    96  		pset.Replace("FIX_ROUND_LEN512()", "VL=2")
    97  
    98  		mask, args := d.decodeArgs(pset, inst)
    99  		d.insts = append(d.insts, &instruction{
   100  			pset:   pset,
   101  			opcode: opcode,
   102  			mask:   mask,
   103  			args:   args,
   104  			enc:    d.decodePattern(pset, inst),
   105  		})
   106  	})
   107  	if err != nil {
   108  		log.Fatalf("walk insts: %v", err)
   109  	}
   110  	return d.insts
   111  }
   112  
   113  // registerArgs maps XED argument name RHS to its decoded version.
   114  var registerArgs = map[string]argument{
   115  	"GPR32_R()":  {"Yrl", "reg"},
   116  	"GPR64_R()":  {"Yrl", "reg"},
   117  	"VGPR32_R()": {"Yrl", "reg"},
   118  	"VGPR64_R()": {"Yrl", "reg"},
   119  	"VGPR32_N()": {"Yrl", "regV"},
   120  	"VGPR64_N()": {"Yrl", "regV"},
   121  	"GPR32_B()":  {"Yrl", "reg/mem"},
   122  	"GPR64_B()":  {"Yrl", "reg/mem"},
   123  	"VGPR32_B()": {"Yrl", "reg/mem"},
   124  	"VGPR64_B()": {"Yrl", "reg/mem"},
   125  
   126  	"XMM_R()":  {"Yxr", "reg"},
   127  	"XMM_R3()": {"YxrEvex", "reg"},
   128  	"XMM_N()":  {"Yxr", "regV"},
   129  	"XMM_N3()": {"YxrEvex", "regV"},
   130  	"XMM_B()":  {"Yxr", "reg/mem"},
   131  	"XMM_B3()": {"YxrEvex", "reg/mem"},
   132  	"XMM_SE()": {"Yxr", "regIH"},
   133  
   134  	"YMM_R()":  {"Yyr", "reg"},
   135  	"YMM_R3()": {"YyrEvex", "reg"},
   136  	"YMM_N()":  {"Yyr", "regV"},
   137  	"YMM_N3()": {"YyrEvex", "regV"},
   138  	"YMM_B()":  {"Yyr", "reg/mem"},
   139  	"YMM_B3()": {"YyrEvex", "reg/mem"},
   140  	"YMM_SE()": {"Yyr", "regIH"},
   141  
   142  	"ZMM_R3()": {"Yzr", "reg"},
   143  	"ZMM_N3()": {"Yzr", "regV"},
   144  	"ZMM_B3()": {"Yzr", "reg/mem"},
   145  
   146  	"MASK_R()": {"Yk", "reg"},
   147  	"MASK_N()": {"Yk", "regV"},
   148  	"MASK_B()": {"Yk", "reg/mem"},
   149  
   150  	"MASKNOT0()": {"Yknot0", "kmask"},
   151  
   152  	// Handled specifically in "generate".
   153  	"MASK1()": {"MASK1()", "MASK1()"},
   154  }
   155  
   156  func (d *decoder) decodeArgs(pset xeddata.PatternSet, inst *xeddata.Inst) (mask *argument, args []*argument) {
   157  	for i, f := range strings.Fields(inst.Operands) {
   158  		xarg, err := xeddata.NewOperand(d.ctx.db, f)
   159  		if err != nil {
   160  			log.Fatalf("%s: args[%d]: %v", inst, i, err)
   161  		}
   162  
   163  		switch {
   164  		case xarg.Action == "":
   165  			continue // Skip meta args like EMX_BROADCAST_1TO32_8
   166  		case !xarg.IsVisible():
   167  			continue
   168  		}
   169  
   170  		arg := &argument{}
   171  		args = append(args, arg)
   172  
   173  		switch xarg.NameLHS() {
   174  		case "IMM0":
   175  			if xarg.Width != "b" {
   176  				log.Fatalf("%s: args[%d]: expected width=b, found %s", inst, i, xarg.Width)
   177  			}
   178  			if pset["IMM0SIGNED=1"] {
   179  				arg.ytype = "Yi8"
   180  			} else {
   181  				arg.ytype = "Yu8"
   182  			}
   183  			arg.zkind = "imm8"
   184  
   185  		case "REG0", "REG1", "REG2", "REG3":
   186  			rhs := xarg.NameRHS()
   187  			if rhs == "MASK1()" {
   188  				mask = arg
   189  			}
   190  			*arg = registerArgs[rhs]
   191  			if arg.ytype == "" {
   192  				log.Fatalf("%s: args[%d]: unexpected %s reg", inst, i, rhs)
   193  			}
   194  			if xarg.Attributes["MULTISOURCE4"] {
   195  				arg.ytype += "Multi4"
   196  			}
   197  
   198  		case "MEM0":
   199  			arg.ytype = pset.MatchOrDefault("Ym",
   200  				"VMODRM_XMM()", "Yxvm",
   201  				"VMODRM_YMM()", "Yyvm",
   202  				"UISA_VMODRM_XMM()", "YxvmEvex",
   203  				"UISA_VMODRM_YMM()", "YyvmEvex",
   204  				"UISA_VMODRM_ZMM()", "Yzvm",
   205  			)
   206  			arg.zkind = "reg/mem"
   207  
   208  		default:
   209  			log.Fatalf("%s: args[%d]: unexpected %s", inst, i, xarg.NameRHS())
   210  		}
   211  	}
   212  
   213  	// Reverse args.
   214  	for i := len(args)/2 - 1; i >= 0; i-- {
   215  		j := len(args) - 1 - i
   216  		args[i], args[j] = args[j], args[i]
   217  	}
   218  
   219  	return mask, args
   220  }
   221  
   222  func (d *decoder) decodePattern(pset xeddata.PatternSet, inst *xeddata.Inst) *encoding {
   223  	var enc encoding
   224  
   225  	enc.opdigit = d.findOpdigit(pset)
   226  	enc.opbyte = d.findOpbyte(pset, inst)
   227  
   228  	if strings.Contains(inst.Attributes, "DISP8_") {
   229  		enc.evexScale = d.findEVEXScale(pset)
   230  		enc.evexBcstScale = d.findEVEXBcstScale(pset, inst)
   231  	}
   232  
   233  	enc.vex.P = pset.Match(
   234  		"VEX_PREFIX=1", "66",
   235  		"VEX_PREFIX=2", "F2",
   236  		"VEX_PREFIX=3", "F3")
   237  	enc.vex.M = pset.Match(
   238  		"MAP=1", "0F",
   239  		"MAP=2", "0F38",
   240  		"MAP=3", "0F3A")
   241  	enc.vex.L = pset.MatchOrDefault("128",
   242  		"VL=0", "128",
   243  		"VL=1", "256",
   244  		"VL=2", "512")
   245  	enc.vex.W = pset.MatchOrDefault("W0",
   246  		"REXW=0", "W0",
   247  		"REXW=1", "W1")
   248  
   249  	if pset.Is("EVEX") {
   250  		enc.evex.SAE = strings.Contains(inst.Operands, "TXT=SAESTR")
   251  		enc.evex.Rounding = strings.Contains(inst.Operands, "TXT=ROUNDC")
   252  		enc.evex.Zeroing = strings.Contains(inst.Operands, "TXT=ZEROSTR")
   253  	}
   254  
   255  	// Prefix each non-empty part with vex or evex.
   256  	parts := [...]*string{
   257  		&enc.evexScale, &enc.evexBcstScale,
   258  		&enc.vex.P, &enc.vex.M, &enc.vex.L, &enc.vex.W,
   259  	}
   260  	for _, p := range parts {
   261  		if *p == "" {
   262  			continue
   263  		}
   264  		if pset.Is("EVEX") {
   265  			*p = "evex" + *p
   266  		} else {
   267  			*p = "vex" + *p
   268  		}
   269  	}
   270  
   271  	return &enc
   272  }
   273  
   274  func (d *decoder) findOpdigit(pset xeddata.PatternSet) string {
   275  	reg := pset.Index(
   276  		"REG[0b000]",
   277  		"REG[0b001]",
   278  		"REG[0b010]",
   279  		"REG[0b011]",
   280  		"REG[0b100]",
   281  		"REG[0b101]",
   282  		"REG[0b110]",
   283  		"REG[0b111]",
   284  	)
   285  	// Fixed ModRM.Reg field means that it is used for opcode extension.
   286  	if reg != -1 {
   287  		return fmt.Sprintf("0%d", reg)
   288  	}
   289  	return ""
   290  }
   291  
   292  // opbyteRE matches uint8 hex literal.
   293  var opbyteRE = regexp.MustCompile(`0x[0-9A-F]{2}`)
   294  
   295  func (d *decoder) findOpbyte(pset xeddata.PatternSet, inst *xeddata.Inst) string {
   296  	opbyte := ""
   297  	for k := range pset {
   298  		if opbyteRE.MatchString(k) {
   299  			if opbyte == "" {
   300  				opbyte = k
   301  			} else {
   302  				log.Fatalf("%s: multiple opbytes", inst)
   303  			}
   304  		}
   305  	}
   306  	return opbyte
   307  }
   308  
   309  func (d *decoder) findEVEXScale(pset xeddata.PatternSet) string {
   310  	switch {
   311  	case pset["NELEM_FULL()"], pset["NELEM_FULLMEM()"]:
   312  		return pset.Match(
   313  			"VL=0", "N16",
   314  			"VL=1", "N32",
   315  			"VL=2", "N64")
   316  	case pset["NELEM_MOVDDUP()"]:
   317  		return pset.Match(
   318  			"VL=0", "N8",
   319  			"VL=1", "N32",
   320  			"VL=2", "N64")
   321  	case pset["NELEM_HALF()"], pset["NELEM_HALFMEM()"]:
   322  		return pset.Match(
   323  			"VL=0", "N8",
   324  			"VL=1", "N16",
   325  			"VL=2", "N32")
   326  	case pset["NELEM_QUARTERMEM()"]:
   327  		return pset.Match(
   328  			"VL=0", "N4",
   329  			"VL=1", "N8",
   330  			"VL=2", "N16")
   331  	case pset["NELEM_EIGHTHMEM()"]:
   332  		return pset.Match(
   333  			"VL=0", "N2",
   334  			"VL=1", "N4",
   335  			"VL=2", "N8")
   336  	case pset["NELEM_TUPLE2()"]:
   337  		return pset.Match(
   338  			"ESIZE_32_BITS()", "N8",
   339  			"ESIZE_64_BITS()", "N16")
   340  	case pset["NELEM_TUPLE4()"]:
   341  		return pset.Match(
   342  			"ESIZE_32_BITS()", "N16",
   343  			"ESIZE_64_BITS()", "N32")
   344  	case pset["NELEM_TUPLE8()"]:
   345  		return "N32"
   346  	case pset["NELEM_MEM128()"], pset["NELEM_TUPLE1_4X()"]:
   347  		return "N16"
   348  	}
   349  
   350  	// Explicit list is required to make it possible to
   351  	// detect unhandled nonterminals for the caller.
   352  	scalars := [...]string{
   353  		"NELEM_SCALAR()",
   354  		"NELEM_GSCAT()",
   355  		"NELEM_GPR_READER()",
   356  		"NELEM_GPR_READER_BYTE()",
   357  		"NELEM_GPR_READER_WORD()",
   358  		"NELEM_GPR_WRITER_STORE()",
   359  		"NELEM_GPR_WRITER_STORE_BYTE()",
   360  		"NELEM_GPR_WRITER_STORE_WORD()",
   361  		"NELEM_GPR_WRITER_LDOP_D()",
   362  		"NELEM_GPR_WRITER_LDOP_Q()",
   363  		"NELEM_TUPLE1()",
   364  		"NELEM_TUPLE1_BYTE()",
   365  		"NELEM_TUPLE1_WORD()",
   366  	}
   367  	for _, scalar := range scalars {
   368  		if pset[scalar] {
   369  			return pset.Match(
   370  				"ESIZE_8_BITS()", "N1",
   371  				"ESIZE_16_BITS()", "N2",
   372  				"ESIZE_32_BITS()", "N4",
   373  				"ESIZE_64_BITS()", "N8")
   374  		}
   375  	}
   376  
   377  	return ""
   378  }
   379  
   380  func (d *decoder) findEVEXBcstScale(pset xeddata.PatternSet, inst *xeddata.Inst) string {
   381  	// Only FULL and HALF tuples are affected by the broadcasting.
   382  	switch {
   383  	case pset["NELEM_FULL()"]:
   384  		return pset.Match(
   385  			"ESIZE_32_BITS()", "BcstN4",
   386  			"ESIZE_64_BITS()", "BcstN8")
   387  	case pset["NELEM_HALF()"]:
   388  		return "BcstN4"
   389  	default:
   390  		if inst.HasAttribute("BROADCAST_ENABLED") {
   391  			log.Fatalf("%s: unexpected tuple for bcst", inst)
   392  		}
   393  		return ""
   394  	}
   395  }
   396  

View as plain text