glsl: Save and restore the whole switch state for nesting.

This stuffs them all in a struct for sanity.  Fixes piglit
glsl-1.30/execution/switch/fs-uniform-nested.

NOTE: This is a candidate for the 8.0 branch.

Reviewed-by: Ian Romanick <ian.d.romanick@intel.com>
This commit is contained in:
Eric Anholt 2012-01-28 11:26:02 -08:00
parent b8c9252570
commit 22d81f154f
3 changed files with 225 additions and 230 deletions

View file

@ -3405,7 +3405,7 @@ ast_jump_statement::hir(exec_list *instructions,
"continue may only appear in a loop"); "continue may only appear in a loop");
} else if (mode == ast_break && } else if (mode == ast_break &&
state->loop_nesting_ast == NULL && state->loop_nesting_ast == NULL &&
state->switch_nesting_ast == NULL) { state->switch_state.switch_nesting_ast == NULL) {
YYLTYPE loc = this->get_location(); YYLTYPE loc = this->get_location();
_mesa_glsl_error(& loc, state, _mesa_glsl_error(& loc, state,
@ -3423,11 +3423,11 @@ ast_jump_statement::hir(exec_list *instructions,
state); state);
} }
if (state->is_switch_innermost && if (state->switch_state.is_switch_innermost &&
mode == ast_break) { mode == ast_break) {
/* Force break out of switch by setting is_break switch state. /* Force break out of switch by setting is_break switch state.
*/ */
ir_variable *const is_break_var = state->is_break_var; ir_variable *const is_break_var = state->switch_state.is_break_var;
ir_dereference_variable *const deref_is_break_var = ir_dereference_variable *const deref_is_break_var =
new(ctx) ir_dereference_variable(is_break_var); new(ctx) ir_dereference_variable(is_break_var);
ir_constant *const true_val = new(ctx) ir_constant(true); ir_constant *const true_val = new(ctx) ir_constant(true);
@ -3530,25 +3530,22 @@ ast_switch_statement::hir(exec_list *instructions,
/* Track the switch-statement nesting in a stack-like manner. /* Track the switch-statement nesting in a stack-like manner.
*/ */
ir_variable *saved_test_var = state->test_var; struct glsl_switch_state saved = state->switch_state;
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_state.is_switch_innermost = true;
state->switch_nesting_ast = this; state->switch_state.switch_nesting_ast = this;
/* Initalize is_fallthru state to false. /* Initalize is_fallthru state to false.
*/ */
ir_rvalue *const is_fallthru_val = new (ctx) ir_constant(false); ir_rvalue *const is_fallthru_val = new (ctx) ir_constant(false);
state->is_fallthru_var = new(ctx) ir_variable(glsl_type::bool_type, state->switch_state.is_fallthru_var =
"switch_is_fallthru_tmp", new(ctx) ir_variable(glsl_type::bool_type,
ir_var_temporary); "switch_is_fallthru_tmp",
instructions->push_tail(state->is_fallthru_var); ir_var_temporary);
instructions->push_tail(state->switch_state.is_fallthru_var);
ir_dereference_variable *deref_is_fallthru_var = ir_dereference_variable *deref_is_fallthru_var =
new(ctx) ir_dereference_variable(state->is_fallthru_var); new(ctx) ir_dereference_variable(state->switch_state.is_fallthru_var);
instructions->push_tail(new(ctx) ir_assignment(deref_is_fallthru_var, instructions->push_tail(new(ctx) ir_assignment(deref_is_fallthru_var,
is_fallthru_val, is_fallthru_val,
NULL)); NULL));
@ -3556,13 +3553,13 @@ ast_switch_statement::hir(exec_list *instructions,
/* Initalize is_break state to false. /* Initalize is_break state to false.
*/ */
ir_rvalue *const is_break_val = new (ctx) ir_constant(false); ir_rvalue *const is_break_val = new (ctx) ir_constant(false);
state->is_break_var = new(ctx) ir_variable(glsl_type::bool_type, state->switch_state.is_break_var = new(ctx) ir_variable(glsl_type::bool_type,
"switch_is_break_tmp", "switch_is_break_tmp",
ir_var_temporary); ir_var_temporary);
instructions->push_tail(state->is_break_var); instructions->push_tail(state->switch_state.is_break_var);
ir_dereference_variable *deref_is_break_var = ir_dereference_variable *deref_is_break_var =
new(ctx) ir_dereference_variable(state->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, instructions->push_tail(new(ctx) ir_assignment(deref_is_break_var,
is_break_val, is_break_val,
NULL)); NULL));
@ -3575,254 +3572,248 @@ ast_switch_statement::hir(exec_list *instructions,
*/ */
body->hir(instructions, state); body->hir(instructions, state);
/* Restore previous nesting before returning. state->switch_state = saved;
*/
state->switch_nesting_ast = saved_nesting_ast;
state->is_switch_innermost = save_is_switch_innermost;
state->test_var = saved_test_var; /* Switch statements do not have r-values.
state->is_fallthru_var = saved_is_fallthru_var; */
return NULL;
/* Switch statements do not have r-values. }
*/
return NULL;
}
void void
ast_switch_statement::test_to_hir(exec_list *instructions, ast_switch_statement::test_to_hir(exec_list *instructions,
struct _mesa_glsl_parse_state *state) struct _mesa_glsl_parse_state *state)
{ {
void *ctx = state; void *ctx = state;
/* Cache value of test expression. /* Cache value of test expression.
*/ */
ir_rvalue *const test_val = ir_rvalue *const test_val =
test_expression->hir(instructions, test_expression->hir(instructions,
state); state);
state->test_var = new(ctx) ir_variable(glsl_type::int_type, state->switch_state.test_var = new(ctx) ir_variable(glsl_type::int_type,
"switch_test_tmp", "switch_test_tmp",
ir_var_temporary); ir_var_temporary);
ir_dereference_variable *deref_test_var = ir_dereference_variable *deref_test_var =
new(ctx) ir_dereference_variable(state->test_var); new(ctx) ir_dereference_variable(state->switch_state.test_var);
instructions->push_tail(state->test_var); instructions->push_tail(state->switch_state.test_var);
instructions->push_tail(new(ctx) ir_assignment(deref_test_var, instructions->push_tail(new(ctx) ir_assignment(deref_test_var,
test_val, test_val,
NULL)); NULL));
} }
ir_rvalue * ir_rvalue *
ast_switch_body::hir(exec_list *instructions, ast_switch_body::hir(exec_list *instructions,
struct _mesa_glsl_parse_state *state) struct _mesa_glsl_parse_state *state)
{ {
if (stmts != NULL) if (stmts != NULL)
stmts->hir(instructions, state); stmts->hir(instructions, state);
/* Switch bodies do not have r-values. /* Switch bodies do not have r-values.
*/ */
return NULL; return NULL;
} }
ir_rvalue * ir_rvalue *
ast_case_statement_list::hir(exec_list *instructions, ast_case_statement_list::hir(exec_list *instructions,
struct _mesa_glsl_parse_state *state) struct _mesa_glsl_parse_state *state)
{ {
foreach_list_typed (ast_case_statement, case_stmt, link, & this->cases) foreach_list_typed (ast_case_statement, case_stmt, link, & this->cases)
case_stmt->hir(instructions, state); case_stmt->hir(instructions, state);
/* Case statements do not have r-values. /* Case statements do not have r-values.
*/ */
return NULL; return NULL;
} }
ir_rvalue * ir_rvalue *
ast_case_statement::hir(exec_list *instructions, ast_case_statement::hir(exec_list *instructions,
struct _mesa_glsl_parse_state *state) struct _mesa_glsl_parse_state *state)
{ {
labels->hir(instructions, state); 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. /* Conditionally set fallthru state based on break state.
*/ */
ir_dereference_variable *const deref_fallthru_guard = ir_constant *const false_val = new(state) ir_constant(false);
new(state) ir_dereference_variable(state->is_fallthru_var); ir_dereference_variable *const deref_is_fallthru_var =
ir_if *const test_fallthru = new(state) ir_if(deref_fallthru_guard); new(state) ir_dereference_variable(state->switch_state.is_fallthru_var);
ir_dereference_variable *const deref_is_break_var =
foreach_list_typed (ast_node, stmt, link, & this->stmts) new(state) ir_dereference_variable(state->switch_state.is_break_var);
stmt->hir(& test_fallthru->then_instructions, state); 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);
instructions->push_tail(test_fallthru); /* Guard case statements depending on fallthru state.
*/
/* Case statements do not have r-values. ir_dereference_variable *const deref_fallthru_guard =
*/ new(state) ir_dereference_variable(state->switch_state.is_fallthru_var);
return NULL; 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 * ir_rvalue *
ast_case_label_list::hir(exec_list *instructions, ast_case_label_list::hir(exec_list *instructions,
struct _mesa_glsl_parse_state *state) struct _mesa_glsl_parse_state *state)
{ {
foreach_list_typed (ast_case_label, label, link, & this->labels) foreach_list_typed (ast_case_label, label, link, & this->labels)
label->hir(instructions, state); label->hir(instructions, state);
/* Case labels do not have r-values. /* Case labels do not have r-values.
*/ */
return NULL; return NULL;
} }
ir_rvalue * ir_rvalue *
ast_case_label::hir(exec_list *instructions, ast_case_label::hir(exec_list *instructions,
struct _mesa_glsl_parse_state *state) struct _mesa_glsl_parse_state *state)
{ {
void *ctx = state; void *ctx = state;
ir_dereference_variable *deref_fallthru_var = ir_dereference_variable *deref_fallthru_var =
new(ctx) ir_dereference_variable(state->is_fallthru_var); new(ctx) ir_dereference_variable(state->switch_state.is_fallthru_var);
ir_rvalue *const true_val = new(ctx) ir_constant(true);
/* If not default case, ... ir_rvalue *const true_val = new(ctx) ir_constant(true);
*/
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 = /* If not default case, ...
new(ctx) ir_dereference_variable(state->test_var); */
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_rvalue *const test_cond = new(ctx) ir_expression(ir_binop_all_equal, ir_dereference_variable *deref_test_var =
glsl_type::bool_type, new(ctx) ir_dereference_variable(state->switch_state.test_var);
test_val,
deref_test_var);
ir_assignment *set_fallthru_on_test = ir_rvalue *const test_cond = new(ctx) ir_expression(ir_binop_all_equal,
new(ctx) ir_assignment(deref_fallthru_var, glsl_type::bool_type,
true_val, test_val,
test_cond); deref_test_var);
instructions->push_tail(set_fallthru_on_test); ir_assignment *set_fallthru_on_test =
} else { /* default case */ new(ctx) ir_assignment(deref_fallthru_var,
/* Set falltrhu state. true_val,
*/ test_cond);
ir_assignment *set_fallthru =
new(ctx) ir_assignment(deref_fallthru_var, instructions->push_tail(set_fallthru_on_test);
true_val, } else { /* default case */
NULL); /* Set falltrhu state.
*/
instructions->push_tail(set_fallthru); ir_assignment *set_fallthru =
} new(ctx) ir_assignment(deref_fallthru_var,
true_val,
/* Case statements do not have r-values. NULL);
*/
return NULL; instructions->push_tail(set_fallthru);
} }
/* Case statements do not have r-values.
*/
return NULL;
}
void void
ast_iteration_statement::condition_to_hir(ir_loop *stmt, ast_iteration_statement::condition_to_hir(ir_loop *stmt,
struct _mesa_glsl_parse_state *state) struct _mesa_glsl_parse_state *state)
{ {
void *ctx = state; void *ctx = state;
if (condition != NULL) { if (condition != NULL) {
ir_rvalue *const cond = ir_rvalue *const cond =
condition->hir(& stmt->body_instructions, state); condition->hir(& stmt->body_instructions, state);
if ((cond == NULL) if ((cond == NULL)
|| !cond->type->is_boolean() || !cond->type->is_scalar()) { || !cond->type->is_boolean() || !cond->type->is_scalar()) {
YYLTYPE loc = condition->get_location(); YYLTYPE loc = condition->get_location();
_mesa_glsl_error(& loc, state, _mesa_glsl_error(& loc, state,
"loop condition must be scalar boolean"); "loop condition must be scalar boolean");
} else { } else {
/* As the first code in the loop body, generate a block that looks /* As the first code in the loop body, generate a block that looks
* like 'if (!condition) break;' as the loop termination condition. * like 'if (!condition) break;' as the loop termination condition.
*/ */
ir_rvalue *const not_cond = ir_rvalue *const not_cond =
new(ctx) ir_expression(ir_unop_logic_not, glsl_type::bool_type, cond, new(ctx) ir_expression(ir_unop_logic_not, glsl_type::bool_type, cond,
NULL); NULL);
ir_if *const if_stmt = new(ctx) ir_if(not_cond); ir_if *const if_stmt = new(ctx) ir_if(not_cond);
ir_jump *const break_stmt = ir_jump *const break_stmt =
new(ctx) ir_loop_jump(ir_loop_jump::jump_break); new(ctx) ir_loop_jump(ir_loop_jump::jump_break);
if_stmt->then_instructions.push_tail(break_stmt); if_stmt->then_instructions.push_tail(break_stmt);
stmt->body_instructions.push_tail(if_stmt); stmt->body_instructions.push_tail(if_stmt);
} }
} }
} }
ir_rvalue * ir_rvalue *
ast_iteration_statement::hir(exec_list *instructions, ast_iteration_statement::hir(exec_list *instructions,
struct _mesa_glsl_parse_state *state) struct _mesa_glsl_parse_state *state)
{ {
void *ctx = state; void *ctx = state;
/* For-loops and while-loops start a new scope, but do-while loops do not. /* For-loops and while-loops start a new scope, but do-while loops do not.
*/ */
if (mode != ast_do_while) if (mode != ast_do_while)
state->symbols->push_scope(); state->symbols->push_scope();
if (init_statement != NULL) if (init_statement != NULL)
init_statement->hir(instructions, state); init_statement->hir(instructions, state);
ir_loop *const stmt = new(ctx) ir_loop(); ir_loop *const stmt = new(ctx) ir_loop();
instructions->push_tail(stmt); instructions->push_tail(stmt);
/* Track the current loop nesting. /* Track the current loop nesting.
*/ */
ast_iteration_statement *nesting_ast = state->loop_nesting_ast; ast_iteration_statement *nesting_ast = state->loop_nesting_ast;
state->loop_nesting_ast = this; state->loop_nesting_ast = this;
/* Likewise, indicate that following code is closest to a loop, /* Likewise, indicate that following code is closest to a loop,
* NOT closest to a switch. * NOT closest to a switch.
*/ */
bool saved_is_switch_innermost = state->is_switch_innermost; bool saved_is_switch_innermost = state->switch_state.is_switch_innermost;
state->is_switch_innermost = false; state->switch_state.is_switch_innermost = false;
if (mode != ast_do_while) if (mode != ast_do_while)
condition_to_hir(stmt, state); condition_to_hir(stmt, state);
if (body != NULL) if (body != NULL)
body->hir(& stmt->body_instructions, state); body->hir(& stmt->body_instructions, state);
if (rest_expression != NULL) if (rest_expression != NULL)
rest_expression->hir(& stmt->body_instructions, state); rest_expression->hir(& stmt->body_instructions, state);
if (mode == ast_do_while) if (mode == ast_do_while)
condition_to_hir(stmt, state); condition_to_hir(stmt, state);
if (mode != ast_do_while) if (mode != ast_do_while)
state->symbols->pop_scope(); state->symbols->pop_scope();
/* Restore previous nesting before returning. /* Restore previous nesting before returning.
*/ */
state->loop_nesting_ast = nesting_ast; state->loop_nesting_ast = nesting_ast;
state->is_switch_innermost = saved_is_switch_innermost; state->switch_state.is_switch_innermost = saved_is_switch_innermost;
/* Loops do not have r-values. /* Loops do not have r-values.
*/ */

View file

@ -51,7 +51,7 @@ _mesa_glsl_parse_state::_mesa_glsl_parse_state(struct gl_context *ctx,
this->info_log = ralloc_strdup(mem_ctx, ""); this->info_log = ralloc_strdup(mem_ctx, "");
this->error = false; this->error = false;
this->loop_nesting_ast = NULL; this->loop_nesting_ast = NULL;
this->switch_nesting_ast = NULL; this->switch_state.switch_nesting_ast = NULL;
this->num_builtins_to_link = 0; this->num_builtins_to_link = 0;

View file

@ -42,6 +42,15 @@ enum _mesa_glsl_parser_targets {
struct gl_context; struct gl_context;
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;
bool is_switch_innermost; // if switch stmt is closest to break, ...
};
struct _mesa_glsl_parse_state { struct _mesa_glsl_parse_state {
_mesa_glsl_parse_state(struct gl_context *ctx, GLenum target, _mesa_glsl_parse_state(struct gl_context *ctx, GLenum target,
void *mem_ctx); void *mem_ctx);
@ -150,13 +159,8 @@ struct _mesa_glsl_parse_state {
/** Loop or switch statement containing the current instructions. */ /** Loop or switch statement containing the current instructions. */
class ast_iteration_statement *loop_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. */ struct glsl_switch_state switch_state;
ir_variable *test_var;
ir_variable *is_fallthru_var;
ir_variable *is_break_var;
/** List of structures defined in user code. */ /** List of structures defined in user code. */
const glsl_type **user_structures; const glsl_type **user_structures;