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 <tapani.palli@intel.com> Reviewed-by: Francisco Jerez <currojerez@riseup.net>
This commit is contained in:
+61
-36
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user