cb424d7448
verify-patch-sanity.py validates every active recipe .patch has internally- consistent hunk line counts — catching the 'malformed patch at line N' failure at commit/CI/preflight time instead of hours into a cook. This cycle hit that class three times (qtwaylandscanner, sddm, xwayland), each only discovered when cookbook tried to apply the patch. Running it across the repo found 29 latent malformed patches (validated against GNU patch: e.g. relibc/P3-sysv-ipc reproduces 'malformed patch at line 22'). They were harmless only because they sit in vendored recipes (baked, not re- applied) — but would fail on any version-bump re-derivation. --fix recounts the hunk headers (body untouched) and repaired all 29. Wired into build-preflight.sh (Phase 1.0D) and redbear-ci.yml, with a unit test (test-patch-sanity.sh). Skips archived/legacy trees and unvalidatable formats (empty placeholders, bare-@@ git hunks).
604 lines
25 KiB
C++
604 lines
25 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/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;
|
|
|
|
namespace llvm {
|
|
LLVM_ABI extern cl::opt<bool> ShouldPreserveAllAttributes;
|
|
} // 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()));
|
|
}
|
|
}
|
|
|
|
bool hasMatchesExactlyAttributes(AssumeInst *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(AssumeInst *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*, 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, i32* align 8 noundef %P2)\n",
|
|
[](Instruction *I) {
|
|
auto *Assume = buildAssumeFromInst(I);
|
|
Assume->insertBefore(I->getIterator());
|
|
ASSERT_TRUE(hasMatchesExactlyAttributes(Assume, I->getOperand(0),
|
|
"(nonnull|align|dereferenceable)"));
|
|
ASSERT_TRUE(hasMatchesExactlyAttributes(Assume, I->getOperand(1),
|
|
"()"));
|
|
ASSERT_TRUE(hasMatchesExactlyAttributes(Assume, I->getOperand(2),
|
|
"(align|noundef)"));
|
|
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) {
|
|
auto *Assume = buildAssumeFromInst(I);
|
|
Assume->insertBefore(I->getIterator());
|
|
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 noundef %P1) cold\n", [](Instruction *I) {
|
|
ShouldPreserveAllAttributes.setValue(true);
|
|
auto *Assume = buildAssumeFromInst(I);
|
|
Assume->insertBefore(I->getIterator());
|
|
ASSERT_TRUE(hasMatchesExactlyAttributes(
|
|
Assume, nullptr,
|
|
"(align|nounwind|norecurse|noundef|willreturn|cold)"));
|
|
ShouldPreserveAllAttributes.setValue(false);
|
|
}));
|
|
Tests.push_back(
|
|
std::make_pair("call void @llvm.assume(i1 true)\n", [](Instruction *I) {
|
|
auto *Assume = cast<AssumeInst>(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) {
|
|
auto *Assume = buildAssumeFromInst(I);
|
|
Assume->insertBefore(I->getIterator());
|
|
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) {
|
|
auto *Assume = buildAssumeFromInst(I);
|
|
Assume->insertBefore(I->getIterator());
|
|
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, i32* %P1)\n",
|
|
[](Instruction *I) {
|
|
auto *Assume = buildAssumeFromInst(I);
|
|
Assume->insertBefore(I->getIterator());
|
|
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.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(i32*, 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, i32* 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(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) {
|
|
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 @func_many(i32* align 8 %P1) cold\n", [](Instruction *I) {
|
|
ShouldPreserveAllAttributes.setValue(true);
|
|
auto *Assume = buildAssumeFromInst(I);
|
|
Assume->insertBefore(I->getIterator());
|
|
|
|
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<AssumeInst>(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) {
|
|
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(i32* nonnull align 4 dereferenceable(16) %P, i32* align "
|
|
"8 noalias %P1, i32* %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\"(i8* 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);
|
|
}
|
|
|
|
static void RunRandTest(uint64_t Seed, int Size, int MinCount, int MaxCount,
|
|
unsigned MaxValue) {
|
|
LLVMContext C;
|
|
SMDiagnostic Err;
|
|
|
|
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(PointerType::getUnqual(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);
|
|
Ret->insertInto(BB, BB->begin());
|
|
Function *FnAssume =
|
|
Intrinsic::getOrInsertDeclaration(Mod.get(), Intrinsic::assume);
|
|
|
|
std::vector<Argument *> ShuffledArgs;
|
|
BitVector 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{str.c_str(), std::move(Args)});
|
|
}
|
|
|
|
auto *Assume = cast<AssumeInst>(CallInst::Create(
|
|
FnAssume, ArrayRef<Value *>({ConstantInt::getTrue(C)}), OpBundle));
|
|
Assume->insertBefore(F->begin()->begin());
|
|
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++;
|
|
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(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();
|
|
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);
|
|
}
|