BreakVariableAliasingInInnerLoops.cpp (3263B)
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/d3d/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: 39 AliasingBreaker() : TIntermTraverser(true, false, true) {} 40 41 protected: 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 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 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