-
Notifications
You must be signed in to change notification settings - Fork 12.8k
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
add const_allocate intrinsic #79594
add const_allocate intrinsic #79594
Conversation
cc @rust-lang/wg-const-eval |
#![feature(const_mut_refs)] | ||
use std::intrinsics; | ||
|
||
const FOO: *const i32 = foo(); |
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's super curious that this doesn't error with the same error that src/test/ui/consts/const-eval/heap/alloc_intrinsic_untyped.rs emits: "untyped pointer".
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... I think I know why... foo
is a function without any arguments, and is thus not actually called but treated as a constant itself and its body is thus evaluated directly:
rust/compiler/rustc_mir/src/const_eval/machine.rs
Lines 40 to 44 in 75f1db1
// For the moment we only do this for functions which take no arguments | |
// (or all arguments are ZSTs) so that we don't memoize too much. | |
if args.iter().any(|a| !a.layout.is_zst()) { | |
return Ok(false); | |
} |
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 guess we'll need to disable that optimization for function items now
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 guess we'll need to disable that optimization for function items now
Why that?
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.
This test memoizes function alls to foo()
, even though every call should be producing a new heap allocation. Otherwise all calls to Box::new_uninit()
will end up with the same heap allocation.
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.
Basically if you used
const fn foo() -> &'static i32 {
const fn bar() -> *mut i32 {
unsafe { intrinsics::const_allocate(4, 4) as *mut i32 }
}
unsafe {
let i = bar();
*i = 20;
...
}
}
the *i = 20
will fail to interpret because you'd be modifying interned memory
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, lol... yeah, const fn
without inputs are no longer pure with this change, That's kind of a big deal actually. We certainly need T-lang approval before stabilizing this.
@@ -104,7 +104,7 @@ fn intern_shallow<'rt, 'mir, 'tcx, M: CompileTimeMachine<'mir, 'tcx>>( | |||
// This match is just a canary for future changes to `MemoryKind`, which most likely need | |||
// changes in this function. | |||
match kind { | |||
MemoryKind::Stack | MemoryKind::Vtable | MemoryKind::CallerLocation => {} | |||
MemoryKind::Stack | MemoryKind::Heap | MemoryKind::Vtable | MemoryKind::CallerLocation => {} |
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.
Should Miri also use this Heap
variant? Or should we rename it to ConstHeap
?
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, good point. I think miri will need its own heap variant to prevent us from accidentally mixing the two somewhere.
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.
@oli-obk so what do you think about using a non-!
MemoryKind
for the CTFE machine?
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.
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.
if you are worried about miri breakage, we can also do that change to an enum MemoryKind {}
in a PR and then rebase this PR on top of it.
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.
No I think this is fine (but please leave a FIXME in the enum
definition).
match instance.def { | ||
InstanceDef::Intrinsic(_) => { |
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 sorry, I meant to run this check within try_eval_const_fn_call
and return Ok(false)
from it if it's not an intrinsic
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 don't understand this new code. Don't we have to just entirely remove memoization?
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.
we still memoize calls to size_of
and align_of
I think
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.
Is that worth it all the code complexity here? Those functions are trivial to evaluate...
src/test/ui/consts/const-eval/heap/alloc_intrinsic_nontransient_fail.rs
Outdated
Show resolved
Hide resolved
| inside `a` at $DIR/infinite-recursion-const-fn.rs:4:5 | ||
| inside `a` at $DIR/infinite-recursion-const-fn.rs:4:5 | ||
| inside `a` at $DIR/infinite-recursion-const-fn.rs:4:5 | ||
| inside `a` at $DIR/infinite-recursion-const-fn.rs:4:5 |
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.
Looks like this test gives worse errors now. Not sure how disabling memoization caused this.
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.
In a sense this is actually a better error, since the full stack trace is printed now (#75390).
@bors r+ |
📌 Commit bc6eb6f has been approved by |
☀️ Test successful - checks-actions |
This had a positive effect on the performance, especially for ctfe-stress for which it had an up to 2% improvement. https://perf.rust-lang.org/compare.html?start=c7cff213e937c1bb301be807ce04fcf6092b9163&end=d015f0d92144f0e72735a918aee8510b0fe2cff5&stat=instructions%3Au |
That is probably caused by memoization being mostly disabled. Funny that memoization actually made things slower in the stress test, probably the memoized cache was rarely/never hit so maintaining that cache was just an unnecessary cost. |
r? @oli-obk
fixes #75390