mesa: s/mesaFormat/attFormat/
[mesa.git] / src / mesa / program / ir_to_mesa.cpp
index 98da90d359ad0bfddaea2bb51e3feb3a523bb612..33c262f8ca4cea2833bc838a80f9551020ed0a8d 100644 (file)
@@ -65,7 +65,7 @@ static int swizzle_for_size(int size);
 typedef struct ir_to_mesa_src_reg {
    ir_to_mesa_src_reg(int file, int index, const glsl_type *type)
    {
-      this->file = file;
+      this->file = (gl_register_file) file;
       this->index = index;
       if (type && (type->is_scalar() || type->is_vector() || type->is_matrix()))
         this->swizzle = swizzle_for_size(type->vector_elements);
@@ -84,7 +84,7 @@ typedef struct ir_to_mesa_src_reg {
       this->reladdr = NULL;
    }
 
-   int file; /**< PROGRAM_* from Mesa */
+   gl_register_file file; /**< PROGRAM_* from Mesa */
    int index; /**< temporary index, VERT_ATTRIB_*, FRAG_ATTRIB_*, etc. */
    GLuint swizzle; /**< SWIZZLE_XYZWONEZERO swizzles from Mesa. */
    int negate; /**< NEGATE_XYZW mask from mesa */
@@ -105,13 +105,13 @@ extern ir_to_mesa_src_reg ir_to_mesa_undef;
 
 class ir_to_mesa_instruction : public exec_node {
 public:
-   /* Callers of this talloc-based new need not call delete. It's
-    * easier to just talloc_free 'ctx' (or any of its ancestors). */
+   /* Callers of this ralloc-based new need not call delete. It's
+    * easier to just ralloc_free 'ctx' (or any of its ancestors). */
    static void* operator new(size_t size, void *ctx)
    {
       void *node;
 
-      node = talloc_zero_size(ctx, size);
+      node = rzalloc_size(ctx, size);
       assert(node != NULL);
 
       return node;
@@ -133,13 +133,13 @@ public:
 
 class variable_storage : public exec_node {
 public:
-   variable_storage(ir_variable *var, int file, int index)
+   variable_storage(ir_variable *var, gl_register_file file, int index)
       : file(file), index(index), var(var)
    {
       /* empty */
    }
 
-   int file;
+   gl_register_file file;
    int index;
    ir_variable *var; /* variable that maps to this, if any */
 };
@@ -295,6 +295,8 @@ public:
 
    bool process_move_condition(ir_rvalue *ir);
 
+   void copy_propagate(void);
+
    void *mem_ctx;
 };
 
@@ -316,7 +318,7 @@ fail_link(struct gl_shader_program *prog, const char *fmt, ...)
 {
    va_list args;
    va_start(args, fmt);
-   prog->InfoLog = talloc_vasprintf_append(prog->InfoLog, fmt, args);
+   ralloc_vasprintf_append(&prog->InfoLog, fmt, args);
    va_end(args);
 
    prog->LinkStatus = GL_FALSE;
@@ -649,6 +651,7 @@ type_size(const struct glsl_type *type)
         return 1;
       }
    case GLSL_TYPE_ARRAY:
+      assert(type->length > 0);
       return type_size(type->fields.array) * type->length;
    case GLSL_TYPE_STRUCT:
       size = 0;
@@ -724,6 +727,29 @@ ir_to_mesa_visitor::visit(ir_variable *ir)
 
       fp->OriginUpperLeft = ir->origin_upper_left;
       fp->PixelCenterInteger = ir->pixel_center_integer;
+
+   } else if (strcmp(ir->name, "gl_FragDepth") == 0) {
+      struct gl_fragment_program *fp = (struct gl_fragment_program *)this->prog;
+      switch (ir->depth_layout) {
+      case ir_depth_layout_none:
+        fp->FragDepthLayout = FRAG_DEPTH_LAYOUT_NONE;
+        break;
+      case ir_depth_layout_any:
+        fp->FragDepthLayout = FRAG_DEPTH_LAYOUT_ANY;
+        break;
+      case ir_depth_layout_greater:
+        fp->FragDepthLayout = FRAG_DEPTH_LAYOUT_GREATER;
+        break;
+      case ir_depth_layout_less:
+        fp->FragDepthLayout = FRAG_DEPTH_LAYOUT_LESS;
+        break;
+      case ir_depth_layout_unchanged:
+        fp->FragDepthLayout = FRAG_DEPTH_LAYOUT_UNCHANGED;
+        break;
+      default:
+        assert(0);
+        break;
+      }
    }
 
    if (ir->mode == ir_var_uniform && strncmp(ir->name, "gl_", 3) == 0) {
@@ -1449,16 +1475,16 @@ void
 ir_to_mesa_visitor::visit(ir_dereference_variable *ir)
 {
    variable_storage *entry = find_variable_storage(ir->var);
+   ir_variable *var = ir->var;
 
    if (!entry) {
-      switch (ir->var->mode) {
+      switch (var->mode) {
       case ir_var_uniform:
-        entry = new(mem_ctx) variable_storage(ir->var, PROGRAM_UNIFORM,
-                                              ir->var->location);
+        entry = new(mem_ctx) variable_storage(var, PROGRAM_UNIFORM,
+                                              var->location);
         this->variables.push_tail(entry);
         break;
       case ir_var_in:
-      case ir_var_out:
       case ir_var_inout:
         /* The linker assigns locations for varyings and attributes,
          * including deprecated builtins (like gl_Color), user-assign
@@ -1467,45 +1493,47 @@ ir_to_mesa_visitor::visit(ir_dereference_variable *ir)
          *
          * 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,
-                                  _mesa_sizeof_glsl_type(ir->var->type->gl_type),
-                                  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);
-        }
-
+        assert(var->location != -1);
+         entry = new(mem_ctx) variable_storage(var,
+                                               PROGRAM_INPUT,
+                                               var->location);
+         if (this->prog->Target == GL_VERTEX_PROGRAM_ARB &&
+             var->location >= VERT_ATTRIB_GENERIC0) {
+            _mesa_add_attribute(this->prog->Attributes,
+                                var->name,
+                                _mesa_sizeof_glsl_type(var->type->gl_type),
+                                var->type->gl_type,
+                                var->location - VERT_ATTRIB_GENERIC0);
+         }
+         break;
+      case ir_var_out:
+        assert(var->location != -1);
+         entry = new(mem_ctx) variable_storage(var,
+                                               PROGRAM_OUTPUT,
+                                               var->location);
         break;
+      case ir_var_system_value:
+         entry = new(mem_ctx) variable_storage(var,
+                                               PROGRAM_SYSTEM_VALUE,
+                                               var->location);
+         break;
       case ir_var_auto:
       case ir_var_temporary:
-        entry = new(mem_ctx) variable_storage(ir->var, PROGRAM_TEMPORARY,
+        entry = new(mem_ctx) variable_storage(var, PROGRAM_TEMPORARY,
                                               this->next_temp);
         this->variables.push_tail(entry);
 
-        next_temp += type_size(ir->var->type);
+        next_temp += type_size(var->type);
         break;
       }
 
       if (!entry) {
-        printf("Failed to make storage for %s\n", ir->var->name);
+        printf("Failed to make storage for %s\n", var->name);
         exit(1);
       }
    }
 
-   this->result = ir_to_mesa_src_reg(entry->file, entry->index, ir->var->type);
+   this->result = ir_to_mesa_src_reg(entry->file, entry->index, var->type);
 }
 
 void
@@ -1542,7 +1570,7 @@ ir_to_mesa_visitor::visit(ir_dereference_array *ir)
                             this->result, src_reg_for_float(element_size));
       }
 
-      src_reg.reladdr = talloc(mem_ctx, ir_to_mesa_src_reg);
+      src_reg.reladdr = ralloc(mem_ctx, ir_to_mesa_src_reg);
       memcpy(src_reg.reladdr, &index_reg, sizeof(index_reg));
    }
 
@@ -1569,7 +1597,13 @@ ir_to_mesa_visitor::visit(ir_dereference_record *ir)
         break;
       offset += type_size(struct_type->fields.structure[i].type);
    }
-   this->result.swizzle = swizzle_for_size(ir->type->vector_elements);
+
+   /* If the type is smaller than a vec4, replicate the last channel out. */
+   if (ir->type->is_scalar() || ir->type->is_vector())
+      this->result.swizzle = swizzle_for_size(ir->type->vector_elements);
+   else
+      this->result.swizzle = SWIZZLE_NOOP;
+
    this->result.index += offset;
 }
 
@@ -1893,7 +1927,7 @@ ir_to_mesa_visitor::get_function_signature(ir_function_signature *sig)
         return entry;
    }
 
-   entry = talloc(mem_ctx, function_entry);
+   entry = ralloc(mem_ctx, function_entry);
    entry->sig = sig;
    entry->sig_id = this->next_signature_id++;
    entry->bgn_inst = NULL;
@@ -2166,9 +2200,14 @@ ir_to_mesa_visitor::visit(ir_discard *ir)
 {
    struct gl_fragment_program *fp = (struct gl_fragment_program *)this->prog;
 
-   assert(ir->condition == NULL); /* FINISHME */
+   if (ir->condition) {
+      ir->condition->accept(this);
+      this->result.negate = ~this->result.negate;
+      ir_to_mesa_emit_op1(ir, OPCODE_KIL, ir_to_mesa_undef_dst, this->result);
+   } else {
+      ir_to_mesa_emit_op0(ir, OPCODE_KIL_NV);
+   }
 
-   ir_to_mesa_emit_op0(ir, OPCODE_KIL_NV);
    fp->UsesKill = GL_TRUE;
 }
 
@@ -2225,12 +2264,12 @@ ir_to_mesa_visitor::ir_to_mesa_visitor()
    next_temp = 1;
    next_signature_id = 1;
    current_function = NULL;
-   mem_ctx = talloc_new(NULL);
+   mem_ctx = ralloc_context(NULL);
 }
 
 ir_to_mesa_visitor::~ir_to_mesa_visitor()
 {
-   talloc_free(mem_ctx);
+   ralloc_free(mem_ctx);
 }
 
 static struct prog_src_register
@@ -2239,7 +2278,7 @@ 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);
+   assert(reg.index < (1 << INST_INDEX_BITS));
    mesa_reg.Index = reg.index;
    mesa_reg.Swizzle = reg.swizzle;
    mesa_reg.RelAddr = reg.reladdr != NULL;
@@ -2279,8 +2318,8 @@ set_branchtargets(ir_to_mesa_visitor *v,
       }
    }
 
-   if_stack = talloc_zero_array(v->mem_ctx, int, if_count);
-   loop_stack = talloc_zero_array(v->mem_ctx, int, loop_count);
+   if_stack = rzalloc_array(v->mem_ctx, int, if_count);
+   loop_stack = rzalloc_array(v->mem_ctx, int, loop_count);
 
    for (i = 0; i < num_instructions; i++) {
       switch (mesa_instructions[i].Opcode) {
@@ -2423,7 +2462,7 @@ add_uniforms_to_parameters_list(struct gl_shader_program *shader_program,
    unsigned int next_sampler = 0, num_uniforms = 0;
    struct uniform_sort *sorted_uniforms;
 
-   sorted_uniforms = talloc_array(NULL, struct uniform_sort,
+   sorted_uniforms = ralloc_array(NULL, struct uniform_sort,
                                  shader_program->Uniforms->NumUniforms);
 
    for (i = 0; i < shader_program->Uniforms->NumUniforms; i++) {
@@ -2502,7 +2541,7 @@ add_uniforms_to_parameters_list(struct gl_shader_program *shader_program,
       }
    }
 
-   talloc_free(sorted_uniforms);
+   ralloc_free(sorted_uniforms);
 }
 
 static void
@@ -2518,7 +2557,7 @@ set_uniform_initializer(struct gl_context *ctx, void *mem_ctx,
 
       for (unsigned int i = 0; i < type->length; i++) {
         const glsl_type *field_type = type->fields.structure[i].type;
-        const char *field_name = talloc_asprintf(mem_ctx, "%s.%s", name,
+        const char *field_name = ralloc_asprintf(mem_ctx, "%s.%s", name,
                                            type->fields.structure[i].name);
         set_uniform_initializer(ctx, mem_ctx, shader_program, field_name,
                                 field_type, field_constant);
@@ -2549,7 +2588,7 @@ set_uniform_initializer(struct gl_context *ctx, void *mem_ctx,
       void *values;
 
       if (element_type->base_type == GLSL_TYPE_BOOL) {
-        int *conv = talloc_array(mem_ctx, int, element_type->components());
+        int *conv = ralloc_array(mem_ctx, int, element_type->components());
         for (unsigned int j = 0; j < element_type->components(); j++) {
            conv[j] = element->value.b[j];
         }
@@ -2595,22 +2634,215 @@ set_uniform_initializers(struct gl_context *ctx,
            continue;
 
         if (!mem_ctx)
-           mem_ctx = talloc_new(NULL);
+           mem_ctx = ralloc_context(NULL);
 
         set_uniform_initializer(ctx, mem_ctx, shader_program, var->name,
                                 var->type, var->constant_value);
       }
    }
 
-   talloc_free(mem_ctx);
+   ralloc_free(mem_ctx);
+}
+
+/*
+ * On a basic block basis, tracks available PROGRAM_TEMPORARY register
+ * channels for copy propagation and updates following instructions to
+ * use the original versions.
+ *
+ * The ir_to_mesa_visitor lazily produces code assuming that this pass
+ * will occur.  As an example, a TXP production before this pass:
+ *
+ * 0: MOV TEMP[1], INPUT[4].xyyy;
+ * 1: MOV TEMP[1].w, INPUT[4].wwww;
+ * 2: TXP TEMP[2], TEMP[1], texture[0], 2D;
+ *
+ * and after:
+ *
+ * 0: MOV TEMP[1], INPUT[4].xyyy;
+ * 1: MOV TEMP[1].w, INPUT[4].wwww;
+ * 2: TXP TEMP[2], INPUT[4].xyyw, texture[0], 2D;
+ *
+ * which allows for dead code elimination on TEMP[1]'s writes.
+ */
+void
+ir_to_mesa_visitor::copy_propagate(void)
+{
+   ir_to_mesa_instruction **acp = rzalloc_array(mem_ctx,
+                                                   ir_to_mesa_instruction *,
+                                                   this->next_temp * 4);
+   int *acp_level = rzalloc_array(mem_ctx, int, this->next_temp * 4);
+   int level = 0;
+
+   foreach_iter(exec_list_iterator, iter, this->instructions) {
+      ir_to_mesa_instruction *inst = (ir_to_mesa_instruction *)iter.get();
+
+      assert(inst->dst_reg.file != PROGRAM_TEMPORARY
+            || inst->dst_reg.index < this->next_temp);
+
+      /* First, do any copy propagation possible into the src regs. */
+      for (int r = 0; r < 3; r++) {
+        ir_to_mesa_instruction *first = NULL;
+        bool good = true;
+        int acp_base = inst->src_reg[r].index * 4;
+
+        if (inst->src_reg[r].file != PROGRAM_TEMPORARY ||
+            inst->src_reg[r].reladdr)
+           continue;
+
+        /* See if we can find entries in the ACP consisting of MOVs
+         * from the same src register for all the swizzled channels
+         * of this src register reference.
+         */
+        for (int i = 0; i < 4; i++) {
+           int src_chan = GET_SWZ(inst->src_reg[r].swizzle, i);
+           ir_to_mesa_instruction *copy_chan = acp[acp_base + src_chan];
+
+           if (!copy_chan) {
+              good = false;
+              break;
+           }
+
+           assert(acp_level[acp_base + src_chan] <= level);
+
+           if (!first) {
+              first = copy_chan;
+           } else {
+              if (first->src_reg[0].file != copy_chan->src_reg[0].file ||
+                  first->src_reg[0].index != copy_chan->src_reg[0].index) {
+                 good = false;
+                 break;
+              }
+           }
+        }
+
+        if (good) {
+           /* We've now validated that we can copy-propagate to
+            * replace this src register reference.  Do it.
+            */
+           inst->src_reg[r].file = first->src_reg[0].file;
+           inst->src_reg[r].index = first->src_reg[0].index;
+
+           int swizzle = 0;
+           for (int i = 0; i < 4; i++) {
+              int src_chan = GET_SWZ(inst->src_reg[r].swizzle, i);
+              ir_to_mesa_instruction *copy_inst = acp[acp_base + src_chan];
+              swizzle |= (GET_SWZ(copy_inst->src_reg[0].swizzle, src_chan) <<
+                          (3 * i));
+           }
+           inst->src_reg[r].swizzle = swizzle;
+        }
+      }
+
+      switch (inst->op) {
+      case OPCODE_BGNLOOP:
+      case OPCODE_ENDLOOP:
+        /* End of a basic block, clear the ACP entirely. */
+        memset(acp, 0, sizeof(*acp) * this->next_temp * 4);
+        break;
+
+      case OPCODE_IF:
+        ++level;
+        break;
+
+      case OPCODE_ENDIF:
+      case OPCODE_ELSE:
+        /* Clear all channels written inside the block from the ACP, but
+         * leaving those that were not touched.
+         */
+        for (int r = 0; r < this->next_temp; r++) {
+           for (int c = 0; c < 4; c++) {
+              if (!acp[4 * r + c])
+                 continue;
+
+              if (acp_level[4 * r + c] >= level)
+                 acp[4 * r + c] = NULL;
+           }
+        }
+        if (inst->op == OPCODE_ENDIF)
+           --level;
+        break;
+
+      default:
+        /* Continuing the block, clear any written channels from
+         * the ACP.
+         */
+        if (inst->dst_reg.file == PROGRAM_TEMPORARY && inst->dst_reg.reladdr) {
+           /* Any temporary might be written, so no copy propagation
+            * across this instruction.
+            */
+           memset(acp, 0, sizeof(*acp) * this->next_temp * 4);
+        } else if (inst->dst_reg.file == PROGRAM_OUTPUT &&
+                   inst->dst_reg.reladdr) {
+           /* Any output might be written, so no copy propagation
+            * from outputs across this instruction.
+            */
+           for (int r = 0; r < this->next_temp; r++) {
+              for (int c = 0; c < 4; c++) {
+                 if (!acp[4 * r + c])
+                    continue;
+
+                 if (acp[4 * r + c]->src_reg[0].file == PROGRAM_OUTPUT)
+                    acp[4 * r + c] = NULL;
+              }
+           }
+        } else if (inst->dst_reg.file == PROGRAM_TEMPORARY ||
+                   inst->dst_reg.file == PROGRAM_OUTPUT) {
+           /* Clear where it's used as dst. */
+           if (inst->dst_reg.file == PROGRAM_TEMPORARY) {
+              for (int c = 0; c < 4; c++) {
+                 if (inst->dst_reg.writemask & (1 << c)) {
+                    acp[4 * inst->dst_reg.index + c] = NULL;
+                 }
+              }
+           }
+
+           /* Clear where it's used as src. */
+           for (int r = 0; r < this->next_temp; r++) {
+              for (int c = 0; c < 4; c++) {
+                 if (!acp[4 * r + c])
+                    continue;
+
+                 int src_chan = GET_SWZ(acp[4 * r + c]->src_reg[0].swizzle, c);
+
+                 if (acp[4 * r + c]->src_reg[0].file == inst->dst_reg.file &&
+                     acp[4 * r + c]->src_reg[0].index == inst->dst_reg.index &&
+                     inst->dst_reg.writemask & (1 << src_chan))
+                 {
+                    acp[4 * r + c] = NULL;
+                 }
+              }
+           }
+        }
+        break;
+      }
+
+      /* If this is a copy, add it to the ACP. */
+      if (inst->op == OPCODE_MOV &&
+         inst->dst_reg.file == PROGRAM_TEMPORARY &&
+         !inst->dst_reg.reladdr &&
+         !inst->saturate &&
+         !inst->src_reg[0].reladdr &&
+         !inst->src_reg[0].negate) {
+        for (int i = 0; i < 4; i++) {
+           if (inst->dst_reg.writemask & (1 << i)) {
+              acp[4 * inst->dst_reg.index + i] = inst;
+              acp_level[4 * inst->dst_reg.index + i] = level;
+           }
+        }
+      }
+   }
+
+   ralloc_free(acp_level);
+   ralloc_free(acp);
 }
 
 
 /**
  * Convert a shader's GLSL IR into a Mesa gl_program.
  */
-struct gl_program *
-get_mesa_program(struct gl_context *ctx, struct gl_shader_program *shader_program,
+static struct gl_program *
+get_mesa_program(struct gl_context *ctx,
+                 struct gl_shader_program *shader_program,
                 struct gl_shader *shader)
 {
    ir_to_mesa_visitor v;
@@ -2633,6 +2865,10 @@ get_mesa_program(struct gl_context *ctx, struct gl_shader_program *shader_progra
       target = GL_FRAGMENT_PROGRAM_ARB;
       target_string = "fragment";
       break;
+   case GL_GEOMETRY_SHADER:
+      target = GL_GEOMETRY_PROGRAM_NV;
+      target_string = "geometry";
+      break;
    default:
       assert(!"should not be reached");
       return NULL;
@@ -2696,9 +2932,11 @@ get_mesa_program(struct gl_context *ctx, struct gl_shader_program *shader_progra
    mesa_instructions =
       (struct prog_instruction *)calloc(num_instructions,
                                        sizeof(*mesa_instructions));
-   mesa_instruction_annotation = talloc_array(v.mem_ctx, ir_instruction *,
+   mesa_instruction_annotation = ralloc_array(v.mem_ctx, ir_instruction *,
                                              num_instructions);
 
+   v.copy_propagate();
+
    /* Convert ir_mesa_instructions into prog_instructions.
     */
    mesa_inst = mesa_instructions;
@@ -2756,6 +2994,15 @@ get_mesa_program(struct gl_context *ctx, struct gl_shader_program *shader_progra
 
       mesa_inst++;
       i++;
+
+      if (!shader_program->LinkStatus)
+         break;
+   }
+
+   if (!shader_program->LinkStatus) {
+      free(mesa_instructions);
+      _mesa_reference_program(ctx, &shader->Program, NULL);
+      return NULL;
    }
 
    set_branchtargets(&v, mesa_instructions, num_instructions);
@@ -2830,8 +3077,9 @@ _mesa_ir_link_shader(struct gl_context *ctx, struct gl_shader_program *prog)
 
         /* Lowering */
         do_mat_op_to_vec(ir);
-        lower_instructions(ir, MOD_TO_FRACT | DIV_TO_MUL_RCP | EXP_TO_EXP2
-                             | LOG_TO_LOG2);
+        lower_instructions(ir, (MOD_TO_FRACT | DIV_TO_MUL_RCP | EXP_TO_EXP2
+                                | LOG_TO_LOG2
+                                | ((options->EmitNoPow) ? POW_TO_EXP2 : 0)));
 
         progress = do_lower_jumps(ir, true, true, options->EmitNoMainReturn, options->EmitNoCont, options->EmitNoLoops) || progress;
 
@@ -2839,8 +3087,10 @@ _mesa_ir_link_shader(struct gl_context *ctx, struct gl_shader_program *prog)
 
         progress = lower_quadop_vector(ir, true) || progress;
 
-        if (options->EmitNoIfs)
-           progress = do_if_to_cond_assign(ir) || progress;
+        if (options->EmitNoIfs) {
+           progress = lower_discard(ir) || progress;
+           progress = lower_if_to_cond_assign(ir) || progress;
+        }
 
         if (options->EmitNoNoise)
            progress = lower_noise(ir) || progress;
@@ -2866,30 +3116,40 @@ _mesa_ir_link_shader(struct gl_context *ctx, struct gl_shader_program *prog)
 
    for (unsigned i = 0; i < MESA_SHADER_TYPES; i++) {
       struct gl_program *linked_prog;
-      bool ok = true;
 
       if (prog->_LinkedShaders[i] == NULL)
         continue;
 
       linked_prog = get_mesa_program(ctx, prog, prog->_LinkedShaders[i]);
 
-      switch (prog->_LinkedShaders[i]->Type) {
-      case GL_VERTEX_SHADER:
-        _mesa_reference_vertprog(ctx, &prog->VertexProgram,
-                                 (struct gl_vertex_program *)linked_prog);
-        ok = 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);
-        ok = ctx->Driver.ProgramStringNotify(ctx, GL_FRAGMENT_PROGRAM_ARB,
-                                             linked_prog);
-        break;
-      }
-      if (!ok) {
-        return GL_FALSE;
+      if (linked_prog) {
+         bool ok = true;
+
+         switch (prog->_LinkedShaders[i]->Type) {
+         case GL_VERTEX_SHADER:
+            _mesa_reference_vertprog(ctx, &prog->VertexProgram,
+                                     (struct gl_vertex_program *)linked_prog);
+            ok = 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);
+            ok = ctx->Driver.ProgramStringNotify(ctx, GL_FRAGMENT_PROGRAM_ARB,
+                                                 linked_prog);
+            break;
+         case GL_GEOMETRY_SHADER:
+            _mesa_reference_geomprog(ctx, &prog->GeometryProgram,
+                                     (struct gl_geometry_program *)linked_prog);
+            ok = ctx->Driver.ProgramStringNotify(ctx, GL_GEOMETRY_PROGRAM_NV,
+                                                 linked_prog);
+            break;
+         }
+         if (!ok) {
+            return GL_FALSE;
+         }
       }
+
       _mesa_reference_program(ctx, &linked_prog, NULL);
    }
 
@@ -2929,7 +3189,7 @@ _mesa_glsl_compile_shader(struct gl_context *ctx, struct gl_shader *shader)
      _mesa_glsl_lexer_dtor(state);
    }
 
-   talloc_free(shader->ir);
+   ralloc_free(shader->ir);
    shader->ir = new(shader) exec_list;
    if (!state->error && !state->translation_unit.is_empty())
       _mesa_ast_to_hir(shader->ir, state);
@@ -2976,7 +3236,7 @@ _mesa_glsl_compile_shader(struct gl_context *ctx, struct gl_shader *shader)
    /* Retain any live IR, but trash the rest. */
    reparent_ir(shader->ir, shader->ir);
 
-   talloc_free(state);
+   ralloc_free(state);
 
    if (shader->CompileStatus) {
       if (!ctx->Driver.CompileShader(ctx, shader))
@@ -3007,6 +3267,7 @@ _mesa_glsl_link_shader(struct gl_context *ctx, struct gl_shader_program *prog)
    prog->Varying = _mesa_new_parameter_list();
    _mesa_reference_vertprog(ctx, &prog->VertexProgram, NULL);
    _mesa_reference_fragprog(ctx, &prog->FragmentProgram, NULL);
+   _mesa_reference_geomprog(ctx, &prog->GeometryProgram, NULL);
 
    if (prog->LinkStatus) {
       link_shaders(ctx, prog);