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

Sean Perry via cfe-commits cfe-commits at lists.llvm.org
Thu Oct 3 12:13:39 PDT 2024


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 1/5] 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 447f2592d23595..b15d53e700c679 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 fbcbf0ed416416..884c4147cf1285 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 53d88482698f00..bf56fa6ad7162f 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 64e6d0407b0ce3..09842ed02efd4b 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 c5c3838407cf48..afef3b84a6985f 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 eb8a851da7e04e..eee7009b8615a7 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 06243f2624876f..f41748af5c8303 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 d616c3834c429d..ddb36138aae838 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 a023d29cbd1d73..48ae73b4d25dfe 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 c9f9b688d0d8a2..830f605fb6ad78 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 25c1c496a4f27f..599ec5bab83bce 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 c58bb88035ca8a..2e9a0617491f60 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 d4e0ab0339a8b0..7658d97af01840 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 074e0556ecd2ad..a1e7ddbe389080 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 a04eed9873c0d4..67e8405c639511 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 6f0f5a0311bc18..5be80011d92bb2 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 cc6f18b5b319f9..cc788dc2bf826d 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 04c2f1d380bc48..e701d7378d50ee 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 12d2d3f6060c63..1fa0173902e38a 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 4be7dfbc293927..22bf20ac9b442f 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 cf2a5a622a3a4d..ea0367f7790f0a 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 0e536f71a2f70d..9182647a4f3d6b 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 00000000000000..ee877aa0fb491b
--- /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 00000000000000..5f78bc81482488
--- /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 00000000000000..999a270b82aa94
--- /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 00000000000000..e394cf668549fe
--- /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 00000000000000..6f0ee601b03f04
--- /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 00000000000000..70495a41232880
--- /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 2/5] 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 f41748af5c8303..d03a6b11d26ab8 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 e4c8663c134fda..4ccc79b928638d 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 f0b4593e0cc22e..750bf38180b0d2 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 415df3ec493a51..94d80b551df80c 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 5f78bc81482488..fbcaffc080db38 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 3/5] 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 4234e713f9b350..4ccf3f76bf0ce2 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 750bf38180b0d2..f0b4593e0cc22e 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 4/5] 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 cc788dc2bf826d..af892115f1f8dd 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 5/5] 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 d03a6b11d26ab8..cf38b378352ee2 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 7658d97af01840..e0c7ae03f90f19 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 af892115f1f8dd..8203e15857ebd5 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 6964a70ef5613c..1e5c17b88adc92 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 15c1a36d9c061e..6ad84b1247306a 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));
     }
   }
 



More information about the cfe-commits mailing list