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:
@@ -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);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user