diff options
-rw-r--r-- | src/glsl/ast_to_hir.cpp | 286 | ||||
-rw-r--r-- | src/glsl/glsl_parser_extras.cpp | 3 | ||||
-rw-r--r-- | src/glsl/glsl_parser_extras.h | 10 |
3 files changed, 254 insertions, 45 deletions
diff --git a/src/glsl/ast_to_hir.cpp b/src/glsl/ast_to_hir.cpp index 200d6596fab..ac090c315e7 100644 --- a/src/glsl/ast_to_hir.cpp +++ b/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); @@ -3449,52 +3464,235 @@ ir_rvalue * ast_switch_statement::hir(exec_list *instructions, struct _mesa_glsl_parse_state *state) { - // FINISHME + 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) + struct _mesa_glsl_parse_state *state) { - // FINISHME + if (stmts != NULL) + stmts->hir(instructions, state); + + /* Switch bodies do not have r-values. + */ return NULL; } ir_rvalue * -ast_case_statement::hir(exec_list *instructions, - struct _mesa_glsl_parse_state *state) +ast_case_statement_list::hir(exec_list *instructions, + struct _mesa_glsl_parse_state *state) { - // FINISHME + 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_list::hir(exec_list *instructions, - struct _mesa_glsl_parse_state *state) +ast_case_statement::hir(exec_list *instructions, + struct _mesa_glsl_parse_state *state) { - // FINISHME + 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::hir(exec_list *instructions, - struct _mesa_glsl_parse_state *state) +ast_case_label_list::hir(exec_list *instructions, + struct _mesa_glsl_parse_state *state) { - // FINISHME + 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_list::hir(exec_list *instructions, - struct _mesa_glsl_parse_state *state) +ast_case_label::hir(exec_list *instructions, + struct _mesa_glsl_parse_state *state) { - // FINISHME + 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; } @@ -3552,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_or_switch_nesting = stmt; - state->loop_or_switch_nesting_ast = this; + 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->is_switch_innermost; + state->is_switch_innermost = false; if (mode != ast_do_while) condition_to_hir(stmt, state); @@ -3577,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/src/glsl/glsl_parser_extras.cpp b/src/glsl/glsl_parser_extras.cpp index 1f3d23a8c73..23aadb143ef 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; this->num_builtins_to_link = 0; diff --git a/src/glsl/glsl_parser_extras.h b/src/glsl/glsl_parser_extras.h index 1f3404c9deb..dd932951f4e 100644 --- a/src/glsl/glsl_parser_extras.h +++ b/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; |