-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

On 06/17/2011 05:43 PM, Dan McCabe wrote:
> Beware! Here be dragons!
> 
> Up until now modyfing the GLSL compiler has been pretty straightforward.
> This is where things get interesting.
> 
> Switch statement processing leverages infrastructure that was previously
> created (specifically for break statements, which are encountered in both
> loops and switch statements). Rather than generating new IR constructs,
> which also requires creating new code generation and optimization, we take
> advantage of the existing infrastructure and IR. Fortunately, the bread
> crumbs that were left in the code from previous work suggested a solution
> to processing switch statements.
> 
> The basic concept is that a switch statement generates a loop. The test
> expression is evaluated and saved in a temporary variable, which is used
> for comparing the subsequent case labels. We also manage a "fallthru" state
> that allows us to maintain the sequential semantics of the switch statement,
> where cases fall through to the next case in the absence of a break statement.
> The loop itself also has an explicit break instruction appended at the end
> to force the termination of the loop (the subject of this commit).
> 
> Case labels and default cases manipulate the fallthru state. If a case label
> equals the test expression, a fall through condition is encountered and the
> fallthru state is set to true. Similarly, if a default case is encountered,
> the fallthru state is set to true. Note that the fallthru state must be
> initialized at the start of the switch statement (at the start of the loop)
> to be false. Thereafter, the fallthru state will only ever monotonically
> transition to true if a case is matched or if a default case is encountered.
> It will never transition from true to false.
> 
> The statements associated with each case are then guarded by the fallthru
> state. Only if the fallthru state is true do case statements get executed.
> 
> We will illustrate the analogous loop and conditional code that a switch
> statement corresponds to by examining an example.
> 
> Consider the following switch statement:
>       switch (42) {
>       case 0:
>       case 1:
>               gl_FragColor = vec4(1.0, 2.0, 3.0, 4.0);
>       case 2:
>       case 3:
>               gl_FragColor = vec4(4.0, 3.0, 2.0, 1.0);
>               break;
>       case 4:
>       default:
>               gl_FragColor = vec4(0.0, 0.0, 0.0, 0.0);
>       }
> 
> Note that case 0 and case 1 fall through to cases 2 and 3 if they occur.
> 
> Note that case 4 and the default case must be reached explicitly, since cases
> 2 and 3 break at the end of their case.
> 
> Finally, note that case 4 and the default case don't break but simply fall
> through to the end of the switch.
> 
> For this code, the equivalent code can be expressed as:
>       do {
>               int test = 42; // capture test expression
>               bool fallthru = false; // prevent initial fall throughs
> 
>               if (test == 0) // case 0
>                       fallthru = true;
>               if (test == 1)  // case 1
>                       fallthru = true;
>               if (fallthru) {
>                       gl_FragColor = vec4(1.0, 2.0, 3.0, 4.0);
>               }
> 
>               if (test == 2)  // case 2
>                       fallthru = true;
>               if (test == 3) // case 3
>                       fallthru = true;
>               if (fallthru) {
>                       gl_FragColor = vec4(4.0, 3.0, 2.0, 1.0);
>                       break; // most AST/IR processing previously in place
>               }
> 
>               if (test == 4)  // case 4
>                       fallthru = true;
>               fallthru = true; // default case
>               if (fallthru) {
>                       gl_FragColor = vec4(0.0, 0.0, 0.0, 0.0);
>               }
> 
>               break; // implicit exit from loop at end of switch
>       } while (true);
> 
> Although we expressed our transformation into a do/while loop, we could
> have used any other loop structure to explain the concept. However, we do
> want to point out the existance of the last break statement which gets
> implicitly generated to force loop termination at the end of the switch
> statement.
> ---
>  src/glsl/ast_to_hir.cpp         |  237 
> +++++++++++++++++++++++++++++++--------
>  src/glsl/glsl_parser_extras.cpp |    3 +-
>  src/glsl/glsl_parser_extras.h   |    8 +-
>  3 files changed, 198 insertions(+), 50 deletions(-)
> 
> diff --git a/src/glsl/ast_to_hir.cpp b/src/glsl/ast_to_hir.cpp
> index 553e459..628bbfc 100644
> --- a/src/glsl/ast_to_hir.cpp
> +++ b/src/glsl/ast_to_hir.cpp
> @@ -3184,39 +3184,37 @@ 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,
> +      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 =
> -            new(ctx) ir_loop_jump((mode == ast_break)
> -                                  ? ir_loop_jump::jump_break
> -                                  : ir_loop_jump::jump_continue);
> -         instructions->push_tail(jump);
> -      }
> +      ir_loop_jump *const jump =
> +         new(ctx) ir_loop_jump((mode == ast_break)
> +                               ? ir_loop_jump::jump_break
> +                               : ir_loop_jump::jump_continue);
> +      instructions->push_tail(jump);
>        }

I think this will generate the wrong code for:

        for (i = 0; i < 10; i++) {
                switch (i) {
                        default: continue;
                }
        }

It seems like that will generate some approximation of:

        for (i = 0; i < 10; i++) {
                do {
                        continue;
                        break;
                } while (true);
        }

Right?  This is why I had originally tracked loops and switch-statements
in a single stack.  In this situation, you'd want to set a "do a
continue" flag in the switch-statement and emit a break (from the
switch).  Something like:


        for (i = 0; i < 10; i++) {
                bool do_cont = false;

                do {
                        do_cont = true;
                        break;
                        break;
                } while (true);

                if (do_cont)
                        continue;
        }

>        break;
> @@ -3278,52 +3276,200 @@ ir_rvalue *
>  ast_switch_statement::hir(exec_list *instructions,
>                         struct _mesa_glsl_parse_state *state)
>  {
> -   // TODO - implement me!!!
> +   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");
> +   }
> +
> +   ir_loop *const stmt = new(ctx) ir_loop();
> +   instructions->push_tail(stmt);
> +
> +   /* Track the switch-statement nesting in a stack-like manner.
> +    */
> +   ir_variable *saved_test_var = state->test_var;
> +   ir_variable *saved_fallthru_var = state->fallthru_var;
> +   ast_switch_statement *saved_nesting_ast = state->switch_nesting_ast;
> +
> +   state->switch_nesting_ast = this;
> +
> +   test_to_hir(stmt, state);
> +   
> +   body->hir(& stmt->body_instructions, state);
> +
> +   /* Restore previous nesting before returning.
> +    */
> +   state->switch_nesting_ast = saved_nesting_ast;
> +   state->test_var = saved_test_var;
> +   state->fallthru_var = saved_fallthru_var;
> +
> +   /* Switch statements do not have r-values.
> +    */
>     return NULL;
>  }
>  
>  
> -ir_rvalue *
> -ast_switch_body::hir(exec_list *instructions,
> -                       struct _mesa_glsl_parse_state *state)
> +void
> +ast_switch_statement::test_to_hir(ir_loop *stmt,
> +                               struct _mesa_glsl_parse_state *state)
>  {
> -   // TODO - implement me!!!
> -   return NULL;
> +   void *ctx = state;
> +
> +   // generate code to cache test value
> +   ir_rvalue *const test_val =
> +      test_expression->hir(& stmt->body_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);
> +
> +   stmt->body_instructions.push_tail(state->test_var);
> +   stmt->body_instructions.push_tail(new(ctx) ir_assignment(deref_test_var,
> +                                                         test_val,
> +                                                         NULL));
> +
> +   // generate code to initalize fallthru boolean
> +   ir_rvalue *const false_val = new (ctx) ir_constant(false);
> +   state->fallthru_var = new(ctx) ir_variable(glsl_type::bool_type,
> +                                           "switch_fallthru_tmp",
> +                                           ir_var_temporary);
> +   ir_dereference_variable *deref_fallthru_var =
> +      new(ctx) ir_dereference_variable(state->fallthru_var);
> +
> +   stmt->body_instructions.push_tail(state->fallthru_var);
> +   stmt->body_instructions.push_tail(new(ctx) 
> ir_assignment(deref_fallthru_var,
> +                                                         false_val,
> +                                                         NULL));
>  }
>  
>  
>  ir_rvalue *
> -ast_case_statement::hir(exec_list *instructions,
> -                       struct _mesa_glsl_parse_state *state)
> +ast_switch_body::hir(exec_list *instructions,
> +                  struct _mesa_glsl_parse_state *state)
>  {
> -   // TODO - implement me!!!
> +   void *ctx = state;
> +   
> +   if (stmts != NULL)
> +      stmts->hir(instructions, state);
> +      
> +   // emit implicit break to force loop exit
> +   ir_jump *const break_stmt =
> +      new(ctx) ir_loop_jump(ir_loop_jump::jump_break);
> +
> +   instructions->push_tail(break_stmt);
> +
> +   /* 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)
> +                          struct _mesa_glsl_parse_state *state)
>  {
> -   // TODO - implement me!!!
> +   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_label::hir(exec_list *instructions,
> -                       struct _mesa_glsl_parse_state *state)
> +ast_case_statement::hir(exec_list *instructions,
> +                     struct _mesa_glsl_parse_state *state)
>  {
> -   // TODO - implement me!!!
> +   void *ctx = state;
> +
> +   labels->hir(instructions, state);
> +
> +   ir_dereference_variable *deref_fallthru_var =
> +      new(ctx) ir_dereference_variable(state->fallthru_var);
> +   
> +   ir_if *const test_fallthru = new(ctx) ir_if(deref_fallthru_var);
> +
> +   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)
> +                      struct _mesa_glsl_parse_state *state)
>  {
> -   // TODO - implement me!!!
> +   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->fallthru_var);
> +   
> +   ir_rvalue *const true_val = new(ctx) ir_constant(true);
> +
> +   ir_assignment *assign_fallthru_true =
> +      new(ctx) ir_assignment(deref_fallthru_var,
> +                          true_val,
> +                          NULL);
> +   
> +   if (this->test_value != NULL) { // if not default, ...
> +      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_if *const if_stmt = new(ctx) ir_if(test_cond);
> +
> +      if_stmt->then_instructions.push_tail(assign_fallthru_true);
> +
> +      instructions->push_tail(if_stmt);
> +   } else { // default
> +      instructions->push_tail(assign_fallthru_true);
> +   }
> +   
> +   /* Case statements do not have r-values.
> +    */
>     return NULL;
>  }
>  
> @@ -3381,13 +3527,11 @@ 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_or_switch_nesting = stmt;
> -   state->loop_or_switch_nesting_ast = this;
> +   state->loop_nesting_ast = this;
>  
>     if (mode != ast_do_while)
>        condition_to_hir(stmt, state);
> @@ -3406,8 +3550,7 @@ 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;
>  
>     /* Loops do not have r-values.
>      */
> diff --git a/src/glsl/glsl_parser_extras.cpp b/src/glsl/glsl_parser_extras.cpp
> index f2ed518..d83526f 100644
> --- a/src/glsl/glsl_parser_extras.cpp
> +++ b/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;
>  
>     /* Set default language version and extensions */
>     this->language_version = 110;
> diff --git a/src/glsl/glsl_parser_extras.h b/src/glsl/glsl_parser_extras.h
> index 878d2ae..3c216d0 100644
> --- a/src/glsl/glsl_parser_extras.h
> +++ b/src/glsl/glsl_parser_extras.h
> @@ -143,8 +143,12 @@ 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;
> +
> +   /** Temporary variables needed for switch statement. */
> +   ir_variable *test_var;
> +   ir_variable *fallthru_var;
>  
>     /** List of structures defined in user code. */
>     const glsl_type **user_structures;

-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.11 (GNU/Linux)
Comment: Using GnuPG with Fedora - http://enigmail.mozdev.org/

iEYEARECAAYFAk3/zroACgkQX1gOwKyEAw8QMQCfZADJyR9t8njfynKcR5uCGYOp
F4oAoIKAQEUeHvUbgiz+3XRETFmRLNaA
=po2G
-----END PGP SIGNATURE-----
_______________________________________________
mesa-dev mailing list
mesa-dev@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/mesa-dev

Reply via email to