From: Dave Airlie <airl...@redhat.com> This is the guts of the GLSL parser and AST support for shader subroutines.
The code creates a subroutine type in the parser, and uses that there to validate the identifiers. The parser also distinguishes between subroutine types/function prototypes /uniforms and subroutine defintions for functions. Then in the AST conversion it recreates the types, and stores the subroutine definition info or subroutine info into the ir_function along with a side lookup table in the parser state. It also converts subroutine calls into the enhanced ir_call. Signed-off-by: Dave Airlie <airl...@redhat.com> --- src/glsl/ast.h | 9 ++++ src/glsl/ast_function.cpp | 58 +++++++++++++++++++++++-- src/glsl/ast_to_hir.cpp | 94 +++++++++++++++++++++++++++++++++++++---- src/glsl/ast_type.cpp | 2 +- src/glsl/glsl_parser.yy | 36 ++++++++++------ src/glsl/glsl_parser_extras.cpp | 23 ++++++++++ src/glsl/glsl_parser_extras.h | 8 ++++ 7 files changed, 205 insertions(+), 25 deletions(-) diff --git a/src/glsl/ast.h b/src/glsl/ast.h index d2c5fec..877ae1a 100644 --- a/src/glsl/ast.h +++ b/src/glsl/ast.h @@ -306,6 +306,13 @@ private: bool cons; }; +class ast_subroutine_list : public ast_node +{ +public: + virtual void print(void) const; + exec_list declarations; +}; + class ast_array_specifier : public ast_node { public: /** Unsized array specifier ([]) */ @@ -517,6 +524,7 @@ struct ast_type_qualifier { /** \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; @@ -639,6 +647,7 @@ struct ast_type_qualifier { ast_type_qualifier q, ast_node* &node); + ast_subroutine_list *subroutine_list; }; class ast_declarator_list; diff --git a/src/glsl/ast_function.cpp b/src/glsl/ast_function.cpp index 87df93e..8cd65d1 100644 --- a/src/glsl/ast_function.cpp +++ b/src/glsl/ast_function.cpp @@ -26,6 +26,8 @@ #include "glsl_types.h" #include "ir.h" #include "main/core.h" /* for MIN2 */ +#include "ir_builder.h" +#include "main/shaderobj.h" static ir_rvalue * convert_component(ir_rvalue *src, const glsl_type *desired_type); @@ -355,6 +357,8 @@ fix_parameter(void *mem_ctx, ir_rvalue *actual, const glsl_type *formal_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; @@ -421,7 +425,8 @@ generate_call(exec_list *instructions, ir_function_signature *sig, 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. */ @@ -489,6 +494,40 @@ done: 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) @@ -1755,9 +1794,18 @@ ast_function_expression::hir(exec_list *instructions, } } 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); @@ -1767,13 +1815,17 @@ ast_function_expression::hir(exec_list *instructions, 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); } else if (!verify_parameter_modes(state, sig, actual_parameters, this->expressions)) { /* 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); } return value; diff --git a/src/glsl/ast_to_hir.cpp b/src/glsl/ast_to_hir.cpp index 90b732c..0d4baea 100644 --- a/src/glsl/ast_to_hir.cpp +++ b/src/glsl/ast_to_hir.cpp @@ -54,6 +54,7 @@ #include "ast.h" #include "glsl_types.h" #include "program/hash_table.h" +#include "main/shaderobj.h" #include "ir.h" #include "ir_builder.h" @@ -970,6 +971,7 @@ do_comparison(void *mem_ctx, int operation, ir_rvalue *op0, ir_rvalue *op1) case GLSL_TYPE_SAMPLER: case GLSL_TYPE_IMAGE: case GLSL_TYPE_INTERFACE: + case GLSL_TYPE_SUBROUTINE: case GLSL_TYPE_ATOMIC_UINT: /* I assume a comparison of a struct containing a sampler just * ignores the sampler present in the type. @@ -3395,7 +3397,7 @@ ast_declarator_list::hir(exec_list *instructions, 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. */ @@ -3413,10 +3415,24 @@ ast_declarator_list::hir(exec_list *instructions, 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 @@ -3488,6 +3504,8 @@ ast_declarator_list::hir(exec_list *instructions, */ 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) { @@ -3626,6 +3644,9 @@ ast_declarator_list::hir(exec_list *instructions, "type %s", check_type->name); } } + } 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, @@ -4065,6 +4086,7 @@ ast_function::hir(exec_list *instructions, ir_function *f = NULL; ir_function_signature *sig = NULL; exec_list hir_parameters; + YYLTYPE loc = this->get_location(); const char *const name = identifier; @@ -4116,6 +4138,17 @@ ast_function::hir(exec_list *instructions, 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." */ @@ -4152,14 +4185,17 @@ ast_function::hir(exec_list *instructions, /* Create an ir_function if one doesn't already exist. */ 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; + f = new(ctx) ir_function(name); + 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); @@ -4248,6 +4284,46 @@ ast_function::hir(exec_list *instructions, sig->replace_parameters(&hir_parameters); signature = sig; + if (this->return_type->qualifier.flags.q.subroutine_def) { + int idx; + f->is_subroutine_def = true; + + 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; diff --git a/src/glsl/ast_type.cpp b/src/glsl/ast_type.cpp index 74b1ab5..e274057 100644 --- a/src/glsl/ast_type.cpp +++ b/src/glsl/ast_type.cpp @@ -44,7 +44,7 @@ ast_fully_specified_type::has_qualifiers() const 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; } diff --git a/src/glsl/glsl_parser.yy b/src/glsl/glsl_parser.yy index 97507eb..2c1a88c 100644 --- a/src/glsl/glsl_parser.yy +++ b/src/glsl/glsl_parser.yy @@ -121,7 +121,7 @@ static bool match_layout_qualifier(const char *s1, const char *s2, 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; @@ -215,7 +215,7 @@ static bool match_layout_qualifier(const char *s1, const char *s2, %type <type_qualifier> layout_qualifier_id_list layout_qualifier_id %type <type_qualifier> interface_block_layout_qualifier %type <type_qualifier> subroutine_qualifier -%type <type_qualifier> subroutine_type_list +%type <subroutine_list> subroutine_type_list %type <type_qualifier> interface_qualifier %type <type_specifier> type_specifier %type <type_specifier> type_specifier_nonarray @@ -477,7 +477,7 @@ postfix_expression: { $$ = $1; } - | postfix_expression '.' any_identifier + | postfix_expression '.' FIELD_SELECTION { void *ctx = state; $$ = new(ctx) ast_expression(ast_field_selection, $1, NULL, NULL); @@ -555,12 +555,10 @@ function_identifier: $$ = new(ctx) ast_function_expression($1); $$->set_location(@1); } - | variable_identifier + | 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); } | FIELD_SELECTION @@ -911,7 +909,11 @@ function_header: $$->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(); } ; @@ -1558,19 +1560,29 @@ subroutine_qualifier: | SUBROUTINE '(' subroutine_type_list ')' { memset(& $$, 0, sizeof($$)); - $$.flags.q.subroutine = 1; - /* TODO: collect the type list from $3 */ + $$.flags.q.subroutine_def = 1; + $$.subroutine_list = $3; } ; subroutine_type_list: any_identifier { - /* TODO */ + 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 { - /* TODO */ + void *ctx = state; + ast_declaration *decl = new(ctx) ast_declaration($3, NULL, NULL); + decl->set_location(@3); + + $$ = $1; + $$->declarations.push_tail(&decl->link); } ; diff --git a/src/glsl/glsl_parser_extras.cpp b/src/glsl/glsl_parser_extras.cpp index 237c2ad..1422757 100644 --- a/src/glsl/glsl_parser_extras.cpp +++ b/src/glsl/glsl_parser_extras.cpp @@ -141,6 +141,10 @@ _mesa_glsl_parse_state::_mesa_glsl_parse_state(struct gl_context *_ctx, 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 2 GLES versions (ES2 & ES3) @@ -810,6 +814,15 @@ _mesa_ast_set_aggregate_type(const glsl_type *type, 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 "); @@ -1398,6 +1411,16 @@ ast_struct_specifier::ast_struct_specifier(const char *identifier, 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) diff --git a/src/glsl/glsl_parser_extras.h b/src/glsl/glsl_parser_extras.h index 1d3da44..e027e96 100644 --- a/src/glsl/glsl_parser_extras.h +++ b/src/glsl/glsl_parser_extras.h @@ -518,6 +518,14 @@ struct _mesa_glsl_parse_state { unsigned atomic_counter_offsets[MAX_COMBINED_ATOMIC_BUFFERS]; bool allow_extension_directive_midshader; + + /* functions that declare a subroutine type */ + int num_subroutine_types; + ir_function **subroutine_types; + + /* functions that are used as a subroutine */ + int num_subroutines; + ir_function **subroutines; }; # define YYLLOC_DEFAULT(Current, Rhs, N) \ -- 2.1.0 _______________________________________________ mesa-dev mailing list mesa-dev@lists.freedesktop.org http://lists.freedesktop.org/mailman/listinfo/mesa-dev