Skip to content

Commit 7289d92

Browse files
authored
Rollup merge of rust-lang#147718 - RalfJung:miri-allocator_shim_contents, r=bjorn3
miri: use allocator_shim_contents codegen helper r? `@bjorn3` Follow-up to rust-lang#147526, also using that new infrastructure in Miri.
2 parents 3d48493 + 227da2d commit 7289d92

File tree

7 files changed

+218
-206
lines changed

7 files changed

+218
-206
lines changed

compiler/rustc_ast/src/expand/allocator.rs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,10 +31,23 @@ pub enum AllocatorTy {
3131
Usize,
3232
}
3333

34+
/// Some allocator methods are known to the compiler: they act more like
35+
/// intrinsics/language primitives than library-defined functions.
36+
/// FIXME: ideally this would be derived from attributes like `#[rustc_allocator]`,
37+
/// so we don't have two sources of truth.
38+
#[derive(Copy, Clone, Debug)]
39+
pub enum SpecialAllocatorMethod {
40+
Alloc,
41+
AllocZeroed,
42+
Dealloc,
43+
Realloc,
44+
}
45+
3446
/// A method that will be codegened in the allocator shim.
3547
#[derive(Copy, Clone)]
3648
pub struct AllocatorMethod {
3749
pub name: Symbol,
50+
pub special: Option<SpecialAllocatorMethod>,
3851
pub inputs: &'static [AllocatorMethodInput],
3952
pub output: AllocatorTy,
4053
}
@@ -47,11 +60,13 @@ pub struct AllocatorMethodInput {
4760
pub static ALLOCATOR_METHODS: &[AllocatorMethod] = &[
4861
AllocatorMethod {
4962
name: sym::alloc,
63+
special: Some(SpecialAllocatorMethod::Alloc),
5064
inputs: &[AllocatorMethodInput { name: "layout", ty: AllocatorTy::Layout }],
5165
output: AllocatorTy::ResultPtr,
5266
},
5367
AllocatorMethod {
5468
name: sym::dealloc,
69+
special: Some(SpecialAllocatorMethod::Dealloc),
5570
inputs: &[
5671
AllocatorMethodInput { name: "ptr", ty: AllocatorTy::Ptr },
5772
AllocatorMethodInput { name: "layout", ty: AllocatorTy::Layout },
@@ -60,6 +75,7 @@ pub static ALLOCATOR_METHODS: &[AllocatorMethod] = &[
6075
},
6176
AllocatorMethod {
6277
name: sym::realloc,
78+
special: Some(SpecialAllocatorMethod::Realloc),
6379
inputs: &[
6480
AllocatorMethodInput { name: "ptr", ty: AllocatorTy::Ptr },
6581
AllocatorMethodInput { name: "layout", ty: AllocatorTy::Layout },
@@ -69,6 +85,7 @@ pub static ALLOCATOR_METHODS: &[AllocatorMethod] = &[
6985
},
7086
AllocatorMethod {
7187
name: sym::alloc_zeroed,
88+
special: Some(SpecialAllocatorMethod::AllocZeroed),
7289
inputs: &[AllocatorMethodInput { name: "layout", ty: AllocatorTy::Layout }],
7390
output: AllocatorTy::ResultPtr,
7491
},

compiler/rustc_codegen_llvm/src/allocator.rs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
use libc::c_uint;
22
use rustc_ast::expand::allocator::{
3-
AllocatorMethod, AllocatorTy, NO_ALLOC_SHIM_IS_UNSTABLE, default_fn_name, global_fn_name,
3+
AllocatorMethod, AllocatorTy, NO_ALLOC_SHIM_IS_UNSTABLE, SpecialAllocatorMethod,
4+
default_fn_name, global_fn_name,
45
};
56
use rustc_codegen_ssa::traits::BaseTypeCodegenMethods as _;
67
use rustc_middle::bug;
78
use rustc_middle::middle::codegen_fn_attrs::{CodegenFnAttrFlags, CodegenFnAttrs};
89
use rustc_middle::ty::TyCtxt;
910
use rustc_session::config::{DebugInfo, OomStrategy};
10-
use rustc_span::sym;
1111
use rustc_symbol_mangling::mangle_internal_symbol;
1212

1313
use crate::attributes::llfn_attrs_from_instance;
@@ -65,12 +65,12 @@ pub(crate) unsafe fn codegen(
6565
let from_name = mangle_internal_symbol(tcx, &global_fn_name(method.name));
6666
let to_name = mangle_internal_symbol(tcx, &default_fn_name(method.name));
6767

68-
let alloc_attr_flag = match method.name {
69-
sym::alloc => CodegenFnAttrFlags::ALLOCATOR,
70-
sym::dealloc => CodegenFnAttrFlags::DEALLOCATOR,
71-
sym::realloc => CodegenFnAttrFlags::REALLOCATOR,
72-
sym::alloc_zeroed => CodegenFnAttrFlags::ALLOCATOR_ZEROED,
73-
_ => CodegenFnAttrFlags::empty(),
68+
let alloc_attr_flag = match method.special {
69+
Some(SpecialAllocatorMethod::Alloc) => CodegenFnAttrFlags::ALLOCATOR,
70+
Some(SpecialAllocatorMethod::Dealloc) => CodegenFnAttrFlags::DEALLOCATOR,
71+
Some(SpecialAllocatorMethod::Realloc) => CodegenFnAttrFlags::REALLOCATOR,
72+
Some(SpecialAllocatorMethod::AllocZeroed) => CodegenFnAttrFlags::ALLOCATOR_ZEROED,
73+
None => CodegenFnAttrFlags::empty(),
7474
};
7575

7676
let mut attrs = CodegenFnAttrs::new();

compiler/rustc_codegen_ssa/src/base.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -669,6 +669,7 @@ pub fn allocator_shim_contents(tcx: TyCtxt<'_>, kind: AllocatorKind) -> Vec<Allo
669669
if tcx.alloc_error_handler_kind(()).unwrap() == AllocatorKind::Default {
670670
methods.push(AllocatorMethod {
671671
name: ALLOC_ERROR_HANDLER,
672+
special: None,
672673
inputs: &[],
673674
output: AllocatorTy::Never,
674675
});

src/tools/miri/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ extern crate tracing;
5757
extern crate rustc_abi;
5858
extern crate rustc_apfloat;
5959
extern crate rustc_ast;
60+
extern crate rustc_codegen_ssa;
6061
extern crate rustc_const_eval;
6162
extern crate rustc_data_structures;
6263
extern crate rustc_errors;

src/tools/miri/src/machine.rs

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,12 @@ use std::path::Path;
88
use std::rc::Rc;
99
use std::{fmt, process};
1010

11+
use either::Either;
1112
use rand::rngs::StdRng;
1213
use rand::{Rng, SeedableRng};
1314
use rustc_abi::{Align, ExternAbi, Size};
1415
use rustc_apfloat::{Float, FloatConvert};
16+
use rustc_ast::expand::allocator::{self, SpecialAllocatorMethod};
1517
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
1618
#[allow(unused)]
1719
use rustc_data_structures::static_assert_size;
@@ -26,6 +28,7 @@ use rustc_middle::ty::{self, Instance, Ty, TyCtxt};
2628
use rustc_session::config::InliningThreshold;
2729
use rustc_span::def_id::{CrateNum, DefId};
2830
use rustc_span::{Span, SpanData, Symbol};
31+
use rustc_symbol_mangling::mangle_internal_symbol;
2932
use rustc_target::callconv::FnAbi;
3033

3134
use crate::alloc_addresses::EvalContextExt;
@@ -651,6 +654,10 @@ pub struct MiriMachine<'tcx> {
651654
pub(crate) pthread_rwlock_sanity: Cell<bool>,
652655
pub(crate) pthread_condvar_sanity: Cell<bool>,
653656

657+
/// (Foreign) symbols that are synthesized as part of the allocator shim: the key indicates the
658+
/// name of the symbol being synthesized; the value indicates whether this should invoke some
659+
/// other symbol or whether this has special allocator semantics.
660+
pub(crate) allocator_shim_symbols: FxHashMap<Symbol, Either<Symbol, SpecialAllocatorMethod>>,
654661
/// Cache for `mangle_internal_symbol`.
655662
pub(crate) mangle_internal_symbol_cache: FxHashMap<&'static str, String>,
656663

@@ -818,6 +825,7 @@ impl<'tcx> MiriMachine<'tcx> {
818825
pthread_mutex_sanity: Cell::new(false),
819826
pthread_rwlock_sanity: Cell::new(false),
820827
pthread_condvar_sanity: Cell::new(false),
828+
allocator_shim_symbols: Self::allocator_shim_symbols(tcx),
821829
mangle_internal_symbol_cache: Default::default(),
822830
force_intrinsic_fallback: config.force_intrinsic_fallback,
823831
float_nondet: config.float_nondet,
@@ -826,6 +834,36 @@ impl<'tcx> MiriMachine<'tcx> {
826834
}
827835
}
828836

837+
fn allocator_shim_symbols(
838+
tcx: TyCtxt<'tcx>,
839+
) -> FxHashMap<Symbol, Either<Symbol, SpecialAllocatorMethod>> {
840+
use rustc_codegen_ssa::base::allocator_shim_contents;
841+
842+
// codegen uses `allocator_kind_for_codegen` here, but that's only needed to deal with
843+
// dylibs which we do not support.
844+
let Some(kind) = tcx.allocator_kind(()) else {
845+
return Default::default();
846+
};
847+
let methods = allocator_shim_contents(tcx, kind);
848+
let mut symbols = FxHashMap::default();
849+
for method in methods {
850+
let from_name = Symbol::intern(&mangle_internal_symbol(
851+
tcx,
852+
&allocator::global_fn_name(method.name),
853+
));
854+
let to = match method.special {
855+
Some(special) => Either::Right(special),
856+
None =>
857+
Either::Left(Symbol::intern(&mangle_internal_symbol(
858+
tcx,
859+
&allocator::default_fn_name(method.name),
860+
))),
861+
};
862+
symbols.try_insert(from_name, to).unwrap();
863+
}
864+
symbols
865+
}
866+
829867
pub(crate) fn late_init(
830868
ecx: &mut MiriInterpCx<'tcx>,
831869
config: &MiriConfig,
@@ -991,6 +1029,7 @@ impl VisitProvenance for MiriMachine<'_> {
9911029
pthread_mutex_sanity: _,
9921030
pthread_rwlock_sanity: _,
9931031
pthread_condvar_sanity: _,
1032+
allocator_shim_symbols: _,
9941033
mangle_internal_symbol_cache: _,
9951034
force_intrinsic_fallback: _,
9961035
float_nondet: _,

src/tools/miri/src/shims/alloc.rs

Lines changed: 94 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
1-
use rustc_abi::{Align, Size};
2-
use rustc_ast::expand::allocator::AllocatorKind;
1+
use rustc_abi::{Align, AlignFromBytesError, CanonAbi, Size};
2+
use rustc_ast::expand::allocator::SpecialAllocatorMethod;
3+
use rustc_middle::ty::Ty;
4+
use rustc_span::Symbol;
5+
use rustc_target::callconv::FnAbi;
36

47
use crate::*;
58

@@ -54,30 +57,100 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
5457
Align::from_bytes(prev_power_of_two(size)).unwrap()
5558
}
5659

57-
/// Emulates calling the internal __rust_* allocator functions
58-
fn emulate_allocator(
60+
/// Check some basic requirements for this allocation request:
61+
/// non-zero size, power-of-two alignment.
62+
fn check_rust_alloc_request(&self, size: u64, align: u64) -> InterpResult<'tcx> {
63+
let this = self.eval_context_ref();
64+
if size == 0 {
65+
throw_ub_format!("creating allocation with size 0");
66+
}
67+
if size > this.max_size_of_val().bytes() {
68+
throw_ub_format!("creating an allocation larger than half the address space");
69+
}
70+
if let Err(e) = Align::from_bytes(align) {
71+
match e {
72+
AlignFromBytesError::TooLarge(_) => {
73+
throw_unsup_format!(
74+
"creating allocation with alignment {align} exceeding rustc's maximum \
75+
supported value"
76+
);
77+
}
78+
AlignFromBytesError::NotPowerOfTwo(_) => {
79+
throw_ub_format!("creating allocation with non-power-of-two alignment {align}");
80+
}
81+
}
82+
}
83+
84+
interp_ok(())
85+
}
86+
87+
fn rust_special_allocator_method(
5988
&mut self,
60-
default: impl FnOnce(&mut MiriInterpCx<'tcx>) -> InterpResult<'tcx>,
61-
) -> InterpResult<'tcx, EmulateItemResult> {
89+
method: SpecialAllocatorMethod,
90+
link_name: Symbol,
91+
abi: &FnAbi<'tcx, Ty<'tcx>>,
92+
args: &[OpTy<'tcx>],
93+
dest: &PlaceTy<'tcx>,
94+
) -> InterpResult<'tcx> {
6295
let this = self.eval_context_mut();
6396

64-
let Some(allocator_kind) = this.tcx.allocator_kind(()) else {
65-
// in real code, this symbol does not exist without an allocator
66-
return interp_ok(EmulateItemResult::NotSupported);
67-
};
97+
match method {
98+
SpecialAllocatorMethod::Alloc | SpecialAllocatorMethod::AllocZeroed => {
99+
let [size, align] =
100+
this.check_shim_sig_lenient(abi, CanonAbi::Rust, link_name, args)?;
101+
let size = this.read_target_usize(size)?;
102+
let align = this.read_target_usize(align)?;
103+
104+
this.check_rust_alloc_request(size, align)?;
68105

69-
match allocator_kind {
70-
AllocatorKind::Global => {
71-
// When `#[global_allocator]` is used, `__rust_*` is defined by the macro expansion
72-
// of this attribute. As such we have to call an exported Rust function,
73-
// and not execute any Miri shim. Somewhat unintuitively doing so is done
74-
// by returning `NotSupported`, which triggers the `lookup_exported_symbol`
75-
// fallback case in `emulate_foreign_item`.
76-
interp_ok(EmulateItemResult::NotSupported)
106+
let ptr = this.allocate_ptr(
107+
Size::from_bytes(size),
108+
Align::from_bytes(align).unwrap(),
109+
MiriMemoryKind::Rust.into(),
110+
if matches!(method, SpecialAllocatorMethod::AllocZeroed) {
111+
AllocInit::Zero
112+
} else {
113+
AllocInit::Uninit
114+
},
115+
)?;
116+
117+
this.write_pointer(ptr, dest)
118+
}
119+
SpecialAllocatorMethod::Dealloc => {
120+
let [ptr, old_size, align] =
121+
this.check_shim_sig_lenient(abi, CanonAbi::Rust, link_name, args)?;
122+
let ptr = this.read_pointer(ptr)?;
123+
let old_size = this.read_target_usize(old_size)?;
124+
let align = this.read_target_usize(align)?;
125+
126+
// No need to check old_size/align; we anyway check that they match the allocation.
127+
this.deallocate_ptr(
128+
ptr,
129+
Some((Size::from_bytes(old_size), Align::from_bytes(align).unwrap())),
130+
MiriMemoryKind::Rust.into(),
131+
)
77132
}
78-
AllocatorKind::Default => {
79-
default(this)?;
80-
interp_ok(EmulateItemResult::NeedsReturn)
133+
SpecialAllocatorMethod::Realloc => {
134+
let [ptr, old_size, align, new_size] =
135+
this.check_shim_sig_lenient(abi, CanonAbi::Rust, link_name, args)?;
136+
let ptr = this.read_pointer(ptr)?;
137+
let old_size = this.read_target_usize(old_size)?;
138+
let align = this.read_target_usize(align)?;
139+
let new_size = this.read_target_usize(new_size)?;
140+
// No need to check old_size; we anyway check that they match the allocation.
141+
142+
this.check_rust_alloc_request(new_size, align)?;
143+
144+
let align = Align::from_bytes(align).unwrap();
145+
let new_ptr = this.reallocate_ptr(
146+
ptr,
147+
Some((Size::from_bytes(old_size), align)),
148+
Size::from_bytes(new_size),
149+
align,
150+
MiriMemoryKind::Rust.into(),
151+
AllocInit::Uninit,
152+
)?;
153+
this.write_pointer(new_ptr, dest)
81154
}
82155
}
83156
}

0 commit comments

Comments
 (0)