//===- 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 using namespace llvm; extern cl::opt ShouldPreserveAllAttributes; extern cl::opt EnableKnowledgeRetention; static void RunTest( StringRef Head, StringRef Tail, std::vector>> &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 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 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>> 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(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 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>> 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(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 DistCount(MinCount, MaxCount); std::uniform_int_distribution DistValue(0, MaxValue); std::uniform_int_distribution DistAttr(0, Attribute::EndAttrKinds - 1); std::unique_ptr Mod = std::make_unique("AssumeQueryAPI", C); if (!Mod) Err.print("AssumeQueryAPI", errs()); std::vector 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 ShuffledArgs; std::vector HasArg; for (auto &Arg : F->args()) { ShuffledArgs.push_back(&Arg); HasArg.push_back(false); } std::shuffle(ShuffledArgs.begin(), ShuffledArgs.end(), Rng); std::vector OpBundle; OpBundle.reserve(Size); std::vector 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(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::Create( FnAssume, ArrayRef({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 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(&*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 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(&*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(&*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(&*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); }