Auto merge of #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.)
This commit is contained in:
bors 2023-05-25 16:59:57 +00:00
commit a2b1646c59
19 changed files with 357 additions and 216 deletions

View File

@ -1,20 +1,28 @@
use rustc_span::symbol::{sym, Symbol}; use rustc_span::symbol::{sym, Symbol};
#[derive(Clone, Debug, Copy, HashStable_Generic)] #[derive(Clone, Debug, Copy, Eq, PartialEq, HashStable_Generic)]
pub enum AllocatorKind { pub enum AllocatorKind {
Global, Global,
Default, Default,
} }
impl AllocatorKind { pub fn global_fn_name(base: Symbol) -> String {
pub fn fn_name(&self, base: Symbol) -> String { format!("__rust_{base}")
match *self { }
AllocatorKind::Global => format!("__rg_{base}"),
AllocatorKind::Default => format!("__rdl_{base}"), pub fn default_fn_name(base: Symbol) -> String {
} format!("__rdl_{base}")
}
pub fn alloc_error_handler_name(alloc_error_handler_kind: AllocatorKind) -> &'static str {
match alloc_error_handler_kind {
AllocatorKind::Global => "__rg_oom",
AllocatorKind::Default => "__rdl_oom",
} }
} }
pub const NO_ALLOC_SHIM_IS_UNSTABLE: &str = "__rust_no_alloc_shim_is_unstable";
pub enum AllocatorTy { pub enum AllocatorTy {
Layout, Layout,
Ptr, Ptr,

View File

@ -1,7 +1,7 @@
use crate::util::check_builtin_macro_attribute; use crate::util::check_builtin_macro_attribute;
use rustc_ast::expand::allocator::{ use rustc_ast::expand::allocator::{
AllocatorKind, AllocatorMethod, AllocatorTy, ALLOCATOR_METHODS, global_fn_name, AllocatorMethod, AllocatorTy, ALLOCATOR_METHODS,
}; };
use rustc_ast::ptr::P; use rustc_ast::ptr::P;
use rustc_ast::{self as ast, AttrVec, Expr, FnHeader, FnSig, Generics, Param, StmtKind}; use rustc_ast::{self as ast, AttrVec, Expr, FnHeader, FnSig, Generics, Param, StmtKind};
@ -40,8 +40,7 @@ pub fn expand(
// Generate a bunch of new items using the AllocFnFactory // Generate a bunch of new items using the AllocFnFactory
let span = ecx.with_def_site_ctxt(item.span); let span = ecx.with_def_site_ctxt(item.span);
let f = let f = AllocFnFactory { span, ty_span, global: item.ident, cx: ecx };
AllocFnFactory { span, ty_span, kind: AllocatorKind::Global, global: item.ident, cx: ecx };
// Generate item statements for the allocator methods. // Generate item statements for the allocator methods.
let stmts = ALLOCATOR_METHODS.iter().map(|method| f.allocator_fn(method)).collect(); let stmts = ALLOCATOR_METHODS.iter().map(|method| f.allocator_fn(method)).collect();
@ -63,7 +62,6 @@ pub fn expand(
struct AllocFnFactory<'a, 'b> { struct AllocFnFactory<'a, 'b> {
span: Span, span: Span,
ty_span: Span, ty_span: Span,
kind: AllocatorKind,
global: Ident, global: Ident,
cx: &'b ExtCtxt<'a>, cx: &'b ExtCtxt<'a>,
} }
@ -92,7 +90,7 @@ fn allocator_fn(&self, method: &AllocatorMethod) -> Stmt {
})); }));
let item = self.cx.item( let item = self.cx.item(
self.span, self.span,
Ident::from_str_and_span(&self.kind.fn_name(method.name), self.span), Ident::from_str_and_span(&global_fn_name(method.name), self.span),
self.attrs(), self.attrs(),
kind, kind,
); );

View File

@ -3,10 +3,12 @@
use crate::prelude::*; use crate::prelude::*;
use rustc_ast::expand::allocator::{AllocatorKind, AllocatorTy, ALLOCATOR_METHODS}; use rustc_ast::expand::allocator::{
alloc_error_handler_name, default_fn_name, global_fn_name, AllocatorKind, AllocatorTy,
ALLOCATOR_METHODS, NO_ALLOC_SHIM_IS_UNSTABLE,
};
use rustc_codegen_ssa::base::allocator_kind_for_codegen; use rustc_codegen_ssa::base::allocator_kind_for_codegen;
use rustc_session::config::OomStrategy; use rustc_session::config::OomStrategy;
use rustc_span::symbol::sym;
/// Returns whether an allocator shim was created /// Returns whether an allocator shim was created
pub(crate) fn codegen( pub(crate) fn codegen(
@ -34,41 +36,43 @@ fn codegen_inner(
) { ) {
let usize_ty = module.target_config().pointer_type(); let usize_ty = module.target_config().pointer_type();
for method in ALLOCATOR_METHODS { if kind == AllocatorKind::Default {
let mut arg_tys = Vec::with_capacity(method.inputs.len()); for method in ALLOCATOR_METHODS {
for ty in method.inputs.iter() { let mut arg_tys = Vec::with_capacity(method.inputs.len());
match *ty { for ty in method.inputs.iter() {
AllocatorTy::Layout => { match *ty {
arg_tys.push(usize_ty); // size AllocatorTy::Layout => {
arg_tys.push(usize_ty); // align arg_tys.push(usize_ty); // size
arg_tys.push(usize_ty); // align
}
AllocatorTy::Ptr => arg_tys.push(usize_ty),
AllocatorTy::Usize => arg_tys.push(usize_ty),
AllocatorTy::ResultPtr | AllocatorTy::Unit => panic!("invalid allocator arg"),
} }
AllocatorTy::Ptr => arg_tys.push(usize_ty),
AllocatorTy::Usize => arg_tys.push(usize_ty),
AllocatorTy::ResultPtr | AllocatorTy::Unit => panic!("invalid allocator arg"),
} }
let output = match method.output {
AllocatorTy::ResultPtr => Some(usize_ty),
AllocatorTy::Unit => None,
AllocatorTy::Layout | AllocatorTy::Usize | AllocatorTy::Ptr => {
panic!("invalid allocator output")
}
};
let sig = Signature {
call_conv: module.target_config().default_call_conv,
params: arg_tys.iter().cloned().map(AbiParam::new).collect(),
returns: output.into_iter().map(AbiParam::new).collect(),
};
crate::common::create_wrapper_function(
module,
unwind_context,
sig,
&global_fn_name(method.name),
&default_fn_name(method.name),
);
} }
let output = match method.output {
AllocatorTy::ResultPtr => Some(usize_ty),
AllocatorTy::Unit => None,
AllocatorTy::Layout | AllocatorTy::Usize | AllocatorTy::Ptr => {
panic!("invalid allocator output")
}
};
let sig = Signature {
call_conv: module.target_config().default_call_conv,
params: arg_tys.iter().cloned().map(AbiParam::new).collect(),
returns: output.into_iter().map(AbiParam::new).collect(),
};
crate::common::create_wrapper_function(
module,
unwind_context,
sig,
&format!("__rust_{}", method.name),
&kind.fn_name(method.name),
);
} }
let sig = Signature { let sig = Signature {
@ -81,7 +85,7 @@ fn codegen_inner(
unwind_context, unwind_context,
sig, sig,
"__rust_alloc_error_handler", "__rust_alloc_error_handler",
&alloc_error_handler_kind.fn_name(sym::oom), &alloc_error_handler_name(alloc_error_handler_kind),
); );
let data_id = module.declare_data(OomStrategy::SYMBOL, Linkage::Export, false, false).unwrap(); let data_id = module.declare_data(OomStrategy::SYMBOL, Linkage::Export, false, false).unwrap();
@ -90,4 +94,11 @@ fn codegen_inner(
let val = oom_strategy.should_panic(); let val = oom_strategy.should_panic();
data_ctx.define(Box::new([val])); data_ctx.define(Box::new([val]));
module.define_data(data_id, &data_ctx).unwrap(); module.define_data(data_id, &data_ctx).unwrap();
let data_id =
module.declare_data(NO_ALLOC_SHIM_IS_UNSTABLE, Linkage::Export, false, false).unwrap();
let mut data_ctx = DataContext::new();
data_ctx.set_align(1);
data_ctx.define(Box::new([0]));
module.define_data(data_id, &data_ctx).unwrap();
} }

View File

@ -1,11 +1,13 @@
#[cfg(feature="master")] #[cfg(feature="master")]
use gccjit::FnAttribute; use gccjit::FnAttribute;
use gccjit::{FunctionType, GlobalKind, ToRValue}; use gccjit::{FunctionType, GlobalKind, ToRValue};
use rustc_ast::expand::allocator::{AllocatorKind, AllocatorTy, ALLOCATOR_METHODS}; use rustc_ast::expand::allocator::{
alloc_error_handler_name, default_fn_name, global_fn_name, AllocatorKind, AllocatorTy,
ALLOCATOR_METHODS, NO_ALLOC_SHIM_IS_UNSTABLE,
};
use rustc_middle::bug; use rustc_middle::bug;
use rustc_middle::ty::TyCtxt; use rustc_middle::ty::TyCtxt;
use rustc_session::config::OomStrategy; use rustc_session::config::OomStrategy;
use rustc_span::symbol::sym;
use crate::GccContext; use crate::GccContext;
@ -22,69 +24,71 @@ pub(crate) unsafe fn codegen(tcx: TyCtxt<'_>, mods: &mut GccContext, _module_nam
let i8p = i8.make_pointer(); let i8p = i8.make_pointer();
let void = context.new_type::<()>(); let void = context.new_type::<()>();
for method in ALLOCATOR_METHODS { if kind == AllocatorKind::Default {
let mut types = Vec::with_capacity(method.inputs.len()); for method in ALLOCATOR_METHODS {
for ty in method.inputs.iter() { let mut types = Vec::with_capacity(method.inputs.len());
match *ty { for ty in method.inputs.iter() {
AllocatorTy::Layout => { match *ty {
types.push(usize); AllocatorTy::Layout => {
types.push(usize); types.push(usize);
types.push(usize);
}
AllocatorTy::Ptr => types.push(i8p),
AllocatorTy::Usize => types.push(usize),
AllocatorTy::ResultPtr | AllocatorTy::Unit => panic!("invalid allocator arg"),
} }
AllocatorTy::Ptr => types.push(i8p),
AllocatorTy::Usize => types.push(usize),
AllocatorTy::ResultPtr | AllocatorTy::Unit => panic!("invalid allocator arg"),
} }
} let output = match method.output {
let output = match method.output { AllocatorTy::ResultPtr => Some(i8p),
AllocatorTy::ResultPtr => Some(i8p), AllocatorTy::Unit => None,
AllocatorTy::Unit => None,
AllocatorTy::Layout | AllocatorTy::Usize | AllocatorTy::Ptr => { AllocatorTy::Layout | AllocatorTy::Usize | AllocatorTy::Ptr => {
panic!("invalid allocator output") panic!("invalid allocator output")
}
};
let name = global_fn_name(method.name);
let args: Vec<_> = types.iter().enumerate()
.map(|(index, typ)| context.new_parameter(None, *typ, &format!("param{}", index)))
.collect();
let func = context.new_function(None, FunctionType::Exported, output.unwrap_or(void), &args, name, false);
if tcx.sess.target.options.default_hidden_visibility {
#[cfg(feature="master")]
func.add_attribute(FnAttribute::Visibility(gccjit::Visibility::Hidden));
}
if tcx.sess.must_emit_unwind_tables() {
// TODO(antoyo): emit unwind tables.
} }
};
let name = format!("__rust_{}", method.name);
let args: Vec<_> = types.iter().enumerate() let callee = default_fn_name(method.name);
.map(|(index, typ)| context.new_parameter(None, *typ, &format!("param{}", index))) let args: Vec<_> = types.iter().enumerate()
.collect(); .map(|(index, typ)| context.new_parameter(None, *typ, &format!("param{}", index)))
let func = context.new_function(None, FunctionType::Exported, output.unwrap_or(void), &args, name, false); .collect();
let callee = context.new_function(None, FunctionType::Extern, output.unwrap_or(void), &args, callee, false);
if tcx.sess.target.options.default_hidden_visibility {
#[cfg(feature="master")] #[cfg(feature="master")]
func.add_attribute(FnAttribute::Visibility(gccjit::Visibility::Hidden)); callee.add_attribute(FnAttribute::Visibility(gccjit::Visibility::Hidden));
}
if tcx.sess.must_emit_unwind_tables() {
// TODO(antoyo): emit unwind tables.
}
let callee = kind.fn_name(method.name); let block = func.new_block("entry");
let args: Vec<_> = types.iter().enumerate()
.map(|(index, typ)| context.new_parameter(None, *typ, &format!("param{}", index)))
.collect();
let callee = context.new_function(None, FunctionType::Extern, output.unwrap_or(void), &args, callee, false);
#[cfg(feature="master")]
callee.add_attribute(FnAttribute::Visibility(gccjit::Visibility::Hidden));
let block = func.new_block("entry"); let args = args
.iter()
.enumerate()
.map(|(i, _)| func.get_param(i as i32).to_rvalue())
.collect::<Vec<_>>();
let ret = context.new_call(None, callee, &args);
//llvm::LLVMSetTailCall(ret, True);
if output.is_some() {
block.end_with_return(None, ret);
}
else {
block.end_with_void_return(None);
}
let args = args // TODO(@Commeownist): Check if we need to emit some extra debugging info in certain circumstances
.iter() // as described in https://github.com/rust-lang/rust/commit/77a96ed5646f7c3ee8897693decc4626fe380643
.enumerate()
.map(|(i, _)| func.get_param(i as i32).to_rvalue())
.collect::<Vec<_>>();
let ret = context.new_call(None, callee, &args);
//llvm::LLVMSetTailCall(ret, True);
if output.is_some() {
block.end_with_return(None, ret);
} }
else {
block.end_with_void_return(None);
}
// TODO(@Commeownist): Check if we need to emit some extra debugging info in certain circumstances
// as described in https://github.com/rust-lang/rust/commit/77a96ed5646f7c3ee8897693decc4626fe380643
} }
let types = [usize, usize]; let types = [usize, usize];
@ -99,7 +103,7 @@ pub(crate) unsafe fn codegen(tcx: TyCtxt<'_>, mods: &mut GccContext, _module_nam
func.add_attribute(FnAttribute::Visibility(gccjit::Visibility::Hidden)); func.add_attribute(FnAttribute::Visibility(gccjit::Visibility::Hidden));
} }
let callee = alloc_error_handler_kind.fn_name(sym::oom); let callee = alloc_error_handler_name(alloc_error_handler_kind);
let args: Vec<_> = types.iter().enumerate() let args: Vec<_> = types.iter().enumerate()
.map(|(index, typ)| context.new_parameter(None, *typ, &format!("param{}", index))) .map(|(index, typ)| context.new_parameter(None, *typ, &format!("param{}", index)))
.collect(); .collect();
@ -123,4 +127,9 @@ pub(crate) unsafe fn codegen(tcx: TyCtxt<'_>, mods: &mut GccContext, _module_nam
let value = tcx.sess.opts.unstable_opts.oom.should_panic(); let value = tcx.sess.opts.unstable_opts.oom.should_panic();
let value = context.new_rvalue_from_int(i8, value as i32); let value = context.new_rvalue_from_int(i8, value as i32);
global.global_set_initializer_rvalue(value); global.global_set_initializer_rvalue(value);
let name = NO_ALLOC_SHIM_IS_UNSTABLE.to_string();
let global = context.new_global(None, GlobalKind::Exported, i8, name);
let value = context.new_rvalue_from_int(i8, 0);
global.global_set_initializer_rvalue(value);
} }

View File

@ -1,10 +1,12 @@
use crate::attributes; use crate::attributes;
use libc::c_uint; use libc::c_uint;
use rustc_ast::expand::allocator::{AllocatorKind, AllocatorTy, ALLOCATOR_METHODS}; use rustc_ast::expand::allocator::{
alloc_error_handler_name, default_fn_name, global_fn_name, AllocatorKind, AllocatorTy,
ALLOCATOR_METHODS, NO_ALLOC_SHIM_IS_UNSTABLE,
};
use rustc_middle::bug; use rustc_middle::bug;
use rustc_middle::ty::TyCtxt; use rustc_middle::ty::TyCtxt;
use rustc_session::config::{DebugInfo, OomStrategy}; use rustc_session::config::{DebugInfo, OomStrategy};
use rustc_span::symbol::sym;
use crate::debuginfo; use crate::debuginfo;
use crate::llvm::{self, False, True}; use crate::llvm::{self, False, True};
@ -29,75 +31,78 @@ pub(crate) unsafe fn codegen(
let i8p = llvm::LLVMPointerType(i8, 0); let i8p = llvm::LLVMPointerType(i8, 0);
let void = llvm::LLVMVoidTypeInContext(llcx); let void = llvm::LLVMVoidTypeInContext(llcx);
for method in ALLOCATOR_METHODS { if kind == AllocatorKind::Default {
let mut args = Vec::with_capacity(method.inputs.len()); for method in ALLOCATOR_METHODS {
for ty in method.inputs.iter() { let mut args = Vec::with_capacity(method.inputs.len());
match *ty { for ty in method.inputs.iter() {
AllocatorTy::Layout => { match *ty {
args.push(usize); // size AllocatorTy::Layout => {
args.push(usize); // align args.push(usize); // size
args.push(usize); // align
}
AllocatorTy::Ptr => args.push(i8p),
AllocatorTy::Usize => args.push(usize),
AllocatorTy::ResultPtr | AllocatorTy::Unit => panic!("invalid allocator arg"),
} }
AllocatorTy::Ptr => args.push(i8p),
AllocatorTy::Usize => args.push(usize),
AllocatorTy::ResultPtr | AllocatorTy::Unit => panic!("invalid allocator arg"),
} }
} let output = match method.output {
let output = match method.output { AllocatorTy::ResultPtr => Some(i8p),
AllocatorTy::ResultPtr => Some(i8p), AllocatorTy::Unit => None,
AllocatorTy::Unit => None,
AllocatorTy::Layout | AllocatorTy::Usize | AllocatorTy::Ptr => { AllocatorTy::Layout | AllocatorTy::Usize | AllocatorTy::Ptr => {
panic!("invalid allocator output") panic!("invalid allocator output")
}
};
let ty = llvm::LLVMFunctionType(
output.unwrap_or(void),
args.as_ptr(),
args.len() as c_uint,
False,
);
let name = global_fn_name(method.name);
let llfn =
llvm::LLVMRustGetOrInsertFunction(llmod, name.as_ptr().cast(), name.len(), ty);
if tcx.sess.target.default_hidden_visibility {
llvm::LLVMRustSetVisibility(llfn, llvm::Visibility::Hidden);
}
if tcx.sess.must_emit_unwind_tables() {
let uwtable = attributes::uwtable_attr(llcx);
attributes::apply_to_llfn(llfn, llvm::AttributePlace::Function, &[uwtable]);
} }
};
let ty = llvm::LLVMFunctionType(
output.unwrap_or(void),
args.as_ptr(),
args.len() as c_uint,
False,
);
let name = format!("__rust_{}", method.name);
let llfn = llvm::LLVMRustGetOrInsertFunction(llmod, name.as_ptr().cast(), name.len(), ty);
if tcx.sess.target.default_hidden_visibility { let callee = default_fn_name(method.name);
llvm::LLVMRustSetVisibility(llfn, llvm::Visibility::Hidden); let callee =
llvm::LLVMRustGetOrInsertFunction(llmod, callee.as_ptr().cast(), callee.len(), ty);
llvm::LLVMRustSetVisibility(callee, llvm::Visibility::Hidden);
let llbb = llvm::LLVMAppendBasicBlockInContext(llcx, llfn, "entry\0".as_ptr().cast());
let llbuilder = llvm::LLVMCreateBuilderInContext(llcx);
llvm::LLVMPositionBuilderAtEnd(llbuilder, llbb);
let args = args
.iter()
.enumerate()
.map(|(i, _)| llvm::LLVMGetParam(llfn, i as c_uint))
.collect::<Vec<_>>();
let ret = llvm::LLVMRustBuildCall(
llbuilder,
ty,
callee,
args.as_ptr(),
args.len() as c_uint,
[].as_ptr(),
0 as c_uint,
);
llvm::LLVMSetTailCall(ret, True);
if output.is_some() {
llvm::LLVMBuildRet(llbuilder, ret);
} else {
llvm::LLVMBuildRetVoid(llbuilder);
}
llvm::LLVMDisposeBuilder(llbuilder);
} }
if tcx.sess.must_emit_unwind_tables() {
let uwtable = attributes::uwtable_attr(llcx);
attributes::apply_to_llfn(llfn, llvm::AttributePlace::Function, &[uwtable]);
}
let callee = kind.fn_name(method.name);
let callee =
llvm::LLVMRustGetOrInsertFunction(llmod, callee.as_ptr().cast(), callee.len(), ty);
llvm::LLVMRustSetVisibility(callee, llvm::Visibility::Hidden);
let llbb = llvm::LLVMAppendBasicBlockInContext(llcx, llfn, "entry\0".as_ptr().cast());
let llbuilder = llvm::LLVMCreateBuilderInContext(llcx);
llvm::LLVMPositionBuilderAtEnd(llbuilder, llbb);
let args = args
.iter()
.enumerate()
.map(|(i, _)| llvm::LLVMGetParam(llfn, i as c_uint))
.collect::<Vec<_>>();
let ret = llvm::LLVMRustBuildCall(
llbuilder,
ty,
callee,
args.as_ptr(),
args.len() as c_uint,
[].as_ptr(),
0 as c_uint,
);
llvm::LLVMSetTailCall(ret, True);
if output.is_some() {
llvm::LLVMBuildRet(llbuilder, ret);
} else {
llvm::LLVMBuildRetVoid(llbuilder);
}
llvm::LLVMDisposeBuilder(llbuilder);
} }
// rust alloc error handler // rust alloc error handler
@ -118,7 +123,7 @@ pub(crate) unsafe fn codegen(
attributes::apply_to_llfn(llfn, llvm::AttributePlace::Function, &[uwtable]); attributes::apply_to_llfn(llfn, llvm::AttributePlace::Function, &[uwtable]);
} }
let callee = alloc_error_handler_kind.fn_name(sym::oom); let callee = alloc_error_handler_name(alloc_error_handler_kind);
let callee = llvm::LLVMRustGetOrInsertFunction(llmod, callee.as_ptr().cast(), callee.len(), ty); let callee = llvm::LLVMRustGetOrInsertFunction(llmod, callee.as_ptr().cast(), callee.len(), ty);
// -> ! DIFlagNoReturn // -> ! DIFlagNoReturn
attributes::apply_to_llfn(callee, llvm::AttributePlace::Function, &[no_return]); attributes::apply_to_llfn(callee, llvm::AttributePlace::Function, &[no_return]);
@ -156,6 +161,14 @@ pub(crate) unsafe fn codegen(
let llval = llvm::LLVMConstInt(i8, val as u64, False); let llval = llvm::LLVMConstInt(i8, val as u64, False);
llvm::LLVMSetInitializer(ll_g, llval); llvm::LLVMSetInitializer(ll_g, llval);
let name = NO_ALLOC_SHIM_IS_UNSTABLE;
let ll_g = llvm::LLVMRustGetOrInsertGlobal(llmod, name.as_ptr().cast(), name.len(), i8);
if tcx.sess.target.default_hidden_visibility {
llvm::LLVMRustSetVisibility(ll_g, llvm::Visibility::Hidden);
}
let llval = llvm::LLVMConstInt(i8, 0, False);
llvm::LLVMSetInitializer(ll_g, llval);
if tcx.sess.opts.debuginfo != DebugInfo::None { if tcx.sess.opts.debuginfo != DebugInfo::None {
let dbg_cx = debuginfo::CodegenUnitDebugContext::new(llmod); let dbg_cx = debuginfo::CodegenUnitDebugContext::new(llmod);
debuginfo::metadata::build_compile_unit_di_node(tcx, module_name, &dbg_cx); debuginfo::metadata::build_compile_unit_di_node(tcx, module_name, &dbg_cx);

View File

@ -2,7 +2,7 @@
use std::collections::hash_map::Entry::*; use std::collections::hash_map::Entry::*;
use rustc_ast::expand::allocator::ALLOCATOR_METHODS; use rustc_ast::expand::allocator::{ALLOCATOR_METHODS, NO_ALLOC_SHIM_IS_UNSTABLE};
use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::fx::FxHashMap;
use rustc_hir::def::DefKind; use rustc_hir::def::DefKind;
use rustc_hir::def_id::{CrateNum, DefId, DefIdMap, LocalDefId, LOCAL_CRATE}; use rustc_hir::def_id::{CrateNum, DefId, DefIdMap, LocalDefId, LOCAL_CRATE};
@ -241,6 +241,17 @@ fn exported_symbols_provider_local(
used: false, used: false,
}, },
)); ));
let exported_symbol =
ExportedSymbol::NoDefId(SymbolName::new(tcx, NO_ALLOC_SHIM_IS_UNSTABLE));
symbols.push((
exported_symbol,
SymbolExportInfo {
level: SymbolExportLevel::Rust,
kind: SymbolExportKind::Data,
used: false,
},
))
} }
if tcx.sess.instrument_coverage() || tcx.sess.opts.cg.profile_generate.enabled() { if tcx.sess.instrument_coverage() || tcx.sess.opts.cg.profile_generate.enabled() {

View File

@ -4,7 +4,7 @@
use crate::locator::{CrateError, CrateLocator, CratePaths}; use crate::locator::{CrateError, CrateLocator, CratePaths};
use crate::rmeta::{CrateDep, CrateMetadata, CrateNumMap, CrateRoot, MetadataBlob}; use crate::rmeta::{CrateDep, CrateMetadata, CrateNumMap, CrateRoot, MetadataBlob};
use rustc_ast::expand::allocator::AllocatorKind; use rustc_ast::expand::allocator::{alloc_error_handler_name, global_fn_name, AllocatorKind};
use rustc_ast::{self as ast, *}; use rustc_ast::{self as ast, *};
use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::fx::FxHashSet;
use rustc_data_structures::svh::Svh; use rustc_data_structures::svh::Svh;
@ -1048,7 +1048,7 @@ fn visit_item(&mut self, item: &'ast ast::Item) {
} }
} }
let name = Symbol::intern(&AllocatorKind::Global.fn_name(sym::alloc)); let name = Symbol::intern(&global_fn_name(sym::alloc));
let mut f = Finder { name, spans: Vec::new() }; let mut f = Finder { name, spans: Vec::new() };
visit::walk_crate(&mut f, krate); visit::walk_crate(&mut f, krate);
f.spans f.spans
@ -1070,7 +1070,7 @@ fn visit_item(&mut self, item: &'ast ast::Item) {
} }
} }
let name = Symbol::intern(&AllocatorKind::Global.fn_name(sym::oom)); let name = Symbol::intern(alloc_error_handler_name(AllocatorKind::Global));
let mut f = Finder { name, spans: Vec::new() }; let mut f = Finder { name, spans: Vec::new() };
visit::walk_crate(&mut f, krate); visit::walk_crate(&mut f, krate);
f.spans f.spans

View File

@ -37,6 +37,9 @@
#[rustc_allocator_zeroed] #[rustc_allocator_zeroed]
#[rustc_nounwind] #[rustc_nounwind]
fn __rust_alloc_zeroed(size: usize, align: usize) -> *mut u8; fn __rust_alloc_zeroed(size: usize, align: usize) -> *mut u8;
#[cfg(not(bootstrap))]
static __rust_no_alloc_shim_is_unstable: u8;
} }
/// The global memory allocator. /// The global memory allocator.
@ -90,7 +93,14 @@
#[must_use = "losing the pointer will leak memory"] #[must_use = "losing the pointer will leak memory"]
#[inline] #[inline]
pub unsafe fn alloc(layout: Layout) -> *mut u8 { pub unsafe fn alloc(layout: Layout) -> *mut u8 {
unsafe { __rust_alloc(layout.size(), layout.align()) } unsafe {
// Make sure we don't accidentally allow omitting the allocator shim in
// stable code until it is actually stabilized.
#[cfg(not(bootstrap))]
core::ptr::read_volatile(&__rust_no_alloc_shim_is_unstable);
__rust_alloc(layout.size(), layout.align())
}
} }
/// Deallocate memory with the global allocator. /// Deallocate memory with the global allocator.

View File

@ -651,6 +651,10 @@ fn alloc_extern_static(
/// Sets up the "extern statics" for this machine. /// Sets up the "extern statics" for this machine.
fn init_extern_statics(this: &mut MiriInterpCx<'mir, 'tcx>) -> InterpResult<'tcx> { fn init_extern_statics(this: &mut MiriInterpCx<'mir, 'tcx>) -> InterpResult<'tcx> {
// "__rust_no_alloc_shim_is_unstable"
let val = ImmTy::from_int(0, this.machine.layouts.u8);
Self::alloc_extern_static(this, "__rust_no_alloc_shim_is_unstable", val)?;
match this.tcx.sess.target.os.as_ref() { match this.tcx.sess.target.os.as_ref() {
"linux" => { "linux" => {
// "environ" // "environ"

View File

@ -347,7 +347,6 @@ fn emulate_foreign_item(
/// Emulates calling the internal __rust_* allocator functions /// Emulates calling the internal __rust_* allocator functions
fn emulate_allocator( fn emulate_allocator(
&mut self, &mut self,
symbol: Symbol,
default: impl FnOnce(&mut MiriInterpCx<'mir, 'tcx>) -> InterpResult<'tcx>, default: impl FnOnce(&mut MiriInterpCx<'mir, 'tcx>) -> InterpResult<'tcx>,
) -> InterpResult<'tcx, EmulateByNameResult<'mir, 'tcx>> { ) -> InterpResult<'tcx, EmulateByNameResult<'mir, 'tcx>> {
let this = self.eval_context_mut(); let this = self.eval_context_mut();
@ -359,11 +358,12 @@ fn emulate_allocator(
match allocator_kind { match allocator_kind {
AllocatorKind::Global => { AllocatorKind::Global => {
let (body, instance) = this // When `#[global_allocator]` is used, `__rust_*` is defined by the macro expansion
.lookup_exported_symbol(symbol)? // of this attribute. As such we have to call an exported Rust function,
.expect("symbol should be present if there is a global allocator"); // and not execute any Miri shim. Somewhat unintuitively doing so is done
// by returning `NotSupported`, which triggers the `lookup_exported_symbol`
Ok(EmulateByNameResult::MirBody(body, instance)) // fallback case in `emulate_foreign_item`.
return Ok(EmulateByNameResult::NotSupported);
} }
AllocatorKind::Default => { AllocatorKind::Default => {
default(this)?; default(this)?;
@ -558,11 +558,13 @@ fn emulate_foreign_item_by_name(
// Rust allocation // Rust allocation
"__rust_alloc" | "miri_alloc" => { "__rust_alloc" | "miri_alloc" => {
let [size, align] = this.check_shim(abi, Abi::Rust, link_name, args)?;
let size = this.read_target_usize(size)?;
let align = this.read_target_usize(align)?;
let default = |this: &mut MiriInterpCx<'mir, 'tcx>| { let default = |this: &mut MiriInterpCx<'mir, 'tcx>| {
// Only call `check_shim` when `#[global_allocator]` isn't used. When that
// macro is used, we act like no shim exists, so that the exported function can run.
let [size, align] = this.check_shim(abi, Abi::Rust, link_name, args)?;
let size = this.read_target_usize(size)?;
let align = this.read_target_usize(align)?;
Self::check_alloc_request(size, align)?; Self::check_alloc_request(size, align)?;
let memory_kind = match link_name.as_str() { let memory_kind = match link_name.as_str() {
@ -581,8 +583,7 @@ fn emulate_foreign_item_by_name(
}; };
match link_name.as_str() { match link_name.as_str() {
"__rust_alloc" => "__rust_alloc" => return this.emulate_allocator(default),
return this.emulate_allocator(Symbol::intern("__rg_alloc"), default),
"miri_alloc" => { "miri_alloc" => {
default(this)?; default(this)?;
return Ok(EmulateByNameResult::NeedsJumping); return Ok(EmulateByNameResult::NeedsJumping);
@ -591,11 +592,13 @@ fn emulate_foreign_item_by_name(
} }
} }
"__rust_alloc_zeroed" => { "__rust_alloc_zeroed" => {
let [size, align] = this.check_shim(abi, Abi::Rust, link_name, args)?; return this.emulate_allocator(|this| {
let size = this.read_target_usize(size)?; // See the comment for `__rust_alloc` why `check_shim` is only called in the
let align = this.read_target_usize(align)?; // default case.
let [size, align] = this.check_shim(abi, Abi::Rust, link_name, args)?;
let size = this.read_target_usize(size)?;
let align = this.read_target_usize(align)?;
return this.emulate_allocator(Symbol::intern("__rg_alloc_zeroed"), |this| {
Self::check_alloc_request(size, align)?; Self::check_alloc_request(size, align)?;
let ptr = this.allocate_ptr( let ptr = this.allocate_ptr(
@ -614,12 +617,15 @@ fn emulate_foreign_item_by_name(
}); });
} }
"__rust_dealloc" | "miri_dealloc" => { "__rust_dealloc" | "miri_dealloc" => {
let [ptr, old_size, align] = this.check_shim(abi, Abi::Rust, link_name, args)?;
let ptr = this.read_pointer(ptr)?;
let old_size = this.read_target_usize(old_size)?;
let align = this.read_target_usize(align)?;
let default = |this: &mut MiriInterpCx<'mir, 'tcx>| { let default = |this: &mut MiriInterpCx<'mir, 'tcx>| {
// See the comment for `__rust_alloc` why `check_shim` is only called in the
// default case.
let [ptr, old_size, align] =
this.check_shim(abi, Abi::Rust, link_name, args)?;
let ptr = this.read_pointer(ptr)?;
let old_size = this.read_target_usize(old_size)?;
let align = this.read_target_usize(align)?;
let memory_kind = match link_name.as_str() { let memory_kind = match link_name.as_str() {
"__rust_dealloc" => MiriMemoryKind::Rust, "__rust_dealloc" => MiriMemoryKind::Rust,
"miri_dealloc" => MiriMemoryKind::Miri, "miri_dealloc" => MiriMemoryKind::Miri,
@ -635,8 +641,9 @@ fn emulate_foreign_item_by_name(
}; };
match link_name.as_str() { match link_name.as_str() {
"__rust_dealloc" => "__rust_dealloc" => {
return this.emulate_allocator(Symbol::intern("__rg_dealloc"), default), return this.emulate_allocator(default);
}
"miri_dealloc" => { "miri_dealloc" => {
default(this)?; default(this)?;
return Ok(EmulateByNameResult::NeedsJumping); return Ok(EmulateByNameResult::NeedsJumping);
@ -645,15 +652,17 @@ fn emulate_foreign_item_by_name(
} }
} }
"__rust_realloc" => { "__rust_realloc" => {
let [ptr, old_size, align, new_size] = return this.emulate_allocator(|this| {
this.check_shim(abi, Abi::Rust, link_name, args)?; // See the comment for `__rust_alloc` why `check_shim` is only called in the
let ptr = this.read_pointer(ptr)?; // default case.
let old_size = this.read_target_usize(old_size)?; let [ptr, old_size, align, new_size] =
let align = this.read_target_usize(align)?; this.check_shim(abi, Abi::Rust, link_name, args)?;
let new_size = this.read_target_usize(new_size)?; let ptr = this.read_pointer(ptr)?;
// No need to check old_size; we anyway check that they match the allocation. let old_size = this.read_target_usize(old_size)?;
let align = this.read_target_usize(align)?;
let new_size = this.read_target_usize(new_size)?;
// No need to check old_size; we anyway check that they match the allocation.
return this.emulate_allocator(Symbol::intern("__rg_realloc"), |this| {
Self::check_alloc_request(new_size, align)?; Self::check_alloc_request(new_size, align)?;
let align = Align::from_bytes(align).unwrap(); let align = Align::from_bytes(align).unwrap();

View File

@ -1,8 +1,8 @@
error: memory leaked: ALLOC (Rust heap, size: 4, align: 4), allocated here: error: memory leaked: ALLOC (Rust heap, size: 4, align: 4), allocated here:
--> RUSTLIB/alloc/src/alloc.rs:LL:CC --> RUSTLIB/alloc/src/alloc.rs:LL:CC
| |
LL | unsafe { __rust_alloc(layout.size(), layout.align()) } LL | __rust_alloc(layout.size(), layout.align())
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
| |
= note: inside `std::alloc::alloc` at RUSTLIB/alloc/src/alloc.rs:LL:CC = note: inside `std::alloc::alloc` at RUSTLIB/alloc/src/alloc.rs:LL:CC
= note: inside `std::alloc::Global::alloc_impl` at RUSTLIB/alloc/src/alloc.rs:LL:CC = note: inside `std::alloc::Global::alloc_impl` at RUSTLIB/alloc/src/alloc.rs:LL:CC

View File

@ -1,8 +1,8 @@
error: memory leaked: ALLOC (Rust heap, size: 16, align: 4), allocated here: error: memory leaked: ALLOC (Rust heap, size: 16, align: 4), allocated here:
--> RUSTLIB/alloc/src/alloc.rs:LL:CC --> RUSTLIB/alloc/src/alloc.rs:LL:CC
| |
LL | unsafe { __rust_alloc(layout.size(), layout.align()) } LL | __rust_alloc(layout.size(), layout.align())
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
| |
= note: inside `std::alloc::alloc` at RUSTLIB/alloc/src/alloc.rs:LL:CC = note: inside `std::alloc::alloc` at RUSTLIB/alloc/src/alloc.rs:LL:CC
= note: inside `std::alloc::Global::alloc_impl` at RUSTLIB/alloc/src/alloc.rs:LL:CC = note: inside `std::alloc::Global::alloc_impl` at RUSTLIB/alloc/src/alloc.rs:LL:CC

View File

@ -1,8 +1,8 @@
error: memory leaked: ALLOC (Rust heap, size: 32, align: 8), allocated here: error: memory leaked: ALLOC (Rust heap, size: 32, align: 8), allocated here:
--> RUSTLIB/alloc/src/alloc.rs:LL:CC --> RUSTLIB/alloc/src/alloc.rs:LL:CC
| |
LL | unsafe { __rust_alloc(layout.size(), layout.align()) } LL | __rust_alloc(layout.size(), layout.align())
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
| |
= note: inside `std::alloc::alloc` at RUSTLIB/alloc/src/alloc.rs:LL:CC = note: inside `std::alloc::alloc` at RUSTLIB/alloc/src/alloc.rs:LL:CC
= note: inside `std::alloc::Global::alloc_impl` at RUSTLIB/alloc/src/alloc.rs:LL:CC = note: inside `std::alloc::Global::alloc_impl` at RUSTLIB/alloc/src/alloc.rs:LL:CC

View File

@ -31,7 +31,7 @@ fn main() {
} }
fn host_to_target_path(path: String) -> PathBuf { fn host_to_target_path(path: String) -> PathBuf {
use std::ffi::{CStr, CString}; use std::ffi::{c_char, CStr, CString};
let path = CString::new(path).unwrap(); let path = CString::new(path).unwrap();
let mut out = Vec::with_capacity(1024); let mut out = Vec::with_capacity(1024);

View File

@ -1,12 +1,13 @@
// //
// no-system-llvm // no-system-llvm
// compile-flags: -O // compile-flags: -O
#![crate_type="lib"] #![crate_type = "lib"]
#[no_mangle] #[no_mangle]
pub fn alloc_test(data: u32) { pub fn alloc_test(data: u32) {
// CHECK-LABEL: @alloc_test // CHECK-LABEL: @alloc_test
// CHECK-NEXT: start: // CHECK-NEXT: start:
// CHECK-NEXT: {{.*}} load volatile i8, ptr @__rust_no_alloc_shim_is_unstable, align 1
// CHECK-NEXT: ret void // CHECK-NEXT: ret void
let x = Box::new(data); let x = Box::new(data);
drop(x); drop(x);

View File

@ -11,8 +11,6 @@
// Make sure that vtables don't have the unnamed_addr attribute when debuginfo is enabled. // Make sure that vtables don't have the unnamed_addr attribute when debuginfo is enabled.
// This helps debuggers more reliably map from dyn pointer to concrete type. // This helps debuggers more reliably map from dyn pointer to concrete type.
// CHECK: @vtable.0 = private constant <{
// CHECK: @vtable.1 = private constant <{
// CHECK: @vtable.2 = private constant <{ // CHECK: @vtable.2 = private constant <{
// CHECK: @vtable.3 = private constant <{ // CHECK: @vtable.3 = private constant <{
// CHECK: @vtable.4 = private constant <{ // CHECK: @vtable.4 = private constant <{

View File

@ -1,12 +1,13 @@
// ignore-debug: the debug assertions get in the way // ignore-debug: the debug assertions get in the way
// no-system-llvm // no-system-llvm
// compile-flags: -O // compile-flags: -O
#![crate_type="lib"] #![crate_type = "lib"]
#[no_mangle] #[no_mangle]
pub fn sum_me() -> i32 { pub fn sum_me() -> i32 {
// CHECK-LABEL: @sum_me // CHECK-LABEL: @sum_me
// CHECK-NEXT: {{^.*:$}} // CHECK-NEXT: {{^.*:$}}
// CHECK-NEXT: {{.*}} load volatile i8, ptr @__rust_no_alloc_shim_is_unstable, align 1
// CHECK-NEXT: ret i32 6 // CHECK-NEXT: ret i32 6
vec![1, 2, 3].iter().sum::<i32>() vec![1, 2, 3].iter().sum::<i32>()
} }

View File

@ -0,0 +1,24 @@
include ../tools.mk
# ignore-cross-compile
# ignore-msvc FIXME(bjorn3) can't figure out how to link with the MSVC toolchain
TARGET_LIBDIR = $$($(RUSTC) --print target-libdir)
all:
$(RUSTC) foo.rs --crate-type bin --emit obj -Cpanic=abort
ifdef IS_MSVC
$(CC) $(CFLAGS) $(TMPDIR)/foo.o $(call OUT_EXE,foo) /link $(TARGET_LIBDIR)/liballoc-*.rlib $(TARGET_LIBDIR)/libcore-*.rlib $(TARGET_LIBDIR)/libcompiler_builtins-*.rlib
$(call OUT_EXE,foo)
else
$(CC) $(CFLAGS) $(TMPDIR)/foo.o $(TARGET_LIBDIR)/liballoc-*.rlib $(TARGET_LIBDIR)/libcore-*.rlib $(TARGET_LIBDIR)/libcompiler_builtins-*.rlib -o $(call RUN_BINFILE,foo)
$(call RUN_BINFILE,foo)
endif
# Check that linking without __rust_no_alloc_shim_is_unstable defined fails
$(RUSTC) foo.rs --crate-type bin --emit obj -Cpanic=abort --cfg check_feature_gate
ifdef IS_MSVC
$(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
else
$(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
endif

View File

@ -0,0 +1,44 @@
#![feature(default_alloc_error_handler)]
#![no_std]
#![no_main]
extern crate alloc;
use alloc::alloc::{GlobalAlloc, Layout};
#[panic_handler]
fn panic_handler(_: &core::panic::PanicInfo) -> ! {
loop {}
}
#[no_mangle]
extern "C" fn rust_eh_personality() {
loop {}
}
#[global_allocator]
static ALLOC: Alloc = Alloc;
struct Alloc;
unsafe impl GlobalAlloc for Alloc {
unsafe fn alloc(&self, _: Layout) -> *mut u8 {
core::ptr::null_mut()
}
unsafe fn dealloc(&self, _: *mut u8, _: Layout) {
todo!()
}
}
#[cfg(not(check_feature_gate))]
#[no_mangle]
static __rust_no_alloc_shim_is_unstable: u8 = 0;
#[no_mangle]
extern "C" fn main(_argc: usize, _argv: *const *const i8) -> i32 {
unsafe {
assert_eq!(alloc::alloc::alloc(Layout::new::<()>()), core::ptr::null_mut());
}
0
}