glsl: Split code to generate an ir_call out from match_function_by_name.

match_function_by_name performs two fairly separate tasks:
1. Hunt down the appropriate ir_function_signature for the callee.
2. Generate the actual ir_call (assuming we found the callee).

Both of these are complicated.  The first has to handle exact/inexact
matches, lazy importing of built-in prototypes, different scoping rules
for 1.10, 1.20+, and ES.  Not to mention printing a user-friendly error
message with pretty-printed "maybe you meant this" candidate signatures.

The second has to deal with void/non-void functions, pre-call implicit
conversions for "in" parmeters, and post-call "out" call conversions.

Trying to do both in one function is just too unwieldy.  Time to split.

This patch purely moves the code to generate an ir_call into a separate
function and reindents it.  Otherwise, the code is identical.

Signed-off-by: Kenneth Graunke <kenneth@whitecape.org>
Reviewed-by: Paul Berry <stereotype441@gmail.com>
This commit is contained in:
Kenneth Graunke
2011-11-09 03:01:54 -08:00
parent 861d0a5e12
commit cdc9408266

View File

@@ -93,6 +93,178 @@ prototype_string(const glsl_type *return_type, const char *name,
return str;
}
static ir_rvalue *
generate_call(exec_list *instructions, ir_function_signature *sig,
YYLTYPE *loc, exec_list *actual_parameters,
struct _mesa_glsl_parse_state *state)
{
void *ctx = state;
exec_list post_call_conversions;
/* Verify that 'out' and 'inout' actual parameters are lvalues. This
* isn't done in ir_function::matching_signature because that function
* cannot generate the necessary diagnostics.
*
* Also, validate that 'const_in' formal parameters (an extension of our
* IR) correspond to ir_constant actual parameters.
*
* Also, perform implicit conversion of arguments. Note: to implicitly
* convert out parameters, we need to place them in a temporary
* variable, and do the conversion after the call takes place. Since we
* haven't emitted the call yet, we'll place the post-call conversions
* in a temporary exec_list, and emit them later.
*/
exec_list_iterator actual_iter = actual_parameters->iterator();
exec_list_iterator formal_iter = sig->parameters.iterator();
while (actual_iter.has_next()) {
ir_rvalue *actual = (ir_rvalue *) actual_iter.get();
ir_variable *formal = (ir_variable *) formal_iter.get();
assert(actual != NULL);
assert(formal != NULL);
if (formal->mode == ir_var_const_in && !actual->as_constant()) {
_mesa_glsl_error(loc, state,
"parameter `%s' must be a constant expression",
formal->name);
return ir_call::get_error_instruction(ctx);
}
if ((formal->mode == ir_var_out)
|| (formal->mode == ir_var_inout)) {
const char *mode = NULL;
switch (formal->mode) {
case ir_var_out: mode = "out"; break;
case ir_var_inout: mode = "inout"; break;
default: assert(false); break;
}
/* FIXME: 'loc' is incorrect (as of 2011-01-21). It is always
* FIXME: 0:0(0).
*/
if (actual->variable_referenced()
&& actual->variable_referenced()->read_only) {
_mesa_glsl_error(loc, state,
"function parameter '%s %s' references the "
"read-only variable '%s'",
mode, formal->name,
actual->variable_referenced()->name);
} else if (!actual->is_lvalue()) {
_mesa_glsl_error(loc, state,
"function parameter '%s %s' is not an lvalue",
mode, formal->name);
}
}
if (formal->type->is_numeric() || formal->type->is_boolean()) {
switch (formal->mode) {
case ir_var_const_in:
case ir_var_in: {
ir_rvalue *converted
= convert_component(actual, formal->type);
actual->replace_with(converted);
break;
}
case ir_var_out:
if (actual->type != formal->type) {
/* To convert an out parameter, we need to create a
* temporary variable to hold the value before conversion,
* and then perform the conversion after the function call
* returns.
*
* This has the effect of transforming code like this:
*
* void f(out int x);
* float value;
* f(value);
*
* Into IR that's equivalent to this:
*
* void f(out int x);
* float value;
* int out_parameter_conversion;
* f(out_parameter_conversion);
* value = float(out_parameter_conversion);
*/
ir_variable *tmp =
new(ctx) ir_variable(formal->type,
"out_parameter_conversion",
ir_var_temporary);
instructions->push_tail(tmp);
ir_dereference_variable *deref_tmp_1
= new(ctx) ir_dereference_variable(tmp);
ir_dereference_variable *deref_tmp_2
= new(ctx) ir_dereference_variable(tmp);
ir_rvalue *converted_tmp
= convert_component(deref_tmp_1, actual->type);
ir_assignment *assignment
= new(ctx) ir_assignment(actual, converted_tmp);
post_call_conversions.push_tail(assignment);
actual->replace_with(deref_tmp_2);
}
break;
case ir_var_inout:
/* Inout parameters should never require conversion, since that
* would require an implicit conversion to exist both to and
* from the formal parameter type, and there are no
* bidirectional implicit conversions.
*/
assert (actual->type == formal->type);
break;
default:
assert (!"Illegal formal parameter mode");
break;
}
}
actual_iter.next();
formal_iter.next();
}
/* Always insert the call in the instruction stream, and return a deref
* of its return val if it returns a value, since we don't know if
* the rvalue is going to be assigned to anything or not.
*
* Also insert any out parameter conversions after the call.
*/
ir_call *call = new(ctx) ir_call(sig, actual_parameters);
ir_dereference_variable *deref;
if (!sig->return_type->is_void()) {
/* If the function call is a constant expression, don't
* generate the instructions to call it; just generate an
* ir_constant representing the constant value.
*
* Function calls can only be constant expressions starting
* in GLSL 1.20.
*/
if (state->language_version >= 120) {
ir_constant *const_val = call->constant_expression_value();
if (const_val) {
return const_val;
}
}
ir_variable *var;
var = new(ctx) ir_variable(sig->return_type,
ralloc_asprintf(ctx, "%s_retval",
sig->function_name()),
ir_var_temporary);
instructions->push_tail(var);
deref = new(ctx) ir_dereference_variable(var);
ir_assignment *assign = new(ctx) ir_assignment(deref, call, NULL);
instructions->push_tail(assign);
deref = new(ctx) ir_dereference_variable(var);
} else {
instructions->push_tail(call);
deref = NULL;
}
instructions->append_list(&post_call_conversions);
return deref;
}
static ir_rvalue *
match_function_by_name(exec_list *instructions, const char *name,
@@ -135,172 +307,8 @@ match_function_by_name(exec_list *instructions, const char *name,
}
}
exec_list post_call_conversions;
if (sig != NULL) {
/* Verify that 'out' and 'inout' actual parameters are lvalues. This
* isn't done in ir_function::matching_signature because that function
* cannot generate the necessary diagnostics.
*
* Also, validate that 'const_in' formal parameters (an extension of our
* IR) correspond to ir_constant actual parameters.
*
* Also, perform implicit conversion of arguments. Note: to implicitly
* convert out parameters, we need to place them in a temporary
* variable, and do the conversion after the call takes place. Since we
* haven't emitted the call yet, we'll place the post-call conversions
* in a temporary exec_list, and emit them later.
*/
exec_list_iterator actual_iter = actual_parameters->iterator();
exec_list_iterator formal_iter = sig->parameters.iterator();
while (actual_iter.has_next()) {
ir_rvalue *actual = (ir_rvalue *) actual_iter.get();
ir_variable *formal = (ir_variable *) formal_iter.get();
assert(actual != NULL);
assert(formal != NULL);
if (formal->mode == ir_var_const_in && !actual->as_constant()) {
_mesa_glsl_error(loc, state,
"parameter `%s' must be a constant expression",
formal->name);
return ir_call::get_error_instruction(ctx);
}
if ((formal->mode == ir_var_out)
|| (formal->mode == ir_var_inout)) {
const char *mode = NULL;
switch (formal->mode) {
case ir_var_out: mode = "out"; break;
case ir_var_inout: mode = "inout"; break;
default: assert(false); break;
}
/* FIXME: 'loc' is incorrect (as of 2011-01-21). It is always
* FIXME: 0:0(0).
*/
if (actual->variable_referenced()
&& actual->variable_referenced()->read_only) {
_mesa_glsl_error(loc, state,
"function parameter '%s %s' references the "
"read-only variable '%s'",
mode, formal->name,
actual->variable_referenced()->name);
} else if (!actual->is_lvalue()) {
_mesa_glsl_error(loc, state,
"function parameter '%s %s' is not an lvalue",
mode, formal->name);
}
}
if (formal->type->is_numeric() || formal->type->is_boolean()) {
switch (formal->mode) {
case ir_var_const_in:
case ir_var_in: {
ir_rvalue *converted
= convert_component(actual, formal->type);
actual->replace_with(converted);
break;
}
case ir_var_out:
if (actual->type != formal->type) {
/* To convert an out parameter, we need to create a
* temporary variable to hold the value before conversion,
* and then perform the conversion after the function call
* returns.
*
* This has the effect of transforming code like this:
*
* void f(out int x);
* float value;
* f(value);
*
* Into IR that's equivalent to this:
*
* void f(out int x);
* float value;
* int out_parameter_conversion;
* f(out_parameter_conversion);
* value = float(out_parameter_conversion);
*/
ir_variable *tmp =
new(ctx) ir_variable(formal->type,
"out_parameter_conversion",
ir_var_temporary);
instructions->push_tail(tmp);
ir_dereference_variable *deref_tmp_1
= new(ctx) ir_dereference_variable(tmp);
ir_dereference_variable *deref_tmp_2
= new(ctx) ir_dereference_variable(tmp);
ir_rvalue *converted_tmp
= convert_component(deref_tmp_1, actual->type);
ir_assignment *assignment
= new(ctx) ir_assignment(actual, converted_tmp);
post_call_conversions.push_tail(assignment);
actual->replace_with(deref_tmp_2);
}
break;
case ir_var_inout:
/* Inout parameters should never require conversion, since that
* would require an implicit conversion to exist both to and
* from the formal parameter type, and there are no
* bidirectional implicit conversions.
*/
assert (actual->type == formal->type);
break;
default:
assert (!"Illegal formal parameter mode");
break;
}
}
actual_iter.next();
formal_iter.next();
}
/* Always insert the call in the instruction stream, and return a deref
* of its return val if it returns a value, since we don't know if
* the rvalue is going to be assigned to anything or not.
*
* Also insert any out parameter conversions after the call.
*/
ir_call *call = new(ctx) ir_call(sig, actual_parameters);
ir_dereference_variable *deref;
if (!sig->return_type->is_void()) {
/* If the function call is a constant expression, don't
* generate the instructions to call it; just generate an
* ir_constant representing the constant value.
*
* Function calls can only be constant expressions starting
* in GLSL 1.20.
*/
if (state->language_version >= 120) {
ir_constant *const_val = call->constant_expression_value();
if (const_val) {
return const_val;
}
}
ir_variable *var;
var = new(ctx) ir_variable(sig->return_type,
ralloc_asprintf(ctx, "%s_retval",
sig->function_name()),
ir_var_temporary);
instructions->push_tail(var);
deref = new(ctx) ir_dereference_variable(var);
ir_assignment *assign = new(ctx) ir_assignment(deref, call, NULL);
instructions->push_tail(assign);
deref = new(ctx) ir_dereference_variable(var);
} else {
instructions->push_tail(call);
deref = NULL;
}
instructions->append_list(&post_call_conversions);
return deref;
return generate_call(instructions, sig, loc, actual_parameters, state);
} else {
char *str = prototype_string(NULL, name, actual_parameters);