-
Notifications
You must be signed in to change notification settings - Fork 60
better const folding for int and bool #317
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
base: main
Are you sure you want to change the base?
Conversation
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.
Love to see this, and I would love to debug & land this ASAP (at a glance it seems reasonably done), can you rebase it?
There's nothing per se preventing this merge, I was going to wait on #321 for some free testing :D But a quick local rebase on it and it all seems to pass, so feel free to merge. |
Looks like the Also, what I thought was a blocker was #317 (comment) - but I see now that you're asking why you had to do such a hack (I would still want to investigate it, but we could open an issue and land this PR as-is). |
} | ||
simple_uni_op! {fneg, float: f_negate} | ||
|
||
/// already unchecked by default |
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.
These comments are kind of funny - I forget exactly how this works but I believe e.g. unchecked_uadd(a, b)
is uN::checked_add(a, b).unchecked_unwrap()
.
That is, overflowing becomes Undefined Behavior.
LLVM has flags that start with n
for this (e.g. nuw
/nsw
meaning "no (un)signed wrap"),
and it turns out rustc_codegen_llvm
is emitting exactly those flags.
SPIR-V seems to have added nuw
/nsw
equivalents in this extension, and later SPIR-V 1.4:
https://github.khronos.org/SPIRV-Registry/extensions/KHR/SPV_KHR_no_integer_wrap_decoration.html#_decorations
I don't expect you to implement using it (which would have to be conditional on the SPIR-V version or that extension being in target-features, anyway), but it would be nice to remove these (wrong-ish) comments, maybe replace them with a big // TODO
or // FIXME
before the block of unchecked_*
etc.
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.
sounds like something that should be an issue and handled in a followup
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 only brought it up because IIRC I saw //
comments turn into ///
comments.
If you keep them //
comments I can kinda see the argument for it being preexisting, but it's still sad that it's effectively a lie, and we could at least turn them into e.g.:
// FIXME: take advantage of `unchecked_*` making overflow UB.
(feel free to workshop it further, but even that should be good enough to replace the incorrect comments with)
$( | ||
(ConstValue::Unsigned($int_lhs), ConstValue::Unsigned($int_rhs)) => return self.const_uint_big(result_type, $fold_int), | ||
(ConstValue::Signed($int_lhs), ConstValue::Signed($int_rhs)) => return self.const_uint_big(result_type, $fold_int as u128), | ||
)? | ||
$((ConstValue::Unsigned($uint_lhs), ConstValue::Unsigned($uint_rhs)) => return self.const_uint_big(result_type, $fold_uint), )? | ||
$((ConstValue::Signed($sint_lhs), ConstValue::Signed($sint_rhs)) => return self.const_uint_big(result_type, $fold_sint as u128), )? |
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.
Hmm, I don't see a failure path here (i.e. "what if const-folding fails"), does const_uint_big
truncate? (truncation might be enough for both signed and unsigned integers)
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.
Currently, none of the const-fold ops can fail.
does const_uint_big truncate?
It seems like it does
rust-gpu/crates/rustc_codegen_spirv/src/builder_spirv.rs
Lines 562 to 574 in 05b3449
// HACK(eddyb) this is done so late (just before interning `val`) to | |
// minimize any potential misuse from direct `def_constant` calls. | |
let val = match (val, scalar_ty) { | |
(SpirvConst::Scalar(val), Some(SpirvType::Integer(bits, signed))) => { | |
let size = Size::from_bits(bits); | |
SpirvConst::Scalar(if signed { | |
size.sign_extend(val) as u128 | |
} else { | |
size.truncate(val) | |
}) | |
} | |
_ => val, | |
}; |
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.
Alright, this should be fine for both signed and unsigned, for both arithmetic and bitwise ops (tho the only bitwise op that would ever be a concern is flipping all the bits).
You only have to worry about anything that explicitly cares about the number of bits - e.g. instead of x.wrapping_shl(y)
you should do x.checked_shl(y & ((x_size.bits() - 1) as u128)).unwrap()
, and same for wrapping_shr
.
If you add a test for something like 1u32 << 32
, the result should be 1
(as the shift amount gets masked out and becomes 1u32 << 0
) not 0
(like (1u128 << 32) as u32
would cause).
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.
So I've setup an entire difftest around this just to notice that 1u32 << 32
doesn't make any sense. In debug this will panic, in a const block this will panic and fail the compile, and in release (without overflow checks) this is UB. How should we go about handling this "correctly" anyway?
Difftests are in https://github.com/Rust-GPU/rust-gpu/tree/const_folding_difftest due to requiring #321
#321 just got merged so there now in this branch
// Note: exactudiv is UB when there's a remainder, so it's valid to implement as a normal div. | ||
// TODO: Can we take advantage of the UB and emit something else? |
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.
Oh, that's funny, that's basically the exact situation unchecked_*
is in, but only this has an accurate comment that mentions UB.
20c541c
to
82a0ee2
Compare
simple_shift_op! { | ||
shl, | ||
int: shift_left_logical, | ||
fold_const { | ||
int(a, b) => a.wrapping_shl(b as u32); | ||
} | ||
} | ||
simple_shift_op! { | ||
lshr, | ||
uint: shift_right_logical, | ||
fold_const { | ||
uint(a, b) => a.wrapping_shr(b as u32); | ||
} | ||
} | ||
simple_shift_op! { | ||
ashr, | ||
sint: shift_right_arithmetic, | ||
fold_const { | ||
sint(a, b) => a.wrapping_shr(b as u32); | ||
} | ||
} |
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 should be possible to show in a test that these misbehave - see #317 (comment)
b06ba9c
to
603c5fc
Compare
prevent const folding from hiding issues
603c5fc
to
d33b067
Compare
see #300
const folding used to only support:
This PR adds const folding for:
icmp
)Should probably add some diff tests