xref: /aosp_15_r20/external/mesa3d/src/freedreno/afuc/asm.c (revision 6104692788411f58d303aa86923a9ff6ecaded22)
1 /*
2  * Copyright © 2017 Rob Clark <[email protected]>
3  * SPDX-License-Identifier: MIT
4  */
5 
6 #include <assert.h>
7 #include <err.h>
8 #include <fcntl.h>
9 #include <getopt.h>
10 #include <stdarg.h>
11 #include <stdbool.h>
12 #include <stdint.h>
13 #include <stdio.h>
14 #include <stdlib.h>
15 #include <string.h>
16 #include <unistd.h>
17 
18 #include "util/macros.h"
19 #include "util/log.h"
20 #include "afuc.h"
21 #include "asm.h"
22 #include "parser.h"
23 #include "util.h"
24 
25 struct encode_state {
26 	unsigned gen;
27 };
28 
29 static afuc_opc
__instruction_case(struct encode_state * s,const struct afuc_instr * instr)30 __instruction_case(struct encode_state *s, const struct afuc_instr *instr)
31 {
32    switch (instr->opc) {
33 #define ALU(name) \
34    case OPC_##name: \
35       if (instr->has_immed) \
36          return OPC_##name##I; \
37       break;
38 
39    ALU(ADD)
40    ALU(ADDHI)
41    ALU(SUB)
42    ALU(SUBHI)
43    ALU(AND)
44    ALU(OR)
45    ALU(XOR)
46    ALU(NOT)
47    ALU(SHL)
48    ALU(USHR)
49    ALU(ISHR)
50    ALU(ROT)
51    ALU(MUL8)
52    ALU(MIN)
53    ALU(MAX)
54    ALU(CMP)
55    ALU(BIC)
56 #undef ALU
57 
58    default:
59       break;
60    }
61 
62    return instr->opc;
63 }
64 
65 #include "encode.h"
66 
67 int gpuver;
68 
69 /* bit lame to hard-code max but fw sizes are small */
70 static struct afuc_instr instructions[0x4000];
71 static unsigned num_instructions;
72 
73 static unsigned instr_offset;
74 
75 static struct asm_label labels[0x512];
76 static unsigned num_labels;
77 
78 static int outfd;
79 
80 struct afuc_instr *
next_instr(afuc_opc opc)81 next_instr(afuc_opc opc)
82 {
83    struct afuc_instr *ai = &instructions[num_instructions++];
84    assert(num_instructions < ARRAY_SIZE(instructions));
85    memset(ai, 0, sizeof(*ai));
86    instr_offset++;
87    ai->opc = opc;
88    return ai;
89 }
90 
91 static void usage(void);
92 
93 void
parse_version(struct afuc_instr * instr)94 parse_version(struct afuc_instr *instr)
95 {
96    if (gpuver != 0)
97       return;
98 
99    int ret = afuc_util_init(afuc_get_fwid(instr->literal), &gpuver, false);
100    if (ret < 0) {
101       usage();
102       exit(1);
103    }
104 }
105 
106 void
decl_label(const char * str)107 decl_label(const char *str)
108 {
109    struct asm_label *label = &labels[num_labels++];
110 
111    assert(num_labels < ARRAY_SIZE(labels));
112 
113    label->offset = instr_offset;
114    label->label = str;
115 }
116 
117 void
decl_jumptbl(void)118 decl_jumptbl(void)
119 {
120    struct afuc_instr *ai = &instructions[num_instructions++];
121    assert(num_instructions < ARRAY_SIZE(instructions));
122    ai->opc = OPC_JUMPTBL;
123    instr_offset += 0x80;
124 }
125 
126 void
align_instr(unsigned alignment)127 align_instr(unsigned alignment)
128 {
129    while (instr_offset % (alignment / 4) != 0) {
130       next_instr(OPC_NOP);
131    }
132 }
133 
134 static int
resolve_label(const char * str)135 resolve_label(const char *str)
136 {
137    int i;
138 
139    for (i = 0; i < num_labels; i++) {
140       struct asm_label *label = &labels[i];
141 
142       if (!strcmp(str, label->label)) {
143          return label->offset;
144       }
145    }
146 
147    fprintf(stderr, "Undeclared label: %s\n", str);
148    exit(2);
149 }
150 
151 static void
emit_jumptable(int outfd)152 emit_jumptable(int outfd)
153 {
154    uint32_t jmptable[0x80] = {0};
155    int i;
156 
157    for (i = 0; i < num_labels; i++) {
158       struct asm_label *label = &labels[i];
159       int id = afuc_pm4_id(label->label);
160 
161       /* if it doesn't match a known PM4 packet-id, try to match UNKN%d: */
162       if (id < 0) {
163          if (sscanf(label->label, "UNKN%d", &id) != 1) {
164             /* if still not found, must not belong in jump-table: */
165             continue;
166          }
167       }
168 
169       jmptable[id] = label->offset;
170    }
171 
172    write(outfd, jmptable, sizeof(jmptable));
173 }
174 
175 static void
emit_instructions(int outfd)176 emit_instructions(int outfd)
177 {
178    int i;
179 
180    struct encode_state s = {
181       .gen = gpuver,
182    };
183 
184    /* Expand some meta opcodes, and resolve branch targets */
185    for (i = 0; i < num_instructions; i++) {
186       struct afuc_instr *ai = &instructions[i];
187 
188       switch (ai->opc) {
189       case OPC_BREQ:
190          ai->offset = resolve_label(ai->label) - i;
191          if (ai->has_bit)
192             ai->opc = OPC_BREQB;
193          else
194             ai->opc = OPC_BREQI;
195          break;
196 
197       case OPC_BRNE:
198          ai->offset = resolve_label(ai->label) - i;
199          if (ai->has_bit)
200             ai->opc = OPC_BRNEB;
201          else
202             ai->opc = OPC_BRNEI;
203          break;
204 
205       case OPC_JUMP:
206          ai->offset = resolve_label(ai->label) - i;
207          ai->opc = OPC_BRNEB;
208          ai->src1 = 0;
209          ai->bit = 0;
210          break;
211 
212       case OPC_CALL:
213       case OPC_BL:
214       case OPC_JUMPA:
215          ai->literal = resolve_label(ai->label);
216          break;
217 
218       case OPC_MOVI:
219          if (ai->label)
220             ai->immed = resolve_label(ai->label);
221          break;
222 
223       default:
224          break;
225       }
226 
227       if (ai->opc == OPC_JUMPTBL) {
228          emit_jumptable(outfd);
229          continue;
230       }
231 
232       if (ai->opc == OPC_RAW_LITERAL) {
233          if (ai->label) {
234             ai->literal = afuc_nop_literal(resolve_label(ai->label), gpuver);
235          }
236          write(outfd, &ai->literal, 4);
237          continue;
238       }
239 
240       uint32_t encoded = bitmask_to_uint64_t(encode__instruction(&s, NULL, ai));
241       write(outfd, &encoded, 4);
242    }
243 }
244 
next_section(void)245 void next_section(void)
246 {
247    /* Sections must be aligned to 32 bytes */
248    align_instr(32);
249 
250    emit_instructions(outfd);
251 
252    num_instructions = 0;
253    instr_offset = 0;
254    num_labels = 0;
255 }
256 
257 
258 unsigned
parse_control_reg(const char * name)259 parse_control_reg(const char *name)
260 {
261    /* skip leading "@" */
262    return afuc_control_reg(name + 1);
263 }
264 
265 unsigned
parse_sqe_reg(const char * name)266 parse_sqe_reg(const char *name)
267 {
268    /* skip leading "%" */
269    return afuc_sqe_reg(name + 1);
270 }
271 
272 static void
usage(void)273 usage(void)
274 {
275    fprintf(stderr, "Usage:\n"
276                    "\tasm filename.asm filename.fw\n");
277    exit(2);
278 }
279 
280 int
main(int argc,char ** argv)281 main(int argc, char **argv)
282 {
283    FILE *in;
284    char *file, *outfile;
285    int ret;
286 
287    if (optind >= (argc + 1)) {
288       fprintf(stderr, "no file specified!\n");
289       usage();
290    }
291 
292    file = argv[optind];
293    outfile = argv[optind + 1];
294 
295    outfd = open(outfile, O_WRONLY | O_CREAT | O_TRUNC, 0644);
296    if (outfd < 0) {
297       fprintf(stderr, "could not open \"%s\"\n", outfile);
298       usage();
299    }
300 
301    in = fopen(file, "r");
302    if (!in) {
303       fprintf(stderr, "could not open \"%s\"\n", file);
304       usage();
305    }
306 
307    yyset_in(in);
308 
309    /* there is an extra 0x00000000 which kernel strips off.. we could
310     * perhaps use it for versioning.
311     */
312    uint32_t zero = 0;
313    write(outfd, &zero, 4);
314 
315    ret = yyparse();
316    if (ret) {
317       fprintf(stderr, "parse failed: %d\n", ret);
318       return ret;
319    }
320 
321    emit_instructions(outfd);
322 
323    close(outfd);
324 
325    return 0;
326 }
327