| // |
| // Copyright (c) 2016 The ANGLE Project Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| // |
| |
| // BreakVariableAliasingInInnerLoops.h: To optimize simple assignments, the HLSL compiler frontend |
| // may record a variable as aliasing another. Sometimes the alias information gets garbled |
| // so we work around this issue by breaking the aliasing chain in inner loops. |
| |
| #include "BreakVariableAliasingInInnerLoops.h" |
| |
| #include "compiler/translator/IntermNode_util.h" |
| #include "compiler/translator/IntermTraverse.h" |
| |
| // A HLSL compiler developer gave us more details on the root cause and the workaround needed: |
| // The root problem is that if the HLSL compiler is applying aliasing information even on |
| // incomplete simulations (in this case, a single pass). The bug is triggered by an assignment |
| // that comes from a series of assignments, possibly with swizzled or ternary operators with |
| // known conditionals, where the source is before the loop. |
| // So, a workaround is to add a +0 term to variables the first time they are assigned to in |
| // an inner loop (if they are declared in an outside scope, otherwise there is no need). |
| // This will break the aliasing chain. |
| |
| // For simplicity here we add a +0 to any assignment that is in at least two nested loops. Because |
| // the bug only shows up with swizzles, and ternary assignment, whole array or whole structure |
| // assignment don't need a workaround. |
| |
| namespace sh |
| { |
| |
| namespace |
| { |
| |
| class AliasingBreaker : public TIntermTraverser |
| { |
| public: |
| AliasingBreaker() : TIntermTraverser(true, false, true) {} |
| |
| protected: |
| bool visitBinary(Visit visit, TIntermBinary *binary) |
| { |
| if (visit != PreVisit) |
| { |
| return false; |
| } |
| |
| if (mLoopLevel < 2 || !binary->isAssignment()) |
| { |
| return true; |
| } |
| |
| TIntermTyped *B = binary->getRight(); |
| TType type = B->getType(); |
| |
| if (!type.isScalar() && !type.isVector() && !type.isMatrix()) |
| { |
| return true; |
| } |
| |
| if (type.isArray() || IsSampler(type.getBasicType())) |
| { |
| return true; |
| } |
| |
| // We have a scalar / vector / matrix assignment with loop depth 2. |
| // Transform it from |
| // A = B |
| // to |
| // A = (B + typeof<B>(0)); |
| |
| TIntermBinary *bPlusZero = new TIntermBinary(EOpAdd, B, CreateZeroNode(type)); |
| bPlusZero->setLine(B->getLine()); |
| |
| binary->replaceChildNode(B, bPlusZero); |
| |
| return true; |
| } |
| |
| bool visitLoop(Visit visit, TIntermLoop *loop) |
| { |
| if (visit == PreVisit) |
| { |
| mLoopLevel++; |
| } |
| else |
| { |
| ASSERT(mLoopLevel > 0); |
| mLoopLevel--; |
| } |
| |
| return true; |
| } |
| |
| private: |
| int mLoopLevel = 0; |
| }; |
| |
| } // anonymous namespace |
| |
| void BreakVariableAliasingInInnerLoops(TIntermNode *root) |
| { |
| AliasingBreaker breaker; |
| root->traverse(&breaker); |
| } |
| |
| } // namespace sh |