//===- 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 #include using namespace llvm; static std::unique_ptr parseIR(LLVMContext &C, const char *IR) { SMDiagnostic Err; std::unique_ptr 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 struct TypeSelect {}; template <> struct TypeSelect { using BasicBlock = llvm::BasicBlock; }; template <> struct TypeSelect { using BasicBlock = sandboxir::BasicBlock; }; template <> struct TypeSelect { using BasicBlock = sandboxir::BasicBlock; }; template static typename TypeSelect::BasicBlock * genIR(std::unique_ptr &LLVMM, LLVMContext &LLVMCtx, sandboxir::Context &Ctx, std::function 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 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(0, Size)) SS << " %add" << Cnt << " = add i32 %v1, %v2\n"; SS << "ret void"; SS << "}"; return SS.str(); } template 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 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 static void BBWalk(benchmark::State &State) { LLVMContext LLVMCtx; sandboxir::Context Ctx(LLVMCtx); unsigned NumInstrs = State.range(0); std::unique_ptr LLVMM; auto *BB = genIR(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 static void GetType(benchmark::State &State) { LLVMContext LLVMCtx; sandboxir::Context Ctx(LLVMCtx); std::unique_ptr LLVMM; auto *BB = genIR(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(0, Size)) SS << " %add" << Cnt << " = add i32 %def1, %def1\n"; SS << "ret void"; SS << "}"; return SS.str(); } template static void RAUW(benchmark::State &State) { LLVMContext LLVMCtx; sandboxir::Context Ctx(LLVMCtx); std::unique_ptr LLVMM; unsigned NumInstrs = State.range(0); auto *BB = genIR(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(Ctx); } static std::string generateRUOWIR(unsigned NumOperands) { std::stringstream SS; auto GenOps = [&SS, NumOperands]() { for (auto Cnt : seq(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 static void RUOW(benchmark::State &State) { LLVMContext LLVMCtx; sandboxir::Context Ctx(LLVMCtx); std::unique_ptr LLVMM; unsigned NumOperands = State.range(0); auto *BB = genIR(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(Ctx); } // Measure the time it takes to create Sandbox IR without/with tracking. BENCHMARK(SBoxIRCreation) ->Args({10}) ->Args({100}) ->Args({1000}); BENCHMARK(SBoxIRCreation) ->Args({10}) ->Args({100}) ->Args({1000}); BENCHMARK(GetType); BENCHMARK(GetType); BENCHMARK(BBWalk)->Args({1024}); BENCHMARK(BBWalk)->Args({1024}); BENCHMARK(RAUW)->Args({512}); BENCHMARK(RAUW)->Args({512}); BENCHMARK(RAUW)->Args({512}); BENCHMARK(RUOW)->Args({4096}); BENCHMARK(RUOW)->Args({4096}); BENCHMARK(RUOW)->Args({4096}); BENCHMARK_MAIN();