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// During parsing, all constant expressions are folded to constant union nodes. The expressions that
7// have been folded may have had precision qualifiers, which should affect the precision of the
8// consuming operation. If the folded constant union nodes are written to output as such they won't
9// have any precision qualifiers, and their effect on the precision of the consuming operation is
10// lost.
11//
12// RecordConstantPrecision is an AST traverser that inspects the precision qualifiers of constants
13// and hoists the constants outside the containing expression as precision qualified named variables
14// in case that is required for correct precision propagation.
15//
16
17#include "compiler/translator/tree_ops/RecordConstantPrecision.h"
18
19#include "compiler/translator/InfoSink.h"
20#include "compiler/translator/tree_util/IntermNode_util.h"
21#include "compiler/translator/tree_util/IntermTraverse.h"
22
23namespace sh
24{
25
26namespace
27{
28
29class RecordConstantPrecisionTraverser : public TIntermTraverser
30{
31 public:
32 RecordConstantPrecisionTraverser(TSymbolTable *symbolTable);
33
34 void visitConstantUnion(TIntermConstantUnion *node) override;
35
36 void nextIteration();
37
38 bool foundHigherPrecisionConstant() const { return mFoundHigherPrecisionConstant; }
39
40 protected:
41 bool operandAffectsParentOperationPrecision(TIntermTyped *operand);
42
43 bool mFoundHigherPrecisionConstant;
44};
45
46RecordConstantPrecisionTraverser::RecordConstantPrecisionTraverser(TSymbolTable *symbolTable)
47 : TIntermTraverser(true, false, true, symbolTable), mFoundHigherPrecisionConstant(false)
48{}
49
50bool RecordConstantPrecisionTraverser::operandAffectsParentOperationPrecision(TIntermTyped *operand)
51{
52 if (getParentNode()->getAsCaseNode() || getParentNode()->getAsBlock())
53 {
54 return false;
55 }
56
57 const TIntermBinary *parentAsBinary = getParentNode()->getAsBinaryNode();
58 if (parentAsBinary != nullptr)
59 {
60 // If the constant is assigned or is used to initialize a variable, or if it's an index,
61 // its precision has no effect.
62 switch (parentAsBinary->getOp())
63 {
64 case EOpInitialize:
65 case EOpAssign:
66 case EOpIndexDirect:
67 case EOpIndexDirectStruct:
68 case EOpIndexDirectInterfaceBlock:
69 case EOpIndexIndirect:
70 return false;
71 default:
72 break;
73 }
74
75 TIntermTyped *otherOperand = parentAsBinary->getRight();
76 if (otherOperand == operand)
77 {
78 otherOperand = parentAsBinary->getLeft();
79 }
80 // If the precision of the other child is at least as high as the precision of the constant,
81 // the precision of the constant has no effect.
82 if (otherOperand->getAsConstantUnion() == nullptr &&
83 otherOperand->getPrecision() >= operand->getPrecision())
84 {
85 return false;
86 }
87 }
88
89 TIntermAggregate *parentAsAggregate = getParentNode()->getAsAggregate();
90 if (parentAsAggregate != nullptr)
91 {
92 if (!parentAsAggregate->gotPrecisionFromChildren())
93 {
94 // This can be either:
95 // * a call to an user-defined function
96 // * a call to a texture function
97 // * some other kind of aggregate
98 // In any of these cases the constant precision has no effect.
99 return false;
100 }
101 if (parentAsAggregate->isConstructor() && parentAsAggregate->getBasicType() == EbtBool)
102 {
103 return false;
104 }
105 // If the precision of operands does affect the result, but the precision of any of the
106 // other children has a precision that's at least as high as the precision of the constant,
107 // the precision of the constant has no effect.
108 TIntermSequence *parameters = parentAsAggregate->getSequence();
109 for (TIntermNode *parameter : *parameters)
110 {
111 const TIntermTyped *typedParameter = parameter->getAsTyped();
112 if (parameter != operand && typedParameter != nullptr &&
113 parameter->getAsConstantUnion() == nullptr &&
114 typedParameter->getPrecision() >= operand->getPrecision())
115 {
116 return false;
117 }
118 }
119 }
120 return true;
121}
122
123void RecordConstantPrecisionTraverser::visitConstantUnion(TIntermConstantUnion *node)
124{
125 if (mFoundHigherPrecisionConstant)
126 return;
127
128 // If the constant has lowp or undefined precision, it can't increase the precision of consuming
129 // operations.
130 if (node->getPrecision() < EbpMedium)
131 return;
132
133 // It's possible the node has no effect on the precision of the consuming expression, depending
134 // on the consuming expression, and the precision of the other parameters of the expression.
135 if (!operandAffectsParentOperationPrecision(node))
136 return;
137
138 // Make the constant a precision-qualified named variable to make sure it affects the precision
139 // of the consuming expression.
140 TIntermDeclaration *variableDeclaration = nullptr;
141 TVariable *variable = DeclareTempVariable(mSymbolTable, node, EvqConst, &variableDeclaration);
142 insertStatementInParentBlock(variableDeclaration);
143 queueReplacement(CreateTempSymbolNode(variable), OriginalNode::IS_DROPPED);
144 mFoundHigherPrecisionConstant = true;
145}
146
147void RecordConstantPrecisionTraverser::nextIteration()
148{
149 mFoundHigherPrecisionConstant = false;
150}
151
152} // namespace
153
154void RecordConstantPrecision(TIntermNode *root, TSymbolTable *symbolTable)
155{
156 RecordConstantPrecisionTraverser traverser(symbolTable);
157 // Iterate as necessary, and reset the traverser between iterations.
158 do
159 {
160 traverser.nextIteration();
161 root->traverse(&traverser);
162 if (traverser.foundHigherPrecisionConstant())
163 traverser.updateTree();
164 } while (traverser.foundHigherPrecisionConstant());
165}
166
167} // namespace sh
168