blob: 5301b04f40c3e5cdcb25df08a7903ba28e6682c2 [file] [log] [blame]
Corentin Wallezf4eab3b2015-03-18 12:55:45 -07001//
2// Copyright (c) 2002-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
7// Analysis of the AST needed for HLSL generation
8
9#include "compiler/translator/ASTMetadataHLSL.h"
10
11#include "compiler/translator/CallDAG.h"
12#include "compiler/translator/SymbolTable.h"
13
Jamie Madill45bcc782016-11-07 13:58:48 -050014namespace sh
15{
16
Corentin Wallezf4eab3b2015-03-18 12:55:45 -070017namespace
18{
19
20// Class used to traverse the AST of a function definition, checking if the
21// function uses a gradient, and writing the set of control flow using gradients.
22// It assumes that the analysis has already been made for the function's
23// callees.
24class PullGradient : public TIntermTraverser
25{
26 public:
27 PullGradient(MetadataList *metadataList, size_t index, const CallDAG &dag)
28 : TIntermTraverser(true, false, true),
29 mMetadataList(metadataList),
30 mMetadata(&(*metadataList)[index]),
31 mIndex(index),
32 mDag(dag)
33 {
34 ASSERT(index < metadataList->size());
35 }
36
Olli Etuaho336b1472016-10-05 16:37:55 +010037 void traverse(TIntermFunctionDefinition *node)
Corentin Wallezf4eab3b2015-03-18 12:55:45 -070038 {
39 node->traverse(this);
40 ASSERT(mParents.empty());
41 }
42
43 // Called when a gradient operation or a call to a function using a gradient is found.
44 void onGradient()
45 {
46 mMetadata->mUsesGradient = true;
47 // Mark the latest control flow as using a gradient.
48 if (!mParents.empty())
49 {
50 mMetadata->mControlFlowsContainingGradient.insert(mParents.back());
51 }
52 }
53
54 void visitControlFlow(Visit visit, TIntermNode *node)
55 {
56 if (visit == PreVisit)
57 {
58 mParents.push_back(node);
59 }
60 else if (visit == PostVisit)
61 {
62 ASSERT(mParents.back() == node);
63 mParents.pop_back();
64 // A control flow's using a gradient means its parents are too.
Jamie Madilld7b1ab52016-12-12 14:42:19 -050065 if (mMetadata->mControlFlowsContainingGradient.count(node) > 0 && !mParents.empty())
Corentin Wallezf4eab3b2015-03-18 12:55:45 -070066 {
67 mMetadata->mControlFlowsContainingGradient.insert(mParents.back());
68 }
69 }
70 }
71
Austin Kinross0998fe92015-12-11 11:31:38 -080072 bool visitLoop(Visit visit, TIntermLoop *loop) override
Corentin Wallezf4eab3b2015-03-18 12:55:45 -070073 {
74 visitControlFlow(visit, loop);
75 return true;
76 }
77
Olli Etuaho57961272016-09-14 13:57:46 +030078 bool visitIfElse(Visit visit, TIntermIfElse *ifElse) override
Corentin Wallezf4eab3b2015-03-18 12:55:45 -070079 {
Olli Etuaho57961272016-09-14 13:57:46 +030080 visitControlFlow(visit, ifElse);
Corentin Wallezf4eab3b2015-03-18 12:55:45 -070081 return true;
82 }
83
84 bool visitUnary(Visit visit, TIntermUnary *node) override
85 {
86 if (visit == PreVisit)
87 {
88 switch (node->getOp())
89 {
Jamie Madilld7b1ab52016-12-12 14:42:19 -050090 case EOpDFdx:
91 case EOpDFdy:
92 onGradient();
93 default:
94 break;
Corentin Wallezf4eab3b2015-03-18 12:55:45 -070095 }
96 }
97
98 return true;
99 }
100
101 bool visitAggregate(Visit visit, TIntermAggregate *node) override
102 {
103 if (visit == PreVisit)
104 {
105 if (node->getOp() == EOpFunctionCall)
106 {
107 if (node->isUserDefined())
108 {
Olli Etuahobd674552016-10-06 13:28:42 +0100109 size_t calleeIndex = mDag.findIndex(node->getFunctionSymbolInfo());
Corentin Wallezf4eab3b2015-03-18 12:55:45 -0700110 ASSERT(calleeIndex != CallDAG::InvalidIndex && calleeIndex < mIndex);
111
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500112 if ((*mMetadataList)[calleeIndex].mUsesGradient)
113 {
Corentin Wallezf4eab3b2015-03-18 12:55:45 -0700114 onGradient();
115 }
116 }
117 else
118 {
Olli Etuahobd674552016-10-06 13:28:42 +0100119 TString name =
120 TFunction::unmangleName(node->getFunctionSymbolInfo()->getName());
Corentin Wallezf4eab3b2015-03-18 12:55:45 -0700121
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500122 if (name == "texture2D" || name == "texture2DProj" || name == "textureCube")
Corentin Wallezf4eab3b2015-03-18 12:55:45 -0700123 {
124 onGradient();
125 }
126 }
127 }
128 }
129
130 return true;
131 }
132
133 private:
134 MetadataList *mMetadataList;
135 ASTMetadataHLSL *mMetadata;
136 size_t mIndex;
137 const CallDAG &mDag;
138
139 // Contains a stack of the control flow nodes that are parents of the node being
140 // currently visited. It is used to mark control flows using a gradient.
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500141 std::vector<TIntermNode *> mParents;
Corentin Wallezf4eab3b2015-03-18 12:55:45 -0700142};
143
Corentin Wallez477b2432015-08-31 10:41:16 -0700144// Traverses the AST of a function definition to compute the the discontinuous loops
145// and the if statements containing gradient loops. It assumes that the gradient loops
146// (loops that contain a gradient) have already been computed and that it has already
147// traversed the current function's callees.
148class PullComputeDiscontinuousAndGradientLoops : public TIntermTraverser
Corentin Wallez50931452015-03-19 10:39:13 -0700149{
150 public:
Corentin Wallez477b2432015-08-31 10:41:16 -0700151 PullComputeDiscontinuousAndGradientLoops(MetadataList *metadataList,
152 size_t index,
153 const CallDAG &dag)
Corentin Wallez50931452015-03-19 10:39:13 -0700154 : TIntermTraverser(true, false, true),
155 mMetadataList(metadataList),
156 mMetadata(&(*metadataList)[index]),
157 mIndex(index),
158 mDag(dag)
159 {
160 }
161
Olli Etuaho336b1472016-10-05 16:37:55 +0100162 void traverse(TIntermFunctionDefinition *node)
Corentin Wallez50931452015-03-19 10:39:13 -0700163 {
164 node->traverse(this);
Olli Etuaho3873cd02015-04-10 15:00:55 +0300165 ASSERT(mLoopsAndSwitches.empty());
Corentin Wallez50931452015-03-19 10:39:13 -0700166 ASSERT(mIfs.empty());
167 }
168
Corentin Wallez477b2432015-08-31 10:41:16 -0700169 // Called when traversing a gradient loop or a call to a function with a
170 // gradient loop in its call graph.
171 void onGradientLoop()
Corentin Wallez50931452015-03-19 10:39:13 -0700172 {
Corentin Wallez477b2432015-08-31 10:41:16 -0700173 mMetadata->mHasGradientLoopInCallGraph = true;
Corentin Wallez50931452015-03-19 10:39:13 -0700174 // Mark the latest if as using a discontinuous loop.
175 if (!mIfs.empty())
176 {
Corentin Wallez477b2432015-08-31 10:41:16 -0700177 mMetadata->mIfsContainingGradientLoop.insert(mIfs.back());
Corentin Wallez50931452015-03-19 10:39:13 -0700178 }
179 }
180
Olli Etuaho3873cd02015-04-10 15:00:55 +0300181 bool visitLoop(Visit visit, TIntermLoop *loop) override
Corentin Wallez50931452015-03-19 10:39:13 -0700182 {
183 if (visit == PreVisit)
184 {
Olli Etuaho3873cd02015-04-10 15:00:55 +0300185 mLoopsAndSwitches.push_back(loop);
Corentin Wallez477b2432015-08-31 10:41:16 -0700186
187 if (mMetadata->hasGradientInCallGraph(loop))
188 {
189 onGradientLoop();
190 }
Corentin Wallez50931452015-03-19 10:39:13 -0700191 }
192 else if (visit == PostVisit)
193 {
Olli Etuaho3873cd02015-04-10 15:00:55 +0300194 ASSERT(mLoopsAndSwitches.back() == loop);
195 mLoopsAndSwitches.pop_back();
Corentin Wallez50931452015-03-19 10:39:13 -0700196 }
197
198 return true;
199 }
200
Olli Etuaho57961272016-09-14 13:57:46 +0300201 bool visitIfElse(Visit visit, TIntermIfElse *node) override
Corentin Wallez50931452015-03-19 10:39:13 -0700202 {
203 if (visit == PreVisit)
204 {
205 mIfs.push_back(node);
206 }
207 else if (visit == PostVisit)
208 {
209 ASSERT(mIfs.back() == node);
210 mIfs.pop_back();
211 // An if using a discontinuous loop means its parents ifs are also discontinuous.
Corentin Wallez477b2432015-08-31 10:41:16 -0700212 if (mMetadata->mIfsContainingGradientLoop.count(node) > 0 && !mIfs.empty())
Corentin Wallez50931452015-03-19 10:39:13 -0700213 {
Corentin Wallez477b2432015-08-31 10:41:16 -0700214 mMetadata->mIfsContainingGradientLoop.insert(mIfs.back());
Corentin Wallez50931452015-03-19 10:39:13 -0700215 }
216 }
217
218 return true;
219 }
220
Olli Etuaho3873cd02015-04-10 15:00:55 +0300221 bool visitBranch(Visit visit, TIntermBranch *node) override
Corentin Wallez50931452015-03-19 10:39:13 -0700222 {
223 if (visit == PreVisit)
224 {
225 switch (node->getFlowOp())
226 {
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500227 case EOpBreak:
Olli Etuaho3873cd02015-04-10 15:00:55 +0300228 {
229 ASSERT(!mLoopsAndSwitches.empty());
230 TIntermLoop *loop = mLoopsAndSwitches.back()->getAsLoopNode();
231 if (loop != nullptr)
232 {
233 mMetadata->mDiscontinuousLoops.insert(loop);
Olli Etuaho3873cd02015-04-10 15:00:55 +0300234 }
235 }
236 break;
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500237 case EOpContinue:
Olli Etuaho3873cd02015-04-10 15:00:55 +0300238 {
239 ASSERT(!mLoopsAndSwitches.empty());
240 TIntermLoop *loop = nullptr;
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500241 size_t i = mLoopsAndSwitches.size();
Olli Etuaho3873cd02015-04-10 15:00:55 +0300242 while (loop == nullptr && i > 0)
243 {
244 --i;
245 loop = mLoopsAndSwitches.at(i)->getAsLoopNode();
246 }
247 ASSERT(loop != nullptr);
248 mMetadata->mDiscontinuousLoops.insert(loop);
Olli Etuaho3873cd02015-04-10 15:00:55 +0300249 }
Corentin Wallez50931452015-03-19 10:39:13 -0700250 break;
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500251 case EOpKill:
252 case EOpReturn:
253 // A return or discard jumps out of all the enclosing loops
254 if (!mLoopsAndSwitches.empty())
Corentin Wallez50931452015-03-19 10:39:13 -0700255 {
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500256 for (TIntermNode *intermNode : mLoopsAndSwitches)
Olli Etuaho3873cd02015-04-10 15:00:55 +0300257 {
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500258 TIntermLoop *loop = intermNode->getAsLoopNode();
259 if (loop)
260 {
261 mMetadata->mDiscontinuousLoops.insert(loop);
262 }
Olli Etuaho3873cd02015-04-10 15:00:55 +0300263 }
Corentin Wallez50931452015-03-19 10:39:13 -0700264 }
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500265 break;
266 default:
267 UNREACHABLE();
Corentin Wallez50931452015-03-19 10:39:13 -0700268 }
269 }
270
271 return true;
272 }
273
274 bool visitAggregate(Visit visit, TIntermAggregate *node) override
275 {
276 if (visit == PreVisit && node->getOp() == EOpFunctionCall)
277 {
278 if (node->isUserDefined())
279 {
Olli Etuahobd674552016-10-06 13:28:42 +0100280 size_t calleeIndex = mDag.findIndex(node->getFunctionSymbolInfo());
Corentin Wallez50931452015-03-19 10:39:13 -0700281 ASSERT(calleeIndex != CallDAG::InvalidIndex && calleeIndex < mIndex);
282
Corentin Wallez477b2432015-08-31 10:41:16 -0700283 if ((*mMetadataList)[calleeIndex].mHasGradientLoopInCallGraph)
Corentin Wallez50931452015-03-19 10:39:13 -0700284 {
Corentin Wallez477b2432015-08-31 10:41:16 -0700285 onGradientLoop();
Corentin Wallez50931452015-03-19 10:39:13 -0700286 }
287 }
288 }
289
290 return true;
291 }
292
Olli Etuaho3873cd02015-04-10 15:00:55 +0300293 bool visitSwitch(Visit visit, TIntermSwitch *node) override
294 {
295 if (visit == PreVisit)
296 {
297 mLoopsAndSwitches.push_back(node);
298 }
299 else if (visit == PostVisit)
300 {
301 ASSERT(mLoopsAndSwitches.back() == node);
302 mLoopsAndSwitches.pop_back();
303 }
304 return true;
305 }
306
Corentin Wallez50931452015-03-19 10:39:13 -0700307 private:
308 MetadataList *mMetadataList;
309 ASTMetadataHLSL *mMetadata;
310 size_t mIndex;
311 const CallDAG &mDag;
312
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500313 std::vector<TIntermNode *> mLoopsAndSwitches;
Olli Etuaho57961272016-09-14 13:57:46 +0300314 std::vector<TIntermIfElse *> mIfs;
Corentin Wallez50931452015-03-19 10:39:13 -0700315};
316
317// Tags all the functions called in a discontinuous loop
318class PushDiscontinuousLoops : public TIntermTraverser
319{
320 public:
321 PushDiscontinuousLoops(MetadataList *metadataList, size_t index, const CallDAG &dag)
322 : TIntermTraverser(true, true, true),
323 mMetadataList(metadataList),
324 mMetadata(&(*metadataList)[index]),
325 mIndex(index),
326 mDag(dag),
327 mNestedDiscont(mMetadata->mCalledInDiscontinuousLoop ? 1 : 0)
328 {
329 }
330
Olli Etuaho336b1472016-10-05 16:37:55 +0100331 void traverse(TIntermFunctionDefinition *node)
Corentin Wallez50931452015-03-19 10:39:13 -0700332 {
333 node->traverse(this);
334 ASSERT(mNestedDiscont == (mMetadata->mCalledInDiscontinuousLoop ? 1 : 0));
335 }
336
Austin Kinross0998fe92015-12-11 11:31:38 -0800337 bool visitLoop(Visit visit, TIntermLoop *loop) override
Corentin Wallez50931452015-03-19 10:39:13 -0700338 {
339 bool isDiscontinuous = mMetadata->mDiscontinuousLoops.count(loop) > 0;
340
341 if (visit == PreVisit && isDiscontinuous)
342 {
343 mNestedDiscont++;
344 }
345 else if (visit == PostVisit && isDiscontinuous)
346 {
347 mNestedDiscont--;
348 }
349
350 return true;
351 }
352
353 bool visitAggregate(Visit visit, TIntermAggregate *node) override
354 {
355 switch (node->getOp())
356 {
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500357 case EOpFunctionCall:
358 if (visit == PreVisit && node->isUserDefined() && mNestedDiscont > 0)
359 {
360 size_t calleeIndex = mDag.findIndex(node->getFunctionSymbolInfo());
361 ASSERT(calleeIndex != CallDAG::InvalidIndex && calleeIndex < mIndex);
Corentin Wallez50931452015-03-19 10:39:13 -0700362
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500363 (*mMetadataList)[calleeIndex].mCalledInDiscontinuousLoop = true;
364 }
365 break;
366 default:
367 break;
Corentin Wallez50931452015-03-19 10:39:13 -0700368 }
369 return true;
370 }
371
372 private:
373 MetadataList *mMetadataList;
374 ASTMetadataHLSL *mMetadata;
375 size_t mIndex;
376 const CallDAG &mDag;
377
378 int mNestedDiscont;
379};
Corentin Wallezf4eab3b2015-03-18 12:55:45 -0700380}
381
Corentin Wallezf4eab3b2015-03-18 12:55:45 -0700382bool ASTMetadataHLSL::hasGradientInCallGraph(TIntermLoop *node)
383{
384 return mControlFlowsContainingGradient.count(node) > 0;
385}
386
Olli Etuaho57961272016-09-14 13:57:46 +0300387bool ASTMetadataHLSL::hasGradientLoop(TIntermIfElse *node)
Corentin Wallez50931452015-03-19 10:39:13 -0700388{
Corentin Wallez477b2432015-08-31 10:41:16 -0700389 return mIfsContainingGradientLoop.count(node) > 0;
Corentin Wallez50931452015-03-19 10:39:13 -0700390}
391
Corentin Wallezf4eab3b2015-03-18 12:55:45 -0700392MetadataList CreateASTMetadataHLSL(TIntermNode *root, const CallDAG &callDag)
393{
394 MetadataList metadataList(callDag.size());
395
396 // Compute all the information related to when gradient operations are used.
397 // We want to know for each function and control flow operation if they have
398 // a gradient operation in their call graph (shortened to "using a gradient"
399 // in the rest of the file).
400 //
401 // This computation is logically split in three steps:
402 // 1 - For each function compute if it uses a gradient in its body, ignoring
403 // calls to other user-defined functions.
404 // 2 - For each function determine if it uses a gradient in its call graph,
405 // using the result of step 1 and the CallDAG to know its callees.
406 // 3 - For each control flow statement of each function, check if it uses a
407 // gradient in the function's body, or if it calls a user-defined function that
408 // uses a gradient.
409 //
410 // We take advantage of the call graph being a DAG and instead compute 1, 2 and 3
411 // for leaves first, then going down the tree. This is correct because 1 doesn't
412 // depend on other functions, and 2 and 3 depend only on callees.
413 for (size_t i = 0; i < callDag.size(); i++)
414 {
415 PullGradient pull(&metadataList, i, callDag);
416 pull.traverse(callDag.getRecordFromIndex(i).node);
417 }
418
Corentin Wallez50931452015-03-19 10:39:13 -0700419 // Compute which loops are discontinuous and which function are called in
420 // these loops. The same way computing gradient usage is a "pull" process,
421 // computing "bing used in a discont. loop" is a push process. However we also
422 // need to know what ifs have a discontinuous loop inside so we do the same type
423 // of callgraph analysis as for the gradient.
424
425 // First compute which loops are discontinuous (no specific order) and pull
Corentin Wallez477b2432015-08-31 10:41:16 -0700426 // the ifs and functions using a gradient loop.
Corentin Wallez50931452015-03-19 10:39:13 -0700427 for (size_t i = 0; i < callDag.size(); i++)
428 {
Corentin Wallez477b2432015-08-31 10:41:16 -0700429 PullComputeDiscontinuousAndGradientLoops pull(&metadataList, i, callDag);
Corentin Wallez50931452015-03-19 10:39:13 -0700430 pull.traverse(callDag.getRecordFromIndex(i).node);
431 }
432
433 // Then push the information to callees, either from the a local discontinuous
434 // loop or from the caller being called in a discontinuous loop already
435 for (size_t i = callDag.size(); i-- > 0;)
436 {
437 PushDiscontinuousLoops push(&metadataList, i, callDag);
438 push.traverse(callDag.getRecordFromIndex(i).node);
439 }
440
441 // We create "Lod0" version of functions with the gradient operations replaced
442 // by non-gradient operations so that the D3D compiler is happier with discont
443 // loops.
444 for (auto &metadata : metadataList)
445 {
446 metadata.mNeedsLod0 = metadata.mCalledInDiscontinuousLoop && metadata.mUsesGradient;
447 }
448
Corentin Wallezf4eab3b2015-03-18 12:55:45 -0700449 return metadataList;
450}
Jamie Madill45bcc782016-11-07 13:58:48 -0500451
452} // namespace sh