Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions include/eosio/vm/allocator.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -316,6 +316,8 @@ namespace eosio { namespace vm {
mprotect(_code_base, _code_size, PROT_NONE);
}

const void* get_code_start() const { return _code_base; }

/* different semantics than free,
* the memory must be at the end of the most recently allocated block.
*/
Expand Down
39 changes: 25 additions & 14 deletions include/eosio/vm/backend.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,32 +24,40 @@ namespace eosio { namespace vm {
struct jit {
template<typename Host>
using context = jit_execution_context<Host>;
template<typename Host, typename Options>
using parser = binary_parser<machine_code_writer<jit_execution_context<Host>>, Options>;
template<typename Host, typename Options, typename DebugInfo>
using parser = binary_parser<machine_code_writer<jit_execution_context<Host>>, Options, DebugInfo>;
static constexpr bool is_jit = true;
};

struct jit_profile {
template<typename Host>
using context = jit_execution_context<Host, true>;
template<typename Host, typename Options, typename DebugInfo>
using parser = binary_parser<machine_code_writer<context<Host>>, Options, DebugInfo>;
static constexpr bool is_jit = true;
};

struct interpreter {
template<typename Host>
using context = execution_context<Host>;
template<typename Host, typename Options>
using parser = binary_parser<bitcode_writer, Options>;
template<typename Host, typename Options, typename DebugInfo>
using parser = binary_parser<bitcode_writer, Options, DebugInfo>;
static constexpr bool is_jit = false;
};

struct null_backend {
template<typename Host>
using context = null_execution_context<Host>;
template<typename Host, typename Options>
using parser = binary_parser<null_writer, Options>;
template<typename Host, typename Options, typename DebugInfo>
using parser = binary_parser<null_writer, Options, DebugInfo>;
static constexpr bool is_jit = false;
};

template <typename HostFunctions = std::nullptr_t, typename Impl = interpreter, typename Options = default_options>
template <typename HostFunctions = std::nullptr_t, typename Impl = interpreter, typename Options = default_options, typename DebugInfo = null_debug_info>
class backend {
using host_t = detail::host_type_t<HostFunctions>;
using context_t = typename Impl::template context<HostFunctions>;
using parser_t = typename Impl::template parser<HostFunctions, Options>;
using parser_t = typename Impl::template parser<HostFunctions, Options, DebugInfo>;
void construct(host_t* host=nullptr) {
mod.finalize();
ctx.set_wasm_allocator(memory_alloc);
Expand All @@ -61,32 +69,32 @@ namespace eosio { namespace vm {
}
public:
backend(wasm_code&& code, host_t& host, wasm_allocator* alloc, const Options& options = Options{})
: memory_alloc(alloc), ctx(parser_t{ mod.allocator, options }.parse_module(code, mod), detail::get_max_call_depth(options)) {
: memory_alloc(alloc), ctx(parser_t{ mod.allocator, options }.parse_module(code, mod, debug), detail::get_max_call_depth(options)) {
ctx.set_max_pages(detail::get_max_pages(options));
construct(&host);
}
backend(wasm_code&& code, wasm_allocator* alloc, const Options& options = Options{})
: memory_alloc(alloc), ctx(parser_t{ mod.allocator, options }.parse_module(code, mod), detail::get_max_call_depth(options)) {
: memory_alloc(alloc), ctx(parser_t{ mod.allocator, options }.parse_module(code, mod, debug), detail::get_max_call_depth(options)) {
ctx.set_max_pages(detail::get_max_pages(options));
construct();
}
backend(wasm_code& code, host_t& host, wasm_allocator* alloc, const Options& options = Options{})
: memory_alloc(alloc), ctx(parser_t{ mod.allocator, options }.parse_module(code, mod), detail::get_max_call_depth(options)) {
: memory_alloc(alloc), ctx(parser_t{ mod.allocator, options }.parse_module(code, mod, debug), detail::get_max_call_depth(options)) {
ctx.set_max_pages(detail::get_max_pages(options));
construct(&host);
}
backend(wasm_code& code, wasm_allocator* alloc, const Options& options = Options{})
: memory_alloc(alloc), ctx(parser_t{ mod.allocator, options }.parse_module(code, mod), detail::get_max_call_depth(options)) {
: memory_alloc(alloc), ctx(parser_t{ mod.allocator, options }.parse_module(code, mod, debug), detail::get_max_call_depth(options)) {
ctx.set_max_pages(detail::get_max_pages(options));
construct();
}
backend(wasm_code_ptr& ptr, size_t sz, host_t& host, wasm_allocator* alloc, const Options& options = Options{})
: memory_alloc(alloc), ctx(parser_t{ mod.allocator, options }.parse_module2(ptr, sz, mod), detail::get_max_call_depth(options)) {
: memory_alloc(alloc), ctx(parser_t{ mod.allocator, options }.parse_module2(ptr, sz, mod, debug), detail::get_max_call_depth(options)) {
ctx.set_max_pages(detail::get_max_pages(options));
construct(&host);
}
backend(wasm_code_ptr& ptr, size_t sz, wasm_allocator* alloc, const Options& options = Options{})
: memory_alloc(alloc), ctx(parser_t{ mod.allocator, options }.parse_module2(ptr, sz, mod), detail::get_max_call_depth(options)) {
: memory_alloc(alloc), ctx(parser_t{ mod.allocator, options }.parse_module2(ptr, sz, mod, debug), detail::get_max_call_depth(options)) {
ctx.set_max_pages(detail::get_max_pages(options));
construct();
}
Expand Down Expand Up @@ -236,9 +244,12 @@ namespace eosio { namespace vm {
inline void exit(const std::error_code& ec) { ctx.exit(ec); }
inline auto& get_context() { return ctx; }

const DebugInfo& get_debug() const { return debug; }

private:
wasm_allocator* memory_alloc = nullptr; // non owning pointer
module mod;
DebugInfo debug;
context_t ctx;
};
}} // namespace eosio::vm
9 changes: 7 additions & 2 deletions include/eosio/vm/bitcode_writer.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -295,11 +295,16 @@ namespace eosio { namespace vm {
}

void finalize(function_body& body) {
fb.resize(op_index + 1);
op_index++;
fb.resize(op_index);
body.code = fb.raw();
body.size = op_index + 1;
body.size = op_index;
_base_offset += body.size;
}

const void* get_addr() const { return fb.raw() + op_index; }
const void* get_base_addr() const { return _code_segment_base; }

private:

growable_allocator& _allocator;
Expand Down
102 changes: 102 additions & 0 deletions include/eosio/vm/debug_info.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
#pragma once

#include <utility>
#include <vector>
#include <cstdint>
#include <cstddef>
#include <algorithm>

namespace eosio::vm {

struct null_debug_info {
using builder = null_debug_info;
void on_code_start(const void* compiled_base, const void* wasm_code_start) {}
void on_function_start(const void* code_addr, const void* wasm_addr) {}
void on_instr_start(const void* code_addr, const void* wasm_addr) {}
void on_code_end(const void* code_addr, const void* wasm_addr) {}
void set(const null_debug_info&) {}
void relocate(const void*) {}
};

// Maps a contiguous region of code to offsets onto the code section of the original wasm.
class profile_instr_map {
struct addr_entry {
uint32_t offset;
uint32_t wasm_addr;
};

public:

struct builder {
void on_code_start(const void* compiled_base, const void* wasm_code_start) {
code_base = compiled_base;
wasm_base = wasm_code_start;
}
void on_function_start(const void* code_addr, const void* wasm_addr) {
data.push_back({
static_cast<std::uint32_t>(reinterpret_cast<const char*>(code_addr) - reinterpret_cast<const char*>(code_base)),
static_cast<std::uint32_t>(reinterpret_cast<const char*>(wasm_addr) - reinterpret_cast<const char*>(wasm_base))
});
}
void on_instr_start(const void* code_addr, const void* wasm_addr) {
data.push_back({
static_cast<std::uint32_t>(reinterpret_cast<const char*>(code_addr) - reinterpret_cast<const char*>(code_base)),
static_cast<std::uint32_t>(reinterpret_cast<const char*>(wasm_addr) - reinterpret_cast<const char*>(wasm_base))
});
}
void on_code_end(const void* code_addr, const void* wasm_addr) {
code_end = code_addr;
}

const void* code_base = nullptr;
const void* wasm_base = nullptr;
const void* code_end = nullptr;
std::vector<addr_entry> data;
};

void set(builder&& b) {
data = std::move(b.data);
std::sort(data.begin(), data.end(), [](const addr_entry& lhs, const addr_entry& rhs){ return lhs.offset < rhs.offset; });
base_address = b.code_base;
code_size = reinterpret_cast<const char*>(b.code_end) - reinterpret_cast<const char*>(base_address);
offset_to_addr = data.data();
offset_to_addr_len = data.size();
}

profile_instr_map() = default;
Comment thread
heifner marked this conversation as resolved.
profile_instr_map(const profile_instr_map&) = delete;
profile_instr_map& operator=(const profile_instr_map&) = delete;

// Indicate that the executable code was moved/copied/mmapped/etc to another location
void relocate(const void* new_base) { base_address = new_base; }

// Cannot use most of the standard library as the STL is not async-signal-safe
std::uint32_t translate(const void* pc) const {
std::size_t diff = (reinterpret_cast<const char*>(pc) - reinterpret_cast<const char*>(base_address)); // negative values wrap
if(diff >= code_size || diff < offset_to_addr[0].offset) return 0xFFFFFFFFu;
std::uint32_t offset = diff;

// Loop invariant: offset_to_addr[lower].offset <= offset < offset_to_addr[upper].offset
std::size_t lower = 0, upper = offset_to_addr_len;
while(upper - lower > 1) {
std::size_t mid = lower + (upper - lower) / 2;
if(offset_to_addr[mid].offset <= offset) {
lower = mid;
} else {
upper = mid;
}
}

return offset_to_addr[lower].wasm_addr;
}
private:
const void* base_address = nullptr;
std::size_t code_size = 0;

addr_entry* offset_to_addr = nullptr;
std::size_t offset_to_addr_len = 0;

std::vector<addr_entry> data;
};

}
1 change: 1 addition & 0 deletions include/eosio/vm/exceptions.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ namespace eosio { namespace vm {
DECLARE_EXCEPTION( timeout_exception, 4010001, "timeout" )
DECLARE_EXCEPTION( wasm_exit_exception, 4010002, "exit" )
DECLARE_EXCEPTION( span_exception, 4020000, "span exception" )
DECLARE_EXCEPTION( profile_exception, 4030000, "profile exception" )
}} // eosio::vm

#undef DECLARE_EXCEPTION
Loading