From e5d00e8cf4659f03fe3061326a24b6a670f7c8f2 Mon Sep 17 00:00:00 2001 From: Brian Date: Tue, 27 Mar 2007 16:04:36 -0600 Subject: Implement true CAL/RET subroutines. Some optimizations, clean-ups coming... --- src/mesa/shader/slang/slang_codegen.c | 10 +- src/mesa/shader/slang/slang_emit.c | 171 ++++++++++++++++++++++++++++++---- 2 files changed, 162 insertions(+), 19 deletions(-) (limited to 'src') diff --git a/src/mesa/shader/slang/slang_codegen.c b/src/mesa/shader/slang/slang_codegen.c index 93b6d9f854..cf3569c3b0 100644 --- a/src/mesa/shader/slang/slang_codegen.c +++ b/src/mesa/shader/slang/slang_codegen.c @@ -1107,12 +1107,18 @@ _slang_gen_function_call(slang_assemble_ctx *A, slang_function *fun, else { /* non-assembly function */ inlined = slang_inline_function_call(A, fun, oper, dest); - if (inlined) { + if (inlined && _slang_find_node_type(inlined, SLANG_OPER_RETURN)) { + /* This inlined function has one or more 'return' statements. + * So, we can't truly inline this function because we need to + * implement 'return' with RET (and CAL). + * XXX check if there's one 'return' and if it's the very last + * statement in the function - we can optimize that case. + */ assert(inlined->type == SLANG_OPER_BLOCK_NEW_SCOPE || inlined->type == SLANG_OPER_SEQUENCE); inlined->type = SLANG_OPER_INLINED_CALL; inlined->fun = fun; - inlined->label = _slang_label_new((char*) fun->header.a_name); + inlined->label = _slang_label_new_unique((char*) fun->header.a_name); } } diff --git a/src/mesa/shader/slang/slang_emit.c b/src/mesa/shader/slang/slang_emit.c index 21b73c2f97..64163c4959 100644 --- a/src/mesa/shader/slang/slang_emit.c +++ b/src/mesa/shader/slang/slang_emit.c @@ -51,23 +51,42 @@ #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) */ @@ -227,19 +246,18 @@ new_instruction(slang_emit_info *emitInfo, gl_inst_opcode opcode) } -#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 * @@ -719,27 +737,62 @@ emit_label(slang_emit_info *emitInfo, const slang_ir_node *n) /** - * 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) { @@ -747,12 +800,17 @@ 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; } @@ -1549,7 +1607,11 @@ emit(slang_emit_info *emitInfo, slang_ir_node *n) 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); @@ -1584,6 +1646,76 @@ emit(slang_emit_info *emitInfo, slang_ir_node *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, @@ -1596,11 +1728,13 @@ _slang_emit_code(slang_ir_node *n, slang_var_table *vt, 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); @@ -1609,6 +1743,9 @@ _slang_emit_code(slang_ir_node *n, slang_var_table *vt, struct prog_instruction *inst; inst = new_instruction(&emitInfo, OPCODE_END); } + + _slang_resolve_subroutines(&emitInfo); + success = GL_TRUE; #if 0 -- cgit v1.2.3