Author: Sean Perry
Date: 2026-01-23T14:46:31-05:00
New Revision: 7638c1df0e50b42087260a95dc9ee890ab6ca594

URL: 
https://github.com/llvm/llvm-project/commit/7638c1df0e50b42087260a95dc9ee890ab6ca594
DIFF: 
https://github.com/llvm/llvm-project/commit/7638c1df0e50b42087260a95dc9ee890ab6ca594.diff

LOG: [SystemZ][z/OS] Implement #pragma export (#141671)

Implement the export pragma that is used in the z/OS XL C/C++ compiler
to indicate that an external symbol is to be exported from the shared
library. The syntax for the pragma is:
```
'#pragma' 'export' '(' name ')'
```
For C++ if `name` is a function it needs to be declared `extern "C"`.

See the following for the XL documentation:
- https://www.ibm.com/docs/en/zos/3.1.0?topic=descriptions-pragma-export

This code was originally in PR
https://github.com/llvm/llvm-project/pull/111035. I have split it out
into separate PRs so the code for #pragma export is in one PR and the
code for _Export keyword is in another. See that original PR for earlier
comments.

Added: 
    clang/test/CodeGen/pragma-export.c
    clang/test/CodeGen/pragma-export.cpp
    clang/test/CodeGen/zos-pragmas.c
    clang/test/CodeGen/zos-pragmas.cpp
    clang/test/Parser/pragma-export.c
    clang/test/Parser/pragma-export.cpp
    clang/test/Sema/pragma-export-failing.c
    clang/test/Sema/pragma-export-failing.cpp

Modified: 
    clang/docs/LanguageExtensions.rst
    clang/docs/ReleaseNotes.rst
    clang/include/clang/Basic/DiagnosticParseKinds.td
    clang/include/clang/Basic/DiagnosticSemaKinds.td
    clang/include/clang/Basic/TokenKinds.def
    clang/include/clang/Parse/Parser.h
    clang/include/clang/Sema/Sema.h
    clang/lib/Driver/ToolChains/ZOS.cpp
    clang/lib/Parse/ParsePragma.cpp
    clang/lib/Parse/ParseStmt.cpp
    clang/lib/Parse/Parser.cpp
    clang/lib/Sema/Sema.cpp
    clang/lib/Sema/SemaAttr.cpp
    clang/lib/Sema/SemaDecl.cpp
    clang/lib/Sema/SemaDeclAttr.cpp

Removed: 
    


################################################################################
diff  --git a/clang/docs/LanguageExtensions.rst 
b/clang/docs/LanguageExtensions.rst
index 2f9be164ad0bb..dc0f838e874a0 100644
--- a/clang/docs/LanguageExtensions.rst
+++ b/clang/docs/LanguageExtensions.rst
@@ -1193,6 +1193,50 @@ specifier for ``_Float16``, and (unlike ``float``) it 
will not be implicitly pro
 ``double`` when passed to ``printf``, so the programmer must explicitly cast 
it to
 ``double`` before using it with an ``%f`` or similar specifier.
 
+Pragmas
+=======
+
+#pragma export
+--------------
+
+Clang supports the export pragma used to indicate an
+external symbol is to be exported from the shared library being built.  The
+syntax for the pragma is:
+
+.. code-block:: c++
+
+  #pragma export (name)
+
+where ``name`` is the name of the external function or variable to be
+exported.  The symbol needs to have external linkage.  The pragma may appear
+before or after the declaration of ``name``, but must precede the
+definition.  The pragma must also appear at file scope.  If ``name`` is not
+defined, the pragma will have no effect.  The pragma needs to be specified
+in the same translation unit as ``name`` is defined.
+
+The pragma has the same effect as adding 
``__attribute__((visibility("default")))``
+to the declaration of ``name``.
+
+In C++, the function being exported must be declared as ``extern "C"``.  If the
+function has overloads, the pragma only applies to the overload with ``extern 
"C"``
+linkage.  For example:
+
+.. code-block:: c++
+
+  #pragma export(func)
+  int func(double) { return 0; }
+  extern "C" int func(int) { return 4;}
+
+In the code above the pragma will export ``func(int)`` but not 
``func(double)``.
+
+If none of the overloads are declared with ``extern "C"`` a warning will be
+generated saying the pragma didn't resolve to a declaration.  For example:
+
+.. code-block:: c++
+
+  #pragma export(func)
+  int func(double) { return 0; } // warning: failed to resolve '#pragma 
export' to a declaration
+
 Messages on ``deprecated`` and ``unavailable`` Attributes
 =========================================================
 

diff  --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index a734804865c57..60a115a434364 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -250,6 +250,13 @@ WebAssembly Support
 AVR Support
 ^^^^^^^^^^^
 
+SystemZ Support
+^^^^^^^^^^^^^^^
+
+- Add support for `#pragma export` for z/OS.  This is a pragma used to export 
functions and variables
+  with external linkage from shared libraries.  It provides compatibility with 
the IBM XL C/C++
+  compiler.
+
 DWARF Support in Clang
 ----------------------
 

diff  --git a/clang/include/clang/Basic/DiagnosticParseKinds.td 
b/clang/include/clang/Basic/DiagnosticParseKinds.td
index 457d3644de35a..8d4c816bae0a5 100644
--- a/clang/include/clang/Basic/DiagnosticParseKinds.td
+++ b/clang/include/clang/Basic/DiagnosticParseKinds.td
@@ -1336,6 +1336,8 @@ def warn_pragma_init_seg_unsupported_target : Warning<
 def err_pragma_file_or_compound_scope : Error<
   "'#pragma %0' can only appear at file scope or at the start of a "
   "compound statement">;
+def err_pragma_file_scope : Error<
+  "'#pragma %0' can only appear at file scope">;
 // - #pragma stdc unknown
 def ext_stdc_pragma_ignored : ExtWarn<"unknown pragma in STDC namespace">,
    InGroup<UnknownPragmas>;

diff  --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td 
b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index a2be7ab3791b9..33461284e11dd 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -1222,6 +1222,16 @@ def err_pragma_pop_visibility_mismatch : Error<
   "#pragma visibility pop with no matching #pragma visibility push">;
 def note_surrounding_namespace_starts_here : Note<
   "surrounding namespace with visibility attribute starts here">;
+def warn_failed_to_resolve_pragma : Warning<
+  "failed to resolve '#pragma %0' to a declaration">,
+  InGroup<IgnoredPragmas>;
+def warn_pragma_not_applied : Warning<
+  "#pragma %0 is applicable to symbols with external linkage only; "
+  "not applied to %1">,
+  InGroup<IgnoredPragmas>;
+def warn_pragma_not_applied_to_defined_symbol : Warning<
+  "#pragma %0 can only applied before a symbol is defined">,
+  InGroup<IgnoredPragmas>;
 def err_pragma_loop_invalid_argument_type : Error<
   "invalid argument of type %0; expected an integer type">;
 def err_pragma_loop_compatibility : Error<

diff  --git a/clang/include/clang/Basic/TokenKinds.def 
b/clang/include/clang/Basic/TokenKinds.def
index a3d286fdb81a7..4ee4aa8b31f65 100644
--- a/clang/include/clang/Basic/TokenKinds.def
+++ b/clang/include/clang/Basic/TokenKinds.def
@@ -1027,6 +1027,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 cd7dc14701914..2b1dcabc9a194 100644
--- a/clang/include/clang/Parse/Parser.h
+++ b/clang/include/clang/Parse/Parser.h
@@ -7041,6 +7041,7 @@ class Parser : public CodeCompletionHandler {
   std::unique_ptr<PragmaHandler> AttributePragmaHandler;
   std::unique_ptr<PragmaHandler> MaxTokensHerePragmaHandler;
   std::unique_ptr<PragmaHandler> MaxTokensTotalPragmaHandler;
+  std::unique_ptr<PragmaHandler> ExportHandler;
   std::unique_ptr<PragmaHandler> RISCVPragmaHandler;
 
   /// Initialize all pragma handlers.
@@ -7162,6 +7163,12 @@ class Parser : public CodeCompletionHandler {
 
   void HandlePragmaAttribute();
 
+  void zOSHandlePragmaHelper(tok::TokenKind);
+
+  /// Handle the annotation token produced for
+  /// #pragma export ...
+  void HandlePragmaExport();
+
   ///@}
 
   //

diff  --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 4d2eda8c32317..48803fae9d837 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -2314,6 +2314,23 @@ class Sema final : public SemaBase {
   ActOnPragmaMSFunction(SourceLocation Loc,
                         const llvm::SmallVectorImpl<StringRef> &NoBuiltins);
 
+  NamedDecl *lookupExternCFunctionOrVariable(IdentifierInfo *IdentId,
+                                             SourceLocation NameLoc,
+                                             Scope *curScope);
+
+  /// Information from a C++ #pragma export, for a symbol that we
+  /// haven't seen the declaration for yet.
+  struct PendingPragmaInfo {
+    SourceLocation NameLoc;
+    bool Used;
+  };
+
+  llvm::DenseMap<IdentifierInfo *, PendingPragmaInfo> PendingExportedNames;
+
+  /// ActonPragmaExport - called on well-formed '\#pragma export'.
+  void ActOnPragmaExport(IdentifierInfo *IdentId, SourceLocation ExportNameLoc,
+                         Scope *curScope);
+
   /// 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.
@@ -3843,6 +3860,8 @@ class Sema final : public SemaBase {
   void warnOnReservedIdentifier(const NamedDecl *D);
   void warnOnCTypeHiddenInCPlusPlus(const NamedDecl *D);
 
+  void ProcessPragmaExport(DeclaratorDecl *newDecl);
+
   Decl *ActOnDeclarator(Scope *S, Declarator &D);
 
   NamedDecl *HandleDeclarator(Scope *S, Declarator &D,
@@ -4933,6 +4952,8 @@ class Sema final : public SemaBase {
                           TypeVisibilityAttr::VisibilityType Vis);
   VisibilityAttr *mergeVisibilityAttr(Decl *D, const AttributeCommonInfo &CI,
                                       VisibilityAttr::VisibilityType Vis);
+  void mergeVisibilityType(Decl *D, SourceLocation Loc,
+                           VisibilityAttr::VisibilityType Type);
   SectionAttr *mergeSectionAttr(Decl *D, const AttributeCommonInfo &CI,
                                 StringRef Name);
 

diff  --git a/clang/lib/Driver/ToolChains/ZOS.cpp 
b/clang/lib/Driver/ToolChains/ZOS.cpp
index eac8f623f9a50..c40f71c170538 100644
--- a/clang/lib/Driver/ToolChains/ZOS.cpp
+++ b/clang/lib/Driver/ToolChains/ZOS.cpp
@@ -36,6 +36,10 @@ void ZOS::addClangTargetOptions(const ArgList &DriverArgs,
                                 options::OPT_fno_aligned_allocation))
     CC1Args.push_back("-faligned-alloc-unavailable");
 
+  if (!DriverArgs.hasArg(options::OPT_fvisibility_EQ,
+                         options::OPT_fvisibility_ms_compat))
+    CC1Args.push_back("-fvisibility=hidden");
+
   if (DriverArgs.hasFlag(options::OPT_fxl_pragma_pack,
                          options::OPT_fno_xl_pragma_pack, true))
     CC1Args.push_back("-fxl-pragma-pack");

diff  --git a/clang/lib/Parse/ParsePragma.cpp b/clang/lib/Parse/ParsePragma.cpp
index 7e8b339f5be49..54b96fd2102b5 100644
--- a/clang/lib/Parse/ParsePragma.cpp
+++ b/clang/lib/Parse/ParsePragma.cpp
@@ -25,6 +25,7 @@
 #include "clang/Sema/SemaCodeCompletion.h"
 #include "clang/Sema/SemaRISCV.h"
 #include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/ScopeExit.h"
 #include "llvm/ADT/StringSwitch.h"
 #include <optional>
 using namespace clang;
@@ -395,6 +396,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) {}
@@ -558,6 +565,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());
@@ -692,6 +704,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();
@@ -1386,6 +1403,73 @@ bool Parser::HandlePragmaMSAllocText(StringRef 
PragmaName,
   return true;
 }
 
+void Parser::zOSHandlePragmaHelper(tok::TokenKind PragmaKind) {
+  assert(Tok.is(PragmaKind));
+
+  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();
+
+  llvm::scope_exit OnReturn([this]() {
+    while (Tok.isNot(tok::eof))
+      PP.Lex(Tok);
+    PP.Lex(Tok);
+  });
+
+  do {
+    PP.Lex(Tok);
+    if (Tok.isNot(tok::l_paren)) {
+      PP.Diag(Tok.getLocation(), diag::warn_pragma_expected_lparen)
+          << PragmaName;
+      return;
+    }
+
+    PP.Lex(Tok);
+    if (Tok.isNot(tok::identifier)) {
+      PP.Diag(Tok.getLocation(), diag::warn_pragma_expected_identifier)
+          << PragmaName;
+      return;
+    }
+
+    IdentifierInfo *IdentName = Tok.getIdentifierInfo();
+    SourceLocation IdentNameLoc = Tok.getLocation();
+    PP.Lex(Tok);
+
+    if (Tok.isNot(tok::r_paren)) {
+      PP.Diag(Tok.getLocation(), diag::warn_pragma_expected_rparen)
+          << PragmaName;
+      return;
+    }
+
+    PP.Lex(Tok);
+    Actions.ActOnPragmaExport(IdentName, IdentNameLoc, getCurScope());
+
+    // Because export is also a C++ keyword, we also check for that.
+    if (Tok.is(tok::identifier) || Tok.is(tok::kw_export)) {
+      PragmaName = Tok.getIdentifierInfo()->getName();
+      if (PragmaName != "export")
+        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;
+    }
+  } while (Tok.isNot(tok::eof));
+  return;
+}
+
+void Parser::HandlePragmaExport() {
+  assert(Tok.is(tok::annot_pragma_export));
+
+  zOSHandlePragmaHelper(tok::annot_pragma_export);
+}
+
 static std::string PragmaLoopHintString(Token PragmaName, Token Option) {
   StringRef Str = PragmaName.getIdentifierInfo()->getName();
   std::string ClangLoopStr("clang loop ");
@@ -4133,6 +4217,44 @@ void 
PragmaMaxTokensTotalHandler::HandlePragma(Preprocessor &PP,
   PP.overrideMaxTokens(MaxTokens, Loc);
 }
 
+static void zOSPragmaHandlerHelper(Preprocessor &PP, Token &Tok,
+                                   tok::TokenKind TokKind) {
+  Token AnnotTok;
+  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.
+  Token EoF;
+  EoF.startToken();
+  EoF.setKind(tok::eof);
+  EoF.setLocation(Tok.getLocation());
+  TokenVector.push_back(EoF);
+  // We must allocate this array with new because EnterTokenStream is going to
+  // delete it later.
+  markAsReinjectedForRelexing(TokenVector);
+  auto TokenArray = std::make_unique<Token[]>(TokenVector.size());
+  std::copy(TokenVector.begin(), TokenVector.end(), TokenArray.get());
+  auto Value = new (PP.getPreprocessorAllocator())
+      std::pair<std::unique_ptr<Token[]>, size_t>(std::move(TokenArray),
+                                                  TokenVector.size());
+  AnnotTok.setAnnotationValue(Value);
+  PP.EnterToken(AnnotTok, /*IsReinject*/ false);
+}
+
+/// Handle #pragma export.
+void PragmaExportHandler::HandlePragma(Preprocessor &PP,
+                                       PragmaIntroducer Introducer,
+                                       Token &FirstToken) {
+  zOSPragmaHandlerHelper(PP, FirstToken, tok::annot_pragma_export);
+}
+
 // Handle '#pragma clang riscv intrinsic vector'.
 //        '#pragma clang riscv intrinsic sifive_vector'.
 //        '#pragma clang riscv intrinsic andes_vector'.

diff  --git a/clang/lib/Parse/ParseStmt.cpp b/clang/lib/Parse/ParseStmt.cpp
index 260f8126360d5..771db0a00091b 100644
--- a/clang/lib/Parse/ParseStmt.cpp
+++ b/clang/lib/Parse/ParseStmt.cpp
@@ -503,6 +503,12 @@ StmtResult 
Parser::ParseStatementOrDeclarationAfterAttributes(
     ProhibitAttributes(GNUAttrs);
     HandlePragmaAttribute();
     return StmtEmpty();
+  case tok::annot_pragma_export:
+    ProhibitAttributes(CXX11Attrs);
+    ProhibitAttributes(GNUAttrs);
+    Diag(Tok, diag::err_pragma_file_scope) << "export";
+    ConsumeAnnotationToken();
+    return StmtEmpty();
   }
 
   // If we reached this code, the statement must end in a semicolon.
@@ -1032,6 +1038,10 @@ void Parser::ParseCompoundStatementLeadingPragmas() {
     case tok::annot_pragma_dump:
       HandlePragmaDump();
       break;
+    case tok::annot_pragma_export:
+      Diag(Tok, diag::err_pragma_file_scope) << "export";
+      ConsumeAnnotationToken();
+      break;
     default:
       checkForPragmas = false;
       break;

diff  --git a/clang/lib/Parse/Parser.cpp b/clang/lib/Parse/Parser.cpp
index af3ba7853820f..be9e0a39a3781 100644
--- a/clang/lib/Parse/Parser.cpp
+++ b/clang/lib/Parse/Parser.cpp
@@ -803,6 +803,9 @@ Parser::ParseExternalDeclaration(ParsedAttributes &Attrs,
   case tok::annot_pragma_attribute:
     HandlePragmaAttribute();
     return nullptr;
+  case tok::annot_pragma_export:
+    HandlePragmaExport();
+    return nullptr;
   case tok::semi:
     // Either a C++11 empty-declaration or attribute-declaration.
     SingleDecl =

diff  --git a/clang/lib/Sema/Sema.cpp b/clang/lib/Sema/Sema.cpp
index 6eea8f6e9d97e..d53527af38653 100644
--- a/clang/lib/Sema/Sema.cpp
+++ b/clang/lib/Sema/Sema.cpp
@@ -1495,6 +1495,12 @@ void Sema::ActOnEndOfTranslationUnit() {
     Consumer.CompleteExternalDeclaration(D);
   }
 
+  // Visit all pending #pragma export.
+  for (const PendingPragmaInfo &Exported : PendingExportedNames.values()) {
+    if (!Exported.Used)
+      Diag(Exported.NameLoc, diag::warn_failed_to_resolve_pragma) << "export";
+  }
+
   if (LangOpts.HLSL)
     HLSL().ActOnEndOfTranslationUnit(getASTContext().getTranslationUnitDecl());
   if (LangOpts.OpenACC)

diff  --git a/clang/lib/Sema/SemaAttr.cpp b/clang/lib/Sema/SemaAttr.cpp
index 7729c113e422e..738daf2f69290 100644
--- a/clang/lib/Sema/SemaAttr.cpp
+++ b/clang/lib/Sema/SemaAttr.cpp
@@ -1327,6 +1327,61 @@ void 
Sema::AddImplicitMSFunctionNoBuiltinAttr(FunctionDecl *FD) {
     FD->addAttr(NoBuiltinAttr::CreateImplicit(Context, V.data(), V.size()));
 }
 
+NamedDecl *Sema::lookupExternCFunctionOrVariable(IdentifierInfo *IdentId,
+                                                 SourceLocation NameLoc,
+                                                 Scope *curScope) {
+  LookupResult Result(*this, IdentId, NameLoc, LookupOrdinaryName);
+  LookupName(Result, curScope);
+  if (!getLangOpts().CPlusPlus)
+    return Result.getAsSingle<NamedDecl>();
+  for (NamedDecl *D : Result) {
+    if (auto *FD = dyn_cast<FunctionDecl>(D))
+      if (FD->isExternC())
+        return D;
+    if (isa<VarDecl>(D))
+      return D;
+  }
+  return nullptr;
+}
+
+void Sema::ActOnPragmaExport(IdentifierInfo *IdentId, SourceLocation NameLoc,
+                             Scope *curScope) {
+  PendingPragmaInfo Info;
+  Info.NameLoc = NameLoc;
+  Info.Used = false;
+
+  NamedDecl *PrevDecl =
+      lookupExternCFunctionOrVariable(IdentId, NameLoc, curScope);
+  if (!PrevDecl) {
+    PendingExportedNames[IdentId] = Info;
+    return;
+  }
+
+  if (auto *FD = dyn_cast<FunctionDecl>(PrevDecl->getCanonicalDecl())) {
+    if (!FD->hasExternalFormalLinkage()) {
+      Diag(NameLoc, diag::warn_pragma_not_applied) << "export" << PrevDecl;
+      return;
+    }
+    if (FD->hasBody()) {
+      Diag(NameLoc, diag::warn_pragma_not_applied_to_defined_symbol)
+          << "export";
+      return;
+    }
+  } else if (auto *VD = dyn_cast<VarDecl>(PrevDecl->getCanonicalDecl())) {
+    if (!VD->hasExternalFormalLinkage()) {
+      Diag(NameLoc, diag::warn_pragma_not_applied) << "export" << PrevDecl;
+      return;
+    }
+    if (VD->hasDefinition() == VarDecl::Definition) {
+      Diag(NameLoc, diag::warn_pragma_not_applied_to_defined_symbol)
+          << "export";
+      return;
+    }
+  }
+  mergeVisibilityType(PrevDecl->getCanonicalDecl(), NameLoc,
+                      VisibilityAttr::Default);
+}
+
 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 ae779d6830d9b..066acc3424c8f 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -7650,6 +7650,32 @@ static void emitReadOnlyPlacementAttrWarning(Sema &S, 
const VarDecl *VD) {
   }
 }
 
+void Sema::ProcessPragmaExport(DeclaratorDecl *NewD) {
+  assert((isa<FunctionDecl>(NewD) || isa<VarDecl>(NewD)) &&
+         "NewD is not a function or variable");
+
+  if (PendingExportedNames.empty())
+    return;
+  if (FunctionDecl *FD = dyn_cast<FunctionDecl>(NewD)) {
+    if (getLangOpts().CPlusPlus && !FD->isExternC())
+      return;
+  }
+  IdentifierInfo *IdentName = NewD->getIdentifier();
+  if (IdentName == nullptr)
+    return;
+  auto PendingName = PendingExportedNames.find(IdentName);
+  if (PendingName != PendingExportedNames.end()) {
+    auto &Label = PendingName->second;
+    if (!Label.Used) {
+      Label.Used = true;
+      if (NewD->hasExternalFormalLinkage())
+        mergeVisibilityType(NewD, Label.NameLoc, VisibilityAttr::Default);
+      else
+        Diag(Label.NameLoc, diag::warn_pragma_not_applied) << "export" << NewD;
+    }
+  }
+}
+
 // Checks if VD is declared at global scope or with C language linkage.
 static bool isMainVar(DeclarationName Name, VarDecl *VD) {
   return Name.getAsIdentifierInfo() &&
@@ -8364,6 +8390,7 @@ NamedDecl *Sema::ActOnVariableDeclarator(
     CheckShadow(NewVD, ShadowedDecl, Previous);
 
   ProcessPragmaWeak(S, NewVD);
+  ProcessPragmaExport(NewVD);
 
   // If this is the first declaration of an extern C variable, update
   // the map of such variables.
@@ -11010,6 +11037,7 @@ Sema::ActOnFunctionDeclarator(Scope *S, Declarator &D, 
DeclContext *DC,
   }
 
   ProcessPragmaWeak(S, NewFD);
+  ProcessPragmaExport(NewFD);
   checkAttributesAfterMerging(*this, *NewFD);
 
   AddKnownFunctionAttributes(NewFD);

diff  --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp
index dc954c3925596..30891777e2631 100644
--- a/clang/lib/Sema/SemaDeclAttr.cpp
+++ b/clang/lib/Sema/SemaDeclAttr.cpp
@@ -2928,6 +2928,15 @@ static void handleExternalSourceSymbolAttr(Sema &S, Decl 
*D,
       S.Context, AL, Language, DefinedIn, IsGeneratedDeclaration, USR));
 }
 
+void Sema::mergeVisibilityType(Decl *D, SourceLocation Loc,
+                               VisibilityAttr::VisibilityType Value) {
+  if (VisibilityAttr *Attr = D->getAttr<VisibilityAttr>()) {
+    if (Attr->getVisibility() != Value)
+      Diag(Loc, diag::err_mismatched_visibility);
+  } else
+    D->addAttr(VisibilityAttr::CreateImplicit(Context, Value));
+}
+
 template <class T>
 static T *mergeVisibilityAttr(Sema &S, Decl *D, const AttributeCommonInfo &CI,
                               typename T::VisibilityType value) {

diff  --git a/clang/test/CodeGen/pragma-export.c 
b/clang/test/CodeGen/pragma-export.c
new file mode 100644
index 0000000000000..d6d2d6ad878f3
--- /dev/null
+++ b/clang/test/CodeGen/pragma-export.c
@@ -0,0 +1,42 @@
+// REQUIRES: systemz-registered-target
+// RUN: %clang_cc1 %s -emit-llvm -fzos-extensions -triple s390x-none-zos 
-fvisibility=hidden -o - | FileCheck %s
+
+// Testing pragma export after decl.
+void f0(void) {}
+int v0;
+int vd = 2;
+#pragma export(f0)
+#pragma export(v0)
+#pragma export(vd)
+
+// Testing pragma export before decl.
+#pragma export(f1)
+#pragma export(v1)
+void f1(void) {}
+int v1;
+
+void f2(void);
+
+void t0(void) { f2();}
+
+#pragma export(f2)
+void f2(void) {}
+
+int func() {
+  int local;
+  int l2;
+  return local+l2;
+}
+
+int local = 2;
+int l2 =4;
+
+// CHECK: @vd = hidden global i32
+// CHECK: @local = hidden global i32
+// CHECK: @l2 = hidden global i32
+// CHECK: @v0 = global i32
+// CHECK: @v1 = global i32
+// CHECK: define hidden void @f0()
+// CHECK: define void @f1()
+// CHECK: define hidden void @t0()
+// CHECK: define void @f2()

diff  --git a/clang/test/CodeGen/pragma-export.cpp 
b/clang/test/CodeGen/pragma-export.cpp
new file mode 100644
index 0000000000000..531afbd659234
--- /dev/null
+++ b/clang/test/CodeGen/pragma-export.cpp
@@ -0,0 +1,69 @@
+// REQUIRES: systemz-registered-target
+// RUN: %clang_cc1 -x c++ %s -emit-llvm -triple s390x-none-zos 
-fzos-extensions -fvisibility=hidden -o - | FileCheck %s
+
+// Testing pragma export after decl.
+extern "C" void f0(void) {}
+int v0;
+#pragma export(f0)
+#pragma export(v0)
+
+// Testing pragma export before decl.
+#pragma export(f1)
+#pragma export(v1)
+extern "C" void f1(void) {}
+int v1;
+
+// Testing overloaded functions.
+#pragma export(f2)
+void f2(double, double) {}
+extern "C" void f2(int) {}
+void f2(int, int) {}
+
+extern "C" void f3(double) {}
+void f3(int, double) {}
+void f3(double, double) {}
+#pragma export(f3)
+
+extern "C" void f2b(void) {}
+
+void t0(void) {
+  f2b();
+}
+
+// Testing pragma export after decl and usage.
+#pragma export(f2b)
+
+// Testing pragma export with namespace.
+extern "C" void f5(void) {}
+extern "C" void f5a(void) {}
+namespace N0 {
+void f5(void) {}
+#pragma export(f5)
+#pragma export(f5a)
+void f5a(void) {}
+} // namespace N0
+
+void f10(int);
+#pragma export(f10)
+extern "C" void f10(double) {}
+void f10(int) {}
+
+// CHECK: @v0 = hidden global i32 0
+// CHECK: @v1 = global i32 0
+// CHECK: define hidden void @f0()
+// CHECK: define void @f1()
+// CHECK: define hidden void @_Z2f2dd(double noundef %0, double noundef %1)
+// CHECK: define void @f2(i32 noundef signext %0)
+// CHECK: define hidden void @_Z2f2ii(i32 noundef signext %0, i32 noundef 
signext %1)
+// CHECK: define hidden void @f3(double noundef %0)
+// CHECK: define hidden void @_Z2f3id(i32 noundef signext %0, double noundef 
%1)
+// CHECK: define hidden void @_Z2f3dd(double noundef %0, double noundef %1)
+// CHECK: define hidden void @f2b()
+// CHECK: define hidden void @_Z2t0v()
+// CHECK: define hidden void @f5()
+// CHECK: define hidden void @f5a()
+// CHECK: define hidden void @_ZN2N02f5Ev()
+// CHECK: define hidden void @_ZN2N03f5aEv()
+// CHECK: define void @f10(double noundef %0)
+// CHECK: define hidden void @_Z3f10i(i32 noundef signext %0)
+

diff  --git a/clang/test/CodeGen/zos-pragmas.c 
b/clang/test/CodeGen/zos-pragmas.c
new file mode 100644
index 0000000000000..e2bd03a33e20a
--- /dev/null
+++ b/clang/test/CodeGen/zos-pragmas.c
@@ -0,0 +1,11 @@
+// REQUIRES: systemz-registered-target
+// RUN: %clang_cc1 -emit-llvm -triple s390x-none-zos -fvisibility=hidden %s -o 
- | FileCheck %s
+
+int a,b,c;
+#pragma export(a) export(b) export(c)
+
+void foo(void);
+
+// CHECK: @a = global i32 0, align 4
+// CHECK: @b = global i32 0, align 4
+// CHECK: @c = global i32 0, align 4

diff  --git a/clang/test/CodeGen/zos-pragmas.cpp 
b/clang/test/CodeGen/zos-pragmas.cpp
new file mode 100644
index 0000000000000..65e428796039e
--- /dev/null
+++ b/clang/test/CodeGen/zos-pragmas.cpp
@@ -0,0 +1,11 @@
+// REQUIRES: systemz-registered-target
+// RUN: %clang_cc1 -x c++ -emit-llvm -triple s390x-none-zos 
-fvisibility=hidden %s -o - | FileCheck %s
+
+#pragma export(a) export(b) export(c)
+int a,b,c;
+
+void foo(void);
+
+// CHECK: @a = global i32 0, align 4
+// CHECK: @b = global i32 0, align 4
+// CHECK: @c = global i32 0, align 4

diff  --git a/clang/test/Parser/pragma-export.c 
b/clang/test/Parser/pragma-export.c
new file mode 100644
index 0000000000000..e78fa21242c77
--- /dev/null
+++ b/clang/test/Parser/pragma-export.c
@@ -0,0 +1,15 @@
+// RUN: %clang_cc1 -triple s390x-ibm-zos -fsyntax-only -verify %s
+
+int x;
+
+#pragma export x  // expected-warning {{missing '(' after '#pragma export' - 
ignoring}}
+#pragma export  // expected-warning {{missing '(' after '#pragma export' - 
ignoring}}
+#pragma export(  // expected-warning {{expected identifier in '#pragma export' 
- ignored}}
+#pragma export(x  // expected-warning {{missing ')' after '#pragma export' - 
ignoring}}
+#pragma export(::x) // expected-warning {{expected identifier in '#pragma 
export' - ignored}}
+#pragma export(x)
+
+void f() {
+}
+
+#pragma export(f()) // expected-warning {{missing ')' after '#pragma export' - 
ignoring}}

diff  --git a/clang/test/Parser/pragma-export.cpp 
b/clang/test/Parser/pragma-export.cpp
new file mode 100644
index 0000000000000..91d2e162bcfec
--- /dev/null
+++ b/clang/test/Parser/pragma-export.cpp
@@ -0,0 +1,16 @@
+// RUN: %clang_cc1 -x c++ -triple s390x-ibm-zos -fsyntax-only -verify %s
+
+extern int i;
+#pragma export( // expected-warning {{expected identifier in '#pragma export' 
- ignored}}
+#pragma export() // expected-warning {{expected identifier in '#pragma export' 
- ignored}}
+#pragma export(i)
+
+struct S {
+  static int i;
+};
+#pragma export(S::i) // expected-warning {{missing ')' after '#pragma export' 
- ignoring}}
+
+void f(int);
+void f(double, double);
+#pragma export(f // expected-warning {{missing ')' after '#pragma export' - 
ignoring}}
+#pragma export(f( // expected-warning {{missing ')' after '#pragma export' - 
ignoring}}

diff  --git a/clang/test/Sema/pragma-export-failing.c 
b/clang/test/Sema/pragma-export-failing.c
new file mode 100644
index 0000000000000..14faf120f3487
--- /dev/null
+++ b/clang/test/Sema/pragma-export-failing.c
@@ -0,0 +1,42 @@
+// REQUIRES: systemz-registered-target
+// RUN: %clang_cc1 -triple s390x-none-zos -fzos-extensions %s -fsyntax-only 
-verify
+
+#pragma export(d0)                         // expected-warning{{failed to 
resolve '#pragma export' to a declaration}}
+#pragma export(f9)                         // expected-warning{{failed to 
resolve '#pragma export' to a declaration}}
+
+#pragma export(sf1) // expected-warning{{#pragma export is applicable to 
symbols with external linkage only; not applied to 'sf1'}}
+#pragma export(s1) // expected-warning{{#pragma export is applicable to 
symbols with external linkage only; not applied to 's1'}}
+static void sf1(void) {}
+static int s1;
+
+static void sf0(void) {}
+int v0;
+static int s0;
+#pragma export(sf0) // expected-warning{{#pragma export is applicable to 
symbols with external linkage only; not applied to 'sf0'}}
+#pragma export(s0) // expected-warning{{#pragma export is applicable to 
symbols with external linkage only; not applied to 's0'}}
+
+#pragma export(f1) // expected-error {{visibility does not match previous 
declaration}}
+int f1() __attribute__((visibility("hidden")));
+int f2() __attribute__((visibility("hidden")));
+#pragma export(f2) // expected-error {{visibility does not match previous 
declaration}}
+
+
+int hoo() __attribute__((visibility("hidden")));
+
+int foo() { return 4; }
+#pragma export(foo)  // expected-warning {{#pragma export can only applied 
before a symbol is defined}}
+
+int var = 4;
+#pragma export(var) // expected-warning {{#pragma export can only applied 
before a symbol is defined}}
+
+int func() {
+#pragma export(local) // expected-error{{'#pragma export' can only appear at 
file scope}}
+  int local;
+  int l2;
+#pragma export(l2) // expected-error{{'#pragma export' can only appear at file 
scope}}
+  return local+l2;
+}
+
+int local = 2;
+int l2 =4;
+

diff  --git a/clang/test/Sema/pragma-export-failing.cpp 
b/clang/test/Sema/pragma-export-failing.cpp
new file mode 100644
index 0000000000000..da0d9ca4531de
--- /dev/null
+++ b/clang/test/Sema/pragma-export-failing.cpp
@@ -0,0 +1,34 @@
+// REQUIRES: systemz-registered-target
+// RUN: %clang_cc1 -x c++ -triple s390x-none-zos -fzos-extensions %s 
-fsyntax-only -verify
+
+#pragma export(f0(int))                    // expected-warning{{missing ')' 
after '#pragma export' - ignoring}}
+#pragma export(f3(double, double, double)) // expected-warning{{missing ')' 
after '#pragma export' - ignoring}}
+
+#pragma export(N::sf1(void)) // expected-warning{{missing ')' after '#pragma 
export' - ignoring}}
+#pragma export(N::s1) // expected-warning{{missing ')' after '#pragma export' 
- ignoring}}
+namespace N {
+static void sf1(void) {}
+static int s1;
+
+static void sf0(void) {}
+int v0;
+static int s0;
+}
+#pragma export(N::sf0(void)) // expected-warning{{missing ')' after '#pragma 
export' - ignoring}}
+#pragma export(N::s0) // expected-warning{{missing ')' after '#pragma export' 
- ignoring}}
+
+void f10(int);
+#pragma export(f10) // expected-warning{{failed to resolve '#pragma export' to 
a declaration}}
+
+#pragma export(f11) // expected-warning{{failed to resolve '#pragma export' to 
a declaration}}
+void f11(int);
+
+template<auto func>
+struct S {
+
+#pragma export(func) // expected-error{{this pragma cannot appear in struct 
declaration}}
+};
+
+extern "C" void funcToExport();
+
+S<funcToExport> s{};


        
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to