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
|
@ -134,6 +134,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
|
||||
|
@ -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
|
||||
* 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();
|
||||
|
@ -185,8 +193,63 @@ match_function_by_name(exec_list *instructions, const char *name,
|
|||
}
|
||||
|
||||
if (formal->type->is_numeric() || formal->type->is_boolean()) {
|
||||
ir_rvalue *converted = convert_component(actual, formal->type);
|
||||
actual->replace_with(converted);
|
||||
switch (formal->mode) {
|
||||
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();
|
||||
|
@ -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
|
||||
* 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
|
||||
|
@ -214,7 +280,6 @@ match_function_by_name(exec_list *instructions, const char *name,
|
|||
}
|
||||
|
||||
ir_variable *var;
|
||||
ir_dereference_variable *deref;
|
||||
|
||||
var = new(ctx) ir_variable(sig->return_type,
|
||||
ralloc_asprintf(ctx, "%s_retval",
|
||||
|
@ -227,11 +292,12 @@ match_function_by_name(exec_list *instructions, const char *name,
|
|||
instructions->push_tail(assign);
|
||||
|
||||
deref = new(ctx) ir_dereference_variable(var);
|
||||
return deref;
|
||||
} else {
|
||||
instructions->push_tail(call);
|
||||
return NULL;
|
||||
deref = NULL;
|
||||
}
|
||||
instructions->append_list(&post_call_conversions);
|
||||
return deref;
|
||||
} else {
|
||||
char *str = prototype_string(NULL, name, actual_parameters);
|
||||
|
||||
|
|
Loading…
Reference in New Issue