2020-02-02 14:46:59 +01:00
|
|
|
//===- KnowledgeRetention.h - utilities to preserve informations *- 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
|
|
|
|
//
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
2020-03-08 12:19:29 +01:00
|
|
|
#include "llvm/IR/KnowledgeRetention.h"
|
2020-02-02 14:46:59 +01:00
|
|
|
#include "llvm/ADT/DenseSet.h"
|
2020-03-25 21:55:35 +01:00
|
|
|
#include "llvm/IR/Function.h"
|
2020-02-02 14:46:59 +01:00
|
|
|
#include "llvm/IR/InstIterator.h"
|
|
|
|
#include "llvm/IR/IntrinsicInst.h"
|
|
|
|
#include "llvm/IR/Module.h"
|
|
|
|
#include "llvm/Support/CommandLine.h"
|
|
|
|
|
|
|
|
using namespace llvm;
|
|
|
|
|
|
|
|
cl::opt<bool> ShouldPreserveAllAttributes(
|
|
|
|
"assume-preserve-all", cl::init(false), cl::Hidden,
|
|
|
|
cl::desc("enable preservation of all attrbitues. even those that are "
|
|
|
|
"unlikely to be usefull"));
|
|
|
|
|
2020-03-13 14:14:55 +01:00
|
|
|
cl::opt<bool> EnableKnowledgeRetention(
|
|
|
|
"enable-knowledge-retention", cl::init(false), cl::Hidden,
|
|
|
|
cl::desc(
|
|
|
|
"enable preservation of attributes throughout code transformation"));
|
|
|
|
|
2020-02-18 19:06:30 +01:00
|
|
|
namespace {
|
|
|
|
|
2020-02-02 14:46:59 +01:00
|
|
|
struct AssumedKnowledge {
|
|
|
|
const char *Name;
|
|
|
|
Value *Argument;
|
|
|
|
enum {
|
|
|
|
None,
|
|
|
|
Empty,
|
|
|
|
Tombstone,
|
|
|
|
};
|
|
|
|
/// Contain the argument and a flag if needed.
|
|
|
|
llvm::PointerIntPair<Value *, 2> WasOn;
|
|
|
|
};
|
|
|
|
|
|
|
|
} // namespace
|
|
|
|
|
|
|
|
namespace llvm {
|
|
|
|
|
|
|
|
template <> struct DenseMapInfo<AssumedKnowledge> {
|
|
|
|
static AssumedKnowledge getEmptyKey() {
|
|
|
|
return {nullptr, nullptr, {nullptr, AssumedKnowledge::Empty}};
|
|
|
|
}
|
|
|
|
static AssumedKnowledge getTombstoneKey() {
|
|
|
|
return {nullptr, nullptr, {nullptr, AssumedKnowledge::Tombstone}};
|
|
|
|
}
|
|
|
|
static unsigned getHashValue(const AssumedKnowledge &AK) {
|
|
|
|
return hash_combine(AK.Name, AK.Argument, AK.WasOn.getPointer());
|
|
|
|
}
|
|
|
|
static bool isEqual(const AssumedKnowledge &LHS,
|
|
|
|
const AssumedKnowledge &RHS) {
|
|
|
|
return LHS.WasOn == RHS.WasOn && LHS.Name == RHS.Name &&
|
|
|
|
LHS.Argument == RHS.Argument;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
} // namespace llvm
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
|
2020-02-18 19:06:30 +01:00
|
|
|
/// Index of elements in the operand bundle.
|
|
|
|
/// If the element exist it is guaranteed to be what is specified in this enum
|
|
|
|
/// but it may not exist.
|
|
|
|
enum BundleOpInfoElem {
|
|
|
|
BOIE_WasOn = 0,
|
|
|
|
BOIE_Argument = 1,
|
|
|
|
};
|
|
|
|
|
2020-02-02 14:46:59 +01:00
|
|
|
/// Deterministically compare OperandBundleDef.
|
|
|
|
/// The ordering is:
|
2020-02-18 19:06:30 +01:00
|
|
|
/// - by the attribute's name aka operand bundle tag, (doesn't change)
|
|
|
|
/// - then by the numeric Value of the argument, (doesn't change)
|
2020-02-02 14:46:59 +01:00
|
|
|
/// - lastly by the Name of the current Value it WasOn. (may change)
|
|
|
|
/// This order is deterministic and allows looking for the right kind of
|
|
|
|
/// attribute with binary search. However finding the right WasOn needs to be
|
2020-02-18 19:06:30 +01:00
|
|
|
/// done via linear search because values can get replaced.
|
2020-02-02 14:46:59 +01:00
|
|
|
bool isLowerOpBundle(const OperandBundleDef &LHS, const OperandBundleDef &RHS) {
|
|
|
|
auto getTuple = [](const OperandBundleDef &Op) {
|
|
|
|
return std::make_tuple(
|
|
|
|
Op.getTag(),
|
2020-02-18 19:06:30 +01:00
|
|
|
Op.input_size() <= BOIE_Argument
|
2020-02-02 14:46:59 +01:00
|
|
|
? 0
|
2020-02-18 19:06:30 +01:00
|
|
|
: cast<ConstantInt>(*(Op.input_begin() + BOIE_Argument))
|
|
|
|
->getZExtValue(),
|
|
|
|
Op.input_size() <= BOIE_WasOn
|
|
|
|
? StringRef("")
|
|
|
|
: (*(Op.input_begin() + BOIE_WasOn))->getName());
|
2020-02-02 14:46:59 +01:00
|
|
|
};
|
|
|
|
return getTuple(LHS) < getTuple(RHS);
|
|
|
|
}
|
|
|
|
|
2020-03-13 14:35:26 +01:00
|
|
|
bool isUsefullToPreserve(Attribute::AttrKind Kind) {
|
|
|
|
switch (Kind) {
|
|
|
|
case Attribute::NonNull:
|
|
|
|
case Attribute::Alignment:
|
|
|
|
case Attribute::Dereferenceable:
|
|
|
|
case Attribute::DereferenceableOrNull:
|
|
|
|
case Attribute::Cold:
|
|
|
|
return true;
|
|
|
|
default:
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-02-02 14:46:59 +01:00
|
|
|
/// This class contain all knowledge that have been gather while building an
|
|
|
|
/// llvm.assume and the function to manipulate it.
|
|
|
|
struct AssumeBuilderState {
|
|
|
|
Module *M;
|
|
|
|
|
|
|
|
SmallDenseSet<AssumedKnowledge, 8> AssumedKnowledgeSet;
|
|
|
|
|
|
|
|
AssumeBuilderState(Module *M) : M(M) {}
|
|
|
|
|
|
|
|
void addAttribute(Attribute Attr, Value *WasOn) {
|
2020-03-13 14:35:26 +01:00
|
|
|
if (!ShouldPreserveAllAttributes &&
|
|
|
|
(Attr.isTypeAttribute() || Attr.isStringAttribute() ||
|
|
|
|
!isUsefullToPreserve(Attr.getKindAsEnum())))
|
|
|
|
return;
|
2020-02-02 14:46:59 +01:00
|
|
|
StringRef Name;
|
|
|
|
Value *AttrArg = nullptr;
|
|
|
|
if (Attr.isStringAttribute())
|
2020-03-13 14:35:26 +01:00
|
|
|
Name = Attr.getKindAsString();
|
2020-02-02 14:46:59 +01:00
|
|
|
else
|
|
|
|
Name = Attribute::getNameFromAttrKind(Attr.getKindAsEnum());
|
|
|
|
if (Attr.isIntAttribute())
|
|
|
|
AttrArg = ConstantInt::get(Type::getInt64Ty(M->getContext()),
|
|
|
|
Attr.getValueAsInt());
|
|
|
|
AssumedKnowledgeSet.insert(
|
|
|
|
{Name.data(), AttrArg, {WasOn, AssumedKnowledge::None}});
|
|
|
|
}
|
|
|
|
|
|
|
|
void addCall(const CallBase *Call) {
|
|
|
|
auto addAttrList = [&](AttributeList AttrList) {
|
|
|
|
for (unsigned Idx = AttributeList::FirstArgIndex;
|
|
|
|
Idx < AttrList.getNumAttrSets(); Idx++)
|
|
|
|
for (Attribute Attr : AttrList.getAttributes(Idx))
|
|
|
|
addAttribute(Attr, Call->getArgOperand(Idx - 1));
|
2020-03-13 14:35:26 +01:00
|
|
|
for (Attribute Attr : AttrList.getFnAttributes())
|
|
|
|
addAttribute(Attr, nullptr);
|
2020-02-02 14:46:59 +01:00
|
|
|
};
|
|
|
|
addAttrList(Call->getAttributes());
|
|
|
|
if (Function *Fn = Call->getCalledFunction())
|
|
|
|
addAttrList(Fn->getAttributes());
|
|
|
|
}
|
|
|
|
|
2020-03-25 18:28:51 +01:00
|
|
|
IntrinsicInst *build() {
|
2020-02-02 14:46:59 +01:00
|
|
|
if (AssumedKnowledgeSet.empty())
|
|
|
|
return nullptr;
|
|
|
|
Function *FnAssume = Intrinsic::getDeclaration(M, Intrinsic::assume);
|
|
|
|
LLVMContext &C = M->getContext();
|
|
|
|
SmallVector<OperandBundleDef, 8> OpBundle;
|
|
|
|
for (const AssumedKnowledge &Elem : AssumedKnowledgeSet) {
|
|
|
|
SmallVector<Value *, 2> Args;
|
2020-02-02 14:47:00 +01:00
|
|
|
assert(Attribute::getAttrKindFromName(Elem.Name) ==
|
|
|
|
Attribute::AttrKind::None ||
|
|
|
|
static_cast<bool>(Elem.Argument) ==
|
|
|
|
Attribute::doesAttrKindHaveArgument(
|
|
|
|
Attribute::getAttrKindFromName(Elem.Name)));
|
2020-02-02 14:46:59 +01:00
|
|
|
if (Elem.WasOn.getPointer())
|
|
|
|
Args.push_back(Elem.WasOn.getPointer());
|
|
|
|
if (Elem.Argument)
|
|
|
|
Args.push_back(Elem.Argument);
|
|
|
|
OpBundle.push_back(OperandBundleDefT<Value *>(Elem.Name, Args));
|
|
|
|
}
|
|
|
|
llvm::sort(OpBundle, isLowerOpBundle);
|
2020-03-25 18:28:51 +01:00
|
|
|
return cast<IntrinsicInst>(CallInst::Create(
|
|
|
|
FnAssume, ArrayRef<Value *>({ConstantInt::getTrue(C)}), OpBundle));
|
2020-02-02 14:46:59 +01:00
|
|
|
}
|
|
|
|
|
2020-03-25 21:55:35 +01:00
|
|
|
void addAttr(Attribute::AttrKind Kind, Value *Ptr, unsigned Argument = 0) {
|
|
|
|
AssumedKnowledge AK;
|
|
|
|
AK.Name = Attribute::getNameFromAttrKind(Kind).data();
|
|
|
|
AK.WasOn.setPointer(Ptr);
|
|
|
|
if (Attribute::doesAttrKindHaveArgument(Kind)) {
|
|
|
|
AK.Argument =
|
|
|
|
ConstantInt::get(Type::getInt64Ty(M->getContext()), Argument);
|
|
|
|
} else {
|
|
|
|
AK.Argument = nullptr;
|
|
|
|
assert(Argument == 0 && "there should be no argument");
|
|
|
|
}
|
|
|
|
AssumedKnowledgeSet.insert(AK);
|
|
|
|
};
|
|
|
|
|
|
|
|
void addLoadOrStore(Instruction *I) {
|
|
|
|
auto Impl = [&](auto *MemInst, Type *T) {
|
|
|
|
uint64_t DerefSize =
|
|
|
|
I->getModule()->getDataLayout().getTypeStoreSize(T).getKnownMinSize();
|
|
|
|
if (DerefSize != 0) {
|
|
|
|
addAttr(Attribute::Dereferenceable, MemInst->getPointerOperand(),
|
|
|
|
DerefSize);
|
|
|
|
if (!NullPointerIsDefined(MemInst->getFunction(),
|
|
|
|
MemInst->getPointerOperand()
|
|
|
|
->getType()
|
|
|
|
->getPointerAddressSpace()))
|
|
|
|
addAttr(Attribute::NonNull, MemInst->getPointerOperand());
|
|
|
|
}
|
|
|
|
MaybeAlign MA = MemInst->getAlign();
|
|
|
|
if (MA.valueOrOne() > 1)
|
|
|
|
addAttr(Attribute::Alignment, MemInst->getPointerOperand(),
|
|
|
|
MA.valueOrOne().value());
|
|
|
|
};
|
|
|
|
if (auto *Load = dyn_cast<LoadInst>(I))
|
|
|
|
Impl(Load, Load->getType());
|
|
|
|
if (auto *Store = dyn_cast<StoreInst>(I))
|
|
|
|
Impl(Store, Store->getValueOperand()->getType());
|
|
|
|
}
|
|
|
|
|
|
|
|
void addInstruction(Instruction *I) {
|
2020-02-02 14:46:59 +01:00
|
|
|
if (auto *Call = dyn_cast<CallBase>(I))
|
2020-03-25 21:55:35 +01:00
|
|
|
return addCall(Call);
|
|
|
|
if (isa<LoadInst>(I) || isa<StoreInst>(I))
|
|
|
|
return addLoadOrStore(I);
|
2020-02-02 14:46:59 +01:00
|
|
|
// TODO: Add support for the other Instructions.
|
|
|
|
// TODO: Maybe we should look around and merge with other llvm.assume.
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
} // namespace
|
|
|
|
|
2020-03-25 18:28:51 +01:00
|
|
|
IntrinsicInst *llvm::buildAssumeFromInst(Instruction *I) {
|
2020-03-13 14:14:55 +01:00
|
|
|
if (!EnableKnowledgeRetention)
|
|
|
|
return nullptr;
|
2020-03-25 18:28:51 +01:00
|
|
|
AssumeBuilderState Builder(I->getModule());
|
2020-02-02 14:46:59 +01:00
|
|
|
Builder.addInstruction(I);
|
|
|
|
return Builder.build();
|
|
|
|
}
|
|
|
|
|
2020-03-25 18:28:51 +01:00
|
|
|
static bool bundleHasArgument(const CallBase::BundleOpInfo &BOI,
|
2020-03-03 13:24:16 +01:00
|
|
|
unsigned Idx) {
|
|
|
|
return BOI.End - BOI.Begin > Idx;
|
|
|
|
}
|
|
|
|
|
|
|
|
static Value *getValueFromBundleOpInfo(IntrinsicInst &Assume,
|
|
|
|
const CallBase::BundleOpInfo &BOI,
|
|
|
|
unsigned Idx) {
|
2020-03-25 18:28:51 +01:00
|
|
|
assert(bundleHasArgument(BOI, Idx) && "index out of range");
|
2020-03-03 13:24:16 +01:00
|
|
|
return (Assume.op_begin() + BOI.Begin + Idx)->get();
|
|
|
|
}
|
|
|
|
|
2020-02-18 19:06:30 +01:00
|
|
|
bool llvm::hasAttributeInAssume(CallInst &AssumeCI, Value *IsOn,
|
|
|
|
StringRef AttrName, uint64_t *ArgVal,
|
|
|
|
AssumeQuery AQR) {
|
2020-02-20 09:06:48 +01:00
|
|
|
assert(isa<IntrinsicInst>(AssumeCI) &&
|
|
|
|
"this function is intended to be used on llvm.assume");
|
2020-02-18 19:06:30 +01:00
|
|
|
IntrinsicInst &Assume = cast<IntrinsicInst>(AssumeCI);
|
|
|
|
assert(Assume.getIntrinsicID() == Intrinsic::assume &&
|
|
|
|
"this function is intended to be used on llvm.assume");
|
2020-03-11 23:21:44 +01:00
|
|
|
assert(Attribute::isExistingAttribute(AttrName) &&
|
|
|
|
"this attribute doesn't exist");
|
2020-02-18 19:06:30 +01:00
|
|
|
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;
|
|
|
|
|
2020-03-12 00:39:05 +01:00
|
|
|
auto Loop = [&](auto &&Range) {
|
|
|
|
for (auto &BOI : Range) {
|
|
|
|
if (BOI.Tag->getKey() != AttrName)
|
|
|
|
continue;
|
|
|
|
if (IsOn && (BOI.End - BOI.Begin <= BOIE_WasOn ||
|
|
|
|
IsOn != getValueFromBundleOpInfo(Assume, BOI, BOIE_WasOn)))
|
|
|
|
continue;
|
|
|
|
if (ArgVal) {
|
|
|
|
assert(BOI.End - BOI.Begin > BOIE_Argument);
|
|
|
|
*ArgVal = cast<ConstantInt>(
|
|
|
|
getValueFromBundleOpInfo(Assume, BOI, BOIE_Argument))
|
|
|
|
->getZExtValue();
|
|
|
|
}
|
|
|
|
return true;
|
2020-02-18 19:06:30 +01:00
|
|
|
}
|
2020-03-12 00:39:05 +01:00
|
|
|
return false;
|
|
|
|
};
|
2020-02-18 19:06:30 +01:00
|
|
|
|
2020-03-12 00:39:05 +01:00
|
|
|
if (AQR == AssumeQuery::Lowest)
|
|
|
|
return Loop(Assume.bundle_op_infos());
|
|
|
|
return Loop(reverse(Assume.bundle_op_infos()));
|
2020-02-18 19:06:30 +01:00
|
|
|
}
|
|
|
|
|
2020-03-03 13:24:16 +01:00
|
|
|
void llvm::fillMapFromAssume(CallInst &AssumeCI, RetainedKnowledgeMap &Result) {
|
|
|
|
IntrinsicInst &Assume = cast<IntrinsicInst>(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<Value *, Attribute::AttrKind> Key{
|
|
|
|
nullptr, Attribute::getAttrKindFromName(Bundles.Tag->getKey())};
|
2020-03-25 18:28:51 +01:00
|
|
|
if (bundleHasArgument(Bundles, BOIE_WasOn))
|
2020-03-03 13:24:16 +01:00
|
|
|
Key.first = getValueFromBundleOpInfo(Assume, Bundles, BOIE_WasOn);
|
|
|
|
|
|
|
|
if (Key.first == nullptr && Key.second == Attribute::None)
|
|
|
|
continue;
|
2020-03-25 18:28:51 +01:00
|
|
|
if (!bundleHasArgument(Bundles, BOIE_Argument)) {
|
2020-02-20 09:06:48 +01:00
|
|
|
Result[Key][&Assume] = {0, 0};
|
2020-03-03 13:24:16 +01:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
unsigned Val = cast<ConstantInt>(
|
|
|
|
getValueFromBundleOpInfo(Assume, Bundles, BOIE_Argument))
|
|
|
|
->getZExtValue();
|
|
|
|
auto Lookup = Result.find(Key);
|
2020-02-20 09:06:48 +01:00
|
|
|
if (Lookup == Result.end() || !Lookup->second.count(&Assume)) {
|
|
|
|
Result[Key][&Assume] = {Val, Val};
|
2020-03-03 13:24:16 +01:00
|
|
|
continue;
|
|
|
|
}
|
2020-02-20 09:06:48 +01:00
|
|
|
Lookup->second[&Assume].Min = std::min(Val, Lookup->second[&Assume].Min);
|
|
|
|
Lookup->second[&Assume].Max = std::max(Val, Lookup->second[&Assume].Max);
|
2020-03-03 13:24:16 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-07 18:00:05 +01:00
|
|
|
RetainedKnowledge llvm::getKnowledgeFromOperandInAssume(CallInst &AssumeCI,
|
|
|
|
unsigned Idx) {
|
|
|
|
IntrinsicInst &Assume = cast<IntrinsicInst>(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, BOIE_WasOn);
|
|
|
|
if (BOI.End - BOI.Begin > BOIE_Argument)
|
|
|
|
Result.ArgValue =
|
|
|
|
cast<ConstantInt>(getValueFromBundleOpInfo(Assume, BOI, BOIE_Argument))
|
|
|
|
->getZExtValue();
|
|
|
|
|
|
|
|
return Result;
|
|
|
|
}
|
|
|
|
|
2020-03-17 11:28:10 +01:00
|
|
|
bool llvm::isAssumeWithEmptyBundle(CallInst &CI) {
|
|
|
|
IntrinsicInst &Assume = cast<IntrinsicInst>(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";
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2020-02-02 14:46:59 +01:00
|
|
|
PreservedAnalyses AssumeBuilderPass::run(Function &F,
|
|
|
|
FunctionAnalysisManager &AM) {
|
|
|
|
for (Instruction &I : instructions(F))
|
2020-03-25 18:28:51 +01:00
|
|
|
if (Instruction *Assume = buildAssumeFromInst(&I))
|
2020-02-02 14:46:59 +01:00
|
|
|
Assume->insertBefore(&I);
|
|
|
|
return PreservedAnalyses::all();
|
|
|
|
}
|