#define ANNOTATE 0
-/* XXX temporarily here */
-
-
typedef struct
{
slang_info_log *log;
slang_var_table *vt;
struct gl_program *prog;
+ struct gl_program **Subroutines;
+ GLuint NumSubroutines;
+
/* code-gen options */
GLboolean EmitHighLevelInstructions;
GLboolean EmitCondCodes;
- GLboolean EmitBeginEndSub;
GLboolean EmitComments;
+ GLboolean EmitBeginEndSub; /* XXX TEMPORARY */
} slang_emit_info;
+static struct gl_program *
+new_subroutine(slang_emit_info *emitInfo, GLuint *id)
+{
+ GET_CURRENT_CONTEXT(ctx);
+ const GLuint n = emitInfo->NumSubroutines;
+
+ emitInfo->Subroutines = (struct gl_program **)
+ _mesa_realloc(emitInfo->Subroutines,
+ n * sizeof(struct gl_program),
+ (n + 1) * sizeof(struct gl_program));
+ emitInfo->Subroutines[n] = _mesa_new_program(ctx, emitInfo->prog->Target, 0);
+ emitInfo->Subroutines[n]->Parameters = emitInfo->prog->Parameters;
+ emitInfo->NumSubroutines++;
+ *id = n;
+ return emitInfo->Subroutines[n];
+}
+
+
+
/**
* Swizzle a swizzle. That is, return swz2(swz1)
*/
}
-#if 0
/**
* Return pointer to last instruction in program.
*/
static struct prog_instruction *
-prev_instruction(struct gl_program *prog)
+prev_instruction(slang_emit_info *emitInfo)
{
+ struct gl_program *prog = emitInfo->prog;
if (prog->NumInstructions == 0)
return NULL;
else
return prog->Instructions + prog->NumInstructions - 1;
}
-#endif
static struct prog_instruction *
/**
- * Emit code for an inlined function call.
+ * Emit code for an inlined function call (subroutine).
*/
static struct prog_instruction *
emit_func(slang_emit_info *emitInfo, slang_ir_node *n)
{
+ struct gl_program *progSave;
struct prog_instruction *inst;
+ GLuint subroutineId;
+
+ assert(n->Opcode == IR_FUNC);
assert(n->Label);
+
+ /* save/push cur program */
+ progSave = emitInfo->prog;
+ emitInfo->prog = new_subroutine(emitInfo, &subroutineId);
+
+ _slang_label_set_location(n->Label, emitInfo->prog->NumInstructions,
+ emitInfo->prog);
+
if (emitInfo->EmitBeginEndSub) {
inst = new_instruction(emitInfo, OPCODE_BGNSUB);
inst->Comment = _mesa_strdup(n->Label->Name);
}
- inst = emit(emitInfo, n->Children[0]);
+
+ /* body of function: */
+ emit(emitInfo, n->Children[0]);
+ n->Store = n->Children[0]->Store;
+
+ /* add RET instruction now, if needed */
+ inst = prev_instruction(emitInfo);
+ if (inst && inst->Opcode != OPCODE_RET) {
+ inst = new_instruction(emitInfo, OPCODE_RET);
+ }
+
if (emitInfo->EmitBeginEndSub) {
inst = new_instruction(emitInfo, OPCODE_ENDSUB);
inst->Comment = _mesa_strdup(n->Label->Name);
}
- n->Store = n->Children[0]->Store;
+
+ /* pop/restore cur program */
+ emitInfo->prog = progSave;
+
+ /* emit the function call */
+ inst = new_instruction(emitInfo, OPCODE_CAL);
+ /* The branch target is just the subroutine number (changed later) */
+ inst->BranchTarget = subroutineId;
+ inst->Comment = _mesa_strdup(n->Label->Name);
+ assert(inst->BranchTarget >= 0);
+
return inst;
}
+/**
+ * Emit code for a 'return' statement.
+ */
static struct prog_instruction *
emit_return(slang_emit_info *emitInfo, slang_ir_node *n)
{
assert(n);
assert(n->Opcode == IR_RETURN);
assert(n->Label);
- inst = new_instruction(emitInfo, OPCODE_BRA /*RET*/); /*XXX TEMPORARY*/
- inst->DstReg.CondMask = COND_TR; /* always branch */
- inst->BranchTarget = _slang_label_get_location(n->Label);
- if (inst->BranchTarget < 0) {
- _slang_label_add_reference(n->Label, emitInfo->prog->NumInstructions - 1);
+ inst = new_instruction(emitInfo, OPCODE_RET/*BRA*/); /*XXX TEMPORARY*/
+ inst->DstReg.CondMask = COND_TR; /* always return/branch */
+
+ if (inst->Opcode == OPCODE_BRA) {
+ inst->BranchTarget = _slang_label_get_location(n->Label);
+ if (inst->BranchTarget < 0) {
+ _slang_label_add_reference(n->Label,
+ emitInfo->prog->NumInstructions - 1);
+ }
}
+
return inst;
}
return emit_kill(emitInfo);
case IR_FUNC:
- return emit_func(emitInfo, n);
+ /* new variable scope for subroutines/function calls*/
+ _slang_push_var_table(emitInfo->vt);
+ inst = emit_func(emitInfo, n);
+ _slang_pop_var_table(emitInfo->vt);
+ return inst;
case IR_IF:
return emit_if(emitInfo, n);
}
+/**
+ * After code generation, any subroutines will be in separate program
+ * objects. This function appends all the subroutines onto the main
+ * program and resolves the linking of all the branch/call instructions.
+ * XXX this logic should really be part of the linking process...
+ */
+static void
+_slang_resolve_subroutines(slang_emit_info *emitInfo)
+{
+ GET_CURRENT_CONTEXT(ctx);
+ struct gl_program *mainP = emitInfo->prog;
+ GLuint *subroutineLoc, i, total;
+
+ subroutineLoc
+ = (GLuint *) _mesa_malloc(emitInfo->NumSubroutines * sizeof(GLuint));
+
+ /* total number of instructions */
+ total = mainP->NumInstructions;
+ for (i = 0; i < emitInfo->NumSubroutines; i++) {
+ subroutineLoc[i] = total;
+ total += emitInfo->Subroutines[i]->NumInstructions;
+ }
+
+ /* adjust BrancTargets within the functions */
+ for (i = 0; i < emitInfo->NumSubroutines; i++) {
+ struct gl_program *sub = emitInfo->Subroutines[i];
+ GLuint j;
+ for (j = 0; j < sub->NumInstructions; j++) {
+ struct prog_instruction *inst = sub->Instructions + j;
+ if (inst->Opcode != OPCODE_CAL && inst->BranchTarget >= 0) {
+ inst->BranchTarget += subroutineLoc[i];
+ }
+ }
+ }
+
+ /* append subroutines' instructions after main's instructions */
+ mainP->Instructions = _mesa_realloc_instructions(mainP->Instructions,
+ mainP->NumInstructions,
+ total);
+ for (i = 0; i < emitInfo->NumSubroutines; i++) {
+ struct gl_program *sub = emitInfo->Subroutines[i];
+ _mesa_copy_instructions(mainP->Instructions + subroutineLoc[i],
+ sub->Instructions,
+ sub->NumInstructions);
+ /* delete subroutine code */
+ sub->Parameters = NULL; /* prevent double-free */
+ _mesa_delete_program(ctx, sub);
+ }
+ mainP->NumInstructions = total;
+
+ /* Examine CAL instructions.
+ * At this point, the BranchTarget field of the CAL instructions is
+ * the number/id of the subroutine to call (an index into the
+ * emitInfo->Subroutines list).
+ * Translate that into an actual instruction location now.
+ */
+ for (i = 0; i < mainP->NumInstructions; i++) {
+ struct prog_instruction *inst = mainP->Instructions + i;
+ if (inst->Opcode == OPCODE_CAL) {
+ const GLuint f = inst->BranchTarget;
+ inst->BranchTarget = subroutineLoc[f];
+ }
+ }
+
+ _mesa_free(subroutineLoc);
+}
+
+
+
+
GLboolean
_slang_emit_code(slang_ir_node *n, slang_var_table *vt,
struct gl_program *prog, GLboolean withEnd,
emitInfo.log = log;
emitInfo.vt = vt;
emitInfo.prog = prog;
+ emitInfo.Subroutines = NULL;
+ emitInfo.NumSubroutines = 0;
emitInfo.EmitHighLevelInstructions = ctx->Shader.EmitHighLevelInstructions;
emitInfo.EmitCondCodes = ctx->Shader.EmitCondCodes;
- emitInfo.EmitBeginEndSub = 0; /* XXX temporary */
emitInfo.EmitComments = ctx->Shader.EmitComments;
+ emitInfo.EmitBeginEndSub = 0; /* XXX for compiler debug only */
(void) emit(&emitInfo, n);
struct prog_instruction *inst;
inst = new_instruction(&emitInfo, OPCODE_END);
}
+
+ _slang_resolve_subroutines(&emitInfo);
+
success = GL_TRUE;
#if 0