mirror of
https://github.com/RPCS3/llvm.git
synced 2025-04-09 17:41:10 +00:00

Here's my second try at making @llvm.assume processing more efficient. My previous attempt, which leveraged operand bundles, r289755, didn't end up working: it did make assume processing more efficient but eliminating the assumption cache made ephemeral value computation too expensive. This is a more-targeted change. We'll keep the assumption cache, but extend it to keep a map of affected values (i.e. values about which an assumption might provide some information) to the corresponding assumption intrinsics. This allows ValueTracking and LVI to find assumptions relevant to the value being queried without scanning all assumptions in the function. The fact that ValueTracking started doing O(number of assumptions in the function) work, for every known-bits query, has become prohibitively expensive in some cases. As discussed during the review, this is a pragmatic fix that, longer term, will likely be replaced by a more-principled solution (perhaps based on an extended SSA form). Differential Revision: https://reviews.llvm.org/D28459 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@291671 91177308-0d34-0410-b5e6-96231b3b80d8
250 lines
8.2 KiB
C++
250 lines
8.2 KiB
C++
//===- AssumptionCache.cpp - Cache finding @llvm.assume calls -------------===//
|
|
//
|
|
// The LLVM Compiler Infrastructure
|
|
//
|
|
// This file is distributed under the University of Illinois Open Source
|
|
// License. See LICENSE.TXT for details.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
//
|
|
// This file contains a pass that keeps track of @llvm.assume intrinsics in
|
|
// the functions of a module.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "llvm/Analysis/AssumptionCache.h"
|
|
#include "llvm/IR/CallSite.h"
|
|
#include "llvm/IR/Dominators.h"
|
|
#include "llvm/IR/Function.h"
|
|
#include "llvm/IR/Instructions.h"
|
|
#include "llvm/IR/IntrinsicInst.h"
|
|
#include "llvm/IR/PassManager.h"
|
|
#include "llvm/IR/PatternMatch.h"
|
|
#include "llvm/Support/Debug.h"
|
|
using namespace llvm;
|
|
using namespace llvm::PatternMatch;
|
|
|
|
SmallVector<WeakVH, 1> &AssumptionCache::getAffectedValues(Value *V) {
|
|
// Try using find_as first to avoid creating extra value handles just for the
|
|
// purpose of doing the lookup.
|
|
auto AVI = AffectedValues.find_as(V);
|
|
if (AVI != AffectedValues.end())
|
|
return AVI->second;
|
|
|
|
auto AVIP = AffectedValues.insert({
|
|
AffectedValueCallbackVH(V, this), SmallVector<WeakVH, 1>()});
|
|
return AVIP.first->second;
|
|
}
|
|
|
|
void AssumptionCache::updateAffectedValues(CallInst *CI) {
|
|
// Note: This code must be kept in-sync with the code in
|
|
// computeKnownBitsFromAssume in ValueTracking.
|
|
|
|
SmallVector<Value *, 16> Affected;
|
|
auto AddAffected = [&Affected](Value *V) {
|
|
if (isa<Argument>(V)) {
|
|
Affected.push_back(V);
|
|
} else if (auto *I = dyn_cast<Instruction>(V)) {
|
|
Affected.push_back(I);
|
|
|
|
if (I->getOpcode() == Instruction::BitCast ||
|
|
I->getOpcode() == Instruction::PtrToInt) {
|
|
auto *Op = I->getOperand(0);
|
|
if (isa<Instruction>(Op) || isa<Argument>(Op))
|
|
Affected.push_back(Op);
|
|
}
|
|
}
|
|
};
|
|
|
|
Value *Cond = CI->getArgOperand(0), *A, *B;
|
|
AddAffected(Cond);
|
|
|
|
CmpInst::Predicate Pred;
|
|
if (match(Cond, m_ICmp(Pred, m_Value(A), m_Value(B)))) {
|
|
AddAffected(A);
|
|
AddAffected(B);
|
|
|
|
if (Pred == ICmpInst::ICMP_EQ) {
|
|
// For equality comparisons, we handle the case of bit inversion.
|
|
auto AddAffectedFromEq = [&AddAffected](Value *V) {
|
|
Value *A;
|
|
if (match(V, m_Not(m_Value(A)))) {
|
|
AddAffected(A);
|
|
V = A;
|
|
}
|
|
|
|
Value *B;
|
|
ConstantInt *C;
|
|
// (A & B) or (A | B) or (A ^ B).
|
|
if (match(V,
|
|
m_CombineOr(m_And(m_Value(A), m_Value(B)),
|
|
m_CombineOr(m_Or(m_Value(A), m_Value(B)),
|
|
m_Xor(m_Value(A), m_Value(B)))))) {
|
|
AddAffected(A);
|
|
AddAffected(B);
|
|
// (A << C) or (A >>_s C) or (A >>_u C) where C is some constant.
|
|
} else if (match(V,
|
|
m_CombineOr(m_Shl(m_Value(A), m_ConstantInt(C)),
|
|
m_CombineOr(m_LShr(m_Value(A), m_ConstantInt(C)),
|
|
m_AShr(m_Value(A),
|
|
m_ConstantInt(C)))))) {
|
|
AddAffected(A);
|
|
}
|
|
};
|
|
|
|
AddAffectedFromEq(A);
|
|
AddAffectedFromEq(B);
|
|
}
|
|
}
|
|
|
|
for (auto &AV : Affected) {
|
|
auto &AVV = getAffectedValues(AV);
|
|
if (std::find(AVV.begin(), AVV.end(), CI) == AVV.end())
|
|
AVV.push_back(CI);
|
|
}
|
|
}
|
|
|
|
void AssumptionCache::AffectedValueCallbackVH::deleted() {
|
|
auto AVI = AC->AffectedValues.find(getValPtr());
|
|
if (AVI != AC->AffectedValues.end())
|
|
AC->AffectedValues.erase(AVI);
|
|
// 'this' now dangles!
|
|
}
|
|
|
|
void AssumptionCache::AffectedValueCallbackVH::allUsesReplacedWith(Value *NV) {
|
|
if (!isa<Instruction>(NV) && !isa<Argument>(NV))
|
|
return;
|
|
|
|
// Any assumptions that affected this value now affect the new value.
|
|
|
|
auto &NAVV = AC->getAffectedValues(NV);
|
|
auto AVI = AC->AffectedValues.find(getValPtr());
|
|
if (AVI == AC->AffectedValues.end())
|
|
return;
|
|
|
|
for (auto &A : AVI->second)
|
|
if (std::find(NAVV.begin(), NAVV.end(), A) == NAVV.end())
|
|
NAVV.push_back(A);
|
|
}
|
|
|
|
void AssumptionCache::scanFunction() {
|
|
assert(!Scanned && "Tried to scan the function twice!");
|
|
assert(AssumeHandles.empty() && "Already have assumes when scanning!");
|
|
|
|
// Go through all instructions in all blocks, add all calls to @llvm.assume
|
|
// to this cache.
|
|
for (BasicBlock &B : F)
|
|
for (Instruction &II : B)
|
|
if (match(&II, m_Intrinsic<Intrinsic::assume>()))
|
|
AssumeHandles.push_back(&II);
|
|
|
|
// Mark the scan as complete.
|
|
Scanned = true;
|
|
|
|
// Update affected values.
|
|
for (auto &A : AssumeHandles)
|
|
updateAffectedValues(cast<CallInst>(A));
|
|
}
|
|
|
|
void AssumptionCache::registerAssumption(CallInst *CI) {
|
|
assert(match(CI, m_Intrinsic<Intrinsic::assume>()) &&
|
|
"Registered call does not call @llvm.assume");
|
|
|
|
// If we haven't scanned the function yet, just drop this assumption. It will
|
|
// be found when we scan later.
|
|
if (!Scanned)
|
|
return;
|
|
|
|
AssumeHandles.push_back(CI);
|
|
|
|
#ifndef NDEBUG
|
|
assert(CI->getParent() &&
|
|
"Cannot register @llvm.assume call not in a basic block");
|
|
assert(&F == CI->getParent()->getParent() &&
|
|
"Cannot register @llvm.assume call not in this function");
|
|
|
|
// We expect the number of assumptions to be small, so in an asserts build
|
|
// check that we don't accumulate duplicates and that all assumptions point
|
|
// to the same function.
|
|
SmallPtrSet<Value *, 16> AssumptionSet;
|
|
for (auto &VH : AssumeHandles) {
|
|
if (!VH)
|
|
continue;
|
|
|
|
assert(&F == cast<Instruction>(VH)->getParent()->getParent() &&
|
|
"Cached assumption not inside this function!");
|
|
assert(match(cast<CallInst>(VH), m_Intrinsic<Intrinsic::assume>()) &&
|
|
"Cached something other than a call to @llvm.assume!");
|
|
assert(AssumptionSet.insert(VH).second &&
|
|
"Cache contains multiple copies of a call!");
|
|
}
|
|
#endif
|
|
|
|
updateAffectedValues(CI);
|
|
}
|
|
|
|
AnalysisKey AssumptionAnalysis::Key;
|
|
|
|
PreservedAnalyses AssumptionPrinterPass::run(Function &F,
|
|
FunctionAnalysisManager &AM) {
|
|
AssumptionCache &AC = AM.getResult<AssumptionAnalysis>(F);
|
|
|
|
OS << "Cached assumptions for function: " << F.getName() << "\n";
|
|
for (auto &VH : AC.assumptions())
|
|
if (VH)
|
|
OS << " " << *cast<CallInst>(VH)->getArgOperand(0) << "\n";
|
|
|
|
return PreservedAnalyses::all();
|
|
}
|
|
|
|
void AssumptionCacheTracker::FunctionCallbackVH::deleted() {
|
|
auto I = ACT->AssumptionCaches.find_as(cast<Function>(getValPtr()));
|
|
if (I != ACT->AssumptionCaches.end())
|
|
ACT->AssumptionCaches.erase(I);
|
|
// 'this' now dangles!
|
|
}
|
|
|
|
AssumptionCache &AssumptionCacheTracker::getAssumptionCache(Function &F) {
|
|
// We probe the function map twice to try and avoid creating a value handle
|
|
// around the function in common cases. This makes insertion a bit slower,
|
|
// but if we have to insert we're going to scan the whole function so that
|
|
// shouldn't matter.
|
|
auto I = AssumptionCaches.find_as(&F);
|
|
if (I != AssumptionCaches.end())
|
|
return *I->second;
|
|
|
|
// Ok, build a new cache by scanning the function, insert it and the value
|
|
// handle into our map, and return the newly populated cache.
|
|
auto IP = AssumptionCaches.insert(std::make_pair(
|
|
FunctionCallbackVH(&F, this), llvm::make_unique<AssumptionCache>(F)));
|
|
assert(IP.second && "Scanning function already in the map?");
|
|
return *IP.first->second;
|
|
}
|
|
|
|
void AssumptionCacheTracker::verifyAnalysis() const {
|
|
#ifndef NDEBUG
|
|
SmallPtrSet<const CallInst *, 4> AssumptionSet;
|
|
for (const auto &I : AssumptionCaches) {
|
|
for (auto &VH : I.second->assumptions())
|
|
if (VH)
|
|
AssumptionSet.insert(cast<CallInst>(VH));
|
|
|
|
for (const BasicBlock &B : cast<Function>(*I.first))
|
|
for (const Instruction &II : B)
|
|
if (match(&II, m_Intrinsic<Intrinsic::assume>()))
|
|
assert(AssumptionSet.count(cast<CallInst>(&II)) &&
|
|
"Assumption in scanned function not in cache");
|
|
}
|
|
#endif
|
|
}
|
|
|
|
AssumptionCacheTracker::AssumptionCacheTracker() : ImmutablePass(ID) {
|
|
initializeAssumptionCacheTrackerPass(*PassRegistry::getPassRegistry());
|
|
}
|
|
|
|
AssumptionCacheTracker::~AssumptionCacheTracker() {}
|
|
|
|
INITIALIZE_PASS(AssumptionCacheTracker, "assumption-cache-tracker",
|
|
"Assumption Cache Tracker", false, true)
|
|
char AssumptionCacheTracker::ID = 0;
|