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:
// drop qualifiers that don't belong in a temporary variable
void makeTemporary()
{
makeNonIo();
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;
centroid = false;
smooth = false;
......@@ -412,15 +425,18 @@ public:
#endif
patch = false;
sample = false;
coherent = false;
volatil = false;
restrict = false;
readonly = false;
writeonly = false;
specConstant = false;
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
// never be done, as it is fundamentally inconsistent, but need to
// explore what downstream consumers need.
......@@ -1209,6 +1225,30 @@ public:
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
void makeTemporary()
{
......@@ -1701,6 +1741,7 @@ public:
const char* getBuiltInVariableString() const { return GetBuiltInVariableString(qualifier.builtIn); }
const char* getPrecisionQualifierString() const { return GetPrecisionQualifierString(qualifier.precision); }
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
int computeNumComponents() const
......
......@@ -1803,15 +1803,13 @@ void HlslParseContext::remapEntryPointIO(const TFunction& function, TVariable*&
// declares entry point IO built-ins, but these have to be undone.
void HlslParseContext::remapNonEntryPointIO(TFunction& function)
{
const auto remapBuiltInType = [&](TType& type) { type.getQualifier().builtIn = EbvNone; };
// return value
if (function.getType().getBasicType() != EbtVoid)
remapBuiltInType(function.getWritableType());
makeNonIoType(&function.getWritableType());
// parameters
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
......@@ -5393,41 +5391,43 @@ void HlslParseContext::declareTypedef(const TSourceLoc& loc, TString& identifier
error(loc, "name already defined", "typedef", identifier.c_str());
}
// Type sanitization: return existing sanitized (temporary) type if there is one, else make new one.
TType* HlslParseContext::sanitizeType(TType* type)
// Create a non-IO type from an IO type. If there is no IO data, this
// 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.
if (!type->isStruct())
// early out if there's nothing to do: prevents introduction of unneeded types.
if (!type->hasIoData())
return type;
// Type sanitization: if this is declaring a variable of a type that contains
// interstage IO, we want to make it a temporary.
const auto sanitizedTypeIter = sanitizedTypeMap.find(type->getStruct());
type->getQualifier().makeNonIo(); // Sanitize the qualifier.
if (sanitizedTypeIter != sanitizedTypeMap.end()) {
// We've sanitized this before. Use that one.
TType* sanitizedType = new TType();
sanitizedType->shallowCopy(*sanitizedTypeIter->second);
// Nothing more to do if there is no deep structure.
if (!type->isStruct())
return type;
// Arrayness is not part of the sanitized type. Use the input type's arrayness.
if (type->isArray())
sanitizedType->newArraySizes(type->getArraySizes());
else
sanitizedType->clearArraySizes();
return sanitizedType;
const auto typeIter = nonIoTypeMap.find(type->getStruct());
if (typeIter != nonIoTypeMap.end()) {
// reuse deep structure if we have sanitized it before, but we must preserve
// our unique shallow structure, which may not be shared with other users of
// 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 {
if (type->containsBuiltInInterstageIO(language)) {
// This means the type contains interstage IO, but we've never encountered it before.
// Copy it, sanitize it, and remember it in the sanitizedTypeMap
TType* sanitizedType = type->clone();
sanitizedType->makeTemporary();
sanitizedTypeMap[type->getStruct()] = sanitizedType;
return sanitizedType;
} else {
// This means the type has no interstage IO, so we can use it as is.
return type;
}
}
// 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
TType nonIoType;
nonIoType.deepCopy(*type);
nonIoType.makeNonIo();
// remember the new deep structure in a map, so we can share it in the future.
nonIoTypeMap[type->getStruct()] = nonIoType.getWritableStruct();
type->shallowCopy(nonIoType); // we modify the input type in place
}
return type;
}
//
......@@ -5456,18 +5456,23 @@ TIntermNode* HlslParseContext::declareVariable(const TSourceLoc& loc, TString& i
const bool flattenVar = shouldFlattenUniform(type);
// Type sanitization: if this is declaring a variable of a type that contains
// interstage IO, we want to make it a temporary.
TType* sanitizedType = sanitizeType(&type);
// make non-IO version of type
switch (type.getQualifier().storage) {
case EvqGlobal:
case EvqTemporary:
makeNonIoType(&type);
default:
break;
}
// Declare the variable
if (type.isArray()) {
// array case
declareArray(loc, identifier, *sanitizedType, symbol, !flattenVar);
declareArray(loc, identifier, type, symbol, !flattenVar);
} else {
// non-array case
if (! symbol)
symbol = declareNonArray(loc, identifier, *sanitizedType, !flattenVar);
symbol = declareNonArray(loc, identifier, type, !flattenVar);
else if (type != symbol->getType())
error(loc, "cannot change the type of", "redeclaration", symbol->getName().c_str());
}
......
......@@ -228,8 +228,9 @@ protected:
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);
// Type sanitization: return existing sanitized (temporary) type if there is one, else make new one.
TType* sanitizeType(TType*);
// Create a non-IO type from an IO type. If there is no IO data, this returns the input type unmodified.
// Otherwise, it modifies the type in place, and returns a pointer to it.
TType* makeNonIoType(TType*);
void finish() override; // post-processing
......@@ -296,9 +297,9 @@ protected:
TVector<int> flattenLevel; // nested postfix operator level for flattening
TVector<int> flattenOffset; // cumulative offset for flattening
// Sanitized type map. During declarations we use the sanitized form of the type
// if it exists.
TMap<const TTypeList*, TType*> sanitizedTypeMap;
// Sanitized type map. If the same type is sanitized again, we want to reuse it.
// We cannot index by the TType: the map is typelist to typelist.
TMap<const TTypeList*, TTypeList*> nonIoTypeMap;
// Structure splitting data:
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