Skip to content

Commit c153ab2

Browse files
committed
ZJIT: Only reload written locals after send-with-block
Track recursive writes to parent frames.
1 parent 1fcabc4 commit c153ab2

File tree

3 files changed

+238
-94
lines changed

3 files changed

+238
-94
lines changed

zjit/src/hir.rs

Lines changed: 56 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -6246,6 +6246,58 @@ fn invalidates_locals(opcode: u32, operands: *const VALUE) -> bool {
62466246
/// The index of the self parameter in the HIR function
62476247
pub const SELF_PARAM_IDX: usize = 0;
62486248

6249+
/// Return (in `written`) which local variables in the parent (currently being compiled) ISEQ are
6250+
/// written to by any of the nested ISEQs in the blockiseq.
6251+
fn locals_written_in_block(parent_iseq: IseqPtr, blockiseq: IseqPtr, target_depth: u32, written: &mut BitSet<usize>) {
6252+
let iseq_size = unsafe { get_iseq_encoded_size(blockiseq) };
6253+
let mut insn_idx: u32 = 0;
6254+
6255+
while insn_idx < iseq_size {
6256+
let pc = unsafe { rb_iseq_pc_at_idx(blockiseq, insn_idx) };
6257+
let opcode = unsafe { rb_iseq_opcode_at_pc(blockiseq, pc) } as u32;
6258+
6259+
match opcode {
6260+
YARVINSN_setlocal | YARVINSN_setblockparam
6261+
if get_arg(pc, 1).as_u32() == target_depth =>
6262+
{
6263+
let ep_offset = get_arg(pc, 0).as_u32();
6264+
written.insert(ep_offset_to_local_idx(parent_iseq, ep_offset));
6265+
}
6266+
YARVINSN_setlocal_WC_1 if target_depth == 1 => {
6267+
let ep_offset = get_arg(pc, 0).as_u32();
6268+
written.insert(ep_offset_to_local_idx(parent_iseq, ep_offset));
6269+
}
6270+
YARVINSN_send
6271+
| YARVINSN_sendforward
6272+
| YARVINSN_invokesuper
6273+
| YARVINSN_invokesuperforward => {
6274+
let nested = get_arg(pc, 1).as_iseq();
6275+
if !nested.is_null() {
6276+
locals_written_in_block(parent_iseq, nested, target_depth + 1, written);
6277+
}
6278+
}
6279+
_ => {}
6280+
}
6281+
insn_idx += insn_len(opcode as usize);
6282+
}
6283+
}
6284+
6285+
/// Reload locals that may have been modified by the blockiseq.
6286+
fn reload_modified_locals(fun: &mut Function, block: BlockId, state: &mut FrameState, iseq: IseqPtr, blockiseq: IseqPtr) {
6287+
// TODO: Avoid reloading locals that are not referenced by the blockiseq
6288+
// or not used after this. Max thinks we could eventually DCE them.
6289+
let mut locals = BitSet::with_capacity(state.locals.len());
6290+
locals_written_in_block(iseq, blockiseq, 1, &mut locals);
6291+
for local_idx in 0..state.locals.len() {
6292+
if locals.get(local_idx) {
6293+
let ep_offset = local_idx_to_ep_offset(iseq, local_idx) as u32;
6294+
// TODO: We could use `use_sp: true` with PatchPoint
6295+
let val = fun.push_insn(block, Insn::GetLocal { ep_offset, level: 0, use_sp: false, rest_param: false });
6296+
state.setlocal(ep_offset, val);
6297+
}
6298+
}
6299+
}
6300+
62496301
/// Compile ISEQ into High-level IR
62506302
pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result<Function, ParseError> {
62516303
if !ZJITState::can_compile_iseq(iseq) {
@@ -7204,15 +7256,7 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result<Function, ParseError> {
72047256
state.stack_push(send);
72057257

72067258
if !blockiseq.is_null() {
7207-
// Reload locals that may have been modified by the blockiseq.
7208-
// TODO: Avoid reloading locals that are not referenced by the blockiseq
7209-
// or not used after this. Max thinks we could eventually DCE them.
7210-
for local_idx in 0..state.locals.len() {
7211-
let ep_offset = local_idx_to_ep_offset(iseq, local_idx) as u32;
7212-
// TODO: We could use `use_sp: true` with PatchPoint
7213-
let val = fun.push_insn(block, Insn::GetLocal { ep_offset, level: 0, use_sp: false, rest_param: false });
7214-
state.setlocal(ep_offset, val);
7215-
}
7259+
reload_modified_locals(&mut fun, block, &mut state, iseq, blockiseq);
72167260
}
72177261
}
72187262
YARVINSN_sendforward => {
@@ -7234,13 +7278,7 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result<Function, ParseError> {
72347278
state.stack_push(send_forward);
72357279

72367280
if !blockiseq.is_null() {
7237-
// Reload locals that may have been modified by the blockiseq.
7238-
for local_idx in 0..state.locals.len() {
7239-
let ep_offset = local_idx_to_ep_offset(iseq, local_idx) as u32;
7240-
// TODO: We could use `use_sp: true` with PatchPoint
7241-
let val = fun.push_insn(block, Insn::GetLocal { ep_offset, level: 0, use_sp: false, rest_param: false });
7242-
state.setlocal(ep_offset, val);
7243-
}
7281+
reload_modified_locals(&mut fun, block, &mut state, iseq, blockiseq);
72447282
}
72457283
}
72467284
YARVINSN_invokesuper => {
@@ -7261,15 +7299,7 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result<Function, ParseError> {
72617299
state.stack_push(result);
72627300

72637301
if !blockiseq.is_null() {
7264-
// Reload locals that may have been modified by the blockiseq.
7265-
// TODO: Avoid reloading locals that are not referenced by the blockiseq
7266-
// or not used after this. Max thinks we could eventually DCE them.
7267-
for local_idx in 0..state.locals.len() {
7268-
let ep_offset = local_idx_to_ep_offset(iseq, local_idx) as u32;
7269-
// TODO: We could use `use_sp: true` with PatchPoint
7270-
let val = fun.push_insn(block, Insn::GetLocal { ep_offset, level: 0, use_sp: false, rest_param: false });
7271-
state.setlocal(ep_offset, val);
7272-
}
7302+
reload_modified_locals(&mut fun, block, &mut state, iseq, blockiseq);
72737303
}
72747304
}
72757305
YARVINSN_invokesuperforward => {
@@ -7290,15 +7320,7 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result<Function, ParseError> {
72907320
state.stack_push(result);
72917321

72927322
if !blockiseq.is_null() {
7293-
// Reload locals that may have been modified by the blockiseq.
7294-
// TODO: Avoid reloading locals that are not referenced by the blockiseq
7295-
// or not used after this. Max thinks we could eventually DCE them.
7296-
for local_idx in 0..state.locals.len() {
7297-
let ep_offset = local_idx_to_ep_offset(iseq, local_idx) as u32;
7298-
// TODO: We could use `use_sp: true` with PatchPoint
7299-
let val = fun.push_insn(block, Insn::GetLocal { ep_offset, level: 0, use_sp: false, rest_param: false });
7300-
state.setlocal(ep_offset, val);
7301-
}
7323+
reload_modified_locals(&mut fun, block, &mut state, iseq, blockiseq);
73027324
}
73037325
}
73047326
YARVINSN_invokeblock => {

zjit/src/hir/opt_tests.rs

Lines changed: 40 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -823,11 +823,10 @@ mod hir_opt_tests {
823823
bb2(v8:BasicObject, v9:BasicObject):
824824
PatchPoint NoSingletonClass(C@0x1000)
825825
PatchPoint MethodRedefined(C@0x1000, fun_new_map@0x1008, cme:0x1010)
826-
v22:ArraySubclass[class_exact:C] = GuardType v9, ArraySubclass[class_exact:C]
827-
v23:BasicObject = SendDirect v22, 0x1038, :fun_new_map (0x1048)
828-
v15:BasicObject = GetLocal :o, l0, EP@3
826+
v21:ArraySubclass[class_exact:C] = GuardType v9, ArraySubclass[class_exact:C]
827+
v22:BasicObject = SendDirect v21, 0x1038, :fun_new_map (0x1048)
829828
CheckInterrupts
830-
Return v23
829+
Return v22
831830
");
832831
}
833832

@@ -855,11 +854,10 @@ mod hir_opt_tests {
855854
bb2(v8:BasicObject, v9:BasicObject):
856855
PatchPoint NoSingletonClass(C@0x1000)
857856
PatchPoint MethodRedefined(C@0x1000, bar@0x1008, cme:0x1010)
858-
v23:HeapObject[class_exact:C] = GuardType v9, HeapObject[class_exact:C]
859-
v24:BasicObject = CCallWithFrame v23, :Enumerable#bar@0x1038, block=0x1040
860-
v15:BasicObject = GetLocal :o, l0, EP@3
857+
v22:HeapObject[class_exact:C] = GuardType v9, HeapObject[class_exact:C]
858+
v23:BasicObject = CCallWithFrame v22, :Enumerable#bar@0x1038, block=0x1040
861859
CheckInterrupts
862-
Return v24
860+
Return v23
863861
");
864862
}
865863

@@ -2955,12 +2953,11 @@ mod hir_opt_tests {
29552953
v13:Fixnum[1] = Const Value(1)
29562954
PatchPoint NoSingletonClass(Object@0x1000)
29572955
PatchPoint MethodRedefined(Object@0x1000, foo@0x1008, cme:0x1010)
2958-
v31:HeapObject[class_exact*:Object@VALUE(0x1000)] = GuardType v8, HeapObject[class_exact*:Object@VALUE(0x1000)]
2956+
v30:HeapObject[class_exact*:Object@VALUE(0x1000)] = GuardType v8, HeapObject[class_exact*:Object@VALUE(0x1000)]
29592957
IncrCounter inline_iseq_optimized_send_count
2960-
v19:BasicObject = GetLocal :a, l0, EP@3
29612958
PatchPoint NoEPEscape(test)
29622959
CheckInterrupts
2963-
Return v19
2960+
Return v13
29642961
");
29652962
}
29662963

@@ -3444,11 +3441,9 @@ mod hir_opt_tests {
34443441
v21:TrueClass = Const Value(true)
34453442
IncrCounter complex_arg_pass_caller_kwarg
34463443
v23:BasicObject = Send v11, 0x1000, :each_line, v21 # SendFallbackReason: Complex argument passing
3447-
v24:BasicObject = GetLocal :s, l0, EP@4
3448-
v25:BasicObject = GetLocal :a, l0, EP@3
34493444
PatchPoint NoEPEscape(test)
34503445
CheckInterrupts
3451-
Return v25
3446+
Return v16
34523447
");
34533448
}
34543449

@@ -6550,17 +6545,16 @@ mod hir_opt_tests {
65506545
v13:ArrayExact = NewArray
65516546
PatchPoint SingleRactorMode
65526547
PatchPoint StableConstantNames(0x1000, A)
6553-
v36:ArrayExact[VALUE(0x1008)] = Const Value(VALUE(0x1008))
6548+
v35:ArrayExact[VALUE(0x1008)] = Const Value(VALUE(0x1008))
65546549
PatchPoint SingleRactorMode
65556550
PatchPoint StableConstantNames(0x1010, B)
6556-
v39:ArrayExact[VALUE(0x1018)] = Const Value(VALUE(0x1018))
6551+
v38:ArrayExact[VALUE(0x1018)] = Const Value(VALUE(0x1018))
65576552
PatchPoint NoSingletonClass(Array@0x1020)
65586553
PatchPoint MethodRedefined(Array@0x1020, zip@0x1028, cme:0x1030)
6559-
v43:BasicObject = CCallVariadic v36, :zip@0x1058, v39
6560-
v24:BasicObject = GetLocal :result, l0, EP@3
6554+
v42:BasicObject = CCallVariadic v35, :zip@0x1058, v38
65616555
PatchPoint NoEPEscape(test)
65626556
CheckInterrupts
6563-
Return v24
6557+
Return v13
65646558
");
65656559
}
65666560

@@ -10585,23 +10579,19 @@ mod hir_opt_tests {
1058510579
CheckInterrupts
1058610580
SetLocal :formatted, l0, EP@3, v15
1058710581
PatchPoint SingleRactorMode
10588-
v57:HeapBasicObject = GuardType v14, HeapBasicObject
10589-
v58:CShape = LoadField v57, :_shape_id@0x1000
10590-
v59:CShape[0x1001] = GuardBitEquals v58, CShape(0x1001)
10591-
StoreField v57, :@formatted@0x1002, v15
10592-
WriteBarrier v57, v15
10593-
v62:CShape[0x1003] = Const CShape(0x1003)
10594-
StoreField v57, :_shape_id@0x1000, v62
10582+
v53:HeapBasicObject = GuardType v14, HeapBasicObject
10583+
v54:CShape = LoadField v53, :_shape_id@0x1000
10584+
v55:CShape[0x1001] = GuardBitEquals v54, CShape(0x1001)
10585+
StoreField v53, :@formatted@0x1002, v15
10586+
WriteBarrier v53, v15
10587+
v58:CShape[0x1003] = Const CShape(0x1003)
10588+
StoreField v53, :_shape_id@0x1000, v58
1059510589
v46:Class[VMFrozenCore] = Const Value(VALUE(0x1008))
1059610590
PatchPoint NoSingletonClass(Class@0x1010)
1059710591
PatchPoint MethodRedefined(Class@0x1010, lambda@0x1018, cme:0x1020)
10598-
v67:BasicObject = CCallWithFrame v46, :RubyVM::FrozenCore.lambda@0x1048, block=0x1050
10599-
v49:BasicObject = GetLocal :a, l0, EP@6
10600-
v50:BasicObject = GetLocal :_b, l0, EP@5
10601-
v51:BasicObject = GetLocal :_c, l0, EP@4
10602-
v52:BasicObject = GetLocal :formatted, l0, EP@3
10592+
v63:BasicObject = CCallWithFrame v46, :RubyVM::FrozenCore.lambda@0x1048, block=0x1050
1060310593
CheckInterrupts
10604-
Return v67
10594+
Return v63
1060510595
");
1060610596
}
1060710597

@@ -11600,14 +11590,13 @@ mod hir_opt_tests {
1160011590
bb2(v10:BasicObject, v11:BasicObject, v12:NilClass):
1160111591
PatchPoint NoSingletonClass(B@0x1000)
1160211592
PatchPoint MethodRedefined(B@0x1000, proc@0x1008, cme:0x1010)
11603-
v35:HeapObject[class_exact:B] = GuardType v10, HeapObject[class_exact:B]
11604-
v36:BasicObject = CCallWithFrame v35, :Kernel#proc@0x1038, block=0x1040
11605-
v18:BasicObject = GetLocal :blk, l0, EP@4
11606-
SetLocal :other_block, l0, EP@3, v36
11607-
v25:BasicObject = GetLocal :other_block, l0, EP@3
11608-
v27:BasicObject = InvokeSuper v10, 0x1048, v25 # SendFallbackReason: super: complex argument passing to `super` call
11593+
v33:HeapObject[class_exact:B] = GuardType v10, HeapObject[class_exact:B]
11594+
v34:BasicObject = CCallWithFrame v33, :Kernel#proc@0x1038, block=0x1040
11595+
SetLocal :other_block, l0, EP@3, v34
11596+
v23:BasicObject = GetLocal :other_block, l0, EP@3
11597+
v25:BasicObject = InvokeSuper v10, 0x1048, v23 # SendFallbackReason: super: complex argument passing to `super` call
1160911598
CheckInterrupts
11610-
Return v27
11599+
Return v25
1161111600
");
1161211601
}
1161311602

@@ -11981,27 +11970,20 @@ mod hir_opt_tests {
1198111970
v70:Falsy = RefineType v59, Falsy
1198211971
PatchPoint NoSingletonClass(Object@0x1018)
1198311972
PatchPoint MethodRedefined(Object@0x1018, lambda@0x1020, cme:0x1028)
11984-
v114:HeapObject[class_exact*:Object@VALUE(0x1018)] = GuardType v57, HeapObject[class_exact*:Object@VALUE(0x1018)]
11985-
v115:BasicObject = CCallWithFrame v114, :Kernel#lambda@0x1050, block=0x1058
11986-
v74:BasicObject = GetLocal :list, l0, EP@6
11987-
v76:BasicObject = GetLocal :iter_method, l0, EP@4
11988-
v77:BasicObject = GetLocal :kwsplat, l0, EP@3
11989-
SetLocal :sep, l0, EP@5, v115
11990-
Jump bb7(v57, v74, v115, v76, v77)
11991-
bb7(v81:BasicObject, v82:BasicObject, v83:BasicObject, v84:BasicObject, v85:BasicObject):
11973+
v106:HeapObject[class_exact*:Object@VALUE(0x1018)] = GuardType v57, HeapObject[class_exact*:Object@VALUE(0x1018)]
11974+
v107:BasicObject = CCallWithFrame v106, :Kernel#lambda@0x1050, block=0x1058
11975+
SetLocal :sep, l0, EP@5, v107
11976+
Jump bb7(v57, v58, v107, v60, v61)
11977+
bb7(v77:BasicObject, v78:BasicObject, v79:BasicObject, v80:BasicObject, v81:NilClass):
1199211978
PatchPoint SingleRactorMode
1199311979
PatchPoint StableConstantNames(0x1060, CONST)
11994-
v110:HashExact[VALUE(0x1068)] = Const Value(VALUE(0x1068))
11995-
SetLocal :kwsplat, l0, EP@3, v110
11996-
v95:BasicObject = GetLocal :list, l0, EP@6
11997-
v97:BasicObject = GetLocal :iter_method, l0, EP@4
11998-
v99:BasicObject = Send v95, 0x1070, :__send__, v97 # SendFallbackReason: Send: unsupported method type Optimized
11999-
v100:BasicObject = GetLocal :list, l0, EP@6
12000-
v101:BasicObject = GetLocal :sep, l0, EP@5
12001-
v102:BasicObject = GetLocal :iter_method, l0, EP@4
12002-
v103:BasicObject = GetLocal :kwsplat, l0, EP@3
12003-
CheckInterrupts
12004-
Return v99
11980+
v102:HashExact[VALUE(0x1068)] = Const Value(VALUE(0x1068))
11981+
SetLocal :kwsplat, l0, EP@3, v102
11982+
v91:BasicObject = GetLocal :list, l0, EP@6
11983+
v93:BasicObject = GetLocal :iter_method, l0, EP@4
11984+
v95:BasicObject = Send v91, 0x1070, :__send__, v93 # SendFallbackReason: Send: unsupported method type Optimized
11985+
CheckInterrupts
11986+
Return v95
1200511987
");
1200611988
}
1200711989

0 commit comments

Comments
 (0)