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