-
Notifications
You must be signed in to change notification settings - Fork 14.4k
[flang] Implement !DIR$ [NO]INLINE and FORCEINLINE directives #134350
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
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -1828,6 +1828,26 @@ class FirConverter : public Fortran::lower::AbstractConverter { | |
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->set_alwaysInline(true); | ||
}, | ||
[&](const Fortran::parser::CompilerDirective::Inline &) { | ||
stmt.typedCall->set_inlineHint(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 +2073,50 @@ 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.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 | ||
|
@@ -2162,6 +2226,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 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why is there special handling for There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. For the moment, I've focused on do_loop, assignment and callstmt. And in |
||
for (IncrementLoopInfo &info : incrementLoopNestInfo) | ||
attachAttributesToDoLoopOperations(info.doLoop, doStmtEval.dirs); | ||
} | ||
|
||
/// Generate FIR to evaluate loop control values (lower, upper and step). | ||
|
@@ -2935,6 +3003,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 +3046,15 @@ 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::Inline &) { | ||
attachInliningDirectiveToStmt(dir, &eval); | ||
}, | ||
[&](const Fortran::parser::CompilerDirective::NoInline &) { | ||
attachInliningDirectiveToStmt(dir, &eval); | ||
}, | ||
[&](const auto &) {}}, | ||
dir.u); | ||
} | ||
|
@@ -4761,7 +4858,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(); | ||
|
||
|
@@ -4834,12 +4933,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) | ||
|
@@ -4907,16 +5016,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()) | ||
|
@@ -5321,7 +5435,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) { | ||
|
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.
nit to match style