Skip to content

Commit a2b1646

Browse files
committedMay 25, 2023
Auto merge of rust-lang#86844 - bjorn3:global_alloc_improvements, r=pnkfelix
Support #[global_allocator] without the allocator shim This makes it possible to use liballoc/libstd in combination with `--emit obj` if you use `#[global_allocator]`. This is what rust-for-linux uses right now and systemd may use in the future. Currently they have to depend on the exact implementation of the allocator shim to create one themself as `--emit obj` doesn't create an allocator shim. Note that currently the allocator shim also defines the oom error handler, which is normally required too. Once `#![feature(default_alloc_error_handler)]` becomes the only option, this can be avoided. In addition when using only fallible allocator methods and either `--cfg no_global_oom_handling` for liballoc (like rust-for-linux) or `--gc-sections` no references to the oom error handler will exist. To avoid this feature being insta-stable, you will have to define `__rust_no_alloc_shim_is_unstable` to avoid linker errors. (Labeling this with both T-compiler and T-lang as it originally involved both an implementation detail and had an insta-stable user facing change. As noted above, the `__rust_no_alloc_shim_is_unstable` symbol requirement should prevent unintended dependence on this unstable feature.)
2 parents cade266 + 33d9b58 commit a2b1646

File tree

19 files changed

+358
-217
lines changed

19 files changed

+358
-217
lines changed
 

‎compiler/rustc_ast/src/expand/allocator.rs

+15-7
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,28 @@
11
use rustc_span::symbol::{sym, Symbol};
22

3-
#[derive(Clone, Debug, Copy, HashStable_Generic)]
3+
#[derive(Clone, Debug, Copy, Eq, PartialEq, HashStable_Generic)]
44
pub enum AllocatorKind {
55
Global,
66
Default,
77
}
88

9-
impl AllocatorKind {
10-
pub fn fn_name(&self, base: Symbol) -> String {
11-
match *self {
12-
AllocatorKind::Global => format!("__rg_{base}"),
13-
AllocatorKind::Default => format!("__rdl_{base}"),
14-
}
9+
pub fn global_fn_name(base: Symbol) -> String {
10+
format!("__rust_{base}")
11+
}
12+
13+
pub fn default_fn_name(base: Symbol) -> String {
14+
format!("__rdl_{base}")
15+
}
16+
17+
pub fn alloc_error_handler_name(alloc_error_handler_kind: AllocatorKind) -> &'static str {
18+
match alloc_error_handler_kind {
19+
AllocatorKind::Global => "__rg_oom",
20+
AllocatorKind::Default => "__rdl_oom",
1521
}
1622
}
1723

24+
pub const NO_ALLOC_SHIM_IS_UNSTABLE: &str = "__rust_no_alloc_shim_is_unstable";
25+
1826
pub enum AllocatorTy {
1927
Layout,
2028
Ptr,

‎compiler/rustc_builtin_macros/src/global_allocator.rs

+3-5
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use crate::util::check_builtin_macro_attribute;
22

33
use rustc_ast::expand::allocator::{
4-
AllocatorKind, AllocatorMethod, AllocatorTy, ALLOCATOR_METHODS,
4+
global_fn_name, AllocatorMethod, AllocatorTy, ALLOCATOR_METHODS,
55
};
66
use rustc_ast::ptr::P;
77
use rustc_ast::{self as ast, AttrVec, Expr, FnHeader, FnSig, Generics, Param, StmtKind};
@@ -40,8 +40,7 @@ pub fn expand(
4040

4141
// Generate a bunch of new items using the AllocFnFactory
4242
let span = ecx.with_def_site_ctxt(item.span);
43-
let f =
44-
AllocFnFactory { span, ty_span, kind: AllocatorKind::Global, global: item.ident, cx: ecx };
43+
let f = AllocFnFactory { span, ty_span, global: item.ident, cx: ecx };
4544

4645
// Generate item statements for the allocator methods.
4746
let stmts = ALLOCATOR_METHODS.iter().map(|method| f.allocator_fn(method)).collect();
@@ -63,7 +62,6 @@ pub fn expand(
6362
struct AllocFnFactory<'a, 'b> {
6463
span: Span,
6564
ty_span: Span,
66-
kind: AllocatorKind,
6765
global: Ident,
6866
cx: &'b ExtCtxt<'a>,
6967
}
@@ -92,7 +90,7 @@ impl AllocFnFactory<'_, '_> {
9290
}));
9391
let item = self.cx.item(
9492
self.span,
95-
Ident::from_str_and_span(&self.kind.fn_name(method.name), self.span),
93+
Ident::from_str_and_span(&global_fn_name(method.name), self.span),
9694
self.attrs(),
9795
kind,
9896
);

‎compiler/rustc_codegen_cranelift/src/allocator.rs

+45-34
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,12 @@
33

44
use crate::prelude::*;
55

6-
use rustc_ast::expand::allocator::{AllocatorKind, AllocatorTy, ALLOCATOR_METHODS};
6+
use rustc_ast::expand::allocator::{
7+
alloc_error_handler_name, default_fn_name, global_fn_name, AllocatorKind, AllocatorTy,
8+
ALLOCATOR_METHODS, NO_ALLOC_SHIM_IS_UNSTABLE,
9+
};
710
use rustc_codegen_ssa::base::allocator_kind_for_codegen;
811
use rustc_session::config::OomStrategy;
9-
use rustc_span::symbol::sym;
1012

1113
/// Returns whether an allocator shim was created
1214
pub(crate) fn codegen(
@@ -34,41 +36,43 @@ fn codegen_inner(
3436
) {
3537
let usize_ty = module.target_config().pointer_type();
3638

37-
for method in ALLOCATOR_METHODS {
38-
let mut arg_tys = Vec::with_capacity(method.inputs.len());
39-
for ty in method.inputs.iter() {
40-
match *ty {
41-
AllocatorTy::Layout => {
42-
arg_tys.push(usize_ty); // size
43-
arg_tys.push(usize_ty); // align
44-
}
45-
AllocatorTy::Ptr => arg_tys.push(usize_ty),
46-
AllocatorTy::Usize => arg_tys.push(usize_ty),
39+
if kind == AllocatorKind::Default {
40+
for method in ALLOCATOR_METHODS {
41+
let mut arg_tys = Vec::with_capacity(method.inputs.len());
42+
for ty in method.inputs.iter() {
43+
match *ty {
44+
AllocatorTy::Layout => {
45+
arg_tys.push(usize_ty); // size
46+
arg_tys.push(usize_ty); // align
47+
}
48+
AllocatorTy::Ptr => arg_tys.push(usize_ty),
49+
AllocatorTy::Usize => arg_tys.push(usize_ty),
4750

48-
AllocatorTy::ResultPtr | AllocatorTy::Unit => panic!("invalid allocator arg"),
51+
AllocatorTy::ResultPtr | AllocatorTy::Unit => panic!("invalid allocator arg"),
52+
}
4953
}
50-
}
51-
let output = match method.output {
52-
AllocatorTy::ResultPtr => Some(usize_ty),
53-
AllocatorTy::Unit => None,
54+
let output = match method.output {
55+
AllocatorTy::ResultPtr => Some(usize_ty),
56+
AllocatorTy::Unit => None,
5457

55-
AllocatorTy::Layout | AllocatorTy::Usize | AllocatorTy::Ptr => {
56-
panic!("invalid allocator output")
57-
}
58-
};
58+
AllocatorTy::Layout | AllocatorTy::Usize | AllocatorTy::Ptr => {
59+
panic!("invalid allocator output")
60+
}
61+
};
5962

60-
let sig = Signature {
61-
call_conv: module.target_config().default_call_conv,
62-
params: arg_tys.iter().cloned().map(AbiParam::new).collect(),
63-
returns: output.into_iter().map(AbiParam::new).collect(),
64-
};
65-
crate::common::create_wrapper_function(
66-
module,
67-
unwind_context,
68-
sig,
69-
&format!("__rust_{}", method.name),
70-
&kind.fn_name(method.name),
71-
);
63+
let sig = Signature {
64+
call_conv: module.target_config().default_call_conv,
65+
params: arg_tys.iter().cloned().map(AbiParam::new).collect(),
66+
returns: output.into_iter().map(AbiParam::new).collect(),
67+
};
68+
crate::common::create_wrapper_function(
69+
module,
70+
unwind_context,
71+
sig,
72+
&global_fn_name(method.name),
73+
&default_fn_name(method.name),
74+
);
75+
}
7276
}
7377

7478
let sig = Signature {
@@ -81,7 +85,7 @@ fn codegen_inner(
8185
unwind_context,
8286
sig,
8387
"__rust_alloc_error_handler",
84-
&alloc_error_handler_kind.fn_name(sym::oom),
88+
&alloc_error_handler_name(alloc_error_handler_kind),
8589
);
8690

8791
let data_id = module.declare_data(OomStrategy::SYMBOL, Linkage::Export, false, false).unwrap();
@@ -90,4 +94,11 @@ fn codegen_inner(
9094
let val = oom_strategy.should_panic();
9195
data_ctx.define(Box::new([val]));
9296
module.define_data(data_id, &data_ctx).unwrap();
97+
98+
let data_id =
99+
module.declare_data(NO_ALLOC_SHIM_IS_UNSTABLE, Linkage::Export, false, false).unwrap();
100+
let mut data_ctx = DataContext::new();
101+
data_ctx.set_align(1);
102+
data_ctx.define(Box::new([0]));
103+
module.define_data(data_id, &data_ctx).unwrap();
93104
}
+67-58
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
#[cfg(feature="master")]
22
use gccjit::FnAttribute;
33
use gccjit::{FunctionType, GlobalKind, ToRValue};
4-
use rustc_ast::expand::allocator::{AllocatorKind, AllocatorTy, ALLOCATOR_METHODS};
4+
use rustc_ast::expand::allocator::{
5+
alloc_error_handler_name, default_fn_name, global_fn_name, AllocatorKind, AllocatorTy,
6+
ALLOCATOR_METHODS, NO_ALLOC_SHIM_IS_UNSTABLE,
7+
};
58
use rustc_middle::bug;
69
use rustc_middle::ty::TyCtxt;
710
use rustc_session::config::OomStrategy;
8-
use rustc_span::symbol::sym;
911

1012
use crate::GccContext;
1113

@@ -22,69 +24,71 @@ pub(crate) unsafe fn codegen(tcx: TyCtxt<'_>, mods: &mut GccContext, _module_nam
2224
let i8p = i8.make_pointer();
2325
let void = context.new_type::<()>();
2426

25-
for method in ALLOCATOR_METHODS {
26-
let mut types = Vec::with_capacity(method.inputs.len());
27-
for ty in method.inputs.iter() {
28-
match *ty {
29-
AllocatorTy::Layout => {
30-
types.push(usize);
31-
types.push(usize);
27+
if kind == AllocatorKind::Default {
28+
for method in ALLOCATOR_METHODS {
29+
let mut types = Vec::with_capacity(method.inputs.len());
30+
for ty in method.inputs.iter() {
31+
match *ty {
32+
AllocatorTy::Layout => {
33+
types.push(usize);
34+
types.push(usize);
35+
}
36+
AllocatorTy::Ptr => types.push(i8p),
37+
AllocatorTy::Usize => types.push(usize),
38+
39+
AllocatorTy::ResultPtr | AllocatorTy::Unit => panic!("invalid allocator arg"),
3240
}
33-
AllocatorTy::Ptr => types.push(i8p),
34-
AllocatorTy::Usize => types.push(usize),
35-
36-
AllocatorTy::ResultPtr | AllocatorTy::Unit => panic!("invalid allocator arg"),
3741
}
38-
}
39-
let output = match method.output {
40-
AllocatorTy::ResultPtr => Some(i8p),
41-
AllocatorTy::Unit => None,
42+
let output = match method.output {
43+
AllocatorTy::ResultPtr => Some(i8p),
44+
AllocatorTy::Unit => None,
4245

43-
AllocatorTy::Layout | AllocatorTy::Usize | AllocatorTy::Ptr => {
44-
panic!("invalid allocator output")
45-
}
46-
};
47-
let name = format!("__rust_{}", method.name);
46+
AllocatorTy::Layout | AllocatorTy::Usize | AllocatorTy::Ptr => {
47+
panic!("invalid allocator output")
48+
}
49+
};
50+
let name = global_fn_name(method.name);
4851

49-
let args: Vec<_> = types.iter().enumerate()
50-
.map(|(index, typ)| context.new_parameter(None, *typ, &format!("param{}", index)))
51-
.collect();
52-
let func = context.new_function(None, FunctionType::Exported, output.unwrap_or(void), &args, name, false);
52+
let args: Vec<_> = types.iter().enumerate()
53+
.map(|(index, typ)| context.new_parameter(None, *typ, &format!("param{}", index)))
54+
.collect();
55+
let func = context.new_function(None, FunctionType::Exported, output.unwrap_or(void), &args, name, false);
5356

54-
if tcx.sess.target.options.default_hidden_visibility {
57+
if tcx.sess.target.options.default_hidden_visibility {
58+
#[cfg(feature="master")]
59+
func.add_attribute(FnAttribute::Visibility(gccjit::Visibility::Hidden));
60+
}
61+
if tcx.sess.must_emit_unwind_tables() {
62+
// TODO(antoyo): emit unwind tables.
63+
}
64+
65+
let callee = default_fn_name(method.name);
66+
let args: Vec<_> = types.iter().enumerate()
67+
.map(|(index, typ)| context.new_parameter(None, *typ, &format!("param{}", index)))
68+
.collect();
69+
let callee = context.new_function(None, FunctionType::Extern, output.unwrap_or(void), &args, callee, false);
5570
#[cfg(feature="master")]
56-
func.add_attribute(FnAttribute::Visibility(gccjit::Visibility::Hidden));
57-
}
58-
if tcx.sess.must_emit_unwind_tables() {
59-
// TODO(antoyo): emit unwind tables.
60-
}
71+
callee.add_attribute(FnAttribute::Visibility(gccjit::Visibility::Hidden));
72+
73+
let block = func.new_block("entry");
74+
75+
let args = args
76+
.iter()
77+
.enumerate()
78+
.map(|(i, _)| func.get_param(i as i32).to_rvalue())
79+
.collect::<Vec<_>>();
80+
let ret = context.new_call(None, callee, &args);
81+
//llvm::LLVMSetTailCall(ret, True);
82+
if output.is_some() {
83+
block.end_with_return(None, ret);
84+
}
85+
else {
86+
block.end_with_void_return(None);
87+
}
6188

62-
let callee = kind.fn_name(method.name);
63-
let args: Vec<_> = types.iter().enumerate()
64-
.map(|(index, typ)| context.new_parameter(None, *typ, &format!("param{}", index)))
65-
.collect();
66-
let callee = context.new_function(None, FunctionType::Extern, output.unwrap_or(void), &args, callee, false);
67-
#[cfg(feature="master")]
68-
callee.add_attribute(FnAttribute::Visibility(gccjit::Visibility::Hidden));
69-
70-
let block = func.new_block("entry");
71-
72-
let args = args
73-
.iter()
74-
.enumerate()
75-
.map(|(i, _)| func.get_param(i as i32).to_rvalue())
76-
.collect::<Vec<_>>();
77-
let ret = context.new_call(None, callee, &args);
78-
//llvm::LLVMSetTailCall(ret, True);
79-
if output.is_some() {
80-
block.end_with_return(None, ret);
81-
}
82-
else {
83-
block.end_with_void_return(None);
89+
// TODO(@Commeownist): Check if we need to emit some extra debugging info in certain circumstances
90+
// as described in https://github.com/rust-lang/rust/commit/77a96ed5646f7c3ee8897693decc4626fe380643
8491
}
85-
86-
// TODO(@Commeownist): Check if we need to emit some extra debugging info in certain circumstances
87-
// as described in https://github.com/rust-lang/rust/commit/77a96ed5646f7c3ee8897693decc4626fe380643
8892
}
8993

9094
let types = [usize, usize];
@@ -99,7 +103,7 @@ pub(crate) unsafe fn codegen(tcx: TyCtxt<'_>, mods: &mut GccContext, _module_nam
99103
func.add_attribute(FnAttribute::Visibility(gccjit::Visibility::Hidden));
100104
}
101105

102-
let callee = alloc_error_handler_kind.fn_name(sym::oom);
106+
let callee = alloc_error_handler_name(alloc_error_handler_kind);
103107
let args: Vec<_> = types.iter().enumerate()
104108
.map(|(index, typ)| context.new_parameter(None, *typ, &format!("param{}", index)))
105109
.collect();
@@ -123,4 +127,9 @@ pub(crate) unsafe fn codegen(tcx: TyCtxt<'_>, mods: &mut GccContext, _module_nam
123127
let value = tcx.sess.opts.unstable_opts.oom.should_panic();
124128
let value = context.new_rvalue_from_int(i8, value as i32);
125129
global.global_set_initializer_rvalue(value);
130+
131+
let name = NO_ALLOC_SHIM_IS_UNSTABLE.to_string();
132+
let global = context.new_global(None, GlobalKind::Exported, i8, name);
133+
let value = context.new_rvalue_from_int(i8, 0);
134+
global.global_set_initializer_rvalue(value);
126135
}

‎compiler/rustc_codegen_llvm/src/allocator.rs

+79-66
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
use crate::attributes;
22
use libc::c_uint;
3-
use rustc_ast::expand::allocator::{AllocatorKind, AllocatorTy, ALLOCATOR_METHODS};
3+
use rustc_ast::expand::allocator::{
4+
alloc_error_handler_name, default_fn_name, global_fn_name, AllocatorKind, AllocatorTy,
5+
ALLOCATOR_METHODS, NO_ALLOC_SHIM_IS_UNSTABLE,
6+
};
47
use rustc_middle::bug;
58
use rustc_middle::ty::TyCtxt;
69
use rustc_session::config::{DebugInfo, OomStrategy};
7-
use rustc_span::symbol::sym;
810

911
use crate::debuginfo;
1012
use crate::llvm::{self, False, True};
@@ -29,75 +31,78 @@ pub(crate) unsafe fn codegen(
2931
let i8p = llvm::LLVMPointerType(i8, 0);
3032
let void = llvm::LLVMVoidTypeInContext(llcx);
3133

32-
for method in ALLOCATOR_METHODS {
33-
let mut args = Vec::with_capacity(method.inputs.len());
34-
for ty in method.inputs.iter() {
35-
match *ty {
36-
AllocatorTy::Layout => {
37-
args.push(usize); // size
38-
args.push(usize); // align
34+
if kind == AllocatorKind::Default {
35+
for method in ALLOCATOR_METHODS {
36+
let mut args = Vec::with_capacity(method.inputs.len());
37+
for ty in method.inputs.iter() {
38+
match *ty {
39+
AllocatorTy::Layout => {
40+
args.push(usize); // size
41+
args.push(usize); // align
42+
}
43+
AllocatorTy::Ptr => args.push(i8p),
44+
AllocatorTy::Usize => args.push(usize),
45+
46+
AllocatorTy::ResultPtr | AllocatorTy::Unit => panic!("invalid allocator arg"),
3947
}
40-
AllocatorTy::Ptr => args.push(i8p),
41-
AllocatorTy::Usize => args.push(usize),
42-
43-
AllocatorTy::ResultPtr | AllocatorTy::Unit => panic!("invalid allocator arg"),
4448
}
45-
}
46-
let output = match method.output {
47-
AllocatorTy::ResultPtr => Some(i8p),
48-
AllocatorTy::Unit => None,
49+
let output = match method.output {
50+
AllocatorTy::ResultPtr => Some(i8p),
51+
AllocatorTy::Unit => None,
4952

50-
AllocatorTy::Layout | AllocatorTy::Usize | AllocatorTy::Ptr => {
51-
panic!("invalid allocator output")
53+
AllocatorTy::Layout | AllocatorTy::Usize | AllocatorTy::Ptr => {
54+
panic!("invalid allocator output")
55+
}
56+
};
57+
let ty = llvm::LLVMFunctionType(
58+
output.unwrap_or(void),
59+
args.as_ptr(),
60+
args.len() as c_uint,
61+
False,
62+
);
63+
let name = global_fn_name(method.name);
64+
let llfn =
65+
llvm::LLVMRustGetOrInsertFunction(llmod, name.as_ptr().cast(), name.len(), ty);
66+
67+
if tcx.sess.target.default_hidden_visibility {
68+
llvm::LLVMRustSetVisibility(llfn, llvm::Visibility::Hidden);
69+
}
70+
if tcx.sess.must_emit_unwind_tables() {
71+
let uwtable = attributes::uwtable_attr(llcx);
72+
attributes::apply_to_llfn(llfn, llvm::AttributePlace::Function, &[uwtable]);
5273
}
53-
};
54-
let ty = llvm::LLVMFunctionType(
55-
output.unwrap_or(void),
56-
args.as_ptr(),
57-
args.len() as c_uint,
58-
False,
59-
);
60-
let name = format!("__rust_{}", method.name);
61-
let llfn = llvm::LLVMRustGetOrInsertFunction(llmod, name.as_ptr().cast(), name.len(), ty);
62-
63-
if tcx.sess.target.default_hidden_visibility {
64-
llvm::LLVMRustSetVisibility(llfn, llvm::Visibility::Hidden);
65-
}
66-
if tcx.sess.must_emit_unwind_tables() {
67-
let uwtable = attributes::uwtable_attr(llcx);
68-
attributes::apply_to_llfn(llfn, llvm::AttributePlace::Function, &[uwtable]);
69-
}
7074

71-
let callee = kind.fn_name(method.name);
72-
let callee =
73-
llvm::LLVMRustGetOrInsertFunction(llmod, callee.as_ptr().cast(), callee.len(), ty);
74-
llvm::LLVMRustSetVisibility(callee, llvm::Visibility::Hidden);
75-
76-
let llbb = llvm::LLVMAppendBasicBlockInContext(llcx, llfn, "entry\0".as_ptr().cast());
77-
78-
let llbuilder = llvm::LLVMCreateBuilderInContext(llcx);
79-
llvm::LLVMPositionBuilderAtEnd(llbuilder, llbb);
80-
let args = args
81-
.iter()
82-
.enumerate()
83-
.map(|(i, _)| llvm::LLVMGetParam(llfn, i as c_uint))
84-
.collect::<Vec<_>>();
85-
let ret = llvm::LLVMRustBuildCall(
86-
llbuilder,
87-
ty,
88-
callee,
89-
args.as_ptr(),
90-
args.len() as c_uint,
91-
[].as_ptr(),
92-
0 as c_uint,
93-
);
94-
llvm::LLVMSetTailCall(ret, True);
95-
if output.is_some() {
96-
llvm::LLVMBuildRet(llbuilder, ret);
97-
} else {
98-
llvm::LLVMBuildRetVoid(llbuilder);
75+
let callee = default_fn_name(method.name);
76+
let callee =
77+
llvm::LLVMRustGetOrInsertFunction(llmod, callee.as_ptr().cast(), callee.len(), ty);
78+
llvm::LLVMRustSetVisibility(callee, llvm::Visibility::Hidden);
79+
80+
let llbb = llvm::LLVMAppendBasicBlockInContext(llcx, llfn, "entry\0".as_ptr().cast());
81+
82+
let llbuilder = llvm::LLVMCreateBuilderInContext(llcx);
83+
llvm::LLVMPositionBuilderAtEnd(llbuilder, llbb);
84+
let args = args
85+
.iter()
86+
.enumerate()
87+
.map(|(i, _)| llvm::LLVMGetParam(llfn, i as c_uint))
88+
.collect::<Vec<_>>();
89+
let ret = llvm::LLVMRustBuildCall(
90+
llbuilder,
91+
ty,
92+
callee,
93+
args.as_ptr(),
94+
args.len() as c_uint,
95+
[].as_ptr(),
96+
0 as c_uint,
97+
);
98+
llvm::LLVMSetTailCall(ret, True);
99+
if output.is_some() {
100+
llvm::LLVMBuildRet(llbuilder, ret);
101+
} else {
102+
llvm::LLVMBuildRetVoid(llbuilder);
103+
}
104+
llvm::LLVMDisposeBuilder(llbuilder);
99105
}
100-
llvm::LLVMDisposeBuilder(llbuilder);
101106
}
102107

103108
// rust alloc error handler
@@ -118,7 +123,7 @@ pub(crate) unsafe fn codegen(
118123
attributes::apply_to_llfn(llfn, llvm::AttributePlace::Function, &[uwtable]);
119124
}
120125

121-
let callee = alloc_error_handler_kind.fn_name(sym::oom);
126+
let callee = alloc_error_handler_name(alloc_error_handler_kind);
122127
let callee = llvm::LLVMRustGetOrInsertFunction(llmod, callee.as_ptr().cast(), callee.len(), ty);
123128
// -> ! DIFlagNoReturn
124129
attributes::apply_to_llfn(callee, llvm::AttributePlace::Function, &[no_return]);
@@ -156,6 +161,14 @@ pub(crate) unsafe fn codegen(
156161
let llval = llvm::LLVMConstInt(i8, val as u64, False);
157162
llvm::LLVMSetInitializer(ll_g, llval);
158163

164+
let name = NO_ALLOC_SHIM_IS_UNSTABLE;
165+
let ll_g = llvm::LLVMRustGetOrInsertGlobal(llmod, name.as_ptr().cast(), name.len(), i8);
166+
if tcx.sess.target.default_hidden_visibility {
167+
llvm::LLVMRustSetVisibility(ll_g, llvm::Visibility::Hidden);
168+
}
169+
let llval = llvm::LLVMConstInt(i8, 0, False);
170+
llvm::LLVMSetInitializer(ll_g, llval);
171+
159172
if tcx.sess.opts.debuginfo != DebugInfo::None {
160173
let dbg_cx = debuginfo::CodegenUnitDebugContext::new(llmod);
161174
debuginfo::metadata::build_compile_unit_di_node(tcx, module_name, &dbg_cx);

‎compiler/rustc_codegen_ssa/src/back/symbol_export.rs

+12-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use crate::base::allocator_kind_for_codegen;
22

33
use std::collections::hash_map::Entry::*;
44

5-
use rustc_ast::expand::allocator::ALLOCATOR_METHODS;
5+
use rustc_ast::expand::allocator::{ALLOCATOR_METHODS, NO_ALLOC_SHIM_IS_UNSTABLE};
66
use rustc_data_structures::fx::FxHashMap;
77
use rustc_hir::def::DefKind;
88
use rustc_hir::def_id::{CrateNum, DefId, DefIdMap, LocalDefId, LOCAL_CRATE};
@@ -241,6 +241,17 @@ fn exported_symbols_provider_local(
241241
used: false,
242242
},
243243
));
244+
245+
let exported_symbol =
246+
ExportedSymbol::NoDefId(SymbolName::new(tcx, NO_ALLOC_SHIM_IS_UNSTABLE));
247+
symbols.push((
248+
exported_symbol,
249+
SymbolExportInfo {
250+
level: SymbolExportLevel::Rust,
251+
kind: SymbolExportKind::Data,
252+
used: false,
253+
},
254+
))
244255
}
245256

246257
if tcx.sess.instrument_coverage() || tcx.sess.opts.cg.profile_generate.enabled() {

‎compiler/rustc_metadata/src/creader.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use crate::errors;
44
use crate::locator::{CrateError, CrateLocator, CratePaths};
55
use crate::rmeta::{CrateDep, CrateMetadata, CrateNumMap, CrateRoot, MetadataBlob};
66

7-
use rustc_ast::expand::allocator::AllocatorKind;
7+
use rustc_ast::expand::allocator::{alloc_error_handler_name, global_fn_name, AllocatorKind};
88
use rustc_ast::{self as ast, *};
99
use rustc_data_structures::fx::FxHashSet;
1010
use rustc_data_structures::svh::Svh;
@@ -1048,7 +1048,7 @@ fn global_allocator_spans(krate: &ast::Crate) -> Vec<Span> {
10481048
}
10491049
}
10501050

1051-
let name = Symbol::intern(&AllocatorKind::Global.fn_name(sym::alloc));
1051+
let name = Symbol::intern(&global_fn_name(sym::alloc));
10521052
let mut f = Finder { name, spans: Vec::new() };
10531053
visit::walk_crate(&mut f, krate);
10541054
f.spans
@@ -1070,7 +1070,7 @@ fn alloc_error_handler_spans(krate: &ast::Crate) -> Vec<Span> {
10701070
}
10711071
}
10721072

1073-
let name = Symbol::intern(&AllocatorKind::Global.fn_name(sym::oom));
1073+
let name = Symbol::intern(alloc_error_handler_name(AllocatorKind::Global));
10741074
let mut f = Finder { name, spans: Vec::new() };
10751075
visit::walk_crate(&mut f, krate);
10761076
f.spans

‎library/alloc/src/alloc.rs

+11-1
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,9 @@ extern "Rust" {
3737
#[rustc_allocator_zeroed]
3838
#[rustc_nounwind]
3939
fn __rust_alloc_zeroed(size: usize, align: usize) -> *mut u8;
40+
41+
#[cfg(not(bootstrap))]
42+
static __rust_no_alloc_shim_is_unstable: u8;
4043
}
4144

4245
/// The global memory allocator.
@@ -90,7 +93,14 @@ pub use std::alloc::Global;
9093
#[must_use = "losing the pointer will leak memory"]
9194
#[inline]
9295
pub unsafe fn alloc(layout: Layout) -> *mut u8 {
93-
unsafe { __rust_alloc(layout.size(), layout.align()) }
96+
unsafe {
97+
// Make sure we don't accidentally allow omitting the allocator shim in
98+
// stable code until it is actually stabilized.
99+
#[cfg(not(bootstrap))]
100+
core::ptr::read_volatile(&__rust_no_alloc_shim_is_unstable);
101+
102+
__rust_alloc(layout.size(), layout.align())
103+
}
94104
}
95105

96106
/// Deallocate memory with the global allocator.

‎src/tools/miri/src/machine.rs

+4
Original file line numberDiff line numberDiff line change
@@ -651,6 +651,10 @@ impl<'mir, 'tcx> MiriMachine<'mir, 'tcx> {
651651

652652
/// Sets up the "extern statics" for this machine.
653653
fn init_extern_statics(this: &mut MiriInterpCx<'mir, 'tcx>) -> InterpResult<'tcx> {
654+
// "__rust_no_alloc_shim_is_unstable"
655+
let val = ImmTy::from_int(0, this.machine.layouts.u8);
656+
Self::alloc_extern_static(this, "__rust_no_alloc_shim_is_unstable", val)?;
657+
654658
match this.tcx.sess.target.os.as_ref() {
655659
"linux" => {
656660
// "environ"

‎src/tools/miri/src/shims/foreign_items.rs

+40-31
Original file line numberDiff line numberDiff line change
@@ -347,7 +347,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
347347
/// Emulates calling the internal __rust_* allocator functions
348348
fn emulate_allocator(
349349
&mut self,
350-
symbol: Symbol,
351350
default: impl FnOnce(&mut MiriInterpCx<'mir, 'tcx>) -> InterpResult<'tcx>,
352351
) -> InterpResult<'tcx, EmulateByNameResult<'mir, 'tcx>> {
353352
let this = self.eval_context_mut();
@@ -359,11 +358,12 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
359358

360359
match allocator_kind {
361360
AllocatorKind::Global => {
362-
let (body, instance) = this
363-
.lookup_exported_symbol(symbol)?
364-
.expect("symbol should be present if there is a global allocator");
365-
366-
Ok(EmulateByNameResult::MirBody(body, instance))
361+
// When `#[global_allocator]` is used, `__rust_*` is defined by the macro expansion
362+
// of this attribute. As such we have to call an exported Rust function,
363+
// and not execute any Miri shim. Somewhat unintuitively doing so is done
364+
// by returning `NotSupported`, which triggers the `lookup_exported_symbol`
365+
// fallback case in `emulate_foreign_item`.
366+
return Ok(EmulateByNameResult::NotSupported);
367367
}
368368
AllocatorKind::Default => {
369369
default(this)?;
@@ -558,11 +558,13 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
558558

559559
// Rust allocation
560560
"__rust_alloc" | "miri_alloc" => {
561-
let [size, align] = this.check_shim(abi, Abi::Rust, link_name, args)?;
562-
let size = this.read_target_usize(size)?;
563-
let align = this.read_target_usize(align)?;
564-
565561
let default = |this: &mut MiriInterpCx<'mir, 'tcx>| {
562+
// Only call `check_shim` when `#[global_allocator]` isn't used. When that
563+
// macro is used, we act like no shim exists, so that the exported function can run.
564+
let [size, align] = this.check_shim(abi, Abi::Rust, link_name, args)?;
565+
let size = this.read_target_usize(size)?;
566+
let align = this.read_target_usize(align)?;
567+
566568
Self::check_alloc_request(size, align)?;
567569

568570
let memory_kind = match link_name.as_str() {
@@ -581,8 +583,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
581583
};
582584

583585
match link_name.as_str() {
584-
"__rust_alloc" =>
585-
return this.emulate_allocator(Symbol::intern("__rg_alloc"), default),
586+
"__rust_alloc" => return this.emulate_allocator(default),
586587
"miri_alloc" => {
587588
default(this)?;
588589
return Ok(EmulateByNameResult::NeedsJumping);
@@ -591,11 +592,13 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
591592
}
592593
}
593594
"__rust_alloc_zeroed" => {
594-
let [size, align] = this.check_shim(abi, Abi::Rust, link_name, args)?;
595-
let size = this.read_target_usize(size)?;
596-
let align = this.read_target_usize(align)?;
595+
return this.emulate_allocator(|this| {
596+
// See the comment for `__rust_alloc` why `check_shim` is only called in the
597+
// default case.
598+
let [size, align] = this.check_shim(abi, Abi::Rust, link_name, args)?;
599+
let size = this.read_target_usize(size)?;
600+
let align = this.read_target_usize(align)?;
597601

598-
return this.emulate_allocator(Symbol::intern("__rg_alloc_zeroed"), |this| {
599602
Self::check_alloc_request(size, align)?;
600603

601604
let ptr = this.allocate_ptr(
@@ -614,12 +617,15 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
614617
});
615618
}
616619
"__rust_dealloc" | "miri_dealloc" => {
617-
let [ptr, old_size, align] = this.check_shim(abi, Abi::Rust, link_name, args)?;
618-
let ptr = this.read_pointer(ptr)?;
619-
let old_size = this.read_target_usize(old_size)?;
620-
let align = this.read_target_usize(align)?;
621-
622620
let default = |this: &mut MiriInterpCx<'mir, 'tcx>| {
621+
// See the comment for `__rust_alloc` why `check_shim` is only called in the
622+
// default case.
623+
let [ptr, old_size, align] =
624+
this.check_shim(abi, Abi::Rust, link_name, args)?;
625+
let ptr = this.read_pointer(ptr)?;
626+
let old_size = this.read_target_usize(old_size)?;
627+
let align = this.read_target_usize(align)?;
628+
623629
let memory_kind = match link_name.as_str() {
624630
"__rust_dealloc" => MiriMemoryKind::Rust,
625631
"miri_dealloc" => MiriMemoryKind::Miri,
@@ -635,8 +641,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
635641
};
636642

637643
match link_name.as_str() {
638-
"__rust_dealloc" =>
639-
return this.emulate_allocator(Symbol::intern("__rg_dealloc"), default),
644+
"__rust_dealloc" => {
645+
return this.emulate_allocator(default);
646+
}
640647
"miri_dealloc" => {
641648
default(this)?;
642649
return Ok(EmulateByNameResult::NeedsJumping);
@@ -645,15 +652,17 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
645652
}
646653
}
647654
"__rust_realloc" => {
648-
let [ptr, old_size, align, new_size] =
649-
this.check_shim(abi, Abi::Rust, link_name, args)?;
650-
let ptr = this.read_pointer(ptr)?;
651-
let old_size = this.read_target_usize(old_size)?;
652-
let align = this.read_target_usize(align)?;
653-
let new_size = this.read_target_usize(new_size)?;
654-
// No need to check old_size; we anyway check that they match the allocation.
655+
return this.emulate_allocator(|this| {
656+
// See the comment for `__rust_alloc` why `check_shim` is only called in the
657+
// default case.
658+
let [ptr, old_size, align, new_size] =
659+
this.check_shim(abi, Abi::Rust, link_name, args)?;
660+
let ptr = this.read_pointer(ptr)?;
661+
let old_size = this.read_target_usize(old_size)?;
662+
let align = this.read_target_usize(align)?;
663+
let new_size = this.read_target_usize(new_size)?;
664+
// No need to check old_size; we anyway check that they match the allocation.
655665

656-
return this.emulate_allocator(Symbol::intern("__rg_realloc"), |this| {
657666
Self::check_alloc_request(new_size, align)?;
658667

659668
let align = Align::from_bytes(align).unwrap();

‎src/tools/miri/tests/fail/memleak.stderr

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
error: memory leaked: ALLOC (Rust heap, size: 4, align: 4), allocated here:
22
--> RUSTLIB/alloc/src/alloc.rs:LL:CC
33
|
4-
LL | unsafe { __rust_alloc(layout.size(), layout.align()) }
5-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
4+
LL | __rust_alloc(layout.size(), layout.align())
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
66
|
77
= note: inside `std::alloc::alloc` at RUSTLIB/alloc/src/alloc.rs:LL:CC
88
= note: inside `std::alloc::Global::alloc_impl` at RUSTLIB/alloc/src/alloc.rs:LL:CC

‎src/tools/miri/tests/fail/memleak_rc.32bit.stderr

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
error: memory leaked: ALLOC (Rust heap, size: 16, align: 4), allocated here:
22
--> RUSTLIB/alloc/src/alloc.rs:LL:CC
33
|
4-
LL | unsafe { __rust_alloc(layout.size(), layout.align()) }
5-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
4+
LL | __rust_alloc(layout.size(), layout.align())
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
66
|
77
= note: inside `std::alloc::alloc` at RUSTLIB/alloc/src/alloc.rs:LL:CC
88
= note: inside `std::alloc::Global::alloc_impl` at RUSTLIB/alloc/src/alloc.rs:LL:CC

‎src/tools/miri/tests/fail/memleak_rc.64bit.stderr

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
error: memory leaked: ALLOC (Rust heap, size: 32, align: 8), allocated here:
22
--> RUSTLIB/alloc/src/alloc.rs:LL:CC
33
|
4-
LL | unsafe { __rust_alloc(layout.size(), layout.align()) }
5-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
4+
LL | __rust_alloc(layout.size(), layout.align())
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
66
|
77
= note: inside `std::alloc::alloc` at RUSTLIB/alloc/src/alloc.rs:LL:CC
88
= note: inside `std::alloc::Global::alloc_impl` at RUSTLIB/alloc/src/alloc.rs:LL:CC

‎src/tools/miri/tests/pass/shims/fs.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ fn main() {
3131
}
3232

3333
fn host_to_target_path(path: String) -> PathBuf {
34-
use std::ffi::{CStr, CString};
34+
use std::ffi::{c_char, CStr, CString};
3535

3636
let path = CString::new(path).unwrap();
3737
let mut out = Vec::with_capacity(1024);

‎tests/codegen/alloc-optimisation.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
//
22
// no-system-llvm
33
// compile-flags: -O
4-
#![crate_type="lib"]
4+
#![crate_type = "lib"]
55

66
#[no_mangle]
77
pub fn alloc_test(data: u32) {
88
// CHECK-LABEL: @alloc_test
99
// CHECK-NEXT: start:
10+
// CHECK-NEXT: {{.*}} load volatile i8, ptr @__rust_no_alloc_shim_is_unstable, align 1
1011
// CHECK-NEXT: ret void
1112
let x = Box::new(data);
1213
drop(x);

‎tests/codegen/debug-vtable.rs

-2
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,6 @@
1111

1212
// Make sure that vtables don't have the unnamed_addr attribute when debuginfo is enabled.
1313
// This helps debuggers more reliably map from dyn pointer to concrete type.
14-
// CHECK: @vtable.0 = private constant <{
15-
// CHECK: @vtable.1 = private constant <{
1614
// CHECK: @vtable.2 = private constant <{
1715
// CHECK: @vtable.3 = private constant <{
1816
// CHECK: @vtable.4 = private constant <{

‎tests/codegen/vec-optimizes-away.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
// ignore-debug: the debug assertions get in the way
22
// no-system-llvm
33
// compile-flags: -O
4-
#![crate_type="lib"]
4+
#![crate_type = "lib"]
55

66
#[no_mangle]
77
pub fn sum_me() -> i32 {
88
// CHECK-LABEL: @sum_me
99
// CHECK-NEXT: {{^.*:$}}
10+
// CHECK-NEXT: {{.*}} load volatile i8, ptr @__rust_no_alloc_shim_is_unstable, align 1
1011
// CHECK-NEXT: ret i32 6
1112
vec![1, 2, 3].iter().sum::<i32>()
1213
}

‎tests/run-make/no-alloc-shim/Makefile

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
include ../tools.mk
2+
3+
# ignore-cross-compile
4+
# ignore-msvc FIXME(bjorn3) can't figure out how to link with the MSVC toolchain
5+
6+
TARGET_LIBDIR = $$($(RUSTC) --print target-libdir)
7+
8+
all:
9+
$(RUSTC) foo.rs --crate-type bin --emit obj -Cpanic=abort
10+
ifdef IS_MSVC
11+
$(CC) $(CFLAGS) $(TMPDIR)/foo.o $(call OUT_EXE,foo) /link $(TARGET_LIBDIR)/liballoc-*.rlib $(TARGET_LIBDIR)/libcore-*.rlib $(TARGET_LIBDIR)/libcompiler_builtins-*.rlib
12+
$(call OUT_EXE,foo)
13+
else
14+
$(CC) $(CFLAGS) $(TMPDIR)/foo.o $(TARGET_LIBDIR)/liballoc-*.rlib $(TARGET_LIBDIR)/libcore-*.rlib $(TARGET_LIBDIR)/libcompiler_builtins-*.rlib -o $(call RUN_BINFILE,foo)
15+
$(call RUN_BINFILE,foo)
16+
endif
17+
18+
# Check that linking without __rust_no_alloc_shim_is_unstable defined fails
19+
$(RUSTC) foo.rs --crate-type bin --emit obj -Cpanic=abort --cfg check_feature_gate
20+
ifdef IS_MSVC
21+
$(CC) $(CFLAGS) $(TMPDIR)/foo.o $(call OUT_EXE,foo) /link $(TARGET_LIBDIR)/liballoc-*.rlib $(TARGET_LIBDIR)/libcore-*.rlib $(TARGET_LIBDIR)/libcompiler_builtins-*.rlib || exit 0 && exit 1
22+
else
23+
$(CC) $(CFLAGS) $(TMPDIR)/foo.o $(TARGET_LIBDIR)/liballoc-*.rlib $(TARGET_LIBDIR)/libcore-*.rlib $(TARGET_LIBDIR)/libcompiler_builtins-*.rlib -o $(call RUN_BINFILE,foo) || exit 0 && exit 1
24+
endif

‎tests/run-make/no-alloc-shim/foo.rs

+44
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
#![feature(default_alloc_error_handler)]
2+
#![no_std]
3+
#![no_main]
4+
5+
extern crate alloc;
6+
7+
use alloc::alloc::{GlobalAlloc, Layout};
8+
9+
#[panic_handler]
10+
fn panic_handler(_: &core::panic::PanicInfo) -> ! {
11+
loop {}
12+
}
13+
14+
#[no_mangle]
15+
extern "C" fn rust_eh_personality() {
16+
loop {}
17+
}
18+
19+
#[global_allocator]
20+
static ALLOC: Alloc = Alloc;
21+
22+
struct Alloc;
23+
24+
unsafe impl GlobalAlloc for Alloc {
25+
unsafe fn alloc(&self, _: Layout) -> *mut u8 {
26+
core::ptr::null_mut()
27+
}
28+
unsafe fn dealloc(&self, _: *mut u8, _: Layout) {
29+
todo!()
30+
}
31+
}
32+
33+
#[cfg(not(check_feature_gate))]
34+
#[no_mangle]
35+
static __rust_no_alloc_shim_is_unstable: u8 = 0;
36+
37+
#[no_mangle]
38+
extern "C" fn main(_argc: usize, _argv: *const *const i8) -> i32 {
39+
unsafe {
40+
assert_eq!(alloc::alloc::alloc(Layout::new::<()>()), core::ptr::null_mut());
41+
}
42+
43+
0
44+
}

0 commit comments

Comments
 (0)
Please sign in to comment.