#include "glsl_parser_extras.h"
#include "ast.h"
#include "glsl_types.h"
+#include "program/hash_table.h"
#include "ir.h"
+static void
+detect_conflicting_assignments(struct _mesa_glsl_parse_state *state,
+ exec_list *instructions);
+
void
_mesa_ast_to_hir(exec_list *instructions, struct _mesa_glsl_parse_state *state)
{
ast->hir(instructions, state);
detect_recursion_unlinked(state, instructions);
+ detect_conflicting_assignments(state, instructions);
state->toplevel_ir = NULL;
}
ir_rvalue *
do_assignment(exec_list *instructions, struct _mesa_glsl_parse_state *state,
+ const char *non_lvalue_description,
ir_rvalue *lhs, ir_rvalue *rhs, bool is_initializer,
YYLTYPE lhs_loc)
{
void *ctx = state;
bool error_emitted = (lhs->type->is_error() || rhs->type->is_error());
+ ir_variable *lhs_var = lhs->variable_referenced();
+ if (lhs_var)
+ lhs_var->assigned = true;
+
if (!error_emitted) {
- if (lhs->variable_referenced() != NULL
- && lhs->variable_referenced()->read_only) {
+ if (non_lvalue_description != NULL) {
+ _mesa_glsl_error(&lhs_loc, state,
+ "assignment to %s",
+ non_lvalue_description);
+ error_emitted = true;
+ } else if (lhs->variable_referenced() != NULL
+ && lhs->variable_referenced()->read_only) {
_mesa_glsl_error(&lhs_loc, state,
"assignment to read-only variable '%s'",
lhs->variable_referenced()->name);
ir_var_temporary);
ir_dereference_variable *deref_var = new(ctx) ir_dereference_variable(var);
instructions->push_tail(var);
- instructions->push_tail(new(ctx) ir_assignment(deref_var,
- rhs,
- NULL));
+ instructions->push_tail(new(ctx) ir_assignment(deref_var, rhs));
deref_var = new(ctx) ir_dereference_variable(var);
if (!error_emitted)
- instructions->push_tail(new(ctx) ir_assignment(lhs, deref_var, NULL));
+ instructions->push_tail(new(ctx) ir_assignment(lhs, deref_var));
return new(ctx) ir_dereference_variable(var);
}
var->mode = ir_var_auto;
instructions->push_tail(new(ctx) ir_assignment(new(ctx) ir_dereference_variable(var),
- lvalue, NULL));
-
- /* Once we've created this temporary, mark it read only so it's no
- * longer considered an lvalue.
- */
- var->read_only = true;
+ lvalue));
return new(ctx) ir_dereference_variable(var);
}
op[0] = this->subexpressions[0]->hir(instructions, state);
op[1] = this->subexpressions[1]->hir(instructions, state);
- result = do_assignment(instructions, state, op[0], op[1], false,
+ result = do_assignment(instructions, state,
+ this->subexpressions[0]->non_lvalue_description,
+ op[0], op[1], false,
this->subexpressions[0]->get_location());
error_emitted = result->type->is_error();
break;
error_emitted = true;
}
- type = op[0]->type;
+ type = error_emitted ? glsl_type::error_type : op[0]->type;
result = new(ctx) ir_expression(ir_unop_bit_not, type, op[0], NULL);
break;
op[1] = get_scalar_boolean_operand(&rhs_instructions, state, this, 1,
"RHS", &error_emitted);
- ir_constant *op0_const = op[0]->constant_expression_value();
- if (op0_const) {
- if (op0_const->value.b[0]) {
- instructions->append_list(&rhs_instructions);
- result = op[1];
- } else {
- result = op0_const;
- }
- type = glsl_type::bool_type;
+ if (rhs_instructions.is_empty()) {
+ result = new(ctx) ir_expression(ir_binop_logic_and, op[0], op[1]);
+ type = result->type;
} else {
ir_variable *const tmp = new(ctx) ir_variable(glsl_type::bool_type,
"and_tmp",
stmt->then_instructions.append_list(&rhs_instructions);
ir_dereference *const then_deref = new(ctx) ir_dereference_variable(tmp);
ir_assignment *const then_assign =
- new(ctx) ir_assignment(then_deref, op[1], NULL);
+ new(ctx) ir_assignment(then_deref, op[1]);
stmt->then_instructions.push_tail(then_assign);
ir_dereference *const else_deref = new(ctx) ir_dereference_variable(tmp);
ir_assignment *const else_assign =
- new(ctx) ir_assignment(else_deref, new(ctx) ir_constant(false), NULL);
+ new(ctx) ir_assignment(else_deref, new(ctx) ir_constant(false));
stmt->else_instructions.push_tail(else_assign);
result = new(ctx) ir_dereference_variable(tmp);
op[1] = get_scalar_boolean_operand(&rhs_instructions, state, this, 1,
"RHS", &error_emitted);
- ir_constant *op0_const = op[0]->constant_expression_value();
- if (op0_const) {
- if (op0_const->value.b[0]) {
- result = op0_const;
- } else {
- result = op[1];
- }
- type = glsl_type::bool_type;
+ if (rhs_instructions.is_empty()) {
+ result = new(ctx) ir_expression(ir_binop_logic_or, op[0], op[1]);
+ type = result->type;
} else {
ir_variable *const tmp = new(ctx) ir_variable(glsl_type::bool_type,
"or_tmp",
ir_dereference *const then_deref = new(ctx) ir_dereference_variable(tmp);
ir_assignment *const then_assign =
- new(ctx) ir_assignment(then_deref, new(ctx) ir_constant(true), NULL);
+ new(ctx) ir_assignment(then_deref, new(ctx) ir_constant(true));
stmt->then_instructions.push_tail(then_assign);
stmt->else_instructions.append_list(&rhs_instructions);
ir_dereference *const else_deref = new(ctx) ir_dereference_variable(tmp);
ir_assignment *const else_assign =
- new(ctx) ir_assignment(else_deref, op[1], NULL);
+ new(ctx) ir_assignment(else_deref, op[1]);
stmt->else_instructions.push_tail(else_assign);
result = new(ctx) ir_dereference_variable(tmp);
op[0], op[1]);
result = do_assignment(instructions, state,
+ this->subexpressions[0]->non_lvalue_description,
op[0]->clone(ctx, NULL), temp_rhs, false,
this->subexpressions[0]->get_location());
error_emitted = (op[0]->type->is_error());
op[0], op[1]);
result = do_assignment(instructions, state,
+ this->subexpressions[0]->non_lvalue_description,
op[0]->clone(ctx, NULL), temp_rhs, false,
this->subexpressions[0]->get_location());
error_emitted = type->is_error();
&loc);
ir_rvalue *temp_rhs = new(ctx) ir_expression(operations[this->oper],
type, op[0], op[1]);
- result = do_assignment(instructions, state, op[0]->clone(ctx, NULL),
- temp_rhs, false,
+ result = do_assignment(instructions, state,
+ this->subexpressions[0]->non_lvalue_description,
+ op[0]->clone(ctx, NULL), temp_rhs, false,
this->subexpressions[0]->get_location());
error_emitted = op[0]->type->is_error() || op[1]->type->is_error();
break;
state, &loc);
ir_rvalue *temp_rhs = new(ctx) ir_expression(operations[this->oper],
type, op[0], op[1]);
- result = do_assignment(instructions, state, op[0]->clone(ctx, NULL),
- temp_rhs, false,
+ result = do_assignment(instructions, state,
+ this->subexpressions[0]->non_lvalue_description,
+ op[0]->clone(ctx, NULL), temp_rhs, false,
this->subexpressions[0]->get_location());
error_emitted = op[0]->type->is_error() || op[1]->type->is_error();
break;
ir_dereference *const then_deref =
new(ctx) ir_dereference_variable(tmp);
ir_assignment *const then_assign =
- new(ctx) ir_assignment(then_deref, op[1], NULL);
+ new(ctx) ir_assignment(then_deref, op[1]);
stmt->then_instructions.push_tail(then_assign);
else_instructions.move_nodes_to(& stmt->else_instructions);
ir_dereference *const else_deref =
new(ctx) ir_dereference_variable(tmp);
ir_assignment *const else_assign =
- new(ctx) ir_assignment(else_deref, op[2], NULL);
+ new(ctx) ir_assignment(else_deref, op[2]);
stmt->else_instructions.push_tail(else_assign);
result = new(ctx) ir_dereference_variable(tmp);
case ast_pre_inc:
case ast_pre_dec: {
+ this->non_lvalue_description = (this->oper == ast_pre_inc)
+ ? "pre-increment operation" : "pre-decrement operation";
+
op[0] = this->subexpressions[0]->hir(instructions, state);
op[1] = constant_one_for_inc_dec(ctx, op[0]->type);
op[0], op[1]);
result = do_assignment(instructions, state,
+ this->subexpressions[0]->non_lvalue_description,
op[0]->clone(ctx, NULL), temp_rhs, false,
this->subexpressions[0]->get_location());
error_emitted = op[0]->type->is_error();
case ast_post_inc:
case ast_post_dec: {
+ this->non_lvalue_description = (this->oper == ast_post_inc)
+ ? "post-increment operation" : "post-decrement operation";
op[0] = this->subexpressions[0]->hir(instructions, state);
op[1] = constant_one_for_inc_dec(ctx, op[0]->type);
result = get_lvalue_copy(instructions, op[0]->clone(ctx, NULL));
(void)do_assignment(instructions, state,
+ this->subexpressions[0]->non_lvalue_description,
op[0]->clone(ctx, NULL), temp_rhs, false,
this->subexpressions[0]->get_location());
ir_variable *var =
state->symbols->get_variable(this->primary_expression.identifier);
- result = new(ctx) ir_dereference_variable(var);
-
if (var != NULL) {
var->used = true;
+ result = new(ctx) ir_dereference_variable(var);
} else {
_mesa_glsl_error(& loc, state, "`%s' undeclared",
this->primary_expression.identifier);
+ result = ir_rvalue::error_value(ctx);
error_emitted = true;
}
break;
apply_type_qualifier_to_variable(const struct ast_type_qualifier *qual,
ir_variable *var,
struct _mesa_glsl_parse_state *state,
- YYLTYPE *loc)
+ YYLTYPE *loc,
+ bool ubo_qualifiers_valid)
{
if (qual->flags.q.invariant) {
if (var->used) {
else
var->interpolation = INTERP_QUALIFIER_NONE;
+ if (var->interpolation != INTERP_QUALIFIER_NONE &&
+ !(state->target == vertex_shader && var->mode == ir_var_out) &&
+ !(state->target == fragment_shader && var->mode == ir_var_in)) {
+ _mesa_glsl_error(loc, state,
+ "interpolation qualifier `%s' can only be applied to "
+ "vertex shader outputs and fragment shader inputs.",
+ var->interpolation_string());
+ }
+
var->pixel_center_integer = qual->flags.q.pixel_center_integer;
var->origin_upper_left = qual->flags.q.origin_upper_left;
if ((qual->flags.q.origin_upper_left || qual->flags.q.pixel_center_integer)
} else {
var->location = qual->location;
}
+
+ if (qual->flags.q.explicit_index) {
+ /* From the GLSL 4.30 specification, section 4.4.2 (Output
+ * Layout Qualifiers):
+ *
+ * "It is also a compile-time error if a fragment shader
+ * sets a layout index to less than 0 or greater than 1."
+ *
+ * Older specifications don't mandate a behavior; we take
+ * this as a clarification and always generate the error.
+ */
+ if (qual->index < 0 || qual->index > 1) {
+ _mesa_glsl_error(loc, state,
+ "explicit index may only be 0 or 1\n");
+ } else {
+ var->explicit_index = true;
+ var->index = qual->index;
+ }
+ }
}
+ } else if (qual->flags.q.explicit_index) {
+ _mesa_glsl_error(loc, state,
+ "explicit index requires explicit location\n");
}
/* Does the declaration use the 'layout' keyword?
*/
const bool uses_layout = qual->flags.q.pixel_center_integer
|| qual->flags.q.origin_upper_left
- || qual->flags.q.explicit_location;
+ || qual->flags.q.explicit_location; /* no need for index since it relies on location */
/* Does the declaration use the deprecated 'attribute' or 'varying'
* keywords?
var->depth_layout = ir_depth_layout_unchanged;
else
var->depth_layout = ir_depth_layout_none;
+
+ if (qual->flags.q.std140 ||
+ qual->flags.q.packed ||
+ qual->flags.q.shared) {
+ _mesa_glsl_error(loc, state,
+ "uniform block layout qualifiers std140, packed, and "
+ "shared can only be applied to uniform blocks, not "
+ "members");
+ }
+
+ if (!ubo_qualifiers_valid &&
+ (qual->flags.q.row_major || qual->flags.q.column_major)) {
+ _mesa_glsl_error(loc, state,
+ "uniform block layout qualifiers row_major and "
+ "column_major can only be applied to uniform block "
+ "members");
+ }
}
/**
const glsl_type *initializer_type;
if (!type->qualifier.flags.q.uniform) {
result = do_assignment(initializer_instructions, state,
+ NULL,
lhs, rhs, true,
type->get_location());
initializer_type = result->type;
}
apply_type_qualifier_to_variable(& this->type->qualifier, var, state,
- & loc);
+ & loc, this->ubo_qualifiers_valid);
if (this->type->qualifier.flags.q.invariant) {
if ((state->target == vertex_shader) && !(var->mode == ir_var_out ||
/* Apply any specified qualifiers to the parameter declaration. Note that
* for function parameters the default mode is 'in'.
*/
- apply_type_qualifier_to_variable(& this->type->qualifier, var, state, & loc);
+ apply_type_qualifier_to_variable(& this->type->qualifier, var, state, & loc,
+ false);
/* From page 17 (page 23 of the PDF) of the GLSL 1.20 spec:
*
"continue may only appear in a loop");
} else if (mode == ast_break &&
state->loop_nesting_ast == NULL &&
- state->switch_nesting_ast == NULL) {
+ state->switch_state.switch_nesting_ast == NULL) {
YYLTYPE loc = this->get_location();
_mesa_glsl_error(& loc, state,
state);
}
- if (state->is_switch_innermost &&
+ 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->is_break_var;
+ 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,
- NULL);
+ new(ctx) ir_assignment(deref_is_break_var, true_val);
instructions->push_tail(set_break_var);
}
*
* "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()) {
+ if (!test_expression->type->is_scalar() ||
+ !test_expression->type->is_integer()) {
YYLTYPE loc = this->test_expression->get_location();
_mesa_glsl_error(& loc,
/* 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;
+ struct glsl_switch_state saved = state->switch_state;
- state->is_switch_innermost = true;
- state->switch_nesting_ast = this;
+ state->switch_state.is_switch_innermost = true;
+ state->switch_state.switch_nesting_ast = this;
+ state->switch_state.labels_ht = hash_table_ctor(0, hash_table_pointer_hash,
+ hash_table_pointer_compare);
+ state->switch_state.previous_default = NULL;
/* 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);
+ state->switch_state.is_fallthru_var =
+ new(ctx) ir_variable(glsl_type::bool_type,
+ "switch_is_fallthru_tmp",
+ ir_var_temporary);
+ instructions->push_tail(state->switch_state.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,
- is_fallthru_val,
- NULL));
+ is_fallthru_val));
/* 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);
+ state->switch_state.is_break_var = new(ctx) ir_variable(glsl_type::bool_type,
+ "switch_is_break_tmp",
+ ir_var_temporary);
+ instructions->push_tail(state->switch_state.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,
- is_break_val,
- NULL));
+ is_break_val));
/* 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;
+ hash_table_dtor(state->switch_state.labels_ht);
- state->test_var = saved_test_var;
- state->is_fallthru_var = saved_is_fallthru_var;
+ state->switch_state = saved;
- /* Switch statements do not have r-values.
- */
+ /* Switch statements do not have r-values. */
return NULL;
}
{
void *ctx = state;
- /* Cache value of test expression.
- */
+ /* 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);
+ state->switch_state.test_var = new(ctx) ir_variable(test_val->type,
+ "switch_test_tmp",
+ ir_var_temporary);
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(new(ctx) ir_assignment(deref_test_var,
- test_val,
- NULL));
+ instructions->push_tail(state->switch_state.test_var);
+ instructions->push_tail(new(ctx) ir_assignment(deref_test_var, test_val));
}
{
if (stmts != NULL)
stmts->hir(instructions, state);
-
- /* Switch bodies do not have r-values.
- */
+
+ /* Switch bodies do not have r-values. */
return NULL;
}
-
ir_rvalue *
ast_case_statement_list::hir(exec_list *instructions,
struct _mesa_glsl_parse_state *state)
{
foreach_list_typed (ast_case_statement, case_stmt, link, & this->cases)
case_stmt->hir(instructions, state);
-
- /* Case statements do not have r-values.
- */
+
+ /* Case statements do not have r-values. */
return NULL;
}
-
ir_rvalue *
ast_case_statement::hir(exec_list *instructions,
struct _mesa_glsl_parse_state *state)
{
labels->hir(instructions, state);
-
- /* Conditionally set fallthru state based on break 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);
+ 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->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.
- */
+ /* Guard case statements depending on fallthru state. */
ir_dereference_variable *const deref_fallthru_guard =
- new(state) ir_dereference_variable(state->is_fallthru_var);
+ new(state) ir_dereference_variable(state->switch_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.
- */
+
+ /* Case statements do not have r-values. */
return NULL;
}
{
foreach_list_typed (ast_case_label, label, link, & this->labels)
label->hir(instructions, state);
-
- /* Case labels do not have r-values.
- */
+
+ /* Case labels do not have r-values. */
return NULL;
}
-
ir_rvalue *
ast_case_label::hir(exec_list *instructions,
struct _mesa_glsl_parse_state *state)
void *ctx = state;
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, ...
- */
+ /* 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_rvalue *const label_rval = this->test_value->hir(instructions, state);
+ ir_constant *label_const = label_rval->constant_expression_value();
+
+ if (!label_const) {
+ YYLTYPE loc = this->test_value->get_location();
+
+ _mesa_glsl_error(& loc, state,
+ "switch statement case label must be a "
+ "constant expression");
+
+ /* Stuff a dummy value in to allow processing to continue. */
+ label_const = new(ctx) ir_constant(0);
+ } else {
+ ast_expression *previous_label = (ast_expression *)
+ hash_table_find(state->switch_state.labels_ht,
+ (void *)(uintptr_t)label_const->value.u[0]);
+
+ if (previous_label) {
+ YYLTYPE loc = this->test_value->get_location();
+ _mesa_glsl_error(& loc, state,
+ "duplicate case value");
+
+ loc = previous_label->get_location();
+ _mesa_glsl_error(& loc, state,
+ "this is the previous case label");
+ } else {
+ hash_table_insert(state->switch_state.labels_ht,
+ this->test_value,
+ (void *)(uintptr_t)label_const->value.u[0]);
+ }
+ }
ir_dereference_variable *deref_test_var =
- new(ctx) ir_dereference_variable(state->test_var);
+ new(ctx) ir_dereference_variable(state->switch_state.test_var);
ir_rvalue *const test_cond = new(ctx) ir_expression(ir_binop_all_equal,
- glsl_type::bool_type,
- test_val,
+ label_const,
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.
- */
+ if (state->switch_state.previous_default) {
+ YYLTYPE loc = this->get_location();
+ _mesa_glsl_error(& loc, state,
+ "multiple default labels in one switch");
+
+ loc = state->switch_state.previous_default->get_location();
+ _mesa_glsl_error(& loc, state,
+ "this is the first default label");
+ }
+ state->switch_state.previous_default = this;
+
+ /* Set falltrhu state. */
ir_assignment *set_fallthru =
- new(ctx) ir_assignment(deref_fallthru_var,
- true_val,
- NULL);
-
+ new(ctx) ir_assignment(deref_fallthru_var, true_val);
+
instructions->push_tail(set_fallthru);
}
-
- /* Case statements do not have r-values.
- */
+
+ /* Case statements do not have r-values. */
return NULL;
}
-
void
ast_iteration_statement::condition_to_hir(ir_loop *stmt,
struct _mesa_glsl_parse_state *state)
* like 'if (!condition) break;' as the loop termination condition.
*/
ir_rvalue *const not_cond =
- new(ctx) ir_expression(ir_unop_logic_not, glsl_type::bool_type, cond,
- NULL);
+ new(ctx) ir_expression(ir_unop_logic_not, cond);
ir_if *const if_stmt = new(ctx) ir_if(not_cond);
ir_loop *const stmt = new(ctx) ir_loop();
instructions->push_tail(stmt);
- /* Track the current loop nesting.
- */
+ /* Track the current loop nesting. */
ast_iteration_statement *nesting_ast = state->loop_nesting_ast;
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;
+ bool saved_is_switch_innermost = state->switch_state.is_switch_innermost;
+ state->switch_state.is_switch_innermost = false;
if (mode != ast_do_while)
condition_to_hir(stmt, state);
if (mode != ast_do_while)
state->symbols->pop_scope();
- /* Restore previous nesting before returning.
- */
+ /* Restore previous nesting before returning. */
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.
*/
"arrays");
return NULL;
}
- if (this->type_specifier != ast_float
- && this->type_specifier != ast_int) {
+ if (strcmp(this->type_name, "float") != 0 &&
+ strcmp(this->type_name, "int") != 0) {
_mesa_glsl_error(&loc, state,
"default precision statements apply only to types "
"float and int");
*/
return NULL;
}
+
+static struct gl_uniform_block *
+get_next_uniform_block(struct _mesa_glsl_parse_state *state)
+{
+ if (state->num_uniform_blocks >= state->uniform_block_array_size) {
+ state->uniform_block_array_size *= 2;
+ if (state->uniform_block_array_size <= 4)
+ state->uniform_block_array_size = 4;
+
+ state->uniform_blocks = reralloc(state,
+ state->uniform_blocks,
+ struct gl_uniform_block,
+ state->uniform_block_array_size);
+ }
+
+ memset(&state->uniform_blocks[state->num_uniform_blocks],
+ 0, sizeof(*state->uniform_blocks));
+ return &state->uniform_blocks[state->num_uniform_blocks++];
+}
+
+ir_rvalue *
+ast_uniform_block::hir(exec_list *instructions,
+ struct _mesa_glsl_parse_state *state)
+{
+ /* The ast_uniform_block has a list of ast_declarator_lists. We
+ * need to turn those into ir_variables with an association
+ * with this uniform block.
+ */
+ struct gl_uniform_block *ubo = get_next_uniform_block(state);
+ ubo->Name = ralloc_strdup(state->uniform_blocks, this->block_name);
+
+ unsigned int num_variables = 0;
+ foreach_list_typed(ast_declarator_list, decl_list, link, &declarations) {
+ foreach_list_const(node, &decl_list->declarations) {
+ num_variables++;
+ }
+ }
+
+ bool block_row_major = this->layout.flags.q.row_major;
+
+ ubo->Uniforms = rzalloc_array(state->uniform_blocks,
+ struct gl_uniform_buffer_variable,
+ num_variables);
+
+ foreach_list_typed(ast_declarator_list, decl_list, link, &declarations) {
+ exec_list declared_variables;
+
+ decl_list->hir(&declared_variables, state);
+
+ foreach_list_const(node, &declared_variables) {
+ ir_variable *var = (ir_variable *)node;
+
+ struct gl_uniform_buffer_variable *ubo_var =
+ &ubo->Uniforms[ubo->NumUniforms++];
+
+ var->uniform_block = ubo - state->uniform_blocks;
+
+ ubo_var->Name = ralloc_strdup(state->uniform_blocks, var->name);
+ ubo_var->Type = var->type;
+ ubo_var->Buffer = ubo - state->uniform_blocks;
+ ubo_var->Offset = 0; /* Assigned at link time. */
+
+ if (var->type->is_matrix() ||
+ (var->type->is_array() && var->type->fields.array->is_matrix())) {
+ ubo_var->RowMajor = block_row_major;
+ if (decl_list->type->qualifier.flags.q.row_major)
+ ubo_var->RowMajor = true;
+ else if (decl_list->type->qualifier.flags.q.column_major)
+ ubo_var->RowMajor = false;
+ }
+
+ /* From the GL_ARB_uniform_buffer_object spec:
+ *
+ * "Sampler types are not allowed inside of uniform
+ * blocks. All other types, arrays, and structures
+ * allowed for uniforms are allowed within a uniform
+ * block."
+ */
+ if (var->type->contains_sampler()) {
+ YYLTYPE loc = decl_list->get_location();
+ _mesa_glsl_error(&loc, state,
+ "Uniform in non-default uniform block contains sampler\n");
+ }
+ }
+
+ instructions->append_list(&declared_variables);
+ }
+
+ return NULL;
+}
+
+static void
+detect_conflicting_assignments(struct _mesa_glsl_parse_state *state,
+ exec_list *instructions)
+{
+ bool gl_FragColor_assigned = false;
+ bool gl_FragData_assigned = false;
+ bool user_defined_fs_output_assigned = false;
+ ir_variable *user_defined_fs_output = NULL;
+
+ /* It would be nice to have proper location information. */
+ YYLTYPE loc;
+ memset(&loc, 0, sizeof(loc));
+
+ foreach_list(node, instructions) {
+ ir_variable *var = ((ir_instruction *)node)->as_variable();
+
+ if (!var || !var->assigned)
+ continue;
+
+ if (strcmp(var->name, "gl_FragColor") == 0)
+ gl_FragColor_assigned = true;
+ else if (strcmp(var->name, "gl_FragData") == 0)
+ gl_FragData_assigned = true;
+ else if (strncmp(var->name, "gl_", 3) != 0) {
+ if (state->target == fragment_shader &&
+ (var->mode == ir_var_out || var->mode == ir_var_inout)) {
+ user_defined_fs_output_assigned = true;
+ user_defined_fs_output = var;
+ }
+ }
+ }
+
+ /* From the GLSL 1.30 spec:
+ *
+ * "If a shader statically assigns a value to gl_FragColor, it
+ * may not assign a value to any element of gl_FragData. If a
+ * shader statically writes a value to any element of
+ * gl_FragData, it may not assign a value to
+ * gl_FragColor. That is, a shader may assign values to either
+ * gl_FragColor or gl_FragData, but not both. Multiple shaders
+ * linked together must also consistently write just one of
+ * these variables. Similarly, if user declared output
+ * variables are in use (statically assigned to), then the
+ * built-in variables gl_FragColor and gl_FragData may not be
+ * assigned to. These incorrect usages all generate compile
+ * time errors."
+ */
+ if (gl_FragColor_assigned && gl_FragData_assigned) {
+ _mesa_glsl_error(&loc, state, "fragment shader writes to both "
+ "`gl_FragColor' and `gl_FragData'\n");
+ } else if (gl_FragColor_assigned && user_defined_fs_output_assigned) {
+ _mesa_glsl_error(&loc, state, "fragment shader writes to both "
+ "`gl_FragColor' and `%s'\n",
+ user_defined_fs_output->name);
+ } else if (gl_FragData_assigned && user_defined_fs_output_assigned) {
+ _mesa_glsl_error(&loc, state, "fragment shader writes to both "
+ "`gl_FragData' and `%s'\n",
+ user_defined_fs_output->name);
+ }
+}