Translator: Helpers to derive precision
Bug: angleproject:4889
Bug: angleproject:6132
Change-Id: I75e2084c76139ccb24b266f262d5e5597d8aa4ca
Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/3071599
Commit-Queue: Shahbaz Youssefi <syoussefi@chromium.org>
Reviewed-by: Jamie Madill <jmadill@chromium.org>
Reviewed-by: Tim Van Patten <timvp@google.com>
diff --git a/src/compiler/translator/IntermNode.cpp b/src/compiler/translator/IntermNode.cpp
index 8e2b101..78b42ab 100644
--- a/src/compiler/translator/IntermNode.cpp
+++ b/src/compiler/translator/IntermNode.cpp
@@ -617,35 +617,21 @@
void TIntermAggregate::setPrecisionAndQualifier()
{
mType.setQualifier(EvqTemporary);
- if (BuiltInGroup::IsBuiltIn(mOp) && !BuiltInGroup::IsMath(mOp))
+ if ((!BuiltInGroup::IsBuiltIn(mOp) && !isFunctionCall()) || BuiltInGroup::IsMath(mOp))
{
- setBuiltInFunctionPrecision();
- }
- else if (!isFunctionCall())
- {
- if (isConstructor())
- {
- // Structs should not be precision qualified, the individual members may be.
- // Built-in types on the other hand should be precision qualified.
- if (getBasicType() != EbtStruct)
- {
- setPrecisionFromChildren();
- }
- }
- else
- {
- setPrecisionForMathBuiltInOp();
- }
if (areChildrenConstQualified())
{
mType.setQualifier(EvqConst);
}
}
+
+ const TPrecision precision = derivePrecision();
+ mType.setPrecision(precision);
}
bool TIntermAggregate::areChildrenConstQualified()
{
- for (TIntermNode *&arg : mArguments)
+ for (TIntermNode *arg : mArguments)
{
TIntermTyped *typedArg = arg->getAsTyped();
if (typedArg && typedArg->getQualifier() != EvqConst)
@@ -656,78 +642,71 @@
return true;
}
-void TIntermAggregate::setPrecisionFromChildren()
+TPrecision TIntermAggregate::derivePrecision() const
{
- if (getBasicType() == EbtBool)
+ if (getBasicType() == EbtBool || getBasicType() == EbtVoid || getBasicType() == EbtStruct)
{
- mType.setPrecision(EbpUndefined);
- return;
+ return EbpUndefined;
}
- TPrecision precision = EbpUndefined;
- TIntermSequence::iterator childIter = mArguments.begin();
- while (childIter != mArguments.end())
+ // For AST function calls, take the qualifier from the declared one.
+ if (isFunctionCall())
{
- TIntermTyped *typed = (*childIter)->getAsTyped();
- if (typed)
- precision = GetHigherPrecision(typed->getPrecision(), precision);
- ++childIter;
+ return mType.getPrecision();
}
- mType.setPrecision(precision);
-}
-void TIntermAggregate::setPrecisionForMathBuiltInOp()
-{
- ASSERT(BuiltInGroup::IsMath(mOp));
- if (!setPrecisionForSpecialBuiltInOp())
- {
- setPrecisionFromChildren();
- }
-}
-
-bool TIntermAggregate::setPrecisionForSpecialBuiltInOp()
-{
+ // Some built-ins explicitly specify their precision.
switch (mOp)
{
case EOpBitfieldExtract:
- mType.setPrecision(mArguments[0]->getAsTyped()->getPrecision());
- return true;
+ return mArguments[0]->getAsTyped()->getPrecision();
case EOpBitfieldInsert:
- mType.setPrecision(GetHigherPrecision(mArguments[0]->getAsTyped()->getPrecision(),
- mArguments[1]->getAsTyped()->getPrecision()));
- return true;
+ return GetHigherPrecision(mArguments[0]->getAsTyped()->getPrecision(),
+ mArguments[1]->getAsTyped()->getPrecision());
+ case EOpTextureSize:
+ case EOpImageSize:
case EOpUaddCarry:
case EOpUsubBorrow:
- mType.setPrecision(EbpHigh);
- return true;
+ case EOpUmulExtended:
+ case EOpImulExtended:
+ case EOpFrexp:
+ case EOpLdexp:
+ return EbpHigh;
default:
- return false;
- }
-}
-
-void TIntermAggregate::setBuiltInFunctionPrecision()
-{
- // All built-ins returning bool are math operations.
- ASSERT(getBasicType() != EbtBool);
- ASSERT(!isFunctionCall() && !isConstructor() && !BuiltInGroup::IsMath(mOp));
-
- TPrecision precision = EbpUndefined;
- for (TIntermNode *arg : mArguments)
- {
- TIntermTyped *typed = arg->getAsTyped();
- // ESSL spec section 8: texture functions get their precision from the sampler.
- if (typed && IsSampler(typed->getBasicType()))
- {
- precision = typed->getPrecision();
break;
- }
}
- // ESSL 3.0 spec section 8: textureSize always gets highp precision.
- // All other functions that take a sampler are assumed to be texture functions.
- if (mOp == EOpTextureSize)
- mType.setPrecision(EbpHigh);
- else
- mType.setPrecision(precision);
+
+ // The rest of the math operations and constructors get their precision from their
+ // arguments.
+ if (BuiltInGroup::IsMath(mOp) || mOp == EOpConstruct)
+ {
+ TPrecision precision = EbpUndefined;
+ for (TIntermNode *argument : mArguments)
+ {
+ precision = GetHigherPrecision(argument->getAsTyped()->getPrecision(), precision);
+ }
+ return precision;
+ }
+
+ // Image load and atomic operations return highp.
+ if (BuiltInGroup::IsImageLoad(mOp) || BuiltInGroup::IsImageAtomic(mOp) ||
+ BuiltInGroup::IsAtomicCounter(mOp) || BuiltInGroup::IsAtomicMemory(mOp))
+ {
+ return EbpHigh;
+ }
+
+ // Texture functions return the same precision as that of the sampler. textureSize returns
+ // highp, but that's handled above. The same is true for dFd*, interpolateAt* and
+ // subpassLoad operations.
+ if (BuiltInGroup::IsTexture(mOp) || BuiltInGroup::IsDerivativesFS(mOp) ||
+ BuiltInGroup::IsInterpolationFS(mOp) || mOp == EOpSubpassLoad)
+ {
+ return mArguments[0]->getAsTyped()->getPrecision();
+ }
+
+ // Every possibility must be explicitly handled, except for desktop-GLSL-specific built-ins
+ // for which precision does't matter.
+ return EbpUndefined;
}
const char *TIntermAggregate::functionName() const
@@ -1296,7 +1275,7 @@
if (mOp == EOpArrayLength)
{
// Special case: the qualifier of .length() doesn't depend on the operand qualifier.
- setType(TType(EbtInt, EbpUndefined, EvqConst));
+ setType(TType(EbtInt, EbpHigh, EvqConst));
return;
}
@@ -1304,74 +1283,115 @@
if (mOperand->getQualifier() == EvqConst)
resultQualifier = EvqConst;
- unsigned char operandPrimarySize =
- static_cast<unsigned char>(mOperand->getType().getNominalSize());
+ TType resultType = mOperand->getType();
+ resultType.setQualifier(resultQualifier);
+
+ // Result is an intermediate value, so make sure it's identified as such.
+ resultType.setInterfaceBlock(nullptr);
+
+ // Override type properties for special built-ins. Precision is determined later by
+ // derivePrecision.
switch (mOp)
{
case EOpFloatBitsToInt:
- setType(TType(EbtInt, EbpHigh, resultQualifier, operandPrimarySize));
+ resultType.setBasicType(EbtInt);
break;
case EOpFloatBitsToUint:
- setType(TType(EbtUInt, EbpHigh, resultQualifier, operandPrimarySize));
+ resultType.setBasicType(EbtUInt);
break;
case EOpIntBitsToFloat:
case EOpUintBitsToFloat:
- setType(TType(EbtFloat, EbpHigh, resultQualifier, operandPrimarySize));
+ resultType.setBasicType(EbtFloat);
break;
case EOpPackSnorm2x16:
case EOpPackUnorm2x16:
case EOpPackHalf2x16:
case EOpPackUnorm4x8:
case EOpPackSnorm4x8:
- setType(TType(EbtUInt, EbpHigh, resultQualifier));
+ resultType.setBasicType(EbtUInt);
+ resultType.setPrimarySize(1);
break;
case EOpUnpackSnorm2x16:
case EOpUnpackUnorm2x16:
- setType(TType(EbtFloat, EbpHigh, resultQualifier, 2));
- break;
case EOpUnpackHalf2x16:
- setType(TType(EbtFloat, EbpMedium, resultQualifier, 2));
+ resultType.setBasicType(EbtFloat);
+ resultType.setPrimarySize(2);
break;
case EOpUnpackUnorm4x8:
case EOpUnpackSnorm4x8:
- setType(TType(EbtFloat, EbpMedium, resultQualifier, 4));
+ resultType.setBasicType(EbtFloat);
+ resultType.setPrimarySize(4);
break;
case EOpAny:
case EOpAll:
- setType(TType(EbtBool, EbpUndefined, resultQualifier));
+ resultType.setBasicType(EbtBool);
+ resultType.setPrimarySize(1);
break;
case EOpLength:
case EOpDeterminant:
- setType(TType(EbtFloat, mOperand->getType().getPrecision(), resultQualifier));
+ resultType.setBasicType(EbtFloat);
+ resultType.setPrimarySize(1);
+ resultType.setSecondarySize(1);
break;
case EOpTranspose:
- setType(TType(EbtFloat, mOperand->getType().getPrecision(), resultQualifier,
- static_cast<unsigned char>(mOperand->getType().getRows()),
- static_cast<unsigned char>(mOperand->getType().getCols())));
+ ASSERT(resultType.getBasicType() == EbtFloat);
+ resultType.setPrimarySize(static_cast<unsigned char>(mOperand->getType().getRows()));
+ resultType.setSecondarySize(static_cast<unsigned char>(mOperand->getType().getCols()));
break;
case EOpIsinf:
case EOpIsnan:
- setType(TType(EbtBool, EbpUndefined, resultQualifier, operandPrimarySize));
- break;
- case EOpBitfieldReverse:
- setType(TType(mOperand->getBasicType(), EbpHigh, resultQualifier, operandPrimarySize));
+ resultType.setBasicType(EbtBool);
break;
case EOpBitCount:
- setType(TType(EbtInt, EbpLow, resultQualifier, operandPrimarySize));
- break;
case EOpFindLSB:
- setType(TType(EbtInt, EbpLow, resultQualifier, operandPrimarySize));
- break;
case EOpFindMSB:
- setType(TType(EbtInt, EbpLow, resultQualifier, operandPrimarySize));
+ resultType.setBasicType(EbtInt);
break;
default:
- setType(mOperand->getType());
- mType.setQualifier(resultQualifier);
- // Result is an intermediate value, so make sure it's identified as such.
- mType.setInterfaceBlock(nullptr);
break;
}
+
+ const TPrecision precision = derivePrecision();
+ resultType.setPrecision(precision);
+ setType(resultType);
+}
+
+TPrecision TIntermUnary::derivePrecision() const
+{
+ // Unary operators generally derive their precision from their operand, except for a few
+ // built-ins where this is overriden.
+ switch (mOp)
+ {
+ case EOpArrayLength:
+ case EOpFloatBitsToInt:
+ case EOpFloatBitsToUint:
+ case EOpIntBitsToFloat:
+ case EOpUintBitsToFloat:
+ case EOpPackSnorm2x16:
+ case EOpPackUnorm2x16:
+ case EOpPackHalf2x16:
+ case EOpPackUnorm4x8:
+ case EOpPackSnorm4x8:
+ case EOpUnpackSnorm2x16:
+ case EOpUnpackUnorm2x16:
+ case EOpBitfieldReverse:
+ return EbpHigh;
+ case EOpUnpackHalf2x16:
+ case EOpUnpackUnorm4x8:
+ case EOpUnpackSnorm4x8:
+ return EbpMedium;
+ case EOpBitCount:
+ case EOpFindLSB:
+ case EOpFindMSB:
+ return EbpLow;
+ case EOpAny:
+ case EOpAll:
+ case EOpIsinf:
+ case EOpIsnan:
+ return EbpUndefined;
+ default:
+ return mOperand->getPrecision();
+ }
}
TIntermSwizzle::TIntermSwizzle(TIntermTyped *operand, const TVector<int> &swizzleOffsets)
@@ -1439,6 +1459,9 @@
ASSERT(mFalseExpression);
getTypePointer()->setQualifier(
TIntermTernary::DetermineQualifier(cond, trueExpression, falseExpression));
+
+ const TPrecision precision = derivePrecision();
+ mType.setPrecision(precision);
}
TIntermLoop::TIntermLoop(TLoopType type,
@@ -1512,6 +1535,11 @@
return EvqTemporary;
}
+TPrecision TIntermTernary::derivePrecision() const
+{
+ return GetHigherPrecision(mTrueExpression->getPrecision(), mFalseExpression->getPrecision());
+}
+
TIntermTyped *TIntermTernary::fold(TDiagnostics * /* diagnostics */)
{
if (mCondition->getAsConstantUnion())
@@ -1535,10 +1563,15 @@
resultQualifier = EvqConst;
auto numFields = mSwizzleOffsets.size();
- setType(TType(mOperand->getBasicType(), mOperand->getPrecision(), resultQualifier,
+ setType(TType(mOperand->getBasicType(), derivePrecision(), resultQualifier,
static_cast<unsigned char>(numFields)));
}
+TPrecision TIntermSwizzle::derivePrecision() const
+{
+ return mOperand->getPrecision();
+}
+
bool TIntermSwizzle::hasDuplicateOffsets() const
{
if (mHasFoldedDuplicateOffsets)
@@ -1635,6 +1668,13 @@
getTypePointer()->setQualifier(EvqTemporary);
}
+ // Result is an intermediate value, so make sure it's identified as such. That's not true for
+ // interface block arrays being indexed.
+ if (mOp != EOpIndexDirect && mOp != EOpIndexIndirect)
+ {
+ getTypePointer()->setInterfaceBlock(nullptr);
+ }
+
// Handle indexing ops.
switch (mOp)
{
@@ -1646,12 +1686,11 @@
}
else if (mLeft->isMatrix())
{
- setType(TType(mLeft->getBasicType(), mLeft->getPrecision(), resultQualifier,
- static_cast<unsigned char>(mLeft->getRows())));
+ mType.toMatrixColumnType();
}
else if (mLeft->isVector())
{
- setType(TType(mLeft->getBasicType(), mLeft->getPrecision(), resultQualifier));
+ mType.toComponentType();
}
else
{
@@ -1661,16 +1700,16 @@
case EOpIndexDirectStruct:
{
const TFieldList &fields = mLeft->getType().getStruct()->fields();
- const int i = mRight->getAsConstantUnion()->getIConst(0);
- setType(*fields[i]->type());
+ const int fieldIndex = mRight->getAsConstantUnion()->getIConst(0);
+ setType(*fields[fieldIndex]->type());
getTypePointer()->setQualifier(resultQualifier);
return;
}
case EOpIndexDirectInterfaceBlock:
{
const TFieldList &fields = mLeft->getType().getInterfaceBlock()->fields();
- const int i = mRight->getAsConstantUnion()->getIConst(0);
- setType(*fields[i]->type());
+ const int fieldIndex = mRight->getAsConstantUnion()->getIConst(0);
+ setType(*fields[fieldIndex]->type());
getTypePointer()->setQualifier(resultQualifier);
return;
}
@@ -1680,51 +1719,8 @@
ASSERT(mLeft->isArray() == mRight->isArray());
- // The result gets promoted to the highest precision.
- TPrecision higherPrecision = GetHigherPrecision(mLeft->getPrecision(), mRight->getPrecision());
- getTypePointer()->setPrecision(higherPrecision);
-
const int nominalSize = std::max(mLeft->getNominalSize(), mRight->getNominalSize());
- //
- // All scalars or structs. Code after this test assumes this case is removed!
- //
- if (nominalSize == 1)
- {
- switch (mOp)
- {
- //
- // Promote to conditional
- //
- case EOpEqual:
- case EOpNotEqual:
- case EOpLessThan:
- case EOpGreaterThan:
- case EOpLessThanEqual:
- case EOpGreaterThanEqual:
- setType(TType(EbtBool, EbpUndefined, resultQualifier));
- break;
-
- //
- // And and Or operate on conditionals
- //
- case EOpLogicalAnd:
- case EOpLogicalXor:
- case EOpLogicalOr:
- ASSERT(mLeft->getBasicType() == EbtBool && mRight->getBasicType() == EbtBool);
- setType(TType(EbtBool, EbpUndefined, resultQualifier));
- break;
-
- default:
- break;
- }
- return;
- }
-
- // If we reach here, at least one of the operands is vector or matrix.
- // The other operand could be a scalar, vector, or matrix.
- TBasicType basicType = mLeft->getBasicType();
-
switch (mOp)
{
case EOpMul:
@@ -1732,27 +1728,24 @@
case EOpMatrixTimesScalar:
if (mRight->isMatrix())
{
- setType(TType(basicType, higherPrecision, resultQualifier,
- static_cast<unsigned char>(mRight->getCols()),
- static_cast<unsigned char>(mRight->getRows())));
+ getTypePointer()->setPrimarySize(static_cast<unsigned char>(mRight->getCols()));
+ getTypePointer()->setSecondarySize(static_cast<unsigned char>(mRight->getRows()));
}
break;
case EOpMatrixTimesVector:
- setType(TType(basicType, higherPrecision, resultQualifier,
- static_cast<unsigned char>(mLeft->getRows()), 1));
+ getTypePointer()->setPrimarySize(static_cast<unsigned char>(mLeft->getRows()));
+ getTypePointer()->setSecondarySize(1);
break;
case EOpMatrixTimesMatrix:
- setType(TType(basicType, higherPrecision, resultQualifier,
- static_cast<unsigned char>(mRight->getCols()),
- static_cast<unsigned char>(mLeft->getRows())));
+ getTypePointer()->setPrimarySize(static_cast<unsigned char>(mRight->getCols()));
+ getTypePointer()->setSecondarySize(static_cast<unsigned char>(mLeft->getRows()));
break;
case EOpVectorTimesScalar:
- setType(TType(basicType, higherPrecision, resultQualifier,
- static_cast<unsigned char>(nominalSize), 1));
+ getTypePointer()->setPrimarySize(static_cast<unsigned char>(nominalSize));
break;
case EOpVectorTimesMatrix:
- setType(TType(basicType, higherPrecision, resultQualifier,
- static_cast<unsigned char>(mRight->getCols()), 1));
+ getTypePointer()->setPrimarySize(static_cast<unsigned char>(mRight->getCols()));
+ ASSERT(getType().getSecondarySize() == 1);
break;
case EOpMulAssign:
case EOpVectorTimesScalarAssign:
@@ -1785,12 +1778,11 @@
case EOpBitwiseXorAssign:
case EOpBitwiseOrAssign:
{
+ ASSERT(!mLeft->isArray() && !mRight->isArray());
const int secondarySize =
std::max(mLeft->getSecondarySize(), mRight->getSecondarySize());
- setType(TType(basicType, higherPrecision, resultQualifier,
- static_cast<unsigned char>(nominalSize),
- static_cast<unsigned char>(secondarySize)));
- ASSERT(!mLeft->isArray() && !mRight->isArray());
+ getTypePointer()->setPrimarySize(static_cast<unsigned char>(nominalSize));
+ getTypePointer()->setSecondarySize(static_cast<unsigned char>(secondarySize));
break;
}
case EOpEqual:
@@ -1804,6 +1796,15 @@
setType(TType(EbtBool, EbpUndefined, resultQualifier));
break;
+ //
+ // And and Or operate on conditionals
+ //
+ case EOpLogicalAnd:
+ case EOpLogicalXor:
+ case EOpLogicalOr:
+ ASSERT(mLeft->getBasicType() == EbtBool && mRight->getBasicType() == EbtBool);
+ break;
+
case EOpIndexDirect:
case EOpIndexIndirect:
case EOpIndexDirectInterfaceBlock:
@@ -1815,6 +1816,55 @@
UNREACHABLE();
break;
}
+
+ const TPrecision precision = derivePrecision();
+ getTypePointer()->setPrecision(precision);
+}
+
+TPrecision TIntermBinary::derivePrecision() const
+{
+ const TPrecision higherPrecision =
+ GetHigherPrecision(mLeft->getPrecision(), mRight->getPrecision());
+
+ switch (mOp)
+ {
+ case EOpComma:
+ // Comma takes the right node's value.
+ return mRight->getPrecision();
+
+ case EOpIndexDirect:
+ case EOpIndexIndirect:
+ // When indexing an array, the precision of the array is preserved.
+ return mLeft->getPrecision();
+
+ case EOpIndexDirectStruct:
+ case EOpIndexDirectInterfaceBlock:
+ {
+ // When selecting the field of a block, the precision is taken from the field's
+ // declaration.
+ const TFieldList &fields = mOp == EOpIndexDirectStruct
+ ? mLeft->getType().getStruct()->fields()
+ : mLeft->getType().getInterfaceBlock()->fields();
+ const int fieldIndex = mRight->getAsConstantUnion()->getIConst(0);
+ return fields[fieldIndex]->type()->getPrecision();
+ }
+
+ case EOpEqual:
+ case EOpNotEqual:
+ case EOpLessThan:
+ case EOpGreaterThan:
+ case EOpLessThanEqual:
+ case EOpGreaterThanEqual:
+ case EOpLogicalAnd:
+ case EOpLogicalXor:
+ case EOpLogicalOr:
+ // No precision specified on bool results.
+ return EbpUndefined;
+
+ default:
+ // All other operations are evaluated at the higher of the two operands' precisions.
+ return higherPrecision;
+ }
}
bool TIntermConstantUnion::hasConstantValue() const
diff --git a/src/compiler/translator/IntermNode.h b/src/compiler/translator/IntermNode.h
index 1b3b42d..62a2398 100644
--- a/src/compiler/translator/IntermNode.h
+++ b/src/compiler/translator/IntermNode.h
@@ -475,6 +475,7 @@
private:
void promote();
+ TPrecision derivePrecision() const;
TIntermSwizzle(const TIntermSwizzle &node); // Note: not deleted, just private!
};
@@ -524,6 +525,7 @@
private:
void promote();
+ TPrecision derivePrecision() const;
static TQualifier GetCommaQualifier(int shaderVersion,
const TIntermTyped *left,
@@ -571,6 +573,7 @@
private:
void promote();
+ TPrecision derivePrecision() const;
TIntermUnary(const TIntermUnary &node); // note: not deleted, just private!
};
@@ -663,19 +666,9 @@
TIntermAggregate(const TIntermAggregate &node); // note: not deleted, just private!
void setPrecisionAndQualifier();
+ TPrecision derivePrecision() const;
bool areChildrenConstQualified();
-
- void setPrecisionFromChildren();
-
- void setPrecisionForMathBuiltInOp();
-
- // Returns true if precision was set according to special rules for this built-in.
- bool setPrecisionForSpecialBuiltInOp();
-
- // Used for non-math built-in functions. The function name in the symbol info needs to be set
- // before calling this.
- void setBuiltInFunctionPrecision();
};
// A list of statements. Either the root node which contains declarations and function definitions,
@@ -903,6 +896,7 @@
static TQualifier DetermineQualifier(TIntermTyped *cond,
TIntermTyped *trueExpression,
TIntermTyped *falseExpression);
+ TPrecision derivePrecision() const;
TIntermTyped *mCondition;
TIntermTyped *mTrueExpression;