summaryrefslogtreecommitdiff
path: root/src/glsl/ir_to_mesa.cpp
diff options
context:
space:
mode:
authorEric Anholt <eric@anholt.net>2010-06-24 15:47:38 -0700
committerEric Anholt <eric@anholt.net>2010-06-24 15:47:40 -0700
commitbcc13b74443137043e8a34f8cb64a5add0d8af93 (patch)
tree5f4e003dceb61c091ce0fcb8c5cf9d91bec033c0 /src/glsl/ir_to_mesa.cpp
parent84341f4b2014810b2964230384fe76338be1d78e (diff)
parente5cf3aadb8d57dcc70b597092ecac276042f73cb (diff)
Merge branch 'glsl2-head' into glsl2
This brings in the standalone GLSL compiler that we are planning on replacing the existing Mesa GLSL compiler. It currently targets GLSL 1.20 and the Mesa IR.
Diffstat (limited to 'src/glsl/ir_to_mesa.cpp')
-rw-r--r--src/glsl/ir_to_mesa.cpp1211
1 files changed, 1211 insertions, 0 deletions
diff --git a/src/glsl/ir_to_mesa.cpp b/src/glsl/ir_to_mesa.cpp
new file mode 100644
index 0000000000..26449c5a5c
--- /dev/null
+++ b/src/glsl/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 <stdio.h>
+#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);
+}