diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index a80fedebf8f89..244c592d2ffb1 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -760,6 +760,11 @@ WebAssembly Support AVR Support ^^^^^^^^^^^ +SystemZ Support +^^^^^^^^^^^^^^^ + +- Add support for `#pragma export` and `_Export` keyword for z/OS + DWARF Support in Clang ---------------------- diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td index a734eb6658c3d..41e2aa1883fed 100644 --- a/clang/include/clang/Basic/Attr.td +++ b/clang/include/clang/Basic/Attr.td @@ -4672,6 +4672,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 cbb397cb31dfb..14e2824f8b848 100644 --- a/clang/include/clang/Basic/AttrDocs.td +++ b/clang/include/clang/Basic/AttrDocs.td @@ -7552,6 +7552,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 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`` +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 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. + }]; +} + 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 e5a7cdc14a737..826456c4d551e 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -1209,6 +1209,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; +def warn_pragma_not_applied : Warning< + "#pragma %0 is applicable to symbols with external linkage only; " + "not applied to %1">, + InGroup; +def warn_pragma_not_applied_to_defined_symbol : Warning< + "#pragma %0 can only applied before a symbol is defined">, + InGroup; def err_pragma_loop_invalid_argument_type : Error< "invalid argument of type %0; expected an integer type">; def err_pragma_loop_compatibility : Error< @@ -9612,6 +9622,8 @@ def warn_redefine_extname_not_applied : Warning< "#pragma redefine_extname is applicable to external C declarations only; " "not applied to %select{function|variable}0 %1">, InGroup; +def err_cannot_be_exported : Error< + "cannot be '_Export` qualified">; } // End of general sema category. // inline asm. diff --git a/clang/include/clang/Basic/TokenKinds.def b/clang/include/clang/Basic/TokenKinds.def index fb53d88deea4a..7ee6c9e3d206e 100644 --- a/clang/include/clang/Basic/TokenKinds.def +++ b/clang/include/clang/Basic/TokenKinds.def @@ -353,6 +353,9 @@ KEYWORD(__ptrauth , KEYALL) // C2y UNARY_EXPR_OR_TYPE_TRAIT(_Countof, CountOf, KEYNOCXX) +// z/OS specific keywords +KEYWORD(_Export , KEYZOS) + // C++ 2.11p1: Keywords. KEYWORD(asm , KEYCXX|KEYGNU) KEYWORD(bool , BOOLSUPPORT|KEYC23) @@ -1009,6 +1012,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 46a2d26beb7f9..6e98ce158548a 100644 --- a/clang/include/clang/Parse/Parser.h +++ b/clang/include/clang/Parse/Parser.h @@ -320,6 +320,7 @@ class Parser : public CodeCompletionHandler { std::unique_ptr AttributePragmaHandler; std::unique_ptr MaxTokensHerePragmaHandler; std::unique_ptr MaxTokensTotalPragmaHandler; + std::unique_ptr ExportHandler; std::unique_ptr RISCVPragmaHandler; std::unique_ptr CommentSemaHandler; @@ -954,6 +955,18 @@ class Parser : public CodeCompletionHandler { void HandlePragmaAttribute(); + /// Helper functions for handling zOS pragmas. + NestedNameSpecifier *zOSParseIdentifier(StringRef PragmaName, + const IdentifierInfo *IdentName); + bool zOSParseParameterList(StringRef PragmaName, + std::optional> &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 6c4a32c4ac2f0..58a69d274a02e 100644 --- a/clang/include/clang/Sema/DeclSpec.h +++ b/clang/include/clang/Sema/DeclSpec.h @@ -397,6 +397,8 @@ class DeclSpec { unsigned FS_virtual_specified : 1; LLVM_PREFERRED_TYPE(bool) unsigned FS_noreturn_specified : 1; + LLVM_PREFERRED_TYPE(bool) + unsigned ExportSpecified : 1; // z/OS extension // friend-specifier LLVM_PREFERRED_TYPE(bool) @@ -443,6 +445,7 @@ class DeclSpec { SourceLocation FS_forceinlineLoc; SourceLocation FriendLoc, ModulePrivateLoc, ConstexprLoc; SourceLocation TQ_pipeLoc; + SourceLocation ExportLoc; WrittenBuiltinSpecs writtenBS; void SaveWrittenBuiltinSpecs(); @@ -491,9 +494,9 @@ 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), - ConstexprSpecifier( - static_cast(ConstexprSpecKind::Unspecified)), + FS_noreturn_specified(false), ExportSpecified(false), + FriendSpecifiedFirst(false), ConstexprSpecifier(static_cast( + ConstexprSpecKind::Unspecified)), Attrs(attrFactory), writtenBS(), ObjCQualifiers(nullptr) {} // storage-class-specifier @@ -660,6 +663,9 @@ class DeclSpec { bool isNoreturnSpecified() const { return FS_noreturn_specified; } SourceLocation getNoreturnSpecLoc() const { return FS_noreturnLoc; } + bool isExportSpecified() const { return ExportSpecified; } + SourceLocation getExportSpecLoc() const { return ExportLoc; } + void ClearFunctionSpecs() { FS_inline_specified = false; FS_inlineLoc = SourceLocation(); @@ -810,6 +816,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 +1963,10 @@ class Declarator { LLVM_PREFERRED_TYPE(bool) unsigned InlineStorageUsed : 1; + /// Indicates whether this is set as _Export. + LLVM_PREFERRED_TYPE(bool) + unsigned ExportSpecified : 1; // z/OS extension + /// Indicates whether this declarator has an initializer. LLVM_PREFERRED_TYPE(bool) unsigned HasInitializer : 1; @@ -2001,6 +2013,9 @@ class Declarator { /// this declarator as a parameter pack. SourceLocation EllipsisLoc; + /// The source location of the _Export keyword on this declarator. + SourceLocation ExportLoc; + Expr *PackIndexingExpr; friend struct DeclaratorChunk; @@ -2030,7 +2045,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 +2125,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,8 +2153,10 @@ class Declarator { HasInitializer = false; ObjCIvar = false; ObjCWeakProperty = false; + ExportSpecified = false; CommaLoc = SourceLocation(); EllipsisLoc = SourceLocation(); + ExportLoc = SourceLocation(); PackIndexingExpr = nullptr; } diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 19343eb0af092..a08b2a81b61fe 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -2278,6 +2278,39 @@ class Sema final : public SemaBase { ActOnPragmaMSFunction(SourceLocation Loc, const llvm::SmallVectorImpl &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> TypeList; + SourceLocation NameLoc; + bool HasTypeList; + Qualifiers CVQual; + NestedNameSpecifier + *NestedNameId; // Nested name identifier for type lookup. + bool Used; + }; + + bool typeListMatchesSymbolLabel(FunctionDecl *FD, + const clang::Sema::SymbolLabel &Label); + + /// tryLookupSymbolLabel try to look up a decl matching the nested + // specifier with optional type list. + NamedDecl *tryLookupSymbolLabel(const clang::Sema::SymbolLabel &Label); + + bool isNamedDeclSameAsSymbolLabel(NamedDecl *D, + clang::Sema::SymbolLabel &Label); + + typedef SmallVector PendingPragmaExportOverloads; + llvm::DenseMap + PendingExportedNames; + + /// ActonPragmaExport - called on well-formed '\#pragma export'. + void ActOnPragmaExport(NestedNameSpecifier *NestedId, + SourceLocation ExportNameLoc, + std::optional> &&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. @@ -3905,6 +3938,9 @@ class Sema final : public SemaBase { /// a C++0x [dcl.typedef]p2 alias-declaration: 'using T = A;'. NamedDecl *ActOnTypedefNameDecl(Scope *S, DeclContext *DC, TypedefNameDecl *D, LookupResult &Previous, bool &Redeclaration); + + void ProcessPragmaExport(DeclaratorDecl *newDecl); + NamedDecl *ActOnVariableDeclarator(Scope *S, Declarator &D, DeclContext *DC, TypeSourceInfo *TInfo, LookupResult &Previous, @@ -4861,6 +4897,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 c5ad3ef1b00f1..bbbd0e53ec0a4 100644 --- a/clang/lib/Driver/ToolChains/ZOS.cpp +++ b/clang/lib/Driver/ToolChains/ZOS.cpp @@ -37,6 +37,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"); @@ -153,11 +157,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 4fe3565687905..443052883ec09 100644 --- a/clang/lib/Parse/ParseDecl.cpp +++ b/clang/lib/Parse/ParseDecl.cpp @@ -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; + break; + // friend case tok::kw_friend: if (DSContext == DeclSpecContext::DSC_class) { @@ -6831,6 +6836,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 6ee2cd31311ef..6643ad0ce317c 100644 --- a/clang/lib/Parse/ParseDeclCXX.cpp +++ b/clang/lib/Parse/ParseDeclCXX.cpp @@ -1752,6 +1752,11 @@ void Parser::ParseClassSpecifier(tok::TokenKind TagTokKind, // If attributes exist after tag, parse them. for (;;) { MaybeParseAttributes(PAKM_CXX11 | PAKM_Declspec | PAKM_GNU, attrs); + 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 69f3568cfdba7..86e7183388838 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(); PP.AddPragmaHandler("clang", MaxTokensTotalPragmaHandler.get()); + if (getLangOpts().ZOSExt) { + ExportHandler = std::make_unique(); + PP.AddPragmaHandler(ExportHandler.get()); + } + if (getTargetInfo().getTriple().isRISCV()) { RISCVPragmaHandler = std::make_unique(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.RemovePragmaHandler("clang", RISCVPragmaHandler.get()); RISCVPragmaHandler.reset(); @@ -1400,6 +1416,171 @@ bool Parser::HandlePragmaMSAllocText(StringRef PragmaName, return true; } +NestedNameSpecifier * +Parser::zOSParseIdentifier(StringRef PragmaName, + const IdentifierInfo *IdentName) { + NestedNameSpecifier *NestedId = nullptr; + if (PP.getLangOpts().CPlusPlus) { + if (Tok.is(tok::coloncolon)) { + // Nothing to do. + } else if (Actions.CurContext->isNamespace()) { + auto *NS = cast(Actions.CurContext); + NestedId = + NestedNameSpecifier::Create(Actions.Context, NS->getIdentifier()); + NestedId = + NestedNameSpecifier::Create(Actions.Context, NestedId, IdentName); + PP.Lex(Tok); + } else { + NestedId = NestedNameSpecifier::Create(Actions.Context, IdentName); + PP.Lex(Tok); + } + while (Tok.is(tok::coloncolon)) { + PP.Lex(Tok); + if (Tok.isNot(tok::identifier)) { + PP.Diag(Tok.getLocation(), diag::warn_pragma_expected_identifier) + << PragmaName; + return nullptr; + } + IdentifierInfo *II = Tok.getIdentifierInfo(); + NestedId = NestedNameSpecifier::Create(Actions.Context, NestedId, II); + PP.Lex(Tok); + } + } else { + NestedId = NestedNameSpecifier::Create(Actions.Context, IdentName); + PP.Lex(Tok); + } + return NestedId; +} + +bool Parser::zOSParseParameterList( + StringRef PragmaName, std::optional> &TypeList, + Qualifiers &CVQual) { + if (Tok.is(tok::l_paren)) { + TypeList = SmallVector(); + PP.Lex(Tok); + while (Tok.isNot(tok::eof) && !Tok.is(tok::r_paren)) { + TypeResult TResult = ParseTypeName(nullptr); + if (!TResult.isInvalid()) { + QualType QT = TResult.get().get(); + if (!QT.getTypePtr()->isVoidType()) { + TypeList->push_back(QT); + } + } + if (Tok.is(tok::comma) || Tok.is(tok::identifier)) + PP.Lex(Tok); + } + if (Tok.is(tok::r_paren)) + PP.Lex(Tok); + else { + // We ate the whole line trying to find the right paren of the parameter + // list. + PP.Diag(Tok.getLocation(), diag::warn_pragma_expected_identifier) + << PragmaName; + return false; + } + + if (TypeList.has_value()) + while (Tok.is(tok::kw_const) || Tok.is(tok::kw_volatile)) { + if (Tok.is(tok::kw_const)) { + CVQual.addConst(); + } else { + assert(Tok.is(tok::kw_volatile)); + CVQual.addVolatile(); + } + PP.Lex(Tok); + } + } + return true; +} + +bool Parser::zOSHandlePragmaHelper(tok::TokenKind PragmaKind) { + assert(Tok.is(PragmaKind)); + + bool IsPragmaExport = PragmaKind == tok::annot_pragma_export; + assert(IsPragmaExport); + StringRef PragmaName = "export"; + + using namespace clang::charinfo; + auto *TheTokens = + (std::pair, size_t> *)Tok.getAnnotationValue(); + PP.EnterTokenStream(std::move(TheTokens->first), TheTokens->second, true, + false); + ConsumeAnnotationToken(); + + do { + PP.Lex(Tok); + if (Tok.isNot(tok::l_paren)) { + PP.Diag(Tok.getLocation(), diag::warn_pragma_expected_lparen) + << PragmaName; + return false; + } + + // C++ could have a nested name, or be qualified with ::. + PP.Lex(Tok); + if (Tok.isNot(tok::identifier) && + !(PP.getLangOpts().CPlusPlus && Tok.is(tok::coloncolon))) { + PP.Diag(Tok.getLocation(), diag::warn_pragma_expected_identifier) + << PragmaName; + return false; + } + + IdentifierInfo *IdentName = Tok.getIdentifierInfo(); + SourceLocation IdentNameLoc = Tok.getLocation(); + NestedNameSpecifier *NestedId = zOSParseIdentifier(PragmaName, IdentName); + if (!NestedId) + return false; + + // C++ can have a paramater list for overloaded functions. + // Try to parse the argument types. + std::optional> TypeList; + Qualifiers CVQual; + + if (PP.getLangOpts().CPlusPlus && Tok.is(tok::l_paren)) { + if (!zOSParseParameterList(PragmaName, TypeList, CVQual)) + return false; + } + + if (Tok.isNot(tok::r_paren)) { + PP.Diag(Tok.getLocation(), diag::warn_pragma_expected_rparen) + << PragmaName; + return false; + } + + PP.Lex(Tok); + Actions.ActOnPragmaExport(NestedId, IdentNameLoc, std::move(TypeList), + CVQual); + + // Because export is also a C++ keyword, we also check for that. + if (Tok.is(tok::identifier) || Tok.is(tok::kw_export)) { + IsPragmaExport = false; + PragmaName = Tok.getIdentifierInfo()->getName(); + if (PragmaName == "export") + IsPragmaExport = true; + else + PP.Diag(Tok.getLocation(), diag::warn_pragma_extra_tokens_at_eol) + << PragmaName; + } else if (Tok.isNot(tok::eof)) { + PP.Diag(Tok.getLocation(), diag::warn_pragma_extra_tokens_at_eol) + << PragmaName; + return false; + } + } while (Tok.isNot(tok::eof)); + PP.Lex(Tok); + return true; +} + +void Parser::HandlePragmaExport() { + assert(Tok.is(tok::annot_pragma_export)); + + if (!zOSHandlePragmaHelper(tok::annot_pragma_export)) { + // Parsing pragma failed, and has been diagnosed. Slurp up the + // tokens until eof (really end of line) to prevent follow-on errors. + while (Tok.isNot(tok::eof)) + PP.Lex(Tok); + PP.Lex(Tok); + } +} + static std::string PragmaLoopHintString(Token PragmaName, Token Option) { StringRef Str = PragmaName.getIdentifierInfo()->getName(); std::string ClangLoopStr("clang loop "); @@ -4147,6 +4328,44 @@ void PragmaMaxTokensTotalHandler::HandlePragma(Preprocessor &PP, PP.overrideMaxTokens(MaxTokens, Loc); } +/// Helper function for handling z/OS pragmas like #pragma export. +static void zOSPragmaHandlerHelper(Preprocessor &PP, Token &Tok, + tok::TokenKind TokKind) { + Token EoF, AnnotTok; + EoF.startToken(); + EoF.setKind(tok::eof); + AnnotTok.startToken(); + AnnotTok.setKind(TokKind); + AnnotTok.setLocation(Tok.getLocation()); + AnnotTok.setAnnotationEndLoc(Tok.getLocation()); + SmallVector 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. + 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(TokenVector.size()); + std::copy(TokenVector.begin(), TokenVector.end(), TokenArray.get()); + auto Value = new (PP.getPreprocessorAllocator()) + std::pair, size_t>(std::move(TokenArray), + TokenVector.size()); + AnnotTok.setAnnotationValue(Value); + PP.EnterToken(AnnotTok, /*IsReinject*/ false); +} + +/// Handle #pragma export. +void PragmaExportHandler::HandlePragma(Preprocessor &PP, + PragmaIntroducer Introducer, + Token &FirstToken) { + zOSPragmaHandlerHelper(PP, FirstToken, tok::annot_pragma_export); +} + // Handle '#pragma clang riscv intrinsic vector'. // '#pragma clang riscv intrinsic sifive_vector'. void PragmaRISCVHandler::HandlePragma(Preprocessor &PP, diff --git a/clang/lib/Parse/ParseStmt.cpp b/clang/lib/Parse/ParseStmt.cpp index 546e524932f5e..a515a5cd098f1 100644 --- a/clang/lib/Parse/ParseStmt.cpp +++ b/clang/lib/Parse/ParseStmt.cpp @@ -536,6 +536,12 @@ StmtResult Parser::ParseStatementOrDeclarationAfterAttributes( ProhibitAttributes(GNUAttrs); HandlePragmaAttribute(); return StmtEmpty(); + + case tok::annot_pragma_export: + ProhibitAttributes(CXX11Attrs); + ProhibitAttributes(GNUAttrs); + HandlePragmaExport(); + return StmtEmpty(); } // If we reached this code, the statement must end in a semicolon. @@ -1124,6 +1130,9 @@ void Parser::ParseCompoundStatementLeadingPragmas() { case tok::annot_pragma_dump: HandlePragmaDump(); break; + case tok::annot_pragma_export: + HandlePragmaExport(); + break; default: checkForPragmas = false; break; diff --git a/clang/lib/Parse/Parser.cpp b/clang/lib/Parse/Parser.cpp index cab0604821c03..11d229de8a1f5 100644 --- a/clang/lib/Parse/Parser.cpp +++ b/clang/lib/Parse/Parser.cpp @@ -908,6 +908,9 @@ Parser::ParseExternalDeclaration(ParsedAttributes &Attrs, case tok::annot_pragma_attribute: HandlePragmaAttribute(); return nullptr; + case tok::annot_pragma_export: + HandlePragmaExport(); + return nullptr; case tok::semi: // Either a C++11 empty-declaration or attribute-declaration. SingleDecl = diff --git a/clang/lib/Sema/DeclSpec.cpp b/clang/lib/Sema/DeclSpec.cpp index ee5a862c32509..e1cafc3d0f977 100644 --- a/clang/lib/Sema/DeclSpec.cpp +++ b/clang/lib/Sema/DeclSpec.cpp @@ -1104,6 +1104,12 @@ bool DeclSpec::setFunctionSpecNoreturn(SourceLocation Loc, return false; } +bool DeclSpec::setExportSpec(SourceLocation Loc) { + ExportSpecified = true; + ExportLoc = Loc; + return false; +} + bool DeclSpec::SetFriendSpec(SourceLocation Loc, const char *&PrevSpec, unsigned &DiagID) { if (isFriendSpecified()) { diff --git a/clang/lib/Sema/Sema.cpp b/clang/lib/Sema/Sema.cpp index 1901d19b14dfc..0f3af893343f2 100644 --- a/clang/lib/Sema/Sema.cpp +++ b/clang/lib/Sema/Sema.cpp @@ -1477,6 +1477,12 @@ void Sema::ActOnEndOfTranslationUnit() { Consumer.CompleteExternalDeclaration(D); } + // Visit all pending #pragma export. + for (auto &Iter : PendingExportedNames) + for (auto &Exported : Iter.second) + if (!Exported.Used) + Diag(Exported.NameLoc, diag::warn_failed_to_resolve_pragma) << "export"; + if (LangOpts.HLSL) HLSL().ActOnEndOfTranslationUnit(getASTContext().getTranslationUnitDecl()); diff --git a/clang/lib/Sema/SemaAttr.cpp b/clang/lib/Sema/SemaAttr.cpp index 44726c4cea123..de00dad5a17a7 100644 --- a/clang/lib/Sema/SemaAttr.cpp +++ b/clang/lib/Sema/SemaAttr.cpp @@ -1328,6 +1328,172 @@ void Sema::AddImplicitMSFunctionNoBuiltinAttr(FunctionDecl *FD) { FD->addAttr(NoBuiltinAttr::CreateImplicit(Context, V.data(), V.size())); } +static QualType getCanonicalParamType(ASTContext &C, QualType T) { + return C.getCanonicalParamType(T); +} + +bool Sema::typeListMatchesSymbolLabel(FunctionDecl *FD, + const clang::Sema::SymbolLabel &Label) { + assert(Label.TypeList.has_value()); + if (FD->getNumParams() != Label.TypeList->size()) { + return false; + } + + // Check if arguments match. + for (unsigned i = 0; i != FD->getNumParams(); ++i) { + const ParmVarDecl *PVD = FD->getParamDecl(i); + QualType ParmType = PVD->getType().getCanonicalType(); + + QualType MapArgType = + getCanonicalParamType(Context, (*Label.TypeList)[i].getCanonicalType()); + + if (ParmType != MapArgType) + return false; + } + + if (isa(FD)) { + // Check if CV qualifiers match. + const clang::CXXMethodDecl *const MFD = + clang::cast(FD); + if (MFD && (MFD->isConst() != Label.CVQual.hasConst() || + MFD->isVolatile() != Label.CVQual.hasVolatile())) { + return false; + } + } else if (Label.CVQual.hasConst() || Label.CVQual.hasVolatile()) + return false; + + return true; +} + +NamedDecl *Sema::tryLookupSymbolLabel(const clang::Sema::SymbolLabel &Label) { + + NestedNameSpecifier *NestedName = Label.NestedNameId; + assert(!NestedName->getPrefix() || + NestedName->getPrefix()->getKind() == NestedNameSpecifier::Identifier); + IdentifierInfo *Prefix = + NestedName->getPrefix() ? NestedName->getPrefix()->getAsIdentifier() : 0; + IdentifierInfo *Name = NestedName->getAsIdentifier(); + LookupResult Result(*this, (Prefix ? Prefix : Name), Label.NameLoc, + LookupOrdinaryName); + LookupName(Result, TUScope); + + // Filter down to just a function, namespace or class. + LookupResult::Filter F = Result.makeFilter(); + while (F.hasNext()) { + NamedDecl *D = F.next(); + if (!(isa(D) || isa(D) || isa(D) || + isa(D))) + F.erase(); + } + F.done(); + + auto MatchDecl = [this, Name, Label](DeclContext *DC) -> NamedDecl * { + auto LRes = DC->lookup(DeclarationName(Name)); + for (auto *I : LRes) { + if (isa(I)) + return I; + if (isa(I)) { + FunctionDecl *FD = dyn_cast(I); + + // All function parameters must match if specified in pragma otherwise, + // we accept a function found by lookup only if it's the only one. + if ((Label.TypeList.has_value() && + typeListMatchesSymbolLabel(FD, Label)) || + (!Label.TypeList.has_value() && LRes.isSingleResult())) + return FD; + } + } + return nullptr; + }; + + // global variable or function in a namespace. + if (NamespaceDecl *ND = Result.getAsSingle()) { + if (ND->getIdentifierNamespace() == Decl::IDNS_Namespace) { + return MatchDecl(ND); + } + } + + // data or function member. + if (CXXRecordDecl *RD = Result.getAsSingle()) { + return MatchDecl(RD); + } + + // either a variable, or a non-overloaded function, or an overloaded + // function with extern "C" linkage. + if (!Label.TypeList.has_value()) { + if (Result.isSingleResult()) { + NamedDecl *ND = Result.getFoundDecl(); + if (isa(ND)) + return ND; + if (FunctionDecl *FD = dyn_cast(ND)) { + if (!getLangOpts().CPlusPlus || FD->isExternC()) + return FD; + else + return nullptr; + } + return ND; + } + if (Result.isOverloadedResult()) { + for (auto *Iter : Result) { + FunctionDecl *FD = dyn_cast(Iter); + if (FD && FD->isExternC()) + return FD; + } + return nullptr; + } + return nullptr; + } + + // Loop over all the found decls and see if the arguments match + // any of the results. + for (LookupResult::iterator I = Result.begin(); I != Result.end(); ++I) { + NamedDecl *ND = (*I)->getUnderlyingDecl(); + FunctionDecl *FD = dyn_cast(ND); + if (FD && typeListMatchesSymbolLabel(FD, Label)) { + return FD; + } + } + return nullptr; +} + +void Sema::ActOnPragmaExport(NestedNameSpecifier *NestedId, + SourceLocation NameLoc, + std::optional> &&TypeList, + Qualifiers CVQual) { + SymbolLabel Label; + Label.NameLoc = NameLoc; + Label.CVQual = CVQual; + Label.TypeList = std::move(TypeList); + Label.NestedNameId = NestedId; + Label.Used = false; + + NamedDecl *PrevDecl = tryLookupSymbolLabel(Label); + if (PrevDecl && (isa(PrevDecl) || isa(PrevDecl))) { + if (PrevDecl->hasExternalFormalLinkage()) { + if (auto *FD = dyn_cast(PrevDecl)) { + if (FD->hasBody()) + Diag(NameLoc, diag::warn_pragma_not_applied_to_defined_symbol) + << "export"; + else + mergeVisibilityType(PrevDecl, NameLoc, VisibilityAttr::Default); + } else { + auto *VD = dyn_cast(PrevDecl); + assert(VD); + if (VD->hasDefinition() == VarDecl::Definition) + Diag(NameLoc, diag::warn_pragma_not_applied_to_defined_symbol) + << "export"; + else + mergeVisibilityType(PrevDecl, NameLoc, VisibilityAttr::Default); + } + } else + Diag(NameLoc, diag::warn_pragma_not_applied) << "export" << PrevDecl; + Label.Used = true; + } + if (!Label.Used) { + PendingExportedNames[NestedId->getAsIdentifier()].push_back(Label); + } +} + typedef std::vector > VisStack; enum : unsigned { NoVisibility = ~0U }; diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index 63937ddc3e386..05ff36b3d9e15 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -5168,6 +5168,9 @@ Decl *Sema::ParsedFreeStandingDeclSpec(Scope *S, AccessSpecifier AS, assert(EllipsisLoc.isInvalid() && "Friend ellipsis but not friend-specified?"); + if (DS.isExportSpecified()) + mergeVisibilityType(Tag, DS.getExportSpecLoc(), VisibilityAttr::Default); + // Track whether this decl-specifier declares anything. bool DeclaresAnything = true; @@ -6508,6 +6511,9 @@ NamedDecl *Sema::HandleDeclarator(Scope *S, Declarator &D, if (!New) return nullptr; + if (D.IsExport()) + mergeVisibilityType(New, D.getExportLoc(), VisibilityAttr::Default); + warnOnCTypeHiddenInCPlusPlus(New); // If this has an identifier and is not a function template specialization, @@ -6747,6 +6753,9 @@ Sema::ActOnTypedefDeclarator(Scope* S, Declarator& D, DeclContext* DC, return nullptr; } + if (D.IsExport()) + Diag(D.getName().StartLocation, diag::err_cannot_be_exported); + TypedefDecl *NewTD = ParseTypedefDecl(S, D, TInfo->getType(), TInfo); if (!NewTD) return nullptr; @@ -7507,6 +7516,109 @@ static void emitReadOnlyPlacementAttrWarning(Sema &S, const VarDecl *VD) { } } +// Checks if the given label matches the named declaration. +bool Sema::isNamedDeclSameAsSymbolLabel(NamedDecl *D, + Sema::SymbolLabel &Label) { + const DeclContext *Ctx = D->getDeclContext(); + + // Check the name. + NestedNameSpecifier *NS = Label.NestedNameId; + if (NS->getAsIdentifier()->getName() != D->getIdentifier()->getName()) + return false; + NS = NS->getPrefix(); + + if (NS) { + // For ObjC methods and properties, look through categories and use the + // interface as context. + if (auto *MD = dyn_cast(D)) { + if (auto *ID = MD->getClassInterface()) + Ctx = ID; + } else if (auto *PD = dyn_cast(D)) { + if (auto *MD = PD->getGetterMethodDecl()) + if (auto *ID = MD->getClassInterface()) + Ctx = ID; + } else if (auto *ID = dyn_cast(D)) { + if (auto *CI = ID->getContainingInterface()) + Ctx = CI; + } + + // Check named contexts. + if (Ctx->isFunctionOrMethod()) + return false; + + DeclarationName NameInScope = D->getDeclName(); + for (; NS && Ctx; Ctx = Ctx->getParent()) { + // Suppress anonymous namespace. + if (isa(Ctx) && + cast(Ctx)->isAnonymousNamespace()) + continue; + + // Suppress inline namespace if it doesn't make the result ambiguous. + if (Ctx->isInlineNamespace() && NameInScope && + cast(Ctx)->isRedundantInlineQualifierFor(NameInScope)) + continue; + + // Skip non-named contexts such as linkage specifications and ExportDecls. + const NamedDecl *ND = dyn_cast(Ctx); + if (!ND) + continue; + + // Fail if the sequence of nested name identifiers is shorter. + if (!NS) + return false; + + // Fail if the names are not equal. + if (NS->getAsIdentifier()->getName() != ND->getIdentifier()->getName()) + return false; + + NameInScope = ND->getDeclName(); + NS = NS->getPrefix(); + } + + // Fail if the sequence of nested name identifiers is longer. + // It makes sure that both lists have the same length. + if (NS) + return false; + } + + if (isa(D) && !Label.TypeList.has_value()) + return true; + if (FunctionDecl *FD = dyn_cast(D)) { + // All function parameters match if specified in pragma. + if (Label.TypeList.has_value()) + return typeListMatchesSymbolLabel(FD, Label); + // There might be overloaded functions. However, with the available + // information it cn only be concluded that the functions are the same. + if (!getLangOpts().CPlusPlus || FD->isExternC()) + return true; + } + + return false; +} + +void Sema::ProcessPragmaExport(DeclaratorDecl *NewD) { + if (PendingExportedNames.empty()) + return; + IdentifierInfo *IdentName = NewD->getIdentifier(); + if (IdentName == nullptr) + return; + auto PendingName = PendingExportedNames.find(IdentName); + if (PendingName != PendingExportedNames.end()) { + for (auto I = PendingName->second.begin(), E = PendingName->second.end(); + I != E; ++I) { + auto &Label = *I; + if (!Label.Used && isNamedDeclSameAsSymbolLabel(NewD, Label)) { + 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() && @@ -8205,6 +8317,10 @@ NamedDecl *Sema::ActOnVariableDeclarator( CheckShadow(NewVD, ShadowedDecl, Previous); ProcessPragmaWeak(S, NewVD); + ProcessPragmaExport(NewVD); + + if (D.IsExport() && !NewVD->hasExternalFormalLinkage()) + Diag(D.getIdentifierLoc(), diag::err_cannot_be_exported); // If this is the first declaration of an extern C variable, update // the map of such variables. @@ -10855,6 +10971,11 @@ Sema::ActOnFunctionDeclarator(Scope *S, Declarator &D, DeclContext *DC, } ProcessPragmaWeak(S, NewFD); + ProcessPragmaExport(NewFD); + + if (D.IsExport() && !NewFD->hasExternalFormalLinkage()) + Diag(D.getIdentifierLoc(), diag::err_cannot_be_exported); + checkAttributesAfterMerging(*this, *NewFD); AddKnownFunctionAttributes(NewFD); @@ -15412,6 +15533,9 @@ Decl *Sema::ActOnParamDeclarator(Scope *S, Declarator &D, if (getLangOpts().OpenCL) deduceOpenCLAddressSpace(New); + if (D.IsExport()) + Diag(D.getIdentifierLoc(), diag::err_cannot_be_exported); + return New; } @@ -18794,6 +18918,9 @@ FieldDecl *Sema::HandleField(Scope *S, RecordDecl *Record, } else Record->addDecl(NewFD); + if (D.IsExport()) + Diag(D.getIdentifierLoc(), diag::err_cannot_be_exported); + return NewFD; } diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp index bfb3ee9dcbd16..92e705a759f79 100644 --- a/clang/lib/Sema/SemaDeclAttr.cpp +++ b/clang/lib/Sema/SemaDeclAttr.cpp @@ -2632,6 +2632,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()) { + if (Attr->getVisibility() != Value) + Diag(Loc, diag::err_mismatched_visibility); + } else + D->addAttr(VisibilityAttr::CreateImplicit(Context, Value)); +} + template static T *mergeVisibilityAttr(Sema &S, Decl *D, const AttributeCommonInfo &CI, typename T::VisibilityType value) { diff --git a/clang/test/CodeGen/attr-export.cpp b/clang/test/CodeGen/attr-export.cpp new file mode 100644 index 0000000000000..23eb5163d130e --- /dev/null +++ b/clang/test/CodeGen/attr-export.cpp @@ -0,0 +1,52 @@ +// REQUIRES: systemz-registered-target +// RUN: %clangxx --target=s390x-none-zos -S -emit-llvm %s -o - | FileCheck %s + +// Check the variables +// CHECK: @var1 = global i32 0, align 4 +// CHECK: @var2 = hidden global i32 0, align 4 +// CHECK: @var3 = global i32 0, align 4 +// CHECK: @var4 = hidden global i32 0, align 4 +// CHECK: @var5 = global i32 0, align 4 +// CHECK: @obj1 = global %class.class1 zeroinitializer, align 2 +// CHECK: @obj2 = hidden global %class.class1 zeroinitializer, align 2 + +// Check the functions +// CHECK: define void @_Z4foo1v +// CHECK: define hidden void @_Z4foo2v +// CHECK: define void @_ZN6class13fooEv +// CHECK: define hidden void @_ZN6class23fooEv +// CHECK: define hidden void @_ZN6class33fooEv +// CHECK: define void @_ZN6class33barEv + +int _Export var1; +int var2; +int _Export var3, var4, _Export var5; + +void _Export foo1(){}; +void foo2(){}; + +class _Export class1 { +public: + void foo(); +}; + +class class2 { +public: + void foo(); +}; + +void class1::foo(){}; + +void class2::foo(){}; + +class1 _Export obj1; +class1 obj2; + +class class3 { +public: + void foo(); + void _Export bar(); +}; + +void class3::foo() {}; +void class3::bar() {}; diff --git a/clang/test/CodeGen/pragma-export.c b/clang/test/CodeGen/pragma-export.c new file mode 100644 index 0000000000000..094fd0c6206b7 --- /dev/null +++ b/clang/test/CodeGen/pragma-export.c @@ -0,0 +1,44 @@ +// 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; +#pragma export(local) +#pragma export(l2) + 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..aa780887bd272 --- /dev/null +++ b/clang/test/CodeGen/pragma-export.cpp @@ -0,0 +1,122 @@ +// 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. +void f0(void) {} +int v0; +#pragma export(f0(void)) +#pragma export(v0) + +// Testing pragma export before decl. +#pragma export(f1(void)) +#pragma export(v1) +void f1(void) {} +int v1; + +// Testing overloaded functions. +#pragma export(f2(double, double)) +#pragma export(f2(int)) +void f2(double, double) {} +void f2(int) {} +void f2(int, int) {} + +void f3(double) {} +void f3(int, double) {} +void f3(double, double) {} +#pragma export(f3(double)) +#pragma export(f3(int, double)) + +void f2(void) {} + +void t0(void) { + f2(); +} + +// Test type decay in arguments + +#pragma export(fd1(int[])) +#pragma export(fd2(int*)) +#pragma export(fd3(int[])) +#pragma export(fd4(int*)) +void fd1(int []) { } +void fd2(int []) { } +void fd3(int *) { } +void fd4(int *) { } + + +#pragma export (fd5(int ())) +#pragma export (fd6(int (*)())) +#pragma export (fd7(int ())) +#pragma export (fd8(int (*)())) +void fd5(int ()) {} +void fd6(int ()) {} +void fd7(int (*)()) {} +void fd8(int (*)()) {} + + +// Testing pragma export after decl and usage. +#pragma export(f2(void)) + +// Testing pragma export with namespace. +void f5(void) {} +void f5a(void) {} +#pragma export(N0::f2a(void)) +namespace N0 { +void f0(void) {} +void f1(void) {} +void f2(void) {} +void f3(void) {} +void f5(void) {} +#pragma export(f0(void)) +#pragma export(N0::f1(void)) +#pragma export(f5(void)) +#pragma export(f0a(void)) +#pragma export(N0::f1a(void)) +#pragma export(f5a(void)) +void f0a(void) {} +void f1a(void) {} +void f2a(void) {} +void f3a(void) {} +void f5a(void) {} +} // namespace N0 +#pragma export(N0::f2(void)) + +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 @_Z2f0v() +// CHECK: define void @_Z2f1v() +// CHECK: define void @_Z2f2dd(double noundef %0, double noundef %1) +// CHECK: define void @_Z2f2i(i32 noundef signext %0) +// CHECK: define hidden void @_Z2f2ii(i32 noundef signext %0, i32 noundef signext %1) +// CHECK: define hidden void @_Z2f3d(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 @_Z2f2v() +// CHECK: define hidden void @_Z2t0v() +// CHECK: define void @_Z3fd1Pi(ptr noundef %0) +// CHECK: define void @_Z3fd2Pi(ptr noundef %0) +// CHECK: define void @_Z3fd3Pi(ptr noundef %0) +// CHECK: define void @_Z3fd4Pi(ptr noundef %0) +// CHECK: define void @_Z3fd5PFivE(ptr noundef %0) +// CHECK: define void @_Z3fd6PFivE(ptr noundef %0) +// CHECK: define void @_Z3fd7PFivE(ptr noundef %0) +// CHECK: define void @_Z3fd8PFivE(ptr noundef %0) +// CHECK: define hidden void @_Z2f5v() +// CHECK: define hidden void @_Z3f5av() +// CHECK: define hidden void @_ZN2N02f0Ev() +// CHECK: define hidden void @_ZN2N02f1Ev() +// CHECK: define hidden void @_ZN2N02f2Ev() +// CHECK: define hidden void @_ZN2N02f3Ev() +// CHECK: define hidden void @_ZN2N02f5Ev() +// CHECK: define void @_ZN2N03f0aEv() +// CHECK: define hidden void @_ZN2N03f1aEv() +// CHECK: define void @_ZN2N03f2aEv() +// CHECK: define hidden void @_ZN2N03f3aEv() +// CHECK: define void @_ZN2N03f5aEv() +// CHECK: define void @f10(double noundef %0) #0 { +// CHECK: define hidden void @_Z3f10i(i32 noundef signext %0) #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..5f1656041b3d3 --- /dev/null +++ b/clang/test/Parser/pragma-export.cpp @@ -0,0 +1,21 @@ +// 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:: // expected-warning {{expected identifier in '#pragma export' - ignored}} +#pragma export(S::i // expected-warning {{missing ')' after '#pragma export' - ignoring}} +#pragma export(S::i) + +void f(int); +void f(double, double); +#pragma export(f( // expected-warning {{expected identifier in '#pragma export' - ignored}} +#pragma export(f() // expected-warning {{missing ')' after '#pragma export' - ignoring}} +#pragma export(f(int) // expected-warning {{missing ')' after '#pragma export' - ignoring}} +#pragma export(f(double,) // expected-warning {{missing ')' after '#pragma export' - ignoring}} +#pragma export(f(double,double)) diff --git a/clang/test/Sema/attr-export-failing.cpp b/clang/test/Sema/attr-export-failing.cpp new file mode 100644 index 0000000000000..14c22c96f2f1f --- /dev/null +++ b/clang/test/Sema/attr-export-failing.cpp @@ -0,0 +1,4 @@ +// REQUIRES: systemz-registered-target +// RUN: %clang_cc1 -triple s390x-none-zos -fzos-extensions %s -fsyntax-only -verify +__attribute__((visibility("hidden"))) int _Export i; // expected-error {{visibility does not match previous declaration}} +class __attribute__((visibility("hidden"))) _Export C; // expected-error {{visibility does not match previous declaration}} diff --git a/clang/test/Sema/pragma-export-failing.c b/clang/test/Sema/pragma-export-failing.c new file mode 100644 index 0000000000000..57bf97e628e32 --- /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() { + int local; +#pragma export(local) // expected-warning{{#pragma export is applicable to symbols with external linkage only; not applied to 'local'}} +#pragma export(l2) // expected-warning{{#pragma export is applicable to symbols with external linkage only; not applied to 'l2'}} + int l2; + 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..908395c899cf5 --- /dev/null +++ b/clang/test/Sema/pragma-export-failing.cpp @@ -0,0 +1,25 @@ +// 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{{failed to resolve '#pragma export' to a declaration}} +#pragma export(f3(double, double, double)) // expected-warning{{failed to resolve '#pragma export' to a declaration}} + +#pragma export(N::sf1(void)) // expected-warning{{#pragma export is applicable to symbols with external linkage only; not applied to 'sf1'}} +#pragma export(N::s1) // expected-warning{{#pragma export is applicable to symbols with external linkage only; not applied to 's1'}} +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{{#pragma export is applicable to symbols with external linkage only; not applied to 'sf0'}} +#pragma export(N::s0) // expected-warning{{#pragma export is applicable to symbols with external linkage only; not applied to 's0'}} + +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); + diff --git a/clang/test/Sema/zos-export.c b/clang/test/Sema/zos-export.c new file mode 100644 index 0000000000000..dd6d754690246 --- /dev/null +++ b/clang/test/Sema/zos-export.c @@ -0,0 +1,25 @@ +// RUN: %clang_cc1 -triple s390x-ibm-zos %s -fsyntax-only -verify + +typedef int _Export ty; //expected-error {{cannot be '_Export` qualified}} +ty x; +int f(int _Export argument); //expected-error {{cannot be '_Export` qualified}} +static int _Export file_scope_static; //expected-error {{cannot be '_Export` qualified}} +struct S { + int _Export nonstaticdatamember; //expected-error {{cannot be '_Export` qualified}} +}; +void g() { + int _Export automatic; //expected-error {{cannot be '_Export` qualified}} +} + +static void _Export static_func() { //expected-error {{cannot be '_Export` qualified}} +} + +void _Export h() { + static_func(); +} + +void j() { + static int _Export sl = 0; //expected-error {{cannot be '_Export` qualified}} +} + +int _Export file_scope; diff --git a/clang/test/Sema/zos-export.cpp b/clang/test/Sema/zos-export.cpp new file mode 100644 index 0000000000000..1417c6396952c --- /dev/null +++ b/clang/test/Sema/zos-export.cpp @@ -0,0 +1,44 @@ +// RUN: %clang_cc1 -triple s390x-ibm-zos %s -fsyntax-only -verify + +typedef int _Export ty; //expected-error {{cannot be '_Export` qualified}} +ty typedef_var; +int f(int _Export argument); //expected-error {{cannot be '_Export` qualified}} +static int _Export file_scope_static; //expected-error {{cannot be '_Export` qualified}} +struct S { + int _Export nonstaticdatamember; //expected-error {{cannot be '_Export` qualified}} +}; +void g() { + int _Export automatic; //expected-error {{cannot be '_Export` qualified}} +} + +static void _Export static_func() { //expected-error {{cannot be '_Export` qualified}} +} + +void _Export h() { + static_func(); +} + +void j() { + static int _Export sl = 0; //expected-error {{cannot be '_Export` qualified}} +} + +int _Export file_scope; + +struct _Export SE { +}; + +struct ST { + void _Export f(); + virtual void _Export v_(); + static int _Export i; +}; + +namespace { + int _Export anon_var; //expected-error {{cannot be '_Export` qualified}} + extern "C" int _Export anon_C_var; + void _Export anon_f() {} //expected-error {{cannot be '_Export` qualified}} + extern "C" void _Export anon_C_f() {} + struct anon_S { + static int _Export anon_static_data_member; //expected-error {{cannot be '_Export` qualified}} + }; +}