//===- AssumeBundleQueries.cpp - tool to query assume bundles ---*- 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 "llvm/Analysis/AssumeBundleQueries.h" #include "llvm/IR/Function.h" #include "llvm/IR/InstIterator.h" #include "llvm/IR/IntrinsicInst.h" using namespace llvm; static bool bundleHasArgument(const CallBase::BundleOpInfo &BOI, unsigned Idx) { return BOI.End - BOI.Begin > Idx; } static Value *getValueFromBundleOpInfo(IntrinsicInst &Assume, const CallBase::BundleOpInfo &BOI, unsigned Idx) { assert(bundleHasArgument(BOI, Idx) && "index out of range"); return (Assume.op_begin() + BOI.Begin + Idx)->get(); } bool llvm::hasAttributeInAssume(CallInst &AssumeCI, Value *IsOn, StringRef AttrName, uint64_t *ArgVal, AssumeQuery AQR) { assert(isa(AssumeCI) && "this function is intended to be used on llvm.assume"); IntrinsicInst &Assume = cast(AssumeCI); assert(Assume.getIntrinsicID() == Intrinsic::assume && "this function is intended to be used on llvm.assume"); assert(Attribute::isExistingAttribute(AttrName) && "this attribute doesn't exist"); assert((ArgVal == nullptr || Attribute::doesAttrKindHaveArgument( Attribute::getAttrKindFromName(AttrName))) && "requested value for an attribute that has no argument"); if (Assume.bundle_op_infos().empty()) return false; auto Loop = [&](auto &&Range) { for (auto &BOI : Range) { if (BOI.Tag->getKey() != AttrName) continue; if (IsOn && (BOI.End - BOI.Begin <= ABA_WasOn || IsOn != getValueFromBundleOpInfo(Assume, BOI, ABA_WasOn))) continue; if (ArgVal) { assert(BOI.End - BOI.Begin > ABA_Argument); *ArgVal = cast( getValueFromBundleOpInfo(Assume, BOI, ABA_Argument)) ->getZExtValue(); } return true; } return false; }; if (AQR == AssumeQuery::Lowest) return Loop(Assume.bundle_op_infos()); return Loop(reverse(Assume.bundle_op_infos())); } void llvm::fillMapFromAssume(CallInst &AssumeCI, RetainedKnowledgeMap &Result) { IntrinsicInst &Assume = cast(AssumeCI); assert(Assume.getIntrinsicID() == Intrinsic::assume && "this function is intended to be used on llvm.assume"); for (auto &Bundles : Assume.bundle_op_infos()) { std::pair Key{ nullptr, Attribute::getAttrKindFromName(Bundles.Tag->getKey())}; if (bundleHasArgument(Bundles, ABA_WasOn)) Key.first = getValueFromBundleOpInfo(Assume, Bundles, ABA_WasOn); if (Key.first == nullptr && Key.second == Attribute::None) continue; if (!bundleHasArgument(Bundles, ABA_Argument)) { Result[Key][&Assume] = {0, 0}; continue; } unsigned Val = cast( getValueFromBundleOpInfo(Assume, Bundles, ABA_Argument)) ->getZExtValue(); auto Lookup = Result.find(Key); if (Lookup == Result.end() || !Lookup->second.count(&Assume)) { Result[Key][&Assume] = {Val, Val}; continue; } Lookup->second[&Assume].Min = std::min(Val, Lookup->second[&Assume].Min); Lookup->second[&Assume].Max = std::max(Val, Lookup->second[&Assume].Max); } } RetainedKnowledge llvm::getKnowledgeFromOperandInAssume(CallInst &AssumeCI, unsigned Idx) { IntrinsicInst &Assume = cast(AssumeCI); assert(Assume.getIntrinsicID() == Intrinsic::assume && "this function is intended to be used on llvm.assume"); CallBase::BundleOpInfo BOI = Assume.getBundleOpInfoForOperand(Idx); RetainedKnowledge Result; Result.AttrKind = Attribute::getAttrKindFromName(BOI.Tag->getKey()); Result.WasOn = getValueFromBundleOpInfo(Assume, BOI, ABA_WasOn); if (BOI.End - BOI.Begin > ABA_Argument) Result.ArgValue = cast(getValueFromBundleOpInfo(Assume, BOI, ABA_Argument)) ->getZExtValue(); return Result; } bool llvm::isAssumeWithEmptyBundle(CallInst &CI) { IntrinsicInst &Assume = cast(CI); assert(Assume.getIntrinsicID() == Intrinsic::assume && "this function is intended to be used on llvm.assume"); return none_of(Assume.bundle_op_infos(), [](const CallBase::BundleOpInfo &BOI) { return BOI.Tag->getKey() != "ignore"; }); }