Skip to content

Commit

Permalink
Merge pull request #16 from AntelopeIO/make_globals_threaded_safe
Browse files Browse the repository at this point in the history
store wasm globals in execution context such that a single parsed module can be shared by multiple threads
  • Loading branch information
linh2931 authored Jul 26, 2023
2 parents 1e9345f + 65568f5 commit 4d5415f
Show file tree
Hide file tree
Showing 5 changed files with 134 additions and 42 deletions.
1 change: 1 addition & 0 deletions include/eosio/vm/backend.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ namespace eosio { namespace vm {
void construct(host_t* host=nullptr) {
mod.finalize();
ctx.set_wasm_allocator(memory_alloc);
ctx.initialize_globals();
if constexpr (!std::is_same_v<HostFunctions, std::nullptr_t>)
HostFunctions::resolve(mod);
// FIXME: should not hard code knowledge of null_backend here
Expand Down
66 changes: 56 additions & 10 deletions include/eosio/vm/execution_context.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,14 @@ namespace eosio { namespace vm {
Derived& derived() { return static_cast<Derived&>(*this); }
execution_context_base(module& m) : _mod(m) {}

inline void initialize_globals() {
EOS_VM_ASSERT(_globals.empty(), wasm_memory_exception, "initialize_globals called on non-empty _globals");
_globals.reserve(_mod.globals.size());
for (uint32_t i = 0; i < _mod.globals.size(); i++) {
_globals.emplace_back(_mod.globals[i].init);
}
}

inline int32_t grow_linear_memory(int32_t pages) {
const int32_t sz = _wasm_alloc->get_current_page();
if (pages < 0) {
Expand Down Expand Up @@ -145,9 +153,11 @@ namespace eosio { namespace vm {
}

// reset the mutable globals
EOS_VM_ASSERT(_globals.size() == _mod.globals.size(), wasm_memory_exception, "number of globals in execution_context not equall to the one in module");
for (uint32_t i = 0; i < _mod.globals.size(); i++) {
if (_mod.globals[i].type.mutability)
_mod.globals[i].current = _mod.globals[i].init;
if (_mod.globals[i].type.mutability) {
_globals[i] = _mod.globals[i].init;
}
}
}

Expand Down Expand Up @@ -193,6 +203,7 @@ namespace eosio { namespace vm {
detail::host_invoker_t<Host> _rhf;
std::error_code _error_code;
operand_stack _os;
std::vector<init_expr> _globals;
};

struct jit_visitor { template<typename T> jit_visitor(T&&) {} };
Expand Down Expand Up @@ -226,6 +237,7 @@ namespace eosio { namespace vm {
using base_type::get_operand_stack;
using base_type::linear_memory;
using base_type::get_interface;
using base_type::_globals;

jit_execution_context(module& m, std::uint32_t max_call_depth) : base_type(m), _remaining_call_depth(max_call_depth) {}

Expand Down Expand Up @@ -404,6 +416,38 @@ namespace eosio { namespace vm {
static constexpr bool async_backtrace() { return EnableBacktrace; }
#endif

inline int32_t get_global_i32(uint32_t index) {
return _globals[index].value.i32;
}

inline int64_t get_global_i64(uint32_t index) {
return _globals[index].value.i64;
}

inline uint32_t get_global_f32(uint32_t index) {
return _globals[index].value.f32;
}

inline uint64_t get_global_f64(uint32_t index) {
return _globals[index].value.f64;
}

inline void set_global_i32(uint32_t index, int32_t value) {
_globals[index].value.i32 = value;
}

inline void set_global_i64(uint32_t index, int64_t value) {
_globals[index].value.i64 = value;
}

inline void set_global_f32(uint32_t index, uint32_t value) {
_globals[index].value.f32 = value;
}

inline void set_global_f64(uint32_t index, uint64_t value) {
_globals[index].value.f64 = value;
}

protected:

template<typename T>
Expand Down Expand Up @@ -501,6 +545,7 @@ namespace eosio { namespace vm {
using base_type::get_operand_stack;
using base_type::linear_memory;
using base_type::get_interface;
using base_type::_globals;

execution_context(module& m, uint32_t max_call_depth)
: base_type(m), _base_allocator{max_call_depth*sizeof(activation_frame)},
Expand Down Expand Up @@ -585,37 +630,38 @@ namespace eosio { namespace vm {
EOS_VM_ASSERT(index < _mod.globals.size(), wasm_interpreter_exception, "global index out of range");
const auto& gl = _mod.globals[index];
switch (gl.type.content_type) {
case types::i32: return i32_const_t{ *(uint32_t*)&gl.current.value.i32 };
case types::i64: return i64_const_t{ *(uint64_t*)&gl.current.value.i64 };
case types::f32: return f32_const_t{ gl.current.value.f32 };
case types::f64: return f64_const_t{ gl.current.value.f64 };
case types::i32: return i32_const_t{ _globals[index].value.i32 };
case types::i64: return i64_const_t{ _globals[index].value.i64 };
case types::f32: return f32_const_t{ _globals[index].value.f32 };
case types::f64: return f64_const_t{ _globals[index].value.f64 };
default: throw wasm_interpreter_exception{ "invalid global type" };
}
}

inline void set_global(uint32_t index, const operand_stack_elem& el) {
EOS_VM_ASSERT(index < _mod.globals.size(), wasm_interpreter_exception, "global index out of range");
EOS_VM_ASSERT(index < _globals.size(), wasm_interpreter_exception, "index for _globals out of range");
auto& gl = _mod.globals[index];
EOS_VM_ASSERT(gl.type.mutability, wasm_interpreter_exception, "global is not mutable");
visit(overloaded{ [&](const i32_const_t& i) {
EOS_VM_ASSERT(gl.type.content_type == types::i32, wasm_interpreter_exception,
"expected i32 global type");
gl.current.value.i32 = i.data.ui;
_globals[index].value.i32 = i.data.ui;
},
[&](const i64_const_t& i) {
EOS_VM_ASSERT(gl.type.content_type == types::i64, wasm_interpreter_exception,
"expected i64 global type");
gl.current.value.i64 = i.data.ui;
_globals[index].value.i64 = i.data.ui;
},
[&](const f32_const_t& f) {
EOS_VM_ASSERT(gl.type.content_type == types::f32, wasm_interpreter_exception,
"expected f32 global type");
gl.current.value.f32 = f.data.ui;
_globals[index].value.f32 = f.data.ui;
},
[&](const f64_const_t& f) {
EOS_VM_ASSERT(gl.type.content_type == types::f64, wasm_interpreter_exception,
"expected f64 global type");
gl.current.value.f64 = f.data.ui;
_globals[index].value.f64 = f.data.ui;
},
[](auto) { throw wasm_interpreter_exception{ "invalid global type" }; } },
el);
Expand Down
1 change: 0 additions & 1 deletion include/eosio/vm/parser.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -469,7 +469,6 @@ namespace eosio { namespace vm {
if(gv.type.mutability)
on_mutable_global(ct);
parse_init_expr(code, gv.init, ct);
gv.current = gv.init;
}

void parse_memory_type(wasm_code_ptr& code, memory_type& mt) {
Expand Down
1 change: 0 additions & 1 deletion include/eosio/vm/types.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,6 @@ namespace eosio { namespace vm {
struct global_variable {
global_type type;
init_expr init;
init_expr current;
};

struct table_type {
Expand Down
107 changes: 77 additions & 30 deletions include/eosio/vm/x86_64.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -445,43 +445,64 @@ namespace eosio { namespace vm {
}

void emit_get_global(uint32_t globalidx) {
auto icount = variable_size_instr(13, 14);
auto icount = variable_size_instr(24, 42); // emit_setup_backtrace can be 0 or 9, and emit_restore_backtrace 0 or 9, the total of the rest 24
auto& gl = _mod.globals[globalidx];
void *ptr = &gl.current.value;
emit_setup_backtrace();
// pushq %rdi -- save %rdi content onto stack
emit_bytes(0x57);
// pushq %rsi -- save %rsi content onto stack
emit_bytes(0x56);
// movq $globalidx, %rsi -- pass globalidx to %rsi, the second argument
emit_bytes(0x48, 0xc7, 0xc6);
emit_operand32(globalidx);
// movabsq $get_global, %rax
emit_bytes(0x48, 0xb8);
switch(gl.type.content_type) {
case types::i32:
case types::f32:
// movabsq $ptr, %rax
emit_bytes(0x48, 0xb8);
emit_operand_ptr(ptr);
// movl (%rax), eax
emit_bytes(0x8b, 0x00);
// push %rax
emit_bytes(0x50);
break;
case types::i64:
case types::f64:
// movabsq $ptr, %rax
emit_bytes(0x48, 0xb8);
emit_operand_ptr(ptr);
// movl (%rax), %rax
emit_bytes(0x48, 0x8b, 0x00);
// push %rax
emit_bytes(0x50);
break;
case types::i32: emit_operand_ptr(&get_global_i32); break;
case types::i64: emit_operand_ptr(&get_global_i64); break;
case types::f32: emit_operand_ptr(&get_global_f32); break;
case types::f64: emit_operand_ptr(&get_global_f64); break;
}
// call *%rax
emit_bytes(0xff, 0xd0);
// pop %rsi
emit_bytes(0x5e);
// pop %rdi
emit_bytes(0x5f);
emit_restore_backtrace();
// push %rax -- return result
emit_bytes(0x50);
}

void emit_set_global(uint32_t globalidx) {
auto icount = fixed_size_instr(14);
auto icount = variable_size_instr(24, 42); // emit_setup_backtrace can be 0 or 9, and emit_restore_backtrace 0 or 9, the total of the rest 24
auto& gl = _mod.globals[globalidx];
void *ptr = &gl.current.value;
// popq %rcx
emit_bytes(0x59);
// movabsq $ptr, %rax
// popq %rdx -- pass global value to %rdx, the third argument in set_global
emit_bytes(0x5a);
emit_setup_backtrace();
// pushq %rdi -- save %rdi content onto stack
emit_bytes(0x57);
// pushq %rsi -- save %rsi content onto stack
emit_bytes(0x56);
// movq $globalidx, %rsi -- pass globalidx to %rsi, the second argument
emit_bytes(0x48, 0xc7, 0xc6);
emit_operand32(globalidx);
// movabsq $set_global, %rax
emit_bytes(0x48, 0xb8);
emit_operand_ptr(ptr);
// movq %rcx, (%rax)
emit_bytes(0x48, 0x89, 0x08);
//emit_operand_ptr(&set_global);
switch(gl.type.content_type) {
case types::i32: emit_operand_ptr(&set_global_i32); break;
case types::i64: emit_operand_ptr(&set_global_i64); break;
case types::f32: emit_operand_ptr(&set_global_f32); break;
case types::f64: emit_operand_ptr(&set_global_f64); break;
}
// call *%rax
emit_bytes(0xff, 0xd0);
// pop %rsi
emit_bytes(0x5e);
// pop %rdi
emit_bytes(0x5f);
emit_restore_backtrace();
}

void emit_i32_load(uint32_t /*alignment*/, uint32_t offset) {
Expand Down Expand Up @@ -2692,6 +2713,32 @@ namespace eosio { namespace vm {
return context->grow_linear_memory(pages);
}

static int32_t get_global_i32(Context* context /*rdi*/, uint32_t index /*rsi*/) {
return context->get_global_i32(index);
}
static int64_t get_global_i64(Context* context /*rdi*/, uint32_t index /*rsi*/) {
return context->get_global_i64(index);
}
static uint32_t get_global_f32(Context* context /*rdi*/, uint32_t index /*rsi*/) {
return context->get_global_f32(index);
}
static uint64_t get_global_f64(Context* context /*rdi*/, uint32_t index /*rsi*/) {
return context->get_global_f64(index);
}

static void set_global_i32(Context* context /*rdi*/, uint32_t index /*rsi*/, int32_t value /*rdx*/) {
context->set_global_i32(index, value);
}
static void set_global_i64(Context* context /*rdi*/, uint32_t index /*rsi*/, int64_t value /*rdx*/) {
context->set_global_i64(index, value);
}
static void set_global_f32(Context* context /*rdi*/, uint32_t index /*rsi*/, uint32_t value /*rdx*/) {
context->set_global_f32(index, value);
}
static void set_global_f64(Context* context /*rdi*/, uint32_t index /*rsi*/, uint64_t value /*rdx*/) {
context->set_global_f64(index, value);
}

static void on_unreachable() { vm::throw_<wasm_interpreter_exception>( "unreachable" ); }
static void on_fp_error() { vm::throw_<wasm_interpreter_exception>( "floating point error" ); }
static void on_call_indirect_error() { vm::throw_<wasm_interpreter_exception>( "call_indirect out of range" ); }
Expand Down

0 comments on commit 4d5415f

Please sign in to comment.