Skip to content

Commit af0e929

Browse files
committed
[flang][OpenMP] Handle REQUIRES ADMO in lowering
The previous approach rewrote the atomic constructs in the AST based on the REQUIRES ATOMIC_DEFAULT_MEM_ORDER directives. The new approach checks for incorrect uses of REQUIRED ADMO in the semantic analysis, and applies it in lowering, eliminating the need for a separate tree-rewriting procedure.
1 parent 01f9dff commit af0e929

File tree

12 files changed

+220
-500
lines changed

12 files changed

+220
-500
lines changed

flang/lib/Lower/OpenMP/OpenMP.cpp

Lines changed: 147 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -2715,58 +2715,129 @@ static mlir::IntegerAttr getAtomicHint(lower::AbstractConverter &converter,
27152715
return nullptr;
27162716
}
27172717

2718-
static mlir::omp::ClauseMemoryOrderKindAttr
2719-
getAtomicMemoryOrder(lower::AbstractConverter &converter,
2720-
semantics::SemanticsContext &semaCtx,
2721-
const List<Clause> &clauses) {
2722-
std::optional<mlir::omp::ClauseMemoryOrderKind> kind;
2718+
static mlir::omp::ClauseMemoryOrderKind
2719+
getMemoryOrderKind(common::OmpMemoryOrderType kind) {
2720+
switch (kind) {
2721+
case common::OmpMemoryOrderType::Acq_Rel:
2722+
return mlir::omp::ClauseMemoryOrderKind::Acq_rel;
2723+
case common::OmpMemoryOrderType::Acquire:
2724+
return mlir::omp::ClauseMemoryOrderKind::Acquire;
2725+
case common::OmpMemoryOrderType::Relaxed:
2726+
return mlir::omp::ClauseMemoryOrderKind::Relaxed;
2727+
case common::OmpMemoryOrderType::Release:
2728+
return mlir::omp::ClauseMemoryOrderKind::Release;
2729+
case common::OmpMemoryOrderType::Seq_Cst:
2730+
return mlir::omp::ClauseMemoryOrderKind::Seq_cst;
2731+
}
2732+
llvm_unreachable("Unexpected kind");
2733+
}
2734+
2735+
static std::optional<mlir::omp::ClauseMemoryOrderKind>
2736+
getMemoryOrderKind(llvm::omp::Clause clauseId) {
2737+
switch (clauseId) {
2738+
case llvm::omp::Clause::OMPC_acq_rel:
2739+
return mlir::omp::ClauseMemoryOrderKind::Acq_rel;
2740+
case llvm::omp::Clause::OMPC_acquire:
2741+
return mlir::omp::ClauseMemoryOrderKind::Acquire;
2742+
case llvm::omp::Clause::OMPC_relaxed:
2743+
return mlir::omp::ClauseMemoryOrderKind::Relaxed;
2744+
case llvm::omp::Clause::OMPC_release:
2745+
return mlir::omp::ClauseMemoryOrderKind::Release;
2746+
case llvm::omp::Clause::OMPC_seq_cst:
2747+
return mlir::omp::ClauseMemoryOrderKind::Seq_cst;
2748+
default:
2749+
return std::nullopt;
2750+
}
2751+
}
2752+
2753+
static std::optional<mlir::omp::ClauseMemoryOrderKind>
2754+
getMemoryOrderFromRequires(const semantics::Scope &scope) {
2755+
// The REQUIRES construct is only allowed in the main program scope
2756+
// and module scope, but seems like we also accept it in a subprogram
2757+
// scope.
2758+
// For safety, traverse all enclosing scopes and check if their symbol
2759+
// contains REQUIRES.
2760+
for (const auto *sc{&scope}; sc->kind() != semantics::Scope::Kind::Global;
2761+
sc = &sc->parent()) {
2762+
const semantics::Symbol *sym = sc->symbol();
2763+
if (!sym)
2764+
continue;
2765+
2766+
const common::OmpMemoryOrderType *admo = common::visit(
2767+
[](auto &&s) {
2768+
using WithOmpDeclarative = semantics::WithOmpDeclarative;
2769+
if constexpr (std::is_convertible_v<decltype(s),
2770+
const WithOmpDeclarative &>) {
2771+
return s.ompAtomicDefaultMemOrder();
2772+
}
2773+
return static_cast<const common::OmpMemoryOrderType *>(nullptr);
2774+
},
2775+
sym->details());
2776+
if (admo)
2777+
return getMemoryOrderKind(*admo);
2778+
}
2779+
2780+
return std::nullopt;
2781+
}
2782+
2783+
static std::optional<mlir::omp::ClauseMemoryOrderKind>
2784+
getDefaultAtomicMemOrder(semantics::SemanticsContext &semaCtx) {
27232785
unsigned version = semaCtx.langOptions().OpenMPVersion;
2786+
if (version > 50)
2787+
return mlir::omp::ClauseMemoryOrderKind::Relaxed;
2788+
return std::nullopt;
2789+
}
27242790

2791+
static std::optional<mlir::omp::ClauseMemoryOrderKind>
2792+
getAtomicMemoryOrder(semantics::SemanticsContext &semaCtx,
2793+
const List<Clause> &clauses,
2794+
const semantics::Scope &scope) {
27252795
for (const Clause &clause : clauses) {
2726-
switch (clause.id) {
2727-
case llvm::omp::Clause::OMPC_acq_rel:
2728-
kind = mlir::omp::ClauseMemoryOrderKind::Acq_rel;
2729-
break;
2730-
case llvm::omp::Clause::OMPC_acquire:
2731-
kind = mlir::omp::ClauseMemoryOrderKind::Acquire;
2732-
break;
2733-
case llvm::omp::Clause::OMPC_relaxed:
2734-
kind = mlir::omp::ClauseMemoryOrderKind::Relaxed;
2735-
break;
2736-
case llvm::omp::Clause::OMPC_release:
2737-
kind = mlir::omp::ClauseMemoryOrderKind::Release;
2738-
break;
2739-
case llvm::omp::Clause::OMPC_seq_cst:
2740-
kind = mlir::omp::ClauseMemoryOrderKind::Seq_cst;
2741-
break;
2742-
default:
2743-
break;
2744-
}
2796+
if (auto maybeKind = getMemoryOrderKind(clause.id))
2797+
return *maybeKind;
27452798
}
27462799

2747-
// Starting with 5.1, if no memory-order clause is present, the effect
2748-
// is as if "relaxed" was present.
2749-
if (!kind) {
2750-
if (version <= 50)
2751-
return nullptr;
2752-
kind = mlir::omp::ClauseMemoryOrderKind::Relaxed;
2800+
if (auto maybeKind = getMemoryOrderFromRequires(scope))
2801+
return *maybeKind;
2802+
2803+
return getDefaultAtomicMemOrder(semaCtx);
2804+
}
2805+
2806+
static mlir::omp::ClauseMemoryOrderKindAttr
2807+
makeMemOrderAttr(lower::AbstractConverter &converter,
2808+
std::optional<mlir::omp::ClauseMemoryOrderKind> maybeKind) {
2809+
if (maybeKind) {
2810+
return mlir::omp::ClauseMemoryOrderKindAttr::get(
2811+
converter.getFirOpBuilder().getContext(), *maybeKind);
27532812
}
2754-
fir::FirOpBuilder &builder = converter.getFirOpBuilder();
2755-
return mlir::omp::ClauseMemoryOrderKindAttr::get(builder.getContext(), *kind);
2813+
return nullptr;
27562814
}
27572815

27582816
static mlir::Operation * //
2759-
genAtomicRead(lower::AbstractConverter &converter, mlir::Location loc,
2817+
genAtomicRead(lower::AbstractConverter &converter,
2818+
semantics::SemanticsContext &semaCtx, mlir::Location loc,
27602819
lower::StatementContext &stmtCtx, mlir::Value atomAddr,
27612820
const semantics::SomeExpr &atom,
27622821
const evaluate::Assignment &assign, mlir::IntegerAttr hint,
2763-
mlir::omp::ClauseMemoryOrderKindAttr memOrder,
2822+
std::optional<mlir::omp::ClauseMemoryOrderKind> memOrder,
27642823
fir::FirOpBuilder::InsertPoint preAt,
27652824
fir::FirOpBuilder::InsertPoint atomicAt,
27662825
fir::FirOpBuilder::InsertPoint postAt) {
27672826
fir::FirOpBuilder &builder = converter.getFirOpBuilder();
27682827
builder.restoreInsertionPoint(preAt);
27692828

2829+
// If the atomic clause is read then the memory-order clause must
2830+
// not be release.
2831+
if (memOrder) {
2832+
if (*memOrder == mlir::omp::ClauseMemoryOrderKind::Release) {
2833+
// Reset it back to the default.
2834+
memOrder = getDefaultAtomicMemOrder(semaCtx);
2835+
} else if (*memOrder == mlir::omp::ClauseMemoryOrderKind::Acq_rel) {
2836+
// The MLIR verifier doesn't like acq_rel either.
2837+
memOrder = mlir::omp::ClauseMemoryOrderKind::Acquire;
2838+
}
2839+
}
2840+
27702841
mlir::Value storeAddr =
27712842
fir::getBase(converter.genExprAddr(assign.lhs, stmtCtx, &loc));
27722843
mlir::Type atomType = fir::unwrapRefType(atomAddr.getType());
@@ -2780,7 +2851,8 @@ genAtomicRead(lower::AbstractConverter &converter, mlir::Location loc,
27802851

27812852
builder.restoreInsertionPoint(atomicAt);
27822853
mlir::Operation *op = builder.create<mlir::omp::AtomicReadOp>(
2783-
loc, atomAddr, toAddr, mlir::TypeAttr::get(atomType), hint, memOrder);
2854+
loc, atomAddr, toAddr, mlir::TypeAttr::get(atomType), hint,
2855+
makeMemOrderAttr(converter, memOrder));
27842856

27852857
if (atomType != storeType) {
27862858
lower::ExprToValueMap overrides;
@@ -2801,34 +2873,48 @@ genAtomicRead(lower::AbstractConverter &converter, mlir::Location loc,
28012873
}
28022874

28032875
static mlir::Operation * //
2804-
genAtomicWrite(lower::AbstractConverter &converter, mlir::Location loc,
2876+
genAtomicWrite(lower::AbstractConverter &converter,
2877+
semantics::SemanticsContext &semaCtx, mlir::Location loc,
28052878
lower::StatementContext &stmtCtx, mlir::Value atomAddr,
28062879
const semantics::SomeExpr &atom,
28072880
const evaluate::Assignment &assign, mlir::IntegerAttr hint,
2808-
mlir::omp::ClauseMemoryOrderKindAttr memOrder,
2881+
std::optional<mlir::omp::ClauseMemoryOrderKind> memOrder,
28092882
fir::FirOpBuilder::InsertPoint preAt,
28102883
fir::FirOpBuilder::InsertPoint atomicAt,
28112884
fir::FirOpBuilder::InsertPoint postAt) {
28122885
fir::FirOpBuilder &builder = converter.getFirOpBuilder();
28132886
builder.restoreInsertionPoint(preAt);
28142887

2888+
// If the atomic clause is write then the memory-order clause must
2889+
// not be acquire.
2890+
if (memOrder) {
2891+
if (*memOrder == mlir::omp::ClauseMemoryOrderKind::Acquire) {
2892+
// Reset it back to the default.
2893+
memOrder = getDefaultAtomicMemOrder(semaCtx);
2894+
} else if (*memOrder == mlir::omp::ClauseMemoryOrderKind::Acq_rel) {
2895+
// The MLIR verifier doesn't like acq_rel either.
2896+
memOrder = mlir::omp::ClauseMemoryOrderKind::Release;
2897+
}
2898+
}
2899+
28152900
mlir::Value value =
28162901
fir::getBase(converter.genExprValue(assign.rhs, stmtCtx, &loc));
28172902
mlir::Type atomType = fir::unwrapRefType(atomAddr.getType());
28182903
mlir::Value converted = builder.createConvert(loc, atomType, value);
28192904

28202905
builder.restoreInsertionPoint(atomicAt);
28212906
mlir::Operation *op = builder.create<mlir::omp::AtomicWriteOp>(
2822-
loc, atomAddr, converted, hint, memOrder);
2907+
loc, atomAddr, converted, hint, makeMemOrderAttr(converter, memOrder));
28232908
return op;
28242909
}
28252910

28262911
static mlir::Operation *
2827-
genAtomicUpdate(lower::AbstractConverter &converter, mlir::Location loc,
2912+
genAtomicUpdate(lower::AbstractConverter &converter,
2913+
semantics::SemanticsContext &semaCtx, mlir::Location loc,
28282914
lower::StatementContext &stmtCtx, mlir::Value atomAddr,
28292915
const semantics::SomeExpr &atom,
28302916
const evaluate::Assignment &assign, mlir::IntegerAttr hint,
2831-
mlir::omp::ClauseMemoryOrderKindAttr memOrder,
2917+
std::optional<mlir::omp::ClauseMemoryOrderKind> memOrder,
28322918
fir::FirOpBuilder::InsertPoint preAt,
28332919
fir::FirOpBuilder::InsertPoint atomicAt,
28342920
fir::FirOpBuilder::InsertPoint postAt) {
@@ -2851,8 +2937,8 @@ genAtomicUpdate(lower::AbstractConverter &converter, mlir::Location loc,
28512937
}
28522938

28532939
builder.restoreInsertionPoint(atomicAt);
2854-
auto updateOp =
2855-
builder.create<mlir::omp::AtomicUpdateOp>(loc, atomAddr, hint, memOrder);
2940+
auto updateOp = builder.create<mlir::omp::AtomicUpdateOp>(
2941+
loc, atomAddr, hint, makeMemOrderAttr(converter, memOrder));
28562942

28572943
mlir::Region &region = updateOp->getRegion(0);
28582944
mlir::Block *block = builder.createBlock(&region, {}, {atomType}, {loc});
@@ -2871,11 +2957,12 @@ genAtomicUpdate(lower::AbstractConverter &converter, mlir::Location loc,
28712957
}
28722958

28732959
static mlir::Operation *
2874-
genAtomicOperation(lower::AbstractConverter &converter, mlir::Location loc,
2960+
genAtomicOperation(lower::AbstractConverter &converter,
2961+
semantics::SemanticsContext &semaCtx, mlir::Location loc,
28752962
lower::StatementContext &stmtCtx, int action,
28762963
mlir::Value atomAddr, const semantics::SomeExpr &atom,
28772964
const evaluate::Assignment &assign, mlir::IntegerAttr hint,
2878-
mlir::omp::ClauseMemoryOrderKindAttr memOrder,
2965+
std::optional<mlir::omp::ClauseMemoryOrderKind> memOrder,
28792966
fir::FirOpBuilder::InsertPoint preAt,
28802967
fir::FirOpBuilder::InsertPoint atomicAt,
28812968
fir::FirOpBuilder::InsertPoint postAt) {
@@ -2887,14 +2974,14 @@ genAtomicOperation(lower::AbstractConverter &converter, mlir::Location loc,
28872974
// builder's insertion point, or set it to anything specific.
28882975
switch (action) {
28892976
case parser::OpenMPAtomicConstruct::Analysis::Read:
2890-
return genAtomicRead(converter, loc, stmtCtx, atomAddr, atom, assign, hint,
2891-
memOrder, preAt, atomicAt, postAt);
2977+
return genAtomicRead(converter, semaCtx, loc, stmtCtx, atomAddr, atom,
2978+
assign, hint, memOrder, preAt, atomicAt, postAt);
28922979
case parser::OpenMPAtomicConstruct::Analysis::Write:
2893-
return genAtomicWrite(converter, loc, stmtCtx, atomAddr, atom, assign, hint,
2894-
memOrder, preAt, atomicAt, postAt);
2980+
return genAtomicWrite(converter, semaCtx, loc, stmtCtx, atomAddr, atom,
2981+
assign, hint, memOrder, preAt, atomicAt, postAt);
28952982
case parser::OpenMPAtomicConstruct::Analysis::Update:
2896-
return genAtomicUpdate(converter, loc, stmtCtx, atomAddr, atom, assign,
2897-
hint, memOrder, preAt, atomicAt, postAt);
2983+
return genAtomicUpdate(converter, semaCtx, loc, stmtCtx, atomAddr, atom,
2984+
assign, hint, memOrder, preAt, atomicAt, postAt);
28982985
default:
28992986
return nullptr;
29002987
}
@@ -3894,8 +3981,9 @@ static void genOMP(lower::AbstractConverter &converter, lower::SymMap &symTable,
38943981
mlir::Value atomAddr =
38953982
fir::getBase(converter.genExprAddr(atom, stmtCtx, &loc));
38963983
mlir::IntegerAttr hint = getAtomicHint(converter, clauses);
3897-
mlir::omp::ClauseMemoryOrderKindAttr memOrder =
3898-
getAtomicMemoryOrder(converter, semaCtx, clauses);
3984+
std::optional<mlir::omp::ClauseMemoryOrderKind> memOrder =
3985+
getAtomicMemoryOrder(semaCtx, clauses,
3986+
semaCtx.FindScope(construct.source));
38993987

39003988
if (auto *cond = get(analysis.cond)) {
39013989
(void)cond;
@@ -3913,8 +4001,8 @@ static void genOMP(lower::AbstractConverter &converter, lower::SymMap &symTable,
39134001
"Expexcing two actions");
39144002
(void)action0;
39154003
(void)action1;
3916-
captureOp =
3917-
builder.create<mlir::omp::AtomicCaptureOp>(loc, hint, memOrder);
4004+
captureOp = builder.create<mlir::omp::AtomicCaptureOp>(
4005+
loc, hint, makeMemOrderAttr(converter, memOrder));
39184006
// Set the non-atomic insertion point to before the atomic.capture.
39194007
preAt = getInsertionPointBefore(captureOp);
39204008

@@ -3926,7 +4014,7 @@ static void genOMP(lower::AbstractConverter &converter, lower::SymMap &symTable,
39264014
atomicAt = getInsertionPointBefore(term);
39274015
postAt = getInsertionPointAfter(captureOp);
39284016
hint = nullptr;
3929-
memOrder = nullptr;
4017+
memOrder = std::nullopt;
39304018
} else {
39314019
// Non-capturing operation.
39324020
assert(action0 != analysis.None && action1 == analysis.None &&
@@ -3938,16 +4026,16 @@ static void genOMP(lower::AbstractConverter &converter, lower::SymMap &symTable,
39384026
// The builder's insertion point needs to be specifically set before
39394027
// each call to `genAtomicOperation`.
39404028
mlir::Operation *firstOp = genAtomicOperation(
3941-
converter, loc, stmtCtx, analysis.op0.what, atomAddr, atom,
4029+
converter, semaCtx, loc, stmtCtx, analysis.op0.what, atomAddr, atom,
39424030
*get(analysis.op0.assign), hint, memOrder, preAt, atomicAt, postAt);
39434031
assert(firstOp && "Should have created an atomic operation");
39444032
atomicAt = getInsertionPointAfter(firstOp);
39454033

39464034
mlir::Operation *secondOp = nullptr;
39474035
if (analysis.op1.what != analysis.None) {
3948-
secondOp = genAtomicOperation(converter, loc, stmtCtx, analysis.op1.what,
3949-
atomAddr, atom, *get(analysis.op1.assign),
3950-
hint, memOrder, preAt, atomicAt, postAt);
4036+
secondOp = genAtomicOperation(
4037+
converter, semaCtx, loc, stmtCtx, analysis.op1.what, atomAddr, atom,
4038+
*get(analysis.op1.assign), hint, memOrder, preAt, atomicAt, postAt);
39514039
}
39524040

39534041
if (construct.IsCapture()) {

flang/lib/Semantics/CMakeLists.txt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,6 @@ add_flang_library(FortranSemantics
4040
resolve-directives.cpp
4141
resolve-names-utils.cpp
4242
resolve-names.cpp
43-
rewrite-directives.cpp
4443
rewrite-parse-tree.cpp
4544
runtime-type-info.cpp
4645
scope.cpp

flang/lib/Semantics/check-omp-structure.cpp

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1719,6 +1719,22 @@ void OmpStructureChecker::Leave(const parser::OpenMPDepobjConstruct &x) {
17191719
void OmpStructureChecker::Enter(const parser::OpenMPRequiresConstruct &x) {
17201720
const auto &dir{std::get<parser::Verbatim>(x.t)};
17211721
PushContextAndClauseSets(dir.source, llvm::omp::Directive::OMPD_requires);
1722+
1723+
if (visitedAtomicSource_.empty()) {
1724+
return;
1725+
}
1726+
const auto &clauseList{std::get<parser::OmpClauseList>(x.t)};
1727+
for (const parser::OmpClause &clause : clauseList.v) {
1728+
llvm::omp::Clause id{clause.Id()};
1729+
if (id == llvm::omp::Clause::OMPC_atomic_default_mem_order) {
1730+
parser::MessageFormattedText txt(
1731+
"REQUIRES directive with '%s' clause found lexically after atomic operation without a memory order clause"_err_en_US,
1732+
parser::ToUpperCaseLetters(llvm::omp::getOpenMPClauseName(id)));
1733+
parser::Message message(clause.source, txt);
1734+
message.Attach(visitedAtomicSource_, "Previous atomic construct"_en_US);
1735+
context_.Say(std::move(message));
1736+
}
1737+
}
17221738
}
17231739

17241740
void OmpStructureChecker::Leave(const parser::OpenMPRequiresConstruct &) {
@@ -4028,6 +4044,9 @@ void OmpStructureChecker::CheckAtomicUpdate(
40284044
}
40294045

40304046
void OmpStructureChecker::Enter(const parser::OpenMPAtomicConstruct &x) {
4047+
if (visitedAtomicSource_.empty())
4048+
visitedAtomicSource_ = x.source;
4049+
40314050
// All of the following groups have the "exclusive" property, i.e. at
40324051
// most one clause from each group is allowed.
40334052
// The exclusivity-checking code should eventually be unified for all

flang/lib/Semantics/check-omp-structure.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -360,6 +360,7 @@ class OmpStructureChecker
360360
};
361361
int directiveNest_[LastType + 1] = {0};
362362

363+
parser::CharBlock visitedAtomicSource_;
363364
SymbolSourceMap deferredNonVariables_;
364365

365366
using LoopConstruct = std::variant<const parser::DoConstruct *,

0 commit comments

Comments
 (0)