Skip to content

Commit 36fdeb2

Browse files
authored
[flang] Set LLVM specific attributes to fir.call's of Fortran runtime. (#128093)
This change is inspired by a case in facerec benchmark, where performance of scalar code may improve by about 6%@aarch64 due to getting rid of redundant loads from Fortran descriptors. These descriptors are corresponding to subroutine local ALLOCATABLE, SAVE variables. The scalar loop nest in LocalMove subroutine contains call to Fortran runtime IO functions, and LLVM globals-aa analysis cannot prove that these calls do not modify the globalized descriptors with internal linkage. This patch sets and propagates llvm.memory_effects attribute for fir.call operations calling Fortran runtime functions. In particular, it tries to set the Other memory effect to NoModRef. The Other memory effect includes accesses to globals and captured pointers, so we cannot set it for functions taking Fortran descriptors with one exception for calls where the Fortran descriptor arguments are all null. As long as different calls to the same Fortran runtime function may have different attributes, I decided to attach the attributes to the calls rather than functions. Moreover, attaching the attributes to func.func will require propagating these attributes to llvm.func, which is not happening right now. In addition to llvm.memory_effects, the new pass sets llvm.nosync and llvm.nocallback attributes that may also help LLVM alias analysis (e.g. see #127707). These attributes are ignored currently. I will support them in LLVM IR dialect in a separate patch. I also added another pass for developers to be able to print declarations/calls of all Fortran runtime functions that are recognized by the attributes setting pass. It should help with maintenance of the LIT tests.
1 parent cc7f22e commit 36fdeb2

File tree

19 files changed

+1971
-162
lines changed

19 files changed

+1971
-162
lines changed

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

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -385,6 +385,15 @@ class FirOpBuilder : public mlir::OpBuilder, public mlir::OpBuilder::Listener {
385385
mlir::FunctionType ty,
386386
mlir::SymbolTable *);
387387

388+
/// Returns a named function for a Fortran runtime API, creating
389+
/// it, if it does not exist in the module yet.
390+
/// If \p isIO is set to true, then the function corresponds
391+
/// to one of Fortran runtime IO APIs.
392+
mlir::func::FuncOp createRuntimeFunction(mlir::Location loc,
393+
llvm::StringRef name,
394+
mlir::FunctionType ty,
395+
bool isIO = false);
396+
388397
/// Cast the input value to IndexType.
389398
mlir::Value convertToIndexType(mlir::Location loc, mlir::Value val) {
390399
return createConvert(loc, getIndexType(), val);

flang/include/flang/Optimizer/Builder/Runtime/RTBuilder.h

Lines changed: 38 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
#include "flang/Optimizer/Builder/FIRBuilder.h"
2222
#include "flang/Optimizer/Dialect/FIRDialect.h"
2323
#include "flang/Optimizer/Dialect/FIRType.h"
24+
#include "flang/Runtime/io-api-consts.h"
2425
#include "flang/Runtime/reduce.h"
2526
#include "flang/Support/Fortran.h"
2627
#include "mlir/IR/BuiltinTypes.h"
@@ -586,6 +587,33 @@ constexpr TypeBuilderFunc getModel<void>() {
586587
};
587588
}
588589

590+
// Define additional runtime type models specific to IO.
591+
template <>
592+
constexpr TypeBuilderFunc getModel<Fortran::runtime::io::IoStatementState *>() {
593+
return getModel<char *>();
594+
}
595+
template <>
596+
constexpr TypeBuilderFunc getModel<Fortran::runtime::io::Iostat>() {
597+
return [](mlir::MLIRContext *context) -> mlir::Type {
598+
return mlir::IntegerType::get(context,
599+
8 * sizeof(Fortran::runtime::io::Iostat));
600+
};
601+
}
602+
template <>
603+
constexpr TypeBuilderFunc
604+
getModel<const Fortran::runtime::io::NamelistGroup &>() {
605+
return [](mlir::MLIRContext *context) -> mlir::Type {
606+
return fir::ReferenceType::get(mlir::TupleType::get(context));
607+
};
608+
}
609+
template <>
610+
constexpr TypeBuilderFunc
611+
getModel<const Fortran::runtime::io::NonTbpDefinedIoTable *>() {
612+
return [](mlir::MLIRContext *context) -> mlir::Type {
613+
return fir::ReferenceType::get(mlir::TupleType::get(context));
614+
};
615+
}
616+
589617
REDUCTION_REF_OPERATION_MODEL(std::int8_t)
590618
REDUCTION_VALUE_OPERATION_MODEL(std::int8_t)
591619
REDUCTION_REF_OPERATION_MODEL(std::int16_t)
@@ -778,16 +806,22 @@ struct RuntimeTableEntry<RuntimeTableKey<KT>, RuntimeIdentifier<Cs...>> {
778806
/// argument is intended to be of the form: <mkRTKey(runtime function name)>.
779807
template <typename RuntimeEntry>
780808
static mlir::func::FuncOp getRuntimeFunc(mlir::Location loc,
781-
fir::FirOpBuilder &builder) {
809+
fir::FirOpBuilder &builder,
810+
bool isIO = false) {
782811
using namespace Fortran::runtime;
783812
auto name = RuntimeEntry::name;
784813
auto func = builder.getNamedFunction(name);
785814
if (func)
786815
return func;
787816
auto funTy = RuntimeEntry::getTypeModel()(builder.getContext());
788-
func = builder.createFunction(loc, name, funTy);
789-
func->setAttr(FIROpsDialect::getFirRuntimeAttrName(), builder.getUnitAttr());
790-
return func;
817+
return builder.createRuntimeFunction(loc, name, funTy, isIO);
818+
}
819+
820+
/// Get (or generate) the MLIR FuncOp for a given IO runtime function.
821+
template <typename E>
822+
static mlir::func::FuncOp getIORuntimeFunc(mlir::Location loc,
823+
fir::FirOpBuilder &builder) {
824+
return getRuntimeFunc<E>(loc, builder, /*isIO=*/true);
791825
}
792826

793827
namespace helper {

flang/include/flang/Optimizer/Dialect/FIRDialect.td

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,18 @@ def FIROpsDialect : Dialect {
5656
static constexpr llvm::StringRef getFirRuntimeAttrName() {
5757
return "fir.runtime";
5858
}
59+
// Return string name of fir.memory attributes.
60+
// It is attached to fir.call operations to convey
61+
// llvm.memory attributes to LLVM IR.
62+
// Its value is intended to be mlir::LLVM::MemoryEffectsAttr.
63+
// TODO: we should probably make it an inherent attribute
64+
// of fir.call, though, it is supposed to be a short-lived
65+
// attribute that appears right before CodeGen and only
66+
// meaningful for LLVM, so it is unclear if embedding
67+
// it into fir.call makes sense.
68+
static constexpr llvm::StringRef getFirCallMemoryAttrName() {
69+
return "fir.llvm_memory";
70+
}
5971
}];
6072
}
6173

flang/include/flang/Optimizer/Transforms/Passes.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,8 @@ namespace fir {
6060
#define GEN_PASS_DECL_FUNCTIONATTR
6161
#define GEN_PASS_DECL_CONSTANTARGUMENTGLOBALISATIONOPT
6262
#define GEN_PASS_DECL_COMPILERGENERATEDNAMESCONVERSION
63+
#define GEN_PASS_DECL_SETRUNTIMECALLATTRIBUTES
64+
#define GEN_PASS_DECL_GENRUNTIMECALLSFORTEST
6365

6466
#include "flang/Optimizer/Transforms/Passes.h.inc"
6567

flang/include/flang/Optimizer/Transforms/Passes.td

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -453,4 +453,37 @@ def CUFGPUToLLVMConversion : Pass<"cuf-gpu-convert-to-llvm", "mlir::ModuleOp"> {
453453
];
454454
}
455455

456+
def SetRuntimeCallAttributes
457+
: Pass<"set-runtime-call-attrs", "mlir::func::FuncOp"> {
458+
let summary = "Set Fortran runtime fir.call attributes targeting LLVM IR";
459+
let description = [{
460+
This pass sets different attributes for Fortran runtime calls
461+
that enable more optimizations in LLVM backend.
462+
For the time being, the meaning of these attributes is not
463+
strictly defined for HLFIR/FIR.
464+
}];
465+
let dependentDialects = ["fir::FIROpsDialect", "mlir::LLVM::LLVMDialect"];
466+
}
467+
468+
def GenRuntimeCallsForTest
469+
: Pass<"gen-runtime-calls-for-test", "mlir::ModuleOp"> {
470+
let summary =
471+
"Print FIR containing declarations/calls of Fortran runtime functions";
472+
let description = [{
473+
This pass is only for developers to be able to print FIR
474+
that declares and calls Fortran runtime functions.
475+
It helps producing/updating tests for passes that modify
476+
the func/call operations based on some knowledge of
477+
Fortran runtime.
478+
}];
479+
let options =
480+
[Option<"doGenerateCalls", "do-generate-calls", "bool",
481+
/*default=*/"false",
482+
"Generate thin wrapper functions that call Fortran runtime "
483+
"functions. If it is set to false, then only the declarations "
484+
"are generated.">,
485+
];
486+
let dependentDialects = ["fir::FIROpsDialect", "mlir::func::FuncDialect"];
487+
}
488+
456489
#endif // FLANG_OPTIMIZER_TRANSFORMS_PASSES
Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
//===-- Optimizer/Transforms/RuntimeFunctions.inc ---------------*- C++ -*-===//
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+
#ifndef KNOWN_IO_FUNC
10+
#error "Define KNOWN_IO_FUNC before including this file"
11+
#endif
12+
#ifndef KNOWN_RUNTIME_FUNC
13+
#error "Define KNOWN_RUNTIME_FUNC before including this file"
14+
#endif
15+
16+
// Fortran runtime functions that SetRuntimeCallAttributesPass recognizes.
17+
// WARNING: if you add a function entry here, you must make sure
18+
// that the attribute computation callbacks that end up being
19+
// used are correct for this function. If needed, add
20+
// specializations for the types that provide attribute
21+
// computation callbacks in SetRuntimeCallAttributesPass.
22+
23+
// clang-format off
24+
KNOWN_IO_FUNC(BeginBackspace),
25+
KNOWN_IO_FUNC(BeginClose),
26+
KNOWN_IO_FUNC(BeginEndfile),
27+
KNOWN_IO_FUNC(BeginExternalFormattedInput),
28+
KNOWN_IO_FUNC(BeginExternalFormattedOutput),
29+
KNOWN_IO_FUNC(BeginExternalListInput),
30+
KNOWN_IO_FUNC(BeginExternalListOutput),
31+
KNOWN_IO_FUNC(BeginFlush),
32+
KNOWN_IO_FUNC(BeginInquireFile),
33+
KNOWN_IO_FUNC(BeginInquireIoLength),
34+
KNOWN_IO_FUNC(BeginInquireUnit),
35+
KNOWN_IO_FUNC(BeginInternalArrayFormattedInput),
36+
KNOWN_IO_FUNC(BeginInternalArrayFormattedOutput),
37+
KNOWN_IO_FUNC(BeginInternalArrayListInput),
38+
KNOWN_IO_FUNC(BeginInternalArrayListOutput),
39+
KNOWN_IO_FUNC(BeginInternalFormattedInput),
40+
KNOWN_IO_FUNC(BeginInternalFormattedOutput),
41+
KNOWN_IO_FUNC(BeginInternalListInput),
42+
KNOWN_IO_FUNC(BeginInternalListOutput),
43+
KNOWN_IO_FUNC(BeginOpenNewUnit),
44+
KNOWN_IO_FUNC(BeginOpenUnit),
45+
KNOWN_IO_FUNC(BeginRewind),
46+
KNOWN_IO_FUNC(BeginUnformattedInput),
47+
KNOWN_IO_FUNC(BeginUnformattedOutput),
48+
KNOWN_IO_FUNC(BeginWait),
49+
KNOWN_IO_FUNC(BeginWaitAll),
50+
KNOWN_IO_FUNC(CheckUnitNumberInRange128),
51+
KNOWN_IO_FUNC(CheckUnitNumberInRange64),
52+
KNOWN_IO_FUNC(EnableHandlers),
53+
KNOWN_IO_FUNC(EndIoStatement),
54+
KNOWN_IO_FUNC(GetAsynchronousId),
55+
KNOWN_IO_FUNC(GetIoLength),
56+
KNOWN_IO_FUNC(GetIoMsg),
57+
KNOWN_IO_FUNC(GetNewUnit),
58+
KNOWN_IO_FUNC(GetSize),
59+
KNOWN_IO_FUNC(InputAscii),
60+
KNOWN_IO_FUNC(InputComplex32),
61+
KNOWN_IO_FUNC(InputComplex64),
62+
KNOWN_IO_FUNC(InputDerivedType),
63+
KNOWN_IO_FUNC(InputDescriptor),
64+
KNOWN_IO_FUNC(InputInteger),
65+
KNOWN_IO_FUNC(InputLogical),
66+
KNOWN_IO_FUNC(InputNamelist),
67+
KNOWN_IO_FUNC(InputReal32),
68+
KNOWN_IO_FUNC(InputReal64),
69+
KNOWN_IO_FUNC(InquireCharacter),
70+
KNOWN_IO_FUNC(InquireInteger64),
71+
KNOWN_IO_FUNC(InquireLogical),
72+
KNOWN_IO_FUNC(InquirePendingId),
73+
KNOWN_IO_FUNC(OutputAscii),
74+
KNOWN_IO_FUNC(OutputComplex32),
75+
KNOWN_IO_FUNC(OutputComplex64),
76+
KNOWN_IO_FUNC(OutputDerivedType),
77+
KNOWN_IO_FUNC(OutputDescriptor),
78+
KNOWN_IO_FUNC(OutputInteger128),
79+
KNOWN_IO_FUNC(OutputInteger16),
80+
KNOWN_IO_FUNC(OutputInteger32),
81+
KNOWN_IO_FUNC(OutputInteger64),
82+
KNOWN_IO_FUNC(OutputInteger8),
83+
KNOWN_IO_FUNC(OutputLogical),
84+
KNOWN_IO_FUNC(OutputNamelist),
85+
KNOWN_IO_FUNC(OutputReal32),
86+
KNOWN_IO_FUNC(OutputReal64),
87+
KNOWN_IO_FUNC(SetAccess),
88+
KNOWN_IO_FUNC(SetAction),
89+
KNOWN_IO_FUNC(SetAdvance),
90+
KNOWN_IO_FUNC(SetAsynchronous),
91+
KNOWN_IO_FUNC(SetBlank),
92+
KNOWN_IO_FUNC(SetCarriagecontrol),
93+
KNOWN_IO_FUNC(SetConvert),
94+
KNOWN_IO_FUNC(SetDecimal),
95+
KNOWN_IO_FUNC(SetDelim),
96+
KNOWN_IO_FUNC(SetEncoding),
97+
KNOWN_IO_FUNC(SetFile),
98+
KNOWN_IO_FUNC(SetForm),
99+
KNOWN_IO_FUNC(SetPad),
100+
KNOWN_IO_FUNC(SetPos),
101+
KNOWN_IO_FUNC(SetPosition),
102+
KNOWN_IO_FUNC(SetRec),
103+
KNOWN_IO_FUNC(SetRecl),
104+
KNOWN_IO_FUNC(SetRound),
105+
KNOWN_IO_FUNC(SetSign),
106+
KNOWN_IO_FUNC(SetStatus)
107+
108+
// clang-format on
109+
110+
#undef KNOWN_IO_FUNC
111+
#undef KNOWN_RUNTIME_FUNC

0 commit comments

Comments
 (0)