Skip to content

Commit 73d4cea

Browse files
authored
[flang][OpenMP] Generalize isOpenMPPrivatizingConstruct (#148654)
Instead of treating all block and all loop constructs as privatizing, actually check if the construct allows a privatizing clause.
1 parent 2c0c87b commit 73d4cea

File tree

4 files changed

+61
-17
lines changed

4 files changed

+61
-17
lines changed

flang/lib/Lower/OpenMP/DataSharingProcessor.cpp

Lines changed: 45 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@
2626
#include "flang/Optimizer/HLFIR/HLFIROps.h"
2727
#include "flang/Semantics/attr.h"
2828
#include "flang/Semantics/tools.h"
29+
#include "llvm/ADT/Sequence.h"
30+
#include "llvm/ADT/SmallSet.h"
2931

3032
namespace Fortran {
3133
namespace lower {
@@ -49,7 +51,7 @@ DataSharingProcessor::DataSharingProcessor(
4951
firOpBuilder(converter.getFirOpBuilder()), clauses(clauses), eval(eval),
5052
shouldCollectPreDeterminedSymbols(shouldCollectPreDeterminedSymbols),
5153
useDelayedPrivatization(useDelayedPrivatization), symTable(symTable),
52-
visitor() {
54+
visitor(semaCtx) {
5355
eval.visit([&](const auto &functionParserNode) {
5456
parser::Walk(functionParserNode, visitor);
5557
});
@@ -424,24 +426,55 @@ getSource(const semantics::SemanticsContext &semaCtx,
424426
return source;
425427
}
426428

429+
static void collectPrivatizingConstructs(
430+
llvm::SmallSet<llvm::omp::Directive, 16> &constructs, unsigned version) {
431+
using Clause = llvm::omp::Clause;
432+
using Directive = llvm::omp::Directive;
433+
434+
static const Clause privatizingClauses[] = {
435+
Clause::OMPC_private,
436+
Clause::OMPC_lastprivate,
437+
Clause::OMPC_firstprivate,
438+
Clause::OMPC_in_reduction,
439+
Clause::OMPC_reduction,
440+
Clause::OMPC_linear,
441+
// TODO: Clause::OMPC_induction,
442+
Clause::OMPC_task_reduction,
443+
Clause::OMPC_detach,
444+
Clause::OMPC_use_device_ptr,
445+
Clause::OMPC_is_device_ptr,
446+
};
447+
448+
for (auto dir : llvm::enum_seq_inclusive<Directive>(Directive::First_,
449+
Directive::Last_)) {
450+
bool allowsPrivatizing = llvm::any_of(privatizingClauses, [&](Clause cls) {
451+
return llvm::omp::isAllowedClauseForDirective(dir, cls, version);
452+
});
453+
if (allowsPrivatizing)
454+
constructs.insert(dir);
455+
}
456+
}
457+
427458
bool DataSharingProcessor::isOpenMPPrivatizingConstruct(
428-
const parser::OpenMPConstruct &omp) {
429-
return common::visit(
430-
[](auto &&s) {
431-
using BareS = llvm::remove_cvref_t<decltype(s)>;
432-
return std::is_same_v<BareS, parser::OpenMPBlockConstruct> ||
433-
std::is_same_v<BareS, parser::OpenMPLoopConstruct> ||
434-
std::is_same_v<BareS, parser::OpenMPSectionsConstruct>;
435-
},
436-
omp.u);
459+
const parser::OpenMPConstruct &omp, unsigned version) {
460+
static llvm::SmallSet<llvm::omp::Directive, 16> privatizing;
461+
[[maybe_unused]] static bool init =
462+
(collectPrivatizingConstructs(privatizing, version), true);
463+
464+
// As of OpenMP 6.0, privatizing constructs (with the test being if they
465+
// allow a privatizing clause) are: dispatch, distribute, do, for, loop,
466+
// parallel, scope, sections, simd, single, target, target_data, task,
467+
// taskgroup, taskloop, and teams.
468+
return llvm::is_contained(privatizing, extractOmpDirective(omp));
437469
}
438470

439471
bool DataSharingProcessor::isOpenMPPrivatizingEvaluation(
440472
const pft::Evaluation &eval) const {
441-
return eval.visit([](auto &&s) {
473+
unsigned version = semaCtx.langOptions().OpenMPVersion;
474+
return eval.visit([=](auto &&s) {
442475
using BareS = llvm::remove_cvref_t<decltype(s)>;
443476
if constexpr (std::is_same_v<BareS, parser::OpenMPConstruct>) {
444-
return isOpenMPPrivatizingConstruct(s);
477+
return isOpenMPPrivatizingConstruct(s, version);
445478
} else {
446479
return false;
447480
}

flang/lib/Lower/OpenMP/DataSharingProcessor.h

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@ class DataSharingProcessor {
3636
/// at any point in time. This is used to track Symbol definition scopes in
3737
/// order to tell which OMP scope defined vs. references a certain Symbol.
3838
struct OMPConstructSymbolVisitor {
39+
OMPConstructSymbolVisitor(semantics::SemanticsContext &ctx)
40+
: version(ctx.langOptions().OpenMPVersion) {}
3941
template <typename T>
4042
bool Pre(const T &) {
4143
return true;
@@ -45,13 +47,13 @@ class DataSharingProcessor {
4547

4648
bool Pre(const parser::OpenMPConstruct &omp) {
4749
// Skip constructs that may not have privatizations.
48-
if (isOpenMPPrivatizingConstruct(omp))
50+
if (isOpenMPPrivatizingConstruct(omp, version))
4951
constructs.push_back(&omp);
5052
return true;
5153
}
5254

5355
void Post(const parser::OpenMPConstruct &omp) {
54-
if (isOpenMPPrivatizingConstruct(omp))
56+
if (isOpenMPPrivatizingConstruct(omp, version))
5557
constructs.pop_back();
5658
}
5759

@@ -68,6 +70,9 @@ class DataSharingProcessor {
6870
/// construct that defines symbol.
6971
bool isSymbolDefineBy(const semantics::Symbol *symbol,
7072
lower::pft::Evaluation &eval) const;
73+
74+
private:
75+
unsigned version;
7176
};
7277

7378
mlir::OpBuilder::InsertPoint lastPrivIP;
@@ -115,7 +120,8 @@ class DataSharingProcessor {
115120
mlir::OpBuilder::InsertPoint *lastPrivIP);
116121
void insertDeallocs();
117122

118-
static bool isOpenMPPrivatizingConstruct(const parser::OpenMPConstruct &omp);
123+
static bool isOpenMPPrivatizingConstruct(const parser::OpenMPConstruct &omp,
124+
unsigned version);
119125
bool isOpenMPPrivatizingEvaluation(const pft::Evaluation &eval) const;
120126

121127
public:

flang/test/Lower/OpenMP/taskgroup02.f90

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,9 @@
33
! Check that variables are not privatized twice when TASKGROUP is used.
44

55
!CHECK-LABEL: func.func @_QPsub() {
6-
!CHECK: omp.parallel {
7-
!CHECK: %[[PAR_I:.*]]:2 = hlfir.declare %{{.*}} {uniq_name = "_QFsubEi"}
6+
!CHECK: omp.parallel private(@_QFsubEi_private_i32 %[[SUB_I:.*]]#0 -> %[[ARG:.*]] : !fir.ref<i32>)
7+
!CHECK: %[[ALLOCA:.*]] = fir.alloca i32
8+
!CHECK: %[[PAR_I:.*]]:2 = hlfir.declare %[[ALLOCA]] {uniq_name = "_QFsubEi"}
89
!CHECK: omp.master {
910
!CHECK: omp.taskgroup {
1011
!CHECK-NEXT: omp.task private(@_QFsubEi_firstprivate_i32 %[[PAR_I]]#0 -> %[[TASK_I:.*]] : !fir.ref<i32>) {

llvm/include/llvm/Frontend/OpenMP/OMP.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,13 +51,17 @@ static constexpr inline bool canHaveIterator(Clause C) {
5151
// Can clause C create a private copy of a variable.
5252
static constexpr inline bool isPrivatizingClause(Clause C) {
5353
switch (C) {
54+
case OMPC_detach:
5455
case OMPC_firstprivate:
56+
// TODO case OMPC_induction:
5557
case OMPC_in_reduction:
58+
case OMPC_is_device_ptr:
5659
case OMPC_lastprivate:
5760
case OMPC_linear:
5861
case OMPC_private:
5962
case OMPC_reduction:
6063
case OMPC_task_reduction:
64+
case OMPC_use_device_ptr:
6165
return true;
6266
default:
6367
return false;

0 commit comments

Comments
 (0)