diff --git a/flang/include/flang/Lower/AbstractConverter.h b/flang/include/flang/Lower/AbstractConverter.h index 477c8164a18ea..c792e75f11464 100644 --- a/flang/include/flang/Lower/AbstractConverter.h +++ b/flang/include/flang/Lower/AbstractConverter.h @@ -212,12 +212,10 @@ class AbstractConverter { /// Register a runtime derived type information object symbol to ensure its /// object will be generated as a global. - virtual void registerRuntimeTypeInfo(mlir::Location loc, - SymbolRef typeInfoSym) = 0; - - virtual void registerDispatchTableInfo( - mlir::Location loc, - const Fortran::semantics::DerivedTypeSpec *typeSpec) = 0; + virtual void + registerTypeInfo(mlir::Location loc, SymbolRef typeInfoSym, + const Fortran::semantics::DerivedTypeSpec &typeSpec, + fir::RecordType type) = 0; //===--------------------------------------------------------------------===// // Locations diff --git a/flang/include/flang/Optimizer/Builder/FIRBuilder.h b/flang/include/flang/Optimizer/Builder/FIRBuilder.h index bba8bc23c26aa..0b36186d68a46 100644 --- a/flang/include/flang/Optimizer/Builder/FIRBuilder.h +++ b/flang/include/flang/Optimizer/Builder/FIRBuilder.h @@ -254,11 +254,6 @@ class FirOpBuilder : public mlir::OpBuilder, public mlir::OpBuilder::Listener { bodyBuilder, linkage); } - /// Create a fir::DispatchTable operation. - fir::DispatchTableOp createDispatchTableOp(mlir::Location loc, - llvm::StringRef name, - llvm::StringRef parentName); - /// Convert a StringRef string into a fir::StringLitOp. fir::StringLitOp createStringLitOp(mlir::Location loc, llvm::StringRef string); diff --git a/flang/include/flang/Optimizer/Dialect/FIROps.td b/flang/include/flang/Optimizer/Dialect/FIROps.td index a57add9f73197..0329a5325a21c 100644 --- a/flang/include/flang/Optimizer/Dialect/FIROps.td +++ b/flang/include/flang/Optimizer/Dialect/FIROps.td @@ -2778,6 +2778,10 @@ def fir_GlobalOp : fir_Op<"global", [IsolatedFromAbove, Symbol]> { (*this)->getAttrOfType( mlir::SymbolTable::getSymbolAttrName()).getValue()); } + + bool isInitialized() { + return getInitVal() || hasInitializationBody(); + } }]; } @@ -2809,20 +2813,31 @@ def fir_GlobalLenOp : fir_Op<"global_len", []> { def ImplicitFirTerminator : SingleBlockImplicitTerminator<"FirEndOp">; -def fir_DispatchTableOp : fir_Op<"dispatch_table", +def fir_TypeInfoOp : fir_Op<"type_info", [IsolatedFromAbove, Symbol, ImplicitFirTerminator]> { - let summary = "Dispatch table definition"; + let summary = "Derived type information"; let description = [{ - Define a dispatch table for a derived type with type-bound procedures. + Define extra information about a !fir.type<> that represents + a Fortran derived type. - A dispatch table is an untyped symbol that contains a list of associations + The optional dispatch table region defines a dispatch table with the derived + type type-bound procedures. It contains a list of associations between method identifiers and corresponding `FuncOp` symbols. - The ordering of associations in the map is determined by the front end. + The "no_init" flag indicates that this type has no components requiring default + initialization (including setting allocatable component to a clean deallocated + state). + + The "no_destroy" flag indicates that there are no allocatable components + that require deallocation. + + The "no_final" flag indicates that there are no final methods for this type, + for its parents ,or for components. + ```mlir - fir.dispatch_table @_QDTMquuzTfoo { + fir.type_info @_QMquuzTfoo noinit nofinal : !fir.type<_QMquuzTfoo{i:i32}> dispatch_table { fir.dt_entry method1, @_QFNMquuzTfooPmethod1AfooR fir.dt_entry method2, @_QFNMquuzTfooPmethod2AfooII } @@ -2831,32 +2846,46 @@ def fir_DispatchTableOp : fir_Op<"dispatch_table", let arguments = (ins SymbolNameAttr:$sym_name, - OptionalAttr:$parent + TypeAttr:$type, + OptionalAttr:$parent_type, + UnitAttr:$no_init, + UnitAttr:$no_destroy, + UnitAttr:$no_final ); - let hasCustomAssemblyFormat = 1; let hasVerifier = 1; - let regions = (region AnyRegion:$region); + let regions = (region MaxSizedRegion<1>:$dispatch_table); - let skipDefaultBuilders = 1; let builders = [ - OpBuilder<(ins "llvm::StringRef":$name, "mlir::Type":$type, - "llvm::StringRef":$parent, + OpBuilder<(ins "fir::RecordType":$type, "fir::RecordType":$parent_type, CArg<"llvm::ArrayRef", "{}">:$attrs)> ]; - let extraClassDeclaration = [{ - static constexpr llvm::StringRef getParentAttrNameStr() { return "parent"; } - static constexpr llvm::StringRef getExtendsKeyword() { return "extends"; } + let assemblyFormat = [{ + $sym_name (`noinit` $no_init^)? (`nodestroy` $no_destroy^)? + (`nofinal` $no_final^)? (`extends` $parent_type^)? attr-dict `:` $type + (`dispatch_table` $dispatch_table^)? + }]; - mlir::Block &getBlock() { - return getRegion().front(); + let extraClassDeclaration = [{ + fir::RecordType getRecordType() { + return mlir::cast(getType()); + } + fir::RecordType getIfParentType() { + if (auto parentType = getParentType()) + return mlir::cast(*parentType); + return {}; + } + std::optional getIfParentName() { + if (auto parentType = getIfParentType()) + return parentType.getName(); + return std::nullopt; } }]; } -def fir_DTEntryOp : fir_Op<"dt_entry", [HasParent<"DispatchTableOp">]> { +def fir_DTEntryOp : fir_Op<"dt_entry", [HasParent<"TypeInfoOp">]> { let summary = "map entry in a dispatch table"; let description = [{ diff --git a/flang/include/flang/Optimizer/Support/Utils.h b/flang/include/flang/Optimizer/Support/Utils.h index ebf506543ebf4..d5b045924f3c0 100644 --- a/flang/include/flang/Optimizer/Support/Utils.h +++ b/flang/include/flang/Optimizer/Support/Utils.h @@ -36,21 +36,22 @@ using BindingTables = llvm::DenseMap; inline void buildBindingTables(BindingTables &bindingTables, mlir::ModuleOp mod) { - // The binding tables are defined in FIR from lowering as fir.dispatch_table - // operation. Go through each binding tables and store the procedure name and + // The binding tables are defined in FIR after lowering inside fir.type_info + // operations. Go through each binding tables and store the procedure name and // binding index for later use by the fir.dispatch conversion pattern. - for (auto dispatchTableOp : mod.getOps()) { + for (auto typeInfo : mod.getOps()) { unsigned bindingIdx = 0; BindingTable bindings; - if (dispatchTableOp.getRegion().empty()) { - bindingTables[dispatchTableOp.getSymName()] = bindings; + if (typeInfo.getDispatchTable().empty()) { + bindingTables[typeInfo.getSymName()] = bindings; continue; } - for (auto dtEntry : dispatchTableOp.getBlock().getOps()) { + for (auto dtEntry : + typeInfo.getDispatchTable().front().getOps()) { bindings[dtEntry.getMethod()] = bindingIdx; ++bindingIdx; } - bindingTables[dispatchTableOp.getSymName()] = bindings; + bindingTables[typeInfo.getSymName()] = bindings; } } diff --git a/flang/lib/Lower/Bridge.cpp b/flang/lib/Lower/Bridge.cpp index ee838b3b4a546..5ac4d822faaae 100644 --- a/flang/lib/Lower/Bridge.cpp +++ b/flang/lib/Lower/Bridge.cpp @@ -137,34 +137,41 @@ struct ConstructContext { Fortran::lower::StatementContext &stmtCtx; // construct exit code }; -/// Helper class to generate the runtime type info global data. This data -/// is required to describe the derived type to the runtime so that it can -/// operate over it. It must be ensured this data will be generated for every -/// derived type lowered in the current translated unit. However, this data +/// Helper class to generate the runtime type info global data and the +/// fir.type_info operations that contain the dipatch tables (if any). +/// The type info global data is required to describe the derived type to the +/// runtime so that it can operate over it. +/// It must be ensured these operations will be generated for every derived type +/// lowered in the current translated unit. However, these operations /// cannot be generated before FuncOp have been created for functions since the /// initializers may take their address (e.g for type bound procedures). This -/// class allows registering all the required runtime type info while it is not -/// possible to create globals, and to generate this data after function -/// lowering. -class RuntimeTypeInfoConverter { +/// class allows registering all the required type info while it is not +/// possible to create GlobalOp/TypeInfoOp, and to generate this data afte +/// function lowering. +class TypeInfoConverter { /// Store the location and symbols of derived type info to be generated. /// The location of the derived type instantiation is also stored because - /// runtime type descriptor symbol are compiler generated and cannot be mapped - /// to user code on their own. - struct TypeInfoSymbol { + /// runtime type descriptor symbols are compiler generated and cannot be + /// mapped to user code on their own. + struct TypeInfo { Fortran::semantics::SymbolRef symbol; + const Fortran::semantics::DerivedTypeSpec &typeSpec; + fir::RecordType type; mlir::Location loc; }; public: - void registerTypeInfoSymbol(Fortran::lower::AbstractConverter &converter, - mlir::Location loc, - Fortran::semantics::SymbolRef typeInfoSym) { + void registerTypeInfo(Fortran::lower::AbstractConverter &converter, + mlir::Location loc, + Fortran::semantics::SymbolRef typeInfoSym, + const Fortran::semantics::DerivedTypeSpec &typeSpec, + fir::RecordType type) { if (seen.contains(typeInfoSym)) return; seen.insert(typeInfoSym); if (!skipRegistration) { - registeredTypeInfoSymbols.emplace_back(TypeInfoSymbol{typeInfoSym, loc}); + registeredTypeInfo.emplace_back( + TypeInfo{typeInfoSym, typeSpec, type, loc}); return; } // Once the registration is closed, symbols cannot be added to the @@ -172,67 +179,59 @@ class RuntimeTypeInfoConverter { // However, after registration is closed, it is safe to directly generate // the globals because all FuncOps whose addresses may be required by the // initializers have been generated. - Fortran::lower::createRuntimeTypeInfoGlobal(converter, loc, - typeInfoSym.get()); + createTypeInfoOpAndGlobal(converter, + TypeInfo{typeInfoSym, typeSpec, type, loc}); } - void createTypeInfoGlobals(Fortran::lower::AbstractConverter &converter) { + void createTypeInfo(Fortran::lower::AbstractConverter &converter) { skipRegistration = true; - for (const TypeInfoSymbol &info : registeredTypeInfoSymbols) - Fortran::lower::createRuntimeTypeInfoGlobal(converter, info.loc, - info.symbol.get()); - registeredTypeInfoSymbols.clear(); + for (const TypeInfo &info : registeredTypeInfo) + createTypeInfoOpAndGlobal(converter, info); + registeredTypeInfo.clear(); } private: - /// Store the runtime type descriptors that will be required for the - /// derived type that have been converted to FIR derived types. - llvm::SmallVector registeredTypeInfoSymbols; - /// Create derived type runtime info global immediately without storing the - /// symbol in registeredTypeInfoSymbols. - bool skipRegistration = false; - /// Track symbols symbols processed during and after the registration - /// to avoid infinite loops between type conversions and global variable - /// creation. - llvm::SmallSetVector seen; -}; - -class DispatchTableConverter { - struct DispatchTableInfo { - const Fortran::semantics::DerivedTypeSpec *typeSpec; - mlir::Location loc; - }; - -public: - void registerTypeSpec(Fortran::lower::AbstractConverter &converter, - mlir::Location loc, - const Fortran::semantics::DerivedTypeSpec *typeSpec) { - assert(typeSpec && "type spec is null"); - std::string dtName = converter.mangleName(*typeSpec); - if (seen.contains(dtName) || dtName.find("__fortran") != std::string::npos) - return; - seen.insert(dtName); - registeredDispatchTableInfo.emplace_back(DispatchTableInfo{typeSpec, loc}); - } - - void createDispatchTableOps(Fortran::lower::AbstractConverter &converter) { - for (const DispatchTableInfo &info : registeredDispatchTableInfo) { - std::string dtName = converter.mangleName(*info.typeSpec); - const Fortran::semantics::DerivedTypeSpec *parent = - Fortran::evaluate::GetParentTypeSpec(*info.typeSpec); - fir::FirOpBuilder &builder = converter.getFirOpBuilder(); - fir::DispatchTableOp dt = builder.createDispatchTableOp( - info.loc, dtName, parent ? converter.mangleName(*parent) : ""); - auto insertPt = builder.saveInsertionPoint(); - const Fortran::semantics::Scope *scope = info.typeSpec->scope(); - if (!scope) - scope = info.typeSpec->typeSymbol().scope(); - Fortran::semantics::SymbolVector bindings = - Fortran::semantics::CollectBindings(*scope); - - if (!bindings.empty()) - builder.createBlock(&dt.getRegion()); - + void createTypeInfoOpAndGlobal(Fortran::lower::AbstractConverter &converter, + const TypeInfo &info) { + Fortran::lower::createRuntimeTypeInfoGlobal(converter, info.loc, + info.symbol.get()); + createTypeInfoOp(converter, info); + } + + void createTypeInfoOp(Fortran::lower::AbstractConverter &converter, + const TypeInfo &info) { + fir::RecordType parentType{}; + if (const Fortran::semantics::DerivedTypeSpec *parent = + Fortran::evaluate::GetParentTypeSpec(info.typeSpec)) + parentType = mlir::cast(converter.genType(*parent)); + + fir::FirOpBuilder &builder = converter.getFirOpBuilder(); + mlir::ModuleOp module = builder.getModule(); + fir::TypeInfoOp dt = + module.lookupSymbol(info.type.getName()); + if (dt) + return; // Already created. + auto insertPt = builder.saveInsertionPoint(); + builder.setInsertionPoint(module.getBody(), module.getBody()->end()); + dt = builder.create(info.loc, info.type, parentType); + + if (!info.typeSpec.HasDefaultInitialization(/*ignoreAllocatable=*/false, + /*ignorePointer=*/false)) + dt->setAttr(dt.getNoInitAttrName(), builder.getUnitAttr()); + if (!info.typeSpec.HasDestruction()) + dt->setAttr(dt.getNoDestroyAttrName(), builder.getUnitAttr()); + if (!Fortran::semantics::MayRequireFinalization(info.typeSpec)) + dt->setAttr(dt.getNoFinalAttrName(), builder.getUnitAttr()); + + const Fortran::semantics::Scope *scope = info.typeSpec.scope(); + if (!scope) + scope = info.typeSpec.typeSymbol().scope(); + assert(scope && "failed to find type scope"); + + Fortran::semantics::SymbolVector bindings = + Fortran::semantics::CollectBindings(*scope); + if (!bindings.empty()) { + builder.createBlock(&dt.getDispatchTable()); for (const Fortran::semantics::SymbolRef &binding : bindings) { const auto &details = binding.get().get(); @@ -244,20 +243,21 @@ class DispatchTableConverter { info.loc, mlir::StringAttr::get(builder.getContext(), tbpName), mlir::SymbolRefAttr::get(builder.getContext(), bindingName)); } - if (!bindings.empty()) - builder.create(info.loc); - builder.restoreInsertionPoint(insertPt); + builder.create(info.loc); } - registeredDispatchTableInfo.clear(); + builder.restoreInsertionPoint(insertPt); } -private: - /// Store the semantic DerivedTypeSpec that will be required to generate the - /// dispatch table. - llvm::SmallVector registeredDispatchTableInfo; - - /// Track processed type specs to avoid multiple creation. - llvm::StringSet<> seen; + /// Store the front-end data that will be required to generate the type info + /// for the derived types that have been converted to fir.type<>. + llvm::SmallVector registeredTypeInfo; + /// Create derived type info immediately without storing the + /// symbol in registeredTypeInfo. + bool skipRegistration = false; + /// Track symbols symbols processed during and after the registration + /// to avoid infinite loops between type conversions and global variable + /// creation. + llvm::SmallSetVector seen; }; using IncrementLoopNestInfo = llvm::SmallVector; @@ -334,13 +334,10 @@ class FirConverter : public Fortran::lower::AbstractConverter { /// Once all the code has been translated, create runtime type info /// global data structure for the derived types that have been - /// processed. - createGlobalOutsideOfFunctionLowering( - [&]() { runtimeTypeInfoConverter.createTypeInfoGlobals(*this); }); - - /// Create the dispatch tables for derived types. + /// processed as well as the fir.type_info operations with the + /// dispatch tables. createGlobalOutsideOfFunctionLowering( - [&]() { dispatchTableConverter.createDispatchTableOps(*this); }); + [&]() { typeInfoConverter.createTypeInfo(*this); }); // Create the list of any environment defaults for the runtime to set. The // runtime default list is only created if there is a main program to ensure @@ -875,16 +872,11 @@ class FirConverter : public Fortran::lower::AbstractConverter { hostAssocTuple = val; } - void registerRuntimeTypeInfo( - mlir::Location loc, - Fortran::lower::SymbolRef typeInfoSym) override final { - runtimeTypeInfoConverter.registerTypeInfoSymbol(*this, loc, typeInfoSym); - } - - void registerDispatchTableInfo( - mlir::Location loc, - const Fortran::semantics::DerivedTypeSpec *typeSpec) override final { - dispatchTableConverter.registerTypeSpec(*this, loc, typeSpec); + void registerTypeInfo(mlir::Location loc, + Fortran::lower::SymbolRef typeInfoSym, + const Fortran::semantics::DerivedTypeSpec &typeSpec, + fir::RecordType type) override final { + typeInfoConverter.registerTypeInfo(*this, loc, typeInfoSym, typeSpec, type); } llvm::StringRef @@ -4811,8 +4803,7 @@ class FirConverter : public Fortran::lower::AbstractConverter { Fortran::lower::pft::Evaluation *evalPtr = nullptr; Fortran::lower::SymMap localSymbols; Fortran::parser::CharBlock currentPosition; - RuntimeTypeInfoConverter runtimeTypeInfoConverter; - DispatchTableConverter dispatchTableConverter; + TypeInfoConverter typeInfoConverter; // Stack to manage object deallocation and finalization at construct exits. llvm::SmallVector activeConstructStack; diff --git a/flang/lib/Lower/ConvertType.cpp b/flang/lib/Lower/ConvertType.cpp index efaeba29b762a..22b83efe8678b 100644 --- a/flang/lib/Lower/ConvertType.cpp +++ b/flang/lib/Lower/ConvertType.cpp @@ -426,14 +426,12 @@ struct TypeBuilderImpl { } LLVM_DEBUG(llvm::dbgs() << "derived type: " << rec << '\n'); - converter.registerDispatchTableInfo(loc, &tySpec); - // Generate the type descriptor object if any if (const Fortran::semantics::Scope *derivedScope = tySpec.scope() ? tySpec.scope() : tySpec.typeSymbol().scope()) if (const Fortran::semantics::Symbol *typeInfoSym = derivedScope->runtimeDerivedTypeDescription()) - converter.registerRuntimeTypeInfo(loc, *typeInfoSym); + converter.registerTypeInfo(loc, *typeInfoSym, tySpec, rec); return rec; } diff --git a/flang/lib/Optimizer/Builder/FIRBuilder.cpp b/flang/lib/Optimizer/Builder/FIRBuilder.cpp index 4dde918af7fa6..a14f3106a7232 100644 --- a/flang/lib/Optimizer/Builder/FIRBuilder.cpp +++ b/flang/lib/Optimizer/Builder/FIRBuilder.cpp @@ -299,18 +299,6 @@ fir::GlobalOp fir::FirOpBuilder::createGlobal( return glob; } -fir::DispatchTableOp fir::FirOpBuilder::createDispatchTableOp( - mlir::Location loc, llvm::StringRef name, llvm::StringRef parentName) { - auto module = getModule(); - auto insertPt = saveInsertionPoint(); - if (auto dt = module.lookupSymbol(name)) - return dt; - setInsertionPoint(module.getBody(), module.getBody()->end()); - auto dt = create(loc, name, mlir::Type{}, parentName); - restoreInsertionPoint(insertPt); - return dt; -} - mlir::Value fir::FirOpBuilder::convertWithSemantics(mlir::Location loc, mlir::Type toTy, mlir::Value val, diff --git a/flang/lib/Optimizer/CodeGen/CodeGen.cpp b/flang/lib/Optimizer/CodeGen/CodeGen.cpp index d1b7f3de93b46..5bf6b87615c68 100644 --- a/flang/lib/Optimizer/CodeGen/CodeGen.cpp +++ b/flang/lib/Optimizer/CodeGen/CodeGen.cpp @@ -1026,14 +1026,15 @@ struct ConvertOpConversion : public FIROpConversion { } }; -/// `fir.disptach_table` operation has no specific CodeGen. The operation is -/// only used to carry information during FIR to FIR passes. -struct DispatchTableOpConversion - : public FIROpConversion { +/// `fir.type_info` operation has no specific CodeGen. The operation is +/// only used to carry information during FIR to FIR passes. It may be used +/// in the future to generate the runtime type info data structures instead +/// of generating them in lowering. +struct TypeInfoOpConversion : public FIROpConversion { using FIROpConversion::FIROpConversion; mlir::LogicalResult - matchAndRewrite(fir::DispatchTableOp op, OpAdaptor, + matchAndRewrite(fir::TypeInfoOp op, OpAdaptor, mlir::ConversionPatternRewriter &rewriter) const override { rewriter.eraseOp(op); return mlir::success(); @@ -3787,19 +3788,19 @@ class FIRToLLVMLowering BoxProcHostOpConversion, BoxRankOpConversion, BoxTypeCodeOpConversion, BoxTypeDescOpConversion, CallOpConversion, CmpcOpConversion, ConstcOpConversion, ConvertOpConversion, CoordinateOpConversion, - DispatchTableOpConversion, DTEntryOpConversion, DivcOpConversion, - EmboxOpConversion, EmboxCharOpConversion, EmboxProcOpConversion, - ExtractValueOpConversion, FieldIndexOpConversion, FirEndOpConversion, - FreeMemOpConversion, GlobalLenOpConversion, GlobalOpConversion, - HasValueOpConversion, InsertOnRangeOpConversion, - InsertValueOpConversion, IsPresentOpConversion, - LenParamIndexOpConversion, LoadOpConversion, MulcOpConversion, - NegcOpConversion, NoReassocOpConversion, SelectCaseOpConversion, - SelectOpConversion, SelectRankOpConversion, SelectTypeOpConversion, - ShapeOpConversion, ShapeShiftOpConversion, ShiftOpConversion, - SliceOpConversion, StoreOpConversion, StringLitOpConversion, - SubcOpConversion, TypeDescOpConversion, UnboxCharOpConversion, - UnboxProcOpConversion, UndefOpConversion, UnreachableOpConversion, + DTEntryOpConversion, DivcOpConversion, EmboxOpConversion, + EmboxCharOpConversion, EmboxProcOpConversion, ExtractValueOpConversion, + FieldIndexOpConversion, FirEndOpConversion, FreeMemOpConversion, + GlobalLenOpConversion, GlobalOpConversion, HasValueOpConversion, + InsertOnRangeOpConversion, InsertValueOpConversion, + IsPresentOpConversion, LenParamIndexOpConversion, LoadOpConversion, + MulcOpConversion, NegcOpConversion, NoReassocOpConversion, + SelectCaseOpConversion, SelectOpConversion, SelectRankOpConversion, + SelectTypeOpConversion, ShapeOpConversion, ShapeShiftOpConversion, + ShiftOpConversion, SliceOpConversion, StoreOpConversion, + StringLitOpConversion, SubcOpConversion, TypeDescOpConversion, + TypeInfoOpConversion, UnboxCharOpConversion, UnboxProcOpConversion, + UndefOpConversion, UnreachableOpConversion, UnrealizedConversionCastOpConversion, XArrayCoorOpConversion, XEmboxOpConversion, XReboxOpConversion, ZeroOpConversion>(typeConverter, options); diff --git a/flang/lib/Optimizer/Dialect/FIROps.cpp b/flang/lib/Optimizer/Dialect/FIROps.cpp index 962b87acd5a80..d60e5e9657ea0 100644 --- a/flang/lib/Optimizer/Dialect/FIROps.cpp +++ b/flang/lib/Optimizer/Dialect/FIROps.cpp @@ -1161,76 +1161,37 @@ mlir::FunctionType fir::DispatchOp::getFunctionType() { } //===----------------------------------------------------------------------===// -// DispatchTableOp +// TypeInfoOp //===----------------------------------------------------------------------===// -mlir::ParseResult fir::DispatchTableOp::parse(mlir::OpAsmParser &parser, - mlir::OperationState &result) { - // Parse the name as a symbol reference attribute. - mlir::StringAttr nameAttr; - if (parser.parseSymbolName(nameAttr, mlir::SymbolTable::getSymbolAttrName(), - result.attributes)) - return mlir::failure(); - - if (!failed(parser.parseOptionalKeyword(getExtendsKeyword()))) { - mlir::StringAttr parent; - if (parser.parseLParen() || - parser.parseAttribute(parent, getParentAttrNameStr(), - result.attributes) || - parser.parseRParen()) - return mlir::failure(); - } - - // Parse the optional table body. - mlir::Region *body = result.addRegion(); - mlir::OptionalParseResult parseResult = parser.parseOptionalRegion(*body); - if (parseResult.has_value() && failed(*parseResult)) - return mlir::failure(); - - fir::DispatchTableOp::ensureTerminator(*body, parser.getBuilder(), - result.location); - return mlir::success(); +void fir::TypeInfoOp::build(mlir::OpBuilder &builder, + mlir::OperationState &result, fir::RecordType type, + fir::RecordType parentType, + llvm::ArrayRef attrs) { + result.addRegion(); + result.addAttribute(mlir::SymbolTable::getSymbolAttrName(), + builder.getStringAttr(type.getName())); + result.addAttribute(getTypeAttrName(result.name), mlir::TypeAttr::get(type)); + if (parentType) + result.addAttribute(getParentTypeAttrName(result.name), + mlir::TypeAttr::get(parentType)); + result.addAttributes(attrs); } -void fir::DispatchTableOp::print(mlir::OpAsmPrinter &p) { - p << ' '; - p.printSymbolName(getSymName()); - if (getParent()) - p << ' ' << getExtendsKeyword() << '(' - << (*this)->getAttr(getParentAttrNameStr()) << ')'; +mlir::LogicalResult fir::TypeInfoOp::verify() { + if (!getDispatchTable().empty()) + for (auto &op : getDispatchTable().front().without_terminator()) + if (!mlir::isa(op)) + return op.emitOpError("dispatch table must contain dt_entry"); - mlir::Region &body = getOperation()->getRegion(0); - if (!body.empty()) { - p << ' '; - p.printRegion(body, /*printEntryBlockArgs=*/false, - /*printBlockTerminators=*/false); - } -} + if (!mlir::isa(getType())) + return emitOpError("type must be a fir.type"); -mlir::LogicalResult fir::DispatchTableOp::verify() { - if (getRegion().empty()) - return mlir::success(); - for (auto &op : getBlock()) - if (!mlir::isa(op)) - return op.emitOpError("dispatch table must contain dt_entry"); + if (getParentType() && !mlir::isa(*getParentType())) + return emitOpError("parent_type must be a fir.type"); return mlir::success(); } -void fir::DispatchTableOp::build(mlir::OpBuilder &builder, - mlir::OperationState &result, - llvm::StringRef name, mlir::Type type, - llvm::StringRef parent, - llvm::ArrayRef attrs) { - result.addRegion(); - result.addAttribute(mlir::SymbolTable::getSymbolAttrName(), - builder.getStringAttr(name)); - if (!parent.empty()) - result.addAttribute(getParentAttrNameStr(), builder.getStringAttr(parent)); - // result.addAttribute(getSymbolAttrNameStr(), - // mlir::SymbolRefAttr::get(builder.getContext(), name)); - result.addAttributes(attrs); -} - //===----------------------------------------------------------------------===// // EmboxOp //===----------------------------------------------------------------------===// diff --git a/flang/lib/Optimizer/Transforms/PolymorphicOpConversion.cpp b/flang/lib/Optimizer/Transforms/PolymorphicOpConversion.cpp index f5d0602c270cc..93efea434cb12 100644 --- a/flang/lib/Optimizer/Transforms/PolymorphicOpConversion.cpp +++ b/flang/lib/Optimizer/Transforms/PolymorphicOpConversion.cpp @@ -70,7 +70,7 @@ class SelectTypeConv : public OpConversionPattern { mlir::PatternRewriter &rewriter, fir::KindMapping &kindMap) const; - llvm::SmallSet collectAncestors(fir::DispatchTableOp dt, + llvm::SmallSet collectAncestors(fir::TypeInfoOp dt, mlir::ModuleOp mod) const; // Mutex used to guard insertion of mlir::func::FuncOp in the module. @@ -305,7 +305,7 @@ mlir::LogicalResult SelectTypeConv::matchAndRewrite( if (auto a = typeGuards[t].dyn_cast()) { if (auto recTy = a.getType().dyn_cast()) { - auto dt = mod.lookupSymbol(recTy.getName()); + auto dt = mod.lookupSymbol(recTy.getName()); assert(dt && "dispatch table not found"); llvm::SmallSet ancestors = collectAncestors(dt, mod); @@ -462,14 +462,12 @@ SelectTypeConv::genTypeDescCompare(mlir::Location loc, mlir::Value selector, } llvm::SmallSet -SelectTypeConv::collectAncestors(fir::DispatchTableOp dt, - mlir::ModuleOp mod) const { +SelectTypeConv::collectAncestors(fir::TypeInfoOp dt, mlir::ModuleOp mod) const { llvm::SmallSet ancestors; - if (!dt.getParent().has_value()) - return ancestors; - while (dt.getParent().has_value()) { - ancestors.insert(*dt.getParent()); - dt = mod.lookupSymbol(*dt.getParent()); + while (auto parentName = dt.getIfParentName()) { + ancestors.insert(*parentName); + dt = mod.lookupSymbol(*parentName); + assert(dt && "parent type info not generated"); } return ancestors; } diff --git a/flang/test/Fir/array-value-copy-cam4.fir b/flang/test/Fir/array-value-copy-cam4.fir index 3b3b0082743ce..3bb465cfaa59f 100644 --- a/flang/test/Fir/array-value-copy-cam4.fir +++ b/flang/test/Fir/array-value-copy-cam4.fir @@ -98,5 +98,5 @@ module { return } func.func private @_QPinit(!fir.ref>) - fir.dispatch_table @_QMcam4Tpbuf_fld + fir.type_info @_QMcam4Tpbuf_fld : !fir.type<_QMcam4Tpbuf_fld{fld_ptr:!fir.box>>}> } diff --git a/flang/test/Fir/convert-to-llvm-invalid.fir b/flang/test/Fir/convert-to-llvm-invalid.fir index fcd042f83bd03..9f003e4eb7d59 100644 --- a/flang/test/Fir/convert-to-llvm-invalid.fir +++ b/flang/test/Fir/convert-to-llvm-invalid.fir @@ -2,12 +2,6 @@ // RUN: fir-opt --split-input-file --fir-to-llvm-ir="target=x86_64-unknown-linux-gnu" --verify-diagnostics %s -// Verify that `fir.dt_entry` requires a parent op - -// expected-error@+1{{'fir.dt_entry' op expects parent op 'fir.dispatch_table'}} -fir.dt_entry "method", @method_impl - -// ----- // Test `fir.shape` conversion failure because the op has uses. @@ -82,11 +76,28 @@ func.func @bar_select_type(%arg : !fir.class>) -> i3 // Verify that `fir.dt_entry` requires a parent op -// expected-error@+1{{'fir.dt_entry' op expects parent op 'fir.dispatch_table'}} +// expected-error@+1{{'fir.dt_entry' op expects parent op 'fir.type_info'}} fir.dt_entry "method", @method_impl // ----- +// expected-error@+1{{'fir.type_info' op type must be a fir.type}} +fir.type_info @bad : i32 + +// ----- + +// expected-error@+1{{'fir.type_info' op parent_type must be a fir.type}} +fir.type_info @bad extends f32 : !fir.type + +// ----- + +fir.type_info @bad : !fir.type dispatch_table { + // expected-error@+1{{dispatch table must contain dt_entry}} + %zero = arith.constant 0 : i32 +} + +// ----- + // `fir.coordinate_of` - dynamically sized arrays are not supported func.func @coordinate_of_dynamic_array(%arg0: !fir.ref>>, %arg1: index) { // expected-error@+2{{fir.coordinate_of with a dynamic element size is unsupported}} diff --git a/flang/test/Fir/dispatch.f90 b/flang/test/Fir/dispatch.f90 index 933c769d3e169..ce60939410de6 100644 --- a/flang/test/Fir/dispatch.f90 +++ b/flang/test/Fir/dispatch.f90 @@ -308,10 +308,10 @@ program test_type_to_class ! Check the layout of the binding table. This is easier to do in FIR than in ! LLVM IR. -! BT-LABEL: fir.dispatch_table @_QMdispatch1Tty_kindK10K20 -! BT-LABEL: fir.dispatch_table @_QMdispatch1Tty_kind_exK10K20 extends("_QMdispatch1Tty_kindK10K20") +! BT-LABEL: fir.type_info @_QMdispatch1Tty_kindK10K20 +! BT-LABEL: fir.type_info @_QMdispatch1Tty_kind_exK10K20 {{.*}}extends !fir.type<_QMdispatch1Tty_kindK10K20{{.*}}> -! BT-LABEL: fir.dispatch_table @_QMdispatch1Tp1 { +! BT-LABEL: fir.type_info @_QMdispatch1Tp1 ! BT: fir.dt_entry "aproc", @_QMdispatch1Paproc ! BT: fir.dt_entry "display1", @_QMdispatch1Pdisplay1_p1 ! BT: fir.dt_entry "display2", @_QMdispatch1Pdisplay2_p1 @@ -321,15 +321,15 @@ program test_type_to_class ! BT: fir.dt_entry "proc_with_values", @_QMdispatch1Pproc_p1 ! BT: } -! BT-LABEL: fir.dispatch_table @_QMdispatch1Ta1 { +! BT-LABEL: fir.type_info @_QMdispatch1Ta1 ! BT: fir.dt_entry "a1_proc", @_QMdispatch1Pa1_proc ! BT: } -! BT-LABEL: fir.dispatch_table @_QMdispatch1Ta2 extends("_QMdispatch1Ta1") { +! BT-LABEL: fir.type_info @_QMdispatch1Ta2 {{.*}}extends !fir.type<_QMdispatch1Ta1{{.*}}> ! BT: fir.dt_entry "a1_proc", @_QMdispatch1Pa2_proc ! BT: } -! BT-LABEL: fir.dispatch_table @_QMdispatch1Tp2 extends("_QMdispatch1Tp1") { +! BT-LABEL: fir.type_info @_QMdispatch1Tp2 {{.*}}extends !fir.type<_QMdispatch1Tp1{{.*}}> ! BT: fir.dt_entry "aproc", @_QMdispatch1Paproc ! BT: fir.dt_entry "display1", @_QMdispatch1Pdisplay1_p2 ! BT: fir.dt_entry "display2", @_QMdispatch1Pdisplay2_p2 diff --git a/flang/test/Fir/fir-ops.fir b/flang/test/Fir/fir-ops.fir index f2175ce5fb5ae..dd0fbb3be36c4 100644 --- a/flang/test/Fir/fir-ops.fir +++ b/flang/test/Fir/fir-ops.fir @@ -450,13 +450,16 @@ fir.global linkonce_odr @global_linkonce_odr : i32 { fir.has_value %0 : i32 } -// CHECK-LABEL: fir.dispatch_table @dispatch_tbl { +// CHECK-LABEL: fir.type_info @dispatch_tbl : !fir.type dispatch_table { // CHECK: fir.dt_entry "method", @method_impl // CHECK: } -fir.dispatch_table @dispatch_tbl { +fir.type_info @dispatch_tbl : !fir.type dispatch_table { fir.dt_entry "method", @method_impl } +// CHECK-LABEL: fir.type_info @test_type_info noinit nodestroy nofinal extends !fir.type : !fir.type +fir.type_info @test_type_info noinit nodestroy nofinal extends !fir.type : !fir.type + // CHECK-LABEL: func @compare_complex( // CHECK-SAME: [[VAL_151:%.*]]: !fir.complex<16>, [[VAL_152:%.*]]: !fir.complex<16>) { func.func @compare_complex(%a : !fir.complex<16>, %b : !fir.complex<16>) { diff --git a/flang/test/Lower/HLFIR/type-info.f90 b/flang/test/Lower/HLFIR/type-info.f90 new file mode 100644 index 0000000000000..e0716fd069020 --- /dev/null +++ b/flang/test/Lower/HLFIR/type-info.f90 @@ -0,0 +1,60 @@ +! Test generation of noinit, nofinal, and nodestroy fir.type_info attributes +! RUN: bbc -emit-hlfir %s -o - | FileCheck %s + +module tyinfo + type boring_type + end type + + type needs_final + contains + final :: needs_final_final + end type + + type needs_init1 + integer :: i =0 + end type + + type needs_init_and_destroy + integer, allocatable :: x + end type + + type needs_all + type(needs_final) :: x + type(needs_init_and_destroy) :: y + end type + + type, extends(needs_final) :: inherits_final + end type + type, extends(needs_init1) :: inherits_init + end type + type, extends(needs_init_and_destroy) :: inherits_init_and_destroy + end type + type, extends(needs_all) :: inherits_all + end type + + interface + subroutine needs_final_final(x) + type(needs_final), intent(inout) :: x + end subroutine + end interface + + type(boring_type) :: x1 + type(needs_final) :: x2 + type(needs_init1) :: x3 + type(needs_init_and_destroy) :: x4 + type(needs_all) :: x5 + type(inherits_final) :: x6 + type(inherits_init) :: x7 + type(inherits_init_and_destroy) :: x8 + type(inherits_all) :: x9 +end module + +! CHECK-DAG: fir.type_info @_QMtyinfoTboring_type noinit nodestroy nofinal : !fir.type<_QMtyinfoTboring_type> +! CHECK-DAG: fir.type_info @_QMtyinfoTneeds_final noinit : !fir.type<_QMtyinfoTneeds_final> +! CHECK-DAG: fir.type_info @_QMtyinfoTneeds_init1 nodestroy nofinal : !fir.type<_QMtyinfoTneeds_init1{i:i32}> +! CHECK-DAG: fir.type_info @_QMtyinfoTneeds_init_and_destroy nofinal : !fir.type<_QMtyinfoTneeds_init_and_destroy{x:!fir.box>}> +! CHECK-DAG: fir.type_info @_QMtyinfoTneeds_all : !fir.type<_QMtyinfoTneeds_all{x:!fir.type<_QMtyinfoTneeds_final>,y:!fir.type<_QMtyinfoTneeds_init_and_destroy{x:!fir.box>}>}> +! CHECK-DAG: fir.type_info @_QMtyinfoTinherits_final noinit extends !fir.type<_QMtyinfoTneeds_final> : !fir.type<_QMtyinfoTinherits_final> +! CHECK-DAG: fir.type_info @_QMtyinfoTinherits_init nodestroy nofinal extends !fir.type<_QMtyinfoTneeds_init1{i:i32}> : !fir.type<_QMtyinfoTinherits_init{i:i32}> +! CHECK-DAG: fir.type_info @_QMtyinfoTinherits_init_and_destroy nofinal extends !fir.type<_QMtyinfoTneeds_init_and_destroy{x:!fir.box>}> : !fir.type<_QMtyinfoTinherits_init_and_destroy{x:!fir.box>}> +! CHECK-DAG: fir.type_info @_QMtyinfoTinherits_all extends !fir.type<_QMtyinfoTneeds_all{x:!fir.type<_QMtyinfoTneeds_final>,y:!fir.type<_QMtyinfoTneeds_init_and_destroy{x:!fir.box>}>}> : !fir.type<_QMtyinfoTinherits_all{x:!fir.type<_QMtyinfoTneeds_final>,y:!fir.type<_QMtyinfoTneeds_init_and_destroy{x:!fir.box>}>}> diff --git a/flang/test/Lower/dispatch-table.f90 b/flang/test/Lower/dispatch-table.f90 index 7b5a5ec4a39e6..0df4981b3eaf8 100644 --- a/flang/test/Lower/dispatch-table.f90 +++ b/flang/test/Lower/dispatch-table.f90 @@ -1,6 +1,6 @@ ! RUN: bbc -polymorphic-type -emit-fir %s -o - | FileCheck %s -! Tests the generation of fir.dispatch_table operations. +! Tests the generation of fir.type_info operations. module polymorphic_types type p1 @@ -53,20 +53,20 @@ subroutine aproc3(p) end module -! CHECK-LABEL: fir.dispatch_table @_QMpolymorphic_typesTp1 { +! CHECK-LABEL: fir.type_info @_QMpolymorphic_typesTp1 ! CHECK: fir.dt_entry "aproc", @_QMpolymorphic_typesPaproc ! CHECK: fir.dt_entry "proc1", @_QMpolymorphic_typesPproc1_p1 ! CHECK: fir.dt_entry "zproc", @_QMpolymorphic_typesPzproc ! CHECK: } -! CHECK-LABEL: fir.dispatch_table @_QMpolymorphic_typesTp2 extends("_QMpolymorphic_typesTp1") { +! CHECK-LABEL: fir.type_info @_QMpolymorphic_typesTp2 {{.*}}extends !fir.type<_QMpolymorphic_typesTp1{{.*}}> ! CHECK: fir.dt_entry "aproc", @_QMpolymorphic_typesPaproc ! CHECK: fir.dt_entry "proc1", @_QMpolymorphic_typesPproc1_p2 ! CHECK: fir.dt_entry "zproc", @_QMpolymorphic_typesPzproc ! CHECK: fir.dt_entry "aproc2", @_QMpolymorphic_typesPaproc2 ! CHECK: } -! CHECK-LABEL: fir.dispatch_table @_QMpolymorphic_typesTp3 extends("_QMpolymorphic_typesTp2") { +! CHECK-LABEL: fir.type_info @_QMpolymorphic_typesTp3 {{.*}}extends !fir.type<_QMpolymorphic_typesTp2{{.*}}> ! CHECK: fir.dt_entry "aproc", @_QMpolymorphic_typesPaproc ! CHECK: fir.dt_entry "proc1", @_QMpolymorphic_typesPproc1_p2 ! CHECK: fir.dt_entry "zproc", @_QMpolymorphic_typesPzproc