blob: 6a55454ef574a70857194c850b29b02ff2c8f24d [file] [edit]
//===- 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/AssumeBundleQueries.h"
#include "llvm/Analysis/AssumptionCache.h"
#include "llvm/AsmParser/Parser.h"
#include "llvm/IR/IntrinsicInst.h"
#include "llvm/IR/LLVMContext.h"
#include "llvm/IR/Module.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/Compiler.h"
#include "llvm/Support/Regex.h"
#include "llvm/Support/SourceMgr.h"
#include "llvm/Transforms/Utils/AssumeBundleBuilder.h"
#include "gtest/gtest.h"
#include <random>
using namespace llvm;
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()));
}
}
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.contains(RetainedKnowledgeKey{
WasOn, Attribute::getAttrKindFromName(Attr)})))
return false;
}
return true;
}
static bool MapHasRightValue(RetainedKnowledgeMap &Map, AssumeInst *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(ptr, ptr, ptr)\n"
"declare void @func1(ptr, ptr, ptr, ptr)\n"
"declare void @func_many(ptr) \"no-jump-tables\" nounwind "
"\"less-precise-fpmad\" willreturn norecurse\n"
"define void @test(ptr %P, ptr %P1, ptr %P2, ptr %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(ptr nonnull align 4 dereferenceable(16) %P, ptr align "
"8 noalias %P1, ptr align 8 dereferenceable(8) %P2)\n",
[](Instruction *I) {
auto *Assume = buildAssumeFromInst(I);
Assume->insertBefore(I->getIterator());
RetainedKnowledgeMap Map;
fillMapFromAssume(*Assume, Map);
ASSERT_TRUE(FindExactlyAttributes(Map, I->getOperand(0),
"(nonnull|align|dereferenceable)"));
ASSERT_FALSE(FindExactlyAttributes(Map, I->getOperand(1),
"(align)"));
ASSERT_TRUE(FindExactlyAttributes(Map, I->getOperand(2),
"(align|dereferenceable)"));
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(ptr nonnull align 32 dereferenceable(48) %P, ptr "
"nonnull "
"align 8 dereferenceable(28) %P, ptr nonnull align 64 "
"dereferenceable(4) "
"%P, ptr nonnull align 16 dereferenceable(12) %P)\n",
[](Instruction *I) {
auto *Assume = buildAssumeFromInst(I);
Assume->insertBefore(I->getIterator());
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 @llvm.assume(i1 true)\n", [](Instruction *I) {
RetainedKnowledgeMap Map;
fillMapFromAssume(*cast<AssumeInst>(I), Map);
ASSERT_TRUE(FindExactlyAttributes(Map, nullptr, ""));
ASSERT_TRUE(Map.empty());
}));
Tests.push_back(std::make_pair(
"call void @func1(ptr readnone align 32 "
"dereferenceable(48) noalias %P, ptr "
"align 8 dereferenceable(28) %P1, ptr align 64 "
"dereferenceable(4) "
"%P2, ptr nonnull align 16 dereferenceable(12) %P3)\n",
[](Instruction *I) {
auto *Assume = buildAssumeFromInst(I);
Assume->insertBefore(I->getIterator());
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(ptr nonnull align 4 dereferenceable(16) %P, ptr align "
"8 noalias %P1, ptr %P2)\n",
[](Instruction *I) {
auto *Assume = buildAssumeFromInst(I);
Assume->insertBefore(I->getIterator());
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, ""));
}));
Tests.push_back(std::make_pair(
"call void @llvm.assume(i1 true) [\"align\"(ptr undef, i32 undef)]",
[](Instruction *I) {
// Don't crash but don't learn from undef.
RetainedKnowledgeMap Map;
fillMapFromAssume(*cast<AssumeInst>(I), Map);
ASSERT_TRUE(Map.empty());
}));
RunTest(Head, Tail, Tests);
}
TEST(AssumeQueryAPI, AssumptionCache) {
LLVMContext C;
SMDiagnostic Err;
std::unique_ptr<Module> Mod = parseAssemblyString(
"declare void @llvm.assume(i1)\n"
"define void @test(ptr %P, ptr %P1, ptr %P2, ptr %P3, i1 %B) {\n"
"call void @llvm.assume(i1 true) [\"nonnull\"(ptr %P), \"align\"(ptr "
"%P2, i32 4), \"align\"(ptr %P, i32 8)]\n"
"call void @llvm.assume(i1 %B) [\"test\"(ptr %P1), "
"\"dereferenceable\"(ptr %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++;
AssumptionCache AC(*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<AssumeInst>(&*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(ptr %P, ptr %P1, ptr %P2, i32 %I3, i1 %B) {\n"
"call void @llvm.assume(i1 true) [\"align\"(ptr %P, i32 8, i32 %I3)]\n"
"call void @llvm.assume(i1 true) [\"align\"(ptr %P1, i32 %I3, i32 "
"%I3)]\n"
"call void @llvm.assume(i1 true) [\"align\"(ptr %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();
AssumeInst *II;
RetainedKnowledge RK;
II = cast<AssumeInst>(&*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<AssumeInst>(&*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<AssumeInst>(&*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);
}