Auto merge of #42727 - alexcrichton:allocators-new, r=eddyb

rustc: Implement the #[global_allocator] attribute

This PR is an implementation of [RFC 1974] which specifies a new method of
defining a global allocator for a program. This obsoletes the old
`#![allocator]` attribute and also removes support for it.

[RFC 1974]: https://github.com/rust-lang/rfcs/pull/1974

The new `#[global_allocator]` attribute solves many issues encountered with the
`#![allocator]` attribute such as composition and restrictions on the crate
graph itself. The compiler now has much more control over the ABI of the
allocator and how it's implemented, allowing much more freedom in terms of how
this feature is implemented.

cc #27389
This commit is contained in:
bors
2017-07-06 00:16:16 +00:00
115 changed files with 2860 additions and 1201 deletions

View File

@@ -8,7 +8,7 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use alloc::heap::{allocate, deallocate};
use alloc::heap::{Heap, Alloc, Layout};
use cmp;
use hash::{BuildHasher, Hash, Hasher};
@@ -781,10 +781,8 @@ impl<K, V> RawTable<K, V> {
.expect("capacity overflow"),
"capacity overflow");
let buffer = allocate(size, alignment);
if buffer.is_null() {
::alloc::oom()
}
let buffer = Heap.alloc(Layout::from_size_align(size, alignment).unwrap())
.unwrap_or_else(|e| Heap.oom(e));
let hashes = buffer.offset(hash_offset as isize) as *mut HashUint;
@@ -1193,7 +1191,8 @@ unsafe impl<#[may_dangle] K, #[may_dangle] V> Drop for RawTable<K, V> {
debug_assert!(!oflo, "should be impossible");
unsafe {
deallocate(self.hashes.ptr() as *mut u8, size, align);
Heap.dealloc(self.hashes.ptr() as *mut u8,
Layout::from_size_align(size, align).unwrap());
// Remember how everything was allocated out of one buffer
// during initialization? We only need one call to free here.
}

View File

@@ -224,7 +224,7 @@ impl Error for ! {
#[unstable(feature = "allocator_api",
reason = "the precise API and guarantees it provides may be tweaked.",
issue = "27700")]
issue = "32838")]
impl Error for allocator::AllocErr {
fn description(&self) -> &str {
allocator::AllocErr::description(self)
@@ -233,7 +233,7 @@ impl Error for allocator::AllocErr {
#[unstable(feature = "allocator_api",
reason = "the precise API and guarantees it provides may be tweaked.",
issue = "27700")]
issue = "32838")]
impl Error for allocator::CannotReallocInPlace {
fn description(&self) -> &str {
allocator::CannotReallocInPlace::description(self)

165
src/libstd/heap.rs Normal file
View File

@@ -0,0 +1,165 @@
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
//! dox
#![unstable(issue = "32838", feature = "allocator_api")]
pub use alloc::heap::{Heap, Alloc, Layout, Excess, CannotReallocInPlace, AllocErr};
#[cfg(not(stage0))]
pub use alloc_system::System;
#[cfg(all(not(stage0), not(test)))]
#[doc(hidden)]
pub mod __default_lib_allocator {
use super::{System, Layout, Alloc, AllocErr};
use ptr;
// for symbol names src/librustc/middle/allocator.rs
// for signatures src/librustc_allocator/lib.rs
// linkage directives are provided as part of the current compiler allocator
// ABI
#[no_mangle]
pub unsafe extern fn __rdl_alloc(size: usize,
align: usize,
err: *mut u8) -> *mut u8 {
let layout = Layout::from_size_align_unchecked(size, align);
match System.alloc(layout) {
Ok(p) => p,
Err(e) => {
ptr::write(err as *mut AllocErr, e);
0 as *mut u8
}
}
}
#[no_mangle]
pub unsafe extern fn __rdl_oom(err: *const u8) -> ! {
System.oom((*(err as *const AllocErr)).clone())
}
#[no_mangle]
pub unsafe extern fn __rdl_dealloc(ptr: *mut u8,
size: usize,
align: usize) {
System.dealloc(ptr, Layout::from_size_align_unchecked(size, align))
}
#[no_mangle]
pub unsafe extern fn __rdl_usable_size(layout: *const u8,
min: *mut usize,
max: *mut usize) {
let pair = System.usable_size(&*(layout as *const Layout));
*min = pair.0;
*max = pair.1;
}
#[no_mangle]
pub unsafe extern fn __rdl_realloc(ptr: *mut u8,
old_size: usize,
old_align: usize,
new_size: usize,
new_align: usize,
err: *mut u8) -> *mut u8 {
let old_layout = Layout::from_size_align_unchecked(old_size, old_align);
let new_layout = Layout::from_size_align_unchecked(new_size, new_align);
match System.realloc(ptr, old_layout, new_layout) {
Ok(p) => p,
Err(e) => {
ptr::write(err as *mut AllocErr, e);
0 as *mut u8
}
}
}
#[no_mangle]
pub unsafe extern fn __rdl_alloc_zeroed(size: usize,
align: usize,
err: *mut u8) -> *mut u8 {
let layout = Layout::from_size_align_unchecked(size, align);
match System.alloc_zeroed(layout) {
Ok(p) => p,
Err(e) => {
ptr::write(err as *mut AllocErr, e);
0 as *mut u8
}
}
}
#[no_mangle]
pub unsafe extern fn __rdl_alloc_excess(size: usize,
align: usize,
excess: *mut usize,
err: *mut u8) -> *mut u8 {
let layout = Layout::from_size_align_unchecked(size, align);
match System.alloc_excess(layout) {
Ok(p) => {
*excess = p.1;
p.0
}
Err(e) => {
ptr::write(err as *mut AllocErr, e);
0 as *mut u8
}
}
}
#[no_mangle]
pub unsafe extern fn __rdl_realloc_excess(ptr: *mut u8,
old_size: usize,
old_align: usize,
new_size: usize,
new_align: usize,
excess: *mut usize,
err: *mut u8) -> *mut u8 {
let old_layout = Layout::from_size_align_unchecked(old_size, old_align);
let new_layout = Layout::from_size_align_unchecked(new_size, new_align);
match System.realloc_excess(ptr, old_layout, new_layout) {
Ok(p) => {
*excess = p.1;
p.0
}
Err(e) => {
ptr::write(err as *mut AllocErr, e);
0 as *mut u8
}
}
}
#[no_mangle]
pub unsafe extern fn __rdl_grow_in_place(ptr: *mut u8,
old_size: usize,
old_align: usize,
new_size: usize,
new_align: usize) -> u8 {
let old_layout = Layout::from_size_align_unchecked(old_size, old_align);
let new_layout = Layout::from_size_align_unchecked(new_size, new_align);
match System.grow_in_place(ptr, old_layout, new_layout) {
Ok(()) => 1,
Err(_) => 0,
}
}
#[no_mangle]
pub unsafe extern fn __rdl_shrink_in_place(ptr: *mut u8,
old_size: usize,
old_align: usize,
new_size: usize,
new_align: usize) -> u8 {
let old_layout = Layout::from_size_align_unchecked(old_size, old_align);
let new_layout = Layout::from_size_align_unchecked(new_size, new_align);
match System.shrink_in_place(ptr, old_layout, new_layout) {
Ok(()) => 1,
Err(_) => 0,
}
}
}

View File

@@ -230,11 +230,6 @@
// Tell the compiler to link to either panic_abort or panic_unwind
#![needs_panic_runtime]
// Always use alloc_system during stage0 since we don't know if the alloc_*
// crate the stage0 compiler will pick by default is available (most
// obviously, if the user has disabled jemalloc in `./configure`).
#![cfg_attr(any(stage0, feature = "force_alloc_system"), feature(alloc_system))]
// Turn warnings into errors, but only after stage0, where it can be useful for
// code to emit warnings during language transitions
#![deny(warnings)]
@@ -246,6 +241,8 @@
// compiler details that will never be stable
#![feature(alloc)]
#![feature(allocator_api)]
#![feature(alloc_system)]
#![feature(allocator_internals)]
#![feature(allow_internal_unstable)]
#![feature(asm)]
#![feature(associated_consts)]
@@ -322,6 +319,8 @@
#![cfg_attr(test, feature(update_panic_count))]
#![cfg_attr(test, feature(float_bits_conv))]
#![cfg_attr(not(stage0), default_lib_allocator)]
// Explicitly import the prelude. The compiler uses this same unstable attribute
// to import the prelude implicitly when building crates that depend on std.
#[prelude_import]
@@ -342,15 +341,13 @@ extern crate core as __core;
#[macro_use]
#[macro_reexport(vec, format)]
extern crate alloc;
extern crate alloc_system;
extern crate std_unicode;
extern crate libc;
// We always need an unwinder currently for backtraces
extern crate unwind;
#[cfg(any(stage0, feature = "force_alloc_system"))]
extern crate alloc_system;
// compiler-rt intrinsics
extern crate compiler_builtins;
@@ -465,6 +462,7 @@ pub mod path;
pub mod process;
pub mod sync;
pub mod time;
pub mod heap;
// Platform-abstraction modules
#[macro_use]

View File

@@ -59,8 +59,6 @@ pub mod stdio;
#[cfg(not(test))]
pub fn init() {
use alloc::oom;
// By default, some platforms will send a *signal* when an EPIPE error
// would otherwise be delivered. This runtime doesn't install a SIGPIPE
// handler, causing it to kill the program, which isn't exactly what we
@@ -72,24 +70,6 @@ pub fn init() {
reset_sigpipe();
}
oom::set_oom_handler(oom_handler);
// A nicer handler for out-of-memory situations than the default one. This
// one prints a message to stderr before aborting. It is critical that this
// code does not allocate any memory since we are in an OOM situation. Any
// errors are ignored while printing since there's nothing we can do about
// them and we are about to exit anyways.
fn oom_handler() -> ! {
use intrinsics;
let msg = "fatal runtime error: out of memory\n";
unsafe {
libc::write(libc::STDERR_FILENO,
msg.as_ptr() as *const libc::c_void,
msg.len());
intrinsics::abort();
}
}
#[cfg(not(any(target_os = "nacl", target_os = "emscripten", target_os="fuchsia")))]
unsafe fn reset_sigpipe() {
assert!(signal(libc::SIGPIPE, libc::SIG_IGN) != libc::SIG_ERR);

View File

@@ -47,24 +47,6 @@ pub mod stdio;
#[cfg(not(test))]
pub fn init() {
::alloc::oom::set_oom_handler(oom_handler);
// See comment in sys/unix/mod.rs
fn oom_handler() -> ! {
use intrinsics;
use ptr;
let msg = "fatal runtime error: out of memory\n";
unsafe {
// WriteFile silently fails if it is passed an invalid handle, so
// there is no need to check the result of GetStdHandle.
c::WriteFile(c::GetStdHandle(c::STD_ERROR_HANDLE),
msg.as_ptr() as c::LPVOID,
msg.len() as c::DWORD,
ptr::null_mut(),
ptr::null_mut());
intrinsics::abort();
}
}
}
pub fn decode_error_kind(errno: i32) -> ErrorKind {