summaryrefslogtreecommitdiff
path: root/source/slang/slang-ir-specialize.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'source/slang/slang-ir-specialize.cpp')
-rw-r--r--source/slang/slang-ir-specialize.cpp98
1 files changed, 50 insertions, 48 deletions
diff --git a/source/slang/slang-ir-specialize.cpp b/source/slang/slang-ir-specialize.cpp
index efcdb4498..693494ac1 100644
--- a/source/slang/slang-ir-specialize.cpp
+++ b/source/slang/slang-ir-specialize.cpp
@@ -69,6 +69,16 @@ struct SpecializationContext
//
if(!inst) return true;
+ // An interface requirement entry should always be considered
+ // to be fully specialized, even if it hasn't been visited.
+ //
+ // Note: This logic is here to stop a circularity, where we
+ // can't mark an interface as used until its requirements are
+ // used, etc.
+ //
+ if(inst->op == kIROp_InterfaceRequirementEntry)
+ return true;
+
return fullySpecializedInsts.Contains(inst);
}
@@ -618,33 +628,6 @@ struct SpecializationContext
return nullptr;
}
- void maybeInsertGetExistentialValue(IRInst* inst)
- {
- // If inst has `ExistentialBox` type, we need to make sure
- // all uses are through `GetValueFromExistentialBox`.
- if (auto existentialBoxType = as<IRExistentialBoxType>(inst->getDataType()))
- {
- ShortList<IRUse*> usesToReplace;
- for (auto use = inst->firstUse; use; use = use->nextUse)
- {
- if (use->getUser()->op != kIROp_GetValueFromExistentialBox)
- usesToReplace.add(use);
- }
- for (auto use : usesToReplace)
- {
- auto user = use->getUser();
- IRBuilder builderStorage;
- auto builder = &builderStorage;
- builder->sharedBuilder = &sharedBuilderStorage;
- builder->setInsertBefore(user);
- auto getValueInst = builder->emitGetValueFromExistentialBox(
- builder->getPtrType(existentialBoxType->getValueType()), inst);
- use->set(getValueInst);
- addToWorkList(getValueInst);
- }
- }
- }
-
// All of the machinery for generic specialization
// has been defined above, so we will now walk
// through the flow of the overall specialization pass.
@@ -712,10 +695,6 @@ struct SpecializationContext
workListSet.Remove(inst);
cleanInsts.Add(inst);
- // If inst represents a value of ExistentialBox type, all its uses
- // must be through a `GetValueFromExistentialBox` inst.
- maybeInsertGetExistentialValue(inst);
-
// For each instruction we process, we want to perform
// a few steps.
//
@@ -1131,8 +1110,33 @@ struct SpecializationContext
// (which implicitly determines the concrete type), and
// the witness table `w.
//
- if(as<IRMakeExistential>(inst))
+ if( auto makeExistential = as<IRMakeExistential>(inst) )
+ {
+ // We need to be careful about the type that we'd be specializing
+ // to, since it needs to be visible to both the caller and calee.
+ //
+ // In particular, if the type is the result of a function-local
+ // operation like `extractExistentialType`, then we can't possibly
+ // specialize the callee, since it wouldn't be able to access
+ // the same type (since the type is the result of an instruction in
+ // the body of the caller)
+ //
+ auto concreteVal = makeExistential->getWrappedValue();
+ auto concreteType = concreteVal->getDataType();
+
+ // TODO: We probably need/want a more robust test here.
+ // For now we are just listing the single opcode that is
+ // causing problems.
+ //
+ // TODO: eventually this check would become unnecessary because
+ // we can simply check if the `concreteType` is a compile-time
+ // constant value.
+ //
+ if(concreteType->op == kIROp_ExtractExistentialType)
+ return false;
+
return true;
+ }
// A `wrapExistential(v, T0,w0, T1, w1, ...)` instruction
// is just a generalization of `makeExistential`, so it
@@ -1531,7 +1535,11 @@ struct SpecializationContext
UInt calcExistentialBoxSlotCount(IRType* type)
{
top:
- if( as<IRExistentialBoxType>(type) )
+ if( as<IRBoundInterfaceType>(type) )
+ {
+ return 2;
+ }
+ else if( as<IRInterfaceType>(type) )
{
return 2;
}
@@ -1793,14 +1801,16 @@ struct SpecializationContext
auto index = inst->getIndex();
auto val = wrapInst->getWrappedValue();
+ auto ptrType = cast<IRPtrTypeBase>(val->getDataType());
+ auto arrayType = cast<IRArrayTypeBase>(ptrType->getValueType());
+ auto elementType = arrayType->getElementType();
+
auto resultType = inst->getFullType();
IRBuilder builder;
builder.sharedBuilder = &sharedBuilderStorage;
builder.setInsertBefore(inst);
- auto elementType = cast<IRArrayTypeBase>(val->getDataType())->getElementType();
-
List<IRInst*> slotOperands;
UInt slotOperandCount = wrapInst->getSlotOperandCount();
@@ -1809,7 +1819,8 @@ struct SpecializationContext
slotOperands.add(wrapInst->getSlotOperand(ii));
}
- auto newElementAddr = builder.emitElementAddress(elementType, val, index);
+ auto elementPtrType = builder.getPtrType(ptrType->op, elementType);
+ auto newElementAddr = builder.emitElementAddress(elementPtrType, val, index);
auto newWrapExistentialInst = builder.emitWrapExistential(
resultType, newElementAddr, slotOperandCount, slotOperands.getBuffer());
@@ -1870,24 +1881,15 @@ struct SpecializationContext
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;
+ if(slotOperandCount < 2) return;
auto concreteType = (IRType*) type->getExistentialArg(0);
- auto newVal = builder.getExistentialBoxType(concreteType, baseInterfaceType);
+ auto witnessTable = type->getExistentialArg(1);
+ auto newVal = builder.getBoundInterfaceType(baseInterfaceType, concreteType, witnessTable);
addUsersToWorkList(type);
type->replaceUsesWith(newVal);