Skip to content

Commit ae58855

Browse files
committed
ZJIT: Optimize Send
1 parent 9a80258 commit ae58855

6 files changed

Lines changed: 263 additions & 39 deletions

File tree

insns.def

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -846,6 +846,7 @@ send
846846
(CALL_DATA cd, ISEQ blockiseq)
847847
(...)
848848
(VALUE val)
849+
// attr bool zjit_profile = true;
849850
// attr rb_snum_t sp_inc = sp_inc_of_sendish(cd->ci);
850851
// attr rb_snum_t comptime_sp_inc = sp_inc_of_sendish(ci);
851852
{

zjit/src/codegen.rs

Lines changed: 115 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ use crate::stats::{counter_ptr, with_time_stat, Counter, Counter::{compile_time_
2020
use crate::{asm::CodeBlock, cruby::*, options::debug, virtualmem::CodePtr};
2121
use crate::backend::lir::{self, asm_comment, asm_ccall, Assembler, Opnd, Target, CFP, C_ARG_OPNDS, C_RET_OPND, EC, NATIVE_STACK_PTR, NATIVE_BASE_PTR, SP};
2222
use crate::hir::{iseq_to_hir, BlockId, BranchEdge, Invariant, RangeType, SideExitReason::{self, *}, SpecialBackrefSymbol, SpecialObjectType};
23-
use crate::hir::{Const, FrameState, Function, Insn, InsnId, SendFallbackReason};
23+
use crate::hir::{Const, FrameState, Function, Insn, InsnId, MethodType, SendFallbackReason};
2424
use crate::hir_type::{types, Type};
2525
use crate::options::get_option;
2626
use crate::cast::IntoUsize;
@@ -378,6 +378,10 @@ fn gen_insn(cb: &mut CodeBlock, jit: &mut JITState, asm: &mut Assembler, functio
378378
Insn::SendWithoutBlockDirect { cd, state, args, .. } if args.len() + 1 > C_ARG_OPNDS.len() => // +1 for self
379379
gen_send_without_block(jit, asm, *cd, &function.frame_state(*state), SendFallbackReason::SendWithoutBlockDirectTooManyArgs),
380380
Insn::SendWithoutBlockDirect { cme, iseq, recv, args, state, .. } => gen_send_without_block_direct(cb, jit, asm, *cme, *iseq, opnd!(recv), opnds!(args), &function.frame_state(*state)),
381+
// Give up SendDirect for 6+ args since asm.ccall() doesn't support it.
382+
Insn::SendDirect { cd, blockiseq, state, args, .. } if args.len() + 1 > C_ARG_OPNDS.len() => // +1 for self
383+
gen_send(jit, asm, *cd, *blockiseq, &function.frame_state(*state), SendFallbackReason::SendDirectTooManyArgs),
384+
Insn::SendDirect { cd, cme, blockiseq, recv, args, state, .. } => gen_send_direct(jit, asm, *cd, *cme, *blockiseq, opnd!(recv), opnds!(args), &function.frame_state(*state)),
381385
&Insn::InvokeSuper { cd, blockiseq, state, reason, .. } => gen_invokesuper(jit, asm, cd, blockiseq, &function.frame_state(state), reason),
382386
&Insn::InvokeBlock { cd, state, reason, .. } => gen_invokeblock(jit, asm, cd, &function.frame_state(state), reason),
383387
// Ensure we have enough room fit ec, self, and arguments
@@ -682,6 +686,8 @@ fn gen_ccall_with_frame(jit: &mut JITState, asm: &mut Assembler, cfunc: *const u
682686
iseq: None,
683687
cme,
684688
frame_type: VM_FRAME_MAGIC_CFUNC | VM_FRAME_FLAG_CFRAME | VM_ENV_FLAG_LOCAL,
689+
block_handler: None,
690+
block_iseq: None,
685691
});
686692

687693
asm_comment!(asm, "switch to new SP register");
@@ -737,6 +743,8 @@ fn gen_ccall_variadic(
737743
iseq: None,
738744
cme,
739745
frame_type: VM_FRAME_MAGIC_CFUNC | VM_FRAME_FLAG_CFRAME | VM_ENV_FLAG_LOCAL,
746+
block_handler: None,
747+
block_iseq: None,
740748
});
741749

742750
asm_comment!(asm, "switch to new SP register");
@@ -1129,6 +1137,8 @@ fn gen_send_without_block_direct(
11291137
iseq: Some(iseq),
11301138
cme,
11311139
frame_type: VM_FRAME_MAGIC_METHOD | VM_ENV_FLAG_LOCAL,
1140+
block_handler: None,
1141+
block_iseq: None,
11321142
});
11331143

11341144
asm_comment!(asm, "switch to new SP register");
@@ -1166,6 +1176,90 @@ fn gen_send_without_block_direct(
11661176
ret
11671177
}
11681178

1179+
/// Compile a direct jump to an ISEQ call with block
1180+
fn gen_send_direct(
1181+
jit: &mut JITState,
1182+
asm: &mut Assembler,
1183+
cd: *const rb_call_data,
1184+
cme: *const rb_callable_method_entry_t,
1185+
blockiseq: IseqPtr,
1186+
recv: Opnd,
1187+
args: Vec<Opnd>,
1188+
state: &FrameState,
1189+
) -> lir::Opnd {
1190+
gen_incr_counter(asm, Counter::iseq_optimized_send_count);
1191+
1192+
let cfunc = unsafe { get_cme_def_body_cfunc(cme) };
1193+
let argc = args.len() as i32;
1194+
1195+
let ci = unsafe { get_call_data_ci(cd) };
1196+
let flags = unsafe { vm_ci_flag(ci) };
1197+
// When seeing &block argument, fall back to dynamic dispatch for now
1198+
// TODO: Support block forwarding
1199+
if flags & VM_CALL_ARGS_BLOCKARG != 0 {
1200+
return gen_send(
1201+
jit,
1202+
asm,
1203+
cd,
1204+
blockiseq,
1205+
state,
1206+
SendFallbackReason::SendNotOptimizedMethodType(MethodType::Cfunc),
1207+
);
1208+
}
1209+
1210+
let stack_growth = state.stack_size();
1211+
gen_stack_overflow_check(jit, asm, state, stack_growth);
1212+
1213+
// Save cfp->pc and cfp->sp for the caller frame
1214+
gen_prepare_call_with_gc(asm, state, false);
1215+
// Special SP math. Can't use gen_prepare_non_leaf_call
1216+
gen_save_sp(asm, state.stack().len() - args.len() - 1); // -1 for receiver
1217+
1218+
gen_spill_locals(jit, asm, state);
1219+
gen_spill_stack(jit, asm, state);
1220+
1221+
let block_handler = if !blockiseq.is_null() {
1222+
asm.store(
1223+
Opnd::mem(64, CFP, RUBY_OFFSET_CFP_BLOCK_CODE),
1224+
VALUE::from(blockiseq).into(),
1225+
);
1226+
let cfp_self = asm.lea(Opnd::mem(64, CFP, RUBY_OFFSET_CFP_SELF));
1227+
Some(asm.or(cfp_self, Opnd::Imm(1)))
1228+
} else {
1229+
None
1230+
};
1231+
1232+
let frame_type = VM_FRAME_MAGIC_CFUNC | VM_FRAME_FLAG_CFRAME | VM_ENV_FLAG_LOCAL;
1233+
gen_push_frame(asm, argc as usize, state, ControlFrame {
1234+
recv,
1235+
iseq: None,
1236+
cme,
1237+
frame_type,
1238+
block_handler,
1239+
block_iseq: None,
1240+
});
1241+
1242+
asm_comment!(asm, "set ec->cfp");
1243+
let new_cfp = asm.lea(Opnd::mem(64, CFP, -(RUBY_SIZEOF_CONTROL_FRAME as i32)));
1244+
asm.mov(CFP, new_cfp);
1245+
asm.store(Opnd::mem(64, EC, RUBY_OFFSET_EC_CFP), CFP);
1246+
1247+
let mut c_args = vec![recv];
1248+
c_args.extend(args);
1249+
1250+
asm_comment!(asm, "call C function");
1251+
let cfunc_ptr = unsafe { get_mct_func(cfunc) }.cast();
1252+
let ret = asm.ccall(cfunc_ptr, c_args);
1253+
1254+
asm_comment!(asm, "restore ec->cfp");
1255+
let ec_cfp_opnd = Opnd::mem(64, EC, RUBY_OFFSET_EC_CFP);
1256+
let caller_cfp = asm.add(CFP, RUBY_SIZEOF_CONTROL_FRAME.into());
1257+
asm.mov(CFP, caller_cfp);
1258+
asm.store(ec_cfp_opnd, CFP);
1259+
1260+
ret
1261+
}
1262+
11691263
/// Compile for invokeblock
11701264
fn gen_invokeblock(
11711265
jit: &mut JITState,
@@ -1709,6 +1803,8 @@ struct ControlFrame {
17091803
iseq: Option<IseqPtr>,
17101804
cme: *const rb_callable_method_entry_t,
17111805
frame_type: u32,
1806+
block_handler: Option<Opnd>,
1807+
block_iseq: Option<IseqPtr>,
17121808
}
17131809

17141810
/// Compile an interpreter frame
@@ -1725,9 +1821,24 @@ fn gen_push_frame(asm: &mut Assembler, argc: usize, state: &FrameState, frame: C
17251821
};
17261822
let ep_offset = state.stack().len() as i32 + local_size - argc as i32 + VM_ENV_DATA_SIZE as i32 - 1;
17271823
asm.store(Opnd::mem(64, SP, (ep_offset - 2) * SIZEOF_VALUE_I32), VALUE::from(frame.cme).into());
1728-
// ep[-1]: block_handler or prev EP
1729-
// block_handler is not supported for now
1730-
asm.store(Opnd::mem(64, SP, (ep_offset - 1) * SIZEOF_VALUE_I32), VM_BLOCK_HANDLER_NONE.into());
1824+
1825+
let block_handler_opnd = if let Some(handler) = frame.block_handler {
1826+
handler
1827+
} else if let Some(block_iseq) = frame.block_iseq {
1828+
asm.store(
1829+
Opnd::mem(64, CFP, RUBY_OFFSET_CFP_BLOCK_CODE),
1830+
VALUE::from(block_iseq).into(),
1831+
);
1832+
let cfp_self_addr = asm.lea(Opnd::mem(64, CFP, RUBY_OFFSET_CFP_SELF));
1833+
asm.or(cfp_self_addr, Opnd::Imm(1))
1834+
} else {
1835+
VM_BLOCK_HANDLER_NONE.into()
1836+
};
1837+
1838+
asm.store(
1839+
Opnd::mem(64, SP, (ep_offset - 1) * SIZEOF_VALUE_I32),
1840+
block_handler_opnd,
1841+
);
17311842
// ep[0]: ENV_FLAGS
17321843
asm.store(Opnd::mem(64, SP, ep_offset * SIZEOF_VALUE_I32), frame.frame_type.into());
17331844

zjit/src/cruby_bindings.inc.rs

Lines changed: 27 additions & 26 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)