1
2
3
4
5 package arm64
6
7 import (
8 "fmt"
9 "regexp"
10 "sort"
11 "strconv"
12 "strings"
13
14 "golang.org/x/arch/arm64/instgen/xmlspec"
15 )
16
17 var (
18 resultInArg0Re = regexp.MustCompile(`= V\{[^}]*\}\(d\)`)
19 floatPatternRe = regexp.MustCompile(`-?(?:half|single|double)`)
20 fixedArrangementRe = regexp.MustCompile(`\.(\d+[BHSD])`)
21 arngSymbolRe = regexp.MustCompile(`\.<(T[a-z]*)>`)
22 )
23
24
25 type Instruction struct {
26 xmlspec.Instruction
27 arrangementsCache []Arrangement
28 mnemonicCache string
29 arngShape ArngShape
30 }
31
32
33
34
35
36 type BaseTypeSet int
37
38 const (
39 BaseTypeInt = 1 << iota
40 BaseTypeUint
41 BaseTypeFloat
42 )
43
44 func (t BaseTypeSet) String() string {
45 switch t {
46 case BaseTypeInt:
47 return "int"
48 case BaseTypeUint:
49 return "uint"
50 case BaseTypeFloat:
51 return "float"
52 default:
53 return ""
54 }
55 }
56
57
58 type template struct {
59 operands []Operand
60 instruction *Instruction
61 }
62
63
64 type Arrangement struct {
65 arrangement string
66 baseType string
67 elemBits int
68 bits int
69 lanes int
70 }
71
72
73 type ArngShape int
74
75 const (
76 DefaultArngs = ArngShape(iota)
77 NarrowArngs
78 LongArngs
79 WideArngs
80 UnsupportedArngs
81 )
82
83
84 func (instruction *Instruction) extractDocVar(key string) string {
85 for _, docVar := range instruction.DocVars {
86 if docVar.Key == key {
87 return docVar.Value
88 }
89 }
90 return ""
91 }
92
93
94 func (instruction *Instruction) Mnemonic() string {
95 if instruction.mnemonicCache != "" {
96 return instruction.mnemonicCache
97 }
98
99 var mnemonic string
100 if instruction.IsAlias() {
101 mnemonic = instruction.extractDocVar("alias_mnemonic")
102 } else {
103 mnemonic = instruction.extractDocVar("mnemonic")
104 }
105
106 instruction.mnemonicCache = mnemonic
107 return mnemonic
108 }
109
110
111
112 func (instruction *Instruction) Bitwise() bool {
113 brief := instruction.Brief()
114 return strings.HasPrefix(brief, "Bitwise ")
115 }
116
117
118 func (instruction *Instruction) InstrClass() string {
119 return instruction.extractDocVar("instr-class")
120 }
121
122
123
124 func (instruction *Instruction) ResultInArg0() bool {
125 mnemonic := instruction.Mnemonic()
126
127
128 if mnemonic == "TBX" {
129 return true
130 }
131
132
133 for _, PsSection := range instruction.PsSections {
134 for _, Ps := range PsSection.Ps {
135 for _, pstext := range Ps.PSText {
136 if resultInArg0Re.MatchString(pstext) {
137 return true
138 }
139 }
140 }
141 }
142 return false
143 }
144
145
146 func (instruction *Instruction) IsAlias() bool {
147 return instruction.Type == "alias"
148 }
149
150
151 func (instruction *Instruction) BaseTypeSet() BaseTypeSet {
152 mnemonic := instruction.Mnemonic()
153
154
155
156 if mnemonic == "DUP" || mnemonic == "INS" {
157 return BaseTypeInt | BaseTypeUint | BaseTypeFloat
158 }
159
160 for _, docVar := range instruction.DocVars {
161 if floatPatternRe.MatchString(docVar.Value) {
162 return BaseTypeFloat
163 }
164 }
165
166 for _, Class := range instruction.Classes.Iclass {
167 for _, docVar := range Class.DocVars {
168 if floatPatternRe.MatchString(docVar.Value) {
169 return BaseTypeFloat
170 }
171 }
172 }
173
174 return BaseTypeInt | BaseTypeUint
175 }
176
177
178
179 func (instruction *Instruction) extractUMOVArrangements() []string {
180 var arrangements []string
181
182
183 var has32Bit, has64Bit bool
184 if len(instruction.Classes.Iclass) > 0 && len(instruction.Classes.Iclass[0].Encodings) > 0 {
185 for _, Encoding := range instruction.Classes.Iclass[0].Encodings {
186 AsmTemplate := asmTemplateToString(Encoding.AsmTemplate)
187 if strings.Contains(AsmTemplate, "<Wd>") {
188 has32Bit = true
189 }
190 if strings.Contains(AsmTemplate, "<Xd>") {
191 has64Bit = true
192 }
193 }
194 }
195
196
197 if has32Bit {
198
199 arrangements = append(arrangements, "8B", "4H", "2S")
200 }
201 if has64Bit {
202
203 arrangements = append(arrangements, "16B", "8H", "4S", "2D")
204 }
205
206 return arrangements
207 }
208
209
210 func (instruction *Instruction) Arrangements() ([]Arrangement, ArngShape) {
211 if instruction.arrangementsCache != nil {
212 return instruction.arrangementsCache, instruction.arngShape
213 }
214 baseTypeSet := instruction.BaseTypeSet()
215 stringArrangements, ashape := instruction.arrangementStrings()
216 bitwise := instruction.Bitwise()
217 var arrangements []Arrangement
218 for ty := BaseTypeSet(BaseTypeInt); ty <= BaseTypeFloat; ty <<= 1 {
219 if ty&baseTypeSet == 0 {
220 continue
221 }
222 for _, arrStr := range stringArrangements {
223 elemBits, bits, lanes := parseArrangement(arrStr)
224 if elemBits == 0 {
225 continue
226 }
227 if ty == BaseTypeFloat && elemBits != 32 && elemBits != 64 {
228 continue
229 }
230 maxElemBits := elemBits
231 if bitwise {
232 maxElemBits = bits >> 1
233 }
234 l := lanes
235 for e := elemBits; e <= maxElemBits; e = e * 2 {
236 arrangements = append(arrangements, Arrangement{
237 arrangement: arrStr,
238 baseType: ty.String(),
239 elemBits: e,
240 bits: bits,
241 lanes: l,
242 })
243 l = l >> 1
244 }
245 }
246 }
247 instruction.arrangementsCache = arrangements
248 instruction.arngShape = ashape
249 return arrangements, ashape
250 }
251
252
253
254
255 func (instruction *Instruction) extractFixedArrangements() []string {
256 var arrangements []string
257
258 for _, class := range instruction.Classes.Iclass {
259 for _, encoding := range class.Encodings {
260 var templateStr string
261 for _, content := range encoding.AsmTemplate.TextA {
262 if content.Link == "sa_t" || content.Link == "sa_ta" {
263 return []string{}
264 }
265 templateStr += content.Value
266 }
267
268
269 matches := fixedArrangementRe.FindAllStringSubmatch(templateStr, -1)
270 for _, match := range matches {
271 if len(match) > 1 {
272 arrangement := match[1]
273 if arrangement != "" {
274
275 _, bits, _ := parseArrangement(arrangement)
276 if bits >= 64 {
277 arrangements = append(arrangements, arrangement)
278 }
279 }
280 }
281 }
282 }
283 }
284 return removeDuplicates(arrangements)
285 }
286
287
288
289
290
291
292
293
294
295
296
297 func (instruction *Instruction) arrangementSymbols() []string {
298 for _, class := range instruction.Classes.Iclass {
299 for _, encoding := range class.Encodings {
300 templateStr := asmTemplateToString(encoding.AsmTemplate)
301 if !strings.Contains(templateStr, ">.") {
302 continue
303 }
304
305 seen := make(map[string]bool)
306 var symbols []string
307
308
309 for _, m := range arngSymbolRe.FindAllStringSubmatch(templateStr, -1) {
310 sym := m[1]
311 if !seen[sym] {
312 seen[sym] = true
313 symbols = append(symbols, sym)
314 }
315 }
316
317
318 for _, m := range fixedArrangementRe.FindAllStringSubmatch(templateStr, -1) {
319 arr := m[1]
320 if !seen[arr] {
321 seen[arr] = true
322 symbols = append(symbols, arr)
323 }
324 }
325
326 if len(symbols) > 0 {
327 return symbols
328 }
329 }
330 }
331 return nil
332 }
333
334
335 func (instruction *Instruction) arrangementStrings() ([]string, ArngShape) {
336 var arrangements []string
337
338 mnemonic := instruction.Mnemonic()
339 ashape := DefaultArngs
340
341 switch mnemonic {
342 case "UMOV":
343 return instruction.extractUMOVArrangements(), DefaultArngs
344
345 case "INS":
346
347
348 arrangements = append(arrangements, "16B", "8H", "4S", "2D")
349 return arrangements, DefaultArngs
350 }
351
352
353
354
355 ashape = instruction.ArngShape()
356 var targetSymbol string
357 if ashape == LongArngs || ashape == NarrowArngs {
358 symbols := instruction.arrangementSymbols()
359 if len(symbols) >= 2 {
360 targetSymbol = "<" + symbols[len(symbols)-1] + ">"
361 }
362 } else if ashape == WideArngs {
363 symbols := instruction.arrangementSymbols()
364 if len(symbols) >= 2 {
365 targetSymbol = "<" + symbols[0] + ">"
366 }
367 }
368
369 nonTarget := map[string]bool{}
370 for _, Explanation := range instruction.Explanations.Explanations {
371 Definition := Explanation.Definition
372 if Definition.Table.TGroup.TBody.Row != nil {
373 isTarget := targetSymbol == "" || targetSymbol == strings.TrimSpace(Explanation.Symbol.Value)
374 for _, Row := range Definition.Table.TGroup.TBody.Row {
375 for _, Entry := range Row.Entries {
376 if Entry.Class == "symbol" {
377 v := strings.TrimSpace(Entry.Value)
378 if isTarget {
379 arrangements = append(arrangements, v)
380 } else if eb, _, _ := parseArrangement(v); eb > 0 {
381 nonTarget[v] = false
382 }
383 }
384 }
385 }
386 }
387 }
388
389 verifyNonTargetArrangements(instruction.Mnemonic(), ashape, arrangements, nonTarget)
390
391 fixedArrangements := instruction.extractFixedArrangements()
392 arrangements = append(arrangements, fixedArrangements...)
393 arrangements = removeDuplicates(arrangements)
394 return arrangements, ashape
395 }
396
397
398
399 func verifyNonTargetArrangements(mnemonic string, ashape ArngShape, target []string, nonTarget map[string]bool) {
400 if ashape == DefaultArngs || len(nonTarget) == 0 {
401 return
402 }
403
404
405 switch mnemonic {
406 case "FCVTN", "FCVTXN":
407 return
408 }
409 for _, t := range target {
410 eb, _, _ := parseArrangement(t)
411 if eb == 0 {
412 continue
413 }
414 var expectedElemBits int
415 switch ashape {
416 case LongArngs:
417 expectedElemBits = eb * 2
418 case NarrowArngs, WideArngs:
419 expectedElemBits = eb / 2
420 }
421 if expectedElemBits == 0 {
422 continue
423 }
424 for nt := range nonTarget {
425 ntEb, _, _ := parseArrangement(nt)
426 if ntEb == expectedElemBits {
427 nonTarget[nt] = true
428 }
429 }
430 }
431 var unexplained []string
432 for nt, explained := range nonTarget {
433 if !explained {
434 unexplained = append(unexplained, nt)
435 }
436 }
437 if len(unexplained) > 0 {
438 sort.Strings(unexplained)
439 panic(fmt.Sprintf("%s: non-target arrangements not explained by target: %v\ntarget: %v",
440 mnemonic, unexplained, target))
441 }
442 }
443
444
445
446 func (instruction *Instruction) regDiagramArngShape() ArngShape {
447 for _, class := range instruction.Classes.Iclass {
448 psName := class.RegDiagram.PsName
449 if strings.Contains(psName, "_L.") || strings.HasSuffix(psName, "_L") ||
450 strings.HasSuffix(psName, "_P") {
451 return LongArngs
452 }
453 if strings.Contains(psName, "_W.") || strings.HasSuffix(psName, "_W") {
454 return WideArngs
455 }
456 if strings.Contains(psName, "_N.") || strings.HasSuffix(psName, "_N") {
457 return NarrowArngs
458 }
459 }
460 switch instruction.Mnemonic() {
461 case "FCVTN":
462 return NarrowArngs
463 case "SHLL":
464 return LongArngs
465 }
466 return DefaultArngs
467 }
468
469
470
471
472 func (instruction *Instruction) ArngShape() ArngShape {
473 symbols := instruction.arrangementSymbols()
474 if symbols == nil {
475 return UnsupportedArngs
476 }
477
478 for _, s := range symbols {
479 switch s {
480 case "T", "Ta", "Tb":
481
482 case "Ts":
483
484 return UnsupportedArngs
485 default:
486
487 if !fixedArrangementRe.MatchString("." + s) {
488 panic(fmt.Sprintf("ArngShape: unknown arrangement symbol %q in %q (symbols: %v)", s, instruction.Mnemonic(), symbols))
489 }
490 }
491 }
492
493 var shape ArngShape
494 if len(symbols) == 1 {
495 if symbols[0] == "Ta" || symbols[0] == "Tb" {
496 panic(fmt.Sprintf("ArngShape: unexpected lone %q in %q (symbols: %v)", symbols[0], instruction.Mnemonic(), symbols))
497 }
498 shape = DefaultArngs
499 } else {
500 switch mnemonic := instruction.Mnemonic(); mnemonic {
501
502 case "XTN", "SQXTN", "UQXTN", "SQXTUN",
503 "ADDHN", "SUBHN", "RADDHN", "RSUBHN",
504 "SHRN", "RSHRN",
505 "SQSHRN", "SQRSHRN", "SQSHRUN", "SQRSHRUN",
506 "UQSHRN", "UQRSHRN",
507 "FCVTN", "FCVTXN":
508 shape = NarrowArngs
509
510
511 case "SXTL", "UXTL",
512 "SADDLP", "UADDLP", "SADALP", "UADALP",
513 "SMULL", "UMULL", "PMULL",
514 "SADDL", "UADDL", "SSUBL", "USUBL",
515 "SSHLL", "USHLL", "SHLL",
516 "SABAL", "UABAL", "SABDL", "UABDL",
517 "SMLAL", "UMLAL", "SMLSL", "UMLSL",
518 "SQDMLAL", "SQDMLSL", "SQDMULL",
519 "FCVTL":
520 shape = LongArngs
521
522
523 case "SADDW", "UADDW", "SSUBW", "USUBW":
524 shape = WideArngs
525
526
527 case "USDOT", "SDOT", "UDOT", "SUDOT",
528 "BFDOT", "FDOT",
529 "MLA", "MLS", "MUL", "PMUL",
530 "FCMLA", "FCADD",
531 "BFCVTN",
532 "BFMLAL", "BFMMLA",
533 "FMMLA", "SMMLA", "UMMLA", "USMMLA":
534 return UnsupportedArngs
535
536
537 case "TBL", "TBX":
538 shape = DefaultArngs
539
540 default:
541 panic(fmt.Sprintf(
542 "ArngShape: unhandled non-uniform arrangement instruction %q (symbols: %v)",
543 mnemonic, symbols))
544 }
545 }
546
547 if shape != instruction.regDiagramArngShape() {
548 panic(fmt.Sprintf("ArngShape: cross-check failed for %q (symbols: %v): %v but regDiagram says %v",
549 instruction.Mnemonic(), symbols, shape, instruction.regDiagramArngShape()))
550 }
551
552 return shape
553 }
554
555
556 func removeDuplicates(slice []string) []string {
557 keys := make(map[string]bool)
558 result := []string{}
559
560 for _, item := range slice {
561 if _, value := keys[item]; !value {
562 keys[item] = true
563 result = append(result, item)
564 }
565 }
566
567 return result
568 }
569
570
571 func parseArrangement(arrangement string) (elemBits, bits, lanes int) {
572 if len(arrangement) < 2 {
573 return 0, 0, 0
574 }
575
576 lanesStr := arrangement[:len(arrangement)-1]
577 elemType := arrangement[len(arrangement)-1:]
578
579 lanes, err := strconv.Atoi(lanesStr)
580 if err != nil {
581 return 0, 0, 0
582 }
583
584 switch elemType {
585 case "B":
586 elemBits = 8
587 case "H":
588 elemBits = 16
589 case "S":
590 elemBits = 32
591 case "D":
592 elemBits = 64
593 default:
594 return 0, 0, 0
595 }
596
597 return elemBits, elemBits * lanes, lanes
598 }
599
600
601 func asmTemplateToString(template xmlspec.AsmTemplate) string {
602 var result strings.Builder
603 for _, content := range template.TextA {
604 result.WriteString(content.Value)
605 }
606 return result.String()
607 }
608
609
610 func (instruction *Instruction) templates() []template {
611 var operands []Operand
612 AsmTemplate := ""
613 searchTemplate:
614 for _, Class := range instruction.Classes.Iclass {
615 for _, Encoding := range Class.Encodings {
616 curAsmTemplate := asmTemplateToString(Encoding.AsmTemplate)
617 if strings.Contains(curAsmTemplate, ">.") && curAsmTemplate != AsmTemplate {
618 AsmTemplate = curAsmTemplate
619 break searchTemplate
620 }
621 }
622 }
623 operands = instruction.operands(AsmTemplate)
624 return []template{{operands: operands, instruction: instruction}}
625 }
626
627
628 func (instruction *Instruction) Documentation() string {
629 documentation := instruction.Title
630 if len(instruction.Desc.Authored.Paragraphs) > 0 {
631 documentation = instruction.Desc.Authored.Paragraphs[0].Text
632 }
633 return documentation
634 }
635
636
637 func (instruction *Instruction) Brief() string {
638 if len(instruction.Desc.Brief.Para) > 0 {
639 return strings.TrimSpace(instruction.Desc.Brief.Para[0].Text)
640 }
641 return ""
642 }
643
View as plain text