summaryrefslogtreecommitdiff
path: root/source/slang/legalize-types.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'source/slang/legalize-types.cpp')
-rw-r--r--source/slang/legalize-types.cpp1086
1 files changed, 1086 insertions, 0 deletions
diff --git a/source/slang/legalize-types.cpp b/source/slang/legalize-types.cpp
new file mode 100644
index 000000000..510b9acd3
--- /dev/null
+++ b/source/slang/legalize-types.cpp
@@ -0,0 +1,1086 @@
+// legalize-types.cpp
+#include "legalize-types.h"
+
+namespace Slang
+{
+
+LegalType LegalType::implicitDeref(
+ LegalType const& valueType)
+{
+ RefPtr<ImplicitDerefType> obj = new ImplicitDerefType();
+ obj->valueType = valueType;
+
+ LegalType result;
+ result.flavor = Flavor::implicitDeref;
+ result.obj = obj;
+ return result;
+}
+
+LegalType LegalType::tuple(
+ RefPtr<TuplePseudoType> tupleType)
+{
+ LegalType result;
+ result.flavor = Flavor::tuple;
+ result.obj = tupleType;
+ return result;
+}
+
+LegalType LegalType::pair(
+ RefPtr<PairPseudoType> pairType)
+{
+ LegalType result;
+ result.flavor = Flavor::pair;
+ result.obj = pairType;
+ return result;
+}
+
+LegalType LegalType::pair(
+ LegalType const& ordinaryType,
+ LegalType const& specialType,
+ RefPtr<PairInfo> pairInfo)
+{
+ // Handle some special cases for when
+ // one or the other of the types isn't
+ // actually used.
+
+ if (ordinaryType.flavor == LegalType::Flavor::none)
+ {
+ // There was nothing ordinary.
+ return specialType;
+ }
+
+ if (specialType.flavor == LegalType::Flavor::none)
+ {
+ return ordinaryType;
+ }
+
+ // There were both ordinary and special fields,
+ // and so we need to handle them here.
+
+ RefPtr<PairPseudoType> obj = new PairPseudoType();
+ obj->ordinaryType = ordinaryType;
+ obj->specialType = specialType;
+ obj->pairInfo = pairInfo;
+ return LegalType::pair(obj);
+}
+
+//
+
+static bool isResourceType(Type* type)
+{
+ while (auto arrayType = type->As<ArrayExpressionType>())
+ {
+ type = arrayType->baseType;
+ }
+
+ if (auto resourceTypeBase = type->As<ResourceTypeBase>())
+ {
+ return true;
+ }
+ else if (auto builtinGenericType = type->As<BuiltinGenericType>())
+ {
+ return true;
+ }
+ else if (auto pointerLikeType = type->As<PointerLikeType>())
+ {
+ return true;
+ }
+ else if (auto samplerType = type->As<SamplerStateType>())
+ {
+ return true;
+ }
+
+ // TODO: need more comprehensive coverage here
+
+ return false;
+}
+
+ModuleDecl* findModuleForDecl(
+ Decl* decl)
+{
+ for (auto dd = decl; dd; dd = dd->ParentDecl)
+ {
+ if (auto moduleDecl = dynamic_cast<ModuleDecl*>(dd))
+ return moduleDecl;
+ }
+ return nullptr;
+}
+
+
+// Helper type for legalization of aggregate types
+// that might need to be turned into tuple pseudo-types.
+struct TupleTypeBuilder
+{
+ TypeLegalizationContext* context;
+ RefPtr<Type> type;
+ DeclRef<AggTypeDecl> typeDeclRef;
+
+ struct OrdinaryElement
+ {
+ DeclRef<VarDeclBase> fieldDeclRef;
+ RefPtr<Type> type;
+ };
+
+
+ List<OrdinaryElement> ordinaryElements;
+ List<TuplePseudoType::Element> specialElements;
+
+ List<PairInfo::Element> pairElements;
+
+ // Did we have any fields that forced us to change
+ // the actual type away from the declared type?
+ bool anyComplex = false;
+
+ // Did we have any fields that actually required
+ // storage in the "special" part of things?
+ bool anySpecial = false;
+
+ // Did we have any fields that actually used ordinary storage?
+ bool anyOrdinary = false;
+
+ // Add a field to the (pseudo-)type we are building
+ void addField(
+ DeclRef<VarDeclBase> fieldDeclRef,
+ LegalType legalFieldType,
+ LegalType legalLeafType,
+ bool isResource)
+ {
+ LegalType ordinaryType;
+ LegalType specialType;
+ RefPtr<PairInfo> elementPairInfo;
+ switch (legalLeafType.flavor)
+ {
+ case LegalType::Flavor::simple:
+ {
+ // We need to add an actual field, but we need
+ // to check if it is a resource type to know
+ // whether it should go in the "ordinary" list or not.
+ if (!isResource)
+ {
+ ordinaryType = legalLeafType;
+ }
+ else
+ {
+ specialType = legalFieldType;
+ }
+ }
+ break;
+
+ case LegalType::Flavor::implicitDeref:
+ {
+ // TODO: we may want to say that any use
+ // of `implicitDeref` puts the entire thing
+ // into the "special" category, rather than
+ // try to look under the hood...
+
+ anyComplex = true;
+
+ // We want to recursively add data
+ // based on the unwrapped type.
+ //
+ // Note: this assumes we can't have a tuple
+ // or a pair "under" an `implicitDeref`, so
+ // we'll need to ensure that elsewhere.
+ addField(
+ fieldDeclRef,
+ legalFieldType,
+ legalLeafType.getImplicitDeref()->valueType,
+ isResource);
+ return;
+ }
+ break;
+
+ case LegalType::Flavor::pair:
+ {
+ // The field's type had both special and non-special parts
+ auto pairType = legalLeafType.getPair();
+ ordinaryType = pairType->ordinaryType;
+ specialType = pairType->specialType;
+ elementPairInfo = pairType->pairInfo;
+ }
+ break;
+
+ case LegalType::Flavor::tuple:
+ {
+ // A tuple always represents "special" data
+ specialType = legalFieldType;
+ }
+ break;
+
+ default:
+ SLANG_UNEXPECTED("unknown legal type flavor");
+ break;
+ }
+
+
+ PairInfo::Element pairElement;
+ pairElement.flags = 0;
+ pairElement.fieldDeclRef = fieldDeclRef;
+ pairElement.fieldPairInfo = elementPairInfo;
+
+ // We will always add a field to the "ordinary"
+ // side of things, even if it has no ordinary
+ // data, just to keep the list of fields aligned
+ // with the original type.
+ OrdinaryElement ordinaryElement;
+ ordinaryElement.fieldDeclRef = fieldDeclRef;
+ if (ordinaryType.flavor != LegalType::Flavor::none)
+ {
+ anyOrdinary = true;
+ pairElement.flags |= PairInfo::kFlag_hasOrdinary;
+
+ LegalType ot = ordinaryType;
+
+ // TODO: any cases we should "unwrap" here?
+ // E.g., `implicitDeref`?
+
+ if(ot.flavor == LegalType::Flavor::simple)
+ {
+ ordinaryElement.type = ot.getSimple();
+ }
+ else
+ {
+ SLANG_UNEXPECTED("unexpected ordinary field type");
+ }
+ }
+ ordinaryElements.Add(ordinaryElement);
+
+ if (specialType.flavor != LegalType::Flavor::none)
+ {
+ anySpecial = true;
+ anyComplex = true;
+ pairElement.flags |= PairInfo::kFlag_hasSpecial;
+
+ TuplePseudoType::Element specialElement;
+ specialElement.fieldDeclRef = fieldDeclRef;
+ specialElement.type = specialType;
+ specialElements.Add(specialElement);
+ }
+
+ pairElement.type = LegalType::pair(ordinaryType, specialType, elementPairInfo);
+ pairElements.Add(pairElement);
+ }
+
+ // Add a field to the (pseudo-)type we are building
+ void addField(
+ DeclRef<VarDeclBase> fieldDeclRef)
+ {
+ // Skip `static` fields.
+ if (fieldDeclRef.getDecl()->HasModifier<HLSLStaticModifier>())
+ return;
+
+ auto fieldType = GetType(fieldDeclRef);
+
+ bool isResourceField = isResourceType(fieldType);
+
+ auto legalFieldType = legalizeType(context, fieldType);
+ addField(
+ fieldDeclRef,
+ legalFieldType,
+ legalFieldType,
+ isResourceField);
+ }
+
+ LegalType getResult()
+ {
+ // If we didn't see anything "special"
+ // then we can use the type as-is.
+ // we can conceivably just use the type as-is
+ //
+ // TODO: this might be a good place to turn
+ // a reference to a generic `struct` type into
+ // a concrete non-generic type so that downstream
+ // codegen doesn't have to deal with generics...
+ //
+ // TODO: In fact, why not just fully replace
+ // all aggregate types here with some structural
+ // types defined in the IR?
+ if (!anyComplex)
+ {
+ return LegalType::simple(type);
+ }
+
+ // If there were any "ordinary" fields along the way,
+ // then we need to collect them into a new `struct` type
+ // that represents these fields.
+ //
+ LegalType ordinaryType;
+ if (anyOrdinary)
+ {
+ // We are going to create a new `struct` type declaration that clones
+ // the fields we care about from the original `struct` type. Note that
+ // these fields may have different types from what they did before,
+ // because the fields themselves might have been legalized.
+ //
+ // Our new declaration will have the same name as the old one, so
+ // downstream code is going to need to be careful not to emit declarations
+ // for both of them. This should be okay, though, because the original
+ // type was illegal (that was the whole point) and so it shouldn't be
+ // allowed in the output anyway.
+ RefPtr<StructDecl> ordinaryStructDecl = new StructDecl();
+ ordinaryStructDecl->loc = typeDeclRef.getDecl()->loc;
+ ordinaryStructDecl->nameAndLoc = typeDeclRef.getDecl()->nameAndLoc;
+
+ addModifier(ordinaryStructDecl, new LegalizedModifier());
+
+ // We will do something a bit unsavory here, by setting the logical
+ // parent of the new `struct` type to be the same as the orignal type
+ // (All of this helps ensure it gets the same mangled name).
+ //
+ ordinaryStructDecl->ParentDecl = typeDeclRef.getDecl()->ParentDecl;
+
+ if (context->mainModuleDecl)
+ {
+ // If the declaration we are lowering belongs to the AST-based
+ // module being lowered (rather than translated to IR), then we
+ // need to add any new declaration we create to that output.
+
+ // If we are *not* outputting an IR module as well, then
+ // everything needs to wind up in a single AST module.
+ if (!context->irModule)
+ {
+ context->outputModuleDecl->Members.Add(ordinaryStructDecl);
+ }
+ else
+ {
+ // Otherwise, check if this declaration belongs to the main
+ // module (which is being lowered via the AST-to-AST pass),
+ // and add it to the output if needed.
+ //
+ // TODO: This won't work correctly if a type from the AST
+ // module is used to specialize a generic in the IR module,
+ // since the declaration would need to precede the specialized
+ // func...
+ auto parentModule = findModuleForDecl(typeDeclRef.getDecl());
+ if (parentModule && (parentModule == context->mainModuleDecl))
+ {
+ context->outputModuleDecl->Members.Add(ordinaryStructDecl);
+ }
+ }
+ }
+
+ // For memory management reasons, we need to keep a reference to
+ // the declaration live, no matter what.
+ context->createdDecls.Add(ordinaryStructDecl);
+
+ UInt elementCounter = 0;
+ for(auto ee : ordinaryElements)
+ {
+ UInt elementIndex = elementCounter++;
+
+ // We will ensure that all the original fields are represented,
+ // although they may have different types (due to legalization).
+ // For fields that have *no* ordinary data, we will give them
+ // a dummy `void` type and rely on downstream passes to not
+ // actually emit declarations for those fields.
+ //
+ // (This helps keeps things simple because both the original
+ // and modified type will have the same number of fields, so
+ // we can continue to look up field layouts by index in the
+ // emit logic)
+ RefPtr<Type> fieldType = ee.type;
+ if(!fieldType)
+ fieldType = context->session->getVoidType();
+
+ // TODO: shallow clone of modifiers, etc.
+
+ RefPtr<StructField> fieldDecl = new StructField();
+ fieldDecl->loc = ee.fieldDeclRef.getDecl()->loc;
+ fieldDecl->nameAndLoc = ee.fieldDeclRef.getDecl()->nameAndLoc;
+ fieldDecl->type.type = fieldType;
+
+ fieldDecl->ParentDecl = ordinaryStructDecl;
+ ordinaryStructDecl->Members.Add(fieldDecl);
+
+ pairElements[elementIndex].ordinaryFieldDeclRef = makeDeclRef(fieldDecl.Ptr());
+
+ addModifier(fieldDecl, new LegalizedModifier());
+ }
+
+ RefPtr<Type> ordinaryStructType = DeclRefType::Create(
+ context->session,
+ makeDeclRef(ordinaryStructDecl.Ptr()));
+
+ ordinaryType = LegalType::simple(ordinaryStructType);
+ }
+
+ LegalType specialType;
+ if (anySpecial)
+ {
+ RefPtr<TuplePseudoType> specialTuple = new TuplePseudoType();
+ specialTuple->elements = specialElements;
+ specialType = LegalType::tuple(specialTuple);
+ }
+
+ RefPtr<PairInfo> pairInfo;
+ if (anyOrdinary && anySpecial)
+ {
+ pairInfo = new PairInfo();
+ pairInfo->elements = pairElements;
+ }
+
+ return LegalType::pair(ordinaryType, specialType, pairInfo);
+ }
+
+};
+
+static RefPtr<Type> createBuiltinGenericType(
+ TypeLegalizationContext* context,
+ DeclRef<Decl> const& typeDeclRef,
+ RefPtr<Type> elementType)
+{
+ // We are going to take the type for the original
+ // decl-ref and construct a new one that uses
+ // our new element type as its parameter.
+ //
+ // TODO: we should have library code to make
+ // manipulations like this way easier.
+
+ RefPtr<GenericSubstitution> oldGenericSubst = getGenericSubstitution(
+ typeDeclRef.substitutions);
+ SLANG_ASSERT(oldGenericSubst);
+
+ RefPtr<GenericSubstitution> newGenericSubst = new GenericSubstitution();
+
+ newGenericSubst->outer = oldGenericSubst->outer;
+ newGenericSubst->genericDecl = oldGenericSubst->genericDecl;
+ newGenericSubst->args = oldGenericSubst->args;
+ newGenericSubst->args[0] = elementType;
+
+ auto newDeclRef = DeclRef<Decl>(
+ typeDeclRef.getDecl(),
+ newGenericSubst);
+
+ auto newType = DeclRefType::Create(
+ context->session,
+ newDeclRef);
+
+ return newType;
+}
+
+// Create a uniform buffer type with a given legalized
+// element type.
+static LegalType createLegalUniformBufferType(
+ TypeLegalizationContext* context,
+ DeclRef<Decl> const& typeDeclRef,
+ LegalType legalElementType)
+{
+ switch (legalElementType.flavor)
+ {
+ case LegalType::Flavor::simple:
+ {
+ // Easy case: we just have a simple element type,
+ // so we want to create a uniform buffer that wraps it.
+ return LegalType::simple(createBuiltinGenericType(
+ context,
+ typeDeclRef,
+ legalElementType.getSimple()));
+ }
+ break;
+
+ case LegalType::Flavor::implicitDeref:
+ {
+ // This is actually an annoying case, because
+ // we are being asked to convert, e.g.,:
+ //
+ // cbuffer Foo { ParameterBlock<Bar> bar; }
+ //
+ // into the equivalent of:
+ //
+ // cbuffer Foo { Bar bar; }
+ //
+ // Which would really require a new `LegalType` that
+ // would reprerent a resource type with a modified
+ // element type.
+ //
+ // I'm going to attempt to hack this for now.
+ return LegalType::implicitDeref(createLegalUniformBufferType(
+ context,
+ typeDeclRef,
+ legalElementType.getImplicitDeref()->valueType));
+ }
+ break;
+
+ case LegalType::Flavor::pair:
+ {
+ // We assume that the "ordinary" part of things
+ // will get wrapped in a constant-buffer type,
+ // and the "special" part needs to be wrapped
+ // with an `implicitDeref`.
+ auto pairType = legalElementType.getPair();
+
+ auto ordinaryType = createLegalUniformBufferType(
+ context,
+ typeDeclRef,
+ pairType->ordinaryType);
+ auto specialType = LegalType::implicitDeref(pairType->specialType);
+
+ return LegalType::pair(ordinaryType, specialType, pairType->pairInfo);
+ }
+
+ case LegalType::Flavor::tuple:
+ {
+ // if we have a tuple type, then it must be representing
+ // the fields that can't be stored in a buffer anyway,
+ // so we just need to wrap each of them in an `implicitDeref`
+
+ auto elementPseudoTupleType = legalElementType.getTuple();
+
+ RefPtr<TuplePseudoType> bufferPseudoTupleType = new TuplePseudoType();
+
+ // Wrap all the pseudo-tuple elements with `implicitDeref`,
+ // since they used to be inside a tuple, but aren't any more.
+ for (auto ee : elementPseudoTupleType->elements)
+ {
+ TuplePseudoType::Element newElement;
+
+ newElement.fieldDeclRef = ee.fieldDeclRef;
+ newElement.type = LegalType::implicitDeref(ee.type);
+
+ bufferPseudoTupleType->elements.Add(newElement);
+ }
+
+ return LegalType::tuple(bufferPseudoTupleType);
+ }
+ break;
+
+ default:
+ SLANG_UNEXPECTED("unknown legal type flavor");
+ UNREACHABLE_RETURN(LegalType());
+ break;
+ }
+}
+
+static LegalType createLegalUniformBufferType(
+ TypeLegalizationContext* context,
+ UniformParameterGroupType* uniformBufferType,
+ LegalType legalElementType)
+{
+ return createLegalUniformBufferType(
+ context,
+ uniformBufferType->declRef,
+ legalElementType);
+}
+
+// Create a pointer type with a given legalized value type.
+static LegalType createLegalPtrType(
+ TypeLegalizationContext* context,
+ DeclRef<Decl> const& typeDeclRef,
+ LegalType legalValueType)
+{
+ switch (legalValueType.flavor)
+ {
+ case LegalType::Flavor::simple:
+ {
+ // Easy case: we just have a simple element type,
+ // so we want to create a uniform buffer that wraps it.
+ return LegalType::simple(createBuiltinGenericType(
+ context,
+ typeDeclRef,
+ legalValueType.getSimple()));
+ }
+ break;
+
+ case LegalType::Flavor::implicitDeref:
+ {
+ // We are being asked to create a pointer type to something
+ // that is implicitly dereferenced, meaning we had:
+ //
+ // Ptr(PtrLink(T))
+ //
+ // and now are being asked to make:
+ //
+ // Ptr(implicitDeref(LegalT))
+ //
+ // So it seems like we can just create:
+ //
+ // implicitDeref(Ptr(LegalT))
+ //
+ // and nobody should really be able to tell the difference, right?
+ return LegalType::implicitDeref(createLegalPtrType(
+ context,
+ typeDeclRef,
+ legalValueType.getImplicitDeref()->valueType));
+ }
+ break;
+
+ case LegalType::Flavor::pair:
+ {
+ // We just need to pointer-ify both sides of the pair.
+ auto pairType = legalValueType.getPair();
+
+ auto ordinaryType = createLegalPtrType(
+ context,
+ typeDeclRef,
+ pairType->ordinaryType);
+ auto specialType = createLegalPtrType(
+ context,
+ typeDeclRef,
+ pairType->specialType);
+
+ return LegalType::pair(ordinaryType, specialType, pairType->pairInfo);
+ }
+
+ case LegalType::Flavor::tuple:
+ {
+ // Wrap each of the tuple elements up as a pointer.
+ auto valuePseudoTupleType = legalValueType.getTuple();
+
+ RefPtr<TuplePseudoType> ptrPseudoTupleType = new TuplePseudoType();
+
+ // Wrap all the pseudo-tuple elements with `implicitDeref`,
+ // since they used to be inside a tuple, but aren't any more.
+ for (auto ee : valuePseudoTupleType->elements)
+ {
+ TuplePseudoType::Element newElement;
+
+ newElement.fieldDeclRef = ee.fieldDeclRef;
+ newElement.type = createLegalPtrType(
+ context,
+ typeDeclRef,
+ ee.type);
+
+ ptrPseudoTupleType->elements.Add(newElement);
+ }
+
+ return LegalType::tuple(ptrPseudoTupleType);
+ }
+ break;
+
+ default:
+ SLANG_UNEXPECTED("unknown legal type flavor");
+ UNREACHABLE_RETURN(LegalType());
+ break;
+ }
+}
+
+struct LegalTypeWrapper
+{
+ virtual LegalType wrap(TypeLegalizationContext* context, Type* type) = 0;
+};
+
+struct ArrayLegalTypeWrapper : LegalTypeWrapper
+{
+ ArrayExpressionType* arrayType;
+
+ LegalType wrap(TypeLegalizationContext* context, Type* type)
+ {
+ return LegalType::simple(context->session->getArrayType(
+ type,
+ arrayType->ArrayLength));
+ }
+};
+
+struct BuiltinGenericLegalTypeWrapper : LegalTypeWrapper
+{
+ DeclRef<Decl> declRef;
+
+ LegalType wrap(TypeLegalizationContext* context, Type* type)
+ {
+ return LegalType::simple(createBuiltinGenericType(
+ context,
+ declRef,
+ type));
+ }
+};
+
+
+struct ImplicitDerefLegalTypeWrapper : LegalTypeWrapper
+{
+ LegalType wrap(TypeLegalizationContext*, Type* type)
+ {
+ return LegalType::implicitDeref(LegalType::simple(type));
+ }
+};
+
+static LegalType wrapLegalType(
+ TypeLegalizationContext* context,
+ LegalType legalType,
+ LegalTypeWrapper* ordinaryWrapper,
+ LegalTypeWrapper* specialWrapper)
+{
+ switch (legalType.flavor)
+ {
+ case LegalType::Flavor::simple:
+ {
+ return ordinaryWrapper->wrap(context, legalType.getSimple());
+ }
+ break;
+
+ case LegalType::Flavor::implicitDeref:
+ {
+ return LegalType::implicitDeref(wrapLegalType(
+ context,
+ legalType,
+ ordinaryWrapper,
+ specialWrapper));
+ }
+ break;
+
+ case LegalType::Flavor::pair:
+ {
+ // We just need to pointer-ify both sides of the pair.
+ auto pairType = legalType.getPair();
+
+ auto ordinaryType = wrapLegalType(
+ context,
+ pairType->ordinaryType,
+ ordinaryWrapper,
+ ordinaryWrapper);
+ auto specialType = wrapLegalType(
+ context,
+ pairType->specialType,
+ specialWrapper,
+ specialWrapper);
+
+ return LegalType::pair(ordinaryType, specialType, pairType->pairInfo);
+ }
+
+ case LegalType::Flavor::tuple:
+ {
+ // Wrap each of the tuple elements up as a pointer.
+ auto tupleType = legalType.getTuple();
+
+ RefPtr<TuplePseudoType> resultTupleType = new TuplePseudoType();
+
+ // Wrap all the pseudo-tuple elements with `implicitDeref`,
+ // since they used to be inside a tuple, but aren't any more.
+ for (auto ee : tupleType->elements)
+ {
+ TuplePseudoType::Element element;
+
+ element.fieldDeclRef = ee.fieldDeclRef;
+ element.type = wrapLegalType(
+ context,
+ ee.type,
+ ordinaryWrapper,
+ specialWrapper);
+
+ resultTupleType->elements.Add(element);
+ }
+
+ return LegalType::tuple(resultTupleType);
+ }
+ break;
+
+ default:
+ SLANG_UNEXPECTED("unknown legal type flavor");
+ UNREACHABLE_RETURN(LegalType());
+ break;
+ }
+}
+
+
+// Legalize a type, including any nested types
+// that it transitively contains.
+LegalType legalizeType(
+ TypeLegalizationContext* context,
+ Type* type)
+{
+ if (auto parameterBlockType = type->As<ParameterBlockType>())
+ {
+ // We basically legalize the `ParameterBlock<T>` type
+ // over to `T`. In order to represent this preoperly,
+ // we need to be careful to wrap it up in a way that
+ // tells us to eliminate downstream deferences...
+
+ auto legalElementType = legalizeType(context,
+ parameterBlockType->getElementType());
+ return LegalType::implicitDeref(legalElementType);
+ }
+ else if (auto uniformBufferType = type->As<UniformParameterGroupType>())
+ {
+ // We have a `ConstantBuffer<T>` or `TextureBuffer<T>` or
+ // other pointer-like type that represents uniform parameters.
+ // We need to pull any resource-type fields out of it, but
+ // leave the non-resource fields where they are.
+
+ // Legalize the element type to see what we are working with.
+ auto legalElementType = legalizeType(context,
+ uniformBufferType->getElementType());
+
+ switch (legalElementType.flavor)
+ {
+ case LegalType::Flavor::simple:
+ return LegalType::simple(type);
+
+ default:
+ return createLegalUniformBufferType(
+ context,
+ uniformBufferType,
+ legalElementType);
+ }
+
+ }
+ else if (isResourceType(type))
+ {
+ // We assume that any resource types not handled above
+ // are legal as-is.
+ return LegalType::simple(type);
+ }
+ else if (type->As<BasicExpressionType>())
+ {
+ return LegalType::simple(type);
+ }
+ else if (type->As<VectorExpressionType>())
+ {
+ return LegalType::simple(type);
+ }
+ else if (type->As<MatrixExpressionType>())
+ {
+ return LegalType::simple(type);
+ }
+ else if (auto ptrType = type->As<PtrTypeBase>())
+ {
+ auto legalValueType = legalizeType(context, ptrType->getValueType());
+ return createLegalPtrType(context, ptrType->declRef, legalValueType);
+ }
+ else if (auto declRefType = type->As<DeclRefType>())
+ {
+ auto declRef = declRefType->declRef;
+
+ LegalType legalType;
+ if(context->mapDeclRefToLegalType.TryGetValue(declRef, legalType))
+ return legalType;
+
+
+ if (auto aggTypeDeclRef = declRef.As<AggTypeDecl>())
+ {
+ // Look at the (non-static) fields, and
+ // see if anything needs to be cleaned up.
+ // The things that need to be "cleaned up" for
+ // our purposes are:
+ //
+ // - Fields of resource type, or any other future
+ // type we run into that isn't allowed in
+ // aggregates for at least some targets
+ //
+ // - Fields with types that themselves had to
+ // get legalized.
+ //
+ // If we don't run into any of these, we
+ // can just use the type as-is. Hooray!
+ //
+ // Otherwise, we are effectively going to split
+ // the type apart and create a `TuplePseudoType`.
+ // Every field of the original type will be
+ // represented as an element of this pseudo-type.
+ // Each element will record its `LegalType`,
+ // and the original field that it was created from.
+ // An element will also track whether it contains
+ // any "ordinary" data, and if so, it will remember
+ // an element index in a real (AST-level, non-pseudo)
+ // `TupleType` that is used to bundle together
+ // such fields.
+ //
+ // Storing all the simple fields together like this
+ // obviously adds complexity to the legalization
+ // pass, but it has important benefits:
+ //
+ // - It avoids creating functions with a very large
+ // number of parameters (when passing a structure
+ // with many fields), which might confuse downstream
+ // compilers.
+ //
+ // - It avoids applying AOS->SOA conversion to fields
+ // that don't actually need it, which is basically
+ // required if we want type layout to work.
+ //
+ // - It ensures that we can actually construct a
+ // constant-buffer type that wraps a legalized
+ // aggregate type; the ordinary fields will get
+ // placed inside a new constant-buffer type,
+ // while the special ones will get left outside.
+ //
+
+ TupleTypeBuilder builder;
+ builder.context = context;
+ builder.type = type;
+ builder.typeDeclRef = aggTypeDeclRef;
+
+
+ for (auto ff : getMembersOfType<StructField>(aggTypeDeclRef))
+ {
+ builder.addField(ff);
+ }
+
+ legalType = builder.getResult();
+ context->mapDeclRefToLegalType.Add(declRef, legalType);
+ return legalType;
+ }
+
+ // TODO: for other declaration-reference types, we really
+ // need to legalize the types used in substitutions, and
+ // signal an error if any of them turn out to be non-simple.
+ //
+ // The limited cases of types that can handle having non-simple
+ // types as generic arguments all need to be special-cased here.
+ // (For example, we can't handle `Texture2D<SomeStructWithTexturesInIt>`.
+ //
+ }
+ else if(auto arrayType = type->As<ArrayExpressionType>())
+ {
+ auto legalElementType = legalizeType(
+ context,
+ arrayType->baseType);
+
+ switch (legalElementType.flavor)
+ {
+ case LegalType::Flavor::simple:
+ // Element type didn't need to be legalized, so
+ // we can just use this type as-is.
+ return LegalType::simple(type);
+
+ default:
+ {
+ ArrayLegalTypeWrapper wrapper;
+ wrapper.arrayType = arrayType;
+
+ return wrapLegalType(
+ context,
+ legalElementType,
+ &wrapper,
+ &wrapper);
+ }
+ break;
+ }
+
+ }
+
+ return LegalType::simple(type);
+}
+
+//
+
+RefPtr<TypeLayout> getDerefTypeLayout(
+ TypeLayout* typeLayout)
+{
+ if (!typeLayout)
+ return nullptr;
+
+ if (auto parameterGroupTypeLayout = dynamic_cast<ParameterGroupTypeLayout*>(typeLayout))
+ {
+ return parameterGroupTypeLayout->elementTypeLayout;
+ }
+
+ return typeLayout;
+}
+
+RefPtr<VarLayout> getFieldLayout(
+ TypeLayout* typeLayout,
+ DeclRef<VarDeclBase> fieldDeclRef)
+{
+ if (!typeLayout)
+ return nullptr;
+
+ while(auto arrayTypeLayout = dynamic_cast<ArrayTypeLayout*>(typeLayout))
+ {
+ typeLayout = arrayTypeLayout->elementTypeLayout;
+ }
+
+ if (auto structTypeLayout = dynamic_cast<StructTypeLayout*>(typeLayout))
+ {
+ RefPtr<VarLayout> fieldLayout;
+ if (structTypeLayout->mapVarToLayout.TryGetValue(fieldDeclRef.getDecl(), fieldLayout))
+ return fieldLayout;
+ }
+
+ return nullptr;
+}
+
+RefPtr<VarLayout> createVarLayout(
+ LegalVarChain* varChain,
+ TypeLayout* typeLayout)
+{
+ if (!typeLayout)
+ return nullptr;
+
+ // We need to construct a layout for the new variable
+ // that reflects both the type we have given it, as
+ // well as all the offset information that has accumulated
+ // along the chain of parent variables.
+
+ // TODO: this logic needs to propagate through semantics...
+
+ RefPtr<VarLayout> varLayout = new VarLayout();
+ varLayout->typeLayout = typeLayout;
+
+ for (auto rr : typeLayout->resourceInfos)
+ {
+ auto resInfo = varLayout->findOrAddResourceInfo(rr.kind);
+
+ for (auto vv = varChain; vv; vv = vv->next)
+ {
+ if (auto parentResInfo = vv->varLayout->FindResourceInfo(rr.kind))
+ {
+ resInfo->index += parentResInfo->index;
+ resInfo->space += parentResInfo->space;
+ }
+ }
+ }
+
+ // Some of the parent variables might actually contain offsets
+ // to the `space` or `set` of the field, and we need to apply
+ // those to all the nested resource infos.
+ for (auto vv = varChain; vv; vv = vv->next)
+ {
+ auto parentSpaceInfo = vv->varLayout->FindResourceInfo(LayoutResourceKind::RegisterSpace);
+ if (!parentSpaceInfo)
+ continue;
+
+ for (auto& rr : varLayout->resourceInfos)
+ {
+ if (rr.kind == LayoutResourceKind::RegisterSpace)
+ {
+ rr.index += parentSpaceInfo->index;
+ }
+ else
+ {
+ rr.space += parentSpaceInfo->index;
+ }
+ }
+ }
+
+ return varLayout;
+}
+
+//
+
+// TODO(tfoley): The code captured here is the logic that used to be
+// applied to decide whether or not to desugar aggregate types that
+// contain resources. Right now the implementation will *always* legalize
+// away such types (since the IR always does this), while the AST-to-AST
+// pass would only do it if required (according to the tests below).
+//
+// For right now this is an academic distinction, since the only project
+// using Slang right now enables this tansformation unconditionally, but
+// we probably need to re-parent this code back into the `TypeLegalizationContext`
+// somewhere.
+#if 0
+
+bool shouldDesugarTupleTypes = false;
+if (getTarget() == CodeGenTarget::GLSL)
+{
+ // Always desugar this stuff for GLSL, since it doesn't
+ // support nesting of resources in structs.
+ //
+ // TODO: Need a way to make this more fine-grained to
+ // handle cases where a nested member might be allowed
+ // due to, e.g., bindless textures.
+ shouldDesugarTupleTypes = true;
+}
+else if( shared->compileRequest->compileFlags & SLANG_COMPILE_FLAG_SPLIT_MIXED_TYPES )
+{
+ // If the user is directly asking us to do this transformation,
+ // then obviously we need to do it.
+ //
+ // TODO: The way this is defined here means it will even apply to user
+ // HLSL code (not just code written in Slang). We may want to
+ // reconsider that choice, and only split things that originated in Slang.
+ //
+ shouldDesugarTupleTypes = true;
+}
+
+#endif
+
+}