https://github.com/perry-ca updated 
https://github.com/llvm/llvm-project/pull/141671

>From 33413248f61e899f6d4dbbddec9c1cec7bf0e44a Mon Sep 17 00:00:00 2001
From: Sean Perry <pe...@ca.ibm.com>
Date: Fri, 23 May 2025 04:36:46 +0000
Subject: [PATCH 1/3] #pragma export support

---
 clang/docs/ReleaseNotes.rst                   |   5 +
 .../clang/Basic/DiagnosticSemaKinds.td        |  10 +
 clang/include/clang/Basic/TokenKinds.def      |   3 +
 clang/include/clang/Parse/Parser.h            |  12 +
 clang/include/clang/Sema/Sema.h               |  37 +++
 clang/lib/Driver/ToolChains/ZOS.cpp           |   4 +
 clang/lib/Parse/ParsePragma.cpp               | 218 ++++++++++++++++++
 clang/lib/Parse/ParseStmt.cpp                 |   8 +
 clang/lib/Parse/Parser.cpp                    |   3 +
 clang/lib/Sema/Sema.cpp                       |   6 +
 clang/lib/Sema/SemaAttr.cpp                   | 166 +++++++++++++
 clang/lib/Sema/SemaDecl.cpp                   | 105 +++++++++
 clang/lib/Sema/SemaDeclAttr.cpp               |   9 +
 clang/test/CodeGen/pragma-export.c            |  44 ++++
 clang/test/CodeGen/pragma-export.cpp          | 122 ++++++++++
 clang/test/CodeGen/zos-pragmas.c              |  11 +
 clang/test/CodeGen/zos-pragmas.cpp            |  11 +
 clang/test/Parser/pragma-export.c             |  15 ++
 clang/test/Parser/pragma-export.cpp           |  21 ++
 clang/test/Sema/pragma-export-failing.c       |  42 ++++
 clang/test/Sema/pragma-export-failing.cpp     |  25 ++
 21 files changed, 877 insertions(+)
 create mode 100644 clang/test/CodeGen/pragma-export.c
 create mode 100644 clang/test/CodeGen/pragma-export.cpp
 create mode 100644 clang/test/CodeGen/zos-pragmas.c
 create mode 100644 clang/test/CodeGen/zos-pragmas.cpp
 create mode 100644 clang/test/Parser/pragma-export.c
 create mode 100644 clang/test/Parser/pragma-export.cpp
 create mode 100644 clang/test/Sema/pragma-export-failing.c
 create mode 100644 clang/test/Sema/pragma-export-failing.cpp

diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 573ae97bff710..8dca697126026 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -886,6 +886,11 @@ WebAssembly Support
 AVR Support
 ^^^^^^^^^^^
 
+SystemZ Support
+^^^^^^^^^^^^^^^
+
+- Add support for `#pragma export` for z/OS
+
 DWARF Support in Clang
 ----------------------
 
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td 
b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 78b36ceb88125..cbaccee01487d 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -1216,6 +1216,16 @@ def err_pragma_pop_visibility_mismatch : Error<
   "#pragma visibility pop with no matching #pragma visibility push">;
 def note_surrounding_namespace_starts_here : Note<
   "surrounding namespace with visibility attribute starts here">;
+def warn_failed_to_resolve_pragma : Warning<
+  "failed to resolve '#pragma %0' to a declaration">,
+  InGroup<IgnoredPragmas>;
+def warn_pragma_not_applied : Warning<
+  "#pragma %0 is applicable to symbols with external linkage only; "
+  "not applied to %1">,
+  InGroup<IgnoredPragmas>;
+def warn_pragma_not_applied_to_defined_symbol : Warning<
+  "#pragma %0 can only applied before a symbol is defined">,
+  InGroup<IgnoredPragmas>;
 def err_pragma_loop_invalid_argument_type : Error<
   "invalid argument of type %0; expected an integer type">;
 def err_pragma_loop_compatibility : Error<
diff --git a/clang/include/clang/Basic/TokenKinds.def 
b/clang/include/clang/Basic/TokenKinds.def
index 94e72fea56a68..5adf0519be8c8 100644
--- a/clang/include/clang/Basic/TokenKinds.def
+++ b/clang/include/clang/Basic/TokenKinds.def
@@ -1015,6 +1015,9 @@ PRAGMA_ANNOTATION(pragma_fp)
 // Annotation for the attribute pragma directives - #pragma clang attribute ...
 PRAGMA_ANNOTATION(pragma_attribute)
 
+// Annotation for C/C++ #pragma export(ident)
+PRAGMA_ANNOTATION(pragma_export)
+
 // Annotation for the riscv pragma directives - #pragma clang riscv intrinsic 
...
 PRAGMA_ANNOTATION(pragma_riscv)
 
diff --git a/clang/include/clang/Parse/Parser.h 
b/clang/include/clang/Parse/Parser.h
index e6492b81dfff8..0fca8b786ac8c 100644
--- a/clang/include/clang/Parse/Parser.h
+++ b/clang/include/clang/Parse/Parser.h
@@ -7019,6 +7019,7 @@ class Parser : public CodeCompletionHandler {
   std::unique_ptr<PragmaHandler> AttributePragmaHandler;
   std::unique_ptr<PragmaHandler> MaxTokensHerePragmaHandler;
   std::unique_ptr<PragmaHandler> MaxTokensTotalPragmaHandler;
+  std::unique_ptr<PragmaHandler> ExportHandler;
   std::unique_ptr<PragmaHandler> RISCVPragmaHandler;
 
   /// Initialize all pragma handlers.
@@ -7136,6 +7137,17 @@ class Parser : public CodeCompletionHandler {
 
   void HandlePragmaAttribute();
 
+  NestedNameSpecifier *zOSParseIdentifier(StringRef PragmaName,
+                                          const IdentifierInfo *IdentName);
+  bool zOSParseParameterList(StringRef PragmaName,
+                             std::optional<SmallVector<QualType, 4>> &TypeList,
+                             Qualifiers &CVQual);
+  bool zOSHandlePragmaHelper(tok::TokenKind);
+
+  /// Handle the annotation token produced for
+  /// #pragma export ...
+  void HandlePragmaExport();
+
   ///@}
 
   //
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index fe93df94438cb..94d5b9acd6e0a 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -2278,6 +2278,39 @@ class Sema final : public SemaBase {
   ActOnPragmaMSFunction(SourceLocation Loc,
                         const llvm::SmallVectorImpl<StringRef> &NoBuiltins);
 
+  /// A label from a C++ #pragma export, for a symbol that we
+  /// haven't seen the declaration for yet. The TypeList is the argument list
+  /// the function must match if HasTypeList is true.
+  struct SymbolLabel {
+    std::optional<SmallVector<QualType, 4>> TypeList;
+    SourceLocation NameLoc;
+    bool HasTypeList;
+    Qualifiers CVQual;
+    NestedNameSpecifier
+        *NestedNameId; // Nested name identifier for type lookup.
+    bool Used;
+  };
+
+  bool typeListMatchesSymbolLabel(FunctionDecl *FD,
+                                  const clang::Sema::SymbolLabel &Label);
+
+  /// tryLookupSymbolLabel try to look up a decl matching the nested
+  //  specifier with optional type list.
+  NamedDecl *tryLookupSymbolLabel(const clang::Sema::SymbolLabel &Label);
+
+  bool isNamedDeclSameAsSymbolLabel(NamedDecl *D,
+                                    clang::Sema::SymbolLabel &Label);
+
+  typedef SmallVector<SymbolLabel, 1> PendingPragmaExportOverloads;
+  llvm::DenseMap<IdentifierInfo *, PendingPragmaExportOverloads>
+      PendingExportedNames;
+
+  /// ActonPragmaExport - called on well-formed '\#pragma export'.
+  void ActOnPragmaExport(NestedNameSpecifier *NestedId,
+                         SourceLocation ExportNameLoc,
+                         std::optional<SmallVector<QualType, 4>> &&TypeList,
+                         Qualifiers CVQual);
+
   /// Only called on function definitions; if there is a pragma in scope
   /// with the effect of a range-based optnone, consider marking the function
   /// with attribute optnone.
@@ -3801,6 +3834,8 @@ class Sema final : public SemaBase {
   void warnOnReservedIdentifier(const NamedDecl *D);
   void warnOnCTypeHiddenInCPlusPlus(const NamedDecl *D);
 
+  void ProcessPragmaExport(DeclaratorDecl *newDecl);
+
   Decl *ActOnDeclarator(Scope *S, Declarator &D);
 
   NamedDecl *HandleDeclarator(Scope *S, Declarator &D,
@@ -4871,6 +4906,8 @@ class Sema final : public SemaBase {
                           TypeVisibilityAttr::VisibilityType Vis);
   VisibilityAttr *mergeVisibilityAttr(Decl *D, const AttributeCommonInfo &CI,
                                       VisibilityAttr::VisibilityType Vis);
+  void mergeVisibilityType(Decl *D, SourceLocation Loc,
+                           VisibilityAttr::VisibilityType Type);
   SectionAttr *mergeSectionAttr(Decl *D, const AttributeCommonInfo &CI,
                                 StringRef Name);
 
diff --git a/clang/lib/Driver/ToolChains/ZOS.cpp 
b/clang/lib/Driver/ToolChains/ZOS.cpp
index c5ad3ef1b00f1..371623b83abd3 100644
--- a/clang/lib/Driver/ToolChains/ZOS.cpp
+++ b/clang/lib/Driver/ToolChains/ZOS.cpp
@@ -37,6 +37,10 @@ void ZOS::addClangTargetOptions(const ArgList &DriverArgs,
                                 options::OPT_fno_aligned_allocation))
     CC1Args.push_back("-faligned-alloc-unavailable");
 
+  if (!DriverArgs.hasArg(options::OPT_fvisibility_EQ,
+                         options::OPT_fvisibility_ms_compat))
+    CC1Args.push_back("-fvisibility=hidden");
+
   if (DriverArgs.hasFlag(options::OPT_fxl_pragma_pack,
                          options::OPT_fno_xl_pragma_pack, true))
     CC1Args.push_back("-fxl-pragma-pack");
diff --git a/clang/lib/Parse/ParsePragma.cpp b/clang/lib/Parse/ParsePragma.cpp
index 4e67fd033b9aa..ac8f37c5e4c4c 100644
--- a/clang/lib/Parse/ParsePragma.cpp
+++ b/clang/lib/Parse/ParsePragma.cpp
@@ -405,6 +405,12 @@ struct PragmaMaxTokensTotalHandler : public PragmaHandler {
                     Token &FirstToken) override;
 };
 
+struct PragmaExportHandler : public PragmaHandler {
+  explicit PragmaExportHandler() : PragmaHandler("export") {}
+  void HandlePragma(Preprocessor &PP, PragmaIntroducer Introducer,
+                    Token &FirstToken) override;
+};
+
 struct PragmaRISCVHandler : public PragmaHandler {
   PragmaRISCVHandler(Sema &Actions)
       : PragmaHandler("riscv"), Actions(Actions) {}
@@ -568,6 +574,11 @@ void Parser::initializePragmaHandlers() {
   MaxTokensTotalPragmaHandler = 
std::make_unique<PragmaMaxTokensTotalHandler>();
   PP.AddPragmaHandler("clang", MaxTokensTotalPragmaHandler.get());
 
+  if (getLangOpts().ZOSExt) {
+    ExportHandler = std::make_unique<PragmaExportHandler>();
+    PP.AddPragmaHandler(ExportHandler.get());
+  }
+
   if (getTargetInfo().getTriple().isRISCV()) {
     RISCVPragmaHandler = std::make_unique<PragmaRISCVHandler>(Actions);
     PP.AddPragmaHandler("clang", RISCVPragmaHandler.get());
@@ -702,6 +713,11 @@ void Parser::resetPragmaHandlers() {
   PP.RemovePragmaHandler("clang", MaxTokensTotalPragmaHandler.get());
   MaxTokensTotalPragmaHandler.reset();
 
+  if (getLangOpts().ZOSExt) {
+    PP.RemovePragmaHandler(ExportHandler.get());
+    ExportHandler.reset();
+  }
+
   if (getTargetInfo().getTriple().isRISCV()) {
     PP.RemovePragmaHandler("clang", RISCVPragmaHandler.get());
     RISCVPragmaHandler.reset();
@@ -1395,6 +1411,171 @@ bool Parser::HandlePragmaMSAllocText(StringRef 
PragmaName,
   return true;
 }
 
+NestedNameSpecifier *
+Parser::zOSParseIdentifier(StringRef PragmaName,
+                           const IdentifierInfo *IdentName) {
+  NestedNameSpecifier *NestedId = nullptr;
+  if (PP.getLangOpts().CPlusPlus) {
+    if (Tok.is(tok::coloncolon)) {
+      // Nothing to do.
+    } else if (Actions.CurContext->isNamespace()) {
+      auto *NS = cast<NamespaceDecl>(Actions.CurContext);
+      NestedId =
+          NestedNameSpecifier::Create(Actions.Context, NS->getIdentifier());
+      NestedId =
+          NestedNameSpecifier::Create(Actions.Context, NestedId, IdentName);
+      PP.Lex(Tok);
+    } else {
+      NestedId = NestedNameSpecifier::Create(Actions.Context, IdentName);
+      PP.Lex(Tok);
+    }
+    while (Tok.is(tok::coloncolon)) {
+      PP.Lex(Tok);
+      if (Tok.isNot(tok::identifier)) {
+        PP.Diag(Tok.getLocation(), diag::warn_pragma_expected_identifier)
+            << PragmaName;
+        return nullptr;
+      }
+      IdentifierInfo *II = Tok.getIdentifierInfo();
+      NestedId = NestedNameSpecifier::Create(Actions.Context, NestedId, II);
+      PP.Lex(Tok);
+    }
+  } else {
+    NestedId = NestedNameSpecifier::Create(Actions.Context, IdentName);
+    PP.Lex(Tok);
+  }
+  return NestedId;
+}
+
+bool Parser::zOSParseParameterList(
+    StringRef PragmaName, std::optional<SmallVector<QualType, 4>> &TypeList,
+    Qualifiers &CVQual) {
+  if (Tok.is(tok::l_paren)) {
+    TypeList = SmallVector<QualType, 4>();
+    PP.Lex(Tok);
+    while (Tok.isNot(tok::eof) && !Tok.is(tok::r_paren)) {
+      TypeResult TResult = ParseTypeName(nullptr);
+      if (!TResult.isInvalid()) {
+        QualType QT = TResult.get().get();
+        if (!QT.getTypePtr()->isVoidType()) {
+          TypeList->push_back(QT);
+        }
+      }
+      if (Tok.is(tok::comma) || Tok.is(tok::identifier))
+        PP.Lex(Tok);
+    }
+    if (Tok.is(tok::r_paren))
+      PP.Lex(Tok);
+    else {
+      // We ate the whole line trying to find the right paren of the parameter
+      // list.
+      PP.Diag(Tok.getLocation(), diag::warn_pragma_expected_identifier)
+          << PragmaName;
+      return false;
+    }
+
+    if (TypeList.has_value())
+      while (Tok.is(tok::kw_const) || Tok.is(tok::kw_volatile)) {
+        if (Tok.is(tok::kw_const)) {
+          CVQual.addConst();
+        } else {
+          assert(Tok.is(tok::kw_volatile));
+          CVQual.addVolatile();
+        }
+        PP.Lex(Tok);
+      }
+  }
+  return true;
+}
+
+bool Parser::zOSHandlePragmaHelper(tok::TokenKind PragmaKind) {
+  assert(Tok.is(PragmaKind));
+
+  bool IsPragmaExport = PragmaKind == tok::annot_pragma_export;
+  assert(IsPragmaExport);
+  StringRef PragmaName = "export";
+
+  using namespace clang::charinfo;
+  auto *TheTokens =
+      (std::pair<std::unique_ptr<Token[]>, size_t> *)Tok.getAnnotationValue();
+  PP.EnterTokenStream(std::move(TheTokens->first), TheTokens->second, true,
+                      false);
+  ConsumeAnnotationToken();
+
+  do {
+    PP.Lex(Tok);
+    if (Tok.isNot(tok::l_paren)) {
+      PP.Diag(Tok.getLocation(), diag::warn_pragma_expected_lparen)
+          << PragmaName;
+      return false;
+    }
+
+    // C++ could have a nested name, or be qualified with ::.
+    PP.Lex(Tok);
+    if (Tok.isNot(tok::identifier) &&
+        !(PP.getLangOpts().CPlusPlus && Tok.is(tok::coloncolon))) {
+      PP.Diag(Tok.getLocation(), diag::warn_pragma_expected_identifier)
+          << PragmaName;
+      return false;
+    }
+
+    IdentifierInfo *IdentName = Tok.getIdentifierInfo();
+    SourceLocation IdentNameLoc = Tok.getLocation();
+    NestedNameSpecifier *NestedId = zOSParseIdentifier(PragmaName, IdentName);
+    if (!NestedId)
+      return false;
+
+    // C++ can have a paramater list for overloaded functions.
+    // Try to parse the argument types.
+    std::optional<SmallVector<QualType, 4>> TypeList;
+    Qualifiers CVQual;
+
+    if (PP.getLangOpts().CPlusPlus && Tok.is(tok::l_paren)) {
+      if (!zOSParseParameterList(PragmaName, TypeList, CVQual))
+        return false;
+    }
+
+    if (Tok.isNot(tok::r_paren)) {
+      PP.Diag(Tok.getLocation(), diag::warn_pragma_expected_rparen)
+          << PragmaName;
+      return false;
+    }
+
+    PP.Lex(Tok);
+    Actions.ActOnPragmaExport(NestedId, IdentNameLoc, std::move(TypeList),
+                              CVQual);
+
+    // Because export is also a C++ keyword, we also check for that.
+    if (Tok.is(tok::identifier) || Tok.is(tok::kw_export)) {
+      IsPragmaExport = false;
+      PragmaName = Tok.getIdentifierInfo()->getName();
+      if (PragmaName == "export")
+        IsPragmaExport = true;
+      else
+        PP.Diag(Tok.getLocation(), diag::warn_pragma_extra_tokens_at_eol)
+            << PragmaName;
+    } else if (Tok.isNot(tok::eof)) {
+      PP.Diag(Tok.getLocation(), diag::warn_pragma_extra_tokens_at_eol)
+          << PragmaName;
+      return false;
+    }
+  } while (Tok.isNot(tok::eof));
+  PP.Lex(Tok);
+  return true;
+}
+
+void Parser::HandlePragmaExport() {
+  assert(Tok.is(tok::annot_pragma_export));
+
+  if (!zOSHandlePragmaHelper(tok::annot_pragma_export)) {
+    // Parsing pragma failed, and has been diagnosed.  Slurp up the
+    // tokens until eof (really end of line) to prevent follow-on errors.
+    while (Tok.isNot(tok::eof))
+      PP.Lex(Tok);
+    PP.Lex(Tok);
+  }
+}
+
 static std::string PragmaLoopHintString(Token PragmaName, Token Option) {
   StringRef Str = PragmaName.getIdentifierInfo()->getName();
   std::string ClangLoopStr("clang loop ");
@@ -4149,6 +4330,43 @@ void 
PragmaMaxTokensTotalHandler::HandlePragma(Preprocessor &PP,
   PP.overrideMaxTokens(MaxTokens, Loc);
 }
 
+static void zOSPragmaHandlerHelper(Preprocessor &PP, Token &Tok,
+                                   tok::TokenKind TokKind) {
+  Token EoF, AnnotTok;
+  EoF.startToken();
+  EoF.setKind(tok::eof);
+  AnnotTok.startToken();
+  AnnotTok.setKind(TokKind);
+  AnnotTok.setLocation(Tok.getLocation());
+  AnnotTok.setAnnotationEndLoc(Tok.getLocation());
+  SmallVector<Token, 8> TokenVector;
+  // Suck up all of the tokens before the eod.
+  for (; Tok.isNot(tok::eod); PP.Lex(Tok)) {
+    TokenVector.push_back(Tok);
+    AnnotTok.setAnnotationEndLoc(Tok.getLocation());
+  }
+  // Add a sentinel EoF token to the end of the list.
+  EoF.setLocation(Tok.getLocation());
+  TokenVector.push_back(EoF);
+  // We must allocate this array with new because EnterTokenStream is going to
+  // delete it later.
+  markAsReinjectedForRelexing(TokenVector);
+  auto TokenArray = std::make_unique<Token[]>(TokenVector.size());
+  std::copy(TokenVector.begin(), TokenVector.end(), TokenArray.get());
+  auto Value = new (PP.getPreprocessorAllocator())
+      std::pair<std::unique_ptr<Token[]>, size_t>(std::move(TokenArray),
+                                                  TokenVector.size());
+  AnnotTok.setAnnotationValue(Value);
+  PP.EnterToken(AnnotTok, /*IsReinject*/ false);
+}
+
+/// Handle #pragma export.
+void PragmaExportHandler::HandlePragma(Preprocessor &PP,
+                                       PragmaIntroducer Introducer,
+                                       Token &FirstToken) {
+  zOSPragmaHandlerHelper(PP, FirstToken, tok::annot_pragma_export);
+}
+
 // Handle '#pragma clang riscv intrinsic vector'.
 //        '#pragma clang riscv intrinsic sifive_vector'.
 //        '#pragma clang riscv intrinsic andes_vector'.
diff --git a/clang/lib/Parse/ParseStmt.cpp b/clang/lib/Parse/ParseStmt.cpp
index c788723023c8b..2b9e5d7f0c9df 100644
--- a/clang/lib/Parse/ParseStmt.cpp
+++ b/clang/lib/Parse/ParseStmt.cpp
@@ -488,6 +488,11 @@ StmtResult 
Parser::ParseStatementOrDeclarationAfterAttributes(
     ProhibitAttributes(GNUAttrs);
     HandlePragmaAttribute();
     return StmtEmpty();
+  case tok::annot_pragma_export:
+    ProhibitAttributes(CXX11Attrs);
+    ProhibitAttributes(GNUAttrs);
+    HandlePragmaExport();
+    return StmtEmpty();
   }
 
   // If we reached this code, the statement must end in a semicolon.
@@ -1012,6 +1017,9 @@ void Parser::ParseCompoundStatementLeadingPragmas() {
     case tok::annot_pragma_dump:
       HandlePragmaDump();
       break;
+    case tok::annot_pragma_export:
+      HandlePragmaExport();
+      break;
     default:
       checkForPragmas = false;
       break;
diff --git a/clang/lib/Parse/Parser.cpp b/clang/lib/Parse/Parser.cpp
index 55a768580d393..8cfb75ec58691 100644
--- a/clang/lib/Parse/Parser.cpp
+++ b/clang/lib/Parse/Parser.cpp
@@ -843,6 +843,9 @@ Parser::ParseExternalDeclaration(ParsedAttributes &Attrs,
   case tok::annot_pragma_attribute:
     HandlePragmaAttribute();
     return nullptr;
+  case tok::annot_pragma_export:
+    HandlePragmaExport();
+    return nullptr;
   case tok::semi:
     // Either a C++11 empty-declaration or attribute-declaration.
     SingleDecl =
diff --git a/clang/lib/Sema/Sema.cpp b/clang/lib/Sema/Sema.cpp
index 1901d19b14dfc..0f3af893343f2 100644
--- a/clang/lib/Sema/Sema.cpp
+++ b/clang/lib/Sema/Sema.cpp
@@ -1477,6 +1477,12 @@ void Sema::ActOnEndOfTranslationUnit() {
     Consumer.CompleteExternalDeclaration(D);
   }
 
+  // Visit all pending #pragma export.
+  for (auto &Iter : PendingExportedNames)
+    for (auto &Exported : Iter.second)
+      if (!Exported.Used)
+        Diag(Exported.NameLoc, diag::warn_failed_to_resolve_pragma) << 
"export";
+
   if (LangOpts.HLSL)
     HLSL().ActOnEndOfTranslationUnit(getASTContext().getTranslationUnitDecl());
 
diff --git a/clang/lib/Sema/SemaAttr.cpp b/clang/lib/Sema/SemaAttr.cpp
index 44726c4cea123..de00dad5a17a7 100644
--- a/clang/lib/Sema/SemaAttr.cpp
+++ b/clang/lib/Sema/SemaAttr.cpp
@@ -1328,6 +1328,172 @@ void 
Sema::AddImplicitMSFunctionNoBuiltinAttr(FunctionDecl *FD) {
     FD->addAttr(NoBuiltinAttr::CreateImplicit(Context, V.data(), V.size()));
 }
 
+static QualType getCanonicalParamType(ASTContext &C, QualType T) {
+  return C.getCanonicalParamType(T);
+}
+
+bool Sema::typeListMatchesSymbolLabel(FunctionDecl *FD,
+                                      const clang::Sema::SymbolLabel &Label) {
+  assert(Label.TypeList.has_value());
+  if (FD->getNumParams() != Label.TypeList->size()) {
+    return false;
+  }
+
+  // Check if arguments match.
+  for (unsigned i = 0; i != FD->getNumParams(); ++i) {
+    const ParmVarDecl *PVD = FD->getParamDecl(i);
+    QualType ParmType = PVD->getType().getCanonicalType();
+
+    QualType MapArgType =
+        getCanonicalParamType(Context, 
(*Label.TypeList)[i].getCanonicalType());
+
+    if (ParmType != MapArgType)
+      return false;
+  }
+
+  if (isa<CXXMethodDecl>(FD)) {
+    // Check if CV qualifiers match.
+    const clang::CXXMethodDecl *const MFD =
+        clang::cast<clang::CXXMethodDecl>(FD);
+    if (MFD && (MFD->isConst() != Label.CVQual.hasConst() ||
+                MFD->isVolatile() != Label.CVQual.hasVolatile())) {
+      return false;
+    }
+  } else if (Label.CVQual.hasConst() || Label.CVQual.hasVolatile())
+    return false;
+
+  return true;
+}
+
+NamedDecl *Sema::tryLookupSymbolLabel(const clang::Sema::SymbolLabel &Label) {
+
+  NestedNameSpecifier *NestedName = Label.NestedNameId;
+  assert(!NestedName->getPrefix() ||
+         NestedName->getPrefix()->getKind() == 
NestedNameSpecifier::Identifier);
+  IdentifierInfo *Prefix =
+      NestedName->getPrefix() ? NestedName->getPrefix()->getAsIdentifier() : 0;
+  IdentifierInfo *Name = NestedName->getAsIdentifier();
+  LookupResult Result(*this, (Prefix ? Prefix : Name), Label.NameLoc,
+                      LookupOrdinaryName);
+  LookupName(Result, TUScope);
+
+  // Filter down to just a function, namespace or class.
+  LookupResult::Filter F = Result.makeFilter();
+  while (F.hasNext()) {
+    NamedDecl *D = F.next();
+    if (!(isa<FunctionDecl>(D) || isa<VarDecl>(D) || isa<NamespaceDecl>(D) ||
+          isa<CXXRecordDecl>(D)))
+      F.erase();
+  }
+  F.done();
+
+  auto MatchDecl = [this, Name, Label](DeclContext *DC) -> NamedDecl * {
+    auto LRes = DC->lookup(DeclarationName(Name));
+    for (auto *I : LRes) {
+      if (isa<VarDecl>(I))
+        return I;
+      if (isa<FunctionDecl>(I)) {
+        FunctionDecl *FD = dyn_cast<FunctionDecl>(I);
+
+        // All function parameters must match if specified in pragma otherwise,
+        // we accept a function found by lookup only if it's the only one.
+        if ((Label.TypeList.has_value() &&
+             typeListMatchesSymbolLabel(FD, Label)) ||
+            (!Label.TypeList.has_value() && LRes.isSingleResult()))
+          return FD;
+      }
+    }
+    return nullptr;
+  };
+
+  // global variable or function in a namespace.
+  if (NamespaceDecl *ND = Result.getAsSingle<NamespaceDecl>()) {
+    if (ND->getIdentifierNamespace() == Decl::IDNS_Namespace) {
+      return MatchDecl(ND);
+    }
+  }
+
+  // data or function member.
+  if (CXXRecordDecl *RD = Result.getAsSingle<CXXRecordDecl>()) {
+    return MatchDecl(RD);
+  }
+
+  // either a variable, or a non-overloaded function, or an overloaded
+  // function with extern "C" linkage.
+  if (!Label.TypeList.has_value()) {
+    if (Result.isSingleResult()) {
+      NamedDecl *ND = Result.getFoundDecl();
+      if (isa<VarDecl>(ND))
+        return ND;
+      if (FunctionDecl *FD = dyn_cast<FunctionDecl>(ND)) {
+        if (!getLangOpts().CPlusPlus || FD->isExternC())
+          return FD;
+        else
+          return nullptr;
+      }
+      return ND;
+    }
+    if (Result.isOverloadedResult()) {
+      for (auto *Iter : Result) {
+        FunctionDecl *FD = dyn_cast<FunctionDecl>(Iter);
+        if (FD && FD->isExternC())
+          return FD;
+      }
+      return nullptr;
+    }
+    return nullptr;
+  }
+
+  // Loop over all the found decls and see if the arguments match
+  // any of the results.
+  for (LookupResult::iterator I = Result.begin(); I != Result.end(); ++I) {
+    NamedDecl *ND = (*I)->getUnderlyingDecl();
+    FunctionDecl *FD = dyn_cast<FunctionDecl>(ND);
+    if (FD && typeListMatchesSymbolLabel(FD, Label)) {
+      return FD;
+    }
+  }
+  return nullptr;
+}
+
+void Sema::ActOnPragmaExport(NestedNameSpecifier *NestedId,
+                             SourceLocation NameLoc,
+                             std::optional<SmallVector<QualType, 4>> 
&&TypeList,
+                             Qualifiers CVQual) {
+  SymbolLabel Label;
+  Label.NameLoc = NameLoc;
+  Label.CVQual = CVQual;
+  Label.TypeList = std::move(TypeList);
+  Label.NestedNameId = NestedId;
+  Label.Used = false;
+
+  NamedDecl *PrevDecl = tryLookupSymbolLabel(Label);
+  if (PrevDecl && (isa<FunctionDecl>(PrevDecl) || isa<VarDecl>(PrevDecl))) {
+    if (PrevDecl->hasExternalFormalLinkage()) {
+      if (auto *FD = dyn_cast<FunctionDecl>(PrevDecl)) {
+        if (FD->hasBody())
+          Diag(NameLoc, diag::warn_pragma_not_applied_to_defined_symbol)
+              << "export";
+        else
+          mergeVisibilityType(PrevDecl, NameLoc, VisibilityAttr::Default);
+      } else {
+        auto *VD = dyn_cast<VarDecl>(PrevDecl);
+        assert(VD);
+        if (VD->hasDefinition() == VarDecl::Definition)
+          Diag(NameLoc, diag::warn_pragma_not_applied_to_defined_symbol)
+              << "export";
+        else
+          mergeVisibilityType(PrevDecl, NameLoc, VisibilityAttr::Default);
+      }
+    } else
+      Diag(NameLoc, diag::warn_pragma_not_applied) << "export" << PrevDecl;
+    Label.Used = true;
+  }
+  if (!Label.Used) {
+    PendingExportedNames[NestedId->getAsIdentifier()].push_back(Label);
+  }
+}
+
 typedef std::vector<std::pair<unsigned, SourceLocation> > VisStack;
 enum : unsigned { NoVisibility = ~0U };
 
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index 814f81cb64cae..64cec75f60bac 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -7514,6 +7514,109 @@ static void emitReadOnlyPlacementAttrWarning(Sema &S, 
const VarDecl *VD) {
   }
 }
 
+// Checks if the given label matches the named declaration.
+bool Sema::isNamedDeclSameAsSymbolLabel(NamedDecl *D,
+                                        Sema::SymbolLabel &Label) {
+  const DeclContext *Ctx = D->getDeclContext();
+
+  // Check the name.
+  NestedNameSpecifier *NS = Label.NestedNameId;
+  if (NS->getAsIdentifier()->getName() != D->getIdentifier()->getName())
+    return false;
+  NS = NS->getPrefix();
+
+  if (NS) {
+    // For ObjC methods and properties, look through categories and use the
+    // interface as context.
+    if (auto *MD = dyn_cast<ObjCMethodDecl>(D)) {
+      if (auto *ID = MD->getClassInterface())
+        Ctx = ID;
+    } else if (auto *PD = dyn_cast<ObjCPropertyDecl>(D)) {
+      if (auto *MD = PD->getGetterMethodDecl())
+        if (auto *ID = MD->getClassInterface())
+          Ctx = ID;
+    } else if (auto *ID = dyn_cast<ObjCIvarDecl>(D)) {
+      if (auto *CI = ID->getContainingInterface())
+        Ctx = CI;
+    }
+
+    // Check named contexts.
+    if (Ctx->isFunctionOrMethod())
+      return false;
+
+    DeclarationName NameInScope = D->getDeclName();
+    for (; NS && Ctx; Ctx = Ctx->getParent()) {
+      // Suppress anonymous namespace.
+      if (isa<NamespaceDecl>(Ctx) &&
+          cast<NamespaceDecl>(Ctx)->isAnonymousNamespace())
+        continue;
+
+      // Suppress inline namespace if it doesn't make the result ambiguous.
+      if (Ctx->isInlineNamespace() && NameInScope &&
+          cast<NamespaceDecl>(Ctx)->isRedundantInlineQualifierFor(NameInScope))
+        continue;
+
+      // Skip non-named contexts such as linkage specifications and 
ExportDecls.
+      const NamedDecl *ND = dyn_cast<NamedDecl>(Ctx);
+      if (!ND)
+        continue;
+
+      // Fail if the sequence of nested name identifiers is shorter.
+      if (!NS)
+        return false;
+
+      // Fail if the names are not equal.
+      if (NS->getAsIdentifier()->getName() != ND->getIdentifier()->getName())
+        return false;
+
+      NameInScope = ND->getDeclName();
+      NS = NS->getPrefix();
+    }
+
+    // Fail if the sequence of nested name identifiers is longer.
+    // It makes sure that both lists have the same length.
+    if (NS)
+      return false;
+  }
+
+  if (isa<VarDecl>(D) && !Label.TypeList.has_value())
+    return true;
+  if (FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) {
+    // All function parameters match if specified in pragma.
+    if (Label.TypeList.has_value())
+      return typeListMatchesSymbolLabel(FD, Label);
+    // There might be overloaded functions. However, with the available
+    // information it cn only be concluded that the functions are the same.
+    if (!getLangOpts().CPlusPlus || FD->isExternC())
+      return true;
+  }
+
+  return false;
+}
+
+void Sema::ProcessPragmaExport(DeclaratorDecl *NewD) {
+  if (PendingExportedNames.empty())
+    return;
+  IdentifierInfo *IdentName = NewD->getIdentifier();
+  if (IdentName == nullptr)
+    return;
+  auto PendingName = PendingExportedNames.find(IdentName);
+  if (PendingName != PendingExportedNames.end()) {
+    for (auto I = PendingName->second.begin(), E = PendingName->second.end();
+         I != E; ++I) {
+      auto &Label = *I;
+      if (!Label.Used && isNamedDeclSameAsSymbolLabel(NewD, Label)) {
+        Label.Used = true;
+        if (NewD->hasExternalFormalLinkage())
+          mergeVisibilityType(NewD, Label.NameLoc, VisibilityAttr::Default);
+        else
+          Diag(Label.NameLoc, diag::warn_pragma_not_applied)
+              << "export" << NewD;
+      }
+    }
+  }
+}
+
 // Checks if VD is declared at global scope or with C language linkage.
 static bool isMainVar(DeclarationName Name, VarDecl *VD) {
   return Name.getAsIdentifierInfo() &&
@@ -8212,6 +8315,7 @@ NamedDecl *Sema::ActOnVariableDeclarator(
     CheckShadow(NewVD, ShadowedDecl, Previous);
 
   ProcessPragmaWeak(S, NewVD);
+  ProcessPragmaExport(NewVD);
 
   // If this is the first declaration of an extern C variable, update
   // the map of such variables.
@@ -10863,6 +10967,7 @@ Sema::ActOnFunctionDeclarator(Scope *S, Declarator &D, 
DeclContext *DC,
   }
 
   ProcessPragmaWeak(S, NewFD);
+  ProcessPragmaExport(NewFD);
   checkAttributesAfterMerging(*this, *NewFD);
 
   AddKnownFunctionAttributes(NewFD);
diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp
index 8ce51cc2882bf..6e3e1104a20b4 100644
--- a/clang/lib/Sema/SemaDeclAttr.cpp
+++ b/clang/lib/Sema/SemaDeclAttr.cpp
@@ -2632,6 +2632,15 @@ static void handleExternalSourceSymbolAttr(Sema &S, Decl 
*D,
       S.Context, AL, Language, DefinedIn, IsGeneratedDeclaration, USR));
 }
 
+void Sema::mergeVisibilityType(Decl *D, SourceLocation Loc,
+                               VisibilityAttr::VisibilityType Value) {
+  if (VisibilityAttr *Attr = D->getAttr<VisibilityAttr>()) {
+    if (Attr->getVisibility() != Value)
+      Diag(Loc, diag::err_mismatched_visibility);
+  } else
+    D->addAttr(VisibilityAttr::CreateImplicit(Context, Value));
+}
+
 template <class T>
 static T *mergeVisibilityAttr(Sema &S, Decl *D, const AttributeCommonInfo &CI,
                               typename T::VisibilityType value) {
diff --git a/clang/test/CodeGen/pragma-export.c 
b/clang/test/CodeGen/pragma-export.c
new file mode 100644
index 0000000000000..094fd0c6206b7
--- /dev/null
+++ b/clang/test/CodeGen/pragma-export.c
@@ -0,0 +1,44 @@
+// REQUIRES: systemz-registered-target
+// RUN: %clang_cc1 %s -emit-llvm -fzos-extensions -triple s390x-none-zos 
-fvisibility=hidden -o - | FileCheck %s
+
+// Testing pragma export after decl.
+void f0(void) {}
+int v0;
+int vd = 2;
+#pragma export(f0)
+#pragma export(v0)
+#pragma export(vd)
+
+// Testing pragma export before decl.
+#pragma export(f1)
+#pragma export(v1)
+void f1(void) {}
+int v1;
+
+void f2(void);
+
+void t0(void) { f2();}
+
+#pragma export(f2)
+void f2(void) {}
+
+int func() {
+  int local;
+#pragma export(local)
+#pragma export(l2)
+  int l2;
+  return local+l2;
+}
+
+int local = 2;
+int l2 =4;
+
+// CHECK: @vd = hidden global i32
+// CHECK: @local = hidden global i32
+// CHECK: @l2 = hidden global i32
+// CHECK: @v0 = global i32
+// CHECK: @v1 = global i32
+// CHECK: define hidden void @f0()
+// CHECK: define void @f1()
+// CHECK: define hidden void @t0()
+// CHECK: define void @f2()
diff --git a/clang/test/CodeGen/pragma-export.cpp 
b/clang/test/CodeGen/pragma-export.cpp
new file mode 100644
index 0000000000000..aa780887bd272
--- /dev/null
+++ b/clang/test/CodeGen/pragma-export.cpp
@@ -0,0 +1,122 @@
+// REQUIRES: systemz-registered-target
+// RUN: %clang_cc1 -x c++ %s -emit-llvm -triple s390x-none-zos 
-fzos-extensions -fvisibility=hidden -o - | FileCheck %s
+
+// Testing pragma export after decl.
+void f0(void) {}
+int v0;
+#pragma export(f0(void))
+#pragma export(v0)
+
+// Testing pragma export before decl.
+#pragma export(f1(void))
+#pragma export(v1)
+void f1(void) {}
+int v1;
+
+// Testing overloaded functions.
+#pragma export(f2(double, double))
+#pragma export(f2(int))
+void f2(double, double) {}
+void f2(int) {}
+void f2(int, int) {}
+
+void f3(double) {}
+void f3(int, double) {}
+void f3(double, double) {}
+#pragma export(f3(double))
+#pragma export(f3(int, double))
+
+void f2(void) {}
+
+void t0(void) {
+  f2();
+}
+
+// Test type decay in arguments
+
+#pragma export(fd1(int[]))
+#pragma export(fd2(int*))
+#pragma export(fd3(int[]))
+#pragma export(fd4(int*))
+void fd1(int []) { }
+void fd2(int []) { }
+void fd3(int *) { }
+void fd4(int *) { }
+
+
+#pragma export (fd5(int ()))
+#pragma export (fd6(int (*)()))
+#pragma export (fd7(int ()))
+#pragma export (fd8(int (*)()))
+void fd5(int ()) {}
+void fd6(int ()) {}
+void fd7(int (*)()) {}
+void fd8(int (*)()) {}
+
+
+// Testing pragma export after decl and usage.
+#pragma export(f2(void))
+
+// Testing pragma export with namespace.
+void f5(void) {}
+void f5a(void) {}
+#pragma export(N0::f2a(void))
+namespace N0 {
+void f0(void) {}
+void f1(void) {}
+void f2(void) {}
+void f3(void) {}
+void f5(void) {}
+#pragma export(f0(void))
+#pragma export(N0::f1(void))
+#pragma export(f5(void))
+#pragma export(f0a(void))
+#pragma export(N0::f1a(void))
+#pragma export(f5a(void))
+void f0a(void) {}
+void f1a(void) {}
+void f2a(void) {}
+void f3a(void) {}
+void f5a(void) {}
+} // namespace N0
+#pragma export(N0::f2(void))
+
+void f10(int);
+#pragma export(f10)
+extern "C" void f10(double) {}
+void f10(int) {}
+
+// CHECK: @v0 = hidden global i32 0
+// CHECK: @v1 = global i32 0
+// CHECK: define hidden void @_Z2f0v()
+// CHECK: define void @_Z2f1v()
+// CHECK: define void @_Z2f2dd(double noundef %0, double noundef %1)
+// CHECK: define void @_Z2f2i(i32 noundef signext %0)
+// CHECK: define hidden void @_Z2f2ii(i32 noundef signext %0, i32 noundef 
signext %1)
+// CHECK: define hidden void @_Z2f3d(double noundef %0)
+// CHECK: define hidden void @_Z2f3id(i32 noundef signext %0, double noundef 
%1)
+// CHECK: define hidden void @_Z2f3dd(double noundef %0, double noundef %1)
+// CHECK: define hidden void @_Z2f2v()
+// CHECK: define hidden void @_Z2t0v()
+// CHECK: define void @_Z3fd1Pi(ptr noundef %0)
+// CHECK: define void @_Z3fd2Pi(ptr noundef %0)
+// CHECK: define void @_Z3fd3Pi(ptr noundef %0)
+// CHECK: define void @_Z3fd4Pi(ptr noundef %0)
+// CHECK: define void @_Z3fd5PFivE(ptr noundef %0)
+// CHECK: define void @_Z3fd6PFivE(ptr noundef %0)
+// CHECK: define void @_Z3fd7PFivE(ptr noundef %0)
+// CHECK: define void @_Z3fd8PFivE(ptr noundef %0)
+// CHECK: define hidden void @_Z2f5v()
+// CHECK: define hidden void @_Z3f5av()
+// CHECK: define hidden void @_ZN2N02f0Ev()
+// CHECK: define hidden void @_ZN2N02f1Ev()
+// CHECK: define hidden void @_ZN2N02f2Ev()
+// CHECK: define hidden void @_ZN2N02f3Ev()
+// CHECK: define hidden void @_ZN2N02f5Ev()
+// CHECK: define void @_ZN2N03f0aEv()
+// CHECK: define hidden void @_ZN2N03f1aEv()
+// CHECK: define void @_ZN2N03f2aEv()
+// CHECK: define hidden void @_ZN2N03f3aEv()
+// CHECK: define void @_ZN2N03f5aEv()
+// CHECK: define void @f10(double noundef %0) #0 {
+// CHECK: define hidden void @_Z3f10i(i32 noundef signext %0) #0 {
diff --git a/clang/test/CodeGen/zos-pragmas.c b/clang/test/CodeGen/zos-pragmas.c
new file mode 100644
index 0000000000000..e2bd03a33e20a
--- /dev/null
+++ b/clang/test/CodeGen/zos-pragmas.c
@@ -0,0 +1,11 @@
+// REQUIRES: systemz-registered-target
+// RUN: %clang_cc1 -emit-llvm -triple s390x-none-zos -fvisibility=hidden %s -o 
- | FileCheck %s
+
+int a,b,c;
+#pragma export(a) export(b) export(c)
+
+void foo(void);
+
+// CHECK: @a = global i32 0, align 4
+// CHECK: @b = global i32 0, align 4
+// CHECK: @c = global i32 0, align 4
diff --git a/clang/test/CodeGen/zos-pragmas.cpp 
b/clang/test/CodeGen/zos-pragmas.cpp
new file mode 100644
index 0000000000000..65e428796039e
--- /dev/null
+++ b/clang/test/CodeGen/zos-pragmas.cpp
@@ -0,0 +1,11 @@
+// REQUIRES: systemz-registered-target
+// RUN: %clang_cc1 -x c++ -emit-llvm -triple s390x-none-zos 
-fvisibility=hidden %s -o - | FileCheck %s
+
+#pragma export(a) export(b) export(c)
+int a,b,c;
+
+void foo(void);
+
+// CHECK: @a = global i32 0, align 4
+// CHECK: @b = global i32 0, align 4
+// CHECK: @c = global i32 0, align 4
diff --git a/clang/test/Parser/pragma-export.c 
b/clang/test/Parser/pragma-export.c
new file mode 100644
index 0000000000000..e78fa21242c77
--- /dev/null
+++ b/clang/test/Parser/pragma-export.c
@@ -0,0 +1,15 @@
+// RUN: %clang_cc1 -triple s390x-ibm-zos -fsyntax-only -verify %s
+
+int x;
+
+#pragma export x  // expected-warning {{missing '(' after '#pragma export' - 
ignoring}}
+#pragma export  // expected-warning {{missing '(' after '#pragma export' - 
ignoring}}
+#pragma export(  // expected-warning {{expected identifier in '#pragma export' 
- ignored}}
+#pragma export(x  // expected-warning {{missing ')' after '#pragma export' - 
ignoring}}
+#pragma export(::x) // expected-warning {{expected identifier in '#pragma 
export' - ignored}}
+#pragma export(x)
+
+void f() {
+}
+
+#pragma export(f()) // expected-warning {{missing ')' after '#pragma export' - 
ignoring}}
diff --git a/clang/test/Parser/pragma-export.cpp 
b/clang/test/Parser/pragma-export.cpp
new file mode 100644
index 0000000000000..5f1656041b3d3
--- /dev/null
+++ b/clang/test/Parser/pragma-export.cpp
@@ -0,0 +1,21 @@
+// RUN: %clang_cc1 -x c++ -triple s390x-ibm-zos -fsyntax-only -verify %s
+
+extern int i;
+#pragma export(:: // expected-warning {{expected identifier in '#pragma 
export' - ignored}}
+#pragma export(::) // expected-warning {{expected identifier in '#pragma 
export' - ignored}}
+#pragma export(::i)
+
+struct S {
+  static int i;
+};
+#pragma export(S:: // expected-warning {{expected identifier in '#pragma 
export' - ignored}}
+#pragma export(S::i // expected-warning {{missing ')' after '#pragma export' - 
ignoring}}
+#pragma export(S::i)
+
+void f(int);
+void f(double, double);
+#pragma export(f( // expected-warning {{expected identifier in '#pragma 
export' - ignored}}
+#pragma export(f() // expected-warning {{missing ')' after '#pragma export' - 
ignoring}}
+#pragma export(f(int) // expected-warning {{missing ')' after '#pragma export' 
- ignoring}}
+#pragma export(f(double,) // expected-warning {{missing ')' after '#pragma 
export' - ignoring}}
+#pragma export(f(double,double))
diff --git a/clang/test/Sema/pragma-export-failing.c 
b/clang/test/Sema/pragma-export-failing.c
new file mode 100644
index 0000000000000..57bf97e628e32
--- /dev/null
+++ b/clang/test/Sema/pragma-export-failing.c
@@ -0,0 +1,42 @@
+// REQUIRES: systemz-registered-target
+// RUN: %clang_cc1 -triple s390x-none-zos -fzos-extensions %s -fsyntax-only 
-verify
+
+#pragma export(d0)                         // expected-warning{{failed to 
resolve '#pragma export' to a declaration}}
+#pragma export(f9)                         // expected-warning{{failed to 
resolve '#pragma export' to a declaration}}
+
+#pragma export(sf1) // expected-warning{{#pragma export is applicable to 
symbols with external linkage only; not applied to 'sf1'}}
+#pragma export(s1) // expected-warning{{#pragma export is applicable to 
symbols with external linkage only; not applied to 's1'}}
+static void sf1(void) {}
+static int s1;
+
+static void sf0(void) {}
+int v0;
+static int s0;
+#pragma export(sf0) // expected-warning{{#pragma export is applicable to 
symbols with external linkage only; not applied to 'sf0'}}
+#pragma export(s0) // expected-warning{{#pragma export is applicable to 
symbols with external linkage only; not applied to 's0'}}
+
+#pragma export(f1) // expected-error {{visibility does not match previous 
declaration}}
+int f1() __attribute__((visibility("hidden")));
+int f2() __attribute__((visibility("hidden")));
+#pragma export(f2) // expected-error {{visibility does not match previous 
declaration}}
+
+
+int hoo() __attribute__((visibility("hidden")));
+
+int foo() { return 4; }
+#pragma export(foo)  // expected-warning {{#pragma export can only applied 
before a symbol is defined}}
+
+int var = 4;
+#pragma export(var) // expected-warning {{#pragma export can only applied 
before a symbol is defined}}
+
+int func() {
+  int local;
+#pragma export(local) // expected-warning{{#pragma export is applicable to 
symbols with external linkage only; not applied to 'local'}}
+#pragma export(l2) // expected-warning{{#pragma export is applicable to 
symbols with external linkage only; not applied to 'l2'}}
+  int l2;
+  return local+l2;
+}
+
+int local = 2;
+int l2 =4;
+
diff --git a/clang/test/Sema/pragma-export-failing.cpp 
b/clang/test/Sema/pragma-export-failing.cpp
new file mode 100644
index 0000000000000..908395c899cf5
--- /dev/null
+++ b/clang/test/Sema/pragma-export-failing.cpp
@@ -0,0 +1,25 @@
+// REQUIRES: systemz-registered-target
+// RUN: %clang_cc1 -x c++ -triple s390x-none-zos -fzos-extensions %s 
-fsyntax-only -verify
+
+#pragma export(f0(int))                    // expected-warning{{failed to 
resolve '#pragma export' to a declaration}}
+#pragma export(f3(double, double, double)) // expected-warning{{failed to 
resolve '#pragma export' to a declaration}}
+
+#pragma export(N::sf1(void)) // expected-warning{{#pragma export is applicable 
to symbols with external linkage only; not applied to 'sf1'}}
+#pragma export(N::s1) // expected-warning{{#pragma export is applicable to 
symbols with external linkage only; not applied to 's1'}}
+namespace N {
+static void sf1(void) {}
+static int s1;
+
+static void sf0(void) {}
+int v0;
+static int s0;
+}
+#pragma export(N::sf0(void)) // expected-warning{{#pragma export is applicable 
to symbols with external linkage only; not applied to 'sf0'}}
+#pragma export(N::s0) // expected-warning{{#pragma export is applicable to 
symbols with external linkage only; not applied to 's0'}}
+
+void f10(int);
+#pragma export(f10) // expected-warning{{failed to resolve '#pragma export' to 
a declaration}}
+
+#pragma export(f11) // expected-warning{{failed to resolve '#pragma export' to 
a declaration}}
+void f11(int);
+

>From c3bdc3a24b5e73c770f7f3a0801dc4a64ac8205f Mon Sep 17 00:00:00 2001
From: Sean Perry <pe...@ca.ibm.com>
Date: Thu, 26 Jun 2025 20:30:22 +0000
Subject: [PATCH 2/3] only accept export(id)

---
 clang/include/clang/Sema/Sema.h           |  15 +--
 clang/lib/Parse/ParsePragma.cpp           |  20 +--
 clang/lib/Sema/Sema.cpp                   |   9 +-
 clang/lib/Sema/SemaAttr.cpp               | 142 +---------------------
 clang/lib/Sema/SemaDecl.cpp               |  82 ++-----------
 clang/test/.clang-format-ignore           |   0
 clang/test/CodeGen/pragma-export.cpp      | 107 ++++------------
 clang/test/Parser/pragma-export.cpp       |  17 +--
 clang/test/Sema/pragma-export-failing.cpp |  12 +-
 9 files changed, 64 insertions(+), 340 deletions(-)
 create mode 100644 clang/test/.clang-format-ignore

diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 3eab936e899a6..14180655aa801 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -2282,12 +2282,8 @@ class Sema final : public SemaBase {
   /// haven't seen the declaration for yet. The TypeList is the argument list
   /// the function must match if HasTypeList is true.
   struct SymbolLabel {
-    std::optional<SmallVector<QualType, 4>> TypeList;
     SourceLocation NameLoc;
-    bool HasTypeList;
-    Qualifiers CVQual;
-    NestedNameSpecifier
-        *NestedNameId; // Nested name identifier for type lookup.
+    IdentifierInfo *IdentId;
     bool Used;
   };
 
@@ -2301,15 +2297,10 @@ class Sema final : public SemaBase {
   bool isNamedDeclSameAsSymbolLabel(NamedDecl *D,
                                     clang::Sema::SymbolLabel &Label);
 
-  typedef SmallVector<SymbolLabel, 1> PendingPragmaExportOverloads;
-  llvm::DenseMap<IdentifierInfo *, PendingPragmaExportOverloads>
-      PendingExportedNames;
+  llvm::DenseMap<IdentifierInfo *, SymbolLabel> PendingExportedNames;
 
   /// ActonPragmaExport - called on well-formed '\#pragma export'.
-  void ActOnPragmaExport(NestedNameSpecifier *NestedId,
-                         SourceLocation ExportNameLoc,
-                         std::optional<SmallVector<QualType, 4>> &&TypeList,
-                         Qualifiers CVQual);
+  void ActOnPragmaExport(IdentifierInfo *IdentId, SourceLocation 
ExportNameLoc);
 
   /// Only called on function definitions; if there is a pragma in scope
   /// with the effect of a range-based optnone, consider marking the function
diff --git a/clang/lib/Parse/ParsePragma.cpp b/clang/lib/Parse/ParsePragma.cpp
index ac8f37c5e4c4c..b57e0d2549120 100644
--- a/clang/lib/Parse/ParsePragma.cpp
+++ b/clang/lib/Parse/ParsePragma.cpp
@@ -1512,8 +1512,7 @@ bool Parser::zOSHandlePragmaHelper(tok::TokenKind 
PragmaKind) {
 
     // C++ could have a nested name, or be qualified with ::.
     PP.Lex(Tok);
-    if (Tok.isNot(tok::identifier) &&
-        !(PP.getLangOpts().CPlusPlus && Tok.is(tok::coloncolon))) {
+    if (Tok.isNot(tok::identifier)) {
       PP.Diag(Tok.getLocation(), diag::warn_pragma_expected_identifier)
           << PragmaName;
       return false;
@@ -1521,19 +1520,7 @@ bool Parser::zOSHandlePragmaHelper(tok::TokenKind 
PragmaKind) {
 
     IdentifierInfo *IdentName = Tok.getIdentifierInfo();
     SourceLocation IdentNameLoc = Tok.getLocation();
-    NestedNameSpecifier *NestedId = zOSParseIdentifier(PragmaName, IdentName);
-    if (!NestedId)
-      return false;
-
-    // C++ can have a paramater list for overloaded functions.
-    // Try to parse the argument types.
-    std::optional<SmallVector<QualType, 4>> TypeList;
-    Qualifiers CVQual;
-
-    if (PP.getLangOpts().CPlusPlus && Tok.is(tok::l_paren)) {
-      if (!zOSParseParameterList(PragmaName, TypeList, CVQual))
-        return false;
-    }
+    PP.Lex(Tok);
 
     if (Tok.isNot(tok::r_paren)) {
       PP.Diag(Tok.getLocation(), diag::warn_pragma_expected_rparen)
@@ -1542,8 +1529,7 @@ bool Parser::zOSHandlePragmaHelper(tok::TokenKind 
PragmaKind) {
     }
 
     PP.Lex(Tok);
-    Actions.ActOnPragmaExport(NestedId, IdentNameLoc, std::move(TypeList),
-                              CVQual);
+    Actions.ActOnPragmaExport(IdentName, IdentNameLoc);
 
     // Because export is also a C++ keyword, we also check for that.
     if (Tok.is(tok::identifier) || Tok.is(tok::kw_export)) {
diff --git a/clang/lib/Sema/Sema.cpp b/clang/lib/Sema/Sema.cpp
index 0f3af893343f2..86a60a8b87ef1 100644
--- a/clang/lib/Sema/Sema.cpp
+++ b/clang/lib/Sema/Sema.cpp
@@ -1478,10 +1478,11 @@ void Sema::ActOnEndOfTranslationUnit() {
   }
 
   // Visit all pending #pragma export.
-  for (auto &Iter : PendingExportedNames)
-    for (auto &Exported : Iter.second)
-      if (!Exported.Used)
-        Diag(Exported.NameLoc, diag::warn_failed_to_resolve_pragma) << 
"export";
+  for (auto &Iter : PendingExportedNames) {
+    auto &Exported = Iter.second;
+    if (!Exported.Used)
+      Diag(Exported.NameLoc, diag::warn_failed_to_resolve_pragma) << "export";
+  }
 
   if (LangOpts.HLSL)
     HLSL().ActOnEndOfTranslationUnit(getASTContext().getTranslationUnitDecl());
diff --git a/clang/lib/Sema/SemaAttr.cpp b/clang/lib/Sema/SemaAttr.cpp
index de00dad5a17a7..e1aa29af84c38 100644
--- a/clang/lib/Sema/SemaAttr.cpp
+++ b/clang/lib/Sema/SemaAttr.cpp
@@ -1328,146 +1328,14 @@ void 
Sema::AddImplicitMSFunctionNoBuiltinAttr(FunctionDecl *FD) {
     FD->addAttr(NoBuiltinAttr::CreateImplicit(Context, V.data(), V.size()));
 }
 
-static QualType getCanonicalParamType(ASTContext &C, QualType T) {
-  return C.getCanonicalParamType(T);
-}
-
-bool Sema::typeListMatchesSymbolLabel(FunctionDecl *FD,
-                                      const clang::Sema::SymbolLabel &Label) {
-  assert(Label.TypeList.has_value());
-  if (FD->getNumParams() != Label.TypeList->size()) {
-    return false;
-  }
-
-  // Check if arguments match.
-  for (unsigned i = 0; i != FD->getNumParams(); ++i) {
-    const ParmVarDecl *PVD = FD->getParamDecl(i);
-    QualType ParmType = PVD->getType().getCanonicalType();
-
-    QualType MapArgType =
-        getCanonicalParamType(Context, 
(*Label.TypeList)[i].getCanonicalType());
-
-    if (ParmType != MapArgType)
-      return false;
-  }
-
-  if (isa<CXXMethodDecl>(FD)) {
-    // Check if CV qualifiers match.
-    const clang::CXXMethodDecl *const MFD =
-        clang::cast<clang::CXXMethodDecl>(FD);
-    if (MFD && (MFD->isConst() != Label.CVQual.hasConst() ||
-                MFD->isVolatile() != Label.CVQual.hasVolatile())) {
-      return false;
-    }
-  } else if (Label.CVQual.hasConst() || Label.CVQual.hasVolatile())
-    return false;
-
-  return true;
-}
-
-NamedDecl *Sema::tryLookupSymbolLabel(const clang::Sema::SymbolLabel &Label) {
-
-  NestedNameSpecifier *NestedName = Label.NestedNameId;
-  assert(!NestedName->getPrefix() ||
-         NestedName->getPrefix()->getKind() == 
NestedNameSpecifier::Identifier);
-  IdentifierInfo *Prefix =
-      NestedName->getPrefix() ? NestedName->getPrefix()->getAsIdentifier() : 0;
-  IdentifierInfo *Name = NestedName->getAsIdentifier();
-  LookupResult Result(*this, (Prefix ? Prefix : Name), Label.NameLoc,
-                      LookupOrdinaryName);
-  LookupName(Result, TUScope);
-
-  // Filter down to just a function, namespace or class.
-  LookupResult::Filter F = Result.makeFilter();
-  while (F.hasNext()) {
-    NamedDecl *D = F.next();
-    if (!(isa<FunctionDecl>(D) || isa<VarDecl>(D) || isa<NamespaceDecl>(D) ||
-          isa<CXXRecordDecl>(D)))
-      F.erase();
-  }
-  F.done();
-
-  auto MatchDecl = [this, Name, Label](DeclContext *DC) -> NamedDecl * {
-    auto LRes = DC->lookup(DeclarationName(Name));
-    for (auto *I : LRes) {
-      if (isa<VarDecl>(I))
-        return I;
-      if (isa<FunctionDecl>(I)) {
-        FunctionDecl *FD = dyn_cast<FunctionDecl>(I);
-
-        // All function parameters must match if specified in pragma otherwise,
-        // we accept a function found by lookup only if it's the only one.
-        if ((Label.TypeList.has_value() &&
-             typeListMatchesSymbolLabel(FD, Label)) ||
-            (!Label.TypeList.has_value() && LRes.isSingleResult()))
-          return FD;
-      }
-    }
-    return nullptr;
-  };
-
-  // global variable or function in a namespace.
-  if (NamespaceDecl *ND = Result.getAsSingle<NamespaceDecl>()) {
-    if (ND->getIdentifierNamespace() == Decl::IDNS_Namespace) {
-      return MatchDecl(ND);
-    }
-  }
-
-  // data or function member.
-  if (CXXRecordDecl *RD = Result.getAsSingle<CXXRecordDecl>()) {
-    return MatchDecl(RD);
-  }
-
-  // either a variable, or a non-overloaded function, or an overloaded
-  // function with extern "C" linkage.
-  if (!Label.TypeList.has_value()) {
-    if (Result.isSingleResult()) {
-      NamedDecl *ND = Result.getFoundDecl();
-      if (isa<VarDecl>(ND))
-        return ND;
-      if (FunctionDecl *FD = dyn_cast<FunctionDecl>(ND)) {
-        if (!getLangOpts().CPlusPlus || FD->isExternC())
-          return FD;
-        else
-          return nullptr;
-      }
-      return ND;
-    }
-    if (Result.isOverloadedResult()) {
-      for (auto *Iter : Result) {
-        FunctionDecl *FD = dyn_cast<FunctionDecl>(Iter);
-        if (FD && FD->isExternC())
-          return FD;
-      }
-      return nullptr;
-    }
-    return nullptr;
-  }
-
-  // Loop over all the found decls and see if the arguments match
-  // any of the results.
-  for (LookupResult::iterator I = Result.begin(); I != Result.end(); ++I) {
-    NamedDecl *ND = (*I)->getUnderlyingDecl();
-    FunctionDecl *FD = dyn_cast<FunctionDecl>(ND);
-    if (FD && typeListMatchesSymbolLabel(FD, Label)) {
-      return FD;
-    }
-  }
-  return nullptr;
-}
-
-void Sema::ActOnPragmaExport(NestedNameSpecifier *NestedId,
-                             SourceLocation NameLoc,
-                             std::optional<SmallVector<QualType, 4>> 
&&TypeList,
-                             Qualifiers CVQual) {
+void Sema::ActOnPragmaExport(IdentifierInfo *IdentId, SourceLocation NameLoc) {
   SymbolLabel Label;
+  Label.IdentId = IdentId;
   Label.NameLoc = NameLoc;
-  Label.CVQual = CVQual;
-  Label.TypeList = std::move(TypeList);
-  Label.NestedNameId = NestedId;
   Label.Used = false;
 
-  NamedDecl *PrevDecl = tryLookupSymbolLabel(Label);
+  NamedDecl *PrevDecl =
+      LookupSingleName(TUScope, IdentId, NameLoc, LookupOrdinaryName);
   if (PrevDecl && (isa<FunctionDecl>(PrevDecl) || isa<VarDecl>(PrevDecl))) {
     if (PrevDecl->hasExternalFormalLinkage()) {
       if (auto *FD = dyn_cast<FunctionDecl>(PrevDecl)) {
@@ -1490,7 +1358,7 @@ void Sema::ActOnPragmaExport(NestedNameSpecifier 
*NestedId,
     Label.Used = true;
   }
   if (!Label.Used) {
-    PendingExportedNames[NestedId->getAsIdentifier()].push_back(Label);
+    PendingExportedNames[IdentId] = Label;
   }
 }
 
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index 98ad21314285a..e1dc77b1108bd 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -7519,73 +7519,13 @@ bool Sema::isNamedDeclSameAsSymbolLabel(NamedDecl *D,
   const DeclContext *Ctx = D->getDeclContext();
 
   // Check the name.
-  NestedNameSpecifier *NS = Label.NestedNameId;
-  if (NS->getAsIdentifier()->getName() != D->getIdentifier()->getName())
+  if (Label.IdentId != D->getIdentifier())
     return false;
-  NS = NS->getPrefix();
-
-  if (NS) {
-    // For ObjC methods and properties, look through categories and use the
-    // interface as context.
-    if (auto *MD = dyn_cast<ObjCMethodDecl>(D)) {
-      if (auto *ID = MD->getClassInterface())
-        Ctx = ID;
-    } else if (auto *PD = dyn_cast<ObjCPropertyDecl>(D)) {
-      if (auto *MD = PD->getGetterMethodDecl())
-        if (auto *ID = MD->getClassInterface())
-          Ctx = ID;
-    } else if (auto *ID = dyn_cast<ObjCIvarDecl>(D)) {
-      if (auto *CI = ID->getContainingInterface())
-        Ctx = CI;
-    }
-
-    // Check named contexts.
-    if (Ctx->isFunctionOrMethod())
-      return false;
-
-    DeclarationName NameInScope = D->getDeclName();
-    for (; NS && Ctx; Ctx = Ctx->getParent()) {
-      // Suppress anonymous namespace.
-      if (isa<NamespaceDecl>(Ctx) &&
-          cast<NamespaceDecl>(Ctx)->isAnonymousNamespace())
-        continue;
-
-      // Suppress inline namespace if it doesn't make the result ambiguous.
-      if (Ctx->isInlineNamespace() && NameInScope &&
-          cast<NamespaceDecl>(Ctx)->isRedundantInlineQualifierFor(NameInScope))
-        continue;
 
-      // Skip non-named contexts such as linkage specifications and 
ExportDecls.
-      const NamedDecl *ND = dyn_cast<NamedDecl>(Ctx);
-      if (!ND)
-        continue;
-
-      // Fail if the sequence of nested name identifiers is shorter.
-      if (!NS)
-        return false;
-
-      // Fail if the names are not equal.
-      if (NS->getAsIdentifier()->getName() != ND->getIdentifier()->getName())
-        return false;
-
-      NameInScope = ND->getDeclName();
-      NS = NS->getPrefix();
-    }
-
-    // Fail if the sequence of nested name identifiers is longer.
-    // It makes sure that both lists have the same length.
-    if (NS)
-      return false;
-  }
-
-  if (isa<VarDecl>(D) && !Label.TypeList.has_value())
+  if (isa<VarDecl>(D))
     return true;
   if (FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) {
     // All function parameters match if specified in pragma.
-    if (Label.TypeList.has_value())
-      return typeListMatchesSymbolLabel(FD, Label);
-    // There might be overloaded functions. However, with the available
-    // information it cn only be concluded that the functions are the same.
     if (!getLangOpts().CPlusPlus || FD->isExternC())
       return true;
   }
@@ -7601,17 +7541,13 @@ void Sema::ProcessPragmaExport(DeclaratorDecl *NewD) {
     return;
   auto PendingName = PendingExportedNames.find(IdentName);
   if (PendingName != PendingExportedNames.end()) {
-    for (auto I = PendingName->second.begin(), E = PendingName->second.end();
-         I != E; ++I) {
-      auto &Label = *I;
-      if (!Label.Used && isNamedDeclSameAsSymbolLabel(NewD, Label)) {
-        Label.Used = true;
-        if (NewD->hasExternalFormalLinkage())
-          mergeVisibilityType(NewD, Label.NameLoc, VisibilityAttr::Default);
-        else
-          Diag(Label.NameLoc, diag::warn_pragma_not_applied)
-              << "export" << NewD;
-      }
+    auto &Label = PendingName->second;
+    if (!Label.Used && isNamedDeclSameAsSymbolLabel(NewD, Label)) {
+      Label.Used = true;
+      if (NewD->hasExternalFormalLinkage())
+        mergeVisibilityType(NewD, Label.NameLoc, VisibilityAttr::Default);
+      else
+        Diag(Label.NameLoc, diag::warn_pragma_not_applied) << "export" << NewD;
     }
   }
 }
diff --git a/clang/test/.clang-format-ignore b/clang/test/.clang-format-ignore
new file mode 100644
index 0000000000000..e69de29bb2d1d
diff --git a/clang/test/CodeGen/pragma-export.cpp 
b/clang/test/CodeGen/pragma-export.cpp
index aa780887bd272..6f5a025e23bd5 100644
--- a/clang/test/CodeGen/pragma-export.cpp
+++ b/clang/test/CodeGen/pragma-export.cpp
@@ -2,84 +2,46 @@
 // RUN: %clang_cc1 -x c++ %s -emit-llvm -triple s390x-none-zos 
-fzos-extensions -fvisibility=hidden -o - | FileCheck %s
 
 // Testing pragma export after decl.
-void f0(void) {}
+extern "C" void f0(void) {}
 int v0;
-#pragma export(f0(void))
+#pragma export(f0)
 #pragma export(v0)
 
 // Testing pragma export before decl.
-#pragma export(f1(void))
+#pragma export(f1)
 #pragma export(v1)
-void f1(void) {}
+extern "C" void f1(void) {}
 int v1;
 
 // Testing overloaded functions.
-#pragma export(f2(double, double))
-#pragma export(f2(int))
+#pragma export(f2)
 void f2(double, double) {}
-void f2(int) {}
+extern "C" void f2(int) {}
 void f2(int, int) {}
 
-void f3(double) {}
+extern "C" void f3(double) {}
 void f3(int, double) {}
 void f3(double, double) {}
-#pragma export(f3(double))
-#pragma export(f3(int, double))
+#pragma export(f3)
 
-void f2(void) {}
+extern "C" void f2b(void) {}
 
 void t0(void) {
-  f2();
+  f2b();
 }
 
-// Test type decay in arguments
-
-#pragma export(fd1(int[]))
-#pragma export(fd2(int*))
-#pragma export(fd3(int[]))
-#pragma export(fd4(int*))
-void fd1(int []) { }
-void fd2(int []) { }
-void fd3(int *) { }
-void fd4(int *) { }
-
-
-#pragma export (fd5(int ()))
-#pragma export (fd6(int (*)()))
-#pragma export (fd7(int ()))
-#pragma export (fd8(int (*)()))
-void fd5(int ()) {}
-void fd6(int ()) {}
-void fd7(int (*)()) {}
-void fd8(int (*)()) {}
-
-
 // Testing pragma export after decl and usage.
-#pragma export(f2(void))
+#pragma export(f2b)
 
 // Testing pragma export with namespace.
-void f5(void) {}
-void f5a(void) {}
-#pragma export(N0::f2a(void))
+extern "C" void f5(void) {}
+extern "C" void f5a(void) {}
 namespace N0 {
-void f0(void) {}
-void f1(void) {}
-void f2(void) {}
-void f3(void) {}
 void f5(void) {}
-#pragma export(f0(void))
-#pragma export(N0::f1(void))
-#pragma export(f5(void))
-#pragma export(f0a(void))
-#pragma export(N0::f1a(void))
-#pragma export(f5a(void))
-void f0a(void) {}
-void f1a(void) {}
-void f2a(void) {}
-void f3a(void) {}
+#pragma export(f5)
+#pragma export(f5a)
 void f5a(void) {}
 } // namespace N0
-#pragma export(N0::f2(void))
 
 void f10(int);
 #pragma export(f10)
@@ -88,35 +50,20 @@ void f10(int) {}
 
 // CHECK: @v0 = hidden global i32 0
 // CHECK: @v1 = global i32 0
-// CHECK: define hidden void @_Z2f0v()
-// CHECK: define void @_Z2f1v()
-// CHECK: define void @_Z2f2dd(double noundef %0, double noundef %1)
-// CHECK: define void @_Z2f2i(i32 noundef signext %0)
+// CHECK: define hidden void @f0()
+// CHECK: define void @f1()
+// CHECK: define hidden void @_Z2f2dd(double noundef %0, double noundef %1)
+// CHECK: define void @f2(i32 noundef signext %0)
 // CHECK: define hidden void @_Z2f2ii(i32 noundef signext %0, i32 noundef 
signext %1)
-// CHECK: define hidden void @_Z2f3d(double noundef %0)
+// CHECK: define hidden void @f3(double noundef %0)
 // CHECK: define hidden void @_Z2f3id(i32 noundef signext %0, double noundef 
%1)
 // CHECK: define hidden void @_Z2f3dd(double noundef %0, double noundef %1)
-// CHECK: define hidden void @_Z2f2v()
+// CHECK: define hidden void @f2b()
 // CHECK: define hidden void @_Z2t0v()
-// CHECK: define void @_Z3fd1Pi(ptr noundef %0)
-// CHECK: define void @_Z3fd2Pi(ptr noundef %0)
-// CHECK: define void @_Z3fd3Pi(ptr noundef %0)
-// CHECK: define void @_Z3fd4Pi(ptr noundef %0)
-// CHECK: define void @_Z3fd5PFivE(ptr noundef %0)
-// CHECK: define void @_Z3fd6PFivE(ptr noundef %0)
-// CHECK: define void @_Z3fd7PFivE(ptr noundef %0)
-// CHECK: define void @_Z3fd8PFivE(ptr noundef %0)
-// CHECK: define hidden void @_Z2f5v()
-// CHECK: define hidden void @_Z3f5av()
-// CHECK: define hidden void @_ZN2N02f0Ev()
-// CHECK: define hidden void @_ZN2N02f1Ev()
-// CHECK: define hidden void @_ZN2N02f2Ev()
-// CHECK: define hidden void @_ZN2N02f3Ev()
+// CHECK: define hidden void @f5()
+// CHECK: define hidden void @f5a()
 // CHECK: define hidden void @_ZN2N02f5Ev()
-// CHECK: define void @_ZN2N03f0aEv()
-// CHECK: define hidden void @_ZN2N03f1aEv()
-// CHECK: define void @_ZN2N03f2aEv()
-// CHECK: define hidden void @_ZN2N03f3aEv()
-// CHECK: define void @_ZN2N03f5aEv()
-// CHECK: define void @f10(double noundef %0) #0 {
-// CHECK: define hidden void @_Z3f10i(i32 noundef signext %0) #0 {
+// CHECK: define hidden void @_ZN2N03f5aEv()
+// CHECK: define hidden void @f10(double noundef %0)
+// CHECK: define void @_Z3f10i(i32 noundef signext %0)
+
diff --git a/clang/test/Parser/pragma-export.cpp 
b/clang/test/Parser/pragma-export.cpp
index 5f1656041b3d3..91d2e162bcfec 100644
--- a/clang/test/Parser/pragma-export.cpp
+++ b/clang/test/Parser/pragma-export.cpp
@@ -1,21 +1,16 @@
 // RUN: %clang_cc1 -x c++ -triple s390x-ibm-zos -fsyntax-only -verify %s
 
 extern int i;
-#pragma export(:: // expected-warning {{expected identifier in '#pragma 
export' - ignored}}
-#pragma export(::) // expected-warning {{expected identifier in '#pragma 
export' - ignored}}
-#pragma export(::i)
+#pragma export( // expected-warning {{expected identifier in '#pragma export' 
- ignored}}
+#pragma export() // expected-warning {{expected identifier in '#pragma export' 
- ignored}}
+#pragma export(i)
 
 struct S {
   static int i;
 };
-#pragma export(S:: // expected-warning {{expected identifier in '#pragma 
export' - ignored}}
-#pragma export(S::i // expected-warning {{missing ')' after '#pragma export' - 
ignoring}}
-#pragma export(S::i)
+#pragma export(S::i) // expected-warning {{missing ')' after '#pragma export' 
- ignoring}}
 
 void f(int);
 void f(double, double);
-#pragma export(f( // expected-warning {{expected identifier in '#pragma 
export' - ignored}}
-#pragma export(f() // expected-warning {{missing ')' after '#pragma export' - 
ignoring}}
-#pragma export(f(int) // expected-warning {{missing ')' after '#pragma export' 
- ignoring}}
-#pragma export(f(double,) // expected-warning {{missing ')' after '#pragma 
export' - ignoring}}
-#pragma export(f(double,double))
+#pragma export(f // expected-warning {{missing ')' after '#pragma export' - 
ignoring}}
+#pragma export(f( // expected-warning {{missing ')' after '#pragma export' - 
ignoring}}
diff --git a/clang/test/Sema/pragma-export-failing.cpp 
b/clang/test/Sema/pragma-export-failing.cpp
index 908395c899cf5..9763adcff5f16 100644
--- a/clang/test/Sema/pragma-export-failing.cpp
+++ b/clang/test/Sema/pragma-export-failing.cpp
@@ -1,11 +1,11 @@
 // REQUIRES: systemz-registered-target
 // RUN: %clang_cc1 -x c++ -triple s390x-none-zos -fzos-extensions %s 
-fsyntax-only -verify
 
-#pragma export(f0(int))                    // expected-warning{{failed to 
resolve '#pragma export' to a declaration}}
-#pragma export(f3(double, double, double)) // expected-warning{{failed to 
resolve '#pragma export' to a declaration}}
+#pragma export(f0(int))                    // expected-warning{{missing ')' 
after '#pragma export' - ignoring}}
+#pragma export(f3(double, double, double)) // expected-warning{{missing ')' 
after '#pragma export' - ignoring}}
 
-#pragma export(N::sf1(void)) // expected-warning{{#pragma export is applicable 
to symbols with external linkage only; not applied to 'sf1'}}
-#pragma export(N::s1) // expected-warning{{#pragma export is applicable to 
symbols with external linkage only; not applied to 's1'}}
+#pragma export(N::sf1(void)) // expected-warning{{missing ')' after '#pragma 
export' - ignoring}}
+#pragma export(N::s1) // expected-warning{{missing ')' after '#pragma export' 
- ignoring}}
 namespace N {
 static void sf1(void) {}
 static int s1;
@@ -14,8 +14,8 @@ static void sf0(void) {}
 int v0;
 static int s0;
 }
-#pragma export(N::sf0(void)) // expected-warning{{#pragma export is applicable 
to symbols with external linkage only; not applied to 'sf0'}}
-#pragma export(N::s0) // expected-warning{{#pragma export is applicable to 
symbols with external linkage only; not applied to 's0'}}
+#pragma export(N::sf0(void)) // expected-warning{{missing ')' after '#pragma 
export' - ignoring}}
+#pragma export(N::s0) // expected-warning{{missing ')' after '#pragma export' 
- ignoring}}
 
 void f10(int);
 #pragma export(f10) // expected-warning{{failed to resolve '#pragma export' to 
a declaration}}

>From 66177a45b005e8f9d00db19b4db38de677e5e455 Mon Sep 17 00:00:00 2001
From: Sean Perry <pe...@ca.ibm.com>
Date: Thu, 26 Jun 2025 20:44:07 +0000
Subject: [PATCH 3/3] update release notes

---
 clang/docs/ReleaseNotes.rst | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 91e720db67547..8d1605ee789e6 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -1005,7 +1005,9 @@ AVR Support
 SystemZ Support
 ^^^^^^^^^^^^^^^
 
-- Add support for `#pragma export` for z/OS
+- Add support for `#pragma export` for z/OS.  This is a pragma used to export 
function and variable
+  with external linkage from shared libraries.  It provides compatibility with 
the IBM XL C/C++
+  compiler.
 
 DWARF Support in Clang
 ----------------------

_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to