Wire Sysio Wire Sysion 1.0.0
Loading...
Searching...
No Matches
wasm_sysio_binary_ops.hpp
Go to the documentation of this file.
1#pragma once
2
3#include <boost/preprocessor/seq/for_each.hpp>
4#include <boost/preprocessor/seq/subseq.hpp>
5#include <boost/preprocessor/seq/remove.hpp>
6#include <boost/preprocessor/seq/push_back.hpp>
9
10#include <cstdint>
11#include <functional>
12#include <iterator>
13#include <memory>
15#include <string>
16#include <unordered_map>
17#include <utility>
18#include <vector>
19
20#include "IR/Operators.h"
21#include "IR/Module.h"
22
23namespace sysio { namespace chain { namespace wasm_ops {
24
26 public:
27 instruction_stream(size_t size) : idx(0) {
28 data.resize(size);
29 }
30 void operator<< (const char c) {
31 if (idx >= data.size())
32 data.resize(data.size()*2);
33 data[idx++] = static_cast<U8>(c);
34 }
35 void set(size_t i, const char* arr) {
36 if (i+idx >= data.size())
37 data.resize(data.size()*2+i);
38 memcpy((char*)&data[idx], arr, i);
39 idx += i;
40 }
41 size_t get_index() { return idx; }
42 std::vector<U8> get() {
43 std::vector<U8> ret = data;
44 ret.resize(idx);
45 return ret;
46 }
47// private:
48 size_t idx;
49 std::vector<U8> data;
50};
51
52// forward declaration
53struct instr;
54using namespace fc;
55using wasm_op_ptr = std::unique_ptr<instr>;
56using wasm_instr_ptr = std::shared_ptr<instr>;
57using wasm_return_t = std::vector<uint8_t>;
58using wasm_instr_callback = std::function<std::vector<wasm_instr_ptr>(uint8_t)>;
59using code_vector = std::vector<uint8_t>;
60using code_iterator = std::vector<uint8_t>::iterator;
61using wasm_op_generator = std::function<wasm_instr_ptr(std::vector<uint8_t>, size_t)>;
62
63#pragma pack (push)
64struct memarg {
65 uint32_t a; // align
66 uint32_t o; // offset
67};
68
69struct blocktype {
70 uint8_t result = 0x40; // either null (0x40) or valtype
71};
72
74 uint8_t end = 0x00;
75};
80
81// using for instructions with no fields
82struct voidtype {};
83
84
85inline std::string to_string( uint32_t field ) {
86 return std::string("i32 : ")+std::to_string(field);
87}
88inline std::string to_string( uint64_t field ) {
89 return std::string("i64 : ")+std::to_string(field);
90}
91inline std::string to_string( blocktype field ) {
92 return std::string("blocktype : ")+std::to_string((uint32_t)field.result);
93}
94inline std::string to_string( memoryoptype field ) {
95 return std::string("memoryoptype : ")+std::to_string((uint32_t)field.end);
96}
97inline std::string to_string( memarg field ) {
98 return std::string("memarg : ")+std::to_string(field.a)+std::string(", ")+std::to_string(field.o);
99}
100inline std::string to_string( branchtabletype field ) {
101 return std::string("branchtabletype : ")+std::to_string(field.target_depth)+std::string(", ")+std::to_string(field.table_index);
102}
103
104inline void pack( instruction_stream* stream, uint32_t field ) {
105 const char packed[] = { char(field), char(field >> 8), char(field >> 16), char(field >> 24) };
106 stream->set(sizeof(packed), packed);
107}
108inline void pack( instruction_stream* stream, uint64_t field ) {
109 const char packed[] = { char(field), char(field >> 8), char(field >> 16), char(field >> 24),
110 char(field >> 32), char(field >> 40), char(field >> 48), char(field >> 56) };
111 stream->set(sizeof(packed), packed);
112}
113inline void pack( instruction_stream* stream, blocktype field ) {
114 const char packed[] = { char(field.result) };
115 stream->set(sizeof(packed), packed);
116}
117inline void pack( instruction_stream* stream, memoryoptype field ) {
118 const char packed[] = { char(field.end) };
119 stream->set(sizeof(packed), packed);
120}
121inline void pack( instruction_stream* stream, memarg field ) {
122 const char packed[] = { char(field.a), char(field.a >> 8), char(field.a >> 16), char(field.a >> 24),
123 char(field.o), char(field.o >> 8), char(field.o >> 16), char(field.o >> 24)};
124 stream->set(sizeof(packed), packed);
125
126}
128 const char packed[] = { char(field.target_depth), char(field.target_depth >> 8), char(field.target_depth >> 16), char(field.target_depth >> 24),
129 char(field.target_depth >> 32), char(field.target_depth >> 40), char(field.target_depth >> 48), char(field.target_depth >> 56),
130 char(field.table_index), char(field.table_index >> 8), char(field.table_index >> 16), char(field.table_index >> 24),
131 char(field.table_index >> 32), char(field.table_index >> 40), char(field.table_index >> 48), char(field.table_index >> 56) };
132 stream->set(sizeof(packed), packed);
133}
134
135template <typename Field>
137 static constexpr int skip_ahead = sizeof(uint16_t) + sizeof(Field);
138 static auto unpack( char* opcode, Field& f ) { f = *reinterpret_cast<Field*>(opcode); }
139 static void pack(instruction_stream* stream, Field& f) { return sysio::chain::wasm_ops::pack(stream, f); }
140 static auto to_string(Field& f) { return std::string(" ")+
142};
143template <>
145 static constexpr int skip_ahead = sizeof(uint16_t);
146 static auto unpack( char* opcode, voidtype& f ) {}
147 static void pack(instruction_stream* stream, voidtype& f) {}
148 static auto to_string(voidtype& f) { return ""; }
149};
150
151#define CONSTRUCT_OP_HAS_DATA( r, DATA, OP ) \
152template <typename ... Mutators> \
153struct OP final : instr_base<Mutators...> { \
154 uint16_t code = BOOST_PP_CAT(OP,_code); \
155 DATA field; \
156 uint16_t get_code() override { return BOOST_PP_CAT(OP,_code); } \
157 int skip_ahead() override { return field_specific_params<DATA>::skip_ahead; } \
158 void unpack( char* opcode ) override { \
159 field_specific_params<DATA>::unpack( opcode, field ); \
160 } \
161 void pack(instruction_stream* stream) override { \
162 stream->set(2, (const char*)&code); \
163 field_specific_params<DATA>::pack( stream, field ); \
164 } \
165 std::string to_string() override { \
166 return std::string(BOOST_PP_STRINGIZE(OP))+field_specific_params<DATA>::to_string( field ); \
167 } \
168};
169
170#define WASM_OP_SEQ (error) \
171 (end) \
172 (unreachable) \
173 (nop) \
174 (else_) \
175 (return_) \
176 (drop) \
177 (select) \
178 (i32_eqz) \
179 (i32_eq) \
180 (i32_ne) \
181 (i32_lt_s) \
182 (i32_lt_u) \
183 (i32_gt_s) \
184 (i32_gt_u) \
185 (i32_le_s) \
186 (i32_le_u) \
187 (i32_ge_s) \
188 (i32_ge_u) \
189 (i64_eqz) \
190 (i64_eq) \
191 (i64_ne) \
192 (i64_lt_s) \
193 (i64_lt_u) \
194 (i64_gt_s) \
195 (i64_gt_u) \
196 (i64_le_s) \
197 (i64_le_u) \
198 (i64_ge_s) \
199 (i64_ge_u) \
200 (f32_eq) \
201 (f32_ne) \
202 (f32_lt) \
203 (f32_gt) \
204 (f32_le) \
205 (f32_ge) \
206 (f64_eq) \
207 (f64_ne) \
208 (f64_lt) \
209 (f64_gt) \
210 (f64_le) \
211 (f64_ge) \
212 (i32_clz) \
213 (i32_ctz) \
214 (i32_popcnt) \
215 (i32_add) \
216 (i32_sub) \
217 (i32_mul) \
218 (i32_div_s) \
219 (i32_div_u) \
220 (i32_rem_s) \
221 (i32_rem_u) \
222 (i32_and) \
223 (i32_or) \
224 (i32_xor) \
225 (i32_shl) \
226 (i32_shr_s) \
227 (i32_shr_u) \
228 (i32_rotl) \
229 (i32_rotr) \
230 (i64_clz) \
231 (i64_ctz) \
232 (i64_popcnt) \
233 (i64_add) \
234 (i64_sub) \
235 (i64_mul) \
236 (i64_div_s) \
237 (i64_div_u) \
238 (i64_rem_s) \
239 (i64_rem_u) \
240 (i64_and) \
241 (i64_or) \
242 (i64_xor) \
243 (i64_shl) \
244 (i64_shr_s) \
245 (i64_shr_u) \
246 (i64_rotl) \
247 (i64_rotr) \
248 (f32_abs) \
249 (f32_neg) \
250 (f32_ceil) \
251 (f32_floor) \
252 (f32_trunc) \
253 (f32_nearest) \
254 (f32_sqrt) \
255 (f32_add) \
256 (f32_sub) \
257 (f32_mul) \
258 (f32_div) \
259 (f32_min) \
260 (f32_max) \
261 (f32_copysign) \
262 (f64_abs) \
263 (f64_neg) \
264 (f64_ceil) \
265 (f64_floor) \
266 (f64_trunc) \
267 (f64_nearest) \
268 (f64_sqrt) \
269 (f64_add) \
270 (f64_sub) \
271 (f64_mul) \
272 (f64_div) \
273 (f64_min) \
274 (f64_max) \
275 (f64_copysign) \
276 (i32_wrap_i64) \
277 (i32_trunc_s_f32) \
278 (i32_trunc_u_f32) \
279 (i32_trunc_s_f64) \
280 (i32_trunc_u_f64) \
281 (i64_extend_s_i32) \
282 (i64_extend_u_i32) \
283 (i64_trunc_s_f32) \
284 (i64_trunc_u_f32) \
285 (i64_trunc_s_f64) \
286 (i64_trunc_u_f64) \
287 (f32_convert_s_i32) \
288 (f32_convert_u_i32) \
289 (f32_convert_s_i64) \
290 (f32_convert_u_i64) \
291 (f32_demote_f64) \
292 (f64_convert_s_i32) \
293 (f64_convert_u_i32) \
294 (f64_convert_s_i64) \
295 (f64_convert_u_i64) \
296 (f64_promote_f32) \
297 (i32_reinterpret_f32) \
298 (i64_reinterpret_f64) \
299 (f32_reinterpret_i32) \
300 (f64_reinterpret_i64) \
301 (grow_memory) \
302 (current_memory) \
303/* BLOCK TYPE OPS */ \
304 (block) \
305 (loop) \
306 (if_) \
307/* 32bit OPS */ \
308 (br) \
309 (br_if) \
310 (call) \
311 (call_indirect) \
312 (get_local) \
313 (set_local) \
314 (tee_local) \
315 (get_global) \
316 (set_global) \
317 (i32_const) \
318 (f32_const) \
319/* memarg OPS */ \
320 (i32_load) \
321 (i64_load) \
322 (f32_load) \
323 (f64_load) \
324 (i32_load8_s) \
325 (i32_load8_u) \
326 (i32_load16_s) \
327 (i32_load16_u) \
328 (i64_load8_s) \
329 (i64_load8_u) \
330 (i64_load16_s) \
331 (i64_load16_u) \
332 (i64_load32_s) \
333 (i64_load32_u) \
334 (i32_store) \
335 (i64_store) \
336 (f32_store) \
337 (f64_store) \
338 (i32_store8) \
339 (i32_store16) \
340 (i64_store8) \
341 (i64_store16) \
342 (i64_store32) \
343/* 64bit OPS */ \
344 (i64_const) \
345 (f64_const) \
346/* branchtable op */ \
347 (br_table) \
348
349enum code {
351 nop_code = 0x01,
353 loop_code = 0x03,
354 if__code = 0x04,
356 end_code = 0x0B,
357 br_code = 0x0C,
361 call_code = 0x10,
363 drop_code = 0x1A,
522 error_code = 0xFF,
523}; // code
524
531
532struct instr {
533 virtual std::string to_string() { return "instr"; }
534 virtual uint16_t get_code() = 0;
535 virtual void visit( visitor_arg&& arg ) = 0;
536 virtual int skip_ahead() = 0;
537 virtual void unpack( char* opcode ) = 0;
538 virtual void pack( instruction_stream* stream ) = 0;
539 virtual bool is_kill() = 0;
540 virtual bool is_post() = 0;
541};
542
543// base injector and utility classes for encoding if we should reflect the instr to the new code block
544template <typename Mutator, typename ... Mutators>
546 static constexpr bool value = Mutator::kills || propagate_should_kill<Mutators...>::value;
547};
548template <typename Mutator>
549struct propagate_should_kill<Mutator> {
550 static constexpr bool value = Mutator::kills;
551};
552template <typename Mutator, typename ... Mutators>
554 static constexpr bool value = Mutator::post || propagate_post_injection<Mutators...>::value;
555};
556template <typename Mutator>
558 static constexpr bool value = Mutator::post;
559};
560template <typename ... Mutators>
562 bool is_post() override { return propagate_post_injection<Mutators...>::value; }
563 bool is_kill() override { return propagate_should_kill<Mutators...>::value; }
564 virtual void visit( visitor_arg&& arg ) override {
565 for ( auto m : { Mutators::accept... } ) {
566 m(this, arg);
567 }
568 }
569};
570
571// construct the instructions
572BOOST_PP_SEQ_FOR_EACH( CONSTRUCT_OP_HAS_DATA, voidtype, BOOST_PP_SEQ_SUBSEQ( WASM_OP_SEQ, 0, 133 ) )
573BOOST_PP_SEQ_FOR_EACH( CONSTRUCT_OP_HAS_DATA, blocktype, BOOST_PP_SEQ_SUBSEQ( WASM_OP_SEQ, 133, 3 ) )
574BOOST_PP_SEQ_FOR_EACH( CONSTRUCT_OP_HAS_DATA, uint32_t, BOOST_PP_SEQ_SUBSEQ( WASM_OP_SEQ, 136, 11 ) )
575BOOST_PP_SEQ_FOR_EACH( CONSTRUCT_OP_HAS_DATA, memarg, BOOST_PP_SEQ_SUBSEQ( WASM_OP_SEQ, 147, 23 ) )
576BOOST_PP_SEQ_FOR_EACH( CONSTRUCT_OP_HAS_DATA, uint64_t, BOOST_PP_SEQ_SUBSEQ( WASM_OP_SEQ, 170, 2 ) )
577BOOST_PP_SEQ_FOR_EACH( CONSTRUCT_OP_HAS_DATA, branchtabletype, BOOST_PP_SEQ_SUBSEQ( WASM_OP_SEQ, 172, 1 ) )
578#undef CONSTRUCT_OP_HAS_DATA
579
580#pragma pack (pop)
581
583 static constexpr bool kills = false;
584 static constexpr bool post = false;
585 static void accept( instr* inst, visitor_arg& arg ) {}
586};
587
588// class to inherit from to attach specific mutators and have default behavior for all specified types
589template < typename Mutator = nop_mutator, typename ... Mutators>
590struct op_types {
591#define GEN_TYPE( r, T, OP ) \
592 using BOOST_PP_CAT( OP, _t ) = OP < T , BOOST_PP_CAT(T, s) ...>;
593 BOOST_PP_SEQ_FOR_EACH( GEN_TYPE, Mutator, WASM_OP_SEQ )
594#undef GEN_TYPE
595}; // op_types
596
597
601template <class Op_Types>
603#define GEN_FIELD( r, P, OP ) \
604 static std::unique_ptr<typename Op_Types::BOOST_PP_CAT(OP,_t)> BOOST_PP_CAT(P, OP);
605 BOOST_PP_SEQ_FOR_EACH( GEN_FIELD, cached_, WASM_OP_SEQ )
606#undef GEN_FIELD
607
608 static std::vector<instr*> _cached_ops;
609 public:
610 static std::vector<instr*>* get_cached_ops() {
611#define PUSH_BACK_OP( r, T, OP ) \
612 _cached_ops[BOOST_PP_CAT(OP,_code)] = BOOST_PP_CAT(T, OP).get();
613 if ( _cached_ops.empty() ) {
614 // prefill with error
615 _cached_ops.resize( 256, cached_error.get() );
616 BOOST_PP_SEQ_FOR_EACH( PUSH_BACK_OP, cached_ , WASM_OP_SEQ )
617 }
618#undef PUSH_BACK_OP
619 return &_cached_ops;
620 }
621};
622
623template <class Op_Types>
624std::vector<instr*> cached_ops<Op_Types>::_cached_ops;
625
626#define INIT_FIELD( r, P, OP ) \
627 template <class Op_Types> \
628 std::unique_ptr<typename Op_Types::BOOST_PP_CAT(OP,_t)> cached_ops<Op_Types>::BOOST_PP_CAT(P, OP) = std::make_unique<typename Op_Types::BOOST_PP_CAT(OP,_t)>();
629 BOOST_PP_SEQ_FOR_EACH( INIT_FIELD, cached_, WASM_OP_SEQ )
630
631template <class Op_Types>
632std::vector<instr*>* get_cached_ops_vec() {
633 #define GEN_FIELD( r, P, OP ) \
634 static std::unique_ptr<typename Op_Types::BOOST_PP_CAT(OP,_t)> BOOST_PP_CAT(P, OP) = std::make_unique<typename Op_Types::BOOST_PP_CAT(OP,_t)>();
635 BOOST_PP_SEQ_FOR_EACH( GEN_FIELD, cached_, WASM_OP_SEQ )
636 #undef GEN_FIELD
637 static std::vector<instr*> _cached_ops;
638
639#define PUSH_BACK_OP( r, T, OP ) \
640 _cached_ops[BOOST_PP_CAT(OP,_code)] = BOOST_PP_CAT(T, OP).get();
641
642 if ( _cached_ops.empty() ) {
643 // prefill with error
644 _cached_ops.resize( 256, cached_error.get() );
645 BOOST_PP_SEQ_FOR_EACH( PUSH_BACK_OP, cached_ , WASM_OP_SEQ )
646 }
647#undef PUSH_BACK_OP
648 return &_cached_ops;
649}
650using namespace IR;
651
652// Decodes an operator from an input stream and dispatches by opcode.
653// This code is from wasm-jit/Include/IR/Operators.h
654template <class Op_Types>
656{
657 SYSIO_OperatorDecoderStream(const std::vector<U8>& codeBytes)
658 : start(codeBytes.data()), nextByte(codeBytes.data()), end(codeBytes.data()+codeBytes.size()) {
659 if(!_cached_ops)
661 }
662
663 operator bool() const { return nextByte < end; }
664
666 SYS_ASSERT(nextByte + sizeof(IR::Opcode) <= end, wasm_exception, "");
667 IR::Opcode opcode = *(IR::Opcode*)nextByte;
668 switch(opcode)
669 {
670 #define VISIT_OPCODE(opcode,name,nameString,Imm,...) \
671 case IR::Opcode::name: \
672 { \
673 SYS_ASSERT(nextByte + sizeof(IR::OpcodeAndImm<IR::Imm>) <= end, wasm_exception, ""); \
674 IR::OpcodeAndImm<IR::Imm>* encodedOperator = (IR::OpcodeAndImm<IR::Imm>*)nextByte; \
675 nextByte += sizeof(IR::OpcodeAndImm<IR::Imm>); \
676 auto op = _cached_ops->at(BOOST_PP_CAT(name, _code)); \
677 op->unpack( reinterpret_cast<char*>(&(encodedOperator->imm)) ); \
678 return op; \
679 }
681 #undef VISIT_OPCODE
682 default:
683 nextByte += sizeof(IR::Opcode);
684 return _cached_ops->at(error_code);
685 }
686 }
687
689 const U8* savedNextByte = nextByte;
690 instr* result = decodeOp();
691 nextByte = savedNextByte;
692 return result;
693 }
694 inline uint32_t index() { return nextByte - start; }
695private:
696 // cached ops to take the address of
697 static const std::vector<instr*>* _cached_ops;
698 const U8* start;
699 const U8* nextByte;
700 const U8* end;
701};
702
703template <class Op_Types>
704const std::vector<instr*>* SYSIO_OperatorDecoderStream<Op_Types>::_cached_ops;
705
706}}} // namespace sysio, chain, wasm_ops
707
708FC_REFLECT_TEMPLATE( (typename T), sysio::chain::wasm_ops::block< T >, (code)(rt) )
uint8_t U8
Definition BasicTypes.h:5
#define ENUM_OPERATORS(visitOp)
Definition Operators.h:571
#define SYS_ASSERT(expr, exc_type, FORMAT,...)
Definition exceptions.hpp:7
static std::vector< instr * > * get_cached_ops()
Defines exception's used by fc.
ehm field
Opcode
Definition Operators.h:576
VISIT_OPCODE(opcode, name,...)
namespace sysio::chain
Definition authority.cpp:3
std::unique_ptr< instr > wasm_op_ptr
void pack(instruction_stream *stream, uint32_t field)
std::vector< uint8_t >::iterator code_iterator
std::function< std::vector< wasm_instr_ptr >(uint8_t)> wasm_instr_callback
std::string to_string(uint32_t field)
std::vector< instr * > * get_cached_ops_vec()
std::function< wasm_instr_ptr(std::vector< uint8_t >, size_t)> wasm_op_generator
std::shared_ptr< instr > wasm_instr_ptr
std::vector< uint8_t > code_vector
std::vector< uint8_t > wasm_return_t
key Invalid authority Invalid transaction Invalid block ID Invalid packed transaction Invalid chain ID Invalid symbol Signature type is not a currently activated type Block can not be found Unlinkable block Block does not guarantee concurrent execution without conflicts Block exhausted allowed resources Block is from the future Block is not signed by expected producer Block includes an ill formed protocol feature activation extension Block includes an ill formed additional block signature extension Error decompressing transaction Transaction should have at least one required authority Expired Transaction Invalid Reference Block Duplicate deferred transaction The transaction can not be found Transaction is too big Invalid transaction extension Transaction includes disallowed Transaction exceeded transient resource limit Account name already exists sysio_assert_message assertion failure Action can not be found Attempt to use unaccessible API Inline Action exceeds maximum size limit sysio_assert_code assertion failure uses restricted error code value action return value size too big Permission Query Exception Contract Table Query Exception Database is an unknown or unsupported version Database usage is at unsafe levels wasm_exception
#define value
Definition pkcs11.h:157
const GenericPointer< typename T::ValueType > T2 T::AllocatorType & a
Definition pointer.h:1181
#define T(meth, val, expected)
#define FC_REFLECT_TEMPLATE(TEMPLATE_ARGS, TYPE, MEMBERS)
Definition reflect.hpp:314
#define FC_REFLECT(TYPE, MEMBERS)
Specializes fc::reflector for TYPE.
Definition reflect.hpp:311
unsigned short uint16_t
Definition stdint.h:125
unsigned int uint32_t
Definition stdint.h:126
unsigned char uint8_t
Definition stdint.h:124
unsigned __int64 uint64_t
Definition stdint.h:136
SYSIO_OperatorDecoderStream(const std::vector< U8 > &codeBytes)
static void pack(instruction_stream *stream, voidtype &f)
static void pack(instruction_stream *stream, Field &f)
virtual void visit(visitor_arg &&arg) override
virtual void pack(instruction_stream *stream)=0
virtual void visit(visitor_arg &&arg)=0
virtual uint16_t get_code()=0
virtual void unpack(char *opcode)=0
static void accept(instr *inst, visitor_arg &arg)
static constexpr bool value
#define CONSTRUCT_OP_HAS_DATA(r, DATA, OP)
#define INIT_FIELD(r, P, OP)
#define GEN_FIELD(r, P, OP)
#define PUSH_BACK_OP(r, T, OP)
#define WASM_OP_SEQ
#define GEN_TYPE(r, T, OP)
CK_RV ret
memcpy((char *) pInfo->slotDescription, s, l)