Author: Sean Perry Date: 2026-01-23T14:46:31-05:00 New Revision: 7638c1df0e50b42087260a95dc9ee890ab6ca594
URL: https://github.com/llvm/llvm-project/commit/7638c1df0e50b42087260a95dc9ee890ab6ca594 DIFF: https://github.com/llvm/llvm-project/commit/7638c1df0e50b42087260a95dc9ee890ab6ca594.diff LOG: [SystemZ][z/OS] Implement #pragma export (#141671) Implement the export pragma that is used in the z/OS XL C/C++ compiler to indicate that an external symbol is to be exported from the shared library. The syntax for the pragma is: ``` '#pragma' 'export' '(' name ')' ``` For C++ if `name` is a function it needs to be declared `extern "C"`. See the following for the XL documentation: - https://www.ibm.com/docs/en/zos/3.1.0?topic=descriptions-pragma-export This code was originally in PR https://github.com/llvm/llvm-project/pull/111035. I have split it out into separate PRs so the code for #pragma export is in one PR and the code for _Export keyword is in another. See that original PR for earlier comments. Added: clang/test/CodeGen/pragma-export.c clang/test/CodeGen/pragma-export.cpp clang/test/CodeGen/zos-pragmas.c clang/test/CodeGen/zos-pragmas.cpp clang/test/Parser/pragma-export.c clang/test/Parser/pragma-export.cpp clang/test/Sema/pragma-export-failing.c clang/test/Sema/pragma-export-failing.cpp Modified: clang/docs/LanguageExtensions.rst clang/docs/ReleaseNotes.rst clang/include/clang/Basic/DiagnosticParseKinds.td clang/include/clang/Basic/DiagnosticSemaKinds.td clang/include/clang/Basic/TokenKinds.def clang/include/clang/Parse/Parser.h clang/include/clang/Sema/Sema.h clang/lib/Driver/ToolChains/ZOS.cpp clang/lib/Parse/ParsePragma.cpp clang/lib/Parse/ParseStmt.cpp clang/lib/Parse/Parser.cpp clang/lib/Sema/Sema.cpp clang/lib/Sema/SemaAttr.cpp clang/lib/Sema/SemaDecl.cpp clang/lib/Sema/SemaDeclAttr.cpp Removed: ################################################################################ diff --git a/clang/docs/LanguageExtensions.rst b/clang/docs/LanguageExtensions.rst index 2f9be164ad0bb..dc0f838e874a0 100644 --- a/clang/docs/LanguageExtensions.rst +++ b/clang/docs/LanguageExtensions.rst @@ -1193,6 +1193,50 @@ specifier for ``_Float16``, and (unlike ``float``) it will not be implicitly pro ``double`` when passed to ``printf``, so the programmer must explicitly cast it to ``double`` before using it with an ``%f`` or similar specifier. +Pragmas +======= + +#pragma export +-------------- + +Clang supports the export pragma used to indicate an +external symbol is to be exported from the shared library being built. The +syntax for the pragma is: + +.. code-block:: c++ + + #pragma export (name) + +where ``name`` is the name of the external function or variable to be +exported. The symbol needs to have external linkage. The pragma may appear +before or after the declaration of ``name``, but must precede the +definition. The pragma must also appear at file scope. If ``name`` is not +defined, the pragma will have no effect. The pragma needs to be specified +in the same translation unit as ``name`` is defined. + +The pragma has the same effect as adding ``__attribute__((visibility("default")))`` +to the declaration of ``name``. + +In C++, the function being exported must be declared as ``extern "C"``. If the +function has overloads, the pragma only applies to the overload with ``extern "C"`` +linkage. For example: + +.. code-block:: c++ + + #pragma export(func) + int func(double) { return 0; } + extern "C" int func(int) { return 4;} + +In the code above the pragma will export ``func(int)`` but not ``func(double)``. + +If none of the overloads are declared with ``extern "C"`` a warning will be +generated saying the pragma didn't resolve to a declaration. For example: + +.. code-block:: c++ + + #pragma export(func) + int func(double) { return 0; } // warning: failed to resolve '#pragma export' to a declaration + Messages on ``deprecated`` and ``unavailable`` Attributes ========================================================= diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index a734804865c57..60a115a434364 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -250,6 +250,13 @@ WebAssembly Support AVR Support ^^^^^^^^^^^ +SystemZ Support +^^^^^^^^^^^^^^^ + +- Add support for `#pragma export` for z/OS. This is a pragma used to export functions and variables + with external linkage from shared libraries. It provides compatibility with the IBM XL C/C++ + compiler. + DWARF Support in Clang ---------------------- diff --git a/clang/include/clang/Basic/DiagnosticParseKinds.td b/clang/include/clang/Basic/DiagnosticParseKinds.td index 457d3644de35a..8d4c816bae0a5 100644 --- a/clang/include/clang/Basic/DiagnosticParseKinds.td +++ b/clang/include/clang/Basic/DiagnosticParseKinds.td @@ -1336,6 +1336,8 @@ def warn_pragma_init_seg_unsupported_target : Warning< def err_pragma_file_or_compound_scope : Error< "'#pragma %0' can only appear at file scope or at the start of a " "compound statement">; +def err_pragma_file_scope : Error< + "'#pragma %0' can only appear at file scope">; // - #pragma stdc unknown def ext_stdc_pragma_ignored : ExtWarn<"unknown pragma in STDC namespace">, InGroup<UnknownPragmas>; diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index a2be7ab3791b9..33461284e11dd 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -1222,6 +1222,16 @@ def err_pragma_pop_visibility_mismatch : Error< "#pragma visibility pop with no matching #pragma visibility push">; def note_surrounding_namespace_starts_here : Note< "surrounding namespace with visibility attribute starts here">; +def warn_failed_to_resolve_pragma : Warning< + "failed to resolve '#pragma %0' to a declaration">, + InGroup<IgnoredPragmas>; +def warn_pragma_not_applied : Warning< + "#pragma %0 is applicable to symbols with external linkage only; " + "not applied to %1">, + InGroup<IgnoredPragmas>; +def warn_pragma_not_applied_to_defined_symbol : Warning< + "#pragma %0 can only applied before a symbol is defined">, + InGroup<IgnoredPragmas>; def err_pragma_loop_invalid_argument_type : Error< "invalid argument of type %0; expected an integer type">; def err_pragma_loop_compatibility : Error< diff --git a/clang/include/clang/Basic/TokenKinds.def b/clang/include/clang/Basic/TokenKinds.def index a3d286fdb81a7..4ee4aa8b31f65 100644 --- a/clang/include/clang/Basic/TokenKinds.def +++ b/clang/include/clang/Basic/TokenKinds.def @@ -1027,6 +1027,9 @@ PRAGMA_ANNOTATION(pragma_fp) // Annotation for the attribute pragma directives - #pragma clang attribute ... PRAGMA_ANNOTATION(pragma_attribute) +// Annotation for C/C++ #pragma export(ident) +PRAGMA_ANNOTATION(pragma_export) + // Annotation for the riscv pragma directives - #pragma clang riscv intrinsic ... PRAGMA_ANNOTATION(pragma_riscv) diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h index cd7dc14701914..2b1dcabc9a194 100644 --- a/clang/include/clang/Parse/Parser.h +++ b/clang/include/clang/Parse/Parser.h @@ -7041,6 +7041,7 @@ class Parser : public CodeCompletionHandler { std::unique_ptr<PragmaHandler> AttributePragmaHandler; std::unique_ptr<PragmaHandler> MaxTokensHerePragmaHandler; std::unique_ptr<PragmaHandler> MaxTokensTotalPragmaHandler; + std::unique_ptr<PragmaHandler> ExportHandler; std::unique_ptr<PragmaHandler> RISCVPragmaHandler; /// Initialize all pragma handlers. @@ -7162,6 +7163,12 @@ class Parser : public CodeCompletionHandler { void HandlePragmaAttribute(); + void zOSHandlePragmaHelper(tok::TokenKind); + + /// Handle the annotation token produced for + /// #pragma export ... + void HandlePragmaExport(); + ///@} // diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 4d2eda8c32317..48803fae9d837 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -2314,6 +2314,23 @@ class Sema final : public SemaBase { ActOnPragmaMSFunction(SourceLocation Loc, const llvm::SmallVectorImpl<StringRef> &NoBuiltins); + NamedDecl *lookupExternCFunctionOrVariable(IdentifierInfo *IdentId, + SourceLocation NameLoc, + Scope *curScope); + + /// Information from a C++ #pragma export, for a symbol that we + /// haven't seen the declaration for yet. + struct PendingPragmaInfo { + SourceLocation NameLoc; + bool Used; + }; + + llvm::DenseMap<IdentifierInfo *, PendingPragmaInfo> PendingExportedNames; + + /// ActonPragmaExport - called on well-formed '\#pragma export'. + void ActOnPragmaExport(IdentifierInfo *IdentId, SourceLocation ExportNameLoc, + Scope *curScope); + /// Only called on function definitions; if there is a pragma in scope /// with the effect of a range-based optnone, consider marking the function /// with attribute optnone. @@ -3843,6 +3860,8 @@ class Sema final : public SemaBase { void warnOnReservedIdentifier(const NamedDecl *D); void warnOnCTypeHiddenInCPlusPlus(const NamedDecl *D); + void ProcessPragmaExport(DeclaratorDecl *newDecl); + Decl *ActOnDeclarator(Scope *S, Declarator &D); NamedDecl *HandleDeclarator(Scope *S, Declarator &D, @@ -4933,6 +4952,8 @@ class Sema final : public SemaBase { TypeVisibilityAttr::VisibilityType Vis); VisibilityAttr *mergeVisibilityAttr(Decl *D, const AttributeCommonInfo &CI, VisibilityAttr::VisibilityType Vis); + void mergeVisibilityType(Decl *D, SourceLocation Loc, + VisibilityAttr::VisibilityType Type); SectionAttr *mergeSectionAttr(Decl *D, const AttributeCommonInfo &CI, StringRef Name); diff --git a/clang/lib/Driver/ToolChains/ZOS.cpp b/clang/lib/Driver/ToolChains/ZOS.cpp index eac8f623f9a50..c40f71c170538 100644 --- a/clang/lib/Driver/ToolChains/ZOS.cpp +++ b/clang/lib/Driver/ToolChains/ZOS.cpp @@ -36,6 +36,10 @@ void ZOS::addClangTargetOptions(const ArgList &DriverArgs, options::OPT_fno_aligned_allocation)) CC1Args.push_back("-faligned-alloc-unavailable"); + if (!DriverArgs.hasArg(options::OPT_fvisibility_EQ, + options::OPT_fvisibility_ms_compat)) + CC1Args.push_back("-fvisibility=hidden"); + if (DriverArgs.hasFlag(options::OPT_fxl_pragma_pack, options::OPT_fno_xl_pragma_pack, true)) CC1Args.push_back("-fxl-pragma-pack"); diff --git a/clang/lib/Parse/ParsePragma.cpp b/clang/lib/Parse/ParsePragma.cpp index 7e8b339f5be49..54b96fd2102b5 100644 --- a/clang/lib/Parse/ParsePragma.cpp +++ b/clang/lib/Parse/ParsePragma.cpp @@ -25,6 +25,7 @@ #include "clang/Sema/SemaCodeCompletion.h" #include "clang/Sema/SemaRISCV.h" #include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/ScopeExit.h" #include "llvm/ADT/StringSwitch.h" #include <optional> using namespace clang; @@ -395,6 +396,12 @@ struct PragmaMaxTokensTotalHandler : public PragmaHandler { Token &FirstToken) override; }; +struct PragmaExportHandler : public PragmaHandler { + explicit PragmaExportHandler() : PragmaHandler("export") {} + void HandlePragma(Preprocessor &PP, PragmaIntroducer Introducer, + Token &FirstToken) override; +}; + struct PragmaRISCVHandler : public PragmaHandler { PragmaRISCVHandler(Sema &Actions) : PragmaHandler("riscv"), Actions(Actions) {} @@ -558,6 +565,11 @@ void Parser::initializePragmaHandlers() { MaxTokensTotalPragmaHandler = std::make_unique<PragmaMaxTokensTotalHandler>(); PP.AddPragmaHandler("clang", MaxTokensTotalPragmaHandler.get()); + if (getLangOpts().ZOSExt) { + ExportHandler = std::make_unique<PragmaExportHandler>(); + PP.AddPragmaHandler(ExportHandler.get()); + } + if (getTargetInfo().getTriple().isRISCV()) { RISCVPragmaHandler = std::make_unique<PragmaRISCVHandler>(Actions); PP.AddPragmaHandler("clang", RISCVPragmaHandler.get()); @@ -692,6 +704,11 @@ void Parser::resetPragmaHandlers() { PP.RemovePragmaHandler("clang", MaxTokensTotalPragmaHandler.get()); MaxTokensTotalPragmaHandler.reset(); + if (getLangOpts().ZOSExt) { + PP.RemovePragmaHandler(ExportHandler.get()); + ExportHandler.reset(); + } + if (getTargetInfo().getTriple().isRISCV()) { PP.RemovePragmaHandler("clang", RISCVPragmaHandler.get()); RISCVPragmaHandler.reset(); @@ -1386,6 +1403,73 @@ bool Parser::HandlePragmaMSAllocText(StringRef PragmaName, return true; } +void Parser::zOSHandlePragmaHelper(tok::TokenKind PragmaKind) { + assert(Tok.is(PragmaKind)); + + StringRef PragmaName = "export"; + + using namespace clang::charinfo; + auto *TheTokens = + (std::pair<std::unique_ptr<Token[]>, size_t> *)Tok.getAnnotationValue(); + PP.EnterTokenStream(std::move(TheTokens->first), TheTokens->second, true, + false); + ConsumeAnnotationToken(); + + llvm::scope_exit OnReturn([this]() { + while (Tok.isNot(tok::eof)) + PP.Lex(Tok); + PP.Lex(Tok); + }); + + do { + PP.Lex(Tok); + if (Tok.isNot(tok::l_paren)) { + PP.Diag(Tok.getLocation(), diag::warn_pragma_expected_lparen) + << PragmaName; + return; + } + + PP.Lex(Tok); + if (Tok.isNot(tok::identifier)) { + PP.Diag(Tok.getLocation(), diag::warn_pragma_expected_identifier) + << PragmaName; + return; + } + + IdentifierInfo *IdentName = Tok.getIdentifierInfo(); + SourceLocation IdentNameLoc = Tok.getLocation(); + PP.Lex(Tok); + + if (Tok.isNot(tok::r_paren)) { + PP.Diag(Tok.getLocation(), diag::warn_pragma_expected_rparen) + << PragmaName; + return; + } + + PP.Lex(Tok); + Actions.ActOnPragmaExport(IdentName, IdentNameLoc, getCurScope()); + + // Because export is also a C++ keyword, we also check for that. + if (Tok.is(tok::identifier) || Tok.is(tok::kw_export)) { + PragmaName = Tok.getIdentifierInfo()->getName(); + if (PragmaName != "export") + PP.Diag(Tok.getLocation(), diag::warn_pragma_extra_tokens_at_eol) + << PragmaName; + } else if (Tok.isNot(tok::eof)) { + PP.Diag(Tok.getLocation(), diag::warn_pragma_extra_tokens_at_eol) + << PragmaName; + return; + } + } while (Tok.isNot(tok::eof)); + return; +} + +void Parser::HandlePragmaExport() { + assert(Tok.is(tok::annot_pragma_export)); + + zOSHandlePragmaHelper(tok::annot_pragma_export); +} + static std::string PragmaLoopHintString(Token PragmaName, Token Option) { StringRef Str = PragmaName.getIdentifierInfo()->getName(); std::string ClangLoopStr("clang loop "); @@ -4133,6 +4217,44 @@ void PragmaMaxTokensTotalHandler::HandlePragma(Preprocessor &PP, PP.overrideMaxTokens(MaxTokens, Loc); } +static void zOSPragmaHandlerHelper(Preprocessor &PP, Token &Tok, + tok::TokenKind TokKind) { + Token AnnotTok; + AnnotTok.startToken(); + AnnotTok.setKind(TokKind); + AnnotTok.setLocation(Tok.getLocation()); + AnnotTok.setAnnotationEndLoc(Tok.getLocation()); + SmallVector<Token, 8> TokenVector; + // Suck up all of the tokens before the eod. + for (; Tok.isNot(tok::eod); PP.Lex(Tok)) { + TokenVector.push_back(Tok); + AnnotTok.setAnnotationEndLoc(Tok.getLocation()); + } + // Add a sentinel EoF token to the end of the list. + Token EoF; + EoF.startToken(); + EoF.setKind(tok::eof); + EoF.setLocation(Tok.getLocation()); + TokenVector.push_back(EoF); + // We must allocate this array with new because EnterTokenStream is going to + // delete it later. + markAsReinjectedForRelexing(TokenVector); + auto TokenArray = std::make_unique<Token[]>(TokenVector.size()); + std::copy(TokenVector.begin(), TokenVector.end(), TokenArray.get()); + auto Value = new (PP.getPreprocessorAllocator()) + std::pair<std::unique_ptr<Token[]>, size_t>(std::move(TokenArray), + TokenVector.size()); + AnnotTok.setAnnotationValue(Value); + PP.EnterToken(AnnotTok, /*IsReinject*/ false); +} + +/// Handle #pragma export. +void PragmaExportHandler::HandlePragma(Preprocessor &PP, + PragmaIntroducer Introducer, + Token &FirstToken) { + zOSPragmaHandlerHelper(PP, FirstToken, tok::annot_pragma_export); +} + // Handle '#pragma clang riscv intrinsic vector'. // '#pragma clang riscv intrinsic sifive_vector'. // '#pragma clang riscv intrinsic andes_vector'. diff --git a/clang/lib/Parse/ParseStmt.cpp b/clang/lib/Parse/ParseStmt.cpp index 260f8126360d5..771db0a00091b 100644 --- a/clang/lib/Parse/ParseStmt.cpp +++ b/clang/lib/Parse/ParseStmt.cpp @@ -503,6 +503,12 @@ StmtResult Parser::ParseStatementOrDeclarationAfterAttributes( ProhibitAttributes(GNUAttrs); HandlePragmaAttribute(); return StmtEmpty(); + case tok::annot_pragma_export: + ProhibitAttributes(CXX11Attrs); + ProhibitAttributes(GNUAttrs); + Diag(Tok, diag::err_pragma_file_scope) << "export"; + ConsumeAnnotationToken(); + return StmtEmpty(); } // If we reached this code, the statement must end in a semicolon. @@ -1032,6 +1038,10 @@ void Parser::ParseCompoundStatementLeadingPragmas() { case tok::annot_pragma_dump: HandlePragmaDump(); break; + case tok::annot_pragma_export: + Diag(Tok, diag::err_pragma_file_scope) << "export"; + ConsumeAnnotationToken(); + break; default: checkForPragmas = false; break; diff --git a/clang/lib/Parse/Parser.cpp b/clang/lib/Parse/Parser.cpp index af3ba7853820f..be9e0a39a3781 100644 --- a/clang/lib/Parse/Parser.cpp +++ b/clang/lib/Parse/Parser.cpp @@ -803,6 +803,9 @@ Parser::ParseExternalDeclaration(ParsedAttributes &Attrs, case tok::annot_pragma_attribute: HandlePragmaAttribute(); return nullptr; + case tok::annot_pragma_export: + HandlePragmaExport(); + return nullptr; case tok::semi: // Either a C++11 empty-declaration or attribute-declaration. SingleDecl = diff --git a/clang/lib/Sema/Sema.cpp b/clang/lib/Sema/Sema.cpp index 6eea8f6e9d97e..d53527af38653 100644 --- a/clang/lib/Sema/Sema.cpp +++ b/clang/lib/Sema/Sema.cpp @@ -1495,6 +1495,12 @@ void Sema::ActOnEndOfTranslationUnit() { Consumer.CompleteExternalDeclaration(D); } + // Visit all pending #pragma export. + for (const PendingPragmaInfo &Exported : PendingExportedNames.values()) { + if (!Exported.Used) + Diag(Exported.NameLoc, diag::warn_failed_to_resolve_pragma) << "export"; + } + if (LangOpts.HLSL) HLSL().ActOnEndOfTranslationUnit(getASTContext().getTranslationUnitDecl()); if (LangOpts.OpenACC) diff --git a/clang/lib/Sema/SemaAttr.cpp b/clang/lib/Sema/SemaAttr.cpp index 7729c113e422e..738daf2f69290 100644 --- a/clang/lib/Sema/SemaAttr.cpp +++ b/clang/lib/Sema/SemaAttr.cpp @@ -1327,6 +1327,61 @@ void Sema::AddImplicitMSFunctionNoBuiltinAttr(FunctionDecl *FD) { FD->addAttr(NoBuiltinAttr::CreateImplicit(Context, V.data(), V.size())); } +NamedDecl *Sema::lookupExternCFunctionOrVariable(IdentifierInfo *IdentId, + SourceLocation NameLoc, + Scope *curScope) { + LookupResult Result(*this, IdentId, NameLoc, LookupOrdinaryName); + LookupName(Result, curScope); + if (!getLangOpts().CPlusPlus) + return Result.getAsSingle<NamedDecl>(); + for (NamedDecl *D : Result) { + if (auto *FD = dyn_cast<FunctionDecl>(D)) + if (FD->isExternC()) + return D; + if (isa<VarDecl>(D)) + return D; + } + return nullptr; +} + +void Sema::ActOnPragmaExport(IdentifierInfo *IdentId, SourceLocation NameLoc, + Scope *curScope) { + PendingPragmaInfo Info; + Info.NameLoc = NameLoc; + Info.Used = false; + + NamedDecl *PrevDecl = + lookupExternCFunctionOrVariable(IdentId, NameLoc, curScope); + if (!PrevDecl) { + PendingExportedNames[IdentId] = Info; + return; + } + + if (auto *FD = dyn_cast<FunctionDecl>(PrevDecl->getCanonicalDecl())) { + if (!FD->hasExternalFormalLinkage()) { + Diag(NameLoc, diag::warn_pragma_not_applied) << "export" << PrevDecl; + return; + } + if (FD->hasBody()) { + Diag(NameLoc, diag::warn_pragma_not_applied_to_defined_symbol) + << "export"; + return; + } + } else if (auto *VD = dyn_cast<VarDecl>(PrevDecl->getCanonicalDecl())) { + if (!VD->hasExternalFormalLinkage()) { + Diag(NameLoc, diag::warn_pragma_not_applied) << "export" << PrevDecl; + return; + } + if (VD->hasDefinition() == VarDecl::Definition) { + Diag(NameLoc, diag::warn_pragma_not_applied_to_defined_symbol) + << "export"; + return; + } + } + mergeVisibilityType(PrevDecl->getCanonicalDecl(), NameLoc, + VisibilityAttr::Default); +} + typedef std::vector<std::pair<unsigned, SourceLocation> > VisStack; enum : unsigned { NoVisibility = ~0U }; diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index ae779d6830d9b..066acc3424c8f 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -7650,6 +7650,32 @@ static void emitReadOnlyPlacementAttrWarning(Sema &S, const VarDecl *VD) { } } +void Sema::ProcessPragmaExport(DeclaratorDecl *NewD) { + assert((isa<FunctionDecl>(NewD) || isa<VarDecl>(NewD)) && + "NewD is not a function or variable"); + + if (PendingExportedNames.empty()) + return; + if (FunctionDecl *FD = dyn_cast<FunctionDecl>(NewD)) { + if (getLangOpts().CPlusPlus && !FD->isExternC()) + return; + } + IdentifierInfo *IdentName = NewD->getIdentifier(); + if (IdentName == nullptr) + return; + auto PendingName = PendingExportedNames.find(IdentName); + if (PendingName != PendingExportedNames.end()) { + auto &Label = PendingName->second; + if (!Label.Used) { + Label.Used = true; + if (NewD->hasExternalFormalLinkage()) + mergeVisibilityType(NewD, Label.NameLoc, VisibilityAttr::Default); + else + Diag(Label.NameLoc, diag::warn_pragma_not_applied) << "export" << NewD; + } + } +} + // Checks if VD is declared at global scope or with C language linkage. static bool isMainVar(DeclarationName Name, VarDecl *VD) { return Name.getAsIdentifierInfo() && @@ -8364,6 +8390,7 @@ NamedDecl *Sema::ActOnVariableDeclarator( CheckShadow(NewVD, ShadowedDecl, Previous); ProcessPragmaWeak(S, NewVD); + ProcessPragmaExport(NewVD); // If this is the first declaration of an extern C variable, update // the map of such variables. @@ -11010,6 +11037,7 @@ Sema::ActOnFunctionDeclarator(Scope *S, Declarator &D, DeclContext *DC, } ProcessPragmaWeak(S, NewFD); + ProcessPragmaExport(NewFD); checkAttributesAfterMerging(*this, *NewFD); AddKnownFunctionAttributes(NewFD); diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp index dc954c3925596..30891777e2631 100644 --- a/clang/lib/Sema/SemaDeclAttr.cpp +++ b/clang/lib/Sema/SemaDeclAttr.cpp @@ -2928,6 +2928,15 @@ static void handleExternalSourceSymbolAttr(Sema &S, Decl *D, S.Context, AL, Language, DefinedIn, IsGeneratedDeclaration, USR)); } +void Sema::mergeVisibilityType(Decl *D, SourceLocation Loc, + VisibilityAttr::VisibilityType Value) { + if (VisibilityAttr *Attr = D->getAttr<VisibilityAttr>()) { + if (Attr->getVisibility() != Value) + Diag(Loc, diag::err_mismatched_visibility); + } else + D->addAttr(VisibilityAttr::CreateImplicit(Context, Value)); +} + template <class T> static T *mergeVisibilityAttr(Sema &S, Decl *D, const AttributeCommonInfo &CI, typename T::VisibilityType value) { diff --git a/clang/test/CodeGen/pragma-export.c b/clang/test/CodeGen/pragma-export.c new file mode 100644 index 0000000000000..d6d2d6ad878f3 --- /dev/null +++ b/clang/test/CodeGen/pragma-export.c @@ -0,0 +1,42 @@ +// REQUIRES: systemz-registered-target +// RUN: %clang_cc1 %s -emit-llvm -fzos-extensions -triple s390x-none-zos -fvisibility=hidden -o - | FileCheck %s + +// Testing pragma export after decl. +void f0(void) {} +int v0; +int vd = 2; +#pragma export(f0) +#pragma export(v0) +#pragma export(vd) + +// Testing pragma export before decl. +#pragma export(f1) +#pragma export(v1) +void f1(void) {} +int v1; + +void f2(void); + +void t0(void) { f2();} + +#pragma export(f2) +void f2(void) {} + +int func() { + int local; + int l2; + return local+l2; +} + +int local = 2; +int l2 =4; + +// CHECK: @vd = hidden global i32 +// CHECK: @local = hidden global i32 +// CHECK: @l2 = hidden global i32 +// CHECK: @v0 = global i32 +// CHECK: @v1 = global i32 +// CHECK: define hidden void @f0() +// CHECK: define void @f1() +// CHECK: define hidden void @t0() +// CHECK: define void @f2() diff --git a/clang/test/CodeGen/pragma-export.cpp b/clang/test/CodeGen/pragma-export.cpp new file mode 100644 index 0000000000000..531afbd659234 --- /dev/null +++ b/clang/test/CodeGen/pragma-export.cpp @@ -0,0 +1,69 @@ +// REQUIRES: systemz-registered-target +// RUN: %clang_cc1 -x c++ %s -emit-llvm -triple s390x-none-zos -fzos-extensions -fvisibility=hidden -o - | FileCheck %s + +// Testing pragma export after decl. +extern "C" void f0(void) {} +int v0; +#pragma export(f0) +#pragma export(v0) + +// Testing pragma export before decl. +#pragma export(f1) +#pragma export(v1) +extern "C" void f1(void) {} +int v1; + +// Testing overloaded functions. +#pragma export(f2) +void f2(double, double) {} +extern "C" void f2(int) {} +void f2(int, int) {} + +extern "C" void f3(double) {} +void f3(int, double) {} +void f3(double, double) {} +#pragma export(f3) + +extern "C" void f2b(void) {} + +void t0(void) { + f2b(); +} + +// Testing pragma export after decl and usage. +#pragma export(f2b) + +// Testing pragma export with namespace. +extern "C" void f5(void) {} +extern "C" void f5a(void) {} +namespace N0 { +void f5(void) {} +#pragma export(f5) +#pragma export(f5a) +void f5a(void) {} +} // namespace N0 + +void f10(int); +#pragma export(f10) +extern "C" void f10(double) {} +void f10(int) {} + +// CHECK: @v0 = hidden global i32 0 +// CHECK: @v1 = global i32 0 +// CHECK: define hidden void @f0() +// CHECK: define void @f1() +// CHECK: define hidden void @_Z2f2dd(double noundef %0, double noundef %1) +// CHECK: define void @f2(i32 noundef signext %0) +// CHECK: define hidden void @_Z2f2ii(i32 noundef signext %0, i32 noundef signext %1) +// CHECK: define hidden void @f3(double noundef %0) +// CHECK: define hidden void @_Z2f3id(i32 noundef signext %0, double noundef %1) +// CHECK: define hidden void @_Z2f3dd(double noundef %0, double noundef %1) +// CHECK: define hidden void @f2b() +// CHECK: define hidden void @_Z2t0v() +// CHECK: define hidden void @f5() +// CHECK: define hidden void @f5a() +// CHECK: define hidden void @_ZN2N02f5Ev() +// CHECK: define hidden void @_ZN2N03f5aEv() +// CHECK: define void @f10(double noundef %0) +// CHECK: define hidden void @_Z3f10i(i32 noundef signext %0) + diff --git a/clang/test/CodeGen/zos-pragmas.c b/clang/test/CodeGen/zos-pragmas.c new file mode 100644 index 0000000000000..e2bd03a33e20a --- /dev/null +++ b/clang/test/CodeGen/zos-pragmas.c @@ -0,0 +1,11 @@ +// REQUIRES: systemz-registered-target +// RUN: %clang_cc1 -emit-llvm -triple s390x-none-zos -fvisibility=hidden %s -o - | FileCheck %s + +int a,b,c; +#pragma export(a) export(b) export(c) + +void foo(void); + +// CHECK: @a = global i32 0, align 4 +// CHECK: @b = global i32 0, align 4 +// CHECK: @c = global i32 0, align 4 diff --git a/clang/test/CodeGen/zos-pragmas.cpp b/clang/test/CodeGen/zos-pragmas.cpp new file mode 100644 index 0000000000000..65e428796039e --- /dev/null +++ b/clang/test/CodeGen/zos-pragmas.cpp @@ -0,0 +1,11 @@ +// REQUIRES: systemz-registered-target +// RUN: %clang_cc1 -x c++ -emit-llvm -triple s390x-none-zos -fvisibility=hidden %s -o - | FileCheck %s + +#pragma export(a) export(b) export(c) +int a,b,c; + +void foo(void); + +// CHECK: @a = global i32 0, align 4 +// CHECK: @b = global i32 0, align 4 +// CHECK: @c = global i32 0, align 4 diff --git a/clang/test/Parser/pragma-export.c b/clang/test/Parser/pragma-export.c new file mode 100644 index 0000000000000..e78fa21242c77 --- /dev/null +++ b/clang/test/Parser/pragma-export.c @@ -0,0 +1,15 @@ +// RUN: %clang_cc1 -triple s390x-ibm-zos -fsyntax-only -verify %s + +int x; + +#pragma export x // expected-warning {{missing '(' after '#pragma export' - ignoring}} +#pragma export // expected-warning {{missing '(' after '#pragma export' - ignoring}} +#pragma export( // expected-warning {{expected identifier in '#pragma export' - ignored}} +#pragma export(x // expected-warning {{missing ')' after '#pragma export' - ignoring}} +#pragma export(::x) // expected-warning {{expected identifier in '#pragma export' - ignored}} +#pragma export(x) + +void f() { +} + +#pragma export(f()) // expected-warning {{missing ')' after '#pragma export' - ignoring}} diff --git a/clang/test/Parser/pragma-export.cpp b/clang/test/Parser/pragma-export.cpp new file mode 100644 index 0000000000000..91d2e162bcfec --- /dev/null +++ b/clang/test/Parser/pragma-export.cpp @@ -0,0 +1,16 @@ +// RUN: %clang_cc1 -x c++ -triple s390x-ibm-zos -fsyntax-only -verify %s + +extern int i; +#pragma export( // expected-warning {{expected identifier in '#pragma export' - ignored}} +#pragma export() // expected-warning {{expected identifier in '#pragma export' - ignored}} +#pragma export(i) + +struct S { + static int i; +}; +#pragma export(S::i) // expected-warning {{missing ')' after '#pragma export' - ignoring}} + +void f(int); +void f(double, double); +#pragma export(f // expected-warning {{missing ')' after '#pragma export' - ignoring}} +#pragma export(f( // expected-warning {{missing ')' after '#pragma export' - ignoring}} diff --git a/clang/test/Sema/pragma-export-failing.c b/clang/test/Sema/pragma-export-failing.c new file mode 100644 index 0000000000000..14faf120f3487 --- /dev/null +++ b/clang/test/Sema/pragma-export-failing.c @@ -0,0 +1,42 @@ +// REQUIRES: systemz-registered-target +// RUN: %clang_cc1 -triple s390x-none-zos -fzos-extensions %s -fsyntax-only -verify + +#pragma export(d0) // expected-warning{{failed to resolve '#pragma export' to a declaration}} +#pragma export(f9) // expected-warning{{failed to resolve '#pragma export' to a declaration}} + +#pragma export(sf1) // expected-warning{{#pragma export is applicable to symbols with external linkage only; not applied to 'sf1'}} +#pragma export(s1) // expected-warning{{#pragma export is applicable to symbols with external linkage only; not applied to 's1'}} +static void sf1(void) {} +static int s1; + +static void sf0(void) {} +int v0; +static int s0; +#pragma export(sf0) // expected-warning{{#pragma export is applicable to symbols with external linkage only; not applied to 'sf0'}} +#pragma export(s0) // expected-warning{{#pragma export is applicable to symbols with external linkage only; not applied to 's0'}} + +#pragma export(f1) // expected-error {{visibility does not match previous declaration}} +int f1() __attribute__((visibility("hidden"))); +int f2() __attribute__((visibility("hidden"))); +#pragma export(f2) // expected-error {{visibility does not match previous declaration}} + + +int hoo() __attribute__((visibility("hidden"))); + +int foo() { return 4; } +#pragma export(foo) // expected-warning {{#pragma export can only applied before a symbol is defined}} + +int var = 4; +#pragma export(var) // expected-warning {{#pragma export can only applied before a symbol is defined}} + +int func() { +#pragma export(local) // expected-error{{'#pragma export' can only appear at file scope}} + int local; + int l2; +#pragma export(l2) // expected-error{{'#pragma export' can only appear at file scope}} + return local+l2; +} + +int local = 2; +int l2 =4; + diff --git a/clang/test/Sema/pragma-export-failing.cpp b/clang/test/Sema/pragma-export-failing.cpp new file mode 100644 index 0000000000000..da0d9ca4531de --- /dev/null +++ b/clang/test/Sema/pragma-export-failing.cpp @@ -0,0 +1,34 @@ +// REQUIRES: systemz-registered-target +// RUN: %clang_cc1 -x c++ -triple s390x-none-zos -fzos-extensions %s -fsyntax-only -verify + +#pragma export(f0(int)) // expected-warning{{missing ')' after '#pragma export' - ignoring}} +#pragma export(f3(double, double, double)) // expected-warning{{missing ')' after '#pragma export' - ignoring}} + +#pragma export(N::sf1(void)) // expected-warning{{missing ')' after '#pragma export' - ignoring}} +#pragma export(N::s1) // expected-warning{{missing ')' after '#pragma export' - ignoring}} +namespace N { +static void sf1(void) {} +static int s1; + +static void sf0(void) {} +int v0; +static int s0; +} +#pragma export(N::sf0(void)) // expected-warning{{missing ')' after '#pragma export' - ignoring}} +#pragma export(N::s0) // expected-warning{{missing ')' after '#pragma export' - ignoring}} + +void f10(int); +#pragma export(f10) // expected-warning{{failed to resolve '#pragma export' to a declaration}} + +#pragma export(f11) // expected-warning{{failed to resolve '#pragma export' to a declaration}} +void f11(int); + +template<auto func> +struct S { + +#pragma export(func) // expected-error{{this pragma cannot appear in struct declaration}} +}; + +extern "C" void funcToExport(); + +S<funcToExport> s{}; _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
