Commit 5d3023af by steve-lunarg Committed by John Kessenich

HLSL: Type sanitization: create non-IO types for var decl and fn param/ret

This introduces parallel types for IO-type containing aggregates used as non-entry point function parameters or return types, or declared as variables. Further uses of the same original type will share the same sanitized deep structure. This is intended to be used with the wrap-entry-point branch.
parent 0fe106af
...@@ -401,7 +401,20 @@ public: ...@@ -401,7 +401,20 @@ public:
// drop qualifiers that don't belong in a temporary variable // drop qualifiers that don't belong in a temporary variable
void makeTemporary() void makeTemporary()
{ {
makeNonIo();
storage = EvqTemporary; storage = EvqTemporary;
specConstant = false;
coherent = false;
volatil = false;
restrict = false;
readonly = false;
writeonly = false;
}
// Remove IO related data from qualifier.
void makeNonIo()
{
// This preserves the storage type
builtIn = EbvNone; builtIn = EbvNone;
centroid = false; centroid = false;
smooth = false; smooth = false;
...@@ -412,15 +425,18 @@ public: ...@@ -412,15 +425,18 @@ public:
#endif #endif
patch = false; patch = false;
sample = false; sample = false;
coherent = false;
volatil = false;
restrict = false;
readonly = false;
writeonly = false;
specConstant = false;
clearLayout(); clearLayout();
} }
// Return true if there is data which would be scrubbed by makeNonIo
bool hasIoData() const
{
return builtIn != EbvNone ||
hasLayout() ||
isInterpolation() ||
isAuxiliary();
}
// Drop just the storage qualification, which perhaps should // Drop just the storage qualification, which perhaps should
// never be done, as it is fundamentally inconsistent, but need to // never be done, as it is fundamentally inconsistent, but need to
// explore what downstream consumers need. // explore what downstream consumers need.
...@@ -1209,6 +1225,30 @@ public: ...@@ -1209,6 +1225,30 @@ public:
deepCopy(copyOf, copied); deepCopy(copyOf, copied);
} }
// Return true if type (recursively) contains IO data.
bool hasIoData() const
{
if (getQualifier().hasIoData())
return true;
if (isStruct())
for (unsigned int i = 0; i < structure->size(); ++i)
if ((*structure)[i].type->hasIoData())
return true;
return false;
}
// Remove IO related data from type
void makeNonIo()
{
getQualifier().makeNonIo();
if (isStruct())
for (unsigned int i = 0; i < structure->size(); ++i)
(*structure)[i].type->makeNonIo();
}
// Recursively make temporary // Recursively make temporary
void makeTemporary() void makeTemporary()
{ {
...@@ -1701,6 +1741,7 @@ public: ...@@ -1701,6 +1741,7 @@ public:
const char* getBuiltInVariableString() const { return GetBuiltInVariableString(qualifier.builtIn); } const char* getBuiltInVariableString() const { return GetBuiltInVariableString(qualifier.builtIn); }
const char* getPrecisionQualifierString() const { return GetPrecisionQualifierString(qualifier.precision); } const char* getPrecisionQualifierString() const { return GetPrecisionQualifierString(qualifier.precision); }
const TTypeList* getStruct() const { return structure; } const TTypeList* getStruct() const { return structure; }
void setStruct(TTypeList* s) { structure = s; }
TTypeList* getWritableStruct() const { return structure; } // This should only be used when known to not be sharing with other threads TTypeList* getWritableStruct() const { return structure; } // This should only be used when known to not be sharing with other threads
int computeNumComponents() const int computeNumComponents() const
......
...@@ -1803,15 +1803,13 @@ void HlslParseContext::remapEntryPointIO(const TFunction& function, TVariable*& ...@@ -1803,15 +1803,13 @@ void HlslParseContext::remapEntryPointIO(const TFunction& function, TVariable*&
// declares entry point IO built-ins, but these have to be undone. // declares entry point IO built-ins, but these have to be undone.
void HlslParseContext::remapNonEntryPointIO(TFunction& function) void HlslParseContext::remapNonEntryPointIO(TFunction& function)
{ {
const auto remapBuiltInType = [&](TType& type) { type.getQualifier().builtIn = EbvNone; };
// return value // return value
if (function.getType().getBasicType() != EbtVoid) if (function.getType().getBasicType() != EbtVoid)
remapBuiltInType(function.getWritableType()); makeNonIoType(&function.getWritableType());
// parameters // parameters
for (int i = 0; i < function.getParamCount(); i++) for (int i = 0; i < function.getParamCount(); i++)
remapBuiltInType(*function[i].type); makeNonIoType(function[i].type);
} }
// Handle function returns, including type conversions to the function return type // Handle function returns, including type conversions to the function return type
...@@ -5393,41 +5391,43 @@ void HlslParseContext::declareTypedef(const TSourceLoc& loc, TString& identifier ...@@ -5393,41 +5391,43 @@ void HlslParseContext::declareTypedef(const TSourceLoc& loc, TString& identifier
error(loc, "name already defined", "typedef", identifier.c_str()); error(loc, "name already defined", "typedef", identifier.c_str());
} }
// Type sanitization: return existing sanitized (temporary) type if there is one, else make new one. // Create a non-IO type from an IO type. If there is no IO data, this
TType* HlslParseContext::sanitizeType(TType* type) // returns the input type unmodified. Otherwise, it modifies the type
// in place, and returns a pointer to it.
TType* HlslParseContext::makeNonIoType(TType* type)
{ {
// We only do this for structs. // early out if there's nothing to do: prevents introduction of unneeded types.
if (!type->isStruct()) if (!type->hasIoData())
return type; return type;
// Type sanitization: if this is declaring a variable of a type that contains type->getQualifier().makeNonIo(); // Sanitize the qualifier.
// interstage IO, we want to make it a temporary.
const auto sanitizedTypeIter = sanitizedTypeMap.find(type->getStruct());
if (sanitizedTypeIter != sanitizedTypeMap.end()) { // Nothing more to do if there is no deep structure.
// We've sanitized this before. Use that one. if (!type->isStruct())
TType* sanitizedType = new TType(); return type;
sanitizedType->shallowCopy(*sanitizedTypeIter->second);
// Arrayness is not part of the sanitized type. Use the input type's arrayness. const auto typeIter = nonIoTypeMap.find(type->getStruct());
if (type->isArray())
sanitizedType->newArraySizes(type->getArraySizes()); if (typeIter != nonIoTypeMap.end()) {
else // reuse deep structure if we have sanitized it before, but we must preserve
sanitizedType->clearArraySizes(); // our unique shallow structure, which may not be shared with other users of
return sanitizedType; // the deep copy. Create a new type with the sanitized qualifier, and the
// shared deep structure
type->setStruct(typeIter->second); // share already sanitized deep structure.
} else { } else {
if (type->containsBuiltInInterstageIO(language)) { // The type contains interstage IO, but we've never encountered it before.
// This means the type contains interstage IO, but we've never encountered it before. // Copy it, scrub data we don't want for an non-IO type, and remember it in the nonIoTypeMap
// Copy it, sanitize it, and remember it in the sanitizedTypeMap
TType* sanitizedType = type->clone(); TType nonIoType;
sanitizedType->makeTemporary(); nonIoType.deepCopy(*type);
sanitizedTypeMap[type->getStruct()] = sanitizedType; nonIoType.makeNonIo();
return sanitizedType;
} else { // remember the new deep structure in a map, so we can share it in the future.
// This means the type has no interstage IO, so we can use it as is. nonIoTypeMap[type->getStruct()] = nonIoType.getWritableStruct();
return type; type->shallowCopy(nonIoType); // we modify the input type in place
} }
}
return type;
} }
// //
...@@ -5456,18 +5456,23 @@ TIntermNode* HlslParseContext::declareVariable(const TSourceLoc& loc, TString& i ...@@ -5456,18 +5456,23 @@ TIntermNode* HlslParseContext::declareVariable(const TSourceLoc& loc, TString& i
const bool flattenVar = shouldFlattenUniform(type); const bool flattenVar = shouldFlattenUniform(type);
// Type sanitization: if this is declaring a variable of a type that contains // make non-IO version of type
// interstage IO, we want to make it a temporary. switch (type.getQualifier().storage) {
TType* sanitizedType = sanitizeType(&type); case EvqGlobal:
case EvqTemporary:
makeNonIoType(&type);
default:
break;
}
// Declare the variable // Declare the variable
if (type.isArray()) { if (type.isArray()) {
// array case // array case
declareArray(loc, identifier, *sanitizedType, symbol, !flattenVar); declareArray(loc, identifier, type, symbol, !flattenVar);
} else { } else {
// non-array case // non-array case
if (! symbol) if (! symbol)
symbol = declareNonArray(loc, identifier, *sanitizedType, !flattenVar); symbol = declareNonArray(loc, identifier, type, !flattenVar);
else if (type != symbol->getType()) else if (type != symbol->getType())
error(loc, "cannot change the type of", "redeclaration", symbol->getName().c_str()); error(loc, "cannot change the type of", "redeclaration", symbol->getName().c_str());
} }
......
...@@ -228,8 +228,9 @@ protected: ...@@ -228,8 +228,9 @@ protected:
int flattenStruct(const TSourceLoc& loc, const TVariable& variable, const TType&, TFlattenData&, TString name); int flattenStruct(const TSourceLoc& loc, const TVariable& variable, const TType&, TFlattenData&, TString name);
int flattenArray(const TSourceLoc& loc, const TVariable& variable, const TType&, TFlattenData&, TString name); int flattenArray(const TSourceLoc& loc, const TVariable& variable, const TType&, TFlattenData&, TString name);
// Type sanitization: return existing sanitized (temporary) type if there is one, else make new one. // Create a non-IO type from an IO type. If there is no IO data, this returns the input type unmodified.
TType* sanitizeType(TType*); // Otherwise, it modifies the type in place, and returns a pointer to it.
TType* makeNonIoType(TType*);
void finish() override; // post-processing void finish() override; // post-processing
...@@ -296,9 +297,9 @@ protected: ...@@ -296,9 +297,9 @@ protected:
TVector<int> flattenLevel; // nested postfix operator level for flattening TVector<int> flattenLevel; // nested postfix operator level for flattening
TVector<int> flattenOffset; // cumulative offset for flattening TVector<int> flattenOffset; // cumulative offset for flattening
// Sanitized type map. During declarations we use the sanitized form of the type // Sanitized type map. If the same type is sanitized again, we want to reuse it.
// if it exists. // We cannot index by the TType: the map is typelist to typelist.
TMap<const TTypeList*, TType*> sanitizedTypeMap; TMap<const TTypeList*, TTypeList*> nonIoTypeMap;
// Structure splitting data: // Structure splitting data:
TMap<int, TVariable*> splitIoVars; // variables with the builtin interstage IO removed, indexed by unique ID. TMap<int, TVariable*> splitIoVars; // variables with the builtin interstage IO removed, indexed by unique ID.
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment