const-eval: always do mem-to-mem copies if there might be padding involved#148967
Conversation
|
rustbot has assigned @JonathanBrouwer. Use |
|
@bors try |
This comment has been minimized.
This comment has been minimized.
…try> const-eval: always do mem-to-mem copies if there might be padding involved
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
c4acb77 to
01194d7
Compare
This comment has been minimized.
This comment has been minimized.
|
Finished benchmarking commit (78c81ee): comparison URL. Overall result: ❌✅ regressions and improvements - please read the text belowBenchmarking this pull request means it may be perf-sensitive – we'll automatically label it not fit for rolling up. You can override this, but we strongly advise not to, due to possible changes in compiler perf. Next Steps: If you can justify the regressions found in this try perf run, please do so in sufficient writing along with @bors rollup=never Instruction countOur most reliable metric. Used to determine the overall result above. However, even this metric can be noisy.
Max RSS (memory usage)Results (primary -3.2%)A less reliable metric. May be of interest, but not used to determine the overall result above.
CyclesResults (primary -2.7%, secondary -9.4%)A less reliable metric. May be of interest, but not used to determine the overall result above.
Binary sizeResults (primary -1.1%, secondary 0.0%)A less reliable metric. May be of interest, but not used to determine the overall result above.
Bootstrap: 472.272s -> 472.014s (-0.05%) |
|
Uh okay I guess this is actually good for perf.^^ At least for the benchmarks we have. The copy apparently gets a little cheaper, but we force more things to use the less efficient in-memory representation. The latter just does not seem to matter in our benchmarks.
Just to be safe:
@craterbot check
|
|
👌 Experiment ℹ️ Crater is a tool to run experiments across parts of the Rust ecosystem. Learn more |
01194d7 to
472364c
Compare
This comment has been minimized.
This comment has been minimized.
|
Most of the performance regressions are from the coercions benchmark. All it does is create an array of a large number of string literals in const. Why did this benchmark's performance regress? There is no padding involved in any of the types. |
Let's add examples and explanatory notes to clarify the restriction that the representation of the final value of a constant or static initializer must only contain bytes with provenance in whole-pointer groups. We'll add a `compile_fail` example demonstrating how storing a pointer that extends into padding creates pointer fragments in the final value, causing compilation to fail and show to work around this by explicitly zeroing the padding bytes. Let's extend the existing note about uninitialized padding bytes to provide deeper intuition about this restriction and explain how constant evaluation makes the details of typed copies observable (whether field-by-field or memory-block), how these details are not yet fully specified in Rust, and why the compiler must be allowed to reject initializers with uninitialized padding bytes to preserve future flexibility (such as always setting padding to uninitialized). Context: rust-lang/rust#148470, rust-lang/rust#148967
Let's add examples and explanatory notes to clarify the restriction that the representation of the final value of a constant or static initializer must only contain bytes with provenance in whole-pointer groups. We'll add a `compile_fail` example demonstrating how storing a pointer that extends into padding creates pointer fragments in the final value, causing compilation to fail and show to work around this by explicitly zeroing the padding bytes. Let's extend the existing note about uninitialized padding bytes to provide deeper intuition about this restriction and explain how constant evaluation makes the details of typed copies observable (whether field-by-field or memory-block), how these details are not yet fully specified in Rust, and why the compiler must be allowed to reject initializers with uninitialized padding bytes to preserve future flexibility (such as always setting padding to uninitialized). Context: rust-lang/rust#148470, rust-lang/rust#148967
Let's add examples and explanatory notes to clarify the restriction that the representation of the final value of a constant or static initializer must only contain bytes with provenance in whole-pointer groups. We'll add a `compile_fail` example demonstrating how storing a pointer that extends into padding creates pointer fragments in the final value, causing compilation to fail and show to work around this by explicitly zeroing the padding bytes. Let's extend the existing note about uninitialized padding bytes to provide deeper intuition about this restriction and explain how constant evaluation makes the details of typed copies observable (whether field-by-field or memory-block), how these details are not yet fully specified in Rust, and why the compiler must be allowed to reject initializers with uninitialized padding bytes to preserve future flexibility (such as always setting padding to uninitialized). Context: rust-lang/rust#148470, rust-lang/rust#148967
Let's add examples and explanatory notes to clarify the restriction that the representation of the final value of a constant or static initializer must only contain bytes with provenance in whole-pointer groups. We'll add a `compile_fail` example demonstrating how storing a pointer that extends into padding creates pointer fragments in the final value, causing compilation to fail and show to work around this by explicitly zeroing the padding bytes. Let's extend the existing note about uninitialized padding bytes to provide deeper intuition about this restriction and explain how constant evaluation makes the details of typed copies observable (whether field-by-field or memory-block), how these details are not yet fully specified in Rust, and why the compiler must be allowed to reject initializers with uninitialized padding bytes to preserve future flexibility (such as always setting padding to uninitialized). Context: rust-lang/rust#148470, rust-lang/rust#148967
Let's add examples and explanatory notes to clarify the restriction that the representation of the final value of a constant or static initializer must only contain bytes with provenance in whole-pointer groups. We'll add a `compile_fail` example demonstrating how storing a pointer that extends into padding creates pointer fragments in the final value, causing compilation to fail and show to work around this by explicitly zeroing the padding bytes. Let's extend the existing note about uninitialized padding bytes to provide deeper intuition about this restriction and explain how constant evaluation makes the details of typed copies observable (whether field-by-field or memory-block), how these details are not yet fully specified in Rust, and why the compiler must be allowed to reject initializers with uninitialized padding bytes to preserve future flexibility (such as always setting padding to uninitialized). Context: - rust-lang/rust#148470 - rust-lang/rust#148967
|
@bors rollup |
Let's add examples and explanatory notes to clarify the restriction that the representation of the final value of a constant or static initializer must only contain bytes with provenance in whole-pointer groups. We'll add a `compile_fail` example demonstrating how storing a pointer that extends into padding creates pointer fragments in the final value, causing compilation to fail and show to work around this by explicitly zeroing the padding bytes. Let's extend the existing note about uninitialized padding bytes to provide deeper intuition about this restriction and explain how constant evaluation makes the details of typed copies observable (whether field-by-field or memory-block), how these details are not yet fully specified in Rust, and why the compiler must be allowed to reject initializers with uninitialized padding bytes to preserve future flexibility (such as always setting padding to uninitialized). Context: - rust-lang/rust#148470 - rust-lang/rust#148967
…adding, r=JonathanBrouwer,traviscross const-eval: always do mem-to-mem copies if there might be padding involved This is the final piece of the puzzle for rust-lang#148470: when copying data of a type that has padding, always do a mem-to-mem copy, so that we always preserve the source padding exactly. That prevents rustc implementation choices from leaking into user-visible behavior. This is technically a breaking change: the example at the top of rust-lang#148470 no longer compiles with this. However, it seems very unlikely that anyone would have depended on this. My main concern is not backwards compatibility, it is performance. Fixes rust-lang#148470 --- > Actually that seems to be entirely fine, it even helps with some benchmarks! I guess the mem-to-mem codepath is actually faster than the scalar pair codepath for the copy itself. It can slow things down later since now we have to do everything bytewise, but that doesn't show up in our benchmarks and might not be very relevant after all (in particular, it only affects types with padding, so the rather common wide pointers still always use the efficient scalar representation). > > So that would be my proposal to for resolving this issue then: to make const-eval behavior consistent, we always copy the padding from the source to the target. IOW, potentially pre-existing provenance in the target always gets overwritten (that part is already in rust-lang#148259), and potentially existing provenance in padding in the source always gets carried over (that's rust-lang#148967). If there's provenance elsewhere in the source our existing handling is fine: > - If it's in an integer, that's UB during const-eval so we can do whatever. > - If it's in a pointer, the the fragments must combine back together to a pointer or else we have UB. > - If it's in a union we just carry it over unchanged. > > @traviscross we should check that this special const-eval-only UB is properly reflected in the reference. Currently we have [this](https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html#r-undefined.const-transmute-ptr2int) but that only talks about int2ptr, not about invalid pointer fragments at pointer type. I also wonder if this shouldn't rather be part of ["invalid values"](https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html#r-undefined.validity) to make it clear that this applies recursively inside fields as well. > EDIT: Reference PR is up at rust-lang/reference#2091. _Originally posted by @RalfJung in [rust-lang#148470](rust-lang#148470 (comment) > Worth noting that this does not resolve the concerns @theemathas had about `-Zextra-const-ub-checks` sometimes causing *more* code to compile. Specifically, with that flag, the behavior changes to "potentially existing provenance in padding in the source never gets carried over". However, it's a nightly-only flag (used by Miri) so while the behavior is odd, I don't think this is a problem. _Originally posted by @RalfJung in [rust-lang#148470](rust-lang#148470 (comment) --- Related: - rust-lang#148470 - rust-lang/reference#2091
…uwer Rollup of 7 pull requests Successful merges: - #148967 (const-eval: always do mem-to-mem copies if there might be padding involved) - #152012 (Use `DEVELOPER_DIR` instead of a custom `xcode-select` script) - #152044 (Convert to inline diagnostics in `rustc_incremental`) - #152046 (Use glob imports for attribute parsers) - #152054 (Distinguish error message for `#[diagnostic::on_const]` on const trait impls) - #152059 (Fix some autodiff tests require Clto=fat) - #152073 (Convert to inline diagnostics in `rustc_mir_dataflow`)
Rollup merge of #148967 - RalfJung:const-eval-preserve-src-padding, r=JonathanBrouwer,traviscross const-eval: always do mem-to-mem copies if there might be padding involved This is the final piece of the puzzle for #148470: when copying data of a type that has padding, always do a mem-to-mem copy, so that we always preserve the source padding exactly. That prevents rustc implementation choices from leaking into user-visible behavior. This is technically a breaking change: the example at the top of #148470 no longer compiles with this. However, it seems very unlikely that anyone would have depended on this. My main concern is not backwards compatibility, it is performance. Fixes #148470 --- > Actually that seems to be entirely fine, it even helps with some benchmarks! I guess the mem-to-mem codepath is actually faster than the scalar pair codepath for the copy itself. It can slow things down later since now we have to do everything bytewise, but that doesn't show up in our benchmarks and might not be very relevant after all (in particular, it only affects types with padding, so the rather common wide pointers still always use the efficient scalar representation). > > So that would be my proposal to for resolving this issue then: to make const-eval behavior consistent, we always copy the padding from the source to the target. IOW, potentially pre-existing provenance in the target always gets overwritten (that part is already in #148259), and potentially existing provenance in padding in the source always gets carried over (that's #148967). If there's provenance elsewhere in the source our existing handling is fine: > - If it's in an integer, that's UB during const-eval so we can do whatever. > - If it's in a pointer, the the fragments must combine back together to a pointer or else we have UB. > - If it's in a union we just carry it over unchanged. > > @traviscross we should check that this special const-eval-only UB is properly reflected in the reference. Currently we have [this](https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html#r-undefined.const-transmute-ptr2int) but that only talks about int2ptr, not about invalid pointer fragments at pointer type. I also wonder if this shouldn't rather be part of ["invalid values"](https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html#r-undefined.validity) to make it clear that this applies recursively inside fields as well. > EDIT: Reference PR is up at rust-lang/reference#2091. _Originally posted by @RalfJung in [#148470](#148470 (comment) > Worth noting that this does not resolve the concerns @theemathas had about `-Zextra-const-ub-checks` sometimes causing *more* code to compile. Specifically, with that flag, the behavior changes to "potentially existing provenance in padding in the source never gets carried over". However, it's a nightly-only flag (used by Miri) so while the behavior is odd, I don't think this is a problem. _Originally posted by @RalfJung in [#148470](#148470 (comment) --- Related: - #148470 - rust-lang/reference#2091
Let's add examples and explanatory notes to clarify the restriction that the representation of the final value of a constant or static initializer must only contain bytes with provenance in whole-pointer groups. We'll add a `compile_fail` example demonstrating how storing a pointer that extends into padding creates pointer fragments in the final value, causing compilation to fail and show to work around this by explicitly zeroing the padding bytes. Let's extend the existing note about uninitialized padding bytes to provide deeper intuition about this restriction and explain how constant evaluation makes the details of typed copies observable (whether field-by-field or memory-block), how these details are not yet fully specified in Rust, and why the compiler must be allowed to reject initializers with uninitialized padding bytes to preserve future flexibility (such as always setting padding to uninitialized). Context: - rust-lang/rust#148470 - rust-lang/rust#148967
Let's add examples and explanatory notes to clarify the restriction that the representation of the final value of a constant or static initializer must only contain bytes with provenance in whole-pointer groups. We'll add a `compile_fail` example demonstrating how storing a pointer that extends into padding creates pointer fragments in the final value, causing compilation to fail and show to work around this by explicitly zeroing the padding bytes. Let's extend the existing note about uninitialized padding bytes to provide deeper intuition about this restriction and explain how constant evaluation makes the details of typed copies observable (whether field-by-field or memory-block), how these details are not yet fully specified in Rust, and why the compiler must be allowed to reject initializers with uninitialized padding bytes to preserve future flexibility (such as always setting padding to uninitialized). Context: - rust-lang/rust#148470 - rust-lang/rust#148967
Let's add examples and explanatory notes to clarify the restriction that the representation of the final value of a constant or static initializer must only contain bytes with provenance in whole-pointer groups. We'll add a `compile_fail` example demonstrating how storing a pointer that extends into padding creates pointer fragments in the final value, causing compilation to fail and show to work around this by explicitly zeroing the padding bytes. Let's extend the existing note about uninitialized padding bytes to provide deeper intuition about this restriction and explain how constant evaluation makes the details of typed copies observable (whether field-by-field or memory-block), how these details are not yet fully specified in Rust, and why the compiler must be allowed to reject initializers with uninitialized padding bytes to preserve future flexibility (such as always setting padding to uninitialized). Context: - rust-lang/rust#148470 - rust-lang/rust#148967
Let's add examples and explanatory notes to clarify the restriction that the representation of the final value of a constant or static initializer must only contain bytes with provenance in whole-pointer groups. We'll add a `compile_fail` example demonstrating how storing a pointer that extends into padding creates pointer fragments in the final value, causing compilation to fail and show to work around this by explicitly zeroing the padding bytes. Let's extend the existing note about uninitialized padding bytes to provide deeper intuition about this restriction and explain how constant evaluation makes the details of typed copies observable (whether field-by-field or memory-block), how these details are not yet fully specified in Rust, and why the compiler must be allowed to reject initializers with uninitialized padding bytes to preserve future flexibility (such as always setting padding to uninitialized). Context: - rust-lang/rust#148470 - rust-lang/rust#148967
|
@rust-timer build 67ecc0e |
This comment has been minimized.
This comment has been minimized.
Let's add examples and explanatory notes to clarify the restriction that the representation of the final value of a constant or static initializer must only contain bytes with provenance in whole-pointer groups. We'll add a `compile_fail` example demonstrating how storing a pointer that extends into padding creates pointer fragments in the final value, causing compilation to fail and show to work around this by explicitly zeroing the padding bytes. Let's extend the existing note about uninitialized padding bytes to provide deeper intuition about this restriction and explain how constant evaluation makes the details of typed copies observable (whether field-by-field or memory-block), how these details are not yet fully specified in Rust, and why the compiler must be allowed to reject initializers with uninitialized padding bytes to preserve future flexibility (such as always setting padding to uninitialized). Context: - rust-lang/rust#148470 - rust-lang/rust#148967
Let's add examples and explanatory notes to clarify the restriction that the representation of the final value of a constant or static initializer must only contain bytes with provenance in whole-pointer groups. We'll add a `compile_fail` example demonstrating how storing a pointer that extends into padding creates pointer fragments in the final value, causing compilation to fail and show to work around this by explicitly zeroing the padding bytes. Let's extend the existing note about uninitialized padding bytes to provide deeper intuition about this restriction and explain how constant evaluation makes the details of typed copies observable (whether field-by-field or memory-block), how these details are not yet fully specified in Rust, and why the compiler must be allowed to reject initializers with uninitialized padding bytes to preserve future flexibility (such as always setting padding to uninitialized). Context: - rust-lang/rust#148470 - rust-lang/rust#148967
|
Finished benchmarking commit (67ecc0e): comparison URL. Overall result: ❌✅ regressions and improvements - please read the text belowBenchmarking this pull request means it may be perf-sensitive – we'll automatically label it not fit for rolling up. You can override this, but we strongly advise not to, due to possible changes in compiler perf. Next Steps: If you can justify the regressions found in this try perf run, please do so in sufficient writing along with @bors rollup=never Instruction countOur most reliable metric. Used to determine the overall result above. However, even this metric can be noisy.
Max RSS (memory usage)Results (secondary 4.3%)A less reliable metric. May be of interest, but not used to determine the overall result above.
CyclesResults (secondary -0.7%)A less reliable metric. May be of interest, but not used to determine the overall result above.
Binary sizeThis benchmark run did not return any relevant results for this metric. Bootstrap: 475.191s -> 477.005s (0.38%) |
|
Sadly perf is a bit more negative than it was before. |
This is the final piece of the puzzle for #148470: when copying data of a type that has padding, always do a mem-to-mem copy, so that we always preserve the source padding exactly. That prevents rustc implementation choices from leaking into user-visible behavior.
This is technically a breaking change: the example at the top of #148470 no longer compiles with this. However, it seems very unlikely that anyone would have depended on this. My main concern is not backwards compatibility, it is performance.
Fixes #148470
Originally posted by @RalfJung in #148470
Originally posted by @RalfJung in #148470
Related: