513 lines · cpp
1//===- RegionTest.cpp -----------------------------------------------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8 9#include "llvm/SandboxIR/Region.h"10#include "llvm/Analysis/TargetTransformInfo.h"11#include "llvm/AsmParser/Parser.h"12#include "llvm/SandboxIR/Context.h"13#include "llvm/SandboxIR/Function.h"14#include "llvm/SandboxIR/Instruction.h"15#include "llvm/Support/SourceMgr.h"16#include "gmock/gmock-matchers.h"17#include "gtest/gtest.h"18 19using namespace llvm;20 21struct RegionTest : public testing::Test {22 LLVMContext C;23 std::unique_ptr<Module> M;24 std::unique_ptr<TargetTransformInfo> TTI;25 26 void parseIR(LLVMContext &C, const char *IR) {27 SMDiagnostic Err;28 M = parseAssemblyString(IR, Err, C);29 TTI = std::make_unique<TargetTransformInfo>(M->getDataLayout());30 if (!M)31 Err.print("RegionTest", errs());32 }33};34 35TEST_F(RegionTest, Basic) {36 parseIR(C, R"IR(37define i8 @foo(i8 %v0, i8 %v1) {38 %t0 = add i8 %v0, 139 %t1 = add i8 %t0, %v140 ret i8 %t141}42)IR");43 llvm::Function *LLVMF = &*M->getFunction("foo");44 sandboxir::Context Ctx(C);45 auto *F = Ctx.createFunction(LLVMF);46 auto *BB = &*F->begin();47 auto It = BB->begin();48 auto *T0 = cast<sandboxir::Instruction>(&*It++);49 auto *T1 = cast<sandboxir::Instruction>(&*It++);50 auto *Ret = cast<sandboxir::Instruction>(&*It++);51 sandboxir::Region Rgn(Ctx, *TTI);52 53 // Check getContext.54 EXPECT_EQ(&Ctx, &Rgn.getContext());55 56 // Check add / remove / empty.57 EXPECT_TRUE(Rgn.empty());58 sandboxir::RegionInternalsAttorney::add(Rgn, T0);59 EXPECT_FALSE(Rgn.empty());60 sandboxir::RegionInternalsAttorney::remove(Rgn, T0);61 EXPECT_TRUE(Rgn.empty());62 63 // Check iteration.64 sandboxir::RegionInternalsAttorney::add(Rgn, T0);65 sandboxir::RegionInternalsAttorney::add(Rgn, T1);66 sandboxir::RegionInternalsAttorney::add(Rgn, Ret);67 // Use an ordered matcher because we're supposed to preserve the insertion68 // order for determinism.69 EXPECT_THAT(Rgn.insts(), testing::ElementsAre(T0, T1, Ret));70 71 // Check contains72 EXPECT_TRUE(Rgn.contains(T0));73 sandboxir::RegionInternalsAttorney::remove(Rgn, T0);74 EXPECT_FALSE(Rgn.contains(T0));75 76#ifndef NDEBUG77 // Check equality comparison. Insert in reverse order into `Other` to check78 // that comparison is order-independent.79 sandboxir::Region Other(Ctx, *TTI);80 sandboxir::RegionInternalsAttorney::add(Other, Ret);81 EXPECT_NE(Rgn, Other);82 sandboxir::RegionInternalsAttorney::add(Other, T1);83 EXPECT_EQ(Rgn, Other);84#endif85}86 87TEST_F(RegionTest, CallbackUpdates) {88 parseIR(C, R"IR(89define i8 @foo(i8 %v0, i8 %v1, ptr %ptr) {90 %t0 = add i8 %v0, 191 %t1 = add i8 %t0, %v192 ret i8 %t093}94)IR");95 llvm::Function *LLVMF = &*M->getFunction("foo");96 sandboxir::Context Ctx(C);97 auto *F = Ctx.createFunction(LLVMF);98 auto *Ptr = F->getArg(2);99 auto *BB = &*F->begin();100 auto It = BB->begin();101 auto *T0 = cast<sandboxir::Instruction>(&*It++);102 auto *T1 = cast<sandboxir::Instruction>(&*It++);103 auto *Ret = cast<sandboxir::Instruction>(&*It++);104 sandboxir::Region Rgn(Ctx, *TTI);105 sandboxir::RegionInternalsAttorney::add(Rgn, T0);106 sandboxir::RegionInternalsAttorney::add(Rgn, T1);107 108 // Test creation.109 auto *NewI = sandboxir::StoreInst::create(T0, Ptr, /*Align=*/std::nullopt,110 Ret->getIterator(), Ctx);111 EXPECT_THAT(Rgn.insts(), testing::ElementsAre(T0, T1, NewI));112 113 // Test deletion.114 T1->eraseFromParent();115 EXPECT_THAT(Rgn.insts(), testing::ElementsAre(T0, NewI));116}117 118TEST_F(RegionTest, MetadataFromIR) {119 parseIR(C, R"IR(120define i8 @foo(i8 %v0, i8 %v1) {121 %t0 = add i8 %v0, 1, !sandboxvec !0122 %t1 = add i8 %t0, %v1, !sandboxvec !1123 %t2 = add i8 %t1, %v1, !sandboxvec !1124 ret i8 %t2125}126 127!0 = distinct !{!"sandboxregion"}128!1 = distinct !{!"sandboxregion"}129)IR");130 llvm::Function *LLVMF = &*M->getFunction("foo");131 sandboxir::Context Ctx(C);132 auto *F = Ctx.createFunction(LLVMF);133 auto *BB = &*F->begin();134 auto It = BB->begin();135 auto *T0 = cast<sandboxir::Instruction>(&*It++);136 auto *T1 = cast<sandboxir::Instruction>(&*It++);137 auto *T2 = cast<sandboxir::Instruction>(&*It++);138 139 SmallVector<std::unique_ptr<sandboxir::Region>> Regions =140 sandboxir::Region::createRegionsFromMD(*F, *TTI);141 EXPECT_THAT(Regions[0]->insts(), testing::UnorderedElementsAre(T0));142 EXPECT_THAT(Regions[1]->insts(), testing::UnorderedElementsAre(T1, T2));143}144 145TEST_F(RegionTest, NonContiguousRegion) {146 parseIR(C, R"IR(147define i8 @foo(i8 %v0, i8 %v1) {148 %t0 = add i8 %v0, 1, !sandboxvec !0149 %t1 = add i8 %t0, %v1150 %t2 = add i8 %t1, %v1, !sandboxvec !0151 ret i8 %t2152}153 154!0 = distinct !{!"sandboxregion"}155)IR");156 llvm::Function *LLVMF = &*M->getFunction("foo");157 sandboxir::Context Ctx(C);158 auto *F = Ctx.createFunction(LLVMF);159 auto *BB = &*F->begin();160 auto It = BB->begin();161 auto *T0 = cast<sandboxir::Instruction>(&*It++);162 [[maybe_unused]] auto *T1 = cast<sandboxir::Instruction>(&*It++);163 auto *T2 = cast<sandboxir::Instruction>(&*It++);164 165 SmallVector<std::unique_ptr<sandboxir::Region>> Regions =166 sandboxir::Region::createRegionsFromMD(*F, *TTI);167 EXPECT_THAT(Regions[0]->insts(), testing::UnorderedElementsAre(T0, T2));168}169 170TEST_F(RegionTest, DumpedMetadata) {171 parseIR(C, R"IR(172define i8 @foo(i8 %v0, i8 %v1) {173 %t0 = add i8 %v0, 1174 %t1 = add i8 %t0, %v1175 %t2 = add i8 %t1, %v1176 ret i8 %t1177}178)IR");179 llvm::Function *LLVMF = &*M->getFunction("foo");180 sandboxir::Context Ctx(C);181 auto *F = Ctx.createFunction(LLVMF);182 auto *BB = &*F->begin();183 auto It = BB->begin();184 auto *T0 = cast<sandboxir::Instruction>(&*It++);185 [[maybe_unused]] auto *T1 = cast<sandboxir::Instruction>(&*It++);186 auto *T2 = cast<sandboxir::Instruction>(&*It++);187 [[maybe_unused]] auto *Ret = cast<sandboxir::Instruction>(&*It++);188 sandboxir::Region Rgn(Ctx, *TTI);189 sandboxir::RegionInternalsAttorney::add(Rgn, T0);190 sandboxir::Region Rgn2(Ctx, *TTI);191 sandboxir::RegionInternalsAttorney::add(Rgn2, T2);192 193 std::string output;194 llvm::raw_string_ostream RSO(output);195 M->print(RSO, nullptr, /*ShouldPreserveUseListOrder=*/true,196 /*IsForDebug=*/true);197 198 // TODO: Replace this with a lit test, which is more suitable for this kind199 // of IR comparison.200 std::string expected = R"(; ModuleID = '<string>'201source_filename = "<string>"202 203define i8 @foo(i8 %v0, i8 %v1) {204 %t0 = add i8 %v0, 1, !sandboxvec !0205 %t1 = add i8 %t0, %v1206 %t2 = add i8 %t1, %v1, !sandboxvec !1207 ret i8 %t1208}209 210!0 = distinct !{!"sandboxregion"}211!1 = distinct !{!"sandboxregion"}212)";213 EXPECT_EQ(expected, output);214}215 216TEST_F(RegionTest, MetadataRoundTrip) {217 parseIR(C, R"IR(218define i8 @foo(i8 %v0, i8 %v1) {219 %t0 = add i8 %v0, 1220 %t1 = add i8 %t0, %v1221 ret i8 %t1222}223)IR");224 llvm::Function *LLVMF = &*M->getFunction("foo");225 sandboxir::Context Ctx(C);226 auto *F = Ctx.createFunction(LLVMF);227 auto *BB = &*F->begin();228 auto It = BB->begin();229 auto *T0 = cast<sandboxir::Instruction>(&*It++);230 auto *T1 = cast<sandboxir::Instruction>(&*It++);231 232 sandboxir::Region Rgn(Ctx, *TTI);233 sandboxir::RegionInternalsAttorney::add(Rgn, T0);234 sandboxir::RegionInternalsAttorney::add(Rgn, T1);235 236 SmallVector<std::unique_ptr<sandboxir::Region>> Regions =237 sandboxir::Region::createRegionsFromMD(*F, *TTI);238 ASSERT_EQ(1U, Regions.size());239#ifndef NDEBUG240 EXPECT_EQ(Rgn, *Regions[0].get());241#endif242}243 244TEST_F(RegionTest, RegionCost) {245 parseIR(C, R"IR(246define void @foo(i8 %v0, i8 %v1, i8 %v2) {247 %add0 = add i8 %v0, 1248 %add1 = add i8 %v1, 2249 %add2 = add i8 %v2, 3250 ret void251}252)IR");253 llvm::Function *LLVMF = &*M->getFunction("foo");254 auto *LLVMBB = &*LLVMF->begin();255 auto LLVMIt = LLVMBB->begin();256 auto *LLVMAdd0 = &*LLVMIt++;257 auto *LLVMAdd1 = &*LLVMIt++;258 auto *LLVMAdd2 = &*LLVMIt++;259 260 sandboxir::Context Ctx(C);261 auto *F = Ctx.createFunction(LLVMF);262 auto *BB = &*F->begin();263 auto It = BB->begin();264 auto *Add0 = cast<sandboxir::Instruction>(&*It++);265 auto *Add1 = cast<sandboxir::Instruction>(&*It++);266 auto *Add2 = cast<sandboxir::Instruction>(&*It++);267 268 sandboxir::Region Rgn(Ctx, *TTI);269 const auto &SB = Rgn.getScoreboard();270 EXPECT_EQ(SB.getAfterCost(), 0);271 EXPECT_EQ(SB.getBeforeCost(), 0);272 273 auto GetCost = [this](llvm::Instruction *LLVMI) {274 constexpr static TTI::TargetCostKind CostKind = TTI::TCK_RecipThroughput;275 SmallVector<const llvm::Value *> Operands(LLVMI->operands());276 return TTI->getInstructionCost(LLVMI, Operands, CostKind);277 };278 // Add `Add0` to the region, should be counted in "After".279 sandboxir::RegionInternalsAttorney::add(Rgn, Add0);280 EXPECT_EQ(SB.getBeforeCost(), 0);281 EXPECT_EQ(SB.getAfterCost(), GetCost(LLVMAdd0));282 // Same for `Add1`.283 sandboxir::RegionInternalsAttorney::add(Rgn, Add1);284 EXPECT_EQ(SB.getBeforeCost(), 0);285 EXPECT_EQ(SB.getAfterCost(), GetCost(LLVMAdd0) + GetCost(LLVMAdd1));286 // Remove `Add0`, should be subtracted from "After".287 sandboxir::RegionInternalsAttorney::remove(Rgn, Add0);288 EXPECT_EQ(SB.getBeforeCost(), 0);289 EXPECT_EQ(SB.getAfterCost(), GetCost(LLVMAdd1));290 // Remove `Add2` which was never in the region, should counted in "Before".291 sandboxir::RegionInternalsAttorney::remove(Rgn, Add2);292 EXPECT_EQ(SB.getBeforeCost(), GetCost(LLVMAdd2));293 EXPECT_EQ(SB.getAfterCost(), GetCost(LLVMAdd1));294}295 296TEST_F(RegionTest, Aux) {297 parseIR(C, R"IR(298define void @foo(i8 %v) {299 %t0 = add i8 %v, 0, !sandboxvec !0, !sandboxaux !2300 %t1 = add i8 %v, 1, !sandboxvec !0, !sandboxaux !3301 %t2 = add i8 %v, 2, !sandboxvec !1302 %t3 = add i8 %v, 3, !sandboxvec !1, !sandboxaux !2303 %t4 = add i8 %v, 4, !sandboxvec !1, !sandboxaux !4304 %t5 = add i8 %v, 5, !sandboxvec !1, !sandboxaux !3305 ret void306}307 308!0 = distinct !{!"sandboxregion"}309!1 = distinct !{!"sandboxregion"}310 311!2 = !{i32 0}312!3 = !{i32 1}313!4 = !{i32 2}314)IR");315 llvm::Function *LLVMF = &*M->getFunction("foo");316 auto *LLVMBB = &*LLVMF->begin();317 auto LLVMIt = LLVMBB->begin();318 auto *LLVMI0 = &*LLVMIt++;319 auto *LLVMI1 = &*LLVMIt++;320 sandboxir::Context Ctx(C);321 auto *F = Ctx.createFunction(LLVMF);322 auto *BB = &*F->begin();323 auto It = BB->begin();324 auto *T0 = cast<sandboxir::Instruction>(&*It++);325 auto *T1 = cast<sandboxir::Instruction>(&*It++);326 auto *T2 = cast<sandboxir::Instruction>(&*It++);327 auto *T3 = cast<sandboxir::Instruction>(&*It++);328 auto *T4 = cast<sandboxir::Instruction>(&*It++);329 auto *T5 = cast<sandboxir::Instruction>(&*It++);330 331 SmallVector<std::unique_ptr<sandboxir::Region>> Regions =332 sandboxir::Region::createRegionsFromMD(*F, *TTI);333 // Check that the regions are correct.334 EXPECT_THAT(Regions[0]->insts(), testing::UnorderedElementsAre(T0, T1));335 EXPECT_THAT(Regions[1]->insts(),336 testing::UnorderedElementsAre(T2, T3, T4, T5));337 // Check aux.338 EXPECT_THAT(Regions[0]->getAux(), testing::ElementsAre(T0, T1));339 EXPECT_THAT(Regions[1]->getAux(), testing::ElementsAre(T3, T5, T4));340 // Check clearAux().341 EXPECT_TRUE(LLVMI0->getMetadata("sandboxaux"));342 EXPECT_TRUE(LLVMI1->getMetadata("sandboxaux"));343 Regions[0]->clearAux();344 EXPECT_TRUE(Regions[0]->getAux().empty());345 EXPECT_FALSE(LLVMI0->getMetadata("sandboxaux"));346 EXPECT_FALSE(LLVMI1->getMetadata("sandboxaux"));347}348 349// Check that Aux is well-formed.350TEST_F(RegionTest, AuxVerify) {351 parseIR(C, R"IR(352define void @foo(i8 %v) {353 %t0 = add i8 %v, 0, !sandboxvec !0, !sandboxaux !2354 %t1 = add i8 %v, 1, !sandboxvec !0, !sandboxaux !3355 ret void356}357 358!0 = distinct !{!"sandboxregion"}359!2 = !{i32 0}360!3 = !{i32 2}361)IR");362 llvm::Function *LLVMF = &*M->getFunction("foo");363 sandboxir::Context Ctx(C);364 auto *F = Ctx.createFunction(LLVMF);365 EXPECT_DEBUG_DEATH(sandboxir::Region::createRegionsFromMD(*F, *TTI),366 ".*Gap*");367}368 369// Check that we get an assertion failure if we try to set the same index more370// than once.371TEST_F(RegionTest, AuxSameIndex) {372 parseIR(C, R"IR(373define void @foo(i8 %v) {374 %t0 = add i8 %v, 0, !sandboxvec !0, !sandboxaux !2375 %t1 = add i8 %v, 1, !sandboxvec !0, !sandboxaux !2376 ret void377}378 379!0 = distinct !{!"sandboxregion"}380!2 = !{i32 0}381)IR");382 llvm::Function *LLVMF = &*M->getFunction("foo");383 sandboxir::Context Ctx(C);384 auto *F = Ctx.createFunction(LLVMF);385 EXPECT_DEBUG_DEATH(sandboxir::Region::createRegionsFromMD(*F, *TTI),386 ".*already.*");387}388 389// Check that Aux automatically drops instructions that get deleted.390TEST_F(RegionTest, AuxDeleteInstr) {391 parseIR(C, R"IR(392define void @foo(i8 %v) {393 %Add0 = add i8 %v, 0, !sandboxvec !0, !sandboxaux !1394 %Add1 = add i8 %v, 1, !sandboxvec !0, !sandboxaux !2395 %Add2 = add i8 %v, 2, !sandboxvec !0, !sandboxaux !3396 %Add3 = add i8 %v, 2, !sandboxvec !0, !sandboxaux !4397 ret void398}399 400!0 = distinct !{!"sandboxregion"}401!1 = !{i32 0}402!2 = !{i32 1}403!3 = !{i32 2}404!4 = !{i32 3}405)IR");406 llvm::Function *LLVMF = &*M->getFunction("foo");407 sandboxir::Context Ctx(C);408 auto *F = Ctx.createFunction(LLVMF);409 auto *BB = &*F->begin();410 auto It = BB->begin();411 auto *Add0 = &*It++;412 auto *Add1 = &*It++;413 auto *Add2 = &*It++;414 auto *Add3 = &*It++;415 SmallVector<std::unique_ptr<sandboxir::Region>> Regions =416 sandboxir::Region::createRegionsFromMD(*F, *TTI);417 auto &R = *Regions[0];418 EXPECT_THAT(R.getAux(), testing::ElementsAre(Add0, Add1, Add2, Add3));419 // Now delete Add1 and check that Aux contains nullptr instead of Add1.420 Add2->eraseFromParent();421 EXPECT_THAT(R.getAux(), testing::ElementsAre(Add0, Add1, Add3));422 {423 // Check that metadata have also been updated.424 // But first drop Add3 to create a legal Aux vector with no gaps.425 Add3->eraseFromParent();426 SmallVector<std::unique_ptr<sandboxir::Region>> Regions =427 sandboxir::Region::createRegionsFromMD(*F, *TTI);428 EXPECT_THAT(Regions[0]->getAux(), testing::ElementsAre(Add0, Add1));429 }430}431 432TEST_F(RegionTest, AuxWithoutRegion) {433 parseIR(C, R"IR(434define void @foo(i8 %v) {435 %Add0 = add i8 %v, 0, !sandboxaux !0436 ret void437}438!0 = !{i32 0}439)IR");440#ifndef NDEBUG441 llvm::Function *LLVMF = &*M->getFunction("foo");442 sandboxir::Context Ctx(C);443 auto *F = Ctx.createFunction(LLVMF);444 EXPECT_DEATH(sandboxir::Region::createRegionsFromMD(*F, *TTI), "No region.*");445#endif446}447 448TEST_F(RegionTest, AuxRoundTrip) {449 parseIR(C, R"IR(450define i8 @foo(i8 %v0, i8 %v1) {451 %t0 = add i8 %v0, 1452 %t1 = add i8 %t0, %v1453 ret i8 %t1454}455)IR");456 llvm::Function *LLVMF = &*M->getFunction("foo");457 sandboxir::Context Ctx(C);458 auto *F = Ctx.createFunction(LLVMF);459 auto *BB = &*F->begin();460 auto It = BB->begin();461 auto *T0 = cast<sandboxir::Instruction>(&*It++);462 auto *T1 = cast<sandboxir::Instruction>(&*It++);463 464 sandboxir::Region Rgn(Ctx, *TTI);465#ifndef NDEBUG466 EXPECT_DEATH(Rgn.setAux({T0, T0}), ".*already.*");467#endif468 sandboxir::RegionInternalsAttorney::add(Rgn, T0);469 sandboxir::RegionInternalsAttorney::add(Rgn, T1);470 Rgn.setAux({T1, T0});471 472 SmallVector<std::unique_ptr<sandboxir::Region>> Regions =473 sandboxir::Region::createRegionsFromMD(*F, *TTI);474 ASSERT_EQ(1U, Regions.size());475#ifndef NDEBUG476 EXPECT_EQ(Rgn, *Regions[0].get());477#endif478 EXPECT_THAT(Rgn.getAux(), testing::ElementsAre(T1, T0));479}480 481// Same as before but only add instructions to aux. They should get added too482// the region too automatically.483TEST_F(RegionTest, AuxOnlyRoundTrip) {484 parseIR(C, R"IR(485define void @foo(i8 %v) {486 %add0 = add i8 %v, 0487 %add1 = add i8 %v, 1488 ret void489}490)IR");491 llvm::Function *LLVMF = &*M->getFunction("foo");492 sandboxir::Context Ctx(C);493 auto *F = Ctx.createFunction(LLVMF);494 auto *BB = &*F->begin();495 auto It = BB->begin();496 auto *Add0 = cast<sandboxir::Instruction>(&*It++);497 auto *Add1 = cast<sandboxir::Instruction>(&*It++);498 499 sandboxir::Region Rgn(Ctx, *TTI);500#ifndef NDEBUG501 EXPECT_DEATH(Rgn.setAux({Add0, Add0}), ".*already.*");502#endif503 Rgn.setAux({Add1, Add0});504 505 SmallVector<std::unique_ptr<sandboxir::Region>> Regions =506 sandboxir::Region::createRegionsFromMD(*F, *TTI);507 ASSERT_EQ(1U, Regions.size());508#ifndef NDEBUG509 EXPECT_EQ(Rgn, *Regions[0].get());510#endif511 EXPECT_THAT(Rgn.getAux(), testing::ElementsAre(Add1, Add0));512}513