From a8e5f06fe01732fbd643bc435dd3b8eaa602defe Mon Sep 17 00:00:00 2001
From: marha <marha@users.sourceforge.net>
Date: Wed, 9 Nov 2011 16:58:33 +0100
Subject: libX11 mesa pixman git update 9 nov 2011

---
 mesalib/src/glsl/ast.h                  |  83 ++++++++-
 mesalib/src/glsl/ast_to_hir.cpp         | 310 +++++++++++++++++++++++++++++---
 mesalib/src/glsl/glsl_parser.yy         |  87 ++++++++-
 mesalib/src/glsl/glsl_parser_extras.cpp | 103 ++++++++++-
 mesalib/src/glsl/glsl_parser_extras.h   |  10 +-
 mesalib/src/glsl/glsl_types.h           |  19 +-
 mesalib/src/glsl/ir_uniform.h           | 128 +++++++++++++
 mesalib/src/glsl/link_uniforms.cpp      | 249 +++++++++++++++++++++++++
 mesalib/src/glsl/linker.cpp             | 180 ++-----------------
 mesalib/src/glsl/linker.h               |   7 +
 10 files changed, 968 insertions(+), 208 deletions(-)
 create mode 100644 mesalib/src/glsl/ir_uniform.h

(limited to 'mesalib/src/glsl')

diff --git a/mesalib/src/glsl/ast.h b/mesalib/src/glsl/ast.h
index 9fe6c4125..d899bc62a 100644
--- a/mesalib/src/glsl/ast.h
+++ b/mesalib/src/glsl/ast.h
@@ -629,13 +629,78 @@ public:
 
 class ast_case_label : public ast_node {
 public:
+   ast_case_label(ast_expression *test_value);
+   virtual void print(void) const;
+
+   virtual ir_rvalue *hir(exec_list *instructions,
+			  struct _mesa_glsl_parse_state *state);
 
    /**
-    * An expression of NULL means 'default'.
+    * An test value of NULL means 'default'.
     */
-   ast_expression *expression;
+   ast_expression *test_value;
+};
+
+
+class ast_case_label_list : public ast_node {
+public:
+   ast_case_label_list(void);
+   virtual void print(void) const;
+
+   virtual ir_rvalue *hir(exec_list *instructions,
+			  struct _mesa_glsl_parse_state *state);
+
+   /**
+    * A list of case labels.
+    */
+   exec_list labels;
 };
 
+
+class ast_case_statement : public ast_node {
+public:
+   ast_case_statement(ast_case_label_list *labels);
+   virtual void print(void) const;
+
+   virtual ir_rvalue *hir(exec_list *instructions,
+			  struct _mesa_glsl_parse_state *state);
+
+   ast_case_label_list *labels;
+
+   /**
+    * A list of statements.
+    */
+   exec_list stmts;
+};
+
+
+class ast_case_statement_list : public ast_node {
+public:
+   ast_case_statement_list(void);
+   virtual void print(void) const;
+
+   virtual ir_rvalue *hir(exec_list *instructions,
+			  struct _mesa_glsl_parse_state *state);
+
+   /**
+    * A list of cases.
+    */
+   exec_list cases;
+};
+
+
+class ast_switch_body : public ast_node {
+public:
+   ast_switch_body(ast_case_statement_list *stmts);
+   virtual void print(void) const;
+
+   virtual ir_rvalue *hir(exec_list *instructions,
+			  struct _mesa_glsl_parse_state *state);
+
+   ast_case_statement_list *stmts;
+};
+
+
 class ast_selection_statement : public ast_node {
 public:
    ast_selection_statement(ast_expression *condition,
@@ -654,8 +719,18 @@ public:
 
 class ast_switch_statement : public ast_node {
 public:
-   ast_expression *expression;
-   exec_list statements;
+   ast_switch_statement(ast_expression *test_expression,
+			ast_node *body);
+   virtual void print(void) const;
+
+   virtual ir_rvalue *hir(exec_list *instructions,
+			  struct _mesa_glsl_parse_state *state);
+
+   ast_expression *test_expression;
+   ast_node *body;
+
+protected:
+   void test_to_hir(exec_list *, struct _mesa_glsl_parse_state *);
 };
 
 class ast_iteration_statement : public ast_node {
diff --git a/mesalib/src/glsl/ast_to_hir.cpp b/mesalib/src/glsl/ast_to_hir.cpp
index ed6abdc70..ac090c315 100644
--- a/mesalib/src/glsl/ast_to_hir.cpp
+++ b/mesalib/src/glsl/ast_to_hir.cpp
@@ -3355,34 +3355,49 @@ ast_jump_statement::hir(exec_list *instructions,
 
    case ast_break:
    case ast_continue:
-      /* FINISHME: Handle switch-statements.  They cannot contain 'continue',
-       * FINISHME: and they use a different IR instruction for 'break'.
-       */
-      /* FINISHME: Correctly handle the nesting.  If a switch-statement is
-       * FINISHME: inside a loop, a 'continue' is valid and will bind to the
-       * FINISHME: loop.
-       */
-      if (state->loop_or_switch_nesting == NULL) {
+      if (mode == ast_continue &&
+	  state->loop_nesting_ast == NULL) {
 	 YYLTYPE loc = this->get_location();
 
 	 _mesa_glsl_error(& loc, state,
-			  "`%s' may only appear in a loop",
-			  (mode == ast_break) ? "break" : "continue");
-      } else {
-	 ir_loop *const loop = state->loop_or_switch_nesting->as_loop();
+			  "continue may only appear in a loop");
+      } else if (mode == ast_break &&
+		 state->loop_nesting_ast == NULL &&
+		 state->switch_nesting_ast == NULL) {
+	 YYLTYPE loc = this->get_location();
 
-	 /* Inline the for loop expression again, since we don't know
-	  * where near the end of the loop body the normal copy of it
+	 _mesa_glsl_error(& loc, state,
+			  "break may only appear in a loop or a switch");
+      } else {
+	 /* For a loop, inline the for loop expression again,
+	  * since we don't know where near the end of
+	  * the loop body the normal copy of it
 	  * is going to be placed.
 	  */
-	 if (mode == ast_continue &&
-	     state->loop_or_switch_nesting_ast->rest_expression) {
-	    state->loop_or_switch_nesting_ast->rest_expression->hir(instructions,
-								    state);
+	 if (state->loop_nesting_ast != NULL &&
+	     mode == ast_continue &&
+	     state->loop_nesting_ast->rest_expression) {
+	    state->loop_nesting_ast->rest_expression->hir(instructions,
+							  state);
 	 }
 
-	 if (loop != NULL) {
-	    ir_loop_jump *const jump =
+	 if (state->is_switch_innermost &&
+	     mode == ast_break) {
+	    /* Force break out of switch by setting is_break switch state.
+	     */
+	    ir_variable *const is_break_var = state->is_break_var;
+	    ir_dereference_variable *const deref_is_break_var =
+	       new(ctx) ir_dereference_variable(is_break_var);
+	    ir_constant *const true_val = new(ctx) ir_constant(true);
+	    ir_assignment *const set_break_var =
+	       new(ctx) ir_assignment(deref_is_break_var,
+				      true_val,
+				      NULL);
+	    
+	    instructions->push_tail(set_break_var);
+	 }
+	 else {
+	    ir_loop_jump *const jump = 
 	       new(ctx) ir_loop_jump((mode == ast_break)
 				     ? ir_loop_jump::jump_break
 				     : ir_loop_jump::jump_continue);
@@ -3445,6 +3460,243 @@ ast_selection_statement::hir(exec_list *instructions,
 }
 
 
+ir_rvalue *
+ast_switch_statement::hir(exec_list *instructions,
+			  struct _mesa_glsl_parse_state *state)
+{
+   void *ctx = state;
+
+   ir_rvalue *const test_expression =
+      this->test_expression->hir(instructions, state);
+
+   /* From page 66 (page 55 of the PDF) of the GLSL 1.50 spec:
+    *
+    *    "The type of init-expression in a switch statement must be a 
+    *     scalar integer." 
+    *
+    * The checks are separated so that higher quality diagnostics can be
+    * generated for cases where the rule is violated.
+    */
+   if (!test_expression->type->is_integer()) {
+      YYLTYPE loc = this->test_expression->get_location();
+
+      _mesa_glsl_error(& loc,
+		       state,
+		       "switch-statement expression must be scalar "
+		       "integer");
+   }
+
+   /* Track the switch-statement nesting in a stack-like manner.
+    */
+   ir_variable *saved_test_var = state->test_var;
+   ir_variable *saved_is_fallthru_var = state->is_fallthru_var;
+   
+   bool save_is_switch_innermost = state->is_switch_innermost;
+   ast_switch_statement *saved_nesting_ast = state->switch_nesting_ast;
+
+   state->is_switch_innermost = true;
+   state->switch_nesting_ast = this;
+
+   /* Initalize is_fallthru state to false.
+    */
+   ir_rvalue *const is_fallthru_val = new (ctx) ir_constant(false);
+   state->is_fallthru_var = new(ctx) ir_variable(glsl_type::bool_type,
+					        "switch_is_fallthru_tmp",
+					        ir_var_temporary);
+   instructions->push_tail(state->is_fallthru_var);
+
+   ir_dereference_variable *deref_is_fallthru_var =
+      new(ctx) ir_dereference_variable(state->is_fallthru_var);
+   instructions->push_tail(new(ctx) ir_assignment(deref_is_fallthru_var,
+						  is_fallthru_val,
+						  NULL));
+
+   /* Initalize is_break state to false.
+    */
+   ir_rvalue *const is_break_val = new (ctx) ir_constant(false);
+   state->is_break_var = new(ctx) ir_variable(glsl_type::bool_type,
+					      "switch_is_break_tmp",
+					      ir_var_temporary);
+   instructions->push_tail(state->is_break_var);
+
+   ir_dereference_variable *deref_is_break_var =
+      new(ctx) ir_dereference_variable(state->is_break_var);
+   instructions->push_tail(new(ctx) ir_assignment(deref_is_break_var,
+						  is_break_val,
+						  NULL));
+
+   /* Cache test expression.
+    */
+   test_to_hir(instructions, state);
+   
+   /* Emit code for body of switch stmt.
+    */
+   body->hir(instructions, state);
+
+   /* Restore previous nesting before returning.
+    */
+   state->switch_nesting_ast = saved_nesting_ast;
+   state->is_switch_innermost = save_is_switch_innermost;
+
+   state->test_var = saved_test_var;
+   state->is_fallthru_var = saved_is_fallthru_var;
+
+   /* Switch statements do not have r-values.
+    */
+   return NULL;
+}
+
+
+void
+ast_switch_statement::test_to_hir(exec_list *instructions,
+				  struct _mesa_glsl_parse_state *state)
+{
+   void *ctx = state;
+
+   /* Cache value of test expression.
+    */
+   ir_rvalue *const test_val =
+      test_expression->hir(instructions,
+			   state);
+
+   state->test_var = new(ctx) ir_variable(glsl_type::int_type,
+					  "switch_test_tmp",
+					  ir_var_temporary);
+   ir_dereference_variable *deref_test_var =
+      new(ctx) ir_dereference_variable(state->test_var);
+
+   instructions->push_tail(state->test_var);
+   instructions->push_tail(new(ctx) ir_assignment(deref_test_var,
+						  test_val,
+						  NULL));
+}
+
+
+ir_rvalue *
+ast_switch_body::hir(exec_list *instructions,
+		     struct _mesa_glsl_parse_state *state)
+{
+   if (stmts != NULL)
+      stmts->hir(instructions, state);
+      
+   /* Switch bodies do not have r-values.
+    */
+   return NULL;
+}
+
+
+ir_rvalue *
+ast_case_statement_list::hir(exec_list *instructions,
+			     struct _mesa_glsl_parse_state *state)
+{
+   foreach_list_typed (ast_case_statement, case_stmt, link, & this->cases)
+      case_stmt->hir(instructions, state);
+         
+   /* Case statements do not have r-values.
+    */
+   return NULL;
+}
+
+
+ir_rvalue *
+ast_case_statement::hir(exec_list *instructions,
+			struct _mesa_glsl_parse_state *state)
+{
+   labels->hir(instructions, state);
+   
+   /* Conditionally set fallthru state based on break state.
+    */
+   ir_constant *const false_val = new(state) ir_constant(false);
+   ir_dereference_variable *const deref_is_fallthru_var =
+      new(state) ir_dereference_variable(state->is_fallthru_var);
+   ir_dereference_variable *const deref_is_break_var =
+      new(state) ir_dereference_variable(state->is_break_var);
+   ir_assignment *const reset_fallthru_on_break =
+      new(state) ir_assignment(deref_is_fallthru_var,
+			       false_val,
+			       deref_is_break_var);
+   instructions->push_tail(reset_fallthru_on_break);
+
+   /* Guard case statements depending on fallthru state.
+    */
+   ir_dereference_variable *const deref_fallthru_guard =
+      new(state) ir_dereference_variable(state->is_fallthru_var);
+   ir_if *const test_fallthru = new(state) ir_if(deref_fallthru_guard);
+   
+   foreach_list_typed (ast_node, stmt, link, & this->stmts)
+      stmt->hir(& test_fallthru->then_instructions, state);
+
+   instructions->push_tail(test_fallthru);
+         
+   /* Case statements do not have r-values.
+    */
+   return NULL;
+}
+
+
+ir_rvalue *
+ast_case_label_list::hir(exec_list *instructions,
+			 struct _mesa_glsl_parse_state *state)
+{
+   foreach_list_typed (ast_case_label, label, link, & this->labels)
+      label->hir(instructions, state);
+         
+   /* Case labels do not have r-values.
+    */
+   return NULL;
+}
+
+
+ir_rvalue *
+ast_case_label::hir(exec_list *instructions,
+		    struct _mesa_glsl_parse_state *state)
+{
+   void *ctx = state;
+
+   ir_dereference_variable *deref_fallthru_var =
+      new(ctx) ir_dereference_variable(state->is_fallthru_var);
+   
+   ir_rvalue *const true_val = new(ctx) ir_constant(true);
+
+   /* If not default case, ...
+    */
+   if (this->test_value != NULL) {
+      /* Conditionally set fallthru state based on
+       * comparison of cached test expression value to case label.
+       */
+      ir_rvalue *const test_val = this->test_value->hir(instructions, state);
+
+      ir_dereference_variable *deref_test_var =
+	 new(ctx) ir_dereference_variable(state->test_var);
+
+      ir_rvalue *const test_cond = new(ctx) ir_expression(ir_binop_all_equal,
+							  glsl_type::bool_type,
+							  test_val,
+							  deref_test_var);
+
+      ir_assignment *set_fallthru_on_test =
+	 new(ctx) ir_assignment(deref_fallthru_var,
+				true_val,
+				test_cond);
+   
+      instructions->push_tail(set_fallthru_on_test);
+   } else { /* default case */
+      /* Set falltrhu state.
+       */
+      ir_assignment *set_fallthru =
+	 new(ctx) ir_assignment(deref_fallthru_var,
+				true_val,
+				NULL);
+   
+      instructions->push_tail(set_fallthru);
+   }
+   
+   /* Case statements do not have r-values.
+    */
+   return NULL;
+}
+
+
 void
 ast_iteration_statement::condition_to_hir(ir_loop *stmt,
 					  struct _mesa_glsl_parse_state *state)
@@ -3498,13 +3750,17 @@ ast_iteration_statement::hir(exec_list *instructions,
    ir_loop *const stmt = new(ctx) ir_loop();
    instructions->push_tail(stmt);
 
-   /* Track the current loop and / or switch-statement nesting.
+   /* Track the current loop nesting.
     */
-   ir_instruction *const nesting = state->loop_or_switch_nesting;
-   ast_iteration_statement *nesting_ast = state->loop_or_switch_nesting_ast;
+   ast_iteration_statement *nesting_ast = state->loop_nesting_ast;
+
+   state->loop_nesting_ast = this;
 
-   state->loop_or_switch_nesting = stmt;
-   state->loop_or_switch_nesting_ast = this;
+   /* Likewise, indicate that following code is closest to a loop,
+    * NOT closest to a switch.
+    */
+   bool saved_is_switch_innermost = state->is_switch_innermost;
+   state->is_switch_innermost = false;
 
    if (mode != ast_do_while)
       condition_to_hir(stmt, state);
@@ -3523,8 +3779,8 @@ ast_iteration_statement::hir(exec_list *instructions,
 
    /* Restore previous nesting before returning.
     */
-   state->loop_or_switch_nesting = nesting;
-   state->loop_or_switch_nesting_ast = nesting_ast;
+   state->loop_nesting_ast = nesting_ast;
+   state->is_switch_innermost = saved_is_switch_innermost;
 
    /* Loops do not have r-values.
     */
diff --git a/mesalib/src/glsl/glsl_parser.yy b/mesalib/src/glsl/glsl_parser.yy
index d32d6e4e1..f3e873800 100644
--- a/mesalib/src/glsl/glsl_parser.yy
+++ b/mesalib/src/glsl/glsl_parser.yy
@@ -67,6 +67,11 @@
    ast_declarator_list *declarator_list;
    ast_struct_specifier *struct_specifier;
    ast_declaration *declaration;
+   ast_switch_body *switch_body;
+   ast_case_label *case_label;
+   ast_case_label_list *case_label_list;
+   ast_case_statement *case_statement;
+   ast_case_statement_list *case_statement_list;
 
    struct {
       ast_node *cond;
@@ -207,6 +212,12 @@
 %type <declaration> struct_declarator_list
 %type <node> selection_statement
 %type <selection_rest_statement> selection_rest_statement
+%type <node> switch_statement
+%type <switch_body> switch_body
+%type <case_label_list> case_label_list
+%type <case_label> case_label
+%type <case_statement> case_statement
+%type <case_statement_list> case_statement_list
 %type <node> iteration_statement
 %type <node> condition
 %type <node> conditionopt
@@ -1517,8 +1528,7 @@ simple_statement:
 	declaration_statement
 	| expression_statement
 	| selection_statement
-	| switch_statement		{ $$ = NULL; }
-	| case_label			{ $$ = NULL; }
+	| switch_statement
 	| iteration_statement
 	| jump_statement
 	;
@@ -1640,13 +1650,84 @@ condition:
 	}
 	;
 
+/*
+ * siwtch_statement grammar is based on the syntax described in the body
+ * of the GLSL spec, not in it's appendix!!!
+ */
 switch_statement:
-	SWITCH '(' expression ')' compound_statement
+	SWITCH '(' expression ')' switch_body
+	{
+	   $$ = new(state) ast_switch_statement($3, $5);
+	}
+	;
+
+switch_body:
+	'{' '}'
+	{
+	   $$ = new(state) ast_switch_body(NULL);
+	   $$->set_location(yylloc);
+	}
+	| '{' case_statement_list '}'
+	{
+	   $$ = new(state) ast_switch_body($2);
+	   $$->set_location(yylloc);
+	}
 	;
 
 case_label:
 	CASE expression ':'
+	{
+	   $$ = new(state) ast_case_label($2);
+	}
 	| DEFAULT ':'
+	{
+	   $$ = new(state) ast_case_label(NULL);
+	}
+	;
+
+case_label_list:
+	case_label
+	{
+	   ast_case_label_list *labels = new(state) ast_case_label_list();
+
+	   labels->labels.push_tail(& $1->link);
+	   $$ = labels;
+	}
+	| case_label_list case_label
+	{
+	   $$ = $1;
+	   $$->labels.push_tail(& $2->link);
+	}
+	;
+
+case_statement:
+	case_label_list statement
+	{
+	   ast_case_statement *stmts = new(state) ast_case_statement($1);
+
+	   stmts->stmts.push_tail(& $2->link);
+	   $$ = stmts
+	}
+	| case_statement statement
+	{
+	   $$ = $1;
+	   $$->stmts.push_tail(& $2->link);
+	}
+	;
+
+case_statement_list:
+	case_statement
+	{
+	   ast_case_statement_list *cases= new(state) ast_case_statement_list();
+
+	   cases->cases.push_tail(& $1->link);
+	   $$ = cases;
+	}
+	| case_statement_list case_statement
+	{
+	   $$ = $1;
+	   $$->cases.push_tail(& $2->link);
+	}
 	;
 
 iteration_statement:
diff --git a/mesalib/src/glsl/glsl_parser_extras.cpp b/mesalib/src/glsl/glsl_parser_extras.cpp
index e627dabf7..23aadb143 100644
--- a/mesalib/src/glsl/glsl_parser_extras.cpp
+++ b/mesalib/src/glsl/glsl_parser_extras.cpp
@@ -50,7 +50,8 @@ _mesa_glsl_parse_state::_mesa_glsl_parse_state(struct gl_context *ctx,
    this->symbols = new(mem_ctx) glsl_symbol_table;
    this->info_log = ralloc_strdup(mem_ctx, "");
    this->error = false;
-   this->loop_or_switch_nesting = NULL;
+   this->loop_nesting_ast = NULL;
+   this->switch_nesting_ast = NULL;
 
    this->num_builtins_to_link = 0;
 
@@ -805,6 +806,106 @@ ast_selection_statement::ast_selection_statement(ast_expression *condition,
 }
 
 
+void
+ast_switch_statement::print(void) const
+{
+   printf("switch ( ");
+   test_expression->print();
+   printf(") ");
+
+   body->print();
+}
+
+
+ast_switch_statement::ast_switch_statement(ast_expression *test_expression,
+					   ast_node *body)
+{
+   this->test_expression = test_expression;
+   this->body = body;
+}
+
+
+void
+ast_switch_body::print(void) const
+{
+   printf("{\n");
+   if (stmts != NULL) {
+      stmts->print();
+   }
+   printf("}\n");
+}
+
+
+ast_switch_body::ast_switch_body(ast_case_statement_list *stmts)
+{
+   this->stmts = stmts;
+}
+
+
+void ast_case_label::print(void) const
+{
+   if (test_value != NULL) {
+      printf("case ");
+      test_value->print();
+      printf(": ");
+   } else {
+      printf("default: ");
+   }
+}
+
+
+ast_case_label::ast_case_label(ast_expression *test_value)
+{
+   this->test_value = test_value;
+}
+
+
+void ast_case_label_list::print(void) const
+{
+   foreach_list_const(n, & this->labels) {
+      ast_node *ast = exec_node_data(ast_node, n, link);
+      ast->print();
+   }
+   printf("\n");
+}
+
+
+ast_case_label_list::ast_case_label_list(void)
+{
+}
+
+
+void ast_case_statement::print(void) const
+{
+   labels->print();
+   foreach_list_const(n, & this->stmts) {
+      ast_node *ast = exec_node_data(ast_node, n, link);
+      ast->print();
+      printf("\n");
+   }
+}
+
+
+ast_case_statement::ast_case_statement(ast_case_label_list *labels)
+{
+   this->labels = labels;
+}
+
+
+void ast_case_statement_list::print(void) const
+{
+   foreach_list_const(n, & this->cases) {
+      ast_node *ast = exec_node_data(ast_node, n, link);
+      ast->print();
+   }
+}
+
+
+ast_case_statement_list::ast_case_statement_list(void)
+{
+}
+
+
 void
 ast_iteration_statement::print(void) const
 {
diff --git a/mesalib/src/glsl/glsl_parser_extras.h b/mesalib/src/glsl/glsl_parser_extras.h
index 1f3404c9d..dd932951f 100644
--- a/mesalib/src/glsl/glsl_parser_extras.h
+++ b/mesalib/src/glsl/glsl_parser_extras.h
@@ -149,8 +149,14 @@ struct _mesa_glsl_parse_state {
    bool all_invariant;
 
    /** Loop or switch statement containing the current instructions. */
-   class ir_instruction *loop_or_switch_nesting;
-   class ast_iteration_statement *loop_or_switch_nesting_ast;
+   class ast_iteration_statement *loop_nesting_ast;
+   class ast_switch_statement *switch_nesting_ast;
+   bool is_switch_innermost; // if switch stmt is closest to break, ...
+
+   /** Temporary variables needed for switch statement. */
+   ir_variable *test_var;
+   ir_variable *is_fallthru_var;
+   ir_variable *is_break_var;
 
    /** List of structures defined in user code. */
    const glsl_type **user_structures;
diff --git a/mesalib/src/glsl/glsl_types.h b/mesalib/src/glsl/glsl_types.h
index 56b8efe7b..4ac90118b 100644
--- a/mesalib/src/glsl/glsl_types.h
+++ b/mesalib/src/glsl/glsl_types.h
@@ -29,21 +29,23 @@
 #include <string.h>
 #include <assert.h>
 
+#ifdef __cplusplus
 extern "C" {
-#include "GL/gl.h"
-}
-
-#include "ralloc.h"
+#endif
 
 struct _mesa_glsl_parse_state;
 struct glsl_symbol_table;
 
-extern "C" void
+extern void
 _mesa_glsl_initialize_types(struct _mesa_glsl_parse_state *state);
 
-extern "C" void
+extern void
 _mesa_glsl_release_types(void);
 
+#ifdef __cplusplus
+}
+#endif
+
 enum glsl_base_type {
    GLSL_TYPE_UINT = 0,
    GLSL_TYPE_INT,
@@ -66,6 +68,9 @@ enum glsl_sampler_dim {
    GLSL_SAMPLER_DIM_EXTERNAL
 };
 
+#ifdef __cplusplus
+#include "GL/gl.h"
+#include "ralloc.h"
 
 struct glsl_type {
    GLenum gl_type;
@@ -530,4 +535,6 @@ struct glsl_struct_field {
    const char *name;
 };
 
+#endif /* __cplusplus */
+
 #endif /* GLSL_TYPES_H */
diff --git a/mesalib/src/glsl/ir_uniform.h b/mesalib/src/glsl/ir_uniform.h
new file mode 100644
index 000000000..225da3fc5
--- /dev/null
+++ b/mesalib/src/glsl/ir_uniform.h
@@ -0,0 +1,128 @@
+/*
+ * Copyright © 2011 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 IR_UNIFORM_H
+#define IR_UNIFORM_H
+
+
+/* stdbool.h is necessary because this file is included in both C and C++ code.
+ */
+#include <stdbool.h>
+
+#include "program/prog_parameter.h"  /* For union gl_constant_value. */
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+enum gl_uniform_driver_format {
+   uniform_native = 0,          /**< Store data in the native format. */
+   uniform_int_float,           /**< Store integer data as floats. */
+   uniform_bool_float,          /**< Store boolean data as floats. */
+
+   /**
+    * Store boolean data as integer using 1 for \c true.
+    */
+   uniform_bool_int_0_1,
+
+   /**
+    * Store boolean data as integer using ~0 for \c true.
+    */
+   uniform_bool_int_0_not0
+};
+
+struct gl_uniform_driver_storage {
+   /**
+    * Number of bytes from one array element to the next.
+    */
+   uint8_t element_stride;
+
+   /**
+    * Number of bytes from one vector in a matrix to the next.
+    */
+   uint8_t vector_stride;
+
+   /**
+    * Base format of the stored data.
+    *
+    * This field must have a value from \c GLSL_TYPE_UINT through \c
+    * GLSL_TYPE_SAMPLER.
+    */
+   uint8_t format;
+
+   /**
+    * Pointer to the base of the data.
+    */
+   void *data;
+};
+
+struct gl_uniform_storage {
+   char *name;
+   const struct glsl_type *type;
+
+   /**
+    * The number of elements in this uniform.
+    *
+    * For non-arrays, this is always 0.  For arrays, the value is the size of
+    * the array.
+    */
+   unsigned array_elements;
+
+   /**
+    * Has this uniform ever been set?
+    */
+   bool initialized;
+
+   /**
+    * Base sampler index
+    *
+    * If \c ::base_type is \c GLSL_TYPE_SAMPLER, this represents the index of
+    * this sampler.  If \c ::array_elements is not zero, the array will use
+    * sampler indexes \c ::sampler through \c ::sampler + \c ::array_elements
+    * - 1, inclusive.
+    */
+   uint8_t sampler;
+
+   /**
+    * Storage used by the driver for the uniform
+    */
+   unsigned num_driver_storage;
+   struct gl_uniform_driver_storage *driver_storage;
+
+   /**
+    * Storage used by Mesa for the uniform
+    *
+    * This form of the uniform is used by Mesa's implementation of \c
+    * glGetUniform.  It can also be used by drivers to obtain the value of the
+    * uniform if the \c ::driver_storage interface is not used.
+    */
+   union gl_constant_value *storage;
+};
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* IR_UNIFORM_H */
diff --git a/mesalib/src/glsl/link_uniforms.cpp b/mesalib/src/glsl/link_uniforms.cpp
index 6dd1f5354..b9d5361b0 100644
--- a/mesalib/src/glsl/link_uniforms.cpp
+++ b/mesalib/src/glsl/link_uniforms.cpp
@@ -24,6 +24,7 @@
 #include "main/core.h"
 #include "ir.h"
 #include "linker.h"
+#include "ir_uniform.h"
 #include "glsl_symbol_table.h"
 #include "program/hash_table.h"
 
@@ -34,6 +35,21 @@
  * \author Ian Romanick <ian.d.romanick@intel.com>
  */
 
+/**
+ * Count the backing storage requirements for a type
+ */
+static unsigned
+values_for_type(const glsl_type *type)
+{
+   if (type->is_sampler()) {
+      return 1;
+   } else if (type->is_array() && type->fields.array->is_sampler()) {
+      return type->array_size();
+   } else {
+      return type->component_slots();
+   }
+}
+
 void
 uniform_field_visitor::process(ir_variable *var)
 {
@@ -83,3 +99,236 @@ uniform_field_visitor::recursion(const glsl_type *t, char **name,
       this->visit_field(t, *name);
    }
 }
+
+/**
+ * Class to help calculate the storage requirements for a set of uniforms
+ *
+ * As uniforms are added to the active set the number of active uniforms and
+ * the storage requirements for those uniforms are accumulated.  The active
+ * uniforms are added the the hash table supplied to the constructor.
+ *
+ * If the same uniform is added multiple times (i.e., once for each shader
+ * target), it will only be accounted once.
+ */
+class count_uniform_size : public uniform_field_visitor {
+public:
+   count_uniform_size(struct string_to_uint_map *map)
+      : num_active_uniforms(0), num_values(0), map(map)
+   {
+      /* empty */
+   }
+
+   /**
+    * Total number of active uniforms counted
+    */
+   unsigned num_active_uniforms;
+
+   /**
+    * Number of data values required to back the storage for the active uniforms
+    */
+   unsigned num_values;
+
+private:
+   virtual void visit_field(const glsl_type *type, const char *name)
+   {
+      assert(!type->is_record());
+      assert(!(type->is_array() && type->fields.array->is_record()));
+
+      /* If the uniform is already in the map, there's nothing more to do.
+       */
+      unsigned id;
+      if (this->map->get(id, name))
+	 return;
+
+      char *key = strdup(name);
+      this->map->put(this->num_active_uniforms, key);
+
+      /* Each leaf uniform occupies one entry in the list of active
+       * uniforms.
+       */
+      this->num_active_uniforms++;
+      this->num_values += values_for_type(type);
+   }
+
+   struct string_to_uint_map *map;
+};
+
+/**
+ * Class to help parcel out pieces of backing storage to uniforms
+ *
+ * Each uniform processed has some range of the \c gl_constant_value
+ * structures associated with it.  The association is done by finding
+ * the uniform in the \c string_to_uint_map and using the value from
+ * the map to connect that slot in the \c gl_uniform_storage table
+ * with the next available slot in the \c gl_constant_value array.
+ *
+ * \warning
+ * This class assumes that every uniform that will be processed is
+ * already in the \c string_to_uint_map.  In addition, it assumes that
+ * the \c gl_uniform_storage and \c gl_constant_value arrays are "big
+ * enough."
+ */
+class parcel_out_uniform_storage : public uniform_field_visitor {
+public:
+   parcel_out_uniform_storage(struct string_to_uint_map *map,
+			      struct gl_uniform_storage *uniforms,
+			      union gl_constant_value *values)
+      : map(map), uniforms(uniforms), next_sampler(0), values(values)
+   {
+      /* empty */
+   }
+
+private:
+   virtual void visit_field(const glsl_type *type, const char *name)
+   {
+      assert(!type->is_record());
+      assert(!(type->is_array() && type->fields.array->is_record()));
+
+      unsigned id;
+      bool found = this->map->get(id, name);
+      assert(found);
+
+      if (!found)
+	 return;
+
+      /* If there is already storage associated with this uniform, it means
+       * that it was set while processing an earlier shader stage.  For
+       * example, we may be processing the uniform in the fragment shader, but
+       * the uniform was already processed in the vertex shader.
+       */
+      if (this->uniforms[id].storage != NULL)
+	 return;
+
+      const glsl_type *base_type;
+      if (type->is_array()) {
+	 this->uniforms[id].array_elements = type->length;
+	 base_type = type->fields.array;
+      } else {
+	 this->uniforms[id].array_elements = 0;
+	 base_type = type;
+      }
+
+      if (base_type->is_sampler()) {
+	 this->uniforms[id].sampler = this->next_sampler;
+
+	 /* Increment the sampler by 1 for non-arrays and by the number of
+	  * array elements for arrays.
+	  */
+	 this->next_sampler += MAX2(1, this->uniforms[id].array_elements);
+      } else {
+	 this->uniforms[id].sampler = ~0;
+      }
+
+      this->uniforms[id].name = strdup(name);
+      this->uniforms[id].type = base_type;
+      this->uniforms[id].initialized = 0;
+      this->uniforms[id].num_driver_storage = 0;
+      this->uniforms[id].driver_storage = NULL;
+      this->uniforms[id].storage = this->values;
+
+      this->values += values_for_type(type);
+   }
+
+   struct string_to_uint_map *map;
+
+   struct gl_uniform_storage *uniforms;
+   unsigned next_sampler;
+
+public:
+   union gl_constant_value *values;
+};
+
+void
+link_assign_uniform_locations(struct gl_shader_program *prog)
+{
+   ralloc_free(prog->UniformStorage);
+   prog->UniformStorage = NULL;
+   prog->NumUserUniformStorage = 0;
+
+   if (prog->UniformHash != NULL) {
+      prog->UniformHash->clear();
+   } else {
+      prog->UniformHash = new string_to_uint_map;
+   }
+
+   for (unsigned i = 0; i < Elements(prog->SamplerUnits); i++) {
+      prog->SamplerUnits[i] = i;
+   }
+
+   /* First pass: Count the uniform resources used by the user-defined
+    * uniforms.  While this happens, each active uniform will have an index
+    * assigned to it.
+    *
+    * Note: this is *NOT* the index that is returned to the application by
+    * glGetUniformLocation.
+    */
+   count_uniform_size uniform_size(prog->UniformHash);
+   for (unsigned i = 0; i < MESA_SHADER_TYPES; i++) {
+      if (prog->_LinkedShaders[i] == NULL)
+	 continue;
+
+      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;
+
+	 /* FINISHME: Update code to process built-in uniforms!
+	  */
+	 if (strncmp("gl_", var->name, 3) == 0)
+	    continue;
+
+	 uniform_size.process(var);
+      }
+   }
+
+   const unsigned num_user_uniforms = uniform_size.num_active_uniforms;
+   const unsigned num_data_slots = uniform_size.num_values;
+
+   /* On the outside chance that there were no uniforms, bail out.
+    */
+   if (num_user_uniforms == 0)
+      return;
+
+   struct gl_uniform_storage *uniforms =
+      rzalloc_array(prog, struct gl_uniform_storage, num_user_uniforms);
+   union gl_constant_value *data =
+      rzalloc_array(uniforms, union gl_constant_value, num_data_slots);
+#ifndef NDEBUG
+   union gl_constant_value *data_end = &data[num_data_slots];
+#endif
+
+   parcel_out_uniform_storage parcel(prog->UniformHash, uniforms, data);
+
+   for (unsigned i = 0; i < MESA_SHADER_TYPES; i++) {
+      if (prog->_LinkedShaders[i] == NULL)
+	 continue;
+
+      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;
+
+	 /* FINISHME: Update code to process built-in uniforms!
+	  */
+	 if (strncmp("gl_", var->name, 3) == 0)
+	    continue;
+
+	 parcel.process(var);
+      }
+   }
+
+#ifndef NDEBUG
+   for (unsigned i = 0; i < num_user_uniforms; i++) {
+      assert(uniforms[i].storage != NULL);
+   }
+#endif
+
+   assert(parcel.values == data_end);
+
+   prog->NumUserUniformStorage = num_user_uniforms;
+   prog->UniformStorage = uniforms;
+
+   return;
+}
diff --git a/mesalib/src/glsl/linker.cpp b/mesalib/src/glsl/linker.cpp
index 915d5bbcf..0306b7a1b 100644
--- a/mesalib/src/glsl/linker.cpp
+++ b/mesalib/src/glsl/linker.cpp
@@ -191,8 +191,8 @@ linker_warning(gl_shader_program *prog, const char *fmt, ...)
 
 
 void
-invalidate_variable_locations(gl_shader *sh, enum ir_variable_mode mode,
-			      int generic_base)
+link_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();
@@ -1021,13 +1021,6 @@ link_intrastage_shaders(void *mem_ctx,
    return linked;
 }
 
-
-struct uniform_node {
-   exec_node link;
-   struct gl_uniform *u;
-   unsigned slots;
-};
-
 /**
  * Update the sizes of linked shader uniform arrays to the maximum
  * array index used.
@@ -1100,151 +1093,6 @@ update_array_sizes(struct gl_shader_program *prog)
    }
 }
 
-static void
-add_uniform(void *mem_ctx, exec_list *uniforms, struct hash_table *ht,
-	    const char *name, const glsl_type *type, GLenum shader_type,
-	    unsigned *next_shader_pos, unsigned *total_uniforms)
-{
-   if (type->is_record()) {
-      for (unsigned int i = 0; i < type->length; i++) {
-	 const glsl_type *field_type = type->fields.structure[i].type;
-	 char *field_name = ralloc_asprintf(mem_ctx, "%s.%s", name,
-					    type->fields.structure[i].name);
-
-	 add_uniform(mem_ctx, uniforms, ht, field_name, field_type,
-		     shader_type, next_shader_pos, total_uniforms);
-      }
-   } else {
-      uniform_node *n = (uniform_node *) hash_table_find(ht, name);
-      unsigned int vec4_slots;
-      const glsl_type *array_elem_type = NULL;
-
-      if (type->is_array()) {
-	 array_elem_type = type->fields.array;
-	 /* Array of structures. */
-	 if (array_elem_type->is_record()) {
-	    for (unsigned int i = 0; i < type->length; i++) {
-	       char *elem_name = ralloc_asprintf(mem_ctx, "%s[%d]", name, i);
-	       add_uniform(mem_ctx, uniforms, ht, elem_name, array_elem_type,
-			   shader_type, next_shader_pos, total_uniforms);
-	    }
-	    return;
-	 }
-      }
-
-      /* Fix the storage size of samplers at 1 vec4 each. Be sure to pad out
-       * vectors to vec4 slots.
-       */
-      if (type->is_array()) {
-	 if (array_elem_type->is_sampler())
-	    vec4_slots = type->length;
-	 else
-	    vec4_slots = type->length * array_elem_type->matrix_columns;
-      } else if (type->is_sampler()) {
-	 vec4_slots = 1;
-      } else {
-	 vec4_slots = type->matrix_columns;
-      }
-
-      if (n == NULL) {
-	 n = (uniform_node *) calloc(1, sizeof(struct uniform_node));
-	 n->u = (gl_uniform *) calloc(1, sizeof(struct gl_uniform));
-	 n->slots = vec4_slots;
-
-	 n->u->Name = strdup(name);
-	 n->u->Type = type;
-	 n->u->VertPos = -1;
-	 n->u->FragPos = -1;
-	 n->u->GeomPos = -1;
-	 (*total_uniforms)++;
-
-	 hash_table_insert(ht, n, name);
-	 uniforms->push_tail(& n->link);
-      }
-
-      switch (shader_type) {
-      case GL_VERTEX_SHADER:
-	 n->u->VertPos = *next_shader_pos;
-	 break;
-      case GL_FRAGMENT_SHADER:
-	 n->u->FragPos = *next_shader_pos;
-	 break;
-      case GL_GEOMETRY_SHADER:
-	 n->u->GeomPos = *next_shader_pos;
-	 break;
-      }
-
-      (*next_shader_pos) += vec4_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);
-   void *mem_ctx = ralloc_context(NULL);
-
-   for (unsigned i = 0; i < MESA_SHADER_TYPES; i++) {
-      if (prog->_LinkedShaders[i] == NULL)
-	 continue;
-
-      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;
-
-	 if (strncmp(var->name, "gl_", 3) == 0) {
-	    /* At the moment, we don't allocate uniform locations for
-	     * builtin uniforms.  It's permitted by spec, and we'll
-	     * likely switch to doing that at some point, but not yet.
-	     */
-	    continue;
-	 }
-
-	 var->location = next_position;
-	 add_uniform(mem_ctx, &uniforms, ht, var->name, var->type,
-		     prog->_LinkedShaders[i]->Type,
-		     &next_position, &total_uniforms);
-      }
-   }
-
-   ralloc_free(mem_ctx);
-
-   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));
-      idx++;
-
-      free(node->u);
-      free(node);
-   }
-
-   hash_table_dtor(ht);
-
-   prog->Uniforms = ul;
-}
-
-
 /**
  * Find a contiguous set of available bits in a bitmask.
  *
@@ -1291,12 +1139,6 @@ find_available_slots(unsigned used_mask, unsigned needed_count)
  * \return
  * If locations are successfully assigned, true is returned.  Otherwise an
  * error is emitted to the shader link log and false is returned.
- *
- * \bug
- * Locations set via \c glBindFragDataLocation are not currently supported.
- * Only locations assigned automatically by the linker, explicitly set by a
- * layout qualifier, or explicitly set by a built-in variable (e.g., \c
- * gl_FragColor) are supported for fragment shaders.
  */
 bool
 assign_attribute_or_color_locations(gl_shader_program *prog,
@@ -1320,7 +1162,8 @@ assign_attribute_or_color_locations(gl_shader_program *prog,
     * 1. Invalidate the location assignments for all vertex shader inputs.
     *
     * 2. Assign locations for inputs that have user-defined (via
-    *    glBindVertexAttribLocation) locations.
+    *    glBindVertexAttribLocation) locations and outputs that have
+    *    user-defined locations (via glBindFragDataLocation).
     *
     * 3. Sort the attributes without assigned locations by number of slots
     *    required in decreasing order.  Fragmentation caused by attribute
@@ -1337,7 +1180,7 @@ assign_attribute_or_color_locations(gl_shader_program *prog,
       (target_index == MESA_SHADER_VERTEX) ? ir_var_in : ir_var_out;
 
 
-   invalidate_variable_locations(sh, direction, generic_base);
+   link_invalidate_variable_locations(sh, direction, generic_base);
 
    /* Temporary storage for the set of attributes that need locations assigned.
     */
@@ -1381,6 +1224,13 @@ assign_attribute_or_color_locations(gl_shader_program *prog,
 	    assert(binding >= VERT_ATTRIB_GENERIC0);
 	    var->location = binding;
 	 }
+      } else if (target_index == MESA_SHADER_FRAGMENT) {
+	 unsigned binding;
+
+	 if (prog->FragDataBindings->get(binding, var->name)) {
+	    assert(binding >= FRAG_RESULT_DATA0);
+	    var->location = binding;
+	 }
       }
 
       /* If the variable is not a built-in and has a location statically
@@ -1539,8 +1389,8 @@ assign_varying_locations(struct gl_context *ctx,
     *    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);
+   link_invalidate_variable_locations(producer, ir_var_out, VERT_RESULT_VAR0);
+   link_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();
@@ -1858,7 +1708,7 @@ link_shaders(struct gl_context *ctx, struct gl_shader_program *prog)
    }
 
    update_array_sizes(prog);
-   assign_uniform_locations(prog);
+   link_assign_uniform_locations(prog);
 
    /* OpenGL ES requires that a vertex shader and a fragment shader both be
     * present in a linked program.  By checking for use of shading language
diff --git a/mesalib/src/glsl/linker.h b/mesalib/src/glsl/linker.h
index 78c632961..433c63be2 100644
--- a/mesalib/src/glsl/linker.h
+++ b/mesalib/src/glsl/linker.h
@@ -30,6 +30,13 @@ extern bool
 link_function_calls(gl_shader_program *prog, gl_shader *main,
 		    gl_shader **shader_list, unsigned num_shaders);
 
+extern void
+link_invalidate_variable_locations(gl_shader *sh, enum ir_variable_mode mode,
+				   int generic_base);
+
+extern void
+link_assign_uniform_locations(struct gl_shader_program *prog);
+
 /**
  * Class for processing all of the leaf fields of an uniform
  *
-- 
cgit v1.2.3