Browse Source

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>
tags/mesa-9.2-rc1
Ian Romanick 12 years ago
parent
commit
1e773626ee
1 changed files with 102 additions and 47 deletions
  1. 102
    47
      src/glsl/ast_function.cpp

+ 102
- 47
src/glsl/ast_function.cpp View File

@@ -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");

Loading…
Cancel
Save