golang.org/x/arch@v0.17.0/x86/x86avxgen/generate.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 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 }