|
| 1 | +// evmone: Fast Ethereum Virtual Machine implementation |
| 2 | +// Copyright 2018 Pawel Bylica. |
| 3 | +// Licensed under the Apache License, Version 2.0. |
| 4 | + |
| 5 | +#include "analysis.hpp" |
| 6 | + |
| 7 | +#include <evmc/instructions.h> |
| 8 | + |
| 9 | +namespace evmone |
| 10 | +{ |
| 11 | +namespace |
| 12 | +{ |
| 13 | +bool is_terminator(uint8_t c) noexcept |
| 14 | +{ |
| 15 | + return c == OP_JUMP || c == OP_JUMPI || c == OP_STOP || c == OP_RETURN || c == OP_REVERT || |
| 16 | + c == OP_SELFDESTRUCT; |
| 17 | +} |
| 18 | +} |
| 19 | + |
| 20 | +int code_analysis::find_jumpdest(int offset) noexcept |
| 21 | +{ |
| 22 | + // TODO: Replace with lower_bound(). |
| 23 | + for (const auto& d : jumpdest_map) |
| 24 | + { |
| 25 | + if (d.first == offset) |
| 26 | + return d.second; |
| 27 | + } |
| 28 | + return -1; |
| 29 | +} |
| 30 | + |
| 31 | +code_analysis analyze(const exec_fn_table& fns, const uint8_t* code, size_t code_size) noexcept |
| 32 | +{ |
| 33 | + code_analysis analysis; |
| 34 | + analysis.instrs.reserve(code_size + 1); |
| 35 | + |
| 36 | + auto* instr_table = evmc_get_instruction_metrics_table(EVMC_BYZANTIUM); |
| 37 | + |
| 38 | + block_info* block = nullptr; |
| 39 | + int instr_index = 0; |
| 40 | + for (size_t i = 0; i < code_size; ++i, ++instr_index) |
| 41 | + { |
| 42 | + const auto c = code[i]; |
| 43 | + auto& instr = analysis.instrs.emplace_back(fns[c]); |
| 44 | + |
| 45 | + const bool jumpdest = c == OP_JUMPDEST; |
| 46 | + if (!block || jumpdest) |
| 47 | + { |
| 48 | + // Create new block. |
| 49 | + block = &analysis.blocks.emplace_back(); |
| 50 | + instr.block_index = static_cast<int>(analysis.blocks.size() - 1); |
| 51 | + |
| 52 | + if (jumpdest) |
| 53 | + analysis.jumpdest_map.emplace_back(static_cast<int>(i), instr_index); |
| 54 | + } |
| 55 | + |
| 56 | + auto metrics = instr_table[c]; |
| 57 | + block->gas_cost += metrics.gas_cost; |
| 58 | + auto stack_req = metrics.num_stack_arguments - block->stack_diff; |
| 59 | + block->stack_diff += (metrics.num_stack_returned_items - metrics.num_stack_arguments); |
| 60 | + block->stack_req = std::max(block->stack_req, stack_req); |
| 61 | + block->stack_max = std::max(block->stack_max, block->stack_diff); |
| 62 | + |
| 63 | + // Skip PUSH data. |
| 64 | + if (c >= OP_PUSH1 && c <= OP_PUSH32) |
| 65 | + { |
| 66 | + ++i; |
| 67 | + auto push_size = size_t(c - OP_PUSH1 + 1); |
| 68 | + analysis.extra.emplace_back(); |
| 69 | + auto& extra = analysis.extra.back(); |
| 70 | + |
| 71 | + auto leading_zeros = 32 - push_size; |
| 72 | + for (auto& b : extra.bytes) |
| 73 | + b = 0; |
| 74 | + for (size_t j = 0; j < push_size && (i + j) < code_size; ++j) |
| 75 | + extra.bytes[leading_zeros + j] = code[i + j]; |
| 76 | + instr.extra_data_index = static_cast<int>(analysis.extra.size() - 1); |
| 77 | + i += push_size - 1; |
| 78 | + } |
| 79 | + else if (is_terminator(c)) |
| 80 | + block = nullptr; |
| 81 | + } |
| 82 | + |
| 83 | + // Not terminated block. |
| 84 | + if (block) |
| 85 | + analysis.instrs.emplace_back(nullptr); |
| 86 | + |
| 87 | + return analysis; |
| 88 | +} |
| 89 | + |
| 90 | +} // namespace evmone |
0 commit comments