[clang] Add visibility features for z/OS (eg. _Export, pragma export) (PR #111035)
Sean Perry via cfe-commits
cfe-commits at lists.llvm.org
Thu Oct 3 12:13:39 PDT 2024
https://github.com/perry-ca updated https://github.com/llvm/llvm-project/pull/111035
>From e8d355c9cd165e0a255bbbfb5b0126cf7b1461a6 Mon Sep 17 00:00:00 2001
From: Sean Perry <perry at ca.ibm.com>
Date: Wed, 2 Oct 2024 12:56:43 -0500
Subject: [PATCH 1/5] initial work for pragma export & _Export keyword
---
clang/include/clang/AST/ASTConsumer.h | 3 +
clang/include/clang/Basic/Attr.td | 6 +
clang/include/clang/Basic/AttrDocs.td | 21 ++
.../clang/Basic/DiagnosticSemaKinds.td | 7 +
clang/include/clang/Basic/TokenKinds.def | 5 +
clang/include/clang/Parse/Parser.h | 13 ++
clang/include/clang/Sema/DeclSpec.h | 31 ++-
clang/include/clang/Sema/Sema.h | 30 +++
clang/lib/CodeGen/BackendConsumer.h | 1 +
clang/lib/CodeGen/CodeGenAction.cpp | 4 +
clang/lib/CodeGen/CodeGenModule.cpp | 15 ++
clang/lib/CodeGen/CodeGenModule.h | 2 +
clang/lib/CodeGen/ModuleBuilder.cpp | 6 +-
clang/lib/Driver/ToolChains/ZOS.cpp | 12 +-
clang/lib/Parse/ParseDecl.cpp | 18 ++
clang/lib/Parse/ParseDeclCXX.cpp | 6 +
clang/lib/Parse/ParsePragma.cpp | 208 ++++++++++++++++++
clang/lib/Parse/Parser.cpp | 3 +
clang/lib/Sema/DeclSpec.cpp | 6 +
clang/lib/Sema/Sema.cpp | 21 ++
clang/lib/Sema/SemaAttr.cpp | 162 ++++++++++++++
clang/lib/Sema/SemaDecl.cpp | 13 ++
clang/test/CodeGen/attr-export-failing.cpp | 4 +
clang/test/CodeGen/attr-export.cpp | 51 +++++
clang/test/CodeGen/pragma-export.c | 39 ++++
clang/test/CodeGen/pragma-export.cpp | 82 +++++++
clang/test/CodeGen/zos-pragmas.c | 11 +
clang/test/CodeGen/zos-pragmas.cpp | 11 +
28 files changed, 784 insertions(+), 7 deletions(-)
create mode 100644 clang/test/CodeGen/attr-export-failing.cpp
create mode 100644 clang/test/CodeGen/attr-export.cpp
create mode 100644 clang/test/CodeGen/pragma-export.c
create mode 100644 clang/test/CodeGen/pragma-export.cpp
create mode 100644 clang/test/CodeGen/zos-pragmas.c
create mode 100644 clang/test/CodeGen/zos-pragmas.cpp
diff --git a/clang/include/clang/AST/ASTConsumer.h b/clang/include/clang/AST/ASTConsumer.h
index 447f2592d23595..b15d53e700c679 100644
--- a/clang/include/clang/AST/ASTConsumer.h
+++ b/clang/include/clang/AST/ASTConsumer.h
@@ -108,6 +108,9 @@ class ASTConsumer {
/// completed.
virtual void CompleteExternalDeclaration(DeclaratorDecl *D) {}
+ /// CompletePragmaExport - complete #pragma export statements.
+ virtual void CompletePragmaExport(Decl *D) {}
+
/// Callback invoked when an MSInheritanceAttr has been attached to a
/// CXXRecordDecl.
virtual void AssignInheritanceModel(CXXRecordDecl *RD) {}
diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td
index fbcbf0ed416416..884c4147cf1285 100644
--- a/clang/include/clang/Basic/Attr.td
+++ b/clang/include/clang/Basic/Attr.td
@@ -4520,6 +4520,12 @@ def ReleaseHandle : InheritableParamAttr {
let Documentation = [ReleaseHandleDocs];
}
+def zOSExport : InheritableAttr {
+ let Spellings = [CustomKeyword<"_Export">];
+ let Subjects = SubjectList<[Function, Var, CXXRecord]>;
+ let Documentation = [zOSExportDocs];
+}
+
def UnsafeBufferUsage : InheritableAttr {
let Spellings = [Clang<"unsafe_buffer_usage">];
let Subjects = SubjectList<[Function, Field]>;
diff --git a/clang/include/clang/Basic/AttrDocs.td b/clang/include/clang/Basic/AttrDocs.td
index 53d88482698f00..bf56fa6ad7162f 100644
--- a/clang/include/clang/Basic/AttrDocs.td
+++ b/clang/include/clang/Basic/AttrDocs.td
@@ -6863,6 +6863,27 @@ attribute requires a string literal argument to identify the handle being releas
}];
}
+def zOSExportDocs : Documentation {
+ let Category = DocCatFunction;
+ let Content = [{
+Use the _Export keyword with a function name or external variable to declare
+that it is to be exported (made available to other modules). You must define
+the object name in the same translation unit in which you use the _Export
+keyword. For example:
+
+.. code-block:: c
+
+ int _Export anthony(float);
+
+This statement exports the function anthony, if you define the function in the
+translation unit. The _Export keyword must immediately precede the object name.
+If you apply the _Export keyword to a class, the compiler automatically exports
+all static data members and member functions of the class. However, if you want
+it to apply to individual class members, then you must apply it to each member
+that can be referenced.
+ }];
+}
+
def UnsafeBufferUsageDocs : Documentation {
let Category = DocCatFunction;
let Content = [{
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 64e6d0407b0ce3..09842ed02efd4b 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -1129,6 +1129,13 @@ def err_pragma_pop_visibility_mismatch : Error<
"#pragma visibility pop with no matching #pragma visibility push">;
def note_surrounding_namespace_starts_here : Note<
"surrounding namespace with visibility attribute starts here">;
+def warn_failed_to_resolve_pragma : Warning<
+ "failed to resolve '#pragma %0' to a declaration">,
+ InGroup<IgnoredPragmas>;
+def warn_pragma_not_applied : Warning<
+ "#pragma %0 is applicable to symbols with external linkage only; "
+ "not applied to %1">,
+ InGroup<IgnoredPragmas>;
def err_pragma_loop_invalid_argument_type : Error<
"invalid argument of type %0; expected an integer type">;
def err_pragma_loop_compatibility : Error<
diff --git a/clang/include/clang/Basic/TokenKinds.def b/clang/include/clang/Basic/TokenKinds.def
index c5c3838407cf48..afef3b84a6985f 100644
--- a/clang/include/clang/Basic/TokenKinds.def
+++ b/clang/include/clang/Basic/TokenKinds.def
@@ -345,6 +345,8 @@ KEYWORD(__func__ , KEYALL)
KEYWORD(__objc_yes , KEYALL)
KEYWORD(__objc_no , KEYALL)
+// z/OS specific keywords
+KEYWORD(_Export , KEYZOS)
// C++ 2.11p1: Keywords.
KEYWORD(asm , KEYCXX|KEYGNU)
@@ -1003,6 +1005,9 @@ PRAGMA_ANNOTATION(pragma_fp)
// Annotation for the attribute pragma directives - #pragma clang attribute ...
PRAGMA_ANNOTATION(pragma_attribute)
+// Annotation for C/C++ #pragma export(ident)
+PRAGMA_ANNOTATION(pragma_export)
+
// Annotation for the riscv pragma directives - #pragma clang riscv intrinsic ...
PRAGMA_ANNOTATION(pragma_riscv)
diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h
index eb8a851da7e04e..eee7009b8615a7 100644
--- a/clang/include/clang/Parse/Parser.h
+++ b/clang/include/clang/Parse/Parser.h
@@ -220,6 +220,7 @@ class Parser : public CodeCompletionHandler {
std::unique_ptr<PragmaHandler> AttributePragmaHandler;
std::unique_ptr<PragmaHandler> MaxTokensHerePragmaHandler;
std::unique_ptr<PragmaHandler> MaxTokensTotalPragmaHandler;
+ std::unique_ptr<PragmaHandler> ExportHandler;
std::unique_ptr<PragmaHandler> RISCVPragmaHandler;
std::unique_ptr<CommentHandler> CommentSemaHandler;
@@ -854,6 +855,18 @@ class Parser : public CodeCompletionHandler {
void HandlePragmaAttribute();
+ /// Helper functions for handling zOS pragmas.
+ NestedNameSpecifier *zOSParseIdentifier(StringRef PragmaName,
+ IdentifierInfo *IdentName);
+ bool zOSParseParameterList(StringRef PragmaName,
+ std::optional<SmallVector<QualType, 4>> &TypeList,
+ Qualifiers &CVQual);
+ bool zOSHandlePragmaHelper(tok::TokenKind);
+
+ /// Handle the annotation token produced for
+ /// #pragma export ...
+ void HandlePragmaExport();
+
/// GetLookAheadToken - This peeks ahead N tokens and returns that token
/// without consuming any tokens. LookAhead(0) returns 'Tok', LookAhead(1)
/// returns the token after Tok, etc.
diff --git a/clang/include/clang/Sema/DeclSpec.h b/clang/include/clang/Sema/DeclSpec.h
index 06243f2624876f..f41748af5c8303 100644
--- a/clang/include/clang/Sema/DeclSpec.h
+++ b/clang/include/clang/Sema/DeclSpec.h
@@ -398,6 +398,8 @@ class DeclSpec {
unsigned FS_virtual_specified : 1;
LLVM_PREFERRED_TYPE(bool)
unsigned FS_noreturn_specified : 1;
+ LLVM_PREFERRED_TYPE(bool)
+ unsigned export_specified : 1;
// friend-specifier
LLVM_PREFERRED_TYPE(bool)
@@ -444,6 +446,7 @@ class DeclSpec {
SourceLocation FS_forceinlineLoc;
SourceLocation FriendLoc, ModulePrivateLoc, ConstexprLoc;
SourceLocation TQ_pipeLoc;
+ SourceLocation exportLoc;
WrittenBuiltinSpecs writtenBS;
void SaveWrittenBuiltinSpecs();
@@ -492,7 +495,8 @@ class DeclSpec {
TypeSpecPipe(false), TypeSpecSat(false), ConstrainedAuto(false),
TypeQualifiers(TQ_unspecified), FS_inline_specified(false),
FS_forceinline_specified(false), FS_virtual_specified(false),
- FS_noreturn_specified(false), FriendSpecifiedFirst(false),
+ FS_noreturn_specified(false), export_specified(false),
+ FriendSpecifiedFirst(false),
ConstexprSpecifier(
static_cast<unsigned>(ConstexprSpecKind::Unspecified)),
Attrs(attrFactory), writtenBS(), ObjCQualifiers(nullptr) {}
@@ -661,7 +665,10 @@ class DeclSpec {
bool isNoreturnSpecified() const { return FS_noreturn_specified; }
SourceLocation getNoreturnSpecLoc() const { return FS_noreturnLoc; }
- void ClearFunctionSpecs() {
+ bool isExportSpecified() const { return export_specified; }
+ SourceLocation getExportSpecLoc() const { return exportLoc; }
+
+ void ClearFunctionSpecs() {
FS_inline_specified = false;
FS_inlineLoc = SourceLocation();
FS_forceinline_specified = false;
@@ -811,6 +818,8 @@ class DeclSpec {
bool setFunctionSpecNoreturn(SourceLocation Loc, const char *&PrevSpec,
unsigned &DiagID);
+ bool setExportSpec(SourceLocation Loc);
+
bool SetFriendSpec(SourceLocation Loc, const char *&PrevSpec,
unsigned &DiagID);
bool setModulePrivateSpec(SourceLocation Loc, const char *&PrevSpec,
@@ -1955,6 +1964,9 @@ class Declarator {
LLVM_PREFERRED_TYPE(bool)
unsigned InlineStorageUsed : 1;
+ /// Indicates whether this is set as _Export
+ unsigned ExportSpecified : 1;
+
/// Indicates whether this declarator has an initializer.
LLVM_PREFERRED_TYPE(bool)
unsigned HasInitializer : 1;
@@ -2001,6 +2013,9 @@ class Declarator {
/// this declarator as a parameter pack.
SourceLocation EllipsisLoc;
+ /// The source location of the _Export keyword on this declarator
+ SourceLocation ExportLoc;
+
Expr *PackIndexingExpr;
friend struct DeclaratorChunk;
@@ -2109,6 +2124,18 @@ class Declarator {
Range.setEnd(SR.getEnd());
}
+ /// Set this declarator as _Export
+ void SetExport(SourceLocation Loc) {
+ ExportSpecified = true;
+ ExportLoc = Loc;
+ }
+
+ /// Whether this declarator is marked as _Export
+ bool IsExport() const { return ExportSpecified; }
+
+ /// Get the location of the _Export keyword
+ SourceLocation getExportLoc() const { return ExportLoc; }
+
/// Reset the contents of this Declarator.
void clear() {
SS.clear();
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index d616c3834c429d..ddb36138aae838 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -1974,6 +1974,36 @@ class Sema final : public SemaBase {
ActOnPragmaMSFunction(SourceLocation Loc,
const llvm::SmallVectorImpl<StringRef> &NoBuiltins);
+ /// A label from a C++ #pragma export, for a symbol that we
+ /// haven't seen the declaration for yet. The TypeList is the argument list
+ /// the function must match if HasTypeList is true.
+ struct SymbolLabel {
+ std::optional<SmallVector<QualType, 4>> TypeList;
+ StringRef MappedName;
+ SourceLocation NameLoc;
+ bool HasTypeList;
+ Qualifiers CVQual;
+ };
+
+ typedef SmallVector<SymbolLabel, 1> PendingSymbolOverloads;
+ typedef llvm::DenseMap<NestedNameSpecifier *, PendingSymbolOverloads>
+ SymbolNames;
+ SymbolNames PendingExportNames;
+
+ FunctionDecl *tryFunctionLookUp(NestedNameSpecifier *NestedName,
+ SourceLocation NameLoc);
+
+ /// trySymbolLookUp try to look up a decl matching the nested specifier
+ /// with optional type list.
+ NamedDecl *trySymbolLookUp(NestedNameSpecifier *NestedName,
+ const clang::Sema::SymbolLabel &Label);
+
+ /// ActonPragmaExport - called on well-formed '\#pragma export'.
+ void ActOnPragmaExport(NestedNameSpecifier *NestedId,
+ SourceLocation ExportNameLoc,
+ std::optional<SmallVector<QualType, 4>> &&TypeList,
+ Qualifiers CVQual);
+
/// Only called on function definitions; if there is a pragma in scope
/// with the effect of a range-based optnone, consider marking the function
/// with attribute optnone.
diff --git a/clang/lib/CodeGen/BackendConsumer.h b/clang/lib/CodeGen/BackendConsumer.h
index a023d29cbd1d73..48ae73b4d25dfe 100644
--- a/clang/lib/CodeGen/BackendConsumer.h
+++ b/clang/lib/CodeGen/BackendConsumer.h
@@ -108,6 +108,7 @@ class BackendConsumer : public ASTConsumer {
void HandleTagDeclRequiredDefinition(const TagDecl *D) override;
void CompleteTentativeDefinition(VarDecl *D) override;
void CompleteExternalDeclaration(DeclaratorDecl *D) override;
+ void CompletePragmaExport(Decl *D) override;
void AssignInheritanceModel(CXXRecordDecl *RD) override;
void HandleVTable(CXXRecordDecl *RD) override;
diff --git a/clang/lib/CodeGen/CodeGenAction.cpp b/clang/lib/CodeGen/CodeGenAction.cpp
index c9f9b688d0d8a2..830f605fb6ad78 100644
--- a/clang/lib/CodeGen/CodeGenAction.cpp
+++ b/clang/lib/CodeGen/CodeGenAction.cpp
@@ -380,6 +380,10 @@ void BackendConsumer::CompleteExternalDeclaration(DeclaratorDecl *D) {
Gen->CompleteExternalDeclaration(D);
}
+void BackendConsumer::CompletePragmaExport(Decl *D) {
+ Gen->CompletePragmaExport(D);
+}
+
void BackendConsumer::AssignInheritanceModel(CXXRecordDecl *RD) {
Gen->AssignInheritanceModel(RD);
}
diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp
index 25c1c496a4f27f..599ec5bab83bce 100644
--- a/clang/lib/CodeGen/CodeGenModule.cpp
+++ b/clang/lib/CodeGen/CodeGenModule.cpp
@@ -5275,6 +5275,21 @@ void CodeGenModule::EmitExternalDeclaration(const DeclaratorDecl *D) {
EmitExternalFunctionDeclaration(FD);
}
+void CodeGenModule::EmitPragmaExport(const Decl *D) {
+ StringRef MangledName;
+ if (auto FD = dyn_cast<FunctionDecl>(D))
+ MangledName = getMangledName(GlobalDecl(FD));
+ else if (auto VD = dyn_cast<VarDecl>(D))
+ MangledName = getMangledName(GlobalDecl(VD));
+ else
+ assert(0 && "Unsupported pragma export Decl type");
+
+ if (llvm::GlobalValue *GV = GetGlobalValue(MangledName)) {
+ GV->setVisibility(llvm::GlobalValue::DefaultVisibility);
+ GV->setDSOLocal(false);
+ }
+}
+
CharUnits CodeGenModule::GetTargetTypeStoreSize(llvm::Type *Ty) const {
return Context.toCharUnitsFromBits(
getDataLayout().getTypeStoreSizeInBits(Ty));
diff --git a/clang/lib/CodeGen/CodeGenModule.h b/clang/lib/CodeGen/CodeGenModule.h
index c58bb88035ca8a..2e9a0617491f60 100644
--- a/clang/lib/CodeGen/CodeGenModule.h
+++ b/clang/lib/CodeGen/CodeGenModule.h
@@ -1364,6 +1364,8 @@ class CodeGenModule : public CodeGenTypeCache {
void EmitExternalDeclaration(const DeclaratorDecl *D);
+ void EmitPragmaExport(const Decl *D);
+
void EmitVTable(CXXRecordDecl *Class);
void RefreshTypeCacheForClass(const CXXRecordDecl *Class);
diff --git a/clang/lib/CodeGen/ModuleBuilder.cpp b/clang/lib/CodeGen/ModuleBuilder.cpp
index d4e0ab0339a8b0..7658d97af01840 100644
--- a/clang/lib/CodeGen/ModuleBuilder.cpp
+++ b/clang/lib/CodeGen/ModuleBuilder.cpp
@@ -314,7 +314,11 @@ namespace {
Builder->EmitExternalDeclaration(D);
}
- void HandleVTable(CXXRecordDecl *RD) override {
+ void CompletePragmaExport(Decl *D) override {
+ Builder->EmitPragmaExport(D);
+ }
+
+ void HandleVTable(CXXRecordDecl *RD) override {
if (Diags.hasUnrecoverableErrorOccurred())
return;
diff --git a/clang/lib/Driver/ToolChains/ZOS.cpp b/clang/lib/Driver/ToolChains/ZOS.cpp
index 074e0556ecd2ad..a1e7ddbe389080 100644
--- a/clang/lib/Driver/ToolChains/ZOS.cpp
+++ b/clang/lib/Driver/ToolChains/ZOS.cpp
@@ -37,6 +37,11 @@ void ZOS::addClangTargetOptions(const ArgList &DriverArgs,
options::OPT_fno_aligned_allocation))
CC1Args.push_back("-faligned-alloc-unavailable");
+ if (!DriverArgs.hasArg(options::OPT_fvisibility_EQ,
+ options::OPT_fvisibility_ms_compat)) {
+ CC1Args.push_back("-fvisibility=hidden");
+ }
+
// Pass "-fno-sized-deallocation" only when the user hasn't manually enabled
// or disabled sized deallocations.
if (!DriverArgs.hasArgNoClaim(options::OPT_fsized_deallocation,
@@ -149,11 +154,10 @@ void zos::Linker::ConstructJob(Compilation &C, const JobAction &JA,
StringRef OutputName = Output.getFilename();
// Strip away the last file suffix in presence from output name and add
// a new .x suffix.
- size_t Suffix = OutputName.find_last_of('.');
- const char *SideDeckName =
- Args.MakeArgString(OutputName.substr(0, Suffix) + ".x");
+ SmallString<128> SideDeckName = OutputName;
+ llvm::sys::path::replace_extension(SideDeckName, "x");
CmdArgs.push_back("-x");
- CmdArgs.push_back(SideDeckName);
+ CmdArgs.push_back(Args.MakeArgString(SideDeckName));
} else {
// We need to direct side file to /dev/null to suppress linker warning when
// the object file contains exported symbols, and -shared or
diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp
index a04eed9873c0d4..67e8405c639511 100644
--- a/clang/lib/Parse/ParseDecl.cpp
+++ b/clang/lib/Parse/ParseDecl.cpp
@@ -4449,6 +4449,12 @@ void Parser::ParseDeclarationSpecifiers(
isInvalid = DS.setFunctionSpecNoreturn(Loc, PrevSpec, DiagID);
break;
+ case tok::kw__Export:
+ // If we find kw__Export, it is being applied to a var or function
+ // This will be handled in ParseDeclaratorInternal()
+ goto DoneWithDeclSpec;
+ break;
+
// friend
case tok::kw_friend:
if (DSContext == DeclSpecContext::DSC_class) {
@@ -6174,6 +6180,7 @@ bool Parser::isDeclarationSpecifier(
case tok::kw_virtual:
case tok::kw_explicit:
case tok::kw__Noreturn:
+ case tok::kw__Export:
// alignment-specifier
case tok::kw__Alignas:
@@ -6765,6 +6772,17 @@ void Parser::ParseDeclaratorInternal(Declarator &D,
tok::TokenKind Kind = Tok.getKind();
+ // If this variable or function is marked as _Export, set the bit
+ if (Kind == tok::kw__Export) {
+ SourceLocation loc = ConsumeToken();
+ D.SetExport(loc);
+ D.SetRangeEnd(loc);
+
+ if (DirectDeclParser)
+ (this->*DirectDeclParser)(D);
+ return;
+ }
+
if (D.getDeclSpec().isTypeSpecPipe() && !isPipeDeclarator(D)) {
DeclSpec DS(AttrFactory);
ParseTypeQualifierListOpt(DS);
diff --git a/clang/lib/Parse/ParseDeclCXX.cpp b/clang/lib/Parse/ParseDeclCXX.cpp
index 6f0f5a0311bc18..5be80011d92bb2 100644
--- a/clang/lib/Parse/ParseDeclCXX.cpp
+++ b/clang/lib/Parse/ParseDeclCXX.cpp
@@ -1751,6 +1751,12 @@ void Parser::ParseClassSpecifier(tok::TokenKind TagTokKind,
// If attributes exist after tag, parse them.
for (;;) {
MaybeParseAttributes(PAKM_CXX11 | PAKM_Declspec | PAKM_GNU, attrs);
+ // If the token is _Export, set the bits
+ if (Tok.is(tok::kw__Export)) {
+ SourceLocation loc = ConsumeToken();
+ DS.setExportSpec(loc);
+ continue;
+ }
// Parse inheritance specifiers.
if (Tok.isOneOf(tok::kw___single_inheritance,
tok::kw___multiple_inheritance,
diff --git a/clang/lib/Parse/ParsePragma.cpp b/clang/lib/Parse/ParsePragma.cpp
index cc6f18b5b319f9..cc788dc2bf826d 100644
--- a/clang/lib/Parse/ParsePragma.cpp
+++ b/clang/lib/Parse/ParsePragma.cpp
@@ -401,6 +401,12 @@ struct PragmaMaxTokensTotalHandler : public PragmaHandler {
Token &FirstToken) override;
};
+struct PragmaExportHandler : public PragmaHandler {
+ explicit PragmaExportHandler() : PragmaHandler("export") {}
+ void HandlePragma(Preprocessor &PP, PragmaIntroducer Introducer,
+ Token &FirstToken) override;
+};
+
struct PragmaRISCVHandler : public PragmaHandler {
PragmaRISCVHandler(Sema &Actions)
: PragmaHandler("riscv"), Actions(Actions) {}
@@ -564,6 +570,11 @@ void Parser::initializePragmaHandlers() {
MaxTokensTotalPragmaHandler = std::make_unique<PragmaMaxTokensTotalHandler>();
PP.AddPragmaHandler("clang", MaxTokensTotalPragmaHandler.get());
+ if (getLangOpts().ZOSExt) {
+ ExportHandler = std::make_unique<PragmaExportHandler>();
+ PP.AddPragmaHandler(ExportHandler.get());
+ }
+
if (getTargetInfo().getTriple().isRISCV()) {
RISCVPragmaHandler = std::make_unique<PragmaRISCVHandler>(Actions);
PP.AddPragmaHandler("clang", RISCVPragmaHandler.get());
@@ -1401,6 +1412,164 @@ bool Parser::HandlePragmaMSAllocText(StringRef PragmaName,
return true;
}
+NestedNameSpecifier *Parser::zOSParseIdentifier(StringRef PragmaName,
+ IdentifierInfo *IdentName) {
+ NestedNameSpecifier *NestedId = nullptr;
+ if (Tok.is(tok::coloncolon)) {
+ NestedId = NestedNameSpecifier::Create(Actions.Context, IdentName);
+ } else if (Actions.CurContext->isNamespace()) {
+ auto *NS = cast<NamespaceDecl>(Actions.CurContext);
+ NestedId =
+ NestedNameSpecifier::Create(Actions.Context, NS->getIdentifier());
+ NestedId =
+ NestedNameSpecifier::Create(Actions.Context, NestedId, IdentName);
+ PP.Lex(Tok);
+ } else {
+ NestedId = NestedNameSpecifier::Create(Actions.Context, IdentName);
+ PP.Lex(Tok);
+ }
+ while (Tok.is(tok::coloncolon)) {
+ PP.Lex(Tok);
+ IdentName = Tok.getIdentifierInfo();
+ if (Tok.isNot(tok::identifier)) {
+ PP.Diag(Tok.getLocation(), diag::warn_pragma_expected_identifier)
+ << PragmaName;
+ return nullptr;
+ }
+ NestedId =
+ NestedNameSpecifier::Create(Actions.Context, NestedId, IdentName);
+ PP.Lex(Tok);
+ }
+ return NestedId;
+}
+
+bool Parser::zOSParseParameterList(StringRef PragmaName,
+ std::optional<SmallVector<QualType, 4>> &TypeList,
+ Qualifiers &CVQual) {
+ if (Tok.is(tok::l_paren)) {
+ TypeList = SmallVector<QualType, 4>();
+ PP.Lex(Tok);
+ while (Tok.isNot(tok::eof) && !Tok.is(tok::r_paren)) {
+ SourceRange MatchingCTypeRange;
+ TypeResult TResult = ParseTypeName(&MatchingCTypeRange);
+ if (!TResult.isInvalid()) {
+ QualType QT = TResult.get().get();
+ if (!QT.getTypePtr()->isVoidType())
+ TypeList->push_back(QT);
+ }
+ if (Tok.is(tok::comma) || Tok.is(tok::identifier))
+ PP.Lex(Tok);
+ }
+ if (Tok.is(tok::r_paren))
+ PP.Lex(Tok);
+ else {
+ // We ate the whole line trying to find the right paren of the parameter
+ // list
+ PP.Diag(Tok.getLocation(), diag::warn_pragma_expected_identifier)
+ << PragmaName;
+ return false;
+ }
+
+ if (TypeList.has_value())
+ while (Tok.is(tok::kw_const) || Tok.is(tok::kw_volatile)) {
+ if (Tok.is(tok::kw_const)) {
+ CVQual.addConst();
+ } else {
+ assert(Tok.is(tok::kw_volatile));
+ CVQual.addVolatile();
+ }
+ PP.Lex(Tok);
+ }
+ }
+ return true;
+}
+
+bool Parser::zOSHandlePragmaHelper(tok::TokenKind PragmaKind) {
+ assert(Tok.is(PragmaKind));
+
+ bool IsPragmaExport = PragmaKind == tok::annot_pragma_export;
+ assert(IsPragmaExport);
+ StringRef PragmaName = "export";
+
+ using namespace clang::charinfo;
+ auto *TheTokens =
+ (std::pair<std::unique_ptr<Token[]>, size_t> *)Tok.getAnnotationValue();
+ PP.EnterTokenStream(std::move(TheTokens->first), TheTokens->second, true,
+ false);
+ ConsumeAnnotationToken(); // The annotation token.
+
+ do {
+
+ PP.Lex(Tok);
+ if (Tok.isNot(tok::l_paren)) {
+ PP.Diag(Tok.getLocation(), diag::warn_pragma_expected_lparen)
+ << PragmaName;
+ return false;
+ }
+
+ // C++ could have a nested name, or be qualified with ::.
+ PP.Lex(Tok);
+ if (Tok.isNot(tok::identifier) && Tok.isNot(tok::coloncolon)) {
+ PP.Diag(Tok.getLocation(), diag::warn_pragma_expected_identifier)
+ << PragmaName;
+ return false;
+ }
+
+ IdentifierInfo *IdentName = Tok.getIdentifierInfo();
+ SourceLocation IdentNameLoc = Tok.getLocation();
+ NestedNameSpecifier *NestedId = zOSParseIdentifier(PragmaName, IdentName);
+ if (!NestedId)
+ return false;
+
+ if (Tok.isNot(tok::l_paren) && Tok.isNot(tok::r_paren)) {
+ PP.Diag(Tok.getLocation(), diag::warn_pragma_expected_identifier)
+ << PragmaName;
+ return false;
+ }
+
+ // C++ can have a paramater list for overloaded functions.
+ // Try to parse the argument types.
+ std::optional<SmallVector<QualType, 4>> TypeList;
+ Qualifiers CVQual;
+
+ if (!zOSParseParameterList(PragmaName, TypeList, CVQual))
+ return false;
+
+ PP.Lex(Tok);
+ Actions.ActOnPragmaExport(NestedId, IdentNameLoc, std::move(TypeList),
+ CVQual);
+
+ //Because export is also a C++ keyword, we also check for that
+ if (Tok.is(tok::identifier) || Tok.is(tok::kw_export)) {
+ IsPragmaExport = false;
+ PragmaName = Tok.getIdentifierInfo()->getName();
+ if (PragmaName == "export")
+ IsPragmaExport = true;
+ else
+ PP.Diag(Tok.getLocation(), diag::warn_pragma_extra_tokens_at_eol)
+ << PragmaName;
+ } else if (Tok.isNot(tok::eof)) {
+ PP.Diag(Tok.getLocation(), diag::warn_pragma_extra_tokens_at_eol)
+ << PragmaName;
+ return false;
+ }
+ } while (Tok.isNot(tok::eof));
+ PP.Lex(Tok);
+ return true;
+}
+
+void Parser::HandlePragmaExport() {
+ assert(Tok.is(tok::annot_pragma_export));
+
+ if (!zOSHandlePragmaHelper(tok::annot_pragma_export)) {
+ // Parsing pragma failed, and has been diagnosed. Slurp up the
+ // tokens until eof (really end of line) to prevent follow-on errors.
+ while (Tok.isNot(tok::eof))
+ PP.Lex(Tok);
+ PP.Lex(Tok);
+ }
+}
+
static std::string PragmaLoopHintString(Token PragmaName, Token Option) {
StringRef Str = PragmaName.getIdentifierInfo()->getName();
std::string ClangLoopStr("clang loop ");
@@ -4123,6 +4292,45 @@ void PragmaMaxTokensTotalHandler::HandlePragma(Preprocessor &PP,
PP.overrideMaxTokens(MaxTokens, Loc);
}
+/// Helper function for handling z/OS pragmas like #pragma export.
+static void zOSPragmaHandlerHelper(Preprocessor &PP,
+ Token &Tok,
+ tok::TokenKind TokKind) {
+ Token EoF, AnnotTok;
+ EoF.startToken();
+ EoF.setKind(tok::eof);
+ AnnotTok.startToken();
+ AnnotTok.setKind(TokKind);
+ AnnotTok.setLocation(Tok.getLocation());
+ AnnotTok.setAnnotationEndLoc(Tok.getLocation());
+ SmallVector<Token, 8> TokenVector;
+ // Suck up all of the tokens before the eod.
+ for (; Tok.isNot(tok::eod); PP.Lex(Tok)) {
+ TokenVector.push_back(Tok);
+ AnnotTok.setAnnotationEndLoc(Tok.getLocation());
+ }
+ // Add a sentinel EoF token to the end of the list.
+ TokenVector.push_back(EoF);
+ // We must allocate this array with new because EnterTokenStream is going to
+ // delete it later.
+ markAsReinjectedForRelexing(TokenVector);
+ auto TokenArray = std::make_unique<Token[]>(TokenVector.size());
+ std::copy(TokenVector.begin(), TokenVector.end(), TokenArray.get());
+ auto Value = new (PP.getPreprocessorAllocator())
+ std::pair<std::unique_ptr<Token[]>, size_t>(std::move(TokenArray),
+
+ TokenVector.size());
+ AnnotTok.setAnnotationValue(Value);
+ PP.EnterToken(AnnotTok, /*IsReinject*/ false);
+}
+
+/// Handle #pragma export.
+void PragmaExportHandler::HandlePragma(Preprocessor &PP,
+ PragmaIntroducer Introducer,
+ Token &FirstToken) {
+ zOSPragmaHandlerHelper(PP, FirstToken, tok::annot_pragma_export);
+}
+
// Handle '#pragma clang riscv intrinsic vector'.
// '#pragma clang riscv intrinsic sifive_vector'.
void PragmaRISCVHandler::HandlePragma(Preprocessor &PP,
diff --git a/clang/lib/Parse/Parser.cpp b/clang/lib/Parse/Parser.cpp
index 04c2f1d380bc48..e701d7378d50ee 100644
--- a/clang/lib/Parse/Parser.cpp
+++ b/clang/lib/Parse/Parser.cpp
@@ -881,6 +881,9 @@ Parser::ParseExternalDeclaration(ParsedAttributes &Attrs,
case tok::annot_pragma_attribute:
HandlePragmaAttribute();
return nullptr;
+ case tok::annot_pragma_export:
+ HandlePragmaExport();
+ return nullptr;
case tok::semi:
// Either a C++11 empty-declaration or attribute-declaration.
SingleDecl =
diff --git a/clang/lib/Sema/DeclSpec.cpp b/clang/lib/Sema/DeclSpec.cpp
index 12d2d3f6060c63..1fa0173902e38a 100644
--- a/clang/lib/Sema/DeclSpec.cpp
+++ b/clang/lib/Sema/DeclSpec.cpp
@@ -1107,6 +1107,12 @@ bool DeclSpec::setFunctionSpecNoreturn(SourceLocation Loc,
return false;
}
+bool DeclSpec::setExportSpec(SourceLocation Loc) {
+ export_specified = true;
+ exportLoc = Loc;
+ return false;
+}
+
bool DeclSpec::SetFriendSpec(SourceLocation Loc, const char *&PrevSpec,
unsigned &DiagID) {
if (isFriendSpecified()) {
diff --git a/clang/lib/Sema/Sema.cpp b/clang/lib/Sema/Sema.cpp
index 4be7dfbc293927..22bf20ac9b442f 100644
--- a/clang/lib/Sema/Sema.cpp
+++ b/clang/lib/Sema/Sema.cpp
@@ -1415,6 +1415,27 @@ void Sema::ActOnEndOfTranslationUnit() {
Consumer.CompleteExternalDeclaration(D);
}
+ // Visit all pending #pragma export
+ for (auto &Iter : PendingExportNames) {
+ NestedNameSpecifier *Name = Iter.first;
+ PendingSymbolOverloads &Overloads = Iter.second;
+ for (auto &I : Overloads) {
+ if (auto *D = trySymbolLookUp(Name, I)) {
+ if (D->hasExternalFormalLinkage()) {
+ if (D->isCXXClassMember()) {
+ D->addAttr(VisibilityAttr::CreateImplicit(
+ Context,
+ (VisibilityAttr::VisibilityType) /*DefaultVisibility*/ 0));
+ } else
+ Consumer.CompletePragmaExport(D);
+ } else
+ Diag(D->getLocation(), diag::warn_pragma_not_applied) << "export"
+ << D;
+ } else
+ Diag(I.NameLoc, diag::warn_failed_to_resolve_pragma) << "export";
+ }
+ }
+
if (LangOpts.HLSL)
HLSL().DiagnoseAvailabilityViolations(
getASTContext().getTranslationUnitDecl());
diff --git a/clang/lib/Sema/SemaAttr.cpp b/clang/lib/Sema/SemaAttr.cpp
index cf2a5a622a3a4d..ea0367f7790f0a 100644
--- a/clang/lib/Sema/SemaAttr.cpp
+++ b/clang/lib/Sema/SemaAttr.cpp
@@ -1273,6 +1273,168 @@ void Sema::AddImplicitMSFunctionNoBuiltinAttr(FunctionDecl *FD) {
FD->addAttr(NoBuiltinAttr::CreateImplicit(Context, V.data(), V.size()));
}
+static bool typeListMatches(FunctionDecl *FD,
+ const clang::Sema::SymbolLabel &Label) {
+ assert(Label.TypeList.has_value());
+ if (FD->getNumParams() != Label.TypeList->size()) {
+ return false;
+ }
+
+ // Check if arguments match.
+ for (unsigned i = 0; i != FD->getNumParams(); ++i) {
+ const ParmVarDecl *PVD = FD->getParamDecl(i);
+ QualType ParmType = PVD->getOriginalType().getCanonicalType();
+ QualType MapArgType = (*Label.TypeList)[i].getCanonicalType();
+
+ if (ParmType != MapArgType)
+ return false;
+ }
+
+ if (isa<CXXMethodDecl>(FD)) {
+ // Check if CV qualifiers match.
+ const clang::CXXMethodDecl *const MFD =
+ clang::cast<clang::CXXMethodDecl>(FD);
+ if (MFD && (MFD->isConst() != Label.CVQual.hasConst() ||
+ MFD->isVolatile() != Label.CVQual.hasVolatile())) {
+ return false;
+ }
+ } else if (Label.CVQual.hasConst() || Label.CVQual.hasVolatile())
+ return false;
+
+ return true;
+}
+
+FunctionDecl *Sema::tryFunctionLookUp(NestedNameSpecifier *NestedName,
+ SourceLocation NameLoc) {
+ assert(!NestedName->getPrefix() ||
+ NestedName->getPrefix()->getKind() == NestedNameSpecifier::Identifier);
+ IdentifierInfo *Prefix =
+ NestedName->getPrefix() ? NestedName->getPrefix()->getAsIdentifier() : 0;
+ IdentifierInfo *Name = NestedName->getAsIdentifier();
+ LookupResult Result(*this, (Prefix ? Prefix : Name), NameLoc,
+ LookupOrdinaryName);
+ LookupName(Result, TUScope);
+
+ // Filter down to just a function, namespace or class.
+ LookupResult::Filter F = Result.makeFilter();
+ while (F.hasNext()) {
+ NamedDecl *D = F.next();
+ if (!(isa<FunctionDecl>(D)))
+ F.erase();
+ }
+ F.done();
+ // Loop over all the found decls and see if the arguments match
+ // any of the results
+ for (LookupResult::iterator I = Result.begin(); I != Result.end(); ++I) {
+ NamedDecl *ND = (*I)->getUnderlyingDecl();
+ FunctionDecl *FD = dyn_cast<FunctionDecl>(ND);
+ if (FD) {
+ return FD;
+ }
+ }
+ return nullptr;
+}
+
+NamedDecl *Sema::trySymbolLookUp(NestedNameSpecifier *NestedName,
+ const clang::Sema::SymbolLabel &Label) {
+
+ assert(!NestedName->getPrefix() ||
+ NestedName->getPrefix()->getKind() == NestedNameSpecifier::Identifier);
+ IdentifierInfo *Prefix =
+ NestedName->getPrefix() ? NestedName->getPrefix()->getAsIdentifier() : 0;
+ IdentifierInfo *Name = NestedName->getAsIdentifier();
+ LookupResult Result(*this, (Prefix ? Prefix : Name), Label.NameLoc,
+ LookupOrdinaryName);
+ LookupName(Result, TUScope);
+
+ // Filter down to just a function, namespace or class.
+ LookupResult::Filter F = Result.makeFilter();
+ while (F.hasNext()) {
+ NamedDecl *D = F.next();
+ if (!(isa<FunctionDecl>(D) || isa<VarDecl>(D) || isa<NamespaceDecl>(D) ||
+ isa<CXXRecordDecl>(D)))
+ F.erase();
+ }
+ F.done();
+
+ auto MatchDecl = [Name, Label](DeclContext *DC) -> NamedDecl * {
+ auto LRes = DC->lookup(DeclarationName(Name));
+ for (auto *I : LRes) {
+ if (isa<VarDecl>(I))
+ return I;
+ if (isa<FunctionDecl>(I)) {
+ FunctionDecl *FD = dyn_cast<FunctionDecl>(I);
+
+ // All function parameters must match if specified in pragma otherwise,
+ // we accept a function found by lookup only if it's the only one.
+ if ((Label.TypeList.has_value() && typeListMatches(FD, Label)) ||
+ (!Label.TypeList.has_value() && LRes.isSingleResult()))
+ return FD;
+ }
+ }
+ return nullptr;
+ };
+
+ // global variable or function in a namespace
+ if (NamespaceDecl *ND = Result.getAsSingle<NamespaceDecl>()) {
+ if (ND->getIdentifierNamespace() == Decl::IDNS_Namespace) {
+ return MatchDecl(ND);
+ }
+ }
+
+ // data or function member
+ if (CXXRecordDecl *RD = Result.getAsSingle<CXXRecordDecl>()) {
+ return MatchDecl(RD);
+ }
+
+ // either a variable, or a non-overloaded function, or an overloaded
+ // function with extern "C" linkage.
+ if (!Label.TypeList.has_value()) {
+ if (Result.isSingleResult())
+ return Result.getFoundDecl();
+ if (Result.isOverloadedResult()) {
+ for (auto *Iter : Result) {
+ FunctionDecl *FD = dyn_cast<FunctionDecl>(Iter);
+ if (FD && FD->isExternC())
+ return FD;
+ }
+ return nullptr;
+ }
+ return nullptr;
+ }
+
+ // Loop over all the found decls and see if the arguments match
+ // any of the results
+ for (LookupResult::iterator I = Result.begin(); I != Result.end(); ++I) {
+ NamedDecl *ND = (*I)->getUnderlyingDecl();
+ FunctionDecl *FD = dyn_cast<FunctionDecl>(ND);
+ if (FD && typeListMatches(FD, Label)) {
+ return FD;
+ }
+ }
+ return nullptr;
+}
+
+void Sema::ActOnPragmaExport(NestedNameSpecifier *NestedId,
+ SourceLocation NameLoc,
+ std::optional<SmallVector<QualType, 4>> &&TypeList,
+ Qualifiers CVQual) {
+ SymbolLabel Label;
+ Label.NameLoc = NameLoc;
+ Label.CVQual = CVQual;
+ Label.TypeList = std::move(TypeList);
+
+ auto I = PendingExportNames.find(NestedId);
+ if (I == PendingExportNames.end()) {
+ std::pair<SymbolNames::iterator, bool> IB = PendingExportNames.insert(
+ std::pair<NestedNameSpecifier *, PendingSymbolOverloads>(
+ NestedId, PendingSymbolOverloads()));
+ assert(IB.second);
+ I = IB.first;
+ }
+ I->second.push_back(Label);
+}
+
typedef std::vector<std::pair<unsigned, SourceLocation> > VisStack;
enum : unsigned { NoVisibility = ~0U };
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index 0e536f71a2f70d..9182647a4f3d6b 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -6418,6 +6418,19 @@ NamedDecl *Sema::HandleDeclarator(Scope *S, Declarator &D,
if (!New)
return nullptr;
+ if (D.IsExport()) {
+ VisibilityAttr *existingAttr = New->getAttr<VisibilityAttr>();
+ if (existingAttr) {
+ VisibilityAttr::VisibilityType existingValue =
+ existingAttr->getVisibility();
+ if (existingValue != VisibilityAttr::Default)
+ Diag(D.getExportLoc(), diag::err_mismatched_visibility);
+ } else {
+ New->addAttr(
+ VisibilityAttr::CreateImplicit(Context, VisibilityAttr::Default));
+ }
+ }
+
// If this has an identifier and is not a function template specialization,
// add it to the scope stack.
if (New->getDeclName() && AddToScope)
diff --git a/clang/test/CodeGen/attr-export-failing.cpp b/clang/test/CodeGen/attr-export-failing.cpp
new file mode 100644
index 00000000000000..ee877aa0fb491b
--- /dev/null
+++ b/clang/test/CodeGen/attr-export-failing.cpp
@@ -0,0 +1,4 @@
+// RUN: not %clang_cc1 -triple s390x-ibm-zos -fzos-extensions %s
+__attribute__((visibility("hidden"))) int _Export i; // expected-error {{visibility does not match previous declaration}}
+class __attribute__((visibility("hidden"))) _Export C; // expected-error {{visibility does not match previous declaration}}
+
diff --git a/clang/test/CodeGen/attr-export.cpp b/clang/test/CodeGen/attr-export.cpp
new file mode 100644
index 00000000000000..5f78bc81482488
--- /dev/null
+++ b/clang/test/CodeGen/attr-export.cpp
@@ -0,0 +1,51 @@
+// RUN: %clang --target=s390x-ibm-zos -S -emit-llvm %s -o - | FileCheck %s
+
+// Check the variables
+// CHECK: @var1 = global i32 0, align 4
+// CHECK: @var2 = hidden global i32 0, align 4
+// CHECK: @var3 = global i32 0, align 4
+// CHECK: @var4 = hidden global i32 0, align 4
+// CHECK: @var5 = global i32 0, align 4
+// CHECK: @obj1 = global %class.class1 zeroinitializer, align 2
+// CHECK: @obj2 = hidden global %class.class1 zeroinitializer, align 2
+
+// Check the functions
+// CHECK: define void @_Z4foo1v
+// CHECK: define hidden void @_Z4foo2v
+// CHECK: define void @_ZN6class13fooEv
+// CHECK: define hidden void @_ZN6class23fooEv
+// CHECK: define hidden void @_ZN6class33fooEv
+// CHECK: define void @_ZN6class33barEv
+
+int _Export var1;
+int var2;
+int _Export var3, var4, _Export var5;
+
+void _Export foo1(){};
+void foo2(){};
+
+class _Export class1 {
+public:
+ void foo();
+};
+
+class class2 {
+public:
+ void foo();
+};
+
+void class1::foo(){};
+
+void class2::foo(){};
+
+class1 _Export obj1;
+class1 obj2;
+
+class class3 {
+public:
+ void foo();
+ void _Export bar();
+};
+
+void class3::foo() {};
+void class3::bar() {};
diff --git a/clang/test/CodeGen/pragma-export.c b/clang/test/CodeGen/pragma-export.c
new file mode 100644
index 00000000000000..999a270b82aa94
--- /dev/null
+++ b/clang/test/CodeGen/pragma-export.c
@@ -0,0 +1,39 @@
+// RUN: %clang_cc1 %s -emit-llvm -fzos-extensions -fvisibility=hidden -verify -o - | FileCheck %s
+
+// Testing missing declarations.
+#pragma export(d0) // expected-warning{{failed to resolve '#pragma export' to a declaration}}
+#pragma export(f9) // expected-warning{{failed to resolve '#pragma export' to a declaration}}
+
+// Testing pragma export after decl.
+void f0(void) {}
+static void sf0(void) {} // expected-warning{{#pragma export is applicable to symbols with external linkage only; not applied to 'sf0'}}
+int v0;
+static int s0; // expected-warning{{#pragma export is applicable to symbols with external linkage only; not applied to 's0'}}
+#pragma export(f0)
+#pragma export(sf0)
+#pragma export(v0)
+#pragma export(s0)
+
+// Testing pragma export before decl.
+#pragma export(f1)
+#pragma export(sf1)
+#pragma export(v1)
+#pragma export(s1)
+void f1(void) {}
+static void sf1(void) {} // expected-warning{{#pragma export is applicable to symbols with external linkage only; not applied to 'sf1'}}
+int v1;
+static int s1; // expected-warning{{#pragma export is applicable to symbols with external linkage only; not applied to 's1'}}
+
+void f2(void) {}
+
+void t0(void) {}
+
+// Testing pragma export after decl and usage.
+#pragma export(f2)
+
+// CHECK: @v0 = global i32
+// CHECK: @v1 = global i32
+// CHECK: define void @f0()
+// CHECK: define void @f1()
+// CHECK: define void @f2()
+// CHECK: define hidden void @t0()
\ No newline at end of file
diff --git a/clang/test/CodeGen/pragma-export.cpp b/clang/test/CodeGen/pragma-export.cpp
new file mode 100644
index 00000000000000..e394cf668549fe
--- /dev/null
+++ b/clang/test/CodeGen/pragma-export.cpp
@@ -0,0 +1,82 @@
+// RUN: %clang_cc1 %s -emit-llvm -fzos-extensions -fvisibility=hidden -verify -o - | FileCheck %s
+
+// Testing missing declarations.
+#pragma export(d0) // expected-warning{{failed to resolve '#pragma export' to a declaration}}
+#pragma export(f9) // expected-warning{{failed to resolve '#pragma export' to a declaration}}
+#pragma export(f0(int)) // expected-warning{{failed to resolve '#pragma export' to a declaration}}
+#pragma export(f3(double, double, double)) // expected-warning{{failed to resolve '#pragma export' to a declaration}}
+
+// Testing pragma export after decl.
+void f0(void) {}
+static void sf0(void) {} // expected-warning{{#pragma export is applicable to symbols with external linkage only; not applied to 'sf0'}}
+int v0;
+static int s0; // expected-warning{{#pragma export is applicable to symbols with external linkage only; not applied to 's0'}}
+#pragma export(f0)
+#pragma export(sf0)
+#pragma export(v0)
+#pragma export(s0)
+
+// Testing pragma export before decl.
+#pragma export(f1)
+#pragma export(sf1)
+#pragma export(v1)
+#pragma export(s1)
+void f1(void) {}
+static void sf1(void) {} // expected-warning{{#pragma export is applicable to symbols with external linkage only; not applied to 'sf1'}}
+int v1;
+static int s1; // expected-warning{{#pragma export is applicable to symbols with external linkage only; not applied to 's1'}}
+
+// Testing overloaded functions.
+#pragma export(f2(double, double))
+#pragma export(f2(int))
+void f2(double, double) {}
+void f2(int) {}
+void f2(int, int) {}
+
+void f3(double) {}
+void f3(int, double) {}
+void f3(double, double) {}
+#pragma export(f3(double))
+#pragma export(f3(int, double))
+
+void f2(void) {}
+
+void t0(void) {
+ f2();
+}
+
+// Testing pragma export after decl and usage.
+#pragma export(f2(void))
+
+// Testing pragma export with namespace.
+void f5(void) {}
+namespace N0 {
+void f0(void) {}
+void f1(void) {}
+void f2(void) {}
+void f3(void) {}
+void f5(void) {}
+#pragma export(f0)
+#pragma export(N0::f1)
+#pragma export(f5)
+} // namespace N0
+#pragma export(N0::f2)
+
+// CHECK: @v0 = global i32
+// CHECK: @v1 = global i32
+// CHECK: define void @_Z2f0v
+// CHECK: define void @_Z2f1v
+// CHECK: define void @_Z2f2dd
+// CHECK: define void @_Z2f2i
+// CHECK: define hidden void @_Z2f2ii
+// CHECK: define void @_Z2f3d
+// CHECK: define void @_Z2f3id
+// CHECK: define hidden void @_Z2f3dd
+// CHECK: define void @_Z2f2v
+// CHECK: define hidden void @_Z2t0v
+// CHECK: define hidden void @_Z2f5v
+// CHECK: define void @_ZN2N02f0Ev
+// CHECK: define void @_ZN2N02f1Ev
+// CHECK: define void @_ZN2N02f2Ev
+// CHECK: define hidden void @_ZN2N02f3Ev
+// CHECK: define void @_ZN2N02f5Ev
diff --git a/clang/test/CodeGen/zos-pragmas.c b/clang/test/CodeGen/zos-pragmas.c
new file mode 100644
index 00000000000000..6f0ee601b03f04
--- /dev/null
+++ b/clang/test/CodeGen/zos-pragmas.c
@@ -0,0 +1,11 @@
+// RUN: %clang_cc1 -emit-llvm -triple s390x-none-zos -fvisibility=hidden %s -o - | FileCheck %s
+// expected-no-diagnostics
+
+int a,b,c;
+#pragma export(a) export(b) export(c)
+
+void foo(void);
+
+// CHECK: @a = global i32 0, align 4
+// CHECK: @b = global i32 0, align 4
+// CHECK: @c = global i32 0, align 4
diff --git a/clang/test/CodeGen/zos-pragmas.cpp b/clang/test/CodeGen/zos-pragmas.cpp
new file mode 100644
index 00000000000000..70495a41232880
--- /dev/null
+++ b/clang/test/CodeGen/zos-pragmas.cpp
@@ -0,0 +1,11 @@
+// RUN: %clang_cc1 -x c++ -emit-llvm -triple s390x-none-zos -fvisibility=hidden %s -o - | FileCheck %s
+// expected-no-diagnostics
+
+int a,b,c;
+#pragma export(a) export(b) export(c)
+
+void foo(void);
+
+// CHECK: @a = global i32 0, align 4
+// CHECK: @b = global i32 0, align 4
+// CHECK: @c = global i32 0, align 4
>From e1cbba0828872c3237633b276be3b1f942760252 Mon Sep 17 00:00:00 2001
From: Sean Perry <perry at ca.ibm.com>
Date: Thu, 3 Oct 2024 08:52:51 -0500
Subject: [PATCH 2/5] Add pragma export & _Export
---
clang/include/clang/Sema/DeclSpec.h | 5 ++++-
clang/lib/AST/MicrosoftMangle.cpp | 1 +
clang/lib/Lex/Preprocessor.cpp | 6 ++++++
clang/lib/Sema/SemaDecl.cpp | 12 ++++++++++++
clang/test/CodeGen/attr-export.cpp | 2 +-
5 files changed, 24 insertions(+), 2 deletions(-)
diff --git a/clang/include/clang/Sema/DeclSpec.h b/clang/include/clang/Sema/DeclSpec.h
index f41748af5c8303..d03a6b11d26ab8 100644
--- a/clang/include/clang/Sema/DeclSpec.h
+++ b/clang/include/clang/Sema/DeclSpec.h
@@ -1965,6 +1965,7 @@ class Declarator {
unsigned InlineStorageUsed : 1;
/// Indicates whether this is set as _Export
+ LLVM_PREFERRED_TYPE(bool)
unsigned ExportSpecified : 1;
/// Indicates whether this declarator has an initializer.
@@ -2045,7 +2046,8 @@ class Declarator {
FunctionDefinitionKind::Declaration)),
Redeclaration(false), Extension(false), ObjCIvar(false),
ObjCWeakProperty(false), InlineStorageUsed(false),
- HasInitializer(false), Attrs(DS.getAttributePool().getFactory()),
+ ExportSpecified(false), HasInitializer(false),
+ Attrs(DS.getAttributePool().getFactory()),
DeclarationAttrs(DeclarationAttrs), AsmLabel(nullptr),
TrailingRequiresClause(nullptr),
InventedTemplateParameterList(nullptr) {
@@ -2152,6 +2154,7 @@ class Declarator {
HasInitializer = false;
ObjCIvar = false;
ObjCWeakProperty = false;
+ ExportSpecified = false;
CommaLoc = SourceLocation();
EllipsisLoc = SourceLocation();
PackIndexingExpr = nullptr;
diff --git a/clang/lib/AST/MicrosoftMangle.cpp b/clang/lib/AST/MicrosoftMangle.cpp
index e4c8663c134fda..4ccc79b928638d 100644
--- a/clang/lib/AST/MicrosoftMangle.cpp
+++ b/clang/lib/AST/MicrosoftMangle.cpp
@@ -1027,6 +1027,7 @@ void MicrosoftCXXNameMangler::mangleFloat(llvm::APFloat Number) {
case APFloat::S_Float6E3M2FN:
case APFloat::S_Float6E2M3FN:
case APFloat::S_Float4E2M1FN:
+ case APFloat::S_Float8E8M0FNU:
llvm_unreachable("Tried to mangle unexpected APFloat semantics");
}
diff --git a/clang/lib/Lex/Preprocessor.cpp b/clang/lib/Lex/Preprocessor.cpp
index f0b4593e0cc22e..750bf38180b0d2 100644
--- a/clang/lib/Lex/Preprocessor.cpp
+++ b/clang/lib/Lex/Preprocessor.cpp
@@ -171,22 +171,28 @@ Preprocessor::Preprocessor(std::shared_ptr<PreprocessorOptions> PPOpts,
Preprocessor::~Preprocessor() {
assert(!isBacktrackEnabled() && "EnableBacktrack/Backtrack imbalance!");
+ fprintf(stderr, "SDP: ----- in Preprocessor::~Preprocessor\n");
IncludeMacroStack.clear();
+ fprintf(stderr, "SDP: - call fill\n");
// Free any cached macro expanders.
// This populates MacroArgCache, so all TokenLexers need to be destroyed
// before the code below that frees up the MacroArgCache list.
std::fill(TokenLexerCache, TokenLexerCache + NumCachedTokenLexers, nullptr);
+ fprintf(stderr, "SDP: - call reset\n");
CurTokenLexer.reset();
+ fprintf(stderr, "SDP: - free cached macros\n");
// Free any cached MacroArgs.
for (MacroArgs *ArgList = MacroArgCache; ArgList;)
ArgList = ArgList->deallocate();
+ fprintf(stderr, "SDP: - del hdr search\n");
// Delete the header search info, if we own it.
if (OwnsHeaderSearch)
delete &HeaderInfo;
+ fprintf(stderr, "SDP: - done\n");
}
void Preprocessor::Initialize(const TargetInfo &Target,
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index 415df3ec493a51..94d80b551df80c 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -5082,6 +5082,18 @@ Decl *Sema::ParsedFreeStandingDeclSpec(Scope *S, AccessSpecifier AS,
assert(EllipsisLoc.isInvalid() &&
"Friend ellipsis but not friend-specified?");
+ if (DS.isExportSpecified()) {
+ VisibilityAttr *existingAttr = TagD->getAttr<VisibilityAttr>();
+ if (existingAttr) {
+ VisibilityAttr::VisibilityType existingValue = existingAttr->getVisibility();
+ if (existingValue != VisibilityAttr::Default)
+ Diag(DS.getExportSpecLoc(), diag::err_mismatched_visibility);
+ } else {
+ Tag->addAttr(VisibilityAttr::CreateImplicit(Context,
+ VisibilityAttr::Default));
+ }
+ }
+
// Track whether this decl-specifier declares anything.
bool DeclaresAnything = true;
diff --git a/clang/test/CodeGen/attr-export.cpp b/clang/test/CodeGen/attr-export.cpp
index 5f78bc81482488..fbcaffc080db38 100644
--- a/clang/test/CodeGen/attr-export.cpp
+++ b/clang/test/CodeGen/attr-export.cpp
@@ -1,4 +1,4 @@
-// RUN: %clang --target=s390x-ibm-zos -S -emit-llvm %s -o - | FileCheck %s
+// RUN: %clangxx --target=s390x-ibm-zos -S -emit-llvm %s -o - | FileCheck %s
// Check the variables
// CHECK: @var1 = global i32 0, align 4
>From 39a1411d8de01334e807d6214f51c23bba52d342 Mon Sep 17 00:00:00 2001
From: Sean Perry <perry at ca.ibm.com>
Date: Thu, 3 Oct 2024 09:52:42 -0500
Subject: [PATCH 3/5] restore to main
---
clang/lib/AST/MicrosoftMangle.cpp | 1 -
clang/lib/Lex/Preprocessor.cpp | 6 ------
2 files changed, 7 deletions(-)
diff --git a/clang/lib/AST/MicrosoftMangle.cpp b/clang/lib/AST/MicrosoftMangle.cpp
index 4234e713f9b350..4ccf3f76bf0ce2 100644
--- a/clang/lib/AST/MicrosoftMangle.cpp
+++ b/clang/lib/AST/MicrosoftMangle.cpp
@@ -1028,7 +1028,6 @@ void MicrosoftCXXNameMangler::mangleFloat(llvm::APFloat Number) {
case APFloat::S_Float6E3M2FN:
case APFloat::S_Float6E2M3FN:
case APFloat::S_Float4E2M1FN:
- case APFloat::S_Float8E8M0FNU:
llvm_unreachable("Tried to mangle unexpected APFloat semantics");
}
diff --git a/clang/lib/Lex/Preprocessor.cpp b/clang/lib/Lex/Preprocessor.cpp
index 750bf38180b0d2..f0b4593e0cc22e 100644
--- a/clang/lib/Lex/Preprocessor.cpp
+++ b/clang/lib/Lex/Preprocessor.cpp
@@ -171,28 +171,22 @@ Preprocessor::Preprocessor(std::shared_ptr<PreprocessorOptions> PPOpts,
Preprocessor::~Preprocessor() {
assert(!isBacktrackEnabled() && "EnableBacktrack/Backtrack imbalance!");
- fprintf(stderr, "SDP: ----- in Preprocessor::~Preprocessor\n");
IncludeMacroStack.clear();
- fprintf(stderr, "SDP: - call fill\n");
// Free any cached macro expanders.
// This populates MacroArgCache, so all TokenLexers need to be destroyed
// before the code below that frees up the MacroArgCache list.
std::fill(TokenLexerCache, TokenLexerCache + NumCachedTokenLexers, nullptr);
- fprintf(stderr, "SDP: - call reset\n");
CurTokenLexer.reset();
- fprintf(stderr, "SDP: - free cached macros\n");
// Free any cached MacroArgs.
for (MacroArgs *ArgList = MacroArgCache; ArgList;)
ArgList = ArgList->deallocate();
- fprintf(stderr, "SDP: - del hdr search\n");
// Delete the header search info, if we own it.
if (OwnsHeaderSearch)
delete &HeaderInfo;
- fprintf(stderr, "SDP: - done\n");
}
void Preprocessor::Initialize(const TargetInfo &Target,
>From 9cfc3ccba6bca8b9b2146e0830e21dc42e5cecdb Mon Sep 17 00:00:00 2001
From: Sean Perry <perry at ca.ibm.com>
Date: Thu, 3 Oct 2024 12:22:35 -0500
Subject: [PATCH 4/5] Reset pragma handler was missing
---
clang/lib/Parse/ParsePragma.cpp | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/clang/lib/Parse/ParsePragma.cpp b/clang/lib/Parse/ParsePragma.cpp
index cc788dc2bf826d..af892115f1f8dd 100644
--- a/clang/lib/Parse/ParsePragma.cpp
+++ b/clang/lib/Parse/ParsePragma.cpp
@@ -709,6 +709,11 @@ void Parser::resetPragmaHandlers() {
PP.RemovePragmaHandler("clang", MaxTokensTotalPragmaHandler.get());
MaxTokensTotalPragmaHandler.reset();
+ if (getLangOpts().ZOSExt) {
+ PP.RemovePragmaHandler(ExportHandler.get());
+ ExportHandler.reset();
+ }
+
if (getTargetInfo().getTriple().isRISCV()) {
PP.RemovePragmaHandler("clang", RISCVPragmaHandler.get());
RISCVPragmaHandler.reset();
>From e0cb769b2f8c4f9b20dad0071fd6a15ae3960e5d Mon Sep 17 00:00:00 2001
From: Sean Perry <perry at ca.ibm.com>
Date: Thu, 3 Oct 2024 14:13:18 -0500
Subject: [PATCH 5/5] formating
---
clang/include/clang/Sema/DeclSpec.h | 9 ++++-----
clang/lib/CodeGen/ModuleBuilder.cpp | 2 +-
clang/lib/Parse/ParsePragma.cpp | 16 +++++++---------
clang/lib/Sema/Sema.cpp | 4 ++--
clang/lib/Sema/SemaDecl.cpp | 7 ++++---
5 files changed, 18 insertions(+), 20 deletions(-)
diff --git a/clang/include/clang/Sema/DeclSpec.h b/clang/include/clang/Sema/DeclSpec.h
index d03a6b11d26ab8..cf38b378352ee2 100644
--- a/clang/include/clang/Sema/DeclSpec.h
+++ b/clang/include/clang/Sema/DeclSpec.h
@@ -496,9 +496,8 @@ class DeclSpec {
TypeQualifiers(TQ_unspecified), FS_inline_specified(false),
FS_forceinline_specified(false), FS_virtual_specified(false),
FS_noreturn_specified(false), export_specified(false),
- FriendSpecifiedFirst(false),
- ConstexprSpecifier(
- static_cast<unsigned>(ConstexprSpecKind::Unspecified)),
+ FriendSpecifiedFirst(false), ConstexprSpecifier( static_cast<unsigned>(
+ ConstexprSpecKind::Unspecified)),
Attrs(attrFactory), writtenBS(), ObjCQualifiers(nullptr) {}
// storage-class-specifier
@@ -668,7 +667,7 @@ class DeclSpec {
bool isExportSpecified() const { return export_specified; }
SourceLocation getExportSpecLoc() const { return exportLoc; }
- void ClearFunctionSpecs() {
+ void ClearFunctionSpecs() {
FS_inline_specified = false;
FS_inlineLoc = SourceLocation();
FS_forceinline_specified = false;
@@ -2046,7 +2045,7 @@ class Declarator {
FunctionDefinitionKind::Declaration)),
Redeclaration(false), Extension(false), ObjCIvar(false),
ObjCWeakProperty(false), InlineStorageUsed(false),
- ExportSpecified(false), HasInitializer(false),
+ ExportSpecified(false), HasInitializer(false),
Attrs(DS.getAttributePool().getFactory()),
DeclarationAttrs(DeclarationAttrs), AsmLabel(nullptr),
TrailingRequiresClause(nullptr),
diff --git a/clang/lib/CodeGen/ModuleBuilder.cpp b/clang/lib/CodeGen/ModuleBuilder.cpp
index 7658d97af01840..e0c7ae03f90f19 100644
--- a/clang/lib/CodeGen/ModuleBuilder.cpp
+++ b/clang/lib/CodeGen/ModuleBuilder.cpp
@@ -318,7 +318,7 @@ namespace {
Builder->EmitPragmaExport(D);
}
- void HandleVTable(CXXRecordDecl *RD) override {
+ void HandleVTable(CXXRecordDecl *RD) override {
if (Diags.hasUnrecoverableErrorOccurred())
return;
diff --git a/clang/lib/Parse/ParsePragma.cpp b/clang/lib/Parse/ParsePragma.cpp
index af892115f1f8dd..8203e15857ebd5 100644
--- a/clang/lib/Parse/ParsePragma.cpp
+++ b/clang/lib/Parse/ParsePragma.cpp
@@ -1448,9 +1448,9 @@ NestedNameSpecifier *Parser::zOSParseIdentifier(StringRef PragmaName,
return NestedId;
}
-bool Parser::zOSParseParameterList(StringRef PragmaName,
- std::optional<SmallVector<QualType, 4>> &TypeList,
- Qualifiers &CVQual) {
+bool Parser::zOSParseParameterList(
+ StringRef PragmaName, std::optional<SmallVector<QualType, 4>> &TypeList,
+ Qualifiers &CVQual) {
if (Tok.is(tok::l_paren)) {
TypeList = SmallVector<QualType, 4>();
PP.Lex(Tok);
@@ -1544,7 +1544,7 @@ bool Parser::zOSHandlePragmaHelper(tok::TokenKind PragmaKind) {
Actions.ActOnPragmaExport(NestedId, IdentNameLoc, std::move(TypeList),
CVQual);
- //Because export is also a C++ keyword, we also check for that
+ // Because export is also a C++ keyword, we also check for that
if (Tok.is(tok::identifier) || Tok.is(tok::kw_export)) {
IsPragmaExport = false;
PragmaName = Tok.getIdentifierInfo()->getName();
@@ -4298,8 +4298,7 @@ void PragmaMaxTokensTotalHandler::HandlePragma(Preprocessor &PP,
}
/// Helper function for handling z/OS pragmas like #pragma export.
-static void zOSPragmaHandlerHelper(Preprocessor &PP,
- Token &Tok,
+static void zOSPragmaHandlerHelper(Preprocessor &PP, Token &Tok,
tok::TokenKind TokKind) {
Token EoF, AnnotTok;
EoF.startToken();
@@ -4322,9 +4321,8 @@ static void zOSPragmaHandlerHelper(Preprocessor &PP,
auto TokenArray = std::make_unique<Token[]>(TokenVector.size());
std::copy(TokenVector.begin(), TokenVector.end(), TokenArray.get());
auto Value = new (PP.getPreprocessorAllocator())
- std::pair<std::unique_ptr<Token[]>, size_t>(std::move(TokenArray),
-
- TokenVector.size());
+ std::pair<std::unique_ptr<Token[]>, size_t>(std::move(TokenArray),
+ TokenVector.size());
AnnotTok.setAnnotationValue(Value);
PP.EnterToken(AnnotTok, /*IsReinject*/ false);
}
diff --git a/clang/lib/Sema/Sema.cpp b/clang/lib/Sema/Sema.cpp
index 6964a70ef5613c..1e5c17b88adc92 100644
--- a/clang/lib/Sema/Sema.cpp
+++ b/clang/lib/Sema/Sema.cpp
@@ -1429,8 +1429,8 @@ void Sema::ActOnEndOfTranslationUnit() {
} else
Consumer.CompletePragmaExport(D);
} else
- Diag(D->getLocation(), diag::warn_pragma_not_applied) << "export"
- << D;
+ Diag(D->getLocation(), diag::warn_pragma_not_applied)
+ << "export" << D;
} else
Diag(I.NameLoc, diag::warn_failed_to_resolve_pragma) << "export";
}
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index 15c1a36d9c061e..6ad84b1247306a 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -5090,12 +5090,13 @@ Decl *Sema::ParsedFreeStandingDeclSpec(Scope *S, AccessSpecifier AS,
if (DS.isExportSpecified()) {
VisibilityAttr *existingAttr = TagD->getAttr<VisibilityAttr>();
if (existingAttr) {
- VisibilityAttr::VisibilityType existingValue = existingAttr->getVisibility();
+ VisibilityAttr::VisibilityType existingValue =
+ existingAttr->getVisibility();
if (existingValue != VisibilityAttr::Default)
Diag(DS.getExportSpecLoc(), diag::err_mismatched_visibility);
} else {
- Tag->addAttr(VisibilityAttr::CreateImplicit(Context,
- VisibilityAttr::Default));
+ Tag->addAttr(
+ VisibilityAttr::CreateImplicit(Context, VisibilityAttr::Default));
}
}
More information about the cfe-commits
mailing list