codegen.rs 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382
  1. use global_pointers;
  2. use jit::JitContext;
  3. use jit::{GEN_LOCAL_SCRATCH0, GEN_LOCAL_SCRATCH1, GEN_LOCAL_SCRATCH2};
  4. use modrm;
  5. use tlb::{TLB_GLOBAL, TLB_NO_USER, TLB_READONLY, TLB_VALID};
  6. use wasmgen::module_init::WasmBuilder;
  7. use wasmgen::{module_init, wasm_util};
  8. pub fn gen_set_previous_eip_offset_from_eip(builder: &mut WasmBuilder, n: u32) {
  9. let cs = &mut builder.code_section;
  10. wasm_util::push_i32(cs, global_pointers::PREVIOUS_IP as i32); // store address of previous ip
  11. wasm_util::load_aligned_i32(cs, global_pointers::INSTRUCTION_POINTER); // load ip
  12. if n != 0 {
  13. wasm_util::push_i32(cs, n as i32);
  14. wasm_util::add_i32(cs); // add constant to ip value
  15. }
  16. wasm_util::store_aligned_i32(cs); // store it as previous ip
  17. }
  18. pub fn gen_increment_instruction_pointer(builder: &mut WasmBuilder, n: u32) {
  19. let cs = &mut builder.code_section;
  20. wasm_util::push_i32(cs, global_pointers::INSTRUCTION_POINTER as i32); // store address of ip
  21. wasm_util::load_aligned_i32(cs, global_pointers::INSTRUCTION_POINTER); // load ip
  22. wasm_util::push_i32(cs, n as i32);
  23. wasm_util::add_i32(cs);
  24. wasm_util::store_aligned_i32(cs); // store it back in
  25. }
  26. pub fn gen_set_previous_eip(builder: &mut WasmBuilder) {
  27. let cs = &mut builder.code_section;
  28. wasm_util::push_i32(cs, global_pointers::PREVIOUS_IP as i32); // store address of previous ip
  29. wasm_util::load_aligned_i32(cs, global_pointers::INSTRUCTION_POINTER); // load ip
  30. wasm_util::store_aligned_i32(cs); // store it as previous ip
  31. }
  32. pub fn gen_relative_jump(builder: &mut WasmBuilder, n: i32) {
  33. // add n to instruction_pointer (without setting the offset as above)
  34. let instruction_body = &mut builder.instruction_body;
  35. wasm_util::push_i32(
  36. instruction_body,
  37. global_pointers::INSTRUCTION_POINTER as i32,
  38. );
  39. wasm_util::load_aligned_i32(instruction_body, global_pointers::INSTRUCTION_POINTER);
  40. wasm_util::push_i32(instruction_body, n);
  41. wasm_util::add_i32(instruction_body);
  42. wasm_util::store_aligned_i32(instruction_body);
  43. }
  44. pub fn gen_increment_variable(builder: &mut WasmBuilder, variable_address: u32, n: i32) {
  45. wasm_util::increment_variable(&mut builder.code_section, variable_address, n);
  46. }
  47. pub fn gen_increment_timestamp_counter(builder: &mut WasmBuilder, n: i32) {
  48. gen_increment_variable(builder, global_pointers::TIMESTAMP_COUNTER, n);
  49. }
  50. pub fn gen_increment_mem32(builder: &mut WasmBuilder, addr: u32) {
  51. wasm_util::increment_mem32(&mut builder.code_section, addr)
  52. }
  53. pub fn gen_fn0_const(ctx: &mut JitContext, name: &str) {
  54. let builder = &mut ctx.builder;
  55. let fn_idx = builder.get_fn_idx(name, module_init::FN0_TYPE_INDEX);
  56. wasm_util::call_fn(&mut builder.instruction_body, fn_idx);
  57. }
  58. pub fn gen_fn0_const_ret(builder: &mut WasmBuilder, name: &str) {
  59. let fn_idx = builder.get_fn_idx(name, module_init::FN0_RET_TYPE_INDEX);
  60. wasm_util::call_fn(&mut builder.instruction_body, fn_idx);
  61. }
  62. pub fn gen_fn1_const(ctx: &mut JitContext, name: &str, arg0: u32) {
  63. let builder = &mut ctx.builder;
  64. let fn_idx = builder.get_fn_idx(name, module_init::FN1_TYPE_INDEX);
  65. wasm_util::push_i32(&mut builder.instruction_body, arg0 as i32);
  66. wasm_util::call_fn(&mut builder.instruction_body, fn_idx);
  67. }
  68. pub fn gen_call_fn1_ret(builder: &mut WasmBuilder, name: &str) {
  69. // generates: fn( _ ) where _ must be left on the stack before calling this, and fn returns a value
  70. let fn_idx = builder.get_fn_idx(name, module_init::FN1_RET_TYPE_INDEX);
  71. wasm_util::call_fn(&mut builder.instruction_body, fn_idx);
  72. }
  73. pub fn gen_fn2_const(ctx: &mut JitContext, name: &str, arg0: u32, arg1: u32) {
  74. let builder = &mut ctx.builder;
  75. let fn_idx = builder.get_fn_idx(name, module_init::FN2_TYPE_INDEX);
  76. wasm_util::push_i32(&mut builder.instruction_body, arg0 as i32);
  77. wasm_util::push_i32(&mut builder.instruction_body, arg1 as i32);
  78. wasm_util::call_fn(&mut builder.instruction_body, fn_idx);
  79. }
  80. pub fn gen_call_fn2(builder: &mut WasmBuilder, name: &str) {
  81. // generates: fn( _, _ ) where _ must be left on the stack before calling this
  82. let fn_idx = builder.get_fn_idx(name, module_init::FN2_TYPE_INDEX);
  83. wasm_util::call_fn(&mut builder.instruction_body, fn_idx);
  84. }
  85. pub fn gen_fn3_const(ctx: &mut JitContext, name: &str, arg0: u32, arg1: u32, arg2: u32) {
  86. let builder = &mut ctx.builder;
  87. let fn_idx = builder.get_fn_idx(name, module_init::FN3_TYPE_INDEX);
  88. wasm_util::push_i32(&mut builder.instruction_body, arg0 as i32);
  89. wasm_util::push_i32(&mut builder.instruction_body, arg1 as i32);
  90. wasm_util::push_i32(&mut builder.instruction_body, arg2 as i32);
  91. wasm_util::call_fn(&mut builder.instruction_body, fn_idx);
  92. }
  93. pub fn gen_modrm_fn0(ctx: &mut JitContext, name: &str) {
  94. // generates: fn( _ )
  95. let builder = &mut ctx.builder;
  96. let fn_idx = builder.get_fn_idx(name, module_init::FN1_TYPE_INDEX);
  97. wasm_util::call_fn(&mut builder.instruction_body, fn_idx);
  98. }
  99. pub fn gen_modrm_fn1(ctx: &mut JitContext, name: &str, arg0: u32) {
  100. // generates: fn( _, arg0 )
  101. let builder = &mut ctx.builder;
  102. let fn_idx = builder.get_fn_idx(name, module_init::FN2_TYPE_INDEX);
  103. wasm_util::push_i32(&mut builder.instruction_body, arg0 as i32);
  104. wasm_util::call_fn(&mut builder.instruction_body, fn_idx);
  105. }
  106. pub fn gen_modrm_fn2(ctx: &mut JitContext, name: &str, arg0: u32, arg1: u32) {
  107. // generates: fn( _, arg0, arg1 )
  108. let builder = &mut ctx.builder;
  109. let fn_idx = builder.get_fn_idx(name, module_init::FN3_TYPE_INDEX);
  110. wasm_util::push_i32(&mut builder.instruction_body, arg0 as i32);
  111. wasm_util::push_i32(&mut builder.instruction_body, arg1 as i32);
  112. wasm_util::call_fn(&mut builder.instruction_body, fn_idx);
  113. }
  114. pub fn gen_modrm_resolve(ctx: &mut JitContext, modrm_byte: u8) { modrm::gen(ctx, modrm_byte) }
  115. pub fn gen_set_reg16_r(ctx: &mut JitContext, dest: u32, src: u32) {
  116. // generates: reg16[r_dest] = reg16[r_src]
  117. let builder = &mut ctx.builder;
  118. wasm_util::push_i32(
  119. &mut builder.instruction_body,
  120. global_pointers::get_reg16_offset(dest) as i32,
  121. );
  122. wasm_util::load_aligned_u16(
  123. &mut builder.instruction_body,
  124. global_pointers::get_reg16_offset(src),
  125. );
  126. wasm_util::store_aligned_u16(&mut builder.instruction_body);
  127. }
  128. pub fn gen_set_reg32_r(ctx: &mut JitContext, dest: u32, src: u32) {
  129. // generates: reg32s[r_dest] = reg32s[r_src]
  130. let builder = &mut ctx.builder;
  131. wasm_util::push_i32(
  132. &mut builder.instruction_body,
  133. global_pointers::get_reg32_offset(dest) as i32,
  134. );
  135. wasm_util::load_aligned_i32(
  136. &mut builder.instruction_body,
  137. global_pointers::get_reg32_offset(src),
  138. );
  139. wasm_util::store_aligned_i32(&mut builder.instruction_body);
  140. }
  141. pub fn gen_set_reg16_fn0(ctx: &mut JitContext, name: &str, reg: u32) {
  142. // generates: reg16[reg] = fn()
  143. let builder = &mut ctx.builder;
  144. let fn_idx = builder.get_fn_idx(name, module_init::FN0_RET_TYPE_INDEX);
  145. wasm_util::push_i32(
  146. &mut builder.instruction_body,
  147. global_pointers::get_reg16_offset(reg) as i32,
  148. );
  149. wasm_util::call_fn(&mut builder.instruction_body, fn_idx);
  150. wasm_util::store_aligned_u16(&mut builder.instruction_body);
  151. }
  152. pub fn gen_set_reg32s_fn0(ctx: &mut JitContext, name: &str, reg: u32) {
  153. // generates: reg32s[reg] = fn()
  154. let builder = &mut ctx.builder;
  155. let fn_idx = builder.get_fn_idx(name, module_init::FN0_RET_TYPE_INDEX);
  156. wasm_util::push_i32(
  157. &mut builder.instruction_body,
  158. global_pointers::get_reg32_offset(reg) as i32,
  159. );
  160. wasm_util::call_fn(&mut builder.instruction_body, fn_idx);
  161. wasm_util::store_aligned_i32(&mut builder.instruction_body);
  162. }
  163. pub fn gen_safe_read32(ctx: &mut JitContext) {
  164. // Assumes virtual address has been pushed to the stack, and generates safe_read32s' fast-path
  165. // inline, bailing to safe_read32s_slow if necessary
  166. let builder = &mut ctx.builder;
  167. //let instruction_body = &mut ctx.builder.instruction_body;
  168. //let cpu = &mut ctx.cpu;
  169. let address_local = GEN_LOCAL_SCRATCH0;
  170. wasm_util::tee_local(&mut builder.instruction_body, address_local);
  171. // Pseudo: base_on_stack = (uint32_t)address >> 12;
  172. wasm_util::push_i32(&mut builder.instruction_body, 12);
  173. wasm_util::shr_u32(&mut builder.instruction_body);
  174. // scale index
  175. wasm_util::push_i32(&mut builder.instruction_body, 2);
  176. wasm_util::shl_i32(&mut builder.instruction_body);
  177. // Pseudo: entry = tlb_data[base_on_stack];
  178. let entry_local = GEN_LOCAL_SCRATCH1;
  179. wasm_util::load_aligned_i32_from_stack(
  180. &mut builder.instruction_body,
  181. global_pointers::TLB_DATA,
  182. );
  183. wasm_util::tee_local(&mut builder.instruction_body, entry_local);
  184. // Pseudo: bool can_use_fast_path = (entry & 0xFFF & ~TLB_READONLY & ~TLB_GLOBAL & ~(cpl == 3 ? 0 : TLB_NO_USER) == TLB_VALID &&
  185. // (address & 0xFFF) <= (0x1000 - 4));
  186. wasm_util::push_i32(
  187. &mut builder.instruction_body,
  188. (0xFFF & !TLB_READONLY & !TLB_GLOBAL & !(if ctx.cpu.cpl3() { 0 } else { TLB_NO_USER }))
  189. as i32,
  190. );
  191. wasm_util::and_i32(&mut builder.instruction_body);
  192. wasm_util::push_i32(&mut builder.instruction_body, TLB_VALID as i32);
  193. wasm_util::eq_i32(&mut builder.instruction_body);
  194. wasm_util::get_local(&mut builder.instruction_body, address_local);
  195. wasm_util::push_i32(&mut builder.instruction_body, 0xFFF);
  196. wasm_util::and_i32(&mut builder.instruction_body);
  197. wasm_util::push_i32(&mut builder.instruction_body, 0x1000 - 4);
  198. wasm_util::le_i32(&mut builder.instruction_body);
  199. wasm_util::and_i32(&mut builder.instruction_body);
  200. // Pseudo:
  201. // if(can_use_fast_path) leave_on_stack(mem8[entry & ~0xFFF ^ address]);
  202. wasm_util::if_i32(&mut builder.instruction_body);
  203. wasm_util::get_local(&mut builder.instruction_body, entry_local);
  204. wasm_util::push_i32(&mut builder.instruction_body, !0xFFF);
  205. wasm_util::and_i32(&mut builder.instruction_body);
  206. wasm_util::get_local(&mut builder.instruction_body, address_local);
  207. wasm_util::xor_i32(&mut builder.instruction_body);
  208. wasm_util::load_unaligned_i32_from_stack(
  209. &mut builder.instruction_body,
  210. global_pointers::MEMORY,
  211. );
  212. // Pseudo:
  213. // else { leave_on_stack(safe_read32s_slow(address)); }
  214. wasm_util::else_(&mut builder.instruction_body);
  215. wasm_util::get_local(&mut builder.instruction_body, address_local);
  216. gen_call_fn1_ret(builder, "safe_read32s_slow");
  217. wasm_util::block_end(&mut builder.instruction_body);
  218. }
  219. pub fn gen_safe_write32(ctx: &mut JitContext, local_for_address: u32, local_for_value: u32) {
  220. // Generates safe_write32' fast-path inline, bailing to safe_write32_slow if necessary.
  221. // local_for_{address,value} are the numbers of the local variables which contain the virtual
  222. // address and value for safe_write32
  223. // Usage:
  224. // set_local(0, value);
  225. // set_local(1, v_addr);
  226. // gen_safe_write32(0, 1);
  227. // Since this function clobbers other variables, we confirm that the caller uses the local
  228. // variables we expect them to
  229. assert!(local_for_address == GEN_LOCAL_SCRATCH0);
  230. assert!(local_for_value == GEN_LOCAL_SCRATCH1);
  231. let builder = &mut ctx.builder;
  232. //let instruction_body = &mut ctx.builder.instruction_body;
  233. //let cpu = &mut ctx.cpu;
  234. wasm_util::get_local(&mut builder.instruction_body, local_for_address);
  235. // Pseudo: base_on_stack = (uint32_t)address >> 12;
  236. wasm_util::push_i32(&mut builder.instruction_body, 12);
  237. wasm_util::shr_u32(&mut builder.instruction_body);
  238. // scale index
  239. wasm_util::push_i32(&mut builder.instruction_body, 2);
  240. wasm_util::shl_i32(&mut builder.instruction_body);
  241. // entry_local is only used in the following block, so the scratch variable can be reused later
  242. {
  243. // Pseudo: entry = tlb_data[base_on_stack];
  244. let entry_local = GEN_LOCAL_SCRATCH2;
  245. wasm_util::load_aligned_i32_from_stack(
  246. &mut builder.instruction_body,
  247. global_pointers::TLB_DATA,
  248. );
  249. wasm_util::tee_local(&mut builder.instruction_body, entry_local);
  250. // Pseudo: bool can_use_fast_path = (entry & 0xFFF & ~TLB_GLOBAL & ~(cpl == 3 ? 0 : TLB_NO_USER) == TLB_VALID &&
  251. // (address & 0xFFF) <= (0x1000 - 4));
  252. wasm_util::push_i32(
  253. &mut builder.instruction_body,
  254. (0xFFF & !TLB_GLOBAL & !(if ctx.cpu.cpl3() { 0 } else { TLB_NO_USER })) as i32,
  255. );
  256. wasm_util::and_i32(&mut builder.instruction_body);
  257. wasm_util::push_i32(&mut builder.instruction_body, TLB_VALID as i32);
  258. wasm_util::eq_i32(&mut builder.instruction_body);
  259. wasm_util::get_local(&mut builder.instruction_body, local_for_address);
  260. wasm_util::push_i32(&mut builder.instruction_body, 0xFFF);
  261. wasm_util::and_i32(&mut builder.instruction_body);
  262. wasm_util::push_i32(&mut builder.instruction_body, 0x1000 - 4);
  263. wasm_util::le_i32(&mut builder.instruction_body);
  264. wasm_util::and_i32(&mut builder.instruction_body);
  265. // Pseudo:
  266. // if(can_use_fast_path)
  267. // {
  268. // phys_addr = entry & ~0xFFF ^ address;
  269. wasm_util::if_void(&mut builder.instruction_body);
  270. wasm_util::get_local(&mut builder.instruction_body, entry_local);
  271. wasm_util::push_i32(&mut builder.instruction_body, !0xFFF);
  272. wasm_util::and_i32(&mut builder.instruction_body);
  273. wasm_util::get_local(&mut builder.instruction_body, local_for_address);
  274. wasm_util::xor_i32(&mut builder.instruction_body);
  275. }
  276. // entry_local isn't needed anymore, so we overwrite it
  277. let phys_addr_local = GEN_LOCAL_SCRATCH2;
  278. // Pseudo:
  279. // /* continued within can_use_fast_path branch */
  280. // mem8[phys_addr] = value;
  281. wasm_util::tee_local(&mut builder.instruction_body, phys_addr_local);
  282. wasm_util::get_local(&mut builder.instruction_body, local_for_value);
  283. wasm_util::store_unaligned_i32(&mut builder.instruction_body, global_pointers::MEMORY);
  284. // Pseudo:
  285. // else { safe_read32_slow(address, value); }
  286. wasm_util::else_(&mut builder.instruction_body);
  287. wasm_util::get_local(&mut builder.instruction_body, local_for_address);
  288. wasm_util::get_local(&mut builder.instruction_body, local_for_value);
  289. gen_call_fn2(builder, "safe_write32_slow");
  290. wasm_util::block_end(&mut builder.instruction_body);
  291. }
  292. pub fn gen_fn1_reg16(ctx: &mut JitContext, name: &str, r: u32) {
  293. let fn_idx = ctx.builder.get_fn_idx(name, module_init::FN1_TYPE_INDEX);
  294. wasm_util::load_aligned_u16(
  295. &mut ctx.builder.instruction_body,
  296. global_pointers::get_reg16_offset(r),
  297. );
  298. wasm_util::call_fn(&mut ctx.builder.instruction_body, fn_idx)
  299. }
  300. pub fn gen_fn1_reg32(ctx: &mut JitContext, name: &str, r: u32) {
  301. let fn_idx = ctx.builder.get_fn_idx(name, module_init::FN1_TYPE_INDEX);
  302. wasm_util::load_aligned_i32(
  303. &mut ctx.builder.instruction_body,
  304. global_pointers::get_reg32_offset(r),
  305. );
  306. wasm_util::call_fn(&mut ctx.builder.instruction_body, fn_idx)
  307. }
  308. pub fn gen_clear_prefixes(ctx: &mut JitContext) {
  309. let instruction_body = &mut ctx.builder.instruction_body;
  310. wasm_util::push_i32(instruction_body, global_pointers::PREFIXES as i32); // load address of prefixes
  311. wasm_util::push_i32(instruction_body, 0);
  312. wasm_util::store_aligned_i32(instruction_body);
  313. }
  314. pub fn gen_add_prefix_bits(ctx: &mut JitContext, mask: u32) {
  315. assert!(mask < 0x100);
  316. let instruction_body = &mut ctx.builder.instruction_body;
  317. wasm_util::push_i32(instruction_body, global_pointers::PREFIXES as i32); // load address of prefixes
  318. wasm_util::load_aligned_i32(instruction_body, global_pointers::PREFIXES); // load old value
  319. wasm_util::push_i32(instruction_body, mask as i32);
  320. wasm_util::or_i32(instruction_body);
  321. wasm_util::store_aligned_i32(instruction_body);
  322. }