summaryrefslogtreecommitdiffstats
path: root/src/3rdparty/angle/src/compiler/translator/BreakVariableAliasingInInnerLoops.cpp
blob: d6a1e025def0adb4b573eff85c1f2d621c7cb259 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
//
// 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