[flang-commits] [flang] [flang][OpenMP] Support omx/ompx extension sentinels (PR #218475)
CHANDRA GHALE via flang-commits
flang-commits at lists.llvm.org
Wed Aug 26 05:14:12 PDT 2026
https://github.com/chandraghale updated https://github.com/llvm/llvm-project/pull/218475
>From 01a82a1316fd971e6f279aaffad1225dcdedaa82 Mon Sep 17 00:00:00 2001
From: Chandra Ghale <ghale at pe34genoa.hpc.amslabs.hpecorp.net>
Date: Mon, 24 Aug 2026 12:52:50 -0500
Subject: [PATCH 1/3] Support omx/ompx extension sentinels
---
flang/docs/ReleaseNotes.md | 5 +++
flang/include/flang/Parser/parse-tree.h | 12 ++++-
flang/lib/Parser/openmp-parsers.cpp | 28 ++++++++++--
flang/lib/Parser/parsing.cpp | 25 ++++++++---
flang/lib/Parser/prescan.h | 7 ++-
flang/lib/Parser/unparse.cpp | 2 +-
flang/lib/Semantics/check-omp-structure.cpp | 18 +++++++-
.../OpenMP/sentinel-extension-ignored.f90 | 34 ++++++++++++++
flang/test/Parser/OpenMP/sentinel-ompx.f90 | 28 ++++++++++++
flang/test/Parser/OpenMP/sentinel-omx.f | 45 +++++++++++++++++++
10 files changed, 189 insertions(+), 15 deletions(-)
create mode 100644 flang/test/Parser/OpenMP/sentinel-extension-ignored.f90
create mode 100644 flang/test/Parser/OpenMP/sentinel-ompx.f90
create mode 100644 flang/test/Parser/OpenMP/sentinel-omx.f
diff --git a/flang/docs/ReleaseNotes.md b/flang/docs/ReleaseNotes.md
index 0ccb34be20571..ce6b987f7f6e4 100644
--- a/flang/docs/ReleaseNotes.md
+++ b/flang/docs/ReleaseNotes.md
@@ -33,6 +33,11 @@ page](https://llvm.org/releases/).
## Non-comprehensive list of changes in this release
+- Added support for the OpenMP implementation-defined extension sentinels
+ (OpenMP 5.2, section 3.1): `!$omx`, `c$omx` and `*$omx` in fixed source form
+ and `!$ompx` in free source form. These sentinels are recognized like their
+ `omp` counterparts when OpenMP is enabled.
+
## New Compiler Flags
- The warning flags with prefixes -Wopen-mp and -Wopen-acc have been deprecated in favor of corrected spellings with the respective prefixes -Wopenmp and -Wopenacc. Removal of the deprecated options is planned for LLVM 25 (July 2027).
diff --git a/flang/include/flang/Parser/parse-tree.h b/flang/include/flang/Parser/parse-tree.h
index ee6288539395c..dfb35967a353d 100644
--- a/flang/include/flang/Parser/parse-tree.h
+++ b/flang/include/flang/Parser/parse-tree.h
@@ -5530,9 +5530,19 @@ struct OpenMPMisplacedEndDirective : public OmpEndDirective {
OpenMPMisplacedEndDirective, OmpEndDirective);
};
-// Unrecognized string after the !$OMP sentinel.
+// Unrecognized string after an OpenMP sentinel. isExtensionSentinel is true
+// when the sentinel was an implementation-defined extension sentinel (!$omx in
+// fixed source form or !$ompx in free source form, OpenMP 5.2 section 3.1); in
+// that case an unrecognized directive is ignored with a warning instead of
+// being reported as an error. The bool constructor is explicit so that the
+// node is not implicitly constructible from a bool: such a conversion would
+// make parse-tree visitors that provide a catch-all handler ambiguous.
struct OpenMPInvalidDirective {
using EmptyTrait = std::true_type;
+ OpenMPInvalidDirective() = default;
+ explicit OpenMPInvalidDirective(bool isExtension)
+ : isExtensionSentinel{isExtension} {}
+ bool isExtensionSentinel{false};
CharBlock source;
};
diff --git a/flang/lib/Parser/openmp-parsers.cpp b/flang/lib/Parser/openmp-parsers.cpp
index 7016c688a572d..1df495d08a793 100644
--- a/flang/lib/Parser/openmp-parsers.cpp
+++ b/flang/lib/Parser/openmp-parsers.cpp
@@ -52,7 +52,18 @@ using DirectiveSet =
state.GetLocation(), std::min<size_t>(64, state.BytesRemaining()));
}
-constexpr auto startOmpLine = skipStuffBeforeStatement >> "!$OMP "_sptok;
+// Accept the standard "!$omp" sentinel as well as the implementation-defined
+// extension sentinels "!$omx" (fixed form) and "!$ompx" (free form) defined in
+// OpenMP 5.2, section 3.1. The prescanner normalizes the comment character of
+// fixed-form sentinels (c$omx, *$omx) to '!'. The sentinels are matched with
+// the "_id" literal (not "_sptok") so that a sentinel is not accepted as a
+// prefix of a longer one: "!$OMP" must not match the "!$omp" that begins
+// "!$ompx". The "_id" match fails when the token is immediately followed by
+// another identifier character.
+constexpr auto ompxSentinel = "!$OMPX"_id || "!$OMX"_id;
+constexpr auto ompSentinel = "!$OMP"_id;
+constexpr auto startOmpLine =
+ skipStuffBeforeStatement >> (ompxSentinel || ompSentinel);
constexpr auto endOmpLine = space >> endOfLine;
constexpr auto logicalConstantExpr{logical(constantExpr)};
@@ -2632,7 +2643,16 @@ static constexpr DirectiveSet GetAllDirectives() { //
TYPE_PARSER(construct<OpenMPMisplacedEndDirective>(
OmpEndDirectiveParser{GetAllDirectives()}))
-TYPE_PARSER(startOmpLine >>
- sourced(construct<OpenMPInvalidDirective>(
- maybe("BEGIN"_sptok) >> !OmpDirectiveNameParser{} >> SkipTo<'\n'>{})))
+// A string following an OpenMP sentinel that is not a recognized directive.
+// When it follows an implementation-defined extension sentinel (!$omx / !$ompx)
+// the node is flagged (true) so that semantics ignores it with a warning rather
+// than reporting an error, allowing portable use of vendor extensions
+// (OpenMP 5.2, section 3.1).
+TYPE_PARSER(skipStuffBeforeStatement >>
+ ((ompxSentinel >>
+ sourced(construct<OpenMPInvalidDirective>(maybe("BEGIN"_sptok) >>
+ !OmpDirectiveNameParser{} >> SkipTo<'\n'>{} >> pure(true)))) ||
+ (ompSentinel >>
+ sourced(construct<OpenMPInvalidDirective>(maybe("BEGIN"_sptok) >>
+ !OmpDirectiveNameParser{} >> SkipTo<'\n'>{} >> pure(false))))))
} // namespace Fortran::parser
diff --git a/flang/lib/Parser/parsing.cpp b/flang/lib/Parser/parsing.cpp
index 667d8d9297ecb..964e4b01e1b89 100644
--- a/flang/lib/Parser/parsing.cpp
+++ b/flang/lib/Parser/parsing.cpp
@@ -90,6 +90,10 @@ const SourceFile *Parsing::Prescan(const std::string &path, Options options) {
if (options.features.IsEnabled(LanguageFeature::OpenMP) ||
(options.prescanAndReformat && noneOfTheAbove)) {
prescanner.AddCompilerDirectiveSentinel("$omp");
+ // Implementation-defined extension sentinels (OpenMP 5.2, section 3.1):
+ // "$omx" in fixed form (!$omx, c$omx, *$omx) and "$ompx" in free form.
+ prescanner.AddCompilerDirectiveSentinel("$omx");
+ prescanner.AddCompilerDirectiveSentinel("$ompx");
prescanner.AddCompilerDirectiveSentinel("$"); // OMP conditional line
}
if (options.features.IsEnabled(LanguageFeature::CUDA) ||
@@ -132,12 +136,13 @@ void Parsing::EmitPreprocessedSource(
bool inContinuation{false};
bool lineWasBlankBefore{true};
const AllSources &allSources{allCooked().allSources()};
- // All directives that flang supports are known to have a length of 4 chars,
- // except for OpenMP conditional compilation lines (!$).
- constexpr int directiveNameLength{4};
// We need to know the current directive in order to provide correct
- // continuation for the directive
+ // continuation for the directive. The sentinel is accumulated until the
+ // blank that follows it, so sentinels of any length are handled: the
+ // 4-character sentinels (e.g. "$omp", "$omx", "$acc") as well as the
+ // 5-character implementation-defined extension sentinel "$ompx".
std::string directive;
+ bool inDirectiveSentinelRegion{false};
for (const char &atChar : cooked().AsCharBlock()) {
char ch{atChar};
if (ch == '\n') {
@@ -149,6 +154,7 @@ void Parsing::EmitPreprocessedSource(
lineWasBlankBefore = true;
++sourceLine;
directive.clear();
+ inDirectiveSentinelRegion = false;
} else {
auto provenance{cooked().GetProvenanceRange(CharBlock{&atChar, 1})};
@@ -172,13 +178,18 @@ void Parsing::EmitPreprocessedSource(
// which signifies a comment (directive) in both source forms.
inDirective = true;
inDirectiveSentinel = true;
+ inDirectiveSentinelRegion = true;
} else if (inDirective && !ompConditionalLine &&
- directive.size() < directiveNameLength) {
+ inDirectiveSentinelRegion) {
if (IsLetter(ch) || ch == '$' || ch == '@') {
directive += getOriginalChar(ch);
inDirectiveSentinel = true;
- } else if (directive == "$"s) {
- ompConditionalLine = true;
+ } else {
+ // The blank that terminates the sentinel has been reached.
+ inDirectiveSentinelRegion = false;
+ if (directive == "$"s) {
+ ompConditionalLine = true;
+ }
}
}
diff --git a/flang/lib/Parser/prescan.h b/flang/lib/Parser/prescan.h
index 8f4f390d4ea37..42311cd128f4d 100644
--- a/flang/lib/Parser/prescan.h
+++ b/flang/lib/Parser/prescan.h
@@ -198,7 +198,12 @@ class Prescanner {
return InOpenMPConditionalLine() || InOpenACCOrCUDAConditionalLine();
}
bool IsOpenMPDirective() const {
- return directiveSentinel_ && std::strcmp(directiveSentinel_, "$omp") == 0;
+ return directiveSentinel_ &&
+ (std::strcmp(directiveSentinel_, "$omp") == 0 ||
+ // Implementation-defined extension sentinels (OpenMP 5.2, 3.1):
+ // "$omx" (fixed form) and "$ompx" (free form).
+ std::strcmp(directiveSentinel_, "$omx") == 0 ||
+ std::strcmp(directiveSentinel_, "$ompx") == 0);
}
bool InFixedFormSource() const {
return inFixedForm_ && !inPreprocessorDirective_ && !InCompilerDirective();
diff --git a/flang/lib/Parser/unparse.cpp b/flang/lib/Parser/unparse.cpp
index 42f042e470e81..b79679ca4500a 100644
--- a/flang/lib/Parser/unparse.cpp
+++ b/flang/lib/Parser/unparse.cpp
@@ -2758,7 +2758,7 @@ class UnparseVisitor {
}
void Unparse(const OpenMPInvalidDirective &x) {
BeginOpenMP();
- Word("!$OMP ");
+ Word(x.isExtensionSentinel ? "!$OMPX " : "!$OMP ");
Put(parser::ToUpperCaseLetters(x.source.ToString()));
Put("\n");
EndOpenMP();
diff --git a/flang/lib/Semantics/check-omp-structure.cpp b/flang/lib/Semantics/check-omp-structure.cpp
index d87b2f1983de6..6fa2effffe7b0 100644
--- a/flang/lib/Semantics/check-omp-structure.cpp
+++ b/flang/lib/Semantics/check-omp-structure.cpp
@@ -5798,7 +5798,23 @@ void OmpStructureChecker::Leave(const parser::OpenMPMisplacedEndDirective &x) {
}
void OmpStructureChecker::Enter(const parser::OpenMPInvalidDirective &x) {
- context_.Say(x.source, "Invalid OpenMP directive"_err_en_US);
+ if (x.isExtensionSentinel) {
+ // A directive following an implementation-defined extension sentinel
+ // (!$omx / !$ompx, OpenMP 5.2 section 3.1) that is not recognized is
+ // ignored with a warning, so that programs using vendor extensions remain
+ // portable to implementations that do not support them. Use Say() rather
+ // than Warn() here because the latter resolves the enclosing scope of the
+ // source location, which may not exist for a directive that appears before
+ // the first statement of an otherwise empty program unit.
+ if (context_.ShouldWarn(common::UsageWarning::IgnoredDirective)) {
+ context_
+ .Say(x.source,
+ "Unrecognized OpenMP extension directive was ignored"_warn_en_US)
+ .set_usageWarning(common::UsageWarning::IgnoredDirective);
+ }
+ } else {
+ context_.Say(x.source, "Invalid OpenMP directive"_err_en_US);
+ }
PushContextAndClauseSets(x.source, llvm::omp::Directive::OMPD_unknown);
}
diff --git a/flang/test/Parser/OpenMP/sentinel-extension-ignored.f90 b/flang/test/Parser/OpenMP/sentinel-extension-ignored.f90
new file mode 100644
index 0000000000000..fe2fd9b0ea8d9
--- /dev/null
+++ b/flang/test/Parser/OpenMP/sentinel-extension-ignored.f90
@@ -0,0 +1,34 @@
+! An implementation-defined extension sentinel (!$omx in fixed form, !$ompx in
+! free form; OpenMP 5.2, section 3.1) that is followed by a directive which the
+! implementation does not recognize is ignored with a warning rather than being
+! reported as an error. This keeps programs that use vendor extensions portable
+! to implementations that do not support them.
+
+! RUN: %flang_fc1 -fopenmp -fdebug-unparse %s 2>&1 | FileCheck %s
+
+! Diagnostics are emitted before the unparsed program. An unrecognized
+! extension directive produces a warning, not an error.
+! CHECK: warning: Unrecognized OpenMP extension directive was ignored
+! CHECK-SAME: [-Wignored-directive]
+! Regression: an unrecognized extension directive that appears before the first
+! statement of a program unit must warn without crashing (the source location of
+! the ignored directive lies outside the range of any statement scope).
+! CHECK: warning: Unrecognized OpenMP extension directive was ignored
+
+! The unrecognized extension directives are ignored but unparsed with the
+! extension sentinel preserved.
+! CHECK: SUBROUTINE ompx_unrecognized
+! CHECK: !$OMPX SOME_VENDOR_DIRECTIVE
+! CHECK: END SUBROUTINE
+! CHECK: !$OMPX ANOTHER_VENDOR_DIRECTIVE
+! CHECK: END PROGRAM
+
+! No error diagnostics are produced for the unrecognized extension directives.
+! CHECK-NOT: error:
+
+subroutine ompx_unrecognized
+ !$ompx some_vendor_directive
+end subroutine
+
+!$ompx another_vendor_directive
+end
diff --git a/flang/test/Parser/OpenMP/sentinel-ompx.f90 b/flang/test/Parser/OpenMP/sentinel-ompx.f90
new file mode 100644
index 0000000000000..4c30d312f95eb
--- /dev/null
+++ b/flang/test/Parser/OpenMP/sentinel-ompx.f90
@@ -0,0 +1,28 @@
+! Implementation-defined extension sentinel "ompx" in free source form
+! (OpenMP 5.2, section 3.1.2). The !$ompx sentinel introduces implementation-
+! defined extension directives and is recognized like the !$omp sentinel.
+
+! RUN: %flang_fc1 -E -fopenmp %s 2>&1 | FileCheck %s --check-prefix=CHECK-E
+! RUN: %flang_fc1 -fopenmp -fdebug-unparse %s 2>&1 | FileCheck %s --check-prefix=CHECK-OMP
+! RUN: %flang_fc1 -fdebug-unparse %s 2>&1 | FileCheck %s --check-prefix=CHECK-NO-OMP
+
+subroutine ompx_sub(a, b, c)
+ real :: a, b, c
+ !$ompx parallel &
+ !$ompx private(a, b, c)
+ a = b + c
+ !$ompx end parallel
+end subroutine
+
+! The 5-character sentinel is preserved contiguously (not split as "!$omp x"),
+! so the -E output round-trips.
+! CHECK-E:{{^}}!$ompx parallel private(a, b, c)
+! CHECK-E:{{^}}!$ompx end parallel
+
+! With -fopenmp the extension directive is parsed as an OpenMP directive.
+! CHECK-OMP: !$OMP PARALLEL PRIVATE(a,b,c)
+! CHECK-OMP: !$OMP END PARALLEL
+
+! Without -fopenmp the extension directive is ignored as a comment.
+! CHECK-NO-OMP: SUBROUTINE ompx_sub
+! CHECK-NO-OMP-NOT: !$OMP
diff --git a/flang/test/Parser/OpenMP/sentinel-omx.f b/flang/test/Parser/OpenMP/sentinel-omx.f
new file mode 100644
index 0000000000000..fbb415ad6b46a
--- /dev/null
+++ b/flang/test/Parser/OpenMP/sentinel-omx.f
@@ -0,0 +1,45 @@
+! Implementation-defined extension sentinel "omx" in fixed source form
+! (OpenMP 5.2, section 3.1.1). The sentinels !$omx, c$omx and *$omx introduce
+! implementation-defined extension directives and are recognized like !$omp;
+! the prescanner normalizes the fixed-form comment character (c, *) to '!'.
+
+! RUN: %flang_fc1 -E -fopenmp %s 2>&1 | FileCheck %s --check-prefix=CHECK-E
+! RUN: %flang_fc1 -fopenmp -fdebug-unparse %s 2>&1 | FileCheck %s --check-prefix=CHECK-OMP
+! RUN: %flang_fc1 -fdebug-unparse %s 2>&1 | FileCheck %s --check-prefix=CHECK-NO-OMP
+
+ subroutine omx_forms(a, n)
+ integer :: n
+ real :: a(n)
+!$omx parallel
+c$omx barrier
+ call work(a, n)
+*$omx end parallel
+ end subroutine
+
+! A continuation line for an omx directive uses a non-blank in column 6.
+ subroutine omx_cont(a, b, c)
+ real :: a, b, c
+c$omx parallel
+c$omx+ private(a, b,
+c$omx+ c)
+ a = b + c
+c$omx end parallel
+ end subroutine
+
+! The comment character is normalized to '!' and the omx sentinel is preserved.
+! CHECK-E:{{^}}!$omx parallel
+! CHECK-E:{{^}}!$omx barrier
+! CHECK-E:{{^}}!$omx end parallel
+! CHECK-E:{{^}}!$omx parallel private(a, b, c)
+! CHECK-E:{{^}}!$omx end parallel
+
+! With -fopenmp the extension directives are parsed as OpenMP directives.
+! CHECK-OMP: !$OMP PARALLEL
+! CHECK-OMP: !$OMP BARRIER
+! CHECK-OMP: !$OMP END PARALLEL
+! CHECK-OMP: !$OMP PARALLEL PRIVATE(a,b,c)
+! CHECK-OMP: !$OMP END PARALLEL
+
+! Without -fopenmp the extension directives are ignored as comments.
+! CHECK-NO-OMP: SUBROUTINE omx_forms
+! CHECK-NO-OMP-NOT: !$OMP
>From f12863828ae22625969aa71fb3e4ac74a39ffa92 Mon Sep 17 00:00:00 2001
From: Chandra Ghale <ghale at pe34genoa.hpc.amslabs.hpecorp.net>
Date: Mon, 24 Aug 2026 14:53:36 -0500
Subject: [PATCH 2/3] formatting
---
flang/lib/Parser/openmp-parsers.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/flang/lib/Parser/openmp-parsers.cpp b/flang/lib/Parser/openmp-parsers.cpp
index e4f226dd8524a..2a979afc86d4a 100644
--- a/flang/lib/Parser/openmp-parsers.cpp
+++ b/flang/lib/Parser/openmp-parsers.cpp
@@ -2857,7 +2857,7 @@ TYPE_PARSER(construct<OpenMPMisplacedEndDirective>(
//
// This is a fallback that is attempted before the enclosing program unit is
// reparsed as an execution-part construct, so the standard "!$omp" branch uses
-// the strict "_id" spelling: it must not prefix-match the "!$omp" that begins an
+// the strict "_id" spelling: it must not prefix-match the "!$omp" that begins
// "!$ompx" line, otherwise a valid extension directive such as "!$ompx barrier"
// would be misreported as an invalid "!$omp" directive instead of being handled
// as a real construct.
>From 5fada923af547d99ce95b1beca5d05a3260259d1 Mon Sep 17 00:00:00 2001
From: Chandra Ghale <ghale at pe34genoa.hpc.amslabs.hpecorp.net>
Date: Wed, 26 Aug 2026 07:13:41 -0500
Subject: [PATCH 3/3] Address review comments
---
flang/docs/ReleaseNotes.md | 1 -
flang/lib/Parser/prescan.cpp | 11 ++++++++++-
flang/lib/Parser/prescan.h | 4 +++-
flang/test/Parser/OpenMP/sentinel-ompx.f90 | 15 +++++++++++++++
4 files changed, 28 insertions(+), 3 deletions(-)
diff --git a/flang/docs/ReleaseNotes.md b/flang/docs/ReleaseNotes.md
index ba48d8ebfd77c..079a8e5ba0e94 100644
--- a/flang/docs/ReleaseNotes.md
+++ b/flang/docs/ReleaseNotes.md
@@ -38,7 +38,6 @@ page](https://llvm.org/releases/).
and `!$ompx` in free source form. These sentinels are recognized like their
`omp` counterparts when OpenMP is enabled.
-## New Compiler Flags
- The legacy array-value operations (`fir.array_load`, `fir.array_fetch`,
`fir.array_update`, `fir.array_modify`, `fir.array_access`,
`fir.array_amend`, `fir.array_merge_store`) have been removed from FIR,
diff --git a/flang/lib/Parser/prescan.cpp b/flang/lib/Parser/prescan.cpp
index a545b8a807c6f..95d1febd68b98 100644
--- a/flang/lib/Parser/prescan.cpp
+++ b/flang/lib/Parser/prescan.cpp
@@ -1795,7 +1795,16 @@ const char *Prescanner::IsCompilerDirectiveSentinel(
return nullptr;
}
const auto iter{compilerDirectiveSentinels_.find(std::string(sentinel, len))};
- return iter == compilerDirectiveSentinels_.end() ? nullptr : iter->c_str();
+ if (iter == compilerDirectiveSentinels_.end()) {
+ return nullptr;
+ }
+ // OpenMP 5.2, 3.1: "omx" is an extension sentinel for fixed source form only
+ // (the free-form extension sentinel is "ompx"), so a free-form "!$omx" is an
+ // ordinary comment rather than a directive.
+ if (!inFixedForm_ && *iter == "$omx") {
+ return nullptr;
+ }
+ return iter->c_str();
}
const char *Prescanner::IsCompilerDirectiveSentinel(CharBlock token) const {
diff --git a/flang/lib/Parser/prescan.h b/flang/lib/Parser/prescan.h
index 42311cd128f4d..13dfbe63aa973 100644
--- a/flang/lib/Parser/prescan.h
+++ b/flang/lib/Parser/prescan.h
@@ -201,7 +201,9 @@ class Prescanner {
return directiveSentinel_ &&
(std::strcmp(directiveSentinel_, "$omp") == 0 ||
// Implementation-defined extension sentinels (OpenMP 5.2, 3.1):
- // "$omx" (fixed form) and "$ompx" (free form).
+ // "$omx" (fixed form) and "$ompx" (free form). The form is enforced
+ // during recognition (IsCompilerDirectiveSentinel), so a wrong-form
+ // spelling is treated as a comment and never reaches here.
std::strcmp(directiveSentinel_, "$omx") == 0 ||
std::strcmp(directiveSentinel_, "$ompx") == 0);
}
diff --git a/flang/test/Parser/OpenMP/sentinel-ompx.f90 b/flang/test/Parser/OpenMP/sentinel-ompx.f90
index 4c30d312f95eb..915c6540d7c19 100644
--- a/flang/test/Parser/OpenMP/sentinel-ompx.f90
+++ b/flang/test/Parser/OpenMP/sentinel-ompx.f90
@@ -1,6 +1,8 @@
! Implementation-defined extension sentinel "ompx" in free source form
! (OpenMP 5.2, section 3.1.2). The !$ompx sentinel introduces implementation-
! defined extension directives and is recognized like the !$omp sentinel.
+! The fixed-form-only "omx" sentinel is not recognized in free form, so a
+! free-form !$omx line is an ordinary comment.
! RUN: %flang_fc1 -E -fopenmp %s 2>&1 | FileCheck %s --check-prefix=CHECK-E
! RUN: %flang_fc1 -fopenmp -fdebug-unparse %s 2>&1 | FileCheck %s --check-prefix=CHECK-OMP
@@ -14,6 +16,15 @@ subroutine ompx_sub(a, b, c)
!$ompx end parallel
end subroutine
+! In free source form the fixed-form "omx" sentinel is not recognized; even
+! with -fopenmp these lines stay ordinary comments.
+subroutine omx_in_free(a, b, c)
+ real :: a, b, c
+ !$omx parallel
+ a = b + c
+ !$omx end parallel
+end subroutine
+
! The 5-character sentinel is preserved contiguously (not split as "!$omp x"),
! so the -E output round-trips.
! CHECK-E:{{^}}!$ompx parallel private(a, b, c)
@@ -22,6 +33,10 @@ subroutine ompx_sub(a, b, c)
! With -fopenmp the extension directive is parsed as an OpenMP directive.
! CHECK-OMP: !$OMP PARALLEL PRIVATE(a,b,c)
! CHECK-OMP: !$OMP END PARALLEL
+! The free-form !$omx lines are comments, so no OpenMP directive is produced.
+! CHECK-OMP: SUBROUTINE omx_in_free
+! CHECK-OMP-NOT: !$OMP
+! CHECK-OMP: END SUBROUTINE
! Without -fopenmp the extension directive is ignored as a comment.
! CHECK-NO-OMP: SUBROUTINE ompx_sub
More information about the flang-commits
mailing list