llvm-project/llvm/unittests/Analysis/AssumeBundleQueriesTest.cpp

587 lines
24 KiB
C++

//===- AssumeBundleQueriesTest.cpp ------------------------------*- 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/AssumptionCache.h"
#include "llvm/Analysis/AssumeBundleQueries.h"
#include "llvm/AsmParser/Parser.h"
#include "llvm/IR/LLVMContext.h"
#include "llvm/IR/IntrinsicInst.h"
#include "llvm/Support/Regex.h"
#include "llvm/Support/SourceMgr.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Transforms/Utils/AssumeBundleBuilder.h"
#include "gtest/gtest.h"
#include <random>
using namespace llvm;
extern cl::opt<bool> ShouldPreserveAllAttributes;
extern cl::opt<bool> EnableKnowledgeRetention;
static void RunTest(
StringRef Head, StringRef Tail,
std::vector<std::pair<StringRef, llvm::function_ref<void(Instruction *)>>>
&Tests) {
for (auto &Elem : Tests) {
std::string IR;
IR.append(Head.begin(), Head.end());
IR.append(Elem.first.begin(), Elem.first.end());
IR.append(Tail.begin(), Tail.end());
LLVMContext C;
SMDiagnostic Err;
std::unique_ptr<Module> Mod = parseAssemblyString(IR, Err, C);
if (!Mod)
Err.print("AssumeQueryAPI", errs());
Elem.second(&*(Mod->getFunction("test")->begin()->begin()));
}
}
bool hasMatchesExactlyAttributes(IntrinsicInst *Assume, Value *WasOn,
StringRef AttrToMatch) {
Regex Reg(AttrToMatch);
SmallVector<StringRef, 1> Matches;
for (StringRef Attr : {
#define GET_ATTR_NAMES
#define ATTRIBUTE_ALL(ENUM_NAME, DISPLAY_NAME) StringRef(#DISPLAY_NAME),
#include "llvm/IR/Attributes.inc"
}) {
bool ShouldHaveAttr = Reg.match(Attr, &Matches) && Matches[0] == Attr;
if (ShouldHaveAttr != hasAttributeInAssume(*Assume, WasOn, Attr))
return false;
}
return true;
}
bool hasTheRightValue(IntrinsicInst *Assume, Value *WasOn,
Attribute::AttrKind Kind, unsigned Value) {
uint64_t ArgVal = 0;
if (!hasAttributeInAssume(*Assume, WasOn, Kind, &ArgVal))
return false;
if (ArgVal != Value)
return false;
return true;
}
TEST(AssumeQueryAPI, hasAttributeInAssume) {
EnableKnowledgeRetention.setValue(true);
StringRef Head =
"declare void @llvm.assume(i1)\n"
"declare void @func(i32*, i32*)\n"
"declare void @func1(i32*, i32*, i32*, i32*)\n"
"declare void @func_many(i32*) \"no-jump-tables\" nounwind "
"\"less-precise-fpmad\" willreturn norecurse\n"
"define void @test(i32* %P, i32* %P1, i32* %P2, i32* %P3) {\n";
StringRef Tail = "ret void\n"
"}";
std::vector<std::pair<StringRef, llvm::function_ref<void(Instruction *)>>>
Tests;
Tests.push_back(std::make_pair(
"call void @func(i32* nonnull align 4 dereferenceable(16) %P, i32* align "
"8 noalias %P1)\n",
[](Instruction *I) {
IntrinsicInst *Assume = buildAssumeFromInst(I);
Assume->insertBefore(I);
ASSERT_TRUE(hasMatchesExactlyAttributes(Assume, I->getOperand(0),
"(nonnull|align|dereferenceable)"));
ASSERT_TRUE(hasMatchesExactlyAttributes(Assume, I->getOperand(1),
"(align)"));
ASSERT_TRUE(hasTheRightValue(Assume, I->getOperand(0),
Attribute::AttrKind::Dereferenceable, 16));
ASSERT_TRUE(hasTheRightValue(Assume, I->getOperand(0),
Attribute::AttrKind::Alignment, 4));
ASSERT_TRUE(hasTheRightValue(Assume, I->getOperand(0),
Attribute::AttrKind::Alignment, 4));
}));
Tests.push_back(std::make_pair(
"call void @func1(i32* nonnull align 32 dereferenceable(48) %P, i32* "
"nonnull "
"align 8 dereferenceable(28) %P, i32* nonnull align 64 "
"dereferenceable(4) "
"%P, i32* nonnull align 16 dereferenceable(12) %P)\n",
[](Instruction *I) {
IntrinsicInst *Assume = buildAssumeFromInst(I);
Assume->insertBefore(I);
ASSERT_TRUE(hasMatchesExactlyAttributes(Assume, I->getOperand(0),
"(nonnull|align|dereferenceable)"));
ASSERT_TRUE(hasMatchesExactlyAttributes(Assume, I->getOperand(1),
"(nonnull|align|dereferenceable)"));
ASSERT_TRUE(hasMatchesExactlyAttributes(Assume, I->getOperand(2),
"(nonnull|align|dereferenceable)"));
ASSERT_TRUE(hasMatchesExactlyAttributes(Assume, I->getOperand(3),
"(nonnull|align|dereferenceable)"));
ASSERT_TRUE(hasTheRightValue(Assume, I->getOperand(0),
Attribute::AttrKind::Dereferenceable, 48));
ASSERT_TRUE(hasTheRightValue(Assume, I->getOperand(0),
Attribute::AttrKind::Alignment, 64));
ASSERT_TRUE(hasTheRightValue(Assume, I->getOperand(1),
Attribute::AttrKind::Alignment, 64));
}));
Tests.push_back(std::make_pair(
"call void @func_many(i32* align 8 %P1) cold\n", [](Instruction *I) {
ShouldPreserveAllAttributes.setValue(true);
IntrinsicInst *Assume = buildAssumeFromInst(I);
Assume->insertBefore(I);
ASSERT_TRUE(hasMatchesExactlyAttributes(
Assume, nullptr, "(align|nounwind|norecurse|willreturn|cold)"));
ShouldPreserveAllAttributes.setValue(false);
}));
Tests.push_back(
std::make_pair("call void @llvm.assume(i1 true)\n", [](Instruction *I) {
IntrinsicInst *Assume = cast<IntrinsicInst>(I);
ASSERT_TRUE(hasMatchesExactlyAttributes(Assume, nullptr, ""));
}));
Tests.push_back(std::make_pair(
"call void @func1(i32* readnone align 32 "
"dereferenceable(48) noalias %P, i32* "
"align 8 dereferenceable(28) %P1, i32* align 64 "
"dereferenceable(4) "
"%P2, i32* nonnull align 16 dereferenceable(12) %P3)\n",
[](Instruction *I) {
IntrinsicInst *Assume = buildAssumeFromInst(I);
Assume->insertBefore(I);
ASSERT_TRUE(hasMatchesExactlyAttributes(
Assume, I->getOperand(0),
"(align|dereferenceable)"));
ASSERT_TRUE(hasMatchesExactlyAttributes(Assume, I->getOperand(1),
"(align|dereferenceable)"));
ASSERT_TRUE(hasMatchesExactlyAttributes(Assume, I->getOperand(2),
"(align|dereferenceable)"));
ASSERT_TRUE(hasMatchesExactlyAttributes(Assume, I->getOperand(3),
"(nonnull|align|dereferenceable)"));
ASSERT_TRUE(hasTheRightValue(Assume, I->getOperand(0),
Attribute::AttrKind::Alignment, 32));
ASSERT_TRUE(hasTheRightValue(Assume, I->getOperand(0),
Attribute::AttrKind::Dereferenceable, 48));
ASSERT_TRUE(hasTheRightValue(Assume, I->getOperand(1),
Attribute::AttrKind::Dereferenceable, 28));
ASSERT_TRUE(hasTheRightValue(Assume, I->getOperand(1),
Attribute::AttrKind::Alignment, 8));
ASSERT_TRUE(hasTheRightValue(Assume, I->getOperand(2),
Attribute::AttrKind::Alignment, 64));
ASSERT_TRUE(hasTheRightValue(Assume, I->getOperand(2),
Attribute::AttrKind::Dereferenceable, 4));
ASSERT_TRUE(hasTheRightValue(Assume, I->getOperand(3),
Attribute::AttrKind::Alignment, 16));
ASSERT_TRUE(hasTheRightValue(Assume, I->getOperand(3),
Attribute::AttrKind::Dereferenceable, 12));
}));
Tests.push_back(std::make_pair(
"call void @func1(i32* readnone align 32 "
"dereferenceable(48) noalias %P, i32* "
"align 8 dereferenceable(28) %P1, i32* align 64 "
"dereferenceable(4) "
"%P2, i32* nonnull align 16 dereferenceable(12) %P3)\n",
[](Instruction *I) {
IntrinsicInst *Assume = buildAssumeFromInst(I);
Assume->insertBefore(I);
I->getOperand(1)->dropDroppableUses();
I->getOperand(2)->dropDroppableUses();
I->getOperand(3)->dropDroppableUses();
ASSERT_TRUE(hasMatchesExactlyAttributes(
Assume, I->getOperand(0),
"(align|dereferenceable)"));
ASSERT_TRUE(hasMatchesExactlyAttributes(Assume, I->getOperand(1),
""));
ASSERT_TRUE(hasMatchesExactlyAttributes(Assume, I->getOperand(2),
""));
ASSERT_TRUE(hasMatchesExactlyAttributes(Assume, I->getOperand(3),
""));
ASSERT_TRUE(hasTheRightValue(Assume, I->getOperand(0),
Attribute::AttrKind::Alignment, 32));
ASSERT_TRUE(hasTheRightValue(Assume, I->getOperand(0),
Attribute::AttrKind::Dereferenceable, 48));
}));
Tests.push_back(std::make_pair(
"call void @func(i32* nonnull align 4 dereferenceable(16) %P, i32* align "
"8 noalias %P1)\n",
[](Instruction *I) {
IntrinsicInst *Assume = buildAssumeFromInst(I);
Assume->insertBefore(I);
Value *New = I->getFunction()->getArg(3);
Value *Old = I->getOperand(0);
ASSERT_TRUE(hasMatchesExactlyAttributes(Assume, New, ""));
ASSERT_TRUE(hasMatchesExactlyAttributes(Assume, Old,
"(nonnull|align|dereferenceable)"));
Old->replaceAllUsesWith(New);
ASSERT_TRUE(hasMatchesExactlyAttributes(Assume, New,
"(nonnull|align|dereferenceable)"));
ASSERT_TRUE(hasMatchesExactlyAttributes(Assume, Old, ""));
}));
RunTest(Head, Tail, Tests);
}
static bool FindExactlyAttributes(RetainedKnowledgeMap &Map, Value *WasOn,
StringRef AttrToMatch) {
Regex Reg(AttrToMatch);
SmallVector<StringRef, 1> Matches;
for (StringRef Attr : {
#define GET_ATTR_NAMES
#define ATTRIBUTE_ENUM(ENUM_NAME, DISPLAY_NAME) StringRef(#DISPLAY_NAME),
#include "llvm/IR/Attributes.inc"
}) {
bool ShouldHaveAttr = Reg.match(Attr, &Matches) && Matches[0] == Attr;
if (ShouldHaveAttr != (Map.find(RetainedKnowledgeKey{WasOn, Attribute::getAttrKindFromName(Attr)}) != Map.end()))
return false;
}
return true;
}
static bool MapHasRightValue(RetainedKnowledgeMap &Map, IntrinsicInst *II,
RetainedKnowledgeKey Key, MinMax MM) {
auto LookupIt = Map.find(Key);
return (LookupIt != Map.end()) && (LookupIt->second[II].Min == MM.Min) &&
(LookupIt->second[II].Max == MM.Max);
}
TEST(AssumeQueryAPI, fillMapFromAssume) {
EnableKnowledgeRetention.setValue(true);
StringRef Head =
"declare void @llvm.assume(i1)\n"
"declare void @func(i32*, i32*)\n"
"declare void @func1(i32*, i32*, i32*, i32*)\n"
"declare void @func_many(i32*) \"no-jump-tables\" nounwind "
"\"less-precise-fpmad\" willreturn norecurse\n"
"define void @test(i32* %P, i32* %P1, i32* %P2, i32* %P3) {\n";
StringRef Tail = "ret void\n"
"}";
std::vector<std::pair<StringRef, llvm::function_ref<void(Instruction *)>>>
Tests;
Tests.push_back(std::make_pair(
"call void @func(i32* nonnull align 4 dereferenceable(16) %P, i32* align "
"8 noalias %P1)\n",
[](Instruction *I) {
IntrinsicInst *Assume = buildAssumeFromInst(I);
Assume->insertBefore(I);
RetainedKnowledgeMap Map;
fillMapFromAssume(*Assume, Map);
ASSERT_TRUE(FindExactlyAttributes(Map, I->getOperand(0),
"(nonnull|align|dereferenceable)"));
ASSERT_TRUE(FindExactlyAttributes(Map, I->getOperand(1),
"(align)"));
ASSERT_TRUE(MapHasRightValue(
Map, Assume, {I->getOperand(0), Attribute::Dereferenceable}, {16, 16}));
ASSERT_TRUE(MapHasRightValue(Map, Assume, {I->getOperand(0), Attribute::Alignment},
{4, 4}));
ASSERT_TRUE(MapHasRightValue(Map, Assume, {I->getOperand(0), Attribute::Alignment},
{4, 4}));
}));
Tests.push_back(std::make_pair(
"call void @func1(i32* nonnull align 32 dereferenceable(48) %P, i32* "
"nonnull "
"align 8 dereferenceable(28) %P, i32* nonnull align 64 "
"dereferenceable(4) "
"%P, i32* nonnull align 16 dereferenceable(12) %P)\n",
[](Instruction *I) {
IntrinsicInst *Assume = buildAssumeFromInst(I);
Assume->insertBefore(I);
RetainedKnowledgeMap Map;
fillMapFromAssume(*Assume, Map);
ASSERT_TRUE(FindExactlyAttributes(Map, I->getOperand(0),
"(nonnull|align|dereferenceable)"));
ASSERT_TRUE(FindExactlyAttributes(Map, I->getOperand(1),
"(nonnull|align|dereferenceable)"));
ASSERT_TRUE(FindExactlyAttributes(Map, I->getOperand(2),
"(nonnull|align|dereferenceable)"));
ASSERT_TRUE(FindExactlyAttributes(Map, I->getOperand(3),
"(nonnull|align|dereferenceable)"));
ASSERT_TRUE(MapHasRightValue(
Map, Assume, {I->getOperand(0), Attribute::Dereferenceable},
{48, 48}));
ASSERT_TRUE(MapHasRightValue(
Map, Assume, {I->getOperand(0), Attribute::Alignment}, {64, 64}));
}));
Tests.push_back(std::make_pair(
"call void @func_many(i32* align 8 %P1) cold\n", [](Instruction *I) {
ShouldPreserveAllAttributes.setValue(true);
IntrinsicInst *Assume = buildAssumeFromInst(I);
Assume->insertBefore(I);
RetainedKnowledgeMap Map;
fillMapFromAssume(*Assume, Map);
ASSERT_TRUE(FindExactlyAttributes(
Map, nullptr, "(nounwind|norecurse|willreturn|cold)"));
ShouldPreserveAllAttributes.setValue(false);
}));
Tests.push_back(
std::make_pair("call void @llvm.assume(i1 true)\n", [](Instruction *I) {
RetainedKnowledgeMap Map;
fillMapFromAssume(*cast<IntrinsicInst>(I), Map);
ASSERT_TRUE(FindExactlyAttributes(Map, nullptr, ""));
ASSERT_TRUE(Map.empty());
}));
Tests.push_back(std::make_pair(
"call void @func1(i32* readnone align 32 "
"dereferenceable(48) noalias %P, i32* "
"align 8 dereferenceable(28) %P1, i32* align 64 "
"dereferenceable(4) "
"%P2, i32* nonnull align 16 dereferenceable(12) %P3)\n",
[](Instruction *I) {
IntrinsicInst *Assume = buildAssumeFromInst(I);
Assume->insertBefore(I);
RetainedKnowledgeMap Map;
fillMapFromAssume(*Assume, Map);
ASSERT_TRUE(FindExactlyAttributes(Map, I->getOperand(0),
"(align|dereferenceable)"));
ASSERT_TRUE(FindExactlyAttributes(Map, I->getOperand(1),
"(align|dereferenceable)"));
ASSERT_TRUE(FindExactlyAttributes(Map, I->getOperand(2),
"(align|dereferenceable)"));
ASSERT_TRUE(FindExactlyAttributes(Map, I->getOperand(3),
"(nonnull|align|dereferenceable)"));
ASSERT_TRUE(MapHasRightValue(Map, Assume, {I->getOperand(0), Attribute::Alignment},
{32, 32}));
ASSERT_TRUE(MapHasRightValue(
Map, Assume, {I->getOperand(0), Attribute::Dereferenceable}, {48, 48}));
ASSERT_TRUE(MapHasRightValue(
Map, Assume, {I->getOperand(1), Attribute::Dereferenceable}, {28, 28}));
ASSERT_TRUE(MapHasRightValue(Map, Assume, {I->getOperand(1), Attribute::Alignment},
{8, 8}));
ASSERT_TRUE(MapHasRightValue(Map, Assume, {I->getOperand(2), Attribute::Alignment},
{64, 64}));
ASSERT_TRUE(MapHasRightValue(
Map, Assume, {I->getOperand(2), Attribute::Dereferenceable}, {4, 4}));
ASSERT_TRUE(MapHasRightValue(Map, Assume, {I->getOperand(3), Attribute::Alignment},
{16, 16}));
ASSERT_TRUE(MapHasRightValue(
Map, Assume, {I->getOperand(3), Attribute::Dereferenceable}, {12, 12}));
}));
/// Keep this test last as it modifies the function.
Tests.push_back(std::make_pair(
"call void @func(i32* nonnull align 4 dereferenceable(16) %P, i32* align "
"8 noalias %P1)\n",
[](Instruction *I) {
IntrinsicInst *Assume = buildAssumeFromInst(I);
Assume->insertBefore(I);
RetainedKnowledgeMap Map;
fillMapFromAssume(*Assume, Map);
Value *New = I->getFunction()->getArg(3);
Value *Old = I->getOperand(0);
ASSERT_TRUE(FindExactlyAttributes(Map, New, ""));
ASSERT_TRUE(FindExactlyAttributes(Map, Old,
"(nonnull|align|dereferenceable)"));
Old->replaceAllUsesWith(New);
Map.clear();
fillMapFromAssume(*Assume, Map);
ASSERT_TRUE(FindExactlyAttributes(Map, New,
"(nonnull|align|dereferenceable)"));
ASSERT_TRUE(FindExactlyAttributes(Map, Old, ""));
}));
RunTest(Head, Tail, Tests);
}
static void RunRandTest(uint64_t Seed, int Size, int MinCount, int MaxCount,
unsigned MaxValue) {
LLVMContext C;
SMDiagnostic Err;
std::random_device dev;
std::mt19937 Rng(Seed);
std::uniform_int_distribution<int> DistCount(MinCount, MaxCount);
std::uniform_int_distribution<unsigned> DistValue(0, MaxValue);
std::uniform_int_distribution<unsigned> DistAttr(0,
Attribute::EndAttrKinds - 1);
std::unique_ptr<Module> Mod = std::make_unique<Module>("AssumeQueryAPI", C);
if (!Mod)
Err.print("AssumeQueryAPI", errs());
std::vector<Type *> TypeArgs;
for (int i = 0; i < (Size * 2); i++)
TypeArgs.push_back(Type::getInt32PtrTy(C));
FunctionType *FuncType =
FunctionType::get(Type::getVoidTy(C), TypeArgs, false);
Function *F =
Function::Create(FuncType, GlobalValue::ExternalLinkage, "test", &*Mod);
BasicBlock *BB = BasicBlock::Create(C);
BB->insertInto(F);
Instruction *Ret = ReturnInst::Create(C);
BB->getInstList().insert(BB->begin(), Ret);
Function *FnAssume = Intrinsic::getDeclaration(Mod.get(), Intrinsic::assume);
std::vector<Argument *> ShuffledArgs;
std::vector<bool> HasArg;
for (auto &Arg : F->args()) {
ShuffledArgs.push_back(&Arg);
HasArg.push_back(false);
}
std::shuffle(ShuffledArgs.begin(), ShuffledArgs.end(), Rng);
std::vector<OperandBundleDef> OpBundle;
OpBundle.reserve(Size);
std::vector<Value *> Args;
Args.reserve(2);
for (int i = 0; i < Size; i++) {
int count = DistCount(Rng);
int value = DistValue(Rng);
int attr = DistAttr(Rng);
std::string str;
raw_string_ostream ss(str);
ss << Attribute::getNameFromAttrKind(
static_cast<Attribute::AttrKind>(attr));
Args.clear();
if (count > 0) {
Args.push_back(ShuffledArgs[i]);
HasArg[i] = true;
}
if (count > 1)
Args.push_back(ConstantInt::get(Type::getInt32Ty(C), value));
OpBundle.push_back(OperandBundleDef{ss.str().c_str(), std::move(Args)});
}
auto *Assume = cast<IntrinsicInst>(IntrinsicInst::Create(
FnAssume, ArrayRef<Value *>({ConstantInt::getTrue(C)}), OpBundle));
Assume->insertBefore(&F->begin()->front());
RetainedKnowledgeMap Map;
fillMapFromAssume(*Assume, Map);
for (int i = 0; i < (Size * 2); i++) {
if (!HasArg[i])
continue;
RetainedKnowledge K =
getKnowledgeFromUseInAssume(&*ShuffledArgs[i]->use_begin());
auto LookupIt = Map.find(RetainedKnowledgeKey{K.WasOn, K.AttrKind});
ASSERT_TRUE(LookupIt != Map.end());
MinMax MM = LookupIt->second[Assume];
ASSERT_TRUE(MM.Min == MM.Max);
ASSERT_TRUE(MM.Min == K.ArgValue);
}
}
TEST(AssumeQueryAPI, getKnowledgeFromUseInAssume) {
// // For Fuzzing
// std::random_device dev;
// std::mt19937 Rng(dev());
// while (true) {
// unsigned Seed = Rng();
// dbgs() << Seed << "\n";
// RunRandTest(Seed, 100000, 0, 2, 100);
// }
RunRandTest(23456, 4, 0, 2, 100);
RunRandTest(560987, 25, -3, 2, 100);
// Large bundles can lead to special cases. this is why this test is soo
// large.
RunRandTest(9876789, 100000, -0, 7, 100);
}
TEST(AssumeQueryAPI, AssumptionCache) {
LLVMContext C;
SMDiagnostic Err;
std::unique_ptr<Module> Mod = parseAssemblyString(
"declare void @llvm.assume(i1)\n"
"define void @test(i32* %P, i32* %P1, i32* %P2, i32* %P3, i1 %B) {\n"
"call void @llvm.assume(i1 true) [\"nonnull\"(i32* %P), \"align\"(i32* "
"%P2, i32 4), \"align\"(i32* %P, i32 8)]\n"
"call void @llvm.assume(i1 %B) [\"test\"(i32* %P1), "
"\"dereferenceable\"(i32* %P, i32 4)]\n"
"ret void\n}\n",
Err, C);
if (!Mod)
Err.print("AssumeQueryAPI", errs());
Function *F = Mod->getFunction("test");
BasicBlock::iterator First = F->begin()->begin();
BasicBlock::iterator Second = F->begin()->begin();
Second++;
AssumptionCacheTracker ACT;
AssumptionCache &AC = ACT.getAssumptionCache(*F);
auto AR = AC.assumptionsFor(F->getArg(3));
ASSERT_EQ(AR.size(), 0u);
AR = AC.assumptionsFor(F->getArg(1));
ASSERT_EQ(AR.size(), 1u);
ASSERT_EQ(AR[0].Index, 0u);
ASSERT_EQ(AR[0].Assume, &*Second);
AR = AC.assumptionsFor(F->getArg(2));
ASSERT_EQ(AR.size(), 1u);
ASSERT_EQ(AR[0].Index, 1u);
ASSERT_EQ(AR[0].Assume, &*First);
AR = AC.assumptionsFor(F->getArg(0));
ASSERT_EQ(AR.size(), 3u);
llvm::sort(AR,
[](const auto &L, const auto &R) { return L.Index < R.Index; });
ASSERT_EQ(AR[0].Assume, &*First);
ASSERT_EQ(AR[0].Index, 0u);
ASSERT_EQ(AR[1].Assume, &*Second);
ASSERT_EQ(AR[1].Index, 1u);
ASSERT_EQ(AR[2].Assume, &*First);
ASSERT_EQ(AR[2].Index, 2u);
AR = AC.assumptionsFor(F->getArg(4));
ASSERT_EQ(AR.size(), 1u);
ASSERT_EQ(AR[0].Assume, &*Second);
ASSERT_EQ(AR[0].Index, AssumptionCache::ExprResultIdx);
AC.unregisterAssumption(cast<CallInst>(&*Second));
AR = AC.assumptionsFor(F->getArg(1));
ASSERT_EQ(AR.size(), 0u);
AR = AC.assumptionsFor(F->getArg(0));
ASSERT_EQ(AR.size(), 3u);
llvm::sort(AR,
[](const auto &L, const auto &R) { return L.Index < R.Index; });
ASSERT_EQ(AR[0].Assume, &*First);
ASSERT_EQ(AR[0].Index, 0u);
ASSERT_EQ(AR[1].Assume, nullptr);
ASSERT_EQ(AR[1].Index, 1u);
ASSERT_EQ(AR[2].Assume, &*First);
ASSERT_EQ(AR[2].Index, 2u);
AR = AC.assumptionsFor(F->getArg(2));
ASSERT_EQ(AR.size(), 1u);
ASSERT_EQ(AR[0].Index, 1u);
ASSERT_EQ(AR[0].Assume, &*First);
}
TEST(AssumeQueryAPI, Alignment) {
LLVMContext C;
SMDiagnostic Err;
std::unique_ptr<Module> Mod = parseAssemblyString(
"declare void @llvm.assume(i1)\n"
"define void @test(i32* %P, i32* %P1, i32* %P2, i32 %I3, i1 %B) {\n"
"call void @llvm.assume(i1 true) [\"align\"(i32* %P, i32 8, i32 %I3)]\n"
"call void @llvm.assume(i1 true) [\"align\"(i32* %P1, i32 %I3, i32 "
"%I3)]\n"
"call void @llvm.assume(i1 true) [\"align\"(i32* %P2, i32 16, i32 8)]\n"
"ret void\n}\n",
Err, C);
if (!Mod)
Err.print("AssumeQueryAPI", errs());
Function *F = Mod->getFunction("test");
BasicBlock::iterator Start = F->begin()->begin();
IntrinsicInst *II;
RetainedKnowledge RK;
II = cast<IntrinsicInst>(&*Start);
RK = getKnowledgeFromBundle(*II, II->bundle_op_info_begin()[0]);
ASSERT_EQ(RK.AttrKind, Attribute::Alignment);
ASSERT_EQ(RK.WasOn, F->getArg(0));
ASSERT_EQ(RK.ArgValue, 1u);
Start++;
II = cast<IntrinsicInst>(&*Start);
RK = getKnowledgeFromBundle(*II, II->bundle_op_info_begin()[0]);
ASSERT_EQ(RK.AttrKind, Attribute::Alignment);
ASSERT_EQ(RK.WasOn, F->getArg(1));
ASSERT_EQ(RK.ArgValue, 1u);
Start++;
II = cast<IntrinsicInst>(&*Start);
RK = getKnowledgeFromBundle(*II, II->bundle_op_info_begin()[0]);
ASSERT_EQ(RK.AttrKind, Attribute::Alignment);
ASSERT_EQ(RK.WasOn, F->getArg(2));
ASSERT_EQ(RK.ArgValue, 8u);
}