diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index 35ab1461e7b89..66195349fc63c 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -918,6 +918,11 @@ WebAssembly Support AVR Support ^^^^^^^^^^^ +SystemZ Support +^^^^^^^^^^^^^^^ + +- Add support for `#pragma export` for z/OS + DWARF Support in Clang ---------------------- diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index b63cc8a11b136..65e6cc265555a 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -1216,6 +1216,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 94e72fea56a68..5adf0519be8c8 100644 --- a/clang/include/clang/Basic/TokenKinds.def +++ b/clang/include/clang/Basic/TokenKinds.def @@ -1015,6 +1015,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 e6492b81dfff8..0fca8b786ac8c 100644 --- a/clang/include/clang/Parse/Parser.h +++ b/clang/include/clang/Parse/Parser.h @@ -7019,6 +7019,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. @@ -7136,6 +7137,17 @@ class Parser : public CodeCompletionHandler { void HandlePragmaAttribute(); + 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(); + ///@} // diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 6680a459a77fd..3eab936e899a6 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. @@ -3802,6 +3835,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, @@ -4872,6 +4907,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 d05202972825d..10e7bcf711068 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 4e67fd033b9aa..ac8f37c5e4c4c 100644 --- a/clang/lib/Parse/ParsePragma.cpp +++ b/clang/lib/Parse/ParsePragma.cpp @@ -405,6 +405,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) {} @@ -568,6 +574,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()); @@ -702,6 +713,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(); @@ -1395,6 +1411,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 "); @@ -4149,6 +4330,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 c788723023c8b..2b9e5d7f0c9df 100644 --- a/clang/lib/Parse/ParseStmt.cpp +++ b/clang/lib/Parse/ParseStmt.cpp @@ -488,6 +488,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. @@ -1012,6 +1017,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 db65c05cc114a..84094f347b4e1 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 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 555faf6aa8e5a..98ad21314285a 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -7513,6 +7513,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() && @@ -8211,6 +8314,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. @@ -10858,6 +10962,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 54bac40982eda..9193366a6e6a8 100644 --- a/clang/lib/Sema/SemaDeclAttr.cpp +++ b/clang/lib/Sema/SemaDeclAttr.cpp @@ -2631,6 +2631,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..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/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); +