• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
42 int gpuver;
43 
44 /* bit lame to hard-code max but fw sizes are small */
45 static struct asm_instruction instructions[0x2000];
46 static unsigned num_instructions;
47 
48 static struct asm_label labels[0x512];
49 static unsigned num_labels;
50 
51 struct asm_instruction *
next_instr(int tok)52 next_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 
60 void
decl_label(const char * str)61 decl_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 
71 static int
resolve_label(const char * str)72 resolve_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 
88 static afuc_opc
tok2alu(int tok)89 tok2alu(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 
132 static void
emit_instructions(int outfd)133 emit_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 
330 unsigned
parse_control_reg(const char * name)331 parse_control_reg(const char *name)
332 {
333    /* skip leading "@" */
334    return afuc_control_reg(name + 1);
335 }
336 
337 static void
emit_jumptable(int outfd)338 emit_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 
361 static void
usage(void)362 usage(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 
370 int
main(int argc,char ** argv)371 main(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