// Copyright 2018 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package main import ( "bytes" "log" "strings" ) // ytab is ytabList element. type ytab struct { Zcase string Zoffset int ArgList string // Ytypes that are matched by this ytab. } // ytabList is a named set of ytab objects. // In asm6.go represented as []ytab. type ytabList struct { Name string Ytabs []ytab } // optab describes instruction encodings for specific opcode. type optab struct { Opcode string YtabList *ytabList OpLines []string } type generator struct { ctx *context ytabLists map[string]*ytabList } // generateOptabs fills ctx.optabs and ctx.ytabLists with objects created // from decoded instructions. func generateOptabs(ctx *context) { gen := generator{ctx: ctx, ytabLists: make(map[string]*ytabList)} optabs := make(map[string]*optab) for _, g := range ctx.groups { optabs[g.opcode] = gen.GenerateGroup(g) } ctx.optabs = optabs ctx.ytabLists = gen.ytabLists } // GenerateGroup converts g into optab. // Populates internal ytab list map. func (gen *generator) GenerateGroup(g *instGroup) *optab { var opLines []string for _, inst := range g.list { opLines = append(opLines, gen.generateOpLine(inst)) } return &optab{ Opcode: "A" + g.opcode, OpLines: opLines, YtabList: gen.internYtabList(g), } } // generateOpLine returns string that describes opBytes for single instruction form. func (gen *generator) generateOpLine(inst *instruction) string { parts := []string{gen.prefixExpr(inst)} if inst.pset.Is("EVEX") { parts = append(parts, gen.evexPrefixExpr(inst)) } parts = append(parts, inst.enc.opbyte) if inst.enc.opdigit != "" { parts = append(parts, inst.enc.opdigit) } return strings.Join(parts, ", ") } func (gen *generator) prefixExpr(inst *instruction) string { enc := inst.enc return gen.joinPrefixParts([]string{ // Special constant that makes AVX byte different from 0x0F, // making it unnecessary to check for both VEX+EVEX when // assigning dealing with legacy instructions that skip it // without advancing "z" counter. "avxEscape", enc.vex.L, enc.vex.P, enc.vex.M, enc.vex.W, }) } func (gen *generator) evexPrefixExpr(inst *instruction) string { enc := inst.enc parts := []string{ enc.evexScale, enc.evexBcstScale, } if enc.evex.SAE { parts = append(parts, "evexSaeEnabled") } if enc.evex.Rounding { parts = append(parts, "evexRoundingEnabled") } if enc.evex.Zeroing { parts = append(parts, "evexZeroingEnabled") } return gen.joinPrefixParts(parts) } // joinPrefixParts returns the Go OR-expression for every non-empty name. // If every name is empty, returns "0". func (gen *generator) joinPrefixParts(names []string) string { filterEmptyStrings := func(xs []string) []string { ys := xs[:0] for _, x := range xs { if x != "" { ys = append(ys, x) } } return ys } names = filterEmptyStrings(names) if len(names) == 0 { return "0" } return strings.Join(names, "|") } // internYtabList returns ytabList for given group. // // Returned ytab lists are interned. // Same ytab list can be returned for different groups. func (gen *generator) internYtabList(g *instGroup) *ytabList { var key string { var buf bytes.Buffer for _, inst := range g.list { buf.WriteString(inst.zform) buf.WriteByte('=') buf.WriteString(inst.YtypeListString()) buf.WriteByte(';') } key = buf.String() } if ylist := gen.ytabLists[key]; ylist != nil { return ylist } var ytabs []ytab for _, inst := range g.list { zoffset := 2 if inst.pset.Is("EVEX") { zoffset++ // Always at least 3 bytes } if inst.enc.opdigit != "" { zoffset++ } if inst.mask != nil { ytabs = append(ytabs, gen.makeMaskYtabs(zoffset, inst)...) } else { ytabs = append(ytabs, gen.makeYtab(zoffset, inst.zform, inst.args)) } } ylist := &ytabList{ Name: "_y" + strings.ToLower(g.opcode), Ytabs: ytabs, } gen.ytabLists[key] = ylist return ylist } var zcaseByZform = map[string]string{ "evex imm8 reg kmask reg/mem": "Zevex_i_r_k_rm", "evex imm8 reg reg/mem": "Zevex_i_r_rm", "evex imm8 reg/mem kmask reg": "Zevex_i_rm_k_r", "evex imm8 reg/mem kmask regV opdigit": "Zevex_i_rm_k_vo", "evex imm8 reg/mem reg": "Zevex_i_rm_r", "evex imm8 reg/mem regV opdigit": "Zevex_i_rm_vo", "evex imm8 reg/mem regV kmask reg": "Zevex_i_rm_v_k_r", "evex imm8 reg/mem regV reg": "Zevex_i_rm_v_r", "evex kmask reg/mem opdigit": "Zevex_k_rmo", "evex reg kmask reg/mem": "Zevex_r_k_rm", "evex reg reg/mem": "Zevex_r_v_rm", "evex reg regV kmask reg/mem": "Zevex_r_v_k_rm", "evex reg regV reg/mem": "Zevex_r_v_rm", "evex reg/mem kmask reg": "Zevex_rm_k_r", "evex reg/mem reg": "Zevex_rm_v_r", "evex reg/mem regV kmask reg": "Zevex_rm_v_k_r", "evex reg/mem regV reg": "Zevex_rm_v_r", "": "Zvex", "imm8 reg reg/mem": "Zvex_i_r_rm", "imm8 reg/mem reg": "Zvex_i_rm_r", "imm8 reg/mem regV opdigit": "Zvex_i_rm_vo", "imm8 reg/mem regV reg": "Zvex_i_rm_v_r", "reg reg/mem": "Zvex_r_v_rm", "reg regV reg/mem": "Zvex_r_v_rm", "reg/mem opdigit": "Zvex_rm_v_ro", "reg/mem reg": "Zvex_rm_v_r", "reg/mem regV opdigit": "Zvex_rm_r_vo", "reg/mem regV reg": "Zvex_rm_v_r", "reg/mem": "Zvex_rm_v_r", "regIH reg/mem regV reg": "Zvex_hr_rm_v_r", "regV reg/mem reg": "Zvex_v_rm_r", } func (gen *generator) makeYtab(zoffset int, zform string, args []*argument) ytab { var ytypes []string for _, arg := range args { if arg.ytype != "Ynone" { ytypes = append(ytypes, arg.ytype) } } argList := strings.Join(ytypes, ", ") zcase := zcaseByZform[zform] if zcase == "" { log.Fatalf("no zcase for %q", zform) } return ytab{ Zcase: zcase, Zoffset: zoffset, ArgList: argList, } } // makeMaskYtabs returns 2 ytabs created from instruction with MASK1() argument. // // This is required due to how masking is implemented in asm6. // Single MASK1() instruction produces 2 ytabs, for example: // 1. OP xmm, mem | Yxr, Yxm | Does not permit K arguments (K0 implied) // 2. OP xmm, K2, mem | Yxr, Yknot0, Yxm | Does not permit K0 argument // // This function also exploits that both ytab entries have same opbytes, // hence it is efficient to emit only one opbytes line and 0 Z-offset // for first ytab object. func (gen *generator) makeMaskYtabs(zoffset int, inst *instruction) []ytab { var k0 ytab { zform := strings.Replace(inst.zform, "MASK1() ", "", 1) inst.mask.ytype = "Ynone" k0 = gen.makeYtab(0, zform, inst.args) } var knot0 ytab { zform := strings.Replace(inst.zform, "MASK1() ", "kmask ", 1) inst.mask.ytype = "Yknot0" knot0 = gen.makeYtab(zoffset, zform, inst.args) } inst.mask.ytype = "MASK1()" // Restore Y-type return []ytab{k0, knot0} }