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.cpp405
1 files changed, 360 insertions, 45 deletions
diff --git a/source/slang/legalize-types.cpp b/source/slang/legalize-types.cpp
index 3accad448..6e459ecf1 100644
--- a/source/slang/legalize-types.cpp
+++ b/source/slang/legalize-types.cpp
@@ -69,9 +69,103 @@ LegalType LegalType::pair(
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>();
+}
+
//
-static bool isResourceType(IRType* type)
+bool isResourceType(IRType* type)
{
while (auto arrayType = as<IRArrayTypeBase>(type))
{
@@ -152,7 +246,7 @@ struct TupleTypeBuilder
IRStructKey* fieldKey,
LegalType legalFieldType,
LegalType legalLeafType,
- bool isResource)
+ bool isSpecial)
{
LegalType ordinaryType;
LegalType specialType;
@@ -164,7 +258,7 @@ struct TupleTypeBuilder
// 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)
+ if (!isSpecial)
{
ordinaryType = legalLeafType;
}
@@ -198,7 +292,7 @@ struct TupleTypeBuilder
fieldKey,
legalFieldType,
legalLeafType.getImplicitDeref()->valueType,
- isResource);
+ isSpecial);
return;
}
break;
@@ -214,7 +308,7 @@ struct TupleTypeBuilder
//
// This is because the "ordinary" side of the legalization
// of `ConstantBuffer<Foo>` will still be a resource type.
- if(isResource)
+ if(isSpecial)
{
specialType = legalFieldType;
}
@@ -239,8 +333,6 @@ struct TupleTypeBuilder
break;
}
-// String mangledFieldName = getMangledName(fieldDeclRef.getDecl());
-
PairInfo::Element pairElement;
pairElement.flags = 0;
pairElement.key = fieldKey;
@@ -295,14 +387,14 @@ struct TupleTypeBuilder
{
auto fieldType = field->getFieldType();
- bool isResourceField = isResourceType(fieldType);
+ bool isSpecialField = context->isSpecialType(fieldType);
auto legalFieldType = legalizeType(context, fieldType);
addField(
field->getKey(),
legalFieldType,
legalFieldType,
- isResourceField);
+ isSpecialField);
}
LegalType getResult()
@@ -317,14 +409,6 @@ struct TupleTypeBuilder
// 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);
@@ -368,7 +452,7 @@ struct TupleTypeBuilder
// collide.
//
// (Also, the original type wasn't legal - that was the whole point...)
- context->instsToRemove.Add(originalStructType);
+ context->replacedInstructions.Add(originalStructType);
for(auto ee : ordinaryElements)
{
@@ -440,8 +524,19 @@ static LegalType createLegalUniformBufferType(
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();
@@ -449,6 +544,11 @@ static LegalType createLegalUniformBufferType(
{
// 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,
@@ -478,36 +578,90 @@ static LegalType createLegalUniformBufferType(
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::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();
+ // 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:
{
- // 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`
+ // 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();
- // 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;
@@ -523,12 +677,174 @@ static LegalType createLegalUniformBufferType(
break;
default:
- SLANG_UNEXPECTED("unknown legal type flavor");
+ 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,
@@ -789,7 +1105,7 @@ LegalType legalizeTypeImpl(
// we still need to create a new uniform buffer type
// from `legalElementType` instead of `type`
// because the `legalElementType` may still differ from `type`
- // if `type` contains empty structs.
+ // if, e.g., `type` contains empty structs.
return createLegalUniformBufferType(
context,
uniformBufferType,
@@ -814,6 +1130,20 @@ LegalType legalizeTypeImpl(
{
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());
@@ -909,21 +1239,6 @@ LegalType legalizeTypeImpl(
return LegalType::simple(type);
}
-void initialize(
- TypeLegalizationContext* context,
- Session* session,
- IRModule* module)
-{
- context->session = session;
- context->irModule = module;
-
- context->sharedBuilder.session = session;
- context->sharedBuilder.module = module;
-
- context->builder.sharedBuilder = &context->sharedBuilder;
- context->builder.setInsertInto(module->moduleInst);
-}
-
LegalType legalizeType(
TypeLegalizationContext* context,
IRType* type)