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