1 //===- SandboxIRBench.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 // These tests measure the performance of some core SandboxIR functions and 10 // compare them against LLVM IR. 11 // 12 //===----------------------------------------------------------------------===// 13 14 #include "benchmark/benchmark.h" 15 #include "llvm/AsmParser/Parser.h" 16 #include "llvm/IR/BasicBlock.h" 17 #include "llvm/IR/DataLayout.h" 18 #include "llvm/IR/Function.h" 19 #include "llvm/IR/Instruction.h" 20 #include "llvm/IR/Module.h" 21 #include "llvm/SandboxIR/Function.h" 22 #include "llvm/SandboxIR/Instruction.h" 23 #include "llvm/SandboxIR/Module.h" 24 #include "llvm/Support/SourceMgr.h" 25 #include <memory> 26 #include <sstream> 27 28 using namespace llvm; 29 30 static std::unique_ptr<Module> parseIR(LLVMContext &C, const char *IR) { 31 SMDiagnostic Err; 32 std::unique_ptr<Module> M = parseAssemblyString(IR, Err, C); 33 if (!M) 34 Err.print("SandboxIRBench", errs()); 35 return M; 36 } 37 38 enum class IR { 39 LLVM, ///> LLVM IR 40 SBoxNoTracking, ///> Sandbox IR with tracking disabled 41 SBoxTracking, ///> Sandbox IR with tracking enabled 42 }; 43 // Traits to get llvm::BasicBlock/sandboxir::BasicBlock from IR::LLVM/IR::SBox. 44 template <IR IRTy> struct TypeSelect {}; 45 template <> struct TypeSelect<IR::LLVM> { 46 using BasicBlock = llvm::BasicBlock; 47 }; 48 template <> struct TypeSelect<IR::SBoxNoTracking> { 49 using BasicBlock = sandboxir::BasicBlock; 50 }; 51 template <> struct TypeSelect<IR::SBoxTracking> { 52 using BasicBlock = sandboxir::BasicBlock; 53 }; 54 55 template <IR IRTy> 56 static typename TypeSelect<IRTy>::BasicBlock * 57 genIR(std::unique_ptr<llvm::Module> &LLVMM, LLVMContext &LLVMCtx, 58 sandboxir::Context &Ctx, 59 std::function<std::string(unsigned)> GenerateIRStr, 60 unsigned NumInstrs = 0u) { 61 std::string IRStr = GenerateIRStr(NumInstrs); 62 LLVMM = parseIR(LLVMCtx, IRStr.c_str()); 63 llvm::Function *LLVMF = &*LLVMM->getFunction("foo"); 64 llvm::BasicBlock *LLVMBB = &*LLVMF->begin(); 65 66 sandboxir::Function *F = Ctx.createFunction(LLVMF); 67 sandboxir::BasicBlock *BB = &*F->begin(); 68 // Start tracking if we are testing with tracking enabled. 69 if constexpr (IRTy == IR::SBoxTracking) 70 Ctx.save(); 71 72 if constexpr (IRTy == IR::LLVM) 73 return LLVMBB; 74 else 75 return BB; 76 } 77 78 template <IR IRTy> static void finalize(sandboxir::Context &Ctx) { 79 // Accept changes if we are tracking. 80 if constexpr (IRTy == IR::SBoxTracking) 81 Ctx.accept(); 82 } 83 84 static std::string generateBBWalkIR(unsigned Size) { 85 std::stringstream SS; 86 SS << "define void @foo(i32 %v1, i32 %v2) {\n"; 87 for (auto Cnt : seq<unsigned>(0, Size)) 88 SS << " %add" << Cnt << " = add i32 %v1, %v2\n"; 89 SS << "ret void"; 90 SS << "}"; 91 return SS.str(); 92 } 93 94 template <IR IRTy> static void SBoxIRCreation(benchmark::State &State) { 95 static_assert(IRTy != IR::LLVM, "Expected SBoxTracking or SBoxNoTracking"); 96 LLVMContext LLVMCtx; 97 unsigned NumInstrs = State.range(0); 98 std::unique_ptr<llvm::Module> LLVMM; 99 std::string IRStr = generateBBWalkIR(NumInstrs); 100 LLVMM = parseIR(LLVMCtx, IRStr.c_str()); 101 llvm::Function *LLVMF = &*LLVMM->getFunction("foo"); 102 103 for (auto _ : State) { 104 State.PauseTiming(); 105 sandboxir::Context Ctx(LLVMCtx); 106 if constexpr (IRTy == IR::SBoxTracking) 107 Ctx.save(); 108 State.ResumeTiming(); 109 110 sandboxir::Function *F = Ctx.createFunction(LLVMF); 111 benchmark::DoNotOptimize(F); 112 State.PauseTiming(); 113 if constexpr (IRTy == IR::SBoxTracking) 114 Ctx.accept(); 115 State.ResumeTiming(); 116 } 117 } 118 119 template <IR IRTy> static void BBWalk(benchmark::State &State) { 120 LLVMContext LLVMCtx; 121 sandboxir::Context Ctx(LLVMCtx); 122 unsigned NumInstrs = State.range(0); 123 std::unique_ptr<llvm::Module> LLVMM; 124 auto *BB = genIR<IRTy>(LLVMM, LLVMCtx, Ctx, generateBBWalkIR, NumInstrs); 125 for (auto _ : State) { 126 // Walk LLVM Instructions. 127 for (auto &I : *BB) 128 benchmark::DoNotOptimize(I); 129 } 130 } 131 132 static std::string generateGetTypeIR(unsigned Size) { 133 return R"IR( 134 define void @foo(i32 %v1, i32 %v2) { 135 %add = add i32 %v1, %v2 136 ret void 137 } 138 )IR"; 139 } 140 141 template <IR IRTy> static void GetType(benchmark::State &State) { 142 LLVMContext LLVMCtx; 143 sandboxir::Context Ctx(LLVMCtx); 144 std::unique_ptr<llvm::Module> LLVMM; 145 auto *BB = genIR<IRTy>(LLVMM, LLVMCtx, Ctx, generateGetTypeIR); 146 auto *I = &*BB->begin(); 147 for (auto _ : State) 148 benchmark::DoNotOptimize(I->getType()); 149 } 150 151 static std::string generateRAUWIR(unsigned Size) { 152 std::stringstream SS; 153 SS << "define void @foo(i32 %v1, i32 %v2) {\n"; 154 SS << " %def1 = add i32 %v1, %v2\n"; 155 SS << " %def2 = add i32 %v1, %v2\n"; 156 for (auto Cnt : seq<unsigned>(0, Size)) 157 SS << " %add" << Cnt << " = add i32 %def1, %def1\n"; 158 SS << "ret void"; 159 SS << "}"; 160 return SS.str(); 161 } 162 163 template <IR IRTy> static void RAUW(benchmark::State &State) { 164 LLVMContext LLVMCtx; 165 sandboxir::Context Ctx(LLVMCtx); 166 std::unique_ptr<llvm::Module> LLVMM; 167 unsigned NumInstrs = State.range(0); 168 auto *BB = genIR<IRTy>(LLVMM, LLVMCtx, Ctx, generateRAUWIR, NumInstrs); 169 auto It = BB->begin(); 170 auto *Def1 = &*It++; 171 auto *Def2 = &*It++; 172 for (auto _ : State) { 173 Def1->replaceAllUsesWith(Def2); 174 Def2->replaceAllUsesWith(Def1); 175 } 176 finalize<IRTy>(Ctx); 177 } 178 179 static std::string generateRUOWIR(unsigned NumOperands) { 180 std::stringstream SS; 181 auto GenOps = [&SS, NumOperands]() { 182 for (auto Cnt : seq<unsigned>(0, NumOperands)) { 183 SS << "i8 %arg" << Cnt; 184 bool IsLast = Cnt + 1 == NumOperands; 185 if (!IsLast) 186 SS << ", "; 187 } 188 }; 189 190 SS << "define void @foo("; 191 GenOps(); 192 SS << ") {\n"; 193 194 SS << " call void @foo("; 195 GenOps(); 196 SS << ")\n"; 197 SS << "ret void"; 198 SS << "}"; 199 return SS.str(); 200 } 201 202 template <IR IRTy> static void RUOW(benchmark::State &State) { 203 LLVMContext LLVMCtx; 204 sandboxir::Context Ctx(LLVMCtx); 205 std::unique_ptr<llvm::Module> LLVMM; 206 unsigned NumOperands = State.range(0); 207 auto *BB = genIR<IRTy>(LLVMM, LLVMCtx, Ctx, generateRUOWIR, NumOperands); 208 209 auto It = BB->begin(); 210 auto *F = BB->getParent(); 211 auto *Arg0 = F->getArg(0); 212 auto *Arg1 = F->getArg(1); 213 auto *Call = &*It++; 214 for (auto _ : State) 215 Call->replaceUsesOfWith(Arg0, Arg1); 216 finalize<IRTy>(Ctx); 217 } 218 219 // Measure the time it takes to create Sandbox IR without/with tracking. 220 BENCHMARK(SBoxIRCreation<IR::SBoxNoTracking>) 221 ->Args({10}) 222 ->Args({100}) 223 ->Args({1000}); 224 BENCHMARK(SBoxIRCreation<IR::SBoxTracking>) 225 ->Args({10}) 226 ->Args({100}) 227 ->Args({1000}); 228 229 BENCHMARK(GetType<IR::LLVM>); 230 BENCHMARK(GetType<IR::SBoxNoTracking>); 231 232 BENCHMARK(BBWalk<IR::LLVM>)->Args({1024}); 233 BENCHMARK(BBWalk<IR::SBoxTracking>)->Args({1024}); 234 235 BENCHMARK(RAUW<IR::LLVM>)->Args({512}); 236 BENCHMARK(RAUW<IR::SBoxNoTracking>)->Args({512}); 237 BENCHMARK(RAUW<IR::SBoxTracking>)->Args({512}); 238 239 BENCHMARK(RUOW<IR::LLVM>)->Args({4096}); 240 BENCHMARK(RUOW<IR::SBoxNoTracking>)->Args({4096}); 241 BENCHMARK(RUOW<IR::SBoxTracking>)->Args({4096}); 242 243 BENCHMARK_MAIN(); 244