Uh oh!
There was an error while loading. Please reload this page.
Support allocation failures when interpreting MIR - #86255
Conversation
rust-highfive
commented
Jun 13, 2021
Some changes occured to the CTFE / Miri engine cc @rust-lang/miri Some changes occured to the CTFE / Miri engine cc @rust-lang/miri |
rust-highfive
commented
Jun 13, 2021
r? @varkor (rust-highfive has picked a reviewer for you, use r? to override) |
RalfJung
commented
Jun 13, 2021
The original plan we had for having a memory limit in Miri was to make this a configurable limit. The other two limits (stack size, execution steps) are completely controlled by the interpreter. The idea is that interpretation should be completely deterministic and repeatable. This new error, however, depends on arbitrary factors from the environment: the same evaluation can sometimes succeed, and sometimes fail with |
oli-obk
commented
Jun 13, 2021
We kind of do have precedent. Consider |
varkor
commented
Jun 13, 2021
r? @oli-obk |
Uh oh!
There was an error while loading. Please reload this page.
RalfJung
commented
Jun 13, 2021
Maybe? I don't know the invariants of the query system well enough. But this can lead to the same CTFE query having different results in different crates. |
oli-obk
commented
Jun 13, 2021
oh right. There's no precedent for that. Maybe this should be a fatal error reported directly at the problematic site instead of it being a regular CTFE error? So the query is never left, but instead the compilation is aborted. not sure how to proceed here. cc @rust-lang/compiler It is very easy to make CTFE run out of RAM, thus crashing rustc. Specifically for CTFE we could catch these out of memory situations and report a proper error, but we're unsure of the implications. |
wesleywiser
commented
Jun 14, 2021
Yeah, that does seem like it could lead to all sorts of issues. From a very quick glance at the PR, it looks like allocation failures in CTFE still result in compilation failing, just with a nicer error message. If compilation still fails, then this seems ok. Are there cases where CTFE will fail but compilation will still succeed? |
RalfJung
commented
Jun 14, 2021
Not all CTFE failures are hard errors (yet). So with We could probably do something similar to #86194, and make all resource exhaustion failures always hard errors. |
oli-obk
commented
Jun 15, 2021
I'm wondering if, even with a hard error, the query system + incremental compilation can get "stuck". We aren't tracking resource exhaustion as an input to the query system, so the query will stay green and not get reevaluated, but loaded from the cache. You may have to remove your cache or modify the right piece of code to get the system "unstuck". |
I've made allocation failures a hard error, and it appears that recompiling with more memory available does work, and that the error doesn't get cached, even when forcing incremental compilation with Note that is a lot easier to test this with this diff to make it possible to simulate zero-memory conditions: diff --git a/compiler/rustc_middle/src/mir/interpret/allocation.rs b/compiler/rustc_middle/src/mir/interpret/allocation.rs
index 7405a70d39a..71d09e16ec4 100644
--- a/compiler/rustc_middle/src/mir/interpret/allocation.rs+++ b/compiler/rustc_middle/src/mir/interpret/allocation.rs@@ -125,6 +125,9 @@ pub fn from_bytes_byte_aligned_immutable<'a>(slice: impl Into<Cow<'a, [u8]>>) ->
/// Try to create an Allocation of `size` bytes, failing if there is not enough memory
/// available to the compiler to do so.
pub fn uninit(size: Size, align: Align) -> InterpResult<'static, Self> {
+ if std::env::var(std::ffi::OsStr::new("RUSTC_ALWAYS_FAIL_MIRI_ALLOC")).is_ok() {+ Err(InterpError::ResourceExhaustion(ResourceExhaustionInfo::MemoryExhausted))?;+ }
let mut bytes = Vec::new();
bytes.try_reserve(size.bytes_usize()).map_err(|_| {Also note that hard errors currently give a confusing and wrong error message, since there is no differentiation between hard and soft errors in the message generation (EDIT: I opened a PR to fix this: #86340): |
wesleywiser
commented
Jun 15, 2021
That's what I would expect. We only write to the incremental cache when the compilation succeeds so as long compilation always fails eventually when there is an allocation failure, there shouldn't be an issue with the incremental system. |
bors
commented
Jun 16, 2021
☔ The latest upstream changes (presumably #86291) made this pull request unmergeable. Please resolve the merge conflicts. |
…=RalfJung Use better error message for hard errors in CTFE I noticed this while working on rust-lang#86255: currently the same message is used for hard errors and soft errors in CTFE. This changes the error messages to make hard errors use a message that indicates the reality of the situation correctly, since usage of the constant is never allowed when there was a hard error evaluating it. This doesn't affect the behaviour of these error messages, only the content. This changes the error logic to check if the error should be hard or soft where it is generated, instead of where it is emitted, to allow this distinction in error messages.
bors
commented
Jun 17, 2021
☔ The latest upstream changes (presumably #86399) made this pull request unmergeable. Please resolve the merge conflicts. |
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
oli-obk
commented
Jun 18, 2021
@bors r+ |
bors
commented
Jun 18, 2021
📌 Commit 72c38905ce940f3543d9067c5398b3670f1ba01f has been approved by |
Uh oh!
There was an error while loading. Please reload this page.
syvb
commented
Jun 30, 2021
Delaying a ICE is a great idea, I've gone ahead and implemented it.
This is already done: rust/compiler/rustc_mir/src/transform/const_prop.rs Lines 40 to 43 in 868c702 |
RalfJung
commented
Jul 1, 2021
Great! Then making allocation panic immediately (ruling out |
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Co-authored-by: Ralf Jung <post@ralfj.de>
RalfJung
commented
Jul 4, 2021
Thanks for sticking with me through all these rounds of review. :) |
bors
commented
Jul 4, 2021
📌 Commit d83c46f has been approved by |
bors
commented
Jul 4, 2021
bors
commented
Jul 4, 2021
☀️ Test successful - checks-actions |
Tested on commit rust-lang/rust@39e20f1. Direct link to PR: <rust-lang/rust#86255> 💔 miri on windows: test-pass → build-fail (cc @RalfJung@oli-obk@eddyb). 💔 miri on linux: test-pass → build-fail (cc @RalfJung@oli-obk@eddyb).
Make work with latest rustc Cc rust-lang/rust#86863, which was caused by PR rust-lang/rust#86255.
…shtriplett Use zeroed allocations in the mir interpreter instead eagerly touching the memory rust-lang#86255 introduced a 30% regression in [page faults](https://perf.rust-lang.org/compare.html?start=64ae15ddd3f3cca7036ab2b2f3a6b130b62af4da&end=39e20f1ae5f13451eb35247808d6a2527cb7d060&stat=faults ) and a 3% regression in [max-rss](https://perf.rust-lang.org/index.html?start=2021-07-01&end=&absolute=false&stat=max-rss) in the ctfe-stress benchmarks. That's most likely happened because it separated allocation from initialization of the vec which defeats the zero-optimization. Currently there's no allocation API that is fallible, zeroing and returns a slice, so this PR introduces one and then uses that to solve the problem. In principle `vec.resize(len, 0)` could be optimized to use `alloc::grow_zeroed` where appropriate but that would require new specializations and new plumbing in `RawVec`.
This closes#79601 by handling the case where memory allocation fails during MIR interpretation, and translates that failure into an
InterpError. The error message is "tried to allocate more memory than available to compiler" to make it clear that the memory shortage is happening at compile-time by the compiler itself, and that it is not a runtime issue.Now that memory allocation can fail, it would be neat if Miri could simulate low-memory devices to make it easy to see how much memory a Rust program needs.
Note that this breaks Miri because it assumes that allocation can never fail.