[flang-commits] [flang] Reland "[flang] add option to generate runtime type info as external (#145901) " (PR #146071)
via flang-commits
flang-commits at lists.llvm.org
Fri Jun 27 05:56:09 PDT 2025
llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-flang-codegen
Author: None (jeanPerier)
<details>
<summary>Changes</summary>
Reland #<!-- -->145901 with a fix for shared library builds (see second commit).
Lowering cannot directly use cl::opts from lib/Optimizer/Passes/CommandLineOpts.cpp without linking against `flangPasses`.
Instead of adding the linking dependency, I think it is cleaner to use a proper lowering options and let the driver read the cl::opt (I would rather keep the option a cl::opt before doing a bit more testing and making it a proper driver option).
---
Patch is 48.11 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/146071.diff
18 Files Affected:
- (modified) flang/include/flang/Evaluate/tools.h (+1)
- (modified) flang/include/flang/Lower/LoweringOptions.def (+4)
- (modified) flang/include/flang/Optimizer/CodeGen/CodeGen.h (+3)
- (modified) flang/include/flang/Optimizer/Passes/CommandLineOpts.h (+13)
- (modified) flang/include/flang/Optimizer/Support/Utils.h (-26)
- (modified) flang/include/flang/Semantics/runtime-type-info.h (+4)
- (modified) flang/lib/Evaluate/tools.cpp (+5)
- (modified) flang/lib/Frontend/CompilerInvocation.cpp (+2)
- (modified) flang/lib/Lower/Bridge.cpp (+14-1)
- (modified) flang/lib/Lower/ConvertVariable.cpp (+13-9)
- (modified) flang/lib/Optimizer/CodeGen/CodeGen.cpp (+53-49)
- (modified) flang/lib/Optimizer/Passes/CommandLineOpts.cpp (+6)
- (modified) flang/lib/Optimizer/Passes/Pipelines.cpp (+1)
- (modified) flang/lib/Optimizer/Transforms/PolymorphicOpConversion.cpp (+60-35)
- (added) flang/test/Integration/skip-external-rtti-definition.F90 (+47)
- (modified) flang/test/Lower/select-type-2.f90 (+4-4)
- (modified) flang/test/Lower/select-type.f90 (+36-36)
- (modified) flang/tools/bbc/bbc.cpp (+1)
``````````diff
diff --git a/flang/include/flang/Evaluate/tools.h b/flang/include/flang/Evaluate/tools.h
index d26a477ddded1..cad1b634f8924 100644
--- a/flang/include/flang/Evaluate/tools.h
+++ b/flang/include/flang/Evaluate/tools.h
@@ -1585,6 +1585,7 @@ bool IsExtensibleType(const DerivedTypeSpec *);
bool IsSequenceOrBindCType(const DerivedTypeSpec *);
bool IsBuiltinDerivedType(const DerivedTypeSpec *derived, const char *name);
bool IsBuiltinCPtr(const Symbol &);
+bool IsFromBuiltinModule(const Symbol &);
bool IsEventType(const DerivedTypeSpec *);
bool IsLockType(const DerivedTypeSpec *);
bool IsNotifyType(const DerivedTypeSpec *);
diff --git a/flang/include/flang/Lower/LoweringOptions.def b/flang/include/flang/Lower/LoweringOptions.def
index d97abf4d864b8..3263ab129d076 100644
--- a/flang/include/flang/Lower/LoweringOptions.def
+++ b/flang/include/flang/Lower/LoweringOptions.def
@@ -66,5 +66,9 @@ ENUM_LOWERINGOPT(RepackArraysWhole, unsigned, 1, 0)
/// If true, CUDA Fortran runtime check is inserted.
ENUM_LOWERINGOPT(CUDARuntimeCheck, unsigned, 1, 0)
+/// If true, do not generate definition for runtime type info global objects of
+/// derived types defined in other compilation units.
+ENUM_LOWERINGOPT(SkipExternalRttiDefinition, unsigned, 1, 0)
+
#undef LOWERINGOPT
#undef ENUM_LOWERINGOPT
diff --git a/flang/include/flang/Optimizer/CodeGen/CodeGen.h b/flang/include/flang/Optimizer/CodeGen/CodeGen.h
index 0398d0f248e08..93f07d8d5d4d9 100644
--- a/flang/include/flang/Optimizer/CodeGen/CodeGen.h
+++ b/flang/include/flang/Optimizer/CodeGen/CodeGen.h
@@ -39,6 +39,9 @@ struct FIRToLLVMPassOptions {
// that such programs would crash at runtime if the derived type descriptors
// are required by the runtime, so this is only an option to help debugging.
bool ignoreMissingTypeDescriptors = false;
+ // Similar to ignoreMissingTypeDescriptors, but generate external declaration
+ // for the missing type descriptor globals instead.
+ bool skipExternalRttiDefinition = false;
// Generate TBAA information for FIR types and memory accessing operations.
bool applyTBAA = false;
diff --git a/flang/include/flang/Optimizer/Passes/CommandLineOpts.h b/flang/include/flang/Optimizer/Passes/CommandLineOpts.h
index 1cfaf285e75e6..76ac9d0622d2b 100644
--- a/flang/include/flang/Optimizer/Passes/CommandLineOpts.h
+++ b/flang/include/flang/Optimizer/Passes/CommandLineOpts.h
@@ -32,6 +32,19 @@ extern llvm::cl::opt<std::size_t> arrayStackAllocationThreshold;
/// generated by the frontend.
extern llvm::cl::opt<bool> ignoreMissingTypeDescriptors;
+/// Shared option in tools to only generate rtti static object definitions for
+/// derived types defined in the current compilation unit. Derived type
+/// descriptor object for types defined in other objects will only be declared
+/// as external. This also changes the linkage of rtti objects defined in the
+/// current compilation unit from linkonce_odr to external so that unused rtti
+/// objects are retained and can be accessed from other compilation units. This
+/// is an experimental option to explore compilation speed improvements and is
+/// an ABI breaking change because of the linkage change.
+/// It will also require linking against module file objects of modules defining
+/// only types (even for trivial types without type bound procedures, which
+/// differs from most compilers).
+extern llvm::cl::opt<bool> skipExternalRttiDefinition;
+
/// Default optimization level used to create Flang pass pipeline is O0.
extern llvm::OptimizationLevel defaultOptLevel;
diff --git a/flang/include/flang/Optimizer/Support/Utils.h b/flang/include/flang/Optimizer/Support/Utils.h
index ec73af6ec72e9..83c936b7dcada 100644
--- a/flang/include/flang/Optimizer/Support/Utils.h
+++ b/flang/include/flang/Optimizer/Support/Utils.h
@@ -35,32 +35,6 @@ inline std::int64_t toInt(mlir::arith::ConstantOp cop) {
.getSExtValue();
}
-// Reconstruct binding tables for dynamic dispatch.
-using BindingTable = llvm::DenseMap<llvm::StringRef, unsigned>;
-using BindingTables = llvm::DenseMap<llvm::StringRef, BindingTable>;
-
-inline void buildBindingTables(BindingTables &bindingTables,
- mlir::ModuleOp mod) {
-
- // 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 typeInfo : mod.getOps<fir::TypeInfoOp>()) {
- unsigned bindingIdx = 0;
- BindingTable bindings;
- if (typeInfo.getDispatchTable().empty()) {
- bindingTables[typeInfo.getSymName()] = bindings;
- continue;
- }
- for (auto dtEntry :
- typeInfo.getDispatchTable().front().getOps<fir::DTEntryOp>()) {
- bindings[dtEntry.getMethod()] = bindingIdx;
- ++bindingIdx;
- }
- bindingTables[typeInfo.getSymName()] = bindings;
- }
-}
-
// Translate front-end KINDs for use in the IR and code gen.
inline std::vector<fir::KindTy>
fromDefaultKinds(const Fortran::common::IntrinsicTypeDefaultKinds &defKinds) {
diff --git a/flang/include/flang/Semantics/runtime-type-info.h b/flang/include/flang/Semantics/runtime-type-info.h
index e90d3ae8baf1e..6c5a061d1c1a2 100644
--- a/flang/include/flang/Semantics/runtime-type-info.h
+++ b/flang/include/flang/Semantics/runtime-type-info.h
@@ -38,6 +38,10 @@ RuntimeDerivedTypeTables BuildRuntimeDerivedTypeTables(SemanticsContext &);
/// to describe other derived types at runtime in flang descriptor.
constexpr char typeInfoBuiltinModule[]{"__fortran_type_info"};
+/// Name of the builtin derived type in __fortran_type_inf that is used for
+/// derived type descriptors.
+constexpr char typeDescriptorTypeName[]{"derivedtype"};
+
/// Name of the bindings descriptor component in the DerivedType type of the
/// __Fortran_type_info module
constexpr char bindingDescCompName[]{"binding"};
diff --git a/flang/lib/Evaluate/tools.cpp b/flang/lib/Evaluate/tools.cpp
index 68838564f87ba..fcacdb93d662b 100644
--- a/flang/lib/Evaluate/tools.cpp
+++ b/flang/lib/Evaluate/tools.cpp
@@ -2334,6 +2334,11 @@ bool IsBuiltinCPtr(const Symbol &symbol) {
return false;
}
+bool IsFromBuiltinModule(const Symbol &symbol) {
+ const Scope &scope{symbol.GetUltimate().owner()};
+ return IsSameModule(&scope, scope.context().GetBuiltinsScope());
+}
+
bool IsIsoCType(const DerivedTypeSpec *derived) {
return IsBuiltinDerivedType(derived, "c_ptr") ||
IsBuiltinDerivedType(derived, "c_funptr");
diff --git a/flang/lib/Frontend/CompilerInvocation.cpp b/flang/lib/Frontend/CompilerInvocation.cpp
index 766131fd69d85..30d81f3daa969 100644
--- a/flang/lib/Frontend/CompilerInvocation.cpp
+++ b/flang/lib/Frontend/CompilerInvocation.cpp
@@ -14,6 +14,7 @@
#include "flang/Frontend/CodeGenOptions.h"
#include "flang/Frontend/PreprocessorOptions.h"
#include "flang/Frontend/TargetOptions.h"
+#include "flang/Optimizer/Passes/CommandLineOpts.h"
#include "flang/Semantics/semantics.h"
#include "flang/Support/Fortran-features.h"
#include "flang/Support/OpenMP-features.h"
@@ -1792,6 +1793,7 @@ void CompilerInvocation::setLoweringOptions() {
// Lower TRANSPOSE as a runtime call under -O0.
loweringOpts.setOptimizeTranspose(codegenOpts.OptimizationLevel > 0);
loweringOpts.setUnderscoring(codegenOpts.Underscoring);
+ loweringOpts.setSkipExternalRttiDefinition(skipExternalRttiDefinition);
const Fortran::common::LangOptions &langOptions = getLangOpts();
loweringOpts.setIntegerWrapAround(langOptions.getSignedOverflowBehavior() ==
diff --git a/flang/lib/Lower/Bridge.cpp b/flang/lib/Lower/Bridge.cpp
index 7b640dd497af3..ff35840a6668c 100644
--- a/flang/lib/Lower/Bridge.cpp
+++ b/flang/lib/Lower/Bridge.cpp
@@ -262,6 +262,7 @@ class TypeInfoConverter {
}
void createTypeInfo(Fortran::lower::AbstractConverter &converter) {
+ createTypeInfoForTypeDescriptorBuiltinType(converter);
while (!registeredTypeInfoA.empty()) {
currentTypeInfoStack = ®isteredTypeInfoB;
for (const TypeInfo &info : registeredTypeInfoA)
@@ -277,10 +278,22 @@ class TypeInfoConverter {
private:
void createTypeInfoOpAndGlobal(Fortran::lower::AbstractConverter &converter,
const TypeInfo &info) {
- Fortran::lower::createRuntimeTypeInfoGlobal(converter, info.symbol.get());
+ if (!converter.getLoweringOptions().getSkipExternalRttiDefinition())
+ Fortran::lower::createRuntimeTypeInfoGlobal(converter, info.symbol.get());
createTypeInfoOp(converter, info);
}
+ void createTypeInfoForTypeDescriptorBuiltinType(
+ Fortran::lower::AbstractConverter &converter) {
+ if (registeredTypeInfoA.empty())
+ return;
+ auto builtinTypeInfoType = llvm::cast<fir::RecordType>(
+ converter.genType(registeredTypeInfoA[0].symbol.get()));
+ converter.getFirOpBuilder().createTypeInfoOp(
+ registeredTypeInfoA[0].loc, builtinTypeInfoType,
+ /*parentType=*/fir::RecordType{});
+ }
+
void createTypeInfoOp(Fortran::lower::AbstractConverter &converter,
const TypeInfo &info) {
fir::RecordType parentType{};
diff --git a/flang/lib/Lower/ConvertVariable.cpp b/flang/lib/Lower/ConvertVariable.cpp
index 49e6ea02d51a7..7ab3c43016bd9 100644
--- a/flang/lib/Lower/ConvertVariable.cpp
+++ b/flang/lib/Lower/ConvertVariable.cpp
@@ -647,13 +647,19 @@ fir::GlobalOp Fortran::lower::defineGlobal(
/// Return linkage attribute for \p var.
static mlir::StringAttr
-getLinkageAttribute(fir::FirOpBuilder &builder,
+getLinkageAttribute(Fortran::lower::AbstractConverter &converter,
const Fortran::lower::pft::Variable &var) {
+ fir::FirOpBuilder &builder = converter.getFirOpBuilder();
// Runtime type info for a same derived type is identical in each compilation
// unit. It desired to avoid having to link against module that only define a
// type. Therefore the runtime type info is generated everywhere it is needed
- // with `linkonce_odr` LLVM linkage.
- if (var.isRuntimeTypeInfoData())
+ // with `linkonce_odr` LLVM linkage (unless the skipExternalRttiDefinition
+ // option is set, in which case one will need to link against objects of
+ // modules defining types). Builtin objects rtti is always generated because
+ // the builtin module is currently not compiled or part of the runtime.
+ if (var.isRuntimeTypeInfoData() &&
+ (!converter.getLoweringOptions().getSkipExternalRttiDefinition() ||
+ Fortran::semantics::IsFromBuiltinModule(var.getSymbol())))
return builder.createLinkOnceODRLinkage();
if (var.isModuleOrSubmoduleVariable())
return {}; // external linkage
@@ -673,7 +679,7 @@ static void instantiateGlobal(Fortran::lower::AbstractConverter &converter,
fir::FirOpBuilder &builder = converter.getFirOpBuilder();
std::string globalName = converter.mangleName(sym);
mlir::Location loc = genLocation(converter, sym);
- mlir::StringAttr linkage = getLinkageAttribute(builder, var);
+ mlir::StringAttr linkage = getLinkageAttribute(converter, var);
fir::GlobalOp global;
if (var.isModuleOrSubmoduleVariable()) {
// A non-intrinsic module global is defined when lowering the module.
@@ -1265,7 +1271,7 @@ instantiateAggregateStore(Fortran::lower::AbstractConverter &converter,
if (var.isGlobal()) {
fir::GlobalOp global;
auto &aggregate = var.getAggregateStore();
- mlir::StringAttr linkage = getLinkageAttribute(builder, var);
+ mlir::StringAttr linkage = getLinkageAttribute(converter, var);
if (var.isModuleOrSubmoduleVariable()) {
// A module global was or will be defined when lowering the module. Emit
// only a declaration if the global does not exist at that point.
@@ -2470,8 +2476,7 @@ void Fortran::lower::defineModuleVariable(
AbstractConverter &converter, const Fortran::lower::pft::Variable &var) {
// Use empty linkage for module variables, which makes them available
// for use in another unit.
- mlir::StringAttr linkage =
- getLinkageAttribute(converter.getFirOpBuilder(), var);
+ mlir::StringAttr linkage = getLinkageAttribute(converter, var);
if (!var.isGlobal())
fir::emitFatalError(converter.getCurrentLocation(),
"attempting to lower module variable as local");
@@ -2606,10 +2611,9 @@ void Fortran::lower::createIntrinsicModuleGlobal(
void Fortran::lower::createRuntimeTypeInfoGlobal(
Fortran::lower::AbstractConverter &converter,
const Fortran::semantics::Symbol &typeInfoSym) {
- fir::FirOpBuilder &builder = converter.getFirOpBuilder();
std::string globalName = converter.mangleName(typeInfoSym);
auto var = Fortran::lower::pft::Variable(typeInfoSym, /*global=*/true);
- mlir::StringAttr linkage = getLinkageAttribute(builder, var);
+ mlir::StringAttr linkage = getLinkageAttribute(converter, var);
defineGlobal(converter, var, globalName, linkage);
}
diff --git a/flang/lib/Optimizer/CodeGen/CodeGen.cpp b/flang/lib/Optimizer/CodeGen/CodeGen.cpp
index a3de3ae9d116a..2b018912b40e4 100644
--- a/flang/lib/Optimizer/CodeGen/CodeGen.cpp
+++ b/flang/lib/Optimizer/CodeGen/CodeGen.cpp
@@ -1294,6 +1294,51 @@ genCUFAllocDescriptor(mlir::Location loc,
.getResult();
}
+/// Get the address of the type descriptor global variable that was created by
+/// lowering for derived type \p recType.
+template <typename ModOpTy>
+static mlir::Value
+getTypeDescriptor(ModOpTy mod, mlir::ConversionPatternRewriter &rewriter,
+ mlir::Location loc, fir::RecordType recType,
+ const fir::FIRToLLVMPassOptions &options) {
+ std::string name =
+ options.typeDescriptorsRenamedForAssembly
+ ? fir::NameUniquer::getTypeDescriptorAssemblyName(recType.getName())
+ : fir::NameUniquer::getTypeDescriptorName(recType.getName());
+ mlir::Type llvmPtrTy = ::getLlvmPtrType(mod.getContext());
+ if (auto global = mod.template lookupSymbol<fir::GlobalOp>(name))
+ return rewriter.create<mlir::LLVM::AddressOfOp>(loc, llvmPtrTy,
+ global.getSymName());
+ // The global may have already been translated to LLVM.
+ if (auto global = mod.template lookupSymbol<mlir::LLVM::GlobalOp>(name))
+ return rewriter.create<mlir::LLVM::AddressOfOp>(loc, llvmPtrTy,
+ global.getSymName());
+ // Type info derived types do not have type descriptors since they are the
+ // types defining type descriptors.
+ if (options.ignoreMissingTypeDescriptors ||
+ fir::NameUniquer::belongsToModule(
+ name, Fortran::semantics::typeInfoBuiltinModule))
+ return rewriter.create<mlir::LLVM::ZeroOp>(loc, llvmPtrTy);
+
+ if (!options.skipExternalRttiDefinition)
+ fir::emitFatalError(loc,
+ "runtime derived type info descriptor was not "
+ "generated and skipExternalRttiDefinition and "
+ "ignoreMissingTypeDescriptors options are not set");
+
+ // Rtti for a derived type defined in another compilation unit and for which
+ // rtti was not defined in lowering because of the skipExternalRttiDefinition
+ // option. Generate the object declaration now.
+ auto insertPt = rewriter.saveInsertionPoint();
+ rewriter.setInsertionPoint(mod.getBody(), mod.getBody()->end());
+ mlir::LLVM::GlobalOp global = rewriter.create<mlir::LLVM::GlobalOp>(
+ loc, llvmPtrTy, /*constant=*/true, mlir::LLVM::Linkage::External, name,
+ mlir::Attribute());
+ rewriter.restoreInsertionPoint(insertPt);
+ return rewriter.create<mlir::LLVM::AddressOfOp>(loc, llvmPtrTy,
+ global.getSymName());
+}
+
/// Common base class for embox to descriptor conversion.
template <typename OP>
struct EmboxCommonConversion : public fir::FIROpConversion<OP> {
@@ -1406,36 +1451,6 @@ struct EmboxCommonConversion : public fir::FIROpConversion<OP> {
stride);
}
- /// Get the address of the type descriptor global variable that was created by
- /// lowering for derived type \p recType.
- template <typename ModOpTy>
- mlir::Value
- getTypeDescriptor(ModOpTy mod, mlir::ConversionPatternRewriter &rewriter,
- mlir::Location loc, fir::RecordType recType) const {
- std::string name =
- this->options.typeDescriptorsRenamedForAssembly
- ? fir::NameUniquer::getTypeDescriptorAssemblyName(recType.getName())
- : fir::NameUniquer::getTypeDescriptorName(recType.getName());
- mlir::Type llvmPtrTy = ::getLlvmPtrType(mod.getContext());
- if (auto global = mod.template lookupSymbol<fir::GlobalOp>(name)) {
- return rewriter.create<mlir::LLVM::AddressOfOp>(loc, llvmPtrTy,
- global.getSymName());
- }
- if (auto global = mod.template lookupSymbol<mlir::LLVM::GlobalOp>(name)) {
- // The global may have already been translated to LLVM.
- return rewriter.create<mlir::LLVM::AddressOfOp>(loc, llvmPtrTy,
- global.getSymName());
- }
- // 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<mlir::LLVM::ZeroOp>(loc, llvmPtrTy);
- }
-
template <typename ModOpTy>
mlir::Value populateDescriptor(mlir::Location loc, ModOpTy mod,
fir::BaseBoxType boxTy, mlir::Type inputType,
@@ -1500,7 +1515,8 @@ struct EmboxCommonConversion : public fir::FIROpConversion<OP> {
mlir::Type innerType = fir::unwrapInnerType(inputType);
if (innerType && mlir::isa<fir::RecordType>(innerType)) {
auto recTy = mlir::dyn_cast<fir::RecordType>(innerType);
- typeDesc = getTypeDescriptor(mod, rewriter, loc, recTy);
+ typeDesc =
+ getTypeDescriptor(mod, rewriter, loc, recTy, this->options);
} else {
// Unlimited polymorphic type descriptor with no record type. Set
// type descriptor address to a clean state.
@@ -1508,8 +1524,8 @@ struct EmboxCommonConversion : public fir::FIROpConversion<OP> {
loc, ::getLlvmPtrType(mod.getContext()));
}
} else {
- typeDesc = getTypeDescriptor(mod, rewriter, loc,
- fir::unwrapIfDerived(boxTy));
+ typeDesc = getTypeDescriptor(
+ mod, rewriter, loc, fir::unwrapIfDerived(boxTy), this->options);
}
}
if (typeDesc)
@@ -3021,22 +3037,10 @@ struct TypeDescOpConversion : public fir::FIROpConversion<fir::TypeDescOp> {
assert(mlir::isa<fir::RecordType>(inTy) && "expecting fir.type");
auto recordType = mlir::dyn_cast<fir::RecordType>(inTy);
auto module = typeDescOp.getOperation()->getParentOfType<mlir::ModuleOp>();
- std::string typeDescName =
- this->options.typeDescriptorsRenamedForAssembly
- ? fir::NameUniquer::getTypeDescriptorAssemblyName(
- recordType.getName())
- : fir::NameUniquer::getTypeDescriptorName(recordType.getName());
- auto llvmPtrTy = ::getLlvmPtrType(typeDescOp.getContext());
- if (auto global = module.lookupSymbol<mlir::LLVM::GlobalOp>(typeDescName)) {
- rewriter.replaceOpWithNewOp<mlir::LLVM::AddressOfOp>(
- typeDescOp, llvmPtrTy, global.getSymName());
- return mlir::success();
- } else if (auto global = module.lookupSymbol<fir::GlobalOp>(typeDescName)) {
- rewriter.replaceOpWithNewOp<mlir::LLVM::AddressOfOp>(
- typeDescOp, llvmPtrTy, global.getSymName());
- return mlir::success();
- }
- return mlir::failure();
+ mlir::Value typeDesc = getTypeDescriptor(
+ module, rewriter, typeDescOp.getLoc(), recordType, this->options);
+ rewriter.replaceOp(typeDescOp, typeDesc);
+ return mlir::success();
}
};
diff --git a/flang/lib/Optimizer/Passes/CommandLineOpts.cpp b/flang/lib/Optimizer/Passes/CommandLineOpts.cpp
index f95a280883cba..014237542f24d 100644
--- a/flang/lib/Optimizer/Passes/Co...
[truncated]
``````````
</details>
https://github.com/llvm/llvm-project/pull/146071
More information about the flang-commits
mailing list