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