formal_iter.next();
}
- /* 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.
+ /* If the function call is a constant expression, don't generate any
+ * instructions; just generate an ir_constant.
*
- * Also insert any out parameter conversions after the call.
+ * Function calls were first allowed to be constant expressions in GLSL 1.20.
*/
- 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
- * ir_constant representing the constant value.
- *
- * Function calls can only be constant expressions starting
- * in GLSL 1.20.
- */
- if (state->language_version >= 120) {
- ir_constant *const_val = call->constant_expression_value();
- if (const_val) {
- return const_val;
- }
+ if (state->language_version >= 120) {
+ ir_constant *value = sig->constant_expression_value(actual_parameters);
+ if (value != NULL) {
+ return value;
}
+ }
+ ir_dereference_variable *deref = NULL;
+ if (!sig->return_type->is_void()) {
+ /* Create a new temporary to hold the return value. */
ir_variable *var;
var = new(ctx) ir_variable(sig->return_type,
instructions->push_tail(var);
deref = new(ctx) ir_dereference_variable(var);
- ir_assignment *assign = new(ctx) ir_assignment(deref, call, NULL);
- instructions->push_tail(assign);
- *call_ir = call;
-
- deref = new(ctx) ir_dereference_variable(var);
- } else {
- instructions->push_tail(call);
- *call_ir = call;
- deref = NULL;
}
+ ir_call *call = new(ctx) ir_call(sig, deref, actual_parameters);
+ instructions->push_tail(call);
+
+ /* Also emit any necessary out-parameter conversions. */
instructions->append_list(&post_call_conversions);
- return deref;
+
+ return deref ? deref->clone(ctx, NULL) : NULL;
}
/**
(signature float
(parameters
(declare (in) float x))
- ((return (expression float - (constant float (1.5707963))
- (call asin ((var_ref x)))))))
+ ((declare () float s)
+ (call asin (var_ref s) ((var_ref x)))
+ (return (expression float - (constant float (1.5707963)) (var_ref s)))))
+
(signature vec2
(parameters
(declare (in) vec2 x))
- ((return (expression vec2 - (constant float (1.5707963))
- (call asin ((var_ref x)))))))
+ ((declare () vec2 s)
+ (call asin (var_ref s) ((var_ref x)))
+ (return (expression vec2 - (constant float (1.5707963)) (var_ref s)))))
+
(signature vec3
(parameters
(declare (in) vec3 x))
- ((return (expression vec3 - (constant float (1.5707963))
- (call asin ((var_ref x)))))))
+ ((declare () vec3 s)
+ (call asin (var_ref s) ((var_ref x)))
+ (return (expression vec3 - (constant float (1.5707963)) (var_ref s)))))
+
(signature vec4
(parameters
(declare (in) vec4 x))
- ((return (expression vec4 - (constant float (1.5707963))
- (call asin ((var_ref x)))))))
+ ((declare () vec4 s)
+ (call asin (var_ref s) ((var_ref x)))
+ (return (expression vec4 - (constant float (1.5707963)) (var_ref s)))))
))
(signature float
(parameters
(declare (in) float y_over_x))
- ((return (call asin ((expression float *
+ ((declare () float s)
+ (call asin (var_ref s)
+ ((expression float *
(var_ref y_over_x)
(expression float rsq
(expression float +
(expression float *
(var_ref y_over_x)
(var_ref y_over_x))
- (constant float (1.0))))))))))
+ (constant float (1.0)))))))
+ (return (var_ref s))))
(signature vec2
(parameters
(declare (in) vec2 y_over_x))
- ((return (call asin ((expression vec2 *
+ ((declare () vec2 s)
+ (call asin (var_ref s)
+ ((expression vec2 *
(var_ref y_over_x)
(expression vec2 rsq
(expression vec2 +
(expression vec2 *
(var_ref y_over_x)
(var_ref y_over_x))
- (constant float (1.0))))))))))
+ (constant float (1.0)))))))
+ (return (var_ref s))))
(signature vec3
(parameters
(declare (in) vec3 y_over_x))
- ((return (call asin ((expression vec3 *
+ ((declare () vec3 s)
+ (call asin (var_ref s)
+ ((expression vec3 *
(var_ref y_over_x)
(expression vec3 rsq
(expression vec3 +
(expression vec3 *
(var_ref y_over_x)
(var_ref y_over_x))
- (constant float (1.0))))))))))
+ (constant float (1.0)))))))
+ (return (var_ref s))))
(signature vec4
(parameters
(declare (in) vec4 y_over_x))
- ((return (call asin ((expression vec4 *
+ ((declare () vec4 s)
+ (call asin (var_ref s)
+ ((expression vec4 *
(var_ref y_over_x)
(expression vec4 rsq
(expression vec4 +
(expression vec4 *
(var_ref y_over_x)
(var_ref y_over_x))
- (constant float (1.0))))))))))
+ (constant float (1.0)))))))
+ (return (var_ref s))))
(signature float
(parameters
(if (expression bool >
(expression float abs (var_ref x))
(expression float * (constant float (1.0e-8)) (expression float abs (var_ref y)))) (
- (assign (x) (var_ref r) (call atan ((expression float / (var_ref y) (var_ref x)))))
+ (call atan (var_ref r) ((expression float / (var_ref y) (var_ref x))))
(if (expression bool < (var_ref x) (constant float (0.000000)) ) (
(if (expression bool >= (var_ref y) (constant float (0.000000)) )
((assign (x) (var_ref r) (expression float + (var_ref r) (constant float (3.141593)))))
(declare (in) vec2 y)
(declare (in) vec2 x))
((declare () vec2 r)
- (assign (x) (var_ref r)
- (call atan ((swiz x (var_ref y))
- (swiz x (var_ref x)))))
- (assign (y) (var_ref r)
- (call atan ((swiz y (var_ref y))
- (swiz y (var_ref x)))))
+ (declare () float temp)
+ (call atan (var_ref temp) ((swiz x (var_ref y)) (swiz x (var_ref x))))
+ (assign (x) (var_ref r) (var_ref temp))
+ (call atan (var_ref temp) ((swiz y (var_ref y)) (swiz y (var_ref x))))
+ (assign (y) (var_ref r) (var_ref temp))
(return (var_ref r))))
(signature vec3
(declare (in) vec3 y)
(declare (in) vec3 x))
((declare () vec3 r)
- (assign (x) (var_ref r)
- (call atan ((swiz x (var_ref y))
- (swiz x (var_ref x)))))
- (assign (y) (var_ref r)
- (call atan ((swiz y (var_ref y))
- (swiz y (var_ref x)))))
- (assign (z) (var_ref r)
- (call atan ((swiz z (var_ref y))
- (swiz z (var_ref x)))))
+ (declare () float temp)
+ (call atan (var_ref temp) ((swiz x (var_ref y)) (swiz x (var_ref x))))
+ (assign (x) (var_ref r) (var_ref temp))
+ (call atan (var_ref temp) ((swiz y (var_ref y)) (swiz y (var_ref x))))
+ (assign (y) (var_ref r) (var_ref temp))
+ (call atan (var_ref temp) ((swiz z (var_ref y)) (swiz z (var_ref x))))
+ (assign (z) (var_ref r) (var_ref temp))
(return (var_ref r))))
(signature vec4
(declare (in) vec4 y)
(declare (in) vec4 x))
((declare () vec4 r)
- (assign (x) (var_ref r)
- (call atan ((swiz x (var_ref y))
- (swiz x (var_ref x)))))
- (assign (y) (var_ref r)
- (call atan ((swiz y (var_ref y))
- (swiz y (var_ref x)))))
- (assign (z) (var_ref r)
- (call atan ((swiz z (var_ref y))
- (swiz z (var_ref x)))))
- (assign (w) (var_ref r)
- (call atan ((swiz w (var_ref y))
- (swiz w (var_ref x)))))
- (return (var_ref r)))))
+ (declare () float temp)
+ (call atan (var_ref temp) ((swiz x (var_ref y)) (swiz x (var_ref x))))
+ (assign (x) (var_ref r) (var_ref temp))
+ (call atan (var_ref temp) ((swiz y (var_ref y)) (swiz y (var_ref x))))
+ (assign (y) (var_ref r) (var_ref temp))
+ (call atan (var_ref temp) ((swiz z (var_ref y)) (swiz z (var_ref x))))
+ (assign (z) (var_ref r) (var_ref temp))
+ (call atan (var_ref temp) ((swiz w (var_ref y)) (swiz w (var_ref x))))
+ (assign (w) (var_ref r) (var_ref temp))
+ (return (var_ref r))))
))
void
ir_call::set_callee(ir_function_signature *sig)
{
- assert((this->type == NULL) || (this->type == sig->return_type));
-
this->callee = sig;
}
/**
- * IR instruction representing a function call
+ * HIR instruction representing a high-level function call, containing a list
+ * of parameters and returning a value in the supplied temporary.
*/
-class ir_call : public ir_rvalue {
+class ir_call : public ir_instruction {
public:
- ir_call(ir_function_signature *callee, exec_list *actual_parameters)
- : callee(callee)
+ ir_call(ir_function_signature *callee,
+ ir_dereference_variable *return_deref,
+ exec_list *actual_parameters)
+ : return_deref(return_deref), callee(callee)
{
ir_type = ir_type_call;
assert(callee->return_type != NULL);
- type = callee->return_type;
actual_parameters->move_nodes_to(& this->actual_parameters);
this->use_builtin = callee->is_builtin;
}
/**
* Generates an inline version of the function before @ir,
- * returning the return value of the function.
+ * storing the return value in return_deref.
*/
- ir_rvalue *generate_inline(ir_instruction *ir);
+ void generate_inline(ir_instruction *ir);
+
+ /**
+ * Storage for the function's return value.
+ * This must be NULL if the return type is void.
+ */
+ ir_dereference_variable *return_deref;
/* List of ir_rvalue of paramaters passed in this call. */
exec_list actual_parameters;
#include "ir_basic_block.h"
#include "glsl_types.h"
-class ir_has_call_visitor : public ir_hierarchical_visitor {
-public:
- ir_has_call_visitor()
- {
- has_call = false;
- }
-
- virtual ir_visitor_status visit_enter(ir_call *ir)
- {
- (void) ir;
- has_call = true;
- return visit_stop;
- }
-
- bool has_call;
-};
-
-bool
-ir_has_call(ir_instruction *ir)
-{
- ir_has_call_visitor v;
- ir->accept(&v);
- return v.has_call;
-}
-
/**
* Calls a user function for every basic block in the instruction stream.
*
call_for_basic_blocks(&ir_sig->body, callback, data);
}
- } else if (ir->as_assignment()) {
- /* If there's a call in the expression tree being assigned,
- * then that ends the BB too.
- *
- * The assumption is that any consumer of the basic block
- * walker is fine with the fact that the call is somewhere in
- * the tree even if portions of the tree may be evaluated
- * after the call.
- *
- * A consumer that has an issue with this could not process
- * the last instruction of the basic block. If doing so,
- * expression flattener may be useful before using the basic
- * block finder to get more maximal basic blocks out.
- */
- if (ir_has_call(ir)) {
- callback(leader, ir, data);
- leader = NULL;
- }
}
last = ir;
}
ir_call *
ir_call::clone(void *mem_ctx, struct hash_table *ht) const
{
+ ir_dereference_variable *new_return_ref = NULL;
+ if (this->return_deref != NULL)
+ new_return_ref = this->return_deref->clone(mem_ctx, ht);
+
exec_list new_parameters;
foreach_iter(exec_list_iterator, iter, this->actual_parameters) {
new_parameters.push_tail(ir->clone(mem_ctx, ht));
}
- return new(mem_ctx) ir_call(this->callee, &new_parameters);
+ return new(mem_ctx) ir_call(this->callee, new_return_ref, &new_parameters);
}
ir_expression *
ir_constant *
ir_call::constant_expression_value()
{
- if (this->type == glsl_type::error_type)
- return NULL;
-
return this->callee->constant_expression_value(&this->actual_parameters);
}
*
* This is used for breaking down matrix operations, where it's easier to
* create a temporary and work on each of its vector components individually.
- *
- * It is also used for automatic function inlining, where we want to take
- * an expression containing a call and move the call out to its own
- * assignment so that we can inline it at the appropriate place in the
- * instruction stream.
*/
#include "ir.h"
if (s != visit_continue)
return (s == visit_continue_with_parent) ? visit_continue : s;
+ if (this->return_deref != NULL) {
+ v->in_assignee = true;
+ s = this->return_deref->accept(v);
+ v->in_assignee = false;
+ if (s != visit_continue)
+ return (s == visit_continue_with_parent) ? visit_continue : s;
+ }
+
s = visit_list_elements(v, &this->actual_parameters, false);
if (s == visit_stop)
return s;
void
ir_print_visitor::visit(ir_call *ir)
{
- printf("(call %s (", ir->callee_name());
+ printf("(call %s ", ir->callee_name());
+ if (ir->return_deref)
+ ir->return_deref->accept(this);
+ printf(" (");
foreach_iter(exec_list_iterator, iter, *ir) {
ir_instruction *const inst = (ir_instruction *) iter.get();
ir_variable *read_declaration(s_expression *);
ir_if *read_if(s_expression *, ir_loop *);
ir_loop *read_loop(s_expression *);
+ ir_call *read_call(s_expression *);
ir_return *read_return(s_expression *);
ir_rvalue *read_rvalue(s_expression *);
ir_assignment *read_assignment(s_expression *);
ir_expression *read_expression(s_expression *);
- ir_call *read_call(s_expression *);
ir_swizzle *read_swizzle(s_expression *);
ir_constant *read_constant(s_expression *);
ir_texture *read_texture(s_expression *);
inst = read_if(list, loop_ctx);
} else if (strcmp(tag->value(), "loop") == 0) {
inst = read_loop(list);
+ } else if (strcmp(tag->value(), "call") == 0) {
+ inst = read_call(list);
} else if (strcmp(tag->value(), "return") == 0) {
inst = read_return(list);
} else if (strcmp(tag->value(), "function") == 0) {
rvalue = read_swizzle(list);
} else if (strcmp(tag->value(), "expression") == 0) {
rvalue = read_expression(list);
- } else if (strcmp(tag->value(), "call") == 0) {
- rvalue = read_call(list);
} else if (strcmp(tag->value(), "constant") == 0) {
rvalue = read_constant(list);
} else {
{
s_symbol *name;
s_list *params;
+ s_list *s_return = NULL;
- s_pattern pat[] = { "call", name, params };
- if (!MATCH(expr, pat)) {
- ir_read_error(expr, "expected (call <name> (<param> ...))");
+ ir_dereference_variable *return_deref = NULL;
+
+ s_pattern void_pat[] = { "call", name, params };
+ s_pattern non_void_pat[] = { "call", name, s_return, params };
+ if (MATCH(expr, non_void_pat)) {
+ return_deref = read_var_ref(s_return);
+ if (return_deref == NULL) {
+ ir_read_error(s_return, "when reading a call's return storage");
+ return NULL;
+ }
+ } else if (!MATCH(expr, void_pat)) {
+ ir_read_error(expr, "expected (call <name> [<deref>] (<param> ...))");
return NULL;
}
return NULL;
}
- return new(mem_ctx) ir_call(callee, ¶meters);
+ if (callee->return_type == glsl_type::void_type && return_deref) {
+ ir_read_error(expr, "call has return value storage but void type");
+ return NULL;
+ } else if (callee->return_type != glsl_type::void_type && !return_deref) {
+ ir_read_error(expr, "call has non-void type but no return value storage");
+ return NULL;
+ }
+
+ return new(mem_ctx) ir_call(callee, return_deref, ¶meters);
}
ir_expression *
abort();
}
+ if (ir->return_deref) {
+ if (ir->return_deref->type != callee->return_type) {
+ printf("callee type %s does not match return storage type %s\n",
+ callee->return_type->name, ir->return_deref->type->name);
+ abort();
+ }
+ } else if (callee->return_type != glsl_type::void_type) {
+ printf("ir_call has non-void callee but no return storage\n");
+ abort();
+ }
+
const exec_node *formal_param_node = callee->parameters.head;
const exec_node *actual_param_node = ir->actual_parameters.head;
while (true) {
sig_iter.next();
}
+ if (ir->return_deref != NULL) {
+ ir_variable *const var = ir->return_deref->variable_referenced();
+
+ if (strcmp(name, var->name) == 0) {
+ found = true;
+ return visit_stop;
+ }
+ }
+
return visit_continue_with_parent;
}
continue;
assert(inst->as_assignment()
+ || inst->as_call()
|| ((var != NULL) && (var->mode == ir_var_temporary)));
if (make_copies) {
ir_visitor_status
ir_constant_folding_visitor::visit_enter(ir_call *ir)
{
+ /* Attempt to constant fold parameters */
exec_list_iterator sig_iter = ir->get_callee()->parameters.iterator();
foreach_iter(exec_list_iterator, iter, *ir) {
ir_rvalue *param_rval = (ir_rvalue *)iter.get();
sig_iter.next();
}
+ /* Next, see if the call can be replaced with an assignment of a constant */
+ ir_constant *const_val = ir->constant_expression_value();
+
+ if (const_val != NULL) {
+ ir_assignment *assignment =
+ new(ralloc_parent(ir)) ir_assignment(ir->return_deref, const_val);
+ ir->replace_with(assignment);
+ }
+
return visit_continue_with_parent;
}
ir_visitor_status
ir_constant_variable_visitor::visit_enter(ir_call *ir)
{
+ /* Mark any out parameters as assigned to */
exec_list_iterator sig_iter = ir->get_callee()->parameters.iterator();
foreach_iter(exec_list_iterator, iter, *ir) {
ir_rvalue *param_rval = (ir_rvalue *)iter.get();
}
sig_iter.next();
}
+
+ /* Mark the return storage as having been assigned to */
+ if (ir->return_deref != NULL) {
+ ir_variable *var = ir->return_deref->variable_referenced();
+ struct assignment_entry *entry;
+
+ assert(var);
+ entry = get_assignment_entry(var, &this->list);
+ entry->assignment_count++;
+ }
+
return visit_continue;
}
* Don't do so if it's a shader output, though.
*/
if (entry->var->mode != ir_var_out &&
- entry->var->mode != ir_var_inout &&
- !ir_has_call(entry->assign)) {
+ entry->var->mode != ir_var_inout) {
entry->assign->remove();
progress = true;
}
}
- /* Add this instruction to the assignment list available to be removed.
- * But not if the assignment has other side effects.
- */
- if (ir_has_call(ir))
- return progress;
-
+ /* Add this instruction to the assignment list available to be removed. */
assignment_entry *entry = new(ctx) assignment_entry(var, ir);
assignments->push_tail(entry);
virtual ir_visitor_status visit_enter(ir_expression *);
virtual ir_visitor_status visit_enter(ir_call *);
- virtual ir_visitor_status visit_enter(ir_assignment *);
virtual ir_visitor_status visit_enter(ir_return *);
virtual ir_visitor_status visit_enter(ir_texture *);
virtual ir_visitor_status visit_enter(ir_swizzle *);
};
-bool
-automatic_inlining_predicate(ir_instruction *ir)
-{
- ir_call *call = ir->as_call();
-
- if (call && can_inline(call))
- return true;
-
- return false;
-}
-
bool
do_function_inlining(exec_list *instructions)
{
ir_function_inlining_visitor v;
- do_expression_flattening(instructions, automatic_inlining_predicate);
-
v.run(instructions);
return v.progress;
replace_return_with_assignment(ir_instruction *ir, void *data)
{
void *ctx = ralloc_parent(ir);
- ir_variable *retval = (ir_variable *)data;
+ ir_dereference *orig_deref = (ir_dereference *) data;
ir_return *ret = ir->as_return();
if (ret) {
if (ret->value) {
- ir_rvalue *lhs = new(ctx) ir_dereference_variable(retval);
+ ir_rvalue *lhs = orig_deref->clone(ctx, NULL);
ret->replace_with(new(ctx) ir_assignment(lhs, ret->value, NULL));
} else {
/* un-valued return has to be the last return, or we shouldn't
}
}
-ir_rvalue *
+void
ir_call::generate_inline(ir_instruction *next_ir)
{
void *ctx = ralloc_parent(this);
ir_variable **parameters;
int num_parameters;
int i;
- ir_variable *retval = NULL;
struct hash_table *ht;
ht = hash_table_ctor(0, hash_table_pointer_hash, hash_table_pointer_compare);
parameters = new ir_variable *[num_parameters];
- /* Generate storage for the return value. */
- if (!this->callee->return_type->is_void()) {
- retval = new(ctx) ir_variable(this->callee->return_type, "_ret_val",
- ir_var_auto);
- next_ir->insert_before(retval);
- }
-
/* Generate the declarations for the parameters to our inlined code,
* and set up the mapping of real function body variables to ours.
*/
ir_instruction *new_ir = ir->clone(ctx, ht);
new_instructions.push_tail(new_ir);
- visit_tree(new_ir, replace_return_with_assignment, retval);
+ visit_tree(new_ir, replace_return_with_assignment, this->return_deref);
}
/* If any samplers were passed in, replace any deref of the sampler
delete [] parameters;
hash_table_dtor(ht);
-
- if (retval)
- return new(ctx) ir_dereference_variable(retval);
- else
- return NULL;
}
ir_function_inlining_visitor::visit_enter(ir_call *ir)
{
if (can_inline(ir)) {
- /* If the call was part of some tree, then it should have been
- * flattened out or we shouldn't have seen it because of a
- * visit_continue_with_parent in this visitor.
- */
- assert(ir == base_ir);
-
- (void) ir->generate_inline(ir);
+ ir->generate_inline(ir);
ir->remove();
this->progress = true;
}
}
-ir_visitor_status
-ir_function_inlining_visitor::visit_enter(ir_assignment *ir)
-{
- ir_call *call = ir->rhs->as_call();
- if (!call || !can_inline(call))
- return visit_continue;
-
- /* generates the parameter setup, function body, and returns the return
- * value of the function
- */
- ir_rvalue *rhs = call->generate_inline(ir);
- assert(rhs);
-
- ir->rhs = rhs;
- this->progress = true;
-
- return visit_continue;
-}
-
/**
* Replaces references to the "sampler" variable with a clone of "deref."
*
sig_iter.next();
}
+ if (ir->return_deref && check_graft(ir, ir->return_deref->var) == visit_stop)
+ return visit_stop;
+
return visit_continue;
}