aboutsummaryrefslogtreecommitdiffstats
path: root/src/glsl/ast_function.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/glsl/ast_function.cpp')
-rw-r--r--src/glsl/ast_function.cpp127
1 files changed, 122 insertions, 5 deletions
diff --git a/src/glsl/ast_function.cpp b/src/glsl/ast_function.cpp
index 92e26bf2416..803edf5a14d 100644
--- a/src/glsl/ast_function.cpp
+++ b/src/glsl/ast_function.cpp
@@ -26,6 +26,7 @@
#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);
@@ -355,6 +356,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 +424,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 +493,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)
@@ -1531,6 +1569,65 @@ process_record_constructor(exec_list *instructions,
&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,
@@ -1543,8 +1640,6 @@ 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];
@@ -1765,11 +1860,22 @@ ast_function_expression::hir(exec_list *instructions,
&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);
@@ -1779,13 +1885,24 @@ 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);
+ if (!value) {
+ ir_variable *const tmp = new(ctx) ir_variable(glsl_type::void_type,
+ "void_var",
+ ir_var_temporary);
+ instructions->push_tail(tmp);
+ value = new(ctx) ir_dereference_variable(tmp);
+ }
}
return value;