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