|  | //===- SandboxIRBench.cpp -------------------------------------------------===// | 
|  | // | 
|  | // 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 | 
|  | // | 
|  | //===----------------------------------------------------------------------===// | 
|  | // | 
|  | // These tests measure the performance of some core SandboxIR functions and | 
|  | // compare them against LLVM IR. | 
|  | // | 
|  | //===----------------------------------------------------------------------===// | 
|  |  | 
|  | #include "benchmark/benchmark.h" | 
|  | #include "llvm/AsmParser/Parser.h" | 
|  | #include "llvm/IR/BasicBlock.h" | 
|  | #include "llvm/IR/DataLayout.h" | 
|  | #include "llvm/IR/Function.h" | 
|  | #include "llvm/IR/Instruction.h" | 
|  | #include "llvm/IR/Module.h" | 
|  | #include "llvm/SandboxIR/Function.h" | 
|  | #include "llvm/SandboxIR/Instruction.h" | 
|  | #include "llvm/SandboxIR/Module.h" | 
|  | #include "llvm/Support/SourceMgr.h" | 
|  | #include <memory> | 
|  | #include <sstream> | 
|  |  | 
|  | using namespace llvm; | 
|  |  | 
|  | static std::unique_ptr<Module> parseIR(LLVMContext &C, const char *IR) { | 
|  | SMDiagnostic Err; | 
|  | std::unique_ptr<Module> M = parseAssemblyString(IR, Err, C); | 
|  | if (!M) | 
|  | Err.print("SandboxIRBench", errs()); | 
|  | return M; | 
|  | } | 
|  |  | 
|  | enum class IR { | 
|  | LLVM,           ///> LLVM IR | 
|  | SBoxNoTracking, ///> Sandbox IR with tracking disabled | 
|  | SBoxTracking,   ///> Sandbox IR with tracking enabled | 
|  | }; | 
|  | // Traits to get llvm::BasicBlock/sandboxir::BasicBlock from IR::LLVM/IR::SBox. | 
|  | template <IR IRTy> struct TypeSelect {}; | 
|  | template <> struct TypeSelect<IR::LLVM> { | 
|  | using BasicBlock = llvm::BasicBlock; | 
|  | }; | 
|  | template <> struct TypeSelect<IR::SBoxNoTracking> { | 
|  | using BasicBlock = sandboxir::BasicBlock; | 
|  | }; | 
|  | template <> struct TypeSelect<IR::SBoxTracking> { | 
|  | using BasicBlock = sandboxir::BasicBlock; | 
|  | }; | 
|  |  | 
|  | template <IR IRTy> | 
|  | static typename TypeSelect<IRTy>::BasicBlock * | 
|  | genIR(std::unique_ptr<llvm::Module> &LLVMM, LLVMContext &LLVMCtx, | 
|  | sandboxir::Context &Ctx, | 
|  | std::function<std::string(unsigned)> GenerateIRStr, | 
|  | unsigned NumInstrs = 0u) { | 
|  | std::string IRStr = GenerateIRStr(NumInstrs); | 
|  | LLVMM = parseIR(LLVMCtx, IRStr.c_str()); | 
|  | llvm::Function *LLVMF = &*LLVMM->getFunction("foo"); | 
|  | llvm::BasicBlock *LLVMBB = &*LLVMF->begin(); | 
|  |  | 
|  | sandboxir::Function *F = Ctx.createFunction(LLVMF); | 
|  | sandboxir::BasicBlock *BB = &*F->begin(); | 
|  | // Start tracking if we are testing with tracking enabled. | 
|  | if constexpr (IRTy == IR::SBoxTracking) | 
|  | Ctx.save(); | 
|  |  | 
|  | if constexpr (IRTy == IR::LLVM) | 
|  | return LLVMBB; | 
|  | else | 
|  | return BB; | 
|  | } | 
|  |  | 
|  | template <IR IRTy> static void finalize(sandboxir::Context &Ctx) { | 
|  | // Accept changes if we are tracking. | 
|  | if constexpr (IRTy == IR::SBoxTracking) | 
|  | Ctx.accept(); | 
|  | } | 
|  |  | 
|  | static std::string generateBBWalkIR(unsigned Size) { | 
|  | std::stringstream SS; | 
|  | SS << "define void @foo(i32 %v1, i32 %v2) {\n"; | 
|  | for (auto Cnt : seq<unsigned>(0, Size)) | 
|  | SS << "  %add" << Cnt << " = add i32 %v1, %v2\n"; | 
|  | SS << "ret void"; | 
|  | SS << "}"; | 
|  | return SS.str(); | 
|  | } | 
|  |  | 
|  | template <IR IRTy> static void SBoxIRCreation(benchmark::State &State) { | 
|  | static_assert(IRTy != IR::LLVM, "Expected SBoxTracking or SBoxNoTracking"); | 
|  | LLVMContext LLVMCtx; | 
|  | unsigned NumInstrs = State.range(0); | 
|  | std::unique_ptr<llvm::Module> LLVMM; | 
|  | std::string IRStr = generateBBWalkIR(NumInstrs); | 
|  | LLVMM = parseIR(LLVMCtx, IRStr.c_str()); | 
|  | llvm::Function *LLVMF = &*LLVMM->getFunction("foo"); | 
|  |  | 
|  | for (auto _ : State) { | 
|  | State.PauseTiming(); | 
|  | sandboxir::Context Ctx(LLVMCtx); | 
|  | if constexpr (IRTy == IR::SBoxTracking) | 
|  | Ctx.save(); | 
|  | State.ResumeTiming(); | 
|  |  | 
|  | sandboxir::Function *F = Ctx.createFunction(LLVMF); | 
|  | benchmark::DoNotOptimize(F); | 
|  | State.PauseTiming(); | 
|  | if constexpr (IRTy == IR::SBoxTracking) | 
|  | Ctx.accept(); | 
|  | State.ResumeTiming(); | 
|  | } | 
|  | } | 
|  |  | 
|  | template <IR IRTy> static void BBWalk(benchmark::State &State) { | 
|  | LLVMContext LLVMCtx; | 
|  | sandboxir::Context Ctx(LLVMCtx); | 
|  | unsigned NumInstrs = State.range(0); | 
|  | std::unique_ptr<llvm::Module> LLVMM; | 
|  | auto *BB = genIR<IRTy>(LLVMM, LLVMCtx, Ctx, generateBBWalkIR, NumInstrs); | 
|  | for (auto _ : State) { | 
|  | // Walk LLVM Instructions. | 
|  | for (auto &I : *BB) | 
|  | benchmark::DoNotOptimize(I); | 
|  | } | 
|  | } | 
|  |  | 
|  | static std::string generateGetTypeIR(unsigned Size) { | 
|  | return R"IR( | 
|  | define void @foo(i32 %v1, i32 %v2) { | 
|  | %add = add i32 %v1, %v2 | 
|  | ret void | 
|  | } | 
|  | )IR"; | 
|  | } | 
|  |  | 
|  | template <IR IRTy> static void GetType(benchmark::State &State) { | 
|  | LLVMContext LLVMCtx; | 
|  | sandboxir::Context Ctx(LLVMCtx); | 
|  | std::unique_ptr<llvm::Module> LLVMM; | 
|  | auto *BB = genIR<IRTy>(LLVMM, LLVMCtx, Ctx, generateGetTypeIR); | 
|  | auto *I = &*BB->begin(); | 
|  | for (auto _ : State) | 
|  | benchmark::DoNotOptimize(I->getType()); | 
|  | } | 
|  |  | 
|  | static std::string generateRAUWIR(unsigned Size) { | 
|  | std::stringstream SS; | 
|  | SS << "define void @foo(i32 %v1, i32 %v2) {\n"; | 
|  | SS << "  %def1 = add i32 %v1, %v2\n"; | 
|  | SS << "  %def2 = add i32 %v1, %v2\n"; | 
|  | for (auto Cnt : seq<unsigned>(0, Size)) | 
|  | SS << "  %add" << Cnt << " = add i32 %def1, %def1\n"; | 
|  | SS << "ret void"; | 
|  | SS << "}"; | 
|  | return SS.str(); | 
|  | } | 
|  |  | 
|  | template <IR IRTy> static void RAUW(benchmark::State &State) { | 
|  | LLVMContext LLVMCtx; | 
|  | sandboxir::Context Ctx(LLVMCtx); | 
|  | std::unique_ptr<llvm::Module> LLVMM; | 
|  | unsigned NumInstrs = State.range(0); | 
|  | auto *BB = genIR<IRTy>(LLVMM, LLVMCtx, Ctx, generateRAUWIR, NumInstrs); | 
|  | auto It = BB->begin(); | 
|  | auto *Def1 = &*It++; | 
|  | auto *Def2 = &*It++; | 
|  | for (auto _ : State) { | 
|  | Def1->replaceAllUsesWith(Def2); | 
|  | Def2->replaceAllUsesWith(Def1); | 
|  | } | 
|  | finalize<IRTy>(Ctx); | 
|  | } | 
|  |  | 
|  | static std::string generateRUOWIR(unsigned NumOperands) { | 
|  | std::stringstream SS; | 
|  | auto GenOps = [&SS, NumOperands]() { | 
|  | for (auto Cnt : seq<unsigned>(0, NumOperands)) { | 
|  | SS << "i8 %arg" << Cnt; | 
|  | bool IsLast = Cnt + 1 == NumOperands; | 
|  | if (!IsLast) | 
|  | SS << ", "; | 
|  | } | 
|  | }; | 
|  |  | 
|  | SS << "define void @foo("; | 
|  | GenOps(); | 
|  | SS << ") {\n"; | 
|  |  | 
|  | SS << "   call void @foo("; | 
|  | GenOps(); | 
|  | SS << ")\n"; | 
|  | SS << "ret void"; | 
|  | SS << "}"; | 
|  | return SS.str(); | 
|  | } | 
|  |  | 
|  | template <IR IRTy> static void RUOW(benchmark::State &State) { | 
|  | LLVMContext LLVMCtx; | 
|  | sandboxir::Context Ctx(LLVMCtx); | 
|  | std::unique_ptr<llvm::Module> LLVMM; | 
|  | unsigned NumOperands = State.range(0); | 
|  | auto *BB = genIR<IRTy>(LLVMM, LLVMCtx, Ctx, generateRUOWIR, NumOperands); | 
|  |  | 
|  | auto It = BB->begin(); | 
|  | auto *F = BB->getParent(); | 
|  | auto *Arg0 = F->getArg(0); | 
|  | auto *Arg1 = F->getArg(1); | 
|  | auto *Call = &*It++; | 
|  | for (auto _ : State) | 
|  | Call->replaceUsesOfWith(Arg0, Arg1); | 
|  | finalize<IRTy>(Ctx); | 
|  | } | 
|  |  | 
|  | // Measure the time it takes to create Sandbox IR without/with tracking. | 
|  | BENCHMARK(SBoxIRCreation<IR::SBoxNoTracking>) | 
|  | ->Args({10}) | 
|  | ->Args({100}) | 
|  | ->Args({1000}); | 
|  | BENCHMARK(SBoxIRCreation<IR::SBoxTracking>) | 
|  | ->Args({10}) | 
|  | ->Args({100}) | 
|  | ->Args({1000}); | 
|  |  | 
|  | BENCHMARK(GetType<IR::LLVM>); | 
|  | BENCHMARK(GetType<IR::SBoxNoTracking>); | 
|  |  | 
|  | BENCHMARK(BBWalk<IR::LLVM>)->Args({1024}); | 
|  | BENCHMARK(BBWalk<IR::SBoxTracking>)->Args({1024}); | 
|  |  | 
|  | BENCHMARK(RAUW<IR::LLVM>)->Args({512}); | 
|  | BENCHMARK(RAUW<IR::SBoxNoTracking>)->Args({512}); | 
|  | BENCHMARK(RAUW<IR::SBoxTracking>)->Args({512}); | 
|  |  | 
|  | BENCHMARK(RUOW<IR::LLVM>)->Args({4096}); | 
|  | BENCHMARK(RUOW<IR::SBoxNoTracking>)->Args({4096}); | 
|  | BENCHMARK(RUOW<IR::SBoxTracking>)->Args({4096}); | 
|  |  | 
|  | BENCHMARK_MAIN(); |