Skip to content

Commit 66d5ca2

Browse files
authored
Reland "[flang] add extra component information in fir.type_info" (#97404)
Reland #96746 with the proper Support/CMakelist.txt change. fir.type does not contain all Fortran level information about components. For instance, component lower bounds and default initial value are lost. For correctness purpose, this does not matter because this information is "applied" in lowering (e.g., when addressing the components, the lower bounds are reflected in the hlfir.designate). However, this "loss" of information will prevent the generation of correct debug info for the type (needs to know about lower bounds). The initial value could help building some optimization pass to get rid of initialization runtime calls. This patch adds lower bound and initial value information into fir.type_info via a new fir.dt_component operation. This operation is generated only for component that needs it, which helps keeping the IR small for "boring" types. In general, adding Fortran level info in fir.type_info will allow delaying the generation of "type descriptors" gobals that are very verbose in FIR and make it hard to work with FIR dumps from applications with many derived types.
1 parent 72f9bff commit 66d5ca2

File tree

12 files changed

+303
-16
lines changed

12 files changed

+303
-16
lines changed

flang/include/flang/Optimizer/Builder/FIRBuilder.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -286,6 +286,10 @@ class FirOpBuilder : public mlir::OpBuilder, public mlir::OpBuilder::Listener {
286286
fir::StringLitOp createStringLitOp(mlir::Location loc,
287287
llvm::StringRef string);
288288

289+
std::pair<fir::TypeInfoOp, mlir::OpBuilder::InsertPoint>
290+
createTypeInfoOp(mlir::Location loc, fir::RecordType recordType,
291+
fir::RecordType parentType);
292+
289293
//===--------------------------------------------------------------------===//
290294
// Linkage helpers (inline). The default linkage is external.
291295
//===--------------------------------------------------------------------===//

flang/include/flang/Optimizer/Dialect/FIROps.td

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2956,7 +2956,10 @@ def fir_TypeInfoOp : fir_Op<"type_info",
29562956

29572957
let hasVerifier = 1;
29582958

2959-
let regions = (region MaxSizedRegion<1>:$dispatch_table);
2959+
let regions = (region
2960+
MaxSizedRegion<1>:$dispatch_table,
2961+
MaxSizedRegion<1>:$component_info
2962+
);
29602963

29612964
let builders = [
29622965
OpBuilder<(ins "fir::RecordType":$type, "fir::RecordType":$parent_type,
@@ -2967,6 +2970,7 @@ def fir_TypeInfoOp : fir_Op<"type_info",
29672970
$sym_name (`noinit` $no_init^)? (`nodestroy` $no_destroy^)?
29682971
(`nofinal` $no_final^)? (`extends` $parent_type^)? attr-dict `:` $type
29692972
(`dispatch_table` $dispatch_table^)?
2973+
(`component_info` $component_info^)?
29702974
}];
29712975

29722976
let extraClassDeclaration = [{
@@ -3010,6 +3014,24 @@ def fir_DTEntryOp : fir_Op<"dt_entry", [HasParent<"TypeInfoOp">]> {
30103014
}];
30113015
}
30123016

3017+
def fir_DTComponentOp : fir_Op<"dt_component", [HasParent<"TypeInfoOp">]> {
3018+
let summary = "define extra information about a component inside fir.type_info";
3019+
3020+
let description = [{
3021+
```
3022+
fir.dt_component i lbs [-1,2] init @init_val
3023+
```
3024+
}];
3025+
3026+
let arguments = (ins
3027+
StrAttr:$name,
3028+
OptionalAttr<DenseI64ArrayAttr>:$lower_bounds,
3029+
OptionalAttr<FlatSymbolRefAttr>:$init_val
3030+
);
3031+
3032+
let assemblyFormat = "$name (`lbs` $lower_bounds^)? (`init` $init_val^)? attr-dict";
3033+
}
3034+
30133035
def fir_AbsentOp : fir_OneResultOp<"absent", [NoMemoryEffect]> {
30143036
let summary = "create value to be passed for absent optional function argument";
30153037
let description = [{

flang/include/flang/Optimizer/Support/InternalNames.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
#include <optional>
1616

1717
static constexpr llvm::StringRef typeDescriptorSeparator = ".dt.";
18+
static constexpr llvm::StringRef componentInitSeparator = ".di.";
1819
static constexpr llvm::StringRef bindingTableSeparator = ".v.";
1920
static constexpr llvm::StringRef boxprocSuffix = "UnboxProc";
2021

@@ -156,6 +157,11 @@ struct NameUniquer {
156157
static std::string
157158
getTypeDescriptorBindingTableName(llvm::StringRef mangledTypeName);
158159

160+
/// Given a mangled derived type name and a component name, get the name of
161+
/// the global object containing the component default initialization.
162+
static std::string getComponentInitName(llvm::StringRef mangledTypeName,
163+
llvm::StringRef componentName);
164+
159165
/// Remove markers that have been added when doing partial type
160166
/// conversions. mlir::Type cannot be mutated in a pass, so new
161167
/// fir::RecordType must be created when lowering member types.

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

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,29 @@ inline void intrinsicTypeTODO(fir::FirOpBuilder &builder, mlir::Type type,
137137
" in " + intrinsicName);
138138
}
139139

140+
/// Find the fir.type_info that was created for this \p recordType in \p module,
141+
/// if any. \p symbolTable can be provided to speed-up the lookup. This tool
142+
/// will match record type even if they have been "altered" in type conversion
143+
/// passes.
144+
fir::TypeInfoOp
145+
lookupTypeInfoOp(fir::RecordType recordType, mlir::ModuleOp module,
146+
const mlir::SymbolTable *symbolTable = nullptr);
147+
148+
/// Find the fir.type_info named \p name in \p module, if any. \p symbolTable
149+
/// can be provided to speed-up the lookup. Prefer using the equivalent with a
150+
/// RecordType argument unless it is certain \p name has not been altered by a
151+
/// pass rewriting fir.type (see NameUniquer::dropTypeConversionMarkers).
152+
fir::TypeInfoOp
153+
lookupTypeInfoOp(llvm::StringRef name, mlir::ModuleOp module,
154+
const mlir::SymbolTable *symbolTable = nullptr);
155+
156+
/// Returns all lower bounds of \p component if it is an array component of \p
157+
/// recordType with non default lower bounds. Returns nullopt if this is not an
158+
/// array componnet of \p recordType or if its lower bounds are all ones.
159+
std::optional<llvm::ArrayRef<int64_t>> getComponentLowerBoundsIfNonDefault(
160+
fir::RecordType recordType, llvm::StringRef component,
161+
mlir::ModuleOp module, const mlir::SymbolTable *symbolTable = nullptr);
162+
140163
} // namespace fir
141164

142165
#endif // FORTRAN_OPTIMIZER_SUPPORT_UTILS_H

flang/lib/Lower/Bridge.cpp

Lines changed: 104 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,71 @@ struct ConstructContext {
148148
bool pushedScope = false; // was a scoped pushed for this construct?
149149
};
150150

151+
/// Helper to gather the lower bounds of array components with non deferred
152+
/// shape when they are not all ones. Return an empty array attribute otherwise.
153+
static mlir::DenseI64ArrayAttr
154+
gatherComponentNonDefaultLowerBounds(mlir::Location loc,
155+
mlir::MLIRContext *mlirContext,
156+
const Fortran::semantics::Symbol &sym) {
157+
if (Fortran::semantics::IsAllocatableOrObjectPointer(&sym))
158+
return {};
159+
mlir::DenseI64ArrayAttr lbs_attr;
160+
if (const auto *objDetails =
161+
sym.detailsIf<Fortran::semantics::ObjectEntityDetails>()) {
162+
llvm::SmallVector<std::int64_t> lbs;
163+
bool hasNonDefaultLbs = false;
164+
for (const Fortran::semantics::ShapeSpec &bounds : objDetails->shape())
165+
if (auto lb = bounds.lbound().GetExplicit()) {
166+
if (auto constant = Fortran::evaluate::ToInt64(*lb)) {
167+
hasNonDefaultLbs |= (*constant != 1);
168+
lbs.push_back(*constant);
169+
} else {
170+
TODO(loc, "generate fir.dt_component for length parametrized derived "
171+
"types");
172+
}
173+
}
174+
if (hasNonDefaultLbs) {
175+
assert(static_cast<int>(lbs.size()) == sym.Rank() &&
176+
"expected component bounds to be constant or deferred");
177+
lbs_attr = mlir::DenseI64ArrayAttr::get(mlirContext, lbs);
178+
}
179+
}
180+
return lbs_attr;
181+
}
182+
183+
// Helper class to generate name of fir.global containing component explicit
184+
// default value for objects, and initial procedure target for procedure pointer
185+
// components.
186+
static mlir::FlatSymbolRefAttr gatherComponentInit(
187+
mlir::Location loc, Fortran::lower::AbstractConverter &converter,
188+
const Fortran::semantics::Symbol &sym, fir::RecordType derivedType) {
189+
mlir::MLIRContext *mlirContext = &converter.getMLIRContext();
190+
// Return procedure target mangled name for procedure pointer components.
191+
if (const auto *procPtr =
192+
sym.detailsIf<Fortran::semantics::ProcEntityDetails>()) {
193+
if (std::optional<const Fortran::semantics::Symbol *> maybeInitSym =
194+
procPtr->init()) {
195+
// So far, do not make distinction between p => NULL() and p without init,
196+
// f18 always initialize pointers to NULL anyway.
197+
if (!*maybeInitSym)
198+
return {};
199+
return mlir::FlatSymbolRefAttr::get(mlirContext,
200+
converter.mangleName(**maybeInitSym));
201+
}
202+
}
203+
204+
const auto *objDetails =
205+
sym.detailsIf<Fortran::semantics::ObjectEntityDetails>();
206+
if (!objDetails || !objDetails->init().has_value())
207+
return {};
208+
// Object component initial value. Semantic package component object default
209+
// value into compiler generated symbols that are lowered as read-only
210+
// fir.global. Get the name of this global.
211+
std::string name = fir::NameUniquer::getComponentInitName(
212+
derivedType.getName(), toStringRef(sym.name()));
213+
return mlir::FlatSymbolRefAttr::get(mlirContext, name);
214+
}
215+
151216
/// Helper class to generate the runtime type info global data and the
152217
/// fir.type_info operations that contain the dipatch tables (if any).
153218
/// The type info global data is required to describe the derived type to the
@@ -213,15 +278,14 @@ class TypeInfoConverter {
213278
parentType = mlir::cast<fir::RecordType>(converter.genType(*parent));
214279

215280
fir::FirOpBuilder &builder = converter.getFirOpBuilder();
216-
mlir::ModuleOp module = builder.getModule();
217-
fir::TypeInfoOp dt =
218-
module.lookupSymbol<fir::TypeInfoOp>(info.type.getName());
219-
if (dt)
220-
return; // Already created.
221-
auto insertPt = builder.saveInsertionPoint();
222-
builder.setInsertionPoint(module.getBody(), module.getBody()->end());
223-
dt = builder.create<fir::TypeInfoOp>(info.loc, info.type, parentType);
224-
281+
fir::TypeInfoOp dt;
282+
mlir::OpBuilder::InsertPoint insertPointIfCreated;
283+
std::tie(dt, insertPointIfCreated) =
284+
builder.createTypeInfoOp(info.loc, info.type, parentType);
285+
if (!insertPointIfCreated.isSet())
286+
return; // fir.type_info was already built in a previous call.
287+
288+
// Set init, destroy, and nofinal attributes.
225289
if (!info.typeSpec.HasDefaultInitialization(/*ignoreAllocatable=*/false,
226290
/*ignorePointer=*/false))
227291
dt->setAttr(dt.getNoInitAttrName(), builder.getUnitAttr());
@@ -230,13 +294,12 @@ class TypeInfoConverter {
230294
if (!Fortran::semantics::MayRequireFinalization(info.typeSpec))
231295
dt->setAttr(dt.getNoFinalAttrName(), builder.getUnitAttr());
232296

233-
const Fortran::semantics::Scope *scope = info.typeSpec.scope();
234-
if (!scope)
235-
scope = info.typeSpec.typeSymbol().scope();
236-
assert(scope && "failed to find type scope");
297+
const Fortran::semantics::Scope &derivedScope =
298+
DEREF(info.typeSpec.GetScope());
237299

300+
// Fill binding table region if the derived type has bindings.
238301
Fortran::semantics::SymbolVector bindings =
239-
Fortran::semantics::CollectBindings(*scope);
302+
Fortran::semantics::CollectBindings(derivedScope);
240303
if (!bindings.empty()) {
241304
builder.createBlock(&dt.getDispatchTable());
242305
for (const Fortran::semantics::SymbolRef &binding : bindings) {
@@ -252,7 +315,33 @@ class TypeInfoConverter {
252315
}
253316
builder.create<fir::FirEndOp>(info.loc);
254317
}
255-
builder.restoreInsertionPoint(insertPt);
318+
// Gather info about components that is not reflected in fir.type and may be
319+
// needed later: component initial values and array component non default
320+
// lower bounds.
321+
mlir::Block *componentInfo = nullptr;
322+
for (const auto &componentName :
323+
info.typeSpec.typeSymbol()
324+
.get<Fortran::semantics::DerivedTypeDetails>()
325+
.componentNames()) {
326+
auto scopeIter = derivedScope.find(componentName);
327+
assert(scopeIter != derivedScope.cend() &&
328+
"failed to find derived type component symbol");
329+
const Fortran::semantics::Symbol &component = scopeIter->second.get();
330+
mlir::FlatSymbolRefAttr init_val =
331+
gatherComponentInit(info.loc, converter, component, info.type);
332+
mlir::DenseI64ArrayAttr lbs = gatherComponentNonDefaultLowerBounds(
333+
info.loc, builder.getContext(), component);
334+
if (init_val || lbs) {
335+
if (!componentInfo)
336+
componentInfo = builder.createBlock(&dt.getComponentInfo());
337+
auto compName = mlir::StringAttr::get(builder.getContext(),
338+
toStringRef(component.name()));
339+
builder.create<fir::DTComponentOp>(info.loc, compName, lbs, init_val);
340+
}
341+
}
342+
if (componentInfo)
343+
builder.create<fir::FirEndOp>(info.loc);
344+
builder.restoreInsertionPoint(insertPointIfCreated);
256345
}
257346

258347
/// Store the front-end data that will be required to generate the type info

flang/lib/Optimizer/Builder/FIRBuilder.cpp

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#include "flang/Optimizer/Dialect/FIRType.h"
2020
#include "flang/Optimizer/Support/FatalError.h"
2121
#include "flang/Optimizer/Support/InternalNames.h"
22+
#include "flang/Optimizer/Support/Utils.h"
2223
#include "mlir/Dialect/LLVMIR/LLVMDialect.h"
2324
#include "mlir/Dialect/OpenACC/OpenACC.h"
2425
#include "mlir/Dialect/OpenMP/OpenMPDialect.h"
@@ -364,6 +365,22 @@ fir::GlobalOp fir::FirOpBuilder::createGlobal(
364365
return glob;
365366
}
366367

368+
std::pair<fir::TypeInfoOp, mlir::OpBuilder::InsertPoint>
369+
fir::FirOpBuilder::createTypeInfoOp(mlir::Location loc,
370+
fir::RecordType recordType,
371+
fir::RecordType parentType) {
372+
mlir::ModuleOp module = getModule();
373+
if (fir::TypeInfoOp typeInfo =
374+
fir::lookupTypeInfoOp(recordType.getName(), module, symbolTable))
375+
return {typeInfo, InsertPoint{}};
376+
InsertPoint insertPoint = saveInsertionPoint();
377+
setInsertionPoint(module.getBody(), module.getBody()->end());
378+
auto typeInfo = create<fir::TypeInfoOp>(loc, recordType, parentType);
379+
if (symbolTable)
380+
symbolTable->insert(typeInfo);
381+
return {typeInfo, insertPoint};
382+
}
383+
367384
mlir::Value fir::FirOpBuilder::convertWithSemantics(
368385
mlir::Location loc, mlir::Type toTy, mlir::Value val,
369386
bool allowCharacterConversion, bool allowRebox) {

flang/lib/Optimizer/Dialect/FIROps.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1579,6 +1579,7 @@ void fir::TypeInfoOp::build(mlir::OpBuilder &builder,
15791579
fir::RecordType parentType,
15801580
llvm::ArrayRef<mlir::NamedAttribute> attrs) {
15811581
result.addRegion();
1582+
result.addRegion();
15821583
result.addAttribute(mlir::SymbolTable::getSymbolAttrName(),
15831584
builder.getStringAttr(type.getName()));
15841585
result.addAttribute(getTypeAttrName(result.name), mlir::TypeAttr::get(type));

flang/lib/Optimizer/Support/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ add_flang_library(FIRSupport
55
DataLayout.cpp
66
InitFIR.cpp
77
InternalNames.cpp
8+
Utils.cpp
89

910
DEPENDS
1011
FIROpsIncGen
@@ -14,6 +15,7 @@ add_flang_library(FIRSupport
1415
${extension_libs}
1516

1617
LINK_LIBS
18+
FIRDialect
1719
${dialect_libs}
1820
${extension_libs}
1921
MLIRBuiltinToLLVMIRTranslation

flang/lib/Optimizer/Support/InternalNames.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -381,6 +381,15 @@ std::string fir::NameUniquer::getTypeDescriptorBindingTableName(
381381
return getDerivedTypeObjectName(mangledTypeName, bindingTableSeparator);
382382
}
383383

384+
std::string
385+
fir::NameUniquer::getComponentInitName(llvm::StringRef mangledTypeName,
386+
llvm::StringRef componentName) {
387+
388+
std::string prefix =
389+
getDerivedTypeObjectName(mangledTypeName, componentInitSeparator);
390+
return prefix + "." + componentName.str();
391+
}
392+
384393
llvm::StringRef
385394
fir::NameUniquer::dropTypeConversionMarkers(llvm::StringRef mangledTypeName) {
386395
if (mangledTypeName.ends_with(boxprocSuffix))

flang/lib/Optimizer/Support/Utils.cpp

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
//===-- Utils.cpp ---------------------------------------------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
//
9+
// Coding style: https://mlir.llvm.org/getting_started/DeveloperGuide/
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
#include "flang/Optimizer/Support/Utils.h"
14+
#include "flang/Optimizer/Dialect/FIROps.h"
15+
#include "flang/Optimizer/Dialect/FIRType.h"
16+
#include "flang/Optimizer/Support/InternalNames.h"
17+
18+
fir::TypeInfoOp fir::lookupTypeInfoOp(fir::RecordType recordType,
19+
mlir::ModuleOp module,
20+
const mlir::SymbolTable *symbolTable) {
21+
// fir.type_info was created with the mangled name of the derived type.
22+
// It is the same as the name in the related fir.type, except when a pass
23+
// lowered the fir.type (e.g., when lowering fir.boxproc type if the type has
24+
// pointer procedure components), in which case suffix may have been added to
25+
// the fir.type name. Get rid of them when looking up for the fir.type_info.
26+
llvm::StringRef originalMangledTypeName =
27+
fir::NameUniquer::dropTypeConversionMarkers(recordType.getName());
28+
return fir::lookupTypeInfoOp(originalMangledTypeName, module, symbolTable);
29+
}
30+
31+
fir::TypeInfoOp fir::lookupTypeInfoOp(llvm::StringRef name,
32+
mlir::ModuleOp module,
33+
const mlir::SymbolTable *symbolTable) {
34+
if (symbolTable)
35+
if (auto typeInfo = symbolTable->lookup<fir::TypeInfoOp>(name))
36+
return typeInfo;
37+
return module.lookupSymbol<fir::TypeInfoOp>(name);
38+
}
39+
40+
std::optional<llvm::ArrayRef<int64_t>> fir::getComponentLowerBoundsIfNonDefault(
41+
fir::RecordType recordType, llvm::StringRef component,
42+
mlir::ModuleOp module, const mlir::SymbolTable *symbolTable) {
43+
fir::TypeInfoOp typeInfo =
44+
fir::lookupTypeInfoOp(recordType, module, symbolTable);
45+
if (!typeInfo || typeInfo.getComponentInfo().empty())
46+
return std::nullopt;
47+
for (auto componentInfo :
48+
typeInfo.getComponentInfo().getOps<fir::DTComponentOp>())
49+
if (componentInfo.getName() == component)
50+
return componentInfo.getLowerBounds();
51+
return std::nullopt;
52+
}

flang/test/Fir/fir-ops.fir

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -460,6 +460,13 @@ fir.type_info @dispatch_tbl : !fir.type<dispatch_tbl{i:i32}> dispatch_table {
460460
// CHECK-LABEL: fir.type_info @test_type_info noinit nodestroy nofinal extends !fir.type<parent{i:i32}> : !fir.type<test_type_info{i:i32,j:f32}>
461461
fir.type_info @test_type_info noinit nodestroy nofinal extends !fir.type<parent{i:i32}> : !fir.type<test_type_info{i:i32,j:f32}>
462462

463+
// CHECK-LABEL: fir.type_info @cpinfo : !fir.type<cpinfo{comp_i:!fir.array<10x20xi32>}> component_info {
464+
// CHECK: fir.dt_component "component_info" lbs [2, 3]
465+
// CHECK: }
466+
fir.type_info @cpinfo : !fir.type<cpinfo{comp_i:!fir.array<10x20xi32>}> component_info {
467+
fir.dt_component "component_info" lbs [2, 3]
468+
}
469+
463470
// CHECK-LABEL: func @compare_complex(
464471
// CHECK-SAME: [[VAL_151:%.*]]: !fir.complex<16>, [[VAL_152:%.*]]: !fir.complex<16>) {
465472
func.func @compare_complex(%a : !fir.complex<16>, %b : !fir.complex<16>) {

0 commit comments

Comments
 (0)