Merge branch 'mesa'
authorEric Anholt <eric@anholt.net>
Thu, 24 Jun 2010 22:21:51 +0000 (15:21 -0700)
committerEric Anholt <eric@anholt.net>
Thu, 24 Jun 2010 22:27:10 +0000 (15:27 -0700)
This brings in the ir_to_mesa.cpp code I've been developing to codegen
to the Mesa IR.  It does not actually generate a complete Mesa
fragment/vertex program yet.

1  2 
ir.h
ir_to_mesa.cpp

diff --cc ir.h
Simple merge
diff --cc ir_to_mesa.cpp
index 0000000000000000000000000000000000000000,b496a87df56cce57e57d92833ba625b9b4c22489..26449c5a5c4e212e9e3ca170803e3dc947576982
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,1204 +1,1211 @@@
 -   ir_to_mesa_instruction *inst = new ir_to_mesa_instruction();
+ /*
+  * 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)
+ {
 -       entry = new temp_entry(ir->var, PROGRAM_UNIFORM, this->next_uniform);
++   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 temp_entry(ir->var,
 -                              builtin_var_to_mesa_reg[i].file,
 -                              builtin_var_to_mesa_reg[i].index);
++       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 temp_entry(ir->var, PROGRAM_TEMPORARY, this->next_temp);
++       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);
+ }