HLSL: Fix #805: Support cast of scalars to structures.

Somewhat complex due to recognizing a general scalar, but not
replicating it for each member to avoid side effects.
This commit is contained in:
John Kessenich
2017-04-04 11:47:42 -06:00
parent 5ce1e4aff8
commit 82460b5e21
6 changed files with 617 additions and 40 deletions

View File

@@ -335,13 +335,6 @@ TIntermTyped* HlslParseContext::handleLvalue(const TSourceLoc& loc, const char*
[](bool isSet) { return isSet; } );
};
// helper to create a temporary variable
const auto addTmpVar = [&](const char* name, const TType& derefType) -> TIntermSymbol* {
TVariable* tmpVar = makeInternalVariable(name, derefType);
tmpVar->getWritableType().getQualifier().makeTemporary();
return intermediate.addSymbol(*tmpVar, loc);
};
// Create swizzle matching input swizzle
const auto addSwizzle = [&](TIntermSymbol* var, TIntermBinary* swizzle) -> TIntermTyped* {
if (swizzle)
@@ -419,7 +412,7 @@ TIntermTyped* HlslParseContext::handleLvalue(const TSourceLoc& loc, const char*
TIntermTyped* coordTmp = coord;
if (rhsTmp == nullptr || isModifyOp || lhsIsSwizzle) {
rhsTmp = addTmpVar("storeTemp", objDerefType);
rhsTmp = makeInternalVariableNode(loc, "storeTemp", objDerefType);
// Partial updates not yet supported
if (!writesAllComponents(rhsTmp, lhsAsBinary)) {
@@ -429,7 +422,7 @@ TIntermTyped* HlslParseContext::handleLvalue(const TSourceLoc& loc, const char*
// Assign storeTemp = rhs
if (isModifyOp) {
// We have to make a temp var for the coordinate, to avoid evaluating it twice.
coordTmp = addTmpVar("coordTemp", coord->getType());
coordTmp = makeInternalVariableNode(loc, "coordTemp", coord->getType());
makeBinary(EOpAssign, coordTmp, coord); // coordtmp = load[param1]
makeLoad(rhsTmp, object, coordTmp, objDerefType); // rhsTmp = OpImageLoad(object, coordTmp)
}
@@ -462,8 +455,8 @@ TIntermTyped* HlslParseContext::handleLvalue(const TSourceLoc& loc, const char*
// OpImageStore(object, coordTmp, rhsTmp)
// rhsTmp
TIntermSymbol* rhsTmp = addTmpVar("storeTemp", objDerefType);
TIntermTyped* coordTmp = addTmpVar("coordTemp", coord->getType());
TIntermSymbol* rhsTmp = makeInternalVariableNode(loc, "storeTemp", objDerefType);
TIntermTyped* coordTmp = makeInternalVariableNode(loc, "coordTemp", coord->getType());
makeBinary(EOpAssign, coordTmp, coord); // coordtmp = load[param1]
makeLoad(rhsTmp, object, coordTmp, objDerefType); // rhsTmp = OpImageLoad(object, coordTmp)
@@ -483,9 +476,9 @@ TIntermTyped* HlslParseContext::handleLvalue(const TSourceLoc& loc, const char*
// rhsTmp2 op
// OpImageStore(object, coordTmp, rhsTmp2)
// rhsTmp1 (pre-op value)
TIntermSymbol* rhsTmp1 = addTmpVar("storeTempPre", objDerefType);
TIntermSymbol* rhsTmp2 = addTmpVar("storeTempPost", objDerefType);
TIntermTyped* coordTmp = addTmpVar("coordTemp", coord->getType());
TIntermSymbol* rhsTmp1 = makeInternalVariableNode(loc, "storeTempPre", objDerefType);
TIntermSymbol* rhsTmp2 = makeInternalVariableNode(loc, "storeTempPost", objDerefType);
TIntermTyped* coordTmp = makeInternalVariableNode(loc, "coordTemp", coord->getType());
makeBinary(EOpAssign, coordTmp, coord); // coordtmp = load[param1]
makeLoad(rhsTmp1, object, coordTmp, objDerefType); // rhsTmp1 = OpImageLoad(object, coordTmp)
@@ -4739,7 +4732,7 @@ bool HlslParseContext::constructorError(const TSourceLoc& loc, TIntermNode* node
return true;
}
if (op == EOpConstructStruct && ! type.isArray() && isZeroConstructor(node))
if (op == EOpConstructStruct && ! type.isArray() && isScalarConstructor(node))
return false;
if (op == EOpConstructStruct && ! type.isArray() && (int)type.getStruct()->size() != function.getParamCount()) {
@@ -4756,10 +4749,21 @@ bool HlslParseContext::constructorError(const TSourceLoc& loc, TIntermNode* node
return false;
}
bool HlslParseContext::isZeroConstructor(const TIntermNode* node)
// See if 'node', in the context of constructing aggregates, is a scalar argument
// to a constructor.
//
bool HlslParseContext::isScalarConstructor(const TIntermNode* node)
{
return node->getAsTyped()->isScalar() && node->getAsConstantUnion() &&
node->getAsConstantUnion()->getConstArray()[0].getIConst() == 0;
// Obviously, it must be a scalar, but an aggregate node might not be fully
// completed yet: holding a sequence of initializers under an aggregate
// would not yet be typed, so don't check it's type. This corresponds to
// the aggregate operator also not being set yet. (An aggregate operation
// that legitimately yields a scalar will have a getOp() of that operator,
// not EOpNull.)
return node->getAsTyped() != nullptr &&
node->getAsTyped()->isScalar() &&
(node->getAsAggregate() == nullptr || node->getAsAggregate()->getOp() != EOpNull);
}
// Verify all the correct semantics for constructing a combined texture/sampler.
@@ -6295,6 +6299,15 @@ TVariable* HlslParseContext::makeInternalVariable(const char* name, const TType&
return variable;
}
// Make a symbol node holding a new internal temporary variable.
TIntermSymbol* HlslParseContext::makeInternalVariableNode(const TSourceLoc& loc, const char* name, const TType& type) const
{
TVariable* tmpVar = makeInternalVariable(name, type);
tmpVar->getWritableType().getQualifier().makeTemporary();
return intermediate.addSymbol(*tmpVar, loc);
}
//
// Declare a non-array variable, the main point being there is no redeclaration
// for resizing allowed.
@@ -6345,7 +6358,7 @@ TIntermNode* HlslParseContext::executeInitializer(const TSourceLoc& loc, TInterm
skeletalType.shallowCopy(variable->getType());
skeletalType.getQualifier().makeTemporary();
if (initializer->getAsAggregate() && initializer->getAsAggregate()->getOp() == EOpNull)
initializer = convertInitializerList(loc, skeletalType, initializer);
initializer = convertInitializerList(loc, skeletalType, initializer, nullptr);
if (! initializer) {
// error recovery; don't leave const without constant values
if (qualifier == EvqConst)
@@ -6427,7 +6440,8 @@ TIntermNode* HlslParseContext::executeInitializer(const TSourceLoc& loc, TInterm
//
// Returns nullptr if there is an error.
//
TIntermTyped* HlslParseContext::convertInitializerList(const TSourceLoc& loc, const TType& type, TIntermTyped* initializer)
TIntermTyped* HlslParseContext::convertInitializerList(const TSourceLoc& loc, const TType& type,
TIntermTyped* initializer, TIntermTyped* scalarInit)
{
// Will operate recursively. Once a subtree is found that is constructor style,
// everything below it is already good: Only the "top part" of the initializer
@@ -6473,12 +6487,12 @@ TIntermTyped* HlslParseContext::convertInitializerList(const TSourceLoc& loc, co
}
// lengthen list to be long enough
lengthenList(loc, initList->getSequence(), arrayType.getOuterArraySize());
lengthenList(loc, initList->getSequence(), arrayType.getOuterArraySize(), scalarInit);
// recursively process each element
TType elementType(arrayType, 0); // dereferenced type
for (int i = 0; i < arrayType.getOuterArraySize(); ++i) {
initList->getSequence()[i] = convertInitializerList(loc, elementType, initList->getSequence()[i]->getAsTyped());
initList->getSequence()[i] = convertInitializerList(loc, elementType, initList->getSequence()[i]->getAsTyped(), scalarInit);
if (initList->getSequence()[i] == nullptr)
return nullptr;
}
@@ -6486,14 +6500,14 @@ TIntermTyped* HlslParseContext::convertInitializerList(const TSourceLoc& loc, co
return addConstructor(loc, initList, arrayType);
} else if (type.isStruct()) {
// lengthen list to be long enough
lengthenList(loc, initList->getSequence(), static_cast<int>(type.getStruct()->size()));
lengthenList(loc, initList->getSequence(), static_cast<int>(type.getStruct()->size()), scalarInit);
if (type.getStruct()->size() != initList->getSequence().size()) {
error(loc, "wrong number of structure members", "initializer list", "");
return nullptr;
}
for (size_t i = 0; i < type.getStruct()->size(); ++i) {
initList->getSequence()[i] = convertInitializerList(loc, *(*type.getStruct())[i].type, initList->getSequence()[i]->getAsTyped());
initList->getSequence()[i] = convertInitializerList(loc, *(*type.getStruct())[i].type, initList->getSequence()[i]->getAsTyped(), scalarInit);
if (initList->getSequence()[i] == nullptr)
return nullptr;
}
@@ -6504,7 +6518,7 @@ TIntermTyped* HlslParseContext::convertInitializerList(const TSourceLoc& loc, co
// a constructor; no further processing needed.
} else {
// lengthen list to be long enough
lengthenList(loc, initList->getSequence(), type.getMatrixCols());
lengthenList(loc, initList->getSequence(), type.getMatrixCols(), scalarInit);
if (type.getMatrixCols() != (int)initList->getSequence().size()) {
error(loc, "wrong number of matrix columns:", "initializer list", type.getCompleteString().c_str());
@@ -6512,14 +6526,14 @@ TIntermTyped* HlslParseContext::convertInitializerList(const TSourceLoc& loc, co
}
TType vectorType(type, 0); // dereferenced type
for (int i = 0; i < type.getMatrixCols(); ++i) {
initList->getSequence()[i] = convertInitializerList(loc, vectorType, initList->getSequence()[i]->getAsTyped());
initList->getSequence()[i] = convertInitializerList(loc, vectorType, initList->getSequence()[i]->getAsTyped(), scalarInit);
if (initList->getSequence()[i] == nullptr)
return nullptr;
}
}
} else if (type.isVector()) {
// lengthen list to be long enough
lengthenList(loc, initList->getSequence(), type.getVectorSize());
lengthenList(loc, initList->getSequence(), type.getVectorSize(), scalarInit);
// error check; we're at bottom, so work is finished below
if (type.getVectorSize() != (int)initList->getSequence().size()) {
@@ -6528,7 +6542,7 @@ TIntermTyped* HlslParseContext::convertInitializerList(const TSourceLoc& loc, co
}
} else if (type.isScalar()) {
// lengthen list to be long enough
lengthenList(loc, initList->getSequence(), 1);
lengthenList(loc, initList->getSequence(), 1, scalarInit);
if ((int)initList->getSequence().size() != 1) {
error(loc, "scalar expected one element:", "initializer list", type.getCompleteString().c_str());
@@ -6553,10 +6567,21 @@ TIntermTyped* HlslParseContext::convertInitializerList(const TSourceLoc& loc, co
// Lengthen list to be long enough to cover any gap from the current list size
// to 'size'. If the list is longer, do nothing.
// The value to lengthen with is the default for short lists.
void HlslParseContext::lengthenList(const TSourceLoc& loc, TIntermSequence& list, int size)
//
// By default, lists that are too short due to lack of initializers initialize to zero.
// Alternatively, it could be a scalar initializer for a structure. Both cases are handled,
// based on whether something is passed in as 'scalarInit'.
//
// 'scalarInit' must be safe to use each time this is called (no side effects replication).
//
void HlslParseContext::lengthenList(const TSourceLoc& loc, TIntermSequence& list, int size, TIntermTyped* scalarInit)
{
for (int c = (int)list.size(); c < size; ++c)
list.push_back(intermediate.addConstantUnion(0, loc));
for (int c = (int)list.size(); c < size; ++c) {
if (scalarInit == nullptr)
list.push_back(intermediate.addConstantUnion(0, loc));
else
list.push_back(scalarInit);
}
}
//
@@ -6570,11 +6595,23 @@ TIntermTyped* HlslParseContext::handleConstructor(const TSourceLoc& loc, TInterm
if (node == nullptr)
return nullptr;
// Handle the idiom "(struct type)0"
// Sequences (in an aggregate) for initialization are not yet tagged with
// an operator or type, so it is too early to ask if they are scalars.
if (type.isStruct() && isZeroConstructor(node))
return convertInitializerList(loc, type, intermediate.makeAggregate(loc));
// Handle the idiom "(struct type)<scalar value>"
if (type.isStruct() && isScalarConstructor(node)) {
// 'node' will almost always get used multiple times, so should not be used directly,
// it would create a DAG instead of a tree, which might be okay (would
// like to formalize that for constants and symbols), but if it has
// side effects, they would get executed multiple times, which is not okay.
if (node->getAsConstantUnion() == nullptr && node->getAsSymbolNode() == nullptr) {
TIntermAggregate* seq = intermediate.makeAggregate(loc);
TIntermSymbol* copy = makeInternalVariableNode(loc, "scalarCopy", node->getType());
seq = intermediate.growAggregate(seq, intermediate.addBinaryNode(EOpAssign, copy, node, loc));
seq = intermediate.growAggregate(seq, convertInitializerList(loc, type, intermediate.makeAggregate(loc), copy));
seq->setOp(EOpComma);
seq->setType(type);
return seq;
} else
return convertInitializerList(loc, type, intermediate.makeAggregate(loc), node);
}
return addConstructor(loc, node, type);
}