Path: blob/main/contrib/llvm-project/llvm/lib/Analysis/AssumptionCache.cpp
35232 views
//===- AssumptionCache.cpp - Cache finding @llvm.assume calls -------------===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//7//8// This file contains a pass that keeps track of @llvm.assume intrinsics in9// the functions of a module.10//11//===----------------------------------------------------------------------===//1213#include "llvm/Analysis/AssumptionCache.h"14#include "llvm/ADT/STLExtras.h"15#include "llvm/ADT/SmallPtrSet.h"16#include "llvm/ADT/SmallVector.h"17#include "llvm/Analysis/AssumeBundleQueries.h"18#include "llvm/Analysis/TargetTransformInfo.h"19#include "llvm/Analysis/ValueTracking.h"20#include "llvm/IR/BasicBlock.h"21#include "llvm/IR/Function.h"22#include "llvm/IR/InstrTypes.h"23#include "llvm/IR/Instruction.h"24#include "llvm/IR/Instructions.h"25#include "llvm/IR/PassManager.h"26#include "llvm/IR/PatternMatch.h"27#include "llvm/InitializePasses.h"28#include "llvm/Pass.h"29#include "llvm/Support/Casting.h"30#include "llvm/Support/CommandLine.h"31#include "llvm/Support/ErrorHandling.h"32#include "llvm/Support/raw_ostream.h"33#include <cassert>34#include <utility>3536using namespace llvm;37using namespace llvm::PatternMatch;3839static cl::opt<bool>40VerifyAssumptionCache("verify-assumption-cache", cl::Hidden,41cl::desc("Enable verification of assumption cache"),42cl::init(false));4344SmallVector<AssumptionCache::ResultElem, 1> &45AssumptionCache::getOrInsertAffectedValues(Value *V) {46// Try using find_as first to avoid creating extra value handles just for the47// purpose of doing the lookup.48auto AVI = AffectedValues.find_as(V);49if (AVI != AffectedValues.end())50return AVI->second;5152auto AVIP = AffectedValues.insert(53{AffectedValueCallbackVH(V, this), SmallVector<ResultElem, 1>()});54return AVIP.first->second;55}5657static void58findAffectedValues(CallBase *CI, TargetTransformInfo *TTI,59SmallVectorImpl<AssumptionCache::ResultElem> &Affected) {60// Note: This code must be kept in-sync with the code in61// computeKnownBitsFromAssume in ValueTracking.6263auto InsertAffected = [&Affected](Value *V) {64Affected.push_back({V, AssumptionCache::ExprResultIdx});65};6667auto AddAffectedVal = [&Affected](Value *V, unsigned Idx) {68if (isa<Argument>(V) || isa<GlobalValue>(V) || isa<Instruction>(V)) {69Affected.push_back({V, Idx});70}71};7273for (unsigned Idx = 0; Idx != CI->getNumOperandBundles(); Idx++) {74OperandBundleUse Bundle = CI->getOperandBundleAt(Idx);75if (Bundle.getTagName() == "separate_storage") {76assert(Bundle.Inputs.size() == 2 &&77"separate_storage must have two args");78AddAffectedVal(getUnderlyingObject(Bundle.Inputs[0]), Idx);79AddAffectedVal(getUnderlyingObject(Bundle.Inputs[1]), Idx);80} else if (Bundle.Inputs.size() > ABA_WasOn &&81Bundle.getTagName() != IgnoreBundleTag)82AddAffectedVal(Bundle.Inputs[ABA_WasOn], Idx);83}8485Value *Cond = CI->getArgOperand(0);86findValuesAffectedByCondition(Cond, /*IsAssume=*/true, InsertAffected);8788if (TTI) {89const Value *Ptr;90unsigned AS;91std::tie(Ptr, AS) = TTI->getPredicatedAddrSpace(Cond);92if (Ptr)93AddAffectedVal(const_cast<Value *>(Ptr->stripInBoundsOffsets()),94AssumptionCache::ExprResultIdx);95}96}9798void AssumptionCache::updateAffectedValues(AssumeInst *CI) {99SmallVector<AssumptionCache::ResultElem, 16> Affected;100findAffectedValues(CI, TTI, Affected);101102for (auto &AV : Affected) {103auto &AVV = getOrInsertAffectedValues(AV.Assume);104if (llvm::none_of(AVV, [&](ResultElem &Elem) {105return Elem.Assume == CI && Elem.Index == AV.Index;106}))107AVV.push_back({CI, AV.Index});108}109}110111void AssumptionCache::unregisterAssumption(AssumeInst *CI) {112SmallVector<AssumptionCache::ResultElem, 16> Affected;113findAffectedValues(CI, TTI, Affected);114115for (auto &AV : Affected) {116auto AVI = AffectedValues.find_as(AV.Assume);117if (AVI == AffectedValues.end())118continue;119bool Found = false;120bool HasNonnull = false;121for (ResultElem &Elem : AVI->second) {122if (Elem.Assume == CI) {123Found = true;124Elem.Assume = nullptr;125}126HasNonnull |= !!Elem.Assume;127if (HasNonnull && Found)128break;129}130assert(Found && "already unregistered or incorrect cache state");131if (!HasNonnull)132AffectedValues.erase(AVI);133}134135llvm::erase(AssumeHandles, CI);136}137138void AssumptionCache::AffectedValueCallbackVH::deleted() {139AC->AffectedValues.erase(getValPtr());140// 'this' now dangles!141}142143void AssumptionCache::transferAffectedValuesInCache(Value *OV, Value *NV) {144auto &NAVV = getOrInsertAffectedValues(NV);145auto AVI = AffectedValues.find(OV);146if (AVI == AffectedValues.end())147return;148149for (auto &A : AVI->second)150if (!llvm::is_contained(NAVV, A))151NAVV.push_back(A);152AffectedValues.erase(OV);153}154155void AssumptionCache::AffectedValueCallbackVH::allUsesReplacedWith(Value *NV) {156if (!isa<Instruction>(NV) && !isa<Argument>(NV))157return;158159// Any assumptions that affected this value now affect the new value.160161AC->transferAffectedValuesInCache(getValPtr(), NV);162// 'this' now might dangle! If the AffectedValues map was resized to add an163// entry for NV then this object might have been destroyed in favor of some164// copy in the grown map.165}166167void AssumptionCache::scanFunction() {168assert(!Scanned && "Tried to scan the function twice!");169assert(AssumeHandles.empty() && "Already have assumes when scanning!");170171// Go through all instructions in all blocks, add all calls to @llvm.assume172// to this cache.173for (BasicBlock &B : F)174for (Instruction &I : B)175if (isa<AssumeInst>(&I))176AssumeHandles.push_back({&I, ExprResultIdx});177178// Mark the scan as complete.179Scanned = true;180181// Update affected values.182for (auto &A : AssumeHandles)183updateAffectedValues(cast<AssumeInst>(A));184}185186void AssumptionCache::registerAssumption(AssumeInst *CI) {187// If we haven't scanned the function yet, just drop this assumption. It will188// be found when we scan later.189if (!Scanned)190return;191192AssumeHandles.push_back({CI, ExprResultIdx});193194#ifndef NDEBUG195assert(CI->getParent() &&196"Cannot register @llvm.assume call not in a basic block");197assert(&F == CI->getParent()->getParent() &&198"Cannot register @llvm.assume call not in this function");199200// We expect the number of assumptions to be small, so in an asserts build201// check that we don't accumulate duplicates and that all assumptions point202// to the same function.203SmallPtrSet<Value *, 16> AssumptionSet;204for (auto &VH : AssumeHandles) {205if (!VH)206continue;207208assert(&F == cast<Instruction>(VH)->getParent()->getParent() &&209"Cached assumption not inside this function!");210assert(match(cast<CallInst>(VH), m_Intrinsic<Intrinsic::assume>()) &&211"Cached something other than a call to @llvm.assume!");212assert(AssumptionSet.insert(VH).second &&213"Cache contains multiple copies of a call!");214}215#endif216217updateAffectedValues(CI);218}219220AssumptionCache AssumptionAnalysis::run(Function &F,221FunctionAnalysisManager &FAM) {222auto &TTI = FAM.getResult<TargetIRAnalysis>(F);223return AssumptionCache(F, &TTI);224}225226AnalysisKey AssumptionAnalysis::Key;227228PreservedAnalyses AssumptionPrinterPass::run(Function &F,229FunctionAnalysisManager &AM) {230AssumptionCache &AC = AM.getResult<AssumptionAnalysis>(F);231232OS << "Cached assumptions for function: " << F.getName() << "\n";233for (auto &VH : AC.assumptions())234if (VH)235OS << " " << *cast<CallInst>(VH)->getArgOperand(0) << "\n";236237return PreservedAnalyses::all();238}239240void AssumptionCacheTracker::FunctionCallbackVH::deleted() {241auto I = ACT->AssumptionCaches.find_as(cast<Function>(getValPtr()));242if (I != ACT->AssumptionCaches.end())243ACT->AssumptionCaches.erase(I);244// 'this' now dangles!245}246247AssumptionCache &AssumptionCacheTracker::getAssumptionCache(Function &F) {248// We probe the function map twice to try and avoid creating a value handle249// around the function in common cases. This makes insertion a bit slower,250// but if we have to insert we're going to scan the whole function so that251// shouldn't matter.252auto I = AssumptionCaches.find_as(&F);253if (I != AssumptionCaches.end())254return *I->second;255256auto *TTIWP = getAnalysisIfAvailable<TargetTransformInfoWrapperPass>();257auto *TTI = TTIWP ? &TTIWP->getTTI(F) : nullptr;258259// Ok, build a new cache by scanning the function, insert it and the value260// handle into our map, and return the newly populated cache.261auto IP = AssumptionCaches.insert(std::make_pair(262FunctionCallbackVH(&F, this), std::make_unique<AssumptionCache>(F, TTI)));263assert(IP.second && "Scanning function already in the map?");264return *IP.first->second;265}266267AssumptionCache *AssumptionCacheTracker::lookupAssumptionCache(Function &F) {268auto I = AssumptionCaches.find_as(&F);269if (I != AssumptionCaches.end())270return I->second.get();271return nullptr;272}273274void AssumptionCacheTracker::verifyAnalysis() const {275// FIXME: In the long term the verifier should not be controllable with a276// flag. We should either fix all passes to correctly update the assumption277// cache and enable the verifier unconditionally or somehow arrange for the278// assumption list to be updated automatically by passes.279if (!VerifyAssumptionCache)280return;281282SmallPtrSet<const CallInst *, 4> AssumptionSet;283for (const auto &I : AssumptionCaches) {284for (auto &VH : I.second->assumptions())285if (VH)286AssumptionSet.insert(cast<CallInst>(VH));287288for (const BasicBlock &B : cast<Function>(*I.first))289for (const Instruction &II : B)290if (match(&II, m_Intrinsic<Intrinsic::assume>()) &&291!AssumptionSet.count(cast<CallInst>(&II)))292report_fatal_error("Assumption in scanned function not in cache");293}294}295296AssumptionCacheTracker::AssumptionCacheTracker() : ImmutablePass(ID) {297initializeAssumptionCacheTrackerPass(*PassRegistry::getPassRegistry());298}299300AssumptionCacheTracker::~AssumptionCacheTracker() = default;301302char AssumptionCacheTracker::ID = 0;303304INITIALIZE_PASS(AssumptionCacheTracker, "assumption-cache-tracker",305"Assumption Cache Tracker", false, true)306307308