-
Notifications
You must be signed in to change notification settings - Fork 5.4k
ZJIT: Pass self through basic block params #13529
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
c41b83b
to
84d0f24
Compare
Co-authored-by: Max Bernstein <tekknolagi@gmail.com>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think there are some lingering indexing and typing issues but otherwise looks ok to me
zjit/src/hir.rs
Outdated
let val = function.push_insn(function.entry_block, Insn::Test { val: param }); | ||
function.infer_types(); | ||
assert_bit_equal(function.type_of(val), types::CBool); | ||
assert_bit_equal(function.type_of(val), Type::from_cbool(false)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why did this change? We shouldn't know that it's false
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is still weird to me :/
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Aha, we need to do
function.param_types.push(types::BasicObject); // self
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(in the test)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for pointing out the fix. Yeah I was looking into this and it had Empty
and is_known_falsy()
returned true (?). Pushed the fix at 5a543a6.
I wish we had less (ideally, no) synthetic tests like this. It seems to test something unreal and requires non-trivial effort for debugging it. The more integrated, the easier to maintain.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think something is still up with the indexing; see my comments about the modified tests
I can't push a commit but I think you want something like commit d4d83a0d74b8ffe406ae9e5acd38b2a53f6829be (HEAD -> zjit-self-param)
Author: Max Bernstein <max@bernsteinbear.com>
Date: Thu Jun 5 16:44:55 2025 -0400
Only push self param for non-entry blocks
diff --git a/zjit/src/hir.rs b/zjit/src/hir.rs
index 1d26c99964..2711c7561b 100644
--- a/zjit/src/hir.rs
+++ b/zjit/src/hir.rs
@@ -1989,6 +1989,7 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result<Function, ParseError> {
// item between commas in the source increase the parameter count by one,
// regardless of parameter kind.
let mut entry_state = FrameState::new(iseq);
+ fun.push_insn(fun.entry_block, Insn::Param { idx: SELF_PARAM_IDX });
fun.param_types.push(types::BasicObject); // self
for local_idx in 0..num_locals(iseq) {
if local_idx < unsafe { get_iseq_body_param_size(iseq) }.as_usize() {
@@ -2012,11 +2013,14 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result<Function, ParseError> {
while let Some((incoming_state, block, mut insn_idx)) = queue.pop_front() {
if visited.contains(&block) { continue; }
visited.insert(block);
- let self_param = fun.push_insn(block, Insn::Param { idx: SELF_PARAM_IDX });
- let mut state = if insn_idx == 0 { incoming_state.clone() } else {
+ let (self_param, mut state) = if insn_idx == 0 {
+ (fun.blocks[fun.entry_block.0].params[0], incoming_state.clone())
+ } else {
+ assert_eq!(fun.blocks[block.0].params.len(), 0);
+ let self_param = fun.push_insn(block, Insn::Param { idx: SELF_PARAM_IDX });
let mut result = FrameState::new(iseq);
let mut idx = 1;
- for _ in 0..incoming_state.locals.len() {
+ for _ in incoming_state.locals {
result.locals.push(fun.push_insn(block, Insn::Param { idx }));
idx += 1;
}
@@ -2024,7 +2028,7 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result<Function, ParseError> {
result.stack.push(fun.push_insn(block, Insn::Param { idx }));
idx += 1;
}
- result
+ (self_param, result)
};
// Start the block off with a Snapshot so that if we need to insert a new Guard later on
// and we don't have a Snapshot handy, we can just iterate backward (at the earliest, to |
Co-authored-by: Max Bernstein <tekknolagi@gmail.com>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
lgtm except for the weird type thing :/
zjit/src/hir.rs
Outdated
let val = function.push_insn(function.entry_block, Insn::Test { val: param }); | ||
function.infer_types(); | ||
assert_bit_equal(function.type_of(val), types::CBool); | ||
assert_bit_equal(function.type_of(val), Type::from_cbool(false)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is still weird to me :/
This PR moves the storage of
self
fromcfp->self
to the first basic block argument to use a register.Currently, ZJIT basic blocks are not allowed to refer to an argument of another basic block because the register allocator uses a fixed register for each basic block argument index, which would cause a conflict for such a reuse. I left a TODO comment in
as_args()
to fix it when we make the register allocator more flexible.