diff --git a/clang-tools-extra/clang-tidy/utils/IncludeInserter.h b/clang-tools-extra/clang-tidy/utils/IncludeInserter.h index 95236c732f1309..74903b2d166db1 100644 --- a/clang-tools-extra/clang-tidy/utils/IncludeInserter.h +++ b/clang-tools-extra/clang-tidy/utils/IncludeInserter.h @@ -34,7 +34,7 @@ namespace utils { /// public: /// void registerPPCallbacks(const SourceManager &SM, Preprocessor *PP, /// Preprocessor *ModuleExpanderPP) override { -/// Inserter.registerPreprocessor(); +/// Inserter.registerPreprocessor(PP); /// } /// /// void registerMatchers(ast_matchers::MatchFinder* Finder) override { ... } @@ -42,8 +42,7 @@ namespace utils { /// void check( /// const ast_matchers::MatchFinder::MatchResult& Result) override { /// ... -/// Inserter.createMainFileIncludeInsertion("path/to/Header.h", -/// /*IsAngled=*/false); +/// Inserter.createMainFileIncludeInsertion("path/to/Header.h"); /// ... /// } /// diff --git a/clang-tools-extra/clangd/ClangdLSPServer.cpp b/clang-tools-extra/clangd/ClangdLSPServer.cpp index 9ed635c88e7195..d46147ac89cd8a 100644 --- a/clang-tools-extra/clangd/ClangdLSPServer.cpp +++ b/clang-tools-extra/clangd/ClangdLSPServer.cpp @@ -7,6 +7,7 @@ //===----------------------------------------------------------------------===// #include "ClangdLSPServer.h" +#include "ClangdServer.h" #include "CodeComplete.h" #include "Diagnostics.h" #include "DraftStore.h" @@ -18,6 +19,7 @@ #include "URI.h" #include "refactor/Tweak.h" #include "support/Context.h" +#include "support/MemoryTree.h" #include "support/Trace.h" #include "clang/Basic/Version.h" #include "clang/Tooling/Core/Replacement.h" @@ -26,6 +28,7 @@ #include "llvm/ADT/ScopeExit.h" #include "llvm/ADT/StringRef.h" #include "llvm/ADT/iterator_range.h" +#include "llvm/Support/Allocator.h" #include "llvm/Support/Errc.h" #include "llvm/Support/Error.h" #include "llvm/Support/FormatVariadic.h" @@ -33,6 +36,7 @@ #include "llvm/Support/Path.h" #include "llvm/Support/SHA1.h" #include "llvm/Support/ScopedPrinter.h" +#include #include #include #include @@ -144,7 +148,6 @@ llvm::Error validateEdits(const DraftStore &DraftMgr, const FileEdits &FE) { return error("Files must be saved first: {0} (and {1} others)", LastInvalidFile, InvalidFileCount - 1); } - } // namespace // MessageHandler dispatches incoming LSP messages. @@ -163,14 +166,16 @@ class ClangdLSPServer::MessageHandler : public Transport::MessageHandler { log("<-- {0}", Method); if (Method == "exit") return false; - if (!Server.Server) + if (!Server.Server) { elog("Notification {0} before initialization", Method); - else if (Method == "$/cancelRequest") + } else if (Method == "$/cancelRequest") { onCancel(std::move(Params)); - else if (auto Handler = Notifications.lookup(Method)) + } else if (auto Handler = Notifications.lookup(Method)) { Handler(std::move(Params)); - else + Server.maybeExportMemoryProfile(); + } else { log("unhandled notification {0}", Method); + } return true; } @@ -1049,22 +1054,21 @@ void ClangdLSPServer::onCompletion(const CompletionParams &Params, vlog("ignored auto-triggered completion, preceding char did not match"); return Reply(CompletionList()); } - Server->codeComplete(Params.textDocument.uri.file(), Params.position, - Opts.CodeComplete, - [Reply = std::move(Reply), - this](llvm::Expected List) mutable { - if (!List) - return Reply(List.takeError()); - CompletionList LSPList; - LSPList.isIncomplete = List->HasMore; - for (const auto &R : List->Completions) { - CompletionItem C = R.render(Opts.CodeComplete); - C.kind = adjustKindToCapability( - C.kind, SupportedCompletionItemKinds); - LSPList.items.push_back(std::move(C)); - } - return Reply(std::move(LSPList)); - }); + Server->codeComplete( + Params.textDocument.uri.file(), Params.position, Opts.CodeComplete, + [Reply = std::move(Reply), + this](llvm::Expected List) mutable { + if (!List) + return Reply(List.takeError()); + CompletionList LSPList; + LSPList.isIncomplete = List->HasMore; + for (const auto &R : List->Completions) { + CompletionItem C = R.render(Opts.CodeComplete); + C.kind = adjustKindToCapability(C.kind, SupportedCompletionItemKinds); + LSPList.items.push_back(std::move(C)); + } + return Reply(std::move(LSPList)); + }); } void ClangdLSPServer::onSignatureHelp(const TextDocumentPositionParams &Params, @@ -1235,6 +1239,25 @@ void ClangdLSPServer::publishDiagnostics( notify("textDocument/publishDiagnostics", Params); } +void ClangdLSPServer::maybeExportMemoryProfile() { + if (!trace::enabled()) + return; + // Profiling might be expensive, so we throttle it to happen once every 5 + // minutes. + static constexpr auto ProfileInterval = std::chrono::minutes(5); + auto Now = std::chrono::steady_clock::now(); + if (Now < NextProfileTime) + return; + + static constexpr trace::Metric MemoryUsage( + "memory_usage", trace::Metric::Value, "component_name"); + trace::Span Tracer("ProfileBrief"); + MemoryTree MT; + profile(MT); + record(MT, "clangd_lsp_server", MemoryUsage); + NextProfileTime = Now + ProfileInterval; +} + // FIXME: This function needs to be properly tested. void ClangdLSPServer::onChangeConfiguration( const DidChangeConfigurationParams &Params) { @@ -1405,6 +1428,9 @@ ClangdLSPServer::ClangdLSPServer(class Transport &Transp, if (Opts.FoldingRanges) MsgHandler->bind("textDocument/foldingRange", &ClangdLSPServer::onFoldingRange); // clang-format on + + // Delay first profile until we've finished warming up. + NextProfileTime = std::chrono::steady_clock::now() + std::chrono::minutes(1); } ClangdLSPServer::~ClangdLSPServer() { @@ -1425,6 +1451,11 @@ bool ClangdLSPServer::run() { return CleanExit && ShutdownRequestReceived; } +void ClangdLSPServer::profile(MemoryTree &MT) const { + if (Server) + Server->profile(MT.child("clangd_server")); +} + std::vector ClangdLSPServer::getFixes(llvm::StringRef File, const clangd::Diagnostic &D) { std::lock_guard Lock(FixItsMutex); diff --git a/clang-tools-extra/clangd/ClangdLSPServer.h b/clang-tools-extra/clangd/ClangdLSPServer.h index a853a408715620..7054c48652c568 100644 --- a/clang-tools-extra/clangd/ClangdLSPServer.h +++ b/clang-tools-extra/clangd/ClangdLSPServer.h @@ -17,11 +17,13 @@ #include "Protocol.h" #include "Transport.h" #include "support/Context.h" +#include "support/MemoryTree.h" #include "support/Path.h" #include "clang/Tooling/Core/Replacement.h" #include "llvm/ADT/Optional.h" #include "llvm/ADT/StringSet.h" #include "llvm/Support/JSON.h" +#include #include namespace clang { @@ -67,6 +69,9 @@ class ClangdLSPServer : private ClangdServer::Callbacks { /// \return Whether we shut down cleanly with a 'shutdown' -> 'exit' sequence. bool run(); + /// Profiles resource-usage. + void profile(MemoryTree &MT) const; + private: // Implement ClangdServer::Callbacks. void onDiagnosticsReady(PathRef File, llvm::StringRef Version, @@ -160,6 +165,14 @@ class ClangdLSPServer : private ClangdServer::Callbacks { /// Sends a "publishDiagnostics" notification to the LSP client. void publishDiagnostics(const PublishDiagnosticsParams &); + /// Runs profiling and exports memory usage metrics if tracing is enabled and + /// profiling hasn't happened recently. + void maybeExportMemoryProfile(); + + /// Timepoint until which profiling is off. It is used to throttle profiling + /// requests. + std::chrono::steady_clock::time_point NextProfileTime; + /// Since initialization of CDBs and ClangdServer is done lazily, the /// following context captures the one used while creating ClangdLSPServer and /// passes it to above mentioned object instances to make sure they share the diff --git a/clang-tools-extra/clangd/ClangdServer.cpp b/clang-tools-extra/clangd/ClangdServer.cpp index 93e3b10b50d55e..82dd7436b6f42f 100644 --- a/clang-tools-extra/clangd/ClangdServer.cpp +++ b/clang-tools-extra/clangd/ClangdServer.cpp @@ -28,6 +28,7 @@ #include "refactor/Tweak.h" #include "support/Logger.h" #include "support/Markup.h" +#include "support/MemoryTree.h" #include "support/ThreadsafeFS.h" #include "support/Trace.h" #include "clang/Format/Format.h" @@ -826,5 +827,12 @@ ClangdServer::blockUntilIdleForTest(llvm::Optional TimeoutSeconds) { BackgroundIdx->blockUntilIdleForTest(TimeoutSeconds)); } +void ClangdServer::profile(MemoryTree &MT) const { + if (DynamicIdx) + DynamicIdx->profile(MT.child("dynamic_index")); + if (BackgroundIdx) + BackgroundIdx->profile(MT.child("background_index")); + WorkScheduler.profile(MT.child("tuscheduler")); +} } // namespace clangd } // namespace clang diff --git a/clang-tools-extra/clangd/ClangdServer.h b/clang-tools-extra/clangd/ClangdServer.h index efba7ace648964..555f387ff3297e 100644 --- a/clang-tools-extra/clangd/ClangdServer.h +++ b/clang-tools-extra/clangd/ClangdServer.h @@ -25,6 +25,7 @@ #include "refactor/Tweak.h" #include "support/Cancellation.h" #include "support/Function.h" +#include "support/MemoryTree.h" #include "support/ThreadsafeFS.h" #include "clang/Tooling/CompilationDatabase.h" #include "clang/Tooling/Core/Replacement.h" @@ -337,6 +338,9 @@ class ClangdServer { LLVM_NODISCARD bool blockUntilIdleForTest(llvm::Optional TimeoutSeconds = 10); + /// Builds a nested representation of memory used by components. + void profile(MemoryTree &MT) const; + private: void formatCode(PathRef File, llvm::StringRef Code, ArrayRef Ranges, diff --git a/clang-tools-extra/clangd/Protocol.cpp b/clang-tools-extra/clangd/Protocol.cpp index f8aca226a599e4..f41cea28fea35b 100644 --- a/clang-tools-extra/clangd/Protocol.cpp +++ b/clang-tools-extra/clangd/Protocol.cpp @@ -484,12 +484,10 @@ bool fromJSON(const llvm::json::Value &Params, DidSaveTextDocumentParams &R, bool fromJSON(const llvm::json::Value &Params, DidChangeTextDocumentParams &R, llvm::json::Path P) { llvm::json::ObjectMapper O(Params, P); - if (!O) - return false; - O.map("forceRebuild", R.forceRebuild); // Optional clangd extension. - return O.map("textDocument", R.textDocument) && + return O && O.map("textDocument", R.textDocument) && O.map("contentChanges", R.contentChanges) && - O.map("wantDiagnostics", R.wantDiagnostics); + O.map("wantDiagnostics", R.wantDiagnostics) && + O.mapOptional("forceRebuild", R.forceRebuild); } bool fromJSON(const llvm::json::Value &E, FileChangeType &Out, @@ -578,12 +576,10 @@ llvm::json::Value toJSON(const Diagnostic &D) { bool fromJSON(const llvm::json::Value &Params, Diagnostic &R, llvm::json::Path P) { llvm::json::ObjectMapper O(Params, P); - if (!O || !O.map("range", R.range) || !O.map("message", R.message)) - return false; - O.map("severity", R.severity); - O.map("category", R.category); - O.map("code", R.code); - O.map("source", R.source); + return O && O.map("range", R.range) && O.map("message", R.message) && + O.mapOptional("severity", R.severity) && + O.mapOptional("category", R.category) && + O.mapOptional("code", R.code) && O.mapOptional("source", R.source); return true; } @@ -800,10 +796,8 @@ llvm::json::Value toJSON(const ApplyWorkspaceEditParams &Params) { bool fromJSON(const llvm::json::Value &Response, ApplyWorkspaceEditResponse &R, llvm::json::Path P) { llvm::json::ObjectMapper O(Response, P); - if (!O || !O.map("applied", R.applied)) - return false; - O.map("failureReason", R.failureReason); - return true; + return O && O.map("applied", R.applied) && + O.map("failureReason", R.failureReason); } bool fromJSON(const llvm::json::Value &Params, TextDocumentPositionParams &R, @@ -816,16 +810,11 @@ bool fromJSON(const llvm::json::Value &Params, TextDocumentPositionParams &R, bool fromJSON(const llvm::json::Value &Params, CompletionContext &R, llvm::json::Path P) { llvm::json::ObjectMapper O(Params, P); - if (!O) - return false; - int TriggerKind; - if (!O.map("triggerKind", TriggerKind)) + if (!O || !O.map("triggerKind", TriggerKind) || + !O.mapOptional("triggerCharacter", R.triggerCharacter)) return false; R.triggerKind = static_cast(TriggerKind); - - if (auto *TC = Params.getAsObject()->get("triggerCharacter")) - return fromJSON(*TC, R.triggerCharacter, P.field("triggerCharacter")); return true; } @@ -1126,8 +1115,8 @@ bool fromJSON(const llvm::json::Value &Params, ConfigurationSettings &S, llvm::json::ObjectMapper O(Params, P); if (!O) return true; // 'any' type in LSP. - O.map("compilationDatabaseChanges", S.compilationDatabaseChanges); - return true; + return O.mapOptional("compilationDatabaseChanges", + S.compilationDatabaseChanges); } bool fromJSON(const llvm::json::Value &Params, InitializationOptions &Opts, @@ -1136,11 +1125,10 @@ bool fromJSON(const llvm::json::Value &Params, InitializationOptions &Opts, if (!O) return true; // 'any' type in LSP. - fromJSON(Params, Opts.ConfigSettings, P); - O.map("compilationDatabasePath", Opts.compilationDatabasePath); - O.map("fallbackFlags", Opts.fallbackFlags); - O.map("clangdFileStatus", Opts.FileStatus); - return true; + return fromJSON(Params, Opts.ConfigSettings, P) && + O.map("compilationDatabasePath", Opts.compilationDatabasePath) && + O.mapOptional("fallbackFlags", Opts.fallbackFlags) && + O.mapOptional("clangdFileStatus", Opts.FileStatus); } bool fromJSON(const llvm::json::Value &E, TypeHierarchyDirection &Out, @@ -1193,20 +1181,13 @@ bool fromJSON(const llvm::json::Value &Params, TypeHierarchyItem &I, llvm::json::ObjectMapper O(Params, P); // Required fields. - if (!(O && O.map("name", I.name) && O.map("kind", I.kind) && - O.map("uri", I.uri) && O.map("range", I.range) && - O.map("selectionRange", I.selectionRange))) { - return false; - } - - // Optional fields. - O.map("detail", I.detail); - O.map("deprecated", I.deprecated); - O.map("parents", I.parents); - O.map("children", I.children); - O.map("data", I.data); - - return true; + return O && O.map("name", I.name) && O.map("kind", I.kind) && + O.map("uri", I.uri) && O.map("range", I.range) && + O.map("selectionRange", I.selectionRange) && + O.mapOptional("detail", I.detail) && + O.mapOptional("deprecated", I.deprecated) && + O.mapOptional("parents", I.parents) && + O.mapOptional("children", I.children) && O.mapOptional("data", I.data); } bool fromJSON(const llvm::json::Value &Params, diff --git a/clang-tools-extra/clangd/TUScheduler.cpp b/clang-tools-extra/clangd/TUScheduler.cpp index c408c8c0731de5..baf3f910b45ee8 100644 --- a/clang-tools-extra/clangd/TUScheduler.cpp +++ b/clang-tools-extra/clangd/TUScheduler.cpp @@ -56,6 +56,7 @@ #include "support/Cancellation.h" #include "support/Context.h" #include "support/Logger.h" +#include "support/MemoryTree.h" #include "support/Path.h" #include "support/Threading.h" #include "support/Trace.h" @@ -932,9 +933,9 @@ TUScheduler::FileStats ASTWorker::stats() const { // Note that we don't report the size of ASTs currently used for processing // the in-flight requests. We used this information for debugging purposes // only, so this should be fine. - Result.UsedBytes = IdleASTs.getUsedBytes(this); + Result.UsedBytesAST = IdleASTs.getUsedBytes(this); if (auto Preamble = getPossiblyStalePreamble()) - Result.UsedBytes += Preamble->Preamble.getSize(); + Result.UsedBytesPreamble = Preamble->Preamble.getSize(); return Result; } @@ -1429,5 +1430,14 @@ DebouncePolicy DebouncePolicy::fixed(clock::duration T) { return P; } +void TUScheduler::profile(MemoryTree &MT) const { + for (const auto &Elem : fileStats()) { + MT.detail(Elem.first()) + .child("preamble") + .addUsage(Opts.StorePreamblesInMemory ? Elem.second.UsedBytesPreamble + : 0); + MT.detail(Elem.first()).child("ast").addUsage(Elem.second.UsedBytesAST); + } +} } // namespace clangd } // namespace clang diff --git a/clang-tools-extra/clangd/TUScheduler.h b/clang-tools-extra/clangd/TUScheduler.h index 5d545b366ec3e0..cc38db8071aba5 100644 --- a/clang-tools-extra/clangd/TUScheduler.h +++ b/clang-tools-extra/clangd/TUScheduler.h @@ -14,6 +14,7 @@ #include "GlobalCompilationDatabase.h" #include "index/CanonicalIncludes.h" #include "support/Function.h" +#include "support/MemoryTree.h" #include "support/Path.h" #include "support/Threading.h" #include "llvm/ADT/Optional.h" @@ -207,7 +208,8 @@ class TUScheduler { ~TUScheduler(); struct FileStats { - std::size_t UsedBytes = 0; + std::size_t UsedBytesAST = 0; + std::size_t UsedBytesPreamble = 0; unsigned PreambleBuilds = 0; unsigned ASTBuilds = 0; }; @@ -311,6 +313,8 @@ class TUScheduler { // FIXME: move to ClangdServer via createProcessingContext. static llvm::Optional getFileBeingProcessedInContext(); + void profile(MemoryTree &MT) const; + private: const GlobalCompilationDatabase &CDB; Options Opts; diff --git a/clang-tools-extra/clangd/index/Background.cpp b/clang-tools-extra/clangd/index/Background.cpp index a1aafeaf31a96e..4779cb8d4c23af 100644 --- a/clang-tools-extra/clangd/index/Background.cpp +++ b/clang-tools-extra/clangd/index/Background.cpp @@ -16,6 +16,7 @@ #include "URI.h" #include "index/BackgroundIndexLoader.h" #include "index/FileIndex.h" +#include "index/Index.h" #include "index/IndexAction.h" #include "index/MemIndex.h" #include "index/Ref.h" @@ -414,5 +415,10 @@ BackgroundIndex::loadProject(std::vector MainFiles) { return {TUsToIndex.begin(), TUsToIndex.end()}; } +void BackgroundIndex::profile(MemoryTree &MT) const { + IndexedSymbols.profile(MT.child("symbols")); + // We don't want to mix memory used by index and symbols, so call base class. + MT.child("index").addUsage(SwapIndex::estimateMemoryUsage()); +} } // namespace clangd } // namespace clang diff --git a/clang-tools-extra/clangd/index/Background.h b/clang-tools-extra/clangd/index/Background.h index 472603013a53ac..e8f9468889f2a9 100644 --- a/clang-tools-extra/clangd/index/Background.h +++ b/clang-tools-extra/clangd/index/Background.h @@ -16,9 +16,11 @@ #include "index/Index.h" #include "index/Serialization.h" #include "support/Context.h" +#include "support/MemoryTree.h" #include "support/Path.h" #include "support/Threading.h" #include "support/ThreadsafeFS.h" +#include "support/Trace.h" #include "clang/Tooling/CompilationDatabase.h" #include "llvm/ADT/StringMap.h" #include "llvm/Support/Threading.h" @@ -172,6 +174,8 @@ class BackgroundIndex : public SwapIndex { return Queue.blockUntilIdleForTest(TimeoutSeconds); } + void profile(MemoryTree &MT) const; + private: /// Represents the state of a single file when indexing was performed. struct ShardVersion { diff --git a/clang-tools-extra/clangd/index/FileIndex.cpp b/clang-tools-extra/clangd/index/FileIndex.cpp index ad55b6ad7f5db1..587c7eb781700c 100644 --- a/clang-tools-extra/clangd/index/FileIndex.cpp +++ b/clang-tools-extra/clangd/index/FileIndex.cpp @@ -22,6 +22,7 @@ #include "index/SymbolOrigin.h" #include "index/dex/Dex.h" #include "support/Logger.h" +#include "support/MemoryTree.h" #include "support/Path.h" #include "clang/AST/ASTContext.h" #include "clang/Index/IndexingAction.h" @@ -388,6 +389,25 @@ FileSymbols::buildIndex(IndexType Type, DuplicateHandling DuplicateHandle, llvm_unreachable("Unknown clangd::IndexType"); } +void FileSymbols::profile(MemoryTree &MT) const { + std::lock_guard Lock(Mutex); + for (const auto &SymSlab : SymbolsSnapshot) { + MT.detail(SymSlab.first()) + .child("symbols") + .addUsage(SymSlab.second->bytes()); + } + for (const auto &RefSlab : RefsSnapshot) { + MT.detail(RefSlab.first()) + .child("references") + .addUsage(RefSlab.second.Slab->bytes()); + } + for (const auto &RelSlab : RelationsSnapshot) { + MT.detail(RelSlab.first()) + .child("relations") + .addUsage(RelSlab.second->bytes()); + } +} + FileIndex::FileIndex(bool UseDex, bool CollectMainFileRefs) : MergedIndex(&MainFileIndex, &PreambleIndex), UseDex(UseDex), CollectMainFileRefs(CollectMainFileRefs), @@ -457,5 +477,15 @@ void FileIndex::updateMain(PathRef Path, ParsedAST &AST) { } } +void FileIndex::profile(MemoryTree &MT) const { + PreambleSymbols.profile(MT.child("preamble").child("symbols")); + MT.child("preamble") + .child("index") + .addUsage(PreambleIndex.estimateMemoryUsage()); + MainFileSymbols.profile(MT.child("main_file").child("symbols")); + MT.child("main_file") + .child("index") + .addUsage(MainFileIndex.estimateMemoryUsage()); +} } // namespace clangd } // namespace clang diff --git a/clang-tools-extra/clangd/index/FileIndex.h b/clang-tools-extra/clangd/index/FileIndex.h index 127203c84c48ab..8ecae66373a5e3 100644 --- a/clang-tools-extra/clangd/index/FileIndex.h +++ b/clang-tools-extra/clangd/index/FileIndex.h @@ -24,6 +24,7 @@ #include "index/Relation.h" #include "index/Serialization.h" #include "index/Symbol.h" +#include "support/MemoryTree.h" #include "support/Path.h" #include "clang/Lex/Preprocessor.h" #include "clang/Tooling/CompilationDatabase.h" @@ -87,6 +88,8 @@ class FileSymbols { DuplicateHandling DuplicateHandle = DuplicateHandling::PickOne, size_t *Version = nullptr); + void profile(MemoryTree &MT) const; + private: struct RefSlabAndCountReferences { std::shared_ptr Slab; @@ -116,6 +119,8 @@ class FileIndex : public MergedIndex { /// `indexMainDecls`. void updateMain(PathRef Path, ParsedAST &AST); + void profile(MemoryTree &MT) const; + private: bool UseDex; // FIXME: this should be always on. bool CollectMainFileRefs; diff --git a/clang-tools-extra/clangd/support/CMakeLists.txt b/clang-tools-extra/clangd/support/CMakeLists.txt index ce08f7d58cd09d..e3412447142c44 100644 --- a/clang-tools-extra/clangd/support/CMakeLists.txt +++ b/clang-tools-extra/clangd/support/CMakeLists.txt @@ -21,6 +21,7 @@ add_clang_library(clangdSupport Context.cpp Logger.cpp Markup.cpp + MemoryTree.cpp Shutdown.cpp Threading.cpp ThreadsafeFS.cpp diff --git a/clang-tools-extra/clangd/support/MemoryTree.cpp b/clang-tools-extra/clangd/support/MemoryTree.cpp new file mode 100644 index 00000000000000..a495778b9e5bce --- /dev/null +++ b/clang-tools-extra/clangd/support/MemoryTree.cpp @@ -0,0 +1,51 @@ +#include "support/MemoryTree.h" +#include "Trace.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/StringRef.h" +#include + +namespace clang { +namespace clangd { + +namespace { + +size_t traverseTree(const MemoryTree &MT, std::string &ComponentName, + const trace::Metric &Out) { + size_t OriginalLen = ComponentName.size(); + if (!ComponentName.empty()) + ComponentName += '.'; + size_t Total = MT.self(); + for (const auto &Entry : MT.children()) { + ComponentName += Entry.first; + Total += traverseTree(Entry.getSecond(), ComponentName, Out); + ComponentName.resize(OriginalLen + 1); + } + ComponentName.resize(OriginalLen); + Out.record(Total, ComponentName); + return Total; +} +} // namespace + +MemoryTree &MemoryTree::createChild(llvm::StringRef Name) { + auto &Child = Children.try_emplace(Name, DetailAlloc).first->getSecond(); + return Child; +} + +const llvm::DenseMap & +MemoryTree::children() const { + return Children; +} + +size_t MemoryTree::total() const { + size_t Total = Size; + for (const auto &Entry : Children) + Total += Entry.getSecond().total(); + return Total; +} + +void record(const MemoryTree &MT, std::string RootName, + const trace::Metric &Out) { + traverseTree(MT, RootName, Out); +} +} // namespace clangd +} // namespace clang diff --git a/clang-tools-extra/clangd/support/MemoryTree.h b/clang-tools-extra/clangd/support/MemoryTree.h new file mode 100644 index 00000000000000..903cd64ebb7cfa --- /dev/null +++ b/clang-tools-extra/clangd/support/MemoryTree.h @@ -0,0 +1,92 @@ +//===--- MemoryTree.h - A special tree for components and sizes -*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_SUPPORT_MEMORYTREE_H_ +#define LLVM_CLANG_TOOLS_EXTRA_CLANGD_SUPPORT_MEMORYTREE_H_ + +#include "Trace.h" +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/Allocator.h" +#include "llvm/Support/StringSaver.h" +#include +#include +#include + +namespace clang { +namespace clangd { + +/// A tree that can be used to represent memory usage of nested components while +/// preserving the hierarchy. +/// Edges have associated names. An edge that might not be interesting to all +/// traversers or costly to copy (e.g. file names) can be marked as "detail". +/// Tree construction allows chosing between a detailed and brief mode, in brief +/// mode all "detail" edges are ignored and tree is constructed without any +/// string copies. +struct MemoryTree { +public: + /// If Alloc is nullptr, tree is in brief mode and will ignore detail edges. + MemoryTree(llvm::BumpPtrAllocator *DetailAlloc = nullptr) + : DetailAlloc(DetailAlloc) {} + + /// No copy of the \p Name. + /// Note that returned pointers are invalidated with subsequent calls to + /// child/detail. + MemoryTree &child(llvm::StringLiteral Name) { return createChild(Name); } + + MemoryTree(const MemoryTree &) = delete; + MemoryTree &operator=(const MemoryTree &) = delete; + + MemoryTree(MemoryTree &&) = default; + MemoryTree &operator=(MemoryTree &&) = default; + + /// Makes a copy of the \p Name in detailed mode, returns current node + /// otherwise. + /// Note that returned pointers are invalidated with subsequent calls to + /// child/detail. + MemoryTree &detail(llvm::StringRef Name) { + return DetailAlloc ? createChild(Name.copy(*DetailAlloc)) : *this; + } + + /// Increases size of current node by \p Increment. + void addUsage(size_t Increment) { Size += Increment; } + + /// Returns edges to direct children of this node. + const llvm::DenseMap &children() const; + + /// Returns total number of bytes used by this sub-tree. Performs a traversal. + size_t total() const; + + /// Returns total number of bytes used by this node only. + size_t self() const { return Size; } + +private: + /// Adds a child with an edge labeled as \p Name. Multiple calls to this + /// function returns the same node. + MemoryTree &createChild(llvm::StringRef Name); + + /// Allocator to use for detailed edge names. + llvm::BumpPtrAllocator *DetailAlloc = nullptr; + + /// Bytes owned by this component specifically. + size_t Size = 0; + + /// Edges from current node to its children. Keys are the labels for edges. + llvm::DenseMap Children; +}; + +/// Records total memory usage of each node under \p Out. Labels are edges on +/// the path joined with ".", starting with \p RootName. +void record(const MemoryTree &MT, std::string RootName, + const trace::Metric &Out); + +} // namespace clangd +} // namespace clang + +#endif diff --git a/clang-tools-extra/clangd/support/Trace.cpp b/clang-tools-extra/clangd/support/Trace.cpp index 7ab09cd23e6a76..d69b1c2bbde593 100644 --- a/clang-tools-extra/clangd/support/Trace.cpp +++ b/clang-tools-extra/clangd/support/Trace.cpp @@ -53,9 +53,12 @@ class JSONTracer : public EventTracer { // We stash a Span object in the context. It will record the start/end, // and this also allows us to look up the parent Span's information. - Context beginSpan(llvm::StringRef Name, llvm::json::Object *Args) override { - return Context::current().derive( - SpanKey, std::make_unique(this, Name, Args)); + Context beginSpan( + llvm::StringRef Name, + llvm::function_ref AttachDetails) override { + auto JS = std::make_unique(this, Name); + AttachDetails(&JS->Args); + return Context::current().derive(SpanKey, std::move(JS)); } // Trace viewer requires each thread to properly stack events. @@ -85,9 +88,9 @@ class JSONTracer : public EventTracer { private: class JSONSpan { public: - JSONSpan(JSONTracer *Tracer, llvm::StringRef Name, llvm::json::Object *Args) + JSONSpan(JSONTracer *Tracer, llvm::StringRef Name) : StartTime(Tracer->timestamp()), EndTime(0), Name(Name), - TID(llvm::get_threadid()), Tracer(Tracer), Args(Args) { + TID(llvm::get_threadid()), Tracer(Tracer) { // ~JSONSpan() may run in a different thread, so we need to capture now. Tracer->captureThreadMetadata(); @@ -125,7 +128,7 @@ class JSONTracer : public EventTracer { // Finally, record the event (ending at EndTime, not timestamp())! Tracer->jsonEvent("X", llvm::json::Object{{"name", std::move(Name)}, - {"args", std::move(*Args)}, + {"args", std::move(Args)}, {"dur", EndTime - StartTime}}, TID, StartTime); } @@ -133,6 +136,8 @@ class JSONTracer : public EventTracer { // May be called by any thread. void markEnded() { EndTime = Tracer->timestamp(); } + llvm::json::Object Args; + private: static int64_t nextID() { static std::atomic Next = {0}; @@ -144,7 +149,6 @@ class JSONTracer : public EventTracer { std::string Name; uint64_t TID; JSONTracer *Tracer; - llvm::json::Object *Args; }; static Key> SpanKey; @@ -277,12 +281,13 @@ void log(const llvm::Twine &Message) { T->instant("Log", llvm::json::Object{{"Message", Message.str()}}); } -// Returned context owns Args. -static Context makeSpanContext(llvm::Twine Name, llvm::json::Object *Args, - const Metric &LatencyMetric) { +bool enabled() { return T != nullptr; } + +// The JSON object is event args (owned by context), if the tracer wants them. +static std::pair +makeSpanContext(llvm::Twine Name, const Metric &LatencyMetric) { if (!T) - return Context::current().clone(); - WithContextValue WithArgs{std::unique_ptr(Args)}; + return std::make_pair(Context::current().clone(), nullptr); llvm::Optional WithLatency; using Clock = std::chrono::high_resolution_clock; WithLatency.emplace(llvm::make_scope_exit( @@ -293,9 +298,15 @@ static Context makeSpanContext(llvm::Twine Name, llvm::json::Object *Args, .count(), Name); })); - return T->beginSpan(Name.isSingleStringRef() ? Name.getSingleStringRef() - : llvm::StringRef(Name.str()), - Args); + llvm::json::Object *Args = nullptr; + Context Ctx = T->beginSpan( + Name.isSingleStringRef() ? Name.getSingleStringRef() + : llvm::StringRef(Name.str()), + [&](llvm::json::Object *A) { + assert(A && A->empty() && "Invalid AttachDetails() placeholder!"); + Args = A; + }); + return std::make_pair(std::move(Ctx), Args); } // Fallback metric that measures latencies for spans without an explicit latency @@ -307,8 +318,9 @@ constexpr Metric SpanLatency("span_latency", Metric::Distribution, "span_name"); // beginSpan() context is destroyed, when the tracing engine will consume them. Span::Span(llvm::Twine Name) : Span(Name, SpanLatency) {} Span::Span(llvm::Twine Name, const Metric &LatencyMetric) - : Args(T ? new llvm::json::Object() : nullptr), - RestoreCtx(makeSpanContext(Name, Args, LatencyMetric)) {} + : Span(makeSpanContext(Name, LatencyMetric)) {} +Span::Span(std::pair Pair) + : Args(Pair.second), RestoreCtx(std::move(Pair.first)) {} Span::~Span() { if (T) @@ -323,7 +335,9 @@ void Metric::record(double Value, llvm::StringRef Label) const { T->record(*this, Value, Label); } -Context EventTracer::beginSpan(llvm::StringRef Name, llvm::json::Object *Args) { +Context EventTracer::beginSpan( + llvm::StringRef Name, + llvm::function_ref AttachDetails) { return Context::current().clone(); } } // namespace trace diff --git a/clang-tools-extra/clangd/support/Trace.h b/clang-tools-extra/clangd/support/Trace.h index 9dc397a84b7449..52ee2ae617da60 100644 --- a/clang-tools-extra/clangd/support/Trace.h +++ b/clang-tools-extra/clangd/support/Trace.h @@ -79,8 +79,13 @@ class EventTracer { /// Returns a derived context that will be destroyed when the event ends. /// Usually implementations will store an object in the returned context /// whose destructor records the end of the event. - /// The args are *Args, only complete when the event ends. - virtual Context beginSpan(llvm::StringRef Name, llvm::json::Object *Args); + /// The tracer may capture event details provided in SPAN_ATTACH() calls. + /// In this case it should call AttachDetails(), and pass in an empty Object + /// to hold them. This Object should be owned by the context, and the data + /// will be complete by the time the context is destroyed. + virtual Context + beginSpan(llvm::StringRef Name, + llvm::function_ref AttachDetails); // Called when a Span is destroyed (it may still be active on other threads). // beginSpan() and endSpan() will always form a proper stack on each thread. // The Context returned by beginSpan is active, but Args is not ready. @@ -123,6 +128,9 @@ std::unique_ptr createCSVMetricTracer(llvm::raw_ostream &OS); /// Records a single instant event, associated with the current thread. void log(const llvm::Twine &Name); +/// Returns true if there is an active tracer. +bool enabled(); + /// Records an event whose duration is the lifetime of the Span object. /// This lifetime is extended when the span's context is reused. /// @@ -146,6 +154,8 @@ class Span { llvm::json::Object *const Args; private: + // Awkward constructor works around constant initialization. + Span(std::pair); WithContext RestoreCtx; }; diff --git a/clang-tools-extra/clangd/unittests/BackgroundIndexTests.cpp b/clang-tools-extra/clangd/unittests/BackgroundIndexTests.cpp index adf39a915c1a15..4f089519530a2c 100644 --- a/clang-tools-extra/clangd/unittests/BackgroundIndexTests.cpp +++ b/clang-tools-extra/clangd/unittests/BackgroundIndexTests.cpp @@ -21,6 +21,7 @@ using ::testing::AllOf; using ::testing::Contains; using ::testing::ElementsAre; using ::testing::Not; +using ::testing::Pair; using ::testing::UnorderedElementsAre; namespace clang { @@ -916,5 +917,18 @@ TEST(BackgroundQueueTest, Progress) { EXPECT_EQ(S.LastIdle, 2000u); } +TEST(BackgroundIndex, Profile) { + MockFS FS; + MockCompilationDatabase CDB; + BackgroundIndex Idx(FS, CDB, [](llvm::StringRef) { return nullptr; }, + /*Opts=*/{}); + + llvm::BumpPtrAllocator Alloc; + MemoryTree MT(&Alloc); + Idx.profile(MT); + ASSERT_THAT(MT.children(), + UnorderedElementsAre(Pair("symbols", _), Pair("index", _))); +} + } // namespace clangd } // namespace clang diff --git a/clang-tools-extra/clangd/unittests/CMakeLists.txt b/clang-tools-extra/clangd/unittests/CMakeLists.txt index a84fd0b71ca511..de8eaca6059fa7 100644 --- a/clang-tools-extra/clangd/unittests/CMakeLists.txt +++ b/clang-tools-extra/clangd/unittests/CMakeLists.txt @@ -99,6 +99,7 @@ add_unittest(ClangdUnitTests ClangdTests support/ContextTests.cpp support/FunctionTests.cpp support/MarkupTests.cpp + support/MemoryTreeTests.cpp support/ThreadingTests.cpp support/TestTracer.cpp support/TraceTests.cpp diff --git a/clang-tools-extra/clangd/unittests/ClangdTests.cpp b/clang-tools-extra/clangd/unittests/ClangdTests.cpp index 813b95aa3c824b..f2d6d6b8192b42 100644 --- a/clang-tools-extra/clangd/unittests/ClangdTests.cpp +++ b/clang-tools-extra/clangd/unittests/ClangdTests.cpp @@ -17,6 +17,7 @@ #include "TestFS.h" #include "TestTU.h" #include "URI.h" +#include "support/MemoryTree.h" #include "support/Path.h" #include "support/Threading.h" #include "clang/Config/config.h" @@ -27,6 +28,7 @@ #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringMap.h" #include "llvm/ADT/StringRef.h" +#include "llvm/Support/Allocator.h" #include "llvm/Support/Errc.h" #include "llvm/Support/Path.h" #include "llvm/Support/Regex.h" @@ -48,6 +50,7 @@ namespace clangd { namespace { using ::testing::AllOf; +using ::testing::Contains; using ::testing::ElementsAre; using ::testing::Field; using ::testing::Gt; @@ -565,7 +568,9 @@ int hello; } MATCHER_P4(Stats, Name, UsesMemory, PreambleBuilds, ASTBuilds, "") { - return arg.first() == Name && (arg.second.UsedBytes != 0) == UsesMemory && + return arg.first() == Name && + (arg.second.UsedBytesAST + arg.second.UsedBytesPreamble != 0) == + UsesMemory && std::tie(arg.second.PreambleBuilds, ASTBuilds) == std::tie(PreambleBuilds, ASTBuilds); } @@ -1234,6 +1239,21 @@ TEST(ClangdServer, TidyOverrideTest) { EXPECT_FALSE(DiagConsumer.HadDiagsInLastCallback); } +TEST(ClangdServer, MemoryUsageTest) { + MockFS FS; + MockCompilationDatabase CDB; + ClangdServer Server(CDB, FS, ClangdServer::optsForTest()); + + auto FooCpp = testPath("foo.cpp"); + Server.addDocument(FooCpp, ""); + ASSERT_TRUE(Server.blockUntilIdleForTest()); + + llvm::BumpPtrAllocator Alloc; + MemoryTree MT(&Alloc); + Server.profile(MT); + ASSERT_TRUE(MT.children().count("tuscheduler")); + EXPECT_TRUE(MT.child("tuscheduler").children().count(FooCpp)); +} } // namespace } // namespace clangd } // namespace clang diff --git a/clang-tools-extra/clangd/unittests/FileIndexTests.cpp b/clang-tools-extra/clangd/unittests/FileIndexTests.cpp index c5bfbe132d37f4..2b20b7e7fef0d2 100644 --- a/clang-tools-extra/clangd/unittests/FileIndexTests.cpp +++ b/clang-tools-extra/clangd/unittests/FileIndexTests.cpp @@ -22,20 +22,25 @@ #include "index/Relation.h" #include "index/Serialization.h" #include "index/Symbol.h" +#include "index/SymbolID.h" #include "support/Threading.h" #include "clang/Frontend/CompilerInvocation.h" #include "clang/Frontend/Utils.h" #include "clang/Index/IndexSymbol.h" #include "clang/Lex/Preprocessor.h" #include "clang/Tooling/CompilationDatabase.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/Support/Allocator.h" #include "gmock/gmock.h" #include "gtest/gtest.h" #include +#include using ::testing::_; using ::testing::AllOf; using ::testing::Contains; using ::testing::ElementsAre; +using ::testing::Gt; using ::testing::IsEmpty; using ::testing::Pair; using ::testing::UnorderedElementsAre; @@ -88,6 +93,13 @@ std::unique_ptr refSlab(const SymbolID &ID, const char *Path) { return std::make_unique(std::move(Slab).build()); } +std::unique_ptr relSlab(llvm::ArrayRef Rels) { + RelationSlab::Builder RelBuilder; + for (auto &Rel : Rels) + RelBuilder.insert(Rel); + return std::make_unique(std::move(RelBuilder).build()); +} + TEST(FileSymbolsTest, UpdateAndGet) { FileSymbols FS; EXPECT_THAT(runFuzzyFind(*FS.buildIndex(IndexType::Light), ""), IsEmpty()); @@ -643,6 +655,50 @@ TEST(FileShardedIndexTest, Sharding) { EXPECT_TRUE(Shard->Cmd.hasValue()); } } + +TEST(FileIndexTest, Profile) { + FileIndex FI; + + auto FileName = testPath("foo.cpp"); + auto AST = TestTU::withHeaderCode("int a;").build(); + FI.updateMain(FileName, AST); + FI.updatePreamble(FileName, "v1", AST.getASTContext(), + AST.getPreprocessorPtr(), AST.getCanonicalIncludes()); + + llvm::BumpPtrAllocator Alloc; + MemoryTree MT(&Alloc); + FI.profile(MT); + ASSERT_THAT(MT.children(), + UnorderedElementsAre(Pair("preamble", _), Pair("main_file", _))); + + ASSERT_THAT(MT.child("preamble").children(), + UnorderedElementsAre(Pair("index", _), Pair("symbols", _))); + ASSERT_THAT(MT.child("main_file").children(), + UnorderedElementsAre(Pair("index", _), Pair("symbols", _))); + + ASSERT_THAT(MT.child("preamble").child("index").total(), Gt(0U)); + ASSERT_THAT(MT.child("main_file").child("index").total(), Gt(0U)); +} + +TEST(FileSymbolsTest, Profile) { + FileSymbols FS; + FS.update("f1", numSlab(1, 2), nullptr, nullptr, false); + FS.update("f2", nullptr, refSlab(SymbolID("1"), "f1"), nullptr, false); + FS.update("f3", nullptr, nullptr, + relSlab({{SymbolID("1"), RelationKind::BaseOf, SymbolID("2")}}), + false); + llvm::BumpPtrAllocator Alloc; + MemoryTree MT(&Alloc); + FS.profile(MT); + ASSERT_THAT(MT.children(), UnorderedElementsAre(Pair("f1", _), Pair("f2", _), + Pair("f3", _))); + EXPECT_THAT(MT.child("f1").children(), ElementsAre(Pair("symbols", _))); + EXPECT_THAT(MT.child("f1").total(), Gt(0U)); + EXPECT_THAT(MT.child("f2").children(), ElementsAre(Pair("references", _))); + EXPECT_THAT(MT.child("f2").total(), Gt(0U)); + EXPECT_THAT(MT.child("f3").children(), ElementsAre(Pair("relations", _))); + EXPECT_THAT(MT.child("f3").total(), Gt(0U)); +} } // namespace } // namespace clangd } // namespace clang diff --git a/clang-tools-extra/clangd/unittests/ParsedASTTests.cpp b/clang-tools-extra/clangd/unittests/ParsedASTTests.cpp index 65d9cffeedc73d..9b7b2e2dcd4796 100644 --- a/clang-tools-extra/clangd/unittests/ParsedASTTests.cpp +++ b/clang-tools-extra/clangd/unittests/ParsedASTTests.cpp @@ -57,6 +57,17 @@ MATCHER_P(DeclNamed, Name, "") { return false; } +MATCHER_P(DeclKind, Kind, "") { + if (NamedDecl *ND = dyn_cast(arg)) + if (ND->getDeclKindName() == Kind) + return true; + if (auto *Stream = result_listener->stream()) { + llvm::raw_os_ostream OS(*Stream); + arg->dump(OS); + } + return false; +} + // Matches if the Decl has template args equal to ArgName. If the decl is a // NamedDecl and ArgName is an empty string it also matches. MATCHER_P(WithTemplateArgs, ArgName, "") { @@ -93,15 +104,22 @@ MATCHER(EqInc, "") { std::tie(Expected.HashLine, Expected.Written); } -TEST(ParsedASTTest, TopLevelDecls) { +// FIXME: figure out why it fails on clang-ppc64le-rhel buildbot. +TEST(ParsedASTTest, DISABLED_TopLevelDecls) { TestTU TU; TU.HeaderCode = R"( int header1(); int header2; )"; - TU.Code = "int main();"; + TU.Code = R"cpp( + int main(); + template bool X = true; + )cpp"; auto AST = TU.build(); - EXPECT_THAT(AST.getLocalTopLevelDecls(), ElementsAre(DeclNamed("main"))); + EXPECT_THAT(AST.getLocalTopLevelDecls(), + testing::UnorderedElementsAreArray( + {AllOf(DeclNamed("main"), DeclKind("Function")), + AllOf(DeclNamed("X"), DeclKind("VarTemplate"))})); } TEST(ParsedASTTest, DoesNotGetIncludedTopDecls) { diff --git a/clang-tools-extra/clangd/unittests/support/MemoryTreeTests.cpp b/clang-tools-extra/clangd/unittests/support/MemoryTreeTests.cpp new file mode 100644 index 00000000000000..7d3d29a9e1ddfc --- /dev/null +++ b/clang-tools-extra/clangd/unittests/support/MemoryTreeTests.cpp @@ -0,0 +1,121 @@ +//===-- MemoryTreeTests.cpp -------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "support/MemoryTree.h" +#include "support/TestTracer.h" +#include "support/Trace.h" +#include "llvm/Support/Allocator.h" +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include + +namespace clang { +namespace clangd { +namespace { +using testing::Contains; +using testing::ElementsAre; +using testing::IsEmpty; +using testing::UnorderedElementsAre; + +MATCHER_P2(WithNameAndSize, Name, Size, "") { + return arg.first == Name && + arg.getSecond().total() == static_cast(Size); +} + +TEST(MemoryTree, Basics) { + MemoryTree MT; + EXPECT_EQ(MT.total(), 0U); + EXPECT_THAT(MT.children(), IsEmpty()); + + MT.addUsage(42); + EXPECT_EQ(MT.total(), 42U); + EXPECT_THAT(MT.children(), IsEmpty()); + + MT.child("leaf").addUsage(1); + EXPECT_EQ(MT.total(), 43U); + EXPECT_THAT(MT.children(), UnorderedElementsAre(WithNameAndSize("leaf", 1))); + + // child should be idempotent. + MT.child("leaf").addUsage(1); + EXPECT_EQ(MT.total(), 44U); + EXPECT_THAT(MT.children(), UnorderedElementsAre(WithNameAndSize("leaf", 2))); +} + +TEST(MemoryTree, DetailedNodesWithoutDetails) { + MemoryTree MT; + MT.detail("should_be_ignored").addUsage(2); + EXPECT_THAT(MT.children(), IsEmpty()); + EXPECT_EQ(MT.total(), 2U); + + // Make sure children from details are merged. + MT.detail("first_detail").child("leaf").addUsage(1); + MT.detail("second_detail").child("leaf").addUsage(1); + EXPECT_THAT(MT.children(), Contains(WithNameAndSize("leaf", 2))); +} + +TEST(MemoryTree, DetailedNodesWithDetails) { + llvm::BumpPtrAllocator Alloc; + MemoryTree MT(&Alloc); + + { + auto &Detail = MT.detail("first_detail"); + Detail.child("leaf").addUsage(1); + EXPECT_THAT(MT.children(), Contains(WithNameAndSize("first_detail", 1))); + EXPECT_THAT(Detail.children(), Contains(WithNameAndSize("leaf", 1))); + } + + { + auto &Detail = MT.detail("second_detail"); + Detail.child("leaf").addUsage(1); + EXPECT_THAT(MT.children(), Contains(WithNameAndSize("second_detail", 1))); + EXPECT_THAT(Detail.children(), Contains(WithNameAndSize("leaf", 1))); + } +} + +TEST(MemoryTree, Record) { + trace::TestTracer Tracer; + static constexpr llvm::StringLiteral MetricName = "memory_usage"; + static constexpr trace::Metric OutMetric(MetricName, trace::Metric::Value, + "component_name"); + auto AddNodes = [](MemoryTree Root) { + Root.child("leaf").addUsage(1); + + { + auto &Detail = Root.detail("detail"); + Detail.addUsage(1); + Detail.child("leaf").addUsage(1); + auto &Child = Detail.child("child"); + Child.addUsage(1); + Child.child("leaf").addUsage(1); + } + + { + auto &Child = Root.child("child"); + Child.addUsage(1); + Child.child("leaf").addUsage(1); + } + return Root; + }; + + llvm::BumpPtrAllocator Alloc; + record(AddNodes(MemoryTree(&Alloc)), "root", OutMetric); + EXPECT_THAT(Tracer.takeMetric(MetricName, "root"), ElementsAre(7)); + EXPECT_THAT(Tracer.takeMetric(MetricName, "root.leaf"), ElementsAre(1)); + EXPECT_THAT(Tracer.takeMetric(MetricName, "root.detail"), ElementsAre(4)); + EXPECT_THAT(Tracer.takeMetric(MetricName, "root.detail.leaf"), + ElementsAre(1)); + EXPECT_THAT(Tracer.takeMetric(MetricName, "root.detail.child"), + ElementsAre(2)); + EXPECT_THAT(Tracer.takeMetric(MetricName, "root.detail.child.leaf"), + ElementsAre(1)); + EXPECT_THAT(Tracer.takeMetric(MetricName, "root.child"), ElementsAre(2)); + EXPECT_THAT(Tracer.takeMetric(MetricName, "root.child.leaf"), ElementsAre(1)); +} +} // namespace +} // namespace clangd +} // namespace clang diff --git a/clang-tools-extra/clangd/unittests/support/TraceTests.cpp b/clang-tools-extra/clangd/unittests/support/TraceTests.cpp index 116d9fcee02c77..bb515edd72535d 100644 --- a/clang-tools-extra/clangd/unittests/support/TraceTests.cpp +++ b/clang-tools-extra/clangd/unittests/support/TraceTests.cpp @@ -186,6 +186,11 @@ TEST_F(CSVMetricsTracerTest, Escaping) { StartsWith("d,dist,\"a\nb\",1"), "")); } +TEST_F(CSVMetricsTracerTest, IgnoresArgs) { + trace::Span Tracer("Foo"); + EXPECT_EQ(nullptr, Tracer.Args); +} + } // namespace } // namespace clangd } // namespace clang diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp index 3f314c59ade6ec..01a16575c239cf 100644 --- a/clang/lib/Parse/ParseDecl.cpp +++ b/clang/lib/Parse/ParseDecl.cpp @@ -2195,6 +2195,7 @@ Decl *Parser::ParseDeclarationAfterDeclaratorAndAttributes( // Inform the current actions module that we just parsed this declarator. Decl *ThisDecl = nullptr; + Decl *OuterDecl = nullptr; switch (TemplateInfo.Kind) { case ParsedTemplateInfo::NonTemplate: ThisDecl = Actions.ActOnDeclarator(getCurScope(), D); @@ -2205,10 +2206,12 @@ Decl *Parser::ParseDeclarationAfterDeclaratorAndAttributes( ThisDecl = Actions.ActOnTemplateDeclarator(getCurScope(), *TemplateInfo.TemplateParams, D); - if (VarTemplateDecl *VT = dyn_cast_or_null(ThisDecl)) + if (VarTemplateDecl *VT = dyn_cast_or_null(ThisDecl)) { // Re-direct this decl to refer to the templated decl so that we can // initialize it. ThisDecl = VT->getTemplatedDecl(); + OuterDecl = VT; + } break; } case ParsedTemplateInfo::ExplicitInstantiation: { @@ -2385,8 +2388,7 @@ Decl *Parser::ParseDeclarationAfterDeclaratorAndAttributes( } Actions.FinalizeDeclaration(ThisDecl); - - return ThisDecl; + return OuterDecl ? OuterDecl : ThisDecl; } /// ParseSpecifierQualifierList diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index 68a8777eae73a1..f0cb227ff58ee5 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -6375,6 +6375,21 @@ ExprResult Sema::BuildCallExpr(Scope *Scope, Expr *Fn, SourceLocation LParenLoc, checkDirectCallValidity(*this, Fn, FD, ArgExprs); } + if (Context.isDependenceAllowed() && + (Fn->isTypeDependent() || Expr::hasAnyTypeDependentArguments(ArgExprs))) { + assert(!getLangOpts().CPlusPlus); + assert(Fn->containsErrors() || + llvm::any_of(ArgExprs, + [](clang::Expr *E) { return E->containsErrors(); }) && + "should only occur in error-recovery path."); + QualType ReturnType = + llvm::isa_and_nonnull(NDecl) + ? dyn_cast(NDecl)->getCallResultType() + : Context.DependentTy; + return CallExpr::Create(Context, Fn, ArgExprs, ReturnType, + Expr::getValueKindForType(ReturnType), RParenLoc, + CurFPFeatureOverrides()); + } return BuildResolvedCallExpr(Fn, NDecl, LParenLoc, ArgExprs, RParenLoc, ExecConfig, IsExecConfig); } @@ -6515,7 +6530,7 @@ ExprResult Sema::BuildResolvedCallExpr(Expr *Fn, NamedDecl *NDecl, CurFPFeatureOverrides(), NumParams, UsesADL); } - if (!getLangOpts().CPlusPlus) { + if (!Context.isDependenceAllowed()) { // Forget about the nulled arguments since typo correction // do not handle them well. TheCall->shrinkNumArgs(Args.size()); @@ -8479,7 +8494,7 @@ ExprResult Sema::ActOnConditionalOp(SourceLocation QuestionLoc, SourceLocation ColonLoc, Expr *CondExpr, Expr *LHSExpr, Expr *RHSExpr) { - if (!getLangOpts().CPlusPlus) { + if (!Context.isDependenceAllowed()) { // C cannot handle TypoExpr nodes in the condition because it // doesn't handle dependent types properly, so make sure any TypoExprs have // been dealt with before checking the operands. @@ -19052,7 +19067,7 @@ static ExprResult diagnoseUnknownAnyExpr(Sema &S, Expr *E) { /// Check for operands with placeholder types and complain if found. /// Returns ExprError() if there was an error and no recovery was possible. ExprResult Sema::CheckPlaceholderExpr(Expr *E) { - if (!getLangOpts().CPlusPlus) { + if (!Context.isDependenceAllowed()) { // C cannot handle TypoExpr nodes on either side of a binop because it // doesn't handle dependent types properly, so make sure any TypoExprs have // been dealt with before checking the operands. diff --git a/clang/test/AST/ast-dump-recovery.c b/clang/test/AST/ast-dump-recovery.c index d14aedebe4903b..f7b3c7bb4f2f4a 100644 --- a/clang/test/AST/ast-dump-recovery.c +++ b/clang/test/AST/ast-dump-recovery.c @@ -24,14 +24,10 @@ int postfix_inc = a++; int unary_address = &(a + 1); // CHECK: VarDecl {{.*}} ternary 'int' cinit -// CHECK-NEXT: `-RecoveryExpr {{.*}} +// CHECK-NEXT: `-ConditionalOperator {{.*}} // CHECK-NEXT: |-DeclRefExpr {{.*}} 'a' -// CHECK-NEXT: |-TypoExpr {{.*}} +// CHECK-NEXT: |-RecoveryExpr {{.*}} // CHECK-NEXT: `-DeclRefExpr {{.*}} 'a' -// FIXME: The TypoExpr should never be print, and should be downgraded to -// RecoveryExpr -- typo correction is performed too early in C-only codepath, -// which makes no correction when clang finishes the full expr (Sema::Sema::ActOnFinishFullExpr). -// this will be fixed when we support dependent mechanism and delayed typo correction for C. int ternary = a ? undef : a; void test1() { @@ -87,3 +83,18 @@ void test2() { // CHECK-NEXT: `-DeclRefExpr {{.*}} 'some_func' (float)some_func(); } + +void test3() { + // CHECK: CallExpr {{.*}} '' contains-errors + // CHECK-NEXT: |-ParenExpr {{.*}} contains-errors lvalue + // CHECK-NEXT: | `-RecoveryExpr {{.*}} contains-errors + // CHECK-NEXT: | `-DeclRefExpr {{.*}} '__builtin_classify_type' + // CHECK-NEXT: `-IntegerLiteral {{.*}} 'int' 1 + (*__builtin_classify_type)(1); + + extern void ext(); + // CHECK: CallExpr {{.*}} 'void' contains-errors + // CHECK-NEXT: |-DeclRefExpr {{.*}} 'ext' + // CHECK-NEXT: `-RecoveryExpr {{.*}} '' + ext(undef_var); +} diff --git a/clang/test/Sema/error-dependence.c b/clang/test/Sema/error-dependence.c index 41733cdba3fe7d..608e9af4444e0c 100644 --- a/clang/test/Sema/error-dependence.c +++ b/clang/test/Sema/error-dependence.c @@ -10,6 +10,9 @@ void test1(int s) { // verify diagnostic "operand of type '' where arithmetic or // pointer type is required" is not emitted. (float)call(); // expected-error {{too few arguments to function call}} + // verify disgnostic "called object type '' is not a function + // or function pointer" is not emitted. + (*__builtin_classify_type)(1); // expected-error {{builtin functions must be directly called}} } void test2(int* ptr, float f) { diff --git a/lldb/docs/lldb-platform-packets.txt b/lldb/docs/lldb-platform-packets.txt index 8d3fed7ab3410e..e688fc92bb7ef4 100644 --- a/lldb/docs/lldb-platform-packets.txt +++ b/lldb/docs/lldb-platform-packets.txt @@ -411,6 +411,12 @@ incompatible with the flags that gdb specifies. // // Response is F, followed by the number of bytes read (base 10), a // semicolon, followed by the data in the binary-escaped-data encoding. +// +// COMPATIBILITY +// The gdb-remote serial protocol documentation says that numbers +// in "vFile:" packets should be hexidecimal. Instead lldb uses +// decimal for the number of bytes and offset. +// lldb-server can process either format. //---------------------------------------------------------------------- @@ -430,7 +436,11 @@ incompatible with the flags that gdb specifies. // 3. binary-escaped-data to be written // // Response is F, followed by the number of bytes written (base 10) - +// +// COMPATIBILITY +// The gdb-remote serial protocol documentation says that numbers +// in "vFile:" packets should be hexidecimal. Instead lldb uses +// decimal for the offset. lldb-server can process either format. diff --git a/lldb/include/lldb/Utility/UnimplementedError.h b/lldb/include/lldb/Utility/UnimplementedError.h new file mode 100644 index 00000000000000..c6fab0a9483c01 --- /dev/null +++ b/lldb/include/lldb/Utility/UnimplementedError.h @@ -0,0 +1,28 @@ +//===-- UnimplementedError.h ------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_UTILITY_UNIMPLEMENTEDERROR_H +#define LLDB_UTILITY_UNIMPLEMENTEDERROR_H + +#include "llvm/Support/Errc.h" +#include "llvm/Support/Error.h" + +namespace lldb_private { +class UnimplementedError : public llvm::ErrorInfo { +public: + static char ID; + + void log(llvm::raw_ostream &OS) const override { OS << "Not implemented"; } + + std::error_code convertToErrorCode() const override { + return llvm::errc::not_supported; + }; +}; +} // namespace lldb_private + +#endif // LLDB_UTILITY_UNIMPLEMENTEDERROR_H diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServer.cpp b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServer.cpp index b78f0916b9b982..60548efc0f33f3 100644 --- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServer.cpp +++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServer.cpp @@ -12,11 +12,11 @@ #include "GDBRemoteCommunicationServer.h" -#include - #include "ProcessGDBRemoteLog.h" #include "lldb/Utility/StreamString.h" #include "lldb/Utility/StringExtractorGDBRemote.h" +#include "lldb/Utility/UnimplementedError.h" +#include using namespace lldb; using namespace lldb_private; @@ -113,18 +113,17 @@ GDBRemoteCommunicationServer::SendErrorResponse(const Status &error) { GDBRemoteCommunication::PacketResult GDBRemoteCommunicationServer::SendErrorResponse(llvm::Error error) { + assert(error); std::unique_ptr EIB; - std::unique_ptr PUE; + std::unique_ptr UE; llvm::handleAllErrors( std::move(error), - [&](std::unique_ptr E) { PUE = std::move(E); }, + [&](std::unique_ptr E) { UE = std::move(E); }, [&](std::unique_ptr E) { EIB = std::move(E); }); if (EIB) return SendErrorResponse(Status(llvm::Error(std::move(EIB)))); - if (PUE) - return SendUnimplementedResponse(PUE->message().c_str()); - return SendErrorResponse(Status("Unknown Error")); + return SendUnimplementedResponse(""); } GDBRemoteCommunication::PacketResult @@ -152,5 +151,3 @@ GDBRemoteCommunicationServer::SendOKResponse() { bool GDBRemoteCommunicationServer::HandshakeWithClient() { return GetAck() == PacketResult::Success; } - -char PacketUnimplementedError::ID; diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServer.h b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServer.h index a7c2ea47e3bad0..63567bb9b5dee7 100644 --- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServer.h +++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServer.h @@ -79,18 +79,6 @@ class GDBRemoteCommunicationServer : public GDBRemoteCommunication { operator=(const GDBRemoteCommunicationServer &) = delete; }; -class PacketUnimplementedError - : public llvm::ErrorInfo { -public: - static char ID; - using llvm::ErrorInfo::ErrorInfo; // inherit constructors - PacketUnimplementedError(const llvm::Twine &S) - : ErrorInfo(S, llvm::errc::not_supported) {} - - PacketUnimplementedError() : ErrorInfo(llvm::errc::not_supported) {} -}; - } // namespace process_gdb_remote } // namespace lldb_private diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp index ae2f4bd041c9b2..6f4d18364b266b 100644 --- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp +++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp @@ -10,13 +10,12 @@ #include "lldb/Host/Config.h" -#include "GDBRemoteCommunicationServerLLGS.h" -#include "lldb/Utility/GDBRemote.h" #include #include #include +#include "GDBRemoteCommunicationServerLLGS.h" #include "lldb/Host/ConnectionFileDescriptor.h" #include "lldb/Host/Debug.h" #include "lldb/Host/File.h" @@ -32,11 +31,13 @@ #include "lldb/Utility/Args.h" #include "lldb/Utility/DataBuffer.h" #include "lldb/Utility/Endian.h" +#include "lldb/Utility/GDBRemote.h" #include "lldb/Utility/LLDBAssert.h" #include "lldb/Utility/Log.h" #include "lldb/Utility/RegisterValue.h" #include "lldb/Utility/State.h" #include "lldb/Utility/StreamString.h" +#include "lldb/Utility/UnimplementedError.h" #include "lldb/Utility/UriParser.h" #include "llvm/ADT/Triple.h" #include "llvm/Support/JSON.h" @@ -2876,8 +2877,7 @@ GDBRemoteCommunicationServerLLGS::ReadXferObject(llvm::StringRef object, if (object == "features" && annex == "target.xml") return BuildTargetXml(); - return llvm::make_error( - "Xfer object not supported"); + return llvm::make_error(); } GDBRemoteCommunication::PacketResult diff --git a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp index 9f4556f791aedf..3f7301871f957c 100644 --- a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp +++ b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp @@ -632,8 +632,7 @@ DWARFDebugInfo &SymbolFileDWARF::DebugInfo() { return *m_info; } -DWARFUnit * -SymbolFileDWARF::GetDWARFCompileUnit(lldb_private::CompileUnit *comp_unit) { +DWARFCompileUnit *SymbolFileDWARF::GetDWARFCompileUnit(CompileUnit *comp_unit) { if (!comp_unit) return nullptr; @@ -641,7 +640,9 @@ SymbolFileDWARF::GetDWARFCompileUnit(lldb_private::CompileUnit *comp_unit) { DWARFUnit *dwarf_cu = DebugInfo().GetUnitAtIndex(comp_unit->GetID()); if (dwarf_cu && dwarf_cu->GetUserData() == nullptr) dwarf_cu->SetUserData(comp_unit); - return dwarf_cu; + + // It must be DWARFCompileUnit when it created a CompileUnit. + return llvm::cast_or_null(dwarf_cu); } DWARFDebugRanges *SymbolFileDWARF::GetDebugRanges() { @@ -1599,8 +1600,7 @@ static uint64_t GetDWOId(DWARFCompileUnit &dwarf_cu, llvm::Optional SymbolFileDWARF::GetDWOId() { if (GetNumCompileUnits() == 1) { if (auto comp_unit = GetCompileUnitAtIndex(0)) - if (DWARFCompileUnit *cu = llvm::dyn_cast_or_null( - GetDWARFCompileUnit(comp_unit.get()))) + if (DWARFCompileUnit *cu = GetDWARFCompileUnit(comp_unit.get())) if (DWARFDebugInfoEntry *cu_die = cu->DIE().GetDIE()) if (uint64_t dwo_id = ::GetDWOId(*cu, *cu_die)) return dwo_id; diff --git a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h index 76ceb279c718e2..019f76c67c637c 100644 --- a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h +++ b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h @@ -346,7 +346,7 @@ class SymbolFileDWARF : public lldb_private::SymbolFile, lldb::CompUnitSP ParseCompileUnit(DWARFCompileUnit &dwarf_cu); - virtual DWARFUnit * + virtual DWARFCompileUnit * GetDWARFCompileUnit(lldb_private::CompileUnit *comp_unit); DWARFUnit *GetNextUnparsedDWARFCompileUnit(DWARFUnit *prev_cu); diff --git a/lldb/source/Utility/CMakeLists.txt b/lldb/source/Utility/CMakeLists.txt index 1e3d859e2a6cbe..875738178541fd 100644 --- a/lldb/source/Utility/CMakeLists.txt +++ b/lldb/source/Utility/CMakeLists.txt @@ -65,6 +65,7 @@ add_lldb_library(lldbUtility StructuredData.cpp TildeExpressionResolver.cpp Timer.cpp + UnimplementedError.cpp UUID.cpp UriParser.cpp UserID.cpp diff --git a/lldb/source/Utility/UnimplementedError.cpp b/lldb/source/Utility/UnimplementedError.cpp new file mode 100644 index 00000000000000..034ad5b17b6402 --- /dev/null +++ b/lldb/source/Utility/UnimplementedError.cpp @@ -0,0 +1,11 @@ +//===-- UnimplementedError.cpp --------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "lldb/Utility/UnimplementedError.h" + +char lldb_private::UnimplementedError::ID; diff --git a/lldb/unittests/Process/gdb-remote/GDBRemoteCommunicationServerTest.cpp b/lldb/unittests/Process/gdb-remote/GDBRemoteCommunicationServerTest.cpp index 4c4916e3668f87..6ab37599ae36bb 100644 --- a/lldb/unittests/Process/gdb-remote/GDBRemoteCommunicationServerTest.cpp +++ b/lldb/unittests/Process/gdb-remote/GDBRemoteCommunicationServerTest.cpp @@ -9,9 +9,9 @@ #include "gtest/gtest.h" #include "GDBRemoteTestUtils.h" - #include "Plugins/Process/gdb-remote/GDBRemoteCommunicationServer.h" #include "lldb/Utility/Connection.h" +#include "lldb/Utility/UnimplementedError.h" namespace lldb_private { namespace process_gdb_remote { @@ -39,8 +39,7 @@ TEST(GDBRemoteCommunicationServerTest, SendErrorResponse_Status) { TEST(GDBRemoteCommunicationServerTest, SendErrorResponse_UnimplementedError) { MockServerWithMockConnection server; - auto error = - llvm::make_error("Test unimplemented error"); + auto error = llvm::make_error(); server.SendErrorResponse(std::move(error)); EXPECT_THAT(server.GetPackets(), testing::ElementsAre("$#00")); @@ -61,8 +60,8 @@ TEST(GDBRemoteCommunicationServerTest, SendErrorResponse_StringError) { TEST(GDBRemoteCommunicationServerTest, SendErrorResponse_ErrorList) { MockServerWithMockConnection server; - auto error = llvm::joinErrors(llvm::make_error(), - llvm::make_error()); + auto error = llvm::joinErrors(llvm::make_error(), + llvm::make_error()); server.SendErrorResponse(std::move(error)); // Make sure only one packet is sent even when there are multiple errors. diff --git a/llvm/include/llvm/CodeGen/TargetLowering.h b/llvm/include/llvm/CodeGen/TargetLowering.h index b8a7a3437915a2..107814aab102f4 100644 --- a/llvm/include/llvm/CodeGen/TargetLowering.h +++ b/llvm/include/llvm/CodeGen/TargetLowering.h @@ -427,7 +427,7 @@ class TargetLoweringBase { virtual TargetLoweringBase::LegalizeTypeAction getPreferredVectorAction(MVT VT) const { // The default action for one element vectors is to scalarize - if (VT.getVectorElementCount() == 1) + if (VT.getVectorElementCount().isScalar()) return TypeScalarizeVector; // The default action for an odd-width vector is to widen. if (!VT.isPow2VectorType()) diff --git a/llvm/include/llvm/CodeGen/ValueTypes.h b/llvm/include/llvm/CodeGen/ValueTypes.h index d409196af8d9d5..958711f1529cca 100644 --- a/llvm/include/llvm/CodeGen/ValueTypes.h +++ b/llvm/include/llvm/CodeGen/ValueTypes.h @@ -214,9 +214,7 @@ namespace llvm { } /// Return true if the bit size is a multiple of 8. - bool isByteSized() const { - return getSizeInBits().isByteSized(); - } + bool isByteSized() const { return getSizeInBits().isKnownMultipleOf(8); } /// Return true if the size is a power-of-two number of bytes. bool isRound() const { diff --git a/llvm/include/llvm/IR/Intrinsics.h b/llvm/include/llvm/IR/Intrinsics.h index f68c834e4cf6d1..7926dc5aa4a491 100644 --- a/llvm/include/llvm/IR/Intrinsics.h +++ b/llvm/include/llvm/IR/Intrinsics.h @@ -188,8 +188,7 @@ namespace Intrinsic { } static IITDescriptor getVector(unsigned Width, bool IsScalable) { - IITDescriptor Result; - Result.Kind = Vector; + IITDescriptor Result = {Vector, {0}}; Result.Vector_Width = ElementCount::get(Width, IsScalable); return Result; } diff --git a/llvm/include/llvm/Support/JSON.h b/llvm/include/llvm/Support/JSON.h index 455673e42e9700..9a8f915eeef79c 100644 --- a/llvm/include/llvm/Support/JSON.h +++ b/llvm/include/llvm/Support/JSON.h @@ -741,10 +741,9 @@ template Value toJSON(const llvm::Optional &Opt) { /// \code /// bool fromJSON(const Value &E, MyStruct &R, Path P) { /// ObjectMapper O(E, P); -/// if (!O || !O.map("mandatory_field", R.MandatoryField)) -/// return false; // error details are already reported -/// O.map("optional_field", R.OptionalField); -/// return true; +/// // When returning false, error details were already reported. +/// return O && O.map("mandatory_field", R.MandatoryField) && +/// O.mapOptional("optional_field", R.OptionalField); /// } /// \endcode class ObjectMapper { @@ -780,6 +779,16 @@ class ObjectMapper { return true; } + /// Maps a property to a field, if it exists. + /// If the property exists and is invalid, reports an error. + /// If the property does not exist, Out is unchanged. + template bool mapOptional(StringLiteral Prop, T &Out) { + assert(*this && "Must check this is an object before calling map()"); + if (const Value *E = O->get(Prop)) + return fromJSON(*E, Out, P.field(Prop)); + return true; + } + private: const Object *O; Path P; diff --git a/llvm/include/llvm/Support/MachineValueType.h b/llvm/include/llvm/Support/MachineValueType.h index 4361d68bcb8da3..33b0fe6750c626 100644 --- a/llvm/include/llvm/Support/MachineValueType.h +++ b/llvm/include/llvm/Support/MachineValueType.h @@ -980,9 +980,7 @@ namespace llvm { /// Returns true if the number of bits for the type is a multiple of an /// 8-bit byte. - bool isByteSized() const { - return getSizeInBits().isByteSized(); - } + bool isByteSized() const { return getSizeInBits().isKnownMultipleOf(8); } /// Return true if we know at compile time this has more bits than VT. bool knownBitsGT(MVT VT) const { diff --git a/llvm/include/llvm/Support/TypeSize.h b/llvm/include/llvm/Support/TypeSize.h index 47aa41b851bffb..47fb90d21c0bae 100644 --- a/llvm/include/llvm/Support/TypeSize.h +++ b/llvm/include/llvm/Support/TypeSize.h @@ -25,148 +25,58 @@ namespace llvm { template struct DenseMapInfo; -class ElementCount { -private: - unsigned Min; // Minimum number of vector elements. - bool Scalable; // If true, NumElements is a multiple of 'Min' determined - // at runtime rather than compile time. - - /// Prevent code from using initializer-list contructors like - /// ElementCount EC = {, }. The static `get*` - /// methods below are preferred, as users should always make a - /// conscious choice on the type of `ElementCount` they are - /// requesting. - ElementCount(unsigned Min, bool Scalable) : Min(Min), Scalable(Scalable) {} +// TODO: This class will be redesigned in a later patch that introduces full +// polynomial behaviour, i.e. the ability to have composites made up of both +// fixed and scalable sizes. +template class PolySize { +protected: + T MinVal; // The minimum value that it could be. + bool IsScalable; // If true, the total value is determined by multiplying + // 'MinVal' by a runtime determinded quantity, 'vscale'. + + constexpr PolySize(T MinVal, bool IsScalable) + : MinVal(MinVal), IsScalable(IsScalable) {} public: - ElementCount() = default; - ElementCount operator*(unsigned RHS) { - return { Min * RHS, Scalable }; + static constexpr PolySize getFixed(T MinVal) { return {MinVal, false}; } + static constexpr PolySize getScalable(T MinVal) { return {MinVal, true}; } + static constexpr PolySize get(T MinVal, bool IsScalable) { + return {MinVal, IsScalable}; } - friend ElementCount operator-(const ElementCount &LHS, - const ElementCount &RHS) { - assert(LHS.Scalable == RHS.Scalable && - "Arithmetic using mixed scalable and fixed types"); - return {LHS.Min - RHS.Min, LHS.Scalable}; - } - - bool operator==(const ElementCount& RHS) const { - return Min == RHS.Min && Scalable == RHS.Scalable; - } - bool operator!=(const ElementCount& RHS) const { - return !(*this == RHS); - } - bool operator==(unsigned RHS) const { return Min == RHS && !Scalable; } - bool operator!=(unsigned RHS) const { return !(*this == RHS); } - - ElementCount &operator*=(unsigned RHS) { - Min *= RHS; - return *this; - } - - /// We do not provide the '/' operator here because division for polynomial - /// types does not work in the same way as for normal integer types. We can - /// only divide the minimum value (or coefficient) by RHS, which is not the - /// same as - /// (Min * Vscale) / RHS - /// The caller is recommended to use this function in combination with - /// isKnownMultipleOf(RHS), which lets the caller know if it's possible to - /// perform a lossless divide by RHS. - ElementCount divideCoefficientBy(unsigned RHS) const { - return ElementCount(Min / RHS, Scalable); - } + static constexpr PolySize getNull() { return {0, false}; } - ElementCount NextPowerOf2() const { - return {(unsigned)llvm::NextPowerOf2(Min), Scalable}; - } - - /// This function tells the caller whether the element count is known at - /// compile time to be a multiple of the scalar value RHS. - bool isKnownMultipleOf(unsigned RHS) const { - return Min % RHS == 0; - } - - static ElementCount getFixed(unsigned Min) { return {Min, false}; } - static ElementCount getScalable(unsigned Min) { return {Min, true}; } - static ElementCount get(unsigned Min, bool Scalable) { - return {Min, Scalable}; - } - - /// Printing function. - void print(raw_ostream &OS) const { - if (Scalable) - OS << "vscale x "; - OS << Min; - } /// Counting predicates. /// - /// Notice that Min = 1 and Scalable = true is considered more than - /// one element. - /// ///@{ No elements.. - bool isZero() const { return Min == 0; } + bool isZero() const { return MinVal == 0; } /// At least one element. - bool isNonZero() const { return Min != 0; } + bool isNonZero() const { return !isZero(); } /// A return value of true indicates we know at compile time that the number /// of elements (vscale * Min) is definitely even. However, returning false /// does not guarantee that the total number of elements is odd. - bool isKnownEven() const { return (Min & 0x1) == 0; } - /// Exactly one element. - bool isScalar() const { return !Scalable && Min == 1; } - /// One or more elements. - bool isVector() const { return (Scalable && Min != 0) || Min > 1; } + bool isKnownEven() const { return (MinVal & 0x1) == 0; } ///@} - unsigned getKnownMinValue() const { return Min; } + T getKnownMinValue() const { return MinVal; } // Return the minimum value with the assumption that the count is exact. // Use in places where a scalable count doesn't make sense (e.g. non-vector // types, or vectors in backends which don't support scalable vectors). - unsigned getFixedValue() const { - assert(!Scalable && + T getFixedValue() const { + assert(!IsScalable && "Request for a fixed element count on a scalable object"); - return Min; - } - - bool isScalable() const { return Scalable; } -}; - -/// Stream operator function for `ElementCount`. -inline raw_ostream &operator<<(raw_ostream &OS, const ElementCount &EC) { - EC.print(OS); - return OS; -} - -// This class is used to represent the size of types. If the type is of fixed -// size, it will represent the exact size. If the type is a scalable vector, -// it will represent the known minimum size. -class TypeSize { - uint64_t MinSize; // The known minimum size. - bool IsScalable; // If true, then the runtime size is an integer multiple - // of MinSize. - -public: - constexpr TypeSize(uint64_t MinSize, bool Scalable) - : MinSize(MinSize), IsScalable(Scalable) {} - - static constexpr TypeSize Fixed(uint64_t Size) { - return TypeSize(Size, /*Scalable=*/false); + return MinVal; } - static constexpr TypeSize Scalable(uint64_t MinSize) { - return TypeSize(MinSize, /*Scalable=*/true); - } + bool isScalable() const { return IsScalable; } - // Scalable vector types with the same minimum size as a fixed size type are - // not guaranteed to be the same size at runtime, so they are never - // considered to be equal. - bool operator==(const TypeSize &RHS) const { - return MinSize == RHS.MinSize && IsScalable == RHS.IsScalable; + bool operator==(const PolySize &RHS) const { + return MinVal == RHS.MinVal && IsScalable == RHS.IsScalable; } - bool operator!=(const TypeSize &RHS) const { return !(*this == RHS); } + bool operator!=(const PolySize &RHS) const { return !(*this == RHS); } // For some cases, size ordering between scalable and fixed size types cannot // be determined at compile time, so such comparisons aren't allowed. @@ -178,43 +88,129 @@ class TypeSize { // All the functions below make use of the fact vscale is always >= 1, which // means that is guaranteed to be >= <4 x i32>, etc. - static bool isKnownLT(const TypeSize &LHS, const TypeSize &RHS) { + static bool isKnownLT(const PolySize &LHS, const PolySize &RHS) { if (!LHS.IsScalable || RHS.IsScalable) - return LHS.MinSize < RHS.MinSize; + return LHS.MinVal < RHS.MinVal; // LHS.IsScalable = true, RHS.IsScalable = false return false; } - static bool isKnownGT(const TypeSize &LHS, const TypeSize &RHS) { + static bool isKnownGT(const PolySize &LHS, const PolySize &RHS) { if (LHS.IsScalable || !RHS.IsScalable) - return LHS.MinSize > RHS.MinSize; + return LHS.MinVal > RHS.MinVal; // LHS.IsScalable = false, RHS.IsScalable = true return false; } - static bool isKnownLE(const TypeSize &LHS, const TypeSize &RHS) { + static bool isKnownLE(const PolySize &LHS, const PolySize &RHS) { if (!LHS.IsScalable || RHS.IsScalable) - return LHS.MinSize <= RHS.MinSize; + return LHS.MinVal <= RHS.MinVal; // LHS.IsScalable = true, RHS.IsScalable = false return false; } - static bool isKnownGE(const TypeSize &LHS, const TypeSize &RHS) { + static bool isKnownGE(const PolySize &LHS, const PolySize &RHS) { if (LHS.IsScalable || !RHS.IsScalable) - return LHS.MinSize >= RHS.MinSize; + return LHS.MinVal >= RHS.MinVal; // LHS.IsScalable = false, RHS.IsScalable = true return false; } + PolySize operator*(T RHS) { return {MinVal * RHS, IsScalable}; } + + PolySize &operator*=(T RHS) { + MinVal *= RHS; + return *this; + } + + friend PolySize operator-(const PolySize &LHS, const PolySize &RHS) { + assert(LHS.IsScalable == RHS.IsScalable && + "Arithmetic using mixed scalable and fixed types"); + return {LHS.MinVal - RHS.MinVal, LHS.IsScalable}; + } + + /// This function tells the caller whether the element count is known at + /// compile time to be a multiple of the scalar value RHS. + bool isKnownMultipleOf(T RHS) const { return MinVal % RHS == 0; } + + /// We do not provide the '/' operator here because division for polynomial + /// types does not work in the same way as for normal integer types. We can + /// only divide the minimum value (or coefficient) by RHS, which is not the + /// same as + /// (Min * Vscale) / RHS + /// The caller is recommended to use this function in combination with + /// isKnownMultipleOf(RHS), which lets the caller know if it's possible to + /// perform a lossless divide by RHS. + PolySize divideCoefficientBy(T RHS) const { + return PolySize(MinVal / RHS, IsScalable); + } + + PolySize coefficientNextPowerOf2() const { + return PolySize(static_cast(llvm::NextPowerOf2(MinVal)), IsScalable); + } + + /// Printing function. + void print(raw_ostream &OS) const { + if (IsScalable) + OS << "vscale x "; + OS << MinVal; + } +}; + +/// Stream operator function for `PolySize`. +template +inline raw_ostream &operator<<(raw_ostream &OS, const PolySize &PS) { + PS.print(OS); + return OS; +} + +class ElementCount : public PolySize { +public: + + constexpr ElementCount(PolySize V) : PolySize(V) {} + + /// Counting predicates. + /// + /// Notice that MinVal = 1 and IsScalable = true is considered more than + /// one element. + /// + ///@{ No elements.. + /// Exactly one element. + bool isScalar() const { return !IsScalable && MinVal == 1; } + /// One or more elements. + bool isVector() const { return (IsScalable && MinVal != 0) || MinVal > 1; } + ///@} +}; + +// This class is used to represent the size of types. If the type is of fixed +// size, it will represent the exact size. If the type is a scalable vector, +// it will represent the known minimum size. +class TypeSize : public PolySize { +public: + constexpr TypeSize(PolySize V) : PolySize(V) {} + + constexpr TypeSize(uint64_t MinVal, bool IsScalable) + : PolySize(MinVal, IsScalable) {} + + static constexpr TypeSize Fixed(uint64_t MinVal) { + return TypeSize(MinVal, false); + } + static constexpr TypeSize Scalable(uint64_t MinVal) { + return TypeSize(MinVal, true); + } + + uint64_t getFixedSize() const { return getFixedValue(); } + uint64_t getKnownMinSize() const { return getKnownMinValue(); } + friend bool operator<(const TypeSize &LHS, const TypeSize &RHS) { assert(LHS.IsScalable == RHS.IsScalable && "Ordering comparison of scalable and fixed types"); - return LHS.MinSize < RHS.MinSize; + return LHS.MinVal < RHS.MinVal; } friend bool operator>(const TypeSize &LHS, const TypeSize &RHS) { @@ -229,83 +225,26 @@ class TypeSize { return !(LHS < RHS); } - // Convenience operators to obtain relative sizes independently of - // the scalable flag. - TypeSize operator*(unsigned RHS) const { - return { MinSize * RHS, IsScalable }; - } - - friend TypeSize operator*(const unsigned LHS, const TypeSize &RHS) { - return { LHS * RHS.MinSize, RHS.IsScalable }; - } - - /// We do not provide the '/' operator here because division for polynomial - /// types does not work in the same way as for normal integer types. We can - /// only divide the minimum value (or coefficient) by RHS, which is not the - /// same as - /// (MinSize * Vscale) / RHS - /// The caller is recommended to use this function in combination with - /// isKnownMultipleOf(RHS), which lets the caller know if it's possible to - /// perform a lossless divide by RHS. - TypeSize divideCoefficientBy(uint64_t RHS) const { - return {MinSize / RHS, IsScalable}; - } - TypeSize &operator-=(TypeSize RHS) { assert(IsScalable == RHS.IsScalable && "Subtraction using mixed scalable and fixed types"); - MinSize -= RHS.MinSize; + MinVal -= RHS.MinVal; return *this; } TypeSize &operator+=(TypeSize RHS) { assert(IsScalable == RHS.IsScalable && "Addition using mixed scalable and fixed types"); - MinSize += RHS.MinSize; + MinVal += RHS.MinVal; return *this; } friend TypeSize operator-(const TypeSize &LHS, const TypeSize &RHS) { assert(LHS.IsScalable == RHS.IsScalable && "Arithmetic using mixed scalable and fixed types"); - return {LHS.MinSize - RHS.MinSize, LHS.IsScalable}; - } - - // Return the minimum size with the assumption that the size is exact. - // Use in places where a scalable size doesn't make sense (e.g. non-vector - // types, or vectors in backends which don't support scalable vectors). - uint64_t getFixedSize() const { - assert(!IsScalable && "Request for a fixed size on a scalable object"); - return MinSize; - } - - // Return the known minimum size. Use in places where the scalable property - // doesn't matter (e.g. determining alignment) or in conjunction with the - // isScalable method below. - uint64_t getKnownMinSize() const { - return MinSize; - } - - // Return whether or not the size is scalable. - bool isScalable() const { - return IsScalable; + return {LHS.MinVal - RHS.MinVal, LHS.IsScalable}; } - // Returns true if the number of bits is a multiple of an 8-bit byte. - bool isByteSized() const { - return (MinSize & 7) == 0; - } - - // Returns true if the type size is non-zero. - bool isNonZero() const { return MinSize != 0; } - - // Returns true if the type size is zero. - bool isZero() const { return MinSize == 0; } - - /// This function tells the caller whether the type size is known at - /// compile time to be a multiple of the scalar value RHS. - bool isKnownMultipleOf(uint64_t RHS) const { return MinSize % RHS == 0; } - // Casts to a uint64_t if this is a fixed-width size. // // This interface is deprecated and will be removed in a future version @@ -317,53 +256,51 @@ class TypeSize { // To determine how to upgrade the code: // // if () - // use getKnownMinSize() + // use getKnownMinValue() // else if () { // if - // update the algorithm and use getKnownMinSize() + // update the algorithm and use getKnownMinValue() // else - // bail out early for scalable vectors and use getFixedSize() + // bail out early for scalable vectors and use getFixedValue() // } operator uint64_t() const { #ifdef STRICT_FIXED_SIZE_VECTORS - return getFixedSize(); + return getFixedValue(); #else if (isScalable()) WithColor::warning() << "Compiler has made implicit assumption that " "TypeSize is not scalable. This may or may not " "lead to broken code.\n"; - return getKnownMinSize(); + return getKnownMinValue(); #endif } + // Convenience operators to obtain relative sizes independently of + // the scalable flag. + TypeSize operator*(unsigned RHS) const { return {MinVal * RHS, IsScalable}; } + + friend TypeSize operator*(const unsigned LHS, const TypeSize &RHS) { + return {LHS * RHS.MinVal, RHS.IsScalable}; + } + // Additional convenience operators needed to avoid ambiguous parses. // TODO: Make uint64_t the default operator? - TypeSize operator*(uint64_t RHS) const { - return { MinSize * RHS, IsScalable }; - } + TypeSize operator*(uint64_t RHS) const { return {MinVal * RHS, IsScalable}; } - TypeSize operator*(int RHS) const { - return { MinSize * RHS, IsScalable }; - } + TypeSize operator*(int RHS) const { return {MinVal * RHS, IsScalable}; } - TypeSize operator*(int64_t RHS) const { - return { MinSize * RHS, IsScalable }; - } + TypeSize operator*(int64_t RHS) const { return {MinVal * RHS, IsScalable}; } friend TypeSize operator*(const uint64_t LHS, const TypeSize &RHS) { - return { LHS * RHS.MinSize, RHS.IsScalable }; + return {LHS * RHS.MinVal, RHS.IsScalable}; } friend TypeSize operator*(const int LHS, const TypeSize &RHS) { - return { LHS * RHS.MinSize, RHS.IsScalable }; + return {LHS * RHS.MinVal, RHS.IsScalable}; } friend TypeSize operator*(const int64_t LHS, const TypeSize &RHS) { - return { LHS * RHS.MinSize, RHS.IsScalable }; - } - - TypeSize NextPowerOf2() const { - return TypeSize(llvm::NextPowerOf2(MinSize), IsScalable); + return {LHS * RHS.MinVal, RHS.IsScalable}; } }; @@ -374,7 +311,7 @@ class TypeSize { /// Similar to the alignTo functions in MathExtras.h inline TypeSize alignTo(TypeSize Size, uint64_t Align) { assert(Align != 0u && "Align must be non-zero"); - return {(Size.getKnownMinSize() + Align - 1) / Align * Align, + return {(Size.getKnownMinValue() + Align - 1) / Align * Align, Size.isScalable()}; } diff --git a/llvm/lib/Analysis/ScalarEvolution.cpp b/llvm/lib/Analysis/ScalarEvolution.cpp index 899fb44a776997..036d80649110aa 100644 --- a/llvm/lib/Analysis/ScalarEvolution.cpp +++ b/llvm/lib/Analysis/ScalarEvolution.cpp @@ -3505,15 +3505,15 @@ const SCEV *ScalarEvolution::getUMinExpr(SmallVectorImpl &Ops) { } const SCEV *ScalarEvolution::getSizeOfExpr(Type *IntTy, Type *AllocTy) { - // We can bypass creating a target-independent - // constant expression and then folding it back into a ConstantInt. - // This is just a compile-time optimization. if (isa(AllocTy)) { Constant *NullPtr = Constant::getNullValue(AllocTy->getPointerTo()); Constant *One = ConstantInt::get(IntTy, 1); Constant *GEP = ConstantExpr::getGetElementPtr(AllocTy, NullPtr, One); - return getSCEV(ConstantExpr::getPtrToInt(GEP, IntTy)); + return getUnknown(ConstantExpr::getPtrToInt(GEP, IntTy)); } + // We can bypass creating a target-independent + // constant expression and then folding it back into a ConstantInt. + // This is just a compile-time optimization. return getConstant(IntTy, getDataLayout().getTypeAllocSize(AllocTy)); } @@ -5500,16 +5500,14 @@ ScalarEvolution::getRangeRef(const SCEV *S, auto RangeFromAffine = getRangeForAffineAR( AddRec->getStart(), AddRec->getStepRecurrence(*this), MaxBECount, BitWidth); - if (!RangeFromAffine.isFullSet()) - ConservativeResult = - ConservativeResult.intersectWith(RangeFromAffine, RangeType); + ConservativeResult = + ConservativeResult.intersectWith(RangeFromAffine, RangeType); auto RangeFromFactoring = getRangeViaFactoring( AddRec->getStart(), AddRec->getStepRecurrence(*this), MaxBECount, BitWidth); - if (!RangeFromFactoring.isFullSet()) - ConservativeResult = - ConservativeResult.intersectWith(RangeFromFactoring, RangeType); + ConservativeResult = + ConservativeResult.intersectWith(RangeFromFactoring, RangeType); } } @@ -6303,6 +6301,36 @@ const SCEV *ScalarEvolution::createSCEV(Value *V) { return getSCEV(U->getOperand(0)); break; + case Instruction::PtrToInt: { + // It's tempting to handle inttoptr and ptrtoint as no-ops, + // however this can lead to pointer expressions which cannot safely be + // expanded to GEPs because ScalarEvolution doesn't respect + // the GEP aliasing rules when simplifying integer expressions. + // + // However, given + // %x = ??? + // %y = ptrtoint %x + // %z = ptrtoint %x + // it is safe to say that %y and %z are the same thing. + // + // So instead of modelling the cast itself as unknown, + // since the casts are transparent within SCEV, + // we can at least model the casts original value as unknow instead. + + // BUT, there's caveat. If we simply model %x as unknown, unrelated uses + // of %x will also see it as unknown, which is obviously bad. + // So we can only do this iff %x would be modelled as unknown anyways. + auto *OpSCEV = getSCEV(U->getOperand(0)); + if (isa(OpSCEV)) + return getTruncateOrZeroExtend(OpSCEV, U->getType()); + // If we can model the operand, however, we must fallback to modelling + // the whole cast as unknown instead. + LLVM_FALLTHROUGH; + } + case Instruction::IntToPtr: + // We can't do this for inttoptr at all, however. + return getUnknown(V); + case Instruction::SDiv: // If both operands are non-negative, this is just an udiv. if (isKnownNonNegative(getSCEV(U->getOperand(0))) && @@ -6317,11 +6345,6 @@ const SCEV *ScalarEvolution::createSCEV(Value *V) { return getURemExpr(getSCEV(U->getOperand(0)), getSCEV(U->getOperand(1))); break; - // It's tempting to handle inttoptr and ptrtoint as no-ops, however this can - // lead to pointer expressions which cannot safely be expanded to GEPs, - // because ScalarEvolution doesn't respect the GEP aliasing rules when - // simplifying integer expressions. - case Instruction::GetElementPtr: return createNodeForGEP(cast(U)); diff --git a/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp b/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp index 65a5c79115035b..eab6bc39b1c6a5 100644 --- a/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp +++ b/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp @@ -19573,7 +19573,7 @@ SDValue DAGCombiner::visitEXTRACT_SUBVECTOR(SDNode *N) { V.getOperand(0), NewIndex); return DAG.getBitcast(NVT, NewExtract); } - if (NewExtEC == 1 && + if (NewExtEC.isScalar() && TLI.isOperationLegalOrCustom(ISD::EXTRACT_VECTOR_ELT, ScalarVT)) { SDValue NewIndex = DAG.getVectorIdxConstant(IndexValScaled, DL); SDValue NewExtract = diff --git a/llvm/lib/CodeGen/SelectionDAG/LegalizeIntegerTypes.cpp b/llvm/lib/CodeGen/SelectionDAG/LegalizeIntegerTypes.cpp index 0000fcb1dde1bb..8c2efe89d6f110 100644 --- a/llvm/lib/CodeGen/SelectionDAG/LegalizeIntegerTypes.cpp +++ b/llvm/lib/CodeGen/SelectionDAG/LegalizeIntegerTypes.cpp @@ -1129,27 +1129,44 @@ SDValue DAGTypeLegalizer::PromoteIntRes_FunnelShift(SDNode *N) { SDValue Lo = GetPromotedInteger(N->getOperand(1)); SDValue Amount = GetPromotedInteger(N->getOperand(2)); - unsigned OldBits = N->getOperand(0).getScalarValueSizeInBits(); - unsigned NewBits = Hi.getScalarValueSizeInBits(); - - // Shift Lo up to occupy the upper bits of the promoted type. SDLoc DL(N); + EVT OldVT = N->getOperand(0).getValueType(); EVT VT = Lo.getValueType(); - Lo = DAG.getNode(ISD::SHL, DL, VT, Lo, - DAG.getConstant(NewBits - OldBits, DL, VT)); + unsigned Opcode = N->getOpcode(); + bool IsFSHR = Opcode == ISD::FSHR; + unsigned OldBits = OldVT.getScalarSizeInBits(); + unsigned NewBits = VT.getScalarSizeInBits(); // Amount has to be interpreted modulo the old bit width. Amount = DAG.getNode(ISD::UREM, DL, VT, Amount, DAG.getConstant(OldBits, DL, VT)); - unsigned Opcode = N->getOpcode(); - if (Opcode == ISD::FSHR) { - // Increase Amount to shift the result into the lower bits of the promoted - // type. - Amount = DAG.getNode(ISD::ADD, DL, VT, Amount, - DAG.getConstant(NewBits - OldBits, DL, VT)); + // If the promoted type is twice the size (or more), then we use the + // traditional funnel 'double' shift codegen. This isn't necessary if the + // shift amount is constant. + // fshl(x,y,z) -> (((aext(x) << bw) | zext(y)) << (z % bw)) >> bw. + // fshr(x,y,z) -> (((aext(x) << bw) | zext(y)) >> (z % bw)). + if (NewBits >= (2 * OldBits) && !isa(Amount) && + !TLI.isOperationLegalOrCustom(Opcode, VT)) { + SDValue HiShift = DAG.getConstant(OldBits, DL, VT); + Hi = DAG.getNode(ISD::SHL, DL, VT, Hi, HiShift); + Lo = DAG.getZeroExtendInReg(Lo, DL, OldVT); + SDValue Res = DAG.getNode(ISD::OR, DL, VT, Hi, Lo); + Res = DAG.getNode(IsFSHR ? ISD::SRL : ISD::SHL, DL, VT, Res, Amount); + if (!IsFSHR) + Res = DAG.getNode(ISD::SRL, DL, VT, Res, HiShift); + return Res; } + // Shift Lo up to occupy the upper bits of the promoted type. + SDValue ShiftOffset = DAG.getConstant(NewBits - OldBits, DL, VT); + Lo = DAG.getNode(ISD::SHL, DL, VT, Lo, ShiftOffset); + + // Increase Amount to shift the result into the lower bits of the promoted + // type. + if (IsFSHR) + Amount = DAG.getNode(ISD::ADD, DL, VT, Amount, ShiftOffset); + return DAG.getNode(Opcode, DL, VT, Hi, Lo, Amount); } diff --git a/llvm/lib/CodeGen/TargetLoweringBase.cpp b/llvm/lib/CodeGen/TargetLoweringBase.cpp index ead52b80345927..84ff390126a14e 100644 --- a/llvm/lib/CodeGen/TargetLoweringBase.cpp +++ b/llvm/lib/CodeGen/TargetLoweringBase.cpp @@ -862,7 +862,7 @@ TargetLoweringBase::getTypeConversion(LLVMContext &Context, EVT VT) const { EVT EltVT = VT.getVectorElementType(); // Vectors with only one element are always scalarized. - if (NumElts == 1) + if (NumElts.isScalar()) return LegalizeKind(TypeScalarizeVector, EltVT); if (VT.getVectorElementCount() == ElementCount::getScalable(1)) @@ -875,7 +875,7 @@ TargetLoweringBase::getTypeConversion(LLVMContext &Context, EVT VT) const { // Vectors with a number of elements that is not a power of two are always // widened, for example <3 x i8> -> <4 x i8>. if (!VT.isPow2VectorType()) { - NumElts = NumElts.NextPowerOf2(); + NumElts = NumElts.coefficientNextPowerOf2(); EVT NVT = EVT::getVectorVT(Context, EltVT, NumElts); return LegalizeKind(TypeWidenVector, NVT); } @@ -924,7 +924,7 @@ TargetLoweringBase::getTypeConversion(LLVMContext &Context, EVT VT) const { // If there is no wider legal type, split the vector. while (true) { // Round up to the next power of 2. - NumElts = NumElts.NextPowerOf2(); + NumElts = NumElts.coefficientNextPowerOf2(); // If there is no simple vector type with this many elements then there // cannot be a larger legal vector type. Note that this assumes that @@ -1499,7 +1499,7 @@ unsigned TargetLoweringBase::getVectorTypeBreakdown(LLVMContext &Context, EVT VT TypeSize NewVTSize = NewVT.getSizeInBits(); // Convert sizes such as i33 to i64. if (!isPowerOf2_32(NewVTSize.getKnownMinSize())) - NewVTSize = NewVTSize.NextPowerOf2(); + NewVTSize = NewVTSize.coefficientNextPowerOf2(); return NumVectorRegs*(NewVTSize/DestVT.getSizeInBits()); } diff --git a/llvm/lib/Support/LowLevelType.cpp b/llvm/lib/Support/LowLevelType.cpp index fe77cb3db4139a..63559d5ac3eee8 100644 --- a/llvm/lib/Support/LowLevelType.cpp +++ b/llvm/lib/Support/LowLevelType.cpp @@ -23,7 +23,7 @@ LLT::LLT(MVT VT) { } else if (VT.isValid()) { // Aggregates are no different from real scalars as far as GlobalISel is // concerned. - assert(VT.getSizeInBits() != 0 && "invalid zero-sized type"); + assert(VT.getSizeInBits().isNonZero() && "invalid zero-sized type"); init(/*IsPointer=*/false, /*IsVector=*/false, /*NumElements=*/0, VT.getSizeInBits(), /*AddressSpace=*/0); } else { diff --git a/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp b/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp index c2972ab4f2f7c9..b1c4903cb5e307 100644 --- a/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp +++ b/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp @@ -1114,24 +1114,16 @@ AArch64TargetLowering::AArch64TargetLowering(const TargetMachine &TM, setOperationAction(ISD::UMAX, MVT::v2i64, Custom); setOperationAction(ISD::UMIN, MVT::v1i64, Custom); setOperationAction(ISD::UMIN, MVT::v2i64, Custom); - setOperationAction(ISD::VECREDUCE_AND, MVT::v8i8, Custom); - setOperationAction(ISD::VECREDUCE_AND, MVT::v16i8, Custom); - setOperationAction(ISD::VECREDUCE_AND, MVT::v4i16, Custom); - setOperationAction(ISD::VECREDUCE_AND, MVT::v8i16, Custom); - setOperationAction(ISD::VECREDUCE_AND, MVT::v2i32, Custom); - setOperationAction(ISD::VECREDUCE_AND, MVT::v4i32, Custom); - setOperationAction(ISD::VECREDUCE_AND, MVT::v2i64, Custom); - setOperationAction(ISD::VECREDUCE_OR, MVT::v8i8, Custom); - setOperationAction(ISD::VECREDUCE_OR, MVT::v16i8, Custom); - setOperationAction(ISD::VECREDUCE_OR, MVT::v4i16, Custom); - setOperationAction(ISD::VECREDUCE_OR, MVT::v8i16, Custom); - setOperationAction(ISD::VECREDUCE_OR, MVT::v2i32, Custom); - setOperationAction(ISD::VECREDUCE_OR, MVT::v4i32, Custom); - setOperationAction(ISD::VECREDUCE_OR, MVT::v2i64, Custom); setOperationAction(ISD::VECREDUCE_SMAX, MVT::v2i64, Custom); setOperationAction(ISD::VECREDUCE_SMIN, MVT::v2i64, Custom); setOperationAction(ISD::VECREDUCE_UMAX, MVT::v2i64, Custom); setOperationAction(ISD::VECREDUCE_UMIN, MVT::v2i64, Custom); + for (auto VT : {MVT::v8i8, MVT::v16i8, MVT::v4i16, MVT::v8i16, + MVT::v2i32, MVT::v4i32, MVT::v2i64}) { + setOperationAction(ISD::VECREDUCE_AND, VT, Custom); + setOperationAction(ISD::VECREDUCE_OR, VT, Custom); + setOperationAction(ISD::VECREDUCE_XOR, VT, Custom); + } } } @@ -1275,6 +1267,7 @@ void AArch64TargetLowering::addTypeForFixedLengthSVE(MVT VT) { setOperationAction(ISD::VECREDUCE_SMIN, VT, Custom); setOperationAction(ISD::VECREDUCE_UMAX, VT, Custom); setOperationAction(ISD::VECREDUCE_UMIN, VT, Custom); + setOperationAction(ISD::VECREDUCE_XOR, VT, Custom); setOperationAction(ISD::VSELECT, VT, Custom); setOperationAction(ISD::XOR, VT, Custom); setOperationAction(ISD::ZERO_EXTEND, VT, Custom); @@ -3953,6 +3946,7 @@ SDValue AArch64TargetLowering::LowerOperation(SDValue Op, case ISD::VECREDUCE_ADD: case ISD::VECREDUCE_AND: case ISD::VECREDUCE_OR: + case ISD::VECREDUCE_XOR: case ISD::VECREDUCE_SMAX: case ISD::VECREDUCE_SMIN: case ISD::VECREDUCE_UMAX: @@ -9742,6 +9736,7 @@ SDValue AArch64TargetLowering::LowerVECREDUCE(SDValue Op, EVT SrcVT = Src.getValueType(); bool OverrideNEON = Op.getOpcode() == ISD::VECREDUCE_AND || Op.getOpcode() == ISD::VECREDUCE_OR || + Op.getOpcode() == ISD::VECREDUCE_XOR || (Op.getOpcode() != ISD::VECREDUCE_ADD && SrcVT.getVectorElementType() == MVT::i64); if (useSVEForFixedLengthVectorVT(SrcVT, OverrideNEON)) { @@ -9760,6 +9755,8 @@ SDValue AArch64TargetLowering::LowerVECREDUCE(SDValue Op, return LowerFixedLengthReductionToSVE(AArch64ISD::UMAXV_PRED, Op, DAG); case ISD::VECREDUCE_UMIN: return LowerFixedLengthReductionToSVE(AArch64ISD::UMINV_PRED, Op, DAG); + case ISD::VECREDUCE_XOR: + return LowerFixedLengthReductionToSVE(AArch64ISD::EORV_PRED, Op, DAG); case ISD::VECREDUCE_FMAX: return LowerFixedLengthReductionToSVE(AArch64ISD::FMAXNMV_PRED, Op, DAG); case ISD::VECREDUCE_FMIN: diff --git a/llvm/lib/Target/VE/VEISelLowering.cpp b/llvm/lib/Target/VE/VEISelLowering.cpp index 79975f82f69468..565ad7ecd53d37 100644 --- a/llvm/lib/Target/VE/VEISelLowering.cpp +++ b/llvm/lib/Target/VE/VEISelLowering.cpp @@ -679,6 +679,11 @@ VETargetLowering::VETargetLowering(const TargetMachine &TM, setOperationAction(ISD::DYNAMIC_STACKALLOC, MVT::i64, Custom); /// } Stack + /// Branch { + // VE doesn't have BRCOND + setOperationAction(ISD::BRCOND, MVT::Other, Expand); + /// } Branch + /// Int Ops { for (MVT IntVT : {MVT::i32, MVT::i64}) { // VE has no REM or DIVREM operations. @@ -735,6 +740,14 @@ VETargetLowering::VETargetLowering(const TargetMachine &TM, /// } Conversion /// Floating-point Ops { + /// Note: Floating-point operations are fneg, fadd, fsub, fmul, fdiv, frem, + /// and fcmp. + + // VE doesn't have following floating point operations. + for (MVT VT : MVT::fp_valuetypes()) { + setOperationAction(ISD::FNEG, VT, Expand); + setOperationAction(ISD::FREM, VT, Expand); + } // VE doesn't have fdiv of f128. setOperationAction(ISD::FDIV, MVT::f128, Expand); @@ -745,6 +758,15 @@ VETargetLowering::VETargetLowering(const TargetMachine &TM, } /// } Floating-point Ops + /// Floating-point math functions { + + // VE doesn't have following floating point math functions. + for (MVT VT : MVT::fp_valuetypes()) { + setOperationAction(ISD::FCOPYSIGN, VT, Expand); + } + + /// } Floating-point math functions + setStackPointerRegisterToSaveRestore(VE::SX11); // We have target-specific dag combine patterns for the following nodes: diff --git a/llvm/lib/Target/X86/X86InterleavedAccess.cpp b/llvm/lib/Target/X86/X86InterleavedAccess.cpp index a19e12766e101a..866c53235db42b 100644 --- a/llvm/lib/Target/X86/X86InterleavedAccess.cpp +++ b/llvm/lib/Target/X86/X86InterleavedAccess.cpp @@ -211,7 +211,7 @@ void X86InterleavedAccessGroup::decompose( VecBasePtr = Builder.CreateBitCast(LI->getPointerOperand(), VecBasePtrTy); } // Generate N loads of T type. - assert(VecBaseTy->getPrimitiveSizeInBits().isByteSized() && + assert(VecBaseTy->getPrimitiveSizeInBits().isKnownMultipleOf(8) && "VecBaseTy's size must be a multiple of 8"); const Align FirstAlignment = LI->getAlign(); const Align SubsequentAlignment = commonAlignment( diff --git a/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp b/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp index 227a0ab649041a..fb415bf20d1a3e 100644 --- a/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp +++ b/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp @@ -2067,13 +2067,19 @@ static Instruction *matchFunnelShift(Instruction &Or, InstCombinerImpl &IC) { Value *ShVal0, *ShVal1, *ShAmt0, *ShAmt1; if (!match(Or0, m_OneUse(m_LogicalShift(m_Value(ShVal0), m_Value(ShAmt0)))) || - !match(Or1, m_OneUse(m_LogicalShift(m_Value(ShVal1), m_Value(ShAmt1))))) + !match(Or1, m_OneUse(m_LogicalShift(m_Value(ShVal1), m_Value(ShAmt1)))) || + Or0->getOpcode() == Or1->getOpcode()) return nullptr; - BinaryOperator::BinaryOps ShiftOpcode0 = Or0->getOpcode(); - BinaryOperator::BinaryOps ShiftOpcode1 = Or1->getOpcode(); - if (ShiftOpcode0 == ShiftOpcode1) - return nullptr; + // Canonicalize to or(shl(ShVal0, ShAmt0), lshr(ShVal1, ShAmt1)). + if (Or0->getOpcode() == BinaryOperator::LShr) { + std::swap(Or0, Or1); + std::swap(ShVal0, ShVal1); + std::swap(ShAmt0, ShAmt1); + } + assert(Or0->getOpcode() == BinaryOperator::Shl && + Or1->getOpcode() == BinaryOperator::LShr && + "Illegal or(shift,shift) pair"); // Match the shift amount operands for a funnel shift pattern. This always // matches a subtraction on the R operand. @@ -2134,20 +2140,17 @@ static Instruction *matchFunnelShift(Instruction &Or, InstCombinerImpl &IC) { }; Value *ShAmt = matchShiftAmount(ShAmt0, ShAmt1, Width); - bool SubIsOnLHS = false; + bool IsFshl = true; // Sub on LSHR. if (!ShAmt) { ShAmt = matchShiftAmount(ShAmt1, ShAmt0, Width); - SubIsOnLHS = true; + IsFshl = false; // Sub on SHL. } if (!ShAmt) return nullptr; - bool IsFshl = (!SubIsOnLHS && ShiftOpcode0 == BinaryOperator::Shl) || - (SubIsOnLHS && ShiftOpcode1 == BinaryOperator::Shl); Intrinsic::ID IID = IsFshl ? Intrinsic::fshl : Intrinsic::fshr; Function *F = Intrinsic::getDeclaration(Or.getModule(), IID, Or.getType()); - return IntrinsicInst::Create( - F, {IsFshl ? ShVal0 : ShVal1, IsFshl ? ShVal1 : ShVal0, ShAmt}); + return IntrinsicInst::Create(F, {ShVal0, ShVal1, ShAmt}); } /// Attempt to combine or(zext(x),shl(zext(y),bw/2) concat packing patterns. diff --git a/llvm/lib/Transforms/Utils/SimplifyIndVar.cpp b/llvm/lib/Transforms/Utils/SimplifyIndVar.cpp index 2d71b0fff88940..3e280a66175c88 100644 --- a/llvm/lib/Transforms/Utils/SimplifyIndVar.cpp +++ b/llvm/lib/Transforms/Utils/SimplifyIndVar.cpp @@ -427,7 +427,7 @@ static bool willNotOverflow(ScalarEvolution *SE, Instruction::BinaryOps BinOp, : &ScalarEvolution::getZeroExtendExpr; // Check ext(LHS op RHS) == ext(LHS) op ext(RHS) - auto *NarrowTy = cast(LHS->getType()); + auto *NarrowTy = cast(SE->getEffectiveSCEVType(LHS->getType())); auto *WideTy = IntegerType::get(NarrowTy->getContext(), NarrowTy->getBitWidth() * 2); diff --git a/llvm/lib/Transforms/Vectorize/LoopVectorize.cpp b/llvm/lib/Transforms/Vectorize/LoopVectorize.cpp index d0539cca27b82e..95d55d062da03c 100644 --- a/llvm/lib/Transforms/Vectorize/LoopVectorize.cpp +++ b/llvm/lib/Transforms/Vectorize/LoopVectorize.cpp @@ -353,7 +353,9 @@ static bool hasIrregularType(Type *Ty, const DataLayout &DL, ElementCount VF) { // with a vector. if (VF.isVector()) { auto *VectorTy = VectorType::get(Ty, VF); - return VF * DL.getTypeAllocSize(Ty) != DL.getTypeStoreSize(VectorTy); + return TypeSize::get(VF.getKnownMinValue() * + DL.getTypeAllocSize(Ty).getFixedValue(), + VF.isScalable()) != DL.getTypeStoreSize(VectorTy); } // If the vectorization factor is one, we just check if an array of type Ty @@ -2166,7 +2168,7 @@ Value *InnerLoopVectorizer::getOrCreateVectorValue(Value *V, unsigned Part) { // If we aren't vectorizing, we can just copy the scalar map values over to // the vector map. - if (VF == 1) { + if (VF.isScalar()) { VectorLoopValueMap.setVectorValue(V, Part, ScalarValue); return ScalarValue; } @@ -2242,7 +2244,7 @@ InnerLoopVectorizer::getOrCreateScalarValue(Value *V, // extractelement instruction. auto *U = getOrCreateVectorValue(V, Instance.Part); if (!U->getType()->isVectorTy()) { - assert(VF == 1 && "Value not scalarized has non-vector type"); + assert(VF.isScalar() && "Value not scalarized has non-vector type"); return U; } @@ -3933,7 +3935,7 @@ void InnerLoopVectorizer::fixReduction(PHINode *Phi) { if (RK == RecurrenceDescriptor::RK_IntegerMinMax || RK == RecurrenceDescriptor::RK_FloatMinMax) { // MinMax reduction have the start value as their identify. - if (VF == 1 || IsInLoopReductionPhi) { + if (VF.isScalar() || IsInLoopReductionPhi) { VectorStart = Identity = ReductionStartValue; } else { VectorStart = Identity = @@ -3943,7 +3945,7 @@ void InnerLoopVectorizer::fixReduction(PHINode *Phi) { // Handle other reduction kinds: Constant *Iden = RecurrenceDescriptor::getRecurrenceIdentity( RK, MinMaxKind, VecTy->getScalarType()); - if (VF == 1 || IsInLoopReductionPhi) { + if (VF.isScalar() || IsInLoopReductionPhi) { Identity = Iden; // This vector is the Identity vector where the first element is the // incoming scalar reduction. @@ -4343,7 +4345,7 @@ void InnerLoopVectorizer::widenGEP(GetElementPtrInst *GEP, VPUser &Operands, ? Builder.CreateInBoundsGEP(GEP->getSourceElementType(), Ptr, Indices) : Builder.CreateGEP(GEP->getSourceElementType(), Ptr, Indices); - assert((VF == 1 || NewGEP->getType()->isVectorTy()) && + assert((VF.isScalar() || NewGEP->getType()->isVectorTy()) && "NewGEP is not a pointer vector"); VectorLoopValueMap.setVectorValue(GEP, Part, NewGEP); addMetadata(NewGEP, GEP); @@ -8413,7 +8415,7 @@ bool LoopVectorizePass::processLoop(Loop *L) { return false; } - if (VF.Width == 1) { + if (VF.Width.isScalar()) { LLVM_DEBUG(dbgs() << "LV: Vectorization is possible but not beneficial.\n"); VecDiagMsg = std::make_pair( "VectorizationNotBeneficial", diff --git a/llvm/lib/Transforms/Vectorize/VPlan.cpp b/llvm/lib/Transforms/Vectorize/VPlan.cpp index 852e9454cf0745..a009393d029cc4 100644 --- a/llvm/lib/Transforms/Vectorize/VPlan.cpp +++ b/llvm/lib/Transforms/Vectorize/VPlan.cpp @@ -536,7 +536,7 @@ void VPlan::execute(VPTransformState *State) { "trip.count.minus.1"); auto VF = State->VF; Value *VTCMO = - VF == 1 ? TCMO : Builder.CreateVectorSplat(VF, TCMO, "broadcast"); + VF.isScalar() ? TCMO : Builder.CreateVectorSplat(VF, TCMO, "broadcast"); for (unsigned Part = 0, UF = State->UF; Part < UF; ++Part) State->set(BackedgeTakenCount, VTCMO, Part); } @@ -930,7 +930,8 @@ void VPWidenCanonicalIVRecipe::execute(VPTransformState &State) { ConstantInt::get(STy, Part * VF.getKnownMinValue() + Lane)); // If VF == 1, there is only one iteration in the loop above, thus the // element pushed back into Indices is ConstantInt::get(STy, Part) - Constant *VStep = VF == 1 ? Indices.back() : ConstantVector::get(Indices); + Constant *VStep = + VF.isScalar() ? Indices.back() : ConstantVector::get(Indices); // Add the consecutive indices to the vector value. Value *CanonicalVectorIV = Builder.CreateAdd(VStart, VStep, "vec.iv"); State.set(getVPValue(), CanonicalVectorIV, Part); diff --git a/llvm/test/Analysis/ScalarEvolution/add-expr-pointer-operand-sorting.ll b/llvm/test/Analysis/ScalarEvolution/add-expr-pointer-operand-sorting.ll index 93a3bf4d4c3786..e798e2715ba1da 100644 --- a/llvm/test/Analysis/ScalarEvolution/add-expr-pointer-operand-sorting.ll +++ b/llvm/test/Analysis/ScalarEvolution/add-expr-pointer-operand-sorting.ll @@ -33,9 +33,9 @@ define i32 @d(i32 %base) { ; CHECK-NEXT: %1 = load i32*, i32** @c, align 8 ; CHECK-NEXT: --> %1 U: full-set S: full-set Exits: <> LoopDispositions: { %for.cond: Variant } ; CHECK-NEXT: %sub.ptr.lhs.cast = ptrtoint i32* %1 to i64 -; CHECK-NEXT: --> %sub.ptr.lhs.cast U: full-set S: full-set Exits: <> LoopDispositions: { %for.cond: Variant } +; CHECK-NEXT: --> %1 U: full-set S: full-set Exits: <> LoopDispositions: { %for.cond: Variant } ; CHECK-NEXT: %sub.ptr.sub = sub i64 %sub.ptr.lhs.cast, ptrtoint ([1 x i32]* @b to i64) -; CHECK-NEXT: --> ((-1 * ptrtoint ([1 x i32]* @b to i64)) + %sub.ptr.lhs.cast) U: full-set S: full-set Exits: <> LoopDispositions: { %for.cond: Variant } +; CHECK-NEXT: --> ((-1 * @b) + %1) U: full-set S: full-set Exits: <> LoopDispositions: { %for.cond: Variant } ; CHECK-NEXT: %sub.ptr.div = sdiv exact i64 %sub.ptr.sub, 4 ; CHECK-NEXT: --> %sub.ptr.div U: full-set S: [-2305843009213693952,2305843009213693952) Exits: <> LoopDispositions: { %for.cond: Variant } ; CHECK-NEXT: %arrayidx1 = getelementptr inbounds [1 x i8], [1 x i8]* %arrayidx, i64 0, i64 %sub.ptr.div diff --git a/llvm/test/Analysis/ScalarEvolution/no-wrap-add-exprs.ll b/llvm/test/Analysis/ScalarEvolution/no-wrap-add-exprs.ll index 5a7bb3c9e5cd54..eb669cab0c790f 100644 --- a/llvm/test/Analysis/ScalarEvolution/no-wrap-add-exprs.ll +++ b/llvm/test/Analysis/ScalarEvolution/no-wrap-add-exprs.ll @@ -170,14 +170,14 @@ define void @f3(i8* %x_addr, i8* %y_addr, i32* %tmp_addr) { %int5 = add i32 %int0, 5 %int.zext = zext i32 %int5 to i64 ; CHECK: %int.zext = zext i32 %int5 to i64 -; CHECK-NEXT: --> (1 + (zext i32 (4 + %int0) to i64)) U: [1,4294967294) S: [1,4294967297) +; CHECK-NEXT: --> (1 + (zext i32 (4 + (trunc [16 x i8]* @z_addr to i32)) to i64)) U: [1,4294967294) S: [1,4294967297) %ptr_noalign = bitcast [16 x i8]* @z_addr_noalign to i8* %int0_na = ptrtoint i8* %ptr_noalign to i32 %int5_na = add i32 %int0_na, 5 %int.zext_na = zext i32 %int5_na to i64 ; CHECK: %int.zext_na = zext i32 %int5_na to i64 -; CHECK-NEXT: --> (zext i32 (5 + %int0_na) to i64) U: [0,4294967296) S: [0,4294967296) +; CHECK-NEXT: --> (zext i32 (5 + (trunc [16 x i8]* @z_addr_noalign to i32)) to i64) U: [0,4294967296) S: [0,4294967296) %tmp = load i32, i32* %tmp_addr %mul = and i32 %tmp, -4 diff --git a/llvm/test/Analysis/ScalarEvolution/ptrtoint.ll b/llvm/test/Analysis/ScalarEvolution/ptrtoint.ll index e3e9330e241f83..ac08fb24775e51 100644 --- a/llvm/test/Analysis/ScalarEvolution/ptrtoint.ll +++ b/llvm/test/Analysis/ScalarEvolution/ptrtoint.ll @@ -16,25 +16,25 @@ define void @ptrtoint(i8* %in, i64* %out0, i32* %out1, i16* %out2, i128* %out3) ; X64-LABEL: 'ptrtoint' ; X64-NEXT: Classifying expressions for: @ptrtoint ; X64-NEXT: %p0 = ptrtoint i8* %in to i64 -; X64-NEXT: --> %p0 U: full-set S: full-set +; X64-NEXT: --> %in U: full-set S: full-set ; X64-NEXT: %p1 = ptrtoint i8* %in to i32 -; X64-NEXT: --> %p1 U: full-set S: full-set +; X64-NEXT: --> (trunc i8* %in to i32) U: full-set S: full-set ; X64-NEXT: %p2 = ptrtoint i8* %in to i16 -; X64-NEXT: --> %p2 U: full-set S: full-set +; X64-NEXT: --> (trunc i8* %in to i16) U: full-set S: full-set ; X64-NEXT: %p3 = ptrtoint i8* %in to i128 -; X64-NEXT: --> %p3 U: [0,18446744073709551616) S: [-18446744073709551616,18446744073709551616) +; X64-NEXT: --> (zext i8* %in to i128) U: [0,18446744073709551616) S: [0,18446744073709551616) ; X64-NEXT: Determining loop execution counts for: @ptrtoint ; ; X32-LABEL: 'ptrtoint' ; X32-NEXT: Classifying expressions for: @ptrtoint ; X32-NEXT: %p0 = ptrtoint i8* %in to i64 -; X32-NEXT: --> %p0 U: [0,4294967296) S: [-4294967296,4294967296) +; X32-NEXT: --> (zext i8* %in to i64) U: [0,4294967296) S: [0,4294967296) ; X32-NEXT: %p1 = ptrtoint i8* %in to i32 -; X32-NEXT: --> %p1 U: full-set S: full-set +; X32-NEXT: --> %in U: full-set S: full-set ; X32-NEXT: %p2 = ptrtoint i8* %in to i16 -; X32-NEXT: --> %p2 U: full-set S: full-set +; X32-NEXT: --> (trunc i8* %in to i16) U: full-set S: full-set ; X32-NEXT: %p3 = ptrtoint i8* %in to i128 -; X32-NEXT: --> %p3 U: [0,4294967296) S: [-4294967296,4294967296) +; X32-NEXT: --> (zext i8* %in to i128) U: [0,4294967296) S: [0,4294967296) ; X32-NEXT: Determining loop execution counts for: @ptrtoint ; %p0 = ptrtoint i8* %in to i64 @@ -53,25 +53,25 @@ define void @ptrtoint_as1(i8 addrspace(1)* %in, i64* %out0, i32* %out1, i16* %ou ; X64-LABEL: 'ptrtoint_as1' ; X64-NEXT: Classifying expressions for: @ptrtoint_as1 ; X64-NEXT: %p0 = ptrtoint i8 addrspace(1)* %in to i64 -; X64-NEXT: --> %p0 U: full-set S: full-set +; X64-NEXT: --> %in U: full-set S: full-set ; X64-NEXT: %p1 = ptrtoint i8 addrspace(1)* %in to i32 -; X64-NEXT: --> %p1 U: full-set S: full-set +; X64-NEXT: --> (trunc i8 addrspace(1)* %in to i32) U: full-set S: full-set ; X64-NEXT: %p2 = ptrtoint i8 addrspace(1)* %in to i16 -; X64-NEXT: --> %p2 U: full-set S: full-set +; X64-NEXT: --> (trunc i8 addrspace(1)* %in to i16) U: full-set S: full-set ; X64-NEXT: %p3 = ptrtoint i8 addrspace(1)* %in to i128 -; X64-NEXT: --> %p3 U: [0,18446744073709551616) S: [-18446744073709551616,18446744073709551616) +; X64-NEXT: --> (zext i8 addrspace(1)* %in to i128) U: [0,18446744073709551616) S: [0,18446744073709551616) ; X64-NEXT: Determining loop execution counts for: @ptrtoint_as1 ; ; X32-LABEL: 'ptrtoint_as1' ; X32-NEXT: Classifying expressions for: @ptrtoint_as1 ; X32-NEXT: %p0 = ptrtoint i8 addrspace(1)* %in to i64 -; X32-NEXT: --> %p0 U: [0,4294967296) S: [-4294967296,4294967296) +; X32-NEXT: --> (zext i8 addrspace(1)* %in to i64) U: [0,4294967296) S: [0,4294967296) ; X32-NEXT: %p1 = ptrtoint i8 addrspace(1)* %in to i32 -; X32-NEXT: --> %p1 U: full-set S: full-set +; X32-NEXT: --> %in U: full-set S: full-set ; X32-NEXT: %p2 = ptrtoint i8 addrspace(1)* %in to i16 -; X32-NEXT: --> %p2 U: full-set S: full-set +; X32-NEXT: --> (trunc i8 addrspace(1)* %in to i16) U: full-set S: full-set ; X32-NEXT: %p3 = ptrtoint i8 addrspace(1)* %in to i128 -; X32-NEXT: --> %p3 U: [0,4294967296) S: [-4294967296,4294967296) +; X32-NEXT: --> (zext i8 addrspace(1)* %in to i128) U: [0,4294967296) S: [0,4294967296) ; X32-NEXT: Determining loop execution counts for: @ptrtoint_as1 ; %p0 = ptrtoint i8 addrspace(1)* %in to i64 @@ -92,7 +92,7 @@ define void @ptrtoint_of_bitcast(i8* %in, i64* %out0) { ; X64-NEXT: %in_casted = bitcast i8* %in to float* ; X64-NEXT: --> %in U: full-set S: full-set ; X64-NEXT: %p0 = ptrtoint float* %in_casted to i64 -; X64-NEXT: --> %p0 U: full-set S: full-set +; X64-NEXT: --> %in U: full-set S: full-set ; X64-NEXT: Determining loop execution counts for: @ptrtoint_of_bitcast ; ; X32-LABEL: 'ptrtoint_of_bitcast' @@ -100,7 +100,7 @@ define void @ptrtoint_of_bitcast(i8* %in, i64* %out0) { ; X32-NEXT: %in_casted = bitcast i8* %in to float* ; X32-NEXT: --> %in U: full-set S: full-set ; X32-NEXT: %p0 = ptrtoint float* %in_casted to i64 -; X32-NEXT: --> %p0 U: [0,4294967296) S: [-4294967296,4294967296) +; X32-NEXT: --> (zext i8* %in to i64) U: [0,4294967296) S: [0,4294967296) ; X32-NEXT: Determining loop execution counts for: @ptrtoint_of_bitcast ; %in_casted = bitcast i8* %in to float* @@ -116,7 +116,7 @@ define void @ptrtoint_of_addrspacecast(i8* %in, i64* %out0) { ; X64-NEXT: %in_casted = addrspacecast i8* %in to i8 addrspace(1)* ; X64-NEXT: --> %in_casted U: full-set S: full-set ; X64-NEXT: %p0 = ptrtoint i8 addrspace(1)* %in_casted to i64 -; X64-NEXT: --> %p0 U: full-set S: full-set +; X64-NEXT: --> %in_casted U: full-set S: full-set ; X64-NEXT: Determining loop execution counts for: @ptrtoint_of_addrspacecast ; ; X32-LABEL: 'ptrtoint_of_addrspacecast' @@ -124,7 +124,7 @@ define void @ptrtoint_of_addrspacecast(i8* %in, i64* %out0) { ; X32-NEXT: %in_casted = addrspacecast i8* %in to i8 addrspace(1)* ; X32-NEXT: --> %in_casted U: full-set S: full-set ; X32-NEXT: %p0 = ptrtoint i8 addrspace(1)* %in_casted to i64 -; X32-NEXT: --> %p0 U: [0,4294967296) S: [-4294967296,4294967296) +; X32-NEXT: --> (zext i8 addrspace(1)* %in_casted to i64) U: [0,4294967296) S: [0,4294967296) ; X32-NEXT: Determining loop execution counts for: @ptrtoint_of_addrspacecast ; %in_casted = addrspacecast i8* %in to i8 addrspace(1)* @@ -140,7 +140,7 @@ define void @ptrtoint_of_inttoptr(i64 %in, i64* %out0) { ; X64-NEXT: %in_casted = inttoptr i64 %in to i8* ; X64-NEXT: --> %in_casted U: full-set S: full-set ; X64-NEXT: %p0 = ptrtoint i8* %in_casted to i64 -; X64-NEXT: --> %p0 U: full-set S: full-set +; X64-NEXT: --> %in_casted U: full-set S: full-set ; X64-NEXT: Determining loop execution counts for: @ptrtoint_of_inttoptr ; ; X32-LABEL: 'ptrtoint_of_inttoptr' @@ -148,7 +148,7 @@ define void @ptrtoint_of_inttoptr(i64 %in, i64* %out0) { ; X32-NEXT: %in_casted = inttoptr i64 %in to i8* ; X32-NEXT: --> %in_casted U: full-set S: full-set ; X32-NEXT: %p0 = ptrtoint i8* %in_casted to i64 -; X32-NEXT: --> %p0 U: [0,4294967296) S: [-4294967296,4294967296) +; X32-NEXT: --> (zext i8* %in_casted to i64) U: [0,4294967296) S: [0,4294967296) ; X32-NEXT: Determining loop execution counts for: @ptrtoint_of_inttoptr ; %in_casted = inttoptr i64 %in to i8* @@ -197,11 +197,17 @@ define void @ptrtoint_of_nullptr(i64* %out0) { ; A constant inttoptr argument of an ptrtoint is still bad. define void @ptrtoint_of_constantexpr_inttoptr(i64* %out0) { -; ALL-LABEL: 'ptrtoint_of_constantexpr_inttoptr' -; ALL-NEXT: Classifying expressions for: @ptrtoint_of_constantexpr_inttoptr -; ALL-NEXT: %p0 = ptrtoint i8* inttoptr (i64 42 to i8*) to i64 -; ALL-NEXT: --> %p0 U: [42,43) S: [-64,64) -; ALL-NEXT: Determining loop execution counts for: @ptrtoint_of_constantexpr_inttoptr +; X64-LABEL: 'ptrtoint_of_constantexpr_inttoptr' +; X64-NEXT: Classifying expressions for: @ptrtoint_of_constantexpr_inttoptr +; X64-NEXT: %p0 = ptrtoint i8* inttoptr (i64 42 to i8*) to i64 +; X64-NEXT: --> inttoptr (i64 42 to i8*) U: [42,43) S: [-64,64) +; X64-NEXT: Determining loop execution counts for: @ptrtoint_of_constantexpr_inttoptr +; +; X32-LABEL: 'ptrtoint_of_constantexpr_inttoptr' +; X32-NEXT: Classifying expressions for: @ptrtoint_of_constantexpr_inttoptr +; X32-NEXT: %p0 = ptrtoint i8* inttoptr (i64 42 to i8*) to i64 +; X32-NEXT: --> (zext i8* inttoptr (i64 42 to i8*) to i64) U: [42,43) S: [0,4294967296) +; X32-NEXT: Determining loop execution counts for: @ptrtoint_of_constantexpr_inttoptr ; %p0 = ptrtoint i8* inttoptr (i64 42 to i8*) to i64 store i64 %p0, i64* %out0 diff --git a/llvm/test/CodeGen/AArch64/sve-fixed-length-log-reduce.ll b/llvm/test/CodeGen/AArch64/sve-fixed-length-log-reduce.ll index efab691b4a9f45..ffe72b511e0d9e 100644 --- a/llvm/test/CodeGen/AArch64/sve-fixed-length-log-reduce.ll +++ b/llvm/test/CodeGen/AArch64/sve-fixed-length-log-reduce.ll @@ -343,6 +343,329 @@ define i64 @andv_v32i64(<32 x i64>* %a) #0 { ret i64 %res } +; +; EORV +; + +; No single instruction NEON EORV support. Use SVE. +define i8 @eorv_v8i8(<8 x i8> %a) #0 { +; CHECK-LABEL: eorv_v8i8: +; CHECK: ptrue [[PG:p[0-9]+]].b, vl8 +; CHECK: eorv b[[REDUCE:[0-9]+]], [[PG]], z0.b +; CHECK: fmov w0, s[[REDUCE]] +; CHECK: ret + %res = call i8 @llvm.experimental.vector.reduce.xor.v8i8(<8 x i8> %a) + ret i8 %res +} + +; No single instruction NEON EORV support. Use SVE. +define i8 @eorv_v16i8(<16 x i8> %a) #0 { +; CHECK-LABEL: eorv_v16i8: +; CHECK: ptrue [[PG:p[0-9]+]].b, vl16 +; CHECK: eorv b[[REDUCE:[0-9]+]], [[PG]], z0.b +; CHECK: fmov w0, s[[REDUCE]] +; CHECK: ret + %res = call i8 @llvm.experimental.vector.reduce.xor.v16i8(<16 x i8> %a) + ret i8 %res +} + +define i8 @eorv_v32i8(<32 x i8>* %a) #0 { +; CHECK-LABEL: eorv_v32i8: +; VBITS_GE_256: ptrue [[PG:p[0-9]+]].b, vl32 +; VBITS_GE_256-NEXT: ld1b { [[OP:z[0-9]+]].b }, [[PG]]/z, [x0] +; VBITS_GE_256-NEXT: eorv b[[REDUCE:[0-9]+]], [[PG]], [[OP]].b +; VBITS_GE_256-NEXT: fmov w0, s[[REDUCE]] +; VBITS_GE_256-NEXT: ret + %op = load <32 x i8>, <32 x i8>* %a + %res = call i8 @llvm.experimental.vector.reduce.xor.v32i8(<32 x i8> %op) + ret i8 %res +} + +define i8 @eorv_v64i8(<64 x i8>* %a) #0 { +; CHECK-LABEL: eorv_v64i8: +; VBITS_GE_512: ptrue [[PG:p[0-9]+]].b, vl64 +; VBITS_GE_512-NEXT: ld1b { [[OP:z[0-9]+]].b }, [[PG]]/z, [x0] +; VBITS_GE_512-NEXT: eorv b[[REDUCE:[0-9]+]], [[PG]], [[OP]].b +; VBITS_GE_512-NEXT: fmov w0, s[[REDUCE]] +; VBITS_GE_512-NEXT: ret + +; Ensure sensible type legalisation. +; VBITS_EQ_256-DAG: ptrue [[PG:p[0-9]+]].b, vl32 +; VBITS_EQ_256-DAG: mov w[[A_HI:[0-9]+]], #32 +; VBITS_EQ_256-DAG: ld1b { [[LO:z[0-9]+]].b }, [[PG]]/z, [x0] +; VBITS_EQ_256-DAG: ld1b { [[HI:z[0-9]+]].b }, [[PG]]/z, [x0, x[[A_HI]]] +; VBITS_EQ_256-DAG: eor [[EOR:z[0-9]+]].d, [[LO]].d, [[HI]].d +; VBITS_EQ_256-DAG: eorv b[[REDUCE:[0-9]+]], [[PG]], [[EOR]].b +; VBITS_EQ_256-NEXT: fmov w0, s[[REDUCE]] +; VBITS_EQ_256-NEXT: ret + + %op = load <64 x i8>, <64 x i8>* %a + %res = call i8 @llvm.experimental.vector.reduce.xor.v64i8(<64 x i8> %op) + ret i8 %res +} + +define i8 @eorv_v128i8(<128 x i8>* %a) #0 { +; CHECK-LABEL: eorv_v128i8: +; VBITS_GE_1024: ptrue [[PG:p[0-9]+]].b, vl128 +; VBITS_GE_1024-NEXT: ld1b { [[OP:z[0-9]+]].b }, [[PG]]/z, [x0] +; VBITS_GE_1024-NEXT: eorv b[[REDUCE:[0-9]+]], [[PG]], [[OP]].b +; VBITS_GE_1024-NEXT: fmov w0, s[[REDUCE]] +; VBITS_GE_1024-NEXT: ret + %op = load <128 x i8>, <128 x i8>* %a + %res = call i8 @llvm.experimental.vector.reduce.xor.v128i8(<128 x i8> %op) + ret i8 %res +} + +define i8 @eorv_v256i8(<256 x i8>* %a) #0 { +; CHECK-LABEL: eorv_v256i8: +; VBITS_GE_2048: ptrue [[PG:p[0-9]+]].b, vl256 +; VBITS_GE_2048-NEXT: ld1b { [[OP:z[0-9]+]].b }, [[PG]]/z, [x0] +; VBITS_GE_2048-NEXT: eorv b[[REDUCE:[0-9]+]], [[PG]], [[OP]].b +; VBITS_GE_2048-NEXT: fmov w0, s[[REDUCE]] +; VBITS_GE_2048-NEXT: ret + %op = load <256 x i8>, <256 x i8>* %a + %res = call i8 @llvm.experimental.vector.reduce.xor.v256i8(<256 x i8> %op) + ret i8 %res +} + +; No single instruction NEON EORV support. Use SVE. +define i16 @eorv_v4i16(<4 x i16> %a) #0 { +; CHECK-LABEL: eorv_v4i16: +; CHECK: ptrue [[PG:p[0-9]+]].h, vl4 +; CHECK: eorv h[[REDUCE:[0-9]+]], [[PG]], z0.h +; CHECK: fmov w0, s[[REDUCE]] +; CHECK: ret + %res = call i16 @llvm.experimental.vector.reduce.xor.v4i16(<4 x i16> %a) + ret i16 %res +} + +; No single instruction NEON EORV support. Use SVE. +define i16 @eorv_v8i16(<8 x i16> %a) #0 { +; CHECK-LABEL: eorv_v8i16: +; CHECK: ptrue [[PG:p[0-9]+]].h, vl8 +; CHECK: eorv h[[REDUCE:[0-9]+]], [[PG]], z0.h +; CHECK: fmov w0, s[[REDUCE]] +; CHECK: ret + %res = call i16 @llvm.experimental.vector.reduce.xor.v8i16(<8 x i16> %a) + ret i16 %res +} + +define i16 @eorv_v16i16(<16 x i16>* %a) #0 { +; CHECK-LABEL: eorv_v16i16: +; VBITS_GE_256: ptrue [[PG:p[0-9]+]].h, vl16 +; VBITS_GE_256-NEXT: ld1h { [[OP:z[0-9]+]].h }, [[PG]]/z, [x0] +; VBITS_GE_256-NEXT: eorv h[[REDUCE:[0-9]+]], [[PG]], [[OP]].h +; VBITS_GE_256-NEXT: fmov w0, s[[REDUCE]] +; VBITS_GE_256-NEXT: ret + %op = load <16 x i16>, <16 x i16>* %a + %res = call i16 @llvm.experimental.vector.reduce.xor.v16i16(<16 x i16> %op) + ret i16 %res +} + +define i16 @eorv_v32i16(<32 x i16>* %a) #0 { +; CHECK-LABEL: eorv_v32i16: +; VBITS_GE_512: ptrue [[PG:p[0-9]+]].h, vl32 +; VBITS_GE_512-NEXT: ld1h { [[OP:z[0-9]+]].h }, [[PG]]/z, [x0] +; VBITS_GE_512-NEXT: eorv h[[REDUCE:[0-9]+]], [[PG]], [[OP]].h +; VBITS_GE_512-NEXT: fmov w0, s[[REDUCE]] +; VBITS_GE_512-NEXT: ret + +; Ensure sensible type legalisation. +; VBITS_EQ_256-DAG: ptrue [[PG:p[0-9]+]].h, vl16 +; VBITS_EQ_256-DAG: add x[[A_HI:[0-9]+]], x0, #32 +; VBITS_EQ_256-DAG: ld1h { [[LO:z[0-9]+]].h }, [[PG]]/z, [x0] +; VBITS_EQ_256-DAG: ld1h { [[HI:z[0-9]+]].h }, [[PG]]/z, [x[[A_HI]]] +; VBITS_EQ_256-DAG: eor [[EOR:z[0-9]+]].d, [[LO]].d, [[HI]].d +; VBITS_EQ_256-DAG: eorv h[[REDUCE:[0-9]+]], [[PG]], [[EOR]].h +; VBITS_EQ_256-NEXT: fmov w0, s[[REDUCE]] +; VBITS_EQ_256-NEXT: ret + %op = load <32 x i16>, <32 x i16>* %a + %res = call i16 @llvm.experimental.vector.reduce.xor.v32i16(<32 x i16> %op) + ret i16 %res +} + +define i16 @eorv_v64i16(<64 x i16>* %a) #0 { +; CHECK-LABEL: eorv_v64i16: +; VBITS_GE_1024: ptrue [[PG:p[0-9]+]].h, vl64 +; VBITS_GE_1024-NEXT: ld1h { [[OP:z[0-9]+]].h }, [[PG]]/z, [x0] +; VBITS_GE_1024-NEXT: eorv h[[REDUCE:[0-9]+]], [[PG]], [[OP]].h +; VBITS_GE_1024-NEXT: fmov w0, s[[REDUCE]] +; VBITS_GE_1024-NEXT: ret + %op = load <64 x i16>, <64 x i16>* %a + %res = call i16 @llvm.experimental.vector.reduce.xor.v64i16(<64 x i16> %op) + ret i16 %res +} + +define i16 @eorv_v128i16(<128 x i16>* %a) #0 { +; CHECK-LABEL: eorv_v128i16: +; VBITS_GE_2048: ptrue [[PG:p[0-9]+]].h, vl128 +; VBITS_GE_2048-NEXT: ld1h { [[OP:z[0-9]+]].h }, [[PG]]/z, [x0] +; VBITS_GE_2048-NEXT: eorv h[[REDUCE:[0-9]+]], [[PG]], [[OP]].h +; VBITS_GE_2048-NEXT: fmov w0, s[[REDUCE]] +; VBITS_GE_2048-NEXT: ret + %op = load <128 x i16>, <128 x i16>* %a + %res = call i16 @llvm.experimental.vector.reduce.xor.v128i16(<128 x i16> %op) + ret i16 %res +} + +; No single instruction NEON EORV support. Use SVE. +define i32 @eorv_v2i32(<2 x i32> %a) #0 { +; CHECK-LABEL: eorv_v2i32: +; CHECK: ptrue [[PG:p[0-9]+]].s, vl2 +; CHECK: eorv [[REDUCE:s[0-9]+]], [[PG]], z0.s +; CHECK: fmov w0, [[REDUCE]] +; CHECK: ret + %res = call i32 @llvm.experimental.vector.reduce.xor.v2i32(<2 x i32> %a) + ret i32 %res +} + +; No single instruction NEON EORV support. Use SVE. +define i32 @eorv_v4i32(<4 x i32> %a) #0 { +; CHECK-LABEL: eorv_v4i32: +; CHECK: ptrue [[PG:p[0-9]+]].s, vl4 +; CHECK: eorv [[REDUCE:s[0-9]+]], [[PG]], z0.s +; CHECK: fmov w0, [[REDUCE]] +; CHECK: ret + %res = call i32 @llvm.experimental.vector.reduce.xor.v4i32(<4 x i32> %a) + ret i32 %res +} + +define i32 @eorv_v8i32(<8 x i32>* %a) #0 { +; CHECK-LABEL: eorv_v8i32: +; VBITS_GE_256: ptrue [[PG:p[0-9]+]].s, vl8 +; VBITS_GE_256-NEXT: ld1w { [[OP:z[0-9]+]].s }, [[PG]]/z, [x0] +; VBITS_GE_256-NEXT: eorv [[REDUCE:s[0-9]+]], [[PG]], [[OP]].s +; VBITS_GE_256-NEXT: fmov w0, [[REDUCE]] +; VBITS_GE_256-NEXT: ret + %op = load <8 x i32>, <8 x i32>* %a + %res = call i32 @llvm.experimental.vector.reduce.xor.v8i32(<8 x i32> %op) + ret i32 %res +} + +define i32 @eorv_v16i32(<16 x i32>* %a) #0 { +; CHECK-LABEL: eorv_v16i32: +; VBITS_GE_512: ptrue [[PG:p[0-9]+]].s, vl16 +; VBITS_GE_512-NEXT: ld1w { [[OP:z[0-9]+]].s }, [[PG]]/z, [x0] +; VBITS_GE_512-NEXT: eorv [[REDUCE:s[0-9]+]], [[PG]], [[OP]].s +; VBITS_GE_512-NEXT: fmov w0, [[REDUCE]] +; VBITS_GE_512-NEXT: ret + +; Ensure sensible type legalisation. +; VBITS_EQ_256-DAG: ptrue [[PG:p[0-9]+]].s, vl8 +; VBITS_EQ_256-DAG: add x[[A_HI:[0-9]+]], x0, #32 +; VBITS_EQ_256-DAG: ld1w { [[LO:z[0-9]+]].s }, [[PG]]/z, [x0] +; VBITS_EQ_256-DAG: ld1w { [[HI:z[0-9]+]].s }, [[PG]]/z, [x[[A_HI]]] +; VBITS_EQ_256-DAG: eor [[EOR:z[0-9]+]].d, [[LO]].d, [[HI]].d +; VBITS_EQ_256-DAG: eorv [[REDUCE:s[0-9]+]], [[PG]], [[EOR]].s +; VBITS_EQ_256-NEXT: fmov w0, [[REDUCE]] +; VBITS_EQ_256-NEXT: ret + %op = load <16 x i32>, <16 x i32>* %a + %res = call i32 @llvm.experimental.vector.reduce.xor.v16i32(<16 x i32> %op) + ret i32 %res +} + +define i32 @eorv_v32i32(<32 x i32>* %a) #0 { +; CHECK-LABEL: eorv_v32i32: +; VBITS_GE_1024: ptrue [[PG:p[0-9]+]].s, vl32 +; VBITS_GE_1024-NEXT: ld1w { [[OP:z[0-9]+]].s }, [[PG]]/z, [x0] +; VBITS_GE_1024-NEXT: eorv [[REDUCE:s[0-9]+]], [[PG]], [[OP]].s +; VBITS_GE_1024-NEXT: fmov w0, [[REDUCE]] +; VBITS_GE_1024-NEXT: ret + %op = load <32 x i32>, <32 x i32>* %a + %res = call i32 @llvm.experimental.vector.reduce.xor.v32i32(<32 x i32> %op) + ret i32 %res +} + +define i32 @eorv_v64i32(<64 x i32>* %a) #0 { +; CHECK-LABEL: eorv_v64i32: +; VBITS_GE_2048: ptrue [[PG:p[0-9]+]].s, vl64 +; VBITS_GE_2048-NEXT: ld1w { [[OP:z[0-9]+]].s }, [[PG]]/z, [x0] +; VBITS_GE_2048-NEXT: eorv [[REDUCE:s[0-9]+]], [[PG]], [[OP]].s +; VBITS_GE_2048-NEXT: fmov w0, [[REDUCE]] +; VBITS_GE_2048-NEXT: ret + %op = load <64 x i32>, <64 x i32>* %a + %res = call i32 @llvm.experimental.vector.reduce.xor.v64i32(<64 x i32> %op) + ret i32 %res +} + +; Nothing to do for single element vectors. +define i64 @eorv_v1i64(<1 x i64> %a) #0 { +; CHECK-LABEL: eorv_v1i64: +; CHECK: fmov x0, d0 +; CHECK: ret + %res = call i64 @llvm.experimental.vector.reduce.xor.v1i64(<1 x i64> %a) + ret i64 %res +} + +; Use SVE for 128-bit vectors +define i64 @eorv_v2i64(<2 x i64> %a) #0 { +; CHECK-LABEL: eorv_v2i64: +; CHECK: ptrue [[PG:p[0-9]+]].d, vl2 +; CHECK: eorv [[REDUCE:d[0-9]+]], [[PG]], z0.d +; CHECK: fmov x0, [[REDUCE]] +; CHECK: ret + %res = call i64 @llvm.experimental.vector.reduce.xor.v2i64(<2 x i64> %a) + ret i64 %res +} + +define i64 @eorv_v4i64(<4 x i64>* %a) #0 { +; CHECK-LABEL: eorv_v4i64: +; VBITS_GE_256: ptrue [[PG:p[0-9]+]].d, vl4 +; VBITS_GE_256-NEXT: ld1d { [[OP:z[0-9]+]].d }, [[PG]]/z, [x0] +; VBITS_GE_256-NEXT: eorv [[REDUCE:d[0-9]+]], [[PG]], [[OP]].d +; VBITS_GE_256-NEXT: fmov x0, [[REDUCE]] +; VBITS_GE_256-NEXT: ret + %op = load <4 x i64>, <4 x i64>* %a + %res = call i64 @llvm.experimental.vector.reduce.xor.v4i64(<4 x i64> %op) + ret i64 %res +} + +define i64 @eorv_v8i64(<8 x i64>* %a) #0 { +; CHECK-LABEL: eorv_v8i64: +; VBITS_GE_512: ptrue [[PG:p[0-9]+]].d, vl8 +; VBITS_GE_512-NEXT: ld1d { [[OP:z[0-9]+]].d }, [[PG]]/z, [x0] +; VBITS_GE_512-NEXT: eorv [[REDUCE:d[0-9]+]], [[PG]], [[OP]].d +; VBITS_GE_512-NEXT: fmov x0, [[REDUCE]] +; VBITS_GE_512-NEXT: ret + +; Ensure sensible type legalisation. +; VBITS_EQ_256-DAG: ptrue [[PG:p[0-9]+]].d, vl4 +; VBITS_EQ_256-DAG: add x[[A_HI:[0-9]+]], x0, #32 +; VBITS_EQ_256-DAG: ld1d { [[LO:z[0-9]+]].d }, [[PG]]/z, [x0] +; VBITS_EQ_256-DAG: ld1d { [[HI:z[0-9]+]].d }, [[PG]]/z, [x[[A_HI]]] +; VBITS_EQ_256-DAG: eor [[EOR:z[0-9]+]].d, [[LO]].d, [[HI]].d +; VBITS_EQ_256-DAG: eorv [[REDUCE:d[0-9]+]], [[PG]], [[EOR]].d +; VBITS_EQ_256-NEXT: fmov x0, [[REDUCE]] +; VBITS_EQ_256-NEXT: ret + %op = load <8 x i64>, <8 x i64>* %a + %res = call i64 @llvm.experimental.vector.reduce.xor.v8i64(<8 x i64> %op) + ret i64 %res +} + +define i64 @eorv_v16i64(<16 x i64>* %a) #0 { +; CHECK-LABEL: eorv_v16i64: +; VBITS_GE_1024: ptrue [[PG:p[0-9]+]].d, vl16 +; VBITS_GE_1024-NEXT: ld1d { [[OP:z[0-9]+]].d }, [[PG]]/z, [x0] +; VBITS_GE_1024-NEXT: eorv [[REDUCE:d[0-9]+]], [[PG]], [[OP]].d +; VBITS_GE_1024-NEXT: fmov x0, [[REDUCE]] +; VBITS_GE_1024-NEXT: ret + %op = load <16 x i64>, <16 x i64>* %a + %res = call i64 @llvm.experimental.vector.reduce.xor.v16i64(<16 x i64> %op) + ret i64 %res +} + +define i64 @eorv_v32i64(<32 x i64>* %a) #0 { +; CHECK-LABEL: eorv_v32i64: +; VBITS_GE_2048: ptrue [[PG:p[0-9]+]].d, vl32 +; VBITS_GE_2048-NEXT: ld1d { [[OP:z[0-9]+]].d }, [[PG]]/z, [x0] +; VBITS_GE_2048-NEXT: eorv [[REDUCE:d[0-9]+]], [[PG]], [[OP]].d +; VBITS_GE_2048-NEXT: fmov x0, [[REDUCE]] +; VBITS_GE_2048-NEXT: ret + %op = load <32 x i64>, <32 x i64>* %a + %res = call i64 @llvm.experimental.vector.reduce.xor.v32i64(<32 x i64> %op) + ret i64 %res +} + ; ; ORV ; @@ -723,3 +1046,31 @@ declare i64 @llvm.experimental.vector.reduce.or.v4i64(<4 x i64>) declare i64 @llvm.experimental.vector.reduce.or.v8i64(<8 x i64>) declare i64 @llvm.experimental.vector.reduce.or.v16i64(<16 x i64>) declare i64 @llvm.experimental.vector.reduce.or.v32i64(<32 x i64>) + +declare i8 @llvm.experimental.vector.reduce.xor.v8i8(<8 x i8>) +declare i8 @llvm.experimental.vector.reduce.xor.v16i8(<16 x i8>) +declare i8 @llvm.experimental.vector.reduce.xor.v32i8(<32 x i8>) +declare i8 @llvm.experimental.vector.reduce.xor.v64i8(<64 x i8>) +declare i8 @llvm.experimental.vector.reduce.xor.v128i8(<128 x i8>) +declare i8 @llvm.experimental.vector.reduce.xor.v256i8(<256 x i8>) + +declare i16 @llvm.experimental.vector.reduce.xor.v4i16(<4 x i16>) +declare i16 @llvm.experimental.vector.reduce.xor.v8i16(<8 x i16>) +declare i16 @llvm.experimental.vector.reduce.xor.v16i16(<16 x i16>) +declare i16 @llvm.experimental.vector.reduce.xor.v32i16(<32 x i16>) +declare i16 @llvm.experimental.vector.reduce.xor.v64i16(<64 x i16>) +declare i16 @llvm.experimental.vector.reduce.xor.v128i16(<128 x i16>) + +declare i32 @llvm.experimental.vector.reduce.xor.v2i32(<2 x i32>) +declare i32 @llvm.experimental.vector.reduce.xor.v4i32(<4 x i32>) +declare i32 @llvm.experimental.vector.reduce.xor.v8i32(<8 x i32>) +declare i32 @llvm.experimental.vector.reduce.xor.v16i32(<16 x i32>) +declare i32 @llvm.experimental.vector.reduce.xor.v32i32(<32 x i32>) +declare i32 @llvm.experimental.vector.reduce.xor.v64i32(<64 x i32>) + +declare i64 @llvm.experimental.vector.reduce.xor.v1i64(<1 x i64>) +declare i64 @llvm.experimental.vector.reduce.xor.v2i64(<2 x i64>) +declare i64 @llvm.experimental.vector.reduce.xor.v4i64(<4 x i64>) +declare i64 @llvm.experimental.vector.reduce.xor.v8i64(<8 x i64>) +declare i64 @llvm.experimental.vector.reduce.xor.v16i64(<16 x i64>) +declare i64 @llvm.experimental.vector.reduce.xor.v32i64(<32 x i64>) diff --git a/llvm/test/CodeGen/ARM/funnel-shift.ll b/llvm/test/CodeGen/ARM/funnel-shift.ll index a8b6aff767a748..54c93b493c9817 100644 --- a/llvm/test/CodeGen/ARM/funnel-shift.ll +++ b/llvm/test/CodeGen/ARM/funnel-shift.ll @@ -19,13 +19,10 @@ declare <4 x i32> @llvm.fshr.v4i32(<4 x i32>, <4 x i32>, <4 x i32>) define i16 @fshl_i16(i16 %x, i16 %y, i16 %z) { ; CHECK-LABEL: fshl_i16: ; CHECK: @ %bb.0: -; CHECK-NEXT: and r2, r2, #15 -; CHECK-NEXT: mov r3, #31 -; CHECK-NEXT: lsl r1, r1, #16 -; CHECK-NEXT: bic r3, r3, r2 -; CHECK-NEXT: lsl r0, r0, r2 -; CHECK-NEXT: lsr r1, r1, #1 -; CHECK-NEXT: orr r0, r0, r1, lsr r3 +; CHECK-NEXT: pkhbt r0, r1, r0, lsl #16 +; CHECK-NEXT: and r1, r2, #15 +; CHECK-NEXT: lsl r0, r0, r1 +; CHECK-NEXT: lsr r0, r0, #16 ; CHECK-NEXT: bx lr %f = call i16 @llvm.fshl.i16(i16 %x, i16 %y, i16 %z) ret i16 %f @@ -188,15 +185,9 @@ define i8 @fshl_i8_const_fold() { define i16 @fshr_i16(i16 %x, i16 %y, i16 %z) { ; CHECK-LABEL: fshr_i16: ; CHECK: @ %bb.0: -; CHECK-NEXT: mov r3, #1 -; CHECK-NEXT: lsl r0, r0, #1 -; CHECK-NEXT: bfi r2, r3, #4, #28 -; CHECK-NEXT: mov r3, #31 -; CHECK-NEXT: bic r3, r3, r2 -; CHECK-NEXT: and r2, r2, #31 -; CHECK-NEXT: lsl r1, r1, #16 -; CHECK-NEXT: lsl r0, r0, r3 -; CHECK-NEXT: orr r0, r0, r1, lsr r2 +; CHECK-NEXT: pkhbt r0, r1, r0, lsl #16 +; CHECK-NEXT: and r1, r2, #15 +; CHECK-NEXT: lsr r0, r0, r1 ; CHECK-NEXT: bx lr %f = call i16 @llvm.fshr.i16(i16 %x, i16 %y, i16 %z) ret i16 %f diff --git a/llvm/test/CodeGen/ARM/lsr-undef-in-binop.ll b/llvm/test/CodeGen/ARM/lsr-undef-in-binop.ll index 564328d999982c..e7339721447580 100644 --- a/llvm/test/CodeGen/ARM/lsr-undef-in-binop.ll +++ b/llvm/test/CodeGen/ARM/lsr-undef-in-binop.ll @@ -186,7 +186,9 @@ define linkonce_odr i32 @vector_insert(%"class.std::__1::vector.182"*, [1 x i32] br i1 %114, label %124, label %115 ; CHECK-LABEL: .preheader: -; CHECK-NEXT: sub i32 [[OLD_CAST]], [[NEW_CAST]] +; CHECK-NEXT: [[NEG_NEW:%[0-9]+]] = sub i32 0, [[NEW_CAST]] +; CHECK-NEXT: getelementptr i8, i8* %97, i32 [[NEG_NEW]] + ;