error_emitted = true;
break;
- case ast_logic_and:
- case ast_logic_xor:
- case ast_logic_or:
+ case ast_logic_and: {
op[0] = this->subexpressions[0]->hir(instructions, state);
- op[1] = this->subexpressions[1]->hir(instructions, state);
if (!op[0]->type->is_boolean() || !op[0]->type->is_scalar()) {
YYLTYPE loc = this->subexpressions[0]->get_location();
error_emitted = true;
}
- if (!op[1]->type->is_boolean() || !op[1]->type->is_scalar()) {
- YYLTYPE loc = this->subexpressions[1]->get_location();
+ ir_constant *op0_const = op[0]->constant_expression_value();
+ if (op0_const) {
+ if (op0_const->value.b[0]) {
+ op[1] = this->subexpressions[1]->hir(instructions, state);
+
+ if (!op[1]->type->is_boolean() || !op[1]->type->is_scalar()) {
+ YYLTYPE loc = this->subexpressions[1]->get_location();
+
+ _mesa_glsl_error(& loc, state,
+ "RHS of `%s' must be scalar boolean",
+ operator_string(this->oper));
+ error_emitted = true;
+ }
+ result = op[1];
+ } else {
+ result = op0_const;
+ }
+ type = glsl_type::bool_type;
+ } else {
+ ir_if *const stmt = new ir_if(op[0]);
+ instructions->push_tail(stmt);
- _mesa_glsl_error(& loc, state, "RHS of `%s' must be scalar boolean",
+ op[1] = this->subexpressions[1]->hir(&stmt->then_instructions, state);
+
+ if (!op[1]->type->is_boolean() || !op[1]->type->is_scalar()) {
+ YYLTYPE loc = this->subexpressions[1]->get_location();
+
+ _mesa_glsl_error(& loc, state,
+ "RHS of `%s' must be scalar boolean",
+ operator_string(this->oper));
+ error_emitted = true;
+ }
+
+ ir_variable *const tmp = generate_temporary(glsl_type::bool_type,
+ instructions, state);
+
+ ir_dereference *const then_deref = new ir_dereference(tmp);
+ ir_assignment *const then_assign =
+ new ir_assignment(then_deref, op[1], NULL);
+ stmt->then_instructions.push_tail(then_assign);
+
+ ir_dereference *const else_deref = new ir_dereference(tmp);
+ ir_assignment *const else_assign =
+ new ir_assignment(else_deref, new ir_constant(false), NULL);
+ stmt->else_instructions.push_tail(else_assign);
+
+ result = new ir_dereference(tmp);
+ type = tmp->type;
+ }
+ break;
+ }
+
+ case ast_logic_or: {
+ op[0] = this->subexpressions[0]->hir(instructions, state);
+
+ if (!op[0]->type->is_boolean() || !op[0]->type->is_scalar()) {
+ YYLTYPE loc = this->subexpressions[0]->get_location();
+
+ _mesa_glsl_error(& loc, state, "LHS of `%s' must be scalar boolean",
operator_string(this->oper));
error_emitted = true;
}
+ ir_constant *op0_const = op[0]->constant_expression_value();
+ if (op0_const) {
+ if (op0_const->value.b[0]) {
+ result = op0_const;
+ } else {
+ op[1] = this->subexpressions[1]->hir(instructions, state);
+
+ if (!op[1]->type->is_boolean() || !op[1]->type->is_scalar()) {
+ YYLTYPE loc = this->subexpressions[1]->get_location();
+
+ _mesa_glsl_error(& loc, state,
+ "RHS of `%s' must be scalar boolean",
+ operator_string(this->oper));
+ error_emitted = true;
+ }
+ result = op[1];
+ }
+ type = glsl_type::bool_type;
+ } else {
+ ir_if *const stmt = new ir_if(op[0]);
+ instructions->push_tail(stmt);
+
+ ir_variable *const tmp = generate_temporary(glsl_type::bool_type,
+ instructions, state);
+
+ op[1] = this->subexpressions[1]->hir(&stmt->then_instructions, state);
+
+ if (!op[1]->type->is_boolean() || !op[1]->type->is_scalar()) {
+ YYLTYPE loc = this->subexpressions[1]->get_location();
+
+ _mesa_glsl_error(& loc, state, "RHS of `%s' must be scalar boolean",
+ operator_string(this->oper));
+ error_emitted = true;
+ }
+
+ ir_dereference *const then_deref = new ir_dereference(tmp);
+ ir_assignment *const then_assign =
+ new ir_assignment(then_deref, new ir_constant(true), NULL);
+ stmt->then_instructions.push_tail(then_assign);
+
+ ir_dereference *const else_deref = new ir_dereference(tmp);
+ ir_assignment *const else_assign =
+ new ir_assignment(else_deref, op[1], NULL);
+ stmt->else_instructions.push_tail(else_assign);
+
+ result = new ir_dereference(tmp);
+ type = tmp->type;
+ }
+ break;
+ }
+
+ case ast_logic_xor:
+ op[0] = this->subexpressions[0]->hir(instructions, state);
+ op[1] = this->subexpressions[1]->hir(instructions, state);
+
+
result = new ir_expression(operations[this->oper], glsl_type::bool_type,
op[0], op[1]);
type = glsl_type::bool_type;
{
const struct glsl_type *type;
- if (this->type_specifier == ast_struct) {
+ if ((this->type_specifier == ast_struct) && (this->type_name == NULL)) {
/* FINISHME: Handle annonymous structures. */
type = NULL;
} else {
if (qual->centroid)
var->centroid = 1;
- if (qual->attribute && state->target == fragment_shader) {
+ if (qual->attribute && state->target != vertex_shader) {
var->type = glsl_type::error_type;
_mesa_glsl_error(loc, state,
"`attribute' variables may not be declared in the "
- "fragment shader");
+ "%s shader",
+ _mesa_glsl_shader_target_name(state->target));
}
/* From page 25 (page 31 of the PDF) of the GLSL 1.10 spec:
else
var->mode = ir_var_auto;
+ if (qual->uniform)
+ var->shader_in = true;
+ if (qual->varying) {
+ if (qual->in)
+ var->shader_in = true;
+ if (qual->out)
+ var->shader_out = true;
+ }
+
if (qual->flat)
var->interpolation = ir_var_flat;
else if (qual->noperspective)
struct simple_node *ptr;
const struct glsl_type *decl_type;
const char *type_name = NULL;
+ ir_rvalue *result = NULL;
+ YYLTYPE loc = this->get_location();
+ /* The type specifier may contain a structure definition. Process that
+ * before any of the variable declarations.
+ */
+ (void) this->type->specifier->hir(instructions, state);
/* FINISHME: Handle vertex shader "invariant" declarations that do not
* FINISHME: include a type. These re-declare built-in variables to be
*/
decl_type = this->type->specifier->glsl_type(& type_name, state);
+ if (is_empty_list(&this->declarations)) {
+ /* There are only two valid cases where the declaration list can be
+ * empty.
+ *
+ * 1. The declaration is setting the default precision of a built-in
+ * type (e.g., 'precision highp vec4;').
+ *
+ * 2. Adding 'invariant' to an existing vertex shader output.
+ */
+
+ if (this->type->qualifier.invariant) {
+ } else if (decl_type != NULL) {
+ } else {
+ _mesa_glsl_error(& loc, state, "incomplete declaration");
+ }
+ }
foreach (ptr, &this->declarations) {
struct ast_declaration *const decl = (struct ast_declaration * )ptr;
const struct glsl_type *var_type;
struct ir_variable *var;
- YYLTYPE loc = this->get_location();
/* FINISHME: Emit a warning if a variable declaration shadows a
* FINISHME: declaration at a higher scope.
* FINISHME: required or not.
*/
- if (var->type->array_size() <= earlier->max_array_access) {
+ if (var->type->array_size() <= (int)earlier->max_array_access) {
YYLTYPE loc = this->get_location();
_mesa_glsl_error(& loc, state, "array size must be > %u due to "
if ((var->mode == ir_var_in) && (state->current_function == NULL)) {
_mesa_glsl_error(& initializer_loc, state,
"cannot initialize %s shader input / %s",
- (state->target == vertex_shader)
- ? "vertex" : "fragment",
+ _mesa_glsl_shader_target_name(state->target),
(state->target == vertex_shader)
? "attribute" : "varying");
}
bool temp = var->read_only;
if (this->type->qualifier.constant)
var->read_only = false;
- (void) do_assignment(instructions, state, lhs, rhs,
- this->get_location());
+ result = do_assignment(instructions, state, lhs, rhs,
+ this->get_location());
var->read_only = temp;
}
}
assert(added_variable);
}
- /* Variable declarations do not have r-values.
+
+ /* Generally, variable declarations do not have r-values. However,
+ * one is used for the declaration in
+ *
+ * while (bool b = some_condition()) {
+ * ...
+ * }
+ *
+ * so we return the rvalue from the last seen declaration here.
*/
- return NULL;
+ return result;
}
}
-static bool
-parameter_lists_match(exec_list *list_a, exec_list *list_b)
-{
- exec_list_iterator iter_a = list_a->iterator();
- exec_list_iterator iter_b = list_b->iterator();
-
- while (iter_a.has_next()) {
- ir_variable *a = (ir_variable *)iter_a.get();
- ir_variable *b = (ir_variable *)iter_b.get();
-
- /* If all of the parameters from the other parameter list have been
- * exhausted, the lists have different length and, by definition,
- * do not match.
- */
- if (!iter_b.has_next())
- return false;
-
- /* If the types of the parameters do not match, the parameters lists
- * are different.
- */
- if (a->type != b->type)
- return false;
-
- iter_a.next();
- iter_b.next();
- }
-
- return true;
-}
-
-
ir_rvalue *
ast_function::hir(exec_list *instructions,
struct _mesa_glsl_parse_state *state)
exec_list hir_parameters;
- /* The prototype part of a function does not generate anything in the IR
- * instruction stream.
- */
- (void) instructions;
-
/* Convert the list of function parameters to HIR now so that they can be
* used below to compare this function's signature with previously seen
* signatures for functions with the same name.
const char *const name = identifier;
f = state->symbols->get_function(name);
if (f != NULL) {
- foreach_iter(exec_list_iterator, iter, *f) {
- sig = (struct ir_function_signature *) iter.get();
+ ir_function_signature *sig = f->exact_matching_signature(&hir_parameters);
+ if (sig != NULL) {
+ const char *badvar = sig->qualifiers_match(&hir_parameters);
+ if (badvar != NULL) {
+ YYLTYPE loc = this->get_location();
- /* Compare the parameter list of the function being defined to the
- * existing function. If the parameter lists match, then the return
- * type must also match and the existing function must not have a
- * definition.
- */
- if (parameter_lists_match(& hir_parameters, & sig->parameters)) {
- /* FINISHME: Compare return types. */
+ _mesa_glsl_error(&loc, state, "function `%s' parameter `%s' "
+ "qualifiers don't match prototype", name, badvar);
+ }
- if (is_definition && (sig->definition != NULL)) {
- YYLTYPE loc = this->get_location();
+ if (sig->return_type != return_type) {
+ YYLTYPE loc = this->get_location();
- _mesa_glsl_error(& loc, state, "function `%s' redefined", name);
- sig = NULL;
- break;
- }
+ _mesa_glsl_error(&loc, state, "function `%s' return type doesn't "
+ "match prototype", name);
}
- sig = NULL;
- }
+ if (is_definition && sig->is_defined) {
+ YYLTYPE loc = this->get_location();
+ _mesa_glsl_error(& loc, state, "function `%s' redefined", name);
+ sig = NULL;
+ }
+ }
} else if (state->symbols->name_declared_this_scope(name)) {
/* This function name shadows a non-function use of the same name.
*/
} else {
f = new ir_function(name);
state->symbols->add_function(f->name, f);
+
+ /* Emit the new function header */
+ instructions->push_tail(f);
}
/* Verify the return type of main() */
if (sig == NULL) {
sig = new ir_function_signature(return_type);
f->add_signature(sig);
- } else if (is_definition) {
- /* Destroy all of the previous parameter information. The previous
- * parameter information comes from the function prototype, and it can
- * either include invalid parameter names or may not have names at all.
- */
- foreach_iter(exec_list_iterator, iter, sig->parameters) {
- assert(((ir_instruction *) iter.get())->as_variable() != NULL);
-
- iter.remove();
- delete iter.get();
- }
}
- hir_parameters.move_nodes_to(& sig->parameters);
+ sig->replace_parameters(&hir_parameters);
signature = sig;
/* Function declarations (prototypes) do not have r-values.
assert(state->current_function == NULL);
state->current_function = signature;
- ir_label *label = new ir_label(signature->function_name());
- if (signature->definition == NULL) {
- signature->definition = label;
- }
- instructions->push_tail(label);
-
/* Duplicate parameters declared in the prototype as concrete variables.
* Add these to the symbol table.
*/
state->symbols->push_scope();
foreach_iter(exec_list_iterator, iter, signature->parameters) {
- ir_variable *const proto = ((ir_instruction *) iter.get())->as_variable();
-
- assert(proto != NULL);
+ ir_variable *const var = ((ir_instruction *) iter.get())->as_variable();
- ir_variable *const var = proto->clone();
-
- instructions->push_tail(var);
+ assert(var != NULL);
/* The only way a parameter would "exist" is if two parameters have
* the same name.
}
}
- /* Convert the body of the function to HIR, and append the resulting
- * instructions to the list that currently consists of the function label
- * and the function parameters.
- */
- this->body->hir(instructions, state);
+ /* Convert the body of the function to HIR. */
+ this->body->hir(&signature->body, state);
+ signature->is_defined = true;
state->symbols->pop_scope();
struct _mesa_glsl_parse_state *state)
{
- if (mode == ast_return) {
+ switch (mode) {
+ case ast_return: {
ir_return *inst;
assert(state->current_function);
_mesa_glsl_error(& loc, state,
"`return` with a value, in function `%s' "
"returning void",
- state->current_function->definition->label);
+ state->current_function->function_name());
}
ir_expression *const ret = (ir_expression *)
_mesa_glsl_error(& loc, state,
"`return' with no value, in function %s returning "
"non-void",
- state->current_function->definition->label);
+ state->current_function->function_name());
}
inst = new ir_return;
}
instructions->push_tail(inst);
+ break;
}
- if (mode == ast_discard) {
+ case ast_discard:
/* FINISHME: discard support */
if (state->target != fragment_shader) {
YYLTYPE loc = this->get_location();
_mesa_glsl_error(& loc, state,
"`discard' may only appear in a fragment shader");
}
+ break;
+
+ 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) {
+ 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();
+
+ if (loop != NULL) {
+ ir_loop_jump *const jump =
+ new ir_loop_jump(loop,
+ (mode == ast_break)
+ ? ir_loop_jump::jump_break
+ : ir_loop_jump::jump_continue);
+ instructions->push_tail(jump);
+ }
+ }
+
+ break;
}
/* Jump instructions do not have r-values.
*/
return NULL;
}
+
+
+void
+ast_iteration_statement::condition_to_hir(ir_loop *stmt,
+ struct _mesa_glsl_parse_state *state)
+{
+ if (condition != NULL) {
+ ir_rvalue *const cond =
+ condition->hir(& stmt->body_instructions, state);
+
+ if ((cond == NULL)
+ || !cond->type->is_boolean() || !cond->type->is_scalar()) {
+ YYLTYPE loc = condition->get_location();
+
+ _mesa_glsl_error(& loc, state,
+ "loop condition must be scalar boolean");
+ } else {
+ /* As the first code in the loop body, generate a block that looks
+ * like 'if (!condition) break;' as the loop termination condition.
+ */
+ ir_rvalue *const not_cond =
+ new ir_expression(ir_unop_logic_not, glsl_type::bool_type, cond,
+ NULL);
+
+ ir_if *const if_stmt = new ir_if(not_cond);
+
+ ir_jump *const break_stmt =
+ new ir_loop_jump(stmt, ir_loop_jump::jump_break);
+
+ if_stmt->then_instructions.push_tail(break_stmt);
+ stmt->body_instructions.push_tail(if_stmt);
+ }
+ }
+}
+
+
+ir_rvalue *
+ast_iteration_statement::hir(exec_list *instructions,
+ struct _mesa_glsl_parse_state *state)
+{
+ /* For-loops and while-loops start a new scope, but do-while loops do not.
+ */
+ if (mode != ast_do_while)
+ state->symbols->push_scope();
+
+ if (init_statement != NULL)
+ init_statement->hir(instructions, state);
+
+ ir_loop *const stmt = new ir_loop();
+ instructions->push_tail(stmt);
+
+ /* Track the current loop and / or switch-statement nesting.
+ */
+ ir_instruction *const nesting = state->loop_or_switch_nesting;
+ state->loop_or_switch_nesting = stmt;
+
+ if (mode != ast_do_while)
+ condition_to_hir(stmt, state);
+
+ if (body != NULL) {
+ ast_node *node = (ast_node *) body;
+ do {
+ node->hir(& stmt->body_instructions, state);
+ node = (ast_node *) node->next;
+ } while (node != body);
+ }
+
+ if (rest_expression != NULL)
+ rest_expression->hir(& stmt->body_instructions, state);
+
+ if (mode == ast_do_while)
+ condition_to_hir(stmt, state);
+
+ if (mode != ast_do_while)
+ state->symbols->pop_scope();
+
+ /* Restore previous nesting before returning.
+ */
+ state->loop_or_switch_nesting = nesting;
+
+ /* Loops do not have r-values.
+ */
+ return NULL;
+}
+
+
+ir_rvalue *
+ast_type_specifier::hir(exec_list *instructions,
+ struct _mesa_glsl_parse_state *state)
+{
+ if (this->structure != NULL)
+ return this->structure->hir(instructions, state);
+
+ return NULL;
+}
+
+
+ir_rvalue *
+ast_struct_specifier::hir(exec_list *instructions,
+ struct _mesa_glsl_parse_state *state)
+{
+ simple_node *ptr;
+ unsigned decl_count = 0;
+
+ /* Make an initial pass over the list of structure fields to determine how
+ * many there are. Each element in this list is an ast_declarator_list.
+ * This means that we actually need to count the number of elements in the
+ * 'declarations' list in each of the elements.
+ */
+ foreach (ptr, & this->declarations) {
+ ast_declarator_list *decl_list = (ast_declarator_list *) ptr;
+ simple_node *decl_ptr;
+
+ foreach (decl_ptr, & decl_list->declarations) {
+ decl_count++;
+ }
+ }
+
+
+ /* Allocate storage for the structure fields and process the field
+ * declarations. As the declarations are processed, try to also convert
+ * the types to HIR. This ensures that structure definitions embedded in
+ * other structure definitions are processed.
+ */
+ glsl_struct_field *const fields = (glsl_struct_field *)
+ malloc(sizeof(*fields) * decl_count);
+
+ unsigned i = 0;
+ foreach (ptr, & this->declarations) {
+ ast_declarator_list *decl_list = (ast_declarator_list *) ptr;
+ simple_node *decl_ptr;
+ const char *type_name;
+
+ decl_list->type->specifier->hir(instructions, state);
+
+ const glsl_type *decl_type =
+ decl_list->type->specifier->glsl_type(& type_name, state);
+
+ foreach (decl_ptr, & decl_list->declarations) {
+ ast_declaration *const decl = (ast_declaration *) decl_ptr;
+ const struct glsl_type *const field_type =
+ (decl->is_array)
+ ? process_array_type(decl_type, decl->array_size, state)
+ : decl_type;
+
+ fields[i].type = (field_type != NULL)
+ ? field_type : glsl_type::error_type;
+ fields[i].name = decl->identifier;
+ i++;
+ }
+ }
+
+ assert(i == decl_count);
+
+ const char *name;
+ if (this->name == NULL) {
+ static unsigned anon_count = 1;
+ char buf[32];
+
+ snprintf(buf, sizeof(buf), "#anon_struct_%04x", anon_count);
+ anon_count++;
+
+ name = strdup(buf);
+ } else {
+ name = this->name;
+ }
+
+ glsl_type *t = new glsl_type(fields, decl_count, name);
+
+ YYLTYPE loc = this->get_location();
+ if (!state->symbols->add_type(name, t)) {
+ _mesa_glsl_error(& loc, state, "struct `%s' previously defined", name);
+ } else {
+ /* This logic is a bit tricky. It is an error to declare a structure at
+ * global scope if there is also a function with the same name.
+ */
+ if ((state->current_function == NULL)
+ && (state->symbols->get_function(name) != NULL)) {
+ _mesa_glsl_error(& loc, state, "name `%s' previously defined", name);
+ } else {
+ t->generate_constructor(state->symbols);
+ }
+
+ const glsl_type **s = (const glsl_type **)
+ realloc(state->user_structures,
+ sizeof(state->user_structures[0]) *
+ (state->num_user_structures + 1));
+ if (s != NULL) {
+ s[state->num_user_structures] = t;
+ state->user_structures = s;
+ state->num_user_structures++;
+ }
+ }
+
+ /* Structure type definitions do not have r-values.
+ */
+ return NULL;
+}