xref: /llvm-project/llvm/benchmarks/SandboxIRBench.cpp (revision e22b07e766e415d6a0ed4c73fe5286fcf25f8d98)
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