From: Eric Anholt Date: Thu, 24 Jun 2010 22:49:18 +0000 (-0700) Subject: glsl2: Move the Mesa IR codegen into mesa/shader/ X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=9aa0b6d728dafc40a65d1a45aa0830f87d76cb23;p=mesa.git glsl2: Move the Mesa IR codegen into mesa/shader/ --- diff --git a/src/glsl/ir_to_mesa.cpp b/src/glsl/ir_to_mesa.cpp deleted file mode 100644 index 26449c5a5c4..00000000000 --- a/src/glsl/ir_to_mesa.cpp +++ /dev/null @@ -1,1211 +0,0 @@ -/* - * Copyright © 2010 Intel Corporation - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice (including the next - * paragraph) shall be included in all copies or substantial portions of the - * Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - */ - -/** - * \file ir_to_mesa.cpp - * - * Translates the IR to ARB_fragment_program text if possible, - * printing the result - */ - -#include -#include "ir.h" -#include "ir_visitor.h" -#include "ir_print_visitor.h" -#include "ir_expression_flattening.h" -#include "glsl_types.h" - -extern "C" { -#include "shader/prog_instruction.h" -#include "shader/prog_print.h" -} - -/** - * This struct is a corresponding struct to Mesa prog_src_register, with - * wider fields. - */ -typedef struct ir_to_mesa_src_reg { - int file; /**< PROGRAM_* from Mesa */ - int index; /**< temporary index, VERT_ATTRIB_*, FRAG_ATTRIB_*, etc. */ - int swizzle; /**< SWIZZLE_XYZWONEZERO swizzles from Mesa. */ - int negate; /**< NEGATE_XYZW mask from mesa */ - bool reladdr; /**< Register index should be offset by address reg. */ -} ir_to_mesa_src_reg; - -typedef struct ir_to_mesa_dst_reg { - int file; /**< PROGRAM_* from Mesa */ - int index; /**< temporary index, VERT_ATTRIB_*, FRAG_ATTRIB_*, etc. */ - int writemask; /**< Bitfield of WRITEMASK_[XYZW] */ -} ir_to_mesa_dst_reg; - -extern ir_to_mesa_src_reg ir_to_mesa_undef; - -class ir_to_mesa_instruction : public exec_node { -public: - enum prog_opcode op; - ir_to_mesa_dst_reg dst_reg; - ir_to_mesa_src_reg src_reg[3]; - /** Pointer to the ir source this tree came from for debugging */ - ir_instruction *ir; -}; - -class temp_entry : public exec_node { -public: - temp_entry(ir_variable *var, int file, int index) - : file(file), index(index), var(var) - { - /* empty */ - } - - int file; - int index; - ir_variable *var; /* variable that maps to this, if any */ -}; - -class ir_to_mesa_visitor : public ir_visitor { -public: - ir_to_mesa_visitor(); - - int next_temp; - int next_constant; - int next_uniform; - - temp_entry *find_variable_storage(ir_variable *var); - - ir_to_mesa_src_reg get_temp(const glsl_type *type); - - struct ir_to_mesa_src_reg src_reg_for_float(float val); - - /** - * \name Visit methods - * - * As typical for the visitor pattern, there must be one \c visit method for - * each concrete subclass of \c ir_instruction. Virtual base classes within - * the hierarchy should not have \c visit methods. - */ - /*@{*/ - virtual void visit(ir_variable *); - virtual void visit(ir_loop *); - virtual void visit(ir_loop_jump *); - virtual void visit(ir_function_signature *); - virtual void visit(ir_function *); - virtual void visit(ir_expression *); - virtual void visit(ir_swizzle *); - virtual void visit(ir_dereference_variable *); - virtual void visit(ir_dereference_array *); - virtual void visit(ir_dereference_record *); - virtual void visit(ir_assignment *); - virtual void visit(ir_constant *); - virtual void visit(ir_call *); - virtual void visit(ir_return *); - virtual void visit(ir_texture *); - virtual void visit(ir_if *); - /*@}*/ - - struct ir_to_mesa_src_reg result; - - /** List of temp_entry */ - exec_list variable_storage; - - /** List of ir_to_mesa_instruction */ - exec_list instructions; - - ir_to_mesa_instruction *ir_to_mesa_emit_op1(ir_instruction *ir, - enum prog_opcode op, - ir_to_mesa_dst_reg dst, - ir_to_mesa_src_reg src0); - - ir_to_mesa_instruction *ir_to_mesa_emit_op2(ir_instruction *ir, - enum prog_opcode op, - ir_to_mesa_dst_reg dst, - ir_to_mesa_src_reg src0, - ir_to_mesa_src_reg src1); - - ir_to_mesa_instruction *ir_to_mesa_emit_op3(ir_instruction *ir, - enum prog_opcode op, - ir_to_mesa_dst_reg dst, - ir_to_mesa_src_reg src0, - ir_to_mesa_src_reg src1, - ir_to_mesa_src_reg src2); - - void ir_to_mesa_emit_scalar_op1(ir_instruction *ir, - enum prog_opcode op, - ir_to_mesa_dst_reg dst, - ir_to_mesa_src_reg src0); - - /* talloc context (the ) */ - void *ctx; -}; - -ir_to_mesa_src_reg ir_to_mesa_undef = { - PROGRAM_UNDEFINED, 0, SWIZZLE_NOOP, NEGATE_NONE, false, -}; - -ir_to_mesa_dst_reg ir_to_mesa_undef_dst = { - PROGRAM_UNDEFINED, 0, SWIZZLE_NOOP -}; - -ir_to_mesa_dst_reg ir_to_mesa_address_reg = { - PROGRAM_ADDRESS, 0, WRITEMASK_X -}; - -static int swizzle_for_size(int size) -{ - int size_swizzles[4] = { - MAKE_SWIZZLE4(SWIZZLE_X, SWIZZLE_X, SWIZZLE_X, SWIZZLE_X), - MAKE_SWIZZLE4(SWIZZLE_X, SWIZZLE_Y, SWIZZLE_Y, SWIZZLE_Y), - MAKE_SWIZZLE4(SWIZZLE_X, SWIZZLE_Y, SWIZZLE_Z, SWIZZLE_Z), - MAKE_SWIZZLE4(SWIZZLE_X, SWIZZLE_Y, SWIZZLE_Z, SWIZZLE_W), - }; - - return size_swizzles[size - 1]; -} - -/* This list should match up with builtin_variables.h */ -static const struct { - const char *name; - int file; - int index; -} builtin_var_to_mesa_reg[] = { - /* core_vs */ - {"gl_Position", PROGRAM_OUTPUT, VERT_RESULT_HPOS}, - {"gl_PointSize", PROGRAM_OUTPUT, VERT_RESULT_PSIZ}, - - /* core_fs */ - {"gl_FragCoord", PROGRAM_INPUT, FRAG_ATTRIB_WPOS}, - {"gl_FrontFacing", PROGRAM_INPUT, FRAG_ATTRIB_FACE}, - {"gl_FragColor", PROGRAM_OUTPUT, FRAG_ATTRIB_COL0}, - {"gl_FragDepth", PROGRAM_UNDEFINED, FRAG_ATTRIB_WPOS}, /* FINISHME: WPOS.z */ - - /* 110_deprecated_fs */ - {"gl_Color", PROGRAM_INPUT, FRAG_ATTRIB_COL0}, - {"gl_SecondaryColor", PROGRAM_INPUT, FRAG_ATTRIB_COL1}, - {"gl_FogFragCoord", PROGRAM_INPUT, FRAG_ATTRIB_FOGC}, - {"gl_TexCoord", PROGRAM_INPUT, FRAG_ATTRIB_TEX0}, /* array */ - - /* 110_deprecated_vs */ - {"gl_Vertex", PROGRAM_INPUT, VERT_ATTRIB_POS}, - {"gl_Normal", PROGRAM_INPUT, VERT_ATTRIB_NORMAL}, - {"gl_Color", PROGRAM_INPUT, VERT_ATTRIB_COLOR0}, - {"gl_SecondaryColor", PROGRAM_INPUT, VERT_ATTRIB_COLOR1}, - {"gl_MultiTexCoord0", PROGRAM_INPUT, VERT_ATTRIB_TEX0}, - {"gl_MultiTexCoord1", PROGRAM_INPUT, VERT_ATTRIB_TEX1}, - {"gl_MultiTexCoord2", PROGRAM_INPUT, VERT_ATTRIB_TEX2}, - {"gl_MultiTexCoord3", PROGRAM_INPUT, VERT_ATTRIB_TEX3}, - {"gl_MultiTexCoord4", PROGRAM_INPUT, VERT_ATTRIB_TEX4}, - {"gl_MultiTexCoord5", PROGRAM_INPUT, VERT_ATTRIB_TEX5}, - {"gl_MultiTexCoord6", PROGRAM_INPUT, VERT_ATTRIB_TEX6}, - {"gl_MultiTexCoord7", PROGRAM_INPUT, VERT_ATTRIB_TEX7}, - {"gl_TexCoord", PROGRAM_OUTPUT, VERT_RESULT_TEX0}, /* array */ - {"gl_FogCoord", PROGRAM_INPUT, VERT_RESULT_FOGC}, - /*{"gl_ClipVertex", PROGRAM_OUTPUT, VERT_ATTRIB_FOGC},*/ /* FINISHME */ - {"gl_FrontColor", PROGRAM_OUTPUT, VERT_RESULT_COL0}, - {"gl_BackColor", PROGRAM_OUTPUT, VERT_RESULT_BFC0}, - {"gl_FrontSecondaryColor", PROGRAM_OUTPUT, VERT_RESULT_COL1}, - {"gl_BackSecondaryColor", PROGRAM_OUTPUT, VERT_RESULT_BFC1}, - {"gl_FogFragCoord", PROGRAM_OUTPUT, VERT_RESULT_FOGC}, - - /* 130_vs */ - /*{"gl_VertexID", PROGRAM_INPUT, VERT_ATTRIB_FOGC},*/ /* FINISHME */ - - {"gl_FragData", PROGRAM_OUTPUT, FRAG_RESULT_DATA0}, /* array */ -}; - -ir_to_mesa_instruction * -ir_to_mesa_visitor::ir_to_mesa_emit_op3(ir_instruction *ir, - enum prog_opcode op, - ir_to_mesa_dst_reg dst, - ir_to_mesa_src_reg src0, - ir_to_mesa_src_reg src1, - ir_to_mesa_src_reg src2) -{ - ir_to_mesa_instruction *inst = new(ctx) ir_to_mesa_instruction(); - - inst->op = op; - inst->dst_reg = dst; - inst->src_reg[0] = src0; - inst->src_reg[1] = src1; - inst->src_reg[2] = src2; - inst->ir = ir; - - this->instructions.push_tail(inst); - - return inst; -} - - -ir_to_mesa_instruction * -ir_to_mesa_visitor::ir_to_mesa_emit_op2(ir_instruction *ir, - enum prog_opcode op, - ir_to_mesa_dst_reg dst, - ir_to_mesa_src_reg src0, - ir_to_mesa_src_reg src1) -{ - return ir_to_mesa_emit_op3(ir, op, dst, src0, src1, ir_to_mesa_undef); -} - -ir_to_mesa_instruction * -ir_to_mesa_visitor::ir_to_mesa_emit_op1(ir_instruction *ir, - enum prog_opcode op, - ir_to_mesa_dst_reg dst, - ir_to_mesa_src_reg src0) -{ - return ir_to_mesa_emit_op3(ir, op, dst, - src0, ir_to_mesa_undef, ir_to_mesa_undef); -} - -inline ir_to_mesa_dst_reg -ir_to_mesa_dst_reg_from_src(ir_to_mesa_src_reg reg) -{ - ir_to_mesa_dst_reg dst_reg; - - dst_reg.file = reg.file; - dst_reg.index = reg.index; - dst_reg.writemask = WRITEMASK_XYZW; - - return dst_reg; -} - -/** - * Emits Mesa scalar opcodes to produce unique answers across channels. - * - * Some Mesa opcodes are scalar-only, like ARB_fp/vp. The src X - * channel determines the result across all channels. So to do a vec4 - * of this operation, we want to emit a scalar per source channel used - * to produce dest channels. - */ -void -ir_to_mesa_visitor::ir_to_mesa_emit_scalar_op1(ir_instruction *ir, - enum prog_opcode op, - ir_to_mesa_dst_reg dst, - ir_to_mesa_src_reg src0) -{ - int i, j; - int done_mask = ~dst.writemask; - - /* Mesa RCP is a scalar operation splatting results to all channels, - * like ARB_fp/vp. So emit as many RCPs as necessary to cover our - * dst channels. - */ - for (i = 0; i < 4; i++) { - int this_mask = (1 << i); - ir_to_mesa_instruction *inst; - ir_to_mesa_src_reg src = src0; - - if (done_mask & this_mask) - continue; - - int src_swiz = GET_SWZ(src.swizzle, i); - for (j = i + 1; j < 4; j++) { - if (!(done_mask & (1 << j)) && GET_SWZ(src.swizzle, j) == src_swiz) { - this_mask |= (1 << j); - } - } - src.swizzle = MAKE_SWIZZLE4(src_swiz, src_swiz, - src_swiz, src_swiz); - - inst = ir_to_mesa_emit_op1(ir, op, - dst, - src); - inst->dst_reg.writemask = this_mask; - done_mask |= this_mask; - } -} - -struct ir_to_mesa_src_reg -ir_to_mesa_visitor::src_reg_for_float(float val) -{ - ir_to_mesa_src_reg src_reg; - - /* FINISHME: This will end up being _mesa_add_unnamed_constant, - * which handles sharing values and sharing channels of vec4 - * constants for small values. - */ - /* FINISHME: Do something with the constant values for now. - */ - (void)val; - src_reg.file = PROGRAM_CONSTANT; - src_reg.index = this->next_constant++; - src_reg.swizzle = SWIZZLE_NOOP; - - return src_reg; -} - -/** - * In the initial pass of codegen, we assign temporary numbers to - * intermediate results. (not SSA -- variable assignments will reuse - * storage). Actual register allocation for the Mesa VM occurs in a - * pass over the Mesa IR later. - */ -ir_to_mesa_src_reg -ir_to_mesa_visitor::get_temp(const glsl_type *type) -{ - ir_to_mesa_src_reg src_reg; - int swizzle[4]; - int i; - - assert(!type->is_array()); - - src_reg.file = PROGRAM_TEMPORARY; - src_reg.index = type->matrix_columns; - src_reg.reladdr = false; - - for (i = 0; i < type->vector_elements; i++) - swizzle[i] = i; - for (; i < 4; i++) - swizzle[i] = type->vector_elements - 1; - src_reg.swizzle = MAKE_SWIZZLE4(swizzle[0], swizzle[1], - swizzle[2], swizzle[3]); - - return src_reg; -} - -static int -type_size(const struct glsl_type *type) -{ - unsigned int i; - int size; - - switch (type->base_type) { - case GLSL_TYPE_UINT: - case GLSL_TYPE_INT: - case GLSL_TYPE_FLOAT: - case GLSL_TYPE_BOOL: - if (type->is_matrix()) { - return 4; /* FINISHME: Not all matrices are 4x4. */ - } else { - /* Regardless of size of vector, it gets a vec4. This is bad - * packing for things like floats, but otherwise arrays become a - * mess. Hopefully a later pass over the code can pack scalars - * down if appropriate. - */ - return 1; - } - case GLSL_TYPE_ARRAY: - return type_size(type->fields.array) * type->length; - case GLSL_TYPE_STRUCT: - size = 0; - for (i = 0; i < type->length; i++) { - size += type_size(type->fields.structure[i].type); - } - return size; - default: - assert(0); - } -} - -temp_entry * -ir_to_mesa_visitor::find_variable_storage(ir_variable *var) -{ - - temp_entry *entry; - - foreach_iter(exec_list_iterator, iter, this->variable_storage) { - entry = (temp_entry *)iter.get(); - - if (entry->var == var) - return entry; - } - - return NULL; -} - -void -ir_to_mesa_visitor::visit(ir_variable *ir) -{ - (void)ir; -} - -void -ir_to_mesa_visitor::visit(ir_loop *ir) -{ - assert(!ir->from); - assert(!ir->to); - assert(!ir->increment); - assert(!ir->counter); - - ir_to_mesa_emit_op1(NULL, OPCODE_BGNLOOP, - ir_to_mesa_undef_dst, ir_to_mesa_undef); - - visit_exec_list(&ir->body_instructions, this); - - ir_to_mesa_emit_op1(NULL, OPCODE_ENDLOOP, - ir_to_mesa_undef_dst, ir_to_mesa_undef); -} - -void -ir_to_mesa_visitor::visit(ir_loop_jump *ir) -{ - switch (ir->mode) { - case ir_loop_jump::jump_break: - ir_to_mesa_emit_op1(NULL, OPCODE_BRK, - ir_to_mesa_undef_dst, ir_to_mesa_undef); - break; - case ir_loop_jump::jump_continue: - ir_to_mesa_emit_op1(NULL, OPCODE_CONT, - ir_to_mesa_undef_dst, ir_to_mesa_undef); - break; - } -} - - -void -ir_to_mesa_visitor::visit(ir_function_signature *ir) -{ - assert(0); - (void)ir; -} - -void -ir_to_mesa_visitor::visit(ir_function *ir) -{ - /* Ignore function bodies other than main() -- we shouldn't see calls to - * them since they should all be inlined before we get to ir_to_mesa. - */ - if (strcmp(ir->name, "main") == 0) { - const ir_function_signature *sig; - exec_list empty; - - sig = ir->matching_signature(&empty); - - assert(sig); - - foreach_iter(exec_list_iterator, iter, sig->body) { - ir_instruction *ir = (ir_instruction *)iter.get(); - - ir->accept(this); - } - } -} - -void -ir_to_mesa_visitor::visit(ir_expression *ir) -{ - unsigned int operand; - struct ir_to_mesa_src_reg op[2]; - struct ir_to_mesa_src_reg result_src; - struct ir_to_mesa_dst_reg result_dst; - const glsl_type *vec4_type = glsl_type::get_instance(GLSL_TYPE_FLOAT, 4, 1); - const glsl_type *vec3_type = glsl_type::get_instance(GLSL_TYPE_FLOAT, 3, 1); - const glsl_type *vec2_type = glsl_type::get_instance(GLSL_TYPE_FLOAT, 2, 1); - - for (operand = 0; operand < ir->get_num_operands(); operand++) { - this->result.file = PROGRAM_UNDEFINED; - ir->operands[operand]->accept(this); - if (this->result.file == PROGRAM_UNDEFINED) { - ir_print_visitor v; - printf("Failed to get tree for expression operand:\n"); - ir->operands[operand]->accept(&v); - exit(1); - } - op[operand] = this->result; - - /* Only expression implemented for matrices yet */ - assert(!ir->operands[operand]->type->is_matrix() || - ir->operation == ir_binop_mul); - } - - this->result.file = PROGRAM_UNDEFINED; - - /* Storage for our result. Ideally for an assignment we'd be using - * the actual storage for the result here, instead. - */ - result_src = get_temp(ir->type); - /* convenience for the emit functions below. */ - result_dst = ir_to_mesa_dst_reg_from_src(result_src); - /* Limit writes to the channels that will be used by result_src later. - * This does limit this temp's use as a temporary for multi-instruction - * sequences. - */ - result_dst.writemask = (1 << ir->type->vector_elements) - 1; - - switch (ir->operation) { - case ir_unop_logic_not: - ir_to_mesa_emit_op2(ir, OPCODE_SEQ, result_dst, - op[0], src_reg_for_float(0.0)); - break; - case ir_unop_neg: - op[0].negate = ~op[0].negate; - result_src = op[0]; - break; - case ir_unop_exp: - ir_to_mesa_emit_scalar_op1(ir, OPCODE_EXP, result_dst, op[0]); - break; - case ir_unop_exp2: - ir_to_mesa_emit_scalar_op1(ir, OPCODE_EX2, result_dst, op[0]); - break; - case ir_unop_log: - ir_to_mesa_emit_scalar_op1(ir, OPCODE_LOG, result_dst, op[0]); - break; - case ir_unop_log2: - ir_to_mesa_emit_scalar_op1(ir, OPCODE_LG2, result_dst, op[0]); - break; - case ir_unop_sin: - ir_to_mesa_emit_scalar_op1(ir, OPCODE_SIN, result_dst, op[0]); - break; - case ir_unop_cos: - ir_to_mesa_emit_scalar_op1(ir, OPCODE_COS, result_dst, op[0]); - break; - case ir_binop_add: - ir_to_mesa_emit_op2(ir, OPCODE_ADD, result_dst, op[0], op[1]); - break; - case ir_binop_sub: - ir_to_mesa_emit_op2(ir, OPCODE_SUB, result_dst, op[0], op[1]); - break; - case ir_binop_mul: - if (ir->operands[0]->type->is_matrix() && - !ir->operands[1]->type->is_matrix()) { - if (ir->operands[0]->type->is_scalar()) { - ir_to_mesa_dst_reg dst_column = result_dst; - ir_to_mesa_src_reg src_column = op[0]; - for (int i = 0; i < ir->operands[0]->type->matrix_columns; i++) { - ir_to_mesa_emit_op2(ir, OPCODE_MUL, - dst_column, src_column, op[1]); - dst_column.index++; - src_column.index++; - } - } else { - ir_to_mesa_dst_reg dst_chan = result_dst; - ir_to_mesa_src_reg src_column = op[0]; - ir_to_mesa_src_reg src_chan = op[1]; - for (int i = 0; i < ir->operands[0]->type->matrix_columns; i++) { - dst_chan.writemask = (1 << i); - src_chan.swizzle = MAKE_SWIZZLE4(i, i, i, i); - ir_to_mesa_emit_op2(ir, OPCODE_MUL, - dst_chan, src_column, src_chan); - src_column.index++; - } - } - } else { - assert(!ir->operands[0]->type->is_matrix()); - assert(!ir->operands[1]->type->is_matrix()); - ir_to_mesa_emit_op2(ir, OPCODE_MUL, result_dst, op[0], op[1]); - } - break; - case ir_binop_div: - ir_to_mesa_emit_scalar_op1(ir, OPCODE_RCP, result_dst, op[1]); - ir_to_mesa_emit_op2(ir, OPCODE_MUL, result_dst, op[0], result_src); - break; - - case ir_binop_less: - ir_to_mesa_emit_op2(ir, OPCODE_SLT, result_dst, op[0], op[1]); - break; - case ir_binop_greater: - ir_to_mesa_emit_op2(ir, OPCODE_SGT, result_dst, op[0], op[1]); - break; - case ir_binop_lequal: - ir_to_mesa_emit_op2(ir, OPCODE_SLE, result_dst, op[0], op[1]); - break; - case ir_binop_gequal: - ir_to_mesa_emit_op2(ir, OPCODE_SGE, result_dst, op[0], op[1]); - break; - case ir_binop_equal: - ir_to_mesa_emit_op2(ir, OPCODE_SEQ, result_dst, op[0], op[1]); - break; - case ir_binop_logic_xor: - case ir_binop_nequal: - ir_to_mesa_emit_op2(ir, OPCODE_SNE, result_dst, op[0], op[1]); - break; - - case ir_binop_logic_or: - /* This could be a saturated add and skip the SNE. */ - ir_to_mesa_emit_op2(ir, OPCODE_ADD, - result_dst, - op[0], op[1]); - - ir_to_mesa_emit_op2(ir, OPCODE_SNE, - result_dst, - result_src, src_reg_for_float(0.0)); - break; - - case ir_binop_logic_and: - /* the bool args are stored as float 0.0 or 1.0, so "mul" gives us "and". */ - ir_to_mesa_emit_op2(ir, OPCODE_MUL, - result_dst, - op[0], op[1]); - break; - - case ir_binop_dot: - if (ir->operands[0]->type == vec4_type) { - assert(ir->operands[1]->type == vec4_type); - ir_to_mesa_emit_op2(ir, OPCODE_DP4, - result_dst, - op[0], op[1]); - } else if (ir->operands[0]->type == vec3_type) { - assert(ir->operands[1]->type == vec3_type); - ir_to_mesa_emit_op2(ir, OPCODE_DP3, - result_dst, - op[0], op[1]); - } else if (ir->operands[0]->type == vec2_type) { - assert(ir->operands[1]->type == vec2_type); - ir_to_mesa_emit_op2(ir, OPCODE_DP2, - result_dst, - op[0], op[1]); - } - break; - case ir_unop_sqrt: - ir_to_mesa_emit_scalar_op1(ir, OPCODE_RSQ, result_dst, op[0]); - ir_to_mesa_emit_op1(ir, OPCODE_RCP, result_dst, result_src); - break; - case ir_unop_rsq: - ir_to_mesa_emit_scalar_op1(ir, OPCODE_RSQ, result_dst, op[0]); - break; - case ir_unop_i2f: - /* Mesa IR lacks types, ints are stored as truncated floats. */ - result_src = op[0]; - break; - case ir_unop_f2i: - ir_to_mesa_emit_op1(ir, OPCODE_TRUNC, result_dst, op[0]); - break; - case ir_unop_f2b: - ir_to_mesa_emit_op2(ir, OPCODE_SNE, result_dst, - result_src, src_reg_for_float(0.0)); - break; - case ir_unop_trunc: - ir_to_mesa_emit_op1(ir, OPCODE_TRUNC, result_dst, op[0]); - break; - case ir_unop_ceil: - op[0].negate = ~op[0].negate; - ir_to_mesa_emit_op1(ir, OPCODE_FLR, result_dst, op[0]); - result_src.negate = ~result_src.negate; - break; - case ir_unop_floor: - ir_to_mesa_emit_op1(ir, OPCODE_FLR, result_dst, op[0]); - break; - case ir_binop_min: - ir_to_mesa_emit_op2(ir, OPCODE_MIN, result_dst, op[0], op[1]); - break; - case ir_binop_max: - ir_to_mesa_emit_op2(ir, OPCODE_MAX, result_dst, op[0], op[1]); - break; - default: - ir_print_visitor v; - printf("Failed to get tree for expression:\n"); - ir->accept(&v); - exit(1); - break; - } - - this->result = result_src; -} - - -void -ir_to_mesa_visitor::visit(ir_swizzle *ir) -{ - ir_to_mesa_src_reg src_reg; - int i; - int swizzle[4]; - - /* Note that this is only swizzles in expressions, not those on the left - * hand side of an assignment, which do write masking. See ir_assignment - * for that. - */ - - ir->val->accept(this); - src_reg = this->result; - assert(src_reg.file != PROGRAM_UNDEFINED); - - for (i = 0; i < 4; i++) { - if (i < ir->type->vector_elements) { - switch (i) { - case 0: - swizzle[i] = ir->mask.x; - break; - case 1: - swizzle[i] = ir->mask.y; - break; - case 2: - swizzle[i] = ir->mask.z; - break; - case 3: - swizzle[i] = ir->mask.w; - break; - } - } else { - /* If the type is smaller than a vec4, replicate the last - * channel out. - */ - swizzle[i] = ir->type->vector_elements - 1; - } - } - - src_reg.swizzle = MAKE_SWIZZLE4(swizzle[0], - swizzle[1], - swizzle[2], - swizzle[3]); - - this->result = src_reg; -} - -void -ir_to_mesa_visitor::visit(ir_dereference_variable *ir) -{ - ir_to_mesa_src_reg src_reg; - temp_entry *entry = find_variable_storage(ir->var); - unsigned int i; - bool var_in; - - if (!entry) { - switch (ir->var->mode) { - case ir_var_uniform: - entry = new(ctx) temp_entry(ir->var, PROGRAM_UNIFORM, - this->next_uniform); - this->variable_storage.push_tail(entry); - - this->next_uniform += type_size(ir->var->type); - break; - case ir_var_in: - case ir_var_out: - case ir_var_inout: - var_in = (ir->var->mode == ir_var_in || - ir->var->mode == ir_var_inout); - - for (i = 0; i < ARRAY_SIZE(builtin_var_to_mesa_reg); i++) { - bool in = builtin_var_to_mesa_reg[i].file == PROGRAM_INPUT; - - if (strcmp(ir->var->name, builtin_var_to_mesa_reg[i].name) == 0 && - !(var_in ^ in)) - break; - } - if (i == ARRAY_SIZE(builtin_var_to_mesa_reg)) { - printf("Failed to find builtin for %s variable %s\n", - var_in ? "in" : "out", - ir->var->name); - abort(); - } - entry = new(ctx) temp_entry(ir->var, - builtin_var_to_mesa_reg[i].file, - builtin_var_to_mesa_reg[i].index); - break; - case ir_var_auto: - entry = new(ctx) temp_entry(ir->var, PROGRAM_TEMPORARY, - this->next_temp); - this->variable_storage.push_tail(entry); - - next_temp += type_size(ir->var->type); - break; - } - - if (!entry) { - printf("Failed to make storage for %s\n", ir->var->name); - exit(1); - } - } - - src_reg.file = entry->file; - src_reg.index = entry->index; - /* If the type is smaller than a vec4, replicate the last channel out. */ - src_reg.swizzle = swizzle_for_size(ir->var->type->vector_elements); - src_reg.reladdr = false; - src_reg.negate = 0; - - this->result = src_reg; -} - -void -ir_to_mesa_visitor::visit(ir_dereference_array *ir) -{ - ir_constant *index; - ir_to_mesa_src_reg src_reg; - - index = ir->array_index->constant_expression_value(); - - /* By the time we make it to this stage, matrices should be broken down - * to vectors. - */ - assert(!ir->type->is_matrix()); - - ir->array->accept(this); - src_reg = this->result; - - if (src_reg.file == PROGRAM_INPUT || - src_reg.file == PROGRAM_OUTPUT) { - assert(index); /* FINISHME: Handle variable indexing of builtins. */ - - src_reg.index += index->value.i[0]; - } else { - if (index) { - src_reg.index += index->value.i[0]; - } else { - ir_to_mesa_src_reg array_base = this->result; - /* Variable index array dereference. It eats the "vec4" of the - * base of the array and an index that offsets the Mesa register - * index. - */ - ir->array_index->accept(this); - - /* FINISHME: This doesn't work when we're trying to do the LHS - * of an assignment. - */ - src_reg.reladdr = true; - ir_to_mesa_emit_op1(ir, OPCODE_ARL, ir_to_mesa_address_reg, - this->result); - - this->result = get_temp(ir->type); - ir_to_mesa_emit_op1(ir, OPCODE_MOV, - ir_to_mesa_dst_reg_from_src(this->result), - src_reg); - } - } - - /* If the type is smaller than a vec4, replicate the last channel out. */ - src_reg.swizzle = swizzle_for_size(ir->type->vector_elements); - - this->result = src_reg; -} - -void -ir_to_mesa_visitor::visit(ir_dereference_record *ir) -{ - unsigned int i; - const glsl_type *struct_type = ir->record->type; - int offset = 0; - - ir->record->accept(this); - - for (i = 0; i < struct_type->length; i++) { - if (strcmp(struct_type->fields.structure[i].name, ir->field) == 0) - break; - offset += type_size(struct_type->fields.structure[i].type); - } - this->result.index += offset; -} - -/** - * We want to be careful in assignment setup to hit the actual storage - * instead of potentially using a temporary like we might with the - * ir_dereference handler. - * - * Thanks to ir_swizzle_swizzle, and ir_vec_index_to_swizzle, we - * should only see potentially one variable array index of a vector, - * and one swizzle, before getting to actual vec4 storage. So handle - * those, then go use ir_dereference to handle the rest. - */ -static struct ir_to_mesa_dst_reg -get_assignment_lhs(ir_instruction *ir, ir_to_mesa_visitor *v) -{ - struct ir_to_mesa_dst_reg dst_reg; - ir_dereference *deref; - ir_swizzle *swiz; - - /* Use the rvalue deref handler for the most part. We'll ignore - * swizzles in it and write swizzles using writemask, though. - */ - ir->accept(v); - dst_reg = ir_to_mesa_dst_reg_from_src(v->result); - - if ((deref = ir->as_dereference())) { - ir_dereference_array *deref_array = ir->as_dereference_array(); - assert(!deref_array || deref_array->array->type->is_array()); - - ir->accept(v); - } else if ((swiz = ir->as_swizzle())) { - dst_reg.writemask = 0; - if (swiz->mask.num_components >= 1) - dst_reg.writemask |= (1 << swiz->mask.x); - if (swiz->mask.num_components >= 2) - dst_reg.writemask |= (1 << swiz->mask.y); - if (swiz->mask.num_components >= 3) - dst_reg.writemask |= (1 << swiz->mask.z); - if (swiz->mask.num_components >= 4) - dst_reg.writemask |= (1 << swiz->mask.w); - } - - return dst_reg; -} - -void -ir_to_mesa_visitor::visit(ir_assignment *ir) -{ - struct ir_to_mesa_dst_reg l; - struct ir_to_mesa_src_reg r; - - assert(!ir->lhs->type->is_matrix()); - assert(!ir->lhs->type->is_array()); - assert(ir->lhs->type->base_type != GLSL_TYPE_STRUCT); - - l = get_assignment_lhs(ir->lhs, this); - - ir->rhs->accept(this); - r = this->result; - assert(l.file != PROGRAM_UNDEFINED); - assert(r.file != PROGRAM_UNDEFINED); - - if (ir->condition) { - ir_constant *condition_constant; - - condition_constant = ir->condition->constant_expression_value(); - - assert(condition_constant && condition_constant->value.b[0]); - } - - ir_to_mesa_emit_op1(ir, OPCODE_MOV, l, r); -} - - -void -ir_to_mesa_visitor::visit(ir_constant *ir) -{ - ir_to_mesa_src_reg src_reg; - - assert(ir->type->base_type == GLSL_TYPE_FLOAT || - ir->type->base_type == GLSL_TYPE_UINT || - ir->type->base_type == GLSL_TYPE_INT || - ir->type->base_type == GLSL_TYPE_BOOL); - - /* FINISHME: This will end up being _mesa_add_unnamed_constant, - * which handles sharing values and sharing channels of vec4 - * constants for small values. - */ - /* FINISHME: Do something with the constant values for now. - */ - src_reg.file = PROGRAM_CONSTANT; - src_reg.index = this->next_constant; - src_reg.swizzle = SWIZZLE_NOOP; - src_reg.reladdr = false; - src_reg.negate = 0; - - this->next_constant += type_size(ir->type); - - this->result = src_reg; -} - - -void -ir_to_mesa_visitor::visit(ir_call *ir) -{ - printf("Can't support call to %s\n", ir->callee_name()); - exit(1); -} - - -void -ir_to_mesa_visitor::visit(ir_texture *ir) -{ - assert(0); - - ir->coordinate->accept(this); -} - -void -ir_to_mesa_visitor::visit(ir_return *ir) -{ - assert(0); - - ir->get_value()->accept(this); -} - - -void -ir_to_mesa_visitor::visit(ir_if *ir) -{ - ir_to_mesa_instruction *if_inst, *else_inst = NULL; - - ir->condition->accept(this); - assert(this->result.file != PROGRAM_UNDEFINED); - - if_inst = ir_to_mesa_emit_op1(ir->condition, - OPCODE_IF, ir_to_mesa_undef_dst, - this->result); - - this->instructions.push_tail(if_inst); - - visit_exec_list(&ir->then_instructions, this); - - if (!ir->else_instructions.is_empty()) { - else_inst = ir_to_mesa_emit_op1(ir->condition, OPCODE_ELSE, - ir_to_mesa_undef_dst, - ir_to_mesa_undef); - visit_exec_list(&ir->then_instructions, this); - } - - if_inst = ir_to_mesa_emit_op1(ir->condition, OPCODE_ENDIF, - ir_to_mesa_undef_dst, ir_to_mesa_undef); -} - -ir_to_mesa_visitor::ir_to_mesa_visitor() -{ - result.file = PROGRAM_UNDEFINED; - next_temp = 1; - next_constant = 0; - next_uniform = 0; -} - -static struct prog_src_register -mesa_src_reg_from_ir_src_reg(ir_to_mesa_src_reg reg) -{ - struct prog_src_register mesa_reg; - - mesa_reg.File = reg.file; - assert(reg.index < (1 << INST_INDEX_BITS) - 1); - mesa_reg.Index = reg.index; - mesa_reg.Swizzle = reg.swizzle; - mesa_reg.RelAddr = reg.reladdr; - - return mesa_reg; -} - -static void -set_branchtargets(struct prog_instruction *mesa_instructions, - int num_instructions) -{ - int if_count = 0, loop_count; - int *if_stack, *loop_stack; - int if_stack_pos = 0, loop_stack_pos = 0; - int i, j; - - for (i = 0; i < num_instructions; i++) { - switch (mesa_instructions[i].Opcode) { - case OPCODE_IF: - if_count++; - break; - case OPCODE_BGNLOOP: - loop_count++; - break; - case OPCODE_BRK: - case OPCODE_CONT: - mesa_instructions[i].BranchTarget = -1; - break; - default: - break; - } - } - - if_stack = (int *)calloc(if_count, sizeof(*if_stack)); - loop_stack = (int *)calloc(loop_count, sizeof(*loop_stack)); - - for (i = 0; i < num_instructions; i++) { - switch (mesa_instructions[i].Opcode) { - case OPCODE_IF: - if_stack[if_stack_pos] = i; - if_stack_pos++; - break; - case OPCODE_ELSE: - mesa_instructions[if_stack[if_stack_pos - 1]].BranchTarget = i; - if_stack[if_stack_pos - 1] = i; - break; - case OPCODE_ENDIF: - mesa_instructions[if_stack[if_stack_pos - 1]].BranchTarget = i; - if_stack_pos--; - break; - case OPCODE_BGNLOOP: - loop_stack[loop_stack_pos] = i; - loop_stack_pos++; - break; - case OPCODE_ENDLOOP: - loop_stack_pos--; - /* Rewrite any breaks/conts at this nesting level (haven't - * already had a BranchTarget assigned) to point to the end - * of the loop. - */ - for (j = loop_stack[loop_stack_pos]; j < i; j++) { - if (mesa_instructions[j].Opcode == OPCODE_BRK || - mesa_instructions[j].Opcode == OPCODE_CONT) { - if (mesa_instructions[j].BranchTarget == -1) { - mesa_instructions[j].BranchTarget = i; - } - } - } - /* 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; - default: - break; - } - } - - free(if_stack); -} - -static void -print_program(struct prog_instruction *mesa_instructions, - ir_instruction **mesa_instruction_annotation, - int num_instructions) -{ - ir_instruction *last_ir = NULL; - int i; - - for (i = 0; i < num_instructions; i++) { - struct prog_instruction *mesa_inst = mesa_instructions + i; - ir_instruction *ir = mesa_instruction_annotation[i]; - - if (last_ir != ir && ir) { - ir_print_visitor print; - ir->accept(&print); - printf("\n"); - last_ir = ir; - } - - _mesa_print_instruction(mesa_inst); - } -} - -void -do_ir_to_mesa(exec_list *instructions) -{ - ir_to_mesa_visitor v; - struct prog_instruction *mesa_instructions, *mesa_inst; - ir_instruction **mesa_instruction_annotation; - int i; - - v.ctx = talloc_new(NULL); - visit_exec_list(instructions, &v); - - int num_instructions = 0; - foreach_iter(exec_list_iterator, iter, v.instructions) { - num_instructions++; - } - - mesa_instructions = - (struct prog_instruction *)calloc(num_instructions, - sizeof(*mesa_instructions)); - mesa_instruction_annotation = - (ir_instruction **)calloc(num_instructions, - sizeof(*mesa_instruction_annotation)); - - mesa_inst = mesa_instructions; - i = 0; - foreach_iter(exec_list_iterator, iter, v.instructions) { - ir_to_mesa_instruction *inst = (ir_to_mesa_instruction *)iter.get(); - - mesa_inst->Opcode = inst->op; - mesa_inst->DstReg.File = inst->dst_reg.file; - mesa_inst->DstReg.Index = inst->dst_reg.index; - mesa_inst->DstReg.CondMask = COND_TR; - mesa_inst->DstReg.WriteMask = inst->dst_reg.writemask; - mesa_inst->SrcReg[0] = mesa_src_reg_from_ir_src_reg(inst->src_reg[0]); - mesa_inst->SrcReg[1] = mesa_src_reg_from_ir_src_reg(inst->src_reg[1]); - mesa_inst->SrcReg[2] = mesa_src_reg_from_ir_src_reg(inst->src_reg[2]); - mesa_instruction_annotation[i] = inst->ir; - - mesa_inst++; - i++; - } - - set_branchtargets(mesa_instructions, num_instructions); - print_program(mesa_instructions, mesa_instruction_annotation, num_instructions); - - free(mesa_instruction_annotation); - talloc_free(v.ctx); -} diff --git a/src/mesa/shader/ir_to_mesa.cpp b/src/mesa/shader/ir_to_mesa.cpp new file mode 100644 index 00000000000..26449c5a5c4 --- /dev/null +++ b/src/mesa/shader/ir_to_mesa.cpp @@ -0,0 +1,1211 @@ +/* + * Copyright © 2010 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +/** + * \file ir_to_mesa.cpp + * + * Translates the IR to ARB_fragment_program text if possible, + * printing the result + */ + +#include +#include "ir.h" +#include "ir_visitor.h" +#include "ir_print_visitor.h" +#include "ir_expression_flattening.h" +#include "glsl_types.h" + +extern "C" { +#include "shader/prog_instruction.h" +#include "shader/prog_print.h" +} + +/** + * This struct is a corresponding struct to Mesa prog_src_register, with + * wider fields. + */ +typedef struct ir_to_mesa_src_reg { + int file; /**< PROGRAM_* from Mesa */ + int index; /**< temporary index, VERT_ATTRIB_*, FRAG_ATTRIB_*, etc. */ + int swizzle; /**< SWIZZLE_XYZWONEZERO swizzles from Mesa. */ + int negate; /**< NEGATE_XYZW mask from mesa */ + bool reladdr; /**< Register index should be offset by address reg. */ +} ir_to_mesa_src_reg; + +typedef struct ir_to_mesa_dst_reg { + int file; /**< PROGRAM_* from Mesa */ + int index; /**< temporary index, VERT_ATTRIB_*, FRAG_ATTRIB_*, etc. */ + int writemask; /**< Bitfield of WRITEMASK_[XYZW] */ +} ir_to_mesa_dst_reg; + +extern ir_to_mesa_src_reg ir_to_mesa_undef; + +class ir_to_mesa_instruction : public exec_node { +public: + enum prog_opcode op; + ir_to_mesa_dst_reg dst_reg; + ir_to_mesa_src_reg src_reg[3]; + /** Pointer to the ir source this tree came from for debugging */ + ir_instruction *ir; +}; + +class temp_entry : public exec_node { +public: + temp_entry(ir_variable *var, int file, int index) + : file(file), index(index), var(var) + { + /* empty */ + } + + int file; + int index; + ir_variable *var; /* variable that maps to this, if any */ +}; + +class ir_to_mesa_visitor : public ir_visitor { +public: + ir_to_mesa_visitor(); + + int next_temp; + int next_constant; + int next_uniform; + + temp_entry *find_variable_storage(ir_variable *var); + + ir_to_mesa_src_reg get_temp(const glsl_type *type); + + struct ir_to_mesa_src_reg src_reg_for_float(float val); + + /** + * \name Visit methods + * + * As typical for the visitor pattern, there must be one \c visit method for + * each concrete subclass of \c ir_instruction. Virtual base classes within + * the hierarchy should not have \c visit methods. + */ + /*@{*/ + virtual void visit(ir_variable *); + virtual void visit(ir_loop *); + virtual void visit(ir_loop_jump *); + virtual void visit(ir_function_signature *); + virtual void visit(ir_function *); + virtual void visit(ir_expression *); + virtual void visit(ir_swizzle *); + virtual void visit(ir_dereference_variable *); + virtual void visit(ir_dereference_array *); + virtual void visit(ir_dereference_record *); + virtual void visit(ir_assignment *); + virtual void visit(ir_constant *); + virtual void visit(ir_call *); + virtual void visit(ir_return *); + virtual void visit(ir_texture *); + virtual void visit(ir_if *); + /*@}*/ + + struct ir_to_mesa_src_reg result; + + /** List of temp_entry */ + exec_list variable_storage; + + /** List of ir_to_mesa_instruction */ + exec_list instructions; + + ir_to_mesa_instruction *ir_to_mesa_emit_op1(ir_instruction *ir, + enum prog_opcode op, + ir_to_mesa_dst_reg dst, + ir_to_mesa_src_reg src0); + + ir_to_mesa_instruction *ir_to_mesa_emit_op2(ir_instruction *ir, + enum prog_opcode op, + ir_to_mesa_dst_reg dst, + ir_to_mesa_src_reg src0, + ir_to_mesa_src_reg src1); + + ir_to_mesa_instruction *ir_to_mesa_emit_op3(ir_instruction *ir, + enum prog_opcode op, + ir_to_mesa_dst_reg dst, + ir_to_mesa_src_reg src0, + ir_to_mesa_src_reg src1, + ir_to_mesa_src_reg src2); + + void ir_to_mesa_emit_scalar_op1(ir_instruction *ir, + enum prog_opcode op, + ir_to_mesa_dst_reg dst, + ir_to_mesa_src_reg src0); + + /* talloc context (the ) */ + void *ctx; +}; + +ir_to_mesa_src_reg ir_to_mesa_undef = { + PROGRAM_UNDEFINED, 0, SWIZZLE_NOOP, NEGATE_NONE, false, +}; + +ir_to_mesa_dst_reg ir_to_mesa_undef_dst = { + PROGRAM_UNDEFINED, 0, SWIZZLE_NOOP +}; + +ir_to_mesa_dst_reg ir_to_mesa_address_reg = { + PROGRAM_ADDRESS, 0, WRITEMASK_X +}; + +static int swizzle_for_size(int size) +{ + int size_swizzles[4] = { + MAKE_SWIZZLE4(SWIZZLE_X, SWIZZLE_X, SWIZZLE_X, SWIZZLE_X), + MAKE_SWIZZLE4(SWIZZLE_X, SWIZZLE_Y, SWIZZLE_Y, SWIZZLE_Y), + MAKE_SWIZZLE4(SWIZZLE_X, SWIZZLE_Y, SWIZZLE_Z, SWIZZLE_Z), + MAKE_SWIZZLE4(SWIZZLE_X, SWIZZLE_Y, SWIZZLE_Z, SWIZZLE_W), + }; + + return size_swizzles[size - 1]; +} + +/* This list should match up with builtin_variables.h */ +static const struct { + const char *name; + int file; + int index; +} builtin_var_to_mesa_reg[] = { + /* core_vs */ + {"gl_Position", PROGRAM_OUTPUT, VERT_RESULT_HPOS}, + {"gl_PointSize", PROGRAM_OUTPUT, VERT_RESULT_PSIZ}, + + /* core_fs */ + {"gl_FragCoord", PROGRAM_INPUT, FRAG_ATTRIB_WPOS}, + {"gl_FrontFacing", PROGRAM_INPUT, FRAG_ATTRIB_FACE}, + {"gl_FragColor", PROGRAM_OUTPUT, FRAG_ATTRIB_COL0}, + {"gl_FragDepth", PROGRAM_UNDEFINED, FRAG_ATTRIB_WPOS}, /* FINISHME: WPOS.z */ + + /* 110_deprecated_fs */ + {"gl_Color", PROGRAM_INPUT, FRAG_ATTRIB_COL0}, + {"gl_SecondaryColor", PROGRAM_INPUT, FRAG_ATTRIB_COL1}, + {"gl_FogFragCoord", PROGRAM_INPUT, FRAG_ATTRIB_FOGC}, + {"gl_TexCoord", PROGRAM_INPUT, FRAG_ATTRIB_TEX0}, /* array */ + + /* 110_deprecated_vs */ + {"gl_Vertex", PROGRAM_INPUT, VERT_ATTRIB_POS}, + {"gl_Normal", PROGRAM_INPUT, VERT_ATTRIB_NORMAL}, + {"gl_Color", PROGRAM_INPUT, VERT_ATTRIB_COLOR0}, + {"gl_SecondaryColor", PROGRAM_INPUT, VERT_ATTRIB_COLOR1}, + {"gl_MultiTexCoord0", PROGRAM_INPUT, VERT_ATTRIB_TEX0}, + {"gl_MultiTexCoord1", PROGRAM_INPUT, VERT_ATTRIB_TEX1}, + {"gl_MultiTexCoord2", PROGRAM_INPUT, VERT_ATTRIB_TEX2}, + {"gl_MultiTexCoord3", PROGRAM_INPUT, VERT_ATTRIB_TEX3}, + {"gl_MultiTexCoord4", PROGRAM_INPUT, VERT_ATTRIB_TEX4}, + {"gl_MultiTexCoord5", PROGRAM_INPUT, VERT_ATTRIB_TEX5}, + {"gl_MultiTexCoord6", PROGRAM_INPUT, VERT_ATTRIB_TEX6}, + {"gl_MultiTexCoord7", PROGRAM_INPUT, VERT_ATTRIB_TEX7}, + {"gl_TexCoord", PROGRAM_OUTPUT, VERT_RESULT_TEX0}, /* array */ + {"gl_FogCoord", PROGRAM_INPUT, VERT_RESULT_FOGC}, + /*{"gl_ClipVertex", PROGRAM_OUTPUT, VERT_ATTRIB_FOGC},*/ /* FINISHME */ + {"gl_FrontColor", PROGRAM_OUTPUT, VERT_RESULT_COL0}, + {"gl_BackColor", PROGRAM_OUTPUT, VERT_RESULT_BFC0}, + {"gl_FrontSecondaryColor", PROGRAM_OUTPUT, VERT_RESULT_COL1}, + {"gl_BackSecondaryColor", PROGRAM_OUTPUT, VERT_RESULT_BFC1}, + {"gl_FogFragCoord", PROGRAM_OUTPUT, VERT_RESULT_FOGC}, + + /* 130_vs */ + /*{"gl_VertexID", PROGRAM_INPUT, VERT_ATTRIB_FOGC},*/ /* FINISHME */ + + {"gl_FragData", PROGRAM_OUTPUT, FRAG_RESULT_DATA0}, /* array */ +}; + +ir_to_mesa_instruction * +ir_to_mesa_visitor::ir_to_mesa_emit_op3(ir_instruction *ir, + enum prog_opcode op, + ir_to_mesa_dst_reg dst, + ir_to_mesa_src_reg src0, + ir_to_mesa_src_reg src1, + ir_to_mesa_src_reg src2) +{ + ir_to_mesa_instruction *inst = new(ctx) ir_to_mesa_instruction(); + + inst->op = op; + inst->dst_reg = dst; + inst->src_reg[0] = src0; + inst->src_reg[1] = src1; + inst->src_reg[2] = src2; + inst->ir = ir; + + this->instructions.push_tail(inst); + + return inst; +} + + +ir_to_mesa_instruction * +ir_to_mesa_visitor::ir_to_mesa_emit_op2(ir_instruction *ir, + enum prog_opcode op, + ir_to_mesa_dst_reg dst, + ir_to_mesa_src_reg src0, + ir_to_mesa_src_reg src1) +{ + return ir_to_mesa_emit_op3(ir, op, dst, src0, src1, ir_to_mesa_undef); +} + +ir_to_mesa_instruction * +ir_to_mesa_visitor::ir_to_mesa_emit_op1(ir_instruction *ir, + enum prog_opcode op, + ir_to_mesa_dst_reg dst, + ir_to_mesa_src_reg src0) +{ + return ir_to_mesa_emit_op3(ir, op, dst, + src0, ir_to_mesa_undef, ir_to_mesa_undef); +} + +inline ir_to_mesa_dst_reg +ir_to_mesa_dst_reg_from_src(ir_to_mesa_src_reg reg) +{ + ir_to_mesa_dst_reg dst_reg; + + dst_reg.file = reg.file; + dst_reg.index = reg.index; + dst_reg.writemask = WRITEMASK_XYZW; + + return dst_reg; +} + +/** + * Emits Mesa scalar opcodes to produce unique answers across channels. + * + * Some Mesa opcodes are scalar-only, like ARB_fp/vp. The src X + * channel determines the result across all channels. So to do a vec4 + * of this operation, we want to emit a scalar per source channel used + * to produce dest channels. + */ +void +ir_to_mesa_visitor::ir_to_mesa_emit_scalar_op1(ir_instruction *ir, + enum prog_opcode op, + ir_to_mesa_dst_reg dst, + ir_to_mesa_src_reg src0) +{ + int i, j; + int done_mask = ~dst.writemask; + + /* Mesa RCP is a scalar operation splatting results to all channels, + * like ARB_fp/vp. So emit as many RCPs as necessary to cover our + * dst channels. + */ + for (i = 0; i < 4; i++) { + int this_mask = (1 << i); + ir_to_mesa_instruction *inst; + ir_to_mesa_src_reg src = src0; + + if (done_mask & this_mask) + continue; + + int src_swiz = GET_SWZ(src.swizzle, i); + for (j = i + 1; j < 4; j++) { + if (!(done_mask & (1 << j)) && GET_SWZ(src.swizzle, j) == src_swiz) { + this_mask |= (1 << j); + } + } + src.swizzle = MAKE_SWIZZLE4(src_swiz, src_swiz, + src_swiz, src_swiz); + + inst = ir_to_mesa_emit_op1(ir, op, + dst, + src); + inst->dst_reg.writemask = this_mask; + done_mask |= this_mask; + } +} + +struct ir_to_mesa_src_reg +ir_to_mesa_visitor::src_reg_for_float(float val) +{ + ir_to_mesa_src_reg src_reg; + + /* FINISHME: This will end up being _mesa_add_unnamed_constant, + * which handles sharing values and sharing channels of vec4 + * constants for small values. + */ + /* FINISHME: Do something with the constant values for now. + */ + (void)val; + src_reg.file = PROGRAM_CONSTANT; + src_reg.index = this->next_constant++; + src_reg.swizzle = SWIZZLE_NOOP; + + return src_reg; +} + +/** + * In the initial pass of codegen, we assign temporary numbers to + * intermediate results. (not SSA -- variable assignments will reuse + * storage). Actual register allocation for the Mesa VM occurs in a + * pass over the Mesa IR later. + */ +ir_to_mesa_src_reg +ir_to_mesa_visitor::get_temp(const glsl_type *type) +{ + ir_to_mesa_src_reg src_reg; + int swizzle[4]; + int i; + + assert(!type->is_array()); + + src_reg.file = PROGRAM_TEMPORARY; + src_reg.index = type->matrix_columns; + src_reg.reladdr = false; + + for (i = 0; i < type->vector_elements; i++) + swizzle[i] = i; + for (; i < 4; i++) + swizzle[i] = type->vector_elements - 1; + src_reg.swizzle = MAKE_SWIZZLE4(swizzle[0], swizzle[1], + swizzle[2], swizzle[3]); + + return src_reg; +} + +static int +type_size(const struct glsl_type *type) +{ + unsigned int i; + int size; + + switch (type->base_type) { + case GLSL_TYPE_UINT: + case GLSL_TYPE_INT: + case GLSL_TYPE_FLOAT: + case GLSL_TYPE_BOOL: + if (type->is_matrix()) { + return 4; /* FINISHME: Not all matrices are 4x4. */ + } else { + /* Regardless of size of vector, it gets a vec4. This is bad + * packing for things like floats, but otherwise arrays become a + * mess. Hopefully a later pass over the code can pack scalars + * down if appropriate. + */ + return 1; + } + case GLSL_TYPE_ARRAY: + return type_size(type->fields.array) * type->length; + case GLSL_TYPE_STRUCT: + size = 0; + for (i = 0; i < type->length; i++) { + size += type_size(type->fields.structure[i].type); + } + return size; + default: + assert(0); + } +} + +temp_entry * +ir_to_mesa_visitor::find_variable_storage(ir_variable *var) +{ + + temp_entry *entry; + + foreach_iter(exec_list_iterator, iter, this->variable_storage) { + entry = (temp_entry *)iter.get(); + + if (entry->var == var) + return entry; + } + + return NULL; +} + +void +ir_to_mesa_visitor::visit(ir_variable *ir) +{ + (void)ir; +} + +void +ir_to_mesa_visitor::visit(ir_loop *ir) +{ + assert(!ir->from); + assert(!ir->to); + assert(!ir->increment); + assert(!ir->counter); + + ir_to_mesa_emit_op1(NULL, OPCODE_BGNLOOP, + ir_to_mesa_undef_dst, ir_to_mesa_undef); + + visit_exec_list(&ir->body_instructions, this); + + ir_to_mesa_emit_op1(NULL, OPCODE_ENDLOOP, + ir_to_mesa_undef_dst, ir_to_mesa_undef); +} + +void +ir_to_mesa_visitor::visit(ir_loop_jump *ir) +{ + switch (ir->mode) { + case ir_loop_jump::jump_break: + ir_to_mesa_emit_op1(NULL, OPCODE_BRK, + ir_to_mesa_undef_dst, ir_to_mesa_undef); + break; + case ir_loop_jump::jump_continue: + ir_to_mesa_emit_op1(NULL, OPCODE_CONT, + ir_to_mesa_undef_dst, ir_to_mesa_undef); + break; + } +} + + +void +ir_to_mesa_visitor::visit(ir_function_signature *ir) +{ + assert(0); + (void)ir; +} + +void +ir_to_mesa_visitor::visit(ir_function *ir) +{ + /* Ignore function bodies other than main() -- we shouldn't see calls to + * them since they should all be inlined before we get to ir_to_mesa. + */ + if (strcmp(ir->name, "main") == 0) { + const ir_function_signature *sig; + exec_list empty; + + sig = ir->matching_signature(&empty); + + assert(sig); + + foreach_iter(exec_list_iterator, iter, sig->body) { + ir_instruction *ir = (ir_instruction *)iter.get(); + + ir->accept(this); + } + } +} + +void +ir_to_mesa_visitor::visit(ir_expression *ir) +{ + unsigned int operand; + struct ir_to_mesa_src_reg op[2]; + struct ir_to_mesa_src_reg result_src; + struct ir_to_mesa_dst_reg result_dst; + const glsl_type *vec4_type = glsl_type::get_instance(GLSL_TYPE_FLOAT, 4, 1); + const glsl_type *vec3_type = glsl_type::get_instance(GLSL_TYPE_FLOAT, 3, 1); + const glsl_type *vec2_type = glsl_type::get_instance(GLSL_TYPE_FLOAT, 2, 1); + + for (operand = 0; operand < ir->get_num_operands(); operand++) { + this->result.file = PROGRAM_UNDEFINED; + ir->operands[operand]->accept(this); + if (this->result.file == PROGRAM_UNDEFINED) { + ir_print_visitor v; + printf("Failed to get tree for expression operand:\n"); + ir->operands[operand]->accept(&v); + exit(1); + } + op[operand] = this->result; + + /* Only expression implemented for matrices yet */ + assert(!ir->operands[operand]->type->is_matrix() || + ir->operation == ir_binop_mul); + } + + this->result.file = PROGRAM_UNDEFINED; + + /* Storage for our result. Ideally for an assignment we'd be using + * the actual storage for the result here, instead. + */ + result_src = get_temp(ir->type); + /* convenience for the emit functions below. */ + result_dst = ir_to_mesa_dst_reg_from_src(result_src); + /* Limit writes to the channels that will be used by result_src later. + * This does limit this temp's use as a temporary for multi-instruction + * sequences. + */ + result_dst.writemask = (1 << ir->type->vector_elements) - 1; + + switch (ir->operation) { + case ir_unop_logic_not: + ir_to_mesa_emit_op2(ir, OPCODE_SEQ, result_dst, + op[0], src_reg_for_float(0.0)); + break; + case ir_unop_neg: + op[0].negate = ~op[0].negate; + result_src = op[0]; + break; + case ir_unop_exp: + ir_to_mesa_emit_scalar_op1(ir, OPCODE_EXP, result_dst, op[0]); + break; + case ir_unop_exp2: + ir_to_mesa_emit_scalar_op1(ir, OPCODE_EX2, result_dst, op[0]); + break; + case ir_unop_log: + ir_to_mesa_emit_scalar_op1(ir, OPCODE_LOG, result_dst, op[0]); + break; + case ir_unop_log2: + ir_to_mesa_emit_scalar_op1(ir, OPCODE_LG2, result_dst, op[0]); + break; + case ir_unop_sin: + ir_to_mesa_emit_scalar_op1(ir, OPCODE_SIN, result_dst, op[0]); + break; + case ir_unop_cos: + ir_to_mesa_emit_scalar_op1(ir, OPCODE_COS, result_dst, op[0]); + break; + case ir_binop_add: + ir_to_mesa_emit_op2(ir, OPCODE_ADD, result_dst, op[0], op[1]); + break; + case ir_binop_sub: + ir_to_mesa_emit_op2(ir, OPCODE_SUB, result_dst, op[0], op[1]); + break; + case ir_binop_mul: + if (ir->operands[0]->type->is_matrix() && + !ir->operands[1]->type->is_matrix()) { + if (ir->operands[0]->type->is_scalar()) { + ir_to_mesa_dst_reg dst_column = result_dst; + ir_to_mesa_src_reg src_column = op[0]; + for (int i = 0; i < ir->operands[0]->type->matrix_columns; i++) { + ir_to_mesa_emit_op2(ir, OPCODE_MUL, + dst_column, src_column, op[1]); + dst_column.index++; + src_column.index++; + } + } else { + ir_to_mesa_dst_reg dst_chan = result_dst; + ir_to_mesa_src_reg src_column = op[0]; + ir_to_mesa_src_reg src_chan = op[1]; + for (int i = 0; i < ir->operands[0]->type->matrix_columns; i++) { + dst_chan.writemask = (1 << i); + src_chan.swizzle = MAKE_SWIZZLE4(i, i, i, i); + ir_to_mesa_emit_op2(ir, OPCODE_MUL, + dst_chan, src_column, src_chan); + src_column.index++; + } + } + } else { + assert(!ir->operands[0]->type->is_matrix()); + assert(!ir->operands[1]->type->is_matrix()); + ir_to_mesa_emit_op2(ir, OPCODE_MUL, result_dst, op[0], op[1]); + } + break; + case ir_binop_div: + ir_to_mesa_emit_scalar_op1(ir, OPCODE_RCP, result_dst, op[1]); + ir_to_mesa_emit_op2(ir, OPCODE_MUL, result_dst, op[0], result_src); + break; + + case ir_binop_less: + ir_to_mesa_emit_op2(ir, OPCODE_SLT, result_dst, op[0], op[1]); + break; + case ir_binop_greater: + ir_to_mesa_emit_op2(ir, OPCODE_SGT, result_dst, op[0], op[1]); + break; + case ir_binop_lequal: + ir_to_mesa_emit_op2(ir, OPCODE_SLE, result_dst, op[0], op[1]); + break; + case ir_binop_gequal: + ir_to_mesa_emit_op2(ir, OPCODE_SGE, result_dst, op[0], op[1]); + break; + case ir_binop_equal: + ir_to_mesa_emit_op2(ir, OPCODE_SEQ, result_dst, op[0], op[1]); + break; + case ir_binop_logic_xor: + case ir_binop_nequal: + ir_to_mesa_emit_op2(ir, OPCODE_SNE, result_dst, op[0], op[1]); + break; + + case ir_binop_logic_or: + /* This could be a saturated add and skip the SNE. */ + ir_to_mesa_emit_op2(ir, OPCODE_ADD, + result_dst, + op[0], op[1]); + + ir_to_mesa_emit_op2(ir, OPCODE_SNE, + result_dst, + result_src, src_reg_for_float(0.0)); + break; + + case ir_binop_logic_and: + /* the bool args are stored as float 0.0 or 1.0, so "mul" gives us "and". */ + ir_to_mesa_emit_op2(ir, OPCODE_MUL, + result_dst, + op[0], op[1]); + break; + + case ir_binop_dot: + if (ir->operands[0]->type == vec4_type) { + assert(ir->operands[1]->type == vec4_type); + ir_to_mesa_emit_op2(ir, OPCODE_DP4, + result_dst, + op[0], op[1]); + } else if (ir->operands[0]->type == vec3_type) { + assert(ir->operands[1]->type == vec3_type); + ir_to_mesa_emit_op2(ir, OPCODE_DP3, + result_dst, + op[0], op[1]); + } else if (ir->operands[0]->type == vec2_type) { + assert(ir->operands[1]->type == vec2_type); + ir_to_mesa_emit_op2(ir, OPCODE_DP2, + result_dst, + op[0], op[1]); + } + break; + case ir_unop_sqrt: + ir_to_mesa_emit_scalar_op1(ir, OPCODE_RSQ, result_dst, op[0]); + ir_to_mesa_emit_op1(ir, OPCODE_RCP, result_dst, result_src); + break; + case ir_unop_rsq: + ir_to_mesa_emit_scalar_op1(ir, OPCODE_RSQ, result_dst, op[0]); + break; + case ir_unop_i2f: + /* Mesa IR lacks types, ints are stored as truncated floats. */ + result_src = op[0]; + break; + case ir_unop_f2i: + ir_to_mesa_emit_op1(ir, OPCODE_TRUNC, result_dst, op[0]); + break; + case ir_unop_f2b: + ir_to_mesa_emit_op2(ir, OPCODE_SNE, result_dst, + result_src, src_reg_for_float(0.0)); + break; + case ir_unop_trunc: + ir_to_mesa_emit_op1(ir, OPCODE_TRUNC, result_dst, op[0]); + break; + case ir_unop_ceil: + op[0].negate = ~op[0].negate; + ir_to_mesa_emit_op1(ir, OPCODE_FLR, result_dst, op[0]); + result_src.negate = ~result_src.negate; + break; + case ir_unop_floor: + ir_to_mesa_emit_op1(ir, OPCODE_FLR, result_dst, op[0]); + break; + case ir_binop_min: + ir_to_mesa_emit_op2(ir, OPCODE_MIN, result_dst, op[0], op[1]); + break; + case ir_binop_max: + ir_to_mesa_emit_op2(ir, OPCODE_MAX, result_dst, op[0], op[1]); + break; + default: + ir_print_visitor v; + printf("Failed to get tree for expression:\n"); + ir->accept(&v); + exit(1); + break; + } + + this->result = result_src; +} + + +void +ir_to_mesa_visitor::visit(ir_swizzle *ir) +{ + ir_to_mesa_src_reg src_reg; + int i; + int swizzle[4]; + + /* Note that this is only swizzles in expressions, not those on the left + * hand side of an assignment, which do write masking. See ir_assignment + * for that. + */ + + ir->val->accept(this); + src_reg = this->result; + assert(src_reg.file != PROGRAM_UNDEFINED); + + for (i = 0; i < 4; i++) { + if (i < ir->type->vector_elements) { + switch (i) { + case 0: + swizzle[i] = ir->mask.x; + break; + case 1: + swizzle[i] = ir->mask.y; + break; + case 2: + swizzle[i] = ir->mask.z; + break; + case 3: + swizzle[i] = ir->mask.w; + break; + } + } else { + /* If the type is smaller than a vec4, replicate the last + * channel out. + */ + swizzle[i] = ir->type->vector_elements - 1; + } + } + + src_reg.swizzle = MAKE_SWIZZLE4(swizzle[0], + swizzle[1], + swizzle[2], + swizzle[3]); + + this->result = src_reg; +} + +void +ir_to_mesa_visitor::visit(ir_dereference_variable *ir) +{ + ir_to_mesa_src_reg src_reg; + temp_entry *entry = find_variable_storage(ir->var); + unsigned int i; + bool var_in; + + if (!entry) { + switch (ir->var->mode) { + case ir_var_uniform: + entry = new(ctx) temp_entry(ir->var, PROGRAM_UNIFORM, + this->next_uniform); + this->variable_storage.push_tail(entry); + + this->next_uniform += type_size(ir->var->type); + break; + case ir_var_in: + case ir_var_out: + case ir_var_inout: + var_in = (ir->var->mode == ir_var_in || + ir->var->mode == ir_var_inout); + + for (i = 0; i < ARRAY_SIZE(builtin_var_to_mesa_reg); i++) { + bool in = builtin_var_to_mesa_reg[i].file == PROGRAM_INPUT; + + if (strcmp(ir->var->name, builtin_var_to_mesa_reg[i].name) == 0 && + !(var_in ^ in)) + break; + } + if (i == ARRAY_SIZE(builtin_var_to_mesa_reg)) { + printf("Failed to find builtin for %s variable %s\n", + var_in ? "in" : "out", + ir->var->name); + abort(); + } + entry = new(ctx) temp_entry(ir->var, + builtin_var_to_mesa_reg[i].file, + builtin_var_to_mesa_reg[i].index); + break; + case ir_var_auto: + entry = new(ctx) temp_entry(ir->var, PROGRAM_TEMPORARY, + this->next_temp); + this->variable_storage.push_tail(entry); + + next_temp += type_size(ir->var->type); + break; + } + + if (!entry) { + printf("Failed to make storage for %s\n", ir->var->name); + exit(1); + } + } + + src_reg.file = entry->file; + src_reg.index = entry->index; + /* If the type is smaller than a vec4, replicate the last channel out. */ + src_reg.swizzle = swizzle_for_size(ir->var->type->vector_elements); + src_reg.reladdr = false; + src_reg.negate = 0; + + this->result = src_reg; +} + +void +ir_to_mesa_visitor::visit(ir_dereference_array *ir) +{ + ir_constant *index; + ir_to_mesa_src_reg src_reg; + + index = ir->array_index->constant_expression_value(); + + /* By the time we make it to this stage, matrices should be broken down + * to vectors. + */ + assert(!ir->type->is_matrix()); + + ir->array->accept(this); + src_reg = this->result; + + if (src_reg.file == PROGRAM_INPUT || + src_reg.file == PROGRAM_OUTPUT) { + assert(index); /* FINISHME: Handle variable indexing of builtins. */ + + src_reg.index += index->value.i[0]; + } else { + if (index) { + src_reg.index += index->value.i[0]; + } else { + ir_to_mesa_src_reg array_base = this->result; + /* Variable index array dereference. It eats the "vec4" of the + * base of the array and an index that offsets the Mesa register + * index. + */ + ir->array_index->accept(this); + + /* FINISHME: This doesn't work when we're trying to do the LHS + * of an assignment. + */ + src_reg.reladdr = true; + ir_to_mesa_emit_op1(ir, OPCODE_ARL, ir_to_mesa_address_reg, + this->result); + + this->result = get_temp(ir->type); + ir_to_mesa_emit_op1(ir, OPCODE_MOV, + ir_to_mesa_dst_reg_from_src(this->result), + src_reg); + } + } + + /* If the type is smaller than a vec4, replicate the last channel out. */ + src_reg.swizzle = swizzle_for_size(ir->type->vector_elements); + + this->result = src_reg; +} + +void +ir_to_mesa_visitor::visit(ir_dereference_record *ir) +{ + unsigned int i; + const glsl_type *struct_type = ir->record->type; + int offset = 0; + + ir->record->accept(this); + + for (i = 0; i < struct_type->length; i++) { + if (strcmp(struct_type->fields.structure[i].name, ir->field) == 0) + break; + offset += type_size(struct_type->fields.structure[i].type); + } + this->result.index += offset; +} + +/** + * We want to be careful in assignment setup to hit the actual storage + * instead of potentially using a temporary like we might with the + * ir_dereference handler. + * + * Thanks to ir_swizzle_swizzle, and ir_vec_index_to_swizzle, we + * should only see potentially one variable array index of a vector, + * and one swizzle, before getting to actual vec4 storage. So handle + * those, then go use ir_dereference to handle the rest. + */ +static struct ir_to_mesa_dst_reg +get_assignment_lhs(ir_instruction *ir, ir_to_mesa_visitor *v) +{ + struct ir_to_mesa_dst_reg dst_reg; + ir_dereference *deref; + ir_swizzle *swiz; + + /* Use the rvalue deref handler for the most part. We'll ignore + * swizzles in it and write swizzles using writemask, though. + */ + ir->accept(v); + dst_reg = ir_to_mesa_dst_reg_from_src(v->result); + + if ((deref = ir->as_dereference())) { + ir_dereference_array *deref_array = ir->as_dereference_array(); + assert(!deref_array || deref_array->array->type->is_array()); + + ir->accept(v); + } else if ((swiz = ir->as_swizzle())) { + dst_reg.writemask = 0; + if (swiz->mask.num_components >= 1) + dst_reg.writemask |= (1 << swiz->mask.x); + if (swiz->mask.num_components >= 2) + dst_reg.writemask |= (1 << swiz->mask.y); + if (swiz->mask.num_components >= 3) + dst_reg.writemask |= (1 << swiz->mask.z); + if (swiz->mask.num_components >= 4) + dst_reg.writemask |= (1 << swiz->mask.w); + } + + return dst_reg; +} + +void +ir_to_mesa_visitor::visit(ir_assignment *ir) +{ + struct ir_to_mesa_dst_reg l; + struct ir_to_mesa_src_reg r; + + assert(!ir->lhs->type->is_matrix()); + assert(!ir->lhs->type->is_array()); + assert(ir->lhs->type->base_type != GLSL_TYPE_STRUCT); + + l = get_assignment_lhs(ir->lhs, this); + + ir->rhs->accept(this); + r = this->result; + assert(l.file != PROGRAM_UNDEFINED); + assert(r.file != PROGRAM_UNDEFINED); + + if (ir->condition) { + ir_constant *condition_constant; + + condition_constant = ir->condition->constant_expression_value(); + + assert(condition_constant && condition_constant->value.b[0]); + } + + ir_to_mesa_emit_op1(ir, OPCODE_MOV, l, r); +} + + +void +ir_to_mesa_visitor::visit(ir_constant *ir) +{ + ir_to_mesa_src_reg src_reg; + + assert(ir->type->base_type == GLSL_TYPE_FLOAT || + ir->type->base_type == GLSL_TYPE_UINT || + ir->type->base_type == GLSL_TYPE_INT || + ir->type->base_type == GLSL_TYPE_BOOL); + + /* FINISHME: This will end up being _mesa_add_unnamed_constant, + * which handles sharing values and sharing channels of vec4 + * constants for small values. + */ + /* FINISHME: Do something with the constant values for now. + */ + src_reg.file = PROGRAM_CONSTANT; + src_reg.index = this->next_constant; + src_reg.swizzle = SWIZZLE_NOOP; + src_reg.reladdr = false; + src_reg.negate = 0; + + this->next_constant += type_size(ir->type); + + this->result = src_reg; +} + + +void +ir_to_mesa_visitor::visit(ir_call *ir) +{ + printf("Can't support call to %s\n", ir->callee_name()); + exit(1); +} + + +void +ir_to_mesa_visitor::visit(ir_texture *ir) +{ + assert(0); + + ir->coordinate->accept(this); +} + +void +ir_to_mesa_visitor::visit(ir_return *ir) +{ + assert(0); + + ir->get_value()->accept(this); +} + + +void +ir_to_mesa_visitor::visit(ir_if *ir) +{ + ir_to_mesa_instruction *if_inst, *else_inst = NULL; + + ir->condition->accept(this); + assert(this->result.file != PROGRAM_UNDEFINED); + + if_inst = ir_to_mesa_emit_op1(ir->condition, + OPCODE_IF, ir_to_mesa_undef_dst, + this->result); + + this->instructions.push_tail(if_inst); + + visit_exec_list(&ir->then_instructions, this); + + if (!ir->else_instructions.is_empty()) { + else_inst = ir_to_mesa_emit_op1(ir->condition, OPCODE_ELSE, + ir_to_mesa_undef_dst, + ir_to_mesa_undef); + visit_exec_list(&ir->then_instructions, this); + } + + if_inst = ir_to_mesa_emit_op1(ir->condition, OPCODE_ENDIF, + ir_to_mesa_undef_dst, ir_to_mesa_undef); +} + +ir_to_mesa_visitor::ir_to_mesa_visitor() +{ + result.file = PROGRAM_UNDEFINED; + next_temp = 1; + next_constant = 0; + next_uniform = 0; +} + +static struct prog_src_register +mesa_src_reg_from_ir_src_reg(ir_to_mesa_src_reg reg) +{ + struct prog_src_register mesa_reg; + + mesa_reg.File = reg.file; + assert(reg.index < (1 << INST_INDEX_BITS) - 1); + mesa_reg.Index = reg.index; + mesa_reg.Swizzle = reg.swizzle; + mesa_reg.RelAddr = reg.reladdr; + + return mesa_reg; +} + +static void +set_branchtargets(struct prog_instruction *mesa_instructions, + int num_instructions) +{ + int if_count = 0, loop_count; + int *if_stack, *loop_stack; + int if_stack_pos = 0, loop_stack_pos = 0; + int i, j; + + for (i = 0; i < num_instructions; i++) { + switch (mesa_instructions[i].Opcode) { + case OPCODE_IF: + if_count++; + break; + case OPCODE_BGNLOOP: + loop_count++; + break; + case OPCODE_BRK: + case OPCODE_CONT: + mesa_instructions[i].BranchTarget = -1; + break; + default: + break; + } + } + + if_stack = (int *)calloc(if_count, sizeof(*if_stack)); + loop_stack = (int *)calloc(loop_count, sizeof(*loop_stack)); + + for (i = 0; i < num_instructions; i++) { + switch (mesa_instructions[i].Opcode) { + case OPCODE_IF: + if_stack[if_stack_pos] = i; + if_stack_pos++; + break; + case OPCODE_ELSE: + mesa_instructions[if_stack[if_stack_pos - 1]].BranchTarget = i; + if_stack[if_stack_pos - 1] = i; + break; + case OPCODE_ENDIF: + mesa_instructions[if_stack[if_stack_pos - 1]].BranchTarget = i; + if_stack_pos--; + break; + case OPCODE_BGNLOOP: + loop_stack[loop_stack_pos] = i; + loop_stack_pos++; + break; + case OPCODE_ENDLOOP: + loop_stack_pos--; + /* Rewrite any breaks/conts at this nesting level (haven't + * already had a BranchTarget assigned) to point to the end + * of the loop. + */ + for (j = loop_stack[loop_stack_pos]; j < i; j++) { + if (mesa_instructions[j].Opcode == OPCODE_BRK || + mesa_instructions[j].Opcode == OPCODE_CONT) { + if (mesa_instructions[j].BranchTarget == -1) { + mesa_instructions[j].BranchTarget = i; + } + } + } + /* 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; + default: + break; + } + } + + free(if_stack); +} + +static void +print_program(struct prog_instruction *mesa_instructions, + ir_instruction **mesa_instruction_annotation, + int num_instructions) +{ + ir_instruction *last_ir = NULL; + int i; + + for (i = 0; i < num_instructions; i++) { + struct prog_instruction *mesa_inst = mesa_instructions + i; + ir_instruction *ir = mesa_instruction_annotation[i]; + + if (last_ir != ir && ir) { + ir_print_visitor print; + ir->accept(&print); + printf("\n"); + last_ir = ir; + } + + _mesa_print_instruction(mesa_inst); + } +} + +void +do_ir_to_mesa(exec_list *instructions) +{ + ir_to_mesa_visitor v; + struct prog_instruction *mesa_instructions, *mesa_inst; + ir_instruction **mesa_instruction_annotation; + int i; + + v.ctx = talloc_new(NULL); + visit_exec_list(instructions, &v); + + int num_instructions = 0; + foreach_iter(exec_list_iterator, iter, v.instructions) { + num_instructions++; + } + + mesa_instructions = + (struct prog_instruction *)calloc(num_instructions, + sizeof(*mesa_instructions)); + mesa_instruction_annotation = + (ir_instruction **)calloc(num_instructions, + sizeof(*mesa_instruction_annotation)); + + mesa_inst = mesa_instructions; + i = 0; + foreach_iter(exec_list_iterator, iter, v.instructions) { + ir_to_mesa_instruction *inst = (ir_to_mesa_instruction *)iter.get(); + + mesa_inst->Opcode = inst->op; + mesa_inst->DstReg.File = inst->dst_reg.file; + mesa_inst->DstReg.Index = inst->dst_reg.index; + mesa_inst->DstReg.CondMask = COND_TR; + mesa_inst->DstReg.WriteMask = inst->dst_reg.writemask; + mesa_inst->SrcReg[0] = mesa_src_reg_from_ir_src_reg(inst->src_reg[0]); + mesa_inst->SrcReg[1] = mesa_src_reg_from_ir_src_reg(inst->src_reg[1]); + mesa_inst->SrcReg[2] = mesa_src_reg_from_ir_src_reg(inst->src_reg[2]); + mesa_instruction_annotation[i] = inst->ir; + + mesa_inst++; + i++; + } + + set_branchtargets(mesa_instructions, num_instructions); + print_program(mesa_instructions, mesa_instruction_annotation, num_instructions); + + free(mesa_instruction_annotation); + talloc_free(v.ctx); +}