Implement FixnumAdd and stub PatchPoint/GuardType (https://github.com/Shopify/zjit/pull/30)
* Implement FixnumAdd and stub PatchPoint/GuardType Co-authored-by: Max Bernstein <max.bernstein@shopify.com> Co-authored-by: Maxime Chevalier-Boisvert <maxime.chevalierboisvert@shopify.com> * Clone Target for arm64 * Use $create instead of use create Co-authored-by: Alan Wu <XrXr@users.noreply.github.com> * Fix misindentation from suggested changes * Drop an unneeded variable for mut * Load operand into a register only if necessary --------- Co-authored-by: Max Bernstein <max.bernstein@shopify.com> Co-authored-by: Maxime Chevalier-Boisvert <maxime.chevalierboisvert@shopify.com> Co-authored-by: Alan Wu <XrXr@users.noreply.github.com>
This commit is contained in:
parent
bd41935b02
commit
22c73f1ccb
Notes:
git
2025-04-18 13:48:24 +00:00
4
.github/workflows/zjit-macos.yml
vendored
4
.github/workflows/zjit-macos.yml
vendored
@ -33,14 +33,14 @@ jobs:
|
|||||||
rust_version: 1.85.0
|
rust_version: 1.85.0
|
||||||
|
|
||||||
- test_task: 'btest'
|
- test_task: 'btest'
|
||||||
zjit_opts: '--zjit-call-threshold=1'
|
zjit_opts: '--zjit-call-threshold=2'
|
||||||
configure: '--enable-zjit=dev'
|
configure: '--enable-zjit=dev'
|
||||||
btests: '../src/bootstraptest/test_zjit.rb'
|
btests: '../src/bootstraptest/test_zjit.rb'
|
||||||
rust_version: 1.85.0
|
rust_version: 1.85.0
|
||||||
|
|
||||||
- test_task: 'check'
|
- test_task: 'check'
|
||||||
# Test without ZJIT for now
|
# Test without ZJIT for now
|
||||||
#zjit_opts: '--zjit-call-threshold=1'
|
#zjit_opts: '--zjit-call-threshold=2'
|
||||||
configure: '--enable-zjit'
|
configure: '--enable-zjit'
|
||||||
rust_version: 1.85.0
|
rust_version: 1.85.0
|
||||||
|
|
||||||
|
4
.github/workflows/zjit-ubuntu.yml
vendored
4
.github/workflows/zjit-ubuntu.yml
vendored
@ -39,14 +39,14 @@ jobs:
|
|||||||
rust_version: 1.85.0
|
rust_version: 1.85.0
|
||||||
|
|
||||||
- test_task: 'btest'
|
- test_task: 'btest'
|
||||||
zjit_opts: '--zjit-call-threshold=1'
|
zjit_opts: '--zjit-call-threshold=2'
|
||||||
configure: '--enable-zjit=dev'
|
configure: '--enable-zjit=dev'
|
||||||
btests: '../src/bootstraptest/test_zjit.rb'
|
btests: '../src/bootstraptest/test_zjit.rb'
|
||||||
rust_version: 1.85.0
|
rust_version: 1.85.0
|
||||||
|
|
||||||
- test_task: 'check'
|
- test_task: 'check'
|
||||||
# Test without ZJIT for now
|
# Test without ZJIT for now
|
||||||
#zjit_opts: '--zjit-call-threshold=1'
|
#zjit_opts: '--zjit-call-threshold=2'
|
||||||
configure: '--enable-zjit'
|
configure: '--enable-zjit'
|
||||||
rust_version: 1.85.0
|
rust_version: 1.85.0
|
||||||
|
|
||||||
|
@ -3,10 +3,15 @@
|
|||||||
|
|
||||||
assert_equal 'nil', %q{
|
assert_equal 'nil', %q{
|
||||||
def test = nil
|
def test = nil
|
||||||
test.inspect
|
test; test.inspect
|
||||||
}
|
}
|
||||||
|
|
||||||
assert_equal '1', %q{
|
assert_equal '1', %q{
|
||||||
def test = 1
|
def test = 1
|
||||||
test
|
test; test
|
||||||
|
}
|
||||||
|
|
||||||
|
assert_equal '3', %q{
|
||||||
|
def test = 1 + 2
|
||||||
|
test; test
|
||||||
}
|
}
|
||||||
|
1
vm.c
1
vm.c
@ -2199,6 +2199,7 @@ rb_vm_check_redefinition_opt_method(const rb_method_entry_t *me, VALUE klass)
|
|||||||
rb_id2name(me->called_id)
|
rb_id2name(me->called_id)
|
||||||
);
|
);
|
||||||
rb_yjit_bop_redefined(flag, (enum ruby_basic_operators)bop);
|
rb_yjit_bop_redefined(flag, (enum ruby_basic_operators)bop);
|
||||||
|
rb_zjit_bop_redefined(flag, (enum ruby_basic_operators)bop);
|
||||||
ruby_vm_redefined_flag[bop] |= flag;
|
ruby_vm_redefined_flag[bop] |= flag;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
2
zjit.h
2
zjit.h
@ -10,10 +10,12 @@ extern uint64_t rb_zjit_call_threshold;
|
|||||||
void rb_zjit_compile_iseq(const rb_iseq_t *iseq, rb_execution_context_t *ec, bool jit_exception);
|
void rb_zjit_compile_iseq(const rb_iseq_t *iseq, rb_execution_context_t *ec, bool jit_exception);
|
||||||
void rb_zjit_profile_insn(enum ruby_vminsn_type insn, rb_execution_context_t *ec);
|
void rb_zjit_profile_insn(enum ruby_vminsn_type insn, rb_execution_context_t *ec);
|
||||||
void rb_zjit_profile_iseq(const rb_iseq_t *iseq);
|
void rb_zjit_profile_iseq(const rb_iseq_t *iseq);
|
||||||
|
void rb_zjit_bop_redefined(int redefined_flag, enum ruby_basic_operators bop);
|
||||||
#else
|
#else
|
||||||
void rb_zjit_compile_iseq(const rb_iseq_t *iseq, rb_execution_context_t *ec, bool jit_exception) {}
|
void rb_zjit_compile_iseq(const rb_iseq_t *iseq, rb_execution_context_t *ec, bool jit_exception) {}
|
||||||
void rb_zjit_profile_insn(enum ruby_vminsn_type insn, rb_execution_context_t *ec) {}
|
void rb_zjit_profile_insn(enum ruby_vminsn_type insn, rb_execution_context_t *ec) {}
|
||||||
void rb_zjit_profile_iseq(const rb_iseq_t *iseq) {}
|
void rb_zjit_profile_iseq(const rb_iseq_t *iseq) {}
|
||||||
|
void rb_zjit_bop_redefined(int redefined_flag, enum ruby_basic_operators bop) {}
|
||||||
#endif // #if USE_YJIT
|
#endif // #if USE_YJIT
|
||||||
|
|
||||||
#endif // #ifndef ZJIT_H
|
#endif // #ifndef ZJIT_H
|
||||||
|
@ -878,10 +878,7 @@ pub fn jmp_label(cb: &mut CodeBlock, label_idx: usize) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Encode a relative jump to a pointer at a 32-bit offset (direct or conditional)
|
/// Encode a relative jump to a pointer at a 32-bit offset (direct or conditional)
|
||||||
fn write_jcc_ptr(_cb: &mut CodeBlock, _op0: u8, _op1: u8, _dst_ptr: CodePtr) {
|
fn write_jcc_ptr(cb: &mut CodeBlock, op0: u8, op1: u8, dst_ptr: CodePtr) {
|
||||||
todo!();
|
|
||||||
|
|
||||||
/*
|
|
||||||
// Write the opcode
|
// Write the opcode
|
||||||
if op0 != 0xFF {
|
if op0 != 0xFF {
|
||||||
cb.write_byte(op0);
|
cb.write_byte(op0);
|
||||||
@ -904,7 +901,6 @@ fn write_jcc_ptr(_cb: &mut CodeBlock, _op0: u8, _op1: u8, _dst_ptr: CodePtr) {
|
|||||||
//cb.dropped_bytes = true;
|
//cb.dropped_bytes = true;
|
||||||
panic!("we should refactor to avoid dropped_bytes");
|
panic!("we should refactor to avoid dropped_bytes");
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// jcc - relative jumps to a pointer (32-bit offset)
|
/// jcc - relative jumps to a pointer (32-bit offset)
|
||||||
|
@ -843,9 +843,9 @@ impl Assembler
|
|||||||
bcond(cb, CONDITION, InstructionOffset::from_bytes(bytes));
|
bcond(cb, CONDITION, InstructionOffset::from_bytes(bytes));
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
//Target::SideExit { .. } => {
|
Target::SideExit { .. } => {
|
||||||
// unreachable!("Target::SideExit should have been compiled by compile_side_exit")
|
unreachable!("Target::SideExit should have been compiled by compile_side_exits")
|
||||||
//},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -900,23 +900,6 @@ impl Assembler
|
|||||||
ldr_post(cb, opnd, A64Opnd::new_mem(64, C_SP_REG, C_SP_STEP));
|
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,
|
|
||||||
asm: &mut Assembler,
|
|
||||||
ocb: &mut Option<&mut OutlinedCb>,
|
|
||||||
) -> Result<Target, EmitError> {
|
|
||||||
if let Target::SideExit { counter, context } = target {
|
|
||||||
let side_exit = asm.get_side_exit(&context.unwrap(), Some(counter), ocb.as_mut().unwrap())
|
|
||||||
.ok_or(EmitError::OutOfMemory)?;
|
|
||||||
Ok(Target::SideExitPtr(side_exit))
|
|
||||||
} else {
|
|
||||||
Ok(target)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
// dbg!(&self.insns);
|
// dbg!(&self.insns);
|
||||||
|
|
||||||
// List of GC offsets
|
// List of GC offsets
|
||||||
@ -1220,40 +1203,40 @@ impl Assembler
|
|||||||
b(cb, InstructionOffset::from_bytes(bytes));
|
b(cb, InstructionOffset::from_bytes(bytes));
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
//Target::SideExit { .. } => {
|
Target::SideExit { .. } => {
|
||||||
// unreachable!("Target::SideExit should have been compiled by compile_side_exit")
|
unreachable!("Target::SideExit should have been compiled by compile_side_exits")
|
||||||
//},
|
},
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
Insn::Je(target) | Insn::Jz(target) => {
|
Insn::Je(target) | Insn::Jz(target) => {
|
||||||
emit_conditional_jump::<{Condition::EQ}>(cb, *target);
|
emit_conditional_jump::<{Condition::EQ}>(cb, target.clone());
|
||||||
},
|
},
|
||||||
Insn::Jne(target) | Insn::Jnz(target) | Insn::JoMul(target) => {
|
Insn::Jne(target) | Insn::Jnz(target) | Insn::JoMul(target) => {
|
||||||
emit_conditional_jump::<{Condition::NE}>(cb, *target);
|
emit_conditional_jump::<{Condition::NE}>(cb, target.clone());
|
||||||
},
|
},
|
||||||
Insn::Jl(target) => {
|
Insn::Jl(target) => {
|
||||||
emit_conditional_jump::<{Condition::LT}>(cb, *target);
|
emit_conditional_jump::<{Condition::LT}>(cb, target.clone());
|
||||||
},
|
},
|
||||||
Insn::Jg(target) => {
|
Insn::Jg(target) => {
|
||||||
emit_conditional_jump::<{Condition::GT}>(cb, *target);
|
emit_conditional_jump::<{Condition::GT}>(cb, target.clone());
|
||||||
},
|
},
|
||||||
Insn::Jge(target) => {
|
Insn::Jge(target) => {
|
||||||
emit_conditional_jump::<{Condition::GE}>(cb, *target);
|
emit_conditional_jump::<{Condition::GE}>(cb, target.clone());
|
||||||
},
|
},
|
||||||
Insn::Jbe(target) => {
|
Insn::Jbe(target) => {
|
||||||
emit_conditional_jump::<{Condition::LS}>(cb, *target);
|
emit_conditional_jump::<{Condition::LS}>(cb, target.clone());
|
||||||
},
|
},
|
||||||
Insn::Jb(target) => {
|
Insn::Jb(target) => {
|
||||||
emit_conditional_jump::<{Condition::CC}>(cb, *target);
|
emit_conditional_jump::<{Condition::CC}>(cb, target.clone());
|
||||||
},
|
},
|
||||||
Insn::Jo(target) => {
|
Insn::Jo(target) => {
|
||||||
emit_conditional_jump::<{Condition::VS}>(cb, *target);
|
emit_conditional_jump::<{Condition::VS}>(cb, target.clone());
|
||||||
},
|
},
|
||||||
Insn::Joz(opnd, target) => {
|
Insn::Joz(opnd, target) => {
|
||||||
emit_cmp_zero_jump(cb, opnd.into(), true, *target);
|
emit_cmp_zero_jump(cb, opnd.into(), true, target.clone());
|
||||||
},
|
},
|
||||||
Insn::Jonz(opnd, target) => {
|
Insn::Jonz(opnd, target) => {
|
||||||
emit_cmp_zero_jump(cb, opnd.into(), false, *target);
|
emit_cmp_zero_jump(cb, opnd.into(), false, target.clone());
|
||||||
},
|
},
|
||||||
Insn::IncrCounter { mem: _, value: _ } => {
|
Insn::IncrCounter { mem: _, value: _ } => {
|
||||||
/*
|
/*
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::mem::take;
|
use std::mem::take;
|
||||||
use crate::cruby::VALUE;
|
use crate::{cruby::VALUE, hir::FrameState};
|
||||||
use crate::backend::current::*;
|
use crate::backend::current::*;
|
||||||
use crate::virtualmem::CodePtr;
|
use crate::virtualmem::CodePtr;
|
||||||
use crate::asm::CodeBlock;
|
use crate::asm::CodeBlock;
|
||||||
@ -290,13 +290,13 @@ impl From<VALUE> for Opnd {
|
|||||||
|
|
||||||
/// Branch target (something that we can jump to)
|
/// Branch target (something that we can jump to)
|
||||||
/// for branch instructions
|
/// for branch instructions
|
||||||
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub enum Target
|
pub enum Target
|
||||||
{
|
{
|
||||||
/// Pointer to a piece of YJIT-generated code
|
/// Pointer to a piece of YJIT-generated code
|
||||||
CodePtr(CodePtr),
|
CodePtr(CodePtr),
|
||||||
// Side exit with a counter
|
// Side exit with a counter
|
||||||
//SideExit { counter: Counter, context: Option<SideExitContext> },
|
SideExit(FrameState),
|
||||||
/// Pointer to a side exit code
|
/// Pointer to a side exit code
|
||||||
SideExitPtr(CodePtr),
|
SideExitPtr(CodePtr),
|
||||||
/// A label within the generated code
|
/// A label within the generated code
|
||||||
@ -305,12 +305,6 @@ pub enum Target
|
|||||||
|
|
||||||
impl Target
|
impl Target
|
||||||
{
|
{
|
||||||
/*
|
|
||||||
pub fn side_exit(counter: Counter) -> Target {
|
|
||||||
Target::SideExit { counter, context: None }
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
pub fn unwrap_label_idx(&self) -> usize {
|
pub fn unwrap_label_idx(&self) -> usize {
|
||||||
match self {
|
match self {
|
||||||
Target::Label(idx) => *idx,
|
Target::Label(idx) => *idx,
|
||||||
@ -966,56 +960,6 @@ 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
|
|
||||||
pub pc: *mut VALUE,
|
|
||||||
|
|
||||||
/// Context fields used by get_generic_ctx()
|
|
||||||
pub stack_size: u8,
|
|
||||||
pub sp_offset: i8,
|
|
||||||
pub reg_mapping: RegMapping,
|
|
||||||
pub is_return_landing: bool,
|
|
||||||
pub is_deferred: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl SideExitContext {
|
|
||||||
/// Convert PC and Context into SideExitContext
|
|
||||||
pub fn new(pc: *mut VALUE, ctx: Context) -> Self {
|
|
||||||
let exit_ctx = SideExitContext {
|
|
||||||
pc,
|
|
||||||
stack_size: ctx.get_stack_size(),
|
|
||||||
sp_offset: ctx.get_sp_offset(),
|
|
||||||
reg_mapping: ctx.get_reg_mapping(),
|
|
||||||
is_return_landing: ctx.is_return_landing(),
|
|
||||||
is_deferred: ctx.is_deferred(),
|
|
||||||
};
|
|
||||||
if cfg!(debug_assertions) {
|
|
||||||
// Assert that we're not losing any mandatory metadata
|
|
||||||
assert_eq!(exit_ctx.get_ctx(), ctx.get_generic_ctx());
|
|
||||||
}
|
|
||||||
exit_ctx
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Convert SideExitContext to Context
|
|
||||||
fn get_ctx(&self) -> Context {
|
|
||||||
let mut ctx = Context::default();
|
|
||||||
ctx.set_stack_size(self.stack_size);
|
|
||||||
ctx.set_sp_offset(self.sp_offset);
|
|
||||||
ctx.set_reg_mapping(self.reg_mapping);
|
|
||||||
if self.is_return_landing {
|
|
||||||
ctx.set_as_return_landing();
|
|
||||||
}
|
|
||||||
if self.is_deferred {
|
|
||||||
ctx.mark_as_deferred();
|
|
||||||
}
|
|
||||||
ctx
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
/// Initial capacity for asm.insns vector
|
/// Initial capacity for asm.insns vector
|
||||||
const ASSEMBLER_INSNS_CAPACITY: usize = 256;
|
const ASSEMBLER_INSNS_CAPACITY: usize = 256;
|
||||||
|
|
||||||
@ -1155,19 +1099,6 @@ impl Assembler
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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() {
|
|
||||||
*context = Some(SideExitContext::new(
|
|
||||||
self.side_exit_pc.unwrap(),
|
|
||||||
self.ctx.with_stack_size(self.side_exit_stack_size.unwrap()),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
self.insns.push(insn);
|
self.insns.push(insn);
|
||||||
self.live_ranges.push(insn_idx);
|
self.live_ranges.push(insn_idx);
|
||||||
}
|
}
|
||||||
@ -1635,8 +1566,10 @@ impl Assembler
|
|||||||
/// Compile the instructions down to machine code.
|
/// Compile the instructions down to machine code.
|
||||||
/// Can fail due to lack of code memory and inopportune code placement, among other reasons.
|
/// Can fail due to lack of code memory and inopportune code placement, among other reasons.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn compile(self, cb: &mut CodeBlock) -> Option<(CodePtr, Vec<u32>)>
|
pub fn compile(mut self, cb: &mut CodeBlock) -> Option<(CodePtr, Vec<u32>)>
|
||||||
{
|
{
|
||||||
|
self.compile_side_exits(cb)?;
|
||||||
|
|
||||||
#[cfg(feature = "disasm")]
|
#[cfg(feature = "disasm")]
|
||||||
let start_addr = cb.get_write_ptr();
|
let start_addr = cb.get_write_ptr();
|
||||||
let alloc_regs = Self::get_alloc_regs();
|
let alloc_regs = Self::get_alloc_regs();
|
||||||
@ -1651,6 +1584,29 @@ impl Assembler
|
|||||||
ret
|
ret
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Compile Target::SideExit and convert it into Target::CodePtr for all instructions
|
||||||
|
#[must_use]
|
||||||
|
pub fn compile_side_exits(&mut self, cb: &mut CodeBlock) -> Option<()> {
|
||||||
|
for insn in self.insns.iter_mut() {
|
||||||
|
if let Some(target) = insn.target_mut() {
|
||||||
|
if let Target::SideExit(state) = target {
|
||||||
|
let side_exit_ptr = cb.get_write_ptr();
|
||||||
|
let mut asm = Assembler::new();
|
||||||
|
asm_comment!(asm, "side exit: {:?}", state);
|
||||||
|
asm.ccall(Self::rb_zjit_side_exit as *const u8, vec![]);
|
||||||
|
asm.compile(cb)?;
|
||||||
|
*target = Target::CodePtr(side_exit_ptr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Some(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[unsafe(no_mangle)]
|
||||||
|
extern "C" fn rb_zjit_side_exit() {
|
||||||
|
unimplemented!("side exits are not implemented yet");
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
/// Compile with a limited number of registers. Used only for unit tests.
|
/// Compile with a limited number of registers. Used only for unit tests.
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
@ -696,7 +696,7 @@ impl Assembler
|
|||||||
match *target {
|
match *target {
|
||||||
Target::CodePtr(code_ptr) | Target::SideExitPtr(code_ptr) => jmp_ptr(cb, code_ptr),
|
Target::CodePtr(code_ptr) | Target::SideExitPtr(code_ptr) => jmp_ptr(cb, code_ptr),
|
||||||
Target::Label(label_idx) => jmp_label(cb, label_idx),
|
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_exits"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -704,7 +704,7 @@ impl Assembler
|
|||||||
match *target {
|
match *target {
|
||||||
Target::CodePtr(code_ptr) | Target::SideExitPtr(code_ptr) => je_ptr(cb, code_ptr),
|
Target::CodePtr(code_ptr) | Target::SideExitPtr(code_ptr) => je_ptr(cb, code_ptr),
|
||||||
Target::Label(label_idx) => je_label(cb, label_idx),
|
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_exits"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -712,7 +712,7 @@ impl Assembler
|
|||||||
match *target {
|
match *target {
|
||||||
Target::CodePtr(code_ptr) | Target::SideExitPtr(code_ptr) => jne_ptr(cb, code_ptr),
|
Target::CodePtr(code_ptr) | Target::SideExitPtr(code_ptr) => jne_ptr(cb, code_ptr),
|
||||||
Target::Label(label_idx) => jne_label(cb, label_idx),
|
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_exits"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -720,7 +720,7 @@ impl Assembler
|
|||||||
match *target {
|
match *target {
|
||||||
Target::CodePtr(code_ptr) | Target::SideExitPtr(code_ptr) => jl_ptr(cb, code_ptr),
|
Target::CodePtr(code_ptr) | Target::SideExitPtr(code_ptr) => jl_ptr(cb, code_ptr),
|
||||||
Target::Label(label_idx) => jl_label(cb, label_idx),
|
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_exits"),
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -728,7 +728,7 @@ impl Assembler
|
|||||||
match *target {
|
match *target {
|
||||||
Target::CodePtr(code_ptr) | Target::SideExitPtr(code_ptr) => jg_ptr(cb, code_ptr),
|
Target::CodePtr(code_ptr) | Target::SideExitPtr(code_ptr) => jg_ptr(cb, code_ptr),
|
||||||
Target::Label(label_idx) => jg_label(cb, label_idx),
|
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_exits"),
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -736,7 +736,7 @@ impl Assembler
|
|||||||
match *target {
|
match *target {
|
||||||
Target::CodePtr(code_ptr) | Target::SideExitPtr(code_ptr) => jge_ptr(cb, code_ptr),
|
Target::CodePtr(code_ptr) | Target::SideExitPtr(code_ptr) => jge_ptr(cb, code_ptr),
|
||||||
Target::Label(label_idx) => jge_label(cb, label_idx),
|
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_exits"),
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -744,7 +744,7 @@ impl Assembler
|
|||||||
match *target {
|
match *target {
|
||||||
Target::CodePtr(code_ptr) | Target::SideExitPtr(code_ptr) => jbe_ptr(cb, code_ptr),
|
Target::CodePtr(code_ptr) | Target::SideExitPtr(code_ptr) => jbe_ptr(cb, code_ptr),
|
||||||
Target::Label(label_idx) => jbe_label(cb, label_idx),
|
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_exits"),
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -752,7 +752,7 @@ impl Assembler
|
|||||||
match *target {
|
match *target {
|
||||||
Target::CodePtr(code_ptr) | Target::SideExitPtr(code_ptr) => jb_ptr(cb, code_ptr),
|
Target::CodePtr(code_ptr) | Target::SideExitPtr(code_ptr) => jb_ptr(cb, code_ptr),
|
||||||
Target::Label(label_idx) => jb_label(cb, label_idx),
|
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_exits"),
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -760,7 +760,7 @@ impl Assembler
|
|||||||
match *target {
|
match *target {
|
||||||
Target::CodePtr(code_ptr) | Target::SideExitPtr(code_ptr) => jz_ptr(cb, code_ptr),
|
Target::CodePtr(code_ptr) | Target::SideExitPtr(code_ptr) => jz_ptr(cb, code_ptr),
|
||||||
Target::Label(label_idx) => jz_label(cb, label_idx),
|
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_exits"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -768,7 +768,7 @@ impl Assembler
|
|||||||
match *target {
|
match *target {
|
||||||
Target::CodePtr(code_ptr) | Target::SideExitPtr(code_ptr) => jnz_ptr(cb, code_ptr),
|
Target::CodePtr(code_ptr) | Target::SideExitPtr(code_ptr) => jnz_ptr(cb, code_ptr),
|
||||||
Target::Label(label_idx) => jnz_label(cb, label_idx),
|
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_exits"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -777,7 +777,7 @@ impl Assembler
|
|||||||
match *target {
|
match *target {
|
||||||
Target::CodePtr(code_ptr) | Target::SideExitPtr(code_ptr) => jo_ptr(cb, code_ptr),
|
Target::CodePtr(code_ptr) | Target::SideExitPtr(code_ptr) => jo_ptr(cb, code_ptr),
|
||||||
Target::Label(label_idx) => jo_label(cb, label_idx),
|
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_exits"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,13 +1,6 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
asm::CodeBlock,
|
asm::CodeBlock, backend::lir::{asm_comment, Assembler, Opnd, Target, CFP, C_ARG_OPNDS, EC, SP}, cruby::*, debug, hir::{Const, FrameState, Function, Insn, InsnId}, hir_type::{types::Fixnum, Type}, virtualmem::CodePtr
|
||||||
backend::lir::{EC, CFP, SP, C_ARG_OPNDS, Assembler, Opnd, asm_comment},
|
|
||||||
cruby::*,
|
|
||||||
debug,
|
|
||||||
hir::{Function, InsnId, Insn, Const},
|
|
||||||
virtualmem::CodePtr
|
|
||||||
};
|
};
|
||||||
#[cfg(feature = "disasm")]
|
|
||||||
use crate::get_option;
|
|
||||||
|
|
||||||
/// Ephemeral code generation state
|
/// Ephemeral code generation state
|
||||||
struct JITState {
|
struct JITState {
|
||||||
@ -40,15 +33,19 @@ pub fn gen_function(cb: &mut CodeBlock, function: &Function, iseq: IseqPtr) -> O
|
|||||||
if !matches!(*insn, Insn::Snapshot { .. }) {
|
if !matches!(*insn, Insn::Snapshot { .. }) {
|
||||||
asm_comment!(asm, "Insn: {:04} {:?}", insn_idx, insn);
|
asm_comment!(asm, "Insn: {:04} {:?}", insn_idx, insn);
|
||||||
}
|
}
|
||||||
match *insn {
|
match insn {
|
||||||
Insn::Const { val: Const::Value(val) } => gen_const(&mut jit, insn_id, val),
|
Insn::Const { val: Const::Value(val) } => gen_const(&mut jit, insn_id, *val),
|
||||||
Insn::Return { val } => gen_return(&jit, &mut asm, val)?,
|
|
||||||
Insn::Snapshot { .. } => {}, // we don't need to do anything for this instruction at the moment
|
Insn::Snapshot { .. } => {}, // we don't need to do anything for this instruction at the moment
|
||||||
|
Insn::Return { val } => gen_return(&jit, &mut asm, *val)?,
|
||||||
|
Insn::FixnumAdd { left, right, state } => gen_fixnum_add(&mut jit, &mut asm, insn_id, *left, *right, state)?,
|
||||||
|
Insn::GuardType { val, guard_type, state } => gen_guard_type(&mut jit, &mut asm, insn_id, *val, *guard_type, state)?,
|
||||||
|
Insn::PatchPoint(_) => {}, // For now, rb_zjit_bop_redefined() panics. TODO: leave a patch point and fix rb_zjit_bop_redefined()
|
||||||
_ => {
|
_ => {
|
||||||
debug!("ZJIT: gen_function: unexpected insn {:?}", insn);
|
debug!("ZJIT: gen_function: unexpected insn {:?}", insn);
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
debug!("Compiled insn: {:04} {:?}", insn_idx, insn);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generate code if everything can be compiled
|
// Generate code if everything can be compiled
|
||||||
@ -105,3 +102,44 @@ fn gen_return(jit: &JITState, asm: &mut Assembler, val: InsnId) -> Option<()> {
|
|||||||
|
|
||||||
Some(())
|
Some(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Compile Fixnum + Fixnum
|
||||||
|
fn gen_fixnum_add(jit: &mut JITState, asm: &mut Assembler, insn_id: InsnId, left: InsnId, right: InsnId, state: &FrameState) -> Option<()> {
|
||||||
|
let left_opnd = jit.opnds[left.0]?;
|
||||||
|
let right_opnd = jit.opnds[right.0]?;
|
||||||
|
|
||||||
|
// Load left into a register if left is a constant. The backend doesn't support sub(imm, imm).
|
||||||
|
let left_reg = match left_opnd {
|
||||||
|
Opnd::Value(_) => asm.load(left_opnd),
|
||||||
|
_ => left_opnd,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Add arg0 + arg1 and test for overflow
|
||||||
|
let left_untag = asm.sub(left_reg, Opnd::Imm(1));
|
||||||
|
let out_val = asm.add(left_untag, right_opnd);
|
||||||
|
asm.jo(Target::SideExit(state.clone()));
|
||||||
|
|
||||||
|
jit.opnds[insn_id.0] = Some(out_val);
|
||||||
|
Some(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Compile a type check with a side exit
|
||||||
|
fn gen_guard_type(jit: &mut JITState, asm: &mut Assembler, insn_id: InsnId, val: InsnId, guard_type: Type, state: &FrameState) -> Option<()> {
|
||||||
|
let opnd = jit.opnds[val.0]?;
|
||||||
|
if guard_type.is_subtype(Fixnum) {
|
||||||
|
// Load opnd into a register if opnd is a constant. The backend doesn't support test(imm, imm) yet.
|
||||||
|
let opnd_reg = match opnd {
|
||||||
|
Opnd::Value(_) => asm.load(opnd),
|
||||||
|
_ => opnd,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Check if opnd is Fixnum
|
||||||
|
asm.test(opnd_reg, Opnd::UImm(RUBY_FIXNUM_FLAG as u64));
|
||||||
|
asm.jz(Target::SideExit(state.clone()));
|
||||||
|
} else {
|
||||||
|
unimplemented!("unsupported type: {guard_type}");
|
||||||
|
}
|
||||||
|
|
||||||
|
jit.opnds[insn_id.0] = Some(opnd);
|
||||||
|
Some(())
|
||||||
|
}
|
||||||
|
@ -192,21 +192,21 @@ pub enum Insn {
|
|||||||
Return { val: InsnId },
|
Return { val: InsnId },
|
||||||
|
|
||||||
/// Fixnum +, -, *, /, %, ==, !=, <, <=, >, >=
|
/// Fixnum +, -, *, /, %, ==, !=, <, <=, >, >=
|
||||||
FixnumAdd { left: InsnId, right: InsnId },
|
FixnumAdd { left: InsnId, right: InsnId, state: FrameState },
|
||||||
FixnumSub { left: InsnId, right: InsnId },
|
FixnumSub { left: InsnId, right: InsnId, state: FrameState },
|
||||||
FixnumMult { left: InsnId, right: InsnId },
|
FixnumMult { left: InsnId, right: InsnId, state: FrameState },
|
||||||
FixnumDiv { left: InsnId, right: InsnId },
|
FixnumDiv { left: InsnId, right: InsnId, state: FrameState },
|
||||||
FixnumMod { left: InsnId, right: InsnId },
|
FixnumMod { left: InsnId, right: InsnId, state: FrameState },
|
||||||
FixnumEq { left: InsnId, right: InsnId },
|
FixnumEq { left: InsnId, right: InsnId, state: FrameState },
|
||||||
FixnumNeq { left: InsnId, right: InsnId },
|
FixnumNeq { left: InsnId, right: InsnId, state: FrameState },
|
||||||
FixnumLt { left: InsnId, right: InsnId },
|
FixnumLt { left: InsnId, right: InsnId, state: FrameState },
|
||||||
FixnumLe { left: InsnId, right: InsnId },
|
FixnumLe { left: InsnId, right: InsnId, state: FrameState },
|
||||||
FixnumGt { left: InsnId, right: InsnId },
|
FixnumGt { left: InsnId, right: InsnId, state: FrameState },
|
||||||
FixnumGe { left: InsnId, right: InsnId },
|
FixnumGe { left: InsnId, right: InsnId, state: FrameState },
|
||||||
|
|
||||||
/// Side-exist if val doesn't have the expected type.
|
/// Side-exist if val doesn't have the expected type.
|
||||||
// TODO: Replace is_fixnum with the type lattice
|
// TODO: Replace is_fixnum with the type lattice
|
||||||
GuardType { val: InsnId, guard_type: Type },
|
GuardType { val: InsnId, guard_type: Type, state: FrameState },
|
||||||
|
|
||||||
/// Generate no code (or padding if necessary) and insert a patch point
|
/// Generate no code (or padding if necessary) and insert a patch point
|
||||||
/// that can be rewritten to a side exit when the Invariant is broken.
|
/// that can be rewritten to a side exit when the Invariant is broken.
|
||||||
@ -422,18 +422,18 @@ impl<'a> std::fmt::Display for FunctionPrinter<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
Insn::Return { val } => { write!(f, "Return {val}")?; }
|
Insn::Return { val } => { write!(f, "Return {val}")?; }
|
||||||
Insn::FixnumAdd { left, right } => { write!(f, "FixnumAdd {left}, {right}")?; },
|
Insn::FixnumAdd { left, right, .. } => { write!(f, "FixnumAdd {left}, {right}")?; },
|
||||||
Insn::FixnumSub { left, right } => { write!(f, "FixnumSub {left}, {right}")?; },
|
Insn::FixnumSub { left, right, .. } => { write!(f, "FixnumSub {left}, {right}")?; },
|
||||||
Insn::FixnumMult { left, right } => { write!(f, "FixnumMult {left}, {right}")?; },
|
Insn::FixnumMult { left, right, .. } => { write!(f, "FixnumMult {left}, {right}")?; },
|
||||||
Insn::FixnumDiv { left, right } => { write!(f, "FixnumDiv {left}, {right}")?; },
|
Insn::FixnumDiv { left, right, .. } => { write!(f, "FixnumDiv {left}, {right}")?; },
|
||||||
Insn::FixnumMod { left, right } => { write!(f, "FixnumMod {left}, {right}")?; },
|
Insn::FixnumMod { left, right, .. } => { write!(f, "FixnumMod {left}, {right}")?; },
|
||||||
Insn::FixnumEq { left, right } => { write!(f, "FixnumEq {left}, {right}")?; },
|
Insn::FixnumEq { left, right, .. } => { write!(f, "FixnumEq {left}, {right}")?; },
|
||||||
Insn::FixnumNeq { left, right } => { write!(f, "FixnumNeq {left}, {right}")?; },
|
Insn::FixnumNeq { left, right, .. } => { write!(f, "FixnumNeq {left}, {right}")?; },
|
||||||
Insn::FixnumLt { left, right } => { write!(f, "FixnumLt {left}, {right}")?; },
|
Insn::FixnumLt { left, right, .. } => { write!(f, "FixnumLt {left}, {right}")?; },
|
||||||
Insn::FixnumLe { left, right } => { write!(f, "FixnumLe {left}, {right}")?; },
|
Insn::FixnumLe { left, right, .. } => { write!(f, "FixnumLe {left}, {right}")?; },
|
||||||
Insn::FixnumGt { left, right } => { write!(f, "FixnumGt {left}, {right}")?; },
|
Insn::FixnumGt { left, right, .. } => { write!(f, "FixnumGt {left}, {right}")?; },
|
||||||
Insn::FixnumGe { left, right } => { write!(f, "FixnumGe {left}, {right}")?; },
|
Insn::FixnumGe { left, right, .. } => { write!(f, "FixnumGe {left}, {right}")?; },
|
||||||
Insn::GuardType { val, guard_type } => { write!(f, "GuardType {val}, {guard_type}")?; },
|
Insn::GuardType { val, guard_type, .. } => { write!(f, "GuardType {val}, {guard_type}")?; },
|
||||||
Insn::PatchPoint(invariant) => { write!(f, "PatchPoint {invariant:}")?; },
|
Insn::PatchPoint(invariant) => { write!(f, "PatchPoint {invariant:}")?; },
|
||||||
insn => { write!(f, "{insn:?}")?; }
|
insn => { write!(f, "{insn:?}")?; }
|
||||||
}
|
}
|
||||||
@ -448,7 +448,7 @@ impl<'a> std::fmt::Display for FunctionPrinter<'a> {
|
|||||||
pub struct FrameState {
|
pub struct FrameState {
|
||||||
iseq: IseqPtr,
|
iseq: IseqPtr,
|
||||||
// Ruby bytecode instruction pointer
|
// Ruby bytecode instruction pointer
|
||||||
pc: VALUE,
|
pub pc: VALUE,
|
||||||
|
|
||||||
stack: Vec<InsnId>,
|
stack: Vec<InsnId>,
|
||||||
locals: Vec<InsnId>,
|
locals: Vec<InsnId>,
|
||||||
@ -769,8 +769,9 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result<Function, ParseError> {
|
|||||||
YARVINSN_opt_plus | YARVINSN_zjit_opt_plus => {
|
YARVINSN_opt_plus | YARVINSN_zjit_opt_plus => {
|
||||||
if payload.have_two_fixnums(current_insn_idx as usize) {
|
if payload.have_two_fixnums(current_insn_idx as usize) {
|
||||||
fun.push_insn(block, Insn::PatchPoint(Invariant::BOPRedefined { klass: INTEGER_REDEFINED_OP_FLAG, bop: BOP_PLUS }));
|
fun.push_insn(block, Insn::PatchPoint(Invariant::BOPRedefined { klass: INTEGER_REDEFINED_OP_FLAG, bop: BOP_PLUS }));
|
||||||
|
let exit_state = state.clone();
|
||||||
let (left, right) = guard_two_fixnums(&mut state, &mut fun, block)?;
|
let (left, right) = guard_two_fixnums(&mut state, &mut fun, block)?;
|
||||||
state.push(fun.push_insn(block, Insn::FixnumAdd { left, right }));
|
state.push(fun.push_insn(block, Insn::FixnumAdd { left, right, state: exit_state }));
|
||||||
} else {
|
} else {
|
||||||
let right = state.pop()?;
|
let right = state.pop()?;
|
||||||
let left = state.pop()?;
|
let left = state.pop()?;
|
||||||
@ -780,8 +781,9 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result<Function, ParseError> {
|
|||||||
YARVINSN_opt_minus | YARVINSN_zjit_opt_minus => {
|
YARVINSN_opt_minus | YARVINSN_zjit_opt_minus => {
|
||||||
if payload.have_two_fixnums(current_insn_idx as usize) {
|
if payload.have_two_fixnums(current_insn_idx as usize) {
|
||||||
fun.push_insn(block, Insn::PatchPoint(Invariant::BOPRedefined { klass: INTEGER_REDEFINED_OP_FLAG, bop: BOP_MINUS }));
|
fun.push_insn(block, Insn::PatchPoint(Invariant::BOPRedefined { klass: INTEGER_REDEFINED_OP_FLAG, bop: BOP_MINUS }));
|
||||||
|
let exit_state = state.clone();
|
||||||
let (left, right) = guard_two_fixnums(&mut state, &mut fun, block)?;
|
let (left, right) = guard_two_fixnums(&mut state, &mut fun, block)?;
|
||||||
state.push(fun.push_insn(block, Insn::FixnumSub { left, right }));
|
state.push(fun.push_insn(block, Insn::FixnumSub { left, right, state: exit_state }));
|
||||||
} else {
|
} else {
|
||||||
let right = state.pop()?;
|
let right = state.pop()?;
|
||||||
let left = state.pop()?;
|
let left = state.pop()?;
|
||||||
@ -791,8 +793,9 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result<Function, ParseError> {
|
|||||||
YARVINSN_opt_mult | YARVINSN_zjit_opt_mult => {
|
YARVINSN_opt_mult | YARVINSN_zjit_opt_mult => {
|
||||||
if payload.have_two_fixnums(current_insn_idx as usize) {
|
if payload.have_two_fixnums(current_insn_idx as usize) {
|
||||||
fun.push_insn(block, Insn::PatchPoint(Invariant::BOPRedefined { klass: INTEGER_REDEFINED_OP_FLAG, bop: BOP_MULT }));
|
fun.push_insn(block, Insn::PatchPoint(Invariant::BOPRedefined { klass: INTEGER_REDEFINED_OP_FLAG, bop: BOP_MULT }));
|
||||||
|
let exit_state = state.clone();
|
||||||
let (left, right) = guard_two_fixnums(&mut state, &mut fun, block)?;
|
let (left, right) = guard_two_fixnums(&mut state, &mut fun, block)?;
|
||||||
state.push(fun.push_insn(block, Insn::FixnumMult { left, right }));
|
state.push(fun.push_insn(block, Insn::FixnumMult { left, right, state: exit_state }));
|
||||||
} else {
|
} else {
|
||||||
let right = state.pop()?;
|
let right = state.pop()?;
|
||||||
let left = state.pop()?;
|
let left = state.pop()?;
|
||||||
@ -802,8 +805,9 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result<Function, ParseError> {
|
|||||||
YARVINSN_opt_div | YARVINSN_zjit_opt_div => {
|
YARVINSN_opt_div | YARVINSN_zjit_opt_div => {
|
||||||
if payload.have_two_fixnums(current_insn_idx as usize) {
|
if payload.have_two_fixnums(current_insn_idx as usize) {
|
||||||
fun.push_insn(block, Insn::PatchPoint(Invariant::BOPRedefined { klass: INTEGER_REDEFINED_OP_FLAG, bop: BOP_DIV }));
|
fun.push_insn(block, Insn::PatchPoint(Invariant::BOPRedefined { klass: INTEGER_REDEFINED_OP_FLAG, bop: BOP_DIV }));
|
||||||
|
let exit_state = state.clone();
|
||||||
let (left, right) = guard_two_fixnums(&mut state, &mut fun, block)?;
|
let (left, right) = guard_two_fixnums(&mut state, &mut fun, block)?;
|
||||||
state.push(fun.push_insn(block, Insn::FixnumDiv { left, right }));
|
state.push(fun.push_insn(block, Insn::FixnumDiv { left, right, state: exit_state }));
|
||||||
} else {
|
} else {
|
||||||
let right = state.pop()?;
|
let right = state.pop()?;
|
||||||
let left = state.pop()?;
|
let left = state.pop()?;
|
||||||
@ -813,8 +817,9 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result<Function, ParseError> {
|
|||||||
YARVINSN_opt_mod | YARVINSN_zjit_opt_mod => {
|
YARVINSN_opt_mod | YARVINSN_zjit_opt_mod => {
|
||||||
if payload.have_two_fixnums(current_insn_idx as usize) {
|
if payload.have_two_fixnums(current_insn_idx as usize) {
|
||||||
fun.push_insn(block, Insn::PatchPoint(Invariant::BOPRedefined { klass: INTEGER_REDEFINED_OP_FLAG, bop: BOP_MOD }));
|
fun.push_insn(block, Insn::PatchPoint(Invariant::BOPRedefined { klass: INTEGER_REDEFINED_OP_FLAG, bop: BOP_MOD }));
|
||||||
|
let exit_state = state.clone();
|
||||||
let (left, right) = guard_two_fixnums(&mut state, &mut fun, block)?;
|
let (left, right) = guard_two_fixnums(&mut state, &mut fun, block)?;
|
||||||
state.push(fun.push_insn(block, Insn::FixnumMod { left, right }));
|
state.push(fun.push_insn(block, Insn::FixnumMod { left, right, state: exit_state }));
|
||||||
} else {
|
} else {
|
||||||
let right = state.pop()?;
|
let right = state.pop()?;
|
||||||
let left = state.pop()?;
|
let left = state.pop()?;
|
||||||
@ -825,8 +830,9 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result<Function, ParseError> {
|
|||||||
YARVINSN_opt_eq | YARVINSN_zjit_opt_eq => {
|
YARVINSN_opt_eq | YARVINSN_zjit_opt_eq => {
|
||||||
if payload.have_two_fixnums(current_insn_idx as usize) {
|
if payload.have_two_fixnums(current_insn_idx as usize) {
|
||||||
fun.push_insn(block, Insn::PatchPoint(Invariant::BOPRedefined { klass: INTEGER_REDEFINED_OP_FLAG, bop: BOP_EQ }));
|
fun.push_insn(block, Insn::PatchPoint(Invariant::BOPRedefined { klass: INTEGER_REDEFINED_OP_FLAG, bop: BOP_EQ }));
|
||||||
|
let exit_state = state.clone();
|
||||||
let (left, right) = guard_two_fixnums(&mut state, &mut fun, block)?;
|
let (left, right) = guard_two_fixnums(&mut state, &mut fun, block)?;
|
||||||
state.push(fun.push_insn(block, Insn::FixnumEq { left, right }));
|
state.push(fun.push_insn(block, Insn::FixnumEq { left, right, state: exit_state }));
|
||||||
} else {
|
} else {
|
||||||
let right = state.pop()?;
|
let right = state.pop()?;
|
||||||
let left = state.pop()?;
|
let left = state.pop()?;
|
||||||
@ -836,8 +842,9 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result<Function, ParseError> {
|
|||||||
YARVINSN_opt_neq | YARVINSN_zjit_opt_neq => {
|
YARVINSN_opt_neq | YARVINSN_zjit_opt_neq => {
|
||||||
if payload.have_two_fixnums(current_insn_idx as usize) {
|
if payload.have_two_fixnums(current_insn_idx as usize) {
|
||||||
fun.push_insn(block, Insn::PatchPoint(Invariant::BOPRedefined { klass: INTEGER_REDEFINED_OP_FLAG, bop: BOP_NEQ }));
|
fun.push_insn(block, Insn::PatchPoint(Invariant::BOPRedefined { klass: INTEGER_REDEFINED_OP_FLAG, bop: BOP_NEQ }));
|
||||||
|
let exit_state = state.clone();
|
||||||
let (left, right) = guard_two_fixnums(&mut state, &mut fun, block)?;
|
let (left, right) = guard_two_fixnums(&mut state, &mut fun, block)?;
|
||||||
state.push(fun.push_insn(block, Insn::FixnumNeq { left, right }));
|
state.push(fun.push_insn(block, Insn::FixnumNeq { left, right, state: exit_state }));
|
||||||
} else {
|
} else {
|
||||||
let right = state.pop()?;
|
let right = state.pop()?;
|
||||||
let left = state.pop()?;
|
let left = state.pop()?;
|
||||||
@ -847,8 +854,9 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result<Function, ParseError> {
|
|||||||
YARVINSN_opt_lt | YARVINSN_zjit_opt_lt => {
|
YARVINSN_opt_lt | YARVINSN_zjit_opt_lt => {
|
||||||
if payload.have_two_fixnums(current_insn_idx as usize) {
|
if payload.have_two_fixnums(current_insn_idx as usize) {
|
||||||
fun.push_insn(block, Insn::PatchPoint(Invariant::BOPRedefined { klass: INTEGER_REDEFINED_OP_FLAG, bop: BOP_LT }));
|
fun.push_insn(block, Insn::PatchPoint(Invariant::BOPRedefined { klass: INTEGER_REDEFINED_OP_FLAG, bop: BOP_LT }));
|
||||||
|
let exit_state = state.clone();
|
||||||
let (left, right) = guard_two_fixnums(&mut state, &mut fun, block)?;
|
let (left, right) = guard_two_fixnums(&mut state, &mut fun, block)?;
|
||||||
state.push(fun.push_insn(block, Insn::FixnumLt { left, right }));
|
state.push(fun.push_insn(block, Insn::FixnumLt { left, right, state: exit_state }));
|
||||||
} else {
|
} else {
|
||||||
let right = state.pop()?;
|
let right = state.pop()?;
|
||||||
let left = state.pop()?;
|
let left = state.pop()?;
|
||||||
@ -858,8 +866,9 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result<Function, ParseError> {
|
|||||||
YARVINSN_opt_le | YARVINSN_zjit_opt_le => {
|
YARVINSN_opt_le | YARVINSN_zjit_opt_le => {
|
||||||
if payload.have_two_fixnums(current_insn_idx as usize) {
|
if payload.have_two_fixnums(current_insn_idx as usize) {
|
||||||
fun.push_insn(block, Insn::PatchPoint(Invariant::BOPRedefined { klass: INTEGER_REDEFINED_OP_FLAG, bop: BOP_LE }));
|
fun.push_insn(block, Insn::PatchPoint(Invariant::BOPRedefined { klass: INTEGER_REDEFINED_OP_FLAG, bop: BOP_LE }));
|
||||||
|
let exit_state = state.clone();
|
||||||
let (left, right) = guard_two_fixnums(&mut state, &mut fun, block)?;
|
let (left, right) = guard_two_fixnums(&mut state, &mut fun, block)?;
|
||||||
state.push(fun.push_insn(block, Insn::FixnumLe { left, right }));
|
state.push(fun.push_insn(block, Insn::FixnumLe { left, right, state: exit_state }));
|
||||||
} else {
|
} else {
|
||||||
let right = state.pop()?;
|
let right = state.pop()?;
|
||||||
let left = state.pop()?;
|
let left = state.pop()?;
|
||||||
@ -869,8 +878,9 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result<Function, ParseError> {
|
|||||||
YARVINSN_opt_gt | YARVINSN_zjit_opt_gt => {
|
YARVINSN_opt_gt | YARVINSN_zjit_opt_gt => {
|
||||||
if payload.have_two_fixnums(current_insn_idx as usize) {
|
if payload.have_two_fixnums(current_insn_idx as usize) {
|
||||||
fun.push_insn(block, Insn::PatchPoint(Invariant::BOPRedefined { klass: INTEGER_REDEFINED_OP_FLAG, bop: BOP_GT }));
|
fun.push_insn(block, Insn::PatchPoint(Invariant::BOPRedefined { klass: INTEGER_REDEFINED_OP_FLAG, bop: BOP_GT }));
|
||||||
|
let exit_state = state.clone();
|
||||||
let (left, right) = guard_two_fixnums(&mut state, &mut fun, block)?;
|
let (left, right) = guard_two_fixnums(&mut state, &mut fun, block)?;
|
||||||
state.push(fun.push_insn(block, Insn::FixnumGt { left, right }));
|
state.push(fun.push_insn(block, Insn::FixnumGt { left, right, state: exit_state }));
|
||||||
} else {
|
} else {
|
||||||
let right = state.pop()?;
|
let right = state.pop()?;
|
||||||
let left = state.pop()?;
|
let left = state.pop()?;
|
||||||
@ -880,8 +890,9 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result<Function, ParseError> {
|
|||||||
YARVINSN_opt_ge | YARVINSN_zjit_opt_ge => {
|
YARVINSN_opt_ge | YARVINSN_zjit_opt_ge => {
|
||||||
if payload.have_two_fixnums(current_insn_idx as usize) {
|
if payload.have_two_fixnums(current_insn_idx as usize) {
|
||||||
fun.push_insn(block, Insn::PatchPoint(Invariant::BOPRedefined { klass: INTEGER_REDEFINED_OP_FLAG, bop: BOP_GE }));
|
fun.push_insn(block, Insn::PatchPoint(Invariant::BOPRedefined { klass: INTEGER_REDEFINED_OP_FLAG, bop: BOP_GE }));
|
||||||
|
let exit_state = state.clone();
|
||||||
let (left, right) = guard_two_fixnums(&mut state, &mut fun, block)?;
|
let (left, right) = guard_two_fixnums(&mut state, &mut fun, block)?;
|
||||||
state.push(fun.push_insn(block, Insn::FixnumGe { left, right }));
|
state.push(fun.push_insn(block, Insn::FixnumGe { left, right, state: exit_state }));
|
||||||
} else {
|
} else {
|
||||||
let right = state.pop()?;
|
let right = state.pop()?;
|
||||||
let left = state.pop()?;
|
let left = state.pop()?;
|
||||||
@ -938,9 +949,9 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result<Function, ParseError> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
match get_option!(dump_hir) {
|
match get_option!(dump_hir) {
|
||||||
Some(DumpHIR::WithoutSnapshot) => print!("HIR:\n{}", FunctionPrinter::without_snapshot(&fun)),
|
Some(DumpHIR::WithoutSnapshot) => println!("HIR:\n{}", FunctionPrinter::without_snapshot(&fun)),
|
||||||
Some(DumpHIR::All) => print!("HIR:\n{}", FunctionPrinter::with_snapshot(&fun)),
|
Some(DumpHIR::All) => println!("HIR:\n{}", FunctionPrinter::with_snapshot(&fun)),
|
||||||
Some(DumpHIR::Raw) => print!("HIR:\n{:#?}", &fun),
|
Some(DumpHIR::Raw) => println!("HIR:\n{:#?}", &fun),
|
||||||
None => {},
|
None => {},
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -949,8 +960,8 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result<Function, ParseError> {
|
|||||||
|
|
||||||
/// Generate guards for two fixnum outputs
|
/// Generate guards for two fixnum outputs
|
||||||
fn guard_two_fixnums(state: &mut FrameState, fun: &mut Function, block: BlockId) -> Result<(InsnId, InsnId), ParseError> {
|
fn guard_two_fixnums(state: &mut FrameState, fun: &mut Function, block: BlockId) -> Result<(InsnId, InsnId), ParseError> {
|
||||||
let left = fun.push_insn(block, Insn::GuardType { val: state.stack_opnd(1)?, guard_type: Fixnum });
|
let left = fun.push_insn(block, Insn::GuardType { val: state.stack_opnd(1)?, guard_type: Fixnum, state: state.clone() });
|
||||||
let right = fun.push_insn(block, Insn::GuardType { val: state.stack_opnd(0)?, guard_type: Fixnum });
|
let right = fun.push_insn(block, Insn::GuardType { val: state.stack_opnd(0)?, guard_type: Fixnum, state: state.clone() });
|
||||||
|
|
||||||
// Pop operands after guards for side exits
|
// Pop operands after guards for side exits
|
||||||
state.pop()?;
|
state.pop()?;
|
||||||
|
14
zjit/src/invariants.rs
Normal file
14
zjit/src/invariants.rs
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
use crate::{cruby::{ruby_basic_operators, RedefinitionFlag}, zjit_enabled_p};
|
||||||
|
|
||||||
|
/// Called when a basic operator is redefined. Note that all the blocks assuming
|
||||||
|
/// the stability of different operators are invalidated together and we don't
|
||||||
|
/// do fine-grained tracking.
|
||||||
|
#[unsafe(no_mangle)]
|
||||||
|
pub extern "C" fn rb_zjit_bop_redefined(_klass: RedefinitionFlag, _bop: ruby_basic_operators) {
|
||||||
|
// If ZJIT isn't enabled, do nothing
|
||||||
|
if !zjit_enabled_p() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
unimplemented!("Invalidation on BOP redefinition is not implemented yet");
|
||||||
|
}
|
@ -19,6 +19,7 @@ mod backend;
|
|||||||
mod disasm;
|
mod disasm;
|
||||||
mod options;
|
mod options;
|
||||||
mod profile;
|
mod profile;
|
||||||
|
mod invariants;
|
||||||
|
|
||||||
use codegen::gen_function;
|
use codegen::gen_function;
|
||||||
use options::{debug, get_option, Options};
|
use options::{debug, get_option, Options};
|
||||||
@ -29,6 +30,11 @@ use crate::cruby::*;
|
|||||||
#[unsafe(no_mangle)]
|
#[unsafe(no_mangle)]
|
||||||
pub static mut rb_zjit_enabled_p: bool = false;
|
pub static mut rb_zjit_enabled_p: bool = false;
|
||||||
|
|
||||||
|
/// Like rb_zjit_enabled_p, but for Rust code.
|
||||||
|
pub fn zjit_enabled_p() -> bool {
|
||||||
|
unsafe { rb_zjit_enabled_p }
|
||||||
|
}
|
||||||
|
|
||||||
/// Initialize ZJIT, given options allocated by rb_zjit_init_options()
|
/// Initialize ZJIT, given options allocated by rb_zjit_init_options()
|
||||||
#[unsafe(no_mangle)]
|
#[unsafe(no_mangle)]
|
||||||
pub extern "C" fn rb_zjit_init(options: *const u8) {
|
pub extern "C" fn rb_zjit_init(options: *const u8) {
|
||||||
|
@ -109,8 +109,7 @@ fn parse_option(options: &mut Options, str_ptr: *const std::os::raw::c_char) ->
|
|||||||
/// Macro to print a message only when --zjit-debug is given
|
/// Macro to print a message only when --zjit-debug is given
|
||||||
macro_rules! debug {
|
macro_rules! debug {
|
||||||
($($msg:tt)*) => {
|
($($msg:tt)*) => {
|
||||||
use crate::options::get_option;
|
if $crate::options::get_option!(debug) {
|
||||||
if get_option!(debug) {
|
|
||||||
eprintln!($($msg)*);
|
eprintln!($($msg)*);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
Loading…
x
Reference in New Issue
Block a user