Skip to content

Commit 936c556

Browse files
authored
[flang][OpenMP] Handle REQUIRES ADMO in lowering (#144362)
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 2b4d757 commit 936c556

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
@@ -2722,58 +2722,129 @@ static mlir::IntegerAttr getAtomicHint(lower::AbstractConverter &converter,
27222722
return nullptr;
27232723
}
27242724

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

2798+
static std::optional<mlir::omp::ClauseMemoryOrderKind>
2799+
getAtomicMemoryOrder(semantics::SemanticsContext &semaCtx,
2800+
const List<Clause> &clauses,
2801+
const semantics::Scope &scope) {
27322802
for (const Clause &clause : clauses) {
2733-
switch (clause.id) {
2734-
case llvm::omp::Clause::OMPC_acq_rel:
2735-
kind = mlir::omp::ClauseMemoryOrderKind::Acq_rel;
2736-
break;
2737-
case llvm::omp::Clause::OMPC_acquire:
2738-
kind = mlir::omp::ClauseMemoryOrderKind::Acquire;
2739-
break;
2740-
case llvm::omp::Clause::OMPC_relaxed:
2741-
kind = mlir::omp::ClauseMemoryOrderKind::Relaxed;
2742-
break;
2743-
case llvm::omp::Clause::OMPC_release:
2744-
kind = mlir::omp::ClauseMemoryOrderKind::Release;
2745-
break;
2746-
case llvm::omp::Clause::OMPC_seq_cst:
2747-
kind = mlir::omp::ClauseMemoryOrderKind::Seq_cst;
2748-
break;
2749-
default:
2750-
break;
2751-
}
2803+
if (auto maybeKind = getMemoryOrderKind(clause.id))
2804+
return *maybeKind;
27522805
}
27532806

2754-
// Starting with 5.1, if no memory-order clause is present, the effect
2755-
// is as if "relaxed" was present.
2756-
if (!kind) {
2757-
if (version <= 50)
2758-
return nullptr;
2759-
kind = mlir::omp::ClauseMemoryOrderKind::Relaxed;
2807+
if (auto maybeKind = getMemoryOrderFromRequires(scope))
2808+
return *maybeKind;
2809+
2810+
return getDefaultAtomicMemOrder(semaCtx);
2811+
}
2812+
2813+
static mlir::omp::ClauseMemoryOrderKindAttr
2814+
makeMemOrderAttr(lower::AbstractConverter &converter,
2815+
std::optional<mlir::omp::ClauseMemoryOrderKind> maybeKind) {
2816+
if (maybeKind) {
2817+
return mlir::omp::ClauseMemoryOrderKindAttr::get(
2818+
converter.getFirOpBuilder().getContext(), *maybeKind);
27602819
}
2761-
fir::FirOpBuilder &builder = converter.getFirOpBuilder();
2762-
return mlir::omp::ClauseMemoryOrderKindAttr::get(builder.getContext(), *kind);
2820+
return nullptr;
27632821
}
27642822

27652823
static mlir::Operation * //
2766-
genAtomicRead(lower::AbstractConverter &converter, mlir::Location loc,
2824+
genAtomicRead(lower::AbstractConverter &converter,
2825+
semantics::SemanticsContext &semaCtx, mlir::Location loc,
27672826
lower::StatementContext &stmtCtx, mlir::Value atomAddr,
27682827
const semantics::SomeExpr &atom,
27692828
const evaluate::Assignment &assign, mlir::IntegerAttr hint,
2770-
mlir::omp::ClauseMemoryOrderKindAttr memOrder,
2829+
std::optional<mlir::omp::ClauseMemoryOrderKind> memOrder,
27712830
fir::FirOpBuilder::InsertPoint preAt,
27722831
fir::FirOpBuilder::InsertPoint atomicAt,
27732832
fir::FirOpBuilder::InsertPoint postAt) {
27742833
fir::FirOpBuilder &builder = converter.getFirOpBuilder();
27752834
builder.restoreInsertionPoint(preAt);
27762835

2836+
// If the atomic clause is read then the memory-order clause must
2837+
// not be release.
2838+
if (memOrder) {
2839+
if (*memOrder == mlir::omp::ClauseMemoryOrderKind::Release) {
2840+
// Reset it back to the default.
2841+
memOrder = getDefaultAtomicMemOrder(semaCtx);
2842+
} else if (*memOrder == mlir::omp::ClauseMemoryOrderKind::Acq_rel) {
2843+
// The MLIR verifier doesn't like acq_rel either.
2844+
memOrder = mlir::omp::ClauseMemoryOrderKind::Acquire;
2845+
}
2846+
}
2847+
27772848
mlir::Value storeAddr =
27782849
fir::getBase(converter.genExprAddr(assign.lhs, stmtCtx, &loc));
27792850
mlir::Type atomType = fir::unwrapRefType(atomAddr.getType());
@@ -2787,7 +2858,8 @@ genAtomicRead(lower::AbstractConverter &converter, mlir::Location loc,
27872858

27882859
builder.restoreInsertionPoint(atomicAt);
27892860
mlir::Operation *op = builder.create<mlir::omp::AtomicReadOp>(
2790-
loc, atomAddr, toAddr, mlir::TypeAttr::get(atomType), hint, memOrder);
2861+
loc, atomAddr, toAddr, mlir::TypeAttr::get(atomType), hint,
2862+
makeMemOrderAttr(converter, memOrder));
27912863

27922864
if (atomType != storeType) {
27932865
lower::ExprToValueMap overrides;
@@ -2808,34 +2880,48 @@ genAtomicRead(lower::AbstractConverter &converter, mlir::Location loc,
28082880
}
28092881

28102882
static mlir::Operation * //
2811-
genAtomicWrite(lower::AbstractConverter &converter, mlir::Location loc,
2883+
genAtomicWrite(lower::AbstractConverter &converter,
2884+
semantics::SemanticsContext &semaCtx, mlir::Location loc,
28122885
lower::StatementContext &stmtCtx, mlir::Value atomAddr,
28132886
const semantics::SomeExpr &atom,
28142887
const evaluate::Assignment &assign, mlir::IntegerAttr hint,
2815-
mlir::omp::ClauseMemoryOrderKindAttr memOrder,
2888+
std::optional<mlir::omp::ClauseMemoryOrderKind> memOrder,
28162889
fir::FirOpBuilder::InsertPoint preAt,
28172890
fir::FirOpBuilder::InsertPoint atomicAt,
28182891
fir::FirOpBuilder::InsertPoint postAt) {
28192892
fir::FirOpBuilder &builder = converter.getFirOpBuilder();
28202893
builder.restoreInsertionPoint(preAt);
28212894

2895+
// If the atomic clause is write then the memory-order clause must
2896+
// not be acquire.
2897+
if (memOrder) {
2898+
if (*memOrder == mlir::omp::ClauseMemoryOrderKind::Acquire) {
2899+
// Reset it back to the default.
2900+
memOrder = getDefaultAtomicMemOrder(semaCtx);
2901+
} else if (*memOrder == mlir::omp::ClauseMemoryOrderKind::Acq_rel) {
2902+
// The MLIR verifier doesn't like acq_rel either.
2903+
memOrder = mlir::omp::ClauseMemoryOrderKind::Release;
2904+
}
2905+
}
2906+
28222907
mlir::Value value =
28232908
fir::getBase(converter.genExprValue(assign.rhs, stmtCtx, &loc));
28242909
mlir::Type atomType = fir::unwrapRefType(atomAddr.getType());
28252910
mlir::Value converted = builder.createConvert(loc, atomType, value);
28262911

28272912
builder.restoreInsertionPoint(atomicAt);
28282913
mlir::Operation *op = builder.create<mlir::omp::AtomicWriteOp>(
2829-
loc, atomAddr, converted, hint, memOrder);
2914+
loc, atomAddr, converted, hint, makeMemOrderAttr(converter, memOrder));
28302915
return op;
28312916
}
28322917

28332918
static mlir::Operation *
2834-
genAtomicUpdate(lower::AbstractConverter &converter, mlir::Location loc,
2919+
genAtomicUpdate(lower::AbstractConverter &converter,
2920+
semantics::SemanticsContext &semaCtx, mlir::Location loc,
28352921
lower::StatementContext &stmtCtx, mlir::Value atomAddr,
28362922
const semantics::SomeExpr &atom,
28372923
const evaluate::Assignment &assign, mlir::IntegerAttr hint,
2838-
mlir::omp::ClauseMemoryOrderKindAttr memOrder,
2924+
std::optional<mlir::omp::ClauseMemoryOrderKind> memOrder,
28392925
fir::FirOpBuilder::InsertPoint preAt,
28402926
fir::FirOpBuilder::InsertPoint atomicAt,
28412927
fir::FirOpBuilder::InsertPoint postAt) {
@@ -2858,8 +2944,8 @@ genAtomicUpdate(lower::AbstractConverter &converter, mlir::Location loc,
28582944
}
28592945

28602946
builder.restoreInsertionPoint(atomicAt);
2861-
auto updateOp =
2862-
builder.create<mlir::omp::AtomicUpdateOp>(loc, atomAddr, hint, memOrder);
2947+
auto updateOp = builder.create<mlir::omp::AtomicUpdateOp>(
2948+
loc, atomAddr, hint, makeMemOrderAttr(converter, memOrder));
28632949

28642950
mlir::Region &region = updateOp->getRegion(0);
28652951
mlir::Block *block = builder.createBlock(&region, {}, {atomType}, {loc});
@@ -2878,11 +2964,12 @@ genAtomicUpdate(lower::AbstractConverter &converter, mlir::Location loc,
28782964
}
28792965

28802966
static mlir::Operation *
2881-
genAtomicOperation(lower::AbstractConverter &converter, mlir::Location loc,
2967+
genAtomicOperation(lower::AbstractConverter &converter,
2968+
semantics::SemanticsContext &semaCtx, mlir::Location loc,
28822969
lower::StatementContext &stmtCtx, int action,
28832970
mlir::Value atomAddr, const semantics::SomeExpr &atom,
28842971
const evaluate::Assignment &assign, mlir::IntegerAttr hint,
2885-
mlir::omp::ClauseMemoryOrderKindAttr memOrder,
2972+
std::optional<mlir::omp::ClauseMemoryOrderKind> memOrder,
28862973
fir::FirOpBuilder::InsertPoint preAt,
28872974
fir::FirOpBuilder::InsertPoint atomicAt,
28882975
fir::FirOpBuilder::InsertPoint postAt) {
@@ -2894,14 +2981,14 @@ genAtomicOperation(lower::AbstractConverter &converter, mlir::Location loc,
28942981
// builder's insertion point, or set it to anything specific.
28952982
switch (action) {
28962983
case parser::OpenMPAtomicConstruct::Analysis::Read:
2897-
return genAtomicRead(converter, loc, stmtCtx, atomAddr, atom, assign, hint,
2898-
memOrder, preAt, atomicAt, postAt);
2984+
return genAtomicRead(converter, semaCtx, loc, stmtCtx, atomAddr, atom,
2985+
assign, hint, memOrder, preAt, atomicAt, postAt);
28992986
case parser::OpenMPAtomicConstruct::Analysis::Write:
2900-
return genAtomicWrite(converter, loc, stmtCtx, atomAddr, atom, assign, hint,
2901-
memOrder, preAt, atomicAt, postAt);
2987+
return genAtomicWrite(converter, semaCtx, loc, stmtCtx, atomAddr, atom,
2988+
assign, hint, memOrder, preAt, atomicAt, postAt);
29022989
case parser::OpenMPAtomicConstruct::Analysis::Update:
2903-
return genAtomicUpdate(converter, loc, stmtCtx, atomAddr, atom, assign,
2904-
hint, memOrder, preAt, atomicAt, postAt);
2990+
return genAtomicUpdate(converter, semaCtx, loc, stmtCtx, atomAddr, atom,
2991+
assign, hint, memOrder, preAt, atomicAt, postAt);
29052992
default:
29062993
return nullptr;
29072994
}
@@ -3899,8 +3986,9 @@ static void genOMP(lower::AbstractConverter &converter, lower::SymMap &symTable,
38993986
mlir::Value atomAddr =
39003987
fir::getBase(converter.genExprAddr(atom, stmtCtx, &loc));
39013988
mlir::IntegerAttr hint = getAtomicHint(converter, clauses);
3902-
mlir::omp::ClauseMemoryOrderKindAttr memOrder =
3903-
getAtomicMemoryOrder(converter, semaCtx, clauses);
3989+
std::optional<mlir::omp::ClauseMemoryOrderKind> memOrder =
3990+
getAtomicMemoryOrder(semaCtx, clauses,
3991+
semaCtx.FindScope(construct.source));
39043992

39053993
if (auto *cond = get(analysis.cond)) {
39063994
(void)cond;
@@ -3918,8 +4006,8 @@ static void genOMP(lower::AbstractConverter &converter, lower::SymMap &symTable,
39184006
"Expexcing two actions");
39194007
(void)action0;
39204008
(void)action1;
3921-
captureOp =
3922-
builder.create<mlir::omp::AtomicCaptureOp>(loc, hint, memOrder);
4009+
captureOp = builder.create<mlir::omp::AtomicCaptureOp>(
4010+
loc, hint, makeMemOrderAttr(converter, memOrder));
39234011
// Set the non-atomic insertion point to before the atomic.capture.
39244012
preAt = getInsertionPointBefore(captureOp);
39254013

@@ -3931,7 +4019,7 @@ static void genOMP(lower::AbstractConverter &converter, lower::SymMap &symTable,
39314019
atomicAt = getInsertionPointBefore(term);
39324020
postAt = getInsertionPointAfter(captureOp);
39334021
hint = nullptr;
3934-
memOrder = nullptr;
4022+
memOrder = std::nullopt;
39354023
} else {
39364024
// Non-capturing operation.
39374025
assert(action0 != analysis.None && action1 == analysis.None &&
@@ -3943,16 +4031,16 @@ static void genOMP(lower::AbstractConverter &converter, lower::SymMap &symTable,
39434031
// The builder's insertion point needs to be specifically set before
39444032
// each call to `genAtomicOperation`.
39454033
mlir::Operation *firstOp = genAtomicOperation(
3946-
converter, loc, stmtCtx, analysis.op0.what, atomAddr, atom,
4034+
converter, semaCtx, loc, stmtCtx, analysis.op0.what, atomAddr, atom,
39474035
*get(analysis.op0.assign), hint, memOrder, preAt, atomicAt, postAt);
39484036
assert(firstOp && "Should have created an atomic operation");
39494037
atomicAt = getInsertionPointAfter(firstOp);
39504038

39514039
mlir::Operation *secondOp = nullptr;
39524040
if (analysis.op1.what != analysis.None) {
3953-
secondOp = genAtomicOperation(converter, loc, stmtCtx, analysis.op1.what,
3954-
atomAddr, atom, *get(analysis.op1.assign),
3955-
hint, memOrder, preAt, atomicAt, postAt);
4041+
secondOp = genAtomicOperation(
4042+
converter, semaCtx, loc, stmtCtx, analysis.op1.what, atomAddr, atom,
4043+
*get(analysis.op1.assign), hint, memOrder, preAt, atomicAt, postAt);
39564044
}
39574045

39584046
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 &) {
@@ -4056,6 +4072,9 @@ void OmpStructureChecker::CheckAtomicUpdate(
40564072
}
40574073

40584074
void OmpStructureChecker::Enter(const parser::OpenMPAtomicConstruct &x) {
4075+
if (visitedAtomicSource_.empty())
4076+
visitedAtomicSource_ = x.source;
4077+
40594078
// All of the following groups have the "exclusive" property, i.e. at
40604079
// most one clause from each group is allowed.
40614080
// 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)