@@ -23,7 +23,7 @@ use crate::backend::lir::{self, Assembler, C_ARG_OPNDS, C_RET_OPND, CFP, EC, NAT
2323use crate :: hir:: { iseq_to_hir, BlockId , Invariant , RangeType , SideExitReason :: { self , * } , SpecialBackrefSymbol , SpecialObjectType } ;
2424use crate :: hir:: { Const , FrameState , Function , Insn , InsnId , SendFallbackReason } ;
2525use crate :: hir_type:: { types, Type } ;
26- use crate :: options:: get_option;
26+ use crate :: options:: { get_option, PerfMap } ;
2727use crate :: cast:: IntoUsize ;
2828
2929/// At the moment, we support recompiling each ISEQ only once.
@@ -211,16 +211,16 @@ pub fn gen_iseq_call(cb: &mut CodeBlock, iseq_call: &IseqCallRef) -> Result<(),
211211}
212212
213213/// Write an entry to the perf map in /tmp
214- fn register_with_perf ( iseq_name : String , start_ptr : usize , code_size : usize ) {
214+ fn register_with_perf ( symbol_name : String , start_ptr : usize , code_size : usize ) {
215215 use std:: io:: Write ;
216216 let perf_map = format ! ( "/tmp/perf-{}.map" , std:: process:: id( ) ) ;
217217 let Ok ( file) = std:: fs:: OpenOptions :: new ( ) . create ( true ) . append ( true ) . open ( & perf_map) else {
218218 debug ! ( "Failed to open perf map file: {perf_map}" ) ;
219219 return ;
220220 } ;
221221 let mut file = std:: io:: BufWriter :: new ( file) ;
222- let Ok ( _) = writeln ! ( file, "{start_ptr:#x} {code_size:#x} zjit::{iseq_name }" ) else {
223- debug ! ( "Failed to write {iseq_name } to perf map file: {perf_map}" ) ;
222+ let Ok ( _) = writeln ! ( file, "{start_ptr:#x} {code_size:#x} ZJIT: {symbol_name }" ) else {
223+ debug ! ( "Failed to write {symbol_name } to perf map file: {perf_map}" ) ;
224224 return ;
225225 } ;
226226}
@@ -244,11 +244,11 @@ pub fn gen_entry_trampoline(cb: &mut CodeBlock) -> Result<CodePtr, CompileError>
244244
245245 let ( code_ptr, gc_offsets) = asm. compile ( cb) ?;
246246 assert ! ( gc_offsets. is_empty( ) ) ;
247- if get_option ! ( perf) {
247+ if get_option ! ( perf) . is_some ( ) {
248248 let start_ptr = code_ptr. raw_addr ( cb) ;
249249 let end_ptr = cb. get_write_ptr ( ) . raw_addr ( cb) ;
250250 let code_size = end_ptr - start_ptr;
251- register_with_perf ( "ZJIT entry trampoline" . into ( ) , start_ptr, code_size) ;
251+ register_with_perf ( "entry trampoline" . into ( ) , start_ptr, code_size) ;
252252 }
253253 Ok ( code_ptr)
254254}
@@ -448,7 +448,23 @@ fn gen_function(cb: &mut CodeBlock, iseq: IseqPtr, version: IseqVersionRef, func
448448 assert ! ( insn_idx == block. insns( ) . len( ) - 1 , "Jump must be the last instruction in HIR block" ) ;
449449 } ,
450450 _ => {
451- if let Err ( last_snapshot) = gen_insn ( cb, & mut jit, & mut asm, function, insn_id, & insn) {
451+ // Start a new perf range for the HIR instruction. For now, we do this only for
452+ // non-terminator instructions because LIR blocks must end with a terminator instruction.
453+ let perf_symbol = if get_option ! ( perf) == Some ( PerfMap :: HIR ) && !insn. is_terminator ( ) {
454+ let insn_name = format ! ( "{insn}" ) . split_whitespace ( ) . next ( ) . unwrap ( ) . to_string ( ) ;
455+ Some ( perf_symbol_range_start ( & mut asm, & insn_name) )
456+ } else {
457+ None
458+ } ;
459+
460+ let result = gen_insn ( cb, & mut jit, & mut asm, function, insn_id, & insn) ;
461+
462+ // Close the current perf range for the HIR instruction.
463+ if let Some ( perf_symbol) = & perf_symbol {
464+ perf_symbol_range_end ( & mut asm, perf_symbol) ;
465+ }
466+
467+ if let Err ( last_snapshot) = result {
452468 debug ! ( "ZJIT: gen_function: Failed to compile insn: {insn_id} {insn}. Generating side-exit." ) ;
453469 gen_incr_counter ( & mut asm, exit_counter_for_unhandled_hir_insn ( & insn) ) ;
454470 gen_side_exit ( & mut jit, & mut asm, & SideExitReason :: UnhandledHIRInsn ( insn_id) , & function. frame_state ( last_snapshot) ) ;
@@ -472,7 +488,7 @@ fn gen_function(cb: &mut CodeBlock, iseq: IseqPtr, version: IseqVersionRef, func
472488 // Generate code if everything can be compiled
473489 let result = asm. compile ( cb) ;
474490 if let Ok ( ( start_ptr, _) ) = result {
475- if get_option ! ( perf) {
491+ if get_option ! ( perf) == Some ( PerfMap :: ISEQ ) {
476492 let start_usize = start_ptr. raw_addr ( cb) ;
477493 let end_usize = cb. get_write_ptr ( ) . raw_addr ( cb) ;
478494 let code_size = end_usize - start_usize;
@@ -3248,6 +3264,34 @@ impl IseqCall {
32483264 }
32493265}
32503266
3267+ type PerfSymbol = Rc < RefCell < Option < ( CodePtr , String ) > > > ;
3268+
3269+ /// Mark the start of a perf symbol range via pos_marker.
3270+ /// Returns a handle to pass to perf_symbol_range_end.
3271+ pub fn perf_symbol_range_start ( asm : & mut Assembler , symbol_name : & str ) -> PerfSymbol {
3272+ let symbol_name = symbol_name. to_string ( ) ;
3273+ let perf_symbol: PerfSymbol = Rc :: new ( RefCell :: new ( None ) ) ;
3274+ let current = perf_symbol. clone ( ) ;
3275+ asm. pos_marker ( move |start, _| {
3276+ let mut current = current. borrow_mut ( ) ;
3277+ assert ! ( current. is_none( ) , "perf symbol range already open" ) ;
3278+ * current = Some ( ( start, symbol_name. clone ( ) ) ) ;
3279+ } ) ;
3280+ perf_symbol
3281+ }
3282+
3283+ /// Mark the end of a perf symbol range via pos_marker.
3284+ pub fn perf_symbol_range_end ( asm : & mut Assembler , perf_symbol : & PerfSymbol ) {
3285+ let current = perf_symbol. clone ( ) ;
3286+ asm. pos_marker ( move |end, cb| {
3287+ if let Some ( ( start, name) ) = current. borrow_mut ( ) . take ( ) {
3288+ let start_addr = start. raw_addr ( cb) ;
3289+ let code_size = end. raw_addr ( cb) - start_addr;
3290+ register_with_perf ( name, start_addr, code_size) ;
3291+ }
3292+ } ) ;
3293+ }
3294+
32513295#[ cfg( test) ]
32523296#[ path = "codegen_tests.rs" ]
32533297mod tests;
0 commit comments