summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTapani Pälli <[email protected]>2014-08-06 09:46:54 +0300
committerTapani Pälli <[email protected]>2014-10-20 07:55:58 +0300
commit73dd50acf6d244979c2a657906aa56d3ac60d550 (patch)
tree5b406b48aa978187ab817819442ecf365de54df3
parent6212d2402df4ad0658cbb98ce889e35ef5f32fa3 (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.cpp97
-rw-r--r--src/glsl/glsl_parser_extras.h4
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;