-
Notifications
You must be signed in to change notification settings - Fork 1.3k
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
cranelift: Sign extend immediates in instructions that embed them. #4602
Conversation
b052b80
to
1d042ec
Compare
1d042ec
to
2dd1d08
Compare
As I stated in the linked issue, IMHO the CLIF instruction definition should be updated. |
You are right, I didn't read your comment properly (sorry!), ill mark this as draft until we make that decision. |
@akirilov-arm I've updated the documentation, is this all you meant, or did I miss something else? Also, cc @cfallin since I can't request reviews. |
ir::Opcode::UremImm => replace.urem(arg, imm), | ||
// comparisons | ||
ir::Opcode::IfcmpImm => replace.ifcmp(arg, imm), | ||
_ => unimplemented!(), |
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.
It looks like it's a behavioral change if we panic here. The outer match just skips the instruction if it doesn't have an expansion for it. Maybe that's why the previous implementation duplicated so much code? I think @cfallin probably needs to weigh in on how this case should be handled.
Gotta say, though, I really like how much shorter your version is. I hope we can preserve that clarity even if this bit has to change.
It might help to introduce a function that's something like this (but I haven't tested this code, let alone compiled it):
fn imm_const(pos: &mut FuncCursor, arg: Value, imm: Imm64) -> Value {
let ty = pos.func.dfg.value_type(arg);
let imm = pos.ins().iconst(ty, imm);
if ty == I128 {
let imm = pos.ins().iconst(I64, imm);
pos.ins().sextend(I128, imm)
} else {
pos.ins().iconst(ty.lane_type(), imm)
}
}
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.
Yes, we don't want to hit an unimplemented!()
here; I think we just want an empty match-arm body (_ => {}
) instead. That way we still construct the imm
but we just don't do anything with it.
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.
Wouldn't an empty match-arm body there cause simple_legalize
to loop, trying to legalize the same instruction repeatedly?
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.
The toplevel loop is a while let Some(inst) = pos.next_inst()
, so it's stepping through insts with no action taken in the loop body. Though actually the more precise thing to do is to replicate the fallback _ => { ... }
below, which sets prev_pos
first then continues, I think.
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.
The statement after the match
is pos.set_position(prev_pos);
and the intent appears to be to re-examine the result of every legalization. So it looks to me like termination of this function relies on every match arm replacing the instruction that it originally matched on. But yes, copying the prev_pos
assignment from the fallback case should work, I think.
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!
ir::Opcode::UremImm => replace.urem(arg, imm), | ||
// comparisons | ||
ir::Opcode::IfcmpImm => replace.ifcmp(arg, imm), | ||
_ => unimplemented!(), |
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.
Yes, we don't want to hit an unimplemented!()
here; I think we just want an empty match-arm body (_ => {}
) instead. That way we still construct the imm
but we just don't do anything with it.
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.
@afonso360 The changes to cranelift/codegen/meta/src/shared/instructions.rs
are exactly what I had in mind.
I also think zero extending the unsigned operations is probably the better choice. However, what if we instead altered the type of the instruction to accept the full I don't think this would grow the size of Would storing the Imm in the DFG for these operations cause performance issues? |
Actually that would be the ideal design: from first principles one would expect "with immediate" variants of 128-bit ops to carry 128-bit (16-byte) immediates. However we can't carry that inline in the instruction, as you're hinting, because I suspect that indirecting for all constants would have some nontrivial overhead, though it might be a bit of work to measure (a lot of refactoring). I had another thought, though, that may be more "honest" about the costs: what if we limited (i) Thoughts on that? |
I was proposing that only for Having more operations only work up to I don't know, I'm a bit undecided on this, I'd like to hear what other people think. |
Generally I agree at an IR-design level: it would be nice to have orthogonality here, and allow all types up through This compromise feels similar to "small immediates" in RISC ISAs though: we have a relatively small struct size for instructions, and its size is critical for performance, and its entire width would be taken by just the I would be curious to see the overhead of a "every immediate is indirect" approach, if you're up for prototyping that. If it's actually negligible, then I think it's a reasonable change to make. |
Me too, and I think we could mitigate some of the cost (if it is non-negligible) by having two const Although right now I don't have a lot of time to try this out. I'm planning on addressing the feedback above and getting this merged, is that okay, or would you prefer restricting the types that we accept? |
👋 Hey,
This PR alters the behaviour of
*_imm
instructions to sign extend their immediate argument when the control type isi128
.This comes from a fuzzer issue where the interpreter was sign extending the immediates but the legalizations were not.
Fixes: #4568
Fixes: #4641