1
2
3
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
19 type instGroup struct {
20 opcode string
21 list []*instruction
22 }
23
24
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
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
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
85
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
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
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
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
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
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
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
273
274
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]
344
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