Skip to content

Commit c943e23

Browse files
committed
YJIT: dump-disasm: Print comments and bytes in release builds
This change implements a fallback mode for the `--yjit-dump-disasm` development command-line option to make it usable in release builds. Previously, using the option with release builds of YJIT yielded only a warning asking the user to build with `--enable-yjit=dev`. While builds that use the `disasm` feature still give the best output, just having the comments is useful enough for many kinds of debugging. Having it usable in release builds is nice for new hackers, too, since this allows for tinkering without having to learn how to build YJIT in development mode. Sample output on A64: ``` # regenerate_branch # Insn: 0001 opt_send_without_block (stack_size: 1) # guard known object with singleton class 0x11f7e0034: 4b 00 00 58 03 00 00 14 08 ce 9c 04 01 00 00 0x11f7e0043: 00 3f 00 0b eb 81 06 01 54 1f 20 03 d5 # RUBY_VM_CHECK_INTS(ec) 0x11f7e0050: 8b 02 42 b8 cb 07 01 35 # stack overflow check 0x11f7e0058: ab 62 02 91 7f 02 0b eb 69 07 01 54 # save PC to CFP 0x11f7e0064: 0b 3b 9a d2 2b 2f a0 f2 0b 00 cc f2 6b 02 00 0x11f7e0073: f8 ab 82 00 91 ``` To ensure this feature doesn't incur too much cost when running without the `--yjit-dump-disasm` option, I checked that there is no significant impact to compile time and memory usage with the `compile_time_ns` and `yjit_alloc_size` entry in `RubyVM::YJIT.runtime_stats`. For each sample, I ran 3 iterations of the `lobsters` YJIT benchmark. The statistics summary and done with the `summary` function in R. Compile time, sample size of 60, lower is better: ``` Before After Min. :2.054e+09 Min. :2.028e+09 1st Qu.:2.069e+09 1st Qu.:2.044e+09 Median :2.081e+09 Median :2.060e+09 Mean :2.089e+09 Mean :2.066e+09 3rd Qu.:2.109e+09 3rd Qu.:2.085e+09 Max. :2.146e+09 Max. :2.144e+09 ``` Allocation size, sample size of 20, lower is better: ``` Before After Min. :21804742 Min. :21794082 1st Qu.:21826682 1st Qu.:21816282 Median :21844042 Median :21826814 Mean :21960664 Mean :22026291 3rd Qu.:21861228 3rd Qu.:22040439 Max. :22587426 Max. :22930614 ``` The `yjit_alloc_size` samples are noisy, but since the average increased by only 0.3%, and the median is lower, I feel safe saying that there is no significant change.
1 parent 3407565 commit c943e23

File tree

6 files changed

+52
-35
lines changed

6 files changed

+52
-35
lines changed

yjit/src/asm/mod.rs

Lines changed: 9 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,14 @@ use std::cell::RefCell;
22
use std::fmt;
33
use std::mem;
44
use std::rc::Rc;
5+
use std::collections::BTreeMap;
6+
57
use crate::core::IseqPayload;
68
use crate::core::for_each_off_stack_iseq_payload;
79
use crate::core::for_each_on_stack_iseq_payload;
810
use crate::invariants::rb_yjit_tracing_invalidate_all;
911
use crate::stats::incr_counter;
1012
use crate::virtualmem::WriteError;
11-
12-
#[cfg(feature = "disasm")]
13-
use std::collections::BTreeMap;
14-
1513
use crate::codegen::CodegenGlobals;
1614
use crate::virtualmem::{VirtualMem, CodePtr};
1715

@@ -78,7 +76,6 @@ pub struct CodeBlock {
7876
label_refs: Vec<LabelRef>,
7977

8078
// Comments for assembly instructions, if that feature is enabled
81-
#[cfg(feature = "disasm")]
8279
asm_comments: BTreeMap<usize, Vec<String>>,
8380

8481
// True for OutlinedCb
@@ -128,7 +125,6 @@ impl CodeBlock {
128125
label_addrs: Vec::new(),
129126
label_names: Vec::new(),
130127
label_refs: Vec::new(),
131-
#[cfg(feature = "disasm")]
132128
asm_comments: BTreeMap::new(),
133129
outlined,
134130
dropped_bytes: false,
@@ -366,9 +362,11 @@ impl CodeBlock {
366362
}
367363

368364
/// Add an assembly comment if the feature is on.
369-
/// If not, this becomes an inline no-op.
370-
#[cfg(feature = "disasm")]
371365
pub fn add_comment(&mut self, comment: &str) {
366+
if crate::options::get_option_ref!(dump_disasm).is_none() {
367+
return;
368+
}
369+
372370
let cur_ptr = self.get_write_ptr().raw_addr(self);
373371

374372
// If there's no current list of comments for this line number, add one.
@@ -379,28 +377,21 @@ impl CodeBlock {
379377
this_line_comments.push(comment.to_string());
380378
}
381379
}
382-
#[cfg(not(feature = "disasm"))]
383-
#[inline]
384-
pub fn add_comment(&mut self, _: &str) {}
385380

386-
#[cfg(feature = "disasm")]
387381
pub fn comments_at(&self, pos: usize) -> Option<&Vec<String>> {
388382
self.asm_comments.get(&pos)
389383
}
390384

391-
#[allow(unused_variables)]
392-
#[cfg(feature = "disasm")]
393385
pub fn remove_comments(&mut self, start_addr: CodePtr, end_addr: CodePtr) {
386+
if self.asm_comments.is_empty() {
387+
return;
388+
}
394389
for addr in start_addr.raw_addr(self)..end_addr.raw_addr(self) {
395390
self.asm_comments.remove(&addr);
396391
}
397392
}
398-
#[cfg(not(feature = "disasm"))]
399-
#[inline]
400-
pub fn remove_comments(&mut self, _: CodePtr, _: CodePtr) {}
401393

402394
pub fn clear_comments(&mut self) {
403-
#[cfg(feature = "disasm")]
404395
self.asm_comments.clear();
405396
}
406397

yjit/src/backend/arm64/mod.rs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -924,9 +924,7 @@ impl Assembler
924924

925925
match insn {
926926
Insn::Comment(text) => {
927-
if cfg!(feature = "disasm") {
928-
cb.add_comment(text);
929-
}
927+
cb.add_comment(text);
930928
},
931929
Insn::Label(target) => {
932930
cb.write_label(target.unwrap_label_idx());

yjit/src/backend/ir.rs

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1565,13 +1565,10 @@ impl Assembler
15651565
#[must_use]
15661566
pub fn compile(self, cb: &mut CodeBlock, ocb: Option<&mut OutlinedCb>) -> Option<(CodePtr, Vec<u32>)>
15671567
{
1568-
#[cfg(feature = "disasm")]
15691568
let start_addr = cb.get_write_ptr();
1570-
15711569
let alloc_regs = Self::get_alloc_regs();
15721570
let ret = self.compile_with_regs(cb, ocb, alloc_regs);
15731571

1574-
#[cfg(feature = "disasm")]
15751572
if let Some(dump_disasm) = get_option_ref!(dump_disasm) {
15761573
use crate::disasm::dump_disasm_addr_range;
15771574
let end_addr = cb.get_write_ptr();
@@ -2057,10 +2054,10 @@ impl Assembler {
20572054
}
20582055

20592056
/// Macro to use format! for Insn::Comment, which skips a format! call
2060-
/// when disasm is not supported.
2057+
/// when not dumping disassembly.
20612058
macro_rules! asm_comment {
20622059
($asm:expr, $($fmt:tt)*) => {
2063-
if cfg!(feature = "disasm") {
2060+
if $crate::options::get_option_ref!(dump_disasm).is_some() {
20642061
$asm.push_insn(Insn::Comment(format!($($fmt)*)));
20652062
}
20662063
};

yjit/src/backend/x86_64/mod.rs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -492,9 +492,7 @@ impl Assembler
492492

493493
match insn {
494494
Insn::Comment(text) => {
495-
if cfg!(feature = "disasm") {
496-
cb.add_comment(text);
497-
}
495+
cb.add_comment(text);
498496
},
499497

500498
// Write the label at the current position

yjit/src/disasm.rs

Lines changed: 38 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,10 @@
11
use crate::core::*;
22
use crate::cruby::*;
33
use crate::yjit::yjit_enabled_p;
4-
#[cfg(feature = "disasm")]
54
use crate::asm::CodeBlock;
6-
#[cfg(feature = "disasm")]
75
use crate::codegen::CodePtr;
8-
#[cfg(feature = "disasm")]
96
use crate::options::DumpDisasm;
107

11-
#[cfg(feature = "disasm")]
128
use std::fmt::Write;
139

1410
/// Primitive called in yjit.rb
@@ -116,7 +112,6 @@ pub fn disasm_iseq_insn_range(iseq: IseqPtr, start_idx: u16, end_idx: u16) -> St
116112
}
117113

118114
/// Dump dissassembly for a range in a [CodeBlock]. VM lock required.
119-
#[cfg(feature = "disasm")]
120115
pub fn dump_disasm_addr_range(cb: &CodeBlock, start_addr: CodePtr, end_addr: CodePtr, dump_disasm: &DumpDisasm) {
121116
for (start_addr, end_addr) in cb.writable_addrs(start_addr, end_addr) {
122117
let disasm = disasm_addr_range(cb, start_addr, end_addr);
@@ -192,6 +187,44 @@ pub fn disasm_addr_range(cb: &CodeBlock, start_addr: usize, end_addr: usize) ->
192187
return out;
193188
}
194189

190+
#[cfg(not(feature = "disasm"))]
191+
pub fn disasm_addr_range(cb: &CodeBlock, start_addr: usize, end_addr: usize) -> String {
192+
let mut out = String::new();
193+
let mut line_byte_idx = 0;
194+
const MAX_BYTES_PER_LINE: usize = 16;
195+
196+
for addr in start_addr..end_addr {
197+
if let Some(comment_list) = cb.comments_at(addr) {
198+
// Start a new line if we're in the middle of one
199+
if line_byte_idx != 0 {
200+
writeln!(&mut out).unwrap();
201+
line_byte_idx = 0;
202+
}
203+
for comment in comment_list {
204+
writeln!(&mut out, " \x1b[1m# {comment}\x1b[22m").unwrap(); // Make comments bold
205+
}
206+
}
207+
if line_byte_idx == 0 {
208+
write!(&mut out, " 0x{addr:x}: ").unwrap();
209+
} else {
210+
write!(&mut out, " ").unwrap();
211+
}
212+
let byte = unsafe { (addr as *const u8).read() };
213+
write!(&mut out, "{byte:02x}").unwrap();
214+
line_byte_idx += 1;
215+
if line_byte_idx == MAX_BYTES_PER_LINE - 1 {
216+
writeln!(&mut out).unwrap();
217+
line_byte_idx = 0;
218+
}
219+
}
220+
221+
if !out.is_empty() {
222+
writeln!(&mut out).unwrap();
223+
}
224+
225+
out
226+
}
227+
195228
/// Assert that CodeBlock has the code specified with hex. In addition, if tested with
196229
/// `cargo test --all-features`, it also checks it generates the specified disasm.
197230
#[cfg(test)]

yjit/src/options.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -250,7 +250,7 @@ pub fn parse_option(str_ptr: *const std::os::raw::c_char) -> Option<()> {
250250

251251
("dump-disasm", _) => {
252252
if !cfg!(feature = "disasm") {
253-
eprintln!("WARNING: the {} option is only available when YJIT is built in dev mode, i.e. ./configure --enable-yjit=dev", opt_name);
253+
eprintln!("WARNING: the {} option works best when YJIT is built in dev mode, i.e. ./configure --enable-yjit=dev", opt_name);
254254
}
255255

256256
match opt_val {

0 commit comments

Comments
 (0)