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.cpp1486
1 files changed, 0 insertions, 1486 deletions
diff --git a/source/slang/legalize-types.cpp b/source/slang/legalize-types.cpp
deleted file mode 100644
index 64dafb938..000000000
--- a/source/slang/legalize-types.cpp
+++ /dev/null
@@ -1,1486 +0,0 @@
-// legalize-types.cpp
-#include "legalize-types.h"
-
-#include "ir-insts.h"
-#include "mangle.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)
-{
- SLANG_ASSERT(tupleType->elements.getCount());
-
- 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);
-}
-
-LegalType LegalType::makeWrappedBuffer(
- IRType* simpleType,
- LegalElementWrapping const& elementInfo)
-{
- RefPtr<WrappedBufferPseudoType> obj = new WrappedBufferPseudoType();
- obj->simpleType = simpleType;
- obj->elementInfo = elementInfo;
-
- LegalType result;
- result.flavor = Flavor::wrappedBuffer;
- result.obj = obj;
- return result;
-}
-
-//
-
-LegalElementWrapping LegalElementWrapping::makeVoid()
-{
- LegalElementWrapping result;
- result.flavor = Flavor::none;
- return result;
-}
-
-LegalElementWrapping LegalElementWrapping::makeSimple(IRStructKey* key, IRType* type)
-{
- RefPtr<SimpleLegalElementWrappingObj> obj = new SimpleLegalElementWrappingObj();
- obj->key = key;
- obj->type = type;
-
- LegalElementWrapping result;
- result.flavor = Flavor::simple;
- result.obj = obj;
- return result;
-}
-
-RefPtr<SimpleLegalElementWrappingObj> LegalElementWrapping::getSimple() const
-{
- SLANG_ASSERT(flavor == Flavor::simple);
- return obj.as<SimpleLegalElementWrappingObj>();
-}
-
-LegalElementWrapping LegalElementWrapping::makeImplicitDeref(LegalElementWrapping const& field)
-{
- RefPtr<ImplicitDerefLegalElementWrappingObj> obj = new ImplicitDerefLegalElementWrappingObj();
- obj->field = field;
-
- LegalElementWrapping result;
- result.flavor = Flavor::implicitDeref;
- result.obj = obj;
- return result;
-}
-
-RefPtr<ImplicitDerefLegalElementWrappingObj> LegalElementWrapping::getImplicitDeref() const
-{
- SLANG_ASSERT(flavor == Flavor::implicitDeref);
- return obj.as<ImplicitDerefLegalElementWrappingObj>();
-}
-
-LegalElementWrapping LegalElementWrapping::makePair(
- LegalElementWrapping const& ordinary,
- LegalElementWrapping const& special,
- PairInfo* pairInfo)
-{
- RefPtr<PairLegalElementWrappingObj> obj = new PairLegalElementWrappingObj();
- obj->ordinary = ordinary;
- obj->special = special;
- obj->pairInfo = pairInfo;
-
- LegalElementWrapping result;
- result.flavor = Flavor::pair;
- result.obj = obj;
- return result;
-}
-
-RefPtr<PairLegalElementWrappingObj> LegalElementWrapping::getPair() const
-{
- SLANG_ASSERT(flavor == Flavor::pair);
- return obj.as<PairLegalElementWrappingObj>();
-}
-
-LegalElementWrapping LegalElementWrapping::makeTuple(TupleLegalElementWrappingObj* obj)
-{
- LegalElementWrapping result;
- result.flavor = Flavor::tuple;
- result.obj = obj;
- return result;
-}
-
-RefPtr<TupleLegalElementWrappingObj> LegalElementWrapping::getTuple() const
-{
- SLANG_ASSERT(flavor == Flavor::tuple);
- return obj.as<TupleLegalElementWrappingObj>();
-}
-
-//
-
-bool isResourceType(IRType* type)
-{
- while (auto arrayType = as<IRArrayTypeBase>(type))
- {
- type = arrayType->getElementType();
- }
-
- if (auto resourceTypeBase = as<IRResourceTypeBase>(type))
- {
- return true;
- }
- else if (auto builtinGenericType = as<IRBuiltinGenericType>(type))
- {
- return true;
- }
- else if (auto pointerLikeType = as<IRPointerLikeType>(type))
- {
- return true;
- }
- else if (auto samplerType = as<IRSamplerStateTypeBase>(type))
- {
- return true;
- }
- else if(auto untypedBufferType = as<IRUntypedBufferResourceType>(type))
- {
- 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 = as<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;
- IRType* type;
- IRStructType* originalStructType;
-
- struct OrdinaryElement
- {
- IRStructKey* fieldKey = nullptr;
- IRType* type = nullptr;
- };
-
-
- 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(
- IRStructKey* fieldKey,
- LegalType legalFieldType,
- LegalType legalLeafType,
- bool isSpecial)
- {
- 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 (!isSpecial)
- {
- ordinaryType = legalLeafType;
- }
- else
- {
- specialType = legalFieldType;
- }
- }
- break;
-
- case LegalType::Flavor::none:
- anyComplex = true;
- 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(
- fieldKey,
- legalFieldType,
- legalLeafType.getImplicitDeref()->valueType,
- isSpecial);
- return;
- }
- break;
-
- case LegalType::Flavor::pair:
- {
- // The field's type had both special and non-special parts
- auto pairType = legalLeafType.getPair();
-
- // If things originally started as a resource type, then
- // we want to externalize all the fields that arose, even
- // if there is (nominally) ordinary data.
- //
- // This is because the "ordinary" side of the legalization
- // of `ConstantBuffer<Foo>` will still be a resource type.
- if(isSpecial)
- {
- specialType = legalFieldType;
- }
- else
- {
- 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.key = fieldKey;
- 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.fieldKey = fieldKey;
- 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.key = fieldKey;
- 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(
- IRStructField* field)
- {
- auto fieldType = field->getFieldType();
-
- bool isSpecialField = context->isSpecialType(fieldType);
- auto legalFieldType = legalizeType(context, fieldType);
-
- addField(
- field->getKey(),
- legalFieldType,
- legalFieldType,
- isSpecialField);
- }
-
- LegalType getResult()
- {
- // If this is an empty struct, return a none type
- // This helps get rid of emtpy structs that often trips up the
- // downstream compiler
- if (!anyOrdinary && !anySpecial && !anyComplex)
- return LegalType();
-
- // If we didn't see anything "special"
- // then we can use the type as-is.
- // we can conceivably just use the type as-is
- //
- 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 an new IR `struct` type that contains
- // the "ordinary" fields from the original type. Note that these
- // fields may have different types from what they did before,
- // because the fields themselves might have been legalized.
- //
- // The new type will have the same mangled 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
- // referenced in the output anyway.
- //
- IRBuilder* builder = context->getBuilder();
- IRStructType* ordinaryStructType = builder->createStructType();
- ordinaryStructType->sourceLoc = originalStructType->sourceLoc;
-
- if(auto nameHintDecoration = originalStructType->findDecoration<IRNameHintDecoration>())
- {
- builder->addNameHintDecoration(ordinaryStructType, nameHintDecoration->getNameOperand());
- }
-
- // The new struct type will appear right after the original in the IR,
- // so that we can be sure any instruction that could reference the
- // original can also reference the new one.
- ordinaryStructType->insertAfter(originalStructType);
-
- // Mark the original type for removal once all the other legalization
- // activity is completed. This is necessary because both the original
- // and replacement type have the same mangled name, so they would
- // collide.
- //
- // (Also, the original type wasn't legal - that was the whole point...)
- context->replacedInstructions.add(originalStructType);
-
- for(auto ee : ordinaryElements)
- {
- // 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)
- //
- // TODO: we should scrap that, and layout lookup should just
- // be based on mangled field names in all cases.
- //
- IRType* fieldType = ee.type;
- if(!fieldType)
- fieldType = context->getBuilder()->getVoidType();
-
- // TODO: shallow clone of modifiers, etc.
-
- builder->createStructField(
- ordinaryStructType,
- ee.fieldKey,
- fieldType);
- }
-
- ordinaryType = LegalType::simple((IRType*) 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 IRType* createBuiltinGenericType(
- TypeLegalizationContext* context,
- IROp op,
- IRType* elementType)
-{
- IRInst* operands[] = { elementType };
- return context->getBuilder()->getType(
- op,
- 1,
- operands);
-}
-
-// Create a uniform buffer type with a given legalized
-// element type.
-static LegalType createLegalUniformBufferType(
- TypeLegalizationContext* context,
- IROp op,
- LegalType legalElementType)
-{
- // We will handle some of the easy/non-interesting
- // cases here in the main routine, but for all
- // the non-trivial cases we will dispatch to logic
- // on the `context` (which may differ depending
- // on what we are using legalization to accomplish).
- //
- switch (legalElementType.flavor)
- {
- default:
- return context->createLegalUniformBufferType(
- op,
- legalElementType);
-
- case LegalType::Flavor::none:
- return LegalType();
-
- case LegalType::Flavor::simple:
- {
- // Easy case: we just have a simple element type,
- // so we want to create a uniform buffer that wraps it.
- //
- // TODO: This isn't *quite* right, since it won't handle something
- // like a `ParameterBlock<Texture2D>`, but that seems like
- // an unlikely case in practice.
- //
- return LegalType::simple(createBuiltinGenericType(
- context,
- op,
- 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,
- op,
- legalElementType.getImplicitDeref()->valueType));
- }
- break;
- }
-}
-
-// Create a uniform buffer type with a given legalized element type,
-// under the assumption that we are doing resource-based type legalization.
-//
-LegalType createLegalUniformBufferTypeForResources(
- TypeLegalizationContext* context,
- IROp op,
- LegalType legalElementType)
-{
- switch (legalElementType.flavor)
- {
- case LegalType::Flavor::simple:
- {
- // Seeing a simple type here means that it must be a
- // "special" type (a resource type or array thereof)
- // because otherwise the catch-all behavior in
- // `createLegalUniformBufferType()` would have handled it.
- //
- // This case is the same as what we do for tuple elements below.
- //
- return LegalType::implicitDeref(legalElementType);
- }
-
- case LegalType::Flavor::pair:
- {
- auto pairType = legalElementType.getPair();
-
- // The pair has both an "ordinary" and a "special"
- // side, where the ordinary side is just plain data
- // that we can put in a constant buffer type without
- // any problems. The special side will (recursively)
- // contain any resource-type fields that were nested
- // in the constant buffer, and we'll need to
- // treat those as resources that stand alongside
- // the original constant buffer.
- //
- // We can start with the ordinary side, which we
- // just want to wrap up in an ordinary uniform
- // buffer with the appropriate `op`, so that case
- // is easy:
- //
- auto ordinaryType = createLegalUniformBufferType(
- context,
- op,
- pairType->ordinaryType);
-
- // For the special side, we really just want to turn
- // a special field of type `R` into a value of type
- // `R`, and the main detail we have to be aware of
- // is that any use sites for the original buffer/block
- // will include a dereferencing step to get from
- // the block to this field, so we need to add
- // something to the type structure to account for
- // that step.
- //
- // We handle that issue by wrapping the special
- // part of the type in an `implicitDeref` wrapper,
- // which indicates that we logically have `SomePtr<R>`
- // but we actually just have `R`, and any attempt to
- // load from or otherwise indirect through that pointer
- // will turn into a plain old reference to the `R` value.
- //
- auto specialType = LegalType::implicitDeref(pairType->specialType);
-
- // Once we've wrapped up both the ordinary and special
- // sides suitably, we tie them back together in a pair
- // and make that be the legalized type of the result.
- //
- return LegalType::pair(ordinaryType, specialType, pairType->pairInfo);
- }
-
- case LegalType::Flavor::tuple:
- {
- // A tuple type always represents purely "special" data,
- // which in this case means resources.
- //
- // As in the `pair` case, the main thing we have to
- // take into account is that each of the entries in the
- // tuple itself (e.g., a value of type `R`) and the code
- // that uses the legalized buffer type will expect a
- // `ConstantBuffer<R>` or at least `SomePtrType<R>`.
- //
- // We will construct a new tuple type that wraps each
- // of the element types in an `implicitDeref` to
- // account for the different in levels of indirection.
- //
- // TODO: This seems odd, because we *should* be able to
- // just wrap the whole thing in an `implicitDeref` and
- // have done. We should investigate why this roundabout
- // way of doing things was ever necessary.
-
- auto elementPseudoTupleType = legalElementType.getTuple();
- RefPtr<TuplePseudoType> bufferPseudoTupleType = new TuplePseudoType();
-
- for (auto ee : elementPseudoTupleType->elements)
- {
- TuplePseudoType::Element newElement;
-
- newElement.key = ee.key;
- newElement.type = LegalType::implicitDeref(ee.type);
-
- bufferPseudoTupleType->elements.add(newElement);
- }
-
- return LegalType::tuple(bufferPseudoTupleType);
- }
- break;
-
- default:
- SLANG_UNEXPECTED("unhandled legal type flavor");
- UNREACHABLE_RETURN(LegalType());
- break;
- }
-}
-
-// Legalizing a uniform buffer/block type for existentials is
-// more interesting, because we don't actually want to push
-// the "special" fields out of the buffer entirely (as we
-// do for resources), and instead we just want to place
-// them in the buffer *after* all the ordinary data.
-//
-// In order to accomplish this we need a way to emit a
-// constant buffer with a new element type, and then
-// "wrap" that constant buffer so that it looks like
-// something that matches the legalization of the original
-// element type.
-//
-// As a concrete example, suppose we have:
-//
-// struct Params { ExistentialBox<Foo> f; int x; ExistentialBox<Bar> b; };
-// ConstantBuffer<Params> gParams;
-//
-// The legalized form of `Params` will be something like:
-//
-// Pair(
-// /* ordinary: */ struct Params_Ordinary { int x; },
-// /* special: */ Tuple(
-// f -> ImplicitDeref(Foo),
-// b -> ImplicitDeref(Bar)))
-//
-// We need to be able to splat that all out into a single
-// structure declaration like:
-//
-// struct Params_Reordered
-// {
-// Params_Ordinary ordinary;
-// Foo f;
-// Bar b;
-// }
-//
-// That allows us to declare:
-//
-// ConstantBuffer<Params_Reordered> gParams;
-//
-// That gets the in-memory layout of things correct for the
-// way we are defining existential value slots to work.
-// The challenge is that elsewehere in the code there are
-// operations like `gParams.x` need to now refer to
-// `gParams.ordinary.x`. Furthermore, even for something like
-// `f` that seems fine in the example above, we have lost
-// a level of indirection, so that where we had `load(gParams.f)`
-// we now want just `gParams.f`.
-//
-// The solution is to take `gParams` as soon as it is declared
-// and wrap it up as a new value:
-//
-// pair(
-// /* ordinary: */ gParams.ordinary,
-// /* special: */ tuple(
-// f -> implicitDeref(gParams.f),
-// b -> implicitDeref(gParams.b)))
-//
-//
-// Let's begin by just defining a function that can take
-// a `LegalType` and turn it into zero or more field
-// declarations, and return enough tracking information
-// for us to be able to reconstruct a value like the above.
-//
-LegalElementWrapping declareStructFields(
- TypeLegalizationContext* context,
- IRStructType* structType,
- LegalType fieldType)
-{
- // TODO: We should eventually thread through some kind
- // of "name hint" that can be used to give the generated
- // fields more useful names.
-
- switch(fieldType.flavor)
- {
- case LegalType::Flavor::none:
- return LegalElementWrapping::makeVoid();
-
- case LegalType::Flavor::simple:
- {
- auto simpleFieldType = fieldType.getSimple();
- auto builder = context->getBuilder();
- auto fieldKey = builder->createStructKey();
- builder->createStructField(structType, fieldKey, simpleFieldType);
- return LegalElementWrapping::makeSimple(fieldKey, simpleFieldType);
- }
-
- case LegalType::Flavor::implicitDeref:
- {
- auto subField = declareStructFields(context, structType, fieldType.getImplicitDeref()->valueType);
- return LegalElementWrapping::makeImplicitDeref(subField);
- }
-
- case LegalType::Flavor::pair:
- {
- auto pairType = fieldType.getPair();
- auto ordinaryField = declareStructFields(context, structType, pairType->ordinaryType);
- auto specialField = declareStructFields(context, structType, pairType->specialType);
- return LegalElementWrapping::makePair(
- ordinaryField,
- specialField,
- pairType->pairInfo);
- }
-
- case LegalType::Flavor::tuple:
- {
- auto tupleType = fieldType.getTuple();
-
- RefPtr<TupleLegalElementWrappingObj> obj = new TupleLegalElementWrappingObj();
- for( auto ee : tupleType->elements )
- {
- TupleLegalElementWrappingObj::Element element;
- element.key = ee.key;
- element.field = declareStructFields(context, structType, ee.type);
- obj->elements.add(element);
- }
- return LegalElementWrapping::makeTuple(obj);
- }
-
- default:
- SLANG_UNEXPECTED("unhandled legal type flavor");
- UNREACHABLE_RETURN(LegalElementWrapping::makeVoid());
- break;
- }
-}
-
-LegalType createLegalUniformBufferTypeForExistentials(
- TypeLegalizationContext* context,
- IROp op,
- LegalType legalElementType)
-{
- auto builder = context->getBuilder();
-
- // In order to wrap up all the data in `legalElementType`,
- // will create a fresh `struct` type and then declare
- // fields in it that are sufficient to hold that data
- // in `legalElementType`.
- //
- auto structType = builder->createStructType();
- auto elementWrapping = declareStructFields(
- context, structType, legalElementType);
-
- // Because the `structType` is an ordinary IR type
- // (not a `LegalType`) we can go ahead and create an
- // IR uniform buffer type that wraps it.
- //
- auto bufferType = createBuiltinGenericType(
- context,
- op,
- structType);
-
- // The `elementWrapping` computed when we declared all
- // the `struct` fields tells us how to get from the
- // actual fields declared in the structure type to a
- // `LegalVal` with the right shape for what users of
- // the buffer will expect. We record both the underlying
- // IR buffer type and that wrapping information into
- // the resulting `LegalType` so that we can use it
- // when declaring variables of this type.
- //
- return LegalType::makeWrappedBuffer(bufferType, elementWrapping);
-}
-
-static LegalType createLegalUniformBufferType(
- TypeLegalizationContext* context,
- IRUniformParameterGroupType* uniformBufferType,
- LegalType legalElementType)
-{
- return createLegalUniformBufferType(
- context,
- uniformBufferType->op,
- legalElementType);
-}
-
-// Create a pointer type with a given legalized value type.
-static LegalType createLegalPtrType(
- TypeLegalizationContext* context,
- IROp op,
- LegalType legalValueType)
-{
- switch (legalValueType.flavor)
- {
- case LegalType::Flavor::none:
- return LegalType();
-
- 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,
- op,
- 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(PtrLike(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?
- //
- // TODO: invetigate whether there are situations where this
- // will matter.
- return LegalType::implicitDeref(createLegalPtrType(
- context,
- op,
- 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,
- op,
- pairType->ordinaryType);
- auto specialType = createLegalPtrType(
- context,
- op,
- 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.key = ee.key;
- newElement.type = createLegalPtrType(
- context,
- op,
- 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, IRType* type) = 0;
-};
-
-struct ArrayLegalTypeWrapper : LegalTypeWrapper
-{
- IRArrayTypeBase* arrayType;
-
- LegalType wrap(TypeLegalizationContext* context, IRType* type)
- {
- return LegalType::simple(context->getBuilder()->getArrayTypeBase(
- arrayType->op,
- type,
- arrayType->getElementCount()));
- }
-};
-
-struct BuiltinGenericLegalTypeWrapper : LegalTypeWrapper
-{
- IROp op;
-
- LegalType wrap(TypeLegalizationContext* context, IRType* type)
- {
- return LegalType::simple(createBuiltinGenericType(
- context,
- op,
- type));
- }
-};
-
-
-struct ImplicitDerefLegalTypeWrapper : LegalTypeWrapper
-{
- LegalType wrap(TypeLegalizationContext*, IRType* type)
- {
- return LegalType::implicitDeref(LegalType::simple(type));
- }
-};
-
-static LegalType wrapLegalType(
- TypeLegalizationContext* context,
- LegalType legalType,
- LegalTypeWrapper* ordinaryWrapper,
- LegalTypeWrapper* specialWrapper)
-{
- switch (legalType.flavor)
- {
- case LegalType::Flavor::none:
- return LegalType();
-
- 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.key = ee.key;
- 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 legalizeTypeImpl(
- TypeLegalizationContext* context,
- IRType* type)
-{
- if(!type)
- return LegalType::simple(nullptr);
-
- context->builder->setInsertBefore(type);
-
- if (auto uniformBufferType = as<IRUniformParameterGroupType>(type))
- {
- // We have one of:
- //
- // ConstantBuffer<T>
- // TextureBuffer<T>
- // ParameterBlock<T>
- //
- // or some other pointer-like type that represents uniform
- // parameters. We need to pull any resource-type fields out
- // of it, but leave non-resource fields where they are.
- //
- // As a special case, if the type contains *no* uniform data,
- // we'll want to completely eliminate the uniform/ordinary
- // part.
-
- auto originalElementType = uniformBufferType->getElementType();
-
- // Legalize the element type to see what we are working with.
- auto legalElementType = legalizeType(context,
- originalElementType);
-
- // As a bit of a corner case, if the user requested something
- // like `ConstantBuffer<Texture2D>` the element type would
- // legalize to a "simple" type, and that would be interpreted
- // as an *ordinary* type, but we really need to notice the
- // case when the element type is simple, but *special*.
- //
- if( context->isSpecialType(originalElementType) )
- {
- // Anything that has a special element type needs to
- // be handled by the pass-specific logic in the context.
- //
- return context->createLegalUniformBufferType(
- uniformBufferType->op,
- legalElementType);
- }
-
- // Note that even when legalElementType.flavor == Simple
- // we still need to create a new uniform buffer type
- // from `legalElementType` instead of `type`
- // because the `legalElementType` may still differ from `type`
- // if, e.g., `type` contains empty structs.
- 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 (as<IRBasicType>(type))
- {
- return LegalType::simple(type);
- }
- else if (as<IRVectorType>(type))
- {
- return LegalType::simple(type);
- }
- else if (as<IRMatrixType>(type))
- {
- return LegalType::simple(type);
- }
- else if( auto existentialPtrType = as<IRExistentialBoxType>(type))
- {
- // We want to transform an `ExistentialBox<T>` into just
- // a `T`, with an `iplicitDeref` to make sure that any
- // pointer-related operations on the box Just Work.
- //
- // Note: the logic here doesn't have to deal with moving
- // existential-type fields to the end of their outer
- // type(s) because that is mostly dealt with in the
- // case for struct types below.
- //
- auto legalValueType = legalizeType(context, existentialPtrType->getValueType());
- return LegalType::implicitDeref(legalValueType);
- }
- else if (auto ptrType = as<IRPtrTypeBase>(type))
- {
- auto legalValueType = legalizeType(context, ptrType->getValueType());
- return createLegalPtrType(context, ptrType->op, legalValueType);
- }
- else if(auto structType = as<IRStructType>(type))
- {
- // 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.
- //
-
- // TODO: there is a risk here that we might recursively
- // invole `legalizeType` on the type that we are
- // currently trying to legalize. We need to detect that
- // situation somehow, by inserting a sentinel value
- // into `mapTypeToLegalType` during the per-field
- // legalization process, and then if we ever see that
- // sentinel in a call to `legalizeType`, we need
- // to construct some kind of proxy type to help resolve
- // the problem.
-
- TupleTypeBuilder builder;
- builder.context = context;
- builder.type = type;
- builder.originalStructType = structType;
-
- for (auto ff : structType->getFields())
- {
- builder.addField(ff);
- }
-
- return builder.getResult();
- }
- else if(auto arrayType = as<IRArrayTypeBase>(type))
- {
- auto legalElementType = legalizeType(
- context,
- arrayType->getElementType());
-
- ArrayLegalTypeWrapper wrapper;
- wrapper.arrayType = arrayType;
-
- return wrapLegalType(
- context,
- legalElementType,
- &wrapper,
- &wrapper);
- }
-
- return LegalType::simple(type);
-}
-
-LegalType legalizeType(
- TypeLegalizationContext* context,
- IRType* type)
-{
- LegalType legalType;
- if(context->mapTypeToLegalType.TryGetValue(type, legalType))
- return legalType;
-
- legalType = legalizeTypeImpl(context, type);
- context->mapTypeToLegalType[type] = legalType;
- return legalType;
-}
-
-//
-
-RefPtr<TypeLayout> getDerefTypeLayout(
- TypeLayout* typeLayout)
-{
- if (!typeLayout)
- return nullptr;
-
- if (auto parameterGroupTypeLayout = as<ParameterGroupTypeLayout>(typeLayout))
- {
- return parameterGroupTypeLayout->offsetElementTypeLayout;
- }
-
- return typeLayout;
-}
-
-RefPtr<VarLayout> getFieldLayout(
- TypeLayout* typeLayout,
- IRInst* fieldKey)
-{
- if (!typeLayout)
- return nullptr;
-
- for(;;)
- {
- if(auto arrayTypeLayout = as<ArrayTypeLayout>(typeLayout))
- {
- typeLayout = arrayTypeLayout->elementTypeLayout;
- }
- else if(auto parameterGroupTypeLayout = as<ParameterGroupTypeLayout>(typeLayout))
- {
- typeLayout = parameterGroupTypeLayout->offsetElementTypeLayout;
- }
- else
- {
- break;
- }
- }
-
-
- if (auto structTypeLayout = as<StructTypeLayout>(typeLayout))
- {
- // First, let's see if the field had a layout registered
- // directly using its IR key.
- //
- RefPtr<VarLayout> fieldLayout;
- if(structTypeLayout->mapKeyToLayout.TryGetValue(fieldKey, fieldLayout))
- return fieldLayout;
-
- // Otherwise, fall back to doing lookup using the linkage
- // attached to the key, and its mangled name.
- //
- auto fieldLinkage = fieldKey->findDecoration<IRLinkageDecoration>();
- if(!fieldLinkage)
- return nullptr;
- auto mangledFieldName = fieldLinkage->getMangledName();
-
- // In this case we fall back to a linear search over the fields.
- //
- for(auto ff : structTypeLayout->fields)
- {
- if(mangledFieldName == getMangledName(ff->varDecl.getDecl()).getUnownedSlice() )
- {
- return ff;
- }
- }
- }
-
- return nullptr;
-}
-
-RefPtr<VarLayout> createSimpleVarLayout(
- SimpleLegalVarChain* 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 most resource kinds, the register index/space to use should
- // be the sum along the entire chain of variables.
- //
- // For example, if we had input:
- //
- // struct S { Texture2D a; Texture2D b; };
- // S s : register(t10);
- //
- // And we were generating a stand-alone variable for `s.b`, then
- // we'd need to add the offset for `b` (1 texture register), to
- // the offset for `s` (10 texture registers) to get the final
- // binding to apply.
- //
- 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;
- }
- }
- }
-
- // As a special case, if the leaf variable doesn't hold an entry for
- // `RegisterSpace`, but at least one declaration in the chain *does*,
- // then we want to make sure that we add such an entry.
- if (!varLayout->FindResourceInfo(LayoutResourceKind::RegisterSpace))
- {
- // Sum up contributions from all parents.
- UInt space = 0;
- for (auto vv = varChain; vv; vv = vv->next)
- {
- if (auto parentResInfo = vv->varLayout->FindResourceInfo(LayoutResourceKind::RegisterSpace))
- {
- space += parentResInfo->index;
- }
- }
-
- // If there were non-zero contributions, then add an entry to represent them.
- if (space)
- {
- varLayout->findOrAddResourceInfo(LayoutResourceKind::RegisterSpace)->index = space;
- }
- }
-
- return varLayout;
-}
-
-
-RefPtr<VarLayout> createVarLayout(
- LegalVarChain const& varChain,
- TypeLayout* typeLayout)
-{
- if(!typeLayout)
- return nullptr;
-
- auto varLayout = createSimpleVarLayout(varChain.primaryChain, typeLayout);
-
- if(auto pendingDataTypeLayout = typeLayout->pendingDataTypeLayout)
- {
- varLayout->pendingVarLayout = createSimpleVarLayout(varChain.pendingChain, typeLayout);
- }
-
- 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
-
-}