-
Notifications
You must be signed in to change notification settings - Fork 13.4k
Make checked
ops emit *unchecked* LLVM operations where feasible
#124114
New issue
Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? # to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,86 @@ | ||
//@ compile-flags: -O -Z merge-functions=disabled | ||
|
||
#![crate_type = "lib"] | ||
#![feature(unchecked_shifts)] | ||
|
||
// Because the result of something like `u32::checked_sub` can only be used if it | ||
// didn't overflow, make sure that LLVM actually knows that in optimized builds. | ||
// Thanks to poison semantics, this doesn't even need branches. | ||
|
||
// CHECK-LABEL: @checked_sub_unsigned | ||
// CHECK-SAME: (i16 noundef %a, i16 noundef %b) | ||
#[no_mangle] | ||
pub fn checked_sub_unsigned(a: u16, b: u16) -> Option<u16> { | ||
// CHECK-DAG: %[[IS_SOME:.+]] = icmp uge i16 %a, %b | ||
// CHECK-DAG: %[[DIFF_P:.+]] = sub nuw i16 %a, %b | ||
// CHECK-DAG: %[[DISCR:.+]] = zext i1 %[[IS_SOME]] to i16 | ||
// CHECK-DAG: %[[DIFF_U:.+]] = select i1 %[[IS_SOME]], i16 %[[DIFF_P]], i16 undef | ||
|
||
// CHECK: %[[R0:.+]] = insertvalue { i16, i16 } poison, i16 %[[DISCR]], 0 | ||
// CHECK: %[[R1:.+]] = insertvalue { i16, i16 } %[[R0]], i16 %[[DIFF_U]], 1 | ||
// CHECK: ret { i16, i16 } %[[R1]] | ||
a.checked_sub(b) | ||
} | ||
|
||
// Note that `shl` and `shr` in LLVM are already unchecked. So rather than | ||
// looking for no-wrap flags, we just need there to not be any masking. | ||
Comment on lines
+25
to
+26
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You can see the masking in optimized LLVM IR and optimized MIR today: https://rust.godbolt.org/z/x5Podh9z5 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. looks like the same assembly emitted, tho'. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Well, on x86 the shift instructions are wrapping. To see a difference you'd need to make an example where LLVM can optimize based on knowing that the shift is in-range. (Just like how There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That's a good point! I tried an architecture that actually has a slightly more annoying shift instruction in this regard, PowerPC! (specifically old_checked_sub:
clrldi 5, 4, 32
clrlwi 4, 4, 27
addi 5, 5, -32
srw 4, 3, 4
rldicl 5, 5, 1, 63
mr 3, 5
blr
.long 0
.quad 0
new_checked_sub:
clrldi 5, 4, 32
slw 4, 3, 4
addi 5, 5, -32
rldicl 5, 5, 1, 63
mr 3, 5
blr
.long 0
.quad 0 One less instruction! |
||
|
||
// CHECK-LABEL: @checked_shl_unsigned | ||
// CHECK-SAME: (i32 noundef %a, i32 noundef %b) | ||
#[no_mangle] | ||
pub fn checked_shl_unsigned(a: u32, b: u32) -> Option<u32> { | ||
// CHECK-DAG: %[[IS_SOME:.+]] = icmp ult i32 %b, 32 | ||
// CHECK-DAG: %[[SHIFTED_P:.+]] = shl i32 %a, %b | ||
// CHECK-DAG: %[[DISCR:.+]] = zext i1 %[[IS_SOME]] to i32 | ||
// CHECK-DAG: %[[SHIFTED_U:.+]] = select i1 %[[IS_SOME]], i32 %[[SHIFTED_P]], i32 undef | ||
|
||
// CHECK: %[[R0:.+]] = insertvalue { i32, i32 } poison, i32 %[[DISCR]], 0 | ||
// CHECK: %[[R1:.+]] = insertvalue { i32, i32 } %[[R0]], i32 %[[SHIFTED_U]], 1 | ||
// CHECK: ret { i32, i32 } %[[R1]] | ||
a.checked_shl(b) | ||
} | ||
|
||
// CHECK-LABEL: @checked_shr_unsigned | ||
// CHECK-SAME: (i32 noundef %a, i32 noundef %b) | ||
#[no_mangle] | ||
pub fn checked_shr_unsigned(a: u32, b: u32) -> Option<u32> { | ||
// CHECK-DAG: %[[IS_SOME:.+]] = icmp ult i32 %b, 32 | ||
// CHECK-DAG: %[[SHIFTED_P:.+]] = lshr i32 %a, %b | ||
// CHECK-DAG: %[[DISCR:.+]] = zext i1 %[[IS_SOME]] to i32 | ||
// CHECK-DAG: %[[SHIFTED_U:.+]] = select i1 %[[IS_SOME]], i32 %[[SHIFTED_P]], i32 undef | ||
|
||
// CHECK: %[[R0:.+]] = insertvalue { i32, i32 } poison, i32 %[[DISCR]], 0 | ||
// CHECK: %[[R1:.+]] = insertvalue { i32, i32 } %[[R0]], i32 %[[SHIFTED_U]], 1 | ||
// CHECK: ret { i32, i32 } %[[R1]] | ||
a.checked_shr(b) | ||
} | ||
|
||
// CHECK-LABEL: @checked_shl_signed | ||
// CHECK-SAME: (i32 noundef %a, i32 noundef %b) | ||
#[no_mangle] | ||
pub fn checked_shl_signed(a: i32, b: u32) -> Option<i32> { | ||
// CHECK-DAG: %[[IS_SOME:.+]] = icmp ult i32 %b, 32 | ||
// CHECK-DAG: %[[SHIFTED_P:.+]] = shl i32 %a, %b | ||
// CHECK-DAG: %[[DISCR:.+]] = zext i1 %[[IS_SOME]] to i32 | ||
// CHECK-DAG: %[[SHIFTED_U:.+]] = select i1 %[[IS_SOME]], i32 %[[SHIFTED_P]], i32 undef | ||
|
||
// CHECK: %[[R0:.+]] = insertvalue { i32, i32 } poison, i32 %[[DISCR]], 0 | ||
// CHECK: %[[R1:.+]] = insertvalue { i32, i32 } %[[R0]], i32 %[[SHIFTED_U]], 1 | ||
// CHECK: ret { i32, i32 } %[[R1]] | ||
a.checked_shl(b) | ||
} | ||
|
||
// CHECK-LABEL: @checked_shr_signed | ||
// CHECK-SAME: (i32 noundef %a, i32 noundef %b) | ||
#[no_mangle] | ||
pub fn checked_shr_signed(a: i32, b: u32) -> Option<i32> { | ||
// CHECK-DAG: %[[IS_SOME:.+]] = icmp ult i32 %b, 32 | ||
// CHECK-DAG: %[[SHIFTED_P:.+]] = ashr i32 %a, %b | ||
// CHECK-DAG: %[[DISCR:.+]] = zext i1 %[[IS_SOME]] to i32 | ||
// CHECK-DAG: %[[SHIFTED_U:.+]] = select i1 %[[IS_SOME]], i32 %[[SHIFTED_P]], i32 undef | ||
|
||
// CHECK: %[[R0:.+]] = insertvalue { i32, i32 } poison, i32 %[[DISCR]], 0 | ||
// CHECK: %[[R1:.+]] = insertvalue { i32, i32 } %[[R0]], i32 %[[SHIFTED_U]], 1 | ||
// CHECK: ret { i32, i32 } %[[R1]] | ||
a.checked_shr(b) | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,5 @@ | ||
// skip-filecheck | ||
//@ compile-flags: -O -Zmir-opt-level=2 -Cdebuginfo=2 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. why this change? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Well, first I was going to say for consistency with the other pre-codegen tests, but it looks like they're not as consistent as I thought. The mem-replace and range-iter tests don't ask for debuginfo, but then the slice-filter and chained-comparison ones do :/ I guess it's because I think it's more common for I could put it back as it was if you'd like. It just adds 4 more There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't mind it, either way, tbh, I just wanted to know the reasoning so that I know it wasn't Perfectly Random. |
||
//@ compile-flags: -O -Zmir-opt-level=2 | ||
// EMIT_MIR_FOR_EACH_PANIC_STRATEGY | ||
|
||
#![crate_type = "lib"] | ||
|
Uh oh!
There was an error while loading. Please reload this page.