-
Notifications
You must be signed in to change notification settings - Fork 13.3k
Don't reset cast kind without also updating the operand in simplify_cast
in GVN
#136450
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
Some changes occurred to MIR optimizations cc @rust-lang/wg-mir-opt |
simplify_cast
in GVN
@bors try @rust-timer queue |
This comment has been minimized.
This comment has been minimized.
Don't reset cast kind without also updating the operand in `simplify_cast` in GVN Consider this heavily elided segment of the pre-GVN example code that was committed as a test: ``` let _4: *const (); let _5: *const [()]; let mut _6: *const (); let _7: *mut (); let mut _8: *const [()]; let mut _9: std::boxed::Box<()>; let mut _10: *const (); /* ... */ _10 = copy ((_9.0: std::ptr::Unique<()>).0: std::ptr::NonNull<()>) as *const () (Transmute); _4 = copy _10; _6 = copy _4; _5 = *const [()] from (copy _6, copy _11); _8 = copy _5; _7 = copy _8 as *mut () (PtrToPtr); ``` A malformed optimization was changing `_7` to: ``` _7 = copy _5 as *mut () (Transmute); ``` (where `_8` was just replaced with `_5` bc of simple copy propagation, that part is not important... the CastKind changing to Transmute is the important part here). In rust-lang#133324, two new functionalities were implemented: * Peeking through unsized -> sized PtrToPtr casts whose operand is `AggregateKind::RawPtr`, to turn it into PtrToPtr casts of the base of the aggregate. In this case, this allows us to see that the value of `_7` is just a ptr-to-ptr cast of `_6`. * Folding a PtrToPtr cast of an operand which is a Transmute cast into just a single Transmute, which (theoretically) allows us to treat `_7` as a transmute into `*mut ()` of the base of the cast of `_10`, which is the place projection of `((_9.0: std::ptr::Unique<()>).0: std::ptr::NonNull<()>)`. However, when applying those two subsequent optimizations, we must *not* update the CastKind of the final cast *unless* we also update the operand of the cast, since the operand may no longer make sense with the updated CastKind. In this case, this is problematic because the type of `_8` is `*const [()]`, but that operand in assignment statement of `_7` does *not* get turned into something like `((_9.0: std::ptr::Unique<()>).0: std::ptr::NonNull<()>)` -- **in other words, `try_to_operand` fails** -- because GVN only turns value nodes into locals or consts, not projections of locals. So we fail to update the operand, but we still update the CastKind to Transmute, which means we now are transmuting types of different sizes (a wide pointer and a thin pointer). r? `@scottmcm` or `@cjgillot` Fixes rust-lang#136361
☀️ Try build successful - checks-actions |
This comment has been minimized.
This comment has been minimized.
Finished benchmarking commit (eed322b): comparison URL. Overall result: no relevant changes - no action neededBenchmarking this pull request likely means that it is perf-sensitive, so we're automatically marking it as not fit for rolling up. While you can manually mark this PR as fit for rollup, we strongly recommend not doing so since this PR may lead to changes in compiler perf. @bors rollup=never Instruction countThis benchmark run did not return any relevant results for this metric. Max RSS (memory usage)This benchmark run did not return any relevant results for this metric. CyclesResults (secondary -5.8%)This is a less reliable metric that may be of interest but was not used to determine the overall result at the top of this comment.
Binary sizeThis benchmark run did not return any relevant results for this metric. Bootstrap: 778.752s -> 778.669s (-0.01%) |
Anyone want to give this a review? |
I was deferring to others who know GVN better but I'll take care of this evening if nobody else r? saethlin |
🧐 I see @bors r+ I think this miscompile is on beta, right? |
@bors p=5 (threading between rollups) |
…thlin Don't reset cast kind without also updating the operand in `simplify_cast` in GVN Consider this heavily elided segment of the pre-GVN example code that was committed as a test: ```rust let _4: *const (); let _5: *const [()]; let mut _6: *const (); let _7: *mut (); let mut _8: *const [()]; let mut _9: std::boxed::Box<()>; let mut _10: *const (); /* ... */ // Deref a box _10 = copy ((_9.0: std::ptr::Unique<()>).0: std::ptr::NonNull<()>) as *const () (Transmute); _4 = copy _10; _6 = copy _4; // Inlined body of `slice::from_raw_parts`, to turn a unit pointer into a slice-of-unit pointer _5 = *const [()] from (copy _6, copy _11); _8 = copy _5; // Cast the raw slice-of-unit pointer back to a unit pointer _7 = copy _8 as *mut () (PtrToPtr); ``` A malformed optimization was changing `_7` (which casted the slice-of-unit ptr to a unit ptr) to: ``` _7 = copy _5 as *mut () (Transmute); ``` ...where `_8` was just replaced with `_5` bc of simple copy propagation, that part is not important... the CastKind changing to Transmute is the important part here. In rust-lang#133324, two new functionalities were implemented: * Peeking through unsized -> sized PtrToPtr casts whose operand is `AggregateKind::RawPtr`, to turn it into PtrToPtr casts of the base of the aggregate. In this case, this allows us to see that the value of `_7` is just a ptr-to-ptr cast of `_6`. * Folding a PtrToPtr cast of an operand which is a Transmute cast into just a single Transmute, which (theoretically) allows us to treat `_7` as a transmute into `*mut ()` of the base of the cast of `_10`, which is the place projection of `((_9.0: std::ptr::Unique<()>).0: std::ptr::NonNull<()>)`. However, when applying those two subsequent optimizations, we must *not* update the CastKind of the final cast *unless* we also update the operand of the cast, since the operand may no longer make sense with the updated CastKind. In this case, this is problematic because the type of `_8` is `*const [()]`, but that operand in assignment statement of `_7` does *not* get turned into something like `((_9.0: std::ptr::Unique<()>).0: std::ptr::NonNull<()>)` -- **in other words, `try_to_operand` fails** -- because GVN only turns value nodes into locals or consts, not projections of locals. So we fail to update the operand, but we still update the CastKind to Transmute, which means we now are transmuting types of different sizes (a wide pointer and a thin pointer). r? `@scottmcm` or `@cjgillot` Fixes rust-lang#136361 Fixes rust-lang#135997
269340f
to
de7d4a8
Compare
|
@bors r=saethlin |
…thlin Don't reset cast kind without also updating the operand in `simplify_cast` in GVN Consider this heavily elided segment of the pre-GVN example code that was committed as a test: ```rust let _4: *const (); let _5: *const [()]; let mut _6: *const (); let _7: *mut (); let mut _8: *const [()]; let mut _9: std::boxed::Box<()>; let mut _10: *const (); /* ... */ // Deref a box _10 = copy ((_9.0: std::ptr::Unique<()>).0: std::ptr::NonNull<()>) as *const () (Transmute); _4 = copy _10; _6 = copy _4; // Inlined body of `slice::from_raw_parts`, to turn a unit pointer into a slice-of-unit pointer _5 = *const [()] from (copy _6, copy _11); _8 = copy _5; // Cast the raw slice-of-unit pointer back to a unit pointer _7 = copy _8 as *mut () (PtrToPtr); ``` A malformed optimization was changing `_7` (which casted the slice-of-unit ptr to a unit ptr) to: ``` _7 = copy _5 as *mut () (Transmute); ``` ...where `_8` was just replaced with `_5` bc of simple copy propagation, that part is not important... the CastKind changing to Transmute is the important part here. In rust-lang#133324, two new functionalities were implemented: * Peeking through unsized -> sized PtrToPtr casts whose operand is `AggregateKind::RawPtr`, to turn it into PtrToPtr casts of the base of the aggregate. In this case, this allows us to see that the value of `_7` is just a ptr-to-ptr cast of `_6`. * Folding a PtrToPtr cast of an operand which is a Transmute cast into just a single Transmute, which (theoretically) allows us to treat `_7` as a transmute into `*mut ()` of the base of the cast of `_10`, which is the place projection of `((_9.0: std::ptr::Unique<()>).0: std::ptr::NonNull<()>)`. However, when applying those two subsequent optimizations, we must *not* update the CastKind of the final cast *unless* we also update the operand of the cast, since the operand may no longer make sense with the updated CastKind. In this case, this is problematic because the type of `_8` is `*const [()]`, but that operand in assignment statement of `_7` does *not* get turned into something like `((_9.0: std::ptr::Unique<()>).0: std::ptr::NonNull<()>)` -- **in other words, `try_to_operand` fails** -- because GVN only turns value nodes into locals or consts, not projections of locals. So we fail to update the operand, but we still update the CastKind to Transmute, which means we now are transmuting types of different sizes (a wide pointer and a thin pointer). r? `@scottmcm` or `@cjgillot` Fixes rust-lang#136361 Fixes rust-lang#135997
💔 Test failed - checks-actions |
@bors retry |
☀️ Test successful - checks-actions |
Finished benchmarking commit (550e035): comparison URL. Overall result: no relevant changes - no action needed@rustbot label: -perf-regression Instruction countThis benchmark run did not return any relevant results for this metric. Max RSS (memory usage)Results (primary 1.1%, secondary 2.2%)This is a less reliable metric that may be of interest but was not used to determine the overall result at the top of this comment.
CyclesResults (secondary 2.8%)This is a less reliable metric that may be of interest but was not used to determine the overall result at the top of this comment.
Binary sizeThis benchmark run did not return any relevant results for this metric. Bootstrap: 779.478s -> 778.095s (-0.18%) |
@@ -1489,10 +1490,11 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { | |||
} | |||
|
|||
if was_ever_updated && let Some(op) = self.try_as_operand(value, location) { | |||
*operand = op; | |||
*initial_operand = op; | |||
*initial_kind = kind; |
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.
Ah, moving these here is smart. Nice job!
Thanks for picking this up, compiler-errors.
…compiler-errors Emit MIR for each bit with on `dont_reset_cast_kind_without_updating_operand` PR rust-lang#136450 introduced a diff that includes a pointer-sized alloc. This doesn't cause any problems on the compiler test suite but it affects the test suite that ferrocene has for `aarch64-unknown-none` as the snapshot of the diff only includes a 32-bit alloc even though this should be a 64-bit alloc on `aarch64-unknown-none`. r? `@compiler-errors`
…compiler-errors Emit MIR for each bit with on `dont_reset_cast_kind_without_updating_operand` PR rust-lang#136450 introduced a diff that includes a pointer-sized alloc. This doesn't cause any problems on the compiler test suite but it affects the test suite that ferrocene has for `aarch64-unknown-none` as the snapshot of the diff only includes a 32-bit alloc even though this should be a 64-bit alloc on `aarch64-unknown-none`. r? ``@compiler-errors``
Rollup merge of rust-lang#137007 - pvdrz:fix-aarch64-alloc-layout, r=compiler-errors Emit MIR for each bit with on `dont_reset_cast_kind_without_updating_operand` PR rust-lang#136450 introduced a diff that includes a pointer-sized alloc. This doesn't cause any problems on the compiler test suite but it affects the test suite that ferrocene has for `aarch64-unknown-none` as the snapshot of the diff only includes a 32-bit alloc even though this should be a 64-bit alloc on `aarch64-unknown-none`. r? ``@compiler-errors``
Consider this heavily elided segment of the pre-GVN example code that was committed as a test:
A malformed optimization was changing
_7
(which casted the slice-of-unit ptr to a unit ptr) to:...where
_8
was just replaced with_5
bc of simple copy propagation, that part is not important... the CastKind changing to Transmute is the important part here.In #133324, two new functionalities were implemented:
AggregateKind::RawPtr
, to turn it into PtrToPtr casts of the base of the aggregate. In this case, this allows us to see that the value of_7
is just a ptr-to-ptr cast of_6
._7
as a transmute into*mut ()
of the base of the cast of_10
, which is the place projection of((_9.0: std::ptr::Unique<()>).0: std::ptr::NonNull<()>)
.However, when applying those two subsequent optimizations, we must not update the CastKind of the final cast unless we also update the operand of the cast, since the operand may no longer make sense with the updated CastKind.
In this case, this is problematic because the type of
_8
is*const [()]
, but that operand in assignment statement of_7
does not get turned into something like((_9.0: std::ptr::Unique<()>).0: std::ptr::NonNull<()>)
-- in other words,try_to_operand
fails -- because GVN only turns value nodes into locals or consts, not projections of locals. So we fail to update the operand, but we still update the CastKind to Transmute, which means we now are transmuting types of different sizes (a wide pointer and a thin pointer).r? @scottmcm or @cjgillot
Fixes #136361
Fixes #135997