1//
2// Copyright (c) 2002-2015 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// RemovePow is an AST traverser to convert pow(x, y) built-in calls where y is a
7// constant to exp2(y * log2(x)). This works around an issue in NVIDIA 311 series
8// OpenGL drivers.
9//
10
11#include "compiler/translator/tree_ops/RemovePow.h"
12
13#include "compiler/translator/InfoSink.h"
14#include "compiler/translator/tree_util/IntermNode_util.h"
15#include "compiler/translator/tree_util/IntermTraverse.h"
16
17namespace sh
18{
19
20namespace
21{
22
23bool IsProblematicPow(TIntermTyped *node)
24{
25 TIntermAggregate *agg = node->getAsAggregate();
26 if (agg != nullptr && agg->getOp() == EOpPow)
27 {
28 ASSERT(agg->getSequence()->size() == 2);
29 return agg->getSequence()->at(1)->getAsConstantUnion() != nullptr;
30 }
31 return false;
32}
33
34// Traverser that converts all pow operations simultaneously.
35class RemovePowTraverser : public TIntermTraverser
36{
37 public:
38 RemovePowTraverser(TSymbolTable *symbolTable);
39
40 bool visitAggregate(Visit visit, TIntermAggregate *node) override;
41
42 void nextIteration() { mNeedAnotherIteration = false; }
43 bool needAnotherIteration() const { return mNeedAnotherIteration; }
44
45 protected:
46 bool mNeedAnotherIteration;
47};
48
49RemovePowTraverser::RemovePowTraverser(TSymbolTable *symbolTable)
50 : TIntermTraverser(true, false, false, symbolTable), mNeedAnotherIteration(false)
51{}
52
53bool RemovePowTraverser::visitAggregate(Visit visit, TIntermAggregate *node)
54{
55 if (IsProblematicPow(node))
56 {
57 TIntermTyped *x = node->getSequence()->at(0)->getAsTyped();
58 TIntermTyped *y = node->getSequence()->at(1)->getAsTyped();
59
60 TIntermSequence *logArgs = new TIntermSequence();
61 logArgs->push_back(x);
62 TIntermTyped *log = CreateBuiltInFunctionCallNode("log2", logArgs, *mSymbolTable, 100);
63 log->setLine(node->getLine());
64
65 TOperator op = TIntermBinary::GetMulOpBasedOnOperands(y->getType(), log->getType());
66 TIntermBinary *mul = new TIntermBinary(op, y, log);
67 mul->setLine(node->getLine());
68
69 TIntermSequence *expArgs = new TIntermSequence();
70 expArgs->push_back(mul);
71 TIntermTyped *exp = CreateBuiltInFunctionCallNode("exp2", expArgs, *mSymbolTable, 100);
72 exp->setLine(node->getLine());
73
74 queueReplacement(exp, OriginalNode::IS_DROPPED);
75
76 // If the x parameter also needs to be replaced, we need to do that in another traversal,
77 // since it's parent node will change in a way that's not handled correctly by updateTree().
78 if (IsProblematicPow(x))
79 {
80 mNeedAnotherIteration = true;
81 return false;
82 }
83 }
84 return true;
85}
86
87} // namespace
88
89void RemovePow(TIntermNode *root, TSymbolTable *symbolTable)
90{
91 RemovePowTraverser traverser(symbolTable);
92 // Iterate as necessary, and reset the traverser between iterations.
93 do
94 {
95 traverser.nextIteration();
96 root->traverse(&traverser);
97 traverser.updateTree();
98 } while (traverser.needAnotherIteration());
99}
100
101} // namespace sh
102