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