glsl2: Move the Mesa IR codegen into mesa/shader/
authorEric Anholt <eric@anholt.net>
Thu, 24 Jun 2010 22:49:18 +0000 (15:49 -0700)
committerEric Anholt <eric@anholt.net>
Thu, 24 Jun 2010 22:49:18 +0000 (15:49 -0700)
src/glsl/ir_to_mesa.cpp [deleted file]
src/mesa/shader/ir_to_mesa.cpp [new file with mode: 0644]

diff --git a/src/glsl/ir_to_mesa.cpp b/src/glsl/ir_to_mesa.cpp
deleted file mode 100644 (file)
index 26449c5..0000000
+++ /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 <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);
-}
diff --git a/src/mesa/shader/ir_to_mesa.cpp b/src/mesa/shader/ir_to_mesa.cpp
new file mode 100644 (file)
index 0000000..26449c5
--- /dev/null
@@ -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);
+}