Skip to content

Commit d6f3a4e

Browse files
committed
Auto merge of #88098 - Amanieu:oom_panic, r=nagisa
Implement -Z oom=panic This PR removes the `#[rustc_allocator_nounwind]` attribute on `alloc_error_handler` which allows it to unwind with a panic instead of always aborting. This is then used to implement `-Z oom=panic` as per RFC 2116 (tracking issue #43596). Perf and binary size tests show negligible impact.
2 parents cd11905 + 0254e31 commit d6f3a4e

File tree

9 files changed

+114
-10
lines changed

9 files changed

+114
-10
lines changed

compiler/rustc_codegen_cranelift/src/allocator.rs

+16-1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
use crate::prelude::*;
55

66
use rustc_ast::expand::allocator::{AllocatorKind, AllocatorTy, ALLOCATOR_METHODS};
7+
use rustc_session::config::OomStrategy;
78

89
/// Returns whether an allocator shim was created
910
pub(crate) fn codegen(
@@ -18,7 +19,13 @@ pub(crate) fn codegen(
1819
if any_dynamic_crate {
1920
false
2021
} else if let Some(kind) = tcx.allocator_kind(()) {
21-
codegen_inner(module, unwind_context, kind, tcx.lang_items().oom().is_some());
22+
codegen_inner(
23+
module,
24+
unwind_context,
25+
kind,
26+
tcx.lang_items().oom().is_some(),
27+
tcx.sess.opts.debugging_opts.oom,
28+
);
2229
true
2330
} else {
2431
false
@@ -30,6 +37,7 @@ fn codegen_inner(
3037
unwind_context: &mut UnwindContext,
3138
kind: AllocatorKind,
3239
has_alloc_error_handler: bool,
40+
oom_strategy: OomStrategy,
3341
) {
3442
let usize_ty = module.target_config().pointer_type();
3543

@@ -129,4 +137,11 @@ fn codegen_inner(
129137
}
130138
module.define_function(func_id, &mut ctx).unwrap();
131139
unwind_context.add_function(func_id, &ctx, module.isa());
140+
141+
let data_id = module.declare_data(OomStrategy::SYMBOL, Linkage::Export, false, false).unwrap();
142+
let mut data_ctx = DataContext::new();
143+
data_ctx.set_align(1);
144+
let val = oom_strategy.should_panic();
145+
data_ctx.define(Box::new([val]));
146+
module.define_data(data_id, &data_ctx).unwrap();
132147
}

compiler/rustc_codegen_gcc/src/allocator.rs

+8-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1-
use gccjit::{FunctionType, ToRValue};
1+
use gccjit::{FunctionType, GlobalKind, ToRValue};
22
use rustc_ast::expand::allocator::{AllocatorKind, AllocatorTy, ALLOCATOR_METHODS};
33
use rustc_middle::bug;
44
use rustc_middle::ty::TyCtxt;
5+
use rustc_session::config::OomStrategy;
56
use rustc_span::symbol::sym;
67

78
use crate::GccContext;
@@ -113,4 +114,10 @@ pub(crate) unsafe fn codegen(tcx: TyCtxt<'_>, mods: &mut GccContext, _module_nam
113114
let _ret = context.new_call(None, callee, &args);
114115
//llvm::LLVMSetTailCall(ret, True);
115116
block.end_with_void_return(None);
117+
118+
let name = OomStrategy::SYMBOL.to_string();
119+
let global = context.new_global(None, GlobalKind::Exported, i8, name);
120+
let value = tcx.sess.opts.debugging_opts.oom.should_panic();
121+
let value = context.new_rvalue_from_int(i8, value as i32);
122+
global.global_set_initializer_rvalue(value);
116123
}

compiler/rustc_codegen_llvm/src/allocator.rs

+11-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use libc::c_uint;
33
use rustc_ast::expand::allocator::{AllocatorKind, AllocatorTy, ALLOCATOR_METHODS};
44
use rustc_middle::bug;
55
use rustc_middle::ty::TyCtxt;
6-
use rustc_session::config::DebugInfo;
6+
use rustc_session::config::{DebugInfo, OomStrategy};
77
use rustc_span::symbol::sym;
88

99
use crate::debuginfo;
@@ -139,6 +139,16 @@ pub(crate) unsafe fn codegen(
139139
llvm::LLVMBuildRetVoid(llbuilder);
140140
llvm::LLVMDisposeBuilder(llbuilder);
141141

142+
// __rust_alloc_error_handler_should_panic
143+
let name = OomStrategy::SYMBOL;
144+
let ll_g = llvm::LLVMRustGetOrInsertGlobal(llmod, name.as_ptr().cast(), name.len(), i8);
145+
if tcx.sess.target.default_hidden_visibility {
146+
llvm::LLVMRustSetVisibility(ll_g, llvm::Visibility::Hidden);
147+
}
148+
let val = tcx.sess.opts.debugging_opts.oom.should_panic();
149+
let llval = llvm::LLVMConstInt(i8, val as u64, False);
150+
llvm::LLVMSetInitializer(ll_g, llval);
151+
142152
if tcx.sess.opts.debuginfo != DebugInfo::None {
143153
let dbg_cx = debuginfo::CodegenUnitDebugContext::new(llmod);
144154
debuginfo::metadata::build_compile_unit_di_node(tcx, module_name, &dbg_cx);

compiler/rustc_interface/src/tests.rs

+3-2
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@ use rustc_session::config::{
99
rustc_optgroups, ErrorOutputType, ExternLocation, LocationDetail, Options, Passes,
1010
};
1111
use rustc_session::config::{
12-
BranchProtection, Externs, OutputType, OutputTypes, PAuthKey, PacRet, SymbolManglingVersion,
13-
WasiExecModel,
12+
BranchProtection, Externs, OomStrategy, OutputType, OutputTypes, PAuthKey, PacRet,
13+
SymbolManglingVersion, WasiExecModel,
1414
};
1515
use rustc_session::config::{CFGuard, ExternEntry, LinkerPluginLto, LtoCli, SwitchWithOptPath};
1616
use rustc_session::lint::Level;
@@ -758,6 +758,7 @@ fn test_debugging_options_tracking_hash() {
758758
tracked!(no_link, true);
759759
tracked!(no_unique_section_names, true);
760760
tracked!(no_profiler_runtime, true);
761+
tracked!(oom, OomStrategy::Panic);
761762
tracked!(osx_rpath_install_name, true);
762763
tracked!(panic_abort_tests, true);
763764
tracked!(panic_in_drop, PanicStrategy::Abort);

compiler/rustc_session/src/config.rs

+25-3
Original file line numberDiff line numberDiff line change
@@ -2842,9 +2842,9 @@ impl PpMode {
28422842
crate mod dep_tracking {
28432843
use super::{
28442844
BranchProtection, CFGuard, CFProtection, CrateType, DebugInfo, ErrorOutputType,
2845-
InstrumentCoverage, LdImpl, LinkerPluginLto, LocationDetail, LtoCli, OptLevel, OutputType,
2846-
OutputTypes, Passes, SourceFileHashAlgorithm, SwitchWithOptPath, SymbolManglingVersion,
2847-
TrimmedDefPaths,
2845+
InstrumentCoverage, LdImpl, LinkerPluginLto, LocationDetail, LtoCli, OomStrategy, OptLevel,
2846+
OutputType, OutputTypes, Passes, SourceFileHashAlgorithm, SwitchWithOptPath,
2847+
SymbolManglingVersion, TrimmedDefPaths,
28482848
};
28492849
use crate::lint;
28502850
use crate::options::WasiExecModel;
@@ -2940,6 +2940,7 @@ crate mod dep_tracking {
29402940
RealFileName,
29412941
LocationDetail,
29422942
BranchProtection,
2943+
OomStrategy,
29432944
);
29442945

29452946
impl<T1, T2> DepTrackingHash for (T1, T2)
@@ -3029,3 +3030,24 @@ crate mod dep_tracking {
30293030
}
30303031
}
30313032
}
3033+
3034+
/// Default behavior to use in out-of-memory situations.
3035+
#[derive(Clone, Copy, PartialEq, Hash, Debug, Encodable, Decodable, HashStable_Generic)]
3036+
pub enum OomStrategy {
3037+
/// Generate a panic that can be caught by `catch_unwind`.
3038+
Panic,
3039+
3040+
/// Abort the process immediately.
3041+
Abort,
3042+
}
3043+
3044+
impl OomStrategy {
3045+
pub const SYMBOL: &'static str = "__rust_alloc_error_handler_should_panic";
3046+
3047+
pub fn should_panic(self) -> u8 {
3048+
match self {
3049+
OomStrategy::Panic => 1,
3050+
OomStrategy::Abort => 0,
3051+
}
3052+
}
3053+
}

compiler/rustc_session/src/options.rs

+12
Original file line numberDiff line numberDiff line change
@@ -375,6 +375,7 @@ mod desc {
375375
pub const parse_passes: &str = "a space-separated list of passes, or `all`";
376376
pub const parse_panic_strategy: &str = "either `unwind` or `abort`";
377377
pub const parse_opt_panic_strategy: &str = parse_panic_strategy;
378+
pub const parse_oom_strategy: &str = "either `panic` or `abort`";
378379
pub const parse_relro_level: &str = "one of: `full`, `partial`, or `off`";
379380
pub const parse_sanitizers: &str = "comma separated list of sanitizers: `address`, `cfi`, `hwaddress`, `leak`, `memory`, `memtag`, or `thread`";
380381
pub const parse_sanitizer_memory_track_origins: &str = "0, 1, or 2";
@@ -620,6 +621,15 @@ mod parse {
620621
true
621622
}
622623

624+
crate fn parse_oom_strategy(slot: &mut OomStrategy, v: Option<&str>) -> bool {
625+
match v {
626+
Some("panic") => *slot = OomStrategy::Panic,
627+
Some("abort") => *slot = OomStrategy::Abort,
628+
_ => return false,
629+
}
630+
true
631+
}
632+
623633
crate fn parse_relro_level(slot: &mut Option<RelroLevel>, v: Option<&str>) -> bool {
624634
match v {
625635
Some(s) => match s.parse::<RelroLevel>() {
@@ -1329,6 +1339,8 @@ options! {
13291339
"prevent automatic injection of the profiler_builtins crate"),
13301340
normalize_docs: bool = (false, parse_bool, [TRACKED],
13311341
"normalize associated items in rustdoc when generating documentation"),
1342+
oom: OomStrategy = (OomStrategy::Abort, parse_oom_strategy, [TRACKED],
1343+
"panic strategy for out-of-memory handling"),
13321344
osx_rpath_install_name: bool = (false, parse_bool, [TRACKED],
13331345
"pass `-install_name @rpath/...` to the macOS linker (default: no)"),
13341346
panic_abort_tests: bool = (false, parse_bool, [TRACKED],

library/std/src/alloc.rs

+15-1
Original file line numberDiff line numberDiff line change
@@ -315,7 +315,21 @@ pub fn take_alloc_error_hook() -> fn(Layout) {
315315
}
316316

317317
fn default_alloc_error_hook(layout: Layout) {
318-
rtprintpanic!("memory allocation of {} bytes failed\n", layout.size());
318+
#[cfg(not(bootstrap))]
319+
extern "Rust" {
320+
// This symbol is emitted by rustc next to __rust_alloc_error_handler.
321+
// Its value depends on the -Zoom={panic,abort} compiler option.
322+
static __rust_alloc_error_handler_should_panic: u8;
323+
}
324+
#[cfg(bootstrap)]
325+
let __rust_alloc_error_handler_should_panic = 0;
326+
327+
#[allow(unused_unsafe)]
328+
if unsafe { __rust_alloc_error_handler_should_panic != 0 } {
329+
panic!("memory allocation of {} bytes failed\n", layout.size());
330+
} else {
331+
rtprintpanic!("memory allocation of {} bytes failed\n", layout.size());
332+
}
319333
}
320334

321335
#[cfg(not(test))]

src/test/ui/oom_unwind.rs

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
// compile-flags: -Z oom=panic
2+
// run-pass
3+
// no-prefer-dynamic
4+
// needs-unwind
5+
// only-linux
6+
7+
#![feature(bench_black_box)]
8+
9+
use std::hint::black_box;
10+
use std::mem::forget;
11+
use std::panic::catch_unwind;
12+
13+
fn main() {
14+
let panic = catch_unwind(|| {
15+
// This is guaranteed to exceed even the size of the address space
16+
for _ in 0..16 {
17+
// Truncates to a suitable value for both 32-bit and 64-bit targets.
18+
let alloc_size = 0x1000_0000_1000_0000u64 as usize;
19+
forget(black_box(vec![0u8; alloc_size]));
20+
}
21+
});
22+
assert!(panic.is_err());
23+
}

src/tools/tidy/src/ui_tests.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use std::path::Path;
77

88
const ENTRY_LIMIT: usize = 1000;
99
// FIXME: The following limits should be reduced eventually.
10-
const ROOT_ENTRY_LIMIT: usize = 983;
10+
const ROOT_ENTRY_LIMIT: usize = 984;
1111
const ISSUES_ENTRY_LIMIT: usize = 2310;
1212

1313
fn check_entries(path: &Path, bad: &mut bool) {

0 commit comments

Comments
 (0)