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

via cfe-commits cfe-commits at lists.llvm.org
Thu Oct 3 11:01:55 PDT 2024


llvmbot wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-clang-codegen

Author: Sean Perry (perry-ca)

<details>
<summary>Changes</summary>

The z/OS operating system uses the linker to control what symbols are exported from shared libraries.  The compilation step sets a  bit on each symbol that should be exported from a shared library and then the linker marks these as exported from the shared library.  The linker also produces what is called a side deck that is a list of all the exported symbols.  This is then used as an import file during the link process for programs (or other shared libraries) that link against a shared library.  The default for compilation is to only export symbols that the user wants to export.

Control over what is exported is done by:
- making the default for visibility to be hidden
- using the _Export keyword or #pragma export to indicate what symbols should be exported.  These are features the existing compilers on z/OS use.
- using attribute((visibility,"default")) to mark the symbol as exported.

Note: `Parser::HandlePragmaExport()` has an extra layer of call to parse the pragma.  We have some more pragmas that have very similar syntax.  I'll be upstreaming those next.  It is easier to leave that layer in than to remove it for this PR and then add it back.

---

Patch is 44.65 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/111035.diff


28 Files Affected:

- (modified) clang/include/clang/AST/ASTConsumer.h (+3) 
- (modified) clang/include/clang/Basic/Attr.td (+6) 
- (modified) clang/include/clang/Basic/AttrDocs.td (+21) 
- (modified) clang/include/clang/Basic/DiagnosticSemaKinds.td (+7) 
- (modified) clang/include/clang/Basic/TokenKinds.def (+5) 
- (modified) clang/include/clang/Parse/Parser.h (+13) 
- (modified) clang/include/clang/Sema/DeclSpec.h (+33-3) 
- (modified) clang/include/clang/Sema/Sema.h (+30) 
- (modified) clang/lib/CodeGen/BackendConsumer.h (+1) 
- (modified) clang/lib/CodeGen/CodeGenAction.cpp (+4) 
- (modified) clang/lib/CodeGen/CodeGenModule.cpp (+15) 
- (modified) clang/lib/CodeGen/CodeGenModule.h (+2) 
- (modified) clang/lib/CodeGen/ModuleBuilder.cpp (+5-1) 
- (modified) clang/lib/Driver/ToolChains/ZOS.cpp (+8-4) 
- (modified) clang/lib/Parse/ParseDecl.cpp (+18) 
- (modified) clang/lib/Parse/ParseDeclCXX.cpp (+6) 
- (modified) clang/lib/Parse/ParsePragma.cpp (+213) 
- (modified) clang/lib/Parse/Parser.cpp (+3) 
- (modified) clang/lib/Sema/DeclSpec.cpp (+6) 
- (modified) clang/lib/Sema/Sema.cpp (+21) 
- (modified) clang/lib/Sema/SemaAttr.cpp (+162) 
- (modified) clang/lib/Sema/SemaDecl.cpp (+25) 
- (added) clang/test/CodeGen/attr-export-failing.cpp (+4) 
- (added) clang/test/CodeGen/attr-export.cpp (+51) 
- (added) clang/test/CodeGen/pragma-export.c (+39) 
- (added) clang/test/CodeGen/pragma-export.cpp (+82) 
- (added) clang/test/CodeGen/zos-pragmas.c (+11) 
- (added) clang/test/CodeGen/zos-pragmas.cpp (+11) 


``````````diff
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 b1512e22ee2dd4..88bd8e605adf6c 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 dc84110ef78211..aaf547de1a48bd 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..d03a6b11d26ab8 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,10 @@ class Declarator {
   LLVM_PREFERRED_TYPE(bool)
   unsigned InlineStorageUsed : 1;
 
+  /// Indicates whether this is set as _Export
+  LLVM_PREFERRED_TYPE(bool)
+  unsigned ExportSpecified : 1;
+
   /// Indicates whether this declarator has an initializer.
   LLVM_PREFERRED_TYPE(bool)
   unsigned HasInitializer : 1;
@@ -2001,6 +2014,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;
@@ -2030,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) {
@@ -2109,6 +2126,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();
@@ -2125,6 +2154,7 @@ class Declarator {
     HasInitializer = false;
     ObjCIvar = false;
     ObjCWeakProperty = false;
+    ExportSpecified = false;
     CommaLoc = SourceLocation();
     EllipsisLoc = SourceLocation();
     PackIndexingExpr = nullptr;
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index bede971ce0191b..44bc9bcc22d4b0 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -1906,6 +1906,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 122a05be1c039a..680e029019b8e6 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..af892115f1f8dd 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());
@@ -698,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....
[truncated]

``````````

</details>


https://github.com/llvm/llvm-project/pull/111035


More information about the cfe-commits mailing list