Skip to content

Commit

Permalink
Rewrite implementation of #[alloc_error_handler]
Browse files Browse the repository at this point in the history
The new implementation doesn't use weak lang items and instead changes
`#[alloc_error_handler]` to an attribute macro just like
`#[global_allocator]`.

The attribute will generate the `__rg_oom` function which is called by
the compiler-generated `__rust_alloc_error_handler`. If no `__rg_oom`
function is defined in any crate then the compiler shim will call
`__rdl_oom` in the alloc crate which will simply panic.

This also fixes link errors with `-C link-dead-code` with
`default_alloc_error_handler`: `__rg_oom` was previously defined in the
alloc crate and would attempt to reference the `oom` lang item, even if
it didn't exist. This worked as long as `__rg_oom` was excluded from
linking since it was not called.

This is a prerequisite for the stabilization of
`default_alloc_error_handler` (#102318).
  • Loading branch information
Amanieu committed Oct 31, 2022
1 parent 2afca78 commit 56074b5
Show file tree
Hide file tree
Showing 40 changed files with 441 additions and 166 deletions.
104 changes: 104 additions & 0 deletions compiler/rustc_builtin_macros/src/alloc_error_handler.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
use crate::util::check_builtin_macro_attribute;

use rustc_ast::ptr::P;
use rustc_ast::{self as ast, FnHeader, FnSig, Generics, StmtKind};
use rustc_ast::{Fn, ItemKind, Stmt, TyKind, Unsafe};
use rustc_expand::base::{Annotatable, ExtCtxt};
use rustc_span::symbol::{kw, sym, Ident};
use rustc_span::Span;
use thin_vec::thin_vec;

pub fn expand(
ecx: &mut ExtCtxt<'_>,
_span: Span,
meta_item: &ast::MetaItem,
item: Annotatable,
) -> Vec<Annotatable> {
check_builtin_macro_attribute(ecx, meta_item, sym::alloc_error_handler);

let orig_item = item.clone();
let not_function = || {
ecx.sess
.parse_sess
.span_diagnostic
.span_err(item.span(), "alloc_error_handler must be a function");
vec![orig_item.clone()]
};

// Allow using `#[alloc_error_handler]` on an item statement
// FIXME - if we get deref patterns, use them to reduce duplication here
let (item, is_stmt, sig_span) = match &item {
Annotatable::Item(item) => match item.kind {
ItemKind::Fn(ref fn_kind) => (item, false, ecx.with_def_site_ctxt(fn_kind.sig.span)),
_ => return not_function(),
},
Annotatable::Stmt(stmt) => match &stmt.kind {
StmtKind::Item(item_) => match item_.kind {
ItemKind::Fn(ref fn_kind) => {
(item_, true, ecx.with_def_site_ctxt(fn_kind.sig.span))
}
_ => return not_function(),
},
_ => return not_function(),
},
_ => return not_function(),
};

// Generate a bunch of new items using the AllocFnFactory
let span = ecx.with_def_site_ctxt(item.span);

// Generate item statements for the allocator methods.
let stmts = vec![generate_handler(ecx, item.ident, span, sig_span)];

// Generate anonymous constant serving as container for the allocator methods.
let const_ty = ecx.ty(sig_span, TyKind::Tup(Vec::new()));
let const_body = ecx.expr_block(ecx.block(span, stmts));
let const_item = ecx.item_const(span, Ident::new(kw::Underscore, span), const_ty, const_body);
let const_item = if is_stmt {
Annotatable::Stmt(P(ecx.stmt_item(span, const_item)))
} else {
Annotatable::Item(const_item)
};

// Return the original item and the new methods.
vec![orig_item, const_item]
}

// #[rustc_std_internal_symbol]
// unsafe fn __rg_oom(size: usize, align: usize) -> ! {
// handler(core::alloc::Layout::from_size_align_unchecked(size, align))
// }
fn generate_handler(cx: &ExtCtxt<'_>, handler: Ident, span: Span, sig_span: Span) -> Stmt {
let usize = cx.path_ident(span, Ident::new(sym::usize, span));
let ty_usize = cx.ty_path(usize);
let size = Ident::from_str_and_span("size", span);
let align = Ident::from_str_and_span("align", span);

let layout_new = cx.std_path(&[sym::alloc, sym::Layout, sym::from_size_align_unchecked]);
let layout_new = cx.expr_path(cx.path(span, layout_new));
let layout =
cx.expr_call(span, layout_new, vec![cx.expr_ident(span, size), cx.expr_ident(span, align)]);

let call = cx.expr_call_ident(sig_span, handler, vec![layout]);

let never = ast::FnRetTy::Ty(cx.ty(span, TyKind::Never));
let params = vec![cx.param(span, size, ty_usize.clone()), cx.param(span, align, ty_usize)];
let decl = cx.fn_decl(params, never);
let header = FnHeader { unsafety: Unsafe::Yes(span), ..FnHeader::default() };
let sig = FnSig { decl, header, span: span };

let body = Some(cx.block_expr(call));
let kind = ItemKind::Fn(Box::new(Fn {
defaultness: ast::Defaultness::Final,
sig,
generics: Generics::default(),
body,
}));

let special = sym::rustc_std_internal_symbol;
let special = cx.meta_word(span, special);
let attrs = thin_vec![cx.attribute(special)];

let item = cx.item(span, Ident::from_str_and_span("__rg_oom", span), attrs, kind);
cx.stmt_item(sig_span, item)
}
2 changes: 2 additions & 0 deletions compiler/rustc_builtin_macros/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ use rustc_expand::base::{MacroExpanderFn, ResolverExpand, SyntaxExtensionKind};
use rustc_expand::proc_macro::BangProcMacro;
use rustc_span::symbol::sym;

mod alloc_error_handler;
mod assert;
mod cfg;
mod cfg_accessible;
Expand Down Expand Up @@ -94,6 +95,7 @@ pub fn register_builtin_macros(resolver: &mut dyn ResolverExpand) {
}

register_attr! {
alloc_error_handler: alloc_error_handler::expand,
bench: test::expand_bench,
cfg_accessible: cfg_accessible::Expander,
cfg_eval: cfg_eval::expand,
Expand Down
9 changes: 5 additions & 4 deletions compiler/rustc_codegen_cranelift/src/allocator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use crate::prelude::*;

use rustc_ast::expand::allocator::{AllocatorKind, AllocatorTy, ALLOCATOR_METHODS};
use rustc_session::config::OomStrategy;
use rustc_span::symbol::sym;

/// Returns whether an allocator shim was created
pub(crate) fn codegen(
Expand All @@ -23,7 +24,7 @@ pub(crate) fn codegen(
module,
unwind_context,
kind,
tcx.lang_items().oom().is_some(),
tcx.alloc_error_handler_kind(()).unwrap(),
tcx.sess.opts.unstable_opts.oom,
);
true
Expand All @@ -36,7 +37,7 @@ fn codegen_inner(
module: &mut impl Module,
unwind_context: &mut UnwindContext,
kind: AllocatorKind,
has_alloc_error_handler: bool,
alloc_error_handler_kind: AllocatorKind,
oom_strategy: OomStrategy,
) {
let usize_ty = module.target_config().pointer_type();
Expand Down Expand Up @@ -108,12 +109,12 @@ fn codegen_inner(
returns: vec![],
};

let callee_name = if has_alloc_error_handler { "__rg_oom" } else { "__rdl_oom" };
let callee_name = alloc_error_handler_kind.fn_name(sym::oom);

let func_id =
module.declare_function("__rust_alloc_error_handler", Linkage::Export, &sig).unwrap();

let callee_func_id = module.declare_function(callee_name, Linkage::Import, &sig).unwrap();
let callee_func_id = module.declare_function(&callee_name, Linkage::Import, &sig).unwrap();

let mut ctx = Context::new();
ctx.func.signature = sig;
Expand Down
11 changes: 2 additions & 9 deletions compiler/rustc_codegen_gcc/src/allocator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use rustc_span::symbol::sym;

use crate::GccContext;

pub(crate) unsafe fn codegen(tcx: TyCtxt<'_>, mods: &mut GccContext, _module_name: &str, kind: AllocatorKind, has_alloc_error_handler: bool) {
pub(crate) unsafe fn codegen(tcx: TyCtxt<'_>, mods: &mut GccContext, _module_name: &str, kind: AllocatorKind, alloc_error_handler_kind: AllocatorKind) {
let context = &mods.context;
let usize =
match tcx.sess.target.pointer_width {
Expand Down Expand Up @@ -90,14 +90,7 @@ pub(crate) unsafe fn codegen(tcx: TyCtxt<'_>, mods: &mut GccContext, _module_nam
.collect();
let func = context.new_function(None, FunctionType::Exported, void, &args, name, false);

let kind =
if has_alloc_error_handler {
AllocatorKind::Global
}
else {
AllocatorKind::Default
};
let callee = kind.fn_name(sym::oom);
let callee = alloc_error_handler_kind.fn_name(sym::oom);
let args: Vec<_> = types.iter().enumerate()
.map(|(index, typ)| context.new_parameter(None, *typ, &format!("param{}", index)))
.collect();
Expand Down
4 changes: 2 additions & 2 deletions compiler/rustc_codegen_gcc/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -153,11 +153,11 @@ impl CodegenBackend for GccCodegenBackend {
}

impl ExtraBackendMethods for GccCodegenBackend {
fn codegen_allocator<'tcx>(&self, tcx: TyCtxt<'tcx>, module_name: &str, kind: AllocatorKind, has_alloc_error_handler: bool) -> Self::Module {
fn codegen_allocator<'tcx>(&self, tcx: TyCtxt<'tcx>, module_name: &str, kind: AllocatorKind, alloc_error_handler_kind: AllocatorKind) -> Self::Module {
let mut mods = GccContext {
context: Context::default(),
};
unsafe { allocator::codegen(tcx, &mut mods, module_name, kind, has_alloc_error_handler); }
unsafe { allocator::codegen(tcx, &mut mods, module_name, kind, alloc_error_handler_kind); }
mods
}

Expand Down
5 changes: 2 additions & 3 deletions compiler/rustc_codegen_llvm/src/allocator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ pub(crate) unsafe fn codegen(
module_llvm: &mut ModuleLlvm,
module_name: &str,
kind: AllocatorKind,
has_alloc_error_handler: bool,
alloc_error_handler_kind: AllocatorKind,
) {
let llcx = &*module_llvm.llcx;
let llmod = module_llvm.llmod();
Expand Down Expand Up @@ -117,8 +117,7 @@ pub(crate) unsafe fn codegen(
attributes::apply_to_llfn(llfn, llvm::AttributePlace::Function, &[uwtable]);
}

let kind = if has_alloc_error_handler { AllocatorKind::Global } else { AllocatorKind::Default };
let callee = kind.fn_name(sym::oom);
let callee = alloc_error_handler_kind.fn_name(sym::oom);
let callee = llvm::LLVMRustGetOrInsertFunction(llmod, callee.as_ptr().cast(), callee.len(), ty);
// -> ! DIFlagNoReturn
attributes::apply_to_llfn(callee, llvm::AttributePlace::Function, &[no_return]);
Expand Down
4 changes: 2 additions & 2 deletions compiler/rustc_codegen_llvm/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -108,11 +108,11 @@ impl ExtraBackendMethods for LlvmCodegenBackend {
tcx: TyCtxt<'tcx>,
module_name: &str,
kind: AllocatorKind,
has_alloc_error_handler: bool,
alloc_error_handler_kind: AllocatorKind,
) -> ModuleLlvm {
let mut module_llvm = ModuleLlvm::new_metadata(tcx, module_name);
unsafe {
allocator::codegen(tcx, &mut module_llvm, module_name, kind, has_alloc_error_handler);
allocator::codegen(tcx, &mut module_llvm, module_name, kind, alloc_error_handler_kind);
}
module_llvm
}
Expand Down
7 changes: 5 additions & 2 deletions compiler/rustc_codegen_ssa/src/back/symbol_export.rs
Original file line number Diff line number Diff line change
Expand Up @@ -193,8 +193,11 @@ fn exported_symbols_provider_local<'tcx>(
}

if tcx.allocator_kind(()).is_some() {
for method in ALLOCATOR_METHODS {
let symbol_name = format!("__rust_{}", method.name);
for symbol_name in ALLOCATOR_METHODS
.iter()
.map(|method| format!("__rust_{}", method.name))
.chain(["__rust_alloc_error_handler".to_string(), OomStrategy::SYMBOL.to_string()])
{
let exported_symbol = ExportedSymbol::NoDefId(SymbolName::new(tcx, &symbol_name));

symbols.push((
Expand Down
9 changes: 8 additions & 1 deletion compiler/rustc_codegen_ssa/src/base.rs
Original file line number Diff line number Diff line change
Expand Up @@ -638,7 +638,14 @@ pub fn codegen_crate<B: ExtraBackendMethods>(
let llmod_id =
cgu_name_builder.build_cgu_name(LOCAL_CRATE, &["crate"], Some("allocator")).to_string();
let module_llvm = tcx.sess.time("write_allocator_module", || {
backend.codegen_allocator(tcx, &llmod_id, kind, tcx.lang_items().oom().is_some())
backend.codegen_allocator(
tcx,
&llmod_id,
kind,
// If allocator_kind is Some then alloc_error_handler_kind must
// also be Some.
tcx.alloc_error_handler_kind(()).unwrap(),
)
});

Some(ModuleCodegen { name: llmod_id, module_llvm, kind: ModuleKind::Allocator })
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_codegen_ssa/src/traits/backend.rs
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ pub trait ExtraBackendMethods: CodegenBackend + WriteBackendMethods + Sized + Se
tcx: TyCtxt<'tcx>,
module_name: &str,
kind: AllocatorKind,
has_alloc_error_handler: bool,
alloc_error_handler_kind: AllocatorKind,
) -> Self::Module;
/// This generates the codegen unit and returns it along with
/// a `u64` giving an estimate of the unit's processing cost.
Expand Down
16 changes: 16 additions & 0 deletions compiler/rustc_error_messages/locales/en-US/metadata.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -150,12 +150,28 @@ metadata_no_multiple_global_alloc =
metadata_prev_global_alloc =
previous global allocator defined here
metadata_no_multiple_alloc_error_handler =
cannot define multiple allocation error handlers
.label = cannot define a new allocation error handler
metadata_prev_alloc_error_handler =
previous allocation error handler defined here
metadata_conflicting_global_alloc =
the `#[global_allocator]` in {$other_crate_name} conflicts with global allocator in: {$crate_name}
metadata_conflicting_alloc_error_handler =
the `#[alloc_error_handler]` in {$other_crate_name} conflicts with allocation error handler in: {$crate_name}
metadata_global_alloc_required =
no global memory allocator found but one is required; link to std or add `#[global_allocator]` to a static item that implements the GlobalAlloc trait
metadata_alloc_func_required =
`#[alloc_error_handler]` function required, but not found
metadata_missing_alloc_error_handler =
use `#![feature(default_alloc_error_handler)]` for a default error handler
metadata_no_transitive_needs_dep =
the crate `{$crate_name}` cannot depend on a crate that needs {$needs_crate_name}, but it depends on `{$deps_crate_name}`
Expand Down
6 changes: 0 additions & 6 deletions compiler/rustc_error_messages/locales/en-US/passes.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -367,12 +367,6 @@ passes_unknown_external_lang_item =
passes_missing_panic_handler =
`#[panic_handler]` function required, but not found
passes_alloc_func_required =
`#[alloc_error_handler]` function required, but not found
passes_missing_alloc_error_handler =
use `#![feature(default_alloc_error_handler)]` for a default error handler
passes_missing_lang_item =
language item required, but not found: `{$name}`
.note = this can occur when a binary crate with `#![no_std]` is compiled for a target where `{$name}` is defined in the standard library
Expand Down
4 changes: 0 additions & 4 deletions compiler/rustc_feature/src/builtin_attrs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -554,10 +554,6 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
rustc_attr!(rustc_reallocator, Normal, template!(Word), WarnFollowing, IMPL_DETAIL),
rustc_attr!(rustc_deallocator, Normal, template!(Word), WarnFollowing, IMPL_DETAIL),
rustc_attr!(rustc_allocator_zeroed, Normal, template!(Word), WarnFollowing, IMPL_DETAIL),
gated!(
alloc_error_handler, Normal, template!(Word), WarnFollowing,
experimental!(alloc_error_handler)
),
gated!(
default_lib_allocator, Normal, template!(Word), WarnFollowing, allocator_internals,
experimental!(default_lib_allocator),
Expand Down
5 changes: 1 addition & 4 deletions compiler/rustc_hir/src/lang_items.rs
Original file line number Diff line number Diff line change
Expand Up @@ -126,14 +126,12 @@ impl<CTX> HashStable<CTX> for LangItem {
}

/// Extracts the first `lang = "$name"` out of a list of attributes.
/// The attributes `#[panic_handler]` and `#[alloc_error_handler]`
/// are also extracted out when found.
/// The `#[panic_handler]` attribute is also extracted out when found.
pub fn extract(attrs: &[ast::Attribute]) -> Option<(Symbol, Span)> {
attrs.iter().find_map(|attr| {
Some(match attr {
_ if attr.has_name(sym::lang) => (attr.value_str()?, attr.span),
_ if attr.has_name(sym::panic_handler) => (sym::panic_impl, attr.span),
_ if attr.has_name(sym::alloc_error_handler) => (sym::oom, attr.span),
_ => return None,
})
})
Expand Down Expand Up @@ -240,7 +238,6 @@ language_item_table! {
ExchangeMalloc, sym::exchange_malloc, exchange_malloc_fn, Target::Fn, GenericRequirement::None;
BoxFree, sym::box_free, box_free_fn, Target::Fn, GenericRequirement::Minimum(1);
DropInPlace, sym::drop_in_place, drop_in_place_fn, Target::Fn, GenericRequirement::Minimum(1);
Oom, sym::oom, oom, Target::Fn, GenericRequirement::None;
AllocLayout, sym::alloc_layout, alloc_layout, Target::Struct, GenericRequirement::None;

Start, sym::start, start_fn, Target::Fn, GenericRequirement::Exact(1);
Expand Down
1 change: 0 additions & 1 deletion compiler/rustc_hir/src/weak_lang_items.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,5 +27,4 @@ weak_lang_items! {
PanicImpl, rust_begin_unwind;
EhPersonality, rust_eh_personality;
EhCatchTypeinfo, rust_eh_catch_typeinfo;
Oom, rust_oom;
}
Loading

0 comments on commit 56074b5

Please sign in to comment.