freedreno/tools: check rnn parse status
[mesa.git] / src / freedreno / afuc / asm.c
1 /*
2 * Copyright (c) 2017 Rob Clark <robdclark@gmail.com>
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice (including the next
12 * paragraph) shall be included in all copies or substantial portions of the
13 * Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 * SOFTWARE.
22 */
23
24 #include <err.h>
25 #include <stdint.h>
26 #include <stdbool.h>
27 #include <unistd.h>
28 #include <fcntl.h>
29 #include <stdarg.h>
30 #include <stdlib.h>
31 #include <stdio.h>
32 #include <string.h>
33 #include <assert.h>
34 #include <getopt.h>
35
36 #include "afuc.h"
37 #include "rnn.h"
38 #include "rnndec.h"
39 #include "parser.h"
40 #include "asm.h"
41
42 int gpuver;
43
44
45 static struct rnndeccontext *ctx;
46 static struct rnndb *db;
47 static struct rnndomain *control_regs;
48 struct rnndomain *dom[2];
49
50
51 /* bit lame to hard-code max but fw sizes are small */
52 static struct asm_instruction instructions[0x2000];
53 static unsigned num_instructions;
54
55 static struct asm_label labels[0x512];
56 static unsigned num_labels;
57
58 struct asm_instruction *next_instr(int tok)
59 {
60 struct asm_instruction *ai = &instructions[num_instructions++];
61 assert(num_instructions < ARRAY_SIZE(instructions));
62 ai->tok = tok;
63 return ai;
64 }
65
66 void decl_label(const char *str)
67 {
68 struct asm_label *label = &labels[num_labels++];
69
70 assert(num_labels < ARRAY_SIZE(labels));
71
72 label->offset = num_instructions;
73 label->label = str;
74 }
75
76 static int resolve_label(const char *str)
77 {
78 int i;
79
80 for (i = 0; i < num_labels; i++) {
81 struct asm_label *label = &labels[i];
82
83 if (!strcmp(str, label->label)) {
84 return label->offset;
85 }
86 }
87
88 fprintf(stderr, "Undeclared label: %s\n", str);
89 exit(2);
90 }
91
92 static afuc_opc tok2alu(int tok)
93 {
94 switch (tok) {
95 case T_OP_ADD: return OPC_ADD;
96 case T_OP_ADDHI: return OPC_ADDHI;
97 case T_OP_SUB: return OPC_SUB;
98 case T_OP_SUBHI: return OPC_SUBHI;
99 case T_OP_AND: return OPC_AND;
100 case T_OP_OR: return OPC_OR;
101 case T_OP_XOR: return OPC_XOR;
102 case T_OP_NOT: return OPC_NOT;
103 case T_OP_SHL: return OPC_SHL;
104 case T_OP_USHR: return OPC_USHR;
105 case T_OP_ISHR: return OPC_ISHR;
106 case T_OP_ROT: return OPC_ROT;
107 case T_OP_MUL8: return OPC_MUL8;
108 case T_OP_MIN: return OPC_MIN;
109 case T_OP_MAX: return OPC_MAX;
110 case T_OP_CMP: return OPC_CMP;
111 case T_OP_MSB: return OPC_MSB;
112 default:
113 assert(0);
114 return -1;
115 }
116 }
117
118 static void emit_instructions(int outfd)
119 {
120 int i;
121
122 /* there is an extra 0x00000000 which kernel strips off.. we could
123 * perhaps use it for versioning.
124 */
125 i = 0;
126 write(outfd, &i, 4);
127
128 for (i = 0; i < num_instructions; i++) {
129 struct asm_instruction *ai = &instructions[i];
130 afuc_instr instr = {0};
131 afuc_opc opc;
132
133 /* special case, 2nd dword is patched up w/ # of instructions
134 * (ie. offset of jmptbl)
135 */
136 if (i == 1) {
137 assert(ai->is_literal);
138 ai->literal &= ~0xffff;
139 ai->literal |= num_instructions;
140 }
141
142 if (ai->is_literal) {
143 write(outfd, &ai->literal, 4);
144 continue;
145 }
146
147 switch (ai->tok) {
148 case T_OP_NOP:
149 opc = OPC_NOP;
150 if (gpuver >= 6)
151 instr.pad = 0x1000000;
152 break;
153 case T_OP_ADD:
154 case T_OP_ADDHI:
155 case T_OP_SUB:
156 case T_OP_SUBHI:
157 case T_OP_AND:
158 case T_OP_OR:
159 case T_OP_XOR:
160 case T_OP_NOT:
161 case T_OP_SHL:
162 case T_OP_USHR:
163 case T_OP_ISHR:
164 case T_OP_ROT:
165 case T_OP_MUL8:
166 case T_OP_MIN:
167 case T_OP_MAX:
168 case T_OP_CMP:
169 case T_OP_MSB:
170 if (ai->has_immed) {
171 /* MSB overlaps with STORE */
172 assert(ai->tok != T_OP_MSB);
173 opc = tok2alu(ai->tok);
174 instr.alui.dst = ai->dst;
175 instr.alui.src = ai->src1;
176 instr.alui.uimm = ai->immed;
177 } else {
178 opc = OPC_ALU;
179 instr.alu.dst = ai->dst;
180 instr.alu.src1 = ai->src1;
181 instr.alu.src2 = ai->src2;
182 instr.alu.alu = tok2alu(ai->tok);
183 }
184 break;
185 case T_OP_MOV:
186 /* move can either be encoded as movi (ie. move w/ immed) or
187 * an alu instruction
188 */
189 if (ai->has_immed) {
190 opc = OPC_MOVI;
191 instr.movi.dst = ai->dst;
192 instr.movi.uimm = ai->immed;
193 instr.movi.shift = ai->shift;
194 } else if (ai->label) {
195 /* mov w/ a label is just an alias for an immediate, this
196 * is useful to load the address of a constant table into
197 * a register:
198 */
199 opc = OPC_MOVI;
200 instr.movi.dst = ai->dst;
201 instr.movi.uimm = resolve_label(ai->label);
202 instr.movi.shift = ai->shift;
203 } else {
204 /* encode as: or $dst, $00, $src */
205 opc = OPC_ALU;
206 instr.alu.dst = ai->dst;
207 instr.alu.src1 = 0x00; /* $00 reads-back 0 */
208 instr.alu.src2 = ai->src1;
209 instr.alu.alu = OPC_OR;
210 }
211 break;
212 case T_OP_CWRITE:
213 case T_OP_CREAD:
214 case T_OP_STORE:
215 case T_OP_LOAD:
216 if (gpuver >= 6) {
217 if (ai->tok == T_OP_CWRITE) {
218 opc = OPC_CWRITE6;
219 } else if (ai->tok == T_OP_CREAD) {
220 opc = OPC_CREAD6;
221 } else if (ai->tok == T_OP_STORE) {
222 opc = OPC_STORE6;
223 } else if (ai->tok == T_OP_LOAD) {
224 opc = OPC_LOAD6;
225 }
226 } else {
227 if (ai->tok == T_OP_CWRITE) {
228 opc = OPC_CWRITE5;
229 } else if (ai->tok == T_OP_CREAD) {
230 opc = OPC_CREAD5;
231 } else if (ai->tok == T_OP_STORE ||
232 ai->tok == T_OP_LOAD) {
233 fprintf(stderr, "load and store do not exist on a5xx\n");
234 exit(1);
235 }
236 }
237 instr.control.src1 = ai->src1;
238 instr.control.src2 = ai->src2;
239 instr.control.flags = ai->bit;
240 instr.control.uimm = ai->immed;
241 break;
242 case T_OP_BRNE:
243 case T_OP_BREQ:
244 if (ai->has_immed) {
245 opc = (ai->tok == T_OP_BRNE) ? OPC_BRNEI : OPC_BREQI;
246 instr.br.bit_or_imm = ai->immed;
247 } else {
248 opc = (ai->tok == T_OP_BRNE) ? OPC_BRNEB : OPC_BREQB;
249 instr.br.bit_or_imm = ai->bit;
250 }
251 instr.br.src = ai->src1;
252 instr.br.ioff = resolve_label(ai->label) - i;
253 break;
254 case T_OP_RET:
255 opc = OPC_RET;
256 break;
257 case T_OP_CALL:
258 opc = OPC_CALL;
259 instr.call.uoff = resolve_label(ai->label);
260 break;
261 case T_OP_PREEMPTLEAVE:
262 opc = OPC_PREEMPTLEAVE6;
263 instr.call.uoff = resolve_label(ai->label);
264 break;
265 case T_OP_JUMP:
266 /* encode jump as: brne $00, b0, #label */
267 opc = OPC_BRNEB;
268 instr.br.bit_or_imm = 0;
269 instr.br.src = 0x00; /* $00 reads-back 0.. compare to 0 */
270 instr.br.ioff = resolve_label(ai->label) - i;
271 break;
272 case T_OP_WAITIN:
273 opc = OPC_WIN;
274 break;
275 default:
276 assert(0);
277 }
278
279 afuc_set_opc(&instr, opc, ai->rep);
280
281 write(outfd, &instr, 4);
282 }
283
284 }
285
286 static int find_enum_val(struct rnnenum *en, const char *name)
287 {
288 int i;
289
290 for (i = 0; i < en->valsnum; i++)
291 if (en->vals[i]->valvalid && !strcmp(name, en->vals[i]->name))
292 return en->vals[i]->value;
293
294 return -1;
295 }
296
297 static int find_reg(struct rnndomain *dom, const char *name)
298 {
299 int i;
300
301 for (i = 0; i < dom->subelemsnum; i++)
302 if (!strcmp(name, dom->subelems[i]->name))
303 return dom->subelems[i]->offset;
304
305 return -1;
306 }
307
308 unsigned parse_control_reg(const char *name)
309 {
310 /* skip leading "@" */
311 int val = find_reg(control_regs, name + 1);
312 if (val < 0) {
313 printf("invalid control reg: %s\n", name);
314 exit(2);
315 }
316 return (unsigned)val;
317 }
318
319 static void emit_jumptable(int outfd)
320 {
321 struct rnnenum *en = rnn_findenum(ctx->db, "adreno_pm4_type3_packets");
322 uint32_t jmptable[0x80] = {0};
323 int i;
324
325 for (i = 0; i < num_labels; i++) {
326 struct asm_label *label = &labels[i];
327 int id = find_enum_val(en, label->label);
328
329 /* if it doesn't match a known PM4 packet-id, try to match UNKN%d: */
330 if (id < 0) {
331 if (sscanf(label->label, "UNKN%d", &id) != 1) {
332 /* if still not found, must not belong in jump-table: */
333 continue;
334 }
335 }
336
337 jmptable[id] = label->offset;
338 }
339
340 write(outfd, jmptable, sizeof(jmptable));
341 }
342
343 static void usage(void)
344 {
345 fprintf(stderr, "Usage:\n"
346 "\tasm [-g GPUVER] filename.asm filename.fw\n"
347 "\t\t-g - specify GPU version (5, etc)\n"
348 );
349 exit(2);
350 }
351
352 int main(int argc, char **argv)
353 {
354 FILE *in;
355 char *file, *outfile, *name, *control_reg_name;
356 int c, ret, outfd;
357
358 /* Argument parsing: */
359 while ((c = getopt (argc, argv, "g:")) != -1) {
360 switch (c) {
361 case 'g':
362 gpuver = atoi(optarg);
363 break;
364 default:
365 usage();
366 }
367 }
368
369 if (optind >= (argc + 1)) {
370 fprintf(stderr, "no file specified!\n");
371 usage();
372 }
373
374 file = argv[optind];
375 outfile = argv[optind + 1];
376
377 outfd = open(outfile, O_WRONLY | O_CREAT | O_TRUNC, 0644);
378 if (outfd < 0) {
379 fprintf(stderr, "could not open \"%s\"\n", outfile);
380 usage();
381 }
382
383 in = fopen(file, "r");
384 if (!in) {
385 fprintf(stderr, "could not open \"%s\"\n", file);
386 usage();
387 }
388
389 yyset_in(in);
390
391 /* if gpu version not specified, infer from filename: */
392 if (!gpuver) {
393 if (strstr(file, "a5")) {
394 gpuver = 5;
395 } else if (strstr(file, "a6")) {
396 gpuver = 6;
397 }
398 }
399
400 switch (gpuver) {
401 case 6:
402 name = "A6XX";
403 control_reg_name = "A6XX_CONTROL_REG";
404 break;
405 case 5:
406 name = "A5XX";
407 control_reg_name = "A5XX_CONTROL_REG";
408 break;
409 default:
410 fprintf(stderr, "unknown GPU version!\n");
411 usage();
412 }
413
414 rnn_init();
415 db = rnn_newdb();
416
417 ctx = rnndec_newcontext(db);
418
419 rnn_parsefile(db, "adreno.xml");
420 if (db->estatus)
421 errx(db->estatus, "failed to parse register database");
422 dom[0] = rnn_finddomain(db, name);
423 dom[1] = rnn_finddomain(db, "AXXX");
424 control_regs = rnn_finddomain(db, control_reg_name);
425
426 ret = yyparse();
427 if (ret) {
428 fprintf(stderr, "parse failed: %d\n", ret);
429 return ret;
430 }
431
432 emit_instructions(outfd);
433 emit_jumptable(outfd);
434
435 close(outfd);
436
437 return 0;
438 }