Skip to content

Commit 9e86a43

Browse files
authored
Rollup merge of #92274 - woppopo:const_deallocate, r=oli-obk
Add `intrinsics::const_deallocate` Tracking issue: #79597 Related: #91884 This allows deallocation of a memory allocated by `intrinsics::const_allocate`. At the moment, this can be only used to reduce memory usage, but in the future this may be useful to detect memory leaks (If an allocated memory remains after evaluation, raise an error...?).
2 parents 11898a5 + 9728cc4 commit 9e86a43

18 files changed

+274
-4
lines changed

compiler/rustc_codegen_ssa/src/mir/intrinsic.rs

+10
Original file line numberDiff line numberDiff line change
@@ -369,6 +369,16 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
369369
}
370370
}
371371

372+
sym::const_allocate => {
373+
// returns a null pointer at runtime.
374+
bx.const_null(bx.type_i8p())
375+
}
376+
377+
sym::const_deallocate => {
378+
// nop at runtime.
379+
return;
380+
}
381+
372382
// This requires that atomic intrinsics follow a specific naming pattern:
373383
// "atomic_<operation>[_<ordering>]", and no ordering means SeqCst
374384
name if name_str.starts_with("atomic_") => {

compiler/rustc_const_eval/src/const_eval/machine.rs

+27
Original file line numberDiff line numberDiff line change
@@ -347,6 +347,33 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir,
347347
)?;
348348
ecx.write_pointer(ptr, dest)?;
349349
}
350+
sym::const_deallocate => {
351+
let ptr = ecx.read_pointer(&args[0])?;
352+
let size = ecx.read_scalar(&args[1])?.to_machine_usize(ecx)?;
353+
let align = ecx.read_scalar(&args[2])?.to_machine_usize(ecx)?;
354+
355+
let size = Size::from_bytes(size);
356+
let align = match Align::from_bytes(align) {
357+
Ok(a) => a,
358+
Err(err) => throw_ub_format!("align has to be a power of 2, {}", err),
359+
};
360+
361+
// If an allocation is created in an another const,
362+
// we don't deallocate it.
363+
let (alloc_id, _, _) = ecx.memory.ptr_get_alloc(ptr)?;
364+
let is_allocated_in_another_const = matches!(
365+
ecx.tcx.get_global_alloc(alloc_id),
366+
Some(interpret::GlobalAlloc::Memory(_))
367+
);
368+
369+
if !is_allocated_in_another_const {
370+
ecx.memory.deallocate(
371+
ptr,
372+
Some((size, align)),
373+
interpret::MemoryKind::Machine(MemoryKind::Heap),
374+
)?;
375+
}
376+
}
350377
_ => {
351378
return Err(ConstEvalErrKind::NeedsRfc(format!(
352379
"calling intrinsic `{}`",

compiler/rustc_span/src/symbol.rs

+1
Original file line numberDiff line numberDiff line change
@@ -461,6 +461,7 @@ symbols! {
461461
const_async_blocks,
462462
const_compare_raw_pointers,
463463
const_constructor,
464+
const_deallocate,
464465
const_eval_limit,
465466
const_eval_select,
466467
const_eval_select_ct,

compiler/rustc_typeck/src/check/intrinsic.rs

+5
Original file line numberDiff line numberDiff line change
@@ -297,6 +297,11 @@ pub fn check_intrinsic_type(tcx: TyCtxt<'_>, it: &hir::ForeignItem<'_>) {
297297
sym::const_allocate => {
298298
(0, vec![tcx.types.usize, tcx.types.usize], tcx.mk_mut_ptr(tcx.types.u8))
299299
}
300+
sym::const_deallocate => (
301+
0,
302+
vec![tcx.mk_mut_ptr(tcx.types.u8), tcx.types.usize, tcx.types.usize],
303+
tcx.mk_unit(),
304+
),
300305

301306
sym::ptr_offset_from => {
302307
(1, vec![tcx.mk_imm_ptr(param(0)), tcx.mk_imm_ptr(param(0))], tcx.types.isize)

library/core/src/intrinsics.rs

+22-1
Original file line numberDiff line numberDiff line change
@@ -1914,10 +1914,31 @@ extern "rust-intrinsic" {
19141914
#[rustc_const_unstable(feature = "const_raw_ptr_comparison", issue = "53020")]
19151915
pub fn ptr_guaranteed_ne<T>(ptr: *const T, other: *const T) -> bool;
19161916

1917-
/// Allocate at compile time. Should not be called at runtime.
1917+
/// Allocates a block of memory at compile time.
1918+
/// At runtime, just returns a null pointer.
1919+
///
1920+
/// # Safety
1921+
///
1922+
/// - The `align` argument must be a power of two.
1923+
/// - At compile time, a compile error occurs if this constraint is violated.
1924+
/// - At runtime, it is not checked.
19181925
#[rustc_const_unstable(feature = "const_heap", issue = "79597")]
19191926
pub fn const_allocate(size: usize, align: usize) -> *mut u8;
19201927

1928+
/// Deallocates a memory which allocated by `intrinsics::const_allocate` at compile time.
1929+
/// At runtime, does nothing.
1930+
///
1931+
/// # Safety
1932+
///
1933+
/// - The `align` argument must be a power of two.
1934+
/// - At compile time, a compile error occurs if this constraint is violated.
1935+
/// - At runtime, it is not checked.
1936+
/// - If the `ptr` is created in an another const, this intrinsic doesn't deallocate it.
1937+
/// - If the `ptr` is pointing to a local variable, this intrinsic doesn't deallocate it.
1938+
#[rustc_const_unstable(feature = "const_heap", issue = "79597")]
1939+
#[cfg(not(bootstrap))]
1940+
pub fn const_deallocate(ptr: *mut u8, size: usize, align: usize);
1941+
19211942
/// Determines whether the raw bytes of the two values are equal.
19221943
///
19231944
/// This is particularly handy for arrays, since it allows things like just

library/core/tests/intrinsics.rs

+22
Original file line numberDiff line numberDiff line change
@@ -80,3 +80,25 @@ fn test_hints_in_const_contexts() {
8080
assert!(42u32 == core::hint::black_box(42u32));
8181
}
8282
}
83+
84+
#[cfg(not(bootstrap))]
85+
#[test]
86+
fn test_const_allocate_at_runtime() {
87+
use core::intrinsics::const_allocate;
88+
unsafe {
89+
assert!(const_allocate(4, 4).is_null());
90+
}
91+
}
92+
93+
#[cfg(not(bootstrap))]
94+
#[test]
95+
fn test_const_deallocate_at_runtime() {
96+
use core::intrinsics::const_deallocate;
97+
const X: &u32 = &42u32;
98+
let x = &0u32;
99+
unsafe {
100+
const_deallocate(X as *const _ as *mut u8, 4, 4); // nop
101+
const_deallocate(x as *const _ as *mut u8, 4, 4); // nop
102+
const_deallocate(core::ptr::null_mut(), 1, 1); // nop
103+
}
104+
}

library/core/tests/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#![feature(const_bool_to_option)]
1414
#![feature(const_cell_into_inner)]
1515
#![feature(const_convert)]
16+
#![feature(const_heap)]
1617
#![feature(const_maybe_uninit_as_mut_ptr)]
1718
#![feature(const_maybe_uninit_assume_init)]
1819
#![feature(const_maybe_uninit_assume_init_read)]

src/test/ui/consts/const-eval/heap/alloc_intrinsic_errors.rs

+1-2
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,10 @@ use std::intrinsics;
66
const FOO: i32 = foo();
77
const fn foo() -> i32 {
88
unsafe {
9-
let _ = intrinsics::const_allocate(4, 3) as * mut i32;
9+
let _ = intrinsics::const_allocate(4, 3) as *mut i32;
1010
//~^ error: evaluation of constant value failed
1111
}
1212
1
13-
1413
}
1514

1615
fn main() {}

src/test/ui/consts/const-eval/heap/alloc_intrinsic_errors.stderr

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ error[E0080]: evaluation of constant value failed
44
LL | const FOO: i32 = foo();
55
| ----- inside `FOO` at $DIR/alloc_intrinsic_errors.rs:6:18
66
...
7-
LL | let _ = intrinsics::const_allocate(4, 3) as * mut i32;
7+
LL | let _ = intrinsics::const_allocate(4, 3) as *mut i32;
88
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
99
| |
1010
| align has to be a power of 2, `3` is not a power of 2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// run-pass
2+
#![feature(core_intrinsics)]
3+
#![feature(const_heap)]
4+
#![feature(inline_const)]
5+
6+
use std::intrinsics;
7+
8+
struct ZST;
9+
10+
fn main() {
11+
const {
12+
unsafe {
13+
let _ = intrinsics::const_allocate(0, 0) as *mut ZST;
14+
}
15+
}
16+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
// run-pass
2+
#![feature(core_intrinsics)]
3+
#![feature(const_heap)]
4+
#![feature(const_mut_refs)]
5+
6+
use std::intrinsics;
7+
8+
const _X: () = unsafe {
9+
let ptr = intrinsics::const_allocate(4, 4);
10+
intrinsics::const_deallocate(ptr, 4, 4);
11+
};
12+
13+
const Y: &u32 = unsafe {
14+
let ptr = intrinsics::const_allocate(4, 4) as *mut u32;
15+
*ptr = 42;
16+
&*ptr
17+
};
18+
19+
const Z: &u32 = &42;
20+
21+
const _Z: () = unsafe {
22+
let ptr1 = Y as *const _ as *mut u8;
23+
intrinsics::const_deallocate(ptr1, 4, 4); // nop
24+
intrinsics::const_deallocate(ptr1, 2, 4); // nop
25+
intrinsics::const_deallocate(ptr1, 4, 2); // nop
26+
27+
let ptr2 = Z as *const _ as *mut u8;
28+
intrinsics::const_deallocate(ptr2, 4, 4); // nop
29+
intrinsics::const_deallocate(ptr2, 2, 4); // nop
30+
intrinsics::const_deallocate(ptr2, 4, 2); // nop
31+
};
32+
33+
fn main() {
34+
assert_eq!(*Y, 42);
35+
assert_eq!(*Z, 42);
36+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
#![feature(core_intrinsics)]
2+
#![feature(const_heap)]
3+
#![feature(const_mut_refs)]
4+
5+
use std::intrinsics;
6+
7+
const _X: &'static u8 = unsafe {
8+
let ptr = intrinsics::const_allocate(4, 4);
9+
intrinsics::const_deallocate(ptr, 4, 4);
10+
&*ptr
11+
//~^ error: evaluation of constant value failed
12+
};
13+
14+
const _Y: u8 = unsafe {
15+
let ptr = intrinsics::const_allocate(4, 4);
16+
let reference = &*ptr;
17+
intrinsics::const_deallocate(ptr, 4, 4);
18+
*reference
19+
//~^ error: evaluation of constant value failed
20+
};
21+
22+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
error[E0080]: evaluation of constant value failed
2+
--> $DIR/dealloc_intrinsic_dangling.rs:10:5
3+
|
4+
LL | &*ptr
5+
| ^^^^^ pointer to alloc2 was dereferenced after this allocation got freed
6+
7+
error[E0080]: evaluation of constant value failed
8+
--> $DIR/dealloc_intrinsic_dangling.rs:18:5
9+
|
10+
LL | *reference
11+
| ^^^^^^^^^^ pointer to alloc4 was dereferenced after this allocation got freed
12+
13+
error: aborting due to 2 previous errors
14+
15+
For more information about this error, try `rustc --explain E0080`.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
#![feature(core_intrinsics)]
2+
#![feature(const_heap)]
3+
4+
use std::intrinsics;
5+
6+
const _X: () = unsafe {
7+
let ptr = intrinsics::const_allocate(4, 4);
8+
intrinsics::const_deallocate(ptr, 4, 4);
9+
intrinsics::const_deallocate(ptr, 4, 4);
10+
//~^ error: evaluation of constant value failed
11+
};
12+
13+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
error[E0080]: evaluation of constant value failed
2+
--> $DIR/dealloc_intrinsic_duplicate.rs:9:5
3+
|
4+
LL | intrinsics::const_deallocate(ptr, 4, 4);
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ pointer to alloc2 was dereferenced after this allocation got freed
6+
7+
error: aborting due to previous error
8+
9+
For more information about this error, try `rustc --explain E0080`.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
#![feature(core_intrinsics)]
2+
#![feature(const_heap)]
3+
4+
use std::intrinsics;
5+
6+
const _X: () = unsafe {
7+
let ptr = intrinsics::const_allocate(4, 4);
8+
intrinsics::const_deallocate(ptr, 4, 2);
9+
//~^ error: evaluation of constant value failed
10+
};
11+
const _Y: () = unsafe {
12+
let ptr = intrinsics::const_allocate(4, 4);
13+
intrinsics::const_deallocate(ptr, 2, 4);
14+
//~^ error: evaluation of constant value failed
15+
};
16+
17+
const _Z: () = unsafe {
18+
let ptr = intrinsics::const_allocate(4, 4);
19+
intrinsics::const_deallocate(ptr, 3, 4);
20+
//~^ error: evaluation of constant value failed
21+
};
22+
23+
const _W: () = unsafe {
24+
let ptr = intrinsics::const_allocate(4, 4);
25+
intrinsics::const_deallocate(ptr, 4, 3);
26+
//~^ error: evaluation of constant value failed
27+
};
28+
29+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
error[E0080]: evaluation of constant value failed
2+
--> $DIR/dealloc_intrinsic_incorrect_layout.rs:8:5
3+
|
4+
LL | intrinsics::const_deallocate(ptr, 4, 2);
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ incorrect layout on deallocation: alloc2 has size 4 and alignment 4, but gave size 4 and alignment 2
6+
7+
error[E0080]: evaluation of constant value failed
8+
--> $DIR/dealloc_intrinsic_incorrect_layout.rs:13:5
9+
|
10+
LL | intrinsics::const_deallocate(ptr, 2, 4);
11+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ incorrect layout on deallocation: alloc4 has size 4 and alignment 4, but gave size 2 and alignment 4
12+
13+
error[E0080]: evaluation of constant value failed
14+
--> $DIR/dealloc_intrinsic_incorrect_layout.rs:19:5
15+
|
16+
LL | intrinsics::const_deallocate(ptr, 3, 4);
17+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ incorrect layout on deallocation: alloc6 has size 4 and alignment 4, but gave size 3 and alignment 4
18+
19+
error[E0080]: evaluation of constant value failed
20+
--> $DIR/dealloc_intrinsic_incorrect_layout.rs:25:5
21+
|
22+
LL | intrinsics::const_deallocate(ptr, 4, 3);
23+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ align has to be a power of 2, `3` is not a power of 2
24+
25+
error: aborting due to 4 previous errors
26+
27+
For more information about this error, try `rustc --explain E0080`.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
// run-pass
2+
#![feature(core_intrinsics)]
3+
#![feature(const_heap)]
4+
#![feature(inline_const)]
5+
6+
use std::intrinsics;
7+
8+
fn main() {
9+
const {
10+
unsafe {
11+
let ptr1 = intrinsics::const_allocate(0, 0);
12+
let ptr2 = intrinsics::const_allocate(0, 0);
13+
intrinsics::const_deallocate(ptr1, 0, 0);
14+
intrinsics::const_deallocate(ptr2, 0, 0);
15+
}
16+
}
17+
}

0 commit comments

Comments
 (0)