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