-
Notifications
You must be signed in to change notification settings - Fork 13.6k
[SystemZ][z/OS] Add visibility features for z/OS (eg. _Export, pragma export) #111035
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
@llvm/pr-subscribers-clang @llvm/pr-subscribers-clang-driver Author: Sean Perry (perry-ca) ChangesThe 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:
Note: Patch is 44.65 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/111035.diff 28 Files Affected:
diff --git a/clang/include/clang/AST/ASTConsumer.h b/clang/include/clang/AST/ASTConsumer.h
index 447f2592d23595..b15d53e700c679 100644
--- a/clang/include/clang/AST/ASTConsumer.h
+++ b/clang/include/clang/AST/ASTConsumer.h
@@ -108,6 +108,9 @@ class ASTConsumer {
/// completed.
virtual void CompleteExternalDeclaration(DeclaratorDecl *D) {}
+ /// CompletePragmaExport - complete #pragma export statements.
+ virtual void CompletePragmaExport(Decl *D) {}
+
/// Callback invoked when an MSInheritanceAttr has been attached to a
/// CXXRecordDecl.
virtual void AssignInheritanceModel(CXXRecordDecl *RD) {}
diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td
index fbcbf0ed416416..884c4147cf1285 100644
--- a/clang/include/clang/Basic/Attr.td
+++ b/clang/include/clang/Basic/Attr.td
@@ -4520,6 +4520,12 @@ def ReleaseHandle : InheritableParamAttr {
let Documentation = [ReleaseHandleDocs];
}
+def zOSExport : InheritableAttr {
+ let Spellings = [CustomKeyword<"_Export">];
+ let Subjects = SubjectList<[Function, Var, CXXRecord]>;
+ let Documentation = [zOSExportDocs];
+}
+
def UnsafeBufferUsage : InheritableAttr {
let Spellings = [Clang<"unsafe_buffer_usage">];
let Subjects = SubjectList<[Function, Field]>;
diff --git a/clang/include/clang/Basic/AttrDocs.td b/clang/include/clang/Basic/AttrDocs.td
index b1512e22ee2dd4..88bd8e605adf6c 100644
--- a/clang/include/clang/Basic/AttrDocs.td
+++ b/clang/include/clang/Basic/AttrDocs.td
@@ -6863,6 +6863,27 @@ attribute requires a string literal argument to identify the handle being releas
}];
}
+def zOSExportDocs : Documentation {
+ let Category = DocCatFunction;
+ let Content = [{
+Use the _Export keyword with a function name or external variable to declare
+that it is to be exported (made available to other modules). You must define
+the object name in the same translation unit in which you use the _Export
+keyword. For example:
+
+.. code-block:: c
+
+ int _Export anthony(float);
+
+This statement exports the function anthony, if you define the function in the
+translation unit. The _Export keyword must immediately precede the object name.
+If you apply the _Export keyword to a class, the compiler automatically exports
+all static data members and member functions of the class. However, if you want
+it to apply to individual class members, then you must apply it to each member
+that can be referenced.
+ }];
+}
+
def UnsafeBufferUsageDocs : Documentation {
let Category = DocCatFunction;
let Content = [{
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index dc84110ef78211..aaf547de1a48bd 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -1129,6 +1129,13 @@ def err_pragma_pop_visibility_mismatch : Error<
"#pragma visibility pop with no matching #pragma visibility push">;
def note_surrounding_namespace_starts_here : Note<
"surrounding namespace with visibility attribute starts here">;
+def warn_failed_to_resolve_pragma : Warning<
+ "failed to resolve '#pragma %0' to a declaration">,
+ InGroup<IgnoredPragmas>;
+def warn_pragma_not_applied : Warning<
+ "#pragma %0 is applicable to symbols with external linkage only; "
+ "not applied to %1">,
+ InGroup<IgnoredPragmas>;
def err_pragma_loop_invalid_argument_type : Error<
"invalid argument of type %0; expected an integer type">;
def err_pragma_loop_compatibility : Error<
diff --git a/clang/include/clang/Basic/TokenKinds.def b/clang/include/clang/Basic/TokenKinds.def
index c5c3838407cf48..afef3b84a6985f 100644
--- a/clang/include/clang/Basic/TokenKinds.def
+++ b/clang/include/clang/Basic/TokenKinds.def
@@ -345,6 +345,8 @@ KEYWORD(__func__ , KEYALL)
KEYWORD(__objc_yes , KEYALL)
KEYWORD(__objc_no , KEYALL)
+// z/OS specific keywords
+KEYWORD(_Export , KEYZOS)
// C++ 2.11p1: Keywords.
KEYWORD(asm , KEYCXX|KEYGNU)
@@ -1003,6 +1005,9 @@ PRAGMA_ANNOTATION(pragma_fp)
// Annotation for the attribute pragma directives - #pragma clang attribute ...
PRAGMA_ANNOTATION(pragma_attribute)
+// Annotation for C/C++ #pragma export(ident)
+PRAGMA_ANNOTATION(pragma_export)
+
// Annotation for the riscv pragma directives - #pragma clang riscv intrinsic ...
PRAGMA_ANNOTATION(pragma_riscv)
diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h
index eb8a851da7e04e..eee7009b8615a7 100644
--- a/clang/include/clang/Parse/Parser.h
+++ b/clang/include/clang/Parse/Parser.h
@@ -220,6 +220,7 @@ class Parser : public CodeCompletionHandler {
std::unique_ptr<PragmaHandler> AttributePragmaHandler;
std::unique_ptr<PragmaHandler> MaxTokensHerePragmaHandler;
std::unique_ptr<PragmaHandler> MaxTokensTotalPragmaHandler;
+ std::unique_ptr<PragmaHandler> ExportHandler;
std::unique_ptr<PragmaHandler> RISCVPragmaHandler;
std::unique_ptr<CommentHandler> CommentSemaHandler;
@@ -854,6 +855,18 @@ class Parser : public CodeCompletionHandler {
void HandlePragmaAttribute();
+ /// Helper functions for handling zOS pragmas.
+ NestedNameSpecifier *zOSParseIdentifier(StringRef PragmaName,
+ IdentifierInfo *IdentName);
+ bool zOSParseParameterList(StringRef PragmaName,
+ std::optional<SmallVector<QualType, 4>> &TypeList,
+ Qualifiers &CVQual);
+ bool zOSHandlePragmaHelper(tok::TokenKind);
+
+ /// Handle the annotation token produced for
+ /// #pragma export ...
+ void HandlePragmaExport();
+
/// GetLookAheadToken - This peeks ahead N tokens and returns that token
/// without consuming any tokens. LookAhead(0) returns 'Tok', LookAhead(1)
/// returns the token after Tok, etc.
diff --git a/clang/include/clang/Sema/DeclSpec.h b/clang/include/clang/Sema/DeclSpec.h
index 06243f2624876f..d03a6b11d26ab8 100644
--- a/clang/include/clang/Sema/DeclSpec.h
+++ b/clang/include/clang/Sema/DeclSpec.h
@@ -398,6 +398,8 @@ class DeclSpec {
unsigned FS_virtual_specified : 1;
LLVM_PREFERRED_TYPE(bool)
unsigned FS_noreturn_specified : 1;
+ LLVM_PREFERRED_TYPE(bool)
+ unsigned export_specified : 1;
// friend-specifier
LLVM_PREFERRED_TYPE(bool)
@@ -444,6 +446,7 @@ class DeclSpec {
SourceLocation FS_forceinlineLoc;
SourceLocation FriendLoc, ModulePrivateLoc, ConstexprLoc;
SourceLocation TQ_pipeLoc;
+ SourceLocation exportLoc;
WrittenBuiltinSpecs writtenBS;
void SaveWrittenBuiltinSpecs();
@@ -492,7 +495,8 @@ class DeclSpec {
TypeSpecPipe(false), TypeSpecSat(false), ConstrainedAuto(false),
TypeQualifiers(TQ_unspecified), FS_inline_specified(false),
FS_forceinline_specified(false), FS_virtual_specified(false),
- FS_noreturn_specified(false), FriendSpecifiedFirst(false),
+ FS_noreturn_specified(false), export_specified(false),
+ FriendSpecifiedFirst(false),
ConstexprSpecifier(
static_cast<unsigned>(ConstexprSpecKind::Unspecified)),
Attrs(attrFactory), writtenBS(), ObjCQualifiers(nullptr) {}
@@ -661,7 +665,10 @@ class DeclSpec {
bool isNoreturnSpecified() const { return FS_noreturn_specified; }
SourceLocation getNoreturnSpecLoc() const { return FS_noreturnLoc; }
- void ClearFunctionSpecs() {
+ bool isExportSpecified() const { return export_specified; }
+ SourceLocation getExportSpecLoc() const { return exportLoc; }
+
+ void ClearFunctionSpecs() {
FS_inline_specified = false;
FS_inlineLoc = SourceLocation();
FS_forceinline_specified = false;
@@ -811,6 +818,8 @@ class DeclSpec {
bool setFunctionSpecNoreturn(SourceLocation Loc, const char *&PrevSpec,
unsigned &DiagID);
+ bool setExportSpec(SourceLocation Loc);
+
bool SetFriendSpec(SourceLocation Loc, const char *&PrevSpec,
unsigned &DiagID);
bool setModulePrivateSpec(SourceLocation Loc, const char *&PrevSpec,
@@ -1955,6 +1964,10 @@ class Declarator {
LLVM_PREFERRED_TYPE(bool)
unsigned InlineStorageUsed : 1;
+ /// Indicates whether this is set as _Export
+ LLVM_PREFERRED_TYPE(bool)
+ unsigned ExportSpecified : 1;
+
/// Indicates whether this declarator has an initializer.
LLVM_PREFERRED_TYPE(bool)
unsigned HasInitializer : 1;
@@ -2001,6 +2014,9 @@ class Declarator {
/// this declarator as a parameter pack.
SourceLocation EllipsisLoc;
+ /// The source location of the _Export keyword on this declarator
+ SourceLocation ExportLoc;
+
Expr *PackIndexingExpr;
friend struct DeclaratorChunk;
@@ -2030,7 +2046,8 @@ class Declarator {
FunctionDefinitionKind::Declaration)),
Redeclaration(false), Extension(false), ObjCIvar(false),
ObjCWeakProperty(false), InlineStorageUsed(false),
- HasInitializer(false), Attrs(DS.getAttributePool().getFactory()),
+ ExportSpecified(false), HasInitializer(false),
+ Attrs(DS.getAttributePool().getFactory()),
DeclarationAttrs(DeclarationAttrs), AsmLabel(nullptr),
TrailingRequiresClause(nullptr),
InventedTemplateParameterList(nullptr) {
@@ -2109,6 +2126,18 @@ class Declarator {
Range.setEnd(SR.getEnd());
}
+ /// Set this declarator as _Export
+ void SetExport(SourceLocation Loc) {
+ ExportSpecified = true;
+ ExportLoc = Loc;
+ }
+
+ /// Whether this declarator is marked as _Export
+ bool IsExport() const { return ExportSpecified; }
+
+ /// Get the location of the _Export keyword
+ SourceLocation getExportLoc() const { return ExportLoc; }
+
/// Reset the contents of this Declarator.
void clear() {
SS.clear();
@@ -2125,6 +2154,7 @@ class Declarator {
HasInitializer = false;
ObjCIvar = false;
ObjCWeakProperty = false;
+ ExportSpecified = false;
CommaLoc = SourceLocation();
EllipsisLoc = SourceLocation();
PackIndexingExpr = nullptr;
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index bede971ce0191b..44bc9bcc22d4b0 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -1906,6 +1906,36 @@ class Sema final : public SemaBase {
ActOnPragmaMSFunction(SourceLocation Loc,
const llvm::SmallVectorImpl<StringRef> &NoBuiltins);
+ /// A label from a C++ #pragma export, for a symbol that we
+ /// haven't seen the declaration for yet. The TypeList is the argument list
+ /// the function must match if HasTypeList is true.
+ struct SymbolLabel {
+ std::optional<SmallVector<QualType, 4>> TypeList;
+ StringRef MappedName;
+ SourceLocation NameLoc;
+ bool HasTypeList;
+ Qualifiers CVQual;
+ };
+
+ typedef SmallVector<SymbolLabel, 1> PendingSymbolOverloads;
+ typedef llvm::DenseMap<NestedNameSpecifier *, PendingSymbolOverloads>
+ SymbolNames;
+ SymbolNames PendingExportNames;
+
+ FunctionDecl *tryFunctionLookUp(NestedNameSpecifier *NestedName,
+ SourceLocation NameLoc);
+
+ /// trySymbolLookUp try to look up a decl matching the nested specifier
+ /// with optional type list.
+ NamedDecl *trySymbolLookUp(NestedNameSpecifier *NestedName,
+ const clang::Sema::SymbolLabel &Label);
+
+ /// ActonPragmaExport - called on well-formed '\#pragma export'.
+ void ActOnPragmaExport(NestedNameSpecifier *NestedId,
+ SourceLocation ExportNameLoc,
+ std::optional<SmallVector<QualType, 4>> &&TypeList,
+ Qualifiers CVQual);
+
/// Only called on function definitions; if there is a pragma in scope
/// with the effect of a range-based optnone, consider marking the function
/// with attribute optnone.
diff --git a/clang/lib/CodeGen/BackendConsumer.h b/clang/lib/CodeGen/BackendConsumer.h
index a023d29cbd1d73..48ae73b4d25dfe 100644
--- a/clang/lib/CodeGen/BackendConsumer.h
+++ b/clang/lib/CodeGen/BackendConsumer.h
@@ -108,6 +108,7 @@ class BackendConsumer : public ASTConsumer {
void HandleTagDeclRequiredDefinition(const TagDecl *D) override;
void CompleteTentativeDefinition(VarDecl *D) override;
void CompleteExternalDeclaration(DeclaratorDecl *D) override;
+ void CompletePragmaExport(Decl *D) override;
void AssignInheritanceModel(CXXRecordDecl *RD) override;
void HandleVTable(CXXRecordDecl *RD) override;
diff --git a/clang/lib/CodeGen/CodeGenAction.cpp b/clang/lib/CodeGen/CodeGenAction.cpp
index c9f9b688d0d8a2..830f605fb6ad78 100644
--- a/clang/lib/CodeGen/CodeGenAction.cpp
+++ b/clang/lib/CodeGen/CodeGenAction.cpp
@@ -380,6 +380,10 @@ void BackendConsumer::CompleteExternalDeclaration(DeclaratorDecl *D) {
Gen->CompleteExternalDeclaration(D);
}
+void BackendConsumer::CompletePragmaExport(Decl *D) {
+ Gen->CompletePragmaExport(D);
+}
+
void BackendConsumer::AssignInheritanceModel(CXXRecordDecl *RD) {
Gen->AssignInheritanceModel(RD);
}
diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp
index 25c1c496a4f27f..599ec5bab83bce 100644
--- a/clang/lib/CodeGen/CodeGenModule.cpp
+++ b/clang/lib/CodeGen/CodeGenModule.cpp
@@ -5275,6 +5275,21 @@ void CodeGenModule::EmitExternalDeclaration(const DeclaratorDecl *D) {
EmitExternalFunctionDeclaration(FD);
}
+void CodeGenModule::EmitPragmaExport(const Decl *D) {
+ StringRef MangledName;
+ if (auto FD = dyn_cast<FunctionDecl>(D))
+ MangledName = getMangledName(GlobalDecl(FD));
+ else if (auto VD = dyn_cast<VarDecl>(D))
+ MangledName = getMangledName(GlobalDecl(VD));
+ else
+ assert(0 && "Unsupported pragma export Decl type");
+
+ if (llvm::GlobalValue *GV = GetGlobalValue(MangledName)) {
+ GV->setVisibility(llvm::GlobalValue::DefaultVisibility);
+ GV->setDSOLocal(false);
+ }
+}
+
CharUnits CodeGenModule::GetTargetTypeStoreSize(llvm::Type *Ty) const {
return Context.toCharUnitsFromBits(
getDataLayout().getTypeStoreSizeInBits(Ty));
diff --git a/clang/lib/CodeGen/CodeGenModule.h b/clang/lib/CodeGen/CodeGenModule.h
index c58bb88035ca8a..2e9a0617491f60 100644
--- a/clang/lib/CodeGen/CodeGenModule.h
+++ b/clang/lib/CodeGen/CodeGenModule.h
@@ -1364,6 +1364,8 @@ class CodeGenModule : public CodeGenTypeCache {
void EmitExternalDeclaration(const DeclaratorDecl *D);
+ void EmitPragmaExport(const Decl *D);
+
void EmitVTable(CXXRecordDecl *Class);
void RefreshTypeCacheForClass(const CXXRecordDecl *Class);
diff --git a/clang/lib/CodeGen/ModuleBuilder.cpp b/clang/lib/CodeGen/ModuleBuilder.cpp
index d4e0ab0339a8b0..7658d97af01840 100644
--- a/clang/lib/CodeGen/ModuleBuilder.cpp
+++ b/clang/lib/CodeGen/ModuleBuilder.cpp
@@ -314,7 +314,11 @@ namespace {
Builder->EmitExternalDeclaration(D);
}
- void HandleVTable(CXXRecordDecl *RD) override {
+ void CompletePragmaExport(Decl *D) override {
+ Builder->EmitPragmaExport(D);
+ }
+
+ void HandleVTable(CXXRecordDecl *RD) override {
if (Diags.hasUnrecoverableErrorOccurred())
return;
diff --git a/clang/lib/Driver/ToolChains/ZOS.cpp b/clang/lib/Driver/ToolChains/ZOS.cpp
index 074e0556ecd2ad..a1e7ddbe389080 100644
--- a/clang/lib/Driver/ToolChains/ZOS.cpp
+++ b/clang/lib/Driver/ToolChains/ZOS.cpp
@@ -37,6 +37,11 @@ void ZOS::addClangTargetOptions(const ArgList &DriverArgs,
options::OPT_fno_aligned_allocation))
CC1Args.push_back("-faligned-alloc-unavailable");
+ if (!DriverArgs.hasArg(options::OPT_fvisibility_EQ,
+ options::OPT_fvisibility_ms_compat)) {
+ CC1Args.push_back("-fvisibility=hidden");
+ }
+
// Pass "-fno-sized-deallocation" only when the user hasn't manually enabled
// or disabled sized deallocations.
if (!DriverArgs.hasArgNoClaim(options::OPT_fsized_deallocation,
@@ -149,11 +154,10 @@ void zos::Linker::ConstructJob(Compilation &C, const JobAction &JA,
StringRef OutputName = Output.getFilename();
// Strip away the last file suffix in presence from output name and add
// a new .x suffix.
- size_t Suffix = OutputName.find_last_of('.');
- const char *SideDeckName =
- Args.MakeArgString(OutputName.substr(0, Suffix) + ".x");
+ SmallString<128> SideDeckName = OutputName;
+ llvm::sys::path::replace_extension(SideDeckName, "x");
CmdArgs.push_back("-x");
- CmdArgs.push_back(SideDeckName);
+ CmdArgs.push_back(Args.MakeArgString(SideDeckName));
} else {
// We need to direct side file to /dev/null to suppress linker warning when
// the object file contains exported symbols, and -shared or
diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp
index 122a05be1c039a..680e029019b8e6 100644
--- a/clang/lib/Parse/ParseDecl.cpp
+++ b/clang/lib/Parse/ParseDecl.cpp
@@ -4449,6 +4449,12 @@ void Parser::ParseDeclarationSpecifiers(
isInvalid = DS.setFunctionSpecNoreturn(Loc, PrevSpec, DiagID);
break;
+ case tok::kw__Export:
+ // If we find kw__Export, it is being applied to a var or function
+ // This will be handled in ParseDeclaratorInternal()
+ goto DoneWithDeclSpec;
+ break;
+
// friend
case tok::kw_friend:
if (DSContext == DeclSpecContext::DSC_class) {
@@ -6174,6 +6180,7 @@ bool Parser::isDeclarationSpecifier(
case tok::kw_virtual:
case tok::kw_explicit:
case tok::kw__Noreturn:
+ case tok::kw__Export:
// alignment-specifier
case tok::kw__Alignas:
@@ -6765,6 +6772,17 @@ void Parser::ParseDeclaratorInternal(Declarator &D,
tok::TokenKind Kind = Tok.getKind();
+ // If this variable or function is marked as _Export, set the bit
+ if (Kind == tok::kw__Export) {
+ SourceLocation loc = ConsumeToken();
+ D.SetExport(loc);
+ D.SetRangeEnd(loc);
+
+ if (DirectDeclParser)
+ (this->*DirectDeclParser)(D);
+ return;
+ }
+
if (D.getDeclSpec().isTypeSpecPipe() && !isPipeDeclarator(D)) {
DeclSpec DS(AttrFactory);
ParseTypeQualifierListOpt(DS);
diff --git a/clang/lib/Parse/ParseDeclCXX.cpp b/clang/lib/Parse/ParseDeclCXX.cpp
index 6f0f5a0311bc18..5be80011d92bb2 100644
--- a/clang/lib/Parse/ParseDeclCXX.cpp
+++ b/clang/lib/Parse/ParseDeclCXX.cpp
@@ -1751,6 +1751,12 @@ void Parser::ParseClassSpecifier(tok::TokenKind TagTokKind,
// If attributes exist after tag, parse them.
for (;;) {
MaybeParseAttributes(PAKM_CXX11 | PAKM_Declspec | PAKM_GNU, attrs);
+ // If the token is _Export, set the bits
+ if (Tok.is(tok::kw__Export)) {
+ SourceLocation loc = ConsumeToken();
+ DS.setExportSpec(loc);
+ continue;
+ }
// Parse inheritance specifiers.
if (Tok.isOneOf(tok::kw___single_inheritance,
tok::kw___multiple_inheritance,
diff --git a/clang/lib/Parse/ParsePragma.cpp b/clang/lib/Parse/ParsePragma.cpp
index cc6f18b5b319f9..af892115f1f8dd 100644
--- a/clang/lib/Parse/ParsePragma.cpp
+++ b/clang/lib/Parse/ParsePragma.cpp
@@ -401,6 +401,12 @@ struct PragmaMaxTokensTotalHandler : public PragmaHandler {
Token &FirstToken) override;
};
+struct PragmaExportHandler : public PragmaHandler {
+ explicit PragmaExportHandler() : PragmaHandler("export") {}
+ void HandlePragma(Preprocessor &PP, PragmaIntroducer Introducer,
+ Token &FirstToken) override;
+};
+
struct PragmaRISCVHandler : public PragmaHandler {
PragmaRISCVHandler(Sema &Actions)
: PragmaHandler("riscv"), Actions(Actions) {}
@@ -564,6 +570,11 @@ void Parser::initializePragmaHandlers() {
MaxTokensTotalPragmaHandler = std::make_unique<PragmaMaxTokensTotalHandler>();
PP.AddPragmaHandler("clang", MaxTokensTotalPragmaHandler.get());
+ if (getLangOpts().ZOSExt) {
+ ExportHandler = std::make_unique<PragmaExportHandler>();
+ PP.AddPragmaHandler(ExportHandler.get());
+ }
+
if (getTargetInfo().getTriple().isRISCV()) {
RISCVPragmaHandler = std::make_unique<PragmaRISCVHandler>(Actions);
PP.AddPragmaHandler("clang", RISCVPragmaHandler.get());
@@ -698,6 +709,11 @@ void Parser::resetPragmaHandlers() {
PP.RemovePragmaHandler("clang", MaxTokensTotalPragmaHandler.get());
MaxTokensTotalPragmaHandler.reset();
+ if (getLangOpts().ZOSExt) {
+ PP.RemovePragmaHandler(ExportHandler.get());
+ ExportHandler.reset();
+ }
+
if (getTargetInfo().getTriple().isRISCV()) {
PP....
[truncated]
|
@llvm/pr-subscribers-backend-systemz Author: Sean Perry (perry-ca) ChangesThe 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:
Note: Patch is 44.65 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/111035.diff 28 Files Affected:
diff --git a/clang/include/clang/AST/ASTConsumer.h b/clang/include/clang/AST/ASTConsumer.h
index 447f2592d23595..b15d53e700c679 100644
--- a/clang/include/clang/AST/ASTConsumer.h
+++ b/clang/include/clang/AST/ASTConsumer.h
@@ -108,6 +108,9 @@ class ASTConsumer {
/// completed.
virtual void CompleteExternalDeclaration(DeclaratorDecl *D) {}
+ /// CompletePragmaExport - complete #pragma export statements.
+ virtual void CompletePragmaExport(Decl *D) {}
+
/// Callback invoked when an MSInheritanceAttr has been attached to a
/// CXXRecordDecl.
virtual void AssignInheritanceModel(CXXRecordDecl *RD) {}
diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td
index fbcbf0ed416416..884c4147cf1285 100644
--- a/clang/include/clang/Basic/Attr.td
+++ b/clang/include/clang/Basic/Attr.td
@@ -4520,6 +4520,12 @@ def ReleaseHandle : InheritableParamAttr {
let Documentation = [ReleaseHandleDocs];
}
+def zOSExport : InheritableAttr {
+ let Spellings = [CustomKeyword<"_Export">];
+ let Subjects = SubjectList<[Function, Var, CXXRecord]>;
+ let Documentation = [zOSExportDocs];
+}
+
def UnsafeBufferUsage : InheritableAttr {
let Spellings = [Clang<"unsafe_buffer_usage">];
let Subjects = SubjectList<[Function, Field]>;
diff --git a/clang/include/clang/Basic/AttrDocs.td b/clang/include/clang/Basic/AttrDocs.td
index b1512e22ee2dd4..88bd8e605adf6c 100644
--- a/clang/include/clang/Basic/AttrDocs.td
+++ b/clang/include/clang/Basic/AttrDocs.td
@@ -6863,6 +6863,27 @@ attribute requires a string literal argument to identify the handle being releas
}];
}
+def zOSExportDocs : Documentation {
+ let Category = DocCatFunction;
+ let Content = [{
+Use the _Export keyword with a function name or external variable to declare
+that it is to be exported (made available to other modules). You must define
+the object name in the same translation unit in which you use the _Export
+keyword. For example:
+
+.. code-block:: c
+
+ int _Export anthony(float);
+
+This statement exports the function anthony, if you define the function in the
+translation unit. The _Export keyword must immediately precede the object name.
+If you apply the _Export keyword to a class, the compiler automatically exports
+all static data members and member functions of the class. However, if you want
+it to apply to individual class members, then you must apply it to each member
+that can be referenced.
+ }];
+}
+
def UnsafeBufferUsageDocs : Documentation {
let Category = DocCatFunction;
let Content = [{
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index dc84110ef78211..aaf547de1a48bd 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -1129,6 +1129,13 @@ def err_pragma_pop_visibility_mismatch : Error<
"#pragma visibility pop with no matching #pragma visibility push">;
def note_surrounding_namespace_starts_here : Note<
"surrounding namespace with visibility attribute starts here">;
+def warn_failed_to_resolve_pragma : Warning<
+ "failed to resolve '#pragma %0' to a declaration">,
+ InGroup<IgnoredPragmas>;
+def warn_pragma_not_applied : Warning<
+ "#pragma %0 is applicable to symbols with external linkage only; "
+ "not applied to %1">,
+ InGroup<IgnoredPragmas>;
def err_pragma_loop_invalid_argument_type : Error<
"invalid argument of type %0; expected an integer type">;
def err_pragma_loop_compatibility : Error<
diff --git a/clang/include/clang/Basic/TokenKinds.def b/clang/include/clang/Basic/TokenKinds.def
index c5c3838407cf48..afef3b84a6985f 100644
--- a/clang/include/clang/Basic/TokenKinds.def
+++ b/clang/include/clang/Basic/TokenKinds.def
@@ -345,6 +345,8 @@ KEYWORD(__func__ , KEYALL)
KEYWORD(__objc_yes , KEYALL)
KEYWORD(__objc_no , KEYALL)
+// z/OS specific keywords
+KEYWORD(_Export , KEYZOS)
// C++ 2.11p1: Keywords.
KEYWORD(asm , KEYCXX|KEYGNU)
@@ -1003,6 +1005,9 @@ PRAGMA_ANNOTATION(pragma_fp)
// Annotation for the attribute pragma directives - #pragma clang attribute ...
PRAGMA_ANNOTATION(pragma_attribute)
+// Annotation for C/C++ #pragma export(ident)
+PRAGMA_ANNOTATION(pragma_export)
+
// Annotation for the riscv pragma directives - #pragma clang riscv intrinsic ...
PRAGMA_ANNOTATION(pragma_riscv)
diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h
index eb8a851da7e04e..eee7009b8615a7 100644
--- a/clang/include/clang/Parse/Parser.h
+++ b/clang/include/clang/Parse/Parser.h
@@ -220,6 +220,7 @@ class Parser : public CodeCompletionHandler {
std::unique_ptr<PragmaHandler> AttributePragmaHandler;
std::unique_ptr<PragmaHandler> MaxTokensHerePragmaHandler;
std::unique_ptr<PragmaHandler> MaxTokensTotalPragmaHandler;
+ std::unique_ptr<PragmaHandler> ExportHandler;
std::unique_ptr<PragmaHandler> RISCVPragmaHandler;
std::unique_ptr<CommentHandler> CommentSemaHandler;
@@ -854,6 +855,18 @@ class Parser : public CodeCompletionHandler {
void HandlePragmaAttribute();
+ /// Helper functions for handling zOS pragmas.
+ NestedNameSpecifier *zOSParseIdentifier(StringRef PragmaName,
+ IdentifierInfo *IdentName);
+ bool zOSParseParameterList(StringRef PragmaName,
+ std::optional<SmallVector<QualType, 4>> &TypeList,
+ Qualifiers &CVQual);
+ bool zOSHandlePragmaHelper(tok::TokenKind);
+
+ /// Handle the annotation token produced for
+ /// #pragma export ...
+ void HandlePragmaExport();
+
/// GetLookAheadToken - This peeks ahead N tokens and returns that token
/// without consuming any tokens. LookAhead(0) returns 'Tok', LookAhead(1)
/// returns the token after Tok, etc.
diff --git a/clang/include/clang/Sema/DeclSpec.h b/clang/include/clang/Sema/DeclSpec.h
index 06243f2624876f..d03a6b11d26ab8 100644
--- a/clang/include/clang/Sema/DeclSpec.h
+++ b/clang/include/clang/Sema/DeclSpec.h
@@ -398,6 +398,8 @@ class DeclSpec {
unsigned FS_virtual_specified : 1;
LLVM_PREFERRED_TYPE(bool)
unsigned FS_noreturn_specified : 1;
+ LLVM_PREFERRED_TYPE(bool)
+ unsigned export_specified : 1;
// friend-specifier
LLVM_PREFERRED_TYPE(bool)
@@ -444,6 +446,7 @@ class DeclSpec {
SourceLocation FS_forceinlineLoc;
SourceLocation FriendLoc, ModulePrivateLoc, ConstexprLoc;
SourceLocation TQ_pipeLoc;
+ SourceLocation exportLoc;
WrittenBuiltinSpecs writtenBS;
void SaveWrittenBuiltinSpecs();
@@ -492,7 +495,8 @@ class DeclSpec {
TypeSpecPipe(false), TypeSpecSat(false), ConstrainedAuto(false),
TypeQualifiers(TQ_unspecified), FS_inline_specified(false),
FS_forceinline_specified(false), FS_virtual_specified(false),
- FS_noreturn_specified(false), FriendSpecifiedFirst(false),
+ FS_noreturn_specified(false), export_specified(false),
+ FriendSpecifiedFirst(false),
ConstexprSpecifier(
static_cast<unsigned>(ConstexprSpecKind::Unspecified)),
Attrs(attrFactory), writtenBS(), ObjCQualifiers(nullptr) {}
@@ -661,7 +665,10 @@ class DeclSpec {
bool isNoreturnSpecified() const { return FS_noreturn_specified; }
SourceLocation getNoreturnSpecLoc() const { return FS_noreturnLoc; }
- void ClearFunctionSpecs() {
+ bool isExportSpecified() const { return export_specified; }
+ SourceLocation getExportSpecLoc() const { return exportLoc; }
+
+ void ClearFunctionSpecs() {
FS_inline_specified = false;
FS_inlineLoc = SourceLocation();
FS_forceinline_specified = false;
@@ -811,6 +818,8 @@ class DeclSpec {
bool setFunctionSpecNoreturn(SourceLocation Loc, const char *&PrevSpec,
unsigned &DiagID);
+ bool setExportSpec(SourceLocation Loc);
+
bool SetFriendSpec(SourceLocation Loc, const char *&PrevSpec,
unsigned &DiagID);
bool setModulePrivateSpec(SourceLocation Loc, const char *&PrevSpec,
@@ -1955,6 +1964,10 @@ class Declarator {
LLVM_PREFERRED_TYPE(bool)
unsigned InlineStorageUsed : 1;
+ /// Indicates whether this is set as _Export
+ LLVM_PREFERRED_TYPE(bool)
+ unsigned ExportSpecified : 1;
+
/// Indicates whether this declarator has an initializer.
LLVM_PREFERRED_TYPE(bool)
unsigned HasInitializer : 1;
@@ -2001,6 +2014,9 @@ class Declarator {
/// this declarator as a parameter pack.
SourceLocation EllipsisLoc;
+ /// The source location of the _Export keyword on this declarator
+ SourceLocation ExportLoc;
+
Expr *PackIndexingExpr;
friend struct DeclaratorChunk;
@@ -2030,7 +2046,8 @@ class Declarator {
FunctionDefinitionKind::Declaration)),
Redeclaration(false), Extension(false), ObjCIvar(false),
ObjCWeakProperty(false), InlineStorageUsed(false),
- HasInitializer(false), Attrs(DS.getAttributePool().getFactory()),
+ ExportSpecified(false), HasInitializer(false),
+ Attrs(DS.getAttributePool().getFactory()),
DeclarationAttrs(DeclarationAttrs), AsmLabel(nullptr),
TrailingRequiresClause(nullptr),
InventedTemplateParameterList(nullptr) {
@@ -2109,6 +2126,18 @@ class Declarator {
Range.setEnd(SR.getEnd());
}
+ /// Set this declarator as _Export
+ void SetExport(SourceLocation Loc) {
+ ExportSpecified = true;
+ ExportLoc = Loc;
+ }
+
+ /// Whether this declarator is marked as _Export
+ bool IsExport() const { return ExportSpecified; }
+
+ /// Get the location of the _Export keyword
+ SourceLocation getExportLoc() const { return ExportLoc; }
+
/// Reset the contents of this Declarator.
void clear() {
SS.clear();
@@ -2125,6 +2154,7 @@ class Declarator {
HasInitializer = false;
ObjCIvar = false;
ObjCWeakProperty = false;
+ ExportSpecified = false;
CommaLoc = SourceLocation();
EllipsisLoc = SourceLocation();
PackIndexingExpr = nullptr;
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index bede971ce0191b..44bc9bcc22d4b0 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -1906,6 +1906,36 @@ class Sema final : public SemaBase {
ActOnPragmaMSFunction(SourceLocation Loc,
const llvm::SmallVectorImpl<StringRef> &NoBuiltins);
+ /// A label from a C++ #pragma export, for a symbol that we
+ /// haven't seen the declaration for yet. The TypeList is the argument list
+ /// the function must match if HasTypeList is true.
+ struct SymbolLabel {
+ std::optional<SmallVector<QualType, 4>> TypeList;
+ StringRef MappedName;
+ SourceLocation NameLoc;
+ bool HasTypeList;
+ Qualifiers CVQual;
+ };
+
+ typedef SmallVector<SymbolLabel, 1> PendingSymbolOverloads;
+ typedef llvm::DenseMap<NestedNameSpecifier *, PendingSymbolOverloads>
+ SymbolNames;
+ SymbolNames PendingExportNames;
+
+ FunctionDecl *tryFunctionLookUp(NestedNameSpecifier *NestedName,
+ SourceLocation NameLoc);
+
+ /// trySymbolLookUp try to look up a decl matching the nested specifier
+ /// with optional type list.
+ NamedDecl *trySymbolLookUp(NestedNameSpecifier *NestedName,
+ const clang::Sema::SymbolLabel &Label);
+
+ /// ActonPragmaExport - called on well-formed '\#pragma export'.
+ void ActOnPragmaExport(NestedNameSpecifier *NestedId,
+ SourceLocation ExportNameLoc,
+ std::optional<SmallVector<QualType, 4>> &&TypeList,
+ Qualifiers CVQual);
+
/// Only called on function definitions; if there is a pragma in scope
/// with the effect of a range-based optnone, consider marking the function
/// with attribute optnone.
diff --git a/clang/lib/CodeGen/BackendConsumer.h b/clang/lib/CodeGen/BackendConsumer.h
index a023d29cbd1d73..48ae73b4d25dfe 100644
--- a/clang/lib/CodeGen/BackendConsumer.h
+++ b/clang/lib/CodeGen/BackendConsumer.h
@@ -108,6 +108,7 @@ class BackendConsumer : public ASTConsumer {
void HandleTagDeclRequiredDefinition(const TagDecl *D) override;
void CompleteTentativeDefinition(VarDecl *D) override;
void CompleteExternalDeclaration(DeclaratorDecl *D) override;
+ void CompletePragmaExport(Decl *D) override;
void AssignInheritanceModel(CXXRecordDecl *RD) override;
void HandleVTable(CXXRecordDecl *RD) override;
diff --git a/clang/lib/CodeGen/CodeGenAction.cpp b/clang/lib/CodeGen/CodeGenAction.cpp
index c9f9b688d0d8a2..830f605fb6ad78 100644
--- a/clang/lib/CodeGen/CodeGenAction.cpp
+++ b/clang/lib/CodeGen/CodeGenAction.cpp
@@ -380,6 +380,10 @@ void BackendConsumer::CompleteExternalDeclaration(DeclaratorDecl *D) {
Gen->CompleteExternalDeclaration(D);
}
+void BackendConsumer::CompletePragmaExport(Decl *D) {
+ Gen->CompletePragmaExport(D);
+}
+
void BackendConsumer::AssignInheritanceModel(CXXRecordDecl *RD) {
Gen->AssignInheritanceModel(RD);
}
diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp
index 25c1c496a4f27f..599ec5bab83bce 100644
--- a/clang/lib/CodeGen/CodeGenModule.cpp
+++ b/clang/lib/CodeGen/CodeGenModule.cpp
@@ -5275,6 +5275,21 @@ void CodeGenModule::EmitExternalDeclaration(const DeclaratorDecl *D) {
EmitExternalFunctionDeclaration(FD);
}
+void CodeGenModule::EmitPragmaExport(const Decl *D) {
+ StringRef MangledName;
+ if (auto FD = dyn_cast<FunctionDecl>(D))
+ MangledName = getMangledName(GlobalDecl(FD));
+ else if (auto VD = dyn_cast<VarDecl>(D))
+ MangledName = getMangledName(GlobalDecl(VD));
+ else
+ assert(0 && "Unsupported pragma export Decl type");
+
+ if (llvm::GlobalValue *GV = GetGlobalValue(MangledName)) {
+ GV->setVisibility(llvm::GlobalValue::DefaultVisibility);
+ GV->setDSOLocal(false);
+ }
+}
+
CharUnits CodeGenModule::GetTargetTypeStoreSize(llvm::Type *Ty) const {
return Context.toCharUnitsFromBits(
getDataLayout().getTypeStoreSizeInBits(Ty));
diff --git a/clang/lib/CodeGen/CodeGenModule.h b/clang/lib/CodeGen/CodeGenModule.h
index c58bb88035ca8a..2e9a0617491f60 100644
--- a/clang/lib/CodeGen/CodeGenModule.h
+++ b/clang/lib/CodeGen/CodeGenModule.h
@@ -1364,6 +1364,8 @@ class CodeGenModule : public CodeGenTypeCache {
void EmitExternalDeclaration(const DeclaratorDecl *D);
+ void EmitPragmaExport(const Decl *D);
+
void EmitVTable(CXXRecordDecl *Class);
void RefreshTypeCacheForClass(const CXXRecordDecl *Class);
diff --git a/clang/lib/CodeGen/ModuleBuilder.cpp b/clang/lib/CodeGen/ModuleBuilder.cpp
index d4e0ab0339a8b0..7658d97af01840 100644
--- a/clang/lib/CodeGen/ModuleBuilder.cpp
+++ b/clang/lib/CodeGen/ModuleBuilder.cpp
@@ -314,7 +314,11 @@ namespace {
Builder->EmitExternalDeclaration(D);
}
- void HandleVTable(CXXRecordDecl *RD) override {
+ void CompletePragmaExport(Decl *D) override {
+ Builder->EmitPragmaExport(D);
+ }
+
+ void HandleVTable(CXXRecordDecl *RD) override {
if (Diags.hasUnrecoverableErrorOccurred())
return;
diff --git a/clang/lib/Driver/ToolChains/ZOS.cpp b/clang/lib/Driver/ToolChains/ZOS.cpp
index 074e0556ecd2ad..a1e7ddbe389080 100644
--- a/clang/lib/Driver/ToolChains/ZOS.cpp
+++ b/clang/lib/Driver/ToolChains/ZOS.cpp
@@ -37,6 +37,11 @@ void ZOS::addClangTargetOptions(const ArgList &DriverArgs,
options::OPT_fno_aligned_allocation))
CC1Args.push_back("-faligned-alloc-unavailable");
+ if (!DriverArgs.hasArg(options::OPT_fvisibility_EQ,
+ options::OPT_fvisibility_ms_compat)) {
+ CC1Args.push_back("-fvisibility=hidden");
+ }
+
// Pass "-fno-sized-deallocation" only when the user hasn't manually enabled
// or disabled sized deallocations.
if (!DriverArgs.hasArgNoClaim(options::OPT_fsized_deallocation,
@@ -149,11 +154,10 @@ void zos::Linker::ConstructJob(Compilation &C, const JobAction &JA,
StringRef OutputName = Output.getFilename();
// Strip away the last file suffix in presence from output name and add
// a new .x suffix.
- size_t Suffix = OutputName.find_last_of('.');
- const char *SideDeckName =
- Args.MakeArgString(OutputName.substr(0, Suffix) + ".x");
+ SmallString<128> SideDeckName = OutputName;
+ llvm::sys::path::replace_extension(SideDeckName, "x");
CmdArgs.push_back("-x");
- CmdArgs.push_back(SideDeckName);
+ CmdArgs.push_back(Args.MakeArgString(SideDeckName));
} else {
// We need to direct side file to /dev/null to suppress linker warning when
// the object file contains exported symbols, and -shared or
diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp
index 122a05be1c039a..680e029019b8e6 100644
--- a/clang/lib/Parse/ParseDecl.cpp
+++ b/clang/lib/Parse/ParseDecl.cpp
@@ -4449,6 +4449,12 @@ void Parser::ParseDeclarationSpecifiers(
isInvalid = DS.setFunctionSpecNoreturn(Loc, PrevSpec, DiagID);
break;
+ case tok::kw__Export:
+ // If we find kw__Export, it is being applied to a var or function
+ // This will be handled in ParseDeclaratorInternal()
+ goto DoneWithDeclSpec;
+ break;
+
// friend
case tok::kw_friend:
if (DSContext == DeclSpecContext::DSC_class) {
@@ -6174,6 +6180,7 @@ bool Parser::isDeclarationSpecifier(
case tok::kw_virtual:
case tok::kw_explicit:
case tok::kw__Noreturn:
+ case tok::kw__Export:
// alignment-specifier
case tok::kw__Alignas:
@@ -6765,6 +6772,17 @@ void Parser::ParseDeclaratorInternal(Declarator &D,
tok::TokenKind Kind = Tok.getKind();
+ // If this variable or function is marked as _Export, set the bit
+ if (Kind == tok::kw__Export) {
+ SourceLocation loc = ConsumeToken();
+ D.SetExport(loc);
+ D.SetRangeEnd(loc);
+
+ if (DirectDeclParser)
+ (this->*DirectDeclParser)(D);
+ return;
+ }
+
if (D.getDeclSpec().isTypeSpecPipe() && !isPipeDeclarator(D)) {
DeclSpec DS(AttrFactory);
ParseTypeQualifierListOpt(DS);
diff --git a/clang/lib/Parse/ParseDeclCXX.cpp b/clang/lib/Parse/ParseDeclCXX.cpp
index 6f0f5a0311bc18..5be80011d92bb2 100644
--- a/clang/lib/Parse/ParseDeclCXX.cpp
+++ b/clang/lib/Parse/ParseDeclCXX.cpp
@@ -1751,6 +1751,12 @@ void Parser::ParseClassSpecifier(tok::TokenKind TagTokKind,
// If attributes exist after tag, parse them.
for (;;) {
MaybeParseAttributes(PAKM_CXX11 | PAKM_Declspec | PAKM_GNU, attrs);
+ // If the token is _Export, set the bits
+ if (Tok.is(tok::kw__Export)) {
+ SourceLocation loc = ConsumeToken();
+ DS.setExportSpec(loc);
+ continue;
+ }
// Parse inheritance specifiers.
if (Tok.isOneOf(tok::kw___single_inheritance,
tok::kw___multiple_inheritance,
diff --git a/clang/lib/Parse/ParsePragma.cpp b/clang/lib/Parse/ParsePragma.cpp
index cc6f18b5b319f9..af892115f1f8dd 100644
--- a/clang/lib/Parse/ParsePragma.cpp
+++ b/clang/lib/Parse/ParsePragma.cpp
@@ -401,6 +401,12 @@ struct PragmaMaxTokensTotalHandler : public PragmaHandler {
Token &FirstToken) override;
};
+struct PragmaExportHandler : public PragmaHandler {
+ explicit PragmaExportHandler() : PragmaHandler("export") {}
+ void HandlePragma(Preprocessor &PP, PragmaIntroducer Introducer,
+ Token &FirstToken) override;
+};
+
struct PragmaRISCVHandler : public PragmaHandler {
PragmaRISCVHandler(Sema &Actions)
: PragmaHandler("riscv"), Actions(Actions) {}
@@ -564,6 +570,11 @@ void Parser::initializePragmaHandlers() {
MaxTokensTotalPragmaHandler = std::make_unique<PragmaMaxTokensTotalHandler>();
PP.AddPragmaHandler("clang", MaxTokensTotalPragmaHandler.get());
+ if (getLangOpts().ZOSExt) {
+ ExportHandler = std::make_unique<PragmaExportHandler>();
+ PP.AddPragmaHandler(ExportHandler.get());
+ }
+
if (getTargetInfo().getTriple().isRISCV()) {
RISCVPragmaHandler = std::make_unique<PragmaRISCVHandler>(Actions);
PP.AddPragmaHandler("clang", RISCVPragmaHandler.get());
@@ -698,6 +709,11 @@ void Parser::resetPragmaHandlers() {
PP.RemovePragmaHandler("clang", MaxTokensTotalPragmaHandler.get());
MaxTokensTotalPragmaHandler.reset();
+ if (getLangOpts().ZOSExt) {
+ PP.RemovePragmaHandler(ExportHandler.get());
+ ExportHandler.reset();
+ }
+
if (getTargetInfo().getTriple().isRISCV()) {
PP....
[truncated]
|
@llvm/pr-subscribers-clang-codegen Author: Sean Perry (perry-ca) ChangesThe 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:
Note: Patch is 44.65 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/111035.diff 28 Files Affected:
diff --git a/clang/include/clang/AST/ASTConsumer.h b/clang/include/clang/AST/ASTConsumer.h
index 447f2592d23595..b15d53e700c679 100644
--- a/clang/include/clang/AST/ASTConsumer.h
+++ b/clang/include/clang/AST/ASTConsumer.h
@@ -108,6 +108,9 @@ class ASTConsumer {
/// completed.
virtual void CompleteExternalDeclaration(DeclaratorDecl *D) {}
+ /// CompletePragmaExport - complete #pragma export statements.
+ virtual void CompletePragmaExport(Decl *D) {}
+
/// Callback invoked when an MSInheritanceAttr has been attached to a
/// CXXRecordDecl.
virtual void AssignInheritanceModel(CXXRecordDecl *RD) {}
diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td
index fbcbf0ed416416..884c4147cf1285 100644
--- a/clang/include/clang/Basic/Attr.td
+++ b/clang/include/clang/Basic/Attr.td
@@ -4520,6 +4520,12 @@ def ReleaseHandle : InheritableParamAttr {
let Documentation = [ReleaseHandleDocs];
}
+def zOSExport : InheritableAttr {
+ let Spellings = [CustomKeyword<"_Export">];
+ let Subjects = SubjectList<[Function, Var, CXXRecord]>;
+ let Documentation = [zOSExportDocs];
+}
+
def UnsafeBufferUsage : InheritableAttr {
let Spellings = [Clang<"unsafe_buffer_usage">];
let Subjects = SubjectList<[Function, Field]>;
diff --git a/clang/include/clang/Basic/AttrDocs.td b/clang/include/clang/Basic/AttrDocs.td
index b1512e22ee2dd4..88bd8e605adf6c 100644
--- a/clang/include/clang/Basic/AttrDocs.td
+++ b/clang/include/clang/Basic/AttrDocs.td
@@ -6863,6 +6863,27 @@ attribute requires a string literal argument to identify the handle being releas
}];
}
+def zOSExportDocs : Documentation {
+ let Category = DocCatFunction;
+ let Content = [{
+Use the _Export keyword with a function name or external variable to declare
+that it is to be exported (made available to other modules). You must define
+the object name in the same translation unit in which you use the _Export
+keyword. For example:
+
+.. code-block:: c
+
+ int _Export anthony(float);
+
+This statement exports the function anthony, if you define the function in the
+translation unit. The _Export keyword must immediately precede the object name.
+If you apply the _Export keyword to a class, the compiler automatically exports
+all static data members and member functions of the class. However, if you want
+it to apply to individual class members, then you must apply it to each member
+that can be referenced.
+ }];
+}
+
def UnsafeBufferUsageDocs : Documentation {
let Category = DocCatFunction;
let Content = [{
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index dc84110ef78211..aaf547de1a48bd 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -1129,6 +1129,13 @@ def err_pragma_pop_visibility_mismatch : Error<
"#pragma visibility pop with no matching #pragma visibility push">;
def note_surrounding_namespace_starts_here : Note<
"surrounding namespace with visibility attribute starts here">;
+def warn_failed_to_resolve_pragma : Warning<
+ "failed to resolve '#pragma %0' to a declaration">,
+ InGroup<IgnoredPragmas>;
+def warn_pragma_not_applied : Warning<
+ "#pragma %0 is applicable to symbols with external linkage only; "
+ "not applied to %1">,
+ InGroup<IgnoredPragmas>;
def err_pragma_loop_invalid_argument_type : Error<
"invalid argument of type %0; expected an integer type">;
def err_pragma_loop_compatibility : Error<
diff --git a/clang/include/clang/Basic/TokenKinds.def b/clang/include/clang/Basic/TokenKinds.def
index c5c3838407cf48..afef3b84a6985f 100644
--- a/clang/include/clang/Basic/TokenKinds.def
+++ b/clang/include/clang/Basic/TokenKinds.def
@@ -345,6 +345,8 @@ KEYWORD(__func__ , KEYALL)
KEYWORD(__objc_yes , KEYALL)
KEYWORD(__objc_no , KEYALL)
+// z/OS specific keywords
+KEYWORD(_Export , KEYZOS)
// C++ 2.11p1: Keywords.
KEYWORD(asm , KEYCXX|KEYGNU)
@@ -1003,6 +1005,9 @@ PRAGMA_ANNOTATION(pragma_fp)
// Annotation for the attribute pragma directives - #pragma clang attribute ...
PRAGMA_ANNOTATION(pragma_attribute)
+// Annotation for C/C++ #pragma export(ident)
+PRAGMA_ANNOTATION(pragma_export)
+
// Annotation for the riscv pragma directives - #pragma clang riscv intrinsic ...
PRAGMA_ANNOTATION(pragma_riscv)
diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h
index eb8a851da7e04e..eee7009b8615a7 100644
--- a/clang/include/clang/Parse/Parser.h
+++ b/clang/include/clang/Parse/Parser.h
@@ -220,6 +220,7 @@ class Parser : public CodeCompletionHandler {
std::unique_ptr<PragmaHandler> AttributePragmaHandler;
std::unique_ptr<PragmaHandler> MaxTokensHerePragmaHandler;
std::unique_ptr<PragmaHandler> MaxTokensTotalPragmaHandler;
+ std::unique_ptr<PragmaHandler> ExportHandler;
std::unique_ptr<PragmaHandler> RISCVPragmaHandler;
std::unique_ptr<CommentHandler> CommentSemaHandler;
@@ -854,6 +855,18 @@ class Parser : public CodeCompletionHandler {
void HandlePragmaAttribute();
+ /// Helper functions for handling zOS pragmas.
+ NestedNameSpecifier *zOSParseIdentifier(StringRef PragmaName,
+ IdentifierInfo *IdentName);
+ bool zOSParseParameterList(StringRef PragmaName,
+ std::optional<SmallVector<QualType, 4>> &TypeList,
+ Qualifiers &CVQual);
+ bool zOSHandlePragmaHelper(tok::TokenKind);
+
+ /// Handle the annotation token produced for
+ /// #pragma export ...
+ void HandlePragmaExport();
+
/// GetLookAheadToken - This peeks ahead N tokens and returns that token
/// without consuming any tokens. LookAhead(0) returns 'Tok', LookAhead(1)
/// returns the token after Tok, etc.
diff --git a/clang/include/clang/Sema/DeclSpec.h b/clang/include/clang/Sema/DeclSpec.h
index 06243f2624876f..d03a6b11d26ab8 100644
--- a/clang/include/clang/Sema/DeclSpec.h
+++ b/clang/include/clang/Sema/DeclSpec.h
@@ -398,6 +398,8 @@ class DeclSpec {
unsigned FS_virtual_specified : 1;
LLVM_PREFERRED_TYPE(bool)
unsigned FS_noreturn_specified : 1;
+ LLVM_PREFERRED_TYPE(bool)
+ unsigned export_specified : 1;
// friend-specifier
LLVM_PREFERRED_TYPE(bool)
@@ -444,6 +446,7 @@ class DeclSpec {
SourceLocation FS_forceinlineLoc;
SourceLocation FriendLoc, ModulePrivateLoc, ConstexprLoc;
SourceLocation TQ_pipeLoc;
+ SourceLocation exportLoc;
WrittenBuiltinSpecs writtenBS;
void SaveWrittenBuiltinSpecs();
@@ -492,7 +495,8 @@ class DeclSpec {
TypeSpecPipe(false), TypeSpecSat(false), ConstrainedAuto(false),
TypeQualifiers(TQ_unspecified), FS_inline_specified(false),
FS_forceinline_specified(false), FS_virtual_specified(false),
- FS_noreturn_specified(false), FriendSpecifiedFirst(false),
+ FS_noreturn_specified(false), export_specified(false),
+ FriendSpecifiedFirst(false),
ConstexprSpecifier(
static_cast<unsigned>(ConstexprSpecKind::Unspecified)),
Attrs(attrFactory), writtenBS(), ObjCQualifiers(nullptr) {}
@@ -661,7 +665,10 @@ class DeclSpec {
bool isNoreturnSpecified() const { return FS_noreturn_specified; }
SourceLocation getNoreturnSpecLoc() const { return FS_noreturnLoc; }
- void ClearFunctionSpecs() {
+ bool isExportSpecified() const { return export_specified; }
+ SourceLocation getExportSpecLoc() const { return exportLoc; }
+
+ void ClearFunctionSpecs() {
FS_inline_specified = false;
FS_inlineLoc = SourceLocation();
FS_forceinline_specified = false;
@@ -811,6 +818,8 @@ class DeclSpec {
bool setFunctionSpecNoreturn(SourceLocation Loc, const char *&PrevSpec,
unsigned &DiagID);
+ bool setExportSpec(SourceLocation Loc);
+
bool SetFriendSpec(SourceLocation Loc, const char *&PrevSpec,
unsigned &DiagID);
bool setModulePrivateSpec(SourceLocation Loc, const char *&PrevSpec,
@@ -1955,6 +1964,10 @@ class Declarator {
LLVM_PREFERRED_TYPE(bool)
unsigned InlineStorageUsed : 1;
+ /// Indicates whether this is set as _Export
+ LLVM_PREFERRED_TYPE(bool)
+ unsigned ExportSpecified : 1;
+
/// Indicates whether this declarator has an initializer.
LLVM_PREFERRED_TYPE(bool)
unsigned HasInitializer : 1;
@@ -2001,6 +2014,9 @@ class Declarator {
/// this declarator as a parameter pack.
SourceLocation EllipsisLoc;
+ /// The source location of the _Export keyword on this declarator
+ SourceLocation ExportLoc;
+
Expr *PackIndexingExpr;
friend struct DeclaratorChunk;
@@ -2030,7 +2046,8 @@ class Declarator {
FunctionDefinitionKind::Declaration)),
Redeclaration(false), Extension(false), ObjCIvar(false),
ObjCWeakProperty(false), InlineStorageUsed(false),
- HasInitializer(false), Attrs(DS.getAttributePool().getFactory()),
+ ExportSpecified(false), HasInitializer(false),
+ Attrs(DS.getAttributePool().getFactory()),
DeclarationAttrs(DeclarationAttrs), AsmLabel(nullptr),
TrailingRequiresClause(nullptr),
InventedTemplateParameterList(nullptr) {
@@ -2109,6 +2126,18 @@ class Declarator {
Range.setEnd(SR.getEnd());
}
+ /// Set this declarator as _Export
+ void SetExport(SourceLocation Loc) {
+ ExportSpecified = true;
+ ExportLoc = Loc;
+ }
+
+ /// Whether this declarator is marked as _Export
+ bool IsExport() const { return ExportSpecified; }
+
+ /// Get the location of the _Export keyword
+ SourceLocation getExportLoc() const { return ExportLoc; }
+
/// Reset the contents of this Declarator.
void clear() {
SS.clear();
@@ -2125,6 +2154,7 @@ class Declarator {
HasInitializer = false;
ObjCIvar = false;
ObjCWeakProperty = false;
+ ExportSpecified = false;
CommaLoc = SourceLocation();
EllipsisLoc = SourceLocation();
PackIndexingExpr = nullptr;
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index bede971ce0191b..44bc9bcc22d4b0 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -1906,6 +1906,36 @@ class Sema final : public SemaBase {
ActOnPragmaMSFunction(SourceLocation Loc,
const llvm::SmallVectorImpl<StringRef> &NoBuiltins);
+ /// A label from a C++ #pragma export, for a symbol that we
+ /// haven't seen the declaration for yet. The TypeList is the argument list
+ /// the function must match if HasTypeList is true.
+ struct SymbolLabel {
+ std::optional<SmallVector<QualType, 4>> TypeList;
+ StringRef MappedName;
+ SourceLocation NameLoc;
+ bool HasTypeList;
+ Qualifiers CVQual;
+ };
+
+ typedef SmallVector<SymbolLabel, 1> PendingSymbolOverloads;
+ typedef llvm::DenseMap<NestedNameSpecifier *, PendingSymbolOverloads>
+ SymbolNames;
+ SymbolNames PendingExportNames;
+
+ FunctionDecl *tryFunctionLookUp(NestedNameSpecifier *NestedName,
+ SourceLocation NameLoc);
+
+ /// trySymbolLookUp try to look up a decl matching the nested specifier
+ /// with optional type list.
+ NamedDecl *trySymbolLookUp(NestedNameSpecifier *NestedName,
+ const clang::Sema::SymbolLabel &Label);
+
+ /// ActonPragmaExport - called on well-formed '\#pragma export'.
+ void ActOnPragmaExport(NestedNameSpecifier *NestedId,
+ SourceLocation ExportNameLoc,
+ std::optional<SmallVector<QualType, 4>> &&TypeList,
+ Qualifiers CVQual);
+
/// Only called on function definitions; if there is a pragma in scope
/// with the effect of a range-based optnone, consider marking the function
/// with attribute optnone.
diff --git a/clang/lib/CodeGen/BackendConsumer.h b/clang/lib/CodeGen/BackendConsumer.h
index a023d29cbd1d73..48ae73b4d25dfe 100644
--- a/clang/lib/CodeGen/BackendConsumer.h
+++ b/clang/lib/CodeGen/BackendConsumer.h
@@ -108,6 +108,7 @@ class BackendConsumer : public ASTConsumer {
void HandleTagDeclRequiredDefinition(const TagDecl *D) override;
void CompleteTentativeDefinition(VarDecl *D) override;
void CompleteExternalDeclaration(DeclaratorDecl *D) override;
+ void CompletePragmaExport(Decl *D) override;
void AssignInheritanceModel(CXXRecordDecl *RD) override;
void HandleVTable(CXXRecordDecl *RD) override;
diff --git a/clang/lib/CodeGen/CodeGenAction.cpp b/clang/lib/CodeGen/CodeGenAction.cpp
index c9f9b688d0d8a2..830f605fb6ad78 100644
--- a/clang/lib/CodeGen/CodeGenAction.cpp
+++ b/clang/lib/CodeGen/CodeGenAction.cpp
@@ -380,6 +380,10 @@ void BackendConsumer::CompleteExternalDeclaration(DeclaratorDecl *D) {
Gen->CompleteExternalDeclaration(D);
}
+void BackendConsumer::CompletePragmaExport(Decl *D) {
+ Gen->CompletePragmaExport(D);
+}
+
void BackendConsumer::AssignInheritanceModel(CXXRecordDecl *RD) {
Gen->AssignInheritanceModel(RD);
}
diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp
index 25c1c496a4f27f..599ec5bab83bce 100644
--- a/clang/lib/CodeGen/CodeGenModule.cpp
+++ b/clang/lib/CodeGen/CodeGenModule.cpp
@@ -5275,6 +5275,21 @@ void CodeGenModule::EmitExternalDeclaration(const DeclaratorDecl *D) {
EmitExternalFunctionDeclaration(FD);
}
+void CodeGenModule::EmitPragmaExport(const Decl *D) {
+ StringRef MangledName;
+ if (auto FD = dyn_cast<FunctionDecl>(D))
+ MangledName = getMangledName(GlobalDecl(FD));
+ else if (auto VD = dyn_cast<VarDecl>(D))
+ MangledName = getMangledName(GlobalDecl(VD));
+ else
+ assert(0 && "Unsupported pragma export Decl type");
+
+ if (llvm::GlobalValue *GV = GetGlobalValue(MangledName)) {
+ GV->setVisibility(llvm::GlobalValue::DefaultVisibility);
+ GV->setDSOLocal(false);
+ }
+}
+
CharUnits CodeGenModule::GetTargetTypeStoreSize(llvm::Type *Ty) const {
return Context.toCharUnitsFromBits(
getDataLayout().getTypeStoreSizeInBits(Ty));
diff --git a/clang/lib/CodeGen/CodeGenModule.h b/clang/lib/CodeGen/CodeGenModule.h
index c58bb88035ca8a..2e9a0617491f60 100644
--- a/clang/lib/CodeGen/CodeGenModule.h
+++ b/clang/lib/CodeGen/CodeGenModule.h
@@ -1364,6 +1364,8 @@ class CodeGenModule : public CodeGenTypeCache {
void EmitExternalDeclaration(const DeclaratorDecl *D);
+ void EmitPragmaExport(const Decl *D);
+
void EmitVTable(CXXRecordDecl *Class);
void RefreshTypeCacheForClass(const CXXRecordDecl *Class);
diff --git a/clang/lib/CodeGen/ModuleBuilder.cpp b/clang/lib/CodeGen/ModuleBuilder.cpp
index d4e0ab0339a8b0..7658d97af01840 100644
--- a/clang/lib/CodeGen/ModuleBuilder.cpp
+++ b/clang/lib/CodeGen/ModuleBuilder.cpp
@@ -314,7 +314,11 @@ namespace {
Builder->EmitExternalDeclaration(D);
}
- void HandleVTable(CXXRecordDecl *RD) override {
+ void CompletePragmaExport(Decl *D) override {
+ Builder->EmitPragmaExport(D);
+ }
+
+ void HandleVTable(CXXRecordDecl *RD) override {
if (Diags.hasUnrecoverableErrorOccurred())
return;
diff --git a/clang/lib/Driver/ToolChains/ZOS.cpp b/clang/lib/Driver/ToolChains/ZOS.cpp
index 074e0556ecd2ad..a1e7ddbe389080 100644
--- a/clang/lib/Driver/ToolChains/ZOS.cpp
+++ b/clang/lib/Driver/ToolChains/ZOS.cpp
@@ -37,6 +37,11 @@ void ZOS::addClangTargetOptions(const ArgList &DriverArgs,
options::OPT_fno_aligned_allocation))
CC1Args.push_back("-faligned-alloc-unavailable");
+ if (!DriverArgs.hasArg(options::OPT_fvisibility_EQ,
+ options::OPT_fvisibility_ms_compat)) {
+ CC1Args.push_back("-fvisibility=hidden");
+ }
+
// Pass "-fno-sized-deallocation" only when the user hasn't manually enabled
// or disabled sized deallocations.
if (!DriverArgs.hasArgNoClaim(options::OPT_fsized_deallocation,
@@ -149,11 +154,10 @@ void zos::Linker::ConstructJob(Compilation &C, const JobAction &JA,
StringRef OutputName = Output.getFilename();
// Strip away the last file suffix in presence from output name and add
// a new .x suffix.
- size_t Suffix = OutputName.find_last_of('.');
- const char *SideDeckName =
- Args.MakeArgString(OutputName.substr(0, Suffix) + ".x");
+ SmallString<128> SideDeckName = OutputName;
+ llvm::sys::path::replace_extension(SideDeckName, "x");
CmdArgs.push_back("-x");
- CmdArgs.push_back(SideDeckName);
+ CmdArgs.push_back(Args.MakeArgString(SideDeckName));
} else {
// We need to direct side file to /dev/null to suppress linker warning when
// the object file contains exported symbols, and -shared or
diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp
index 122a05be1c039a..680e029019b8e6 100644
--- a/clang/lib/Parse/ParseDecl.cpp
+++ b/clang/lib/Parse/ParseDecl.cpp
@@ -4449,6 +4449,12 @@ void Parser::ParseDeclarationSpecifiers(
isInvalid = DS.setFunctionSpecNoreturn(Loc, PrevSpec, DiagID);
break;
+ case tok::kw__Export:
+ // If we find kw__Export, it is being applied to a var or function
+ // This will be handled in ParseDeclaratorInternal()
+ goto DoneWithDeclSpec;
+ break;
+
// friend
case tok::kw_friend:
if (DSContext == DeclSpecContext::DSC_class) {
@@ -6174,6 +6180,7 @@ bool Parser::isDeclarationSpecifier(
case tok::kw_virtual:
case tok::kw_explicit:
case tok::kw__Noreturn:
+ case tok::kw__Export:
// alignment-specifier
case tok::kw__Alignas:
@@ -6765,6 +6772,17 @@ void Parser::ParseDeclaratorInternal(Declarator &D,
tok::TokenKind Kind = Tok.getKind();
+ // If this variable or function is marked as _Export, set the bit
+ if (Kind == tok::kw__Export) {
+ SourceLocation loc = ConsumeToken();
+ D.SetExport(loc);
+ D.SetRangeEnd(loc);
+
+ if (DirectDeclParser)
+ (this->*DirectDeclParser)(D);
+ return;
+ }
+
if (D.getDeclSpec().isTypeSpecPipe() && !isPipeDeclarator(D)) {
DeclSpec DS(AttrFactory);
ParseTypeQualifierListOpt(DS);
diff --git a/clang/lib/Parse/ParseDeclCXX.cpp b/clang/lib/Parse/ParseDeclCXX.cpp
index 6f0f5a0311bc18..5be80011d92bb2 100644
--- a/clang/lib/Parse/ParseDeclCXX.cpp
+++ b/clang/lib/Parse/ParseDeclCXX.cpp
@@ -1751,6 +1751,12 @@ void Parser::ParseClassSpecifier(tok::TokenKind TagTokKind,
// If attributes exist after tag, parse them.
for (;;) {
MaybeParseAttributes(PAKM_CXX11 | PAKM_Declspec | PAKM_GNU, attrs);
+ // If the token is _Export, set the bits
+ if (Tok.is(tok::kw__Export)) {
+ SourceLocation loc = ConsumeToken();
+ DS.setExportSpec(loc);
+ continue;
+ }
// Parse inheritance specifiers.
if (Tok.isOneOf(tok::kw___single_inheritance,
tok::kw___multiple_inheritance,
diff --git a/clang/lib/Parse/ParsePragma.cpp b/clang/lib/Parse/ParsePragma.cpp
index cc6f18b5b319f9..af892115f1f8dd 100644
--- a/clang/lib/Parse/ParsePragma.cpp
+++ b/clang/lib/Parse/ParsePragma.cpp
@@ -401,6 +401,12 @@ struct PragmaMaxTokensTotalHandler : public PragmaHandler {
Token &FirstToken) override;
};
+struct PragmaExportHandler : public PragmaHandler {
+ explicit PragmaExportHandler() : PragmaHandler("export") {}
+ void HandlePragma(Preprocessor &PP, PragmaIntroducer Introducer,
+ Token &FirstToken) override;
+};
+
struct PragmaRISCVHandler : public PragmaHandler {
PragmaRISCVHandler(Sema &Actions)
: PragmaHandler("riscv"), Actions(Actions) {}
@@ -564,6 +570,11 @@ void Parser::initializePragmaHandlers() {
MaxTokensTotalPragmaHandler = std::make_unique<PragmaMaxTokensTotalHandler>();
PP.AddPragmaHandler("clang", MaxTokensTotalPragmaHandler.get());
+ if (getLangOpts().ZOSExt) {
+ ExportHandler = std::make_unique<PragmaExportHandler>();
+ PP.AddPragmaHandler(ExportHandler.get());
+ }
+
if (getTargetInfo().getTriple().isRISCV()) {
RISCVPragmaHandler = std::make_unique<PragmaRISCVHandler>(Actions);
PP.AddPragmaHandler("clang", RISCVPragmaHandler.get());
@@ -698,6 +709,11 @@ void Parser::resetPragmaHandlers() {
PP.RemovePragmaHandler("clang", MaxTokensTotalPragmaHandler.get());
MaxTokensTotalPragmaHandler.reset();
+ if (getLangOpts().ZOSExt) {
+ PP.RemovePragmaHandler(ExportHandler.get());
+ ExportHandler.reset();
+ }
+
if (getTargetInfo().getTriple().isRISCV()) {
PP....
[truncated]
|
✅ With the latest revision this PR passed the C/C++ code formatter. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
@@ -0,0 +1,5 @@ | |||
// REQUIRES: systemz-registered-target |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do these tests actually require systemz-registered-target?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This question is still open, but also, these look like they should be tested in Sema and not CodeGen. Further, it's missing the -verify
line so these expected diagnostics aren't actually being tested.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should there be diagnostics (and corresponding testing) for ignored/meaningless uses of _Export
?
e.g.,
typedef int _Export ty;
ty x;
int f(int _Export x);
static int _Export s;
struct S {
int _Export nonstaticdatamember;
};
void g() {
int _Export automatic;
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, there should be. As well as potentially confusing uses of _Export
as in int _Export x, y;
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The int _Export x, y;
is tested in attr-export.cpp
int _Export var3, var4, _Export var5;
clang/lib/Sema/SemaDecl.cpp
Outdated
if (existingValue != VisibilityAttr::Default) | ||
Diag(DS.getExportSpecLoc(), diag::err_mismatched_visibility); | ||
} else { | ||
Tag->addAttr( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure adding implicit attributes is necessary?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When -fvisibility=hidden (which is the default on z/OS), etc this is needed to ensure the symbol is exported. This is analogous to a declaration with the default visibility attribute specified.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My concern here is that you have code scattered in multiple places to control the visibility; you have two different places you add a visibility attribute, and then you override the visibility in codegen. Having overlapping ways of specifying the visibility will likely lead to weird bugs in edge cases.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Totally valid. The intention was to have these be another way to specify __attribute__((visibility("default")))
. I've rewritten the code to these issues and have hopefully addressed all of your concerns.
clang/include/clang/Sema/Sema.h
Outdated
|
||
/// trySymbolLookUp try to look up a decl matching the nested specifier | ||
/// with optional type list. | ||
NamedDecl *trySymbolLookUp(NestedNameSpecifier *NestedName, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please name this so it's clear it's specifically the lookup algorithm for pragma export.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure. I'll pick a name the works with the other pragmas too.
clang/lib/Sema/SemaAttr.cpp
Outdated
// Check if arguments match. | ||
for (unsigned i = 0; i != FD->getNumParams(); ++i) { | ||
const ParmVarDecl *PVD = FD->getParamDecl(i); | ||
QualType ParmType = PVD->getOriginalType().getCanonicalType(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Using getOriginalType() here seems weird; does that really produce the result you want? Please add tests with function parameters that decay (array/function types).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good catch. This code does work for a case like:
void func(int []);
#pragma export(func(int[]))
but not for:
void func(int []);
#pragma export(func(int*))
(or vice versa)
The later case should work. I'll add code to use getType()
and then to decay the types.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@efriedma-quic I've found a problem related to this comment. If I have a pragma like:
void fd6(int (*)()) {}
#pragma export (fd6(int (*)()))
I noticed that the type chain for the function arguments when I parse them on the pragma has the ParenType. eg:
PointerType 0xc7957d5eeb0 'int (*)(void)'
`-ParenType 0xc7957d5ee80 'int (void)' sugar
`-FunctionProtoType 0xc7957d5eb90 'int (void)' cdecl
`-BuiltinType 0xc7957d2e570 'int'
This results in a type mismatch when I do the QualType compare in this code. Any idea on how to solve this? Thanks
clang/include/clang/Sema/Sema.h
Outdated
SymbolNames; | ||
SymbolNames PendingExportNames; | ||
|
||
FunctionDecl *tryFunctionLookUp(NestedNameSpecifier *NestedName, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
tryFunctionLookUp is dead code?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is used by another pragma I'm going to upstream next. This isn't used by pragma export but it is used by other pragmas we'll upstream after this one. I missed this when removing all of that code and can remove it for now if you wish.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for this! Some things that are missing:
- a release note in
clang/docs/ReleaseNotes.rst
- Parsing and Sema tests
Also, it looks like these are two separate (but related) features, one for the pragma and another for _Export
. Is that the case? If so, they should be split into separate PRs.
def zOSExportDocs : Documentation { | ||
let Category = DocCatFunction; | ||
let Content = [{ | ||
Use the _Export keyword with a function name or external variable to declare |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use the _Export keyword with a function name or external variable to declare | |
Use the ``_Export`` keyword on a function or external variable to declare |
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 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the object name in the same translation unit in which you use the _Export | |
the object name in the same translation unit in which you use the ``_Export`` |
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. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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. | |
This statement exports the function ``anthony``, if you define the function in the | |
translation unit. The ``_Export`` keyword must immediately precede the declaration 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. |
clang/include/clang/Sema/DeclSpec.h
Outdated
@@ -444,6 +446,7 @@ class DeclSpec { | |||
SourceLocation FS_forceinlineLoc; | |||
SourceLocation FriendLoc, ModulePrivateLoc, ConstexprLoc; | |||
SourceLocation TQ_pipeLoc; | |||
SourceLocation exportLoc; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
SourceLocation exportLoc; | |
SourceLocation ExportLoc; |
clang/include/clang/Sema/DeclSpec.h
Outdated
@@ -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; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
unsigned export_specified : 1; | |
unsigned ExportSpecified : 1; // zOS extension |
clang/test/CodeGen/pragma-export.cpp
Outdated
// 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'}} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These look like they should be tested in Sema not CodeGen.
clang/test/CodeGen/pragma-export.c
Outdated
// 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'}} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These look like they should be testing in Sema rather than in CodeGen.
@@ -0,0 +1,5 @@ | |||
// REQUIRES: systemz-registered-target |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This question is still open, but also, these look like they should be tested in Sema and not CodeGen. Further, it's missing the -verify
line so these expected diagnostics aren't actually being tested.
clang/lib/Sema/SemaType.cpp
Outdated
@@ -4250,6 +4250,7 @@ static TypeSourceInfo *GetFullTypeForDeclarator(TypeProcessingState &state, | |||
// The TypeSourceInfo that this function returns will not be a null type. | |||
// If there is an error, this function will fill in a dummy type as fallback. | |||
QualType T = declSpecType; | |||
fprintf(stderr, "SDP: === GetFullTypeForDeclarator\n"); T->dump(); fprintf(stderr, "SDP: === GetFullTypeForDeclarator\n"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Debugging code?
clang/lib/Sema/SemaType.cpp
Outdated
@@ -5722,6 +5723,7 @@ TypeSourceInfo *Sema::GetTypeForDeclarator(Declarator &D) { | |||
|
|||
TypeSourceInfo *ReturnTypeInfo = nullptr; | |||
QualType T = GetDeclSpecTypeForDeclarator(state, ReturnTypeInfo); | |||
fprintf(stderr, "SDP: === GetTypeForDeclarator\n"); T->dump(); fprintf(stderr, "SDP: === GetTypeForDeclarator\n"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Debugging code?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yeah. I merged in the latest code and the PR got updated. I'll remove it. I'm sorting out a problem so not ready for a review again. I wish I could put the PR in draft mode.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@AaronBallman Thanks for the feedback. I'll make the changes and look into why _Exxport wasn't implemented like the other declspecs.
I did consider these to be the same feature (two ways to set the same information). If it's easier for reviewing I can look into splitting the PR.
clang/include/clang/Sema/Sema.h
Outdated
SymbolNames; | ||
SymbolNames PendingExportNames; | ||
|
||
FunctionDecl *tryFunctionLookUp(NestedNameSpecifier *NestedName, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is used by another pragma I'm going to upstream next. This isn't used by pragma export but it is used by other pragmas we'll upstream after this one. I missed this when removing all of that code and can remove it for now if you wish.
clang/include/clang/Sema/Sema.h
Outdated
|
||
/// trySymbolLookUp try to look up a decl matching the nested specifier | ||
/// with optional type list. | ||
NamedDecl *trySymbolLookUp(NestedNameSpecifier *NestedName, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure. I'll pick a name the works with the other pragmas too.
clang/lib/Sema/SemaAttr.cpp
Outdated
// Check if arguments match. | ||
for (unsigned i = 0; i != FD->getNumParams(); ++i) { | ||
const ParmVarDecl *PVD = FD->getParamDecl(i); | ||
QualType ParmType = PVD->getOriginalType().getCanonicalType(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good catch. This code does work for a case like:
void func(int []);
#pragma export(func(int[]))
but not for:
void func(int []);
#pragma export(func(int*))
(or vice versa)
The later case should work. I'll add code to use getType()
and then to decay the types.
clang/lib/Sema/SemaAttr.cpp
Outdated
// Check if arguments match. | ||
for (unsigned i = 0; i != FD->getNumParams(); ++i) { | ||
const ParmVarDecl *PVD = FD->getParamDecl(i); | ||
QualType ParmType = PVD->getOriginalType().getCanonicalType(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@efriedma-quic I've found a problem related to this comment. If I have a pragma like:
void fd6(int (*)()) {}
#pragma export (fd6(int (*)()))
I noticed that the type chain for the function arguments when I parse them on the pragma has the ParenType. eg:
PointerType 0xc7957d5eeb0 'int (*)(void)'
`-ParenType 0xc7957d5ee80 'int (void)' sugar
`-FunctionProtoType 0xc7957d5eb90 'int (void)' cdecl
`-BuiltinType 0xc7957d2e570 'int'
This results in a type mismatch when I do the QualType compare in this code. Any idea on how to solve this? Thanks
clang/lib/Sema/SemaType.cpp
Outdated
@@ -5722,6 +5723,7 @@ TypeSourceInfo *Sema::GetTypeForDeclarator(Declarator &D) { | |||
|
|||
TypeSourceInfo *ReturnTypeInfo = nullptr; | |||
QualType T = GetDeclSpecTypeForDeclarator(state, ReturnTypeInfo); | |||
fprintf(stderr, "SDP: === GetTypeForDeclarator\n"); T->dump(); fprintf(stderr, "SDP: === GetTypeForDeclarator\n"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yeah. I merged in the latest code and the PR got updated. I'll remove it. I'm sorting out a problem so not ready for a review again. I wish I could put the PR in draft mode.
I don't insist on a split, FWIW. |
@AaronBallman @efriedma-quic. I believe I have addressed all of your comments. I moved the tests into Parser/Sema and added some more (and fixed problems these found). Thanks for the feedback. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I didn't spot any further concerns, but I'd appreciate a second set of eyes on this given the size of the changes.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There seems to be a LOT of added complexity for the #pragma export
, to the point that I'm not sure of the value compared to the keyword, and if we're going to keep it, we need to spend quite a bit more time on it.
As far as the keyword itself, I think there are quite a few more diagnostics we should look at, particularly for 'completeness'. I don't really like the error that is being diagnosed (just 'cannot be qualified'), as that is really quite vague. We need to try harder on that.
let Content = [{ | ||
Use the ``_Export`` keyword on a function 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`` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This documentation should also mention the pragma spelling and document the whole thing. Also, do we diagnose that anothony
isn't defined?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We don't diagnose that anthony
isn't defined. This was never diagnosed in the existing z/OS compiler and neither with the visibility attribute. The wording is a little strong. We should say the object is only exported if it is also defined in the translation unit.
|
||
This statement exports the function ``anthony``, if you define the function in the | ||
translation unit. The ``_Export`` keyword must immediately precede the declaration name. | ||
If you apply the ``_Export`` keyword to a class, the compiler automatically exports |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you apply the ``_Export`` keyword to a class, the compiler automatically exports | |
If you apply the ``_Export`` keyword to a class or struct, the compiler automatically exports |
What does it do with unions?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unions are handled the same way as classes or structs. Any member function or static data member of the union will be exported.
@@ -4504,6 +4504,11 @@ void Parser::ParseDeclarationSpecifiers( | |||
isInvalid = DS.setFunctionSpecNoreturn(Loc, PrevSpec, DiagID); | |||
break; | |||
|
|||
case tok::kw__Export: | |||
// We're done with the declaration-specifiers. | |||
goto DoneWithDeclSpec; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does this mean _Export
has to be last? Not a parser guy here, but this one doesn't feel right to me? @AaronBallman can you double check here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
_Export
is part of the declarator. If you see the keyword, the declspec is done and and you are now in the declarator.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
_Export
is not really a declaration specifier (at least in the traditional implementation).
It is more like a C++11 attribute except that it lives on the left of a declarator-id instead of on the right.
In some realizations, the (less clean) conceptual model is that it is a declarator like *
is, except it binds more strongly than (<function parameters>)
or [<array bound>]
.
@perry-ca, I don't see any test in this PR for _Export
in positions like:
int (*_Export x)(void) = 0;
Is the above accepted with this PR? Which conceptual model is being implemented here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The test case above is valid and exports x.
@x = global ptr null, align 8
I'll add a test case to cover this.
You are correct. This keyword is expected to appear right before the identifier of the symbol being exported.
@@ -401,6 +401,12 @@ struct PragmaMaxTokensTotalHandler : public PragmaHandler { | |||
Token &FirstToken) override; | |||
}; | |||
|
|||
struct PragmaExportHandler : public PragmaHandler { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This patch is big enough... I am beginning to wonder if we should split this pragma off into a separate patch.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok. Multiple requests now. I'll move the keyword _Export into a new PR. Do you prefer two new ones or keep this one going just for the pragma?
NestedNameSpecifier * | ||
Parser::zOSParseIdentifier(StringRef PragmaName, | ||
const IdentifierInfo *IdentName) { | ||
NestedNameSpecifier *NestedId = nullptr; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is this for the grammar of? Are you just trying to get the name of a thing? I did something similar, see : ParseOpenACCIDExpression
. For C++ you can use ParseCXXIdExpression
, and I emulated it for C.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The grammar for the pragma is:
'#pragma' 'export' '(' name ',' string-literal ')'
or
'#pragma' 'export' '(' qualified-name '(' argument-list ')' cv-qualifiers ',' string-literal ')'
The first syntax is straight forward. The name matches functions declared as extern "C" in C++. The second syntax provides matching for functions with C++ linkage. The qualified name here isn't an expression. It is like the declarator-id and will match a future declaration. I haven't seen anything that does what this code does.
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:
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.