mirror of
https://github.com/intel/llvm.git
synced 2026-01-13 11:02:04 +08:00
[flang] Implement !DIR$ [NO]INLINE and FORCEINLINE directives (#134350)
This patch adds the support of these two directives : `!dir$ inline` and `!dir$ noinline`. - `!dir$ noinline` tells to the compiler to not perform inlining on specific function calls by adding the `noinline` metadata on the call. - `!dir$ inline` tells to the compiler to attempt inlining on specific function calls by adding the `inlinehint` metadata on the call. - `!dir$ forceinline` tells to the compiler to always perfom inlining on specific function calls by adding the `alwaysinline` metadata on the call. Currently, these directives can be placed before a `DO LOOP`, call functions or assignments. Maybe other statements can be added in the future if needed. For the `inline` directive the correct name might be `forceinline` but I'm not sure ?
This commit is contained in:
committed by
GitHub
parent
20c323aa0e
commit
c1779f33bd
@@ -53,6 +53,14 @@ 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` instructs the compiler to attempt to inline the called routines if the
|
||||
directive is specified before a call statement or all call statements within the loop
|
||||
body if specified before a DO LOOP or all function references if specified before an
|
||||
assignment statement.
|
||||
* `!dir$ forceinline` works in the same way as the `inline` directive, but it forces
|
||||
inlining by the compiler on a function call statement.
|
||||
* `!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
|
||||
|
||||
|
||||
@@ -255,6 +255,13 @@ public:
|
||||
bool IsElemental() const { return proc_.IsElemental(); }
|
||||
bool hasAlternateReturns() const { return hasAlternateReturns_; }
|
||||
|
||||
bool hasNoInline() const { return noInline_; }
|
||||
void setNoInline(bool ni) { noInline_ = ni; }
|
||||
bool hasAlwaysInline() const { return alwaysInline_; }
|
||||
void setAlwaysInline(bool ai) { alwaysInline_ = ai; }
|
||||
bool hasInlineHint() const { return inlineHint_; }
|
||||
void setInlineHint(bool ih) { inlineHint_ = ih; }
|
||||
|
||||
Expr<SomeType> *UnwrapArgExpr(int n) {
|
||||
if (static_cast<std::size_t>(n) < arguments_.size() && arguments_[n]) {
|
||||
return arguments_[n]->UnwrapExpr();
|
||||
@@ -278,6 +285,9 @@ protected:
|
||||
ActualArguments arguments_;
|
||||
Chevrons chevrons_;
|
||||
bool hasAlternateReturns_;
|
||||
bool noInline_{false};
|
||||
bool alwaysInline_{false};
|
||||
bool inlineHint_{false};
|
||||
};
|
||||
|
||||
template <typename A> class FunctionRef : public ProcedureRef {
|
||||
|
||||
@@ -219,4 +219,24 @@ def LocalitySpecifierTypeAttr : EnumAttr<FIROpsDialect, LocalitySpecifierType,
|
||||
let assemblyFormat = "`{` `type` `=` $value `}`";
|
||||
}
|
||||
|
||||
/// Fortran inline attribute
|
||||
def FIRinlineNone : I32BitEnumAttrCaseNone<"none">;
|
||||
def FIRinlineNo : I32BitEnumAttrCaseBit<"no_inline", 0>;
|
||||
def FIRinlineAlways : I32BitEnumAttrCaseBit<"always_inline", 1>;
|
||||
def FIRinlineHint : I32BitEnumAttrCaseBit<"inline_hint", 2>;
|
||||
|
||||
def fir_FortranInlineEnum
|
||||
: I32BitEnumAttr<"FortranInlineEnum", "Fortran inline attributes",
|
||||
[FIRinlineNone, FIRinlineNo, FIRinlineAlways,
|
||||
FIRinlineHint]> {
|
||||
let separator = ", ";
|
||||
let cppNamespace = "::fir";
|
||||
let genSpecializedAttr = 0;
|
||||
let printBitEnumPrimaryGroups = 1;
|
||||
}
|
||||
|
||||
def fir_FortranInlineAttr
|
||||
: EnumAttr<FIROpsDialect, fir_FortranInlineEnum, "inline_attrs"> {
|
||||
let assemblyFormat = "`<` $value `>`";
|
||||
}
|
||||
#endif // FIR_DIALECT_FIR_ATTRS
|
||||
|
||||
@@ -2549,6 +2549,7 @@ def fir_CallOp : fir_Op<"call",
|
||||
OptionalAttr<DictArrayAttr>:$arg_attrs,
|
||||
OptionalAttr<DictArrayAttr>:$res_attrs,
|
||||
OptionalAttr<fir_FortranProcedureFlagsAttr>:$procedure_attrs,
|
||||
OptionalAttr<fir_FortranInlineAttr>:$inline_attr,
|
||||
DefaultValuedAttr<Arith_FastMathAttr,
|
||||
"::mlir::arith::FastMathFlags::none">:$fastmath
|
||||
);
|
||||
|
||||
@@ -206,8 +206,11 @@ public:
|
||||
NODE(parser, CompilerDirective)
|
||||
NODE(CompilerDirective, AssumeAligned)
|
||||
NODE(CompilerDirective, IgnoreTKR)
|
||||
NODE(CompilerDirective, Inline)
|
||||
NODE(CompilerDirective, ForceInline)
|
||||
NODE(CompilerDirective, LoopCount)
|
||||
NODE(CompilerDirective, NameValue)
|
||||
NODE(CompilerDirective, NoInline)
|
||||
NODE(CompilerDirective, Unrecognized)
|
||||
NODE(CompilerDirective, VectorAlways)
|
||||
NODE(CompilerDirective, Unroll)
|
||||
|
||||
@@ -3356,6 +3356,9 @@ struct StmtFunctionStmt {
|
||||
// !DIR$ NOVECTOR
|
||||
// !DIR$ NOUNROLL
|
||||
// !DIR$ NOUNROLL_AND_JAM
|
||||
// !DIR$ FORCEINLINE
|
||||
// !DIR$ INLINE
|
||||
// !DIR$ NOINLINE
|
||||
// !DIR$ <anything else>
|
||||
struct CompilerDirective {
|
||||
UNION_CLASS_BOILERPLATE(CompilerDirective);
|
||||
@@ -3384,11 +3387,14 @@ struct CompilerDirective {
|
||||
EMPTY_CLASS(NoVector);
|
||||
EMPTY_CLASS(NoUnroll);
|
||||
EMPTY_CLASS(NoUnrollAndJam);
|
||||
EMPTY_CLASS(ForceInline);
|
||||
EMPTY_CLASS(Inline);
|
||||
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, Inline, NoInline>
|
||||
u;
|
||||
};
|
||||
|
||||
|
||||
@@ -1884,6 +1884,26 @@ private:
|
||||
setCurrentPosition(stmt.source);
|
||||
assert(stmt.typedCall && "Call was not analyzed");
|
||||
mlir::Value res{};
|
||||
|
||||
// Set 'no_inline', 'inline_hint' 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->setAlwaysInline(true);
|
||||
},
|
||||
[&](const Fortran::parser::CompilerDirective::Inline &) {
|
||||
stmt.typedCall->setInlineHint(true);
|
||||
},
|
||||
[&](const Fortran::parser::CompilerDirective::NoInline &) {
|
||||
stmt.typedCall->setNoInline(true);
|
||||
},
|
||||
[&](const auto &) {}},
|
||||
dir->u);
|
||||
}
|
||||
|
||||
if (lowerToHighLevelFIR()) {
|
||||
std::optional<mlir::Type> resultType;
|
||||
if (stmt.typedCall->hasAlternateReturns())
|
||||
@@ -2200,6 +2220,50 @@ private:
|
||||
// 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.setInlineAttr(fir::FortranInlineEnum::no_inline);
|
||||
},
|
||||
[&](const Fortran::parser::CompilerDirective::Inline &) {
|
||||
callOp.setInlineAttr(fir::FortranInlineEnum::inline_hint);
|
||||
},
|
||||
[&](const Fortran::parser::CompilerDirective::ForceInline &) {
|
||||
callOp.setInlineAttr(fir::FortranInlineEnum::always_inline);
|
||||
},
|
||||
[&](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
|
||||
@@ -2351,6 +2415,11 @@ private:
|
||||
if (!incrementLoopNestInfo.empty() &&
|
||||
incrementLoopNestInfo.back().isConcurrent)
|
||||
localSymbols.popScope();
|
||||
|
||||
// Add attribute(s) on operations in fir::DoLoopOp if necessary
|
||||
for (IncrementLoopInfo &info : incrementLoopNestInfo)
|
||||
if (auto loopOp = mlir::dyn_cast_if_present<fir::DoLoopOp>(info.loopOp))
|
||||
attachAttributesToDoLoopOperations(loopOp, doStmtEval.dirs);
|
||||
}
|
||||
|
||||
/// Generate FIR to evaluate loop control values (lower, upper and step).
|
||||
@@ -3154,6 +3223,26 @@ private:
|
||||
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();
|
||||
|
||||
@@ -3177,6 +3266,15 @@ private:
|
||||
[&](const Fortran::parser::CompilerDirective::NoUnrollAndJam &) {
|
||||
attachDirectiveToLoop(dir, &eval);
|
||||
},
|
||||
[&](const Fortran::parser::CompilerDirective::ForceInline &) {
|
||||
attachInliningDirectiveToStmt(dir, &eval);
|
||||
},
|
||||
[&](const Fortran::parser::CompilerDirective::Inline &) {
|
||||
attachInliningDirectiveToStmt(dir, &eval);
|
||||
},
|
||||
[&](const Fortran::parser::CompilerDirective::NoInline &) {
|
||||
attachInliningDirectiveToStmt(dir, &eval);
|
||||
},
|
||||
[&](const auto &) {}},
|
||||
dir.u);
|
||||
}
|
||||
@@ -5086,7 +5184,9 @@ private:
|
||||
|
||||
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();
|
||||
|
||||
@@ -5166,10 +5266,20 @@ private:
|
||||
genCUDADataTransfer(builder, loc, assign, lhs, rhs,
|
||||
isWholeAllocatableAssignment,
|
||||
keepLhsLengthInAllocatableAssignment);
|
||||
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);
|
||||
}
|
||||
hlfir::AssignOp::create(builder, loc, rhs, lhs,
|
||||
isWholeAllocatableAssignment,
|
||||
keepLhsLengthInAllocatableAssignment);
|
||||
}
|
||||
if (hasCUDAImplicitTransfer && !isInDeviceContext) {
|
||||
localSymbols.popScope();
|
||||
for (mlir::Value temp : implicitTemps)
|
||||
@@ -5237,16 +5347,21 @@ private:
|
||||
}
|
||||
|
||||
/// 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())
|
||||
@@ -5651,7 +5766,8 @@ private:
|
||||
}
|
||||
|
||||
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) {
|
||||
|
||||
@@ -700,9 +700,20 @@ Fortran::lower::genCallOpAndResult(
|
||||
callResult = dispatch.getResult(0);
|
||||
} else {
|
||||
// Standard procedure call with fir.call.
|
||||
fir::FortranInlineEnumAttr inlineAttr;
|
||||
|
||||
if (caller.getCallDescription().hasNoInline())
|
||||
inlineAttr = fir::FortranInlineEnumAttr::get(
|
||||
builder.getContext(), fir::FortranInlineEnum::no_inline);
|
||||
else if (caller.getCallDescription().hasInlineHint())
|
||||
inlineAttr = fir::FortranInlineEnumAttr::get(
|
||||
builder.getContext(), fir::FortranInlineEnum::inline_hint);
|
||||
else if (caller.getCallDescription().hasAlwaysInline())
|
||||
inlineAttr = fir::FortranInlineEnumAttr::get(
|
||||
builder.getContext(), fir::FortranInlineEnum::always_inline);
|
||||
auto call = fir::CallOp::create(
|
||||
builder, loc, funcType.getResults(), funcSymbolAttr, operands,
|
||||
/*arg_attrs=*/nullptr, /*res_attrs=*/nullptr, procAttrs);
|
||||
/*arg_attrs=*/nullptr, /*res_attrs=*/nullptr, procAttrs, inlineAttr);
|
||||
|
||||
callNumResults = call.getNumResults();
|
||||
if (callNumResults != 0)
|
||||
|
||||
@@ -680,6 +680,18 @@ struct CallOpConversion : public fir::FIROpConversion<fir::CallOp> {
|
||||
if (mlir::ArrayAttr resAttrs = call.getResAttrsAttr())
|
||||
llvmCall.setResAttrsAttr(resAttrs);
|
||||
|
||||
if (auto inlineAttr = call.getInlineAttrAttr()) {
|
||||
llvmCall->removeAttr("inline_attr");
|
||||
if (inlineAttr.getValue() == fir::FortranInlineEnum::no_inline) {
|
||||
llvmCall.setNoInlineAttr(rewriter.getUnitAttr());
|
||||
} else if (inlineAttr.getValue() == fir::FortranInlineEnum::inline_hint) {
|
||||
llvmCall.setInlineHintAttr(rewriter.getUnitAttr());
|
||||
} else if (inlineAttr.getValue() ==
|
||||
fir::FortranInlineEnum::always_inline) {
|
||||
llvmCall.setAlwaysInlineAttr(rewriter.getUnitAttr());
|
||||
}
|
||||
}
|
||||
|
||||
if (memAttr)
|
||||
llvmCall.setMemoryEffectsAttr(
|
||||
mlir::cast<mlir::LLVM::MemoryEffectsAttr>(memAttr));
|
||||
|
||||
@@ -246,7 +246,8 @@ 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(),
|
||||
/*inline_attr*/ fir::FortranInlineEnumAttr{});
|
||||
return mlir::success();
|
||||
}
|
||||
|
||||
|
||||
@@ -1314,6 +1314,11 @@ 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>()};
|
||||
constexpr auto inlineDir{"INLINE" >> construct<CompilerDirective::Inline>()};
|
||||
TYPE_PARSER(beginDirective >> "DIR$ "_tok >>
|
||||
sourced((construct<CompilerDirective>(ignore_tkr) ||
|
||||
construct<CompilerDirective>(loopCount) ||
|
||||
@@ -1324,6 +1329,9 @@ TYPE_PARSER(beginDirective >> "DIR$ "_tok >>
|
||||
construct<CompilerDirective>(novector) ||
|
||||
construct<CompilerDirective>(nounrollAndJam) ||
|
||||
construct<CompilerDirective>(nounroll) ||
|
||||
construct<CompilerDirective>(noinlineDir) ||
|
||||
construct<CompilerDirective>(forceinlineDir) ||
|
||||
construct<CompilerDirective>(inlineDir) ||
|
||||
construct<CompilerDirective>(
|
||||
many(construct<CompilerDirective::NameValue>(
|
||||
name, maybe(("="_tok || ":"_tok) >> digitString64))))) /
|
||||
|
||||
@@ -1867,6 +1867,13 @@ public:
|
||||
[&](const CompilerDirective::NoUnrollAndJam &) {
|
||||
Word("!DIR$ NOUNROLL_AND_JAM");
|
||||
},
|
||||
[&](const CompilerDirective::ForceInline &) {
|
||||
Word("!DIR$ FORCEINLINE");
|
||||
},
|
||||
[&](const CompilerDirective::Inline &) { Word("!DIR$ INLINE"); },
|
||||
[&](const CompilerDirective::NoInline &) {
|
||||
Word("!DIR$ NOINLINE");
|
||||
},
|
||||
[&](const CompilerDirective::Unrecognized &) {
|
||||
Word("!DIR$ ");
|
||||
Word(x.source.ToString());
|
||||
|
||||
@@ -60,7 +60,11 @@ 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::Inline>(dir.u) ||
|
||||
std::holds_alternative<parser::CompilerDirective::NoInline>(dir.u);
|
||||
}
|
||||
|
||||
void CanonicalizationOfDirectives::Post(parser::SpecificationPart &spec) {
|
||||
|
||||
@@ -10078,7 +10078,10 @@ 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::Inline>(x.u) ||
|
||||
std::holds_alternative<parser::CompilerDirective::NoInline>(x.u)) {
|
||||
return;
|
||||
}
|
||||
if (const auto *tkr{
|
||||
|
||||
69
flang/test/Integration/inline_directive.f90
Normal file
69
flang/test/Integration/inline_directive.f90
Normal file
@@ -0,0 +1,69 @@
|
||||
! This directory can be used to add Integration tests involving multiple stages of the compiler (for eg. from Fortran to LLVM IR).
|
||||
! It should not contain executable tests. We should only add tests here sparingly and only if there is no other way to test.
|
||||
! 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$ inline
|
||||
y = g(x)
|
||||
!dir$ inline
|
||||
call f(x, y)
|
||||
!CHECK: %[[VAL_10:.*]] = call i32 @_QFtest_inlinePg(ptr %[[VAL_3]]) #[[INLINE:.*]]
|
||||
!CHECK: store i32 %[[VAL_10]], ptr %[[VAL_2]], align 4
|
||||
!CHECK: call void @_QFtest_inlinePf(ptr %[[VAL_3]], ptr %[[VAL_2]]) #[[INLINE]]
|
||||
|
||||
!dir$ inline
|
||||
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]]) #[[INLINE]]
|
||||
enddo
|
||||
|
||||
!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 #[[INLINE]] = { inlinehint }
|
||||
!CHECK: attributes #[[NOINLINE]] = { noinline }
|
||||
61
flang/test/Lower/inline_directive.f90
Normal file
61
flang/test/Lower/inline_directive.f90
Normal file
@@ -0,0 +1,61 @@
|
||||
! 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> {inline_attr = #fir.inline_attrs<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> {inline_attr = #fir.inline_attrs<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> {inline_attr = #fir.inline_attrs<no_inline>} : (!fir.ref<i32>) -> i32
|
||||
!CHECK: %[[VAL_9:.*]] = fir.call @_QFtest_inlinePg(%[[VAL_3]]) fastmath<contract> {inline_attr = #fir.inline_attrs<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> {inline_attr = #fir.inline_attrs<no_inline>} : (!fir.ref<i32>, !fir.ref<i32>) -> ()
|
||||
|
||||
!dir$ inline
|
||||
call f(x, y)
|
||||
!CHECK: fir.call @_QFtest_inlinePf(%[[VAL_1]], %[[VAL_3]]) fastmath<contract> {inline_attr = #fir.inline_attrs<inline_hint>} : (!fir.ref<i32>, !fir.ref<i32>) -> ()
|
||||
|
||||
!dir$ forceinline
|
||||
do i = 1, 100
|
||||
!CHECK: fir.do_loop %[[ARG_0:.*]] = %[[FROM:.*]] to %[[TO:.*]] step %[[C1:.*]] iter_args(%[[ARG_1:.*]] = {{.*}}) -> (i32) {
|
||||
!CHECK: fir.call @_QFtest_inlinePf(%[[VAL_1]], %[[VAL_3]]) fastmath<contract> {inline_attr = #fir.inline_attrs<always_inline>} : (!fir.ref<i32>, !fir.ref<i32>) -> ()
|
||||
call f(x, y)
|
||||
enddo
|
||||
|
||||
!dir$ inline
|
||||
do i = 1, 100
|
||||
!CHECK: fir.do_loop %[[ARG_0:.*]] = %[[FROM:.*]] to %[[TO:.*]] step %[[C1:.*]] iter_args(%[[ARG_1:.*]] = {{.*}}) -> (i32) {
|
||||
!CHECK: fir.call @_QFtest_inlinePf(%[[VAL_1]], %[[VAL_3]]) fastmath<contract> {inline_attr = #fir.inline_attrs<inline_hint>} : (!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
|
||||
@@ -72,3 +72,27 @@ subroutine no_vector
|
||||
do i=1,10
|
||||
enddo
|
||||
end subroutine
|
||||
|
||||
subroutine inline
|
||||
integer :: a
|
||||
!dir$ forceinline
|
||||
! CHECK: !DIR$ FORCEINLINE
|
||||
a = f(2)
|
||||
|
||||
!dir$ inline
|
||||
! CHECK: !DIR$ INLINE
|
||||
call g()
|
||||
|
||||
!dir$ noinline
|
||||
! CHECK: !DIR$ NOINLINE
|
||||
call g()
|
||||
|
||||
contains
|
||||
function f(x)
|
||||
integer :: x
|
||||
f = x**2
|
||||
end function
|
||||
|
||||
subroutine g()
|
||||
end subroutine
|
||||
end subroutine
|
||||
|
||||
Reference in New Issue
Block a user