1 //
2 // Copyright 2002 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
23 namespace sh
24 {
25
26 namespace
27 {
28
29 class RecordConstantPrecisionTraverser : public TIntermTraverser
30 {
31 public:
32 RecordConstantPrecisionTraverser(TSymbolTable *symbolTable);
33
34 void visitConstantUnion(TIntermConstantUnion *node) override;
35
36 void nextIteration();
37
foundHigherPrecisionConstant() const38 bool foundHigherPrecisionConstant() const { return mFoundHigherPrecisionConstant; }
39
40 protected:
41 bool operandAffectsParentOperationPrecision(TIntermTyped *operand);
42
43 bool mFoundHigherPrecisionConstant;
44 };
45
RecordConstantPrecisionTraverser(TSymbolTable * symbolTable)46 RecordConstantPrecisionTraverser::RecordConstantPrecisionTraverser(TSymbolTable *symbolTable)
47 : TIntermTraverser(true, false, true, symbolTable), mFoundHigherPrecisionConstant(false)
48 {}
49
operandAffectsParentOperationPrecision(TIntermTyped * operand)50 bool 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
visitConstantUnion(TIntermConstantUnion * node)123 void 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
nextIteration()147 void RecordConstantPrecisionTraverser::nextIteration()
148 {
149 mFoundHigherPrecisionConstant = false;
150 }
151
152 } // namespace
153
RecordConstantPrecision(TCompiler * compiler,TIntermNode * root,TSymbolTable * symbolTable)154 bool RecordConstantPrecision(TCompiler *compiler, 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 {
164 if (!traverser.updateTree(compiler, root))
165 {
166 return false;
167 }
168 }
169 } while (traverser.foundHigherPrecisionConstant());
170
171 return true;
172 }
173
174 } // namespace sh
175