352 lines · cpp
1//===- PassTest.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/Pass.h"10#include "llvm/Analysis/TargetTransformInfo.h"11#include "llvm/AsmParser/Parser.h"12#include "llvm/IR/Module.h"13#include "llvm/SandboxIR/Constant.h"14#include "llvm/SandboxIR/Context.h"15#include "llvm/SandboxIR/Function.h"16#include "llvm/SandboxIR/PassManager.h"17#include "llvm/SandboxIR/Region.h"18#include "llvm/Support/SourceMgr.h"19#include "gtest/gtest.h"20 21using namespace llvm::sandboxir;22 23struct PassTest : public testing::Test {24 llvm::LLVMContext LLVMCtx;25 std::unique_ptr<llvm::Module> LLVMM;26 std::unique_ptr<Context> Ctx;27 std::unique_ptr<llvm::TargetTransformInfo> TTI;28 29 Function *parseFunction(const char *IR, const char *FuncName) {30 llvm::SMDiagnostic Err;31 LLVMM = parseAssemblyString(IR, Err, LLVMCtx);32 TTI = std::make_unique<llvm::TargetTransformInfo>(LLVMM->getDataLayout());33 34 if (!LLVMM)35 Err.print("PassTest", llvm::errs());36 Ctx = std::make_unique<Context>(LLVMCtx);37 return Ctx->createFunction(LLVMM->getFunction(FuncName));38 }39};40 41TEST_F(PassTest, FunctionPass) {42 auto *F = parseFunction(R"IR(43define void @foo() {44 ret void45}46)IR",47 "foo");48 class TestPass final : public FunctionPass {49 unsigned &BBCnt;50 51 public:52 TestPass(unsigned &BBCnt) : FunctionPass("test-pass"), BBCnt(BBCnt) {}53 bool runOnFunction(Function &F, const Analyses &A) final {54 for ([[maybe_unused]] auto &BB : F)55 ++BBCnt;56 return false;57 }58 };59 unsigned BBCnt = 0;60 TestPass TPass(BBCnt);61 // Check getName(),62 EXPECT_EQ(TPass.getName(), "test-pass");63 // Check classof().64 EXPECT_TRUE(llvm::isa<FunctionPass>(TPass));65 // Check runOnFunction();66 TPass.runOnFunction(*F, Analyses::emptyForTesting());67 EXPECT_EQ(BBCnt, 1u);68#ifndef NDEBUG69 {70 // Check print().71 std::string Buff;72 llvm::raw_string_ostream SS(Buff);73 TPass.print(SS);74 EXPECT_EQ(Buff, "test-pass");75 }76 {77 // Check operator<<().78 std::string Buff;79 llvm::raw_string_ostream SS(Buff);80 SS << TPass;81 EXPECT_EQ(Buff, "test-pass");82 }83 // Check pass name assertions.84 class TestNamePass final : public FunctionPass {85 public:86 TestNamePass(llvm::StringRef Name) : FunctionPass(Name) {}87 bool runOnFunction(Function &F, const Analyses &A) override {88 return false;89 }90 };91 EXPECT_DEATH(TestNamePass("white space"), ".*whitespace.*");92 EXPECT_DEATH(TestNamePass("-dash"), ".*start with.*");93#endif94}95 96TEST_F(PassTest, RegionPass) {97 auto *F = parseFunction(R"IR(98define i8 @foo(i8 %v0, i8 %v1) {99 %t0 = add i8 %v0, 1100 %t1 = add i8 %t0, %v1, !sandboxvec !0101 %t2 = add i8 %t1, %v1, !sandboxvec !0102 ret i8 %t1103}104 105!0 = distinct !{!"sandboxregion"}106)IR",107 "foo");108 109 class TestPass final : public RegionPass {110 unsigned &InstCount;111 112 public:113 TestPass(unsigned &InstCount)114 : RegionPass("test-pass"), InstCount(InstCount) {}115 bool runOnRegion(Region &R, const Analyses &A) final {116 for ([[maybe_unused]] auto &Inst : R) {117 ++InstCount;118 }119 return false;120 }121 };122 unsigned InstCount = 0;123 TestPass TPass(InstCount);124 // Check getName(),125 EXPECT_EQ(TPass.getName(), "test-pass");126 // Check runOnRegion();127 llvm::SmallVector<std::unique_ptr<Region>> Regions =128 Region::createRegionsFromMD(*F, *TTI);129 ASSERT_EQ(Regions.size(), 1u);130 TPass.runOnRegion(*Regions[0], Analyses::emptyForTesting());131 EXPECT_EQ(InstCount, 2u);132#ifndef NDEBUG133 {134 // Check print().135 std::string Buff;136 llvm::raw_string_ostream SS(Buff);137 TPass.print(SS);138 EXPECT_EQ(Buff, "test-pass");139 }140 {141 // Check operator<<().142 std::string Buff;143 llvm::raw_string_ostream SS(Buff);144 SS << TPass;145 EXPECT_EQ(Buff, "test-pass");146 }147 // Check pass name assertions.148 class TestNamePass final : public RegionPass {149 public:150 TestNamePass(llvm::StringRef Name) : RegionPass(Name) {}151 bool runOnRegion(Region &F, const Analyses &A) override { return false; }152 };153 EXPECT_DEATH(TestNamePass("white space"), ".*whitespace.*");154 EXPECT_DEATH(TestNamePass("-dash"), ".*start with.*");155#endif156}157 158TEST_F(PassTest, FunctionPassManager) {159 auto *F = parseFunction(R"IR(160define void @foo() {161 ret void162}163)IR",164 "foo");165 class TestPass1 final : public FunctionPass {166 unsigned &BBCnt;167 168 public:169 TestPass1(unsigned &BBCnt) : FunctionPass("test-pass1"), BBCnt(BBCnt) {}170 bool runOnFunction(Function &F, const Analyses &A) final {171 for ([[maybe_unused]] auto &BB : F)172 ++BBCnt;173 return false;174 }175 };176 class TestPass2 final : public FunctionPass {177 unsigned &BBCnt;178 179 public:180 TestPass2(unsigned &BBCnt) : FunctionPass("test-pass2"), BBCnt(BBCnt) {}181 bool runOnFunction(Function &F, const Analyses &A) final {182 for ([[maybe_unused]] auto &BB : F)183 ++BBCnt;184 return false;185 }186 };187 unsigned BBCnt1 = 0;188 unsigned BBCnt2 = 0;189 190 FunctionPassManager FPM("test-fpm");191 FPM.addPass(std::make_unique<TestPass1>(BBCnt1));192 FPM.addPass(std::make_unique<TestPass2>(BBCnt2));193 // Check runOnFunction().194 FPM.runOnFunction(*F, Analyses::emptyForTesting());195 EXPECT_EQ(BBCnt1, 1u);196 EXPECT_EQ(BBCnt2, 1u);197#ifndef NDEBUG198 // Check dump().199 std::string Buff;200 llvm::raw_string_ostream SS(Buff);201 FPM.print(SS);202 EXPECT_EQ(Buff, "test-fpm<test-pass1,test-pass2>");203#endif // NDEBUG204}205 206TEST_F(PassTest, RegionPassManager) {207 auto *F = parseFunction(R"IR(208define i8 @foo(i8 %v0, i8 %v1) {209 %t0 = add i8 %v0, 1210 %t1 = add i8 %t0, %v1, !sandboxvec !0211 %t2 = add i8 %t1, %v1, !sandboxvec !0212 ret i8 %t1213}214 215!0 = distinct !{!"sandboxregion"}216)IR",217 "foo");218 219 class TestPass1 final : public RegionPass {220 unsigned &InstCount;221 222 public:223 TestPass1(unsigned &InstCount)224 : RegionPass("test-pass1"), InstCount(InstCount) {}225 bool runOnRegion(Region &R, const Analyses &A) final {226 for ([[maybe_unused]] auto &Inst : R)227 ++InstCount;228 return false;229 }230 };231 class TestPass2 final : public RegionPass {232 unsigned &InstCount;233 234 public:235 TestPass2(unsigned &InstCount)236 : RegionPass("test-pass2"), InstCount(InstCount) {}237 bool runOnRegion(Region &R, const Analyses &A) final {238 for ([[maybe_unused]] auto &Inst : R)239 ++InstCount;240 return false;241 }242 };243 unsigned InstCount1 = 0;244 unsigned InstCount2 = 0;245 246 RegionPassManager RPM("test-rpm");247 RPM.addPass(std::make_unique<TestPass1>(InstCount1));248 RPM.addPass(std::make_unique<TestPass2>(InstCount2));249 // Check runOnRegion().250 llvm::SmallVector<std::unique_ptr<Region>> Regions =251 Region::createRegionsFromMD(*F, *TTI);252 ASSERT_EQ(Regions.size(), 1u);253 RPM.runOnRegion(*Regions[0], Analyses::emptyForTesting());254 EXPECT_EQ(InstCount1, 2u);255 EXPECT_EQ(InstCount2, 2u);256#ifndef NDEBUG257 // Check dump().258 std::string Buff;259 llvm::raw_string_ostream SS(Buff);260 RPM.print(SS);261 EXPECT_EQ(Buff, "test-rpm<test-pass1,test-pass2>");262#endif // NDEBUG263}264 265TEST_F(PassTest, SetPassPipeline) {266 auto *F = parseFunction(R"IR(267define void @f() {268 ret void269}270)IR",271 "f");272 class FooPass final : public FunctionPass {273 std::string &Str;274 std::string Args;275 276 public:277 FooPass(std::string &Str, llvm::StringRef Args)278 : FunctionPass("foo-pass"), Str(Str), Args(Args.str()) {}279 bool runOnFunction(Function &F, const Analyses &A) final {280 Str += "foo<" + Args + ">";281 return false;282 }283 };284 class BarPass final : public FunctionPass {285 std::string &Str;286 std::string Args;287 288 public:289 BarPass(std::string &Str, llvm::StringRef Args)290 : FunctionPass("bar-pass"), Str(Str), Args(Args.str()) {}291 bool runOnFunction(Function &F, const Analyses &A) final {292 Str += "bar<" + Args + ">";293 return false;294 }295 };296 297 std::string Str;298 auto CreatePass =299 [&Str](llvm::StringRef Name,300 llvm::StringRef Args) -> std::unique_ptr<FunctionPass> {301 if (Name == "foo")302 return std::make_unique<FooPass>(Str, Args);303 if (Name == "bar")304 return std::make_unique<BarPass>(Str, Args);305 return nullptr;306 };307 308 FunctionPassManager FPM("test-fpm");309 FPM.setPassPipeline("foo<abc>,bar<nested1<nested2<nested3>>>,foo",310 CreatePass);311 FPM.runOnFunction(*F, Analyses::emptyForTesting());312 EXPECT_EQ(Str, "foo<abc>bar<nested1<nested2<nested3>>>foo<>");313 314 // A second call to setPassPipeline will trigger an assertion in debug mode.315#ifndef NDEBUG316 EXPECT_DEATH(FPM.setPassPipeline("bar,bar,foo", CreatePass),317 "setPassPipeline called on a non-empty sandboxir::PassManager");318#endif319 320 // Fresh PM for the death tests so they die from bad pipeline strings, rather321 // than from multiple setPassPipeline calls.322 FunctionPassManager FPM2("test-fpm");323 // Bad/empty pass names.324 EXPECT_DEATH(FPM2.setPassPipeline("bad-pass-name", CreatePass),325 ".*not registered.*");326 EXPECT_DEATH(FPM2.setPassPipeline(",", CreatePass), ".*empty pass name.*");327 EXPECT_DEATH(FPM2.setPassPipeline("<>", CreatePass), ".*empty pass name.*");328 EXPECT_DEATH(FPM2.setPassPipeline("<>foo", CreatePass),329 ".*empty pass name.*");330 EXPECT_DEATH(FPM2.setPassPipeline("foo,<>", CreatePass),331 ".*empty pass name.*");332 333 // Mismatched argument brackets.334 EXPECT_DEATH(FPM2.setPassPipeline("foo<", CreatePass), ".*Missing '>'.*");335 EXPECT_DEATH(FPM2.setPassPipeline("foo<bar", CreatePass), ".*Missing '>'.*");336 EXPECT_DEATH(FPM2.setPassPipeline("foo<bar<>", CreatePass),337 ".*Missing '>'.*");338 EXPECT_DEATH(FPM2.setPassPipeline("foo>", CreatePass), ".*Unexpected '>'.*");339 EXPECT_DEATH(FPM2.setPassPipeline(">foo", CreatePass), ".*Unexpected '>'.*");340 // Extra garbage between args and next delimiter/end-of-string.341 EXPECT_DEATH(FPM2.setPassPipeline("foo<bar<>>>", CreatePass),342 ".*Expected delimiter.*");343 EXPECT_DEATH(FPM2.setPassPipeline("bar<>foo", CreatePass),344 ".*Expected delimiter.*");345 EXPECT_DEATH(FPM2.setPassPipeline("bar<>foo,baz", CreatePass),346 ".*Expected delimiter.*");347 EXPECT_DEATH(FPM2.setPassPipeline("foo<args><more-args>", CreatePass),348 ".*Expected delimiter.*");349 EXPECT_DEATH(FPM2.setPassPipeline("foo<args>bar", CreatePass),350 ".*Expected delimiter.*");351}352