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