@aptre/v86 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (111) hide show
  1. package/LICENSE +22 -0
  2. package/LICENSE.MIT +22 -0
  3. package/Readme.md +237 -0
  4. package/dist/v86.browser.js +26666 -0
  5. package/dist/v86.browser.js.map +7 -0
  6. package/dist/v86.js +26632 -0
  7. package/dist/v86.js.map +7 -0
  8. package/gen/generate_analyzer.ts +512 -0
  9. package/gen/generate_interpreter.ts +522 -0
  10. package/gen/generate_jit.ts +624 -0
  11. package/gen/rust_ast.ts +107 -0
  12. package/gen/util.ts +35 -0
  13. package/gen/x86_table.ts +1836 -0
  14. package/lib/9p.ts +1547 -0
  15. package/lib/filesystem.ts +1879 -0
  16. package/lib/marshall.ts +168 -0
  17. package/lib/softfloat/softfloat.c +32501 -0
  18. package/lib/zstd/zstddeclib.c +13520 -0
  19. package/package.json +75 -0
  20. package/src/acpi.ts +267 -0
  21. package/src/browser/dummy_screen.ts +106 -0
  22. package/src/browser/fake_network.ts +1771 -0
  23. package/src/browser/fetch_network.ts +361 -0
  24. package/src/browser/filestorage.ts +124 -0
  25. package/src/browser/inbrowser_network.ts +57 -0
  26. package/src/browser/keyboard.ts +564 -0
  27. package/src/browser/main.ts +3415 -0
  28. package/src/browser/mouse.ts +255 -0
  29. package/src/browser/network.ts +142 -0
  30. package/src/browser/print_stats.ts +336 -0
  31. package/src/browser/screen.ts +978 -0
  32. package/src/browser/serial.ts +316 -0
  33. package/src/browser/speaker.ts +1223 -0
  34. package/src/browser/starter.ts +1688 -0
  35. package/src/browser/wisp_network.ts +332 -0
  36. package/src/browser/worker_bus.ts +64 -0
  37. package/src/buffer.ts +652 -0
  38. package/src/bus.ts +78 -0
  39. package/src/const.ts +128 -0
  40. package/src/cpu.ts +2891 -0
  41. package/src/dma.ts +474 -0
  42. package/src/elf.ts +251 -0
  43. package/src/floppy.ts +1778 -0
  44. package/src/ide.ts +3455 -0
  45. package/src/io.ts +504 -0
  46. package/src/iso9660.ts +317 -0
  47. package/src/kernel.ts +250 -0
  48. package/src/lib.ts +645 -0
  49. package/src/log.ts +149 -0
  50. package/src/main.ts +199 -0
  51. package/src/ne2k.ts +1589 -0
  52. package/src/pci.ts +815 -0
  53. package/src/pit.ts +406 -0
  54. package/src/ps2.ts +820 -0
  55. package/src/rtc.ts +537 -0
  56. package/src/rust/analysis.rs +101 -0
  57. package/src/rust/codegen.rs +2660 -0
  58. package/src/rust/config.rs +3 -0
  59. package/src/rust/control_flow.rs +425 -0
  60. package/src/rust/cpu/apic.rs +658 -0
  61. package/src/rust/cpu/arith.rs +1207 -0
  62. package/src/rust/cpu/call_indirect.rs +2 -0
  63. package/src/rust/cpu/cpu.rs +4501 -0
  64. package/src/rust/cpu/fpu.rs +923 -0
  65. package/src/rust/cpu/global_pointers.rs +112 -0
  66. package/src/rust/cpu/instructions.rs +2486 -0
  67. package/src/rust/cpu/instructions_0f.rs +5261 -0
  68. package/src/rust/cpu/ioapic.rs +316 -0
  69. package/src/rust/cpu/memory.rs +351 -0
  70. package/src/rust/cpu/misc_instr.rs +613 -0
  71. package/src/rust/cpu/mod.rs +16 -0
  72. package/src/rust/cpu/modrm.rs +133 -0
  73. package/src/rust/cpu/pic.rs +402 -0
  74. package/src/rust/cpu/sse_instr.rs +361 -0
  75. package/src/rust/cpu/string.rs +701 -0
  76. package/src/rust/cpu/vga.rs +175 -0
  77. package/src/rust/cpu_context.rs +69 -0
  78. package/src/rust/dbg.rs +98 -0
  79. package/src/rust/gen/analyzer.rs +3807 -0
  80. package/src/rust/gen/analyzer0f.rs +3992 -0
  81. package/src/rust/gen/interpreter.rs +4447 -0
  82. package/src/rust/gen/interpreter0f.rs +5404 -0
  83. package/src/rust/gen/jit.rs +5080 -0
  84. package/src/rust/gen/jit0f.rs +5547 -0
  85. package/src/rust/gen/mod.rs +14 -0
  86. package/src/rust/jit.rs +2443 -0
  87. package/src/rust/jit_instructions.rs +7881 -0
  88. package/src/rust/js_api.rs +6 -0
  89. package/src/rust/leb.rs +46 -0
  90. package/src/rust/lib.rs +29 -0
  91. package/src/rust/modrm.rs +330 -0
  92. package/src/rust/opstats.rs +249 -0
  93. package/src/rust/page.rs +15 -0
  94. package/src/rust/paging.rs +25 -0
  95. package/src/rust/prefix.rs +15 -0
  96. package/src/rust/profiler.rs +155 -0
  97. package/src/rust/regs.rs +38 -0
  98. package/src/rust/softfloat.rs +286 -0
  99. package/src/rust/state_flags.rs +27 -0
  100. package/src/rust/wasmgen/mod.rs +2 -0
  101. package/src/rust/wasmgen/wasm_builder.rs +1047 -0
  102. package/src/rust/wasmgen/wasm_opcodes.rs +221 -0
  103. package/src/rust/zstd.rs +105 -0
  104. package/src/sb16.ts +1928 -0
  105. package/src/state.ts +359 -0
  106. package/src/uart.ts +472 -0
  107. package/src/vga.ts +2791 -0
  108. package/src/virtio.ts +1756 -0
  109. package/src/virtio_balloon.ts +273 -0
  110. package/src/virtio_console.ts +372 -0
  111. package/src/virtio_net.ts +326 -0
@@ -0,0 +1,2660 @@
1
+ use crate::cpu::cpu::{
2
+ tlb_data, FLAG_CARRY, FLAG_OVERFLOW, FLAG_SIGN, FLAG_ZERO, OPSIZE_16, OPSIZE_32, OPSIZE_8,
3
+ TLB_GLOBAL, TLB_HAS_CODE, TLB_NO_USER, TLB_READONLY, TLB_VALID,
4
+ };
5
+ use crate::cpu::global_pointers;
6
+ use crate::cpu::memory;
7
+ use crate::jit::{Instruction, InstructionOperand, InstructionOperandDest, JitContext};
8
+ use crate::modrm;
9
+ use crate::modrm::ModrmByte;
10
+ use crate::opstats;
11
+ use crate::profiler;
12
+ use crate::regs;
13
+ use crate::wasmgen::wasm_builder::{WasmBuilder, WasmLocal, WasmLocalI64};
14
+
15
+ pub fn gen_add_cs_offset(ctx: &mut JitContext) {
16
+ if !ctx.cpu.has_flat_segmentation() {
17
+ ctx.builder
18
+ .load_fixed_i32(global_pointers::get_seg_offset(regs::CS));
19
+ ctx.builder.add_i32();
20
+ }
21
+ }
22
+
23
+ pub fn gen_get_eip(builder: &mut WasmBuilder) {
24
+ builder.load_fixed_i32(global_pointers::instruction_pointer as u32);
25
+ }
26
+
27
+ pub fn gen_set_eip_to_after_current_instruction(ctx: &mut JitContext) {
28
+ ctx.builder
29
+ .const_i32(global_pointers::instruction_pointer as i32);
30
+ gen_get_eip(ctx.builder);
31
+ ctx.builder.const_i32(!0xFFF);
32
+ ctx.builder.and_i32();
33
+ ctx.builder.const_i32(ctx.cpu.eip as i32 & 0xFFF);
34
+ ctx.builder.or_i32();
35
+ ctx.builder.store_aligned_i32(0);
36
+ }
37
+
38
+ pub fn gen_set_previous_eip_offset_from_eip_with_low_bits(
39
+ builder: &mut WasmBuilder,
40
+ low_bits: i32,
41
+ ) {
42
+ // previous_ip = instruction_pointer & ~0xFFF | low_bits;
43
+ dbg_assert!(low_bits & !0xFFF == 0);
44
+ builder.const_i32(global_pointers::previous_ip as i32);
45
+ gen_get_eip(builder);
46
+ builder.const_i32(!0xFFF);
47
+ builder.and_i32();
48
+ builder.const_i32(low_bits);
49
+ builder.or_i32();
50
+ builder.store_aligned_i32(0);
51
+ }
52
+
53
+ pub fn gen_set_eip_low_bits(builder: &mut WasmBuilder, low_bits: i32) {
54
+ // instruction_pointer = instruction_pointer & ~0xFFF | low_bits;
55
+ dbg_assert!(low_bits & !0xFFF == 0);
56
+ builder.const_i32(global_pointers::instruction_pointer as i32);
57
+ gen_get_eip(builder);
58
+ builder.const_i32(!0xFFF);
59
+ builder.and_i32();
60
+ builder.const_i32(low_bits);
61
+ builder.or_i32();
62
+ builder.store_aligned_i32(0);
63
+ }
64
+
65
+ pub fn gen_set_eip_low_bits_and_jump_rel32(builder: &mut WasmBuilder, low_bits: i32, n: i32) {
66
+ // instruction_pointer = (instruction_pointer & ~0xFFF | low_bits) + n;
67
+ dbg_assert!(low_bits & !0xFFF == 0);
68
+ builder.const_i32(global_pointers::instruction_pointer as i32);
69
+ gen_get_eip(builder);
70
+ builder.const_i32(!0xFFF);
71
+ builder.and_i32();
72
+ builder.const_i32(low_bits);
73
+ builder.or_i32();
74
+ if n != 0 {
75
+ builder.const_i32(n);
76
+ builder.add_i32();
77
+ }
78
+ builder.store_aligned_i32(0);
79
+ }
80
+
81
+ pub fn gen_relative_jump(builder: &mut WasmBuilder, n: i32) {
82
+ // add n to instruction_pointer
83
+ if n != 0 {
84
+ builder.const_i32(global_pointers::instruction_pointer as i32);
85
+ gen_get_eip(builder);
86
+ builder.const_i32(n);
87
+ builder.add_i32();
88
+ builder.store_aligned_i32(0);
89
+ }
90
+ }
91
+
92
+ pub fn gen_page_switch_check(
93
+ ctx: &mut JitContext,
94
+ next_block_addr: u32,
95
+ last_instruction_addr: u32,
96
+ ) {
97
+ // After switching a page while in jitted code, check if the page mapping still holds
98
+
99
+ gen_get_eip(ctx.builder);
100
+ let address_local = ctx.builder.set_new_local();
101
+ gen_get_phys_eip_plus_mem(ctx, &address_local);
102
+ ctx.builder.free_local(address_local);
103
+
104
+ ctx.builder
105
+ .const_i32(next_block_addr as i32 + unsafe { memory::mem8 } as i32);
106
+ ctx.builder.ne_i32();
107
+
108
+ if cfg!(debug_assertions) {
109
+ ctx.builder.if_void();
110
+ gen_profiler_stat_increment(ctx.builder, profiler::stat::FAILED_PAGE_CHANGE);
111
+ gen_debug_track_jit_exit(ctx.builder, last_instruction_addr);
112
+ ctx.builder.br(ctx.exit_label);
113
+ ctx.builder.block_end();
114
+ }
115
+ else {
116
+ ctx.builder.br_if(ctx.exit_label);
117
+ }
118
+ }
119
+
120
+ pub fn gen_update_instruction_counter(ctx: &mut JitContext) {
121
+ ctx.builder
122
+ .const_i32(global_pointers::instruction_counter as i32);
123
+ ctx.builder
124
+ .load_fixed_i32(global_pointers::instruction_counter as u32);
125
+ ctx.builder.get_local(&ctx.instruction_counter);
126
+ ctx.builder.add_i32();
127
+ ctx.builder.store_aligned_i32(0);
128
+ }
129
+
130
+ pub fn gen_get_reg8(ctx: &mut JitContext, r: u32) {
131
+ match r {
132
+ regs::AL | regs::CL | regs::DL | regs::BL => {
133
+ ctx.builder.get_local(&ctx.register_locals[r as usize]);
134
+ ctx.builder.const_i32(0xFF);
135
+ ctx.builder.and_i32();
136
+ },
137
+ regs::AH | regs::CH | regs::DH | regs::BH => {
138
+ ctx.builder
139
+ .get_local(&ctx.register_locals[(r - 4) as usize]);
140
+ ctx.builder.const_i32(8);
141
+ ctx.builder.shr_u_i32();
142
+ ctx.builder.const_i32(0xFF);
143
+ ctx.builder.and_i32();
144
+ },
145
+ _ => assert!(false),
146
+ }
147
+ }
148
+
149
+ /// Return a new local referencing one of the 8 bit registers or a direct reference to one of the
150
+ /// register locals. Higher bits might be garbage (suitable for gen_cmp8 etc.). Must be freed with
151
+ /// gen_free_reg8_or_alias.
152
+ pub fn gen_get_reg8_or_alias_to_reg32(ctx: &mut JitContext, r: u32) -> WasmLocal {
153
+ match r {
154
+ regs::AL | regs::CL | regs::DL | regs::BL => ctx.register_locals[r as usize].unsafe_clone(),
155
+ regs::AH | regs::CH | regs::DH | regs::BH => {
156
+ ctx.builder
157
+ .get_local(&ctx.register_locals[(r - 4) as usize]);
158
+ ctx.builder.const_i32(8);
159
+ ctx.builder.shr_u_i32();
160
+ ctx.builder.set_new_local()
161
+ },
162
+ _ => panic!(),
163
+ }
164
+ }
165
+
166
+ pub fn gen_free_reg8_or_alias(ctx: &mut JitContext, r: u32, local: WasmLocal) {
167
+ match r {
168
+ regs::AL | regs::CL | regs::DL | regs::BL => {},
169
+ regs::AH | regs::CH | regs::DH | regs::BH => ctx.builder.free_local(local),
170
+ _ => panic!(),
171
+ }
172
+ }
173
+
174
+ pub fn gen_get_reg16(ctx: &mut JitContext, r: u32) {
175
+ ctx.builder.get_local(&ctx.register_locals[r as usize]);
176
+ ctx.builder.const_i32(0xFFFF);
177
+ ctx.builder.and_i32();
178
+ }
179
+
180
+ pub fn gen_get_reg32(ctx: &mut JitContext, r: u32) {
181
+ ctx.builder.get_local(&ctx.register_locals[r as usize]);
182
+ }
183
+
184
+ pub fn gen_set_reg8(ctx: &mut JitContext, r: u32) {
185
+ match r {
186
+ regs::AL | regs::CL | regs::DL | regs::BL => {
187
+ // reg32[r] = stack_value & 0xFF | reg32[r] & ~0xFF
188
+ ctx.builder.const_i32(0xFF);
189
+ ctx.builder.and_i32();
190
+
191
+ ctx.builder.get_local(&ctx.register_locals[r as usize]);
192
+ ctx.builder.const_i32(!0xFF);
193
+ ctx.builder.and_i32();
194
+
195
+ ctx.builder.or_i32();
196
+ ctx.builder.set_local(&ctx.register_locals[r as usize]);
197
+ },
198
+ regs::AH | regs::CH | regs::DH | regs::BH => {
199
+ // reg32[r] = stack_value << 8 & 0xFF00 | reg32[r] & ~0xFF00
200
+ ctx.builder.const_i32(8);
201
+ ctx.builder.shl_i32();
202
+ ctx.builder.const_i32(0xFF00);
203
+ ctx.builder.and_i32();
204
+
205
+ ctx.builder
206
+ .get_local(&ctx.register_locals[(r - 4) as usize]);
207
+ ctx.builder.const_i32(!0xFF00);
208
+ ctx.builder.and_i32();
209
+
210
+ ctx.builder.or_i32();
211
+ ctx.builder
212
+ .set_local(&ctx.register_locals[(r - 4) as usize]);
213
+ },
214
+ _ => assert!(false),
215
+ }
216
+ }
217
+
218
+ pub fn gen_set_reg8_unmasked(ctx: &mut JitContext, r: u32) {
219
+ if cfg!(debug_assertions) {
220
+ let val = ctx.builder.set_new_local();
221
+ ctx.builder.get_local(&val);
222
+ ctx.builder.const_i32(!0xFF);
223
+ ctx.builder.and_i32();
224
+ ctx.builder.if_void();
225
+ ctx.builder.unreachable();
226
+ ctx.builder.block_end();
227
+ ctx.builder.get_local(&val);
228
+ ctx.builder.free_local(val);
229
+ }
230
+
231
+ match r {
232
+ regs::AL | regs::CL | regs::DL | regs::BL => {
233
+ // reg32[r] = stack_value | reg32[r] & ~0xFF
234
+ ctx.builder.get_local(&ctx.register_locals[r as usize]);
235
+ ctx.builder.const_i32(!0xFF);
236
+ ctx.builder.and_i32();
237
+
238
+ ctx.builder.or_i32();
239
+ ctx.builder.set_local(&ctx.register_locals[r as usize]);
240
+ },
241
+ regs::AH | regs::CH | regs::DH | regs::BH => {
242
+ // reg32[r] = stack_value << 8 | reg32[r] & ~0xFF00
243
+ ctx.builder.const_i32(8);
244
+ ctx.builder.shl_i32();
245
+ ctx.builder.const_i32(0xFF00);
246
+ ctx.builder.and_i32();
247
+
248
+ ctx.builder
249
+ .get_local(&ctx.register_locals[(r - 4) as usize]);
250
+ ctx.builder.const_i32(!0xFF00);
251
+ ctx.builder.and_i32();
252
+
253
+ ctx.builder.or_i32();
254
+ ctx.builder
255
+ .set_local(&ctx.register_locals[(r - 4) as usize]);
256
+ },
257
+ _ => assert!(false),
258
+ }
259
+ }
260
+
261
+ pub fn gen_set_reg16(ctx: &mut JitContext, r: u32) {
262
+ gen_set_reg16_local(ctx.builder, &ctx.register_locals[r as usize]);
263
+ }
264
+
265
+ pub fn gen_set_reg16_unmasked(ctx: &mut JitContext, r: u32) {
266
+ if cfg!(debug_assertions) {
267
+ let val = ctx.builder.set_new_local();
268
+ ctx.builder.get_local(&val);
269
+ ctx.builder.const_i32(!0xFFFF);
270
+ ctx.builder.and_i32();
271
+ ctx.builder.if_void();
272
+ ctx.builder.unreachable();
273
+ ctx.builder.block_end();
274
+ ctx.builder.get_local(&val);
275
+ ctx.builder.free_local(val);
276
+ }
277
+
278
+ ctx.builder.get_local(&ctx.reg(r));
279
+ ctx.builder.const_i32(!0xFFFF);
280
+ ctx.builder.and_i32();
281
+ ctx.builder.or_i32();
282
+ ctx.builder.set_local(&ctx.reg(r));
283
+ }
284
+
285
+ pub fn gen_set_reg16_local(builder: &mut WasmBuilder, local: &WasmLocal) {
286
+ // reg32[r] = v & 0xFFFF | reg32[r] & ~0xFFFF
287
+ builder.const_i32(0xFFFF);
288
+ builder.and_i32();
289
+ builder.get_local(local);
290
+ builder.const_i32(!0xFFFF);
291
+ builder.and_i32();
292
+ builder.or_i32();
293
+ builder.set_local(local);
294
+ }
295
+
296
+ pub fn gen_set_reg32(ctx: &mut JitContext, r: u32) {
297
+ ctx.builder.set_local(&ctx.register_locals[r as usize]);
298
+ }
299
+
300
+ pub fn decr_exc_asize(ctx: &mut JitContext) {
301
+ gen_get_reg32(ctx, regs::ECX);
302
+ ctx.builder.const_i32(1);
303
+ ctx.builder.sub_i32();
304
+ if ctx.cpu.asize_32() {
305
+ gen_set_reg32(ctx, regs::ECX);
306
+ }
307
+ else {
308
+ gen_set_reg16(ctx, regs::CX);
309
+ }
310
+ }
311
+
312
+ pub fn gen_read_reg_xmm128_into_scratch(ctx: &mut JitContext, r: u32) {
313
+ ctx.builder
314
+ .const_i32(global_pointers::sse_scratch_register as i32);
315
+ let dest = global_pointers::get_reg_xmm_offset(r);
316
+ ctx.builder.const_i32(dest as i32);
317
+ ctx.builder.load_aligned_i64(0);
318
+ ctx.builder.store_aligned_i64(0);
319
+
320
+ ctx.builder
321
+ .const_i32(global_pointers::sse_scratch_register as i32 + 8);
322
+ let dest = global_pointers::get_reg_xmm_offset(r) + 8;
323
+ ctx.builder.const_i32(dest as i32);
324
+ ctx.builder.load_aligned_i64(0);
325
+ ctx.builder.store_aligned_i64(0);
326
+ }
327
+
328
+ pub fn gen_get_sreg(ctx: &mut JitContext, r: u32) {
329
+ ctx.builder
330
+ .load_fixed_u16(global_pointers::get_sreg_offset(r))
331
+ }
332
+
333
+ pub fn gen_get_ss_offset(ctx: &mut JitContext) {
334
+ ctx.builder
335
+ .load_fixed_i32(global_pointers::get_seg_offset(regs::SS));
336
+ }
337
+
338
+ pub fn gen_get_flags(builder: &mut WasmBuilder) {
339
+ builder.load_fixed_i32(global_pointers::flags as u32);
340
+ }
341
+ fn gen_get_flags_changed(builder: &mut WasmBuilder) {
342
+ builder.load_fixed_i32(global_pointers::flags_changed as u32);
343
+ }
344
+ fn gen_get_last_result(builder: &mut WasmBuilder, previous_instruction: &Instruction) {
345
+ match previous_instruction {
346
+ Instruction::Add {
347
+ dest: InstructionOperandDest::WasmLocal(l),
348
+ opsize: OPSIZE_32,
349
+ ..
350
+ }
351
+ | Instruction::AdcSbb {
352
+ dest: InstructionOperandDest::WasmLocal(l),
353
+ opsize: OPSIZE_32,
354
+ ..
355
+ }
356
+ | Instruction::Sub {
357
+ dest: InstructionOperandDest::WasmLocal(l),
358
+ opsize: OPSIZE_32,
359
+ ..
360
+ }
361
+ | Instruction::Bitwise {
362
+ dest: InstructionOperandDest::WasmLocal(l),
363
+ opsize: OPSIZE_32,
364
+ }
365
+ | Instruction::NonZeroShift {
366
+ dest: InstructionOperandDest::WasmLocal(l),
367
+ opsize: OPSIZE_32,
368
+ } => builder.get_local(&l),
369
+ Instruction::Cmp {
370
+ dest: InstructionOperandDest::WasmLocal(l),
371
+ source,
372
+ opsize: OPSIZE_32,
373
+ } => {
374
+ if source.is_zero() {
375
+ builder.get_local(&l)
376
+ }
377
+ else {
378
+ builder.load_fixed_i32(global_pointers::last_result as u32)
379
+ }
380
+ },
381
+ _ => builder.load_fixed_i32(global_pointers::last_result as u32),
382
+ }
383
+ }
384
+ fn gen_get_last_op_size(builder: &mut WasmBuilder) {
385
+ builder.load_fixed_i32(global_pointers::last_op_size as u32);
386
+ }
387
+ fn gen_get_last_op1(builder: &mut WasmBuilder, previous_instruction: &Instruction) {
388
+ match previous_instruction {
389
+ Instruction::Cmp {
390
+ dest: InstructionOperandDest::WasmLocal(l),
391
+ source: _,
392
+ opsize: OPSIZE_32,
393
+ } => builder.get_local(&l),
394
+ _ => builder.load_fixed_i32(global_pointers::last_op1 as u32),
395
+ }
396
+ }
397
+
398
+ pub fn gen_get_page_fault(builder: &mut WasmBuilder) {
399
+ builder.load_fixed_u8(global_pointers::page_fault as u32);
400
+ }
401
+
402
+ /// sign-extend a byte value on the stack and leave it on the stack
403
+ pub fn sign_extend_i8(builder: &mut WasmBuilder) {
404
+ builder.const_i32(24);
405
+ builder.shl_i32();
406
+ builder.const_i32(24);
407
+ builder.shr_s_i32();
408
+ }
409
+
410
+ /// sign-extend a two byte value on the stack and leave it on the stack
411
+ pub fn sign_extend_i16(builder: &mut WasmBuilder) {
412
+ builder.const_i32(16);
413
+ builder.shl_i32();
414
+ builder.const_i32(16);
415
+ builder.shr_s_i32();
416
+ }
417
+
418
+ pub fn gen_fn0_const(builder: &mut WasmBuilder, name: &str) { builder.call_fn0(name) }
419
+ pub fn gen_fn1_const(builder: &mut WasmBuilder, name: &str, arg0: u32) {
420
+ builder.const_i32(arg0 as i32);
421
+ builder.call_fn1(name);
422
+ }
423
+ pub fn gen_fn2_const(builder: &mut WasmBuilder, name: &str, arg0: u32, arg1: u32) {
424
+ builder.const_i32(arg0 as i32);
425
+ builder.const_i32(arg1 as i32);
426
+ builder.call_fn2(name);
427
+ }
428
+
429
+ // helper functions for gen/generate_jit.js
430
+ pub fn gen_modrm_fn0(builder: &mut WasmBuilder, name: &str) {
431
+ // generates: fn( _ )
432
+ builder.call_fn1(name);
433
+ }
434
+ pub fn gen_modrm_fn1(builder: &mut WasmBuilder, name: &str, arg0: u32) {
435
+ // generates: fn( _, arg0 )
436
+ builder.const_i32(arg0 as i32);
437
+ builder.call_fn2(name);
438
+ }
439
+
440
+ pub fn gen_modrm_resolve(ctx: &mut JitContext, modrm_byte: ModrmByte) {
441
+ modrm::gen(ctx, modrm_byte, 0)
442
+ }
443
+ pub fn gen_modrm_resolve_with_local(
444
+ ctx: &mut JitContext,
445
+ modrm_byte: ModrmByte,
446
+ gen: &dyn Fn(&mut JitContext, &WasmLocal),
447
+ ) {
448
+ if let Some(r) = modrm::get_as_reg_index_if_possible(ctx, &modrm_byte) {
449
+ gen(ctx, &ctx.reg(r));
450
+ }
451
+ else {
452
+ gen_modrm_resolve(ctx, modrm_byte);
453
+ let address = ctx.builder.set_new_local();
454
+ gen(ctx, &address);
455
+ ctx.builder.free_local(address);
456
+ }
457
+ }
458
+ pub fn gen_modrm_resolve_with_esp_offset(
459
+ ctx: &mut JitContext,
460
+ modrm_byte: ModrmByte,
461
+ esp_offset: i32,
462
+ ) {
463
+ modrm::gen(ctx, modrm_byte, esp_offset)
464
+ }
465
+
466
+ pub fn gen_set_reg8_r(ctx: &mut JitContext, dest: u32, src: u32) {
467
+ // generates: reg8[r_dest] = reg8[r_src]
468
+ if src != dest {
469
+ gen_get_reg8(ctx, src);
470
+ gen_set_reg8_unmasked(ctx, dest);
471
+ }
472
+ }
473
+ pub fn gen_set_reg16_r(ctx: &mut JitContext, dest: u32, src: u32) {
474
+ // generates: reg16[r_dest] = reg16[r_src]
475
+ if src != dest {
476
+ gen_get_reg16(ctx, src);
477
+ gen_set_reg16_unmasked(ctx, dest);
478
+ }
479
+ }
480
+ pub fn gen_set_reg32_r(ctx: &mut JitContext, dest: u32, src: u32) {
481
+ // generates: reg32[r_dest] = reg32[r_src]
482
+ if src != dest {
483
+ gen_get_reg32(ctx, src);
484
+ gen_set_reg32(ctx, dest);
485
+ }
486
+ }
487
+
488
+ pub fn gen_modrm_resolve_safe_read8(ctx: &mut JitContext, modrm_byte: ModrmByte) {
489
+ gen_modrm_resolve_with_local(ctx, modrm_byte, &|ctx, addr| gen_safe_read8(ctx, addr));
490
+ }
491
+ pub fn gen_modrm_resolve_safe_read16(ctx: &mut JitContext, modrm_byte: ModrmByte) {
492
+ gen_modrm_resolve_with_local(ctx, modrm_byte, &|ctx, addr| gen_safe_read16(ctx, addr));
493
+ }
494
+ pub fn gen_modrm_resolve_safe_read32(ctx: &mut JitContext, modrm_byte: ModrmByte) {
495
+ gen_modrm_resolve_with_local(ctx, modrm_byte, &|ctx, addr| gen_safe_read32(ctx, addr));
496
+ }
497
+ pub fn gen_modrm_resolve_safe_read64(ctx: &mut JitContext, modrm_byte: ModrmByte) {
498
+ gen_modrm_resolve_with_local(ctx, modrm_byte, &|ctx, addr| gen_safe_read64(ctx, addr));
499
+ }
500
+ pub fn gen_modrm_resolve_safe_read128(
501
+ ctx: &mut JitContext,
502
+ modrm_byte: ModrmByte,
503
+ where_to_write: u32,
504
+ ) {
505
+ gen_modrm_resolve_with_local(ctx, modrm_byte, &|ctx, addr| {
506
+ gen_safe_read128(ctx, addr, where_to_write)
507
+ });
508
+ }
509
+
510
+ pub fn gen_safe_read8(ctx: &mut JitContext, address_local: &WasmLocal) {
511
+ gen_safe_read(ctx, BitSize::BYTE, address_local, None);
512
+ }
513
+ pub fn gen_safe_read16(ctx: &mut JitContext, address_local: &WasmLocal) {
514
+ gen_safe_read(ctx, BitSize::WORD, address_local, None);
515
+ }
516
+ pub fn gen_safe_read32(ctx: &mut JitContext, address_local: &WasmLocal) {
517
+ gen_safe_read(ctx, BitSize::DWORD, address_local, None);
518
+ }
519
+ pub fn gen_safe_read64(ctx: &mut JitContext, address_local: &WasmLocal) {
520
+ gen_safe_read(ctx, BitSize::QWORD, &address_local, None);
521
+ }
522
+ pub fn gen_safe_read128(ctx: &mut JitContext, address_local: &WasmLocal, where_to_write: u32) {
523
+ gen_safe_read(ctx, BitSize::DQWORD, &address_local, Some(where_to_write));
524
+ }
525
+
526
+ // only used internally for gen_safe_write
527
+ enum GenSafeWriteValue<'a> {
528
+ I32(&'a WasmLocal),
529
+ I64(&'a WasmLocalI64),
530
+ TwoI64s(&'a WasmLocalI64, &'a WasmLocalI64),
531
+ }
532
+
533
+ enum GenSafeReadWriteValue {
534
+ I32(WasmLocal),
535
+ I64(WasmLocalI64),
536
+ }
537
+
538
+ #[derive(Copy, Clone, Eq, PartialEq)]
539
+ pub enum BitSize {
540
+ BYTE,
541
+ WORD,
542
+ DWORD,
543
+ QWORD,
544
+ DQWORD,
545
+ }
546
+ impl BitSize {
547
+ pub fn bytes(&self) -> u32 {
548
+ match self {
549
+ BitSize::BYTE => 1,
550
+ BitSize::WORD => 2,
551
+ BitSize::DWORD => 4,
552
+ BitSize::QWORD => 8,
553
+ BitSize::DQWORD => 16,
554
+ }
555
+ }
556
+ }
557
+
558
+ pub fn gen_safe_write8(ctx: &mut JitContext, address_local: &WasmLocal, value_local: &WasmLocal) {
559
+ gen_safe_write(
560
+ ctx,
561
+ BitSize::BYTE,
562
+ address_local,
563
+ GenSafeWriteValue::I32(value_local),
564
+ )
565
+ }
566
+ pub fn gen_safe_write16(ctx: &mut JitContext, address_local: &WasmLocal, value_local: &WasmLocal) {
567
+ gen_safe_write(
568
+ ctx,
569
+ BitSize::WORD,
570
+ address_local,
571
+ GenSafeWriteValue::I32(value_local),
572
+ )
573
+ }
574
+ pub fn gen_safe_write32(ctx: &mut JitContext, address_local: &WasmLocal, value_local: &WasmLocal) {
575
+ gen_safe_write(
576
+ ctx,
577
+ BitSize::DWORD,
578
+ address_local,
579
+ GenSafeWriteValue::I32(value_local),
580
+ )
581
+ }
582
+ pub fn gen_safe_write64(
583
+ ctx: &mut JitContext,
584
+ address_local: &WasmLocal,
585
+ value_local: &WasmLocalI64,
586
+ ) {
587
+ gen_safe_write(
588
+ ctx,
589
+ BitSize::QWORD,
590
+ address_local,
591
+ GenSafeWriteValue::I64(value_local),
592
+ )
593
+ }
594
+
595
+ pub fn gen_safe_write128(
596
+ ctx: &mut JitContext,
597
+ address_local: &WasmLocal,
598
+ value_local_low: &WasmLocalI64,
599
+ value_local_high: &WasmLocalI64,
600
+ ) {
601
+ gen_safe_write(
602
+ ctx,
603
+ BitSize::DQWORD,
604
+ address_local,
605
+ GenSafeWriteValue::TwoI64s(value_local_low, value_local_high),
606
+ )
607
+ }
608
+
609
+ fn gen_safe_read(
610
+ ctx: &mut JitContext,
611
+ bits: BitSize,
612
+ address_local: &WasmLocal,
613
+ where_to_write: Option<u32>,
614
+ ) {
615
+ // Execute a virtual memory read. All slow paths (memory-mapped IO, tlb miss, page fault and
616
+ // read across page boundary are handled in safe_read_jit_slow
617
+
618
+ // entry <- tlb_data[addr >> 12 << 2]
619
+ // if entry & MASK == TLB_VALID && (addr & 0xFFF) <= 0x1000 - bytes: goto fast
620
+ // entry <- safe_read_jit_slow(addr, instruction_pointer)
621
+ // if page_fault: goto exit-with-pagefault
622
+ // fast: mem[(entry & ~0xFFF) ^ addr]
623
+
624
+ let cont = ctx.builder.block_void();
625
+ ctx.builder.get_local(&address_local);
626
+
627
+ ctx.builder.const_i32(12);
628
+ ctx.builder.shr_u_i32();
629
+ ctx.builder.const_i32(2);
630
+ ctx.builder.shl_i32();
631
+
632
+ ctx.builder
633
+ .load_aligned_i32(unsafe { &tlb_data[0] as *const i32 as u32 });
634
+ let entry_local = ctx.builder.tee_new_local();
635
+
636
+ ctx.builder.const_i32(
637
+ (0xFFF
638
+ & !TLB_READONLY
639
+ & !TLB_GLOBAL
640
+ & !TLB_HAS_CODE
641
+ & !(if ctx.cpu.cpl3() { 0 } else { TLB_NO_USER })) as i32,
642
+ );
643
+ ctx.builder.and_i32();
644
+
645
+ ctx.builder.const_i32(TLB_VALID as i32);
646
+ ctx.builder.eq_i32();
647
+
648
+ if bits != BitSize::BYTE {
649
+ ctx.builder.get_local(&address_local);
650
+ ctx.builder.const_i32(0xFFF);
651
+ ctx.builder.and_i32();
652
+ ctx.builder.const_i32(0x1000 - bits.bytes() as i32);
653
+ ctx.builder.le_i32();
654
+
655
+ ctx.builder.and_i32();
656
+ }
657
+
658
+ ctx.builder.br_if(cont);
659
+
660
+ if cfg!(feature = "profiler") {
661
+ ctx.builder.get_local(&address_local);
662
+ ctx.builder.get_local(&entry_local);
663
+ ctx.builder.call_fn2("report_safe_read_jit_slow");
664
+ }
665
+
666
+ ctx.builder.get_local(&address_local);
667
+ ctx.builder
668
+ .const_i32(ctx.start_of_current_instruction as i32 & 0xFFF);
669
+ match bits {
670
+ BitSize::BYTE => {
671
+ ctx.builder.call_fn2_ret("safe_read8_slow_jit");
672
+ },
673
+ BitSize::WORD => {
674
+ ctx.builder.call_fn2_ret("safe_read16_slow_jit");
675
+ },
676
+ BitSize::DWORD => {
677
+ ctx.builder.call_fn2_ret("safe_read32s_slow_jit");
678
+ },
679
+ BitSize::QWORD => {
680
+ ctx.builder.call_fn2_ret("safe_read64s_slow_jit");
681
+ },
682
+ BitSize::DQWORD => {
683
+ ctx.builder.call_fn2_ret("safe_read128s_slow_jit");
684
+ },
685
+ }
686
+ ctx.builder.tee_local(&entry_local);
687
+ ctx.builder.const_i32(1);
688
+ ctx.builder.and_i32();
689
+
690
+ if cfg!(feature = "profiler") {
691
+ ctx.builder.if_void();
692
+ gen_debug_track_jit_exit(ctx.builder, ctx.start_of_current_instruction);
693
+ ctx.builder.block_end();
694
+
695
+ ctx.builder.get_local(&entry_local);
696
+ ctx.builder.const_i32(1);
697
+ ctx.builder.and_i32();
698
+ }
699
+
700
+ ctx.builder.br_if(ctx.exit_with_fault_label);
701
+
702
+ ctx.builder.block_end();
703
+
704
+ gen_profiler_stat_increment(ctx.builder, profiler::stat::SAFE_READ_FAST); // XXX: Both fast and slow
705
+
706
+ ctx.builder.get_local(&entry_local);
707
+ ctx.builder.const_i32(!0xFFF);
708
+ ctx.builder.and_i32();
709
+ ctx.builder.get_local(&address_local);
710
+ ctx.builder.xor_i32();
711
+
712
+ // where_to_write is only used by dqword
713
+ dbg_assert!((where_to_write != None) == (bits == BitSize::DQWORD));
714
+
715
+ match bits {
716
+ BitSize::BYTE => {
717
+ ctx.builder.load_u8(0);
718
+ },
719
+ BitSize::WORD => {
720
+ ctx.builder.load_unaligned_u16(0);
721
+ },
722
+ BitSize::DWORD => {
723
+ ctx.builder.load_unaligned_i32(0);
724
+ },
725
+ BitSize::QWORD => {
726
+ ctx.builder.load_unaligned_i64(0);
727
+ },
728
+ BitSize::DQWORD => {
729
+ let where_to_write = where_to_write.unwrap();
730
+ let virt_address_local = ctx.builder.set_new_local();
731
+ ctx.builder.const_i32(0);
732
+ ctx.builder.get_local(&virt_address_local);
733
+ ctx.builder.load_unaligned_i64(0);
734
+ ctx.builder.store_unaligned_i64(where_to_write);
735
+
736
+ ctx.builder.const_i32(0);
737
+ ctx.builder.get_local(&virt_address_local);
738
+ ctx.builder.load_unaligned_i64(8);
739
+ ctx.builder.store_unaligned_i64(where_to_write + 8);
740
+
741
+ ctx.builder.free_local(virt_address_local);
742
+ },
743
+ }
744
+
745
+ ctx.builder.free_local(entry_local);
746
+ }
747
+
748
+ pub fn gen_get_phys_eip_plus_mem(ctx: &mut JitContext, address_local: &WasmLocal) {
749
+ // Similar to gen_safe_read, but return the physical eip + memory::mem rather than reading from memory
750
+ // In functions that need to use this value we need to fix it by substracting memory::mem
751
+ // this is done in order to remove one instruction from the fast path of memory accesses (no need to add
752
+ // memory::mem anymore ).
753
+ // We need to account for this in gen_page_switch_check and we compare with next_block_addr + memory::mem8
754
+ // We cannot the same while processing an AbsoluteEip flow control change so there we need to fix the value
755
+ // by subscracting memory::mem. Overall, since AbsoluteEip is encountered less often than memory accesses so
756
+ // this ends up improving perf.
757
+ // Does not (need to) handle mapped memory
758
+ // XXX: Currently does not use ctx.start_of_current_instruction, but rather assumes that eip is
759
+ // already correct (pointing at the current instruction)
760
+
761
+ let cont = ctx.builder.block_void();
762
+ ctx.builder.get_local(&address_local);
763
+
764
+ ctx.builder.const_i32(12);
765
+ ctx.builder.shr_u_i32();
766
+ ctx.builder.const_i32(2);
767
+ ctx.builder.shl_i32();
768
+
769
+ ctx.builder
770
+ .load_aligned_i32(unsafe { &tlb_data[0] as *const i32 as u32 });
771
+ let entry_local = ctx.builder.tee_new_local();
772
+
773
+ ctx.builder.const_i32(
774
+ (0xFFF
775
+ & !TLB_READONLY
776
+ & !TLB_GLOBAL
777
+ & !TLB_HAS_CODE
778
+ & !(if ctx.cpu.cpl3() { 0 } else { TLB_NO_USER })) as i32,
779
+ );
780
+ ctx.builder.and_i32();
781
+
782
+ ctx.builder.const_i32(TLB_VALID as i32);
783
+ ctx.builder.eq_i32();
784
+
785
+ ctx.builder.br_if(cont);
786
+
787
+ if cfg!(feature = "profiler") {
788
+ ctx.builder.get_local(&address_local);
789
+ ctx.builder.get_local(&entry_local);
790
+ ctx.builder.call_fn2("report_safe_read_jit_slow");
791
+ }
792
+
793
+ ctx.builder.get_local(&address_local);
794
+ ctx.builder.call_fn1_ret("get_phys_eip_slow_jit");
795
+
796
+ ctx.builder.tee_local(&entry_local);
797
+ ctx.builder.const_i32(1);
798
+ ctx.builder.and_i32();
799
+
800
+ if cfg!(feature = "profiler") {
801
+ ctx.builder.if_void();
802
+ gen_debug_track_jit_exit(ctx.builder, ctx.start_of_current_instruction); // XXX
803
+ ctx.builder.block_end();
804
+
805
+ ctx.builder.get_local(&entry_local);
806
+ ctx.builder.const_i32(1);
807
+ ctx.builder.and_i32();
808
+ }
809
+
810
+ ctx.builder.br_if(ctx.exit_with_fault_label);
811
+
812
+ ctx.builder.block_end();
813
+
814
+ gen_profiler_stat_increment(ctx.builder, profiler::stat::SAFE_READ_FAST); // XXX: Both fast and slow
815
+
816
+ ctx.builder.get_local(&entry_local);
817
+ ctx.builder.const_i32(!0xFFF);
818
+ ctx.builder.and_i32();
819
+ ctx.builder.get_local(&address_local);
820
+ ctx.builder.xor_i32();
821
+
822
+ ctx.builder.free_local(entry_local);
823
+ }
824
+
825
+ fn gen_safe_write(
826
+ ctx: &mut JitContext,
827
+ bits: BitSize,
828
+ address_local: &WasmLocal,
829
+ value_local: GenSafeWriteValue,
830
+ ) {
831
+ // Execute a virtual memory write. All slow paths (memory-mapped IO, tlb miss, page fault,
832
+ // write across page boundary and page containing jitted code are handled in safe_write_jit_slow
833
+
834
+ // entry <- tlb_data[addr >> 12 << 2]
835
+ // if entry & MASK == TLB_VALID && (addr & 0xFFF) <= 0x1000 - bytes: goto fast
836
+ // entry <- safe_write_jit_slow(addr, value, instruction_pointer)
837
+ // if page_fault: goto exit-with-pagefault
838
+ // fast: mem[(entry & ~0xFFF) ^ addr] <- value
839
+
840
+ let cont = ctx.builder.block_void();
841
+ ctx.builder.get_local(&address_local);
842
+
843
+ ctx.builder.const_i32(12);
844
+ ctx.builder.shr_u_i32();
845
+ ctx.builder.const_i32(2);
846
+ ctx.builder.shl_i32();
847
+
848
+ ctx.builder
849
+ .load_aligned_i32(unsafe { &tlb_data[0] as *const i32 as u32 });
850
+ let entry_local = ctx.builder.tee_new_local();
851
+
852
+ ctx.builder
853
+ .const_i32((0xFFF & !TLB_GLOBAL & !(if ctx.cpu.cpl3() { 0 } else { TLB_NO_USER })) as i32);
854
+ ctx.builder.and_i32();
855
+
856
+ ctx.builder.const_i32(TLB_VALID as i32);
857
+ ctx.builder.eq_i32();
858
+
859
+ if bits != BitSize::BYTE {
860
+ ctx.builder.get_local(&address_local);
861
+ ctx.builder.const_i32(0xFFF);
862
+ ctx.builder.and_i32();
863
+ ctx.builder.const_i32(0x1000 - bits.bytes() as i32);
864
+ ctx.builder.le_i32();
865
+
866
+ ctx.builder.and_i32();
867
+ }
868
+
869
+ ctx.builder.br_if(cont);
870
+
871
+ if cfg!(feature = "profiler") {
872
+ ctx.builder.get_local(&address_local);
873
+ ctx.builder.get_local(&entry_local);
874
+ ctx.builder.call_fn2("report_safe_write_jit_slow");
875
+ }
876
+
877
+ ctx.builder.get_local(&address_local);
878
+ match value_local {
879
+ GenSafeWriteValue::I32(local) => ctx.builder.get_local(local),
880
+ GenSafeWriteValue::I64(local) => ctx.builder.get_local_i64(local),
881
+ GenSafeWriteValue::TwoI64s(local1, local2) => {
882
+ ctx.builder.get_local_i64(local1);
883
+ ctx.builder.get_local_i64(local2)
884
+ },
885
+ }
886
+ ctx.builder
887
+ .const_i32(ctx.start_of_current_instruction as i32 & 0xFFF);
888
+ match bits {
889
+ BitSize::BYTE => {
890
+ ctx.builder.call_fn3_ret("safe_write8_slow_jit");
891
+ },
892
+ BitSize::WORD => {
893
+ ctx.builder.call_fn3_ret("safe_write16_slow_jit");
894
+ },
895
+ BitSize::DWORD => {
896
+ ctx.builder.call_fn3_ret("safe_write32_slow_jit");
897
+ },
898
+ BitSize::QWORD => {
899
+ ctx.builder
900
+ .call_fn3_i32_i64_i32_ret("safe_write64_slow_jit");
901
+ },
902
+ BitSize::DQWORD => {
903
+ ctx.builder
904
+ .call_fn4_i32_i64_i64_i32_ret("safe_write128_slow_jit");
905
+ },
906
+ }
907
+ ctx.builder.tee_local(&entry_local);
908
+ ctx.builder.const_i32(1);
909
+ ctx.builder.and_i32();
910
+
911
+ if cfg!(feature = "profiler") {
912
+ ctx.builder.if_void();
913
+ gen_debug_track_jit_exit(ctx.builder, ctx.start_of_current_instruction);
914
+ ctx.builder.block_end();
915
+
916
+ ctx.builder.get_local(&entry_local);
917
+ ctx.builder.const_i32(1);
918
+ ctx.builder.and_i32();
919
+ }
920
+
921
+ ctx.builder.br_if(ctx.exit_with_fault_label);
922
+
923
+ ctx.builder.block_end();
924
+
925
+ gen_profiler_stat_increment(ctx.builder, profiler::stat::SAFE_WRITE_FAST); // XXX: Both fast and slow
926
+
927
+ ctx.builder.get_local(&entry_local);
928
+ ctx.builder.const_i32(!0xFFF);
929
+ ctx.builder.and_i32();
930
+ ctx.builder.get_local(&address_local);
931
+ ctx.builder.xor_i32();
932
+
933
+ match value_local {
934
+ GenSafeWriteValue::I32(local) => ctx.builder.get_local(local),
935
+ GenSafeWriteValue::I64(local) => ctx.builder.get_local_i64(local),
936
+ GenSafeWriteValue::TwoI64s(local1, local2) => {
937
+ assert!(bits == BitSize::DQWORD);
938
+
939
+ let virt_address_local = ctx.builder.tee_new_local();
940
+ ctx.builder.get_local_i64(local1);
941
+ ctx.builder.store_unaligned_i64(0);
942
+
943
+ ctx.builder.get_local(&virt_address_local);
944
+ ctx.builder.get_local_i64(local2);
945
+ ctx.builder.store_unaligned_i64(8);
946
+ ctx.builder.free_local(virt_address_local);
947
+ },
948
+ }
949
+ match bits {
950
+ BitSize::BYTE => {
951
+ ctx.builder.store_u8(0);
952
+ },
953
+ BitSize::WORD => {
954
+ ctx.builder.store_unaligned_u16(0);
955
+ },
956
+ BitSize::DWORD => {
957
+ ctx.builder.store_unaligned_i32(0);
958
+ },
959
+ BitSize::QWORD => {
960
+ ctx.builder.store_unaligned_i64(0);
961
+ },
962
+ BitSize::DQWORD => {}, // handled above
963
+ }
964
+
965
+ ctx.builder.free_local(entry_local);
966
+ }
967
+
968
+ pub fn gen_safe_read_write(
969
+ ctx: &mut JitContext,
970
+ bits: BitSize,
971
+ address_local: &WasmLocal,
972
+ f: &dyn Fn(&mut JitContext),
973
+ ) {
974
+ // Execute a virtual memory read+write. All slow paths (memory-mapped IO, tlb miss, page fault,
975
+ // write across page boundary and page containing jitted code are handled in
976
+ // safe_read_write_jit_slow
977
+
978
+ // entry <- tlb_data[addr >> 12 << 2]
979
+ // can_use_fast_path <- entry & MASK == TLB_VALID && (addr & 0xFFF) <= 0x1000 - bytes
980
+ // if can_use_fast_path: goto fast
981
+ // entry <- safe_read_write_jit_slow(addr, instruction_pointer)
982
+ // if page_fault: goto exit-with-pagefault
983
+ // fast: value <- f(mem[(entry & ~0xFFF) ^ addr])
984
+ // if !can_use_fast_path { safe_write_jit_slow(addr, value, instruction_pointer) }
985
+ // mem[(entry & ~0xFFF) ^ addr] <- value
986
+
987
+ let cont = ctx.builder.block_void();
988
+ ctx.builder.get_local(address_local);
989
+
990
+ ctx.builder.const_i32(12);
991
+ ctx.builder.shr_u_i32();
992
+ ctx.builder.const_i32(2);
993
+ ctx.builder.shl_i32();
994
+
995
+ ctx.builder
996
+ .load_aligned_i32(unsafe { &tlb_data[0] as *const i32 as u32 });
997
+ let entry_local = ctx.builder.tee_new_local();
998
+
999
+ ctx.builder
1000
+ .const_i32((0xFFF & !TLB_GLOBAL & !(if ctx.cpu.cpl3() { 0 } else { TLB_NO_USER })) as i32);
1001
+ ctx.builder.and_i32();
1002
+
1003
+ ctx.builder.const_i32(TLB_VALID as i32);
1004
+ ctx.builder.eq_i32();
1005
+
1006
+ if bits != BitSize::BYTE {
1007
+ ctx.builder.get_local(&address_local);
1008
+ ctx.builder.const_i32(0xFFF);
1009
+ ctx.builder.and_i32();
1010
+ ctx.builder.const_i32(0x1000 - bits.bytes() as i32);
1011
+ ctx.builder.le_i32();
1012
+ ctx.builder.and_i32();
1013
+ }
1014
+
1015
+ let can_use_fast_path_local = ctx.builder.tee_new_local();
1016
+
1017
+ ctx.builder.br_if(cont);
1018
+
1019
+ if cfg!(feature = "profiler") {
1020
+ ctx.builder.get_local(&address_local);
1021
+ ctx.builder.get_local(&entry_local);
1022
+ ctx.builder.call_fn2("report_safe_read_write_jit_slow");
1023
+ }
1024
+
1025
+ ctx.builder.get_local(&address_local);
1026
+ ctx.builder
1027
+ .const_i32(ctx.start_of_current_instruction as i32 & 0xFFF);
1028
+
1029
+ match bits {
1030
+ BitSize::BYTE => {
1031
+ ctx.builder.call_fn2_ret("safe_read_write8_slow_jit");
1032
+ },
1033
+ BitSize::WORD => {
1034
+ ctx.builder.call_fn2_ret("safe_read_write16_slow_jit");
1035
+ },
1036
+ BitSize::DWORD => {
1037
+ ctx.builder.call_fn2_ret("safe_read_write32s_slow_jit");
1038
+ },
1039
+ BitSize::QWORD => {
1040
+ ctx.builder.call_fn2_ret("safe_read_write64_slow_jit");
1041
+ },
1042
+ BitSize::DQWORD => {
1043
+ dbg_assert!(false);
1044
+ },
1045
+ }
1046
+ ctx.builder.tee_local(&entry_local);
1047
+ ctx.builder.const_i32(1);
1048
+ ctx.builder.and_i32();
1049
+
1050
+ if cfg!(feature = "profiler") {
1051
+ ctx.builder.if_void();
1052
+ gen_debug_track_jit_exit(ctx.builder, ctx.start_of_current_instruction);
1053
+ ctx.builder.block_end();
1054
+
1055
+ ctx.builder.get_local(&entry_local);
1056
+ ctx.builder.const_i32(1);
1057
+ ctx.builder.and_i32();
1058
+ }
1059
+
1060
+ ctx.builder.br_if(ctx.exit_with_fault_label);
1061
+
1062
+ ctx.builder.block_end();
1063
+
1064
+ gen_profiler_stat_increment(ctx.builder, profiler::stat::SAFE_READ_WRITE_FAST); // XXX: Also slow
1065
+
1066
+ ctx.builder.get_local(&entry_local);
1067
+ ctx.builder.const_i32(!0xFFF);
1068
+ ctx.builder.and_i32();
1069
+ ctx.builder.get_local(&address_local);
1070
+ ctx.builder.xor_i32();
1071
+
1072
+ ctx.builder.free_local(entry_local);
1073
+ let phys_addr_local = ctx.builder.tee_new_local();
1074
+
1075
+ match bits {
1076
+ BitSize::BYTE => {
1077
+ ctx.builder.load_u8(0);
1078
+ },
1079
+ BitSize::WORD => {
1080
+ ctx.builder.load_unaligned_u16(0);
1081
+ },
1082
+ BitSize::DWORD => {
1083
+ ctx.builder.load_unaligned_i32(0);
1084
+ },
1085
+ BitSize::QWORD => {
1086
+ ctx.builder.load_unaligned_i64(0);
1087
+ },
1088
+ BitSize::DQWORD => assert!(false), // not used
1089
+ }
1090
+
1091
+ // value is now on stack
1092
+
1093
+ f(ctx);
1094
+
1095
+ // TODO: Could get rid of this local by returning one from f
1096
+ let value_local = if bits == BitSize::QWORD {
1097
+ GenSafeReadWriteValue::I64(ctx.builder.set_new_local_i64())
1098
+ }
1099
+ else {
1100
+ GenSafeReadWriteValue::I32(ctx.builder.set_new_local())
1101
+ };
1102
+
1103
+ ctx.builder.get_local(&can_use_fast_path_local);
1104
+
1105
+ ctx.builder.eqz_i32();
1106
+ ctx.builder.if_void();
1107
+ {
1108
+ ctx.builder.get_local(&address_local);
1109
+
1110
+ match &value_local {
1111
+ GenSafeReadWriteValue::I32(l) => ctx.builder.get_local(l),
1112
+ GenSafeReadWriteValue::I64(l) => ctx.builder.get_local_i64(l),
1113
+ }
1114
+
1115
+ ctx.builder
1116
+ .const_i32(ctx.start_of_current_instruction as i32 & 0xFFF);
1117
+
1118
+ match bits {
1119
+ BitSize::BYTE => {
1120
+ ctx.builder.call_fn3_ret("safe_write8_slow_jit");
1121
+ },
1122
+ BitSize::WORD => {
1123
+ ctx.builder.call_fn3_ret("safe_write16_slow_jit");
1124
+ },
1125
+ BitSize::DWORD => {
1126
+ ctx.builder.call_fn3_ret("safe_write32_slow_jit");
1127
+ },
1128
+ BitSize::QWORD => {
1129
+ ctx.builder
1130
+ .call_fn3_i32_i64_i32_ret("safe_write64_slow_jit");
1131
+ },
1132
+ BitSize::DQWORD => {
1133
+ dbg_assert!(false);
1134
+ },
1135
+ }
1136
+
1137
+ if cfg!(debug_assertions) {
1138
+ ctx.builder.const_i32(1);
1139
+ ctx.builder.and_i32();
1140
+
1141
+ ctx.builder.if_void();
1142
+ {
1143
+ // handled above
1144
+ ctx.builder.const_i32(match bits {
1145
+ BitSize::BYTE => 8,
1146
+ BitSize::WORD => 16,
1147
+ BitSize::DWORD => 32,
1148
+ BitSize::QWORD => 64,
1149
+ _ => {
1150
+ dbg_assert!(false);
1151
+ 0
1152
+ },
1153
+ });
1154
+ ctx.builder.get_local(&address_local);
1155
+ ctx.builder.call_fn2("bug_gen_safe_read_write_page_fault");
1156
+ }
1157
+ ctx.builder.block_end();
1158
+ }
1159
+ else {
1160
+ ctx.builder.drop_();
1161
+ }
1162
+ }
1163
+ ctx.builder.block_end();
1164
+
1165
+ ctx.builder.get_local(&phys_addr_local);
1166
+ match &value_local {
1167
+ GenSafeReadWriteValue::I32(l) => ctx.builder.get_local(l),
1168
+ GenSafeReadWriteValue::I64(l) => ctx.builder.get_local_i64(l),
1169
+ }
1170
+
1171
+ match bits {
1172
+ BitSize::BYTE => {
1173
+ ctx.builder.store_u8(0);
1174
+ },
1175
+ BitSize::WORD => {
1176
+ ctx.builder.store_unaligned_u16(0);
1177
+ },
1178
+ BitSize::DWORD => {
1179
+ ctx.builder.store_unaligned_i32(0);
1180
+ },
1181
+ BitSize::QWORD => {
1182
+ ctx.builder.store_unaligned_i64(0);
1183
+ },
1184
+ BitSize::DQWORD => {
1185
+ dbg_assert!(false);
1186
+ },
1187
+ }
1188
+
1189
+ match value_local {
1190
+ GenSafeReadWriteValue::I32(l) => ctx.builder.free_local(l),
1191
+ GenSafeReadWriteValue::I64(l) => ctx.builder.free_local_i64(l),
1192
+ }
1193
+ ctx.builder.free_local(can_use_fast_path_local);
1194
+ ctx.builder.free_local(phys_addr_local);
1195
+ }
1196
+
1197
+ #[cfg(debug_assertions)]
1198
+ #[no_mangle]
1199
+ pub fn bug_gen_safe_read_write_page_fault(bits: i32, addr: u32) {
1200
+ dbg_log!("bug: gen_safe_read_write_page_fault {} {:x}", bits, addr);
1201
+ dbg_assert!(false);
1202
+ }
1203
+
1204
+ pub fn gen_jmp_rel16(builder: &mut WasmBuilder, rel16: u16) {
1205
+ let cs_offset_addr = global_pointers::get_seg_offset(regs::CS);
1206
+ builder.load_fixed_i32(cs_offset_addr);
1207
+ let local = builder.set_new_local();
1208
+
1209
+ // generate:
1210
+ // *instruction_pointer = cs_offset + ((*instruction_pointer - cs_offset + rel16) & 0xFFFF);
1211
+ {
1212
+ builder.const_i32(global_pointers::instruction_pointer as i32);
1213
+
1214
+ gen_get_eip(builder);
1215
+ builder.get_local(&local);
1216
+ builder.sub_i32();
1217
+
1218
+ builder.const_i32(rel16 as i32);
1219
+ builder.add_i32();
1220
+
1221
+ builder.const_i32(0xFFFF);
1222
+ builder.and_i32();
1223
+
1224
+ builder.get_local(&local);
1225
+ builder.add_i32();
1226
+
1227
+ builder.store_aligned_i32(0);
1228
+ }
1229
+ builder.free_local(local);
1230
+ }
1231
+
1232
+ pub fn gen_pop16_ss16(ctx: &mut JitContext) {
1233
+ // sp = segment_offsets[SS] + reg16[SP] (or just reg16[SP] if has_flat_segmentation)
1234
+ gen_get_reg16(ctx, regs::SP);
1235
+
1236
+ if !ctx.cpu.has_flat_segmentation() {
1237
+ gen_get_ss_offset(ctx);
1238
+ ctx.builder.add_i32();
1239
+ }
1240
+
1241
+ // result = safe_read16(sp)
1242
+ let address_local = ctx.builder.set_new_local();
1243
+ gen_safe_read16(ctx, &address_local);
1244
+ ctx.builder.free_local(address_local);
1245
+
1246
+ // reg16[SP] += 2;
1247
+ gen_get_reg16(ctx, regs::SP);
1248
+ ctx.builder.const_i32(2);
1249
+ ctx.builder.add_i32();
1250
+ gen_set_reg16(ctx, regs::SP);
1251
+
1252
+ // return value is already on stack
1253
+ }
1254
+
1255
+ pub fn gen_pop16_ss32(ctx: &mut JitContext) {
1256
+ // esp = segment_offsets[SS] + reg32[ESP] (or just reg32[ESP] if has_flat_segmentation)
1257
+ gen_get_reg32(ctx, regs::ESP);
1258
+
1259
+ if !ctx.cpu.has_flat_segmentation() {
1260
+ gen_get_ss_offset(ctx);
1261
+ ctx.builder.add_i32();
1262
+ }
1263
+
1264
+ // result = safe_read16(esp)
1265
+ let address_local = ctx.builder.set_new_local();
1266
+ gen_safe_read16(ctx, &address_local);
1267
+ ctx.builder.free_local(address_local);
1268
+
1269
+ // reg32[ESP] += 2;
1270
+ gen_get_reg32(ctx, regs::ESP);
1271
+ ctx.builder.const_i32(2);
1272
+ ctx.builder.add_i32();
1273
+ gen_set_reg32(ctx, regs::ESP);
1274
+
1275
+ // return value is already on stack
1276
+ }
1277
+
1278
+ pub fn gen_pop16(ctx: &mut JitContext) {
1279
+ if ctx.cpu.ssize_32() {
1280
+ gen_pop16_ss32(ctx);
1281
+ }
1282
+ else {
1283
+ gen_pop16_ss16(ctx);
1284
+ }
1285
+ }
1286
+
1287
+ pub fn gen_pop32s_ss16(ctx: &mut JitContext) {
1288
+ // sp = reg16[SP]
1289
+ gen_get_reg16(ctx, regs::SP);
1290
+
1291
+ // result = safe_read32s(segment_offsets[SS] + sp) (or just sp if has_flat_segmentation)
1292
+ if !ctx.cpu.has_flat_segmentation() {
1293
+ gen_get_ss_offset(ctx);
1294
+ ctx.builder.add_i32();
1295
+ }
1296
+
1297
+ let address_local = ctx.builder.set_new_local();
1298
+ gen_safe_read32(ctx, &address_local);
1299
+ ctx.builder.free_local(address_local);
1300
+
1301
+ // reg16[SP] = sp + 4;
1302
+ gen_get_reg16(ctx, regs::SP);
1303
+ ctx.builder.const_i32(4);
1304
+ ctx.builder.add_i32();
1305
+ gen_set_reg16(ctx, regs::SP);
1306
+
1307
+ // return value is already on stack
1308
+ }
1309
+
1310
+ pub fn gen_pop32s_ss32(ctx: &mut JitContext) {
1311
+ if !ctx.cpu.has_flat_segmentation() {
1312
+ gen_get_reg32(ctx, regs::ESP);
1313
+ gen_get_ss_offset(ctx);
1314
+ ctx.builder.add_i32();
1315
+ let address_local = ctx.builder.set_new_local();
1316
+ gen_safe_read32(ctx, &address_local);
1317
+ ctx.builder.free_local(address_local);
1318
+ }
1319
+ else {
1320
+ let reg = ctx.register_locals[regs::ESP as usize].unsafe_clone();
1321
+ gen_safe_read32(ctx, &reg);
1322
+ }
1323
+
1324
+ gen_get_reg32(ctx, regs::ESP);
1325
+ ctx.builder.const_i32(4);
1326
+ ctx.builder.add_i32();
1327
+ gen_set_reg32(ctx, regs::ESP);
1328
+
1329
+ // return value is already on stack
1330
+ }
1331
+
1332
+ pub fn gen_pop32s(ctx: &mut JitContext) {
1333
+ if ctx.cpu.ssize_32() {
1334
+ gen_pop32s_ss32(ctx);
1335
+ }
1336
+ else {
1337
+ gen_pop32s_ss16(ctx);
1338
+ }
1339
+ }
1340
+
1341
+ pub fn gen_adjust_stack_reg(ctx: &mut JitContext, offset: u32) {
1342
+ if ctx.cpu.ssize_32() {
1343
+ gen_get_reg32(ctx, regs::ESP);
1344
+ ctx.builder.const_i32(offset as i32);
1345
+ ctx.builder.add_i32();
1346
+ gen_set_reg32(ctx, regs::ESP);
1347
+ }
1348
+ else {
1349
+ gen_get_reg16(ctx, regs::SP);
1350
+ ctx.builder.const_i32(offset as i32);
1351
+ ctx.builder.add_i32();
1352
+ gen_set_reg16(ctx, regs::SP);
1353
+ }
1354
+ }
1355
+
1356
+ pub fn gen_leave(ctx: &mut JitContext, os32: bool) {
1357
+ // [e]bp = safe_read{16,32}([e]bp)
1358
+
1359
+ if ctx.cpu.ssize_32() {
1360
+ gen_get_reg32(ctx, regs::EBP);
1361
+ }
1362
+ else {
1363
+ gen_get_reg16(ctx, regs::BP);
1364
+ }
1365
+
1366
+ let old_vbp = ctx.builder.tee_new_local();
1367
+
1368
+ if !ctx.cpu.has_flat_segmentation() {
1369
+ gen_get_ss_offset(ctx);
1370
+ ctx.builder.add_i32();
1371
+ }
1372
+ if os32 {
1373
+ let address_local = ctx.builder.set_new_local();
1374
+ gen_safe_read32(ctx, &address_local);
1375
+ ctx.builder.free_local(address_local);
1376
+ gen_set_reg32(ctx, regs::EBP);
1377
+ }
1378
+ else {
1379
+ let address_local = ctx.builder.set_new_local();
1380
+ gen_safe_read16(ctx, &address_local);
1381
+ ctx.builder.free_local(address_local);
1382
+ gen_set_reg16(ctx, regs::BP);
1383
+ }
1384
+
1385
+ // [e]sp = [e]bp + (os32 ? 4 : 2)
1386
+
1387
+ if ctx.cpu.ssize_32() {
1388
+ ctx.builder.get_local(&old_vbp);
1389
+ ctx.builder.const_i32(if os32 { 4 } else { 2 });
1390
+ ctx.builder.add_i32();
1391
+ gen_set_reg32(ctx, regs::ESP);
1392
+ }
1393
+ else {
1394
+ ctx.builder.get_local(&old_vbp);
1395
+ ctx.builder.const_i32(if os32 { 4 } else { 2 });
1396
+ ctx.builder.add_i32();
1397
+ gen_set_reg16(ctx, regs::SP);
1398
+ }
1399
+
1400
+ ctx.builder.free_local(old_vbp);
1401
+ }
1402
+
1403
+ pub fn gen_task_switch_test(ctx: &mut JitContext) {
1404
+ // generate if(cr[0] & (CR0_EM | CR0_TS)) { task_switch_test_jit(); goto exit_with_fault; }
1405
+ let cr0_offset = global_pointers::get_creg_offset(0);
1406
+
1407
+ dbg_assert!(regs::CR0_EM | regs::CR0_TS <= 0xFF);
1408
+ ctx.builder.load_fixed_u8(cr0_offset);
1409
+ ctx.builder.const_i32((regs::CR0_EM | regs::CR0_TS) as i32);
1410
+ ctx.builder.and_i32();
1411
+
1412
+ ctx.builder.if_void();
1413
+ {
1414
+ gen_debug_track_jit_exit(ctx.builder, ctx.start_of_current_instruction);
1415
+ gen_fn1_const(
1416
+ ctx.builder,
1417
+ "task_switch_test_jit",
1418
+ ctx.start_of_current_instruction & 0xFFF,
1419
+ );
1420
+ ctx.builder.br(ctx.exit_with_fault_label);
1421
+ }
1422
+ ctx.builder.block_end();
1423
+ }
1424
+
1425
+ pub fn gen_task_switch_test_mmx(ctx: &mut JitContext) {
1426
+ // generate if(cr[0] & (CR0_EM | CR0_TS)) { task_switch_test_mmx_jit(); goto exit_with_fault; }
1427
+ let cr0_offset = global_pointers::get_creg_offset(0);
1428
+
1429
+ dbg_assert!(regs::CR0_EM | regs::CR0_TS <= 0xFF);
1430
+ ctx.builder.load_fixed_u8(cr0_offset);
1431
+ ctx.builder.const_i32((regs::CR0_EM | regs::CR0_TS) as i32);
1432
+ ctx.builder.and_i32();
1433
+
1434
+ ctx.builder.if_void();
1435
+ {
1436
+ gen_debug_track_jit_exit(ctx.builder, ctx.start_of_current_instruction);
1437
+ gen_fn1_const(
1438
+ ctx.builder,
1439
+ "task_switch_test_mmx_jit",
1440
+ ctx.start_of_current_instruction & 0xFFF,
1441
+ );
1442
+ ctx.builder.br(ctx.exit_with_fault_label);
1443
+ }
1444
+ ctx.builder.block_end();
1445
+ }
1446
+
1447
+ pub fn gen_push16(ctx: &mut JitContext, value_local: &WasmLocal) {
1448
+ if ctx.cpu.ssize_32() {
1449
+ gen_get_reg32(ctx, regs::ESP);
1450
+ }
1451
+ else {
1452
+ gen_get_reg16(ctx, regs::SP);
1453
+ };
1454
+
1455
+ ctx.builder.const_i32(2);
1456
+ ctx.builder.sub_i32();
1457
+
1458
+ let reg_updated_local = if !ctx.cpu.ssize_32() || !ctx.cpu.has_flat_segmentation() {
1459
+ let reg_updated_local = ctx.builder.tee_new_local();
1460
+ if !ctx.cpu.ssize_32() {
1461
+ ctx.builder.const_i32(0xFFFF);
1462
+ ctx.builder.and_i32();
1463
+ }
1464
+
1465
+ if !ctx.cpu.has_flat_segmentation() {
1466
+ gen_get_ss_offset(ctx);
1467
+ ctx.builder.add_i32();
1468
+ }
1469
+
1470
+ let sp_local = ctx.builder.set_new_local();
1471
+ gen_safe_write16(ctx, &sp_local, &value_local);
1472
+ ctx.builder.free_local(sp_local);
1473
+
1474
+ ctx.builder.get_local(&reg_updated_local);
1475
+ reg_updated_local
1476
+ }
1477
+ else {
1478
+ // short path: The address written to is equal to ESP/SP minus two
1479
+ let reg_updated_local = ctx.builder.tee_new_local();
1480
+ gen_safe_write16(ctx, &reg_updated_local, &value_local);
1481
+ reg_updated_local
1482
+ };
1483
+
1484
+ if ctx.cpu.ssize_32() {
1485
+ gen_set_reg32(ctx, regs::ESP);
1486
+ }
1487
+ else {
1488
+ gen_set_reg16(ctx, regs::SP);
1489
+ };
1490
+ ctx.builder.free_local(reg_updated_local);
1491
+ }
1492
+
1493
+ pub fn gen_push32(ctx: &mut JitContext, value_local: &WasmLocal) {
1494
+ if ctx.cpu.ssize_32() {
1495
+ gen_get_reg32(ctx, regs::ESP);
1496
+ }
1497
+ else {
1498
+ gen_get_reg16(ctx, regs::SP);
1499
+ };
1500
+
1501
+ ctx.builder.const_i32(4);
1502
+ ctx.builder.sub_i32();
1503
+
1504
+ let new_sp_local = if !ctx.cpu.ssize_32() || !ctx.cpu.has_flat_segmentation() {
1505
+ let new_sp_local = ctx.builder.tee_new_local();
1506
+ if !ctx.cpu.ssize_32() {
1507
+ ctx.builder.const_i32(0xFFFF);
1508
+ ctx.builder.and_i32();
1509
+ }
1510
+
1511
+ if !ctx.cpu.has_flat_segmentation() {
1512
+ gen_get_ss_offset(ctx);
1513
+ ctx.builder.add_i32();
1514
+ }
1515
+
1516
+ let sp_local = ctx.builder.set_new_local();
1517
+
1518
+ gen_safe_write32(ctx, &sp_local, &value_local);
1519
+ ctx.builder.free_local(sp_local);
1520
+
1521
+ ctx.builder.get_local(&new_sp_local);
1522
+ new_sp_local
1523
+ }
1524
+ else {
1525
+ // short path: The address written to is equal to ESP/SP minus four
1526
+ let new_sp_local = ctx.builder.tee_new_local();
1527
+ gen_safe_write32(ctx, &new_sp_local, &value_local);
1528
+ new_sp_local
1529
+ };
1530
+
1531
+ if ctx.cpu.ssize_32() {
1532
+ gen_set_reg32(ctx, regs::ESP);
1533
+ }
1534
+ else {
1535
+ gen_set_reg16(ctx, regs::SP);
1536
+ };
1537
+ ctx.builder.free_local(new_sp_local);
1538
+ }
1539
+
1540
+ pub fn gen_push32_sreg(ctx: &mut JitContext, reg: u32) {
1541
+ gen_get_sreg(ctx, reg);
1542
+ let value_local = ctx.builder.set_new_local();
1543
+
1544
+ if ctx.cpu.ssize_32() {
1545
+ gen_get_reg32(ctx, regs::ESP);
1546
+ }
1547
+ else {
1548
+ gen_get_reg16(ctx, regs::SP);
1549
+ };
1550
+
1551
+ ctx.builder.const_i32(4);
1552
+ ctx.builder.sub_i32();
1553
+
1554
+ let new_sp_local = if !ctx.cpu.ssize_32() || !ctx.cpu.has_flat_segmentation() {
1555
+ let new_sp_local = ctx.builder.tee_new_local();
1556
+ if !ctx.cpu.ssize_32() {
1557
+ ctx.builder.const_i32(0xFFFF);
1558
+ ctx.builder.and_i32();
1559
+ }
1560
+
1561
+ if !ctx.cpu.has_flat_segmentation() {
1562
+ gen_get_ss_offset(ctx);
1563
+ ctx.builder.add_i32();
1564
+ }
1565
+
1566
+ let sp_local = ctx.builder.set_new_local();
1567
+
1568
+ gen_safe_write16(ctx, &sp_local, &value_local);
1569
+ ctx.builder.free_local(sp_local);
1570
+
1571
+ ctx.builder.get_local(&new_sp_local);
1572
+ new_sp_local
1573
+ }
1574
+ else {
1575
+ // short path: The address written to is equal to ESP/SP minus four
1576
+ let new_sp_local = ctx.builder.tee_new_local();
1577
+ gen_safe_write16(ctx, &new_sp_local, &value_local);
1578
+ new_sp_local
1579
+ };
1580
+
1581
+ if ctx.cpu.ssize_32() {
1582
+ gen_set_reg32(ctx, regs::ESP);
1583
+ }
1584
+ else {
1585
+ gen_set_reg16(ctx, regs::SP);
1586
+ };
1587
+ ctx.builder.free_local(new_sp_local);
1588
+ ctx.builder.free_local(value_local);
1589
+ }
1590
+
1591
+ pub fn gen_get_real_eip(ctx: &mut JitContext) {
1592
+ gen_get_eip(ctx.builder);
1593
+ ctx.builder.const_i32(!0xFFF);
1594
+ ctx.builder.and_i32();
1595
+ ctx.builder.const_i32(ctx.cpu.eip as i32 & 0xFFF);
1596
+ ctx.builder.or_i32();
1597
+ if !ctx.cpu.has_flat_segmentation() {
1598
+ ctx.builder
1599
+ .load_fixed_i32(global_pointers::get_seg_offset(regs::CS));
1600
+ ctx.builder.sub_i32();
1601
+ }
1602
+ }
1603
+
1604
+ pub fn gen_set_last_op1(builder: &mut WasmBuilder, source: &WasmLocal) {
1605
+ builder.const_i32(global_pointers::last_op1 as i32);
1606
+ builder.get_local(&source);
1607
+ builder.store_aligned_i32(0);
1608
+ }
1609
+
1610
+ pub fn gen_set_last_result(builder: &mut WasmBuilder, source: &WasmLocal) {
1611
+ builder.const_i32(global_pointers::last_result as i32);
1612
+ builder.get_local(&source);
1613
+ builder.store_aligned_i32(0);
1614
+ }
1615
+
1616
+ pub fn gen_clear_flags_changed_bits(builder: &mut WasmBuilder, bits_to_clear: i32) {
1617
+ builder.const_i32(global_pointers::flags_changed as i32);
1618
+ gen_get_flags_changed(builder);
1619
+ builder.const_i32(!bits_to_clear);
1620
+ builder.and_i32();
1621
+ builder.store_aligned_i32(0);
1622
+ }
1623
+
1624
+ pub fn gen_set_last_op_size_and_flags_changed(
1625
+ builder: &mut WasmBuilder,
1626
+ last_op_size: i32,
1627
+ flags_changed: i32,
1628
+ ) {
1629
+ dbg_assert!(last_op_size == OPSIZE_8 || last_op_size == OPSIZE_16 || last_op_size == OPSIZE_32);
1630
+ dbg_assert!(global_pointers::last_op_size as i32 % 8 == 0);
1631
+ dbg_assert!(global_pointers::last_op_size as i32 + 4 == global_pointers::flags_changed as i32);
1632
+ builder.const_i32(global_pointers::last_op_size as i32);
1633
+ builder.const_i64(last_op_size as u32 as i64 | (flags_changed as u32 as i64) << 32);
1634
+ builder.store_aligned_i64(0);
1635
+ }
1636
+
1637
+ pub fn gen_set_flags_bits(builder: &mut WasmBuilder, bits_to_set: i32) {
1638
+ builder.const_i32(global_pointers::flags as i32);
1639
+ gen_get_flags(builder);
1640
+ builder.const_i32(bits_to_set);
1641
+ builder.or_i32();
1642
+ builder.store_aligned_i32(0);
1643
+ }
1644
+
1645
+ pub fn gen_clear_flags_bits(builder: &mut WasmBuilder, bits_to_clear: i32) {
1646
+ builder.const_i32(global_pointers::flags as i32);
1647
+ gen_get_flags(builder);
1648
+ builder.const_i32(!bits_to_clear);
1649
+ builder.and_i32();
1650
+ builder.store_aligned_i32(0);
1651
+ }
1652
+
1653
+ #[derive(PartialEq)]
1654
+ pub enum ConditionNegate {
1655
+ True,
1656
+ False,
1657
+ }
1658
+
1659
+ pub fn gen_getzf(ctx: &mut JitContext, negate: ConditionNegate) {
1660
+ match &ctx.previous_instruction {
1661
+ Instruction::Cmp {
1662
+ dest: InstructionOperandDest::WasmLocal(dest),
1663
+ source: InstructionOperand::WasmLocal(source),
1664
+ opsize: OPSIZE_32,
1665
+ } => {
1666
+ gen_profiler_stat_increment(ctx.builder, profiler::stat::CONDITION_OPTIMISED);
1667
+ ctx.builder.get_local(dest);
1668
+ ctx.builder.get_local(source);
1669
+ if negate == ConditionNegate::False {
1670
+ ctx.builder.eq_i32();
1671
+ }
1672
+ else {
1673
+ ctx.builder.ne_i32();
1674
+ }
1675
+ },
1676
+ Instruction::Cmp {
1677
+ dest: InstructionOperandDest::WasmLocal(dest),
1678
+ source: InstructionOperand::Immediate(0),
1679
+ opsize: OPSIZE_32,
1680
+ } => {
1681
+ gen_profiler_stat_increment(ctx.builder, profiler::stat::CONDITION_OPTIMISED);
1682
+ ctx.builder.get_local(dest);
1683
+ if negate == ConditionNegate::False {
1684
+ ctx.builder.eqz_i32();
1685
+ }
1686
+ },
1687
+ Instruction::Cmp {
1688
+ dest: InstructionOperandDest::WasmLocal(dest),
1689
+ source: InstructionOperand::Immediate(i),
1690
+ opsize: OPSIZE_32,
1691
+ } => {
1692
+ gen_profiler_stat_increment(ctx.builder, profiler::stat::CONDITION_OPTIMISED);
1693
+ ctx.builder.get_local(dest);
1694
+ ctx.builder.const_i32(*i);
1695
+ if negate == ConditionNegate::False {
1696
+ ctx.builder.eq_i32();
1697
+ }
1698
+ else {
1699
+ ctx.builder.ne_i32();
1700
+ }
1701
+ },
1702
+ Instruction::Cmp { .. }
1703
+ | Instruction::Sub { .. }
1704
+ | Instruction::Add { .. }
1705
+ | Instruction::AdcSbb { .. }
1706
+ | Instruction::NonZeroShift { .. } => {
1707
+ gen_profiler_stat_increment(ctx.builder, profiler::stat::CONDITION_OPTIMISED);
1708
+ gen_get_last_result(ctx.builder, &ctx.previous_instruction);
1709
+ if negate == ConditionNegate::False {
1710
+ ctx.builder.eqz_i32();
1711
+ }
1712
+ },
1713
+ Instruction::Bitwise { opsize, .. } => {
1714
+ let &opsize = opsize;
1715
+ gen_profiler_stat_increment(ctx.builder, profiler::stat::CONDITION_OPTIMISED);
1716
+ // Note: Necessary because test{8,16} don't mask either last_result or any of their operands
1717
+ // TODO: Use local instead of last_result for 8-bit/16-bit
1718
+ if opsize == OPSIZE_32 {
1719
+ gen_get_last_result(ctx.builder, &ctx.previous_instruction);
1720
+ }
1721
+ else if opsize == OPSIZE_16 {
1722
+ ctx.builder
1723
+ .load_fixed_u16(global_pointers::last_result as u32);
1724
+ }
1725
+ else if opsize == OPSIZE_8 {
1726
+ ctx.builder
1727
+ .load_fixed_u8(global_pointers::last_result as u32);
1728
+ }
1729
+ if negate == ConditionNegate::False {
1730
+ ctx.builder.eqz_i32();
1731
+ }
1732
+ },
1733
+ &Instruction::Other => {
1734
+ gen_profiler_stat_increment(ctx.builder, profiler::stat::CONDITION_UNOPTIMISED);
1735
+ gen_get_flags_changed(ctx.builder);
1736
+ ctx.builder.const_i32(FLAG_ZERO);
1737
+ ctx.builder.and_i32();
1738
+ ctx.builder.if_i32();
1739
+
1740
+ gen_get_last_result(ctx.builder, &ctx.previous_instruction);
1741
+ let last_result = ctx.builder.tee_new_local();
1742
+ ctx.builder.const_i32(-1);
1743
+ ctx.builder.xor_i32();
1744
+ ctx.builder.get_local(&last_result);
1745
+ ctx.builder.free_local(last_result);
1746
+ ctx.builder.const_i32(1);
1747
+ ctx.builder.sub_i32();
1748
+ ctx.builder.and_i32();
1749
+ gen_get_last_op_size(ctx.builder);
1750
+ ctx.builder.shr_u_i32();
1751
+ ctx.builder.const_i32(1);
1752
+ ctx.builder.and_i32();
1753
+
1754
+ ctx.builder.else_();
1755
+ gen_get_flags(ctx.builder);
1756
+ ctx.builder.const_i32(FLAG_ZERO);
1757
+ ctx.builder.and_i32();
1758
+ ctx.builder.block_end();
1759
+
1760
+ if negate == ConditionNegate::True {
1761
+ ctx.builder.eqz_i32();
1762
+ }
1763
+ },
1764
+ }
1765
+ }
1766
+
1767
+ pub fn gen_getcf(ctx: &mut JitContext, negate: ConditionNegate) {
1768
+ match &ctx.previous_instruction {
1769
+ Instruction::Cmp { source, opsize, .. }
1770
+ | Instruction::Sub {
1771
+ source,
1772
+ opsize,
1773
+ is_dec: false,
1774
+ ..
1775
+ } => {
1776
+ // Note: x < y and x < x - y can be used interchangeably (see getcf)
1777
+ gen_profiler_stat_increment(ctx.builder, profiler::stat::CONDITION_OPTIMISED);
1778
+ gen_get_last_op1(ctx.builder, &ctx.previous_instruction);
1779
+ match (opsize, source) {
1780
+ (&OPSIZE_32, InstructionOperand::WasmLocal(l)) => ctx.builder.get_local(l),
1781
+ (_, &InstructionOperand::Immediate(i)) => ctx.builder.const_i32(i),
1782
+ _ => gen_get_last_result(ctx.builder, &ctx.previous_instruction),
1783
+ }
1784
+ if negate == ConditionNegate::True {
1785
+ ctx.builder.geu_i32();
1786
+ }
1787
+ else {
1788
+ ctx.builder.ltu_i32();
1789
+ }
1790
+ },
1791
+ Instruction::Add {
1792
+ source,
1793
+ opsize,
1794
+ is_inc: false,
1795
+ ..
1796
+ } => {
1797
+ gen_get_last_result(ctx.builder, &ctx.previous_instruction);
1798
+ match (opsize, source) {
1799
+ (&OPSIZE_32, InstructionOperand::WasmLocal(l)) => ctx.builder.get_local(l),
1800
+ (_, &InstructionOperand::Immediate(i)) => ctx.builder.const_i32(i),
1801
+ _ => gen_get_last_op1(ctx.builder, &ctx.previous_instruction),
1802
+ }
1803
+ if negate == ConditionNegate::True {
1804
+ ctx.builder.geu_i32();
1805
+ }
1806
+ else {
1807
+ ctx.builder.ltu_i32();
1808
+ }
1809
+ },
1810
+ Instruction::Add { is_inc: true, .. } | Instruction::Sub { is_dec: true, .. } => {
1811
+ gen_get_flags(ctx.builder);
1812
+ ctx.builder.const_i32(FLAG_CARRY);
1813
+ ctx.builder.and_i32();
1814
+ if negate == ConditionNegate::True {
1815
+ ctx.builder.eqz_i32();
1816
+ }
1817
+ },
1818
+ Instruction::Bitwise { .. } => {
1819
+ gen_profiler_stat_increment(ctx.builder, profiler::stat::CONDITION_OPTIMISED);
1820
+ ctx.builder
1821
+ .const_i32(if negate == ConditionNegate::True { 1 } else { 0 });
1822
+ },
1823
+ Instruction::NonZeroShift { .. } | Instruction::AdcSbb { .. } => {
1824
+ gen_profiler_stat_increment(ctx.builder, profiler::stat::CONDITION_OPTIMISED);
1825
+ gen_get_flags(ctx.builder);
1826
+ ctx.builder.const_i32(FLAG_CARRY);
1827
+ ctx.builder.and_i32();
1828
+ if negate == ConditionNegate::True {
1829
+ ctx.builder.eqz_i32();
1830
+ }
1831
+ },
1832
+ &Instruction::Other => {
1833
+ gen_profiler_stat_increment(ctx.builder, profiler::stat::CONDITION_UNOPTIMISED);
1834
+
1835
+ gen_get_flags_changed(ctx.builder);
1836
+ let flags_changed = ctx.builder.tee_new_local();
1837
+ ctx.builder.const_i32(FLAG_CARRY);
1838
+ ctx.builder.and_i32();
1839
+ ctx.builder.if_i32();
1840
+
1841
+ ctx.builder.get_local(&flags_changed);
1842
+ ctx.builder.const_i32(31);
1843
+ ctx.builder.shr_s_i32();
1844
+ ctx.builder.free_local(flags_changed);
1845
+ let sub_mask = ctx.builder.set_new_local();
1846
+
1847
+ gen_get_last_result(ctx.builder, &ctx.previous_instruction);
1848
+ ctx.builder.get_local(&sub_mask);
1849
+ ctx.builder.xor_i32();
1850
+
1851
+ gen_get_last_op1(ctx.builder, &ctx.previous_instruction);
1852
+ ctx.builder.get_local(&sub_mask);
1853
+ ctx.builder.xor_i32();
1854
+
1855
+ ctx.builder.ltu_i32();
1856
+
1857
+ ctx.builder.else_();
1858
+ gen_get_flags(ctx.builder);
1859
+ ctx.builder.const_i32(FLAG_CARRY);
1860
+ ctx.builder.and_i32();
1861
+ ctx.builder.block_end();
1862
+
1863
+ ctx.builder.free_local(sub_mask);
1864
+
1865
+ if negate == ConditionNegate::True {
1866
+ ctx.builder.eqz_i32();
1867
+ }
1868
+ },
1869
+ }
1870
+ }
1871
+
1872
+ pub fn gen_getsf(ctx: &mut JitContext, negate: ConditionNegate) {
1873
+ match &ctx.previous_instruction {
1874
+ Instruction::Cmp { opsize, .. }
1875
+ | Instruction::Sub { opsize, .. }
1876
+ | Instruction::Add { opsize, .. }
1877
+ | Instruction::AdcSbb { opsize, .. }
1878
+ | Instruction::Bitwise { opsize, .. }
1879
+ | Instruction::NonZeroShift { opsize, .. } => {
1880
+ let &opsize = opsize;
1881
+ gen_profiler_stat_increment(ctx.builder, profiler::stat::CONDITION_OPTIMISED);
1882
+ gen_get_last_result(ctx.builder, &ctx.previous_instruction);
1883
+ if opsize == OPSIZE_32 {
1884
+ ctx.builder.const_i32(0);
1885
+ if negate == ConditionNegate::True {
1886
+ ctx.builder.ge_i32();
1887
+ }
1888
+ else {
1889
+ ctx.builder.lt_i32();
1890
+ }
1891
+ }
1892
+ else {
1893
+ // TODO: use register (see get_last_result)
1894
+ ctx.builder
1895
+ .const_i32(if opsize == OPSIZE_16 { 0x8000 } else { 0x80 });
1896
+ ctx.builder.and_i32();
1897
+ if negate == ConditionNegate::True {
1898
+ ctx.builder.eqz_i32();
1899
+ }
1900
+ }
1901
+ },
1902
+ &Instruction::Other => {
1903
+ gen_profiler_stat_increment(ctx.builder, profiler::stat::CONDITION_UNOPTIMISED);
1904
+ gen_get_flags_changed(ctx.builder);
1905
+ ctx.builder.const_i32(FLAG_SIGN);
1906
+ ctx.builder.and_i32();
1907
+ ctx.builder.if_i32();
1908
+ {
1909
+ gen_get_last_result(ctx.builder, &ctx.previous_instruction);
1910
+ gen_get_last_op_size(ctx.builder);
1911
+ ctx.builder.shr_u_i32();
1912
+ ctx.builder.const_i32(1);
1913
+ ctx.builder.and_i32();
1914
+ }
1915
+ ctx.builder.else_();
1916
+ {
1917
+ gen_get_flags(ctx.builder);
1918
+ ctx.builder.const_i32(FLAG_SIGN);
1919
+ ctx.builder.and_i32();
1920
+ }
1921
+ ctx.builder.block_end();
1922
+ if negate == ConditionNegate::True {
1923
+ ctx.builder.eqz_i32();
1924
+ }
1925
+ },
1926
+ }
1927
+ }
1928
+
1929
+ pub fn gen_getof(ctx: &mut JitContext) {
1930
+ match &ctx.previous_instruction {
1931
+ Instruction::Cmp { opsize, .. } | Instruction::Sub { opsize, .. } => {
1932
+ // TODO: a better formula might be possible
1933
+ let &opsize = opsize;
1934
+ gen_profiler_stat_increment(ctx.builder, profiler::stat::CONDITION_OPTIMISED);
1935
+ gen_get_last_op1(ctx.builder, &ctx.previous_instruction);
1936
+ gen_get_last_result(ctx.builder, &ctx.previous_instruction);
1937
+ ctx.builder.xor_i32();
1938
+
1939
+ gen_get_last_op1(ctx.builder, &ctx.previous_instruction);
1940
+ gen_get_last_op1(ctx.builder, &ctx.previous_instruction);
1941
+ gen_get_last_result(ctx.builder, &ctx.previous_instruction);
1942
+ ctx.builder.sub_i32();
1943
+ ctx.builder.xor_i32();
1944
+ ctx.builder.and_i32();
1945
+
1946
+ ctx.builder.const_i32(if opsize == OPSIZE_32 {
1947
+ 0x8000_0000u32 as i32
1948
+ }
1949
+ else if opsize == OPSIZE_16 {
1950
+ 0x8000
1951
+ }
1952
+ else {
1953
+ 0x80
1954
+ });
1955
+ ctx.builder.and_i32();
1956
+ },
1957
+ Instruction::Add { opsize, .. } => {
1958
+ // TODO: a better formula might be possible
1959
+ let &opsize = opsize;
1960
+ gen_profiler_stat_increment(ctx.builder, profiler::stat::CONDITION_OPTIMISED);
1961
+ gen_get_last_op1(ctx.builder, &ctx.previous_instruction);
1962
+ gen_get_last_result(ctx.builder, &ctx.previous_instruction);
1963
+ ctx.builder.xor_i32();
1964
+
1965
+ gen_get_last_result(ctx.builder, &ctx.previous_instruction);
1966
+ gen_get_last_result(ctx.builder, &ctx.previous_instruction);
1967
+ gen_get_last_op1(ctx.builder, &ctx.previous_instruction);
1968
+ ctx.builder.sub_i32();
1969
+ ctx.builder.xor_i32();
1970
+ ctx.builder.and_i32();
1971
+
1972
+ ctx.builder.const_i32(if opsize == OPSIZE_32 {
1973
+ 0x8000_0000u32 as i32
1974
+ }
1975
+ else if opsize == OPSIZE_16 {
1976
+ 0x8000
1977
+ }
1978
+ else {
1979
+ 0x80
1980
+ });
1981
+ ctx.builder.and_i32();
1982
+ },
1983
+ Instruction::Bitwise { .. } => {
1984
+ gen_profiler_stat_increment(ctx.builder, profiler::stat::CONDITION_OPTIMISED);
1985
+ ctx.builder.const_i32(0);
1986
+ },
1987
+ Instruction::NonZeroShift { .. } | Instruction::AdcSbb { .. } => {
1988
+ gen_profiler_stat_increment(ctx.builder, profiler::stat::CONDITION_OPTIMISED);
1989
+ gen_get_flags(ctx.builder);
1990
+ ctx.builder.const_i32(FLAG_OVERFLOW);
1991
+ ctx.builder.and_i32();
1992
+ },
1993
+ &Instruction::Other => {
1994
+ gen_profiler_stat_increment(ctx.builder, profiler::stat::CONDITION_UNOPTIMISED);
1995
+ gen_get_flags_changed(ctx.builder);
1996
+ let flags_changed = ctx.builder.tee_new_local();
1997
+ ctx.builder.const_i32(FLAG_OVERFLOW);
1998
+ ctx.builder.and_i32();
1999
+ ctx.builder.if_i32();
2000
+ {
2001
+ gen_get_last_op1(ctx.builder, &ctx.previous_instruction);
2002
+ let last_op1 = ctx.builder.tee_new_local();
2003
+ gen_get_last_result(ctx.builder, &ctx.previous_instruction);
2004
+ let last_result = ctx.builder.tee_new_local();
2005
+ ctx.builder.xor_i32();
2006
+
2007
+ ctx.builder.get_local(&last_result);
2008
+ ctx.builder.get_local(&last_op1);
2009
+ ctx.builder.sub_i32();
2010
+ gen_get_flags_changed(ctx.builder);
2011
+ ctx.builder.const_i32(31);
2012
+ ctx.builder.shr_u_i32();
2013
+ ctx.builder.sub_i32();
2014
+
2015
+ ctx.builder.get_local(&last_result);
2016
+ ctx.builder.xor_i32();
2017
+
2018
+ ctx.builder.and_i32();
2019
+
2020
+ gen_get_last_op_size(ctx.builder);
2021
+ ctx.builder.shr_u_i32();
2022
+ ctx.builder.const_i32(1);
2023
+ ctx.builder.and_i32();
2024
+
2025
+ ctx.builder.free_local(last_op1);
2026
+ ctx.builder.free_local(last_result);
2027
+ }
2028
+ ctx.builder.else_();
2029
+ {
2030
+ gen_get_flags(ctx.builder);
2031
+ ctx.builder.const_i32(FLAG_OVERFLOW);
2032
+ ctx.builder.and_i32();
2033
+ }
2034
+ ctx.builder.block_end();
2035
+ ctx.builder.free_local(flags_changed);
2036
+ },
2037
+ }
2038
+ }
2039
+
2040
+ pub fn gen_test_be(ctx: &mut JitContext, negate: ConditionNegate) {
2041
+ match &ctx.previous_instruction {
2042
+ Instruction::Cmp {
2043
+ dest,
2044
+ source,
2045
+ opsize,
2046
+ } => {
2047
+ gen_profiler_stat_increment(ctx.builder, profiler::stat::CONDITION_OPTIMISED);
2048
+ match dest {
2049
+ InstructionOperandDest::WasmLocal(l) => {
2050
+ ctx.builder.get_local(l);
2051
+ if *opsize == OPSIZE_8 || *opsize == OPSIZE_16 {
2052
+ ctx.builder
2053
+ .const_i32(if *opsize == OPSIZE_8 { 0xFF } else { 0xFFFF });
2054
+ ctx.builder.and_i32();
2055
+ }
2056
+ },
2057
+ InstructionOperandDest::Other => {
2058
+ gen_get_last_op1(ctx.builder, &ctx.previous_instruction);
2059
+ },
2060
+ }
2061
+ match source {
2062
+ InstructionOperand::WasmLocal(l) => {
2063
+ ctx.builder.get_local(l);
2064
+ if *opsize == OPSIZE_8 || *opsize == OPSIZE_16 {
2065
+ ctx.builder
2066
+ .const_i32(if *opsize == OPSIZE_8 { 0xFF } else { 0xFFFF });
2067
+ ctx.builder.and_i32();
2068
+ }
2069
+ },
2070
+ InstructionOperand::Other => {
2071
+ gen_get_last_op1(ctx.builder, &ctx.previous_instruction);
2072
+ gen_get_last_result(ctx.builder, &ctx.previous_instruction);
2073
+ ctx.builder.sub_i32();
2074
+ if *opsize == OPSIZE_8 || *opsize == OPSIZE_16 {
2075
+ ctx.builder
2076
+ .const_i32(if *opsize == OPSIZE_8 { 0xFF } else { 0xFFFF });
2077
+ ctx.builder.and_i32();
2078
+ }
2079
+ },
2080
+ &InstructionOperand::Immediate(i) => {
2081
+ dbg_assert!(*opsize != OPSIZE_8 || i >= 0 && i < 0x100);
2082
+ dbg_assert!(*opsize != OPSIZE_16 || i >= 0 && i < 0x10000);
2083
+ ctx.builder.const_i32(i);
2084
+ },
2085
+ }
2086
+
2087
+ if negate == ConditionNegate::True {
2088
+ ctx.builder.gtu_i32();
2089
+ }
2090
+ else {
2091
+ ctx.builder.leu_i32();
2092
+ }
2093
+ },
2094
+ Instruction::Sub {
2095
+ opsize,
2096
+ source,
2097
+ is_dec: false,
2098
+ ..
2099
+ } => {
2100
+ gen_profiler_stat_increment(ctx.builder, profiler::stat::CONDITION_OPTIMISED);
2101
+
2102
+ gen_get_last_op1(ctx.builder, &ctx.previous_instruction);
2103
+ match (opsize, source) {
2104
+ (&OPSIZE_32, InstructionOperand::WasmLocal(l)) => ctx.builder.get_local(l),
2105
+ (_, &InstructionOperand::Immediate(i)) => ctx.builder.const_i32(i),
2106
+ _ => {
2107
+ gen_get_last_op1(ctx.builder, &ctx.previous_instruction);
2108
+ gen_get_last_result(ctx.builder, &ctx.previous_instruction);
2109
+ ctx.builder.sub_i32();
2110
+ if *opsize == OPSIZE_8 || *opsize == OPSIZE_16 {
2111
+ ctx.builder
2112
+ .const_i32(if *opsize == OPSIZE_8 { 0xFF } else { 0xFFFF });
2113
+ ctx.builder.and_i32();
2114
+ }
2115
+ },
2116
+ }
2117
+
2118
+ if negate == ConditionNegate::True {
2119
+ ctx.builder.gtu_i32();
2120
+ }
2121
+ else {
2122
+ ctx.builder.leu_i32();
2123
+ }
2124
+ },
2125
+ &Instruction::Bitwise { .. } => {
2126
+ gen_profiler_stat_increment(ctx.builder, profiler::stat::CONDITION_OPTIMISED);
2127
+ gen_getzf(ctx, negate);
2128
+ },
2129
+ &Instruction::Add { .. } | &Instruction::Sub { is_dec: true, .. } => {
2130
+ gen_profiler_stat_increment(ctx.builder, profiler::stat::CONDITION_OPTIMISED);
2131
+ // not the best code generation, but reasonable for this fairly uncommon case
2132
+ gen_getcf(ctx, ConditionNegate::False);
2133
+ gen_getzf(ctx, ConditionNegate::False);
2134
+ ctx.builder.or_i32();
2135
+ if negate == ConditionNegate::True {
2136
+ ctx.builder.eqz_i32();
2137
+ }
2138
+ },
2139
+ Instruction::Other | Instruction::NonZeroShift { .. } | Instruction::AdcSbb { .. } => {
2140
+ gen_profiler_stat_increment(ctx.builder, profiler::stat::CONDITION_UNOPTIMISED);
2141
+ gen_getcf(ctx, ConditionNegate::False);
2142
+ gen_getzf(ctx, ConditionNegate::False);
2143
+ ctx.builder.or_i32();
2144
+ if negate == ConditionNegate::True {
2145
+ ctx.builder.eqz_i32();
2146
+ }
2147
+ },
2148
+ }
2149
+ }
2150
+
2151
+ pub fn gen_test_l(ctx: &mut JitContext, negate: ConditionNegate) {
2152
+ match &ctx.previous_instruction {
2153
+ Instruction::Cmp {
2154
+ dest,
2155
+ source,
2156
+ opsize,
2157
+ } => {
2158
+ gen_profiler_stat_increment(ctx.builder, profiler::stat::CONDITION_OPTIMISED);
2159
+ match dest {
2160
+ InstructionOperandDest::WasmLocal(l) => {
2161
+ ctx.builder.get_local(l);
2162
+ if *opsize == OPSIZE_8 || *opsize == OPSIZE_16 {
2163
+ ctx.builder
2164
+ .const_i32(if *opsize == OPSIZE_8 { 24 } else { 16 });
2165
+ ctx.builder.shl_i32();
2166
+ }
2167
+ },
2168
+ InstructionOperandDest::Other => {
2169
+ gen_get_last_op1(ctx.builder, &ctx.previous_instruction);
2170
+ if *opsize == OPSIZE_8 || *opsize == OPSIZE_16 {
2171
+ ctx.builder
2172
+ .const_i32(if *opsize == OPSIZE_8 { 24 } else { 16 });
2173
+ ctx.builder.shl_i32();
2174
+ }
2175
+ },
2176
+ }
2177
+ match source {
2178
+ InstructionOperand::WasmLocal(l) => {
2179
+ ctx.builder.get_local(l);
2180
+ if *opsize == OPSIZE_8 || *opsize == OPSIZE_16 {
2181
+ ctx.builder
2182
+ .const_i32(if *opsize == OPSIZE_8 { 24 } else { 16 });
2183
+ ctx.builder.shl_i32();
2184
+ }
2185
+ },
2186
+ InstructionOperand::Other => {
2187
+ gen_get_last_op1(ctx.builder, &ctx.previous_instruction);
2188
+ gen_get_last_result(ctx.builder, &ctx.previous_instruction);
2189
+ ctx.builder.sub_i32();
2190
+ if *opsize == OPSIZE_8 || *opsize == OPSIZE_16 {
2191
+ ctx.builder
2192
+ .const_i32(if *opsize == OPSIZE_8 { 24 } else { 16 });
2193
+ ctx.builder.shl_i32();
2194
+ }
2195
+ },
2196
+ &InstructionOperand::Immediate(i) => {
2197
+ ctx.builder.const_i32(i);
2198
+ if *opsize == OPSIZE_8 || *opsize == OPSIZE_16 {
2199
+ ctx.builder
2200
+ .const_i32(if *opsize == OPSIZE_8 { 24 } else { 16 });
2201
+ ctx.builder.shl_i32();
2202
+ }
2203
+ },
2204
+ }
2205
+ if negate == ConditionNegate::True {
2206
+ ctx.builder.ge_i32();
2207
+ }
2208
+ else {
2209
+ ctx.builder.lt_i32();
2210
+ }
2211
+ },
2212
+ Instruction::Sub { opsize, source, .. } => {
2213
+ gen_profiler_stat_increment(ctx.builder, profiler::stat::CONDITION_OPTIMISED);
2214
+ gen_get_last_op1(ctx.builder, &ctx.previous_instruction);
2215
+ if *opsize == OPSIZE_8 || *opsize == OPSIZE_16 {
2216
+ ctx.builder
2217
+ .const_i32(if *opsize == OPSIZE_8 { 24 } else { 16 });
2218
+ ctx.builder.shl_i32();
2219
+ }
2220
+ match (opsize, source) {
2221
+ (&OPSIZE_32, InstructionOperand::WasmLocal(l)) => ctx.builder.get_local(l),
2222
+ (_, &InstructionOperand::Immediate(i)) => ctx.builder.const_i32(
2223
+ i << if *opsize == OPSIZE_32 {
2224
+ 0
2225
+ }
2226
+ else if *opsize == OPSIZE_16 {
2227
+ 16
2228
+ }
2229
+ else {
2230
+ 24
2231
+ },
2232
+ ),
2233
+ _ => {
2234
+ gen_get_last_op1(ctx.builder, &ctx.previous_instruction);
2235
+ gen_get_last_result(ctx.builder, &ctx.previous_instruction);
2236
+ ctx.builder.sub_i32();
2237
+ if *opsize == OPSIZE_8 || *opsize == OPSIZE_16 {
2238
+ ctx.builder
2239
+ .const_i32(if *opsize == OPSIZE_8 { 24 } else { 16 });
2240
+ ctx.builder.shl_i32();
2241
+ }
2242
+ },
2243
+ }
2244
+ if negate == ConditionNegate::True {
2245
+ ctx.builder.ge_i32();
2246
+ }
2247
+ else {
2248
+ ctx.builder.lt_i32();
2249
+ }
2250
+ },
2251
+ &Instruction::Bitwise { .. } => {
2252
+ gen_profiler_stat_increment(ctx.builder, profiler::stat::CONDITION_OPTIMISED);
2253
+ gen_getsf(ctx, negate);
2254
+ },
2255
+ &Instruction::Other
2256
+ | Instruction::Add { .. }
2257
+ | Instruction::NonZeroShift { .. }
2258
+ | Instruction::AdcSbb { .. } => {
2259
+ gen_profiler_stat_increment(ctx.builder, profiler::stat::CONDITION_UNOPTIMISED);
2260
+ if let Instruction::Add { .. } = ctx.previous_instruction {
2261
+ gen_profiler_stat_increment(
2262
+ ctx.builder,
2263
+ profiler::stat::CONDITION_UNOPTIMISED_UNHANDLED_L,
2264
+ );
2265
+ }
2266
+ gen_getsf(ctx, ConditionNegate::False);
2267
+ ctx.builder.eqz_i32();
2268
+ gen_getof(ctx);
2269
+ ctx.builder.eqz_i32();
2270
+ ctx.builder.xor_i32();
2271
+ if negate == ConditionNegate::True {
2272
+ ctx.builder.eqz_i32();
2273
+ }
2274
+ },
2275
+ }
2276
+ }
2277
+
2278
+ pub fn gen_test_le(ctx: &mut JitContext, negate: ConditionNegate) {
2279
+ match &ctx.previous_instruction {
2280
+ Instruction::Cmp {
2281
+ dest,
2282
+ source,
2283
+ opsize,
2284
+ } => {
2285
+ gen_profiler_stat_increment(ctx.builder, profiler::stat::CONDITION_OPTIMISED);
2286
+ match dest {
2287
+ InstructionOperandDest::WasmLocal(l) => {
2288
+ ctx.builder.get_local(l);
2289
+ if *opsize == OPSIZE_8 || *opsize == OPSIZE_16 {
2290
+ ctx.builder
2291
+ .const_i32(if *opsize == OPSIZE_8 { 24 } else { 16 });
2292
+ ctx.builder.shl_i32();
2293
+ }
2294
+ },
2295
+ InstructionOperandDest::Other => {
2296
+ gen_get_last_op1(ctx.builder, &ctx.previous_instruction);
2297
+ if *opsize == OPSIZE_8 || *opsize == OPSIZE_16 {
2298
+ ctx.builder
2299
+ .const_i32(if *opsize == OPSIZE_8 { 24 } else { 16 });
2300
+ ctx.builder.shl_i32();
2301
+ }
2302
+ },
2303
+ }
2304
+ match source {
2305
+ InstructionOperand::WasmLocal(l) => {
2306
+ ctx.builder.get_local(l);
2307
+ if *opsize == OPSIZE_8 || *opsize == OPSIZE_16 {
2308
+ ctx.builder
2309
+ .const_i32(if *opsize == OPSIZE_8 { 24 } else { 16 });
2310
+ ctx.builder.shl_i32();
2311
+ }
2312
+ },
2313
+ InstructionOperand::Other => {
2314
+ gen_get_last_op1(ctx.builder, &ctx.previous_instruction);
2315
+ gen_get_last_result(ctx.builder, &ctx.previous_instruction);
2316
+ ctx.builder.sub_i32();
2317
+ if *opsize == OPSIZE_8 || *opsize == OPSIZE_16 {
2318
+ ctx.builder
2319
+ .const_i32(if *opsize == OPSIZE_8 { 24 } else { 16 });
2320
+ ctx.builder.shl_i32();
2321
+ }
2322
+ },
2323
+ &InstructionOperand::Immediate(i) => {
2324
+ ctx.builder.const_i32(i);
2325
+ if *opsize == OPSIZE_8 || *opsize == OPSIZE_16 {
2326
+ ctx.builder
2327
+ .const_i32(if *opsize == OPSIZE_8 { 24 } else { 16 });
2328
+ ctx.builder.shl_i32();
2329
+ }
2330
+ },
2331
+ }
2332
+ if negate == ConditionNegate::True {
2333
+ ctx.builder.gt_i32();
2334
+ }
2335
+ else {
2336
+ ctx.builder.le_i32();
2337
+ }
2338
+ },
2339
+ Instruction::Sub { opsize, source, .. } => {
2340
+ gen_profiler_stat_increment(ctx.builder, profiler::stat::CONDITION_OPTIMISED);
2341
+ gen_get_last_op1(ctx.builder, &ctx.previous_instruction);
2342
+ if *opsize == OPSIZE_8 || *opsize == OPSIZE_16 {
2343
+ ctx.builder
2344
+ .const_i32(if *opsize == OPSIZE_8 { 24 } else { 16 });
2345
+ ctx.builder.shl_i32();
2346
+ }
2347
+ match (opsize, source) {
2348
+ (&OPSIZE_32, InstructionOperand::WasmLocal(l)) => ctx.builder.get_local(l),
2349
+ (_, &InstructionOperand::Immediate(i)) => ctx.builder.const_i32(
2350
+ i << if *opsize == OPSIZE_32 {
2351
+ 0
2352
+ }
2353
+ else if *opsize == OPSIZE_16 {
2354
+ 16
2355
+ }
2356
+ else {
2357
+ 24
2358
+ },
2359
+ ),
2360
+ _ => {
2361
+ gen_get_last_op1(ctx.builder, &ctx.previous_instruction);
2362
+ gen_get_last_result(ctx.builder, &ctx.previous_instruction);
2363
+ ctx.builder.sub_i32();
2364
+ if *opsize == OPSIZE_8 || *opsize == OPSIZE_16 {
2365
+ ctx.builder
2366
+ .const_i32(if *opsize == OPSIZE_8 { 24 } else { 16 });
2367
+ ctx.builder.shl_i32();
2368
+ }
2369
+ },
2370
+ }
2371
+ if negate == ConditionNegate::True {
2372
+ ctx.builder.gt_i32();
2373
+ }
2374
+ else {
2375
+ ctx.builder.le_i32();
2376
+ }
2377
+ },
2378
+ &Instruction::Bitwise { .. } => {
2379
+ gen_profiler_stat_increment(ctx.builder, profiler::stat::CONDITION_OPTIMISED);
2380
+ // TODO: Could probably be improved (<= 0)
2381
+ gen_test_l(ctx, ConditionNegate::False);
2382
+ gen_getzf(ctx, ConditionNegate::False);
2383
+ ctx.builder.or_i32();
2384
+ if negate == ConditionNegate::True {
2385
+ ctx.builder.eqz_i32();
2386
+ }
2387
+ },
2388
+ Instruction::Other
2389
+ | Instruction::Add { .. }
2390
+ | Instruction::NonZeroShift { .. }
2391
+ | Instruction::AdcSbb { .. } => {
2392
+ gen_profiler_stat_increment(ctx.builder, profiler::stat::CONDITION_UNOPTIMISED);
2393
+ if let Instruction::Add { .. } = ctx.previous_instruction {
2394
+ gen_profiler_stat_increment(
2395
+ ctx.builder,
2396
+ profiler::stat::CONDITION_UNOPTIMISED_UNHANDLED_LE,
2397
+ );
2398
+ }
2399
+ gen_test_l(ctx, ConditionNegate::False);
2400
+ gen_getzf(ctx, ConditionNegate::False);
2401
+ ctx.builder.or_i32();
2402
+ if negate == ConditionNegate::True {
2403
+ ctx.builder.eqz_i32();
2404
+ }
2405
+ },
2406
+ }
2407
+ }
2408
+
2409
+ pub fn gen_test_loopnz(ctx: &mut JitContext, is_asize_32: bool) {
2410
+ gen_test_loop(ctx, is_asize_32);
2411
+ ctx.builder.eqz_i32();
2412
+ gen_getzf(ctx, ConditionNegate::False);
2413
+ ctx.builder.or_i32();
2414
+ ctx.builder.eqz_i32();
2415
+ }
2416
+ pub fn gen_test_loopz(ctx: &mut JitContext, is_asize_32: bool) {
2417
+ gen_test_loop(ctx, is_asize_32);
2418
+ ctx.builder.eqz_i32();
2419
+ gen_getzf(ctx, ConditionNegate::False);
2420
+ ctx.builder.eqz_i32();
2421
+ ctx.builder.or_i32();
2422
+ ctx.builder.eqz_i32();
2423
+ }
2424
+ pub fn gen_test_loop(ctx: &mut JitContext, is_asize_32: bool) {
2425
+ if is_asize_32 {
2426
+ gen_get_reg32(ctx, regs::ECX);
2427
+ }
2428
+ else {
2429
+ gen_get_reg16(ctx, regs::CX);
2430
+ }
2431
+ }
2432
+ pub fn gen_test_jcxz(ctx: &mut JitContext, is_asize_32: bool) {
2433
+ if is_asize_32 {
2434
+ gen_get_reg32(ctx, regs::ECX);
2435
+ }
2436
+ else {
2437
+ gen_get_reg16(ctx, regs::CX);
2438
+ }
2439
+ ctx.builder.eqz_i32();
2440
+ }
2441
+
2442
+ pub fn gen_fpu_get_sti(ctx: &mut JitContext, i: u32) {
2443
+ ctx.builder
2444
+ .const_i32(global_pointers::sse_scratch_register as i32);
2445
+ ctx.builder.const_i32(i as i32);
2446
+ ctx.builder.call_fn2("fpu_get_sti_jit");
2447
+ ctx.builder
2448
+ .load_fixed_i64(global_pointers::sse_scratch_register as u32);
2449
+ ctx.builder
2450
+ .load_fixed_u16(global_pointers::sse_scratch_register as u32 + 8);
2451
+ }
2452
+
2453
+ pub fn gen_fpu_load_m32(ctx: &mut JitContext, modrm_byte: ModrmByte) {
2454
+ ctx.builder
2455
+ .const_i32(global_pointers::sse_scratch_register as i32);
2456
+ gen_modrm_resolve_safe_read32(ctx, modrm_byte);
2457
+ ctx.builder.call_fn2("f32_to_f80_jit");
2458
+ ctx.builder
2459
+ .load_fixed_i64(global_pointers::sse_scratch_register as u32);
2460
+ ctx.builder
2461
+ .load_fixed_u16(global_pointers::sse_scratch_register as u32 + 8);
2462
+ }
2463
+
2464
+ pub fn gen_fpu_load_m64(ctx: &mut JitContext, modrm_byte: ModrmByte) {
2465
+ ctx.builder
2466
+ .const_i32(global_pointers::sse_scratch_register as i32);
2467
+ gen_modrm_resolve_safe_read64(ctx, modrm_byte);
2468
+ ctx.builder.call_fn2_i32_i64("f64_to_f80_jit");
2469
+ ctx.builder
2470
+ .load_fixed_i64(global_pointers::sse_scratch_register as u32);
2471
+ ctx.builder
2472
+ .load_fixed_u16(global_pointers::sse_scratch_register as u32 + 8);
2473
+ }
2474
+
2475
+ pub fn gen_fpu_load_i16(ctx: &mut JitContext, modrm_byte: ModrmByte) {
2476
+ ctx.builder
2477
+ .const_i32(global_pointers::sse_scratch_register as i32);
2478
+ gen_modrm_resolve_safe_read16(ctx, modrm_byte);
2479
+ sign_extend_i16(ctx.builder);
2480
+ ctx.builder.call_fn2("i32_to_f80_jit");
2481
+ ctx.builder
2482
+ .load_fixed_i64(global_pointers::sse_scratch_register as u32);
2483
+ ctx.builder
2484
+ .load_fixed_u16(global_pointers::sse_scratch_register as u32 + 8);
2485
+ }
2486
+ pub fn gen_fpu_load_i32(ctx: &mut JitContext, modrm_byte: ModrmByte) {
2487
+ ctx.builder
2488
+ .const_i32(global_pointers::sse_scratch_register as i32);
2489
+ gen_modrm_resolve_safe_read32(ctx, modrm_byte);
2490
+ ctx.builder.call_fn2("i32_to_f80_jit");
2491
+ ctx.builder
2492
+ .load_fixed_i64(global_pointers::sse_scratch_register as u32);
2493
+ ctx.builder
2494
+ .load_fixed_u16(global_pointers::sse_scratch_register as u32 + 8);
2495
+ }
2496
+ pub fn gen_fpu_load_i64(ctx: &mut JitContext, modrm_byte: ModrmByte) {
2497
+ ctx.builder
2498
+ .const_i32(global_pointers::sse_scratch_register as i32);
2499
+ gen_modrm_resolve_safe_read64(ctx, modrm_byte);
2500
+ ctx.builder.call_fn2_i32_i64("i64_to_f80_jit");
2501
+ ctx.builder
2502
+ .load_fixed_i64(global_pointers::sse_scratch_register as u32);
2503
+ ctx.builder
2504
+ .load_fixed_u16(global_pointers::sse_scratch_register as u32 + 8);
2505
+ }
2506
+
2507
+ pub fn gen_trigger_de(ctx: &mut JitContext) {
2508
+ gen_fn1_const(
2509
+ ctx.builder,
2510
+ "trigger_de_jit",
2511
+ ctx.start_of_current_instruction & 0xFFF,
2512
+ );
2513
+ gen_debug_track_jit_exit(ctx.builder, ctx.start_of_current_instruction);
2514
+ ctx.builder.br(ctx.exit_with_fault_label);
2515
+ }
2516
+
2517
+ pub fn gen_trigger_ud(ctx: &mut JitContext) {
2518
+ gen_fn1_const(
2519
+ ctx.builder,
2520
+ "trigger_ud_jit",
2521
+ ctx.start_of_current_instruction & 0xFFF,
2522
+ );
2523
+ gen_debug_track_jit_exit(ctx.builder, ctx.start_of_current_instruction);
2524
+ ctx.builder.br(ctx.exit_with_fault_label);
2525
+ }
2526
+
2527
+ pub fn gen_trigger_gp(ctx: &mut JitContext, error_code: u32) {
2528
+ gen_fn2_const(
2529
+ ctx.builder,
2530
+ "trigger_gp_jit",
2531
+ error_code,
2532
+ ctx.start_of_current_instruction & 0xFFF,
2533
+ );
2534
+ gen_debug_track_jit_exit(ctx.builder, ctx.start_of_current_instruction);
2535
+ ctx.builder.br(ctx.exit_with_fault_label);
2536
+ }
2537
+
2538
+ pub fn gen_condition_fn_negated(ctx: &mut JitContext, condition: u8) {
2539
+ gen_condition_fn(ctx, condition ^ 1)
2540
+ }
2541
+
2542
+ pub fn gen_condition_fn(ctx: &mut JitContext, condition: u8) {
2543
+ if condition & 0xF0 == 0x00 || condition & 0xF0 == 0x70 || condition & 0xF0 == 0x80 {
2544
+ match condition & 0xF {
2545
+ 0x0 => {
2546
+ gen_getof(ctx);
2547
+ },
2548
+ 0x1 => {
2549
+ gen_getof(ctx);
2550
+ ctx.builder.eqz_i32();
2551
+ },
2552
+ 0x2 => {
2553
+ gen_getcf(ctx, ConditionNegate::False);
2554
+ },
2555
+ 0x3 => {
2556
+ gen_getcf(ctx, ConditionNegate::True);
2557
+ },
2558
+ 0x4 => {
2559
+ gen_getzf(ctx, ConditionNegate::False);
2560
+ },
2561
+ 0x5 => {
2562
+ gen_getzf(ctx, ConditionNegate::True);
2563
+ },
2564
+ 0x6 => {
2565
+ gen_test_be(ctx, ConditionNegate::False);
2566
+ },
2567
+ 0x7 => {
2568
+ gen_test_be(ctx, ConditionNegate::True);
2569
+ },
2570
+ 0x8 => {
2571
+ gen_getsf(ctx, ConditionNegate::False);
2572
+ },
2573
+ 0x9 => {
2574
+ gen_getsf(ctx, ConditionNegate::True);
2575
+ },
2576
+ 0xA => {
2577
+ gen_profiler_stat_increment(ctx.builder, profiler::stat::CONDITION_UNOPTIMISED);
2578
+ gen_profiler_stat_increment(ctx.builder, profiler::stat::CONDITION_UNOPTIMISED_PF);
2579
+ ctx.builder.call_fn0_ret("test_p");
2580
+ },
2581
+ 0xB => {
2582
+ gen_profiler_stat_increment(ctx.builder, profiler::stat::CONDITION_UNOPTIMISED);
2583
+ gen_profiler_stat_increment(ctx.builder, profiler::stat::CONDITION_UNOPTIMISED_PF);
2584
+ ctx.builder.call_fn0_ret("test_np");
2585
+ },
2586
+ 0xC => {
2587
+ gen_test_l(ctx, ConditionNegate::False);
2588
+ },
2589
+ 0xD => {
2590
+ gen_test_l(ctx, ConditionNegate::True);
2591
+ },
2592
+ 0xE => {
2593
+ gen_test_le(ctx, ConditionNegate::False);
2594
+ },
2595
+ 0xF => {
2596
+ gen_test_le(ctx, ConditionNegate::True);
2597
+ },
2598
+ _ => {
2599
+ dbg_assert!(false);
2600
+ },
2601
+ }
2602
+ }
2603
+ else {
2604
+ // loop, loopnz, loopz, jcxz
2605
+ dbg_assert!(condition & !0x3 == 0xE0);
2606
+ if condition == 0xE0 {
2607
+ gen_test_loopnz(ctx, ctx.cpu.asize_32());
2608
+ }
2609
+ else if condition == 0xE1 {
2610
+ gen_test_loopz(ctx, ctx.cpu.asize_32());
2611
+ }
2612
+ else if condition == 0xE2 {
2613
+ gen_test_loop(ctx, ctx.cpu.asize_32());
2614
+ }
2615
+ else if condition == 0xE3 {
2616
+ gen_test_jcxz(ctx, ctx.cpu.asize_32());
2617
+ }
2618
+ }
2619
+ }
2620
+
2621
+ pub fn gen_move_registers_from_locals_to_memory(ctx: &mut JitContext) {
2622
+ if cfg!(feature = "profiler") {
2623
+ let instruction = memory::read32s(ctx.start_of_current_instruction) as u32;
2624
+ opstats::gen_opstat_unguarded_register(ctx.builder, instruction);
2625
+ }
2626
+
2627
+ for i in 0..8 {
2628
+ ctx.builder
2629
+ .const_i32(global_pointers::get_reg32_offset(i as u32) as i32);
2630
+ ctx.builder.get_local(&ctx.register_locals[i]);
2631
+ ctx.builder.store_aligned_i32(0);
2632
+ }
2633
+ }
2634
+ pub fn gen_move_registers_from_memory_to_locals(ctx: &mut JitContext) {
2635
+ if cfg!(feature = "profiler") {
2636
+ let instruction = memory::read32s(ctx.start_of_current_instruction) as u32;
2637
+ opstats::gen_opstat_unguarded_register(ctx.builder, instruction);
2638
+ }
2639
+
2640
+ for i in 0..8 {
2641
+ ctx.builder
2642
+ .const_i32(global_pointers::get_reg32_offset(i as u32) as i32);
2643
+ ctx.builder.load_aligned_i32(0);
2644
+ ctx.builder.set_local(&ctx.register_locals[i]);
2645
+ }
2646
+ }
2647
+
2648
+ pub fn gen_profiler_stat_increment(builder: &mut WasmBuilder, stat: profiler::stat) {
2649
+ if !cfg!(feature = "profiler") {
2650
+ return;
2651
+ }
2652
+ let addr = unsafe { &raw mut profiler::stat_array[stat as usize] } as u32;
2653
+ builder.increment_fixed_i64(addr, 1)
2654
+ }
2655
+
2656
+ pub fn gen_debug_track_jit_exit(builder: &mut WasmBuilder, address: u32) {
2657
+ if cfg!(feature = "profiler") {
2658
+ gen_fn1_const(builder, "track_jit_exit", address);
2659
+ }
2660
+ }