[clang] [SystemZ][z/OS] Add visibility features for z/OS (eg. _Export, pragma export) (PR #111035)

Sean Perry via cfe-commits cfe-commits at lists.llvm.org
Fri Apr 4 12:15:52 PDT 2025


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

>From e8d355c9cd165e0a255bbbfb5b0126cf7b1461a6 Mon Sep 17 00:00:00 2001
From: Sean Perry <perry at ca.ibm.com>
Date: Wed, 2 Oct 2024 12:56:43 -0500
Subject: [PATCH 01/15] initial work for pragma export & _Export keyword

---
 clang/include/clang/AST/ASTConsumer.h         |   3 +
 clang/include/clang/Basic/Attr.td             |   6 +
 clang/include/clang/Basic/AttrDocs.td         |  21 ++
 .../clang/Basic/DiagnosticSemaKinds.td        |   7 +
 clang/include/clang/Basic/TokenKinds.def      |   5 +
 clang/include/clang/Parse/Parser.h            |  13 ++
 clang/include/clang/Sema/DeclSpec.h           |  31 ++-
 clang/include/clang/Sema/Sema.h               |  30 +++
 clang/lib/CodeGen/BackendConsumer.h           |   1 +
 clang/lib/CodeGen/CodeGenAction.cpp           |   4 +
 clang/lib/CodeGen/CodeGenModule.cpp           |  15 ++
 clang/lib/CodeGen/CodeGenModule.h             |   2 +
 clang/lib/CodeGen/ModuleBuilder.cpp           |   6 +-
 clang/lib/Driver/ToolChains/ZOS.cpp           |  12 +-
 clang/lib/Parse/ParseDecl.cpp                 |  18 ++
 clang/lib/Parse/ParseDeclCXX.cpp              |   6 +
 clang/lib/Parse/ParsePragma.cpp               | 208 ++++++++++++++++++
 clang/lib/Parse/Parser.cpp                    |   3 +
 clang/lib/Sema/DeclSpec.cpp                   |   6 +
 clang/lib/Sema/Sema.cpp                       |  21 ++
 clang/lib/Sema/SemaAttr.cpp                   | 162 ++++++++++++++
 clang/lib/Sema/SemaDecl.cpp                   |  13 ++
 clang/test/CodeGen/attr-export-failing.cpp    |   4 +
 clang/test/CodeGen/attr-export.cpp            |  51 +++++
 clang/test/CodeGen/pragma-export.c            |  39 ++++
 clang/test/CodeGen/pragma-export.cpp          |  82 +++++++
 clang/test/CodeGen/zos-pragmas.c              |  11 +
 clang/test/CodeGen/zos-pragmas.cpp            |  11 +
 28 files changed, 784 insertions(+), 7 deletions(-)
 create mode 100644 clang/test/CodeGen/attr-export-failing.cpp
 create mode 100644 clang/test/CodeGen/attr-export.cpp
 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

diff --git a/clang/include/clang/AST/ASTConsumer.h b/clang/include/clang/AST/ASTConsumer.h
index 447f2592d2359..b15d53e700c67 100644
--- a/clang/include/clang/AST/ASTConsumer.h
+++ b/clang/include/clang/AST/ASTConsumer.h
@@ -108,6 +108,9 @@ class ASTConsumer {
   /// completed.
   virtual void CompleteExternalDeclaration(DeclaratorDecl *D) {}
 
+  /// CompletePragmaExport - complete #pragma export statements.
+  virtual void CompletePragmaExport(Decl *D) {}
+
   /// Callback invoked when an MSInheritanceAttr has been attached to a
   /// CXXRecordDecl.
   virtual void AssignInheritanceModel(CXXRecordDecl *RD) {}
diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td
index fbcbf0ed41641..884c4147cf128 100644
--- a/clang/include/clang/Basic/Attr.td
+++ b/clang/include/clang/Basic/Attr.td
@@ -4520,6 +4520,12 @@ def ReleaseHandle : InheritableParamAttr {
   let Documentation = [ReleaseHandleDocs];
 }
 
+def zOSExport : InheritableAttr {
+  let Spellings = [CustomKeyword<"_Export">];
+  let Subjects = SubjectList<[Function, Var, CXXRecord]>;
+  let Documentation = [zOSExportDocs];
+}
+
 def UnsafeBufferUsage : InheritableAttr {
   let Spellings = [Clang<"unsafe_buffer_usage">];
   let Subjects = SubjectList<[Function, Field]>;
diff --git a/clang/include/clang/Basic/AttrDocs.td b/clang/include/clang/Basic/AttrDocs.td
index 53d88482698f0..bf56fa6ad7162 100644
--- a/clang/include/clang/Basic/AttrDocs.td
+++ b/clang/include/clang/Basic/AttrDocs.td
@@ -6863,6 +6863,27 @@ attribute requires a string literal argument to identify the handle being releas
   }];
 }
 
+def zOSExportDocs : Documentation {
+  let Category = DocCatFunction;
+  let Content = [{
+Use the _Export keyword with a function name or external variable to declare
+that it is to be exported (made available to other modules). You must define
+the object name in the same translation unit in which you use the _Export
+keyword. For example:
+
+.. code-block:: c
+
+  int _Export anthony(float);
+
+This statement exports the function anthony, if you define the function in the
+translation unit. The _Export keyword must immediately precede the object name.
+If you apply the _Export keyword to a class, the compiler automatically exports
+all static data members and member functions of the class. However, if you want
+it to apply to individual class members, then you must apply it to each member
+that can be referenced.
+  }];
+}
+
 def UnsafeBufferUsageDocs : Documentation {
   let Category = DocCatFunction;
   let Content = [{
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 64e6d0407b0ce..09842ed02efd4 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -1129,6 +1129,13 @@ 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 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 c5c3838407cf4..afef3b84a6985 100644
--- a/clang/include/clang/Basic/TokenKinds.def
+++ b/clang/include/clang/Basic/TokenKinds.def
@@ -345,6 +345,8 @@ KEYWORD(__func__                    , KEYALL)
 KEYWORD(__objc_yes                  , KEYALL)
 KEYWORD(__objc_no                   , KEYALL)
 
+// z/OS specific keywords
+KEYWORD(_Export                     , KEYZOS)
 
 // C++ 2.11p1: Keywords.
 KEYWORD(asm                         , KEYCXX|KEYGNU)
@@ -1003,6 +1005,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 eb8a851da7e04..eee7009b8615a 100644
--- a/clang/include/clang/Parse/Parser.h
+++ b/clang/include/clang/Parse/Parser.h
@@ -220,6 +220,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;
 
   std::unique_ptr<CommentHandler> CommentSemaHandler;
@@ -854,6 +855,18 @@ class Parser : public CodeCompletionHandler {
 
   void HandlePragmaAttribute();
 
+  /// Helper functions for handling zOS pragmas.
+  NestedNameSpecifier *zOSParseIdentifier(StringRef PragmaName,
+                                          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();
+
   /// GetLookAheadToken - This peeks ahead N tokens and returns that token
   /// without consuming any tokens.  LookAhead(0) returns 'Tok', LookAhead(1)
   /// returns the token after Tok, etc.
diff --git a/clang/include/clang/Sema/DeclSpec.h b/clang/include/clang/Sema/DeclSpec.h
index 06243f2624876..f41748af5c830 100644
--- a/clang/include/clang/Sema/DeclSpec.h
+++ b/clang/include/clang/Sema/DeclSpec.h
@@ -398,6 +398,8 @@ class DeclSpec {
   unsigned FS_virtual_specified : 1;
   LLVM_PREFERRED_TYPE(bool)
   unsigned FS_noreturn_specified : 1;
+  LLVM_PREFERRED_TYPE(bool)
+  unsigned export_specified : 1;
 
   // friend-specifier
   LLVM_PREFERRED_TYPE(bool)
@@ -444,6 +446,7 @@ class DeclSpec {
   SourceLocation FS_forceinlineLoc;
   SourceLocation FriendLoc, ModulePrivateLoc, ConstexprLoc;
   SourceLocation TQ_pipeLoc;
+  SourceLocation exportLoc;
 
   WrittenBuiltinSpecs writtenBS;
   void SaveWrittenBuiltinSpecs();
@@ -492,7 +495,8 @@ class DeclSpec {
         TypeSpecPipe(false), TypeSpecSat(false), ConstrainedAuto(false),
         TypeQualifiers(TQ_unspecified), FS_inline_specified(false),
         FS_forceinline_specified(false), FS_virtual_specified(false),
-        FS_noreturn_specified(false), FriendSpecifiedFirst(false),
+        FS_noreturn_specified(false), export_specified(false),
+        FriendSpecifiedFirst(false),
         ConstexprSpecifier(
             static_cast<unsigned>(ConstexprSpecKind::Unspecified)),
         Attrs(attrFactory), writtenBS(), ObjCQualifiers(nullptr) {}
@@ -661,7 +665,10 @@ class DeclSpec {
   bool isNoreturnSpecified() const { return FS_noreturn_specified; }
   SourceLocation getNoreturnSpecLoc() const { return FS_noreturnLoc; }
 
-  void ClearFunctionSpecs() {
+  bool isExportSpecified() const { return export_specified; }
+  SourceLocation getExportSpecLoc() const { return exportLoc; }
+
+    void ClearFunctionSpecs() {
     FS_inline_specified = false;
     FS_inlineLoc = SourceLocation();
     FS_forceinline_specified = false;
@@ -811,6 +818,8 @@ class DeclSpec {
   bool setFunctionSpecNoreturn(SourceLocation Loc, const char *&PrevSpec,
                                unsigned &DiagID);
 
+  bool setExportSpec(SourceLocation Loc);
+
   bool SetFriendSpec(SourceLocation Loc, const char *&PrevSpec,
                      unsigned &DiagID);
   bool setModulePrivateSpec(SourceLocation Loc, const char *&PrevSpec,
@@ -1955,6 +1964,9 @@ class Declarator {
   LLVM_PREFERRED_TYPE(bool)
   unsigned InlineStorageUsed : 1;
 
+  /// Indicates whether this is set as _Export
+  unsigned ExportSpecified : 1;
+
   /// Indicates whether this declarator has an initializer.
   LLVM_PREFERRED_TYPE(bool)
   unsigned HasInitializer : 1;
@@ -2001,6 +2013,9 @@ class Declarator {
   /// this declarator as a parameter pack.
   SourceLocation EllipsisLoc;
 
+  /// The source location of the _Export keyword on this declarator
+  SourceLocation ExportLoc;
+
   Expr *PackIndexingExpr;
 
   friend struct DeclaratorChunk;
@@ -2109,6 +2124,18 @@ class Declarator {
       Range.setEnd(SR.getEnd());
   }
 
+  /// Set this declarator as _Export
+  void SetExport(SourceLocation Loc) {
+    ExportSpecified = true;
+    ExportLoc = Loc;
+  }
+
+  /// Whether this declarator is marked as _Export
+  bool IsExport() const { return ExportSpecified; }
+
+  /// Get the location of the _Export keyword
+  SourceLocation getExportLoc() const { return ExportLoc; }
+
   /// Reset the contents of this Declarator.
   void clear() {
     SS.clear();
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index d616c3834c429..ddb36138aae83 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -1974,6 +1974,36 @@ 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;
+    StringRef MappedName;
+    SourceLocation NameLoc;
+    bool HasTypeList;
+    Qualifiers CVQual;
+  };
+
+  typedef SmallVector<SymbolLabel, 1> PendingSymbolOverloads;
+  typedef llvm::DenseMap<NestedNameSpecifier *, PendingSymbolOverloads>
+      SymbolNames;
+  SymbolNames PendingExportNames;
+
+  FunctionDecl *tryFunctionLookUp(NestedNameSpecifier *NestedName,
+                                  SourceLocation NameLoc);
+
+  /// trySymbolLookUp try to look up a decl matching the nested specifier
+  /// with optional type list.
+  NamedDecl *trySymbolLookUp(NestedNameSpecifier *NestedName,
+                             const clang::Sema::SymbolLabel &Label);
+
+  /// 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.
diff --git a/clang/lib/CodeGen/BackendConsumer.h b/clang/lib/CodeGen/BackendConsumer.h
index a023d29cbd1d7..48ae73b4d25df 100644
--- a/clang/lib/CodeGen/BackendConsumer.h
+++ b/clang/lib/CodeGen/BackendConsumer.h
@@ -108,6 +108,7 @@ class BackendConsumer : public ASTConsumer {
   void HandleTagDeclRequiredDefinition(const TagDecl *D) override;
   void CompleteTentativeDefinition(VarDecl *D) override;
   void CompleteExternalDeclaration(DeclaratorDecl *D) override;
+  void CompletePragmaExport(Decl *D) override;
   void AssignInheritanceModel(CXXRecordDecl *RD) override;
   void HandleVTable(CXXRecordDecl *RD) override;
 
diff --git a/clang/lib/CodeGen/CodeGenAction.cpp b/clang/lib/CodeGen/CodeGenAction.cpp
index c9f9b688d0d8a..830f605fb6ad7 100644
--- a/clang/lib/CodeGen/CodeGenAction.cpp
+++ b/clang/lib/CodeGen/CodeGenAction.cpp
@@ -380,6 +380,10 @@ void BackendConsumer::CompleteExternalDeclaration(DeclaratorDecl *D) {
   Gen->CompleteExternalDeclaration(D);
 }
 
+void BackendConsumer::CompletePragmaExport(Decl *D) {
+  Gen->CompletePragmaExport(D);
+}
+
 void BackendConsumer::AssignInheritanceModel(CXXRecordDecl *RD) {
   Gen->AssignInheritanceModel(RD);
 }
diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp
index 25c1c496a4f27..599ec5bab83bc 100644
--- a/clang/lib/CodeGen/CodeGenModule.cpp
+++ b/clang/lib/CodeGen/CodeGenModule.cpp
@@ -5275,6 +5275,21 @@ void CodeGenModule::EmitExternalDeclaration(const DeclaratorDecl *D) {
     EmitExternalFunctionDeclaration(FD);
 }
 
+void CodeGenModule::EmitPragmaExport(const Decl *D) {
+  StringRef MangledName;
+  if (auto FD = dyn_cast<FunctionDecl>(D))
+    MangledName = getMangledName(GlobalDecl(FD));
+  else if (auto VD = dyn_cast<VarDecl>(D))
+    MangledName = getMangledName(GlobalDecl(VD));
+  else
+    assert(0 && "Unsupported pragma export Decl type");
+
+  if (llvm::GlobalValue *GV = GetGlobalValue(MangledName)) {
+    GV->setVisibility(llvm::GlobalValue::DefaultVisibility);
+    GV->setDSOLocal(false);
+  }
+}
+
 CharUnits CodeGenModule::GetTargetTypeStoreSize(llvm::Type *Ty) const {
   return Context.toCharUnitsFromBits(
       getDataLayout().getTypeStoreSizeInBits(Ty));
diff --git a/clang/lib/CodeGen/CodeGenModule.h b/clang/lib/CodeGen/CodeGenModule.h
index c58bb88035ca8..2e9a0617491f6 100644
--- a/clang/lib/CodeGen/CodeGenModule.h
+++ b/clang/lib/CodeGen/CodeGenModule.h
@@ -1364,6 +1364,8 @@ class CodeGenModule : public CodeGenTypeCache {
 
   void EmitExternalDeclaration(const DeclaratorDecl *D);
 
+  void EmitPragmaExport(const Decl *D);
+
   void EmitVTable(CXXRecordDecl *Class);
 
   void RefreshTypeCacheForClass(const CXXRecordDecl *Class);
diff --git a/clang/lib/CodeGen/ModuleBuilder.cpp b/clang/lib/CodeGen/ModuleBuilder.cpp
index d4e0ab0339a8b..7658d97af0184 100644
--- a/clang/lib/CodeGen/ModuleBuilder.cpp
+++ b/clang/lib/CodeGen/ModuleBuilder.cpp
@@ -314,7 +314,11 @@ namespace {
       Builder->EmitExternalDeclaration(D);
     }
 
-    void HandleVTable(CXXRecordDecl *RD) override {
+    void CompletePragmaExport(Decl *D) override {
+      Builder->EmitPragmaExport(D);
+    }
+
+  void HandleVTable(CXXRecordDecl *RD) override {
       if (Diags.hasUnrecoverableErrorOccurred())
         return;
 
diff --git a/clang/lib/Driver/ToolChains/ZOS.cpp b/clang/lib/Driver/ToolChains/ZOS.cpp
index 074e0556ecd2a..a1e7ddbe38908 100644
--- a/clang/lib/Driver/ToolChains/ZOS.cpp
+++ b/clang/lib/Driver/ToolChains/ZOS.cpp
@@ -37,6 +37,11 @@ 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");
+  }
+
   // Pass "-fno-sized-deallocation" only when the user hasn't manually enabled
   // or disabled sized deallocations.
   if (!DriverArgs.hasArgNoClaim(options::OPT_fsized_deallocation,
@@ -149,11 +154,10 @@ void zos::Linker::ConstructJob(Compilation &C, const JobAction &JA,
     StringRef OutputName = Output.getFilename();
     // Strip away the last file suffix in presence from output name and add
     // a new .x suffix.
-    size_t Suffix = OutputName.find_last_of('.');
-    const char *SideDeckName =
-        Args.MakeArgString(OutputName.substr(0, Suffix) + ".x");
+    SmallString<128> SideDeckName = OutputName;
+    llvm::sys::path::replace_extension(SideDeckName, "x");
     CmdArgs.push_back("-x");
-    CmdArgs.push_back(SideDeckName);
+    CmdArgs.push_back(Args.MakeArgString(SideDeckName));
   } else {
     // We need to direct side file to /dev/null to suppress linker warning when
     // the object file contains exported symbols, and -shared or
diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp
index a04eed9873c0d..67e8405c63951 100644
--- a/clang/lib/Parse/ParseDecl.cpp
+++ b/clang/lib/Parse/ParseDecl.cpp
@@ -4449,6 +4449,12 @@ void Parser::ParseDeclarationSpecifiers(
       isInvalid = DS.setFunctionSpecNoreturn(Loc, PrevSpec, DiagID);
       break;
 
+    case tok::kw__Export:
+      // If we find kw__Export, it is being applied to a var or function
+      // This will be handled in ParseDeclaratorInternal()
+      goto DoneWithDeclSpec;
+      break;
+
     // friend
     case tok::kw_friend:
       if (DSContext == DeclSpecContext::DSC_class) {
@@ -6174,6 +6180,7 @@ bool Parser::isDeclarationSpecifier(
   case tok::kw_virtual:
   case tok::kw_explicit:
   case tok::kw__Noreturn:
+  case tok::kw__Export:
 
     // alignment-specifier
   case tok::kw__Alignas:
@@ -6765,6 +6772,17 @@ void Parser::ParseDeclaratorInternal(Declarator &D,
 
   tok::TokenKind Kind = Tok.getKind();
 
+  // If this variable or function is marked as _Export, set the bit
+  if (Kind == tok::kw__Export) {
+    SourceLocation loc = ConsumeToken();
+    D.SetExport(loc);
+    D.SetRangeEnd(loc);
+
+    if (DirectDeclParser)
+      (this->*DirectDeclParser)(D);
+    return;
+  }
+
   if (D.getDeclSpec().isTypeSpecPipe() && !isPipeDeclarator(D)) {
     DeclSpec DS(AttrFactory);
     ParseTypeQualifierListOpt(DS);
diff --git a/clang/lib/Parse/ParseDeclCXX.cpp b/clang/lib/Parse/ParseDeclCXX.cpp
index 6f0f5a0311bc1..5be80011d92bb 100644
--- a/clang/lib/Parse/ParseDeclCXX.cpp
+++ b/clang/lib/Parse/ParseDeclCXX.cpp
@@ -1751,6 +1751,12 @@ void Parser::ParseClassSpecifier(tok::TokenKind TagTokKind,
   // If attributes exist after tag, parse them.
   for (;;) {
     MaybeParseAttributes(PAKM_CXX11 | PAKM_Declspec | PAKM_GNU, attrs);
+    // If the token is _Export, set the bits
+    if (Tok.is(tok::kw__Export)) {
+      SourceLocation loc = ConsumeToken();
+      DS.setExportSpec(loc);
+      continue;
+    }
     // Parse inheritance specifiers.
     if (Tok.isOneOf(tok::kw___single_inheritance,
                     tok::kw___multiple_inheritance,
diff --git a/clang/lib/Parse/ParsePragma.cpp b/clang/lib/Parse/ParsePragma.cpp
index cc6f18b5b319f..cc788dc2bf826 100644
--- a/clang/lib/Parse/ParsePragma.cpp
+++ b/clang/lib/Parse/ParsePragma.cpp
@@ -401,6 +401,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) {}
@@ -564,6 +570,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());
@@ -1401,6 +1412,164 @@ bool Parser::HandlePragmaMSAllocText(StringRef PragmaName,
   return true;
 }
 
+NestedNameSpecifier *Parser::zOSParseIdentifier(StringRef PragmaName,
+                                                IdentifierInfo *IdentName) {
+  NestedNameSpecifier *NestedId = nullptr;
+  if (Tok.is(tok::coloncolon)) {
+    NestedId = NestedNameSpecifier::Create(Actions.Context, IdentName);
+  } 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);
+    IdentName = Tok.getIdentifierInfo();
+    if (Tok.isNot(tok::identifier)) {
+      PP.Diag(Tok.getLocation(), diag::warn_pragma_expected_identifier)
+          << PragmaName;
+      return nullptr;
+    }
+    NestedId =
+        NestedNameSpecifier::Create(Actions.Context, NestedId, 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)) {
+      SourceRange MatchingCTypeRange;
+      TypeResult TResult = ParseTypeName(&MatchingCTypeRange);
+      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(); // The annotation token.
+
+  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) && Tok.isNot(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;
+
+    if (Tok.isNot(tok::l_paren) && Tok.isNot(tok::r_paren)) {
+      PP.Diag(Tok.getLocation(), diag::warn_pragma_expected_identifier)
+          << PragmaName;
+      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 (!zOSParseParameterList(PragmaName, TypeList, CVQual))
+      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 ");
@@ -4123,6 +4292,45 @@ void PragmaMaxTokensTotalHandler::HandlePragma(Preprocessor &PP,
   PP.overrideMaxTokens(MaxTokens, Loc);
 }
 
+/// Helper function for handling z/OS pragmas like #pragma export.
+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.
+  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'.
 void PragmaRISCVHandler::HandlePragma(Preprocessor &PP,
diff --git a/clang/lib/Parse/Parser.cpp b/clang/lib/Parse/Parser.cpp
index 04c2f1d380bc4..e701d7378d50e 100644
--- a/clang/lib/Parse/Parser.cpp
+++ b/clang/lib/Parse/Parser.cpp
@@ -881,6 +881,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/DeclSpec.cpp b/clang/lib/Sema/DeclSpec.cpp
index 12d2d3f6060c6..1fa0173902e38 100644
--- a/clang/lib/Sema/DeclSpec.cpp
+++ b/clang/lib/Sema/DeclSpec.cpp
@@ -1107,6 +1107,12 @@ bool DeclSpec::setFunctionSpecNoreturn(SourceLocation Loc,
   return false;
 }
 
+bool DeclSpec::setExportSpec(SourceLocation Loc) {
+  export_specified = true;
+  exportLoc = Loc;
+  return false;
+}
+
 bool DeclSpec::SetFriendSpec(SourceLocation Loc, const char *&PrevSpec,
                              unsigned &DiagID) {
   if (isFriendSpecified()) {
diff --git a/clang/lib/Sema/Sema.cpp b/clang/lib/Sema/Sema.cpp
index 4be7dfbc29392..22bf20ac9b442 100644
--- a/clang/lib/Sema/Sema.cpp
+++ b/clang/lib/Sema/Sema.cpp
@@ -1415,6 +1415,27 @@ void Sema::ActOnEndOfTranslationUnit() {
     Consumer.CompleteExternalDeclaration(D);
   }
 
+  // Visit all pending #pragma export
+  for (auto &Iter : PendingExportNames) {
+    NestedNameSpecifier *Name = Iter.first;
+    PendingSymbolOverloads &Overloads = Iter.second;
+    for (auto &I : Overloads) {
+      if (auto *D = trySymbolLookUp(Name, I)) {
+        if (D->hasExternalFormalLinkage()) {
+          if (D->isCXXClassMember()) {
+            D->addAttr(VisibilityAttr::CreateImplicit(
+                Context,
+                (VisibilityAttr::VisibilityType) /*DefaultVisibility*/ 0));
+          } else
+            Consumer.CompletePragmaExport(D);
+        } else
+          Diag(D->getLocation(), diag::warn_pragma_not_applied) << "export"
+              << D;
+      } else
+        Diag(I.NameLoc, diag::warn_failed_to_resolve_pragma) << "export";
+    }
+  }
+
   if (LangOpts.HLSL)
     HLSL().DiagnoseAvailabilityViolations(
         getASTContext().getTranslationUnitDecl());
diff --git a/clang/lib/Sema/SemaAttr.cpp b/clang/lib/Sema/SemaAttr.cpp
index cf2a5a622a3a4..ea0367f7790f0 100644
--- a/clang/lib/Sema/SemaAttr.cpp
+++ b/clang/lib/Sema/SemaAttr.cpp
@@ -1273,6 +1273,168 @@ void Sema::AddImplicitMSFunctionNoBuiltinAttr(FunctionDecl *FD) {
     FD->addAttr(NoBuiltinAttr::CreateImplicit(Context, V.data(), V.size()));
 }
 
+static bool typeListMatches(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->getOriginalType().getCanonicalType();
+    QualType MapArgType = (*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;
+}
+
+FunctionDecl *Sema::tryFunctionLookUp(NestedNameSpecifier *NestedName,
+                                      SourceLocation NameLoc) {
+  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), 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)))
+      F.erase();
+  }
+  F.done();
+  // 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) {
+      return FD;
+    }
+  }
+  return nullptr;
+}
+
+NamedDecl *Sema::trySymbolLookUp(NestedNameSpecifier *NestedName,
+                                 const clang::Sema::SymbolLabel &Label) {
+
+  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 = [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() && typeListMatches(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())
+      return Result.getFoundDecl();
+    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 && typeListMatches(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);
+
+  auto I = PendingExportNames.find(NestedId);
+  if (I == PendingExportNames.end()) {
+    std::pair<SymbolNames::iterator, bool> IB = PendingExportNames.insert(
+        std::pair<NestedNameSpecifier *, PendingSymbolOverloads>(
+            NestedId, PendingSymbolOverloads()));
+    assert(IB.second);
+    I = IB.first;
+  }
+  I->second.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 0e536f71a2f70..9182647a4f3d6 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -6418,6 +6418,19 @@ NamedDecl *Sema::HandleDeclarator(Scope *S, Declarator &D,
   if (!New)
     return nullptr;
 
+  if (D.IsExport()) {
+    VisibilityAttr *existingAttr = New->getAttr<VisibilityAttr>();
+    if (existingAttr) {
+      VisibilityAttr::VisibilityType existingValue =
+          existingAttr->getVisibility();
+      if (existingValue != VisibilityAttr::Default)
+        Diag(D.getExportLoc(), diag::err_mismatched_visibility);
+    } else {
+      New->addAttr(
+          VisibilityAttr::CreateImplicit(Context, VisibilityAttr::Default));
+    }
+  }
+
   // If this has an identifier and is not a function template specialization,
   // add it to the scope stack.
   if (New->getDeclName() && AddToScope)
diff --git a/clang/test/CodeGen/attr-export-failing.cpp b/clang/test/CodeGen/attr-export-failing.cpp
new file mode 100644
index 0000000000000..ee877aa0fb491
--- /dev/null
+++ b/clang/test/CodeGen/attr-export-failing.cpp
@@ -0,0 +1,4 @@
+// RUN: not %clang_cc1 -triple s390x-ibm-zos -fzos-extensions %s
+__attribute__((visibility("hidden"))) int _Export i; // expected-error {{visibility does not match previous declaration}}
+class __attribute__((visibility("hidden"))) _Export C; // expected-error {{visibility does not match previous declaration}}
+
diff --git a/clang/test/CodeGen/attr-export.cpp b/clang/test/CodeGen/attr-export.cpp
new file mode 100644
index 0000000000000..5f78bc8148248
--- /dev/null
+++ b/clang/test/CodeGen/attr-export.cpp
@@ -0,0 +1,51 @@
+// RUN: %clang --target=s390x-ibm-zos -S -emit-llvm %s -o - | FileCheck %s
+
+// Check the variables
+// CHECK: @var1 = global i32 0, align 4
+// CHECK: @var2 = hidden global i32 0, align 4
+// CHECK: @var3 = global i32 0, align 4
+// CHECK: @var4 = hidden global i32 0, align 4
+// CHECK: @var5 = global i32 0, align 4
+// CHECK: @obj1 = global %class.class1 zeroinitializer, align 2
+// CHECK: @obj2 = hidden global %class.class1 zeroinitializer, align 2
+
+// Check the functions
+// CHECK: define void @_Z4foo1v
+// CHECK: define hidden void @_Z4foo2v
+// CHECK: define void @_ZN6class13fooEv
+// CHECK: define hidden void @_ZN6class23fooEv
+// CHECK: define hidden void @_ZN6class33fooEv
+// CHECK: define void @_ZN6class33barEv
+
+int _Export var1;
+int var2;
+int _Export var3, var4, _Export var5;
+
+void _Export foo1(){};
+void foo2(){};
+
+class _Export class1 {
+public:
+  void foo();
+};
+
+class class2 {
+public:
+  void foo();
+};
+
+void class1::foo(){};
+
+void class2::foo(){};
+
+class1 _Export obj1;
+class1 obj2;
+
+class class3 {
+public:
+  void foo();
+  void _Export bar();
+};
+
+void class3::foo() {};
+void class3::bar() {};
diff --git a/clang/test/CodeGen/pragma-export.c b/clang/test/CodeGen/pragma-export.c
new file mode 100644
index 0000000000000..999a270b82aa9
--- /dev/null
+++ b/clang/test/CodeGen/pragma-export.c
@@ -0,0 +1,39 @@
+// RUN: %clang_cc1 %s -emit-llvm -fzos-extensions -fvisibility=hidden -verify -o - | FileCheck %s
+
+// Testing missing declarations.
+#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}}
+
+// Testing pragma export after decl.
+void f0(void) {}
+static void sf0(void) {} // expected-warning{{#pragma export is applicable to symbols with external linkage only; not applied to 'sf0'}}
+int v0;
+static int s0; // expected-warning{{#pragma export is applicable to symbols with external linkage only; not applied to 's0'}}
+#pragma export(f0)
+#pragma export(sf0)
+#pragma export(v0)
+#pragma export(s0)
+
+// Testing pragma export before decl.
+#pragma export(f1)
+#pragma export(sf1)
+#pragma export(v1)
+#pragma export(s1)
+void f1(void) {}
+static void sf1(void) {} // expected-warning{{#pragma export is applicable to symbols with external linkage only; not applied to 'sf1'}}
+int v1;
+static int s1; // expected-warning{{#pragma export is applicable to symbols with external linkage only; not applied to 's1'}}
+
+void f2(void) {}
+
+void t0(void) {}
+
+// Testing pragma export after decl and usage.
+#pragma export(f2)
+
+// CHECK: @v0 = global i32
+// CHECK: @v1 = global i32
+// CHECK: define void @f0()
+// CHECK: define void @f1()
+// CHECK: define void @f2()
+// CHECK: define hidden void @t0()
\ No newline at end of file
diff --git a/clang/test/CodeGen/pragma-export.cpp b/clang/test/CodeGen/pragma-export.cpp
new file mode 100644
index 0000000000000..e394cf668549f
--- /dev/null
+++ b/clang/test/CodeGen/pragma-export.cpp
@@ -0,0 +1,82 @@
+// RUN: %clang_cc1 %s -emit-llvm -fzos-extensions -fvisibility=hidden -verify -o - | FileCheck %s
+
+// Testing missing declarations.
+#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(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}}
+
+// Testing pragma export after decl.
+void f0(void) {}
+static void sf0(void) {} // expected-warning{{#pragma export is applicable to symbols with external linkage only; not applied to 'sf0'}}
+int v0;
+static int s0; // expected-warning{{#pragma export is applicable to symbols with external linkage only; not applied to 's0'}}
+#pragma export(f0)
+#pragma export(sf0)
+#pragma export(v0)
+#pragma export(s0)
+
+// Testing pragma export before decl.
+#pragma export(f1)
+#pragma export(sf1)
+#pragma export(v1)
+#pragma export(s1)
+void f1(void) {}
+static void sf1(void) {} // expected-warning{{#pragma export is applicable to symbols with external linkage only; not applied to 'sf1'}}
+int v1;
+static int s1; // expected-warning{{#pragma export is applicable to symbols with external linkage only; not applied to 's1'}}
+
+// 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();
+}
+
+// Testing pragma export after decl and usage.
+#pragma export(f2(void))
+
+// Testing pragma export with namespace.
+void f5(void) {}
+namespace N0 {
+void f0(void) {}
+void f1(void) {}
+void f2(void) {}
+void f3(void) {}
+void f5(void) {}
+#pragma export(f0)
+#pragma export(N0::f1)
+#pragma export(f5)
+} // namespace N0
+#pragma export(N0::f2)
+
+// CHECK: @v0 = global i32
+// CHECK: @v1 = global i32
+// CHECK: define void @_Z2f0v
+// CHECK: define void @_Z2f1v
+// CHECK: define void @_Z2f2dd
+// CHECK: define void @_Z2f2i
+// CHECK: define hidden void @_Z2f2ii
+// CHECK: define void @_Z2f3d
+// CHECK: define void @_Z2f3id
+// CHECK: define hidden void @_Z2f3dd
+// CHECK: define void @_Z2f2v
+// CHECK: define hidden void @_Z2t0v
+// CHECK: define hidden void @_Z2f5v
+// CHECK: define void @_ZN2N02f0Ev
+// CHECK: define void @_ZN2N02f1Ev
+// CHECK: define void @_ZN2N02f2Ev
+// CHECK: define hidden void @_ZN2N02f3Ev
+// CHECK: define void @_ZN2N02f5Ev
diff --git a/clang/test/CodeGen/zos-pragmas.c b/clang/test/CodeGen/zos-pragmas.c
new file mode 100644
index 0000000000000..6f0ee601b03f0
--- /dev/null
+++ b/clang/test/CodeGen/zos-pragmas.c
@@ -0,0 +1,11 @@
+// RUN: %clang_cc1 -emit-llvm -triple s390x-none-zos -fvisibility=hidden %s -o - | FileCheck %s
+// expected-no-diagnostics
+
+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..70495a4123288
--- /dev/null
+++ b/clang/test/CodeGen/zos-pragmas.cpp
@@ -0,0 +1,11 @@
+// RUN: %clang_cc1 -x c++ -emit-llvm -triple s390x-none-zos -fvisibility=hidden %s -o - | FileCheck %s
+// expected-no-diagnostics
+
+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

>From e1cbba0828872c3237633b276be3b1f942760252 Mon Sep 17 00:00:00 2001
From: Sean Perry <perry at ca.ibm.com>
Date: Thu, 3 Oct 2024 08:52:51 -0500
Subject: [PATCH 02/15] Add pragma export & _Export

---
 clang/include/clang/Sema/DeclSpec.h |  5 ++++-
 clang/lib/AST/MicrosoftMangle.cpp   |  1 +
 clang/lib/Lex/Preprocessor.cpp      |  6 ++++++
 clang/lib/Sema/SemaDecl.cpp         | 12 ++++++++++++
 clang/test/CodeGen/attr-export.cpp  |  2 +-
 5 files changed, 24 insertions(+), 2 deletions(-)

diff --git a/clang/include/clang/Sema/DeclSpec.h b/clang/include/clang/Sema/DeclSpec.h
index f41748af5c830..d03a6b11d26ab 100644
--- a/clang/include/clang/Sema/DeclSpec.h
+++ b/clang/include/clang/Sema/DeclSpec.h
@@ -1965,6 +1965,7 @@ class Declarator {
   unsigned InlineStorageUsed : 1;
 
   /// Indicates whether this is set as _Export
+  LLVM_PREFERRED_TYPE(bool)
   unsigned ExportSpecified : 1;
 
   /// Indicates whether this declarator has an initializer.
@@ -2045,7 +2046,8 @@ class Declarator {
                                    FunctionDefinitionKind::Declaration)),
         Redeclaration(false), Extension(false), ObjCIvar(false),
         ObjCWeakProperty(false), InlineStorageUsed(false),
-        HasInitializer(false), Attrs(DS.getAttributePool().getFactory()),
+        ExportSpecified(false), HasInitializer(false), 
+        Attrs(DS.getAttributePool().getFactory()),
         DeclarationAttrs(DeclarationAttrs), AsmLabel(nullptr),
         TrailingRequiresClause(nullptr),
         InventedTemplateParameterList(nullptr) {
@@ -2152,6 +2154,7 @@ class Declarator {
     HasInitializer = false;
     ObjCIvar = false;
     ObjCWeakProperty = false;
+    ExportSpecified = false;
     CommaLoc = SourceLocation();
     EllipsisLoc = SourceLocation();
     PackIndexingExpr = nullptr;
diff --git a/clang/lib/AST/MicrosoftMangle.cpp b/clang/lib/AST/MicrosoftMangle.cpp
index e4c8663c134fd..4ccc79b928638 100644
--- a/clang/lib/AST/MicrosoftMangle.cpp
+++ b/clang/lib/AST/MicrosoftMangle.cpp
@@ -1027,6 +1027,7 @@ void MicrosoftCXXNameMangler::mangleFloat(llvm::APFloat Number) {
   case APFloat::S_Float6E3M2FN:
   case APFloat::S_Float6E2M3FN:
   case APFloat::S_Float4E2M1FN:
+  case APFloat::S_Float8E8M0FNU:
     llvm_unreachable("Tried to mangle unexpected APFloat semantics");
   }
 
diff --git a/clang/lib/Lex/Preprocessor.cpp b/clang/lib/Lex/Preprocessor.cpp
index f0b4593e0cc22..750bf38180b0d 100644
--- a/clang/lib/Lex/Preprocessor.cpp
+++ b/clang/lib/Lex/Preprocessor.cpp
@@ -171,22 +171,28 @@ Preprocessor::Preprocessor(std::shared_ptr<PreprocessorOptions> PPOpts,
 
 Preprocessor::~Preprocessor() {
   assert(!isBacktrackEnabled() && "EnableBacktrack/Backtrack imbalance!");
+  fprintf(stderr, "SDP: ----- in Preprocessor::~Preprocessor\n");
 
   IncludeMacroStack.clear();
+  fprintf(stderr, "SDP:   - call fill\n");
 
   // Free any cached macro expanders.
   // This populates MacroArgCache, so all TokenLexers need to be destroyed
   // before the code below that frees up the MacroArgCache list.
   std::fill(TokenLexerCache, TokenLexerCache + NumCachedTokenLexers, nullptr);
+  fprintf(stderr, "SDP:   - call reset\n");
   CurTokenLexer.reset();
 
+  fprintf(stderr, "SDP:   - free cached macros\n");
   // Free any cached MacroArgs.
   for (MacroArgs *ArgList = MacroArgCache; ArgList;)
     ArgList = ArgList->deallocate();
 
+  fprintf(stderr, "SDP:   - del hdr search\n");
   // Delete the header search info, if we own it.
   if (OwnsHeaderSearch)
     delete &HeaderInfo;
+  fprintf(stderr, "SDP:   - done\n");
 }
 
 void Preprocessor::Initialize(const TargetInfo &Target,
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index 415df3ec493a5..94d80b551df80 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -5082,6 +5082,18 @@ Decl *Sema::ParsedFreeStandingDeclSpec(Scope *S, AccessSpecifier AS,
   assert(EllipsisLoc.isInvalid() &&
          "Friend ellipsis but not friend-specified?");
 
+  if (DS.isExportSpecified()) {
+    VisibilityAttr *existingAttr = TagD->getAttr<VisibilityAttr>();
+    if (existingAttr) {
+      VisibilityAttr::VisibilityType existingValue = existingAttr->getVisibility();
+      if (existingValue != VisibilityAttr::Default)
+        Diag(DS.getExportSpecLoc(), diag::err_mismatched_visibility);
+    } else {
+      Tag->addAttr(VisibilityAttr::CreateImplicit(Context,
+              VisibilityAttr::Default));
+    }
+  }
+
   // Track whether this decl-specifier declares anything.
   bool DeclaresAnything = true;
 
diff --git a/clang/test/CodeGen/attr-export.cpp b/clang/test/CodeGen/attr-export.cpp
index 5f78bc8148248..fbcaffc080db3 100644
--- a/clang/test/CodeGen/attr-export.cpp
+++ b/clang/test/CodeGen/attr-export.cpp
@@ -1,4 +1,4 @@
-// RUN: %clang --target=s390x-ibm-zos -S -emit-llvm %s -o - | FileCheck %s
+// RUN: %clangxx --target=s390x-ibm-zos -S -emit-llvm %s -o - | FileCheck %s
 
 // Check the variables
 // CHECK: @var1 = global i32 0, align 4

>From 39a1411d8de01334e807d6214f51c23bba52d342 Mon Sep 17 00:00:00 2001
From: Sean Perry <perry at ca.ibm.com>
Date: Thu, 3 Oct 2024 09:52:42 -0500
Subject: [PATCH 03/15] restore to main

---
 clang/lib/AST/MicrosoftMangle.cpp | 1 -
 clang/lib/Lex/Preprocessor.cpp    | 6 ------
 2 files changed, 7 deletions(-)

diff --git a/clang/lib/AST/MicrosoftMangle.cpp b/clang/lib/AST/MicrosoftMangle.cpp
index 4234e713f9b35..4ccf3f76bf0ce 100644
--- a/clang/lib/AST/MicrosoftMangle.cpp
+++ b/clang/lib/AST/MicrosoftMangle.cpp
@@ -1028,7 +1028,6 @@ void MicrosoftCXXNameMangler::mangleFloat(llvm::APFloat Number) {
   case APFloat::S_Float6E3M2FN:
   case APFloat::S_Float6E2M3FN:
   case APFloat::S_Float4E2M1FN:
-  case APFloat::S_Float8E8M0FNU:
     llvm_unreachable("Tried to mangle unexpected APFloat semantics");
   }
 
diff --git a/clang/lib/Lex/Preprocessor.cpp b/clang/lib/Lex/Preprocessor.cpp
index 750bf38180b0d..f0b4593e0cc22 100644
--- a/clang/lib/Lex/Preprocessor.cpp
+++ b/clang/lib/Lex/Preprocessor.cpp
@@ -171,28 +171,22 @@ Preprocessor::Preprocessor(std::shared_ptr<PreprocessorOptions> PPOpts,
 
 Preprocessor::~Preprocessor() {
   assert(!isBacktrackEnabled() && "EnableBacktrack/Backtrack imbalance!");
-  fprintf(stderr, "SDP: ----- in Preprocessor::~Preprocessor\n");
 
   IncludeMacroStack.clear();
-  fprintf(stderr, "SDP:   - call fill\n");
 
   // Free any cached macro expanders.
   // This populates MacroArgCache, so all TokenLexers need to be destroyed
   // before the code below that frees up the MacroArgCache list.
   std::fill(TokenLexerCache, TokenLexerCache + NumCachedTokenLexers, nullptr);
-  fprintf(stderr, "SDP:   - call reset\n");
   CurTokenLexer.reset();
 
-  fprintf(stderr, "SDP:   - free cached macros\n");
   // Free any cached MacroArgs.
   for (MacroArgs *ArgList = MacroArgCache; ArgList;)
     ArgList = ArgList->deallocate();
 
-  fprintf(stderr, "SDP:   - del hdr search\n");
   // Delete the header search info, if we own it.
   if (OwnsHeaderSearch)
     delete &HeaderInfo;
-  fprintf(stderr, "SDP:   - done\n");
 }
 
 void Preprocessor::Initialize(const TargetInfo &Target,

>From 9cfc3ccba6bca8b9b2146e0830e21dc42e5cecdb Mon Sep 17 00:00:00 2001
From: Sean Perry <perry at ca.ibm.com>
Date: Thu, 3 Oct 2024 12:22:35 -0500
Subject: [PATCH 04/15] Reset pragma handler was missing

---
 clang/lib/Parse/ParsePragma.cpp | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/clang/lib/Parse/ParsePragma.cpp b/clang/lib/Parse/ParsePragma.cpp
index cc788dc2bf826..af892115f1f8d 100644
--- a/clang/lib/Parse/ParsePragma.cpp
+++ b/clang/lib/Parse/ParsePragma.cpp
@@ -709,6 +709,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();

>From e0cb769b2f8c4f9b20dad0071fd6a15ae3960e5d Mon Sep 17 00:00:00 2001
From: Sean Perry <perry at ca.ibm.com>
Date: Thu, 3 Oct 2024 14:13:18 -0500
Subject: [PATCH 05/15] formating

---
 clang/include/clang/Sema/DeclSpec.h |  9 ++++-----
 clang/lib/CodeGen/ModuleBuilder.cpp |  2 +-
 clang/lib/Parse/ParsePragma.cpp     | 16 +++++++---------
 clang/lib/Sema/Sema.cpp             |  4 ++--
 clang/lib/Sema/SemaDecl.cpp         |  7 ++++---
 5 files changed, 18 insertions(+), 20 deletions(-)

diff --git a/clang/include/clang/Sema/DeclSpec.h b/clang/include/clang/Sema/DeclSpec.h
index d03a6b11d26ab..cf38b378352ee 100644
--- a/clang/include/clang/Sema/DeclSpec.h
+++ b/clang/include/clang/Sema/DeclSpec.h
@@ -496,9 +496,8 @@ class DeclSpec {
         TypeQualifiers(TQ_unspecified), FS_inline_specified(false),
         FS_forceinline_specified(false), FS_virtual_specified(false),
         FS_noreturn_specified(false), export_specified(false),
-        FriendSpecifiedFirst(false),
-        ConstexprSpecifier(
-            static_cast<unsigned>(ConstexprSpecKind::Unspecified)),
+        FriendSpecifiedFirst(false), ConstexprSpecifier( static_cast<unsigned>(
+                                         ConstexprSpecKind::Unspecified)),
         Attrs(attrFactory), writtenBS(), ObjCQualifiers(nullptr) {}
 
   // storage-class-specifier
@@ -668,7 +667,7 @@ class DeclSpec {
   bool isExportSpecified() const { return export_specified; }
   SourceLocation getExportSpecLoc() const { return exportLoc; }
 
-    void ClearFunctionSpecs() {
+  void ClearFunctionSpecs() {
     FS_inline_specified = false;
     FS_inlineLoc = SourceLocation();
     FS_forceinline_specified = false;
@@ -2046,7 +2045,7 @@ class Declarator {
                                    FunctionDefinitionKind::Declaration)),
         Redeclaration(false), Extension(false), ObjCIvar(false),
         ObjCWeakProperty(false), InlineStorageUsed(false),
-        ExportSpecified(false), HasInitializer(false), 
+        ExportSpecified(false), HasInitializer(false),
         Attrs(DS.getAttributePool().getFactory()),
         DeclarationAttrs(DeclarationAttrs), AsmLabel(nullptr),
         TrailingRequiresClause(nullptr),
diff --git a/clang/lib/CodeGen/ModuleBuilder.cpp b/clang/lib/CodeGen/ModuleBuilder.cpp
index 7658d97af0184..e0c7ae03f90f1 100644
--- a/clang/lib/CodeGen/ModuleBuilder.cpp
+++ b/clang/lib/CodeGen/ModuleBuilder.cpp
@@ -318,7 +318,7 @@ namespace {
       Builder->EmitPragmaExport(D);
     }
 
-  void HandleVTable(CXXRecordDecl *RD) override {
+    void HandleVTable(CXXRecordDecl *RD) override {
       if (Diags.hasUnrecoverableErrorOccurred())
         return;
 
diff --git a/clang/lib/Parse/ParsePragma.cpp b/clang/lib/Parse/ParsePragma.cpp
index af892115f1f8d..8203e15857ebd 100644
--- a/clang/lib/Parse/ParsePragma.cpp
+++ b/clang/lib/Parse/ParsePragma.cpp
@@ -1448,9 +1448,9 @@ NestedNameSpecifier *Parser::zOSParseIdentifier(StringRef PragmaName,
   return NestedId;
 }
 
-bool Parser::zOSParseParameterList(StringRef PragmaName,
-                                   std::optional<SmallVector<QualType, 4>> &TypeList,
-                                   Qualifiers &CVQual) {
+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);
@@ -1544,7 +1544,7 @@ bool Parser::zOSHandlePragmaHelper(tok::TokenKind PragmaKind) {
     Actions.ActOnPragmaExport(NestedId, IdentNameLoc, std::move(TypeList),
                               CVQual);
 
-    //Because export is also a C++ keyword, we also check for that
+    // 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();
@@ -4298,8 +4298,7 @@ void PragmaMaxTokensTotalHandler::HandlePragma(Preprocessor &PP,
 }
 
 /// Helper function for handling z/OS pragmas like #pragma export.
-static void zOSPragmaHandlerHelper(Preprocessor &PP,
-                                   Token &Tok,
+static void zOSPragmaHandlerHelper(Preprocessor &PP, Token &Tok,
                                    tok::TokenKind TokKind) {
   Token EoF, AnnotTok;
   EoF.startToken();
@@ -4322,9 +4321,8 @@ static void zOSPragmaHandlerHelper(Preprocessor &PP,
   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());
+      std::pair<std::unique_ptr<Token[]>, size_t>(std::move(TokenArray),
+                                                 TokenVector.size());
   AnnotTok.setAnnotationValue(Value);
   PP.EnterToken(AnnotTok, /*IsReinject*/ false);
 }
diff --git a/clang/lib/Sema/Sema.cpp b/clang/lib/Sema/Sema.cpp
index 6964a70ef5613..1e5c17b88adc9 100644
--- a/clang/lib/Sema/Sema.cpp
+++ b/clang/lib/Sema/Sema.cpp
@@ -1429,8 +1429,8 @@ void Sema::ActOnEndOfTranslationUnit() {
           } else
             Consumer.CompletePragmaExport(D);
         } else
-          Diag(D->getLocation(), diag::warn_pragma_not_applied) << "export"
-              << D;
+          Diag(D->getLocation(), diag::warn_pragma_not_applied) 
+              << "export" << D;
       } else
         Diag(I.NameLoc, diag::warn_failed_to_resolve_pragma) << "export";
     }
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index 15c1a36d9c061..6ad84b1247306 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -5090,12 +5090,13 @@ Decl *Sema::ParsedFreeStandingDeclSpec(Scope *S, AccessSpecifier AS,
   if (DS.isExportSpecified()) {
     VisibilityAttr *existingAttr = TagD->getAttr<VisibilityAttr>();
     if (existingAttr) {
-      VisibilityAttr::VisibilityType existingValue = existingAttr->getVisibility();
+      VisibilityAttr::VisibilityType existingValue = 
+          existingAttr->getVisibility();
       if (existingValue != VisibilityAttr::Default)
         Diag(DS.getExportSpecLoc(), diag::err_mismatched_visibility);
     } else {
-      Tag->addAttr(VisibilityAttr::CreateImplicit(Context,
-              VisibilityAttr::Default));
+      Tag->addAttr(
+          VisibilityAttr::CreateImplicit(Context, VisibilityAttr::Default));
     }
   }
 

>From 82e996299bdd68e8efe8fee1eb0c1609c25c8844 Mon Sep 17 00:00:00 2001
From: Sean Perry <perry at ca.ibm.com>
Date: Thu, 3 Oct 2024 14:31:34 -0500
Subject: [PATCH 06/15] more formating

---
 clang/include/clang/Sema/DeclSpec.h | 2 +-
 clang/lib/Parse/ParsePragma.cpp     | 2 +-
 clang/lib/Sema/Sema.cpp             | 2 +-
 clang/lib/Sema/SemaDecl.cpp         | 2 +-
 4 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/clang/include/clang/Sema/DeclSpec.h b/clang/include/clang/Sema/DeclSpec.h
index cf38b378352ee..243776ada2b64 100644
--- a/clang/include/clang/Sema/DeclSpec.h
+++ b/clang/include/clang/Sema/DeclSpec.h
@@ -496,7 +496,7 @@ class DeclSpec {
         TypeQualifiers(TQ_unspecified), FS_inline_specified(false),
         FS_forceinline_specified(false), FS_virtual_specified(false),
         FS_noreturn_specified(false), export_specified(false),
-        FriendSpecifiedFirst(false), ConstexprSpecifier( static_cast<unsigned>(
+        FriendSpecifiedFirst(false), ConstexprSpecifier(static_cast<unsigned>(
                                          ConstexprSpecKind::Unspecified)),
         Attrs(attrFactory), writtenBS(), ObjCQualifiers(nullptr) {}
 
diff --git a/clang/lib/Parse/ParsePragma.cpp b/clang/lib/Parse/ParsePragma.cpp
index 8203e15857ebd..b1e41cd33ef89 100644
--- a/clang/lib/Parse/ParsePragma.cpp
+++ b/clang/lib/Parse/ParsePragma.cpp
@@ -4322,7 +4322,7 @@ static void zOSPragmaHandlerHelper(Preprocessor &PP, Token &Tok,
   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());
+                                                  TokenVector.size());
   AnnotTok.setAnnotationValue(Value);
   PP.EnterToken(AnnotTok, /*IsReinject*/ false);
 }
diff --git a/clang/lib/Sema/Sema.cpp b/clang/lib/Sema/Sema.cpp
index 1e5c17b88adc9..83f35342f473e 100644
--- a/clang/lib/Sema/Sema.cpp
+++ b/clang/lib/Sema/Sema.cpp
@@ -1429,7 +1429,7 @@ void Sema::ActOnEndOfTranslationUnit() {
           } else
             Consumer.CompletePragmaExport(D);
         } else
-          Diag(D->getLocation(), diag::warn_pragma_not_applied) 
+          Diag(D->getLocation(), diag::warn_pragma_not_applied)
               << "export" << D;
       } else
         Diag(I.NameLoc, diag::warn_failed_to_resolve_pragma) << "export";
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index 6ad84b1247306..969cda279018f 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -5090,7 +5090,7 @@ Decl *Sema::ParsedFreeStandingDeclSpec(Scope *S, AccessSpecifier AS,
   if (DS.isExportSpecified()) {
     VisibilityAttr *existingAttr = TagD->getAttr<VisibilityAttr>();
     if (existingAttr) {
-      VisibilityAttr::VisibilityType existingValue = 
+      VisibilityAttr::VisibilityType existingValue =
           existingAttr->getVisibility();
       if (existingValue != VisibilityAttr::Default)
         Diag(DS.getExportSpecLoc(), diag::err_mismatched_visibility);

>From e7e560af0ddc28e8f64ad529af6ca646c65bbe1e Mon Sep 17 00:00:00 2001
From: Sean Perry <perry at ca.ibm.com>
Date: Thu, 3 Oct 2024 15:31:00 -0500
Subject: [PATCH 07/15] missing requires & s390x-none-zos targets in some

---
 clang/test/CodeGen/attr-export-failing.cpp | 3 ++-
 clang/test/CodeGen/attr-export.cpp         | 3 ++-
 clang/test/CodeGen/pragma-export.c         | 5 +++--
 clang/test/CodeGen/pragma-export.cpp       | 3 ++-
 clang/test/CodeGen/zos-pragmas.c           | 1 +
 clang/test/CodeGen/zos-pragmas.cpp         | 1 +
 6 files changed, 11 insertions(+), 5 deletions(-)

diff --git a/clang/test/CodeGen/attr-export-failing.cpp b/clang/test/CodeGen/attr-export-failing.cpp
index ee877aa0fb491..612e3bb554430 100644
--- a/clang/test/CodeGen/attr-export-failing.cpp
+++ b/clang/test/CodeGen/attr-export-failing.cpp
@@ -1,4 +1,5 @@
-// RUN: not %clang_cc1 -triple s390x-ibm-zos -fzos-extensions %s
+// REQUIRES: systemz-registered-target
+// RUN: not %clang_cc1 -triple s390x-none-zos -fzos-extensions %s
 __attribute__((visibility("hidden"))) int _Export i; // expected-error {{visibility does not match previous declaration}}
 class __attribute__((visibility("hidden"))) _Export C; // expected-error {{visibility does not match previous declaration}}
 
diff --git a/clang/test/CodeGen/attr-export.cpp b/clang/test/CodeGen/attr-export.cpp
index fbcaffc080db3..23eb5163d130e 100644
--- a/clang/test/CodeGen/attr-export.cpp
+++ b/clang/test/CodeGen/attr-export.cpp
@@ -1,4 +1,5 @@
-// RUN: %clangxx --target=s390x-ibm-zos -S -emit-llvm %s -o - | FileCheck %s
+// REQUIRES: systemz-registered-target
+// RUN: %clangxx --target=s390x-none-zos -S -emit-llvm %s -o - | FileCheck %s
 
 // Check the variables
 // CHECK: @var1 = global i32 0, align 4
diff --git a/clang/test/CodeGen/pragma-export.c b/clang/test/CodeGen/pragma-export.c
index 999a270b82aa9..855fb31568ac8 100644
--- a/clang/test/CodeGen/pragma-export.c
+++ b/clang/test/CodeGen/pragma-export.c
@@ -1,4 +1,5 @@
-// RUN: %clang_cc1 %s -emit-llvm -fzos-extensions -fvisibility=hidden -verify -o - | FileCheck %s
+// REQUIRES: systemz-registered-target
+// RUN: %clang_cc1 %s -emit-llvm -fzos-extensions -triple s390x-none-zos -fvisibility=hidden -verify -o - | FileCheck %s
 
 // Testing missing declarations.
 #pragma export(d0) // expected-warning{{failed to resolve '#pragma export' to a declaration}}
@@ -36,4 +37,4 @@ void t0(void) {}
 // CHECK: define void @f0()
 // CHECK: define void @f1()
 // CHECK: define void @f2()
-// CHECK: define hidden void @t0()
\ No newline at end of file
+// CHECK: define hidden void @t0()
diff --git a/clang/test/CodeGen/pragma-export.cpp b/clang/test/CodeGen/pragma-export.cpp
index e394cf668549f..a7f3f8bfce8a1 100644
--- a/clang/test/CodeGen/pragma-export.cpp
+++ b/clang/test/CodeGen/pragma-export.cpp
@@ -1,4 +1,5 @@
-// RUN: %clang_cc1 %s -emit-llvm -fzos-extensions -fvisibility=hidden -verify -o - | FileCheck %s
+// REQUIRES: systemz-registered-target
+// RUN: %clang_cc1 %s -emit-llvm -triple s390x-none-zos -fzos-extensions -fvisibility=hidden -verify -o - | FileCheck %s
 
 // Testing missing declarations.
 #pragma export(d0)                         // expected-warning{{failed to resolve '#pragma export' to a declaration}}
diff --git a/clang/test/CodeGen/zos-pragmas.c b/clang/test/CodeGen/zos-pragmas.c
index 6f0ee601b03f0..adcc541b7f767 100644
--- a/clang/test/CodeGen/zos-pragmas.c
+++ b/clang/test/CodeGen/zos-pragmas.c
@@ -1,3 +1,4 @@
+// REQUIRES: systemz-registered-target
 // RUN: %clang_cc1 -emit-llvm -triple s390x-none-zos -fvisibility=hidden %s -o - | FileCheck %s
 // expected-no-diagnostics
 
diff --git a/clang/test/CodeGen/zos-pragmas.cpp b/clang/test/CodeGen/zos-pragmas.cpp
index 70495a4123288..e589058e9726b 100644
--- a/clang/test/CodeGen/zos-pragmas.cpp
+++ b/clang/test/CodeGen/zos-pragmas.cpp
@@ -1,3 +1,4 @@
+// REQUIRES: systemz-registered-target
 // RUN: %clang_cc1 -x c++ -emit-llvm -triple s390x-none-zos -fvisibility=hidden %s -o - | FileCheck %s
 // expected-no-diagnostics
 

>From 5d400563ddd0340c1b04da2ba290bb81260d9358 Mon Sep 17 00:00:00 2001
From: Sean Perry <perry at ca.ibm.com>
Date: Mon, 28 Oct 2024 15:46:16 -0500
Subject: [PATCH 08/15] wip

---
 clang/lib/Parse/ParsePragma.cpp      |  4 ++--
 clang/lib/Sema/SemaAttr.cpp          | 31 +++++++++++++++++++++++-----
 clang/lib/Sema/SemaType.cpp          |  2 ++
 clang/test/CodeGen/pragma-export.cpp | 30 +++++++++++++++++++++++++++
 4 files changed, 60 insertions(+), 7 deletions(-)

diff --git a/clang/lib/Parse/ParsePragma.cpp b/clang/lib/Parse/ParsePragma.cpp
index e5cf08e064e40..44effb2509414 100644
--- a/clang/lib/Parse/ParsePragma.cpp
+++ b/clang/lib/Parse/ParsePragma.cpp
@@ -1455,8 +1455,8 @@ bool Parser::zOSParseParameterList(
     TypeList = SmallVector<QualType, 4>();
     PP.Lex(Tok);
     while (Tok.isNot(tok::eof) && !Tok.is(tok::r_paren)) {
-      SourceRange MatchingCTypeRange;
-      TypeResult TResult = ParseTypeName(&MatchingCTypeRange);
+      //SourceRange MatchingCTypeRange;
+      TypeResult TResult = ParseTypeName(nullptr, DeclaratorContext::Prototype);
       if (!TResult.isInvalid()) {
         QualType QT = TResult.get().get();
         if (!QT.getTypePtr()->isVoidType())
diff --git a/clang/lib/Sema/SemaAttr.cpp b/clang/lib/Sema/SemaAttr.cpp
index 4775b4ca828fc..8b9d012ea0e0d 100644
--- a/clang/lib/Sema/SemaAttr.cpp
+++ b/clang/lib/Sema/SemaAttr.cpp
@@ -1309,7 +1309,7 @@ void Sema::AddImplicitMSFunctionNoBuiltinAttr(FunctionDecl *FD) {
     FD->addAttr(NoBuiltinAttr::CreateImplicit(Context, V.data(), V.size()));
 }
 
-static bool typeListMatches(FunctionDecl *FD,
+static bool typeListMatches(ASTContext& Context, FunctionDecl *FD,
                             const clang::Sema::SymbolLabel &Label) {
   assert(Label.TypeList.has_value());
   if (FD->getNumParams() != Label.TypeList->size()) {
@@ -1319,9 +1319,30 @@ static bool typeListMatches(FunctionDecl *FD,
   // Check if arguments match.
   for (unsigned i = 0; i != FD->getNumParams(); ++i) {
     const ParmVarDecl *PVD = FD->getParamDecl(i);
-    QualType ParmType = PVD->getOriginalType().getCanonicalType();
+    QualType ParmType = PVD->getType().getCanonicalType();
+    fprintf(stderr, "SDP: --- pramtype\n");
+    ParmType->dump();
+    if (ParmType->isArrayType())
+      ParmType = Context.getArrayDecayedType(ParmType);
+    else if (ParmType->isFunctionType())
+    { fprintf(stderr, "  - is function\n");
+      ParmType = Context.getPointerType(ParmType);
+      }
+    ParmType->dump();
+
     QualType MapArgType = (*Label.TypeList)[i].getCanonicalType();
+    fprintf(stderr, "SDP: --- MapArgtype\n");
+    MapArgType->dump();
+    if (MapArgType->isArrayType())
+      MapArgType = Context.getArrayDecayedType(MapArgType);
+    else if (MapArgType->isFunctionType())
+    { fprintf(stderr, "  - is function\n");
+      MapArgType = Context.getPointerType(MapArgType);
+      }
+    MapArgType.getDesugaredType(Context)->dump();
 
+    assert(!ParmType->canDecayToPointerType());
+    assert(!MapArgType->canDecayToPointerType());
     if (ParmType != MapArgType)
       return false;
   }
@@ -1393,7 +1414,7 @@ NamedDecl *Sema::trySymbolLookUp(NestedNameSpecifier *NestedName,
   }
   F.done();
 
-  auto MatchDecl = [Name, Label](DeclContext *DC) -> NamedDecl * {
+  auto MatchDecl = [this, Name, Label](DeclContext *DC) -> NamedDecl * {
     auto LRes = DC->lookup(DeclarationName(Name));
     for (auto *I : LRes) {
       if (isa<VarDecl>(I))
@@ -1403,7 +1424,7 @@ NamedDecl *Sema::trySymbolLookUp(NestedNameSpecifier *NestedName,
 
         // 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() && typeListMatches(FD, Label)) ||
+        if ((Label.TypeList.has_value() && typeListMatches(Context, FD, Label)) ||
             (!Label.TypeList.has_value() && LRes.isSingleResult()))
           return FD;
       }
@@ -1444,7 +1465,7 @@ NamedDecl *Sema::trySymbolLookUp(NestedNameSpecifier *NestedName,
   for (LookupResult::iterator I = Result.begin(); I != Result.end(); ++I) {
     NamedDecl *ND = (*I)->getUnderlyingDecl();
     FunctionDecl *FD = dyn_cast<FunctionDecl>(ND);
-    if (FD && typeListMatches(FD, Label)) {
+    if (FD && typeListMatches(Context, FD, Label)) {
       return FD;
     }
   }
diff --git a/clang/lib/Sema/SemaType.cpp b/clang/lib/Sema/SemaType.cpp
index f32edc5ac0644..11166a558906c 100644
--- a/clang/lib/Sema/SemaType.cpp
+++ b/clang/lib/Sema/SemaType.cpp
@@ -4238,6 +4238,7 @@ static TypeSourceInfo *GetFullTypeForDeclarator(TypeProcessingState &state,
   // The TypeSourceInfo that this function returns will not be a null type.
   // If there is an error, this function will fill in a dummy type as fallback.
   QualType T = declSpecType;
+  fprintf(stderr, "SDP: === GetFullTypeForDeclarator\n"); T->dump(); fprintf(stderr, "SDP: === GetFullTypeForDeclarator\n");
   Declarator &D = state.getDeclarator();
   Sema &S = state.getSema();
   ASTContext &Context = S.Context;
@@ -5692,6 +5693,7 @@ TypeSourceInfo *Sema::GetTypeForDeclarator(Declarator &D) {
 
   TypeSourceInfo *ReturnTypeInfo = nullptr;
   QualType T = GetDeclSpecTypeForDeclarator(state, ReturnTypeInfo);
+  fprintf(stderr, "SDP: === GetTypeForDeclarator\n"); T->dump(); fprintf(stderr, "SDP: === GetTypeForDeclarator\n");
   if (D.isPrototypeContext() && getLangOpts().ObjCAutoRefCount)
     inferARCWriteback(state, T);
 
diff --git a/clang/test/CodeGen/pragma-export.cpp b/clang/test/CodeGen/pragma-export.cpp
index a7f3f8bfce8a1..0041e74166b0e 100644
--- a/clang/test/CodeGen/pragma-export.cpp
+++ b/clang/test/CodeGen/pragma-export.cpp
@@ -46,6 +46,28 @@ void t0(void) {
   f2();
 }
 
+// Test type decay in arguments
+
+void fd1(int []) { }
+void fd2(int []) { }
+void fd3(int *) { }
+void fd4(int *) { }
+
+#pragma export(fd1(int[]))
+#pragma export(fd2(int*))
+#pragma export(fd3(int[]))
+#pragma export(fd4(int*))
+
+void fd5(int ()) {}
+void fd6(int ()) {}
+void fd7(int (*)()) {}
+void fd8(int (*)()) {}
+
+#pragma export (fd5(int ()))
+#pragma export (fd6(int (*)()))
+#pragma export (fd7(int ()))
+#pragma export (fd8(int (*)()))
+
 // Testing pragma export after decl and usage.
 #pragma export(f2(void))
 
@@ -75,6 +97,14 @@ void f5(void) {}
 // CHECK: define hidden void @_Z2f3dd
 // CHECK: define void @_Z2f2v
 // CHECK: define hidden void @_Z2t0v
+// CHECK: define void @_Z3fd1Pi
+// CHECK: define void @_Z3fd2Pi
+// CHECK: define void @_Z3fd3Pi
+// CHECK: define void @_Z3fd4Pi
+// CHECK: define void @_Z3fd5PFivE
+// CHECK: define void @_Z3fd6PFivE
+// CHECK: define void @_Z3fd7PFivE
+// CHECK: define void @_Z3fd8PFivE
 // CHECK: define hidden void @_Z2f5v
 // CHECK: define void @_ZN2N02f0Ev
 // CHECK: define void @_ZN2N02f1Ev

>From 266840a826c4c4bace99b14b53511fa714509681 Mon Sep 17 00:00:00 2001
From: Sean Perry <perry at ca.ibm.com>
Date: Mon, 25 Nov 2024 22:44:38 -0600
Subject: [PATCH 09/15] change func names

---
 clang/include/clang/Sema/Sema.h | 4 ++--
 clang/lib/Sema/Sema.cpp         | 2 +-
 clang/lib/Sema/SemaAttr.cpp     | 4 ++--
 3 files changed, 5 insertions(+), 5 deletions(-)

diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 871a7b196b5b3..f37abf12c8634 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -1931,12 +1931,12 @@ class Sema final : public SemaBase {
       SymbolNames;
   SymbolNames PendingExportNames;
 
-  FunctionDecl *tryFunctionLookUp(NestedNameSpecifier *NestedName,
+  FunctionDecl *tryFunctionLookUpInPragma(NestedNameSpecifier *NestedName,
                                   SourceLocation NameLoc);
 
   /// trySymbolLookUp try to look up a decl matching the nested specifier
   /// with optional type list.
-  NamedDecl *trySymbolLookUp(NestedNameSpecifier *NestedName,
+  NamedDecl *trySymbolLookUpInPragma(NestedNameSpecifier *NestedName,
                              const clang::Sema::SymbolLabel &Label);
 
   /// ActonPragmaExport - called on well-formed '\#pragma export'.
diff --git a/clang/lib/Sema/Sema.cpp b/clang/lib/Sema/Sema.cpp
index f63508a7b77d5..3c6a3cbf4534f 100644
--- a/clang/lib/Sema/Sema.cpp
+++ b/clang/lib/Sema/Sema.cpp
@@ -1409,7 +1409,7 @@ void Sema::ActOnEndOfTranslationUnit() {
     NestedNameSpecifier *Name = Iter.first;
     PendingSymbolOverloads &Overloads = Iter.second;
     for (auto &I : Overloads) {
-      if (auto *D = trySymbolLookUp(Name, I)) {
+      if (auto *D = trySymbolLookUpInPragma(Name, I)) {
         if (D->hasExternalFormalLinkage()) {
           if (D->isCXXClassMember()) {
             D->addAttr(VisibilityAttr::CreateImplicit(
diff --git a/clang/lib/Sema/SemaAttr.cpp b/clang/lib/Sema/SemaAttr.cpp
index 8b9d012ea0e0d..241a41a038c67 100644
--- a/clang/lib/Sema/SemaAttr.cpp
+++ b/clang/lib/Sema/SemaAttr.cpp
@@ -1361,7 +1361,7 @@ static bool typeListMatches(ASTContext& Context, FunctionDecl *FD,
   return true;
 }
 
-FunctionDecl *Sema::tryFunctionLookUp(NestedNameSpecifier *NestedName,
+FunctionDecl *Sema::tryFunctionLookUpInPragma(NestedNameSpecifier *NestedName,
                                       SourceLocation NameLoc) {
   assert(!NestedName->getPrefix() ||
          NestedName->getPrefix()->getKind() == NestedNameSpecifier::Identifier);
@@ -1392,7 +1392,7 @@ FunctionDecl *Sema::tryFunctionLookUp(NestedNameSpecifier *NestedName,
   return nullptr;
 }
 
-NamedDecl *Sema::trySymbolLookUp(NestedNameSpecifier *NestedName,
+NamedDecl *Sema::trySymbolLookUpInPragma(NestedNameSpecifier *NestedName,
                                  const clang::Sema::SymbolLabel &Label) {
 
   assert(!NestedName->getPrefix() ||

>From 0fcfcfe3262a0323b4c2a00cee42901e6e1a8bd2 Mon Sep 17 00:00:00 2001
From: Sean Perry <perry at ca.ibm.com>
Date: Fri, 28 Mar 2025 17:54:03 -0400
Subject: [PATCH 10/15] address comments

---
 clang/include/clang/Basic/AttrDocs.td      | 10 ++++----
 clang/include/clang/Parse/Parser.h         |  2 +-
 clang/include/clang/Sema/DeclSpec.h        | 21 +++++++++--------
 clang/lib/Parse/ParseDecl.cpp              |  5 ++--
 clang/lib/Parse/ParsePragma.cpp            | 16 +++++++------
 clang/lib/Parse/ParseStmt.cpp              |  8 +++++++
 clang/lib/Sema/DeclSpec.cpp                |  4 ++--
 clang/lib/Sema/SemaAttr.cpp                | 14 ++++-------
 clang/lib/Sema/SemaType.cpp                |  2 --
 clang/test/CodeGen/attr-export-failing.cpp |  5 ----
 clang/test/CodeGen/attr-export.cpp         |  2 ++
 clang/test/CodeGen/pragma-export.c         | 14 +----------
 clang/test/CodeGen/zos-pragmas.c           |  1 -
 clang/test/CodeGen/zos-pragmas.cpp         |  1 -
 clang/test/Sema/attr-export-failing.cpp    | 27 ++++++++++++++++++++++
 15 files changed, 74 insertions(+), 58 deletions(-)
 delete mode 100644 clang/test/CodeGen/attr-export-failing.cpp
 create mode 100644 clang/test/Sema/attr-export-failing.cpp

diff --git a/clang/include/clang/Basic/AttrDocs.td b/clang/include/clang/Basic/AttrDocs.td
index 64f79faeae1ba..16cde6be8122d 100644
--- a/clang/include/clang/Basic/AttrDocs.td
+++ b/clang/include/clang/Basic/AttrDocs.td
@@ -7505,18 +7505,18 @@ attribute requires a string literal argument to identify the handle being releas
 def zOSExportDocs : Documentation {
   let Category = DocCatFunction;
   let Content = [{
-Use the _Export keyword with a function name or external variable to declare
+Use the ``_Export`` keyword on a function or external variable to declare
 that it is to be exported (made available to other modules). You must define
-the object name in the same translation unit in which you use the _Export
+the object name in the same translation unit in which you use the ``_Export``
 keyword. For example:
 
 .. code-block:: c
 
   int _Export anthony(float);
 
-This statement exports the function anthony, if you define the function in the
-translation unit. The _Export keyword must immediately precede the object name.
-If you apply the _Export keyword to a class, the compiler automatically exports
+This statement exports the function ``anthony``, if you define the function in the
+translation unit. The ``_Export`` keyword must immediately precede the declaration name.
+If you apply the ``_Export`` keyword to a class, the compiler automatically exports
 all static data members and member functions of the class. However, if you want
 it to apply to individual class members, then you must apply it to each member
 that can be referenced.
diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h
index 0cc127d28a030..236555daebf65 100644
--- a/clang/include/clang/Parse/Parser.h
+++ b/clang/include/clang/Parse/Parser.h
@@ -857,7 +857,7 @@ class Parser : public CodeCompletionHandler {
 
   /// Helper functions for handling zOS pragmas.
   NestedNameSpecifier *zOSParseIdentifier(StringRef PragmaName,
-                                          IdentifierInfo *IdentName);
+                                          const IdentifierInfo *IdentName);
   bool zOSParseParameterList(StringRef PragmaName,
                              std::optional<SmallVector<QualType, 4>> &TypeList,
                              Qualifiers &CVQual);
diff --git a/clang/include/clang/Sema/DeclSpec.h b/clang/include/clang/Sema/DeclSpec.h
index df4b2de66a667..2cc1949b39c11 100644
--- a/clang/include/clang/Sema/DeclSpec.h
+++ b/clang/include/clang/Sema/DeclSpec.h
@@ -399,7 +399,7 @@ class DeclSpec {
   LLVM_PREFERRED_TYPE(bool)
   unsigned FS_noreturn_specified : 1;
   LLVM_PREFERRED_TYPE(bool)
-  unsigned export_specified : 1;
+  unsigned ExportSpecified : 1; // z/OS extension
 
   // friend-specifier
   LLVM_PREFERRED_TYPE(bool)
@@ -446,7 +446,7 @@ class DeclSpec {
   SourceLocation FS_forceinlineLoc;
   SourceLocation FriendLoc, ModulePrivateLoc, ConstexprLoc;
   SourceLocation TQ_pipeLoc;
-  SourceLocation exportLoc;
+  SourceLocation ExportLoc;
 
   WrittenBuiltinSpecs writtenBS;
   void SaveWrittenBuiltinSpecs();
@@ -495,7 +495,7 @@ class DeclSpec {
         TypeSpecPipe(false), TypeSpecSat(false), ConstrainedAuto(false),
         TypeQualifiers(TQ_unspecified), FS_inline_specified(false),
         FS_forceinline_specified(false), FS_virtual_specified(false),
-        FS_noreturn_specified(false), export_specified(false),
+        FS_noreturn_specified(false), ExportSpecified(false),
         FriendSpecifiedFirst(false), ConstexprSpecifier(static_cast<unsigned>(
                                          ConstexprSpecKind::Unspecified)),
         Attrs(attrFactory), writtenBS(), ObjCQualifiers(nullptr) {}
@@ -664,8 +664,8 @@ class DeclSpec {
   bool isNoreturnSpecified() const { return FS_noreturn_specified; }
   SourceLocation getNoreturnSpecLoc() const { return FS_noreturnLoc; }
 
-  bool isExportSpecified() const { return export_specified; }
-  SourceLocation getExportSpecLoc() const { return exportLoc; }
+  bool isExportSpecified() const { return ExportSpecified; }
+  SourceLocation getExportSpecLoc() const { return ExportLoc; }
 
   void ClearFunctionSpecs() {
     FS_inline_specified = false;
@@ -1964,9 +1964,9 @@ class Declarator {
   LLVM_PREFERRED_TYPE(bool)
   unsigned InlineStorageUsed : 1;
 
-  /// Indicates whether this is set as _Export
+  /// Indicates whether this is set as _Export.
   LLVM_PREFERRED_TYPE(bool)
-  unsigned ExportSpecified : 1;
+  unsigned ExportSpecified : 1; // z/OS extension
 
   /// Indicates whether this declarator has an initializer.
   LLVM_PREFERRED_TYPE(bool)
@@ -2014,7 +2014,7 @@ class Declarator {
   /// this declarator as a parameter pack.
   SourceLocation EllipsisLoc;
 
-  /// The source location of the _Export keyword on this declarator
+  /// The source location of the _Export keyword on this declarator.
   SourceLocation ExportLoc;
 
   Expr *PackIndexingExpr;
@@ -2126,13 +2126,13 @@ class Declarator {
       Range.setEnd(SR.getEnd());
   }
 
-  /// Set this declarator as _Export
+  /// Set this declarator as _Export.
   void SetExport(SourceLocation Loc) {
     ExportSpecified = true;
     ExportLoc = Loc;
   }
 
-  /// Whether this declarator is marked as _Export
+  /// Whether this declarator is marked as _Export.
   bool IsExport() const { return ExportSpecified; }
 
   /// Get the location of the _Export keyword
@@ -2157,6 +2157,7 @@ class Declarator {
     ExportSpecified = false;
     CommaLoc = SourceLocation();
     EllipsisLoc = SourceLocation();
+    ExportLoc = SourceLocation();
     PackIndexingExpr = nullptr;
   }
 
diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp
index 8ce41e670d72e..63a270cc010ce 100644
--- a/clang/lib/Parse/ParseDecl.cpp
+++ b/clang/lib/Parse/ParseDecl.cpp
@@ -4457,8 +4457,7 @@ void Parser::ParseDeclarationSpecifiers(
       break;
 
     case tok::kw__Export:
-      // If we find kw__Export, it is being applied to a var or function
-      // This will be handled in ParseDeclaratorInternal()
+      // We're done with the declaration-specifiers.
       goto DoneWithDeclSpec;
       break;
 
@@ -6187,7 +6186,9 @@ bool Parser::isDeclarationSpecifier(
   case tok::kw_virtual:
   case tok::kw_explicit:
   case tok::kw__Noreturn:
+#if SDP // need this in declspec?
   case tok::kw__Export:
+#endif
 
     // alignment-specifier
   case tok::kw__Alignas:
diff --git a/clang/lib/Parse/ParsePragma.cpp b/clang/lib/Parse/ParsePragma.cpp
index 44effb2509414..fedce362c12d9 100644
--- a/clang/lib/Parse/ParsePragma.cpp
+++ b/clang/lib/Parse/ParsePragma.cpp
@@ -1417,8 +1417,9 @@ bool Parser::HandlePragmaMSAllocText(StringRef PragmaName,
   return true;
 }
 
-NestedNameSpecifier *Parser::zOSParseIdentifier(StringRef PragmaName,
-                                                IdentifierInfo *IdentName) {
+NestedNameSpecifier *
+Parser::zOSParseIdentifier(StringRef PragmaName,
+                           const IdentifierInfo *IdentName) {
   NestedNameSpecifier *NestedId = nullptr;
   if (Tok.is(tok::coloncolon)) {
     NestedId = NestedNameSpecifier::Create(Actions.Context, IdentName);
@@ -1435,14 +1436,13 @@ NestedNameSpecifier *Parser::zOSParseIdentifier(StringRef PragmaName,
   }
   while (Tok.is(tok::coloncolon)) {
     PP.Lex(Tok);
-    IdentName = Tok.getIdentifierInfo();
+    IdentifierInfo *II = Tok.getIdentifierInfo();
     if (Tok.isNot(tok::identifier)) {
       PP.Diag(Tok.getLocation(), diag::warn_pragma_expected_identifier)
           << PragmaName;
       return nullptr;
     }
-    NestedId =
-        NestedNameSpecifier::Create(Actions.Context, NestedId, IdentName);
+    NestedId = NestedNameSpecifier::Create(Actions.Context, NestedId, II);
     PP.Lex(Tok);
   }
   return NestedId;
@@ -1455,12 +1455,14 @@ bool Parser::zOSParseParameterList(
     TypeList = SmallVector<QualType, 4>();
     PP.Lex(Tok);
     while (Tok.isNot(tok::eof) && !Tok.is(tok::r_paren)) {
-      //SourceRange MatchingCTypeRange;
       TypeResult TResult = ParseTypeName(nullptr, DeclaratorContext::Prototype);
       if (!TResult.isInvalid()) {
         QualType QT = TResult.get().get();
-        if (!QT.getTypePtr()->isVoidType())
+        if (!QT.getTypePtr()->isVoidType()) {
+          fprintf(stderr, "SDP: paramType -\n");
+          QT.getCanonicalType()->dump();
           TypeList->push_back(QT);
+        }
       }
       if (Tok.is(tok::comma) || Tok.is(tok::identifier))
         PP.Lex(Tok);
diff --git a/clang/lib/Parse/ParseStmt.cpp b/clang/lib/Parse/ParseStmt.cpp
index cd4504630f871..366bc75d80c58 100644
--- a/clang/lib/Parse/ParseStmt.cpp
+++ b/clang/lib/Parse/ParseStmt.cpp
@@ -538,6 +538,11 @@ StmtResult Parser::ParseStatementOrDeclarationAfterAttributes(
   case tok::annot_pragma_attribute:
     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.
@@ -1122,6 +1127,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/Sema/DeclSpec.cpp b/clang/lib/Sema/DeclSpec.cpp
index d51cb08204519..181d47745c62e 100644
--- a/clang/lib/Sema/DeclSpec.cpp
+++ b/clang/lib/Sema/DeclSpec.cpp
@@ -1105,8 +1105,8 @@ bool DeclSpec::setFunctionSpecNoreturn(SourceLocation Loc,
 }
 
 bool DeclSpec::setExportSpec(SourceLocation Loc) {
-  export_specified = true;
-  exportLoc = Loc;
+  ExportSpecified = true;
+  ExportLoc = Loc;
   return false;
 }
 
diff --git a/clang/lib/Sema/SemaAttr.cpp b/clang/lib/Sema/SemaAttr.cpp
index ad926a5bdb34a..1ef321f8ab813 100644
--- a/clang/lib/Sema/SemaAttr.cpp
+++ b/clang/lib/Sema/SemaAttr.cpp
@@ -1334,29 +1334,25 @@ static bool typeListMatches(ASTContext& Context, FunctionDecl *FD,
   for (unsigned i = 0; i != FD->getNumParams(); ++i) {
     const ParmVarDecl *PVD = FD->getParamDecl(i);
     QualType ParmType = PVD->getType().getCanonicalType();
-    fprintf(stderr, "SDP: --- pramtype\n");
-    ParmType->dump();
+#if SDP
+    // SDP QualType ParmType = PVD->getOriginalType().getCanonicalType();
     if (ParmType->isArrayType())
       ParmType = Context.getArrayDecayedType(ParmType);
     else if (ParmType->isFunctionType())
-    { fprintf(stderr, "  - is function\n");
       ParmType = Context.getPointerType(ParmType);
-      }
-    ParmType->dump();
+#endif
 
     QualType MapArgType = (*Label.TypeList)[i].getCanonicalType();
-    fprintf(stderr, "SDP: --- MapArgtype\n");
-    MapArgType->dump();
+#if SDP
     if (MapArgType->isArrayType())
       MapArgType = Context.getArrayDecayedType(MapArgType);
     else if (MapArgType->isFunctionType())
-    { fprintf(stderr, "  - is function\n");
       MapArgType = Context.getPointerType(MapArgType);
-      }
     MapArgType.getDesugaredType(Context)->dump();
 
     assert(!ParmType->canDecayToPointerType());
     assert(!MapArgType->canDecayToPointerType());
+#endif
     if (ParmType != MapArgType)
       return false;
   }
diff --git a/clang/lib/Sema/SemaType.cpp b/clang/lib/Sema/SemaType.cpp
index 6e573a137ce1b..11943c0b53591 100644
--- a/clang/lib/Sema/SemaType.cpp
+++ b/clang/lib/Sema/SemaType.cpp
@@ -4250,7 +4250,6 @@ static TypeSourceInfo *GetFullTypeForDeclarator(TypeProcessingState &state,
   // The TypeSourceInfo that this function returns will not be a null type.
   // If there is an error, this function will fill in a dummy type as fallback.
   QualType T = declSpecType;
-  fprintf(stderr, "SDP: === GetFullTypeForDeclarator\n"); T->dump(); fprintf(stderr, "SDP: === GetFullTypeForDeclarator\n");
   Declarator &D = state.getDeclarator();
   Sema &S = state.getSema();
   ASTContext &Context = S.Context;
@@ -5723,7 +5722,6 @@ TypeSourceInfo *Sema::GetTypeForDeclarator(Declarator &D) {
 
   TypeSourceInfo *ReturnTypeInfo = nullptr;
   QualType T = GetDeclSpecTypeForDeclarator(state, ReturnTypeInfo);
-  fprintf(stderr, "SDP: === GetTypeForDeclarator\n"); T->dump(); fprintf(stderr, "SDP: === GetTypeForDeclarator\n");
   if (D.isPrototypeContext() && getLangOpts().ObjCAutoRefCount)
     inferARCWriteback(state, T);
 
diff --git a/clang/test/CodeGen/attr-export-failing.cpp b/clang/test/CodeGen/attr-export-failing.cpp
deleted file mode 100644
index 612e3bb554430..0000000000000
--- a/clang/test/CodeGen/attr-export-failing.cpp
+++ /dev/null
@@ -1,5 +0,0 @@
-// REQUIRES: systemz-registered-target
-// RUN: not %clang_cc1 -triple s390x-none-zos -fzos-extensions %s
-__attribute__((visibility("hidden"))) int _Export i; // expected-error {{visibility does not match previous declaration}}
-class __attribute__((visibility("hidden"))) _Export C; // expected-error {{visibility does not match previous declaration}}
-
diff --git a/clang/test/CodeGen/attr-export.cpp b/clang/test/CodeGen/attr-export.cpp
index 23eb5163d130e..41649b9f55f83 100644
--- a/clang/test/CodeGen/attr-export.cpp
+++ b/clang/test/CodeGen/attr-export.cpp
@@ -29,6 +29,7 @@ class _Export class1 {
 public:
   void foo();
 };
+#if 0
 
 class class2 {
 public:
@@ -50,3 +51,4 @@ class class3 {
 
 void class3::foo() {};
 void class3::bar() {};
+#endif
diff --git a/clang/test/CodeGen/pragma-export.c b/clang/test/CodeGen/pragma-export.c
index 855fb31568ac8..c5b0206ca1f07 100644
--- a/clang/test/CodeGen/pragma-export.c
+++ b/clang/test/CodeGen/pragma-export.c
@@ -1,33 +1,21 @@
 // REQUIRES: systemz-registered-target
 // RUN: %clang_cc1 %s -emit-llvm -fzos-extensions -triple s390x-none-zos -fvisibility=hidden -verify -o - | FileCheck %s
 
-// Testing missing declarations.
-#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}}
-
 // Testing pragma export after decl.
 void f0(void) {}
-static void sf0(void) {} // expected-warning{{#pragma export is applicable to symbols with external linkage only; not applied to 'sf0'}}
 int v0;
-static int s0; // expected-warning{{#pragma export is applicable to symbols with external linkage only; not applied to 's0'}}
 #pragma export(f0)
-#pragma export(sf0)
 #pragma export(v0)
-#pragma export(s0)
 
 // Testing pragma export before decl.
 #pragma export(f1)
-#pragma export(sf1)
 #pragma export(v1)
-#pragma export(s1)
 void f1(void) {}
-static void sf1(void) {} // expected-warning{{#pragma export is applicable to symbols with external linkage only; not applied to 'sf1'}}
 int v1;
-static int s1; // expected-warning{{#pragma export is applicable to symbols with external linkage only; not applied to 's1'}}
 
 void f2(void) {}
 
-void t0(void) {}
+void t0(void) { f2();}
 
 // Testing pragma export after decl and usage.
 #pragma export(f2)
diff --git a/clang/test/CodeGen/zos-pragmas.c b/clang/test/CodeGen/zos-pragmas.c
index adcc541b7f767..e2bd03a33e20a 100644
--- a/clang/test/CodeGen/zos-pragmas.c
+++ b/clang/test/CodeGen/zos-pragmas.c
@@ -1,6 +1,5 @@
 // REQUIRES: systemz-registered-target
 // RUN: %clang_cc1 -emit-llvm -triple s390x-none-zos -fvisibility=hidden %s -o - | FileCheck %s
-// expected-no-diagnostics
 
 int a,b,c;
 #pragma export(a) export(b) export(c)
diff --git a/clang/test/CodeGen/zos-pragmas.cpp b/clang/test/CodeGen/zos-pragmas.cpp
index e589058e9726b..4485d6f921558 100644
--- a/clang/test/CodeGen/zos-pragmas.cpp
+++ b/clang/test/CodeGen/zos-pragmas.cpp
@@ -1,6 +1,5 @@
 // REQUIRES: systemz-registered-target
 // RUN: %clang_cc1 -x c++ -emit-llvm -triple s390x-none-zos -fvisibility=hidden %s -o - | FileCheck %s
-// expected-no-diagnostics
 
 int a,b,c;
 #pragma export(a) export(b) export(c)
diff --git a/clang/test/Sema/attr-export-failing.cpp b/clang/test/Sema/attr-export-failing.cpp
new file mode 100644
index 0000000000000..5eaee7a8d66ef
--- /dev/null
+++ b/clang/test/Sema/attr-export-failing.cpp
@@ -0,0 +1,27 @@
+// REQUIRES: systemz-registered-target
+// RUN: not %clang_cc1 -triple s390x-none-zos -fzos-extensions %s -fsyntax-only -verify
+__attribute__((visibility("hidden"))) int _Export i; // expected-error {{visibility does not match previous declaration}}
+class __attribute__((visibility("hidden"))) _Export C; // expected-error {{visibility does not match previous declaration}}
+
+#pragma export(sf1)
+#pragma export(s1)
+static void sf1(void) {} // expected-warning{{#pragma export is applicable to symbols with external linkage only; not applied to 'sf1'}}
+static int s1; // expected-warning{{#pragma export is applicable to symbols with external linkage only; not applied to 's1'}}
+
+static void sf0(void) {} // expected-warning{{#pragma export is applicable to symbols with external linkage only; not applied to 'sf0'}}
+int v0;
+static int s0; // expected-warning{{#pragma export is applicable to symbols with external linkage only; not applied to 's0'}}
+#pragma export(sf0)
+#pragma export(s0)
+
+typedef int _Export ty;
+ty x;
+int f(int _Export x);
+static int _Export s;
+struct S {
+  int _Export nonstaticdatamember;
+};
+void g() {
+  int _Export automatic;
+}
+

>From b54bd170dadb01cb80a5ca1ee003bf890f83035b Mon Sep 17 00:00:00 2001
From: Sean Perry <perry at ca.ibm.com>
Date: Mon, 31 Mar 2025 17:50:07 -0400
Subject: [PATCH 11/15] handle arrays & func ptrs better

---
 clang/lib/Parse/ParsePragma.cpp         |  4 +---
 clang/lib/Sema/SemaAttr.cpp             | 29 ++++++++-----------------
 clang/test/CodeGen/attr-export.cpp      |  2 --
 clang/test/CodeGen/pragma-export.c      |  2 +-
 clang/test/CodeGen/pragma-export.cpp    | 16 +-------------
 clang/test/Sema/attr-export-failing.cpp |  2 +-
 clang/test/Sema/pragma-export-failing.c | 18 +++++++++++++++
 clang/test/Sema/zos-export.c            | 12 ++++++++++
 8 files changed, 43 insertions(+), 42 deletions(-)
 create mode 100644 clang/test/Sema/pragma-export-failing.c
 create mode 100644 clang/test/Sema/zos-export.c

diff --git a/clang/lib/Parse/ParsePragma.cpp b/clang/lib/Parse/ParsePragma.cpp
index fedce362c12d9..f57a02e6cc8bf 100644
--- a/clang/lib/Parse/ParsePragma.cpp
+++ b/clang/lib/Parse/ParsePragma.cpp
@@ -1455,12 +1455,10 @@ bool Parser::zOSParseParameterList(
     TypeList = SmallVector<QualType, 4>();
     PP.Lex(Tok);
     while (Tok.isNot(tok::eof) && !Tok.is(tok::r_paren)) {
-      TypeResult TResult = ParseTypeName(nullptr, DeclaratorContext::Prototype);
+      TypeResult TResult = ParseTypeName(nullptr);
       if (!TResult.isInvalid()) {
         QualType QT = TResult.get().get();
         if (!QT.getTypePtr()->isVoidType()) {
-          fprintf(stderr, "SDP: paramType -\n");
-          QT.getCanonicalType()->dump();
           TypeList->push_back(QT);
         }
       }
diff --git a/clang/lib/Sema/SemaAttr.cpp b/clang/lib/Sema/SemaAttr.cpp
index 1ef321f8ab813..cf441ac19fce9 100644
--- a/clang/lib/Sema/SemaAttr.cpp
+++ b/clang/lib/Sema/SemaAttr.cpp
@@ -1323,7 +1323,11 @@ void Sema::AddImplicitMSFunctionNoBuiltinAttr(FunctionDecl *FD) {
     FD->addAttr(NoBuiltinAttr::CreateImplicit(Context, V.data(), V.size()));
 }
 
-static bool typeListMatches(ASTContext& Context, FunctionDecl *FD,
+static QualType getCanonicalParamType(ASTContext &C, QualType T) {
+  return C.getCanonicalParamType(T);
+}
+
+static bool typeListMatches(ASTContext &Context, FunctionDecl *FD,
                             const clang::Sema::SymbolLabel &Label) {
   assert(Label.TypeList.has_value());
   if (FD->getNumParams() != Label.TypeList->size()) {
@@ -1334,25 +1338,10 @@ static bool typeListMatches(ASTContext& Context, FunctionDecl *FD,
   for (unsigned i = 0; i != FD->getNumParams(); ++i) {
     const ParmVarDecl *PVD = FD->getParamDecl(i);
     QualType ParmType = PVD->getType().getCanonicalType();
-#if SDP
-    // SDP QualType ParmType = PVD->getOriginalType().getCanonicalType();
-    if (ParmType->isArrayType())
-      ParmType = Context.getArrayDecayedType(ParmType);
-    else if (ParmType->isFunctionType())
-      ParmType = Context.getPointerType(ParmType);
-#endif
-
-    QualType MapArgType = (*Label.TypeList)[i].getCanonicalType();
-#if SDP
-    if (MapArgType->isArrayType())
-      MapArgType = Context.getArrayDecayedType(MapArgType);
-    else if (MapArgType->isFunctionType())
-      MapArgType = Context.getPointerType(MapArgType);
-    MapArgType.getDesugaredType(Context)->dump();
-
-    assert(!ParmType->canDecayToPointerType());
-    assert(!MapArgType->canDecayToPointerType());
-#endif
+
+    QualType MapArgType =
+        getCanonicalParamType(Context, (*Label.TypeList)[i].getCanonicalType());
+
     if (ParmType != MapArgType)
       return false;
   }
diff --git a/clang/test/CodeGen/attr-export.cpp b/clang/test/CodeGen/attr-export.cpp
index 41649b9f55f83..23eb5163d130e 100644
--- a/clang/test/CodeGen/attr-export.cpp
+++ b/clang/test/CodeGen/attr-export.cpp
@@ -29,7 +29,6 @@ class _Export class1 {
 public:
   void foo();
 };
-#if 0
 
 class class2 {
 public:
@@ -51,4 +50,3 @@ class class3 {
 
 void class3::foo() {};
 void class3::bar() {};
-#endif
diff --git a/clang/test/CodeGen/pragma-export.c b/clang/test/CodeGen/pragma-export.c
index c5b0206ca1f07..c2f454fc1dec2 100644
--- a/clang/test/CodeGen/pragma-export.c
+++ b/clang/test/CodeGen/pragma-export.c
@@ -1,5 +1,5 @@
 // REQUIRES: systemz-registered-target
-// RUN: %clang_cc1 %s -emit-llvm -fzos-extensions -triple s390x-none-zos -fvisibility=hidden -verify -o - | FileCheck %s
+// 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) {}
diff --git a/clang/test/CodeGen/pragma-export.cpp b/clang/test/CodeGen/pragma-export.cpp
index 0041e74166b0e..30a928a6d8708 100644
--- a/clang/test/CodeGen/pragma-export.cpp
+++ b/clang/test/CodeGen/pragma-export.cpp
@@ -1,31 +1,17 @@
 // REQUIRES: systemz-registered-target
-// RUN: %clang_cc1 %s -emit-llvm -triple s390x-none-zos -fzos-extensions -fvisibility=hidden -verify -o - | FileCheck %s
-
-// Testing missing declarations.
-#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(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}}
+// 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) {}
-static void sf0(void) {} // expected-warning{{#pragma export is applicable to symbols with external linkage only; not applied to 'sf0'}}
 int v0;
-static int s0; // expected-warning{{#pragma export is applicable to symbols with external linkage only; not applied to 's0'}}
 #pragma export(f0)
-#pragma export(sf0)
 #pragma export(v0)
-#pragma export(s0)
 
 // Testing pragma export before decl.
 #pragma export(f1)
-#pragma export(sf1)
 #pragma export(v1)
-#pragma export(s1)
 void f1(void) {}
-static void sf1(void) {} // expected-warning{{#pragma export is applicable to symbols with external linkage only; not applied to 'sf1'}}
 int v1;
-static int s1; // expected-warning{{#pragma export is applicable to symbols with external linkage only; not applied to 's1'}}
 
 // Testing overloaded functions.
 #pragma export(f2(double, double))
diff --git a/clang/test/Sema/attr-export-failing.cpp b/clang/test/Sema/attr-export-failing.cpp
index 5eaee7a8d66ef..d433b3b0ff9fc 100644
--- a/clang/test/Sema/attr-export-failing.cpp
+++ b/clang/test/Sema/attr-export-failing.cpp
@@ -1,5 +1,5 @@
 // REQUIRES: systemz-registered-target
-// RUN: not %clang_cc1 -triple s390x-none-zos -fzos-extensions %s -fsyntax-only -verify
+// RUN: %clang_cc1 -triple s390x-none-zos -fzos-extensions %s -fsyntax-only -verify
 __attribute__((visibility("hidden"))) int _Export i; // expected-error {{visibility does not match previous declaration}}
 class __attribute__((visibility("hidden"))) _Export C; // expected-error {{visibility does not match previous declaration}}
 
diff --git a/clang/test/Sema/pragma-export-failing.c b/clang/test/Sema/pragma-export-failing.c
new file mode 100644
index 0000000000000..6629762326593
--- /dev/null
+++ b/clang/test/Sema/pragma-export-failing.c
@@ -0,0 +1,18 @@
+// 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(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(sf1)
+#pragma export(s1)
+static void sf1(void) {} // expected-warning{{#pragma export is applicable to symbols with external linkage only; not applied to 'sf1'}}
+static int s1; // expected-warning{{#pragma export is applicable to symbols with external linkage only; not applied to 's1'}}
+
+static void sf0(void) {} // expected-warning{{#pragma export is applicable to symbols with external linkage only; not applied to 'sf0'}}
+int v0;
+static int s0; // expected-warning{{#pragma export is applicable to symbols with external linkage only; not applied to 's0'}}
+#pragma export(sf0)
+#pragma export(s0)
diff --git a/clang/test/Sema/zos-export.c b/clang/test/Sema/zos-export.c
new file mode 100644
index 0000000000000..67bec95d6c624
--- /dev/null
+++ b/clang/test/Sema/zos-export.c
@@ -0,0 +1,12 @@
+// RUN: %clang_cc1 -triple s390x-ibm-zos %s -fsyntax-only -verify
+
+typedef int _Export ty;
+ty x;
+int f(int _Export x);
+static int _Export s;
+struct S {
+  int _Export nonstaticdatamember;
+};
+void g() {
+  int _Export automatic;
+}

>From 6ba783c683a74e08693954007058e4a92f041712 Mon Sep 17 00:00:00 2001
From: Sean Perry <perry at ca.ibm.com>
Date: Fri, 4 Apr 2025 10:15:16 -0400
Subject: [PATCH 12/15] Add error checking

---
 .../clang/Basic/DiagnosticSemaKinds.td        |  2 +
 clang/lib/Parse/ParseStmt.cpp                 |  1 +
 clang/lib/Sema/SemaDecl.cpp                   | 20 ++++++++-
 clang/test/Sema/attr-export-failing.cpp       | 12 -----
 clang/test/Sema/zos-export.c                  | 23 +++++++---
 clang/test/Sema/zos-export.cpp                | 44 +++++++++++++++++++
 6 files changed, 83 insertions(+), 19 deletions(-)
 create mode 100644 clang/test/Sema/zos-export.cpp

diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index e255fc6036e9a..2ab1b2b6ee3e6 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -9498,6 +9498,8 @@ def warn_redefine_extname_not_applied : Warning<
   "#pragma redefine_extname is applicable to external C declarations only; "
   "not applied to %select{function|variable}0 %1">,
   InGroup<Pragmas>;
+def err_cannot_be_exported : Error<
+  "cannot be '_Export` qualified">;
 } // End of general sema category.
 
 // inline asm.
diff --git a/clang/lib/Parse/ParseStmt.cpp b/clang/lib/Parse/ParseStmt.cpp
index 366bc75d80c58..2a13220efa481 100644
--- a/clang/lib/Parse/ParseStmt.cpp
+++ b/clang/lib/Parse/ParseStmt.cpp
@@ -538,6 +538,7 @@ StmtResult Parser::ParseStatementOrDeclarationAfterAttributes(
   case tok::annot_pragma_attribute:
     HandlePragmaAttribute();
     return StmtEmpty();
+
   case tok::annot_pragma_export:
     ProhibitAttributes(CXX11Attrs);
     ProhibitAttributes(GNUAttrs);
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index 445799fc25f76..3659eda3cc205 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -6503,10 +6503,10 @@ NamedDecl *Sema::HandleDeclarator(Scope *S, Declarator &D,
           existingAttr->getVisibility();
       if (existingValue != VisibilityAttr::Default)
         Diag(D.getExportLoc(), diag::err_mismatched_visibility);
-    } else {
+    } else
+      // Add VisibilityAttr::Default since the default could be hidden, etc
       New->addAttr(
           VisibilityAttr::CreateImplicit(Context, VisibilityAttr::Default));
-    }
   }
 
   // If this has an identifier and is not a function template specialization,
@@ -6746,6 +6746,9 @@ Sema::ActOnTypedefDeclarator(Scope* S, Declarator& D, DeclContext* DC,
     return nullptr;
   }
 
+  if (D.IsExport())
+    Diag(D.getName().StartLocation, diag::err_cannot_be_exported);
+
   TypedefDecl *NewTD = ParseTypedefDecl(S, D, TInfo->getType(), TInfo);
   if (!NewTD) return nullptr;
 
@@ -8208,6 +8211,9 @@ NamedDecl *Sema::ActOnVariableDeclarator(
 
   ProcessPragmaWeak(S, NewVD);
 
+  if (D.IsExport() && !NewVD->hasExternalFormalLinkage())
+    Diag(D.getIdentifierLoc(), diag::err_cannot_be_exported);
+
   // If this is the first declaration of an extern C variable, update
   // the map of such variables.
   if (NewVD->isFirstDecl() && !NewVD->isInvalidDecl() &&
@@ -10851,6 +10857,10 @@ Sema::ActOnFunctionDeclarator(Scope *S, Declarator &D, DeclContext *DC,
   }
 
   ProcessPragmaWeak(S, NewFD);
+
+  if (D.IsExport() && !NewFD->hasExternalFormalLinkage())
+    Diag(D.getIdentifierLoc(), diag::err_cannot_be_exported);
+
   checkAttributesAfterMerging(*this, *NewFD);
 
   AddKnownFunctionAttributes(NewFD);
@@ -15343,6 +15353,9 @@ Decl *Sema::ActOnParamDeclarator(Scope *S, Declarator &D,
   if (getLangOpts().OpenCL)
     deduceOpenCLAddressSpace(New);
 
+  if (D.IsExport())
+    Diag(D.getIdentifierLoc(), diag::err_cannot_be_exported);
+
   return New;
 }
 
@@ -18705,6 +18718,9 @@ FieldDecl *Sema::HandleField(Scope *S, RecordDecl *Record,
   } else
     Record->addDecl(NewFD);
 
+  if (D.IsExport())
+    Diag(D.getIdentifierLoc(), diag::err_cannot_be_exported);
+
   return NewFD;
 }
 
diff --git a/clang/test/Sema/attr-export-failing.cpp b/clang/test/Sema/attr-export-failing.cpp
index d433b3b0ff9fc..f381f89468c32 100644
--- a/clang/test/Sema/attr-export-failing.cpp
+++ b/clang/test/Sema/attr-export-failing.cpp
@@ -13,15 +13,3 @@ int v0;
 static int s0; // expected-warning{{#pragma export is applicable to symbols with external linkage only; not applied to 's0'}}
 #pragma export(sf0)
 #pragma export(s0)
-
-typedef int _Export ty;
-ty x;
-int f(int _Export x);
-static int _Export s;
-struct S {
-  int _Export nonstaticdatamember;
-};
-void g() {
-  int _Export automatic;
-}
-
diff --git a/clang/test/Sema/zos-export.c b/clang/test/Sema/zos-export.c
index 67bec95d6c624..dd6d754690246 100644
--- a/clang/test/Sema/zos-export.c
+++ b/clang/test/Sema/zos-export.c
@@ -1,12 +1,25 @@
 // RUN: %clang_cc1 -triple s390x-ibm-zos %s -fsyntax-only -verify
 
-typedef int _Export ty;
+typedef int _Export ty; //expected-error {{cannot be '_Export` qualified}}
 ty x;
-int f(int _Export x);
-static int _Export s;
+int f(int _Export argument); //expected-error {{cannot be '_Export` qualified}}
+static int _Export file_scope_static; //expected-error {{cannot be '_Export` qualified}}
 struct S {
-  int _Export nonstaticdatamember;
+  int _Export nonstaticdatamember; //expected-error {{cannot be '_Export` qualified}}
 };
 void g() {
-  int _Export automatic;
+  int _Export automatic; //expected-error {{cannot be '_Export` qualified}}
 }
+
+static void _Export static_func() { //expected-error {{cannot be '_Export` qualified}}
+}
+
+void _Export h() {
+  static_func();
+}
+
+void j() {
+  static int _Export sl = 0; //expected-error {{cannot be '_Export` qualified}}
+}
+
+int _Export file_scope;
diff --git a/clang/test/Sema/zos-export.cpp b/clang/test/Sema/zos-export.cpp
new file mode 100644
index 0000000000000..1417c6396952c
--- /dev/null
+++ b/clang/test/Sema/zos-export.cpp
@@ -0,0 +1,44 @@
+// RUN: %clang_cc1 -triple s390x-ibm-zos %s -fsyntax-only -verify
+
+typedef int _Export ty; //expected-error {{cannot be '_Export` qualified}}
+ty typedef_var;
+int f(int _Export argument); //expected-error {{cannot be '_Export` qualified}}
+static int _Export file_scope_static; //expected-error {{cannot be '_Export` qualified}}
+struct S {
+  int _Export nonstaticdatamember; //expected-error {{cannot be '_Export` qualified}}
+};
+void g() {
+  int _Export automatic; //expected-error {{cannot be '_Export` qualified}}
+}
+
+static void _Export static_func() { //expected-error {{cannot be '_Export` qualified}}
+}
+
+void _Export h() {
+  static_func();
+}
+
+void j() {
+  static int _Export sl = 0; //expected-error {{cannot be '_Export` qualified}}
+}
+
+int _Export file_scope;
+
+struct _Export SE {
+};
+
+struct ST {
+  void _Export f();
+  virtual void _Export v_();
+  static int _Export i;
+};
+
+namespace {
+  int _Export anon_var; //expected-error {{cannot be '_Export` qualified}}
+  extern "C" int _Export anon_C_var;
+  void _Export anon_f() {} //expected-error {{cannot be '_Export` qualified}}
+  extern "C" void _Export anon_C_f() {}
+  struct anon_S {
+    static int _Export anon_static_data_member; //expected-error {{cannot be '_Export` qualified}}
+  };
+}

>From a1faeca40bc76bd1a48b05fceffe16085521563b Mon Sep 17 00:00:00 2001
From: Sean Perry <perry at ca.ibm.com>
Date: Fri, 4 Apr 2025 10:36:50 -0400
Subject: [PATCH 13/15] remove dead code

---
 clang/lib/Parse/ParseDecl.cpp | 3 ---
 1 file changed, 3 deletions(-)

diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp
index 92b730414284c..efd3d7f421d13 100644
--- a/clang/lib/Parse/ParseDecl.cpp
+++ b/clang/lib/Parse/ParseDecl.cpp
@@ -6188,9 +6188,6 @@ bool Parser::isDeclarationSpecifier(
   case tok::kw_virtual:
   case tok::kw_explicit:
   case tok::kw__Noreturn:
-#if SDP // need this in declspec?
-  case tok::kw__Export:
-#endif
 
     // alignment-specifier
   case tok::kw__Alignas:

>From 7871a6deb6744c9d7a42effcf9bdcbce48308a06 Mon Sep 17 00:00:00 2001
From: Sean Perry <perry at ca.ibm.com>
Date: Fri, 4 Apr 2025 10:56:12 -0400
Subject: [PATCH 14/15] formatting

---
 clang/include/clang/Sema/Sema.h |  4 ++--
 clang/lib/Sema/SemaAttr.cpp     | 10 ++++++----
 2 files changed, 8 insertions(+), 6 deletions(-)

diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index a8cfcba2a96a8..4582bbc9baeeb 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -1962,12 +1962,12 @@ class Sema final : public SemaBase {
   SymbolNames PendingExportNames;
 
   FunctionDecl *tryFunctionLookUpInPragma(NestedNameSpecifier *NestedName,
-                                  SourceLocation NameLoc);
+                                          SourceLocation NameLoc);
 
   /// trySymbolLookUp try to look up a decl matching the nested specifier
   /// with optional type list.
   NamedDecl *trySymbolLookUpInPragma(NestedNameSpecifier *NestedName,
-                             const clang::Sema::SymbolLabel &Label);
+                                     const clang::Sema::SymbolLabel &Label);
 
   /// ActonPragmaExport - called on well-formed '\#pragma export'.
   void ActOnPragmaExport(NestedNameSpecifier *NestedId,
diff --git a/clang/lib/Sema/SemaAttr.cpp b/clang/lib/Sema/SemaAttr.cpp
index 06482ac71ebd7..9a60217875e0a 100644
--- a/clang/lib/Sema/SemaAttr.cpp
+++ b/clang/lib/Sema/SemaAttr.cpp
@@ -1366,7 +1366,7 @@ static bool typeListMatches(ASTContext &Context, FunctionDecl *FD,
 }
 
 FunctionDecl *Sema::tryFunctionLookUpInPragma(NestedNameSpecifier *NestedName,
-                                      SourceLocation NameLoc) {
+                                              SourceLocation NameLoc) {
   assert(!NestedName->getPrefix() ||
          NestedName->getPrefix()->getKind() == NestedNameSpecifier::Identifier);
   IdentifierInfo *Prefix =
@@ -1396,8 +1396,9 @@ FunctionDecl *Sema::tryFunctionLookUpInPragma(NestedNameSpecifier *NestedName,
   return nullptr;
 }
 
-NamedDecl *Sema::trySymbolLookUpInPragma(NestedNameSpecifier *NestedName,
-                                 const clang::Sema::SymbolLabel &Label) {
+NamedDecl *
+Sema::trySymbolLookUpInPragma(NestedNameSpecifier *NestedName,
+                              const clang::Sema::SymbolLabel &Label) {
 
   assert(!NestedName->getPrefix() ||
          NestedName->getPrefix()->getKind() == NestedNameSpecifier::Identifier);
@@ -1428,7 +1429,8 @@ NamedDecl *Sema::trySymbolLookUpInPragma(NestedNameSpecifier *NestedName,
 
         // 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() && typeListMatches(Context, FD, Label)) ||
+        if ((Label.TypeList.has_value() &&
+             typeListMatches(Context, FD, Label)) ||
             (!Label.TypeList.has_value() && LRes.isSingleResult()))
           return FD;
       }

>From 9672490581b4ce9ebd6c33c871d05c834b92d174 Mon Sep 17 00:00:00 2001
From: Sean Perry <perry at ca.ibm.com>
Date: Fri, 4 Apr 2025 15:15:26 -0400
Subject: [PATCH 15/15] add release note

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

diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index c521b56a98606..b9b2b657736d9 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -502,6 +502,11 @@ WebAssembly Support
 AVR Support
 ^^^^^^^^^^^
 
+SystemZ Support
+^^^^^^^^^^^^^^^
+
+- Add support for `#pragma export` and `_Export` keyword for z/OS
+
 DWARF Support in Clang
 ----------------------
 



More information about the cfe-commits mailing list