From 7b130149427019ac9ae6d39b6871c14fb6ba2dad Mon Sep 17 00:00:00 2001 From: Eric Anholt Date: Tue, 13 Jul 2010 12:22:05 -0700 Subject: ir_to_mesa: Add support for function calls. Unlike the previous compiler, in this case we emit only one copy of the function regardless of how many times it's called. --- src/mesa/shader/ir_to_mesa.cpp | 233 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 227 insertions(+), 6 deletions(-) (limited to 'src/mesa/shader') diff --git a/src/mesa/shader/ir_to_mesa.cpp b/src/mesa/shader/ir_to_mesa.cpp index f99a1fc450..6d1837f59e 100644 --- a/src/mesa/shader/ir_to_mesa.cpp +++ b/src/mesa/shader/ir_to_mesa.cpp @@ -87,6 +87,8 @@ public: int sampler; /**< sampler index */ int tex_target; /**< One of TEXTURE_*_INDEX */ GLboolean tex_shadow; + + class function_entry *function; /* Set on OPCODE_CAL or OPCODE_BGNSUB */ }; class temp_entry : public exec_node { @@ -102,10 +104,45 @@ public: ir_variable *var; /* variable that maps to this, if any */ }; +class function_entry : public exec_node { +public: + ir_function_signature *sig; + + /** + * identifier of this function signature used by the program. + * + * At the point that Mesa instructions for function calls are + * generated, we don't know the address of the first instruction of + * the function body. So we make the BranchTarget that is called a + * small integer and rewrite them during set_branchtargets(). + */ + int sig_id; + + /** + * Pointer to first instruction of the function body. + * + * Set during function body emits after main() is processed. + */ + ir_to_mesa_instruction *bgn_inst; + + /** + * Index of the first instruction of the function body in actual + * Mesa IR. + * + * Set after convertion from ir_to_mesa_instruction to prog_instruction. + */ + int inst; + + /** Storage for the return value. */ + ir_to_mesa_src_reg return_reg; +}; + class ir_to_mesa_visitor : public ir_visitor { public: ir_to_mesa_visitor(); + function_entry *current_function; + GLcontext *ctx; struct gl_program *prog; @@ -113,6 +150,8 @@ public: temp_entry *find_variable_storage(ir_variable *var); + function_entry *get_function_signature(ir_function_signature *sig); + ir_to_mesa_src_reg get_temp(const glsl_type *type); void reladdr_to_temp(ir_instruction *ir, ir_to_mesa_src_reg *reg, int *num_reladdr); @@ -151,6 +190,10 @@ public: /** List of temp_entry */ exec_list variable_storage; + /** List of function_entry */ + exec_list function_signatures; + int next_signature_id; + /** List of ir_to_mesa_instruction */ exec_list instructions; @@ -261,6 +304,8 @@ ir_to_mesa_visitor::ir_to_mesa_emit_op3(ir_instruction *ir, inst->src_reg[2] = src2; inst->ir = ir; + inst->function = NULL; + this->instructions.push_tail(inst); return inst; @@ -1396,12 +1441,128 @@ ir_to_mesa_visitor::visit(ir_constant *ir) this->result = src_reg; } +function_entry * +ir_to_mesa_visitor::get_function_signature(ir_function_signature *sig) +{ + function_entry *entry; + + foreach_iter(exec_list_iterator, iter, this->function_signatures) { + entry = (function_entry *)iter.get(); + + if (entry->sig == sig) + return entry; + } + + entry = talloc(mem_ctx, function_entry); + entry->sig = sig; + entry->sig_id = this->next_signature_id++; + entry->bgn_inst = NULL; + + /* Allocate storage for all the parameters. */ + foreach_iter(exec_list_iterator, iter, sig->parameters) { + ir_variable *param = (ir_variable *)iter.get(); + temp_entry *storage; + + storage = find_variable_storage(param); + assert(!storage); + + storage = new(mem_ctx) temp_entry(param, PROGRAM_TEMPORARY, + this->next_temp); + this->variable_storage.push_tail(storage); + + this->next_temp += type_size(param->type); + break; + } + + if (sig->return_type) { + entry->return_reg = get_temp(sig->return_type); + } else { + entry->return_reg = ir_to_mesa_undef; + } + + this->function_signatures.push_tail(entry); + return entry; +} void ir_to_mesa_visitor::visit(ir_call *ir) { - printf("Can't support call to %s\n", ir->callee_name()); - exit(1); + ir_to_mesa_instruction *call_inst; + ir_function_signature *sig = ir->get_callee(); + function_entry *entry = get_function_signature(sig); + int i; + + /* Process in parameters. */ + exec_list_iterator sig_iter = sig->parameters.iterator(); + foreach_iter(exec_list_iterator, iter, *ir) { + ir_rvalue *param_rval = (ir_rvalue *)iter.get(); + ir_variable *param = (ir_variable *)sig_iter.get(); + + if (param->mode == ir_var_in || + param->mode == ir_var_inout) { + temp_entry *storage = find_variable_storage(param); + assert(storage); + + param_rval->accept(this); + ir_to_mesa_src_reg r = this->result; + + ir_to_mesa_dst_reg l; + l.file = storage->file; + l.index = storage->index; + l.reladdr = NULL; + l.writemask = WRITEMASK_XYZW; + l.cond_mask = COND_TR; + + for (i = 0; i < type_size(param->type); i++) { + ir_to_mesa_emit_op1(ir, OPCODE_MOV, l, r); + l.index++; + r.index++; + } + } + + sig_iter.next(); + } + assert(!sig_iter.has_next()); + + /* Emit call instruction */ + call_inst = ir_to_mesa_emit_op1(ir, OPCODE_CAL, + ir_to_mesa_undef_dst, ir_to_mesa_undef); + call_inst->function = entry; + + /* Process out parameters. */ + sig_iter = sig->parameters.iterator(); + foreach_iter(exec_list_iterator, iter, *ir) { + ir_rvalue *param_rval = (ir_rvalue *)iter.get(); + ir_variable *param = (ir_variable *)sig_iter.get(); + + if (param->mode == ir_var_out || + param->mode == ir_var_inout) { + temp_entry *storage = find_variable_storage(param); + assert(storage); + + ir_to_mesa_src_reg r; + r.file = storage->file; + r.index = storage->index; + r.reladdr = NULL; + r.swizzle = SWIZZLE_NOOP; + r.negate = 0; + + param_rval->accept(this); + ir_to_mesa_dst_reg l = ir_to_mesa_dst_reg_from_src(this->result); + + for (i = 0; i < type_size(param->type); i++) { + ir_to_mesa_emit_op1(ir, OPCODE_MOV, l, r); + l.index++; + r.index++; + } + } + + sig_iter.next(); + } + assert(!sig_iter.has_next()); + + /* Process return value. */ + this->result = entry->return_reg; } @@ -1536,9 +1697,25 @@ ir_to_mesa_visitor::visit(ir_texture *ir) void ir_to_mesa_visitor::visit(ir_return *ir) { - assert(0); + assert(current_function); + + if (ir->get_value()) { + ir_to_mesa_dst_reg l; + int i; + + ir->get_value()->accept(this); + ir_to_mesa_src_reg r = this->result; - ir->get_value()->accept(this); + l = ir_to_mesa_dst_reg_from_src(current_function->return_reg); + + for (i = 0; i < type_size(current_function->sig->return_type); i++) { + ir_to_mesa_emit_op1(ir, OPCODE_MOV, l, r); + l.index++; + r.index++; + } + } + + ir_to_mesa_emit_op0(ir, OPCODE_RET); } void @@ -1600,8 +1777,10 @@ ir_to_mesa_visitor::ir_to_mesa_visitor() { result.file = PROGRAM_UNDEFINED; next_temp = 1; + next_signature_id = 1; sampler_map = NULL; sampler_map_size = 0; + current_function = NULL; } static struct prog_src_register @@ -1621,7 +1800,8 @@ mesa_src_reg_from_ir_src_reg(ir_to_mesa_src_reg reg) } static void -set_branchtargets(struct prog_instruction *mesa_instructions, +set_branchtargets(ir_to_mesa_visitor *v, + struct prog_instruction *mesa_instructions, int num_instructions) { int if_count = 0, loop_count = 0; @@ -1684,6 +1864,17 @@ set_branchtargets(struct prog_instruction *mesa_instructions, /* The loop ends point at each other. */ mesa_instructions[i].BranchTarget = loop_stack[loop_stack_pos]; mesa_instructions[loop_stack[loop_stack_pos]].BranchTarget = i; + break; + case OPCODE_CAL: + foreach_iter(exec_list_iterator, iter, v->function_signatures) { + function_entry *entry = (function_entry *)iter.get(); + + if (entry->sig_id == mesa_instructions[i].BranchTarget) { + mesa_instructions[i].BranchTarget = entry->inst; + break; + } + } + break; default: break; } @@ -1842,6 +2033,7 @@ get_mesa_program(GLcontext *ctx, void *mem_ctx, struct gl_shader *shader) int i; struct gl_program *prog; GLenum target; + GLboolean progress; switch (shader->Type) { case GL_VERTEX_SHADER: target = GL_VERTEX_PROGRAM_ARB; break; @@ -1859,9 +2051,33 @@ get_mesa_program(GLcontext *ctx, void *mem_ctx, struct gl_shader *shader) v.prog = prog; v.mem_ctx = talloc_new(NULL); + + /* Emit Mesa IR for main(). */ visit_exec_list(shader->ir, &v); v.ir_to_mesa_emit_op0(NULL, OPCODE_END); + /* Now emit bodies for any functions that were used. */ + do { + progress = GL_FALSE; + + foreach_iter(exec_list_iterator, iter, v.function_signatures) { + function_entry *entry = (function_entry *)iter.get(); + + if (!entry->bgn_inst) { + v.current_function = entry; + + entry->bgn_inst = v.ir_to_mesa_emit_op0(NULL, OPCODE_BGNSUB); + entry->bgn_inst->function = entry; + + visit_exec_list(&entry->sig->body, &v); + + entry->bgn_inst = v.ir_to_mesa_emit_op0(NULL, OPCODE_RET); + entry->bgn_inst = v.ir_to_mesa_emit_op0(NULL, OPCODE_ENDSUB); + progress = GL_TRUE; + } + } + } while (progress); + prog->NumTemporaries = v.next_temp; int num_instructions = 0; @@ -1895,11 +2111,16 @@ get_mesa_program(GLcontext *ctx, void *mem_ctx, struct gl_shader *shader) mesa_inst->TexShadow = inst->tex_shadow; mesa_instruction_annotation[i] = inst->ir; + if (mesa_inst->Opcode == OPCODE_BGNSUB) + inst->function->inst = i; + else if (mesa_inst->Opcode == OPCODE_CAL) + mesa_inst->BranchTarget = inst->function->sig_id; /* rewritten later */ + mesa_inst++; i++; } - set_branchtargets(mesa_instructions, num_instructions); + set_branchtargets(&v, mesa_instructions, num_instructions); if (0) { print_program(mesa_instructions, mesa_instruction_annotation, num_instructions); -- cgit v1.2.3