diff options
-rw-r--r-- | src/glsl/ast_function.cpp | 149 |
1 files changed, 102 insertions, 47 deletions
diff --git a/src/glsl/ast_function.cpp b/src/glsl/ast_function.cpp index 26f72cf8e95..00e0c05dddc 100644 --- a/src/glsl/ast_function.cpp +++ b/src/glsl/ast_function.cpp @@ -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"); |