[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 11:01:23 PDT 2024
https://github.com/perry-ca created https://github.com/llvm/llvm-project/pull/111035
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.
>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/4] 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/4] 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/4] 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/4] 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();
More information about the cfe-commits
mailing list