Skip to content

Commit

Permalink
Refactor
Browse files Browse the repository at this point in the history
  • Loading branch information
chfast committed Jan 4, 2021
1 parent 827e9e4 commit 440c0dc
Showing 1 changed file with 76 additions and 106 deletions.
182 changes: 76 additions & 106 deletions test/bench/synthetic_benchmarks.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,126 +7,99 @@
#include "test/utils/bytecode.hpp"
#include <evmc/instructions.h>
#include <evmone/instruction_traits.hpp>
#include <iterator>

using namespace benchmark;

namespace evmone::test
{
namespace
{
/// Stack limit inside the EVM benchmarking loop (one stack item is used for the loop count).
constexpr auto stack_limit = 1023;

enum class Mode
{
min_stack,
full_stack,
min_stack = 0, ///< The code uses as minimal stack as possible.
full_stack = 1, ///< The code fill the stuck up to its limit.
};

const bytes loop_prefix = push(255) + OP_JUMPDEST;
const bytes loop_suffix = push("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff") +
OP_ADD + OP_DUP1 + push(2) + OP_JUMPI;

constexpr bool is_unary_op(evmc_opcode opcode) noexcept
enum class InstructionKind : char
{
const auto trait = instr::traits[opcode];
return trait.stack_height_required == 1 && trait.stack_height_change == 0;
}

constexpr bool is_binary_op(evmc_opcode opcode) noexcept
{
const auto trait = instr::traits[opcode];
return trait.stack_height_required == 2 && trait.stack_height_change == -1;
}
unop = 'u',
binop = 'b',
push = 'p',
producer = 'a',
unknown = 'X',
};

constexpr bool is_producer(evmc_opcode opcode) noexcept
constexpr InstructionKind get_instruction_kind(evmc_opcode opcode) noexcept
{
const auto trait = instr::traits[opcode];
return trait.stack_height_required == 0 && trait.stack_height_change == 1;
if (opcode >= OP_PUSH1 && opcode <= OP_PUSH32)
return InstructionKind::push;
else if (trait.stack_height_required == 1 && trait.stack_height_change == 0)
return InstructionKind::unop;
else if (trait.stack_height_required == 2 && trait.stack_height_change == -1)
return InstructionKind::binop;
else if (trait.stack_height_required == 0 && trait.stack_height_change == 1)
return InstructionKind::producer;
else
return InstructionKind::unknown;
}

bytes_view generate_code(evmc_opcode opcode, Mode mode) noexcept
/// Generates the EVM benchmarking loop inner code for the given opcode and "mode".
bytecode generate_loop_inner_code(evmc_opcode opcode, Mode mode)
{
static bytes cache[256][2]{};

auto& code = cache[opcode][static_cast<int>(mode)];
if (!code.empty())
return code;

bytes inner_code;
const auto kind = get_instruction_kind(opcode);
switch (mode)
{
case Mode::min_stack:
{
if (opcode >= OP_PUSH1 && opcode <= OP_PUSH32)
{
bytes instr_pair;
instr_pair.push_back(uint8_t(opcode));
std::fill_n(std::back_inserter(instr_pair), opcode - OP_PUSH1 + 1, uint8_t{0});
instr_pair.push_back(OP_POP);
for (int i = 0; i < stack_limit; ++i)
inner_code += instr_pair;
}
else if (is_producer(opcode))
{
bytes instr_pair;
instr_pair.push_back(uint8_t(opcode));
instr_pair.push_back(OP_POP);
for (int i = 0; i < stack_limit; ++i)
inner_code += instr_pair;
}
else if (is_unary_op(opcode))
switch (kind)
{
// Add the instruction twice to have the same instruction count in the loop as in the
// other cases.
inner_code.push_back(OP_DUP1);
for (int i = 0; i < stack_limit; ++i)
{
inner_code.push_back(uint8_t(opcode));
inner_code.push_back(uint8_t(opcode));
}
inner_code.push_back(OP_POP);
}
else if (is_binary_op(opcode))
{
inner_code.push_back(OP_DUP1);
for (int i = 0; i < (stack_limit - 1); ++i)
{
inner_code.push_back(OP_DUP1);
inner_code.push_back(uint8_t(opcode));
}
inner_code.push_back(OP_POP);
case InstructionKind::push:
return stack_limit * (push(opcode, {}) + OP_POP);
case InstructionKind::producer:
return stack_limit * (bytecode{opcode} + OP_POP);
case InstructionKind::unop:
return OP_DUP1 + stack_limit * 2 * bytecode{opcode} + OP_POP;
case InstructionKind::binop:
return OP_DUP1 + (stack_limit - 1) * (OP_DUP1 + bytecode{opcode}) + OP_POP;
default:
INTX_UNREACHABLE();
}
break;
}
case Mode::full_stack:
{
if (opcode >= OP_PUSH1 && opcode <= OP_PUSH32)
switch (kind)
{
for (int i = 0; i < stack_limit; ++i)
{
inner_code.push_back(uint8_t(opcode));
if (opcode >= OP_PUSH1 && opcode <= OP_PUSH32)
std::fill_n(std::back_inserter(inner_code), opcode - OP_PUSH1 + 1, uint8_t{0});
}
std::fill_n(std::back_inserter(inner_code), stack_limit, uint8_t{OP_POP});
case InstructionKind::push:
return stack_limit * push(opcode, {}) + stack_limit * OP_POP;
case InstructionKind::producer:
return stack_limit * opcode + stack_limit * OP_POP;
case InstructionKind::binop:
return stack_limit * OP_DUP1 + (stack_limit - 1) * opcode + OP_POP;
default:
INTX_UNREACHABLE();
}
else if (is_producer(opcode))
{
std::fill_n(std::back_inserter(inner_code), stack_limit, uint8_t(opcode));
std::fill_n(std::back_inserter(inner_code), stack_limit, uint8_t{OP_POP});
}
else if (is_binary_op(opcode))
{
std::fill_n(std::back_inserter(inner_code), stack_limit, uint8_t{OP_DUP1});
std::fill_n(std::back_inserter(inner_code), stack_limit - 1, uint8_t(opcode));
inner_code.push_back(OP_POP);
}
break;
}
default:
INTX_UNREACHABLE();
}

code = loop_prefix + inner_code + loop_suffix;
return {}; // Make old compilers happy.
}


const auto loop_prefix = push(255) + OP_JUMPDEST;
const auto loop_suffix = push("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff") +
OP_ADD + OP_DUP1 + push(2) + OP_JUMPI;

bytes_view generate_code(evmc_opcode opcode, Mode mode) noexcept
{
static bytes cache[256][2]{};

auto& code = cache[opcode][static_cast<int>(mode)];
if (!code.empty())
return code;

code = loop_prefix + generate_loop_inner_code(opcode, mode) + loop_suffix; // Cache it.
return code;
}
} // namespace
Expand Down Expand Up @@ -159,26 +132,23 @@ void register_synthetic_benchmarks()

for (auto opcode : opcodes)
{
for (auto& [vm_name, vm] : registered_vms)
{
RegisterBenchmark(
(std::string{vm_name} + "/execute/synth/" + instr::traits[opcode].name + "/m")
.c_str(),
[&vm = vm, opcode](
State& state) { execute(state, vm, generate_code(opcode, Mode::min_stack)); })
->Unit(kMicrosecond);
}
const auto kind = get_instruction_kind(opcode);

if (!is_unary_op(opcode))
for (auto mode : {Mode::min_stack, Mode::full_stack})
{
if (mode == Mode::full_stack && kind == InstructionKind::unop)
continue;

const auto name_suffix =
std::string{'/', static_cast<char>(kind)} + std::to_string(static_cast<int>(mode));

for (auto& [vm_name, vm] : registered_vms)
{
RegisterBenchmark(
(std::string{vm_name} + "/execute/synth/" + instr::traits[opcode].name + "/f")
.c_str(),
[&vm = vm, opcode](State& state) {
execute(state, vm, generate_code(opcode, Mode::full_stack));
})
RegisterBenchmark((std::string{vm_name} + "/execute/synth/" +
instr::traits[opcode].name + name_suffix)
.c_str(),
[&vm = vm, opcode, mode](
State& state) { execute(state, vm, generate_code(opcode, mode)); })
->Unit(kMicrosecond);
}
}
Expand Down

0 comments on commit 440c0dc

Please sign in to comment.