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-exception 6 // 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 19 using namespace llvm; 20 21 struct 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 35 TEST_F(RegionTest, Basic) { 36 parseIR(C, R"IR( 37 define i8 @foo(i8 %v0, i8 %v1) { 38 %t0 = add i8 %v0, 1 39 %t1 = add i8 %t0, %v1 40 ret i8 %t1 41 } 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 Rgn.add(T0); 59 EXPECT_FALSE(Rgn.empty()); 60 Rgn.remove(T0); 61 EXPECT_TRUE(Rgn.empty()); 62 63 // Check iteration. 64 Rgn.add(T0); 65 Rgn.add(T1); 66 Rgn.add(Ret); 67 // Use an ordered matcher because we're supposed to preserve the insertion 68 // order for determinism. 69 EXPECT_THAT(Rgn.insts(), testing::ElementsAre(T0, T1, Ret)); 70 71 // Check contains 72 EXPECT_TRUE(Rgn.contains(T0)); 73 Rgn.remove(T0); 74 EXPECT_FALSE(Rgn.contains(T0)); 75 76 #ifndef NDEBUG 77 // Check equality comparison. Insert in reverse order into `Other` to check 78 // that comparison is order-independent. 79 sandboxir::Region Other(Ctx, *TTI); 80 Other.add(Ret); 81 EXPECT_NE(Rgn, Other); 82 Other.add(T1); 83 EXPECT_EQ(Rgn, Other); 84 #endif 85 } 86 87 TEST_F(RegionTest, CallbackUpdates) { 88 parseIR(C, R"IR( 89 define i8 @foo(i8 %v0, i8 %v1, ptr %ptr) { 90 %t0 = add i8 %v0, 1 91 %t1 = add i8 %t0, %v1 92 ret i8 %t0 93 } 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 Rgn.add(T0); 106 Rgn.add(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 118 TEST_F(RegionTest, MetadataFromIR) { 119 parseIR(C, R"IR( 120 define i8 @foo(i8 %v0, i8 %v1) { 121 %t0 = add i8 %v0, 1, !sandboxvec !0 122 %t1 = add i8 %t0, %v1, !sandboxvec !1 123 %t2 = add i8 %t1, %v1, !sandboxvec !1 124 ret i8 %t2 125 } 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 145 TEST_F(RegionTest, NonContiguousRegion) { 146 parseIR(C, R"IR( 147 define i8 @foo(i8 %v0, i8 %v1) { 148 %t0 = add i8 %v0, 1, !sandboxvec !0 149 %t1 = add i8 %t0, %v1 150 %t2 = add i8 %t1, %v1, !sandboxvec !0 151 ret i8 %t2 152 } 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 170 TEST_F(RegionTest, DumpedMetadata) { 171 parseIR(C, R"IR( 172 define i8 @foo(i8 %v0, i8 %v1) { 173 %t0 = add i8 %v0, 1 174 %t1 = add i8 %t0, %v1 175 %t2 = add i8 %t1, %v1 176 ret i8 %t1 177 } 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 Rgn.add(T0); 190 sandboxir::Region Rgn2(Ctx, *TTI); 191 Rgn2.add(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 kind 199 // of IR comparison. 200 std::string expected = R"(; ModuleID = '<string>' 201 source_filename = "<string>" 202 203 define i8 @foo(i8 %v0, i8 %v1) { 204 %t0 = add i8 %v0, 1, !sandboxvec !0 205 %t1 = add i8 %t0, %v1 206 %t2 = add i8 %t1, %v1, !sandboxvec !1 207 ret i8 %t1 208 } 209 210 !0 = distinct !{!"sandboxregion"} 211 !1 = distinct !{!"sandboxregion"} 212 )"; 213 EXPECT_EQ(expected, output); 214 } 215 216 TEST_F(RegionTest, MetadataRoundTrip) { 217 parseIR(C, R"IR( 218 define i8 @foo(i8 %v0, i8 %v1) { 219 %t0 = add i8 %v0, 1 220 %t1 = add i8 %t0, %v1 221 ret i8 %t1 222 } 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 Rgn.add(T0); 234 Rgn.add(T1); 235 236 SmallVector<std::unique_ptr<sandboxir::Region>> Regions = 237 sandboxir::Region::createRegionsFromMD(*F, *TTI); 238 ASSERT_EQ(1U, Regions.size()); 239 #ifndef NDEBUG 240 EXPECT_EQ(Rgn, *Regions[0].get()); 241 #endif 242 } 243 244 TEST_F(RegionTest, RegionCost) { 245 parseIR(C, R"IR( 246 define void @foo(i8 %v0, i8 %v1, i8 %v2) { 247 %add0 = add i8 %v0, 1 248 %add1 = add i8 %v1, 2 249 %add2 = add i8 %v2, 3 250 ret void 251 } 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 Rgn.add(Add0); 280 EXPECT_EQ(SB.getBeforeCost(), 0); 281 EXPECT_EQ(SB.getAfterCost(), GetCost(LLVMAdd0)); 282 // Same for `Add1`. 283 Rgn.add(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 Rgn.remove(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 Rgn.remove(Add2); 292 EXPECT_EQ(SB.getBeforeCost(), GetCost(LLVMAdd2)); 293 EXPECT_EQ(SB.getAfterCost(), GetCost(LLVMAdd1)); 294 } 295