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