// // 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(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