ruby-changes:73325
From: Takashi <ko1@a...>
Date: Tue, 30 Aug 2022 01:10:23 +0900 (JST)
Subject: [ruby-changes:73325] def3ade8a8 (master): Add --yjit-dump-disasm to dump every compiled code (https://github.com/Shopify/ruby/pull/430)
https://git.ruby-lang.org/ruby.git/commit/?id=def3ade8a8 From def3ade8a809a230648cdffbf4ab066b07fe7bf1 Mon Sep 17 00:00:00 2001 From: Takashi Kokubun <takashikkbn@g...> Date: Tue, 23 Aug 2022 13:46:43 -0700 Subject: Add --yjit-dump-disasm to dump every compiled code (https://github.com/Shopify/ruby/pull/430) * Add --yjit-dump-disasm to dump every compiled code * Just use get_option * Carve out disasm_from_addr * Avoid push_str with format! * Share the logic through asm.compile * This seems to negatively impact the compilation speed --- yjit/src/asm/mod.rs | 10 ++++-- yjit/src/backend/ir.rs | 19 ++++++++++-- yjit/src/codegen.rs | 15 +++++++-- yjit/src/core.rs | 1 + yjit/src/disasm.rs | 82 +++++++++++++++++++++++++++++--------------------- yjit/src/options.rs | 5 +++ 6 files changed, 91 insertions(+), 41 deletions(-) diff --git a/yjit/src/asm/mod.rs b/yjit/src/asm/mod.rs index fef4518816..4029e2ca67 100644 --- a/yjit/src/asm/mod.rs +++ b/yjit/src/asm/mod.rs @@ -57,6 +57,10 @@ pub struct CodeBlock { https://github.com/ruby/ruby/blob/trunk/yjit/src/asm/mod.rs#L57 #[cfg(feature = "asm_comments")] asm_comments: BTreeMap<usize, Vec<String>>, + // True for OutlinedCb + #[cfg(feature = "disasm")] + pub outlined: bool, + // Set if the CodeBlock is unable to output some instructions, // for example, when there is not enough space or when a jump // target is too far away. @@ -65,7 +69,7 @@ pub struct CodeBlock { https://github.com/ruby/ruby/blob/trunk/yjit/src/asm/mod.rs#L69 impl CodeBlock { /// Make a new CodeBlock - pub fn new(mem_block: VirtualMem) -> Self { + pub fn new(mem_block: VirtualMem, outlined: bool) -> Self { Self { mem_size: mem_block.virtual_region_size(), mem_block, @@ -75,6 +79,8 @@ impl CodeBlock { https://github.com/ruby/ruby/blob/trunk/yjit/src/asm/mod.rs#L79 label_refs: Vec::new(), #[cfg(feature = "asm_comments")] asm_comments: BTreeMap::new(), + #[cfg(feature = "disasm")] + outlined, dropped_bytes: false, } } @@ -282,7 +288,7 @@ impl CodeBlock { https://github.com/ruby/ruby/blob/trunk/yjit/src/asm/mod.rs#L288 let mem_start: *const u8 = alloc.mem_start(); let virt_mem = VirtualMem::new(alloc, 1, mem_start as *mut u8, mem_size); - Self::new(virt_mem) + Self::new(virt_mem, false) } } diff --git a/yjit/src/backend/ir.rs b/yjit/src/backend/ir.rs index f01ab398da..33a79a4179 100644 --- a/yjit/src/backend/ir.rs +++ b/yjit/src/backend/ir.rs @@ -10,6 +10,7 @@ use crate::cruby::{VALUE}; https://github.com/ruby/ruby/blob/trunk/yjit/src/backend/ir.rs#L10 use crate::virtualmem::{CodePtr}; use crate::asm::{CodeBlock, uimm_num_bits, imm_num_bits}; use crate::core::{Context, Type, TempMapping}; +use crate::options::*; #[cfg(target_arch = "x86_64")] use crate::backend::x86_64::*; @@ -1075,11 +1076,25 @@ impl Assembler https://github.com/ruby/ruby/blob/trunk/yjit/src/backend/ir.rs#L1076 /// compiling multiple blocks at a time? pub fn compile(self, cb: &mut CodeBlock) -> Vec<u32> { + #[cfg(feature = "disasm")] + let start_addr = cb.get_write_ptr().raw_ptr(); + let alloc_regs = Self::get_alloc_regs(); - self.compile_with_regs(cb, alloc_regs) + let gc_offsets = self.compile_with_regs(cb, alloc_regs); + + #[cfg(feature = "disasm")] + if get_option!(dump_disasm) && !cb.outlined { + use crate::disasm::disasm_addr_range; + let last_ptr = cb.get_write_ptr(); + let disasm = disasm_addr_range(cb, start_addr, last_ptr.raw_ptr() as usize - start_addr as usize); + if disasm.len() > 0 { + println!("{disasm}"); + } + } + gc_offsets } - /// Compile with a limited number of registers + /// Compile with a limited number of registers. Used only for unit tests. pub fn compile_with_num_regs(self, cb: &mut CodeBlock, num_regs: usize) -> Vec<u32> { let mut alloc_regs = Self::get_alloc_regs(); diff --git a/yjit/src/codegen.rs b/yjit/src/codegen.rs index 1336fe3c57..7c4c974345 100644 --- a/yjit/src/codegen.rs +++ b/yjit/src/codegen.rs @@ -622,9 +622,13 @@ pub fn gen_entry_prologue(cb: &mut CodeBlock, iseq: IseqPtr, insn_idx: u32) -> O https://github.com/ruby/ruby/blob/trunk/yjit/src/codegen.rs#L622 cb.align_pos(64); let code_ptr = cb.get_write_ptr(); - add_comment(cb, "yjit entry"); let mut asm = Assembler::new(); + if get_option!(dump_disasm) { + asm.comment(&format!("YJIT entry: {}", iseq_get_location(iseq))); + } else { + asm.comment("YJIT entry"); + } asm.frame_setup(); @@ -748,6 +752,11 @@ pub fn gen_single_block( https://github.com/ruby/ruby/blob/trunk/yjit/src/codegen.rs#L752 // Create a backend assembler instance let mut asm = Assembler::new(); + #[cfg(feature = "disasm")] + if get_option!(dump_disasm) { + asm.comment(&format!("Block: {} (ISEQ offset: {})", iseq_get_location(blockid.iseq), blockid.idx)); + } + // For each instruction to compile // NOTE: could rewrite this loop with a std::iter::Iterator while insn_idx < iseq_size { @@ -6049,8 +6058,8 @@ impl CodegenGlobals { https://github.com/ruby/ruby/blob/trunk/yjit/src/codegen.rs#L6058 half_size ); - let cb = CodeBlock::new(first_half); - let ocb = OutlinedCb::wrap(CodeBlock::new(second_half)); + let cb = CodeBlock::new(first_half, false); + let ocb = OutlinedCb::wrap(CodeBlock::new(second_half, true)); (cb, ocb) }; diff --git a/yjit/src/core.rs b/yjit/src/core.rs index 7d07918228..fa82dcc308 100644 --- a/yjit/src/core.rs +++ b/yjit/src/core.rs @@ -1510,6 +1510,7 @@ fn regenerate_branch(cb: &mut CodeBlock, branch: &mut Branch) { https://github.com/ruby/ruby/blob/trunk/yjit/src/core.rs#L1510 cb.set_write_ptr(branch.start_addr.unwrap()); let mut asm = Assembler::new(); + asm.comment("regenerate_branch"); (branch.gen_fn)( &mut asm, diff --git a/yjit/src/disasm.rs b/yjit/src/disasm.rs index 015c0c25ef..3d1c5b33fd 100644 --- a/yjit/src/disasm.rs +++ b/yjit/src/disasm.rs @@ -1,6 +1,9 @@ https://github.com/ruby/ruby/blob/trunk/yjit/src/disasm.rs#L1 use crate::core::*; use crate::cruby::*; use crate::yjit::yjit_enabled_p; +use crate::asm::CodeBlock; +use crate::codegen::CodePtr; +use std::fmt::Write; /// Primitive called in yjit.rb /// Produce a string representing the disassembly for an ISEQ @@ -36,7 +39,7 @@ pub extern "C" fn rb_yjit_disasm_iseq(_ec: EcPtr, _ruby_self: VALUE, iseqw: VALU https://github.com/ruby/ruby/blob/trunk/yjit/src/disasm.rs#L39 #[cfg(feature = "disasm")] pub fn disasm_iseq_insn_range(iseq: IseqPtr, start_idx: u32, end_idx: u32) -> String { - let mut out = String::from(""); + let mut out = String::from(""); // Get a list of block versions generated for this iseq let mut block_list = get_iseq_block_list(iseq); @@ -67,26 +70,6 @@ pub fn disasm_iseq_insn_range(iseq: IseqPtr, start_idx: u32, end_idx: u32) -> St https://github.com/ruby/ruby/blob/trunk/yjit/src/disasm.rs#L70 total_code_size += blockref.borrow().code_size(); } - // Initialize capstone - use capstone::prelude::*; - - #[cfg(target_arch = "x86_64")] - let mut cs = Capstone::new() - .x86() - .mode(arch::x86::ArchMode::Mode64) - .syntax(arch::x86::ArchSyntax::Intel) - .build() - .unwrap(); - - #[cfg(target_arch = "aarch64")] - let mut cs = Capstone::new() - .arm64() - .mode(arch::arm64::ArchMode::Arm) - .detail(true) - .build() - .unwrap(); - cs.set_skipdata(true); - out.push_str(&format!("NUM BLOCK VERSIONS: {}\n", block_list.len())); out.push_str(&format!( "TOTAL INLINE CODE SIZE: {} bytes\n", @@ -115,19 +98,7 @@ pub fn disasm_iseq_insn_range(iseq: IseqPtr, start_idx: u32, end_idx: u32) -> St https://github.com/ruby/ruby/blob/trunk/yjit/src/disasm.rs#L98 out.push_str(&format!("== {:=<60}\n", block_ident)); // Disassemble the instructions - let code_slice = unsafe { std::slice::from_raw_parts(start_addr, code_size) }; - let insns = cs.disasm_all(code_slice, start_addr as u64).unwrap(); - - // For each instruction in this block - for insn in insns.as_ref() { - // Comments for this block - if let Some(comment_list) = global_cb.comments_at(insn.address() as usize) { - for comment in comment_list { - out.push_str(&format!(" \x1b[1m# {}\x1b[0m\n", comment)); - } - } - out.push_str(&format!(" {}\n", insn)); - } + out.push_str(&disasm_addr_range(global_cb, start_addr, code_size)); // If this is not the last block if block_idx < block_list.len() - 1 { @@ -147,6 +118,49 @@ pub fn disasm_iseq_insn_range(iseq: IseqPtr, start_idx: u32, end_idx: u32) -> St https://github.com/ruby/ruby/blob/trunk/yjit/src/disasm.rs#L118 return out; } + +#[cfg(feature = "disasm")] +pub fn disasm_addr_range(cb: &CodeBlock, start_addr: *const u8, code_size: usize) -> String { + let mut out = String::from(""); + + // Initialize capstone + use capstone::prelude::*; + + #[cfg(target_arch = "x86_64")] + let mut cs = Capstone::new() + .x86() + .mode(arch::x86::ArchMode::Mode64) + .syntax(arch::x86::ArchSyntax::Intel) + .build() + .unwrap(); + + #[cfg(target_arch = "aarch64")] + let mut cs = Capstone::new() + .arm64() + .mode(arch::arm64::ArchMode::Arm) + .detail(true) + .build() + .unwrap(); + cs.set_skipdata(true); + + // Disassemble the (... truncated) -- ML: ruby-changes@q... Info: http://www.atdot.net/~ko1/quickml/