* Is this function call actually a constructor?
*/
bool cons;
+ ir_rvalue *
+ handle_method(exec_list *instructions,
+ struct _mesa_glsl_parse_state *state);
+};
+
+class ast_subroutine_list : public ast_node
+{
+public:
+ virtual void print(void) const;
+ exec_list declarations;
};
class ast_array_specifier : public ast_node {
/* tess control output layout */
unsigned vertices:1;
/** \} */
+
+ /** \name Qualifiers for GL_ARB_shader_subroutine */
+ /** \{ */
+ unsigned subroutine:1; /**< Is this marked 'subroutine' */
+ unsigned subroutine_def:1; /**< Is this marked 'subroutine' with a list of types */
+ /** \} */
}
/** \brief Set of flags, accessed by name. */
q;
ast_type_qualifier q,
ast_node* &node);
+ ast_subroutine_list *subroutine_list;
};
class ast_declarator_list;
#include "glsl_types.h"
#include "ir.h"
#include "main/core.h" /* for MIN2 */
+#include "main/shaderobj.h"
static ir_rvalue *
convert_component(ir_rvalue *src, const glsl_type *desired_type);
static ir_rvalue *
generate_call(exec_list *instructions, ir_function_signature *sig,
exec_list *actual_parameters,
+ ir_variable *sub_var,
+ ir_rvalue *array_idx,
struct _mesa_glsl_parse_state *state)
{
void *ctx = state;
deref = new(ctx) ir_dereference_variable(var);
}
- ir_call *call = new(ctx) ir_call(sig, deref, actual_parameters);
+
+ ir_call *call = new(ctx) ir_call(sig, deref, actual_parameters, sub_var, array_idx);
instructions->push_tail(call);
/* Also emit any necessary out-parameter conversions. */
return sig;
}
+static ir_function_signature *
+match_subroutine_by_name(const char *name,
+ exec_list *actual_parameters,
+ struct _mesa_glsl_parse_state *state,
+ ir_variable **var_r)
+{
+ void *ctx = state;
+ ir_function_signature *sig = NULL;
+ ir_function *f, *found = NULL;
+ const char *new_name;
+ ir_variable *var;
+ bool is_exact = false;
+
+ new_name = ralloc_asprintf(ctx, "%s_%s", _mesa_shader_stage_to_subroutine_prefix(state->stage), name);
+ var = state->symbols->get_variable(new_name);
+ if (!var)
+ return NULL;
+
+ for (int i = 0; i < state->num_subroutine_types; i++) {
+ f = state->subroutine_types[i];
+ if (strcmp(f->name, var->type->without_array()->name))
+ continue;
+ found = f;
+ break;
+ }
+
+ if (!found)
+ return NULL;
+ *var_r = var;
+ sig = found->matching_signature(state, actual_parameters,
+ false, &is_exact);
+ return sig;
+}
+
static void
print_function_prototypes(_mesa_glsl_parse_state *state, YYLTYPE *loc,
ir_function *f)
&actual_parameters, state);
}
+ir_rvalue *
+ast_function_expression::handle_method(exec_list *instructions,
+ struct _mesa_glsl_parse_state *state)
+{
+ const ast_expression *field = subexpressions[0];
+ ir_rvalue *op;
+ ir_rvalue *result;
+ void *ctx = state;
+ /* Handle "method calls" in GLSL 1.20 - namely, array.length() */
+ YYLTYPE loc = get_location();
+ state->check_version(120, 300, &loc, "methods not supported");
+
+ const char *method;
+ method = field->primary_expression.identifier;
+
+ op = field->subexpressions[0]->hir(instructions, state);
+ if (strcmp(method, "length") == 0) {
+ if (!this->expressions.is_empty()) {
+ _mesa_glsl_error(&loc, state, "length method takes no arguments");
+ goto fail;
+ }
+
+ if (op->type->is_array()) {
+ if (op->type->is_unsized_array()) {
+ _mesa_glsl_error(&loc, state, "length called on unsized array");
+ goto fail;
+ }
+
+ result = new(ctx) ir_constant(op->type->array_size());
+ } else if (op->type->is_vector()) {
+ if (state->ARB_shading_language_420pack_enable) {
+ /* .length() returns int. */
+ result = new(ctx) ir_constant((int) op->type->vector_elements);
+ } else {
+ _mesa_glsl_error(&loc, state, "length method on matrix only available"
+ "with ARB_shading_language_420pack");
+ goto fail;
+ }
+ } else if (op->type->is_matrix()) {
+ if (state->ARB_shading_language_420pack_enable) {
+ /* .length() returns int. */
+ result = new(ctx) ir_constant((int) op->type->matrix_columns);
+ } else {
+ _mesa_glsl_error(&loc, state, "length method on matrix only available"
+ "with ARB_shading_language_420pack");
+ goto fail;
+ }
+ } else {
+ _mesa_glsl_error(&loc, state, "length called on scalar.");
+ goto fail;
+ }
+ } else {
+ _mesa_glsl_error(&loc, state, "unknown method: `%s'", method);
+ goto fail;
+ }
+ return result;
+fail:
+ return ir_rvalue::error_value(ctx);
+}
ir_rvalue *
ast_function_expression::hir(exec_list *instructions,
* 2. methods - Only the .length() method of array types.
* 3. functions - Calls to regular old functions.
*
- * Method calls are actually detected when the ast_field_selection
- * expression is handled.
*/
if (is_constructor()) {
const ast_type_specifier *type = (ast_type_specifier *) subexpressions[0];
&actual_parameters,
ctx);
}
+ } else if (subexpressions[0]->oper == ast_field_selection) {
+ return handle_method(instructions, state);
} else {
const ast_expression *id = subexpressions[0];
- const char *func_name = id->primary_expression.identifier;
+ const char *func_name;
YYLTYPE loc = get_location();
exec_list actual_parameters;
+ ir_variable *sub_var = NULL;
+ ir_rvalue *array_idx = NULL;
+
+ if (id->oper == ast_array_index) {
+ func_name = id->subexpressions[0]->primary_expression.identifier;
+ array_idx = id->subexpressions[1]->hir(instructions, state);
+ } else {
+ func_name = id->primary_expression.identifier;
+ }
process_parameters(instructions, &actual_parameters, &this->expressions,
state);
match_function_by_name(func_name, &actual_parameters, state);
ir_rvalue *value = NULL;
+ if (sig == NULL) {
+ sig = match_subroutine_by_name(func_name, &actual_parameters, state, &sub_var);
+ }
+
if (sig == NULL) {
no_matching_function_error(func_name, &loc, &actual_parameters, state);
value = ir_rvalue::error_value(ctx);
/* an error has already been emitted */
value = ir_rvalue::error_value(ctx);
} else {
- value = generate_call(instructions, sig, &actual_parameters, state);
+ value = generate_call(instructions, sig, &actual_parameters, sub_var, array_idx, state);
if (!value) {
ir_variable *const tmp = new(ctx) ir_variable(glsl_type::void_type,
"void_var",
#include "ast.h"
#include "glsl_types.h"
#include "program/hash_table.h"
+#include "main/shaderobj.h"
#include "ir.h"
#include "ir_builder.h"
}
}
+ if (qual->flags.q.subroutine && !qual->flags.q.uniform) {
+ _mesa_glsl_error(loc, state,
+ "`subroutine' may only be applied to uniforms, "
+ "subroutine type declarations, or function definitions");
+ }
+
if (qual->flags.q.constant || qual->flags.q.attribute
|| qual->flags.q.uniform
|| (qual->flags.q.varying && (state->stage == MESA_SHADER_FRAGMENT)))
foreach_list_typed (ast_declaration, decl, link, &this->declarations) {
const struct glsl_type *var_type;
ir_variable *var;
-
+ const char *identifier = decl->identifier;
/* FINISHME: Emit a warning if a variable declaration shadows a
* FINISHME: declaration at a higher scope.
*/
continue;
}
+ if (this->type->qualifier.flags.q.subroutine) {
+ const glsl_type *t;
+ const char *name;
+
+ t = state->symbols->get_type(this->type->specifier->type_name);
+ if (!t)
+ _mesa_glsl_error(& loc, state,
+ "invalid type in declaration of `%s'",
+ decl->identifier);
+ name = ralloc_asprintf(ctx, "%s_%s", _mesa_shader_stage_to_subroutine_prefix(state->stage), decl->identifier);
+
+ identifier = name;
+
+ }
var_type = process_array_type(&loc, decl_type, decl->array_specifier,
state);
- var = new(ctx) ir_variable(var_type, decl->identifier, ir_var_auto);
+ var = new(ctx) ir_variable(var_type, identifier, ir_var_auto);
/* The 'varying in' and 'varying out' qualifiers can only be used with
* ARB_geometry_shader4 and EXT_geometry_shader4, which we don't support
*/
if (this->type->qualifier.flags.q.attribute) {
mode = "attribute";
+ } else if (this->type->qualifier.flags.q.subroutine) {
+ mode = "subroutine uniform";
} else if (this->type->qualifier.flags.q.uniform) {
mode = "uniform";
} else if (this->type->qualifier.flags.q.varying) {
if (state->stage == MESA_SHADER_TESS_CTRL) {
handle_tess_ctrl_shader_output_decl(state, loc, var);
}
+ } else if (var->type->contains_subroutine()) {
+ /* declare subroutine uniforms as hidden */
+ var->data.how_declared = ir_var_hidden;
}
/* Integer fragment inputs must be qualified with 'flat'. In GLSL ES,
ir_function *f = NULL;
ir_function_signature *sig = NULL;
exec_list hir_parameters;
+ YYLTYPE loc = this->get_location();
const char *const name = identifier;
return_type = glsl_type::error_type;
}
+ /* ARB_shader_subroutine states:
+ * "Subroutine declarations cannot be prototyped. It is an error to prepend
+ * subroutine(...) to a function declaration."
+ */
+ if (this->return_type->qualifier.flags.q.subroutine_def && !is_definition) {
+ YYLTYPE loc = this->get_location();
+ _mesa_glsl_error(&loc, state,
+ "function declaration `%s' cannot have subroutine prepended",
+ name);
+ }
+
/* From page 56 (page 62 of the PDF) of the GLSL 1.30 spec:
* "No qualifier is allowed on the return type of a function."
*/
f = state->symbols->get_function(name);
if (f == NULL) {
f = new(ctx) ir_function(name);
- if (!state->symbols->add_function(f)) {
- /* This function name shadows a non-function use of the same name. */
- YYLTYPE loc = this->get_location();
-
- _mesa_glsl_error(&loc, state, "function name `%s' conflicts with "
- "non-function", name);
- return NULL;
+ if (!this->return_type->qualifier.flags.q.subroutine) {
+ if (!state->symbols->add_function(f)) {
+ /* This function name shadows a non-function use of the same name. */
+ YYLTYPE loc = this->get_location();
+ _mesa_glsl_error(&loc, state, "function name `%s' conflicts with "
+ "non-function", name);
+ return NULL;
+ }
}
-
emit_function(state, f);
}
sig->replace_parameters(&hir_parameters);
signature = sig;
+ if (this->return_type->qualifier.flags.q.subroutine_def) {
+ int idx;
+
+ f->num_subroutine_types = this->return_type->qualifier.subroutine_list->declarations.length();
+ f->subroutine_types = ralloc_array(state, const struct glsl_type *,
+ f->num_subroutine_types);
+ idx = 0;
+ foreach_list_typed(ast_declaration, decl, link, &this->return_type->qualifier.subroutine_list->declarations) {
+ const struct glsl_type *type;
+ /* the subroutine type must be already declared */
+ type = state->symbols->get_type(decl->identifier);
+ if (!type) {
+ _mesa_glsl_error(& loc, state, "unknown type '%s' in subroutine function definition", decl->identifier);
+ }
+ f->subroutine_types[idx++] = type;
+ }
+ state->subroutines = (ir_function **)reralloc(state, state->subroutines,
+ ir_function *,
+ state->num_subroutines + 1);
+ state->subroutines[state->num_subroutines] = f;
+ state->num_subroutines++;
+
+ }
+
+ if (this->return_type->qualifier.flags.q.subroutine) {
+ if (!state->symbols->add_type(this->identifier, glsl_type::get_subroutine_instance(this->identifier))) {
+ _mesa_glsl_error(& loc, state, "type '%s' previously defined", this->identifier);
+ return NULL;
+ }
+ state->subroutine_types = (ir_function **)reralloc(state, state->subroutine_types,
+ ir_function *,
+ state->num_subroutine_types + 1);
+ state->subroutine_types[state->num_subroutine_types] = f;
+ state->num_subroutine_types++;
+
+ f->is_subroutine = true;
+ }
+
/* Function declarations (prototypes) do not have r-values.
*/
return NULL;
bool
ast_fully_specified_type::has_qualifiers() const
{
- return this->qualifier.flags.i != 0;
+ /* 'subroutine' isnt a real qualifier. */
+ ast_type_qualifier subroutine_only;
+ subroutine_only.flags.i = 0;
+ subroutine_only.flags.q.subroutine = 1;
+ subroutine_only.flags.q.subroutine_def = 1;
+ return (this->qualifier.flags.i & ~subroutine_only.flags.i) != 0;
}
bool ast_type_qualifier::has_interpolation() const
return classify_identifier(state, yytext);
}
+\. { struct _mesa_glsl_parse_state *state = yyextra;
+ state->is_field = true;
+ return DOT_TOK; }
+
. { return yytext[0]; }
%%
int
classify_identifier(struct _mesa_glsl_parse_state *state, const char *name)
{
+ if (state->is_field) {
+ state->is_field = false;
+ return FIELD_SELECTION;
+ }
if (state->symbols->get_variable(name) || state->symbols->get_function(name))
return IDENTIFIER;
else if (state->symbols->get_type(name))
ast_case_statement *case_statement;
ast_case_statement_list *case_statement_list;
ast_interface_block *interface_block;
-
+ ast_subroutine_list *subroutine_list;
struct {
ast_node *cond;
ast_expression *rest;
%token PRAGMA_OPTIMIZE_ON PRAGMA_OPTIMIZE_OFF
%token PRAGMA_INVARIANT_ALL
%token LAYOUT_TOK
-
+%token DOT_TOK
/* Reserved words that are not actually used in the grammar.
*/
%token ASM CLASS UNION ENUM TYPEDEF TEMPLATE THIS PACKED_TOK GOTO
%type <type_qualifier> layout_qualifier_id_list layout_qualifier_id
%type <type_qualifier> interface_block_layout_qualifier
%type <type_qualifier> memory_qualifier
+%type <type_qualifier> subroutine_qualifier
+%type <subroutine_list> subroutine_type_list
%type <type_qualifier> interface_qualifier
%type <type_specifier> type_specifier
%type <type_specifier> type_specifier_nonarray
%type <expression> function_call_generic
%type <expression> function_call_or_method
%type <expression> function_call
-%type <expression> method_call_generic
-%type <expression> method_call_header_with_parameters
-%type <expression> method_call_header_no_parameters
-%type <expression> method_call_header
%type <n> assignment_operator
%type <n> unary_operator
%type <expression> function_identifier
{
$$ = $1;
}
- | postfix_expression '.' any_identifier
+ | postfix_expression DOT_TOK FIELD_SELECTION
{
void *ctx = state;
$$ = new(ctx) ast_expression(ast_field_selection, $1, NULL, NULL);
function_call_or_method:
function_call_generic
- | postfix_expression '.' method_call_generic
- {
- void *ctx = state;
- $$ = new(ctx) ast_expression(ast_field_selection, $1, $3, NULL);
- $$->set_location_range(@1, @3);
- }
;
function_call_generic:
$$ = new(ctx) ast_function_expression($1);
$$->set_location(@1);
}
- | variable_identifier
- {
- void *ctx = state;
- ast_expression *callee = new(ctx) ast_expression($1);
- callee->set_location(@1);
- $$ = new(ctx) ast_function_expression(callee);
- $$->set_location(@1);
- }
- | FIELD_SELECTION
+ | postfix_expression
{
void *ctx = state;
- ast_expression *callee = new(ctx) ast_expression($1);
- callee->set_location(@1);
- $$ = new(ctx) ast_function_expression(callee);
+ $$ = new(ctx) ast_function_expression($1);
$$->set_location(@1);
}
;
-method_call_generic:
- method_call_header_with_parameters ')'
- | method_call_header_no_parameters ')'
- ;
-
-method_call_header_no_parameters:
- method_call_header VOID_TOK
- | method_call_header
- ;
-
-method_call_header_with_parameters:
- method_call_header assignment_expression
- {
- $$ = $1;
- $$->set_location(@1);
- $$->expressions.push_tail(& $2->link);
- }
- | method_call_header_with_parameters ',' assignment_expression
- {
- $$ = $1;
- $$->set_location(@1);
- $$->expressions.push_tail(& $3->link);
- }
- ;
-
// Grammar Note: Constructors look like methods, but lexical
// analysis recognized most of them as keywords. They are now
// recognized through "type_specifier".
-method_call_header:
- variable_identifier '('
- {
- void *ctx = state;
- ast_expression *callee = new(ctx) ast_expression($1);
- callee->set_location(@1);
- $$ = new(ctx) ast_function_expression(callee);
- $$->set_location(@1);
- }
- ;
// Grammar Note: No traditional style type casts.
unary_expression:
$$->return_type = $1;
$$->identifier = $2;
- state->symbols->add_function(new(state) ir_function($2));
+ if ($1->qualifier.flags.q.subroutine) {
+ /* add type for IDENTIFIER search */
+ state->symbols->add_type($2, glsl_type::get_subroutine_instance($2));
+ } else
+ state->symbols->add_function(new(state) ir_function($2));
state->symbols->push_scope();
}
;
}
;
+subroutine_qualifier:
+ SUBROUTINE
+ {
+ memset(& $$, 0, sizeof($$));
+ $$.flags.q.subroutine = 1;
+ }
+ | SUBROUTINE '(' subroutine_type_list ')'
+ {
+ memset(& $$, 0, sizeof($$));
+ $$.flags.q.subroutine_def = 1;
+ $$.subroutine_list = $3;
+ }
+ ;
+
+subroutine_type_list:
+ any_identifier
+ {
+ void *ctx = state;
+ ast_declaration *decl = new(ctx) ast_declaration($1, NULL, NULL);
+ decl->set_location(@1);
+
+ $$ = new(ctx) ast_subroutine_list();
+ $$->declarations.push_tail(&decl->link);
+ }
+ | subroutine_type_list ',' any_identifier
+ {
+ void *ctx = state;
+ ast_declaration *decl = new(ctx) ast_declaration($3, NULL, NULL);
+ decl->set_location(@3);
+
+ $$ = $1;
+ $$->declarations.push_tail(&decl->link);
+ }
+ ;
+
interpolation_qualifier:
SMOOTH
{
| interpolation_qualifier
| layout_qualifier
| memory_qualifier
+ | subroutine_qualifier
| precision_qualifier
{
memset(&$$, 0, sizeof($$));
$$ = $1;
$$.merge_qualifier(&@1, state, $2);
}
+ | subroutine_qualifier type_qualifier
+ {
+ $$ = $1;
+ $$.merge_qualifier(&@1, state, $2);
+ }
| auxiliary_storage_qualifier type_qualifier
{
if ($2.has_auxiliary_storage()) {
this->all_invariant = false;
this->user_structures = NULL;
this->num_user_structures = 0;
+ this->num_subroutines = 0;
+ this->subroutines = NULL;
+ this->num_subroutine_types = 0;
+ this->subroutine_types = NULL;
/* supported_versions should be large enough to support the known desktop
* GLSL versions plus 3 GLES versions (ES 1.00, ES 3.00, and ES 3.10))
void
_mesa_ast_type_qualifier_print(const struct ast_type_qualifier *q)
{
+ if (q->flags.q.subroutine)
+ printf("subroutine ");
+
+ if (q->flags.q.subroutine_def) {
+ printf("subroutine (");
+ q->subroutine_list->print();
+ printf(")");
+ }
+
if (q->flags.q.constant)
printf("const ");
is_declaration = true;
}
+void ast_subroutine_list::print(void) const
+{
+ foreach_list_typed (ast_node, ast, link, & this->declarations) {
+ if (&ast->link != this->declarations.get_head())
+ printf(", ");
+ ast->print();
+ }
+}
+
static void
set_shader_inout_layout(struct gl_shader *shader,
struct _mesa_glsl_parse_state *state)
unsigned atomic_counter_offsets[MAX_COMBINED_ATOMIC_BUFFERS];
bool allow_extension_directive_midshader;
+
+ /**
+ * Known subroutine type declarations.
+ */
+ int num_subroutine_types;
+ ir_function **subroutine_types;
+
+ /**
+ * Functions that are associated with
+ * subroutine types.
+ */
+ int num_subroutines;
+ ir_function **subroutines;
+
+ /**
+ * field selection temporary parser storage -
+ * did the parser just parse a dot.
+ */
+ bool is_field;
};
# define YYLLOC_DEFAULT(Current, Rhs, N) \
"structure",
expr->primary_expression.identifier);
}
- } else if (expr->subexpressions[1] != NULL) {
- /* Handle "method calls" in GLSL 1.20 - namely, array.length() */
- state->check_version(120, 300, &loc, "methods not supported");
-
- ast_expression *call = expr->subexpressions[1];
- assert(call->oper == ast_function_call);
-
- const char *method;
- method = call->subexpressions[0]->primary_expression.identifier;
-
- if (strcmp(method, "length") == 0) {
- if (!call->expressions.is_empty())
- _mesa_glsl_error(&loc, state, "length method takes no arguments");
-
- if (op->type->is_array()) {
- if (op->type->is_unsized_array())
- _mesa_glsl_error(&loc, state, "length called on unsized array");
-
- result = new(ctx) ir_constant(op->type->array_size());
- } else if (op->type->is_vector()) {
- if (state->ARB_shading_language_420pack_enable) {
- /* .length() returns int. */
- result = new(ctx) ir_constant((int) op->type->vector_elements);
- } else {
- _mesa_glsl_error(&loc, state, "length method on matrix only available"
- "with ARB_shading_language_420pack");
- }
- } else if (op->type->is_matrix()) {
- if (state->ARB_shading_language_420pack_enable) {
- /* .length() returns int. */
- result = new(ctx) ir_constant((int) op->type->matrix_columns);
- } else {
- _mesa_glsl_error(&loc, state, "length method on matrix only available"
- "with ARB_shading_language_420pack");
- }
- }
- } else {
- _mesa_glsl_error(&loc, state, "unknown method: `%s'", method);
- }
} else if (op->type->is_vector() ||
(state->ARB_shading_language_420pack_enable &&
op->type->is_scalar())) {