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