-
Notifications
You must be signed in to change notification settings - Fork 14.3k
[flang] Set LLVM specific attributes to fir.call's of Fortran runtime. #128093
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
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 llvm#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.
@llvm/pr-subscribers-flang-driver Author: Slava Zakharin (vzakhari) ChangesThis change is inspired by a case in facerec benchmark, where performance This patch sets and propagates llvm.memory_effects attribute for fir.call As long as different calls to the same Fortran runtime function may have In addition to llvm.memory_effects, the new pass sets llvm.nosync I also added another pass for developers to be able to print Patch is 183.39 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/128093.diff 19 Files Affected:
diff --git a/flang/include/flang/Optimizer/Builder/FIRBuilder.h b/flang/include/flang/Optimizer/Builder/FIRBuilder.h
index 93eca78424775..1675c15363868 100644
--- a/flang/include/flang/Optimizer/Builder/FIRBuilder.h
+++ b/flang/include/flang/Optimizer/Builder/FIRBuilder.h
@@ -385,6 +385,15 @@ class FirOpBuilder : public mlir::OpBuilder, public mlir::OpBuilder::Listener {
mlir::FunctionType ty,
mlir::SymbolTable *);
+ /// Returns a named function for a Fortran runtime API, creating
+ /// it, if it does not exist in the module yet.
+ /// If \p isIO is set to true, then the function corresponds
+ /// to one of Fortran runtime IO APIs.
+ mlir::func::FuncOp createRuntimeFunction(mlir::Location loc,
+ llvm::StringRef name,
+ mlir::FunctionType ty,
+ bool isIO = false);
+
/// Cast the input value to IndexType.
mlir::Value convertToIndexType(mlir::Location loc, mlir::Value val) {
return createConvert(loc, getIndexType(), val);
diff --git a/flang/include/flang/Optimizer/Builder/Runtime/RTBuilder.h b/flang/include/flang/Optimizer/Builder/Runtime/RTBuilder.h
index 1ffc354d6b80f..5158abaa31ed1 100644
--- a/flang/include/flang/Optimizer/Builder/Runtime/RTBuilder.h
+++ b/flang/include/flang/Optimizer/Builder/Runtime/RTBuilder.h
@@ -21,6 +21,7 @@
#include "flang/Optimizer/Builder/FIRBuilder.h"
#include "flang/Optimizer/Dialect/FIRDialect.h"
#include "flang/Optimizer/Dialect/FIRType.h"
+#include "flang/Runtime/io-api-consts.h"
#include "flang/Runtime/reduce.h"
#include "flang/Support/Fortran.h"
#include "mlir/IR/BuiltinTypes.h"
@@ -586,6 +587,33 @@ constexpr TypeBuilderFunc getModel<void>() {
};
}
+// Define additional runtime type models specific to IO.
+template <>
+constexpr TypeBuilderFunc getModel<Fortran::runtime::io::IoStatementState *>() {
+ return getModel<char *>();
+}
+template <>
+constexpr TypeBuilderFunc getModel<Fortran::runtime::io::Iostat>() {
+ return [](mlir::MLIRContext *context) -> mlir::Type {
+ return mlir::IntegerType::get(context,
+ 8 * sizeof(Fortran::runtime::io::Iostat));
+ };
+}
+template <>
+constexpr TypeBuilderFunc
+getModel<const Fortran::runtime::io::NamelistGroup &>() {
+ return [](mlir::MLIRContext *context) -> mlir::Type {
+ return fir::ReferenceType::get(mlir::TupleType::get(context));
+ };
+}
+template <>
+constexpr TypeBuilderFunc
+getModel<const Fortran::runtime::io::NonTbpDefinedIoTable *>() {
+ return [](mlir::MLIRContext *context) -> mlir::Type {
+ return fir::ReferenceType::get(mlir::TupleType::get(context));
+ };
+}
+
REDUCTION_REF_OPERATION_MODEL(std::int8_t)
REDUCTION_VALUE_OPERATION_MODEL(std::int8_t)
REDUCTION_REF_OPERATION_MODEL(std::int16_t)
@@ -778,16 +806,22 @@ struct RuntimeTableEntry<RuntimeTableKey<KT>, RuntimeIdentifier<Cs...>> {
/// argument is intended to be of the form: <mkRTKey(runtime function name)>.
template <typename RuntimeEntry>
static mlir::func::FuncOp getRuntimeFunc(mlir::Location loc,
- fir::FirOpBuilder &builder) {
+ fir::FirOpBuilder &builder,
+ bool isIO = false) {
using namespace Fortran::runtime;
auto name = RuntimeEntry::name;
auto func = builder.getNamedFunction(name);
if (func)
return func;
auto funTy = RuntimeEntry::getTypeModel()(builder.getContext());
- func = builder.createFunction(loc, name, funTy);
- func->setAttr(FIROpsDialect::getFirRuntimeAttrName(), builder.getUnitAttr());
- return func;
+ return builder.createRuntimeFunction(loc, name, funTy, isIO);
+}
+
+/// Get (or generate) the MLIR FuncOp for a given IO runtime function.
+template <typename E>
+static mlir::func::FuncOp getIORuntimeFunc(mlir::Location loc,
+ fir::FirOpBuilder &builder) {
+ return getRuntimeFunc<E>(loc, builder, /*isIO=*/true);
}
namespace helper {
diff --git a/flang/include/flang/Optimizer/Dialect/FIRDialect.td b/flang/include/flang/Optimizer/Dialect/FIRDialect.td
index 0dfb3eda585ce..b05f4e731bc73 100644
--- a/flang/include/flang/Optimizer/Dialect/FIRDialect.td
+++ b/flang/include/flang/Optimizer/Dialect/FIRDialect.td
@@ -56,6 +56,18 @@ def FIROpsDialect : Dialect {
static constexpr llvm::StringRef getFirRuntimeAttrName() {
return "fir.runtime";
}
+ // Return string name of fir.memory attributes.
+ // It is attached to fir.call operations to convey
+ // llvm.memory attributes to LLVM IR.
+ // Its value is intended to be mlir::LLVM::MemoryEffectsAttr.
+ // TODO: we should probably make it an inherent attribute
+ // of fir.call, though, it is supposed to be a short-lived
+ // attribute that appears right before CodeGen and only
+ // meaningful for LLVM, so it is unclear if embedding
+ // it into fir.call makes sense.
+ static constexpr llvm::StringRef getFirCallMemoryAttrName() {
+ return "fir.llvm_memory";
+ }
}];
}
diff --git a/flang/include/flang/Optimizer/Transforms/Passes.h b/flang/include/flang/Optimizer/Transforms/Passes.h
index 10e1c999d4533..afbbeb55632f1 100644
--- a/flang/include/flang/Optimizer/Transforms/Passes.h
+++ b/flang/include/flang/Optimizer/Transforms/Passes.h
@@ -60,6 +60,8 @@ namespace fir {
#define GEN_PASS_DECL_FUNCTIONATTR
#define GEN_PASS_DECL_CONSTANTARGUMENTGLOBALISATIONOPT
#define GEN_PASS_DECL_COMPILERGENERATEDNAMESCONVERSION
+#define GEN_PASS_DECL_SETRUNTIMECALLATTRIBUTES
+#define GEN_PASS_DECL_GENRUNTIMECALLSFORTEST
#include "flang/Optimizer/Transforms/Passes.h.inc"
diff --git a/flang/include/flang/Optimizer/Transforms/Passes.td b/flang/include/flang/Optimizer/Transforms/Passes.td
index 0b6e0119c16c3..64341b42bd1e4 100644
--- a/flang/include/flang/Optimizer/Transforms/Passes.td
+++ b/flang/include/flang/Optimizer/Transforms/Passes.td
@@ -453,4 +453,37 @@ def CUFGPUToLLVMConversion : Pass<"cuf-gpu-convert-to-llvm", "mlir::ModuleOp"> {
];
}
+def SetRuntimeCallAttributes
+ : Pass<"set-runtime-call-attrs", "mlir::func::FuncOp"> {
+ let summary = "Set Fortran runtime fir.call attributes targeting LLVM IR";
+ let description = [{
+ This pass sets different attributes for Fortran runtime calls
+ that enable more optimizations in LLVM backend.
+ For the time being, the meaning of these attributes is not
+ strictly defined for HLFIR/FIR.
+ }];
+ let dependentDialects = ["fir::FIROpsDialect", "mlir::LLVM::LLVMDialect"];
+}
+
+def GenRuntimeCallsForTest
+ : Pass<"gen-runtime-calls-for-test", "mlir::ModuleOp"> {
+ let summary =
+ "Print FIR containing declarations/calls of Fortran runtime functions";
+ let description = [{
+ This pass is only for developers to be able to print FIR
+ that declares and calls Fortran runtime functions.
+ It helps producing/updating tests for passes that modify
+ the func/call operations based on some knowledge of
+ Fortran runtime.
+ }];
+ let options =
+ [Option<"doGenerateCalls", "do-generate-calls", "bool",
+ /*default=*/"false",
+ "Generate thin wrapper functions that call Fortran runtime "
+ "functions. If it is set to false, then only the declarations "
+ "are generated.">,
+ ];
+ let dependentDialects = ["fir::FIROpsDialect", "mlir::func::FuncDialect"];
+}
+
#endif // FLANG_OPTIMIZER_TRANSFORMS_PASSES
diff --git a/flang/include/flang/Optimizer/Transforms/RuntimeFunctions.inc b/flang/include/flang/Optimizer/Transforms/RuntimeFunctions.inc
new file mode 100644
index 0000000000000..cb4bf4ecf559d
--- /dev/null
+++ b/flang/include/flang/Optimizer/Transforms/RuntimeFunctions.inc
@@ -0,0 +1,111 @@
+//===-- Optimizer/Transforms/RuntimeFunctions.inc ---------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef KNOWN_IO_FUNC
+#error "Define KNOWN_IO_FUNC before including this file"
+#endif
+#ifndef KNOWN_RUNTIME_FUNC
+#error "Define KNOWN_RUNTIME_FUNC before including this file"
+#endif
+
+// Fortran runtime functions that SetRuntimeCallAttributesPass recognizes.
+// WARNING: if you add a function entry here, you must make sure
+// that the attribute computation callbacks that end up being
+// used are correct for this function. If needed, add
+// specializations for the types that provide attribute
+// computation callbacks in SetRuntimeCallAttributesPass.
+
+// clang-format off
+KNOWN_IO_FUNC(BeginBackspace),
+KNOWN_IO_FUNC(BeginClose),
+KNOWN_IO_FUNC(BeginEndfile),
+KNOWN_IO_FUNC(BeginExternalFormattedInput),
+KNOWN_IO_FUNC(BeginExternalFormattedOutput),
+KNOWN_IO_FUNC(BeginExternalListInput),
+KNOWN_IO_FUNC(BeginExternalListOutput),
+KNOWN_IO_FUNC(BeginFlush),
+KNOWN_IO_FUNC(BeginInquireFile),
+KNOWN_IO_FUNC(BeginInquireIoLength),
+KNOWN_IO_FUNC(BeginInquireUnit),
+KNOWN_IO_FUNC(BeginInternalArrayFormattedInput),
+KNOWN_IO_FUNC(BeginInternalArrayFormattedOutput),
+KNOWN_IO_FUNC(BeginInternalArrayListInput),
+KNOWN_IO_FUNC(BeginInternalArrayListOutput),
+KNOWN_IO_FUNC(BeginInternalFormattedInput),
+KNOWN_IO_FUNC(BeginInternalFormattedOutput),
+KNOWN_IO_FUNC(BeginInternalListInput),
+KNOWN_IO_FUNC(BeginInternalListOutput),
+KNOWN_IO_FUNC(BeginOpenNewUnit),
+KNOWN_IO_FUNC(BeginOpenUnit),
+KNOWN_IO_FUNC(BeginRewind),
+KNOWN_IO_FUNC(BeginUnformattedInput),
+KNOWN_IO_FUNC(BeginUnformattedOutput),
+KNOWN_IO_FUNC(BeginWait),
+KNOWN_IO_FUNC(BeginWaitAll),
+KNOWN_IO_FUNC(CheckUnitNumberInRange128),
+KNOWN_IO_FUNC(CheckUnitNumberInRange64),
+KNOWN_IO_FUNC(EnableHandlers),
+KNOWN_IO_FUNC(EndIoStatement),
+KNOWN_IO_FUNC(GetAsynchronousId),
+KNOWN_IO_FUNC(GetIoLength),
+KNOWN_IO_FUNC(GetIoMsg),
+KNOWN_IO_FUNC(GetNewUnit),
+KNOWN_IO_FUNC(GetSize),
+KNOWN_IO_FUNC(InputAscii),
+KNOWN_IO_FUNC(InputComplex32),
+KNOWN_IO_FUNC(InputComplex64),
+KNOWN_IO_FUNC(InputDerivedType),
+KNOWN_IO_FUNC(InputDescriptor),
+KNOWN_IO_FUNC(InputInteger),
+KNOWN_IO_FUNC(InputLogical),
+KNOWN_IO_FUNC(InputNamelist),
+KNOWN_IO_FUNC(InputReal32),
+KNOWN_IO_FUNC(InputReal64),
+KNOWN_IO_FUNC(InquireCharacter),
+KNOWN_IO_FUNC(InquireInteger64),
+KNOWN_IO_FUNC(InquireLogical),
+KNOWN_IO_FUNC(InquirePendingId),
+KNOWN_IO_FUNC(OutputAscii),
+KNOWN_IO_FUNC(OutputComplex32),
+KNOWN_IO_FUNC(OutputComplex64),
+KNOWN_IO_FUNC(OutputDerivedType),
+KNOWN_IO_FUNC(OutputDescriptor),
+KNOWN_IO_FUNC(OutputInteger128),
+KNOWN_IO_FUNC(OutputInteger16),
+KNOWN_IO_FUNC(OutputInteger32),
+KNOWN_IO_FUNC(OutputInteger64),
+KNOWN_IO_FUNC(OutputInteger8),
+KNOWN_IO_FUNC(OutputLogical),
+KNOWN_IO_FUNC(OutputNamelist),
+KNOWN_IO_FUNC(OutputReal32),
+KNOWN_IO_FUNC(OutputReal64),
+KNOWN_IO_FUNC(SetAccess),
+KNOWN_IO_FUNC(SetAction),
+KNOWN_IO_FUNC(SetAdvance),
+KNOWN_IO_FUNC(SetAsynchronous),
+KNOWN_IO_FUNC(SetBlank),
+KNOWN_IO_FUNC(SetCarriagecontrol),
+KNOWN_IO_FUNC(SetConvert),
+KNOWN_IO_FUNC(SetDecimal),
+KNOWN_IO_FUNC(SetDelim),
+KNOWN_IO_FUNC(SetEncoding),
+KNOWN_IO_FUNC(SetFile),
+KNOWN_IO_FUNC(SetForm),
+KNOWN_IO_FUNC(SetPad),
+KNOWN_IO_FUNC(SetPos),
+KNOWN_IO_FUNC(SetPosition),
+KNOWN_IO_FUNC(SetRec),
+KNOWN_IO_FUNC(SetRecl),
+KNOWN_IO_FUNC(SetRound),
+KNOWN_IO_FUNC(SetSign),
+KNOWN_IO_FUNC(SetStatus)
+
+// clang-format on
+
+#undef KNOWN_IO_FUNC
+#undef KNOWN_RUNTIME_FUNC
diff --git a/flang/lib/Lower/IO.cpp b/flang/lib/Lower/IO.cpp
index 75453721d91a2..35068f0519cbd 100644
--- a/flang/lib/Lower/IO.cpp
+++ b/flang/lib/Lower/IO.cpp
@@ -43,35 +43,6 @@
#define DEBUG_TYPE "flang-lower-io"
-// Define additional runtime type models specific to IO.
-namespace fir::runtime {
-template <>
-constexpr TypeBuilderFunc getModel<Fortran::runtime::io::IoStatementState *>() {
- return getModel<char *>();
-}
-template <>
-constexpr TypeBuilderFunc getModel<Fortran::runtime::io::Iostat>() {
- return [](mlir::MLIRContext *context) -> mlir::Type {
- return mlir::IntegerType::get(context,
- 8 * sizeof(Fortran::runtime::io::Iostat));
- };
-}
-template <>
-constexpr TypeBuilderFunc
-getModel<const Fortran::runtime::io::NamelistGroup &>() {
- return [](mlir::MLIRContext *context) -> mlir::Type {
- return fir::ReferenceType::get(mlir::TupleType::get(context));
- };
-}
-template <>
-constexpr TypeBuilderFunc
-getModel<const Fortran::runtime::io::NonTbpDefinedIoTable *>() {
- return [](mlir::MLIRContext *context) -> mlir::Type {
- return fir::ReferenceType::get(mlir::TupleType::get(context));
- };
-}
-} // namespace fir::runtime
-
using namespace Fortran::runtime::io;
#define mkIOKey(X) FirmkKey(IONAME(X))
@@ -172,22 +143,6 @@ inline int64_t getLength(mlir::Type argTy) {
return mlir::cast<fir::SequenceType>(argTy).getShape()[0];
}
-/// Get (or generate) the MLIR FuncOp for a given IO runtime function.
-template <typename E>
-static mlir::func::FuncOp getIORuntimeFunc(mlir::Location loc,
- fir::FirOpBuilder &builder) {
- llvm::StringRef name = getName<E>();
- mlir::func::FuncOp func = builder.getNamedFunction(name);
- if (func)
- return func;
- auto funTy = getTypeModel<E>()(builder.getContext());
- func = builder.createFunction(loc, name, funTy);
- func->setAttr(fir::FIROpsDialect::getFirRuntimeAttrName(),
- builder.getUnitAttr());
- func->setAttr("fir.io", builder.getUnitAttr());
- return func;
-}
-
/// Generate calls to end an IO statement. Return the IOSTAT value, if any.
/// It is the caller's responsibility to generate branches on that value.
static mlir::Value genEndIO(Fortran::lower::AbstractConverter &converter,
@@ -197,7 +152,7 @@ static mlir::Value genEndIO(Fortran::lower::AbstractConverter &converter,
fir::FirOpBuilder &builder = converter.getFirOpBuilder();
if (csi.ioMsg) {
mlir::func::FuncOp getIoMsg =
- getIORuntimeFunc<mkIOKey(GetIoMsg)>(loc, builder);
+ fir::runtime::getIORuntimeFunc<mkIOKey(GetIoMsg)>(loc, builder);
builder.create<fir::CallOp>(
loc, getIoMsg,
mlir::ValueRange{
@@ -208,7 +163,7 @@ static mlir::Value genEndIO(Fortran::lower::AbstractConverter &converter,
fir::getLen(*csi.ioMsg))});
}
mlir::func::FuncOp endIoStatement =
- getIORuntimeFunc<mkIOKey(EndIoStatement)>(loc, builder);
+ fir::runtime::getIORuntimeFunc<mkIOKey(EndIoStatement)>(loc, builder);
auto call = builder.create<fir::CallOp>(loc, endIoStatement,
mlir::ValueRange{cookie});
mlir::Value iostat = call.getResult(0);
@@ -659,45 +614,57 @@ static mlir::func::FuncOp getOutputFunc(mlir::Location loc,
fir::FirOpBuilder &builder,
mlir::Type type, bool isFormatted) {
if (mlir::isa<fir::RecordType>(fir::unwrapPassByRefType(type)))
- return getIORuntimeFunc<mkIOKey(OutputDerivedType)>(loc, builder);
+ return fir::runtime::getIORuntimeFunc<mkIOKey(OutputDerivedType)>(loc,
+ builder);
if (!isFormatted)
- return getIORuntimeFunc<mkIOKey(OutputDescriptor)>(loc, builder);
+ return fir::runtime::getIORuntimeFunc<mkIOKey(OutputDescriptor)>(loc,
+ builder);
if (auto ty = mlir::dyn_cast<mlir::IntegerType>(type)) {
if (!ty.isUnsigned()) {
switch (ty.getWidth()) {
case 1:
- return getIORuntimeFunc<mkIOKey(OutputLogical)>(loc, builder);
+ return fir::runtime::getIORuntimeFunc<mkIOKey(OutputLogical)>(loc,
+ builder);
case 8:
- return getIORuntimeFunc<mkIOKey(OutputInteger8)>(loc, builder);
+ return fir::runtime::getIORuntimeFunc<mkIOKey(OutputInteger8)>(loc,
+ builder);
case 16:
- return getIORuntimeFunc<mkIOKey(OutputInteger16)>(loc, builder);
+ return fir::runtime::getIORuntimeFunc<mkIOKey(OutputInteger16)>(
+ loc, builder);
case 32:
- return getIORuntimeFunc<mkIOKey(OutputInteger32)>(loc, builder);
+ return fir::runtime::getIORuntimeFunc<mkIOKey(OutputInteger32)>(
+ loc, builder);
case 64:
- return getIORuntimeFunc<mkIOKey(OutputInteger64)>(loc, builder);
+ return fir::runtime::getIORuntimeFunc<mkIOKey(OutputInteger64)>(
+ loc, builder);
case 128:
- return getIORuntimeFunc<mkIOKey(OutputInteger128)>(loc, builder);
+ return fir::runtime::getIORuntimeFunc<mkIOKey(OutputInteger128)>(
+ loc, builder);
}
llvm_unreachable("unknown OutputInteger kind");
}
}
if (auto ty = mlir::dyn_cast<mlir::FloatType>(type)) {
if (auto width = ty.getWidth(); width == 32)
- return getIORuntimeFunc<mkIOKey(OutputReal32)>(loc, builder);
+ return fir::runtime::getIORuntimeFunc<mkIOKey(OutputReal32)>(loc,
+ builder);
else if (width == 64)
- return getIORuntimeFunc<mkIOKey(OutputReal64)>(loc, builder);
+ return fir::runtime::getIORuntimeFunc<mkIOKey(OutputReal64)>(loc,
+ builder);
}
auto kindMap = fir::getKindMapping(builder.getModule());
if (auto ty = mlir::dyn_cast<mlir::ComplexType>(type)) {
// COMPLEX(KIND=k) corresponds to a pair of REAL(KIND=k).
auto width = mlir::cast<mlir::FloatType>(ty.getElementType()).getWidth();
if (width == 32)
- return getIORuntimeFunc<mkIOKey(OutputComplex32)>(loc, builder);
+ return fir::runtime::getIORuntimeFunc<mkIOKey(OutputComplex32)>(loc,
+ builder);
else if (width == 64)
- return getIORuntimeFunc<mkIOKey(OutputComplex64)>(loc, builder);
+ return fir::runtime::getIORuntimeFunc<mkIOKey(OutputComplex64)>(loc,
+ builder);
}
if (mlir::isa<fir::LogicalType>(type))
- return getIORuntimeFunc<mkIOKey(OutputLogical)>(loc, builder);
+ return fir::runtime::getIORuntimeFunc<mkIOKey(OutputLogical)>(loc, builder);
if (fir::factory::CharacterExprHelper::isCharacterScalar(type)) {
// TODO: What would it mean if the default CHARACTER KIND is set to a wide
// character encoding scheme? How do we handle UTF-8? Is it a distinct KIND
@@ -706,9 +673,10 @@ static mlir::func::FuncOp getOutputFunc(mlir::Location loc,
auto asciiKind = kindMap.defaultCharacterKind();
if (kindMap.getCharacterBitsize(asciiKind) == 8 &&
fir::factory::CharacterExprHelper::getCharacterKind(type) == asciiKind)
- return getIORuntimeFunc<mkIOKey(OutputAscii)>(loc, builder);
+ return fir::runtime::getIORuntimeFunc<mkIOKey(OutputAscii)>(loc, builder);
}
- return getIORuntimeFunc<mkIOKey(OutputDescriptor)>(loc, builder);
+ return fir::runtime::getIORuntimeFunc<mkIOKey(OutputDescriptor)>(loc,
+ builder);
}
/// Generate a sequence of output data transfer calls.
@@ -778,39 +746,46 @@ static mlir::func::FuncOp getInputFunc(mlir::Location loc,
fir::FirOpBuilder &builder,
mlir::Type type, bool isFormatted) {
if (mlir::isa<fir::RecordType>(fir::unwrapPassByRefType(type)))
- return getIORuntimeFunc<mkIOKey(InputDerivedType)>(loc, builder);
+ return fir::runtime::getIORuntimeFunc<mkIOKey(InputDerivedType)>(loc,
+ builder);
if (!isFormatted)
- return getIORuntimeFunc<mkIOKey(InputDescriptor)>(loc, builder);
+ return fir::runtime::getIORuntimeFunc<mkIOKey(InputDescriptor)>(loc,
+ builder);
if (auto ty = mlir::dyn_cast<mlir::IntegerType>(type)) {
if (type.isUnsignedInteger())
- ...
[truncated]
|
@llvm/pr-subscribers-flang-fir-hlfir Author: Slava Zakharin (vzakhari) ChangesThis change is inspired by a case in facerec benchmark, where performance This patch sets and propagates llvm.memory_effects attribute for fir.call As long as different calls to the same Fortran runtime function may have In addition to llvm.memory_effects, the new pass sets llvm.nosync I also added another pass for developers to be able to print Patch is 183.39 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/128093.diff 19 Files Affected:
diff --git a/flang/include/flang/Optimizer/Builder/FIRBuilder.h b/flang/include/flang/Optimizer/Builder/FIRBuilder.h
index 93eca78424775..1675c15363868 100644
--- a/flang/include/flang/Optimizer/Builder/FIRBuilder.h
+++ b/flang/include/flang/Optimizer/Builder/FIRBuilder.h
@@ -385,6 +385,15 @@ class FirOpBuilder : public mlir::OpBuilder, public mlir::OpBuilder::Listener {
mlir::FunctionType ty,
mlir::SymbolTable *);
+ /// Returns a named function for a Fortran runtime API, creating
+ /// it, if it does not exist in the module yet.
+ /// If \p isIO is set to true, then the function corresponds
+ /// to one of Fortran runtime IO APIs.
+ mlir::func::FuncOp createRuntimeFunction(mlir::Location loc,
+ llvm::StringRef name,
+ mlir::FunctionType ty,
+ bool isIO = false);
+
/// Cast the input value to IndexType.
mlir::Value convertToIndexType(mlir::Location loc, mlir::Value val) {
return createConvert(loc, getIndexType(), val);
diff --git a/flang/include/flang/Optimizer/Builder/Runtime/RTBuilder.h b/flang/include/flang/Optimizer/Builder/Runtime/RTBuilder.h
index 1ffc354d6b80f..5158abaa31ed1 100644
--- a/flang/include/flang/Optimizer/Builder/Runtime/RTBuilder.h
+++ b/flang/include/flang/Optimizer/Builder/Runtime/RTBuilder.h
@@ -21,6 +21,7 @@
#include "flang/Optimizer/Builder/FIRBuilder.h"
#include "flang/Optimizer/Dialect/FIRDialect.h"
#include "flang/Optimizer/Dialect/FIRType.h"
+#include "flang/Runtime/io-api-consts.h"
#include "flang/Runtime/reduce.h"
#include "flang/Support/Fortran.h"
#include "mlir/IR/BuiltinTypes.h"
@@ -586,6 +587,33 @@ constexpr TypeBuilderFunc getModel<void>() {
};
}
+// Define additional runtime type models specific to IO.
+template <>
+constexpr TypeBuilderFunc getModel<Fortran::runtime::io::IoStatementState *>() {
+ return getModel<char *>();
+}
+template <>
+constexpr TypeBuilderFunc getModel<Fortran::runtime::io::Iostat>() {
+ return [](mlir::MLIRContext *context) -> mlir::Type {
+ return mlir::IntegerType::get(context,
+ 8 * sizeof(Fortran::runtime::io::Iostat));
+ };
+}
+template <>
+constexpr TypeBuilderFunc
+getModel<const Fortran::runtime::io::NamelistGroup &>() {
+ return [](mlir::MLIRContext *context) -> mlir::Type {
+ return fir::ReferenceType::get(mlir::TupleType::get(context));
+ };
+}
+template <>
+constexpr TypeBuilderFunc
+getModel<const Fortran::runtime::io::NonTbpDefinedIoTable *>() {
+ return [](mlir::MLIRContext *context) -> mlir::Type {
+ return fir::ReferenceType::get(mlir::TupleType::get(context));
+ };
+}
+
REDUCTION_REF_OPERATION_MODEL(std::int8_t)
REDUCTION_VALUE_OPERATION_MODEL(std::int8_t)
REDUCTION_REF_OPERATION_MODEL(std::int16_t)
@@ -778,16 +806,22 @@ struct RuntimeTableEntry<RuntimeTableKey<KT>, RuntimeIdentifier<Cs...>> {
/// argument is intended to be of the form: <mkRTKey(runtime function name)>.
template <typename RuntimeEntry>
static mlir::func::FuncOp getRuntimeFunc(mlir::Location loc,
- fir::FirOpBuilder &builder) {
+ fir::FirOpBuilder &builder,
+ bool isIO = false) {
using namespace Fortran::runtime;
auto name = RuntimeEntry::name;
auto func = builder.getNamedFunction(name);
if (func)
return func;
auto funTy = RuntimeEntry::getTypeModel()(builder.getContext());
- func = builder.createFunction(loc, name, funTy);
- func->setAttr(FIROpsDialect::getFirRuntimeAttrName(), builder.getUnitAttr());
- return func;
+ return builder.createRuntimeFunction(loc, name, funTy, isIO);
+}
+
+/// Get (or generate) the MLIR FuncOp for a given IO runtime function.
+template <typename E>
+static mlir::func::FuncOp getIORuntimeFunc(mlir::Location loc,
+ fir::FirOpBuilder &builder) {
+ return getRuntimeFunc<E>(loc, builder, /*isIO=*/true);
}
namespace helper {
diff --git a/flang/include/flang/Optimizer/Dialect/FIRDialect.td b/flang/include/flang/Optimizer/Dialect/FIRDialect.td
index 0dfb3eda585ce..b05f4e731bc73 100644
--- a/flang/include/flang/Optimizer/Dialect/FIRDialect.td
+++ b/flang/include/flang/Optimizer/Dialect/FIRDialect.td
@@ -56,6 +56,18 @@ def FIROpsDialect : Dialect {
static constexpr llvm::StringRef getFirRuntimeAttrName() {
return "fir.runtime";
}
+ // Return string name of fir.memory attributes.
+ // It is attached to fir.call operations to convey
+ // llvm.memory attributes to LLVM IR.
+ // Its value is intended to be mlir::LLVM::MemoryEffectsAttr.
+ // TODO: we should probably make it an inherent attribute
+ // of fir.call, though, it is supposed to be a short-lived
+ // attribute that appears right before CodeGen and only
+ // meaningful for LLVM, so it is unclear if embedding
+ // it into fir.call makes sense.
+ static constexpr llvm::StringRef getFirCallMemoryAttrName() {
+ return "fir.llvm_memory";
+ }
}];
}
diff --git a/flang/include/flang/Optimizer/Transforms/Passes.h b/flang/include/flang/Optimizer/Transforms/Passes.h
index 10e1c999d4533..afbbeb55632f1 100644
--- a/flang/include/flang/Optimizer/Transforms/Passes.h
+++ b/flang/include/flang/Optimizer/Transforms/Passes.h
@@ -60,6 +60,8 @@ namespace fir {
#define GEN_PASS_DECL_FUNCTIONATTR
#define GEN_PASS_DECL_CONSTANTARGUMENTGLOBALISATIONOPT
#define GEN_PASS_DECL_COMPILERGENERATEDNAMESCONVERSION
+#define GEN_PASS_DECL_SETRUNTIMECALLATTRIBUTES
+#define GEN_PASS_DECL_GENRUNTIMECALLSFORTEST
#include "flang/Optimizer/Transforms/Passes.h.inc"
diff --git a/flang/include/flang/Optimizer/Transforms/Passes.td b/flang/include/flang/Optimizer/Transforms/Passes.td
index 0b6e0119c16c3..64341b42bd1e4 100644
--- a/flang/include/flang/Optimizer/Transforms/Passes.td
+++ b/flang/include/flang/Optimizer/Transforms/Passes.td
@@ -453,4 +453,37 @@ def CUFGPUToLLVMConversion : Pass<"cuf-gpu-convert-to-llvm", "mlir::ModuleOp"> {
];
}
+def SetRuntimeCallAttributes
+ : Pass<"set-runtime-call-attrs", "mlir::func::FuncOp"> {
+ let summary = "Set Fortran runtime fir.call attributes targeting LLVM IR";
+ let description = [{
+ This pass sets different attributes for Fortran runtime calls
+ that enable more optimizations in LLVM backend.
+ For the time being, the meaning of these attributes is not
+ strictly defined for HLFIR/FIR.
+ }];
+ let dependentDialects = ["fir::FIROpsDialect", "mlir::LLVM::LLVMDialect"];
+}
+
+def GenRuntimeCallsForTest
+ : Pass<"gen-runtime-calls-for-test", "mlir::ModuleOp"> {
+ let summary =
+ "Print FIR containing declarations/calls of Fortran runtime functions";
+ let description = [{
+ This pass is only for developers to be able to print FIR
+ that declares and calls Fortran runtime functions.
+ It helps producing/updating tests for passes that modify
+ the func/call operations based on some knowledge of
+ Fortran runtime.
+ }];
+ let options =
+ [Option<"doGenerateCalls", "do-generate-calls", "bool",
+ /*default=*/"false",
+ "Generate thin wrapper functions that call Fortran runtime "
+ "functions. If it is set to false, then only the declarations "
+ "are generated.">,
+ ];
+ let dependentDialects = ["fir::FIROpsDialect", "mlir::func::FuncDialect"];
+}
+
#endif // FLANG_OPTIMIZER_TRANSFORMS_PASSES
diff --git a/flang/include/flang/Optimizer/Transforms/RuntimeFunctions.inc b/flang/include/flang/Optimizer/Transforms/RuntimeFunctions.inc
new file mode 100644
index 0000000000000..cb4bf4ecf559d
--- /dev/null
+++ b/flang/include/flang/Optimizer/Transforms/RuntimeFunctions.inc
@@ -0,0 +1,111 @@
+//===-- Optimizer/Transforms/RuntimeFunctions.inc ---------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef KNOWN_IO_FUNC
+#error "Define KNOWN_IO_FUNC before including this file"
+#endif
+#ifndef KNOWN_RUNTIME_FUNC
+#error "Define KNOWN_RUNTIME_FUNC before including this file"
+#endif
+
+// Fortran runtime functions that SetRuntimeCallAttributesPass recognizes.
+// WARNING: if you add a function entry here, you must make sure
+// that the attribute computation callbacks that end up being
+// used are correct for this function. If needed, add
+// specializations for the types that provide attribute
+// computation callbacks in SetRuntimeCallAttributesPass.
+
+// clang-format off
+KNOWN_IO_FUNC(BeginBackspace),
+KNOWN_IO_FUNC(BeginClose),
+KNOWN_IO_FUNC(BeginEndfile),
+KNOWN_IO_FUNC(BeginExternalFormattedInput),
+KNOWN_IO_FUNC(BeginExternalFormattedOutput),
+KNOWN_IO_FUNC(BeginExternalListInput),
+KNOWN_IO_FUNC(BeginExternalListOutput),
+KNOWN_IO_FUNC(BeginFlush),
+KNOWN_IO_FUNC(BeginInquireFile),
+KNOWN_IO_FUNC(BeginInquireIoLength),
+KNOWN_IO_FUNC(BeginInquireUnit),
+KNOWN_IO_FUNC(BeginInternalArrayFormattedInput),
+KNOWN_IO_FUNC(BeginInternalArrayFormattedOutput),
+KNOWN_IO_FUNC(BeginInternalArrayListInput),
+KNOWN_IO_FUNC(BeginInternalArrayListOutput),
+KNOWN_IO_FUNC(BeginInternalFormattedInput),
+KNOWN_IO_FUNC(BeginInternalFormattedOutput),
+KNOWN_IO_FUNC(BeginInternalListInput),
+KNOWN_IO_FUNC(BeginInternalListOutput),
+KNOWN_IO_FUNC(BeginOpenNewUnit),
+KNOWN_IO_FUNC(BeginOpenUnit),
+KNOWN_IO_FUNC(BeginRewind),
+KNOWN_IO_FUNC(BeginUnformattedInput),
+KNOWN_IO_FUNC(BeginUnformattedOutput),
+KNOWN_IO_FUNC(BeginWait),
+KNOWN_IO_FUNC(BeginWaitAll),
+KNOWN_IO_FUNC(CheckUnitNumberInRange128),
+KNOWN_IO_FUNC(CheckUnitNumberInRange64),
+KNOWN_IO_FUNC(EnableHandlers),
+KNOWN_IO_FUNC(EndIoStatement),
+KNOWN_IO_FUNC(GetAsynchronousId),
+KNOWN_IO_FUNC(GetIoLength),
+KNOWN_IO_FUNC(GetIoMsg),
+KNOWN_IO_FUNC(GetNewUnit),
+KNOWN_IO_FUNC(GetSize),
+KNOWN_IO_FUNC(InputAscii),
+KNOWN_IO_FUNC(InputComplex32),
+KNOWN_IO_FUNC(InputComplex64),
+KNOWN_IO_FUNC(InputDerivedType),
+KNOWN_IO_FUNC(InputDescriptor),
+KNOWN_IO_FUNC(InputInteger),
+KNOWN_IO_FUNC(InputLogical),
+KNOWN_IO_FUNC(InputNamelist),
+KNOWN_IO_FUNC(InputReal32),
+KNOWN_IO_FUNC(InputReal64),
+KNOWN_IO_FUNC(InquireCharacter),
+KNOWN_IO_FUNC(InquireInteger64),
+KNOWN_IO_FUNC(InquireLogical),
+KNOWN_IO_FUNC(InquirePendingId),
+KNOWN_IO_FUNC(OutputAscii),
+KNOWN_IO_FUNC(OutputComplex32),
+KNOWN_IO_FUNC(OutputComplex64),
+KNOWN_IO_FUNC(OutputDerivedType),
+KNOWN_IO_FUNC(OutputDescriptor),
+KNOWN_IO_FUNC(OutputInteger128),
+KNOWN_IO_FUNC(OutputInteger16),
+KNOWN_IO_FUNC(OutputInteger32),
+KNOWN_IO_FUNC(OutputInteger64),
+KNOWN_IO_FUNC(OutputInteger8),
+KNOWN_IO_FUNC(OutputLogical),
+KNOWN_IO_FUNC(OutputNamelist),
+KNOWN_IO_FUNC(OutputReal32),
+KNOWN_IO_FUNC(OutputReal64),
+KNOWN_IO_FUNC(SetAccess),
+KNOWN_IO_FUNC(SetAction),
+KNOWN_IO_FUNC(SetAdvance),
+KNOWN_IO_FUNC(SetAsynchronous),
+KNOWN_IO_FUNC(SetBlank),
+KNOWN_IO_FUNC(SetCarriagecontrol),
+KNOWN_IO_FUNC(SetConvert),
+KNOWN_IO_FUNC(SetDecimal),
+KNOWN_IO_FUNC(SetDelim),
+KNOWN_IO_FUNC(SetEncoding),
+KNOWN_IO_FUNC(SetFile),
+KNOWN_IO_FUNC(SetForm),
+KNOWN_IO_FUNC(SetPad),
+KNOWN_IO_FUNC(SetPos),
+KNOWN_IO_FUNC(SetPosition),
+KNOWN_IO_FUNC(SetRec),
+KNOWN_IO_FUNC(SetRecl),
+KNOWN_IO_FUNC(SetRound),
+KNOWN_IO_FUNC(SetSign),
+KNOWN_IO_FUNC(SetStatus)
+
+// clang-format on
+
+#undef KNOWN_IO_FUNC
+#undef KNOWN_RUNTIME_FUNC
diff --git a/flang/lib/Lower/IO.cpp b/flang/lib/Lower/IO.cpp
index 75453721d91a2..35068f0519cbd 100644
--- a/flang/lib/Lower/IO.cpp
+++ b/flang/lib/Lower/IO.cpp
@@ -43,35 +43,6 @@
#define DEBUG_TYPE "flang-lower-io"
-// Define additional runtime type models specific to IO.
-namespace fir::runtime {
-template <>
-constexpr TypeBuilderFunc getModel<Fortran::runtime::io::IoStatementState *>() {
- return getModel<char *>();
-}
-template <>
-constexpr TypeBuilderFunc getModel<Fortran::runtime::io::Iostat>() {
- return [](mlir::MLIRContext *context) -> mlir::Type {
- return mlir::IntegerType::get(context,
- 8 * sizeof(Fortran::runtime::io::Iostat));
- };
-}
-template <>
-constexpr TypeBuilderFunc
-getModel<const Fortran::runtime::io::NamelistGroup &>() {
- return [](mlir::MLIRContext *context) -> mlir::Type {
- return fir::ReferenceType::get(mlir::TupleType::get(context));
- };
-}
-template <>
-constexpr TypeBuilderFunc
-getModel<const Fortran::runtime::io::NonTbpDefinedIoTable *>() {
- return [](mlir::MLIRContext *context) -> mlir::Type {
- return fir::ReferenceType::get(mlir::TupleType::get(context));
- };
-}
-} // namespace fir::runtime
-
using namespace Fortran::runtime::io;
#define mkIOKey(X) FirmkKey(IONAME(X))
@@ -172,22 +143,6 @@ inline int64_t getLength(mlir::Type argTy) {
return mlir::cast<fir::SequenceType>(argTy).getShape()[0];
}
-/// Get (or generate) the MLIR FuncOp for a given IO runtime function.
-template <typename E>
-static mlir::func::FuncOp getIORuntimeFunc(mlir::Location loc,
- fir::FirOpBuilder &builder) {
- llvm::StringRef name = getName<E>();
- mlir::func::FuncOp func = builder.getNamedFunction(name);
- if (func)
- return func;
- auto funTy = getTypeModel<E>()(builder.getContext());
- func = builder.createFunction(loc, name, funTy);
- func->setAttr(fir::FIROpsDialect::getFirRuntimeAttrName(),
- builder.getUnitAttr());
- func->setAttr("fir.io", builder.getUnitAttr());
- return func;
-}
-
/// Generate calls to end an IO statement. Return the IOSTAT value, if any.
/// It is the caller's responsibility to generate branches on that value.
static mlir::Value genEndIO(Fortran::lower::AbstractConverter &converter,
@@ -197,7 +152,7 @@ static mlir::Value genEndIO(Fortran::lower::AbstractConverter &converter,
fir::FirOpBuilder &builder = converter.getFirOpBuilder();
if (csi.ioMsg) {
mlir::func::FuncOp getIoMsg =
- getIORuntimeFunc<mkIOKey(GetIoMsg)>(loc, builder);
+ fir::runtime::getIORuntimeFunc<mkIOKey(GetIoMsg)>(loc, builder);
builder.create<fir::CallOp>(
loc, getIoMsg,
mlir::ValueRange{
@@ -208,7 +163,7 @@ static mlir::Value genEndIO(Fortran::lower::AbstractConverter &converter,
fir::getLen(*csi.ioMsg))});
}
mlir::func::FuncOp endIoStatement =
- getIORuntimeFunc<mkIOKey(EndIoStatement)>(loc, builder);
+ fir::runtime::getIORuntimeFunc<mkIOKey(EndIoStatement)>(loc, builder);
auto call = builder.create<fir::CallOp>(loc, endIoStatement,
mlir::ValueRange{cookie});
mlir::Value iostat = call.getResult(0);
@@ -659,45 +614,57 @@ static mlir::func::FuncOp getOutputFunc(mlir::Location loc,
fir::FirOpBuilder &builder,
mlir::Type type, bool isFormatted) {
if (mlir::isa<fir::RecordType>(fir::unwrapPassByRefType(type)))
- return getIORuntimeFunc<mkIOKey(OutputDerivedType)>(loc, builder);
+ return fir::runtime::getIORuntimeFunc<mkIOKey(OutputDerivedType)>(loc,
+ builder);
if (!isFormatted)
- return getIORuntimeFunc<mkIOKey(OutputDescriptor)>(loc, builder);
+ return fir::runtime::getIORuntimeFunc<mkIOKey(OutputDescriptor)>(loc,
+ builder);
if (auto ty = mlir::dyn_cast<mlir::IntegerType>(type)) {
if (!ty.isUnsigned()) {
switch (ty.getWidth()) {
case 1:
- return getIORuntimeFunc<mkIOKey(OutputLogical)>(loc, builder);
+ return fir::runtime::getIORuntimeFunc<mkIOKey(OutputLogical)>(loc,
+ builder);
case 8:
- return getIORuntimeFunc<mkIOKey(OutputInteger8)>(loc, builder);
+ return fir::runtime::getIORuntimeFunc<mkIOKey(OutputInteger8)>(loc,
+ builder);
case 16:
- return getIORuntimeFunc<mkIOKey(OutputInteger16)>(loc, builder);
+ return fir::runtime::getIORuntimeFunc<mkIOKey(OutputInteger16)>(
+ loc, builder);
case 32:
- return getIORuntimeFunc<mkIOKey(OutputInteger32)>(loc, builder);
+ return fir::runtime::getIORuntimeFunc<mkIOKey(OutputInteger32)>(
+ loc, builder);
case 64:
- return getIORuntimeFunc<mkIOKey(OutputInteger64)>(loc, builder);
+ return fir::runtime::getIORuntimeFunc<mkIOKey(OutputInteger64)>(
+ loc, builder);
case 128:
- return getIORuntimeFunc<mkIOKey(OutputInteger128)>(loc, builder);
+ return fir::runtime::getIORuntimeFunc<mkIOKey(OutputInteger128)>(
+ loc, builder);
}
llvm_unreachable("unknown OutputInteger kind");
}
}
if (auto ty = mlir::dyn_cast<mlir::FloatType>(type)) {
if (auto width = ty.getWidth(); width == 32)
- return getIORuntimeFunc<mkIOKey(OutputReal32)>(loc, builder);
+ return fir::runtime::getIORuntimeFunc<mkIOKey(OutputReal32)>(loc,
+ builder);
else if (width == 64)
- return getIORuntimeFunc<mkIOKey(OutputReal64)>(loc, builder);
+ return fir::runtime::getIORuntimeFunc<mkIOKey(OutputReal64)>(loc,
+ builder);
}
auto kindMap = fir::getKindMapping(builder.getModule());
if (auto ty = mlir::dyn_cast<mlir::ComplexType>(type)) {
// COMPLEX(KIND=k) corresponds to a pair of REAL(KIND=k).
auto width = mlir::cast<mlir::FloatType>(ty.getElementType()).getWidth();
if (width == 32)
- return getIORuntimeFunc<mkIOKey(OutputComplex32)>(loc, builder);
+ return fir::runtime::getIORuntimeFunc<mkIOKey(OutputComplex32)>(loc,
+ builder);
else if (width == 64)
- return getIORuntimeFunc<mkIOKey(OutputComplex64)>(loc, builder);
+ return fir::runtime::getIORuntimeFunc<mkIOKey(OutputComplex64)>(loc,
+ builder);
}
if (mlir::isa<fir::LogicalType>(type))
- return getIORuntimeFunc<mkIOKey(OutputLogical)>(loc, builder);
+ return fir::runtime::getIORuntimeFunc<mkIOKey(OutputLogical)>(loc, builder);
if (fir::factory::CharacterExprHelper::isCharacterScalar(type)) {
// TODO: What would it mean if the default CHARACTER KIND is set to a wide
// character encoding scheme? How do we handle UTF-8? Is it a distinct KIND
@@ -706,9 +673,10 @@ static mlir::func::FuncOp getOutputFunc(mlir::Location loc,
auto asciiKind = kindMap.defaultCharacterKind();
if (kindMap.getCharacterBitsize(asciiKind) == 8 &&
fir::factory::CharacterExprHelper::getCharacterKind(type) == asciiKind)
- return getIORuntimeFunc<mkIOKey(OutputAscii)>(loc, builder);
+ return fir::runtime::getIORuntimeFunc<mkIOKey(OutputAscii)>(loc, builder);
}
- return getIORuntimeFunc<mkIOKey(OutputDescriptor)>(loc, builder);
+ return fir::runtime::getIORuntimeFunc<mkIOKey(OutputDescriptor)>(loc,
+ builder);
}
/// Generate a sequence of output data transfer calls.
@@ -778,39 +746,46 @@ static mlir::func::FuncOp getInputFunc(mlir::Location loc,
fir::FirOpBuilder &builder,
mlir::Type type, bool isFormatted) {
if (mlir::isa<fir::RecordType>(fir::unwrapPassByRefType(type)))
- return getIORuntimeFunc<mkIOKey(InputDerivedType)>(loc, builder);
+ return fir::runtime::getIORuntimeFunc<mkIOKey(InputDerivedType)>(loc,
+ builder);
if (!isFormatted)
- return getIORuntimeFunc<mkIOKey(InputDescriptor)>(loc, builder);
+ return fir::runtime::getIORuntimeFunc<mkIOKey(InputDescriptor)>(loc,
+ builder);
if (auto ty = mlir::dyn_cast<mlir::IntegerType>(type)) {
if (type.isUnsignedInteger())
- ...
[truncated]
|
@llvm/pr-subscribers-flang-codegen Author: Slava Zakharin (vzakhari) ChangesThis change is inspired by a case in facerec benchmark, where performance This patch sets and propagates llvm.memory_effects attribute for fir.call As long as different calls to the same Fortran runtime function may have In addition to llvm.memory_effects, the new pass sets llvm.nosync I also added another pass for developers to be able to print Patch is 183.39 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/128093.diff 19 Files Affected:
diff --git a/flang/include/flang/Optimizer/Builder/FIRBuilder.h b/flang/include/flang/Optimizer/Builder/FIRBuilder.h
index 93eca78424775..1675c15363868 100644
--- a/flang/include/flang/Optimizer/Builder/FIRBuilder.h
+++ b/flang/include/flang/Optimizer/Builder/FIRBuilder.h
@@ -385,6 +385,15 @@ class FirOpBuilder : public mlir::OpBuilder, public mlir::OpBuilder::Listener {
mlir::FunctionType ty,
mlir::SymbolTable *);
+ /// Returns a named function for a Fortran runtime API, creating
+ /// it, if it does not exist in the module yet.
+ /// If \p isIO is set to true, then the function corresponds
+ /// to one of Fortran runtime IO APIs.
+ mlir::func::FuncOp createRuntimeFunction(mlir::Location loc,
+ llvm::StringRef name,
+ mlir::FunctionType ty,
+ bool isIO = false);
+
/// Cast the input value to IndexType.
mlir::Value convertToIndexType(mlir::Location loc, mlir::Value val) {
return createConvert(loc, getIndexType(), val);
diff --git a/flang/include/flang/Optimizer/Builder/Runtime/RTBuilder.h b/flang/include/flang/Optimizer/Builder/Runtime/RTBuilder.h
index 1ffc354d6b80f..5158abaa31ed1 100644
--- a/flang/include/flang/Optimizer/Builder/Runtime/RTBuilder.h
+++ b/flang/include/flang/Optimizer/Builder/Runtime/RTBuilder.h
@@ -21,6 +21,7 @@
#include "flang/Optimizer/Builder/FIRBuilder.h"
#include "flang/Optimizer/Dialect/FIRDialect.h"
#include "flang/Optimizer/Dialect/FIRType.h"
+#include "flang/Runtime/io-api-consts.h"
#include "flang/Runtime/reduce.h"
#include "flang/Support/Fortran.h"
#include "mlir/IR/BuiltinTypes.h"
@@ -586,6 +587,33 @@ constexpr TypeBuilderFunc getModel<void>() {
};
}
+// Define additional runtime type models specific to IO.
+template <>
+constexpr TypeBuilderFunc getModel<Fortran::runtime::io::IoStatementState *>() {
+ return getModel<char *>();
+}
+template <>
+constexpr TypeBuilderFunc getModel<Fortran::runtime::io::Iostat>() {
+ return [](mlir::MLIRContext *context) -> mlir::Type {
+ return mlir::IntegerType::get(context,
+ 8 * sizeof(Fortran::runtime::io::Iostat));
+ };
+}
+template <>
+constexpr TypeBuilderFunc
+getModel<const Fortran::runtime::io::NamelistGroup &>() {
+ return [](mlir::MLIRContext *context) -> mlir::Type {
+ return fir::ReferenceType::get(mlir::TupleType::get(context));
+ };
+}
+template <>
+constexpr TypeBuilderFunc
+getModel<const Fortran::runtime::io::NonTbpDefinedIoTable *>() {
+ return [](mlir::MLIRContext *context) -> mlir::Type {
+ return fir::ReferenceType::get(mlir::TupleType::get(context));
+ };
+}
+
REDUCTION_REF_OPERATION_MODEL(std::int8_t)
REDUCTION_VALUE_OPERATION_MODEL(std::int8_t)
REDUCTION_REF_OPERATION_MODEL(std::int16_t)
@@ -778,16 +806,22 @@ struct RuntimeTableEntry<RuntimeTableKey<KT>, RuntimeIdentifier<Cs...>> {
/// argument is intended to be of the form: <mkRTKey(runtime function name)>.
template <typename RuntimeEntry>
static mlir::func::FuncOp getRuntimeFunc(mlir::Location loc,
- fir::FirOpBuilder &builder) {
+ fir::FirOpBuilder &builder,
+ bool isIO = false) {
using namespace Fortran::runtime;
auto name = RuntimeEntry::name;
auto func = builder.getNamedFunction(name);
if (func)
return func;
auto funTy = RuntimeEntry::getTypeModel()(builder.getContext());
- func = builder.createFunction(loc, name, funTy);
- func->setAttr(FIROpsDialect::getFirRuntimeAttrName(), builder.getUnitAttr());
- return func;
+ return builder.createRuntimeFunction(loc, name, funTy, isIO);
+}
+
+/// Get (or generate) the MLIR FuncOp for a given IO runtime function.
+template <typename E>
+static mlir::func::FuncOp getIORuntimeFunc(mlir::Location loc,
+ fir::FirOpBuilder &builder) {
+ return getRuntimeFunc<E>(loc, builder, /*isIO=*/true);
}
namespace helper {
diff --git a/flang/include/flang/Optimizer/Dialect/FIRDialect.td b/flang/include/flang/Optimizer/Dialect/FIRDialect.td
index 0dfb3eda585ce..b05f4e731bc73 100644
--- a/flang/include/flang/Optimizer/Dialect/FIRDialect.td
+++ b/flang/include/flang/Optimizer/Dialect/FIRDialect.td
@@ -56,6 +56,18 @@ def FIROpsDialect : Dialect {
static constexpr llvm::StringRef getFirRuntimeAttrName() {
return "fir.runtime";
}
+ // Return string name of fir.memory attributes.
+ // It is attached to fir.call operations to convey
+ // llvm.memory attributes to LLVM IR.
+ // Its value is intended to be mlir::LLVM::MemoryEffectsAttr.
+ // TODO: we should probably make it an inherent attribute
+ // of fir.call, though, it is supposed to be a short-lived
+ // attribute that appears right before CodeGen and only
+ // meaningful for LLVM, so it is unclear if embedding
+ // it into fir.call makes sense.
+ static constexpr llvm::StringRef getFirCallMemoryAttrName() {
+ return "fir.llvm_memory";
+ }
}];
}
diff --git a/flang/include/flang/Optimizer/Transforms/Passes.h b/flang/include/flang/Optimizer/Transforms/Passes.h
index 10e1c999d4533..afbbeb55632f1 100644
--- a/flang/include/flang/Optimizer/Transforms/Passes.h
+++ b/flang/include/flang/Optimizer/Transforms/Passes.h
@@ -60,6 +60,8 @@ namespace fir {
#define GEN_PASS_DECL_FUNCTIONATTR
#define GEN_PASS_DECL_CONSTANTARGUMENTGLOBALISATIONOPT
#define GEN_PASS_DECL_COMPILERGENERATEDNAMESCONVERSION
+#define GEN_PASS_DECL_SETRUNTIMECALLATTRIBUTES
+#define GEN_PASS_DECL_GENRUNTIMECALLSFORTEST
#include "flang/Optimizer/Transforms/Passes.h.inc"
diff --git a/flang/include/flang/Optimizer/Transforms/Passes.td b/flang/include/flang/Optimizer/Transforms/Passes.td
index 0b6e0119c16c3..64341b42bd1e4 100644
--- a/flang/include/flang/Optimizer/Transforms/Passes.td
+++ b/flang/include/flang/Optimizer/Transforms/Passes.td
@@ -453,4 +453,37 @@ def CUFGPUToLLVMConversion : Pass<"cuf-gpu-convert-to-llvm", "mlir::ModuleOp"> {
];
}
+def SetRuntimeCallAttributes
+ : Pass<"set-runtime-call-attrs", "mlir::func::FuncOp"> {
+ let summary = "Set Fortran runtime fir.call attributes targeting LLVM IR";
+ let description = [{
+ This pass sets different attributes for Fortran runtime calls
+ that enable more optimizations in LLVM backend.
+ For the time being, the meaning of these attributes is not
+ strictly defined for HLFIR/FIR.
+ }];
+ let dependentDialects = ["fir::FIROpsDialect", "mlir::LLVM::LLVMDialect"];
+}
+
+def GenRuntimeCallsForTest
+ : Pass<"gen-runtime-calls-for-test", "mlir::ModuleOp"> {
+ let summary =
+ "Print FIR containing declarations/calls of Fortran runtime functions";
+ let description = [{
+ This pass is only for developers to be able to print FIR
+ that declares and calls Fortran runtime functions.
+ It helps producing/updating tests for passes that modify
+ the func/call operations based on some knowledge of
+ Fortran runtime.
+ }];
+ let options =
+ [Option<"doGenerateCalls", "do-generate-calls", "bool",
+ /*default=*/"false",
+ "Generate thin wrapper functions that call Fortran runtime "
+ "functions. If it is set to false, then only the declarations "
+ "are generated.">,
+ ];
+ let dependentDialects = ["fir::FIROpsDialect", "mlir::func::FuncDialect"];
+}
+
#endif // FLANG_OPTIMIZER_TRANSFORMS_PASSES
diff --git a/flang/include/flang/Optimizer/Transforms/RuntimeFunctions.inc b/flang/include/flang/Optimizer/Transforms/RuntimeFunctions.inc
new file mode 100644
index 0000000000000..cb4bf4ecf559d
--- /dev/null
+++ b/flang/include/flang/Optimizer/Transforms/RuntimeFunctions.inc
@@ -0,0 +1,111 @@
+//===-- Optimizer/Transforms/RuntimeFunctions.inc ---------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef KNOWN_IO_FUNC
+#error "Define KNOWN_IO_FUNC before including this file"
+#endif
+#ifndef KNOWN_RUNTIME_FUNC
+#error "Define KNOWN_RUNTIME_FUNC before including this file"
+#endif
+
+// Fortran runtime functions that SetRuntimeCallAttributesPass recognizes.
+// WARNING: if you add a function entry here, you must make sure
+// that the attribute computation callbacks that end up being
+// used are correct for this function. If needed, add
+// specializations for the types that provide attribute
+// computation callbacks in SetRuntimeCallAttributesPass.
+
+// clang-format off
+KNOWN_IO_FUNC(BeginBackspace),
+KNOWN_IO_FUNC(BeginClose),
+KNOWN_IO_FUNC(BeginEndfile),
+KNOWN_IO_FUNC(BeginExternalFormattedInput),
+KNOWN_IO_FUNC(BeginExternalFormattedOutput),
+KNOWN_IO_FUNC(BeginExternalListInput),
+KNOWN_IO_FUNC(BeginExternalListOutput),
+KNOWN_IO_FUNC(BeginFlush),
+KNOWN_IO_FUNC(BeginInquireFile),
+KNOWN_IO_FUNC(BeginInquireIoLength),
+KNOWN_IO_FUNC(BeginInquireUnit),
+KNOWN_IO_FUNC(BeginInternalArrayFormattedInput),
+KNOWN_IO_FUNC(BeginInternalArrayFormattedOutput),
+KNOWN_IO_FUNC(BeginInternalArrayListInput),
+KNOWN_IO_FUNC(BeginInternalArrayListOutput),
+KNOWN_IO_FUNC(BeginInternalFormattedInput),
+KNOWN_IO_FUNC(BeginInternalFormattedOutput),
+KNOWN_IO_FUNC(BeginInternalListInput),
+KNOWN_IO_FUNC(BeginInternalListOutput),
+KNOWN_IO_FUNC(BeginOpenNewUnit),
+KNOWN_IO_FUNC(BeginOpenUnit),
+KNOWN_IO_FUNC(BeginRewind),
+KNOWN_IO_FUNC(BeginUnformattedInput),
+KNOWN_IO_FUNC(BeginUnformattedOutput),
+KNOWN_IO_FUNC(BeginWait),
+KNOWN_IO_FUNC(BeginWaitAll),
+KNOWN_IO_FUNC(CheckUnitNumberInRange128),
+KNOWN_IO_FUNC(CheckUnitNumberInRange64),
+KNOWN_IO_FUNC(EnableHandlers),
+KNOWN_IO_FUNC(EndIoStatement),
+KNOWN_IO_FUNC(GetAsynchronousId),
+KNOWN_IO_FUNC(GetIoLength),
+KNOWN_IO_FUNC(GetIoMsg),
+KNOWN_IO_FUNC(GetNewUnit),
+KNOWN_IO_FUNC(GetSize),
+KNOWN_IO_FUNC(InputAscii),
+KNOWN_IO_FUNC(InputComplex32),
+KNOWN_IO_FUNC(InputComplex64),
+KNOWN_IO_FUNC(InputDerivedType),
+KNOWN_IO_FUNC(InputDescriptor),
+KNOWN_IO_FUNC(InputInteger),
+KNOWN_IO_FUNC(InputLogical),
+KNOWN_IO_FUNC(InputNamelist),
+KNOWN_IO_FUNC(InputReal32),
+KNOWN_IO_FUNC(InputReal64),
+KNOWN_IO_FUNC(InquireCharacter),
+KNOWN_IO_FUNC(InquireInteger64),
+KNOWN_IO_FUNC(InquireLogical),
+KNOWN_IO_FUNC(InquirePendingId),
+KNOWN_IO_FUNC(OutputAscii),
+KNOWN_IO_FUNC(OutputComplex32),
+KNOWN_IO_FUNC(OutputComplex64),
+KNOWN_IO_FUNC(OutputDerivedType),
+KNOWN_IO_FUNC(OutputDescriptor),
+KNOWN_IO_FUNC(OutputInteger128),
+KNOWN_IO_FUNC(OutputInteger16),
+KNOWN_IO_FUNC(OutputInteger32),
+KNOWN_IO_FUNC(OutputInteger64),
+KNOWN_IO_FUNC(OutputInteger8),
+KNOWN_IO_FUNC(OutputLogical),
+KNOWN_IO_FUNC(OutputNamelist),
+KNOWN_IO_FUNC(OutputReal32),
+KNOWN_IO_FUNC(OutputReal64),
+KNOWN_IO_FUNC(SetAccess),
+KNOWN_IO_FUNC(SetAction),
+KNOWN_IO_FUNC(SetAdvance),
+KNOWN_IO_FUNC(SetAsynchronous),
+KNOWN_IO_FUNC(SetBlank),
+KNOWN_IO_FUNC(SetCarriagecontrol),
+KNOWN_IO_FUNC(SetConvert),
+KNOWN_IO_FUNC(SetDecimal),
+KNOWN_IO_FUNC(SetDelim),
+KNOWN_IO_FUNC(SetEncoding),
+KNOWN_IO_FUNC(SetFile),
+KNOWN_IO_FUNC(SetForm),
+KNOWN_IO_FUNC(SetPad),
+KNOWN_IO_FUNC(SetPos),
+KNOWN_IO_FUNC(SetPosition),
+KNOWN_IO_FUNC(SetRec),
+KNOWN_IO_FUNC(SetRecl),
+KNOWN_IO_FUNC(SetRound),
+KNOWN_IO_FUNC(SetSign),
+KNOWN_IO_FUNC(SetStatus)
+
+// clang-format on
+
+#undef KNOWN_IO_FUNC
+#undef KNOWN_RUNTIME_FUNC
diff --git a/flang/lib/Lower/IO.cpp b/flang/lib/Lower/IO.cpp
index 75453721d91a2..35068f0519cbd 100644
--- a/flang/lib/Lower/IO.cpp
+++ b/flang/lib/Lower/IO.cpp
@@ -43,35 +43,6 @@
#define DEBUG_TYPE "flang-lower-io"
-// Define additional runtime type models specific to IO.
-namespace fir::runtime {
-template <>
-constexpr TypeBuilderFunc getModel<Fortran::runtime::io::IoStatementState *>() {
- return getModel<char *>();
-}
-template <>
-constexpr TypeBuilderFunc getModel<Fortran::runtime::io::Iostat>() {
- return [](mlir::MLIRContext *context) -> mlir::Type {
- return mlir::IntegerType::get(context,
- 8 * sizeof(Fortran::runtime::io::Iostat));
- };
-}
-template <>
-constexpr TypeBuilderFunc
-getModel<const Fortran::runtime::io::NamelistGroup &>() {
- return [](mlir::MLIRContext *context) -> mlir::Type {
- return fir::ReferenceType::get(mlir::TupleType::get(context));
- };
-}
-template <>
-constexpr TypeBuilderFunc
-getModel<const Fortran::runtime::io::NonTbpDefinedIoTable *>() {
- return [](mlir::MLIRContext *context) -> mlir::Type {
- return fir::ReferenceType::get(mlir::TupleType::get(context));
- };
-}
-} // namespace fir::runtime
-
using namespace Fortran::runtime::io;
#define mkIOKey(X) FirmkKey(IONAME(X))
@@ -172,22 +143,6 @@ inline int64_t getLength(mlir::Type argTy) {
return mlir::cast<fir::SequenceType>(argTy).getShape()[0];
}
-/// Get (or generate) the MLIR FuncOp for a given IO runtime function.
-template <typename E>
-static mlir::func::FuncOp getIORuntimeFunc(mlir::Location loc,
- fir::FirOpBuilder &builder) {
- llvm::StringRef name = getName<E>();
- mlir::func::FuncOp func = builder.getNamedFunction(name);
- if (func)
- return func;
- auto funTy = getTypeModel<E>()(builder.getContext());
- func = builder.createFunction(loc, name, funTy);
- func->setAttr(fir::FIROpsDialect::getFirRuntimeAttrName(),
- builder.getUnitAttr());
- func->setAttr("fir.io", builder.getUnitAttr());
- return func;
-}
-
/// Generate calls to end an IO statement. Return the IOSTAT value, if any.
/// It is the caller's responsibility to generate branches on that value.
static mlir::Value genEndIO(Fortran::lower::AbstractConverter &converter,
@@ -197,7 +152,7 @@ static mlir::Value genEndIO(Fortran::lower::AbstractConverter &converter,
fir::FirOpBuilder &builder = converter.getFirOpBuilder();
if (csi.ioMsg) {
mlir::func::FuncOp getIoMsg =
- getIORuntimeFunc<mkIOKey(GetIoMsg)>(loc, builder);
+ fir::runtime::getIORuntimeFunc<mkIOKey(GetIoMsg)>(loc, builder);
builder.create<fir::CallOp>(
loc, getIoMsg,
mlir::ValueRange{
@@ -208,7 +163,7 @@ static mlir::Value genEndIO(Fortran::lower::AbstractConverter &converter,
fir::getLen(*csi.ioMsg))});
}
mlir::func::FuncOp endIoStatement =
- getIORuntimeFunc<mkIOKey(EndIoStatement)>(loc, builder);
+ fir::runtime::getIORuntimeFunc<mkIOKey(EndIoStatement)>(loc, builder);
auto call = builder.create<fir::CallOp>(loc, endIoStatement,
mlir::ValueRange{cookie});
mlir::Value iostat = call.getResult(0);
@@ -659,45 +614,57 @@ static mlir::func::FuncOp getOutputFunc(mlir::Location loc,
fir::FirOpBuilder &builder,
mlir::Type type, bool isFormatted) {
if (mlir::isa<fir::RecordType>(fir::unwrapPassByRefType(type)))
- return getIORuntimeFunc<mkIOKey(OutputDerivedType)>(loc, builder);
+ return fir::runtime::getIORuntimeFunc<mkIOKey(OutputDerivedType)>(loc,
+ builder);
if (!isFormatted)
- return getIORuntimeFunc<mkIOKey(OutputDescriptor)>(loc, builder);
+ return fir::runtime::getIORuntimeFunc<mkIOKey(OutputDescriptor)>(loc,
+ builder);
if (auto ty = mlir::dyn_cast<mlir::IntegerType>(type)) {
if (!ty.isUnsigned()) {
switch (ty.getWidth()) {
case 1:
- return getIORuntimeFunc<mkIOKey(OutputLogical)>(loc, builder);
+ return fir::runtime::getIORuntimeFunc<mkIOKey(OutputLogical)>(loc,
+ builder);
case 8:
- return getIORuntimeFunc<mkIOKey(OutputInteger8)>(loc, builder);
+ return fir::runtime::getIORuntimeFunc<mkIOKey(OutputInteger8)>(loc,
+ builder);
case 16:
- return getIORuntimeFunc<mkIOKey(OutputInteger16)>(loc, builder);
+ return fir::runtime::getIORuntimeFunc<mkIOKey(OutputInteger16)>(
+ loc, builder);
case 32:
- return getIORuntimeFunc<mkIOKey(OutputInteger32)>(loc, builder);
+ return fir::runtime::getIORuntimeFunc<mkIOKey(OutputInteger32)>(
+ loc, builder);
case 64:
- return getIORuntimeFunc<mkIOKey(OutputInteger64)>(loc, builder);
+ return fir::runtime::getIORuntimeFunc<mkIOKey(OutputInteger64)>(
+ loc, builder);
case 128:
- return getIORuntimeFunc<mkIOKey(OutputInteger128)>(loc, builder);
+ return fir::runtime::getIORuntimeFunc<mkIOKey(OutputInteger128)>(
+ loc, builder);
}
llvm_unreachable("unknown OutputInteger kind");
}
}
if (auto ty = mlir::dyn_cast<mlir::FloatType>(type)) {
if (auto width = ty.getWidth(); width == 32)
- return getIORuntimeFunc<mkIOKey(OutputReal32)>(loc, builder);
+ return fir::runtime::getIORuntimeFunc<mkIOKey(OutputReal32)>(loc,
+ builder);
else if (width == 64)
- return getIORuntimeFunc<mkIOKey(OutputReal64)>(loc, builder);
+ return fir::runtime::getIORuntimeFunc<mkIOKey(OutputReal64)>(loc,
+ builder);
}
auto kindMap = fir::getKindMapping(builder.getModule());
if (auto ty = mlir::dyn_cast<mlir::ComplexType>(type)) {
// COMPLEX(KIND=k) corresponds to a pair of REAL(KIND=k).
auto width = mlir::cast<mlir::FloatType>(ty.getElementType()).getWidth();
if (width == 32)
- return getIORuntimeFunc<mkIOKey(OutputComplex32)>(loc, builder);
+ return fir::runtime::getIORuntimeFunc<mkIOKey(OutputComplex32)>(loc,
+ builder);
else if (width == 64)
- return getIORuntimeFunc<mkIOKey(OutputComplex64)>(loc, builder);
+ return fir::runtime::getIORuntimeFunc<mkIOKey(OutputComplex64)>(loc,
+ builder);
}
if (mlir::isa<fir::LogicalType>(type))
- return getIORuntimeFunc<mkIOKey(OutputLogical)>(loc, builder);
+ return fir::runtime::getIORuntimeFunc<mkIOKey(OutputLogical)>(loc, builder);
if (fir::factory::CharacterExprHelper::isCharacterScalar(type)) {
// TODO: What would it mean if the default CHARACTER KIND is set to a wide
// character encoding scheme? How do we handle UTF-8? Is it a distinct KIND
@@ -706,9 +673,10 @@ static mlir::func::FuncOp getOutputFunc(mlir::Location loc,
auto asciiKind = kindMap.defaultCharacterKind();
if (kindMap.getCharacterBitsize(asciiKind) == 8 &&
fir::factory::CharacterExprHelper::getCharacterKind(type) == asciiKind)
- return getIORuntimeFunc<mkIOKey(OutputAscii)>(loc, builder);
+ return fir::runtime::getIORuntimeFunc<mkIOKey(OutputAscii)>(loc, builder);
}
- return getIORuntimeFunc<mkIOKey(OutputDescriptor)>(loc, builder);
+ return fir::runtime::getIORuntimeFunc<mkIOKey(OutputDescriptor)>(loc,
+ builder);
}
/// Generate a sequence of output data transfer calls.
@@ -778,39 +746,46 @@ static mlir::func::FuncOp getInputFunc(mlir::Location loc,
fir::FirOpBuilder &builder,
mlir::Type type, bool isFormatted) {
if (mlir::isa<fir::RecordType>(fir::unwrapPassByRefType(type)))
- return getIORuntimeFunc<mkIOKey(InputDerivedType)>(loc, builder);
+ return fir::runtime::getIORuntimeFunc<mkIOKey(InputDerivedType)>(loc,
+ builder);
if (!isFormatted)
- return getIORuntimeFunc<mkIOKey(InputDescriptor)>(loc, builder);
+ return fir::runtime::getIORuntimeFunc<mkIOKey(InputDescriptor)>(loc,
+ builder);
if (auto ty = mlir::dyn_cast<mlir::IntegerType>(type)) {
if (type.isUnsignedInteger())
- ...
[truncated]
|
✅ With the latest revision this PR passed the Python code formatter. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks ok to me. You might want to get additional review.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks great, thanks
// cannot access memory indirectly through the box's | ||
// base_addr. | ||
auto def = arg.getDefiningOp(); | ||
if (!def || !mlir::isa<fir::ZeroOp>(def)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe worth adding fir::AbsentOp here that is also used in some context to create null box.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks! Will do.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is really nice, Slava :)
Just one question, if it turns out not to be relevant then this LGTM.
} | ||
}; | ||
|
||
// Derived types IO may call back into a Fortran module. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Couldn't derived type allocation and assignment also call back in the module?
Edit: It looks like this is only enabled for I/O functions for now?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You are right, Tom. I will have to provide specializations for those non-I/O APIs.
Another point is that distinguishing these cases from trivial ones (when no callback may happen) might be not qutie reliable that late in the pipeline, so I think it may be worth introducing separate APIs for trivial and non-trivial cases of assignment/etc. For the trivial cases, this will also help to avoid generating Fortran runtime calls that end up using call recursion, which is a problem for some offload devices.
If we split the APIs in such a way, then we will be able to set the attributes based on the APIs name rather than based on the recognition of the actual arguments.
LLVM Buildbot has detected a new failure on builder Full details are available at: https://lab.llvm.org/buildbot/#/builders/140/builds/17587 Here is the relevant piece of the build log for the reference
|
… 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.
LLVM Buildbot has detected a new failure on builder Full details are available at: https://lab.llvm.org/buildbot/#/builders/35/builds/7581 Here is the relevant piece of the build log for the reference
|
LLVM Buildbot has detected a new failure on builder Full details are available at: https://lab.llvm.org/buildbot/#/builders/166/builds/889 Here is the relevant piece of the build log for the reference
|
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.