This patch adds a set of unit tests for the new lifetime tracker. --- configure.ac | 1 + src/mesa/Makefile.am | 2 +- src/mesa/state_tracker/tests/Makefile.am | 36 + .../tests/test_glsl_to_tgsi_lifetime.cpp | 976 +++++++++++++++++++++ 4 files changed, 1014 insertions(+), 1 deletion(-) create mode 100644 src/mesa/state_tracker/tests/Makefile.am create mode 100644 src/mesa/state_tracker/tests/test_glsl_to_tgsi_lifetime.cpp
diff --git a/configure.ac b/configure.ac index da7b2f8f81..5279b231ed 100644 --- a/configure.ac +++ b/configure.ac @@ -2839,6 +2839,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..a88a94165d 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 diff --git a/src/mesa/state_tracker/tests/Makefile.am b/src/mesa/state_tracker/tests/Makefile.am new file mode 100644 index 0000000000..fb64cf9dc2 --- /dev/null +++ b/src/mesa/state_tracker/tests/Makefile.am @@ -0,0 +1,36 @@ +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) + +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/gtest/libgtest.la \ + $(GALLIUM_COMMON_LIB_DEPS) \ + $(LLVM_LIBS) \ + $(PTHREAD_LIBS) \ + $(DLOPEN_LIBS) 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..5f3378637a --- /dev/null +++ b/src/mesa/state_tracker/tests/test_glsl_to_tgsi_lifetime.cpp @@ -0,0 +1,976 @@ +/* + * 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; + +/* A line to describe a TGSI instruction for building mock shaders */ +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; + +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>>; + + +class MesaTestWithMemCtx : public testing::Test { + void SetUp(); + void TearDown(); +protected: + void *mem_ctx; +}; + +class LifetimeEvaluatorTest : public MesaTestWithMemCtx { +protected: + void run(const vector<MockCodeline>& code, const expectation& e); +private: + virtual void check(const vector<lifetime>& result, const expectation& e) = 0; +}; + +/* This is a test class to check the exact life times of + * registers. */ +class LifetimeEvaluatorExactTest : public LifetimeEvaluatorTest { +protected: + void check(const vector<lifetime>& result, const expectation& e); +}; + +/* This test class checks that the life time covers at least + * in the expected range. It is used for cases where we know that + * a the implementation could be improved on estimating the minimal + * life time. + */ +class LifetimeEvaluatorAtLeastTest : public LifetimeEvaluatorTest { +protected: + void check(const vector<lifetime>& result, const expectation& e); +}; + +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}, {}}, + { TGSI_OPCODE_BGNLOOP }, + { TGSI_OPCODE_UADD, {2}, {1, in0}, {}}, + { TGSI_OPCODE_UADD, {3}, {1, 2}, {}}, + { TGSI_OPCODE_UADD, {3}, {3, in1}, {}}, + { TGSI_OPCODE_ENDLOOP }, + { TGSI_OPCODE_MOV, {out0}, {3}, {}}, + { 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}, {}}, + { TGSI_OPCODE_BGNLOOP }, + { TGSI_OPCODE_IF}, + { TGSI_OPCODE_UADD, {2}, {1, in0}, {}}, + { TGSI_OPCODE_ENDIF}, + { TGSI_OPCODE_UADD, {3}, {1, 2}, {}}, + { TGSI_OPCODE_UADD, {3}, {3, in1}, {}}, + { TGSI_OPCODE_ENDLOOP }, + { TGSI_OPCODE_MOV, {out0}, {3}, {}}, + { 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}, {}}, + { TGSI_OPCODE_BGNLOOP }, + { TGSI_OPCODE_IF}, + { TGSI_OPCODE_UADD, {2}, {1, in0}, {}}, + { TGSI_OPCODE_ELSE }, + { TGSI_OPCODE_MOV, {2}, {1}, {}}, + { TGSI_OPCODE_ENDIF}, + { TGSI_OPCODE_UADD, {3}, {1, 2}, {}}, + { TGSI_OPCODE_UADD, {3}, {3, in1}, {}}, + { TGSI_OPCODE_ENDLOOP }, + { TGSI_OPCODE_MOV, {out0}, {3}, {}}, + { 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}, {}}, + { TGSI_OPCODE_BGNLOOP }, + { TGSI_OPCODE_IF}, + { TGSI_OPCODE_UADD, {2}, {1, in0}, {}}, + { TGSI_OPCODE_ELSE }, + { TGSI_OPCODE_ADD, {2}, {1, 2}, {}}, + { TGSI_OPCODE_ENDIF}, + { TGSI_OPCODE_UADD, {3}, {1, 2}, {}}, + { TGSI_OPCODE_UADD, {3}, {3, in1}, {}}, + { TGSI_OPCODE_ENDLOOP }, + { TGSI_OPCODE_MOV, {out0}, {3}, {}}, + { 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}, {}}, + { TGSI_OPCODE_BGNLOOP }, + { TGSI_OPCODE_IF, {}, {in0}, {}}, + { TGSI_OPCODE_UADD, {2}, {1, 3}, {}}, + { TGSI_OPCODE_ENDIF}, + { TGSI_OPCODE_UADD, {3}, {1, 2}, {}}, + { TGSI_OPCODE_UADD, {3}, {3, in1}, {}}, + { TGSI_OPCODE_ENDLOOP }, + { TGSI_OPCODE_MOV, {out0}, {3}, {}}, + { 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 }, + { TGSI_OPCODE_IF, {}, {in0}, {}}, + { TGSI_OPCODE_IF, {}, {in0}, {}}, + { TGSI_OPCODE_MOV, {1}, {in0}, {}}, + { TGSI_OPCODE_ELSE}, + { TGSI_OPCODE_MOV, {1}, {in0}, {}}, + { TGSI_OPCODE_ENDIF}, + { TGSI_OPCODE_ELSE}, + { TGSI_OPCODE_IF, {}, {in0}, {}}, + { TGSI_OPCODE_MOV, {1}, {in0}, {}}, + { TGSI_OPCODE_ELSE}, + { TGSI_OPCODE_MOV, {1}, {in0}, {}}, + { TGSI_OPCODE_ENDIF}, + { TGSI_OPCODE_ENDIF}, + { TGSI_OPCODE_MOV, {out0}, {1}, {}}, + { TGSI_OPCODE_ENDLOOP }, + { TGSI_OPCODE_END} + }; + run (code, expectation({{-1,-1},{3, 14}})); +} + + + +TEST_F(LifetimeEvaluatorExactTest, NestedIfInLoopWriteNotAlways) +{ + const vector<MockCodeline> code = { + { TGSI_OPCODE_BGNLOOP }, + { TGSI_OPCODE_IF, {}, {in0}, {}}, + { TGSI_OPCODE_IF, {}, {in0}, {}}, + { TGSI_OPCODE_MOV, {1}, {in0}, {}}, + { TGSI_OPCODE_ELSE}, + { TGSI_OPCODE_MOV, {1}, {in0}, {}}, + { TGSI_OPCODE_ENDIF}, + { TGSI_OPCODE_ELSE}, + { TGSI_OPCODE_IF, {}, {in0}, {}}, + { TGSI_OPCODE_MOV, {1}, {in0}, {}}, + { TGSI_OPCODE_ENDIF}, + { TGSI_OPCODE_ENDIF}, + { TGSI_OPCODE_MOV, {out0}, {1}, {}}, + { TGSI_OPCODE_ENDLOOP }, + { 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 }, + { TGSI_OPCODE_IF, {}, {in0}, {}}, + { TGSI_OPCODE_CONT}, + { TGSI_OPCODE_ENDIF}, + { TGSI_OPCODE_MOV, {1}, {in0}, {}}, + { TGSI_OPCODE_ENDLOOP }, + { TGSI_OPCODE_MOV, {out0}, {1}, {}}, + { 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 }, + { TGSI_OPCODE_BGNLOOP }, + { TGSI_OPCODE_IF, {}, {in0}, {}}, + { TGSI_OPCODE_CONT}, + { TGSI_OPCODE_ENDIF}, + { TGSI_OPCODE_MOV, {1}, {in0}, {}}, + { TGSI_OPCODE_ENDLOOP }, + { TGSI_OPCODE_MOV, {out0}, {1}, {}}, + { TGSI_OPCODE_ENDLOOP }, + { TGSI_OPCODE_END} + }; + run (code, expectation({{-1,-1},{0, 8}})); +} + +/* 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 }, + { TGSI_OPCODE_BGNLOOP }, + { TGSI_OPCODE_BGNLOOP }, + { 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}, {}}, + { TGSI_OPCODE_ENDLOOP }, + { TGSI_OPCODE_END} + }; + run (code, expectation({{-1,-1},{0, 10}})); +} + +/* Temporary used to switch must live through all case statememts */ +TEST_F(LifetimeEvaluatorExactTest, UseSwitchCase) +{ + const vector<MockCodeline> code = { + {TGSI_OPCODE_MOV, {1}, {in0}, {}}, + {TGSI_OPCODE_SWITCH, {}, {1}, {}}, + { TGSI_OPCODE_CASE, {}, {1}, {}}, + { TGSI_OPCODE_CASE, {}, {1}, {}}, + { TGSI_OPCODE_BRK}, + { TGSI_OPCODE_DEFAULT}, + {TGSI_OPCODE_ENDSWITCH}, + { TGSI_OPCODE_END} + }; + run (code, expectation({{-1,-1},{0, 3}})); +} + +TEST_F(LifetimeEvaluatorExactTest, WriteTwoOnlyUseOne) +{ + const vector<MockCodeline> code = { + { TGSI_OPCODE_DFRACEXP , {1, 2}, {in0}, {}}, + { TGSI_OPCODE_ADD , {3}, {2, in0}, {}}, + { TGSI_OPCODE_MOV, {out1}, {3}, {}}, + { TGSI_OPCODE_END}, + + }; + run (code, expectation({{-1,-1},{0,1}, {0,1}, {1,2}})); +} + +/* if a break is in the loop, all variables written after the + * break and used outside the loop must be maintained for the + * whole loop */ +TEST_F(LifetimeEvaluatorExactTest, LoopWithWriteAfterBreak) +{ + const vector<MockCodeline> code = { + { TGSI_OPCODE_BGNLOOP }, + { TGSI_OPCODE_IF, {}, {in0}, {}}, + { TGSI_OPCODE_BRK}, + { TGSI_OPCODE_ENDIF}, + { TGSI_OPCODE_MOV, {1}, {in0}, {}}, + { TGSI_OPCODE_ENDLOOP }, + { TGSI_OPCODE_MOV, {out0}, {1}, {}}, + { TGSI_OPCODE_END} + }; + run (code, expectation({{-1,-1},{0, 6}})); + +} + +/* if a break is in the loop, but inside a switch case, so it + * referes to that inner loop. The variable has to survive the loop */ +TEST_F(LifetimeEvaluatorExactTest, LoopWithWriteAfterBreakInSwitchInLoop) +{ + const vector<MockCodeline> code = { + { TGSI_OPCODE_SWITCH, {}, {in1}, {}}, + { TGSI_OPCODE_CASE, {}, {in1}, {}}, + { TGSI_OPCODE_BGNLOOP }, + { TGSI_OPCODE_IF, {}, {in0}, {}}, + { TGSI_OPCODE_BRK}, + { TGSI_OPCODE_ENDIF}, + { TGSI_OPCODE_MOV, {1}, {in0}, {}}, + { TGSI_OPCODE_ENDLOOP }, + { TGSI_OPCODE_DEFAULT, {}, {}, {}}, + { TGSI_OPCODE_ENDSWITCH, {}, {}, {}}, + { TGSI_OPCODE_MOV, {out0}, {1}, {}}, + { TGSI_OPCODE_END} + }; + run (code, expectation({{-1,-1},{2, 10}})); +} + +/* value read/write in differnt loops, conditional */ +TEST_F(LifetimeEvaluatorExactTest, LoopsWithDifferntScopesConditionalWrite) +{ + const vector<MockCodeline> code = { + { TGSI_OPCODE_BGNLOOP }, + { TGSI_OPCODE_IF, {}, {in0}, {}}, + { TGSI_OPCODE_MOV, {1}, {in0}, {}}, + { TGSI_OPCODE_ENDIF}, + { TGSI_OPCODE_ENDLOOP }, + { TGSI_OPCODE_BGNLOOP }, + { TGSI_OPCODE_MOV, {out0}, {1}, {}}, + { TGSI_OPCODE_ENDLOOP }, + { TGSI_OPCODE_END} + }; + run (code, expectation({{-1,-1},{0,7}})); +} + + +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 }, + { TGSI_OPCODE_SWITCH, {}, {in0}, {} }, + { TGSI_OPCODE_CASE, {}, {in0}, {} }, + { TGSI_OPCODE_MOV, {1}, {in0}, {}}, + { TGSI_OPCODE_BRK }, + { TGSI_OPCODE_DEFAULT }, + { TGSI_OPCODE_MOV, {out0}, {1}, {}}, + { TGSI_OPCODE_BRK }, + { TGSI_OPCODE_ENDSWITCH }, + { TGSI_OPCODE_ENDLOOP }, + { TGSI_OPCODE_END} + }; + run (code, expectation({{-1,-1},{0, 9}})); +} + + +TEST_F(LifetimeEvaluatorExactTest, LoopRWInSwitchCaseLastCaseWithoutBreak) +{ + 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_MOV, {out0}, {1}, {}}, + { TGSI_OPCODE_ENDSWITCH }, + { TGSI_OPCODE_ENDLOOP }, + { 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 }, + { TGSI_OPCODE_SWITCH, {}, {in0}, {} }, + { TGSI_OPCODE_CASE, {}, {in0}, {} }, + { TGSI_OPCODE_MOV, {1}, {in0}, {}}, + { TGSI_OPCODE_MOV, {out0}, {1}, {}}, + { TGSI_OPCODE_BRK }, + { TGSI_OPCODE_DEFAULT }, + { TGSI_OPCODE_BRK }, + { TGSI_OPCODE_ENDSWITCH }, + { TGSI_OPCODE_ENDLOOP }, + { 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 }, + { TGSI_OPCODE_SWITCH, {}, {in0}, {}}, + { TGSI_OPCODE_CASE, {}, {in0}, {} }, + { TGSI_OPCODE_MOV, {}, {in0}, {}}, + { TGSI_OPCODE_BRK }, + { TGSI_OPCODE_DEFAULT }, + { TGSI_OPCODE_MOV, {1}, {in0}, {}}, + { TGSI_OPCODE_BRK }, + { TGSI_OPCODE_ENDSWITCH }, + { TGSI_OPCODE_MOV, {out0}, {1}, {}}, + { TGSI_OPCODE_ENDLOOP }, + { TGSI_OPCODE_END} + }; + run (code, expectation({{-1,-1},{3,9}})); +} + +/* value read/write in differnt loops */ +TEST_F(LifetimeEvaluatorExactTest, LoopsWithDifferntScopes) +{ + const vector<MockCodeline> code = { + { TGSI_OPCODE_BGNLOOP }, + { TGSI_OPCODE_MOV, {1}, {in0}, {}}, + { TGSI_OPCODE_ENDLOOP }, + { TGSI_OPCODE_BGNLOOP }, + { TGSI_OPCODE_MOV, {out0}, {1}, {}}, + { TGSI_OPCODE_ENDLOOP }, + { TGSI_OPCODE_END} + }; + run (code, expectation({{-1,-1},{1,5}})); +} + +/* first read before first write wiredness with nested loops */ +TEST_F(LifetimeEvaluatorExactTest, LoopsWithDifferntScopesConditionalReadBeforeWrite) +{ + const vector<MockCodeline> code = { + { TGSI_OPCODE_BGNLOOP }, + { TGSI_OPCODE_BGNLOOP }, + { TGSI_OPCODE_IF, {}, {in0}, {}}, + { TGSI_OPCODE_MOV, {out0}, {1}, {}}, + { TGSI_OPCODE_ENDIF}, + { TGSI_OPCODE_ENDLOOP }, + { TGSI_OPCODE_BGNLOOP }, + { TGSI_OPCODE_MOV, {1}, {in0}, {}}, + { TGSI_OPCODE_ENDLOOP }, + { TGSI_OPCODE_ENDLOOP }, + { TGSI_OPCODE_END} + }; + run (code, expectation({{-1,-1},{0,9}})); +} + +/* The variable is conditionally read before first written, so + * it has to surive all the loops. */ +TEST_F(LifetimeEvaluatorExactTest, FRaWSameInstructionInLoopAndCondition) +{ + const vector<MockCodeline> code = { + { TGSI_OPCODE_BGNLOOP }, + { TGSI_OPCODE_BGNLOOP }, + { TGSI_OPCODE_IF }, + { TGSI_OPCODE_ADD, {1}, {1,in0}, {}}, + { TGSI_OPCODE_ENDIF}, + { TGSI_OPCODE_MOV, {1}, {in1}, {}}, + { TGSI_OPCODE_ENDLOOP }, + { TGSI_OPCODE_ENDLOOP }, + { TGSI_OPCODE_END}, + + }; + run (code, expectation({{-1,-1},{0,7}})); +} + +/* If unconditionally first written and read in the same + * instruction, then the register must be kept for the + * one write, but not more (undefined behaviour) */ + +TEST_F(LifetimeEvaluatorExactTest, FRaWSameInstruction) +{ + const vector<MockCodeline> code = { + { TGSI_OPCODE_ADD, {1}, {1,in0}, {}}, + { TGSI_OPCODE_END}, + + }; + run (code, expectation({{-1,-1},{0,1}})); +} + +/* If unconditionally written and read in the same + * instruction, various times then the register must be + * kept for the one write, but not more (undefined behaviour) */ + +TEST_F(LifetimeEvaluatorExactTest, FRaWSameInstructionMoreThenOnce) +{ + const vector<MockCodeline> code = { + { TGSI_OPCODE_ADD, {1}, {1,in0}, {}}, + { TGSI_OPCODE_ADD, {1}, {1,in0}, {}}, + { TGSI_OPCODE_END}, + + }; + run (code, expectation({{-1,-1},{0,2}})); +} + + +/* 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}, {}}, + { TGSI_OPCODE_END} + }; + run (code, expectation({{-1,-1},{0,1}})); +} + +/* register read in if */ +TEST_F(LifetimeEvaluatorExactTest, SimpleReadForIf) +{ + const vector<MockCodeline> code = { + { TGSI_OPCODE_MOV, {1}, {in0}, {}}, + { TGSI_OPCODE_ADD, {out0}, {in0, in1}, {}}, + { TGSI_OPCODE_IF, {}, {1}, {}}, + { TGSI_OPCODE_ENDIF} + }; + run (code, expectation({{-1,-1},{0,2}})); +} + +/* register read in switch and cases */ +TEST_F(LifetimeEvaluatorExactTest, SimpleReadForSwitchAndCase) +{ + const vector<MockCodeline> code = { + { TGSI_OPCODE_MOV, {1}, {in0}, {}}, + { TGSI_OPCODE_SWITCH, {}, {1}, {}}, + { TGSI_OPCODE_CASE, {}, {1}, {}}, + { TGSI_OPCODE_CASE, {}, {1}, {}}, + { TGSI_OPCODE_END, {}, {1}, {}}, + }; + run (code, expectation({{-1,-1},{0,3}})); +} + +/* first read before first write wiredness with nested loops */ +TEST_F(LifetimeEvaluatorExactTest, LoopsWithDifferentScopesCondReadBeforeWrite) +{ + const vector<MockCodeline> code = { + { TGSI_OPCODE_BGNLOOP }, + { TGSI_OPCODE_BGNLOOP }, + { TGSI_OPCODE_IF, {}, {in0}, {}}, + { TGSI_OPCODE_MOV, {out0}, {1}, {}}, + { TGSI_OPCODE_ENDIF}, + { TGSI_OPCODE_ENDLOOP }, + { TGSI_OPCODE_BGNLOOP }, + { TGSI_OPCODE_MOV, {1}, {in0}, {}}, + { TGSI_OPCODE_ENDLOOP }, + { TGSI_OPCODE_ENDLOOP }, + { TGSI_OPCODE_END} + }; + run (code, expectation({{-1,-1},{0,9}})); +} + + +TEST_F(LifetimeEvaluatorExactTest, WriteTwoReadOne) +{ + const vector<MockCodeline> code = { + { TGSI_OPCODE_DFRACEXP , {1, 2}, {in0}, {}}, + { TGSI_OPCODE_ADD , {3}, {2, in0}, {}}, + { TGSI_OPCODE_MOV, {out1}, {3}, {}}, + { TGSI_OPCODE_END}, + + }; + run (code, expectation({{-1,-1},{0,1}, {0,1}, {1,2}})); +} + + +TEST_F(LifetimeEvaluatorExactTest, SomeScopesAndNoEndProgramId) +{ + const vector<MockCodeline> code = { + { TGSI_OPCODE_MOV, {1}, {in0}, {}}, + { 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}})); +} + +TEST_F(LifetimeEvaluatorExactTest, SerialReadWrite) +{ + const vector<MockCodeline> code = { + { TGSI_OPCODE_MOV, {1}, {in0}, {}}, + { TGSI_OPCODE_MOV, {2}, {1}, {}}, + { TGSI_OPCODE_MOV, {3}, {2}, {}}, + { TGSI_OPCODE_MOV, {out0}, {3}, {}}, + { TGSI_OPCODE_END}, + + }; + run (code, expectation({{-1,-1},{0,1}, {1,2}, {2,3}})); +} + + +/* Check that two destination registers are used */ +TEST_F(LifetimeEvaluatorExactTest, TwoDestRegisters) +{ + const vector<MockCodeline> code = { + { TGSI_OPCODE_DFRACEXP , {1,2}, {in0}, {}}, + { 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, WriteInLoopInConditionalReadOutside) +{ + const vector<MockCodeline> code = { + { TGSI_OPCODE_BGNLOOP}, + { TGSI_OPCODE_IF, {}, {in0}, {}}, + { TGSI_OPCODE_BGNLOOP}, + { TGSI_OPCODE_MOV, {1}, {in1}, {}}, + { TGSI_OPCODE_ENDLOOP}, + { TGSI_OPCODE_ENDIF}, + { TGSI_OPCODE_ADD, {2}, {1,in1}, {}}, + { TGSI_OPCODE_ENDLOOP}, + { TGSI_OPCODE_MOV, {out0}, {2}, {}}, + { TGSI_OPCODE_END} + }; + run (code, expectation({{-1,-1},{0,7}, {6,8}})); +} + + +/* + * With two destinations if one value is thrown away, we must + * ensure that the two output registers don't merge. + * In this test case the last access for 2 and 3 is in line 4, + * but 4 can only be merged with 3 because it is read, 2 on the + * other hand is written to, and merging it with 4 would result in + * a bug. */ +TEST_F(LifetimeEvaluatorExactTest, WritePastLastRead2) +{ + const vector<MockCodeline> code = { + { TGSI_OPCODE_MOV, {1}, {in0}, {}}, + { TGSI_OPCODE_MOV, {2}, {in0}, {}}, + { TGSI_OPCODE_ADD, {3}, {1,2}, {}}, + { TGSI_OPCODE_DFRACEXP , {2,4}, {3}, {}}, + { TGSI_OPCODE_MOV, {out1}, {4}, {}}, + { TGSI_OPCODE_END} + }; + run (code, expectation({{-1,-1},{0,2}, {1,4}, {2,3}, {3,4}})); +} + +/* Check that three destination registers are used */ +TEST_F(LifetimeEvaluatorExactTest, ThreeSourceRegisters) +{ + const vector<MockCodeline> code = { + { TGSI_OPCODE_DFRACEXP , {1,2}, {in0}, {}}, + { TGSI_OPCODE_ADD , {3}, {in0, in1}, {}}, + { TGSI_OPCODE_MAD, {out0}, {1,2, 3}, {}}, + { TGSI_OPCODE_END} + }; + run (code, expectation({{-1,-1},{0,2}, {0,2}, {1,2}})); +} + +/* Check minimal lifetime for registers only written to */ +TEST_F(LifetimeEvaluatorExactTest, OverwriteWrittenOnlyTemps) +{ + const vector<MockCodeline> code = { + { TGSI_OPCODE_MOV , {1}, {in0}, {}}, + { TGSI_OPCODE_MOV , {2}, {in1}, {}}, + { TGSI_OPCODE_END} + }; + run (code, expectation({{-1,-1},{0,1}, {1,2}})); +} + +/* same register is only written. This should not happen, + * but to handle the case we want the register to life + * at least past the last write instruction */ +TEST_F(LifetimeEvaluatorExactTest, WriteOnlyTwiceSame) +{ + const vector<MockCodeline> code = { + { TGSI_OPCODE_MOV, {1}, {in0}, {}}, + { TGSI_OPCODE_MOV, {1}, {in0}, {}}, + { TGSI_OPCODE_END} + }; + run (code, expectation({{-1,-1},{0,2}})); +} + + +/* Dead code elimination should catch and remove the case + * when a variable is written after its last read, but + * we want the code to be aware of this case. + * The life time of this uselessly written variable is set + * to the instruction after the write, because + * otherwise it could be re-used too early. */ +TEST_F(LifetimeEvaluatorExactTest, WritePastLastRead) +{ + const vector<MockCodeline> code = { + { TGSI_OPCODE_MOV, {1}, {in0}, {}}, + { TGSI_OPCODE_MOV, {2}, {1}, {}}, + { TGSI_OPCODE_MOV, {1}, {2}, {}}, + { TGSI_OPCODE_END}, + + }; + run (code, expectation({{-1,-1},{0,3}, {1,2}})); +} + +/* if a break is in the loop, all variables written after the + * break and used outside the loop the variable must survive the + * outer loop + */ +TEST_F(LifetimeEvaluatorExactTest, NestedLoopWithWriteAfterBreak) +{ + const vector<MockCodeline> code = { + { TGSI_OPCODE_BGNLOOP }, + { TGSI_OPCODE_BGNLOOP }, + { TGSI_OPCODE_IF, {}, {in0}, {}}, + { TGSI_OPCODE_BRK}, + { TGSI_OPCODE_ENDIF}, + { TGSI_OPCODE_MOV, {1}, {in0}, {}}, + { TGSI_OPCODE_ENDLOOP }, + { TGSI_OPCODE_MOV, {out0}, {1}, {}}, + { TGSI_OPCODE_ENDLOOP }, + { TGSI_OPCODE_END} + }; + run (code, expectation({{-1,-1},{0, 8}})); +} + +/* Implementation of helper and test classes */ + +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]); + } + 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); +} + + +void MesaTestWithMemCtx::SetUp() +{ + mem_ctx = ralloc_context(nullptr); +} + +void MesaTestWithMemCtx::TearDown() +{ + ralloc_free(mem_ctx); + mem_ctx = nullptr; +} + +void LifetimeEvaluatorTest::run(const vector<MockCodeline>& code, const expectation& e) +{ + MockShader shader(code); + std::vector<lifetime> result(shader.get_num_temps()); + + get_temp_registers_required_lifetimes(mem_ctx, shader.get_program(), + shader.get_num_temps(), &result[0]); + + /* lifetimes[0] not used, but created for simpler processing */ + ASSERT_EQ(result.size(), e.size()); + check(result, e); +} + + +void LifetimeEvaluatorExactTest::check( const vector<lifetime>& lifetimes, + const expectation& e) +{ + for (unsigned i = 1; i < lifetimes.size(); ++i) { + EXPECT_EQ(lifetimes[i].begin, e[i][0]); + EXPECT_EQ(lifetimes[i].end, e[i][1]); + } +} + +void LifetimeEvaluatorAtLeastTest::check( const vector<lifetime>& lifetimes, + const expectation& e) +{ + for (unsigned i = 1; i < lifetimes.size(); ++i) { + EXPECT_LE(lifetimes[i].begin, e[i][0]); + EXPECT_GE(lifetimes[i].end, e[i][1]); + } +} -- 2.13.0 _______________________________________________ mesa-dev mailing list mesa-dev@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/mesa-dev