diff --git a/include/eosio/vm/backend.hpp b/include/eosio/vm/backend.hpp index 3172bd6..9c607eb 100644 --- a/include/eosio/vm/backend.hpp +++ b/include/eosio/vm/backend.hpp @@ -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::resolve(mod); // FIXME: should not hard code knowledge of null_backend here diff --git a/include/eosio/vm/execution_context.hpp b/include/eosio/vm/execution_context.hpp index ba48f8c..60489d4 100644 --- a/include/eosio/vm/execution_context.hpp +++ b/include/eosio/vm/execution_context.hpp @@ -87,6 +87,14 @@ namespace eosio { namespace vm { Derived& derived() { return static_cast(*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) { @@ -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; + } } } @@ -193,6 +203,7 @@ namespace eosio { namespace vm { detail::host_invoker_t _rhf; std::error_code _error_code; operand_stack _os; + std::vector _globals; }; struct jit_visitor { template jit_visitor(T&&) {} }; @@ -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) {} @@ -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 @@ -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)}, @@ -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); diff --git a/include/eosio/vm/parser.hpp b/include/eosio/vm/parser.hpp index d35581c..ab37f7b 100644 --- a/include/eosio/vm/parser.hpp +++ b/include/eosio/vm/parser.hpp @@ -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) { diff --git a/include/eosio/vm/types.hpp b/include/eosio/vm/types.hpp index 149afc2..443d119 100644 --- a/include/eosio/vm/types.hpp +++ b/include/eosio/vm/types.hpp @@ -74,7 +74,6 @@ namespace eosio { namespace vm { struct global_variable { global_type type; init_expr init; - init_expr current; }; struct table_type { diff --git a/include/eosio/vm/x86_64.hpp b/include/eosio/vm/x86_64.hpp index b52a8cf..23ddf0a 100644 --- a/include/eosio/vm/x86_64.hpp +++ b/include/eosio/vm/x86_64.hpp @@ -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) { @@ -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_( "unreachable" ); } static void on_fp_error() { vm::throw_( "floating point error" ); } static void on_call_indirect_error() { vm::throw_( "call_indirect out of range" ); }