a03a89a31fa33621a0a1bcf5ec88b3e933fcd34c
[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_IRET:
258 opc = OPC_RET;
259 instr.ret.interrupt = 1;
260 break;
261 case T_OP_CALL:
262 opc = OPC_CALL;
263 instr.call.uoff = resolve_label(ai->label);
264 break;
265 case T_OP_PREEMPTLEAVE:
266 opc = OPC_PREEMPTLEAVE6;
267 instr.call.uoff = resolve_label(ai->label);
268 break;
269 case T_OP_SETSECURE:
270 opc = OPC_SETSECURE;
271 if (resolve_label(ai->label) != i + 3) {
272 fprintf(stderr, "jump label %s is incorrect for setsecure\n", ai->label);
273 exit(1);
274 }
275 if (ai->src1 != 0x2) {
276 fprintf(stderr, "source for setsecure must be $02\n");
277 exit(1);
278 }
279 break;
280 case T_OP_JUMP:
281 /* encode jump as: brne $00, b0, #label */
282 opc = OPC_BRNEB;
283 instr.br.bit_or_imm = 0;
284 instr.br.src = 0x00; /* $00 reads-back 0.. compare to 0 */
285 instr.br.ioff = resolve_label(ai->label) - i;
286 break;
287 case T_OP_WAITIN:
288 opc = OPC_WIN;
289 break;
290 default:
291 assert(0);
292 }
293
294 afuc_set_opc(&instr, opc, ai->rep);
295
296 write(outfd, &instr, 4);
297 }
298
299 }
300
301 static int find_enum_val(struct rnnenum *en, const char *name)
302 {
303 int i;
304
305 for (i = 0; i < en->valsnum; i++)
306 if (en->vals[i]->valvalid && !strcmp(name, en->vals[i]->name))
307 return en->vals[i]->value;
308
309 return -1;
310 }
311
312 static int find_reg(struct rnndomain *dom, const char *name)
313 {
314 int i;
315
316 for (i = 0; i < dom->subelemsnum; i++)
317 if (!strcmp(name, dom->subelems[i]->name))
318 return dom->subelems[i]->offset;
319
320 return -1;
321 }
322
323 unsigned parse_control_reg(const char *name)
324 {
325 /* skip leading "@" */
326 int val = find_reg(control_regs, name + 1);
327 if (val < 0) {
328 printf("invalid control reg: %s\n", name);
329 exit(2);
330 }
331 return (unsigned)val;
332 }
333
334 static void emit_jumptable(int outfd)
335 {
336 struct rnnenum *en = rnn_findenum(ctx->db, "adreno_pm4_type3_packets");
337 uint32_t jmptable[0x80] = {0};
338 int i;
339
340 for (i = 0; i < num_labels; i++) {
341 struct asm_label *label = &labels[i];
342 int id = find_enum_val(en, label->label);
343
344 /* if it doesn't match a known PM4 packet-id, try to match UNKN%d: */
345 if (id < 0) {
346 if (sscanf(label->label, "UNKN%d", &id) != 1) {
347 /* if still not found, must not belong in jump-table: */
348 continue;
349 }
350 }
351
352 jmptable[id] = label->offset;
353 }
354
355 write(outfd, jmptable, sizeof(jmptable));
356 }
357
358 static void usage(void)
359 {
360 fprintf(stderr, "Usage:\n"
361 "\tasm [-g GPUVER] filename.asm filename.fw\n"
362 "\t\t-g - specify GPU version (5, etc)\n"
363 );
364 exit(2);
365 }
366
367 int main(int argc, char **argv)
368 {
369 FILE *in;
370 char *file, *outfile, *name, *control_reg_name;
371 int c, ret, outfd;
372
373 /* Argument parsing: */
374 while ((c = getopt (argc, argv, "g:")) != -1) {
375 switch (c) {
376 case 'g':
377 gpuver = atoi(optarg);
378 break;
379 default:
380 usage();
381 }
382 }
383
384 if (optind >= (argc + 1)) {
385 fprintf(stderr, "no file specified!\n");
386 usage();
387 }
388
389 file = argv[optind];
390 outfile = argv[optind + 1];
391
392 outfd = open(outfile, O_WRONLY | O_CREAT | O_TRUNC, 0644);
393 if (outfd < 0) {
394 fprintf(stderr, "could not open \"%s\"\n", outfile);
395 usage();
396 }
397
398 in = fopen(file, "r");
399 if (!in) {
400 fprintf(stderr, "could not open \"%s\"\n", file);
401 usage();
402 }
403
404 yyset_in(in);
405
406 /* if gpu version not specified, infer from filename: */
407 if (!gpuver) {
408 if (strstr(file, "a5")) {
409 gpuver = 5;
410 } else if (strstr(file, "a6")) {
411 gpuver = 6;
412 }
413 }
414
415 switch (gpuver) {
416 case 6:
417 name = "A6XX";
418 control_reg_name = "A6XX_CONTROL_REG";
419 break;
420 case 5:
421 name = "A5XX";
422 control_reg_name = "A5XX_CONTROL_REG";
423 break;
424 default:
425 fprintf(stderr, "unknown GPU version!\n");
426 usage();
427 }
428
429 rnn_init();
430 db = rnn_newdb();
431
432 ctx = rnndec_newcontext(db);
433
434 rnn_parsefile(db, "adreno.xml");
435 rnn_prepdb(db);
436 if (db->estatus)
437 errx(db->estatus, "failed to parse register database");
438 dom[0] = rnn_finddomain(db, name);
439 dom[1] = rnn_finddomain(db, "AXXX");
440 control_regs = rnn_finddomain(db, control_reg_name);
441
442 ret = yyparse();
443 if (ret) {
444 fprintf(stderr, "parse failed: %d\n", ret);
445 return ret;
446 }
447
448 emit_instructions(outfd);
449 emit_jumptable(outfd);
450
451 close(outfd);
452
453 return 0;
454 }