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