1 //
2 // Copyright 2015 The ANGLE Project Authors. All rights reserved.
3 // Use of this source code is governed by a BSD-style license that can be
4 // found in the LICENSE file.
5 //
6 // compiler_test.cpp:
7 // utilities for compiler unit tests.
8
9 #include "tests/test_utils/compiler_test.h"
10
11 #include "angle_gl.h"
12 #include "compiler/translator/Compiler.h"
13 #include "compiler/translator/FunctionLookup.h"
14 #include "compiler/translator/tree_util/IntermTraverse.h"
15
16 namespace sh
17 {
18
19 namespace
20 {
21 constexpr char kBinaryBlob[] = "<binary blob>";
IsBinaryBlob(const std::string & code)22 bool IsBinaryBlob(const std::string &code)
23 {
24 return code == kBinaryBlob;
25 }
26
GetSymbolTableMangledName(TIntermAggregate * node)27 ImmutableString GetSymbolTableMangledName(TIntermAggregate *node)
28 {
29 ASSERT(!node->isConstructor());
30 return TFunctionLookup::GetMangledName(node->getFunction()->name().data(),
31 *node->getSequence());
32 }
33
34 class FunctionCallFinder : public TIntermTraverser
35 {
36 public:
FunctionCallFinder(const char * functionMangledName)37 FunctionCallFinder(const char *functionMangledName)
38 : TIntermTraverser(true, false, false),
39 mFunctionMangledName(functionMangledName),
40 mNodeFound(nullptr)
41 {}
42
visitAggregate(Visit visit,TIntermAggregate * node)43 bool visitAggregate(Visit visit, TIntermAggregate *node) override
44 {
45 if (!node->isConstructor() && GetSymbolTableMangledName(node) == mFunctionMangledName)
46 {
47 mNodeFound = node;
48 return false;
49 }
50 return true;
51 }
52
isFound() const53 bool isFound() const { return mNodeFound != nullptr; }
getNode() const54 const TIntermAggregate *getNode() const { return mNodeFound; }
55
56 private:
57 const char *mFunctionMangledName;
58 TIntermAggregate *mNodeFound;
59 };
60
61 } // anonymous namespace
62
compileTestShader(GLenum type,ShShaderSpec spec,ShShaderOutput output,const std::string & shaderString,ShBuiltInResources * resources,ShCompileOptions compileOptions,std::string * translatedCode,std::string * infoLog)63 bool compileTestShader(GLenum type,
64 ShShaderSpec spec,
65 ShShaderOutput output,
66 const std::string &shaderString,
67 ShBuiltInResources *resources,
68 ShCompileOptions compileOptions,
69 std::string *translatedCode,
70 std::string *infoLog)
71 {
72 sh::TCompiler *translator = sh::ConstructCompiler(type, spec, output);
73 if (!translator->Init(*resources))
74 {
75 SafeDelete(translator);
76 return false;
77 }
78
79 const char *shaderStrings[] = {shaderString.c_str()};
80
81 bool compilationSuccess =
82 translator->compile(shaderStrings, 1, SH_OBJECT_CODE | compileOptions);
83 TInfoSink &infoSink = translator->getInfoSink();
84 if (translatedCode)
85 {
86 *translatedCode = infoSink.obj.isBinary() ? kBinaryBlob : infoSink.obj.c_str();
87 }
88 if (infoLog)
89 {
90 *infoLog = infoSink.info.c_str();
91 }
92 SafeDelete(translator);
93 return compilationSuccess;
94 }
95
compileTestShader(GLenum type,ShShaderSpec spec,ShShaderOutput output,const std::string & shaderString,ShCompileOptions compileOptions,std::string * translatedCode,std::string * infoLog)96 bool compileTestShader(GLenum type,
97 ShShaderSpec spec,
98 ShShaderOutput output,
99 const std::string &shaderString,
100 ShCompileOptions compileOptions,
101 std::string *translatedCode,
102 std::string *infoLog)
103 {
104 ShBuiltInResources resources;
105 sh::InitBuiltInResources(&resources);
106 resources.FragmentPrecisionHigh = 1;
107 return compileTestShader(type, spec, output, shaderString, &resources, compileOptions,
108 translatedCode, infoLog);
109 }
110
MatchOutputCodeTest(GLenum shaderType,ShCompileOptions defaultCompileOptions,ShShaderOutput outputType)111 MatchOutputCodeTest::MatchOutputCodeTest(GLenum shaderType,
112 ShCompileOptions defaultCompileOptions,
113 ShShaderOutput outputType)
114 : mShaderType(shaderType), mDefaultCompileOptions(defaultCompileOptions)
115 {
116 sh::InitBuiltInResources(&mResources);
117 mResources.FragmentPrecisionHigh = 1;
118 mOutputCode[outputType] = std::string();
119 }
120
addOutputType(const ShShaderOutput outputType)121 void MatchOutputCodeTest::addOutputType(const ShShaderOutput outputType)
122 {
123 mOutputCode[outputType] = std::string();
124 }
125
getResources()126 ShBuiltInResources *MatchOutputCodeTest::getResources()
127 {
128 return &mResources;
129 }
130
compile(const std::string & shaderString)131 void MatchOutputCodeTest::compile(const std::string &shaderString)
132 {
133 compile(shaderString, mDefaultCompileOptions);
134 }
135
compile(const std::string & shaderString,const ShCompileOptions compileOptions)136 void MatchOutputCodeTest::compile(const std::string &shaderString,
137 const ShCompileOptions compileOptions)
138 {
139 std::string infoLog;
140 for (auto &code : mOutputCode)
141 {
142 bool compilationSuccess =
143 compileWithSettings(code.first, shaderString, compileOptions, &code.second, &infoLog);
144 if (!compilationSuccess)
145 {
146 FAIL() << "Shader compilation failed:\n" << infoLog;
147 }
148 }
149 }
150
compileWithSettings(ShShaderOutput output,const std::string & shaderString,const ShCompileOptions compileOptions,std::string * translatedCode,std::string * infoLog)151 bool MatchOutputCodeTest::compileWithSettings(ShShaderOutput output,
152 const std::string &shaderString,
153 const ShCompileOptions compileOptions,
154 std::string *translatedCode,
155 std::string *infoLog)
156 {
157 return compileTestShader(mShaderType, SH_GLES3_1_SPEC, output, shaderString, &mResources,
158 compileOptions, translatedCode, infoLog);
159 }
160
foundInCodeRegex(ShShaderOutput output,const std::regex & regexToFind,std::smatch * match) const161 bool MatchOutputCodeTest::foundInCodeRegex(ShShaderOutput output,
162 const std::regex ®exToFind,
163 std::smatch *match) const
164 {
165 const auto code = mOutputCode.find(output);
166 EXPECT_NE(mOutputCode.end(), code);
167 if (code == mOutputCode.end())
168 {
169 return std::string::npos;
170 }
171
172 // No meaningful check for binary blobs
173 if (IsBinaryBlob(code->second))
174 {
175 return true;
176 }
177
178 if (match)
179 {
180 return std::regex_search(code->second, *match, regexToFind);
181 }
182 else
183 {
184 return std::regex_search(code->second, regexToFind);
185 }
186 }
187
foundInCode(ShShaderOutput output,const char * stringToFind) const188 bool MatchOutputCodeTest::foundInCode(ShShaderOutput output, const char *stringToFind) const
189 {
190 const auto code = mOutputCode.find(output);
191 EXPECT_NE(mOutputCode.end(), code);
192 if (code == mOutputCode.end())
193 {
194 return std::string::npos;
195 }
196
197 // No meaningful check for binary blobs
198 if (IsBinaryBlob(code->second))
199 {
200 return true;
201 }
202
203 return code->second.find(stringToFind) != std::string::npos;
204 }
205
foundInCodeInOrder(ShShaderOutput output,std::vector<const char * > stringsToFind)206 bool MatchOutputCodeTest::foundInCodeInOrder(ShShaderOutput output,
207 std::vector<const char *> stringsToFind)
208 {
209 const auto code = mOutputCode.find(output);
210 EXPECT_NE(mOutputCode.end(), code);
211 if (code == mOutputCode.end())
212 {
213 return false;
214 }
215
216 // No meaningful check for binary blobs
217 if (IsBinaryBlob(code->second))
218 {
219 return true;
220 }
221
222 size_t currentPos = 0;
223 for (const char *stringToFind : stringsToFind)
224 {
225 auto position = code->second.find(stringToFind, currentPos);
226 if (position == std::string::npos)
227 {
228 return false;
229 }
230 currentPos = position + strlen(stringToFind);
231 }
232 return true;
233 }
234
foundInCode(ShShaderOutput output,const char * stringToFind,const int expectedOccurrences) const235 bool MatchOutputCodeTest::foundInCode(ShShaderOutput output,
236 const char *stringToFind,
237 const int expectedOccurrences) const
238 {
239 const auto code = mOutputCode.find(output);
240 EXPECT_NE(mOutputCode.end(), code);
241 if (code == mOutputCode.end())
242 {
243 return false;
244 }
245
246 // No meaningful check for binary blobs
247 if (IsBinaryBlob(code->second))
248 {
249 return true;
250 }
251
252 size_t currentPos = 0;
253 int occurencesLeft = expectedOccurrences;
254
255 const size_t searchStringLength = strlen(stringToFind);
256
257 while (occurencesLeft-- > 0)
258 {
259 auto position = code->second.find(stringToFind, currentPos);
260 if (position == std::string::npos)
261 {
262 return false;
263 }
264 // Search strings should not overlap.
265 currentPos = position + searchStringLength;
266 }
267 // Make sure that there aren't extra occurrences.
268 return code->second.find(stringToFind, currentPos) == std::string::npos;
269 }
270
foundInCode(const char * stringToFind) const271 bool MatchOutputCodeTest::foundInCode(const char *stringToFind) const
272 {
273 for (auto &code : mOutputCode)
274 {
275 if (!foundInCode(code.first, stringToFind))
276 {
277 return false;
278 }
279 }
280 return true;
281 }
282
foundInCodeRegex(const std::regex & regexToFind,std::smatch * match) const283 bool MatchOutputCodeTest::foundInCodeRegex(const std::regex ®exToFind, std::smatch *match) const
284 {
285 for (auto &code : mOutputCode)
286 {
287 if (!foundInCodeRegex(code.first, regexToFind, match))
288 {
289 return false;
290 }
291 }
292 return true;
293 }
294
foundInCode(const char * stringToFind,const int expectedOccurrences) const295 bool MatchOutputCodeTest::foundInCode(const char *stringToFind, const int expectedOccurrences) const
296 {
297 for (auto &code : mOutputCode)
298 {
299 if (!foundInCode(code.first, stringToFind, expectedOccurrences))
300 {
301 return false;
302 }
303 }
304 return true;
305 }
306
foundInCodeInOrder(std::vector<const char * > stringsToFind)307 bool MatchOutputCodeTest::foundInCodeInOrder(std::vector<const char *> stringsToFind)
308 {
309 for (auto &code : mOutputCode)
310 {
311 if (!foundInCodeInOrder(code.first, stringsToFind))
312 {
313 return false;
314 }
315 }
316 return true;
317 }
318
notFoundInCode(const char * stringToFind) const319 bool MatchOutputCodeTest::notFoundInCode(const char *stringToFind) const
320 {
321 for (auto &code : mOutputCode)
322 {
323 // No meaningful check for binary blobs
324 if (IsBinaryBlob(code.second))
325 {
326 continue;
327 }
328
329 if (foundInCode(code.first, stringToFind))
330 {
331 return false;
332 }
333 }
334 return true;
335 }
336
FindFunctionCallNode(TIntermNode * root,const TString & functionMangledName)337 const TIntermAggregate *FindFunctionCallNode(TIntermNode *root, const TString &functionMangledName)
338 {
339 FunctionCallFinder finder(functionMangledName.c_str());
340 root->traverse(&finder);
341 return finder.getNode();
342 }
343
344 } // namespace sh
345