1
2
3
4
5 package arm64
6
7 import (
8 "fmt"
9 "strings"
10 )
11
12
13 type OperandType int
14
15 const (
16 OperandVReg OperandType = iota
17 OperandGReg
18 OperandImm
19 OperandVElem
20 OperandList
21 )
22
23 func (t OperandType) String() string {
24 switch t {
25 case OperandVReg:
26 return "VReg"
27 case OperandGReg:
28 return "GReg"
29 case OperandImm:
30 return "Imm"
31 case OperandVElem:
32 return "VElem"
33 case OperandList:
34 return "List"
35 default:
36 return "Unknown"
37 }
38 }
39
40
41 type Operand struct {
42 Type OperandType
43 Class string
44 BaseType string
45 ElemBits int
46 Bits int
47 Lanes int
48 ImmMax int
49
50
51
52
53
54
55 Role string
56 ListNumber int
57 AsmPos int
58
59
60 }
61
62
63 type token struct {
64 text string
65 asmPos int
66 }
67
68
69 type parsedOperand struct {
70 token
71 operandType OperandType
72 isDestination bool
73 immName string
74 immMax int
75 }
76
77
78
79 func (op *Operand) instantiate(arrangement Arrangement, ashape ArngShape, vregPos int, mnemonic string) {
80 switch op.Type {
81 case OperandVReg:
82 switch {
83 case ashape == NarrowArngs && vregPos == 0:
84 op.ElemBits = arrangement.elemBits / 2
85 op.Bits = arrangement.bits
86 op.Lanes = arrangement.bits / (arrangement.elemBits / 2)
87 case ashape == LongArngs && vregPos == 0:
88 op.ElemBits = arrangement.elemBits * 2
89 op.Bits = min(arrangement.bits*2, 128)
90 op.Lanes = arrangement.bits / op.ElemBits
91 case ashape == WideArngs && vregPos == 2:
92 op.ElemBits = arrangement.elemBits / 2
93 op.Bits = arrangement.bits
94 op.Lanes = arrangement.bits / op.ElemBits
95 default:
96 op.ElemBits = arrangement.elemBits
97 op.Bits = arrangement.bits
98 op.Lanes = arrangement.lanes
99 }
100 op.BaseType = arrangement.baseType
101 case OperandImm:
102
103
104
105 if op.ImmMax == -1 {
106
107 if strings.HasSuffix(op.Role, "_i") {
108
109 op.ImmMax = arrangement.lanes - 1
110 } else if mnemonic == "EXT" {
111
112 op.ImmMax = arrangement.bits/8 - 1
113 } else if ashape == NarrowArngs {
114
115 op.ImmMax = arrangement.elemBits/2 - 1
116 } else {
117
118 op.ImmMax = arrangement.elemBits - 1
119 }
120 }
121 case OperandGReg:
122 op.BaseType = arrangement.baseType
123 if mnemonic == "UMOV" {
124
125
126
127 if arrangement.arrangement == "2D" {
128 op.Bits = 64
129 } else {
130 op.Bits = 32
131 }
132 }
133 if mnemonic == "INS" {
134 op.Bits = arrangement.elemBits
135 }
136 case OperandVElem, OperandList:
137 panic("expected this operand type to be early-lowered")
138 }
139 }
140
141
142 func (instruction *Instruction) operands(asmTemplate string) []Operand {
143 tokens := tokenizeTemplate(asmTemplate)
144 parsed := classifyTokens(tokens)
145 return buildOperandList(parsed, instruction.ResultInArg0())
146 }
147
148
149
150
151
152
153 func tokenizeTemplate(template string) []token {
154 template = stripMnemonic(template)
155 parts := strings.Split(template, ",")
156
157 var tokens []token
158 var listBuf strings.Builder
159 var inList bool
160
161 for _, part := range parts {
162 part = strings.TrimSpace(part)
163 if part == "" {
164 continue
165 }
166
167
168 if isListStart(part) {
169 inList = true
170 listBuf.WriteString(part)
171 if strings.Contains(part, "}") {
172 tokens = append(tokens, token{text: listBuf.String(), asmPos: len(tokens)})
173 listBuf.Reset()
174 inList = false
175 }
176 continue
177 }
178 if inList {
179 listBuf.WriteString(", ")
180 listBuf.WriteString(part)
181 if strings.HasSuffix(part, "}") {
182 tokens = append(tokens, token{text: listBuf.String(), asmPos: len(tokens)})
183 listBuf.Reset()
184 inList = false
185 }
186 continue
187 }
188
189
190 if shouldMergeWithPrevious(part, tokens) {
191 tokens[len(tokens)-1].text += ", " + part
192 continue
193 }
194
195 tokens = append(tokens, token{text: part, asmPos: len(tokens)})
196 }
197 return tokens
198 }
199
200
201
202 func stripMnemonic(template string) string {
203 if _, after, ok := strings.Cut(template, " "); ok {
204 return strings.TrimSpace(after)
205 }
206 return template
207 }
208
209
210
211 func isListStart(part string) bool {
212 return strings.HasPrefix(part, "{") && strings.Contains(part, "<V")
213 }
214
215
216
217 func shouldMergeWithPrevious(part string, tokens []token) bool {
218 if len(tokens) == 0 {
219 return false
220 }
221 return strings.HasPrefix(part, "{") || strings.HasSuffix(tokens[len(tokens)-1].text, "{")
222 }
223
224
225
226 func classifyTokens(tokens []token) []parsedOperand {
227 parsed := make([]parsedOperand, len(tokens))
228 for i, tok := range tokens {
229 opType, isDest, immName, immMax := analyzeOperand(tok.text)
230 parsed[i] = parsedOperand{
231 token: tok,
232 operandType: opType,
233 isDestination: isDest,
234 immName: immName,
235 immMax: immMax,
236 }
237 }
238 return parsed
239 }
240
241
242
243 func buildOperandList(parsed []parsedOperand, resultInArg0 bool) []Operand {
244 var outs, ins, imms []Operand
245 inputCount := 0
246
247 for _, p := range parsed {
248 switch p.operandType {
249 case OperandVElem:
250 idx, reg := lowerVElem(p, &inputCount)
251 imms = append(imms, idx)
252 if p.isDestination {
253 outs = append(outs, reg)
254 resultInArg0 = true
255 } else {
256 ins = append(ins, reg)
257 }
258
259 case OperandList:
260 ins = append(ins, lowerList(p, inputCount))
261 inputCount++
262
263 case OperandImm:
264 imms = append(imms, makeImm(p, inputCount))
265 inputCount++
266
267 case OperandVReg, OperandGReg:
268 op := makeReg(p, p.operandType, inputCount)
269 if p.isDestination {
270 outs = append(outs, op)
271 } else {
272 ins = append(ins, op)
273 inputCount++
274 }
275 }
276 }
277
278
279 result := append(outs, imms...)
280 if resultInArg0 && len(outs) > 0 {
281 original := outs[0]
282 original.Role = "original"
283 result = append(result, original)
284 }
285 return append(result, ins...)
286 }
287
288
289
290 func lowerVElem(p parsedOperand, inputCount *int) (idx Operand, reg Operand) {
291 if p.isDestination {
292 idx = Operand{
293 Type: OperandImm, Class: "immediate",
294 Role: "destination_i", AsmPos: 0, ListNumber: -1, ImmMax: -1,
295 }
296 reg = Operand{
297 Type: OperandVReg, Class: "vreg",
298 Role: "destination", AsmPos: 0, ListNumber: -1,
299 }
300 } else {
301 role := inputRole(*inputCount)
302 idx = Operand{
303 Type: OperandImm, Class: "immediate",
304 Role: role + "_i", AsmPos: p.token.asmPos, ListNumber: -1, ImmMax: -1,
305 }
306 reg = Operand{
307 Type: OperandVReg, Class: "vreg",
308 Role: role, AsmPos: p.token.asmPos, ListNumber: -1,
309 }
310 *inputCount++
311 }
312 return
313 }
314
315
316 func lowerList(p parsedOperand, inputCount int) Operand {
317 return Operand{
318 Type: OperandVReg, Class: "vreg",
319 Role: inputRole(inputCount), AsmPos: p.token.asmPos, ListNumber: 0,
320 }
321 }
322
323
324 func makeImm(p parsedOperand, inputCount int) Operand {
325 return Operand{
326 Type: OperandImm, Class: "immediate",
327 Role: p.immName, AsmPos: p.token.asmPos, ListNumber: -1, ImmMax: p.immMax,
328 }
329 }
330
331
332 func makeReg(p parsedOperand, opType OperandType, inputCount int) Operand {
333 class := "vreg"
334 if opType == OperandGReg {
335 class = "greg"
336 }
337 role := "destination"
338 if !p.isDestination {
339 role = inputRole(inputCount)
340 }
341 return Operand{
342 Type: opType, Class: class,
343 Role: role, AsmPos: p.token.asmPos, ListNumber: -1,
344 }
345 }
346
347
348
349 func inputRole(index int) string {
350 return fmt.Sprintf("op%d", index)
351 }
352
353
354 func extractImmediateInfo(operandStr string) (string, int) {
355 if strings.Contains(operandStr, "#<") {
356
357
358 start := strings.Index(operandStr, "#<") + 2
359 end := strings.Index(operandStr[start:], ">")
360 if end >= 0 {
361 immediateName := operandStr[start : start+end]
362 return immediateName, -1
363 }
364 return "immediate", -1
365 }
366 if strings.Contains(operandStr, "#0") {
367 return "immzero", 0
368 }
369 return "", 0
370 }
371
372
373 func detectOperandType(operandStr string) OperandType {
374 switch {
375 case strings.HasPrefix(operandStr, "<V"):
376 return OperandVReg
377 case strings.HasPrefix(operandStr, "<W") || strings.HasPrefix(operandStr, "<X") || strings.HasPrefix(operandStr, "<R"):
378 return OperandGReg
379 case strings.Contains(operandStr, "#<") || strings.Contains(operandStr, "#0"):
380 return OperandImm
381 default:
382 return OperandVReg
383 }
384 }
385
386
387 func analyzeOperand(operandStr string) (OperandType, bool, string, int) {
388 opType := detectOperandType(operandStr)
389 isDestination := strings.Contains(operandStr, "d>")
390 switch opType {
391 case OperandVReg:
392 if strings.HasPrefix(operandStr, "{") && strings.HasSuffix(operandStr, "}") {
393 return OperandList, false, "", 0
394 }
395 if strings.Contains(operandStr, "[<index") {
396 return OperandVElem, isDestination, "", 0
397 }
398 case OperandImm:
399 immediateName, immMax := extractImmediateInfo(operandStr)
400 return opType, false, immediateName, immMax
401 }
402 return opType, isDestination, "", 0
403 }
404
View as plain text