diff options
author | Tapani Pälli <[email protected]> | 2014-08-06 09:46:54 +0300 |
---|---|---|
committer | Tapani Pälli <[email protected]> | 2014-10-20 07:55:58 +0300 |
commit | 73dd50acf6d244979c2a657906aa56d3ac60d550 (patch) | |
tree | 5b406b48aa978187ab817819442ecf365de54df3 | |
parent | 6212d2402df4ad0658cbb98ce889e35ef5f32fa3 (diff) |
glsl: implement switch flow control using a loop
Patch removes old variable based logic for handling a break inside
switch. Switch is put inside a loop so that existing infrastructure
for loop flow control can be used for the switch, now also dead code
elimination works properly.
Possible 'continue' call inside a switch needs now special handling
which is taken care of by detecting continue, breaking out and calling
continue for the outside loop.
v2: remove one unnecessary ir_expression (Curro)
Fixes following Piglit tests:
fs-exec-after-break.shader_test
fs-conditional-break.shader_test
No Piglit or es3conform regressions.
Signed-off-by: Tapani Pälli <[email protected]>
Reviewed-by: Francisco Jerez <[email protected]>
-rw-r--r-- | src/glsl/ast_to_hir.cpp | 97 | ||||
-rw-r--r-- | src/glsl/glsl_parser_extras.h | 4 |
2 files changed, 64 insertions, 37 deletions
diff --git a/src/glsl/ast_to_hir.cpp b/src/glsl/ast_to_hir.cpp index 068af295aa1..afbb3b21bbd 100644 --- a/src/glsl/ast_to_hir.cpp +++ b/src/glsl/ast_to_hir.cpp @@ -4373,7 +4373,7 @@ ast_jump_statement::hir(exec_list *instructions, * loop. */ if (state->loop_nesting_ast != NULL && - mode == ast_continue) { + mode == ast_continue && !state->switch_state.is_switch_innermost) { if (state->loop_nesting_ast->rest_expression) { state->loop_nesting_ast->rest_expression->hir(instructions, state); @@ -4385,19 +4385,27 @@ ast_jump_statement::hir(exec_list *instructions, } if (state->switch_state.is_switch_innermost && + mode == ast_continue) { + /* Set 'continue_inside' to true. */ + ir_rvalue *const true_val = new (ctx) ir_constant(true); + ir_dereference_variable *deref_continue_inside_var = + new(ctx) ir_dereference_variable(state->switch_state.continue_inside); + instructions->push_tail(new(ctx) ir_assignment(deref_continue_inside_var, + true_val)); + + /* Break out from the switch, continue for the loop will + * be called right after switch. */ + ir_loop_jump *const jump = + new(ctx) ir_loop_jump(ir_loop_jump::jump_break); + instructions->push_tail(jump); + + } else 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); - - instructions->push_tail(set_break_var); - } - else { + /* Force break out of switch by inserting a break. */ + ir_loop_jump *const jump = + new(ctx) ir_loop_jump(ir_loop_jump::jump_break); + instructions->push_tail(jump); + } else { ir_loop_jump *const jump = new(ctx) ir_loop_jump((mode == ast_break) ? ir_loop_jump::jump_break @@ -4509,19 +4517,19 @@ ast_switch_statement::hir(exec_list *instructions, instructions->push_tail(new(ctx) ir_assignment(deref_is_fallthru_var, is_fallthru_val)); - /* Initalize is_break state to false. + /* Initialize continue_inside state to false. */ - ir_rvalue *const is_break_val = new (ctx) ir_constant(false); - state->switch_state.is_break_var = + state->switch_state.continue_inside = new(ctx) ir_variable(glsl_type::bool_type, - "switch_is_break_tmp", + "continue_inside_tmp", ir_var_temporary); - instructions->push_tail(state->switch_state.is_break_var); + instructions->push_tail(state->switch_state.continue_inside); - 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)); + ir_rvalue *const false_val = new (ctx) ir_constant(false); + ir_dereference_variable *deref_continue_inside_var = + new(ctx) ir_dereference_variable(state->switch_state.continue_inside); + instructions->push_tail(new(ctx) ir_assignment(deref_continue_inside_var, + false_val)); state->switch_state.run_default = new(ctx) ir_variable(glsl_type::bool_type, @@ -4529,13 +4537,42 @@ ast_switch_statement::hir(exec_list *instructions, ir_var_temporary); instructions->push_tail(state->switch_state.run_default); + /* Loop around the switch is used for flow control. */ + ir_loop * loop = new(ctx) ir_loop(); + instructions->push_tail(loop); + /* Cache test expression. */ - test_to_hir(instructions, state); + test_to_hir(&loop->body_instructions, state); /* Emit code for body of switch stmt. */ - body->hir(instructions, state); + body->hir(&loop->body_instructions, state); + + /* Insert a break at the end to exit loop. */ + ir_loop_jump *jump = new(ctx) ir_loop_jump(ir_loop_jump::jump_break); + loop->body_instructions.push_tail(jump); + + /* If we are inside loop, check if continue got called inside switch. */ + if (state->loop_nesting_ast != NULL) { + ir_dereference_variable *deref_continue_inside = + new(ctx) ir_dereference_variable(state->switch_state.continue_inside); + ir_if *irif = new(ctx) ir_if(deref_continue_inside); + ir_loop_jump *jump = new(ctx) ir_loop_jump(ir_loop_jump::jump_continue); + + if (state->loop_nesting_ast != NULL) { + if (state->loop_nesting_ast->rest_expression) { + state->loop_nesting_ast->rest_expression->hir(&irif->then_instructions, + state); + } + if (state->loop_nesting_ast->mode == + ast_iteration_statement::ast_do_while) { + state->loop_nesting_ast->condition_to_hir(&irif->then_instructions, state); + } + } + irif->then_instructions.push_tail(jump); + instructions->push_tail(irif); + } hash_table_dtor(state->switch_state.labels_ht); @@ -4659,18 +4696,6 @@ ast_case_statement::hir(exec_list *instructions, { 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); diff --git a/src/glsl/glsl_parser_extras.h b/src/glsl/glsl_parser_extras.h index c8b94781c70..c14d74c9846 100644 --- a/src/glsl/glsl_parser_extras.h +++ b/src/glsl/glsl_parser_extras.h @@ -40,9 +40,11 @@ struct glsl_switch_state { /** Temporary variables needed for switch statement. */ ir_variable *test_var; ir_variable *is_fallthru_var; - ir_variable *is_break_var; class ast_switch_statement *switch_nesting_ast; + /** Used to detect if 'continue' was called inside a switch. */ + ir_variable *continue_inside; + /** Used to set condition if 'default' label should be chosen. */ ir_variable *run_default; |