linker: Link built-in functions instead of including them in every shader
[mesa.git] / src / mesa / shader / ir_to_mesa.cpp
index a0217bf8d92721520266e015ed42ea2a85ddaa50..bfb8e3201ac961b085979f8c1680bafc4fddf2bd 100644 (file)
@@ -44,6 +44,7 @@
 extern "C" {
 #include "main/mtypes.h"
 #include "shader/prog_instruction.h"
+#include "shader/prog_optimize.h"
 #include "shader/prog_print.h"
 #include "shader/program.h"
 #include "shader/prog_uniform.h"
@@ -60,13 +61,17 @@ typedef struct ir_to_mesa_src_reg {
    int index; /**< temporary index, VERT_ATTRIB_*, FRAG_ATTRIB_*, etc. */
    GLuint swizzle; /**< SWIZZLE_XYZWONEZERO swizzles from Mesa. */
    int negate; /**< NEGATE_XYZW mask from mesa */
-   bool reladdr; /**< Register index should be offset by address reg. */
+   /** Register index should be offset by the integer in this reg. */
+   ir_to_mesa_src_reg *reladdr;
 } 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] */
+   GLuint cond_mask:4;
+   /** Register index should be offset by the integer in this reg. */
+   ir_to_mesa_src_reg *reladdr;
 } ir_to_mesa_dst_reg;
 
 extern ir_to_mesa_src_reg ir_to_mesa_undef;
@@ -78,11 +83,17 @@ public:
    ir_to_mesa_src_reg src_reg[3];
    /** Pointer to the ir source this tree came from for debugging */
    ir_instruction *ir;
+   GLboolean cond_update;
+   int sampler; /**< sampler index */
+   int tex_target; /**< One of TEXTURE_*_INDEX */
+   GLboolean tex_shadow;
+
+   class function_entry *function; /* Set on OPCODE_CAL or OPCODE_BGNSUB */
 };
 
-class temp_entry : public exec_node {
+class variable_storage : public exec_node {
 public:
-   temp_entry(ir_variable *var, int file, int index)
+   variable_storage(ir_variable *var, int file, int index)
       : file(file), index(index), var(var)
    {
       /* empty */
@@ -93,18 +104,57 @@ public:
    ir_variable *var; /* variable that maps to this, if any */
 };
 
+class function_entry : public exec_node {
+public:
+   ir_function_signature *sig;
+
+   /**
+    * identifier of this function signature used by the program.
+    *
+    * At the point that Mesa instructions for function calls are
+    * generated, we don't know the address of the first instruction of
+    * the function body.  So we make the BranchTarget that is called a
+    * small integer and rewrite them during set_branchtargets().
+    */
+   int sig_id;
+
+   /**
+    * Pointer to first instruction of the function body.
+    *
+    * Set during function body emits after main() is processed.
+    */
+   ir_to_mesa_instruction *bgn_inst;
+
+   /**
+    * Index of the first instruction of the function body in actual
+    * Mesa IR.
+    *
+    * Set after convertion from ir_to_mesa_instruction to prog_instruction.
+    */
+   int inst;
+
+   /** Storage for the return value. */
+   ir_to_mesa_src_reg return_reg;
+};
+
 class ir_to_mesa_visitor : public ir_visitor {
 public:
    ir_to_mesa_visitor();
 
+   function_entry *current_function;
+
    GLcontext *ctx;
    struct gl_program *prog;
 
    int next_temp;
 
-   temp_entry *find_variable_storage(ir_variable *var);
+   variable_storage *find_variable_storage(ir_variable *var);
+
+   function_entry *get_function_signature(ir_function_signature *sig);
 
    ir_to_mesa_src_reg get_temp(const glsl_type *type);
+   void reladdr_to_temp(ir_instruction *ir,
+                       ir_to_mesa_src_reg *reg, int *num_reladdr);
 
    struct ir_to_mesa_src_reg src_reg_for_float(float val);
 
@@ -130,18 +180,26 @@ public:
    virtual void visit(ir_constant *);
    virtual void visit(ir_call *);
    virtual void visit(ir_return *);
+   virtual void visit(ir_discard *);
    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 variable_storage */
+   exec_list variables;
+
+   /** List of function_entry */
+   exec_list function_signatures;
+   int next_signature_id;
 
    /** List of ir_to_mesa_instruction */
    exec_list instructions;
 
+   ir_to_mesa_instruction *ir_to_mesa_emit_op0(ir_instruction *ir,
+                                              enum prog_opcode op);
+
    ir_to_mesa_instruction *ir_to_mesa_emit_op1(ir_instruction *ir,
                                               enum prog_opcode op,
                                               ir_to_mesa_dst_reg dst,
@@ -165,19 +223,34 @@ public:
                                   ir_to_mesa_dst_reg dst,
                                   ir_to_mesa_src_reg src0);
 
+   void ir_to_mesa_emit_scalar_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);
+
+   GLboolean try_emit_mad(ir_expression *ir,
+                         int mul_operand);
+
+   int *sampler_map;
+   int sampler_map_size;
+
+   void map_sampler(int location, int sampler);
+   int get_sampler_number(int location);
+
    void *mem_ctx;
 };
 
 ir_to_mesa_src_reg ir_to_mesa_undef = {
-   PROGRAM_UNDEFINED, 0, SWIZZLE_NOOP, NEGATE_NONE, false,
+   PROGRAM_UNDEFINED, 0, SWIZZLE_NOOP, NEGATE_NONE, NULL,
 };
 
 ir_to_mesa_dst_reg ir_to_mesa_undef_dst = {
-   PROGRAM_UNDEFINED, 0, SWIZZLE_NOOP
+   PROGRAM_UNDEFINED, 0, SWIZZLE_NOOP, COND_TR, NULL,
 };
 
 ir_to_mesa_dst_reg ir_to_mesa_address_reg = {
-   PROGRAM_ADDRESS, 0, WRITEMASK_X
+   PROGRAM_ADDRESS, 0, WRITEMASK_X, COND_TR, NULL
 };
 
 static int swizzle_for_size(int size)
@@ -192,56 +265,6 @@ static int swizzle_for_size(int size)
    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,
@@ -251,6 +274,28 @@ ir_to_mesa_visitor::ir_to_mesa_emit_op3(ir_instruction *ir,
                                        ir_to_mesa_src_reg src2)
 {
    ir_to_mesa_instruction *inst = new(mem_ctx) ir_to_mesa_instruction();
+   int num_reladdr = 0;
+
+   /* If we have to do relative addressing, we want to load the ARL
+    * reg directly for one of the regs, and preload the other reladdr
+    * sources into temps.
+    */
+   num_reladdr += dst.reladdr != NULL;
+   num_reladdr += src0.reladdr != NULL;
+   num_reladdr += src1.reladdr != NULL;
+   num_reladdr += src2.reladdr != NULL;
+
+   reladdr_to_temp(ir, &src2, &num_reladdr);
+   reladdr_to_temp(ir, &src1, &num_reladdr);
+   reladdr_to_temp(ir, &src0, &num_reladdr);
+
+   if (dst.reladdr) {
+      ir_to_mesa_emit_op1(ir, OPCODE_ARL, ir_to_mesa_address_reg,
+                          *dst.reladdr);
+
+      num_reladdr--;
+   }
+   assert(num_reladdr == 0);
 
    inst->op = op;
    inst->dst_reg = dst;
@@ -259,6 +304,8 @@ ir_to_mesa_visitor::ir_to_mesa_emit_op3(ir_instruction *ir,
    inst->src_reg[2] = src2;
    inst->ir = ir;
 
+   inst->function = NULL;
+
    this->instructions.push_tail(inst);
 
    return inst;
@@ -285,6 +332,35 @@ ir_to_mesa_visitor::ir_to_mesa_emit_op1(ir_instruction *ir,
                              src0, ir_to_mesa_undef, ir_to_mesa_undef);
 }
 
+ir_to_mesa_instruction *
+ir_to_mesa_visitor::ir_to_mesa_emit_op0(ir_instruction *ir,
+                                       enum prog_opcode op)
+{
+   return ir_to_mesa_emit_op3(ir, op, ir_to_mesa_undef_dst,
+                             ir_to_mesa_undef,
+                             ir_to_mesa_undef,
+                             ir_to_mesa_undef);
+}
+
+void
+ir_to_mesa_visitor::map_sampler(int location, int sampler)
+{
+   if (this->sampler_map_size <= location) {
+      this->sampler_map = talloc_realloc(this->mem_ctx, this->sampler_map,
+                                        int, location + 1);
+      this->sampler_map_size = location + 1;
+   }
+
+   this->sampler_map[location] = sampler;
+}
+
+int
+ir_to_mesa_visitor::get_sampler_number(int location)
+{
+   assert(location < this->sampler_map_size);
+   return this->sampler_map[location];
+}
+
 inline ir_to_mesa_dst_reg
 ir_to_mesa_dst_reg_from_src(ir_to_mesa_src_reg reg)
 {
@@ -293,10 +369,26 @@ ir_to_mesa_dst_reg_from_src(ir_to_mesa_src_reg reg)
    dst_reg.file = reg.file;
    dst_reg.index = reg.index;
    dst_reg.writemask = WRITEMASK_XYZW;
+   dst_reg.cond_mask = COND_TR;
+   dst_reg.reladdr = reg.reladdr;
 
    return dst_reg;
 }
 
+inline ir_to_mesa_src_reg
+ir_to_mesa_src_reg_from_dst(ir_to_mesa_dst_reg reg)
+{
+   ir_to_mesa_src_reg src_reg;
+
+   src_reg.file = reg.file;
+   src_reg.index = reg.index;
+   src_reg.swizzle = SWIZZLE_XYZW;
+   src_reg.negate = 0;
+   src_reg.reladdr = reg.reladdr;
+
+   return src_reg;
+}
+
 /**
  * Emits Mesa scalar opcodes to produce unique answers across channels.
  *
@@ -306,10 +398,11 @@ ir_to_mesa_dst_reg_from_src(ir_to_mesa_src_reg reg)
  * to produce dest channels.
  */
 void
-ir_to_mesa_visitor::ir_to_mesa_emit_scalar_op1(ir_instruction *ir,
+ir_to_mesa_visitor::ir_to_mesa_emit_scalar_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 orig_src0,
+                                              ir_to_mesa_src_reg orig_src1)
 {
    int i, j;
    int done_mask = ~dst.writemask;
@@ -321,65 +414,58 @@ ir_to_mesa_visitor::ir_to_mesa_emit_scalar_op1(ir_instruction *ir,
    for (i = 0; i < 4; i++) {
       GLuint this_mask = (1 << i);
       ir_to_mesa_instruction *inst;
-      ir_to_mesa_src_reg src = src0;
+      ir_to_mesa_src_reg src0 = orig_src0;
+      ir_to_mesa_src_reg src1 = orig_src1;
 
       if (done_mask & this_mask)
         continue;
 
-      GLuint src_swiz = GET_SWZ(src.swizzle, i);
+      GLuint src0_swiz = GET_SWZ(src0.swizzle, i);
+      GLuint src1_swiz = GET_SWZ(src1.swizzle, i);
       for (j = i + 1; j < 4; j++) {
-        if (!(done_mask & (1 << j)) && GET_SWZ(src.swizzle, j) == src_swiz) {
+        if (!(done_mask & (1 << j)) &&
+            GET_SWZ(src0.swizzle, j) == src0_swiz &&
+            GET_SWZ(src1.swizzle, j) == src1_swiz) {
            this_mask |= (1 << j);
         }
       }
-      src.swizzle = MAKE_SWIZZLE4(src_swiz, src_swiz,
-                                 src_swiz, src_swiz);
+      src0.swizzle = MAKE_SWIZZLE4(src0_swiz, src0_swiz,
+                                  src0_swiz, src0_swiz);
+      src1.swizzle = MAKE_SWIZZLE4(src1_swiz, src1_swiz,
+                                 src1_swiz, src1_swiz);
 
-      inst = ir_to_mesa_emit_op1(ir, op,
+      inst = ir_to_mesa_emit_op2(ir, op,
                                 dst,
-                                src);
+                                src0,
+                                src1);
       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)
+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)
 {
-   ir_to_mesa_src_reg src_reg;
+   ir_to_mesa_src_reg undef = ir_to_mesa_undef;
 
-   src_reg.file = PROGRAM_CONSTANT;
-   src_reg.index = _mesa_add_unnamed_constant(this->prog->Parameters,
-                                             &val, 1, &src_reg.swizzle);
+   undef.swizzle = SWIZZLE_XXXX;
 
-   return src_reg;
+   ir_to_mesa_emit_scalar_op2(ir, op, dst, src0, undef);
 }
 
-/**
- * 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)
+struct ir_to_mesa_src_reg
+ir_to_mesa_visitor::src_reg_for_float(float val)
 {
    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]);
+   src_reg.file = PROGRAM_CONSTANT;
+   src_reg.index = _mesa_add_unnamed_constant(this->prog->Parameters,
+                                             &val, 1, &src_reg.swizzle);
+   src_reg.reladdr = NULL;
+   src_reg.negate = 0;
 
    return src_reg;
 }
@@ -396,7 +482,7 @@ type_size(const struct glsl_type *type)
    case GLSL_TYPE_FLOAT:
    case GLSL_TYPE_BOOL:
       if (type->is_matrix()) {
-        return 4; /* FINISHME: Not all matrices are 4x4. */
+        return type->matrix_columns;
       } else {
         /* Regardless of size of vector, it gets a vec4. This is bad
          * packing for things like floats, but otherwise arrays become a
@@ -418,14 +504,45 @@ type_size(const struct glsl_type *type)
    }
 }
 
-temp_entry *
+/**
+ * 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 = next_temp;
+   src_reg.reladdr = NULL;
+   next_temp += type_size(type);
+
+   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]);
+   src_reg.negate = 0;
+
+   return src_reg;
+}
+
+variable_storage *
 ir_to_mesa_visitor::find_variable_storage(ir_variable *var)
 {
    
-   temp_entry *entry;
+   variable_storage *entry;
 
-   foreach_iter(exec_list_iterator, iter, this->variable_storage) {
-      entry = (temp_entry *)iter.get();
+   foreach_iter(exec_list_iterator, iter, this->variables) {
+      entry = (variable_storage *)iter.get();
 
       if (entry->var == var)
         return entry;
@@ -448,13 +565,9 @@ ir_to_mesa_visitor::visit(ir_loop *ir)
    assert(!ir->increment);
    assert(!ir->counter);
 
-   ir_to_mesa_emit_op1(NULL, OPCODE_BGNLOOP,
-                      ir_to_mesa_undef_dst, ir_to_mesa_undef);
-
+   ir_to_mesa_emit_op0(NULL, OPCODE_BGNLOOP);
    visit_exec_list(&ir->body_instructions, this);
-
-   ir_to_mesa_emit_op1(NULL, OPCODE_ENDLOOP,
-                      ir_to_mesa_undef_dst, ir_to_mesa_undef);
+   ir_to_mesa_emit_op0(NULL, OPCODE_ENDLOOP);
 }
 
 void
@@ -462,12 +575,10 @@ 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);
+      ir_to_mesa_emit_op0(NULL, OPCODE_BRK);
       break;
    case ir_loop_jump::jump_continue:
-      ir_to_mesa_emit_op1(NULL, OPCODE_CONT,
-                         ir_to_mesa_undef_dst, ir_to_mesa_undef);
+      ir_to_mesa_emit_op0(NULL, OPCODE_CONT);
       break;
    }
 }
@@ -502,6 +613,50 @@ ir_to_mesa_visitor::visit(ir_function *ir)
    }
 }
 
+GLboolean
+ir_to_mesa_visitor::try_emit_mad(ir_expression *ir, int mul_operand)
+{
+   int nonmul_operand = 1 - mul_operand;
+   ir_to_mesa_src_reg a, b, c;
+
+   ir_expression *expr = ir->operands[mul_operand]->as_expression();
+   if (!expr || expr->operation != ir_binop_mul)
+      return false;
+
+   expr->operands[0]->accept(this);
+   a = this->result;
+   expr->operands[1]->accept(this);
+   b = this->result;
+   ir->operands[nonmul_operand]->accept(this);
+   c = this->result;
+
+   this->result = get_temp(ir->type);
+   ir_to_mesa_emit_op3(ir, OPCODE_MAD,
+                      ir_to_mesa_dst_reg_from_src(this->result), a, b, c);
+
+   return true;
+}
+
+void
+ir_to_mesa_visitor::reladdr_to_temp(ir_instruction *ir,
+                                   ir_to_mesa_src_reg *reg, int *num_reladdr)
+{
+   if (!reg->reladdr)
+      return;
+
+   ir_to_mesa_emit_op1(ir, OPCODE_ARL, ir_to_mesa_address_reg, *reg->reladdr);
+
+   if (*num_reladdr != 1) {
+      ir_to_mesa_src_reg temp = get_temp(glsl_type::vec4_type);
+
+      ir_to_mesa_emit_op1(ir, OPCODE_MOV,
+                         ir_to_mesa_dst_reg_from_src(temp), *reg);
+      *reg = temp;
+   }
+
+   (*num_reladdr)--;
+}
+
 void
 ir_to_mesa_visitor::visit(ir_expression *ir)
 {
@@ -513,6 +668,15 @@ ir_to_mesa_visitor::visit(ir_expression *ir)
    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);
 
+   /* Quick peephole: Emit OPCODE_MAD(a, b, c) instead of ADD(MUL(a, b), c)
+    */
+   if (ir->operation == ir_binop_add) {
+      if (try_emit_mad(ir, 1))
+        return;
+      if (try_emit_mad(ir, 0))
+        return;
+   }
+
    for (operand = 0; operand < ir->get_num_operands(); operand++) {
       this->result.file = PROGRAM_UNDEFINED;
       ir->operands[operand]->accept(this);
@@ -524,9 +688,10 @@ ir_to_mesa_visitor::visit(ir_expression *ir)
       }
       op[operand] = this->result;
 
-      /* Only expression implemented for matrices yet */
-      assert(!ir->operands[operand]->type->is_matrix() ||
-            ir->operation == ir_binop_mul);
+      /* Matrix expression operands should have been broken down to vector
+       * operations already.
+       */
+      assert(!ir->operands[operand]->type->is_matrix());
    }
 
    this->result.file = PROGRAM_UNDEFINED;
@@ -552,8 +717,19 @@ ir_to_mesa_visitor::visit(ir_expression *ir)
       op[0].negate = ~op[0].negate;
       result_src = op[0];
       break;
+   case ir_unop_abs:
+      ir_to_mesa_emit_op1(ir, OPCODE_ABS, result_dst, op[0]);
+      break;
+   case ir_unop_sign:
+      ir_to_mesa_emit_op1(ir, OPCODE_SSG, result_dst, op[0]);
+      break;
+   case ir_unop_rcp:
+      ir_to_mesa_emit_scalar_op1(ir, OPCODE_RCP, result_dst, op[0]);
+      break;
+
    case ir_unop_exp:
-      ir_to_mesa_emit_scalar_op1(ir, OPCODE_EXP, result_dst, op[0]);
+      ir_to_mesa_emit_scalar_op2(ir, OPCODE_POW, result_dst,
+                                src_reg_for_float(M_E), op[0]);
       break;
    case ir_unop_exp2:
       ir_to_mesa_emit_scalar_op1(ir, OPCODE_EX2, result_dst, op[0]);
@@ -570,45 +746,28 @@ ir_to_mesa_visitor::visit(ir_expression *ir)
    case ir_unop_cos:
       ir_to_mesa_emit_scalar_op1(ir, OPCODE_COS, result_dst, op[0]);
       break;
+
+   case ir_unop_dFdx:
+      ir_to_mesa_emit_op1(ir, OPCODE_DDX, result_dst, op[0]);
+      break;
+   case ir_unop_dFdy:
+      ir_to_mesa_emit_op1(ir, OPCODE_DDY, 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]);
-      }
+      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);
+      assert(!"not reached: should be handled by ir_div_to_mul_rcp");
+   case ir_binop_mod:
+      assert(!"ir_binop_mod should have been converted to b * fract(a/b)");
       break;
 
    case ir_binop_less:
@@ -667,14 +826,24 @@ ir_to_mesa_visitor::visit(ir_expression *ir)
                             op[0], op[1]);
       }
       break;
+
+   case ir_binop_cross:
+      ir_to_mesa_emit_op2(ir, OPCODE_XPD, 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);
+      ir_to_mesa_emit_scalar_op1(ir, OPCODE_RCP, result_dst, result_src);
+      /* For incoming channels < 0, set the result to 0. */
+      ir_to_mesa_emit_op3(ir, OPCODE_CMP, result_dst,
+                         op[0], src_reg_for_float(0.0), 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:
+   case ir_unop_b2f:
+   case ir_unop_b2i:
       /* Mesa IR lacks types, ints are stored as truncated floats. */
       result_src = op[0];
       break;
@@ -682,6 +851,7 @@ ir_to_mesa_visitor::visit(ir_expression *ir)
       ir_to_mesa_emit_op1(ir, OPCODE_TRUNC, result_dst, op[0]);
       break;
    case ir_unop_f2b:
+   case ir_unop_i2b:
       ir_to_mesa_emit_op2(ir, OPCODE_SNE, result_dst,
                          result_src, src_reg_for_float(0.0));
       break;
@@ -696,17 +866,28 @@ ir_to_mesa_visitor::visit(ir_expression *ir)
    case ir_unop_floor:
       ir_to_mesa_emit_op1(ir, OPCODE_FLR, result_dst, op[0]);
       break;
+   case ir_unop_fract:
+      ir_to_mesa_emit_op1(ir, OPCODE_FRC, 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);
+   case ir_binop_pow:
+      ir_to_mesa_emit_scalar_op2(ir, OPCODE_POW, result_dst, op[0], op[1]);
+      break;
+
+   case ir_unop_bit_not:
+   case ir_unop_u2f:
+   case ir_binop_lshift:
+   case ir_binop_rshift:
+   case ir_binop_bit_and:
+   case ir_binop_bit_xor:
+   case ir_binop_bit_or:
+      assert(!"GLSL 1.30 features unsupported");
       break;
    }
 
@@ -734,23 +915,23 @@ ir_to_mesa_visitor::visit(ir_swizzle *ir)
       if (i < ir->type->vector_elements) {
         switch (i) {
         case 0:
-           swizzle[i] = ir->mask.x;
+           swizzle[i] = GET_SWZ(src_reg.swizzle, ir->mask.x);
            break;
         case 1:
-           swizzle[i] = ir->mask.y;
+           swizzle[i] = GET_SWZ(src_reg.swizzle, ir->mask.y);
            break;
         case 2:
-           swizzle[i] = ir->mask.z;
+           swizzle[i] = GET_SWZ(src_reg.swizzle, ir->mask.z);
            break;
         case 3:
-           swizzle[i] = ir->mask.w;
+           swizzle[i] = GET_SWZ(src_reg.swizzle, 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;
+        swizzle[i] = swizzle[ir->type->vector_elements - 1];
       }
    }
 
@@ -762,8 +943,34 @@ ir_to_mesa_visitor::visit(ir_swizzle *ir)
    this->result = src_reg;
 }
 
-static temp_entry *
-get_builtin_matrix_ref(void *mem_ctx, struct gl_program *prog, ir_variable *var)
+static int
+add_matrix_ref(struct gl_program *prog, int *tokens)
+{
+   int base_pos = -1;
+   int i;
+
+   /* Add a ref for each column.  It looks like the reason we do
+    * it this way is that _mesa_add_state_reference doesn't work
+    * for things that aren't vec4s, so the tokens[2]/tokens[3]
+    * range has to be equal.
+    */
+   for (i = 0; i < 4; i++) {
+      tokens[2] = i;
+      tokens[3] = i;
+      int pos = _mesa_add_state_reference(prog->Parameters,
+                                         (gl_state_index *)tokens);
+      if (base_pos == -1)
+        base_pos = pos;
+      else
+        assert(base_pos + i == pos);
+   }
+
+   return base_pos;
+}
+
+static variable_storage *
+get_builtin_matrix_ref(void *mem_ctx, struct gl_program *prog, ir_variable *var,
+                      ir_rvalue *array_index)
 {
    /*
     * NOTE: The ARB_vertex_program extension specified that matrices get
@@ -799,7 +1006,7 @@ get_builtin_matrix_ref(void *mem_ctx, struct gl_program *prog, ir_variable *var)
 
    };
    unsigned int i;
-   temp_entry *entry;
+   variable_storage *entry;
 
    /* C++ gets angry when we try to use an int as a gl_state_index, so we use
     * ints for gl_state_index.  Make sure they're compatible.
@@ -808,32 +1015,35 @@ get_builtin_matrix_ref(void *mem_ctx, struct gl_program *prog, ir_variable *var)
 
    for (i = 0; i < Elements(matrices); i++) {
       if (strcmp(var->name, matrices[i].name) == 0) {
-        int j;
-        int last_pos = -1, base_pos = -1;
         int tokens[STATE_LENGTH];
+        int base_pos = -1;
 
         tokens[0] = matrices[i].matrix;
-        tokens[1] = 0; /* array index! */
         tokens[4] = matrices[i].modifier;
-
-        /* Add a ref for each column.  It looks like the reason we do
-         * it this way is that _mesa_add_state_reference doesn't work
-         * for things that aren't vec4s, so the tokens[2]/tokens[3]
-         * range has to be equal.
-         */
-        for (j = 0; j < 4; j++) {
-           tokens[2] = j;
-           tokens[3] = j;
-           int pos = _mesa_add_state_reference(prog->Parameters,
-                                               (gl_state_index *)tokens);
-           assert(last_pos == -1 || last_pos == base_pos + j);
-           if (base_pos == -1)
-              base_pos = pos;
+        if (matrices[i].matrix == STATE_TEXTURE_MATRIX) {
+           ir_constant *index = array_index->constant_expression_value();
+           if (index) {
+              tokens[1] = index->value.i[0];
+              base_pos = add_matrix_ref(prog, tokens);
+           } else {
+              for (i = 0; i < var->type->length; i++) {
+                 tokens[1] = i;
+                 int pos = add_matrix_ref(prog, tokens);
+                 if (base_pos == -1)
+                    base_pos = pos;
+                 else
+                    assert(base_pos + (int)i * 4 == pos);
+              }
+           }
+        } else {
+           tokens[1] = 0; /* unused array index */
+           base_pos = add_matrix_ref(prog, tokens);
         }
+        tokens[4] = matrices[i].modifier;
 
-        entry = new(mem_ctx) temp_entry(var,
-                                        PROGRAM_STATE_VAR,
-                                        base_pos);
+        entry = new(mem_ctx) variable_storage(var,
+                                              PROGRAM_STATE_VAR,
+                                              base_pos);
 
         return entry;
       }
@@ -846,18 +1056,34 @@ 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, loc;
-   bool var_in;
+   variable_storage *entry = find_variable_storage(ir->var);
+   unsigned int loc;
 
    if (!entry) {
       switch (ir->var->mode) {
       case ir_var_uniform:
-        entry = get_builtin_matrix_ref(this->mem_ctx, this->prog, ir->var);
+        entry = get_builtin_matrix_ref(this->mem_ctx, this->prog, ir->var,
+                                       NULL);
         if (entry)
            break;
 
         /* FINISHME: Fix up uniform name for arrays and things */
+        if (ir->var->type->base_type == GLSL_TYPE_SAMPLER) {
+           /* FINISHME: we whack the location of the var here, which
+            * is probably not expected.  But we need to communicate
+            * mesa's sampler number to the tex instruction.
+            */
+           int sampler = _mesa_add_sampler(this->prog->Parameters,
+                                           ir->var->name,
+                                           ir->var->type->gl_type);
+           map_sampler(ir->var->location, sampler);
+
+           entry = new(mem_ctx) variable_storage(ir->var, PROGRAM_SAMPLER,
+                                                 sampler);
+           this->variables.push_tail(entry);
+           break;
+        }
+
         assert(ir->var->type->gl_type != 0 &&
                ir->var->type->gl_type != GL_INVALID_ENUM);
         loc = _mesa_add_uniform(this->prog->Parameters,
@@ -865,41 +1091,52 @@ ir_to_mesa_visitor::visit(ir_dereference_variable *ir)
                                 type_size(ir->var->type) * 4,
                                 ir->var->type->gl_type,
                                 NULL);
+
         /* Always mark the uniform used at this point.  If it isn't
          * used, dead code elimination should have nuked the decl already.
          */
         this->prog->Parameters->Parameters[loc].Used = GL_TRUE;
 
-        entry = new(mem_ctx) temp_entry(ir->var, PROGRAM_UNIFORM, loc);
-        this->variable_storage.push_tail(entry);
+        entry = new(mem_ctx) variable_storage(ir->var, PROGRAM_UNIFORM, loc);
+        this->variables.push_tail(entry);
         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();
+        /* The linker assigns locations for varyings and attributes,
+         * including deprecated builtins (like gl_Color), user-assign
+         * generic attributes (glBindVertexLocation), and
+         * user-defined varyings.
+         *
+         * FINISHME: We would hit this path for function arguments.  Fix!
+         */
+        assert(ir->var->location != -1);
+        if (ir->var->mode == ir_var_in ||
+            ir->var->mode == ir_var_inout) {
+           entry = new(mem_ctx) variable_storage(ir->var,
+                                                 PROGRAM_INPUT,
+                                                 ir->var->location);
+
+           if (this->prog->Target == GL_VERTEX_PROGRAM_ARB &&
+               ir->var->location >= VERT_ATTRIB_GENERIC0) {
+              _mesa_add_attribute(prog->Attributes,
+                                  ir->var->name,
+                                  type_size(ir->var->type) * 4,
+                                  ir->var->type->gl_type,
+                                  ir->var->location - VERT_ATTRIB_GENERIC0);
+           }
+        } else {
+           entry = new(mem_ctx) variable_storage(ir->var,
+                                                 PROGRAM_OUTPUT,
+                                                 ir->var->location);
         }
-        entry = new(mem_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(mem_ctx) temp_entry(ir->var, PROGRAM_TEMPORARY,
-                                        this->next_temp);
-        this->variable_storage.push_tail(entry);
+      case ir_var_temporary:
+        entry = new(mem_ctx) variable_storage(ir->var, PROGRAM_TEMPORARY,
+                                              this->next_temp);
+        this->variables.push_tail(entry);
 
         next_temp += type_size(ir->var->type);
         break;
@@ -915,7 +1152,7 @@ ir_to_mesa_visitor::visit(ir_dereference_variable *ir)
    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.reladdr = NULL;
    src_reg.negate = 0;
 
    this->result = src_reg;
@@ -926,45 +1163,71 @@ ir_to_mesa_visitor::visit(ir_dereference_array *ir)
 {
    ir_constant *index;
    ir_to_mesa_src_reg src_reg;
+   ir_dereference_variable *deref_var = ir->array->as_dereference_variable();
+   int element_size = type_size(ir->type);
 
    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());
+   if (deref_var && strncmp(deref_var->var->name,
+                           "gl_TextureMatrix",
+                           strlen("gl_TextureMatrix")) == 0) {
+      ir_to_mesa_src_reg src_reg;
+      struct variable_storage *entry;
 
-   ir->array->accept(this);
-   src_reg = this->result;
+      entry = get_builtin_matrix_ref(this->mem_ctx, this->prog, deref_var->var,
+                                    ir->array_index);
+      assert(entry);
 
-   if (src_reg.file == PROGRAM_INPUT ||
-       src_reg.file == PROGRAM_OUTPUT) {
-      assert(index); /* FINISHME: Handle variable indexing of builtins. */
+      src_reg.file = entry->file;
+      src_reg.index = entry->index;
+      src_reg.swizzle = swizzle_for_size(ir->type->vector_elements);
+      src_reg.negate = 0;
 
-      src_reg.index += index->value.i[0];
-   } else {
       if (index) {
-        src_reg.index += index->value.i[0];
+        src_reg.reladdr = NULL;
       } 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_to_mesa_src_reg index_reg = get_temp(glsl_type::float_type);
+
         ir->array_index->accept(this);
+        ir_to_mesa_emit_op2(ir, OPCODE_MUL,
+                            ir_to_mesa_dst_reg_from_src(index_reg),
+                            this->result, src_reg_for_float(element_size));
 
-        /* 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);
+        src_reg.reladdr = talloc(mem_ctx, ir_to_mesa_src_reg);
+        memcpy(src_reg.reladdr, &index_reg, sizeof(index_reg));
       }
+
+      this->result = src_reg;
+      return;
+   }
+
+   ir->array->accept(this);
+   src_reg = this->result;
+
+   if (index) {
+      src_reg.index += index->value.i[0] * element_size;
+   } 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);
+
+      ir_to_mesa_src_reg index_reg;
+
+      if (element_size == 1) {
+        index_reg = this->result;
+      } else {
+        index_reg = get_temp(glsl_type::float_type);
+
+        ir_to_mesa_emit_op2(ir, OPCODE_MUL,
+                            ir_to_mesa_dst_reg_from_src(index_reg),
+                            this->result, src_reg_for_float(element_size));
+      }
+
+      src_reg.reladdr = talloc(mem_ctx, ir_to_mesa_src_reg);
+      memcpy(src_reg.reladdr, &index_reg, sizeof(index_reg));
    }
 
    /* If the type is smaller than a vec4, replicate the last channel out. */
@@ -1001,33 +1264,51 @@ ir_to_mesa_visitor::visit(ir_dereference_record *ir)
  * 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)
+get_assignment_lhs(ir_instruction *ir, ir_to_mesa_visitor *v,
+                  ir_to_mesa_src_reg *r)
 {
    struct ir_to_mesa_dst_reg dst_reg;
-   ir_dereference *deref;
    ir_swizzle *swiz;
 
+   ir_dereference_array *deref_array = ir->as_dereference_array();
+   /* This should have been handled by ir_vec_index_to_cond_assign */
+   if (deref_array) {
+      assert(!deref_array->array->type->is_vector());
+   }
+
    /* 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());
+   if ((swiz = ir->as_swizzle())) {
+      int swizzles[4] = {
+        swiz->mask.x,
+        swiz->mask.y,
+        swiz->mask.z,
+        swiz->mask.w
+      };
+      int new_r_swizzle[4];
+      int orig_r_swizzle = r->swizzle;
+      int i;
+
+      for (i = 0; i < 4; i++) {
+        new_r_swizzle[i] = GET_SWZ(orig_r_swizzle, 0);
+      }
 
-      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);
+      for (i = 0; i < 4; i++) {
+        if (i < swiz->mask.num_components) {
+           dst_reg.writemask |= 1 << swizzles[i];
+           new_r_swizzle[swizzles[i]] = GET_SWZ(orig_r_swizzle, i);
+        }
+      }
+
+      r->swizzle = MAKE_SWIZZLE4(new_r_swizzle[0],
+                                new_r_swizzle[1],
+                                new_r_swizzle[2],
+                                new_r_swizzle[3]);
    }
 
    return dst_reg;
@@ -1038,27 +1319,44 @@ ir_to_mesa_visitor::visit(ir_assignment *ir)
 {
    struct ir_to_mesa_dst_reg l;
    struct ir_to_mesa_src_reg r;
+   int i;
 
-   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;
+
+   l = get_assignment_lhs(ir->lhs, this, &r);
+
    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_src_reg condition;
+
+      ir->condition->accept(this);
+      condition = this->result;
+
+      /* We use the OPCODE_CMP (a < 0 ? b : c) for conditional moves,
+       * and the condition we produced is 0.0 or 1.0.  By flipping the
+       * sign, we can choose which value OPCODE_CMP produces without
+       * an extra computing the condition.
+       */
+      condition.negate = ~condition.negate;
+      for (i = 0; i < type_size(ir->lhs->type); i++) {
+        ir_to_mesa_emit_op3(ir, OPCODE_CMP, l,
+                            condition, r, ir_to_mesa_src_reg_from_dst(l));
+        l.index++;
+        r.index++;
+      }
+   } else {
+      for (i = 0; i < type_size(ir->lhs->type); i++) {
+        ir_to_mesa_emit_op1(ir, OPCODE_MOV, l, r);
+        l.index++;
+        r.index++;
+      }
    }
-
-   ir_to_mesa_emit_op1(ir, OPCODE_MOV, l, r);
 }
 
 
@@ -1070,8 +1368,39 @@ ir_to_mesa_visitor::visit(ir_constant *ir)
    GLfloat *values = stack_vals;
    unsigned int i;
 
-   if (ir->type->is_matrix() || ir->type->is_array()) {
-      assert(!"FINISHME: array/matrix constants");
+   if (ir->type->is_array()) {
+      ir->print();
+      printf("\n");
+      assert(!"FINISHME: array constants");
+   }
+
+   if (ir->type->is_matrix()) {
+      /* Unfortunately, 4 floats is all we can get into
+       * _mesa_add_unnamed_constant.  So, make a temp to store the
+       * matrix and move each constant value into it.  If we get
+       * lucky, copy propagation will eliminate the extra moves.
+       */
+      ir_to_mesa_src_reg mat = get_temp(glsl_type::vec4_type);
+      ir_to_mesa_dst_reg mat_column = ir_to_mesa_dst_reg_from_src(mat);
+
+      for (i = 0; i < ir->type->matrix_columns; i++) {
+        src_reg.file = PROGRAM_CONSTANT;
+
+        assert(ir->type->base_type == GLSL_TYPE_FLOAT);
+        values = &ir->value.f[i * ir->type->vector_elements];
+
+        src_reg.index = _mesa_add_unnamed_constant(this->prog->Parameters,
+                                                   values,
+                                                   ir->type->vector_elements,
+                                                   &src_reg.swizzle);
+        src_reg.reladdr = NULL;
+        src_reg.negate = 0;
+        ir_to_mesa_emit_op1(ir, OPCODE_MOV, mat_column, src_reg);
+
+        mat_column.index++;
+      }
+
+      this->result = mat;
    }
 
    src_reg.file = PROGRAM_CONSTANT;
@@ -1101,59 +1430,338 @@ ir_to_mesa_visitor::visit(ir_constant *ir)
    src_reg.index = _mesa_add_unnamed_constant(this->prog->Parameters,
                                              values, ir->type->vector_elements,
                                              &src_reg.swizzle);
-   src_reg.reladdr = false;
+   src_reg.reladdr = NULL;
    src_reg.negate = 0;
 
    this->result = src_reg;
 }
 
+function_entry *
+ir_to_mesa_visitor::get_function_signature(ir_function_signature *sig)
+{
+   function_entry *entry;
+
+   foreach_iter(exec_list_iterator, iter, this->function_signatures) {
+      entry = (function_entry *)iter.get();
+
+      if (entry->sig == sig)
+        return entry;
+   }
+
+   entry = talloc(mem_ctx, function_entry);
+   entry->sig = sig;
+   entry->sig_id = this->next_signature_id++;
+   entry->bgn_inst = NULL;
+
+   /* Allocate storage for all the parameters. */
+   foreach_iter(exec_list_iterator, iter, sig->parameters) {
+      ir_variable *param = (ir_variable *)iter.get();
+      variable_storage *storage;
+
+      storage = find_variable_storage(param);
+      assert(!storage);
+
+      storage = new(mem_ctx) variable_storage(param, PROGRAM_TEMPORARY,
+                                             this->next_temp);
+      this->variables.push_tail(storage);
+
+      this->next_temp += type_size(param->type);
+      break;
+   }
+
+   if (sig->return_type) {
+      entry->return_reg = get_temp(sig->return_type);
+   } else {
+      entry->return_reg = ir_to_mesa_undef;
+   }
+
+   this->function_signatures.push_tail(entry);
+   return entry;
+}
 
 void
 ir_to_mesa_visitor::visit(ir_call *ir)
 {
-   printf("Can't support call to %s\n", ir->callee_name());
-   exit(1);
+   ir_to_mesa_instruction *call_inst;
+   ir_function_signature *sig = ir->get_callee();
+   function_entry *entry = get_function_signature(sig);
+   int i;
+
+   /* Process in parameters. */
+   exec_list_iterator sig_iter = sig->parameters.iterator();
+   foreach_iter(exec_list_iterator, iter, *ir) {
+      ir_rvalue *param_rval = (ir_rvalue *)iter.get();
+      ir_variable *param = (ir_variable *)sig_iter.get();
+
+      if (param->mode == ir_var_in ||
+         param->mode == ir_var_inout) {
+        variable_storage *storage = find_variable_storage(param);
+        assert(storage);
+
+        param_rval->accept(this);
+        ir_to_mesa_src_reg r = this->result;
+
+        ir_to_mesa_dst_reg l;
+        l.file = storage->file;
+        l.index = storage->index;
+        l.reladdr = NULL;
+        l.writemask = WRITEMASK_XYZW;
+        l.cond_mask = COND_TR;
+
+        for (i = 0; i < type_size(param->type); i++) {
+           ir_to_mesa_emit_op1(ir, OPCODE_MOV, l, r);
+           l.index++;
+           r.index++;
+        }
+      }
+
+      sig_iter.next();
+   }
+   assert(!sig_iter.has_next());
+
+   /* Emit call instruction */
+   call_inst = ir_to_mesa_emit_op1(ir, OPCODE_CAL,
+                                  ir_to_mesa_undef_dst, ir_to_mesa_undef);
+   call_inst->function = entry;
+
+   /* Process out parameters. */
+   sig_iter = sig->parameters.iterator();
+   foreach_iter(exec_list_iterator, iter, *ir) {
+      ir_rvalue *param_rval = (ir_rvalue *)iter.get();
+      ir_variable *param = (ir_variable *)sig_iter.get();
+
+      if (param->mode == ir_var_out ||
+         param->mode == ir_var_inout) {
+        variable_storage *storage = find_variable_storage(param);
+        assert(storage);
+
+        ir_to_mesa_src_reg r;
+        r.file = storage->file;
+        r.index = storage->index;
+        r.reladdr = NULL;
+        r.swizzle = SWIZZLE_NOOP;
+        r.negate = 0;
+
+        param_rval->accept(this);
+        ir_to_mesa_dst_reg l = ir_to_mesa_dst_reg_from_src(this->result);
+
+        for (i = 0; i < type_size(param->type); i++) {
+           ir_to_mesa_emit_op1(ir, OPCODE_MOV, l, r);
+           l.index++;
+           r.index++;
+        }
+      }
+
+      sig_iter.next();
+   }
+   assert(!sig_iter.has_next());
+
+   /* Process return value. */
+   this->result = entry->return_reg;
 }
 
 
 void
 ir_to_mesa_visitor::visit(ir_texture *ir)
 {
-   assert(0);
+   ir_to_mesa_src_reg result_src, coord, lod_info = { 0 }, projector;
+   ir_to_mesa_dst_reg result_dst, coord_dst;
+   ir_to_mesa_instruction *inst = NULL;
+   prog_opcode opcode = OPCODE_NOP;
 
    ir->coordinate->accept(this);
+
+   /* Put our coords in a temp.  We'll need to modify them for shadow,
+    * projection, or LOD, so the only case we'd use it as is is if
+    * we're doing plain old texturing.  Mesa IR optimization should
+    * handle cleaning up our mess in that case.
+    */
+   coord = get_temp(glsl_type::vec4_type);
+   coord_dst = ir_to_mesa_dst_reg_from_src(coord);
+   ir_to_mesa_emit_op1(ir, OPCODE_MOV, coord_dst,
+                      this->result);
+
+   if (ir->projector) {
+      ir->projector->accept(this);
+      projector = this->result;
+   }
+
+   /* Storage for our result.  Ideally for an assignment we'd be using
+    * the actual storage for the result here, instead.
+    */
+   result_src = get_temp(glsl_type::vec4_type);
+   result_dst = ir_to_mesa_dst_reg_from_src(result_src);
+
+   switch (ir->op) {
+   case ir_tex:
+      opcode = OPCODE_TEX;
+      break;
+   case ir_txb:
+      opcode = OPCODE_TXB;
+      ir->lod_info.bias->accept(this);
+      lod_info = this->result;
+      break;
+   case ir_txl:
+      opcode = OPCODE_TXL;
+      ir->lod_info.lod->accept(this);
+      lod_info = this->result;
+      break;
+   case ir_txd:
+   case ir_txf:
+      assert(!"GLSL 1.30 features unsupported");
+      break;
+   }
+
+   if (ir->projector) {
+      if (opcode == OPCODE_TEX) {
+        /* Slot the projector in as the last component of the coord. */
+        coord_dst.writemask = WRITEMASK_W;
+        ir_to_mesa_emit_op1(ir, OPCODE_MOV, coord_dst, projector);
+        coord_dst.writemask = WRITEMASK_XYZW;
+        opcode = OPCODE_TXP;
+      } else {
+        ir_to_mesa_src_reg coord_w = coord;
+        coord_w.swizzle = SWIZZLE_WWWW;
+
+        /* For the other TEX opcodes there's no projective version
+         * since the last slot is taken up by lod info.  Do the
+         * projective divide now.
+         */
+        coord_dst.writemask = WRITEMASK_W;
+        ir_to_mesa_emit_op1(ir, OPCODE_RCP, coord_dst, projector);
+
+        coord_dst.writemask = WRITEMASK_XYZ;
+        ir_to_mesa_emit_op2(ir, OPCODE_MUL, coord_dst, coord, coord_w);
+
+        coord_dst.writemask = WRITEMASK_XYZW;
+        coord.swizzle = SWIZZLE_XYZW;
+      }
+   }
+
+   if (ir->shadow_comparitor) {
+      /* Slot the shadow value in as the second to last component of the
+       * coord.
+       */
+      ir->shadow_comparitor->accept(this);
+      coord_dst.writemask = WRITEMASK_Z;
+      ir_to_mesa_emit_op1(ir, OPCODE_MOV, coord_dst, this->result);
+      coord_dst.writemask = WRITEMASK_XYZW;
+   }
+
+   if (opcode == OPCODE_TXL || opcode == OPCODE_TXB) {
+      /* Mesa IR stores lod or lod bias in the last channel of the coords. */
+      coord_dst.writemask = WRITEMASK_W;
+      ir_to_mesa_emit_op1(ir, OPCODE_MOV, coord_dst, lod_info);
+      coord_dst.writemask = WRITEMASK_XYZW;
+   }
+
+   inst = ir_to_mesa_emit_op1(ir, opcode, result_dst, coord);
+
+   if (ir->shadow_comparitor)
+      inst->tex_shadow = GL_TRUE;
+
+   ir_dereference_variable *sampler = ir->sampler->as_dereference_variable();
+   assert(sampler); /* FINISHME: sampler arrays */
+   /* generate the mapping, remove when we generate storage at
+    * declaration time
+    */
+   sampler->accept(this);
+
+   inst->sampler = get_sampler_number(sampler->var->location);
+
+   switch (sampler->type->sampler_dimensionality) {
+   case GLSL_SAMPLER_DIM_1D:
+      inst->tex_target = TEXTURE_1D_INDEX;
+      break;
+   case GLSL_SAMPLER_DIM_2D:
+      inst->tex_target = TEXTURE_2D_INDEX;
+      break;
+   case GLSL_SAMPLER_DIM_3D:
+      inst->tex_target = TEXTURE_3D_INDEX;
+      break;
+   case GLSL_SAMPLER_DIM_CUBE:
+      inst->tex_target = TEXTURE_CUBE_INDEX;
+      break;
+   default:
+      assert(!"FINISHME: other texture targets");
+   }
+
+   this->result = result_src;
 }
 
 void
 ir_to_mesa_visitor::visit(ir_return *ir)
 {
-   assert(0);
+   assert(current_function);
+
+   if (ir->get_value()) {
+      ir_to_mesa_dst_reg l;
+      int i;
 
-   ir->get_value()->accept(this);
+      ir->get_value()->accept(this);
+      ir_to_mesa_src_reg r = this->result;
+
+      l = ir_to_mesa_dst_reg_from_src(current_function->return_reg);
+
+      for (i = 0; i < type_size(current_function->sig->return_type); i++) {
+        ir_to_mesa_emit_op1(ir, OPCODE_MOV, l, r);
+        l.index++;
+        r.index++;
+      }
+   }
+
+   ir_to_mesa_emit_op0(ir, OPCODE_RET);
 }
 
+void
+ir_to_mesa_visitor::visit(ir_discard *ir)
+{
+   assert(ir->condition == NULL); /* FINISHME */
+
+   ir_to_mesa_emit_op0(ir, OPCODE_KIL_NV);
+}
 
 void
 ir_to_mesa_visitor::visit(ir_if *ir)
 {
-   ir_to_mesa_instruction *if_inst, *else_inst = NULL;
+   ir_to_mesa_instruction *cond_inst, *if_inst, *else_inst = NULL;
+   ir_to_mesa_instruction *prev_inst;
+
+   prev_inst = (ir_to_mesa_instruction *)this->instructions.get_tail();
 
    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);
+   if (ctx->Shader.EmitCondCodes) {
+      cond_inst = (ir_to_mesa_instruction *)this->instructions.get_tail();
+
+      /* See if we actually generated any instruction for generating
+       * the condition.  If not, then cook up a move to a temp so we
+       * have something to set cond_update on.
+       */
+      if (cond_inst == prev_inst) {
+        ir_to_mesa_src_reg temp = get_temp(glsl_type::bool_type);
+        cond_inst = ir_to_mesa_emit_op1(ir->condition, OPCODE_MOV,
+                                        ir_to_mesa_dst_reg_from_src(temp),
+                                        result);
+      }
+      cond_inst->cond_update = GL_TRUE;
+
+      if_inst = ir_to_mesa_emit_op0(ir->condition, OPCODE_IF);
+      if_inst->dst_reg.cond_mask = COND_NE;
+   } else {
+      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);
+      else_inst = ir_to_mesa_emit_op0(ir->condition, OPCODE_ELSE);
+      visit_exec_list(&ir->else_instructions, this);
    }
 
    if_inst = ir_to_mesa_emit_op1(ir->condition, OPCODE_ENDIF,
@@ -1164,6 +1772,10 @@ ir_to_mesa_visitor::ir_to_mesa_visitor()
 {
    result.file = PROGRAM_UNDEFINED;
    next_temp = 1;
+   next_signature_id = 1;
+   sampler_map = NULL;
+   sampler_map_size = 0;
+   current_function = NULL;
 }
 
 static struct prog_src_register
@@ -1175,16 +1787,19 @@ mesa_src_reg_from_ir_src_reg(ir_to_mesa_src_reg reg)
    assert(reg.index < (1 << INST_INDEX_BITS) - 1);
    mesa_reg.Index = reg.index;
    mesa_reg.Swizzle = reg.swizzle;
-   mesa_reg.RelAddr = reg.reladdr;
+   mesa_reg.RelAddr = reg.reladdr != NULL;
+   mesa_reg.Negate = reg.negate;
+   mesa_reg.Abs = 0;
 
    return mesa_reg;
 }
 
 static void
-set_branchtargets(struct prog_instruction *mesa_instructions,
+set_branchtargets(ir_to_mesa_visitor *v,
+                 struct prog_instruction *mesa_instructions,
                  int num_instructions)
 {
-   int if_count = 0, loop_count;
+   int if_count = 0, loop_count = 0;
    int *if_stack, *loop_stack;
    int if_stack_pos = 0, loop_stack_pos = 0;
    int i, j;
@@ -1244,6 +1859,17 @@ set_branchtargets(struct prog_instruction *mesa_instructions,
         /* The loop ends point at each other. */
         mesa_instructions[i].BranchTarget = loop_stack[loop_stack_pos];
         mesa_instructions[loop_stack[loop_stack_pos]].BranchTarget = i;
+        break;
+      case OPCODE_CAL:
+        foreach_iter(exec_list_iterator, iter, v->function_signatures) {
+           function_entry *entry = (function_entry *)iter.get();
+
+           if (entry->sig_id == mesa_instructions[i].BranchTarget) {
+              mesa_instructions[i].BranchTarget = entry->inst;
+              break;
+           }
+        }
+        break;
       default:
         break;
       }
@@ -1275,12 +1901,52 @@ print_program(struct prog_instruction *mesa_instructions,
    }
 }
 
+static void
+mark_input(struct gl_program *prog,
+          int index,
+          GLboolean reladdr)
+{
+   prog->InputsRead |= BITFIELD64_BIT(index);
+   int i;
+
+   if (reladdr) {
+      if (index >= FRAG_ATTRIB_TEX0 && index <= FRAG_ATTRIB_TEX7) {
+        for (i = 0; i < 8; i++) {
+           prog->InputsRead |= BITFIELD64_BIT(FRAG_ATTRIB_TEX0 + i);
+        }
+      } else {
+        assert(!"FINISHME: Mark InputsRead for varying arrays");
+      }
+   }
+}
+
+static void
+mark_output(struct gl_program *prog,
+          int index,
+          GLboolean reladdr)
+{
+   prog->OutputsWritten |= BITFIELD64_BIT(index);
+   int i;
+
+   if (reladdr) {
+      if (index >= VERT_RESULT_TEX0 && index <= VERT_RESULT_TEX7) {
+        for (i = 0; i < 8; i++) {
+           prog->OutputsWritten |= BITFIELD64_BIT(FRAG_ATTRIB_TEX0 + i);
+        }
+      } else {
+        assert(!"FINISHME: Mark OutputsWritten for varying arrays");
+      }
+   }
+}
+
 static void
 count_resources(struct gl_program *prog)
 {
+   unsigned int i;
+
    prog->InputsRead = 0;
    prog->OutputsWritten = 0;
-   unsigned int i;
+   prog->SamplersUsed = 0;
 
    for (i = 0; i < prog->NumInstructions; i++) {
       struct prog_instruction *inst = &prog->Instructions[i];
@@ -1288,10 +1954,10 @@ count_resources(struct gl_program *prog)
 
       switch (inst->DstReg.File) {
       case PROGRAM_OUTPUT:
-        prog->OutputsWritten |= BITFIELD64_BIT(inst->DstReg.Index);
+        mark_output(prog, inst->DstReg.Index, inst->DstReg.RelAddr);
         break;
       case PROGRAM_INPUT:
-        prog->InputsRead |= BITFIELD64_BIT(inst->DstReg.Index);
+        mark_input(prog, inst->DstReg.Index, inst->DstReg.RelAddr);
         break;
       default:
         break;
@@ -1300,16 +1966,34 @@ count_resources(struct gl_program *prog)
       for (reg = 0; reg < _mesa_num_inst_src_regs(inst->Opcode); reg++) {
         switch (inst->SrcReg[reg].File) {
         case PROGRAM_OUTPUT:
-           prog->OutputsWritten |= BITFIELD64_BIT(inst->SrcReg[reg].Index);
+           mark_output(prog, inst->SrcReg[reg].Index,
+                       inst->SrcReg[reg].RelAddr);
            break;
         case PROGRAM_INPUT:
-           prog->InputsRead |= BITFIELD64_BIT(inst->SrcReg[reg].Index);
+           mark_input(prog, inst->SrcReg[reg].Index, inst->SrcReg[reg].RelAddr);
            break;
         default:
            break;
         }
       }
+
+      /* Instead of just using the uniform's value to map to a
+       * sampler, Mesa first allocates a separate number for the
+       * sampler (_mesa_add_sampler), then we reindex it down to a
+       * small integer (sampler_map[], SamplersUsed), then that gets
+       * mapped to the uniform's value, and we get an actual sampler.
+       */
+      if (_mesa_is_tex_instruction(inst->Opcode)) {
+        prog->SamplerTargets[inst->TexSrcUnit] =
+           (gl_texture_index)inst->TexSrcTarget;
+        prog->SamplersUsed |= 1 << inst->TexSrcUnit;
+        if (inst->TexShadow) {
+           prog->ShadowSamplers |= 1 << inst->TexSrcUnit;
+        }
+      }
    }
+
+   _mesa_update_shader_textures_used(prog);
 }
 
 /* Each stage has some uniforms in its Parameters list.  The Uniforms
@@ -1336,15 +2020,17 @@ link_uniforms_to_shared_uniform_list(struct gl_uniform_list *uniforms,
 }
 
 struct gl_program *
-get_mesa_program(GLcontext *ctx, void *mem_ctx, struct glsl_shader *shader)
+get_mesa_program(GLcontext *ctx, struct gl_shader_program *shader_program,
+                struct gl_shader *shader)
 {
+   void *mem_ctx = shader_program;
    ir_to_mesa_visitor v;
    struct prog_instruction *mesa_instructions, *mesa_inst;
    ir_instruction **mesa_instruction_annotation;
    int i;
-   exec_list *instructions = &shader->ir;
    struct gl_program *prog;
    GLenum target;
+   GLboolean progress;
 
    switch (shader->Type) {
    case GL_VERTEX_SHADER:   target = GL_VERTEX_PROGRAM_ARB; break;
@@ -1352,6 +2038,8 @@ get_mesa_program(GLcontext *ctx, void *mem_ctx, struct glsl_shader *shader)
    default: assert(!"should not be reached"); break;
    }
 
+   validate_ir_tree(shader->ir);
+
    prog = ctx->Driver.NewProgram(ctx, target, 1);
    if (!prog)
       return NULL;
@@ -1362,9 +2050,32 @@ get_mesa_program(GLcontext *ctx, void *mem_ctx, struct glsl_shader *shader)
    v.prog = prog;
 
    v.mem_ctx = talloc_new(NULL);
-   visit_exec_list(instructions, &v);
-   v.ir_to_mesa_emit_op1(NULL, OPCODE_END,
-                        ir_to_mesa_undef_dst, ir_to_mesa_undef);
+
+   /* Emit Mesa IR for main(). */
+   visit_exec_list(shader->ir, &v);
+   v.ir_to_mesa_emit_op0(NULL, OPCODE_END);
+
+   /* Now emit bodies for any functions that were used. */
+   do {
+      progress = GL_FALSE;
+
+      foreach_iter(exec_list_iterator, iter, v.function_signatures) {
+        function_entry *entry = (function_entry *)iter.get();
+
+        if (!entry->bgn_inst) {
+           v.current_function = entry;
+
+           entry->bgn_inst = v.ir_to_mesa_emit_op0(NULL, OPCODE_BGNSUB);
+           entry->bgn_inst->function = entry;
+
+           visit_exec_list(&entry->sig->body, &v);
+
+           entry->bgn_inst = v.ir_to_mesa_emit_op0(NULL, OPCODE_RET);
+           entry->bgn_inst = v.ir_to_mesa_emit_op0(NULL, OPCODE_ENDSUB);
+           progress = GL_TRUE;
+        }
+      }
+   } while (progress);
 
    prog->NumTemporaries = v.next_temp;
 
@@ -1385,20 +2096,37 @@ get_mesa_program(GLcontext *ctx, void *mem_ctx, struct glsl_shader *shader)
       ir_to_mesa_instruction *inst = (ir_to_mesa_instruction *)iter.get();
 
       mesa_inst->Opcode = inst->op;
+      mesa_inst->CondUpdate = inst->cond_update;
       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.CondMask = inst->dst_reg.cond_mask;
       mesa_inst->DstReg.WriteMask = inst->dst_reg.writemask;
+      mesa_inst->DstReg.RelAddr = inst->dst_reg.reladdr != NULL;
       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_inst->TexSrcUnit = inst->sampler;
+      mesa_inst->TexSrcTarget = inst->tex_target;
+      mesa_inst->TexShadow = inst->tex_shadow;
       mesa_instruction_annotation[i] = inst->ir;
 
+      if (ctx->Shader.EmitNoIfs && mesa_inst->Opcode == OPCODE_IF) {
+        shader_program->InfoLog =
+           talloc_asprintf_append(shader_program->InfoLog,
+                                  "Couldn't flatten if statement\n");
+        shader_program->LinkStatus = false;
+      }
+
+      if (mesa_inst->Opcode == OPCODE_BGNSUB)
+        inst->function->inst = i;
+      else if (mesa_inst->Opcode == OPCODE_CAL)
+        mesa_inst->BranchTarget = inst->function->sig_id; /* rewritten later */
+
       mesa_inst++;
       i++;
    }
 
-   set_branchtargets(mesa_instructions, num_instructions);
+   set_branchtargets(&v, mesa_instructions, num_instructions);
    if (0) {
       print_program(mesa_instructions, mesa_instruction_annotation,
                    num_instructions);
@@ -1407,154 +2135,146 @@ get_mesa_program(GLcontext *ctx, void *mem_ctx, struct glsl_shader *shader)
    prog->Instructions = mesa_instructions;
    prog->NumInstructions = num_instructions;
 
-   _mesa_reference_program(ctx, &shader->mesa_shader->Program, prog);
+   _mesa_reference_program(ctx, &shader->Program, prog);
+
+   if ((ctx->Shader.Flags & GLSL_NO_OPT) == 0) {
+      _mesa_optimize_program(ctx, prog);
+   }
 
    return prog;
 }
 
-/* Takes a Mesa gl shader structure and compiles it, returning our Mesa-like
- * structure with the IR and such attached.
- */
-static struct glsl_shader *
-_mesa_get_glsl_shader(GLcontext *ctx, void *mem_ctx, struct gl_shader *sh)
+extern "C" {
+
+void
+_mesa_glsl_compile_shader(GLcontext *ctx, struct gl_shader *shader)
 {
-   struct glsl_shader *shader = talloc_zero(mem_ctx, struct glsl_shader);
-   struct _mesa_glsl_parse_state *state;
+   struct _mesa_glsl_parse_state *state =
+      new(shader) _mesa_glsl_parse_state(ctx, shader->Type, shader);
 
-   shader->Type = sh->Type;
-   shader->Name = sh->Name;
-   shader->RefCount = 1;
-   shader->Source = sh->Source;
-   shader->SourceLen = strlen(sh->Source);
-   shader->mesa_shader = sh;
+   const char *source = shader->Source;
+   state->error = preprocess(state, &source, &state->info_log,
+                            &ctx->Extensions);
 
-   state = talloc_zero(shader, struct _mesa_glsl_parse_state);
-   switch (shader->Type) {
-   case GL_VERTEX_SHADER:   state->target = vertex_shader; break;
-   case GL_FRAGMENT_SHADER: state->target = fragment_shader; break;
-   case GL_GEOMETRY_SHADER: state->target = geometry_shader; break;
+   if (!state->error) {
+     _mesa_glsl_lexer_ctor(state, source);
+     _mesa_glsl_parse(state);
+     _mesa_glsl_lexer_dtor(state);
    }
 
-   state->scanner = NULL;
-   state->translation_unit.make_empty();
-   state->symbols = new(mem_ctx) glsl_symbol_table;
-   state->info_log = talloc_strdup(shader, "");
-   state->error = false;
-   state->temp_index = 0;
-   state->loop_or_switch_nesting = NULL;
-   state->ARB_texture_rectangle_enable = true;
+   shader->ir = new(shader) exec_list;
+   if (!state->error && !state->translation_unit.is_empty())
+      _mesa_ast_to_hir(shader->ir, state);
 
-   _mesa_glsl_lexer_ctor(state, shader->Source);
-   _mesa_glsl_parse(state);
-   _mesa_glsl_lexer_dtor(state);
+   if (!state->error && !shader->ir->is_empty()) {
+      validate_ir_tree(shader->ir);
 
-   shader->ir.make_empty();
-   if (!state->error && !state->translation_unit.is_empty())
-      _mesa_ast_to_hir(&shader->ir, state);
+      /* Lowering */
+      do_mat_op_to_vec(shader->ir);
+      do_mod_to_fract(shader->ir);
+      do_div_to_mul_rcp(shader->ir);
 
-   /* Optimization passes */
-   if (!state->error && !shader->ir.is_empty()) {
+      /* Optimization passes */
       bool progress;
       do {
         progress = false;
 
-        progress = do_function_inlining(&shader->ir) || progress;
-        progress = do_if_simplification(&shader->ir) || progress;
-        progress = do_copy_propagation(&shader->ir) || progress;
-        progress = do_dead_code_local(&shader->ir) || progress;
-        progress = do_dead_code_unlinked(state, &shader->ir) || progress;
-        progress = do_constant_variable_unlinked(&shader->ir) || progress;
-        progress = do_constant_folding(&shader->ir) || progress;
-        progress = do_vec_index_to_swizzle(&shader->ir) || progress;
-        progress = do_swizzle_swizzle(&shader->ir) || progress;
+        progress = do_function_inlining(shader->ir) || progress;
+        progress = do_if_simplification(shader->ir) || progress;
+        progress = do_copy_propagation(shader->ir) || progress;
+        progress = do_dead_code_local(shader->ir) || progress;
+        progress = do_dead_code_unlinked(state, shader->ir) || progress;
+        progress = do_constant_variable_unlinked(shader->ir) || progress;
+        progress = do_constant_folding(shader->ir) || progress;
+        progress = do_if_return(shader->ir) || progress;
+        if (ctx->Shader.EmitNoIfs)
+           progress = do_if_to_cond_assign(shader->ir) || progress;
+
+        progress = do_vec_index_to_swizzle(shader->ir) || progress;
+        /* Do this one after the previous to let the easier pass handle
+         * constant vector indexing.
+         */
+        progress = do_vec_index_to_cond_assign(shader->ir) || progress;
+
+        progress = do_swizzle_swizzle(shader->ir) || progress;
       } while (progress);
+
+      validate_ir_tree(shader->ir);
    }
 
    shader->symbols = state->symbols;
 
    shader->CompileStatus = !state->error;
    shader->InfoLog = state->info_log;
+   shader->Version = state->language_version;
+   memcpy(shader->builtins_to_link, state->builtins_to_link,
+         sizeof(shader->builtins_to_link[0]) * state->num_builtins_to_link);
+   shader->num_builtins_to_link = state->num_builtins_to_link;
 
-   talloc_free(state);
-
-   return shader;
-}
-
-extern "C" {
-
-void
-_mesa_glsl_compile_shader(GLcontext *ctx, struct gl_shader *sh)
-{
-   struct glsl_shader *shader;
-   TALLOC_CTX *mem_ctx = talloc_new(NULL);
-
-   shader = _mesa_get_glsl_shader(ctx, mem_ctx, sh);
+   /* Retain any live IR, but trash the rest. */
+   reparent_ir(shader->ir, shader);
 
-   sh->CompileStatus = shader->CompileStatus;
-   sh->InfoLog = strdup(shader->InfoLog);
-   talloc_free(mem_ctx);
+   talloc_free(state);
  }
 
 void
 _mesa_glsl_link_shader(GLcontext *ctx, struct gl_shader_program *prog)
 {
-   struct glsl_program *whole_program;
    unsigned int i;
 
    _mesa_clear_shader_program_data(ctx, prog);
 
-   whole_program = talloc_zero(NULL, struct glsl_program);
-   whole_program->LinkStatus = GL_TRUE;
-   whole_program->NumShaders = prog->NumShaders;
-   whole_program->Shaders = talloc_array(whole_program, struct glsl_shader *,
-                                        prog->NumShaders);
+   prog->LinkStatus = GL_TRUE;
 
    for (i = 0; i < prog->NumShaders; i++) {
-      whole_program->Shaders[i] = _mesa_get_glsl_shader(ctx, whole_program,
-                                                       prog->Shaders[i]);
-      if (!whole_program->Shaders[i]->CompileStatus) {
-        whole_program->InfoLog =
-           talloc_asprintf_append(whole_program->InfoLog,
+      if (!prog->Shaders[i]->CompileStatus) {
+        prog->InfoLog =
+           talloc_asprintf_append(prog->InfoLog,
                                   "linking with uncompiled shader");
-        whole_program->LinkStatus = GL_FALSE;
+        prog->LinkStatus = GL_FALSE;
       }
    }
 
-   prog->Uniforms = _mesa_new_uniform_list();
    prog->Varying = _mesa_new_parameter_list();
    _mesa_reference_vertprog(ctx, &prog->VertexProgram, NULL);
    _mesa_reference_fragprog(ctx, &prog->FragmentProgram, NULL);
 
-   if (whole_program->LinkStatus)
-      link_shaders(whole_program);
+   if (prog->LinkStatus) {
+      link_shaders(prog);
 
-   prog->LinkStatus = whole_program->LinkStatus;
+      /* We don't use the linker's uniforms list, and cook up our own at
+       * generate time.
+       */
+      free(prog->Uniforms);
+      prog->Uniforms = _mesa_new_uniform_list();
+   }
 
-   /* FINISHME: This should use the linker-generated code */
    if (prog->LinkStatus) {
-      for (i = 0; i < prog->NumShaders; i++) {
+      for (i = 0; i < prog->_NumLinkedShaders; i++) {
         struct gl_program *linked_prog;
 
-        linked_prog = get_mesa_program(ctx, whole_program,
-                                       whole_program->Shaders[i]);
+        linked_prog = get_mesa_program(ctx, prog,
+                                       prog->_LinkedShaders[i]);
         count_resources(linked_prog);
 
         link_uniforms_to_shared_uniform_list(prog->Uniforms, linked_prog);
 
-        switch (whole_program->Shaders[i]->Type) {
+        switch (prog->_LinkedShaders[i]->Type) {
         case GL_VERTEX_SHADER:
            _mesa_reference_vertprog(ctx, &prog->VertexProgram,
                                     (struct gl_vertex_program *)linked_prog);
+           ctx->Driver.ProgramStringNotify(ctx, GL_VERTEX_PROGRAM_ARB,
+                                           linked_prog);
            break;
         case GL_FRAGMENT_SHADER:
            _mesa_reference_fragprog(ctx, &prog->FragmentProgram,
                                     (struct gl_fragment_program *)linked_prog);
+           ctx->Driver.ProgramStringNotify(ctx, GL_FRAGMENT_PROGRAM_ARB,
+                                           linked_prog);
            break;
         }
       }
    }
-
-   talloc_free(whole_program);
 }
 
 } /* extern "C" */