[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