Track last executed jump instruction, check for missed entry points while looking for compiled code
This commit is contained in:
parent
7e3f1ad401
commit
7e1d398e05
|
@ -25,7 +25,8 @@ const print_stats = {
|
|||
"RUN_INTERPRETED_PENDING",
|
||||
"RUN_INTERPRETED_NEAR_END_OF_PAGE",
|
||||
"RUN_INTERPRETED_DIFFERENT_STATE",
|
||||
"RUN_INTERPRETED_MISSED_COMPILED_ENTRY",
|
||||
"RUN_INTERPRETED_MISSED_COMPILED_ENTRY_RUN_INTERPRETED",
|
||||
"RUN_INTERPRETED_MISSED_COMPILED_ENTRY_LOOKUP",
|
||||
"RUN_INTERPRETED_STEPS",
|
||||
"RUN_FROM_CACHE",
|
||||
"RUN_FROM_CACHE_STEPS",
|
||||
|
|
|
@ -351,6 +351,7 @@ fn gen_safe_read(ctx: &mut JitContext, bits: BitSize) {
|
|||
.load_u8(global_pointers::PAGE_FAULT);
|
||||
|
||||
builder.instruction_body.if_void();
|
||||
gen_debug_track_jit_exit(builder, ctx.start_of_current_instruction);
|
||||
builder.instruction_body.return_();
|
||||
builder.instruction_body.block_end();
|
||||
|
||||
|
@ -494,6 +495,7 @@ fn gen_safe_write(
|
|||
.load_u8(global_pointers::PAGE_FAULT);
|
||||
|
||||
builder.instruction_body.if_void();
|
||||
gen_debug_track_jit_exit(builder, ctx.start_of_current_instruction);
|
||||
builder.instruction_body.return_();
|
||||
builder.instruction_body.block_end();
|
||||
|
||||
|
@ -698,6 +700,7 @@ pub fn gen_task_switch_test(ctx: &mut JitContext) {
|
|||
|
||||
gen_fn0_const(ctx.builder, "task_switch_test_void");
|
||||
|
||||
gen_debug_track_jit_exit(ctx.builder, ctx.start_of_current_instruction);
|
||||
ctx.builder.instruction_body.return_();
|
||||
|
||||
ctx.builder.instruction_body.block_end();
|
||||
|
@ -717,6 +720,7 @@ pub fn gen_task_switch_test_mmx(ctx: &mut JitContext) {
|
|||
|
||||
gen_fn0_const(ctx.builder, "task_switch_test_mmx_void");
|
||||
|
||||
gen_debug_track_jit_exit(ctx.builder, ctx.start_of_current_instruction);
|
||||
ctx.builder.instruction_body.return_();
|
||||
|
||||
ctx.builder.instruction_body.block_end();
|
||||
|
@ -949,6 +953,7 @@ pub fn gen_safe_read_write(
|
|||
.load_u8(global_pointers::PAGE_FAULT);
|
||||
|
||||
ctx.builder.instruction_body.if_void();
|
||||
gen_debug_track_jit_exit(ctx.builder, ctx.start_of_current_instruction);
|
||||
ctx.builder.instruction_body.return_();
|
||||
ctx.builder.instruction_body.block_end();
|
||||
|
||||
|
@ -961,3 +966,9 @@ pub fn gen_profiler_stat_increment(builder: &mut WasmBuilder, stat: profiler::st
|
|||
let addr = unsafe { profiler::stat_array.as_mut_ptr().offset(stat as isize) } as u32;
|
||||
gen_increment_variable(builder, addr, 1)
|
||||
}
|
||||
|
||||
pub fn gen_debug_track_jit_exit(builder: &mut WasmBuilder, address: u32) {
|
||||
if cfg!(feature = "profiler") {
|
||||
gen_fn1_const(builder, "track_jit_exit", address);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -67,6 +67,8 @@ pub union reg128 {
|
|||
pub f64_0: [f64; 2],
|
||||
}
|
||||
|
||||
pub const CHECK_MISSED_ENTRY_POINTS: bool = false;
|
||||
|
||||
pub const FLAG_CARRY: i32 = 1;
|
||||
pub const FLAG_PARITY: i32 = 4;
|
||||
pub const FLAG_ADJUST: i32 = 16;
|
||||
|
@ -248,6 +250,41 @@ pub static mut tsc_offset: u64 = 0;
|
|||
pub static mut valid_tlb_entries: [i32; 10000] = [0; 10000];
|
||||
pub static mut valid_tlb_entries_count: i32 = 0;
|
||||
|
||||
pub enum LastJump {
|
||||
Interrupt {
|
||||
phys_addr: u32,
|
||||
int: u8,
|
||||
software: bool,
|
||||
error: Option<u32>,
|
||||
},
|
||||
Compiled {
|
||||
phys_addr: u32,
|
||||
},
|
||||
Interpreted {
|
||||
phys_addr: u32,
|
||||
},
|
||||
None,
|
||||
}
|
||||
impl LastJump {
|
||||
pub fn phys_address(&self) -> Option<u32> {
|
||||
match self {
|
||||
LastJump::Interrupt { phys_addr, .. } => Some(*phys_addr),
|
||||
LastJump::Compiled { phys_addr } => Some(*phys_addr),
|
||||
LastJump::Interpreted { phys_addr } => Some(*phys_addr),
|
||||
LastJump::None => None,
|
||||
}
|
||||
}
|
||||
pub fn name(&self) -> &'static str {
|
||||
match self {
|
||||
LastJump::Interrupt { .. } => "interrupt",
|
||||
LastJump::Compiled { .. } => "compiled",
|
||||
LastJump::Interpreted { .. } => "interpreted",
|
||||
LastJump::None => "none",
|
||||
}
|
||||
}
|
||||
}
|
||||
pub static mut debug_last_jump: LastJump = LastJump::None;
|
||||
|
||||
pub struct SegmentSelector {
|
||||
raw: u16,
|
||||
}
|
||||
|
@ -304,6 +341,13 @@ impl SegmentDescriptor {
|
|||
|
||||
pub unsafe fn after_block_boundary() { jit_block_boundary = true; }
|
||||
|
||||
#[no_mangle]
|
||||
pub fn track_jit_exit(phys_addr: u32) {
|
||||
unsafe {
|
||||
debug_last_jump = LastJump::Compiled { phys_addr };
|
||||
}
|
||||
}
|
||||
|
||||
pub unsafe fn same_page(addr1: i32, addr2: i32) -> bool { return addr1 & !0xFFF == addr2 & !0xFFF; }
|
||||
|
||||
#[no_mangle]
|
||||
|
@ -1044,6 +1088,13 @@ pub unsafe fn cycle_internal() {
|
|||
);
|
||||
}
|
||||
else {
|
||||
#[cfg(feature = "profiler")]
|
||||
{
|
||||
if CHECK_MISSED_ENTRY_POINTS {
|
||||
::jit::check_missed_entry_points(phys_addr, state_flags as u32);
|
||||
}
|
||||
}
|
||||
|
||||
if DEBUG {
|
||||
dbg_assert!(!must_not_fault);
|
||||
must_not_fault = true
|
||||
|
@ -1090,6 +1141,12 @@ unsafe fn jit_run_interpreted(phys_addr: i32) {
|
|||
profiler::stat_increment(S_RUN_INTERPRETED);
|
||||
dbg_assert!(!in_mapped_range(phys_addr as u32));
|
||||
|
||||
if cfg!(debug_assertions) {
|
||||
debug_last_jump = LastJump::Interpreted {
|
||||
phys_addr: phys_addr as u32,
|
||||
};
|
||||
}
|
||||
|
||||
jit_block_boundary = false;
|
||||
let opcode = *mem8.offset(phys_addr as isize) as i32;
|
||||
*instruction_pointer += 1;
|
||||
|
@ -1102,13 +1159,13 @@ unsafe fn jit_run_interpreted(phys_addr: i32) {
|
|||
*previous_ip = *instruction_pointer;
|
||||
let opcode = return_on_pagefault!(read_imm8());
|
||||
|
||||
if DEBUG {
|
||||
if CHECK_MISSED_ENTRY_POINTS {
|
||||
let phys_addr = return_on_pagefault!(get_phys_eip()) as u32;
|
||||
let state_flags: CachedStateFlags = pack_current_state_flags();
|
||||
let entry = ::c_api::jit_find_cache_entry(phys_addr, state_flags as u32);
|
||||
|
||||
if entry != 0 {
|
||||
profiler::stat_increment(S_RUN_INTERPRETED_MISSED_COMPILED_ENTRY);
|
||||
profiler::stat_increment(S_RUN_INTERPRETED_MISSED_COMPILED_ENTRY_RUN_INTERPRETED);
|
||||
//dbg_log!(
|
||||
// "missed entry point at {:x} prev_opcode={:x} opcode={:x}",
|
||||
// phys_addr,
|
||||
|
@ -1118,6 +1175,12 @@ unsafe fn jit_run_interpreted(phys_addr: i32) {
|
|||
}
|
||||
}
|
||||
|
||||
if cfg!(debug_assertions) {
|
||||
debug_last_jump = LastJump::Interpreted {
|
||||
phys_addr: phys_addr as u32,
|
||||
};
|
||||
}
|
||||
|
||||
*timestamp_counter += 1;
|
||||
|
||||
//if DEBUG {
|
||||
|
|
|
@ -49,7 +49,7 @@ mod jit_cache_array {
|
|||
pub struct Entry {
|
||||
pub start_addr: u32,
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
#[cfg(any(debug_assertions, feature = "profiler"))]
|
||||
pub len: u32,
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
|
@ -82,7 +82,7 @@ mod jit_cache_array {
|
|||
state_flags,
|
||||
pending,
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
#[cfg(any(debug_assertions, feature = "profiler"))]
|
||||
len: 0,
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
|
@ -116,7 +116,7 @@ mod jit_cache_array {
|
|||
state_flags: CachedStateFlags::EMPTY,
|
||||
pending: false,
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
#[cfg(any(debug_assertions, feature = "profiler"))]
|
||||
len: 0,
|
||||
#[cfg(debug_assertions)]
|
||||
opcode: 0,
|
||||
|
@ -131,7 +131,7 @@ mod jit_cache_array {
|
|||
state_flags: CachedStateFlags::EMPTY,
|
||||
pending: false,
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
#[cfg(any(debug_assertions, feature = "profiler"))]
|
||||
len: 0,
|
||||
#[cfg(debug_assertions)]
|
||||
opcode: 0,
|
||||
|
@ -817,9 +817,13 @@ fn jit_analyze_and_generate(
|
|||
true,
|
||||
);
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
#[cfg(any(debug_assertions, feature = "profiler"))]
|
||||
{
|
||||
entry.len = block.end_addr - block.addr;
|
||||
}
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
{
|
||||
entry.opcode = cpu::read32(block.addr);
|
||||
}
|
||||
|
||||
|
@ -963,6 +967,7 @@ fn jit_generate_module(
|
|||
.get_local(gen_local_iteration_counter);
|
||||
builder.instruction_body.eqz_i32();
|
||||
builder.instruction_body.if_void();
|
||||
codegen::gen_debug_track_jit_exit(builder, 0);
|
||||
builder.instruction_body.return_();
|
||||
builder.instruction_body.block_end();
|
||||
}
|
||||
|
@ -1001,6 +1006,7 @@ fn jit_generate_module(
|
|||
match &block.ty {
|
||||
BasicBlockType::Exit => {
|
||||
// Exit this function
|
||||
codegen::gen_debug_track_jit_exit(builder, block.last_instruction_addr);
|
||||
builder.instruction_body.return_();
|
||||
},
|
||||
BasicBlockType::Normal { next_block_addr } => {
|
||||
|
@ -1058,6 +1064,7 @@ fn jit_generate_module(
|
|||
}
|
||||
else {
|
||||
// Jump to different page
|
||||
codegen::gen_debug_track_jit_exit(builder, block.last_instruction_addr);
|
||||
builder.instruction_body.return_();
|
||||
}
|
||||
|
||||
|
@ -1077,6 +1084,7 @@ fn jit_generate_module(
|
|||
}
|
||||
else {
|
||||
// End of this page
|
||||
codegen::gen_debug_track_jit_exit(builder, block.last_instruction_addr);
|
||||
builder.instruction_body.return_();
|
||||
}
|
||||
|
||||
|
@ -1377,3 +1385,53 @@ pub fn jit_get_wasm_table_index_free_list_count(ctx: &JitState) -> u32 {
|
|||
|
||||
pub fn jit_get_op_len(ctx: &JitState) -> u32 { ctx.wasm_builder.get_op_len() }
|
||||
pub fn jit_get_op_ptr(ctx: &JitState) -> *const u8 { ctx.wasm_builder.get_op_ptr() }
|
||||
|
||||
#[cfg(feature = "profiler")]
|
||||
pub fn check_missed_entry_points(phys_address: u32, state_flags: u32) {
|
||||
let state_flags = CachedStateFlags::of_u32(state_flags);
|
||||
let page = Page::page_of(phys_address);
|
||||
|
||||
for i in page.to_address()..page.to_address() + 4096 {
|
||||
// No need to check [CODE_CACHE_SEARCH_SIZE] entries here as we look at consecutive
|
||||
// addresses anyway
|
||||
let index = i & jit_cache_array::MASK;
|
||||
let entry = jit_cache_array::get_unchecked(index);
|
||||
|
||||
if !entry.pending
|
||||
&& entry.state_flags == state_flags
|
||||
&& phys_address >= entry.start_addr
|
||||
&& phys_address < entry.start_addr + entry.len
|
||||
{
|
||||
profiler::stat_increment(stat::S_RUN_INTERPRETED_MISSED_COMPILED_ENTRY_LOOKUP);
|
||||
|
||||
let last_jump_type = unsafe { cpu2::cpu::debug_last_jump.name() };
|
||||
let last_jump_addr = unsafe { cpu2::cpu::debug_last_jump.phys_address() }.unwrap_or(0);
|
||||
let last_jump_opcode = if last_jump_addr != 0 {
|
||||
cpu::read32(last_jump_addr)
|
||||
}
|
||||
else {
|
||||
0
|
||||
};
|
||||
|
||||
let opcode = cpu::read32(phys_address);
|
||||
dbg_log!(
|
||||
"Compiled exists, but no entry point, \
|
||||
start={:x} end={:x} phys_addr={:x} opcode={:02x} {:02x} {:02x} {:02x}. \
|
||||
Last jump at {:x} ({}) opcode={:02x} {:02x} {:02x} {:02x}",
|
||||
entry.start_addr,
|
||||
entry.start_addr + entry.len,
|
||||
phys_address,
|
||||
opcode & 0xFF,
|
||||
opcode >> 8 & 0xFF,
|
||||
opcode >> 16 & 0xFF,
|
||||
opcode >> 16 & 0xFF,
|
||||
last_jump_addr,
|
||||
last_jump_type,
|
||||
last_jump_opcode & 0xFF,
|
||||
last_jump_opcode >> 8 & 0xFF,
|
||||
last_jump_opcode >> 16 & 0xFF,
|
||||
last_jump_opcode >> 16 & 0xFF,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,7 +12,8 @@ pub enum stat {
|
|||
S_RUN_INTERPRETED_PENDING,
|
||||
S_RUN_INTERPRETED_NEAR_END_OF_PAGE,
|
||||
S_RUN_INTERPRETED_DIFFERENT_STATE,
|
||||
S_RUN_INTERPRETED_MISSED_COMPILED_ENTRY,
|
||||
S_RUN_INTERPRETED_MISSED_COMPILED_ENTRY_RUN_INTERPRETED,
|
||||
S_RUN_INTERPRETED_MISSED_COMPILED_ENTRY_LOOKUP,
|
||||
S_RUN_INTERPRETED_STEPS,
|
||||
|
||||
S_RUN_FROM_CACHE,
|
||||
|
|
Loading…
Reference in a new issue