intel/assembler: Add labels support
[mesa.git] / src / intel / tools / i965_asm.c
1 /*
2 * Copyright © 2018 Intel Corporation
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
25 #include <stdio.h>
26 #include <getopt.h>
27 #include "i965_asm.h"
28
29 enum opt_output_type {
30 OPT_OUTPUT_HEX,
31 OPT_OUTPUT_C_LITERAL,
32 OPT_OUTPUT_BIN,
33 };
34
35 extern FILE *yyin;
36 struct brw_codegen *p;
37 static enum opt_output_type output_type = OPT_OUTPUT_BIN;
38 char *input_filename = NULL;
39 int errors;
40
41 struct list_head instr_labels;
42 struct list_head target_labels;
43
44 static void
45 print_help(const char *progname, FILE *file)
46 {
47 fprintf(file,
48 "Usage: %s [OPTION] inputfile\n"
49 "Assemble i965 instructions from input file.\n\n"
50 " -h, --help display this help and exit\n"
51 " -t, --type=OUTPUT_TYPE OUTPUT_TYPE can be 'bin' (default if omitted),\n"
52 " 'c_literal', or 'hex'\n"
53 " -o, --output specify output file\n"
54 " --compact print compacted instructions\n"
55 " -g, --gen=platform assemble instructions for given \n"
56 " platform (3 letter platform name)\n"
57 "Example:\n"
58 " i965_asm -g kbl input.asm -t hex -o output\n",
59 progname);
60 }
61
62 static uint32_t
63 get_dword(const brw_inst *inst, int idx)
64 {
65 uint32_t dword;
66 memcpy(&dword, (char *)inst + 4 * idx, sizeof(dword));
67 return dword;
68 }
69
70 static void
71 print_instruction(FILE *output, bool compact, const brw_inst *instruction)
72 {
73 int byte_limit;
74
75 byte_limit = (compact == true) ? 8 : 16;
76
77 switch (output_type) {
78 case OPT_OUTPUT_HEX: {
79 fprintf(output, "%02x", ((unsigned char *)instruction)[0]);
80
81 for (unsigned i = 1; i < byte_limit; i++) {
82 fprintf(output, " %02x", ((unsigned char *)instruction)[i]);
83 }
84 break;
85 }
86 case OPT_OUTPUT_C_LITERAL: {
87 fprintf(output, "\t0x%08x,", get_dword(instruction, 0));
88
89 for (unsigned i = 1; i < byte_limit / 4; i++)
90 fprintf(output, " 0x%08x,", get_dword(instruction, i));
91
92 break;
93 }
94 case OPT_OUTPUT_BIN:
95 fwrite(instruction, 1, byte_limit, output);
96 break;
97 }
98
99 if (output_type != OPT_OUTPUT_BIN) {
100 fprintf(output, "\n");
101 }
102 }
103
104 static struct gen_device_info *
105 i965_disasm_init(uint16_t pci_id)
106 {
107 struct gen_device_info *devinfo;
108
109 devinfo = malloc(sizeof *devinfo);
110 if (devinfo == NULL)
111 return NULL;
112
113 if (!gen_get_device_info_from_pci_id(pci_id, devinfo)) {
114 fprintf(stderr, "can't find device information: pci_id=0x%x\n",
115 pci_id);
116 free(devinfo);
117 return NULL;
118 }
119
120 brw_init_compaction_tables(devinfo);
121
122 return devinfo;
123 }
124
125 static bool
126 i965_postprocess_labels()
127 {
128 if (p->devinfo->gen < 6) {
129 return true;
130 }
131
132 void *store = p->store;
133
134 struct target_label *tlabel;
135 struct instr_label *ilabel, *s;
136
137 const unsigned to_bytes_scale = brw_jump_scale(p->devinfo);
138
139 LIST_FOR_EACH_ENTRY(tlabel, &target_labels, link) {
140 LIST_FOR_EACH_ENTRY_SAFE(ilabel, s, &instr_labels, link) {
141 if (!strcmp(tlabel->name, ilabel->name)) {
142 brw_inst *inst = store + ilabel->offset;
143
144 int relative_offset = (tlabel->offset - ilabel->offset) / sizeof(brw_inst);
145 relative_offset *= to_bytes_scale;
146
147 unsigned opcode = brw_inst_opcode(p->devinfo, inst);
148
149 if (ilabel->type == INSTR_LABEL_JIP) {
150 switch (opcode) {
151 case BRW_OPCODE_IF:
152 case BRW_OPCODE_ELSE:
153 case BRW_OPCODE_ENDIF:
154 case BRW_OPCODE_WHILE:
155 if (p->devinfo->gen >= 7) {
156 brw_inst_set_jip(p->devinfo, inst, relative_offset);
157 } else if (p->devinfo->gen == 6) {
158 brw_inst_set_gen6_jump_count(p->devinfo, inst, relative_offset);
159 }
160 break;
161 case BRW_OPCODE_BREAK:
162 case BRW_OPCODE_HALT:
163 case BRW_OPCODE_CONTINUE:
164 brw_inst_set_jip(p->devinfo, inst, relative_offset);
165 break;
166 default:
167 fprintf(stderr, "Unknown opcode %d with JIP label\n", opcode);
168 return false;
169 }
170 } else {
171 switch (opcode) {
172 case BRW_OPCODE_IF:
173 case BRW_OPCODE_ELSE:
174 if (p->devinfo->gen > 7) {
175 brw_inst_set_uip(p->devinfo, inst, relative_offset);
176 } else if (p->devinfo->gen == 7) {
177 brw_inst_set_uip(p->devinfo, inst, relative_offset);
178 } else if (p->devinfo->gen == 6) {
179 // Nothing
180 }
181 break;
182 case BRW_OPCODE_WHILE:
183 case BRW_OPCODE_ENDIF:
184 fprintf(stderr, "WHILE/ENDIF cannot have UIP offset\n");
185 return false;
186 case BRW_OPCODE_BREAK:
187 case BRW_OPCODE_CONTINUE:
188 case BRW_OPCODE_HALT:
189 brw_inst_set_uip(p->devinfo, inst, relative_offset);
190 break;
191 default:
192 fprintf(stderr, "Unknown opcode %d with UIP label\n", opcode);
193 return false;
194 }
195 }
196
197 list_del(&ilabel->link);
198 }
199 }
200 }
201
202 LIST_FOR_EACH_ENTRY(ilabel, &instr_labels, link) {
203 fprintf(stderr, "Unknown label '%s'\n", ilabel->name);
204 }
205
206 return list_is_empty(&instr_labels);
207 }
208
209 int main(int argc, char **argv)
210 {
211 char *output_file = NULL;
212 char c;
213 FILE *output = stdout;
214 bool help = false, compact = false;
215 void *store;
216 uint64_t pci_id = 0;
217 int offset = 0, err;
218 int start_offset = 0;
219 struct disasm_info *disasm_info;
220 struct gen_device_info *devinfo = NULL;
221 int result = EXIT_FAILURE;
222 list_inithead(&instr_labels);
223 list_inithead(&target_labels);
224
225 const struct option i965_asm_opts[] = {
226 { "help", no_argument, (int *) &help, true },
227 { "type", required_argument, NULL, 't' },
228 { "gen", required_argument, NULL, 'g' },
229 { "output", required_argument, NULL, 'o' },
230 { "compact", no_argument, (int *) &compact, true },
231 { NULL, 0, NULL, 0 }
232 };
233
234 while ((c = getopt_long(argc, argv, ":t:g:o:h", i965_asm_opts, NULL)) != -1) {
235 switch (c) {
236 case 'g': {
237 const int id = gen_device_name_to_pci_device_id(optarg);
238 if (id < 0) {
239 fprintf(stderr, "can't parse gen: '%s', expected 3 letter "
240 "platform name\n", optarg);
241 goto end;
242 } else {
243 pci_id = id;
244 }
245 break;
246 }
247 case 'h':
248 help = true;
249 print_help(argv[0], stderr);
250 goto end;
251 case 't': {
252 if (strcmp(optarg, "hex") == 0) {
253 output_type = OPT_OUTPUT_HEX;
254 } else if (strcmp(optarg, "c_literal") == 0) {
255 output_type = OPT_OUTPUT_C_LITERAL;
256 } else if (strcmp(optarg, "bin") == 0) {
257 output_type = OPT_OUTPUT_BIN;
258 } else {
259 fprintf(stderr, "invalid value for --type: %s\n", optarg);
260 goto end;
261 }
262 break;
263 }
264 case 'o':
265 output_file = strdup(optarg);
266 break;
267 case 0:
268 break;
269 case ':':
270 fprintf(stderr, "%s: option `-%c' requires an argument\n",
271 argv[0], optopt);
272 goto end;
273 case '?':
274 default:
275 fprintf(stderr, "%s: option `-%c' is invalid: ignored\n",
276 argv[0], optopt);
277 goto end;
278 }
279 }
280
281 if (help || !pci_id) {
282 print_help(argv[0], stderr);
283 goto end;
284 }
285
286 if (!argv[optind]) {
287 fprintf(stderr, "Please specify input file\n");
288 goto end;
289 }
290
291 input_filename = strdup(argv[optind]);
292 yyin = fopen(input_filename, "r");
293 if (!yyin) {
294 fprintf(stderr, "Unable to read input file : %s\n",
295 input_filename);
296 goto end;
297 }
298
299 if (output_file) {
300 output = fopen(output_file, "w");
301 if (!output) {
302 fprintf(stderr, "Couldn't open output file\n");
303 goto end;
304 }
305 }
306
307 devinfo = i965_disasm_init(pci_id);
308 if (!devinfo) {
309 fprintf(stderr, "Unable to allocate memory for "
310 "gen_device_info struct instance.\n");
311 goto end;
312 }
313
314 p = rzalloc(NULL, struct brw_codegen);
315 brw_init_codegen(devinfo, p, p);
316 p->automatic_exec_sizes = false;
317
318 err = yyparse();
319 if (err || errors)
320 goto end;
321
322 if (!i965_postprocess_labels())
323 goto end;
324
325 store = p->store;
326
327 disasm_info = disasm_initialize(p->devinfo, NULL);
328 if (!disasm_info) {
329 fprintf(stderr, "Unable to initialize disasm_info struct instance\n");
330 goto end;
331 }
332
333 if (output_type == OPT_OUTPUT_C_LITERAL)
334 fprintf(output, "{\n");
335
336 brw_validate_instructions(p->devinfo, p->store, 0,
337 p->next_insn_offset, disasm_info);
338
339 const int nr_insn = (p->next_insn_offset - start_offset) / 16;
340
341 if (compact)
342 brw_compact_instructions(p, start_offset, disasm_info);
343
344 for (int i = 0; i < nr_insn; i++) {
345 const brw_inst *insn = store + offset;
346 bool compacted = false;
347
348 if (compact && brw_inst_cmpt_control(p->devinfo, insn)) {
349 offset += 8;
350 compacted = true;
351 } else {
352 offset += 16;
353 }
354
355 print_instruction(output, compacted, insn);
356 }
357
358 ralloc_free(disasm_info);
359
360 if (output_type == OPT_OUTPUT_C_LITERAL)
361 fprintf(output, "}");
362
363 result = EXIT_SUCCESS;
364 goto end;
365
366 end:
367 free(input_filename);
368 free(output_file);
369
370 if (yyin)
371 fclose(yyin);
372
373 if (output)
374 fclose(output);
375
376 if (p)
377 ralloc_free(p);
378
379 if (devinfo)
380 free(devinfo);
381
382 exit(result);
383 }