summaryrefslogtreecommitdiffstats
path: root/source/slang/ir-specialize.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'source/slang/ir-specialize.cpp')
-rw-r--r--source/slang/ir-specialize.cpp169
1 files changed, 169 insertions, 0 deletions
diff --git a/source/slang/ir-specialize.cpp b/source/slang/ir-specialize.cpp
index 0da06580f..7a129a03b 100644
--- a/source/slang/ir-specialize.cpp
+++ b/source/slang/ir-specialize.cpp
@@ -426,6 +426,14 @@ struct SpecializationContext
case kIROp_ExtractExistentialWitnessTable:
maybeSpecializeExtractExistentialWitnessTable(inst);
break;
+
+ case kIROp_Load:
+ maybeSpecializeLoad(as<IRLoad>(inst));
+ break;
+
+ case kIROp_BindExistentialsType:
+ maybeSpecializeBindExistentialsType(as<IRBindExistentialsType>(inst));
+ break;
}
}
@@ -1145,6 +1153,167 @@ struct SpecializationContext
}
}
+ /// Given a type being used as pointer, try to determine the type it points to.
+ IRType* tryGetPointedToType(
+ IRBuilder* builder,
+ IRType* type)
+ {
+ // The "true" pointers and the pointer-like stdlib types are the easy cases.
+ if( auto ptrType = as<IRPtrTypeBase>(type) )
+ {
+ return ptrType->getValueType();
+ }
+ else if( auto ptrLikeType = as<IRPointerLikeType>(type) )
+ {
+ return ptrLikeType->getElementType();
+ }
+ //
+ // A more interesting case arises when we have a `BindExistentials<P<T>, ...>`
+ // where `P<T>` is a pointer(-like) type.
+ //
+ else if( auto bindExistentials = as<IRBindExistentialsType>(type) )
+ {
+ // We know that `BindExistentials` won't introduce its own
+ // existential type parameters, nor will any of the pointer(-like)
+ // type constructors `P`.
+ //
+ // Thus we know that the type that is pointed to should be
+ // the same as `BindExistentials<T, ...>`.
+ //
+ auto baseType = bindExistentials->getBaseType();
+ if( auto baseElementType = tryGetPointedToType(builder, baseType) )
+ {
+ UInt existentialArgCount = bindExistentials->getExistentialArgCount();
+ List<IRInst*> existentialArgs;
+ for( UInt ii = 0; ii < existentialArgCount; ++ii )
+ {
+ existentialArgs.Add(bindExistentials->getExistentialArg(ii));
+ }
+ return builder->getBindExistentialsType(
+ baseElementType,
+ existentialArgCount,
+ existentialArgs.Buffer());
+ }
+ }
+
+ // TODO: We may need to handle other cases here.
+
+ return nullptr;
+ }
+
+ void maybeSpecializeLoad(IRLoad* inst)
+ {
+ auto ptrArg = inst->ptr.get();
+
+ if( auto wrapInst = as<IRWrapExistential>(ptrArg) )
+ {
+ // We have an instruction of the form `load(wrapExistential(val, ...))`
+ //
+ auto val = wrapInst->getWrappedValue();
+
+ // We know what type we are expected to
+ // produce (which should be the pointed-to
+ // type for whatever the type of the
+ // `wrapExistential` is).
+ //
+ auto resultType = inst->getFullType();
+
+ IRBuilder builder;
+ builder.sharedBuilder = &sharedBuilderStorage;
+ builder.setInsertBefore(inst);
+
+ // We'd *like* to replace this instruction with
+ // `wrapExistential(load(val))` instead, since that
+ // will enable subsequent specializations.
+ //
+ // To do that, we need to be able to determine
+ // the type that `load(val)` should return.
+ //
+ auto elementType = tryGetPointedToType(&builder, val->getDataType());
+ if(!elementType)
+ return;
+
+
+ List<IRInst*> slotOperands;
+ UInt slotOperandCount = wrapInst->getSlotOperandCount();
+ for( UInt ii = 0; ii < slotOperandCount; ++ii )
+ {
+ slotOperands.Add(wrapInst->getSlotOperand(ii));
+ }
+
+ auto newLoadInst = builder.emitLoad(elementType, val);
+ auto newWrapExistentialInst = builder.emitWrapExistential(
+ resultType,
+ newLoadInst,
+ slotOperandCount,
+ slotOperands.Buffer());
+
+ addUsersToWorkList(inst);
+
+ inst->replaceUsesWith(newWrapExistentialInst);
+ inst->removeAndDeallocate();
+ }
+ }
+
+ void maybeSpecializeBindExistentialsType(IRBindExistentialsType* type)
+ {
+ auto baseType = type->getBaseType();
+ UInt slotOperandCount = type->getExistentialArgCount();
+
+ IRBuilder builder;
+ builder.sharedBuilder = &sharedBuilderStorage;
+ builder.setInsertBefore(type);
+
+ if( auto baseInterfaceType = as<IRInterfaceType>(baseType) )
+ {
+ // A `BindExistentials<ISomeInterface, ConcreteType, ...>` can
+ // just be simplified to `ExistentialBox<ConcreteType>`.
+ //
+ // Note: We do *not* simplify straight to `ConcreteType`, because
+ // that would mess up the layout for aggregate types that
+ // contain interfaces. The logical indirection introduced
+ // by `ExistentialBox<...>` will be handled by a later type
+ // legalization pass that moved the type "pointed to" by
+ // the box out of line from other fields.
+
+ // We always expect two slot operands, one for the concrete type
+ // and one for the witness table.
+ //
+ SLANG_ASSERT(slotOperandCount == 2);
+ if(slotOperandCount <= 1) return;
+
+ auto concreteType = (IRType*) type->getExistentialArg(0);
+ auto newVal = builder.getPtrType(kIROp_ExistentialBoxType, concreteType);
+
+ addUsersToWorkList(type);
+ type->replaceUsesWith(newVal);
+ type->removeAndDeallocate();
+ return;
+ }
+ else if( auto basePtrLikeType = as<IRPointerLikeType>(baseType) )
+ {
+ // A `BindExistentials<P<T>, ...>` can be simplified to
+ // `P<BindExistentials<T, ...>>` when `P` is a pointer-like
+ // type constructor.
+ //
+ auto baseElementType = basePtrLikeType->getElementType();
+ IRInst* wrappedElementType = builder.getBindExistentialsType(
+ baseElementType,
+ slotOperandCount,
+ type->getExistentialArgs());
+
+ auto newPtrLikeType = builder.getType(
+ basePtrLikeType->op,
+ 1,
+ &wrappedElementType);
+
+ addUsersToWorkList(type);
+ type->replaceUsesWith(newPtrLikeType);
+ type->removeAndDeallocate();
+ return;
+ }
+ }
+
// The handling of specialization for global generic type
// parameters involves searching for all `bind_global_generic_param`
// instructions in the input module.