244 lines
7.0 KiB
C++
244 lines
7.0 KiB
C++
//===- 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();
|