| //===- 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(); |