diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index c036f66d60bf7a..e810b92c492712 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -188,6 +188,8 @@ X86 Support in Clang - The x86 intrinsics ``__rorb``, ``__rorw``, ``__rord``, ``__rorq`, ``_rotr``, ``_rotwr`` and ``_lrotr`` may now be used within constant expressions. +- Support for -march=sapphirerapids was added. + Internal API Changes -------------------- diff --git a/clang/lib/CodeGen/CGDebugInfo.cpp b/clang/lib/CodeGen/CGDebugInfo.cpp index e3442ecd4bd59f..36bab9c22d4378 100644 --- a/clang/lib/CodeGen/CGDebugInfo.cpp +++ b/clang/lib/CodeGen/CGDebugInfo.cpp @@ -2260,6 +2260,25 @@ static bool hasExplicitMemberDefinition(CXXRecordDecl::method_iterator I, return false; } +static bool canUseCtorHoming(const CXXRecordDecl *RD) { + // Constructor homing can be used for classes that have at least one + // constructor and have no trivial or constexpr constructors. + // Skip this optimization if the class or any of its methods are marked + // dllimport. + if (RD->isLambda() || RD->hasConstexprNonCopyMoveConstructor() || + isClassOrMethodDLLImport(RD)) + return false; + + if (RD->ctors().empty()) + return false; + + for (const auto *Ctor : RD->ctors()) + if (Ctor->isTrivial() && !Ctor->isCopyOrMoveConstructor()) + return false; + + return true; +} + static bool shouldOmitDefinition(codegenoptions::DebugInfoKind DebugKind, bool DebugTypeExtRefs, const RecordDecl *RD, const LangOptions &LangOpts) { @@ -2294,23 +2313,6 @@ static bool shouldOmitDefinition(codegenoptions::DebugInfoKind DebugKind, !isClassOrMethodDLLImport(CXXDecl)) return true; - // In constructor debug mode, only emit debug info for a class when its - // constructor is emitted. Skip this optimization if the class or any of - // its methods are marked dllimport. - // - // This applies to classes that don't have any trivial constructors and have - // at least one constructor. - if (DebugKind == codegenoptions::DebugInfoConstructor && - !CXXDecl->isLambda() && !CXXDecl->hasConstexprNonCopyMoveConstructor() && - !isClassOrMethodDLLImport(CXXDecl)) { - if (CXXDecl->ctors().empty()) - return false; - for (const auto *Ctor : CXXDecl->ctors()) - if (Ctor->isTrivial() && !Ctor->isCopyOrMoveConstructor()) - return false; - return true; - } - TemplateSpecializationKind Spec = TSK_Undeclared; if (const auto *SD = dyn_cast(RD)) Spec = SD->getSpecializationKind(); @@ -2320,6 +2322,12 @@ static bool shouldOmitDefinition(codegenoptions::DebugInfoKind DebugKind, CXXDecl->method_end())) return true; + // In constructor homing mode, only emit complete debug info for a class + // when its constructor is emitted. + if ((DebugKind == codegenoptions::DebugInfoConstructor) && + canUseCtorHoming(CXXDecl)) + return true; + return false; } diff --git a/clang/test/CodeGenCXX/debug-info-template-explicit-specialization.cpp b/clang/test/CodeGenCXX/debug-info-template-explicit-specialization.cpp index 4e41c4092bf4e3..b756674f54c405 100644 --- a/clang/test/CodeGenCXX/debug-info-template-explicit-specialization.cpp +++ b/clang/test/CodeGenCXX/debug-info-template-explicit-specialization.cpp @@ -1,5 +1,8 @@ // RUN: %clang_cc1 -emit-llvm -triple %itanium_abi_triple -debug-info-kind=limited %s -o - | FileCheck %s +// Make sure this still works with constructor homing. +// RUN: %clang_cc1 -emit-llvm -triple %itanium_abi_triple -debug-info-kind=constructor %s -o - | FileCheck %s + // Run again with -gline-tables-only or -gline-directives-only and verify we don't crash. We won't output // type info at all. // RUN: %clang_cc1 -emit-llvm -triple %itanium_abi_triple -debug-info-kind=line-tables-only %s -o - | FileCheck %s -check-prefix LINES-ONLY diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_ptrauth.h b/compiler-rt/lib/sanitizer_common/sanitizer_ptrauth.h index 4d0d96a64f622f..a288068bf94383 100644 --- a/compiler-rt/lib/sanitizer_common/sanitizer_ptrauth.h +++ b/compiler-rt/lib/sanitizer_common/sanitizer_ptrauth.h @@ -18,4 +18,6 @@ #define ptrauth_string_discriminator(__string) ((int)0) #endif +#define STRIP_PC(pc) ((uptr)ptrauth_strip(pc, 0)) + #endif // SANITIZER_PTRAUTH_H diff --git a/compiler-rt/lib/tsan/rtl/tsan_external.cpp b/compiler-rt/lib/tsan/rtl/tsan_external.cpp index 0faa1ee93a139e..466b2bf0f66ce5 100644 --- a/compiler-rt/lib/tsan/rtl/tsan_external.cpp +++ b/compiler-rt/lib/tsan/rtl/tsan_external.cpp @@ -11,6 +11,7 @@ //===----------------------------------------------------------------------===// #include "tsan_rtl.h" #include "tsan_interceptors.h" +#include "sanitizer_common/sanitizer_ptrauth.h" namespace __tsan { @@ -57,13 +58,13 @@ uptr TagFromShadowStackFrame(uptr pc) { #if !SANITIZER_GO typedef void(*AccessFunc)(ThreadState *, uptr, uptr, int); -void ExternalAccess(void *addr, void *caller_pc, void *tag, AccessFunc access) { +void ExternalAccess(void *addr, uptr caller_pc, void *tag, AccessFunc access) { CHECK_LT(tag, atomic_load(&used_tags, memory_order_relaxed)); ThreadState *thr = cur_thread(); - if (caller_pc) FuncEntry(thr, (uptr)caller_pc); + if (caller_pc) FuncEntry(thr, caller_pc); InsertShadowStackFrameForTag(thr, (uptr)tag); bool in_ignored_lib; - if (!caller_pc || !libignore()->IsIgnored((uptr)caller_pc, &in_ignored_lib)) { + if (!caller_pc || !libignore()->IsIgnored(caller_pc, &in_ignored_lib)) { access(thr, CALLERPC, (uptr)addr, kSizeLog1); } FuncExit(thr); @@ -110,12 +111,12 @@ void __tsan_external_assign_tag(void *addr, void *tag) { SANITIZER_INTERFACE_ATTRIBUTE void __tsan_external_read(void *addr, void *caller_pc, void *tag) { - ExternalAccess(addr, caller_pc, tag, MemoryRead); + ExternalAccess(addr, STRIP_PC(caller_pc), tag, MemoryRead); } SANITIZER_INTERFACE_ATTRIBUTE void __tsan_external_write(void *addr, void *caller_pc, void *tag) { - ExternalAccess(addr, caller_pc, tag, MemoryWrite); + ExternalAccess(addr, STRIP_PC(caller_pc), tag, MemoryWrite); } } // extern "C" diff --git a/compiler-rt/lib/tsan/rtl/tsan_interface.cpp b/compiler-rt/lib/tsan/rtl/tsan_interface.cpp index 2b3a0889b70a4a..5c2a617a24c3d6 100644 --- a/compiler-rt/lib/tsan/rtl/tsan_interface.cpp +++ b/compiler-rt/lib/tsan/rtl/tsan_interface.cpp @@ -14,6 +14,7 @@ #include "tsan_interface_ann.h" #include "tsan_rtl.h" #include "sanitizer_common/sanitizer_internal_defs.h" +#include "sanitizer_common/sanitizer_ptrauth.h" #define CALLERPC ((uptr)__builtin_return_address(0)) @@ -43,13 +44,13 @@ void __tsan_write16(void *addr) { } void __tsan_read16_pc(void *addr, void *pc) { - MemoryRead(cur_thread(), (uptr)pc, (uptr)addr, kSizeLog8); - MemoryRead(cur_thread(), (uptr)pc, (uptr)addr + 8, kSizeLog8); + MemoryRead(cur_thread(), STRIP_PC(pc), (uptr)addr, kSizeLog8); + MemoryRead(cur_thread(), STRIP_PC(pc), (uptr)addr + 8, kSizeLog8); } void __tsan_write16_pc(void *addr, void *pc) { - MemoryWrite(cur_thread(), (uptr)pc, (uptr)addr, kSizeLog8); - MemoryWrite(cur_thread(), (uptr)pc, (uptr)addr + 8, kSizeLog8); + MemoryWrite(cur_thread(), STRIP_PC(pc), (uptr)addr, kSizeLog8); + MemoryWrite(cur_thread(), STRIP_PC(pc), (uptr)addr + 8, kSizeLog8); } // __tsan_unaligned_read/write calls are emitted by compiler. diff --git a/compiler-rt/lib/tsan/rtl/tsan_interface_inl.h b/compiler-rt/lib/tsan/rtl/tsan_interface_inl.h index f955ddf99247c5..f5d743c10772ec 100644 --- a/compiler-rt/lib/tsan/rtl/tsan_interface_inl.h +++ b/compiler-rt/lib/tsan/rtl/tsan_interface_inl.h @@ -12,6 +12,7 @@ #include "tsan_interface.h" #include "tsan_rtl.h" +#include "sanitizer_common/sanitizer_ptrauth.h" #define CALLERPC ((uptr)__builtin_return_address(0)) @@ -50,35 +51,35 @@ void __tsan_write8(void *addr) { } void __tsan_read1_pc(void *addr, void *pc) { - MemoryRead(cur_thread(), (uptr)pc, (uptr)addr, kSizeLog1); + MemoryRead(cur_thread(), STRIP_PC(pc), (uptr)addr, kSizeLog1); } void __tsan_read2_pc(void *addr, void *pc) { - MemoryRead(cur_thread(), (uptr)pc, (uptr)addr, kSizeLog2); + MemoryRead(cur_thread(), STRIP_PC(pc), (uptr)addr, kSizeLog2); } void __tsan_read4_pc(void *addr, void *pc) { - MemoryRead(cur_thread(), (uptr)pc, (uptr)addr, kSizeLog4); + MemoryRead(cur_thread(), STRIP_PC(pc), (uptr)addr, kSizeLog4); } void __tsan_read8_pc(void *addr, void *pc) { - MemoryRead(cur_thread(), (uptr)pc, (uptr)addr, kSizeLog8); + MemoryRead(cur_thread(), STRIP_PC(pc), (uptr)addr, kSizeLog8); } void __tsan_write1_pc(void *addr, void *pc) { - MemoryWrite(cur_thread(), (uptr)pc, (uptr)addr, kSizeLog1); + MemoryWrite(cur_thread(), STRIP_PC(pc), (uptr)addr, kSizeLog1); } void __tsan_write2_pc(void *addr, void *pc) { - MemoryWrite(cur_thread(), (uptr)pc, (uptr)addr, kSizeLog2); + MemoryWrite(cur_thread(), STRIP_PC(pc), (uptr)addr, kSizeLog2); } void __tsan_write4_pc(void *addr, void *pc) { - MemoryWrite(cur_thread(), (uptr)pc, (uptr)addr, kSizeLog4); + MemoryWrite(cur_thread(), STRIP_PC(pc), (uptr)addr, kSizeLog4); } void __tsan_write8_pc(void *addr, void *pc) { - MemoryWrite(cur_thread(), (uptr)pc, (uptr)addr, kSizeLog8); + MemoryWrite(cur_thread(), STRIP_PC(pc), (uptr)addr, kSizeLog8); } void __tsan_vptr_update(void **vptr_p, void *new_val) { @@ -100,7 +101,7 @@ void __tsan_vptr_read(void **vptr_p) { } void __tsan_func_entry(void *pc) { - FuncEntry(cur_thread(), (uptr)pc); + FuncEntry(cur_thread(), STRIP_PC(pc)); } void __tsan_func_exit() { @@ -124,9 +125,9 @@ void __tsan_write_range(void *addr, uptr size) { } void __tsan_read_range_pc(void *addr, uptr size, void *pc) { - MemoryAccessRange(cur_thread(), (uptr)pc, (uptr)addr, size, false); + MemoryAccessRange(cur_thread(), STRIP_PC(pc), (uptr)addr, size, false); } void __tsan_write_range_pc(void *addr, uptr size, void *pc) { - MemoryAccessRange(cur_thread(), (uptr)pc, (uptr)addr, size, true); + MemoryAccessRange(cur_thread(), STRIP_PC(pc), (uptr)addr, size, true); } diff --git a/flang/include/flang/Parser/characters.h b/flang/include/flang/Parser/characters.h index eefb524e5d879b..120560625da29c 100644 --- a/flang/include/flang/Parser/characters.h +++ b/flang/include/flang/Parser/characters.h @@ -151,6 +151,33 @@ inline constexpr std::optional BackslashEscapeChar(char ch) { } } +// Does not include spaces or line ending characters. +inline constexpr bool IsValidFortranTokenCharacter(char ch) { + switch (ch) { + case '"': + case '%': + case '\'': + case '(': + case ')': + case '*': + case '+': + case ',': + case '-': + case '.': + case '/': + case ':': + case ';': + case '<': + case '=': + case '>': + case '[': + case ']': + return true; + default: + return IsLegalIdentifierStart(ch) || IsDecimalDigit(ch); + } +} + struct EncodedCharacter { static constexpr int maxEncodingBytes{6}; char buffer[maxEncodingBytes]; diff --git a/flang/include/flang/Parser/parse-tree.h b/flang/include/flang/Parser/parse-tree.h index cbff637007755e..317d772889eee3 100644 --- a/flang/include/flang/Parser/parse-tree.h +++ b/flang/include/flang/Parser/parse-tree.h @@ -547,7 +547,8 @@ struct ProgramUnit { std::variant, common::Indirection, common::Indirection, common::Indirection, - common::Indirection, common::Indirection> + common::Indirection, common::Indirection, + common::Indirection> u; }; diff --git a/flang/lib/Parser/prescan.cpp b/flang/lib/Parser/prescan.cpp index 9e90f7f6228feb..5e6f13797646b8 100644 --- a/flang/lib/Parser/prescan.cpp +++ b/flang/lib/Parser/prescan.cpp @@ -184,7 +184,8 @@ void Prescanner::Statement() { case LineClassification::Kind::PreprocessorDirective: Say(preprocessed->GetProvenanceRange(), "Preprocessed line resembles a preprocessor directive"_en_US); - preprocessed->ToLowerCase().Emit(cooked_); + preprocessed->ToLowerCase().CheckBadFortranCharacters(messages_).Emit( + cooked_); break; case LineClassification::Kind::CompilerDirective: if (preprocessed->HasRedundantBlanks()) { @@ -193,7 +194,9 @@ void Prescanner::Statement() { NormalizeCompilerDirectiveCommentMarker(*preprocessed); preprocessed->ToLowerCase(); SourceFormChange(preprocessed->ToString()); - preprocessed->ClipComment(true /* skip first ! */).Emit(cooked_); + preprocessed->ClipComment(true /* skip first ! */) + .CheckBadFortranCharacters(messages_) + .Emit(cooked_); break; case LineClassification::Kind::Source: if (inFixedForm_) { @@ -205,7 +208,10 @@ void Prescanner::Statement() { preprocessed->RemoveRedundantBlanks(); } } - preprocessed->ToLowerCase().ClipComment().Emit(cooked_); + preprocessed->ToLowerCase() + .ClipComment() + .CheckBadFortranCharacters(messages_) + .Emit(cooked_); break; } } else { @@ -213,7 +219,7 @@ void Prescanner::Statement() { if (line.kind == LineClassification::Kind::CompilerDirective) { SourceFormChange(tokens.ToString()); } - tokens.Emit(cooked_); + tokens.CheckBadFortranCharacters(messages_).Emit(cooked_); } if (omitNewline_) { omitNewline_ = false; @@ -245,8 +251,9 @@ void Prescanner::NextLine() { } } -void Prescanner::LabelField(TokenSequence &token, int outCol) { +void Prescanner::LabelField(TokenSequence &token) { const char *bad{nullptr}; + int outCol{1}; for (; *at_ != '\n' && column_ <= 6; ++at_) { if (*at_ == '\t') { ++at_; @@ -256,20 +263,26 @@ void Prescanner::LabelField(TokenSequence &token, int outCol) { if (*at_ != ' ' && !(*at_ == '0' && column_ == 6)) { // '0' in column 6 becomes space EmitChar(token, *at_); + ++outCol; if (!bad && !IsDecimalDigit(*at_)) { bad = at_; } - ++outCol; } ++column_; } - if (outCol > 1) { + if (outCol == 1) { // empty label field + // Emit a space so that, if the line is rescanned after preprocessing, + // a leading 'C' or 'D' won't be left-justified and then accidentally + // misinterpreted as a comment card. + EmitChar(token, ' '); + ++outCol; + } else { if (bad && !preprocessor_.IsNameDefined(token.CurrentOpenToken())) { Say(GetProvenance(bad), "Character in fixed-form label field must be a digit"_en_US); } - token.CloseToken(); } + token.CloseToken(); SkipToNextSignificantCharacter(); if (IsDecimalDigit(*at_)) { Say(GetProvenance(at_), diff --git a/flang/lib/Parser/prescan.h b/flang/lib/Parser/prescan.h index 595f8f701185b2..0b5b64792004ac 100644 --- a/flang/lib/Parser/prescan.h +++ b/flang/lib/Parser/prescan.h @@ -147,7 +147,7 @@ class Prescanner { common::LanguageFeature::ClassicCComments))); } - void LabelField(TokenSequence &, int outCol = 1); + void LabelField(TokenSequence &); void SkipToEndOfLine(); bool MustSkipToEndOfLine() const; void NextChar(); diff --git a/flang/lib/Parser/program-parsers.cpp b/flang/lib/Parser/program-parsers.cpp index 278cc6fdb51a5c..dee359e240cfa6 100644 --- a/flang/lib/Parser/program-parsers.cpp +++ b/flang/lib/Parser/program-parsers.cpp @@ -20,20 +20,6 @@ namespace Fortran::parser { -// R501 program -> program-unit [program-unit]... -// This is the top-level production for the Fortran language. -// F'2018 6.3.1 defines a program unit as a sequence of one or more lines, -// implying that a line can't be part of two distinct program units. -// Consequently, a program unit END statement should be the last statement -// on its line. We parse those END statements via unterminatedStatement() -// and then skip over the end of the line here. -TYPE_PARSER(construct( - extension(skipStuffBeforeStatement >> - !nextCh >> pure>()) || - some(StartNewSubprogram{} >> Parser{} / skipMany(";"_tok) / - space / recovery(endOfLine, SkipPast<'\n'>{})) / - skipStuffBeforeStatement)) - // R502 program-unit -> // main-program | external-subprogram | module | submodule | block-data // R503 external-subprogram -> function-subprogram | subroutine-subprogram @@ -49,12 +35,30 @@ TYPE_PARSER(construct( // variant parsers for several productions; giving the "module" production // priority here is a cleaner solution, though regrettably subtle. Enforcing // C1547 is done in semantics. -TYPE_PARSER(construct(indirect(Parser{})) || +static constexpr auto programUnit{ + construct(indirect(Parser{})) || construct(indirect(functionSubprogram)) || construct(indirect(subroutineSubprogram)) || construct(indirect(Parser{})) || construct(indirect(Parser{})) || - construct(indirect(Parser{}))) + construct(indirect(Parser{}))}; +static constexpr auto normalProgramUnit{StartNewSubprogram{} >> programUnit / + skipMany(";"_tok) / space / recovery(endOfLine, SkipPast<'\n'>{})}; +static constexpr auto globalCompilerDirective{ + construct(indirect(compilerDirective))}; + +// R501 program -> program-unit [program-unit]... +// This is the top-level production for the Fortran language. +// F'2018 6.3.1 defines a program unit as a sequence of one or more lines, +// implying that a line can't be part of two distinct program units. +// Consequently, a program unit END statement should be the last statement +// on its line. We parse those END statements via unterminatedStatement() +// and then skip over the end of the line here. +TYPE_PARSER(construct( + extension(skipStuffBeforeStatement >> + !nextCh >> pure>()) || + some(globalCompilerDirective || normalProgramUnit) / + skipStuffBeforeStatement)) // R504 specification-part -> // [use-stmt]... [import-stmt]... [implicit-part] diff --git a/flang/lib/Parser/token-sequence.cpp b/flang/lib/Parser/token-sequence.cpp index 07c5b12e5f759f..4797cb759a72b2 100644 --- a/flang/lib/Parser/token-sequence.cpp +++ b/flang/lib/Parser/token-sequence.cpp @@ -8,6 +8,7 @@ #include "token-sequence.h" #include "flang/Parser/characters.h" +#include "flang/Parser/message.h" #include "llvm/Support/raw_ostream.h" namespace Fortran::parser { @@ -310,4 +311,25 @@ ProvenanceRange TokenSequence::GetIntervalProvenanceRange( ProvenanceRange TokenSequence::GetProvenanceRange() const { return GetIntervalProvenanceRange(0, start_.size()); } + +const TokenSequence &TokenSequence::CheckBadFortranCharacters( + Messages &messages) const { + std::size_t tokens{SizeInTokens()}; + for (std::size_t j{0}; j < tokens; ++j) { + CharBlock token{TokenAt(j)}; + char ch{token.FirstNonBlank()}; + if (ch != ' ' && !IsValidFortranTokenCharacter(ch)) { + if (ch == '!' && j == 0) { + // allow in !dir$ + } else if (ch < ' ' || ch >= '\x7f') { + messages.Say(GetTokenProvenanceRange(j), + "bad character (0x%02x) in Fortran token"_err_en_US, ch & 0xff); + } else { + messages.Say(GetTokenProvenanceRange(j), + "bad character ('%c') in Fortran token"_err_en_US, ch); + } + } + } + return *this; +} } // namespace Fortran::parser diff --git a/flang/lib/Parser/token-sequence.h b/flang/lib/Parser/token-sequence.h index d98c0b955c5e93..6a10ef1977d38a 100644 --- a/flang/lib/Parser/token-sequence.h +++ b/flang/lib/Parser/token-sequence.h @@ -27,6 +27,8 @@ class raw_ostream; namespace Fortran::parser { +class Messages; + // Buffers a contiguous sequence of characters that has been partitioned into // a sequence of preprocessing tokens with provenances. class TokenSequence { @@ -115,6 +117,7 @@ class TokenSequence { TokenSequence &RemoveBlanks(std::size_t firstChar = 0); TokenSequence &RemoveRedundantBlanks(std::size_t firstChar = 0); TokenSequence &ClipComment(bool skipFirst = false); + const TokenSequence &CheckBadFortranCharacters(Messages &) const; void Emit(CookedSource &) const; void Dump(llvm::raw_ostream &) const; diff --git a/flang/lib/Semantics/check-io.cpp b/flang/lib/Semantics/check-io.cpp index 250ad492ebc926..d00f56c38042d0 100644 --- a/flang/lib/Semantics/check-io.cpp +++ b/flang/lib/Semantics/check-io.cpp @@ -155,7 +155,8 @@ void IoChecker::Enter(const parser::ConnectSpec::CharExpr &spec) { } } -void IoChecker::Enter(const parser::ConnectSpec::Newunit &) { +void IoChecker::Enter(const parser::ConnectSpec::Newunit &var) { + CheckForDefinableVariable(var, "NEWUNIT"); SetSpecifier(IoSpecKind::Newunit); } @@ -266,10 +267,11 @@ void IoChecker::Enter(const parser::IdExpr &) { SetSpecifier(IoSpecKind::Id); } void IoChecker::Enter(const parser::IdVariable &spec) { SetSpecifier(IoSpecKind::Id); - auto expr{GetExpr(spec)}; + const auto *expr{GetExpr(spec)}; if (!expr || !expr->GetType()) { return; } + CheckForDefinableVariable(spec, "ID"); int kind{expr->GetType()->kind()}; int defaultKind{context_.GetDefaultKind(TypeCategory::Integer)}; if (kind < defaultKind) { @@ -281,21 +283,18 @@ void IoChecker::Enter(const parser::IdVariable &spec) { void IoChecker::Enter(const parser::InputItem &spec) { flags_.set(Flag::DataList); - if (const parser::Variable * var{std::get_if(&spec.u)}) { - const parser::Name &name{GetLastName(*var)}; - if (name.symbol) { - if (auto *details{name.symbol->detailsIf()}) { - // TODO: Determine if this check is needed at all, and if so, replace - // the false subcondition with a check for a whole array. Otherwise, - // the check incorrectly flags array element and section references. - if (details->IsAssumedSize() && false) { - // This check may be superseded by C928 or C1002. - context_.Say(name.source, - "'%s' must not be a whole assumed size array"_err_en_US, - name.source); // C1231 - } - } - } + const parser::Variable *var{std::get_if(&spec.u)}; + if (!var) { + return; + } + CheckForDefinableVariable(*var, "Input"); + const auto &name{GetLastName(*var)}; + const auto *expr{GetExpr(*var)}; + if (name.symbol && IsAssumedSizeArray(*name.symbol) && expr && + !evaluate::IsArrayElement(*GetExpr(*var))) { + context_.Say(name.source, + "Whole assumed size array '%s' may not be an input item"_err_en_US, + name.source); // C1231 } } @@ -386,6 +385,8 @@ void IoChecker::Enter(const parser::InquireSpec::CharVar &spec) { specKind = IoSpecKind::Dispose; break; } + CheckForDefinableVariable(std::get(spec.t), + parser::ToUpperCaseLetters(common::EnumToString(specKind))); SetSpecifier(specKind); } @@ -412,6 +413,8 @@ void IoChecker::Enter(const parser::InquireSpec::IntVar &spec) { specKind = IoSpecKind::Size; break; } + CheckForDefinableVariable(std::get(spec.t), + parser::ToUpperCaseLetters(common::EnumToString(specKind))); SetSpecifier(specKind); } @@ -500,17 +503,23 @@ void IoChecker::Enter(const parser::IoControlSpec::Rec &) { SetSpecifier(IoSpecKind::Rec); } -void IoChecker::Enter(const parser::IoControlSpec::Size &) { +void IoChecker::Enter(const parser::IoControlSpec::Size &var) { + CheckForDefinableVariable(var, "SIZE"); SetSpecifier(IoSpecKind::Size); } void IoChecker::Enter(const parser::IoUnit &spec) { if (const parser::Variable * var{std::get_if(&spec.u)}) { - // TODO: C1201 - internal file variable must not be an array section ... - if (auto expr{GetExpr(*var)}) { - if (!ExprTypeKindIsDefault(*expr, context_)) { + if (stmt_ == IoStmtKind::Write) { + CheckForDefinableVariable(*var, "Internal file"); + } + if (const auto *expr{GetExpr(*var)}) { + if (HasVectorSubscript(*expr)) { + context_.Say(parser::FindSourceLocation(*var), // C1201 + "Internal file must not have a vector subscript"_err_en_US); + } else if (!ExprTypeKindIsDefault(*expr, context_)) { // This may be too restrictive; other kinds may be valid. - context_.Say( // C1202 + context_.Say(parser::FindSourceLocation(*var), // C1202 "Invalid character kind for an internal file variable"_err_en_US); } } @@ -522,13 +531,26 @@ void IoChecker::Enter(const parser::IoUnit &spec) { } } -void IoChecker::Enter(const parser::MsgVariable &) { +void IoChecker::Enter(const parser::MsgVariable &var) { + if (stmt_ == IoStmtKind::None) { + // allocate, deallocate, image control + CheckForDefinableVariable(var, "ERRMSG"); + return; + } + CheckForDefinableVariable(var, "IOMSG"); SetSpecifier(IoSpecKind::Iomsg); } -void IoChecker::Enter(const parser::OutputItem &) { +void IoChecker::Enter(const parser::OutputItem &item) { flags_.set(Flag::DataList); - // TODO: C1233 - output item must not be a procedure pointer + if (const auto *x{std::get_if(&item.u)}) { + if (const auto *expr{GetExpr(*x)}) { + if (IsProcedurePointer(*expr)) { + context_.Say(parser::FindSourceLocation(*x), + "Output item must not be a procedure pointer"_err_en_US); // C1233 + } + } + } } void IoChecker::Enter(const parser::StatusExpr &spec) { @@ -555,12 +577,14 @@ void IoChecker::Enter(const parser::StatusExpr &spec) { } } -void IoChecker::Enter(const parser::StatVariable &) { +void IoChecker::Enter(const parser::StatVariable &var) { if (stmt_ == IoStmtKind::None) { - // ALLOCATE & DEALLOCATE - } else { - SetSpecifier(IoSpecKind::Iostat); + // allocate, deallocate, image control + CheckForDefinableVariable(var, "STAT"); + return; } + CheckForDefinableVariable(var, "IOSTAT"); + SetSpecifier(IoSpecKind::Iostat); } void IoChecker::Leave(const parser::BackspaceStmt &) { @@ -808,7 +832,7 @@ void IoChecker::CheckStringValue(IoSpecKind specKind, const std::string &value, // CheckForRequiredSpecifier and CheckForProhibitedSpecifier functions // need conditions to check, and string arguments to insert into a message. -// A IoSpecKind provides both an absence/presence condition and a string +// An IoSpecKind provides both an absence/presence condition and a string // argument (its name). A (condition, string) pair provides an arbitrary // condition and an arbitrary string. @@ -893,6 +917,17 @@ void IoChecker::CheckForProhibitedSpecifier( } } +template +void IoChecker::CheckForDefinableVariable( + const A &var, const std::string &s) const { + const Symbol *sym{ + GetFirstName(*parser::Unwrap(var)).symbol}; + if (WhyNotModifiable(*sym, context_.FindScope(*context_.location()))) { + context_.Say(parser::FindSourceLocation(var), + "%s variable '%s' must be definable"_err_en_US, s, sym->name()); + } +} + void IoChecker::CheckForPureSubprogram() const { // C1597 CHECK(context_.location()); if (FindPureProcedureContaining(context_.FindScope(*context_.location()))) { diff --git a/flang/lib/Semantics/check-io.h b/flang/lib/Semantics/check-io.h index b5e8f12b5ee643..01bbcd9ba24ff7 100644 --- a/flang/lib/Semantics/check-io.h +++ b/flang/lib/Semantics/check-io.h @@ -122,6 +122,11 @@ class IoChecker : public virtual BaseChecker { void CheckForProhibitedSpecifier(IoSpecKind, bool, const std::string &) const; void CheckForProhibitedSpecifier(bool, const std::string &, IoSpecKind) const; + template + void CheckForDefinableVariable(const A &var, const std::string &s) const; + + void CheckForPureSubprogram() const; + void Init(IoStmtKind s) { stmt_ = s; specifierSet_.reset(); @@ -130,8 +135,6 @@ class IoChecker : public virtual BaseChecker { void Done() { stmt_ = IoStmtKind::None; } - void CheckForPureSubprogram() const; - SemanticsContext &context_; IoStmtKind stmt_{IoStmtKind::None}; common::EnumSet specifierSet_; diff --git a/flang/lib/Semantics/program-tree.cpp b/flang/lib/Semantics/program-tree.cpp index e6dfd9b2f51cdd..9466a748567e1a 100644 --- a/flang/lib/Semantics/program-tree.cpp +++ b/flang/lib/Semantics/program-tree.cpp @@ -112,6 +112,10 @@ ProgramTree ProgramTree::Build(const parser::BlockData &x) { return result.set_stmt(stmt).set_endStmt(end); } +ProgramTree ProgramTree::Build(const parser::CompilerDirective &) { + DIE("ProgramTree::Build() called for CompilerDirective"); +} + const parser::ParentIdentifier &ProgramTree::GetParentId() const { const auto *stmt{ std::get *>(stmt_)}; diff --git a/flang/lib/Semantics/program-tree.h b/flang/lib/Semantics/program-tree.h index 69c133bfbde7bb..6b074522820173 100644 --- a/flang/lib/Semantics/program-tree.h +++ b/flang/lib/Semantics/program-tree.h @@ -38,6 +38,7 @@ class ProgramTree { static ProgramTree Build(const parser::Module &); static ProgramTree Build(const parser::Submodule &); static ProgramTree Build(const parser::BlockData &); + static ProgramTree Build(const parser::CompilerDirective &); ENUM_CLASS(Kind, // kind of node Program, Function, Subroutine, MpSubprogram, Module, Submodule, BlockData) diff --git a/flang/lib/Semantics/resolve-names.cpp b/flang/lib/Semantics/resolve-names.cpp index 60376a1b84690d..36c38c1cd99c4b 100644 --- a/flang/lib/Semantics/resolve-names.cpp +++ b/flang/lib/Semantics/resolve-names.cpp @@ -6226,6 +6226,11 @@ void ResolveNamesVisitor::Post(const parser::AssignedGotoStmt &x) { } bool ResolveNamesVisitor::Pre(const parser::ProgramUnit &x) { + if (std::holds_alternative>( + x.u)) { + // TODO: global directives + return true; + } auto root{ProgramTree::Build(x)}; SetScope(context().globalScope()); ResolveSpecificationParts(root); diff --git a/flang/test/Parser/compiler-directives.f90 b/flang/test/Parser/compiler-directives.f90 index 5545d0486e56f9..c916b16b532742 100644 --- a/flang/test/Parser/compiler-directives.f90 +++ b/flang/test/Parser/compiler-directives.f90 @@ -2,6 +2,7 @@ ! Test that compiler directives can appear in various places. +!dir$ integer module m !dir$ integer use iso_fortran_env diff --git a/flang/test/Preprocessing/fixed-rescan.F b/flang/test/Preprocessing/fixed-rescan.F new file mode 100644 index 00000000000000..3d6ba9a8c6f450 --- /dev/null +++ b/flang/test/Preprocessing/fixed-rescan.F @@ -0,0 +1,7 @@ +! RUN: %f18 -E %s | FileCheck %s +! CHECK: callbar +! Ensure that rescanned lines after macro replacement are not +! misinterpreted as fixed-form comments when they start with C or D. +#define foo bar + call foo + end diff --git a/flang/test/Preprocessing/pp029.F b/flang/test/Preprocessing/pp029.F index bb8efe6c1a2e03..9be309f75143e5 100644 --- a/flang/test/Preprocessing/pp029.F +++ b/flang/test/Preprocessing/pp029.F @@ -1,5 +1,5 @@ ! RUN: %f18 -E %s 2>&1 | FileCheck %s -! CHECK: if(77 7.eq.777)then +! CHECK: if(777.eq.777)then * \ newline allowed in #define integer, parameter :: KWM = 666 #define KWM 77\ diff --git a/flang/test/Preprocessing/pp130.F90 b/flang/test/Preprocessing/pp130.F90 index af4ad126e6fa48..be1148807b8bfd 100644 --- a/flang/test/Preprocessing/pp130.F90 +++ b/flang/test/Preprocessing/pp130.F90 @@ -1,5 +1,5 @@ -! RUN: %f18 -E %s 2>&1 | FileCheck %s -! CHECK: j = j + & +! RUN: (%f18 -E %s 2>&1 || true) | FileCheck %s +! CHECK: error: bad character ('&') in Fortran token ! #define KWM &, use for continuation w/o pasting (ifort and nag seem to continue #define) #define KWM & diff --git a/flang/test/Semantics/deallocate05.f90 b/flang/test/Semantics/deallocate05.f90 index 7524cc88fe0b8b..4a54469e5ab676 100644 --- a/flang/test/Semantics/deallocate05.f90 +++ b/flang/test/Semantics/deallocate05.f90 @@ -21,6 +21,7 @@ Program deallocatetest Real :: r Integer :: s +Integer, Parameter :: const_s = 13 Integer :: e Integer :: pi Character(256) :: ee @@ -56,6 +57,8 @@ Program deallocatetest !ERROR: STAT may not be duplicated in a DEALLOCATE statement Deallocate(x, stat=s, stat=s) +!ERROR: STAT variable 'const_s' must be definable +Deallocate(x, stat=const_s) !ERROR: ERRMSG may not be duplicated in a DEALLOCATE statement Deallocate(x, errmsg=ee, errmsg=ee) !ERROR: STAT may not be duplicated in a DEALLOCATE statement diff --git a/flang/test/Semantics/io01.f90 b/flang/test/Semantics/io01.f90 index 4238df89f5d0ee..9828d4afe8921f 100644 --- a/flang/test/Semantics/io01.f90 +++ b/flang/test/Semantics/io01.f90 @@ -21,6 +21,7 @@ integer :: unit10 = 10 integer :: unit11 = 11 integer :: n = 40 + integer, parameter :: const_new_unit = 66 integer(kind=1) :: stat1 integer(kind=2) :: stat2 @@ -73,6 +74,9 @@ !ERROR: If NEWUNIT appears, FILE or STATUS must also appear open(newunit=n, newunit=nn, iostat=stat4) + !ERROR: NEWUNIT variable 'const_new_unit' must be definable + open(newunit=const_new_unit, status=cc) + !ERROR: Duplicate UNIT specifier open(unit=100, unit=100) diff --git a/flang/test/Semantics/io02.f90 b/flang/test/Semantics/io02.f90 index 5fd5fca4bc0c8d..9f5235d353cbd9 100644 --- a/flang/test/Semantics/io02.f90 +++ b/flang/test/Semantics/io02.f90 @@ -1,6 +1,7 @@ ! RUN: %S/test_errors.sh %s %t %f18 integer :: unit10 = 10 integer :: unit11 = 11 + integer, parameter :: const_stat = 6666 integer(kind=1) :: stat1 integer(kind=8) :: stat8 @@ -28,5 +29,8 @@ !ERROR: Invalid STATUS value 'old' close(status='old', unit=17) + !ERROR: IOSTAT variable 'const_stat' must be definable + close(14, iostat=const_stat) + 9 continue end diff --git a/flang/test/Semantics/io03.f90 b/flang/test/Semantics/io03.f90 index 0041e6cd0f5c44..5eb3420d1aea17 100644 --- a/flang/test/Semantics/io03.f90 +++ b/flang/test/Semantics/io03.f90 @@ -2,13 +2,18 @@ character(kind=1,len=50) internal_file character(kind=2,len=50) internal_file2 character(kind=4,len=50) internal_file4 + character(kind=1,len=50) internal_fileA(20) character(kind=1,len=111) msg character(20) advance + character(20) :: cvar; + character, parameter :: const_internal_file = "(I6)" + character, parameter :: const_cvar = "Ceci n'est pas une pipe." integer*1 stat1 integer*2 stat2, id2 integer*8 stat8 integer :: iunit = 10 - integer, parameter :: junit = 11 + integer, parameter :: junit = 11, const_size = 13, const_int = 15 + integer :: vv(10) = 7 namelist /mmm/ mm1, mm2 namelist /nnn/ nn1, nn2 @@ -29,11 +34,14 @@ read(fmt='(I4)', unit=*) jj read(iunit, *) jj read(junit, *) jj - read(10, *) jj + read(10, *) jj, cvar, cvar(7:17) read(internal_file, *) jj + read(internal_fileA(3), *) jj + read(internal_fileA(4:9), *) jj read(10, nnn) read(internal_file, nnn) read(internal_file, nml=nnn) + read(const_internal_file, *) read(fmt=*, unit=internal_file) read(nml=nnn, unit=internal_file) read(iunit, nnn) @@ -53,6 +61,21 @@ !ERROR: Invalid character kind for an internal file variable read(internal_file4, *) jj + !ERROR: Internal file must not have a vector subscript + read(internal_fileA(vv), *) jj + + !ERROR: Input variable 'const_int' must be definable + read(11, *) const_int + + !ERROR: SIZE variable 'const_size' must be definable + read(11, pos=ipos, size=const_size, end=9) + + !ERROR: Input variable 'const_cvar' must be definable + read(11, *) const_cvar + + !ERROR: Input variable 'const_cvar' must be definable + read(11, *) const_cvar(3:13) + !ERROR: Duplicate IOSTAT specifier read(11, pos=ipos, iostat=stat1, iostat=stat2) @@ -136,3 +159,25 @@ 9 continue end + +subroutine s(aa, n) + integer :: aa(5,*) + integer, intent(in) :: n + integer :: bb(10), vv(10) + type tt + real :: x, y, z + end type tt + type(tt) :: qq(20) + + vv = 1 + + read(*, *) aa(n,1) + read(*, *) aa(n:n+2,2) + read(*, *) qq(2:5)%y + + !ERROR: Input variable 'n' must be definable + read(*, *) n + + !ERROR: Whole assumed size array 'aa' may not be an input item + read(*, *) aa +end diff --git a/flang/test/Semantics/io04.f90 b/flang/test/Semantics/io04.f90 index 0a37d685d3ee50..6be26047fd5b28 100644 --- a/flang/test/Semantics/io04.f90 +++ b/flang/test/Semantics/io04.f90 @@ -2,6 +2,7 @@ character(kind=1,len=50) internal_file character(kind=1,len=100) msg character(20) sign + character, parameter :: const_internal_file = "(I6)" integer*1 stat1, id1 integer*2 stat2 integer*4 stat4 @@ -9,6 +10,8 @@ integer :: iunit = 10 integer, parameter :: junit = 11 integer, pointer :: a(:) + integer, parameter :: const_id = 66666 + procedure(), pointer :: procptr namelist /nnn/ nn1, nn2 @@ -66,6 +69,9 @@ !ERROR: If NML appears, a data list must not appear write(10, nnn, rec=40, fmt=1) 'Ok' + !ERROR: Internal file variable 'const_internal_file' must be definable + write(const_internal_file, fmt=*) + !ERROR: If UNIT=* appears, POS must not appear write(*, pos=n, nml=nnn) @@ -118,8 +124,14 @@ !ERROR: ID kind (1) is smaller than default INTEGER kind (4) write(id=id1, unit=10, asynchronous='Yes') 'Ok' + !ERROR: ID variable 'const_id' must be definable + write(10, *, asynchronous='yes', id=const_id, iostat=stat2) 'Ok' + write(*, '(X)') + !ERROR: Output item must not be a procedure pointer + print*, n1, procptr, n2 + 1 format (A) 9 continue end diff --git a/flang/test/Semantics/io05.f90 b/flang/test/Semantics/io05.f90 index 1501fbf587f5ea..ed6b77f7d4ad97 100644 --- a/flang/test/Semantics/io05.f90 +++ b/flang/test/Semantics/io05.f90 @@ -1,10 +1,12 @@ ! RUN: %S/test_errors.sh %s %t %f18 character*20 c(25), cv character(kind=1,len=59) msg + character, parameter :: const_round = "c'est quoi?" logical*2 v(5), lv integer*1 stat1 integer*2 stat4 integer*8 stat8, iv + integer, parameter :: const_id = 1 inquire(10) inquire(file='abc') @@ -22,6 +24,7 @@ exist=v(1), named=v(2), opened=v(3), pending=v(4)) inquire(pending=v(5), file='abc') inquire(10, id=id, pending=v(5)) + inquire(10, id=const_id, pending=v(5)) ! using variable 'cv' multiple times seems to be allowed inquire(file='abc', & @@ -56,5 +59,8 @@ !ERROR: If ID appears, PENDING must also appear inquire(file='abc', id=id) + !ERROR: ROUND variable 'const_round' must be definable + inquire(file='abc', round=const_round) + 9 continue end diff --git a/flang/test/Semantics/io06.f90 b/flang/test/Semantics/io06.f90 index 157d831dc33315..fe3b97f0e67e4a 100644 --- a/flang/test/Semantics/io06.f90 +++ b/flang/test/Semantics/io06.f90 @@ -1,6 +1,7 @@ ! RUN: %S/test_errors.sh %s %t %f18 character(kind=1,len=100) msg1 character(kind=2,len=200) msg2 + character, parameter :: const_msg = 'doof' integer(1) stat1 integer(2) stat2 integer(8) stat8 @@ -28,6 +29,9 @@ !ERROR: Duplicate IOSTAT specifier endfile(iostat=stat2, err=9, unit=10, iostat=stat8, iomsg=msg1) + !ERROR: IOMSG variable 'const_msg' must be definable + flush(iomsg=const_msg, unit=10, iostat=stat8, err=9) + !ERROR: REWIND statement must have a UNIT number specifier rewind(iostat=stat2) diff --git a/lldb/test/API/macosx/queues/TestQueues.py b/lldb/test/API/macosx/queues/TestQueues.py index e177daa54fa347..711c99a7d400d6 100644 --- a/lldb/test/API/macosx/queues/TestQueues.py +++ b/lldb/test/API/macosx/queues/TestQueues.py @@ -192,7 +192,6 @@ def queues(self): user_initiated_thread = lldb.SBThread() user_interactive_thread = lldb.SBThread() utility_thread = lldb.SBThread() - unspecified_thread = lldb.SBThread() background_thread = lldb.SBThread() for th in process.threads: if th.GetName() == "user initiated QoS": @@ -201,8 +200,6 @@ def queues(self): user_interactive_thread = th if th.GetName() == "utility QoS": utility_thread = th - if th.GetName() == "unspecified QoS": - unspecified_thread = th if th.GetName() == "background QoS": background_thread = th @@ -213,9 +210,6 @@ def queues(self): user_interactive_thread.IsValid(), "Found user interactive QoS thread") self.assertTrue(utility_thread.IsValid(), "Found utility QoS thread") - self.assertTrue( - unspecified_thread.IsValid(), - "Found unspecified QoS thread") self.assertTrue( background_thread.IsValid(), "Found background QoS thread") @@ -248,16 +242,6 @@ def queues(self): stream.GetData(), "Utility", "utility QoS thread name is valid") stream.Clear() - self.assertTrue( - unspecified_thread.GetInfoItemByPathAsString( - "requested_qos.printable_name", - stream), - "Get QoS printable string for unspecified QoS thread") - qosName = stream.GetData() - self.assertTrue( - qosName == "User Initiated" or qosName == "Default", - "unspecified QoS thread name is valid: " + str(qosName)) - stream.Clear() self.assertTrue( background_thread.GetInfoItemByPathAsString( "requested_qos.printable_name", diff --git a/lldb/test/API/macosx/queues/main.c b/lldb/test/API/macosx/queues/main.c index 3978b92bff1a62..2bf390b1330a63 100644 --- a/lldb/test/API/macosx/queues/main.c +++ b/lldb/test/API/macosx/queues/main.c @@ -136,15 +136,9 @@ int main (int argc, const char **argv) while (1) sleep (10); }); - dispatch_async (dispatch_get_global_queue(QOS_CLASS_UNSPECIFIED, 0), ^{ - pthread_setname_np ("unspecified QoS"); - atomic_fetch_add(&thread_count, 1); - while (1) - sleep (10); - }); // Unfortunately there is no pthread_barrier on darwin. - while ((atomic_load(&thread_count) < 13) || (finished_enqueueing_work == 0)) + while ((atomic_load(&thread_count) < 12) || (finished_enqueueing_work == 0)) sleep (1); stopper (); diff --git a/lldb/unittests/Symbol/LocateSymbolFileTest.cpp b/lldb/unittests/Symbol/LocateSymbolFileTest.cpp index 268faeaf1dbbda..a2f9be56635d1f 100644 --- a/lldb/unittests/Symbol/LocateSymbolFileTest.cpp +++ b/lldb/unittests/Symbol/LocateSymbolFileTest.cpp @@ -14,6 +14,7 @@ #include "lldb/Host/HostInfo.h" #include "lldb/Symbol/LocateSymbolFile.h" #include "lldb/Target/Target.h" +#include "lldb/Utility/Reproducer.h" using namespace lldb_private; @@ -27,15 +28,22 @@ class SymbolsTest : public ::testing::Test { TEST_F( SymbolsTest, TerminateLocateExecutableSymbolFileForUnknownExecutableAndUnknownSymbolFile) { + EXPECT_THAT_ERROR( + repro::Reproducer::Initialize(repro::ReproducerMode::Off, llvm::None), + llvm::Succeeded()); ModuleSpec module_spec; FileSpecList search_paths = Target::GetDefaultDebugFileSearchPaths(); FileSpec symbol_file_spec = Symbols::LocateExecutableSymbolFile(module_spec, search_paths); EXPECT_TRUE(symbol_file_spec.GetFilename().IsEmpty()); + repro::Reproducer::Terminate(); } TEST_F(SymbolsTest, LocateExecutableSymbolFileForUnknownExecutableAndMissingSymbolFile) { + EXPECT_THAT_ERROR( + repro::Reproducer::Initialize(repro::ReproducerMode::Off, llvm::None), + llvm::Succeeded()); ModuleSpec module_spec; // using a GUID here because the symbol file shouldn't actually exist on disk module_spec.GetSymbolFileSpec().SetFile( @@ -44,4 +52,5 @@ TEST_F(SymbolsTest, FileSpec symbol_file_spec = Symbols::LocateExecutableSymbolFile(module_spec, search_paths); EXPECT_TRUE(symbol_file_spec.GetFilename().IsEmpty()); + repro::Reproducer::Terminate(); } diff --git a/llvm/docs/ReleaseNotes.rst b/llvm/docs/ReleaseNotes.rst index 2a2775f13dd1fa..c6b2a3ac9bb57a 100644 --- a/llvm/docs/ReleaseNotes.rst +++ b/llvm/docs/ReleaseNotes.rst @@ -97,6 +97,7 @@ During this release ... * The 'mpx' feature was removed from the backend. It had been removed from clang frontend in 10.0. Mention of the 'mpx' feature in an IR file will print a message to stderr, but IR should still compile. +* Support for -march=sapphirerapids was added. Changes to the AMDGPU Target ----------------------------- diff --git a/llvm/examples/OrcV2Examples/LLJITWithThinLTOSummaries/CMakeLists.txt b/llvm/examples/OrcV2Examples/LLJITWithThinLTOSummaries/CMakeLists.txt index 22262e4eb5b972..175e655007050a 100644 --- a/llvm/examples/OrcV2Examples/LLJITWithThinLTOSummaries/CMakeLists.txt +++ b/llvm/examples/OrcV2Examples/LLJITWithThinLTOSummaries/CMakeLists.txt @@ -1,7 +1,7 @@ set(LLVM_LINK_COMPONENTS + BitReader Core ExecutionEngine - IRReader OrcJIT Support nativecodegen diff --git a/llvm/include/llvm/Analysis/ValueTracking.h b/llvm/include/llvm/Analysis/ValueTracking.h index f1b9cc906049be..0c82311d857c13 100644 --- a/llvm/include/llvm/Analysis/ValueTracking.h +++ b/llvm/include/llvm/Analysis/ValueTracking.h @@ -584,10 +584,10 @@ constexpr unsigned MaxAnalysisRecursionDepth = 6; /// getGuaranteedNonPoisonOp. bool propagatesPoison(const Instruction *I); - /// Return either nullptr or an operand of I such that I will trigger - /// undefined behavior if I is executed and that operand has a poison - /// value. - const Value *getGuaranteedNonPoisonOp(const Instruction *I); + /// Insert operands of I into Ops such that I will trigger undefined behavior + /// if I is executed and that operand has a poison value. + void getGuaranteedNonPoisonOps(const Instruction *I, + SmallPtrSetImpl &Ops); /// Return true if the given instruction must trigger undefined behavior. /// when I is executed with any operands which appear in KnownPoison holding diff --git a/llvm/include/llvm/ExecutionEngine/Orc/TPCIndirectionUtils.h b/llvm/include/llvm/ExecutionEngine/Orc/TPCIndirectionUtils.h index db9cd1b98cf9cb..90097f1131f317 100644 --- a/llvm/include/llvm/ExecutionEngine/Orc/TPCIndirectionUtils.h +++ b/llvm/include/llvm/ExecutionEngine/Orc/TPCIndirectionUtils.h @@ -206,4 +206,4 @@ TPCIndirectionUtils::CreateWithABI(TargetProcessControl &TPC) { } // end namespace orc } // end namespace llvm -#endif // LLVM_EXECUTIONENGINE_ORC_T_H +#endif // LLVM_EXECUTIONENGINE_ORC_TPCINDIRECTIONUTILS_H diff --git a/llvm/lib/Analysis/MemoryDependenceAnalysis.cpp b/llvm/lib/Analysis/MemoryDependenceAnalysis.cpp index 7f3de0fcf140af..2428d57d2809fa 100644 --- a/llvm/lib/Analysis/MemoryDependenceAnalysis.cpp +++ b/llvm/lib/Analysis/MemoryDependenceAnalysis.cpp @@ -362,6 +362,8 @@ MemoryDependenceResults::getInvariantGroupPointerDependency(LoadInst *LI, MemDepResult MemoryDependenceResults::getSimplePointerDependencyFrom( const MemoryLocation &MemLoc, bool isLoad, BasicBlock::iterator ScanIt, BasicBlock *BB, Instruction *QueryInst, unsigned *Limit) { + // We can batch AA queries, because IR does not change during a MemDep query. + BatchAAResults BatchAA(AA); bool isInvariantLoad = false; unsigned DefaultLimit = getDefaultBlockScanLimit(); @@ -445,7 +447,7 @@ MemDepResult MemoryDependenceResults::getSimplePointerDependencyFrom( // pointer, not on query pointers that are indexed off of them. It'd // be nice to handle that at some point (the right approach is to use // GetPointerBaseWithConstantOffset). - if (AA.isMustAlias(MemoryLocation(II->getArgOperand(1)), MemLoc)) + if (BatchAA.isMustAlias(MemoryLocation(II->getArgOperand(1)), MemLoc)) return MemDepResult::getDef(II); continue; } @@ -485,7 +487,7 @@ MemDepResult MemoryDependenceResults::getSimplePointerDependencyFrom( MemoryLocation LoadLoc = MemoryLocation::get(LI); // If we found a pointer, check if it could be the same as our pointer. - AliasResult R = AA.alias(LoadLoc, MemLoc); + AliasResult R = BatchAA.alias(LoadLoc, MemLoc); if (isLoad) { if (R == NoAlias) @@ -516,7 +518,7 @@ MemDepResult MemoryDependenceResults::getSimplePointerDependencyFrom( continue; // Stores don't alias loads from read-only memory. - if (AA.pointsToConstantMemory(LoadLoc)) + if (BatchAA.pointsToConstantMemory(LoadLoc)) continue; // Stores depend on may/must aliased loads. @@ -547,7 +549,7 @@ MemDepResult MemoryDependenceResults::getSimplePointerDependencyFrom( // If alias analysis can tell that this store is guaranteed to not modify // the query pointer, ignore it. Use getModRefInfo to handle cases where // the query pointer points to constant memory etc. - if (!isModOrRefSet(AA.getModRefInfo(SI, MemLoc))) + if (!isModOrRefSet(BatchAA.getModRefInfo(SI, MemLoc))) continue; // Ok, this store might clobber the query pointer. Check to see if it is @@ -556,7 +558,7 @@ MemDepResult MemoryDependenceResults::getSimplePointerDependencyFrom( MemoryLocation StoreLoc = MemoryLocation::get(SI); // If we found a pointer, check if it could be the same as our pointer. - AliasResult R = AA.alias(StoreLoc, MemLoc); + AliasResult R = BatchAA.alias(StoreLoc, MemLoc); if (R == NoAlias) continue; @@ -575,7 +577,7 @@ MemDepResult MemoryDependenceResults::getSimplePointerDependencyFrom( // handled by BasicAA. if (isa(Inst) || isNoAliasFn(Inst, &TLI)) { const Value *AccessPtr = getUnderlyingObject(MemLoc.Ptr); - if (AccessPtr == Inst || AA.isMustAlias(Inst, AccessPtr)) + if (AccessPtr == Inst || BatchAA.isMustAlias(Inst, AccessPtr)) return MemDepResult::getDef(Inst); } @@ -592,9 +594,10 @@ MemDepResult MemoryDependenceResults::getSimplePointerDependencyFrom( continue; // See if this instruction (e.g. a call or vaarg) mod/ref's the pointer. - ModRefInfo MR = AA.getModRefInfo(Inst, MemLoc); + ModRefInfo MR = BatchAA.getModRefInfo(Inst, MemLoc); // If necessary, perform additional analysis. if (isModAndRefSet(MR)) + // TODO: Support callCapturesBefore() on BatchAAResults. MR = AA.callCapturesBefore(Inst, MemLoc, &DT); switch (clearMust(MR)) { case ModRefInfo::NoModRef: diff --git a/llvm/lib/Analysis/ValueTracking.cpp b/llvm/lib/Analysis/ValueTracking.cpp index 07c4e57228f114..2835d2f06edd3f 100644 --- a/llvm/lib/Analysis/ValueTracking.cpp +++ b/llvm/lib/Analysis/ValueTracking.cpp @@ -5065,46 +5065,69 @@ bool llvm::propagatesPoison(const Instruction *I) { } } -const Value *llvm::getGuaranteedNonPoisonOp(const Instruction *I) { +void llvm::getGuaranteedNonPoisonOps(const Instruction *I, + SmallPtrSetImpl &Operands) { switch (I->getOpcode()) { case Instruction::Store: - return cast(I)->getPointerOperand(); + Operands.insert(cast(I)->getPointerOperand()); + break; case Instruction::Load: - return cast(I)->getPointerOperand(); + Operands.insert(cast(I)->getPointerOperand()); + break; case Instruction::AtomicCmpXchg: - return cast(I)->getPointerOperand(); + Operands.insert(cast(I)->getPointerOperand()); + break; case Instruction::AtomicRMW: - return cast(I)->getPointerOperand(); + Operands.insert(cast(I)->getPointerOperand()); + break; case Instruction::UDiv: case Instruction::SDiv: case Instruction::URem: case Instruction::SRem: - return I->getOperand(1); + Operands.insert(I->getOperand(1)); + break; case Instruction::Call: + case Instruction::Invoke: { if (auto *II = dyn_cast(I)) { switch (II->getIntrinsicID()) { case Intrinsic::assume: - return II->getArgOperand(0); + Operands.insert(II->getArgOperand(0)); + break; default: - return nullptr; + break; } } - return nullptr; + + const CallBase *CB = cast(I); + if (CB->isIndirectCall()) + Operands.insert(CB->getCalledOperand()); + for (unsigned i = 0; i < CB->arg_size(); ++i) { + if (CB->paramHasAttr(i, Attribute::NoUndef)) + Operands.insert(CB->getArgOperand(i)); + } + break; + } default: - return nullptr; + break; } } bool llvm::mustTriggerUB(const Instruction *I, const SmallSet& KnownPoison) { - auto *NotPoison = getGuaranteedNonPoisonOp(I); - return (NotPoison && KnownPoison.count(NotPoison)); + SmallPtrSet NonPoisonOps; + getGuaranteedNonPoisonOps(I, NonPoisonOps); + + for (const auto *V : NonPoisonOps) + if (KnownPoison.count(V)) + return true; + + return false; } diff --git a/llvm/lib/LTO/LTO.cpp b/llvm/lib/LTO/LTO.cpp index 6e1e3998e490e3..6230216aa44666 100644 --- a/llvm/lib/LTO/LTO.cpp +++ b/llvm/lib/LTO/LTO.cpp @@ -798,7 +798,7 @@ Error LTO::linkRegularLTO(RegularLTOState::AddedModule Mod, for (GlobalValue *GV : Mod.Keep) { if (LivenessFromIndex && !ThinLTO.CombinedIndex.isGUIDLive(GV->getGUID())) { if (Function *F = dyn_cast(GV)) { - OptimizationRemarkEmitter ORE(F); + OptimizationRemarkEmitter ORE(F, nullptr); ORE.emit(OptimizationRemark(DEBUG_TYPE, "deadfunction", F) << ore::NV("Function", F) << " not added to the combined module "); diff --git a/llvm/lib/Transforms/Instrumentation/PoisonChecking.cpp b/llvm/lib/Transforms/Instrumentation/PoisonChecking.cpp index fa97a194ea2b52..6f785687b50452 100644 --- a/llvm/lib/Transforms/Instrumentation/PoisonChecking.cpp +++ b/llvm/lib/Transforms/Instrumentation/PoisonChecking.cpp @@ -282,8 +282,10 @@ static bool rewrite(Function &F) { // Note: There are many more sources of documented UB, but this pass only // attempts to find UB triggered by propagation of poison. - if (Value *Op = const_cast(getGuaranteedNonPoisonOp(&I))) - CreateAssertNot(B, getPoisonFor(ValToPoison, Op)); + SmallPtrSet NonPoisonOps; + getGuaranteedNonPoisonOps(&I, NonPoisonOps); + for (const Value *Op : NonPoisonOps) + CreateAssertNot(B, getPoisonFor(ValToPoison, const_cast(Op))); if (LocalCheck) if (auto *RI = dyn_cast(&I)) diff --git a/llvm/test/CodeGen/AArch64/stack-guard-reassign.ll b/llvm/test/CodeGen/AArch64/stack-guard-reassign.ll index 6b00eda6233a28..71d3d27a1b6490 100644 --- a/llvm/test/CodeGen/AArch64/stack-guard-reassign.ll +++ b/llvm/test/CodeGen/AArch64/stack-guard-reassign.ll @@ -3,6 +3,6 @@ ; Verify that the offset assigned to the stack protector is at the top of the ; frame, covering the locals. ; CHECK-LABEL: fn: -; CHECK: adrp x8, __stack_chk_guard -; CHECK-NEXT: ldr x8, [x8, :lo12:__stack_chk_guard] -; CHECK-NEXT: stur x8, [x29, #-8] +; CHECK: adrp [[REG:x[0-9]+]], __stack_chk_guard +; CHECK-NEXT: ldr [[REG]], {{\[}}[[REG]], :lo12:__stack_chk_guard] +; CHECK-NEXT: stur [[REG]], [x29, #-8] diff --git a/llvm/test/LTO/Resolution/X86/dead-strip-fulllto.ll b/llvm/test/LTO/Resolution/X86/dead-strip-fulllto.ll index 43659313f2dc5c..fb4fde7661b7d4 100644 --- a/llvm/test/LTO/Resolution/X86/dead-strip-fulllto.ll +++ b/llvm/test/LTO/Resolution/X86/dead-strip-fulllto.ll @@ -1,7 +1,8 @@ ; RUN: opt -module-summary -o %t %s ; RUN: opt -module-summary -o %t2 %S/Inputs/dead-strip-fulllto.ll -; RUN: llvm-lto2 run --pass-remarks-output=%t4.yaml --pass-remarks-filter=. \ +; Adding '--pass-remarks-with-hotness' should not cause crash. +; RUN: llvm-lto2 run --pass-remarks-output=%t4.yaml --pass-remarks-filter=. --pass-remarks-with-hotness \ ; RUN: %t -r %t,main,px -r %t,live1, -r %t,live2,p -r %t,dead2,p \ ; RUN: %t2 -r %t2,live1,p -r %t2,live2, -r %t2,dead1,p -r %t2,dead2, -r %t2,odr, \ ; RUN: -save-temps -o %t3 diff --git a/llvm/test/Transforms/InstSimplify/freeze-noundef.ll b/llvm/test/Transforms/InstSimplify/freeze-noundef.ll index f2e897b507e3d2..6a52bd5b257f0e 100644 --- a/llvm/test/Transforms/InstSimplify/freeze-noundef.ll +++ b/llvm/test/Transforms/InstSimplify/freeze-noundef.ll @@ -69,7 +69,6 @@ define {i8, i32} @aggr({i8, i32} noundef %x) { ret {i8, i32} %y } -; TODO: should look into extract operations define i32 @extract({i8, i32} noundef %x) { ; CHECK-LABEL: @extract( ; CHECK-NEXT: [[Y:%.*]] = extractvalue { i8, i32 } [[X:%.*]], 1 @@ -91,3 +90,17 @@ define i32 @extract2({i8, {i8, i32}} noundef %x) { %w = freeze i32 %z ret i32 %w } + +declare void @use_i1(i1 noundef) + +define i1 @used_by_fncall(i1 %x) { +; CHECK-LABEL: @used_by_fncall( +; CHECK-NEXT: [[Y:%.*]] = add nsw i1 [[X:%.*]], true +; CHECK-NEXT: call void @use_i1(i1 [[Y]]) +; CHECK-NEXT: ret i1 [[Y]] +; + %y = add nsw i1 %x, 1 + call void @use_i1(i1 %y) + %f = freeze i1 %y + ret i1 %f +} diff --git a/llvm/test/Transforms/LoopVectorize/PowerPC/massv-altivec.ll b/llvm/test/Transforms/LoopVectorize/PowerPC/massv-altivec.ll index c63db5b0b66363..df2aa99f2d4b4d 100644 --- a/llvm/test/Transforms/LoopVectorize/PowerPC/massv-altivec.ll +++ b/llvm/test/Transforms/LoopVectorize/PowerPC/massv-altivec.ll @@ -1,4 +1,4 @@ -; RUN: opt -vector-library=MASSV -loop-vectorize -force-vector-interleave=1 -mattr=-altivec -S < %s | FileCheck %s +; RUN: opt -vector-library=MASSV -inject-tli-mappings -loop-vectorize -force-vector-interleave=1 -mattr=-altivec -S < %s | FileCheck %s target datalayout = "e-m:e-i64:64-n32:64" target triple = "powerpc64le-unknown-linux-gnu" diff --git a/llvm/test/Transforms/LoopVectorize/PowerPC/massv-calls.ll b/llvm/test/Transforms/LoopVectorize/PowerPC/massv-calls.ll index a08c23b5b2dd8b..eafca05c5aa778 100644 --- a/llvm/test/Transforms/LoopVectorize/PowerPC/massv-calls.ll +++ b/llvm/test/Transforms/LoopVectorize/PowerPC/massv-calls.ll @@ -1,4 +1,4 @@ -; RUN: opt -vector-library=MASSV -loop-vectorize -force-vector-interleave=1 -S < %s | FileCheck %s +; RUN: opt -vector-library=MASSV -inject-tli-mappings -loop-vectorize -force-vector-interleave=1 -S < %s | FileCheck %s target datalayout = "e-m:e-i64:64-n32:64" target triple = "powerpc64le-unknown-linux-gnu" diff --git a/llvm/test/Transforms/LoopVectorize/PowerPC/massv-nobuiltin.ll b/llvm/test/Transforms/LoopVectorize/PowerPC/massv-nobuiltin.ll index e7503b615f44a8..872326934ab405 100644 --- a/llvm/test/Transforms/LoopVectorize/PowerPC/massv-nobuiltin.ll +++ b/llvm/test/Transforms/LoopVectorize/PowerPC/massv-nobuiltin.ll @@ -1,4 +1,4 @@ -; RUN: opt -vector-library=MASSV -loop-vectorize -force-vector-interleave=1 -S < %s | FileCheck %s +; RUN: opt -vector-library=MASSV -inject-tli-mappings -loop-vectorize -force-vector-interleave=1 -S < %s | FileCheck %s target datalayout = "e-m:e-i64:64-n32:64" target triple = "powerpc64le-unknown-linux-gnu" diff --git a/llvm/test/Transforms/LoopVectorize/PowerPC/massv-unsupported.ll b/llvm/test/Transforms/LoopVectorize/PowerPC/massv-unsupported.ll index f9b4b72a027e83..575f9b2700ad5b 100644 --- a/llvm/test/Transforms/LoopVectorize/PowerPC/massv-unsupported.ll +++ b/llvm/test/Transforms/LoopVectorize/PowerPC/massv-unsupported.ll @@ -1,4 +1,4 @@ -; RUN: opt -vector-library=MASSV -loop-vectorize -force-vector-interleave=1 -S < %s | FileCheck %s +; RUN: opt -vector-library=MASSV -inject-tli-mappings -loop-vectorize -force-vector-interleave=1 -S < %s | FileCheck %s target datalayout = "e-m:e-i64:64-n32:64" target triple = "powerpc64le-unknown-linux-gnu" diff --git a/llvm/test/Transforms/LoopVectorize/PowerPC/widened-massv-call.ll b/llvm/test/Transforms/LoopVectorize/PowerPC/widened-massv-call.ll index 18098520908046..f5626598f29c34 100644 --- a/llvm/test/Transforms/LoopVectorize/PowerPC/widened-massv-call.ll +++ b/llvm/test/Transforms/LoopVectorize/PowerPC/widened-massv-call.ll @@ -1,6 +1,6 @@ ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py ; RUN: opt < %s -vector-library=MASSV -force-vector-interleave=1 \ -; RUN: -vectorizer-maximize-bandwidth -O2 -loop-vectorize \ +; RUN: -vectorizer-maximize-bandwidth -O2 -inject-tli-mappings -loop-vectorize \ ; RUN: -mtriple=powerpc64le-unknown-linux -S -mcpu=pwr9 2>&1 | FileCheck %s define dso_local double @test(float* %Arr) { diff --git a/llvm/test/Transforms/LoopVectorize/X86/svml-calls-finite.ll b/llvm/test/Transforms/LoopVectorize/X86/svml-calls-finite.ll index 1e55e7de91bb4c..d6e3469c7bdb79 100644 --- a/llvm/test/Transforms/LoopVectorize/X86/svml-calls-finite.ll +++ b/llvm/test/Transforms/LoopVectorize/X86/svml-calls-finite.ll @@ -1,4 +1,4 @@ -; RUN: opt -vector-library=SVML -loop-vectorize -S < %s | FileCheck %s +; RUN: opt -vector-library=SVML -inject-tli-mappings -loop-vectorize -S < %s | FileCheck %s ; Test to verify that when math headers are built with ; __FINITE_MATH_ONLY__ enabled, causing use of ___finite diff --git a/llvm/test/Transforms/LoopVectorize/X86/svml-calls.ll b/llvm/test/Transforms/LoopVectorize/X86/svml-calls.ll index 1f2b71f6ba21a5..aa8a25c3b87f57 100644 --- a/llvm/test/Transforms/LoopVectorize/X86/svml-calls.ll +++ b/llvm/test/Transforms/LoopVectorize/X86/svml-calls.ll @@ -1,4 +1,4 @@ -; RUN: opt -vector-library=SVML -loop-vectorize -force-vector-width=4 -force-vector-interleave=1 -mattr=avx -S < %s | FileCheck %s +; RUN: opt -vector-library=SVML -inject-tli-mappings -loop-vectorize -force-vector-width=4 -force-vector-interleave=1 -mattr=avx -S < %s | FileCheck %s target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" target triple = "x86_64-unknown-linux-gnu" diff --git a/llvm/test/Transforms/LoopVectorize/X86/veclib-calls.ll b/llvm/test/Transforms/LoopVectorize/X86/veclib-calls.ll index 6f8f5223ce4588..8cbcf9351d83c2 100644 --- a/llvm/test/Transforms/LoopVectorize/X86/veclib-calls.ll +++ b/llvm/test/Transforms/LoopVectorize/X86/veclib-calls.ll @@ -1,4 +1,4 @@ -; RUN: opt < %s -vector-library=Accelerate -loop-vectorize -S | FileCheck %s +; RUN: opt < %s -vector-library=Accelerate -inject-tli-mappings -loop-vectorize -S | FileCheck %s target datalayout = "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-s0:64:64-f80:128:128-n8:16:32:64-S128" target triple = "x86_64-unknown-linux-gnu" diff --git a/llvm/tools/llvm-mca/Views/BottleneckAnalysis.cpp b/llvm/tools/llvm-mca/Views/BottleneckAnalysis.cpp index 99deed6eae976d..519b928fda5dc0 100644 --- a/llvm/tools/llvm-mca/Views/BottleneckAnalysis.cpp +++ b/llvm/tools/llvm-mca/Views/BottleneckAnalysis.cpp @@ -16,7 +16,6 @@ #include "llvm/MC/MCInst.h" #include "llvm/MCA/Support.h" #include "llvm/Support/Format.h" -#include "llvm/Support/FormattedStream.h" namespace llvm { namespace mca { @@ -284,21 +283,14 @@ void DependencyGraph::getCriticalSequence( } } -static void printInstruction(formatted_raw_ostream &FOS, - const MCSubtargetInfo &STI, MCInstPrinter &MCIP, - const MCInst &MCI, - bool UseDifferentColor = false) { - std::string Instruction; - raw_string_ostream InstrStream(Instruction); - +void BottleneckAnalysis::printInstruction(formatted_raw_ostream &FOS, + const MCInst &MCI, + bool UseDifferentColor) const { FOS.PadToColumn(14); - MCIP.printInst(&MCI, 0, "", STI, InstrStream); - InstrStream.flush(); - if (UseDifferentColor) FOS.changeColor(raw_ostream::CYAN, true, false); - FOS << StringRef(Instruction).ltrim(); + FOS << printInstructionString(MCI); if (UseDifferentColor) FOS.resetColor(); } @@ -316,6 +308,7 @@ void BottleneckAnalysis::printCriticalSequence(raw_ostream &OS) const { OS << "\nCritical sequence based on the simulation:\n\n"; const DependencyEdge &FirstEdge = *Seq[0]; + ArrayRef Source = getSource(); unsigned FromIID = FirstEdge.FromIID % Source.size(); unsigned ToIID = FirstEdge.ToIID % Source.size(); bool IsLoopCarried = FromIID >= ToIID; @@ -331,17 +324,17 @@ void BottleneckAnalysis::printCriticalSequence(raw_ostream &OS) const { unsigned CurrentIID = 0; if (IsLoopCarried) { FOS << "\n +----< " << FromIID << "."; - printInstruction(FOS, STI, MCIP, Source[FromIID], HasColors); + printInstruction(FOS, Source[FromIID], HasColors); FOS << "\n |\n | < loop carried > \n |"; } else { while (CurrentIID < FromIID) { FOS << "\n " << CurrentIID << "."; - printInstruction(FOS, STI, MCIP, Source[CurrentIID]); + printInstruction(FOS, Source[CurrentIID]); CurrentIID++; } FOS << "\n +----< " << CurrentIID << "."; - printInstruction(FOS, STI, MCIP, Source[CurrentIID], HasColors); + printInstruction(FOS, Source[CurrentIID], HasColors); CurrentIID++; } @@ -351,17 +344,17 @@ void BottleneckAnalysis::printCriticalSequence(raw_ostream &OS) const { while (CurrentIID < LastIID) { FOS << "\n | " << CurrentIID << "."; - printInstruction(FOS, STI, MCIP, Source[CurrentIID]); + printInstruction(FOS, Source[CurrentIID]); CurrentIID++; } if (CurrentIID == ToIID) { FOS << "\n +----> " << ToIID << "."; - printInstruction(FOS, STI, MCIP, Source[CurrentIID], HasColors); + printInstruction(FOS, Source[CurrentIID], HasColors); } else { FOS << "\n |\n | < loop carried > \n |" << "\n +----> " << ToIID << "."; - printInstruction(FOS, STI, MCIP, Source[ToIID], HasColors); + printInstruction(FOS, Source[ToIID], HasColors); } FOS.PadToColumn(58); @@ -373,7 +366,7 @@ void BottleneckAnalysis::printCriticalSequence(raw_ostream &OS) const { FOS << "## REGISTER dependency: "; if (HasColors) FOS.changeColor(raw_ostream::MAGENTA, true, false); - MCIP.printRegName(FOS, Dep.ResourceOrRegID); + getInstPrinter().printRegName(FOS, Dep.ResourceOrRegID); } else if (Dep.Type == DependencyEdge::DT_MEMORY) { FOS << "## MEMORY dependency."; } else { @@ -397,7 +390,7 @@ void BottleneckAnalysis::printCriticalSequence(raw_ostream &OS) const { while (CurrentIID < Source.size()) { FOS << "\n " << CurrentIID << "."; - printInstruction(FOS, STI, MCIP, Source[CurrentIID]); + printInstruction(FOS, Source[CurrentIID]); CurrentIID++; } @@ -451,8 +444,8 @@ void DependencyGraph::addDependency(unsigned From, unsigned To, BottleneckAnalysis::BottleneckAnalysis(const MCSubtargetInfo &sti, MCInstPrinter &Printer, ArrayRef S, unsigned NumIter) - : STI(sti), MCIP(Printer), Tracker(STI.getSchedModel()), DG(S.size() * 3), - Source(S), Iterations(NumIter), TotalCycles(0), + : InstructionView(sti, Printer, S), Tracker(sti.getSchedModel()), + DG(S.size() * 3), Iterations(NumIter), TotalCycles(0), PressureIncreasedBecauseOfResources(false), PressureIncreasedBecauseOfRegisterDependencies(false), PressureIncreasedBecauseOfMemoryDependencies(false), @@ -461,7 +454,7 @@ BottleneckAnalysis::BottleneckAnalysis(const MCSubtargetInfo &sti, void BottleneckAnalysis::addRegisterDep(unsigned From, unsigned To, unsigned RegID, unsigned Cost) { bool IsLoopCarried = From >= To; - unsigned SourceSize = Source.size(); + unsigned SourceSize = getSource().size(); if (IsLoopCarried) { DG.addRegisterDep(From, To + SourceSize, RegID, Cost); DG.addRegisterDep(From + SourceSize, To + (SourceSize * 2), RegID, Cost); @@ -473,7 +466,7 @@ void BottleneckAnalysis::addRegisterDep(unsigned From, unsigned To, void BottleneckAnalysis::addMemoryDep(unsigned From, unsigned To, unsigned Cost) { bool IsLoopCarried = From >= To; - unsigned SourceSize = Source.size(); + unsigned SourceSize = getSource().size(); if (IsLoopCarried) { DG.addMemoryDep(From, To + SourceSize, Cost); DG.addMemoryDep(From + SourceSize, To + (SourceSize * 2), Cost); @@ -485,7 +478,7 @@ void BottleneckAnalysis::addMemoryDep(unsigned From, unsigned To, void BottleneckAnalysis::addResourceDep(unsigned From, unsigned To, uint64_t Mask, unsigned Cost) { bool IsLoopCarried = From >= To; - unsigned SourceSize = Source.size(); + unsigned SourceSize = getSource().size(); if (IsLoopCarried) { DG.addResourceDep(From, To + SourceSize, Mask, Cost); DG.addResourceDep(From + SourceSize, To + (SourceSize * 2), Mask, Cost); @@ -508,6 +501,7 @@ void BottleneckAnalysis::onEvent(const HWInstructionEvent &Event) { if (Event.Type != HWInstructionEvent::Issued) return; + ArrayRef Source = getSource(); const Instruction &IS = *Event.IR.getInstruction(); unsigned To = IID % Source.size(); @@ -617,7 +611,7 @@ void BottleneckAnalysis::printBottleneckHints(raw_ostream &OS) const { if (BPI.PressureIncreaseCycles) { ArrayRef Distribution = Tracker.getResourcePressureDistribution(); - const MCSchedModel &SM = STI.getSchedModel(); + const MCSchedModel &SM = getSubTargetInfo().getSchedModel(); for (unsigned I = 0, E = Distribution.size(); I < E; ++I) { unsigned ResourceCycles = Distribution[I]; if (ResourceCycles) { diff --git a/llvm/tools/llvm-mca/Views/BottleneckAnalysis.h b/llvm/tools/llvm-mca/Views/BottleneckAnalysis.h index 9e3bd5978f0931..a0aad9faff4098 100644 --- a/llvm/tools/llvm-mca/Views/BottleneckAnalysis.h +++ b/llvm/tools/llvm-mca/Views/BottleneckAnalysis.h @@ -87,6 +87,7 @@ #include "llvm/MC/MCSchedule.h" #include "llvm/MC/MCSubtargetInfo.h" #include "llvm/Support/raw_ostream.h" +#include "llvm/Support/FormattedStream.h" namespace llvm { namespace mca { @@ -282,13 +283,10 @@ class DependencyGraph { }; /// A view that collects and prints a few performance numbers. -class BottleneckAnalysis : public View { - const MCSubtargetInfo &STI; - MCInstPrinter &MCIP; +class BottleneckAnalysis : public InstructionView { PressureTracker Tracker; DependencyGraph DG; - ArrayRef Source; unsigned Iterations; unsigned TotalCycles; @@ -317,6 +315,9 @@ class BottleneckAnalysis : public View { void addMemoryDep(unsigned From, unsigned To, unsigned Cy); void addResourceDep(unsigned From, unsigned To, uint64_t Mask, unsigned Cy); + void printInstruction(formatted_raw_ostream &FOS, const MCInst &MCI, + bool UseDifferentColor = false) const; + // Prints a bottleneck message to OS. void printBottleneckHints(raw_ostream &OS) const; void printCriticalSequence(raw_ostream &OS) const; diff --git a/llvm/tools/llvm-mca/Views/InstructionInfoView.cpp b/llvm/tools/llvm-mca/Views/InstructionInfoView.cpp index 548f0b36d0557a..da53f16a991389 100644 --- a/llvm/tools/llvm-mca/Views/InstructionInfoView.cpp +++ b/llvm/tools/llvm-mca/Views/InstructionInfoView.cpp @@ -20,9 +20,8 @@ namespace mca { void InstructionInfoView::printView(raw_ostream &OS) const { std::string Buffer; raw_string_ostream TempStream(Buffer); - std::string Instruction; - raw_string_ostream InstrStream(Instruction); + ArrayRef Source = getSource(); if (!Source.size()) return; @@ -82,14 +81,7 @@ void InstructionInfoView::printView(raw_ostream &OS) const { } const MCInst &Inst = std::get<1>(I.value()); - MCIP.printInst(&Inst, 0, "", STI, InstrStream); - InstrStream.flush(); - - // Consume any tabs or spaces at the beginning of the string. - StringRef Str(Instruction); - Str = Str.ltrim(); - TempStream << Str << '\n'; - Instruction = ""; + TempStream << printInstructionString(Inst) << '\n'; } TempStream.flush(); @@ -98,8 +90,9 @@ void InstructionInfoView::printView(raw_ostream &OS) const { void InstructionInfoView::collectData( MutableArrayRef IIVD) const { + const llvm::MCSubtargetInfo &STI = getSubTargetInfo(); const MCSchedModel &SM = STI.getSchedModel(); - for (auto I : zip(Source, IIVD)) { + for (auto I : zip(getSource(), IIVD)) { const MCInst &Inst = std::get<0>(I); InstructionInfoViewData &IIVDEntry = std::get<1>(I); const MCInstrDesc &MCDesc = MCII.get(Inst.getOpcode()); diff --git a/llvm/tools/llvm-mca/Views/InstructionInfoView.h b/llvm/tools/llvm-mca/Views/InstructionInfoView.h index aca7e5894a8921..c2093b2d042910 100644 --- a/llvm/tools/llvm-mca/Views/InstructionInfoView.h +++ b/llvm/tools/llvm-mca/Views/InstructionInfoView.h @@ -50,13 +50,10 @@ namespace llvm { namespace mca { /// A view that prints out generic instruction information. -class InstructionInfoView : public View { - const llvm::MCSubtargetInfo &STI; +class InstructionInfoView : public InstructionView { const llvm::MCInstrInfo &MCII; CodeEmitter &CE; bool PrintEncodings; - llvm::ArrayRef Source; - llvm::MCInstPrinter &MCIP; struct InstructionInfoViewData { unsigned NumMicroOpcodes = 0; @@ -76,8 +73,8 @@ class InstructionInfoView : public View { const llvm::MCInstrInfo &II, CodeEmitter &C, bool ShouldPrintEncodings, llvm::ArrayRef S, llvm::MCInstPrinter &IP) - : STI(ST), MCII(II), CE(C), PrintEncodings(ShouldPrintEncodings), - Source(S), MCIP(IP) {} + : InstructionView(ST, IP, S), MCII(II), CE(C), + PrintEncodings(ShouldPrintEncodings) {} void printView(llvm::raw_ostream &OS) const override; }; diff --git a/llvm/tools/llvm-mca/Views/ResourcePressureView.cpp b/llvm/tools/llvm-mca/Views/ResourcePressureView.cpp index bdb9dc21247b08..a5a74210c67263 100644 --- a/llvm/tools/llvm-mca/Views/ResourcePressureView.cpp +++ b/llvm/tools/llvm-mca/Views/ResourcePressureView.cpp @@ -21,10 +21,10 @@ namespace mca { ResourcePressureView::ResourcePressureView(const llvm::MCSubtargetInfo &sti, MCInstPrinter &Printer, ArrayRef S) - : STI(sti), MCIP(Printer), Source(S), LastInstructionIdx(0) { + : InstructionView(sti, Printer, S), LastInstructionIdx(0) { // Populate the map of resource descriptors. unsigned R2VIndex = 0; - const MCSchedModel &SM = STI.getSchedModel(); + const MCSchedModel &SM = getSubTargetInfo().getSchedModel(); for (unsigned I = 0, E = SM.getNumProcResourceKinds(); I < E; ++I) { const MCProcResourceDesc &ProcResource = *SM.getProcResource(I); unsigned NumUnits = ProcResource.NumUnits; @@ -37,7 +37,7 @@ ResourcePressureView::ResourcePressureView(const llvm::MCSubtargetInfo &sti, } NumResourceUnits = R2VIndex; - ResourceUsage.resize(NumResourceUnits * (Source.size() + 1)); + ResourceUsage.resize(NumResourceUnits * (getSource().size() + 1)); std::fill(ResourceUsage.begin(), ResourceUsage.end(), 0.0); } @@ -52,6 +52,7 @@ void ResourcePressureView::onEvent(const HWInstructionEvent &Event) { return; const auto &IssueEvent = static_cast(Event); + ArrayRef Source = getSource(); const unsigned SourceIdx = Event.IR.getSourceIndex() % Source.size(); for (const std::pair &Use : IssueEvent.UsedResources) { @@ -105,7 +106,7 @@ void ResourcePressureView::printResourcePressurePerIter(raw_ostream &OS) const { formatted_raw_ostream FOS(TempStream); FOS << "\n\nResources:\n"; - const MCSchedModel &SM = STI.getSchedModel(); + const MCSchedModel &SM = getSubTargetInfo().getSchedModel(); for (unsigned I = 1, ResourceIndex = 0, E = SM.getNumProcResourceKinds(); I < E; ++I) { const MCProcResourceDesc &ProcResource = *SM.getProcResource(I); @@ -132,6 +133,7 @@ void ResourcePressureView::printResourcePressurePerIter(raw_ostream &OS) const { FOS << '\n'; FOS.flush(); + ArrayRef Source = getSource(); const unsigned Executions = LastInstructionIdx / Source.size() + 1; for (unsigned I = 0, E = NumResourceUnits; I < E; ++I) { double Usage = ResourceUsage[I + Source.size() * E]; @@ -148,13 +150,11 @@ void ResourcePressureView::printResourcePressurePerInst(raw_ostream &OS) const { formatted_raw_ostream FOS(TempStream); FOS << "\n\nResource pressure by instruction:\n"; - printColumnNames(FOS, STI.getSchedModel()); + printColumnNames(FOS, getSubTargetInfo().getSchedModel()); FOS << "Instructions:\n"; - std::string Instruction; - raw_string_ostream InstrStream(Instruction); - unsigned InstrIndex = 0; + ArrayRef Source = getSource(); const unsigned Executions = LastInstructionIdx / Source.size() + 1; for (const MCInst &MCI : Source) { unsigned BaseEltIdx = InstrIndex * NumResourceUnits; @@ -163,16 +163,7 @@ void ResourcePressureView::printResourcePressurePerInst(raw_ostream &OS) const { printResourcePressure(FOS, Usage / Executions, (J + 1) * 7); } - MCIP.printInst(&MCI, 0, "", STI, InstrStream); - InstrStream.flush(); - StringRef Str(Instruction); - - // Remove any tabs or spaces at the beginning of the instruction. - Str = Str.ltrim(); - - FOS << Str << '\n'; - Instruction = ""; - + FOS << printInstructionString(MCI) << '\n'; FOS.flush(); OS << Buffer; Buffer = ""; diff --git a/llvm/tools/llvm-mca/Views/ResourcePressureView.h b/llvm/tools/llvm-mca/Views/ResourcePressureView.h index 0fa0b9a36aa381..39914f9e2f27d7 100644 --- a/llvm/tools/llvm-mca/Views/ResourcePressureView.h +++ b/llvm/tools/llvm-mca/Views/ResourcePressureView.h @@ -69,10 +69,7 @@ namespace mca { /// This class collects resource pressure statistics and it is able to print /// out all the collected information as a table to an output stream. -class ResourcePressureView : public View { - const llvm::MCSubtargetInfo &STI; - llvm::MCInstPrinter &MCIP; - llvm::ArrayRef Source; +class ResourcePressureView : public InstructionView { unsigned LastInstructionIdx; // Map to quickly obtain the ResourceUsage column index from a processor diff --git a/llvm/tools/llvm-mca/Views/TimelineView.cpp b/llvm/tools/llvm-mca/Views/TimelineView.cpp index cf5b48e811b88d..f520c5c31d3f84 100644 --- a/llvm/tools/llvm-mca/Views/TimelineView.cpp +++ b/llvm/tools/llvm-mca/Views/TimelineView.cpp @@ -20,10 +20,10 @@ namespace mca { TimelineView::TimelineView(const MCSubtargetInfo &sti, MCInstPrinter &Printer, llvm::ArrayRef S, unsigned Iterations, unsigned Cycles) - : STI(sti), MCIP(Printer), Source(S), CurrentCycle(0), + : InstructionView(sti, Printer, S), CurrentCycle(0), MaxCycle(Cycles == 0 ? 80 : Cycles), LastCycle(0), WaitTime(S.size()), UsedBuffer(S.size()) { - unsigned NumInstructions = Source.size(); + unsigned NumInstructions = getSource().size(); assert(Iterations && "Invalid number of iterations specified!"); NumInstructions *= Iterations; Timeline.resize(NumInstructions); @@ -40,10 +40,10 @@ TimelineView::TimelineView(const MCSubtargetInfo &sti, MCInstPrinter &Printer, void TimelineView::onReservedBuffers(const InstRef &IR, ArrayRef Buffers) { - if (IR.getSourceIndex() >= Source.size()) + if (IR.getSourceIndex() >= getSource().size()) return; - const MCSchedModel &SM = STI.getSchedModel(); + const MCSchedModel &SM = getSubTargetInfo().getSchedModel(); std::pair BufferInfo = {0, -1}; for (const unsigned Buffer : Buffers) { const MCProcResourceDesc &MCDesc = *SM.getProcResource(Buffer); @@ -70,7 +70,7 @@ void TimelineView::onEvent(const HWInstructionEvent &Event) { // Update the WaitTime entry which corresponds to this Index. assert(TVEntry.CycleDispatched >= 0 && "Invalid TVEntry found!"); unsigned CycleDispatched = static_cast(TVEntry.CycleDispatched); - WaitTimeEntry &WTEntry = WaitTime[Index % Source.size()]; + WaitTimeEntry &WTEntry = WaitTime[Index % getSource().size()]; WTEntry.CyclesSpentInSchedulerQueue += TVEntry.CycleIssued - CycleDispatched; assert(CycleDispatched <= TVEntry.CycleReady && @@ -133,7 +133,7 @@ void TimelineView::printWaitTimeEntry(formatted_raw_ostream &OS, const WaitTimeEntry &Entry, unsigned SourceIndex, unsigned Executions) const { - bool PrintingTotals = SourceIndex == Source.size(); + bool PrintingTotals = SourceIndex == getSource().size(); unsigned CumulativeExecutions = PrintingTotals ? Timeline.size() : Executions; if (!PrintingTotals) @@ -164,7 +164,8 @@ void TimelineView::printWaitTimeEntry(formatted_raw_ostream &OS, OS.PadToColumn(27); if (!PrintingTotals) tryChangeColor(OS, Entry.CyclesSpentAfterWBAndBeforeRetire, - CumulativeExecutions, STI.getSchedModel().MicroOpBufferSize); + CumulativeExecutions, + getSubTargetInfo().getSchedModel().MicroOpBufferSize); OS << format("%.1f", floor((AverageTime3 * 10) + 0.5) / 10); if (OS.has_colors()) @@ -181,33 +182,19 @@ void TimelineView::printAverageWaitTimes(raw_ostream &OS) const { "[3]: Average time elapsed from WB until retire stage\n\n" " [0] [1] [2] [3]\n"; OS << Header; - - // Use a different string stream for printing instructions. - std::string Instruction; - raw_string_ostream InstrStream(Instruction); - formatted_raw_ostream FOS(OS); - unsigned Executions = Timeline.size() / Source.size(); + unsigned Executions = Timeline.size() / getSource().size(); unsigned IID = 0; - for (const MCInst &Inst : Source) { + for (const MCInst &Inst : getSource()) { printWaitTimeEntry(FOS, WaitTime[IID], IID, Executions); - // Append the instruction info at the end of the line. - MCIP.printInst(&Inst, 0, "", STI, InstrStream); - InstrStream.flush(); - - // Consume any tabs or spaces at the beginning of the string. - StringRef Str(Instruction); - Str = Str.ltrim(); - FOS << " " << Str << '\n'; + FOS << " " << printInstructionString(Inst) << '\n'; FOS.flush(); - Instruction = ""; - ++IID; } // If the timeline contains more than one instruction, // let's also print global averages. - if (Source.size() != 1) { + if (getSource().size() != 1) { WaitTimeEntry TotalWaitTime = std::accumulate( WaitTime.begin(), WaitTime.end(), WaitTimeEntry{0, 0, 0}, [](const WaitTimeEntry &A, const WaitTimeEntry &B) { @@ -220,7 +207,7 @@ void TimelineView::printAverageWaitTimes(raw_ostream &OS) const { printWaitTimeEntry(FOS, TotalWaitTime, IID, Executions); FOS << " " << "" << '\n'; - InstrStream.flush(); + FOS.flush(); } } @@ -292,11 +279,8 @@ void TimelineView::printTimeline(raw_ostream &OS) const { printTimelineHeader(FOS, LastCycle); FOS.flush(); - // Use a different string stream for the instruction. - std::string Instruction; - raw_string_ostream InstrStream(Instruction); - unsigned IID = 0; + ArrayRef Source = getSource(); const unsigned Iterations = Timeline.size() / Source.size(); for (unsigned Iteration = 0; Iteration < Iterations; ++Iteration) { for (const MCInst &Inst : Source) { @@ -306,16 +290,8 @@ void TimelineView::printTimeline(raw_ostream &OS) const { unsigned SourceIndex = IID % Source.size(); printTimelineViewEntry(FOS, Entry, Iteration, SourceIndex); - // Append the instruction info at the end of the line. - MCIP.printInst(&Inst, 0, "", STI, InstrStream); - InstrStream.flush(); - - // Consume any tabs or spaces at the beginning of the string. - StringRef Str(Instruction); - Str = Str.ltrim(); - FOS << " " << Str << '\n'; + FOS << " " << printInstructionString(Inst) << '\n'; FOS.flush(); - Instruction = ""; ++IID; } diff --git a/llvm/tools/llvm-mca/Views/TimelineView.h b/llvm/tools/llvm-mca/Views/TimelineView.h index 9bec3b87db45d3..528579edf7087d 100644 --- a/llvm/tools/llvm-mca/Views/TimelineView.h +++ b/llvm/tools/llvm-mca/Views/TimelineView.h @@ -118,11 +118,7 @@ namespace mca { /// a TimelineViewEntry object. TimelineViewEntry objects are then used /// to print the timeline information, as well as the "average wait times" /// for every instruction in the input assembly sequence. -class TimelineView : public View { - const llvm::MCSubtargetInfo &STI; - llvm::MCInstPrinter &MCIP; - llvm::ArrayRef Source; - +class TimelineView : public InstructionView { unsigned CurrentCycle; unsigned MaxCycle; unsigned LastCycle; diff --git a/llvm/tools/llvm-mca/Views/View.cpp b/llvm/tools/llvm-mca/Views/View.cpp index 8e5c34d2d5c247..4cef7456f36616 100644 --- a/llvm/tools/llvm-mca/Views/View.cpp +++ b/llvm/tools/llvm-mca/Views/View.cpp @@ -17,5 +17,13 @@ namespace llvm { namespace mca { void View::anchor() {} + +StringRef InstructionView::printInstructionString(const llvm::MCInst &MCI) const { + InstructionString = ""; + MCIP.printInst(&MCI, 0, "", STI, InstrStream); + InstrStream.flush(); + // Remove any tabs or spaces at the beginning of the instruction. + return StringRef(InstructionString).ltrim(); + } } // namespace mca } // namespace llvm diff --git a/llvm/tools/llvm-mca/Views/View.h b/llvm/tools/llvm-mca/Views/View.h index 3b52511b4d2922..1af6e5959f3125 100644 --- a/llvm/tools/llvm-mca/Views/View.h +++ b/llvm/tools/llvm-mca/Views/View.h @@ -15,6 +15,7 @@ #ifndef LLVM_TOOLS_LLVM_MCA_VIEW_H #define LLVM_TOOLS_LLVM_MCA_VIEW_H +#include "llvm/MC/MCInstPrinter.h" #include "llvm/MCA/HWEventListener.h" #include "llvm/Support/raw_ostream.h" @@ -27,6 +28,33 @@ class View : public HWEventListener { virtual ~View() = default; void anchor() override; }; + +// The base class for views that deal with individual machine instructions. +class InstructionView : public View { + const llvm::MCSubtargetInfo &STI; + llvm::MCInstPrinter &MCIP; + llvm::ArrayRef Source; + + mutable std::string InstructionString; + mutable raw_string_ostream InstrStream; + +protected: + InstructionView(const llvm::MCSubtargetInfo &STI, + llvm::MCInstPrinter &Printer, + llvm::ArrayRef S) + : STI(STI), MCIP(Printer), Source(S), InstrStream(InstructionString) {} + + virtual ~InstructionView() = default; + + // Return a reference to a string representing a given machine instruction. + // The result should be used or copied before the next call to + // printInstructionString() as it will overwrite the previous result. + StringRef printInstructionString(const llvm::MCInst &MCI) const; + + const llvm::MCSubtargetInfo &getSubTargetInfo() const { return STI; } + llvm::MCInstPrinter &getInstPrinter() const { return MCIP; } + llvm::ArrayRef getSource() const { return Source; } +}; } // namespace mca } // namespace llvm diff --git a/mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td b/mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td index a4a0db171e810a..d2bcd64529b5d0 100644 --- a/mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td +++ b/mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td @@ -984,6 +984,14 @@ def LLVM_MatrixTransposeOp // LLVM masked operations. // +/// Create a llvm.get.active.lane.mask to set a mask up to a given position. +def LLVM_GetActiveLaneMaskOp + : LLVM_OneResultIntrOp<"get.active.lane.mask", [0], [0], [NoSideEffect]>, + Arguments<(ins LLVM_Type:$base, LLVM_Type:$n)> { + let assemblyFormat = "$base `,` $n attr-dict `:` " + "type($base) `,` type($n) `to` type($res)"; +} + /// Create a call to Masked Load intrinsic. def LLVM_MaskedLoadOp : LLVM_OneResultOp<"intr.masked.load">, diff --git a/mlir/include/mlir/Dialect/OpenACC/OpenACC.h b/mlir/include/mlir/Dialect/OpenACC/OpenACC.h index 8d3798103b0ce6..399db74fae2ca8 100644 --- a/mlir/include/mlir/Dialect/OpenACC/OpenACC.h +++ b/mlir/include/mlir/Dialect/OpenACC/OpenACC.h @@ -35,7 +35,7 @@ namespace acc { /// /// Value can be combined bitwise to reflect the mapping applied to the /// construct. e.g. `acc.loop gang vector`, the `gang` and `vector` could be -/// combined and the final mapping value would be 5 (4 & 1). +/// combined and the final mapping value would be 5 (4 | 1). enum OpenACCExecMapping { NONE = 0, VECTOR = 1, WORKER = 2, GANG = 4, SEQ = 8 }; } // end namespace acc diff --git a/mlir/test/Conversion/StandardToLLVM/calling-convention.mlir b/mlir/test/Conversion/StandardToLLVM/calling-convention.mlir index 4cdf9d6bace87d..4c318c14a95e60 100644 --- a/mlir/test/Conversion/StandardToLLVM/calling-convention.mlir +++ b/mlir/test/Conversion/StandardToLLVM/calling-convention.mlir @@ -173,7 +173,7 @@ func @return_var_memref(%arg0: memref<4x3xf32>) -> memref<*xf32> { // CHECK: %[[RANK:.*]] = llvm.extractvalue %[[DESC_2]][0] : !llvm.struct<(i64, ptr)> // CHECK: %[[NEW_DESC_1:.*]] = llvm.insertvalue %[[RANK]], %[[NEW_DESC]][0] // CHECK: %[[NEW_DESC_2:.*]] = llvm.insertvalue %[[ALLOCATED]], %[[NEW_DESC_1]][1] - // CHECL: llvm.return %[[NEW_DESC_2]] + // CHECK: llvm.return %[[NEW_DESC_2]] return %0 : memref<*xf32> } diff --git a/mlir/test/Target/llvmir-intrinsics.mlir b/mlir/test/Target/llvmir-intrinsics.mlir index 6bf9b9768dd3ae..8a598e67d17b89 100644 --- a/mlir/test/Target/llvmir-intrinsics.mlir +++ b/mlir/test/Target/llvmir-intrinsics.mlir @@ -237,6 +237,13 @@ llvm.func @matrix_intrinsics(%A: !llvm.vec<64 x float>, %B: !llvm.vec<48 x float llvm.return } +// CHECK-LABEL: @get_active_lane_mask +llvm.func @get_active_lane_mask(%base: !llvm.i64, %n: !llvm.i64) -> (!llvm.vec<7 x i1>) { + // CHECK: call <7 x i1> @llvm.get.active.lane.mask.v7i1.i64(i64 %0, i64 %1) + %0 = llvm.intr.get.active.lane.mask %base, %n : !llvm.i64, !llvm.i64 to !llvm.vec<7 x i1> + llvm.return %0 : !llvm.vec<7 x i1> +} + // CHECK-LABEL: @masked_load_store_intrinsics llvm.func @masked_load_store_intrinsics(%A: !llvm.ptr>, %mask: !llvm.vec<7 x i1>) { // CHECK: call <7 x float> @llvm.masked.load.v7f32.p0v7f32(<7 x float>* %{{.*}}, i32 1, <7 x i1> %{{.*}}, <7 x float> undef) @@ -314,7 +321,12 @@ llvm.func @memcpy_test(%arg0: !llvm.i32, %arg1: !llvm.i1, %arg2: !llvm.ptr, // CHECK-DAG: declare <48 x float> @llvm.matrix.transpose.v48f32(<48 x float>, i32 immarg, i32 immarg) // CHECK-DAG: declare <48 x float> @llvm.matrix.column.major.load.v48f32(float* nocapture, i64, i1 immarg, i32 immarg, i32 immarg) // CHECK-DAG: declare void @llvm.matrix.column.major.store.v48f32(<48 x float>, float* nocapture writeonly, i64, i1 immarg, i32 immarg, i32 immarg) +// CHECK-DAG: declare <7 x i1> @llvm.get.active.lane.mask.v7i1.i64(i64, i64) // CHECK-DAG: declare <7 x float> @llvm.masked.load.v7f32.p0v7f32(<7 x float>*, i32 immarg, <7 x i1>, <7 x float>) // CHECK-DAG: declare void @llvm.masked.store.v7f32.p0v7f32(<7 x float>, <7 x float>*, i32 immarg, <7 x i1>) +// CHECK-DAG: declare <7 x float> @llvm.masked.gather.v7f32.v7p0f32(<7 x float*>, i32 immarg, <7 x i1>, <7 x float>) +// CHECK-DAG: declare void @llvm.masked.scatter.v7f32.v7p0f32(<7 x float>, <7 x float*>, i32 immarg, <7 x i1>) +// CHECK-DAG: declare <7 x float> @llvm.masked.expandload.v7f32(float*, <7 x i1>, <7 x float>) +// CHECK-DAG: declare void @llvm.masked.compressstore.v7f32(<7 x float>, float*, <7 x i1>) // CHECK-DAG: declare void @llvm.memcpy.p0i8.p0i8.i32(i8* noalias nocapture writeonly, i8* noalias nocapture readonly, i32, i1 immarg) // CHECK-DAG: declare void @llvm.memcpy.inline.p0i8.p0i8.i64(i8* noalias nocapture writeonly, i8* noalias nocapture readonly, i64 immarg, i1 immarg) diff --git a/mlir/test/mlir-tblgen/op-decl.td b/mlir/test/mlir-tblgen/op-decl.td index a30de0959d5790..0cb793b2cd2ac2 100644 --- a/mlir/test/mlir-tblgen/op-decl.td +++ b/mlir/test/mlir-tblgen/op-decl.td @@ -56,7 +56,7 @@ def NS_AOp : NS_Op<"a_op", [IsolatedFromAbove, IsolatedFromAbove]> { // CHECK: ::mlir::Value a(); // CHECK: ::mlir::ValueRange b(); // CHECK: ::mlir::IntegerAttr attr1(); -// CHECL: ::mlir::FloatAttr attr2(); +// CHECK: ::mlir::FloatAttr attr2(); // CHECK: private: // CHECK: ::mlir::ValueRange odsOperands; // CHECK: };