From: Manuel Klimek Date: Tue, 8 Sep 2015 15:14:06 +0000 (+0000) Subject: Fix performance regression when running clang tools. X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=90ea10e5ea2f9907adea9f90667c34cc39820e61;p=clang Fix performance regression when running clang tools. Brings tool start time for a large synthetic test case down from (on my machine) 4 seconds to 0.5 seconds. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@247018 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/Tooling/JSONCompilationDatabase.h b/include/clang/Tooling/JSONCompilationDatabase.h index a288469449..ec1a3ab296 100644 --- a/include/clang/Tooling/JSONCompilationDatabase.h +++ b/include/clang/Tooling/JSONCompilationDatabase.h @@ -99,17 +99,21 @@ private: /// failed. bool parse(std::string &ErrorMessage); - // Tuple (directory, commandline) where 'commandline' pointing to the - // corresponding nodes in the YAML stream. - typedef std::pair> CompileCommandRef; + // Tuple (directory, commandline) where 'commandline' points to the + // corresponding scalar nodes in the YAML stream. + // If the command line contains a single argument, it is a shell-escaped + // command line. + // Otherwise, each entry in the command line vector is a literal + // argument to the compiler. + typedef std::pair> CompileCommandRef; /// \brief Converts the given array of CompileCommandRefs to CompileCommands. void getCommands(ArrayRef CommandsRef, std::vector &Commands) const; // Maps file paths to the compile command lines for that file. - llvm::StringMap< std::vector > IndexByFile; + llvm::StringMap> IndexByFile; FileMatchTrie MatchTrie; diff --git a/lib/Tooling/JSONCompilationDatabase.cpp b/lib/Tooling/JSONCompilationDatabase.cpp index 421651cdce..3ac6f697e5 100644 --- a/lib/Tooling/JSONCompilationDatabase.cpp +++ b/lib/Tooling/JSONCompilationDatabase.cpp @@ -214,15 +214,26 @@ JSONCompilationDatabase::getAllCompileCommands() const { return Commands; } +static std::vector +nodeToCommandLine(const std::vector &Nodes) { + SmallString<1024> Storage; + if (Nodes.size() == 1) { + return unescapeCommandLine(Nodes[0]->getValue(Storage)); + } + std::vector Arguments; + for (auto *Node : Nodes) { + Arguments.push_back(Node->getValue(Storage)); + } + return Arguments; +} + void JSONCompilationDatabase::getCommands( - ArrayRef CommandsRef, - std::vector &Commands) const { + ArrayRef CommandsRef, + std::vector &Commands) const { for (int I = 0, E = CommandsRef.size(); I != E; ++I) { SmallString<8> DirectoryStorage; - SmallString<1024> CommandStorage; - Commands.emplace_back( - CommandsRef[I].first->getValue(DirectoryStorage), - CommandsRef[I].second); + Commands.emplace_back(CommandsRef[I].first->getValue(DirectoryStorage), + nodeToCommandLine(CommandsRef[I].second)); } } @@ -249,11 +260,8 @@ bool JSONCompilationDatabase::parse(std::string &ErrorMessage) { return false; } llvm::yaml::ScalarNode *Directory = nullptr; - std::vector Arguments; - std::vector Command; + llvm::Optional> Command; llvm::yaml::ScalarNode *File = nullptr; - bool ArgumentsFound = false; - bool CommandFound = false; for (auto& NextKeyValue : *Object) { llvm::yaml::ScalarNode *KeyString = dyn_cast(NextKeyValue.getKey()); @@ -282,18 +290,18 @@ bool JSONCompilationDatabase::parse(std::string &ErrorMessage) { if (KeyValue == "directory") { Directory = ValueString; } else if (KeyValue == "arguments") { - for (auto& NextArgument : *SequenceString) { - SmallString<128> CommandStorage; - auto ValueString = dyn_cast(&NextArgument); - - Arguments.push_back(ValueString->getValue(CommandStorage)); + Command = std::vector(); + for (auto &Argument : *SequenceString) { + auto Scalar = dyn_cast(&Argument); + if (!Scalar) { + ErrorMessage = "Only strings are allowed in 'arguments'."; + return false; + } + Command->push_back(Scalar); } - ArgumentsFound = true; } else if (KeyValue == "command") { - SmallString<1024> CommandStorage; - // FIXME: Escape correctly: - Command = unescapeCommandLine(ValueString->getValue(CommandStorage)); - CommandFound = true; + if (!Command) + Command = std::vector(1, ValueString); } else if (KeyValue == "file") { File = ValueString; } else { @@ -306,7 +314,7 @@ bool JSONCompilationDatabase::parse(std::string &ErrorMessage) { ErrorMessage = "Missing key: \"file\"."; return false; } - if (!ArgumentsFound && !CommandFound) { + if (!Command) { ErrorMessage = "Missing key: \"command\" or \"arguments\"."; return false; } @@ -327,7 +335,7 @@ bool JSONCompilationDatabase::parse(std::string &ErrorMessage) { llvm::sys::path::native(FileName, NativeFilePath); } IndexByFile[NativeFilePath].push_back( - CompileCommandRef(Directory, ArgumentsFound ? Arguments : Command)); + CompileCommandRef(Directory, *Command)); MatchTrie.insert(NativeFilePath); } return true; diff --git a/unittests/Tooling/CompilationDatabaseTest.cpp b/unittests/Tooling/CompilationDatabaseTest.cpp index e84b6b1e3b..8e1f828428 100644 --- a/unittests/Tooling/CompilationDatabaseTest.cpp +++ b/unittests/Tooling/CompilationDatabaseTest.cpp @@ -42,6 +42,8 @@ TEST(JSONCompilationDatabase, ErrsOnInvalidFormat) { expectFailure("[{\"arguments\":\"\",\"file\":\"\"}]", "Missing directory"); expectFailure("[{\"directory\":\"\",\"arguments\":\"\",\"file\":\"\"}]", "Arguments not array"); expectFailure("[{\"directory\":\"\",\"command\":[],\"file\":\"\"}]", "Command not string"); + expectFailure("[{\"directory\":\"\",\"arguments\":[[]],\"file\":\"\"}]", + "Arguments contain non-string"); } static std::vector getAllFiles(StringRef JSONDatabase, @@ -140,8 +142,8 @@ TEST(JSONCompilationDatabase, ArgumentsPreferredOverCommand) { CompileCommand FoundCommand = findCompileArgsInJsonDatabase( FileName, ("[{\"directory\":\"" + Directory + "\"," - "\"command\":\"" + Command + "\"," "\"arguments\":[\"" + Arguments + "\"]," + "\"command\":\"" + Command + "\"," "\"file\":\"" + FileName + "\"}]").str(), ErrorMessage); EXPECT_EQ(Directory, FoundCommand.Directory) << ErrorMessage;