diff options
| author | Takashi Kokubun <takashikkbn@gmail.com> | 2025-02-10 17:12:31 -0800 |
|---|---|---|
| committer | Takashi Kokubun <takashikkbn@gmail.com> | 2025-04-18 21:52:57 +0900 |
| commit | 9c267256d896125a2aea4be8609ec415ff7760ef (patch) | |
| tree | 8748691ef4902c78ed17b98033116a9f1aa518ce /zjit | |
| parent | e28ab5480e457b54681b4b23b1081a62aec8cb16 (diff) | |
Stub YJIT-specific implementations in the backend
Notes
Notes:
Merged: https://github.com/ruby/ruby/pull/13131
Diffstat (limited to 'zjit')
| -rw-r--r-- | zjit/src/asm/arm64/arg/mod.rs | 2 | ||||
| -rw-r--r-- | zjit/src/asm/mod.rs | 9 | ||||
| -rw-r--r-- | zjit/src/asm/x86_64/mod.rs | 4 | ||||
| -rw-r--r-- | zjit/src/backend.rs | 31 | ||||
| -rw-r--r-- | zjit/src/backend/arm64/mod.rs | 137 | ||||
| -rw-r--r-- | zjit/src/backend/ir.rs | 89 | ||||
| -rw-r--r-- | zjit/src/backend/x86_64/mod.rs | 122 | ||||
| -rw-r--r-- | zjit/src/lib.rs | 3 | ||||
| -rw-r--r-- | zjit/src/utils.rs | 13 |
9 files changed, 225 insertions, 185 deletions
diff --git a/zjit/src/asm/arm64/arg/mod.rs b/zjit/src/asm/arm64/arg/mod.rs index 0949090d68..9aff99a817 100644 --- a/zjit/src/asm/arm64/arg/mod.rs +++ b/zjit/src/asm/arm64/arg/mod.rs @@ -10,6 +10,8 @@ mod sys_reg; mod truncate; pub use bitmask_imm::BitmaskImmediate; +#[cfg(target_arch = "aarch64")] +pub use condition::Condition; pub use inst_offset::InstructionOffset; pub use sf::Sf; pub use shifted_imm::ShiftedImmediate; diff --git a/zjit/src/asm/mod.rs b/zjit/src/asm/mod.rs index b112b2373a..a9d4621b1e 100644 --- a/zjit/src/asm/mod.rs +++ b/zjit/src/asm/mod.rs @@ -50,6 +50,10 @@ impl CodeBlock { } } + pub fn get_write_pos(&self) -> usize { + self.write_pos + } + /// Get a (possibly dangling) direct pointer to the current write position pub fn get_write_ptr(&self) -> CodePtr { self.get_ptr(self.write_pos) @@ -105,6 +109,11 @@ impl CodeBlock { } } + /// Check if bytes have been dropped (unwritten because of insufficient space) + pub fn has_dropped_bytes(&self) -> bool { + self.dropped_bytes + } + // Add a label reference at the current write position pub fn label_ref(&mut self, _label_idx: usize, _num_bytes: usize, _encode: fn(&mut CodeBlock, i64, i64)) { // TODO: copy labels diff --git a/zjit/src/asm/x86_64/mod.rs b/zjit/src/asm/x86_64/mod.rs index b9b64402d9..c8c9187ea4 100644 --- a/zjit/src/asm/x86_64/mod.rs +++ b/zjit/src/asm/x86_64/mod.rs @@ -682,11 +682,10 @@ pub fn call_rel32(cb: &mut CodeBlock, rel32: i32) { cb.write_bytes(&rel32.to_le_bytes()); } -/* /// call - Call a pointer, encode with a 32-bit offset if possible pub fn call_ptr(cb: &mut CodeBlock, scratch_opnd: X86Opnd, dst_ptr: *const u8) { if let X86Opnd::Reg(_scratch_reg) = scratch_opnd { - todo!(); + // TODO: implement a counter // Pointer to the end of this call instruction let end_ptr = cb.get_ptr(cb.write_pos + 5); @@ -707,7 +706,6 @@ pub fn call_ptr(cb: &mut CodeBlock, scratch_opnd: X86Opnd, dst_ptr: *const u8) { unreachable!(); } } -*/ /// call - Call to label with 32-bit offset pub fn call_label(cb: &mut CodeBlock, label_idx: usize) { diff --git a/zjit/src/backend.rs b/zjit/src/backend.rs deleted file mode 100644 index 01249ba13d..0000000000 --- a/zjit/src/backend.rs +++ /dev/null @@ -1,31 +0,0 @@ -use crate::{asm::x86_64::{add, mov, ret, RAX_REG, RDI_REG, RSI_REG}, asm::CodeBlock, cruby::{Qnil, RUBY_OFFSET_EC_CFP, RUBY_SIZEOF_CONTROL_FRAME}}; -use crate::asm::x86_64::X86Opnd::Mem; -use crate::asm::x86_64::X86Opnd::Reg; -use crate::asm::x86_64::X86Opnd::UImm; -use crate::asm::x86_64::X86UImm; -use crate::asm::x86_64::X86Mem; - -// Emit x86_64 instructions into CodeBlock -// TODO: Create a module like YJIT's Assembler and consider putting this there -pub fn x86_emit(cb: &mut CodeBlock) { // TODO: take our backend IR - // rdi: EC, rsi: CFP - let ec = RDI_REG; - let cfp = RSI_REG; - - // Pop frame: CFP = CFP + RUBY_SIZEOF_CONTROL_FRAME - add(cb, Reg(cfp), UImm(X86UImm { num_bits: 64, value: RUBY_SIZEOF_CONTROL_FRAME as u64 })); - - // Set ec->cfp: *(EC + RUBY_OFFSET_EC_CFP) = CFP - let ec_cfp = X86Mem { - num_bits: 64, - base_reg_no: ec.reg_no, - idx_reg_no: None, - scale_exp: 0, - disp: RUBY_OFFSET_EC_CFP, - }; - mov(cb, Mem(ec_cfp), Reg(cfp)); - - // Return Qnil - mov(cb, Reg(RAX_REG), UImm(X86UImm { num_bits: 64, value: Qnil.as_u64() })); - ret(cb); -} diff --git a/zjit/src/backend/arm64/mod.rs b/zjit/src/backend/arm64/mod.rs index 66e333f867..42cefca5f2 100644 --- a/zjit/src/backend/arm64/mod.rs +++ b/zjit/src/backend/arm64/mod.rs @@ -1,6 +1,6 @@ use std::mem::take; -use crate::asm::{CodeBlock, OutlinedCb}; +use crate::asm::{CodeBlock}; use crate::asm::arm64::*; use crate::cruby::*; use crate::backend::ir::*; @@ -39,11 +39,14 @@ impl CodeBlock { pub fn jmp_ptr_bytes(&self) -> usize { // b instruction's offset is encoded as imm26 times 4. It can jump to // +/-128MiB, so this can be used when --yjit-exec-mem-size <= 128. + /* let num_insns = if b_offset_fits_bits(self.virtual_region_size() as i64 / 4) { 1 // b instruction } else { 5 // 4 instructions to load a 64-bit absolute address + br instruction }; + */ + let num_insns = 5; // TODO: support virtual_region_size() check num_insns * 4 } @@ -70,7 +73,7 @@ impl From<Opnd> for A64Opnd { Opnd::CArg(_) => panic!("attempted to lower an Opnd::CArg"), Opnd::InsnOut { .. } => panic!("attempted to lower an Opnd::InsnOut"), Opnd::Value(_) => panic!("attempted to lower an Opnd::Value"), - Opnd::Stack { .. } => panic!("attempted to lower an Opnd::Stack"), + //Opnd::Stack { .. } => panic!("attempted to lower an Opnd::Stack"), Opnd::None => panic!( "Attempted to lower an Opnd::None. This often happens when an out operand was not allocated for an instruction because the output of the instruction was not used. Please ensure you are using the output." ), @@ -92,14 +95,18 @@ impl From<&Opnd> for A64Opnd { /// more than necessary when other_cb jumps from a position early in the page. /// This invalidates a small range of cb twice, but we accept the small cost. fn emit_jmp_ptr_with_invalidation(cb: &mut CodeBlock, dst_ptr: CodePtr) { + /* #[cfg(not(test))] let start = cb.get_write_ptr(); + */ emit_jmp_ptr(cb, dst_ptr, true); + /* #[cfg(not(test))] { let end = cb.get_write_ptr(); unsafe { rb_yjit_icache_invalidate(start.raw_ptr(cb) as _, end.raw_ptr(cb) as _) }; } + */ } fn emit_jmp_ptr(cb: &mut CodeBlock, dst_ptr: CodePtr, padding: bool) { @@ -280,7 +287,7 @@ impl Assembler /// do follow that encoding, and if they don't then we load them first. fn split_bitmask_immediate(asm: &mut Assembler, opnd: Opnd, dest_num_bits: u8) -> Opnd { match opnd { - Opnd::Reg(_) | Opnd::CArg(_) | Opnd::InsnOut { .. } | Opnd::Stack { .. } => opnd, + Opnd::Reg(_) | Opnd::CArg(_) | Opnd::InsnOut { .. } /*| Opnd::Stack { .. }*/ => opnd, Opnd::Mem(_) => split_load_operand(asm, opnd), Opnd::Imm(imm) => { if imm == 0 { @@ -327,7 +334,7 @@ impl Assembler asm.load(opnd) } }, - Opnd::None | Opnd::Value(_) | Opnd::Stack { .. } => unreachable!() + Opnd::None | Opnd::Value(_) /*| Opnd::Stack { .. }*/ => unreachable!() } } @@ -381,7 +388,7 @@ impl Assembler } let live_ranges: Vec<usize> = take(&mut self.live_ranges); - let mut asm_local = Assembler::new_with_label_names(take(&mut self.label_names), take(&mut self.side_exits), self.num_locals); + let mut asm_local = Assembler::new_with_label_names(take(&mut self.label_names)); let asm = &mut asm_local; let mut iterator = self.into_draining_iter(); @@ -403,9 +410,9 @@ impl Assembler *opnd = asm.load(*opnd); } }, - Opnd::Stack { .. } => { - *opnd = asm.lower_stack_opnd(opnd); - } + //Opnd::Stack { .. } => { + // *opnd = asm.lower_stack_opnd(opnd); + //} _ => {} }; } @@ -445,11 +452,12 @@ impl Assembler if let (Opnd::Reg(_), Opnd::Reg(_), Some(Insn::Mov { dest, src })) = (left, right, iterator.peek()) { if live_ranges[index] == index + 1 { // Check after potentially lowering a stack operand to a register operand - let lowered_dest = if let Opnd::Stack { .. } = dest { - asm.lower_stack_opnd(dest) - } else { - *dest - }; + //let lowered_dest = if let Opnd::Stack { .. } = dest { + // asm.lower_stack_opnd(dest) + //} else { + // *dest + //}; + let lowered_dest = *dest; if out == src && matches!(lowered_dest, Opnd::Reg(_)) { *out = lowered_dest; iterator.map_insn_index(asm); @@ -460,6 +468,7 @@ impl Assembler asm.push_insn(insn); } + /* // Lower to Joz and Jonz for generating CBZ/CBNZ for compare-with-0-and-branch. ref insn @ Insn::Cmp { ref left, right: ref right @ (Opnd::UImm(0) | Opnd::Imm(0)) } | ref insn @ Insn::Test { ref left, right: ref right @ (Opnd::InsnOut { .. } | Opnd::Reg(_)) } if { @@ -487,6 +496,7 @@ impl Assembler iterator.map_insn_index(asm); iterator.next_unmapped(); // Pop merged jump instruction } + */ Insn::CCall { opnds, fptr, .. } => { assert!(opnds.len() <= C_ARG_OPNDS.len()); @@ -743,7 +753,7 @@ impl Assembler /// Emit platform-specific machine code /// Returns a list of GC offsets. Can return failure to signal caller to retry. - fn arm64_emit(&mut self, cb: &mut CodeBlock, ocb: &mut Option<&mut OutlinedCb>) -> Result<Vec<u32>, EmitError> { + fn arm64_emit(&mut self, cb: &mut CodeBlock) -> Result<Vec<u32>, EmitError> { /// Determine how many instructions it will take to represent moving /// this value into a register. Note that the return value of this /// function must correspond to how many instructions are used to @@ -833,9 +843,9 @@ impl Assembler bcond(cb, CONDITION, InstructionOffset::from_bytes(bytes)); }); }, - Target::SideExit { .. } => { - unreachable!("Target::SideExit should have been compiled by compile_side_exit") - }, + //Target::SideExit { .. } => { + // unreachable!("Target::SideExit should have been compiled by compile_side_exit") + //}, }; } @@ -845,7 +855,7 @@ impl Assembler let dst_addr = dst_ptr.as_offset(); let src_addr = cb.get_write_ptr().as_offset(); - if cmp_branch_offset_fits_bits((dst_addr - src_addr) / 4) { + if bcond_offset_fits_bits((dst_addr - src_addr) / 4) { // If the offset fits in one instruction, generate cbz or cbnz let bytes = (dst_addr - src_addr) as i32; if branch_if_zero { @@ -890,6 +900,7 @@ impl Assembler ldr_post(cb, opnd, A64Opnd::new_mem(64, C_SP_REG, C_SP_STEP)); } + /* /// Compile a side exit if Target::SideExit is given. fn compile_side_exit( target: Target, @@ -904,6 +915,7 @@ impl Assembler Ok(target) } } + */ // dbg!(&self.insns); @@ -914,20 +926,22 @@ impl Assembler let mut pos_markers: Vec<(usize, CodePtr)> = vec![]; // For each instruction - let start_write_pos = cb.get_write_pos(); + //let start_write_pos = cb.get_write_pos(); let mut insn_idx: usize = 0; while let Some(insn) = self.insns.get(insn_idx) { - let src_ptr = cb.get_write_ptr(); + //let src_ptr = cb.get_write_ptr(); let had_dropped_bytes = cb.has_dropped_bytes(); - let old_label_state = cb.get_label_state(); + //let old_label_state = cb.get_label_state(); let mut insn_gc_offsets: Vec<u32> = Vec::new(); match insn { - Insn::Comment(text) => { - cb.add_comment(text); + Insn::Comment(_text) => { + //cb.add_comment(text); + unimplemented!("comments are not supported yet"); }, - Insn::Label(target) => { - cb.write_label(target.unwrap_label_idx()); + Insn::Label(_target) => { + //cb.write_label(target.unwrap_label_idx()); + unimplemented!("labels are not supported yet"); }, // Report back the current position in the generated code Insn::PosMarker(..) => { @@ -1065,9 +1079,9 @@ impl Assembler Opnd::CArg { .. } => { unreachable!("C argument operand was not lowered before arm64_emit"); } - Opnd::Stack { .. } => { - unreachable!("Stack operand was not lowered before arm64_emit"); - } + //Opnd::Stack { .. } => { + // unreachable!("Stack operand was not lowered before arm64_emit"); + //} Opnd::None => { unreachable!("Attempted to load from None operand"); } @@ -1189,7 +1203,7 @@ impl Assembler br(cb, opnd.into()); }, Insn::Jmp(target) => { - match compile_side_exit(*target, self, ocb)? { + match *target { Target::CodePtr(dst_ptr) => { emit_jmp_ptr(cb, dst_ptr, true); }, @@ -1207,42 +1221,43 @@ impl Assembler b(cb, InstructionOffset::from_bytes(bytes)); }); }, - Target::SideExit { .. } => { - unreachable!("Target::SideExit should have been compiled by compile_side_exit") - }, + //Target::SideExit { .. } => { + // unreachable!("Target::SideExit should have been compiled by compile_side_exit") + //}, }; }, Insn::Je(target) | Insn::Jz(target) => { - emit_conditional_jump::<{Condition::EQ}>(cb, compile_side_exit(*target, self, ocb)?); + emit_conditional_jump::<{Condition::EQ}>(cb, *target); }, Insn::Jne(target) | Insn::Jnz(target) | Insn::JoMul(target) => { - emit_conditional_jump::<{Condition::NE}>(cb, compile_side_exit(*target, self, ocb)?); + emit_conditional_jump::<{Condition::NE}>(cb, *target); }, Insn::Jl(target) => { - emit_conditional_jump::<{Condition::LT}>(cb, compile_side_exit(*target, self, ocb)?); + emit_conditional_jump::<{Condition::LT}>(cb, *target); }, Insn::Jg(target) => { - emit_conditional_jump::<{Condition::GT}>(cb, compile_side_exit(*target, self, ocb)?); + emit_conditional_jump::<{Condition::GT}>(cb, *target); }, Insn::Jge(target) => { - emit_conditional_jump::<{Condition::GE}>(cb, compile_side_exit(*target, self, ocb)?); + emit_conditional_jump::<{Condition::GE}>(cb, *target); }, Insn::Jbe(target) => { - emit_conditional_jump::<{Condition::LS}>(cb, compile_side_exit(*target, self, ocb)?); + emit_conditional_jump::<{Condition::LS}>(cb, *target); }, Insn::Jb(target) => { - emit_conditional_jump::<{Condition::CC}>(cb, compile_side_exit(*target, self, ocb)?); + emit_conditional_jump::<{Condition::CC}>(cb, *target); }, Insn::Jo(target) => { - emit_conditional_jump::<{Condition::VS}>(cb, compile_side_exit(*target, self, ocb)?); + emit_conditional_jump::<{Condition::VS}>(cb, *target); }, Insn::Joz(opnd, target) => { - emit_cmp_zero_jump(cb, opnd.into(), true, compile_side_exit(*target, self, ocb)?); + emit_cmp_zero_jump(cb, opnd.into(), true, *target); }, Insn::Jonz(opnd, target) => { - emit_cmp_zero_jump(cb, opnd.into(), false, compile_side_exit(*target, self, ocb)?); + emit_cmp_zero_jump(cb, opnd.into(), false, *target); }, - Insn::IncrCounter { mem, value } => { + Insn::IncrCounter { mem: _, value: _ } => { + /* let label = cb.new_label("incr_counter_loop".to_string()); cb.write_label(label); @@ -1258,6 +1273,8 @@ impl Assembler cmp(cb, Self::SCRATCH1, A64Opnd::new_uimm(0)); emit_conditional_jump::<{Condition::NE}>(cb, Target::Label(label)); + */ + unimplemented!("labels are not supported yet"); }, Insn::Breakpoint => { brk(cb, A64Opnd::None); @@ -1284,16 +1301,14 @@ impl Assembler } Insn::LiveReg { .. } => (), // just a reg alloc signal, no code Insn::PadInvalPatch => { - while (cb.get_write_pos().saturating_sub(std::cmp::max(start_write_pos, cb.page_start_pos()))) < cb.jmp_ptr_bytes() && !cb.has_dropped_bytes() { - nop(cb); - } + unimplemented!("we haven't needed padding in ZJIT yet"); } }; // On failure, jump to the next page and retry the current insn - if !had_dropped_bytes && cb.has_dropped_bytes() && cb.next_page(src_ptr, emit_jmp_ptr_with_invalidation) { + if !had_dropped_bytes && cb.has_dropped_bytes() { // Reset cb states before retrying the current Insn - cb.set_label_state(old_label_state); + //cb.set_label_state(old_label_state); // We don't want label references to cross page boundaries. Signal caller for // retry. @@ -1324,19 +1339,21 @@ impl Assembler } /// Optimize and compile the stored instructions - pub fn compile_with_regs(self, cb: &mut CodeBlock, ocb: Option<&mut OutlinedCb>, regs: Vec<Reg>) -> Option<(CodePtr, Vec<u32>)> { + pub fn compile_with_regs(self, cb: &mut CodeBlock, regs: Vec<Reg>) -> Option<(CodePtr, Vec<u32>)> { let asm = self.arm64_split(); let mut asm = asm.alloc_regs(regs); // Create label instances in the code block + /* for (idx, name) in asm.label_names.iter().enumerate() { let label_idx = cb.new_label(name.to_string()); assert!(label_idx == idx); } + */ let start_ptr = cb.get_write_ptr(); + /* let starting_label_state = cb.get_label_state(); - let mut ocb = ocb; // for &mut let emit_result = match asm.arm64_emit(cb, &mut ocb) { Err(EmitError::RetryOnNextPage) => { // we want to lower jumps to labels to b.cond instructions, which have a 1 MiB @@ -1351,29 +1368,32 @@ impl Assembler } result => result }; + */ + let emit_result = asm.arm64_emit(cb); if let (Ok(gc_offsets), false) = (emit_result, cb.has_dropped_bytes()) { - cb.link_labels(); + //cb.link_labels(); // Invalidate icache for newly written out region so we don't run stale code. // It should invalidate only the code ranges of the current cb because the code // ranges of the other cb might have a memory region that is still PROT_NONE. - #[cfg(not(test))] - cb.without_page_end_reserve(|cb| { - for (start, end) in cb.writable_addrs(start_ptr, cb.get_write_ptr()) { - unsafe { rb_yjit_icache_invalidate(start as _, end as _) }; - } - }); + //#[cfg(not(test))] + //cb.without_page_end_reserve(|cb| { + // for (start, end) in cb.writable_addrs(start_ptr, cb.get_write_ptr()) { + // unsafe { rb_yjit_icache_invalidate(start as _, end as _) }; + // } + //}); Some((start_ptr, gc_offsets)) } else { - cb.clear_labels(); + //cb.clear_labels(); None } } } +/* #[cfg(test)] mod tests { use super::*; @@ -1828,3 +1848,4 @@ mod tests { "}); } } +*/ diff --git a/zjit/src/backend/ir.rs b/zjit/src/backend/ir.rs index 75cec765f7..7902df7d9b 100644 --- a/zjit/src/backend/ir.rs +++ b/zjit/src/backend/ir.rs @@ -1,14 +1,14 @@ -use std::collections::HashMap; use std::fmt; use std::convert::From; use std::mem::take; -use crate::codegen::{gen_counted_exit, gen_outlined_exit}; -use crate::cruby::{vm_stack_canary, SIZEOF_VALUE_I32, VALUE, VM_ENV_DATA_SIZE}; +use crate::asm::CodeBlock; +//use crate::codegen::{gen_counted_exit, gen_outlined_exit}; +use crate::cruby::VALUE; use crate::virtualmem::CodePtr; -use crate::asm::{CodeBlock, OutlinedCb}; -use crate::core::{Context, RegMapping, RegOpnd, MAX_CTX_TEMPS}; +//use crate::asm::{CodeBlock, OutlinedCb}; +//use crate::core::{Context, RegMapping, RegOpnd, MAX_CTX_TEMPS}; use crate::options::*; -use crate::stats::*; +//use crate::stats::*; use crate::backend::current::*; @@ -70,6 +70,7 @@ pub enum Opnd InsnOut{ idx: usize, num_bits: u8 }, /// Pointer to a slot on the VM stack + /* Stack { /// Index from stack top. Used for conversion to StackOpnd. idx: i32, @@ -84,6 +85,7 @@ pub enum Opnd /// ctx.reg_mapping when this operand is read. Used for register allocation. reg_mapping: Option<RegMapping> }, + */ // Low-level operands, for lowering Imm(i64), // Raw signed immediate @@ -99,7 +101,7 @@ impl fmt::Debug for Opnd { Self::None => write!(fmt, "None"), Value(val) => write!(fmt, "Value({val:?})"), CArg(reg) => write!(fmt, "CArg({reg:?})"), - Stack { idx, sp_offset, .. } => write!(fmt, "SP[{}]", *sp_offset as i32 - idx - 1), + //Stack { idx, sp_offset, .. } => write!(fmt, "SP[{}]", *sp_offset as i32 - idx - 1), InsnOut { idx, num_bits } => write!(fmt, "Out{num_bits}({idx})"), Imm(signed) => write!(fmt, "{signed:x}_i64"), UImm(unsigned) => write!(fmt, "{unsigned:x}_u64"), @@ -174,7 +176,7 @@ impl Opnd Opnd::Reg(reg) => Some(Opnd::Reg(reg.with_num_bits(num_bits))), Opnd::Mem(Mem { base, disp, .. }) => Some(Opnd::Mem(Mem { base, disp, num_bits })), Opnd::InsnOut { idx, .. } => Some(Opnd::InsnOut { idx, num_bits }), - Opnd::Stack { idx, stack_size, num_locals, sp_offset, reg_mapping, .. } => Some(Opnd::Stack { idx, num_bits, stack_size, num_locals, sp_offset, reg_mapping }), + //Opnd::Stack { idx, stack_size, num_locals, sp_offset, reg_mapping, .. } => Some(Opnd::Stack { idx, num_bits, stack_size, num_locals, sp_offset, reg_mapping }), _ => None, } } @@ -229,6 +231,7 @@ impl Opnd Self::match_num_bits_iter(opnds.iter()) } + /* /// Convert Opnd::Stack into RegMapping pub fn reg_opnd(&self) -> RegOpnd { self.get_reg_opnd().unwrap() @@ -251,6 +254,7 @@ impl Opnd _ => None, } } + */ } impl From<usize> for Opnd { @@ -296,8 +300,8 @@ pub enum Target { /// Pointer to a piece of YJIT-generated code CodePtr(CodePtr), - /// Side exit with a counter - SideExit { counter: Counter, context: Option<SideExitContext> }, + // Side exit with a counter + //SideExit { counter: Counter, context: Option<SideExitContext> }, /// Pointer to a side exit code SideExitPtr(CodePtr), /// A label within the generated code @@ -306,9 +310,11 @@ pub enum Target impl Target { + /* pub fn side_exit(counter: Counter) -> Target { Target::SideExit { counter, context: None } } + */ pub fn unwrap_label_idx(&self) -> usize { match self { @@ -966,6 +972,7 @@ impl fmt::Debug for Insn { } /// Set of variables used for generating side exits +/* #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] pub struct SideExitContext { /// PC of the instruction being compiled @@ -1012,6 +1019,7 @@ impl SideExitContext { ctx } } +*/ /// Initial capacity for asm.insns vector const ASSEMBLER_INSNS_CAPACITY: usize = 256; @@ -1028,6 +1036,7 @@ pub struct Assembler { /// Names of labels pub(super) label_names: Vec<String>, + /* /// Context for generating the current insn pub ctx: Context, @@ -1049,10 +1058,17 @@ pub struct Assembler { /// If true, the next ccall() should verify its leafness leaf_ccall: bool, + */ } impl Assembler { + /// Create an Assembler + pub fn new() -> Self { + Self::new_with_label_names(Vec::default()) + } + + /* /// Create an Assembler for ISEQ-specific code. /// It includes all inline code and some outlined code like side exits and stubs. pub fn new(num_locals: u32) -> Self { @@ -1064,27 +1080,19 @@ impl Assembler pub fn new_without_iseq() -> Self { Self::new_with_label_names(Vec::default(), HashMap::default(), None) } + */ /// Create an Assembler with parameters that are populated by another Assembler instance. /// This API is used for copying an Assembler for the next compiler pass. - pub fn new_with_label_names( - label_names: Vec<String>, - side_exits: HashMap<SideExitContext, CodePtr>, - num_locals: Option<u32> - ) -> Self { + pub fn new_with_label_names(label_names: Vec<String>) -> Self { Self { insns: Vec::with_capacity(ASSEMBLER_INSNS_CAPACITY), live_ranges: Vec::with_capacity(ASSEMBLER_INSNS_CAPACITY), label_names, - ctx: Context::default(), - num_locals, - side_exits, - side_exit_pc: None, - side_exit_stack_size: None, - leaf_ccall: false, } } + /* /// Get the list of registers that can be used for stack temps. pub fn get_temp_regs2() -> &'static [Reg] { let num_regs = get_option!(num_temp_regs); @@ -1101,6 +1109,7 @@ impl Assembler self.side_exit_pc = Some(pc); self.side_exit_stack_size = Some(stack_size); } + */ /// Build an Opnd::InsnOut from the current index of the assembler and the /// given number of bits. @@ -1129,6 +1138,7 @@ impl Assembler self.live_ranges[idx] = insn_idx; } // Set current ctx.reg_mapping to Opnd::Stack. + /* Opnd::Stack { idx, num_bits, stack_size, num_locals, sp_offset, reg_mapping: None } => { assert_eq!( self.ctx.get_stack_size() as i16 - self.ctx.get_sp_offset() as i16, @@ -1145,11 +1155,13 @@ impl Assembler reg_mapping: Some(self.ctx.get_reg_mapping()), }; } + */ _ => {} } } // Set a side exit context to Target::SideExit + /* if let Some(Target::SideExit { context, .. }) = insn.target_mut() { // We should skip this when this instruction is being copied from another Assembler. if context.is_none() { @@ -1159,11 +1171,13 @@ impl Assembler )); } } + */ self.insns.push(insn); self.live_ranges.push(insn_idx); } + /* /// Get a cached side exit, wrapping a counter if specified pub fn get_side_exit(&mut self, side_exit_context: &SideExitContext, counter: Option<Counter>, ocb: &mut OutlinedCb) -> Option<CodePtr> { // Get a cached side exit @@ -1179,6 +1193,7 @@ impl Assembler // Wrap a counter if needed gen_counted_exit(side_exit_context.pc, side_exit, ocb, counter) } + */ /// Create a new label instance that we can jump to pub fn new_label(&mut self, name: &str) -> Target @@ -1190,6 +1205,7 @@ impl Assembler Target::Label(label_idx) } + /* /// Convert Opnd::Stack to Opnd::Mem or Opnd::Reg pub fn lower_stack_opnd(&self, opnd: &Opnd) -> Opnd { // Convert Opnd::Stack to Opnd::Mem @@ -1345,6 +1361,7 @@ impl Assembler self.ctx.set_reg_mapping(reg_mapping); } } + */ // Shuffle register moves, sometimes adding extra moves using SCRATCH_REG, // so that they will not rewrite each other before they are used. @@ -1471,7 +1488,7 @@ impl Assembler let live_ranges: Vec<usize> = take(&mut self.live_ranges); // shifted_live_ranges is indexed by mapped indexes in insn operands. let mut shifted_live_ranges: Vec<usize> = live_ranges.clone(); - let mut asm = Assembler::new_with_label_names(take(&mut self.label_names), take(&mut self.side_exits), self.num_locals); + let mut asm = Assembler::new_with_label_names(take(&mut self.label_names)); let mut iterator = self.into_draining_iter(); while let Some((index, mut insn)) = iterator.next_mapped() { @@ -1623,20 +1640,21 @@ impl Assembler /// Compile the instructions down to machine code. /// Can fail due to lack of code memory and inopportune code placement, among other reasons. #[must_use] - pub fn compile(self, cb: &mut CodeBlock, ocb: Option<&mut OutlinedCb>) -> Option<(CodePtr, Vec<u32>)> + pub fn compile(self, cb: &mut CodeBlock) -> Option<(CodePtr, Vec<u32>)> { let start_addr = cb.get_write_ptr(); let alloc_regs = Self::get_alloc_regs(); - let ret = self.compile_with_regs(cb, ocb, alloc_regs); + let ret = self.compile_with_regs(cb, alloc_regs); - if let Some(dump_disasm) = get_option_ref!(dump_disasm) { - use crate::disasm::dump_disasm_addr_range; + if get_option!(dump_disasm) { let end_addr = cb.get_write_ptr(); - dump_disasm_addr_range(cb, start_addr, end_addr, dump_disasm) + let disasm = crate::disasm::disasm_addr_range(start_addr.raw_ptr(cb) as usize, end_addr.raw_ptr(cb) as usize); + println!("{}", disasm); } ret } + /* /// Compile with a limited number of registers. Used only for unit tests. #[cfg(test)] pub fn compile_with_num_regs(self, cb: &mut CodeBlock, num_regs: usize) -> (CodePtr, Vec<u32>) @@ -1645,12 +1663,14 @@ impl Assembler let alloc_regs = alloc_regs.drain(0..num_regs).collect(); self.compile_with_regs(cb, None, alloc_regs).unwrap() } + */ /// Consume the assembler by creating a new draining iterator. pub fn into_draining_iter(self) -> AssemblerDrainingIterator { AssemblerDrainingIterator::new(self) } + /* /// Return true if the next ccall() is expected to be leaf. pub fn get_leaf_ccall(&mut self) -> bool { self.leaf_ccall @@ -1660,6 +1680,7 @@ impl Assembler pub fn expect_leaf_ccall(&mut self) { self.leaf_ccall = true; } + */ } /// A struct that allows iterating through an assembler's instructions and @@ -1760,6 +1781,7 @@ impl Assembler { } pub fn ccall(&mut self, fptr: *const u8, opnds: Vec<Opnd>) -> Opnd { + /* // Let vm_check_canary() assert this ccall's leafness if leaf_ccall is set let canary_opnd = self.set_stack_canary(&opnds); @@ -1773,11 +1795,13 @@ impl Assembler { // Temporarily manipulate RegMappings so that we can use registers // to pass stack operands that are already spilled above. self.ctx.set_reg_mapping(old_temps); + */ // Call a C function let out = self.next_opnd_out(Opnd::match_num_bits(&opnds)); self.push_insn(Insn::CCall { fptr, opnds, out }); + /* // Registers in old_temps may be clobbered by the above C call, // so rollback the manipulated RegMappings to a spilled version. self.ctx.set_reg_mapping(new_temps); @@ -1786,10 +1810,12 @@ impl Assembler { if let Some(canary_opnd) = canary_opnd { self.mov(canary_opnd, 0.into()); } + */ out } + /* /// Let vm_check_canary() assert the leafness of this ccall if leaf_ccall is set fn set_stack_canary(&mut self, opnds: &Vec<Opnd>) -> Option<Opnd> { // Use the slot right above the stack top for verifying leafness. @@ -1812,6 +1838,7 @@ impl Assembler { canary_opnd } + */ pub fn cmp(&mut self, left: Opnd, right: Opnd) { self.push_insn(Insn::Cmp { left, right }); @@ -1829,7 +1856,7 @@ impl Assembler { // Re-enable ccall's RegMappings assertion disabled by cpush_all. // cpush_all + cpop_all preserve all stack temp registers, so it's safe. - self.set_reg_mapping(self.ctx.get_reg_mapping()); + //self.set_reg_mapping(self.ctx.get_reg_mapping()); } pub fn cpop_into(&mut self, opnd: Opnd) { @@ -1847,7 +1874,7 @@ impl Assembler { // Temps will be marked back as being in registers by cpop_all. // We assume that cpush_all + cpop_all are used for C functions in utils.rs // that don't require spill_regs for GC. - self.set_reg_mapping(RegMapping::default()); + //self.set_reg_mapping(RegMapping::default()); } pub fn cret(&mut self, opnd: Opnd) { @@ -2089,6 +2116,7 @@ impl Assembler { out } + /* /// Verify the leafness of the given block pub fn with_leaf_ccall<F, R>(&mut self, mut block: F) -> R where F: FnMut(&mut Self) -> R { @@ -2098,6 +2126,7 @@ impl Assembler { self.leaf_ccall = old_leaf_ccall; ret } + */ /// Add a label at the current position pub fn write_label(&mut self, target: Target) { @@ -2115,6 +2144,7 @@ impl Assembler { /// Macro to use format! for Insn::Comment, which skips a format! call /// when not dumping disassembly. +/* macro_rules! asm_comment { ($asm:expr, $($fmt:tt)*) => { if $crate::options::get_option_ref!(dump_disasm).is_some() { @@ -2123,6 +2153,7 @@ macro_rules! asm_comment { }; } pub(crate) use asm_comment; +*/ #[cfg(test)] mod tests { diff --git a/zjit/src/backend/x86_64/mod.rs b/zjit/src/backend/x86_64/mod.rs index c0d42e79e6..17e5a3e605 100644 --- a/zjit/src/backend/x86_64/mod.rs +++ b/zjit/src/backend/x86_64/mod.rs @@ -2,10 +2,9 @@ use std::mem::take; use crate::asm::*; use crate::asm::x86_64::*; -use crate::codegen::CodePtr; +use crate::virtualmem::CodePtr; use crate::cruby::*; use crate::backend::ir::*; -use crate::options::*; use crate::utils::*; // Use the x86 register type for this platform @@ -112,7 +111,7 @@ impl Assembler fn x86_split(mut self) -> Assembler { let live_ranges: Vec<usize> = take(&mut self.live_ranges); - let mut asm = Assembler::new_with_label_names(take(&mut self.label_names), take(&mut self.side_exits), self.num_locals); + let mut asm = Assembler::new_with_label_names(take(&mut self.label_names)); let mut iterator = self.into_draining_iter(); while let Some((index, mut insn)) = iterator.next_unmapped() { @@ -143,9 +142,9 @@ impl Assembler let mut opnd_iter = insn.opnd_iter_mut(); while let Some(opnd) = opnd_iter.next() { - if let Opnd::Stack { .. } = opnd { - *opnd = asm.lower_stack_opnd(opnd); - } + //if let Opnd::Stack { .. } = opnd { + // *opnd = asm.lower_stack_opnd(opnd); + //} unmapped_opnds.push(*opnd); *opnd = match opnd { @@ -186,11 +185,12 @@ impl Assembler // We want to do `dest == left`, but `left` has already gone // through lower_stack_opnd() while `dest` has not. So we // lower `dest` before comparing. - let lowered_dest = if let Opnd::Stack { .. } = dest { - asm.lower_stack_opnd(dest) - } else { - *dest - }; + //let lowered_dest = if let Opnd::Stack { .. } = dest { + // asm.lower_stack_opnd(dest) + //} else { + // *dest + //}; + let lowered_dest = *dest; lowered_dest == *left } => { *out = *dest; @@ -402,7 +402,7 @@ impl Assembler } /// Emit platform-specific machine code - pub fn x86_emit(&mut self, cb: &mut CodeBlock, ocb: &mut Option<&mut OutlinedCb>) -> Option<Vec<u32>> + pub fn x86_emit(&mut self, cb: &mut CodeBlock) -> Option<Vec<u32>> { /// For some instructions, we want to be able to lower a 64-bit operand /// without requiring more registers to be available in the register @@ -432,20 +432,6 @@ impl Assembler } } - /// Compile a side exit if Target::SideExit is given. - fn compile_side_exit( - target: Target, - asm: &mut Assembler, - ocb: &mut Option<&mut OutlinedCb>, - ) -> Option<Target> { - if let Target::SideExit { counter, context } = target { - let side_exit = asm.get_side_exit(&context.unwrap(), Some(counter), ocb.as_mut().unwrap()); - Some(Target::SideExitPtr(side_exit?)) - } else { - Some(target) - } - } - fn emit_csel( cb: &mut CodeBlock, truthy: Opnd, @@ -482,22 +468,24 @@ impl Assembler let mut pos_markers: Vec<(usize, CodePtr)> = vec![]; // For each instruction - let start_write_pos = cb.get_write_pos(); + //let start_write_pos = cb.get_write_pos(); let mut insn_idx: usize = 0; while let Some(insn) = self.insns.get(insn_idx) { - let src_ptr = cb.get_write_ptr(); + //let src_ptr = cb.get_write_ptr(); let had_dropped_bytes = cb.has_dropped_bytes(); - let old_label_state = cb.get_label_state(); + //let old_label_state = cb.get_label_state(); let mut insn_gc_offsets: Vec<u32> = Vec::new(); match insn { - Insn::Comment(text) => { - cb.add_comment(text); + Insn::Comment(_text) => { + unimplemented!("comments are not supported yet"); + //cb.add_comment(text); }, // Write the label at the current position - Insn::Label(target) => { - cb.write_label(target.unwrap_label_idx()); + Insn::Label(_target) => { + unimplemented!("labels are not supported yet"); + //cb.write_label(target.unwrap_label_idx()); }, // Report back the current position in the generated code @@ -518,17 +506,23 @@ impl Assembler // Set up RBP to work with frame pointer unwinding // (e.g. with Linux `perf record --call-graph fp`) Insn::FrameSetup => { + unimplemented!("frames are not supported yet"); + /* if get_option!(frame_pointer) { push(cb, RBP); mov(cb, RBP, RSP); push(cb, RBP); } + */ }, Insn::FrameTeardown => { + unimplemented!("frames are not supported yet"); + /* if get_option!(frame_pointer) { pop(cb, RBP); pop(cb, RBP); } + */ }, Insn::Add { left, right, .. } => { @@ -706,91 +700,91 @@ impl Assembler // Conditional jump to a label Insn::Jmp(target) => { - match compile_side_exit(*target, self, ocb)? { + match *target { Target::CodePtr(code_ptr) | Target::SideExitPtr(code_ptr) => jmp_ptr(cb, code_ptr), Target::Label(label_idx) => jmp_label(cb, label_idx), - Target::SideExit { .. } => unreachable!("Target::SideExit should have been compiled by compile_side_exit"), + //Target::SideExit { .. } => unreachable!("Target::SideExit should have been compiled by compile_side_exit"), } } Insn::Je(target) => { - match compile_side_exit(*target, self, ocb)? { + match *target { Target::CodePtr(code_ptr) | Target::SideExitPtr(code_ptr) => je_ptr(cb, code_ptr), Target::Label(label_idx) => je_label(cb, label_idx), - Target::SideExit { .. } => unreachable!("Target::SideExit should have been compiled by compile_side_exit"), + //Target::SideExit { .. } => unreachable!("Target::SideExit should have been compiled by compile_side_exit"), } } Insn::Jne(target) => { - match compile_side_exit(*target, self, ocb)? { + match *target { Target::CodePtr(code_ptr) | Target::SideExitPtr(code_ptr) => jne_ptr(cb, code_ptr), Target::Label(label_idx) => jne_label(cb, label_idx), - Target::SideExit { .. } => unreachable!("Target::SideExit should have been compiled by compile_side_exit"), + //Target::SideExit { .. } => unreachable!("Target::SideExit should have been compiled by compile_side_exit"), } } Insn::Jl(target) => { - match compile_side_exit(*target, self, ocb)? { + match *target { Target::CodePtr(code_ptr) | Target::SideExitPtr(code_ptr) => jl_ptr(cb, code_ptr), Target::Label(label_idx) => jl_label(cb, label_idx), - Target::SideExit { .. } => unreachable!("Target::SideExit should have been compiled by compile_side_exit"), + //Target::SideExit { .. } => unreachable!("Target::SideExit should have been compiled by compile_side_exit"), } }, Insn::Jg(target) => { - match compile_side_exit(*target, self, ocb)? { + match *target { Target::CodePtr(code_ptr) | Target::SideExitPtr(code_ptr) => jg_ptr(cb, code_ptr), Target::Label(label_idx) => jg_label(cb, label_idx), - Target::SideExit { .. } => unreachable!("Target::SideExit should have been compiled by compile_side_exit"), + //Target::SideExit { .. } => unreachable!("Target::SideExit should have been compiled by compile_side_exit"), } }, Insn::Jge(target) => { - match compile_side_exit(*target, self, ocb)? { + match *target { Target::CodePtr(code_ptr) | Target::SideExitPtr(code_ptr) => jge_ptr(cb, code_ptr), Target::Label(label_idx) => jge_label(cb, label_idx), - Target::SideExit { .. } => unreachable!("Target::SideExit should have been compiled by compile_side_exit"), + //Target::SideExit { .. } => unreachable!("Target::SideExit should have been compiled by compile_side_exit"), } }, Insn::Jbe(target) => { - match compile_side_exit(*target, self, ocb)? { + match *target { Target::CodePtr(code_ptr) | Target::SideExitPtr(code_ptr) => jbe_ptr(cb, code_ptr), Target::Label(label_idx) => jbe_label(cb, label_idx), - Target::SideExit { .. } => unreachable!("Target::SideExit should have been compiled by compile_side_exit"), + //Target::SideExit { .. } => unreachable!("Target::SideExit should have been compiled by compile_side_exit"), } }, Insn::Jb(target) => { - match compile_side_exit(*target, self, ocb)? { + match *target { Target::CodePtr(code_ptr) | Target::SideExitPtr(code_ptr) => jb_ptr(cb, code_ptr), Target::Label(label_idx) => jb_label(cb, label_idx), - Target::SideExit { .. } => unreachable!("Target::SideExit should have been compiled by compile_side_exit"), + //Target::SideExit { .. } => unreachable!("Target::SideExit should have been compiled by compile_side_exit"), } }, Insn::Jz(target) => { - match compile_side_exit(*target, self, ocb)? { + match *target { Target::CodePtr(code_ptr) | Target::SideExitPtr(code_ptr) => jz_ptr(cb, code_ptr), Target::Label(label_idx) => jz_label(cb, label_idx), - Target::SideExit { .. } => unreachable!("Target::SideExit should have been compiled by compile_side_exit"), + //Target::SideExit { .. } => unreachable!("Target::SideExit should have been compiled by compile_side_exit"), } } Insn::Jnz(target) => { - match compile_side_exit(*target, self, ocb)? { + match *target { Target::CodePtr(code_ptr) | Target::SideExitPtr(code_ptr) => jnz_ptr(cb, code_ptr), Target::Label(label_idx) => jnz_label(cb, label_idx), - Target::SideExit { .. } => unreachable!("Target::SideExit should have been compiled by compile_side_exit"), + //Target::SideExit { .. } => unreachable!("Target::SideExit should have been compiled by compile_side_exit"), } } Insn::Jo(target) | Insn::JoMul(target) => { - match compile_side_exit(*target, self, ocb)? { + match *target { Target::CodePtr(code_ptr) | Target::SideExitPtr(code_ptr) => jo_ptr(cb, code_ptr), Target::Label(label_idx) => jo_label(cb, label_idx), - Target::SideExit { .. } => unreachable!("Target::SideExit should have been compiled by compile_side_exit"), + //Target::SideExit { .. } => unreachable!("Target::SideExit should have been compiled by compile_side_exit"), } } @@ -832,17 +826,20 @@ impl Assembler } Insn::LiveReg { .. } => (), // just a reg alloc signal, no code Insn::PadInvalPatch => { + unimplemented!("we don't need padding yet"); + /* let code_size = cb.get_write_pos().saturating_sub(std::cmp::max(start_write_pos, cb.page_start_pos())); if code_size < cb.jmp_ptr_bytes() { nop(cb, (cb.jmp_ptr_bytes() - code_size) as u32); } + */ } }; // On failure, jump to the next page and retry the current insn - if !had_dropped_bytes && cb.has_dropped_bytes() && cb.next_page(src_ptr, jmp_ptr) { + if !had_dropped_bytes && cb.has_dropped_bytes() { // Reset cb states before retrying the current Insn - cb.set_label_state(old_label_state); + //cb.set_label_state(old_label_state); } else { insn_idx += 1; gc_offsets.append(&mut insn_gc_offsets); @@ -867,26 +864,27 @@ impl Assembler } /// Optimize and compile the stored instructions - pub fn compile_with_regs(self, cb: &mut CodeBlock, ocb: Option<&mut OutlinedCb>, regs: Vec<Reg>) -> Option<(CodePtr, Vec<u32>)> { + pub fn compile_with_regs(self, cb: &mut CodeBlock, regs: Vec<Reg>) -> Option<(CodePtr, Vec<u32>)> { let asm = self.x86_split(); let mut asm = asm.alloc_regs(regs); // Create label instances in the code block + /* for (idx, name) in asm.label_names.iter().enumerate() { let label_idx = cb.new_label(name.to_string()); assert!(label_idx == idx); } + */ - let mut ocb = ocb; // for &mut let start_ptr = cb.get_write_ptr(); - let gc_offsets = asm.x86_emit(cb, &mut ocb); + let gc_offsets = asm.x86_emit(cb); if let (Some(gc_offsets), false) = (gc_offsets, cb.has_dropped_bytes()) { - cb.link_labels(); + //cb.link_labels(); Some((start_ptr, gc_offsets)) } else { - cb.clear_labels(); + //cb.clear_labels(); None } diff --git a/zjit/src/lib.rs b/zjit/src/lib.rs index 60fa90d402..d84706d172 100644 --- a/zjit/src/lib.rs +++ b/zjit/src/lib.rs @@ -12,7 +12,6 @@ mod backend; mod disasm; mod options; -use backend::x86_emit; use codegen::ZJITState; use options::get_option; use crate::cruby::*; @@ -80,7 +79,7 @@ pub extern "C" fn rb_zjit_iseq_gen_entry_point(iseq: IseqPtr, _ec: EcPtr) -> *co let cb = ZJITState::get_code_block(); let start_ptr = cb.get_write_ptr(); - x86_emit(cb); + //x86_emit(cb); #[cfg(feature = "disasm")] if get_option!(dump_disasm) { diff --git a/zjit/src/utils.rs b/zjit/src/utils.rs index e69de29bb2..57ee75fdf0 100644 --- a/zjit/src/utils.rs +++ b/zjit/src/utils.rs @@ -0,0 +1,13 @@ +/// The `Into<u64>` Rust does not provide. +/// Convert to u64 with assurance that the value is preserved. +/// Currently, `usize::BITS == 64` holds for all platforms we support. +pub(crate) trait IntoU64 { + fn as_u64(self) -> u64; +} + +#[cfg(target_pointer_width = "64")] +impl IntoU64 for usize { + fn as_u64(self) -> u64 { + self as u64 + } +} |
