[flang-commits] [flang] [flang] Implement !DIR$ [NO]INLINE directives (PR #134350)
Jean-Didier PAILLEUX via flang-commits
flang-commits at lists.llvm.org
Fri Apr 4 07:13:37 PDT 2025
https://github.com/JDPailleux updated https://github.com/llvm/llvm-project/pull/134350
>From 038819a4a4726215b59eab86dc6a19bd2c94e39f Mon Sep 17 00:00:00 2001
From: Jean-Didier Pailleux <jean-didier.pailleux at sipearl.com>
Date: Fri, 4 Apr 2025 08:55:20 +0200
Subject: [PATCH] [flang] Implement !DIR$ NOINLINE and FORCEINLINE directives
---
flang/docs/Directives.md | 6 +
flang/include/flang/Evaluate/call.h | 7 +
.../include/flang/Optimizer/Dialect/FIROps.td | 2 +
flang/include/flang/Parser/dump-parse-tree.h | 2 +
flang/include/flang/Parser/parse-tree.h | 6 +-
flang/lib/Lower/Bridge.cpp | 120 +++++++++++++++++-
flang/lib/Lower/ConvertCall.cpp | 8 +-
.../Transforms/PolymorphicOpConversion.cpp | 4 +-
flang/lib/Parser/Fortran-parsers.cpp | 6 +
flang/lib/Parser/unparse.cpp | 6 +
.../lib/Semantics/canonicalize-directives.cpp | 5 +-
flang/lib/Semantics/resolve-names.cpp | 4 +-
flang/test/Integration/inline_directive.f90 | 51 ++++++++
flang/test/Lower/inline_directive.f90 | 51 ++++++++
flang/test/Parser/compiler-directives.f90 | 20 +++
15 files changed, 286 insertions(+), 12 deletions(-)
create mode 100644 flang/test/Integration/inline_directive.f90
create mode 100644 flang/test/Lower/inline_directive.f90
diff --git a/flang/docs/Directives.md b/flang/docs/Directives.md
index 91c27cb510ea0..4f3748bf79722 100644
--- a/flang/docs/Directives.md
+++ b/flang/docs/Directives.md
@@ -53,6 +53,12 @@ A list of non-standard directives supported by Flang
* `!dir$ novector` disabling vectorization on the following loop.
* `!dir$ nounroll` disabling unrolling on the following loop.
* `!dir$ nounroll_and_jam` disabling unrolling and jamming on the following loop.
+* `!dir$ inline` tells the compiler to attempt to inline routines if
+ this directive is specified before a call statement or for all call function statements
+ within a DO LOOP. This directive can be improved later to support other place(s) for
+ inlining function calls.
+* `!dir$ noinline` works in the same way as the `inline` directive, but prevents
+ any attempt of inlining by the compiler on a function call statement.
# Directive Details
diff --git a/flang/include/flang/Evaluate/call.h b/flang/include/flang/Evaluate/call.h
index 2a5929b873d74..7a1bad030c88f 100644
--- a/flang/include/flang/Evaluate/call.h
+++ b/flang/include/flang/Evaluate/call.h
@@ -254,6 +254,11 @@ class ProcedureRef {
bool IsElemental() const { return proc_.IsElemental(); }
bool hasAlternateReturns() const { return hasAlternateReturns_; }
+ bool hasNoInline() const { return noInline_; }
+ void set_noInline(bool ni) { noInline_ = ni; }
+ bool hasAlwaysInline() const { return alwaysInline_; }
+ void set_alwaysInline(bool ai) { alwaysInline_ = ai; }
+
Expr<SomeType> *UnwrapArgExpr(int n) {
if (static_cast<std::size_t>(n) < arguments_.size() && arguments_[n]) {
return arguments_[n]->UnwrapExpr();
@@ -277,6 +282,8 @@ class ProcedureRef {
ActualArguments arguments_;
Chevrons chevrons_;
bool hasAlternateReturns_;
+ bool noInline_{false};
+ bool alwaysInline_{false};
};
template <typename A> class FunctionRef : public ProcedureRef {
diff --git a/flang/include/flang/Optimizer/Dialect/FIROps.td b/flang/include/flang/Optimizer/Dialect/FIROps.td
index 753e4bd18dc6d..18a198ca061b7 100644
--- a/flang/include/flang/Optimizer/Dialect/FIROps.td
+++ b/flang/include/flang/Optimizer/Dialect/FIROps.td
@@ -2490,6 +2490,8 @@ def fir_CallOp : fir_Op<"call",
OptionalAttr<DictArrayAttr>:$arg_attrs,
OptionalAttr<DictArrayAttr>:$res_attrs,
OptionalAttr<fir_FortranProcedureFlagsAttr>:$procedure_attrs,
+ OptionalAttr<UnitAttr>:$no_inline,
+ OptionalAttr<UnitAttr>:$always_inline,
DefaultValuedAttr<Arith_FastMathAttr,
"::mlir::arith::FastMathFlags::none">:$fastmath
);
diff --git a/flang/include/flang/Parser/dump-parse-tree.h b/flang/include/flang/Parser/dump-parse-tree.h
index c66f0735f33f0..614467bfa62ea 100644
--- a/flang/include/flang/Parser/dump-parse-tree.h
+++ b/flang/include/flang/Parser/dump-parse-tree.h
@@ -204,8 +204,10 @@ class ParseTreeDumper {
NODE(parser, CompilerDirective)
NODE(CompilerDirective, AssumeAligned)
NODE(CompilerDirective, IgnoreTKR)
+ NODE(CompilerDirective, ForceInline)
NODE(CompilerDirective, LoopCount)
NODE(CompilerDirective, NameValue)
+ NODE(CompilerDirective, NoInline)
NODE(CompilerDirective, Unrecognized)
NODE(CompilerDirective, VectorAlways)
NODE(CompilerDirective, Unroll)
diff --git a/flang/include/flang/Parser/parse-tree.h b/flang/include/flang/Parser/parse-tree.h
index eeb438991feee..4c4c6da1a73b4 100644
--- a/flang/include/flang/Parser/parse-tree.h
+++ b/flang/include/flang/Parser/parse-tree.h
@@ -3354,6 +3354,8 @@ struct StmtFunctionStmt {
// !DIR$ NOVECTOR
// !DIR$ NOUNROLL
// !DIR$ NOUNROLL_AND_JAM
+// !DIR$ FORCEINLINE
+// !DIR$ NOINLINE
// !DIR$ <anything else>
struct CompilerDirective {
UNION_CLASS_BOILERPLATE(CompilerDirective);
@@ -3382,11 +3384,13 @@ struct CompilerDirective {
EMPTY_CLASS(NoVector);
EMPTY_CLASS(NoUnroll);
EMPTY_CLASS(NoUnrollAndJam);
+ EMPTY_CLASS(ForceInline);
+ EMPTY_CLASS(NoInline);
EMPTY_CLASS(Unrecognized);
CharBlock source;
std::variant<std::list<IgnoreTKR>, LoopCount, std::list<AssumeAligned>,
VectorAlways, std::list<NameValue>, Unroll, UnrollAndJam, Unrecognized,
- NoVector, NoUnroll, NoUnrollAndJam>
+ NoVector, NoUnroll, NoUnrollAndJam, ForceInline, NoInline>
u;
};
diff --git a/flang/lib/Lower/Bridge.cpp b/flang/lib/Lower/Bridge.cpp
index 65edf1cea8761..16d64c653acdc 100644
--- a/flang/lib/Lower/Bridge.cpp
+++ b/flang/lib/Lower/Bridge.cpp
@@ -1828,6 +1828,23 @@ class FirConverter : public Fortran::lower::AbstractConverter {
setCurrentPosition(stmt.source);
assert(stmt.typedCall && "Call was not analyzed");
mlir::Value res{};
+
+ // Set 'no_inline' or 'always_inline' to true on the ProcedureRef.
+ // The NoInline and AlwaysInline attribute will be set in genProcedureRef
+ // later.
+ for (const auto *dir : eval.dirs) {
+ Fortran::common::visit(
+ Fortran::common::visitors{
+ [&](const Fortran::parser::CompilerDirective::ForceInline &) {
+ stmt.typedCall->set_alwaysInline(true);
+ },
+ [&](const Fortran::parser::CompilerDirective::NoInline &) {
+ stmt.typedCall->set_noInline(true);
+ },
+ [&](const auto &) {}},
+ dir->u);
+ }
+
if (lowerToHighLevelFIR()) {
std::optional<mlir::Type> resultType;
if (stmt.typedCall->hasAlternateReturns())
@@ -2053,6 +2070,47 @@ class FirConverter : public Fortran::lower::AbstractConverter {
// so no clean-up needs to be generated for these entities.
}
+ void attachInlineAttributes(
+ mlir::Operation &op,
+ const llvm::ArrayRef<const Fortran::parser::CompilerDirective *> &dirs) {
+ if (dirs.empty())
+ return;
+
+ for (mlir::Value operand : op.getOperands()) {
+ if (operand.getDefiningOp())
+ attachInlineAttributes(*operand.getDefiningOp(), dirs);
+ }
+
+ if (fir::CallOp callOp = mlir::dyn_cast<fir::CallOp>(op)) {
+ for (const auto *dir : dirs) {
+ Fortran::common::visit(
+ Fortran::common::visitors{
+ [&](const Fortran::parser::CompilerDirective::NoInline &) {
+ callOp.setNoInlineAttr(builder->getUnitAttr());
+ },
+ [&](const Fortran::parser::CompilerDirective::ForceInline &) {
+ callOp.setAlwaysInlineAttr(builder->getUnitAttr());
+ },
+ [&](const auto &) {}},
+ dir->u);
+ }
+ }
+ }
+
+ void attachAttributesToDoLoopOperations(
+ fir::DoLoopOp &doLoop,
+ llvm::SmallVectorImpl<const Fortran::parser::CompilerDirective *> &dirs) {
+ if (!doLoop.getOperation() || dirs.empty())
+ return;
+
+ for (mlir::Block &block : doLoop.getRegion()) {
+ for (mlir::Operation &op : block.getOperations()) {
+ if (!dirs.empty())
+ attachInlineAttributes(op, dirs);
+ }
+ }
+ }
+
/// Generate FIR for a DO construct. There are six variants:
/// - unstructured infinite and while loops
/// - structured and unstructured increment loops
@@ -2162,6 +2220,10 @@ class FirConverter : public Fortran::lower::AbstractConverter {
// This call may generate a branch in some contexts.
genFIR(endDoEval, unstructuredContext);
+
+ // Add attribute(s) on operations in fir::DoLoopOp if necessary
+ for (IncrementLoopInfo &info : incrementLoopNestInfo)
+ attachAttributesToDoLoopOperations(info.doLoop, doStmtEval.dirs);
}
/// Generate FIR to evaluate loop control values (lower, upper and step).
@@ -2935,6 +2997,26 @@ class FirConverter : public Fortran::lower::AbstractConverter {
e->dirs.push_back(&dir);
}
+ void
+ attachInliningDirectiveToStmt(const Fortran::parser::CompilerDirective &dir,
+ Fortran::lower::pft::Evaluation *e) {
+ while (e->isDirective())
+ e = e->lexicalSuccessor;
+
+ // If the successor is a statement or a do loop, the compiler
+ // will perform inlining.
+ if (e->isA<Fortran::parser::CallStmt>() ||
+ e->isA<Fortran::parser::NonLabelDoStmt>() ||
+ e->isA<Fortran::parser::AssignmentStmt>()) {
+ e->dirs.push_back(&dir);
+ } else {
+ mlir::Location loc = toLocation();
+ mlir::emitWarning(loc,
+ "Inlining directive not in front of loops, function"
+ "call or assignment.\n");
+ }
+ }
+
void genFIR(const Fortran::parser::CompilerDirective &dir) {
Fortran::lower::pft::Evaluation &eval = getEval();
@@ -2958,6 +3040,12 @@ class FirConverter : public Fortran::lower::AbstractConverter {
[&](const Fortran::parser::CompilerDirective::NoUnrollAndJam &) {
attachDirectiveToLoop(dir, &eval);
},
+ [&](const Fortran::parser::CompilerDirective::ForceInline &) {
+ attachInliningDirectiveToStmt(dir, &eval);
+ },
+ [&](const Fortran::parser::CompilerDirective::NoInline &) {
+ attachInliningDirectiveToStmt(dir, &eval);
+ },
[&](const auto &) {}},
dir.u);
}
@@ -4763,7 +4851,9 @@ class FirConverter : public Fortran::lower::AbstractConverter {
void genDataAssignment(
const Fortran::evaluate::Assignment &assign,
- const Fortran::evaluate::ProcedureRef *userDefinedAssignment) {
+ const Fortran::evaluate::ProcedureRef *userDefinedAssignment,
+ const llvm::ArrayRef<const Fortran::parser::CompilerDirective *> &dirs =
+ {}) {
mlir::Location loc = getCurrentLocation();
fir::FirOpBuilder &builder = getFirOpBuilder();
@@ -4836,12 +4926,22 @@ class FirConverter : public Fortran::lower::AbstractConverter {
Fortran::lower::StatementContext localStmtCtx;
hlfir::Entity rhs = evaluateRhs(localStmtCtx);
hlfir::Entity lhs = evaluateLhs(localStmtCtx);
- if (isCUDATransfer && !hasCUDAImplicitTransfer)
+ if (isCUDATransfer && !hasCUDAImplicitTransfer) {
genCUDADataTransfer(builder, loc, assign, lhs, rhs);
- else
+ } else {
+ // If RHS or LHS have a CallOp in their expression, this operation will
+ // have the 'no_inline' or 'always_inline' attribute if there is a
+ // directive just before the assignement.
+ if (!dirs.empty()) {
+ if (rhs.getDefiningOp())
+ attachInlineAttributes(*rhs.getDefiningOp(), dirs);
+ if (lhs.getDefiningOp())
+ attachInlineAttributes(*lhs.getDefiningOp(), dirs);
+ }
builder.create<hlfir::AssignOp>(loc, rhs, lhs,
isWholeAllocatableAssignment,
keepLhsLengthInAllocatableAssignment);
+ }
if (hasCUDAImplicitTransfer && !isInDeviceContext) {
localSymbols.popScope();
for (mlir::Value temp : implicitTemps)
@@ -4909,16 +5009,21 @@ class FirConverter : public Fortran::lower::AbstractConverter {
}
/// Shared for both assignments and pointer assignments.
- void genAssignment(const Fortran::evaluate::Assignment &assign) {
+ void
+ genAssignment(const Fortran::evaluate::Assignment &assign,
+ const llvm::ArrayRef<const Fortran::parser::CompilerDirective *>
+ &dirs = {}) {
mlir::Location loc = toLocation();
if (lowerToHighLevelFIR()) {
Fortran::common::visit(
Fortran::common::visitors{
[&](const Fortran::evaluate::Assignment::Intrinsic &) {
- genDataAssignment(assign, /*userDefinedAssignment=*/nullptr);
+ genDataAssignment(assign, /*userDefinedAssignment=*/nullptr,
+ dirs);
},
[&](const Fortran::evaluate::ProcedureRef &procRef) {
- genDataAssignment(assign, /*userDefinedAssignment=*/&procRef);
+ genDataAssignment(assign, /*userDefinedAssignment=*/&procRef,
+ dirs);
},
[&](const Fortran::evaluate::Assignment::BoundsSpec &lbExprs) {
if (isInsideHlfirForallOrWhere())
@@ -5323,7 +5428,8 @@ class FirConverter : public Fortran::lower::AbstractConverter {
}
void genFIR(const Fortran::parser::AssignmentStmt &stmt) {
- genAssignment(*stmt.typedAssignment->v);
+ Fortran::lower::pft::Evaluation &eval = getEval();
+ genAssignment(*stmt.typedAssignment->v, eval.dirs);
}
void genFIR(const Fortran::parser::SyncAllStmt &stmt) {
diff --git a/flang/lib/Lower/ConvertCall.cpp b/flang/lib/Lower/ConvertCall.cpp
index 6a0f4d1090adc..fe8d84c2c8c37 100644
--- a/flang/lib/Lower/ConvertCall.cpp
+++ b/flang/lib/Lower/ConvertCall.cpp
@@ -647,9 +647,15 @@ Fortran::lower::genCallOpAndResult(
callResult = dispatch.getResult(0);
} else {
// Standard procedure call with fir.call.
+ mlir::UnitAttr noinlineAttr, alwaysinlineAttr;
+ if (caller.getCallDescription().hasNoInline())
+ noinlineAttr = builder.getUnitAttr();
+ else if (caller.getCallDescription().hasAlwaysInline())
+ alwaysinlineAttr = builder.getUnitAttr();
auto call = builder.create<fir::CallOp>(
loc, funcType.getResults(), funcSymbolAttr, operands,
- /*arg_attrs=*/nullptr, /*res_attrs=*/nullptr, procAttrs);
+ /*arg_attrs=*/nullptr, /*res_attrs=*/nullptr, procAttrs, noinlineAttr,
+ alwaysinlineAttr);
callNumResults = call.getNumResults();
if (callNumResults != 0)
diff --git a/flang/lib/Optimizer/Transforms/PolymorphicOpConversion.cpp b/flang/lib/Optimizer/Transforms/PolymorphicOpConversion.cpp
index 0c78a878cdc53..eff623d983535 100644
--- a/flang/lib/Optimizer/Transforms/PolymorphicOpConversion.cpp
+++ b/flang/lib/Optimizer/Transforms/PolymorphicOpConversion.cpp
@@ -207,7 +207,9 @@ struct DispatchOpConv : public OpConversionPattern<fir::DispatchOp> {
args.append(dispatch.getArgs().begin(), dispatch.getArgs().end());
rewriter.replaceOpWithNewOp<fir::CallOp>(
dispatch, resTypes, nullptr, args, dispatch.getArgAttrsAttr(),
- dispatch.getResAttrsAttr(), dispatch.getProcedureAttrsAttr());
+ dispatch.getResAttrsAttr(), dispatch.getProcedureAttrsAttr(),
+ /*no_inline*/ mlir::UnitAttr{},
+ /*alwais_inline*/ mlir::UnitAttr{});
return mlir::success();
}
diff --git a/flang/lib/Parser/Fortran-parsers.cpp b/flang/lib/Parser/Fortran-parsers.cpp
index fbe629ab52935..88bd77607c7a8 100644
--- a/flang/lib/Parser/Fortran-parsers.cpp
+++ b/flang/lib/Parser/Fortran-parsers.cpp
@@ -1314,6 +1314,10 @@ constexpr auto novector{"NOVECTOR" >> construct<CompilerDirective::NoVector>()};
constexpr auto nounroll{"NOUNROLL" >> construct<CompilerDirective::NoUnroll>()};
constexpr auto nounrollAndJam{
"NOUNROLL_AND_JAM" >> construct<CompilerDirective::NoUnrollAndJam>()};
+constexpr auto forceinlineDir{
+ "FORCEINLINE" >> construct<CompilerDirective::ForceInline>()};
+constexpr auto noinlineDir{
+ "NOINLINE" >> construct<CompilerDirective::NoInline>()};
TYPE_PARSER(beginDirective >> "DIR$ "_tok >>
sourced((construct<CompilerDirective>(ignore_tkr) ||
construct<CompilerDirective>(loopCount) ||
@@ -1324,6 +1328,8 @@ TYPE_PARSER(beginDirective >> "DIR$ "_tok >>
construct<CompilerDirective>(novector) ||
construct<CompilerDirective>(nounrollAndJam) ||
construct<CompilerDirective>(nounroll) ||
+ construct<CompilerDirective>(noinlineDir) ||
+ construct<CompilerDirective>(forceinlineDir) ||
construct<CompilerDirective>(
many(construct<CompilerDirective::NameValue>(
name, maybe(("="_tok || ":"_tok) >> digitString64))))) /
diff --git a/flang/lib/Parser/unparse.cpp b/flang/lib/Parser/unparse.cpp
index 47dae0ae753d2..4bee93d138076 100644
--- a/flang/lib/Parser/unparse.cpp
+++ b/flang/lib/Parser/unparse.cpp
@@ -1864,6 +1864,12 @@ class UnparseVisitor {
[&](const CompilerDirective::NoUnrollAndJam &) {
Word("!DIR$ NOUNROLL_AND_JAM");
},
+ [&](const CompilerDirective::ForceInline &) {
+ Word("!DIR$ FORCEINLINE");
+ },
+ [&](const CompilerDirective::NoInline &) {
+ Word("!DIR$ NOINLINE");
+ },
[&](const CompilerDirective::Unrecognized &) {
Word("!DIR$ ");
Word(x.source.ToString());
diff --git a/flang/lib/Semantics/canonicalize-directives.cpp b/flang/lib/Semantics/canonicalize-directives.cpp
index 104df253ab642..3d9e5460ec862 100644
--- a/flang/lib/Semantics/canonicalize-directives.cpp
+++ b/flang/lib/Semantics/canonicalize-directives.cpp
@@ -60,7 +60,10 @@ static bool IsExecutionDirective(const parser::CompilerDirective &dir) {
std::holds_alternative<parser::CompilerDirective::UnrollAndJam>(dir.u) ||
std::holds_alternative<parser::CompilerDirective::NoVector>(dir.u) ||
std::holds_alternative<parser::CompilerDirective::NoUnroll>(dir.u) ||
- std::holds_alternative<parser::CompilerDirective::NoUnrollAndJam>(dir.u);
+ std::holds_alternative<parser::CompilerDirective::NoUnrollAndJam>(
+ dir.u) ||
+ std::holds_alternative<parser::CompilerDirective::ForceInline>(dir.u) ||
+ std::holds_alternative<parser::CompilerDirective::NoInline>(dir.u);
}
void CanonicalizationOfDirectives::Post(parser::SpecificationPart &spec) {
diff --git a/flang/lib/Semantics/resolve-names.cpp b/flang/lib/Semantics/resolve-names.cpp
index 11c0ecc9e8410..12b26f4211c61 100644
--- a/flang/lib/Semantics/resolve-names.cpp
+++ b/flang/lib/Semantics/resolve-names.cpp
@@ -9576,7 +9576,9 @@ void ResolveNamesVisitor::Post(const parser::CompilerDirective &x) {
std::holds_alternative<parser::CompilerDirective::UnrollAndJam>(x.u) ||
std::holds_alternative<parser::CompilerDirective::NoVector>(x.u) ||
std::holds_alternative<parser::CompilerDirective::NoUnroll>(x.u) ||
- std::holds_alternative<parser::CompilerDirective::NoUnrollAndJam>(x.u)) {
+ std::holds_alternative<parser::CompilerDirective::NoUnrollAndJam>(x.u) ||
+ std::holds_alternative<parser::CompilerDirective::ForceInline>(x.u) ||
+ std::holds_alternative<parser::CompilerDirective::NoInline>(x.u)) {
return;
}
if (const auto *tkr{
diff --git a/flang/test/Integration/inline_directive.f90 b/flang/test/Integration/inline_directive.f90
new file mode 100644
index 0000000000000..b555d9058195e
--- /dev/null
+++ b/flang/test/Integration/inline_directive.f90
@@ -0,0 +1,51 @@
+! RUN: %flang_fc1 -emit-llvm -o - %s | FileCheck %s
+
+! CHECK-LABEL: test_inline
+subroutine test_inline()
+ integer :: x, y
+!CHECK: %[[VAL_1:.*]] = alloca i32, i64 1, align 4
+!CHECK: %[[VAL_2:.*]] = alloca i32, i64 1, align 4
+!CHECK: %[[VAL_3:.*]] = alloca i32, i64 1, align 4
+!CHECK: %[[VAL_4:.*]] = alloca i32, i64 1, align 4
+
+ !dir$ forceinline
+ y = g(x)
+ !dir$ forceinline
+ call f(x, y)
+!CHECK: %[[VAL_5:.*]] = load i32, ptr %[[VAL_3]], align 4
+!CHECK: %[[VAL_6:.*]] = mul i32 %[[VAL_5]], 2
+!CHECK: store i32 %6, ptr %[[VAL_1]], align 4
+!CHECK: %[[VAL_7:.*]] = load i32, ptr %[[VAL_1]], align 4
+!CHECK: store i32 %7, ptr %[[VAL_2]], align 4
+!CHECK: %[[VAL_8:.]] = load i32, ptr %[[VAL_3]], align 4
+!CHECK: %[[VAL_9:.]] = mul i32 %[[VAL_8]], 2
+!CHECK: store i32 %9, ptr %[[VAL_2]], align 4
+
+ !dir$ noinline
+ y = g(x)
+ !dir$ noinline
+ call f(x, y)
+!CHECK: %[[VAL_10:.*]] = call i32 @_QFtest_inlinePg(ptr %[[VAL_3]]) #[[NOINLINE:.*]]
+!CHECK: store i32 %[[VAL_10]], ptr %[[VAL_2]], align 4
+!CHECK: call void @_QFtest_inlinePf(ptr %[[VAL_3]], ptr %[[VAL_2]]) #[[NOINLINE]]
+
+ !dir$ noinline
+ do i = 1, 100
+ call f(x, y)
+ !CHECK: br i1 %[[VAL_14:.*]], label %[[VAL_15:.*]], label %[[VAL_19:.*]]
+ !CHECK: call void @_QFtest_inlinePf(ptr %[[VAL_3]], ptr %[[VAL_2]]) #[[NOINLINE]]
+ enddo
+
+ contains
+ subroutine f(x, y)
+ integer, intent(in) :: x
+ integer, intent(out) :: y
+ y = x*2
+ end subroutine f
+ integer function g(x)
+ integer :: x
+ g = x*2
+ end function g
+end subroutine test_inline
+
+!CHECK: attributes #[[NOINLINE]] = { noinline }
diff --git a/flang/test/Lower/inline_directive.f90 b/flang/test/Lower/inline_directive.f90
new file mode 100644
index 0000000000000..ac016f32cf009
--- /dev/null
+++ b/flang/test/Lower/inline_directive.f90
@@ -0,0 +1,51 @@
+! RUN: %flang_fc1 -emit-fir -o - %s | FileCheck %s
+
+subroutine test_inline()
+ integer :: x, y
+!CHECK: %[[VAL_0:.*]] = fir.alloca i32 {bindc_name = "x", uniq_name = "_QFtest_inlineEx"}
+!CHECK: %[[VAL_1:.*]] = fir.declare %[[VAL_0]] {uniq_name = "_QFtest_inlineEx"} : (!fir.ref<i32>) -> !fir.ref<i32>
+!CHECK: %[[VAL_2:.*]] = fir.alloca i32 {bindc_name = "y", uniq_name = "_QFtest_inlineEy"}
+!CHECK: %[[VAL_3:.*]] = fir.declare %[[VAL_2]] {uniq_name = "_QFtest_inlineEy"} : (!fir.ref<i32>) -> !fir.ref<i32>
+
+ !dir$ forceinline
+ y = g(x)
+ !CHECK: %[[VAL_4:.*]] = fir.call @_QFtest_inlinePg(%[[VAL_1]]) fastmath<contract> {always_inline} : (!fir.ref<i32>) -> i32
+ !CHECK: fir.store %[[VAL_4]] to %[[VAL_3]] : !fir.ref<i32>
+
+ !dir$ forceinline
+ call f(x, y)
+ !CHECK: fir.call @_QFtest_inlinePf(%[[VAL_1]], %[[VAL_3]]) fastmath<contract> {always_inline} : (!fir.ref<i32>, !fir.ref<i32>) -> ()
+
+ !dir$ noinline
+ y = g(x) + 7 * (8 + g(y))
+ !CHECK: %[[VAL_8:.*]] = fir.call @_QFtest_inlinePg(%[[VAL_1]]) fastmath<contract> {no_inline} : (!fir.ref<i32>) -> i32
+ !CHECK: %[[VAL_9:.*]] = fir.call @_QFtest_inlinePg(%[[VAL_3]]) fastmath<contract> {no_inline} : (!fir.ref<i32>) -> i32
+ !CHECK: %[[VAL_10:.*]] = arith.addi %[[VAL_9]], %[[C8:.*]] : i32
+ !CHECK: %[[VAL_11:.*]] = fir.no_reassoc %[[VAL_10]] : i32
+ !CHECK: %[[VAL_12:.*]] = arith.muli %[[VAL_11]], %[[C7:.*]] : i32
+ !CHECK: %[[VAL_13:.*]] = arith.addi %[[VAL_8]], %[[VAL_12]] : i32
+ !CHECK: fir.store %[[VAL_13]] to %[[VAL_3]] : !fir.ref<i32>
+
+ !dir$ noinline
+ call f(x, y)
+ !CHECK: fir.call @_QFtest_inlinePf(%[[VAL_1]], %[[VAL_3]]) fastmath<contract> {no_inline} : (!fir.ref<i32>, !fir.ref<i32>) -> ()
+
+ !dir$ forceinline
+ do i = 1, 100
+ !CHECK: fir.do_loop %[[ARG_0:.*]] = {{.*}} -> (index, i32) {
+ !CHECK: fir.call @_QFtest_inlinePf(%[[VAL_1]], %[[VAL_3]]) fastmath<contract> {always_inline} : (!fir.ref<i32>, !fir.ref<i32>) -> ()
+ call f(x, y)
+ enddo
+
+!CHECK: return
+ contains
+ subroutine f(x, y)
+ integer, intent(in) :: x
+ integer, intent(out) :: y
+ y = x*2
+ end subroutine f
+ integer function g(x)
+ integer :: x
+ g = x*2
+ end function g
+end subroutine test_inline
diff --git a/flang/test/Parser/compiler-directives.f90 b/flang/test/Parser/compiler-directives.f90
index 04d22ff0fd8ee..3378bf74e7d46 100644
--- a/flang/test/Parser/compiler-directives.f90
+++ b/flang/test/Parser/compiler-directives.f90
@@ -72,3 +72,23 @@ subroutine no_vector
do i=1,10
enddo
end subroutine
+
+subroutine inline
+ integer :: a
+ !dir$ forceinline
+ ! CHECK: !DIR$ FORCEINLINE
+ a = f(2)
+
+ !dir$ noinline
+ ! CHECK: !DIR$ NOINLINE
+ call g()
+
+ contains
+ function f(x)
+ integer :: x
+ f = x**2
+ end function
+
+ subroutine g()
+ end subroutine
+end subroutine
More information about the flang-commits
mailing list