• 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 "util/log.h"
38 #include "afuc.h"
39 #include "asm.h"
40 #include "parser.h"
41 #include "util.h"
42 
43 struct encode_state {
44 	unsigned gen;
45 };
46 
47 static afuc_opc
__instruction_case(struct encode_state * s,struct afuc_instr * instr)48 __instruction_case(struct encode_state *s, struct afuc_instr *instr)
49 {
50    switch (instr->opc) {
51 #define ALU(name) \
52    case OPC_##name: \
53       if (instr->has_immed) \
54          return OPC_##name##I; \
55       break;
56 
57    ALU(ADD)
58    ALU(ADDHI)
59    ALU(SUB)
60    ALU(SUBHI)
61    ALU(AND)
62    ALU(OR)
63    ALU(XOR)
64    ALU(NOT)
65    ALU(SHL)
66    ALU(USHR)
67    ALU(ISHR)
68    ALU(ROT)
69    ALU(MUL8)
70    ALU(MIN)
71    ALU(MAX)
72    ALU(CMP)
73 #undef ALU
74 
75    default:
76       break;
77    }
78 
79    return instr->opc;
80 }
81 
82 #include "encode.h"
83 
84 int gpuver;
85 
86 /* bit lame to hard-code max but fw sizes are small */
87 static struct afuc_instr instructions[0x2000];
88 static unsigned num_instructions;
89 
90 static struct asm_label labels[0x512];
91 static unsigned num_labels;
92 
93 struct afuc_instr *
next_instr(afuc_opc opc)94 next_instr(afuc_opc opc)
95 {
96    struct afuc_instr *ai = &instructions[num_instructions++];
97    assert(num_instructions < ARRAY_SIZE(instructions));
98    ai->opc = opc;
99    return ai;
100 }
101 
102 void
decl_label(const char * str)103 decl_label(const char *str)
104 {
105    struct asm_label *label = &labels[num_labels++];
106 
107    assert(num_labels < ARRAY_SIZE(labels));
108 
109    label->offset = num_instructions;
110    label->label = str;
111 }
112 
113 static int
resolve_label(const char * str)114 resolve_label(const char *str)
115 {
116    int i;
117 
118    for (i = 0; i < num_labels; i++) {
119       struct asm_label *label = &labels[i];
120 
121       if (!strcmp(str, label->label)) {
122          return label->offset;
123       }
124    }
125 
126    fprintf(stderr, "Undeclared label: %s\n", str);
127    exit(2);
128 }
129 
130 static void
emit_instructions(int outfd)131 emit_instructions(int outfd)
132 {
133    int i;
134 
135    struct encode_state s = {
136       .gen = gpuver,
137    };
138 
139    /* there is an extra 0x00000000 which kernel strips off.. we could
140     * perhaps use it for versioning.
141     */
142    i = 0;
143    write(outfd, &i, 4);
144 
145    /* Expand some meta opcodes, and resolve branch targets */
146    for (i = 0; i < num_instructions; i++) {
147       struct afuc_instr *ai = &instructions[i];
148 
149       switch (ai->opc) {
150       case OPC_BREQ:
151          ai->offset = resolve_label(ai->label) - i;
152          if (ai->has_bit)
153             ai->opc = OPC_BREQB;
154          else
155             ai->opc = OPC_BREQI;
156          break;
157 
158       case OPC_BRNE:
159          ai->offset = resolve_label(ai->label) - i;
160          if (ai->has_bit)
161             ai->opc = OPC_BRNEB;
162          else
163             ai->opc = OPC_BRNEI;
164          break;
165 
166       case OPC_JUMP:
167          ai->offset = resolve_label(ai->label) - i;
168          ai->opc = OPC_BRNEB;
169          ai->src1 = 0;
170          ai->bit = 0;
171          break;
172 
173       case OPC_CALL:
174       case OPC_PREEMPTLEAVE:
175          ai->literal = resolve_label(ai->label);
176          break;
177 
178       case OPC_MOVI:
179          if (ai->label)
180             ai->immed = resolve_label(ai->label);
181          break;
182 
183       default:
184          break;
185       }
186 
187       /* special case, 2nd dword is patched up w/ # of instructions
188        * (ie. offset of jmptbl)
189        */
190       if (i == 1) {
191          assert(ai->opc == OPC_RAW_LITERAL);
192          ai->literal &= ~0xffff;
193          ai->literal |= num_instructions;
194       }
195 
196       if (ai->opc == OPC_RAW_LITERAL) {
197          write(outfd, &ai->literal, 4);
198          continue;
199       }
200 
201       uint32_t encoded = bitmask_to_uint64_t(encode__instruction(&s, NULL, ai));
202       write(outfd, &encoded, 4);
203    }
204 }
205 
206 unsigned
parse_control_reg(const char * name)207 parse_control_reg(const char *name)
208 {
209    /* skip leading "@" */
210    return afuc_control_reg(name + 1);
211 }
212 
213 unsigned
parse_sqe_reg(const char * name)214 parse_sqe_reg(const char *name)
215 {
216    /* skip leading "%" */
217    return afuc_sqe_reg(name + 1);
218 }
219 
220 static void
emit_jumptable(int outfd)221 emit_jumptable(int outfd)
222 {
223    uint32_t jmptable[0x80] = {0};
224    int i;
225 
226    for (i = 0; i < num_labels; i++) {
227       struct asm_label *label = &labels[i];
228       int id = afuc_pm4_id(label->label);
229 
230       /* if it doesn't match a known PM4 packet-id, try to match UNKN%d: */
231       if (id < 0) {
232          if (sscanf(label->label, "UNKN%d", &id) != 1) {
233             /* if still not found, must not belong in jump-table: */
234             continue;
235          }
236       }
237 
238       jmptable[id] = label->offset;
239    }
240 
241    write(outfd, jmptable, sizeof(jmptable));
242 }
243 
244 static void
usage(void)245 usage(void)
246 {
247    fprintf(stderr, "Usage:\n"
248                    "\tasm [-g GPUVER] filename.asm filename.fw\n"
249                    "\t\t-g - specify GPU version (5, etc)\n");
250    exit(2);
251 }
252 
253 int
main(int argc,char ** argv)254 main(int argc, char **argv)
255 {
256    FILE *in;
257    char *file, *outfile;
258    int c, ret, outfd;
259 
260    /* Argument parsing: */
261    while ((c = getopt(argc, argv, "g:")) != -1) {
262       switch (c) {
263       case 'g':
264          gpuver = atoi(optarg);
265          break;
266       default:
267          usage();
268       }
269    }
270 
271    if (optind >= (argc + 1)) {
272       fprintf(stderr, "no file specified!\n");
273       usage();
274    }
275 
276    file = argv[optind];
277    outfile = argv[optind + 1];
278 
279    outfd = open(outfile, O_WRONLY | O_CREAT | O_TRUNC, 0644);
280    if (outfd < 0) {
281       fprintf(stderr, "could not open \"%s\"\n", outfile);
282       usage();
283    }
284 
285    in = fopen(file, "r");
286    if (!in) {
287       fprintf(stderr, "could not open \"%s\"\n", file);
288       usage();
289    }
290 
291    yyset_in(in);
292 
293    /* if gpu version not specified, infer from filename: */
294    if (!gpuver) {
295       if (strstr(file, "a5")) {
296          gpuver = 5;
297       } else if (strstr(file, "a6")) {
298          gpuver = 6;
299       } else if (strstr(file, "a7")) {
300          gpuver = 7;
301       }
302    }
303 
304    ret = afuc_util_init(gpuver, false);
305    if (ret < 0) {
306       usage();
307    }
308 
309    ret = yyparse();
310    if (ret) {
311       fprintf(stderr, "parse failed: %d\n", ret);
312       return ret;
313    }
314 
315    emit_instructions(outfd);
316    emit_jumptable(outfd);
317 
318    close(outfd);
319 
320    return 0;
321 }
322