diff options
Diffstat (limited to 'source/slang/slang-ir-specialize.cpp')
| -rw-r--r-- | source/slang/slang-ir-specialize.cpp | 98 |
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); |
