280 changes: 197 additions & 83 deletions clang-tools-extra/clangd/GlobalCompilationDatabase.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
//===----------------------------------------------------------------------===//

#include "GlobalCompilationDatabase.h"
#include "Config.h"
#include "FS.h"
#include "SourceCode.h"
#include "support/Logger.h"
Expand All @@ -20,6 +21,7 @@
#include "clang/Tooling/JSONCompilationDatabase.h"
#include "llvm/ADT/None.h"
#include "llvm/ADT/Optional.h"
#include "llvm/ADT/PointerIntPair.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/ScopeExit.h"
#include "llvm/ADT/SmallString.h"
Expand Down Expand Up @@ -362,8 +364,10 @@ bool DirectoryBasedGlobalCompilationDatabase::DirectoryCache::load(
DirectoryBasedGlobalCompilationDatabase::
DirectoryBasedGlobalCompilationDatabase(const Options &Opts)
: Opts(Opts), Broadcaster(std::make_unique<BroadcastThread>(*this)) {
if (Opts.CompileCommandsDir)
OnlyDirCache = std::make_unique<DirectoryCache>(*Opts.CompileCommandsDir);
if (!this->Opts.ContextProvider)
this->Opts.ContextProvider = [](llvm::StringRef) {
return Context::current().clone();
};
}

DirectoryBasedGlobalCompilationDatabase::
Expand Down Expand Up @@ -405,14 +409,6 @@ static std::string maybeCaseFoldPath(PathRef Path) {
#endif
}

static bool pathEqual(PathRef A, PathRef B) {
#if defined(_WIN32) || defined(__APPLE__)
return A.equals_lower(B);
#else
return A == B;
#endif
}

std::vector<DirectoryBasedGlobalCompilationDatabase::DirectoryCache *>
DirectoryBasedGlobalCompilationDatabase::getDirectoryCaches(
llvm::ArrayRef<llvm::StringRef> Dirs) const {
Expand Down Expand Up @@ -441,31 +437,42 @@ DirectoryBasedGlobalCompilationDatabase::lookupCDB(
assert(llvm::sys::path::is_absolute(Request.FileName) &&
"path must be absolute");

std::string Storage;
std::vector<llvm::StringRef> SearchDirs;
if (Opts.CompileCommandsDir) // FIXME: unify this case with config.
SearchDirs = {Opts.CompileCommandsDir.getValue()};
else {
WithContext WithProvidedContext(Opts.ContextProvider(Request.FileName));
const auto &Spec = Config::current().CompileFlags.CDBSearch;
switch (Spec.Policy) {
case Config::CDBSearchSpec::NoCDBSearch:
return llvm::None;
case Config::CDBSearchSpec::FixedDir:
Storage = Spec.FixedCDBPath.getValue();
SearchDirs = {Storage};
break;
case Config::CDBSearchSpec::Ancestors:
// Traverse the canonical version to prevent false positives. i.e.:
// src/build/../a.cc can detect a CDB in /src/build if not
// canonicalized.
Storage = removeDots(Request.FileName);
actOnAllParentDirectories(Storage, [&](llvm::StringRef Dir) {
SearchDirs.push_back(Dir);
return false;
});
}
}

std::shared_ptr<const tooling::CompilationDatabase> CDB = nullptr;
bool ShouldBroadcast = false;
DirectoryCache *DirCache = nullptr;
std::shared_ptr<const tooling::CompilationDatabase> CDB = nullptr;
if (OnlyDirCache) {
DirCache = OnlyDirCache.get();
ShouldBroadcast = Request.ShouldBroadcast;
CDB = DirCache->get(Opts.TFS, ShouldBroadcast, Request.FreshTime,
Request.FreshTimeMissing);
} else {
// Traverse the canonical version to prevent false positives. i.e.:
// src/build/../a.cc can detect a CDB in /src/build if not canonicalized.
std::string CanonicalPath = removeDots(Request.FileName);
std::vector<llvm::StringRef> SearchDirs;
actOnAllParentDirectories(CanonicalPath, [&](PathRef Path) {
SearchDirs.push_back(Path);
return false;
});
for (DirectoryCache *Candidate : getDirectoryCaches(SearchDirs)) {
bool CandidateShouldBroadcast = Request.ShouldBroadcast;
if ((CDB = Candidate->get(Opts.TFS, CandidateShouldBroadcast,
Request.FreshTime, Request.FreshTimeMissing))) {
DirCache = Candidate;
ShouldBroadcast = CandidateShouldBroadcast;
break;
}
for (DirectoryCache *Candidate : getDirectoryCaches(SearchDirs)) {
bool CandidateShouldBroadcast = Request.ShouldBroadcast;
if ((CDB = Candidate->get(Opts.TFS, CandidateShouldBroadcast,
Request.FreshTime, Request.FreshTimeMissing))) {
DirCache = Candidate;
ShouldBroadcast = CandidateShouldBroadcast;
break;
}
}

Expand Down Expand Up @@ -566,69 +573,176 @@ class DirectoryBasedGlobalCompilationDatabase::BroadcastThread {
}
};

void DirectoryBasedGlobalCompilationDatabase::BroadcastThread::process(
const CDBLookupResult &T) {
vlog("Broadcasting compilation database from {0}", T.PI.SourceRoot);
// The DirBasedCDB associates each file with a specific CDB.
// When a CDB is discovered, it may claim to describe files that we associate
// with a different CDB. We do not want to broadcast discovery of these, and
// trigger background indexing of them.
//
// We must filter the list, and check whether they are associated with this CDB.
// This class attempts to do so efficiently.
//
// Roughly, it:
// - loads the config for each file, and determines the relevant search path
// - gathers all directories that are part of any search path
// - (lazily) checks for a CDB in each such directory at most once
// - walks the search path for each file and determines whether to include it.
class DirectoryBasedGlobalCompilationDatabase::BroadcastThread::Filter {
llvm::StringRef ThisDir;
DirectoryBasedGlobalCompilationDatabase &Parent;

std::vector<std::string> AllFiles = T.CDB->getAllFiles();
// We assume CDB in CompileCommandsDir owns all of its entries, since we don't
// perform any search in parent paths whenever it is set.
if (Parent.OnlyDirCache) {
assert(Parent.OnlyDirCache->Path == T.PI.SourceRoot &&
"Trying to broadcast a CDB outside of CompileCommandsDir!");
Parent.OnCommandChanged.broadcast(std::move(AllFiles));
return;
// Keep track of all directories we might check for CDBs.
struct DirInfo {
DirectoryCache *Cache = nullptr;
enum { Unknown, Missing, TargetCDB, OtherCDB } State = Unknown;
DirInfo *Parent = nullptr;
};
llvm::StringMap<DirInfo> Dirs;

// A search path starts at a directory, and either includes ancestors or not.
using SearchPath = llvm::PointerIntPair<DirInfo *, 1>;

// Add all ancestor directories of FilePath to the tracked set.
// Returns the immediate parent of the file.
DirInfo *addParents(llvm::StringRef FilePath) {
DirInfo *Leaf = nullptr;
DirInfo *Child = nullptr;
actOnAllParentDirectories(FilePath, [&](llvm::StringRef Dir) {
auto &Info = Dirs[Dir];
// If this is the first iteration, then this node is the overall result.
if (!Leaf)
Leaf = &Info;
// Fill in the parent link from the previous iteration to this parent.
if (Child)
Child->Parent = &Info;
// Keep walking, whether we inserted or not, if parent link is missing.
// (If it's present, parent links must be present up to the root, so stop)
Child = &Info;
return Info.Parent != nullptr;
});
return Leaf;
}

// Uniquify all parent directories of all files.
llvm::StringMap<bool> DirectoryHasCDB;
std::vector<llvm::StringRef> FileAncestors;
for (llvm::StringRef File : AllFiles) {
actOnAllParentDirectories(File, [&](PathRef Path) {
auto It = DirectoryHasCDB.try_emplace(Path);
// Already seen this path, and all of its parents.
if (!It.second)
return true;
// Populates DirInfo::Cache (and State, if it is TargetCDB).
void grabCaches() {
// Fast path out if there were no files, or CDB loading is off.
if (Dirs.empty())
return;

FileAncestors.push_back(It.first->getKey());
return pathEqual(Path, T.PI.SourceRoot);
});
std::vector<llvm::StringRef> DirKeys;
std::vector<DirInfo *> DirValues;
DirKeys.reserve(Dirs.size() + 1);
DirValues.reserve(Dirs.size());
for (auto &E : Dirs) {
DirKeys.push_back(E.first());
DirValues.push_back(&E.second);
}

// Also look up the cache entry for the CDB we're broadcasting.
// Comparing DirectoryCache pointers is more robust than checking string
// equality, e.g. reuses the case-sensitivity handling.
DirKeys.push_back(ThisDir);
auto DirCaches = Parent.getDirectoryCaches(DirKeys);
const DirectoryCache *ThisCache = DirCaches.back();
DirCaches.pop_back();
DirKeys.pop_back();

for (unsigned I = 0; I < DirKeys.size(); ++I) {
DirValues[I]->Cache = DirCaches[I];
if (DirCaches[I] == ThisCache)
DirValues[I]->State = DirInfo::TargetCDB;
}
}
// Work out which ones have CDBs in them.
// Given that we know that CDBs have been moved/generated, don't trust caches.
// (This should be rare, so it's OK to add a little latency).
constexpr auto IgnoreCache = std::chrono::steady_clock::time_point::max();
auto DirectoryCaches = Parent.getDirectoryCaches(FileAncestors);
assert(DirectoryCaches.size() == FileAncestors.size());
for (unsigned I = 0; I < DirectoryCaches.size(); ++I) {
bool ShouldBroadcast = false;
if (ShouldStop.load(std::memory_order_acquire)) {
log("Giving up on broadcasting CDB, as we're shutting down");
return;

// Should we include a file from this search path?
bool shouldInclude(SearchPath P) {
DirInfo *Info = P.getPointer();
if (!Info)
return false;
if (Info->State == DirInfo::Unknown) {
assert(Info->Cache && "grabCaches() should have filled this");
// Given that we know that CDBs have been moved/generated, don't trust
// caches. (This should be rare, so it's OK to add a little latency).
constexpr auto IgnoreCache = std::chrono::steady_clock::time_point::max();
// Don't broadcast CDBs discovered while broadcasting!
bool ShouldBroadcast = false;
bool Exists =
nullptr != Info->Cache->get(Parent.Opts.TFS, ShouldBroadcast,
/*FreshTime=*/IgnoreCache,
/*FreshTimeMissing=*/IgnoreCache);
Info->State = Exists ? DirInfo::OtherCDB : DirInfo::Missing;
}
if (DirectoryCaches[I]->get(Parent.Opts.TFS, ShouldBroadcast,
/*FreshTime=*/IgnoreCache,
/*FreshTimeMissing=*/IgnoreCache))
DirectoryHasCDB.find(FileAncestors[I])->setValue(true);
// If we have a CDB, include the file if it's the target CDB only.
if (Info->State != DirInfo::Missing)
return Info->State == DirInfo::TargetCDB;
// If we have no CDB and no relevant parent, don't include the file.
if (!P.getInt() || !Info->Parent)
return false;
// Walk up to the next parent.
return shouldInclude(SearchPath(Info->Parent, 1));
}

std::vector<std::string> GovernedFiles;
for (llvm::StringRef File : AllFiles) {
// A file is governed by this CDB if lookup for the file would find it.
// Independent of whether it has an entry for that file or not.
actOnAllParentDirectories(File, [&](PathRef Path) {
if (DirectoryHasCDB.lookup(Path)) {
if (pathEqual(Path, T.PI.SourceRoot))
// Make sure listeners always get a canonical path for the file.
GovernedFiles.push_back(removeDots(File));
// Stop as soon as we hit a CDB.
public:
Filter(llvm::StringRef ThisDir,
DirectoryBasedGlobalCompilationDatabase &Parent)
: ThisDir(ThisDir), Parent(Parent) {}

std::vector<std::string> filter(std::vector<std::string> AllFiles,
std::atomic<bool> &ShouldStop) {
std::vector<std::string> Filtered;
// Allow for clean early-exit of the slow parts.
auto ExitEarly = [&] {
if (ShouldStop.load(std::memory_order_acquire)) {
log("Giving up on broadcasting CDB, as we're shutting down");
Filtered.clear();
return true;
}
return false;
});
};
// Compute search path for each file.
std::vector<SearchPath> SearchPaths(AllFiles.size());
for (unsigned I = 0; I < AllFiles.size(); ++I) {
if (Parent.Opts.CompileCommandsDir) { // FIXME: unify with config
SearchPaths[I].setPointer(
&Dirs[Parent.Opts.CompileCommandsDir.getValue()]);
continue;
}
if (ExitEarly()) // loading config may be slow
return Filtered;
WithContext WithProvidedContent(Parent.Opts.ContextProvider(AllFiles[I]));
const Config::CDBSearchSpec &Spec =
Config::current().CompileFlags.CDBSearch;
switch (Spec.Policy) {
case Config::CDBSearchSpec::NoCDBSearch:
break;
case Config::CDBSearchSpec::Ancestors:
SearchPaths[I].setInt(/*Recursive=*/1);
SearchPaths[I].setPointer(addParents(AllFiles[I]));
break;
case Config::CDBSearchSpec::FixedDir:
SearchPaths[I].setPointer(&Dirs[Spec.FixedCDBPath.getValue()]);
break;
}
}
// Get the CDB cache for each dir on the search path, but don't load yet.
grabCaches();
// Now work out which files we want to keep, loading CDBs where needed.
for (unsigned I = 0; I < AllFiles.size(); ++I) {
if (ExitEarly()) // loading CDBs may be slow
return Filtered;
if (shouldInclude(SearchPaths[I]))
Filtered.push_back(std::move(AllFiles[I]));
}
return Filtered;
}
};

Parent.OnCommandChanged.broadcast(std::move(GovernedFiles));
void DirectoryBasedGlobalCompilationDatabase::BroadcastThread::process(
const CDBLookupResult &T) {
vlog("Broadcasting compilation database from {0}", T.PI.SourceRoot);
std::vector<std::string> GovernedFiles =
Filter(T.PI.SourceRoot, Parent).filter(T.CDB->getAllFiles(), ShouldStop);
if (!GovernedFiles.empty())
Parent.OnCommandChanged.broadcast(std::move(GovernedFiles));
}

void DirectoryBasedGlobalCompilationDatabase::broadcastCDB(
Expand Down
8 changes: 3 additions & 5 deletions clang-tools-extra/clangd/GlobalCompilationDatabase.h
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,10 @@ class DirectoryBasedGlobalCompilationDatabase
// (This is more expensive to check frequently, as we check many locations).
std::chrono::steady_clock::duration RevalidateMissingAfter =
std::chrono::seconds(30);
// Used to provide per-file configuration.
std::function<Context(llvm::StringRef)> ContextProvider;
// Only look for a compilation database in this one fixed directory.
// FIXME: fold this into config/context mechanism.
llvm::Optional<Path> CompileCommandsDir;
};

Expand All @@ -126,14 +129,9 @@ class DirectoryBasedGlobalCompilationDatabase
Options Opts;

class DirectoryCache;
// If there's an explicit CompileCommandsDir, cache of the CDB found there.
mutable std::unique_ptr<DirectoryCache> OnlyDirCache;

// Keyed by possibly-case-folded directory path.
// We can hand out pointers as they're stable and entries are never removed.
// Empty if CompileCommandsDir is given (OnlyDirCache is used instead).
mutable llvm::StringMap<DirectoryCache> DirCaches;
// DirCaches access must be locked (unlike OnlyDirCache, which is threadsafe).
mutable std::mutex DirCachesMutex;

std::vector<DirectoryCache *>
Expand Down
27 changes: 27 additions & 0 deletions clang-tools-extra/clangd/unittests/ClangdLSPServerTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,33 @@ TEST_F(LSPTest, IncomingCalls) {
EXPECT_EQ(From["name"], "caller1");
}

TEST_F(LSPTest, CDBConfigIntegration) {
auto CfgProvider =
config::Provider::fromAncestorRelativeYAMLFiles(".clangd", FS);
Opts.ConfigProvider = CfgProvider.get();

// Map bar.cpp to a different compilation database which defines FOO->BAR.
FS.Files[".clangd"] = R"yaml(
If:
PathMatch: bar.cpp
CompileFlags:
CompilationDatabase: bar
)yaml";
FS.Files["bar/compile_flags.txt"] = "-DFOO=BAR";

auto &Client = start();
// foo.cpp gets parsed as normal.
Client.didOpen("foo.cpp", "int x = FOO;");
EXPECT_THAT(Client.diagnostics("foo.cpp"),
llvm::ValueIs(testing::ElementsAre(
DiagMessage("Use of undeclared identifier 'FOO'"))));
// bar.cpp shows the configured compile command.
Client.didOpen("bar.cpp", "int x = FOO;");
EXPECT_THAT(Client.diagnostics("bar.cpp"),
llvm::ValueIs(testing::ElementsAre(
DiagMessage("Use of undeclared identifier 'BAR'"))));
}

} // namespace
} // namespace clangd
} // namespace clang
40 changes: 40 additions & 0 deletions clang-tools-extra/clangd/unittests/ConfigCompileTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,46 @@ TEST_F(ConfigCompileTests, CompileCommands) {
EXPECT_THAT(Argv, ElementsAre("clang", "a.cc", "-foo"));
}

TEST_F(ConfigCompileTests, CompilationDatabase) {
Frag.CompileFlags.CompilationDatabase.emplace("None");
EXPECT_TRUE(compileAndApply());
EXPECT_EQ(Conf.CompileFlags.CDBSearch.Policy,
Config::CDBSearchSpec::NoCDBSearch);

Frag.CompileFlags.CompilationDatabase.emplace("Ancestors");
EXPECT_TRUE(compileAndApply());
EXPECT_EQ(Conf.CompileFlags.CDBSearch.Policy,
Config::CDBSearchSpec::Ancestors);

// Relative path not allowed without directory set.
Frag.CompileFlags.CompilationDatabase.emplace("Something");
EXPECT_TRUE(compileAndApply());
EXPECT_EQ(Conf.CompileFlags.CDBSearch.Policy,
Config::CDBSearchSpec::Ancestors)
<< "default value";
EXPECT_THAT(Diags.Diagnostics,
ElementsAre(DiagMessage(
"CompilationDatabase must be an absolute path, because this "
"fragment is not associated with any directory.")));

// Relative path allowed if directory is set.
Frag.Source.Directory = testRoot();
EXPECT_TRUE(compileAndApply());
EXPECT_EQ(Conf.CompileFlags.CDBSearch.Policy,
Config::CDBSearchSpec::FixedDir);
EXPECT_EQ(Conf.CompileFlags.CDBSearch.FixedCDBPath, testPath("Something"));
EXPECT_THAT(Diags.Diagnostics, IsEmpty());

// Absolute path allowed.
Frag.Source.Directory.clear();
Frag.CompileFlags.CompilationDatabase.emplace(testPath("Something2"));
EXPECT_TRUE(compileAndApply());
EXPECT_EQ(Conf.CompileFlags.CDBSearch.Policy,
Config::CDBSearchSpec::FixedDir);
EXPECT_EQ(Conf.CompileFlags.CDBSearch.FixedCDBPath, testPath("Something2"));
EXPECT_THAT(Diags.Diagnostics, IsEmpty());
}

TEST_F(ConfigCompileTests, Index) {
Frag.Index.Background.emplace("Skip");
EXPECT_TRUE(compileAndApply());
Expand Down
102 changes: 101 additions & 1 deletion clang-tools-extra/clangd/unittests/GlobalCompilationDatabaseTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

#include "GlobalCompilationDatabase.h"

#include "Config.h"
#include "Matchers.h"
#include "TestFS.h"
#include "support/Path.h"
Expand Down Expand Up @@ -205,10 +206,12 @@ TEST(GlobalCompilationDatabaseTest, DiscoveryWithNestedCDBs) {
llvm::formatv(CDBOuter, llvm::sys::path::convert_to_slash(testRoot()));
FS.Files[testPath("build/compile_commands.json")] =
llvm::formatv(CDBInner, llvm::sys::path::convert_to_slash(testRoot()));
FS.Files[testPath("foo/compile_flags.txt")] = "-DFOO";

// Note that gen2.cc goes missing with our following model, not sure this
// happens in practice though.
{
SCOPED_TRACE("Default ancestor scanning");
DirectoryBasedGlobalCompilationDatabase DB(FS);
std::vector<std::string> DiscoveredFiles;
auto Sub =
Expand All @@ -227,8 +230,53 @@ TEST(GlobalCompilationDatabaseTest, DiscoveryWithNestedCDBs) {
EXPECT_THAT(DiscoveredFiles, UnorderedElementsAre(EndsWith("gen.cc")));
}

// With a custom compile commands dir.
{
SCOPED_TRACE("With config");
DirectoryBasedGlobalCompilationDatabase::Options Opts(FS);
Opts.ContextProvider = [&](llvm::StringRef Path) {
Config Cfg;
if (Path.endswith("a.cc")) {
// a.cc uses another directory's CDB, so it won't be discovered.
Cfg.CompileFlags.CDBSearch.Policy = Config::CDBSearchSpec::FixedDir;
Cfg.CompileFlags.CDBSearch.FixedCDBPath = testPath("foo");
} else if (Path.endswith("gen.cc")) {
// gen.cc has CDB search disabled, so it won't be discovered.
Cfg.CompileFlags.CDBSearch.Policy = Config::CDBSearchSpec::NoCDBSearch;
} else if (Path.endswith("gen2.cc")) {
// gen2.cc explicitly lists this directory, so it will be discovered.
Cfg.CompileFlags.CDBSearch.Policy = Config::CDBSearchSpec::FixedDir;
Cfg.CompileFlags.CDBSearch.FixedCDBPath = testRoot();
}
return Context::current().derive(Config::Key, std::move(Cfg));
};
DirectoryBasedGlobalCompilationDatabase DB(Opts);
std::vector<std::string> DiscoveredFiles;
auto Sub =
DB.watch([&DiscoveredFiles](const std::vector<std::string> Changes) {
DiscoveredFiles = Changes;
});

// Does not use the root CDB, so no broadcast.
auto Cmd = DB.getCompileCommand(testPath("build/../a.cc"));
ASSERT_TRUE(Cmd.hasValue());
EXPECT_THAT(Cmd->CommandLine, Contains("-DFOO")) << "a.cc uses foo/ CDB";
ASSERT_TRUE(DB.blockUntilIdle(timeoutSeconds(10)));
EXPECT_THAT(DiscoveredFiles, IsEmpty()) << "Root CDB not discovered yet";

// No special config for b.cc, so we trigger broadcast of the root CDB.
DB.getCompileCommand(testPath("b.cc"));
ASSERT_TRUE(DB.blockUntilIdle(timeoutSeconds(10)));
EXPECT_THAT(DiscoveredFiles, ElementsAre(testPath("build/gen2.cc")));
DiscoveredFiles.clear();

// No CDB search so no discovery/broadcast triggered for build/ CDB.
DB.getCompileCommand(testPath("build/gen.cc"));
ASSERT_TRUE(DB.blockUntilIdle(timeoutSeconds(10)));
EXPECT_THAT(DiscoveredFiles, IsEmpty());
}

{
SCOPED_TRACE("With custom compile commands dir");
DirectoryBasedGlobalCompilationDatabase::Options Opts(FS);
Opts.CompileCommandsDir = testRoot();
DirectoryBasedGlobalCompilationDatabase DB(Opts);
Expand Down Expand Up @@ -294,6 +342,58 @@ TEST(GlobalCompilationDatabaseTest, CompileFlagsDirectory) {
EXPECT_EQ(testPath("x"), Commands.getValue().Directory);
}

MATCHER_P(hasArg, Flag, "") {
if (!arg.hasValue()) {
*result_listener << "command is null";
return false;
}
if (!llvm::is_contained(arg->CommandLine, Flag)) {
*result_listener << "flags are " << llvm::join(arg->CommandLine, " ");
return false;
}
return true;
}

TEST(GlobalCompilationDatabaseTest, Config) {
MockFS FS;
FS.Files[testPath("x/compile_flags.txt")] = "-DX";
FS.Files[testPath("x/y/z/compile_flags.txt")] = "-DZ";

Config::CDBSearchSpec Spec;
DirectoryBasedGlobalCompilationDatabase::Options Opts(FS);
Opts.ContextProvider = [&](llvm::StringRef Path) {
Config C;
C.CompileFlags.CDBSearch = Spec;
return Context::current().derive(Config::Key, std::move(C));
};
DirectoryBasedGlobalCompilationDatabase CDB(Opts);

// Default ancestor behavior.
EXPECT_FALSE(CDB.getCompileCommand(testPath("foo.cc")));
EXPECT_THAT(CDB.getCompileCommand(testPath("x/foo.cc")), hasArg("-DX"));
EXPECT_THAT(CDB.getCompileCommand(testPath("x/y/foo.cc")), hasArg("-DX"));
EXPECT_THAT(CDB.getCompileCommand(testPath("x/y/z/foo.cc")), hasArg("-DZ"));

Spec.Policy = Config::CDBSearchSpec::NoCDBSearch;
EXPECT_FALSE(CDB.getCompileCommand(testPath("foo.cc")));
EXPECT_FALSE(CDB.getCompileCommand(testPath("x/foo.cc")));
EXPECT_FALSE(CDB.getCompileCommand(testPath("x/y/foo.cc")));
EXPECT_FALSE(CDB.getCompileCommand(testPath("x/y/z/foo.cc")));

Spec.Policy = Config::CDBSearchSpec::FixedDir;
Spec.FixedCDBPath = testPath("w"); // doesn't exist
EXPECT_FALSE(CDB.getCompileCommand(testPath("foo.cc")));
EXPECT_FALSE(CDB.getCompileCommand(testPath("x/foo.cc")));
EXPECT_FALSE(CDB.getCompileCommand(testPath("x/y/foo.cc")));
EXPECT_FALSE(CDB.getCompileCommand(testPath("x/y/z/foo.cc")));

Spec.FixedCDBPath = testPath("x/y/z");
EXPECT_THAT(CDB.getCompileCommand(testPath("foo.cc")), hasArg("-DZ"));
EXPECT_THAT(CDB.getCompileCommand(testPath("x/foo.cc")), hasArg("-DZ"));
EXPECT_THAT(CDB.getCompileCommand(testPath("x/y/foo.cc")), hasArg("-DZ"));
EXPECT_THAT(CDB.getCompileCommand(testPath("x/y/z/foo.cc")), hasArg("-DZ"));
}

TEST(GlobalCompilationDatabaseTest, NonCanonicalFilenames) {
OverlayCDB DB(nullptr);
std::vector<std::string> DiscoveredFiles;
Expand Down
11 changes: 11 additions & 0 deletions llvm/lib/Transforms/Utils/ScalarEvolutionExpander.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1440,6 +1440,17 @@ Value *SCEVExpander::expandAddRecExprLiterally(const SCEVAddRecExpr *S) {
assert(LatchBlock && "PostInc mode requires a unique loop latch!");
Result = PN->getIncomingValueForBlock(LatchBlock);

// We might be introducing a new use of the post-inc IV that is not poison
// safe, in which case we should drop poison generating flags. Only keep
// those flags for which SCEV has proven that they always hold.
if (isa<OverflowingBinaryOperator>(Result)) {
auto *I = cast<Instruction>(Result);
if (!S->hasNoUnsignedWrap())
I->setHasNoUnsignedWrap(false);
if (!S->hasNoSignedWrap())
I->setHasNoSignedWrap(false);
}

// For an expansion to use the postinc form, the client must call
// expandCodeFor with an InsertPoint that is either outside the PostIncLoop
// or dominated by IVIncInsertPos.
Expand Down
58 changes: 27 additions & 31 deletions llvm/test/CodeGen/Thumb2/LowOverheadLoops/fast-fp-loops.ll
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,11 @@ define arm_aapcs_vfpcc void @fast_float_mul(float* nocapture %a, float* nocaptur
; CHECK-NEXT: beq .LBB0_4
; CHECK-NEXT: @ %bb.2: @ %for.body.preheader
; CHECK-NEXT: subs r5, r3, #1
; CHECK-NEXT: and r7, r3, #3
; CHECK-NEXT: and lr, r3, #3
; CHECK-NEXT: cmp r5, #3
; CHECK-NEXT: bhs .LBB0_6
; CHECK-NEXT: @ %bb.3:
; CHECK-NEXT: mov.w r12, #0
; CHECK-NEXT: movs r3, #0
; CHECK-NEXT: b .LBB0_8
; CHECK-NEXT: .LBB0_4: @ %vector.ph
; CHECK-NEXT: mov.w r12, #0
Expand All @@ -46,44 +46,40 @@ define arm_aapcs_vfpcc void @fast_float_mul(float* nocapture %a, float* nocaptur
; CHECK-NEXT: letp lr, .LBB0_5
; CHECK-NEXT: b .LBB0_11
; CHECK-NEXT: .LBB0_6: @ %for.body.preheader.new
; CHECK-NEXT: bic r3, r3, #3
; CHECK-NEXT: movs r5, #1
; CHECK-NEXT: subs r3, #4
; CHECK-NEXT: mov.w r12, #0
; CHECK-NEXT: add.w lr, r5, r3, lsr #2
; CHECK-NEXT: sub.w r12, r3, lr
; CHECK-NEXT: movs r4, #0
; CHECK-NEXT: movs r3, #0
; CHECK-NEXT: dls lr, lr
; CHECK-NEXT: .LBB0_7: @ %for.body
; CHECK-NEXT: @ =>This Inner Loop Header: Depth=1
; CHECK-NEXT: adds r4, r1, r3
; CHECK-NEXT: adds r5, r2, r3
; CHECK-NEXT: adds r6, r0, r3
; CHECK-NEXT: adds r3, #16
; CHECK-NEXT: vldr s0, [r4]
; CHECK-NEXT: add.w r12, r12, #4
; CHECK-NEXT: vldr s2, [r5]
; CHECK-NEXT: adds r5, r1, r4
; CHECK-NEXT: adds r6, r2, r4
; CHECK-NEXT: adds r7, r0, r4
; CHECK-NEXT: adds r3, #4
; CHECK-NEXT: vldr s0, [r5]
; CHECK-NEXT: adds r4, #16
; CHECK-NEXT: vldr s2, [r6]
; CHECK-NEXT: cmp r12, r3
; CHECK-NEXT: vmul.f32 s0, s2, s0
; CHECK-NEXT: vstr s0, [r6]
; CHECK-NEXT: vldr s0, [r4, #4]
; CHECK-NEXT: vldr s2, [r5, #4]
; CHECK-NEXT: vstr s0, [r7]
; CHECK-NEXT: vldr s0, [r5, #4]
; CHECK-NEXT: vldr s2, [r6, #4]
; CHECK-NEXT: vmul.f32 s0, s2, s0
; CHECK-NEXT: vstr s0, [r6, #4]
; CHECK-NEXT: vldr s0, [r4, #8]
; CHECK-NEXT: vldr s2, [r5, #8]
; CHECK-NEXT: vstr s0, [r7, #4]
; CHECK-NEXT: vldr s0, [r5, #8]
; CHECK-NEXT: vldr s2, [r6, #8]
; CHECK-NEXT: vmul.f32 s0, s2, s0
; CHECK-NEXT: vstr s0, [r6, #8]
; CHECK-NEXT: vldr s0, [r4, #12]
; CHECK-NEXT: vldr s2, [r5, #12]
; CHECK-NEXT: vstr s0, [r7, #8]
; CHECK-NEXT: vldr s0, [r5, #12]
; CHECK-NEXT: vldr s2, [r6, #12]
; CHECK-NEXT: vmul.f32 s0, s2, s0
; CHECK-NEXT: vstr s0, [r6, #12]
; CHECK-NEXT: le lr, .LBB0_7
; CHECK-NEXT: vstr s0, [r7, #12]
; CHECK-NEXT: bne .LBB0_7
; CHECK-NEXT: .LBB0_8: @ %for.cond.cleanup.loopexit.unr-lcssa
; CHECK-NEXT: wls lr, r7, .LBB0_11
; CHECK-NEXT: wls lr, lr, .LBB0_11
; CHECK-NEXT: @ %bb.9: @ %for.body.epil.preheader
; CHECK-NEXT: add.w r1, r1, r12, lsl #2
; CHECK-NEXT: add.w r2, r2, r12, lsl #2
; CHECK-NEXT: add.w r0, r0, r12, lsl #2
; CHECK-NEXT: mov lr, r7
; CHECK-NEXT: add.w r1, r1, r3, lsl #2
; CHECK-NEXT: add.w r2, r2, r3, lsl #2
; CHECK-NEXT: add.w r0, r0, r3, lsl #2
; CHECK-NEXT: .LBB0_10: @ %for.body.epil
; CHECK-NEXT: @ =>This Inner Loop Header: Depth=1
; CHECK-NEXT: vldr s0, [r1]
Expand Down
153 changes: 69 additions & 84 deletions llvm/test/CodeGen/Thumb2/LowOverheadLoops/mve-float-loops.ll
Original file line number Diff line number Diff line change
Expand Up @@ -1459,58 +1459,53 @@ define arm_aapcs_vfpcc float @half_half_mac(half* nocapture readonly %a, half* n
; CHECK-NEXT: cbz r2, .LBB9_3
; CHECK-NEXT: @ %bb.1: @ %for.body.preheader
; CHECK-NEXT: subs r3, r2, #1
; CHECK-NEXT: and r5, r2, #3
; CHECK-NEXT: and lr, r2, #3
; CHECK-NEXT: vldr s0, .LCPI9_0
; CHECK-NEXT: cmp r3, #3
; CHECK-NEXT: bhs .LBB9_4
; CHECK-NEXT: @ %bb.2:
; CHECK-NEXT: vldr s0, .LCPI9_0
; CHECK-NEXT: mov.w r12, #0
; CHECK-NEXT: movs r2, #0
; CHECK-NEXT: b .LBB9_6
; CHECK-NEXT: .LBB9_3:
; CHECK-NEXT: vldr s0, .LCPI9_0
; CHECK-NEXT: b .LBB9_9
; CHECK-NEXT: .LBB9_4: @ %for.body.preheader.new
; CHECK-NEXT: bic r2, r2, #3
; CHECK-NEXT: movs r3, #1
; CHECK-NEXT: subs r2, #4
; CHECK-NEXT: vldr s0, .LCPI9_0
; CHECK-NEXT: mov.w r12, #0
; CHECK-NEXT: add.w lr, r3, r2, lsr #2
; CHECK-NEXT: sub.w r12, r2, lr
; CHECK-NEXT: movs r3, #0
; CHECK-NEXT: dls lr, lr
; CHECK-NEXT: movs r2, #0
; CHECK-NEXT: .LBB9_5: @ %for.body
; CHECK-NEXT: @ =>This Inner Loop Header: Depth=1
; CHECK-NEXT: adds r4, r0, r3
; CHECK-NEXT: adds r2, r1, r3
; CHECK-NEXT: vldr.16 s2, [r2, #6]
; CHECK-NEXT: vldr.16 s4, [r4, #6]
; CHECK-NEXT: vldr.16 s6, [r4, #4]
; CHECK-NEXT: vldr.16 s8, [r4, #2]
; CHECK-NEXT: adds r5, r0, r3
; CHECK-NEXT: adds r4, r1, r3
; CHECK-NEXT: vldr.16 s2, [r4, #6]
; CHECK-NEXT: vldr.16 s4, [r5, #6]
; CHECK-NEXT: vldr.16 s6, [r5, #4]
; CHECK-NEXT: vldr.16 s8, [r5, #2]
; CHECK-NEXT: vmul.f16 s2, s4, s2
; CHECK-NEXT: vldr.16 s4, [r2, #4]
; CHECK-NEXT: vldr.16 s10, [r4]
; CHECK-NEXT: vldr.16 s4, [r4, #4]
; CHECK-NEXT: vldr.16 s10, [r5]
; CHECK-NEXT: vcvtb.f32.f16 s2, s2
; CHECK-NEXT: vmul.f16 s4, s6, s4
; CHECK-NEXT: vldr.16 s6, [r2, #2]
; CHECK-NEXT: vldr.16 s6, [r4, #2]
; CHECK-NEXT: vcvtb.f32.f16 s4, s4
; CHECK-NEXT: adds r3, #8
; CHECK-NEXT: adds r2, #4
; CHECK-NEXT: vmul.f16 s6, s8, s6
; CHECK-NEXT: vldr.16 s8, [r2]
; CHECK-NEXT: vldr.16 s8, [r4]
; CHECK-NEXT: vcvtb.f32.f16 s6, s6
; CHECK-NEXT: add.w r12, r12, #4
; CHECK-NEXT: adds r3, #8
; CHECK-NEXT: vmul.f16 s8, s10, s8
; CHECK-NEXT: cmp r12, r2
; CHECK-NEXT: vcvtb.f32.f16 s8, s8
; CHECK-NEXT: vadd.f32 s0, s0, s8
; CHECK-NEXT: vadd.f32 s0, s0, s6
; CHECK-NEXT: vadd.f32 s0, s0, s4
; CHECK-NEXT: vadd.f32 s0, s0, s2
; CHECK-NEXT: le lr, .LBB9_5
; CHECK-NEXT: bne .LBB9_5
; CHECK-NEXT: .LBB9_6: @ %for.cond.cleanup.loopexit.unr-lcssa
; CHECK-NEXT: wls lr, r5, .LBB9_9
; CHECK-NEXT: wls lr, lr, .LBB9_9
; CHECK-NEXT: @ %bb.7: @ %for.body.epil.preheader
; CHECK-NEXT: add.w r0, r0, r12, lsl #1
; CHECK-NEXT: add.w r1, r1, r12, lsl #1
; CHECK-NEXT: mov lr, r5
; CHECK-NEXT: add.w r0, r0, r2, lsl #1
; CHECK-NEXT: add.w r1, r1, r2, lsl #1
; CHECK-NEXT: .LBB9_8: @ %for.body.epil
; CHECK-NEXT: @ =>This Inner Loop Header: Depth=1
; CHECK-NEXT: vldr.16 s2, [r1]
Expand Down Expand Up @@ -1616,58 +1611,53 @@ define arm_aapcs_vfpcc float @half_half_acc(half* nocapture readonly %a, half* n
; CHECK-NEXT: cbz r2, .LBB10_3
; CHECK-NEXT: @ %bb.1: @ %for.body.preheader
; CHECK-NEXT: subs r3, r2, #1
; CHECK-NEXT: and r5, r2, #3
; CHECK-NEXT: and lr, r2, #3
; CHECK-NEXT: vldr s0, .LCPI10_0
; CHECK-NEXT: cmp r3, #3
; CHECK-NEXT: bhs .LBB10_4
; CHECK-NEXT: @ %bb.2:
; CHECK-NEXT: vldr s0, .LCPI10_0
; CHECK-NEXT: mov.w r12, #0
; CHECK-NEXT: movs r2, #0
; CHECK-NEXT: b .LBB10_6
; CHECK-NEXT: .LBB10_3:
; CHECK-NEXT: vldr s0, .LCPI10_0
; CHECK-NEXT: b .LBB10_9
; CHECK-NEXT: .LBB10_4: @ %for.body.preheader.new
; CHECK-NEXT: bic r2, r2, #3
; CHECK-NEXT: movs r3, #1
; CHECK-NEXT: subs r2, #4
; CHECK-NEXT: vldr s0, .LCPI10_0
; CHECK-NEXT: mov.w r12, #0
; CHECK-NEXT: add.w lr, r3, r2, lsr #2
; CHECK-NEXT: sub.w r12, r2, lr
; CHECK-NEXT: movs r3, #0
; CHECK-NEXT: dls lr, lr
; CHECK-NEXT: movs r2, #0
; CHECK-NEXT: .LBB10_5: @ %for.body
; CHECK-NEXT: @ =>This Inner Loop Header: Depth=1
; CHECK-NEXT: adds r4, r0, r3
; CHECK-NEXT: adds r2, r1, r3
; CHECK-NEXT: vldr.16 s2, [r2, #6]
; CHECK-NEXT: vldr.16 s4, [r4, #6]
; CHECK-NEXT: vldr.16 s6, [r4, #4]
; CHECK-NEXT: vldr.16 s8, [r4, #2]
; CHECK-NEXT: adds r5, r0, r3
; CHECK-NEXT: adds r4, r1, r3
; CHECK-NEXT: vldr.16 s2, [r4, #6]
; CHECK-NEXT: vldr.16 s4, [r5, #6]
; CHECK-NEXT: vldr.16 s6, [r5, #4]
; CHECK-NEXT: vldr.16 s8, [r5, #2]
; CHECK-NEXT: vadd.f16 s2, s4, s2
; CHECK-NEXT: vldr.16 s4, [r2, #4]
; CHECK-NEXT: vldr.16 s10, [r4]
; CHECK-NEXT: vldr.16 s4, [r4, #4]
; CHECK-NEXT: vldr.16 s10, [r5]
; CHECK-NEXT: vcvtb.f32.f16 s2, s2
; CHECK-NEXT: vadd.f16 s4, s6, s4
; CHECK-NEXT: vldr.16 s6, [r2, #2]
; CHECK-NEXT: vldr.16 s6, [r4, #2]
; CHECK-NEXT: vcvtb.f32.f16 s4, s4
; CHECK-NEXT: adds r3, #8
; CHECK-NEXT: adds r2, #4
; CHECK-NEXT: vadd.f16 s6, s8, s6
; CHECK-NEXT: vldr.16 s8, [r2]
; CHECK-NEXT: vldr.16 s8, [r4]
; CHECK-NEXT: vcvtb.f32.f16 s6, s6
; CHECK-NEXT: add.w r12, r12, #4
; CHECK-NEXT: adds r3, #8
; CHECK-NEXT: vadd.f16 s8, s10, s8
; CHECK-NEXT: cmp r12, r2
; CHECK-NEXT: vcvtb.f32.f16 s8, s8
; CHECK-NEXT: vadd.f32 s0, s0, s8
; CHECK-NEXT: vadd.f32 s0, s0, s6
; CHECK-NEXT: vadd.f32 s0, s0, s4
; CHECK-NEXT: vadd.f32 s0, s0, s2
; CHECK-NEXT: le lr, .LBB10_5
; CHECK-NEXT: bne .LBB10_5
; CHECK-NEXT: .LBB10_6: @ %for.cond.cleanup.loopexit.unr-lcssa
; CHECK-NEXT: wls lr, r5, .LBB10_9
; CHECK-NEXT: wls lr, lr, .LBB10_9
; CHECK-NEXT: @ %bb.7: @ %for.body.epil.preheader
; CHECK-NEXT: add.w r0, r0, r12, lsl #1
; CHECK-NEXT: add.w r1, r1, r12, lsl #1
; CHECK-NEXT: mov lr, r5
; CHECK-NEXT: add.w r0, r0, r2, lsl #1
; CHECK-NEXT: add.w r1, r1, r2, lsl #1
; CHECK-NEXT: .LBB10_8: @ %for.body.epil
; CHECK-NEXT: @ =>This Inner Loop Header: Depth=1
; CHECK-NEXT: vldr.16 s2, [r1]
Expand Down Expand Up @@ -1773,65 +1763,60 @@ define arm_aapcs_vfpcc float @half_short_mac(half* nocapture readonly %a, i16* n
; CHECK-NEXT: cbz r2, .LBB11_3
; CHECK-NEXT: @ %bb.1: @ %for.body.preheader
; CHECK-NEXT: subs r3, r2, #1
; CHECK-NEXT: and r6, r2, #3
; CHECK-NEXT: and lr, r2, #3
; CHECK-NEXT: vldr s0, .LCPI11_0
; CHECK-NEXT: cmp r3, #3
; CHECK-NEXT: bhs .LBB11_4
; CHECK-NEXT: @ %bb.2:
; CHECK-NEXT: vldr s0, .LCPI11_0
; CHECK-NEXT: mov.w r12, #0
; CHECK-NEXT: movs r2, #0
; CHECK-NEXT: b .LBB11_6
; CHECK-NEXT: .LBB11_3:
; CHECK-NEXT: vldr s0, .LCPI11_0
; CHECK-NEXT: b .LBB11_9
; CHECK-NEXT: .LBB11_4: @ %for.body.preheader.new
; CHECK-NEXT: bic r2, r2, #3
; CHECK-NEXT: movs r3, #1
; CHECK-NEXT: subs r2, #4
; CHECK-NEXT: vldr s0, .LCPI11_0
; CHECK-NEXT: mov.w r12, #0
; CHECK-NEXT: add.w lr, r3, r2, lsr #2
; CHECK-NEXT: sub.w r12, r2, lr
; CHECK-NEXT: adds r3, r1, #4
; CHECK-NEXT: dls lr, lr
; CHECK-NEXT: adds r2, r0, #4
; CHECK-NEXT: adds r4, r0, #4
; CHECK-NEXT: movs r2, #0
; CHECK-NEXT: .LBB11_5: @ %for.body
; CHECK-NEXT: @ =>This Inner Loop Header: Depth=1
; CHECK-NEXT: ldrsh.w r4, [r3, #2]
; CHECK-NEXT: vldr.16 s2, [r2, #2]
; CHECK-NEXT: add.w r12, r12, #4
; CHECK-NEXT: vmov s4, r4
; CHECK-NEXT: ldrsh r4, [r3], #8
; CHECK-NEXT: ldrsh.w r5, [r3, #2]
; CHECK-NEXT: vldr.16 s2, [r4, #2]
; CHECK-NEXT: adds r2, #4
; CHECK-NEXT: cmp r12, r2
; CHECK-NEXT: vmov s4, r5
; CHECK-NEXT: ldrsh r5, [r3], #8
; CHECK-NEXT: vcvt.f16.s32 s4, s4
; CHECK-NEXT: ldrsh r5, [r3, #-10]
; CHECK-NEXT: ldrsh r6, [r3, #-10]
; CHECK-NEXT: vmul.f16 s2, s2, s4
; CHECK-NEXT: vmov s6, r4
; CHECK-NEXT: vldr.16 s4, [r2]
; CHECK-NEXT: vmov s6, r5
; CHECK-NEXT: vldr.16 s4, [r4]
; CHECK-NEXT: vcvt.f16.s32 s6, s6
; CHECK-NEXT: ldrsh r4, [r3, #-12]
; CHECK-NEXT: ldrsh r5, [r3, #-12]
; CHECK-NEXT: vmul.f16 s4, s4, s6
; CHECK-NEXT: vmov s8, r5
; CHECK-NEXT: vldr.16 s6, [r2, #-2]
; CHECK-NEXT: vmov s8, r6
; CHECK-NEXT: vldr.16 s6, [r4, #-2]
; CHECK-NEXT: vcvt.f16.s32 s8, s8
; CHECK-NEXT: vmov s10, r4
; CHECK-NEXT: vmov s10, r5
; CHECK-NEXT: vcvtb.f32.f16 s4, s4
; CHECK-NEXT: vmul.f16 s6, s6, s8
; CHECK-NEXT: vldr.16 s8, [r2, #-4]
; CHECK-NEXT: vldr.16 s8, [r4, #-4]
; CHECK-NEXT: vcvt.f16.s32 s10, s10
; CHECK-NEXT: vcvtb.f32.f16 s6, s6
; CHECK-NEXT: vmul.f16 s8, s8, s10
; CHECK-NEXT: vcvtb.f32.f16 s2, s2
; CHECK-NEXT: vcvtb.f32.f16 s8, s8
; CHECK-NEXT: adds r2, #8
; CHECK-NEXT: add.w r4, r4, #8
; CHECK-NEXT: vadd.f32 s0, s0, s8
; CHECK-NEXT: vadd.f32 s0, s0, s6
; CHECK-NEXT: vadd.f32 s0, s0, s4
; CHECK-NEXT: vadd.f32 s0, s0, s2
; CHECK-NEXT: le lr, .LBB11_5
; CHECK-NEXT: bne .LBB11_5
; CHECK-NEXT: .LBB11_6: @ %for.cond.cleanup.loopexit.unr-lcssa
; CHECK-NEXT: wls lr, r6, .LBB11_9
; CHECK-NEXT: wls lr, lr, .LBB11_9
; CHECK-NEXT: @ %bb.7: @ %for.body.epil.preheader
; CHECK-NEXT: add.w r0, r0, r12, lsl #1
; CHECK-NEXT: add.w r1, r1, r12, lsl #1
; CHECK-NEXT: mov lr, r6
; CHECK-NEXT: add.w r0, r0, r2, lsl #1
; CHECK-NEXT: add.w r1, r1, r2, lsl #1
; CHECK-NEXT: .LBB11_8: @ %for.body.epil
; CHECK-NEXT: @ =>This Inner Loop Header: Depth=1
; CHECK-NEXT: ldrsh r2, [r1], #2
Expand Down
292 changes: 140 additions & 152 deletions llvm/test/CodeGen/Thumb2/LowOverheadLoops/mve-tail-data-types.ll

Large diffs are not rendered by default.

7 changes: 5 additions & 2 deletions llvm/test/Transforms/LoopStrengthReduce/X86/pr46943.ll
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,15 @@ target triple = "x86_64-unknown-linux-gnu"
declare void @use(i8 zeroext)
declare void @use_p(i8*)

; nuw needs to be dropped when switching to post-inc comparison.
define i8 @drop_nuw() {
; CHECK-LABEL: @drop_nuw(
; CHECK-NEXT: entry:
; CHECK-NEXT: br label [[LOOP:%.*]]
; CHECK: loop:
; CHECK-NEXT: [[IV:%.*]] = phi i8 [ 0, [[ENTRY:%.*]] ], [ [[IV_NEXT:%.*]], [[LOOP]] ]
; CHECK-NEXT: call void @use(i8 [[IV]])
; CHECK-NEXT: [[IV_NEXT]] = add nuw i8 [[IV]], 1
; CHECK-NEXT: [[IV_NEXT]] = add i8 [[IV]], 1
; CHECK-NEXT: [[CMP:%.*]] = icmp eq i8 [[IV_NEXT]], 0
; CHECK-NEXT: br i1 [[CMP]], label [[EXIT:%.*]], label [[LOOP]]
; CHECK: exit:
Expand All @@ -36,14 +37,15 @@ exit:
ret i8 %iv
}

; nsw needs to be dropped when switching to post-inc comparison.
define i8 @drop_nsw() {
; CHECK-LABEL: @drop_nsw(
; CHECK-NEXT: entry:
; CHECK-NEXT: br label [[LOOP:%.*]]
; CHECK: loop:
; CHECK-NEXT: [[IV:%.*]] = phi i8 [ 127, [[ENTRY:%.*]] ], [ [[IV_NEXT:%.*]], [[LOOP]] ]
; CHECK-NEXT: call void @use(i8 [[IV]])
; CHECK-NEXT: [[IV_NEXT]] = add nsw i8 [[IV]], -1
; CHECK-NEXT: [[IV_NEXT]] = add i8 [[IV]], -1
; CHECK-NEXT: [[CMP:%.*]] = icmp eq i8 [[IV_NEXT]], 127
; CHECK-NEXT: br i1 [[CMP]], label [[EXIT:%.*]], label [[LOOP]]
; CHECK: exit:
Expand All @@ -65,6 +67,7 @@ exit:
ret i8 %iv
}

; Comparison already in post-inc form, no need to drop nuw.
define i8 @already_postinc() {
; CHECK-LABEL: @already_postinc(
; CHECK-NEXT: entry:
Expand Down
10 changes: 5 additions & 5 deletions llvm/test/Transforms/LoopStrengthReduce/X86/sibling-loops.ll
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ define void @foo(i64 %N) local_unnamed_addr {
; CHECK: do.body:
; CHECK-NEXT: [[I_0:%.*]] = phi i64 [ 0, [[ENTRY:%.*]] ], [ [[INC:%.*]], [[DO_BODY]] ]
; CHECK-NEXT: tail call void @goo(i64 [[I_0]], i64 [[I_0]])
; CHECK-NEXT: [[INC]] = add nuw nsw i64 [[I_0]], 1
; CHECK-NEXT: [[INC]] = add nuw i64 [[I_0]], 1
; CHECK-NEXT: [[T0:%.*]] = load i64, i64* @cond, align 8
; CHECK-NEXT: [[TOBOOL:%.*]] = icmp eq i64 [[T0]], 0
; CHECK-NEXT: br i1 [[TOBOOL]], label [[DO_BODY2_PREHEADER:%.*]], label [[DO_BODY]]
Expand All @@ -27,7 +27,7 @@ define void @foo(i64 %N) local_unnamed_addr {
; CHECK-NEXT: [[I_1:%.*]] = phi i64 [ [[INC3:%.*]], [[DO_BODY2]] ], [ 0, [[DO_BODY2_PREHEADER]] ]
; CHECK-NEXT: [[TMP0:%.*]] = add i64 [[INC]], [[I_1]]
; CHECK-NEXT: tail call void @goo(i64 [[I_1]], i64 [[TMP0]])
; CHECK-NEXT: [[INC3]] = add nuw nsw i64 [[I_1]], 1
; CHECK-NEXT: [[INC3]] = add nuw i64 [[I_1]], 1
; CHECK-NEXT: [[T1:%.*]] = load i64, i64* @cond, align 8
; CHECK-NEXT: [[TOBOOL6:%.*]] = icmp eq i64 [[T1]], 0
; CHECK-NEXT: br i1 [[TOBOOL6]], label [[DO_BODY8_PREHEADER:%.*]], label [[DO_BODY2]]
Expand All @@ -39,7 +39,7 @@ define void @foo(i64 %N) local_unnamed_addr {
; CHECK-NEXT: [[J_2:%.*]] = phi i64 [ [[INC10:%.*]], [[DO_BODY8]] ], [ [[TMP1]], [[DO_BODY8_PREHEADER]] ]
; CHECK-NEXT: tail call void @goo(i64 [[I_2]], i64 [[J_2]])
; CHECK-NEXT: [[INC9]] = add nuw nsw i64 [[I_2]], 1
; CHECK-NEXT: [[INC10]] = add nsw i64 [[J_2]], 1
; CHECK-NEXT: [[INC10]] = add i64 [[J_2]], 1
; CHECK-NEXT: [[T2:%.*]] = load i64, i64* @cond, align 8
; CHECK-NEXT: [[TOBOOL12:%.*]] = icmp eq i64 [[T2]], 0
; CHECK-NEXT: br i1 [[TOBOOL12]], label [[DO_BODY14_PREHEADER:%.*]], label [[DO_BODY8]]
Expand All @@ -50,7 +50,7 @@ define void @foo(i64 %N) local_unnamed_addr {
; CHECK-NEXT: [[J_3:%.*]] = phi i64 [ [[INC16:%.*]], [[DO_BODY14]] ], [ [[INC10]], [[DO_BODY14_PREHEADER]] ]
; CHECK-NEXT: tail call void @goo(i64 [[I_3]], i64 [[J_3]])
; CHECK-NEXT: [[INC15]] = add nuw nsw i64 [[I_3]], 1
; CHECK-NEXT: [[INC16]] = add nsw i64 [[J_3]], 1
; CHECK-NEXT: [[INC16]] = add i64 [[J_3]], 1
; CHECK-NEXT: [[T3:%.*]] = load i64, i64* @cond, align 8
; CHECK-NEXT: [[TOBOOL18:%.*]] = icmp eq i64 [[T3]], 0
; CHECK-NEXT: br i1 [[TOBOOL18]], label [[DO_BODY20_PREHEADER:%.*]], label [[DO_BODY14]]
Expand All @@ -61,7 +61,7 @@ define void @foo(i64 %N) local_unnamed_addr {
; CHECK-NEXT: [[J_4:%.*]] = phi i64 [ [[INC22:%.*]], [[DO_BODY20]] ], [ [[INC16]], [[DO_BODY20_PREHEADER]] ]
; CHECK-NEXT: tail call void @goo(i64 [[I_4]], i64 [[J_4]])
; CHECK-NEXT: [[INC21]] = add nuw nsw i64 [[I_4]], 1
; CHECK-NEXT: [[INC22]] = add nsw i64 [[J_4]], 1
; CHECK-NEXT: [[INC22]] = add i64 [[J_4]], 1
; CHECK-NEXT: [[T4:%.*]] = load i64, i64* @cond, align 8
; CHECK-NEXT: [[TOBOOL24:%.*]] = icmp eq i64 [[T4]], 0
; CHECK-NEXT: br i1 [[TOBOOL24]], label [[DO_BODY26_PREHEADER:%.*]], label [[DO_BODY20]]
Expand Down