summaryrefslogtreecommitdiff
path: root/weerun_jit.hpp
blob: 509f02cd2af3723a946669487f0c06108fd2d4e9 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
#pragma once

#include <array>
#include <map>
#include <utility>
#include <vector>

#include "weerun.hpp"

#include <xbyak/xbyak.h>
#include <xbyak/xbyak_bin2hex.h>

namespace reg {
enum reg {
  rax = 0,
  rdx = 1,
  rcx = 2,
  rdi = 3,
  rsi = 4,
  r8 = 5,
  r9 = 6,
  r10 = 7,
  r11 = 8,
  // callee-saved:
  rbx = 9,
  r12 = 10,
  r13 = 11,
  r14 = 12,
  r15 = 13,

  xmm0 = 14,
  xmm1 = 15,
  xmm2 = 16,
  xmm3 = 17,
  xmm4 = 18,
  xmm5 = 19,
  xmm6 = 20,
  xmm7 = 21,
  xmm8 = 22,
  xmm9 = 23,
  xmm10 = 24,
  xmm11 = 25,
  xmm12 = 26,
  xmm13 = 27,
  xmm14 = 28,
  xmm15 = 29,

  num_r = 30
};

const int32_t in_reg = 0;
const int32_t on_stack = 1;
}  // namespace reg

struct wasm_jit_loc_t {
  int32_t type;   // 0:in register, 1:on stack
  int32_t index;  // register index or [rbp-8*index]

  bool operator==(const wasm_jit_loc_t& rhs) {
    return type == rhs.type && index == rhs.index;
  }
};

enum class wasm_jit_type_t : int32_t {
  INVALID,
  CALLER_DATA,
  INTERMEDIATE,
  LOCAL_VAL,  // index = local variable index
  STACK_VAL,  // index = stack index
  GLOBAL_BASE,
  MEMORY_BASE,
  MEMORY_SIZE,
};

struct wasm_jit_val_t {
  wasm_jit_type_t type;
  int32_t index;

  bool operator==(const wasm_jit_val_t& rhs) {
    return type == rhs.type && index == rhs.index;
  }
  bool operator!=(const wasm_jit_val_t& rhs) {
    return type != rhs.type || index != rhs.index;
  }
};

struct wasm_jit_ctx_t {
  std::array<wasm_jit_val_t, 30> reg_;  // reg content
  std::array<uint32_t, 30> reg_age_;    // reg content
  uint32_t age_;
  std::vector<uint8_t> stack_valid_;  // stack valid?

  std::vector<wasm_jit_loc_t> stack_loc_;  // location of wasm stack values
  std::vector<wasm_jit_loc_t> local_loc_;  // location of wasm local values

  void init(wasm_jit_t* jit, wasm_func_t* f);
};

struct wasm_jit_t : public Xbyak::CodeGenerator {
  wasm_module_t* mod_;
  wasm_instance_t* ins_;
  int max_prologue_size_;
  bool enable_check_;
  std::vector<uint8_t*> f_addr_;
  uint8_t* run_fn_;  // call helper function

  // current function info
  wasm_func_t* f_;
  uint32_t funcidx_;
  uint32_t num_locals_;
  std::vector<uint8_t> local_types_;
  uint32_t rsp_sub_;
  std::array<uint8_t, 5> used_callee_;

  // decoder info
  uint32_t opcode_;
  buffer_t buf_;
  uint32_t next_bi_;
  uint32_t stop_;

  // register allocation context
  wasm_jit_ctx_t ctx_;

  // branch info
  struct block_t {
    uint32_t loop;
    uint32_t index;
    wasm_jit_ctx_t saved_ctx;  // for loop, we need to save context early
    std::vector<wasm_jit_ctx_t> br_ctxs;  // brs' cpntext
  };
  std::vector<block_t> block_stack_;

  wasm_jit_t(uint64_t code_size);

  void compile_instance(wasm_instance_t* ins);
  void emit_runtime_functions();
  void compile_function(uint32_t index);
  void run_fn(uint32_t index, wasm_value_t* args, wasm_value_t* r);

  Xbyak::Reg64 to_xbyak_r64(int32_t index);
  Xbyak::Reg32 to_xbyak_r32(int32_t index);
  Xbyak::Reg16 to_xbyak_r16(int32_t index);
  Xbyak::Reg8 to_xbyak_r8(int32_t index);
  Xbyak::Xmm to_xbyak_xmm(int32_t index);

  int32_t alloc_r(wasm_jit_val_t usage, uint8_t type);
  void acquire_r(int32_t index, wasm_jit_val_t usage);
  void use_r(int32_t index, wasm_jit_val_t usage);
  void release_r(int32_t index);  // mark a register as invalid

  int32_t alloc_stk();
  void release_stk(int32_t index);
  void acquire_stk(int32_t index);

  inline wasm_jit_loc_t peek_wasm_stk(uint32_t depth);
  inline wasm_jit_loc_t pop_wasm_stk();

  int32_t pop_r(uint8_t type);              // pop to a 32bit register
  void pop_r(int32_t index, uint8_t type);  // pop to the given 32bit register
  void push_r(int32_t index);               // push a register

  void restore_state(wasm_jit_ctx_t* from, wasm_jit_ctx_t* to);

  typedef void (CodeGenerator::*oo_op_t)(const Xbyak::Operand&,
                                         const Xbyak::Operand&);
  typedef void (CodeGenerator::*or8_op_t)(const Xbyak::Operand&,
                                          const Xbyak::Reg8&);
  typedef void (CodeGenerator::*o_op_t)(const Xbyak::Operand&);
  typedef void (CodeGenerator::*xo_op_t)(const Xbyak::Xmm&,
                                         const Xbyak::Operand&);

  int32_t pop_binop_r32_r32(oo_op_t binop);
  int32_t pop_binop_xmm_xmm(xo_op_t binop);

  void rbp_load_r(int32_t index, int32_t r, uint8_t type);
  void rbp_save_r(int32_t index, int32_t r);

  int32_t alloc_special(wasm_jit_type_t t,
                        size_t off);  // load global base to a register
  int32_t alloc_global_base();        // load global base to a register
  int32_t alloc_memory_base();        // load memory base to a register
  int32_t alloc_memory_size();        // load memory size to a register

  void emit_lea_r_rip(int32_t r, size_t dest_offset);
  void emit_mov_r_rip(int32_t r, size_t dest_offset);
  void emit_call_internal(wasm_func_decl_t* sig, int is_direct, int index);
  void emit_pop_result();

  void emit_unreachable();
  void emit_nop();
  void emit_drop();
  void emit_select();
  void emit_local_get();
  void emit_local_set();
  void emit_local_tee();
  void emit_global_get();
  void emit_global_set();
  void emit_i32_load();
  void emit_f64_load();
  void emit_i32_store();
  void emit_f64_store();
  void emit_i32_const();
  void emit_f64_const();
  void emit_i32_numeric();
  void emit_i32_shift();
  void emit_i32_convert();
  void emit_f64_compute();
  void emit_call();
  void emit_call_indirect();
  void emit_return();
  void emit_block();
  void emit_end();
  void emit_br();
  void emit_br_if();
  void emit_br_table();
};