diff --git a/flang/include/flang/Optimizer/CodeGen/CodeGen.h b/flang/include/flang/Optimizer/CodeGen/CodeGen.h --- a/flang/include/flang/Optimizer/CodeGen/CodeGen.h +++ b/flang/include/flang/Optimizer/CodeGen/CodeGen.h @@ -35,9 +35,24 @@ std::unique_ptr> createFirTargetRewritePass( const TargetRewriteOptions &options = TargetRewriteOptions()); -/// Convert FIR to the LLVM IR dialect +/// FIR to LLVM translation pass options. +struct FIRToLLVMPassOptions { + // Do not fail when type descriptors are not found when translating + // operations that uses them at the LLVM level like fir.embox. Instead, + // just use a null pointer. + // This is useful to test translating programs manually written where a + // frontend did not generate type descriptor data structures. However, note + // that this programs would crash at runtime if the derived type descriptors + // are required by the runtime, so this only an option to help debugging. + bool ignoreMissingTypeDescriptors = false; +}; + +/// Convert FIR to the LLVM IR dialect with default options. std::unique_ptr createFIRToLLVMPass(); +/// Convert FIR to the LLVM IR dialect +std::unique_ptr createFIRToLLVMPass(FIRToLLVMPassOptions options); + using LLVMIRLoweringPrinter = std::function; /// Convert the LLVM IR dialect to LLVM-IR proper diff --git a/flang/include/flang/Optimizer/Dialect/FIRTypes.td b/flang/include/flang/Optimizer/Dialect/FIRTypes.td --- a/flang/include/flang/Optimizer/Dialect/FIRTypes.td +++ b/flang/include/flang/Optimizer/Dialect/FIRTypes.td @@ -323,8 +323,6 @@ void finalize(llvm::ArrayRef lenPList, llvm::ArrayRef typeList); - - std::string translateNameToFrontendMangledName() const; detail::RecordTypeStorage const *uniqueKey() const; }]; diff --git a/flang/include/flang/Optimizer/Support/InternalNames.h b/flang/include/flang/Optimizer/Support/InternalNames.h --- a/flang/include/flang/Optimizer/Support/InternalNames.h +++ b/flang/include/flang/Optimizer/Support/InternalNames.h @@ -137,6 +137,11 @@ static bool belongsToModule(llvm::StringRef uniquedName, llvm::StringRef moduleName); + /// Given a mangled derived type name, get the name of the related derived + /// type descriptor object. Returns an empty string if \p mangledTypeName is + /// not a valid mangled derived type name. + static std::string getTypeDescriptorName(llvm::StringRef mangledTypeName); + private: static std::string intAsString(std::int64_t i); static std::string doKind(std::int64_t kind); diff --git a/flang/include/flang/Tools/CLOptions.inc b/flang/include/flang/Tools/CLOptions.inc --- a/flang/include/flang/Tools/CLOptions.inc +++ b/flang/include/flang/Tools/CLOptions.inc @@ -37,6 +37,18 @@ "place all array allocations more than elements on the heap"), llvm::cl::init(~static_cast(0)), llvm::cl::Hidden); +/// Shared option in tools to ignore missing runtime type descriptor objects +/// when translating FIR to LLVM. The resulting program will crash if the +/// runtime needs the derived type descriptors, this is only a debug option to +/// allow compiling manually written FIR programs involving derived types +/// without having to write the derived type descriptors which are normally +/// generated by the frontend. +static llvm::cl::opt ignoreMissingTypeDescriptors( + "ignore-missing-type-desc", + llvm::cl::desc("ignore failures to find derived type descriptors when " + "translating FIR to LLVM"), + llvm::cl::init(false), llvm::cl::Hidden); + namespace { /// Optimizer Passes DisableOption(CfgConversion, "cfg-conversion", "disable FIR to CFG pass"); @@ -107,7 +119,10 @@ } inline void addFIRToLLVMPass(mlir::PassManager &pm) { - addPassConditionally(pm, disableFirToLlvmIr, fir::createFIRToLLVMPass); + fir::FIRToLLVMPassOptions options; + options.ignoreMissingTypeDescriptors = ignoreMissingTypeDescriptors; + addPassConditionally(pm, disableFirToLlvmIr, + [&]() { return fir::createFIRToLLVMPass(options); }); } inline void addLLVMDialectToLLVMPass( diff --git a/flang/lib/Optimizer/CodeGen/CodeGen.cpp b/flang/lib/Optimizer/CodeGen/CodeGen.cpp --- a/flang/lib/Optimizer/CodeGen/CodeGen.cpp +++ b/flang/lib/Optimizer/CodeGen/CodeGen.cpp @@ -66,8 +66,9 @@ template class FIROpConversion : public mlir::ConvertOpToLLVMPattern { public: - explicit FIROpConversion(fir::LLVMTypeConverter &lowering) - : mlir::ConvertOpToLLVMPattern(lowering) {} + explicit FIROpConversion(fir::LLVMTypeConverter &lowering, + const fir::FIRToLLVMPassOptions &options) + : mlir::ConvertOpToLLVMPattern(lowering), options(options) {} protected: mlir::Type convertType(mlir::Type ty) const { @@ -251,6 +252,8 @@ fir::LLVMTypeConverter &lowerTy() const { return *static_cast(this->getTypeConverter()); } + + const fir::FIRToLLVMPassOptions &options; }; /// FIR conversion pattern template @@ -1259,7 +1262,8 @@ mlir::Value getTypeDescriptor(BOX box, mlir::ConversionPatternRewriter &rewriter, mlir::Location loc, fir::RecordType recType) const { - std::string name = recType.translateNameToFrontendMangledName(); + std::string name = + fir::NameUniquer::getTypeDescriptorName(recType.getName()); auto module = box->template getParentOfType(); if (auto global = module.template lookupSymbol(name)) { auto ty = mlir::LLVM::LLVMPointerType::get( @@ -1274,24 +1278,15 @@ return rewriter.create(loc, ty, global.getSymName()); } - if (fir::NameUniquer::belongsToModule( - name, Fortran::semantics::typeInfoBuiltinModule)) { - // Type info derived types do not have type descriptors since they are the - // types defining type descriptors. - return rewriter.create( - loc, ::getVoidPtrType(box.getContext())); - } - // The global does not exist in the current translation unit, but may be - // defined elsewhere (e.g., type defined in a module). - // Create an available_externally global to require the symbols to be - // defined elsewhere and to cause link-time failure otherwise. - auto i8Ty = rewriter.getIntegerType(8); - mlir::OpBuilder modBuilder(module.getBodyRegion()); - modBuilder.create( - loc, i8Ty, /*isConstant=*/true, - mlir::LLVM::Linkage::AvailableExternally, name, mlir::Attribute{}); - auto ty = mlir::LLVM::LLVMPointerType::get(i8Ty); - return rewriter.create(loc, ty, name); + // Type info derived types do not have type descriptors since they are the + // types defining type descriptors. + if (!this->options.ignoreMissingTypeDescriptors && + !fir::NameUniquer::belongsToModule( + name, Fortran::semantics::typeInfoBuiltinModule)) + fir::emitFatalError( + loc, "runtime derived type info descriptor was not generated"); + return rewriter.create( + loc, ::getVoidPtrType(box.getContext())); } template @@ -3233,8 +3228,9 @@ /// These operations are normally dead after the pre-codegen pass. template struct MustBeDeadConversion : public FIROpConversion { - explicit MustBeDeadConversion(fir::LLVMTypeConverter &lowering) - : FIROpConversion(lowering) {} + explicit MustBeDeadConversion(fir::LLVMTypeConverter &lowering, + const fir::FIRToLLVMPassOptions &options) + : FIROpConversion(lowering, options) {} using OpAdaptor = typename FromOp::Adaptor; mlir::LogicalResult @@ -3274,6 +3270,8 @@ /// This pass is not complete yet. We are upstreaming it in small patches. class FIRToLLVMLowering : public fir::FIRToLLVMLoweringBase { public: + FIRToLLVMLowering() = default; + FIRToLLVMLowering(fir::FIRToLLVMPassOptions options) : options{options} {} mlir::ModuleOp getModule() { return getOperation(); } void runOnOperation() override final { @@ -3306,8 +3304,8 @@ SliceOpConversion, StoreOpConversion, StringLitOpConversion, SubcOpConversion, UnboxCharOpConversion, UnboxProcOpConversion, UndefOpConversion, UnreachableOpConversion, XArrayCoorOpConversion, - XEmboxOpConversion, XReboxOpConversion, ZeroOpConversion>( - typeConverter); + XEmboxOpConversion, XReboxOpConversion, ZeroOpConversion>(typeConverter, + options); mlir::populateStdToLLVMConversionPatterns(typeConverter, pattern); mlir::arith::populateArithmeticToLLVMConversionPatterns(typeConverter, pattern); @@ -3325,6 +3323,9 @@ signalPassFailure(); } } + +private: + fir::FIRToLLVMPassOptions options; }; /// Lower from LLVM IR dialect to proper LLVM-IR and dump the module @@ -3362,6 +3363,11 @@ return std::make_unique(); } +std::unique_ptr +fir::createFIRToLLVMPass(FIRToLLVMPassOptions options) { + return std::make_unique(options); +} + std::unique_ptr fir::createLLVMDialectToLLVMPass(raw_ostream &output, fir::LLVMIRLoweringPrinter printer) { diff --git a/flang/lib/Optimizer/Dialect/FIRType.cpp b/flang/lib/Optimizer/Dialect/FIRType.cpp --- a/flang/lib/Optimizer/Dialect/FIRType.cpp +++ b/flang/lib/Optimizer/Dialect/FIRType.cpp @@ -642,12 +642,6 @@ return std::numeric_limits::max(); } -std::string fir::RecordType::translateNameToFrontendMangledName() const { - auto split = getName().split('T'); - std::string name = (split.first + "E.dt." + split.second).str(); - return name; -} - //===----------------------------------------------------------------------===// // ReferenceType //===----------------------------------------------------------------------===// diff --git a/flang/lib/Optimizer/Support/InternalNames.cpp b/flang/lib/Optimizer/Support/InternalNames.cpp --- a/flang/lib/Optimizer/Support/InternalNames.cpp +++ b/flang/lib/Optimizer/Support/InternalNames.cpp @@ -324,3 +324,29 @@ return !result.second.modules.empty() && result.second.modules[0] == moduleName; } + +static std::string +mangleTypeDescriptorKinds(llvm::ArrayRef kinds) { + if (kinds.empty()) + return ""; + std::string result = ""; + for (std::int64_t kind : kinds) + result += "." + std::to_string(kind); + return result; +} + +std::string +fir::NameUniquer::getTypeDescriptorName(llvm::StringRef mangledTypeName) { + auto result = deconstruct(mangledTypeName); + if (result.first != NameKind::DERIVED_TYPE) + return ""; + std::string varName = ".dt." + result.second.name + + mangleTypeDescriptorKinds(result.second.kinds); + llvm::SmallVector modules; + for (const std::string &mod : result.second.modules) + modules.push_back(mod); + llvm::Optional host; + if (result.second.host) + host = *result.second.host; + return doVariable(modules, host, varName); +} diff --git a/flang/test/Fir/convert-to-llvm.fir b/flang/test/Fir/convert-to-llvm.fir --- a/flang/test/Fir/convert-to-llvm.fir +++ b/flang/test/Fir/convert-to-llvm.fir @@ -1618,12 +1618,14 @@ // to 1 meaning the addendum is present (true) and the addendum values are // inserted. +fir.global linkonce @_QMtest_dinitE.dt.tseq constant : i8 + func @embox1(%arg0: !fir.ref>) { %0 = fir.embox %arg0() : (!fir.ref>) -> !fir.box> return } -// CHECK: llvm.mlir.global available_externally constant @_QMtest_dinitE.dt.tseq() : i8 +// CHECK: llvm.mlir.global linkonce constant @_QMtest_dinitE.dt.tseq() : i8 // CHECK-LABEL: llvm.func @embox1 // CHECK: %[[TYPE_CODE:.*]] = llvm.mlir.constant(42 : i32) : i32 // CHECK: %[[TYPE_CODE_I8:.*]] = llvm.trunc %[[TYPE_CODE]] : i32 to i8 diff --git a/flang/test/Fir/ignore-missing-type-descriptor.fir b/flang/test/Fir/ignore-missing-type-descriptor.fir new file mode 100644 --- /dev/null +++ b/flang/test/Fir/ignore-missing-type-descriptor.fir @@ -0,0 +1,21 @@ +// Test the option to avoid failing if derived type descriptors are not found. +// This is a debug option to allow manually writing derived type fir.embox without +// having to care with providing an ABI compliant derived type descriptor object. +// Missing derived type descriptor pointers are replaced by null pointers. +// RUN: tco --ignore-missing-type-desc -o - %s | FileCheck %s + +!some_freestyle_type = type !fir.type + +func private @bar(!fir.box) + +func @test_embox(%addr: !fir.ref) { + %0 = fir.embox %addr : (!fir.ref) -> !fir.box + fir.call @bar(%0) : (!fir.box) -> () + return +} +// CHECK-LABEL: define void @test_embox( +// CHECK-SAME: %some_not_mangled_type* %[[ADDR:.*]]) +// CHECK: insertvalue { %some_not_mangled_type*, i64, i32, i8, i8, i8, i8, i8*, [1 x i64] } +// CHECK-SAME: { %some_not_mangled_type* undef, i64 ptrtoint (%some_not_mangled_type* getelementptr (%some_not_mangled_type, %some_not_mangled_type* null, i32 1) to i64), +// CHECK-SAME: i32 20180515, i8 0, i8 42, i8 0, i8 1, i8* null, [1 x i64] undef }, +// CHECK-SAME: %some_not_mangled_type* %[[ADDR]], 0,