Merge remote branch 'origin/master' into glsl2
authorEric Anholt <eric@anholt.net>
Tue, 27 Jul 2010 00:47:59 +0000 (17:47 -0700)
committerEric Anholt <eric@anholt.net>
Tue, 27 Jul 2010 00:53:27 +0000 (17:53 -0700)
This pulls in multiple i965 driver fixes which will help ensure better
testing coverage during development, and also gets past the conflicts
of the src/mesa/shader -> src/mesa/program move.

Conflicts:
src/mesa/Makefile
src/mesa/main/shaderapi.c
src/mesa/main/shaderobj.h

25 files changed:
1  2 
configure.ac
src/glsl/Makefile
src/glsl/glsl_symbol_table.h
src/glsl/linker.cpp
src/glsl/program.h
src/mesa/Makefile
src/mesa/drivers/dri/i965/brw_vs_emit.c
src/mesa/drivers/dri/i965/brw_wm.h
src/mesa/drivers/dri/i965/brw_wm_emit.c
src/mesa/drivers/dri/i965/brw_wm_glsl.c
src/mesa/main/mtypes.h
src/mesa/main/shaderapi.c
src/mesa/main/shaderobj.c
src/mesa/main/shaderobj.h
src/mesa/program/hash_table.c
src/mesa/program/hash_table.h
src/mesa/program/ir_to_mesa.cpp
src/mesa/program/ir_to_mesa.h
src/mesa/program/prog_execute.c
src/mesa/program/prog_instruction.h
src/mesa/program/prog_optimize.c
src/mesa/program/prog_parameter.c
src/mesa/program/symbol_table.c
src/mesa/program/symbol_table.h
src/mesa/sources.mak

diff --cc configure.ac
Simple merge
@@@ -4,158 -4,12 +4,158 @@@ TOP = ../.
  
  include $(TOP)/configs/current
  
 -SUBDIRS = pp cl apps
 +LIBNAME = glsl
  
 -default install clean:
 -      @for dir in $(SUBDIRS) ; do \
 -              if [ -d $$dir ] ; then \
 -                      (cd $$dir && $(MAKE) $@) || exit 1; \
 -              fi \
 -      done
 +LIBGLCPP_SOURCES = \
 +      glcpp/glcpp-lex.c \
 +      glcpp/glcpp-parse.c \
 +      glcpp/pp.c \
 +      glcpp/xtalloc.c
  
-       ../mesa/shader/hash_table.c \
-       ../mesa/shader/symbol_table.c
 +GLCPP_SOURCES = \
 +      $(LIBGLCPP_SOURCES) \
 +      glcpp/glcpp.c
 +
 +C_SOURCES = \
 +      $(LIBGLCPP_SOURCES)
 +
 +CXX_SOURCES = \
 +      ast_expr.cpp \
 +      ast_function.cpp \
 +      ast_to_hir.cpp \
 +      ast_type.cpp \
 +      builtin_function.cpp \
 +      glsl_lexer.cpp \
 +      glsl_parser.cpp \
 +      glsl_parser_extras.cpp \
 +      glsl_types.cpp \
 +      hir_field_selection.cpp \
 +      ir_basic_block.cpp \
 +      ir_clone.cpp \
 +      ir_constant_expression.cpp \
 +      ir_constant_folding.cpp \
 +      ir_constant_variable.cpp \
 +      ir_copy_propagation.cpp \
 +      ir.cpp \
 +      ir_dead_code.cpp \
 +      ir_dead_code_local.cpp \
 +      ir_div_to_mul_rcp.cpp \
 +      ir_expression_flattening.cpp \
 +      ir_function_can_inline.cpp \
 +      ir_function.cpp \
 +      ir_function_inlining.cpp \
 +      ir_hierarchical_visitor.cpp \
 +      ir_hv_accept.cpp \
 +      ir_if_return.cpp \
 +      ir_if_simplification.cpp \
 +      ir_if_to_cond_assign.cpp \
 +      ir_import_prototypes.cpp \
 +      ir_mat_op_to_vec.cpp \
 +      ir_mod_to_fract.cpp \
 +      ir_print_visitor.cpp \
 +      ir_reader.cpp \
 +      ir_swizzle_swizzle.cpp \
 +      ir_validate.cpp \
 +      ir_variable.cpp \
 +      ir_vec_index_to_cond_assign.cpp \
 +      ir_vec_index_to_swizzle.cpp \
 +      linker.cpp \
 +      link_functions.cpp \
 +      s_expression.cpp
 +
 +LIBS = \
 +      $(TOP)/src/glsl/libglsl.a \
 +      $(shell pkg-config --libs talloc)
 +
 +APPS = glsl_compiler glcpp/glcpp
 +
 +GLSL2_C_SOURCES = \
-       ../mesa/shader/hash_table.o
++      ../mesa/program/hash_table.c \
++      ../mesa/program/symbol_table.c
 +GLSL2_CXX_SOURCES = \
 +      main.cpp
 +
 +GLSL2_OBJECTS = \
 +      $(GLSL2_C_SOURCES:.c=.o) \
 +      $(GLSL2_CXX_SOURCES:.cpp=.o)
 +
 +### Basic defines ###
 +
 +DEFINES = \
 +      $(LIBRARY_DEFINES) \
 +      $(API_DEFINES)
 +
 +GLCPP_OBJECTS = \
 +      $(GLCPP_SOURCES:.c=.o) \
-       -I../mesa/shader \
++      ../mesa/program/hash_table.o
 +
 +OBJECTS = \
 +      $(C_SOURCES:.c=.o) \
 +      $(CXX_SOURCES:.cpp=.o)
 +
 +INCLUDES = \
 +      -I. \
 +      -I../mesa \
 +      -I../mapi \
++      -I../mesa/program \
 +      -I../../include \
 +      $(LIBRARY_INCLUDES)
 +
 +ALL_SOURCES = \
 +      $(C_SOURCES) \
 +      $(CXX_SOURCES) \
 +      $(GLSL2_CXX_SOURCES) \
 +      $(GLSL2_C_SOURCES)
 +
 +##### TARGETS #####
 +
 +default: depend lib$(LIBNAME).a $(APPS)
 +
 +lib$(LIBNAME).a: $(OBJECTS) Makefile $(TOP)/src/glsl/Makefile.template
 +      $(MKLIB) -cplusplus -o $(LIBNAME) -static $(OBJECTS)
 +
 +depend: $(ALL_SOURCES) Makefile
 +      rm -f depend
 +      touch depend
 +      $(MKDEP) $(MKDEP_OPTIONS) $(INCLUDES) $(ALL_SOURCES) 2> /dev/null
 +
 +# Remove .o and backup files
 +clean:
 +      rm -f $(OBJECTS) lib$(LIBNAME).a depend depend.bak
 +      -rm -f $(APPS)
 +
 +# Dummy target
 +install:
 +      @echo -n ""
 +
 +
 +##### RULES #####
 +
 +glsl_compiler: $(GLSL2_OBJECTS) libglsl.a
 +      $(APP_CXX) $(INCLUDES) $(CFLAGS) $(LDFLAGS) $(GLSL2_OBJECTS) $(LIBS) -o $@
 +
 +glcpp/glcpp: $(GLCPP_OBJECTS) libglsl.a
 +      $(APP_CC) $(INCLUDES) $(CFLAGS) $(LDFLAGS) $(GLCPP_OBJECTS) $(LIBS) -o $@
 +
 +.cpp.o:
 +      $(CXX) -c $(INCLUDES) $(CXXFLAGS) $(DEFINES) $< -o $@
 +
 +.c.o:
 +      $(CC) -c $(INCLUDES) $(CFLAGS) $(DEFINES) $< -o $@
 +
 +glsl_lexer.cpp: glsl_lexer.lpp
 +      flex --never-interactive --outfile="$@"  $<
 +
 +glsl_parser.cpp: glsl_parser.ypp
 +      bison -v -o "$@" --defines=glsl_parser.h $<
 +
 +glcpp/glcpp-lex.c: glcpp/glcpp-lex.l
 +      flex --never-interactive --outfile="$@" $<
 +
 +glcpp/glcpp-parse.c: glcpp/glcpp-parse.y
 +      bison -v -o "$@" --defines=glcpp/glcpp-parse.h $<
 +
 +builtin_function.cpp: builtins/*/*
 +      ./builtins/tools/generate_builtins.pl > builtin_function.cpp
 +
 +-include depend
index 8fbc66c,0000000..27e8255
mode 100644,000000..100644
--- /dev/null
@@@ -1,165 -1,0 +1,165 @@@
- #include "symbol_table.h"
 +/* -*- c++ -*- */
 +/*
 + * Copyright © 2010 Intel Corporation
 + *
 + * Permission is hereby granted, free of charge, to any person obtaining a
 + * copy of this software and associated documentation files (the "Software"),
 + * to deal in the Software without restriction, including without limitation
 + * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 + * and/or sell copies of the Software, and to permit persons to whom the
 + * Software is furnished to do so, subject to the following conditions:
 + *
 + * The above copyright notice and this permission notice (including the next
 + * paragraph) shall be included in all copies or substantial portions of the
 + * Software.
 + *
 + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
 + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 + * DEALINGS IN THE SOFTWARE.
 + */
 +
 +#pragma once
 +#ifndef GLSL_SYMBOL_TABLE
 +#define GLSL_SYMBOL_TABLE
 +
 +#include <new>
 +
 +extern "C" {
++#include "program/symbol_table.h"
 +}
 +#include "ir.h"
 +#include "glsl_types.h"
 +
 +/**
 + * Facade class for _mesa_symbol_table
 + *
 + * Wraps the existing \c _mesa_symbol_table data structure to enforce some
 + * type safe and some symbol table invariants.
 + */
 +class glsl_symbol_table {
 +private:
 +   enum glsl_symbol_name_space {
 +      glsl_variable_name_space = 0,
 +      glsl_type_name_space = 1,
 +      glsl_function_name_space = 2
 +   };
 +
 +   static int
 +   _glsl_symbol_table_destructor (glsl_symbol_table *table)
 +   {
 +      table->~glsl_symbol_table();
 +
 +      return 0;
 +   }
 +
 +public:
 +   /* Callers of this talloc-based new need not call delete. It's
 +    * easier to just talloc_free 'ctx' (or any of its ancestors). */
 +   static void* operator new(size_t size, void *ctx)
 +   {
 +      void *table;
 +
 +      table = talloc_size(ctx, size);
 +      assert(table != NULL);
 +
 +      talloc_set_destructor(table, (int (*)(void*)) _glsl_symbol_table_destructor);
 +
 +      return table;
 +   }
 +
 +   /* If the user *does* call delete, that's OK, we will just
 +    * talloc_free in that case. Here, C++ will have already called the
 +    * destructor so tell talloc not to do that again. */
 +   static void operator delete(void *table)
 +   {
 +      talloc_set_destructor(table, NULL);
 +      talloc_free(table);
 +   }
 +   
 +   glsl_symbol_table()
 +   {
 +      table = _mesa_symbol_table_ctor();
 +   }
 +
 +   ~glsl_symbol_table()
 +   {
 +      _mesa_symbol_table_dtor(table);
 +   }
 +
 +   void push_scope()
 +   {
 +      _mesa_symbol_table_push_scope(table);
 +   }
 +
 +   void pop_scope()
 +   {
 +      _mesa_symbol_table_pop_scope(table);
 +   }
 +
 +   /**
 +    * Determine whether a name was declared at the current scope
 +    */
 +   bool name_declared_this_scope(const char *name)
 +   {
 +      return _mesa_symbol_table_symbol_scope(table, -1, name) == 0;
 +   }
 +
 +   /**
 +    * \name Methods to add symbols to the table
 +    *
 +    * There is some temptation to rename all these functions to \c add_symbol
 +    * or similar.  However, this breaks symmetry with the getter functions and
 +    * reduces the clarity of the intention of code that uses these methods.
 +    */
 +   /*@{*/
 +   bool add_variable(const char *name, ir_variable *v)
 +   {
 +      return _mesa_symbol_table_add_symbol(table, glsl_variable_name_space,
 +                                         name, v) == 0;
 +   }
 +
 +   bool add_type(const char *name, const glsl_type *t)
 +   {
 +      return _mesa_symbol_table_add_symbol(table, glsl_type_name_space,
 +                                         name, (void *) t) == 0;
 +   }
 +
 +   bool add_function(const char *name, ir_function *f)
 +   {
 +      return _mesa_symbol_table_add_symbol(table, glsl_function_name_space,
 +                                         name, f) == 0;
 +   }
 +   /*@}*/
 +
 +   /**
 +    * \name Methods to get symbols from the table
 +    */
 +   /*@{*/
 +   ir_variable *get_variable(const char *name)
 +   {
 +      return (ir_variable *)
 +       _mesa_symbol_table_find_symbol(table, glsl_variable_name_space, name);
 +   }
 +
 +   glsl_type *get_type(const char *name)
 +   {
 +      return (glsl_type *)
 +       _mesa_symbol_table_find_symbol(table, glsl_type_name_space, name);
 +   }
 +
 +   ir_function *get_function(const char *name)
 +   {
 +      return (ir_function *)
 +       _mesa_symbol_table_find_symbol(table, glsl_function_name_space, name);
 +   }
 +   /*@}*/
 +
 +private:
 +   struct _mesa_symbol_table *table;
 +};
 +
 +#endif /* GLSL_SYMBOL_TABLE */
index 7c30a40,0000000..ea0274e
mode 100644,000000..100644
--- /dev/null
@@@ -1,1305 -1,0 +1,1305 @@@
- #include "shader_api.h"
 +/*
 + * Copyright © 2010 Intel Corporation
 + *
 + * Permission is hereby granted, free of charge, to any person obtaining a
 + * copy of this software and associated documentation files (the "Software"),
 + * to deal in the Software without restriction, including without limitation
 + * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 + * and/or sell copies of the Software, and to permit persons to whom the
 + * Software is furnished to do so, subject to the following conditions:
 + *
 + * The above copyright notice and this permission notice (including the next
 + * paragraph) shall be included in all copies or substantial portions of the
 + * Software.
 + *
 + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
 + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 + * DEALINGS IN THE SOFTWARE.
 + */
 +
 +/**
 + * \file linker.cpp
 + * GLSL linker implementation
 + *
 + * Given a set of shaders that are to be linked to generate a final program,
 + * there are three distinct stages.
 + *
 + * In the first stage shaders are partitioned into groups based on the shader
 + * type.  All shaders of a particular type (e.g., vertex shaders) are linked
 + * together.
 + *
 + *   - Undefined references in each shader are resolve to definitions in
 + *     another shader.
 + *   - Types and qualifiers of uniforms, outputs, and global variables defined
 + *     in multiple shaders with the same name are verified to be the same.
 + *   - Initializers for uniforms and global variables defined
 + *     in multiple shaders with the same name are verified to be the same.
 + *
 + * The result, in the terminology of the GLSL spec, is a set of shader
 + * executables for each processing unit.
 + *
 + * After the first stage is complete, a series of semantic checks are performed
 + * on each of the shader executables.
 + *
 + *   - Each shader executable must define a \c main function.
 + *   - Each vertex shader executable must write to \c gl_Position.
 + *   - Each fragment shader executable must write to either \c gl_FragData or
 + *     \c gl_FragColor.
 + *
 + * In the final stage individual shader executables are linked to create a
 + * complete exectuable.
 + *
 + *   - Types of uniforms defined in multiple shader stages with the same name
 + *     are verified to be the same.
 + *   - Initializers for uniforms defined in multiple shader stages with the
 + *     same name are verified to be the same.
 + *   - Types and qualifiers of outputs defined in one stage are verified to
 + *     be the same as the types and qualifiers of inputs defined with the same
 + *     name in a later stage.
 + *
 + * \author Ian Romanick <ian.d.romanick@intel.com>
 + */
 +#include <cstdlib>
 +#include <cstdio>
 +#include <cstdarg>
 +#include <climits>
 +
 +extern "C" {
 +#include <talloc.h>
 +}
 +
 +#include "main/mtypes.h"
 +#include "main/macros.h"
++#include "main/shaderobj.h"
 +#include "glsl_symbol_table.h"
 +#include "ir.h"
 +#include "program.h"
 +#include "hash_table.h"
 +#include "linker.h"
 +#include "ir_optimization.h"
 +
 +/**
 + * Visitor that determines whether or not a variable is ever written.
 + */
 +class find_assignment_visitor : public ir_hierarchical_visitor {
 +public:
 +   find_assignment_visitor(const char *name)
 +      : name(name), found(false)
 +   {
 +      /* empty */
 +   }
 +
 +   virtual ir_visitor_status visit_enter(ir_assignment *ir)
 +   {
 +      ir_variable *const var = ir->lhs->variable_referenced();
 +
 +      if (strcmp(name, var->name) == 0) {
 +       found = true;
 +       return visit_stop;
 +      }
 +
 +      return visit_continue_with_parent;
 +   }
 +
 +   bool variable_found()
 +   {
 +      return found;
 +   }
 +
 +private:
 +   const char *name;       /**< Find writes to a variable with this name. */
 +   bool found;             /**< Was a write to the variable found? */
 +};
 +
 +
 +void
 +linker_error_printf(gl_shader_program *prog, const char *fmt, ...)
 +{
 +   va_list ap;
 +
 +   prog->InfoLog = talloc_strdup_append(prog->InfoLog, "error: ");
 +   va_start(ap, fmt);
 +   prog->InfoLog = talloc_vasprintf_append(prog->InfoLog, fmt, ap);
 +   va_end(ap);
 +}
 +
 +
 +void
 +invalidate_variable_locations(gl_shader *sh, enum ir_variable_mode mode,
 +                            int generic_base)
 +{
 +   foreach_list(node, sh->ir) {
 +      ir_variable *const var = ((ir_instruction *) node)->as_variable();
 +
 +      if ((var == NULL) || (var->mode != (unsigned) mode))
 +       continue;
 +
 +      /* Only assign locations for generic attributes / varyings / etc.
 +       */
 +      if (var->location >= generic_base)
 +        var->location = -1;
 +   }
 +}
 +
 +
 +/**
 + * Determine the number of attribute slots required for a particular type
 + *
 + * This code is here because it implements the language rules of a specific
 + * GLSL version.  Since it's a property of the language and not a property of
 + * types in general, it doesn't really belong in glsl_type.
 + */
 +unsigned
 +count_attribute_slots(const glsl_type *t)
 +{
 +   /* From page 31 (page 37 of the PDF) of the GLSL 1.50 spec:
 +    *
 +    *     "A scalar input counts the same amount against this limit as a vec4,
 +    *     so applications may want to consider packing groups of four
 +    *     unrelated float inputs together into a vector to better utilize the
 +    *     capabilities of the underlying hardware. A matrix input will use up
 +    *     multiple locations.  The number of locations used will equal the
 +    *     number of columns in the matrix."
 +    *
 +    * The spec does not explicitly say how arrays are counted.  However, it
 +    * should be safe to assume the total number of slots consumed by an array
 +    * is the number of entries in the array multiplied by the number of slots
 +    * consumed by a single element of the array.
 +    */
 +
 +   if (t->is_array())
 +      return t->array_size() * count_attribute_slots(t->element_type());
 +
 +   if (t->is_matrix())
 +      return t->matrix_columns;
 +
 +   return 1;
 +}
 +
 +
 +/**
 + * Verify that a vertex shader executable meets all semantic requirements
 + *
 + * \param shader  Vertex shader executable to be verified
 + */
 +bool
 +validate_vertex_shader_executable(struct gl_shader_program *prog,
 +                                struct gl_shader *shader)
 +{
 +   if (shader == NULL)
 +      return true;
 +
 +   find_assignment_visitor find("gl_Position");
 +   find.run(shader->ir);
 +   if (!find.variable_found()) {
 +      linker_error_printf(prog,
 +                        "vertex shader does not write to `gl_Position'\n");
 +      return false;
 +   }
 +
 +   return true;
 +}
 +
 +
 +/**
 + * Verify that a fragment shader executable meets all semantic requirements
 + *
 + * \param shader  Fragment shader executable to be verified
 + */
 +bool
 +validate_fragment_shader_executable(struct gl_shader_program *prog,
 +                                  struct gl_shader *shader)
 +{
 +   if (shader == NULL)
 +      return true;
 +
 +   find_assignment_visitor frag_color("gl_FragColor");
 +   find_assignment_visitor frag_data("gl_FragData");
 +
 +   frag_color.run(shader->ir);
 +   frag_data.run(shader->ir);
 +
 +   if (frag_color.variable_found() && frag_data.variable_found()) {
 +      linker_error_printf(prog,  "fragment shader writes to both "
 +                        "`gl_FragColor' and `gl_FragData'\n");
 +      return false;
 +   }
 +
 +   return true;
 +}
 +
 +
 +/**
 + * Generate a string describing the mode of a variable
 + */
 +static const char *
 +mode_string(const ir_variable *var)
 +{
 +   switch (var->mode) {
 +   case ir_var_auto:
 +      return (var->read_only) ? "global constant" : "global variable";
 +
 +   case ir_var_uniform: return "uniform";
 +   case ir_var_in:      return "shader input";
 +   case ir_var_out:     return "shader output";
 +   case ir_var_inout:   return "shader inout";
 +
 +   case ir_var_temporary:
 +   default:
 +      assert(!"Should not get here.");
 +      return "invalid variable";
 +   }
 +}
 +
 +
 +/**
 + * Perform validation of global variables used across multiple shaders
 + */
 +bool
 +cross_validate_globals(struct gl_shader_program *prog,
 +                     struct gl_shader **shader_list,
 +                     unsigned num_shaders,
 +                     bool uniforms_only)
 +{
 +   /* Examine all of the uniforms in all of the shaders and cross validate
 +    * them.
 +    */
 +   glsl_symbol_table variables;
 +   for (unsigned i = 0; i < num_shaders; i++) {
 +      foreach_list(node, shader_list[i]->ir) {
 +       ir_variable *const var = ((ir_instruction *) node)->as_variable();
 +
 +       if (var == NULL)
 +          continue;
 +
 +       if (uniforms_only && (var->mode != ir_var_uniform))
 +          continue;
 +
 +       /* Don't cross validate temporaries that are at global scope.  These
 +        * will eventually get pulled into the shaders 'main'.
 +        */
 +       if (var->mode == ir_var_temporary)
 +          continue;
 +
 +       /* If a global with this name has already been seen, verify that the
 +        * new instance has the same type.  In addition, if the globals have
 +        * initializers, the values of the initializers must be the same.
 +        */
 +       ir_variable *const existing = variables.get_variable(var->name);
 +       if (existing != NULL) {
 +          if (var->type != existing->type) {
 +             linker_error_printf(prog, "%s `%s' declared as type "
 +                                 "`%s' and type `%s'\n",
 +                                 mode_string(var),
 +                                 var->name, var->type->name,
 +                                 existing->type->name);
 +             return false;
 +          }
 +
 +          /* FINISHME: Handle non-constant initializers.
 +           */
 +          if (var->constant_value != NULL) {
 +             if (existing->constant_value != NULL) {
 +                if (!var->constant_value->has_value(existing->constant_value)) {
 +                   linker_error_printf(prog, "initializers for %s "
 +                                       "`%s' have differing values\n",
 +                                       mode_string(var), var->name);
 +                   return false;
 +                }
 +             } else
 +                /* If the first-seen instance of a particular uniform did not
 +                 * have an initializer but a later instance does, copy the
 +                 * initializer to the version stored in the symbol table.
 +                 */
 +                /* FINISHME: This is wrong.  The constant_value field should
 +                 * FINISHME: not be modified!  Imagine a case where a shader
 +                 * FINISHME: without an initializer is linked in two different
 +                 * FINISHME: programs with shaders that have differing
 +                 * FINISHME: initializers.  Linking with the first will
 +                 * FINISHME: modify the shader, and linking with the second
 +                 * FINISHME: will fail.
 +                 */
 +                existing->constant_value = var->constant_value->clone(NULL);
 +          }
 +       } else
 +          variables.add_variable(var->name, var);
 +      }
 +   }
 +
 +   return true;
 +}
 +
 +
 +/**
 + * Perform validation of uniforms used across multiple shader stages
 + */
 +bool
 +cross_validate_uniforms(struct gl_shader_program *prog)
 +{
 +   return cross_validate_globals(prog, prog->_LinkedShaders,
 +                               prog->_NumLinkedShaders, true);
 +}
 +
 +
 +/**
 + * Validate that outputs from one stage match inputs of another
 + */
 +bool
 +cross_validate_outputs_to_inputs(struct gl_shader_program *prog,
 +                               gl_shader *producer, gl_shader *consumer)
 +{
 +   glsl_symbol_table parameters;
 +   /* FINISHME: Figure these out dynamically. */
 +   const char *const producer_stage = "vertex";
 +   const char *const consumer_stage = "fragment";
 +
 +   /* Find all shader outputs in the "producer" stage.
 +    */
 +   foreach_list(node, producer->ir) {
 +      ir_variable *const var = ((ir_instruction *) node)->as_variable();
 +
 +      /* FINISHME: For geometry shaders, this should also look for inout
 +       * FINISHME: variables.
 +       */
 +      if ((var == NULL) || (var->mode != ir_var_out))
 +       continue;
 +
 +      parameters.add_variable(var->name, var);
 +   }
 +
 +
 +   /* Find all shader inputs in the "consumer" stage.  Any variables that have
 +    * matching outputs already in the symbol table must have the same type and
 +    * qualifiers.
 +    */
 +   foreach_list(node, consumer->ir) {
 +      ir_variable *const input = ((ir_instruction *) node)->as_variable();
 +
 +      /* FINISHME: For geometry shaders, this should also look for inout
 +       * FINISHME: variables.
 +       */
 +      if ((input == NULL) || (input->mode != ir_var_in))
 +       continue;
 +
 +      ir_variable *const output = parameters.get_variable(input->name);
 +      if (output != NULL) {
 +       /* Check that the types match between stages.
 +        */
 +       if (input->type != output->type) {
 +          linker_error_printf(prog,
 +                              "%s shader output `%s' delcared as "
 +                              "type `%s', but %s shader input declared "
 +                              "as type `%s'\n",
 +                              producer_stage, output->name,
 +                              output->type->name,
 +                              consumer_stage, input->type->name);
 +          return false;
 +       }
 +
 +       /* Check that all of the qualifiers match between stages.
 +        */
 +       if (input->centroid != output->centroid) {
 +          linker_error_printf(prog,
 +                              "%s shader output `%s' %s centroid qualifier, "
 +                              "but %s shader input %s centroid qualifier\n",
 +                              producer_stage,
 +                              output->name,
 +                              (output->centroid) ? "has" : "lacks",
 +                              consumer_stage,
 +                              (input->centroid) ? "has" : "lacks");
 +          return false;
 +       }
 +
 +       if (input->invariant != output->invariant) {
 +          linker_error_printf(prog,
 +                              "%s shader output `%s' %s invariant qualifier, "
 +                              "but %s shader input %s invariant qualifier\n",
 +                              producer_stage,
 +                              output->name,
 +                              (output->invariant) ? "has" : "lacks",
 +                              consumer_stage,
 +                              (input->invariant) ? "has" : "lacks");
 +          return false;
 +       }
 +
 +       if (input->interpolation != output->interpolation) {
 +          linker_error_printf(prog,
 +                              "%s shader output `%s' specifies %s "
 +                              "interpolation qualifier, "
 +                              "but %s shader input specifies %s "
 +                              "interpolation qualifier\n",
 +                              producer_stage,
 +                              output->name,
 +                              output->interpolation_string(),
 +                              consumer_stage,
 +                              input->interpolation_string());
 +          return false;
 +       }
 +      }
 +   }
 +
 +   return true;
 +}
 +
 +
 +/**
 + * Populates a shaders symbol table with all global declarations
 + */
 +static void
 +populate_symbol_table(gl_shader *sh)
 +{
 +   sh->symbols = new(sh) glsl_symbol_table;
 +
 +   foreach_list(node, sh->ir) {
 +      ir_instruction *const inst = (ir_instruction *) node;
 +      ir_variable *var;
 +      ir_function *func;
 +
 +      if ((func = inst->as_function()) != NULL) {
 +       sh->symbols->add_function(func->name, func);
 +      } else if ((var = inst->as_variable()) != NULL) {
 +       sh->symbols->add_variable(var->name, var);
 +      }
 +   }
 +}
 +
 +
 +/**
 + * Remap variables referenced in an instruction tree
 + *
 + * This is used when instruction trees are cloned from one shader and placed in
 + * another.  These trees will contain references to \c ir_variable nodes that
 + * do not exist in the target shader.  This function finds these \c ir_variable
 + * references and replaces the references with matching variables in the target
 + * shader.
 + *
 + * If there is no matching variable in the target shader, a clone of the
 + * \c ir_variable is made and added to the target shader.  The new variable is
 + * added to \b both the instruction stream and the symbol table.
 + *
 + * \param inst         IR tree that is to be processed.
 + * \param symbols      Symbol table containing global scope symbols in the
 + *                     linked shader.
 + * \param instructions Instruction stream where new variable declarations
 + *                     should be added.
 + */
 +void
 +remap_variables(ir_instruction *inst, glsl_symbol_table *symbols,
 +              exec_list *instructions, hash_table *temps)
 +{
 +   class remap_visitor : public ir_hierarchical_visitor {
 +   public:
 +      remap_visitor(glsl_symbol_table *symbols, exec_list *instructions,
 +                  hash_table *temps)
 +      {
 +       this->symbols = symbols;
 +       this->instructions = instructions;
 +       this->temps = temps;
 +      }
 +
 +      virtual ir_visitor_status visit(ir_dereference_variable *ir)
 +      {
 +       if (ir->var->mode == ir_var_temporary) {
 +          ir_variable *var = (ir_variable *) hash_table_find(temps, ir->var);
 +
 +          assert(var != NULL);
 +          ir->var = var;
 +          return visit_continue;
 +       }
 +
 +       ir_variable *const existing =
 +          this->symbols->get_variable(ir->var->name);
 +       if (existing != NULL)
 +          ir->var = existing;
 +       else {
 +          ir_variable *copy = ir->var->clone(NULL);
 +
 +          this->symbols->add_variable(copy->name, copy);
 +          this->instructions->push_head(copy);
 +          ir->var = copy;
 +       }
 +
 +       return visit_continue;
 +      }
 +
 +   private:
 +      glsl_symbol_table *symbols;
 +      exec_list *instructions;
 +      hash_table *temps;
 +   };
 +
 +   remap_visitor v(symbols, instructions, temps);
 +
 +   inst->accept(&v);
 +}
 +
 +
 +/**
 + * Move non-declarations from one instruction stream to another
 + *
 + * The intended usage pattern of this function is to pass the pointer to the
 + * head sentinal of a list (i.e., a pointer to the list cast to an \c exec_node
 + * pointer) for \c last and \c false for \c make_copies on the first
 + * call.  Successive calls pass the return value of the previous call for
 + * \c last and \c true for \c make_copies.
 + *
 + * \param instructions Source instruction stream
 + * \param last         Instruction after which new instructions should be
 + *                     inserted in the target instruction stream
 + * \param make_copies  Flag selecting whether instructions in \c instructions
 + *                     should be copied (via \c ir_instruction::clone) into the
 + *                     target list or moved.
 + *
 + * \return
 + * The new "last" instruction in the target instruction stream.  This pointer
 + * is suitable for use as the \c last parameter of a later call to this
 + * function.
 + */
 +exec_node *
 +move_non_declarations(exec_list *instructions, exec_node *last,
 +                    bool make_copies, gl_shader *target)
 +{
 +   hash_table *temps = NULL;
 +
 +   if (make_copies)
 +      temps = hash_table_ctor(0, hash_table_pointer_hash,
 +                            hash_table_pointer_compare);
 +
 +   foreach_list_safe(node, instructions) {
 +      ir_instruction *inst = (ir_instruction *) node;
 +
 +      if (inst->as_function())
 +       continue;
 +
 +      ir_variable *var = inst->as_variable();
 +      if ((var != NULL) && (var->mode != ir_var_temporary))
 +       continue;
 +
 +      assert(inst->as_assignment()
 +           || ((var != NULL) && (var->mode == ir_var_temporary)));
 +
 +      if (make_copies) {
 +       inst = inst->clone(NULL);
 +
 +       if (var != NULL)
 +          hash_table_insert(temps, inst, var);
 +       else
 +          remap_variables(inst, target->symbols, target->ir, temps);
 +      } else {
 +       inst->remove();
 +      }
 +
 +      last->insert_after(inst);
 +      last = inst;
 +   }
 +
 +   if (make_copies)
 +      hash_table_dtor(temps);
 +
 +   return last;
 +}
 +
 +/**
 + * Get the function signature for main from a shader
 + */
 +static ir_function_signature *
 +get_main_function_signature(gl_shader *sh)
 +{
 +   ir_function *const f = sh->symbols->get_function("main");
 +   if (f != NULL) {
 +      exec_list void_parameters;
 +
 +      /* Look for the 'void main()' signature and ensure that it's defined.
 +       * This keeps the linker from accidentally pick a shader that just
 +       * contains a prototype for main.
 +       *
 +       * We don't have to check for multiple definitions of main (in multiple
 +       * shaders) because that would have already been caught above.
 +       */
 +      ir_function_signature *sig = f->matching_signature(&void_parameters);
 +      if ((sig != NULL) && sig->is_defined) {
 +       return sig;
 +      }
 +   }
 +
 +   return NULL;
 +}
 +
 +
 +/**
 + * Combine a group of shaders for a single stage to generate a linked shader
 + *
 + * \note
 + * If this function is supplied a single shader, it is cloned, and the new
 + * shader is returned.
 + */
 +static struct gl_shader *
 +link_intrastage_shaders(struct gl_shader_program *prog,
 +                      struct gl_shader **shader_list,
 +                      unsigned num_shaders)
 +{
 +   /* Check that global variables defined in multiple shaders are consistent.
 +    */
 +   if (!cross_validate_globals(prog, shader_list, num_shaders, false))
 +      return NULL;
 +
 +   /* Check that there is only a single definition of each function signature
 +    * across all shaders.
 +    */
 +   for (unsigned i = 0; i < (num_shaders - 1); i++) {
 +      foreach_list(node, shader_list[i]->ir) {
 +       ir_function *const f = ((ir_instruction *) node)->as_function();
 +
 +       if (f == NULL)
 +          continue;
 +
 +       for (unsigned j = i + 1; j < num_shaders; j++) {
 +          ir_function *const other =
 +             shader_list[j]->symbols->get_function(f->name);
 +
 +          /* If the other shader has no function (and therefore no function
 +           * signatures) with the same name, skip to the next shader.
 +           */
 +          if (other == NULL)
 +             continue;
 +
 +          foreach_iter (exec_list_iterator, iter, *f) {
 +             ir_function_signature *sig =
 +                (ir_function_signature *) iter.get();
 +
 +             if (!sig->is_defined || sig->is_built_in)
 +                continue;
 +
 +             ir_function_signature *other_sig =
 +                other->exact_matching_signature(& sig->parameters);
 +
 +             if ((other_sig != NULL) && other_sig->is_defined
 +                 && !other_sig->is_built_in) {
 +                linker_error_printf(prog,
 +                                    "function `%s' is multiply defined",
 +                                    f->name);
 +                return NULL;
 +             }
 +          }
 +       }
 +      }
 +   }
 +
 +   /* Find the shader that defines main, and make a clone of it.
 +    *
 +    * Starting with the clone, search for undefined references.  If one is
 +    * found, find the shader that defines it.  Clone the reference and add
 +    * it to the shader.  Repeat until there are no undefined references or
 +    * until a reference cannot be resolved.
 +    */
 +   gl_shader *main = NULL;
 +   for (unsigned i = 0; i < num_shaders; i++) {
 +      if (get_main_function_signature(shader_list[i]) != NULL) {
 +       main = shader_list[i];
 +       break;
 +      }
 +   }
 +
 +   if (main == NULL) {
 +      linker_error_printf(prog, "%s shader lacks `main'\n",
 +                        (shader_list[0]->Type == GL_VERTEX_SHADER)
 +                        ? "vertex" : "fragment");
 +      return NULL;
 +   }
 +
 +   gl_shader *const linked = _mesa_new_shader(NULL, 0, main->Type);
 +   linked->ir = new(linked) exec_list;
 +   clone_ir_list(linked->ir, main->ir);
 +
 +   populate_symbol_table(linked);
 +
 +   /* The a pointer to the main function in the final linked shader (i.e., the
 +    * copy of the original shader that contained the main function).
 +    */
 +   ir_function_signature *const main_sig = get_main_function_signature(linked);
 +
 +   /* Move any instructions other than variable declarations or function
 +    * declarations into main.
 +    */
 +   exec_node *insertion_point =
 +      move_non_declarations(linked->ir, (exec_node *) &main_sig->body, false,
 +                          linked);
 +
 +   for (unsigned i = 0; i < num_shaders; i++) {
 +      if (shader_list[i] == main)
 +       continue;
 +
 +      insertion_point = move_non_declarations(shader_list[i]->ir,
 +                                            insertion_point, true, linked);
 +   }
 +
 +   /* Resolve initializers for global variables in the linked shader.
 +    */
 +   unsigned num_linking_shaders = num_shaders;
 +   for (unsigned i = 0; i < num_shaders; i++)
 +      num_linking_shaders += shader_list[i]->num_builtins_to_link;
 +
 +   gl_shader **linking_shaders =
 +      (gl_shader **) calloc(num_linking_shaders, sizeof(gl_shader *));
 +
 +   memcpy(linking_shaders, shader_list,
 +        sizeof(linking_shaders[0]) * num_shaders);
 +
 +   unsigned idx = num_shaders;
 +   for (unsigned i = 0; i < num_shaders; i++) {
 +      memcpy(&linking_shaders[idx], shader_list[i]->builtins_to_link,
 +           sizeof(linking_shaders[0]) * shader_list[i]->num_builtins_to_link);
 +      idx += shader_list[i]->num_builtins_to_link;
 +   }
 +
 +   assert(idx == num_linking_shaders);
 +
 +   link_function_calls(prog, linked, linking_shaders, num_linking_shaders);
 +
 +   free(linking_shaders);
 +
 +   return linked;
 +}
 +
 +
 +struct uniform_node {
 +   exec_node link;
 +   struct gl_uniform *u;
 +   unsigned slots;
 +};
 +
 +void
 +assign_uniform_locations(struct gl_shader_program *prog)
 +{
 +   /* */
 +   exec_list uniforms;
 +   unsigned total_uniforms = 0;
 +   hash_table *ht = hash_table_ctor(32, hash_table_string_hash,
 +                                  hash_table_string_compare);
 +
 +   for (unsigned i = 0; i < prog->_NumLinkedShaders; i++) {
 +      unsigned next_position = 0;
 +
 +      foreach_list(node, prog->_LinkedShaders[i]->ir) {
 +       ir_variable *const var = ((ir_instruction *) node)->as_variable();
 +
 +       if ((var == NULL) || (var->mode != ir_var_uniform))
 +          continue;
 +
 +       const unsigned vec4_slots = (var->component_slots() + 3) / 4;
 +       assert(vec4_slots != 0);
 +
 +       uniform_node *n = (uniform_node *) hash_table_find(ht, var->name);
 +       if (n == NULL) {
 +          n = (uniform_node *) calloc(1, sizeof(struct uniform_node));
 +          n->u = (gl_uniform *) calloc(vec4_slots, sizeof(struct gl_uniform));
 +          n->slots = vec4_slots;
 +
 +          n->u[0].Name = strdup(var->name);
 +          for (unsigned j = 1; j < vec4_slots; j++)
 +             n->u[j].Name = n->u[0].Name;
 +
 +          hash_table_insert(ht, n, n->u[0].Name);
 +          uniforms.push_tail(& n->link);
 +          total_uniforms += vec4_slots;
 +       }
 +
 +       if (var->constant_value != NULL)
 +          for (unsigned j = 0; j < vec4_slots; j++)
 +             n->u[j].Initialized = true;
 +
 +       var->location = next_position;
 +
 +       for (unsigned j = 0; j < vec4_slots; j++) {
 +          switch (prog->_LinkedShaders[i]->Type) {
 +          case GL_VERTEX_SHADER:
 +             n->u[j].VertPos = next_position;
 +             break;
 +          case GL_FRAGMENT_SHADER:
 +             n->u[j].FragPos = next_position;
 +             break;
 +          case GL_GEOMETRY_SHADER:
 +             /* FINISHME: Support geometry shaders. */
 +             assert(prog->_LinkedShaders[i]->Type != GL_GEOMETRY_SHADER);
 +             break;
 +          }
 +
 +          next_position++;
 +       }
 +      }
 +   }
 +
 +   gl_uniform_list *ul = (gl_uniform_list *)
 +      calloc(1, sizeof(gl_uniform_list));
 +
 +   ul->Size = total_uniforms;
 +   ul->NumUniforms = total_uniforms;
 +   ul->Uniforms = (gl_uniform *) calloc(total_uniforms, sizeof(gl_uniform));
 +
 +   unsigned idx = 0;
 +   uniform_node *next;
 +   for (uniform_node *node = (uniform_node *) uniforms.head
 +         ; node->link.next != NULL
 +         ; node = next) {
 +      next = (uniform_node *) node->link.next;
 +
 +      node->link.remove();
 +      memcpy(&ul->Uniforms[idx], node->u, sizeof(gl_uniform) * node->slots);
 +      idx += node->slots;
 +
 +      free(node->u);
 +      free(node);
 +   }
 +
 +   hash_table_dtor(ht);
 +
 +   prog->Uniforms = ul;
 +}
 +
 +
 +/**
 + * Find a contiguous set of available bits in a bitmask
 + *
 + * \param used_mask     Bits representing used (1) and unused (0) locations
 + * \param needed_count  Number of contiguous bits needed.
 + *
 + * \return
 + * Base location of the available bits on success or -1 on failure.
 + */
 +int
 +find_available_slots(unsigned used_mask, unsigned needed_count)
 +{
 +   unsigned needed_mask = (1 << needed_count) - 1;
 +   const int max_bit_to_test = (8 * sizeof(used_mask)) - needed_count;
 +
 +   /* The comparison to 32 is redundant, but without it GCC emits "warning:
 +    * cannot optimize possibly infinite loops" for the loop below.
 +    */
 +   if ((needed_count == 0) || (max_bit_to_test < 0) || (max_bit_to_test > 32))
 +      return -1;
 +
 +   for (int i = 0; i <= max_bit_to_test; i++) {
 +      if ((needed_mask & ~used_mask) == needed_mask)
 +       return i;
 +
 +      needed_mask <<= 1;
 +   }
 +
 +   return -1;
 +}
 +
 +
 +bool
 +assign_attribute_locations(gl_shader_program *prog, unsigned max_attribute_index)
 +{
 +   /* Mark invalid attribute locations as being used.
 +    */
 +   unsigned used_locations = (max_attribute_index >= 32)
 +      ? ~0 : ~((1 << max_attribute_index) - 1);
 +
 +   gl_shader *const sh = prog->_LinkedShaders[0];
 +   assert(sh->Type == GL_VERTEX_SHADER);
 +
 +   /* Operate in a total of four passes.
 +    *
 +    * 1. Invalidate the location assignments for all vertex shader inputs.
 +    *
 +    * 2. Assign locations for inputs that have user-defined (via
 +    *    glBindVertexAttribLocation) locatoins.
 +    *
 +    * 3. Sort the attributes without assigned locations by number of slots
 +    *    required in decreasing order.  Fragmentation caused by attribute
 +    *    locations assigned by the application may prevent large attributes
 +    *    from having enough contiguous space.
 +    *
 +    * 4. Assign locations to any inputs without assigned locations.
 +    */
 +
 +   invalidate_variable_locations(sh, ir_var_in, VERT_ATTRIB_GENERIC0);
 +
 +   if (prog->Attributes != NULL) {
 +      for (unsigned i = 0; i < prog->Attributes->NumParameters; i++) {
 +       ir_variable *const var =
 +          sh->symbols->get_variable(prog->Attributes->Parameters[i].Name);
 +
 +       /* Note: attributes that occupy multiple slots, such as arrays or
 +        * matrices, may appear in the attrib array multiple times.
 +        */
 +       if ((var == NULL) || (var->location != -1))
 +          continue;
 +
 +       /* From page 61 of the OpenGL 4.0 spec:
 +        *
 +        *     "LinkProgram will fail if the attribute bindings assigned by
 +        *     BindAttribLocation do not leave not enough space to assign a
 +        *     location for an active matrix attribute or an active attribute
 +        *     array, both of which require multiple contiguous generic
 +        *     attributes."
 +        *
 +        * Previous versions of the spec contain similar language but omit the
 +        * bit about attribute arrays.
 +        *
 +        * Page 61 of the OpenGL 4.0 spec also says:
 +        *
 +        *     "It is possible for an application to bind more than one
 +        *     attribute name to the same location. This is referred to as
 +        *     aliasing. This will only work if only one of the aliased
 +        *     attributes is active in the executable program, or if no path
 +        *     through the shader consumes more than one attribute of a set
 +        *     of attributes aliased to the same location. A link error can
 +        *     occur if the linker determines that every path through the
 +        *     shader consumes multiple aliased attributes, but
 +        *     implementations are not required to generate an error in this
 +        *     case."
 +        *
 +        * These two paragraphs are either somewhat contradictory, or I don't
 +        * fully understand one or both of them.
 +        */
 +       /* FINISHME: The code as currently written does not support attribute
 +        * FINISHME: location aliasing (see comment above).
 +        */
 +       const int attr = prog->Attributes->Parameters[i].StateIndexes[0];
 +       const unsigned slots = count_attribute_slots(var->type);
 +
 +       /* Mask representing the contiguous slots that will be used by this
 +        * attribute.
 +        */
 +       const unsigned use_mask = (1 << slots) - 1;
 +
 +       /* Generate a link error if the set of bits requested for this
 +        * attribute overlaps any previously allocated bits.
 +        */
 +       if ((~(use_mask << attr) & used_locations) != used_locations) {
 +          linker_error_printf(prog,
 +                              "insufficient contiguous attribute locations "
 +                              "available for vertex shader input `%s'",
 +                              var->name);
 +          return false;
 +       }
 +
 +       var->location = VERT_ATTRIB_GENERIC0 + attr;
 +       used_locations |= (use_mask << attr);
 +      }
 +   }
 +
 +   /* Temporary storage for the set of attributes that need locations assigned.
 +    */
 +   struct temp_attr {
 +      unsigned slots;
 +      ir_variable *var;
 +
 +      /* Used below in the call to qsort. */
 +      static int compare(const void *a, const void *b)
 +      {
 +       const temp_attr *const l = (const temp_attr *) a;
 +       const temp_attr *const r = (const temp_attr *) b;
 +
 +       /* Reversed because we want a descending order sort below. */
 +       return r->slots - l->slots;
 +      }
 +   } to_assign[16];
 +
 +   unsigned num_attr = 0;
 +
 +   foreach_list(node, sh->ir) {
 +      ir_variable *const var = ((ir_instruction *) node)->as_variable();
 +
 +      if ((var == NULL) || (var->mode != ir_var_in))
 +       continue;
 +
 +      /* The location was explicitly assigned, nothing to do here.
 +       */
 +      if (var->location != -1)
 +       continue;
 +
 +      to_assign[num_attr].slots = count_attribute_slots(var->type);
 +      to_assign[num_attr].var = var;
 +      num_attr++;
 +   }
 +
 +   /* If all of the attributes were assigned locations by the application (or
 +    * are built-in attributes with fixed locations), return early.  This should
 +    * be the common case.
 +    */
 +   if (num_attr == 0)
 +      return true;
 +
 +   qsort(to_assign, num_attr, sizeof(to_assign[0]), temp_attr::compare);
 +
 +   /* VERT_ATTRIB_GENERIC0 is a psdueo-alias for VERT_ATTRIB_POS.  It can only
 +    * be explicitly assigned by via glBindAttribLocation.  Mark it as reserved
 +    * to prevent it from being automatically allocated below.
 +    */
 +   used_locations |= (1 << 0);
 +
 +   for (unsigned i = 0; i < num_attr; i++) {
 +      /* Mask representing the contiguous slots that will be used by this
 +       * attribute.
 +       */
 +      const unsigned use_mask = (1 << to_assign[i].slots) - 1;
 +
 +      int location = find_available_slots(used_locations, to_assign[i].slots);
 +
 +      if (location < 0) {
 +       linker_error_printf(prog,
 +                           "insufficient contiguous attribute locations "
 +                           "available for vertex shader input `%s'",
 +                           to_assign[i].var->name);
 +       return false;
 +      }
 +
 +      to_assign[i].var->location = VERT_ATTRIB_GENERIC0 + location;
 +      used_locations |= (use_mask << location);
 +   }
 +
 +   return true;
 +}
 +
 +
 +void
 +assign_varying_locations(gl_shader *producer, gl_shader *consumer)
 +{
 +   /* FINISHME: Set dynamically when geometry shader support is added. */
 +   unsigned output_index = VERT_RESULT_VAR0;
 +   unsigned input_index = FRAG_ATTRIB_VAR0;
 +
 +   /* Operate in a total of three passes.
 +    *
 +    * 1. Assign locations for any matching inputs and outputs.
 +    *
 +    * 2. Mark output variables in the producer that do not have locations as
 +    *    not being outputs.  This lets the optimizer eliminate them.
 +    *
 +    * 3. Mark input variables in the consumer that do not have locations as
 +    *    not being inputs.  This lets the optimizer eliminate them.
 +    */
 +
 +   invalidate_variable_locations(producer, ir_var_out, VERT_RESULT_VAR0);
 +   invalidate_variable_locations(consumer, ir_var_in, FRAG_ATTRIB_VAR0);
 +
 +   foreach_list(node, producer->ir) {
 +      ir_variable *const output_var = ((ir_instruction *) node)->as_variable();
 +
 +      if ((output_var == NULL) || (output_var->mode != ir_var_out)
 +        || (output_var->location != -1))
 +       continue;
 +
 +      ir_variable *const input_var =
 +       consumer->symbols->get_variable(output_var->name);
 +
 +      if ((input_var == NULL) || (input_var->mode != ir_var_in))
 +       continue;
 +
 +      assert(input_var->location == -1);
 +
 +      /* FINISHME: Location assignment will need some changes when arrays,
 +       * FINISHME: matrices, and structures are allowed as shader inputs /
 +       * FINISHME: outputs.
 +       */
 +      output_var->location = output_index;
 +      input_var->location = input_index;
 +
 +      output_index++;
 +      input_index++;
 +   }
 +
 +   foreach_list(node, producer->ir) {
 +      ir_variable *const var = ((ir_instruction *) node)->as_variable();
 +
 +      if ((var == NULL) || (var->mode != ir_var_out))
 +       continue;
 +
 +      /* An 'out' variable is only really a shader output if its value is read
 +       * by the following stage.
 +       */
 +      if (var->location == -1) {
 +       var->shader_out = false;
 +       var->mode = ir_var_auto;
 +      }
 +   }
 +
 +   foreach_list(node, consumer->ir) {
 +      ir_variable *const var = ((ir_instruction *) node)->as_variable();
 +
 +      if ((var == NULL) || (var->mode != ir_var_in))
 +       continue;
 +
 +      /* An 'in' variable is only really a shader input if its value is written
 +       * by the previous stage.
 +       */
 +      var->shader_in = (var->location != -1);
 +   }
 +}
 +
 +
 +void
 +link_shaders(struct gl_shader_program *prog)
 +{
 +   prog->LinkStatus = false;
 +   prog->Validated = false;
 +   prog->_Used = false;
 +
 +   if (prog->InfoLog != NULL)
 +      talloc_free(prog->InfoLog);
 +
 +   prog->InfoLog = talloc_strdup(NULL, "");
 +
 +   /* Separate the shaders into groups based on their type.
 +    */
 +   struct gl_shader **vert_shader_list;
 +   unsigned num_vert_shaders = 0;
 +   struct gl_shader **frag_shader_list;
 +   unsigned num_frag_shaders = 0;
 +
 +   vert_shader_list = (struct gl_shader **)
 +      calloc(2 * prog->NumShaders, sizeof(struct gl_shader *));
 +   frag_shader_list =  &vert_shader_list[prog->NumShaders];
 +
 +   unsigned min_version = UINT_MAX;
 +   unsigned max_version = 0;
 +   for (unsigned i = 0; i < prog->NumShaders; i++) {
 +      min_version = MIN2(min_version, prog->Shaders[i]->Version);
 +      max_version = MAX2(max_version, prog->Shaders[i]->Version);
 +
 +      switch (prog->Shaders[i]->Type) {
 +      case GL_VERTEX_SHADER:
 +       vert_shader_list[num_vert_shaders] = prog->Shaders[i];
 +       num_vert_shaders++;
 +       break;
 +      case GL_FRAGMENT_SHADER:
 +       frag_shader_list[num_frag_shaders] = prog->Shaders[i];
 +       num_frag_shaders++;
 +       break;
 +      case GL_GEOMETRY_SHADER:
 +       /* FINISHME: Support geometry shaders. */
 +       assert(prog->Shaders[i]->Type != GL_GEOMETRY_SHADER);
 +       break;
 +      }
 +   }
 +
 +   /* Previous to GLSL version 1.30, different compilation units could mix and
 +    * match shading language versions.  With GLSL 1.30 and later, the versions
 +    * of all shaders must match.
 +    */
 +   assert(min_version >= 110);
 +   assert(max_version <= 130);
 +   if ((max_version >= 130) && (min_version != max_version)) {
 +      linker_error_printf(prog, "all shaders must use same shading "
 +                        "language version\n");
 +      goto done;
 +   }
 +
 +   prog->Version = max_version;
 +
 +   /* Link all shaders for a particular stage and validate the result.
 +    */
 +   prog->_NumLinkedShaders = 0;
 +   if (num_vert_shaders > 0) {
 +      gl_shader *const sh =
 +       link_intrastage_shaders(prog, vert_shader_list, num_vert_shaders);
 +
 +      if (sh == NULL)
 +       goto done;
 +
 +      if (!validate_vertex_shader_executable(prog, sh))
 +        goto done;
 +
 +      prog->_LinkedShaders[prog->_NumLinkedShaders] = sh;
 +      prog->_NumLinkedShaders++;
 +   }
 +
 +   if (num_frag_shaders > 0) {
 +      gl_shader *const sh =
 +       link_intrastage_shaders(prog, frag_shader_list, num_frag_shaders);
 +
 +      if (sh == NULL)
 +       goto done;
 +
 +      if (!validate_fragment_shader_executable(prog, sh))
 +        goto done;
 +
 +      prog->_LinkedShaders[prog->_NumLinkedShaders] = sh;
 +      prog->_NumLinkedShaders++;
 +   }
 +
 +   /* Here begins the inter-stage linking phase.  Some initial validation is
 +    * performed, then locations are assigned for uniforms, attributes, and
 +    * varyings.
 +    */
 +   if (cross_validate_uniforms(prog)) {
 +      /* Validate the inputs of each stage with the output of the preceeding
 +       * stage.
 +       */
 +      for (unsigned i = 1; i < prog->_NumLinkedShaders; i++) {
 +       if (!cross_validate_outputs_to_inputs(prog,
 +                                             prog->_LinkedShaders[i - 1],
 +                                             prog->_LinkedShaders[i]))
 +          goto done;
 +      }
 +
 +      prog->LinkStatus = true;
 +   }
 +
 +   /* FINISHME: Perform whole-program optimization here. */
 +   for (unsigned i = 0; i < prog->_NumLinkedShaders; i++) {
 +      /* Optimization passes */
 +      bool progress;
 +      exec_list *ir = prog->_LinkedShaders[i]->ir;
 +
 +      /* Lowering */
 +      do_mat_op_to_vec(ir);
 +      do_mod_to_fract(ir);
 +      do_div_to_mul_rcp(ir);
 +
 +      do {
 +       progress = false;
 +
 +       progress = do_function_inlining(ir) || progress;
 +       progress = do_if_simplification(ir) || progress;
 +       progress = do_copy_propagation(ir) || progress;
 +       progress = do_dead_code_local(ir) || progress;
 +#if 0
 +       progress = do_dead_code_unlinked(state, ir) || progress;
 +#endif
 +       progress = do_constant_variable_unlinked(ir) || progress;
 +       progress = do_constant_folding(ir) || progress;
 +       progress = do_if_return(ir) || progress;
 +#if 0
 +       if (ctx->Shader.EmitNoIfs)
 +          progress = do_if_to_cond_assign(ir) || progress;
 +#endif
 +
 +       progress = do_vec_index_to_swizzle(ir) || progress;
 +       /* Do this one after the previous to let the easier pass handle
 +        * constant vector indexing.
 +        */
 +       progress = do_vec_index_to_cond_assign(ir) || progress;
 +
 +       progress = do_swizzle_swizzle(ir) || progress;
 +      } while (progress);
 +   }
 +
 +   assign_uniform_locations(prog);
 +
 +   if (prog->_LinkedShaders[0]->Type == GL_VERTEX_SHADER)
 +      /* FINISHME: The value of the max_attribute_index parameter is
 +       * FINISHME: implementation dependent based on the value of
 +       * FINISHME: GL_MAX_VERTEX_ATTRIBS.  GL_MAX_VERTEX_ATTRIBS must be
 +       * FINISHME: at least 16, so hardcode 16 for now.
 +       */
 +      if (!assign_attribute_locations(prog, 16))
 +       goto done;
 +
 +   for (unsigned i = 1; i < prog->_NumLinkedShaders; i++)
 +      assign_varying_locations(prog->_LinkedShaders[i - 1],
 +                             prog->_LinkedShaders[i]);
 +
 +   /* FINISHME: Assign fragment shader output locations. */
 +
 +done:
 +   free(vert_shader_list);
 +}
index bb1cd91,0000000..0a49203
mode 100644,000000..100644
--- /dev/null
@@@ -1,33 -1,0 +1,33 @@@
- #include "shader/prog_parameter.h"
- #include "shader/prog_uniform.h"
 +/*
 + * Copyright (C) 1999-2008  Brian Paul   All Rights Reserved.
 + * Copyright (C) 2009  VMware, Inc.  All Rights Reserved.
 + * Copyright © 2010 Intel Corporation
 + *
 + * Permission is hereby granted, free of charge, to any person obtaining a
 + * copy of this software and associated documentation files (the "Software"),
 + * to deal in the Software without restriction, including without limitation
 + * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 + * and/or sell copies of the Software, and to permit persons to whom the
 + * Software is furnished to do so, subject to the following conditions:
 + *
 + * The above copyright notice and this permission notice shall be included
 + * in all copies or substantial portions of the Software.
 + *
 + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
 + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
 + * BRIAN PAUL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
 + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 + */
 +
 +#include <GL/gl.h>
 +#include "main/mtypes.h"
 +
 +extern "C" {
++#include "program/prog_parameter.h"
++#include "program/prog_uniform.h"
 +}
 +
 +extern void
 +link_shaders(struct gl_shader_program *prog);
Simple merge
Simple merge
Simple merge
  
  #include "main/glheader.h"
  #include "main/context.h"
+ #include "main/dispatch.h"
+ #include "main/enums.h"
  #include "main/hash.h"
- #include "shader/program.h"
- #include "shader/prog_parameter.h"
- #include "shader/prog_uniform.h"
- #include "shader/shader_api.h"
- #include "shader/uniforms.h"
+ #include "main/shaderapi.h"
+ #include "main/shaderobj.h"
+ #include "program/program.h"
+ #include "program/prog_parameter.h"
+ #include "program/prog_uniform.h"
+ #include "slang/slang_compile.h"
+ #include "slang/slang_link.h"
 +#include "talloc.h"
  
- /**
-  * Allocate a new gl_shader_program object, initialize it.
-  */
- static struct gl_shader_program *
- _mesa_new_shader_program(GLcontext *ctx, GLuint name)
- {
-    struct gl_shader_program *shProg;
-    shProg = talloc_zero(NULL, struct gl_shader_program);
-    if (shProg) {
-       shProg->Type = GL_SHADER_PROGRAM_MESA;
-       shProg->Name = name;
-       shProg->RefCount = 1;
-       shProg->Attributes = _mesa_new_parameter_list();
-    }
-    return shProg;
- }
- /**
-  * Clear (free) the shader program state that gets produced by linking.
-  */
- void
- _mesa_clear_shader_program_data(GLcontext *ctx,
-                                 struct gl_shader_program *shProg)
- {
-    _mesa_reference_vertprog(ctx, &shProg->VertexProgram, NULL);
-    _mesa_reference_fragprog(ctx, &shProg->FragmentProgram, NULL);
-    if (shProg->Uniforms) {
-       _mesa_free_uniform_list(shProg->Uniforms);
-       shProg->Uniforms = NULL;
-    }
-    if (shProg->Varying) {
-       _mesa_free_parameter_list(shProg->Varying);
-       shProg->Varying = NULL;
-    }
- }
- /**
-  * Free all the data that hangs off a shader program object, but not the
-  * object itself.
-  */
- void
- _mesa_free_shader_program_data(GLcontext *ctx,
-                                struct gl_shader_program *shProg)
- {
-    GLuint i;
-    assert(shProg->Type == GL_SHADER_PROGRAM_MESA);
-    _mesa_clear_shader_program_data(ctx, shProg);
-    if (shProg->Attributes) {
-       _mesa_free_parameter_list(shProg->Attributes);
-       shProg->Attributes = NULL;
-    }
-    /* detach shaders */
-    for (i = 0; i < shProg->NumShaders; i++) {
-       _mesa_reference_shader(ctx, &shProg->Shaders[i], NULL);
-    }
-    shProg->NumShaders = 0;
-    if (shProg->Shaders) {
-       free(shProg->Shaders);
-       shProg->Shaders = NULL;
-    }
-    if (shProg->InfoLog) {
-       talloc_free(shProg->InfoLog);
-       shProg->InfoLog = NULL;
-    }
-    /* Transform feedback varying vars */
-    for (i = 0; i < shProg->TransformFeedback.NumVarying; i++) {
-       free(shProg->TransformFeedback.VaryingNames[i]);
-    }
-    free(shProg->TransformFeedback.VaryingNames);
-    shProg->TransformFeedback.VaryingNames = NULL;
-    shProg->TransformFeedback.NumVarying = 0;
- }
- /**
-  * Free/delete a shader program object.
-  */
- void
- _mesa_free_shader_program(GLcontext *ctx, struct gl_shader_program *shProg)
- {
-    _mesa_free_shader_program_data(ctx, shProg);
-    talloc_free(shProg);
- }
- /**
-  * Set ptr to point to shProg.
-  * If ptr is pointing to another object, decrement its refcount (and delete
-  * if refcount hits zero).
-  * Then set ptr to point to shProg, incrementing its refcount.
-  */
- /* XXX this could be static */
- void
- _mesa_reference_shader_program(GLcontext *ctx,
-                                struct gl_shader_program **ptr,
-                                struct gl_shader_program *shProg)
- {
-    assert(ptr);
-    if (*ptr == shProg) {
-       /* no-op */
-       return;
-    }
-    if (*ptr) {
-       /* Unreference the old shader program */
-       GLboolean deleteFlag = GL_FALSE;
-       struct gl_shader_program *old = *ptr;
-       ASSERT(old->RefCount > 0);
-       old->RefCount--;
- #if 0
-       printf("ShaderProgram %p ID=%u  RefCount-- to %d\n",
-              (void *) old, old->Name, old->RefCount);
- #endif
-       deleteFlag = (old->RefCount == 0);
-       if (deleteFlag) {
-          _mesa_HashRemove(ctx->Shared->ShaderObjects, old->Name);
-          _mesa_free_shader_program(ctx, old);
-       }
-       *ptr = NULL;
-    }
-    assert(!*ptr);
-    if (shProg) {
-       shProg->RefCount++;
- #if 0
-       printf("ShaderProgram %p ID=%u  RefCount++ to %d\n",
-              (void *) shProg, shProg->Name, shProg->RefCount);
- #endif
-       *ptr = shProg;
-    }
- }
- /**
-  * Lookup a GLSL program object.
-  */
- struct gl_shader_program *
- _mesa_lookup_shader_program(GLcontext *ctx, GLuint name)
- {
-    struct gl_shader_program *shProg;
-    if (name) {
-       shProg = (struct gl_shader_program *)
-          _mesa_HashLookup(ctx->Shared->ShaderObjects, name);
-       /* Note that both gl_shader and gl_shader_program objects are kept
-        * in the same hash table.  Check the object's type to be sure it's
-        * what we're expecting.
-        */
-       if (shProg && shProg->Type != GL_SHADER_PROGRAM_MESA) {
-          return NULL;
-       }
-       return shProg;
-    }
-    return NULL;
- }
- /**
-  * As above, but record an error if program is not found.
-  */
- struct gl_shader_program *
- _mesa_lookup_shader_program_err(GLcontext *ctx, GLuint name,
-                                 const char *caller)
- {
-    if (!name) {
-       _mesa_error(ctx, GL_INVALID_VALUE, caller);
-       return NULL;
-    }
-    else {
-       struct gl_shader_program *shProg = (struct gl_shader_program *)
-          _mesa_HashLookup(ctx->Shared->ShaderObjects, name);
-       if (!shProg) {
-          _mesa_error(ctx, GL_INVALID_VALUE, caller);
-          return NULL;
-       }
-       if (shProg->Type != GL_SHADER_PROGRAM_MESA) {
-          _mesa_error(ctx, GL_INVALID_OPERATION, caller);
-          return NULL;
-       }
-       return shProg;
-    }
- }
- /**
-  * Allocate a new gl_shader object, initialize it.
-  */
- struct gl_shader *
- _mesa_new_shader(GLcontext *ctx, GLuint name, GLenum type)
- {
-    struct gl_shader *shader;
-    assert(type == GL_FRAGMENT_SHADER || type == GL_VERTEX_SHADER);
-    shader = talloc_zero(NULL, struct gl_shader);
-    if (shader) {
-       shader->Type = type;
-       shader->Name = name;
-       shader->RefCount = 1;
-    }
-    return shader;
- }
- void
- _mesa_free_shader(GLcontext *ctx, struct gl_shader *sh)
- {
-    if (sh->Source)
-       free((void *) sh->Source);
-    _mesa_reference_program(ctx, &sh->Program, NULL);
-    talloc_free(sh);
- }
- /**
-  * Set ptr to point to sh.
-  * If ptr is pointing to another shader, decrement its refcount (and delete
-  * if refcount hits zero).
-  * Then set ptr to point to sh, incrementing its refcount.
-  */
- /* XXX this could be static */
- void
- _mesa_reference_shader(GLcontext *ctx, struct gl_shader **ptr,
-                        struct gl_shader *sh)
- {
-    assert(ptr);
-    if (*ptr == sh) {
-       /* no-op */
-       return;
-    }
-    if (*ptr) {
-       /* Unreference the old shader */
-       GLboolean deleteFlag = GL_FALSE;
-       struct gl_shader *old = *ptr;
-       ASSERT(old->RefCount > 0);
-       old->RefCount--;
-       /*printf("SHADER DECR %p (%d) to %d\n",
-         (void*) old, old->Name, old->RefCount);*/
-       deleteFlag = (old->RefCount == 0);
-       if (deleteFlag) {
-          _mesa_HashRemove(ctx->Shared->ShaderObjects, old->Name);
-          _mesa_free_shader(ctx, old);
-       }
-       *ptr = NULL;
-    }
-    assert(!*ptr);
-    if (sh) {
-       /* reference new */
-       sh->RefCount++;
-       /*printf("SHADER INCR %p (%d) to %d\n",
-         (void*) sh, sh->Name, sh->RefCount);*/
-       *ptr = sh;
-    }
- }
- /**
-  * Lookup a GLSL shader object.
-  */
- struct gl_shader *
- _mesa_lookup_shader(GLcontext *ctx, GLuint name)
- {
-    if (name) {
-       struct gl_shader *sh = (struct gl_shader *)
-          _mesa_HashLookup(ctx->Shared->ShaderObjects, name);
-       /* Note that both gl_shader and gl_shader_program objects are kept
-        * in the same hash table.  Check the object's type to be sure it's
-        * what we're expecting.
-        */
-       if (sh && sh->Type == GL_SHADER_PROGRAM_MESA) {
-          return NULL;
-       }
-       return sh;
-    }
-    return NULL;
- }
  
- /**
-  * As above, but record an error if shader is not found.
-  */
- static struct gl_shader *
- _mesa_lookup_shader_err(GLcontext *ctx, GLuint name, const char *caller)
- {
-    if (!name) {
-       _mesa_error(ctx, GL_INVALID_VALUE, caller);
-       return NULL;
-    }
-    else {
-       struct gl_shader *sh = (struct gl_shader *)
-          _mesa_HashLookup(ctx->Shared->ShaderObjects, name);
-       if (!sh) {
-          _mesa_error(ctx, GL_INVALID_VALUE, caller);
-          return NULL;
-       }
-       if (sh->Type == GL_SHADER_PROGRAM_MESA) {
-          _mesa_error(ctx, GL_INVALID_OPERATION, caller);
-          return NULL;
-       }
-       return sh;
-    }
- }
+ /** Define this to enable shader substitution (see below) */
+ #define SHADER_SUBST 0
  
  
  /**
index 0000000,14bbb2e..129d974
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,392 +1,390 @@@
 -
+ /*
+  * Mesa 3-D graphics library
+  *
+  * Copyright (C) 2004-2008  Brian Paul   All Rights Reserved.
+  * Copyright (C) 2009-2010  VMware, Inc.  All Rights Reserved.
+  *
+  * Permission is hereby granted, free of charge, to any person obtaining a
+  * copy of this software and associated documentation files (the "Software"),
+  * to deal in the Software without restriction, including without limitation
+  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+  * and/or sell copies of the Software, and to permit persons to whom the
+  * Software is furnished to do so, subject to the following conditions:
+  *
+  * The above copyright notice and this permission notice shall be included
+  * in all copies or substantial portions of the Software.
+  *
+  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+  * BRIAN PAUL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+  * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+  */
+ /**
+  * \file shaderobj.c
+  * \author Brian Paul
+  *
+  */
+ #include "main/glheader.h"
+ #include "main/context.h"
+ #include "main/hash.h"
+ #include "main/shaderobj.h"
+ #include "program/program.h"
+ #include "program/prog_parameter.h"
+ #include "program/prog_uniform.h"
 -static struct gl_shader *
++#include "talloc.h"
+ /**********************************************************************/
+ /*** Shader object functions                                        ***/
+ /**********************************************************************/
+ /**
+  * Set ptr to point to sh.
+  * If ptr is pointing to another shader, decrement its refcount (and delete
+  * if refcount hits zero).
+  * Then set ptr to point to sh, incrementing its refcount.
+  */
+ void
+ _mesa_reference_shader(GLcontext *ctx, struct gl_shader **ptr,
+                        struct gl_shader *sh)
+ {
+    assert(ptr);
+    if (*ptr == sh) {
+       /* no-op */
+       return;
+    }
+    if (*ptr) {
+       /* Unreference the old shader */
+       GLboolean deleteFlag = GL_FALSE;
+       struct gl_shader *old = *ptr;
+       ASSERT(old->RefCount > 0);
+       old->RefCount--;
+       /*printf("SHADER DECR %p (%d) to %d\n",
+         (void*) old, old->Name, old->RefCount);*/
+       deleteFlag = (old->RefCount == 0);
+       if (deleteFlag) {
+          _mesa_HashRemove(ctx->Shared->ShaderObjects, old->Name);
+          ctx->Driver.DeleteShader(ctx, old);
+       }
+       *ptr = NULL;
+    }
+    assert(!*ptr);
+    if (sh) {
+       /* reference new */
+       sh->RefCount++;
+       /*printf("SHADER INCR %p (%d) to %d\n",
+         (void*) sh, sh->Name, sh->RefCount);*/
+       *ptr = sh;
+    }
+ }
+ /**
+  * Allocate a new gl_shader object, initialize it.
+  * Called via ctx->Driver.NewShader()
+  */
 -   shader = CALLOC_STRUCT(gl_shader);
++struct gl_shader *
+ _mesa_new_shader(GLcontext *ctx, GLuint name, GLenum type)
+ {
+    struct gl_shader *shader;
+    assert(type == GL_FRAGMENT_SHADER || type == GL_VERTEX_SHADER ||
+           type == GL_GEOMETRY_SHADER_ARB);
 -   if (sh->InfoLog)
 -      free(sh->InfoLog);
++   shader = talloc_zero(NULL, struct gl_shader);
+    if (shader) {
+       shader->Type = type;
+       shader->Name = name;
+       shader->RefCount = 1;
+    }
+    return shader;
+ }
+ /**
+  * Delete a shader object.
+  * Called via ctx->Driver.DeleteShader().
+  */
+ static void
+ __mesa_delete_shader(GLcontext *ctx, struct gl_shader *sh)
+ {
+    if (sh->Source)
+       free((void *) sh->Source);
 -   free(sh);
+    _mesa_reference_program(ctx, &sh->Program, NULL);
 -   shProg = CALLOC_STRUCT(gl_shader_program);
++   talloc_free(sh);
+ }
+ /**
+  * Lookup a GLSL shader object.
+  */
+ struct gl_shader *
+ _mesa_lookup_shader(GLcontext *ctx, GLuint name)
+ {
+    if (name) {
+       struct gl_shader *sh = (struct gl_shader *)
+          _mesa_HashLookup(ctx->Shared->ShaderObjects, name);
+       /* Note that both gl_shader and gl_shader_program objects are kept
+        * in the same hash table.  Check the object's type to be sure it's
+        * what we're expecting.
+        */
+       if (sh && sh->Type == GL_SHADER_PROGRAM_MESA) {
+          return NULL;
+       }
+       return sh;
+    }
+    return NULL;
+ }
+ /**
+  * As above, but record an error if shader is not found.
+  */
+ struct gl_shader *
+ _mesa_lookup_shader_err(GLcontext *ctx, GLuint name, const char *caller)
+ {
+    if (!name) {
+       _mesa_error(ctx, GL_INVALID_VALUE, caller);
+       return NULL;
+    }
+    else {
+       struct gl_shader *sh = (struct gl_shader *)
+          _mesa_HashLookup(ctx->Shared->ShaderObjects, name);
+       if (!sh) {
+          _mesa_error(ctx, GL_INVALID_VALUE, caller);
+          return NULL;
+       }
+       if (sh->Type == GL_SHADER_PROGRAM_MESA) {
+          _mesa_error(ctx, GL_INVALID_OPERATION, caller);
+          return NULL;
+       }
+       return sh;
+    }
+ }
+ /**********************************************************************/
+ /*** Shader Program object functions                                ***/
+ /**********************************************************************/
+ /**
+  * Set ptr to point to shProg.
+  * If ptr is pointing to another object, decrement its refcount (and delete
+  * if refcount hits zero).
+  * Then set ptr to point to shProg, incrementing its refcount.
+  */
+ void
+ _mesa_reference_shader_program(GLcontext *ctx,
+                                struct gl_shader_program **ptr,
+                                struct gl_shader_program *shProg)
+ {
+    assert(ptr);
+    if (*ptr == shProg) {
+       /* no-op */
+       return;
+    }
+    if (*ptr) {
+       /* Unreference the old shader program */
+       GLboolean deleteFlag = GL_FALSE;
+       struct gl_shader_program *old = *ptr;
+       ASSERT(old->RefCount > 0);
+       old->RefCount--;
+ #if 0
+       printf("ShaderProgram %p ID=%u  RefCount-- to %d\n",
+              (void *) old, old->Name, old->RefCount);
+ #endif
+       deleteFlag = (old->RefCount == 0);
+       if (deleteFlag) {
+          _mesa_HashRemove(ctx->Shared->ShaderObjects, old->Name);
+          ctx->Driver.DeleteShaderProgram(ctx, old);
+       }
+       *ptr = NULL;
+    }
+    assert(!*ptr);
+    if (shProg) {
+       shProg->RefCount++;
+ #if 0
+       printf("ShaderProgram %p ID=%u  RefCount++ to %d\n",
+              (void *) shProg, shProg->Name, shProg->RefCount);
+ #endif
+       *ptr = shProg;
+    }
+ }
+ /**
+  * Allocate a new gl_shader_program object, initialize it.
+  * Called via ctx->Driver.NewShaderProgram()
+  */
+ static struct gl_shader_program *
+ _mesa_new_shader_program(GLcontext *ctx, GLuint name)
+ {
+    struct gl_shader_program *shProg;
 -      free(shProg->InfoLog);
++   shProg = talloc_zero(NULL, struct gl_shader_program);
+    if (shProg) {
+       shProg->Type = GL_SHADER_PROGRAM_MESA;
+       shProg->Name = name;
+       shProg->RefCount = 1;
+       shProg->Attributes = _mesa_new_parameter_list();
+ #if FEATURE_ARB_geometry_shader4
+       shProg->Geom.VerticesOut = 0;
+       shProg->Geom.InputType = GL_TRIANGLES;
+       shProg->Geom.OutputType = GL_TRIANGLE_STRIP;
+ #endif
+    }
+    return shProg;
+ }
+ /**
+  * Clear (free) the shader program state that gets produced by linking.
+  */
+ void
+ _mesa_clear_shader_program_data(GLcontext *ctx,
+                                 struct gl_shader_program *shProg)
+ {
+    _mesa_reference_vertprog(ctx, &shProg->VertexProgram, NULL);
+    _mesa_reference_fragprog(ctx, &shProg->FragmentProgram, NULL);
+    _mesa_reference_geomprog(ctx, &shProg->GeometryProgram, NULL);
+    if (shProg->Uniforms) {
+       _mesa_free_uniform_list(shProg->Uniforms);
+       shProg->Uniforms = NULL;
+    }
+    if (shProg->Varying) {
+       _mesa_free_parameter_list(shProg->Varying);
+       shProg->Varying = NULL;
+    }
+ }
+ /**
+  * Free all the data that hangs off a shader program object, but not the
+  * object itself.
+  */
+ void
+ _mesa_free_shader_program_data(GLcontext *ctx,
+                                struct gl_shader_program *shProg)
+ {
+    GLuint i;
+    assert(shProg->Type == GL_SHADER_PROGRAM_MESA);
+    _mesa_clear_shader_program_data(ctx, shProg);
+    if (shProg->Attributes) {
+       _mesa_free_parameter_list(shProg->Attributes);
+       shProg->Attributes = NULL;
+    }
+    /* detach shaders */
+    for (i = 0; i < shProg->NumShaders; i++) {
+       _mesa_reference_shader(ctx, &shProg->Shaders[i], NULL);
+    }
+    shProg->NumShaders = 0;
+    if (shProg->Shaders) {
+       free(shProg->Shaders);
+       shProg->Shaders = NULL;
+    }
+    if (shProg->InfoLog) {
 -   free(shProg);
++      talloc_free(shProg->InfoLog);
+       shProg->InfoLog = NULL;
+    }
+    /* Transform feedback varying vars */
+    for (i = 0; i < shProg->TransformFeedback.NumVarying; i++) {
+       free(shProg->TransformFeedback.VaryingNames[i]);
+    }
+    free(shProg->TransformFeedback.VaryingNames);
+    shProg->TransformFeedback.VaryingNames = NULL;
+    shProg->TransformFeedback.NumVarying = 0;
+ }
+ /**
+  * Free/delete a shader program object.
+  * Called via ctx->Driver.DeleteShaderProgram().
+  */
+ static void
+ __mesa_delete_shader_program(GLcontext *ctx, struct gl_shader_program *shProg)
+ {
+    _mesa_free_shader_program_data(ctx, shProg);
++   talloc_free(shProg);
+ }
+ /**
+  * Lookup a GLSL program object.
+  */
+ struct gl_shader_program *
+ _mesa_lookup_shader_program(GLcontext *ctx, GLuint name)
+ {
+    struct gl_shader_program *shProg;
+    if (name) {
+       shProg = (struct gl_shader_program *)
+          _mesa_HashLookup(ctx->Shared->ShaderObjects, name);
+       /* Note that both gl_shader and gl_shader_program objects are kept
+        * in the same hash table.  Check the object's type to be sure it's
+        * what we're expecting.
+        */
+       if (shProg && shProg->Type != GL_SHADER_PROGRAM_MESA) {
+          return NULL;
+       }
+       return shProg;
+    }
+    return NULL;
+ }
+ /**
+  * As above, but record an error if program is not found.
+  */
+ struct gl_shader_program *
+ _mesa_lookup_shader_program_err(GLcontext *ctx, GLuint name,
+                                 const char *caller)
+ {
+    if (!name) {
+       _mesa_error(ctx, GL_INVALID_VALUE, caller);
+       return NULL;
+    }
+    else {
+       struct gl_shader_program *shProg = (struct gl_shader_program *)
+          _mesa_HashLookup(ctx->Shared->ShaderObjects, name);
+       if (!shProg) {
+          _mesa_error(ctx, GL_INVALID_VALUE, caller);
+          return NULL;
+       }
+       if (shProg->Type != GL_SHADER_PROGRAM_MESA) {
+          _mesa_error(ctx, GL_INVALID_OPERATION, caller);
+          return NULL;
+       }
+       return shProg;
+    }
+ }
+ void
+ _mesa_init_shader_object_functions(struct dd_function_table *driver)
+ {
+    driver->NewShader = _mesa_new_shader;
+    driver->DeleteShader = __mesa_delete_shader;
+    driver->NewShaderProgram = _mesa_new_shader_program;
+    driver->DeleteShaderProgram = __mesa_delete_shader_program;
+ }
   */
  
  
- #ifndef SHADER_API_H
- #define SHADER_API_H
+ #ifndef SHADEROBJ_H
+ #define SHADEROBJ_H
  
  
 -#include "glheader.h"
 -#include "mtypes.h"
 +#include "main/glheader.h"
 +#include "main/mtypes.h"
- #include "ir_to_mesa.h"
++#include "program/ir_to_mesa.h"
 +
 +#ifdef __cplusplus
 +extern "C" {
 +#endif
 +/**
 + * Internal functions
 + */
 +
 +extern void
 +_mesa_init_shader_state(GLcontext * ctx);
 +
 +extern void
 +_mesa_free_shader_state(GLcontext *ctx);
  
  
  extern void
@@@ -74,6 -48,6 +62,9 @@@ _mesa_reference_shader_program(GLcontex
                                 struct gl_shader_program **ptr,
                                 struct gl_shader_program *shProg);
  
++extern struct gl_shader *
++_mesa_new_shader(GLcontext *ctx, GLuint name, GLenum type);
++
  extern struct gl_shader_program *
  _mesa_lookup_shader_program(GLcontext *ctx, GLuint name);
  
@@@ -95,25 -66,13 +83,16 @@@ _mesa_free_shader_program_data(GLcontex
  
  
  extern void
- _mesa_update_shader_textures_used(struct gl_program *prog);
+ _mesa_init_shader_object_functions(struct dd_function_table *driver);
  
  extern void
- _mesa_use_program(GLcontext *ctx, GLuint program);
- extern GLboolean
- _mesa_validate_shader_program(GLcontext *ctx,
-                               const struct gl_shader_program *shProg,
-                               char *errMsg);
+ _mesa_init_shader_state(GLcontext *ctx);
  
  extern void
- _mesa_init_glsl_driver_functions(struct dd_function_table *driver);
+ _mesa_free_shader_state(GLcontext *ctx);
  
 +#ifdef __cplusplus
 +};
 +#endif
  
- #endif /* SHADER_API_H */
+ #endif /* SHADEROBJ_H */
Simple merge
Simple merge
index 89cad8a,0000000..1903b8f
mode 100644,000000..100644
--- /dev/null
@@@ -1,2308 -1,0 +1,2309 @@@
- #include "shader/prog_instruction.h"
- #include "shader/prog_optimize.h"
- #include "shader/prog_print.h"
- #include "shader/program.h"
- #include "shader/prog_uniform.h"
- #include "shader/prog_parameter.h"
- #include "shader/shader_api.h"
 +/*
 + * Copyright (C) 2005-2007  Brian Paul   All Rights Reserved.
 + * Copyright (C) 2008  VMware, Inc.   All Rights Reserved.
 + * Copyright © 2010 Intel Corporation
 + *
 + * Permission is hereby granted, free of charge, to any person obtaining a
 + * copy of this software and associated documentation files (the "Software"),
 + * to deal in the Software without restriction, including without limitation
 + * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 + * and/or sell copies of the Software, and to permit persons to whom the
 + * Software is furnished to do so, subject to the following conditions:
 + *
 + * The above copyright notice and this permission notice (including the next
 + * paragraph) shall be included in all copies or substantial portions of the
 + * Software.
 + *
 + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
 + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 + * DEALINGS IN THE SOFTWARE.
 + */
 +
 +/**
 + * \file ir_to_mesa.cpp
 + *
 + * Translates the IR to ARB_fragment_program text if possible,
 + * printing the result
 + */
 +
 +#include <stdio.h>
 +#include "ir.h"
 +#include "ir_visitor.h"
 +#include "ir_print_visitor.h"
 +#include "ir_expression_flattening.h"
 +#include "glsl_types.h"
 +#include "glsl_parser_extras.h"
 +#include "../glsl/program.h"
 +#include "ir_optimization.h"
 +#include "ast.h"
 +
 +extern "C" {
 +#include "main/mtypes.h"
++#include "main/shaderobj.h"
++#include "main/uniforms.h"
++#include "program/prog_instruction.h"
++#include "program/prog_optimize.h"
++#include "program/prog_print.h"
++#include "program/program.h"
++#include "program/prog_uniform.h"
++#include "program/prog_parameter.h"
 +}
 +
 +/**
 + * This struct is a corresponding struct to Mesa prog_src_register, with
 + * wider fields.
 + */
 +typedef struct ir_to_mesa_src_reg {
 +   int file; /**< PROGRAM_* from Mesa */
 +   int index; /**< temporary index, VERT_ATTRIB_*, FRAG_ATTRIB_*, etc. */
 +   GLuint swizzle; /**< SWIZZLE_XYZWONEZERO swizzles from Mesa. */
 +   int negate; /**< NEGATE_XYZW mask from mesa */
 +   /** 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;
 +
 +class ir_to_mesa_instruction : public exec_node {
 +public:
 +   enum prog_opcode op;
 +   ir_to_mesa_dst_reg dst_reg;
 +   ir_to_mesa_src_reg src_reg[3];
 +   /** Pointer to the ir source this tree came from for debugging */
 +   ir_instruction *ir;
 +   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 variable_storage : public exec_node {
 +public:
 +   variable_storage(ir_variable *var, int file, int index)
 +      : file(file), index(index), var(var)
 +   {
 +      /* empty */
 +   }
 +
 +   int file;
 +   int index;
 +   ir_variable *var; /* variable that maps to this, if any */
 +};
 +
 +class 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;
 +
 +   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);
 +
 +   /**
 +    * \name Visit methods
 +    *
 +    * As typical for the visitor pattern, there must be one \c visit method for
 +    * each concrete subclass of \c ir_instruction.  Virtual base classes within
 +    * the hierarchy should not have \c visit methods.
 +    */
 +   /*@{*/
 +   virtual void visit(ir_variable *);
 +   virtual void visit(ir_loop *);
 +   virtual void visit(ir_loop_jump *);
 +   virtual void visit(ir_function_signature *);
 +   virtual void visit(ir_function *);
 +   virtual void visit(ir_expression *);
 +   virtual void visit(ir_swizzle *);
 +   virtual void visit(ir_dereference_variable  *);
 +   virtual void visit(ir_dereference_array *);
 +   virtual void visit(ir_dereference_record *);
 +   virtual void visit(ir_assignment *);
 +   virtual void visit(ir_constant *);
 +   virtual void visit(ir_call *);
 +   virtual void visit(ir_return *);
 +   virtual void visit(ir_discard *);
 +   virtual void visit(ir_texture *);
 +   virtual void visit(ir_if *);
 +   /*@}*/
 +
 +   struct ir_to_mesa_src_reg result;
 +
 +   /** 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,
 +                                             ir_to_mesa_src_reg src0);
 +
 +   ir_to_mesa_instruction *ir_to_mesa_emit_op2(ir_instruction *ir,
 +                                             enum prog_opcode op,
 +                                             ir_to_mesa_dst_reg dst,
 +                                             ir_to_mesa_src_reg src0,
 +                                             ir_to_mesa_src_reg src1);
 +
 +   ir_to_mesa_instruction *ir_to_mesa_emit_op3(ir_instruction *ir,
 +                                             enum prog_opcode op,
 +                                             ir_to_mesa_dst_reg dst,
 +                                             ir_to_mesa_src_reg src0,
 +                                             ir_to_mesa_src_reg src1,
 +                                             ir_to_mesa_src_reg src2);
 +
 +   void ir_to_mesa_emit_scalar_op1(ir_instruction *ir,
 +                                 enum prog_opcode op,
 +                                 ir_to_mesa_dst_reg dst,
 +                                 ir_to_mesa_src_reg src0);
 +
 +   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, NULL,
 +};
 +
 +ir_to_mesa_dst_reg ir_to_mesa_undef_dst = {
 +   PROGRAM_UNDEFINED, 0, SWIZZLE_NOOP, COND_TR, NULL,
 +};
 +
 +ir_to_mesa_dst_reg ir_to_mesa_address_reg = {
 +   PROGRAM_ADDRESS, 0, WRITEMASK_X, COND_TR, NULL
 +};
 +
 +static int swizzle_for_size(int size)
 +{
 +   int size_swizzles[4] = {
 +      MAKE_SWIZZLE4(SWIZZLE_X, SWIZZLE_X, SWIZZLE_X, SWIZZLE_X),
 +      MAKE_SWIZZLE4(SWIZZLE_X, SWIZZLE_Y, SWIZZLE_Y, SWIZZLE_Y),
 +      MAKE_SWIZZLE4(SWIZZLE_X, SWIZZLE_Y, SWIZZLE_Z, SWIZZLE_Z),
 +      MAKE_SWIZZLE4(SWIZZLE_X, SWIZZLE_Y, SWIZZLE_Z, SWIZZLE_W),
 +   };
 +
 +   return size_swizzles[size - 1];
 +}
 +
 +ir_to_mesa_instruction *
 +ir_to_mesa_visitor::ir_to_mesa_emit_op3(ir_instruction *ir,
 +                                      enum prog_opcode op,
 +                                      ir_to_mesa_dst_reg dst,
 +                                      ir_to_mesa_src_reg src0,
 +                                      ir_to_mesa_src_reg src1,
 +                                      ir_to_mesa_src_reg src2)
 +{
 +   ir_to_mesa_instruction *inst = new(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;
 +   inst->src_reg[0] = src0;
 +   inst->src_reg[1] = src1;
 +   inst->src_reg[2] = src2;
 +   inst->ir = ir;
 +
 +   inst->function = NULL;
 +
 +   this->instructions.push_tail(inst);
 +
 +   return inst;
 +}
 +
 +
 +ir_to_mesa_instruction *
 +ir_to_mesa_visitor::ir_to_mesa_emit_op2(ir_instruction *ir,
 +                                      enum prog_opcode op,
 +                                      ir_to_mesa_dst_reg dst,
 +                                      ir_to_mesa_src_reg src0,
 +                                      ir_to_mesa_src_reg src1)
 +{
 +   return ir_to_mesa_emit_op3(ir, op, dst, src0, src1, ir_to_mesa_undef);
 +}
 +
 +ir_to_mesa_instruction *
 +ir_to_mesa_visitor::ir_to_mesa_emit_op1(ir_instruction *ir,
 +                                      enum prog_opcode op,
 +                                      ir_to_mesa_dst_reg dst,
 +                                      ir_to_mesa_src_reg src0)
 +{
 +   return ir_to_mesa_emit_op3(ir, op, dst,
 +                            src0, ir_to_mesa_undef, ir_to_mesa_undef);
 +}
 +
 +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)
 +{
 +   ir_to_mesa_dst_reg dst_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.
 + *
 + * Some Mesa opcodes are scalar-only, like ARB_fp/vp.  The src X
 + * channel determines the result across all channels.  So to do a vec4
 + * of this operation, we want to emit a scalar per source channel used
 + * to produce dest channels.
 + */
 +void
 +ir_to_mesa_visitor::ir_to_mesa_emit_scalar_op2(ir_instruction *ir,
 +                                             enum prog_opcode op,
 +                                             ir_to_mesa_dst_reg dst,
 +                                             ir_to_mesa_src_reg orig_src0,
 +                                             ir_to_mesa_src_reg orig_src1)
 +{
 +   int i, j;
 +   int done_mask = ~dst.writemask;
 +
 +   /* Mesa RCP is a scalar operation splatting results to all channels,
 +    * like ARB_fp/vp.  So emit as many RCPs as necessary to cover our
 +    * dst channels.
 +    */
 +   for (i = 0; i < 4; i++) {
 +      GLuint this_mask = (1 << i);
 +      ir_to_mesa_instruction *inst;
 +      ir_to_mesa_src_reg src0 = orig_src0;
 +      ir_to_mesa_src_reg src1 = orig_src1;
 +
 +      if (done_mask & this_mask)
 +       continue;
 +
 +      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(src0.swizzle, j) == src0_swiz &&
 +           GET_SWZ(src1.swizzle, j) == src1_swiz) {
 +          this_mask |= (1 << j);
 +       }
 +      }
 +      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_op2(ir, op,
 +                               dst,
 +                               src0,
 +                               src1);
 +      inst->dst_reg.writemask = this_mask;
 +      done_mask |= this_mask;
 +   }
 +}
 +
 +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 undef = ir_to_mesa_undef;
 +
 +   undef.swizzle = SWIZZLE_XXXX;
 +
 +   ir_to_mesa_emit_scalar_op2(ir, op, dst, src0, undef);
 +}
 +
 +struct ir_to_mesa_src_reg
 +ir_to_mesa_visitor::src_reg_for_float(float val)
 +{
 +   ir_to_mesa_src_reg src_reg;
 +
 +   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;
 +}
 +
 +static int
 +type_size(const struct glsl_type *type)
 +{
 +   unsigned int i;
 +   int size;
 +
 +   switch (type->base_type) {
 +   case GLSL_TYPE_UINT:
 +   case GLSL_TYPE_INT:
 +   case GLSL_TYPE_FLOAT:
 +   case GLSL_TYPE_BOOL:
 +      if (type->is_matrix()) {
 +       return 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
 +        * mess.  Hopefully a later pass over the code can pack scalars
 +        * down if appropriate.
 +        */
 +       return 1;
 +      }
 +   case GLSL_TYPE_ARRAY:
 +      return type_size(type->fields.array) * type->length;
 +   case GLSL_TYPE_STRUCT:
 +      size = 0;
 +      for (i = 0; i < type->length; i++) {
 +       size += type_size(type->fields.structure[i].type);
 +      }
 +      return size;
 +   default:
 +      assert(0);
 +   }
 +}
 +
 +/**
 + * 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)
 +{
 +   
 +   variable_storage *entry;
 +
 +   foreach_iter(exec_list_iterator, iter, this->variables) {
 +      entry = (variable_storage *)iter.get();
 +
 +      if (entry->var == var)
 +       return entry;
 +   }
 +
 +   return NULL;
 +}
 +
 +void
 +ir_to_mesa_visitor::visit(ir_variable *ir)
 +{
 +   (void)ir;
 +}
 +
 +void
 +ir_to_mesa_visitor::visit(ir_loop *ir)
 +{
 +   assert(!ir->from);
 +   assert(!ir->to);
 +   assert(!ir->increment);
 +   assert(!ir->counter);
 +
 +   ir_to_mesa_emit_op0(NULL, OPCODE_BGNLOOP);
 +   visit_exec_list(&ir->body_instructions, this);
 +   ir_to_mesa_emit_op0(NULL, OPCODE_ENDLOOP);
 +}
 +
 +void
 +ir_to_mesa_visitor::visit(ir_loop_jump *ir)
 +{
 +   switch (ir->mode) {
 +   case ir_loop_jump::jump_break:
 +      ir_to_mesa_emit_op0(NULL, OPCODE_BRK);
 +      break;
 +   case ir_loop_jump::jump_continue:
 +      ir_to_mesa_emit_op0(NULL, OPCODE_CONT);
 +      break;
 +   }
 +}
 +
 +
 +void
 +ir_to_mesa_visitor::visit(ir_function_signature *ir)
 +{
 +   assert(0);
 +   (void)ir;
 +}
 +
 +void
 +ir_to_mesa_visitor::visit(ir_function *ir)
 +{
 +   /* Ignore function bodies other than main() -- we shouldn't see calls to
 +    * them since they should all be inlined before we get to ir_to_mesa.
 +    */
 +   if (strcmp(ir->name, "main") == 0) {
 +      const ir_function_signature *sig;
 +      exec_list empty;
 +
 +      sig = ir->matching_signature(&empty);
 +
 +      assert(sig);
 +
 +      foreach_iter(exec_list_iterator, iter, sig->body) {
 +       ir_instruction *ir = (ir_instruction *)iter.get();
 +
 +       ir->accept(this);
 +      }
 +   }
 +}
 +
 +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)
 +{
 +   unsigned int operand;
 +   struct ir_to_mesa_src_reg op[2];
 +   struct ir_to_mesa_src_reg result_src;
 +   struct ir_to_mesa_dst_reg result_dst;
 +   const glsl_type *vec4_type = glsl_type::get_instance(GLSL_TYPE_FLOAT, 4, 1);
 +   const glsl_type *vec3_type = glsl_type::get_instance(GLSL_TYPE_FLOAT, 3, 1);
 +   const glsl_type *vec2_type = glsl_type::get_instance(GLSL_TYPE_FLOAT, 2, 1);
 +
 +   /* 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);
 +      if (this->result.file == PROGRAM_UNDEFINED) {
 +       ir_print_visitor v;
 +       printf("Failed to get tree for expression operand:\n");
 +       ir->operands[operand]->accept(&v);
 +       exit(1);
 +      }
 +      op[operand] = this->result;
 +
 +      /* Matrix expression operands should have been broken down to vector
 +       * operations already.
 +       */
 +      assert(!ir->operands[operand]->type->is_matrix());
 +   }
 +
 +   this->result.file = PROGRAM_UNDEFINED;
 +
 +   /* Storage for our result.  Ideally for an assignment we'd be using
 +    * the actual storage for the result here, instead.
 +    */
 +   result_src = get_temp(ir->type);
 +   /* convenience for the emit functions below. */
 +   result_dst = ir_to_mesa_dst_reg_from_src(result_src);
 +   /* Limit writes to the channels that will be used by result_src later.
 +    * This does limit this temp's use as a temporary for multi-instruction
 +    * sequences.
 +    */
 +   result_dst.writemask = (1 << ir->type->vector_elements) - 1;
 +
 +   switch (ir->operation) {
 +   case ir_unop_logic_not:
 +      ir_to_mesa_emit_op2(ir, OPCODE_SEQ, result_dst,
 +                        op[0], src_reg_for_float(0.0));
 +      break;
 +   case ir_unop_neg:
 +      op[0].negate = ~op[0].negate;
 +      result_src = op[0];
 +      break;
 +   case ir_unop_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_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]);
 +      break;
 +   case ir_unop_log:
 +      ir_to_mesa_emit_scalar_op1(ir, OPCODE_LOG, result_dst, op[0]);
 +      break;
 +   case ir_unop_log2:
 +      ir_to_mesa_emit_scalar_op1(ir, OPCODE_LG2, result_dst, op[0]);
 +      break;
 +   case ir_unop_sin:
 +      ir_to_mesa_emit_scalar_op1(ir, OPCODE_SIN, result_dst, op[0]);
 +      break;
 +   case ir_unop_cos:
 +      ir_to_mesa_emit_scalar_op1(ir, OPCODE_COS, result_dst, op[0]);
 +      break;
 +
 +   case ir_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:
 +      ir_to_mesa_emit_op2(ir, OPCODE_MUL, result_dst, op[0], op[1]);
 +      break;
 +   case ir_binop_div:
 +      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:
 +      ir_to_mesa_emit_op2(ir, OPCODE_SLT, result_dst, op[0], op[1]);
 +      break;
 +   case ir_binop_greater:
 +      ir_to_mesa_emit_op2(ir, OPCODE_SGT, result_dst, op[0], op[1]);
 +      break;
 +   case ir_binop_lequal:
 +      ir_to_mesa_emit_op2(ir, OPCODE_SLE, result_dst, op[0], op[1]);
 +      break;
 +   case ir_binop_gequal:
 +      ir_to_mesa_emit_op2(ir, OPCODE_SGE, result_dst, op[0], op[1]);
 +      break;
 +   case ir_binop_equal:
 +      ir_to_mesa_emit_op2(ir, OPCODE_SEQ, result_dst, op[0], op[1]);
 +      break;
 +   case ir_binop_logic_xor:
 +   case ir_binop_nequal:
 +      ir_to_mesa_emit_op2(ir, OPCODE_SNE, result_dst, op[0], op[1]);
 +      break;
 +
 +   case ir_binop_logic_or:
 +      /* This could be a saturated add and skip the SNE. */
 +      ir_to_mesa_emit_op2(ir, OPCODE_ADD,
 +                        result_dst,
 +                        op[0], op[1]);
 +
 +      ir_to_mesa_emit_op2(ir, OPCODE_SNE,
 +                        result_dst,
 +                        result_src, src_reg_for_float(0.0));
 +      break;
 +
 +   case ir_binop_logic_and:
 +      /* the bool args are stored as float 0.0 or 1.0, so "mul" gives us "and". */
 +      ir_to_mesa_emit_op2(ir, OPCODE_MUL,
 +                        result_dst,
 +                        op[0], op[1]);
 +      break;
 +
 +   case ir_binop_dot:
 +      if (ir->operands[0]->type == vec4_type) {
 +       assert(ir->operands[1]->type == vec4_type);
 +       ir_to_mesa_emit_op2(ir, OPCODE_DP4,
 +                           result_dst,
 +                           op[0], op[1]);
 +      } else if (ir->operands[0]->type == vec3_type) {
 +       assert(ir->operands[1]->type == vec3_type);
 +       ir_to_mesa_emit_op2(ir, OPCODE_DP3,
 +                           result_dst,
 +                           op[0], op[1]);
 +      } else if (ir->operands[0]->type == vec2_type) {
 +       assert(ir->operands[1]->type == vec2_type);
 +       ir_to_mesa_emit_op2(ir, OPCODE_DP2,
 +                           result_dst,
 +                           op[0], op[1]);
 +      }
 +      break;
 +
 +   case ir_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_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;
 +   case ir_unop_f2i:
 +      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;
 +   case ir_unop_trunc:
 +      ir_to_mesa_emit_op1(ir, OPCODE_TRUNC, result_dst, op[0]);
 +      break;
 +   case ir_unop_ceil:
 +      op[0].negate = ~op[0].negate;
 +      ir_to_mesa_emit_op1(ir, OPCODE_FLR, result_dst, op[0]);
 +      result_src.negate = ~result_src.negate;
 +      break;
 +   case ir_unop_floor:
 +      ir_to_mesa_emit_op1(ir, OPCODE_FLR, result_dst, op[0]);
 +      break;
 +   case ir_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;
 +   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;
 +   }
 +
 +   this->result = result_src;
 +}
 +
 +
 +void
 +ir_to_mesa_visitor::visit(ir_swizzle *ir)
 +{
 +   ir_to_mesa_src_reg src_reg;
 +   int i;
 +   int swizzle[4];
 +
 +   /* Note that this is only swizzles in expressions, not those on the left
 +    * hand side of an assignment, which do write masking.  See ir_assignment
 +    * for that.
 +    */
 +
 +   ir->val->accept(this);
 +   src_reg = this->result;
 +   assert(src_reg.file != PROGRAM_UNDEFINED);
 +
 +   for (i = 0; i < 4; i++) {
 +      if (i < ir->type->vector_elements) {
 +       switch (i) {
 +       case 0:
 +          swizzle[i] = GET_SWZ(src_reg.swizzle, ir->mask.x);
 +          break;
 +       case 1:
 +          swizzle[i] = GET_SWZ(src_reg.swizzle, ir->mask.y);
 +          break;
 +       case 2:
 +          swizzle[i] = GET_SWZ(src_reg.swizzle, ir->mask.z);
 +          break;
 +       case 3:
 +          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] = swizzle[ir->type->vector_elements - 1];
 +      }
 +   }
 +
 +   src_reg.swizzle = MAKE_SWIZZLE4(swizzle[0],
 +                                 swizzle[1],
 +                                 swizzle[2],
 +                                 swizzle[3]);
 +
 +   this->result = src_reg;
 +}
 +
 +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
 +    * loaded in registers in row-major order.  With GLSL, we want column-
 +    * major order.  So, we need to transpose all matrices here...
 +    */
 +   static const struct {
 +      const char *name;
 +      int matrix;
 +      int modifier;
 +   } matrices[] = {
 +      { "gl_ModelViewMatrix", STATE_MODELVIEW_MATRIX, STATE_MATRIX_TRANSPOSE },
 +      { "gl_ModelViewMatrixInverse", STATE_MODELVIEW_MATRIX, STATE_MATRIX_INVTRANS },
 +      { "gl_ModelViewMatrixTranspose", STATE_MODELVIEW_MATRIX, 0 },
 +      { "gl_ModelViewMatrixInverseTranspose", STATE_MODELVIEW_MATRIX, STATE_MATRIX_INVERSE },
 +
 +      { "gl_ProjectionMatrix", STATE_PROJECTION_MATRIX, STATE_MATRIX_TRANSPOSE },
 +      { "gl_ProjectionMatrixInverse", STATE_PROJECTION_MATRIX, STATE_MATRIX_INVTRANS },
 +      { "gl_ProjectionMatrixTranspose", STATE_PROJECTION_MATRIX, 0 },
 +      { "gl_ProjectionMatrixInverseTranspose", STATE_PROJECTION_MATRIX, STATE_MATRIX_INVERSE },
 +
 +      { "gl_ModelViewProjectionMatrix", STATE_MVP_MATRIX, STATE_MATRIX_TRANSPOSE },
 +      { "gl_ModelViewProjectionMatrixInverse", STATE_MVP_MATRIX, STATE_MATRIX_INVTRANS },
 +      { "gl_ModelViewProjectionMatrixTranspose", STATE_MVP_MATRIX, 0 },
 +      { "gl_ModelViewProjectionMatrixInverseTranspose", STATE_MVP_MATRIX, STATE_MATRIX_INVERSE },
 +
 +      { "gl_TextureMatrix", STATE_TEXTURE_MATRIX, STATE_MATRIX_TRANSPOSE },
 +      { "gl_TextureMatrixInverse", STATE_TEXTURE_MATRIX, STATE_MATRIX_INVTRANS },
 +      { "gl_TextureMatrixTranspose", STATE_TEXTURE_MATRIX, 0 },
 +      { "gl_TextureMatrixInverseTranspose", STATE_TEXTURE_MATRIX, STATE_MATRIX_INVERSE },
 +
 +      { "gl_NormalMatrix", STATE_MODELVIEW_MATRIX, STATE_MATRIX_INVERSE },
 +
 +   };
 +   unsigned int i;
 +   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.
 +    */
 +   assert(sizeof(gl_state_index) == sizeof(int));
 +
 +   for (i = 0; i < Elements(matrices); i++) {
 +      if (strcmp(var->name, matrices[i].name) == 0) {
 +       int tokens[STATE_LENGTH];
 +       int base_pos = -1;
 +
 +       tokens[0] = matrices[i].matrix;
 +       tokens[4] = matrices[i].modifier;
 +       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) variable_storage(var,
 +                                             PROGRAM_STATE_VAR,
 +                                             base_pos);
 +
 +       return entry;
 +      }
 +   }
 +
 +   return NULL;
 +}
 +
 +void
 +ir_to_mesa_visitor::visit(ir_dereference_variable *ir)
 +{
 +   ir_to_mesa_src_reg src_reg;
 +   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,
 +                                      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,
 +                               ir->var->name,
 +                               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) 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:
 +       /* 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);
 +       }
 +
 +       break;
 +      case ir_var_auto:
 +      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;
 +      }
 +
 +      if (!entry) {
 +       printf("Failed to make storage for %s\n", ir->var->name);
 +       exit(1);
 +      }
 +   }
 +
 +   src_reg.file = entry->file;
 +   src_reg.index = entry->index;
 +   /* If the type is smaller than a vec4, replicate the last channel out. */
 +   if (ir->type->is_scalar() || ir->type->is_vector())
 +      src_reg.swizzle = swizzle_for_size(ir->var->type->vector_elements);
 +   else
 +      src_reg.swizzle = SWIZZLE_NOOP;
 +   src_reg.reladdr = NULL;
 +   src_reg.negate = 0;
 +
 +   this->result = src_reg;
 +}
 +
 +void
 +ir_to_mesa_visitor::visit(ir_dereference_array *ir)
 +{
 +   ir_constant *index;
 +   ir_to_mesa_src_reg src_reg;
 +   ir_dereference_variable *deref_var = ir->array->as_dereference_variable();
 +   int element_size = type_size(ir->type);
 +
 +   index = ir->array_index->constant_expression_value();
 +
 +   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;
 +
 +      entry = get_builtin_matrix_ref(this->mem_ctx, this->prog, deref_var->var,
 +                                   ir->array_index);
 +      assert(entry);
 +
 +      src_reg.file = entry->file;
 +      src_reg.index = entry->index;
 +      src_reg.swizzle = swizzle_for_size(ir->type->vector_elements);
 +      src_reg.negate = 0;
 +
 +      if (index) {
 +       src_reg.reladdr = NULL;
 +      } else {
 +       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));
 +
 +       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. */
 +   if (ir->type->is_scalar() || ir->type->is_vector())
 +      src_reg.swizzle = swizzle_for_size(ir->type->vector_elements);
 +   else
 +      src_reg.swizzle = SWIZZLE_NOOP;
 +
 +   this->result = src_reg;
 +}
 +
 +void
 +ir_to_mesa_visitor::visit(ir_dereference_record *ir)
 +{
 +   unsigned int i;
 +   const glsl_type *struct_type = ir->record->type;
 +   int offset = 0;
 +
 +   ir->record->accept(this);
 +
 +   for (i = 0; i < struct_type->length; i++) {
 +      if (strcmp(struct_type->fields.structure[i].name, ir->field) == 0)
 +       break;
 +      offset += type_size(struct_type->fields.structure[i].type);
 +   }
 +   this->result.swizzle = swizzle_for_size(ir->type->vector_elements);
 +   this->result.index += offset;
 +}
 +
 +/**
 + * We want to be careful in assignment setup to hit the actual storage
 + * instead of potentially using a temporary like we might with the
 + * ir_dereference handler.
 + *
 + * Thanks to ir_swizzle_swizzle, and ir_vec_index_to_swizzle, we
 + * should only see potentially one variable array index of a vector,
 + * and one swizzle, before getting to actual vec4 storage.  So handle
 + * those, then go use ir_dereference to handle the rest.
 + */
 +static struct ir_to_mesa_dst_reg
 +get_assignment_lhs(ir_instruction *ir, ir_to_mesa_visitor *v,
 +                 ir_to_mesa_src_reg *r)
 +{
 +   struct ir_to_mesa_dst_reg dst_reg;
 +   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 ((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);
 +      }
 +
 +      dst_reg.writemask = 0;
 +      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;
 +}
 +
 +void
 +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_array());
 +
 +   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_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++;
 +      }
 +   }
 +}
 +
 +
 +void
 +ir_to_mesa_visitor::visit(ir_constant *ir)
 +{
 +   ir_to_mesa_src_reg src_reg;
 +   GLfloat stack_vals[4];
 +   GLfloat *values = stack_vals;
 +   unsigned int i;
 +
 +   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;
 +   switch (ir->type->base_type) {
 +   case GLSL_TYPE_FLOAT:
 +      values = &ir->value.f[0];
 +      break;
 +   case GLSL_TYPE_UINT:
 +      for (i = 0; i < ir->type->vector_elements; i++) {
 +       values[i] = ir->value.u[i];
 +      }
 +      break;
 +   case GLSL_TYPE_INT:
 +      for (i = 0; i < ir->type->vector_elements; i++) {
 +       values[i] = ir->value.i[i];
 +      }
 +      break;
 +   case GLSL_TYPE_BOOL:
 +      for (i = 0; i < ir->type->vector_elements; i++) {
 +       values[i] = ir->value.b[i];
 +      }
 +      break;
 +   default:
 +      assert(!"Non-float/uint/int/bool constant");
 +   }
 +
 +   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;
 +
 +   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)
 +{
 +   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)
 +{
 +   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(current_function);
 +
 +   if (ir->get_value()) {
 +      ir_to_mesa_dst_reg l;
 +      int i;
 +
 +      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 *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 (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_op0(ir->condition, OPCODE_ELSE);
 +      visit_exec_list(&ir->else_instructions, this);
 +   }
 +
 +   if_inst = ir_to_mesa_emit_op1(ir->condition, OPCODE_ENDIF,
 +                               ir_to_mesa_undef_dst, ir_to_mesa_undef);
 +}
 +
 +ir_to_mesa_visitor::ir_to_mesa_visitor()
 +{
 +   result.file = PROGRAM_UNDEFINED;
 +   next_temp = 1;
 +   next_signature_id = 1;
 +   sampler_map = NULL;
 +   sampler_map_size = 0;
 +   current_function = NULL;
 +}
 +
 +static struct prog_src_register
 +mesa_src_reg_from_ir_src_reg(ir_to_mesa_src_reg reg)
 +{
 +   struct prog_src_register mesa_reg;
 +
 +   mesa_reg.File = reg.file;
 +   assert(reg.index < (1 << INST_INDEX_BITS) - 1);
 +   mesa_reg.Index = reg.index;
 +   mesa_reg.Swizzle = reg.swizzle;
 +   mesa_reg.RelAddr = reg.reladdr != NULL;
 +   mesa_reg.Negate = reg.negate;
 +   mesa_reg.Abs = 0;
 +
 +   return mesa_reg;
 +}
 +
 +static void
 +set_branchtargets(ir_to_mesa_visitor *v,
 +                struct prog_instruction *mesa_instructions,
 +                int num_instructions)
 +{
 +   int if_count = 0, loop_count = 0;
 +   int *if_stack, *loop_stack;
 +   int if_stack_pos = 0, loop_stack_pos = 0;
 +   int i, j;
 +
 +   for (i = 0; i < num_instructions; i++) {
 +      switch (mesa_instructions[i].Opcode) {
 +      case OPCODE_IF:
 +       if_count++;
 +       break;
 +      case OPCODE_BGNLOOP:
 +       loop_count++;
 +       break;
 +      case OPCODE_BRK:
 +      case OPCODE_CONT:
 +       mesa_instructions[i].BranchTarget = -1;
 +       break;
 +      default:
 +       break;
 +      }
 +   }
 +
 +   if_stack = (int *)calloc(if_count, sizeof(*if_stack));
 +   loop_stack = (int *)calloc(loop_count, sizeof(*loop_stack));
 +
 +   for (i = 0; i < num_instructions; i++) {
 +      switch (mesa_instructions[i].Opcode) {
 +      case OPCODE_IF:
 +       if_stack[if_stack_pos] = i;
 +       if_stack_pos++;
 +       break;
 +      case OPCODE_ELSE:
 +       mesa_instructions[if_stack[if_stack_pos - 1]].BranchTarget = i;
 +       if_stack[if_stack_pos - 1] = i;
 +       break;
 +      case OPCODE_ENDIF:
 +       mesa_instructions[if_stack[if_stack_pos - 1]].BranchTarget = i;
 +       if_stack_pos--;
 +       break;
 +      case OPCODE_BGNLOOP:
 +       loop_stack[loop_stack_pos] = i;
 +       loop_stack_pos++;
 +       break;
 +      case OPCODE_ENDLOOP:
 +       loop_stack_pos--;
 +       /* Rewrite any breaks/conts at this nesting level (haven't
 +        * already had a BranchTarget assigned) to point to the end
 +        * of the loop.
 +        */
 +       for (j = loop_stack[loop_stack_pos]; j < i; j++) {
 +          if (mesa_instructions[j].Opcode == OPCODE_BRK ||
 +              mesa_instructions[j].Opcode == OPCODE_CONT) {
 +             if (mesa_instructions[j].BranchTarget == -1) {
 +                mesa_instructions[j].BranchTarget = i;
 +             }
 +          }
 +       }
 +       /* The loop ends point at each other. */
 +       mesa_instructions[i].BranchTarget = loop_stack[loop_stack_pos];
 +       mesa_instructions[loop_stack[loop_stack_pos]].BranchTarget = i;
 +       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;
 +      }
 +   }
 +
 +   free(if_stack);
 +}
 +
 +static void
 +print_program(struct prog_instruction *mesa_instructions,
 +            ir_instruction **mesa_instruction_annotation,
 +            int num_instructions)
 +{
 +   ir_instruction *last_ir = NULL;
 +   int i;
 +   int indent = 0;
 +
 +   for (i = 0; i < num_instructions; i++) {
 +      struct prog_instruction *mesa_inst = mesa_instructions + i;
 +      ir_instruction *ir = mesa_instruction_annotation[i];
 +
 +      fprintf(stdout, "%3d: ", i);
 +
 +      if (last_ir != ir && ir) {
 +       int j;
 +
 +       for (j = 0; j < indent; j++) {
 +          fprintf(stdout, " ");
 +       }
 +       ir->print();
 +       printf("\n");
 +       last_ir = ir;
 +
 +       fprintf(stdout, "     "); /* line number spacing. */
 +      }
 +
 +      indent = _mesa_fprint_instruction_opt(stdout, mesa_inst, indent,
 +                                          PROG_PRINT_DEBUG, NULL);
 +   }
 +}
 +
 +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;
 +   prog->SamplersUsed = 0;
 +
 +   for (i = 0; i < prog->NumInstructions; i++) {
 +      struct prog_instruction *inst = &prog->Instructions[i];
 +      unsigned int reg;
 +
 +      switch (inst->DstReg.File) {
 +      case PROGRAM_OUTPUT:
 +       mark_output(prog, inst->DstReg.Index, inst->DstReg.RelAddr);
 +       break;
 +      case PROGRAM_INPUT:
 +       mark_input(prog, inst->DstReg.Index, inst->DstReg.RelAddr);
 +       break;
 +      default:
 +       break;
 +      }
 +
 +      for (reg = 0; reg < _mesa_num_inst_src_regs(inst->Opcode); reg++) {
 +       switch (inst->SrcReg[reg].File) {
 +       case PROGRAM_OUTPUT:
 +          mark_output(prog, inst->SrcReg[reg].Index,
 +                      inst->SrcReg[reg].RelAddr);
 +          break;
 +       case PROGRAM_INPUT:
 +          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
 + * list for the linked shader program has a pointer to these uniforms
 + * in each of the stage's Parameters list, so that their values can be
 + * updated when a uniform is set.
 + */
 +static void
 +link_uniforms_to_shared_uniform_list(struct gl_uniform_list *uniforms,
 +                                   struct gl_program *prog)
 +{
 +   unsigned int i;
 +
 +   for (i = 0; i < prog->Parameters->NumParameters; i++) {
 +      const struct gl_program_parameter *p = prog->Parameters->Parameters + i;
 +
 +      if (p->Type == PROGRAM_UNIFORM || p->Type == PROGRAM_SAMPLER) {
 +       struct gl_uniform *uniform =
 +          _mesa_append_uniform(uniforms, p->Name, prog->Target, i);
 +       if (uniform)
 +          uniform->Initialized = p->Initialized;
 +      }
 +   }
 +}
 +
 +struct gl_program *
 +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;
 +   struct gl_program *prog;
 +   GLenum target;
 +   const char *target_string;
 +   GLboolean progress;
 +
 +   switch (shader->Type) {
 +   case GL_VERTEX_SHADER:
 +      target = GL_VERTEX_PROGRAM_ARB;
 +      target_string = "vertex";
 +      break;
 +   case GL_FRAGMENT_SHADER:
 +      target = GL_FRAGMENT_PROGRAM_ARB;
 +      target_string = "fragment";
 +      break;
 +   default:
 +      assert(!"should not be reached");
 +      break;
 +   }
 +
 +   validate_ir_tree(shader->ir);
 +
 +   prog = ctx->Driver.NewProgram(ctx, target, 1);
 +   if (!prog)
 +      return NULL;
 +   prog->Parameters = _mesa_new_parameter_list();
 +   prog->Varying = _mesa_new_parameter_list();
 +   prog->Attributes = _mesa_new_parameter_list();
 +   v.ctx = ctx;
 +   v.prog = prog;
 +
 +   v.mem_ctx = talloc_new(NULL);
 +
 +   /* 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;
 +
 +   int num_instructions = 0;
 +   foreach_iter(exec_list_iterator, iter, v.instructions) {
 +      num_instructions++;
 +   }
 +
 +   mesa_instructions =
 +      (struct prog_instruction *)calloc(num_instructions,
 +                                      sizeof(*mesa_instructions));
 +   mesa_instruction_annotation = talloc_array(mem_ctx, ir_instruction *,
 +                                            num_instructions);
 +
 +   mesa_inst = mesa_instructions;
 +   i = 0;
 +   foreach_iter(exec_list_iterator, iter, v.instructions) {
 +      ir_to_mesa_instruction *inst = (ir_to_mesa_instruction *)iter.get();
 +
 +      mesa_inst->Opcode = inst->op;
 +      mesa_inst->CondUpdate = inst->cond_update;
 +      mesa_inst->DstReg.File = inst->dst_reg.file;
 +      mesa_inst->DstReg.Index = inst->dst_reg.index;
 +      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 */
 +      else if (mesa_inst->Opcode == OPCODE_ARL)
 +       prog->NumAddressRegs = 1;
 +
 +      mesa_inst++;
 +      i++;
 +   }
 +
 +   set_branchtargets(&v, mesa_instructions, num_instructions);
 +   if (ctx->Shader.Flags & GLSL_DUMP) {
 +      printf("Mesa %s program:\n", target_string);
 +      print_program(mesa_instructions, mesa_instruction_annotation,
 +                  num_instructions);
 +   }
 +
 +   prog->Instructions = mesa_instructions;
 +   prog->NumInstructions = num_instructions;
 +
 +   _mesa_reference_program(ctx, &shader->Program, prog);
 +
 +   if ((ctx->Shader.Flags & GLSL_NO_OPT) == 0) {
 +      _mesa_optimize_program(ctx, prog);
 +   }
 +
 +   return prog;
 +}
 +
 +extern "C" {
 +
 +void
 +_mesa_glsl_compile_shader(GLcontext *ctx, struct gl_shader *shader)
 +{
 +   struct _mesa_glsl_parse_state *state =
 +      new(shader) _mesa_glsl_parse_state(ctx, shader->Type, shader);
 +
 +   const char *source = shader->Source;
 +   state->error = preprocess(state, &source, &state->info_log,
 +                           &ctx->Extensions);
 +
 +   if (!state->error) {
 +     _mesa_glsl_lexer_ctor(state, source);
 +     _mesa_glsl_parse(state);
 +     _mesa_glsl_lexer_dtor(state);
 +   }
 +
 +   shader->ir = new(shader) exec_list;
 +   if (!state->error && !state->translation_unit.is_empty())
 +      _mesa_ast_to_hir(shader->ir, state);
 +
 +   if (!state->error && !shader->ir->is_empty()) {
 +      validate_ir_tree(shader->ir);
 +
 +      /* Lowering */
 +      do_mat_op_to_vec(shader->ir);
 +      do_mod_to_fract(shader->ir);
 +      do_div_to_mul_rcp(shader->ir);
 +
 +      /* 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_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;
 +
 +   /* Retain any live IR, but trash the rest. */
 +   reparent_ir(shader->ir, shader);
 +
 +   talloc_free(state);
 + }
 +
 +void
 +_mesa_glsl_link_shader(GLcontext *ctx, struct gl_shader_program *prog)
 +{
 +   unsigned int i;
 +
 +   _mesa_clear_shader_program_data(ctx, prog);
 +
 +   prog->LinkStatus = GL_TRUE;
 +
 +   for (i = 0; i < prog->NumShaders; i++) {
 +      if (!prog->Shaders[i]->CompileStatus) {
 +       prog->InfoLog =
 +          talloc_asprintf_append(prog->InfoLog,
 +                                 "linking with uncompiled shader");
 +       prog->LinkStatus = GL_FALSE;
 +      }
 +   }
 +
 +   prog->Varying = _mesa_new_parameter_list();
 +   _mesa_reference_vertprog(ctx, &prog->VertexProgram, NULL);
 +   _mesa_reference_fragprog(ctx, &prog->FragmentProgram, NULL);
 +
 +   if (prog->LinkStatus) {
 +      link_shaders(prog);
 +
 +      /* 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();
 +   }
 +
 +   if (prog->LinkStatus) {
 +      for (i = 0; i < prog->_NumLinkedShaders; i++) {
 +       struct gl_program *linked_prog;
 +
 +       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 (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;
 +       }
 +      }
 +   }
 +}
 +
 +} /* extern "C" */
index e832f84,0000000..e832f84
mode 100644,000000..100644
--- /dev/null
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
@@@ -223,56 -228,48 +228,51 @@@ STATETRACKER_SOURCES = 
        state_tracker/st_program.c \
        state_tracker/st_texture.c
  
- SHADER_SOURCES = \
-       shader/arbprogparse.c \
-       shader/arbprogram.c \
-       shader/atifragshader.c \
-       shader/hash_table.c \
-       shader/lex.yy.c \
-       shader/nvfragparse.c \
-       shader/nvprogram.c \
-       shader/nvvertparse.c \
-       shader/program.c \
-       shader/program_parse.tab.c \
-       shader/program_parse_extra.c \
-       shader/prog_cache.c \
-       shader/prog_execute.c \
-       shader/prog_instruction.c \
-       shader/prog_noise.c \
-       shader/prog_optimize.c \
-       shader/prog_parameter.c \
-       shader/prog_parameter_layout.c \
-       shader/prog_print.c \
-       shader/prog_statevars.c \
-       shader/prog_uniform.c \
-       shader/programopt.c \
-       shader/symbol_table.c \
-       shader/shader_api.c \
-       shader/uniforms.c
+ PROGRAM_SOURCES = \
+       program/arbprogparse.c \
+       program/hash_table.c \
+       program/lex.yy.c \
+       program/nvfragparse.c \
+       program/nvvertparse.c \
+       program/program.c \
+       program/program_parse.tab.c \
+       program/program_parse_extra.c \
+       program/prog_cache.c \
+       program/prog_execute.c \
+       program/prog_instruction.c \
+       program/prog_noise.c \
+       program/prog_optimize.c \
+       program/prog_parameter.c \
+       program/prog_parameter_layout.c \
+       program/prog_print.c \
+       program/prog_statevars.c \
+       program/prog_uniform.c \
+       program/programopt.c \
+       program/symbol_table.c
  
-       shader/ir_to_mesa.cpp
 +SHADER_CXX_SOURCES = \
++      program/ir_to_mesa.cpp
 +
  SLANG_SOURCES =       \
-       shader/slang/slang_builtin.c    \
-       shader/slang/slang_codegen.c    \
-       shader/slang/slang_compile.c    \
-       shader/slang/slang_compile_function.c   \
-       shader/slang/slang_compile_operation.c  \
-       shader/slang/slang_compile_struct.c     \
-       shader/slang/slang_compile_variable.c   \
-       shader/slang/slang_emit.c       \
-       shader/slang/slang_ir.c \
-       shader/slang/slang_label.c      \
-       shader/slang/slang_link.c       \
-       shader/slang/slang_log.c        \
-       shader/slang/slang_mem.c        \
-       shader/slang/slang_print.c      \
-       shader/slang/slang_simplify.c   \
-       shader/slang/slang_storage.c    \
-       shader/slang/slang_typeinfo.c   \
-       shader/slang/slang_vartable.c   \
-       shader/slang/slang_utility.c
+       slang/slang_builtin.c   \
+       slang/slang_codegen.c   \
+       slang/slang_compile.c   \
+       slang/slang_compile_function.c  \
+       slang/slang_compile_operation.c \
+       slang/slang_compile_struct.c    \
+       slang/slang_compile_variable.c  \
+       slang/slang_emit.c      \
+       slang/slang_ir.c        \
+       slang/slang_label.c     \
+       slang/slang_link.c      \
+       slang/slang_log.c       \
+       slang/slang_mem.c       \
+       slang/slang_print.c     \
+       slang/slang_simplify.c  \
+       slang/slang_storage.c   \
+       slang/slang_typeinfo.c  \
+       slang/slang_vartable.c  \
+       slang/slang_utility.c
  
  ASM_C_SOURCES =       \
        x86/common_x86.c \
@@@ -338,12 -333,10 +338,12 @@@ MESA_GALLIUM_SOURCES = 
        $(MATH_SOURCES)         \
        $(VBO_SOURCES)          \
        $(STATETRACKER_SOURCES) \
-       $(SHADER_SOURCES)       \
+       $(PROGRAM_SOURCES)      \
        ppc/common_ppc.c        \
 -      x86/common_x86.c        \
 -      $(SLANG_SOURCES)
 +      x86/common_x86.c
 +
 +MESA_GALLIUM_CXX_SOURCES = \
 +       $(SHADER_CXX_SOURCES)
  
  # All the core C sources, for dependency checking
  ALL_SOURCES = \