Fix #980: flatten opaque initializers to use aliases.

This commit is contained in:
John Kessenich
2017-07-16 05:46:13 -06:00
parent 37c202aa02
commit 0e6e2ffd9c
7 changed files with 398 additions and 31 deletions

View File

@@ -506,12 +506,14 @@ TIntermTyped* HlslParseContext::handleLvalue(const TSourceLoc& loc, const char*
}
// Deal with sampler aliasing: turning assignments into aliases
// Return a placeholder node for higher-level code that think assignments must
// generate code.
TIntermTyped* HlslParseContext::handleSamplerLvalue(const TSourceLoc& loc, const char* op, TIntermTyped*& node)
{
// Can only alias an assignment: "s1 = s2"
TIntermBinary* binary = node->getAsBinaryNode();
if (binary == nullptr || node->getAsOperator()->getOp() != EOpAssign ||
binary->getLeft() ->getAsSymbolNode() == nullptr ||
binary->getLeft()->getAsSymbolNode() == nullptr ||
binary->getRight()->getAsSymbolNode() == nullptr) {
error(loc, "can't modify sampler", op, "");
return node;
@@ -520,11 +522,25 @@ TIntermTyped* HlslParseContext::handleSamplerLvalue(const TSourceLoc& loc, const
if (controlFlowNestingLevel > 0)
warn(loc, "sampler or image aliased under control flow; consumption must be in same path", op, "");
TIntermTyped* set = setOpaqueLvalue(binary->getLeft(), binary->getRight());
if (set == nullptr)
warn(loc, "could not create alias for sampler", op, "");
else
node = set;
return node;
}
// Do an opaque assignment that needs to turn into an alias.
// Return nullptr if it can't be done, otherwise return a placeholder
// node for higher-level code that think assignments must generate code.
TIntermTyped* HlslParseContext::setOpaqueLvalue(TIntermTyped* leftTyped, TIntermTyped* rightTyped)
{
// Best is if we are aliasing a flattened struct member "S.s1 = s2",
// in which case we want to update the flattening information with the alias,
// making everything else work seamlessly.
TIntermSymbol* left = binary->getLeft()->getAsSymbolNode();
TIntermSymbol* right = binary->getRight()->getAsSymbolNode();
TIntermSymbol* left = leftTyped->getAsSymbolNode();
TIntermSymbol* right = rightTyped->getAsSymbolNode();
for (auto fit = flattenMap.begin(); fit != flattenMap.end(); ++fit) {
for (auto mit = fit->second.members.begin(); mit != fit->second.members.end(); ++mit) {
if ((*mit)->getUniqueId() == left->getId()) {
@@ -533,15 +549,12 @@ TIntermTyped* HlslParseContext::handleSamplerLvalue(const TSourceLoc& loc, const
(*mit)->setUniqueId(right->getId());
// replace node (rest of compiler expects either an error or code to generate)
// with pointless access
node = binary->getRight();
return node;
return right;
}
}
}
warn(loc, "could not create alias for sampler", op, "");
return node;
return nullptr;
}
void HlslParseContext::handlePragma(const TSourceLoc& loc, const TVector<TString>& tokens)
@@ -2412,6 +2425,34 @@ TIntermAggregate* HlslParseContext::assignClipCullDistance(const TSourceLoc& loc
return assignList;
}
// For a declaration with an initializer, where the initialized symbol is flattened,
// and possibly contains opaque values, such that the initializer should never exist
// as emitted code, because even creating the initializer would write opaques.
//
// Decompose this into individual member-wise assignments, which themselves are
// expected to then not exist for opaque types, because they will turn into aliases.
//
// Return a node that contains the non-aliased assignments that must continue to exist.
TIntermAggregate* HlslParseContext::flattenedInit(const TSourceLoc& loc, TIntermSymbol* symbol, const TIntermAggregate& initializer)
{
TIntermAggregate* initList = nullptr;
// synthesize an access to each member, and then an assignment to it
const TTypeList& typeList = *symbol->getType().getStruct();
for (int member = 0; member < (int)typeList.size(); ++member) {
TIntermTyped* memberInitializer = initializer.getSequence()[member]->getAsTyped();
TIntermTyped* flattenedMember = flattenAccess(symbol, member);
if (flattenedMember->getType().containsOpaque())
setOpaqueLvalue(flattenedMember, memberInitializer);
else
initList = intermediate.growAggregate(initList, handleAssign(loc, EOpAssign, flattenedMember,
memberInitializer));
}
if (initList)
initList->setOperator(EOpSequence);
return initList;
}
// Some simple source assignments need to be flattened to a sequence
// of AST assignments. Catch these and flatten, otherwise, pass through
// to intermediate.addAssign().
@@ -2449,7 +2490,7 @@ TIntermTyped* HlslParseContext::handleAssign(const TSourceLoc& loc, TOperator op
// A temporary to store the right node's value, so we don't keep indirecting into it
// if it's not a simple symbol.
TVariable* rhsTempVar = nullptr;
TVariable* rhsTempVar = nullptr;
// If the RHS is a simple symbol node, we'll copy it for each member.
TIntermSymbol* cloneSymNode = nullptr;
@@ -7222,27 +7263,22 @@ TIntermNode* HlslParseContext::declareVariable(const TSourceLoc& loc, const TStr
error(loc, "cannot change the type of", "redeclaration", symbol->getName().c_str());
}
if (flattenVar)
flatten(loc, *symbol->getAsVariable());
if (symbol == nullptr)
return nullptr;
if (flattenVar)
flatten(loc, *symbol->getAsVariable());
if (initializer == nullptr)
return nullptr;
// Deal with initializer
TIntermNode* initNode = nullptr;
if (symbol && initializer) {
if (flattenVar)
error(loc, "flattened array with initializer list unsupported", identifier.c_str(), "");
TVariable* variable = symbol->getAsVariable();
if (variable == nullptr) {
error(loc, "initializer requires a variable, not a member", identifier.c_str(), "");
return nullptr;
}
initNode = executeInitializer(loc, initializer, variable);
TVariable* variable = symbol->getAsVariable();
if (variable == nullptr) {
error(loc, "initializer requires a variable, not a member", identifier.c_str(), "");
return nullptr;
}
return initNode;
return executeInitializer(loc, initializer, variable, flattenVar);
}
// Pick up global defaults from the provide global defaults into dst.
@@ -7308,7 +7344,7 @@ TVariable* HlslParseContext::declareNonArray(const TSourceLoc& loc, const TStrin
// Returning nullptr just means there is no code to execute to handle the
// initializer, which will, for example, be the case for constant initializers.
//
TIntermNode* HlslParseContext::executeInitializer(const TSourceLoc& loc, TIntermTyped* initializer, TVariable* variable)
TIntermNode* HlslParseContext::executeInitializer(const TSourceLoc& loc, TIntermTyped* initializer, TVariable* variable, bool flattened)
{
//
// Identifier must be of type constant, a global, or a temporary, and
@@ -7329,6 +7365,7 @@ TIntermNode* HlslParseContext::executeInitializer(const TSourceLoc& loc, TInterm
TType skeletalType;
skeletalType.shallowCopy(variable->getType());
skeletalType.getQualifier().makeTemporary();
TIntermAggregate* initializerList = nullptr;
if (initializer->getAsAggregate() && initializer->getAsAggregate()->getOp() == EOpNull)
initializer = convertInitializerList(loc, skeletalType, initializer, nullptr);
if (initializer == nullptr) {
@@ -7388,11 +7425,20 @@ TIntermNode* HlslParseContext::executeInitializer(const TSourceLoc& loc, TInterm
// normal assigning of a value to a variable...
specializationCheck(loc, initializer->getType(), "initializer");
TIntermSymbol* intermSymbol = intermediate.addSymbol(*variable, loc);
TIntermNode* initNode = handleAssign(loc, EOpAssign, intermSymbol, initializer);
if (initNode == nullptr)
assignError(loc, "=", intermSymbol->getCompleteString(), initializer->getCompleteString());
return initNode;
// If we are flattening, it could be due to setting opaques, which must be handled
// as aliases, and the 'initializer' node cannot actually be emitted, because it
// itself stores the result of the constructor, and we can't store to opaques.
// handleAssign() will emit the initializer.
TIntermNode* initNode = nullptr;
if (flattened && intermSymbol->getType().containsOpaque())
return flattenedInit(loc, intermSymbol, *initializer->getAsAggregate());
else {
initNode = handleAssign(loc, EOpAssign, intermSymbol, initializer);
if (initNode == nullptr)
assignError(loc, "=", intermSymbol->getCompleteString(), initializer->getCompleteString());
return initNode;
}
}
return nullptr;

View File

@@ -87,6 +87,7 @@ public:
void remapNonEntryPointIO(TFunction& function);
TIntermNode* handleReturnValue(const TSourceLoc&, TIntermTyped*);
void handleFunctionArgument(TFunction*, TIntermTyped*& arguments, TIntermTyped* newArg);
TIntermAggregate* flattenedInit(const TSourceLoc&, TIntermSymbol*, const TIntermAggregate&);
TIntermTyped* handleAssign(const TSourceLoc&, TOperator, TIntermTyped* left, TIntermTyped* right);
TIntermTyped* handleAssignToMatrixSwizzle(const TSourceLoc&, TOperator, TIntermTyped* left, TIntermTyped* right);
TIntermTyped* handleFunctionCall(const TSourceLoc&, TFunction*, TIntermTyped*);
@@ -191,6 +192,7 @@ public:
// Apply L-value conversions. E.g, turning a write to a RWTexture into an ImageStore.
TIntermTyped* handleLvalue(const TSourceLoc&, const char* op, TIntermTyped*& node);
TIntermTyped* handleSamplerLvalue(const TSourceLoc&, const char* op, TIntermTyped*& node);
TIntermTyped* setOpaqueLvalue(TIntermTyped* left, TIntermTyped* right);
bool lValueErrorCheck(const TSourceLoc&, const char* op, TIntermTyped*) override;
TLayoutFormat getLayoutFromTxType(const TSourceLoc&, const TType&);
@@ -231,7 +233,7 @@ protected:
TIntermSymbol* makeInternalVariableNode(const TSourceLoc&, const char* name, const TType&) const;
TVariable* declareNonArray(const TSourceLoc&, const TString& identifier, const TType&, bool track);
void declareArray(const TSourceLoc&, const TString& identifier, const TType&, TSymbol*&, bool track);
TIntermNode* executeInitializer(const TSourceLoc&, TIntermTyped* initializer, TVariable* variable);
TIntermNode* executeInitializer(const TSourceLoc&, TIntermTyped* initializer, TVariable* variable, bool flattened);
TIntermTyped* convertInitializerList(const TSourceLoc&, const TType&, TIntermTyped* initializer, TIntermTyped* scalarInit);
bool isScalarConstructor(const TIntermNode*);
TOperator mapAtomicOp(const TSourceLoc& loc, TOperator op, bool isImage);