[Mesa-dev] [PATCH 2/3] mesa/st: glsl_to_tgsi Implement a new lifetime tracker for temporaries

Gert Wollny gw.fossdev at gmail.com
Fri Jun 9 23:15:07 UTC 2017


This patch adds new classes and tests to implement a tracker for the
life time of temporary registers for the register renaming stage of
glsl_to_tgsi. The tracker aims at estimating the shortest possible
life time for each register. The code base requires c++11, the flag is
propagated from the LLVM_CXXFLAGS.
---
 configure.ac                                       |   1 +
 src/mesa/Makefile.am                               |   4 +-
 src/mesa/Makefile.sources                          |   2 +
 .../state_tracker/st_glsl_to_tgsi_temprename.cpp   | 551 ++++++++++++++
 .../state_tracker/st_glsl_to_tgsi_temprename.h     | 114 +++
 src/mesa/state_tracker/tests/Makefile.am           |  40 ++
 src/mesa/state_tracker/tests/st-renumerate-test    | 210 ++++++
 .../tests/test_glsl_to_tgsi_lifetime.cpp           | 789 +++++++++++++++++++++
 8 files changed, 1709 insertions(+), 2 deletions(-)
 create mode 100644 src/mesa/state_tracker/st_glsl_to_tgsi_temprename.cpp
 create mode 100644 src/mesa/state_tracker/st_glsl_to_tgsi_temprename.h
 create mode 100644 src/mesa/state_tracker/tests/Makefile.am
 create mode 100755 src/mesa/state_tracker/tests/st-renumerate-test
 create mode 100644 src/mesa/state_tracker/tests/test_glsl_to_tgsi_lifetime.cpp

diff --git a/configure.ac b/configure.ac
index f379ba8573..579e159420 100644
--- a/configure.ac
+++ b/configure.ac
@@ -2827,6 +2827,7 @@ AC_CONFIG_FILES([Makefile
 		src/mesa/drivers/osmesa/osmesa.pc
 		src/mesa/drivers/x11/Makefile
 		src/mesa/main/tests/Makefile
+		src/mesa/state_tracker/tests/Makefile
 		src/util/Makefile
 		src/util/tests/hash_table/Makefile
 		src/vulkan/Makefile])
diff --git a/src/mesa/Makefile.am b/src/mesa/Makefile.am
index 53f311d2a9..72ffd61212 100644
--- a/src/mesa/Makefile.am
+++ b/src/mesa/Makefile.am
@@ -19,7 +19,7 @@
 # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 # IN THE SOFTWARE.
 
-SUBDIRS = . main/tests
+SUBDIRS = . main/tests state_tracker/tests
 
 if HAVE_XLIB_GLX
 SUBDIRS += drivers/x11
@@ -101,7 +101,7 @@ AM_CFLAGS = \
 	$(VISIBILITY_CFLAGS) \
 	$(MSVC2013_COMPAT_CFLAGS)
 AM_CXXFLAGS = \
-	$(LLVM_CFLAGS) \
+        $(LLVM_CXXFLAGS) \
 	$(VISIBILITY_CXXFLAGS) \
 	$(MSVC2013_COMPAT_CXXFLAGS)
 
diff --git a/src/mesa/Makefile.sources b/src/mesa/Makefile.sources
index 4450d80090..908d1acff6 100644
--- a/src/mesa/Makefile.sources
+++ b/src/mesa/Makefile.sources
@@ -507,6 +507,8 @@ STATETRACKER_FILES = \
 	state_tracker/st_glsl_to_tgsi.h \
 	state_tracker/st_glsl_to_tgsi_private.cpp \
 	state_tracker/st_glsl_to_tgsi_private.h \
+        state_tracker/st_glsl_to_tgsi_temprename.cpp \
+	state_tracker/st_glsl_to_tgsi_temprename.h \
 	state_tracker/st_glsl_types.cpp \
 	state_tracker/st_glsl_types.h \
 	state_tracker/st_manager.c \
diff --git a/src/mesa/state_tracker/st_glsl_to_tgsi_temprename.cpp b/src/mesa/state_tracker/st_glsl_to_tgsi_temprename.cpp
new file mode 100644
index 0000000000..389a4b6b5f
--- /dev/null
+++ b/src/mesa/state_tracker/st_glsl_to_tgsi_temprename.cpp
@@ -0,0 +1,551 @@
+/*
+ * Copyright © 2017 Gert Wollny
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+
+#include "st_glsl_to_tgsi_temprename.h"
+#include <tgsi/tgsi_info.h>
+#include <mesa/program/prog_instruction.h>
+#include <stack>
+#include <limits>
+#include <iostream>
+
+
+using std::vector;
+using std::stack;
+using std::shared_ptr;
+using std::weak_ptr;
+using std::pair;
+using std::make_pair;
+using std::make_shared;
+using std::numeric_limits;
+
+tgsi_temp_lifetime::tgsi_temp_lifetime(exec_list *instructions, int ntemps):
+   lifetimes(ntemps)
+{
+   evaluate(instructions);
+}
+
+const std::vector<std::pair<int, int> >& tgsi_temp_lifetime::get_lifetimes() const
+{
+   return lifetimes;
+}
+
+void tgsi_temp_lifetime::evaluate(exec_list *instructions)
+{
+   int i = 0;
+   int loop_id = 0;
+   int if_id = 0;
+   int switch_id = 0;
+   int scope_level = 0;
+   bool is_at_end = false;
+   shared_ptr<prog_scope> current = make_shared<prog_scope>(sct_outer, 0, scope_level++, i);
+   stack<shared_ptr<prog_scope>> scope_stack;
+
+   vector<temp_access> acc(lifetimes.size());
+
+   foreach_in_list(glsl_to_tgsi_instruction, inst, instructions) {
+      if (is_at_end) {
+         std::cerr << "GLSL_TO_TGSI: shader has instructions past end marker\n";
+         break;
+      }
+
+      switch (inst->op) {
+      case TGSI_OPCODE_BGNLOOP: {
+         shared_ptr<prog_scope> scope = make_shared<prog_scope>(current, sct_loop, loop_id, scope_level, i);
+         ++loop_id;
+         ++scope_level;
+         scope_stack.push(current);
+         current = scope;
+         break;
+      }
+      case TGSI_OPCODE_ENDLOOP: {
+         --scope_level;
+         current->set_end(i);
+         current = scope_stack.top();
+         scope_stack.pop();
+         break;
+      }
+      case TGSI_OPCODE_IF:
+      case TGSI_OPCODE_UIF:{
+         for (unsigned j = 0; j < num_inst_src_regs(inst); j++) {
+            if (inst->src[j].file == PROGRAM_TEMPORARY) {
+               acc[inst->src[j].index].append(i, acc_read, current);
+            }
+         }
+         shared_ptr<prog_scope> scope = make_shared<prog_scope>(current, sct_if, if_id, scope_level, i);
+         ++if_id;
+         ++scope_level;
+         scope_stack.push(current);
+         current = scope;
+         break;
+      }
+      case TGSI_OPCODE_ELSE: {
+         current->set_end(i-1);
+         current = make_shared<prog_scope>(current->parent(), sct_else, current->id(),
+                                              current->level(), i);
+         break;
+      }
+      case TGSI_OPCODE_END:{
+         current->set_end(i);
+         is_at_end = true;
+         break;
+      }
+      case TGSI_OPCODE_ENDIF:{
+         --scope_level;
+         current->set_end(i-1);
+         current = scope_stack.top();
+         scope_stack.pop();
+         break;
+      }
+      case TGSI_OPCODE_SWITCH: {
+         shared_ptr<prog_scope> scope = make_shared<prog_scope>(current, sct_switch, switch_id, scope_level, i);
+         ++scope_level;
+         ++switch_id;
+         scope_stack.push(current);
+         current = scope;
+         break;
+      }
+      case TGSI_OPCODE_ENDSWITCH: {
+         --scope_level;
+         current->set_end(i-1);
+
+         // remove the case level
+         if  (current->type() != sct_switch ) {
+            current = scope_stack.top();
+            scope_stack.pop();
+         }
+         current = scope_stack.top();
+         scope_stack.pop();
+         break;
+      }
+
+      case TGSI_OPCODE_CASE:
+      case TGSI_OPCODE_DEFAULT: {
+         if ( current->type() == sct_switch ) {
+            scope_stack.push(current);
+            current = make_shared<prog_scope>(current, sct_case, current->id(), scope_level, i);
+         }else{
+            auto p = current->parent();
+            auto scope = make_shared<prog_scope>(p, sct_case, p->id(), p->level(), i);
+            if (current->end() == -1)
+               scope->set_previous(current);
+            current = scope;
+         }
+         for (unsigned j = 0; j < num_inst_src_regs(inst); j++) {
+            if (inst->src[j].file == PROGRAM_TEMPORARY) {
+               acc[inst->src[j].index].append(i, acc_read, current);
+            }
+         }
+      }
+      case TGSI_OPCODE_BRK:  {
+         if ( current->type() == sct_case) {
+            current->set_end(i-1);
+         }
+      }
+      case TGSI_OPCODE_CONT: {
+         current->set_continue(current, i);
+         break;
+      }
+
+      default: {
+
+         for (unsigned j = 0; j < num_inst_dst_regs(inst); j++) {
+            if (inst->dst[j].file == PROGRAM_TEMPORARY) {
+               acc[inst->dst[j].index].append(i, acc_write, current);
+            }
+         }
+
+         for (unsigned j = 0; j < num_inst_src_regs(inst); j++) {
+            if (inst->src[j].file == PROGRAM_TEMPORARY) {
+               acc[inst->src[j].index].append(i, acc_read, current);
+            }
+         }
+
+         for (unsigned j = 0; j < inst->tex_offset_num_offset; j++) {
+            if (inst->tex_offsets[j].file == PROGRAM_TEMPORARY) {
+               acc[inst->tex_offsets[j].index].append(i, acc_read, current);
+            }
+         }
+
+      } // end default
+      } // end switch (op)
+
+      ++i;
+   }
+
+   // make sure last scope is closed, even though no
+   // TGSI_OPCODE_END was given
+   if (current->end() < 0) {
+      current->set_end(i-1);
+   }
+
+   for(unsigned i = 1; i < lifetimes.size(); ++i) {
+      lifetimes[i] = acc[i].get_required_lifetime();
+   }
+}
+
+
+tgsi_temp_lifetime::prog_scope::prog_scope(e_scope_type type, int id, int lvl, int s_begin):
+   prog_scope(std::shared_ptr<prog_scope>(), type, id, lvl, s_begin)
+{
+}
+
+tgsi_temp_lifetime::prog_scope::prog_scope(std::shared_ptr<prog_scope> p, e_scope_type type, int id, int lvl, int s_begin):
+   scope_type(type),
+   scope_id(id),
+   nested_level(lvl),
+   scope_begin(s_begin),
+   scope_end(-1),
+   loop_continue(numeric_limits<int>::max()),
+   parent_scope(p)
+{
+}
+
+tgsi_temp_lifetime::e_scope_type tgsi_temp_lifetime::prog_scope::type() const
+{
+   return scope_type;
+}
+
+
+std::shared_ptr<tgsi_temp_lifetime::prog_scope>
+tgsi_temp_lifetime::prog_scope::parent() const
+{
+   return parent_scope;
+}
+
+int tgsi_temp_lifetime::prog_scope::level() const
+{
+   return nested_level;
+}
+
+bool tgsi_temp_lifetime::prog_scope::in_loop() const
+{
+   if (scope_type == sct_loop)
+      return true;
+   if (parent_scope)
+      return parent_scope->in_loop();
+   return false;
+}
+
+const tgsi_temp_lifetime::prog_scope *
+tgsi_temp_lifetime::prog_scope::get_parent_loop() const
+{
+   if (scope_type == sct_loop)
+      return this;
+   if (parent_scope)
+      return parent_scope->get_parent_loop();
+   else
+      return nullptr;
+}
+
+bool tgsi_temp_lifetime::prog_scope::contains(const prog_scope& other) const
+{
+   return (begin() <= other.begin()) &&  (end() >= other.end());
+}
+
+bool tgsi_temp_lifetime::prog_scope::is_conditional() const
+{
+   return scope_type == sct_if || scope_type == sct_else ||
+         scope_type == sct_case;
+}
+
+const tgsi_temp_lifetime::prog_scope *
+tgsi_temp_lifetime::prog_scope::in_ifelse() const
+{
+   if (scope_type == sct_if ||
+       scope_type == sct_else)
+      return this;
+   else if (parent_scope)
+      return parent_scope->in_ifelse();
+   else
+      return nullptr;
+}
+
+const tgsi_temp_lifetime::prog_scope *
+tgsi_temp_lifetime::prog_scope::in_switchcase() const
+{
+   if (scope_type == sct_case)
+      return this;
+   else if (parent_scope)
+      return parent_scope->in_switchcase();
+   else
+      return nullptr;
+}
+
+int tgsi_temp_lifetime::prog_scope::id() const
+{
+   return scope_id;
+}
+
+int tgsi_temp_lifetime::prog_scope::begin() const
+{
+   return scope_begin;
+}
+
+int tgsi_temp_lifetime::prog_scope::end() const
+{
+   return scope_end;
+}
+
+void tgsi_temp_lifetime::prog_scope::set_previous(std::shared_ptr<prog_scope> prev)
+{
+   previous = prev;
+}
+
+void tgsi_temp_lifetime::prog_scope::set_end(int end)
+{
+   if (scope_end == -1) {
+      scope_end = end;
+      if (previous)
+         previous->set_end(end);
+   }
+}
+
+void tgsi_temp_lifetime::prog_scope::set_continue(weak_ptr<prog_scope> scope, int i)
+{
+   if (scope_type == sct_loop) {
+      loop_to_continue_scope = scope;
+      loop_continue = i;
+   } else if (parent_scope)
+      parent()->set_continue(scope, i);
+}
+
+int tgsi_temp_lifetime::prog_scope::loop_continue_idx() const
+{
+   return loop_continue;
+}
+
+void tgsi_temp_lifetime::temp_access::append(int index, e_acc_type rw, std::shared_ptr<prog_scope> pstate)
+{
+   temp_access_record r = {index, rw, pstate};
+   timeline.push_back(r);
+}
+
+pair<int, int> tgsi_temp_lifetime::temp_access::get_required_lifetime() const
+{
+   bool keep_for_full_loop = false;
+
+   std::shared_ptr<prog_scope> lr_scope;
+   std::shared_ptr<prog_scope> fr_scope;
+   std::shared_ptr<prog_scope> fw_scope;
+   const prog_scope *fw_ifthen_scope = nullptr;
+   const prog_scope *fw_switch_scope = nullptr;
+
+   int first_write = -1;
+   int last_read = -1;
+   int first_read = numeric_limits<int>::max();
+
+   for (temp_access_record i: timeline) {
+      if (i.acc == acc_read) {
+         last_read = i.index;
+         lr_scope = i.pstate;
+         if (first_read > i.index) {
+            first_read = i.index;
+            fr_scope = i.pstate;
+         }
+      }else{
+         if (first_write == -1) {
+            first_write = i.index;
+            fw_scope = i.pstate;
+
+            // we write in an if-branch
+            fw_ifthen_scope = i.pstate->in_ifelse();
+            if (fw_ifthen_scope && fw_ifthen_scope->in_loop()) {
+               // value not always written, in loops we must keep it
+               keep_for_full_loop = true;
+            } else {
+               // test for switch-case
+               fw_switch_scope = i.pstate->in_switchcase();
+
+               if (fw_switch_scope && fw_switch_scope->in_loop()) {
+                  keep_for_full_loop = true;
+               }
+            }
+         } else if (keep_for_full_loop) {
+
+            // written in if branch?
+            // disable because read first in else branch
+            // makes this invalid and this is not (yet) tracked
+            if (0 && fw_ifthen_scope) {
+               auto s = i.pstate->in_ifelse();
+               // also written in the else branch?
+               if (s  && (s->id() == fw_ifthen_scope->id())) {
+                  keep_for_full_loop = false;
+               }
+            }
+         }
+      }
+   }
+
+   // this temp is only read, this is undefined
+   // behaviour, so we can use the register otherwise
+   if (!fw_scope) {
+      return make_pair(-1, -1);
+   }
+
+   // Only written to, just make sure it doesn't overlap
+   if (!lr_scope) {
+      return make_pair(first_write, first_write);
+   }
+
+   int target_level = -1;
+   // evaluate the shared scope
+   while (target_level  < 0) {
+      if (lr_scope->contains(*fw_scope)) {
+         target_level = lr_scope->level();
+      } else if (fw_scope->contains(*lr_scope)) {
+         target_level = fw_scope->level();
+      } else {
+         // scopes (partially) disjunct, move up
+         if (lr_scope->type() == sct_loop) {
+            last_read = lr_scope->end();
+         }
+         lr_scope = lr_scope->parent();
+      }
+   }
+
+   // propagate the read scope to the target_level
+   while (lr_scope->level() > target_level) {
+      // if the read is in a loop we need to extend the
+      // variables life time to the end of that loop
+      if (lr_scope->type() == sct_loop) {
+         last_read = lr_scope->end();
+      }
+      lr_scope = lr_scope->parent();
+   }
+
+
+   // propagate the first_write scope to the target_level
+   bool has_continue = false;
+   while (target_level < fw_scope->level()) {
+
+      // propagate lifetime also if there was a continue
+      // in a loop and the write was after the continue
+      if (has_continue  || (fw_scope->loop_continue_idx() < first_write)) {
+         has_continue  = true;
+         first_write = fw_scope->begin();
+         int lr = fw_scope->end();
+         if (last_read < lr)
+            last_read = lr;
+      }
+
+      if ((fw_scope->type() == sct_loop) && (first_read < first_write)) {
+         first_write = fw_scope->begin();
+         int lr = fw_scope->end();
+         if (last_read < lr)
+            last_read = lr;
+      }
+
+      fw_scope = fw_scope->parent();
+
+      // if the value is conditionally written in a loop
+      // then propagate its lifetime to the full loop
+      if (fw_scope->type() == sct_loop) {
+         if (keep_for_full_loop || (first_read < first_write)) {
+            first_write = fw_scope->begin();
+            int lr = fw_scope->end();
+            if (last_read < lr)
+               last_read = lr;
+         }
+      }
+
+      // if we currently don't propagate the lifetime but
+      // the enclosing scope is a conditional within a loop
+      // up to the last-read level we need to propagate,
+      // todo: to tighten the life time check whether the value
+      // is written in all consitional code path below the loop
+      if (!keep_for_full_loop &&
+          fw_scope->is_conditional() &&
+          fw_scope->in_loop()) {
+         keep_for_full_loop = true;
+      }
+   }
+
+
+   
+   
+   // same level and same range means it is first
+   // written and last read in the same scope
+   // ignore the case when first read is before
+   // first write, because it is undefined behaviour
+   if ((lr_scope->begin() == fw_scope->begin()) &&
+       (lr_scope->end() == fw_scope->end())) {
+      return make_pair(first_write, last_read);
+   }
+
+   // different scopes,
+   if (!keep_for_full_loop && first_read > first_write) {
+      return make_pair(first_write, last_read);
+   }else{
+      // 1. if the value is not always written in a loop
+      // it must be kept for the whole loop scope.
+      //
+      // 2. if the value is read (maybe conditionally)
+      // before it is written first it also must be
+      // kept valid for the whole loop
+      auto enclosing_loop = lr_scope->get_parent_loop();
+      assert(enclosing_loop);
+      return make_pair(enclosing_loop->begin(), enclosing_loop->end());
+   }
+}
+
+
+void evaluate_remapping(const vector<std::pair<int, int>>& lt, std::vector<rename_reg_pair>& result)
+{
+
+   struct out_access_record {
+      int end;
+      unsigned reg;
+      bool tested;
+   };
+
+   std::multimap<int, out_access_record> m;
+   for (unsigned i = 1; i < lt.size(); ++i) {
+      out_access_record oar = {lt[i].second, i, false};
+      m.insert(make_pair(lt[i].first, oar));
+   }
+
+   while (true) {
+      auto trgt = m.begin();
+
+      while ( trgt != m.end() && trgt->second.tested)
+         ++trgt;
+
+      if (trgt == m.end())
+         break;
+
+      while (trgt != m.end()) {
+         auto src = m.upper_bound(trgt->second.end - 1);
+         if (src ==  m.end()) {
+            trgt->second.tested = true;
+         } else {
+            result[src->second.reg].new_reg = trgt->second.reg;
+            result[src->second.reg].valid = true;
+            trgt->second.end = src->second.end;
+            m.erase(src);
+            break;
+         }
+         ++trgt;
+      }
+   }
+}
diff --git a/src/mesa/state_tracker/st_glsl_to_tgsi_temprename.h b/src/mesa/state_tracker/st_glsl_to_tgsi_temprename.h
new file mode 100644
index 0000000000..e764f0f0c2
--- /dev/null
+++ b/src/mesa/state_tracker/st_glsl_to_tgsi_temprename.h
@@ -0,0 +1,114 @@
+/*
+ * Copyright © 2017 Gert Wollny
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#include "st_glsl_to_tgsi_private.h"
+#include <memory>
+#include <map>
+
+class tgsi_temp_lifetime {
+public:
+
+   enum e_scope_type {
+      sct_outer,
+      sct_loop,
+      sct_if,
+      sct_else,
+      sct_switch,
+      sct_case,
+      sct_unknown
+   };
+
+   enum e_acc_type {
+      acc_read,
+      acc_write
+   };
+
+   class prog_scope {
+
+   public:
+      prog_scope(e_scope_type type, int id, int lvl, int s_begin);
+      prog_scope(std::shared_ptr<prog_scope> p, e_scope_type type, int id, int lvl, int s_begin);
+
+      e_scope_type type() const;
+      std::shared_ptr<prog_scope> parent() const;
+      int level() const;
+      int id() const;
+      int end() const;
+      int begin() const;
+      const prog_scope *in_ifelse() const;
+      const prog_scope *in_switchcase() const;
+      int loop_continue_idx() const;
+      bool in_loop() const;
+      const prog_scope *get_parent_loop() const;
+      bool is_conditional() const;
+      bool contains(const prog_scope& other) const;
+      void set_end(int end);
+      void set_previous(std::shared_ptr<prog_scope> prev);
+      void set_continue(std::weak_ptr<prog_scope> scope, int i);
+   private:
+      e_scope_type scope_type;
+      int scope_id;
+      int nested_level;
+      int scope_begin;
+      int scope_end;
+      int loop_continue;
+      std::weak_ptr<prog_scope> loop_to_continue_scope;
+      std::shared_ptr<prog_scope> previous;
+      std::shared_ptr<prog_scope> parent_scope;
+
+   };
+
+   class temp_access {
+
+   public:
+
+      void append(int index, e_acc_type rw, std::shared_ptr<prog_scope> pstate);
+      std::pair<int, int> get_required_lifetime() const;
+
+   private:
+
+      struct temp_access_record {
+         int index;
+         e_acc_type acc;
+         std::shared_ptr<prog_scope> pstate;
+      };
+
+      std::vector< temp_access_record > timeline;
+
+   };
+
+   tgsi_temp_lifetime(exec_list *instructions, int ntemps);
+
+   const std::vector<std::pair<int, int> >&  get_lifetimes() const;
+
+
+private:
+
+   void evaluate(exec_list *instructions);
+
+   std::vector<std::pair<int, int> > lifetimes;
+};
+
+void evaluate_remapping(const std::vector<std::pair<int, int>>& lt, std::vector<rename_reg_pair>& result);
+
+
diff --git a/src/mesa/state_tracker/tests/Makefile.am b/src/mesa/state_tracker/tests/Makefile.am
new file mode 100644
index 0000000000..a2bcad8dde
--- /dev/null
+++ b/src/mesa/state_tracker/tests/Makefile.am
@@ -0,0 +1,40 @@
+AM_CFLAGS = \
+        $(PTHREAD_CFLAGS)
+
+AM_CXXFLAGS = \
+        $(LLVM_CXXFLAGS)
+
+AM_CPPFLAGS = \
+	-I$(top_srcdir)/src/gtest/include \
+	-I$(top_srcdir)/src \
+	-I$(top_srcdir)/src/mapi \
+	-I$(top_builddir)/src/mesa \
+	-I$(top_srcdir)/src/mesa \
+	-I$(top_srcdir)/include \
+        -I$(top_srcdir)/src/gallium/include \
+        -I$(top_srcdir)/src/gallium/auxiliary \
+	$(DEFINES) $(INCLUDE_DIRS)
+
+TESTS = st-renumerate-test
+check_PROGRAMS = st-renumerate-test
+
+st_renumerate_test_SOURCES =			\
+	test_glsl_to_tgsi_lifetime.cpp
+
+st_renumerate_test_LDFLAGS = \
+        $(LLVM_LDFLAGS)
+
+st_renumerate_test_LDADD = \
+        $(top_builddir)/src/mesa/libmesagallium.la \
+        $(top_builddir)/src/mapi/shared-glapi/libglapi.la \
+        $(top_builddir)/src/gallium/auxiliary/libgallium.la \
+        $(top_builddir)/src/util/libmesautil.la \
+        $(top_builddir)/src/gallium/drivers/trace/libtrace.la \
+        $(top_builddir)/src/gallium/winsys/sw/null/libws_null.la \
+        $(top_builddir)/src/gallium/drivers/softpipe/libsoftpipe.la \
+        $(top_builddir)/src/gtest/libgtest.la \
+        $(GALLIUM_COMMON_LIB_DEPS) \
+        $(LLVM_LIBS) \
+	$(PTHREAD_LIBS) \
+        $(DLOPEN_LIBS)
+
diff --git a/src/mesa/state_tracker/tests/st-renumerate-test b/src/mesa/state_tracker/tests/st-renumerate-test
new file mode 100755
index 0000000000..bc2f325899
--- /dev/null
+++ b/src/mesa/state_tracker/tests/st-renumerate-test
@@ -0,0 +1,210 @@
+#! /bin/sh
+
+# st-renumerate-test - temporary wrapper script for .libs/st-renumerate-test
+# Generated by libtool (GNU libtool) 2.4.6
+#
+# The st-renumerate-test program cannot be directly executed until all the libtool
+# libraries that it depends on are installed.
+#
+# This wrapper script should never be moved out of the build directory.
+# If it is, it will not operate correctly.
+
+# Sed substitution that helps us do robust quoting.  It backslashifies
+# metacharacters that are still active within double-quoted strings.
+sed_quote_subst='s|\([`"$\\]\)|\\\1|g'
+
+# Be Bourne compatible
+if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then
+  emulate sh
+  NULLCMD=:
+  # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which
+  # is contrary to our usage.  Disable this feature.
+  alias -g '${1+"$@"}'='"$@"'
+  setopt NO_GLOB_SUBST
+else
+  case `(set -o) 2>/dev/null` in *posix*) set -o posix;; esac
+fi
+BIN_SH=xpg4; export BIN_SH # for Tru64
+DUALCASE=1; export DUALCASE # for MKS sh
+
+# The HP-UX ksh and POSIX shell print the target directory to stdout
+# if CDPATH is set.
+(unset CDPATH) >/dev/null 2>&1 && unset CDPATH
+
+relink_command=""
+
+# This environment variable determines our operation mode.
+if test "$libtool_install_magic" = "%%%MAGIC variable%%%"; then
+  # install mode needs the following variables:
+  generated_by_libtool_version='2.4.6'
+  notinst_deplibs=' ../../../../src/mapi/shared-glapi/libglapi.la'
+else
+  # When we are sourced in execute mode, $file and $ECHO are already set.
+  if test "$libtool_execute_magic" != "%%%MAGIC variable%%%"; then
+    file="$0"
+
+# A function that is used when there is no print builtin or printf.
+func_fallback_echo ()
+{
+  eval 'cat <<_LTECHO_EOF
+$1
+_LTECHO_EOF'
+}
+    ECHO="printf %s\\n"
+  fi
+
+# Very basic option parsing. These options are (a) specific to
+# the libtool wrapper, (b) are identical between the wrapper
+# /script/ and the wrapper /executable/ that is used only on
+# windows platforms, and (c) all begin with the string --lt-
+# (application programs are unlikely to have options that match
+# this pattern).
+#
+# There are only two supported options: --lt-debug and
+# --lt-dump-script. There is, deliberately, no --lt-help.
+#
+# The first argument to this parsing function should be the
+# script's ../../../../libtool value, followed by no.
+lt_option_debug=
+func_parse_lt_options ()
+{
+  lt_script_arg0=$0
+  shift
+  for lt_opt
+  do
+    case "$lt_opt" in
+    --lt-debug) lt_option_debug=1 ;;
+    --lt-dump-script)
+        lt_dump_D=`$ECHO "X$lt_script_arg0" | /bin/sed -e 's/^X//' -e 's%/[^/]*$%%'`
+        test "X$lt_dump_D" = "X$lt_script_arg0" && lt_dump_D=.
+        lt_dump_F=`$ECHO "X$lt_script_arg0" | /bin/sed -e 's/^X//' -e 's%^.*/%%'`
+        cat "$lt_dump_D/$lt_dump_F"
+        exit 0
+      ;;
+    --lt-*)
+        $ECHO "Unrecognized --lt- option: '$lt_opt'" 1>&2
+        exit 1
+      ;;
+    esac
+  done
+
+  # Print the debug banner immediately:
+  if test -n "$lt_option_debug"; then
+    echo "st-renumerate-test:st-renumerate-test:$LINENO: libtool wrapper (GNU libtool) 2.4.6" 1>&2
+  fi
+}
+
+# Used when --lt-debug. Prints its arguments to stdout
+# (redirection is the responsibility of the caller)
+func_lt_dump_args ()
+{
+  lt_dump_args_N=1;
+  for lt_arg
+  do
+    $ECHO "st-renumerate-test:st-renumerate-test:$LINENO: newargv[$lt_dump_args_N]: $lt_arg"
+    lt_dump_args_N=`expr $lt_dump_args_N + 1`
+  done
+}
+
+# Core function for launching the target application
+func_exec_program_core ()
+{
+
+      if test -n "$lt_option_debug"; then
+        $ECHO "st-renumerate-test:st-renumerate-test:$LINENO: newargv[0]: $progdir/$program" 1>&2
+        func_lt_dump_args ${1+"$@"} 1>&2
+      fi
+      exec "$progdir/$program" ${1+"$@"}
+
+      $ECHO "$0: cannot exec $program $*" 1>&2
+      exit 1
+}
+
+# A function to encapsulate launching the target application
+# Strips options in the --lt-* namespace from $@ and
+# launches target application with the remaining arguments.
+func_exec_program ()
+{
+  case " $* " in
+  *\ --lt-*)
+    for lt_wr_arg
+    do
+      case $lt_wr_arg in
+      --lt-*) ;;
+      *) set x "$@" "$lt_wr_arg"; shift;;
+      esac
+      shift
+    done ;;
+  esac
+  func_exec_program_core ${1+"$@"}
+}
+
+  # Parse options
+  func_parse_lt_options "$0" ${1+"$@"}
+
+  # Find the directory that this script lives in.
+  thisdir=`$ECHO "$file" | /bin/sed 's%/[^/]*$%%'`
+  test "x$thisdir" = "x$file" && thisdir=.
+
+  # Follow symbolic links until we get to the real thisdir.
+  file=`ls -ld "$file" | /bin/sed -n 's/.*-> //p'`
+  while test -n "$file"; do
+    destdir=`$ECHO "$file" | /bin/sed 's%/[^/]*$%%'`
+
+    # If there was a directory component, then change thisdir.
+    if test "x$destdir" != "x$file"; then
+      case "$destdir" in
+      [\\/]* | [A-Za-z]:[\\/]*) thisdir="$destdir" ;;
+      *) thisdir="$thisdir/$destdir" ;;
+      esac
+    fi
+
+    file=`$ECHO "$file" | /bin/sed 's%^.*/%%'`
+    file=`ls -ld "$thisdir/$file" | /bin/sed -n 's/.*-> //p'`
+  done
+
+  # Usually 'no', except on cygwin/mingw when embedded into
+  # the cwrapper.
+  WRAPPER_SCRIPT_BELONGS_IN_OBJDIR=no
+  if test "$WRAPPER_SCRIPT_BELONGS_IN_OBJDIR" = "yes"; then
+    # special case for '.'
+    if test "$thisdir" = "."; then
+      thisdir=`pwd`
+    fi
+    # remove .libs from thisdir
+    case "$thisdir" in
+    *[\\/].libs ) thisdir=`$ECHO "$thisdir" | /bin/sed 's%[\\/][^\\/]*$%%'` ;;
+    .libs )   thisdir=. ;;
+    esac
+  fi
+
+  # Try to get the absolute directory name.
+  absdir=`cd "$thisdir" && pwd`
+  test -n "$absdir" && thisdir="$absdir"
+
+  program='st-renumerate-test'
+  progdir="$thisdir/.libs"
+
+
+  if test -f "$progdir/$program"; then
+    # Add our own library path to LD_LIBRARY_PATH
+    LD_LIBRARY_PATH="/home/gerddie/src/Freedesktop/mesa-orig/src/mapi/shared-glapi/.libs:$LD_LIBRARY_PATH"
+
+    # Some systems cannot cope with colon-terminated LD_LIBRARY_PATH
+    # The second colon is a workaround for a bug in BeOS R4 sed
+    LD_LIBRARY_PATH=`$ECHO "$LD_LIBRARY_PATH" | /bin/sed 's/::*$//'`
+
+    export LD_LIBRARY_PATH
+
+    if test "$libtool_execute_magic" != "%%%MAGIC variable%%%"; then
+      # Run the actual program with our arguments.
+      func_exec_program ${1+"$@"}
+    fi
+  else
+    # The program doesn't exist.
+    $ECHO "$0: error: '$progdir/$program' does not exist" 1>&2
+    $ECHO "This script is just a wrapper for $program." 1>&2
+    $ECHO "See the libtool documentation for more information." 1>&2
+    exit 1
+  fi
+fi
diff --git a/src/mesa/state_tracker/tests/test_glsl_to_tgsi_lifetime.cpp b/src/mesa/state_tracker/tests/test_glsl_to_tgsi_lifetime.cpp
new file mode 100644
index 0000000000..3e094f0dda
--- /dev/null
+++ b/src/mesa/state_tracker/tests/test_glsl_to_tgsi_lifetime.cpp
@@ -0,0 +1,789 @@
+/*
+ * Copyright © 2017 Gert Wollny
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#include <state_tracker/st_glsl_to_tgsi_temprename.h>
+#include <tgsi/tgsi_ureg.h>
+#include <tgsi/tgsi_info.h>
+#include <compiler/glsl/list.h>
+#include <gtest/gtest.h>
+
+using std::vector;
+using std::pair;
+
+struct MockCodeline {
+   MockCodeline(unsigned  _op): op(_op) {}
+   MockCodeline(unsigned _op, const vector<int>& _dst, const vector<int>& _src, const vector<int>&_to):
+      op(_op), dst(_dst), src(_src), tex_offsets(_to){}
+   unsigned op;
+   vector<int> dst;
+   vector<int> src;
+   vector<int> tex_offsets;
+};
+
+const int in0 = 0;
+const int in1 = -1;
+const int in2 = -2;
+
+const int out0 = 0;
+const int out1 = -1;
+
+
+
+/*
+ * supported opcodes for mock-shaders:
+ *
+ * Arithmentic
+ *
+ * TGSI_OPCODE_MOV, TGSI_OPCODE_UADD, TGSI_OPCODE_UMAD
+ *
+ * Flow control
+ * loops:
+ * TGSI_OPCODE_BGNLOOP, TGSI_OPCODE_ENDLOOP,
+ * TGSI_OPCODE_BRK, TGSI_OPCODE_CONT
+ *
+ * if/switch:
+ * TGSI_OPCODE_IF, TGSI_OPCODE_UIF, TGSI_OPCODE_ELSE, TGSI_OPCODE_ENDIF
+ * TGSI_OPCODE_SWITCH, TGSI_OPCODE_CASE, TGSI_OPCODE_DEFAULT
+ *
+ * TGSI_OPCODE_END
+ */
+
+
+class MockShader {
+public:
+   MockShader(const vector<MockCodeline>& source);
+   ~MockShader();
+
+   void free();
+
+   exec_list* get_program();
+   int get_num_temps();
+private:
+   st_src_reg create_src_register(int src_idx);
+   st_dst_reg create_dst_register(int dst_idx);
+   exec_list* program;
+   int num_temps;
+   void *mem_ctx;
+};
+
+using expectation = vector<vector<int>>;
+
+MockShader::~MockShader()
+{
+   free();
+   ralloc_free(mem_ctx);
+}
+
+int MockShader::get_num_temps()
+{
+   return num_temps;
+}
+
+
+exec_list* MockShader::get_program()
+{
+   return program;
+}
+
+MockShader::MockShader(const vector<MockCodeline>& source):
+   num_temps(0)
+{
+   mem_ctx = ralloc_context(NULL);
+
+   program = new(mem_ctx) exec_list();
+
+   for (MockCodeline i: source) {
+      glsl_to_tgsi_instruction *next_instr = new(mem_ctx) glsl_to_tgsi_instruction();
+      next_instr->op = i.op;
+      next_instr->info = tgsi_get_opcode_info(i.op);
+
+      assert(i.src.size() < 4);
+      assert(i.dst.size() < 3);
+      assert(i.tex_offsets.size() < 3);
+
+      for (unsigned  k = 0; k < i.src.size(); ++k) {
+         next_instr->src[k] = create_src_register(i.src[k]);
+      }
+      for (unsigned k = 0; k < i.dst.size(); ++k) {
+         next_instr->dst[k] = create_dst_register(i.dst[k]);
+      }
+
+      // set texture registers
+      next_instr->tex_offset_num_offset = i.tex_offsets.size();
+      next_instr->tex_offsets = new st_src_reg[i.tex_offsets.size()];
+      for (unsigned k = 0; k < i.tex_offsets.size(); ++k) {
+         next_instr->tex_offsets[k] = create_src_register(i.tex_offsets[k]);
+      }
+
+      program->push_tail(next_instr);
+   }
+   ++num_temps;
+}
+
+void MockShader::free()
+{
+   // the list is not fully initialized, so
+   // tearing it down also must be done manually.
+   exec_node *p;
+   while ((p = program->pop_head())) {
+      glsl_to_tgsi_instruction * instr = static_cast<glsl_to_tgsi_instruction *>(p);
+      if (instr->tex_offset_num_offset > 0)
+         delete[] instr->tex_offsets;
+      delete p;
+   }
+   program = 0;
+   num_temps = 0;
+}
+
+st_src_reg MockShader::create_src_register(int src_idx)
+{
+   gl_register_file file;
+   int idx = 0;
+   if (src_idx > 0) {
+      file = PROGRAM_TEMPORARY;
+      idx = src_idx;
+      if (num_temps < idx)
+         num_temps = idx;
+   } else {
+      file = PROGRAM_INPUT;
+      idx = -src_idx;
+   }
+   return st_src_reg(file, idx, GLSL_TYPE_INT);
+
+}
+
+st_dst_reg MockShader::create_dst_register(int dst_idx)
+{
+   gl_register_file file;
+   int idx = 0;
+   if (dst_idx > 0) {
+      file = PROGRAM_TEMPORARY;
+      idx = dst_idx;
+      if (num_temps < idx)
+         num_temps = idx;
+   } else {
+      file = PROGRAM_OUTPUT;
+      idx = - dst_idx;
+   }
+   return st_dst_reg(file, 0xF, GLSL_TYPE_INT, idx);
+}
+
+/**
+  This is a text class to check the exact life times
+*/
+class LifetimeEvaluatorExactTest : public testing::Test {
+protected:
+   void run(const vector<MockCodeline>& code, const expectation& e);
+};
+
+/**
+  This is a text class to check that the life time is at least
+  in the expected range
+*/
+class LifetimeEvaluatorAtLeastTest : public testing::Test {
+protected:
+   void run(const vector<MockCodeline>& code, const expectation& e);
+};
+
+
+void LifetimeEvaluatorExactTest::run(const vector<MockCodeline>& code, const expectation& e)
+{
+   MockShader shader(code);
+
+   tgsi_temp_lifetime ana(shader.get_program(), shader.get_num_temps());
+   auto lifetimes = ana.get_lifetimes();
+
+   // lifetimes[0] not used, but created for simpler processing
+   ASSERT_EQ(lifetimes.size(), e.size());
+
+   for (unsigned i = 1; i < lifetimes.size(); ++i) {
+      EXPECT_EQ(lifetimes[i].first, e[i][0]);
+      EXPECT_EQ(lifetimes[i].second, e[i][1]);
+   }
+}
+
+void LifetimeEvaluatorAtLeastTest::run(const vector<MockCodeline>& code, const expectation& e)
+{
+   MockShader shader(code);
+
+   tgsi_temp_lifetime ana(shader.get_program(), shader.get_num_temps());
+   auto lifetimes = ana.get_lifetimes();
+
+   // lifetimes[0] not used, but created for simpler processing
+   ASSERT_EQ(lifetimes.size(), e.size());
+
+   for (unsigned i = 1; i < lifetimes.size(); ++i) {
+      EXPECT_LE(lifetimes[i].first, e[i][0]);
+      EXPECT_GE(lifetimes[i].second, e[i][1]);
+   }
+}
+
+TEST_F(LifetimeEvaluatorExactTest, SimpleMoveAdd)
+{
+   const vector<MockCodeline> code = {
+      { TGSI_OPCODE_MOV, {1}, {in0}, {}},
+      { TGSI_OPCODE_UADD, {out0}, {1, in0},  {}},
+      { TGSI_OPCODE_END}
+   };
+   run(code, expectation({{-1, -1}, {0,1}}));
+}
+
+
+TEST_F(LifetimeEvaluatorExactTest, SimpleMoveAddMove)
+{
+   const vector<MockCodeline> code = {
+      { TGSI_OPCODE_MOV, {1}, {in0}, {}},
+      { TGSI_OPCODE_UADD, {2}, {1, in0},  {}},
+      { TGSI_OPCODE_MOV, {out0}, {2},  {}},
+      { TGSI_OPCODE_END}
+   };
+   run(code, expectation({{-1, -1}, {0,1}, {1,2}}));
+}
+
+TEST_F(LifetimeEvaluatorExactTest, SimpleMoveAddMoveTexoffset)
+{
+   const vector<MockCodeline> code = {
+      { TGSI_OPCODE_MOV, {1}, {in0}, {}},
+      { TGSI_OPCODE_MOV, {2}, {in1}, {}},
+      { TGSI_OPCODE_UADD, {out0}, {},  {1,2}},
+      { TGSI_OPCODE_END}
+   };
+   run(code, expectation({{-1, -1}, {0,2}, {1,2}}));
+}
+
+
+TEST_F(LifetimeEvaluatorExactTest, SimpleMoveInLoop)
+{
+   const vector<MockCodeline> code = {
+      { TGSI_OPCODE_MOV, {1}, {in0}, {}},      // 0
+      { TGSI_OPCODE_BGNLOOP },                 // 1
+      { TGSI_OPCODE_UADD, {2}, {1, in0},  {}}, // 2
+      { TGSI_OPCODE_UADD, {3}, {1, 2},  {}},   // 3
+      { TGSI_OPCODE_UADD, {3}, {3, in1},  {}}, // 4
+      { TGSI_OPCODE_ENDLOOP },                 // 5
+      { TGSI_OPCODE_MOV, {out0}, {3},  {}},    // 6
+      { TGSI_OPCODE_END}
+   };
+   run (code, expectation({{-1,-1},{0, 5}, {2,3}, {3, 6}}));
+}
+
+
+// in loop if/else value written only in one path, and read later
+//   - value must survive the whole loop
+TEST_F(LifetimeEvaluatorExactTest, MoveInIfInLoop)
+{
+   const vector<MockCodeline> code = {
+      { TGSI_OPCODE_MOV, {1}, {in0}, {}},      // 0
+      { TGSI_OPCODE_BGNLOOP },                 // 1
+      { TGSI_OPCODE_IF},                       // 2
+      { TGSI_OPCODE_UADD, {2}, {1, in0},  {}}, // 3
+      { TGSI_OPCODE_ENDIF},                    // 4
+      { TGSI_OPCODE_UADD, {3}, {1, 2},  {}},   // 5
+      { TGSI_OPCODE_UADD, {3}, {3, in1},  {}}, // 6
+      { TGSI_OPCODE_ENDLOOP },                 // 7
+      { TGSI_OPCODE_MOV, {out0}, {3},  {}},    // 8
+      { TGSI_OPCODE_END}
+   };
+   run (code, expectation({{-1,-1},{0, 7}, {1,7}, {5, 8}}));
+}
+
+
+// in loop if/else value written in both path, and read later
+//   - value must survive from first write to last read in loop
+// for now we only check that the minimum life time is correct
+TEST_F(LifetimeEvaluatorAtLeastTest, WriteInIfAndElseInLoop)
+{
+   const vector<MockCodeline> code = {
+      { TGSI_OPCODE_MOV, {1}, {in0}, {}},      // 0
+      { TGSI_OPCODE_BGNLOOP },                 // 1
+      { TGSI_OPCODE_IF},                       // 2
+      { TGSI_OPCODE_UADD, {2}, {1, in0},  {}}, // 3
+      { TGSI_OPCODE_ELSE },                    // 4
+      { TGSI_OPCODE_MOV, {2}, {1},  {}},       // 5
+      { TGSI_OPCODE_ENDIF},                    // 6
+      { TGSI_OPCODE_UADD, {3}, {1, 2},  {}},   // 7
+      { TGSI_OPCODE_UADD, {3}, {3, in1},  {}}, // 8
+      { TGSI_OPCODE_ENDLOOP },                 // 9
+      { TGSI_OPCODE_MOV, {out0}, {3},  {}},    // 10
+      { TGSI_OPCODE_END}
+   };
+   run (code, expectation({{-1,-1},{0, 9}, {3,7}, {7, 10}}));
+}
+
+// in loop if/else value written in both path, red in else path
+// before read and also read later
+//   - value must survive from first write to last read in loop
+TEST_F(LifetimeEvaluatorExactTest, WriteInIfAndElseReadInElseInLoop)
+{
+   const vector<MockCodeline> code = {
+      { TGSI_OPCODE_MOV, {1}, {in0}, {}},      // 0
+      { TGSI_OPCODE_BGNLOOP },                 // 1
+      { TGSI_OPCODE_IF},                       // 2
+      { TGSI_OPCODE_UADD, {2}, {1, in0},  {}}, // 3
+      { TGSI_OPCODE_ELSE },                    // 4
+      { TGSI_OPCODE_ADD, {2}, {1, 2},  {}},    // 5
+      { TGSI_OPCODE_ENDIF},                    // 6
+      { TGSI_OPCODE_UADD, {3}, {1, 2},  {}},   // 7
+      { TGSI_OPCODE_UADD, {3}, {3, in1},  {}}, // 8
+      { TGSI_OPCODE_ENDLOOP },                 // 9
+      { TGSI_OPCODE_MOV, {out0}, {3},  {}},    // 10
+      { TGSI_OPCODE_END}
+   };
+   run (code, expectation({{-1,-1},{0, 9}, {1,9}, {7, 10}}));
+}
+
+// in loop if/else read in one path before written in the same loop
+//   - value must survive the whole loop
+TEST_F(LifetimeEvaluatorExactTest, ReadInIfInLoopBeforeWrite)
+{
+   const vector<MockCodeline> code = {
+      { TGSI_OPCODE_MOV, {1}, {in0}, {}},      // 0
+      { TGSI_OPCODE_BGNLOOP },                 // 1
+      { TGSI_OPCODE_IF, {}, {in0},  {}},                       // 2
+      { TGSI_OPCODE_UADD, {2}, {1, 3},  {}},   // 3
+      { TGSI_OPCODE_ENDIF},                    // 4
+      { TGSI_OPCODE_UADD, {3}, {1, 2},  {}},   // 5
+      { TGSI_OPCODE_UADD, {3}, {3, in1},  {}}, // 6
+      { TGSI_OPCODE_ENDLOOP },                 // 7
+      { TGSI_OPCODE_MOV, {out0}, {3},  {}},    // 8
+      { TGSI_OPCODE_END}
+   };
+   run (code, expectation({{-1,-1},{0, 7}, {1,7}, {1, 8}}));
+}
+
+/* Write in nested ifs in loop, for now we do test whether the
+ * life time is atleast what is required, but we know that the
+ * implementation doesn't do a full check and sets larger boundaries
+ */
+TEST_F(LifetimeEvaluatorAtLeastTest, NestedIfInLoopAlwaysWriteButNotPropagated)
+{
+   const vector<MockCodeline> code = {
+      { TGSI_OPCODE_BGNLOOP },                     // 1
+      {   TGSI_OPCODE_IF, {}, {in0},  {}},
+      {     TGSI_OPCODE_IF, {}, {in0},  {}},
+      {       TGSI_OPCODE_MOV, {1}, {in0},  {}},
+      {     TGSI_OPCODE_ELSE},
+      {       TGSI_OPCODE_MOV, {1}, {in0},  {}},   // 5
+      {     TGSI_OPCODE_ENDIF},
+      {   TGSI_OPCODE_ELSE},
+      {     TGSI_OPCODE_IF, {}, {in0},  {}},
+      {       TGSI_OPCODE_MOV, {1}, {in0},  {}},
+      {     TGSI_OPCODE_ELSE},                     // 10
+      {       TGSI_OPCODE_MOV, {1}, {in0},  {}},
+      {     TGSI_OPCODE_ENDIF},
+      {   TGSI_OPCODE_ENDIF},
+      {   TGSI_OPCODE_MOV, {out0}, {1},  {}},
+      { TGSI_OPCODE_ENDLOOP },                    // 15
+      { TGSI_OPCODE_END}
+   };
+   run (code, expectation({{-1,-1},{3, 14}}));
+}
+
+
+
+TEST_F(LifetimeEvaluatorExactTest, NestedIfInLoopWriteNotAlways)
+{
+   const vector<MockCodeline> code = {
+      { TGSI_OPCODE_BGNLOOP },                     // 0
+      {   TGSI_OPCODE_IF, {}, {in0},  {}},
+      {     TGSI_OPCODE_IF, {}, {in0},  {}},
+      {       TGSI_OPCODE_MOV, {1}, {in0},  {}},
+      {     TGSI_OPCODE_ELSE},
+      {       TGSI_OPCODE_MOV, {1}, {in0},  {}},   // 5
+      {     TGSI_OPCODE_ENDIF},
+      {   TGSI_OPCODE_ELSE},
+      {     TGSI_OPCODE_IF, {}, {in0},  {}},
+      {       TGSI_OPCODE_MOV, {1}, {in0},  {}},
+      {     TGSI_OPCODE_ENDIF},                   // 10
+      {   TGSI_OPCODE_ENDIF},
+      {   TGSI_OPCODE_MOV, {out0}, {1},  {}},
+      { TGSI_OPCODE_ENDLOOP },                     // 13
+      { TGSI_OPCODE_END}
+   };
+   run (code, expectation({{-1,-1},{0, 13}}));
+}
+
+
+// if a continue is in the loop, all variables written after the
+// continue and used outside the loop must be maintained for the
+// whole loop
+TEST_F(LifetimeEvaluatorExactTest, LoopWithWriteAfterContinue)
+{
+   const vector<MockCodeline> code = {
+      { TGSI_OPCODE_BGNLOOP },                     // 0
+      {   TGSI_OPCODE_IF, {}, {in0},  {}},
+      {     TGSI_OPCODE_CONT},
+      {   TGSI_OPCODE_ENDIF},
+      {   TGSI_OPCODE_MOV, {1}, {in0},  {}},
+      { TGSI_OPCODE_ENDLOOP },                     // 5
+      { TGSI_OPCODE_MOV, {out0}, {1},  {}},        // 6
+      { TGSI_OPCODE_END}
+   };
+   run (code, expectation({{-1,-1},{0, 6}}));
+}
+
+// if a continue is in the loop, all variables written after the
+// continue and used outside the loop must be maintained for the
+// whole loop, but not further
+TEST_F(LifetimeEvaluatorExactTest, NestedLoopWithWriteAfterContinue)
+{
+   const vector<MockCodeline> code = {
+      { TGSI_OPCODE_BGNLOOP },                     // 0
+      {   TGSI_OPCODE_BGNLOOP },                     // 1
+      {     TGSI_OPCODE_IF, {}, {in0},  {}},
+      {       TGSI_OPCODE_CONT},
+      {     TGSI_OPCODE_ENDIF},
+      {     TGSI_OPCODE_MOV, {1}, {in0},  {}},
+      {   TGSI_OPCODE_ENDLOOP },                     // 6
+      {   TGSI_OPCODE_MOV, {out0}, {1},  {}},        // 7
+      { TGSI_OPCODE_ENDLOOP },                     // 6
+      { TGSI_OPCODE_END}
+   };
+   run (code, expectation({{-1,-1},{1, 7}}));
+}
+
+// if a continue is in the loop, all variables written after the
+// continue and used outside the loop must be maintained for all
+// loops up untto the read scope, but not further
+TEST_F(LifetimeEvaluatorExactTest, Nested2LoopWithWriteAfterContinue)
+{
+   const vector<MockCodeline> code = {
+      { TGSI_OPCODE_BGNLOOP },                     // 0
+      {   TGSI_OPCODE_BGNLOOP },                     // 1
+      {     TGSI_OPCODE_BGNLOOP },                   // 2
+      {       TGSI_OPCODE_IF, {}, {in0},  {}},
+      {         TGSI_OPCODE_CONT},
+      {       TGSI_OPCODE_ENDIF},
+      {       TGSI_OPCODE_MOV, {1}, {in0},  {}},
+      {     TGSI_OPCODE_ENDLOOP },
+      {   TGSI_OPCODE_ENDLOOP },
+      {   TGSI_OPCODE_MOV, {out0}, {1},  {}},        // 9
+      { TGSI_OPCODE_ENDLOOP },                     // 6
+      { TGSI_OPCODE_END}
+   };
+   run (code, expectation({{-1,-1},{1, 9}}));
+}
+
+TEST_F(LifetimeEvaluatorExactTest, LoopWithWriteInSwitch)
+{
+   const vector<MockCodeline> code = {
+      { TGSI_OPCODE_BGNLOOP },
+      {   TGSI_OPCODE_SWITCH, {}, {in0},  {} },
+      {    TGSI_OPCODE_CASE, {}, {in0},  {} },
+      {     TGSI_OPCODE_MOV, {1}, {in0},  {}},
+      {    TGSI_OPCODE_BRK },
+      {   TGSI_OPCODE_DEFAULT },
+      {   TGSI_OPCODE_BRK },
+      {   TGSI_OPCODE_ENDSWITCH },
+      {   TGSI_OPCODE_MOV, {out0}, {1},  {}},
+      { TGSI_OPCODE_ENDLOOP },
+      { TGSI_OPCODE_END}
+   };
+   run (code, expectation({{-1,-1},{0, 9}}));
+}
+
+// value written in one case, and read in other, in loop
+//  - must survive the loop
+TEST_F(LifetimeEvaluatorExactTest, LoopWithReadWriteInSwitchDifferentCase)
+{
+   const vector<MockCodeline> code = {
+      { TGSI_OPCODE_BGNLOOP },                     // 0
+      {   TGSI_OPCODE_SWITCH, {}, {in0},  {} },                     // 0
+      {    TGSI_OPCODE_CASE, {}, {in0},  {} },                     // 0
+      {     TGSI_OPCODE_MOV, {1}, {in0},  {}},
+      {    TGSI_OPCODE_BRK },                     // 0
+      {   TGSI_OPCODE_DEFAULT },                     // 0
+      {     TGSI_OPCODE_MOV, {out0}, {1},  {}},
+      {   TGSI_OPCODE_BRK },                     // 0
+      {   TGSI_OPCODE_ENDSWITCH },                     // 0
+      { TGSI_OPCODE_ENDLOOP },                     // 6
+      { TGSI_OPCODE_END}
+   };
+   run (code, expectation({{-1,-1},{0, 9}}));
+}
+
+
+TEST_F(LifetimeEvaluatorExactTest, LoopRWInSwitchCaseLastCaseWithoutBreak)
+{
+   const vector<MockCodeline> code = {
+      { TGSI_OPCODE_BGNLOOP },                     // 0
+      {   TGSI_OPCODE_SWITCH, {}, {in0},  {} },                     // 0
+      {    TGSI_OPCODE_CASE, {}, {in0},  {} },                     // 0
+      {     TGSI_OPCODE_MOV, {1}, {in0},  {}},
+      {    TGSI_OPCODE_BRK },                     // 0
+      {   TGSI_OPCODE_DEFAULT },                     // 0
+      {     TGSI_OPCODE_MOV, {out0}, {1},  {}},
+      {   TGSI_OPCODE_ENDSWITCH },                     // 0
+      { TGSI_OPCODE_ENDLOOP },                     // 6
+      { TGSI_OPCODE_END}
+   };
+   run (code, expectation({{-1,-1},{0, 8}}));
+}
+
+
+// value read/write in same case, stays there
+
+
+TEST_F(LifetimeEvaluatorExactTest, LoopWithReadWriteInSwitchSameCase)
+{
+   const vector<MockCodeline> code = {
+      { TGSI_OPCODE_BGNLOOP },                     // 0
+      {   TGSI_OPCODE_SWITCH, {}, {in0},  {} },                     // 0
+      {    TGSI_OPCODE_CASE, {}, {in0},  {} },                     // 0
+      {     TGSI_OPCODE_MOV, {1}, {in0},  {}},
+      {     TGSI_OPCODE_MOV, {out0}, {1},  {}},
+      {    TGSI_OPCODE_BRK },                     // 0
+      {   TGSI_OPCODE_DEFAULT },                     // 0
+      {   TGSI_OPCODE_BRK },                     // 0
+      {   TGSI_OPCODE_ENDSWITCH },                     // 0
+      { TGSI_OPCODE_ENDLOOP },                     // 6
+      { TGSI_OPCODE_END}
+   };
+   run (code, expectation({{-1,-1},{3,4}}));
+}
+
+// value read/write in all cases, should only live from first
+// write to last read, but currently the whole loop is used.
+TEST_F(LifetimeEvaluatorAtLeastTest, LoopWithReadWriteInSwitchSameCase)
+{
+   const vector<MockCodeline> code = {
+      { TGSI_OPCODE_BGNLOOP },                     // 0
+      {   TGSI_OPCODE_SWITCH, {}, {in0},  {}},                     // 0
+      {    TGSI_OPCODE_CASE, {}, {in0},  {} },                     // 0
+      {     TGSI_OPCODE_MOV, {}, {in0},  {}},
+      {    TGSI_OPCODE_BRK },                     // 0
+      {   TGSI_OPCODE_DEFAULT },                     // 0
+      {     TGSI_OPCODE_MOV, {1}, {in0},  {}},
+      {   TGSI_OPCODE_BRK },                     // 0
+      {   TGSI_OPCODE_ENDSWITCH },                     // 0
+      {   TGSI_OPCODE_MOV, {out0}, {1},  {}},
+      { TGSI_OPCODE_ENDLOOP },                     // 6
+      { TGSI_OPCODE_END}
+   };
+   run (code, expectation({{-1,-1},{3,9}}));
+}
+
+// value written in one case, and read in other, in loop, may fall through
+//  - must survive the loop
+
+// value read/write in differnt loops
+TEST_F(LifetimeEvaluatorExactTest, LoopsWithDifferntScopes)
+{
+   const vector<MockCodeline> code = {
+      { TGSI_OPCODE_BGNLOOP },                     // 0
+      {     TGSI_OPCODE_MOV, {1}, {in0},  {}},     // 1
+      { TGSI_OPCODE_ENDLOOP },                     // 2
+      { TGSI_OPCODE_BGNLOOP },                     // 3
+      {     TGSI_OPCODE_MOV, {out0}, {1},  {}},    // 4
+      { TGSI_OPCODE_ENDLOOP },                     // 5
+      { TGSI_OPCODE_END}
+   };
+   run (code, expectation({{-1,-1},{1,5}}));
+}
+
+// value read/write in differnt loops
+TEST_F(LifetimeEvaluatorExactTest, LoopsWithDifferntScopesConditionalWrite)
+{
+   const vector<MockCodeline> code = {
+      { TGSI_OPCODE_BGNLOOP },                     // 0
+      {  TGSI_OPCODE_IF, {}, {in0},  {}},
+      {     TGSI_OPCODE_MOV, {1}, {in0},  {}},     // 3
+      {   TGSI_OPCODE_ENDIF},     // 1
+      { TGSI_OPCODE_ENDLOOP },                     // 5
+      { TGSI_OPCODE_BGNLOOP },                     // 6
+      {     TGSI_OPCODE_MOV, {out0}, {1},  {}},    // 7
+      { TGSI_OPCODE_ENDLOOP },                     // 5
+      { TGSI_OPCODE_END}
+   };
+   run (code, expectation({{-1,-1},{0,7}}));
+}
+
+// first read before first write wiredness with nested loops
+TEST_F(LifetimeEvaluatorExactTest, LoopsWithDifferntScopesConditionalReadBeforeWrite)
+{
+   const vector<MockCodeline> code = {
+      { TGSI_OPCODE_BGNLOOP },                     // 0
+      {   TGSI_OPCODE_BGNLOOP },                   // 1
+      {    TGSI_OPCODE_IF, {}, {in0},  {}},                        // 2
+      {     TGSI_OPCODE_MOV, {out0}, {1},  {}},    // 3
+      {    TGSI_OPCODE_ENDIF},                     // 4
+      {   TGSI_OPCODE_ENDLOOP },                   // 5
+      {   TGSI_OPCODE_BGNLOOP },                   // 6
+      {     TGSI_OPCODE_MOV, {1}, {in0},  {}},     // 7
+      {   TGSI_OPCODE_ENDLOOP },                   // 8
+      { TGSI_OPCODE_ENDLOOP },                     // 9
+      { TGSI_OPCODE_END}
+   };
+   run (code, expectation({{-1,-1},{0,9}}));
+}
+
+// register is only written. This should not happen,
+// but to handle the case we want the register to life
+// at least one instruction
+TEST_F(LifetimeEvaluatorExactTest, WriteOnly)
+{
+   const vector<MockCodeline> code = {
+      { TGSI_OPCODE_MOV, {1}, {in0},  {}},    // 3
+      { TGSI_OPCODE_END}
+   };
+   run (code, expectation({{-1,-1},{0,0}}));
+}
+
+// register read in if
+TEST_F(LifetimeEvaluatorExactTest, SimpleReadForIf)
+{
+   const vector<MockCodeline> code = {
+      { TGSI_OPCODE_MOV, {1}, {in0},  {}},    // 3
+      { TGSI_OPCODE_ADD, {out0}, {in0, in1},  {}},    // 3
+      { TGSI_OPCODE_IF, {}, {1}, {}},
+      { TGSI_OPCODE_ENDIF}
+   };
+   run (code, expectation({{-1,-1},{0,2}}));
+}
+
+// register read in switch
+TEST_F(LifetimeEvaluatorExactTest, SimpleReadForSwitchAndCase)
+{
+   const vector<MockCodeline> code = {
+      { TGSI_OPCODE_MOV, {1}, {in0},  {}},    // 3
+      { TGSI_OPCODE_SWITCH, {}, {1}, {}},
+      { TGSI_OPCODE_CASE, {}, {1}, {}},
+      { TGSI_OPCODE_CASE, {}, {1}, {}},
+      { TGSI_OPCODE_END, {}, {1}, {}},
+   };
+   run (code, expectation({{-1,-1},{0,3}}));
+}
+
+TEST_F(LifetimeEvaluatorExactTest, DistinceScopesAndNoEndProgramId)
+{
+   const vector<MockCodeline> code = {
+      { TGSI_OPCODE_MOV, {1}, {in0},  {}},    // 3
+      { TGSI_OPCODE_IF, {}, {1}, {}},
+      { TGSI_OPCODE_MOV, {2}, {1}, {}},
+      { TGSI_OPCODE_ENDIF},
+      { TGSI_OPCODE_IF, {}, {1}, {}},
+      { TGSI_OPCODE_MOV, {out0}, {2}, {}},
+      { TGSI_OPCODE_ENDIF},
+
+   };
+   run (code, expectation({{-1,-1},{0,4}, {2,5}}));
+}
+
+/* Check that two destination registers are used
+*/
+TEST_F(LifetimeEvaluatorExactTest, TwoDestRegisters)
+{
+   const vector<MockCodeline> code = {
+      { TGSI_OPCODE_DFRACEXP , {1,2}, {in0},  {}},    // 3
+      { TGSI_OPCODE_ADD, {out0}, {1,2}, {}},
+      { TGSI_OPCODE_END}
+   };
+   run (code, expectation({{-1,-1},{0,1}, {0,1}}));
+}
+
+/* Check that two destination registers are used
+*/
+TEST_F(LifetimeEvaluatorExactTest, ThreeSourceRegisters)
+{
+   const vector<MockCodeline> code = {
+      { TGSI_OPCODE_DFRACEXP , {1,2}, {in0},  {}},    // 3
+      { TGSI_OPCODE_ADD , {3}, {in0, in1},  {}},    // 3
+      { TGSI_OPCODE_MAD, {out0}, {1,2, 3}, {}},
+      { TGSI_OPCODE_END}
+   };
+   run (code, expectation({{-1,-1},{0,2}, {0,2}, {1,2}}));
+}
+
+/* Check that two destination registers are used
+*/
+TEST_F(LifetimeEvaluatorExactTest, OverwriteWrittenOnlyTemps)
+{
+   const vector<MockCodeline> code = {
+      { TGSI_OPCODE_MOV , {1}, {in0},  {}},    // 3
+      { TGSI_OPCODE_MOV , {2}, {in1},  {}},    // 3
+      { TGSI_OPCODE_END}
+   };
+   run (code, expectation({{-1,-1},{0,0}, {1,1}}));
+}
+
+
+TEST(RegisterRemapping, RegisterRemapping)
+{
+   rename_reg_pair proto{false, 0};
+   vector<rename_reg_pair> result(7, proto);
+
+   vector<pair<int, int>> lt({{-1,-1},
+                             {0, 1}, // 1
+                             {0, 2}, // 2
+                             {1, 2}, // 3
+                             {2, 10}, // 4
+                             {3, 5},  // 5
+                             {5, 10}  // 6
+                            });
+
+
+
+   evaluate_remapping(lt, result);
+
+   vector<int> remap({0,1, 2, 3, 4, 5, 6});
+
+   std::transform(remap.begin(), remap.end(), result.begin(), remap.begin(),
+                  [](int x, const rename_reg_pair& rn) {return rn.valid ? rn.new_reg : x;});
+
+   vector<int> expect({0, 1, 2, 1, 1, 2, 2});
+
+   for(unsigned  i = 1; i < remap.size(); ++i) {
+      EXPECT_EQ(remap[i], expect[i]);
+   }
+
+}
+
+
+TEST(RegisterRemapping, RegisterRemapping2)
+{
+   rename_reg_pair proto{false, 0};
+   vector<rename_reg_pair> result(7, proto);
+
+   vector<pair<int, int>> lt({{-1,-1},
+                             {0, 1}, // 1
+                             {0, 2}, // 2
+                             {3, 3}, // 3
+                             {4, 4}, // 4
+                            });
+
+
+
+   evaluate_remapping(lt, result);
+
+   vector<int> remap({0, 1, 2, 3, 4});
+
+   std::transform(remap.begin(), remap.end(), result.begin(), remap.begin(),
+                  [](int x, const rename_reg_pair& rn) {return rn.valid ? rn.new_reg : x;});
+
+   vector<int> expect({0, 1, 2, 1, 1});
+
+   for(unsigned  i = 1; i < remap.size(); ++i) {
+      EXPECT_EQ(remap[i], expect[i]);
+   }
+
+}
-- 
2.13.0



More information about the mesa-dev mailing list