Improve the life-time evaluation of temporary registers by also tracking writes in both if and else branches and in up to 32 nested scopes. As a result the estimated required register life-times can be further reduced enabling more registers to be merged. --- .../state_tracker/st_glsl_to_tgsi_temprename.cpp | 211 ++++++++++++++++++++- 1 file changed, 205 insertions(+), 6 deletions(-)
diff --git a/src/mesa/state_tracker/st_glsl_to_tgsi_temprename.cpp b/src/mesa/state_tracker/st_glsl_to_tgsi_temprename.cpp index 36ddd7a258..0828927457 100644 --- a/src/mesa/state_tracker/st_glsl_to_tgsi_temprename.cpp +++ b/src/mesa/state_tracker/st_glsl_to_tgsi_temprename.cpp @@ -58,6 +58,10 @@ using std::numeric_limits; #define nullptr 0 #endif +#define SUPPORED_IFELSE_NESTING_SCOPES 32 +#define IFELSE_WRITE_CONDITIONAL -1 +#define IFELSE_WRITE_UNRESOLVED 0 + #ifndef NDEBUG /* Helper function to check whether we want to seen debugging output */ static inline bool is_debug_enabled () @@ -98,7 +102,9 @@ public: int begin() const; int loop_break_line() const; + const prog_scope *in_else_scope() const; const prog_scope *in_ifelse_scope() const; + const prog_scope *in_parent_ifelse_scope() const; const prog_scope *in_switchcase_scope() const; const prog_scope *innermost_loop() const; const prog_scope *outermost_loop() const; @@ -107,13 +113,14 @@ public: bool is_loop() const; bool is_in_loop() const; bool is_conditional() const; + bool is_child_of(const prog_scope *scope) const; + bool is_child_of_ifelse_id_sibling(const prog_scope *scope) const; bool break_is_for_switchcase() const; bool contains_range_of(const prog_scope& other) const; void set_end(int end); void set_loop_break_line(int line); - private: prog_scope_type scope_type; int scope_id; @@ -140,20 +147,34 @@ private: class temp_comp_access { public: temp_comp_access(); + void record_read(int line, prog_scope *scope); void record_write(int line, prog_scope *scope); lifetime get_required_lifetime(); private: void propagate_lifetime_to_dominant_write_scope(); + void record_write_in_ifelse(const prog_scope& scope); + bool conditional_ifelse_write_in_loop() const; + + void record_ifelse_write(const prog_scope& scope); + void record_if_write(const prog_scope& scope); + void record_else_write(const prog_scope& scope); prog_scope *last_read_scope; prog_scope *first_read_scope; prog_scope *first_write_scope; + const prog_scope *last_ifelse_write_scope; + int first_write; int last_read; int last_write; int first_read; - bool keep_for_full_loop; + + int write_unconditional_in_loop_id; + unsigned int if_write_flags; + int next_ifelse_nesting_depth; + + bool else_write; }; class temp_access { @@ -258,6 +279,32 @@ const prog_scope *prog_scope::outermost_loop() const return loop; } +bool prog_scope::is_child_of_ifelse_id_sibling(const prog_scope *scope) const +{ + const prog_scope *my_parent = in_parent_ifelse_scope(); + while (my_parent) { + /* is a direct child? */ + if (my_parent == scope) + return false; + /* is a child of the conditions sibling? */ + if (my_parent->id() == scope->id()) + return true; + my_parent = my_parent->in_parent_ifelse_scope(); + } + return false; +} + +bool prog_scope::is_child_of(const prog_scope *scope) const +{ + const prog_scope *my_parent = parent(); + while (my_parent) { + if (my_parent == scope) + return true; + my_parent = my_parent->parent(); + } + return false; +} + const prog_scope *prog_scope::enclosing_conditional() const { if (is_conditional()) @@ -282,6 +329,25 @@ bool prog_scope::is_conditional() const scope_type == switch_default_branch; } +const prog_scope *prog_scope::in_else_scope() const +{ + if (scope_type == else_branch) + return this; + + if (parent_scope) + return parent_scope->in_else_scope(); + + return nullptr; +} + +const prog_scope *prog_scope::in_parent_ifelse_scope() const +{ + if (parent_scope) + return parent_scope->in_ifelse_scope(); + else + return nullptr; +} + const prog_scope *prog_scope::in_ifelse_scope() const { if (scope_type == if_branch || @@ -439,10 +505,15 @@ temp_comp_access::temp_comp_access(): last_read_scope(nullptr), first_read_scope(nullptr), first_write_scope(nullptr), + last_ifelse_write_scope(nullptr), first_write(-1), last_read(-1), last_write(-1), - first_read(numeric_limits<int>::max()) + first_read(numeric_limits<int>::max()), + write_unconditional_in_loop_id(IFELSE_WRITE_UNRESOLVED), + if_write_flags(0), + next_ifelse_nesting_depth(0), + else_write(false) { } @@ -455,6 +526,39 @@ void temp_comp_access::record_read(int line, prog_scope *scope) first_read = line; first_read_scope = scope; } + + /* If we have not yet written and it is not yet established that the write + * is conditional in a loop then check whether we read first in an if/else + * branch. + */ + if (write_unconditional_in_loop_id == IFELSE_WRITE_UNRESOLVED) { + + const prog_scope *ifelse_scope = scope->in_ifelse_scope(); + if (ifelse_scope && ifelse_scope->innermost_loop()) { + + if (last_ifelse_write_scope) { + /* Has been written in a parent scope, which makes the temporary + * unconditionally set at this point. + */ + if (scope->is_child_of(last_ifelse_write_scope)) + return; + + /* Has been written in the same scope before it was read? */ + if (ifelse_scope->type() == if_branch) { + if (last_ifelse_write_scope->id() == scope->id()) + return; + } else { + if (else_write) + return; + } + } + + /* The temporary was read conditionally befor it was written, hence + * it should be treated like conditionally written. + */ + write_unconditional_in_loop_id = IFELSE_WRITE_CONDITIONAL; + } + } } void temp_comp_access::record_write(int line, prog_scope *scope) @@ -465,6 +569,100 @@ void temp_comp_access::record_write(int line, prog_scope *scope) first_write = line; first_write_scope = scope; } + + /* If it is not yet established that this temporary is written conditionally + * within a loop, then track this write + */ + if (write_unconditional_in_loop_id >= 0) { + const prog_scope *ifelse_scope = scope->in_ifelse_scope(); + if (ifelse_scope && ifelse_scope->innermost_loop()) + record_write_in_ifelse(*ifelse_scope); + } +} + +void temp_comp_access::record_write_in_ifelse(const prog_scope& scope) +{ + if (write_unconditional_in_loop_id != IFELSE_WRITE_UNRESOLVED) + return; + + /* If the nesting depth is larger than the supported level, + * then we asume conditional writes. + */ + if (next_ifelse_nesting_depth >= SUPPORED_IFELSE_NESTING_SCOPES) { + write_unconditional_in_loop_id = IFELSE_WRITE_CONDITIONAL; + return; + } + + else_write = (scope.type() == else_branch); + record_ifelse_write(scope); +} + +void temp_comp_access::record_ifelse_write(const prog_scope& scope) +{ + if (scope.type() == if_branch) { + record_if_write(scope); + write_unconditional_in_loop_id = IFELSE_WRITE_UNRESOLVED; + } else + record_else_write(scope); +} + +void temp_comp_access::record_if_write(const prog_scope& scope) +{ + /* Record write, if non has been recorded yet, or if it is in + * a child of the else branch related to the last active if branch. + */ + if (!last_ifelse_write_scope || + (last_ifelse_write_scope->id() != scope.id() && + scope.is_child_of_ifelse_id_sibling(last_ifelse_write_scope))) { + if_write_flags |= 1 << next_ifelse_nesting_depth; + last_ifelse_write_scope = &scope; + next_ifelse_nesting_depth++; + } +} + +void temp_comp_access::record_else_write(const prog_scope& scope) +{ + int mask = 1 << (next_ifelse_nesting_depth - 1); + + if ((if_write_flags & mask) && + (scope.id() == last_ifelse_write_scope->id())) { + --next_ifelse_nesting_depth; + if_write_flags &= ~mask; + + if (1 << (next_ifelse_nesting_depth - 1) & if_write_flags) { + /* We already recorded a write within a parent ifelse scope, and + * it is not yet resolved. Mark it as the last relevant ifelse + * scope. + */ + last_ifelse_write_scope = scope.parent()->in_ifelse_scope(); + } else { + last_ifelse_write_scope = nullptr; + } + + const prog_scope *parent_ifelse = scope.parent()->in_ifelse_scope(); + + /* If some parent is if/else and in a loop then propagate the + * write to that scope. Otherwise the write is unconditional + * because it happens in both corresponding if-else branches + * in this loop, and hence, record the loop id to signal the + * resolution. + */ + if (parent_ifelse && parent_ifelse->is_in_loop()) { + record_ifelse_write(*parent_ifelse); + } else { + write_unconditional_in_loop_id = scope.innermost_loop()->id(); + } + } else { + /* The temporary was not written in the if-branch corresponding + * to this else scope, hence the write is conditional. + */ + write_unconditional_in_loop_id = IFELSE_WRITE_CONDITIONAL; + } +} + +bool temp_comp_access::conditional_ifelse_write_in_loop() const +{ + return write_unconditional_in_loop_id <= IFELSE_WRITE_UNRESOLVED; } void temp_comp_access::propagate_lifetime_to_dominant_write_scope() @@ -514,7 +712,8 @@ lifetime temp_comp_access::get_required_lifetime() */ const prog_scope *conditional = enclosing_scope_first_write->enclosing_conditional(); if (conditional && conditional->is_in_loop() && - !conditional->contains_range_of(*last_read_scope)) { + !conditional->contains_range_of(*last_read_scope) && + (conditional->in_switchcase_scope() || conditional_ifelse_write_in_loop())) { keep_for_full_loop = true; enclosing_scope_first_write = conditional->outermost_loop(); } @@ -613,8 +812,8 @@ get_temp_registers_required_lifetimes(void *mem_ctx, exec_list *instructions, int ntemps, struct lifetime *lifetimes) { int line = 0; - int loop_id = 0; - int if_id = 0; + int loop_id = 1; + int if_id = 1; int switch_id = 0; bool is_at_end = false; bool ok = true; -- 2.13.6 _______________________________________________ mesa-dev mailing list mesa-dev@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/mesa-dev