1 // Copyright (c) 2017 Google Inc.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #include <string>
16 #include <vector>
17
18 #include "gmock/gmock.h"
19 #include "spirv-tools/libspirv.hpp"
20 #include "spirv-tools/optimizer.hpp"
21 #include "test/opt/pass_fixture.h"
22
23 namespace spvtools {
24 namespace opt {
25 namespace {
26
27 using ::testing::Eq;
28
29 // Return a string that contains the minimum instructions needed to form
30 // a valid module. Other instructions can be appended to this string.
Header()31 std::string Header() {
32 return R"(OpCapability Shader
33 OpCapability Linkage
34 OpMemoryModel Logical GLSL450
35 )";
36 }
37
TEST(Optimizer,CanRunNullPassWithDistinctInputOutputVectors)38 TEST(Optimizer, CanRunNullPassWithDistinctInputOutputVectors) {
39 SpirvTools tools(SPV_ENV_UNIVERSAL_1_0);
40 std::vector<uint32_t> binary_in;
41 tools.Assemble(Header() + "OpName %foo \"foo\"\n%foo = OpTypeVoid",
42 &binary_in);
43
44 Optimizer opt(SPV_ENV_UNIVERSAL_1_0);
45 opt.RegisterPass(CreateNullPass());
46 std::vector<uint32_t> binary_out;
47 opt.Run(binary_in.data(), binary_in.size(), &binary_out);
48
49 std::string disassembly;
50 tools.Disassemble(binary_out.data(), binary_out.size(), &disassembly);
51 EXPECT_THAT(disassembly,
52 Eq(Header() + "OpName %foo \"foo\"\n%foo = OpTypeVoid\n"));
53 }
54
TEST(Optimizer,CanRunTransformingPassWithDistinctInputOutputVectors)55 TEST(Optimizer, CanRunTransformingPassWithDistinctInputOutputVectors) {
56 SpirvTools tools(SPV_ENV_UNIVERSAL_1_0);
57 std::vector<uint32_t> binary_in;
58 tools.Assemble(Header() + "OpName %foo \"foo\"\n%foo = OpTypeVoid",
59 &binary_in);
60
61 Optimizer opt(SPV_ENV_UNIVERSAL_1_0);
62 opt.RegisterPass(CreateStripDebugInfoPass());
63 std::vector<uint32_t> binary_out;
64 opt.Run(binary_in.data(), binary_in.size(), &binary_out);
65
66 std::string disassembly;
67 tools.Disassemble(binary_out.data(), binary_out.size(), &disassembly);
68 EXPECT_THAT(disassembly, Eq(Header() + "%void = OpTypeVoid\n"));
69 }
70
TEST(Optimizer,CanRunNullPassWithAliasedVectors)71 TEST(Optimizer, CanRunNullPassWithAliasedVectors) {
72 SpirvTools tools(SPV_ENV_UNIVERSAL_1_0);
73 std::vector<uint32_t> binary;
74 tools.Assemble("OpName %foo \"foo\"\n%foo = OpTypeVoid", &binary);
75
76 Optimizer opt(SPV_ENV_UNIVERSAL_1_0);
77 opt.RegisterPass(CreateNullPass());
78 opt.Run(binary.data(), binary.size(), &binary); // This is the key.
79
80 std::string disassembly;
81 tools.Disassemble(binary.data(), binary.size(), &disassembly);
82 EXPECT_THAT(disassembly, Eq("OpName %foo \"foo\"\n%foo = OpTypeVoid\n"));
83 }
84
TEST(Optimizer,CanRunNullPassWithAliasedVectorDataButDifferentSize)85 TEST(Optimizer, CanRunNullPassWithAliasedVectorDataButDifferentSize) {
86 SpirvTools tools(SPV_ENV_UNIVERSAL_1_0);
87 std::vector<uint32_t> binary;
88 tools.Assemble(Header() + "OpName %foo \"foo\"\n%foo = OpTypeVoid", &binary);
89
90 Optimizer opt(SPV_ENV_UNIVERSAL_1_0);
91 opt.RegisterPass(CreateNullPass());
92 auto orig_size = binary.size();
93 // Now change the size. Add a word that will be ignored
94 // by the optimizer.
95 binary.push_back(42);
96 EXPECT_THAT(orig_size + 1, Eq(binary.size()));
97 opt.Run(binary.data(), orig_size, &binary); // This is the key.
98 // The binary vector should have been rewritten.
99 EXPECT_THAT(binary.size(), Eq(orig_size));
100
101 std::string disassembly;
102 tools.Disassemble(binary.data(), binary.size(), &disassembly);
103 EXPECT_THAT(disassembly,
104 Eq(Header() + "OpName %foo \"foo\"\n%foo = OpTypeVoid\n"));
105 }
106
TEST(Optimizer,CanRunTransformingPassWithAliasedVectors)107 TEST(Optimizer, CanRunTransformingPassWithAliasedVectors) {
108 SpirvTools tools(SPV_ENV_UNIVERSAL_1_0);
109 std::vector<uint32_t> binary;
110 tools.Assemble(Header() + "OpName %foo \"foo\"\n%foo = OpTypeVoid", &binary);
111
112 Optimizer opt(SPV_ENV_UNIVERSAL_1_0);
113 opt.RegisterPass(CreateStripDebugInfoPass());
114 opt.Run(binary.data(), binary.size(), &binary); // This is the key
115
116 std::string disassembly;
117 tools.Disassemble(binary.data(), binary.size(), &disassembly);
118 EXPECT_THAT(disassembly, Eq(Header() + "%void = OpTypeVoid\n"));
119 }
120
TEST(Optimizer,CanValidateFlags)121 TEST(Optimizer, CanValidateFlags) {
122 Optimizer opt(SPV_ENV_UNIVERSAL_1_0);
123 EXPECT_FALSE(opt.FlagHasValidForm("bad-flag"));
124 EXPECT_TRUE(opt.FlagHasValidForm("-O"));
125 EXPECT_TRUE(opt.FlagHasValidForm("-Os"));
126 EXPECT_FALSE(opt.FlagHasValidForm("-O2"));
127 EXPECT_TRUE(opt.FlagHasValidForm("--this_flag"));
128 }
129
TEST(Optimizer,CanRegisterPassesFromFlags)130 TEST(Optimizer, CanRegisterPassesFromFlags) {
131 SpirvTools tools(SPV_ENV_UNIVERSAL_1_0);
132 Optimizer opt(SPV_ENV_UNIVERSAL_1_0);
133
134 spv_message_level_t msg_level;
135 const char* msg_fname;
136 spv_position_t msg_position;
137 const char* msg;
138 auto examine_message = [&msg_level, &msg_fname, &msg_position, &msg](
139 spv_message_level_t ml, const char* f,
140 const spv_position_t& p, const char* m) {
141 msg_level = ml;
142 msg_fname = f;
143 msg_position = p;
144 msg = m;
145 };
146 opt.SetMessageConsumer(examine_message);
147
148 std::vector<std::string> pass_flags = {
149 "--strip-debug",
150 "--strip-reflect",
151 "--set-spec-const-default-value=23:42 21:12",
152 "--if-conversion",
153 "--freeze-spec-const",
154 "--inline-entry-points-exhaustive",
155 "--inline-entry-points-opaque",
156 "--convert-local-access-chains",
157 "--eliminate-dead-code-aggressive",
158 "--eliminate-insert-extract",
159 "--eliminate-local-single-block",
160 "--eliminate-local-single-store",
161 "--merge-blocks",
162 "--merge-return",
163 "--eliminate-dead-branches",
164 "--eliminate-dead-functions",
165 "--eliminate-local-multi-store",
166 "--eliminate-common-uniform",
167 "--eliminate-dead-const",
168 "--eliminate-dead-inserts",
169 "--eliminate-dead-variables",
170 "--fold-spec-const-op-composite",
171 "--loop-unswitch",
172 "--scalar-replacement=300",
173 "--scalar-replacement",
174 "--strength-reduction",
175 "--unify-const",
176 "--flatten-decorations",
177 "--compact-ids",
178 "--cfg-cleanup",
179 "--local-redundancy-elimination",
180 "--loop-invariant-code-motion",
181 "--reduce-load-size",
182 "--redundancy-elimination",
183 "--private-to-local",
184 "--remove-duplicates",
185 "--workaround-1209",
186 "--replace-invalid-opcode",
187 "--simplify-instructions",
188 "--ssa-rewrite",
189 "--copy-propagate-arrays",
190 "--loop-fission=20",
191 "--loop-fusion=2",
192 "--loop-unroll",
193 "--vector-dce",
194 "--loop-unroll-partial=3",
195 "--loop-peeling",
196 "--ccp",
197 "-O",
198 "-Os",
199 "--legalize-hlsl"};
200 EXPECT_TRUE(opt.RegisterPassesFromFlags(pass_flags));
201
202 // Test some invalid flags.
203 EXPECT_FALSE(opt.RegisterPassFromFlag("-O2"));
204 EXPECT_EQ(msg_level, SPV_MSG_ERROR);
205
206 EXPECT_FALSE(opt.RegisterPassFromFlag("-loop-unroll"));
207 EXPECT_EQ(msg_level, SPV_MSG_ERROR);
208
209 EXPECT_FALSE(opt.RegisterPassFromFlag("--set-spec-const-default-value"));
210 EXPECT_EQ(msg_level, SPV_MSG_ERROR);
211
212 EXPECT_FALSE(opt.RegisterPassFromFlag("--scalar-replacement=s"));
213 EXPECT_EQ(msg_level, SPV_MSG_ERROR);
214
215 EXPECT_FALSE(opt.RegisterPassFromFlag("--loop-fission=-4"));
216 EXPECT_EQ(msg_level, SPV_MSG_ERROR);
217
218 EXPECT_FALSE(opt.RegisterPassFromFlag("--loop-fusion=xx"));
219 EXPECT_EQ(msg_level, SPV_MSG_ERROR);
220
221 EXPECT_FALSE(opt.RegisterPassFromFlag("--loop-unroll-partial"));
222 EXPECT_EQ(msg_level, SPV_MSG_ERROR);
223 }
224
225 } // namespace
226 } // namespace opt
227 } // namespace spvtools
228