Skip to content

Commit 0364a61

Browse files
authored
ZJIT: Clean up branching in HIR construction (ruby#16616)
We have the global register allocator now and don't need to thread everything through manually as block params. Simplify HIR construction.
1 parent 9c827a4 commit 0364a61

3 files changed

Lines changed: 382 additions & 491 deletions

File tree

zjit/src/hir.rs

Lines changed: 41 additions & 106 deletions
Original file line numberDiff line numberDiff line change
@@ -2610,24 +2610,6 @@ impl Function {
26102610
id
26112611
}
26122612

2613-
fn new_branch_block(
2614-
&mut self,
2615-
insn_idx: u32,
2616-
exit_state: &FrameState,
2617-
locals_count: usize,
2618-
stack_count: usize,
2619-
) -> (BlockId, InsnId, FrameState, InsnId) {
2620-
let block = self.new_block(insn_idx);
2621-
let self_param = self.push_insn(block, Insn::Param);
2622-
let mut state = exit_state.clone();
2623-
state.locals.clear();
2624-
state.stack.clear();
2625-
state.locals.extend((0..locals_count).map(|_| self.push_insn(block, Insn::Param)));
2626-
state.stack.extend((0..stack_count).map(|_| self.push_insn(block, Insn::Param)));
2627-
let snapshot = self.push_insn(block, Insn::Snapshot { state: state.clone() });
2628-
(block, self_param, state, snapshot)
2629-
}
2630-
26312613
fn remove_block(&mut self, block_id: BlockId) {
26322614
if BlockId(self.blocks.len() - 1) != block_id {
26332615
panic!("Can only remove the last block");
@@ -7587,44 +7569,39 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result<Function, ParseError> {
75877569
None
75887570
};
75897571

7590-
let locals_count = state.locals.len();
7591-
let stack_count = state.stack.len();
7592-
let entry_args = state.as_args(self_param);
7593-
75947572
// `getblockparamproxy` has two semantic paths:
75957573
// - modified: return the already-materialized block local from EP
75967574
// - unmodified: inspect the block handler and produce proxy/nil
7597-
let (modified_block, modified_self_param, mut modified_state, ..) =
7598-
fun.new_branch_block(branch_insn_idx, &exit_state, locals_count, stack_count);
7599-
let (unmodified_block, unmodified_self_param, mut unmodified_state, unmodified_exit_id) =
7600-
fun.new_branch_block(branch_insn_idx, &exit_state, locals_count, stack_count);
7575+
let modified_block = fun.new_block(branch_insn_idx);
7576+
let unmodified_block = fun.new_block(branch_insn_idx);
76017577
let join_block = insn_idx_to_block.get(&insn_idx).copied().unwrap_or_else(|| fun.new_block(insn_idx));
7578+
let join_result = fun.push_insn(join_block, Insn::Param);
7579+
let join_local = if level == 0 { Some(fun.push_insn(join_block, Insn::Param)) } else { None };
76027580

76037581
let ep = fun.push_insn(block, Insn::GetEP { level });
76047582
let is_modified = fun.push_insn(block, Insn::IsBlockParamModified { ep });
76057583

7606-
fun.push_insn(block, Insn::IfTrue { val: is_modified, target: BranchEdge { target: modified_block, args: entry_args.clone() }});
7607-
fun.push_insn(block, Insn::Jump(BranchEdge { target: unmodified_block, args: entry_args }));
7584+
fun.push_insn(block, Insn::IfTrue { val: is_modified, target: BranchEdge { target: modified_block, args: vec![] }});
7585+
fun.push_insn(block, Insn::Jump(BranchEdge { target: unmodified_block, args: vec![] }));
76087586

76097587
// Push modified block: load the block local via EP.
7610-
let ep = fun.push_insn(modified_block, Insn::GetEP { level });
76117588
let modified_val = fun.get_local_from_ep(modified_block, ep, ep_offset, level, types::BasicObject);
7612-
if level == 0 {
7613-
modified_state.setlocal(ep_offset, modified_val);
7614-
}
7615-
modified_state.stack_push(modified_val);
7616-
fun.push_insn(modified_block, Insn::Jump(BranchEdge { target: join_block, args: modified_state.as_args(modified_self_param) }));
7589+
let mut modified_args = vec![modified_val];
7590+
if level == 0 { modified_args.push(modified_val); }
7591+
fun.push_insn(modified_block, Insn::Jump(BranchEdge { target: join_block, args: modified_args }));
76177592

76187593
// Push unmodified block: inspect the current block handler to
76197594
// decide whether this path returns `nil` or `BlockParamProxy`.
7620-
let ep = fun.push_insn(unmodified_block, Insn::GetEP { level });
76217595
let block_handler = fun.push_insn(unmodified_block, Insn::LoadField { recv: ep, id: ID!(_env_data_index_specval), offset: SIZEOF_VALUE_I32 * VM_ENV_DATA_INDEX_SPECVAL, return_type: types::CInt64 });
7596+
let original_local = if level == 0 { Some(state.getlocal(ep_offset)) } else { None };
76227597

76237598
match profiled_block_type {
76247599
Some(ty) if ty.nil_p() => {
7625-
fun.push_insn(unmodified_block, Insn::GuardBitEquals { val: block_handler, expected: Const::CInt64(VM_BLOCK_HANDLER_NONE.into()), reason: SideExitReason::BlockParamProxyNotNil, state: unmodified_exit_id });
7626-
unmodified_state.stack_push(fun.push_insn(unmodified_block, Insn::Const { val: Const::Value(Qnil) }));
7627-
fun.push_insn(unmodified_block, Insn::Jump(BranchEdge { target: join_block, args: unmodified_state.as_args(unmodified_self_param) }));
7600+
fun.push_insn(unmodified_block, Insn::GuardBitEquals { val: block_handler, expected: Const::CInt64(VM_BLOCK_HANDLER_NONE.into()), reason: SideExitReason::BlockParamProxyNotNil, state: exit_id });
7601+
let nil_val = fun.push_insn(unmodified_block, Insn::Const { val: Const::Value(Qnil) });
7602+
let mut unmodified_args = vec![nil_val];
7603+
if let Some(local) = original_local { unmodified_args.push(local); }
7604+
fun.push_insn(unmodified_block, Insn::Jump(BranchEdge { target: join_block, args: unmodified_args }));
76287605
}
76297606
_ => {
76307607
// This handles two cases which are nearly identical
@@ -7635,107 +7612,65 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result<Function, ParseError> {
76357612
const _: () = assert!(RUBY_SYMBOL_FLAG & 1 == 0, "guard below rejects symbol block handlers");
76367613

76377614
// Bail out if the block handler is neither ISEQ nor ifunc
7638-
fun.push_insn(unmodified_block, Insn::GuardAnyBitSet { val: block_handler, mask: Const::CUInt64(0x1), mask_name: None, reason: SideExitReason::BlockParamProxyNotIseqOrIfunc, state: unmodified_exit_id });
7615+
fun.push_insn(unmodified_block, Insn::GuardAnyBitSet { val: block_handler, mask: Const::CUInt64(0x1), mask_name: None, reason: SideExitReason::BlockParamProxyNotIseqOrIfunc, state: exit_id });
76397616
// TODO(Shopify/ruby#753): GC root, so we should be able to avoid unnecessary GC tracing
7640-
unmodified_state.stack_push(fun.push_insn(unmodified_block, Insn::Const { val: Const::Value(unsafe { rb_block_param_proxy }) }));
7641-
fun.push_insn(unmodified_block, Insn::Jump(BranchEdge { target: join_block, args: unmodified_state.as_args(unmodified_self_param) }));
7617+
let proxy_val = fun.push_insn(unmodified_block, Insn::Const { val: Const::Value(unsafe { rb_block_param_proxy }) });
7618+
let mut unmodified_args = vec![proxy_val];
7619+
if let Some(local) = original_local { unmodified_args.push(local); }
7620+
fun.push_insn(unmodified_block, Insn::Jump(BranchEdge { target: join_block, args: unmodified_args }));
76427621
}
76437622
}
76447623

76457624
// Continue compilation from the merged continuation block at the next
76467625
// instruction.
7647-
queue.push_back((unmodified_state, join_block, insn_idx, local_inval));
7648-
break;
7626+
if let Some(local_param) = join_local {
7627+
state.setlocal(ep_offset, local_param);
7628+
}
7629+
state.stack_push(join_result);
7630+
block = join_block;
76497631
}
76507632
YARVINSN_getblockparam => {
7651-
fn finish_getblockparam_branch(
7652-
fun: &mut Function,
7653-
block: BlockId,
7654-
self_param: InsnId,
7655-
state: &mut FrameState,
7656-
join_block: BlockId,
7657-
ep_offset: u32,
7658-
level: u32,
7659-
val: InsnId,
7660-
) {
7661-
if level == 0 {
7662-
state.setlocal(ep_offset, val);
7663-
}
7664-
state.stack_push(val);
7665-
fun.push_insn(block, Insn::Jump(BranchEdge {
7666-
target: join_block,
7667-
args: state.as_args(self_param),
7668-
}));
7669-
}
7670-
76717633
let ep_offset = get_arg(pc, 0).as_u32();
76727634
let level = get_arg(pc, 1).as_u32();
76737635
let branch_insn_idx = exit_state.insn_idx as u32;
76747636

76757637
// If the block param is already a Proc (modified), read it from EP.
76767638
// Otherwise, convert it to a Proc and store it to EP.
7639+
let modified_block = fun.new_block(branch_insn_idx);
7640+
let unmodified_block = fun.new_block(branch_insn_idx);
7641+
let join_block = insn_idx_to_block.get(&insn_idx).copied().unwrap_or_else(|| fun.new_block(insn_idx));
7642+
let join_param = fun.push_insn(join_block, Insn::Param);
7643+
76777644
let ep = fun.push_insn(block, Insn::GetEP { level });
76787645
let is_modified = fun.push_insn(block, Insn::IsBlockParamModified { ep });
76797646

7680-
let locals_count = state.locals.len();
7681-
let stack_count = state.stack.len();
7682-
let entry_args = state.as_args(self_param);
7683-
7684-
// Set up branch and join blocks.
7685-
let (modified_block, modified_self_param, mut modified_state, ..) =
7686-
fun.new_branch_block(branch_insn_idx, &exit_state, locals_count, stack_count);
7687-
let (unmodified_block, unmodified_self_param, mut unmodified_state, unmodified_exit_id) =
7688-
fun.new_branch_block(branch_insn_idx, &exit_state, locals_count, stack_count);
7689-
let join_block = insn_idx_to_block.get(&insn_idx).copied().unwrap_or_else(|| fun.new_block(insn_idx));
7690-
76917647
fun.push_insn(block, Insn::IfTrue {
76927648
val: is_modified,
7693-
target: BranchEdge { target: modified_block, args: entry_args.clone() },
7649+
target: BranchEdge { target: modified_block, args: vec![] },
76947650
});
76957651
fun.push_insn(block, Insn::Jump(BranchEdge {
76967652
target: unmodified_block,
7697-
args: entry_args,
7653+
args: vec![],
76987654
}));
76997655

77007656
// Push modified block: read Proc from EP.
7701-
let ep = fun.push_insn(modified_block, Insn::GetEP { level });
7702-
let modified_val = fun.get_local_from_ep(modified_block,
7703-
ep,
7704-
ep_offset,
7705-
level,
7706-
types::BasicObject,
7707-
);
7708-
finish_getblockparam_branch(
7709-
&mut fun,
7710-
modified_block,
7711-
modified_self_param,
7712-
&mut modified_state,
7713-
join_block,
7714-
ep_offset,
7715-
level,
7716-
modified_val,
7717-
);
7657+
let modified_val = fun.get_local_from_ep(modified_block, ep, ep_offset, level, types::BasicObject);
7658+
fun.push_insn(modified_block, Insn::Jump(BranchEdge { target: join_block, args: vec![modified_val] }));
77187659

77197660
// Push unmodified block: convert block handler to Proc.
77207661
let unmodified_val = fun.push_insn(unmodified_block, Insn::GetBlockParam {
77217662
ep_offset,
77227663
level,
7723-
state: unmodified_exit_id,
7664+
state: exit_id,
77247665
});
7725-
finish_getblockparam_branch(
7726-
&mut fun,
7727-
unmodified_block,
7728-
unmodified_self_param,
7729-
&mut unmodified_state,
7730-
join_block,
7731-
ep_offset,
7732-
level,
7733-
unmodified_val,
7734-
);
7666+
fun.push_insn(unmodified_block, Insn::Jump(BranchEdge { target: join_block, args: vec![unmodified_val] }));
77357667

77367668
// Continue compilation from the join block at the next instruction.
7737-
queue.push_back((unmodified_state, join_block, insn_idx, local_inval));
7738-
break;
7669+
if level == 0 {
7670+
state.setlocal(ep_offset, join_param);
7671+
}
7672+
state.stack_push(join_param);
7673+
block = join_block;
77397674
}
77407675
YARVINSN_pop => { state.stack_pop()?; }
77417676
YARVINSN_dup => { state.stack_push(state.stack_top()?); }

0 commit comments

Comments
 (0)