summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/glsl/ast_to_hir.cpp286
-rw-r--r--src/glsl/glsl_parser_extras.cpp3
-rw-r--r--src/glsl/glsl_parser_extras.h10
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;