-
Notifications
You must be signed in to change notification settings - Fork 13.6k
[SystemZ][z/OS] Implement #pragma export #141671
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -2278,6 +2278,39 @@ 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; | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
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<SymbolLabel, 1> PendingPragmaExportOverloads; | ||||||
llvm::DenseMap<IdentifierInfo *, PendingPragmaExportOverloads> | ||||||
PendingExportedNames; | ||||||
|
||||||
/// 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. | ||||||
|
@@ -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); | ||||||
|
||||||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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<PragmaMaxTokensTotalHandler>(); | ||
PP.AddPragmaHandler("clang", MaxTokensTotalPragmaHandler.get()); | ||
|
||
if (getLangOpts().ZOSExt) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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? |
||
ExportHandler = std::make_unique<PragmaExportHandler>(); | ||
PP.AddPragmaHandler(ExportHandler.get()); | ||
} | ||
|
||
if (getTargetInfo().getTriple().isRISCV()) { | ||
RISCVPragmaHandler = std::make_unique<PragmaRISCVHandler>(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)) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Presumably this should ensure we only look up in the global namespace, right? VS local namespace? |
||
// Nothing to do. | ||
} 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); | ||
if (Tok.isNot(tok::identifier)) { | ||
PP.Diag(Tok.getLocation(), diag::warn_pragma_expected_identifier) | ||
<< PragmaName; | ||
return nullptr; | ||
} | ||
IdentifierInfo *II = Tok.getIdentifierInfo(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Identifier isn't sufficient, what if this is naming a type inside a template? Or a dependent type? |
||
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<SmallVector<QualType, 4>> &TypeList, | ||
Qualifiers &CVQual) { | ||
if (Tok.is(tok::l_paren)) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We probably want to use a BalancedDelimiterTracker/etc for the parens. |
||
TypeList = SmallVector<QualType, 4>(); | ||
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); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This feels lossy... Also, why are we allowing 'void' in this list at all? |
||
} | ||
} | ||
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)) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This part seems wrong too, what about const before? |
||
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(); | ||
|
||
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<SmallVector<QualType, 4>> 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); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. |
||
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<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'. | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We definitely need a much better intro/documentation here in the release notes.