mirror of
https://gitlab.freedesktop.org/mesa/mesa.git
synced 2025-12-21 00:40:10 +01:00
glsl: Perform implicit type conversions on function call out parameters.
When an out parameter undergoes an implicit type conversion, we need to store it in a temporary, and then after the call completes, convert the resulting value. In other words, we convert code like the following: 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); This transformation needs to happen during ast-to-IR convertion (as opposed to, say, a lowering pass), because it is invalid IR for formal and actual parameters to have types that don't match. Fixes piglit tests spec/glsl-1.20/compiler/qualifiers/out-conversion-int-to-float.vert and spec/glsl-1.20/execution/qualifiers/vs-out-conversion-*.shader_test, and bug 39651. Bugzilla: https://bugs.freedesktop.org/show_bug.cgi?id=39651 Reviewed-by: Chad Versace <chad@chad-versace.us>
This commit is contained in:
parent
c548192caf
commit
67b5a3267d
1 changed files with 71 additions and 5 deletions
|
|
@ -134,6 +134,8 @@ match_function_by_name(exec_list *instructions, const char *name,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
exec_list post_call_conversions;
|
||||||
|
|
||||||
if (sig != NULL) {
|
if (sig != NULL) {
|
||||||
/* Verify that 'out' and 'inout' actual parameters are lvalues. This
|
/* Verify that 'out' and 'inout' actual parameters are lvalues. This
|
||||||
* isn't done in ir_function::matching_signature because that function
|
* isn't done in ir_function::matching_signature because that function
|
||||||
|
|
@ -141,6 +143,12 @@ match_function_by_name(exec_list *instructions, const char *name,
|
||||||
*
|
*
|
||||||
* Also, validate that 'const_in' formal parameters (an extension of our
|
* Also, validate that 'const_in' formal parameters (an extension of our
|
||||||
* IR) correspond to ir_constant actual parameters.
|
* 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 actual_iter = actual_parameters->iterator();
|
||||||
exec_list_iterator formal_iter = sig->parameters.iterator();
|
exec_list_iterator formal_iter = sig->parameters.iterator();
|
||||||
|
|
@ -185,8 +193,63 @@ match_function_by_name(exec_list *instructions, const char *name,
|
||||||
}
|
}
|
||||||
|
|
||||||
if (formal->type->is_numeric() || formal->type->is_boolean()) {
|
if (formal->type->is_numeric() || formal->type->is_boolean()) {
|
||||||
ir_rvalue *converted = convert_component(actual, formal->type);
|
switch (formal->mode) {
|
||||||
actual->replace_with(converted);
|
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();
|
actual_iter.next();
|
||||||
|
|
@ -196,8 +259,11 @@ match_function_by_name(exec_list *instructions, const char *name,
|
||||||
/* Always insert the call in the instruction stream, and return a deref
|
/* 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
|
* 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.
|
* 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_call *call = new(ctx) ir_call(sig, actual_parameters);
|
||||||
|
ir_dereference_variable *deref;
|
||||||
if (!sig->return_type->is_void()) {
|
if (!sig->return_type->is_void()) {
|
||||||
/* If the function call is a constant expression, don't
|
/* If the function call is a constant expression, don't
|
||||||
* generate the instructions to call it; just generate an
|
* generate the instructions to call it; just generate an
|
||||||
|
|
@ -214,7 +280,6 @@ match_function_by_name(exec_list *instructions, const char *name,
|
||||||
}
|
}
|
||||||
|
|
||||||
ir_variable *var;
|
ir_variable *var;
|
||||||
ir_dereference_variable *deref;
|
|
||||||
|
|
||||||
var = new(ctx) ir_variable(sig->return_type,
|
var = new(ctx) ir_variable(sig->return_type,
|
||||||
ralloc_asprintf(ctx, "%s_retval",
|
ralloc_asprintf(ctx, "%s_retval",
|
||||||
|
|
@ -227,11 +292,12 @@ match_function_by_name(exec_list *instructions, const char *name,
|
||||||
instructions->push_tail(assign);
|
instructions->push_tail(assign);
|
||||||
|
|
||||||
deref = new(ctx) ir_dereference_variable(var);
|
deref = new(ctx) ir_dereference_variable(var);
|
||||||
return deref;
|
|
||||||
} else {
|
} else {
|
||||||
instructions->push_tail(call);
|
instructions->push_tail(call);
|
||||||
return NULL;
|
deref = NULL;
|
||||||
}
|
}
|
||||||
|
instructions->append_list(&post_call_conversions);
|
||||||
|
return deref;
|
||||||
} else {
|
} else {
|
||||||
char *str = prototype_string(NULL, name, actual_parameters);
|
char *str = prototype_string(NULL, name, actual_parameters);
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue