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