blob: ba991b70972fd42cdb01773d360bd0cc5ebb1617 [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.
65 if (mMetadata->mControlFlowsContainingGradient.count(node)> 0 && !mParents.empty())
66 {
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 {
90 case EOpDFdx:
91 case EOpDFdy:
92 onGradient();
93 default:
94 break;
95 }
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
112 if ((*mMetadataList)[calleeIndex].mUsesGradient) {
113 onGradient();
114 }
115 }
116 else
117 {
Olli Etuahobd674552016-10-06 13:28:42 +0100118 TString name =
119 TFunction::unmangleName(node->getFunctionSymbolInfo()->getName());
Corentin Wallezf4eab3b2015-03-18 12:55:45 -0700120
121 if (name == "texture2D" ||
122 name == "texture2DProj" ||
123 name == "textureCube")
124 {
125 onGradient();
126 }
127 }
128 }
129 }
130
131 return true;
132 }
133
134 private:
135 MetadataList *mMetadataList;
136 ASTMetadataHLSL *mMetadata;
137 size_t mIndex;
138 const CallDAG &mDag;
139
140 // Contains a stack of the control flow nodes that are parents of the node being
141 // currently visited. It is used to mark control flows using a gradient.
142 std::vector<TIntermNode*> mParents;
143};
144
Corentin Wallez477b2432015-08-31 10:41:16 -0700145// Traverses the AST of a function definition to compute the the discontinuous loops
146// and the if statements containing gradient loops. It assumes that the gradient loops
147// (loops that contain a gradient) have already been computed and that it has already
148// traversed the current function's callees.
149class PullComputeDiscontinuousAndGradientLoops : public TIntermTraverser
Corentin Wallez50931452015-03-19 10:39:13 -0700150{
151 public:
Corentin Wallez477b2432015-08-31 10:41:16 -0700152 PullComputeDiscontinuousAndGradientLoops(MetadataList *metadataList,
153 size_t index,
154 const CallDAG &dag)
Corentin Wallez50931452015-03-19 10:39:13 -0700155 : TIntermTraverser(true, false, true),
156 mMetadataList(metadataList),
157 mMetadata(&(*metadataList)[index]),
158 mIndex(index),
159 mDag(dag)
160 {
161 }
162
Olli Etuaho336b1472016-10-05 16:37:55 +0100163 void traverse(TIntermFunctionDefinition *node)
Corentin Wallez50931452015-03-19 10:39:13 -0700164 {
165 node->traverse(this);
Olli Etuaho3873cd02015-04-10 15:00:55 +0300166 ASSERT(mLoopsAndSwitches.empty());
Corentin Wallez50931452015-03-19 10:39:13 -0700167 ASSERT(mIfs.empty());
168 }
169
Corentin Wallez477b2432015-08-31 10:41:16 -0700170 // Called when traversing a gradient loop or a call to a function with a
171 // gradient loop in its call graph.
172 void onGradientLoop()
Corentin Wallez50931452015-03-19 10:39:13 -0700173 {
Corentin Wallez477b2432015-08-31 10:41:16 -0700174 mMetadata->mHasGradientLoopInCallGraph = true;
Corentin Wallez50931452015-03-19 10:39:13 -0700175 // Mark the latest if as using a discontinuous loop.
176 if (!mIfs.empty())
177 {
Corentin Wallez477b2432015-08-31 10:41:16 -0700178 mMetadata->mIfsContainingGradientLoop.insert(mIfs.back());
Corentin Wallez50931452015-03-19 10:39:13 -0700179 }
180 }
181
Olli Etuaho3873cd02015-04-10 15:00:55 +0300182 bool visitLoop(Visit visit, TIntermLoop *loop) override
Corentin Wallez50931452015-03-19 10:39:13 -0700183 {
184 if (visit == PreVisit)
185 {
Olli Etuaho3873cd02015-04-10 15:00:55 +0300186 mLoopsAndSwitches.push_back(loop);
Corentin Wallez477b2432015-08-31 10:41:16 -0700187
188 if (mMetadata->hasGradientInCallGraph(loop))
189 {
190 onGradientLoop();
191 }
Corentin Wallez50931452015-03-19 10:39:13 -0700192 }
193 else if (visit == PostVisit)
194 {
Olli Etuaho3873cd02015-04-10 15:00:55 +0300195 ASSERT(mLoopsAndSwitches.back() == loop);
196 mLoopsAndSwitches.pop_back();
Corentin Wallez50931452015-03-19 10:39:13 -0700197 }
198
199 return true;
200 }
201
Olli Etuaho57961272016-09-14 13:57:46 +0300202 bool visitIfElse(Visit visit, TIntermIfElse *node) override
Corentin Wallez50931452015-03-19 10:39:13 -0700203 {
204 if (visit == PreVisit)
205 {
206 mIfs.push_back(node);
207 }
208 else if (visit == PostVisit)
209 {
210 ASSERT(mIfs.back() == node);
211 mIfs.pop_back();
212 // An if using a discontinuous loop means its parents ifs are also discontinuous.
Corentin Wallez477b2432015-08-31 10:41:16 -0700213 if (mMetadata->mIfsContainingGradientLoop.count(node) > 0 && !mIfs.empty())
Corentin Wallez50931452015-03-19 10:39:13 -0700214 {
Corentin Wallez477b2432015-08-31 10:41:16 -0700215 mMetadata->mIfsContainingGradientLoop.insert(mIfs.back());
Corentin Wallez50931452015-03-19 10:39:13 -0700216 }
217 }
218
219 return true;
220 }
221
Olli Etuaho3873cd02015-04-10 15:00:55 +0300222 bool visitBranch(Visit visit, TIntermBranch *node) override
Corentin Wallez50931452015-03-19 10:39:13 -0700223 {
224 if (visit == PreVisit)
225 {
226 switch (node->getFlowOp())
227 {
Corentin Wallez50931452015-03-19 10:39:13 -0700228 case EOpBreak:
Olli Etuaho3873cd02015-04-10 15:00:55 +0300229 {
230 ASSERT(!mLoopsAndSwitches.empty());
231 TIntermLoop *loop = mLoopsAndSwitches.back()->getAsLoopNode();
232 if (loop != nullptr)
233 {
234 mMetadata->mDiscontinuousLoops.insert(loop);
Olli Etuaho3873cd02015-04-10 15:00:55 +0300235 }
236 }
237 break;
Corentin Wallez50931452015-03-19 10:39:13 -0700238 case EOpContinue:
Olli Etuaho3873cd02015-04-10 15:00:55 +0300239 {
240 ASSERT(!mLoopsAndSwitches.empty());
241 TIntermLoop *loop = nullptr;
242 size_t i = mLoopsAndSwitches.size();
243 while (loop == nullptr && i > 0)
244 {
245 --i;
246 loop = mLoopsAndSwitches.at(i)->getAsLoopNode();
247 }
248 ASSERT(loop != nullptr);
249 mMetadata->mDiscontinuousLoops.insert(loop);
Olli Etuaho3873cd02015-04-10 15:00:55 +0300250 }
Corentin Wallez50931452015-03-19 10:39:13 -0700251 break;
Corentin Walleza1884f22015-04-29 10:15:16 -0700252 case EOpKill:
Corentin Wallez50931452015-03-19 10:39:13 -0700253 case EOpReturn:
Corentin Walleza1884f22015-04-29 10:15:16 -0700254 // A return or discard jumps out of all the enclosing loops
Olli Etuaho3873cd02015-04-10 15:00:55 +0300255 if (!mLoopsAndSwitches.empty())
Corentin Wallez50931452015-03-19 10:39:13 -0700256 {
Cooper Partin4f488492015-08-07 16:05:25 -0700257 for (TIntermNode *intermNode : mLoopsAndSwitches)
Corentin Wallez50931452015-03-19 10:39:13 -0700258 {
Cooper Partin4f488492015-08-07 16:05:25 -0700259 TIntermLoop *loop = intermNode->getAsLoopNode();
Olli Etuaho3873cd02015-04-10 15:00:55 +0300260 if (loop)
261 {
262 mMetadata->mDiscontinuousLoops.insert(loop);
263 }
Corentin Wallez50931452015-03-19 10:39:13 -0700264 }
Corentin Wallez50931452015-03-19 10:39:13 -0700265 }
266 break;
267 default:
268 UNREACHABLE();
269 }
270 }
271
272 return true;
273 }
274
275 bool visitAggregate(Visit visit, TIntermAggregate *node) override
276 {
277 if (visit == PreVisit && node->getOp() == EOpFunctionCall)
278 {
279 if (node->isUserDefined())
280 {
Olli Etuahobd674552016-10-06 13:28:42 +0100281 size_t calleeIndex = mDag.findIndex(node->getFunctionSymbolInfo());
Corentin Wallez50931452015-03-19 10:39:13 -0700282 ASSERT(calleeIndex != CallDAG::InvalidIndex && calleeIndex < mIndex);
283
Corentin Wallez477b2432015-08-31 10:41:16 -0700284 if ((*mMetadataList)[calleeIndex].mHasGradientLoopInCallGraph)
Corentin Wallez50931452015-03-19 10:39:13 -0700285 {
Corentin Wallez477b2432015-08-31 10:41:16 -0700286 onGradientLoop();
Corentin Wallez50931452015-03-19 10:39:13 -0700287 }
288 }
289 }
290
291 return true;
292 }
293
Olli Etuaho3873cd02015-04-10 15:00:55 +0300294 bool visitSwitch(Visit visit, TIntermSwitch *node) override
295 {
296 if (visit == PreVisit)
297 {
298 mLoopsAndSwitches.push_back(node);
299 }
300 else if (visit == PostVisit)
301 {
302 ASSERT(mLoopsAndSwitches.back() == node);
303 mLoopsAndSwitches.pop_back();
304 }
305 return true;
306 }
307
Corentin Wallez50931452015-03-19 10:39:13 -0700308 private:
309 MetadataList *mMetadataList;
310 ASTMetadataHLSL *mMetadata;
311 size_t mIndex;
312 const CallDAG &mDag;
313
Olli Etuaho3873cd02015-04-10 15:00:55 +0300314 std::vector<TIntermNode*> mLoopsAndSwitches;
Olli Etuaho57961272016-09-14 13:57:46 +0300315 std::vector<TIntermIfElse *> mIfs;
Corentin Wallez50931452015-03-19 10:39:13 -0700316};
317
318// Tags all the functions called in a discontinuous loop
319class PushDiscontinuousLoops : public TIntermTraverser
320{
321 public:
322 PushDiscontinuousLoops(MetadataList *metadataList, size_t index, const CallDAG &dag)
323 : TIntermTraverser(true, true, true),
324 mMetadataList(metadataList),
325 mMetadata(&(*metadataList)[index]),
326 mIndex(index),
327 mDag(dag),
328 mNestedDiscont(mMetadata->mCalledInDiscontinuousLoop ? 1 : 0)
329 {
330 }
331
Olli Etuaho336b1472016-10-05 16:37:55 +0100332 void traverse(TIntermFunctionDefinition *node)
Corentin Wallez50931452015-03-19 10:39:13 -0700333 {
334 node->traverse(this);
335 ASSERT(mNestedDiscont == (mMetadata->mCalledInDiscontinuousLoop ? 1 : 0));
336 }
337
Austin Kinross0998fe92015-12-11 11:31:38 -0800338 bool visitLoop(Visit visit, TIntermLoop *loop) override
Corentin Wallez50931452015-03-19 10:39:13 -0700339 {
340 bool isDiscontinuous = mMetadata->mDiscontinuousLoops.count(loop) > 0;
341
342 if (visit == PreVisit && isDiscontinuous)
343 {
344 mNestedDiscont++;
345 }
346 else if (visit == PostVisit && isDiscontinuous)
347 {
348 mNestedDiscont--;
349 }
350
351 return true;
352 }
353
354 bool visitAggregate(Visit visit, TIntermAggregate *node) override
355 {
356 switch (node->getOp())
357 {
358 case EOpFunctionCall:
359 if (visit == PreVisit && node->isUserDefined() && mNestedDiscont > 0)
360 {
Olli Etuahobd674552016-10-06 13:28:42 +0100361 size_t calleeIndex = mDag.findIndex(node->getFunctionSymbolInfo());
Corentin Wallez50931452015-03-19 10:39:13 -0700362 ASSERT(calleeIndex != CallDAG::InvalidIndex && calleeIndex < mIndex);
363
364 (*mMetadataList)[calleeIndex].mCalledInDiscontinuousLoop = true;
365 }
366 break;
367 default:
368 break;
369 }
370 return true;
371 }
372
373 private:
374 MetadataList *mMetadataList;
375 ASTMetadataHLSL *mMetadata;
376 size_t mIndex;
377 const CallDAG &mDag;
378
379 int mNestedDiscont;
380};
381
Corentin Wallezf4eab3b2015-03-18 12:55:45 -0700382}
383
Corentin Wallezf4eab3b2015-03-18 12:55:45 -0700384bool ASTMetadataHLSL::hasGradientInCallGraph(TIntermLoop *node)
385{
386 return mControlFlowsContainingGradient.count(node) > 0;
387}
388
Olli Etuaho57961272016-09-14 13:57:46 +0300389bool ASTMetadataHLSL::hasGradientLoop(TIntermIfElse *node)
Corentin Wallez50931452015-03-19 10:39:13 -0700390{
Corentin Wallez477b2432015-08-31 10:41:16 -0700391 return mIfsContainingGradientLoop.count(node) > 0;
Corentin Wallez50931452015-03-19 10:39:13 -0700392}
393
Corentin Wallezf4eab3b2015-03-18 12:55:45 -0700394MetadataList CreateASTMetadataHLSL(TIntermNode *root, const CallDAG &callDag)
395{
396 MetadataList metadataList(callDag.size());
397
398 // Compute all the information related to when gradient operations are used.
399 // We want to know for each function and control flow operation if they have
400 // a gradient operation in their call graph (shortened to "using a gradient"
401 // in the rest of the file).
402 //
403 // This computation is logically split in three steps:
404 // 1 - For each function compute if it uses a gradient in its body, ignoring
405 // calls to other user-defined functions.
406 // 2 - For each function determine if it uses a gradient in its call graph,
407 // using the result of step 1 and the CallDAG to know its callees.
408 // 3 - For each control flow statement of each function, check if it uses a
409 // gradient in the function's body, or if it calls a user-defined function that
410 // uses a gradient.
411 //
412 // We take advantage of the call graph being a DAG and instead compute 1, 2 and 3
413 // for leaves first, then going down the tree. This is correct because 1 doesn't
414 // depend on other functions, and 2 and 3 depend only on callees.
415 for (size_t i = 0; i < callDag.size(); i++)
416 {
417 PullGradient pull(&metadataList, i, callDag);
418 pull.traverse(callDag.getRecordFromIndex(i).node);
419 }
420
Corentin Wallez50931452015-03-19 10:39:13 -0700421 // Compute which loops are discontinuous and which function are called in
422 // these loops. The same way computing gradient usage is a "pull" process,
423 // computing "bing used in a discont. loop" is a push process. However we also
424 // need to know what ifs have a discontinuous loop inside so we do the same type
425 // of callgraph analysis as for the gradient.
426
427 // First compute which loops are discontinuous (no specific order) and pull
Corentin Wallez477b2432015-08-31 10:41:16 -0700428 // the ifs and functions using a gradient loop.
Corentin Wallez50931452015-03-19 10:39:13 -0700429 for (size_t i = 0; i < callDag.size(); i++)
430 {
Corentin Wallez477b2432015-08-31 10:41:16 -0700431 PullComputeDiscontinuousAndGradientLoops pull(&metadataList, i, callDag);
Corentin Wallez50931452015-03-19 10:39:13 -0700432 pull.traverse(callDag.getRecordFromIndex(i).node);
433 }
434
435 // Then push the information to callees, either from the a local discontinuous
436 // loop or from the caller being called in a discontinuous loop already
437 for (size_t i = callDag.size(); i-- > 0;)
438 {
439 PushDiscontinuousLoops push(&metadataList, i, callDag);
440 push.traverse(callDag.getRecordFromIndex(i).node);
441 }
442
443 // We create "Lod0" version of functions with the gradient operations replaced
444 // by non-gradient operations so that the D3D compiler is happier with discont
445 // loops.
446 for (auto &metadata : metadataList)
447 {
448 metadata.mNeedsLod0 = metadata.mCalledInDiscontinuousLoop && metadata.mUsesGradient;
449 }
450
Corentin Wallezf4eab3b2015-03-18 12:55:45 -0700451 return metadataList;
452}
Jamie Madill45bcc782016-11-07 13:58:48 -0500453
454} // namespace sh