glsl: Avoid extra if statements for logic and/or with no side effects.
[mesa.git] / src / glsl / ast_to_hir.cpp
index 484786c5fbddfd3ea2b552f1392f85276961f906..75d7e9d5793079b5c5917c87323a6e13edb0628c 100644 (file)
 #include "glsl_parser_extras.h"
 #include "ast.h"
 #include "glsl_types.h"
+#include "program/hash_table.h"
 #include "ir.h"
 
 void
 _mesa_ast_to_hir(exec_list *instructions, struct _mesa_glsl_parse_state *state)
 {
    _mesa_glsl_initialize_variables(instructions, state);
-   _mesa_glsl_initialize_functions(state);
 
    state->symbols->language_version = state->language_version;
 
@@ -665,6 +665,7 @@ mark_whole_array_access(ir_rvalue *access)
 
 ir_rvalue *
 do_assignment(exec_list *instructions, struct _mesa_glsl_parse_state *state,
+             const char *non_lvalue_description,
              ir_rvalue *lhs, ir_rvalue *rhs, bool is_initializer,
              YYLTYPE lhs_loc)
 {
@@ -672,8 +673,13 @@ do_assignment(exec_list *instructions, struct _mesa_glsl_parse_state *state,
    bool error_emitted = (lhs->type->is_error() || rhs->type->is_error());
 
    if (!error_emitted) {
-      if (lhs->variable_referenced() != NULL
-          && lhs->variable_referenced()->read_only) {
+      if (non_lvalue_description != NULL) {
+         _mesa_glsl_error(&lhs_loc, state,
+                          "assignment to %s",
+                         non_lvalue_description);
+        error_emitted = true;
+      } else if (lhs->variable_referenced() != NULL
+                && lhs->variable_referenced()->read_only) {
          _mesa_glsl_error(&lhs_loc, state,
                           "assignment to read-only variable '%s'",
                           lhs->variable_referenced()->name);
@@ -770,11 +776,6 @@ get_lvalue_copy(exec_list *instructions, ir_rvalue *lvalue)
    instructions->push_tail(new(ctx) ir_assignment(new(ctx) ir_dereference_variable(var),
                                                  lvalue, NULL));
 
-   /* Once we've created this temporary, mark it read only so it's no
-    * longer considered an lvalue.
-    */
-   var->read_only = true;
-
    return new(ctx) ir_dereference_variable(var);
 }
 
@@ -936,6 +937,28 @@ check_builtin_array_max_size(const char *name, unsigned size,
    return false;
 }
 
+/**
+ * Create the constant 1, of a which is appropriate for incrementing and
+ * decrementing values of the given GLSL type.  For example, if type is vec4,
+ * this creates a constant value of 1.0 having type float.
+ *
+ * If the given type is invalid for increment and decrement operators, return
+ * a floating point 1--the error will be detected later.
+ */
+static ir_rvalue *
+constant_one_for_inc_dec(void *ctx, const glsl_type *type)
+{
+   switch (type->base_type) {
+   case GLSL_TYPE_UINT:
+      return new(ctx) ir_constant((unsigned) 1);
+   case GLSL_TYPE_INT:
+      return new(ctx) ir_constant(1);
+   default:
+   case GLSL_TYPE_FLOAT:
+      return new(ctx) ir_constant(1.0f);
+   }
+}
+
 ir_rvalue *
 ast_expression::hir(exec_list *instructions,
                    struct _mesa_glsl_parse_state *state)
@@ -1009,7 +1032,9 @@ ast_expression::hir(exec_list *instructions,
       op[0] = this->subexpressions[0]->hir(instructions, state);
       op[1] = this->subexpressions[1]->hir(instructions, state);
 
-      result = do_assignment(instructions, state, op[0], op[1], false,
+      result = do_assignment(instructions, state,
+                            this->subexpressions[0]->non_lvalue_description,
+                            op[0], op[1], false,
                             this->subexpressions[0]->get_location());
       error_emitted = result->type->is_error();
       break;
@@ -1163,7 +1188,7 @@ ast_expression::hir(exec_list *instructions,
         error_emitted = true;
       }
 
-      type = op[0]->type;
+      type = error_emitted ? glsl_type::error_type : op[0]->type;
       result = new(ctx) ir_expression(ir_unop_bit_not, type, op[0], NULL);
       break;
 
@@ -1174,15 +1199,9 @@ ast_expression::hir(exec_list *instructions,
       op[1] = get_scalar_boolean_operand(&rhs_instructions, state, this, 1,
                                         "RHS", &error_emitted);
 
-      ir_constant *op0_const = op[0]->constant_expression_value();
-      if (op0_const) {
-        if (op0_const->value.b[0]) {
-           instructions->append_list(&rhs_instructions);
-           result = op[1];
-        } else {
-           result = op0_const;
-        }
-        type = glsl_type::bool_type;
+      if (rhs_instructions.is_empty()) {
+        result = new(ctx) ir_expression(ir_binop_logic_and, op[0], op[1]);
+        type = result->type;
       } else {
         ir_variable *const tmp = new(ctx) ir_variable(glsl_type::bool_type,
                                                       "and_tmp",
@@ -1216,14 +1235,9 @@ ast_expression::hir(exec_list *instructions,
       op[1] = get_scalar_boolean_operand(&rhs_instructions, state, this, 1,
                                         "RHS", &error_emitted);
 
-      ir_constant *op0_const = op[0]->constant_expression_value();
-      if (op0_const) {
-        if (op0_const->value.b[0]) {
-           result = op0_const;
-        } else {
-           result = op[1];
-        }
-        type = glsl_type::bool_type;
+      if (rhs_instructions.is_empty()) {
+        result = new(ctx) ir_expression(ir_binop_logic_or, op[0], op[1]);
+        type = result->type;
       } else {
         ir_variable *const tmp = new(ctx) ir_variable(glsl_type::bool_type,
                                                       "or_tmp",
@@ -1289,6 +1303,7 @@ ast_expression::hir(exec_list *instructions,
                                                   op[0], op[1]);
 
       result = do_assignment(instructions, state,
+                            this->subexpressions[0]->non_lvalue_description,
                             op[0]->clone(ctx, NULL), temp_rhs, false,
                             this->subexpressions[0]->get_location());
       error_emitted = (op[0]->type->is_error());
@@ -1314,6 +1329,7 @@ ast_expression::hir(exec_list *instructions,
                                        op[0], op[1]);
 
       result = do_assignment(instructions, state,
+                            this->subexpressions[0]->non_lvalue_description,
                             op[0]->clone(ctx, NULL), temp_rhs, false,
                             this->subexpressions[0]->get_location());
       error_emitted = type->is_error();
@@ -1328,8 +1344,9 @@ ast_expression::hir(exec_list *instructions,
                                &loc);
       ir_rvalue *temp_rhs = new(ctx) ir_expression(operations[this->oper],
                                                    type, op[0], op[1]);
-      result = do_assignment(instructions, state, op[0]->clone(ctx, NULL),
-                             temp_rhs, false,
+      result = do_assignment(instructions, state,
+                            this->subexpressions[0]->non_lvalue_description,
+                            op[0]->clone(ctx, NULL), temp_rhs, false,
                              this->subexpressions[0]->get_location());
       error_emitted = op[0]->type->is_error() || op[1]->type->is_error();
       break;
@@ -1344,8 +1361,9 @@ ast_expression::hir(exec_list *instructions,
                                    state, &loc);
       ir_rvalue *temp_rhs = new(ctx) ir_expression(operations[this->oper],
                                                    type, op[0], op[1]);
-      result = do_assignment(instructions, state, op[0]->clone(ctx, NULL),
-                             temp_rhs, false,
+      result = do_assignment(instructions, state,
+                            this->subexpressions[0]->non_lvalue_description,
+                            op[0]->clone(ctx, NULL), temp_rhs, false,
                              this->subexpressions[0]->get_location());
       error_emitted = op[0]->type->is_error() || op[1]->type->is_error();
       break;
@@ -1442,11 +1460,11 @@ ast_expression::hir(exec_list *instructions,
 
    case ast_pre_inc:
    case ast_pre_dec: {
+      this->non_lvalue_description = (this->oper == ast_pre_inc)
+        ? "pre-increment operation" : "pre-decrement operation";
+
       op[0] = this->subexpressions[0]->hir(instructions, state);
-      if (op[0]->type->base_type == GLSL_TYPE_FLOAT)
-        op[1] = new(ctx) ir_constant(1.0f);
-      else
-        op[1] = new(ctx) ir_constant(1);
+      op[1] = constant_one_for_inc_dec(ctx, op[0]->type);
 
       type = arithmetic_result_type(op[0], op[1], false, state, & loc);
 
@@ -1455,6 +1473,7 @@ ast_expression::hir(exec_list *instructions,
                                        op[0], op[1]);
 
       result = do_assignment(instructions, state,
+                            this->subexpressions[0]->non_lvalue_description,
                             op[0]->clone(ctx, NULL), temp_rhs, false,
                             this->subexpressions[0]->get_location());
       error_emitted = op[0]->type->is_error();
@@ -1463,11 +1482,10 @@ ast_expression::hir(exec_list *instructions,
 
    case ast_post_inc:
    case ast_post_dec: {
+      this->non_lvalue_description = (this->oper == ast_post_inc)
+        ? "post-increment operation" : "post-decrement operation";
       op[0] = this->subexpressions[0]->hir(instructions, state);
-      if (op[0]->type->base_type == GLSL_TYPE_FLOAT)
-        op[1] = new(ctx) ir_constant(1.0f);
-      else
-        op[1] = new(ctx) ir_constant(1);
+      op[1] = constant_one_for_inc_dec(ctx, op[0]->type);
 
       error_emitted = op[0]->type->is_error() || op[1]->type->is_error();
 
@@ -1483,6 +1501,7 @@ ast_expression::hir(exec_list *instructions,
       result = get_lvalue_copy(instructions, op[0]->clone(ctx, NULL));
 
       (void)do_assignment(instructions, state,
+                         this->subexpressions[0]->non_lvalue_description,
                          op[0]->clone(ctx, NULL), temp_rhs, false,
                          this->subexpressions[0]->get_location());
 
@@ -1813,7 +1832,17 @@ process_array_type(YYLTYPE *loc, const glsl_type *base, ast_node *array_size,
 {
    unsigned length = 0;
 
-   /* FINISHME: Reject delcarations of multidimensional arrays. */
+   /* From page 19 (page 25) of the GLSL 1.20 spec:
+    *
+    *     "Only one-dimensional arrays may be declared."
+    */
+   if (base->is_array()) {
+      _mesa_glsl_error(loc, state,
+                      "invalid array of `%s' (only one-dimensional arrays "
+                      "may be declared)",
+                      base->name);
+      return glsl_type::error_type;
+   }
 
    if (array_size != NULL) {
       exec_list dummy_instructions;
@@ -1963,11 +1992,36 @@ apply_type_qualifier_to_variable(const struct ast_type_qualifier *qual,
    }
 
    if (qual->flags.q.flat)
-      var->interpolation = ir_var_flat;
+      var->interpolation = INTERP_QUALIFIER_FLAT;
    else if (qual->flags.q.noperspective)
-      var->interpolation = ir_var_noperspective;
+      var->interpolation = INTERP_QUALIFIER_NOPERSPECTIVE;
+   else if (qual->flags.q.smooth)
+      var->interpolation = INTERP_QUALIFIER_SMOOTH;
    else
-      var->interpolation = ir_var_smooth;
+      var->interpolation = INTERP_QUALIFIER_NONE;
+
+   if (var->interpolation != INTERP_QUALIFIER_NONE &&
+       !(state->target == vertex_shader && var->mode == ir_var_out) &&
+       !(state->target == fragment_shader && var->mode == ir_var_in)) {
+      const char *qual_string = NULL;
+      switch (var->interpolation) {
+      case INTERP_QUALIFIER_FLAT:
+        qual_string = "flat";
+        break;
+      case INTERP_QUALIFIER_NOPERSPECTIVE:
+        qual_string = "noperspective";
+        break;
+      case INTERP_QUALIFIER_SMOOTH:
+        qual_string = "smooth";
+        break;
+      }
+
+      _mesa_glsl_error(loc, state,
+                      "interpolation qualifier `%s' can only be applied to "
+                      "vertex shader outputs and fragment shader inputs.",
+                      qual_string);
+
+   }
 
    var->pixel_center_integer = qual->flags.q.pixel_center_integer;
    var->origin_upper_left = qual->flags.q.origin_upper_left;
@@ -2063,6 +2117,7 @@ apply_type_qualifier_to_variable(const struct ast_type_qualifier *qual,
     * The following extension do not allow the deprecated keywords:
     *
     *    GL_AMD_conservative_depth
+    *    GL_ARB_conservative_depth
     *    GL_ARB_gpu_shader5
     *    GL_ARB_separate_shader_objects
     *    GL_ARB_tesselation_shader
@@ -2095,9 +2150,11 @@ apply_type_qualifier_to_variable(const struct ast_type_qualifier *qual,
       + qual->flags.q.depth_less
       + qual->flags.q.depth_unchanged;
    if (depth_layout_count > 0
-       && !state->AMD_conservative_depth_enable) {
+       && !state->AMD_conservative_depth_enable
+       && !state->ARB_conservative_depth_enable) {
        _mesa_glsl_error(loc, state,
-                        "extension GL_AMD_conservative_depth must be enabled "
+                        "extension GL_AMD_conservative_depth or "
+                        "GL_ARB_conservative_depth must be enabled "
                        "to use depth layout qualifiers");
    } else if (depth_layout_count > 0
               && strcmp(var->name, "gl_FragDepth") != 0) {
@@ -2120,25 +2177,6 @@ apply_type_qualifier_to_variable(const struct ast_type_qualifier *qual,
        var->depth_layout = ir_depth_layout_unchanged;
    else
        var->depth_layout = ir_depth_layout_none;
-
-   /* From page 46 (page 52 of the PDF) of the GLSL ES specification:
-    *
-    *    "Array variables are l-values and may be passed to parameters
-    *     declared as out or inout. However, they may not be used as
-    *     the target of an assignment."
-    *
-    * From page 32 (page 38 of the PDF) of the GLSL 1.10 spec:
-    *
-    *    "Other binary or unary expressions, non-dereferenced arrays,
-    *     function names, swizzles with repeated fields, and constants
-    *     cannot be l-values."
-    *
-    * So we only mark 1.10 as non-lvalues, and check for array
-    * assignment in 100 specifically in do_assignment.
-    */
-   if (var->type->is_array() && state->language_version != 110) {
-      var->array_lvalue = true;
-   }
 }
 
 /**
@@ -2229,7 +2267,8 @@ get_variable_being_redeclared(ir_variable *var, ast_declaration *decl,
       earlier->interpolation = var->interpolation;
 
       /* Layout qualifiers for gl_FragDepth. */
-   } else if (state->AMD_conservative_depth_enable
+   } else if ((state->AMD_conservative_depth_enable ||
+               state->ARB_conservative_depth_enable)
              && strcmp(var->name, "gl_FragDepth") == 0
              && earlier->type == var->type
              && earlier->mode == var->mode) {
@@ -2353,12 +2392,16 @@ process_initializer(ir_variable *var, ast_declaration *decl,
       const glsl_type *initializer_type;
       if (!type->qualifier.flags.q.uniform) {
         result = do_assignment(initializer_instructions, state,
+                               NULL,
                                lhs, rhs, true,
                                type->get_location());
         initializer_type = result->type;
       } else
         initializer_type = rhs->type;
 
+      var->constant_initializer = rhs->constant_expression_value();
+      var->has_initializer = true;
+
       /* If the declared variable is an unsized array, it must inherrit
        * its full type from the initializer.  A declaration such as
        *
@@ -2463,14 +2506,32 @@ ast_declarator_list::hir(exec_list *instructions,
 
    decl_type = this->type->specifier->glsl_type(& type_name, state);
    if (this->declarations.is_empty()) {
-      if (decl_type != NULL) {
-        /* Warn if this empty declaration is not for declaring a structure.
-         */
-        if (this->type->specifier->structure == NULL) {
+      /* If there is no structure involved in the program text, there are two
+       * possible scenarios:
+       *
+       * - The program text contained something like 'vec4;'.  This is an
+       *   empty declaration.  It is valid but weird.  Emit a warning.
+       *
+       * - The program text contained something like 'S;' and 'S' is not the
+       *   name of a known structure type.  This is both invalid and weird.
+       *   Emit an error.
+       *
+       * Note that if decl_type is NULL and there is a structure involved,
+       * there must have been some sort of error with the structure.  In this
+       * case we assume that an error was already generated on this line of
+       * code for the structure.  There is no need to generate an additional,
+       * confusing error.
+       */
+      assert(this->type->specifier->structure == NULL || decl_type != NULL
+            || state->error);
+      if (this->type->specifier->structure == NULL) {
+        if (decl_type != NULL) {
            _mesa_glsl_warning(&loc, state, "empty declaration");
+        } else {
+           _mesa_glsl_error(&loc, state,
+                            "invalid type `%s' in empty declaration",
+                            type_name);
         }
-      } else {
-           _mesa_glsl_error(& loc, state, "incomplete declaration");
       }
    }
 
@@ -2498,6 +2559,8 @@ ast_declarator_list::hir(exec_list *instructions,
       if (decl->is_array) {
         var_type = process_array_type(&loc, decl_type, decl->array_size,
                                       state);
+        if (var_type->is_error())
+           continue;
       } else {
         var_type = decl_type;
       }
@@ -2650,7 +2713,7 @@ ast_declarator_list::hir(exec_list *instructions,
           && state->current_function == NULL
           && var->type->is_integer()
           && var->mode == ir_var_out
-          && var->interpolation != ir_var_flat) {
+          && var->interpolation != INTERP_QUALIFIER_FLAT) {
 
          _mesa_glsl_error(&loc, state, "If a vertex output is an integer, "
                           "then it must be qualified with 'flat'");
@@ -2823,6 +2886,18 @@ ast_declarator_list::hir(exec_list *instructions,
            _mesa_glsl_error(& loc, state,
                             "identifier `%s' uses reserved `gl_' prefix",
                             decl->identifier);
+        else if (strstr(decl->identifier, "__")) {
+           /* From page 14 (page 20 of the PDF) of the GLSL 1.10
+            * spec:
+            *
+            *     "In addition, all identifiers containing two
+            *      consecutive underscores (__) are reserved as
+            *      possible future keywords."
+            */
+           _mesa_glsl_error(& loc, state,
+                            "identifier `%s' uses reserved `__' string",
+                            decl->identifier);
+        }
 
         /* Add the variable to the symbol table.  Note that the initializer's
          * IR was already processed earlier (though it hasn't been emitted
@@ -2927,7 +3002,7 @@ ast_parameter_declarator::hir(exec_list *instructions,
       type = process_array_type(&loc, type, this->array_size, state);
    }
 
-   if (type->array_size() == 0) {
+   if (!type->is_error() && type->array_size() == 0) {
       _mesa_glsl_error(&loc, state, "arrays passed as parameters must have "
                       "a declared size.");
       type = glsl_type::error_type;
@@ -2953,6 +3028,26 @@ ast_parameter_declarator::hir(exec_list *instructions,
       type = glsl_type::error_type;
    }
 
+   /* From page 39 (page 45 of the PDF) of the GLSL 1.10 spec:
+    *
+    *    "When calling a function, expressions that do not evaluate to
+    *     l-values cannot be passed to parameters declared as out or inout."
+    *
+    * From page 32 (page 38 of the PDF) of the GLSL 1.10 spec:
+    *
+    *    "Other binary or unary expressions, non-dereferenced arrays,
+    *     function names, swizzles with repeated fields, and constants
+    *     cannot be l-values."
+    *
+    * So for GLSL 1.10, passing an array as an out or inout parameter is not
+    * allowed.  This restriction is removed in GLSL 1.20, and in GLSL ES.
+    */
+   if ((var->mode == ir_var_inout || var->mode == ir_var_out)
+       && type->is_array() && state->language_version == 110) {
+      _mesa_glsl_error(&loc, state, "Arrays cannot be out or inout parameters in GLSL 1.10");
+      type = glsl_type::error_type;
+   }
+
    instructions->push_tail(var);
 
    /* Parameter declarations do not have r-values.
@@ -3292,34 +3387,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_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->switch_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->switch_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);
@@ -3382,86 +3492,367 @@ ast_selection_statement::hir(exec_list *instructions,
 }
 
 
-void
-ast_iteration_statement::condition_to_hir(ir_loop *stmt,
-                                         struct _mesa_glsl_parse_state *state)
+ir_rvalue *
+ast_switch_statement::hir(exec_list *instructions,
+                         struct _mesa_glsl_parse_state *state)
 {
    void *ctx = state;
 
-   if (condition != NULL) {
-      ir_rvalue *const cond =
-        condition->hir(& stmt->body_instructions, 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();
 
-      if ((cond == NULL)
-         || !cond->type->is_boolean() || !cond->type->is_scalar()) {
-        YYLTYPE loc = condition->get_location();
+      _mesa_glsl_error(& loc,
+                      state,
+                      "switch-statement expression must be scalar "
+                      "integer");
+   }
 
-        _mesa_glsl_error(& loc, state,
-                         "loop condition must be scalar boolean");
-      } else {
-        /* As the first code in the loop body, generate a block that looks
-         * like 'if (!condition) break;' as the loop termination condition.
-         */
-        ir_rvalue *const not_cond =
-           new(ctx) ir_expression(ir_unop_logic_not, glsl_type::bool_type, cond,
-                                  NULL);
+   /* Track the switch-statement nesting in a stack-like manner.
+    */
+   struct glsl_switch_state saved = state->switch_state;
 
-        ir_if *const if_stmt = new(ctx) ir_if(not_cond);
+   state->switch_state.is_switch_innermost = true;
+   state->switch_state.switch_nesting_ast = this;
+   state->switch_state.labels_ht = hash_table_ctor(0, hash_table_pointer_hash,
+                                                  hash_table_pointer_compare);
+   state->switch_state.previous_default = NULL;
 
-        ir_jump *const break_stmt =
-           new(ctx) ir_loop_jump(ir_loop_jump::jump_break);
+   /* Initalize is_fallthru state to false.
+    */
+   ir_rvalue *const is_fallthru_val = new (ctx) ir_constant(false);
+   state->switch_state.is_fallthru_var =
+      new(ctx) ir_variable(glsl_type::bool_type,
+                          "switch_is_fallthru_tmp",
+                          ir_var_temporary);
+   instructions->push_tail(state->switch_state.is_fallthru_var);
 
-        if_stmt->then_instructions.push_tail(break_stmt);
-        stmt->body_instructions.push_tail(if_stmt);
-      }
-   }
-}
+   ir_dereference_variable *deref_is_fallthru_var =
+      new(ctx) ir_dereference_variable(state->switch_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->switch_state.is_break_var = new(ctx) ir_variable(glsl_type::bool_type,
+                                                          "switch_is_break_tmp",
+                                                          ir_var_temporary);
+   instructions->push_tail(state->switch_state.is_break_var);
 
-ir_rvalue *
-ast_iteration_statement::hir(exec_list *instructions,
-                            struct _mesa_glsl_parse_state *state)
-{
-   void *ctx = state;
+   ir_dereference_variable *deref_is_break_var =
+      new(ctx) ir_dereference_variable(state->switch_state.is_break_var);
+   instructions->push_tail(new(ctx) ir_assignment(deref_is_break_var,
+                                                 is_break_val,
+                                                 NULL));
 
-   /* For-loops and while-loops start a new scope, but do-while loops do not.
+   /* Cache test expression.
     */
-   if (mode != ast_do_while)
-      state->symbols->push_scope();
+   test_to_hir(instructions, state);
+   
+   /* Emit code for body of switch stmt.
+    */
+   body->hir(instructions, state);
 
-   if (init_statement != NULL)
-      init_statement->hir(instructions, state);
+   hash_table_dtor(state->switch_state.labels_ht);
 
-   ir_loop *const stmt = new(ctx) ir_loop();
-   instructions->push_tail(stmt);
+   state->switch_state = saved;
 
-   /* Track the current loop and / or switch-statement nesting.
-    */
-   ir_instruction *const nesting = state->loop_or_switch_nesting;
-   ast_iteration_statement *nesting_ast = state->loop_or_switch_nesting_ast;
+     /* Switch statements do not have r-values.
+      */
+     return NULL;
+  }
 
-   state->loop_or_switch_nesting = stmt;
-   state->loop_or_switch_nesting_ast = this;
 
-   if (mode != ast_do_while)
-      condition_to_hir(stmt, state);
+  void
+  ast_switch_statement::test_to_hir(exec_list *instructions,
+                                   struct _mesa_glsl_parse_state *state)
+  {
+     void *ctx = state;
 
-   if (body != NULL)
-      body->hir(& stmt->body_instructions, state);
+     /* Cache value of test expression.
+      */
+     ir_rvalue *const test_val =
+       test_expression->hir(instructions,
+                            state);
 
-   if (rest_expression != NULL)
-      rest_expression->hir(& stmt->body_instructions, state);
+     state->switch_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->switch_state.test_var);
 
-   if (mode == ast_do_while)
-      condition_to_hir(stmt, state);
+     instructions->push_tail(state->switch_state.test_var);
+     instructions->push_tail(new(ctx) ir_assignment(deref_test_var,
+                                                   test_val,
+                                                   NULL));
+  }
 
-   if (mode != ast_do_while)
-      state->symbols->pop_scope();
 
-   /* Restore previous nesting before returning.
-    */
-   state->loop_or_switch_nesting = nesting;
-   state->loop_or_switch_nesting_ast = nesting_ast;
+  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->switch_state.is_fallthru_var);
+     ir_dereference_variable *const deref_is_break_var =
+       new(state) ir_dereference_variable(state->switch_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->switch_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->switch_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 label_rval = this->test_value->hir(instructions, state);
+       ir_constant *label_const = label_rval->constant_expression_value();
+
+       if (!label_const) {
+          YYLTYPE loc = this->test_value->get_location();
+
+          _mesa_glsl_error(& loc, state,
+                           "switch statement case label must be a "
+                           "constant expression");
+
+          /* Stuff a dummy value in to allow processing to continue. */
+          label_const = new(ctx) ir_constant(0);
+       } else {
+          ast_expression *previous_label = (ast_expression *)
+             hash_table_find(state->switch_state.labels_ht,
+                             (void *)(uintptr_t)label_const->value.u[0]);
+
+          if (previous_label) {
+             YYLTYPE loc = this->test_value->get_location();
+             _mesa_glsl_error(& loc, state,
+                              "duplicate case value");
+
+             loc = previous_label->get_location();
+             _mesa_glsl_error(& loc, state,
+                              "this is the previous case label");
+          } else {
+             hash_table_insert(state->switch_state.labels_ht,
+                               this->test_value,
+                               (void *)(uintptr_t)label_const->value.u[0]);
+          }
+       }
+
+       ir_dereference_variable *deref_test_var =
+          new(ctx) ir_dereference_variable(state->switch_state.test_var);
+
+       ir_rvalue *const test_cond = new(ctx) ir_expression(ir_binop_all_equal,
+                                                           glsl_type::bool_type,
+                                                           label_const,
+                                                           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 */
+       if (state->switch_state.previous_default) {
+          printf("a\n");
+          YYLTYPE loc = this->get_location();
+          _mesa_glsl_error(& loc, state,
+                              "multiple default labels in one switch");
+
+          printf("b\n");
+
+          loc = state->switch_state.previous_default->get_location();
+          _mesa_glsl_error(& loc, state,
+                           "this is the first default label");
+       }
+       state->switch_state.previous_default = this;
+
+       /* 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)
+  {
+     void *ctx = state;
+
+     if (condition != NULL) {
+       ir_rvalue *const cond =
+          condition->hir(& stmt->body_instructions, state);
+
+       if ((cond == NULL)
+           || !cond->type->is_boolean() || !cond->type->is_scalar()) {
+          YYLTYPE loc = condition->get_location();
+
+          _mesa_glsl_error(& loc, state,
+                           "loop condition must be scalar boolean");
+       } else {
+          /* As the first code in the loop body, generate a block that looks
+           * like 'if (!condition) break;' as the loop termination condition.
+           */
+          ir_rvalue *const not_cond =
+             new(ctx) ir_expression(ir_unop_logic_not, glsl_type::bool_type, cond,
+                                    NULL);
+
+          ir_if *const if_stmt = new(ctx) ir_if(not_cond);
+
+          ir_jump *const break_stmt =
+             new(ctx) ir_loop_jump(ir_loop_jump::jump_break);
+
+          if_stmt->then_instructions.push_tail(break_stmt);
+          stmt->body_instructions.push_tail(if_stmt);
+       }
+     }
+  }
+
+
+  ir_rvalue *
+  ast_iteration_statement::hir(exec_list *instructions,
+                              struct _mesa_glsl_parse_state *state)
+  {
+     void *ctx = state;
+
+     /* For-loops and while-loops start a new scope, but do-while loops do not.
+      */
+     if (mode != ast_do_while)
+       state->symbols->push_scope();
+
+     if (init_statement != NULL)
+       init_statement->hir(instructions, state);
+
+     ir_loop *const stmt = new(ctx) ir_loop();
+     instructions->push_tail(stmt);
+
+     /* Track the current loop nesting.
+      */
+     ast_iteration_statement *nesting_ast = state->loop_nesting_ast;
+
+     state->loop_nesting_ast = this;
+
+     /* Likewise, indicate that following code is closest to a loop,
+      * NOT closest to a switch.
+      */
+     bool saved_is_switch_innermost = state->switch_state.is_switch_innermost;
+     state->switch_state.is_switch_innermost = false;
+
+     if (mode != ast_do_while)
+       condition_to_hir(stmt, state);
+
+     if (body != NULL)
+       body->hir(& stmt->body_instructions, state);
+
+     if (rest_expression != NULL)
+       rest_expression->hir(& stmt->body_instructions, state);
+
+     if (mode == ast_do_while)
+       condition_to_hir(stmt, state);
+
+     if (mode != ast_do_while)
+       state->symbols->pop_scope();
+
+     /* Restore previous nesting before returning.
+      */
+     state->loop_nesting_ast = nesting_ast;
+     state->switch_state.is_switch_innermost = saved_is_switch_innermost;
 
    /* Loops do not have r-values.
     */