Skip to content

Commit faefe7c

Browse files
authored
[flang] add option to generate runtime type info as external (#146071)
Reland #145901 with a fix for shared library builds. So far flang generates runtime derived type info global definitions (as opposed to declarations) for all the types used in the current compilation unit even when the derived types are defined in other compilation units. It is using linkonce_odr to achieve derived type descriptor address "uniqueness" aspect needed to match two derived type inside the runtime. This comes at a big compile time cost because of all the extra globals and their definitions in apps with many and complex derived types. This patch adds and experimental option to only generate the rtti definition for the types defined in the current compilation unit and to only generate external declaration for the derived type descriptor object of types defined elsewhere. Note that objects compiled with this option are not compatible with object files compiled without because files compiled without it may drop the rtti for type they defined if it is not used in the compilation unit because of the linkonce_odr aspect. I am adding the option so that we can better measure the extra cost of the current approach on apps and allow speeding up some compilation where devirtualization does not matter (and the build config links to all module file object anyway).
1 parent 834c410 commit faefe7c

File tree

18 files changed

+267
-160
lines changed

18 files changed

+267
-160
lines changed

flang/include/flang/Evaluate/tools.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1585,6 +1585,7 @@ bool IsExtensibleType(const DerivedTypeSpec *);
15851585
bool IsSequenceOrBindCType(const DerivedTypeSpec *);
15861586
bool IsBuiltinDerivedType(const DerivedTypeSpec *derived, const char *name);
15871587
bool IsBuiltinCPtr(const Symbol &);
1588+
bool IsFromBuiltinModule(const Symbol &);
15881589
bool IsEventType(const DerivedTypeSpec *);
15891590
bool IsLockType(const DerivedTypeSpec *);
15901591
bool IsNotifyType(const DerivedTypeSpec *);

flang/include/flang/Lower/LoweringOptions.def

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,5 +66,9 @@ ENUM_LOWERINGOPT(RepackArraysWhole, unsigned, 1, 0)
6666
/// If true, CUDA Fortran runtime check is inserted.
6767
ENUM_LOWERINGOPT(CUDARuntimeCheck, unsigned, 1, 0)
6868

69+
/// If true, do not generate definition for runtime type info global objects of
70+
/// derived types defined in other compilation units.
71+
ENUM_LOWERINGOPT(SkipExternalRttiDefinition, unsigned, 1, 0)
72+
6973
#undef LOWERINGOPT
7074
#undef ENUM_LOWERINGOPT

flang/include/flang/Optimizer/CodeGen/CodeGen.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,9 @@ struct FIRToLLVMPassOptions {
3939
// that such programs would crash at runtime if the derived type descriptors
4040
// are required by the runtime, so this is only an option to help debugging.
4141
bool ignoreMissingTypeDescriptors = false;
42+
// Similar to ignoreMissingTypeDescriptors, but generate external declaration
43+
// for the missing type descriptor globals instead.
44+
bool skipExternalRttiDefinition = false;
4245

4346
// Generate TBAA information for FIR types and memory accessing operations.
4447
bool applyTBAA = false;

flang/include/flang/Optimizer/Passes/CommandLineOpts.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,19 @@ extern llvm::cl::opt<std::size_t> arrayStackAllocationThreshold;
3232
/// generated by the frontend.
3333
extern llvm::cl::opt<bool> ignoreMissingTypeDescriptors;
3434

35+
/// Shared option in tools to only generate rtti static object definitions for
36+
/// derived types defined in the current compilation unit. Derived type
37+
/// descriptor object for types defined in other objects will only be declared
38+
/// as external. This also changes the linkage of rtti objects defined in the
39+
/// current compilation unit from linkonce_odr to external so that unused rtti
40+
/// objects are retained and can be accessed from other compilation units. This
41+
/// is an experimental option to explore compilation speed improvements and is
42+
/// an ABI breaking change because of the linkage change.
43+
/// It will also require linking against module file objects of modules defining
44+
/// only types (even for trivial types without type bound procedures, which
45+
/// differs from most compilers).
46+
extern llvm::cl::opt<bool> skipExternalRttiDefinition;
47+
3548
/// Default optimization level used to create Flang pass pipeline is O0.
3649
extern llvm::OptimizationLevel defaultOptLevel;
3750

flang/include/flang/Optimizer/Support/Utils.h

Lines changed: 0 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -35,32 +35,6 @@ inline std::int64_t toInt(mlir::arith::ConstantOp cop) {
3535
.getSExtValue();
3636
}
3737

38-
// Reconstruct binding tables for dynamic dispatch.
39-
using BindingTable = llvm::DenseMap<llvm::StringRef, unsigned>;
40-
using BindingTables = llvm::DenseMap<llvm::StringRef, BindingTable>;
41-
42-
inline void buildBindingTables(BindingTables &bindingTables,
43-
mlir::ModuleOp mod) {
44-
45-
// The binding tables are defined in FIR after lowering inside fir.type_info
46-
// operations. Go through each binding tables and store the procedure name and
47-
// binding index for later use by the fir.dispatch conversion pattern.
48-
for (auto typeInfo : mod.getOps<fir::TypeInfoOp>()) {
49-
unsigned bindingIdx = 0;
50-
BindingTable bindings;
51-
if (typeInfo.getDispatchTable().empty()) {
52-
bindingTables[typeInfo.getSymName()] = bindings;
53-
continue;
54-
}
55-
for (auto dtEntry :
56-
typeInfo.getDispatchTable().front().getOps<fir::DTEntryOp>()) {
57-
bindings[dtEntry.getMethod()] = bindingIdx;
58-
++bindingIdx;
59-
}
60-
bindingTables[typeInfo.getSymName()] = bindings;
61-
}
62-
}
63-
6438
// Translate front-end KINDs for use in the IR and code gen.
6539
inline std::vector<fir::KindTy>
6640
fromDefaultKinds(const Fortran::common::IntrinsicTypeDefaultKinds &defKinds) {

flang/include/flang/Semantics/runtime-type-info.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,10 @@ RuntimeDerivedTypeTables BuildRuntimeDerivedTypeTables(SemanticsContext &);
3838
/// to describe other derived types at runtime in flang descriptor.
3939
constexpr char typeInfoBuiltinModule[]{"__fortran_type_info"};
4040

41+
/// Name of the builtin derived type in __fortran_type_inf that is used for
42+
/// derived type descriptors.
43+
constexpr char typeDescriptorTypeName[]{"derivedtype"};
44+
4145
/// Name of the bindings descriptor component in the DerivedType type of the
4246
/// __Fortran_type_info module
4347
constexpr char bindingDescCompName[]{"binding"};

flang/lib/Evaluate/tools.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2334,6 +2334,11 @@ bool IsBuiltinCPtr(const Symbol &symbol) {
23342334
return false;
23352335
}
23362336

2337+
bool IsFromBuiltinModule(const Symbol &symbol) {
2338+
const Scope &scope{symbol.GetUltimate().owner()};
2339+
return IsSameModule(&scope, scope.context().GetBuiltinsScope());
2340+
}
2341+
23372342
bool IsIsoCType(const DerivedTypeSpec *derived) {
23382343
return IsBuiltinDerivedType(derived, "c_ptr") ||
23392344
IsBuiltinDerivedType(derived, "c_funptr");

flang/lib/Frontend/CompilerInvocation.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#include "flang/Frontend/CodeGenOptions.h"
1515
#include "flang/Frontend/PreprocessorOptions.h"
1616
#include "flang/Frontend/TargetOptions.h"
17+
#include "flang/Optimizer/Passes/CommandLineOpts.h"
1718
#include "flang/Semantics/semantics.h"
1819
#include "flang/Support/Fortran-features.h"
1920
#include "flang/Support/OpenMP-features.h"
@@ -1792,6 +1793,7 @@ void CompilerInvocation::setLoweringOptions() {
17921793
// Lower TRANSPOSE as a runtime call under -O0.
17931794
loweringOpts.setOptimizeTranspose(codegenOpts.OptimizationLevel > 0);
17941795
loweringOpts.setUnderscoring(codegenOpts.Underscoring);
1796+
loweringOpts.setSkipExternalRttiDefinition(skipExternalRttiDefinition);
17951797

17961798
const Fortran::common::LangOptions &langOptions = getLangOpts();
17971799
loweringOpts.setIntegerWrapAround(langOptions.getSignedOverflowBehavior() ==

flang/lib/Lower/Bridge.cpp

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -262,6 +262,7 @@ class TypeInfoConverter {
262262
}
263263

264264
void createTypeInfo(Fortran::lower::AbstractConverter &converter) {
265+
createTypeInfoForTypeDescriptorBuiltinType(converter);
265266
while (!registeredTypeInfoA.empty()) {
266267
currentTypeInfoStack = &registeredTypeInfoB;
267268
for (const TypeInfo &info : registeredTypeInfoA)
@@ -277,10 +278,22 @@ class TypeInfoConverter {
277278
private:
278279
void createTypeInfoOpAndGlobal(Fortran::lower::AbstractConverter &converter,
279280
const TypeInfo &info) {
280-
Fortran::lower::createRuntimeTypeInfoGlobal(converter, info.symbol.get());
281+
if (!converter.getLoweringOptions().getSkipExternalRttiDefinition())
282+
Fortran::lower::createRuntimeTypeInfoGlobal(converter, info.symbol.get());
281283
createTypeInfoOp(converter, info);
282284
}
283285

286+
void createTypeInfoForTypeDescriptorBuiltinType(
287+
Fortran::lower::AbstractConverter &converter) {
288+
if (registeredTypeInfoA.empty())
289+
return;
290+
auto builtinTypeInfoType = llvm::cast<fir::RecordType>(
291+
converter.genType(registeredTypeInfoA[0].symbol.get()));
292+
converter.getFirOpBuilder().createTypeInfoOp(
293+
registeredTypeInfoA[0].loc, builtinTypeInfoType,
294+
/*parentType=*/fir::RecordType{});
295+
}
296+
284297
void createTypeInfoOp(Fortran::lower::AbstractConverter &converter,
285298
const TypeInfo &info) {
286299
fir::RecordType parentType{};

flang/lib/Lower/ConvertVariable.cpp

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -647,13 +647,19 @@ fir::GlobalOp Fortran::lower::defineGlobal(
647647

648648
/// Return linkage attribute for \p var.
649649
static mlir::StringAttr
650-
getLinkageAttribute(fir::FirOpBuilder &builder,
650+
getLinkageAttribute(Fortran::lower::AbstractConverter &converter,
651651
const Fortran::lower::pft::Variable &var) {
652+
fir::FirOpBuilder &builder = converter.getFirOpBuilder();
652653
// Runtime type info for a same derived type is identical in each compilation
653654
// unit. It desired to avoid having to link against module that only define a
654655
// type. Therefore the runtime type info is generated everywhere it is needed
655-
// with `linkonce_odr` LLVM linkage.
656-
if (var.isRuntimeTypeInfoData())
656+
// with `linkonce_odr` LLVM linkage (unless the skipExternalRttiDefinition
657+
// option is set, in which case one will need to link against objects of
658+
// modules defining types). Builtin objects rtti is always generated because
659+
// the builtin module is currently not compiled or part of the runtime.
660+
if (var.isRuntimeTypeInfoData() &&
661+
(!converter.getLoweringOptions().getSkipExternalRttiDefinition() ||
662+
Fortran::semantics::IsFromBuiltinModule(var.getSymbol())))
657663
return builder.createLinkOnceODRLinkage();
658664
if (var.isModuleOrSubmoduleVariable())
659665
return {}; // external linkage
@@ -673,7 +679,7 @@ static void instantiateGlobal(Fortran::lower::AbstractConverter &converter,
673679
fir::FirOpBuilder &builder = converter.getFirOpBuilder();
674680
std::string globalName = converter.mangleName(sym);
675681
mlir::Location loc = genLocation(converter, sym);
676-
mlir::StringAttr linkage = getLinkageAttribute(builder, var);
682+
mlir::StringAttr linkage = getLinkageAttribute(converter, var);
677683
fir::GlobalOp global;
678684
if (var.isModuleOrSubmoduleVariable()) {
679685
// A non-intrinsic module global is defined when lowering the module.
@@ -1265,7 +1271,7 @@ instantiateAggregateStore(Fortran::lower::AbstractConverter &converter,
12651271
if (var.isGlobal()) {
12661272
fir::GlobalOp global;
12671273
auto &aggregate = var.getAggregateStore();
1268-
mlir::StringAttr linkage = getLinkageAttribute(builder, var);
1274+
mlir::StringAttr linkage = getLinkageAttribute(converter, var);
12691275
if (var.isModuleOrSubmoduleVariable()) {
12701276
// A module global was or will be defined when lowering the module. Emit
12711277
// only a declaration if the global does not exist at that point.
@@ -2470,8 +2476,7 @@ void Fortran::lower::defineModuleVariable(
24702476
AbstractConverter &converter, const Fortran::lower::pft::Variable &var) {
24712477
// Use empty linkage for module variables, which makes them available
24722478
// for use in another unit.
2473-
mlir::StringAttr linkage =
2474-
getLinkageAttribute(converter.getFirOpBuilder(), var);
2479+
mlir::StringAttr linkage = getLinkageAttribute(converter, var);
24752480
if (!var.isGlobal())
24762481
fir::emitFatalError(converter.getCurrentLocation(),
24772482
"attempting to lower module variable as local");
@@ -2606,10 +2611,9 @@ void Fortran::lower::createIntrinsicModuleGlobal(
26062611
void Fortran::lower::createRuntimeTypeInfoGlobal(
26072612
Fortran::lower::AbstractConverter &converter,
26082613
const Fortran::semantics::Symbol &typeInfoSym) {
2609-
fir::FirOpBuilder &builder = converter.getFirOpBuilder();
26102614
std::string globalName = converter.mangleName(typeInfoSym);
26112615
auto var = Fortran::lower::pft::Variable(typeInfoSym, /*global=*/true);
2612-
mlir::StringAttr linkage = getLinkageAttribute(builder, var);
2616+
mlir::StringAttr linkage = getLinkageAttribute(converter, var);
26132617
defineGlobal(converter, var, globalName, linkage);
26142618
}
26152619

0 commit comments

Comments
 (0)