Skip to content
Open
Show file tree
Hide file tree
Changes from 13 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions clang/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -582,6 +582,13 @@ WebAssembly Support
AVR Support
^^^^^^^^^^^

SystemZ Support
^^^^^^^^^^^^^^^

- Add support for `#pragma export` for z/OS. This is a pragma used to export functions and variables
with external linkage from shared libraries. It provides compatibility with the IBM XL C/C++
compiler.

DWARF Support in Clang
----------------------

Expand Down
10 changes: 10 additions & 0 deletions clang/include/clang/Basic/DiagnosticSemaKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -1220,6 +1220,16 @@ def err_pragma_pop_visibility_mismatch : Error<
"#pragma visibility pop with no matching #pragma visibility push">;
def note_surrounding_namespace_starts_here : Note<
"surrounding namespace with visibility attribute starts here">;
def warn_failed_to_resolve_pragma : Warning<
"failed to resolve '#pragma %0' to a declaration">,
InGroup<IgnoredPragmas>;
def warn_pragma_not_applied : Warning<
"#pragma %0 is applicable to symbols with external linkage only; "
"not applied to %1">,
InGroup<IgnoredPragmas>;
def warn_pragma_not_applied_to_defined_symbol : Warning<
"#pragma %0 can only applied before a symbol is defined">,
InGroup<IgnoredPragmas>;
def err_pragma_loop_invalid_argument_type : Error<
"invalid argument of type %0; expected an integer type">;
def err_pragma_loop_compatibility : Error<
Expand Down
3 changes: 3 additions & 0 deletions clang/include/clang/Basic/TokenKinds.def
Original file line number Diff line number Diff line change
Expand Up @@ -1019,6 +1019,9 @@ PRAGMA_ANNOTATION(pragma_fp)
// Annotation for the attribute pragma directives - #pragma clang attribute ...
PRAGMA_ANNOTATION(pragma_attribute)

// Annotation for C/C++ #pragma export(ident)
PRAGMA_ANNOTATION(pragma_export)

// Annotation for the riscv pragma directives - #pragma clang riscv intrinsic ...
PRAGMA_ANNOTATION(pragma_riscv)

Expand Down
7 changes: 7 additions & 0 deletions clang/include/clang/Parse/Parser.h
Original file line number Diff line number Diff line change
Expand Up @@ -7050,6 +7050,7 @@ class Parser : public CodeCompletionHandler {
std::unique_ptr<PragmaHandler> AttributePragmaHandler;
std::unique_ptr<PragmaHandler> MaxTokensHerePragmaHandler;
std::unique_ptr<PragmaHandler> MaxTokensTotalPragmaHandler;
std::unique_ptr<PragmaHandler> ExportHandler;
std::unique_ptr<PragmaHandler> RISCVPragmaHandler;

/// Initialize all pragma handlers.
Expand Down Expand Up @@ -7171,6 +7172,12 @@ class Parser : public CodeCompletionHandler {

void HandlePragmaAttribute();

bool zOSHandlePragmaHelper(tok::TokenKind);

/// Handle the annotation token produced for
/// #pragma export ...
void HandlePragmaExport();

///@}

//
Expand Down
20 changes: 20 additions & 0 deletions clang/include/clang/Sema/Sema.h
Original file line number Diff line number Diff line change
Expand Up @@ -2309,6 +2309,22 @@ class Sema final : public SemaBase {
ActOnPragmaMSFunction(SourceLocation Loc,
const llvm::SmallVectorImpl<StringRef> &NoBuiltins);

NamedDecl *lookupExternCName(IdentifierInfo *IdentId, SourceLocation NameLoc,
Scope *curScope);

/// A label from a C++ #pragma export, for a symbol that we
/// haven't seen the declaration for yet.
struct SymbolLabel {
SourceLocation NameLoc;
bool Used;
};

llvm::DenseMap<IdentifierInfo *, SymbolLabel> PendingExportedNames;

/// ActonPragmaExport - called on well-formed '\#pragma export'.
void ActOnPragmaExport(IdentifierInfo *IdentId, SourceLocation ExportNameLoc,
Scope *curScope);

/// Only called on function definitions; if there is a pragma in scope
/// with the effect of a range-based optnone, consider marking the function
/// with attribute optnone.
Expand Down Expand Up @@ -3836,6 +3852,8 @@ class Sema final : public SemaBase {
void warnOnReservedIdentifier(const NamedDecl *D);
void warnOnCTypeHiddenInCPlusPlus(const NamedDecl *D);

void ProcessPragmaExport(DeclaratorDecl *newDecl);

Decl *ActOnDeclarator(Scope *S, Declarator &D);

NamedDecl *HandleDeclarator(Scope *S, Declarator &D,
Expand Down Expand Up @@ -4920,6 +4938,8 @@ class Sema final : public SemaBase {
TypeVisibilityAttr::VisibilityType Vis);
VisibilityAttr *mergeVisibilityAttr(Decl *D, const AttributeCommonInfo &CI,
VisibilityAttr::VisibilityType Vis);
void mergeVisibilityType(Decl *D, SourceLocation Loc,
VisibilityAttr::VisibilityType Type);
SectionAttr *mergeSectionAttr(Decl *D, const AttributeCommonInfo &CI,
StringRef Name);

Expand Down
4 changes: 4 additions & 0 deletions clang/lib/Driver/ToolChains/ZOS.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Expand Down
121 changes: 121 additions & 0 deletions clang/lib/Parse/ParsePragma.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -395,6 +395,12 @@ struct PragmaMaxTokensTotalHandler : public PragmaHandler {
Token &FirstToken) override;
};

struct PragmaExportHandler : public PragmaHandler {
explicit PragmaExportHandler() : PragmaHandler("export") {}
void HandlePragma(Preprocessor &PP, PragmaIntroducer Introducer,
Token &FirstToken) override;
};

struct PragmaRISCVHandler : public PragmaHandler {
PragmaRISCVHandler(Sema &Actions)
: PragmaHandler("riscv"), Actions(Actions) {}
Expand Down Expand Up @@ -558,6 +564,11 @@ void Parser::initializePragmaHandlers() {
MaxTokensTotalPragmaHandler = std::make_unique<PragmaMaxTokensTotalHandler>();
PP.AddPragmaHandler("clang", MaxTokensTotalPragmaHandler.get());

if (getLangOpts().ZOSExt) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This isn't particularly ZOS specific other than for compiler compat, right? Is there a good reason we couldn't allow this syntax on other compilers?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This pragma duplicates on a limited scope the functionality of the visibility attribute. We're adding the pragma for compatibility with the z/OS XL C/C++ compiler. I'm more that happy to make it available everywhere. I just didn't want to cause confusion by providing yet another mechanism for specifying visibility especially since it is limited to which C++ declarations it can be applied too.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you think I should enable this for all platforms?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IDK why I didn't see the comment response here... I think doing these based on the target here is somewhat reasonable, it harms portability of this attribute, but in a way that I don't think is particularly challenging thanks to the ZOSExt flag being required.

ExportHandler = std::make_unique<PragmaExportHandler>();
PP.AddPragmaHandler(ExportHandler.get());
}

if (getTargetInfo().getTriple().isRISCV()) {
RISCVPragmaHandler = std::make_unique<PragmaRISCVHandler>(Actions);
PP.AddPragmaHandler("clang", RISCVPragmaHandler.get());
Expand Down Expand Up @@ -692,6 +703,11 @@ void Parser::resetPragmaHandlers() {
PP.RemovePragmaHandler("clang", MaxTokensTotalPragmaHandler.get());
MaxTokensTotalPragmaHandler.reset();

if (getLangOpts().ZOSExt) {
PP.RemovePragmaHandler(ExportHandler.get());
ExportHandler.reset();
}

if (getTargetInfo().getTriple().isRISCV()) {
PP.RemovePragmaHandler("clang", RISCVPragmaHandler.get());
RISCVPragmaHandler.reset();
Expand Down Expand Up @@ -1386,6 +1402,74 @@ bool Parser::HandlePragmaMSAllocText(StringRef PragmaName,
return true;
}

bool Parser::zOSHandlePragmaHelper(tok::TokenKind PragmaKind) {
assert(Tok.is(PragmaKind));

StringRef PragmaName = "export";

using namespace clang::charinfo;
auto *TheTokens =
(std::pair<std::unique_ptr<Token[]>, size_t> *)Tok.getAnnotationValue();
PP.EnterTokenStream(std::move(TheTokens->first), TheTokens->second, true,
false);
ConsumeAnnotationToken();

do {
PP.Lex(Tok);
if (Tok.isNot(tok::l_paren)) {
PP.Diag(Tok.getLocation(), diag::warn_pragma_expected_lparen)
<< PragmaName;
return false;
}

PP.Lex(Tok);
if (Tok.isNot(tok::identifier)) {
PP.Diag(Tok.getLocation(), diag::warn_pragma_expected_identifier)
<< PragmaName;
return false;
}

IdentifierInfo *IdentName = Tok.getIdentifierInfo();
SourceLocation IdentNameLoc = Tok.getLocation();
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is unfortunate that Fully Qualified Names aren't supported here. Since we're doing a lookup anyway, way can't we do that?

PP.Lex(Tok);

if (Tok.isNot(tok::r_paren)) {
PP.Diag(Tok.getLocation(), diag::warn_pragma_expected_rparen)
<< PragmaName;
return false;
}

PP.Lex(Tok);
Actions.ActOnPragmaExport(IdentName, IdentNameLoc, getCurScope());
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can ActOnPragmaExport not check the current scope?


// Because export is also a C++ keyword, we also check for that.
if (Tok.is(tok::identifier) || Tok.is(tok::kw_export)) {
PragmaName = Tok.getIdentifierInfo()->getName();
if (PragmaName != "export")
PP.Diag(Tok.getLocation(), diag::warn_pragma_extra_tokens_at_eol)
<< PragmaName;
} else if (Tok.isNot(tok::eof)) {
PP.Diag(Tok.getLocation(), diag::warn_pragma_extra_tokens_at_eol)
<< PragmaName;
return false;
}
} while (Tok.isNot(tok::eof));
PP.Lex(Tok);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is quite a bit on how htis parsing is working that doesn't seem right to me, but the parser code owner shoudl take a look.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you take a look at the parsing again. I have removed the part that parsed the parameter list as part of removing that form of the pragma syntax.

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 ");
Expand Down Expand Up @@ -4133,6 +4217,43 @@ void PragmaMaxTokensTotalHandler::HandlePragma(Preprocessor &PP,
PP.overrideMaxTokens(MaxTokens, Loc);
}

static void zOSPragmaHandlerHelper(Preprocessor &PP, Token &Tok,
tok::TokenKind TokKind) {
Token EoF, AnnotTok;
EoF.startToken();
EoF.setKind(tok::eof);
AnnotTok.startToken();
AnnotTok.setKind(TokKind);
AnnotTok.setLocation(Tok.getLocation());
AnnotTok.setAnnotationEndLoc(Tok.getLocation());
SmallVector<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.
EoF.setLocation(Tok.getLocation());
TokenVector.push_back(EoF);
// We must allocate this array with new because EnterTokenStream is going to
// delete it later.
markAsReinjectedForRelexing(TokenVector);
auto TokenArray = std::make_unique<Token[]>(TokenVector.size());
std::copy(TokenVector.begin(), TokenVector.end(), TokenArray.get());
auto Value = new (PP.getPreprocessorAllocator())
std::pair<std::unique_ptr<Token[]>, size_t>(std::move(TokenArray),
TokenVector.size());
AnnotTok.setAnnotationValue(Value);
PP.EnterToken(AnnotTok, /*IsReinject*/ false);
}

/// Handle #pragma export.
void PragmaExportHandler::HandlePragma(Preprocessor &PP,
PragmaIntroducer Introducer,
Token &FirstToken) {
zOSPragmaHandlerHelper(PP, FirstToken, tok::annot_pragma_export);
}

// Handle '#pragma clang riscv intrinsic vector'.
// '#pragma clang riscv intrinsic sifive_vector'.
// '#pragma clang riscv intrinsic andes_vector'.
Expand Down
8 changes: 8 additions & 0 deletions clang/lib/Parse/ParseStmt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -500,6 +500,11 @@ StmtResult Parser::ParseStatementOrDeclarationAfterAttributes(
ProhibitAttributes(GNUAttrs);
HandlePragmaAttribute();
return StmtEmpty();
case tok::annot_pragma_export:
ProhibitAttributes(CXX11Attrs);
ProhibitAttributes(GNUAttrs);
HandlePragmaExport();
return StmtEmpty();
}

// If we reached this code, the statement must end in a semicolon.
Expand Down Expand Up @@ -1029,6 +1034,9 @@ void Parser::ParseCompoundStatementLeadingPragmas() {
case tok::annot_pragma_dump:
HandlePragmaDump();
break;
case tok::annot_pragma_export:
HandlePragmaExport();
break;
default:
checkForPragmas = false;
break;
Expand Down
3 changes: 3 additions & 0 deletions clang/lib/Parse/Parser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 =
Expand Down
7 changes: 7 additions & 0 deletions clang/lib/Sema/Sema.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1494,6 +1494,13 @@ void Sema::ActOnEndOfTranslationUnit() {
Consumer.CompleteExternalDeclaration(D);
}

// Visit all pending #pragma export.
for (auto &Iter : PendingExportedNames) {
auto &Exported = Iter.second;
if (!Exported.Used)
Diag(Exported.NameLoc, diag::warn_failed_to_resolve_pragma) << "export";
}

if (LangOpts.HLSL)
HLSL().ActOnEndOfTranslationUnit(getASTContext().getTranslationUnitDecl());

Expand Down
53 changes: 53 additions & 0 deletions clang/lib/Sema/SemaAttr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1327,6 +1327,59 @@ void Sema::AddImplicitMSFunctionNoBuiltinAttr(FunctionDecl *FD) {
FD->addAttr(NoBuiltinAttr::CreateImplicit(Context, V.data(), V.size()));
}

NamedDecl *Sema::lookupExternCName(IdentifierInfo *IdentId,
SourceLocation NameLoc, Scope *curScope) {
LookupResult Result(*this, IdentId, NameLoc, LookupOrdinaryName);
LookupName(Result, curScope);
if (!getLangOpts().CPlusPlus)
return Result.getAsSingle<NamedDecl>();
for (LookupResult::iterator I = Result.begin(); I != Result.end(); ++I) {
NamedDecl *D = (*I)->getUnderlyingDecl();
if (auto *FD = dyn_cast<FunctionDecl>(D->getCanonicalDecl()))
if (FD->isExternC())
return D;
if (isa<VarDecl>(D->getCanonicalDecl()))
return D;
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this lookup is quite right. We should probably be just doing normal name lookup rather than this. Also, this doesn't support dependent names, which we definitely should be doing.

return nullptr;
}

void Sema::ActOnPragmaExport(IdentifierInfo *IdentId, SourceLocation NameLoc, Scope *curScope) {
SymbolLabel Label;
Label.NameLoc = NameLoc;
Label.Used = false;

NamedDecl *PrevDecl = lookupExternCName(IdentId, NameLoc, curScope);
if (!PrevDecl) {
PendingExportedNames[IdentId] = Label;
return;
}

if (auto *FD = dyn_cast<FunctionDecl>(PrevDecl->getCanonicalDecl())) {
if (!FD->hasExternalFormalLinkage()) {
Diag(NameLoc, diag::warn_pragma_not_applied) << "export" << PrevDecl;
return;
}
if (FD->hasBody()) {
Diag(NameLoc, diag::warn_pragma_not_applied_to_defined_symbol)
<< "export";
return;
}
} else if (auto *VD = dyn_cast<VarDecl>(PrevDecl->getCanonicalDecl())) {
if (!VD->hasExternalFormalLinkage()) {
Diag(NameLoc, diag::warn_pragma_not_applied) << "export" << PrevDecl;
return;
}
if (VD->hasDefinition() == VarDecl::Definition) {
Diag(NameLoc, diag::warn_pragma_not_applied_to_defined_symbol)
<< "export";
return;
}
}
mergeVisibilityType(PrevDecl->getCanonicalDecl(), NameLoc,
VisibilityAttr::Default);
}

typedef std::vector<std::pair<unsigned, SourceLocation> > VisStack;
enum : unsigned { NoVisibility = ~0U };

Expand Down
Loading
Loading