From ba4cc3b38087eb256ef7a93b14dc7d09be15c511 Mon Sep 17 00:00:00 2001 From: peter klausler Date: Tue, 25 Aug 2020 09:39:09 -0700 Subject: [PATCH 01/21] [flang] Don't completely left-justify fixed-form tokenization If the label field is empty, and macro replacement occurs, the rescanned text might be misclassified as a comment card if it happens to begin with a C or a D. Insert a leading space into these otherwise empty label fields. Fixes https://bugs.llvm.org/show_bug.cgi?id=47173 --- flang/lib/Parser/prescan.cpp | 15 +++++++++++---- flang/lib/Parser/prescan.h | 2 +- flang/test/Preprocessing/fixed-rescan.F | 7 +++++++ flang/test/Preprocessing/pp029.F | 2 +- 4 files changed, 20 insertions(+), 6 deletions(-) create mode 100644 flang/test/Preprocessing/fixed-rescan.F diff --git a/flang/lib/Parser/prescan.cpp b/flang/lib/Parser/prescan.cpp index 9e90f7f6228feb..7fdeaf00d185bd 100644 --- a/flang/lib/Parser/prescan.cpp +++ b/flang/lib/Parser/prescan.cpp @@ -245,8 +245,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 +257,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/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\ From 1b3de8812df8a009084c554f65e3dafb54732edf Mon Sep 17 00:00:00 2001 From: Matt Arsenault Date: Wed, 3 Jun 2020 17:53:15 -0400 Subject: [PATCH 02/21] AArch64: Fix hardcoded register in test --- llvm/test/CodeGen/AArch64/stack-guard-reassign.ll | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) 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] From 40cbb2484d72faefc9f6cd09548cd24d3fd9e9d5 Mon Sep 17 00:00:00 2001 From: Kazuaki Ishizaki Date: Wed, 26 Aug 2020 03:11:48 +0900 Subject: [PATCH 03/21] [mlir] NFC: fix typo in FileCheck prefix CHECL -> CHECK Reviewed By: ftynse Differential Revision: https://reviews.llvm.org/D86550 --- mlir/test/Conversion/StandardToLLVM/calling-convention.mlir | 2 +- mlir/test/mlir-tblgen/op-decl.td | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) 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/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: }; From ae90df8e5a68c7df4ea3e552dd7f87270fbcb0c7 Mon Sep 17 00:00:00 2001 From: Wei Wang Date: Tue, 25 Aug 2020 10:42:54 -0700 Subject: [PATCH 04/21] [FIX] Avoid creating BFI when emitting remarks for dead functions Dead function has its body stripped away, and can cause various analyses to panic. Also it does not make sense to apply analyses on such function. Reviewed By: xazax.hun, MaskRay, wenlei, hoy Differential Revision: https://reviews.llvm.org/D84715 --- llvm/lib/LTO/LTO.cpp | 2 +- llvm/test/LTO/Resolution/X86/dead-strip-fulllto.ll | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) 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/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 From 7de7fe5d0e3f7f4d28e1dde42df4a7defa564f11 Mon Sep 17 00:00:00 2001 From: Raphael Isemann Date: Tue, 25 Aug 2020 19:53:48 +0200 Subject: [PATCH 05/21] [lldb] Don't ask for QOS_CLASS_UNSPECIFIED queue in TestQueues TestQueues is curiously failing for me as my queue for QOS_CLASS_UNSPECIFIED is named "Utility" and not "User Initiated" or "Default". While debugging, this I noticed that this test isn't actually using this API right from what I understand. The API documentation for `dispatch_get_global_queue` specifies for the parameter: "You may specify the value QOS_CLASS_USER_INTERACTIVE, QOS_CLASS_USER_INITIATED, QOS_CLASS_UTILITY, or QOS_CLASS_BACKGROUND." QOS_CLASS_UNSPECIFIED isn't listed as one of the supported values. swift-corelibs-libdispatch even checks for this value and returns a DISPATCH_BAD_INPUT. The libdispatch shipped on macOS seems to also check for QOS_CLASS_UNSPECIFIED and seems to instead cause a "client crash", but somehow this doesn't trigger in this test and instead we just get whatever queue This patch just removes that part of the test as it appears the code is just incorrect. Reviewed By: jasonmolenda Differential Revision: https://reviews.llvm.org/D86211 --- lldb/test/API/macosx/queues/TestQueues.py | 16 ---------------- lldb/test/API/macosx/queues/main.c | 8 +------- 2 files changed, 1 insertion(+), 23 deletions(-) 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 (); From ef76686916d40f20c782ed3967130bd2e0105b31 Mon Sep 17 00:00:00 2001 From: Raphael Isemann Date: Tue, 25 Aug 2020 20:25:15 +0200 Subject: [PATCH 06/21] [lldb] Initialize reproducers in LocateSymbolFileTest Since a842950b62b6d029a392c3c312c6495d6368c2a4 this test started using the reproducer subsystem but we never initialized it in the test. The Subsystem takes an argument, so we can't use the usual SubsystemRAII at the moment to do this for us. This just adds the initialize/terminate calls to get the test passing again. --- lldb/unittests/Symbol/LocateSymbolFileTest.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) 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(); } From 13cee14bb1612ccdc3a7e7e770373fa2d2a94254 Mon Sep 17 00:00:00 2001 From: peter klausler Date: Tue, 25 Aug 2020 09:39:52 -0700 Subject: [PATCH 07/21] [flang] Parse global compiler directives Accept and represent "global" compiler directives that appear before and between program units in a source file. Differential Revision: https://reviews.llvm.org/D86555 --- flang/include/flang/Parser/parse-tree.h | 3 +- flang/lib/Parser/program-parsers.cpp | 36 +++++++++++++---------- flang/lib/Semantics/program-tree.cpp | 4 +++ flang/lib/Semantics/program-tree.h | 1 + flang/lib/Semantics/resolve-names.cpp | 5 ++++ flang/test/Parser/compiler-directives.f90 | 1 + 6 files changed, 33 insertions(+), 17 deletions(-) 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/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/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 From a0a1a4e5c83db53c806c56011a8741b31ab598a4 Mon Sep 17 00:00:00 2001 From: peter klausler Date: Tue, 25 Aug 2020 09:38:41 -0700 Subject: [PATCH 08/21] [flang] Improve error handling for bad characters in source When an illegal character appears in Fortran source (after preprocessing), catch and report it in the prescanning phase rather than leaving it for the parser to cope with. Differential Revision: https://reviews.llvm.org/D86553 --- flang/include/flang/Parser/characters.h | 27 +++++++++++++++++++++++++ flang/lib/Parser/prescan.cpp | 14 +++++++++---- flang/lib/Parser/token-sequence.cpp | 22 ++++++++++++++++++++ flang/lib/Parser/token-sequence.h | 3 +++ flang/test/Preprocessing/pp130.F90 | 4 ++-- 5 files changed, 64 insertions(+), 6 deletions(-) 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/lib/Parser/prescan.cpp b/flang/lib/Parser/prescan.cpp index 7fdeaf00d185bd..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; 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/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 & From 594107d488646c0d3d874b4624b0ee4f5fee5656 Mon Sep 17 00:00:00 2001 From: Lang Hames Date: Sun, 23 Aug 2020 20:34:57 -0700 Subject: [PATCH 09/21] [ORC] Fix an endif comment. --- llvm/include/llvm/ExecutionEngine/Orc/TPCIndirectionUtils.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 From f436bef5070fb453b5d394b5b3fd6f11a5920c01 Mon Sep 17 00:00:00 2001 From: Lang Hames Date: Tue, 25 Aug 2020 11:50:32 -0700 Subject: [PATCH 10/21] [examples] Fix dependencies for OrcV2Examples/LLJITWithThinLTOSummaries. --- .../OrcV2Examples/LLJITWithThinLTOSummaries/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 From df5576a852088337e03970641db291c6799afe97 Mon Sep 17 00:00:00 2001 From: Arthur Eubanks Date: Tue, 25 Aug 2020 10:59:12 -0700 Subject: [PATCH 11/21] [test] Add -inject-tli-mapping to -loop-vectorize -vector-library tests The legacy LoopVectorize has a dependency on InjectTLIMappingsLegacy. That cannot be expressed in the new PM since they are both normal passes. Explicitly add -inject-tli-mappings as a pass. Follow-up to https://reviews.llvm.org/D86492. Reviewed By: spatel Differential Revision: https://reviews.llvm.org/D86561 --- llvm/test/Transforms/LoopVectorize/PowerPC/massv-altivec.ll | 2 +- llvm/test/Transforms/LoopVectorize/PowerPC/massv-calls.ll | 2 +- llvm/test/Transforms/LoopVectorize/PowerPC/massv-nobuiltin.ll | 2 +- llvm/test/Transforms/LoopVectorize/PowerPC/massv-unsupported.ll | 2 +- .../test/Transforms/LoopVectorize/PowerPC/widened-massv-call.ll | 2 +- llvm/test/Transforms/LoopVectorize/X86/svml-calls-finite.ll | 2 +- llvm/test/Transforms/LoopVectorize/X86/svml-calls.ll | 2 +- llvm/test/Transforms/LoopVectorize/X86/veclib-calls.ll | 2 +- 8 files changed, 8 insertions(+), 8 deletions(-) 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" From 01eb1233db54454b146cb1e70d6f810ffbc354e5 Mon Sep 17 00:00:00 2001 From: Craig Topper Date: Tue, 25 Aug 2020 11:56:43 -0700 Subject: [PATCH 12/21] [X86] Mention -march=sapphirerapids in the release notes. This was just added in e02d081f2b60b61eb60ef6a49b1a9f907e432d4c. --- clang/docs/ReleaseNotes.rst | 2 ++ llvm/docs/ReleaseNotes.rst | 1 + 2 files changed, 3 insertions(+) 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/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 ----------------------------- From e713b0ecbc893b70e2ff28b016b64f420137824b Mon Sep 17 00:00:00 2001 From: Kuba Mracek Date: Tue, 25 Aug 2020 11:59:05 -0700 Subject: [PATCH 13/21] [tsan] On arm64e, strip out ptrauth bits from incoming PCs Differential Revision: https://reviews.llvm.org/D86378 --- .../lib/sanitizer_common/sanitizer_ptrauth.h | 2 ++ compiler-rt/lib/tsan/rtl/tsan_external.cpp | 11 +++++---- compiler-rt/lib/tsan/rtl/tsan_interface.cpp | 9 ++++---- compiler-rt/lib/tsan/rtl/tsan_interface_inl.h | 23 ++++++++++--------- 4 files changed, 25 insertions(+), 20 deletions(-) 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); } From bce7a7edf32d5094b37d65c9198b048c86645f99 Mon Sep 17 00:00:00 2001 From: peter klausler Date: Tue, 25 Aug 2020 10:34:33 -0700 Subject: [PATCH 14/21] [flang] Check that various variables referenced in I/O statements may be defined A number of I/O syntax rules involve variables that will be written to, and must therefore be definable. This includes internal file variables, IOSTAT= and IOMSG= specifiers, most INQUIRE statement specifiers, a few other specifiers, and input variables. This patch checks for these violations, and implements several additional I/O TODO constraint checks. Differential Revision: https://reviews.llvm.org/D86557 --- flang/lib/Semantics/check-io.cpp | 95 ++++++++++++++++++--------- flang/lib/Semantics/check-io.h | 7 +- flang/test/Semantics/deallocate05.f90 | 3 + flang/test/Semantics/io01.f90 | 4 ++ flang/test/Semantics/io02.f90 | 4 ++ flang/test/Semantics/io03.f90 | 49 +++++++++++++- flang/test/Semantics/io04.f90 | 12 ++++ flang/test/Semantics/io05.f90 | 6 ++ flang/test/Semantics/io06.f90 | 4 ++ 9 files changed, 150 insertions(+), 34 deletions(-) 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/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) From 4d69bcb12fa7c89ff68c2ede5869faeb2d294ad8 Mon Sep 17 00:00:00 2001 From: clementval Date: Tue, 25 Aug 2020 15:11:05 -0400 Subject: [PATCH 15/21] [mlir][openacc][NFC] Fix comment about OpenACCExecMapping --- mlir/include/mlir/Dialect/OpenACC/OpenACC.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 From e02920fe55761aaa06b33caec381bc2a1c36ad1c Mon Sep 17 00:00:00 2001 From: Wolfgang Pieb Date: Fri, 21 Aug 2020 15:52:02 -0700 Subject: [PATCH 16/21] [llvm-mca][NFC] Refactor handling of views that examine individual instructions, including printing them. Reviewers: andreadb, lebedev.ri Differential Review: https://reviews.llvm.org/D86390 Introduces a new base class "InstructionView" that such views derive from. Other views still use the "View" base class. --- .../llvm-mca/Views/BottleneckAnalysis.cpp | 46 +++++++--------- .../tools/llvm-mca/Views/BottleneckAnalysis.h | 9 ++-- .../llvm-mca/Views/InstructionInfoView.cpp | 15 ++---- .../llvm-mca/Views/InstructionInfoView.h | 9 ++-- .../llvm-mca/Views/ResourcePressureView.cpp | 27 ++++------ .../llvm-mca/Views/ResourcePressureView.h | 5 +- llvm/tools/llvm-mca/Views/TimelineView.cpp | 54 ++++++------------- llvm/tools/llvm-mca/Views/TimelineView.h | 6 +-- llvm/tools/llvm-mca/Views/View.cpp | 8 +++ llvm/tools/llvm-mca/Views/View.h | 28 ++++++++++ 10 files changed, 94 insertions(+), 113 deletions(-) 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 From 84fdc33f4785115cd517c789a99fdebb6a74ac3e Mon Sep 17 00:00:00 2001 From: aartbik Date: Tue, 25 Aug 2020 11:55:45 -0700 Subject: [PATCH 17/21] [mlir] [LLVMIR] Add get active lane mask intrinsic Provides fast, generic way of setting a mask up to a certain point. Potential use cases that may benefit are create_mask and transfer_read/write operations in the vector dialect. Reviewed By: bkramer Differential Revision: https://reviews.llvm.org/D86501 --- mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td | 8 ++++++++ mlir/test/Target/llvmir-intrinsics.mlir | 12 ++++++++++++ 2 files changed, 20 insertions(+) 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/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) From 3a54b6a4b71c21cf3bab4f132cbc2904fb9d997e Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Thu, 25 Jun 2020 21:35:41 +0200 Subject: [PATCH 18/21] [MemDep] Use BatchAA when computing pointer dependencies We're not changing IR while running a single MemDep query, so it's safe to cache alias analysis results using BatchAA. This adds BatchAA usage to getSimplePointerDependencyFrom(), which is non-intrusive -- covering larger parts (like a whole processNonLocalLoad query) is also possible, but requires threading BatchAA through a bunch of APIs. For the ThinLTO configuration, this is a 1% geomean improvement on CTMark. Differential Revision: https://reviews.llvm.org/D85583 --- llvm/lib/Analysis/MemoryDependenceAnalysis.cpp | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) 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: From b1009ee84fc0242bcebd07889306bf39d9b7170f Mon Sep 17 00:00:00 2001 From: Amy Huang Date: Tue, 25 Aug 2020 10:36:03 -0700 Subject: [PATCH 19/21] Reland "[DebugInfo] Move constructor homing case in shouldOmitDefinition." For some reason the ctor homing case was before the template specialization case, and could have returned false too early. I moved the code out into a separate function to avoid this. This reverts commit 05777ab941063192b9ccb1775358a83a2700ccc1. --- clang/lib/CodeGen/CGDebugInfo.cpp | 42 +++++++++++-------- ...-info-template-explicit-specialization.cpp | 3 ++ 2 files changed, 28 insertions(+), 17 deletions(-) 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 From 8e51bb249bc2a71ecd13092bc0e1e246995feba6 Mon Sep 17 00:00:00 2001 From: Juneyoung Lee Date: Tue, 25 Aug 2020 02:34:17 +0900 Subject: [PATCH 20/21] [ValueTracking] Add a noundef test for D86477; NFC --- .../Transforms/InstSimplify/freeze-noundef.ll | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/llvm/test/Transforms/InstSimplify/freeze-noundef.ll b/llvm/test/Transforms/InstSimplify/freeze-noundef.ll index f2e897b507e3d2..a3cf11ef31c4a8 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,18 @@ 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: [[F:%.*]] = freeze i1 [[Y]] +; CHECK-NEXT: ret i1 [[F]] +; + %y = add nsw i1 %x, 1 + call void @use_i1(i1 %y) + %f = freeze i1 %y + ret i1 %f +} From f753f5b05033bf1d8b89b19b753b78c89de41ae3 Mon Sep 17 00:00:00 2001 From: Juneyoung Lee Date: Mon, 24 Aug 2020 02:37:10 +0900 Subject: [PATCH 21/21] [ValueTracking] Let getGuaranteedNonPoisonOp find multiple non-poison operands This patch helps getGuaranteedNonPoisonOp find multiple non-poison operands. Instead of special-casing llvm.assume, I think it is also a viable option to add noundef to Intrinsics.td. If it makes sense, I'll make a patch for that. Reviewed By: jdoerfert Differential Revision: https://reviews.llvm.org/D86477 --- llvm/include/llvm/Analysis/ValueTracking.h | 8 ++-- llvm/lib/Analysis/ValueTracking.cpp | 47 ++++++++++++++----- .../Instrumentation/PoisonChecking.cpp | 6 ++- .../Transforms/InstSimplify/freeze-noundef.ll | 3 +- 4 files changed, 44 insertions(+), 20 deletions(-) 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/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/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/Transforms/InstSimplify/freeze-noundef.ll b/llvm/test/Transforms/InstSimplify/freeze-noundef.ll index a3cf11ef31c4a8..6a52bd5b257f0e 100644 --- a/llvm/test/Transforms/InstSimplify/freeze-noundef.ll +++ b/llvm/test/Transforms/InstSimplify/freeze-noundef.ll @@ -97,8 +97,7 @@ 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: [[F:%.*]] = freeze i1 [[Y]] -; CHECK-NEXT: ret i1 [[F]] +; CHECK-NEXT: ret i1 [[Y]] ; %y = add nsw i1 %x, 1 call void @use_i1(i1 %y)