Wire Sysio Wire Sysion 1.0.0
Loading...
Searching...
No Matches
backend.hpp
Go to the documentation of this file.
1#pragma once
2
3#include <sysio/vm/allocator.hpp>
4#include <sysio/vm/bitcode_writer.hpp>
5#include <sysio/vm/config.hpp>
6#include <sysio/vm/debug_visitor.hpp>
7#include <sysio/vm/execution_context.hpp>
8#include <sysio/vm/interpret_visitor.hpp>
9#include <sysio/vm/null_writer.hpp>
10#include <sysio/vm/parser.hpp>
11#include <sysio/vm/types.hpp>
12
13#ifdef __x86_64__
14#include <sysio/vm/x86_64.hpp>
15#endif
16
17#include <atomic>
18#include <exception>
19#include <iostream>
20#include <optional>
21#include <string_view>
22#include <system_error>
23#include <vector>
24
25namespace sysio { namespace vm {
26
27#ifdef __x86_64__
28 struct jit {
29 template<typename Host>
30 using context = jit_execution_context<Host>;
31 template<typename Host, typename Options, typename DebugInfo>
32 using parser = binary_parser<machine_code_writer<jit_execution_context<Host>>, Options, DebugInfo>;
33 static constexpr bool is_jit = true;
34 };
35
36 struct jit_profile {
37 template<typename Host>
38 using context = jit_execution_context<Host, true>;
39 template<typename Host, typename Options, typename DebugInfo>
40 using parser = binary_parser<machine_code_writer<context<Host>>, Options, DebugInfo>;
41 static constexpr bool is_jit = true;
42 };
43#endif
44
45 struct interpreter {
46 template<typename Host>
48 template<typename Host, typename Options, typename DebugInfo>
50 static constexpr bool is_jit = false;
51 };
52
53 struct null_backend {
54 template<typename Host>
56 template<typename Host, typename Options, typename DebugInfo>
58 static constexpr bool is_jit = false;
59 };
60
61 template <typename HostFunctions = std::nullptr_t, typename Impl = interpreter, typename Options = default_options, typename DebugInfo = null_debug_info>
62 class backend {
64 using context_t = typename Impl::template context<HostFunctions>;
65 using parser_t = typename Impl::template parser<HostFunctions, Options, DebugInfo>;
66 void construct(host_t* host=nullptr) {
67 mod.finalize();
68 ctx.set_wasm_allocator(memory_alloc);
69 if constexpr (!std::is_same_v<HostFunctions, std::nullptr_t>)
70 HostFunctions::resolve(mod);
71 // FIXME: should not hard code knowledge of null_backend here
72 if constexpr (!std::is_same_v<Impl, null_backend>)
73 initialize(host);
74 }
75 public:
76 backend(wasm_code&& code, host_t& host, wasm_allocator* alloc, const Options& options = Options{})
77 : memory_alloc(alloc), ctx(parser_t{ mod.allocator, options }.parse_module(code, mod, debug), detail::get_max_call_depth(options)) {
78 ctx.set_max_pages(detail::get_max_pages(options));
79 construct(&host);
80 }
81 backend(wasm_code&& code, wasm_allocator* alloc, const Options& options = Options{})
82 : memory_alloc(alloc), ctx(parser_t{ mod.allocator, options }.parse_module(code, mod, debug), detail::get_max_call_depth(options)) {
83 ctx.set_max_pages(detail::get_max_pages(options));
84 construct();
85 }
86 backend(wasm_code& code, host_t& host, wasm_allocator* alloc, const Options& options = Options{})
87 : memory_alloc(alloc), ctx(parser_t{ mod.allocator, options }.parse_module(code, mod, debug), detail::get_max_call_depth(options)) {
88 ctx.set_max_pages(detail::get_max_pages(options));
89 construct(&host);
90 }
91 backend(wasm_code& code, wasm_allocator* alloc, const Options& options = Options{})
92 : memory_alloc(alloc), ctx(parser_t{ mod.allocator, options }.parse_module(code, mod, debug), detail::get_max_call_depth(options)) {
93 ctx.set_max_pages(detail::get_max_pages(options));
94 construct();
95 }
96 backend(wasm_code_ptr& ptr, size_t sz, host_t& host, wasm_allocator* alloc, const Options& options = Options{})
97 : memory_alloc(alloc), ctx(parser_t{ mod.allocator, options }.parse_module2(ptr, sz, mod, debug), detail::get_max_call_depth(options)) {
98 ctx.set_max_pages(detail::get_max_pages(options));
99 construct(&host);
100 }
101 backend(wasm_code_ptr& ptr, size_t sz, wasm_allocator* alloc, const Options& options = Options{})
102 : memory_alloc(alloc), ctx(parser_t{ mod.allocator, options }.parse_module2(ptr, sz, mod, debug), detail::get_max_call_depth(options)) {
103 ctx.set_max_pages(detail::get_max_pages(options));
104 construct();
105 }
106
107 template <typename... Args>
108 inline auto operator()(host_t& host, const std::string_view& mod, const std::string_view& func, Args... args) {
109 return call(host, mod, func, args...);
110 }
111
112 template <typename... Args>
113 inline bool operator()(const std::string_view& mod, const std::string_view& func, Args... args) {
114 return call(mod, func, args...);
115 }
116
117 // Only dynamic options matter. Parser options will be ignored.
118 inline backend& initialize(host_t* host, const Options& new_options) {
119 ctx.set_max_call_depth(detail::get_max_call_depth(new_options));
120 ctx.set_max_pages(detail::get_max_pages(new_options));
121 initialize(host);
122 return *this;
123 }
124
125 inline backend& initialize(host_t* host=nullptr) {
126 if (memory_alloc) {
127 ctx.reset();
128 ctx.execute_start(host, interpret_visitor(ctx));
129 }
130 return *this;
131 }
132
133 inline backend& initialize(host_t& host) {
134 return initialize(&host);
135 }
136
137 template <typename... Args>
138 inline bool call_indirect(host_t* host, uint32_t func_index, Args... args) {
139 if constexpr (eos_vm_debug) {
140 ctx.execute_func_table(host, debug_visitor(ctx), func_index, args...);
141 } else {
142 ctx.execute_func_table(host, interpret_visitor(ctx), func_index, args...);
143 }
144 return true;
145 }
146
147 template <typename... Args>
148 inline bool call(host_t* host, uint32_t func_index, Args... args) {
149 if constexpr (eos_vm_debug) {
150 ctx.execute(host, debug_visitor(ctx), func_index, args...);
151 } else {
152 ctx.execute(host, interpret_visitor(ctx), func_index, args...);
153 }
154 return true;
155 }
156
157 template <typename... Args>
158 inline bool call(host_t& host, const std::string_view& mod, const std::string_view& func, Args... args) {
159 if constexpr (eos_vm_debug) {
160 ctx.execute(&host, debug_visitor(ctx), func, args...);
161 } else {
162 ctx.execute(&host, interpret_visitor(ctx), func, args...);
163 }
164 return true;
165 }
166
167 template <typename... Args>
168 inline bool call(const std::string_view& mod, const std::string_view& func, Args... args) {
169 if constexpr (eos_vm_debug) {
170 ctx.execute(nullptr, debug_visitor(ctx), func, args...);
171 } else {
172 ctx.execute(nullptr, interpret_visitor(ctx), func, args...);
173 }
174 return true;
175 }
176
177 template <typename... Args>
178 inline auto call_with_return(host_t& host, const std::string_view& mod, const std::string_view& func, Args... args ) {
179 if constexpr (eos_vm_debug) {
180 return ctx.execute(&host, debug_visitor(ctx), func, args...);
181 } else {
182 return ctx.execute(&host, interpret_visitor(ctx), func, args...);
183 }
184 }
185
186 template <typename... Args>
187 inline auto call_with_return(const std::string_view& mod, const std::string_view& func, Args... args) {
188 if constexpr (eos_vm_debug) {
189 return ctx.execute(nullptr, debug_visitor(ctx), func, args...);
190 } else {
191 return ctx.execute(nullptr, interpret_visitor(ctx), func, args...);
192 }
193 }
194
195 template<typename Watchdog, typename F>
196 inline void timed_run(Watchdog&& wd, F&& f) {
197 std::atomic<bool> _timed_out = false;
198 auto reenable_code = scope_guard{[&](){
199 if (_timed_out) {
200 mod.allocator.enable_code(Impl::is_jit);
201 }
202 }};
203 try {
204 auto wd_guard = wd.scoped_run([this,&_timed_out]() {
205 _timed_out = true;
206 mod.allocator.disable_code();
207 });
208 static_cast<F&&>(f)();
209 } catch(wasm_memory_exception&) {
210 if (_timed_out) {
211 throw timeout_exception{ "execution timed out" };
212 } else {
213 throw;
214 }
215 }
216 }
217
218 template <typename Watchdog>
219 inline void execute_all(Watchdog&& wd, host_t& host) {
220 timed_run(static_cast<Watchdog&&>(wd), [&]() {
221 for (int i = 0; i < mod.exports.size(); i++) {
222 if (mod.exports[i].kind == external_kind::Function) {
223 std::string s{ (const char*)mod.exports[i].field_str.raw(), mod.exports[i].field_str.size() };
224 ctx.execute(host, interpret_visitor(ctx), s);
225 }
226 }
227 });
228 }
229
230 template <typename Watchdog>
231 inline void execute_all(Watchdog&& wd) {
232 timed_run(static_cast<Watchdog&&>(wd), [&]() {
233 for (int i = 0; i < mod.exports.size(); i++) {
234 if (mod.exports[i].kind == external_kind::Function) {
235 std::string s{ (const char*)mod.exports[i].field_str.raw(), mod.exports[i].field_str.size() };
236 ctx.execute(nullptr, interpret_visitor(ctx), s);
237 }
238 }
239 });
240 }
241
242 inline void set_wasm_allocator(wasm_allocator* alloc) {
243 memory_alloc = alloc;
244 ctx.set_wasm_allocator(memory_alloc);
245 }
246
247 inline wasm_allocator* get_wasm_allocator() { return memory_alloc; }
248 inline module& get_module() { return mod; }
249 inline void exit(const std::error_code& ec) { ctx.exit(ec); }
250 inline auto& get_context() { return ctx; }
251
252 const DebugInfo& get_debug() const { return debug; }
253
254 private:
255 wasm_allocator* memory_alloc = nullptr; // non owning pointer
256 module mod;
257 DebugInfo debug;
258 context_t ctx;
259 };
260}} // namespace sysio::vm
backend(wasm_code &code, host_t &host, wasm_allocator *alloc, const Options &options=Options{})
Definition backend.hpp:86
const DebugInfo & get_debug() const
Definition backend.hpp:252
auto call_with_return(const std::string_view &mod, const std::string_view &func, Args... args)
Definition backend.hpp:187
bool call_indirect(host_t *host, uint32_t func_index, Args... args)
Definition backend.hpp:138
backend(wasm_code &&code, host_t &host, wasm_allocator *alloc, const Options &options=Options{})
Definition backend.hpp:76
backend(wasm_code &code, wasm_allocator *alloc, const Options &options=Options{})
Definition backend.hpp:91
backend(wasm_code_ptr &ptr, size_t sz, wasm_allocator *alloc, const Options &options=Options{})
Definition backend.hpp:101
void execute_all(Watchdog &&wd)
Definition backend.hpp:231
backend & initialize(host_t *host=nullptr)
Definition backend.hpp:125
backend(wasm_code &&code, wasm_allocator *alloc, const Options &options=Options{})
Definition backend.hpp:81
bool call(host_t *host, uint32_t func_index, Args... args)
Definition backend.hpp:148
void execute_all(Watchdog &&wd, host_t &host)
Definition backend.hpp:219
backend & initialize(host_t &host)
Definition backend.hpp:133
auto operator()(host_t &host, const std::string_view &mod, const std::string_view &func, Args... args)
Definition backend.hpp:108
backend(wasm_code_ptr &ptr, size_t sz, host_t &host, wasm_allocator *alloc, const Options &options=Options{})
Definition backend.hpp:96
auto & get_context()
Definition backend.hpp:250
bool call(const std::string_view &mod, const std::string_view &func, Args... args)
Definition backend.hpp:168
void exit(const std::error_code &ec)
Definition backend.hpp:249
void timed_run(Watchdog &&wd, F &&f)
Definition backend.hpp:196
bool call(host_t &host, const std::string_view &mod, const std::string_view &func, Args... args)
Definition backend.hpp:158
backend & initialize(host_t *host, const Options &new_options)
Definition backend.hpp:118
bool operator()(const std::string_view &mod, const std::string_view &func, Args... args)
Definition backend.hpp:113
wasm_allocator * get_wasm_allocator()
Definition backend.hpp:247
auto call_with_return(host_t &host, const std::string_view &mod, const std::string_view &func, Args... args)
Definition backend.hpp:178
void set_wasm_allocator(wasm_allocator *alloc)
Definition backend.hpp:242
bool debug
watchdog wd
bip::allocator< T, pinnable_mapped_file::segment_manager > allocator
Definition chainbase.hpp:56
typename host_type< HF >::type host_type_t
constexpr bool eos_vm_debug
Definition config.hpp:22
std::vector< uint8_t > wasm_code
Definition types.hpp:147
unsigned int uint32_t
Definition stdint.h:126
static constexpr bool is_jit
Definition backend.hpp:50
static constexpr bool is_jit
Definition backend.hpp:58
char * s