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