use crate::wasmgen::wasm_builder::WasmBuilder; const SIZE: usize = if cfg!(feature = "profiler") { 8192 } else { 0 }; #[allow(non_upper_case_globals)] pub static mut opstats_buffer: [u64; SIZE] = [0; SIZE]; #[allow(non_upper_case_globals)] pub static mut opstats_compiled_buffer: [u64; SIZE] = [0; SIZE]; #[allow(non_upper_case_globals)] pub static mut opstats_jit_exit_buffer: [u64; SIZE] = [0; SIZE]; #[allow(non_upper_case_globals)] pub static mut opstats_unguarded_register_buffer: [u64; SIZE] = [0; SIZE]; #[allow(non_upper_case_globals)] pub static mut opstats_wasm_size: [u64; SIZE] = [0; SIZE]; pub struct Instruction { pub prefixes: Vec, pub opcode: u8, pub fixed_g: u8, pub is_mem: bool, pub is_0f: bool, } pub fn decode(mut instruction: u32) -> Instruction { let mut is_0f = false; let mut prefixes = vec![]; let mut final_opcode = 0; for _ in 0..4 { let opcode = (instruction & 0xFF) as u8; instruction >>= 8; // TODO: // - If the instruction uses 4 or more prefixes, only the prefixes will be counted if is_0f { final_opcode = opcode; break; } else { if opcode == 0x0F { is_0f = true; } else if opcode == 0x26 || opcode == 0x2E || opcode == 0x36 || opcode == 0x3E || opcode == 0x64 || opcode == 0x65 || opcode == 0x66 || opcode == 0x67 || opcode == 0xF0 || opcode == 0xF2 || opcode == 0xF3 { prefixes.push(opcode); } else { final_opcode = opcode; break; } } } let has_modrm_byte = if is_0f { match final_opcode { 0x0 | 0x1 | 0x2 | 0x3 | 0x10 | 0x11 | 0x12 | 0x13 | 0x14 | 0x15 | 0x16 | 0x17 | 0x18 | 0x19 | 0x20 | 0x21 | 0x22 | 0x23 | 0x28 | 0x29 | 0x40 | 0x41 | 0x42 | 0x43 | 0x44 | 0x45 | 0x46 | 0x47 | 0x48 | 0x49 | 0x50 | 0x51 | 0x52 | 0x53 | 0x54 | 0x55 | 0x56 | 0x57 | 0x58 | 0x59 | 0x60 | 0x61 | 0x62 | 0x63 | 0x64 | 0x65 | 0x66 | 0x67 | 0x68 | 0x69 | 0x70 | 0x71 | 0x72 | 0x73 | 0x74 | 0x75 | 0x76 | 0x90 | 0x91 | 0x92 | 0x93 | 0x94 | 0x95 | 0x96 | 0x97 | 0x98 | 0x99 | 0x1c | 0x1d | 0x1e | 0x1f | 0x2a | 0x2b | 0x2c | 0x2d | 0x2e | 0x2f | 0x4a | 0x4b | 0x4c | 0x4d | 0x4e | 0x4f | 0x5a | 0x5b | 0x5c | 0x5d | 0x5e | 0x5f | 0x6a | 0x6b | 0x6c | 0x6d | 0x6e | 0x6f | 0x7e | 0x7f | 0x9a | 0x9b | 0x9c | 0x9d | 0x9e | 0x9f | 0xa3 | 0xa4 | 0xa5 | 0xab | 0xac | 0xad | 0xae | 0xaf | 0xb0 | 0xb1 | 0xb2 | 0xb3 | 0xb4 | 0xb5 | 0xb6 | 0xb7 | 0xb8 | 0xba | 0xbb | 0xbc | 0xbd | 0xbe | 0xbf | 0xc0 | 0xc1 | 0xc2 | 0xc3 | 0xc4 | 0xc5 | 0xc6 | 0xc7 | 0xd1 | 0xd2 | 0xd3 | 0xd4 | 0xd5 | 0xd6 | 0xd7 | 0xd8 | 0xd9 | 0xda | 0xdb | 0xdc | 0xdd | 0xde | 0xdf | 0xe0 | 0xe1 | 0xe2 | 0xe3 | 0xe4 | 0xe5 | 0xe6 | 0xe7 | 0xe8 | 0xe9 | 0xea | 0xeb | 0xec | 0xed | 0xee | 0xef | 0xf1 | 0xf2 | 0xf3 | 0xf4 | 0xf5 | 0xf6 | 0xf7 | 0xf8 | 0xf9 | 0xfa | 0xfb | 0xfc | 0xfd | 0xfe => true, _ => false, } } else { match final_opcode { 0x0 | 0x1 | 0x2 | 0x3 | 0x8 | 0x9 | 0x10 | 0x11 | 0x12 | 0x13 | 0x18 | 0x19 | 0x20 | 0x21 | 0x22 | 0x23 | 0x28 | 0x29 | 0x30 | 0x31 | 0x32 | 0x33 | 0x38 | 0x39 | 0x62 | 0x63 | 0x69 | 0x80 | 0x81 | 0x82 | 0x83 | 0x84 | 0x85 | 0x86 | 0x87 | 0x88 | 0x89 | 0xa | 0xb | 0x1a | 0x1b | 0x2a | 0x2b | 0x3a | 0x3b | 0x6b | 0x8a | 0x8b | 0x8c | 0x8d | 0x8e | 0x8f | 0xc0 | 0xc1 | 0xc4 | 0xc5 | 0xc6 | 0xc7 | 0xd0 | 0xd1 | 0xd2 | 0xd3 | 0xd8 | 0xd9 | 0xda | 0xdb | 0xdc | 0xdd | 0xde | 0xdf | 0xf6 | 0xf7 | 0xfe | 0xff => true, _ => false, } }; let has_fixed_g = if is_0f { final_opcode == 0x71 || final_opcode == 0x72 || final_opcode == 0x73 || final_opcode == 0xAE || final_opcode == 0xBA || final_opcode == 0xC7 } else { final_opcode >= 0x80 && final_opcode < 0x84 || final_opcode >= 0xC0 && final_opcode < 0xC2 || final_opcode >= 0xD0 && final_opcode < 0xD4 || final_opcode >= 0xD8 && final_opcode < 0xE0 || final_opcode >= 0xF6 && final_opcode < 0xF8 || final_opcode == 0xFE || final_opcode == 0xFF }; let mut is_mem = false; let mut fixed_g = 0; if has_fixed_g { dbg_assert!(has_modrm_byte); let modrm_byte = (instruction & 0xFF) as u8; fixed_g = modrm_byte >> 3 & 7; is_mem = modrm_byte < 0xC0 } if has_modrm_byte { let modrm_byte = (instruction & 0xFF) as u8; is_mem = modrm_byte < 0xC0 } Instruction { prefixes, opcode: final_opcode, is_mem, fixed_g, is_0f, } } pub fn gen_opstats(builder: &mut WasmBuilder, opcode: u32) { if !cfg!(feature = "profiler") { return; } let instruction = decode(opcode); for prefix in instruction.prefixes { let index = (prefix as u32) << 4; builder.increment_fixed_i64( unsafe { &mut opstats_buffer[index as usize] as *mut _ } as u32, 1, ); } let index = (instruction.is_0f as u32) << 12 | (instruction.opcode as u32) << 4 | (instruction.is_mem as u32) << 3 | instruction.fixed_g as u32; builder.increment_fixed_i64( unsafe { &mut opstats_buffer[index as usize] as *mut _ } as u32, 1, ); } pub fn record_opstat_compiled(opcode: u32) { if !cfg!(feature = "profiler") { return; } let instruction = decode(opcode); for prefix in instruction.prefixes { let index = (prefix as u32) << 4; unsafe { opstats_compiled_buffer[index as usize] += 1 } } let index = (instruction.is_0f as u32) << 12 | (instruction.opcode as u32) << 4 | (instruction.is_mem as u32) << 3 | instruction.fixed_g as u32; unsafe { opstats_compiled_buffer[index as usize] += 1 } } pub fn record_opstat_jit_exit(opcode: u32) { if !cfg!(feature = "profiler") { return; } let instruction = decode(opcode); for prefix in instruction.prefixes { let index = (prefix as u32) << 4; unsafe { opstats_jit_exit_buffer[index as usize] += 1 } } let index = (instruction.is_0f as u32) << 12 | (instruction.opcode as u32) << 4 | (instruction.is_mem as u32) << 3 | instruction.fixed_g as u32; unsafe { opstats_jit_exit_buffer[index as usize] += 1 } } pub fn gen_opstat_unguarded_register(builder: &mut WasmBuilder, opcode: u32) { if !cfg!(feature = "profiler") { return; } let instruction = decode(opcode); for prefix in instruction.prefixes { let index = (prefix as u32) << 4; builder.increment_fixed_i64( unsafe { &mut opstats_unguarded_register_buffer[index as usize] as *mut _ } as u32, 1, ); } let index = (instruction.is_0f as u32) << 12 | (instruction.opcode as u32) << 4 | (instruction.is_mem as u32) << 3 | instruction.fixed_g as u32; builder.increment_fixed_i64( unsafe { &mut opstats_unguarded_register_buffer[index as usize] as *mut _ } as u32, 1, ); } pub fn record_opstat_size_wasm(opcode: u32, size: u64) { if !cfg!(feature = "profiler") { return; } let instruction = decode(opcode); for prefix in instruction.prefixes { let index = (prefix as u32) << 4; unsafe { opstats_wasm_size[index as usize] += size } } let index = (instruction.is_0f as u32) << 12 | (instruction.opcode as u32) << 4 | (instruction.is_mem as u32) << 3 | instruction.fixed_g as u32; unsafe { opstats_wasm_size[index as usize] += size } }