mirror of
https://github.com/RPCS3/llvm-mirror.git
synced 2024-11-24 03:33:20 +01:00
8a1b84f3ac
Summary: refactor assume bulider for the next patch. the assume builder now generate only one assume per attribute kind and per value they are on. to do this it takes the highest. this is desirable because currently, for all attributes the higest value is the most valuable. Reviewers: jdoerfert Reviewed By: jdoerfert Subscribers: hiraditya, llvm-commits Tags: #llvm Differential Revision: https://reviews.llvm.org/D78013
549 lines
23 KiB
C++
549 lines
23 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);
|
|
}
|