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 | 41 + .../tests/test_glsl_to_tgsi_lifetime.cpp | 969 +++++++++++++++++++++ 4 files changed, 1012 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 3339926d93..0075e91f77 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..a931cb6498 --- /dev/null +++ b/src/mesa/state_tracker/tests/Makefile.am @@ -0,0 +1,41 @@ +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) \ + -ldl + 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..7e07f8868f --- /dev/null +++ b/src/mesa/state_tracker/tests/test_glsl_to_tgsi_lifetime.cpp @@ -0,0 +1,969 @@ +/* + * 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; + + +/* 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; +}; + +/* A few constants to use in the mock shaders */ +const int in0 = 0; +const int in1 = -1; +const int in2 = -2; + +const int out0 = 0; +const int out1 = -1; + +/* A class to create a shader program to check the register allocation + * and renaming. The created exec_list is not completely set up and can + * only be used for the register tife-time analyis. */ +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; +}; + +/* type for register lifetime expectation */ +using expectation = vector<vector<int>>; + + +class LifetimeEvaluatorTest : public testing::Test { + + void SetUp(); + void TearDown(); +protected: + void run(const vector<MockCodeline>& code, const expectation& e); +private: + virtual void check(const vector<lifetime>& result, const expectation& e) = 0; + void *mem_ctx; +}; + +/* This is a teat 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 }, // 15 + { 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 }, // 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 }, + { 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 outer loop */ +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}})); +} + +/* Test whether variable is kept also if the continue is in a + * higher scope than the variable write */ +TEST_F(LifetimeEvaluatorExactTest, NestedLoopWithWriteInLoopAfterContinue) +{ + const vector<MockCodeline> code = { + { TGSI_OPCODE_BGNLOOP }, + { TGSI_OPCODE_BGNLOOP }, + { TGSI_OPCODE_IF, {}, {in0}, {}}, + { TGSI_OPCODE_CONT}, + { TGSI_OPCODE_ENDIF}, + { TGSI_OPCODE_BGNLOOP }, + { 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}})); +} + +/* if a continue is in the loop, all variables written after the + * continue and used outside the loop must be maintained for all + * loops including the read loop */ +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}})); +} + +/* 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 the case and not to the loop, the variable doesn't + * need to survive the loop */ +TEST_F(LifetimeEvaluatorExactTest, LoopWithWriteAfterBreakInSwitch) +{ + const vector<MockCodeline> code = { + { TGSI_OPCODE_BGNLOOP }, + { TGSI_OPCODE_SWITCH, {}, {in1}, {}}, + { TGSI_OPCODE_CASE, {}, {in1}, {}}, + { TGSI_OPCODE_IF, {}, {in0}, {}}, + { TGSI_OPCODE_BRK}, + { TGSI_OPCODE_ENDIF}, + { TGSI_OPCODE_DEFAULT}, + { TGSI_OPCODE_ENDSWITCH}, + { TGSI_OPCODE_MOV, {1}, {in0}, {}}, + { TGSI_OPCODE_ENDLOOP }, + { TGSI_OPCODE_MOV, {out0}, {1}, {}}, + { TGSI_OPCODE_END} + }; + run (code, expectation({{-1,-1},{8, 10}})); +} + +/* 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}})); +} + + +/* 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 that includes the read */ +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}})); +} + +/* if a break is in the loop, all variables written after the + * break and used outside the loop must be maintained for all + * loops up onto the read scope */ +TEST_F(LifetimeEvaluatorExactTest, Nested2LoopWithWriteAfterBreak) +{ + const vector<MockCodeline> code = { + { TGSI_OPCODE_BGNLOOP }, + { TGSI_OPCODE_BGNLOOP }, + { 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_ENDLOOP }, + { TGSI_OPCODE_MOV, {out0}, {1}, {}}, + { TGSI_OPCODE_ENDLOOP }, + { TGSI_OPCODE_ENDLOOP }, + { TGSI_OPCODE_END} + }; + run (code, expectation({{-1,-1},{1, 11}})); +} + +/* 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}})); +} + +/* variable written in a switch within a loop must survive the loop */ +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}})); +} + +/* Make sure SWITCH is closed correctly in the scope stack */ +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}})); +} + +/* 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}})); +} + +/* 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}})); +} + +/* register is only written. This should not happen, + * but to handle the case we want the register to life + * at least past the write 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 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, WriteOnlyTwice) +{ + const vector<MockCodeline> code = { + { TGSI_OPCODE_MOV, {1}, {in0}, {}}, + { TGSI_OPCODE_MOV, {1}, {in0}, {}}, + { TGSI_OPCODE_END} + }; + run (code, expectation({{-1,-1},{0,2}})); +} + +/* 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 */ +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_ENDSWITCH}, + }; + run (code, expectation({{-1,-1},{0,3}})); +} + +/* Check that a missing END is handled (Unigine-Haven creates such a + * shader) */ +TEST_F(LifetimeEvaluatorExactTest, DistinceScopesAndNoEndProgramId) +{ + 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}})); +} + +/* 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}})); +} + +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}})); +} + +/* + * Somehow a duplicate of above tests specifically using the + * problematic corner case n question. DFRACEXP has two + * destinations, and 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 only 3 can be merged with 4 because it is read, 2 on the + * other hand is written to, and merging it with 4 would result in + * undefined behaviour. +*/ +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}})); +} + +/* 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}})); +} + + + +TEST_F(LifetimeEvaluatorExactTest, OnlyWriteOne) +{ + 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}})); +} + + +/* Check that two destination registers are actually 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 and three source 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}})); +} + +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(); + if (i.tex_offsets.size() > 0) + next_instr->tex_offsets = new st_src_reg[i.tex_offsets.size()]; + else + next_instr->tex_offsets = 0; + 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 LifetimeEvaluatorTest::SetUp() +{ + mem_ctx = ralloc_context(nullptr); +} + +void LifetimeEvaluatorTest::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()); + + estimate_temporary_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