jit.rs 49 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476
  1. use std::collections::{BTreeMap, HashMap, HashSet};
  2. use std::iter::FromIterator;
  3. use std::mem;
  4. use std::ptr::NonNull;
  5. use analysis::AnalysisType;
  6. use codegen;
  7. use cpu::cpu;
  8. use cpu::global_pointers;
  9. use cpu::memory;
  10. use cpu_context::CpuContext;
  11. use jit_instructions;
  12. use page::Page;
  13. use profiler;
  14. use profiler::stat;
  15. use state_flags::CachedStateFlags;
  16. use util::SafeToU16;
  17. use wasmgen::wasm_builder::{Label, WasmBuilder, WasmLocal};
  18. #[derive(Copy, Clone, Eq, Hash, PartialEq)]
  19. #[repr(transparent)]
  20. pub struct WasmTableIndex(u16);
  21. impl WasmTableIndex {
  22. pub fn to_u16(self) -> u16 { self.0 }
  23. }
  24. mod unsafe_jit {
  25. use jit::{CachedStateFlags, WasmTableIndex};
  26. extern "C" {
  27. pub fn codegen_finalize(
  28. wasm_table_index: WasmTableIndex,
  29. phys_addr: u32,
  30. state_flags: CachedStateFlags,
  31. ptr: u32,
  32. len: u32,
  33. );
  34. pub fn jit_clear_func(wasm_table_index: WasmTableIndex);
  35. }
  36. }
  37. fn codegen_finalize(
  38. wasm_table_index: WasmTableIndex,
  39. phys_addr: u32,
  40. state_flags: CachedStateFlags,
  41. ptr: u32,
  42. len: u32,
  43. ) {
  44. unsafe { unsafe_jit::codegen_finalize(wasm_table_index, phys_addr, state_flags, ptr, len) }
  45. }
  46. pub fn jit_clear_func(wasm_table_index: WasmTableIndex) {
  47. unsafe { unsafe_jit::jit_clear_func(wasm_table_index) }
  48. }
  49. pub const WASM_TABLE_SIZE: u32 = 900;
  50. pub const HASH_PRIME: u32 = 6151;
  51. pub const CHECK_JIT_STATE_INVARIANTS: bool = false;
  52. pub const JIT_MAX_ITERATIONS_PER_FUNCTION: u32 = 20011;
  53. pub const JIT_ALWAYS_USE_LOOP_SAFETY: bool = true;
  54. pub const JIT_THRESHOLD: u32 = 200 * 1000;
  55. const MAX_INSTRUCTION_LENGTH: u32 = 16;
  56. #[allow(non_upper_case_globals)]
  57. static mut jit_state: NonNull<JitState> =
  58. unsafe { NonNull::new_unchecked(mem::align_of::<JitState>() as *mut _) };
  59. pub fn get_jit_state() -> &'static mut JitState { unsafe { jit_state.as_mut() } }
  60. #[no_mangle]
  61. pub fn rust_init() {
  62. let x = Box::new(JitState::create_and_initialise());
  63. unsafe {
  64. jit_state = NonNull::new(Box::into_raw(x)).unwrap()
  65. }
  66. use std::panic;
  67. panic::set_hook(Box::new(|panic_info| {
  68. console_log!("{}", panic_info.to_string());
  69. }));
  70. }
  71. pub struct Entry {
  72. #[cfg(any(debug_assertions, feature = "profiler"))]
  73. pub len: u32,
  74. #[cfg(debug_assertions)]
  75. pub opcode: u32,
  76. pub initial_state: u16,
  77. pub wasm_table_index: WasmTableIndex,
  78. pub state_flags: CachedStateFlags,
  79. }
  80. enum PageState {
  81. Compiling { basic_blocks: Vec<BasicBlock> },
  82. CompilingWritten,
  83. }
  84. pub struct JitState {
  85. wasm_builder: WasmBuilder,
  86. // as an alternative to HashSet, we could use a bitmap of 4096 bits here
  87. // (faster, but uses much more memory)
  88. // or a compressed bitmap (likely faster)
  89. // or HashSet<u32> rather than nested
  90. entry_points: HashMap<Page, HashSet<u16>>,
  91. hot_pages: [u32; HASH_PRIME as usize],
  92. wasm_table_index_free_list: Vec<WasmTableIndex>,
  93. used_wasm_table_indices: HashMap<WasmTableIndex, HashSet<Page>>,
  94. // All pages from used_wasm_table_indices
  95. // Used to improve the performance of jit_dirty_page and jit_page_has_code
  96. all_pages: HashSet<Page>,
  97. cache: HashMap<u32, Entry>,
  98. compiling: Option<(WasmTableIndex, PageState)>,
  99. }
  100. pub fn check_jit_state_invariants(ctx: &mut JitState) {
  101. if !CHECK_JIT_STATE_INVARIANTS {
  102. return;
  103. }
  104. let mut all_pages = HashSet::new();
  105. for pages in ctx.used_wasm_table_indices.values() {
  106. all_pages.extend(pages);
  107. }
  108. dbg_assert!(ctx.all_pages == all_pages);
  109. }
  110. impl JitState {
  111. pub fn create_and_initialise() -> JitState {
  112. // don't assign 0 (XXX: Check)
  113. let wasm_table_indices = (1..=(WASM_TABLE_SIZE - 1) as u16).map(|x| WasmTableIndex(x));
  114. JitState {
  115. wasm_builder: WasmBuilder::new(),
  116. entry_points: HashMap::new(),
  117. hot_pages: [0; HASH_PRIME as usize],
  118. wasm_table_index_free_list: Vec::from_iter(wasm_table_indices),
  119. used_wasm_table_indices: HashMap::new(),
  120. all_pages: HashSet::new(),
  121. cache: HashMap::new(),
  122. compiling: None,
  123. }
  124. }
  125. }
  126. #[derive(PartialEq, Eq)]
  127. enum BasicBlockType {
  128. Normal {
  129. next_block_addr: u32,
  130. },
  131. ConditionalJump {
  132. next_block_addr: Option<u32>,
  133. next_block_branch_taken_addr: Option<u32>,
  134. condition: u8,
  135. jump_offset: i32,
  136. jump_offset_is_32: bool,
  137. },
  138. Exit,
  139. }
  140. struct BasicBlock {
  141. addr: u32,
  142. virt_addr: i32,
  143. last_instruction_addr: u32,
  144. end_addr: u32,
  145. is_entry_block: bool,
  146. ty: BasicBlockType,
  147. has_sti: bool,
  148. number_of_instructions: u32,
  149. }
  150. #[derive(Copy, Clone, PartialEq)]
  151. pub struct CachedCode {
  152. pub wasm_table_index: WasmTableIndex,
  153. pub initial_state: u16,
  154. }
  155. impl CachedCode {
  156. pub const NONE: CachedCode = CachedCode {
  157. wasm_table_index: WasmTableIndex(0),
  158. initial_state: 0,
  159. };
  160. }
  161. pub struct JitContext<'a> {
  162. pub cpu: &'a mut CpuContext,
  163. pub builder: &'a mut WasmBuilder,
  164. pub register_locals: &'a mut Vec<WasmLocal>,
  165. pub start_of_current_instruction: u32,
  166. pub main_loop_label: Label,
  167. pub exit_with_fault_label: Label,
  168. pub exit_label: Label,
  169. pub our_wasm_table_index: WasmTableIndex,
  170. pub basic_block_index_local: &'a WasmLocal,
  171. pub state_flags: CachedStateFlags,
  172. }
  173. pub const JIT_INSTR_BLOCK_BOUNDARY_FLAG: u32 = 1 << 0;
  174. fn jit_hot_hash_page(page: Page) -> u32 { page.to_u32() % HASH_PRIME }
  175. pub fn is_near_end_of_page(address: u32) -> bool {
  176. address & 0xFFF >= 0x1000 - MAX_INSTRUCTION_LENGTH
  177. }
  178. pub fn jit_find_cache_entry(phys_address: u32, state_flags: CachedStateFlags) -> CachedCode {
  179. if is_near_end_of_page(phys_address) {
  180. profiler::stat_increment(stat::RUN_INTERPRETED_NEAR_END_OF_PAGE);
  181. }
  182. let ctx = get_jit_state();
  183. match ctx.cache.get(&phys_address) {
  184. Some(entry) => {
  185. if entry.state_flags == state_flags {
  186. return CachedCode {
  187. wasm_table_index: entry.wasm_table_index,
  188. initial_state: entry.initial_state,
  189. };
  190. }
  191. else {
  192. profiler::stat_increment(stat::RUN_INTERPRETED_DIFFERENT_STATE);
  193. }
  194. },
  195. None => {},
  196. }
  197. return CachedCode::NONE;
  198. }
  199. #[no_mangle]
  200. pub fn jit_find_cache_entry_in_page(
  201. phys_address: u32,
  202. wasm_table_index: WasmTableIndex,
  203. state_flags: u32,
  204. ) -> i32 {
  205. let state_flags = CachedStateFlags::of_u32(state_flags);
  206. let ctx = get_jit_state();
  207. match ctx.cache.get(&phys_address) {
  208. Some(entry) => {
  209. if entry.state_flags == state_flags && entry.wasm_table_index == wasm_table_index {
  210. return entry.initial_state as i32;
  211. }
  212. },
  213. None => {},
  214. }
  215. return -1;
  216. }
  217. pub fn record_entry_point(phys_address: u32) {
  218. let ctx = get_jit_state();
  219. if is_near_end_of_page(phys_address) {
  220. return;
  221. }
  222. let page = Page::page_of(phys_address);
  223. let offset_in_page = phys_address as u16 & 0xFFF;
  224. let mut is_new = false;
  225. ctx.entry_points
  226. .entry(page)
  227. .or_insert_with(|| {
  228. is_new = true;
  229. HashSet::new()
  230. })
  231. .insert(offset_in_page);
  232. if is_new {
  233. cpu::tlb_set_has_code(page, true);
  234. }
  235. }
  236. // Maximum number of pages per wasm module. Necessary for the following reasons:
  237. // - There is an upper limit on the size of a single function in wasm (currently ~7MB in all browsers)
  238. // See https://github.com/WebAssembly/design/issues/1138
  239. // - v8 poorly handles large br_table elements and OOMs on modules much smaller than the above limit
  240. // See https://bugs.chromium.org/p/v8/issues/detail?id=9697 and https://bugs.chromium.org/p/v8/issues/detail?id=9141
  241. // Will hopefully be fixed in the near future by generating direct control flow
  242. const MAX_PAGES: usize = 5;
  243. fn jit_find_basic_blocks(
  244. ctx: &mut JitState,
  245. entry_points: HashSet<i32>,
  246. cpu: CpuContext,
  247. ) -> Vec<BasicBlock> {
  248. let mut to_visit_stack: Vec<i32> = entry_points.iter().map(|e| *e).collect();
  249. let mut marked_as_entry: HashSet<i32> = entry_points.clone();
  250. let mut basic_blocks: BTreeMap<u32, BasicBlock> = BTreeMap::new();
  251. let mut pages: HashSet<Page> = HashSet::new();
  252. // 16-bit doesn't not work correctly, most likely due to instruction pointer wrap-around
  253. let max_pages = if cpu.state_flags.is_32() { MAX_PAGES } else { 1 };
  254. while let Some(to_visit) = to_visit_stack.pop() {
  255. let phys_addr = match cpu::translate_address_read_no_side_effects(to_visit) {
  256. None => {
  257. dbg_log!("Not analysing {:x} (page not mapped)", to_visit);
  258. continue;
  259. },
  260. Some(phys_addr) => phys_addr,
  261. };
  262. if basic_blocks.contains_key(&phys_addr) {
  263. continue;
  264. }
  265. pages.insert(Page::page_of(phys_addr));
  266. dbg_assert!(pages.len() <= max_pages);
  267. let may_include_page = |page| pages.contains(&page) || pages.len() < max_pages;
  268. if let Some(entry_points) = ctx.entry_points.remove(&Page::page_of(phys_addr)) {
  269. let address_hash = jit_hot_hash_page(Page::page_of(phys_addr)) as usize;
  270. ctx.hot_pages[address_hash] = 0;
  271. for addr_low in entry_points {
  272. let addr = to_visit & !0xFFF | addr_low as i32;
  273. to_visit_stack.push(addr);
  274. marked_as_entry.insert(addr);
  275. }
  276. }
  277. if is_near_end_of_page(phys_addr) {
  278. // Empty basic block, don't insert
  279. profiler::stat_increment(stat::COMPILE_CUT_OFF_AT_END_OF_PAGE);
  280. continue;
  281. }
  282. let mut current_address = phys_addr;
  283. let mut current_block = BasicBlock {
  284. addr: current_address,
  285. virt_addr: to_visit,
  286. last_instruction_addr: 0,
  287. end_addr: 0,
  288. ty: BasicBlockType::Exit,
  289. is_entry_block: false,
  290. has_sti: false,
  291. number_of_instructions: 0,
  292. };
  293. loop {
  294. let addr_before_instruction = current_address;
  295. let mut ctx = &mut CpuContext {
  296. eip: current_address,
  297. ..cpu
  298. };
  299. let analysis = ::analysis::analyze_step(&mut ctx);
  300. current_block.number_of_instructions += 1;
  301. let has_next_instruction = !analysis.no_next_instruction;
  302. current_address = ctx.eip;
  303. dbg_assert!(Page::page_of(current_address) == Page::page_of(addr_before_instruction));
  304. let current_virt_addr = to_visit & !0xFFF | current_address as i32 & 0xFFF;
  305. match analysis.ty {
  306. AnalysisType::Normal | AnalysisType::STI => {
  307. dbg_assert!(has_next_instruction);
  308. if current_block.has_sti {
  309. // Convert next instruction after STI (i.e., the current instruction) into block boundary
  310. marked_as_entry.insert(current_virt_addr);
  311. to_visit_stack.push(current_virt_addr);
  312. current_block.last_instruction_addr = addr_before_instruction;
  313. current_block.end_addr = current_address;
  314. break;
  315. }
  316. if analysis.ty == AnalysisType::STI {
  317. current_block.has_sti = true;
  318. dbg_assert!(
  319. !is_near_end_of_page(current_address),
  320. "TODO: Handle STI instruction near end of page"
  321. );
  322. }
  323. else {
  324. // Only split non-STI blocks (one instruction needs to run after STI before
  325. // handle_irqs may be called)
  326. if basic_blocks.contains_key(&current_address) {
  327. current_block.last_instruction_addr = addr_before_instruction;
  328. current_block.end_addr = current_address;
  329. dbg_assert!(!is_near_end_of_page(current_address));
  330. current_block.ty = BasicBlockType::Normal {
  331. next_block_addr: current_address,
  332. };
  333. break;
  334. }
  335. }
  336. },
  337. AnalysisType::Jump {
  338. offset,
  339. is_32,
  340. condition: Some(condition),
  341. } => {
  342. // conditional jump: continue at next and continue at jump target
  343. let jump_target = if is_32 {
  344. current_virt_addr + offset
  345. }
  346. else {
  347. ctx.cs_offset as i32
  348. + (current_virt_addr - ctx.cs_offset as i32 + offset & 0xFFFF)
  349. };
  350. dbg_assert!(has_next_instruction);
  351. to_visit_stack.push(current_virt_addr);
  352. let next_block_branch_taken_addr;
  353. if let Some(phys_jump_target) =
  354. cpu::translate_address_read_no_side_effects(jump_target as i32)
  355. {
  356. if !is_near_end_of_page(jump_target as u32)
  357. && may_include_page(Page::page_of(phys_jump_target))
  358. {
  359. pages.insert(Page::page_of(phys_jump_target));
  360. to_visit_stack.push(jump_target);
  361. next_block_branch_taken_addr = Some(phys_jump_target);
  362. }
  363. else {
  364. next_block_branch_taken_addr = None;
  365. }
  366. }
  367. else {
  368. next_block_branch_taken_addr = None;
  369. }
  370. let next_block_addr = if is_near_end_of_page(current_address) {
  371. None
  372. }
  373. else {
  374. Some(current_address)
  375. };
  376. current_block.ty = BasicBlockType::ConditionalJump {
  377. next_block_addr,
  378. next_block_branch_taken_addr,
  379. condition,
  380. jump_offset: offset,
  381. jump_offset_is_32: is_32,
  382. };
  383. current_block.last_instruction_addr = addr_before_instruction;
  384. current_block.end_addr = current_address;
  385. break;
  386. },
  387. AnalysisType::Jump {
  388. offset,
  389. is_32,
  390. condition: None,
  391. } => {
  392. // non-conditional jump: continue at jump target
  393. let jump_target = if is_32 {
  394. current_virt_addr + offset
  395. }
  396. else {
  397. ctx.cs_offset as i32
  398. + (current_virt_addr - ctx.cs_offset as i32 + offset & 0xFFFF)
  399. };
  400. if has_next_instruction {
  401. // Execution will eventually come back to the next instruction (CALL)
  402. marked_as_entry.insert(current_virt_addr);
  403. to_visit_stack.push(current_virt_addr);
  404. }
  405. if let Some(phys_jump_target) =
  406. cpu::translate_address_read_no_side_effects(jump_target as i32)
  407. {
  408. if !is_near_end_of_page(jump_target as u32)
  409. && may_include_page(Page::page_of(phys_jump_target))
  410. {
  411. pages.insert(Page::page_of(phys_jump_target));
  412. to_visit_stack.push(jump_target);
  413. current_block.ty = BasicBlockType::Normal {
  414. next_block_addr: phys_jump_target,
  415. };
  416. }
  417. else {
  418. current_block.ty = BasicBlockType::Exit;
  419. }
  420. }
  421. else {
  422. current_block.ty = BasicBlockType::Exit;
  423. }
  424. current_block.last_instruction_addr = addr_before_instruction;
  425. current_block.end_addr = current_address;
  426. break;
  427. },
  428. AnalysisType::BlockBoundary => {
  429. // a block boundary but not a jump, get out
  430. if has_next_instruction {
  431. // block boundary, but execution will eventually come back
  432. // to the next instruction. Create a new basic block
  433. // starting at the next instruction and register it as an
  434. // entry point
  435. marked_as_entry.insert(current_virt_addr);
  436. to_visit_stack.push(current_virt_addr);
  437. }
  438. current_block.last_instruction_addr = addr_before_instruction;
  439. current_block.end_addr = current_address;
  440. break;
  441. },
  442. }
  443. if is_near_end_of_page(current_address) {
  444. current_block.last_instruction_addr = addr_before_instruction;
  445. current_block.end_addr = current_address;
  446. profiler::stat_increment(stat::COMPILE_CUT_OFF_AT_END_OF_PAGE);
  447. break;
  448. }
  449. }
  450. let previous_block = basic_blocks
  451. .range(..current_block.addr)
  452. .next_back()
  453. .filter(|(_, previous_block)| (!previous_block.has_sti))
  454. .map(|(_, previous_block)| previous_block.clone());
  455. if let Some(previous_block) = previous_block {
  456. if current_block.addr < previous_block.end_addr {
  457. // If this block overlaps with the previous block, re-analyze the previous block
  458. to_visit_stack.push(previous_block.virt_addr);
  459. let addr = previous_block.addr;
  460. let old_block = basic_blocks.remove(&addr);
  461. dbg_assert!(old_block.is_some());
  462. // Note that this does not ensure the invariant that two consecutive blocks don't
  463. // overlay. For that, we also need to check the following block.
  464. }
  465. }
  466. dbg_assert!(current_block.addr < current_block.end_addr);
  467. dbg_assert!(current_block.addr <= current_block.last_instruction_addr);
  468. dbg_assert!(current_block.last_instruction_addr < current_block.end_addr);
  469. basic_blocks.insert(current_block.addr, current_block);
  470. }
  471. dbg_assert!(pages.len() <= max_pages);
  472. for block in basic_blocks.values_mut() {
  473. if marked_as_entry.contains(&block.virt_addr) {
  474. block.is_entry_block = true;
  475. }
  476. }
  477. let basic_blocks: Vec<BasicBlock> = basic_blocks.into_iter().map(|(_, block)| block).collect();
  478. for i in 0..basic_blocks.len() - 1 {
  479. let next_block_addr = basic_blocks[i + 1].addr;
  480. let next_block_end_addr = basic_blocks[i + 1].end_addr;
  481. let next_block_is_entry = basic_blocks[i + 1].is_entry_block;
  482. let block = &basic_blocks[i];
  483. dbg_assert!(block.addr < next_block_addr);
  484. if next_block_addr < block.end_addr {
  485. dbg_log!(
  486. "Overlapping first=[from={:x} to={:x} is_entry={}] second=[from={:x} to={:x} is_entry={}]",
  487. block.addr,
  488. block.end_addr,
  489. block.is_entry_block as u8,
  490. next_block_addr,
  491. next_block_end_addr,
  492. next_block_is_entry as u8
  493. );
  494. }
  495. }
  496. basic_blocks
  497. }
  498. #[no_mangle]
  499. #[cfg(debug_assertions)]
  500. pub fn jit_force_generate_unsafe(virt_addr: i32) {
  501. let ctx = get_jit_state();
  502. let phys_addr = cpu::translate_address_read(virt_addr).unwrap();
  503. record_entry_point(phys_addr);
  504. let cs_offset = cpu::get_seg_cs() as u32;
  505. let state_flags = cpu::pack_current_state_flags();
  506. jit_analyze_and_generate(ctx, virt_addr, phys_addr, cs_offset, state_flags);
  507. }
  508. #[inline(never)]
  509. fn jit_analyze_and_generate(
  510. ctx: &mut JitState,
  511. virt_entry_point: i32,
  512. phys_entry_point: u32,
  513. cs_offset: u32,
  514. state_flags: CachedStateFlags,
  515. ) {
  516. let page = Page::page_of(phys_entry_point);
  517. if ctx.compiling.is_some() {
  518. return;
  519. }
  520. let entry_points = ctx.entry_points.remove(&page);
  521. if let Some(entry_points) = entry_points {
  522. dbg_log!("Compile code for page at {:x}", page.to_address());
  523. profiler::stat_increment(stat::COMPILE);
  524. let cpu = CpuContext {
  525. eip: 0,
  526. prefixes: 0,
  527. cs_offset,
  528. state_flags,
  529. };
  530. dbg_assert!(
  531. cpu::translate_address_read_no_side_effects(virt_entry_point).unwrap()
  532. == phys_entry_point
  533. );
  534. let virt_page = Page::page_of(virt_entry_point as u32);
  535. let entry_points: HashSet<i32> = entry_points
  536. .iter()
  537. .map(|e| virt_page.to_address() as i32 | *e as i32)
  538. .collect();
  539. let basic_blocks = jit_find_basic_blocks(ctx, entry_points, cpu.clone());
  540. let mut pages = HashSet::new();
  541. for b in basic_blocks.iter() {
  542. // Remove this assertion once page-crossing jit is enabled
  543. dbg_assert!(Page::page_of(b.addr) == Page::page_of(b.end_addr));
  544. pages.insert(Page::page_of(b.addr));
  545. }
  546. if ctx.wasm_table_index_free_list.is_empty() {
  547. dbg_log!("wasm_table_index_free_list empty, clearing cache",);
  548. // When no free slots are available, delete all cached modules. We could increase the
  549. // size of the table, but this way the initial size acts as an upper bound for the
  550. // number of wasm modules that we generate, which we want anyway to avoid getting our
  551. // tab killed by browsers due to memory constraints.
  552. jit_clear_cache(ctx);
  553. profiler::stat_increment(stat::INVALIDATE_ALL_MODULES_NO_FREE_WASM_INDICES);
  554. dbg_log!(
  555. "after jit_clear_cache: {} free",
  556. ctx.wasm_table_index_free_list.len(),
  557. );
  558. // This assertion can fail if all entries are pending (not possible unless
  559. // WASM_TABLE_SIZE is set very low)
  560. dbg_assert!(!ctx.wasm_table_index_free_list.is_empty());
  561. }
  562. // allocate an index in the wasm table
  563. let wasm_table_index = ctx
  564. .wasm_table_index_free_list
  565. .pop()
  566. .expect("allocate wasm table index");
  567. dbg_assert!(wasm_table_index != WasmTableIndex(0));
  568. dbg_assert!(!pages.is_empty());
  569. dbg_assert!(pages.len() <= MAX_PAGES);
  570. ctx.used_wasm_table_indices
  571. .insert(wasm_table_index, pages.clone());
  572. ctx.all_pages.extend(pages.clone());
  573. jit_generate_module(
  574. &basic_blocks,
  575. cpu.clone(),
  576. &mut ctx.wasm_builder,
  577. wasm_table_index,
  578. state_flags,
  579. );
  580. profiler::stat_increment_by(
  581. stat::COMPILE_WASM_TOTAL_BYTES,
  582. ctx.wasm_builder.get_output_len() as u64,
  583. );
  584. profiler::stat_increment_by(stat::COMPILE_PAGE, pages.len() as u64);
  585. for &p in &pages {
  586. cpu::tlb_set_has_code(p, true);
  587. }
  588. dbg_assert!(ctx.compiling.is_none());
  589. ctx.compiling = Some((wasm_table_index, PageState::Compiling { basic_blocks }));
  590. let phys_addr = page.to_address();
  591. // will call codegen_finalize_finished asynchronously when finished
  592. codegen_finalize(
  593. wasm_table_index,
  594. phys_addr,
  595. state_flags,
  596. ctx.wasm_builder.get_output_ptr() as u32,
  597. ctx.wasm_builder.get_output_len(),
  598. );
  599. profiler::stat_increment(stat::COMPILE_SUCCESS);
  600. }
  601. else {
  602. //dbg_log!("No basic blocks, not generating code");
  603. // Nothing to do
  604. }
  605. check_jit_state_invariants(ctx);
  606. }
  607. #[no_mangle]
  608. pub fn codegen_finalize_finished(
  609. wasm_table_index: WasmTableIndex,
  610. phys_addr: u32,
  611. state_flags: CachedStateFlags,
  612. ) {
  613. let ctx = get_jit_state();
  614. dbg_assert!(wasm_table_index != WasmTableIndex(0));
  615. dbg_log!(
  616. "Finished compiling for page at {:x}",
  617. Page::page_of(phys_addr).to_address()
  618. );
  619. let basic_blocks = match mem::replace(&mut ctx.compiling, None) {
  620. None => {
  621. dbg_assert!(false);
  622. return;
  623. },
  624. Some((in_progress_wasm_table_index, PageState::CompilingWritten)) => {
  625. dbg_assert!(wasm_table_index == in_progress_wasm_table_index);
  626. profiler::stat_increment(stat::INVALIDATE_MODULE_WRITTEN_WHILE_COMPILED);
  627. free_wasm_table_index(ctx, wasm_table_index);
  628. return;
  629. },
  630. Some((in_progress_wasm_table_index, PageState::Compiling { basic_blocks })) => {
  631. dbg_assert!(wasm_table_index == in_progress_wasm_table_index);
  632. basic_blocks
  633. },
  634. };
  635. // create entries for each basic block that is marked as an entry point
  636. let mut entry_point_count = 0;
  637. let mut check_for_unused_wasm_table_index = HashSet::new();
  638. for (i, block) in basic_blocks.iter().enumerate() {
  639. profiler::stat_increment(stat::COMPILE_BASIC_BLOCK);
  640. dbg_assert!(block.addr < block.end_addr);
  641. if block.is_entry_block {
  642. let initial_state = i.safe_to_u16();
  643. let entry = Entry {
  644. wasm_table_index,
  645. initial_state,
  646. state_flags,
  647. #[cfg(any(debug_assertions, feature = "profiler"))]
  648. len: block.end_addr - block.addr,
  649. #[cfg(debug_assertions)]
  650. opcode: memory::read32s(block.addr) as u32,
  651. };
  652. let maybe_old_entry = ctx.cache.insert(block.addr, entry);
  653. if let Some(old_entry) = maybe_old_entry {
  654. check_for_unused_wasm_table_index.insert(old_entry.wasm_table_index);
  655. if old_entry.state_flags == state_flags {
  656. // TODO: stat
  657. }
  658. else {
  659. // TODO: stat
  660. }
  661. }
  662. entry_point_count += 1;
  663. profiler::stat_increment(stat::COMPILE_ENTRY_POINT);
  664. }
  665. }
  666. dbg_assert!(entry_point_count > 0);
  667. for index in check_for_unused_wasm_table_index {
  668. let pages = ctx.used_wasm_table_indices.get(&index).unwrap();
  669. let mut is_used = false;
  670. 'outer: for p in pages {
  671. for addr in p.address_range() {
  672. if let Some(entry) = ctx.cache.get(&addr) {
  673. if entry.wasm_table_index == index {
  674. is_used = true;
  675. break 'outer;
  676. }
  677. }
  678. }
  679. }
  680. if !is_used {
  681. profiler::stat_increment(stat::INVALIDATE_MODULE_UNUSED_AFTER_OVERWRITE);
  682. free_wasm_table_index(ctx, index);
  683. }
  684. if !is_used {
  685. for (_, entry) in &ctx.cache {
  686. dbg_assert!(entry.wasm_table_index != index);
  687. }
  688. }
  689. else {
  690. let mut ok = false;
  691. for (_, entry) in &ctx.cache {
  692. if entry.wasm_table_index == index {
  693. ok = true;
  694. break;
  695. }
  696. }
  697. dbg_assert!(ok);
  698. }
  699. }
  700. check_jit_state_invariants(ctx);
  701. }
  702. fn jit_generate_module(
  703. basic_blocks: &Vec<BasicBlock>,
  704. mut cpu: CpuContext,
  705. builder: &mut WasmBuilder,
  706. wasm_table_index: WasmTableIndex,
  707. state_flags: CachedStateFlags,
  708. ) {
  709. builder.reset();
  710. let basic_block_indices: HashMap<u32, u32> = basic_blocks
  711. .iter()
  712. .enumerate()
  713. .map(|(index, block)| (block.addr, index as u32))
  714. .collect();
  715. // set state local variable to the initial state passed as the first argument
  716. builder.get_local(&builder.arg_local_initial_state.unsafe_clone());
  717. let gen_local_state = builder.set_new_local();
  718. // initialise max_iterations
  719. let gen_local_iteration_counter = if JIT_ALWAYS_USE_LOOP_SAFETY {
  720. builder.const_i32(JIT_MAX_ITERATIONS_PER_FUNCTION as i32);
  721. Some(builder.set_new_local())
  722. }
  723. else {
  724. None
  725. };
  726. let mut register_locals = (0..8)
  727. .map(|i| {
  728. builder.load_fixed_i32(global_pointers::get_reg32_offset(i));
  729. builder.set_new_local()
  730. })
  731. .collect();
  732. let exit_label = builder.block_void();
  733. // main state machine loop
  734. let main_loop_label = builder.loop_void();
  735. builder.block_void(); // for the default case
  736. let exit_with_fault_label = builder.block_void(); // for the exit-with-fault case
  737. let ctx = &mut JitContext {
  738. cpu: &mut cpu,
  739. builder,
  740. register_locals: &mut register_locals,
  741. start_of_current_instruction: 0,
  742. main_loop_label,
  743. exit_with_fault_label,
  744. exit_label,
  745. our_wasm_table_index: wasm_table_index,
  746. basic_block_index_local: &gen_local_state,
  747. state_flags,
  748. };
  749. if let Some(gen_local_iteration_counter) = gen_local_iteration_counter.as_ref() {
  750. profiler::stat_increment(stat::COMPILE_WITH_LOOP_SAFETY);
  751. // decrement max_iterations
  752. ctx.builder.get_local(gen_local_iteration_counter);
  753. ctx.builder.const_i32(-1);
  754. ctx.builder.add_i32();
  755. ctx.builder.set_local(gen_local_iteration_counter);
  756. // if max_iterations == 0: return
  757. ctx.builder.get_local(gen_local_iteration_counter);
  758. ctx.builder.eqz_i32();
  759. ctx.builder.if_void();
  760. codegen::gen_debug_track_jit_exit(ctx.builder, 0);
  761. ctx.builder.br(exit_label);
  762. ctx.builder.block_end();
  763. }
  764. // generate the opening blocks for the cases
  765. for _ in 0..basic_blocks.len() {
  766. ctx.builder.block_void();
  767. }
  768. ctx.builder.get_local(&gen_local_state);
  769. ctx.builder.brtable_and_cases(basic_blocks.len() as u32 + 1); // plus one for the exit-with-fault case
  770. for (i, block) in basic_blocks.iter().enumerate() {
  771. // Case [i] will jump after the [i]th block, so we first generate the
  772. // block end opcode and then the code for that block
  773. ctx.builder.block_end();
  774. dbg_assert!(block.addr < block.end_addr);
  775. jit_generate_basic_block(ctx, block);
  776. let invalid_connection_to_next_block = block.end_addr != ctx.cpu.eip;
  777. dbg_assert!(!invalid_connection_to_next_block);
  778. if block.has_sti {
  779. match block.ty {
  780. BasicBlockType::ConditionalJump {
  781. condition,
  782. jump_offset,
  783. jump_offset_is_32,
  784. ..
  785. } => {
  786. codegen::gen_condition_fn(ctx, condition);
  787. ctx.builder.if_void();
  788. if jump_offset_is_32 {
  789. codegen::gen_relative_jump(ctx.builder, jump_offset);
  790. }
  791. else {
  792. codegen::gen_jmp_rel16(ctx.builder, jump_offset as u16);
  793. }
  794. ctx.builder.block_end();
  795. },
  796. BasicBlockType::Normal { .. } => {},
  797. BasicBlockType::Exit => {},
  798. };
  799. codegen::gen_debug_track_jit_exit(ctx.builder, block.last_instruction_addr);
  800. codegen::gen_move_registers_from_locals_to_memory(ctx);
  801. codegen::gen_fn0_const(ctx.builder, "handle_irqs");
  802. ctx.builder.return_();
  803. continue;
  804. }
  805. match &block.ty {
  806. BasicBlockType::Exit => {
  807. // Exit this function
  808. codegen::gen_debug_track_jit_exit(ctx.builder, block.last_instruction_addr);
  809. ctx.builder.br(exit_label);
  810. },
  811. BasicBlockType::Normal { next_block_addr } => {
  812. // Unconditional jump to next basic block
  813. // - All instructions that don't change eip
  814. // - Unconditional jump
  815. if Page::page_of(*next_block_addr) != Page::page_of(block.addr) {
  816. codegen::gen_page_switch_check(
  817. ctx,
  818. *next_block_addr,
  819. block.last_instruction_addr,
  820. );
  821. #[cfg(debug_assertions)]
  822. codegen::gen_fn2_const(
  823. ctx.builder,
  824. "check_page_switch",
  825. block.addr,
  826. *next_block_addr,
  827. );
  828. }
  829. let next_basic_block_index = *basic_block_indices
  830. .get(&next_block_addr)
  831. .expect("basic_block_indices.get (Normal)");
  832. if next_basic_block_index == (i as u32) + 1 {
  833. // fallthru
  834. }
  835. else {
  836. // set state variable to next basic block
  837. ctx.builder.const_i32(next_basic_block_index as i32);
  838. ctx.builder.set_local(&gen_local_state);
  839. ctx.builder.br(main_loop_label);
  840. }
  841. },
  842. &BasicBlockType::ConditionalJump {
  843. next_block_addr,
  844. next_block_branch_taken_addr,
  845. condition,
  846. jump_offset,
  847. jump_offset_is_32,
  848. } => {
  849. // Conditional jump to next basic block
  850. // - jnz, jc, loop, jcxz, etc.
  851. codegen::gen_condition_fn(ctx, condition);
  852. ctx.builder.if_void();
  853. // Branch taken
  854. if jump_offset_is_32 {
  855. codegen::gen_relative_jump(ctx.builder, jump_offset);
  856. }
  857. else {
  858. codegen::gen_jmp_rel16(ctx.builder, jump_offset as u16);
  859. }
  860. if let Some(next_block_branch_taken_addr) = next_block_branch_taken_addr {
  861. let next_basic_block_branch_taken_index = *basic_block_indices
  862. .get(&next_block_branch_taken_addr)
  863. .expect("basic_block_indices.get (branch taken)");
  864. dbg_assert!(
  865. (block.end_addr + jump_offset as u32) & 0xFFF
  866. == next_block_branch_taken_addr & 0xFFF
  867. );
  868. if Page::page_of(next_block_branch_taken_addr) != Page::page_of(block.addr) {
  869. codegen::gen_page_switch_check(
  870. ctx,
  871. next_block_branch_taken_addr,
  872. block.last_instruction_addr,
  873. );
  874. #[cfg(debug_assertions)]
  875. codegen::gen_fn2_const(
  876. ctx.builder,
  877. "check_page_switch",
  878. block.addr,
  879. next_block_branch_taken_addr,
  880. );
  881. }
  882. ctx.builder
  883. .const_i32(next_basic_block_branch_taken_index as i32);
  884. ctx.builder.set_local(&gen_local_state);
  885. ctx.builder.br(main_loop_label);
  886. }
  887. else {
  888. // Jump to different page
  889. codegen::gen_debug_track_jit_exit(ctx.builder, block.last_instruction_addr);
  890. ctx.builder.br(exit_label);
  891. }
  892. if let Some(next_block_addr) = next_block_addr {
  893. dbg_assert!(Page::page_of(next_block_addr) == Page::page_of(block.addr));
  894. // Branch not taken
  895. let next_basic_block_index = *basic_block_indices
  896. .get(&next_block_addr)
  897. .expect("basic_block_indices.get (branch not taken)");
  898. if next_basic_block_index == (i as u32) + 1 {
  899. // fallthru
  900. ctx.builder.block_end();
  901. }
  902. else {
  903. ctx.builder.else_();
  904. ctx.builder.const_i32(next_basic_block_index as i32);
  905. ctx.builder.set_local(&gen_local_state);
  906. ctx.builder.br(main_loop_label);
  907. ctx.builder.block_end();
  908. }
  909. }
  910. else {
  911. ctx.builder.else_();
  912. // End of this page
  913. codegen::gen_debug_track_jit_exit(ctx.builder, block.last_instruction_addr);
  914. ctx.builder.br(exit_label);
  915. ctx.builder.block_end();
  916. }
  917. },
  918. }
  919. }
  920. {
  921. // exit-with-fault case
  922. ctx.builder.block_end();
  923. codegen::gen_move_registers_from_locals_to_memory(ctx);
  924. codegen::gen_fn0_const(ctx.builder, "trigger_fault_end_jit");
  925. ctx.builder.return_();
  926. }
  927. ctx.builder.block_end(); // default case
  928. ctx.builder.unreachable();
  929. ctx.builder.block_end(); // loop
  930. ctx.builder.block_end(); // exit
  931. codegen::gen_move_registers_from_locals_to_memory(ctx);
  932. ctx.builder.free_local(gen_local_state.unsafe_clone());
  933. if let Some(local) = gen_local_iteration_counter {
  934. ctx.builder.free_local(local);
  935. }
  936. for local in ctx.register_locals.drain(..) {
  937. ctx.builder.free_local(local);
  938. }
  939. ctx.builder.finish();
  940. }
  941. fn jit_generate_basic_block(ctx: &mut JitContext, block: &BasicBlock) {
  942. let start_addr = block.addr;
  943. let last_instruction_addr = block.last_instruction_addr;
  944. let stop_addr = block.end_addr;
  945. // First iteration of do-while assumes the caller confirms this condition
  946. dbg_assert!(!is_near_end_of_page(start_addr));
  947. codegen::gen_increment_timestamp_counter(ctx.builder, block.number_of_instructions as i32);
  948. ctx.cpu.eip = start_addr;
  949. loop {
  950. let mut instruction = 0;
  951. if cfg!(feature = "profiler") {
  952. instruction = memory::read32s(ctx.cpu.eip) as u32;
  953. ::opstats::gen_opstats(ctx.builder, instruction);
  954. ::opstats::record_opstat_compiled(instruction);
  955. }
  956. if ctx.cpu.eip == last_instruction_addr {
  957. // Before the last instruction:
  958. // - Set eip to *after* the instruction
  959. // - Set previous_eip to *before* the instruction
  960. codegen::gen_set_previous_eip_offset_from_eip(
  961. ctx.builder,
  962. last_instruction_addr - start_addr,
  963. );
  964. codegen::gen_increment_instruction_pointer(ctx.builder, stop_addr - start_addr);
  965. }
  966. let wasm_length_before = ctx.builder.instruction_body_length();
  967. ctx.start_of_current_instruction = ctx.cpu.eip;
  968. let start_eip = ctx.cpu.eip;
  969. let mut instruction_flags = 0;
  970. jit_instructions::jit_instruction(ctx, &mut instruction_flags);
  971. let end_eip = ctx.cpu.eip;
  972. let instruction_length = end_eip - start_eip;
  973. let was_block_boundary = instruction_flags & JIT_INSTR_BLOCK_BOUNDARY_FLAG != 0;
  974. let wasm_length = ctx.builder.instruction_body_length() - wasm_length_before;
  975. ::opstats::record_opstat_size_wasm(instruction, wasm_length as u32);
  976. dbg_assert!((end_eip == stop_addr) == (start_eip == last_instruction_addr));
  977. dbg_assert!(instruction_length < MAX_INSTRUCTION_LENGTH);
  978. let end_addr = ctx.cpu.eip;
  979. if end_addr == stop_addr {
  980. // no page was crossed
  981. dbg_assert!(Page::page_of(end_addr) == Page::page_of(start_addr));
  982. break;
  983. }
  984. if was_block_boundary || is_near_end_of_page(end_addr) || end_addr > stop_addr {
  985. dbg_log!(
  986. "Overlapping basic blocks start={:x} expected_end={:x} end={:x} was_block_boundary={} near_end_of_page={}",
  987. start_addr,
  988. stop_addr,
  989. end_addr,
  990. was_block_boundary,
  991. is_near_end_of_page(end_addr)
  992. );
  993. dbg_assert!(false);
  994. break;
  995. }
  996. }
  997. }
  998. #[no_mangle]
  999. pub fn jit_increase_hotness_and_maybe_compile(
  1000. virt_address: i32,
  1001. phys_address: u32,
  1002. cs_offset: u32,
  1003. state_flags: CachedStateFlags,
  1004. hotness: u32,
  1005. ) {
  1006. let ctx = get_jit_state();
  1007. let page = Page::page_of(phys_address);
  1008. let address_hash = jit_hot_hash_page(page) as usize;
  1009. ctx.hot_pages[address_hash] += hotness;
  1010. if ctx.hot_pages[address_hash] >= JIT_THRESHOLD {
  1011. if ctx.compiling.is_some() {
  1012. return;
  1013. }
  1014. // only try generating if we're in the correct address space
  1015. if cpu::translate_address_read_no_side_effects(virt_address) == Some(phys_address) {
  1016. ctx.hot_pages[address_hash] = 0;
  1017. jit_analyze_and_generate(ctx, virt_address, phys_address, cs_offset, state_flags)
  1018. }
  1019. else {
  1020. profiler::stat_increment(stat::COMPILE_WRONG_ADDRESS_SPACE);
  1021. }
  1022. };
  1023. }
  1024. fn free_wasm_table_index(ctx: &mut JitState, wasm_table_index: WasmTableIndex) {
  1025. if CHECK_JIT_STATE_INVARIANTS {
  1026. dbg_assert!(!ctx.wasm_table_index_free_list.contains(&wasm_table_index));
  1027. match &ctx.compiling {
  1028. Some((wasm_table_index_compiling, _)) => {
  1029. dbg_assert!(
  1030. *wasm_table_index_compiling != wasm_table_index,
  1031. "Attempt to free wasm table index that is currently being compiled"
  1032. );
  1033. },
  1034. _ => {},
  1035. }
  1036. }
  1037. match ctx.used_wasm_table_indices.remove(&wasm_table_index) {
  1038. None => dbg_assert!(false),
  1039. Some(_pages) => {
  1040. //dbg_assert!(!pages.is_empty()); // only if CompilingWritten
  1041. },
  1042. }
  1043. ctx.wasm_table_index_free_list.push(wasm_table_index);
  1044. dbg_assert!(
  1045. ctx.wasm_table_index_free_list.len() + ctx.used_wasm_table_indices.len()
  1046. == WASM_TABLE_SIZE as usize - 1
  1047. );
  1048. // It is not strictly necessary to clear the function, but it will fail more predictably if we
  1049. // accidentally use the function and may garbage collect unused modules earlier
  1050. jit_clear_func(wasm_table_index);
  1051. rebuild_all_pages(ctx);
  1052. check_jit_state_invariants(ctx);
  1053. }
  1054. pub fn rebuild_all_pages(ctx: &mut JitState) {
  1055. // rebuild ctx.all_pages
  1056. let mut all_pages = HashSet::new();
  1057. for pages in ctx.used_wasm_table_indices.values() {
  1058. all_pages.extend(pages);
  1059. }
  1060. ctx.all_pages = all_pages;
  1061. }
  1062. /// Register a write in this page: Delete all present code
  1063. pub fn jit_dirty_page(ctx: &mut JitState, page: Page) {
  1064. let mut did_have_code = false;
  1065. if ctx.all_pages.contains(&page) {
  1066. profiler::stat_increment(stat::INVALIDATE_PAGE_HAD_CODE);
  1067. did_have_code = true;
  1068. let mut index_to_free = HashSet::new();
  1069. let compiling = match &ctx.compiling {
  1070. Some((wasm_table_index, _)) => Some(*wasm_table_index),
  1071. None => None,
  1072. };
  1073. for (&wasm_table_index, pages) in &ctx.used_wasm_table_indices {
  1074. if Some(wasm_table_index) != compiling && pages.contains(&page) {
  1075. index_to_free.insert(wasm_table_index);
  1076. }
  1077. }
  1078. match &ctx.compiling {
  1079. None => {},
  1080. Some((_, PageState::CompilingWritten)) => {},
  1081. Some((wasm_table_index, PageState::Compiling { .. })) => {
  1082. let pages = ctx
  1083. .used_wasm_table_indices
  1084. .get_mut(wasm_table_index)
  1085. .unwrap();
  1086. if pages.contains(&page) {
  1087. pages.clear();
  1088. ctx.compiling = Some((*wasm_table_index, PageState::CompilingWritten));
  1089. rebuild_all_pages(ctx);
  1090. }
  1091. },
  1092. }
  1093. for index in &index_to_free {
  1094. match ctx.used_wasm_table_indices.get(&index) {
  1095. None => dbg_assert!(false),
  1096. Some(pages) => {
  1097. for &p in pages {
  1098. for addr in p.address_range() {
  1099. if let Some(e) = ctx.cache.get(&addr) {
  1100. if index_to_free.contains(&e.wasm_table_index) {
  1101. ctx.cache.remove(&addr);
  1102. }
  1103. }
  1104. }
  1105. }
  1106. },
  1107. }
  1108. }
  1109. for index in index_to_free {
  1110. profiler::stat_increment(stat::INVALIDATE_MODULE_DIRTY_PAGE);
  1111. free_wasm_table_index(ctx, index)
  1112. }
  1113. }
  1114. match ctx.entry_points.remove(&page) {
  1115. None => {},
  1116. Some(_entry_points) => {
  1117. profiler::stat_increment(stat::INVALIDATE_PAGE_HAD_ENTRY_POINTS);
  1118. did_have_code = true;
  1119. // don't try to compile code in this page anymore until it's hot again
  1120. ctx.hot_pages[jit_hot_hash_page(page) as usize] = 0;
  1121. },
  1122. }
  1123. for pages in ctx.used_wasm_table_indices.values() {
  1124. dbg_assert!(!pages.contains(&page));
  1125. }
  1126. check_jit_state_invariants(ctx);
  1127. dbg_assert!(!ctx.all_pages.contains(&page));
  1128. dbg_assert!(!jit_page_has_code_ctx(ctx, page));
  1129. if did_have_code {
  1130. cpu::tlb_set_has_code(page, false);
  1131. }
  1132. if !did_have_code {
  1133. profiler::stat_increment(stat::DIRTY_PAGE_DID_NOT_HAVE_CODE);
  1134. }
  1135. }
  1136. #[no_mangle]
  1137. pub fn jit_dirty_cache(start_addr: u32, end_addr: u32) {
  1138. dbg_assert!(start_addr < end_addr);
  1139. let start_page = Page::page_of(start_addr);
  1140. let end_page = Page::page_of(end_addr - 1);
  1141. for page in start_page.to_u32()..end_page.to_u32() + 1 {
  1142. jit_dirty_page(get_jit_state(), Page::page_of(page << 12));
  1143. }
  1144. }
  1145. /// dirty pages in the range of start_addr and end_addr, which must span at most two pages
  1146. pub fn jit_dirty_cache_small(start_addr: u32, end_addr: u32) {
  1147. dbg_assert!(start_addr < end_addr);
  1148. let start_page = Page::page_of(start_addr);
  1149. let end_page = Page::page_of(end_addr - 1);
  1150. let ctx = get_jit_state();
  1151. jit_dirty_page(ctx, start_page);
  1152. // Note: This can't happen when paging is enabled, as writes across
  1153. // boundaries are split up on two pages
  1154. if start_page != end_page {
  1155. dbg_assert!(start_page.to_u32() + 1 == end_page.to_u32());
  1156. jit_dirty_page(ctx, end_page);
  1157. }
  1158. }
  1159. #[no_mangle]
  1160. pub fn jit_clear_cache_js() { jit_clear_cache(get_jit_state()) }
  1161. pub fn jit_clear_cache(ctx: &mut JitState) {
  1162. let mut pages_with_code = HashSet::new();
  1163. for page in ctx.entry_points.keys() {
  1164. pages_with_code.insert(*page);
  1165. }
  1166. for &p in &ctx.all_pages {
  1167. pages_with_code.insert(p);
  1168. }
  1169. for addr in ctx.cache.keys() {
  1170. dbg_assert!(pages_with_code.contains(&Page::page_of(*addr)));
  1171. }
  1172. for pages in ctx.used_wasm_table_indices.values() {
  1173. dbg_assert!(pages_with_code.is_superset(pages));
  1174. }
  1175. for page in pages_with_code {
  1176. jit_dirty_page(ctx, page);
  1177. }
  1178. }
  1179. pub fn jit_page_has_code(page: Page) -> bool { jit_page_has_code_ctx(get_jit_state(), page) }
  1180. pub fn jit_page_has_code_ctx(ctx: &mut JitState, page: Page) -> bool {
  1181. ctx.all_pages.contains(&page) || ctx.entry_points.contains_key(&page)
  1182. }
  1183. #[no_mangle]
  1184. pub fn jit_get_wasm_table_index_free_list_count() -> u32 {
  1185. if cfg!(feature = "profiler") {
  1186. get_jit_state().wasm_table_index_free_list.len() as u32
  1187. }
  1188. else {
  1189. 0
  1190. }
  1191. }
  1192. #[cfg(feature = "profiler")]
  1193. pub fn check_missed_entry_points(phys_address: u32, state_flags: CachedStateFlags) {
  1194. let ctx = get_jit_state();
  1195. // backwards until beginning of page
  1196. for offset in 0..=(phys_address & 0xFFF) {
  1197. let addr = phys_address - offset;
  1198. dbg_assert!(phys_address >= addr);
  1199. if let Some(entry) = ctx.cache.get(&addr) {
  1200. if entry.state_flags != state_flags || phys_address >= addr + entry.len {
  1201. // give up search on first entry that is not a match
  1202. break;
  1203. }
  1204. profiler::stat_increment(stat::RUN_INTERPRETED_MISSED_COMPILED_ENTRY_LOOKUP);
  1205. let last_jump_type = unsafe { cpu::debug_last_jump.name() };
  1206. let last_jump_addr = unsafe { cpu::debug_last_jump.phys_address() }.unwrap_or(0);
  1207. let last_jump_opcode =
  1208. if last_jump_addr != 0 { memory::read32s(last_jump_addr) } else { 0 };
  1209. let opcode = memory::read32s(phys_address);
  1210. dbg_log!(
  1211. "Compiled exists, but no entry point, \
  1212. start={:x} end={:x} phys_addr={:x} opcode={:02x} {:02x} {:02x} {:02x}. \
  1213. Last jump at {:x} ({}) opcode={:02x} {:02x} {:02x} {:02x}",
  1214. addr,
  1215. addr + entry.len,
  1216. phys_address,
  1217. opcode & 0xFF,
  1218. opcode >> 8 & 0xFF,
  1219. opcode >> 16 & 0xFF,
  1220. opcode >> 16 & 0xFF,
  1221. last_jump_addr,
  1222. last_jump_type,
  1223. last_jump_opcode & 0xFF,
  1224. last_jump_opcode >> 8 & 0xFF,
  1225. last_jump_opcode >> 16 & 0xFF,
  1226. last_jump_opcode >> 16 & 0xFF,
  1227. );
  1228. }
  1229. }
  1230. }