From 0ca75fe002f346f6ab9b77f40c0576d2905560f1 Mon Sep 17 00:00:00 2001 From: Yong He Date: Wed, 24 Jun 2020 13:16:11 -0700 Subject: Dynamic dispatch for generic interface requirements. -Lower interfaces into actual `IRInterfaceType` insts. -Lower `DeclRef` into `IRAssociatedType` -Generate proper IRType for generic functions. -Add a test case exercising dynamic dispatching a generic static function through an associated type. -Bug fixes for the test case. --- source/slang/slang-emit-c-like.cpp | 21 +- source/slang/slang-emit-cpp.cpp | 64 +++--- source/slang/slang-emit-cpp.h | 2 +- source/slang/slang-ir-inst-defs.h | 14 +- source/slang/slang-ir-insts.h | 25 ++- source/slang/slang-ir-link.cpp | 5 +- source/slang/slang-ir-lower-generics.cpp | 181 ++++++++++++++++- source/slang/slang-ir.cpp | 32 ++- source/slang/slang-ir.h | 19 ++ source/slang/slang-lower-to-ir.cpp | 336 +++++++++++++++++++++---------- 10 files changed, 529 insertions(+), 170 deletions(-) (limited to 'source') diff --git a/source/slang/slang-emit-c-like.cpp b/source/slang/slang-emit-c-like.cpp index 3438fd3f4..2605723c7 100644 --- a/source/slang/slang-emit-c-like.cpp +++ b/source/slang/slang-emit-c-like.cpp @@ -236,9 +236,9 @@ List 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(interfaceType->getOperand(i)); + auto reqEntry = cast(interfaceType->getOperand(i)); IRWitnessTableEntry* entry = nullptr; - if (witnessTableEntryDictionary.TryGetValue(reqKey, entry)) + if (witnessTableEntryDictionary.TryGetValue(reqEntry->getRequirementKey(), entry)) { sortedWitnessTableEntries.add(entry); } @@ -1962,6 +1962,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: @@ -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, @@ -3620,8 +3633,8 @@ void CLikeSourceEmitter::ensureInstOperandsRec(ComputeEmitActionsContext* ctx, I // only need the type they point to to be forward-declared. // 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()) diff --git a/source/slang/slang-emit-cpp.cpp b/source/slang/slang-emit-cpp.cpp index 4a59f4cf9..eeace4aa7 100644 --- a/source/slang/slang-emit-cpp.cpp +++ b/source/slang/slang-emit-cpp.cpp @@ -390,12 +390,27 @@ static UnownedStringSlice _getResourceTypePrefix(IROp op) } } +static bool isVoidPtrType(IRType* type) +{ + auto ptrType = as(type); + if (!ptrType) return false; + return ptrType->getValueType()->op == kIROp_VoidType; +} + SlangResult CPPSourceEmitter::calcTypeName(IRType* type, CodeGenTarget target, StringBuilder& out) { switch (type->op) { case kIROp_PtrType: { + if (isVoidPtrType(type)) + { + // A `void*` type will always emit as `void*`. + // `void*` types are generated as a result of generics lowering + // for dynamic dispatch. + out << "void*"; + return SLANG_OK; + } auto ptrType = static_cast(type); SLANG_RETURN_ON_FAIL(calcTypeName(ptrType->getValueType(), target, out)); // TODO(JS): It seems although it says it is a pointer, it can actually be output as a reference @@ -494,7 +509,7 @@ SlangResult CPPSourceEmitter::calcTypeName(IRType* type, CodeGenTarget target, S // struct of function pointers corresponding to the interface type. auto witnessTableType = static_cast(type); auto baseType = cast(witnessTableType->getOperand(0)); - emitType(baseType); + SLANG_RETURN_ON_FAIL(calcTypeName(baseType, target, out)); out << "*"; return SLANG_OK; } @@ -1591,8 +1606,7 @@ void CPPSourceEmitter::emitWitnessTable(IRWitnessTable* witnessTable) { auto interfaceType = cast(witnessTable->getOperand(0)); auto witnessTableItems = witnessTable->getChildren(); - List sortedWitnessTableEntries = getSortedWitnessTableEntries(witnessTable); - _maybeEmitWitnessTableTypeDefinition(interfaceType, sortedWitnessTableEntries); + _maybeEmitWitnessTableTypeDefinition(interfaceType); // Define a global variable for the witness table. m_writer->emit("extern "); @@ -1747,17 +1761,16 @@ void CPPSourceEmitter::emitInterface(IRInterfaceType* interfaceType) /// acoording to the order defined by `interfaceType`. /// void CPPSourceEmitter::_maybeEmitWitnessTableTypeDefinition( - IRInterfaceType* interfaceType, - const List& 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(entry->satisfyingVal.get())) + auto entry = as(interfaceType->getOperand(i)); + if (auto funcVal = as(entry->getRequirementVal())) { emitType(funcVal->getResultType()); m_writer->emit(" (KernelContext::*"); @@ -1765,33 +1778,35 @@ void CPPSourceEmitter::_maybeEmitWitnessTableTypeDefinition( 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(paramType)) + continue; + if (!isFirstParam) m_writer->emit(", "); else isFirstParam = false; - if (param->findDecoration()) + auto thisDecor = funcVal->findDecoration(); + if (thisDecor && cast(thisDecor->getOperand(0))->value.intVal == (IRIntegerValue)p) { - m_writer->emit("void* "); - m_writer->emit(getName(param)); + m_writer->emit("void* param"); + m_writer->emit(p); continue; } - emitSimpleFuncParamImpl(param); + emitParamType(paramType, String("param") + String(p)); } m_writer->emit(");\n"); } - else if (auto witnessTableVal = as(entry->getSatisfyingVal())) + else if (auto constraintInterfaceType = as(entry->getRequirementVal())) { - emitType(as(witnessTableVal->getOperand(0))); + emitType(constraintInterfaceType); m_writer->emit("* "); m_writer->emit(getName(entry->requirementKey.get())); m_writer->emit(";\n"); } - else - { - // TODO: handle other witness table entry types. - } } m_writer->dedent(); m_writer->emit("};\n"); @@ -1990,13 +2005,6 @@ void CPPSourceEmitter::emitSimpleValueImpl(IRInst* inst) } } -static bool isVoidPtrType(IRType* type) -{ - auto ptrType = as(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 @@ -2004,9 +2012,7 @@ void CPPSourceEmitter::emitSimpleFuncParamImpl(IRParam* param) // 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() && - isVoidPtrType(param->getDataType())) + if (isVoidPtrType(param->getDataType())) { m_writer->emit("void* "); m_writer->emit(getName(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& sortedWitnessTableEntries); + void _maybeEmitWitnessTableTypeDefinition(IRInterfaceType* interfaceType); void _maybeEmitSpecializedOperationDefinition(const HLSLIntrinsic* specOp); void _emitForwardDeclarations(const List& actions); diff --git a/source/slang/slang-ir-inst-defs.h b/source/slang/slang-ir-inst-defs.h index 58ff1a79f..e9bc23993 100644 --- a/source/slang/slang-ir-inst-defs.h +++ b/source/slang/slang-ir-inst-defs.h @@ -164,7 +164,8 @@ 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) // A TypeType-typed IRValue represents a IRType. // It is used to represent a type parameter/argument in a generics. @@ -223,6 +224,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,14 +509,12 @@ 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) + /// `[this_ptr]` decoration is also used to mark an `IRFunc` as a non-static function. + /// The argument is an integer value that represents the index of the `this` parameter, + /// which is always 0. + INST(ThisPointerDecoration, this_ptr, 1, 0) /// A `[format(f)]` decoration specifies that the format of an image should be `f` diff --git a/source/slang/slang-ir-insts.h b/source/slang/slang-ir-insts.h index b13d52981..fb0cc57c7 100644 --- a/source/slang/slang-ir-insts.h +++ b/source/slang/slang-ir-insts.h @@ -165,8 +165,6 @@ IR_SIMPLE_DECORATION(VulkanCallablePayloadDecoration) /// vulkan hit attributes, and should have a location assigned /// to it. IR_SIMPLE_DECORATION(VulkanHitAttributesDecoration) - -IR_SIMPLE_DECORATION(PolymorphicDecoration) IR_SIMPLE_DECORATION(ThisPointerDecoration) @@ -410,9 +408,13 @@ struct IRLookupWitnessMethod : IRInst { IRUse witnessTable; IRUse requirementKey; + IRUse interfaceType; IRInst* getWitnessTable() { return witnessTable.get(); } IRInst* getRequirementKey() { return requirementKey.get(); } + IRInst* getInterfaceType() { return interfaceType.get(); } + + IR_LEAF_ISA(lookup_interface_method) }; struct IRLookupWitnessTable : IRInst @@ -1675,7 +1677,8 @@ struct IRBuilder IRInst* emitLookupInterfaceMethodInst( IRType* type, IRInst* witnessTableVal, - IRInst* interfaceMethodVal); + IRInst* interfaceMethodVal, + IRType* interfaceType); IRInst* emitCallInst( IRType* type, @@ -1809,9 +1812,16 @@ struct IRBuilder IRInst* requirementKey, IRInst* satisfyingVal); + IRInterfaceRequirementEntry* createInterfaceRequirementEntry( + IRInst* requirementKey, + IRInst* requirementVal); + // Create an initially empty `struct` type. IRStructType* createStructType(); + // Create an IRType representing an `associatedtype` decl. + IRAssociatedType* createAssociatedType(); + // Create an empty `interface` type. IRInterfaceType* createInterfaceType(UInt operandCount, IRInst* const* operands); @@ -2160,14 +2170,9 @@ struct IRBuilder addDecoration(value, kIROp_LoopControlDecoration, getIntValue(getIntType(), IRIntegerValue(mode))); } - void addPolymorphicDecoration(IRInst* value) - { - addDecoration(value, kIROp_PolymorphicDecoration); - } - - void addThisPointerDecoration(IRInst* value) + void addThisPointerDecoration(IRInst* value, int paramIndex) { - addDecoration(value, kIROp_ThisPointerDecoration); + addDecoration(value, kIROp_ThisPointerDecoration, getIntValue(getIntType(), paramIndex)); } void addSemanticDecoration(IRInst* value, UnownedStringSlice const& text, int index = 0) diff --git a/source/slang/slang-ir-link.cpp b/source/slang/slang-ir-link.cpp index 3f51aa876..4e6ad74a4 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. // diff --git a/source/slang/slang-ir-lower-generics.cpp b/source/slang/slang-ir-lower-generics.cpp index f6340a633..fe0fa3364 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 loweredGenericFunctions; + HashSet loweredInterfaceTypes; SharedIRBuilder sharedBuilderStorage; @@ -45,6 +46,20 @@ namespace Slang workListSet.Add(inst); } + bool isPolymorphicType(IRInst* typeInst) + { + if (as(typeInst) && as(typeInst->getFullType())) + return true; + switch (typeInst->op) + { + case kIROp_AssociatedType: + case kIROp_InterfaceType: + return true; + default: + return false; + } + } + IRInst* lowerGenericFunction(IRInst* genericValue) { IRInst* result = nullptr; @@ -64,6 +79,7 @@ namespace Slang builder.sharedBuilder = &sharedBuilderStorage; builder.setInsertBefore(genericParent); auto loweredFunc = cloneInstAndOperands(&cloneEnv, &builder, func); + loweredFunc->setFullType(lowerGenericFuncType(&builder, cast(genericParent->typeUse.get()))); List clonedParams; for (auto genericParam : genericParent->getParams()) { @@ -82,7 +98,7 @@ namespace Slang // Turn generic parameters into void pointers. for (auto param : cast(loweredFunc)->getParams()) { - if (param->findDecoration()) + if (isPolymorphicType(param->getFullType())) { param->setFullType(builder.getPtrType(builder.getVoidType())); } @@ -91,6 +107,106 @@ namespace Slang return loweredFunc; } + IRType* lowerGenericFuncType(IRBuilder* builder, IRGeneric* genericVal) + { + List genericParamTypes; + for (auto genericParam : genericVal->getParams()) + { + if (isPolymorphicType(genericParam->getFullType())) + { + genericParamTypes.add(builder->getPtrType(builder->getVoidType())); + } + else + { + genericParamTypes.add(genericParam->getFullType()); + } + } + + auto innerType = (IRFuncType*)lowerFuncType( + builder, + cast(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 newOperands; + bool translated = false; + for (UInt i = 0; i < funcType->getOperandCount(); i++) + { + auto paramType = funcType->getOperand(i); + if (isPolymorphicType(paramType)) + { + newOperands.add(builder->getPtrType(builder->getVoidType())); + 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 into Array. + newOperands.add(builder->getPtrType(builder->getVoidType())); + translated = true; + } + else + { + newOperands.add(paramType); + } + } + if (!translated) + 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(interfaceType->getOperand(i))) + { + if (auto funcType = as(entry->getRequirementVal())) + { + entry->requirementVal.set(lowerFuncType(&builder, funcType)); + } + else if (auto genericFuncType = as(entry->getRequirementVal())) + { + entry->requirementVal.set(lowerGenericFuncType(&builder, genericFuncType)); + } + } + } + + loweredInterfaceTypes.Add(interfaceType); + return interfaceType; + } + void processInst(IRInst* inst) { if (auto callInst = as(inst)) @@ -98,25 +214,53 @@ 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(funcOperand)) { - auto loweredFunc = lowerGenericFunction(specializeInst->getOperand(0)); - if (loweredFunc == specializeInst->getOperand(0)) + auto funcToSpecialize = specializeInst->getOperand(0); + List paramTypes; + if (auto interfaceLookup = as(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 interfaceType = maybeLowerInterfaceType(cast(interfaceLookup->getInterfaceType())); + for (UInt i = 0; i < interfaceType->getOperandCount(); i++) + { + auto entry = cast(interfaceType->getOperand(i)); + if (entry->getRequirementKey() == interfaceLookup->getOperand(1)) + { + callee = entry->getRequirementVal(); + break; + } + } + auto funcType = cast(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(loweredFunc)->getParams()) + paramTypes.add(param->getDataType()); } + IRBuilder builderStorage; auto builder = &builderStorage; builder->sharedBuilder = &sharedBuilderStorage; builder->setInsertBefore(inst); List args; - auto pp = as(loweredFunc)->getParams().begin(); auto voidPtrType = builder->getPtrType(builder->getVoidType()); for (UInt i = 0; i < callInst->getArgCount(); i++) { auto arg = callInst->getArg(i); - if ((*pp)->getDataType() == voidPtrType && + if (paramTypes[i] == voidPtrType && arg->getDataType() != voidPtrType) { // We are calling a generic function that with an argument of @@ -132,7 +276,6 @@ namespace Slang arg); } args.add(arg); - ++pp; } for (UInt i = 0; i < specializeInst->getArgCount(); i++) args.add(specializeInst->getArg(i)); @@ -141,6 +284,28 @@ namespace Slang callInst->removeAndDeallocate(); } } + else if (auto witnessTable = as(inst)) + { + // Lower generic functions in witness table. + for (auto child : witnessTable->getChildren()) + { + auto entry = as(child); + if (!entry) + continue; + if (auto genericVal = as(entry->getSatisfyingVal())) + { + if (findGenericReturnVal(genericVal)->op == kIROp_Func) + { + auto loweredFunc = lowerGenericFunction(genericVal); + entry->satisfyingVal.set(loweredFunc); + } + } + } + } + else if (auto interfaceType = as(inst)) + { + maybeLowerInterfaceType(interfaceType); + } } void processModule() diff --git a/source/slang/slang-ir.cpp b/source/slang/slang-ir.cpp index 77011b569..891f4b3e0 100644 --- a/source/slang/slang-ir.cpp +++ b/source/slang/slang-ir.cpp @@ -2508,14 +2508,16 @@ namespace Slang IRInst* IRBuilder::emitLookupInterfaceMethodInst( IRType* type, IRInst* witnessTableVal, - IRInst* interfaceMethodVal) + IRInst* interfaceMethodVal, + IRType* interfaceType) { + IRInst* args[3] = { witnessTableVal , interfaceMethodVal, interfaceType }; auto inst = createInst( this, kIROp_lookup_interface_method, type, - witnessTableVal, - interfaceMethodVal); + 3, + args); addInst(inst); return inst; @@ -2811,6 +2813,20 @@ namespace Slang return entry; } + IRInterfaceRequirementEntry* IRBuilder::createInterfaceRequirementEntry( + IRInst* requirementKey, + IRInst* requirementVal) + { + IRInterfaceRequirementEntry* entry = createInst( + this, + kIROp_InterfaceRequirementEntry, + nullptr, + requirementKey, + requirementVal); + addGlobalValue(this, entry); + return entry; + } + IRStructType* IRBuilder::createStructType() { IRStructType* structType = createInst( @@ -2821,6 +2837,16 @@ namespace Slang return structType; } + IRAssociatedType* IRBuilder::createAssociatedType() + { + IRAssociatedType* associatedType = createInst( + this, + kIROp_AssociatedType, + nullptr); + addGlobalValue(this, associatedType); + return associatedType; + } + IRInterfaceType* IRBuilder::createInterfaceType(UInt operandCount, IRInst* const* operands) { IRInterfaceType* interfaceType = createInst( diff --git a/source/slang/slang-ir.h b/source/slang/slang-ir.h index 3c9a15650..b41c94e7f 100644 --- a/source/slang/slang-ir.h +++ b/source/slang/slang-ir.h @@ -1190,6 +1190,25 @@ struct IRStructType : IRType IR_LEAF_ISA(StructType) }; +struct IRAssociatedType : IRType +{ + IR_LEAF_ISA(AssociatedType) +}; + +struct IRInterfaceRequirementEntry : IRInst +{ + // The AST-level requirement + IRUse requirementKey; + + // The IR-level value that represents the declaration of the requirement + IRUse requirementVal; + + IRInst* getRequirementKey() { return getOperand(0); } + IRInst* getRequirementVal() { return getOperand(1); } + + IR_LEAF_ISA(InterfaceRequirementEntry); +}; + struct IRInterfaceType : IRType { IR_LEAF_ISA(InterfaceType) diff --git a/source/slang/slang-lower-to-ir.cpp b/source/slang/slang-lower-to-ir.cpp index ea04ea85c..ff356fd48 100644 --- a/source/slang/slang-lower-to-ir.cpp +++ b/source/slang/slang-lower-to-ir.cpp @@ -401,6 +401,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 +998,8 @@ IRStructKey* getInterfaceRequirementKey( IRGenContext* context, Decl* requirementDecl) { + if (auto genericDecl = as(requirementDecl)) + return getInterfaceRequirementKey(context, genericDecl->inner); IRStructKey* requirementKey = nullptr; if(context->shared->interfaceRequirementKeys.TryGetValue(requirementDecl, requirementKey)) { @@ -1059,7 +1073,8 @@ struct ValLoweringVisitor : ValVisitoremitLookupInterfaceMethodInst( nullptr, baseWitnessTable, - requirementKey)); + requirementKey, + lowerType(context, val->subToMid->sup))); } LoweredValInfo visitTaggedUnionSubtypeWitness( @@ -1240,7 +1255,8 @@ struct ValLoweringVisitor : ValVisitoremitLookupInterfaceMethodInst( caseFuncType, caseWitnessTable, - irReqKey); + irReqKey, + irWitnessTableBaseType); // We are going to emit a `call` to the satisfying value // for the case type, so we will collect the arguments for that call. @@ -4520,7 +4536,7 @@ struct DeclLoweringVisitor : DeclVisitor Dictionary mapASTToIRWitnessTable) { auto subBuilder = subContext->irBuilder; - + for(auto entry : astWitnessTable->requirementDictionary) { auto requiredMemberDecl = entry.Key; @@ -5275,11 +5291,16 @@ struct DeclLoweringVisitor : DeclVisitor // a witness table for the interface type's conformance // to its own interface. // - List requirementKeys; + NestedContext nestedContext(this); + auto subBuilder = nestedContext.getBuilder(); + auto subContext = nestedContext.getContext(); + List requirementEntries; + for (auto requirementDecl : decl->members) { - requirementKeys.add(getInterfaceRequirementKey(requirementDecl)); - + auto key = getInterfaceRequirementKey(requirementDecl); + auto entry = subBuilder->createInterfaceRequirementEntry(key, nullptr); + requirementEntries.add(entry); // As a special case, any type constraints placed // on an associated type will *also* need to be turned // into requirement keys for this interface. @@ -5287,22 +5308,20 @@ struct DeclLoweringVisitor : DeclVisitor { for (auto constraintDecl : associatedTypeDecl->getMembersOfType()) { - requirementKeys.add(getInterfaceRequirementKey(constraintDecl)); + auto constraintKey = getInterfaceRequirementKey(constraintDecl); + requirementEntries.add( + subBuilder->createInterfaceRequirementEntry(constraintKey, + lowerType(context, constraintDecl->getSup().type))); } } } - - NestedContext nestedContext(this); - auto subBuilder = nestedContext.getBuilder(); - auto subContext = nestedContext.getContext(); - // Emit any generics that should wrap the actual type. emitOuterGenerics(subContext, decl, decl); IRInterfaceType* irInterface = subBuilder->createInterfaceType( - requirementKeys.getCount(), - reinterpret_cast(requirementKeys.getBuffer())); + requirementEntries.getCount(), + reinterpret_cast(requirementEntries.getBuffer())); addNameHint(context, irInterface, decl); addLinkageDecoration(context, irInterface, decl); subBuilder->setInsertInto(irInterface); @@ -5389,63 +5408,76 @@ struct DeclLoweringVisitor : DeclVisitor // Emit any generics that should wrap the actual type. emitOuterGenerics(subContext, decl, decl); - IRStructType* irStruct = subBuilder->createStructType(); - addNameHint(context, irStruct, decl); - addLinkageDecoration(context, irStruct, decl); + IRInst* resultType = nullptr; + if (as(decl)) + { + resultType = subBuilder->createAssociatedType(); + } + else + { + resultType = subBuilder->createStructType(); + } - subBuilder->setInsertInto(irStruct); + addNameHint(context, resultType, decl); + addLinkageDecoration(context, resultType, decl); - // A `struct` that inherits from another `struct` must start - // with a member for the direct base type. - // - for( auto inheritanceDecl : decl->getMembersOfType() ) + if (resultType->op == kIROp_StructType) { - auto superType = inheritanceDecl->base; - if(auto superDeclRefType = as(superType)) + IRStructType* irStruct = (IRStructType*)resultType; + subBuilder->setInsertInto(irStruct); + + // A `struct` that inherits from another `struct` must start + // with a member for the direct base type. + // + for( auto inheritanceDecl : decl->getMembersOfType() ) { - if(auto superStructDeclRef = superDeclRefType->declRef.as()) + auto superType = inheritanceDecl->base; + if(auto superDeclRefType = as(superType)) { - auto superKey = (IRStructKey*) getSimpleVal(context, ensureDecl(context, inheritanceDecl)); - auto irSuperType = lowerType(context, superType.type); - subBuilder->createStructField( - irStruct, - superKey, - irSuperType); + if(auto superStructDeclRef = superDeclRefType->declRef.as()) + { + auto superKey = (IRStructKey*) getSimpleVal(context, ensureDecl(context, inheritanceDecl)); + auto irSuperType = lowerType(context, superType.type); + subBuilder->createStructField( + irStruct, + superKey, + irSuperType); + } } } - } - for (auto fieldDecl : decl->getMembersOfType()) - { - if (fieldDecl->hasModifier()) + for (auto fieldDecl : decl->getMembersOfType()) { - // A `static` field is actually a global variable, - // and we should emit it as such. - ensureDecl(context, fieldDecl); - continue; - } - - // Each ordinary field will need to turn into a struct "key" - // that is used for fetching the field. - IRInst* fieldKeyInst = getSimpleVal(context, - ensureDecl(context, fieldDecl)); - auto fieldKey = as(fieldKeyInst); - SLANG_ASSERT(fieldKey); - - // Note: we lower the type of the field in the "sub" - // context, so that any generic parameters that were - // set up for the type can be referenced by the field type. - IRType* fieldType = lowerType( - subContext, - fieldDecl->getType()); + if (fieldDecl->hasModifier()) + { + // A `static` field is actually a global variable, + // and we should emit it as such. + ensureDecl(context, fieldDecl); + continue; + } - // Then, the parent `struct` instruction itself will have - // a "field" instruction. - subBuilder->createStructField( - irStruct, - fieldKey, - fieldType); + // Each ordinary field will need to turn into a struct "key" + // that is used for fetching the field. + IRInst* fieldKeyInst = getSimpleVal(context, + ensureDecl(context, fieldDecl)); + auto fieldKey = as(fieldKeyInst); + SLANG_ASSERT(fieldKey); + + // Note: we lower the type of the field in the "sub" + // context, so that any generic parameters that were + // set up for the type can be referenced by the field type. + IRType* fieldType = lowerType( + subContext, + fieldDecl->getType()); + + // Then, the parent `struct` instruction itself will have + // a "field" instruction. + subBuilder->createStructField( + irStruct, + fieldKey, + fieldType); + } } // There may be members not handled by the above logic (e.g., @@ -5455,10 +5487,10 @@ struct DeclLoweringVisitor : DeclVisitor // Instead we will force emission of all children of aggregate // type declarations later, from the top-level emit logic. - irStruct->moveToEnd(); - addTargetIntrinsicDecorations(irStruct, decl); + resultType->moveToEnd(); + addTargetIntrinsicDecorations(resultType, decl); - return LoweredValInfo::simple(finishOuterGenerics(subBuilder, irStruct)); + return LoweredValInfo::simple(finishOuterGenerics(subBuilder, resultType)); } LoweredValInfo lowerMemberVarDecl(VarDecl* fieldDecl) @@ -5995,29 +6027,17 @@ struct DeclLoweringVisitor : DeclVisitor return as(builder->getStringValue(stringLitExpr->value.getUnownedSlice())); } - LoweredValInfo lowerFuncDecl(FunctionDeclBase* decl) + void _lowerFuncResultAndParameterTypes( + ParameterLists& parameterLists, + List& 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 +6056,6 @@ struct DeclLoweringVisitor : DeclVisitor } } - // need to create an IR function here - - IRFunc* irFunc = subBuilder->createFunc(); - addNameHint(context, irFunc, decl); - addLinkageDecoration(context, irFunc, decl); - - List paramTypes; - for( auto paramInfo : parameterLists.params ) { IRType* irParamType = lowerType(subContext, paramInfo.type); @@ -6054,10 +6066,10 @@ struct DeclLoweringVisitor : DeclVisitor // Simple case of a by-value input parameter. break; - // If the parameter is declared `out` or `inout`, - // then we will represent it with a pointer type in - // the IR, but we will use a specialized pointer - // type that encodes the parameter direction information. + // If the parameter is declared `out` or `inout`, + // then we will represent it with a pointer type in + // the IR, but we will use a specialized pointer + // type that encodes the parameter direction information. case kParameterDirection_Out: irParamType = subBuilder->getOutType(irParamType); break; @@ -6084,7 +6096,7 @@ struct DeclLoweringVisitor : DeclVisitor paramTypes.add(irParamType); } - auto irResultType = lowerType(subContext, declForReturnType->returnType); + irResultType = lowerType(subContext, declForReturnType->returnType); if (auto setterDecl = as(decl)) { @@ -6107,11 +6119,83 @@ struct DeclLoweringVisitor : DeclVisitor // being accessed, rather than a simple value. irResultType = subBuilder->getPtrType(irResultType); } + } - auto irFuncType = subBuilder->getFuncType( + IRFuncType* _lowerFuncTypeImpl( + ParameterLists& parameterLists, + List& paramTypes, + IRType*& irResultType, + IRBuilder* builder, + IRGenContext* irGenContext, + FunctionDeclBase* decl) + { + _lowerFuncResultAndParameterTypes( + parameterLists, + paramTypes, + irResultType, + builder, + irGenContext, + decl); + + auto irFuncType = builder->getFuncType( paramTypes.getCount(), paramTypes.getBuffer(), irResultType); + + if (parameterLists.params.getCount() && parameterLists.params[0].isThisParam) + builder->addThisPointerDecoration(irFuncType, 0); + return irFuncType; + } + + IRInst* lowerFuncType(FunctionDeclBase* decl) + { + NestedContext nestedContextFuncType(this); + auto funcTypeBuilder = nestedContextFuncType.getBuilder(); + auto funcTypeContext = nestedContextFuncType.getContext(); + + emitOuterGenerics(funcTypeContext, decl, decl); + + ParameterLists parameterLists; + List paramTypes; + IRType* irResultType = nullptr; + auto irFuncType = _lowerFuncTypeImpl( + parameterLists, + paramTypes, + irResultType, + funcTypeBuilder, + funcTypeContext, + decl); + + return finishOuterGenerics(funcTypeBuilder, irFuncType); + } + + 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(); + + 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 paramTypes; + IRType* irResultType = nullptr; + auto irFuncType = _lowerFuncTypeImpl( + parameterLists, + paramTypes, + irResultType, + subBuilder, + subContext, + decl); irFunc->setFullType(irFuncType); subBuilder->setInsertInto(irFunc); @@ -6251,14 +6335,7 @@ struct DeclLoweringVisitor : DeclVisitor if (paramInfo.isThisParam) { subContext->thisVal = paramVal; - subBuilder->addThisPointerDecoration(irParam); - } - - // Add a [polymorphic] decoration for generic-typed parameters. - if (as(irParamType) && - as(irParamType->getFullType())) - { - subBuilder->addPolymorphicDecoration(irParam); + subBuilder->addThisPointerDecoration(irParam, (int)(paramTypeIndex - 1)); } } @@ -6470,7 +6547,53 @@ struct DeclLoweringVisitor : DeclVisitor // 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); + + if (auto genericVal = as(finalVal)) + { + auto funcType = lowerFuncType(decl); + genericVal->typeUse.set(funcType); + } + + maybeAssociateToInterfaceType(decl, finalVal); + + return LoweredValInfo::simple(finalVal); + } + + void maybeAssociateToInterfaceType(Decl* decl, IRInst* irFuncVal) + { + auto parent = decl->parentDecl; + InterfaceDecl* interfaceDecl = nullptr; + while (parent) + { + interfaceDecl = as(parent); + if (interfaceDecl) break; + parent = parent->parentDecl; + } + if (!interfaceDecl) + return; + auto loweredVal = context->findLoweredDecl(interfaceDecl); + if (!loweredVal) + { + return; + } + IRInst* irFuncType = irFuncVal->typeUse.get(); + auto irInterfaceType = cast(loweredVal->val); + auto key = getInterfaceRequirementKey(decl); + for (UInt i = 0; i < irInterfaceType->getOperandCount(); i++) + { + auto operand = cast(irInterfaceType->getOperand(i)); + if (operand->getOperand(0) == key) + { + operand->setOperand(1, irFuncType); + return; + } + } + SLANG_UNREACHABLE("associating interface function declaration:" + "requirement not found in the interface type."); } LoweredValInfo visitGenericDecl(GenericDecl * genDecl) @@ -6759,7 +6882,8 @@ LoweredValInfo emitDeclRef( auto irSatisfyingVal = context->irBuilder->emitLookupInterfaceMethodInst( type, irWitnessTable, - irRequirementKey); + irRequirementKey, + lowerType(context, thisTypeSubst->witness->sup)); return LoweredValInfo::simple(irSatisfyingVal); } else -- cgit v1.2.3 From 161c525c56dfd4feb3db528f09169412d0b7ed55 Mon Sep 17 00:00:00 2001 From: Yong He Date: Wed, 24 Jun 2020 14:22:52 -0700 Subject: Fixes. --- source/slang/slang-emit-c-like.cpp | 2 +- source/slang/slang-ir.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'source') diff --git a/source/slang/slang-emit-c-like.cpp b/source/slang/slang-emit-c-like.cpp index 2605723c7..faf7b8c1d 100644 --- a/source/slang/slang-emit-c-like.cpp +++ b/source/slang/slang-emit-c-like.cpp @@ -3633,7 +3633,7 @@ void CLikeSourceEmitter::ensureInstOperandsRec(ComputeEmitActionsContext* ctx, I // only need the type they point to to be forward-declared. // Similarly, a `call` instruction only needs the callee // to be forward-declared, etc. - + ensureInstOperand(ctx, inst->getOperand(ii), requiredLevel); } diff --git a/source/slang/slang-ir.h b/source/slang/slang-ir.h index b41c94e7f..3af800404 100644 --- a/source/slang/slang-ir.h +++ b/source/slang/slang-ir.h @@ -458,7 +458,7 @@ struct IRInst void setOperand(UInt index, IRInst* value) { - getOperands()[index].set(value); + getOperands()[index].init(this, value); } -- cgit v1.2.3 From ffa9a3575ff888dc494ba4878f52441c64a9e08c Mon Sep 17 00:00:00 2001 From: Yong He Date: Wed, 24 Jun 2020 18:09:40 -0700 Subject: Fix `lowerFuncType` and small bug fixes. --- source/slang/slang-emit-c-like.cpp | 6 ++-- source/slang/slang-emit.cpp | 3 +- source/slang/slang-ir-link.cpp | 28 +++++++++++++-- source/slang/slang-ir-lower-generics.cpp | 2 +- source/slang/slang-ir-specialize.cpp | 5 +++ source/slang/slang-lower-to-ir.cpp | 62 ++++++++++++++++---------------- tests/compute/dynamic-dispatch-2.slang | 2 -- 7 files changed, 68 insertions(+), 40 deletions(-) (limited to 'source') diff --git a/source/slang/slang-emit-c-like.cpp b/source/slang/slang-emit-c-like.cpp index faf7b8c1d..516a8ff22 100644 --- a/source/slang/slang-emit-c-like.cpp +++ b/source/slang/slang-emit-c-like.cpp @@ -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) @@ -2289,7 +2290,6 @@ void CLikeSourceEmitter::defaultEmitInstExpr(IRInst* inst, const EmitOpInfo& inO m_writer->emit(")"); } break; - default: diagnoseUnhandledInst(inst); break; @@ -3654,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 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-link.cpp b/source/slang/slang-ir-link.cpp index 4e6ad74a4..e556fd738 100644 --- a/source/slang/slang-ir-link.cpp +++ b/source/slang/slang-ir-link.cpp @@ -89,6 +89,25 @@ struct IRSpecContextBase } }; +enum class GlobalValueClass +{ + StructKey, + Other +}; + +// Get the "class" of a global value. If there are more than one value of the same class, +// only one value in each class will be selected during linking. +GlobalValueClass getGlobalValueClass(IRInst* value) +{ + switch (value->op) + { + case kIROp_StructKey: + return GlobalValueClass::StructKey; + default: + return GlobalValueClass::Other; + } +} + void registerClonedValue( IRSpecContextBase* context, IRInst* clonedValue, @@ -128,9 +147,11 @@ void registerClonedValue( IROriginalValuesForClone const& originalValues) { registerClonedValue(context, clonedValue, originalValues.originalVal); + auto valueClass = getGlobalValueClass(clonedValue); for( auto s = originalValues.sym; s; s = s->nextWithSameName ) { - registerClonedValue(context, clonedValue, s->irGlobalValue); + if (getGlobalValueClass(s->irGlobalValue) == valueClass) + registerClonedValue(context, clonedValue, s->irGlobalValue); } } @@ -1153,7 +1174,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. @@ -1214,10 +1234,12 @@ IRInst* cloneGlobalValueWithLinkage( // definitions over declarations. // IRInst* bestVal = nullptr; + auto valueClass = getGlobalValueClass(originalVal); for(IRSpecSymbol* ss = sym; ss; ss = ss->nextWithSameName ) { IRInst* newVal = ss->irGlobalValue; - if(isBetterForTarget(context, newVal, bestVal)) + if (getGlobalValueClass(newVal) == valueClass && + 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 fe0fa3364..4701a3cce 100644 --- a/source/slang/slang-ir-lower-generics.cpp +++ b/source/slang/slang-ir-lower-generics.cpp @@ -162,7 +162,7 @@ namespace Slang newOperands.add(paramType); } } - if (!translated) + if (!translated && additionalParamCount == 0) return funcType; for (UInt i = 0; i < additionalParamCount; i++) { 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-lower-to-ir.cpp b/source/slang/slang-lower-to-ir.cpp index ff356fd48..3e211e7ca 100644 --- a/source/slang/slang-lower-to-ir.cpp +++ b/source/slang/slang-lower-to-ir.cpp @@ -4466,7 +4466,7 @@ struct DeclLoweringVisitor : DeclVisitor auto type = lowerType(subContext, decl->type.type); - return LoweredValInfo::simple(finishOuterGenerics(subBuilder, type)); + return LoweredValInfo::simple(finishOuterGenerics(subBuilder, type, outerGeneric)); } LoweredValInfo visitGenericTypeParamDecl(GenericTypeParamDecl* /*decl*/) @@ -4536,7 +4536,7 @@ struct DeclLoweringVisitor : DeclVisitor Dictionary mapASTToIRWitnessTable) { auto subBuilder = subContext->irBuilder; - + for(auto entry : astWitnessTable->requirementDictionary) { auto requiredMemberDecl = entry.Key; @@ -4676,7 +4676,7 @@ struct DeclLoweringVisitor : DeclVisitor 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. // @@ -4705,7 +4705,7 @@ struct DeclLoweringVisitor : DeclVisitor irWitnessTable->moveToEnd(); - return LoweredValInfo::simple(finishOuterGenerics(subBuilder, irWitnessTable)); + return LoweredValInfo::simple(finishOuterGenerics(subBuilder, irWitnessTable, outerGeneric)); } LoweredValInfo visitDeclGroup(DeclGroup* declGroup) @@ -5106,7 +5106,7 @@ struct DeclLoweringVisitor : DeclVisitor 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()); @@ -5207,7 +5207,7 @@ struct DeclLoweringVisitor : DeclVisitor } irGlobal->moveToEnd(); - finishOuterGenerics(subBuilder, irGlobal); + finishOuterGenerics(subBuilder, irGlobal, outerGeneric); return globalVal; } @@ -5317,7 +5317,7 @@ struct DeclLoweringVisitor : DeclVisitor } // Emit any generics that should wrap the actual type. - emitOuterGenerics(subContext, decl, decl); + auto outerGeneric = emitOuterGenerics(subContext, decl, decl); IRInterfaceType* irInterface = subBuilder->createInterfaceType( requirementEntries.getCount(), @@ -5333,7 +5333,7 @@ struct DeclLoweringVisitor : DeclVisitor addTargetIntrinsicDecorations(irInterface, decl); - return LoweredValInfo::simple(finishOuterGenerics(subBuilder, irInterface)); + return LoweredValInfo::simple(finishOuterGenerics(subBuilder, irInterface, outerGeneric)); } LoweredValInfo visitEnumCaseDecl(EnumCaseDecl* decl) @@ -5367,7 +5367,7 @@ struct DeclLoweringVisitor : DeclVisitor 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 @@ -5379,7 +5379,7 @@ struct DeclLoweringVisitor : DeclVisitor IRType* loweredTagType = lowerType(subContext, decl->tagType); - return LoweredValInfo::simple(finishOuterGenerics(subBuilder, loweredTagType)); + return LoweredValInfo::simple(finishOuterGenerics(subBuilder, loweredTagType, outerGeneric)); } LoweredValInfo visitAggTypeDecl(AggTypeDecl* decl) @@ -5406,7 +5406,7 @@ struct DeclLoweringVisitor : DeclVisitor auto subContext = nestedContext.getContext(); // Emit any generics that should wrap the actual type. - emitOuterGenerics(subContext, decl, decl); + auto outerGeneric = emitOuterGenerics(subContext, decl, decl); IRInst* resultType = nullptr; if (as(decl)) @@ -5490,7 +5490,7 @@ struct DeclLoweringVisitor : DeclVisitor resultType->moveToEnd(); addTargetIntrinsicDecorations(resultType, decl); - return LoweredValInfo::simple(finishOuterGenerics(subBuilder, resultType)); + return LoweredValInfo::simple(finishOuterGenerics(subBuilder, resultType, outerGeneric)); } LoweredValInfo lowerMemberVarDecl(VarDecl* fieldDecl) @@ -5819,24 +5819,25 @@ struct DeclLoweringVisitor : DeclVisitor // IRInst* finishOuterGenerics( IRBuilder* subBuilder, - IRInst* val) + IRInst* val, + IRGeneric* parentGeneric) { IRInst* v = val; - for(;;) + while (parentGeneric) { - auto parentBlock = as(v->getParent()); - if (!parentBlock) break; - - auto parentGeneric = as(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(v->getParent()); + if (!parentBlock) break; + + parentGeneric = as(parentBlock->getParent()); + if (!parentGeneric) break; + } return v; } @@ -6066,10 +6067,10 @@ struct DeclLoweringVisitor : DeclVisitor // Simple case of a by-value input parameter. break; - // If the parameter is declared `out` or `inout`, - // then we will represent it with a pointer type in - // the IR, but we will use a specialized pointer - // type that encodes the parameter direction information. + // If the parameter is declared `out` or `inout`, + // then we will represent it with a pointer type in + // the IR, but we will use a specialized pointer + // type that encodes the parameter direction information. case kParameterDirection_Out: irParamType = subBuilder->getOutType(irParamType); break; @@ -6153,7 +6154,7 @@ struct DeclLoweringVisitor : DeclVisitor auto funcTypeBuilder = nestedContextFuncType.getBuilder(); auto funcTypeContext = nestedContextFuncType.getContext(); - emitOuterGenerics(funcTypeContext, decl, decl); + auto outerGenerics = emitOuterGenerics(funcTypeContext, decl, decl); ParameterLists parameterLists; List paramTypes; @@ -6166,7 +6167,7 @@ struct DeclLoweringVisitor : DeclVisitor funcTypeContext, decl); - return finishOuterGenerics(funcTypeBuilder, irFuncType); + return finishOuterGenerics(funcTypeBuilder, irFuncType, outerGenerics); } LoweredValInfo lowerFuncDecl(FunctionDeclBase* decl) @@ -6178,7 +6179,7 @@ struct DeclLoweringVisitor : DeclVisitor auto subBuilder = nestedContextFunc.getBuilder(); auto subContext = nestedContextFunc.getContext(); - emitOuterGenerics(subContext, decl, decl); + auto outerGeneric = emitOuterGenerics(subContext, decl, decl); // need to create an IR function here @@ -6550,12 +6551,11 @@ struct DeclLoweringVisitor : DeclVisitor // 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); - + auto finalVal = finishOuterGenerics(subBuilder, irFunc, outerGeneric); if (auto genericVal = as(finalVal)) { auto funcType = lowerFuncType(decl); - genericVal->typeUse.set(funcType); + genericVal->setFullType((IRType*)funcType); } maybeAssociateToInterfaceType(decl, finalVal); diff --git a/tests/compute/dynamic-dispatch-2.slang b/tests/compute/dynamic-dispatch-2.slang index ade8aeb84..6b8b0e633 100644 --- a/tests/compute/dynamic-dispatch-2.slang +++ b/tests/compute/dynamic-dispatch-2.slang @@ -12,7 +12,6 @@ interface IInterface { associatedtype Assoc : IAssoc; int Compute(int inVal); - Assoc getAssoc(); }; int GenericCompute(T obj, int inVal) @@ -30,7 +29,6 @@ struct Impl : IInterface }; int base; int Compute(int inVal) { return base + inVal * inVal; } - Assoc getAssoc() { Assoc rs; rs.val = 1; return rs; } }; int test(int inVal) -- cgit v1.2.3 From a1fed5e49bc1c8452752d13d401ee0bbbc5c71c4 Mon Sep 17 00:00:00 2001 From: Yong He Date: Thu, 25 Jun 2020 13:19:45 -0700 Subject: Partial fixes to code review comments --- source/slang/slang-emit-c-like.cpp | 2 +- source/slang/slang-emit-cpp.cpp | 35 ++------- source/slang/slang-ir-inst-defs.h | 1 + source/slang/slang-ir-insts.h | 6 +- source/slang/slang-ir-link.cpp | 27 +------ source/slang/slang-ir-lower-generics.cpp | 22 +++--- source/slang/slang-ir.cpp | 20 +++--- source/slang/slang-ir.h | 16 ++--- source/slang/slang-lower-to-ir.cpp | 118 ++++++++++++++----------------- 9 files changed, 98 insertions(+), 149 deletions(-) (limited to 'source') diff --git a/source/slang/slang-emit-c-like.cpp b/source/slang/slang-emit-c-like.cpp index 516a8ff22..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(); } } diff --git a/source/slang/slang-emit-cpp.cpp b/source/slang/slang-emit-cpp.cpp index eeace4aa7..a449a2c56 100644 --- a/source/slang/slang-emit-cpp.cpp +++ b/source/slang/slang-emit-cpp.cpp @@ -390,27 +390,12 @@ static UnownedStringSlice _getResourceTypePrefix(IROp op) } } -static bool isVoidPtrType(IRType* type) -{ - auto ptrType = as(type); - if (!ptrType) return false; - return ptrType->getValueType()->op == kIROp_VoidType; -} - SlangResult CPPSourceEmitter::calcTypeName(IRType* type, CodeGenTarget target, StringBuilder& out) { switch (type->op) { case kIROp_PtrType: { - if (isVoidPtrType(type)) - { - // A `void*` type will always emit as `void*`. - // `void*` types are generated as a result of generics lowering - // for dynamic dispatch. - out << "void*"; - return SLANG_OK; - } auto ptrType = static_cast(type); SLANG_RETURN_ON_FAIL(calcTypeName(ptrType->getValueType(), target, out)); // TODO(JS): It seems although it says it is a pointer, it can actually be output as a reference @@ -513,6 +498,11 @@ SlangResult CPPSourceEmitter::calcTypeName(IRType* type, CodeGenTarget target, S out << "*"; return SLANG_OK; } + case kIROp_RawPointerType: + { + out << "void*"; + return SLANG_OK; + } default: { if (isNominalOp(type->op)) @@ -1774,7 +1764,7 @@ void CPPSourceEmitter::_maybeEmitWitnessTableTypeDefinition( { 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; @@ -1804,7 +1794,7 @@ void CPPSourceEmitter::_maybeEmitWitnessTableTypeDefinition( { emitType(constraintInterfaceType); m_writer->emit("* "); - m_writer->emit(getName(entry->requirementKey.get())); + m_writer->emit(getName(entry->getRequirementKey())); m_writer->emit(";\n"); } } @@ -2007,17 +1997,6 @@ void CPPSourceEmitter::emitSimpleValueImpl(IRInst* inst) 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. - if (isVoidPtrType(param->getDataType())) - { - m_writer->emit("void* "); - m_writer->emit(getName(param)); - return; - } CLikeSourceEmitter::emitSimpleFuncParamImpl(param); } diff --git a/source/slang/slang-ir-inst-defs.h b/source/slang/slang-ir-inst-defs.h index e9bc23993..c4fd6edf8 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) diff --git a/source/slang/slang-ir-insts.h b/source/slang/slang-ir-insts.h index fb0cc57c7..5d467085e 100644 --- a/source/slang/slang-ir-insts.h +++ b/source/slang/slang-ir-insts.h @@ -1573,6 +1573,9 @@ struct IRBuilder IRBasicType* getBoolType(); IRBasicType* getIntType(); IRStringType* getStringType(); + IRAssociatedType* getAssociatedType(); + IRRawPointerType* getRawPointerType(); + IRBasicBlockType* getBasicBlockType(); IRWitnessTableType* getWitnessTableType(IRType* baseType); @@ -1819,9 +1822,6 @@ struct IRBuilder // Create an initially empty `struct` type. IRStructType* createStructType(); - // Create an IRType representing an `associatedtype` decl. - IRAssociatedType* createAssociatedType(); - // Create an empty `interface` type. IRInterfaceType* createInterfaceType(UInt operandCount, IRInst* const* operands); diff --git a/source/slang/slang-ir-link.cpp b/source/slang/slang-ir-link.cpp index e556fd738..f80f97aff 100644 --- a/source/slang/slang-ir-link.cpp +++ b/source/slang/slang-ir-link.cpp @@ -89,25 +89,6 @@ struct IRSpecContextBase } }; -enum class GlobalValueClass -{ - StructKey, - Other -}; - -// Get the "class" of a global value. If there are more than one value of the same class, -// only one value in each class will be selected during linking. -GlobalValueClass getGlobalValueClass(IRInst* value) -{ - switch (value->op) - { - case kIROp_StructKey: - return GlobalValueClass::StructKey; - default: - return GlobalValueClass::Other; - } -} - void registerClonedValue( IRSpecContextBase* context, IRInst* clonedValue, @@ -147,11 +128,9 @@ void registerClonedValue( IROriginalValuesForClone const& originalValues) { registerClonedValue(context, clonedValue, originalValues.originalVal); - auto valueClass = getGlobalValueClass(clonedValue); for( auto s = originalValues.sym; s; s = s->nextWithSameName ) { - if (getGlobalValueClass(s->irGlobalValue) == valueClass) - registerClonedValue(context, clonedValue, s->irGlobalValue); + registerClonedValue(context, clonedValue, s->irGlobalValue); } } @@ -1234,12 +1213,10 @@ IRInst* cloneGlobalValueWithLinkage( // definitions over declarations. // IRInst* bestVal = nullptr; - auto valueClass = getGlobalValueClass(originalVal); for(IRSpecSymbol* ss = sym; ss; ss = ss->nextWithSameName ) { IRInst* newVal = ss->irGlobalValue; - if (getGlobalValueClass(newVal) == valueClass && - 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 4701a3cce..4b1b267f9 100644 --- a/source/slang/slang-ir-lower-generics.cpp +++ b/source/slang/slang-ir-lower-generics.cpp @@ -79,7 +79,7 @@ namespace Slang builder.sharedBuilder = &sharedBuilderStorage; builder.setInsertBefore(genericParent); auto loweredFunc = cloneInstAndOperands(&cloneEnv, &builder, func); - loweredFunc->setFullType(lowerGenericFuncType(&builder, cast(genericParent->typeUse.get()))); + loweredFunc->setFullType(lowerGenericFuncType(&builder, cast(genericParent->getFullType()))); List clonedParams; for (auto genericParam : genericParent->getParams()) { @@ -100,7 +100,7 @@ namespace Slang { if (isPolymorphicType(param->getFullType())) { - param->setFullType(builder.getPtrType(builder.getVoidType())); + param->setFullType(builder.getRawPointerType()); } } addToWorkList(loweredFunc); @@ -114,7 +114,7 @@ namespace Slang { if (isPolymorphicType(genericParam->getFullType())) { - genericParamTypes.add(builder->getPtrType(builder->getVoidType())); + genericParamTypes.add(builder->getRawPointerType()); } else { @@ -146,7 +146,7 @@ namespace Slang auto paramType = funcType->getOperand(i); if (isPolymorphicType(paramType)) { - newOperands.add(builder->getPtrType(builder->getVoidType())); + newOperands.add(builder->getRawPointerType()); translated = true; } else if (paramType->op == kIROp_Specialize) @@ -154,7 +154,7 @@ namespace Slang // TODO: handle static specialized type here. // For now treat all specialized types as dynamic. // In the future, we need to turn things like Array into Array. - newOperands.add(builder->getPtrType(builder->getVoidType())); + newOperands.add(builder->getRawPointerType()); translated = true; } else @@ -194,11 +194,11 @@ namespace Slang { if (auto funcType = as(entry->getRequirementVal())) { - entry->requirementVal.set(lowerFuncType(&builder, funcType)); + entry->setRequirementVal(lowerFuncType(&builder, funcType)); } else if (auto genericFuncType = as(entry->getRequirementVal())) { - entry->requirementVal.set(lowerGenericFuncType(&builder, genericFuncType)); + entry->setRequirementVal(lowerGenericFuncType(&builder, genericFuncType)); } } } @@ -256,12 +256,12 @@ namespace Slang builder->sharedBuilder = &sharedBuilderStorage; builder->setInsertBefore(inst); List args; - auto voidPtrType = builder->getPtrType(builder->getVoidType()); + auto rawPtrType = builder->getRawPointerType(); for (UInt i = 0; i < callInst->getArgCount(); i++) { auto arg = callInst->getArg(i); - if (paramTypes[i] == 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*. @@ -272,7 +272,7 @@ 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); diff --git a/source/slang/slang-ir.cpp b/source/slang/slang-ir.cpp index 891f4b3e0..07cb957db 100644 --- a/source/slang/slang-ir.cpp +++ b/source/slang/slang-ir.cpp @@ -2194,6 +2194,16 @@ namespace Slang return (IRStringType*)getType(kIROp_StringType); } + IRAssociatedType* IRBuilder::getAssociatedType() + { + return (IRAssociatedType*)getType(kIROp_AssociatedType); + } + + IRRawPointerType* IRBuilder::getRawPointerType() + { + return (IRRawPointerType*)getType(kIROp_RawPointerType); + } + IRBasicBlockType* IRBuilder::getBasicBlockType() { return (IRBasicBlockType*)getType(kIROp_BasicBlockType); @@ -2837,16 +2847,6 @@ namespace Slang return structType; } - IRAssociatedType* IRBuilder::createAssociatedType() - { - IRAssociatedType* associatedType = createInst( - this, - kIROp_AssociatedType, - nullptr); - addGlobalValue(this, associatedType); - return associatedType; - } - IRInterfaceType* IRBuilder::createInterfaceType(UInt operandCount, IRInst* const* operands) { IRInterfaceType* interfaceType = createInst( diff --git a/source/slang/slang-ir.h b/source/slang/slang-ir.h index 3af800404..6447deddb 100644 --- a/source/slang/slang-ir.h +++ b/source/slang/slang-ir.h @@ -458,7 +458,8 @@ struct IRInst void setOperand(UInt index, IRInst* value) { - getOperands()[index].init(this, 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) @@ -1197,14 +1202,9 @@ struct IRAssociatedType : IRType struct IRInterfaceRequirementEntry : IRInst { - // The AST-level requirement - IRUse requirementKey; - - // The IR-level value that represents the declaration of the requirement - IRUse requirementVal; - IRInst* getRequirementKey() { return getOperand(0); } IRInst* getRequirementVal() { return getOperand(1); } + void setRequirementVal(IRInst* val) { setOperand(1, val); } IR_LEAF_ISA(InterfaceRequirementEntry); }; diff --git a/source/slang/slang-lower-to-ir.cpp b/source/slang/slang-lower-to-ir.cpp index 3e211e7ca..96d4b121f 100644 --- a/source/slang/slang-lower-to-ir.cpp +++ b/source/slang/slang-lower-to-ir.cpp @@ -5390,6 +5390,11 @@ struct DeclLoweringVisitor : DeclVisitor return LoweredValInfo(); } + if (as(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. @@ -5407,77 +5412,64 @@ struct DeclLoweringVisitor : DeclVisitor // Emit any generics that should wrap the actual type. auto outerGeneric = emitOuterGenerics(subContext, decl, decl); + - IRInst* resultType = nullptr; - if (as(decl)) - { - resultType = subBuilder->createAssociatedType(); - } - else - { - resultType = subBuilder->createStructType(); - } - - addNameHint(context, resultType, decl); - addLinkageDecoration(context, resultType, decl); + IRStructType* irStruct = subBuilder->createStructType(); + addNameHint(context, irStruct, decl); + addLinkageDecoration(context, irStruct, decl); + subBuilder->setInsertInto(irStruct); - if (resultType->op == kIROp_StructType) + // A `struct` that inherits from another `struct` must start + // with a member for the direct base type. + // + for( auto inheritanceDecl : decl->getMembersOfType() ) { - IRStructType* irStruct = (IRStructType*)resultType; - subBuilder->setInsertInto(irStruct); - - // A `struct` that inherits from another `struct` must start - // with a member for the direct base type. - // - for( auto inheritanceDecl : decl->getMembersOfType() ) + auto superType = inheritanceDecl->base; + if(auto superDeclRefType = as(superType)) { - auto superType = inheritanceDecl->base; - if(auto superDeclRefType = as(superType)) + if(auto superStructDeclRef = superDeclRefType->declRef.as()) { - if(auto superStructDeclRef = superDeclRefType->declRef.as()) - { - auto superKey = (IRStructKey*) getSimpleVal(context, ensureDecl(context, inheritanceDecl)); - auto irSuperType = lowerType(context, superType.type); - subBuilder->createStructField( - irStruct, - superKey, - irSuperType); - } + auto superKey = (IRStructKey*) getSimpleVal(context, ensureDecl(context, inheritanceDecl)); + auto irSuperType = lowerType(context, superType.type); + subBuilder->createStructField( + irStruct, + superKey, + irSuperType); } } + } - for (auto fieldDecl : decl->getMembersOfType()) + for (auto fieldDecl : decl->getMembersOfType()) + { + if (fieldDecl->hasModifier()) { - if (fieldDecl->hasModifier()) - { - // A `static` field is actually a global variable, - // and we should emit it as such. - ensureDecl(context, fieldDecl); - continue; - } - - // Each ordinary field will need to turn into a struct "key" - // that is used for fetching the field. - IRInst* fieldKeyInst = getSimpleVal(context, - ensureDecl(context, fieldDecl)); - auto fieldKey = as(fieldKeyInst); - SLANG_ASSERT(fieldKey); - - // Note: we lower the type of the field in the "sub" - // context, so that any generic parameters that were - // set up for the type can be referenced by the field type. - IRType* fieldType = lowerType( - subContext, - fieldDecl->getType()); - - // Then, the parent `struct` instruction itself will have - // a "field" instruction. - subBuilder->createStructField( - irStruct, - fieldKey, - fieldType); + // A `static` field is actually a global variable, + // and we should emit it as such. + ensureDecl(context, fieldDecl); + continue; } + + // Each ordinary field will need to turn into a struct "key" + // that is used for fetching the field. + IRInst* fieldKeyInst = getSimpleVal(context, + ensureDecl(context, fieldDecl)); + auto fieldKey = as(fieldKeyInst); + SLANG_ASSERT(fieldKey); + + // Note: we lower the type of the field in the "sub" + // context, so that any generic parameters that were + // set up for the type can be referenced by the field type. + IRType* fieldType = lowerType( + subContext, + fieldDecl->getType()); + + // Then, the parent `struct` instruction itself will have + // a "field" instruction. + subBuilder->createStructField( + irStruct, + fieldKey, + fieldType); } // There may be members not handled by the above logic (e.g., @@ -5487,10 +5479,10 @@ struct DeclLoweringVisitor : DeclVisitor // Instead we will force emission of all children of aggregate // type declarations later, from the top-level emit logic. - resultType->moveToEnd(); - addTargetIntrinsicDecorations(resultType, decl); + irStruct->moveToEnd(); + addTargetIntrinsicDecorations(irStruct, decl); - return LoweredValInfo::simple(finishOuterGenerics(subBuilder, resultType, outerGeneric)); + return LoweredValInfo::simple(finishOuterGenerics(subBuilder, irStruct, outerGeneric)); } LoweredValInfo lowerMemberVarDecl(VarDecl* fieldDecl) -- cgit v1.2.3 From 509e36b62de7578843abc2547921beadff7a3ce0 Mon Sep 17 00:00:00 2001 From: Yong He Date: Thu, 25 Jun 2020 14:01:33 -0700 Subject: Remove interfaceType operand from lookup_witness_method inst --- source/slang/slang-emit-cpp.cpp | 4 ++-- source/slang/slang-ir-inst-defs.h | 2 +- source/slang/slang-ir-insts.h | 7 ++----- source/slang/slang-ir-lower-generics.cpp | 3 ++- source/slang/slang-ir.cpp | 17 ++++++++--------- source/slang/slang-ir.h | 4 ++++ source/slang/slang-lower-to-ir.cpp | 19 ++++++++----------- 7 files changed, 27 insertions(+), 29 deletions(-) (limited to 'source') diff --git a/source/slang/slang-emit-cpp.cpp b/source/slang/slang-emit-cpp.cpp index a449a2c56..f895b1119 100644 --- a/source/slang/slang-emit-cpp.cpp +++ b/source/slang/slang-emit-cpp.cpp @@ -1790,9 +1790,9 @@ void CPPSourceEmitter::_maybeEmitWitnessTableTypeDefinition( } m_writer->emit(");\n"); } - else if (auto constraintInterfaceType = as(entry->getRequirementVal())) + else if (auto witnessTableType = as(entry->getRequirementVal())) { - emitType(constraintInterfaceType); + emitType((IRType*)witnessTableType->getConformanceType()); m_writer->emit("* "); m_writer->emit(getName(entry->getRequirementKey())); m_writer->emit(";\n"); diff --git a/source/slang/slang-ir-inst-defs.h b/source/slang/slang-ir-inst-defs.h index c4fd6edf8..94c29371a 100644 --- a/source/slang/slang-ir-inst-defs.h +++ b/source/slang/slang-ir-inst-defs.h @@ -173,7 +173,7 @@ INST(AssociatedType, associated_type, 0, 0) 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) diff --git a/source/slang/slang-ir-insts.h b/source/slang/slang-ir-insts.h index 5d467085e..fcefa8b26 100644 --- a/source/slang/slang-ir-insts.h +++ b/source/slang/slang-ir-insts.h @@ -408,11 +408,9 @@ struct IRLookupWitnessMethod : IRInst { IRUse witnessTable; IRUse requirementKey; - IRUse interfaceType; IRInst* getWitnessTable() { return witnessTable.get(); } IRInst* getRequirementKey() { return requirementKey.get(); } - IRInst* getInterfaceType() { return interfaceType.get(); } IR_LEAF_ISA(lookup_interface_method) }; @@ -1680,8 +1678,7 @@ struct IRBuilder IRInst* emitLookupInterfaceMethodInst( IRType* type, IRInst* witnessTableVal, - IRInst* interfaceMethodVal, - IRType* interfaceType); + IRInst* interfaceMethodVal); IRInst* emitCallInst( IRType* type, @@ -1806,7 +1803,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); diff --git a/source/slang/slang-ir-lower-generics.cpp b/source/slang/slang-ir-lower-generics.cpp index 4b1b267f9..c374f45fa 100644 --- a/source/slang/slang-ir-lower-generics.cpp +++ b/source/slang/slang-ir-lower-generics.cpp @@ -224,7 +224,8 @@ namespace Slang // The callee is a result of witness table lookup, we will only // translate the call. IRInst* callee = nullptr; - auto interfaceType = maybeLowerInterfaceType(cast(interfaceLookup->getInterfaceType())); + auto witnessTableType = cast(interfaceLookup->getWitnessTable()->getFullType()); + auto interfaceType = maybeLowerInterfaceType(cast(witnessTableType->getConformanceType())); for (UInt i = 0; i < interfaceType->getOperandCount(); i++) { auto entry = cast(interfaceType->getOperand(i)); diff --git a/source/slang/slang-ir.cpp b/source/slang/slang-ir.cpp index 07cb957db..8cf7ab171 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++; } @@ -2518,16 +2519,14 @@ namespace Slang IRInst* IRBuilder::emitLookupInterfaceMethodInst( IRType* type, IRInst* witnessTableVal, - IRInst* interfaceMethodVal, - IRType* interfaceType) + IRInst* interfaceMethodVal) { - IRInst* args[3] = { witnessTableVal , interfaceMethodVal, interfaceType }; auto inst = createInst( this, kIROp_lookup_interface_method, type, - 3, - args); + witnessTableVal, + interfaceMethodVal); addInst(inst); return inst; diff --git a/source/slang/slang-ir.h b/source/slang/slang-ir.h index 6447deddb..54658980d 100644 --- a/source/slang/slang-ir.h +++ b/source/slang/slang-ir.h @@ -1226,6 +1226,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 96d4b121f..5bd983cab 100644 --- a/source/slang/slang-lower-to-ir.cpp +++ b/source/slang/slang-lower-to-ir.cpp @@ -1049,7 +1049,7 @@ struct ValLoweringVisitor : ValVisitordeclRef, context->irBuilder->getWitnessTableType( - lowerType(context, DeclRefType::create(context->astBuilder, val->declRef)))); + lowerType(context, val->sup))); } LoweredValInfo visitTransitiveSubtypeWitness( @@ -1071,10 +1071,9 @@ struct ValLoweringVisitor : ValVisitoremitLookupInterfaceMethodInst( - nullptr, + getBuilder()->getWitnessTableType(lowerType(context, val->sup)), baseWitnessTable, - requirementKey, - lowerType(context, val->subToMid->sup))); + requirementKey)); } LoweredValInfo visitTaggedUnionSubtypeWitness( @@ -1255,8 +1254,7 @@ struct ValLoweringVisitor : ValVisitoremitLookupInterfaceMethodInst( caseFuncType, caseWitnessTable, - irReqKey, - irWitnessTableBaseType); + irReqKey); // We are going to emit a `call` to the satisfying value // for the case type, so we will collect the arguments for that call. @@ -5311,7 +5309,7 @@ struct DeclLoweringVisitor : DeclVisitor auto constraintKey = getInterfaceRequirementKey(constraintDecl); requirementEntries.add( subBuilder->createInterfaceRequirementEntry(constraintKey, - lowerType(context, constraintDecl->getSup().type))); + getBuilder()->getWitnessTableType(lowerType(context, constraintDecl->getSup().type)))); } } } @@ -6578,9 +6576,9 @@ struct DeclLoweringVisitor : DeclVisitor for (UInt i = 0; i < irInterfaceType->getOperandCount(); i++) { auto operand = cast(irInterfaceType->getOperand(i)); - if (operand->getOperand(0) == key) + if (operand->getRequirementKey() == key) { - operand->setOperand(1, irFuncType); + operand->setRequirementVal(irFuncType); return; } } @@ -6874,8 +6872,7 @@ LoweredValInfo emitDeclRef( auto irSatisfyingVal = context->irBuilder->emitLookupInterfaceMethodInst( type, irWitnessTable, - irRequirementKey, - lowerType(context, thisTypeSubst->witness->sup)); + irRequirementKey); return LoweredValInfo::simple(irSatisfyingVal); } else -- cgit v1.2.3 From 218a39b65c86654772c3d6adf2479e7cadc85d24 Mon Sep 17 00:00:00 2001 From: Yong He Date: Thu, 25 Jun 2020 16:29:07 -0700 Subject: remove ThisPointerDecoration, generate IRInterfaceType in one pass --- source/slang/slang-emit-cpp.cpp | 50 ++++++++++++------- source/slang/slang-ir-inst-defs.h | 9 +--- source/slang/slang-ir-insts.h | 13 +++-- source/slang/slang-ir-lower-generics.cpp | 1 + source/slang/slang-ir.cpp | 5 ++ source/slang/slang-ir.h | 5 ++ source/slang/slang-lower-to-ir.cpp | 85 +++++++++++++++----------------- 7 files changed, 90 insertions(+), 78 deletions(-) (limited to 'source') diff --git a/source/slang/slang-emit-cpp.cpp b/source/slang/slang-emit-cpp.cpp index f895b1119..7f743d9e0 100644 --- a/source/slang/slang-emit-cpp.cpp +++ b/source/slang/slang-emit-cpp.cpp @@ -1617,12 +1617,27 @@ void CPPSourceEmitter::_emitWitnessTableWrappers() { for (auto witnessTable : pendingWitnessTableDefinitions) { + auto interfaceType = cast(witnessTable->getOperand(0)); for (auto child : witnessTable->getChildren()) { if (auto entry = as(child)) { if (auto funcVal = as(entry->getSatisfyingVal())) { + IRInst* requirementVal = nullptr; + for (UInt i = 0; i < interfaceType->getOperandCount(); i++) + { + if (auto reqEntry = as(interfaceType->getOperand(i))) + { + if (reqEntry->getRequirementKey() == entry->getRequirementKey()) + { + requirementVal = reqEntry->getRequirementVal(); + break; + } + } + } + SLANG_ASSERT(requirementVal != nullptr); + IRFuncType* requirementFuncType = cast(requirementVal); emitType(funcVal->getResultType()); m_writer->emit(" "); m_writer->emit(_getWitnessTableWrapperFuncName(funcVal)); @@ -1630,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(param->getFullType())) + auto paramType = requirementFuncType->getParamType(i); + + if (as(paramType)) continue; if (isFirst) isFirst = false; else m_writer->emit(","); - - if (param->findDecoration()) - { - m_writer->emit("void* "); - m_writer->emit(getName(param)); - continue; - } - emitSimpleFuncParamImpl(param); + emitParamType(paramType, getName(*pp)); } } m_writer->emit(")\n{\n"); @@ -1657,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(param->getFullType())) continue; @@ -1667,7 +1684,8 @@ void CPPSourceEmitter::_emitWitnessTableWrappers() else m_writer->emit(", "); - if (param->findDecoration()) + if (reqParamType->op == kIROp_RawPointerType && + param->getFullType()->op != kIROp_RawPointerType) { m_writer->emit("*static_cast<"); emitType(param->getFullType()); @@ -1779,13 +1797,7 @@ void CPPSourceEmitter::_maybeEmitWitnessTableTypeDefinition( m_writer->emit(", "); else isFirstParam = false; - auto thisDecor = funcVal->findDecoration(); - if (thisDecor && cast(thisDecor->getOperand(0))->value.intVal == (IRIntegerValue)p) - { - m_writer->emit("void* param"); - m_writer->emit(p); - continue; - } + emitParamType(paramType, String("param") + String(p)); } m_writer->emit(");\n"); diff --git a/source/slang/slang-ir-inst-defs.h b/source/slang/slang-ir-inst-defs.h index 94c29371a..85c489528 100644 --- a/source/slang/slang-ir-inst-defs.h +++ b/source/slang/slang-ir-inst-defs.h @@ -167,6 +167,7 @@ INST(Nop, nop, 0, 0) INST(StructType, struct, 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. @@ -510,14 +511,6 @@ INST(HighLevelDeclDecoration, highLevelDecl, 1, 0) INST(BindExistentialSlotsDecoration, bindExistentialSlots, 0, 0) - - /// A `[this_ptr]` decoration marks a function parameter that serves as `this` pointer. - /// `[this_ptr]` decoration is also used to mark an `IRFunc` as a non-static function. - /// The argument is an integer value that represents the index of the `this` parameter, - /// which is always 0. - INST(ThisPointerDecoration, this_ptr, 1, 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 fcefa8b26..f3a0688a7 100644 --- a/source/slang/slang-ir-insts.h +++ b/source/slang/slang-ir-insts.h @@ -165,8 +165,6 @@ IR_SIMPLE_DECORATION(VulkanCallablePayloadDecoration) /// vulkan hit attributes, and should have a location assigned /// to it. IR_SIMPLE_DECORATION(VulkanHitAttributesDecoration) -IR_SIMPLE_DECORATION(ThisPointerDecoration) - struct IRRequireGLSLVersionDecoration : IRDecoration { @@ -1432,6 +1430,11 @@ struct IRWitnessTable : IRInst return IRInstList(getChildren()); } + IRInst* getConformanceType() + { + return getOperand(0); + } + IR_LEAF_ISA(WitnessTable) }; @@ -1572,6 +1575,7 @@ struct IRBuilder IRBasicType* getIntType(); IRStringType* getStringType(); IRAssociatedType* getAssociatedType(); + IRThisType* getThisType(); IRRawPointerType* getRawPointerType(); @@ -2167,11 +2171,6 @@ struct IRBuilder addDecoration(value, kIROp_LoopControlDecoration, getIntValue(getIntType(), IRIntegerValue(mode))); } - void addThisPointerDecoration(IRInst* value, int paramIndex) - { - addDecoration(value, kIROp_ThisPointerDecoration, getIntValue(getIntType(), paramIndex)); - } - 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-lower-generics.cpp b/source/slang/slang-ir-lower-generics.cpp index c374f45fa..774836e29 100644 --- a/source/slang/slang-ir-lower-generics.cpp +++ b/source/slang/slang-ir-lower-generics.cpp @@ -52,6 +52,7 @@ namespace Slang return true; switch (typeInst->op) { + case kIROp_ThisType: case kIROp_AssociatedType: case kIROp_InterfaceType: return true; diff --git a/source/slang/slang-ir.cpp b/source/slang/slang-ir.cpp index 8cf7ab171..ef5ecb959 100644 --- a/source/slang/slang-ir.cpp +++ b/source/slang/slang-ir.cpp @@ -2200,6 +2200,11 @@ namespace Slang return (IRAssociatedType*)getType(kIROp_AssociatedType); } + IRThisType* IRBuilder::getThisType() + { + return (IRThisType*)getType(kIROp_ThisType); + } + IRRawPointerType* IRBuilder::getRawPointerType() { return (IRRawPointerType*)getType(kIROp_RawPointerType); diff --git a/source/slang/slang-ir.h b/source/slang/slang-ir.h index 54658980d..dadcce386 100644 --- a/source/slang/slang-ir.h +++ b/source/slang/slang-ir.h @@ -1200,6 +1200,11 @@ struct IRAssociatedType : IRType IR_LEAF_ISA(AssociatedType) }; +struct IRThisType : IRType +{ + IR_LEAF_ISA(ThisType) +}; + struct IRInterfaceRequirementEntry : IRInst { IRInst* getRequirementKey() { return getOperand(0); } diff --git a/source/slang/slang-lower-to-ir.cpp b/source/slang/slang-lower-to-ir.cpp index 5bd983cab..ebc2e1b74 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) @@ -1582,6 +1585,8 @@ struct ValLoweringVisitor : ValVisitorthisType != nullptr) + return LoweredValInfo::simple(context->thisType); return emitDeclRef(context, type->interfaceDeclRef, getBuilder()->getTypeKind()); } @@ -5068,6 +5073,8 @@ struct DeclLoweringVisitor : DeclVisitor subContextStorage.irBuilder = &subBuilderStorage; subContextStorage.env = &subEnvStorage; + + subContextStorage.thisType = outerContext->thisType; } IRBuilder* getBuilder() { return &subBuilderStorage; } @@ -5292,12 +5299,38 @@ struct DeclLoweringVisitor : DeclVisitor 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(); + // Create a temporary IR value for self references, this will be replaced + // by actual interface type after we create the actual interface type. + auto temporarySelf = subBuilder->createIntrinsicInst(nullptr, kIROp_undefined, 0, nullptr); + subContext->env->mapDeclToValue[decl] = LoweredValInfo::simple(temporarySelf); + for (auto member : decl->members) + { + if (as(member)) + { + subContext->env->mapDeclToValue[member] = getBuilder()->getAssociatedType(); + } + } + List requirementEntries; for (auto requirementDecl : decl->members) { auto key = getInterfaceRequirementKey(requirementDecl); - auto entry = subBuilder->createInterfaceRequirementEntry(key, nullptr); + IRInst* requirementVal = lowerDecl(subContext, requirementDecl).val; + if (requirementVal) + { + auto reqType = requirementVal->getFullType(); + requirementVal->removeAndDeallocate(); + requirementVal = reqType; + } + auto entry = subBuilder->createInterfaceRequirementEntry(key, requirementVal); requirementEntries.add(entry); // As a special case, any type constraints placed // on an associated type will *also* need to be turned @@ -5312,11 +5345,9 @@ struct DeclLoweringVisitor : DeclVisitor getBuilder()->getWitnessTableType(lowerType(context, constraintDecl->getSup().type)))); } } + context->env->mapDeclToValue[requirementDecl] = LoweredValInfo::simple(entry); } - // Emit any generics that should wrap the actual type. - auto outerGeneric = emitOuterGenerics(subContext, decl, decl); - IRInterfaceType* irInterface = subBuilder->createInterfaceType( requirementEntries.getCount(), reinterpret_cast(requirementEntries.getBuffer())); @@ -5330,8 +5361,12 @@ struct DeclLoweringVisitor : DeclVisitor addTargetIntrinsicDecorations(irInterface, decl); - - return LoweredValInfo::simple(finishOuterGenerics(subBuilder, irInterface, outerGeneric)); + auto finalVal = finishOuterGenerics(subBuilder, irInterface, outerGeneric); + // Now we can replace all self references in the requirement types to the + // actual interface type we generated. + temporarySelf->replaceUsesWith(finalVal); + temporarySelf->removeAndDeallocate(); + return LoweredValInfo::simple(finalVal); } LoweredValInfo visitEnumCaseDecl(EnumCaseDecl* decl) @@ -6133,8 +6168,6 @@ struct DeclLoweringVisitor : DeclVisitor paramTypes.getBuffer(), irResultType); - if (parameterLists.params.getCount() && parameterLists.params[0].isThisParam) - builder->addThisPointerDecoration(irFuncType, 0); return irFuncType; } @@ -6326,7 +6359,6 @@ struct DeclLoweringVisitor : DeclVisitor if (paramInfo.isThisParam) { subContext->thisVal = paramVal; - subBuilder->addThisPointerDecoration(irParam, (int)(paramTypeIndex - 1)); } } @@ -6548,44 +6580,9 @@ struct DeclLoweringVisitor : DeclVisitor genericVal->setFullType((IRType*)funcType); } - maybeAssociateToInterfaceType(decl, finalVal); - return LoweredValInfo::simple(finalVal); } - void maybeAssociateToInterfaceType(Decl* decl, IRInst* irFuncVal) - { - auto parent = decl->parentDecl; - InterfaceDecl* interfaceDecl = nullptr; - while (parent) - { - interfaceDecl = as(parent); - if (interfaceDecl) break; - parent = parent->parentDecl; - } - if (!interfaceDecl) - return; - auto loweredVal = context->findLoweredDecl(interfaceDecl); - if (!loweredVal) - { - return; - } - IRInst* irFuncType = irFuncVal->typeUse.get(); - auto irInterfaceType = cast(loweredVal->val); - auto key = getInterfaceRequirementKey(decl); - for (UInt i = 0; i < irInterfaceType->getOperandCount(); i++) - { - auto operand = cast(irInterfaceType->getOperand(i)); - if (operand->getRequirementKey() == key) - { - operand->setRequirementVal(irFuncType); - return; - } - } - SLANG_UNREACHABLE("associating interface function declaration:" - "requirement not found in the interface type."); - } - LoweredValInfo visitGenericDecl(GenericDecl * genDecl) { // TODO: Should this just always visit/lower the inner decl? -- cgit v1.2.3 From 5b571955403b7562e62681157bb166b35c5d2e7b Mon Sep 17 00:00:00 2001 From: Yong He Date: Thu, 25 Jun 2020 18:26:12 -0700 Subject: Fixes. --- source/slang/slang-ir.h | 1 + source/slang/slang-lower-to-ir.cpp | 28 +++++++++++++++++++++++----- 2 files changed, 24 insertions(+), 5 deletions(-) (limited to 'source') diff --git a/source/slang/slang-ir.h b/source/slang/slang-ir.h index dadcce386..f3eccad25 100644 --- a/source/slang/slang-ir.h +++ b/source/slang/slang-ir.h @@ -1209,6 +1209,7 @@ 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); diff --git a/source/slang/slang-lower-to-ir.cpp b/source/slang/slang-lower-to-ir.cpp index ebc2e1b74..f95ffff81 100644 --- a/source/slang/slang-lower-to-ir.cpp +++ b/source/slang/slang-lower-to-ir.cpp @@ -5306,6 +5306,7 @@ struct DeclLoweringVisitor : DeclVisitor // Setup subContext for proper lowering `ThisType`, associated types and // the interface decl's self reference. subContext->thisType = getBuilder()->getThisType(); + // Create a temporary IR value for self references, this will be replaced // by actual interface type after we create the actual interface type. auto temporarySelf = subBuilder->createIntrinsicInst(nullptr, kIROp_undefined, 0, nullptr); @@ -5322,15 +5323,29 @@ struct DeclLoweringVisitor : DeclVisitor for (auto requirementDecl : decl->members) { - auto key = getInterfaceRequirementKey(requirementDecl); + auto entry = subBuilder->createInterfaceRequirementEntry( + getInterfaceRequirementKey(requirementDecl), + nullptr); IRInst* requirementVal = lowerDecl(subContext, requirementDecl).val; if (requirementVal) { auto reqType = requirementVal->getFullType(); - requirementVal->removeAndDeallocate(); - requirementVal = reqType; + 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; + } + } } - auto entry = subBuilder->createInterfaceRequirementEntry(key, requirementVal); requirementEntries.add(entry); // As a special case, any type constraints placed // on an associated type will *also* need to be turned @@ -5345,7 +5360,10 @@ struct DeclLoweringVisitor : DeclVisitor getBuilder()->getWitnessTableType(lowerType(context, constraintDecl->getSup().type)))); } } - context->env->mapDeclToValue[requirementDecl] = LoweredValInfo::simple(entry); + // 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)); } IRInterfaceType* irInterface = subBuilder->createInterfaceType( -- cgit v1.2.3 From dd88ba1271417d0ba27b0a82194b977eee873b3d Mon Sep 17 00:00:00 2001 From: Yong He Date: Thu, 25 Jun 2020 19:03:51 -0700 Subject: Fixes --- source/slang/slang-lower-to-ir.cpp | 46 ++++++++++++++++++++++++-------------- 1 file changed, 29 insertions(+), 17 deletions(-) (limited to 'source') diff --git a/source/slang/slang-lower-to-ir.cpp b/source/slang/slang-lower-to-ir.cpp index f95ffff81..d18cd3bab 100644 --- a/source/slang/slang-lower-to-ir.cpp +++ b/source/slang/slang-lower-to-ir.cpp @@ -5307,10 +5307,6 @@ struct DeclLoweringVisitor : DeclVisitor // the interface decl's self reference. subContext->thisType = getBuilder()->getThisType(); - // Create a temporary IR value for self references, this will be replaced - // by actual interface type after we create the actual interface type. - auto temporarySelf = subBuilder->createIntrinsicInst(nullptr, kIROp_undefined, 0, nullptr); - subContext->env->mapDeclToValue[decl] = LoweredValInfo::simple(temporarySelf); for (auto member : decl->members) { if (as(member)) @@ -5319,7 +5315,28 @@ struct DeclLoweringVisitor : DeclVisitor } } - List requirementEntries; + // 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(requirementDecl)) + { + operandCount += associatedTypeDecl->getMembersOfType().getCount(); + } + } + + // Allocate an IRInterfaceType with the `operandCount` operands. + IRInterfaceType* irInterface = subBuilder->createInterfaceType(operandCount, nullptr); + + // Add `irInterface` to decl mapping now to prevent cyclic lowering. + setValue(subContext, decl, LoweredValInfo::simple(irInterface)); + + UInt entryIndex = 0; for (auto requirementDecl : decl->members) { @@ -5346,18 +5363,19 @@ struct DeclLoweringVisitor : DeclVisitor } } } - requirementEntries.add(entry); - // As a special case, any type constraints placed - // on an associated type will *also* need to be turned - // into requirement keys for this interface. + irInterface->setOperand(entryIndex, entry); + entryIndex++; + // Add addtional requirements for type constraints placed + // on an associated types. if (auto associatedTypeDecl = as(requirementDecl)) { for (auto constraintDecl : associatedTypeDecl->getMembersOfType()) { auto constraintKey = getInterfaceRequirementKey(constraintDecl); - requirementEntries.add( + irInterface->setOperand(entryIndex, subBuilder->createInterfaceRequirementEntry(constraintKey, getBuilder()->getWitnessTableType(lowerType(context, constraintDecl->getSup().type)))); + entryIndex++; } } // Add lowered requirement entry to current decl mapping to prevent @@ -5366,9 +5384,7 @@ struct DeclLoweringVisitor : DeclVisitor setValue(context, requirementDecl, LoweredValInfo::simple(entry)); } - IRInterfaceType* irInterface = subBuilder->createInterfaceType( - requirementEntries.getCount(), - reinterpret_cast(requirementEntries.getBuffer())); + addNameHint(context, irInterface, decl); addLinkageDecoration(context, irInterface, decl); subBuilder->setInsertInto(irInterface); @@ -5380,10 +5396,6 @@ struct DeclLoweringVisitor : DeclVisitor addTargetIntrinsicDecorations(irInterface, decl); auto finalVal = finishOuterGenerics(subBuilder, irInterface, outerGeneric); - // Now we can replace all self references in the requirement types to the - // actual interface type we generated. - temporarySelf->replaceUsesWith(finalVal); - temporarySelf->removeAndDeallocate(); return LoweredValInfo::simple(finalVal); } -- cgit v1.2.3 From 4cf71197650ec1bf04c54783823a315cc7d477a5 Mon Sep 17 00:00:00 2001 From: Yong He Date: Thu, 25 Jun 2020 19:56:39 -0700 Subject: Add a TODO comment for generic interface requirement key --- source/slang/slang-lower-to-ir.cpp | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'source') diff --git a/source/slang/slang-lower-to-ir.cpp b/source/slang/slang-lower-to-ir.cpp index d18cd3bab..58f23b0c8 100644 --- a/source/slang/slang-lower-to-ir.cpp +++ b/source/slang/slang-lower-to-ir.cpp @@ -1001,6 +1001,10 @@ 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(requirementDecl)) return getInterfaceRequirementKey(context, genericDecl->inner); IRStructKey* requirementKey = nullptr; -- cgit v1.2.3