glsl: Generate correct ir_binop_vector_extract code for out and inout parameters
Like with type conversions on out parameters, some extra copies need to occur to handle these cases. The fundamental problem is that ir_binop_vector_extract is not an lvalue, but out and inout parameters must be lvalues. A previous patch delt with a similar problem in the LHS of ir_assignment. v2: Convert tabs to spaces. Suggested by Eric. Signed-off-by: Ian Romanick <ian.d.romanick@intel.com> Reviewed-by: Eric Anholt <eric@anholt.net> Reviewed-by: Kenneth Graunke <kenneth@whitecape.org>
This commit is contained in:
@@ -165,10 +165,18 @@ verify_parameter_modes(_mesa_glsl_parse_state *state,
|
||||
actual->variable_referenced()->name);
|
||||
return false;
|
||||
} else if (!actual->is_lvalue()) {
|
||||
_mesa_glsl_error(&loc, state,
|
||||
"function parameter '%s %s' is not an lvalue",
|
||||
mode, formal->name);
|
||||
return false;
|
||||
/* Even though ir_binop_vector_extract is not an l-value, let it
|
||||
* slop through. generate_call will handle it correctly.
|
||||
*/
|
||||
ir_expression *const expr = ((ir_rvalue *) actual)->as_expression();
|
||||
if (expr == NULL
|
||||
|| expr->operation != ir_binop_vector_extract
|
||||
|| !expr->operands[0]->is_lvalue()) {
|
||||
_mesa_glsl_error(&loc, state,
|
||||
"function parameter '%s %s' is not an lvalue",
|
||||
mode, formal->name);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -178,6 +186,93 @@ verify_parameter_modes(_mesa_glsl_parse_state *state,
|
||||
return true;
|
||||
}
|
||||
|
||||
static void
|
||||
fix_parameter(void *mem_ctx, ir_rvalue *actual, const glsl_type *formal_type,
|
||||
exec_list *before_instructions, exec_list *after_instructions,
|
||||
bool parameter_is_inout)
|
||||
{
|
||||
ir_expression *const expr = actual->as_expression();
|
||||
|
||||
/* If the types match exactly and the parameter is not a vector-extract,
|
||||
* nothing needs to be done to fix the parameter.
|
||||
*/
|
||||
if (formal_type == actual->type
|
||||
&& (expr == NULL || expr->operation != ir_binop_vector_extract))
|
||||
return;
|
||||
|
||||
/* 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);
|
||||
*
|
||||
* If the parameter is an ir_expression of ir_binop_vector_extract,
|
||||
* additional conversion is needed in the post-call re-write.
|
||||
*/
|
||||
ir_variable *tmp =
|
||||
new(mem_ctx) ir_variable(formal_type, "inout_tmp", ir_var_temporary);
|
||||
|
||||
before_instructions->push_tail(tmp);
|
||||
|
||||
/* If the parameter is an inout parameter, copy the value of the actual
|
||||
* parameter to the new temporary. Note that no type conversion is allowed
|
||||
* here because inout parameters must match types exactly.
|
||||
*/
|
||||
if (parameter_is_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);
|
||||
|
||||
ir_dereference_variable *const deref_tmp_1 =
|
||||
new(mem_ctx) ir_dereference_variable(tmp);
|
||||
ir_assignment *const assignment =
|
||||
new(mem_ctx) ir_assignment(deref_tmp_1, actual);
|
||||
before_instructions->push_tail(assignment);
|
||||
}
|
||||
|
||||
/* Replace the parameter in the call with a dereference of the new
|
||||
* temporary.
|
||||
*/
|
||||
ir_dereference_variable *const deref_tmp_2 =
|
||||
new(mem_ctx) ir_dereference_variable(tmp);
|
||||
actual->replace_with(deref_tmp_2);
|
||||
|
||||
|
||||
/* Copy the temporary variable to the actual parameter with optional
|
||||
* type conversion applied.
|
||||
*/
|
||||
ir_rvalue *rhs = new(mem_ctx) ir_dereference_variable(tmp);
|
||||
if (actual->type != formal_type)
|
||||
rhs = convert_component(rhs, actual->type);
|
||||
|
||||
ir_rvalue *lhs = actual;
|
||||
if (expr != NULL && expr->operation == ir_binop_vector_extract) {
|
||||
rhs = new(mem_ctx) ir_expression(ir_triop_vector_insert,
|
||||
expr->operands[0]->type,
|
||||
expr->operands[0]->clone(mem_ctx, NULL),
|
||||
rhs,
|
||||
expr->operands[1]->clone(mem_ctx, NULL));
|
||||
lhs = expr->operands[0]->clone(mem_ctx, NULL);
|
||||
}
|
||||
|
||||
ir_assignment *const assignment_2 = new(mem_ctx) ir_assignment(lhs, rhs);
|
||||
after_instructions->push_tail(assignment_2);
|
||||
}
|
||||
|
||||
/**
|
||||
* If a function call is generated, \c call_ir will point to it on exit.
|
||||
* Otherwise \c call_ir will be set to \c NULL.
|
||||
@@ -218,50 +313,10 @@ generate_call(exec_list *instructions, ir_function_signature *sig,
|
||||
break;
|
||||
}
|
||||
case ir_var_function_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_function_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);
|
||||
fix_parameter(ctx, actual, formal->type,
|
||||
instructions, &post_call_conversions,
|
||||
formal->mode == ir_var_function_inout);
|
||||
break;
|
||||
default:
|
||||
assert (!"Illegal formal parameter mode");
|
||||
|
||||
Reference in New Issue
Block a user