2 * Mesa 3-D graphics library
5 * Copyright (C) 2005-2007 Brian Paul All Rights Reserved.
7 * Permission is hereby granted, free of charge, to any person obtaining a
8 * copy of this software and associated documentation files (the "Software"),
9 * to deal in the Software without restriction, including without limitation
10 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
11 * and/or sell copies of the Software, and to permit persons to whom the
12 * Software is furnished to do so, subject to the following conditions:
14 * The above copyright notice and this permission notice shall be included
15 * in all copies or substantial portions of the Software.
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
18 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
20 * BRIAN PAUL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
21 * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
22 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
27 * Emit program instructions (PI code) from IR trees.
35 #include "prog_instruction.h"
36 #include "prog_parameter.h"
37 #include "slang_emit.h"
41 * Assembly and IR info
45 slang_ir_opcode IrOpcode
;
47 gl_inst_opcode InstOpcode
;
48 GLuint ResultSize
, NumParams
;
53 static slang_ir_info IrInfo
[] = {
55 { IR_ADD
, "IR_ADD", OPCODE_ADD
, 4, 2 },
56 { IR_SUB
, "IR_SUB", OPCODE_SUB
, 4, 2 },
57 { IR_MUL
, "IR_MUL", OPCODE_MUL
, 4, 2 },
58 { IR_DIV
, "IR_DIV", OPCODE_NOP
, 0, 2 }, /* XXX broke */
59 { IR_DOT4
, "IR_DOT_4", OPCODE_DP4
, 1, 2 },
60 { IR_DOT3
, "IR_DOT_3", OPCODE_DP3
, 1, 2 },
61 { IR_CROSS
, "IR_CROSS", OPCODE_XPD
, 3, 2 },
62 { IR_MIN
, "IR_MIN", OPCODE_MIN
, 4, 2 },
63 { IR_MAX
, "IR_MAX", OPCODE_MAX
, 4, 2 },
64 { IR_SEQUAL
, "IR_SEQUAL", OPCODE_SEQ
, 4, 2 },
65 { IR_SNEQUAL
, "IR_SNEQUAL", OPCODE_SNE
, 4, 2 },
66 { IR_SGE
, "IR_SGE", OPCODE_SGE
, 4, 2 },
67 { IR_SGT
, "IR_SGT", OPCODE_SGT
, 4, 2 },
68 { IR_POW
, "IR_POW", OPCODE_POW
, 1, 2 },
70 { IR_I_TO_F
, "IR_I_TO_F", OPCODE_NOP
, 1, 1 },
71 { IR_EXP
, "IR_EXP", OPCODE_EXP
, 1, 1 },
72 { IR_EXP2
, "IR_EXP2", OPCODE_EX2
, 1, 1 },
73 { IR_LOG2
, "IR_LOG2", OPCODE_LG2
, 1, 1 },
74 { IR_RSQ
, "IR_RSQ", OPCODE_RSQ
, 1, 1 },
75 { IR_RCP
, "IR_RCP", OPCODE_RCP
, 1, 1 },
76 { IR_FLOOR
, "IR_FLOOR", OPCODE_FLR
, 4, 1 },
77 { IR_FRAC
, "IR_FRAC", OPCODE_FRC
, 4, 1 },
78 { IR_ABS
, "IR_ABS", OPCODE_ABS
, 4, 1 },
79 { IR_NEG
, "IR_NEG", 0/*spec case*/, 4, 1 },
80 { IR_SIN
, "IR_SIN", OPCODE_SIN
, 1, 1 },
81 { IR_COS
, "IR_COS", OPCODE_COS
, 1, 1 },
83 { IR_SEQ
, "IR_SEQ", 0, 0, 0 },
84 { IR_LABEL
, "IR_LABEL", 0, 0, 0 },
85 { IR_JUMP
, "IR_JUMP", 0, 0, 0 },
86 { IR_CJUMP
, "IR_CJUMP", 0, 0, 0 },
87 { IR_COND
, "IR_COND", 0, 0, 0 },
88 { IR_CALL
, "IR_CALL", 0, 0, 0 },
89 { IR_MOVE
, "IR_MOVE", 0, 0, 1 },
90 { IR_NOT
, "IR_NOT", 0, 1, 1 },
91 { IR_VAR
, "IR_VAR", 0, 0, 0 },
92 { IR_VAR_DECL
, "IR_VAR_DECL", 0, 0, 0 },
93 { IR_TEX
, "IR_TEX", OPCODE_TEX
, 4, 1 },
94 { IR_TEXB
, "IR_TEXB", OPCODE_TXB
, 4, 2 },
95 { IR_FLOAT
, "IR_FLOAT", 0, 0, 0 },
96 { IR_FIELD
, "IR_FIELD", 0, 0, 0 },
97 { IR_NOP
, NULL
, OPCODE_NOP
, 0, 0 }
101 static slang_ir_info
*
102 slang_find_ir_info(slang_ir_opcode opcode
)
105 for (i
= 0; IrInfo
[i
].IrName
; i
++) {
106 if (IrInfo
[i
].IrOpcode
== opcode
) {
114 slang_ir_name(slang_ir_opcode opcode
)
116 return slang_find_ir_info(opcode
)->IrName
;
121 _slang_new_ir_storage(enum register_file file
, GLint index
, GLint size
)
123 slang_ir_storage
*st
;
124 st
= (slang_ir_storage
*) _mesa_calloc(sizeof(slang_ir_storage
));
135 _slang_clone_ir_storage(slang_ir_storage
*store
)
137 slang_ir_storage
*clone
138 = _slang_new_ir_storage(store
->File
, store
->Index
, store
->Size
);
144 swizzle_string(GLuint swizzle
)
149 for (i
= 1; i
< 5; i
++) {
150 s
[i
] = "xyzw"[GET_SWZ(swizzle
, i
-1)];
157 writemask_string(GLuint writemask
)
162 for (i
= 0; i
< 4; i
++) {
163 if (writemask
& (1 << i
))
171 storage_string(const slang_ir_storage
*st
)
173 static const char *files
[] = {
191 sprintf(s
, "%s[%d]", files
[st
->File
], st
->Index
);
193 sprintf(s
, "%s[%d..%d]", files
[st
->File
], st
->Index
,
194 st
->Index
+ st
->Size
- 1);
196 assert(st
->File
< sizeof(files
) / sizeof(files
[0]));
197 sprintf(s
, "%s[%d]", files
[st
->File
], st
->Index
);
204 slang_print_ir(const slang_ir_node
*n
, int indent
)
210 if (n
->Opcode
!= IR_SEQ
)
212 printf("%3d:", indent
);
214 for (i
= 0; i
< indent
; i
++)
220 printf("SEQ at %p\n", (void*) n
);
222 assert(n
->Children
[0]);
223 assert(n
->Children
[1]);
224 slang_print_ir(n
->Children
[0], indent
+ IND
);
225 slang_print_ir(n
->Children
[1], indent
+ IND
);
228 printf("MOVE (writemask = %s)\n", writemask_string(n
->Writemask
));
229 slang_print_ir(n
->Children
[0], indent
+3);
230 slang_print_ir(n
->Children
[1], indent
+3);
233 printf("LABEL: %s\n", n
->Target
);
237 slang_print_ir(n
->Children
[0], indent
+ 3);
240 printf("JUMP %s\n", n
->Target
);
243 printf("CJUMP %s\n", n
->Target
);
244 slang_print_ir(n
->Children
[0], indent
+3);
247 printf("VAR %s%s at %s store %p\n",
248 (char *) n
->Var
->a_name
, swizzle_string(n
->Swizzle
),
249 storage_string(n
->Store
), (void*) n
->Store
);
252 printf("VAR_DECL %s (%p) at %s store %p\n",
253 (char *) n
->Var
->a_name
, (void*) n
->Var
, storage_string(n
->Store
),
257 printf("FIELD %s of\n", n
->Target
);
258 slang_print_ir(n
->Children
[0], indent
+3);
261 printf("ASMCALL %s(%d args)\n", n
->Target
, n
->Swizzle
);
264 printf("FLOAT %f %f %f %f\n",
265 n
->Value
[0], n
->Value
[1], n
->Value
[2], n
->Value
[3]);
268 printf("INT_TO_FLOAT %d\n", (int) n
->Value
[0]);
271 printf("%s (%p, %p)\n", slang_ir_name(n
->Opcode
),
272 (void*) n
->Children
[0], (void*) n
->Children
[1]);
273 slang_print_ir(n
->Children
[0], indent
+3);
274 slang_print_ir(n
->Children
[1], indent
+3);
280 _slang_alloc_temporary(slang_gen_context
*gc
, GLint size
)
282 const GLuint sz4
= (size
+ 3) / 4;
284 ASSERT(size
> 0); /* number of floats */
285 for (i
= 0; i
< MAX_PROGRAM_TEMPS
; i
++) {
287 for (j
= 0; j
< sz4
; j
++) {
288 if (!gc
->TempUsed
[i
+ j
]) {
293 /* found block of size/4 free regs */
294 for (j
= 0; j
< sz4
; j
++)
295 gc
->TempUsed
[i
+ j
] = GL_TRUE
;
305 is_temporary(const slang_gen_context
*gc
, const slang_ir_storage
*st
)
307 if (st
->File
== PROGRAM_TEMPORARY
&& gc
->TempUsed
[st
->Index
])
308 return gc
->TempUsed
[st
->Index
];
315 free_temporary(slang_gen_context
*gc
, GLuint r
, GLint size
)
317 const GLuint sz4
= (size
+ 3) / 4;
319 for (i
= 0; i
< sz4
; i
++) {
320 if (gc
->TempUsed
[r
+ i
])
321 gc
->TempUsed
[r
+ i
] = GL_FALSE
;
327 * Allocate temporary storage for an intermediate result (such as for
328 * a multiply or add, etc.
331 slang_alloc_temp_storage(slang_gen_context
*gc
, slang_ir_node
*n
, GLint size
)
337 indx
= _slang_alloc_temporary(gc
, size
);
338 n
->Store
= _slang_new_ir_storage(PROGRAM_TEMPORARY
, indx
, size
);
342 static slang_ir_storage
*
343 alloc_constant(const GLfloat v
[], GLuint size
, struct gl_program
*prog
)
346 GLint ind
= _mesa_add_unnamed_constant(prog
->Parameters
, v
, size
, &swizzle
);
347 slang_ir_storage
*st
= _slang_new_ir_storage(PROGRAM_CONSTANT
, ind
, size
);
357 swizzle_compose(GLuint swz1
, GLuint swz2
)
360 for (i
= 0; i
< 4; i
++) {
361 GLuint c
= GET_SWZ(swz1
, i
);
362 s
[i
] = GET_SWZ(swz2
, c
);
364 swz
= MAKE_SWIZZLE4(s
[0], s
[1], s
[2], s
[3]);
371 * Convert IR storage to an instruction dst register.
374 storage_to_dst_reg(struct prog_dst_register
*dst
, const slang_ir_storage
*st
,
377 static const GLuint defaultWritemask
[4] = {
379 WRITEMASK_X
| WRITEMASK_Y
,
380 WRITEMASK_X
| WRITEMASK_Y
| WRITEMASK_Z
,
381 WRITEMASK_X
| WRITEMASK_Y
| WRITEMASK_Z
| WRITEMASK_W
383 dst
->File
= st
->File
;
384 dst
->Index
= st
->Index
;
385 assert(st
->File
!= PROGRAM_UNDEFINED
);
386 assert(st
->Size
>= 1);
387 assert(st
->Size
<= 4);
388 dst
->WriteMask
= defaultWritemask
[st
->Size
- 1] & writemask
;
393 * Convert IR storage to an instruction src register.
396 storage_to_src_reg(struct prog_src_register
*src
, const slang_ir_storage
*st
,
399 static const GLuint defaultSwizzle
[4] = {
400 MAKE_SWIZZLE4(SWIZZLE_X
, SWIZZLE_X
, SWIZZLE_X
, SWIZZLE_X
),
401 MAKE_SWIZZLE4(SWIZZLE_X
, SWIZZLE_Y
, SWIZZLE_Z
, SWIZZLE_W
),
402 MAKE_SWIZZLE4(SWIZZLE_X
, SWIZZLE_Y
, SWIZZLE_Z
, SWIZZLE_W
),
403 MAKE_SWIZZLE4(SWIZZLE_X
, SWIZZLE_Y
, SWIZZLE_Z
, SWIZZLE_W
)
406 src
->File
= st
->File
;
407 src
->Index
= st
->Index
;
408 assert(st
->File
!= PROGRAM_UNDEFINED
);
409 assert(st
->Size
>= 1);
410 assert(st
->Size
<= 4);
411 /* XXX swizzling logic here may need some work */
412 /*src->Swizzle = swizzle_compose(swizzle, defaultSwizzle[st->Size - 1]);*/
413 if (swizzle
!= SWIZZLE_NOOP
)
414 src
->Swizzle
= swizzle
;
416 src
->Swizzle
= defaultSwizzle
[st
->Size
- 1];
422 * Add new instruction at end of given program.
423 * \param prog the program to append instruction onto
424 * \param opcode opcode for the new instruction
425 * \return pointer to the new instruction
427 static struct prog_instruction
*
428 new_instruction(struct gl_program
*prog
, gl_inst_opcode opcode
)
430 struct prog_instruction
*inst
;
431 prog
->Instructions
= _mesa_realloc_instructions(prog
->Instructions
,
432 prog
->NumInstructions
,
433 prog
->NumInstructions
+ 1);
434 inst
= prog
->Instructions
+ prog
->NumInstructions
;
435 prog
->NumInstructions
++;
436 _mesa_init_instructions(inst
, 1);
437 inst
->Opcode
= opcode
;
442 static struct prog_instruction
*
443 emit(slang_gen_context
*gc
, slang_ir_node
*n
, struct gl_program
*prog
);
447 * Generate code for a simple binary-op instruction.
449 static struct prog_instruction
*
450 emit_binop(slang_gen_context
*gc
, slang_ir_node
*n
, struct gl_program
*prog
)
452 struct prog_instruction
*inst
;
453 const slang_ir_info
*info
= slang_find_ir_info(n
->Opcode
);
456 assert(info
->InstOpcode
!= OPCODE_NOP
);
458 emit(gc
, n
->Children
[0], prog
);
459 emit(gc
, n
->Children
[1], prog
);
460 inst
= new_instruction(prog
, info
->InstOpcode
);
461 /* alloc temp storage for the result: */
462 if (!n
->Store
|| n
->Store
->File
== PROGRAM_UNDEFINED
) {
463 slang_alloc_temp_storage(gc
, n
, info
->ResultSize
);
465 storage_to_dst_reg(&inst
->DstReg
, n
->Store
, n
->Writemask
);
466 storage_to_src_reg(&inst
->SrcReg
[0], n
->Children
[0]->Store
,
467 n
->Children
[0]->Swizzle
);
468 storage_to_src_reg(&inst
->SrcReg
[1], n
->Children
[1]->Store
,
469 n
->Children
[1]->Swizzle
);
470 inst
->Comment
= n
->Comment
;
475 static struct prog_instruction
*
476 emit_unop(slang_gen_context
*gc
, slang_ir_node
*n
, struct gl_program
*prog
)
478 struct prog_instruction
*inst
;
479 const slang_ir_info
*info
= slang_find_ir_info(n
->Opcode
);
482 assert(info
->NumParams
== 1);
484 emit(gc
, n
->Children
[0], prog
);
486 inst
= new_instruction(prog
, info
->InstOpcode
);
489 slang_alloc_temp_storage(gc
, n
, info
->ResultSize
);
491 storage_to_dst_reg(&inst
->DstReg
, n
->Store
, n
->Writemask
);
493 storage_to_src_reg(&inst
->SrcReg
[0], n
->Children
[0]->Store
,
494 n
->Children
[0]->Swizzle
);
496 inst
->Comment
= n
->Comment
;
502 static struct prog_instruction
*
503 emit_negation(slang_gen_context
*gc
, slang_ir_node
*n
, struct gl_program
*prog
)
505 /* Implement as MOV dst, -src; */
506 /* XXX we could look at the previous instruction and in some circumstances
507 * modify it to accomplish the negation.
509 struct prog_instruction
*inst
;
511 emit(gc
, n
->Children
[0], prog
);
514 slang_alloc_temp_storage(gc
, n
, n
->Children
[0]->Store
->Size
);
516 inst
= new_instruction(prog
, OPCODE_MOV
);
517 storage_to_dst_reg(&inst
->DstReg
, n
->Store
, n
->Writemask
);
518 storage_to_src_reg(&inst
->SrcReg
[0], n
->Children
[0]->Store
,
519 n
->Children
[0]->Swizzle
);
520 inst
->SrcReg
[0].NegateBase
= NEGATE_XYZW
;
521 inst
->Comment
= n
->Comment
;
526 static struct prog_instruction
*
527 emit_label(const char *target
, struct gl_program
*prog
)
529 struct prog_instruction
*inst
;
530 inst
= new_instruction(prog
, OPCODE_NOP
);
531 inst
->Comment
= _mesa_strdup(target
);
536 static struct prog_instruction
*
537 emit_cjump(const char *target
, struct gl_program
*prog
)
539 struct prog_instruction
*inst
;
540 inst
= new_instruction(prog
, OPCODE_BRA
);
541 inst
->DstReg
.CondMask
= COND_EQ
; /* branch if equal to zero */
542 inst
->DstReg
.CondSwizzle
= SWIZZLE_X
;
543 inst
->Comment
= _mesa_strdup(target
);
548 static struct prog_instruction
*
549 emit_jump(const char *target
, struct gl_program
*prog
)
551 struct prog_instruction
*inst
;
552 inst
= new_instruction(prog
, OPCODE_BRA
);
553 inst
->DstReg
.CondMask
= COND_TR
; /* always branch */
554 /*inst->DstReg.CondSwizzle = SWIZZLE_X;*/
555 inst
->Comment
= _mesa_strdup(target
);
560 static struct prog_instruction
*
561 emit_tex(slang_gen_context
*gc
, slang_ir_node
*n
, struct gl_program
*prog
)
563 struct prog_instruction
*inst
;
564 if (n
->Opcode
== IR_TEX
) {
565 inst
= new_instruction(prog
, OPCODE_TEX
);
568 assert(n
->Opcode
== IR_TEXB
);
569 inst
= new_instruction(prog
, OPCODE_TXB
);
573 slang_alloc_temp_storage(gc
, n
, 4);
575 storage_to_dst_reg(&inst
->DstReg
, n
->Store
, n
->Writemask
);
577 /* Child[1] is the coord */
578 storage_to_src_reg(&inst
->SrcReg
[0], n
->Children
[1]->Store
,
579 n
->Children
[1]->Swizzle
);
581 /* Child[0] is the sampler (a uniform which'll indicate the texture unit) */
582 assert(n
->Children
[0]->Store
);
583 assert(n
->Children
[0]->Store
->Size
>= TEXTURE_1D_INDEX
);
585 inst
->Sampler
= n
->Children
[0]->Store
->Index
; /* i.e. uniform's index */
586 inst
->TexSrcTarget
= n
->Children
[0]->Store
->Size
;
587 inst
->TexSrcUnit
= 27; /* Dummy value; the TexSrcUnit will be computed at
588 * link time, using the sampler uniform's value.
594 static struct prog_instruction
*
595 emit(slang_gen_context
*gc
, slang_ir_node
*n
, struct gl_program
*prog
)
597 struct prog_instruction
*inst
;
603 assert(n
->Children
[0]);
604 assert(n
->Children
[1]);
605 emit(gc
, n
->Children
[0], prog
);
606 inst
= emit(gc
, n
->Children
[1], prog
);
607 n
->Store
= n
->Children
[1]->Store
;
612 /* Storage should have already been resolved/allocated */
614 assert(n
->Store
->File
!= PROGRAM_UNDEFINED
);
615 assert(n
->Store
->Index
>= 0);
616 assert(n
->Store
->Size
> 0);
620 assert(n
->Children
[1]);
621 inst
= emit(gc
, n
->Children
[1], prog
);
623 emit(gc
, n
->Children
[0], prog
);
626 if (inst
&& is_temporary(gc
, n
->Children
[1]->Store
)) {
627 /* Peephole optimization:
628 * Just modify the RHS to put its result into the dest of this
629 * MOVE operation. Then, this MOVE is a no-op.
631 free_temporary(gc
, n
->Children
[1]->Store
->Index
,
632 n
->Children
[1]->Store
->Size
);
633 *n
->Children
[1]->Store
= *n
->Children
[0]->Store
;
634 /* fixup the prev (RHS) instruction */
635 storage_to_dst_reg(&inst
->DstReg
, n
->Children
[0]->Store
, n
->Writemask
);
641 if (n
->Children
[0]->Store
->Size
> 4) {
642 /* move matrix/struct etc */
643 slang_ir_storage dstStore
= *n
->Children
[0]->Store
;
644 slang_ir_storage srcStore
= *n
->Children
[1]->Store
;
645 GLint size
= srcStore
.Size
;
646 ASSERT(n
->Children
[0]->Writemask
== WRITEMASK_XYZW
);
647 ASSERT(n
->Children
[1]->Swizzle
== SWIZZLE_NOOP
);
651 inst
= new_instruction(prog
, OPCODE_MOV
);
652 inst
->Comment
= _mesa_strdup("IR_MOVE block");
653 storage_to_dst_reg(&inst
->DstReg
, &dstStore
, n
->Writemask
);
654 storage_to_src_reg(&inst
->SrcReg
[0], &srcStore
,
655 n
->Children
[1]->Swizzle
);
662 inst
= new_instruction(prog
, OPCODE_MOV
);
663 storage_to_dst_reg(&inst
->DstReg
, n
->Children
[0]->Store
, n
->Writemask
);
664 storage_to_src_reg(&inst
->SrcReg
[0], n
->Children
[1]->Store
,
665 n
->Children
[1]->Swizzle
);
667 /* XXX is this test correct? */
668 if (n
->Children
[1]->Store
->File
== PROGRAM_TEMPORARY
) {
669 free_temporary(gc
, n
->Children
[1]->Store
->Index
,
670 n
->Children
[1]->Store
->Size
);
672 /*inst->Comment = _mesa_strdup("IR_MOVE");*/
673 n
->Store
= n
->Children
[0]->Store
; /*XXX new */
692 return emit_binop(gc
, n
, prog
);
700 return emit_unop(gc
, n
, prog
);
703 return emit_tex(gc
, n
, prog
);
705 return emit_negation(gc
, n
, prog
);
707 return emit_label(n
->Target
, prog
);
709 n
->Store
= alloc_constant(n
->Value
, 4, prog
); /*XXX fix size */
713 /* Conditional expression (in if/while/for stmts).
714 * Need to update condition code register.
715 * Next instruction is typically an IR_CJUMP.
717 /* last child expr instruction: */
718 struct prog_instruction
*inst
= emit(gc
, n
->Children
[0], prog
);
720 /* set inst's CondUpdate flag */
721 inst
->CondUpdate
= GL_TRUE
;
722 return inst
; /* XXX or null? */
725 /* This'll happen for things like "if (i) ..." where no code
726 * is normally generated for the expression "i".
727 * Generate a move instruction just to set condition codes.
729 slang_alloc_temp_storage(gc
, n
, 1);
730 inst
= new_instruction(prog
, OPCODE_MOV
);
731 inst
->CondUpdate
= GL_TRUE
;
732 storage_to_dst_reg(&inst
->DstReg
, n
->Store
, n
->Writemask
);
733 storage_to_src_reg(&inst
->SrcReg
[0], n
->Children
[0]->Store
,
734 n
->Children
[0]->Swizzle
);
735 free_temporary(gc
, n
->Store
->Index
, n
->Store
->Size
);
736 return inst
; /* XXX or null? */
741 return emit_jump(n
->Target
, prog
);
743 return emit_cjump(n
->Target
, prog
);
753 _slang_new_codegen_context(void)
755 slang_gen_context
*gc
= (slang_gen_context
*) _mesa_calloc(sizeof(*gc
));
762 _slang_emit_code(slang_ir_node
*n
, slang_gen_context
*gc
,
763 struct gl_program
*prog
)
765 /*GET_CURRENT_CONTEXT(ctx);*/
768 gc = _slang_new_codegen_context();
771 printf("************ Begin generate code\n");
773 (void) emit(gc
, n
, prog
);
776 struct prog_instruction
*inst
;
777 inst
= new_instruction(prog
, OPCODE_END
);
780 printf("************ End generate code (%u inst):\n", prog
->NumInstructions
);
783 _mesa_print_program(prog
);
784 _mesa_print_program_parameters(ctx
,prog
);