mirror of
https://github.com/ZDoom/ZDRay.git
synced 2024-11-29 15:12:25 +00:00
4001 lines
134 KiB
C++
4001 lines
134 KiB
C++
//
|
|
// Copyright (C) 2002-2005 3Dlabs Inc. Ltd.
|
|
// Copyright (C) 2012-2015 LunarG, Inc.
|
|
// Copyright (C) 2015-2020 Google, Inc.
|
|
// Copyright (C) 2017 ARM Limited.
|
|
//
|
|
// All rights reserved.
|
|
//
|
|
// Redistribution and use in source and binary forms, with or without
|
|
// modification, are permitted provided that the following conditions
|
|
// are met:
|
|
//
|
|
// Redistributions of source code must retain the above copyright
|
|
// notice, this list of conditions and the following disclaimer.
|
|
//
|
|
// Redistributions in binary form must reproduce the above
|
|
// copyright notice, this list of conditions and the following
|
|
// disclaimer in the documentation and/or other materials provided
|
|
// with the distribution.
|
|
//
|
|
// Neither the name of 3Dlabs Inc. Ltd. nor the names of its
|
|
// contributors may be used to endorse or promote products derived
|
|
// from this software without specific prior written permission.
|
|
//
|
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
|
// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
|
// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
|
// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
|
// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
// POSSIBILITY OF SUCH DAMAGE.
|
|
//
|
|
|
|
//
|
|
// Build the intermediate representation.
|
|
//
|
|
|
|
#include "localintermediate.h"
|
|
#include "RemoveTree.h"
|
|
#include "SymbolTable.h"
|
|
#include "propagateNoContraction.h"
|
|
|
|
#include <cfloat>
|
|
#include <utility>
|
|
#include <tuple>
|
|
|
|
namespace glslang {
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// First set of functions are to help build the intermediate representation.
|
|
// These functions are not member functions of the nodes.
|
|
// They are called from parser productions.
|
|
//
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
//
|
|
// Add a terminal node for an identifier in an expression.
|
|
//
|
|
// Returns the added node.
|
|
//
|
|
|
|
TIntermSymbol* TIntermediate::addSymbol(long long id, const TString& name, const TType& type, const TConstUnionArray& constArray,
|
|
TIntermTyped* constSubtree, const TSourceLoc& loc)
|
|
{
|
|
TIntermSymbol* node = new TIntermSymbol(id, name, type);
|
|
node->setLoc(loc);
|
|
node->setConstArray(constArray);
|
|
node->setConstSubtree(constSubtree);
|
|
|
|
return node;
|
|
}
|
|
|
|
TIntermSymbol* TIntermediate::addSymbol(const TIntermSymbol& intermSymbol)
|
|
{
|
|
return addSymbol(intermSymbol.getId(),
|
|
intermSymbol.getName(),
|
|
intermSymbol.getType(),
|
|
intermSymbol.getConstArray(),
|
|
intermSymbol.getConstSubtree(),
|
|
intermSymbol.getLoc());
|
|
}
|
|
|
|
TIntermSymbol* TIntermediate::addSymbol(const TVariable& variable)
|
|
{
|
|
glslang::TSourceLoc loc; // just a null location
|
|
loc.init();
|
|
|
|
return addSymbol(variable, loc);
|
|
}
|
|
|
|
TIntermSymbol* TIntermediate::addSymbol(const TVariable& variable, const TSourceLoc& loc)
|
|
{
|
|
return addSymbol(variable.getUniqueId(), variable.getName(), variable.getType(), variable.getConstArray(), variable.getConstSubtree(), loc);
|
|
}
|
|
|
|
TIntermSymbol* TIntermediate::addSymbol(const TType& type, const TSourceLoc& loc)
|
|
{
|
|
TConstUnionArray unionArray; // just a null constant
|
|
|
|
return addSymbol(0, "", type, unionArray, nullptr, loc);
|
|
}
|
|
|
|
//
|
|
// Connect two nodes with a new parent that does a binary operation on the nodes.
|
|
//
|
|
// Returns the added node.
|
|
//
|
|
// Returns nullptr if the working conversions and promotions could not be found.
|
|
//
|
|
TIntermTyped* TIntermediate::addBinaryMath(TOperator op, TIntermTyped* left, TIntermTyped* right, const TSourceLoc& loc)
|
|
{
|
|
// No operations work on blocks
|
|
if (left->getType().getBasicType() == EbtBlock || right->getType().getBasicType() == EbtBlock)
|
|
return nullptr;
|
|
|
|
// Convert "reference +/- int" and "reference - reference" to integer math
|
|
if (op == EOpAdd || op == EOpSub) {
|
|
|
|
// No addressing math on struct with unsized array.
|
|
if ((left->isReference() && left->getType().getReferentType()->containsUnsizedArray()) ||
|
|
(right->isReference() && right->getType().getReferentType()->containsUnsizedArray())) {
|
|
return nullptr;
|
|
}
|
|
|
|
if (left->isReference() && isTypeInt(right->getBasicType())) {
|
|
const TType& referenceType = left->getType();
|
|
TIntermConstantUnion* size = addConstantUnion((unsigned long long)computeBufferReferenceTypeSize(left->getType()), loc, true);
|
|
left = addBuiltInFunctionCall(loc, EOpConvPtrToUint64, true, left, TType(EbtUint64));
|
|
|
|
right = createConversion(EbtInt64, right);
|
|
right = addBinaryMath(EOpMul, right, size, loc);
|
|
|
|
TIntermTyped *node = addBinaryMath(op, left, right, loc);
|
|
node = addBuiltInFunctionCall(loc, EOpConvUint64ToPtr, true, node, referenceType);
|
|
return node;
|
|
}
|
|
}
|
|
|
|
if (op == EOpAdd && right->isReference() && isTypeInt(left->getBasicType())) {
|
|
const TType& referenceType = right->getType();
|
|
TIntermConstantUnion* size =
|
|
addConstantUnion((unsigned long long)computeBufferReferenceTypeSize(right->getType()), loc, true);
|
|
right = addBuiltInFunctionCall(loc, EOpConvPtrToUint64, true, right, TType(EbtUint64));
|
|
|
|
left = createConversion(EbtInt64, left);
|
|
left = addBinaryMath(EOpMul, left, size, loc);
|
|
|
|
TIntermTyped *node = addBinaryMath(op, left, right, loc);
|
|
node = addBuiltInFunctionCall(loc, EOpConvUint64ToPtr, true, node, referenceType);
|
|
return node;
|
|
}
|
|
|
|
if (op == EOpSub && left->isReference() && right->isReference()) {
|
|
TIntermConstantUnion* size =
|
|
addConstantUnion((long long)computeBufferReferenceTypeSize(left->getType()), loc, true);
|
|
|
|
left = addBuiltInFunctionCall(loc, EOpConvPtrToUint64, true, left, TType(EbtUint64));
|
|
right = addBuiltInFunctionCall(loc, EOpConvPtrToUint64, true, right, TType(EbtUint64));
|
|
|
|
left = addBuiltInFunctionCall(loc, EOpConvUint64ToInt64, true, left, TType(EbtInt64));
|
|
right = addBuiltInFunctionCall(loc, EOpConvUint64ToInt64, true, right, TType(EbtInt64));
|
|
|
|
left = addBinaryMath(EOpSub, left, right, loc);
|
|
|
|
TIntermTyped *node = addBinaryMath(EOpDiv, left, size, loc);
|
|
return node;
|
|
}
|
|
|
|
// No other math operators supported on references
|
|
if (left->isReference() || right->isReference())
|
|
return nullptr;
|
|
|
|
// Try converting the children's base types to compatible types.
|
|
auto children = addPairConversion(op, left, right);
|
|
left = std::get<0>(children);
|
|
right = std::get<1>(children);
|
|
|
|
if (left == nullptr || right == nullptr)
|
|
return nullptr;
|
|
|
|
// Convert the children's type shape to be compatible.
|
|
addBiShapeConversion(op, left, right);
|
|
if (left == nullptr || right == nullptr)
|
|
return nullptr;
|
|
|
|
//
|
|
// Need a new node holding things together. Make
|
|
// one and promote it to the right type.
|
|
//
|
|
TIntermBinary* node = addBinaryNode(op, left, right, loc);
|
|
if (! promote(node))
|
|
return nullptr;
|
|
|
|
node->updatePrecision();
|
|
|
|
//
|
|
// If they are both (non-specialization) constants, they must be folded.
|
|
// (Unless it's the sequence (comma) operator, but that's handled in addComma().)
|
|
//
|
|
TIntermConstantUnion *leftTempConstant = node->getLeft()->getAsConstantUnion();
|
|
TIntermConstantUnion *rightTempConstant = node->getRight()->getAsConstantUnion();
|
|
if (leftTempConstant && rightTempConstant) {
|
|
TIntermTyped* folded = leftTempConstant->fold(node->getOp(), rightTempConstant);
|
|
if (folded)
|
|
return folded;
|
|
}
|
|
|
|
// If can propagate spec-constantness and if the operation is an allowed
|
|
// specialization-constant operation, make a spec-constant.
|
|
if (specConstantPropagates(*node->getLeft(), *node->getRight()) && isSpecializationOperation(*node))
|
|
node->getWritableType().getQualifier().makeSpecConstant();
|
|
|
|
// If must propagate nonuniform, make a nonuniform.
|
|
if ((node->getLeft()->getQualifier().isNonUniform() || node->getRight()->getQualifier().isNonUniform()) &&
|
|
isNonuniformPropagating(node->getOp()))
|
|
node->getWritableType().getQualifier().nonUniform = true;
|
|
|
|
return node;
|
|
}
|
|
|
|
//
|
|
// Low level: add binary node (no promotions or other argument modifications)
|
|
//
|
|
TIntermBinary* TIntermediate::addBinaryNode(TOperator op, TIntermTyped* left, TIntermTyped* right,
|
|
const TSourceLoc& loc) const
|
|
{
|
|
// build the node
|
|
TIntermBinary* node = new TIntermBinary(op);
|
|
node->setLoc(loc.line != 0 ? loc : left->getLoc());
|
|
node->setLeft(left);
|
|
node->setRight(right);
|
|
|
|
return node;
|
|
}
|
|
|
|
//
|
|
// like non-type form, but sets node's type.
|
|
//
|
|
TIntermBinary* TIntermediate::addBinaryNode(TOperator op, TIntermTyped* left, TIntermTyped* right,
|
|
const TSourceLoc& loc, const TType& type) const
|
|
{
|
|
TIntermBinary* node = addBinaryNode(op, left, right, loc);
|
|
node->setType(type);
|
|
return node;
|
|
}
|
|
|
|
//
|
|
// Low level: add unary node (no promotions or other argument modifications)
|
|
//
|
|
TIntermUnary* TIntermediate::addUnaryNode(TOperator op, TIntermTyped* child, const TSourceLoc& loc) const
|
|
{
|
|
TIntermUnary* node = new TIntermUnary(op);
|
|
node->setLoc(loc.line != 0 ? loc : child->getLoc());
|
|
node->setOperand(child);
|
|
|
|
return node;
|
|
}
|
|
|
|
//
|
|
// like non-type form, but sets node's type.
|
|
//
|
|
TIntermUnary* TIntermediate::addUnaryNode(TOperator op, TIntermTyped* child, const TSourceLoc& loc, const TType& type)
|
|
const
|
|
{
|
|
TIntermUnary* node = addUnaryNode(op, child, loc);
|
|
node->setType(type);
|
|
return node;
|
|
}
|
|
|
|
//
|
|
// Connect two nodes through an assignment.
|
|
//
|
|
// Returns the added node.
|
|
//
|
|
// Returns nullptr if the 'right' type could not be converted to match the 'left' type,
|
|
// or the resulting operation cannot be properly promoted.
|
|
//
|
|
TIntermTyped* TIntermediate::addAssign(TOperator op, TIntermTyped* left, TIntermTyped* right,
|
|
const TSourceLoc& loc)
|
|
{
|
|
// No block assignment
|
|
if (left->getType().getBasicType() == EbtBlock || right->getType().getBasicType() == EbtBlock)
|
|
return nullptr;
|
|
|
|
// Convert "reference += int" to "reference = reference + int". We need this because the
|
|
// "reference + int" calculation involves a cast back to the original type, which makes it
|
|
// not an lvalue.
|
|
if ((op == EOpAddAssign || op == EOpSubAssign) && left->isReference()) {
|
|
if (!(right->getType().isScalar() && right->getType().isIntegerDomain()))
|
|
return nullptr;
|
|
|
|
TIntermTyped* node = addBinaryMath(op == EOpAddAssign ? EOpAdd : EOpSub, left, right, loc);
|
|
if (!node)
|
|
return nullptr;
|
|
|
|
TIntermSymbol* symbol = left->getAsSymbolNode();
|
|
left = addSymbol(*symbol);
|
|
|
|
node = addAssign(EOpAssign, left, node, loc);
|
|
return node;
|
|
}
|
|
|
|
//
|
|
// Like adding binary math, except the conversion can only go
|
|
// from right to left.
|
|
//
|
|
|
|
// convert base types, nullptr return means not possible
|
|
right = addConversion(op, left->getType(), right);
|
|
if (right == nullptr)
|
|
return nullptr;
|
|
|
|
// convert shape
|
|
right = addUniShapeConversion(op, left->getType(), right);
|
|
|
|
// build the node
|
|
TIntermBinary* node = addBinaryNode(op, left, right, loc);
|
|
|
|
if (! promote(node))
|
|
return nullptr;
|
|
|
|
node->updatePrecision();
|
|
|
|
return node;
|
|
}
|
|
|
|
//
|
|
// Connect two nodes through an index operator, where the left node is the base
|
|
// of an array or struct, and the right node is a direct or indirect offset.
|
|
//
|
|
// Returns the added node.
|
|
// The caller should set the type of the returned node.
|
|
//
|
|
TIntermTyped* TIntermediate::addIndex(TOperator op, TIntermTyped* base, TIntermTyped* index,
|
|
const TSourceLoc& loc)
|
|
{
|
|
// caller should set the type
|
|
return addBinaryNode(op, base, index, loc);
|
|
}
|
|
|
|
//
|
|
// Add one node as the parent of another that it operates on.
|
|
//
|
|
// Returns the added node.
|
|
//
|
|
TIntermTyped* TIntermediate::addUnaryMath(TOperator op, TIntermTyped* child,
|
|
const TSourceLoc& loc)
|
|
{
|
|
if (child == 0)
|
|
return nullptr;
|
|
|
|
if (child->getType().getBasicType() == EbtBlock)
|
|
return nullptr;
|
|
|
|
switch (op) {
|
|
case EOpLogicalNot:
|
|
if (getSource() == EShSourceHlsl) {
|
|
break; // HLSL can promote logical not
|
|
}
|
|
|
|
if (child->getType().getBasicType() != EbtBool || child->getType().isMatrix() || child->getType().isArray() || child->getType().isVector()) {
|
|
return nullptr;
|
|
}
|
|
break;
|
|
|
|
case EOpPostIncrement:
|
|
case EOpPreIncrement:
|
|
case EOpPostDecrement:
|
|
case EOpPreDecrement:
|
|
case EOpNegative:
|
|
if (child->getType().getBasicType() == EbtStruct || child->getType().isArray())
|
|
return nullptr;
|
|
default: break; // some compilers want this
|
|
}
|
|
|
|
//
|
|
// Do we need to promote the operand?
|
|
//
|
|
TBasicType newType = EbtVoid;
|
|
switch (op) {
|
|
case EOpConstructBool: newType = EbtBool; break;
|
|
case EOpConstructFloat: newType = EbtFloat; break;
|
|
case EOpConstructInt: newType = EbtInt; break;
|
|
case EOpConstructUint: newType = EbtUint; break;
|
|
#ifndef GLSLANG_WEB
|
|
case EOpConstructInt8: newType = EbtInt8; break;
|
|
case EOpConstructUint8: newType = EbtUint8; break;
|
|
case EOpConstructInt16: newType = EbtInt16; break;
|
|
case EOpConstructUint16: newType = EbtUint16; break;
|
|
case EOpConstructInt64: newType = EbtInt64; break;
|
|
case EOpConstructUint64: newType = EbtUint64; break;
|
|
case EOpConstructDouble: newType = EbtDouble; break;
|
|
case EOpConstructFloat16: newType = EbtFloat16; break;
|
|
#endif
|
|
default: break; // some compilers want this
|
|
}
|
|
|
|
if (newType != EbtVoid) {
|
|
child = addConversion(op, TType(newType, EvqTemporary, child->getVectorSize(),
|
|
child->getMatrixCols(),
|
|
child->getMatrixRows(),
|
|
child->isVector()),
|
|
child);
|
|
if (child == nullptr)
|
|
return nullptr;
|
|
}
|
|
|
|
//
|
|
// For constructors, we are now done, it was all in the conversion.
|
|
// TODO: but, did this bypass constant folding?
|
|
//
|
|
switch (op) {
|
|
case EOpConstructInt8:
|
|
case EOpConstructUint8:
|
|
case EOpConstructInt16:
|
|
case EOpConstructUint16:
|
|
case EOpConstructInt:
|
|
case EOpConstructUint:
|
|
case EOpConstructInt64:
|
|
case EOpConstructUint64:
|
|
case EOpConstructBool:
|
|
case EOpConstructFloat:
|
|
case EOpConstructDouble:
|
|
case EOpConstructFloat16:
|
|
return child;
|
|
default: break; // some compilers want this
|
|
}
|
|
|
|
//
|
|
// Make a new node for the operator.
|
|
//
|
|
TIntermUnary* node = addUnaryNode(op, child, loc);
|
|
|
|
if (! promote(node))
|
|
return nullptr;
|
|
|
|
node->updatePrecision();
|
|
|
|
// If it's a (non-specialization) constant, it must be folded.
|
|
if (node->getOperand()->getAsConstantUnion())
|
|
return node->getOperand()->getAsConstantUnion()->fold(op, node->getType());
|
|
|
|
// If it's a specialization constant, the result is too,
|
|
// if the operation is allowed for specialization constants.
|
|
if (node->getOperand()->getType().getQualifier().isSpecConstant() && isSpecializationOperation(*node))
|
|
node->getWritableType().getQualifier().makeSpecConstant();
|
|
|
|
// If must propagate nonuniform, make a nonuniform.
|
|
if (node->getOperand()->getQualifier().isNonUniform() && isNonuniformPropagating(node->getOp()))
|
|
node->getWritableType().getQualifier().nonUniform = true;
|
|
|
|
return node;
|
|
}
|
|
|
|
TIntermTyped* TIntermediate::addBuiltInFunctionCall(const TSourceLoc& loc, TOperator op, bool unary,
|
|
TIntermNode* childNode, const TType& returnType)
|
|
{
|
|
if (unary) {
|
|
//
|
|
// Treat it like a unary operator.
|
|
// addUnaryMath() should get the type correct on its own;
|
|
// including constness (which would differ from the prototype).
|
|
//
|
|
TIntermTyped* child = childNode->getAsTyped();
|
|
if (child == nullptr)
|
|
return nullptr;
|
|
|
|
if (child->getAsConstantUnion()) {
|
|
TIntermTyped* folded = child->getAsConstantUnion()->fold(op, returnType);
|
|
if (folded)
|
|
return folded;
|
|
}
|
|
|
|
return addUnaryNode(op, child, child->getLoc(), returnType);
|
|
} else {
|
|
// setAggregateOperater() calls fold() for constant folding
|
|
TIntermTyped* node = setAggregateOperator(childNode, op, returnType, loc);
|
|
|
|
return node;
|
|
}
|
|
}
|
|
|
|
//
|
|
// This is the safe way to change the operator on an aggregate, as it
|
|
// does lots of error checking and fixing. Especially for establishing
|
|
// a function call's operation on its set of parameters. Sequences
|
|
// of instructions are also aggregates, but they just directly set
|
|
// their operator to EOpSequence.
|
|
//
|
|
// Returns an aggregate node, which could be the one passed in if
|
|
// it was already an aggregate.
|
|
//
|
|
TIntermTyped* TIntermediate::setAggregateOperator(TIntermNode* node, TOperator op, const TType& type,
|
|
const TSourceLoc& loc)
|
|
{
|
|
TIntermAggregate* aggNode;
|
|
|
|
//
|
|
// Make sure we have an aggregate. If not turn it into one.
|
|
//
|
|
if (node != nullptr) {
|
|
aggNode = node->getAsAggregate();
|
|
if (aggNode == nullptr || aggNode->getOp() != EOpNull) {
|
|
//
|
|
// Make an aggregate containing this node.
|
|
//
|
|
aggNode = new TIntermAggregate();
|
|
aggNode->getSequence().push_back(node);
|
|
}
|
|
} else
|
|
aggNode = new TIntermAggregate();
|
|
|
|
//
|
|
// Set the operator.
|
|
//
|
|
aggNode->setOperator(op);
|
|
if (loc.line != 0 || node != nullptr)
|
|
aggNode->setLoc(loc.line != 0 ? loc : node->getLoc());
|
|
|
|
aggNode->setType(type);
|
|
|
|
return fold(aggNode);
|
|
}
|
|
|
|
bool TIntermediate::isConversionAllowed(TOperator op, TIntermTyped* node) const
|
|
{
|
|
//
|
|
// Does the base type even allow the operation?
|
|
//
|
|
switch (node->getBasicType()) {
|
|
case EbtVoid:
|
|
return false;
|
|
case EbtAtomicUint:
|
|
case EbtSampler:
|
|
case EbtAccStruct:
|
|
// opaque types can be passed to functions
|
|
if (op == EOpFunction)
|
|
break;
|
|
|
|
// HLSL can assign samplers directly (no constructor)
|
|
if (getSource() == EShSourceHlsl && node->getBasicType() == EbtSampler)
|
|
break;
|
|
|
|
// samplers can get assigned via a sampler constructor
|
|
// (well, not yet, but code in the rest of this function is ready for it)
|
|
if (node->getBasicType() == EbtSampler && op == EOpAssign &&
|
|
node->getAsOperator() != nullptr && node->getAsOperator()->getOp() == EOpConstructTextureSampler)
|
|
break;
|
|
|
|
// otherwise, opaque types can't even be operated on, let alone converted
|
|
return false;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool TIntermediate::buildConvertOp(TBasicType dst, TBasicType src, TOperator& newOp) const
|
|
{
|
|
switch (dst) {
|
|
#ifndef GLSLANG_WEB
|
|
case EbtDouble:
|
|
switch (src) {
|
|
case EbtUint: newOp = EOpConvUintToDouble; break;
|
|
case EbtBool: newOp = EOpConvBoolToDouble; break;
|
|
case EbtFloat: newOp = EOpConvFloatToDouble; break;
|
|
case EbtInt: newOp = EOpConvIntToDouble; break;
|
|
case EbtInt8: newOp = EOpConvInt8ToDouble; break;
|
|
case EbtUint8: newOp = EOpConvUint8ToDouble; break;
|
|
case EbtInt16: newOp = EOpConvInt16ToDouble; break;
|
|
case EbtUint16: newOp = EOpConvUint16ToDouble; break;
|
|
case EbtFloat16: newOp = EOpConvFloat16ToDouble; break;
|
|
case EbtInt64: newOp = EOpConvInt64ToDouble; break;
|
|
case EbtUint64: newOp = EOpConvUint64ToDouble; break;
|
|
default:
|
|
return false;
|
|
}
|
|
break;
|
|
#endif
|
|
case EbtFloat:
|
|
switch (src) {
|
|
case EbtInt: newOp = EOpConvIntToFloat; break;
|
|
case EbtUint: newOp = EOpConvUintToFloat; break;
|
|
case EbtBool: newOp = EOpConvBoolToFloat; break;
|
|
#ifndef GLSLANG_WEB
|
|
case EbtDouble: newOp = EOpConvDoubleToFloat; break;
|
|
case EbtInt8: newOp = EOpConvInt8ToFloat; break;
|
|
case EbtUint8: newOp = EOpConvUint8ToFloat; break;
|
|
case EbtInt16: newOp = EOpConvInt16ToFloat; break;
|
|
case EbtUint16: newOp = EOpConvUint16ToFloat; break;
|
|
case EbtFloat16: newOp = EOpConvFloat16ToFloat; break;
|
|
case EbtInt64: newOp = EOpConvInt64ToFloat; break;
|
|
case EbtUint64: newOp = EOpConvUint64ToFloat; break;
|
|
#endif
|
|
default:
|
|
return false;
|
|
}
|
|
break;
|
|
#ifndef GLSLANG_WEB
|
|
case EbtFloat16:
|
|
switch (src) {
|
|
case EbtInt8: newOp = EOpConvInt8ToFloat16; break;
|
|
case EbtUint8: newOp = EOpConvUint8ToFloat16; break;
|
|
case EbtInt16: newOp = EOpConvInt16ToFloat16; break;
|
|
case EbtUint16: newOp = EOpConvUint16ToFloat16; break;
|
|
case EbtInt: newOp = EOpConvIntToFloat16; break;
|
|
case EbtUint: newOp = EOpConvUintToFloat16; break;
|
|
case EbtBool: newOp = EOpConvBoolToFloat16; break;
|
|
case EbtFloat: newOp = EOpConvFloatToFloat16; break;
|
|
case EbtDouble: newOp = EOpConvDoubleToFloat16; break;
|
|
case EbtInt64: newOp = EOpConvInt64ToFloat16; break;
|
|
case EbtUint64: newOp = EOpConvUint64ToFloat16; break;
|
|
default:
|
|
return false;
|
|
}
|
|
break;
|
|
#endif
|
|
case EbtBool:
|
|
switch (src) {
|
|
case EbtInt: newOp = EOpConvIntToBool; break;
|
|
case EbtUint: newOp = EOpConvUintToBool; break;
|
|
case EbtFloat: newOp = EOpConvFloatToBool; break;
|
|
#ifndef GLSLANG_WEB
|
|
case EbtDouble: newOp = EOpConvDoubleToBool; break;
|
|
case EbtInt8: newOp = EOpConvInt8ToBool; break;
|
|
case EbtUint8: newOp = EOpConvUint8ToBool; break;
|
|
case EbtInt16: newOp = EOpConvInt16ToBool; break;
|
|
case EbtUint16: newOp = EOpConvUint16ToBool; break;
|
|
case EbtFloat16: newOp = EOpConvFloat16ToBool; break;
|
|
case EbtInt64: newOp = EOpConvInt64ToBool; break;
|
|
case EbtUint64: newOp = EOpConvUint64ToBool; break;
|
|
#endif
|
|
default:
|
|
return false;
|
|
}
|
|
break;
|
|
#ifndef GLSLANG_WEB
|
|
case EbtInt8:
|
|
switch (src) {
|
|
case EbtUint8: newOp = EOpConvUint8ToInt8; break;
|
|
case EbtInt16: newOp = EOpConvInt16ToInt8; break;
|
|
case EbtUint16: newOp = EOpConvUint16ToInt8; break;
|
|
case EbtInt: newOp = EOpConvIntToInt8; break;
|
|
case EbtUint: newOp = EOpConvUintToInt8; break;
|
|
case EbtInt64: newOp = EOpConvInt64ToInt8; break;
|
|
case EbtUint64: newOp = EOpConvUint64ToInt8; break;
|
|
case EbtBool: newOp = EOpConvBoolToInt8; break;
|
|
case EbtFloat: newOp = EOpConvFloatToInt8; break;
|
|
case EbtDouble: newOp = EOpConvDoubleToInt8; break;
|
|
case EbtFloat16: newOp = EOpConvFloat16ToInt8; break;
|
|
default:
|
|
return false;
|
|
}
|
|
break;
|
|
case EbtUint8:
|
|
switch (src) {
|
|
case EbtInt8: newOp = EOpConvInt8ToUint8; break;
|
|
case EbtInt16: newOp = EOpConvInt16ToUint8; break;
|
|
case EbtUint16: newOp = EOpConvUint16ToUint8; break;
|
|
case EbtInt: newOp = EOpConvIntToUint8; break;
|
|
case EbtUint: newOp = EOpConvUintToUint8; break;
|
|
case EbtInt64: newOp = EOpConvInt64ToUint8; break;
|
|
case EbtUint64: newOp = EOpConvUint64ToUint8; break;
|
|
case EbtBool: newOp = EOpConvBoolToUint8; break;
|
|
case EbtFloat: newOp = EOpConvFloatToUint8; break;
|
|
case EbtDouble: newOp = EOpConvDoubleToUint8; break;
|
|
case EbtFloat16: newOp = EOpConvFloat16ToUint8; break;
|
|
default:
|
|
return false;
|
|
}
|
|
break;
|
|
|
|
case EbtInt16:
|
|
switch (src) {
|
|
case EbtUint8: newOp = EOpConvUint8ToInt16; break;
|
|
case EbtInt8: newOp = EOpConvInt8ToInt16; break;
|
|
case EbtUint16: newOp = EOpConvUint16ToInt16; break;
|
|
case EbtInt: newOp = EOpConvIntToInt16; break;
|
|
case EbtUint: newOp = EOpConvUintToInt16; break;
|
|
case EbtInt64: newOp = EOpConvInt64ToInt16; break;
|
|
case EbtUint64: newOp = EOpConvUint64ToInt16; break;
|
|
case EbtBool: newOp = EOpConvBoolToInt16; break;
|
|
case EbtFloat: newOp = EOpConvFloatToInt16; break;
|
|
case EbtDouble: newOp = EOpConvDoubleToInt16; break;
|
|
case EbtFloat16: newOp = EOpConvFloat16ToInt16; break;
|
|
default:
|
|
return false;
|
|
}
|
|
break;
|
|
case EbtUint16:
|
|
switch (src) {
|
|
case EbtInt8: newOp = EOpConvInt8ToUint16; break;
|
|
case EbtUint8: newOp = EOpConvUint8ToUint16; break;
|
|
case EbtInt16: newOp = EOpConvInt16ToUint16; break;
|
|
case EbtInt: newOp = EOpConvIntToUint16; break;
|
|
case EbtUint: newOp = EOpConvUintToUint16; break;
|
|
case EbtInt64: newOp = EOpConvInt64ToUint16; break;
|
|
case EbtUint64: newOp = EOpConvUint64ToUint16; break;
|
|
case EbtBool: newOp = EOpConvBoolToUint16; break;
|
|
case EbtFloat: newOp = EOpConvFloatToUint16; break;
|
|
case EbtDouble: newOp = EOpConvDoubleToUint16; break;
|
|
case EbtFloat16: newOp = EOpConvFloat16ToUint16; break;
|
|
default:
|
|
return false;
|
|
}
|
|
break;
|
|
#endif
|
|
|
|
case EbtInt:
|
|
switch (src) {
|
|
case EbtUint: newOp = EOpConvUintToInt; break;
|
|
case EbtBool: newOp = EOpConvBoolToInt; break;
|
|
case EbtFloat: newOp = EOpConvFloatToInt; break;
|
|
#ifndef GLSLANG_WEB
|
|
case EbtInt8: newOp = EOpConvInt8ToInt; break;
|
|
case EbtUint8: newOp = EOpConvUint8ToInt; break;
|
|
case EbtInt16: newOp = EOpConvInt16ToInt; break;
|
|
case EbtUint16: newOp = EOpConvUint16ToInt; break;
|
|
case EbtDouble: newOp = EOpConvDoubleToInt; break;
|
|
case EbtFloat16: newOp = EOpConvFloat16ToInt; break;
|
|
case EbtInt64: newOp = EOpConvInt64ToInt; break;
|
|
case EbtUint64: newOp = EOpConvUint64ToInt; break;
|
|
#endif
|
|
default:
|
|
return false;
|
|
}
|
|
break;
|
|
case EbtUint:
|
|
switch (src) {
|
|
case EbtInt: newOp = EOpConvIntToUint; break;
|
|
case EbtBool: newOp = EOpConvBoolToUint; break;
|
|
case EbtFloat: newOp = EOpConvFloatToUint; break;
|
|
#ifndef GLSLANG_WEB
|
|
case EbtInt8: newOp = EOpConvInt8ToUint; break;
|
|
case EbtUint8: newOp = EOpConvUint8ToUint; break;
|
|
case EbtInt16: newOp = EOpConvInt16ToUint; break;
|
|
case EbtUint16: newOp = EOpConvUint16ToUint; break;
|
|
case EbtDouble: newOp = EOpConvDoubleToUint; break;
|
|
case EbtFloat16: newOp = EOpConvFloat16ToUint; break;
|
|
case EbtInt64: newOp = EOpConvInt64ToUint; break;
|
|
case EbtUint64: newOp = EOpConvUint64ToUint; break;
|
|
#endif
|
|
default:
|
|
return false;
|
|
}
|
|
break;
|
|
#ifndef GLSLANG_WEB
|
|
case EbtInt64:
|
|
switch (src) {
|
|
case EbtInt8: newOp = EOpConvInt8ToInt64; break;
|
|
case EbtUint8: newOp = EOpConvUint8ToInt64; break;
|
|
case EbtInt16: newOp = EOpConvInt16ToInt64; break;
|
|
case EbtUint16: newOp = EOpConvUint16ToInt64; break;
|
|
case EbtInt: newOp = EOpConvIntToInt64; break;
|
|
case EbtUint: newOp = EOpConvUintToInt64; break;
|
|
case EbtBool: newOp = EOpConvBoolToInt64; break;
|
|
case EbtFloat: newOp = EOpConvFloatToInt64; break;
|
|
case EbtDouble: newOp = EOpConvDoubleToInt64; break;
|
|
case EbtFloat16: newOp = EOpConvFloat16ToInt64; break;
|
|
case EbtUint64: newOp = EOpConvUint64ToInt64; break;
|
|
default:
|
|
return false;
|
|
}
|
|
break;
|
|
case EbtUint64:
|
|
switch (src) {
|
|
case EbtInt8: newOp = EOpConvInt8ToUint64; break;
|
|
case EbtUint8: newOp = EOpConvUint8ToUint64; break;
|
|
case EbtInt16: newOp = EOpConvInt16ToUint64; break;
|
|
case EbtUint16: newOp = EOpConvUint16ToUint64; break;
|
|
case EbtInt: newOp = EOpConvIntToUint64; break;
|
|
case EbtUint: newOp = EOpConvUintToUint64; break;
|
|
case EbtBool: newOp = EOpConvBoolToUint64; break;
|
|
case EbtFloat: newOp = EOpConvFloatToUint64; break;
|
|
case EbtDouble: newOp = EOpConvDoubleToUint64; break;
|
|
case EbtFloat16: newOp = EOpConvFloat16ToUint64; break;
|
|
case EbtInt64: newOp = EOpConvInt64ToUint64; break;
|
|
default:
|
|
return false;
|
|
}
|
|
break;
|
|
#endif
|
|
default:
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
// This is 'mechanism' here, it does any conversion told.
|
|
// It is about basic type, not about shape.
|
|
// The policy comes from the shader or the calling code.
|
|
TIntermTyped* TIntermediate::createConversion(TBasicType convertTo, TIntermTyped* node) const
|
|
{
|
|
//
|
|
// Add a new newNode for the conversion.
|
|
//
|
|
|
|
#ifndef GLSLANG_WEB
|
|
bool convertToIntTypes = (convertTo == EbtInt8 || convertTo == EbtUint8 ||
|
|
convertTo == EbtInt16 || convertTo == EbtUint16 ||
|
|
convertTo == EbtInt || convertTo == EbtUint ||
|
|
convertTo == EbtInt64 || convertTo == EbtUint64);
|
|
|
|
bool convertFromIntTypes = (node->getBasicType() == EbtInt8 || node->getBasicType() == EbtUint8 ||
|
|
node->getBasicType() == EbtInt16 || node->getBasicType() == EbtUint16 ||
|
|
node->getBasicType() == EbtInt || node->getBasicType() == EbtUint ||
|
|
node->getBasicType() == EbtInt64 || node->getBasicType() == EbtUint64);
|
|
|
|
bool convertToFloatTypes = (convertTo == EbtFloat16 || convertTo == EbtFloat || convertTo == EbtDouble);
|
|
|
|
bool convertFromFloatTypes = (node->getBasicType() == EbtFloat16 ||
|
|
node->getBasicType() == EbtFloat ||
|
|
node->getBasicType() == EbtDouble);
|
|
|
|
if (((convertTo == EbtInt8 || convertTo == EbtUint8) && ! convertFromIntTypes) ||
|
|
((node->getBasicType() == EbtInt8 || node->getBasicType() == EbtUint8) && ! convertToIntTypes)) {
|
|
if (! getArithemeticInt8Enabled()) {
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
if (((convertTo == EbtInt16 || convertTo == EbtUint16) && ! convertFromIntTypes) ||
|
|
((node->getBasicType() == EbtInt16 || node->getBasicType() == EbtUint16) && ! convertToIntTypes)) {
|
|
if (! getArithemeticInt16Enabled()) {
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
if ((convertTo == EbtFloat16 && ! convertFromFloatTypes) ||
|
|
(node->getBasicType() == EbtFloat16 && ! convertToFloatTypes)) {
|
|
if (! getArithemeticFloat16Enabled()) {
|
|
return nullptr;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
TIntermUnary* newNode = nullptr;
|
|
TOperator newOp = EOpNull;
|
|
if (!buildConvertOp(convertTo, node->getBasicType(), newOp)) {
|
|
return nullptr;
|
|
}
|
|
|
|
TType newType(convertTo, EvqTemporary, node->getVectorSize(), node->getMatrixCols(), node->getMatrixRows());
|
|
newNode = addUnaryNode(newOp, node, node->getLoc(), newType);
|
|
|
|
if (node->getAsConstantUnion()) {
|
|
#ifndef GLSLANG_WEB
|
|
// 8/16-bit storage extensions don't support 8/16-bit constants, so don't fold conversions
|
|
// to those types
|
|
if ((getArithemeticInt8Enabled() || !(convertTo == EbtInt8 || convertTo == EbtUint8)) &&
|
|
(getArithemeticInt16Enabled() || !(convertTo == EbtInt16 || convertTo == EbtUint16)) &&
|
|
(getArithemeticFloat16Enabled() || !(convertTo == EbtFloat16)))
|
|
#endif
|
|
{
|
|
TIntermTyped* folded = node->getAsConstantUnion()->fold(newOp, newType);
|
|
if (folded)
|
|
return folded;
|
|
}
|
|
}
|
|
|
|
// Propagate specialization-constant-ness, if allowed
|
|
if (node->getType().getQualifier().isSpecConstant() && isSpecializationOperation(*newNode))
|
|
newNode->getWritableType().getQualifier().makeSpecConstant();
|
|
|
|
return newNode;
|
|
}
|
|
|
|
TIntermTyped* TIntermediate::addConversion(TBasicType convertTo, TIntermTyped* node) const
|
|
{
|
|
return createConversion(convertTo, node);
|
|
}
|
|
|
|
// For converting a pair of operands to a binary operation to compatible
|
|
// types with each other, relative to the operation in 'op'.
|
|
// This does not cover assignment operations, which is asymmetric in that the
|
|
// left type is not changeable.
|
|
// See addConversion(op, type, node) for assignments and unary operation
|
|
// conversions.
|
|
//
|
|
// Generally, this is focused on basic type conversion, not shape conversion.
|
|
// See addShapeConversion() for shape conversions.
|
|
//
|
|
// Returns the converted pair of nodes.
|
|
// Returns <nullptr, nullptr> when there is no conversion.
|
|
std::tuple<TIntermTyped*, TIntermTyped*>
|
|
TIntermediate::addPairConversion(TOperator op, TIntermTyped* node0, TIntermTyped* node1)
|
|
{
|
|
if (!isConversionAllowed(op, node0) || !isConversionAllowed(op, node1))
|
|
return std::make_tuple(nullptr, nullptr);
|
|
|
|
if (node0->getType() != node1->getType()) {
|
|
// If differing structure, then no conversions.
|
|
if (node0->isStruct() || node1->isStruct())
|
|
return std::make_tuple(nullptr, nullptr);
|
|
|
|
// If differing arrays, then no conversions.
|
|
if (node0->getType().isArray() || node1->getType().isArray())
|
|
return std::make_tuple(nullptr, nullptr);
|
|
|
|
// No implicit conversions for operations involving cooperative matrices
|
|
if (node0->getType().isCoopMat() || node1->getType().isCoopMat())
|
|
return std::make_tuple(node0, node1);
|
|
}
|
|
|
|
auto promoteTo = std::make_tuple(EbtNumTypes, EbtNumTypes);
|
|
|
|
switch (op) {
|
|
//
|
|
// List all the binary ops that can implicitly convert one operand to the other's type;
|
|
// This implements the 'policy' for implicit type conversion.
|
|
//
|
|
case EOpLessThan:
|
|
case EOpGreaterThan:
|
|
case EOpLessThanEqual:
|
|
case EOpGreaterThanEqual:
|
|
case EOpEqual:
|
|
case EOpNotEqual:
|
|
|
|
case EOpAdd:
|
|
case EOpSub:
|
|
case EOpMul:
|
|
case EOpDiv:
|
|
case EOpMod:
|
|
|
|
case EOpVectorTimesScalar:
|
|
case EOpVectorTimesMatrix:
|
|
case EOpMatrixTimesVector:
|
|
case EOpMatrixTimesScalar:
|
|
|
|
case EOpAnd:
|
|
case EOpInclusiveOr:
|
|
case EOpExclusiveOr:
|
|
|
|
case EOpSequence: // used by ?:
|
|
|
|
if (node0->getBasicType() == node1->getBasicType())
|
|
return std::make_tuple(node0, node1);
|
|
|
|
promoteTo = getConversionDestinationType(node0->getBasicType(), node1->getBasicType(), op);
|
|
if (std::get<0>(promoteTo) == EbtNumTypes || std::get<1>(promoteTo) == EbtNumTypes)
|
|
return std::make_tuple(nullptr, nullptr);
|
|
|
|
break;
|
|
|
|
case EOpLogicalAnd:
|
|
case EOpLogicalOr:
|
|
case EOpLogicalXor:
|
|
if (getSource() == EShSourceHlsl)
|
|
promoteTo = std::make_tuple(EbtBool, EbtBool);
|
|
else
|
|
return std::make_tuple(node0, node1);
|
|
break;
|
|
|
|
// There are no conversions needed for GLSL; the shift amount just needs to be an
|
|
// integer type, as does the base.
|
|
// HLSL can promote bools to ints to make this work.
|
|
case EOpLeftShift:
|
|
case EOpRightShift:
|
|
if (getSource() == EShSourceHlsl) {
|
|
TBasicType node0BasicType = node0->getBasicType();
|
|
if (node0BasicType == EbtBool)
|
|
node0BasicType = EbtInt;
|
|
if (node1->getBasicType() == EbtBool)
|
|
promoteTo = std::make_tuple(node0BasicType, EbtInt);
|
|
else
|
|
promoteTo = std::make_tuple(node0BasicType, node1->getBasicType());
|
|
} else {
|
|
if (isTypeInt(node0->getBasicType()) && isTypeInt(node1->getBasicType()))
|
|
return std::make_tuple(node0, node1);
|
|
else
|
|
return std::make_tuple(nullptr, nullptr);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
if (node0->getType() == node1->getType())
|
|
return std::make_tuple(node0, node1);
|
|
|
|
return std::make_tuple(nullptr, nullptr);
|
|
}
|
|
|
|
TIntermTyped* newNode0;
|
|
TIntermTyped* newNode1;
|
|
|
|
if (std::get<0>(promoteTo) != node0->getType().getBasicType()) {
|
|
if (node0->getAsConstantUnion())
|
|
newNode0 = promoteConstantUnion(std::get<0>(promoteTo), node0->getAsConstantUnion());
|
|
else
|
|
newNode0 = createConversion(std::get<0>(promoteTo), node0);
|
|
} else
|
|
newNode0 = node0;
|
|
|
|
if (std::get<1>(promoteTo) != node1->getType().getBasicType()) {
|
|
if (node1->getAsConstantUnion())
|
|
newNode1 = promoteConstantUnion(std::get<1>(promoteTo), node1->getAsConstantUnion());
|
|
else
|
|
newNode1 = createConversion(std::get<1>(promoteTo), node1);
|
|
} else
|
|
newNode1 = node1;
|
|
|
|
return std::make_tuple(newNode0, newNode1);
|
|
}
|
|
|
|
//
|
|
// Convert the node's type to the given type, as allowed by the operation involved: 'op'.
|
|
// For implicit conversions, 'op' is not the requested conversion, it is the explicit
|
|
// operation requiring the implicit conversion.
|
|
//
|
|
// Binary operation conversions should be handled by addConversion(op, node, node), not here.
|
|
//
|
|
// Returns a node representing the conversion, which could be the same
|
|
// node passed in if no conversion was needed.
|
|
//
|
|
// Generally, this is focused on basic type conversion, not shape conversion.
|
|
// See addShapeConversion() for shape conversions.
|
|
//
|
|
// Return nullptr if a conversion can't be done.
|
|
//
|
|
TIntermTyped* TIntermediate::addConversion(TOperator op, const TType& type, TIntermTyped* node)
|
|
{
|
|
if (!isConversionAllowed(op, node))
|
|
return nullptr;
|
|
|
|
// Otherwise, if types are identical, no problem
|
|
if (type == node->getType())
|
|
return node;
|
|
|
|
// If one's a structure, then no conversions.
|
|
if (type.isStruct() || node->isStruct())
|
|
return nullptr;
|
|
|
|
// If one's an array, then no conversions.
|
|
if (type.isArray() || node->getType().isArray())
|
|
return nullptr;
|
|
|
|
// Note: callers are responsible for other aspects of shape,
|
|
// like vector and matrix sizes.
|
|
|
|
switch (op) {
|
|
//
|
|
// Explicit conversions (unary operations)
|
|
//
|
|
case EOpConstructBool:
|
|
case EOpConstructFloat:
|
|
case EOpConstructInt:
|
|
case EOpConstructUint:
|
|
#ifndef GLSLANG_WEB
|
|
case EOpConstructDouble:
|
|
case EOpConstructFloat16:
|
|
case EOpConstructInt8:
|
|
case EOpConstructUint8:
|
|
case EOpConstructInt16:
|
|
case EOpConstructUint16:
|
|
case EOpConstructInt64:
|
|
case EOpConstructUint64:
|
|
break;
|
|
|
|
#endif
|
|
|
|
//
|
|
// Implicit conversions
|
|
//
|
|
case EOpLogicalNot:
|
|
|
|
case EOpFunctionCall:
|
|
|
|
case EOpReturn:
|
|
case EOpAssign:
|
|
case EOpAddAssign:
|
|
case EOpSubAssign:
|
|
case EOpMulAssign:
|
|
case EOpVectorTimesScalarAssign:
|
|
case EOpMatrixTimesScalarAssign:
|
|
case EOpDivAssign:
|
|
case EOpModAssign:
|
|
case EOpAndAssign:
|
|
case EOpInclusiveOrAssign:
|
|
case EOpExclusiveOrAssign:
|
|
|
|
case EOpAtan:
|
|
case EOpClamp:
|
|
case EOpCross:
|
|
case EOpDistance:
|
|
case EOpDot:
|
|
case EOpDst:
|
|
case EOpFaceForward:
|
|
case EOpFma:
|
|
case EOpFrexp:
|
|
case EOpLdexp:
|
|
case EOpMix:
|
|
case EOpLit:
|
|
case EOpMax:
|
|
case EOpMin:
|
|
case EOpMod:
|
|
case EOpModf:
|
|
case EOpPow:
|
|
case EOpReflect:
|
|
case EOpRefract:
|
|
case EOpSmoothStep:
|
|
case EOpStep:
|
|
|
|
case EOpSequence:
|
|
case EOpConstructStruct:
|
|
case EOpConstructCooperativeMatrix:
|
|
|
|
if (type.isReference() || node->getType().isReference()) {
|
|
// types must match to assign a reference
|
|
if (type == node->getType())
|
|
return node;
|
|
else
|
|
return nullptr;
|
|
}
|
|
|
|
if (type.getBasicType() == node->getType().getBasicType())
|
|
return node;
|
|
|
|
if (! canImplicitlyPromote(node->getBasicType(), type.getBasicType(), op))
|
|
return nullptr;
|
|
break;
|
|
|
|
// For GLSL, there are no conversions needed; the shift amount just needs to be an
|
|
// integer type, as do the base/result.
|
|
// HLSL can convert the shift from a bool to an int.
|
|
case EOpLeftShiftAssign:
|
|
case EOpRightShiftAssign:
|
|
{
|
|
if (!(getSource() == EShSourceHlsl && node->getType().getBasicType() == EbtBool)) {
|
|
if (isTypeInt(type.getBasicType()) && isTypeInt(node->getBasicType()))
|
|
return node;
|
|
else
|
|
return nullptr;
|
|
}
|
|
break;
|
|
}
|
|
|
|
default:
|
|
// default is to require a match; all exceptions should have case statements above
|
|
|
|
if (type.getBasicType() == node->getType().getBasicType())
|
|
return node;
|
|
else
|
|
return nullptr;
|
|
}
|
|
|
|
bool canPromoteConstant = true;
|
|
#ifndef GLSLANG_WEB
|
|
// GL_EXT_shader_16bit_storage can't do OpConstantComposite with
|
|
// 16-bit types, so disable promotion for those types.
|
|
// Many issues with this, from JohnK:
|
|
// - this isn't really right to discuss SPIR-V here
|
|
// - this could easily be entirely about scalars, so is overstepping
|
|
// - we should be looking at what the shader asked for, and saying whether or
|
|
// not it can be done, in the parser, by calling requireExtensions(), not
|
|
// changing language sementics on the fly by asking what extensions are in use
|
|
// - at the time of this writing (14-Aug-2020), no test results are changed by this.
|
|
switch (op) {
|
|
case EOpConstructFloat16:
|
|
canPromoteConstant = numericFeatures.contains(TNumericFeatures::shader_explicit_arithmetic_types) ||
|
|
numericFeatures.contains(TNumericFeatures::shader_explicit_arithmetic_types_float16);
|
|
break;
|
|
case EOpConstructInt8:
|
|
case EOpConstructUint8:
|
|
canPromoteConstant = numericFeatures.contains(TNumericFeatures::shader_explicit_arithmetic_types) ||
|
|
numericFeatures.contains(TNumericFeatures::shader_explicit_arithmetic_types_int8);
|
|
break;
|
|
case EOpConstructInt16:
|
|
case EOpConstructUint16:
|
|
canPromoteConstant = numericFeatures.contains(TNumericFeatures::shader_explicit_arithmetic_types) ||
|
|
numericFeatures.contains(TNumericFeatures::shader_explicit_arithmetic_types_int16);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
#endif
|
|
|
|
if (canPromoteConstant && node->getAsConstantUnion())
|
|
return promoteConstantUnion(type.getBasicType(), node->getAsConstantUnion());
|
|
|
|
//
|
|
// Add a new newNode for the conversion.
|
|
//
|
|
TIntermTyped* newNode = createConversion(type.getBasicType(), node);
|
|
|
|
return newNode;
|
|
}
|
|
|
|
// Convert the node's shape of type for the given type, as allowed by the
|
|
// operation involved: 'op'. This is for situations where there is only one
|
|
// direction to consider doing the shape conversion.
|
|
//
|
|
// This implements policy, it call addShapeConversion() for the mechanism.
|
|
//
|
|
// Generally, the AST represents allowed GLSL shapes, so this isn't needed
|
|
// for GLSL. Bad shapes are caught in conversion or promotion.
|
|
//
|
|
// Return 'node' if no conversion was done. Promotion handles final shape
|
|
// checking.
|
|
//
|
|
TIntermTyped* TIntermediate::addUniShapeConversion(TOperator op, const TType& type, TIntermTyped* node)
|
|
{
|
|
// some source languages don't do this
|
|
switch (getSource()) {
|
|
case EShSourceHlsl:
|
|
break;
|
|
case EShSourceGlsl:
|
|
default:
|
|
return node;
|
|
}
|
|
|
|
// some operations don't do this
|
|
switch (op) {
|
|
case EOpFunctionCall:
|
|
case EOpReturn:
|
|
break;
|
|
|
|
case EOpMulAssign:
|
|
// want to support vector *= scalar native ops in AST and lower, not smear, similarly for
|
|
// matrix *= scalar, etc.
|
|
|
|
case EOpAddAssign:
|
|
case EOpSubAssign:
|
|
case EOpDivAssign:
|
|
case EOpAndAssign:
|
|
case EOpInclusiveOrAssign:
|
|
case EOpExclusiveOrAssign:
|
|
case EOpRightShiftAssign:
|
|
case EOpLeftShiftAssign:
|
|
if (node->getVectorSize() == 1)
|
|
return node;
|
|
break;
|
|
|
|
case EOpAssign:
|
|
break;
|
|
|
|
case EOpMix:
|
|
break;
|
|
|
|
default:
|
|
return node;
|
|
}
|
|
|
|
return addShapeConversion(type, node);
|
|
}
|
|
|
|
// Convert the nodes' shapes to be compatible for the operation 'op'.
|
|
//
|
|
// This implements policy, it call addShapeConversion() for the mechanism.
|
|
//
|
|
// Generally, the AST represents allowed GLSL shapes, so this isn't needed
|
|
// for GLSL. Bad shapes are caught in conversion or promotion.
|
|
//
|
|
void TIntermediate::addBiShapeConversion(TOperator op, TIntermTyped*& lhsNode, TIntermTyped*& rhsNode)
|
|
{
|
|
// some source languages don't do this
|
|
switch (getSource()) {
|
|
case EShSourceHlsl:
|
|
break;
|
|
case EShSourceGlsl:
|
|
default:
|
|
return;
|
|
}
|
|
|
|
// some operations don't do this
|
|
// 'break' will mean attempt bidirectional conversion
|
|
switch (op) {
|
|
case EOpMulAssign:
|
|
case EOpAssign:
|
|
case EOpAddAssign:
|
|
case EOpSubAssign:
|
|
case EOpDivAssign:
|
|
case EOpAndAssign:
|
|
case EOpInclusiveOrAssign:
|
|
case EOpExclusiveOrAssign:
|
|
case EOpRightShiftAssign:
|
|
case EOpLeftShiftAssign:
|
|
// switch to unidirectional conversion (the lhs can't change)
|
|
rhsNode = addUniShapeConversion(op, lhsNode->getType(), rhsNode);
|
|
return;
|
|
|
|
case EOpMul:
|
|
// matrix multiply does not change shapes
|
|
if (lhsNode->isMatrix() && rhsNode->isMatrix())
|
|
return;
|
|
case EOpAdd:
|
|
case EOpSub:
|
|
case EOpDiv:
|
|
// want to support vector * scalar native ops in AST and lower, not smear, similarly for
|
|
// matrix * vector, etc.
|
|
if (lhsNode->getVectorSize() == 1 || rhsNode->getVectorSize() == 1)
|
|
return;
|
|
break;
|
|
|
|
case EOpRightShift:
|
|
case EOpLeftShift:
|
|
// can natively support the right operand being a scalar and the left a vector,
|
|
// but not the reverse
|
|
if (rhsNode->getVectorSize() == 1)
|
|
return;
|
|
break;
|
|
|
|
case EOpLessThan:
|
|
case EOpGreaterThan:
|
|
case EOpLessThanEqual:
|
|
case EOpGreaterThanEqual:
|
|
|
|
case EOpEqual:
|
|
case EOpNotEqual:
|
|
|
|
case EOpLogicalAnd:
|
|
case EOpLogicalOr:
|
|
case EOpLogicalXor:
|
|
|
|
case EOpAnd:
|
|
case EOpInclusiveOr:
|
|
case EOpExclusiveOr:
|
|
|
|
case EOpMix:
|
|
break;
|
|
|
|
default:
|
|
return;
|
|
}
|
|
|
|
// Do bidirectional conversions
|
|
if (lhsNode->getType().isScalarOrVec1() || rhsNode->getType().isScalarOrVec1()) {
|
|
if (lhsNode->getType().isScalarOrVec1())
|
|
lhsNode = addShapeConversion(rhsNode->getType(), lhsNode);
|
|
else
|
|
rhsNode = addShapeConversion(lhsNode->getType(), rhsNode);
|
|
}
|
|
lhsNode = addShapeConversion(rhsNode->getType(), lhsNode);
|
|
rhsNode = addShapeConversion(lhsNode->getType(), rhsNode);
|
|
}
|
|
|
|
// Convert the node's shape of type for the given type, as allowed by the
|
|
// operation involved: 'op'.
|
|
//
|
|
// Generally, the AST represents allowed GLSL shapes, so this isn't needed
|
|
// for GLSL. Bad shapes are caught in conversion or promotion.
|
|
//
|
|
// Return 'node' if no conversion was done. Promotion handles final shape
|
|
// checking.
|
|
//
|
|
TIntermTyped* TIntermediate::addShapeConversion(const TType& type, TIntermTyped* node)
|
|
{
|
|
// no conversion needed
|
|
if (node->getType() == type)
|
|
return node;
|
|
|
|
// structures and arrays don't change shape, either to or from
|
|
if (node->getType().isStruct() || node->getType().isArray() ||
|
|
type.isStruct() || type.isArray())
|
|
return node;
|
|
|
|
// The new node that handles the conversion
|
|
TOperator constructorOp = mapTypeToConstructorOp(type);
|
|
|
|
if (getSource() == EShSourceHlsl) {
|
|
// HLSL rules for scalar, vector and matrix conversions:
|
|
// 1) scalar can become anything, initializing every component with its value
|
|
// 2) vector and matrix can become scalar, first element is used (warning: truncation)
|
|
// 3) matrix can become matrix with less rows and/or columns (warning: truncation)
|
|
// 4) vector can become vector with less rows size (warning: truncation)
|
|
// 5a) vector 4 can become 2x2 matrix (special case) (same packing layout, its a reinterpret)
|
|
// 5b) 2x2 matrix can become vector 4 (special case) (same packing layout, its a reinterpret)
|
|
|
|
const TType &sourceType = node->getType();
|
|
|
|
// rule 1 for scalar to matrix is special
|
|
if (sourceType.isScalarOrVec1() && type.isMatrix()) {
|
|
|
|
// HLSL semantics: the scalar (or vec1) is replicated to every component of the matrix. Left to its
|
|
// own devices, the constructor from a scalar would populate the diagonal. This forces replication
|
|
// to every matrix element.
|
|
|
|
// Note that if the node is complex (e.g, a function call), we don't want to duplicate it here
|
|
// repeatedly, so we copy it to a temp, then use the temp.
|
|
const int matSize = type.computeNumComponents();
|
|
TIntermAggregate* rhsAggregate = new TIntermAggregate();
|
|
|
|
const bool isSimple = (node->getAsSymbolNode() != nullptr) || (node->getAsConstantUnion() != nullptr);
|
|
|
|
if (!isSimple) {
|
|
assert(0); // TODO: use node replicator service when available.
|
|
}
|
|
|
|
for (int x = 0; x < matSize; ++x)
|
|
rhsAggregate->getSequence().push_back(node);
|
|
|
|
return setAggregateOperator(rhsAggregate, constructorOp, type, node->getLoc());
|
|
}
|
|
|
|
// rule 1 and 2
|
|
if ((sourceType.isScalar() && !type.isScalar()) || (!sourceType.isScalar() && type.isScalar()))
|
|
return setAggregateOperator(makeAggregate(node), constructorOp, type, node->getLoc());
|
|
|
|
// rule 3 and 5b
|
|
if (sourceType.isMatrix()) {
|
|
// rule 3
|
|
if (type.isMatrix()) {
|
|
if ((sourceType.getMatrixCols() != type.getMatrixCols() || sourceType.getMatrixRows() != type.getMatrixRows()) &&
|
|
sourceType.getMatrixCols() >= type.getMatrixCols() && sourceType.getMatrixRows() >= type.getMatrixRows())
|
|
return setAggregateOperator(makeAggregate(node), constructorOp, type, node->getLoc());
|
|
// rule 5b
|
|
} else if (type.isVector()) {
|
|
if (type.getVectorSize() == 4 && sourceType.getMatrixCols() == 2 && sourceType.getMatrixRows() == 2)
|
|
return setAggregateOperator(makeAggregate(node), constructorOp, type, node->getLoc());
|
|
}
|
|
}
|
|
|
|
// rule 4 and 5a
|
|
if (sourceType.isVector()) {
|
|
// rule 4
|
|
if (type.isVector())
|
|
{
|
|
if (sourceType.getVectorSize() > type.getVectorSize())
|
|
return setAggregateOperator(makeAggregate(node), constructorOp, type, node->getLoc());
|
|
// rule 5a
|
|
} else if (type.isMatrix()) {
|
|
if (sourceType.getVectorSize() == 4 && type.getMatrixCols() == 2 && type.getMatrixRows() == 2)
|
|
return setAggregateOperator(makeAggregate(node), constructorOp, type, node->getLoc());
|
|
}
|
|
}
|
|
}
|
|
|
|
// scalar -> vector or vec1 -> vector or
|
|
// vector -> scalar or
|
|
// bigger vector -> smaller vector
|
|
if ((node->getType().isScalarOrVec1() && type.isVector()) ||
|
|
(node->getType().isVector() && type.isScalar()) ||
|
|
(node->isVector() && type.isVector() && node->getVectorSize() > type.getVectorSize()))
|
|
return setAggregateOperator(makeAggregate(node), constructorOp, type, node->getLoc());
|
|
|
|
return node;
|
|
}
|
|
|
|
bool TIntermediate::isIntegralPromotion(TBasicType from, TBasicType to) const
|
|
{
|
|
// integral promotions
|
|
if (to == EbtInt) {
|
|
switch(from) {
|
|
case EbtInt8:
|
|
case EbtInt16:
|
|
case EbtUint8:
|
|
case EbtUint16:
|
|
return true;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool TIntermediate::isFPPromotion(TBasicType from, TBasicType to) const
|
|
{
|
|
// floating-point promotions
|
|
if (to == EbtDouble) {
|
|
switch(from) {
|
|
case EbtFloat16:
|
|
case EbtFloat:
|
|
return true;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool TIntermediate::isIntegralConversion(TBasicType from, TBasicType to) const
|
|
{
|
|
#ifdef GLSLANG_WEB
|
|
return false;
|
|
#endif
|
|
|
|
switch (from) {
|
|
case EbtInt:
|
|
switch(to) {
|
|
case EbtUint:
|
|
return version >= 400 || getSource() == EShSourceHlsl;
|
|
case EbtInt64:
|
|
case EbtUint64:
|
|
return true;
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
case EbtUint:
|
|
switch(to) {
|
|
case EbtInt64:
|
|
case EbtUint64:
|
|
return true;
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
case EbtInt8:
|
|
switch (to) {
|
|
case EbtUint8:
|
|
case EbtInt16:
|
|
case EbtUint16:
|
|
case EbtUint:
|
|
case EbtInt64:
|
|
case EbtUint64:
|
|
return true;
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
case EbtUint8:
|
|
switch (to) {
|
|
case EbtInt16:
|
|
case EbtUint16:
|
|
case EbtUint:
|
|
case EbtInt64:
|
|
case EbtUint64:
|
|
return true;
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
case EbtInt16:
|
|
switch(to) {
|
|
case EbtUint16:
|
|
case EbtUint:
|
|
case EbtInt64:
|
|
case EbtUint64:
|
|
return true;
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
case EbtUint16:
|
|
switch(to) {
|
|
case EbtUint:
|
|
case EbtInt64:
|
|
case EbtUint64:
|
|
return true;
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
case EbtInt64:
|
|
if (to == EbtUint64) {
|
|
return true;
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool TIntermediate::isFPConversion(TBasicType from, TBasicType to) const
|
|
{
|
|
#ifdef GLSLANG_WEB
|
|
return false;
|
|
#endif
|
|
|
|
if (to == EbtFloat && from == EbtFloat16) {
|
|
return true;
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
bool TIntermediate::isFPIntegralConversion(TBasicType from, TBasicType to) const
|
|
{
|
|
switch (from) {
|
|
case EbtInt:
|
|
case EbtUint:
|
|
switch(to) {
|
|
case EbtFloat:
|
|
case EbtDouble:
|
|
return true;
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
#ifndef GLSLANG_WEB
|
|
case EbtInt8:
|
|
case EbtUint8:
|
|
case EbtInt16:
|
|
case EbtUint16:
|
|
switch (to) {
|
|
case EbtFloat16:
|
|
case EbtFloat:
|
|
case EbtDouble:
|
|
return true;
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
case EbtInt64:
|
|
case EbtUint64:
|
|
if (to == EbtDouble) {
|
|
return true;
|
|
}
|
|
break;
|
|
#endif
|
|
default:
|
|
break;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
//
|
|
// See if the 'from' type is allowed to be implicitly converted to the
|
|
// 'to' type. This is not about vector/array/struct, only about basic type.
|
|
//
|
|
bool TIntermediate::canImplicitlyPromote(TBasicType from, TBasicType to, TOperator op) const
|
|
{
|
|
if ((isEsProfile() && version < 310 ) || version == 110)
|
|
return false;
|
|
|
|
if (from == to)
|
|
return true;
|
|
|
|
// TODO: Move more policies into language-specific handlers.
|
|
// Some languages allow more general (or potentially, more specific) conversions under some conditions.
|
|
if (getSource() == EShSourceHlsl) {
|
|
const bool fromConvertable = (from == EbtFloat || from == EbtDouble || from == EbtInt || from == EbtUint || from == EbtBool);
|
|
const bool toConvertable = (to == EbtFloat || to == EbtDouble || to == EbtInt || to == EbtUint || to == EbtBool);
|
|
|
|
if (fromConvertable && toConvertable) {
|
|
switch (op) {
|
|
case EOpAndAssign: // assignments can perform arbitrary conversions
|
|
case EOpInclusiveOrAssign: // ...
|
|
case EOpExclusiveOrAssign: // ...
|
|
case EOpAssign: // ...
|
|
case EOpAddAssign: // ...
|
|
case EOpSubAssign: // ...
|
|
case EOpMulAssign: // ...
|
|
case EOpVectorTimesScalarAssign: // ...
|
|
case EOpMatrixTimesScalarAssign: // ...
|
|
case EOpDivAssign: // ...
|
|
case EOpModAssign: // ...
|
|
case EOpReturn: // function returns can also perform arbitrary conversions
|
|
case EOpFunctionCall: // conversion of a calling parameter
|
|
case EOpLogicalNot:
|
|
case EOpLogicalAnd:
|
|
case EOpLogicalOr:
|
|
case EOpLogicalXor:
|
|
case EOpConstructStruct:
|
|
return true;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (getSource() == EShSourceHlsl) {
|
|
// HLSL
|
|
if (from == EbtBool && (to == EbtInt || to == EbtUint || to == EbtFloat))
|
|
return true;
|
|
} else {
|
|
// GLSL
|
|
if (isIntegralPromotion(from, to) ||
|
|
isFPPromotion(from, to) ||
|
|
isIntegralConversion(from, to) ||
|
|
isFPConversion(from, to) ||
|
|
isFPIntegralConversion(from, to)) {
|
|
|
|
if (numericFeatures.contains(TNumericFeatures::shader_explicit_arithmetic_types) ||
|
|
numericFeatures.contains(TNumericFeatures::shader_explicit_arithmetic_types_int8) ||
|
|
numericFeatures.contains(TNumericFeatures::shader_explicit_arithmetic_types_int16) ||
|
|
numericFeatures.contains(TNumericFeatures::shader_explicit_arithmetic_types_int32) ||
|
|
numericFeatures.contains(TNumericFeatures::shader_explicit_arithmetic_types_int64) ||
|
|
numericFeatures.contains(TNumericFeatures::shader_explicit_arithmetic_types_float16) ||
|
|
numericFeatures.contains(TNumericFeatures::shader_explicit_arithmetic_types_float32) ||
|
|
numericFeatures.contains(TNumericFeatures::shader_explicit_arithmetic_types_float64)) {
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (isEsProfile()) {
|
|
switch (to) {
|
|
case EbtFloat:
|
|
switch (from) {
|
|
case EbtInt:
|
|
case EbtUint:
|
|
return numericFeatures.contains(TNumericFeatures::shader_implicit_conversions);
|
|
default:
|
|
return false;
|
|
}
|
|
case EbtUint:
|
|
switch (from) {
|
|
case EbtInt:
|
|
return numericFeatures.contains(TNumericFeatures::shader_implicit_conversions);
|
|
default:
|
|
return false;
|
|
}
|
|
default:
|
|
return false;
|
|
}
|
|
} else {
|
|
switch (to) {
|
|
case EbtDouble:
|
|
switch (from) {
|
|
case EbtInt:
|
|
case EbtUint:
|
|
case EbtInt64:
|
|
case EbtUint64:
|
|
case EbtFloat:
|
|
return version >= 400 || numericFeatures.contains(TNumericFeatures::gpu_shader_fp64);
|
|
case EbtInt16:
|
|
case EbtUint16:
|
|
return (version >= 400 || numericFeatures.contains(TNumericFeatures::gpu_shader_fp64)) &&
|
|
numericFeatures.contains(TNumericFeatures::gpu_shader_int16);
|
|
case EbtFloat16:
|
|
return (version >= 400 || numericFeatures.contains(TNumericFeatures::gpu_shader_fp64)) &&
|
|
numericFeatures.contains(TNumericFeatures::gpu_shader_half_float);
|
|
default:
|
|
return false;
|
|
}
|
|
case EbtFloat:
|
|
switch (from) {
|
|
case EbtInt:
|
|
case EbtUint:
|
|
return true;
|
|
case EbtBool:
|
|
return getSource() == EShSourceHlsl;
|
|
case EbtInt16:
|
|
case EbtUint16:
|
|
return numericFeatures.contains(TNumericFeatures::gpu_shader_int16);
|
|
case EbtFloat16:
|
|
return numericFeatures.contains(TNumericFeatures::gpu_shader_half_float) ||
|
|
getSource() == EShSourceHlsl;
|
|
default:
|
|
return false;
|
|
}
|
|
case EbtUint:
|
|
switch (from) {
|
|
case EbtInt:
|
|
return version >= 400 || getSource() == EShSourceHlsl || IsRequestedExtension(E_GL_ARB_gpu_shader5);
|
|
case EbtBool:
|
|
return getSource() == EShSourceHlsl;
|
|
case EbtInt16:
|
|
case EbtUint16:
|
|
return numericFeatures.contains(TNumericFeatures::gpu_shader_int16);
|
|
default:
|
|
return false;
|
|
}
|
|
case EbtInt:
|
|
switch (from) {
|
|
case EbtBool:
|
|
return getSource() == EShSourceHlsl;
|
|
case EbtInt16:
|
|
return numericFeatures.contains(TNumericFeatures::gpu_shader_int16);
|
|
default:
|
|
return false;
|
|
}
|
|
case EbtUint64:
|
|
switch (from) {
|
|
case EbtInt:
|
|
case EbtUint:
|
|
case EbtInt64:
|
|
return true;
|
|
case EbtInt16:
|
|
case EbtUint16:
|
|
return numericFeatures.contains(TNumericFeatures::gpu_shader_int16);
|
|
default:
|
|
return false;
|
|
}
|
|
case EbtInt64:
|
|
switch (from) {
|
|
case EbtInt:
|
|
return true;
|
|
case EbtInt16:
|
|
return numericFeatures.contains(TNumericFeatures::gpu_shader_int16);
|
|
default:
|
|
return false;
|
|
}
|
|
case EbtFloat16:
|
|
switch (from) {
|
|
case EbtInt16:
|
|
case EbtUint16:
|
|
return numericFeatures.contains(TNumericFeatures::gpu_shader_int16);
|
|
default:
|
|
break;
|
|
}
|
|
return false;
|
|
case EbtUint16:
|
|
switch (from) {
|
|
case EbtInt16:
|
|
return numericFeatures.contains(TNumericFeatures::gpu_shader_int16);
|
|
default:
|
|
break;
|
|
}
|
|
return false;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
static bool canSignedIntTypeRepresentAllUnsignedValues(TBasicType sintType, TBasicType uintType)
|
|
{
|
|
#ifdef GLSLANG_WEB
|
|
return false;
|
|
#endif
|
|
|
|
switch(sintType) {
|
|
case EbtInt8:
|
|
switch(uintType) {
|
|
case EbtUint8:
|
|
case EbtUint16:
|
|
case EbtUint:
|
|
case EbtUint64:
|
|
return false;
|
|
default:
|
|
assert(false);
|
|
return false;
|
|
}
|
|
break;
|
|
case EbtInt16:
|
|
switch(uintType) {
|
|
case EbtUint8:
|
|
return true;
|
|
case EbtUint16:
|
|
case EbtUint:
|
|
case EbtUint64:
|
|
return false;
|
|
default:
|
|
assert(false);
|
|
return false;
|
|
}
|
|
break;
|
|
case EbtInt:
|
|
switch(uintType) {
|
|
case EbtUint8:
|
|
case EbtUint16:
|
|
return true;
|
|
case EbtUint:
|
|
return false;
|
|
default:
|
|
assert(false);
|
|
return false;
|
|
}
|
|
break;
|
|
case EbtInt64:
|
|
switch(uintType) {
|
|
case EbtUint8:
|
|
case EbtUint16:
|
|
case EbtUint:
|
|
return true;
|
|
case EbtUint64:
|
|
return false;
|
|
default:
|
|
assert(false);
|
|
return false;
|
|
}
|
|
break;
|
|
default:
|
|
assert(false);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
|
|
static TBasicType getCorrespondingUnsignedType(TBasicType type)
|
|
{
|
|
#ifdef GLSLANG_WEB
|
|
assert(type == EbtInt);
|
|
return EbtUint;
|
|
#endif
|
|
|
|
switch(type) {
|
|
case EbtInt8:
|
|
return EbtUint8;
|
|
case EbtInt16:
|
|
return EbtUint16;
|
|
case EbtInt:
|
|
return EbtUint;
|
|
case EbtInt64:
|
|
return EbtUint64;
|
|
default:
|
|
assert(false);
|
|
return EbtNumTypes;
|
|
}
|
|
}
|
|
|
|
// Implements the following rules
|
|
// - If either operand has type float64_t or derived from float64_t,
|
|
// the other shall be converted to float64_t or derived type.
|
|
// - Otherwise, if either operand has type float32_t or derived from
|
|
// float32_t, the other shall be converted to float32_t or derived type.
|
|
// - Otherwise, if either operand has type float16_t or derived from
|
|
// float16_t, the other shall be converted to float16_t or derived type.
|
|
// - Otherwise, if both operands have integer types the following rules
|
|
// shall be applied to the operands:
|
|
// - If both operands have the same type, no further conversion
|
|
// is needed.
|
|
// - Otherwise, if both operands have signed integer types or both
|
|
// have unsigned integer types, the operand with the type of lesser
|
|
// integer conversion rank shall be converted to the type of the
|
|
// operand with greater rank.
|
|
// - Otherwise, if the operand that has unsigned integer type has rank
|
|
// greater than or equal to the rank of the type of the other
|
|
// operand, the operand with signed integer type shall be converted
|
|
// to the type of the operand with unsigned integer type.
|
|
// - Otherwise, if the type of the operand with signed integer type can
|
|
// represent all of the values of the type of the operand with
|
|
// unsigned integer type, the operand with unsigned integer type
|
|
// shall be converted to the type of the operand with signed
|
|
// integer type.
|
|
// - Otherwise, both operands shall be converted to the unsigned
|
|
// integer type corresponding to the type of the operand with signed
|
|
// integer type.
|
|
|
|
std::tuple<TBasicType, TBasicType> TIntermediate::getConversionDestinationType(TBasicType type0, TBasicType type1, TOperator op) const
|
|
{
|
|
TBasicType res0 = EbtNumTypes;
|
|
TBasicType res1 = EbtNumTypes;
|
|
|
|
if ((isEsProfile() &&
|
|
(version < 310 || !numericFeatures.contains(TNumericFeatures::shader_implicit_conversions))) ||
|
|
version == 110)
|
|
return std::make_tuple(res0, res1);
|
|
|
|
if (getSource() == EShSourceHlsl) {
|
|
if (canImplicitlyPromote(type1, type0, op)) {
|
|
res0 = type0;
|
|
res1 = type0;
|
|
} else if (canImplicitlyPromote(type0, type1, op)) {
|
|
res0 = type1;
|
|
res1 = type1;
|
|
}
|
|
return std::make_tuple(res0, res1);
|
|
}
|
|
|
|
if ((type0 == EbtDouble && canImplicitlyPromote(type1, EbtDouble, op)) ||
|
|
(type1 == EbtDouble && canImplicitlyPromote(type0, EbtDouble, op)) ) {
|
|
res0 = EbtDouble;
|
|
res1 = EbtDouble;
|
|
} else if ((type0 == EbtFloat && canImplicitlyPromote(type1, EbtFloat, op)) ||
|
|
(type1 == EbtFloat && canImplicitlyPromote(type0, EbtFloat, op)) ) {
|
|
res0 = EbtFloat;
|
|
res1 = EbtFloat;
|
|
} else if ((type0 == EbtFloat16 && canImplicitlyPromote(type1, EbtFloat16, op)) ||
|
|
(type1 == EbtFloat16 && canImplicitlyPromote(type0, EbtFloat16, op)) ) {
|
|
res0 = EbtFloat16;
|
|
res1 = EbtFloat16;
|
|
} else if (isTypeInt(type0) && isTypeInt(type1) &&
|
|
(canImplicitlyPromote(type0, type1, op) || canImplicitlyPromote(type1, type0, op))) {
|
|
if ((isTypeSignedInt(type0) && isTypeSignedInt(type1)) ||
|
|
(isTypeUnsignedInt(type0) && isTypeUnsignedInt(type1))) {
|
|
if (getTypeRank(type0) < getTypeRank(type1)) {
|
|
res0 = type1;
|
|
res1 = type1;
|
|
} else {
|
|
res0 = type0;
|
|
res1 = type0;
|
|
}
|
|
} else if (isTypeUnsignedInt(type0) && (getTypeRank(type0) > getTypeRank(type1))) {
|
|
res0 = type0;
|
|
res1 = type0;
|
|
} else if (isTypeUnsignedInt(type1) && (getTypeRank(type1) > getTypeRank(type0))) {
|
|
res0 = type1;
|
|
res1 = type1;
|
|
} else if (isTypeSignedInt(type0)) {
|
|
if (canSignedIntTypeRepresentAllUnsignedValues(type0, type1)) {
|
|
res0 = type0;
|
|
res1 = type0;
|
|
} else {
|
|
res0 = getCorrespondingUnsignedType(type0);
|
|
res1 = getCorrespondingUnsignedType(type0);
|
|
}
|
|
} else if (isTypeSignedInt(type1)) {
|
|
if (canSignedIntTypeRepresentAllUnsignedValues(type1, type0)) {
|
|
res0 = type1;
|
|
res1 = type1;
|
|
} else {
|
|
res0 = getCorrespondingUnsignedType(type1);
|
|
res1 = getCorrespondingUnsignedType(type1);
|
|
}
|
|
}
|
|
}
|
|
|
|
return std::make_tuple(res0, res1);
|
|
}
|
|
|
|
//
|
|
// Given a type, find what operation would fully construct it.
|
|
//
|
|
TOperator TIntermediate::mapTypeToConstructorOp(const TType& type) const
|
|
{
|
|
TOperator op = EOpNull;
|
|
|
|
if (type.getQualifier().isNonUniform())
|
|
return EOpConstructNonuniform;
|
|
|
|
if (type.isCoopMat())
|
|
return EOpConstructCooperativeMatrix;
|
|
|
|
switch (type.getBasicType()) {
|
|
case EbtStruct:
|
|
op = EOpConstructStruct;
|
|
break;
|
|
case EbtSampler:
|
|
if (type.getSampler().isCombined())
|
|
op = EOpConstructTextureSampler;
|
|
break;
|
|
case EbtFloat:
|
|
if (type.isMatrix()) {
|
|
switch (type.getMatrixCols()) {
|
|
case 2:
|
|
switch (type.getMatrixRows()) {
|
|
case 2: op = EOpConstructMat2x2; break;
|
|
case 3: op = EOpConstructMat2x3; break;
|
|
case 4: op = EOpConstructMat2x4; break;
|
|
default: break; // some compilers want this
|
|
}
|
|
break;
|
|
case 3:
|
|
switch (type.getMatrixRows()) {
|
|
case 2: op = EOpConstructMat3x2; break;
|
|
case 3: op = EOpConstructMat3x3; break;
|
|
case 4: op = EOpConstructMat3x4; break;
|
|
default: break; // some compilers want this
|
|
}
|
|
break;
|
|
case 4:
|
|
switch (type.getMatrixRows()) {
|
|
case 2: op = EOpConstructMat4x2; break;
|
|
case 3: op = EOpConstructMat4x3; break;
|
|
case 4: op = EOpConstructMat4x4; break;
|
|
default: break; // some compilers want this
|
|
}
|
|
break;
|
|
default: break; // some compilers want this
|
|
}
|
|
} else {
|
|
switch(type.getVectorSize()) {
|
|
case 1: op = EOpConstructFloat; break;
|
|
case 2: op = EOpConstructVec2; break;
|
|
case 3: op = EOpConstructVec3; break;
|
|
case 4: op = EOpConstructVec4; break;
|
|
default: break; // some compilers want this
|
|
}
|
|
}
|
|
break;
|
|
case EbtInt:
|
|
if (type.getMatrixCols()) {
|
|
switch (type.getMatrixCols()) {
|
|
case 2:
|
|
switch (type.getMatrixRows()) {
|
|
case 2: op = EOpConstructIMat2x2; break;
|
|
case 3: op = EOpConstructIMat2x3; break;
|
|
case 4: op = EOpConstructIMat2x4; break;
|
|
default: break; // some compilers want this
|
|
}
|
|
break;
|
|
case 3:
|
|
switch (type.getMatrixRows()) {
|
|
case 2: op = EOpConstructIMat3x2; break;
|
|
case 3: op = EOpConstructIMat3x3; break;
|
|
case 4: op = EOpConstructIMat3x4; break;
|
|
default: break; // some compilers want this
|
|
}
|
|
break;
|
|
case 4:
|
|
switch (type.getMatrixRows()) {
|
|
case 2: op = EOpConstructIMat4x2; break;
|
|
case 3: op = EOpConstructIMat4x3; break;
|
|
case 4: op = EOpConstructIMat4x4; break;
|
|
default: break; // some compilers want this
|
|
}
|
|
break;
|
|
}
|
|
} else {
|
|
switch(type.getVectorSize()) {
|
|
case 1: op = EOpConstructInt; break;
|
|
case 2: op = EOpConstructIVec2; break;
|
|
case 3: op = EOpConstructIVec3; break;
|
|
case 4: op = EOpConstructIVec4; break;
|
|
default: break; // some compilers want this
|
|
}
|
|
}
|
|
break;
|
|
case EbtUint:
|
|
if (type.getMatrixCols()) {
|
|
switch (type.getMatrixCols()) {
|
|
case 2:
|
|
switch (type.getMatrixRows()) {
|
|
case 2: op = EOpConstructUMat2x2; break;
|
|
case 3: op = EOpConstructUMat2x3; break;
|
|
case 4: op = EOpConstructUMat2x4; break;
|
|
default: break; // some compilers want this
|
|
}
|
|
break;
|
|
case 3:
|
|
switch (type.getMatrixRows()) {
|
|
case 2: op = EOpConstructUMat3x2; break;
|
|
case 3: op = EOpConstructUMat3x3; break;
|
|
case 4: op = EOpConstructUMat3x4; break;
|
|
default: break; // some compilers want this
|
|
}
|
|
break;
|
|
case 4:
|
|
switch (type.getMatrixRows()) {
|
|
case 2: op = EOpConstructUMat4x2; break;
|
|
case 3: op = EOpConstructUMat4x3; break;
|
|
case 4: op = EOpConstructUMat4x4; break;
|
|
default: break; // some compilers want this
|
|
}
|
|
break;
|
|
}
|
|
} else {
|
|
switch(type.getVectorSize()) {
|
|
case 1: op = EOpConstructUint; break;
|
|
case 2: op = EOpConstructUVec2; break;
|
|
case 3: op = EOpConstructUVec3; break;
|
|
case 4: op = EOpConstructUVec4; break;
|
|
default: break; // some compilers want this
|
|
}
|
|
}
|
|
break;
|
|
case EbtBool:
|
|
if (type.getMatrixCols()) {
|
|
switch (type.getMatrixCols()) {
|
|
case 2:
|
|
switch (type.getMatrixRows()) {
|
|
case 2: op = EOpConstructBMat2x2; break;
|
|
case 3: op = EOpConstructBMat2x3; break;
|
|
case 4: op = EOpConstructBMat2x4; break;
|
|
default: break; // some compilers want this
|
|
}
|
|
break;
|
|
case 3:
|
|
switch (type.getMatrixRows()) {
|
|
case 2: op = EOpConstructBMat3x2; break;
|
|
case 3: op = EOpConstructBMat3x3; break;
|
|
case 4: op = EOpConstructBMat3x4; break;
|
|
default: break; // some compilers want this
|
|
}
|
|
break;
|
|
case 4:
|
|
switch (type.getMatrixRows()) {
|
|
case 2: op = EOpConstructBMat4x2; break;
|
|
case 3: op = EOpConstructBMat4x3; break;
|
|
case 4: op = EOpConstructBMat4x4; break;
|
|
default: break; // some compilers want this
|
|
}
|
|
break;
|
|
}
|
|
} else {
|
|
switch(type.getVectorSize()) {
|
|
case 1: op = EOpConstructBool; break;
|
|
case 2: op = EOpConstructBVec2; break;
|
|
case 3: op = EOpConstructBVec3; break;
|
|
case 4: op = EOpConstructBVec4; break;
|
|
default: break; // some compilers want this
|
|
}
|
|
}
|
|
break;
|
|
#ifndef GLSLANG_WEB
|
|
case EbtDouble:
|
|
if (type.getMatrixCols()) {
|
|
switch (type.getMatrixCols()) {
|
|
case 2:
|
|
switch (type.getMatrixRows()) {
|
|
case 2: op = EOpConstructDMat2x2; break;
|
|
case 3: op = EOpConstructDMat2x3; break;
|
|
case 4: op = EOpConstructDMat2x4; break;
|
|
default: break; // some compilers want this
|
|
}
|
|
break;
|
|
case 3:
|
|
switch (type.getMatrixRows()) {
|
|
case 2: op = EOpConstructDMat3x2; break;
|
|
case 3: op = EOpConstructDMat3x3; break;
|
|
case 4: op = EOpConstructDMat3x4; break;
|
|
default: break; // some compilers want this
|
|
}
|
|
break;
|
|
case 4:
|
|
switch (type.getMatrixRows()) {
|
|
case 2: op = EOpConstructDMat4x2; break;
|
|
case 3: op = EOpConstructDMat4x3; break;
|
|
case 4: op = EOpConstructDMat4x4; break;
|
|
default: break; // some compilers want this
|
|
}
|
|
break;
|
|
}
|
|
} else {
|
|
switch(type.getVectorSize()) {
|
|
case 1: op = EOpConstructDouble; break;
|
|
case 2: op = EOpConstructDVec2; break;
|
|
case 3: op = EOpConstructDVec3; break;
|
|
case 4: op = EOpConstructDVec4; break;
|
|
default: break; // some compilers want this
|
|
}
|
|
}
|
|
break;
|
|
case EbtFloat16:
|
|
if (type.getMatrixCols()) {
|
|
switch (type.getMatrixCols()) {
|
|
case 2:
|
|
switch (type.getMatrixRows()) {
|
|
case 2: op = EOpConstructF16Mat2x2; break;
|
|
case 3: op = EOpConstructF16Mat2x3; break;
|
|
case 4: op = EOpConstructF16Mat2x4; break;
|
|
default: break; // some compilers want this
|
|
}
|
|
break;
|
|
case 3:
|
|
switch (type.getMatrixRows()) {
|
|
case 2: op = EOpConstructF16Mat3x2; break;
|
|
case 3: op = EOpConstructF16Mat3x3; break;
|
|
case 4: op = EOpConstructF16Mat3x4; break;
|
|
default: break; // some compilers want this
|
|
}
|
|
break;
|
|
case 4:
|
|
switch (type.getMatrixRows()) {
|
|
case 2: op = EOpConstructF16Mat4x2; break;
|
|
case 3: op = EOpConstructF16Mat4x3; break;
|
|
case 4: op = EOpConstructF16Mat4x4; break;
|
|
default: break; // some compilers want this
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
else {
|
|
switch (type.getVectorSize()) {
|
|
case 1: op = EOpConstructFloat16; break;
|
|
case 2: op = EOpConstructF16Vec2; break;
|
|
case 3: op = EOpConstructF16Vec3; break;
|
|
case 4: op = EOpConstructF16Vec4; break;
|
|
default: break; // some compilers want this
|
|
}
|
|
}
|
|
break;
|
|
case EbtInt8:
|
|
switch(type.getVectorSize()) {
|
|
case 1: op = EOpConstructInt8; break;
|
|
case 2: op = EOpConstructI8Vec2; break;
|
|
case 3: op = EOpConstructI8Vec3; break;
|
|
case 4: op = EOpConstructI8Vec4; break;
|
|
default: break; // some compilers want this
|
|
}
|
|
break;
|
|
case EbtUint8:
|
|
switch(type.getVectorSize()) {
|
|
case 1: op = EOpConstructUint8; break;
|
|
case 2: op = EOpConstructU8Vec2; break;
|
|
case 3: op = EOpConstructU8Vec3; break;
|
|
case 4: op = EOpConstructU8Vec4; break;
|
|
default: break; // some compilers want this
|
|
}
|
|
break;
|
|
case EbtInt16:
|
|
switch(type.getVectorSize()) {
|
|
case 1: op = EOpConstructInt16; break;
|
|
case 2: op = EOpConstructI16Vec2; break;
|
|
case 3: op = EOpConstructI16Vec3; break;
|
|
case 4: op = EOpConstructI16Vec4; break;
|
|
default: break; // some compilers want this
|
|
}
|
|
break;
|
|
case EbtUint16:
|
|
switch(type.getVectorSize()) {
|
|
case 1: op = EOpConstructUint16; break;
|
|
case 2: op = EOpConstructU16Vec2; break;
|
|
case 3: op = EOpConstructU16Vec3; break;
|
|
case 4: op = EOpConstructU16Vec4; break;
|
|
default: break; // some compilers want this
|
|
}
|
|
break;
|
|
case EbtInt64:
|
|
switch(type.getVectorSize()) {
|
|
case 1: op = EOpConstructInt64; break;
|
|
case 2: op = EOpConstructI64Vec2; break;
|
|
case 3: op = EOpConstructI64Vec3; break;
|
|
case 4: op = EOpConstructI64Vec4; break;
|
|
default: break; // some compilers want this
|
|
}
|
|
break;
|
|
case EbtUint64:
|
|
switch(type.getVectorSize()) {
|
|
case 1: op = EOpConstructUint64; break;
|
|
case 2: op = EOpConstructU64Vec2; break;
|
|
case 3: op = EOpConstructU64Vec3; break;
|
|
case 4: op = EOpConstructU64Vec4; break;
|
|
default: break; // some compilers want this
|
|
}
|
|
break;
|
|
case EbtReference:
|
|
op = EOpConstructReference;
|
|
break;
|
|
|
|
case EbtAccStruct:
|
|
op = EOpConstructAccStruct;
|
|
break;
|
|
#endif
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return op;
|
|
}
|
|
|
|
//
|
|
// Safe way to combine two nodes into an aggregate. Works with null pointers,
|
|
// a node that's not a aggregate yet, etc.
|
|
//
|
|
// Returns the resulting aggregate, unless nullptr was passed in for
|
|
// both existing nodes.
|
|
//
|
|
TIntermAggregate* TIntermediate::growAggregate(TIntermNode* left, TIntermNode* right)
|
|
{
|
|
if (left == nullptr && right == nullptr)
|
|
return nullptr;
|
|
|
|
TIntermAggregate* aggNode = nullptr;
|
|
if (left != nullptr)
|
|
aggNode = left->getAsAggregate();
|
|
if (aggNode == nullptr || aggNode->getOp() != EOpNull) {
|
|
aggNode = new TIntermAggregate;
|
|
if (left != nullptr)
|
|
aggNode->getSequence().push_back(left);
|
|
}
|
|
|
|
if (right != nullptr)
|
|
aggNode->getSequence().push_back(right);
|
|
|
|
return aggNode;
|
|
}
|
|
|
|
TIntermAggregate* TIntermediate::growAggregate(TIntermNode* left, TIntermNode* right, const TSourceLoc& loc)
|
|
{
|
|
TIntermAggregate* aggNode = growAggregate(left, right);
|
|
if (aggNode)
|
|
aggNode->setLoc(loc);
|
|
|
|
return aggNode;
|
|
}
|
|
|
|
//
|
|
// Turn an existing node into an aggregate.
|
|
//
|
|
// Returns an aggregate, unless nullptr was passed in for the existing node.
|
|
//
|
|
TIntermAggregate* TIntermediate::makeAggregate(TIntermNode* node)
|
|
{
|
|
if (node == nullptr)
|
|
return nullptr;
|
|
|
|
TIntermAggregate* aggNode = new TIntermAggregate;
|
|
aggNode->getSequence().push_back(node);
|
|
aggNode->setLoc(node->getLoc());
|
|
|
|
return aggNode;
|
|
}
|
|
|
|
TIntermAggregate* TIntermediate::makeAggregate(TIntermNode* node, const TSourceLoc& loc)
|
|
{
|
|
if (node == nullptr)
|
|
return nullptr;
|
|
|
|
TIntermAggregate* aggNode = new TIntermAggregate;
|
|
aggNode->getSequence().push_back(node);
|
|
aggNode->setLoc(loc);
|
|
|
|
return aggNode;
|
|
}
|
|
|
|
//
|
|
// Make an aggregate with an empty sequence.
|
|
//
|
|
TIntermAggregate* TIntermediate::makeAggregate(const TSourceLoc& loc)
|
|
{
|
|
TIntermAggregate* aggNode = new TIntermAggregate;
|
|
aggNode->setLoc(loc);
|
|
|
|
return aggNode;
|
|
}
|
|
|
|
//
|
|
// For "if" test nodes. There are three children; a condition,
|
|
// a true path, and a false path. The two paths are in the
|
|
// nodePair.
|
|
//
|
|
// Returns the selection node created.
|
|
//
|
|
TIntermSelection* TIntermediate::addSelection(TIntermTyped* cond, TIntermNodePair nodePair, const TSourceLoc& loc)
|
|
{
|
|
//
|
|
// Don't prune the false path for compile-time constants; it's needed
|
|
// for static access analysis.
|
|
//
|
|
|
|
TIntermSelection* node = new TIntermSelection(cond, nodePair.node1, nodePair.node2);
|
|
node->setLoc(loc);
|
|
|
|
return node;
|
|
}
|
|
|
|
TIntermTyped* TIntermediate::addComma(TIntermTyped* left, TIntermTyped* right, const TSourceLoc& loc)
|
|
{
|
|
// However, the lowest precedence operators of the sequence operator ( , ) and the assignment operators
|
|
// ... are not included in the operators that can create a constant expression.
|
|
//
|
|
// if (left->getType().getQualifier().storage == EvqConst &&
|
|
// right->getType().getQualifier().storage == EvqConst) {
|
|
|
|
// return right;
|
|
//}
|
|
|
|
TIntermTyped *commaAggregate = growAggregate(left, right, loc);
|
|
commaAggregate->getAsAggregate()->setOperator(EOpComma);
|
|
commaAggregate->setType(right->getType());
|
|
commaAggregate->getWritableType().getQualifier().makeTemporary();
|
|
|
|
return commaAggregate;
|
|
}
|
|
|
|
TIntermTyped* TIntermediate::addMethod(TIntermTyped* object, const TType& type, const TString* name, const TSourceLoc& loc)
|
|
{
|
|
TIntermMethod* method = new TIntermMethod(object, type, *name);
|
|
method->setLoc(loc);
|
|
|
|
return method;
|
|
}
|
|
|
|
//
|
|
// For "?:" test nodes. There are three children; a condition,
|
|
// a true path, and a false path. The two paths are specified
|
|
// as separate parameters. For vector 'cond', the true and false
|
|
// are not paths, but vectors to mix.
|
|
//
|
|
// Specialization constant operations include
|
|
// - The ternary operator ( ? : )
|
|
//
|
|
// Returns the selection node created, or nullptr if one could not be.
|
|
//
|
|
TIntermTyped* TIntermediate::addSelection(TIntermTyped* cond, TIntermTyped* trueBlock, TIntermTyped* falseBlock,
|
|
const TSourceLoc& loc)
|
|
{
|
|
// If it's void, go to the if-then-else selection()
|
|
if (trueBlock->getBasicType() == EbtVoid && falseBlock->getBasicType() == EbtVoid) {
|
|
TIntermNodePair pair = { trueBlock, falseBlock };
|
|
TIntermSelection* selection = addSelection(cond, pair, loc);
|
|
if (getSource() == EShSourceHlsl)
|
|
selection->setNoShortCircuit();
|
|
|
|
return selection;
|
|
}
|
|
|
|
//
|
|
// Get compatible types.
|
|
//
|
|
auto children = addPairConversion(EOpSequence, trueBlock, falseBlock);
|
|
trueBlock = std::get<0>(children);
|
|
falseBlock = std::get<1>(children);
|
|
|
|
if (trueBlock == nullptr || falseBlock == nullptr)
|
|
return nullptr;
|
|
|
|
// Handle a vector condition as a mix
|
|
if (!cond->getType().isScalarOrVec1()) {
|
|
TType targetVectorType(trueBlock->getType().getBasicType(), EvqTemporary,
|
|
cond->getType().getVectorSize());
|
|
// smear true/false operands as needed
|
|
trueBlock = addUniShapeConversion(EOpMix, targetVectorType, trueBlock);
|
|
falseBlock = addUniShapeConversion(EOpMix, targetVectorType, falseBlock);
|
|
|
|
// After conversion, types have to match.
|
|
if (falseBlock->getType() != trueBlock->getType())
|
|
return nullptr;
|
|
|
|
// make the mix operation
|
|
TIntermAggregate* mix = makeAggregate(loc);
|
|
mix = growAggregate(mix, falseBlock);
|
|
mix = growAggregate(mix, trueBlock);
|
|
mix = growAggregate(mix, cond);
|
|
mix->setType(targetVectorType);
|
|
mix->setOp(EOpMix);
|
|
|
|
return mix;
|
|
}
|
|
|
|
// Now have a scalar condition...
|
|
|
|
// Convert true and false expressions to matching types
|
|
addBiShapeConversion(EOpMix, trueBlock, falseBlock);
|
|
|
|
// After conversion, types have to match.
|
|
if (falseBlock->getType() != trueBlock->getType())
|
|
return nullptr;
|
|
|
|
// Eliminate the selection when the condition is a scalar and all operands are constant.
|
|
if (cond->getAsConstantUnion() && trueBlock->getAsConstantUnion() && falseBlock->getAsConstantUnion()) {
|
|
if (cond->getAsConstantUnion()->getConstArray()[0].getBConst())
|
|
return trueBlock;
|
|
else
|
|
return falseBlock;
|
|
}
|
|
|
|
//
|
|
// Make a selection node.
|
|
//
|
|
TIntermSelection* node = new TIntermSelection(cond, trueBlock, falseBlock, trueBlock->getType());
|
|
node->setLoc(loc);
|
|
node->getQualifier().precision = std::max(trueBlock->getQualifier().precision, falseBlock->getQualifier().precision);
|
|
|
|
if ((cond->getQualifier().isConstant() && specConstantPropagates(*trueBlock, *falseBlock)) ||
|
|
(cond->getQualifier().isSpecConstant() && trueBlock->getQualifier().isConstant() &&
|
|
falseBlock->getQualifier().isConstant()))
|
|
node->getQualifier().makeSpecConstant();
|
|
else
|
|
node->getQualifier().makeTemporary();
|
|
|
|
if (getSource() == EShSourceHlsl)
|
|
node->setNoShortCircuit();
|
|
|
|
return node;
|
|
}
|
|
|
|
//
|
|
// Constant terminal nodes. Has a union that contains bool, float or int constants
|
|
//
|
|
// Returns the constant union node created.
|
|
//
|
|
|
|
TIntermConstantUnion* TIntermediate::addConstantUnion(const TConstUnionArray& unionArray, const TType& t, const TSourceLoc& loc, bool literal) const
|
|
{
|
|
TIntermConstantUnion* node = new TIntermConstantUnion(unionArray, t);
|
|
node->getQualifier().storage = EvqConst;
|
|
node->setLoc(loc);
|
|
if (literal)
|
|
node->setLiteral();
|
|
|
|
return node;
|
|
}
|
|
TIntermConstantUnion* TIntermediate::addConstantUnion(signed char i8, const TSourceLoc& loc, bool literal) const
|
|
{
|
|
TConstUnionArray unionArray(1);
|
|
unionArray[0].setI8Const(i8);
|
|
|
|
return addConstantUnion(unionArray, TType(EbtInt8, EvqConst), loc, literal);
|
|
}
|
|
|
|
TIntermConstantUnion* TIntermediate::addConstantUnion(unsigned char u8, const TSourceLoc& loc, bool literal) const
|
|
{
|
|
TConstUnionArray unionArray(1);
|
|
unionArray[0].setUConst(u8);
|
|
|
|
return addConstantUnion(unionArray, TType(EbtUint8, EvqConst), loc, literal);
|
|
}
|
|
|
|
TIntermConstantUnion* TIntermediate::addConstantUnion(signed short i16, const TSourceLoc& loc, bool literal) const
|
|
{
|
|
TConstUnionArray unionArray(1);
|
|
unionArray[0].setI16Const(i16);
|
|
|
|
return addConstantUnion(unionArray, TType(EbtInt16, EvqConst), loc, literal);
|
|
}
|
|
|
|
TIntermConstantUnion* TIntermediate::addConstantUnion(unsigned short u16, const TSourceLoc& loc, bool literal) const
|
|
{
|
|
TConstUnionArray unionArray(1);
|
|
unionArray[0].setU16Const(u16);
|
|
|
|
return addConstantUnion(unionArray, TType(EbtUint16, EvqConst), loc, literal);
|
|
}
|
|
|
|
TIntermConstantUnion* TIntermediate::addConstantUnion(int i, const TSourceLoc& loc, bool literal) const
|
|
{
|
|
TConstUnionArray unionArray(1);
|
|
unionArray[0].setIConst(i);
|
|
|
|
return addConstantUnion(unionArray, TType(EbtInt, EvqConst), loc, literal);
|
|
}
|
|
|
|
TIntermConstantUnion* TIntermediate::addConstantUnion(unsigned int u, const TSourceLoc& loc, bool literal) const
|
|
{
|
|
TConstUnionArray unionArray(1);
|
|
unionArray[0].setUConst(u);
|
|
|
|
return addConstantUnion(unionArray, TType(EbtUint, EvqConst), loc, literal);
|
|
}
|
|
|
|
TIntermConstantUnion* TIntermediate::addConstantUnion(long long i64, const TSourceLoc& loc, bool literal) const
|
|
{
|
|
TConstUnionArray unionArray(1);
|
|
unionArray[0].setI64Const(i64);
|
|
|
|
return addConstantUnion(unionArray, TType(EbtInt64, EvqConst), loc, literal);
|
|
}
|
|
|
|
TIntermConstantUnion* TIntermediate::addConstantUnion(unsigned long long u64, const TSourceLoc& loc, bool literal) const
|
|
{
|
|
TConstUnionArray unionArray(1);
|
|
unionArray[0].setU64Const(u64);
|
|
|
|
return addConstantUnion(unionArray, TType(EbtUint64, EvqConst), loc, literal);
|
|
}
|
|
|
|
TIntermConstantUnion* TIntermediate::addConstantUnion(bool b, const TSourceLoc& loc, bool literal) const
|
|
{
|
|
TConstUnionArray unionArray(1);
|
|
unionArray[0].setBConst(b);
|
|
|
|
return addConstantUnion(unionArray, TType(EbtBool, EvqConst), loc, literal);
|
|
}
|
|
|
|
TIntermConstantUnion* TIntermediate::addConstantUnion(double d, TBasicType baseType, const TSourceLoc& loc, bool literal) const
|
|
{
|
|
assert(baseType == EbtFloat || baseType == EbtDouble || baseType == EbtFloat16);
|
|
|
|
TConstUnionArray unionArray(1);
|
|
unionArray[0].setDConst(d);
|
|
|
|
return addConstantUnion(unionArray, TType(baseType, EvqConst), loc, literal);
|
|
}
|
|
|
|
TIntermConstantUnion* TIntermediate::addConstantUnion(const TString* s, const TSourceLoc& loc, bool literal) const
|
|
{
|
|
TConstUnionArray unionArray(1);
|
|
unionArray[0].setSConst(s);
|
|
|
|
return addConstantUnion(unionArray, TType(EbtString, EvqConst), loc, literal);
|
|
}
|
|
|
|
// Put vector swizzle selectors onto the given sequence
|
|
void TIntermediate::pushSelector(TIntermSequence& sequence, const TVectorSelector& selector, const TSourceLoc& loc)
|
|
{
|
|
TIntermConstantUnion* constIntNode = addConstantUnion(selector, loc);
|
|
sequence.push_back(constIntNode);
|
|
}
|
|
|
|
// Put matrix swizzle selectors onto the given sequence
|
|
void TIntermediate::pushSelector(TIntermSequence& sequence, const TMatrixSelector& selector, const TSourceLoc& loc)
|
|
{
|
|
TIntermConstantUnion* constIntNode = addConstantUnion(selector.coord1, loc);
|
|
sequence.push_back(constIntNode);
|
|
constIntNode = addConstantUnion(selector.coord2, loc);
|
|
sequence.push_back(constIntNode);
|
|
}
|
|
|
|
// Make an aggregate node that has a sequence of all selectors.
|
|
template TIntermTyped* TIntermediate::addSwizzle<TVectorSelector>(TSwizzleSelectors<TVectorSelector>& selector, const TSourceLoc& loc);
|
|
template TIntermTyped* TIntermediate::addSwizzle<TMatrixSelector>(TSwizzleSelectors<TMatrixSelector>& selector, const TSourceLoc& loc);
|
|
template<typename selectorType>
|
|
TIntermTyped* TIntermediate::addSwizzle(TSwizzleSelectors<selectorType>& selector, const TSourceLoc& loc)
|
|
{
|
|
TIntermAggregate* node = new TIntermAggregate(EOpSequence);
|
|
|
|
node->setLoc(loc);
|
|
TIntermSequence &sequenceVector = node->getSequence();
|
|
|
|
for (int i = 0; i < selector.size(); i++)
|
|
pushSelector(sequenceVector, selector[i], loc);
|
|
|
|
return node;
|
|
}
|
|
|
|
//
|
|
// Follow the left branches down to the root of an l-value
|
|
// expression (just "." and []).
|
|
//
|
|
// Return the base of the l-value (where following indexing quits working).
|
|
// Return nullptr if a chain following dereferences cannot be followed.
|
|
//
|
|
// 'swizzleOkay' says whether or not it is okay to consider a swizzle
|
|
// a valid part of the dereference chain.
|
|
//
|
|
// 'BufferReferenceOk' says if type is buffer_reference, the routine stop to find the most left node.
|
|
//
|
|
//
|
|
|
|
const TIntermTyped* TIntermediate::findLValueBase(const TIntermTyped* node, bool swizzleOkay , bool bufferReferenceOk)
|
|
{
|
|
do {
|
|
const TIntermBinary* binary = node->getAsBinaryNode();
|
|
if (binary == nullptr)
|
|
return node;
|
|
TOperator op = binary->getOp();
|
|
if (op != EOpIndexDirect && op != EOpIndexIndirect && op != EOpIndexDirectStruct && op != EOpVectorSwizzle && op != EOpMatrixSwizzle)
|
|
return nullptr;
|
|
if (! swizzleOkay) {
|
|
if (op == EOpVectorSwizzle || op == EOpMatrixSwizzle)
|
|
return nullptr;
|
|
if ((op == EOpIndexDirect || op == EOpIndexIndirect) &&
|
|
(binary->getLeft()->getType().isVector() || binary->getLeft()->getType().isScalar()) &&
|
|
! binary->getLeft()->getType().isArray())
|
|
return nullptr;
|
|
}
|
|
node = node->getAsBinaryNode()->getLeft();
|
|
if (bufferReferenceOk && node->isReference())
|
|
return node;
|
|
} while (true);
|
|
}
|
|
|
|
//
|
|
// Create while and do-while loop nodes.
|
|
//
|
|
TIntermLoop* TIntermediate::addLoop(TIntermNode* body, TIntermTyped* test, TIntermTyped* terminal, bool testFirst,
|
|
const TSourceLoc& loc)
|
|
{
|
|
TIntermLoop* node = new TIntermLoop(body, test, terminal, testFirst);
|
|
node->setLoc(loc);
|
|
|
|
return node;
|
|
}
|
|
|
|
//
|
|
// Create a for-loop sequence.
|
|
//
|
|
TIntermAggregate* TIntermediate::addForLoop(TIntermNode* body, TIntermNode* initializer, TIntermTyped* test,
|
|
TIntermTyped* terminal, bool testFirst, const TSourceLoc& loc, TIntermLoop*& node)
|
|
{
|
|
node = new TIntermLoop(body, test, terminal, testFirst);
|
|
node->setLoc(loc);
|
|
|
|
// make a sequence of the initializer and statement, but try to reuse the
|
|
// aggregate already created for whatever is in the initializer, if there is one
|
|
TIntermAggregate* loopSequence = (initializer == nullptr ||
|
|
initializer->getAsAggregate() == nullptr) ? makeAggregate(initializer, loc)
|
|
: initializer->getAsAggregate();
|
|
if (loopSequence != nullptr && loopSequence->getOp() == EOpSequence)
|
|
loopSequence->setOp(EOpNull);
|
|
loopSequence = growAggregate(loopSequence, node);
|
|
loopSequence->setOperator(EOpSequence);
|
|
|
|
return loopSequence;
|
|
}
|
|
|
|
//
|
|
// Add branches.
|
|
//
|
|
TIntermBranch* TIntermediate::addBranch(TOperator branchOp, const TSourceLoc& loc)
|
|
{
|
|
return addBranch(branchOp, nullptr, loc);
|
|
}
|
|
|
|
TIntermBranch* TIntermediate::addBranch(TOperator branchOp, TIntermTyped* expression, const TSourceLoc& loc)
|
|
{
|
|
TIntermBranch* node = new TIntermBranch(branchOp, expression);
|
|
node->setLoc(loc);
|
|
|
|
return node;
|
|
}
|
|
|
|
// Propagate precision from formal function return type to actual return type,
|
|
// and on to its subtree.
|
|
void TIntermBranch::updatePrecision(TPrecisionQualifier parentPrecision)
|
|
{
|
|
TIntermTyped* exp = getExpression();
|
|
if (exp == nullptr)
|
|
return;
|
|
|
|
if (exp->getBasicType() == EbtInt || exp->getBasicType() == EbtUint ||
|
|
exp->getBasicType() == EbtFloat || exp->getBasicType() == EbtFloat16) {
|
|
if (parentPrecision != EpqNone && exp->getQualifier().precision == EpqNone) {
|
|
exp->propagatePrecision(parentPrecision);
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// This is to be executed after the final root is put on top by the parsing
|
|
// process.
|
|
//
|
|
bool TIntermediate::postProcess(TIntermNode* root, EShLanguage /*language*/)
|
|
{
|
|
if (root == nullptr)
|
|
return true;
|
|
|
|
// Finish off the top-level sequence
|
|
TIntermAggregate* aggRoot = root->getAsAggregate();
|
|
if (aggRoot && aggRoot->getOp() == EOpNull)
|
|
aggRoot->setOperator(EOpSequence);
|
|
|
|
#ifndef GLSLANG_WEB
|
|
// Propagate 'noContraction' label in backward from 'precise' variables.
|
|
glslang::PropagateNoContraction(*this);
|
|
|
|
switch (textureSamplerTransformMode) {
|
|
case EShTexSampTransKeep:
|
|
break;
|
|
case EShTexSampTransUpgradeTextureRemoveSampler:
|
|
performTextureUpgradeAndSamplerRemovalTransformation(root);
|
|
break;
|
|
case EShTexSampTransCount:
|
|
assert(0);
|
|
break;
|
|
}
|
|
#endif
|
|
|
|
return true;
|
|
}
|
|
|
|
void TIntermediate::addSymbolLinkageNodes(TIntermAggregate*& linkage, EShLanguage language, TSymbolTable& symbolTable)
|
|
{
|
|
// Add top-level nodes for declarations that must be checked cross
|
|
// compilation unit by a linker, yet might not have been referenced
|
|
// by the AST.
|
|
//
|
|
// Almost entirely, translation of symbols is driven by what's present
|
|
// in the AST traversal, not by translating the symbol table.
|
|
//
|
|
// However, there are some special cases:
|
|
// - From the specification: "Special built-in inputs gl_VertexID and
|
|
// gl_InstanceID are also considered active vertex attributes."
|
|
// - Linker-based type mismatch error reporting needs to see all
|
|
// uniforms/ins/outs variables and blocks.
|
|
// - ftransform() can make gl_Vertex and gl_ModelViewProjectionMatrix active.
|
|
//
|
|
|
|
// if (ftransformUsed) {
|
|
// TODO: 1.1 lowering functionality: track ftransform() usage
|
|
// addSymbolLinkageNode(root, symbolTable, "gl_Vertex");
|
|
// addSymbolLinkageNode(root, symbolTable, "gl_ModelViewProjectionMatrix");
|
|
//}
|
|
|
|
if (language == EShLangVertex) {
|
|
// the names won't be found in the symbol table unless the versions are right,
|
|
// so version logic does not need to be repeated here
|
|
addSymbolLinkageNode(linkage, symbolTable, "gl_VertexID");
|
|
addSymbolLinkageNode(linkage, symbolTable, "gl_InstanceID");
|
|
}
|
|
|
|
// Add a child to the root node for the linker objects
|
|
linkage->setOperator(EOpLinkerObjects);
|
|
treeRoot = growAggregate(treeRoot, linkage);
|
|
}
|
|
|
|
//
|
|
// Add the given name or symbol to the list of nodes at the end of the tree used
|
|
// for link-time checking and external linkage.
|
|
//
|
|
|
|
void TIntermediate::addSymbolLinkageNode(TIntermAggregate*& linkage, TSymbolTable& symbolTable, const TString& name)
|
|
{
|
|
TSymbol* symbol = symbolTable.find(name);
|
|
if (symbol)
|
|
addSymbolLinkageNode(linkage, *symbol->getAsVariable());
|
|
}
|
|
|
|
void TIntermediate::addSymbolLinkageNode(TIntermAggregate*& linkage, const TSymbol& symbol)
|
|
{
|
|
const TVariable* variable = symbol.getAsVariable();
|
|
if (! variable) {
|
|
// This must be a member of an anonymous block, and we need to add the whole block
|
|
const TAnonMember* anon = symbol.getAsAnonMember();
|
|
variable = &anon->getAnonContainer();
|
|
}
|
|
TIntermSymbol* node = addSymbol(*variable);
|
|
linkage = growAggregate(linkage, node);
|
|
}
|
|
|
|
//
|
|
// Add a caller->callee relationship to the call graph.
|
|
// Assumes the strings are unique per signature.
|
|
//
|
|
void TIntermediate::addToCallGraph(TInfoSink& /*infoSink*/, const TString& caller, const TString& callee)
|
|
{
|
|
// Duplicates are okay, but faster to not keep them, and they come grouped by caller,
|
|
// as long as new ones are push on the same end we check on for duplicates
|
|
for (TGraph::const_iterator call = callGraph.begin(); call != callGraph.end(); ++call) {
|
|
if (call->caller != caller)
|
|
break;
|
|
if (call->callee == callee)
|
|
return;
|
|
}
|
|
|
|
callGraph.emplace_front(caller, callee);
|
|
}
|
|
|
|
//
|
|
// This deletes the tree.
|
|
//
|
|
void TIntermediate::removeTree()
|
|
{
|
|
if (treeRoot)
|
|
RemoveAllTreeNodes(treeRoot);
|
|
}
|
|
|
|
//
|
|
// Implement the part of KHR_vulkan_glsl that lists the set of operations
|
|
// that can result in a specialization constant operation.
|
|
//
|
|
// "5.x Specialization Constant Operations"
|
|
//
|
|
// Only some operations discussed in this section may be applied to a
|
|
// specialization constant and still yield a result that is as
|
|
// specialization constant. The operations allowed are listed below.
|
|
// When a specialization constant is operated on with one of these
|
|
// operators and with another constant or specialization constant, the
|
|
// result is implicitly a specialization constant.
|
|
//
|
|
// - int(), uint(), and bool() constructors for type conversions
|
|
// from any of the following types to any of the following types:
|
|
// * int
|
|
// * uint
|
|
// * bool
|
|
// - vector versions of the above conversion constructors
|
|
// - allowed implicit conversions of the above
|
|
// - swizzles (e.g., foo.yx)
|
|
// - The following when applied to integer or unsigned integer types:
|
|
// * unary negative ( - )
|
|
// * binary operations ( + , - , * , / , % )
|
|
// * shift ( <<, >> )
|
|
// * bitwise operations ( & , | , ^ )
|
|
// - The following when applied to integer or unsigned integer scalar types:
|
|
// * comparison ( == , != , > , >= , < , <= )
|
|
// - The following when applied to the Boolean scalar type:
|
|
// * not ( ! )
|
|
// * logical operations ( && , || , ^^ )
|
|
// * comparison ( == , != )"
|
|
//
|
|
// This function just handles binary and unary nodes. Construction
|
|
// rules are handled in construction paths that are not covered by the unary
|
|
// and binary paths, while required conversions will still show up here
|
|
// as unary converters in the from a construction operator.
|
|
//
|
|
bool TIntermediate::isSpecializationOperation(const TIntermOperator& node) const
|
|
{
|
|
// The operations resulting in floating point are quite limited
|
|
// (However, some floating-point operations result in bool, like ">",
|
|
// so are handled later.)
|
|
if (node.getType().isFloatingDomain()) {
|
|
switch (node.getOp()) {
|
|
case EOpIndexDirect:
|
|
case EOpIndexIndirect:
|
|
case EOpIndexDirectStruct:
|
|
case EOpVectorSwizzle:
|
|
case EOpConvFloatToDouble:
|
|
case EOpConvDoubleToFloat:
|
|
case EOpConvFloat16ToFloat:
|
|
case EOpConvFloatToFloat16:
|
|
case EOpConvFloat16ToDouble:
|
|
case EOpConvDoubleToFloat16:
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// Check for floating-point arguments
|
|
if (const TIntermBinary* bin = node.getAsBinaryNode())
|
|
if (bin->getLeft() ->getType().isFloatingDomain() ||
|
|
bin->getRight()->getType().isFloatingDomain())
|
|
return false;
|
|
|
|
// So, for now, we can assume everything left is non-floating-point...
|
|
|
|
// Now check for integer/bool-based operations
|
|
switch (node.getOp()) {
|
|
|
|
// dereference/swizzle
|
|
case EOpIndexDirect:
|
|
case EOpIndexIndirect:
|
|
case EOpIndexDirectStruct:
|
|
case EOpVectorSwizzle:
|
|
|
|
// (u)int* -> bool
|
|
case EOpConvInt8ToBool:
|
|
case EOpConvInt16ToBool:
|
|
case EOpConvIntToBool:
|
|
case EOpConvInt64ToBool:
|
|
case EOpConvUint8ToBool:
|
|
case EOpConvUint16ToBool:
|
|
case EOpConvUintToBool:
|
|
case EOpConvUint64ToBool:
|
|
|
|
// bool -> (u)int*
|
|
case EOpConvBoolToInt8:
|
|
case EOpConvBoolToInt16:
|
|
case EOpConvBoolToInt:
|
|
case EOpConvBoolToInt64:
|
|
case EOpConvBoolToUint8:
|
|
case EOpConvBoolToUint16:
|
|
case EOpConvBoolToUint:
|
|
case EOpConvBoolToUint64:
|
|
|
|
// int8_t -> (u)int*
|
|
case EOpConvInt8ToInt16:
|
|
case EOpConvInt8ToInt:
|
|
case EOpConvInt8ToInt64:
|
|
case EOpConvInt8ToUint8:
|
|
case EOpConvInt8ToUint16:
|
|
case EOpConvInt8ToUint:
|
|
case EOpConvInt8ToUint64:
|
|
|
|
// int16_t -> (u)int*
|
|
case EOpConvInt16ToInt8:
|
|
case EOpConvInt16ToInt:
|
|
case EOpConvInt16ToInt64:
|
|
case EOpConvInt16ToUint8:
|
|
case EOpConvInt16ToUint16:
|
|
case EOpConvInt16ToUint:
|
|
case EOpConvInt16ToUint64:
|
|
|
|
// int32_t -> (u)int*
|
|
case EOpConvIntToInt8:
|
|
case EOpConvIntToInt16:
|
|
case EOpConvIntToInt64:
|
|
case EOpConvIntToUint8:
|
|
case EOpConvIntToUint16:
|
|
case EOpConvIntToUint:
|
|
case EOpConvIntToUint64:
|
|
|
|
// int64_t -> (u)int*
|
|
case EOpConvInt64ToInt8:
|
|
case EOpConvInt64ToInt16:
|
|
case EOpConvInt64ToInt:
|
|
case EOpConvInt64ToUint8:
|
|
case EOpConvInt64ToUint16:
|
|
case EOpConvInt64ToUint:
|
|
case EOpConvInt64ToUint64:
|
|
|
|
// uint8_t -> (u)int*
|
|
case EOpConvUint8ToInt8:
|
|
case EOpConvUint8ToInt16:
|
|
case EOpConvUint8ToInt:
|
|
case EOpConvUint8ToInt64:
|
|
case EOpConvUint8ToUint16:
|
|
case EOpConvUint8ToUint:
|
|
case EOpConvUint8ToUint64:
|
|
|
|
// uint16_t -> (u)int*
|
|
case EOpConvUint16ToInt8:
|
|
case EOpConvUint16ToInt16:
|
|
case EOpConvUint16ToInt:
|
|
case EOpConvUint16ToInt64:
|
|
case EOpConvUint16ToUint8:
|
|
case EOpConvUint16ToUint:
|
|
case EOpConvUint16ToUint64:
|
|
|
|
// uint32_t -> (u)int*
|
|
case EOpConvUintToInt8:
|
|
case EOpConvUintToInt16:
|
|
case EOpConvUintToInt:
|
|
case EOpConvUintToInt64:
|
|
case EOpConvUintToUint8:
|
|
case EOpConvUintToUint16:
|
|
case EOpConvUintToUint64:
|
|
|
|
// uint64_t -> (u)int*
|
|
case EOpConvUint64ToInt8:
|
|
case EOpConvUint64ToInt16:
|
|
case EOpConvUint64ToInt:
|
|
case EOpConvUint64ToInt64:
|
|
case EOpConvUint64ToUint8:
|
|
case EOpConvUint64ToUint16:
|
|
case EOpConvUint64ToUint:
|
|
|
|
// unary operations
|
|
case EOpNegative:
|
|
case EOpLogicalNot:
|
|
case EOpBitwiseNot:
|
|
|
|
// binary operations
|
|
case EOpAdd:
|
|
case EOpSub:
|
|
case EOpMul:
|
|
case EOpVectorTimesScalar:
|
|
case EOpDiv:
|
|
case EOpMod:
|
|
case EOpRightShift:
|
|
case EOpLeftShift:
|
|
case EOpAnd:
|
|
case EOpInclusiveOr:
|
|
case EOpExclusiveOr:
|
|
case EOpLogicalOr:
|
|
case EOpLogicalXor:
|
|
case EOpLogicalAnd:
|
|
case EOpEqual:
|
|
case EOpNotEqual:
|
|
case EOpLessThan:
|
|
case EOpGreaterThan:
|
|
case EOpLessThanEqual:
|
|
case EOpGreaterThanEqual:
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// Is the operation one that must propagate nonuniform?
|
|
bool TIntermediate::isNonuniformPropagating(TOperator op) const
|
|
{
|
|
// "* All Operators in Section 5.1 (Operators), except for assignment,
|
|
// arithmetic assignment, and sequence
|
|
// * Component selection in Section 5.5
|
|
// * Matrix components in Section 5.6
|
|
// * Structure and Array Operations in Section 5.7, except for the length
|
|
// method."
|
|
switch (op) {
|
|
case EOpPostIncrement:
|
|
case EOpPostDecrement:
|
|
case EOpPreIncrement:
|
|
case EOpPreDecrement:
|
|
|
|
case EOpNegative:
|
|
case EOpLogicalNot:
|
|
case EOpVectorLogicalNot:
|
|
case EOpBitwiseNot:
|
|
|
|
case EOpAdd:
|
|
case EOpSub:
|
|
case EOpMul:
|
|
case EOpDiv:
|
|
case EOpMod:
|
|
case EOpRightShift:
|
|
case EOpLeftShift:
|
|
case EOpAnd:
|
|
case EOpInclusiveOr:
|
|
case EOpExclusiveOr:
|
|
case EOpEqual:
|
|
case EOpNotEqual:
|
|
case EOpLessThan:
|
|
case EOpGreaterThan:
|
|
case EOpLessThanEqual:
|
|
case EOpGreaterThanEqual:
|
|
case EOpVectorTimesScalar:
|
|
case EOpVectorTimesMatrix:
|
|
case EOpMatrixTimesVector:
|
|
case EOpMatrixTimesScalar:
|
|
|
|
case EOpLogicalOr:
|
|
case EOpLogicalXor:
|
|
case EOpLogicalAnd:
|
|
|
|
case EOpIndexDirect:
|
|
case EOpIndexIndirect:
|
|
case EOpIndexDirectStruct:
|
|
case EOpVectorSwizzle:
|
|
return true;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
//
|
|
// Member functions of the nodes used for building the tree.
|
|
//
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
//
|
|
// Say whether or not an operation node changes the value of a variable.
|
|
//
|
|
// Returns true if state is modified.
|
|
//
|
|
bool TIntermOperator::modifiesState() const
|
|
{
|
|
switch (op) {
|
|
case EOpPostIncrement:
|
|
case EOpPostDecrement:
|
|
case EOpPreIncrement:
|
|
case EOpPreDecrement:
|
|
case EOpAssign:
|
|
case EOpAddAssign:
|
|
case EOpSubAssign:
|
|
case EOpMulAssign:
|
|
case EOpVectorTimesMatrixAssign:
|
|
case EOpVectorTimesScalarAssign:
|
|
case EOpMatrixTimesScalarAssign:
|
|
case EOpMatrixTimesMatrixAssign:
|
|
case EOpDivAssign:
|
|
case EOpModAssign:
|
|
case EOpAndAssign:
|
|
case EOpInclusiveOrAssign:
|
|
case EOpExclusiveOrAssign:
|
|
case EOpLeftShiftAssign:
|
|
case EOpRightShiftAssign:
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
//
|
|
// returns true if the operator is for one of the constructors
|
|
//
|
|
bool TIntermOperator::isConstructor() const
|
|
{
|
|
return op > EOpConstructGuardStart && op < EOpConstructGuardEnd;
|
|
}
|
|
|
|
//
|
|
// Make sure the type of an operator is appropriate for its
|
|
// combination of operation and operand type. This will invoke
|
|
// promoteUnary, promoteBinary, etc as needed.
|
|
//
|
|
// Returns false if nothing makes sense.
|
|
//
|
|
bool TIntermediate::promote(TIntermOperator* node)
|
|
{
|
|
if (node == nullptr)
|
|
return false;
|
|
|
|
if (node->getAsUnaryNode())
|
|
return promoteUnary(*node->getAsUnaryNode());
|
|
|
|
if (node->getAsBinaryNode())
|
|
return promoteBinary(*node->getAsBinaryNode());
|
|
|
|
if (node->getAsAggregate())
|
|
return promoteAggregate(*node->getAsAggregate());
|
|
|
|
return false;
|
|
}
|
|
|
|
//
|
|
// See TIntermediate::promote
|
|
//
|
|
bool TIntermediate::promoteUnary(TIntermUnary& node)
|
|
{
|
|
const TOperator op = node.getOp();
|
|
TIntermTyped* operand = node.getOperand();
|
|
|
|
switch (op) {
|
|
case EOpLogicalNot:
|
|
// Convert operand to a boolean type
|
|
if (operand->getBasicType() != EbtBool) {
|
|
// Add constructor to boolean type. If that fails, we can't do it, so return false.
|
|
TIntermTyped* converted = addConversion(op, TType(EbtBool), operand);
|
|
if (converted == nullptr)
|
|
return false;
|
|
|
|
// Use the result of converting the node to a bool.
|
|
node.setOperand(operand = converted); // also updates stack variable
|
|
}
|
|
break;
|
|
case EOpBitwiseNot:
|
|
if (!isTypeInt(operand->getBasicType()))
|
|
return false;
|
|
break;
|
|
case EOpNegative:
|
|
case EOpPostIncrement:
|
|
case EOpPostDecrement:
|
|
case EOpPreIncrement:
|
|
case EOpPreDecrement:
|
|
if (!isTypeInt(operand->getBasicType()) &&
|
|
operand->getBasicType() != EbtFloat &&
|
|
operand->getBasicType() != EbtFloat16 &&
|
|
operand->getBasicType() != EbtDouble)
|
|
|
|
return false;
|
|
break;
|
|
default:
|
|
// HLSL uses this path for initial function signature finding for built-ins
|
|
// taking a single argument, which generally don't participate in
|
|
// operator-based type promotion (type conversion will occur later).
|
|
// For now, scalar argument cases are relying on the setType() call below.
|
|
if (getSource() == EShSourceHlsl)
|
|
break;
|
|
|
|
// GLSL only allows integer arguments for the cases identified above in the
|
|
// case statements.
|
|
if (operand->getBasicType() != EbtFloat)
|
|
return false;
|
|
}
|
|
|
|
node.setType(operand->getType());
|
|
node.getWritableType().getQualifier().makeTemporary();
|
|
|
|
return true;
|
|
}
|
|
|
|
// Propagate precision qualifiers *up* from children to parent.
|
|
void TIntermUnary::updatePrecision()
|
|
{
|
|
if (getBasicType() == EbtInt || getBasicType() == EbtUint ||
|
|
getBasicType() == EbtFloat || getBasicType() == EbtFloat16) {
|
|
if (operand->getQualifier().precision > getQualifier().precision)
|
|
getQualifier().precision = operand->getQualifier().precision;
|
|
}
|
|
}
|
|
|
|
//
|
|
// See TIntermediate::promote
|
|
//
|
|
bool TIntermediate::promoteBinary(TIntermBinary& node)
|
|
{
|
|
TOperator op = node.getOp();
|
|
TIntermTyped* left = node.getLeft();
|
|
TIntermTyped* right = node.getRight();
|
|
|
|
// Arrays and structures have to be exact matches.
|
|
if ((left->isArray() || right->isArray() || left->getBasicType() == EbtStruct || right->getBasicType() == EbtStruct)
|
|
&& left->getType() != right->getType())
|
|
return false;
|
|
|
|
// Base assumption: just make the type the same as the left
|
|
// operand. Only deviations from this will be coded.
|
|
node.setType(left->getType());
|
|
node.getWritableType().getQualifier().clear();
|
|
|
|
// Composite and opaque types don't having pending operator changes, e.g.,
|
|
// array, structure, and samplers. Just establish final type and correctness.
|
|
if (left->isArray() || left->getBasicType() == EbtStruct || left->getBasicType() == EbtSampler) {
|
|
switch (op) {
|
|
case EOpEqual:
|
|
case EOpNotEqual:
|
|
if (left->getBasicType() == EbtSampler) {
|
|
// can't compare samplers
|
|
return false;
|
|
} else {
|
|
// Promote to conditional
|
|
node.setType(TType(EbtBool));
|
|
}
|
|
|
|
return true;
|
|
|
|
case EOpAssign:
|
|
// Keep type from above
|
|
|
|
return true;
|
|
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
//
|
|
// We now have only scalars, vectors, and matrices to worry about.
|
|
//
|
|
|
|
// HLSL implicitly promotes bool -> int for numeric operations.
|
|
// (Implicit conversions to make the operands match each other's types were already done.)
|
|
if (getSource() == EShSourceHlsl &&
|
|
(left->getBasicType() == EbtBool || right->getBasicType() == EbtBool)) {
|
|
switch (op) {
|
|
case EOpLessThan:
|
|
case EOpGreaterThan:
|
|
case EOpLessThanEqual:
|
|
case EOpGreaterThanEqual:
|
|
|
|
case EOpRightShift:
|
|
case EOpLeftShift:
|
|
|
|
case EOpMod:
|
|
|
|
case EOpAnd:
|
|
case EOpInclusiveOr:
|
|
case EOpExclusiveOr:
|
|
|
|
case EOpAdd:
|
|
case EOpSub:
|
|
case EOpDiv:
|
|
case EOpMul:
|
|
if (left->getBasicType() == EbtBool)
|
|
left = createConversion(EbtInt, left);
|
|
if (right->getBasicType() == EbtBool)
|
|
right = createConversion(EbtInt, right);
|
|
if (left == nullptr || right == nullptr)
|
|
return false;
|
|
node.setLeft(left);
|
|
node.setRight(right);
|
|
|
|
// Update the original base assumption on result type..
|
|
node.setType(left->getType());
|
|
node.getWritableType().getQualifier().clear();
|
|
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Do general type checks against individual operands (comparing left and right is coming up, checking mixed shapes after that)
|
|
switch (op) {
|
|
case EOpLessThan:
|
|
case EOpGreaterThan:
|
|
case EOpLessThanEqual:
|
|
case EOpGreaterThanEqual:
|
|
// Relational comparisons need numeric types and will promote to scalar Boolean.
|
|
if (left->getBasicType() == EbtBool)
|
|
return false;
|
|
|
|
node.setType(TType(EbtBool, EvqTemporary, left->getVectorSize()));
|
|
break;
|
|
|
|
case EOpEqual:
|
|
case EOpNotEqual:
|
|
if (getSource() == EShSourceHlsl) {
|
|
const int resultWidth = std::max(left->getVectorSize(), right->getVectorSize());
|
|
|
|
// In HLSL, == or != on vectors means component-wise comparison.
|
|
if (resultWidth > 1) {
|
|
op = (op == EOpEqual) ? EOpVectorEqual : EOpVectorNotEqual;
|
|
node.setOp(op);
|
|
}
|
|
|
|
node.setType(TType(EbtBool, EvqTemporary, resultWidth));
|
|
} else {
|
|
// All the above comparisons result in a bool (but not the vector compares)
|
|
node.setType(TType(EbtBool));
|
|
}
|
|
break;
|
|
|
|
case EOpLogicalAnd:
|
|
case EOpLogicalOr:
|
|
case EOpLogicalXor:
|
|
// logical ops operate only on Booleans or vectors of Booleans.
|
|
if (left->getBasicType() != EbtBool || left->isMatrix())
|
|
return false;
|
|
|
|
if (getSource() == EShSourceGlsl) {
|
|
// logical ops operate only on scalar Booleans and will promote to scalar Boolean.
|
|
if (left->isVector())
|
|
return false;
|
|
}
|
|
|
|
node.setType(TType(EbtBool, EvqTemporary, left->getVectorSize()));
|
|
break;
|
|
|
|
case EOpRightShift:
|
|
case EOpLeftShift:
|
|
case EOpRightShiftAssign:
|
|
case EOpLeftShiftAssign:
|
|
|
|
case EOpMod:
|
|
case EOpModAssign:
|
|
|
|
case EOpAnd:
|
|
case EOpInclusiveOr:
|
|
case EOpExclusiveOr:
|
|
case EOpAndAssign:
|
|
case EOpInclusiveOrAssign:
|
|
case EOpExclusiveOrAssign:
|
|
if (getSource() == EShSourceHlsl)
|
|
break;
|
|
|
|
// Check for integer-only operands.
|
|
if (!isTypeInt(left->getBasicType()) && !isTypeInt(right->getBasicType()))
|
|
return false;
|
|
if (left->isMatrix() || right->isMatrix())
|
|
return false;
|
|
|
|
break;
|
|
|
|
case EOpAdd:
|
|
case EOpSub:
|
|
case EOpDiv:
|
|
case EOpMul:
|
|
case EOpAddAssign:
|
|
case EOpSubAssign:
|
|
case EOpMulAssign:
|
|
case EOpDivAssign:
|
|
// check for non-Boolean operands
|
|
if (left->getBasicType() == EbtBool || right->getBasicType() == EbtBool)
|
|
return false;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
// Compare left and right, and finish with the cases where the operand types must match
|
|
switch (op) {
|
|
case EOpLessThan:
|
|
case EOpGreaterThan:
|
|
case EOpLessThanEqual:
|
|
case EOpGreaterThanEqual:
|
|
|
|
case EOpEqual:
|
|
case EOpNotEqual:
|
|
case EOpVectorEqual:
|
|
case EOpVectorNotEqual:
|
|
|
|
case EOpLogicalAnd:
|
|
case EOpLogicalOr:
|
|
case EOpLogicalXor:
|
|
return left->getType() == right->getType();
|
|
|
|
case EOpMod:
|
|
case EOpModAssign:
|
|
|
|
case EOpAnd:
|
|
case EOpInclusiveOr:
|
|
case EOpExclusiveOr:
|
|
case EOpAndAssign:
|
|
case EOpInclusiveOrAssign:
|
|
case EOpExclusiveOrAssign:
|
|
|
|
case EOpAdd:
|
|
case EOpSub:
|
|
case EOpDiv:
|
|
|
|
case EOpAddAssign:
|
|
case EOpSubAssign:
|
|
case EOpDivAssign:
|
|
// Quick out in case the types do match
|
|
if (left->getType() == right->getType())
|
|
return true;
|
|
|
|
// Fall through
|
|
|
|
case EOpMul:
|
|
case EOpMulAssign:
|
|
// At least the basic type has to match
|
|
if (left->getBasicType() != right->getBasicType())
|
|
return false;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (left->getType().isCoopMat() || right->getType().isCoopMat()) {
|
|
if (left->getType().isCoopMat() && right->getType().isCoopMat() &&
|
|
*left->getType().getTypeParameters() != *right->getType().getTypeParameters()) {
|
|
return false;
|
|
}
|
|
switch (op) {
|
|
case EOpMul:
|
|
case EOpMulAssign:
|
|
if (left->getType().isCoopMat() && right->getType().isCoopMat()) {
|
|
return false;
|
|
}
|
|
if (op == EOpMulAssign && right->getType().isCoopMat()) {
|
|
return false;
|
|
}
|
|
node.setOp(op == EOpMulAssign ? EOpMatrixTimesScalarAssign : EOpMatrixTimesScalar);
|
|
if (right->getType().isCoopMat()) {
|
|
node.setType(right->getType());
|
|
}
|
|
return true;
|
|
case EOpAdd:
|
|
case EOpSub:
|
|
case EOpDiv:
|
|
case EOpAssign:
|
|
// These require both to be cooperative matrices
|
|
if (!left->getType().isCoopMat() || !right->getType().isCoopMat()) {
|
|
return false;
|
|
}
|
|
return true;
|
|
default:
|
|
break;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// Finish handling the case, for all ops, where both operands are scalars.
|
|
if (left->isScalar() && right->isScalar())
|
|
return true;
|
|
|
|
// Finish handling the case, for all ops, where there are two vectors of different sizes
|
|
if (left->isVector() && right->isVector() && left->getVectorSize() != right->getVectorSize() && right->getVectorSize() > 1)
|
|
return false;
|
|
|
|
//
|
|
// We now have a mix of scalars, vectors, or matrices, for non-relational operations.
|
|
//
|
|
|
|
// Can these two operands be combined, what is the resulting type?
|
|
TBasicType basicType = left->getBasicType();
|
|
switch (op) {
|
|
case EOpMul:
|
|
if (!left->isMatrix() && right->isMatrix()) {
|
|
if (left->isVector()) {
|
|
if (left->getVectorSize() != right->getMatrixRows())
|
|
return false;
|
|
node.setOp(op = EOpVectorTimesMatrix);
|
|
node.setType(TType(basicType, EvqTemporary, right->getMatrixCols()));
|
|
} else {
|
|
node.setOp(op = EOpMatrixTimesScalar);
|
|
node.setType(TType(basicType, EvqTemporary, 0, right->getMatrixCols(), right->getMatrixRows()));
|
|
}
|
|
} else if (left->isMatrix() && !right->isMatrix()) {
|
|
if (right->isVector()) {
|
|
if (left->getMatrixCols() != right->getVectorSize())
|
|
return false;
|
|
node.setOp(op = EOpMatrixTimesVector);
|
|
node.setType(TType(basicType, EvqTemporary, left->getMatrixRows()));
|
|
} else {
|
|
node.setOp(op = EOpMatrixTimesScalar);
|
|
}
|
|
} else if (left->isMatrix() && right->isMatrix()) {
|
|
if (left->getMatrixCols() != right->getMatrixRows())
|
|
return false;
|
|
node.setOp(op = EOpMatrixTimesMatrix);
|
|
node.setType(TType(basicType, EvqTemporary, 0, right->getMatrixCols(), left->getMatrixRows()));
|
|
} else if (! left->isMatrix() && ! right->isMatrix()) {
|
|
if (left->isVector() && right->isVector()) {
|
|
; // leave as component product
|
|
} else if (left->isVector() || right->isVector()) {
|
|
node.setOp(op = EOpVectorTimesScalar);
|
|
if (right->isVector())
|
|
node.setType(TType(basicType, EvqTemporary, right->getVectorSize()));
|
|
}
|
|
} else {
|
|
return false;
|
|
}
|
|
break;
|
|
case EOpMulAssign:
|
|
if (! left->isMatrix() && right->isMatrix()) {
|
|
if (left->isVector()) {
|
|
if (left->getVectorSize() != right->getMatrixRows() || left->getVectorSize() != right->getMatrixCols())
|
|
return false;
|
|
node.setOp(op = EOpVectorTimesMatrixAssign);
|
|
} else {
|
|
return false;
|
|
}
|
|
} else if (left->isMatrix() && !right->isMatrix()) {
|
|
if (right->isVector()) {
|
|
return false;
|
|
} else {
|
|
node.setOp(op = EOpMatrixTimesScalarAssign);
|
|
}
|
|
} else if (left->isMatrix() && right->isMatrix()) {
|
|
if (left->getMatrixCols() != right->getMatrixCols() || left->getMatrixCols() != right->getMatrixRows())
|
|
return false;
|
|
node.setOp(op = EOpMatrixTimesMatrixAssign);
|
|
} else if (!left->isMatrix() && !right->isMatrix()) {
|
|
if (left->isVector() && right->isVector()) {
|
|
// leave as component product
|
|
} else if (left->isVector() || right->isVector()) {
|
|
if (! left->isVector())
|
|
return false;
|
|
node.setOp(op = EOpVectorTimesScalarAssign);
|
|
}
|
|
} else {
|
|
return false;
|
|
}
|
|
break;
|
|
|
|
case EOpRightShift:
|
|
case EOpLeftShift:
|
|
case EOpRightShiftAssign:
|
|
case EOpLeftShiftAssign:
|
|
if (right->isVector() && (! left->isVector() || right->getVectorSize() != left->getVectorSize()))
|
|
return false;
|
|
break;
|
|
|
|
case EOpAssign:
|
|
if (left->getVectorSize() != right->getVectorSize() || left->getMatrixCols() != right->getMatrixCols() || left->getMatrixRows() != right->getMatrixRows())
|
|
return false;
|
|
// fall through
|
|
|
|
case EOpAdd:
|
|
case EOpSub:
|
|
case EOpDiv:
|
|
case EOpMod:
|
|
case EOpAnd:
|
|
case EOpInclusiveOr:
|
|
case EOpExclusiveOr:
|
|
case EOpAddAssign:
|
|
case EOpSubAssign:
|
|
case EOpDivAssign:
|
|
case EOpModAssign:
|
|
case EOpAndAssign:
|
|
case EOpInclusiveOrAssign:
|
|
case EOpExclusiveOrAssign:
|
|
|
|
if ((left->isMatrix() && right->isVector()) ||
|
|
(left->isVector() && right->isMatrix()) ||
|
|
left->getBasicType() != right->getBasicType())
|
|
return false;
|
|
if (left->isMatrix() && right->isMatrix() && (left->getMatrixCols() != right->getMatrixCols() || left->getMatrixRows() != right->getMatrixRows()))
|
|
return false;
|
|
if (left->isVector() && right->isVector() && left->getVectorSize() != right->getVectorSize())
|
|
return false;
|
|
if (right->isVector() || right->isMatrix()) {
|
|
node.getWritableType().shallowCopy(right->getType());
|
|
node.getWritableType().getQualifier().makeTemporary();
|
|
}
|
|
break;
|
|
|
|
default:
|
|
return false;
|
|
}
|
|
|
|
//
|
|
// One more check for assignment.
|
|
//
|
|
switch (op) {
|
|
// The resulting type has to match the left operand.
|
|
case EOpAssign:
|
|
case EOpAddAssign:
|
|
case EOpSubAssign:
|
|
case EOpMulAssign:
|
|
case EOpDivAssign:
|
|
case EOpModAssign:
|
|
case EOpAndAssign:
|
|
case EOpInclusiveOrAssign:
|
|
case EOpExclusiveOrAssign:
|
|
case EOpLeftShiftAssign:
|
|
case EOpRightShiftAssign:
|
|
if (node.getType() != left->getType())
|
|
return false;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
//
|
|
// See TIntermediate::promote
|
|
//
|
|
bool TIntermediate::promoteAggregate(TIntermAggregate& node)
|
|
{
|
|
TOperator op = node.getOp();
|
|
TIntermSequence& args = node.getSequence();
|
|
const int numArgs = static_cast<int>(args.size());
|
|
|
|
// Presently, only hlsl does intrinsic promotions.
|
|
if (getSource() != EShSourceHlsl)
|
|
return true;
|
|
|
|
// set of opcodes that can be promoted in this manner.
|
|
switch (op) {
|
|
case EOpAtan:
|
|
case EOpClamp:
|
|
case EOpCross:
|
|
case EOpDistance:
|
|
case EOpDot:
|
|
case EOpDst:
|
|
case EOpFaceForward:
|
|
// case EOpFindMSB: TODO:
|
|
// case EOpFindLSB: TODO:
|
|
case EOpFma:
|
|
case EOpMod:
|
|
case EOpFrexp:
|
|
case EOpLdexp:
|
|
case EOpMix:
|
|
case EOpLit:
|
|
case EOpMax:
|
|
case EOpMin:
|
|
case EOpModf:
|
|
// case EOpGenMul: TODO:
|
|
case EOpPow:
|
|
case EOpReflect:
|
|
case EOpRefract:
|
|
// case EOpSinCos: TODO:
|
|
case EOpSmoothStep:
|
|
case EOpStep:
|
|
break;
|
|
default:
|
|
return true;
|
|
}
|
|
|
|
// TODO: array and struct behavior
|
|
|
|
// Try converting all nodes to the given node's type
|
|
TIntermSequence convertedArgs(numArgs, nullptr);
|
|
|
|
// Try to convert all types to the nonConvArg type.
|
|
for (int nonConvArg = 0; nonConvArg < numArgs; ++nonConvArg) {
|
|
// Try converting all args to this arg's type
|
|
for (int convArg = 0; convArg < numArgs; ++convArg) {
|
|
convertedArgs[convArg] = addConversion(op, args[nonConvArg]->getAsTyped()->getType(),
|
|
args[convArg]->getAsTyped());
|
|
}
|
|
|
|
// If we successfully converted all the args, use the result.
|
|
if (std::all_of(convertedArgs.begin(), convertedArgs.end(),
|
|
[](const TIntermNode* node) { return node != nullptr; })) {
|
|
|
|
std::swap(args, convertedArgs);
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
// Propagate precision qualifiers *up* from children to parent, and then
|
|
// back *down* again to the children's subtrees.
|
|
void TIntermBinary::updatePrecision()
|
|
{
|
|
if (getBasicType() == EbtInt || getBasicType() == EbtUint ||
|
|
getBasicType() == EbtFloat || getBasicType() == EbtFloat16) {
|
|
if (op == EOpRightShift || op == EOpLeftShift) {
|
|
// For shifts get precision from left side only and thus no need to propagate
|
|
getQualifier().precision = left->getQualifier().precision;
|
|
} else {
|
|
getQualifier().precision = std::max(right->getQualifier().precision, left->getQualifier().precision);
|
|
if (getQualifier().precision != EpqNone) {
|
|
left->propagatePrecision(getQualifier().precision);
|
|
right->propagatePrecision(getQualifier().precision);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Recursively propagate precision qualifiers *down* the subtree of the current node,
|
|
// until reaching a node that already has a precision qualifier or otherwise does
|
|
// not participate in precision propagation.
|
|
void TIntermTyped::propagatePrecision(TPrecisionQualifier newPrecision)
|
|
{
|
|
if (getQualifier().precision != EpqNone ||
|
|
(getBasicType() != EbtInt && getBasicType() != EbtUint &&
|
|
getBasicType() != EbtFloat && getBasicType() != EbtFloat16))
|
|
return;
|
|
|
|
getQualifier().precision = newPrecision;
|
|
|
|
TIntermBinary* binaryNode = getAsBinaryNode();
|
|
if (binaryNode) {
|
|
binaryNode->getLeft()->propagatePrecision(newPrecision);
|
|
binaryNode->getRight()->propagatePrecision(newPrecision);
|
|
|
|
return;
|
|
}
|
|
|
|
TIntermUnary* unaryNode = getAsUnaryNode();
|
|
if (unaryNode) {
|
|
unaryNode->getOperand()->propagatePrecision(newPrecision);
|
|
|
|
return;
|
|
}
|
|
|
|
TIntermAggregate* aggregateNode = getAsAggregate();
|
|
if (aggregateNode) {
|
|
TIntermSequence operands = aggregateNode->getSequence();
|
|
for (unsigned int i = 0; i < operands.size(); ++i) {
|
|
TIntermTyped* typedNode = operands[i]->getAsTyped();
|
|
if (! typedNode)
|
|
break;
|
|
typedNode->propagatePrecision(newPrecision);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
TIntermSelection* selectionNode = getAsSelectionNode();
|
|
if (selectionNode) {
|
|
TIntermTyped* typedNode = selectionNode->getTrueBlock()->getAsTyped();
|
|
if (typedNode) {
|
|
typedNode->propagatePrecision(newPrecision);
|
|
typedNode = selectionNode->getFalseBlock()->getAsTyped();
|
|
if (typedNode)
|
|
typedNode->propagatePrecision(newPrecision);
|
|
}
|
|
|
|
return;
|
|
}
|
|
}
|
|
|
|
TIntermTyped* TIntermediate::promoteConstantUnion(TBasicType promoteTo, TIntermConstantUnion* node) const
|
|
{
|
|
const TConstUnionArray& rightUnionArray = node->getConstArray();
|
|
int size = node->getType().computeNumComponents();
|
|
|
|
TConstUnionArray leftUnionArray(size);
|
|
|
|
for (int i=0; i < size; i++) {
|
|
|
|
#define PROMOTE(Set, CType, Get) leftUnionArray[i].Set(static_cast<CType>(rightUnionArray[i].Get()))
|
|
#define PROMOTE_TO_BOOL(Get) leftUnionArray[i].setBConst(rightUnionArray[i].Get() != 0)
|
|
|
|
#ifdef GLSLANG_WEB
|
|
#define TO_ALL(Get) \
|
|
switch (promoteTo) { \
|
|
case EbtFloat: PROMOTE(setDConst, double, Get); break; \
|
|
case EbtInt: PROMOTE(setIConst, int, Get); break; \
|
|
case EbtUint: PROMOTE(setUConst, unsigned int, Get); break; \
|
|
case EbtBool: PROMOTE_TO_BOOL(Get); break; \
|
|
default: return node; \
|
|
}
|
|
#else
|
|
#define TO_ALL(Get) \
|
|
switch (promoteTo) { \
|
|
case EbtFloat16: PROMOTE(setDConst, double, Get); break; \
|
|
case EbtFloat: PROMOTE(setDConst, double, Get); break; \
|
|
case EbtDouble: PROMOTE(setDConst, double, Get); break; \
|
|
case EbtInt8: PROMOTE(setI8Const, char, Get); break; \
|
|
case EbtInt16: PROMOTE(setI16Const, short, Get); break; \
|
|
case EbtInt: PROMOTE(setIConst, int, Get); break; \
|
|
case EbtInt64: PROMOTE(setI64Const, long long, Get); break; \
|
|
case EbtUint8: PROMOTE(setU8Const, unsigned char, Get); break; \
|
|
case EbtUint16: PROMOTE(setU16Const, unsigned short, Get); break; \
|
|
case EbtUint: PROMOTE(setUConst, unsigned int, Get); break; \
|
|
case EbtUint64: PROMOTE(setU64Const, unsigned long long, Get); break; \
|
|
case EbtBool: PROMOTE_TO_BOOL(Get); break; \
|
|
default: return node; \
|
|
}
|
|
#endif
|
|
|
|
switch (node->getType().getBasicType()) {
|
|
case EbtFloat: TO_ALL(getDConst); break;
|
|
case EbtInt: TO_ALL(getIConst); break;
|
|
case EbtUint: TO_ALL(getUConst); break;
|
|
case EbtBool: TO_ALL(getBConst); break;
|
|
#ifndef GLSLANG_WEB
|
|
case EbtFloat16: TO_ALL(getDConst); break;
|
|
case EbtDouble: TO_ALL(getDConst); break;
|
|
case EbtInt8: TO_ALL(getI8Const); break;
|
|
case EbtInt16: TO_ALL(getI16Const); break;
|
|
case EbtInt64: TO_ALL(getI64Const); break;
|
|
case EbtUint8: TO_ALL(getU8Const); break;
|
|
case EbtUint16: TO_ALL(getU16Const); break;
|
|
case EbtUint64: TO_ALL(getU64Const); break;
|
|
#endif
|
|
default: return node;
|
|
}
|
|
}
|
|
|
|
const TType& t = node->getType();
|
|
|
|
return addConstantUnion(leftUnionArray, TType(promoteTo, t.getQualifier().storage, t.getVectorSize(), t.getMatrixCols(), t.getMatrixRows()),
|
|
node->getLoc());
|
|
}
|
|
|
|
void TIntermAggregate::setPragmaTable(const TPragmaTable& pTable)
|
|
{
|
|
assert(pragmaTable == nullptr);
|
|
pragmaTable = new TPragmaTable;
|
|
*pragmaTable = pTable;
|
|
}
|
|
|
|
// If either node is a specialization constant, while the other is
|
|
// a constant (or specialization constant), the result is still
|
|
// a specialization constant.
|
|
bool TIntermediate::specConstantPropagates(const TIntermTyped& node1, const TIntermTyped& node2)
|
|
{
|
|
return (node1.getType().getQualifier().isSpecConstant() && node2.getType().getQualifier().isConstant()) ||
|
|
(node2.getType().getQualifier().isSpecConstant() && node1.getType().getQualifier().isConstant());
|
|
}
|
|
|
|
struct TextureUpgradeAndSamplerRemovalTransform : public TIntermTraverser {
|
|
void visitSymbol(TIntermSymbol* symbol) override {
|
|
if (symbol->getBasicType() == EbtSampler && symbol->getType().getSampler().isTexture()) {
|
|
symbol->getWritableType().getSampler().setCombined(true);
|
|
}
|
|
}
|
|
bool visitAggregate(TVisit, TIntermAggregate* ag) override {
|
|
using namespace std;
|
|
TIntermSequence& seq = ag->getSequence();
|
|
TQualifierList& qual = ag->getQualifierList();
|
|
|
|
// qual and seq are indexed using the same indices, so we have to modify both in lock-step
|
|
assert(seq.size() == qual.size() || qual.empty());
|
|
|
|
size_t write = 0;
|
|
for (size_t i = 0; i < seq.size(); ++i) {
|
|
TIntermSymbol* symbol = seq[i]->getAsSymbolNode();
|
|
if (symbol && symbol->getBasicType() == EbtSampler && symbol->getType().getSampler().isPureSampler()) {
|
|
// remove pure sampler variables
|
|
continue;
|
|
}
|
|
|
|
TIntermNode* result = seq[i];
|
|
|
|
// replace constructors with sampler/textures
|
|
TIntermAggregate *constructor = seq[i]->getAsAggregate();
|
|
if (constructor && constructor->getOp() == EOpConstructTextureSampler) {
|
|
if (!constructor->getSequence().empty())
|
|
result = constructor->getSequence()[0];
|
|
}
|
|
|
|
// write new node & qualifier
|
|
seq[write] = result;
|
|
if (!qual.empty())
|
|
qual[write] = qual[i];
|
|
write++;
|
|
}
|
|
|
|
seq.resize(write);
|
|
if (!qual.empty())
|
|
qual.resize(write);
|
|
|
|
return true;
|
|
}
|
|
};
|
|
|
|
void TIntermediate::performTextureUpgradeAndSamplerRemovalTransformation(TIntermNode* root)
|
|
{
|
|
TextureUpgradeAndSamplerRemovalTransform transform;
|
|
root->traverse(&transform);
|
|
}
|
|
|
|
const char* TIntermediate::getResourceName(TResourceType res)
|
|
{
|
|
switch (res) {
|
|
case EResSampler: return "shift-sampler-binding";
|
|
case EResTexture: return "shift-texture-binding";
|
|
case EResImage: return "shift-image-binding";
|
|
case EResUbo: return "shift-UBO-binding";
|
|
case EResSsbo: return "shift-ssbo-binding";
|
|
case EResUav: return "shift-uav-binding";
|
|
default:
|
|
assert(0); // internal error: should only be called with valid resource types.
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
|
|
} // end namespace glslang
|