1 //
2 // Copyright (c) 2016 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 // BreakVariableAliasingInInnerLoops.h: To optimize simple assignments, the HLSL compiler frontend
8 // may record a variable as aliasing another. Sometimes the alias information gets garbled
9 // so we work around this issue by breaking the aliasing chain in inner loops.
10
11 #include "BreakVariableAliasingInInnerLoops.h"
12
13 #include "compiler/translator/IntermNode_util.h"
14 #include "compiler/translator/IntermTraverse.h"
15
16 // A HLSL compiler developer gave us more details on the root cause and the workaround needed:
17 // The root problem is that if the HLSL compiler is applying aliasing information even on
18 // incomplete simulations (in this case, a single pass). The bug is triggered by an assignment
19 // that comes from a series of assignments, possibly with swizzled or ternary operators with
20 // known conditionals, where the source is before the loop.
21 // So, a workaround is to add a +0 term to variables the first time they are assigned to in
22 // an inner loop (if they are declared in an outside scope, otherwise there is no need).
23 // This will break the aliasing chain.
24
25 // For simplicity here we add a +0 to any assignment that is in at least two nested loops. Because
26 // the bug only shows up with swizzles, and ternary assignment, whole array or whole structure
27 // assignment don't need a workaround.
28
29 namespace sh
30 {
31
32 namespace
33 {
34
35 class AliasingBreaker : public TIntermTraverser
36 {
37 public:
AliasingBreaker()38 AliasingBreaker() : TIntermTraverser(true, false, true) {}
39
40 protected:
visitBinary(Visit visit,TIntermBinary * binary)41 bool visitBinary(Visit visit, TIntermBinary *binary)
42 {
43 if (visit != PreVisit)
44 {
45 return false;
46 }
47
48 if (mLoopLevel < 2 || !binary->isAssignment())
49 {
50 return true;
51 }
52
53 TIntermTyped *B = binary->getRight();
54 TType type = B->getType();
55
56 if (!type.isScalar() && !type.isVector() && !type.isMatrix())
57 {
58 return true;
59 }
60
61 if (type.isArray() || IsSampler(type.getBasicType()))
62 {
63 return true;
64 }
65
66 // We have a scalar / vector / matrix assignment with loop depth 2.
67 // Transform it from
68 // A = B
69 // to
70 // A = (B + typeof<B>(0));
71
72 TIntermBinary *bPlusZero = new TIntermBinary(EOpAdd, B, CreateZeroNode(type));
73 bPlusZero->setLine(B->getLine());
74
75 binary->replaceChildNode(B, bPlusZero);
76
77 return true;
78 }
79
visitLoop(Visit visit,TIntermLoop * loop)80 bool visitLoop(Visit visit, TIntermLoop *loop)
81 {
82 if (visit == PreVisit)
83 {
84 mLoopLevel++;
85 }
86 else
87 {
88 ASSERT(mLoopLevel > 0);
89 mLoopLevel--;
90 }
91
92 return true;
93 }
94
95 private:
96 int mLoopLevel = 0;
97 };
98
99 } // anonymous namespace
100
BreakVariableAliasingInInnerLoops(TIntermNode * root)101 void BreakVariableAliasingInInnerLoops(TIntermNode *root)
102 {
103 AliasingBreaker breaker;
104 root->traverse(&breaker);
105 }
106
107 } // namespace sh
108