Merge pull request #306 from johnkslang/finish-spec-const-semantics

Vulkan: Finish semantics for what creates spec-const-semantics.
This commit is contained in:
John Kessenich 2016-05-23 23:37:39 -06:00
commit 0c968f9d0e
16 changed files with 610 additions and 245 deletions

View file

@ -922,6 +922,7 @@ TIntermTyped* TIntermediate::addSelection(TIntermTyped* cond, TIntermTyped* true
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();
@ -1165,20 +1166,44 @@ void TIntermediate::removeTree()
//
// "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.
//
// It also needs to allow basic construction, swizzling, and indexing
// operations.
// - 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
{
// allow construction
if (node.isConstructor())
return true;
// The set for floating point is quite limited
if (node.getBasicType() == EbtFloat ||
node.getBasicType() == EbtDouble) {
// 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:
@ -1190,7 +1215,14 @@ bool TIntermediate::isSpecializationOperation(const TIntermOperator& node) const
}
}
// Floating-point is out of the way.
// 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()) {

View file

@ -2208,6 +2208,18 @@ bool TParseContext::builtInName(const TString& identifier)
// constructor to build something of the type of the constructor. Also returns
// the type of the constructor.
//
// Part of establishing type is establishing specialization-constness.
// We don't yet know "top down" whether type is a specialization constant,
// but a const constructor can becomes a specialization constant if any of
// its children are, subject to KHR_vulkan_glsl rules:
//
// - 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
//
// Returns true if there was an error in construction.
//
bool TParseContext::constructorError(const TSourceLoc& loc, TIntermNode* node, TFunction& function, TOperator op, TType& type)
@ -2253,6 +2265,7 @@ bool TParseContext::constructorError(const TSourceLoc& loc, TIntermNode* node, T
bool overFull = false;
bool matrixInMatrix = false;
bool arrayArg = false;
bool floatArgument = false;
for (int arg = 0; arg < function.getParamCount(); ++arg) {
if (function[arg].type->isArray()) {
if (! function[arg].type->isExplicitlySizedArray()) {
@ -2281,11 +2294,52 @@ bool TParseContext::constructorError(const TSourceLoc& loc, TIntermNode* node, T
constType = false;
if (function[arg].type->getQualifier().isSpecConstant())
specConstType = true;
if (function[arg].type->isFloatingDomain())
floatArgument = true;
}
// inherit constness from children
if (constType) {
if (specConstType)
bool makeSpecConst;
// Finish pinning down spec-const semantics
if (specConstType) {
switch (op) {
case EOpConstructInt:
case EOpConstructUint:
case EOpConstructInt64:
case EOpConstructUint64:
case EOpConstructBool:
case EOpConstructBVec2:
case EOpConstructBVec3:
case EOpConstructBVec4:
case EOpConstructIVec2:
case EOpConstructIVec3:
case EOpConstructIVec4:
case EOpConstructUVec2:
case EOpConstructUVec3:
case EOpConstructUVec4:
case EOpConstructI64Vec2:
case EOpConstructI64Vec3:
case EOpConstructI64Vec4:
case EOpConstructU64Vec2:
case EOpConstructU64Vec3:
case EOpConstructU64Vec4:
// This was the list of valid ones, if they aren't converting from float
// and aren't making an array.
makeSpecConst = ! floatArgument && ! type.isArray();
break;
default:
// anything else wasn't white-listed in the spec as a conversion
makeSpecConst = false;
break;
}
} else
makeSpecConst = false;
if (makeSpecConst)
type.getQualifier().makeSpecConstant();
else if (specConstType)
type.getQualifier().makeTemporary();
else
type.getQualifier().storage = EvqConst;
}
@ -4941,7 +4995,14 @@ TIntermNode* TParseContext::executeInitializer(const TSourceLoc& loc, TIntermTyp
// constructor-style subtree, allowing the rest of the code to operate
// identically for both kinds of initializers.
//
initializer = convertInitializerList(loc, variable->getType(), initializer);
// Type can't be deduced from the initializer list, so a skeletal type to
// follow has to be passed in. Constness and specialization-constness
// should be deduced bottom up, not dictated by the skeletal type.
//
TType skeletalType;
skeletalType.shallowCopy(variable->getType());
skeletalType.getQualifier().makeTemporary();
initializer = convertInitializerList(loc, skeletalType, initializer);
if (! initializer) {
// error recovery; don't leave const without constant values
if (qualifier == EvqConst)
@ -5030,7 +5091,7 @@ TIntermNode* TParseContext::executeInitializer(const TSourceLoc& loc, TIntermTyp
// normal assigning of a value to a variable...
specializationCheck(loc, initializer->getType(), "initializer");
TIntermSymbol* intermSymbol = intermediate.addSymbol(*variable, loc);
TIntermNode* initNode = intermediate.addAssign(EOpAssign, intermSymbol, initializer, loc);
TIntermTyped* initNode = intermediate.addAssign(EOpAssign, intermSymbol, initializer, loc);
if (! initNode)
assignError(loc, "=", intermSymbol->getCompleteString(), initializer->getCompleteString());
@ -5041,11 +5102,14 @@ TIntermNode* TParseContext::executeInitializer(const TSourceLoc& loc, TIntermTyp
}
//
// Reprocess any initializer-list { ... } parts of the initializer.
// Reprocess any initializer-list (the "{ ... }" syntax) parts of the
// initializer.
//
// Need to hierarchically assign correct types and implicit
// conversions. Will do this mimicking the same process used for
// creating a constructor-style initializer, ensuring we get the
// same form.
// same form. However, it has to in parallel walk the 'type'
// passed in, as type cannot be deduced from an initializer list.
//
TIntermTyped* TParseContext::convertInitializerList(const TSourceLoc& loc, const TType& type, TIntermTyped* initializer)
{
@ -5191,12 +5255,6 @@ TIntermTyped* TParseContext::addConstructor(const TSourceLoc& loc, TIntermNode*
// for each parameter to the constructor call, check to see if the right type is passed or convert them
// to the right type if possible (and allowed).
// for structure constructors, just check if the right type is passed, no conversion is allowed.
// We don't know "top down" whether type is a specialization constant,
// but a const becomes a specialization constant if any of its children are.
bool hasSpecConst = false;
bool isConstConstrutor = true;
for (TIntermSequence::iterator p = sequenceVector.begin();
p != sequenceVector.end(); p++, paramCount++) {
if (type.isArray())
@ -5206,21 +5264,13 @@ TIntermTyped* TParseContext::addConstructor(const TSourceLoc& loc, TIntermNode*
else
newNode = constructBuiltIn(type, op, (*p)->getAsTyped(), node->getLoc(), true);
if (newNode) {
if (newNode)
*p = newNode;
if (! newNode->getType().getQualifier().isConstant())
isConstConstrutor = false;
if (newNode->getType().getQualifier().isSpecConstant())
hasSpecConst = true;
} else
else
return nullptr;
}
TIntermTyped* constructor = intermediate.setAggregateOperator(aggrNode, op, type, loc);
if (isConstConstrutor && hasSpecConst)
constructor->getWritableType().getQualifier().makeSpecConstant();
return constructor;
return intermediate.setAggregateOperator(aggrNode, op, type, loc);
}
// Function for constructor implementation. Calls addUnaryMath with appropriate EOp value

View file

@ -346,7 +346,6 @@ protected:
static int getBaseAlignmentScalar(const TType&, int& size);
bool isSpecializationOperation(const TIntermOperator&) const;
const EShLanguage language; // stage, known at construction time
EShSource source; // source language, known a bit later
std::string entryPoint;