1 /* -*- mode: C; c-file-style: "k&r"; tab-width 4; indent-tabs-mode: t; -*- */
2
3 /*
4 * Copyright (C) 2012 Rob Clark <robclark@freedesktop.org>
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a
7 * copy of this software and associated documentation files (the "Software"),
8 * to deal in the Software without restriction, including without limitation
9 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
10 * and/or sell copies of the Software, and to permit persons to whom the
11 * Software is furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice (including the next
14 * paragraph) shall be included in all copies or substantial portions of the
15 * Software.
16 *
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
20 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23 * SOFTWARE.
24 *
25 * Authors:
26 * Rob Clark <robclark@freedesktop.org>
27 */
28
29 #include "pipe/p_state.h"
30 #include "util/u_string.h"
31 #include "util/u_memory.h"
32 #include "util/u_inlines.h"
33 #include "tgsi/tgsi_parse.h"
34 #include "tgsi/tgsi_ureg.h"
35 #include "tgsi/tgsi_info.h"
36 #include "tgsi/tgsi_strings.h"
37 #include "tgsi/tgsi_dump.h"
38
39 #include "fd2_compiler.h"
40 #include "fd2_program.h"
41 #include "fd2_util.h"
42
43 #include "instr-a2xx.h"
44 #include "ir-a2xx.h"
45
46 struct fd2_compile_context {
47 struct fd_program_stateobj *prog;
48 struct fd2_shader_stateobj *so;
49
50 struct tgsi_parse_context parser;
51 unsigned type;
52
53 /* predicate stack: */
54 int pred_depth;
55 enum ir2_pred pred_stack[8];
56
57 /* Internal-Temporary and Predicate register assignment:
58 *
59 * Some TGSI instructions which translate into multiple actual
60 * instructions need one or more temporary registers, which are not
61 * assigned from TGSI perspective (ie. not TGSI_FILE_TEMPORARY).
62 * And some instructions (texture fetch) cannot write directly to
63 * output registers. We could be more clever and re-use dst or a
64 * src register in some cases. But for now don't try to be clever.
65 * Eventually we should implement an optimization pass that re-
66 * juggles the register usage and gets rid of unneeded temporaries.
67 *
68 * The predicate register must be valid across multiple TGSI
69 * instructions, but internal temporary's do not. For this reason,
70 * once the predicate register is requested, until it is no longer
71 * needed, it gets the first register slot after after the TGSI
72 * assigned temporaries (ie. num_regs[TGSI_FILE_TEMPORARY]), and the
73 * internal temporaries get the register slots above this.
74 */
75
76 int pred_reg;
77 int num_internal_temps;
78
79 uint8_t num_regs[TGSI_FILE_COUNT];
80
81 /* maps input register idx to prog->export_linkage idx: */
82 uint8_t input_export_idx[64];
83
84 /* maps output register idx to prog->export_linkage idx: */
85 uint8_t output_export_idx[64];
86
87 /* idx/slot for last compiler generated immediate */
88 unsigned immediate_idx;
89
90 // TODO we can skip emit exports in the VS that the FS doesn't need..
91 // and get rid perhaps of num_param..
92 unsigned num_position, num_param;
93 unsigned position, psize;
94
95 uint64_t need_sync;
96
97 /* current exec CF instruction */
98 struct ir2_cf *cf;
99 };
100
101 static int
semantic_idx(struct tgsi_declaration_semantic * semantic)102 semantic_idx(struct tgsi_declaration_semantic *semantic)
103 {
104 int idx = semantic->Name;
105 if (idx == TGSI_SEMANTIC_GENERIC)
106 idx = TGSI_SEMANTIC_COUNT + semantic->Index;
107 return idx;
108 }
109
110 /* assign/get the input/export register # for given semantic idx as
111 * returned by semantic_idx():
112 */
113 static int
export_linkage(struct fd2_compile_context * ctx,int idx)114 export_linkage(struct fd2_compile_context *ctx, int idx)
115 {
116 struct fd_program_stateobj *prog = ctx->prog;
117
118 /* if first time we've seen this export, assign the next available slot: */
119 if (prog->export_linkage[idx] == 0xff)
120 prog->export_linkage[idx] = prog->num_exports++;
121
122 return prog->export_linkage[idx];
123 }
124
125 static unsigned
compile_init(struct fd2_compile_context * ctx,struct fd_program_stateobj * prog,struct fd2_shader_stateobj * so)126 compile_init(struct fd2_compile_context *ctx, struct fd_program_stateobj *prog,
127 struct fd2_shader_stateobj *so)
128 {
129 unsigned ret;
130
131 ctx->prog = prog;
132 ctx->so = so;
133 ctx->cf = NULL;
134 ctx->pred_depth = 0;
135
136 ret = tgsi_parse_init(&ctx->parser, so->tokens);
137 if (ret != TGSI_PARSE_OK)
138 return ret;
139
140 ctx->type = ctx->parser.FullHeader.Processor.Processor;
141 ctx->position = ~0;
142 ctx->psize = ~0;
143 ctx->num_position = 0;
144 ctx->num_param = 0;
145 ctx->need_sync = 0;
146 ctx->immediate_idx = 0;
147 ctx->pred_reg = -1;
148 ctx->num_internal_temps = 0;
149
150 memset(ctx->num_regs, 0, sizeof(ctx->num_regs));
151 memset(ctx->input_export_idx, 0, sizeof(ctx->input_export_idx));
152 memset(ctx->output_export_idx, 0, sizeof(ctx->output_export_idx));
153
154 /* do first pass to extract declarations: */
155 while (!tgsi_parse_end_of_tokens(&ctx->parser)) {
156 tgsi_parse_token(&ctx->parser);
157
158 switch (ctx->parser.FullToken.Token.Type) {
159 case TGSI_TOKEN_TYPE_DECLARATION: {
160 struct tgsi_full_declaration *decl =
161 &ctx->parser.FullToken.FullDeclaration;
162 if (decl->Declaration.File == TGSI_FILE_OUTPUT) {
163 unsigned name = decl->Semantic.Name;
164
165 assert(decl->Declaration.Semantic); // TODO is this ever not true?
166
167 ctx->output_export_idx[decl->Range.First] =
168 semantic_idx(&decl->Semantic);
169
170 if (ctx->type == PIPE_SHADER_VERTEX) {
171 switch (name) {
172 case TGSI_SEMANTIC_POSITION:
173 ctx->position = ctx->num_regs[TGSI_FILE_OUTPUT];
174 ctx->num_position++;
175 break;
176 case TGSI_SEMANTIC_PSIZE:
177 ctx->psize = ctx->num_regs[TGSI_FILE_OUTPUT];
178 ctx->num_position++;
179 break;
180 case TGSI_SEMANTIC_COLOR:
181 case TGSI_SEMANTIC_GENERIC:
182 ctx->num_param++;
183 break;
184 default:
185 DBG("unknown VS semantic name: %s",
186 tgsi_semantic_names[name]);
187 assert(0);
188 }
189 } else {
190 switch (name) {
191 case TGSI_SEMANTIC_COLOR:
192 case TGSI_SEMANTIC_GENERIC:
193 ctx->num_param++;
194 break;
195 default:
196 DBG("unknown PS semantic name: %s",
197 tgsi_semantic_names[name]);
198 assert(0);
199 }
200 }
201 } else if (decl->Declaration.File == TGSI_FILE_INPUT) {
202 ctx->input_export_idx[decl->Range.First] =
203 semantic_idx(&decl->Semantic);
204 }
205 ctx->num_regs[decl->Declaration.File] =
206 MAX2(ctx->num_regs[decl->Declaration.File], decl->Range.Last + 1);
207 break;
208 }
209 case TGSI_TOKEN_TYPE_IMMEDIATE: {
210 struct tgsi_full_immediate *imm =
211 &ctx->parser.FullToken.FullImmediate;
212 unsigned n = ctx->so->num_immediates++;
213 memcpy(ctx->so->immediates[n].val, imm->u, 16);
214 break;
215 }
216 default:
217 break;
218 }
219 }
220
221 /* TGSI generated immediates are always entire vec4's, ones we
222 * generate internally are not:
223 */
224 ctx->immediate_idx = ctx->so->num_immediates * 4;
225
226 ctx->so->first_immediate = ctx->num_regs[TGSI_FILE_CONSTANT];
227
228 tgsi_parse_free(&ctx->parser);
229
230 return tgsi_parse_init(&ctx->parser, so->tokens);
231 }
232
233 static void
compile_free(struct fd2_compile_context * ctx)234 compile_free(struct fd2_compile_context *ctx)
235 {
236 tgsi_parse_free(&ctx->parser);
237 }
238
239 static struct ir2_cf *
next_exec_cf(struct fd2_compile_context * ctx)240 next_exec_cf(struct fd2_compile_context *ctx)
241 {
242 struct ir2_cf *cf = ctx->cf;
243 if (!cf || cf->exec.instrs_count >= ARRAY_SIZE(ctx->cf->exec.instrs))
244 ctx->cf = cf = ir2_cf_create(ctx->so->ir, EXEC);
245 return cf;
246 }
247
248 static void
compile_vtx_fetch(struct fd2_compile_context * ctx)249 compile_vtx_fetch(struct fd2_compile_context *ctx)
250 {
251 struct ir2_instruction **vfetch_instrs = ctx->so->vfetch_instrs;
252 int i;
253 for (i = 0; i < ctx->num_regs[TGSI_FILE_INPUT]; i++) {
254 struct ir2_instruction *instr = ir2_instr_create(
255 next_exec_cf(ctx), IR2_FETCH);
256 instr->fetch.opc = VTX_FETCH;
257
258 ctx->need_sync |= 1 << (i+1);
259
260 ir2_reg_create(instr, i+1, "xyzw", 0);
261 ir2_reg_create(instr, 0, "x", 0);
262
263 if (i == 0)
264 instr->sync = true;
265
266 vfetch_instrs[i] = instr;
267 }
268 ctx->so->num_vfetch_instrs = i;
269 ctx->cf = NULL;
270 }
271
272 /*
273 * For vertex shaders (VS):
274 * --- ------ -------------
275 *
276 * Inputs: R1-R(num_input)
277 * Constants: C0-C(num_const-1)
278 * Immediates: C(num_const)-C(num_const+num_imm-1)
279 * Outputs: export0-export(n) and export62, export63
280 * n is # of outputs minus gl_Position (export62) and gl_PointSize (export63)
281 * Temps: R(num_input+1)-R(num_input+num_temps)
282 *
283 * R0 could be clobbered after the vertex fetch instructions.. so we
284 * could use it for one of the temporaries.
285 *
286 * TODO: maybe the vertex fetch part could fetch first input into R0 as
287 * the last vtx fetch instruction, which would let us use the same
288 * register layout in either case.. although this is not what the blob
289 * compiler does.
290 *
291 *
292 * For frag shaders (PS):
293 * --- ---- -------------
294 *
295 * Inputs: R0-R(num_input-1)
296 * Constants: same as VS
297 * Immediates: same as VS
298 * Outputs: export0-export(num_outputs)
299 * Temps: R(num_input)-R(num_input+num_temps-1)
300 *
301 * In either case, immediates are are postpended to the constants
302 * (uniforms).
303 *
304 */
305
306 static unsigned
get_temp_gpr(struct fd2_compile_context * ctx,int idx)307 get_temp_gpr(struct fd2_compile_context *ctx, int idx)
308 {
309 unsigned num = idx + ctx->num_regs[TGSI_FILE_INPUT];
310 if (ctx->type == PIPE_SHADER_VERTEX)
311 num++;
312 return num;
313 }
314
315 static struct ir2_register *
add_dst_reg(struct fd2_compile_context * ctx,struct ir2_instruction * alu,const struct tgsi_dst_register * dst)316 add_dst_reg(struct fd2_compile_context *ctx, struct ir2_instruction *alu,
317 const struct tgsi_dst_register *dst)
318 {
319 unsigned flags = 0, num = 0;
320 char swiz[5];
321
322 switch (dst->File) {
323 case TGSI_FILE_OUTPUT:
324 flags |= IR2_REG_EXPORT;
325 if (ctx->type == PIPE_SHADER_VERTEX) {
326 if (dst->Index == ctx->position) {
327 num = 62;
328 } else if (dst->Index == ctx->psize) {
329 num = 63;
330 } else {
331 num = export_linkage(ctx,
332 ctx->output_export_idx[dst->Index]);
333 }
334 } else {
335 num = dst->Index;
336 }
337 break;
338 case TGSI_FILE_TEMPORARY:
339 num = get_temp_gpr(ctx, dst->Index);
340 break;
341 default:
342 DBG("unsupported dst register file: %s",
343 tgsi_file_name(dst->File));
344 assert(0);
345 break;
346 }
347
348 swiz[0] = (dst->WriteMask & TGSI_WRITEMASK_X) ? 'x' : '_';
349 swiz[1] = (dst->WriteMask & TGSI_WRITEMASK_Y) ? 'y' : '_';
350 swiz[2] = (dst->WriteMask & TGSI_WRITEMASK_Z) ? 'z' : '_';
351 swiz[3] = (dst->WriteMask & TGSI_WRITEMASK_W) ? 'w' : '_';
352 swiz[4] = '\0';
353
354 return ir2_reg_create(alu, num, swiz, flags);
355 }
356
357 static struct ir2_register *
add_src_reg(struct fd2_compile_context * ctx,struct ir2_instruction * alu,const struct tgsi_src_register * src)358 add_src_reg(struct fd2_compile_context *ctx, struct ir2_instruction *alu,
359 const struct tgsi_src_register *src)
360 {
361 static const char swiz_vals[] = {
362 'x', 'y', 'z', 'w',
363 };
364 char swiz[5];
365 unsigned flags = 0, num = 0;
366
367 switch (src->File) {
368 case TGSI_FILE_CONSTANT:
369 num = src->Index;
370 flags |= IR2_REG_CONST;
371 break;
372 case TGSI_FILE_INPUT:
373 if (ctx->type == PIPE_SHADER_VERTEX) {
374 num = src->Index + 1;
375 } else {
376 num = export_linkage(ctx,
377 ctx->input_export_idx[src->Index]);
378 }
379 break;
380 case TGSI_FILE_TEMPORARY:
381 num = get_temp_gpr(ctx, src->Index);
382 break;
383 case TGSI_FILE_IMMEDIATE:
384 num = src->Index + ctx->num_regs[TGSI_FILE_CONSTANT];
385 flags |= IR2_REG_CONST;
386 break;
387 default:
388 DBG("unsupported src register file: %s",
389 tgsi_file_name(src->File));
390 assert(0);
391 break;
392 }
393
394 if (src->Absolute)
395 flags |= IR2_REG_ABS;
396 if (src->Negate)
397 flags |= IR2_REG_NEGATE;
398
399 swiz[0] = swiz_vals[src->SwizzleX];
400 swiz[1] = swiz_vals[src->SwizzleY];
401 swiz[2] = swiz_vals[src->SwizzleZ];
402 swiz[3] = swiz_vals[src->SwizzleW];
403 swiz[4] = '\0';
404
405 if ((ctx->need_sync & ((uint64_t)1 << num)) &&
406 !(flags & IR2_REG_CONST)) {
407 alu->sync = true;
408 ctx->need_sync &= ~((uint64_t)1 << num);
409 }
410
411 return ir2_reg_create(alu, num, swiz, flags);
412 }
413
414 static void
add_vector_clamp(struct tgsi_full_instruction * inst,struct ir2_instruction * alu)415 add_vector_clamp(struct tgsi_full_instruction *inst, struct ir2_instruction *alu)
416 {
417 if (inst->Instruction.Saturate) {
418 alu->alu.vector_clamp = true;
419 }
420 }
421
422 static void
add_scalar_clamp(struct tgsi_full_instruction * inst,struct ir2_instruction * alu)423 add_scalar_clamp(struct tgsi_full_instruction *inst, struct ir2_instruction *alu)
424 {
425 if (inst->Instruction.Saturate) {
426 alu->alu.scalar_clamp = true;
427 }
428 }
429
430 static void
add_regs_vector_1(struct fd2_compile_context * ctx,struct tgsi_full_instruction * inst,struct ir2_instruction * alu)431 add_regs_vector_1(struct fd2_compile_context *ctx,
432 struct tgsi_full_instruction *inst, struct ir2_instruction *alu)
433 {
434 assert(inst->Instruction.NumSrcRegs == 1);
435 assert(inst->Instruction.NumDstRegs == 1);
436
437 add_dst_reg(ctx, alu, &inst->Dst[0].Register);
438 add_src_reg(ctx, alu, &inst->Src[0].Register);
439 add_src_reg(ctx, alu, &inst->Src[0].Register);
440 add_vector_clamp(inst, alu);
441 }
442
443 static void
add_regs_vector_2(struct fd2_compile_context * ctx,struct tgsi_full_instruction * inst,struct ir2_instruction * alu)444 add_regs_vector_2(struct fd2_compile_context *ctx,
445 struct tgsi_full_instruction *inst, struct ir2_instruction *alu)
446 {
447 assert(inst->Instruction.NumSrcRegs == 2);
448 assert(inst->Instruction.NumDstRegs == 1);
449
450 add_dst_reg(ctx, alu, &inst->Dst[0].Register);
451 add_src_reg(ctx, alu, &inst->Src[0].Register);
452 add_src_reg(ctx, alu, &inst->Src[1].Register);
453 add_vector_clamp(inst, alu);
454 }
455
456 static void
add_regs_vector_3(struct fd2_compile_context * ctx,struct tgsi_full_instruction * inst,struct ir2_instruction * alu)457 add_regs_vector_3(struct fd2_compile_context *ctx,
458 struct tgsi_full_instruction *inst, struct ir2_instruction *alu)
459 {
460 assert(inst->Instruction.NumSrcRegs == 3);
461 assert(inst->Instruction.NumDstRegs == 1);
462
463 add_dst_reg(ctx, alu, &inst->Dst[0].Register);
464 /* maybe should re-arrange the syntax some day, but
465 * in assembler/disassembler and what ir.c expects
466 * is: MULADDv Rdst = Rsrc2 + Rsrc0 * Rscr1
467 */
468 add_src_reg(ctx, alu, &inst->Src[2].Register);
469 add_src_reg(ctx, alu, &inst->Src[0].Register);
470 add_src_reg(ctx, alu, &inst->Src[1].Register);
471 add_vector_clamp(inst, alu);
472 }
473
474 static void
add_regs_dummy_vector(struct ir2_instruction * alu)475 add_regs_dummy_vector(struct ir2_instruction *alu)
476 {
477 /* create dummy, non-written vector dst/src regs
478 * for unused vector instr slot:
479 */
480 ir2_reg_create(alu, 0, "____", 0); /* vector dst */
481 ir2_reg_create(alu, 0, NULL, 0); /* vector src1 */
482 ir2_reg_create(alu, 0, NULL, 0); /* vector src2 */
483 }
484
485 static void
add_regs_scalar_1(struct fd2_compile_context * ctx,struct tgsi_full_instruction * inst,struct ir2_instruction * alu)486 add_regs_scalar_1(struct fd2_compile_context *ctx,
487 struct tgsi_full_instruction *inst, struct ir2_instruction *alu)
488 {
489 assert(inst->Instruction.NumSrcRegs == 1);
490 assert(inst->Instruction.NumDstRegs == 1);
491
492 add_regs_dummy_vector(alu);
493
494 add_dst_reg(ctx, alu, &inst->Dst[0].Register);
495 add_src_reg(ctx, alu, &inst->Src[0].Register);
496 add_scalar_clamp(inst, alu);
497 }
498
499 /*
500 * Helpers for TGSI instructions that don't map to a single shader instr:
501 */
502
503 static void
src_from_dst(struct tgsi_src_register * src,struct tgsi_dst_register * dst)504 src_from_dst(struct tgsi_src_register *src, struct tgsi_dst_register *dst)
505 {
506 src->File = dst->File;
507 src->Indirect = dst->Indirect;
508 src->Dimension = dst->Dimension;
509 src->Index = dst->Index;
510 src->Absolute = 0;
511 src->Negate = 0;
512 src->SwizzleX = TGSI_SWIZZLE_X;
513 src->SwizzleY = TGSI_SWIZZLE_Y;
514 src->SwizzleZ = TGSI_SWIZZLE_Z;
515 src->SwizzleW = TGSI_SWIZZLE_W;
516 }
517
518 /* Get internal-temp src/dst to use for a sequence of instructions
519 * generated by a single TGSI op.
520 */
521 static void
get_internal_temp(struct fd2_compile_context * ctx,struct tgsi_dst_register * tmp_dst,struct tgsi_src_register * tmp_src)522 get_internal_temp(struct fd2_compile_context *ctx,
523 struct tgsi_dst_register *tmp_dst,
524 struct tgsi_src_register *tmp_src)
525 {
526 int n;
527
528 tmp_dst->File = TGSI_FILE_TEMPORARY;
529 tmp_dst->WriteMask = TGSI_WRITEMASK_XYZW;
530 tmp_dst->Indirect = 0;
531 tmp_dst->Dimension = 0;
532
533 /* assign next temporary: */
534 n = ctx->num_internal_temps++;
535 if (ctx->pred_reg != -1)
536 n++;
537
538 tmp_dst->Index = ctx->num_regs[TGSI_FILE_TEMPORARY] + n;
539
540 src_from_dst(tmp_src, tmp_dst);
541 }
542
543 static void
get_predicate(struct fd2_compile_context * ctx,struct tgsi_dst_register * dst,struct tgsi_src_register * src)544 get_predicate(struct fd2_compile_context *ctx, struct tgsi_dst_register *dst,
545 struct tgsi_src_register *src)
546 {
547 assert(ctx->pred_reg != -1);
548
549 dst->File = TGSI_FILE_TEMPORARY;
550 dst->WriteMask = TGSI_WRITEMASK_W;
551 dst->Indirect = 0;
552 dst->Dimension = 0;
553 dst->Index = get_temp_gpr(ctx, ctx->pred_reg);
554
555 if (src) {
556 src_from_dst(src, dst);
557 src->SwizzleX = TGSI_SWIZZLE_W;
558 src->SwizzleY = TGSI_SWIZZLE_W;
559 src->SwizzleZ = TGSI_SWIZZLE_W;
560 src->SwizzleW = TGSI_SWIZZLE_W;
561 }
562 }
563
564 static void
push_predicate(struct fd2_compile_context * ctx,struct tgsi_src_register * src)565 push_predicate(struct fd2_compile_context *ctx, struct tgsi_src_register *src)
566 {
567 struct ir2_instruction *alu;
568 struct tgsi_dst_register pred_dst;
569
570 /* NOTE blob compiler seems to always puts PRED_* instrs in a CF by
571 * themselves:
572 */
573 ctx->cf = NULL;
574
575 if (ctx->pred_depth == 0) {
576 /* assign predicate register: */
577 ctx->pred_reg = ctx->num_regs[TGSI_FILE_TEMPORARY];
578
579 get_predicate(ctx, &pred_dst, NULL);
580
581 alu = ir2_instr_create_alu(next_exec_cf(ctx), ~0, PRED_SETNEs);
582 add_regs_dummy_vector(alu);
583 add_dst_reg(ctx, alu, &pred_dst);
584 add_src_reg(ctx, alu, src);
585 } else {
586 struct tgsi_src_register pred_src;
587
588 get_predicate(ctx, &pred_dst, &pred_src);
589
590 alu = ir2_instr_create_alu(next_exec_cf(ctx), MULv, ~0);
591 add_dst_reg(ctx, alu, &pred_dst);
592 add_src_reg(ctx, alu, &pred_src);
593 add_src_reg(ctx, alu, src);
594
595 // XXX need to make PRED_SETE_PUSHv IR2_PRED_NONE.. but need to make
596 // sure src reg is valid if it was calculated with a predicate
597 // condition..
598 alu->pred = IR2_PRED_NONE;
599 }
600
601 /* save previous pred state to restore in pop_predicate(): */
602 ctx->pred_stack[ctx->pred_depth++] = ctx->so->ir->pred;
603
604 ctx->cf = NULL;
605 }
606
607 static void
pop_predicate(struct fd2_compile_context * ctx)608 pop_predicate(struct fd2_compile_context *ctx)
609 {
610 /* NOTE blob compiler seems to always puts PRED_* instrs in a CF by
611 * themselves:
612 */
613 ctx->cf = NULL;
614
615 /* restore previous predicate state: */
616 ctx->so->ir->pred = ctx->pred_stack[--ctx->pred_depth];
617
618 if (ctx->pred_depth != 0) {
619 struct ir2_instruction *alu;
620 struct tgsi_dst_register pred_dst;
621 struct tgsi_src_register pred_src;
622
623 get_predicate(ctx, &pred_dst, &pred_src);
624
625 alu = ir2_instr_create_alu(next_exec_cf(ctx), ~0, PRED_SET_POPs);
626 add_regs_dummy_vector(alu);
627 add_dst_reg(ctx, alu, &pred_dst);
628 add_src_reg(ctx, alu, &pred_src);
629 alu->pred = IR2_PRED_NONE;
630 } else {
631 /* predicate register no longer needed: */
632 ctx->pred_reg = -1;
633 }
634
635 ctx->cf = NULL;
636 }
637
638 static void
get_immediate(struct fd2_compile_context * ctx,struct tgsi_src_register * reg,uint32_t val)639 get_immediate(struct fd2_compile_context *ctx,
640 struct tgsi_src_register *reg, uint32_t val)
641 {
642 unsigned neg, swiz, idx, i;
643 /* actually maps 1:1 currently.. not sure if that is safe to rely on: */
644 static const unsigned swiz2tgsi[] = {
645 TGSI_SWIZZLE_X, TGSI_SWIZZLE_Y, TGSI_SWIZZLE_Z, TGSI_SWIZZLE_W,
646 };
647
648 for (i = 0; i < ctx->immediate_idx; i++) {
649 swiz = i % 4;
650 idx = i / 4;
651
652 if (ctx->so->immediates[idx].val[swiz] == val) {
653 neg = 0;
654 break;
655 }
656
657 if (ctx->so->immediates[idx].val[swiz] == -val) {
658 neg = 1;
659 break;
660 }
661 }
662
663 if (i == ctx->immediate_idx) {
664 /* need to generate a new immediate: */
665 swiz = i % 4;
666 idx = i / 4;
667 neg = 0;
668 ctx->so->immediates[idx].val[swiz] = val;
669 ctx->so->num_immediates = idx + 1;
670 ctx->immediate_idx++;
671 }
672
673 reg->File = TGSI_FILE_IMMEDIATE;
674 reg->Indirect = 0;
675 reg->Dimension = 0;
676 reg->Index = idx;
677 reg->Absolute = 0;
678 reg->Negate = neg;
679 reg->SwizzleX = swiz2tgsi[swiz];
680 reg->SwizzleY = swiz2tgsi[swiz];
681 reg->SwizzleZ = swiz2tgsi[swiz];
682 reg->SwizzleW = swiz2tgsi[swiz];
683 }
684
685 /* POW(a,b) = EXP2(b * LOG2(a)) */
686 static void
translate_pow(struct fd2_compile_context * ctx,struct tgsi_full_instruction * inst)687 translate_pow(struct fd2_compile_context *ctx,
688 struct tgsi_full_instruction *inst)
689 {
690 struct tgsi_dst_register tmp_dst;
691 struct tgsi_src_register tmp_src;
692 struct ir2_instruction *alu;
693
694 get_internal_temp(ctx, &tmp_dst, &tmp_src);
695
696 alu = ir2_instr_create_alu(next_exec_cf(ctx), ~0, LOG_CLAMP);
697 add_regs_dummy_vector(alu);
698 add_dst_reg(ctx, alu, &tmp_dst);
699 add_src_reg(ctx, alu, &inst->Src[0].Register);
700
701 alu = ir2_instr_create_alu(next_exec_cf(ctx), MULv, ~0);
702 add_dst_reg(ctx, alu, &tmp_dst);
703 add_src_reg(ctx, alu, &tmp_src);
704 add_src_reg(ctx, alu, &inst->Src[1].Register);
705
706 /* NOTE: some of the instructions, like EXP_IEEE, seem hard-
707 * coded to take their input from the w component.
708 */
709 switch(inst->Dst[0].Register.WriteMask) {
710 case TGSI_WRITEMASK_X:
711 tmp_src.SwizzleW = TGSI_SWIZZLE_X;
712 break;
713 case TGSI_WRITEMASK_Y:
714 tmp_src.SwizzleW = TGSI_SWIZZLE_Y;
715 break;
716 case TGSI_WRITEMASK_Z:
717 tmp_src.SwizzleW = TGSI_SWIZZLE_Z;
718 break;
719 case TGSI_WRITEMASK_W:
720 tmp_src.SwizzleW = TGSI_SWIZZLE_W;
721 break;
722 default:
723 DBG("invalid writemask!");
724 assert(0);
725 break;
726 }
727
728 alu = ir2_instr_create_alu(next_exec_cf(ctx), ~0, EXP_IEEE);
729 add_regs_dummy_vector(alu);
730 add_dst_reg(ctx, alu, &inst->Dst[0].Register);
731 add_src_reg(ctx, alu, &tmp_src);
732 add_scalar_clamp(inst, alu);
733 }
734
735 static void
translate_tex(struct fd2_compile_context * ctx,struct tgsi_full_instruction * inst,unsigned opc)736 translate_tex(struct fd2_compile_context *ctx,
737 struct tgsi_full_instruction *inst, unsigned opc)
738 {
739 struct ir2_instruction *instr;
740 struct ir2_register *reg;
741 struct tgsi_dst_register tmp_dst;
742 struct tgsi_src_register tmp_src;
743 const struct tgsi_src_register *coord;
744 bool using_temp = (inst->Dst[0].Register.File == TGSI_FILE_OUTPUT) ||
745 inst->Instruction.Saturate;
746 int idx;
747
748 if (using_temp || (opc == TGSI_OPCODE_TXP))
749 get_internal_temp(ctx, &tmp_dst, &tmp_src);
750
751 if (opc == TGSI_OPCODE_TXP) {
752 static const char *swiz[] = {
753 [TGSI_SWIZZLE_X] = "xxxx",
754 [TGSI_SWIZZLE_Y] = "yyyy",
755 [TGSI_SWIZZLE_Z] = "zzzz",
756 [TGSI_SWIZZLE_W] = "wwww",
757 };
758
759 /* TXP - Projective Texture Lookup:
760 *
761 * coord.x = src0.x / src.w
762 * coord.y = src0.y / src.w
763 * coord.z = src0.z / src.w
764 * coord.w = src0.w
765 * bias = 0.0
766 *
767 * dst = texture_sample(unit, coord, bias)
768 */
769 instr = ir2_instr_create_alu(next_exec_cf(ctx), MAXv, RECIP_IEEE);
770
771 /* MAXv: */
772 add_dst_reg(ctx, instr, &tmp_dst)->swizzle = "___w";
773 add_src_reg(ctx, instr, &inst->Src[0].Register);
774 add_src_reg(ctx, instr, &inst->Src[0].Register);
775
776 /* RECIP_IEEE: */
777 add_dst_reg(ctx, instr, &tmp_dst)->swizzle = "x___";
778 add_src_reg(ctx, instr, &inst->Src[0].Register)->swizzle =
779 swiz[inst->Src[0].Register.SwizzleW];
780
781 instr = ir2_instr_create_alu(next_exec_cf(ctx), MULv, ~0);
782 add_dst_reg(ctx, instr, &tmp_dst)->swizzle = "xyz_";
783 add_src_reg(ctx, instr, &tmp_src)->swizzle = "xxxx";
784 add_src_reg(ctx, instr, &inst->Src[0].Register);
785
786 coord = &tmp_src;
787 } else {
788 coord = &inst->Src[0].Register;
789 }
790
791 instr = ir2_instr_create(next_exec_cf(ctx), IR2_FETCH);
792 instr->fetch.opc = TEX_FETCH;
793 instr->fetch.is_cube = (inst->Texture.Texture == TGSI_TEXTURE_3D);
794 assert(inst->Texture.NumOffsets <= 1); // TODO what to do in other cases?
795
796 /* save off the tex fetch to be patched later with correct const_idx: */
797 idx = ctx->so->num_tfetch_instrs++;
798 ctx->so->tfetch_instrs[idx].samp_id = inst->Src[1].Register.Index;
799 ctx->so->tfetch_instrs[idx].instr = instr;
800
801 add_dst_reg(ctx, instr, using_temp ? &tmp_dst : &inst->Dst[0].Register);
802 reg = add_src_reg(ctx, instr, coord);
803
804 /* blob compiler always sets 3rd component to same as 1st for 2d: */
805 if (inst->Texture.Texture == TGSI_TEXTURE_2D)
806 reg->swizzle[2] = reg->swizzle[0];
807
808 /* dst register needs to be marked for sync: */
809 ctx->need_sync |= 1 << instr->regs[0]->num;
810
811 /* TODO we need some way to know if the tex fetch needs to sync on alu pipe.. */
812 instr->sync = true;
813
814 if (using_temp) {
815 /* texture fetch can't write directly to export, so if tgsi
816 * is telling us the dst register is in output file, we load
817 * the texture to a temp and the use ALU instruction to move
818 * to output
819 */
820 instr = ir2_instr_create_alu(next_exec_cf(ctx), MAXv, ~0);
821
822 add_dst_reg(ctx, instr, &inst->Dst[0].Register);
823 add_src_reg(ctx, instr, &tmp_src);
824 add_src_reg(ctx, instr, &tmp_src);
825 add_vector_clamp(inst, instr);
826 }
827 }
828
829 /* SGE(a,b) = GTE((b - a), 1.0, 0.0) */
830 /* SLT(a,b) = GTE((b - a), 0.0, 1.0) */
831 static void
translate_sge_slt(struct fd2_compile_context * ctx,struct tgsi_full_instruction * inst,unsigned opc)832 translate_sge_slt(struct fd2_compile_context *ctx,
833 struct tgsi_full_instruction *inst, unsigned opc)
834 {
835 struct ir2_instruction *instr;
836 struct tgsi_dst_register tmp_dst;
837 struct tgsi_src_register tmp_src;
838 struct tgsi_src_register tmp_const;
839 float c0, c1;
840
841 switch (opc) {
842 default:
843 assert(0);
844 case TGSI_OPCODE_SGE:
845 c0 = 1.0;
846 c1 = 0.0;
847 break;
848 case TGSI_OPCODE_SLT:
849 c0 = 0.0;
850 c1 = 1.0;
851 break;
852 }
853
854 get_internal_temp(ctx, &tmp_dst, &tmp_src);
855
856 instr = ir2_instr_create_alu(next_exec_cf(ctx), ADDv, ~0);
857 add_dst_reg(ctx, instr, &tmp_dst);
858 add_src_reg(ctx, instr, &inst->Src[0].Register)->flags |= IR2_REG_NEGATE;
859 add_src_reg(ctx, instr, &inst->Src[1].Register);
860
861 instr = ir2_instr_create_alu(next_exec_cf(ctx), CNDGTEv, ~0);
862 add_dst_reg(ctx, instr, &inst->Dst[0].Register);
863 /* maybe should re-arrange the syntax some day, but
864 * in assembler/disassembler and what ir.c expects
865 * is: MULADDv Rdst = Rsrc2 + Rsrc0 * Rscr1
866 */
867 get_immediate(ctx, &tmp_const, fui(c0));
868 add_src_reg(ctx, instr, &tmp_const);
869 add_src_reg(ctx, instr, &tmp_src);
870 get_immediate(ctx, &tmp_const, fui(c1));
871 add_src_reg(ctx, instr, &tmp_const);
872 }
873
874 /* LRP(a,b,c) = (a * b) + ((1 - a) * c) */
875 static void
translate_lrp(struct fd2_compile_context * ctx,struct tgsi_full_instruction * inst,unsigned opc)876 translate_lrp(struct fd2_compile_context *ctx,
877 struct tgsi_full_instruction *inst,
878 unsigned opc)
879 {
880 struct ir2_instruction *instr;
881 struct tgsi_dst_register tmp_dst1, tmp_dst2;
882 struct tgsi_src_register tmp_src1, tmp_src2;
883 struct tgsi_src_register tmp_const;
884
885 get_internal_temp(ctx, &tmp_dst1, &tmp_src1);
886 get_internal_temp(ctx, &tmp_dst2, &tmp_src2);
887
888 get_immediate(ctx, &tmp_const, fui(1.0));
889
890 /* tmp1 = (a * b) */
891 instr = ir2_instr_create_alu(next_exec_cf(ctx), MULv, ~0);
892 add_dst_reg(ctx, instr, &tmp_dst1);
893 add_src_reg(ctx, instr, &inst->Src[0].Register);
894 add_src_reg(ctx, instr, &inst->Src[1].Register);
895
896 /* tmp2 = (1 - a) */
897 instr = ir2_instr_create_alu(next_exec_cf(ctx), ADDv, ~0);
898 add_dst_reg(ctx, instr, &tmp_dst2);
899 add_src_reg(ctx, instr, &tmp_const);
900 add_src_reg(ctx, instr, &inst->Src[0].Register)->flags |= IR2_REG_NEGATE;
901
902 /* tmp2 = tmp2 * c */
903 instr = ir2_instr_create_alu(next_exec_cf(ctx), MULv, ~0);
904 add_dst_reg(ctx, instr, &tmp_dst2);
905 add_src_reg(ctx, instr, &tmp_src2);
906 add_src_reg(ctx, instr, &inst->Src[2].Register);
907
908 /* dst = tmp1 + tmp2 */
909 instr = ir2_instr_create_alu(next_exec_cf(ctx), ADDv, ~0);
910 add_dst_reg(ctx, instr, &inst->Dst[0].Register);
911 add_src_reg(ctx, instr, &tmp_src1);
912 add_src_reg(ctx, instr, &tmp_src2);
913 }
914
915 static void
translate_trig(struct fd2_compile_context * ctx,struct tgsi_full_instruction * inst,unsigned opc)916 translate_trig(struct fd2_compile_context *ctx,
917 struct tgsi_full_instruction *inst,
918 unsigned opc)
919 {
920 struct ir2_instruction *instr;
921 struct tgsi_dst_register tmp_dst;
922 struct tgsi_src_register tmp_src;
923 struct tgsi_src_register tmp_const;
924 instr_scalar_opc_t op;
925
926 switch (opc) {
927 default:
928 assert(0);
929 case TGSI_OPCODE_SIN:
930 op = SIN;
931 break;
932 case TGSI_OPCODE_COS:
933 op = COS;
934 break;
935 }
936
937 get_internal_temp(ctx, &tmp_dst, &tmp_src);
938
939 tmp_dst.WriteMask = TGSI_WRITEMASK_X;
940 tmp_src.SwizzleX = tmp_src.SwizzleY =
941 tmp_src.SwizzleZ = tmp_src.SwizzleW = TGSI_SWIZZLE_X;
942
943 /* maybe should re-arrange the syntax some day, but
944 * in assembler/disassembler and what ir.c expects
945 * is: MULADDv Rdst = Rsrc2 + Rsrc0 * Rscr1
946 */
947 instr = ir2_instr_create_alu(next_exec_cf(ctx), MULADDv, ~0);
948 add_dst_reg(ctx, instr, &tmp_dst);
949 get_immediate(ctx, &tmp_const, fui(0.5));
950 add_src_reg(ctx, instr, &tmp_const);
951 add_src_reg(ctx, instr, &inst->Src[0].Register);
952 get_immediate(ctx, &tmp_const, fui(0.159155));
953 add_src_reg(ctx, instr, &tmp_const);
954
955 instr = ir2_instr_create_alu(next_exec_cf(ctx), FRACv, ~0);
956 add_dst_reg(ctx, instr, &tmp_dst);
957 add_src_reg(ctx, instr, &tmp_src);
958 add_src_reg(ctx, instr, &tmp_src);
959
960 instr = ir2_instr_create_alu(next_exec_cf(ctx), MULADDv, ~0);
961 add_dst_reg(ctx, instr, &tmp_dst);
962 get_immediate(ctx, &tmp_const, fui(-3.141593));
963 add_src_reg(ctx, instr, &tmp_const);
964 add_src_reg(ctx, instr, &tmp_src);
965 get_immediate(ctx, &tmp_const, fui(6.283185));
966 add_src_reg(ctx, instr, &tmp_const);
967
968 instr = ir2_instr_create_alu(next_exec_cf(ctx), ~0, op);
969 add_regs_dummy_vector(instr);
970 add_dst_reg(ctx, instr, &inst->Dst[0].Register);
971 add_src_reg(ctx, instr, &tmp_src);
972 }
973
974 /*
975 * Main part of compiler/translator:
976 */
977
978 static void
translate_instruction(struct fd2_compile_context * ctx,struct tgsi_full_instruction * inst)979 translate_instruction(struct fd2_compile_context *ctx,
980 struct tgsi_full_instruction *inst)
981 {
982 unsigned opc = inst->Instruction.Opcode;
983 struct ir2_instruction *instr;
984 static struct ir2_cf *cf;
985
986 if (opc == TGSI_OPCODE_END)
987 return;
988
989 if (inst->Dst[0].Register.File == TGSI_FILE_OUTPUT) {
990 unsigned num = inst->Dst[0].Register.Index;
991 /* seems like we need to ensure that position vs param/pixel
992 * exports don't end up in the same EXEC clause.. easy way
993 * to do this is force a new EXEC clause on first appearance
994 * of an position or param/pixel export.
995 */
996 if ((num == ctx->position) || (num == ctx->psize)) {
997 if (ctx->num_position > 0) {
998 ctx->cf = NULL;
999 ir2_cf_create_alloc(ctx->so->ir, SQ_POSITION,
1000 ctx->num_position - 1);
1001 ctx->num_position = 0;
1002 }
1003 } else {
1004 if (ctx->num_param > 0) {
1005 ctx->cf = NULL;
1006 ir2_cf_create_alloc(ctx->so->ir, SQ_PARAMETER_PIXEL,
1007 ctx->num_param - 1);
1008 ctx->num_param = 0;
1009 }
1010 }
1011 }
1012
1013 cf = next_exec_cf(ctx);
1014
1015 /* TODO turn this into a table: */
1016 switch (opc) {
1017 case TGSI_OPCODE_MOV:
1018 instr = ir2_instr_create_alu(cf, MAXv, ~0);
1019 add_regs_vector_1(ctx, inst, instr);
1020 break;
1021 case TGSI_OPCODE_RCP:
1022 instr = ir2_instr_create_alu(cf, ~0, RECIP_IEEE);
1023 add_regs_scalar_1(ctx, inst, instr);
1024 break;
1025 case TGSI_OPCODE_RSQ:
1026 instr = ir2_instr_create_alu(cf, ~0, RECIPSQ_IEEE);
1027 add_regs_scalar_1(ctx, inst, instr);
1028 break;
1029 case TGSI_OPCODE_SQRT:
1030 instr = ir2_instr_create_alu(cf, ~0, SQRT_IEEE);
1031 add_regs_scalar_1(ctx, inst, instr);
1032 break;
1033 case TGSI_OPCODE_MUL:
1034 instr = ir2_instr_create_alu(cf, MULv, ~0);
1035 add_regs_vector_2(ctx, inst, instr);
1036 break;
1037 case TGSI_OPCODE_ADD:
1038 instr = ir2_instr_create_alu(cf, ADDv, ~0);
1039 add_regs_vector_2(ctx, inst, instr);
1040 break;
1041 case TGSI_OPCODE_DP3:
1042 instr = ir2_instr_create_alu(cf, DOT3v, ~0);
1043 add_regs_vector_2(ctx, inst, instr);
1044 break;
1045 case TGSI_OPCODE_DP4:
1046 instr = ir2_instr_create_alu(cf, DOT4v, ~0);
1047 add_regs_vector_2(ctx, inst, instr);
1048 break;
1049 case TGSI_OPCODE_MIN:
1050 instr = ir2_instr_create_alu(cf, MINv, ~0);
1051 add_regs_vector_2(ctx, inst, instr);
1052 break;
1053 case TGSI_OPCODE_MAX:
1054 instr = ir2_instr_create_alu(cf, MAXv, ~0);
1055 add_regs_vector_2(ctx, inst, instr);
1056 break;
1057 case TGSI_OPCODE_SLT:
1058 case TGSI_OPCODE_SGE:
1059 translate_sge_slt(ctx, inst, opc);
1060 break;
1061 case TGSI_OPCODE_MAD:
1062 instr = ir2_instr_create_alu(cf, MULADDv, ~0);
1063 add_regs_vector_3(ctx, inst, instr);
1064 break;
1065 case TGSI_OPCODE_LRP:
1066 translate_lrp(ctx, inst, opc);
1067 break;
1068 case TGSI_OPCODE_FRC:
1069 instr = ir2_instr_create_alu(cf, FRACv, ~0);
1070 add_regs_vector_1(ctx, inst, instr);
1071 break;
1072 case TGSI_OPCODE_FLR:
1073 instr = ir2_instr_create_alu(cf, FLOORv, ~0);
1074 add_regs_vector_1(ctx, inst, instr);
1075 break;
1076 case TGSI_OPCODE_EX2:
1077 instr = ir2_instr_create_alu(cf, ~0, EXP_IEEE);
1078 add_regs_scalar_1(ctx, inst, instr);
1079 break;
1080 case TGSI_OPCODE_POW:
1081 translate_pow(ctx, inst);
1082 break;
1083 case TGSI_OPCODE_COS:
1084 case TGSI_OPCODE_SIN:
1085 translate_trig(ctx, inst, opc);
1086 break;
1087 case TGSI_OPCODE_TEX:
1088 case TGSI_OPCODE_TXP:
1089 translate_tex(ctx, inst, opc);
1090 break;
1091 case TGSI_OPCODE_CMP:
1092 instr = ir2_instr_create_alu(cf, CNDGTEv, ~0);
1093 add_regs_vector_3(ctx, inst, instr);
1094 // TODO this should be src0 if regs where in sane order..
1095 instr->regs[2]->flags ^= IR2_REG_NEGATE; /* src1 */
1096 break;
1097 case TGSI_OPCODE_IF:
1098 push_predicate(ctx, &inst->Src[0].Register);
1099 ctx->so->ir->pred = IR2_PRED_EQ;
1100 break;
1101 case TGSI_OPCODE_ELSE:
1102 ctx->so->ir->pred = IR2_PRED_NE;
1103 /* not sure if this is required in all cases, but blob compiler
1104 * won't combine EQ and NE in same CF:
1105 */
1106 ctx->cf = NULL;
1107 break;
1108 case TGSI_OPCODE_ENDIF:
1109 pop_predicate(ctx);
1110 break;
1111 case TGSI_OPCODE_F2I:
1112 instr = ir2_instr_create_alu(cf, TRUNCv, ~0);
1113 add_regs_vector_1(ctx, inst, instr);
1114 break;
1115 default:
1116 DBG("unknown TGSI opc: %s", tgsi_get_opcode_name(opc));
1117 tgsi_dump(ctx->so->tokens, 0);
1118 assert(0);
1119 break;
1120 }
1121
1122 /* internal temporaries are only valid for the duration of a single
1123 * TGSI instruction:
1124 */
1125 ctx->num_internal_temps = 0;
1126 }
1127
1128 static void
compile_instructions(struct fd2_compile_context * ctx)1129 compile_instructions(struct fd2_compile_context *ctx)
1130 {
1131 while (!tgsi_parse_end_of_tokens(&ctx->parser)) {
1132 tgsi_parse_token(&ctx->parser);
1133
1134 switch (ctx->parser.FullToken.Token.Type) {
1135 case TGSI_TOKEN_TYPE_INSTRUCTION:
1136 translate_instruction(ctx,
1137 &ctx->parser.FullToken.FullInstruction);
1138 break;
1139 default:
1140 break;
1141 }
1142 }
1143
1144 ctx->cf->cf_type = EXEC_END;
1145 }
1146
1147 int
fd2_compile_shader(struct fd_program_stateobj * prog,struct fd2_shader_stateobj * so)1148 fd2_compile_shader(struct fd_program_stateobj *prog,
1149 struct fd2_shader_stateobj *so)
1150 {
1151 struct fd2_compile_context ctx;
1152
1153 ir2_shader_destroy(so->ir);
1154 so->ir = ir2_shader_create();
1155 so->num_vfetch_instrs = so->num_tfetch_instrs = so->num_immediates = 0;
1156
1157 if (compile_init(&ctx, prog, so) != TGSI_PARSE_OK)
1158 return -1;
1159
1160 if (ctx.type == PIPE_SHADER_VERTEX) {
1161 compile_vtx_fetch(&ctx);
1162 } else if (ctx.type == PIPE_SHADER_FRAGMENT) {
1163 prog->num_exports = 0;
1164 memset(prog->export_linkage, 0xff,
1165 sizeof(prog->export_linkage));
1166 }
1167
1168 compile_instructions(&ctx);
1169
1170 compile_free(&ctx);
1171
1172 return 0;
1173 }
1174
1175