diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index 88a05affebf9e..143f79e9aadd3 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -626,6 +626,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/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 3e864475f22a1..04292e28e5ba8 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -1220,6 +1220,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< diff --git a/clang/include/clang/Basic/TokenKinds.def b/clang/include/clang/Basic/TokenKinds.def index 564d6010181cc..18491aa6bb4f6 100644 --- a/clang/include/clang/Basic/TokenKinds.def +++ b/clang/include/clang/Basic/TokenKinds.def @@ -1019,6 +1019,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 58eb1c0a7c114..44d93d9a4abcb 100644 --- a/clang/include/clang/Parse/Parser.h +++ b/clang/include/clang/Parse/Parser.h @@ -7046,6 +7046,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; /// Initialize all pragma handlers. @@ -7167,6 +7168,12 @@ class Parser : public CodeCompletionHandler { void HandlePragmaAttribute(); + bool 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 163ab32fafa48..4e625853b1832 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -2309,6 +2309,22 @@ class Sema final : public SemaBase { ActOnPragmaMSFunction(SourceLocation Loc, const llvm::SmallVectorImpl &NoBuiltins); + NamedDecl *lookupExternCName(IdentifierInfo *IdentId, SourceLocation NameLoc, + Scope *curScope); + + /// A label from a C++ #pragma export, for a symbol that we + /// haven't seen the declaration for yet. + struct SymbolLabel { + SourceLocation NameLoc; + bool Used; + }; + + llvm::DenseMap 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. @@ -3836,6 +3852,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, @@ -4920,6 +4938,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 7c2b9280f0b76..2b99ca56992f9 100644 --- a/clang/lib/Parse/ParsePragma.cpp +++ b/clang/lib/Parse/ParsePragma.cpp @@ -395,6 +395,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 +564,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()); @@ -692,6 +703,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 +1402,74 @@ bool Parser::HandlePragmaMSAllocText(StringRef PragmaName, return true; } +bool Parser::zOSHandlePragmaHelper(tok::TokenKind PragmaKind) { + assert(Tok.is(PragmaKind)); + + 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; + } + + PP.Lex(Tok); + if (Tok.isNot(tok::identifier)) { + PP.Diag(Tok.getLocation(), diag::warn_pragma_expected_identifier) + << PragmaName; + return false; + } + + 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 false; + } + + 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 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 "); @@ -4133,6 +4217,43 @@ void PragmaMaxTokensTotalHandler::HandlePragma(Preprocessor &PP, PP.overrideMaxTokens(MaxTokens, Loc); } +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'. // '#pragma clang riscv intrinsic andes_vector'. diff --git a/clang/lib/Parse/ParseStmt.cpp b/clang/lib/Parse/ParseStmt.cpp index 7e73d89c2a18c..a8498e572323a 100644 --- a/clang/lib/Parse/ParseStmt.cpp +++ b/clang/lib/Parse/ParseStmt.cpp @@ -500,6 +500,11 @@ 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. @@ -1029,6 +1034,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 a6fc676f23a51..a870675fd2678 100644 --- a/clang/lib/Parse/Parser.cpp +++ b/clang/lib/Parse/Parser.cpp @@ -842,6 +842,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 46addea232b03..b9a32f44e0946 100644 --- a/clang/lib/Sema/Sema.cpp +++ b/clang/lib/Sema/Sema.cpp @@ -1495,6 +1495,13 @@ void Sema::ActOnEndOfTranslationUnit() { Consumer.CompleteExternalDeclaration(D); } + // Visit all pending #pragma export. + for (auto &Iter : PendingExportedNames) { + 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 8411a3da8322d..1c7e9833e4343 100644 --- a/clang/lib/Sema/SemaAttr.cpp +++ b/clang/lib/Sema/SemaAttr.cpp @@ -1327,6 +1327,60 @@ void Sema::AddImplicitMSFunctionNoBuiltinAttr(FunctionDecl *FD) { FD->addAttr(NoBuiltinAttr::CreateImplicit(Context, V.data(), V.size())); } +NamedDecl *Sema::lookupExternCName(IdentifierInfo *IdentId, + SourceLocation NameLoc, Scope *curScope) { + LookupResult Result(*this, IdentId, NameLoc, LookupOrdinaryName); + LookupName(Result, curScope); + if (!getLangOpts().CPlusPlus) + return Result.getAsSingle(); + for (LookupResult::iterator I = Result.begin(); I != Result.end(); ++I) { + NamedDecl *D = (*I)->getUnderlyingDecl(); + if (auto *FD = dyn_cast(D->getCanonicalDecl())) + if (FD->isExternC()) + return D; + if (isa(D->getCanonicalDecl())) + return D; + } + return nullptr; +} + +void Sema::ActOnPragmaExport(IdentifierInfo *IdentId, SourceLocation NameLoc, + Scope *curScope) { + SymbolLabel Label; + Label.NameLoc = NameLoc; + Label.Used = false; + + NamedDecl *PrevDecl = lookupExternCName(IdentId, NameLoc, curScope); + if (!PrevDecl) { + PendingExportedNames[IdentId] = Label; + return; + } + + if (auto *FD = dyn_cast(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(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 > VisStack; enum : unsigned { NoVisibility = ~0U }; diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index 25b89d65847ad..74650225b9929 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -7632,6 +7632,29 @@ static void emitReadOnlyPlacementAttrWarning(Sema &S, const VarDecl *VD) { } } +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()) { + auto &Label = PendingName->second; + if (!Label.Used) { + if (FunctionDecl *FD = dyn_cast(NewD)) { + if (getLangOpts().CPlusPlus && !FD->isExternC()) + return; + } + 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() && @@ -8346,6 +8369,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. @@ -11002,6 +11026,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 a9e7b44ac9d73..dcad96ba5c005 100644 --- a/clang/lib/Sema/SemaDeclAttr.cpp +++ b/clang/lib/Sema/SemaDeclAttr.cpp @@ -2764,6 +2764,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/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..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..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..9763adcff5f16 --- /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{{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); +