diff options
| author | Yong He <yonghe@outlook.com> | 2020-06-26 11:59:33 -0700 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2020-06-26 11:59:33 -0700 |
| commit | 3e8bdb60afb5b0c0a53ce06d1dbbc429988f5885 (patch) | |
| tree | 03f379d064f5e4df3423824140fad897b8a688e7 /source | |
| parent | d084f632a136354dd12952183994240b459240ee (diff) | |
| parent | 4e443984065552cc2f648ae2fae9e49a4ef21107 (diff) | |
Merge pull request #1408 from csyonghe/dyndispatch2
Dynamic dispatch for generic interface requirements and `associatedtype`
Diffstat (limited to 'source')
| -rw-r--r-- | source/slang/slang-emit-c-like.cpp | 27 | ||||
| -rw-r--r-- | source/slang/slang-emit-cpp.cpp | 109 | ||||
| -rw-r--r-- | source/slang/slang-emit-cpp.h | 2 | ||||
| -rw-r--r-- | source/slang/slang-emit.cpp | 3 | ||||
| -rw-r--r-- | source/slang/slang-ir-inst-defs.h | 18 | ||||
| -rw-r--r-- | source/slang/slang-ir-insts.h | 31 | ||||
| -rw-r--r-- | source/slang/slang-ir-link.cpp | 8 | ||||
| -rw-r--r-- | source/slang/slang-ir-lower-generics.cpp | 191 | ||||
| -rw-r--r-- | source/slang/slang-ir-specialize.cpp | 5 | ||||
| -rw-r--r-- | source/slang/slang-ir.cpp | 38 | ||||
| -rw-r--r-- | source/slang/slang-ir.h | 31 | ||||
| -rw-r--r-- | source/slang/slang-lower-to-ir.cpp | 296 |
12 files changed, 571 insertions, 188 deletions
diff --git a/source/slang/slang-emit-c-like.cpp b/source/slang/slang-emit-c-like.cpp index 3438fd3f4..b1a664ade 100644 --- a/source/slang/slang-emit-c-like.cpp +++ b/source/slang/slang-emit-c-like.cpp @@ -203,7 +203,7 @@ void CLikeSourceEmitter::emitSimpleType(IRType* type) case kIROp_HalfType: return UnownedStringSlice("half"); case kIROp_FloatType: return UnownedStringSlice("float"); - case kIROp_DoubleType: return UnownedStringSlice("double"); + case kIROp_DoubleType: return UnownedStringSlice("double"); default: return UnownedStringSlice(); } } @@ -236,9 +236,9 @@ List<IRWitnessTableEntry*> CLikeSourceEmitter::getSortedWitnessTableEntries(IRWi // Get a sorted list of entries using RequirementKeys defined in `interfaceType`. for (UInt i = 0; i < interfaceType->getOperandCount(); i++) { - auto reqKey = cast<IRStructKey>(interfaceType->getOperand(i)); + auto reqEntry = cast<IRInterfaceRequirementEntry>(interfaceType->getOperand(i)); IRWitnessTableEntry* entry = nullptr; - if (witnessTableEntryDictionary.TryGetValue(reqKey, entry)) + if (witnessTableEntryDictionary.TryGetValue(reqEntry->getRequirementKey(), entry)) { sortedWitnessTableEntries.add(entry); } @@ -305,7 +305,8 @@ void CLikeSourceEmitter::emitWitnessTable(IRWitnessTable* witnessTable) void CLikeSourceEmitter::emitInterface(IRInterfaceType* interfaceType) { SLANG_UNUSED(interfaceType); - SLANG_DIAGNOSE_UNEXPECTED(getSink(), SourceLoc(), "Unimplemented emit: IROpInterfaceType."); + // By default, don't emit anything for interface types. + // This behavior is overloaded by concrete emitters. } void CLikeSourceEmitter::emitTypeImpl(IRType* type, const StringSliceLoc* nameAndLoc) @@ -1962,6 +1963,10 @@ void CLikeSourceEmitter::defaultEmitInstExpr(IRInst* inst, const EmitOpInfo& inO are hashed with 'getStringHash' */ break; + case kIROp_undefined: + m_writer->emit(getName(inst)); + break; + case kIROp_IntLit: case kIROp_FloatLit: case kIROp_BoolLit: @@ -2285,7 +2290,6 @@ void CLikeSourceEmitter::defaultEmitInstExpr(IRInst* inst, const EmitOpInfo& inO m_writer->emit(")"); } break; - default: diagnoseUnhandledInst(inst); break; @@ -3554,6 +3558,11 @@ void CLikeSourceEmitter::emitGlobalInst(IRInst* inst) are hashed with 'getStringHash' */ break; + case kIROp_InterfaceRequirementEntry: + // Don't emit anything for interface requirement at global level. + // They are handled in `emitInterface`. + break; + case kIROp_Func: emitFunc((IRFunc*) inst); break; @@ -3610,6 +3619,10 @@ void CLikeSourceEmitter::ensureInstOperandsRec(ComputeEmitActionsContext* ctx, I ensureInstOperand(ctx, inst->getFullType()); UInt operandCount = inst->operandCount; + auto requiredLevel = EmitAction::Definition; + if (inst->op == kIROp_InterfaceType) + requiredLevel = EmitAction::ForwardDeclaration; + for(UInt ii = 0; ii < operandCount; ++ii) { // TODO: there are some special cases we can add here, @@ -3621,7 +3634,7 @@ void CLikeSourceEmitter::ensureInstOperandsRec(ComputeEmitActionsContext* ctx, I // Similarly, a `call` instruction only needs the callee // to be forward-declared, etc. - ensureInstOperand(ctx, inst->getOperand(ii)); + ensureInstOperand(ctx, inst->getOperand(ii), requiredLevel); } for(auto child : inst->getDecorationsAndChildren()) @@ -3641,6 +3654,8 @@ void CLikeSourceEmitter::ensureGlobalInst(ComputeEmitActionsContext* ctx, IRInst if (!m_compileRequest->allowDynamicCode) return; break; + + case kIROp_InterfaceRequirementEntry: case kIROp_Generic: return; diff --git a/source/slang/slang-emit-cpp.cpp b/source/slang/slang-emit-cpp.cpp index 4a59f4cf9..7f743d9e0 100644 --- a/source/slang/slang-emit-cpp.cpp +++ b/source/slang/slang-emit-cpp.cpp @@ -494,10 +494,15 @@ SlangResult CPPSourceEmitter::calcTypeName(IRType* type, CodeGenTarget target, S // struct of function pointers corresponding to the interface type. auto witnessTableType = static_cast<IRWitnessTableType*>(type); auto baseType = cast<IRType>(witnessTableType->getOperand(0)); - emitType(baseType); + SLANG_RETURN_ON_FAIL(calcTypeName(baseType, target, out)); out << "*"; return SLANG_OK; } + case kIROp_RawPointerType: + { + out << "void*"; + return SLANG_OK; + } default: { if (isNominalOp(type->op)) @@ -1591,8 +1596,7 @@ void CPPSourceEmitter::emitWitnessTable(IRWitnessTable* witnessTable) { auto interfaceType = cast<IRInterfaceType>(witnessTable->getOperand(0)); auto witnessTableItems = witnessTable->getChildren(); - List<IRWitnessTableEntry*> sortedWitnessTableEntries = getSortedWitnessTableEntries(witnessTable); - _maybeEmitWitnessTableTypeDefinition(interfaceType, sortedWitnessTableEntries); + _maybeEmitWitnessTableTypeDefinition(interfaceType); // Define a global variable for the witness table. m_writer->emit("extern "); @@ -1613,12 +1617,27 @@ void CPPSourceEmitter::_emitWitnessTableWrappers() { for (auto witnessTable : pendingWitnessTableDefinitions) { + auto interfaceType = cast<IRInterfaceType>(witnessTable->getOperand(0)); for (auto child : witnessTable->getChildren()) { if (auto entry = as<IRWitnessTableEntry>(child)) { if (auto funcVal = as<IRFunc>(entry->getSatisfyingVal())) { + IRInst* requirementVal = nullptr; + for (UInt i = 0; i < interfaceType->getOperandCount(); i++) + { + if (auto reqEntry = as<IRInterfaceRequirementEntry>(interfaceType->getOperand(i))) + { + if (reqEntry->getRequirementKey() == entry->getRequirementKey()) + { + requirementVal = reqEntry->getRequirementVal(); + break; + } + } + } + SLANG_ASSERT(requirementVal != nullptr); + IRFuncType* requirementFuncType = cast<IRFuncType>(requirementVal); emitType(funcVal->getResultType()); m_writer->emit(" "); m_writer->emit(_getWitnessTableWrapperFuncName(funcVal)); @@ -1626,23 +1645,20 @@ void CPPSourceEmitter::_emitWitnessTableWrappers() // Emit parameter list. { bool isFirst = true; - for (auto param : funcVal->getParams()) + SLANG_ASSERT(funcVal->getParamCount() == requirementFuncType->getParamCount()); + auto pp = funcVal->getParams().begin(); + for (UInt i = 0; i < requirementFuncType->getParamCount(); ++i, ++pp) { - if (as<IRTypeType>(param->getFullType())) + auto paramType = requirementFuncType->getParamType(i); + + if (as<IRTypeType>(paramType)) continue; if (isFirst) isFirst = false; else m_writer->emit(","); - - if (param->findDecoration<IRThisPointerDecoration>()) - { - m_writer->emit("void* "); - m_writer->emit(getName(param)); - continue; - } - emitSimpleFuncParamImpl(param); + emitParamType(paramType, getName(*pp)); } } m_writer->emit(")\n{\n"); @@ -1653,8 +1669,13 @@ void CPPSourceEmitter::_emitWitnessTableWrappers() // Emit argument list. { bool isFirst = true; - for (auto param : funcVal->getParams()) + UInt paramIndex = 0; + for (auto defParamIter = funcVal->getParams().begin(); + defParamIter!=funcVal->getParams().end(); + ++defParamIter, ++paramIndex) { + auto param = *defParamIter; + auto reqParamType = requirementFuncType->getParamType(paramIndex); if (as<IRTypeType>(param->getFullType())) continue; @@ -1663,7 +1684,8 @@ void CPPSourceEmitter::_emitWitnessTableWrappers() else m_writer->emit(", "); - if (param->findDecoration<IRThisPointerDecoration>()) + if (reqParamType->op == kIROp_RawPointerType && + param->getFullType()->op != kIROp_RawPointerType) { m_writer->emit("*static_cast<"); emitType(param->getFullType()); @@ -1747,51 +1769,46 @@ void CPPSourceEmitter::emitInterface(IRInterfaceType* interfaceType) /// acoording to the order defined by `interfaceType`. /// void CPPSourceEmitter::_maybeEmitWitnessTableTypeDefinition( - IRInterfaceType* interfaceType, - const List<IRWitnessTableEntry*>& sortedWitnessTableEntries) + IRInterfaceType* interfaceType) { m_writer->emit("struct "); emitSimpleType(interfaceType); m_writer->emit("\n{\n"); m_writer->indent(); - for (Index i = 0; i < sortedWitnessTableEntries.getCount(); i++) + for (UInt i = 0; i < interfaceType->getOperandCount(); i++) { - auto entry = sortedWitnessTableEntries[i]; - if (auto funcVal = as<IRFunc>(entry->satisfyingVal.get())) + auto entry = as<IRInterfaceRequirementEntry>(interfaceType->getOperand(i)); + if (auto funcVal = as<IRFuncType>(entry->getRequirementVal())) { emitType(funcVal->getResultType()); m_writer->emit(" (KernelContext::*"); - m_writer->emit(getName(entry->requirementKey.get())); + m_writer->emit(getName(entry->getRequirementKey())); m_writer->emit(")"); m_writer->emit("("); bool isFirstParam = true; - for (auto param : funcVal->getParams()) + for (UInt p = 0; p < funcVal->getParamCount(); p++) { + auto paramType = funcVal->getParamType(p); + // Ingore TypeType-typed parameters for now. + if (as<IRTypeType>(paramType)) + continue; + if (!isFirstParam) m_writer->emit(", "); else isFirstParam = false; - if (param->findDecoration<IRThisPointerDecoration>()) - { - m_writer->emit("void* "); - m_writer->emit(getName(param)); - continue; - } - emitSimpleFuncParamImpl(param); + + emitParamType(paramType, String("param") + String(p)); } m_writer->emit(");\n"); } - else if (auto witnessTableVal = as<IRWitnessTable>(entry->getSatisfyingVal())) + else if (auto witnessTableType = as<IRWitnessTableType>(entry->getRequirementVal())) { - emitType(as<IRType>(witnessTableVal->getOperand(0))); + emitType((IRType*)witnessTableType->getConformanceType()); m_writer->emit("* "); - m_writer->emit(getName(entry->requirementKey.get())); + m_writer->emit(getName(entry->getRequirementKey())); m_writer->emit(";\n"); } - else - { - // TODO: handle other witness table entry types. - } } m_writer->dedent(); m_writer->emit("};\n"); @@ -1990,28 +2007,8 @@ void CPPSourceEmitter::emitSimpleValueImpl(IRInst* inst) } } -static bool isVoidPtrType(IRType* type) -{ - auto ptrType = as<IRPtrType>(type); - if (!ptrType) return false; - return ptrType->getValueType()->op == kIROp_VoidType; -} - void CPPSourceEmitter::emitSimpleFuncParamImpl(IRParam* param) { - // Polymorphic types are already translated to void* type in - // lower-generics pass. However, the current emitting logic will - // emit "void&" instead of "void*" for pointer types. - // In the future, we will handle pointer types more properly, - // and this override logic will not be necessary. - // For now we special-case this scenario. - if (param->findDecoration<IRPolymorphicDecoration>() && - isVoidPtrType(param->getDataType())) - { - m_writer->emit("void* "); - m_writer->emit(getName(param)); - return; - } CLikeSourceEmitter::emitSimpleFuncParamImpl(param); } diff --git a/source/slang/slang-emit-cpp.h b/source/slang/slang-emit-cpp.h index 47ba03d70..6f91444a3 100644 --- a/source/slang/slang-emit-cpp.h +++ b/source/slang/slang-emit-cpp.h @@ -89,7 +89,7 @@ protected: virtual SlangResult calcScalarFuncName(HLSLIntrinsic::Op op, IRBasicType* type, StringBuilder& outBuilder); // Emits a struct of function pointers defined in `interfaceType`. - void _maybeEmitWitnessTableTypeDefinition(IRInterfaceType* interfaceType, const List<IRWitnessTableEntry*>& sortedWitnessTableEntries); + void _maybeEmitWitnessTableTypeDefinition(IRInterfaceType* interfaceType); void _maybeEmitSpecializedOperationDefinition(const HLSLIntrinsic* specOp); void _emitForwardDeclarations(const List<EmitAction>& actions); diff --git a/source/slang/slang-emit.cpp b/source/slang/slang-emit.cpp index f2552f95d..59b059e91 100644 --- a/source/slang/slang-emit.cpp +++ b/source/slang/slang-emit.cpp @@ -280,7 +280,8 @@ Result linkAndOptimizeIR( // For targets that supports dynamic dispatch, we need to lower the // generics / interface types to ordinary functions and types using // function pointers. - lowerGenerics(irModule); + if (compileRequest->allowDynamicCode) + lowerGenerics(irModule); break; default: break; diff --git a/source/slang/slang-ir-inst-defs.h b/source/slang/slang-ir-inst-defs.h index 58ff1a79f..85c489528 100644 --- a/source/slang/slang-ir-inst-defs.h +++ b/source/slang/slang-ir-inst-defs.h @@ -25,6 +25,7 @@ INST(Nop, nop, 0, 0) INST_RANGE(BasicType, VoidType, AfterBaseType) INST(StringType, String, 0, 0) + INST(RawPointerType, RawPointerType, 0, 0) /* ArrayTypeBase */ INST(ArrayType, Array, 2, 0) @@ -164,14 +165,16 @@ INST(Nop, nop, 0, 0) // `field` instructions. // INST(StructType, struct, 0, PARENT) -INST(InterfaceType, interface, 0, PARENT) +INST(InterfaceType, interface, 0, 0) +INST(AssociatedType, associated_type, 0, 0) +INST(ThisType, this_type, 0, 0) // A TypeType-typed IRValue represents a IRType. // It is used to represent a type parameter/argument in a generics. INST(TypeType, type_t, 0, 0) // An `IRWitnessTable` has type `WitnessTableType`. -INST(WitnessTableType, witness_table_t, 0, 0) +INST(WitnessTableType, witness_table_t, 1, 0) INST_RANGE(Type, VoidType, WitnessTableType) @@ -223,6 +226,7 @@ INST(Call, call, 1, 0) INST(WitnessTableEntry, witness_table_entry, 2, 0) +INST(InterfaceRequirementEntry, interface_req_entry, 2, 0) INST(Param, param, 0, 0) INST(StructField, field, 2, 0) @@ -507,16 +511,6 @@ INST(HighLevelDeclDecoration, highLevelDecl, 1, 0) INST(BindExistentialSlotsDecoration, bindExistentialSlots, 0, 0) - /// A `[polymorphic]` decoration marks a function parameter that should translate to an abstract type - /// e.g. (void*) that are casted to actual type before use. For example, a parameter of generic type - /// is marked `[polymorphic]`, so that the code gen logic can emit it as a `void*` parameter, - /// allowing the function to be used at sites that are agnostic of the actual object type. - INST(PolymorphicDecoration, polymorphic, 0, 0) - - /// A `[this_ptr]` decoration marks a function parameter that serves as `this` pointer. - INST(ThisPointerDecoration, this_ptr, 0, 0) - - /// A `[format(f)]` decoration specifies that the format of an image should be `f` INST(FormatDecoration, format, 1, 0) diff --git a/source/slang/slang-ir-insts.h b/source/slang/slang-ir-insts.h index b13d52981..f3a0688a7 100644 --- a/source/slang/slang-ir-insts.h +++ b/source/slang/slang-ir-insts.h @@ -166,10 +166,6 @@ IR_SIMPLE_DECORATION(VulkanCallablePayloadDecoration) /// to it. IR_SIMPLE_DECORATION(VulkanHitAttributesDecoration) -IR_SIMPLE_DECORATION(PolymorphicDecoration) -IR_SIMPLE_DECORATION(ThisPointerDecoration) - - struct IRRequireGLSLVersionDecoration : IRDecoration { enum { kOp = kIROp_RequireGLSLVersionDecoration }; @@ -413,6 +409,8 @@ struct IRLookupWitnessMethod : IRInst IRInst* getWitnessTable() { return witnessTable.get(); } IRInst* getRequirementKey() { return requirementKey.get(); } + + IR_LEAF_ISA(lookup_interface_method) }; struct IRLookupWitnessTable : IRInst @@ -1432,6 +1430,11 @@ struct IRWitnessTable : IRInst return IRInstList<IRWitnessTableEntry>(getChildren()); } + IRInst* getConformanceType() + { + return getOperand(0); + } + IR_LEAF_ISA(WitnessTable) }; @@ -1571,6 +1574,10 @@ struct IRBuilder IRBasicType* getBoolType(); IRBasicType* getIntType(); IRStringType* getStringType(); + IRAssociatedType* getAssociatedType(); + IRThisType* getThisType(); + IRRawPointerType* getRawPointerType(); + IRBasicBlockType* getBasicBlockType(); IRWitnessTableType* getWitnessTableType(IRType* baseType); @@ -1800,7 +1807,7 @@ struct IRBuilder IRType* valueType); IRGlobalParam* createGlobalParam( IRType* valueType); - + /// Creates an IRWitnessTable value. /// @param baseType: The comformant-to type of this witness. IRWitnessTable* createWitnessTable(IRType* baseType); @@ -1809,6 +1816,10 @@ struct IRBuilder IRInst* requirementKey, IRInst* satisfyingVal); + IRInterfaceRequirementEntry* createInterfaceRequirementEntry( + IRInst* requirementKey, + IRInst* requirementVal); + // Create an initially empty `struct` type. IRStructType* createStructType(); @@ -2160,16 +2171,6 @@ struct IRBuilder addDecoration(value, kIROp_LoopControlDecoration, getIntValue(getIntType(), IRIntegerValue(mode))); } - void addPolymorphicDecoration(IRInst* value) - { - addDecoration(value, kIROp_PolymorphicDecoration); - } - - void addThisPointerDecoration(IRInst* value) - { - addDecoration(value, kIROp_ThisPointerDecoration); - } - void addSemanticDecoration(IRInst* value, UnownedStringSlice const& text, int index = 0) { addDecoration(value, kIROp_SemanticDecoration, getStringValue(text), getIntValue(getIntType(), index)); diff --git a/source/slang/slang-ir-link.cpp b/source/slang/slang-ir-link.cpp index 3f51aa876..f80f97aff 100644 --- a/source/slang/slang-ir-link.cpp +++ b/source/slang/slang-ir-link.cpp @@ -228,6 +228,7 @@ IRInst* IRSpecContext::maybeCloneValue(IRInst* originalValue) case kIROp_StructKey: case kIROp_GlobalGenericParam: case kIROp_WitnessTable: + case kIROp_InterfaceType: case kIROp_TaggedUnionType: return cloneGlobalValue(this, originalValue); @@ -607,8 +608,7 @@ IRInterfaceType* cloneInterfaceTypeImpl( auto clonedInterface = builder->createInterfaceType(originalInterface->getOperandCount(), nullptr); for (UInt i = 0; i < originalInterface->getOperandCount(); i++) { - auto clonedKey = findClonedValue(context, originalInterface->getOperand(i)); - SLANG_ASSERT(clonedKey); + auto clonedKey = cloneValue(context, originalInterface->getOperand(i)); clonedInterface->setOperand(i, clonedKey); } cloneSimpleGlobalValueImpl(context, originalInterface, originalValues, clonedInterface); @@ -628,6 +628,7 @@ void cloneGlobalValueWithCodeCommon( cloneDecorations(context, clonedValue, originalValue); cloneExtraDecorations(context, clonedValue, originalValues); + clonedValue->setFullType((IRType*)cloneValue(context, originalValue->getFullType())); // We will walk through the blocks of the function, and clone each of them. // @@ -1152,7 +1153,6 @@ IRInst* cloneGlobalValueImpl( return clonedValue; } - /// Clone a global value, which has the given `originalLinkage`. /// /// The `originalVal` is a known global IR value with that linkage, if one is available. @@ -1216,7 +1216,7 @@ IRInst* cloneGlobalValueWithLinkage( for(IRSpecSymbol* ss = sym; ss; ss = ss->nextWithSameName ) { IRInst* newVal = ss->irGlobalValue; - if(isBetterForTarget(context, newVal, bestVal)) + if (isBetterForTarget(context, newVal, bestVal)) bestVal = newVal; } diff --git a/source/slang/slang-ir-lower-generics.cpp b/source/slang/slang-ir-lower-generics.cpp index f6340a633..774836e29 100644 --- a/source/slang/slang-ir-lower-generics.cpp +++ b/source/slang/slang-ir-lower-generics.cpp @@ -16,6 +16,7 @@ namespace Slang IRModule* module; Dictionary<IRInst*, IRInst*> loweredGenericFunctions; + HashSet<IRInterfaceType*> loweredInterfaceTypes; SharedIRBuilder sharedBuilderStorage; @@ -45,6 +46,21 @@ namespace Slang workListSet.Add(inst); } + bool isPolymorphicType(IRInst* typeInst) + { + if (as<IRParam>(typeInst) && as<IRTypeType>(typeInst->getFullType())) + return true; + switch (typeInst->op) + { + case kIROp_ThisType: + case kIROp_AssociatedType: + case kIROp_InterfaceType: + return true; + default: + return false; + } + } + IRInst* lowerGenericFunction(IRInst* genericValue) { IRInst* result = nullptr; @@ -64,6 +80,7 @@ namespace Slang builder.sharedBuilder = &sharedBuilderStorage; builder.setInsertBefore(genericParent); auto loweredFunc = cloneInstAndOperands(&cloneEnv, &builder, func); + loweredFunc->setFullType(lowerGenericFuncType(&builder, cast<IRGeneric>(genericParent->getFullType()))); List<IRInst*> clonedParams; for (auto genericParam : genericParent->getParams()) { @@ -82,15 +99,115 @@ namespace Slang // Turn generic parameters into void pointers. for (auto param : cast<IRFunc>(loweredFunc)->getParams()) { - if (param->findDecoration<IRPolymorphicDecoration>()) + if (isPolymorphicType(param->getFullType())) { - param->setFullType(builder.getPtrType(builder.getVoidType())); + param->setFullType(builder.getRawPointerType()); } } addToWorkList(loweredFunc); return loweredFunc; } + IRType* lowerGenericFuncType(IRBuilder* builder, IRGeneric* genericVal) + { + List<IRInst*> genericParamTypes; + for (auto genericParam : genericVal->getParams()) + { + if (isPolymorphicType(genericParam->getFullType())) + { + genericParamTypes.add(builder->getRawPointerType()); + } + else + { + genericParamTypes.add(genericParam->getFullType()); + } + } + + auto innerType = (IRFuncType*)lowerFuncType( + builder, + cast<IRFuncType>(findGenericReturnVal(genericVal)), + genericParamTypes.getCount()); + + for (int i = 0; i < genericParamTypes.getCount(); i++) + { + innerType->setOperand( + innerType->getOperandCount() - genericParamTypes.getCount() + i, + genericParamTypes[i]); + } + + return innerType; + } + + IRType* lowerFuncType(IRBuilder* builder, IRFuncType* funcType, UInt additionalParamCount = 0) + { + List<IRInst*> newOperands; + bool translated = false; + for (UInt i = 0; i < funcType->getOperandCount(); i++) + { + auto paramType = funcType->getOperand(i); + if (isPolymorphicType(paramType)) + { + newOperands.add(builder->getRawPointerType()); + translated = true; + } + else if (paramType->op == kIROp_Specialize) + { + // TODO: handle static specialized type here. + // For now treat all specialized types as dynamic. + // In the future, we need to turn things like Array<IDynamic> into Array<void*>. + newOperands.add(builder->getRawPointerType()); + translated = true; + } + else + { + newOperands.add(paramType); + } + } + if (!translated && additionalParamCount == 0) + return funcType; + for (UInt i = 0; i < additionalParamCount; i++) + { + newOperands.add(nullptr); + } + auto newFuncType = builder->getFuncType( + newOperands.getCount() - 1, + (IRType**)(newOperands.begin() + 1), + (IRType*)newOperands[0]); + + IRCloneEnv cloneEnv; + cloneInstDecorationsAndChildren(&cloneEnv, &sharedBuilderStorage, funcType, newFuncType); + return newFuncType; + } + + IRInterfaceType* maybeLowerInterfaceType(IRInterfaceType* interfaceType) + { + if (loweredInterfaceTypes.Contains(interfaceType)) + return interfaceType; + + IRBuilder builder; + builder.sharedBuilder = &sharedBuilderStorage; + builder.setInsertBefore(interfaceType); + + // Translate IRFuncType in interface requirements. + for (UInt i = 0; i < interfaceType->getOperandCount(); i++) + { + if (auto entry = as<IRInterfaceRequirementEntry>(interfaceType->getOperand(i))) + { + if (auto funcType = as<IRFuncType>(entry->getRequirementVal())) + { + entry->setRequirementVal(lowerFuncType(&builder, funcType)); + } + else if (auto genericFuncType = as<IRGeneric>(entry->getRequirementVal())) + { + entry->setRequirementVal(lowerGenericFuncType(&builder, genericFuncType)); + } + } + } + + loweredInterfaceTypes.Add(interfaceType); + return interfaceType; + } + void processInst(IRInst* inst) { if (auto callInst = as<IRCall>(inst)) @@ -98,26 +215,55 @@ namespace Slang // If we see a call(specialize(gFunc, Targs), args), // translate it into call(gFunc, args, Targs). auto funcOperand = callInst->getOperand(0); + IRInst* loweredFunc = nullptr; if (auto specializeInst = as<IRSpecialize>(funcOperand)) { - auto loweredFunc = lowerGenericFunction(specializeInst->getOperand(0)); - if (loweredFunc == specializeInst->getOperand(0)) + auto funcToSpecialize = specializeInst->getOperand(0); + List<IRType*> paramTypes; + if (auto interfaceLookup = as<IRLookupWitnessMethod>(funcToSpecialize)) { - // This is an intrinsic function, don't transform. - return; + // The callee is a result of witness table lookup, we will only + // translate the call. + IRInst* callee = nullptr; + auto witnessTableType = cast<IRWitnessTableType>(interfaceLookup->getWitnessTable()->getFullType()); + auto interfaceType = maybeLowerInterfaceType(cast<IRInterfaceType>(witnessTableType->getConformanceType())); + for (UInt i = 0; i < interfaceType->getOperandCount(); i++) + { + auto entry = cast<IRInterfaceRequirementEntry>(interfaceType->getOperand(i)); + if (entry->getRequirementKey() == interfaceLookup->getOperand(1)) + { + callee = entry->getRequirementVal(); + break; + } + } + auto funcType = cast<IRFuncType>(callee); + for (UInt i = 0; i < funcType->getParamCount(); i++) + paramTypes.add(funcType->getParamType(i)); + loweredFunc = funcToSpecialize; + } + else + { + loweredFunc = lowerGenericFunction(specializeInst->getOperand(0)); + if (loweredFunc == specializeInst->getOperand(0)) + { + // This is an intrinsic function, don't transform. + return; + } + for (auto param : as<IRFunc>(loweredFunc)->getParams()) + paramTypes.add(param->getDataType()); } + IRBuilder builderStorage; auto builder = &builderStorage; builder->sharedBuilder = &sharedBuilderStorage; builder->setInsertBefore(inst); List<IRInst*> args; - auto pp = as<IRFunc>(loweredFunc)->getParams().begin(); - auto voidPtrType = builder->getPtrType(builder->getVoidType()); + auto rawPtrType = builder->getRawPointerType(); for (UInt i = 0; i < callInst->getArgCount(); i++) { auto arg = callInst->getArg(i); - if ((*pp)->getDataType() == voidPtrType && - arg->getDataType() != voidPtrType) + if (paramTypes[i] == rawPtrType && + arg->getDataType() != rawPtrType) { // We are calling a generic function that with an argument of // concrete type. We need to convert this argument o void*. @@ -128,11 +274,10 @@ namespace Slang // what we needed. For now we use another instruction here // to keep changes minimal. arg = builder->emitGetAddress( - voidPtrType, + rawPtrType, arg); } args.add(arg); - ++pp; } for (UInt i = 0; i < specializeInst->getArgCount(); i++) args.add(specializeInst->getArg(i)); @@ -141,6 +286,28 @@ namespace Slang callInst->removeAndDeallocate(); } } + else if (auto witnessTable = as<IRWitnessTable>(inst)) + { + // Lower generic functions in witness table. + for (auto child : witnessTable->getChildren()) + { + auto entry = as<IRWitnessTableEntry>(child); + if (!entry) + continue; + if (auto genericVal = as<IRGeneric>(entry->getSatisfyingVal())) + { + if (findGenericReturnVal(genericVal)->op == kIROp_Func) + { + auto loweredFunc = lowerGenericFunction(genericVal); + entry->satisfyingVal.set(loweredFunc); + } + } + } + } + else if (auto interfaceType = as<IRInterfaceType>(inst)) + { + maybeLowerInterfaceType(interfaceType); + } } void processModule() diff --git a/source/slang/slang-ir-specialize.cpp b/source/slang/slang-ir-specialize.cpp index cf475f1ff..3acb34c87 100644 --- a/source/slang/slang-ir-specialize.cpp +++ b/source/slang/slang-ir-specialize.cpp @@ -416,6 +416,11 @@ struct SpecializationContext case kIROp_BindExistentialsType: break; + // An interface type is always fully specialized. + case kIROp_InterfaceType: + markInstAsFullySpecialized(inst); + break; + case kIROp_Specialize: // The `specialize` instruction is a bit sepcial, // because it is possible to have a `specialize` diff --git a/source/slang/slang-ir.cpp b/source/slang/slang-ir.cpp index 77011b569..ef5ecb959 100644 --- a/source/slang/slang-ir.cpp +++ b/source/slang/slang-ir.cpp @@ -1408,10 +1408,7 @@ namespace Slang inst->op = op; - if (type) - { - inst->typeUse.init(inst, type); - } + inst->typeUse.init(inst, type); maybeSetSourceLoc(builder, inst); @@ -1423,6 +1420,10 @@ namespace Slang { operand->init(inst, fixedArgs[aa]); } + else + { + operand->init(inst, nullptr); + } operand++; } @@ -2194,6 +2195,21 @@ namespace Slang return (IRStringType*)getType(kIROp_StringType); } + IRAssociatedType* IRBuilder::getAssociatedType() + { + return (IRAssociatedType*)getType(kIROp_AssociatedType); + } + + IRThisType* IRBuilder::getThisType() + { + return (IRThisType*)getType(kIROp_ThisType); + } + + IRRawPointerType* IRBuilder::getRawPointerType() + { + return (IRRawPointerType*)getType(kIROp_RawPointerType); + } + IRBasicBlockType* IRBuilder::getBasicBlockType() { return (IRBasicBlockType*)getType(kIROp_BasicBlockType); @@ -2811,6 +2827,20 @@ namespace Slang return entry; } + IRInterfaceRequirementEntry* IRBuilder::createInterfaceRequirementEntry( + IRInst* requirementKey, + IRInst* requirementVal) + { + IRInterfaceRequirementEntry* entry = createInst<IRInterfaceRequirementEntry>( + this, + kIROp_InterfaceRequirementEntry, + nullptr, + requirementKey, + requirementVal); + addGlobalValue(this, entry); + return entry; + } + IRStructType* IRBuilder::createStructType() { IRStructType* structType = createInst<IRStructType>( diff --git a/source/slang/slang-ir.h b/source/slang/slang-ir.h index 3c9a15650..f3eccad25 100644 --- a/source/slang/slang-ir.h +++ b/source/slang/slang-ir.h @@ -458,6 +458,7 @@ struct IRInst void setOperand(UInt index, IRInst* value) { + SLANG_ASSERT(getOperands()[index].user != nullptr); getOperands()[index].set(value); } @@ -1104,12 +1105,16 @@ struct IRPtrTypeBase : IRType SIMPLE_IR_TYPE(PtrType, PtrTypeBase) SIMPLE_IR_TYPE(RefType, PtrTypeBase) - SIMPLE_IR_PARENT_TYPE(OutTypeBase, PtrTypeBase) SIMPLE_IR_TYPE(OutType, OutTypeBase) SIMPLE_IR_TYPE(InOutType, OutTypeBase) SIMPLE_IR_TYPE(ExistentialBoxType, PtrTypeBase) +struct IRRawPointerType : IRType +{ + IR_LEAF_ISA(RawPointerType) +}; + struct IRGlobalHashedStringLiterals : IRInst { IR_LEAF_ISA(GlobalHashedStringLiterals) @@ -1190,6 +1195,26 @@ struct IRStructType : IRType IR_LEAF_ISA(StructType) }; +struct IRAssociatedType : IRType +{ + IR_LEAF_ISA(AssociatedType) +}; + +struct IRThisType : IRType +{ + IR_LEAF_ISA(ThisType) +}; + +struct IRInterfaceRequirementEntry : IRInst +{ + IRInst* getRequirementKey() { return getOperand(0); } + IRInst* getRequirementVal() { return getOperand(1); } + void setRequirementKey(IRInst* val) { setOperand(0, val); } + void setRequirementVal(IRInst* val) { setOperand(1, val); } + + IR_LEAF_ISA(InterfaceRequirementEntry); +}; + struct IRInterfaceType : IRType { IR_LEAF_ISA(InterfaceType) @@ -1207,6 +1232,10 @@ struct IRTypeType : IRType struct IRWitnessTableType : IRType { + IRInst* getConformanceType() + { + return getOperand(0); + } IR_LEAF_ISA(WitnessTableType); }; diff --git a/source/slang/slang-lower-to-ir.cpp b/source/slang/slang-lower-to-ir.cpp index ea04ea85c..58f23b0c8 100644 --- a/source/slang/slang-lower-to-ir.cpp +++ b/source/slang/slang-lower-to-ir.cpp @@ -380,6 +380,9 @@ struct IRGenContext // might be insufficient. LoweredValInfo thisVal; + // The IRType value to lower into for `ThisType`. + IRInst* thisType = nullptr; + explicit IRGenContext(SharedIRGenContext* inShared, ASTBuilder* inAstBuilder) : shared(inShared) , astBuilder(inAstBuilder) @@ -401,6 +404,18 @@ struct IRGenContext { return shared->m_mainModuleDecl; } + + LoweredValInfo* findLoweredDecl(Decl* decl) + { + IRGenEnv* envToFindIn = env; + while (envToFindIn) + { + if (auto rs = envToFindIn->mapDeclToValue.TryGetValue(decl)) + return rs; + envToFindIn = envToFindIn->outer; + } + return nullptr; + } }; void setGlobalValue(SharedIRGenContext* sharedContext, Decl* decl, LoweredValInfo value) @@ -986,6 +1001,12 @@ IRStructKey* getInterfaceRequirementKey( IRGenContext* context, Decl* requirementDecl) { + // TODO: this special case logic can be removed if we also clean up `doesGenericSignatureMatchRequirement` + // Currently `doesGenericSignatureMatchRequirement` will use the inner func decl as the key + // in AST WitnessTable. Therefore we need to match this behavior by always using the inner + // decl as the requirement key. + if (auto genericDecl = as<GenericDecl>(requirementDecl)) + return getInterfaceRequirementKey(context, genericDecl->inner); IRStructKey* requirementKey = nullptr; if(context->shared->interfaceRequirementKeys.TryGetValue(requirementDecl, requirementKey)) { @@ -1035,7 +1056,7 @@ struct ValLoweringVisitor : ValVisitor<ValLoweringVisitor, LoweredValInfo, Lower { return emitDeclRef(context, val->declRef, context->irBuilder->getWitnessTableType( - lowerType(context, DeclRefType::create(context->astBuilder, val->declRef)))); + lowerType(context, val->sup))); } LoweredValInfo visitTransitiveSubtypeWitness( @@ -1057,7 +1078,7 @@ struct ValLoweringVisitor : ValVisitor<ValLoweringVisitor, LoweredValInfo, Lower // to reflect the right constraints. return LoweredValInfo::simple(getBuilder()->emitLookupInterfaceMethodInst( - nullptr, + getBuilder()->getWitnessTableType(lowerType(context, val->sup)), baseWitnessTable, requirementKey)); } @@ -1568,6 +1589,8 @@ struct ValLoweringVisitor : ValVisitor<ValLoweringVisitor, LoweredValInfo, Lower // // For now we punt and emit the `ThisType` of an interface `IFoo` as `IFoo`. // + if (context->thisType != nullptr) + return LoweredValInfo::simple(context->thisType); return emitDeclRef(context, type->interfaceDeclRef, getBuilder()->getTypeKind()); } @@ -4450,7 +4473,7 @@ struct DeclLoweringVisitor : DeclVisitor<DeclLoweringVisitor, LoweredValInfo> auto type = lowerType(subContext, decl->type.type); - return LoweredValInfo::simple(finishOuterGenerics(subBuilder, type)); + return LoweredValInfo::simple(finishOuterGenerics(subBuilder, type, outerGeneric)); } LoweredValInfo visitGenericTypeParamDecl(GenericTypeParamDecl* /*decl*/) @@ -4660,7 +4683,7 @@ struct DeclLoweringVisitor : DeclVisitor<DeclLoweringVisitor, LoweredValInfo> NestedContext nested(this); auto subBuilder = nested.getBuilder(); auto subContext = nested.getContext(); - emitOuterGenerics(subContext, inheritanceDecl, inheritanceDecl); + auto outerGeneric = emitOuterGenerics(subContext, inheritanceDecl, inheritanceDecl); // Lower the super-type to force its declaration to be lowered. // @@ -4689,7 +4712,7 @@ struct DeclLoweringVisitor : DeclVisitor<DeclLoweringVisitor, LoweredValInfo> irWitnessTable->moveToEnd(); - return LoweredValInfo::simple(finishOuterGenerics(subBuilder, irWitnessTable)); + return LoweredValInfo::simple(finishOuterGenerics(subBuilder, irWitnessTable, outerGeneric)); } LoweredValInfo visitDeclGroup(DeclGroup* declGroup) @@ -5054,6 +5077,8 @@ struct DeclLoweringVisitor : DeclVisitor<DeclLoweringVisitor, LoweredValInfo> subContextStorage.irBuilder = &subBuilderStorage; subContextStorage.env = &subEnvStorage; + + subContextStorage.thisType = outerContext->thisType; } IRBuilder* getBuilder() { return &subBuilderStorage; } @@ -5090,7 +5115,7 @@ struct DeclLoweringVisitor : DeclVisitor<DeclLoweringVisitor, LoweredValInfo> auto subBuilder = nestedContext.getBuilder(); auto subContext = nestedContext.getContext(); subBuilder->setInsertInto(subBuilder->getModule()->getModuleInst()); - emitOuterGenerics(subContext, decl, decl); + auto outerGeneric = emitOuterGenerics(subContext, decl, decl); IRType* subVarType = lowerType(subContext, decl->getType()); @@ -5191,7 +5216,7 @@ struct DeclLoweringVisitor : DeclVisitor<DeclLoweringVisitor, LoweredValInfo> } irGlobal->moveToEnd(); - finishOuterGenerics(subBuilder, irGlobal); + finishOuterGenerics(subBuilder, irGlobal, outerGeneric); return globalVal; } @@ -5275,34 +5300,95 @@ struct DeclLoweringVisitor : DeclVisitor<DeclLoweringVisitor, LoweredValInfo> // a witness table for the interface type's conformance // to its own interface. // - List<IRStructKey*> requirementKeys; - for (auto requirementDecl : decl->members) + NestedContext nestedContext(this); + auto subBuilder = nestedContext.getBuilder(); + auto subContext = nestedContext.getContext(); + + // Emit any generics that should wrap the actual type. + auto outerGeneric = emitOuterGenerics(subContext, decl, decl); + + // Setup subContext for proper lowering `ThisType`, associated types and + // the interface decl's self reference. + subContext->thisType = getBuilder()->getThisType(); + + for (auto member : decl->members) { - requirementKeys.add(getInterfaceRequirementKey(requirementDecl)); + if (as<AssocTypeDecl>(member)) + { + subContext->env->mapDeclToValue[member] = getBuilder()->getAssociatedType(); + } + } + // First, compute the number of requirement entries that will be included in this + // interface type. + UInt operandCount = 0; + for (auto requirementDecl : decl->members) + { + operandCount++; // As a special case, any type constraints placed // on an associated type will *also* need to be turned // into requirement keys for this interface. if (auto associatedTypeDecl = as<AssocTypeDecl>(requirementDecl)) { - for (auto constraintDecl : associatedTypeDecl->getMembersOfType<TypeConstraintDecl>()) - { - requirementKeys.add(getInterfaceRequirementKey(constraintDecl)); - } + operandCount += associatedTypeDecl->getMembersOfType<TypeConstraintDecl>().getCount(); } } + // Allocate an IRInterfaceType with the `operandCount` operands. + IRInterfaceType* irInterface = subBuilder->createInterfaceType(operandCount, nullptr); - NestedContext nestedContext(this); - auto subBuilder = nestedContext.getBuilder(); - auto subContext = nestedContext.getContext(); + // Add `irInterface` to decl mapping now to prevent cyclic lowering. + setValue(subContext, decl, LoweredValInfo::simple(irInterface)); - // Emit any generics that should wrap the actual type. - emitOuterGenerics(subContext, decl, decl); + UInt entryIndex = 0; - IRInterfaceType* irInterface = subBuilder->createInterfaceType( - requirementKeys.getCount(), - reinterpret_cast<IRInst**>(requirementKeys.getBuffer())); + for (auto requirementDecl : decl->members) + { + auto entry = subBuilder->createInterfaceRequirementEntry( + getInterfaceRequirementKey(requirementDecl), + nullptr); + IRInst* requirementVal = lowerDecl(subContext, requirementDecl).val; + if (requirementVal) + { + auto reqType = requirementVal->getFullType(); + entry->setRequirementVal(reqType); + if (!requirementVal->hasUses()) + { + // Remove lowered `IRFunc`s since we only care about + // function types. + switch (requirementVal->op) + { + case kIROp_Func: + case kIROp_Generic: + requirementVal->removeAndDeallocate(); + break; + default: + break; + } + } + } + irInterface->setOperand(entryIndex, entry); + entryIndex++; + // Add addtional requirements for type constraints placed + // on an associated types. + if (auto associatedTypeDecl = as<AssocTypeDecl>(requirementDecl)) + { + for (auto constraintDecl : associatedTypeDecl->getMembersOfType<TypeConstraintDecl>()) + { + auto constraintKey = getInterfaceRequirementKey(constraintDecl); + irInterface->setOperand(entryIndex, + subBuilder->createInterfaceRequirementEntry(constraintKey, + getBuilder()->getWitnessTableType(lowerType(context, constraintDecl->getSup().type)))); + entryIndex++; + } + } + // Add lowered requirement entry to current decl mapping to prevent + // the function requirements from being lowered again when we get to + // `ensureAllDeclsRec`. + setValue(context, requirementDecl, LoweredValInfo::simple(entry)); + } + + addNameHint(context, irInterface, decl); addLinkageDecoration(context, irInterface, decl); subBuilder->setInsertInto(irInterface); @@ -5313,8 +5399,8 @@ struct DeclLoweringVisitor : DeclVisitor<DeclLoweringVisitor, LoweredValInfo> addTargetIntrinsicDecorations(irInterface, decl); - - return LoweredValInfo::simple(finishOuterGenerics(subBuilder, irInterface)); + auto finalVal = finishOuterGenerics(subBuilder, irInterface, outerGeneric); + return LoweredValInfo::simple(finalVal); } LoweredValInfo visitEnumCaseDecl(EnumCaseDecl* decl) @@ -5348,7 +5434,7 @@ struct DeclLoweringVisitor : DeclVisitor<DeclLoweringVisitor, LoweredValInfo> NestedContext nestedContext(this); auto subBuilder = nestedContext.getBuilder(); auto subContext = nestedContext.getContext(); - emitOuterGenerics(subContext, decl, decl); + auto outerGeneric = emitOuterGenerics(subContext, decl, decl); // An `enum` declaration will currently lower directly to its "tag" // type, so that any references to the `enum` become referenes to @@ -5360,7 +5446,7 @@ struct DeclLoweringVisitor : DeclVisitor<DeclLoweringVisitor, LoweredValInfo> IRType* loweredTagType = lowerType(subContext, decl->tagType); - return LoweredValInfo::simple(finishOuterGenerics(subBuilder, loweredTagType)); + return LoweredValInfo::simple(finishOuterGenerics(subBuilder, loweredTagType, outerGeneric)); } LoweredValInfo visitAggTypeDecl(AggTypeDecl* decl) @@ -5371,6 +5457,11 @@ struct DeclLoweringVisitor : DeclVisitor<DeclLoweringVisitor, LoweredValInfo> return LoweredValInfo(); } + if (as<AssocTypeDecl>(decl)) + { + return LoweredValInfo::simple(getBuilder()->getAssociatedType()); + } + // Given a declaration of a type, we need to make sure // to output "witness tables" for any interfaces this // type has declared conformance to. @@ -5387,12 +5478,12 @@ struct DeclLoweringVisitor : DeclVisitor<DeclLoweringVisitor, LoweredValInfo> auto subContext = nestedContext.getContext(); // Emit any generics that should wrap the actual type. - emitOuterGenerics(subContext, decl, decl); + auto outerGeneric = emitOuterGenerics(subContext, decl, decl); + IRStructType* irStruct = subBuilder->createStructType(); addNameHint(context, irStruct, decl); addLinkageDecoration(context, irStruct, decl); - subBuilder->setInsertInto(irStruct); // A `struct` that inherits from another `struct` must start @@ -5458,7 +5549,7 @@ struct DeclLoweringVisitor : DeclVisitor<DeclLoweringVisitor, LoweredValInfo> irStruct->moveToEnd(); addTargetIntrinsicDecorations(irStruct, decl); - return LoweredValInfo::simple(finishOuterGenerics(subBuilder, irStruct)); + return LoweredValInfo::simple(finishOuterGenerics(subBuilder, irStruct, outerGeneric)); } LoweredValInfo lowerMemberVarDecl(VarDecl* fieldDecl) @@ -5787,24 +5878,25 @@ struct DeclLoweringVisitor : DeclVisitor<DeclLoweringVisitor, LoweredValInfo> // IRInst* finishOuterGenerics( IRBuilder* subBuilder, - IRInst* val) + IRInst* val, + IRGeneric* parentGeneric) { IRInst* v = val; - for(;;) + while (parentGeneric) { - auto parentBlock = as<IRBlock>(v->getParent()); - if (!parentBlock) break; - - auto parentGeneric = as<IRGeneric>(parentBlock->getParent()); - if (!parentGeneric) break; - - subBuilder->setInsertInto(parentBlock); + subBuilder->setInsertInto(parentGeneric->getFirstBlock()); subBuilder->emitReturn(v); parentGeneric->moveToEnd(); // There might be more outer generics, // so we need to loop until we run out. v = parentGeneric; + auto parentBlock = as<IRBlock>(v->getParent()); + if (!parentBlock) break; + + parentGeneric = as<IRGeneric>(parentBlock->getParent()); + if (!parentGeneric) break; + } return v; } @@ -5995,29 +6087,17 @@ struct DeclLoweringVisitor : DeclVisitor<DeclLoweringVisitor, LoweredValInfo> return as<IRStringLit>(builder->getStringValue(stringLitExpr->value.getUnownedSlice())); } - LoweredValInfo lowerFuncDecl(FunctionDeclBase* decl) + void _lowerFuncResultAndParameterTypes( + ParameterLists& parameterLists, + List<IRType*>& paramTypes, + IRType*& irResultType, + IRBuilder* subBuilder, + IRGenContext* subContext, + FunctionDeclBase* decl) { - // We are going to use a nested builder, because we will - // change the parent node that things get nested into. - // - NestedContext nestedContext(this); - auto subBuilder = nestedContext.getBuilder(); - auto subContext = nestedContext.getContext(); - - // The actual `IRFunction` that we emit needs to be nested - // inside of one `IRGeneric` for every outer `GenericDecl` - // in the declaration hierarchy. - - emitOuterGenerics(subContext, decl, decl); - // Collect the parameter lists we will use for our new function. - ParameterLists parameterLists; collectParameterLists(decl, ¶meterLists, kParameterListCollectMode_Default); - // TODO: if there are any generic parameters in the collected list, then - // we need to output an IR function with generic parameters (or a generic - // with a nested function... the exact representation is still TBD). - // In most cases the return type for a declaration can be read off the declaration // itself, but things get a bit more complicated when we have to deal with // accessors for subscript declarations (and eventually for properties). @@ -6036,14 +6116,6 @@ struct DeclLoweringVisitor : DeclVisitor<DeclLoweringVisitor, LoweredValInfo> } } - // need to create an IR function here - - IRFunc* irFunc = subBuilder->createFunc(); - addNameHint(context, irFunc, decl); - addLinkageDecoration(context, irFunc, decl); - - List<IRType*> paramTypes; - for( auto paramInfo : parameterLists.params ) { IRType* irParamType = lowerType(subContext, paramInfo.type); @@ -6084,7 +6156,7 @@ struct DeclLoweringVisitor : DeclVisitor<DeclLoweringVisitor, LoweredValInfo> paramTypes.add(irParamType); } - auto irResultType = lowerType(subContext, declForReturnType->returnType); + irResultType = lowerType(subContext, declForReturnType->returnType); if (auto setterDecl = as<SetterDecl>(decl)) { @@ -6107,11 +6179,81 @@ struct DeclLoweringVisitor : DeclVisitor<DeclLoweringVisitor, LoweredValInfo> // being accessed, rather than a simple value. irResultType = subBuilder->getPtrType(irResultType); } + } + + IRFuncType* _lowerFuncTypeImpl( + ParameterLists& parameterLists, + List<IRType*>& paramTypes, + IRType*& irResultType, + IRBuilder* builder, + IRGenContext* irGenContext, + FunctionDeclBase* decl) + { + _lowerFuncResultAndParameterTypes( + parameterLists, + paramTypes, + irResultType, + builder, + irGenContext, + decl); - auto irFuncType = subBuilder->getFuncType( + auto irFuncType = builder->getFuncType( paramTypes.getCount(), paramTypes.getBuffer(), irResultType); + + return irFuncType; + } + + IRInst* lowerFuncType(FunctionDeclBase* decl) + { + NestedContext nestedContextFuncType(this); + auto funcTypeBuilder = nestedContextFuncType.getBuilder(); + auto funcTypeContext = nestedContextFuncType.getContext(); + + auto outerGenerics = emitOuterGenerics(funcTypeContext, decl, decl); + + ParameterLists parameterLists; + List<IRType*> paramTypes; + IRType* irResultType = nullptr; + auto irFuncType = _lowerFuncTypeImpl( + parameterLists, + paramTypes, + irResultType, + funcTypeBuilder, + funcTypeContext, + decl); + + return finishOuterGenerics(funcTypeBuilder, irFuncType, outerGenerics); + } + + LoweredValInfo lowerFuncDecl(FunctionDeclBase* decl) + { + // We are going to use a nested builder, because we will + // change the parent node that things get nested into. + // + NestedContext nestedContextFunc(this); + auto subBuilder = nestedContextFunc.getBuilder(); + auto subContext = nestedContextFunc.getContext(); + + auto outerGeneric = emitOuterGenerics(subContext, decl, decl); + + // need to create an IR function here + + IRFunc* irFunc = subBuilder->createFunc(); + addNameHint(context, irFunc, decl); + addLinkageDecoration(context, irFunc, decl); + + ParameterLists parameterLists; + List<IRType*> paramTypes; + IRType* irResultType = nullptr; + auto irFuncType = _lowerFuncTypeImpl( + parameterLists, + paramTypes, + irResultType, + subBuilder, + subContext, + decl); irFunc->setFullType(irFuncType); subBuilder->setInsertInto(irFunc); @@ -6251,14 +6393,6 @@ struct DeclLoweringVisitor : DeclVisitor<DeclLoweringVisitor, LoweredValInfo> if (paramInfo.isThisParam) { subContext->thisVal = paramVal; - subBuilder->addThisPointerDecoration(irParam); - } - - // Add a [polymorphic] decoration for generic-typed parameters. - if (as<IRParam>(irParamType) && - as<IRTypeType>(irParamType->getFullType())) - { - subBuilder->addPolymorphicDecoration(irParam); } } @@ -6470,7 +6604,17 @@ struct DeclLoweringVisitor : DeclVisitor<DeclLoweringVisitor, LoweredValInfo> // body appear before the function itself in the list // of global values. irFunc->moveToEnd(); - return LoweredValInfo::simple(finishOuterGenerics(subBuilder, irFunc)); + + // If this function is defined inside an interface, add a reference to the IRFunc from + // the interface's type definition. + auto finalVal = finishOuterGenerics(subBuilder, irFunc, outerGeneric); + if (auto genericVal = as<IRGeneric>(finalVal)) + { + auto funcType = lowerFuncType(decl); + genericVal->setFullType((IRType*)funcType); + } + + return LoweredValInfo::simple(finalVal); } LoweredValInfo visitGenericDecl(GenericDecl * genDecl) |
