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 _______________________________________________ mesa-dev mailing list mesa-dev@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/mesa-dev