jit.rs 87 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379
  1. use std::collections::{BTreeMap, HashMap, HashSet, VecDeque};
  2. use std::iter::FromIterator;
  3. use std::mem;
  4. use std::ptr::NonNull;
  5. use analysis::AnalysisType;
  6. use codegen;
  7. use control_flow;
  8. use control_flow::WasmStructure;
  9. use cpu::cpu;
  10. use cpu::global_pointers;
  11. use cpu::memory;
  12. use cpu_context::CpuContext;
  13. use jit_instructions;
  14. use opstats;
  15. use page::Page;
  16. use profiler;
  17. use profiler::stat;
  18. use state_flags::CachedStateFlags;
  19. use util::SafeToU16;
  20. use wasmgen::wasm_builder::{Label, WasmBuilder, WasmLocal};
  21. #[derive(Copy, Clone, Eq, Hash, PartialEq)]
  22. #[repr(transparent)]
  23. pub struct WasmTableIndex(u16);
  24. impl WasmTableIndex {
  25. pub fn to_u16(self) -> u16 { self.0 }
  26. }
  27. mod unsafe_jit {
  28. use jit::{CachedStateFlags, WasmTableIndex};
  29. extern "C" {
  30. pub fn codegen_finalize(
  31. wasm_table_index: WasmTableIndex,
  32. phys_addr: u32,
  33. state_flags: CachedStateFlags,
  34. ptr: u32,
  35. len: u32,
  36. );
  37. pub fn jit_clear_func(wasm_table_index: WasmTableIndex);
  38. }
  39. }
  40. fn codegen_finalize(
  41. wasm_table_index: WasmTableIndex,
  42. phys_addr: u32,
  43. state_flags: CachedStateFlags,
  44. ptr: u32,
  45. len: u32,
  46. ) {
  47. unsafe { unsafe_jit::codegen_finalize(wasm_table_index, phys_addr, state_flags, ptr, len) }
  48. }
  49. pub fn jit_clear_func(wasm_table_index: WasmTableIndex) {
  50. unsafe { unsafe_jit::jit_clear_func(wasm_table_index) }
  51. }
  52. // less branches will generate if-else, more will generate brtable
  53. pub const BRTABLE_CUTOFF: usize = 10;
  54. pub const WASM_TABLE_SIZE: u32 = 900;
  55. pub const CHECK_JIT_STATE_INVARIANTS: bool = false;
  56. pub const JIT_USE_LOOP_SAFETY: bool = true;
  57. pub const JIT_THRESHOLD: u32 = 200 * 1000;
  58. pub const MAX_EXTRA_BASIC_BLOCKS: usize = 250;
  59. const MAX_INSTRUCTION_LENGTH: u32 = 16;
  60. #[allow(non_upper_case_globals)]
  61. static mut jit_state: NonNull<JitState> =
  62. unsafe { NonNull::new_unchecked(mem::align_of::<JitState>() as *mut _) };
  63. pub fn get_jit_state() -> &'static mut JitState { unsafe { jit_state.as_mut() } }
  64. #[no_mangle]
  65. pub fn rust_init() {
  66. dbg_assert!(std::mem::size_of::<[Option<NonNull<cpu::Code>>; 0x100000]>() == 0x100000 * 4);
  67. let x = Box::new(JitState::create_and_initialise());
  68. unsafe {
  69. jit_state = NonNull::new(Box::into_raw(x)).unwrap()
  70. }
  71. use std::panic;
  72. panic::set_hook(Box::new(|panic_info| {
  73. console_log!("{}", panic_info.to_string());
  74. }));
  75. }
  76. struct PageInfo {
  77. wasm_table_index: WasmTableIndex,
  78. hidden_wasm_table_indices: Vec<WasmTableIndex>,
  79. entry_points: Vec<(u16, u16)>,
  80. state_flags: CachedStateFlags,
  81. }
  82. enum CompilingPageState {
  83. Compiling { pages: HashMap<Page, PageInfo> },
  84. CompilingWritten,
  85. }
  86. pub struct JitState {
  87. wasm_builder: WasmBuilder,
  88. // as an alternative to HashSet, we could use a bitmap of 4096 bits here
  89. // (faster, but uses much more memory)
  90. // or a compressed bitmap (likely faster)
  91. // or HashSet<u32> rather than nested
  92. entry_points: HashMap<Page, (u32, HashSet<u16>)>,
  93. pages: HashMap<Page, PageInfo>,
  94. wasm_table_index_free_list: Vec<WasmTableIndex>,
  95. compiling: Option<(WasmTableIndex, CompilingPageState)>,
  96. }
  97. pub fn check_jit_state_invariants(ctx: &mut JitState) {
  98. if !CHECK_JIT_STATE_INVARIANTS {
  99. return;
  100. }
  101. match &ctx.compiling {
  102. Some((_, CompilingPageState::Compiling { pages })) => {
  103. dbg_assert!(pages.keys().all(|page| ctx.entry_points.contains_key(page)));
  104. },
  105. _ => {},
  106. }
  107. let free: HashSet<WasmTableIndex> =
  108. HashSet::from_iter(ctx.wasm_table_index_free_list.iter().cloned());
  109. let used = HashSet::from_iter(ctx.pages.values().map(|info| info.wasm_table_index));
  110. let compiling = HashSet::from_iter(ctx.compiling.as_ref().map(|&(index, _)| index));
  111. dbg_assert!(free.intersection(&used).next().is_none());
  112. dbg_assert!(used.intersection(&compiling).next().is_none());
  113. dbg_assert!(free.len() + used.len() + compiling.len() == (WASM_TABLE_SIZE - 1) as usize);
  114. match &ctx.compiling {
  115. Some((_, CompilingPageState::Compiling { pages })) => {
  116. dbg_assert!(pages.keys().all(|page| ctx.entry_points.contains_key(page)));
  117. },
  118. _ => {},
  119. }
  120. for i in 0..unsafe { cpu::valid_tlb_entries_count } {
  121. let page = unsafe { cpu::valid_tlb_entries[i as usize] };
  122. let entry = unsafe { cpu::tlb_data[page as usize] };
  123. if 0 != entry {
  124. let tlb_physical_page = Page::of_u32(
  125. (entry as u32 >> 12 ^ page as u32) - (unsafe { memory::mem8 } as u32 >> 12),
  126. );
  127. let w = match unsafe { cpu::tlb_code[page as usize] } {
  128. None => None,
  129. Some(c) => unsafe {
  130. Some(c.as_ref().wasm_table_index)
  131. },
  132. };
  133. let tlb_has_code = entry & cpu::TLB_HAS_CODE == cpu::TLB_HAS_CODE;
  134. let infos = ctx.pages.get(&tlb_physical_page);
  135. let entry_points = ctx.entry_points.get(&tlb_physical_page);
  136. dbg_assert!(tlb_has_code || !w.is_some());
  137. dbg_assert!(tlb_has_code || !infos.is_some());
  138. dbg_assert!(tlb_has_code || !entry_points.is_some());
  139. //dbg_assert!((w.is_some() || page.is_some() || entry_points.is_some()) == tlb_has_code); // XXX: check this
  140. }
  141. }
  142. }
  143. impl JitState {
  144. pub fn create_and_initialise() -> JitState {
  145. // don't assign 0 (XXX: Check)
  146. let wasm_table_indices = (1..=(WASM_TABLE_SIZE - 1) as u16).map(|x| WasmTableIndex(x));
  147. JitState {
  148. wasm_builder: WasmBuilder::new(),
  149. entry_points: HashMap::new(),
  150. pages: HashMap::new(),
  151. wasm_table_index_free_list: Vec::from_iter(wasm_table_indices),
  152. compiling: None,
  153. }
  154. }
  155. }
  156. #[derive(PartialEq, Eq)]
  157. pub enum BasicBlockType {
  158. Normal {
  159. next_block_addr: Option<u32>,
  160. jump_offset: i32,
  161. jump_offset_is_32: bool,
  162. },
  163. ConditionalJump {
  164. next_block_addr: Option<u32>,
  165. next_block_branch_taken_addr: Option<u32>,
  166. condition: u8,
  167. jump_offset: i32,
  168. jump_offset_is_32: bool,
  169. },
  170. // Set eip to an absolute value (ret, jmp r/m, call r/m)
  171. AbsoluteEip,
  172. Exit,
  173. }
  174. pub struct BasicBlock {
  175. pub addr: u32,
  176. pub virt_addr: i32,
  177. pub last_instruction_addr: u32,
  178. pub end_addr: u32,
  179. pub is_entry_block: bool,
  180. pub ty: BasicBlockType,
  181. pub has_sti: bool,
  182. pub number_of_instructions: u32,
  183. }
  184. #[derive(Copy, Clone, PartialEq)]
  185. pub struct CachedCode {
  186. pub wasm_table_index: WasmTableIndex,
  187. pub initial_state: u16,
  188. }
  189. impl CachedCode {
  190. pub const NONE: CachedCode = CachedCode {
  191. wasm_table_index: WasmTableIndex(0),
  192. initial_state: 0,
  193. };
  194. }
  195. #[derive(PartialEq)]
  196. pub enum InstructionOperandDest {
  197. WasmLocal(WasmLocal),
  198. Other,
  199. }
  200. #[derive(PartialEq)]
  201. pub enum InstructionOperand {
  202. WasmLocal(WasmLocal),
  203. Immediate(i32),
  204. Other,
  205. }
  206. impl InstructionOperand {
  207. pub fn is_zero(&self) -> bool {
  208. match self {
  209. InstructionOperand::Immediate(0) => true,
  210. _ => false,
  211. }
  212. }
  213. }
  214. impl Into<InstructionOperand> for InstructionOperandDest {
  215. fn into(self: InstructionOperandDest) -> InstructionOperand {
  216. match self {
  217. InstructionOperandDest::WasmLocal(l) => InstructionOperand::WasmLocal(l),
  218. InstructionOperandDest::Other => InstructionOperand::Other,
  219. }
  220. }
  221. }
  222. pub enum Instruction {
  223. Cmp {
  224. dest: InstructionOperandDest,
  225. source: InstructionOperand,
  226. opsize: i32,
  227. },
  228. Sub {
  229. dest: InstructionOperandDest,
  230. source: InstructionOperand,
  231. opsize: i32,
  232. is_dec: bool,
  233. },
  234. Add {
  235. dest: InstructionOperandDest,
  236. source: InstructionOperand,
  237. opsize: i32,
  238. is_inc: bool,
  239. },
  240. AdcSbb {
  241. dest: InstructionOperandDest,
  242. source: InstructionOperand,
  243. opsize: i32,
  244. },
  245. NonZeroShift {
  246. dest: InstructionOperandDest,
  247. opsize: i32,
  248. },
  249. Bitwise {
  250. dest: InstructionOperandDest,
  251. opsize: i32,
  252. },
  253. Other,
  254. }
  255. pub struct JitContext<'a> {
  256. pub cpu: &'a mut CpuContext,
  257. pub builder: &'a mut WasmBuilder,
  258. pub register_locals: &'a mut Vec<WasmLocal>,
  259. pub start_of_current_instruction: u32,
  260. pub exit_with_fault_label: Label,
  261. pub exit_label: Label,
  262. pub current_instruction: Instruction,
  263. pub previous_instruction: Instruction,
  264. pub instruction_counter: WasmLocal,
  265. }
  266. impl<'a> JitContext<'a> {
  267. pub fn reg(&self, i: u32) -> WasmLocal { self.register_locals[i as usize].unsafe_clone() }
  268. }
  269. pub const JIT_INSTR_BLOCK_BOUNDARY_FLAG: u32 = 1 << 0;
  270. pub fn is_near_end_of_page(address: u32) -> bool {
  271. address & 0xFFF >= 0x1000 - MAX_INSTRUCTION_LENGTH
  272. }
  273. pub fn jit_find_cache_entry(phys_address: u32, state_flags: CachedStateFlags) -> CachedCode {
  274. // TODO: dedup with jit_find_cache_entry_in_page?
  275. // NOTE: This is currently only used for invariant/missed-entry-point checking
  276. let ctx = get_jit_state();
  277. match ctx.pages.get(&Page::page_of(phys_address)) {
  278. Some(PageInfo {
  279. wasm_table_index,
  280. state_flags: s,
  281. entry_points,
  282. hidden_wasm_table_indices: _,
  283. }) => {
  284. if *s == state_flags {
  285. let page_offset = phys_address as u16 & 0xFFF;
  286. if let Some(&(_, initial_state)) =
  287. entry_points.iter().find(|(p, _)| p == &page_offset)
  288. {
  289. return CachedCode {
  290. wasm_table_index: *wasm_table_index,
  291. initial_state,
  292. };
  293. }
  294. }
  295. },
  296. None => {},
  297. }
  298. return CachedCode::NONE;
  299. }
  300. #[no_mangle]
  301. pub fn jit_find_cache_entry_in_page(
  302. virt_address: u32,
  303. wasm_table_index: WasmTableIndex,
  304. state_flags: u32,
  305. ) -> i32 {
  306. // TODO: generate code for this
  307. profiler::stat_increment(stat::INDIRECT_JUMP);
  308. let state_flags = CachedStateFlags::of_u32(state_flags);
  309. unsafe {
  310. match cpu::tlb_code[(virt_address >> 12) as usize] {
  311. None => {},
  312. Some(c) => {
  313. let c = c.as_ref();
  314. if state_flags == c.state_flags && wasm_table_index == c.wasm_table_index {
  315. let state = c.state_table[virt_address as usize & 0xFFF];
  316. if state != u16::MAX {
  317. return state.into();
  318. }
  319. }
  320. },
  321. }
  322. }
  323. profiler::stat_increment(stat::INDIRECT_JUMP_NO_ENTRY);
  324. return -1;
  325. }
  326. // Maximum number of pages per wasm module. Necessary for the following reasons:
  327. // - There is an upper limit on the size of a single function in wasm (currently ~7MB in all browsers)
  328. // See https://github.com/WebAssembly/design/issues/1138
  329. // - v8 poorly handles large br_table elements and OOMs on modules much smaller than the above limit
  330. // See https://bugs.chromium.org/p/v8/issues/detail?id=9697 and https://bugs.chromium.org/p/v8/issues/detail?id=9141
  331. // Will hopefully be fixed in the near future by generating direct control flow
  332. const MAX_PAGES: usize = 3;
  333. fn jit_find_basic_blocks(
  334. ctx: &mut JitState,
  335. entry_points: HashSet<i32>,
  336. cpu: CpuContext,
  337. ) -> Vec<BasicBlock> {
  338. fn follow_jump(
  339. virt_target: i32,
  340. ctx: &mut JitState,
  341. pages: &mut HashSet<Page>,
  342. page_blacklist: &mut HashSet<Page>,
  343. max_pages: usize,
  344. marked_as_entry: &mut HashSet<i32>,
  345. to_visit_stack: &mut Vec<i32>,
  346. ) -> Option<u32> {
  347. if is_near_end_of_page(virt_target as u32) {
  348. return None;
  349. }
  350. let phys_target = match cpu::translate_address_read_no_side_effects(virt_target) {
  351. Err(()) => {
  352. dbg_log!("Not analysing {:x} (page not mapped)", virt_target);
  353. return None;
  354. },
  355. Ok(t) => t,
  356. };
  357. let phys_page = Page::page_of(phys_target);
  358. if !pages.contains(&phys_page) && pages.len() == max_pages
  359. || page_blacklist.contains(&phys_page)
  360. {
  361. return None;
  362. }
  363. if !pages.contains(&phys_page) {
  364. // page seen for the first time, handle entry points
  365. if let Some((hotness, entry_points)) = ctx.entry_points.get_mut(&phys_page) {
  366. let existing_entry_points = match ctx.pages.get(&phys_page) {
  367. Some(PageInfo { entry_points, .. }) => {
  368. HashSet::from_iter(entry_points.iter().map(|x| x.0))
  369. },
  370. None => HashSet::new(),
  371. };
  372. if entry_points
  373. .iter()
  374. .all(|entry_point| existing_entry_points.contains(entry_point))
  375. {
  376. page_blacklist.insert(phys_page);
  377. return None;
  378. }
  379. // XXX: Remove this paragraph
  380. //let old_length = entry_points.len();
  381. //entry_points.extend(existing_entry_points);
  382. //dbg_assert!(
  383. // entry_points.union(&existing_entry_points).count() == entry_points.len()
  384. //);
  385. *hotness = 0;
  386. for &addr_low in entry_points.iter() {
  387. let addr = virt_target & !0xFFF | addr_low as i32;
  388. to_visit_stack.push(addr);
  389. marked_as_entry.insert(addr);
  390. }
  391. }
  392. else {
  393. // no entry points: ignore this page?
  394. page_blacklist.insert(phys_page);
  395. return None;
  396. }
  397. pages.insert(phys_page);
  398. dbg_assert!(pages.len() <= max_pages);
  399. }
  400. to_visit_stack.push(virt_target);
  401. Some(phys_target)
  402. }
  403. let mut to_visit_stack: Vec<i32> = Vec::new();
  404. let mut marked_as_entry: HashSet<i32> = HashSet::new();
  405. let mut basic_blocks: BTreeMap<u32, BasicBlock> = BTreeMap::new();
  406. let mut pages: HashSet<Page> = HashSet::new();
  407. let mut page_blacklist = HashSet::new();
  408. // 16-bit doesn't not work correctly, most likely due to instruction pointer wrap-around
  409. let max_pages = if cpu.state_flags.is_32() { MAX_PAGES } else { 1 };
  410. for virt_addr in entry_points {
  411. let ok = follow_jump(
  412. virt_addr,
  413. ctx,
  414. &mut pages,
  415. &mut page_blacklist,
  416. max_pages,
  417. &mut marked_as_entry,
  418. &mut to_visit_stack,
  419. );
  420. dbg_assert!(ok.is_some());
  421. dbg_assert!(marked_as_entry.contains(&virt_addr));
  422. }
  423. while let Some(to_visit) = to_visit_stack.pop() {
  424. let phys_addr = match cpu::translate_address_read_no_side_effects(to_visit) {
  425. Err(()) => {
  426. dbg_log!("Not analysing {:x} (page not mapped)", to_visit);
  427. continue;
  428. },
  429. Ok(phys_addr) => phys_addr,
  430. };
  431. if basic_blocks.contains_key(&phys_addr) {
  432. continue;
  433. }
  434. if is_near_end_of_page(phys_addr) {
  435. // Empty basic block, don't insert
  436. profiler::stat_increment(stat::COMPILE_CUT_OFF_AT_END_OF_PAGE);
  437. continue;
  438. }
  439. let mut current_address = phys_addr;
  440. let mut current_block = BasicBlock {
  441. addr: current_address,
  442. virt_addr: to_visit,
  443. last_instruction_addr: 0,
  444. end_addr: 0,
  445. ty: BasicBlockType::Exit,
  446. is_entry_block: false,
  447. has_sti: false,
  448. number_of_instructions: 0,
  449. };
  450. loop {
  451. let addr_before_instruction = current_address;
  452. let mut cpu = &mut CpuContext {
  453. eip: current_address,
  454. ..cpu
  455. };
  456. let analysis = ::analysis::analyze_step(&mut cpu);
  457. current_block.number_of_instructions += 1;
  458. let has_next_instruction = !analysis.no_next_instruction;
  459. current_address = cpu.eip;
  460. dbg_assert!(Page::page_of(current_address) == Page::page_of(addr_before_instruction));
  461. let current_virt_addr = to_visit & !0xFFF | current_address as i32 & 0xFFF;
  462. match analysis.ty {
  463. AnalysisType::Normal | AnalysisType::STI => {
  464. dbg_assert!(has_next_instruction);
  465. dbg_assert!(!analysis.absolute_jump);
  466. if current_block.has_sti {
  467. // Convert next instruction after STI (i.e., the current instruction) into block boundary
  468. marked_as_entry.insert(current_virt_addr);
  469. to_visit_stack.push(current_virt_addr);
  470. current_block.last_instruction_addr = addr_before_instruction;
  471. current_block.end_addr = current_address;
  472. break;
  473. }
  474. if analysis.ty == AnalysisType::STI {
  475. current_block.has_sti = true;
  476. dbg_assert!(
  477. !is_near_end_of_page(current_address),
  478. "TODO: Handle STI instruction near end of page"
  479. );
  480. }
  481. else {
  482. // Only split non-STI blocks (one instruction needs to run after STI before
  483. // handle_irqs may be called)
  484. if basic_blocks.contains_key(&current_address) {
  485. current_block.last_instruction_addr = addr_before_instruction;
  486. current_block.end_addr = current_address;
  487. dbg_assert!(!is_near_end_of_page(current_address));
  488. current_block.ty = BasicBlockType::Normal {
  489. next_block_addr: Some(current_address),
  490. jump_offset: 0,
  491. jump_offset_is_32: true,
  492. };
  493. break;
  494. }
  495. }
  496. },
  497. AnalysisType::Jump {
  498. offset,
  499. is_32,
  500. condition: Some(condition),
  501. } => {
  502. dbg_assert!(!analysis.absolute_jump);
  503. // conditional jump: continue at next and continue at jump target
  504. let jump_target = if is_32 {
  505. current_virt_addr + offset
  506. }
  507. else {
  508. cpu.cs_offset as i32
  509. + (current_virt_addr - cpu.cs_offset as i32 + offset & 0xFFFF)
  510. };
  511. dbg_assert!(has_next_instruction);
  512. to_visit_stack.push(current_virt_addr);
  513. let next_block_addr = if is_near_end_of_page(current_address) {
  514. None
  515. }
  516. else {
  517. Some(current_address)
  518. };
  519. current_block.ty = BasicBlockType::ConditionalJump {
  520. next_block_addr,
  521. next_block_branch_taken_addr: follow_jump(
  522. jump_target,
  523. ctx,
  524. &mut pages,
  525. &mut page_blacklist,
  526. max_pages,
  527. &mut marked_as_entry,
  528. &mut to_visit_stack,
  529. ),
  530. condition,
  531. jump_offset: offset,
  532. jump_offset_is_32: is_32,
  533. };
  534. current_block.last_instruction_addr = addr_before_instruction;
  535. current_block.end_addr = current_address;
  536. break;
  537. },
  538. AnalysisType::Jump {
  539. offset,
  540. is_32,
  541. condition: None,
  542. } => {
  543. dbg_assert!(!analysis.absolute_jump);
  544. // non-conditional jump: continue at jump target
  545. let jump_target = if is_32 {
  546. current_virt_addr + offset
  547. }
  548. else {
  549. cpu.cs_offset as i32
  550. + (current_virt_addr - cpu.cs_offset as i32 + offset & 0xFFFF)
  551. };
  552. if has_next_instruction {
  553. // Execution will eventually come back to the next instruction (CALL)
  554. marked_as_entry.insert(current_virt_addr);
  555. to_visit_stack.push(current_virt_addr);
  556. }
  557. current_block.ty = BasicBlockType::Normal {
  558. next_block_addr: follow_jump(
  559. jump_target,
  560. ctx,
  561. &mut pages,
  562. &mut page_blacklist,
  563. max_pages,
  564. &mut marked_as_entry,
  565. &mut to_visit_stack,
  566. ),
  567. jump_offset: offset,
  568. jump_offset_is_32: is_32,
  569. };
  570. current_block.last_instruction_addr = addr_before_instruction;
  571. current_block.end_addr = current_address;
  572. break;
  573. },
  574. AnalysisType::BlockBoundary => {
  575. // a block boundary but not a jump, get out
  576. if has_next_instruction {
  577. // block boundary, but execution will eventually come back
  578. // to the next instruction. Create a new basic block
  579. // starting at the next instruction and register it as an
  580. // entry point
  581. marked_as_entry.insert(current_virt_addr);
  582. to_visit_stack.push(current_virt_addr);
  583. }
  584. if analysis.absolute_jump {
  585. current_block.ty = BasicBlockType::AbsoluteEip;
  586. }
  587. current_block.last_instruction_addr = addr_before_instruction;
  588. current_block.end_addr = current_address;
  589. break;
  590. },
  591. }
  592. if is_near_end_of_page(current_address) {
  593. current_block.last_instruction_addr = addr_before_instruction;
  594. current_block.end_addr = current_address;
  595. profiler::stat_increment(stat::COMPILE_CUT_OFF_AT_END_OF_PAGE);
  596. break;
  597. }
  598. }
  599. let previous_block = basic_blocks
  600. .range(..current_block.addr)
  601. .next_back()
  602. .filter(|(_, previous_block)| (!previous_block.has_sti))
  603. .map(|(_, previous_block)| previous_block.clone());
  604. if let Some(previous_block) = previous_block {
  605. if current_block.addr < previous_block.end_addr {
  606. // If this block overlaps with the previous block, re-analyze the previous block
  607. to_visit_stack.push(previous_block.virt_addr);
  608. let addr = previous_block.addr;
  609. let old_block = basic_blocks.remove(&addr);
  610. dbg_assert!(old_block.is_some());
  611. // Note that this does not ensure the invariant that two consecutive blocks don't
  612. // overlay. For that, we also need to check the following block.
  613. }
  614. }
  615. dbg_assert!(current_block.addr < current_block.end_addr);
  616. dbg_assert!(current_block.addr <= current_block.last_instruction_addr);
  617. dbg_assert!(current_block.last_instruction_addr < current_block.end_addr);
  618. basic_blocks.insert(current_block.addr, current_block);
  619. }
  620. dbg_assert!(pages.len() <= max_pages);
  621. for block in basic_blocks.values_mut() {
  622. if marked_as_entry.contains(&block.virt_addr) {
  623. block.is_entry_block = true;
  624. }
  625. }
  626. let basic_blocks: Vec<BasicBlock> = basic_blocks.into_iter().map(|(_, block)| block).collect();
  627. for i in 0..basic_blocks.len() - 1 {
  628. let next_block_addr = basic_blocks[i + 1].addr;
  629. let next_block_end_addr = basic_blocks[i + 1].end_addr;
  630. let next_block_is_entry = basic_blocks[i + 1].is_entry_block;
  631. let block = &basic_blocks[i];
  632. dbg_assert!(block.addr < next_block_addr);
  633. if next_block_addr < block.end_addr {
  634. dbg_log!(
  635. "Overlapping first=[from={:x} to={:x} is_entry={}] second=[from={:x} to={:x} is_entry={}]",
  636. block.addr,
  637. block.end_addr,
  638. block.is_entry_block as u8,
  639. next_block_addr,
  640. next_block_end_addr,
  641. next_block_is_entry as u8
  642. );
  643. }
  644. }
  645. basic_blocks
  646. }
  647. #[no_mangle]
  648. #[cfg(debug_assertions)]
  649. pub fn jit_force_generate_unsafe(virt_addr: i32) {
  650. dbg_assert!(
  651. !is_near_end_of_page(virt_addr as u32),
  652. "cannot force compile near end of page"
  653. );
  654. jit_increase_hotness_and_maybe_compile(
  655. virt_addr,
  656. cpu::translate_address_read(virt_addr).unwrap(),
  657. cpu::get_seg_cs() as u32,
  658. cpu::get_state_flags(),
  659. JIT_THRESHOLD,
  660. );
  661. dbg_assert!(get_jit_state().compiling.is_some());
  662. }
  663. #[inline(never)]
  664. fn jit_analyze_and_generate(
  665. ctx: &mut JitState,
  666. virt_entry_point: i32,
  667. phys_entry_point: u32,
  668. cs_offset: u32,
  669. state_flags: CachedStateFlags,
  670. ) {
  671. let page = Page::page_of(phys_entry_point);
  672. dbg_assert!(ctx.compiling.is_none());
  673. let (_, entry_points) = match ctx.entry_points.get(&page) {
  674. None => return,
  675. Some(entry_points) => entry_points,
  676. };
  677. let existing_entry_points = match ctx.pages.get(&page) {
  678. Some(PageInfo { entry_points, .. }) => HashSet::from_iter(entry_points.iter().map(|x| x.0)),
  679. None => HashSet::new(),
  680. };
  681. if entry_points
  682. .iter()
  683. .all(|entry_point| existing_entry_points.contains(entry_point))
  684. {
  685. profiler::stat_increment(stat::COMPILE_SKIPPED_NO_NEW_ENTRY_POINTS);
  686. return;
  687. }
  688. // XXX: check and remove
  689. //let old_length = entry_points.len();
  690. //entry_points.extend(existing_entry_points);
  691. //dbg_log!(
  692. // "{} + {} = {}",
  693. // entry_points.len(),
  694. // existing_entry_points.len(),
  695. // entry_points.union(&existing_entry_points).count()
  696. //);
  697. //dbg_assert!(entry_points.union(&existing_entry_points).count() == entry_points.len());
  698. profiler::stat_increment(stat::COMPILE);
  699. let cpu = CpuContext {
  700. eip: 0,
  701. prefixes: 0,
  702. cs_offset,
  703. state_flags,
  704. };
  705. dbg_assert!(
  706. cpu::translate_address_read_no_side_effects(virt_entry_point).unwrap() == phys_entry_point
  707. );
  708. let virt_page = Page::page_of(virt_entry_point as u32);
  709. let entry_points: HashSet<i32> = entry_points
  710. .iter()
  711. .map(|e| virt_page.to_address() as i32 | *e as i32)
  712. .collect();
  713. let basic_blocks = jit_find_basic_blocks(ctx, entry_points, cpu.clone());
  714. let mut pages = HashSet::new();
  715. for b in basic_blocks.iter() {
  716. // Remove this assertion once page-crossing jit is enabled
  717. dbg_assert!(Page::page_of(b.addr) == Page::page_of(b.end_addr));
  718. pages.insert(Page::page_of(b.addr));
  719. }
  720. let print = false;
  721. for b in basic_blocks.iter() {
  722. if !print {
  723. break;
  724. }
  725. let last_instruction_opcode = memory::read32s(b.last_instruction_addr);
  726. let op = opstats::decode(last_instruction_opcode as u32);
  727. dbg_log!(
  728. "BB: 0x{:x} {}{:02x} {} {}",
  729. b.addr,
  730. if op.is_0f { "0f" } else { "" },
  731. op.opcode,
  732. if b.is_entry_block { "entry" } else { "noentry" },
  733. match &b.ty {
  734. BasicBlockType::ConditionalJump {
  735. next_block_addr: Some(next_block_addr),
  736. next_block_branch_taken_addr: Some(next_block_branch_taken_addr),
  737. ..
  738. } => format!(
  739. "0x{:x} 0x{:x}",
  740. next_block_addr, next_block_branch_taken_addr
  741. ),
  742. BasicBlockType::ConditionalJump {
  743. next_block_addr: None,
  744. next_block_branch_taken_addr: Some(next_block_branch_taken_addr),
  745. ..
  746. } => format!("0x{:x}", next_block_branch_taken_addr),
  747. BasicBlockType::ConditionalJump {
  748. next_block_addr: Some(next_block_addr),
  749. next_block_branch_taken_addr: None,
  750. ..
  751. } => format!("0x{:x}", next_block_addr),
  752. BasicBlockType::ConditionalJump {
  753. next_block_addr: None,
  754. next_block_branch_taken_addr: None,
  755. ..
  756. } => format!(""),
  757. BasicBlockType::Normal {
  758. next_block_addr: Some(next_block_addr),
  759. ..
  760. } => format!("0x{:x}", next_block_addr),
  761. BasicBlockType::Normal {
  762. next_block_addr: None,
  763. ..
  764. } => format!(""),
  765. BasicBlockType::Exit => format!(""),
  766. BasicBlockType::AbsoluteEip => format!(""),
  767. }
  768. );
  769. }
  770. let graph = control_flow::make_graph(&basic_blocks);
  771. let mut structure = control_flow::loopify(&graph);
  772. if print {
  773. dbg_log!("before blockify:");
  774. for group in &structure {
  775. dbg_log!("=> Group");
  776. group.print(0);
  777. }
  778. }
  779. control_flow::blockify(&mut structure, &graph);
  780. if cfg!(debug_assertions) {
  781. control_flow::assert_invariants(&structure);
  782. }
  783. if print {
  784. dbg_log!("after blockify:");
  785. for group in &structure {
  786. dbg_log!("=> Group");
  787. group.print(0);
  788. }
  789. }
  790. if ctx.wasm_table_index_free_list.is_empty() {
  791. dbg_log!("wasm_table_index_free_list empty, clearing cache");
  792. // When no free slots are available, delete all cached modules. We could increase the
  793. // size of the table, but this way the initial size acts as an upper bound for the
  794. // number of wasm modules that we generate, which we want anyway to avoid getting our
  795. // tab killed by browsers due to memory constraints.
  796. jit_clear_cache(ctx);
  797. profiler::stat_increment(stat::INVALIDATE_ALL_MODULES_NO_FREE_WASM_INDICES);
  798. dbg_log!(
  799. "after jit_clear_cache: {} free",
  800. ctx.wasm_table_index_free_list.len(),
  801. );
  802. // This assertion can fail if all entries are pending (not possible unless
  803. // WASM_TABLE_SIZE is set very low)
  804. dbg_assert!(!ctx.wasm_table_index_free_list.is_empty());
  805. }
  806. // allocate an index in the wasm table
  807. let wasm_table_index = ctx
  808. .wasm_table_index_free_list
  809. .pop()
  810. .expect("allocate wasm table index");
  811. dbg_assert!(wasm_table_index != WasmTableIndex(0));
  812. dbg_assert!(!pages.is_empty());
  813. dbg_assert!(pages.len() <= MAX_PAGES);
  814. let basic_block_by_addr: HashMap<u32, BasicBlock> =
  815. basic_blocks.into_iter().map(|b| (b.addr, b)).collect();
  816. let entries = jit_generate_module(
  817. structure,
  818. &basic_block_by_addr,
  819. cpu,
  820. &mut ctx.wasm_builder,
  821. wasm_table_index,
  822. state_flags,
  823. );
  824. dbg_assert!(!entries.is_empty());
  825. let mut page_info = HashMap::new();
  826. for &(addr, state) in &entries {
  827. let code = page_info
  828. .entry(Page::page_of(addr))
  829. .or_insert_with(|| PageInfo {
  830. wasm_table_index,
  831. state_flags,
  832. entry_points: Vec::new(),
  833. hidden_wasm_table_indices: Vec::new(),
  834. });
  835. code.entry_points.push((addr as u16 & 0xFFF, state));
  836. }
  837. profiler::stat_increment_by(
  838. stat::COMPILE_WASM_TOTAL_BYTES,
  839. ctx.wasm_builder.get_output_len() as u64,
  840. );
  841. profiler::stat_increment_by(stat::COMPILE_PAGE, pages.len() as u64);
  842. for &p in &pages {
  843. ctx.entry_points
  844. .entry(p)
  845. .or_insert_with(|| (0, HashSet::new()));
  846. }
  847. cpu::tlb_set_has_code_multiple(&pages, true);
  848. dbg_assert!(ctx.compiling.is_none());
  849. ctx.compiling = Some((
  850. wasm_table_index,
  851. CompilingPageState::Compiling { pages: page_info },
  852. ));
  853. let phys_addr = page.to_address();
  854. // will call codegen_finalize_finished asynchronously when finished
  855. codegen_finalize(
  856. wasm_table_index,
  857. phys_addr,
  858. state_flags,
  859. ctx.wasm_builder.get_output_ptr() as u32,
  860. ctx.wasm_builder.get_output_len(),
  861. );
  862. check_jit_state_invariants(ctx);
  863. }
  864. #[no_mangle]
  865. pub fn codegen_finalize_finished(
  866. wasm_table_index: WasmTableIndex,
  867. phys_addr: u32,
  868. state_flags: CachedStateFlags,
  869. ) {
  870. let ctx = get_jit_state();
  871. dbg_assert!(wasm_table_index != WasmTableIndex(0));
  872. dbg_log!(
  873. "Finished compiling for page at {:x}",
  874. Page::page_of(phys_addr).to_address()
  875. );
  876. let pages = match mem::replace(&mut ctx.compiling, None) {
  877. None => {
  878. dbg_assert!(false);
  879. return;
  880. },
  881. Some((in_progress_wasm_table_index, CompilingPageState::CompilingWritten)) => {
  882. dbg_assert!(wasm_table_index == in_progress_wasm_table_index);
  883. profiler::stat_increment(stat::INVALIDATE_MODULE_WRITTEN_WHILE_COMPILED);
  884. free_wasm_table_index(ctx, wasm_table_index);
  885. check_jit_state_invariants(ctx);
  886. return;
  887. },
  888. Some((in_progress_wasm_table_index, CompilingPageState::Compiling { pages })) => {
  889. dbg_assert!(wasm_table_index == in_progress_wasm_table_index);
  890. dbg_assert!(!pages.is_empty());
  891. pages
  892. },
  893. };
  894. for i in 0..unsafe { cpu::valid_tlb_entries_count } {
  895. let page = unsafe { cpu::valid_tlb_entries[i as usize] };
  896. let entry = unsafe { cpu::tlb_data[page as usize] };
  897. if 0 != entry {
  898. let tlb_physical_page = Page::of_u32(
  899. (entry as u32 >> 12 ^ page as u32) - (unsafe { memory::mem8 } as u32 >> 12),
  900. );
  901. if let Some(info) = pages.get(&tlb_physical_page) {
  902. set_tlb_code(
  903. Page::of_u32(page as u32),
  904. wasm_table_index,
  905. &info.entry_points,
  906. state_flags,
  907. );
  908. }
  909. }
  910. }
  911. let mut check_for_unused_wasm_table_index = HashSet::new();
  912. for (page, mut info) in pages {
  913. if let Some(old_entry) = ctx.pages.remove(&page) {
  914. info.hidden_wasm_table_indices
  915. .extend(old_entry.hidden_wasm_table_indices);
  916. info.hidden_wasm_table_indices
  917. .push(old_entry.wasm_table_index);
  918. check_for_unused_wasm_table_index.insert(old_entry.wasm_table_index);
  919. }
  920. ctx.pages.insert(page, info);
  921. }
  922. let unused: Vec<&WasmTableIndex> = check_for_unused_wasm_table_index
  923. .iter()
  924. .filter(|&&i| ctx.pages.values().all(|page| page.wasm_table_index != i))
  925. .collect();
  926. for &index in unused {
  927. for p in ctx.pages.values_mut() {
  928. p.hidden_wasm_table_indices.retain(|&w| w != index);
  929. }
  930. dbg_log!("unused after overwrite {}", index.to_u16());
  931. profiler::stat_increment(stat::INVALIDATE_MODULE_UNUSED_AFTER_OVERWRITE);
  932. free_wasm_table_index(ctx, index);
  933. }
  934. check_jit_state_invariants(ctx);
  935. }
  936. pub fn update_tlb_code(virt_page: Page, phys_page: Page) {
  937. let ctx = get_jit_state();
  938. match ctx.pages.get(&phys_page) {
  939. Some(PageInfo {
  940. wasm_table_index,
  941. entry_points,
  942. state_flags,
  943. hidden_wasm_table_indices: _,
  944. }) => set_tlb_code(virt_page, *wasm_table_index, entry_points, *state_flags),
  945. None => cpu::clear_tlb_code(phys_page.to_u32() as i32),
  946. };
  947. }
  948. pub fn set_tlb_code(
  949. virt_page: Page,
  950. wasm_table_index: WasmTableIndex,
  951. entries: &Vec<(u16, u16)>,
  952. state_flags: CachedStateFlags,
  953. ) {
  954. let c = match unsafe { cpu::tlb_code[virt_page.to_u32() as usize] } {
  955. None => {
  956. let state_table = [u16::MAX; 0x1000];
  957. unsafe {
  958. let mut c = NonNull::new_unchecked(Box::into_raw(Box::new(cpu::Code {
  959. wasm_table_index,
  960. state_flags,
  961. state_table,
  962. })));
  963. cpu::tlb_code[virt_page.to_u32() as usize] = Some(c);
  964. c.as_mut()
  965. }
  966. },
  967. Some(mut c) => unsafe {
  968. let c = c.as_mut();
  969. c.state_table.fill(u16::MAX);
  970. c.state_flags = state_flags;
  971. c.wasm_table_index = wasm_table_index;
  972. c
  973. },
  974. };
  975. for &(addr, state) in entries {
  976. dbg_assert!(state != u16::MAX);
  977. c.state_table[addr as usize] = state;
  978. }
  979. }
  980. fn jit_generate_module(
  981. structure: Vec<WasmStructure>,
  982. basic_blocks: &HashMap<u32, BasicBlock>,
  983. mut cpu: CpuContext,
  984. builder: &mut WasmBuilder,
  985. wasm_table_index: WasmTableIndex,
  986. state_flags: CachedStateFlags,
  987. ) -> Vec<(u32, u16)> {
  988. builder.reset();
  989. let mut register_locals = (0..8)
  990. .map(|i| {
  991. builder.load_fixed_i32(global_pointers::get_reg32_offset(i));
  992. builder.set_new_local()
  993. })
  994. .collect();
  995. builder.const_i32(0);
  996. let instruction_counter = builder.set_new_local();
  997. let exit_label = builder.block_void();
  998. let exit_with_fault_label = builder.block_void();
  999. let main_loop_label = builder.loop_void();
  1000. if JIT_USE_LOOP_SAFETY {
  1001. builder.get_local(&instruction_counter);
  1002. builder.const_i32(cpu::LOOP_COUNTER);
  1003. builder.geu_i32();
  1004. if cfg!(feature = "profiler") {
  1005. builder.if_void();
  1006. codegen::gen_debug_track_jit_exit(builder, 0);
  1007. builder.br(exit_label);
  1008. builder.block_end();
  1009. }
  1010. else {
  1011. builder.br_if(exit_label);
  1012. }
  1013. }
  1014. let brtable_default = builder.block_void();
  1015. let ctx = &mut JitContext {
  1016. cpu: &mut cpu,
  1017. builder,
  1018. register_locals: &mut register_locals,
  1019. start_of_current_instruction: 0,
  1020. exit_with_fault_label,
  1021. exit_label,
  1022. current_instruction: Instruction::Other,
  1023. previous_instruction: Instruction::Other,
  1024. instruction_counter,
  1025. };
  1026. let entry_blocks = {
  1027. let mut nodes = &structure;
  1028. let result;
  1029. loop {
  1030. match &nodes[0] {
  1031. WasmStructure::Dispatcher(e) => {
  1032. result = e.clone();
  1033. break;
  1034. },
  1035. WasmStructure::Loop { .. } => {
  1036. dbg_assert!(false);
  1037. },
  1038. WasmStructure::BasicBlock(_) => {
  1039. dbg_assert!(false);
  1040. },
  1041. // Note: We could use these blocks as entry points, which will yield
  1042. // more entries for free, but it requires adding those to the dispatcher
  1043. // It's to be investigated if this yields a performance improvement
  1044. // See also the comment at the bottom of this function when creating entry
  1045. // points
  1046. WasmStructure::Block(children) => {
  1047. nodes = children;
  1048. },
  1049. }
  1050. }
  1051. result
  1052. };
  1053. let mut index_for_addr = HashMap::new();
  1054. for (i, &addr) in entry_blocks.iter().enumerate() {
  1055. index_for_addr.insert(addr, i as i32);
  1056. }
  1057. for b in basic_blocks.values() {
  1058. if !index_for_addr.contains_key(&b.addr) {
  1059. let i = index_for_addr.len();
  1060. index_for_addr.insert(b.addr, i as i32);
  1061. }
  1062. }
  1063. let mut label_for_addr: HashMap<u32, (Label, Option<i32>)> = HashMap::new();
  1064. enum Work {
  1065. WasmStructure(WasmStructure),
  1066. BlockEnd {
  1067. label: Label,
  1068. targets: Vec<u32>,
  1069. olds: HashMap<u32, (Label, Option<i32>)>,
  1070. },
  1071. LoopEnd {
  1072. label: Label,
  1073. entries: Vec<u32>,
  1074. olds: HashMap<u32, (Label, Option<i32>)>,
  1075. },
  1076. }
  1077. let mut work: VecDeque<Work> = structure
  1078. .into_iter()
  1079. .map(|x| Work::WasmStructure(x))
  1080. .collect();
  1081. while let Some(block) = work.pop_front() {
  1082. let next_addr: Option<Vec<u32>> = work.iter().find_map(|x| match x {
  1083. Work::WasmStructure(l) => Some(l.head().collect()),
  1084. _ => None,
  1085. });
  1086. let target_block = &ctx.builder.arg_local_initial_state.unsafe_clone();
  1087. match block {
  1088. Work::WasmStructure(WasmStructure::BasicBlock(addr)) => {
  1089. let block = basic_blocks.get(&addr).unwrap();
  1090. jit_generate_basic_block(ctx, block);
  1091. if block.has_sti {
  1092. match block.ty {
  1093. BasicBlockType::ConditionalJump {
  1094. condition,
  1095. jump_offset,
  1096. jump_offset_is_32,
  1097. ..
  1098. } => {
  1099. codegen::gen_set_eip_low_bits(
  1100. ctx.builder,
  1101. block.end_addr as i32 & 0xFFF,
  1102. );
  1103. codegen::gen_condition_fn(ctx, condition);
  1104. ctx.builder.if_void();
  1105. if jump_offset_is_32 {
  1106. codegen::gen_relative_jump(ctx.builder, jump_offset);
  1107. }
  1108. else {
  1109. codegen::gen_jmp_rel16(ctx.builder, jump_offset as u16);
  1110. }
  1111. ctx.builder.block_end();
  1112. },
  1113. BasicBlockType::Normal {
  1114. jump_offset,
  1115. jump_offset_is_32,
  1116. ..
  1117. } => {
  1118. if jump_offset_is_32 {
  1119. codegen::gen_set_eip_low_bits_and_jump_rel32(
  1120. ctx.builder,
  1121. block.end_addr as i32 & 0xFFF,
  1122. jump_offset,
  1123. );
  1124. }
  1125. else {
  1126. codegen::gen_set_eip_low_bits(
  1127. ctx.builder,
  1128. block.end_addr as i32 & 0xFFF,
  1129. );
  1130. codegen::gen_jmp_rel16(ctx.builder, jump_offset as u16);
  1131. }
  1132. },
  1133. BasicBlockType::Exit => {},
  1134. BasicBlockType::AbsoluteEip => {},
  1135. };
  1136. codegen::gen_debug_track_jit_exit(ctx.builder, block.last_instruction_addr);
  1137. codegen::gen_move_registers_from_locals_to_memory(ctx);
  1138. codegen::gen_fn0_const(ctx.builder, "handle_irqs");
  1139. codegen::gen_update_instruction_counter(ctx);
  1140. ctx.builder.return_();
  1141. continue;
  1142. }
  1143. match &block.ty {
  1144. BasicBlockType::Exit => {
  1145. // Exit this function
  1146. codegen::gen_debug_track_jit_exit(ctx.builder, block.last_instruction_addr);
  1147. codegen::gen_profiler_stat_increment(ctx.builder, stat::DIRECT_EXIT);
  1148. ctx.builder.br(ctx.exit_label);
  1149. },
  1150. BasicBlockType::AbsoluteEip => {
  1151. // Check if we can stay in this module, if not exit
  1152. codegen::gen_get_eip(ctx.builder);
  1153. ctx.builder.const_i32(wasm_table_index.to_u16() as i32);
  1154. ctx.builder.const_i32(state_flags.to_u32() as i32);
  1155. ctx.builder.call_fn3_ret("jit_find_cache_entry_in_page");
  1156. ctx.builder.tee_local(target_block);
  1157. ctx.builder.const_i32(0);
  1158. ctx.builder.ge_i32();
  1159. // TODO: Could make this unconditional by including exit_label in the main br_table
  1160. ctx.builder.br_if(main_loop_label);
  1161. codegen::gen_debug_track_jit_exit(ctx.builder, block.last_instruction_addr);
  1162. ctx.builder.br(ctx.exit_label);
  1163. },
  1164. &BasicBlockType::Normal {
  1165. next_block_addr: None,
  1166. jump_offset,
  1167. jump_offset_is_32,
  1168. } => {
  1169. if jump_offset_is_32 {
  1170. codegen::gen_set_eip_low_bits_and_jump_rel32(
  1171. ctx.builder,
  1172. block.end_addr as i32 & 0xFFF,
  1173. jump_offset,
  1174. );
  1175. }
  1176. else {
  1177. codegen::gen_set_eip_low_bits(
  1178. ctx.builder,
  1179. block.end_addr as i32 & 0xFFF,
  1180. );
  1181. codegen::gen_jmp_rel16(ctx.builder, jump_offset as u16);
  1182. }
  1183. codegen::gen_debug_track_jit_exit(ctx.builder, block.last_instruction_addr);
  1184. codegen::gen_profiler_stat_increment(ctx.builder, stat::DIRECT_EXIT);
  1185. ctx.builder.br(ctx.exit_label);
  1186. },
  1187. &BasicBlockType::Normal {
  1188. next_block_addr: Some(next_block_addr),
  1189. jump_offset,
  1190. jump_offset_is_32,
  1191. } => {
  1192. // Unconditional jump to next basic block
  1193. // - All instructions that don't change eip
  1194. // - Unconditional jumps
  1195. if Page::page_of(next_block_addr) != Page::page_of(block.addr) {
  1196. if jump_offset_is_32 {
  1197. codegen::gen_set_eip_low_bits_and_jump_rel32(
  1198. ctx.builder,
  1199. block.end_addr as i32 & 0xFFF,
  1200. jump_offset,
  1201. );
  1202. }
  1203. else {
  1204. codegen::gen_set_eip_low_bits(
  1205. ctx.builder,
  1206. block.end_addr as i32 & 0xFFF,
  1207. );
  1208. codegen::gen_jmp_rel16(ctx.builder, jump_offset as u16);
  1209. }
  1210. codegen::gen_profiler_stat_increment(
  1211. ctx.builder,
  1212. stat::NORMAL_PAGE_CHANGE,
  1213. );
  1214. codegen::gen_page_switch_check(
  1215. ctx,
  1216. next_block_addr,
  1217. block.last_instruction_addr,
  1218. );
  1219. #[cfg(debug_assertions)]
  1220. codegen::gen_fn2_const(
  1221. ctx.builder,
  1222. "check_page_switch",
  1223. block.addr,
  1224. next_block_addr,
  1225. );
  1226. }
  1227. if next_addr
  1228. .as_ref()
  1229. .map_or(false, |n| n.contains(&next_block_addr))
  1230. {
  1231. // Blocks are consecutive
  1232. if next_addr.unwrap().len() > 1 {
  1233. let target_index = *index_for_addr.get(&next_block_addr).unwrap();
  1234. if cfg!(feature = "profiler") {
  1235. ctx.builder.const_i32(target_index);
  1236. ctx.builder.call_fn1("debug_set_dispatcher_target");
  1237. }
  1238. ctx.builder.const_i32(target_index);
  1239. ctx.builder.set_local(target_block);
  1240. codegen::gen_profiler_stat_increment(
  1241. ctx.builder,
  1242. stat::NORMAL_FALLTHRU_WITH_TARGET_BLOCK,
  1243. );
  1244. }
  1245. else {
  1246. codegen::gen_profiler_stat_increment(
  1247. ctx.builder,
  1248. stat::NORMAL_FALLTHRU,
  1249. );
  1250. }
  1251. }
  1252. else {
  1253. let &(br, target_index) = label_for_addr.get(&next_block_addr).unwrap();
  1254. if let Some(target_index) = target_index {
  1255. if cfg!(feature = "profiler") {
  1256. ctx.builder.const_i32(target_index);
  1257. ctx.builder.call_fn1("debug_set_dispatcher_target");
  1258. }
  1259. ctx.builder.const_i32(target_index);
  1260. ctx.builder.set_local(target_block);
  1261. codegen::gen_profiler_stat_increment(
  1262. ctx.builder,
  1263. stat::NORMAL_BRANCH_WITH_TARGET_BLOCK,
  1264. );
  1265. }
  1266. else {
  1267. codegen::gen_profiler_stat_increment(
  1268. ctx.builder,
  1269. stat::NORMAL_BRANCH,
  1270. );
  1271. }
  1272. ctx.builder.br(br);
  1273. }
  1274. },
  1275. &BasicBlockType::ConditionalJump {
  1276. next_block_addr,
  1277. next_block_branch_taken_addr,
  1278. condition,
  1279. jump_offset,
  1280. jump_offset_is_32,
  1281. } => {
  1282. // Conditional jump to next basic block
  1283. // - jnz, jc, loop, jcxz, etc.
  1284. // Generate:
  1285. // (1) condition()
  1286. // (2) br_if()
  1287. // (3) br()
  1288. // Except:
  1289. // If we need to update eip in case (2), it's replaced by if { update_eip(); br() }
  1290. // If case (3) can fall through to the next basic block, the branch is eliminated
  1291. // Dispatcher target writes can be generated in either case
  1292. // Condition may be inverted if it helps generate a fallthrough instead of the second branch
  1293. codegen::gen_profiler_stat_increment(ctx.builder, stat::CONDITIONAL_JUMP);
  1294. #[derive(PartialEq)]
  1295. enum Case {
  1296. BranchTaken,
  1297. BranchNotTaken,
  1298. }
  1299. let mut handle_case = |case: Case, is_first| {
  1300. // first case generates condition and *has* to branch away,
  1301. // second case branches unconditionally or falls through
  1302. if is_first {
  1303. if case == Case::BranchNotTaken {
  1304. codegen::gen_condition_fn_negated(ctx, condition);
  1305. }
  1306. else {
  1307. codegen::gen_condition_fn(ctx, condition);
  1308. }
  1309. }
  1310. let next_block_addr = if case == Case::BranchTaken {
  1311. next_block_branch_taken_addr
  1312. }
  1313. else {
  1314. next_block_addr
  1315. };
  1316. if let Some(next_block_addr) = next_block_addr {
  1317. if Page::page_of(next_block_addr) != Page::page_of(block.addr) {
  1318. dbg_assert!(case == Case::BranchTaken); // currently not possible in other case
  1319. if is_first {
  1320. ctx.builder.if_i32();
  1321. }
  1322. if jump_offset_is_32 {
  1323. codegen::gen_set_eip_low_bits_and_jump_rel32(
  1324. ctx.builder,
  1325. block.end_addr as i32 & 0xFFF,
  1326. jump_offset,
  1327. );
  1328. }
  1329. else {
  1330. codegen::gen_set_eip_low_bits(
  1331. ctx.builder,
  1332. block.end_addr as i32 & 0xFFF,
  1333. );
  1334. codegen::gen_jmp_rel16(ctx.builder, jump_offset as u16);
  1335. }
  1336. codegen::gen_profiler_stat_increment(
  1337. ctx.builder,
  1338. stat::CONDITIONAL_JUMP_PAGE_CHANGE,
  1339. );
  1340. codegen::gen_page_switch_check(
  1341. ctx,
  1342. next_block_addr,
  1343. block.last_instruction_addr,
  1344. );
  1345. #[cfg(debug_assertions)]
  1346. codegen::gen_fn2_const(
  1347. ctx.builder,
  1348. "check_page_switch",
  1349. block.addr,
  1350. next_block_addr,
  1351. );
  1352. if is_first {
  1353. ctx.builder.const_i32(1);
  1354. ctx.builder.else_();
  1355. ctx.builder.const_i32(0);
  1356. ctx.builder.block_end();
  1357. }
  1358. }
  1359. if next_addr
  1360. .as_ref()
  1361. .map_or(false, |n| n.contains(&next_block_addr))
  1362. {
  1363. // blocks are consecutive
  1364. // fallthrough, has to be second
  1365. dbg_assert!(!is_first);
  1366. if next_addr.as_ref().unwrap().len() > 1 {
  1367. let target_index =
  1368. *index_for_addr.get(&next_block_addr).unwrap();
  1369. if cfg!(feature = "profiler") {
  1370. ctx.builder.const_i32(target_index);
  1371. ctx.builder.call_fn1("debug_set_dispatcher_target");
  1372. }
  1373. ctx.builder.const_i32(target_index);
  1374. ctx.builder.set_local(target_block);
  1375. codegen::gen_profiler_stat_increment(
  1376. ctx.builder,
  1377. stat::CONDITIONAL_JUMP_FALLTHRU_WITH_TARGET_BLOCK,
  1378. );
  1379. }
  1380. else {
  1381. codegen::gen_profiler_stat_increment(
  1382. ctx.builder,
  1383. stat::CONDITIONAL_JUMP_FALLTHRU,
  1384. );
  1385. }
  1386. }
  1387. else {
  1388. let &(br, target_index) =
  1389. label_for_addr.get(&next_block_addr).unwrap();
  1390. if let Some(target_index) = target_index {
  1391. if cfg!(feature = "profiler") {
  1392. // Note: Currently called unconditionally, even if the
  1393. // br_if below doesn't branch
  1394. ctx.builder.const_i32(target_index);
  1395. ctx.builder.call_fn1("debug_set_dispatcher_target");
  1396. }
  1397. ctx.builder.const_i32(target_index);
  1398. ctx.builder.set_local(target_block);
  1399. }
  1400. if is_first {
  1401. if cfg!(feature = "profiler") {
  1402. ctx.builder.if_void();
  1403. codegen::gen_profiler_stat_increment(
  1404. ctx.builder,
  1405. if target_index.is_some() {
  1406. stat::CONDITIONAL_JUMP_BRANCH_WITH_TARGET_BLOCK
  1407. }
  1408. else {
  1409. stat::CONDITIONAL_JUMP_BRANCH
  1410. },
  1411. );
  1412. ctx.builder.br(br);
  1413. ctx.builder.block_end();
  1414. }
  1415. else {
  1416. ctx.builder.br_if(br);
  1417. }
  1418. }
  1419. else {
  1420. codegen::gen_profiler_stat_increment(
  1421. ctx.builder,
  1422. if target_index.is_some() {
  1423. stat::CONDITIONAL_JUMP_BRANCH_WITH_TARGET_BLOCK
  1424. }
  1425. else {
  1426. stat::CONDITIONAL_JUMP_BRANCH
  1427. },
  1428. );
  1429. ctx.builder.br(br);
  1430. }
  1431. }
  1432. }
  1433. else {
  1434. // target is outside of this module, update eip and exit
  1435. if is_first {
  1436. ctx.builder.if_void();
  1437. }
  1438. if case == Case::BranchTaken {
  1439. if jump_offset_is_32 {
  1440. codegen::gen_set_eip_low_bits_and_jump_rel32(
  1441. ctx.builder,
  1442. block.end_addr as i32 & 0xFFF,
  1443. jump_offset,
  1444. );
  1445. }
  1446. else {
  1447. codegen::gen_set_eip_low_bits(
  1448. ctx.builder,
  1449. block.end_addr as i32 & 0xFFF,
  1450. );
  1451. codegen::gen_jmp_rel16(ctx.builder, jump_offset as u16);
  1452. }
  1453. }
  1454. else {
  1455. codegen::gen_set_eip_low_bits(
  1456. ctx.builder,
  1457. block.end_addr as i32 & 0xFFF,
  1458. );
  1459. }
  1460. codegen::gen_debug_track_jit_exit(
  1461. ctx.builder,
  1462. block.last_instruction_addr,
  1463. );
  1464. codegen::gen_profiler_stat_increment(
  1465. ctx.builder,
  1466. stat::CONDITIONAL_JUMP_EXIT,
  1467. );
  1468. ctx.builder.br(ctx.exit_label);
  1469. if is_first {
  1470. ctx.builder.block_end();
  1471. }
  1472. }
  1473. };
  1474. let branch_taken_is_fallthrough = next_block_branch_taken_addr
  1475. .map_or(false, |addr| {
  1476. next_addr.as_ref().map_or(false, |n| n.contains(&addr))
  1477. });
  1478. let branch_not_taken_is_fallthrough = next_block_addr
  1479. .map_or(false, |addr| {
  1480. next_addr.as_ref().map_or(false, |n| n.contains(&addr))
  1481. });
  1482. if branch_not_taken_is_fallthrough && branch_taken_is_fallthrough {
  1483. let next_block_addr = next_block_addr.unwrap();
  1484. let next_block_branch_taken_addr =
  1485. next_block_branch_taken_addr.unwrap();
  1486. dbg_assert!(
  1487. Page::page_of(next_block_addr) == Page::page_of(block.addr)
  1488. ); // currently not possible
  1489. if Page::page_of(next_block_branch_taken_addr)
  1490. != Page::page_of(block.addr)
  1491. {
  1492. if jump_offset_is_32 {
  1493. codegen::gen_set_eip_low_bits_and_jump_rel32(
  1494. ctx.builder,
  1495. block.end_addr as i32 & 0xFFF,
  1496. jump_offset,
  1497. );
  1498. }
  1499. else {
  1500. codegen::gen_set_eip_low_bits(
  1501. ctx.builder,
  1502. block.end_addr as i32 & 0xFFF,
  1503. );
  1504. codegen::gen_jmp_rel16(ctx.builder, jump_offset as u16);
  1505. }
  1506. codegen::gen_profiler_stat_increment(
  1507. ctx.builder,
  1508. stat::CONDITIONAL_JUMP_PAGE_CHANGE,
  1509. );
  1510. codegen::gen_page_switch_check(
  1511. ctx,
  1512. next_block_branch_taken_addr,
  1513. block.last_instruction_addr,
  1514. );
  1515. #[cfg(debug_assertions)]
  1516. codegen::gen_fn2_const(
  1517. ctx.builder,
  1518. "check_page_switch",
  1519. block.addr,
  1520. next_block_branch_taken_addr,
  1521. );
  1522. }
  1523. if next_addr.unwrap().len() > 1 {
  1524. let target_index_taken =
  1525. *index_for_addr.get(&next_block_branch_taken_addr).unwrap();
  1526. let target_index_not_taken =
  1527. *index_for_addr.get(&next_block_addr).unwrap();
  1528. codegen::gen_condition_fn(ctx, condition);
  1529. ctx.builder.if_i32();
  1530. ctx.builder.const_i32(target_index_taken);
  1531. ctx.builder.else_();
  1532. ctx.builder.const_i32(target_index_not_taken);
  1533. ctx.builder.block_end();
  1534. ctx.builder.set_local(target_block);
  1535. }
  1536. }
  1537. else if branch_taken_is_fallthrough {
  1538. handle_case(Case::BranchNotTaken, true);
  1539. handle_case(Case::BranchTaken, false);
  1540. }
  1541. else {
  1542. handle_case(Case::BranchTaken, true);
  1543. handle_case(Case::BranchNotTaken, false);
  1544. }
  1545. },
  1546. }
  1547. },
  1548. Work::WasmStructure(WasmStructure::Dispatcher(entries)) => {
  1549. profiler::stat_increment(stat::COMPILE_DISPATCHER);
  1550. if cfg!(feature = "profiler") {
  1551. ctx.builder.get_local(target_block);
  1552. ctx.builder.const_i32(index_for_addr.len() as i32);
  1553. ctx.builder.call_fn2("check_dispatcher_target");
  1554. }
  1555. if entries.len() > BRTABLE_CUTOFF {
  1556. // generate a brtable
  1557. codegen::gen_profiler_stat_increment(ctx.builder, stat::DISPATCHER_LARGE);
  1558. let mut cases = Vec::new();
  1559. for &addr in &entries {
  1560. let &(label, target_index) = label_for_addr.get(&addr).unwrap();
  1561. let &index = index_for_addr.get(&addr).unwrap();
  1562. dbg_assert!(target_index.is_none() || target_index == Some(index));
  1563. while index as usize >= cases.len() {
  1564. cases.push(brtable_default);
  1565. }
  1566. cases[index as usize] = label;
  1567. }
  1568. ctx.builder.get_local(target_block);
  1569. ctx.builder.brtable(brtable_default, &mut cases.iter());
  1570. }
  1571. else {
  1572. // generate a if target == block.addr then br block.label ...
  1573. codegen::gen_profiler_stat_increment(ctx.builder, stat::DISPATCHER_SMALL);
  1574. let nexts: HashSet<u32> = next_addr
  1575. .as_ref()
  1576. .map_or(HashSet::new(), |nexts| nexts.iter().copied().collect());
  1577. for &addr in &entries {
  1578. if nexts.contains(&addr) {
  1579. continue;
  1580. }
  1581. let index = *index_for_addr.get(&addr).unwrap();
  1582. let &(label, _) = label_for_addr.get(&addr).unwrap();
  1583. ctx.builder.get_local(target_block);
  1584. ctx.builder.const_i32(index);
  1585. ctx.builder.eq_i32();
  1586. ctx.builder.br_if(label);
  1587. }
  1588. }
  1589. },
  1590. Work::WasmStructure(WasmStructure::Loop(children)) => {
  1591. profiler::stat_increment(stat::COMPILE_WASM_LOOP);
  1592. let entries: Vec<u32> = children[0].head().collect();
  1593. let label = ctx.builder.loop_void();
  1594. codegen::gen_profiler_stat_increment(ctx.builder, stat::LOOP);
  1595. if entries.len() == 1 {
  1596. let addr = entries[0];
  1597. codegen::gen_set_eip_low_bits(ctx.builder, addr as i32 & 0xFFF);
  1598. profiler::stat_increment(stat::COMPILE_WITH_LOOP_SAFETY);
  1599. codegen::gen_profiler_stat_increment(ctx.builder, stat::LOOP_SAFETY);
  1600. if JIT_USE_LOOP_SAFETY {
  1601. ctx.builder.get_local(&ctx.instruction_counter);
  1602. ctx.builder.const_i32(cpu::LOOP_COUNTER);
  1603. ctx.builder.geu_i32();
  1604. if cfg!(feature = "profiler") {
  1605. ctx.builder.if_void();
  1606. codegen::gen_debug_track_jit_exit(ctx.builder, addr);
  1607. ctx.builder.br(exit_label);
  1608. ctx.builder.block_end();
  1609. }
  1610. else {
  1611. ctx.builder.br_if(exit_label);
  1612. }
  1613. }
  1614. }
  1615. let mut olds = HashMap::new();
  1616. for &target in entries.iter() {
  1617. let index = if entries.len() == 1 {
  1618. None
  1619. }
  1620. else {
  1621. Some(*index_for_addr.get(&target).unwrap())
  1622. };
  1623. let old = label_for_addr.insert(target, (label, index));
  1624. if let Some(old) = old {
  1625. olds.insert(target, old);
  1626. }
  1627. }
  1628. work.push_front(Work::LoopEnd {
  1629. label,
  1630. entries,
  1631. olds,
  1632. });
  1633. for c in children.into_iter().rev() {
  1634. work.push_front(Work::WasmStructure(c));
  1635. }
  1636. },
  1637. Work::LoopEnd {
  1638. label,
  1639. entries,
  1640. olds,
  1641. } => {
  1642. for target in entries {
  1643. let old = label_for_addr.remove(&target);
  1644. dbg_assert!(old.map(|(l, _)| l) == Some(label));
  1645. }
  1646. for (target, old) in olds {
  1647. let old = label_for_addr.insert(target, old);
  1648. dbg_assert!(old.is_none());
  1649. }
  1650. ctx.builder.block_end();
  1651. },
  1652. Work::WasmStructure(WasmStructure::Block(children)) => {
  1653. profiler::stat_increment(stat::COMPILE_WASM_BLOCK);
  1654. let targets = next_addr.clone().unwrap();
  1655. let label = ctx.builder.block_void();
  1656. let mut olds = HashMap::new();
  1657. for &target in targets.iter() {
  1658. let index = if targets.len() == 1 {
  1659. None
  1660. }
  1661. else {
  1662. Some(*index_for_addr.get(&target).unwrap())
  1663. };
  1664. let old = label_for_addr.insert(target, (label, index));
  1665. if let Some(old) = old {
  1666. olds.insert(target, old);
  1667. }
  1668. }
  1669. work.push_front(Work::BlockEnd {
  1670. label,
  1671. targets,
  1672. olds,
  1673. });
  1674. for c in children.into_iter().rev() {
  1675. work.push_front(Work::WasmStructure(c));
  1676. }
  1677. },
  1678. Work::BlockEnd {
  1679. label,
  1680. targets,
  1681. olds,
  1682. } => {
  1683. for target in targets {
  1684. let old = label_for_addr.remove(&target);
  1685. dbg_assert!(old.map(|(l, _)| l) == Some(label));
  1686. }
  1687. for (target, old) in olds {
  1688. let old = label_for_addr.insert(target, old);
  1689. dbg_assert!(old.is_none());
  1690. }
  1691. ctx.builder.block_end();
  1692. },
  1693. }
  1694. }
  1695. dbg_assert!(label_for_addr.is_empty());
  1696. {
  1697. ctx.builder.block_end(); // default case for the brtable
  1698. ctx.builder.unreachable();
  1699. }
  1700. {
  1701. ctx.builder.block_end(); // main loop
  1702. }
  1703. {
  1704. // exit-with-fault case
  1705. ctx.builder.block_end();
  1706. codegen::gen_move_registers_from_locals_to_memory(ctx);
  1707. codegen::gen_fn0_const(ctx.builder, "trigger_fault_end_jit");
  1708. codegen::gen_update_instruction_counter(ctx);
  1709. ctx.builder.return_();
  1710. }
  1711. {
  1712. // exit
  1713. ctx.builder.block_end();
  1714. codegen::gen_move_registers_from_locals_to_memory(ctx);
  1715. codegen::gen_update_instruction_counter(ctx);
  1716. }
  1717. for local in ctx.register_locals.drain(..) {
  1718. ctx.builder.free_local(local);
  1719. }
  1720. ctx.builder
  1721. .free_local(ctx.instruction_counter.unsafe_clone());
  1722. ctx.builder.finish();
  1723. let entries = Vec::from_iter(entry_blocks.iter().map(|addr| {
  1724. let block = basic_blocks.get(&addr).unwrap();
  1725. let index = *index_for_addr.get(&addr).unwrap();
  1726. profiler::stat_increment(stat::COMPILE_ENTRY_POINT);
  1727. dbg_assert!(block.addr < block.end_addr);
  1728. // Note: We also insert blocks that weren't originally marked as entries here
  1729. // This doesn't have any downside, besides making the hash table slightly larger
  1730. let initial_state = index.safe_to_u16();
  1731. (block.addr, initial_state)
  1732. }));
  1733. for b in basic_blocks.values() {
  1734. if b.is_entry_block {
  1735. dbg_assert!(entries.iter().find(|(addr, _)| *addr == b.addr).is_some());
  1736. }
  1737. }
  1738. return entries;
  1739. }
  1740. fn jit_generate_basic_block(ctx: &mut JitContext, block: &BasicBlock) {
  1741. let needs_eip_updated = match block.ty {
  1742. BasicBlockType::Exit => true,
  1743. _ => false,
  1744. };
  1745. profiler::stat_increment(stat::COMPILE_BASIC_BLOCK);
  1746. let start_addr = block.addr;
  1747. let last_instruction_addr = block.last_instruction_addr;
  1748. let stop_addr = block.end_addr;
  1749. // First iteration of do-while assumes the caller confirms this condition
  1750. dbg_assert!(!is_near_end_of_page(start_addr));
  1751. if cfg!(feature = "profiler") {
  1752. ctx.builder.const_i32(start_addr as i32);
  1753. ctx.builder.call_fn1("enter_basic_block");
  1754. }
  1755. ctx.builder.get_local(&ctx.instruction_counter);
  1756. ctx.builder.const_i32(block.number_of_instructions as i32);
  1757. ctx.builder.add_i32();
  1758. ctx.builder.set_local(&ctx.instruction_counter);
  1759. ctx.cpu.eip = start_addr;
  1760. ctx.current_instruction = Instruction::Other;
  1761. ctx.previous_instruction = Instruction::Other;
  1762. loop {
  1763. let mut instruction = 0;
  1764. if cfg!(feature = "profiler") {
  1765. instruction = memory::read32s(ctx.cpu.eip) as u32;
  1766. opstats::gen_opstats(ctx.builder, instruction);
  1767. opstats::record_opstat_compiled(instruction);
  1768. }
  1769. if ctx.cpu.eip == last_instruction_addr {
  1770. // Before the last instruction:
  1771. // - Set eip to *after* the instruction
  1772. // - Set previous_eip to *before* the instruction
  1773. if needs_eip_updated {
  1774. codegen::gen_set_previous_eip_offset_from_eip_with_low_bits(
  1775. ctx.builder,
  1776. last_instruction_addr as i32 & 0xFFF,
  1777. );
  1778. codegen::gen_set_eip_low_bits(ctx.builder, stop_addr as i32 & 0xFFF);
  1779. }
  1780. }
  1781. let wasm_length_before = ctx.builder.instruction_body_length();
  1782. ctx.start_of_current_instruction = ctx.cpu.eip;
  1783. let start_eip = ctx.cpu.eip;
  1784. let mut instruction_flags = 0;
  1785. jit_instructions::jit_instruction(ctx, &mut instruction_flags);
  1786. let end_eip = ctx.cpu.eip;
  1787. let instruction_length = end_eip - start_eip;
  1788. let was_block_boundary = instruction_flags & JIT_INSTR_BLOCK_BOUNDARY_FLAG != 0;
  1789. let wasm_length = ctx.builder.instruction_body_length() - wasm_length_before;
  1790. opstats::record_opstat_size_wasm(instruction, wasm_length as u64);
  1791. dbg_assert!((end_eip == stop_addr) == (start_eip == last_instruction_addr));
  1792. dbg_assert!(instruction_length < MAX_INSTRUCTION_LENGTH);
  1793. let end_addr = ctx.cpu.eip;
  1794. if end_addr == stop_addr {
  1795. // no page was crossed
  1796. dbg_assert!(Page::page_of(end_addr) == Page::page_of(start_addr));
  1797. break;
  1798. }
  1799. if was_block_boundary || is_near_end_of_page(end_addr) || end_addr > stop_addr {
  1800. dbg_log!(
  1801. "Overlapping basic blocks start={:x} expected_end={:x} end={:x} was_block_boundary={} near_end_of_page={}",
  1802. start_addr,
  1803. stop_addr,
  1804. end_addr,
  1805. was_block_boundary,
  1806. is_near_end_of_page(end_addr)
  1807. );
  1808. dbg_assert!(false);
  1809. break;
  1810. }
  1811. ctx.previous_instruction = mem::replace(&mut ctx.current_instruction, Instruction::Other);
  1812. }
  1813. }
  1814. pub fn jit_increase_hotness_and_maybe_compile(
  1815. virt_address: i32,
  1816. phys_address: u32,
  1817. cs_offset: u32,
  1818. state_flags: CachedStateFlags,
  1819. heat: u32,
  1820. ) {
  1821. let ctx = get_jit_state();
  1822. let page = Page::page_of(phys_address);
  1823. let (hotness, entry_points) = ctx.entry_points.entry(page).or_insert_with(|| {
  1824. cpu::tlb_set_has_code(page, true);
  1825. profiler::stat_increment(stat::RUN_INTERPRETED_NEW_PAGE);
  1826. (0, HashSet::new())
  1827. });
  1828. if !is_near_end_of_page(phys_address) {
  1829. entry_points.insert(phys_address as u16 & 0xFFF);
  1830. }
  1831. *hotness += heat;
  1832. if *hotness >= JIT_THRESHOLD {
  1833. if ctx.compiling.is_some() {
  1834. return;
  1835. }
  1836. // only try generating if we're in the correct address space
  1837. if cpu::translate_address_read_no_side_effects(virt_address) == Ok(phys_address) {
  1838. *hotness = 0;
  1839. jit_analyze_and_generate(ctx, virt_address, phys_address, cs_offset, state_flags)
  1840. }
  1841. else {
  1842. profiler::stat_increment(stat::COMPILE_WRONG_ADDRESS_SPACE);
  1843. }
  1844. };
  1845. }
  1846. fn free_wasm_table_index(ctx: &mut JitState, wasm_table_index: WasmTableIndex) {
  1847. if CHECK_JIT_STATE_INVARIANTS {
  1848. dbg_assert!(!ctx.wasm_table_index_free_list.contains(&wasm_table_index));
  1849. match &ctx.compiling {
  1850. Some((wasm_table_index_compiling, _)) => {
  1851. dbg_assert!(
  1852. *wasm_table_index_compiling != wasm_table_index,
  1853. "Attempt to free wasm table index that is currently being compiled"
  1854. );
  1855. },
  1856. _ => {},
  1857. }
  1858. dbg_assert!(
  1859. !ctx.pages
  1860. .values()
  1861. .any(|info| info.wasm_table_index == wasm_table_index)
  1862. );
  1863. dbg_assert!(
  1864. !ctx.pages
  1865. .values()
  1866. .any(|info| info.hidden_wasm_table_indices.contains(&wasm_table_index))
  1867. );
  1868. for i in 0..unsafe { cpu::valid_tlb_entries_count } {
  1869. let page = unsafe { cpu::valid_tlb_entries[i as usize] };
  1870. unsafe {
  1871. match cpu::tlb_code[page as usize] {
  1872. None => {},
  1873. Some(c) => {
  1874. let c = c.as_ref();
  1875. dbg_assert!(c.wasm_table_index != wasm_table_index);
  1876. },
  1877. }
  1878. }
  1879. }
  1880. }
  1881. ctx.wasm_table_index_free_list.push(wasm_table_index);
  1882. // It is not strictly necessary to clear the function, but it will fail more predictably if we
  1883. // accidentally use the function and may garbage collect unused modules earlier
  1884. jit_clear_func(wasm_table_index);
  1885. }
  1886. /// Register a write in this page: Delete all present code
  1887. pub fn jit_dirty_page(ctx: &mut JitState, page: Page) {
  1888. let mut did_have_code = false;
  1889. if let Some(PageInfo {
  1890. wasm_table_index,
  1891. hidden_wasm_table_indices,
  1892. state_flags: _,
  1893. entry_points: _,
  1894. }) = ctx.pages.remove(&page)
  1895. {
  1896. profiler::stat_increment(stat::INVALIDATE_PAGE_HAD_CODE);
  1897. did_have_code = true;
  1898. free(ctx, wasm_table_index);
  1899. for wasm_table_index in hidden_wasm_table_indices {
  1900. free(ctx, wasm_table_index);
  1901. }
  1902. fn free(ctx: &mut JitState, wasm_table_index: WasmTableIndex) {
  1903. for i in 0..unsafe { cpu::valid_tlb_entries_count } {
  1904. let page = unsafe { cpu::valid_tlb_entries[i as usize] };
  1905. let entry = unsafe { cpu::tlb_data[page as usize] };
  1906. if 0 != entry {
  1907. let tlb_physical_page = Page::of_u32(
  1908. (entry as u32 >> 12 ^ page as u32) - (unsafe { memory::mem8 } as u32 >> 12),
  1909. );
  1910. match unsafe { cpu::tlb_code[page as usize] } {
  1911. None => {},
  1912. Some(c) => unsafe {
  1913. let w = c.as_ref().wasm_table_index;
  1914. if wasm_table_index == w {
  1915. drop(Box::from_raw(c.as_ptr()));
  1916. cpu::tlb_code[page as usize] = None;
  1917. if !ctx.entry_points.contains_key(&tlb_physical_page) {
  1918. cpu::tlb_data[page as usize] &= !cpu::TLB_HAS_CODE; // XXX
  1919. }
  1920. }
  1921. },
  1922. }
  1923. }
  1924. }
  1925. ctx.pages.retain(
  1926. |
  1927. _,
  1928. &mut PageInfo {
  1929. wasm_table_index: w,
  1930. ..
  1931. },
  1932. | w != wasm_table_index,
  1933. );
  1934. for info in ctx.pages.values_mut() {
  1935. info.hidden_wasm_table_indices
  1936. .retain(|&w| w != wasm_table_index)
  1937. }
  1938. free_wasm_table_index(ctx, wasm_table_index);
  1939. }
  1940. }
  1941. match ctx.entry_points.remove(&page) {
  1942. None => {},
  1943. Some(_) => {
  1944. profiler::stat_increment(stat::INVALIDATE_PAGE_HAD_ENTRY_POINTS);
  1945. did_have_code = true;
  1946. match &ctx.compiling {
  1947. Some((index, CompilingPageState::Compiling { pages })) => {
  1948. if pages.contains_key(&page) {
  1949. ctx.compiling = Some((*index, CompilingPageState::CompilingWritten));
  1950. }
  1951. },
  1952. _ => {},
  1953. }
  1954. },
  1955. }
  1956. match &ctx.compiling {
  1957. Some((_, CompilingPageState::Compiling { pages })) => {
  1958. dbg_assert!(!pages.contains_key(&page));
  1959. },
  1960. _ => {},
  1961. }
  1962. check_jit_state_invariants(ctx);
  1963. dbg_assert!(!jit_page_has_code_ctx(ctx, page));
  1964. if did_have_code {
  1965. cpu::tlb_set_has_code(page, false);
  1966. }
  1967. if !did_have_code {
  1968. profiler::stat_increment(stat::DIRTY_PAGE_DID_NOT_HAVE_CODE);
  1969. }
  1970. }
  1971. #[no_mangle]
  1972. pub fn jit_dirty_cache(start_addr: u32, end_addr: u32) {
  1973. dbg_assert!(start_addr < end_addr);
  1974. let start_page = Page::page_of(start_addr);
  1975. let end_page = Page::page_of(end_addr - 1);
  1976. for page in start_page.to_u32()..end_page.to_u32() + 1 {
  1977. jit_dirty_page(get_jit_state(), Page::page_of(page << 12));
  1978. }
  1979. }
  1980. /// dirty pages in the range of start_addr and end_addr, which must span at most two pages
  1981. pub fn jit_dirty_cache_small(start_addr: u32, end_addr: u32) {
  1982. dbg_assert!(start_addr < end_addr);
  1983. let start_page = Page::page_of(start_addr);
  1984. let end_page = Page::page_of(end_addr - 1);
  1985. let ctx = get_jit_state();
  1986. jit_dirty_page(ctx, start_page);
  1987. // Note: This can't happen when paging is enabled, as writes across
  1988. // boundaries are split up on two pages
  1989. if start_page != end_page {
  1990. dbg_assert!(start_page.to_u32() + 1 == end_page.to_u32());
  1991. jit_dirty_page(ctx, end_page);
  1992. }
  1993. }
  1994. #[no_mangle]
  1995. pub fn jit_clear_cache_js() { jit_clear_cache(get_jit_state()) }
  1996. pub fn jit_clear_cache(ctx: &mut JitState) {
  1997. let mut pages_with_code = HashSet::new();
  1998. for &p in ctx.entry_points.keys() {
  1999. pages_with_code.insert(p);
  2000. }
  2001. for &p in ctx.pages.keys() {
  2002. pages_with_code.insert(p);
  2003. }
  2004. for page in pages_with_code {
  2005. jit_dirty_page(ctx, page);
  2006. }
  2007. }
  2008. pub fn jit_page_has_code(page: Page) -> bool { jit_page_has_code_ctx(get_jit_state(), page) }
  2009. pub fn jit_page_has_code_ctx(ctx: &mut JitState, page: Page) -> bool {
  2010. ctx.pages.contains_key(&page) || ctx.entry_points.contains_key(&page)
  2011. }
  2012. #[no_mangle]
  2013. pub fn jit_get_wasm_table_index_free_list_count() -> u32 {
  2014. if cfg!(feature = "profiler") {
  2015. get_jit_state().wasm_table_index_free_list.len() as u32
  2016. }
  2017. else {
  2018. 0
  2019. }
  2020. }
  2021. #[no_mangle]
  2022. pub fn jit_get_cache_size() -> u32 {
  2023. if cfg!(feature = "profiler") {
  2024. get_jit_state()
  2025. .pages
  2026. .values()
  2027. .map(|p| p.entry_points.len() as u32)
  2028. .sum()
  2029. }
  2030. else {
  2031. 0
  2032. }
  2033. }
  2034. #[cfg(feature = "profiler")]
  2035. pub fn check_missed_entry_points(phys_address: u32, state_flags: CachedStateFlags) {
  2036. let ctx = get_jit_state();
  2037. if let Some(infos) = ctx.pages.get(&Page::page_of(phys_address)) {
  2038. if infos.state_flags != state_flags {
  2039. return;
  2040. }
  2041. let last_jump_type = unsafe { cpu::debug_last_jump.name() };
  2042. let last_jump_addr = unsafe { cpu::debug_last_jump.phys_address() }.unwrap_or(0);
  2043. let last_jump_opcode =
  2044. if last_jump_addr != 0 { memory::read32s(last_jump_addr) } else { 0 };
  2045. let opcode = memory::read32s(phys_address);
  2046. dbg_log!(
  2047. "Compiled exists, but no entry point, \
  2048. phys_addr={:x} opcode={:02x} {:02x} {:02x} {:02x}. \
  2049. Last jump at {:x} ({}) opcode={:02x} {:02x} {:02x} {:02x}",
  2050. phys_address,
  2051. opcode & 0xFF,
  2052. opcode >> 8 & 0xFF,
  2053. opcode >> 16 & 0xFF,
  2054. opcode >> 16 & 0xFF,
  2055. last_jump_addr,
  2056. last_jump_type,
  2057. last_jump_opcode & 0xFF,
  2058. last_jump_opcode >> 8 & 0xFF,
  2059. last_jump_opcode >> 16 & 0xFF,
  2060. last_jump_opcode >> 16 & 0xFF,
  2061. );
  2062. }
  2063. }
  2064. #[no_mangle]
  2065. #[cfg(feature = "profiler")]
  2066. pub fn debug_set_dispatcher_target(_target_index: i32) {
  2067. //dbg_log!("About to call dispatcher target_index={}", target_index);
  2068. }
  2069. #[no_mangle]
  2070. #[cfg(feature = "profiler")]
  2071. pub fn check_dispatcher_target(target_index: i32, max: i32) {
  2072. //dbg_log!("Dispatcher called target={}", target_index);
  2073. dbg_assert!(target_index >= 0);
  2074. dbg_assert!(target_index < max);
  2075. }
  2076. #[no_mangle]
  2077. #[cfg(feature = "profiler")]
  2078. pub fn enter_basic_block(phys_eip: u32) {
  2079. let eip =
  2080. unsafe { cpu::translate_address_read(*global_pointers::instruction_pointer).unwrap() };
  2081. if Page::page_of(eip) != Page::page_of(phys_eip) {
  2082. dbg_log!(
  2083. "enter basic block failed block=0x{:x} actual eip=0x{:x}",
  2084. phys_eip,
  2085. eip
  2086. );
  2087. panic!();
  2088. }
  2089. }
  2090. #[no_mangle]
  2091. #[cfg(feature = "profiler")]
  2092. pub fn get_config(index: u32) -> u32 {
  2093. match index {
  2094. 0 => MAX_PAGES as u32,
  2095. 1 => JIT_USE_LOOP_SAFETY as u32,
  2096. 2 => MAX_EXTRA_BASIC_BLOCKS as u32,
  2097. _ => 0,
  2098. }
  2099. }