ruby-changes:73133
From: Maxime <ko1@a...>
Date: Tue, 30 Aug 2022 00:55:19 +0900 (JST)
Subject: [ruby-changes:73133] d75c346c1c (master): Port gen_leave_exit(), add support for labels to backend
https://git.ruby-lang.org/ruby.git/commit/?id=d75c346c1c From d75c346c1cb5d67fd4c6582274a3ff4f1450af15 Mon Sep 17 00:00:00 2001 From: Maxime Chevalier-Boisvert <maxime.chevalierboisvert@s...> Date: Thu, 9 Jun 2022 15:40:27 -0400 Subject: Port gen_leave_exit(), add support for labels to backend --- yjit/src/asm/mod.rs | 6 +--- yjit/src/backend/ir.rs | 68 ++++++++++++++++++++++++++++++++++-------- yjit/src/backend/x86_64/mod.rs | 47 +++++++++++++++++++++-------- yjit/src/codegen.rs | 38 ++++++++++------------- 4 files changed, 106 insertions(+), 53 deletions(-) diff --git a/yjit/src/asm/mod.rs b/yjit/src/asm/mod.rs index 751f9fce0b..9f518398b7 100644 --- a/yjit/src/asm/mod.rs +++ b/yjit/src/asm/mod.rs @@ -214,16 +214,12 @@ impl CodeBlock { https://github.com/ruby/ruby/blob/trunk/yjit/src/asm/mod.rs#L214 /// Write a label at the current address pub fn write_label(&mut self, label_idx: usize) { - // TODO: make sure that label_idx is valid - // TODO: add an asseer here - self.label_addrs[label_idx] = self.write_pos; } // Add a label reference at the current write position pub fn label_ref(&mut self, label_idx: usize) { - // TODO: make sure that label_idx is valid - // TODO: add an asseer here + assert!(label_idx < self.label_addrs.len()); // Keep track of the reference self.label_refs.push(LabelRef { diff --git a/yjit/src/backend/ir.rs b/yjit/src/backend/ir.rs index 514ac4a67e..63bf85f3a0 100644 --- a/yjit/src/backend/ir.rs +++ b/yjit/src/backend/ir.rs @@ -256,6 +256,13 @@ impl Opnd https://github.com/ruby/ruby/blob/trunk/yjit/src/backend/ir.rs#L256 } } +impl From<VALUE> for Opnd { + fn from(value: VALUE) -> Self { + let VALUE(uimm) = value; + Opnd::UImm(uimm as u64) + } +} + /// NOTE: this is useful during the port but can probably be removed once /// Context returns ir::Opnd instead of X86Opnd /// @@ -290,13 +297,22 @@ impl From<X86Opnd> for Opnd { https://github.com/ruby/ruby/blob/trunk/yjit/src/backend/ir.rs#L297 /// Branch target (something that we can jump to) /// for branch instructions -#[derive(Clone, PartialEq, Eq, Debug)] +#[derive(Clone, Copy, PartialEq, Eq, Debug)] pub enum Target { CodePtr(CodePtr), // Pointer to a piece of YJIT-generated code (e.g. side-exit) FunPtr(*const u8), // Pointer to a C function - LabelName(String), // A label without an index in the output - LabelIdx(usize), // A label that has been indexed + Label(usize), // A label within the generated code +} + +impl Target +{ + pub fn unwrap_label_idx(&self) -> usize { + match self { + Target::Label(idx) => *idx, + _ => unreachable!() + } + } } /// YJIT IR instruction @@ -332,6 +348,9 @@ pub struct Assembler https://github.com/ruby/ruby/blob/trunk/yjit/src/backend/ir.rs#L348 /// Parallel vec with insns /// Index of the last insn using the output of this insn pub(super) live_ranges: Vec<usize>, + + /// Names of labels + pub(super) label_names: Vec<String>, } impl Assembler @@ -340,6 +359,7 @@ impl Assembler https://github.com/ruby/ruby/blob/trunk/yjit/src/backend/ir.rs#L359 Assembler { insns: Vec::default(), live_ranges: Vec::default(), + label_names: Vec::default(), } } @@ -387,30 +407,42 @@ impl Assembler https://github.com/ruby/ruby/blob/trunk/yjit/src/backend/ir.rs#L407 self.live_ranges.push(self.insns.len()); } + /// Create a new label instance that we can jump to + pub fn new_label(&mut self, name: &str) -> Target + { + let label_idx = self.label_names.len(); + dbg!(label_idx); + + self.label_names.push(name.to_string()); + Target::Label(label_idx) + } + /// Add a label at the current position - pub fn label(&mut self, name: &str) -> Target + pub fn write_label(&mut self, label: Target) { - let insn_idx = self.insns.len(); + assert!(label.unwrap_label_idx() < self.label_names.len()); let insn = Insn { op: Op::Label, - text: Some(name.to_owned()), + text: None, opnds: vec![], out: Opnd::None, - target: None, + target: Some(label), pos: None }; self.insns.push(insn); self.live_ranges.push(self.insns.len()); - - Target::LabelIdx(insn_idx) } /// Transform input instructions, consumes the input assembler pub(super) fn transform_insns<F>(mut self, mut map_insn: F) -> Assembler where F: FnMut(&mut Assembler, usize, Op, Vec<Opnd>, Option<Target>) { - let mut asm = Assembler::new(); + let mut asm = Assembler { + insns: Vec::default(), + live_ranges: Vec::default(), + label_names: self.label_names, + }; // indices maps from the old instruction index to the new instruction // index. @@ -435,9 +467,6 @@ impl Assembler https://github.com/ruby/ruby/blob/trunk/yjit/src/backend/ir.rs#L467 Op::Comment => { asm.comment(insn.text.unwrap().as_str()); }, - Op::Label => { - asm.label(insn.text.unwrap().as_str()); - }, _ => { map_insn(&mut asm, index, insn.op, opnds, insn.target); } @@ -931,4 +960,17 @@ mod tests { https://github.com/ruby/ruby/blob/trunk/yjit/src/backend/ir.rs#L960 asm.compile_with_regs(&mut cb, regs); } + + #[test] + fn test_jcc_label() + { + let (mut asm, mut cb, regs) = setup_asm(1); + + let label = asm.new_label("foo"); + asm.cmp(EC, EC); + asm.je(label); + asm.write_label(label); + + asm.compile_with_regs(&mut cb, regs); + } } diff --git a/yjit/src/backend/x86_64/mod.rs b/yjit/src/backend/x86_64/mod.rs index a40bc2a980..467a347b01 100644 --- a/yjit/src/backend/x86_64/mod.rs +++ b/yjit/src/backend/x86_64/mod.rs @@ -107,11 +107,12 @@ impl Assembler https://github.com/ruby/ruby/blob/trunk/yjit/src/backend/x86_64/mod.rs#L107 } }, - Op::Label => {}, + // Write the label at the current position + Op::Label => { + cb.write_label(insn.target.unwrap().unwrap_label_idx()); + }, Op::Add => { - // FIXME: this fails because insn.out is none sometimes - //assert_eq!(insn.out, insn.opnds[0]); add(cb, insn.opnds[0].into(), insn.opnds[1].into()) }, @@ -160,14 +161,23 @@ impl Assembler https://github.com/ruby/ruby/blob/trunk/yjit/src/backend/x86_64/mod.rs#L161 ret(cb); } + // Compare + Op::Cmp => test(cb, insn.opnds[0].into(), insn.opnds[1].into()), + // Test and set flags Op::Test => test(cb, insn.opnds[0].into(), insn.opnds[1].into()), - /* - Cmp, - Jnz, - Jbe, - */ + Op::Je => { + match insn.target.unwrap() { + Target::Label(idx) => { + + dbg!(idx); + je_label(cb, idx); + + }, + _ => unimplemented!() + } + } _ => panic!("unsupported instruction passed to x86 backend: {:?}", insn.op) }; @@ -179,10 +189,21 @@ impl Assembler https://github.com/ruby/ruby/blob/trunk/yjit/src/backend/x86_64/mod.rs#L189 /// Optimize and compile the stored instructions pub fn compile_with_regs(self, cb: &mut CodeBlock, regs: Vec<Reg>) -> Vec<u32> { - self - .x86_split() - .split_loads() - .alloc_regs(regs) - .x86_emit(cb) + let mut asm = self.x86_split(); + let mut asm = asm.split_loads(); + let mut asm = asm.alloc_regs(regs); + + // Create label instances in the code block + for (idx, name) in asm.label_names.iter().enumerate() { + dbg!("creating label, idx={}", idx); + let label_idx = cb.new_label(name.to_string()); + assert!(label_idx == idx); + } + + let gc_offsets = asm.x86_emit(cb); + + cb.link_labels(); + + gc_offsets } } diff --git a/yjit/src/codegen.rs b/yjit/src/codegen.rs index be051c39a6..01c0b7ee85 100644 --- a/yjit/src/codegen.rs +++ b/yjit/src/codegen.rs @@ -535,8 +535,9 @@ fn gen_leave_exit(ocb: &mut OutlinedCb) -> CodePtr { https://github.com/ruby/ruby/blob/trunk/yjit/src/codegen.rs#L535 // Note, gen_leave() fully reconstructs interpreter state and leaves the // return value in RAX before coming here. + // FIXME // Every exit to the interpreter should be counted - gen_counter_incr!(ocb, leave_interp_return); + //gen_counter_incr!(ocb, leave_interp_return); pop(ocb, REG_SP); pop(ocb, REG_EC); @@ -552,31 +553,28 @@ fn gen_leave_exit(ocb: &mut OutlinedCb) -> CodePtr { https://github.com/ruby/ruby/blob/trunk/yjit/src/codegen.rs#L553 // This is to handle the situation of optional parameters. // When a function with optional parameters is called, the entry // PC for the method isn't necessarily 0. -fn gen_pc_guard(cb: &mut CodeBlock, iseq: IseqPtr, insn_idx: u32) { - let pc_opnd = mem_opnd(64, REG_CFP, RUBY_OFFSET_CFP_PC); +fn gen_pc_guard(asm: &mut Assembler, iseq: IseqPtr, insn_idx: u32) { + let pc_opnd = Opnd::mem(64, CFP, RUBY_OFFSET_CFP_PC); let expected_pc = unsafe { rb_iseq_pc_at_idx(iseq, insn_idx) }; - let expected_pc_opnd = const_ptr_opnd(expected_pc as *const u8); + let expected_pc_opnd = Opnd::const_ptr(ex (... truncated) -- ML: ruby-changes@q... Info: http://www.atdot.net/~ko1/quickml/