From 695dee063bcd40f154bb27b7beafcb3d4dd775ac Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Sat, 3 Jun 2017 14:54:08 -0700 Subject: [PATCH] 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/197 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 --- src/Cargo.lock | 18 + .../language-features/allocator-internals.md | 7 + .../src/language-features/allocator.md | 119 ----- .../src/language-features/global-allocator.md | 71 +++ src/liballoc/allocator.rs | 23 +- src/liballoc/arc.rs | 10 +- src/liballoc/boxed.rs | 27 +- src/liballoc/btree/node.rs | 26 +- src/liballoc/heap.rs | 353 ++++++------- src/liballoc/lib.rs | 6 +- src/liballoc/oom.rs | 61 --- src/liballoc/raw_vec.rs | 22 +- src/liballoc/rc.rs | 11 +- src/liballoc_jemalloc/Cargo.toml | 4 + src/liballoc_jemalloc/lib.rs | 201 ++++--- src/liballoc_system/Cargo.toml | 3 + src/liballoc_system/lib.rs | 479 ++++++++++++----- src/liballoc_system/old.rs | 268 ++++++++++ src/libcollections/lib.rs | 2 - src/librustc/lib.rs | 1 + src/librustc/middle/allocator.rs | 26 + src/librustc/middle/dead.rs | 5 + src/librustc/middle/dependency_format.rs | 29 +- src/librustc/session/mod.rs | 8 +- src/librustc_allocator/Cargo.toml | 15 + src/librustc_allocator/expand.rs | 498 ++++++++++++++++++ src/librustc_allocator/lib.rs | 101 ++++ src/librustc_asan/Cargo.toml | 1 + src/librustc_asan/lib.rs | 9 + .../target/aarch64_unknown_freebsd.rs | 2 +- .../target/aarch64_unknown_linux_gnu.rs | 2 +- src/librustc_back/target/bitrig_base.rs | 1 - src/librustc_back/target/fuchsia_base.rs | 1 - .../target/mips64_unknown_linux_gnuabi64.rs | 2 +- .../target/mips64el_unknown_linux_gnuabi64.rs | 2 +- .../target/mips_unknown_linux_gnu.rs | 2 +- .../target/mips_unknown_linux_musl.rs | 2 +- .../target/mips_unknown_linux_uclibc.rs | 2 +- .../target/mipsel_unknown_linux_gnu.rs | 2 +- .../target/mipsel_unknown_linux_musl.rs | 2 +- .../target/mipsel_unknown_linux_uclibc.rs | 2 +- src/librustc_back/target/mod.rs | 18 +- src/librustc_back/target/openbsd_base.rs | 1 - .../target/powerpc64_unknown_linux_gnu.rs | 2 +- .../target/powerpc64le_unknown_linux_gnu.rs | 2 +- .../target/powerpc_unknown_linux_gnu.rs | 2 +- src/librustc_back/target/redox_base.rs | 2 - .../target/s390x_unknown_linux_gnu.rs | 2 +- .../target/sparc64_unknown_linux_gnu.rs | 2 +- src/librustc_back/target/windows_msvc_base.rs | 1 - .../target/x86_64_rumprun_netbsd.rs | 2 +- src/librustc_driver/Cargo.toml | 1 + src/librustc_driver/driver.rs | 8 + src/librustc_driver/lib.rs | 1 + src/librustc_lint/builtin.rs | 3 +- src/librustc_llvm/ffi.rs | 1 + src/librustc_lsan/Cargo.toml | 1 + src/librustc_lsan/lib.rs | 9 + src/librustc_metadata/creader.rs | 192 +++++-- src/librustc_metadata/cstore.rs | 21 +- src/librustc_metadata/cstore_impl.rs | 1 - src/librustc_metadata/encoder.rs | 5 + src/librustc_metadata/schema.rs | 2 + src/librustc_msan/Cargo.toml | 1 + src/librustc_msan/lib.rs | 9 + src/librustc_trans/Cargo.toml | 1 + src/librustc_trans/allocator.rs | 117 ++++ src/librustc_trans/back/link.rs | 58 +- src/librustc_trans/back/symbol_export.rs | 1 - src/librustc_trans/back/write.rs | 20 + src/librustc_trans/base.rs | 40 ++ src/librustc_trans/lib.rs | 3 + src/libstd/collections/hash/table.rs | 11 +- src/libstd/error.rs | 4 +- src/libstd/heap.rs | 165 ++++++ src/libstd/lib.rs | 14 +- src/libstd/sys/unix/mod.rs | 20 - src/libstd/sys/windows/mod.rs | 18 - src/libsyntax/ext/build.rs | 6 + src/libsyntax/feature_gate.rs | 24 +- src/rustllvm/llvm-rebuild-trigger | 4 + src/test/codegen/function-arguments.rs | 2 +- .../allocator-depends-on-needs-allocators.rs | 21 - .../compile-fail/allocator-dylib-is-system.rs | 41 -- .../allocator-rust-dylib-is-jemalloc.rs | 41 -- .../auxiliary/system-allocator.rs} | 10 +- .../auxiliary/system-allocator2.rs} | 11 +- .../allocator/function-allocator.rs | 16 + .../allocator/not-an-allocator.rs | 26 + .../{ => allocator}/two-allocators.rs | 15 +- .../compile-fail/allocator/two-allocators2.rs | 25 + .../allocator/two-allocators3.rs} | 14 +- ...rs => feature-gate-allocator_internals.rs} | 7 +- ...r1.rs => feature-gate-global_allocator.rs} | 10 +- src/test/compile-fail/two-allocators-2.rs | 21 - src/test/compile-fail/two-allocators-3.rs | 23 - src/test/run-make/no-duplicate-libs/Makefile | 4 +- .../no-duplicate-libs/bar.c} | 8 +- src/test/run-make/no-duplicate-libs/bar.rs | 25 - .../no-duplicate-libs/foo.c} | 5 +- src/test/run-make/no-duplicate-libs/foo.rs | 25 - src/test/run-make/no-duplicate-libs/main.rs | 6 +- src/test/run-pass/allocator-alloc-one.rs | 13 +- src/test/run-pass/allocator-default.rs | 19 - src/test/run-pass/allocator-override.rs | 29 - .../allocator/auxiliary/custom-as-global.rs | 27 + .../run-pass/allocator/auxiliary/custom.rs | 31 ++ .../allocator/auxiliary/helper.rs} | 11 +- src/test/run-pass/allocator/custom.rs | 68 +++ src/test/run-pass/allocator/xcrate-use.rs | 44 ++ src/test/run-pass/allocator/xcrate-use2.rs | 57 ++ src/test/run-pass/lib-defaults.rs | 9 +- src/test/run-pass/realloc-16687.rs | 75 +-- src/test/run-pass/rfc1717/library-override.rs | 7 +- src/test/run-pass/smallest-hello-world.rs | 30 +- 115 files changed, 2828 insertions(+), 1169 deletions(-) create mode 100644 src/doc/unstable-book/src/language-features/allocator-internals.md delete mode 100644 src/doc/unstable-book/src/language-features/allocator.md create mode 100644 src/doc/unstable-book/src/language-features/global-allocator.md delete mode 100644 src/liballoc/oom.rs create mode 100644 src/liballoc_system/old.rs create mode 100644 src/librustc/middle/allocator.rs create mode 100644 src/librustc_allocator/Cargo.toml create mode 100644 src/librustc_allocator/expand.rs create mode 100644 src/librustc_allocator/lib.rs create mode 100644 src/librustc_trans/allocator.rs create mode 100644 src/libstd/heap.rs delete mode 100644 src/test/compile-fail/allocator-depends-on-needs-allocators.rs delete mode 100644 src/test/compile-fail/allocator-dylib-is-system.rs delete mode 100644 src/test/compile-fail/allocator-rust-dylib-is-jemalloc.rs rename src/test/compile-fail/{auxiliary/allocator3.rs => allocator/auxiliary/system-allocator.rs} (71%) rename src/test/compile-fail/{auxiliary/allocator2.rs => allocator/auxiliary/system-allocator2.rs} (71%) create mode 100644 src/test/compile-fail/allocator/function-allocator.rs create mode 100644 src/test/compile-fail/allocator/not-an-allocator.rs rename src/test/compile-fail/{ => allocator}/two-allocators.rs (59%) create mode 100644 src/test/compile-fail/allocator/two-allocators2.rs rename src/test/{run-pass/allocator-system.rs => compile-fail/allocator/two-allocators3.rs} (60%) rename src/test/compile-fail/{auxiliary/allocator-dylib.rs => feature-gate-allocator_internals.rs} (72%) rename src/test/compile-fail/{auxiliary/allocator1.rs => feature-gate-global_allocator.rs} (70%) delete mode 100644 src/test/compile-fail/two-allocators-2.rs delete mode 100644 src/test/compile-fail/two-allocators-3.rs rename src/test/{compile-fail/feature-gate-allocator.rs => run-make/no-duplicate-libs/bar.c} (76%) delete mode 100644 src/test/run-make/no-duplicate-libs/bar.rs rename src/test/{compile-fail/auxiliary/allocator-dylib2.rs => run-make/no-duplicate-libs/foo.c} (83%) delete mode 100644 src/test/run-make/no-duplicate-libs/foo.rs delete mode 100644 src/test/run-pass/allocator-default.rs delete mode 100644 src/test/run-pass/allocator-override.rs create mode 100644 src/test/run-pass/allocator/auxiliary/custom-as-global.rs create mode 100644 src/test/run-pass/allocator/auxiliary/custom.rs rename src/test/{compile-fail/auxiliary/needs_allocator.rs => run-pass/allocator/auxiliary/helper.rs} (77%) create mode 100644 src/test/run-pass/allocator/custom.rs create mode 100644 src/test/run-pass/allocator/xcrate-use.rs create mode 100644 src/test/run-pass/allocator/xcrate-use2.rs diff --git a/src/Cargo.lock b/src/Cargo.lock index 2d42903ad0a..cb3efea0d9f 100644 --- a/src/Cargo.lock +++ b/src/Cargo.lock @@ -43,6 +43,8 @@ dependencies = [ name = "alloc_jemalloc" version = "0.0.0" dependencies = [ + "alloc 0.0.0", + "alloc_system 0.0.0", "build_helper 0.1.0", "core 0.0.0", "gcc 0.3.51 (registry+https://github.com/rust-lang/crates.io-index)", @@ -53,6 +55,7 @@ dependencies = [ name = "alloc_system" version = "0.0.0" dependencies = [ + "alloc 0.0.0", "core 0.0.0", "libc 0.0.0", ] @@ -1127,10 +1130,21 @@ name = "rustc-serialize" version = "0.3.24" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "rustc_allocator" +version = "0.0.0" +dependencies = [ + "rustc 0.0.0", + "rustc_errors 0.0.0", + "syntax 0.0.0", + "syntax_pos 0.0.0", +] + [[package]] name = "rustc_asan" version = "0.0.0" dependencies = [ + "alloc 0.0.0", "alloc_system 0.0.0", "build_helper 0.1.0", "cmake 0.1.24 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1205,6 +1219,7 @@ dependencies = [ "log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", "proc_macro_plugin 0.0.0", "rustc 0.0.0", + "rustc_allocator 0.0.0", "rustc_back 0.0.0", "rustc_borrowck 0.0.0", "rustc_const_eval 0.0.0", @@ -1273,6 +1288,7 @@ dependencies = [ name = "rustc_lsan" version = "0.0.0" dependencies = [ + "alloc 0.0.0", "alloc_system 0.0.0", "build_helper 0.1.0", "cmake 0.1.24 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1317,6 +1333,7 @@ dependencies = [ name = "rustc_msan" version = "0.0.0" dependencies = [ + "alloc 0.0.0", "alloc_system 0.0.0", "build_helper 0.1.0", "cmake 0.1.24 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1399,6 +1416,7 @@ dependencies = [ "owning_ref 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", "rustc 0.0.0", "rustc-demangle 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc_allocator 0.0.0", "rustc_back 0.0.0", "rustc_bitflags 0.0.0", "rustc_const_math 0.0.0", diff --git a/src/doc/unstable-book/src/language-features/allocator-internals.md b/src/doc/unstable-book/src/language-features/allocator-internals.md new file mode 100644 index 00000000000..2023d758fe3 --- /dev/null +++ b/src/doc/unstable-book/src/language-features/allocator-internals.md @@ -0,0 +1,7 @@ +# `allocator_internals` + +This feature does not have a tracking issue, it is an unstable implementation +detail of the `global_allocator` feature not intended for use outside the +compiler. + +------------------------ diff --git a/src/doc/unstable-book/src/language-features/allocator.md b/src/doc/unstable-book/src/language-features/allocator.md deleted file mode 100644 index cfcf8e22d70..00000000000 --- a/src/doc/unstable-book/src/language-features/allocator.md +++ /dev/null @@ -1,119 +0,0 @@ -# `allocator` - -The tracking issue for this feature is: [#27389] - -[#27389]: https://github.com/rust-lang/rust/issues/27389 - ------------------------- - -Sometimes even the choices of jemalloc vs the system allocator aren't enough and -an entirely new custom allocator is required. In this you'll write your own -crate which implements the allocator API (e.g. the same as `alloc_system` or -`alloc_jemalloc`). As an example, let's take a look at a simplified and -annotated version of `alloc_system` - -```rust,no_run -# // Only needed for rustdoc --test down below. -# #![feature(lang_items)] -// The compiler needs to be instructed that this crate is an allocator in order -// to realize that when this is linked in another allocator like jemalloc should -// not be linked in. -#![feature(allocator)] -#![allocator] - -// Allocators are not allowed to depend on the standard library which in turn -// requires an allocator in order to avoid circular dependencies. This crate, -// however, can use all of libcore. -#![no_std] - -// Let's give a unique name to our custom allocator: -#![crate_name = "my_allocator"] -#![crate_type = "rlib"] - -// Our system allocator will use the in-tree libc crate for FFI bindings. Note -// that currently the external (crates.io) libc cannot be used because it links -// to the standard library (e.g. `#![no_std]` isn't stable yet), so that's why -// this specifically requires the in-tree version. -#![feature(libc)] -extern crate libc; - -// Listed below are the five allocation functions currently required by custom -// allocators. Their signatures and symbol names are not currently typechecked -// by the compiler, but this is a future extension and are required to match -// what is found below. -// -// Note that the standard `malloc` and `realloc` functions do not provide a way -// to communicate alignment so this implementation would need to be improved -// with respect to alignment in that aspect. - -#[no_mangle] -pub extern fn __rust_allocate(size: usize, _align: usize) -> *mut u8 { - unsafe { libc::malloc(size as libc::size_t) as *mut u8 } -} - -#[no_mangle] -pub extern fn __rust_allocate_zeroed(size: usize, _align: usize) -> *mut u8 { - unsafe { libc::calloc(size as libc::size_t, 1) as *mut u8 } -} - -#[no_mangle] -pub extern fn __rust_deallocate(ptr: *mut u8, _old_size: usize, _align: usize) { - unsafe { libc::free(ptr as *mut libc::c_void) } -} - -#[no_mangle] -pub extern fn __rust_reallocate(ptr: *mut u8, _old_size: usize, size: usize, - _align: usize) -> *mut u8 { - unsafe { - libc::realloc(ptr as *mut libc::c_void, size as libc::size_t) as *mut u8 - } -} - -#[no_mangle] -pub extern fn __rust_reallocate_inplace(_ptr: *mut u8, old_size: usize, - _size: usize, _align: usize) -> usize { - old_size // This api is not supported by libc. -} - -#[no_mangle] -pub extern fn __rust_usable_size(size: usize, _align: usize) -> usize { - size -} - -# // Only needed to get rustdoc to test this: -# fn main() {} -# #[lang = "panic_fmt"] fn panic_fmt() {} -# #[lang = "eh_personality"] fn eh_personality() {} -# #[lang = "eh_unwind_resume"] extern fn eh_unwind_resume() {} -# #[no_mangle] pub extern fn rust_eh_register_frames () {} -# #[no_mangle] pub extern fn rust_eh_unregister_frames () {} -``` - -After we compile this crate, it can be used as follows: - -```rust,ignore -extern crate my_allocator; - -fn main() { - let a = Box::new(8); // Allocates memory via our custom allocator crate. - println!("{}", a); -} -``` - -## Custom allocator limitations - -There are a few restrictions when working with custom allocators which may cause -compiler errors: - -* Any one artifact may only be linked to at most one allocator. Binaries, - dylibs, and staticlibs must link to exactly one allocator, and if none have - been explicitly chosen the compiler will choose one. On the other hand rlibs - do not need to link to an allocator (but still can). - -* A consumer of an allocator is tagged with `#![needs_allocator]` (e.g. the - `liballoc` crate currently) and an `#[allocator]` crate cannot transitively - depend on a crate which needs an allocator (e.g. circular dependencies are not - allowed). This basically means that allocators must restrict themselves to - libcore currently. - - diff --git a/src/doc/unstable-book/src/language-features/global-allocator.md b/src/doc/unstable-book/src/language-features/global-allocator.md new file mode 100644 index 00000000000..2eae40aef34 --- /dev/null +++ b/src/doc/unstable-book/src/language-features/global-allocator.md @@ -0,0 +1,71 @@ +# `global_allocator` + +The tracking issue for this feature is: [#27389] + +[#27389]: https://github.com/rust-lang/rust/issues/27389 + +------------------------ + +Rust programs may need to change the allocator that they're running with from +time to time. This use case is distinct from an allocator-per-collection (e.g. a +`Vec` with a custom allocator) and instead is more related to changing the +global default allocator, e.g. what `Vec` uses by default. + +Currently Rust programs don't have a specified global allocator. The compiler +may link to a version of [jemalloc] on some platforms, but this is not +guaranteed. Libraries, however, like cdylibs and staticlibs are guaranteed +to use the "system allocator" which means something like `malloc` on Unixes and +`HeapAlloc` on Windows. + +[jemalloc]: https://github.com/jemalloc/jemalloc + +The `#[global_allocator]` attribute, however, allows configuring this choice. +You can use this to implement a completely custom global allocator to route all +default allocation requests to a custom object. Defined in [RFC 1974] usage +looks like: + +[RFC 1974]: https://github.com/rust-lang/rfcs/pull/1974 + +```rust +#![feature(global_allocator, heap_api)] + +use std::heap::{Alloc, System, Layout, AllocErr}; + +struct MyAllocator; + +unsafe impl<'a> Alloc for &'a MyAllocator { + unsafe fn alloc(&mut self, layout: Layout) -> Result<*mut u8, AllocErr> { + System.alloc(layout) + } + + unsafe fn dealloc(&mut self, ptr: *mut u8, layout: Layout) { + System.dealloc(ptr, layout) + } +} + +#[global_allocator] +static GLOBAL: MyAllocator = MyAllocator; + +fn main() { + // This `Vec` will allocate memory through `GLOBAL` above + let mut v = Vec::new(); + v.push(1); +} +``` + +And that's it! The `#[global_allocator]` attribute is applied to a `static` +which implements the `Alloc` trait in the `std::heap` module. Note, though, +that the implementation is defined for `&MyAllocator`, not just `MyAllocator`. +You may wish, however, to also provide `Alloc for MyAllocator` for other use +cases. + +A crate can only have one instance of `#[global_allocator]` and this instance +may be loaded through a dependency. For example `#[global_allocator]` above +could have been placed in one of the dependencies loaded through `extern crate`. + +Note that `Alloc` itself is an `unsafe` trait, with much documentation on the +trait itself about usage and for implementors. Extra care should be taken when +implementing a global allocator as well as the allocator may be called from many +portions of the standard library, such as the panicking routine. As a result it +is highly recommended to not panic during allocation and work in as many +situations with as few dependencies as possible as well. diff --git a/src/liballoc/allocator.rs b/src/liballoc/allocator.rs index bf38629ed38..ca5388b4701 100644 --- a/src/liballoc/allocator.rs +++ b/src/liballoc/allocator.rs @@ -13,7 +13,7 @@ slightly, especially to possibly take into account the \ types being stored to make room for a future \ tracing garbage collector", - issue = "27700")] + issue = "32838")] use core::cmp; use core::fmt; @@ -73,6 +73,7 @@ impl Layout { /// * `size`, when rounded up to the nearest multiple of `align`, /// must not overflow (i.e. the rounded value must be less than /// `usize::MAX`). + #[inline] pub fn from_size_align(size: usize, align: usize) -> Option { if !align.is_power_of_two() { return None; @@ -96,13 +97,28 @@ pub fn from_size_align(size: usize, align: usize) -> Option { return None; } - Some(Layout { size: size, align: align }) + unsafe { + Some(Layout::from_size_align_unchecked(size, align)) + } + } + + /// Creates a layout, bypassing all checks. + /// + /// # Unsafety + /// + /// This function is unsafe as it does not verify that `align` is a power of + /// two nor that `size` aligned to `align` fits within the address space. + #[inline] + pub unsafe fn from_size_align_unchecked(size: usize, align: usize) -> Layout { + Layout { size: size, align: align } } /// The minimum size in bytes for a memory block of this layout. + #[inline] pub fn size(&self) -> usize { self.size } /// The minimum byte alignment for a memory block of this layout. + #[inline] pub fn align(&self) -> usize { self.align } /// Constructs a `Layout` suitable for holding a value of type `T`. @@ -135,6 +151,7 @@ pub fn for_value(t: &T) -> Self { /// /// Panics if the combination of `self.size` and the given `align` /// violates the conditions listed in `from_size_align`. + #[inline] pub fn align_to(&self, align: usize) -> Self { Layout::from_size_align(self.size, cmp::max(self.align, align)).unwrap() } @@ -155,6 +172,7 @@ pub fn align_to(&self, align: usize) -> Self { /// to be less than or equal to the alignment of the starting /// address for the whole allocated block of memory. One way to /// satisfy this constraint is to ensure `align <= self.align`. + #[inline] pub fn padding_needed_for(&self, align: usize) -> usize { let len = self.size(); @@ -556,6 +574,7 @@ fn oom(&mut self, _: AllocErr) -> ! { /// However, for clients that do not wish to track the capacity /// returned by `alloc_excess` locally, this method is likely to /// produce useful results. + #[inline] fn usable_size(&self, layout: &Layout) -> (usize, usize) { (layout.size(), layout.size()) } diff --git a/src/liballoc/arc.rs b/src/liballoc/arc.rs index 7c51c4b161c..d9edf50b9c8 100644 --- a/src/liballoc/arc.rs +++ b/src/liballoc/arc.rs @@ -23,7 +23,6 @@ use core::borrow; use core::fmt; use core::cmp::Ordering; -use core::mem::{align_of_val, size_of_val}; use core::intrinsics::abort; use core::mem; use core::mem::uninitialized; @@ -34,7 +33,8 @@ use core::hash::{Hash, Hasher}; use core::{isize, usize}; use core::convert::From; -use heap::deallocate; + +use heap::{Heap, Alloc, Layout}; /// A soft limit on the amount of references that may be made to an `Arc`. /// @@ -503,7 +503,7 @@ unsafe fn drop_slow(&mut self) { if self.inner().weak.fetch_sub(1, Release) == 1 { atomic::fence(Acquire); - deallocate(ptr as *mut u8, size_of_val(&*ptr), align_of_val(&*ptr)) + Heap.dealloc(ptr as *mut u8, Layout::for_value(&*ptr)) } } @@ -1007,7 +1007,9 @@ fn drop(&mut self) { // ref, which can only happen after the lock is released. if self.inner().weak.fetch_sub(1, Release) == 1 { atomic::fence(Acquire); - unsafe { deallocate(ptr as *mut u8, size_of_val(&*ptr), align_of_val(&*ptr)) } + unsafe { + Heap.dealloc(ptr as *mut u8, Layout::for_value(&*ptr)) + } } } } diff --git a/src/liballoc/boxed.rs b/src/liballoc/boxed.rs index 4a43018e973..76cf10f0d55 100644 --- a/src/liballoc/boxed.rs +++ b/src/liballoc/boxed.rs @@ -55,7 +55,7 @@ #![stable(feature = "rust1", since = "1.0.0")] -use heap; +use heap::{Heap, Layout, Alloc}; use raw_vec::RawVec; use core::any::Any; @@ -135,8 +135,7 @@ pub struct ExchangeHeapSingleton { #[allow(missing_debug_implementations)] pub struct IntermediateBox { ptr: *mut u8, - size: usize, - align: usize, + layout: Layout, marker: marker::PhantomData<*mut T>, } @@ -156,23 +155,21 @@ unsafe fn finalize(b: IntermediateBox) -> Box { } fn make_place() -> IntermediateBox { - let size = mem::size_of::(); - let align = mem::align_of::(); + let layout = Layout::new::(); - let p = if size == 0 { + let p = if layout.size() == 0 { mem::align_of::() as *mut u8 } else { - let p = unsafe { heap::allocate(size, align) }; - if p.is_null() { - panic!("Box make_place allocation failure."); + unsafe { + Heap.alloc(layout.clone()).unwrap_or_else(|err| { + Heap.oom(err) + }) } - p }; IntermediateBox { ptr: p, - size: size, - align: align, + layout: layout, marker: marker::PhantomData, } } @@ -221,8 +218,10 @@ fn make_place(self) -> IntermediateBox { issue = "27779")] impl Drop for IntermediateBox { fn drop(&mut self) { - if self.size > 0 { - unsafe { heap::deallocate(self.ptr, self.size, self.align) } + if self.layout.size() > 0 { + unsafe { + Heap.dealloc(self.ptr, self.layout.clone()) + } } } } diff --git a/src/liballoc/btree/node.rs b/src/liballoc/btree/node.rs index 811174b331e..0eaff6f2192 100644 --- a/src/liballoc/btree/node.rs +++ b/src/liballoc/btree/node.rs @@ -48,7 +48,7 @@ use core::slice; use boxed::Box; -use heap; +use heap::{Heap, Alloc, Layout}; const B: usize = 6; pub const MIN_LEN: usize = B - 1; @@ -254,11 +254,7 @@ pub fn pop_level(&mut self) { self.as_mut().as_leaf_mut().parent = ptr::null(); unsafe { - heap::deallocate( - top, - mem::size_of::>(), - mem::align_of::>() - ); + Heap.dealloc(top, Layout::new::>()); } } } @@ -445,7 +441,7 @@ pub unsafe fn deallocate_and_ascend(self) -> Option< > { let ptr = self.as_leaf() as *const LeafNode as *const u8 as *mut u8; let ret = self.ascend().ok(); - heap::deallocate(ptr, mem::size_of::>(), mem::align_of::>()); + Heap.dealloc(ptr, Layout::new::>()); ret } } @@ -466,11 +462,7 @@ pub unsafe fn deallocate_and_ascend(self) -> Option< > { let ptr = self.as_internal() as *const InternalNode as *const u8 as *mut u8; let ret = self.ascend().ok(); - heap::deallocate( - ptr, - mem::size_of::>(), - mem::align_of::>() - ); + Heap.dealloc(ptr, Layout::new::>()); ret } } @@ -1252,16 +1244,14 @@ pub fn merge(mut self) ).correct_parent_link(); } - heap::deallocate( + Heap.dealloc( right_node.node.get() as *mut u8, - mem::size_of::>(), - mem::align_of::>() + Layout::new::>(), ); } else { - heap::deallocate( + Heap.dealloc( right_node.node.get() as *mut u8, - mem::size_of::>(), - mem::align_of::>() + Layout::new::>(), ); } diff --git a/src/liballoc/heap.rs b/src/liballoc/heap.rs index d46c6a83ff3..1d959ac5bf6 100644 --- a/src/liballoc/heap.rs +++ b/src/liballoc/heap.rs @@ -8,207 +8,212 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -#![unstable(feature = "heap_api", +#![unstable(feature = "allocator_api", reason = "the precise API and guarantees it provides may be tweaked \ slightly, especially to possibly take into account the \ types being stored to make room for a future \ tracing garbage collector", - issue = "27700")] + issue = "32838")] -use allocator::{Alloc, AllocErr, CannotReallocInPlace, Layout}; -use core::{isize, usize, cmp, ptr}; use core::intrinsics::{min_align_of_val, size_of_val}; +use core::mem::{self, ManuallyDrop}; +use core::usize; -#[allow(improper_ctypes)] -extern "C" { - #[allocator] - fn __rust_allocate(size: usize, align: usize) -> *mut u8; - fn __rust_allocate_zeroed(size: usize, align: usize) -> *mut u8; - fn __rust_deallocate(ptr: *mut u8, old_size: usize, align: usize); - fn __rust_reallocate(ptr: *mut u8, old_size: usize, size: usize, align: usize) -> *mut u8; - fn __rust_reallocate_inplace(ptr: *mut u8, - old_size: usize, - size: usize, - align: usize) - -> usize; - fn __rust_usable_size(size: usize, align: usize) -> usize; +pub use allocator::*; +#[doc(hidden)] +pub mod __core { + pub use core::*; } -#[inline(always)] -fn check_size_and_alignment(size: usize, align: usize) { - debug_assert!(size != 0); - debug_assert!(size <= isize::MAX as usize, - "Tried to allocate too much: {} bytes", - size); - debug_assert!(usize::is_power_of_two(align), - "Invalid alignment of allocation: {}", - align); +extern "Rust" { + #[allocator] + fn __rust_alloc(size: usize, align: usize, err: *mut u8) -> *mut u8; + fn __rust_oom(err: *const u8) -> !; + fn __rust_dealloc(ptr: *mut u8, size: usize, align: usize); + fn __rust_usable_size(layout: *const u8, + min: *mut usize, + max: *mut usize); + fn __rust_realloc(ptr: *mut u8, + old_size: usize, + old_align: usize, + new_size: usize, + new_align: usize, + err: *mut u8) -> *mut u8; + fn __rust_alloc_zeroed(size: usize, align: usize, err: *mut u8) -> *mut u8; + fn __rust_alloc_excess(size: usize, + align: usize, + excess: *mut usize, + err: *mut u8) -> *mut u8; + fn __rust_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; + fn __rust_grow_in_place(ptr: *mut u8, + old_size: usize, + old_align: usize, + new_size: usize, + new_align: usize) -> u8; + fn __rust_shrink_in_place(ptr: *mut u8, + old_size: usize, + old_align: usize, + new_size: usize, + new_align: usize) -> u8; } #[derive(Copy, Clone, Default, Debug)] -pub struct HeapAlloc; +pub struct Heap; -unsafe impl Alloc for HeapAlloc { +unsafe impl Alloc for Heap { + #[inline] unsafe fn alloc(&mut self, layout: Layout) -> Result<*mut u8, AllocErr> { - let addr = allocate(layout.size(), layout.align()); - if addr.is_null() { - Err(AllocErr::Exhausted { request: layout }) + let mut err = ManuallyDrop::new(mem::uninitialized::()); + let ptr = __rust_alloc(layout.size(), + layout.align(), + &mut *err as *mut AllocErr as *mut u8); + if ptr.is_null() { + Err(ManuallyDrop::into_inner(err)) } else { - Ok(addr) + Ok(ptr) } } - unsafe fn alloc_zeroed(&mut self, layout: Layout) -> Result<*mut u8, AllocErr> { - let addr = allocate_zeroed(layout.size(), layout.align()); - if addr.is_null() { - Err(AllocErr::Exhausted { request: layout }) - } else { - Ok(addr) + #[inline] + fn oom(&mut self, err: AllocErr) -> ! { + unsafe { + __rust_oom(&err as *const AllocErr as *const u8) } } + #[inline] unsafe fn dealloc(&mut self, ptr: *mut u8, layout: Layout) { - deallocate(ptr, layout.size(), layout.align()); + __rust_dealloc(ptr, layout.size(), layout.align()) } + #[inline] fn usable_size(&self, layout: &Layout) -> (usize, usize) { - (layout.size(), usable_size(layout.size(), layout.align())) + let mut min = 0; + let mut max = 0; + unsafe { + __rust_usable_size(layout as *const Layout as *const u8, + &mut min, + &mut max); + } + (min, max) } + #[inline] unsafe fn realloc(&mut self, ptr: *mut u8, layout: Layout, new_layout: Layout) -> Result<*mut u8, AllocErr> { - let old_size = layout.size(); - let new_size = new_layout.size(); - if layout.align() == new_layout.align() { - let new_ptr = reallocate(ptr, old_size, new_size, layout.align()); - if new_ptr.is_null() { - // We assume `reallocate` already tried alloc + copy + - // dealloc fallback; thus pointless to repeat effort - Err(AllocErr::Exhausted { request: new_layout }) - } else { - Ok(new_ptr) - } + let mut err = ManuallyDrop::new(mem::uninitialized::()); + let ptr = __rust_realloc(ptr, + layout.size(), + layout.align(), + new_layout.size(), + new_layout.align(), + &mut *err as *mut AllocErr as *mut u8); + if ptr.is_null() { + Err(ManuallyDrop::into_inner(err)) } else { - // if alignments don't match, fall back on alloc + copy + dealloc - let result = self.alloc(new_layout); - if let Ok(new_ptr) = result { - ptr::copy_nonoverlapping(ptr as *const u8, new_ptr, cmp::min(old_size, new_size)); - self.dealloc(ptr, layout); - } - result + mem::forget(err); + Ok(ptr) } } + #[inline] + unsafe fn alloc_zeroed(&mut self, layout: Layout) -> Result<*mut u8, AllocErr> { + let mut err = ManuallyDrop::new(mem::uninitialized::()); + let ptr = __rust_alloc_zeroed(layout.size(), + layout.align(), + &mut *err as *mut AllocErr as *mut u8); + if ptr.is_null() { + Err(ManuallyDrop::into_inner(err)) + } else { + Ok(ptr) + } + } + + #[inline] + unsafe fn alloc_excess(&mut self, layout: Layout) -> Result { + let mut err = ManuallyDrop::new(mem::uninitialized::()); + let mut size = 0; + let ptr = __rust_alloc_excess(layout.size(), + layout.align(), + &mut size, + &mut *err as *mut AllocErr as *mut u8); + if ptr.is_null() { + Err(ManuallyDrop::into_inner(err)) + } else { + Ok(Excess(ptr, size)) + } + } + + #[inline] + unsafe fn realloc_excess(&mut self, + ptr: *mut u8, + layout: Layout, + new_layout: Layout) -> Result { + let mut err = ManuallyDrop::new(mem::uninitialized::()); + let mut size = 0; + let ptr = __rust_realloc_excess(ptr, + layout.size(), + layout.align(), + new_layout.size(), + new_layout.align(), + &mut size, + &mut *err as *mut AllocErr as *mut u8); + if ptr.is_null() { + Err(ManuallyDrop::into_inner(err)) + } else { + Ok(Excess(ptr, size)) + } + } + + #[inline] unsafe fn grow_in_place(&mut self, ptr: *mut u8, layout: Layout, new_layout: Layout) -> Result<(), CannotReallocInPlace> { - // grow_in_place spec requires this, and the spec for reallocate_inplace - // makes it hard to detect failure if it does not hold. debug_assert!(new_layout.size() >= layout.size()); - - if layout.align() != new_layout.align() { // reallocate_inplace requires this. - return Err(CannotReallocInPlace); + debug_assert!(new_layout.align() == layout.align()); + let ret = __rust_grow_in_place(ptr, + layout.size(), + layout.align(), + new_layout.size(), + new_layout.align()); + if ret != 0 { + Ok(()) + } else { + Err(CannotReallocInPlace) } - let usable = reallocate_inplace(ptr, layout.size(), new_layout.size(), layout.align()); - if usable >= new_layout.size() { Ok(()) } else { Err(CannotReallocInPlace) } } -} - -// FIXME: #13996: mark the `allocate` and `reallocate` return value as `noalias` -/// Return a pointer to `size` bytes of memory aligned to `align`. -/// -/// On failure, return a null pointer. -/// -/// Behavior is undefined if the requested size is 0 or the alignment is not a -/// power of 2. The alignment must be no larger than the largest supported page -/// size on the platform. -#[inline] -pub unsafe fn allocate(size: usize, align: usize) -> *mut u8 { - check_size_and_alignment(size, align); - __rust_allocate(size, align) -} - -/// Return a pointer to `size` bytes of memory aligned to `align` and -/// initialized to zeroes. -/// -/// On failure, return a null pointer. -/// -/// Behavior is undefined if the requested size is 0 or the alignment is not a -/// power of 2. The alignment must be no larger than the largest supported page -/// size on the platform. -#[inline] -pub unsafe fn allocate_zeroed(size: usize, align: usize) -> *mut u8 { - check_size_and_alignment(size, align); - __rust_allocate_zeroed(size, align) -} - -/// Resize the allocation referenced by `ptr` to `size` bytes. -/// -/// On failure, return a null pointer and leave the original allocation intact. -/// -/// If the allocation was relocated, the memory at the passed-in pointer is -/// undefined after the call. -/// -/// Behavior is undefined if the requested size is 0 or the alignment is not a -/// power of 2. The alignment must be no larger than the largest supported page -/// size on the platform. -/// -/// The `old_size` and `align` parameters are the parameters that were used to -/// create the allocation referenced by `ptr`. The `old_size` parameter may be -/// any value in range_inclusive(requested_size, usable_size). -#[inline] -pub unsafe fn reallocate(ptr: *mut u8, old_size: usize, size: usize, align: usize) -> *mut u8 { - check_size_and_alignment(size, align); - __rust_reallocate(ptr, old_size, size, align) -} - -/// Resize the allocation referenced by `ptr` to `size` bytes. -/// -/// If the operation succeeds, it returns `usable_size(size, align)` and if it -/// fails (or is a no-op) it returns `usable_size(old_size, align)`. -/// -/// Behavior is undefined if the requested size is 0 or the alignment is not a -/// power of 2. The alignment must be no larger than the largest supported page -/// size on the platform. -/// -/// The `old_size` and `align` parameters are the parameters that were used to -/// create the allocation referenced by `ptr`. The `old_size` parameter may be -/// any value in range_inclusive(requested_size, usable_size). -#[inline] -pub unsafe fn reallocate_inplace(ptr: *mut u8, - old_size: usize, - size: usize, - align: usize) - -> usize { - check_size_and_alignment(size, align); - __rust_reallocate_inplace(ptr, old_size, size, align) -} - -/// Deallocates the memory referenced by `ptr`. -/// -/// The `ptr` parameter must not be null. -/// -/// The `old_size` and `align` parameters are the parameters that were used to -/// create the allocation referenced by `ptr`. The `old_size` parameter may be -/// any value in range_inclusive(requested_size, usable_size). -#[inline] -pub unsafe fn deallocate(ptr: *mut u8, old_size: usize, align: usize) { - __rust_deallocate(ptr, old_size, align) -} - -/// Returns the usable size of an allocation created with the specified the -/// `size` and `align`. -#[inline] -pub fn usable_size(size: usize, align: usize) -> usize { - unsafe { __rust_usable_size(size, align) } + #[inline] + unsafe fn shrink_in_place(&mut self, + ptr: *mut u8, + layout: Layout, + new_layout: Layout) -> Result<(), CannotReallocInPlace> { + debug_assert!(new_layout.size() <= layout.size()); + debug_assert!(new_layout.align() == layout.align()); + let ret = __rust_shrink_in_place(ptr, + layout.size(), + layout.align(), + new_layout.size(), + new_layout.align()); + if ret != 0 { + Ok(()) + } else { + Err(CannotReallocInPlace) + } + } } /// An arbitrary non-null address to represent zero-size allocations. @@ -228,11 +233,10 @@ unsafe fn exchange_malloc(size: usize, align: usize) -> *mut u8 { if size == 0 { align as *mut u8 } else { - let ptr = allocate(size, align); - if ptr.is_null() { - ::oom() - } - ptr + let layout = Layout::from_size_align_unchecked(size, align); + Heap.alloc(layout).unwrap_or_else(|err| { + Heap.oom(err) + }) } } @@ -243,7 +247,8 @@ pub(crate) unsafe fn box_free(ptr: *mut T) { let align = min_align_of_val(&*ptr); // We do not allocate for Box when T is ZST, so deallocation is also not necessary. if size != 0 { - deallocate(ptr as *mut u8, size, align); + let layout = Layout::from_size_align_unchecked(size, align); + Heap.dealloc(ptr as *mut u8, layout); } } @@ -252,38 +257,22 @@ mod tests { extern crate test; use self::test::Bencher; use boxed::Box; - use heap; + use heap::{Heap, Alloc, Layout}; #[test] fn allocate_zeroed() { unsafe { - let size = 1024; - let ptr = heap::allocate_zeroed(size, 1); - if ptr.is_null() { - ::oom() - } + let layout = Layout::from_size_align(1024, 1).unwrap(); + let ptr = Heap.alloc_zeroed(layout.clone()) + .unwrap_or_else(|e| Heap.oom(e)); - let end = ptr.offset(size as isize); + let end = ptr.offset(layout.size() as isize); let mut i = ptr; while i < end { assert_eq!(*i, 0); i = i.offset(1); } - heap::deallocate(ptr, size, 1); - } - } - - #[test] - fn basic_reallocate_inplace_noop() { - unsafe { - let size = 4000; - let ptr = heap::allocate(size, 8); - if ptr.is_null() { - ::oom() - } - let ret = heap::reallocate_inplace(ptr, size, size, 8); - heap::deallocate(ptr, size, 8); - assert_eq!(ret, heap::usable_size(size, 8)); + Heap.dealloc(ptr, layout); } } diff --git a/src/liballoc/lib.rs b/src/liballoc/lib.rs index 23da2913136..b419aeb5ab5 100644 --- a/src/liballoc/lib.rs +++ b/src/liballoc/lib.rs @@ -85,7 +85,7 @@ #![cfg_attr(not(test), feature(slice_rotate))] #![cfg_attr(not(test), feature(str_checked_slicing))] #![cfg_attr(test, feature(rand, test))] -#![feature(allocator)] +#![cfg_attr(stage0, feature(allocator))] #![feature(allow_internal_unstable)] #![feature(box_patterns)] #![feature(box_syntax)] @@ -124,6 +124,7 @@ #![feature(unicode)] #![feature(unique)] #![feature(unsize)] +#![cfg_attr(not(stage0), feature(allocator_internals))] #![cfg_attr(not(test), feature(fused, fn_traits, placement_new_protocol))] #![cfg_attr(test, feature(test, box_heap))] @@ -168,7 +169,6 @@ mod boxed { pub mod arc; pub mod rc; pub mod raw_vec; -pub mod oom; // collections modules pub mod binary_heap; @@ -260,8 +260,6 @@ trait SpecExtend { fn spec_extend(&mut self, iter: I); } -pub use oom::oom; - #[doc(no_inline)] pub use binary_heap::BinaryHeap; #[doc(no_inline)] diff --git a/src/liballoc/oom.rs b/src/liballoc/oom.rs deleted file mode 100644 index 3640156fec2..00000000000 --- a/src/liballoc/oom.rs +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright 2014-2015 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 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -#[cfg(target_has_atomic = "ptr")] -pub use self::imp::set_oom_handler; -use core::intrinsics; - -fn default_oom_handler() -> ! { - // The default handler can't do much more since we can't assume the presence - // of libc or any way of printing an error message. - unsafe { intrinsics::abort() } -} - -/// Common out-of-memory routine -#[cold] -#[inline(never)] -#[unstable(feature = "oom", reason = "not a scrutinized interface", - issue = "27700")] -pub fn oom() -> ! { - self::imp::oom() -} - -#[cfg(target_has_atomic = "ptr")] -mod imp { - use core::mem; - use core::sync::atomic::{AtomicPtr, Ordering}; - - static OOM_HANDLER: AtomicPtr<()> = AtomicPtr::new(super::default_oom_handler as *mut ()); - - #[inline(always)] - pub fn oom() -> ! { - let value = OOM_HANDLER.load(Ordering::SeqCst); - let handler: fn() -> ! = unsafe { mem::transmute(value) }; - handler(); - } - - /// Set a custom handler for out-of-memory conditions - /// - /// To avoid recursive OOM failures, it is critical that the OOM handler does - /// not allocate any memory itself. - #[unstable(feature = "oom", reason = "not a scrutinized interface", - issue = "27700")] - pub fn set_oom_handler(handler: fn() -> !) { - OOM_HANDLER.store(handler as *mut (), Ordering::SeqCst); - } -} - -#[cfg(not(target_has_atomic = "ptr"))] -mod imp { - #[inline(always)] - pub fn oom() -> ! { - super::default_oom_handler() - } -} diff --git a/src/liballoc/raw_vec.rs b/src/liballoc/raw_vec.rs index c56a93c0460..d1aab4c70be 100644 --- a/src/liballoc/raw_vec.rs +++ b/src/liballoc/raw_vec.rs @@ -12,7 +12,7 @@ use core::ptr::{self, Unique}; use core::mem; use core::slice; -use heap::{HeapAlloc}; +use heap::Heap; use super::boxed::Box; use core::ops::Drop; use core::cmp; @@ -45,7 +45,7 @@ /// field. This allows zero-sized types to not be special-cased by consumers of /// this type. #[allow(missing_debug_implementations)] -pub struct RawVec { +pub struct RawVec { ptr: Unique, cap: usize, a: A, @@ -112,14 +112,14 @@ fn allocate_in(cap: usize, zeroed: bool, mut a: A) -> Self { } } -impl RawVec { +impl RawVec { /// Creates the biggest possible RawVec (on the system heap) /// without allocating. If T has positive size, then this makes a /// RawVec with capacity 0. If T has 0 size, then it it makes a /// RawVec with capacity `usize::MAX`. Useful for implementing /// delayed allocation. pub fn new() -> Self { - Self::new_in(HeapAlloc) + Self::new_in(Heap) } /// Creates a RawVec (on the system heap) with exactly the @@ -139,13 +139,13 @@ pub fn new() -> Self { /// Aborts on OOM #[inline] pub fn with_capacity(cap: usize) -> Self { - RawVec::allocate_in(cap, false, HeapAlloc) + RawVec::allocate_in(cap, false, Heap) } /// Like `with_capacity` but guarantees the buffer is zeroed. #[inline] pub fn with_capacity_zeroed(cap: usize) -> Self { - RawVec::allocate_in(cap, true, HeapAlloc) + RawVec::allocate_in(cap, true, Heap) } } @@ -166,7 +166,7 @@ pub unsafe fn from_raw_parts_in(ptr: *mut T, cap: usize, a: A) -> Self { } } -impl RawVec { +impl RawVec { /// Reconstitutes a RawVec from a pointer, capacity. /// /// # Undefined Behavior @@ -178,7 +178,7 @@ pub unsafe fn from_raw_parts(ptr: *mut T, cap: usize) -> Self { RawVec { ptr: Unique::new(ptr), cap: cap, - a: HeapAlloc, + a: Heap, } } @@ -609,7 +609,7 @@ pub fn shrink_to_fit(&mut self, amount: usize) { } } -impl RawVec { +impl RawVec { /// Converts the entire buffer into `Box<[T]>`. /// /// While it is not *strictly* Undefined Behavior to call @@ -693,13 +693,13 @@ unsafe fn alloc(&mut self, layout: Layout) -> Result<*mut u8, AllocErr> { if size > self.fuel { return Err(AllocErr::Unsupported { details: "fuel exhausted" }); } - match HeapAlloc.alloc(layout) { + match Heap.alloc(layout) { ok @ Ok(_) => { self.fuel -= size; ok } err @ Err(_) => err, } } unsafe fn dealloc(&mut self, ptr: *mut u8, layout: Layout) { - HeapAlloc.dealloc(ptr, layout) + Heap.dealloc(ptr, layout) } } diff --git a/src/liballoc/rc.rs b/src/liballoc/rc.rs index 94fe36d01a5..306136b21c8 100644 --- a/src/liballoc/rc.rs +++ b/src/liballoc/rc.rs @@ -252,13 +252,13 @@ use core::intrinsics::abort; use core::marker; use core::marker::Unsize; -use core::mem::{self, align_of_val, forget, size_of, size_of_val, uninitialized}; +use core::mem::{self, forget, size_of, size_of_val, uninitialized}; use core::ops::Deref; use core::ops::CoerceUnsized; use core::ptr::{self, Shared}; use core::convert::From; -use heap::{allocate, deallocate, box_free}; +use heap::{Heap, Alloc, Layout, box_free}; use raw_vec::RawVec; struct RcBox { @@ -461,7 +461,8 @@ pub fn __from_array(value: Box<[T]>) -> Rc<[T]> { // FIXME(custom-DST): creating this invalid &[T] is dubiously defined, // we should have a better way of getting the size/align // of a DST from its unsized part. - let ptr = allocate(size_of_val(&*ptr), align_of_val(&*ptr)); + let ptr = Heap.alloc(Layout::for_value(&*ptr)) + .unwrap_or_else(|e| Heap.oom(e)); let ptr: *mut RcBox<[T]> = mem::transmute([ptr as usize, value.len()]); // Initialize the new RcBox. @@ -719,7 +720,7 @@ fn drop(&mut self) { self.dec_weak(); if self.weak() == 0 { - deallocate(ptr as *mut u8, size_of_val(&*ptr), align_of_val(&*ptr)) + Heap.dealloc(ptr as *mut u8, Layout::for_value(&*ptr)); } } } @@ -1097,7 +1098,7 @@ fn drop(&mut self) { // the weak count starts at 1, and will only go to zero if all // the strong pointers have disappeared. if self.weak() == 0 { - deallocate(ptr as *mut u8, size_of_val(&*ptr), align_of_val(&*ptr)) + Heap.dealloc(ptr as *mut u8, Layout::for_value(&*ptr)); } } } diff --git a/src/liballoc_jemalloc/Cargo.toml b/src/liballoc_jemalloc/Cargo.toml index 49e5baad74d..99c0bf2aaab 100644 --- a/src/liballoc_jemalloc/Cargo.toml +++ b/src/liballoc_jemalloc/Cargo.toml @@ -15,6 +15,10 @@ doc = false core = { path = "../libcore" } libc = { path = "../rustc/libc_shim" } +[target.'cfg(not(stage0))'.dependencies] +alloc = { path = "../liballoc" } +alloc_system = { path = "../liballoc_system" } + [build-dependencies] build_helper = { path = "../build_helper" } gcc = "0.3.50" diff --git a/src/liballoc_jemalloc/lib.rs b/src/liballoc_jemalloc/lib.rs index 288531cb5b2..72686ddcc09 100644 --- a/src/liballoc_jemalloc/lib.rs +++ b/src/liballoc_jemalloc/lib.rs @@ -11,23 +11,36 @@ #![crate_name = "alloc_jemalloc"] #![crate_type = "rlib"] #![no_std] -#![allocator] #![unstable(feature = "alloc_jemalloc", reason = "this library is unlikely to be stabilized in its current \ form or name", issue = "27783")] #![deny(warnings)] -#![feature(allocator)] #![feature(libc)] #![feature(staged_api)] - +#![feature(linkage)] +#![cfg_attr(stage0, allocator)] +#![cfg_attr(stage0, feature(allocator))] +#![cfg_attr(not(stage0), feature(global_allocator))] +#![cfg_attr(all(not(stage0), not(dummy_jemalloc)), feature(allocator_api))] +#![cfg_attr(not(stage0), feature(alloc))] +#![cfg_attr(not(stage0), feature(alloc_system))] +#![cfg_attr(dummy_jemalloc, allow(dead_code))] + +#[cfg(not(stage0))] +extern crate alloc; +#[cfg(not(stage0))] +extern crate alloc_system; extern crate libc; -pub use imp::*; +#[cfg(all(not(stage0), not(dummy_jemalloc)))] +pub use contents::*; +#[cfg(all(not(stage0), not(dummy_jemalloc)))] +mod contents { + use core::ptr; -// See comments in build.rs for why we sometimes build a crate that does nothing -#[cfg(not(dummy_jemalloc))] -mod imp { + use alloc::heap::{Alloc, AllocErr, Layout}; + use alloc_system::System; use libc::{c_int, c_void, size_t}; // Note that the symbols here are prefixed by default on macOS and Windows (we @@ -91,96 +104,152 @@ fn align_to_flags(align: usize) -> c_int { } } - #[no_mangle] - pub extern "C" fn __rust_allocate(size: usize, align: usize) -> *mut u8 { - let flags = align_to_flags(align); - unsafe { mallocx(size as size_t, flags) as *mut u8 } - } + // for symbol names src/librustc/middle/allocator.rs + // for signatures src/librustc_allocator/lib.rs - #[no_mangle] - pub extern "C" fn __rust_allocate_zeroed(size: usize, align: usize) -> *mut u8 { - if align <= MIN_ALIGN { - unsafe { calloc(size as size_t, 1) as *mut u8 } - } else { - let flags = align_to_flags(align) | MALLOCX_ZERO; - unsafe { mallocx(size as size_t, flags) as *mut u8 } - } - } + // linkage directives are provided as part of the current compiler allocator + // ABI #[no_mangle] - pub extern "C" fn __rust_reallocate(ptr: *mut u8, - _old_size: usize, - size: usize, - align: usize) - -> *mut u8 { + #[linkage = "external"] + pub unsafe extern fn __rde_alloc(size: usize, + align: usize, + err: *mut u8) -> *mut u8 { let flags = align_to_flags(align); - unsafe { rallocx(ptr as *mut c_void, size as size_t, flags) as *mut u8 } + let ptr = mallocx(size as size_t, flags) as *mut u8; + if ptr.is_null() { + let layout = Layout::from_size_align_unchecked(size, align); + ptr::write(err as *mut AllocErr, + AllocErr::Exhausted { request: layout }); + } + ptr } #[no_mangle] - pub extern "C" fn __rust_reallocate_inplace(ptr: *mut u8, - _old_size: usize, - size: usize, - align: usize) - -> usize { - let flags = align_to_flags(align); - unsafe { xallocx(ptr as *mut c_void, size as size_t, 0, flags) as usize } + #[linkage = "external"] + pub unsafe extern fn __rde_oom(err: *const u8) -> ! { + System.oom((*(err as *const AllocErr)).clone()) } #[no_mangle] - pub extern "C" fn __rust_deallocate(ptr: *mut u8, old_size: usize, align: usize) { + #[linkage = "external"] + pub unsafe extern fn __rde_dealloc(ptr: *mut u8, + size: usize, + align: usize) { let flags = align_to_flags(align); - unsafe { sdallocx(ptr as *mut c_void, old_size as size_t, flags) } + sdallocx(ptr as *mut c_void, size, flags); } #[no_mangle] - pub extern "C" fn __rust_usable_size(size: usize, align: usize) -> usize { - let flags = align_to_flags(align); - unsafe { nallocx(size as size_t, flags) as usize } - } -} - -#[cfg(dummy_jemalloc)] -mod imp { - fn bogus() -> ! { - panic!("jemalloc is not implemented for this platform"); + #[linkage = "external"] + pub unsafe extern fn __rde_usable_size(layout: *const u8, + min: *mut usize, + max: *mut usize) { + let layout = &*(layout as *const Layout); + let flags = align_to_flags(layout.align()); + let size = nallocx(layout.size(), flags) as usize; + *min = layout.size(); + if size > 0 { + *max = size; + } else { + *max = layout.size(); + } } #[no_mangle] - pub extern "C" fn __rust_allocate(_size: usize, _align: usize) -> *mut u8 { - bogus() + #[linkage = "external"] + pub unsafe extern fn __rde_realloc(ptr: *mut u8, + _old_size: usize, + old_align: usize, + new_size: usize, + new_align: usize, + err: *mut u8) -> *mut u8 { + if new_align != old_align { + ptr::write(err as *mut AllocErr, + AllocErr::Unsupported { details: "can't change alignments" }); + return 0 as *mut u8 + } + + let flags = align_to_flags(new_align); + let ptr = rallocx(ptr as *mut c_void, new_size, flags) as *mut u8; + if ptr.is_null() { + let layout = Layout::from_size_align_unchecked(new_size, new_align); + ptr::write(err as *mut AllocErr, + AllocErr::Exhausted { request: layout }); + } + ptr } #[no_mangle] - pub extern "C" fn __rust_allocate_zeroed(_size: usize, _align: usize) -> *mut u8 { - bogus() + #[linkage = "external"] + pub unsafe extern fn __rde_alloc_zeroed(size: usize, + align: usize, + err: *mut u8) -> *mut u8 { + let ptr = if align <= MIN_ALIGN { + calloc(size as size_t, 1) as *mut u8 + } else { + let flags = align_to_flags(align) | MALLOCX_ZERO; + mallocx(size as size_t, flags) as *mut u8 + }; + if ptr.is_null() { + let layout = Layout::from_size_align_unchecked(size, align); + ptr::write(err as *mut AllocErr, + AllocErr::Exhausted { request: layout }); + } + ptr } #[no_mangle] - pub extern "C" fn __rust_reallocate(_ptr: *mut u8, - _old_size: usize, - _size: usize, - _align: usize) - -> *mut u8 { - bogus() + #[linkage = "external"] + pub unsafe extern fn __rde_alloc_excess(size: usize, + align: usize, + excess: *mut usize, + err: *mut u8) -> *mut u8 { + let p = __rde_alloc(size, align, err); + if !p.is_null() { + *excess = size; + } + return p } #[no_mangle] - pub extern "C" fn __rust_reallocate_inplace(_ptr: *mut u8, - _old_size: usize, - _size: usize, - _align: usize) - -> usize { - bogus() + #[linkage = "external"] + pub unsafe extern fn __rde_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 p = __rde_realloc(ptr, old_size, old_align, new_size, new_align, err); + if !p.is_null() { + *excess = new_size; + } + return p } #[no_mangle] - pub extern "C" fn __rust_deallocate(_ptr: *mut u8, _old_size: usize, _align: usize) { - bogus() + #[linkage = "external"] + pub unsafe extern fn __rde_grow_in_place(ptr: *mut u8, + old_size: usize, + old_align: usize, + new_size: usize, + new_align: usize) -> u8 { + __rde_shrink_in_place(ptr, old_size, old_align, new_size, new_align) } #[no_mangle] - pub extern "C" fn __rust_usable_size(_size: usize, _align: usize) -> usize { - bogus() + #[linkage = "external"] + pub unsafe extern fn __rde_shrink_in_place(ptr: *mut u8, + _old_size: usize, + old_align: usize, + new_size: usize, + new_align: usize) -> u8 { + if old_align == new_align { + let flags = align_to_flags(new_align); + (xallocx(ptr as *mut c_void, new_size, 0, flags) == new_size) as u8 + } else { + 0 + } } } diff --git a/src/liballoc_system/Cargo.toml b/src/liballoc_system/Cargo.toml index 8e3c2c0b9cc..f20be5fdf5f 100644 --- a/src/liballoc_system/Cargo.toml +++ b/src/liballoc_system/Cargo.toml @@ -12,3 +12,6 @@ doc = false [dependencies] core = { path = "../libcore" } libc = { path = "../rustc/libc_shim" } + +[target.'cfg(not(stage0))'.dependencies] +alloc = { path = "../liballoc" } diff --git a/src/liballoc_system/lib.rs b/src/liballoc_system/lib.rs index 1f36bc4fbce..afecfc16f2c 100644 --- a/src/liballoc_system/lib.rs +++ b/src/liballoc_system/lib.rs @@ -11,13 +11,18 @@ #![crate_name = "alloc_system"] #![crate_type = "rlib"] #![no_std] -#![allocator] #![deny(warnings)] #![unstable(feature = "alloc_system", reason = "this library is unlikely to be stabilized in its current \ form or name", issue = "27783")] -#![feature(allocator)] +#![cfg_attr(stage0, allocator)] +#![cfg_attr(stage0, feature(allocator))] +#![cfg_attr(stage0, feature(core_intrinsics))] +#![cfg_attr(not(stage0), feature(global_allocator))] +#![cfg_attr(not(stage0), feature(allocator_api))] +#![cfg_attr(not(stage0), feature(alloc))] +#![cfg_attr(not(stage0), feature(core_intrinsics))] #![feature(staged_api)] #![cfg_attr(any(unix, target_os = "redox"), feature(libc))] @@ -39,62 +44,201 @@ target_arch = "sparc64")))] const MIN_ALIGN: usize = 16; -#[no_mangle] -pub extern "C" fn __rust_allocate(size: usize, align: usize) -> *mut u8 { - unsafe { imp::allocate(size, align) } -} +#[cfg(stage0)] +pub use old::*; +#[cfg(stage0)] +mod old; -#[no_mangle] -pub extern "C" fn __rust_allocate_zeroed(size: usize, align: usize) -> *mut u8 { - unsafe { imp::allocate_zeroed(size, align) } -} +#[cfg(not(stage0))] +pub use new::System; +#[cfg(not(stage0))] +mod new { + pub extern crate alloc; -#[no_mangle] -pub extern "C" fn __rust_deallocate(ptr: *mut u8, old_size: usize, align: usize) { - unsafe { imp::deallocate(ptr, old_size, align) } -} + use self::alloc::heap::{Alloc, AllocErr, Layout, Excess, CannotReallocInPlace}; -#[no_mangle] -pub extern "C" fn __rust_reallocate(ptr: *mut u8, - old_size: usize, - size: usize, - align: usize) - -> *mut u8 { - unsafe { imp::reallocate(ptr, old_size, size, align) } -} + #[unstable(feature = "allocator_api", issue = "32838")] + pub struct System; -#[no_mangle] -pub extern "C" fn __rust_reallocate_inplace(ptr: *mut u8, - old_size: usize, - size: usize, - align: usize) - -> usize { - unsafe { imp::reallocate_inplace(ptr, old_size, size, align) } -} + #[unstable(feature = "allocator_api", issue = "32838")] + unsafe impl Alloc for System { + #[inline] + unsafe fn alloc(&mut self, layout: Layout) -> Result<*mut u8, AllocErr> { + (&*self).alloc(layout) + } + + #[inline] + unsafe fn alloc_zeroed(&mut self, layout: Layout) + -> Result<*mut u8, AllocErr> + { + (&*self).alloc_zeroed(layout) + } + + #[inline] + unsafe fn dealloc(&mut self, ptr: *mut u8, layout: Layout) { + (&*self).dealloc(ptr, layout) + } + + #[inline] + unsafe fn realloc(&mut self, + ptr: *mut u8, + old_layout: Layout, + new_layout: Layout) -> Result<*mut u8, AllocErr> { + (&*self).realloc(ptr, old_layout, new_layout) + } -#[no_mangle] -pub extern "C" fn __rust_usable_size(size: usize, align: usize) -> usize { - imp::usable_size(size, align) + fn oom(&mut self, err: AllocErr) -> ! { + (&*self).oom(err) + } + + #[inline] + fn usable_size(&self, layout: &Layout) -> (usize, usize) { + (&self).usable_size(layout) + } + + #[inline] + unsafe fn alloc_excess(&mut self, layout: Layout) -> Result { + (&*self).alloc_excess(layout) + } + + #[inline] + unsafe fn realloc_excess(&mut self, + ptr: *mut u8, + layout: Layout, + new_layout: Layout) -> Result { + (&*self).realloc_excess(ptr, layout, new_layout) + } + + #[inline] + unsafe fn grow_in_place(&mut self, + ptr: *mut u8, + layout: Layout, + new_layout: Layout) -> Result<(), CannotReallocInPlace> { + (&*self).grow_in_place(ptr, layout, new_layout) + } + + #[inline] + unsafe fn shrink_in_place(&mut self, + ptr: *mut u8, + layout: Layout, + new_layout: Layout) -> Result<(), CannotReallocInPlace> { + (&*self).shrink_in_place(ptr, layout, new_layout) + } + } } -#[cfg(any(unix, target_os = "redox"))] -mod imp { +#[cfg(all(not(stage0), any(unix, target_os = "redox")))] +mod platform { extern crate libc; use core::cmp; use core::ptr; + use MIN_ALIGN; + use new::System; + use new::alloc::heap::{Alloc, AllocErr, Layout}; + + #[unstable(feature = "allocator_api", issue = "32838")] + unsafe impl<'a> Alloc for &'a System { + #[inline] + unsafe fn alloc(&mut self, layout: Layout) -> Result<*mut u8, AllocErr> { + let ptr = if layout.align() <= MIN_ALIGN { + libc::malloc(layout.size()) as *mut u8 + } else { + aligned_malloc(&layout) + }; + if !ptr.is_null() { + Ok(ptr) + } else { + Err(AllocErr::Exhausted { request: layout }) + } + } - pub unsafe fn allocate(size: usize, align: usize) -> *mut u8 { - if align <= MIN_ALIGN { - libc::malloc(size as libc::size_t) as *mut u8 - } else { - aligned_malloc(size, align) + #[inline] + unsafe fn alloc_zeroed(&mut self, layout: Layout) + -> Result<*mut u8, AllocErr> + { + if layout.align() <= MIN_ALIGN { + let ptr = libc::calloc(layout.size(), 1) as *mut u8; + if !ptr.is_null() { + Ok(ptr) + } else { + Err(AllocErr::Exhausted { request: layout }) + } + } else { + let ret = self.alloc(layout.clone()); + if let Ok(ptr) = ret { + ptr::write_bytes(ptr, 0, layout.size()); + } + ret + } + } + + #[inline] + unsafe fn dealloc(&mut self, ptr: *mut u8, _layout: Layout) { + libc::free(ptr as *mut libc::c_void) + } + + #[inline] + unsafe fn realloc(&mut self, + ptr: *mut u8, + old_layout: Layout, + new_layout: Layout) -> Result<*mut u8, AllocErr> { + if old_layout.align() != new_layout.align() { + return Err(AllocErr::Unsupported { + details: "cannot change alignment on `realloc`", + }) + } + + if new_layout.align() <= MIN_ALIGN { + let ptr = libc::realloc(ptr as *mut libc::c_void, new_layout.size()); + if !ptr.is_null() { + Ok(ptr as *mut u8) + } else { + Err(AllocErr::Exhausted { request: new_layout }) + } + } else { + let res = self.alloc(new_layout.clone()); + if let Ok(new_ptr) = res { + let size = cmp::min(old_layout.size(), new_layout.size()); + ptr::copy_nonoverlapping(ptr, new_ptr, size); + self.dealloc(ptr, old_layout); + } + res + } + } + + fn oom(&mut self, err: AllocErr) -> ! { + use core::fmt::{self, Write}; + + // Print a message to stderr before aborting to assist with + // debugging. 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. + drop(writeln!(Stderr, "fatal runtime error: {}", err)); + unsafe { + ::core::intrinsics::abort(); + } + + struct Stderr; + + impl Write for Stderr { + fn write_str(&mut self, s: &str) -> fmt::Result { + unsafe { + libc::write(libc::STDERR_FILENO, + s.as_ptr() as *const libc::c_void, + s.len()); + } + Ok(()) + } + } } } #[cfg(any(target_os = "android", target_os = "redox"))] - unsafe fn aligned_malloc(size: usize, align: usize) -> *mut u8 { + #[inline] + unsafe fn aligned_malloc(layout: &Layout) -> *mut u8 { // On android we currently target API level 9 which unfortunately // doesn't have the `posix_memalign` API used below. Instead we use // `memalign`, but this unfortunately has the property on some systems @@ -112,74 +256,41 @@ unsafe fn aligned_malloc(size: usize, align: usize) -> *mut u8 { // [3]: https://bugs.chromium.org/p/chromium/issues/detail?id=138579 // [4]: https://chromium.googlesource.com/chromium/src/base/+/master/ // /memory/aligned_memory.cc - libc::memalign(align as libc::size_t, size as libc::size_t) as *mut u8 + libc::memalign(layout.align(), layout.size()) as *mut u8 } #[cfg(not(any(target_os = "android", target_os = "redox")))] - unsafe fn aligned_malloc(size: usize, align: usize) -> *mut u8 { + #[inline] + unsafe fn aligned_malloc(layout: &Layout) -> *mut u8 { let mut out = ptr::null_mut(); - let ret = libc::posix_memalign(&mut out, align as libc::size_t, size as libc::size_t); + let ret = libc::posix_memalign(&mut out, layout.align(), layout.size()); if ret != 0 { ptr::null_mut() } else { out as *mut u8 } } - - pub unsafe fn allocate_zeroed(size: usize, align: usize) -> *mut u8 { - if align <= MIN_ALIGN { - libc::calloc(size as libc::size_t, 1) as *mut u8 - } else { - let ptr = aligned_malloc(size, align); - if !ptr.is_null() { - ptr::write_bytes(ptr, 0, size); - } - ptr - } - } - - pub unsafe fn reallocate(ptr: *mut u8, old_size: usize, size: usize, align: usize) -> *mut u8 { - if align <= MIN_ALIGN { - libc::realloc(ptr as *mut libc::c_void, size as libc::size_t) as *mut u8 - } else { - let new_ptr = allocate(size, align); - if !new_ptr.is_null() { - ptr::copy(ptr, new_ptr, cmp::min(size, old_size)); - deallocate(ptr, old_size, align); - } - new_ptr - } - } - - pub unsafe fn reallocate_inplace(_ptr: *mut u8, - old_size: usize, - _size: usize, - _align: usize) - -> usize { - old_size - } - - pub unsafe fn deallocate(ptr: *mut u8, _old_size: usize, _align: usize) { - libc::free(ptr as *mut libc::c_void) - } - - pub fn usable_size(size: usize, _align: usize) -> usize { - size - } } -#[cfg(windows)] +#[cfg(all(windows, not(stage0)))] #[allow(bad_style)] -mod imp { - use core::cmp::min; - use core::ptr::copy_nonoverlapping; +mod platform { + use core::cmp; + use core::ptr; + use MIN_ALIGN; + use new::System; + use new::alloc::heap::{Alloc, AllocErr, Layout, CannotReallocInPlace}; type LPVOID = *mut u8; type HANDLE = LPVOID; type SIZE_T = usize; type DWORD = u32; type BOOL = i32; + type LPDWORD = *mut DWORD; + type LPOVERLAPPED = *mut u8; + + const STD_ERROR_HANDLE: DWORD = -12i32 as DWORD; extern "system" { fn GetProcessHeap() -> HANDLE; @@ -187,12 +298,18 @@ mod imp { fn HeapReAlloc(hHeap: HANDLE, dwFlags: DWORD, lpMem: LPVOID, dwBytes: SIZE_T) -> LPVOID; fn HeapFree(hHeap: HANDLE, dwFlags: DWORD, lpMem: LPVOID) -> BOOL; fn GetLastError() -> DWORD; + fn WriteFile(hFile: HANDLE, + lpBuffer: LPVOID, + nNumberOfBytesToWrite: DWORD, + lpNumberOfBytesWritten: LPDWORD, + lpOverlapped: LPOVERLAPPED) + -> BOOL; + fn GetStdHandle(which: DWORD) -> HANDLE; } #[repr(C)] struct Header(*mut u8); - const HEAP_ZERO_MEMORY: DWORD = 0x00000008; const HEAP_REALLOC_IN_PLACE_ONLY: DWORD = 0x00000010; @@ -207,71 +324,149 @@ unsafe fn align_ptr(ptr: *mut u8, align: usize) -> *mut u8 { } #[inline] - unsafe fn allocate_with_flags(size: usize, align: usize, flags: DWORD) -> *mut u8 { - if align <= MIN_ALIGN { - HeapAlloc(GetProcessHeap(), flags, size as SIZE_T) as *mut u8 + unsafe fn allocate_with_flags(layout: Layout, flags: DWORD) + -> Result<*mut u8, AllocErr> + { + let ptr = if layout.align() <= MIN_ALIGN { + HeapAlloc(GetProcessHeap(), flags, layout.size()) } else { - let ptr = HeapAlloc(GetProcessHeap(), flags, (size + align) as SIZE_T) as *mut u8; + let size = layout.size() + layout.align(); + let ptr = HeapAlloc(GetProcessHeap(), flags, size); if ptr.is_null() { - return ptr; + ptr + } else { + align_ptr(ptr, layout.align()) } - align_ptr(ptr, align) + }; + if ptr.is_null() { + Err(AllocErr::Exhausted { request: layout }) + } else { + Ok(ptr as *mut u8) } } - pub unsafe fn allocate(size: usize, align: usize) -> *mut u8 { - allocate_with_flags(size, align, 0) - } + #[unstable(feature = "allocator_api", issue = "32838")] + unsafe impl<'a> Alloc for &'a System { + #[inline] + unsafe fn alloc(&mut self, layout: Layout) -> Result<*mut u8, AllocErr> { + allocate_with_flags(layout, 0) + } - pub unsafe fn allocate_zeroed(size: usize, align: usize) -> *mut u8 { - allocate_with_flags(size, align, HEAP_ZERO_MEMORY) - } + #[inline] + unsafe fn alloc_zeroed(&mut self, layout: Layout) + -> Result<*mut u8, AllocErr> + { + allocate_with_flags(layout, HEAP_ZERO_MEMORY) + } - pub unsafe fn reallocate(ptr: *mut u8, old_size: usize, size: usize, align: usize) -> *mut u8 { - if align <= MIN_ALIGN { - HeapReAlloc(GetProcessHeap(), 0, ptr as LPVOID, size as SIZE_T) as *mut u8 - } else { - let new = allocate(size, align); - if !new.is_null() { - copy_nonoverlapping(ptr, new, min(size, old_size)); - deallocate(ptr, old_size, align); + #[inline] + unsafe fn dealloc(&mut self, ptr: *mut u8, layout: Layout) { + if layout.align() <= MIN_ALIGN { + let err = HeapFree(GetProcessHeap(), 0, ptr as LPVOID); + debug_assert!(err != 0, "Failed to free heap memory: {}", + GetLastError()); + } else { + let header = get_header(ptr); + let err = HeapFree(GetProcessHeap(), 0, header.0 as LPVOID); + debug_assert!(err != 0, "Failed to free heap memory: {}", + GetLastError()); } - new } - } - pub unsafe fn reallocate_inplace(ptr: *mut u8, - old_size: usize, - size: usize, - align: usize) - -> usize { - let new = if align <= MIN_ALIGN { - HeapReAlloc(GetProcessHeap(), - HEAP_REALLOC_IN_PLACE_ONLY, - ptr as LPVOID, - size as SIZE_T) as *mut u8 - } else { - let header = get_header(ptr); - HeapReAlloc(GetProcessHeap(), - HEAP_REALLOC_IN_PLACE_ONLY, - header.0 as LPVOID, - size + align as SIZE_T) as *mut u8 - }; - if new.is_null() { old_size } else { size } - } + #[inline] + unsafe fn realloc(&mut self, + ptr: *mut u8, + old_layout: Layout, + new_layout: Layout) -> Result<*mut u8, AllocErr> { + if old_layout.align() != new_layout.align() { + return Err(AllocErr::Unsupported { + details: "cannot change alignment on `realloc`", + }) + } - pub unsafe fn deallocate(ptr: *mut u8, _old_size: usize, align: usize) { - if align <= MIN_ALIGN { - let err = HeapFree(GetProcessHeap(), 0, ptr as LPVOID); - debug_assert!(err != 0, "Failed to free heap memory: {}", GetLastError()); - } else { - let header = get_header(ptr); - let err = HeapFree(GetProcessHeap(), 0, header.0 as LPVOID); - debug_assert!(err != 0, "Failed to free heap memory: {}", GetLastError()); + if new_layout.align() <= MIN_ALIGN { + let ptr = HeapReAlloc(GetProcessHeap(), + 0, + ptr as LPVOID, + new_layout.size()); + if !ptr.is_null() { + Ok(ptr as *mut u8) + } else { + Err(AllocErr::Exhausted { request: new_layout }) + } + } else { + let res = self.alloc(new_layout.clone()); + if let Ok(new_ptr) = res { + let size = cmp::min(old_layout.size(), new_layout.size()); + ptr::copy_nonoverlapping(ptr, new_ptr, size); + self.dealloc(ptr, old_layout); + } + res + } } - } - pub fn usable_size(size: usize, _align: usize) -> usize { - size + #[inline] + unsafe fn grow_in_place(&mut self, + ptr: *mut u8, + layout: Layout, + new_layout: Layout) -> Result<(), CannotReallocInPlace> { + self.shrink_in_place(ptr, layout, new_layout) + } + + #[inline] + unsafe fn shrink_in_place(&mut self, + ptr: *mut u8, + old_layout: Layout, + new_layout: Layout) -> Result<(), CannotReallocInPlace> { + if old_layout.align() != new_layout.align() { + return Err(CannotReallocInPlace) + } + + let new = if new_layout.align() <= MIN_ALIGN { + HeapReAlloc(GetProcessHeap(), + HEAP_REALLOC_IN_PLACE_ONLY, + ptr as LPVOID, + new_layout.size()) + } else { + let header = get_header(ptr); + HeapReAlloc(GetProcessHeap(), + HEAP_REALLOC_IN_PLACE_ONLY, + header.0 as LPVOID, + new_layout.size() + new_layout.align()) + }; + if new.is_null() { + Err(CannotReallocInPlace) + } else { + Ok(()) + } + } + + fn oom(&mut self, err: AllocErr) -> ! { + use core::fmt::{self, Write}; + + // Same as with unix we ignore all errors here + drop(writeln!(Stderr, "fatal runtime error: {}", err)); + unsafe { + ::core::intrinsics::abort(); + } + + struct Stderr; + + impl Write for Stderr { + fn write_str(&mut self, s: &str) -> fmt::Result { + unsafe { + // WriteFile silently fails if it is passed an invalid + // handle, so there is no need to check the result of + // GetStdHandle. + WriteFile(GetStdHandle(STD_ERROR_HANDLE), + s.as_ptr() as LPVOID, + s.len() as DWORD, + ptr::null_mut(), + ptr::null_mut()); + } + Ok(()) + } + } + } } } diff --git a/src/liballoc_system/old.rs b/src/liballoc_system/old.rs new file mode 100644 index 00000000000..80aa4607594 --- /dev/null +++ b/src/liballoc_system/old.rs @@ -0,0 +1,268 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#[no_mangle] +pub unsafe extern fn __rust_alloc(size: usize, + align: usize, + err: *mut u8) -> *mut u8 { + let p = imp::allocate(size, align); + if p.is_null() { + __rust_oom(err); + } + p +} + +#[no_mangle] +pub unsafe extern fn __rust_oom(_err: *const u8) -> ! { + ::core::intrinsics::abort() +} + +#[no_mangle] +pub unsafe extern fn __rust_dealloc(ptr: *mut u8, + size: usize, + align: usize) { + imp::deallocate(ptr, size, align) +} + +#[no_mangle] +pub unsafe extern fn __rust_usable_size(size: usize, + _align: usize, + min: *mut usize, + max: *mut usize) { + *min = size; + *max = size; +} + +#[no_mangle] +pub unsafe extern fn __rust_realloc(ptr: *mut u8, + old_size: usize, + old_align: usize, + new_size: usize, + new_align: usize, + err: *mut u8) -> *mut u8 { + if new_align != old_align { + __rust_oom(err); + } + let p = imp::reallocate(ptr, old_size, new_size, new_align); + if p.is_null() { + __rust_oom(err); + } + p +} + +#[no_mangle] +pub unsafe extern fn __rust_alloc_zeroed(size: usize, + align: usize, + err: *mut u8) -> *mut u8 { + let p = imp::allocate_zeroed(size, align); + if p.is_null() { + __rust_oom(err); + } + p +} + +#[no_mangle] +pub unsafe extern fn __rust_alloc_excess(_size: usize, + _align: usize, + _excess: *mut usize, + err: *mut u8) -> *mut u8 { + __rust_oom(err); +} + +#[no_mangle] +pub unsafe extern fn __rust_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 { + __rust_oom(err); +} + +#[no_mangle] +pub unsafe extern fn __rust_grow_in_place(_ptr: *mut u8, + _old_size: usize, + _old_align: usize, + _new_size: usize, + _new_align: usize) -> u8 { + 0 +} + +#[no_mangle] +pub unsafe extern fn __rust_shrink_in_place(_ptr: *mut u8, + _old_size: usize, + _old_align: usize, + _new_size: usize, + _new_align: usize) -> u8 { + 0 +} + +#[cfg(any(unix, target_os = "redox"))] +mod imp { + extern crate libc; + + use core::cmp; + use core::ptr; + use MIN_ALIGN; + + pub unsafe fn allocate(size: usize, align: usize) -> *mut u8 { + if align <= MIN_ALIGN { + libc::malloc(size as libc::size_t) as *mut u8 + } else { + aligned_malloc(size, align) + } + } + + #[cfg(any(target_os = "android", target_os = "redox"))] + unsafe fn aligned_malloc(size: usize, align: usize) -> *mut u8 { + // On android we currently target API level 9 which unfortunately + // doesn't have the `posix_memalign` API used below. Instead we use + // `memalign`, but this unfortunately has the property on some systems + // where the memory returned cannot be deallocated by `free`! + // + // Upon closer inspection, however, this appears to work just fine with + // Android, so for this platform we should be fine to call `memalign` + // (which is present in API level 9). Some helpful references could + // possibly be chromium using memalign [1], attempts at documenting that + // memalign + free is ok [2] [3], or the current source of chromium + // which still uses memalign on android [4]. + // + // [1]: https://codereview.chromium.org/10796020/ + // [2]: https://code.google.com/p/android/issues/detail?id=35391 + // [3]: https://bugs.chromium.org/p/chromium/issues/detail?id=138579 + // [4]: https://chromium.googlesource.com/chromium/src/base/+/master/ + // /memory/aligned_memory.cc + libc::memalign(align as libc::size_t, size as libc::size_t) as *mut u8 + } + + #[cfg(not(any(target_os = "android", target_os = "redox")))] + unsafe fn aligned_malloc(size: usize, align: usize) -> *mut u8 { + let mut out = ptr::null_mut(); + let ret = libc::posix_memalign(&mut out, align as libc::size_t, size as libc::size_t); + if ret != 0 { + ptr::null_mut() + } else { + out as *mut u8 + } + } + + pub unsafe fn allocate_zeroed(size: usize, align: usize) -> *mut u8 { + if align <= MIN_ALIGN { + libc::calloc(size as libc::size_t, 1) as *mut u8 + } else { + let ptr = aligned_malloc(size, align); + if !ptr.is_null() { + ptr::write_bytes(ptr, 0, size); + } + ptr + } + } + + pub unsafe fn reallocate(ptr: *mut u8, old_size: usize, size: usize, align: usize) -> *mut u8 { + if align <= MIN_ALIGN { + libc::realloc(ptr as *mut libc::c_void, size as libc::size_t) as *mut u8 + } else { + let new_ptr = allocate(size, align); + if !new_ptr.is_null() { + ptr::copy(ptr, new_ptr, cmp::min(size, old_size)); + deallocate(ptr, old_size, align); + } + new_ptr + } + } + + pub unsafe fn deallocate(ptr: *mut u8, _old_size: usize, _align: usize) { + libc::free(ptr as *mut libc::c_void) + } +} + +#[cfg(windows)] +#[allow(bad_style)] +mod imp { + use core::cmp::min; + use core::ptr::copy_nonoverlapping; + use MIN_ALIGN; + + type LPVOID = *mut u8; + type HANDLE = LPVOID; + type SIZE_T = usize; + type DWORD = u32; + type BOOL = i32; + + extern "system" { + fn GetProcessHeap() -> HANDLE; + fn HeapAlloc(hHeap: HANDLE, dwFlags: DWORD, dwBytes: SIZE_T) -> LPVOID; + fn HeapReAlloc(hHeap: HANDLE, dwFlags: DWORD, lpMem: LPVOID, dwBytes: SIZE_T) -> LPVOID; + fn HeapFree(hHeap: HANDLE, dwFlags: DWORD, lpMem: LPVOID) -> BOOL; + fn GetLastError() -> DWORD; + } + + #[repr(C)] + struct Header(*mut u8); + + + const HEAP_ZERO_MEMORY: DWORD = 0x00000008; + + unsafe fn get_header<'a>(ptr: *mut u8) -> &'a mut Header { + &mut *(ptr as *mut Header).offset(-1) + } + + unsafe fn align_ptr(ptr: *mut u8, align: usize) -> *mut u8 { + let aligned = ptr.offset((align - (ptr as usize & (align - 1))) as isize); + *get_header(aligned) = Header(ptr); + aligned + } + + #[inline] + unsafe fn allocate_with_flags(size: usize, align: usize, flags: DWORD) -> *mut u8 { + if align <= MIN_ALIGN { + HeapAlloc(GetProcessHeap(), flags, size as SIZE_T) as *mut u8 + } else { + let ptr = HeapAlloc(GetProcessHeap(), flags, (size + align) as SIZE_T) as *mut u8; + if ptr.is_null() { + return ptr; + } + align_ptr(ptr, align) + } + } + + pub unsafe fn allocate(size: usize, align: usize) -> *mut u8 { + allocate_with_flags(size, align, 0) + } + + pub unsafe fn allocate_zeroed(size: usize, align: usize) -> *mut u8 { + allocate_with_flags(size, align, HEAP_ZERO_MEMORY) + } + + pub unsafe fn reallocate(ptr: *mut u8, old_size: usize, size: usize, align: usize) -> *mut u8 { + if align <= MIN_ALIGN { + HeapReAlloc(GetProcessHeap(), 0, ptr as LPVOID, size as SIZE_T) as *mut u8 + } else { + let new = allocate(size, align); + if !new.is_null() { + copy_nonoverlapping(ptr, new, min(size, old_size)); + deallocate(ptr, old_size, align); + } + new + } + } + + pub unsafe fn deallocate(ptr: *mut u8, _old_size: usize, align: usize) { + if align <= MIN_ALIGN { + let err = HeapFree(GetProcessHeap(), 0, ptr as LPVOID); + debug_assert!(err != 0, "Failed to free heap memory: {}", GetLastError()); + } else { + let header = get_header(ptr); + let err = HeapFree(GetProcessHeap(), 0, header.0 as LPVOID); + debug_assert!(err != 0, "Failed to free heap memory: {}", GetLastError()); + } + } +} diff --git a/src/libcollections/lib.rs b/src/libcollections/lib.rs index de5d6df328c..38143593eb1 100644 --- a/src/libcollections/lib.rs +++ b/src/libcollections/lib.rs @@ -23,13 +23,11 @@ issue_tracker_base_url = "https://github.com/rust-lang/rust/issues/", test(no_crate_inject, attr(allow(unused_variables), deny(warnings))))] #![no_std] -#![needs_allocator] #![deny(warnings)] #![feature(alloc)] #![feature(collections_range)] #![feature(macro_reexport)] -#![feature(needs_allocator)] #![feature(staged_api)] //! Collection types diff --git a/src/librustc/lib.rs b/src/librustc/lib.rs index e4cf893375c..efa6a6cccc2 100644 --- a/src/librustc/lib.rs +++ b/src/librustc/lib.rs @@ -84,6 +84,7 @@ pub mod lint; pub mod middle { + pub mod allocator; pub mod expr_use_visitor; pub mod const_val; pub mod cstore; diff --git a/src/librustc/middle/allocator.rs b/src/librustc/middle/allocator.rs new file mode 100644 index 00000000000..79a9ef0e8b5 --- /dev/null +++ b/src/librustc/middle/allocator.rs @@ -0,0 +1,26 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#[derive(Clone, Copy)] +pub enum AllocatorKind { + Global, + DefaultLib, + DefaultExe, +} + +impl AllocatorKind { + pub fn fn_name(&self, base: &str) -> String { + match *self { + AllocatorKind::Global => format!("__rg_{}", base), + AllocatorKind::DefaultLib => format!("__rdl_{}", base), + AllocatorKind::DefaultExe => format!("__rde_{}", base), + } + } +} diff --git a/src/librustc/middle/dead.rs b/src/librustc/middle/dead.rs index 6077b7863e2..77b4c977d28 100644 --- a/src/librustc/middle/dead.rs +++ b/src/librustc/middle/dead.rs @@ -287,6 +287,11 @@ fn has_allow_dead_code_or_lang_attr(attrs: &[ast::Attribute]) -> bool { return true; } + // Don't lint about global allocators + if attr::contains_name(attrs, "global_allocator") { + return true; + } + let dead_code = lint::builtin::DEAD_CODE.name_lower(); for attr in lint::gather_attrs(attrs) { match attr { diff --git a/src/librustc/middle/dependency_format.rs b/src/librustc/middle/dependency_format.rs index 4e1f06cca06..9af93d0d494 100644 --- a/src/librustc/middle/dependency_format.rs +++ b/src/librustc/middle/dependency_format.rs @@ -214,10 +214,9 @@ fn calculate_type<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, // // Things like allocators and panic runtimes may not have been activated // quite yet, so do so here. - activate_injected_dep(sess.injected_allocator.get(), &mut ret, - &|cnum| tcx.is_allocator(cnum.as_def_id())); activate_injected_dep(sess.injected_panic_runtime.get(), &mut ret, &|cnum| tcx.is_panic_runtime(cnum.as_def_id())); + activate_injected_allocator(sess, &mut ret); // When dylib B links to dylib A, then when using B we must also link to A. // It could be the case, however, that the rlib for A is present (hence we @@ -295,10 +294,9 @@ fn attempt_static<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>) -> Option, } } +fn activate_injected_allocator(sess: &session::Session, + list: &mut DependencyList) { + let cnum = match sess.injected_allocator.get() { + Some(cnum) => cnum, + None => return, + }; + let idx = cnum.as_usize() - 1; + if list[idx] == Linkage::NotLinked { + list[idx] = Linkage::Static; + } +} + // After the linkage for a crate has been determined we need to verify that // there's only going to be one allocator in the output. fn verify_ok<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, list: &[Linkage]) { @@ -338,23 +348,12 @@ fn verify_ok<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, list: &[Linkage]) { if list.len() == 0 { return } - let mut allocator = None; let mut panic_runtime = None; for (i, linkage) in list.iter().enumerate() { if let Linkage::NotLinked = *linkage { continue } let cnum = CrateNum::new(i + 1); - if tcx.is_allocator(cnum.as_def_id()) { - if let Some(prev) = allocator { - let prev_name = sess.cstore.crate_name(prev); - let cur_name = sess.cstore.crate_name(cnum); - sess.err(&format!("cannot link together two \ - allocators: {} and {}", - prev_name, cur_name)); - } - allocator = Some(cnum); - } if tcx.is_panic_runtime(cnum.as_def_id()) { if let Some((prev, _)) = panic_runtime { diff --git a/src/librustc/session/mod.rs b/src/librustc/session/mod.rs index 8bafdda234a..39a719faa12 100644 --- a/src/librustc/session/mod.rs +++ b/src/librustc/session/mod.rs @@ -16,6 +16,7 @@ use lint; use middle::cstore::CrateStore; +use middle::allocator::AllocatorKind; use middle::dependency_format; use session::search_paths::PathKind; use session::config::DebugInfoLevel; @@ -106,6 +107,7 @@ pub struct Session { /// dependency if it didn't already find one, and this tracks what was /// injected. pub injected_allocator: Cell>, + pub allocator_kind: Cell>, pub injected_panic_runtime: Cell>, /// Map from imported macro spans (which consist of @@ -140,6 +142,9 @@ pub struct Session { /// Loaded up early on in the initialization of this `Session` to avoid /// false positives about a job server in our environment. pub jobserver_from_env: Option, + + /// Metadata about the allocators for the current crate being compiled + pub has_global_allocator: Cell, } pub struct PerfStats { @@ -715,6 +720,7 @@ pub fn build_session_(sopts: config::Options, type_length_limit: Cell::new(1048576), next_node_id: Cell::new(NodeId::new(1)), injected_allocator: Cell::new(None), + allocator_kind: Cell::new(None), injected_panic_runtime: Cell::new(None), imported_macro_spans: RefCell::new(HashMap::new()), incr_comp_session: RefCell::new(IncrCompSession::NotInitialized), @@ -732,7 +738,6 @@ pub fn build_session_(sopts: config::Options, print_fuel_crate: print_fuel_crate, print_fuel: print_fuel, out_of_fuel: Cell::new(false), - // Note that this is unsafe because it may misinterpret file descriptors // on Unix as jobserver file descriptors. We hopefully execute this near // the beginning of the process though to ensure we don't get false @@ -750,6 +755,7 @@ pub fn build_session_(sopts: config::Options, }); (*GLOBAL_JOBSERVER).clone() }, + has_global_allocator: Cell::new(false), }; sess diff --git a/src/librustc_allocator/Cargo.toml b/src/librustc_allocator/Cargo.toml new file mode 100644 index 00000000000..e3d1d8e32c4 --- /dev/null +++ b/src/librustc_allocator/Cargo.toml @@ -0,0 +1,15 @@ +[package] +authors = ["The Rust Project Developers"] +name = "rustc_allocator" +version = "0.0.0" + +[lib] +path = "lib.rs" +crate-type = ["dylib"] +test = false + +[dependencies] +rustc = { path = "../librustc" } +rustc_errors = { path = "../librustc_errors" } +syntax = { path = "../libsyntax" } +syntax_pos = { path = "../libsyntax_pos" } diff --git a/src/librustc_allocator/expand.rs b/src/librustc_allocator/expand.rs new file mode 100644 index 00000000000..e942b7264c5 --- /dev/null +++ b/src/librustc_allocator/expand.rs @@ -0,0 +1,498 @@ +// Copyright 2016 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use rustc::middle::allocator::AllocatorKind; +use rustc_errors; +use syntax::abi::Abi; +use syntax::ast::{Crate, Attribute, LitKind, StrStyle, ExprKind}; +use syntax::ast::{Unsafety, Constness, Generics, Mutability, Ty, Mac, Arg}; +use syntax::ast::{self, Ident, Item, ItemKind, TyKind, Visibility, Expr}; +use syntax::attr; +use syntax::codemap::dummy_spanned; +use syntax::codemap::{ExpnInfo, NameAndSpan, MacroAttribute}; +use syntax::ext::base::ExtCtxt; +use syntax::ext::base::Resolver; +use syntax::ext::build::AstBuilder; +use syntax::ext::expand::ExpansionConfig; +use syntax::ext::hygiene::{Mark, SyntaxContext}; +use syntax::fold::{self, Folder}; +use syntax::parse::ParseSess; +use syntax::ptr::P; +use syntax::symbol::Symbol; +use syntax::util::small_vector::SmallVector; +use syntax_pos::{Span, DUMMY_SP}; + +use {AllocatorMethod, AllocatorTy, ALLOCATOR_METHODS}; + +pub fn modify(sess: &ParseSess, + resolver: &mut Resolver, + krate: Crate, + handler: &rustc_errors::Handler) -> ast::Crate { + ExpandAllocatorDirectives { + handler: handler, + sess: sess, + resolver: resolver, + found: false, + }.fold_crate(krate) +} + +struct ExpandAllocatorDirectives<'a> { + found: bool, + handler: &'a rustc_errors::Handler, + sess: &'a ParseSess, + resolver: &'a mut Resolver, +} + +impl<'a> Folder for ExpandAllocatorDirectives<'a> { + fn fold_item(&mut self, item: P) -> SmallVector> { + let name = if attr::contains_name(&item.attrs, "global_allocator") { + "global_allocator" + } else { + return fold::noop_fold_item(item, self) + }; + match item.node { + ItemKind::Static(..) => {} + _ => { + self.handler.span_err(item.span, "allocators must be statics"); + return SmallVector::one(item) + } + } + + if self.found { + self.handler.span_err(item.span, "cannot define more than one \ + #[global_allocator]"); + return SmallVector::one(item) + } + self.found = true; + + let mark = Mark::fresh(Mark::root()); + mark.set_expn_info(ExpnInfo { + call_site: DUMMY_SP, + callee: NameAndSpan { + format: MacroAttribute(Symbol::intern(name)), + span: None, + allow_internal_unstable: true, + } + }); + let span = Span { + ctxt: SyntaxContext::empty().apply_mark(mark), + ..item.span + }; + let ecfg = ExpansionConfig::default(name.to_string()); + let mut f = AllocFnFactory { + span: span, + kind: AllocatorKind::Global, + global: item.ident, + alloc: Ident::from_str("alloc"), + cx: ExtCtxt::new(self.sess, ecfg, self.resolver), + }; + let super_path = f.cx.path(f.span, vec![ + Ident::from_str("super"), + f.global, + ]); + let mut items = vec![ + f.cx.item_extern_crate(f.span, f.alloc), + f.cx.item_use_simple(f.span, Visibility::Inherited, super_path), + ]; + for method in ALLOCATOR_METHODS { + items.push(f.allocator_fn(method)); + } + let name = f.kind.fn_name("allocator_abi"); + let allocator_abi = Ident::with_empty_ctxt(Symbol::gensym(&name)); + let module = f.cx.item_mod(span, span, allocator_abi, Vec::new(), items); + let module = f.cx.monotonic_expander().fold_item(module).pop().unwrap(); + + let mut ret = SmallVector::new(); + ret.push(item); + ret.push(module); + return ret + } + + fn fold_mac(&mut self, mac: Mac) -> Mac { + fold::noop_fold_mac(mac, self) + } +} + +struct AllocFnFactory<'a> { + span: Span, + kind: AllocatorKind, + global: Ident, + alloc: Ident, + cx: ExtCtxt<'a>, +} + +impl<'a> AllocFnFactory<'a> { + fn allocator_fn(&self, method: &AllocatorMethod) -> P { + let mut abi_args = Vec::new(); + let mut i = 0; + let ref mut mk = || { + let name = Ident::from_str(&format!("arg{}", i)); + i += 1; + name + }; + let args = method.inputs.iter().map(|ty| { + self.arg_ty(ty, &mut abi_args, mk) + }).collect(); + let result = self.call_allocator(method.name, args); + let (output_ty, output_expr) = + self.ret_ty(&method.output, &mut abi_args, mk, result); + let kind = ItemKind::Fn(self.cx.fn_decl(abi_args, output_ty), + Unsafety::Unsafe, + dummy_spanned(Constness::NotConst), + Abi::Rust, + Generics::default(), + self.cx.block_expr(output_expr)); + self.cx.item(self.span, + Ident::from_str(&self.kind.fn_name(method.name)), + self.attrs(), + kind) + } + + fn call_allocator(&self, method: &str, mut args: Vec>) -> P { + let method = self.cx.path(self.span, vec![ + self.alloc, + Ident::from_str("heap"), + Ident::from_str("Alloc"), + Ident::from_str(method), + ]); + let method = self.cx.expr_path(method); + let allocator = self.cx.path_ident(self.span, self.global); + let allocator = self.cx.expr_path(allocator); + let allocator = self.cx.expr_addr_of(self.span, allocator); + let allocator = self.cx.expr_mut_addr_of(self.span, allocator); + args.insert(0, allocator); + + self.cx.expr_call(self.span, method, args) + } + + fn attrs(&self) -> Vec { + let key = Symbol::intern("linkage"); + let value = LitKind::Str(Symbol::intern("external"), StrStyle::Cooked); + let linkage = self.cx.meta_name_value(self.span, key, value); + + let no_mangle = Symbol::intern("no_mangle"); + let no_mangle = self.cx.meta_word(self.span, no_mangle); + vec![ + self.cx.attribute(self.span, linkage), + self.cx.attribute(self.span, no_mangle), + ] + } + + fn arg_ty(&self, + ty: &AllocatorTy, + args: &mut Vec, + mut ident: &mut FnMut() -> Ident) -> P { + match *ty { + AllocatorTy::Layout => { + let usize = self.cx.path_ident(self.span, Ident::from_str("usize")); + let ty_usize = self.cx.ty_path(usize); + let size = ident(); + let align = ident(); + args.push(self.cx.arg(self.span, size, ty_usize.clone())); + args.push(self.cx.arg(self.span, align, ty_usize)); + + let layout_new = self.cx.path(self.span, vec![ + self.alloc, + Ident::from_str("heap"), + Ident::from_str("Layout"), + Ident::from_str("from_size_align_unchecked"), + ]); + let layout_new = self.cx.expr_path(layout_new); + let size = self.cx.expr_ident(self.span, size); + let align = self.cx.expr_ident(self.span, align); + let layout = self.cx.expr_call(self.span, + layout_new, + vec![size, align]); + layout + } + + AllocatorTy::LayoutRef => { + let ident = ident(); + args.push(self.cx.arg(self.span, ident, self.ptr_u8())); + + // Convert our `arg: *const u8` via: + // + // &*(arg as *const Layout) + let expr = self.cx.expr_ident(self.span, ident); + let expr = self.cx.expr_cast(self.span, expr, self.layout_ptr()); + let expr = self.cx.expr_deref(self.span, expr); + self.cx.expr_addr_of(self.span, expr) + } + + AllocatorTy::AllocErr => { + // We're creating: + // + // (*(arg as *const AllocErr)).clone() + let ident = ident(); + args.push(self.cx.arg(self.span, ident, self.ptr_u8())); + let expr = self.cx.expr_ident(self.span, ident); + let expr = self.cx.expr_cast(self.span, expr, self.alloc_err_ptr()); + let expr = self.cx.expr_deref(self.span, expr); + self.cx.expr_method_call( + self.span, + expr, + Ident::from_str("clone"), + Vec::new() + ) + } + + AllocatorTy::Ptr => { + let ident = ident(); + args.push(self.cx.arg(self.span, ident, self.ptr_u8())); + self.cx.expr_ident(self.span, ident) + } + + AllocatorTy::ResultPtr | + AllocatorTy::ResultExcess | + AllocatorTy::ResultUnit | + AllocatorTy::Bang | + AllocatorTy::UsizePair | + AllocatorTy::Unit => { + panic!("can't convert AllocatorTy to an argument") + } + } + } + + fn ret_ty(&self, + ty: &AllocatorTy, + args: &mut Vec, + mut ident: &mut FnMut() -> Ident, + expr: P) -> (P, P) + { + match *ty { + AllocatorTy::UsizePair => { + // We're creating: + // + // let arg = #expr; + // *min = arg.0; + // *max = arg.1; + + let min = ident(); + let max = ident(); + + args.push(self.cx.arg(self.span, min, self.ptr_usize())); + args.push(self.cx.arg(self.span, max, self.ptr_usize())); + + let ident = ident(); + let stmt = self.cx.stmt_let(self.span, false, ident, expr); + let min = self.cx.expr_ident(self.span, min); + let max = self.cx.expr_ident(self.span, max); + let layout = self.cx.expr_ident(self.span, ident); + let assign_min = self.cx.expr(self.span, ExprKind::Assign( + self.cx.expr_deref(self.span, min), + self.cx.expr_tup_field_access(self.span, layout.clone(), 0), + )); + let assign_min = self.cx.stmt_semi(assign_min); + let assign_max = self.cx.expr(self.span, ExprKind::Assign( + self.cx.expr_deref(self.span, max), + self.cx.expr_tup_field_access(self.span, layout.clone(), 1), + )); + let assign_max = self.cx.stmt_semi(assign_max); + + let stmts = vec![stmt, assign_min, assign_max]; + let block = self.cx.block(self.span, stmts); + let ty_unit = self.cx.ty(self.span, TyKind::Tup(Vec::new())); + (ty_unit, self.cx.expr_block(block)) + } + + AllocatorTy::ResultExcess => { + // We're creating: + // + // match #expr { + // Ok(ptr) => { + // *excess = ptr.1; + // ptr.0 + // } + // Err(e) => { + // ptr::write(err_ptr, e); + // 0 as *mut u8 + // } + // } + + let excess_ptr = ident(); + args.push(self.cx.arg(self.span, excess_ptr, self.ptr_usize())); + let excess_ptr = self.cx.expr_ident(self.span, excess_ptr); + + let err_ptr = ident(); + args.push(self.cx.arg(self.span, err_ptr, self.ptr_u8())); + let err_ptr = self.cx.expr_ident(self.span, err_ptr); + let err_ptr = self.cx.expr_cast(self.span, + err_ptr, + self.alloc_err_ptr()); + + let name = ident(); + let ok_expr = { + let ptr = self.cx.expr_ident(self.span, name); + let write = self.cx.expr(self.span, ExprKind::Assign( + self.cx.expr_deref(self.span, excess_ptr), + self.cx.expr_tup_field_access(self.span, ptr.clone(), 1), + )); + let write = self.cx.stmt_semi(write); + let ret = self.cx.expr_tup_field_access(self.span, + ptr.clone(), + 0); + let ret = self.cx.stmt_expr(ret); + let block = self.cx.block(self.span, vec![write, ret]); + self.cx.expr_block(block) + }; + let pat = self.cx.pat_ident(self.span, name); + let ok = self.cx.path_ident(self.span, Ident::from_str("Ok")); + let ok = self.cx.pat_tuple_struct(self.span, ok, vec![pat]); + let ok = self.cx.arm(self.span, vec![ok], ok_expr); + + let name = ident(); + let err_expr = { + let err = self.cx.expr_ident(self.span, name); + let write = self.cx.path(self.span, vec![ + self.alloc, + Ident::from_str("heap"), + Ident::from_str("__core"), + Ident::from_str("ptr"), + Ident::from_str("write"), + ]); + let write = self.cx.expr_path(write); + let write = self.cx.expr_call(self.span, write, + vec![err_ptr, err]); + let write = self.cx.stmt_semi(write); + let null = self.cx.expr_usize(self.span, 0); + let null = self.cx.expr_cast(self.span, null, self.ptr_u8()); + let null = self.cx.stmt_expr(null); + let block = self.cx.block(self.span, vec![write, null]); + self.cx.expr_block(block) + }; + let pat = self.cx.pat_ident(self.span, name); + let err = self.cx.path_ident(self.span, Ident::from_str("Err")); + let err = self.cx.pat_tuple_struct(self.span, err, vec![pat]); + let err = self.cx.arm(self.span, vec![err], err_expr); + + let expr = self.cx.expr_match(self.span, expr, vec![ok, err]); + (self.ptr_u8(), expr) + } + + AllocatorTy::ResultPtr => { + // We're creating: + // + // match #expr { + // Ok(ptr) => ptr, + // Err(e) => { + // ptr::write(err_ptr, e); + // 0 as *mut u8 + // } + // } + + let err_ptr = ident(); + args.push(self.cx.arg(self.span, err_ptr, self.ptr_u8())); + let err_ptr = self.cx.expr_ident(self.span, err_ptr); + let err_ptr = self.cx.expr_cast(self.span, + err_ptr, + self.alloc_err_ptr()); + + let name = ident(); + let ok_expr = self.cx.expr_ident(self.span, name); + let pat = self.cx.pat_ident(self.span, name); + let ok = self.cx.path_ident(self.span, Ident::from_str("Ok")); + let ok = self.cx.pat_tuple_struct(self.span, ok, vec![pat]); + let ok = self.cx.arm(self.span, vec![ok], ok_expr); + + let name = ident(); + let err_expr = { + let err = self.cx.expr_ident(self.span, name); + let write = self.cx.path(self.span, vec![ + self.alloc, + Ident::from_str("heap"), + Ident::from_str("__core"), + Ident::from_str("ptr"), + Ident::from_str("write"), + ]); + let write = self.cx.expr_path(write); + let write = self.cx.expr_call(self.span, write, + vec![err_ptr, err]); + let write = self.cx.stmt_semi(write); + let null = self.cx.expr_usize(self.span, 0); + let null = self.cx.expr_cast(self.span, null, self.ptr_u8()); + let null = self.cx.stmt_expr(null); + let block = self.cx.block(self.span, vec![write, null]); + self.cx.expr_block(block) + }; + let pat = self.cx.pat_ident(self.span, name); + let err = self.cx.path_ident(self.span, Ident::from_str("Err")); + let err = self.cx.pat_tuple_struct(self.span, err, vec![pat]); + let err = self.cx.arm(self.span, vec![err], err_expr); + + let expr = self.cx.expr_match(self.span, expr, vec![ok, err]); + (self.ptr_u8(), expr) + } + + AllocatorTy::ResultUnit => { + // We're creating: + // + // #expr.is_ok() as u8 + + let cast = self.cx.expr_method_call( + self.span, + expr, + Ident::from_str("is_ok"), + Vec::new() + ); + let u8 = self.cx.path_ident(self.span, Ident::from_str("u8")); + let u8 = self.cx.ty_path(u8); + let cast = self.cx.expr_cast(self.span, cast, u8.clone()); + (u8, cast) + } + + AllocatorTy::Bang => { + (self.cx.ty(self.span, TyKind::Never), expr) + } + + AllocatorTy::Unit => { + (self.cx.ty(self.span, TyKind::Tup(Vec::new())), expr) + } + + AllocatorTy::AllocErr | + AllocatorTy::Layout | + AllocatorTy::LayoutRef | + AllocatorTy::Ptr => { + panic!("can't convert AllocatorTy to an output") + } + } + } + + fn ptr_u8(&self) -> P { + let u8 = self.cx.path_ident(self.span, Ident::from_str("u8")); + let ty_u8 = self.cx.ty_path(u8); + self.cx.ty_ptr(self.span, ty_u8, Mutability::Mutable) + } + + fn ptr_usize(&self) -> P { + let usize = self.cx.path_ident(self.span, Ident::from_str("usize")); + let ty_usize = self.cx.ty_path(usize); + self.cx.ty_ptr(self.span, ty_usize, Mutability::Mutable) + } + + fn layout_ptr(&self) -> P { + let layout = self.cx.path(self.span, vec![ + self.alloc, + Ident::from_str("heap"), + Ident::from_str("Layout"), + ]); + let layout = self.cx.ty_path(layout); + self.cx.ty_ptr(self.span, layout, Mutability::Mutable) + } + + fn alloc_err_ptr(&self) -> P { + let err = self.cx.path(self.span, vec![ + self.alloc, + Ident::from_str("heap"), + Ident::from_str("AllocErr"), + ]); + let err = self.cx.ty_path(err); + self.cx.ty_ptr(self.span, err, Mutability::Mutable) + } +} diff --git a/src/librustc_allocator/lib.rs b/src/librustc_allocator/lib.rs new file mode 100644 index 00000000000..d0ea40d1e36 --- /dev/null +++ b/src/librustc_allocator/lib.rs @@ -0,0 +1,101 @@ +// Copyright 2016 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(rustc_private)] + +extern crate rustc; +extern crate rustc_errors; +extern crate syntax; +extern crate syntax_pos; + +pub mod expand; + +pub static ALLOCATOR_METHODS: &[AllocatorMethod] = &[ + AllocatorMethod { + name: "alloc", + inputs: &[AllocatorTy::Layout], + output: AllocatorTy::ResultPtr, + is_unsafe: true, + }, + AllocatorMethod { + name: "oom", + inputs: &[AllocatorTy::AllocErr], + output: AllocatorTy::Bang, + is_unsafe: false, + }, + AllocatorMethod { + name: "dealloc", + inputs: &[AllocatorTy::Ptr, AllocatorTy::Layout], + output: AllocatorTy::Unit, + is_unsafe: true, + }, + AllocatorMethod { + name: "usable_size", + inputs: &[AllocatorTy::LayoutRef], + output: AllocatorTy::UsizePair, + is_unsafe: false, + }, + AllocatorMethod { + name: "realloc", + inputs: &[AllocatorTy::Ptr, AllocatorTy::Layout, AllocatorTy::Layout], + output: AllocatorTy::ResultPtr, + is_unsafe: true, + }, + AllocatorMethod { + name: "alloc_zeroed", + inputs: &[AllocatorTy::Layout], + output: AllocatorTy::ResultPtr, + is_unsafe: true, + }, + AllocatorMethod { + name: "alloc_excess", + inputs: &[AllocatorTy::Layout], + output: AllocatorTy::ResultExcess, + is_unsafe: true, + }, + AllocatorMethod { + name: "realloc_excess", + inputs: &[AllocatorTy::Ptr, AllocatorTy::Layout, AllocatorTy::Layout], + output: AllocatorTy::ResultExcess, + is_unsafe: true, + }, + AllocatorMethod { + name: "grow_in_place", + inputs: &[AllocatorTy::Ptr, AllocatorTy::Layout, AllocatorTy::Layout], + output: AllocatorTy::ResultUnit, + is_unsafe: true, + }, + AllocatorMethod { + name: "shrink_in_place", + inputs: &[AllocatorTy::Ptr, AllocatorTy::Layout, AllocatorTy::Layout], + output: AllocatorTy::ResultUnit, + is_unsafe: true, + }, +]; + +pub struct AllocatorMethod { + pub name: &'static str, + pub inputs: &'static [AllocatorTy], + pub output: AllocatorTy, + pub is_unsafe: bool, +} + +pub enum AllocatorTy { + AllocErr, + Bang, + Layout, + LayoutRef, + Ptr, + ResultExcess, + ResultPtr, + ResultUnit, + Unit, + UsizePair, +} diff --git a/src/librustc_asan/Cargo.toml b/src/librustc_asan/Cargo.toml index 0fda2805fea..8f8ef1cc4a0 100644 --- a/src/librustc_asan/Cargo.toml +++ b/src/librustc_asan/Cargo.toml @@ -14,5 +14,6 @@ build_helper = { path = "../build_helper" } cmake = "0.1.18" [dependencies] +alloc = { path = "../liballoc" } alloc_system = { path = "../liballoc_system" } core = { path = "../libcore" } diff --git a/src/librustc_asan/lib.rs b/src/librustc_asan/lib.rs index 54941362e84..e987b1f335e 100644 --- a/src/librustc_asan/lib.rs +++ b/src/librustc_asan/lib.rs @@ -11,6 +11,8 @@ #![sanitizer_runtime] #![feature(sanitizer_runtime)] #![feature(alloc_system)] +#![cfg_attr(not(stage0), feature(allocator_api))] +#![cfg_attr(not(stage0), feature(global_allocator))] #![feature(staged_api)] #![no_std] #![unstable(feature = "sanitizer_runtime_lib", @@ -18,3 +20,10 @@ issue = "0")] extern crate alloc_system; + +#[cfg(not(stage0))] +use alloc_system::System; + +#[cfg(not(stage0))] +#[global_allocator] +static ALLOC: System = System; diff --git a/src/librustc_back/target/aarch64_unknown_freebsd.rs b/src/librustc_back/target/aarch64_unknown_freebsd.rs index c5cfff0be03..c5427a13e4c 100644 --- a/src/librustc_back/target/aarch64_unknown_freebsd.rs +++ b/src/librustc_back/target/aarch64_unknown_freebsd.rs @@ -16,7 +16,7 @@ pub fn target() -> TargetResult { base.max_atomic_width = Some(128); // see #36994 - base.exe_allocation_crate = "alloc_system".to_string(); + base.exe_allocation_crate = None; Ok(Target { llvm_target: "aarch64-unknown-freebsd".to_string(), diff --git a/src/librustc_back/target/aarch64_unknown_linux_gnu.rs b/src/librustc_back/target/aarch64_unknown_linux_gnu.rs index 043bd881c72..7c2c45a2843 100644 --- a/src/librustc_back/target/aarch64_unknown_linux_gnu.rs +++ b/src/librustc_back/target/aarch64_unknown_linux_gnu.rs @@ -16,7 +16,7 @@ pub fn target() -> TargetResult { base.max_atomic_width = Some(128); // see #36994 - base.exe_allocation_crate = "alloc_system".to_string(); + base.exe_allocation_crate = None; Ok(Target { llvm_target: "aarch64-unknown-linux-gnu".to_string(), diff --git a/src/librustc_back/target/bitrig_base.rs b/src/librustc_back/target/bitrig_base.rs index 62418e68d43..5c4e01886a4 100644 --- a/src/librustc_back/target/bitrig_base.rs +++ b/src/librustc_back/target/bitrig_base.rs @@ -19,7 +19,6 @@ pub fn opts() -> TargetOptions { linker_is_gnu: true, has_rpath: true, position_independent_executables: true, - exe_allocation_crate: "alloc_system".to_string(), .. Default::default() } diff --git a/src/librustc_back/target/fuchsia_base.rs b/src/librustc_back/target/fuchsia_base.rs index c6207cdc4d9..63ccd21c220 100644 --- a/src/librustc_back/target/fuchsia_base.rs +++ b/src/librustc_back/target/fuchsia_base.rs @@ -37,7 +37,6 @@ pub fn opts() -> TargetOptions { has_rpath: true, pre_link_args: args, position_independent_executables: true, - exe_allocation_crate: "alloc_system".to_string(), has_elf_tls: true, .. Default::default() } diff --git a/src/librustc_back/target/mips64_unknown_linux_gnuabi64.rs b/src/librustc_back/target/mips64_unknown_linux_gnuabi64.rs index 038a70ed6b1..2d779020461 100644 --- a/src/librustc_back/target/mips64_unknown_linux_gnuabi64.rs +++ b/src/librustc_back/target/mips64_unknown_linux_gnuabi64.rs @@ -29,7 +29,7 @@ pub fn target() -> TargetResult { max_atomic_width: Some(64), // see #36994 - exe_allocation_crate: "alloc_system".to_string(), + exe_allocation_crate: None, ..super::linux_base::opts() }, diff --git a/src/librustc_back/target/mips64el_unknown_linux_gnuabi64.rs b/src/librustc_back/target/mips64el_unknown_linux_gnuabi64.rs index aed4c4fbb08..c26780b9e65 100644 --- a/src/librustc_back/target/mips64el_unknown_linux_gnuabi64.rs +++ b/src/librustc_back/target/mips64el_unknown_linux_gnuabi64.rs @@ -29,7 +29,7 @@ pub fn target() -> TargetResult { max_atomic_width: Some(64), // see #36994 - exe_allocation_crate: "alloc_system".to_string(), + exe_allocation_crate: None, ..super::linux_base::opts() }, diff --git a/src/librustc_back/target/mips_unknown_linux_gnu.rs b/src/librustc_back/target/mips_unknown_linux_gnu.rs index 9ef61f9cadd..24649851d76 100644 --- a/src/librustc_back/target/mips_unknown_linux_gnu.rs +++ b/src/librustc_back/target/mips_unknown_linux_gnu.rs @@ -28,7 +28,7 @@ pub fn target() -> TargetResult { max_atomic_width: Some(32), // see #36994 - exe_allocation_crate: "alloc_system".to_string(), + exe_allocation_crate: None, ..super::linux_base::opts() }, diff --git a/src/librustc_back/target/mips_unknown_linux_musl.rs b/src/librustc_back/target/mips_unknown_linux_musl.rs index f54790bab97..6303722945c 100644 --- a/src/librustc_back/target/mips_unknown_linux_musl.rs +++ b/src/librustc_back/target/mips_unknown_linux_musl.rs @@ -28,7 +28,7 @@ pub fn target() -> TargetResult { max_atomic_width: Some(32), // see #36994 - exe_allocation_crate: "alloc_system".to_string(), + exe_allocation_crate: None, ..super::linux_base::opts() } diff --git a/src/librustc_back/target/mips_unknown_linux_uclibc.rs b/src/librustc_back/target/mips_unknown_linux_uclibc.rs index 59c07efe0fd..1a7a56a9779 100644 --- a/src/librustc_back/target/mips_unknown_linux_uclibc.rs +++ b/src/librustc_back/target/mips_unknown_linux_uclibc.rs @@ -28,7 +28,7 @@ pub fn target() -> TargetResult { max_atomic_width: Some(32), // see #36994 - exe_allocation_crate: "alloc_system".to_string(), + exe_allocation_crate: None, ..super::linux_base::opts() }, diff --git a/src/librustc_back/target/mipsel_unknown_linux_gnu.rs b/src/librustc_back/target/mipsel_unknown_linux_gnu.rs index ec19cc1a536..cbf8339993c 100644 --- a/src/librustc_back/target/mipsel_unknown_linux_gnu.rs +++ b/src/librustc_back/target/mipsel_unknown_linux_gnu.rs @@ -29,7 +29,7 @@ pub fn target() -> TargetResult { max_atomic_width: Some(32), // see #36994 - exe_allocation_crate: "alloc_system".to_string(), + exe_allocation_crate: None, ..super::linux_base::opts() }, diff --git a/src/librustc_back/target/mipsel_unknown_linux_musl.rs b/src/librustc_back/target/mipsel_unknown_linux_musl.rs index 00085d18e6d..b367bce75a1 100644 --- a/src/librustc_back/target/mipsel_unknown_linux_musl.rs +++ b/src/librustc_back/target/mipsel_unknown_linux_musl.rs @@ -28,7 +28,7 @@ pub fn target() -> TargetResult { max_atomic_width: Some(32), // see #36994 - exe_allocation_crate: "alloc_system".to_string(), + exe_allocation_crate: None, ..super::linux_base::opts() } diff --git a/src/librustc_back/target/mipsel_unknown_linux_uclibc.rs b/src/librustc_back/target/mipsel_unknown_linux_uclibc.rs index b3ca2edec1e..686dfbe987d 100644 --- a/src/librustc_back/target/mipsel_unknown_linux_uclibc.rs +++ b/src/librustc_back/target/mipsel_unknown_linux_uclibc.rs @@ -29,7 +29,7 @@ pub fn target() -> TargetResult { max_atomic_width: Some(32), // see #36994 - exe_allocation_crate: "alloc_system".to_string(), + exe_allocation_crate: None, ..super::linux_base::opts() }, diff --git a/src/librustc_back/target/mod.rs b/src/librustc_back/target/mod.rs index 5df227e39ac..983a98e350c 100644 --- a/src/librustc_back/target/mod.rs +++ b/src/librustc_back/target/mod.rs @@ -378,9 +378,8 @@ pub struct TargetOptions { /// `eh_unwind_resume` lang item. pub custom_unwind_resume: bool, - /// Default crate for allocation symbols to link against - pub lib_allocation_crate: String, - pub exe_allocation_crate: String, + /// If necessary, a different crate to link exe allocators by default + pub exe_allocation_crate: Option, /// Flag indicating whether ELF TLS (e.g. #[thread_local]) is available for /// this target. @@ -457,8 +456,7 @@ fn default() -> TargetOptions { link_env: Vec::new(), archive_format: "gnu".to_string(), custom_unwind_resume: false, - lib_allocation_crate: "alloc_system".to_string(), - exe_allocation_crate: "alloc_system".to_string(), + exe_allocation_crate: None, allow_asm: true, has_elf_tls: false, obj_is_bitcode: false, @@ -682,8 +680,7 @@ macro_rules! key { key!(archive_format); key!(allow_asm, bool); key!(custom_unwind_resume, bool); - key!(lib_allocation_crate); - key!(exe_allocation_crate); + key!(exe_allocation_crate, optional); key!(has_elf_tls, bool); key!(obj_is_bitcode, bool); key!(no_integrated_as, bool); @@ -869,7 +866,6 @@ macro_rules! target_option_val { target_option_val!(archive_format); target_option_val!(allow_asm); target_option_val!(custom_unwind_resume); - target_option_val!(lib_allocation_crate); target_option_val!(exe_allocation_crate); target_option_val!(has_elf_tls); target_option_val!(obj_is_bitcode); @@ -889,10 +885,10 @@ macro_rules! target_option_val { } } -fn maybe_jemalloc() -> String { +fn maybe_jemalloc() -> Option { if cfg!(feature = "jemalloc") { - "alloc_jemalloc".to_string() + Some("alloc_jemalloc".to_string()) } else { - "alloc_system".to_string() + None } } diff --git a/src/librustc_back/target/openbsd_base.rs b/src/librustc_back/target/openbsd_base.rs index 2df9b8e03ff..051028d5c4a 100644 --- a/src/librustc_back/target/openbsd_base.rs +++ b/src/librustc_back/target/openbsd_base.rs @@ -34,7 +34,6 @@ pub fn opts() -> TargetOptions { is_like_openbsd: true, pre_link_args: args, position_independent_executables: true, - exe_allocation_crate: "alloc_system".to_string(), .. Default::default() } } diff --git a/src/librustc_back/target/powerpc64_unknown_linux_gnu.rs b/src/librustc_back/target/powerpc64_unknown_linux_gnu.rs index 55a5bfd1e67..718a79a685e 100644 --- a/src/librustc_back/target/powerpc64_unknown_linux_gnu.rs +++ b/src/librustc_back/target/powerpc64_unknown_linux_gnu.rs @@ -18,7 +18,7 @@ pub fn target() -> TargetResult { base.max_atomic_width = Some(64); // see #36994 - base.exe_allocation_crate = "alloc_system".to_string(); + base.exe_allocation_crate = None; Ok(Target { llvm_target: "powerpc64-unknown-linux-gnu".to_string(), diff --git a/src/librustc_back/target/powerpc64le_unknown_linux_gnu.rs b/src/librustc_back/target/powerpc64le_unknown_linux_gnu.rs index c22bc3b041a..5b50b96837f 100644 --- a/src/librustc_back/target/powerpc64le_unknown_linux_gnu.rs +++ b/src/librustc_back/target/powerpc64le_unknown_linux_gnu.rs @@ -18,7 +18,7 @@ pub fn target() -> TargetResult { base.max_atomic_width = Some(64); // see #36994 - base.exe_allocation_crate = "alloc_system".to_string(); + base.exe_allocation_crate = None; Ok(Target { llvm_target: "powerpc64le-unknown-linux-gnu".to_string(), diff --git a/src/librustc_back/target/powerpc_unknown_linux_gnu.rs b/src/librustc_back/target/powerpc_unknown_linux_gnu.rs index 677d198b1a3..8d4ad5f0b44 100644 --- a/src/librustc_back/target/powerpc_unknown_linux_gnu.rs +++ b/src/librustc_back/target/powerpc_unknown_linux_gnu.rs @@ -17,7 +17,7 @@ pub fn target() -> TargetResult { base.max_atomic_width = Some(32); // see #36994 - base.exe_allocation_crate = "alloc_system".to_string(); + base.exe_allocation_crate = None; Ok(Target { llvm_target: "powerpc-unknown-linux-gnu".to_string(), diff --git a/src/librustc_back/target/redox_base.rs b/src/librustc_back/target/redox_base.rs index f26a86d4bdc..2eae0a12408 100644 --- a/src/librustc_back/target/redox_base.rs +++ b/src/librustc_back/target/redox_base.rs @@ -36,8 +36,6 @@ pub fn opts() -> TargetOptions { eliminate_frame_pointer: false, target_family: None, linker_is_gnu: true, - lib_allocation_crate: "alloc_system".to_string(), - exe_allocation_crate: "alloc_system".to_string(), has_elf_tls: true, panic_strategy: PanicStrategy::Abort, .. Default::default() diff --git a/src/librustc_back/target/s390x_unknown_linux_gnu.rs b/src/librustc_back/target/s390x_unknown_linux_gnu.rs index cc8eb7c4e84..78a6bb7933d 100644 --- a/src/librustc_back/target/s390x_unknown_linux_gnu.rs +++ b/src/librustc_back/target/s390x_unknown_linux_gnu.rs @@ -21,7 +21,7 @@ pub fn target() -> TargetResult { base.features = "-vector".to_string(); base.max_atomic_width = Some(64); // see #36994 - base.exe_allocation_crate = "alloc_system".to_string(); + base.exe_allocation_crate = None; Ok(Target { llvm_target: "s390x-unknown-linux-gnu".to_string(), diff --git a/src/librustc_back/target/sparc64_unknown_linux_gnu.rs b/src/librustc_back/target/sparc64_unknown_linux_gnu.rs index 1bd51ac6258..7f710ad4020 100644 --- a/src/librustc_back/target/sparc64_unknown_linux_gnu.rs +++ b/src/librustc_back/target/sparc64_unknown_linux_gnu.rs @@ -15,7 +15,7 @@ pub fn target() -> TargetResult { let mut base = super::linux_base::opts(); base.cpu = "v9".to_string(); base.max_atomic_width = Some(64); - base.exe_allocation_crate = "alloc_system".to_string(); + base.exe_allocation_crate = None; Ok(Target { llvm_target: "sparc64-unknown-linux-gnu".to_string(), diff --git a/src/librustc_back/target/windows_msvc_base.rs b/src/librustc_back/target/windows_msvc_base.rs index 421f59aea93..c07321e418e 100644 --- a/src/librustc_back/target/windows_msvc_base.rs +++ b/src/librustc_back/target/windows_msvc_base.rs @@ -63,7 +63,6 @@ pub fn opts() -> TargetOptions { is_like_windows: true, is_like_msvc: true, pre_link_args: args, - exe_allocation_crate: "alloc_system".to_string(), .. Default::default() } diff --git a/src/librustc_back/target/x86_64_rumprun_netbsd.rs b/src/librustc_back/target/x86_64_rumprun_netbsd.rs index eea4389cfd6..ec5cc197dfc 100644 --- a/src/librustc_back/target/x86_64_rumprun_netbsd.rs +++ b/src/librustc_back/target/x86_64_rumprun_netbsd.rs @@ -24,7 +24,7 @@ pub fn target() -> TargetResult { base.position_independent_executables = false; base.disable_redzone = true; base.no_default_libraries = false; - base.exe_allocation_crate = "alloc_system".to_string(); + base.exe_allocation_crate = None; Ok(Target { llvm_target: "x86_64-rumprun-netbsd".to_string(), diff --git a/src/librustc_driver/Cargo.toml b/src/librustc_driver/Cargo.toml index 2e949f48c17..def72752389 100644 --- a/src/librustc_driver/Cargo.toml +++ b/src/librustc_driver/Cargo.toml @@ -15,6 +15,7 @@ log = { version = "0.3", features = ["release_max_level_info"] } env_logger = { version = "0.4", default-features = false } proc_macro_plugin = { path = "../libproc_macro_plugin" } rustc = { path = "../librustc" } +rustc_allocator = { path = "../librustc_allocator" } rustc_back = { path = "../librustc_back" } rustc_borrowck = { path = "../librustc_borrowck" } rustc_const_eval = { path = "../librustc_const_eval" } diff --git a/src/librustc_driver/driver.rs b/src/librustc_driver/driver.rs index a3e1cf7c1a8..daa5917cf32 100644 --- a/src/librustc_driver/driver.rs +++ b/src/librustc_driver/driver.rs @@ -27,6 +27,7 @@ use rustc::util::common::{ErrorReported, time}; use rustc::util::nodemap::NodeSet; use rustc::util::fs::rename_or_copy_remove; +use rustc_allocator as allocator; use rustc_borrowck as borrowck; use rustc_incremental::{self, IncrementalHashesMap}; use rustc_resolve::{MakeGlobMap, Resolver}; @@ -750,6 +751,13 @@ pub fn phase_2_configure_and_expand(sess: &Session, }); } + krate = time(time_passes, "creating allocators", || { + allocator::expand::modify(&sess.parse_sess, + &mut resolver, + krate, + sess.diagnostic()) + }); + after_expand(&krate)?; if sess.opts.debugging_opts.input_stats { diff --git a/src/librustc_driver/lib.rs b/src/librustc_driver/lib.rs index f2aacbc629f..add827536c8 100644 --- a/src/librustc_driver/lib.rs +++ b/src/librustc_driver/lib.rs @@ -34,6 +34,7 @@ extern crate env_logger; extern crate libc; extern crate rustc; +extern crate rustc_allocator; extern crate rustc_back; extern crate rustc_borrowck; extern crate rustc_const_eval; diff --git a/src/librustc_lint/builtin.rs b/src/librustc_lint/builtin.rs index 2b331509025..02d68a41b4c 100644 --- a/src/librustc_lint/builtin.rs +++ b/src/librustc_lint/builtin.rs @@ -1071,7 +1071,8 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for InvalidNoMangleItems { fn check_item(&mut self, cx: &LateContext, it: &hir::Item) { match it.node { hir::ItemFn(.., ref generics, _) => { - if attr::contains_name(&it.attrs, "no_mangle") { + if attr::contains_name(&it.attrs, "no_mangle") && + !attr::contains_name(&it.attrs, "linkage") { if !cx.access_levels.is_reachable(it.id) { let msg = format!("function {} is marked #[no_mangle], but not exported", it.name); diff --git a/src/librustc_llvm/ffi.rs b/src/librustc_llvm/ffi.rs index 770d16e5c02..9f0ee95b5a6 100644 --- a/src/librustc_llvm/ffi.rs +++ b/src/librustc_llvm/ffi.rs @@ -698,6 +698,7 @@ pub fn LLVMConstInlineAsm(Ty: TypeRef, pub fn LLVMIsGlobalConstant(GlobalVar: ValueRef) -> Bool; pub fn LLVMSetGlobalConstant(GlobalVar: ValueRef, IsConstant: Bool); pub fn LLVMRustGetNamedValue(M: ModuleRef, Name: *const c_char) -> ValueRef; + pub fn LLVMSetTailCall(CallInst: ValueRef, IsTailCall: Bool); // Operations on functions pub fn LLVMAddFunction(M: ModuleRef, Name: *const c_char, FunctionTy: TypeRef) -> ValueRef; diff --git a/src/librustc_lsan/Cargo.toml b/src/librustc_lsan/Cargo.toml index 786883d2135..087c3162119 100644 --- a/src/librustc_lsan/Cargo.toml +++ b/src/librustc_lsan/Cargo.toml @@ -14,5 +14,6 @@ build_helper = { path = "../build_helper" } cmake = "0.1.18" [dependencies] +alloc = { path = "../liballoc" } alloc_system = { path = "../liballoc_system" } core = { path = "../libcore" } diff --git a/src/librustc_lsan/lib.rs b/src/librustc_lsan/lib.rs index 54941362e84..e987b1f335e 100644 --- a/src/librustc_lsan/lib.rs +++ b/src/librustc_lsan/lib.rs @@ -11,6 +11,8 @@ #![sanitizer_runtime] #![feature(sanitizer_runtime)] #![feature(alloc_system)] +#![cfg_attr(not(stage0), feature(allocator_api))] +#![cfg_attr(not(stage0), feature(global_allocator))] #![feature(staged_api)] #![no_std] #![unstable(feature = "sanitizer_runtime_lib", @@ -18,3 +20,10 @@ issue = "0")] extern crate alloc_system; + +#[cfg(not(stage0))] +use alloc_system::System; + +#[cfg(not(stage0))] +#[global_allocator] +static ALLOC: System = System; diff --git a/src/librustc_metadata/creader.rs b/src/librustc_metadata/creader.rs index 27c2d22168c..ac39da48ac1 100644 --- a/src/librustc_metadata/creader.rs +++ b/src/librustc_metadata/creader.rs @@ -16,6 +16,7 @@ use rustc::hir::def_id::{CrateNum, DefIndex}; use rustc::hir::svh::Svh; +use rustc::middle::allocator::AllocatorKind; use rustc::middle::cstore::DepKind; use rustc::session::Session; use rustc::session::config::{Sanitizer, self}; @@ -40,6 +41,7 @@ use syntax::ext::base::SyntaxExtension; use syntax::feature_gate::{self, GateIssue}; use syntax::symbol::Symbol; +use syntax::visit; use syntax_pos::{Span, DUMMY_SP}; use log; @@ -920,34 +922,28 @@ fn inject_profiler_runtime(&mut self) { } } - fn inject_allocator_crate(&mut self) { - // Make sure that we actually need an allocator, if none of our - // dependencies need one then we definitely don't! - // - // Also, if one of our dependencies has an explicit allocator, then we - // also bail out as we don't need to implicitly inject one. - let mut needs_allocator = false; - let mut found_required_allocator = false; + fn inject_allocator_crate(&mut self, krate: &ast::Crate) { + let has_global_allocator = has_global_allocator(krate); + if has_global_allocator { + self.sess.has_global_allocator.set(true); + } + + // Check to see if we actually need an allocator. This desire comes + // about through the `#![needs_allocator]` attribute and is typically + // written down in liballoc. + let mut needs_allocator = attr::contains_name(&krate.attrs, + "needs_allocator"); let dep_graph = &self.sess.dep_graph; - self.cstore.iter_crate_data(|cnum, data| { + self.cstore.iter_crate_data(|_, data| { needs_allocator = needs_allocator || data.needs_allocator(dep_graph); - if data.is_allocator(dep_graph) { - info!("{} required by rlib and is an allocator", data.name()); - self.inject_dependency_if(cnum, "an allocator", - &|data| data.needs_allocator(dep_graph)); - found_required_allocator = found_required_allocator || - data.dep_kind.get() == DepKind::Explicit; - } }); - if !needs_allocator || found_required_allocator { return } + if !needs_allocator { + return + } - // At this point we've determined that we need an allocator and no - // previous allocator has been activated. We look through our outputs of - // crate types to see what kind of allocator types we may need. - // - // The main special output type here is that rlibs do **not** need an - // allocator linked in (they're just object files), only final products - // (exes, dylibs, staticlibs) need allocators. + // At this point we've determined that we need an allocator. Let's see + // if our compilation session actually needs an allocator based on what + // we're emitting. let mut need_lib_alloc = false; let mut need_exe_alloc = false; for ct in self.sess.crate_types.borrow().iter() { @@ -960,44 +956,132 @@ fn inject_allocator_crate(&mut self) { config::CrateTypeRlib => {} } } - if !need_lib_alloc && !need_exe_alloc { return } + if !need_lib_alloc && !need_exe_alloc { + return + } - // The default allocator crate comes from the custom target spec, and we - // choose between the standard library allocator or exe allocator. This - // distinction exists because the default allocator for binaries (where - // the world is Rust) is different than library (where the world is - // likely *not* Rust). - // - // If a library is being produced, but we're also flagged with `-C - // prefer-dynamic`, then we interpret this as a *Rust* dynamic library - // is being produced so we use the exe allocator instead. + // Ok, we need an allocator. Not only that but we're actually going to + // create an artifact that needs one linked in. Let's go find the one + // that we're going to link in. // - // What this boils down to is: - // - // * Binaries use jemalloc - // * Staticlibs and Rust dylibs use system malloc - // * Rust dylibs used as dependencies to rust use jemalloc - let name = if need_lib_alloc && !self.sess.opts.cg.prefer_dynamic { - Symbol::intern(&self.sess.target.target.options.lib_allocation_crate) + // First up we check for global allocators. Look at the crate graph here + // and see what's a global allocator, including if we ourselves are a + // global allocator. + let dep_graph = &self.sess.dep_graph; + let mut global_allocator = if has_global_allocator { + Some(None) } else { - Symbol::intern(&self.sess.target.target.options.exe_allocation_crate) + None }; - let dep_kind = DepKind::Implicit; - let (cnum, data) = - self.resolve_crate(&None, name, name, None, DUMMY_SP, PathKind::Crate, dep_kind); + self.cstore.iter_crate_data(|_, data| { + if !data.has_global_allocator(dep_graph) { + return + } + match global_allocator { + Some(Some(other_crate)) => { + self.sess.err(&format!("the #[global_allocator] in {} \ + conflicts with this global \ + allocator in: {}", + other_crate, + data.name())); + } + Some(None) => { + self.sess.err(&format!("the #[global_allocator] in this \ + crate conflicts with global \ + allocator in: {}", data.name())); + } + None => global_allocator = Some(Some(data.name())), + } + }); + if global_allocator.is_some() { + self.sess.allocator_kind.set(Some(AllocatorKind::Global)); + return + } + + // Ok we haven't found a global allocator but we still need an + // allocator. At this point we'll either fall back to the "library + // allocator" or the "exe allocator" depending on a few variables. Let's + // figure out which one. + // + // Note that here we favor linking to the "library allocator" as much as + // possible. If we're not creating rustc's version of libstd + // (need_lib_alloc and prefer_dynamic) then we select `None`, and if the + // exe allocation crate doesn't exist for this target then we also + // select `None`. + let exe_allocation_crate = + if need_lib_alloc && !self.sess.opts.cg.prefer_dynamic { + None + } else { + self.sess.target.target.options.exe_allocation_crate.as_ref() + }; + + match exe_allocation_crate { + // We've determined that we're injecting an "exe allocator" which + // means that we're going to load up a whole new crate. An example + // of this is that we're producing a normal binary on Linux which + // means we need to load the `alloc_jemalloc` crate to link as an + // allocator. + Some(krate) => { + self.sess.allocator_kind.set(Some(AllocatorKind::DefaultExe)); + let name = Symbol::intern(krate); + let dep_kind = DepKind::Implicit; + let (cnum, _data) = + self.resolve_crate(&None, + name, + name, + None, + DUMMY_SP, + PathKind::Crate, dep_kind); + self.sess.injected_allocator.set(Some(cnum)); + // self.cstore.iter_crate_data(|_, data| { + // if !data.needs_allocator(dep_graph) { + // return + // } + // data.cnum_map.borrow_mut().push(cnum); + // }); + } - // Sanity check the crate we loaded to ensure that it is indeed an - // allocator. - if !data.is_allocator(dep_graph) { - self.sess.err(&format!("the allocator crate `{}` is not tagged \ - with #![allocator]", data.name())); + // We're not actually going to inject an allocator, we're going to + // require that something in our crate graph is the default lib + // allocator. This is typically libstd, so this'll rarely be an + // error. + None => { + self.sess.allocator_kind.set(Some(AllocatorKind::DefaultLib)); + let mut found_lib_allocator = + attr::contains_name(&krate.attrs, "default_lib_allocator"); + self.cstore.iter_crate_data(|_, data| { + if !found_lib_allocator { + if data.has_default_lib_allocator(dep_graph) { + found_lib_allocator = true; + } + } + }); + if found_lib_allocator { + return + } + self.sess.err("no #[default_lib_allocator] found but one is \ + required; is libstd not linked?"); + } } - self.sess.injected_allocator.set(Some(cnum)); - self.inject_dependency_if(cnum, "an allocator", - &|data| data.needs_allocator(dep_graph)); + fn has_global_allocator(krate: &ast::Crate) -> bool { + struct Finder(bool); + let mut f = Finder(false); + visit::walk_crate(&mut f, krate); + return f.0; + + impl<'ast> visit::Visitor<'ast> for Finder { + fn visit_item(&mut self, i: &'ast ast::Item) { + if attr::contains_name(&i.attrs, "global_allocator") { + self.0 = true; + } + visit::walk_item(self, i) + } + } + } } + fn inject_dependency_if(&self, krate: CrateNum, what: &str, @@ -1123,7 +1207,7 @@ fn postprocess(&mut self, krate: &ast::Crate) { // sanitizers force the use of the `alloc_system` allocator self.inject_sanitizer_runtime(); self.inject_profiler_runtime(); - self.inject_allocator_crate(); + self.inject_allocator_crate(krate); self.inject_panic_runtime(krate); if log_enabled!(log::LogLevel::Info) { diff --git a/src/librustc_metadata/cstore.rs b/src/librustc_metadata/cstore.rs index 86146fe3947..fb43f91c46d 100644 --- a/src/librustc_metadata/cstore.rs +++ b/src/librustc_metadata/cstore.rs @@ -275,16 +275,27 @@ pub fn disambiguator(&self) -> Symbol { self.root.disambiguator } - pub fn is_allocator(&self, dep_graph: &DepGraph) -> bool { - let attrs = self.get_item_attrs(CRATE_DEF_INDEX, dep_graph); - attr::contains_name(&attrs, "allocator") - } - pub fn needs_allocator(&self, dep_graph: &DepGraph) -> bool { let attrs = self.get_item_attrs(CRATE_DEF_INDEX, dep_graph); attr::contains_name(&attrs, "needs_allocator") } + pub fn has_global_allocator(&self, dep_graph: &DepGraph) -> bool { + let dep_node = self.metadata_dep_node(GlobalMetaDataKind::Krate); + self.root + .has_global_allocator + .get(dep_graph, dep_node) + .clone() + } + + pub fn has_default_lib_allocator(&self, dep_graph: &DepGraph) -> bool { + let dep_node = self.metadata_dep_node(GlobalMetaDataKind::Krate); + self.root + .has_default_lib_allocator + .get(dep_graph, dep_node) + .clone() + } + pub fn is_panic_runtime(&self, dep_graph: &DepGraph) -> bool { let attrs = self.get_item_attrs(CRATE_DEF_INDEX, dep_graph); attr::contains_name(&attrs, "panic_runtime") diff --git a/src/librustc_metadata/cstore_impl.rs b/src/librustc_metadata/cstore_impl.rs index 502eab44dac..35ce993d335 100644 --- a/src/librustc_metadata/cstore_impl.rs +++ b/src/librustc_metadata/cstore_impl.rs @@ -134,7 +134,6 @@ pub fn provide<$lt>(providers: &mut Providers<$lt>) { is_mir_available => { cdata.is_item_mir_available(def_id.index) } dylib_dependency_formats => { Rc::new(cdata.get_dylib_dependency_formats(&tcx.dep_graph)) } - is_allocator => { cdata.is_allocator(&tcx.dep_graph) } is_panic_runtime => { cdata.is_panic_runtime(&tcx.dep_graph) } extern_crate => { Rc::new(cdata.extern_crate.get()) } } diff --git a/src/librustc_metadata/encoder.rs b/src/librustc_metadata/encoder.rs index e9701b95002..47f586ac895 100644 --- a/src/librustc_metadata/encoder.rs +++ b/src/librustc_metadata/encoder.rs @@ -400,12 +400,17 @@ fn encode_crate_root(&mut self) -> Lazy { let tcx = self.tcx; let link_meta = self.link_meta; let is_proc_macro = tcx.sess.crate_types.borrow().contains(&CrateTypeProcMacro); + let has_default_lib_allocator = + attr::contains_name(tcx.hir.krate_attrs(), "default_lib_allocator"); + let has_global_allocator = tcx.sess.has_global_allocator.get(); let root = self.lazy(&CrateRoot { name: tcx.crate_name(LOCAL_CRATE), triple: tcx.sess.opts.target_triple.clone(), hash: link_meta.crate_hash, disambiguator: tcx.sess.local_crate_disambiguator(), panic_strategy: Tracked::new(tcx.sess.panic_strategy()), + has_global_allocator: Tracked::new(has_global_allocator), + has_default_lib_allocator: Tracked::new(has_default_lib_allocator), plugin_registrar_fn: tcx.sess .plugin_registrar_fn .get() diff --git a/src/librustc_metadata/schema.rs b/src/librustc_metadata/schema.rs index 9ef5b940830..0b670121ba2 100644 --- a/src/librustc_metadata/schema.rs +++ b/src/librustc_metadata/schema.rs @@ -243,6 +243,8 @@ pub struct CrateRoot { pub hash: hir::svh::Svh, pub disambiguator: Symbol, pub panic_strategy: Tracked, + pub has_global_allocator: Tracked, + pub has_default_lib_allocator: Tracked, pub plugin_registrar_fn: Option, pub macro_derive_registrar: Option, diff --git a/src/librustc_msan/Cargo.toml b/src/librustc_msan/Cargo.toml index b3b70bc0a25..8d7279b29eb 100644 --- a/src/librustc_msan/Cargo.toml +++ b/src/librustc_msan/Cargo.toml @@ -14,5 +14,6 @@ build_helper = { path = "../build_helper" } cmake = "0.1.18" [dependencies] +alloc = { path = "../liballoc" } alloc_system = { path = "../liballoc_system" } core = { path = "../libcore" } diff --git a/src/librustc_msan/lib.rs b/src/librustc_msan/lib.rs index 54941362e84..e987b1f335e 100644 --- a/src/librustc_msan/lib.rs +++ b/src/librustc_msan/lib.rs @@ -11,6 +11,8 @@ #![sanitizer_runtime] #![feature(sanitizer_runtime)] #![feature(alloc_system)] +#![cfg_attr(not(stage0), feature(allocator_api))] +#![cfg_attr(not(stage0), feature(global_allocator))] #![feature(staged_api)] #![no_std] #![unstable(feature = "sanitizer_runtime_lib", @@ -18,3 +20,10 @@ issue = "0")] extern crate alloc_system; + +#[cfg(not(stage0))] +use alloc_system::System; + +#[cfg(not(stage0))] +#[global_allocator] +static ALLOC: System = System; diff --git a/src/librustc_trans/Cargo.toml b/src/librustc_trans/Cargo.toml index a512cf2f02a..c7db2a9a8ae 100644 --- a/src/librustc_trans/Cargo.toml +++ b/src/librustc_trans/Cargo.toml @@ -17,6 +17,7 @@ log = "0.3" owning_ref = "0.3.3" rustc-demangle = "0.1.4" rustc = { path = "../librustc" } +rustc_allocator = { path = "../librustc_allocator" } rustc_back = { path = "../librustc_back" } rustc_bitflags = { path = "../librustc_bitflags" } rustc_const_math = { path = "../librustc_const_math" } diff --git a/src/librustc_trans/allocator.rs b/src/librustc_trans/allocator.rs new file mode 100644 index 00000000000..9abb6d66f9c --- /dev/null +++ b/src/librustc_trans/allocator.rs @@ -0,0 +1,117 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use std::ffi::CString; +use std::ptr; + +use libc::c_uint; +use rustc::middle::allocator::AllocatorKind; +use rustc::ty::TyCtxt; +use rustc_allocator::{ALLOCATOR_METHODS, AllocatorTy}; + +use ModuleLlvm; +use llvm::{self, False, True}; + +pub unsafe fn trans(tcx: TyCtxt, mods: &ModuleLlvm, kind: AllocatorKind) { + let llcx = mods.llcx; + let llmod = mods.llmod; + let usize = match &tcx.sess.target.target.target_pointer_width[..] { + "16" => llvm::LLVMInt16TypeInContext(llcx), + "32" => llvm::LLVMInt32TypeInContext(llcx), + "64" => llvm::LLVMInt64TypeInContext(llcx), + tws => bug!("Unsupported target word size for int: {}", tws), + }; + let i8 = llvm::LLVMInt8TypeInContext(llcx); + let i8p = llvm::LLVMPointerType(i8, 0); + let usizep = llvm::LLVMPointerType(usize, 0); + let void = llvm::LLVMVoidTypeInContext(llcx); + + for method in ALLOCATOR_METHODS { + let mut args = Vec::new(); + for ty in method.inputs.iter() { + match *ty { + AllocatorTy::Layout => { + args.push(usize); // size + args.push(usize); // align + } + AllocatorTy::LayoutRef => args.push(i8p), + AllocatorTy::Ptr => args.push(i8p), + AllocatorTy::AllocErr => args.push(i8p), + + AllocatorTy::Bang | + AllocatorTy::ResultExcess | + AllocatorTy::ResultPtr | + AllocatorTy::ResultUnit | + AllocatorTy::UsizePair | + AllocatorTy::Unit => panic!("invalid allocator arg"), + } + } + let output = match method.output { + AllocatorTy::UsizePair => { + args.push(usizep); // min + args.push(usizep); // max + None + } + AllocatorTy::Bang => None, + AllocatorTy::ResultExcess => { + args.push(i8p); // excess_ptr + args.push(i8p); // err_ptr + Some(i8p) + } + AllocatorTy::ResultPtr => { + args.push(i8p); // err_ptr + Some(i8p) + } + AllocatorTy::ResultUnit => Some(i8), + AllocatorTy::Unit => None, + + AllocatorTy::AllocErr | + AllocatorTy::Layout | + AllocatorTy::LayoutRef | + AllocatorTy::Ptr => panic!("invalid allocator output"), + }; + let ty = llvm::LLVMFunctionType(output.unwrap_or(void), + args.as_ptr(), + args.len() as c_uint, + False); + let name = CString::new(format!("__rust_{}", method.name)).unwrap(); + let llfn = llvm::LLVMRustGetOrInsertFunction(llmod, + name.as_ptr(), + ty); + + let callee = CString::new(kind.fn_name(method.name)).unwrap(); + let callee = llvm::LLVMRustGetOrInsertFunction(llmod, + callee.as_ptr(), + ty); + + let llbb = llvm::LLVMAppendBasicBlockInContext(llcx, + llfn, + "entry\0".as_ptr() as *const _); + + let llbuilder = llvm::LLVMCreateBuilderInContext(llcx); + llvm::LLVMPositionBuilderAtEnd(llbuilder, llbb); + let args = args.iter().enumerate().map(|(i, _)| { + llvm::LLVMGetParam(llfn, i as c_uint) + }).collect::>(); + let ret = llvm::LLVMRustBuildCall(llbuilder, + callee, + args.as_ptr(), + args.len() as c_uint, + ptr::null_mut(), + "\0".as_ptr() as *const _); + llvm::LLVMSetTailCall(ret, True); + if output.is_some() { + llvm::LLVMBuildRet(llbuilder, ret); + } else { + llvm::LLVMBuildRetVoid(llbuilder); + } + llvm::LLVMDisposeBuilder(llbuilder); + } +} diff --git a/src/librustc_trans/back/link.rs b/src/librustc_trans/back/link.rs index a7f205a18a4..6f235ae5ee0 100644 --- a/src/librustc_trans/back/link.rs +++ b/src/librustc_trans/back/link.rs @@ -55,6 +55,10 @@ /// match up with `METADATA_MODULE_NAME`. pub const METADATA_OBJ_NAME: &'static str = "crate.metadata.o"; +// same as for metadata above, but for allocator shim +pub const ALLOCATOR_MODULE_NAME: &'static str = "crate.allocator"; +pub const ALLOCATOR_OBJ_NAME: &'static str = "crate.allocator.o"; + // RLIB LLVM-BYTECODE OBJECT LAYOUT // Version 1 // Bytes Data @@ -240,6 +244,9 @@ pub fn link_binary(sess: &Session, } } remove(sess, &outputs.with_extension(METADATA_OBJ_NAME)); + if trans.allocator_module.is_some() { + remove(sess, &outputs.with_extension(ALLOCATOR_OBJ_NAME)); + } } out_filenames @@ -417,11 +424,21 @@ fn link_binary_output(sess: &Session, let out_filename = out_filename(sess, crate_type, outputs, crate_name); match crate_type { config::CrateTypeRlib => { - link_rlib(sess, Some(trans), &objects, &out_filename, + link_rlib(sess, + trans, + RlibFlavor::Normal, + &objects, + outputs, + &out_filename, tmpdir.path()).build(); } config::CrateTypeStaticlib => { - link_staticlib(sess, &objects, &out_filename, tmpdir.path()); + link_staticlib(sess, + trans, + outputs, + &objects, + &out_filename, + tmpdir.path()); } _ => { link_natively(sess, crate_type, &objects, &out_filename, trans, @@ -477,6 +494,11 @@ fn emit_metadata<'a>(sess: &'a Session, trans: &CrateTranslation, out_filename: } } +enum RlibFlavor { + Normal, + StaticlibBase, +} + // Create an 'rlib' // // An rlib in its current incarnation is essentially a renamed .a file. The @@ -484,8 +506,10 @@ fn emit_metadata<'a>(sess: &'a Session, trans: &CrateTranslation, out_filename: // all of the object files from native libraries. This is done by unzipping // native libraries and inserting all of the contents into this archive. fn link_rlib<'a>(sess: &'a Session, - trans: Option<&CrateTranslation>, // None == no metadata/bytecode + trans: &CrateTranslation, + flavor: RlibFlavor, objects: &[PathBuf], + outputs: &OutputFilenames, out_filename: &Path, tmpdir: &Path) -> ArchiveBuilder<'a> { info!("preparing rlib from {:?} to {:?}", objects, out_filename); @@ -546,8 +570,8 @@ fn link_rlib<'a>(sess: &'a Session, // // Basically, all this means is that this code should not move above the // code above. - match trans { - Some(trans) => { + match flavor { + RlibFlavor::Normal => { // Instead of putting the metadata in an object file section, rlibs // contain the metadata in a separate file. We use a temp directory // here so concurrent builds in the same directory don't try to use @@ -620,7 +644,11 @@ fn link_rlib<'a>(sess: &'a Session, } } - None => {} + RlibFlavor::StaticlibBase => { + if trans.allocator_module.is_some() { + ab.add_file(&outputs.with_extension(ALLOCATOR_OBJ_NAME)); + } + } } ab @@ -672,9 +700,19 @@ fn write_rlib_bytecode_object_v1(writer: &mut Write, // There's no need to include metadata in a static archive, so ensure to not // link in the metadata object file (and also don't prepare the archive with a // metadata file). -fn link_staticlib(sess: &Session, objects: &[PathBuf], out_filename: &Path, +fn link_staticlib(sess: &Session, + trans: &CrateTranslation, + outputs: &OutputFilenames, + objects: &[PathBuf], + out_filename: &Path, tempdir: &Path) { - let mut ab = link_rlib(sess, None, objects, out_filename, tempdir); + let mut ab = link_rlib(sess, + trans, + RlibFlavor::StaticlibBase, + objects, + outputs, + out_filename, + tempdir); let mut all_native_libs = vec![]; let res = each_linked_rlib(sess, &mut |cnum, path| { @@ -944,6 +982,10 @@ fn link_args(cmd: &mut Linker, cmd.add_object(&outputs.with_extension(METADATA_OBJ_NAME)); } + if trans.allocator_module.is_some() { + cmd.add_object(&outputs.with_extension(ALLOCATOR_OBJ_NAME)); + } + // Try to strip as much out of the generated object by removing unused // sections if possible. See more comments in linker.rs if !sess.opts.cg.link_dead_code { diff --git a/src/librustc_trans/back/symbol_export.rs b/src/librustc_trans/back/symbol_export.rs index b38dc188389..52fe747858c 100644 --- a/src/librustc_trans/back/symbol_export.rs +++ b/src/librustc_trans/back/symbol_export.rs @@ -92,7 +92,6 @@ pub fn compute<'a, 'tcx>(scx: &SharedCrateContext<'a, 'tcx>) -> ExportedSymbols // Down below we'll hardwire all of the symbols to the `Rust` export // level instead. let special_runtime_crate = - scx.tcx().is_allocator(cnum.as_def_id()) || scx.tcx().is_panic_runtime(cnum.as_def_id()) || scx.sess().cstore.is_compiler_builtins(cnum); diff --git a/src/librustc_trans/back/write.rs b/src/librustc_trans/back/write.rs index 562d7171156..0c233dfe109 100644 --- a/src/librustc_trans/back/write.rs +++ b/src/librustc_trans/back/write.rs @@ -644,6 +644,7 @@ pub fn run_passes(sess: &Session, let mut modules_config = ModuleConfig::new(tm, sess.opts.cg.passes.clone()); let mut metadata_config = ModuleConfig::new(tm, vec![]); + let mut allocator_config = ModuleConfig::new(tm, vec![]); if let Some(ref sanitizer) = sess.opts.debugging_opts.sanitizer { match *sanitizer { @@ -674,6 +675,7 @@ pub fn run_passes(sess: &Session, modules_config.emit_bc = true; modules_config.emit_lto_bc = true; metadata_config.emit_bc = true; + allocator_config.emit_bc = true; } // Emit bitcode files for the crate if we're emitting an rlib. @@ -699,6 +701,7 @@ pub fn run_passes(sess: &Session, // in this case we still want the metadata object file. if !sess.opts.output_types.contains_key(&OutputType::Assembly) { metadata_config.emit_obj = true; + allocator_config.emit_obj = true; } } OutputType::Object => { modules_config.emit_obj = true; } @@ -706,6 +709,7 @@ pub fn run_passes(sess: &Session, OutputType::Exe => { modules_config.emit_obj = true; metadata_config.emit_obj = true; + allocator_config.emit_obj = true; }, OutputType::Mir => {} OutputType::DepInfo => {} @@ -714,6 +718,7 @@ pub fn run_passes(sess: &Session, modules_config.set_flags(sess, trans); metadata_config.set_flags(sess, trans); + allocator_config.set_flags(sess, trans); // Populate a buffer with a list of codegen threads. Items are processed in @@ -729,6 +734,14 @@ pub fn run_passes(sess: &Session, work_items.push(work); } + if let Some(allocator) = trans.allocator_module.clone() { + let work = build_work_item(sess, + allocator, + allocator_config.clone(), + crate_output.clone()); + work_items.push(work); + } + for mtrans in trans.modules.iter() { let work = build_work_item(sess, mtrans.clone(), @@ -905,6 +918,13 @@ pub fn run_passes(sess: &Session, Some(&trans.metadata_module.name)); remove(sess, &path); } + if allocator_config.emit_bc && !user_wants_bitcode { + if let Some(ref module) = trans.allocator_module { + let path = crate_output.temp_path(OutputType::Bitcode, + Some(&module.name)); + remove(sess, &path); + } + } } // We leave the following files around by default: diff --git a/src/librustc_trans/base.rs b/src/librustc_trans/base.rs index 2589a3538a9..8298324e996 100644 --- a/src/librustc_trans/base.rs +++ b/src/librustc_trans/base.rs @@ -47,6 +47,7 @@ use rustc::session::Session; use rustc_incremental::IncrementalHashesMap; use abi; +use allocator; use mir::lvalue::LvalueRef; use attributes; use builder::Builder; @@ -1086,8 +1087,10 @@ pub fn trans_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, llmod: metadata_llmod, }), }; + let no_builtins = attr::contains_name(&krate.attrs, "no_builtins"); + // Skip crate items and just output metadata in -Z no-trans mode. if tcx.sess.opts.debugging_opts.no_trans || !tcx.sess.opts.output_types.should_trans() { @@ -1097,6 +1100,7 @@ pub fn trans_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, crate_name: tcx.crate_name(LOCAL_CRATE), modules: vec![], metadata_module: metadata_module, + allocator_module: None, link: link_meta, metadata: metadata, exported_symbols: empty_exported_symbols, @@ -1296,6 +1300,41 @@ fn module_translation<'a, 'tcx>( create_imps(sess, &llvm_modules); } + // Translate an allocator shim, if any + // + // If LTO is enabled and we've got some previous LLVM module we translated + // above, then we can just translate directly into that LLVM module. If not, + // however, we need to create a separate module and trans into that. Note + // that the separate translation is critical for the standard library where + // the rlib's object file doesn't have allocator functions but the dylib + // links in an object file that has allocator functions. When we're + // compiling a final LTO artifact, though, there's no need to worry about + // this as we're not working with this dual "rlib/dylib" functionality. + let allocator_module = tcx.sess.allocator_kind.get().and_then(|kind| unsafe { + if sess.lto() && llvm_modules.len() > 0 { + time(tcx.sess.time_passes(), "write allocator module", || { + allocator::trans(tcx, &llvm_modules[0], kind) + }); + None + } else { + let (llcx, llmod) = + context::create_context_and_module(tcx.sess, "allocator"); + let modules = ModuleLlvm { + llmod: llmod, + llcx: llcx, + }; + time(tcx.sess.time_passes(), "write allocator module", || { + allocator::trans(tcx, &modules, kind) + }); + + Some(ModuleTranslation { + name: link::ALLOCATOR_MODULE_NAME.to_string(), + symbol_name_hash: 0, // we always rebuild allocator shims + source: ModuleSource::Translated(modules), + }) + } + }); + let linker_info = LinkerInfo::new(&shared_ccx, &exported_symbols); let subsystem = attr::first_attr_value_str_by_name(&krate.attrs, @@ -1313,6 +1352,7 @@ fn module_translation<'a, 'tcx>( crate_name: tcx.crate_name(LOCAL_CRATE), modules: modules, metadata_module: metadata_module, + allocator_module: allocator_module, link: link_meta, metadata: metadata, exported_symbols: exported_symbols, diff --git a/src/librustc_trans/lib.rs b/src/librustc_trans/lib.rs index 6acd10cb887..5c76f778f8d 100644 --- a/src/librustc_trans/lib.rs +++ b/src/librustc_trans/lib.rs @@ -43,6 +43,7 @@ extern crate libc; extern crate owning_ref; #[macro_use] extern crate rustc; +extern crate rustc_allocator; extern crate rustc_back; extern crate rustc_data_structures; extern crate rustc_incremental; @@ -84,6 +85,7 @@ pub mod back { mod abi; mod adt; +mod allocator; mod asm; mod assert_module_sources; mod attributes; @@ -163,6 +165,7 @@ pub struct CrateTranslation { pub crate_name: Symbol, pub modules: Vec, pub metadata_module: ModuleTranslation, + pub allocator_module: Option, pub link: rustc::middle::cstore::LinkMeta, pub metadata: rustc::middle::cstore::EncodedMetadata, pub exported_symbols: back::symbol_export::ExportedSymbols, diff --git a/src/libstd/collections/hash/table.rs b/src/libstd/collections/hash/table.rs index 50c721db849..06f4f7643ec 100644 --- a/src/libstd/collections/hash/table.rs +++ b/src/libstd/collections/hash/table.rs @@ -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 @@ unsafe fn new_uninitialized(capacity: usize) -> RawTable { .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 @@ fn drop(&mut self) { 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. } diff --git a/src/libstd/error.rs b/src/libstd/error.rs index 4b340f70fbc..d77f817659c 100644 --- a/src/libstd/error.rs +++ b/src/libstd/error.rs @@ -224,7 +224,7 @@ fn description(&self) -> &str { *self } #[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 @@ fn description(&self) -> &str { #[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) diff --git a/src/libstd/heap.rs b/src/libstd/heap.rs new file mode 100644 index 00000000000..83bd3b04b4d --- /dev/null +++ b/src/libstd/heap.rs @@ -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 or the MIT license +// , 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, + } + } +} diff --git a/src/libstd/lib.rs b/src/libstd/lib.rs index bafe23e80a0..c4bdf7c5b82 100644 --- a/src/libstd/lib.rs +++ b/src/libstd/lib.rs @@ -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 @@ #[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 process; pub mod sync; pub mod time; +pub mod heap; // Platform-abstraction modules #[macro_use] diff --git a/src/libstd/sys/unix/mod.rs b/src/libstd/sys/unix/mod.rs index 854d380d128..46e5acdf3d2 100644 --- a/src/libstd/sys/unix/mod.rs +++ b/src/libstd/sys/unix/mod.rs @@ -59,8 +59,6 @@ #[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); diff --git a/src/libstd/sys/windows/mod.rs b/src/libstd/sys/windows/mod.rs index 840e7fdfc9b..ee58efc5144 100644 --- a/src/libstd/sys/windows/mod.rs +++ b/src/libstd/sys/windows/mod.rs @@ -47,24 +47,6 @@ #[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 { diff --git a/src/libsyntax/ext/build.rs b/src/libsyntax/ext/build.rs index 412a3493208..2cfb1616927 100644 --- a/src/libsyntax/ext/build.rs +++ b/src/libsyntax/ext/build.rs @@ -249,6 +249,8 @@ fn item_mod(&self, span: Span, inner_span: Span, name: Ident, attrs: Vec, items: Vec>) -> P; + fn item_extern_crate(&self, span: Span, name: Ident) -> P; + fn item_static(&self, span: Span, name: Ident, @@ -1095,6 +1097,10 @@ fn item_mod(&self, span: Span, inner_span: Span, name: Ident, ) } + fn item_extern_crate(&self, span: Span, name: Ident) -> P { + self.item(span, name, Vec::new(), ast::ItemKind::ExternCrate(None)) + } + fn item_static(&self, span: Span, name: Ident, diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs index df8ee189d21..0163bb3b1d0 100644 --- a/src/libsyntax/feature_gate.rs +++ b/src/libsyntax/feature_gate.rs @@ -137,7 +137,6 @@ pub fn new() -> Features { (active, placement_in_syntax, "1.0.0", Some(27779)), (active, unboxed_closures, "1.0.0", Some(29625)), - (active, allocator, "1.0.0", Some(27389)), (active, fundamental, "1.0.0", Some(29635)), (active, main, "1.0.0", Some(29634)), (active, needs_allocator, "1.4.0", Some(27389)), @@ -360,6 +359,10 @@ pub fn new() -> Features { // Allows unsized tuple coercion. (active, unsized_tuple_coercion, "1.20.0", Some(42877)), + + // global allocators and their internals + (active, global_allocator, "1.20.0", None), + (active, allocator_internals, "1.20.0", None), ); declare_features! ( @@ -379,6 +382,7 @@ pub fn new() -> Features { // rustc internal (removed, unmarked_api, "1.0.0", None), (removed, pushpop_unsafe, "1.2.0", None), + (removed, allocator, "1.0.0", None), ); declare_features! ( @@ -585,16 +589,22 @@ pub fn is_builtin_attr(attr: &ast::Attribute) -> bool { "the `#[rustc_on_unimplemented]` attribute \ is an experimental feature", cfg_fn!(on_unimplemented))), - ("allocator", Whitelisted, Gated(Stability::Unstable, - "allocator", - "the `#[allocator]` attribute is an experimental feature", - cfg_fn!(allocator))), + ("global_allocator", Normal, Gated(Stability::Unstable, + "global_allocator", + "the `#[global_allocator]` attribute is \ + an experimental feature", + cfg_fn!(global_allocator))), + ("default_lib_allocator", Whitelisted, Gated(Stability::Unstable, + "allocator_internals", + "the `#[default_lib_allocator]` \ + attribute is an experimental feature", + cfg_fn!(allocator_internals))), ("needs_allocator", Normal, Gated(Stability::Unstable, - "needs_allocator", + "allocator_internals", "the `#[needs_allocator]` \ attribute is an experimental \ feature", - cfg_fn!(needs_allocator))), + cfg_fn!(allocator_internals))), ("panic_runtime", Whitelisted, Gated(Stability::Unstable, "panic_runtime", "the `#[panic_runtime]` attribute is \ diff --git a/src/rustllvm/llvm-rebuild-trigger b/src/rustllvm/llvm-rebuild-trigger index 37ba3264452..b2d4c113295 100644 --- a/src/rustllvm/llvm-rebuild-trigger +++ b/src/rustllvm/llvm-rebuild-trigger @@ -1,4 +1,8 @@ # If this file is modified, then llvm will be (optionally) cleaned and then rebuilt. # The actual contents of this file do not matter, but to trigger a change on the # build bots then the contents should be changed so git updates the mtime. +<<<<<<< 37849a002ed91ac2b80aeb2172364b4e19250e05 2017-06-27 +======= +2017-06-26 +>>>>>>> rustc: Implement the #[global_allocator] attribute diff --git a/src/test/codegen/function-arguments.rs b/src/test/codegen/function-arguments.rs index bc84ac49da9..d8bbcd9b732 100644 --- a/src/test/codegen/function-arguments.rs +++ b/src/test/codegen/function-arguments.rs @@ -11,7 +11,7 @@ // compile-flags: -C no-prepopulate-passes #![crate_type = "lib"] -#![feature(allocator)] +#![feature(custom_attribute)] pub struct S { _field: [i64; 4], diff --git a/src/test/compile-fail/allocator-depends-on-needs-allocators.rs b/src/test/compile-fail/allocator-depends-on-needs-allocators.rs deleted file mode 100644 index 7f420ff735a..00000000000 --- a/src/test/compile-fail/allocator-depends-on-needs-allocators.rs +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright 2015 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 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -// error-pattern: `allocator3` cannot depend on a crate that needs an allocator -// aux-build:needs_allocator.rs -// aux-build:allocator3.rs - -// The needs_allocator crate is a dependency of the allocator crate allocator3, -// which is not allowed - -extern crate allocator3; - -fn main() { -} diff --git a/src/test/compile-fail/allocator-dylib-is-system.rs b/src/test/compile-fail/allocator-dylib-is-system.rs deleted file mode 100644 index 31009554c69..00000000000 --- a/src/test/compile-fail/allocator-dylib-is-system.rs +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright 2015 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 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -// ignore-musl no dylibs -// aux-build:allocator-dylib.rs -// aux-build:allocator1.rs -// no-prefer-dynamic -// error-pattern: cannot link together two allocators - -// Verify that the allocator for statically linked dynamic libraries is the -// system allocator. Do this by linking in jemalloc and making sure that we get -// an error. - -// ignore-emscripten FIXME: What "other allocator" should we use for emcc? - -#![feature(alloc_jemalloc)] - -extern crate allocator_dylib; - -// The main purpose of this test is to ensure that `alloc_jemalloc` **fails** -// here (specifically the jemalloc allocator), but currently jemalloc is -// disabled on quite a few platforms (bsds, emscripten, msvc, etc). To ensure -// that this just passes on those platforms we link in some other allocator to -// ensure we get the same error. -// -// So long as we CI linux/macOS we should be good. -#[cfg(any(target_os = "linux", target_os = "macos"))] -extern crate alloc_jemalloc; -#[cfg(not(any(target_os = "linux", target_os = "macos")))] -extern crate allocator1; - -fn main() { - allocator_dylib::foo(); -} diff --git a/src/test/compile-fail/allocator-rust-dylib-is-jemalloc.rs b/src/test/compile-fail/allocator-rust-dylib-is-jemalloc.rs deleted file mode 100644 index 68e01bad96b..00000000000 --- a/src/test/compile-fail/allocator-rust-dylib-is-jemalloc.rs +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright 2015 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 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -// ignore-musl no dylibs -// aux-build:allocator-dylib2.rs -// aux-build:allocator1.rs -// error-pattern: cannot link together two allocators - -// Ensure that rust dynamic libraries use jemalloc as their allocator, verifying -// by linking in the system allocator here and ensuring that we get a complaint. - -// ignore-emscripten FIXME: What "other allocator" is correct for emscripten? - -#![feature(alloc_system)] - -extern crate allocator_dylib2; - -// The main purpose of this test is to ensure that `alloc_system` **fails** -// here (specifically the system allocator), but currently system is -// disabled on quite a few platforms (bsds, emscripten, msvc, etc). To ensure -// that this just passes on those platforms we link in some other allocator to -// ensure we get the same error. -// -// So long as we CI linux/macOS we should be good. -#[cfg(any(all(target_os = "linux", any(target_arch = "x86", target_arch = "x86_64")), - target_os = "macos"))] -extern crate alloc_system; -#[cfg(not(any(all(target_os = "linux", any(target_arch = "x86", target_arch = "x86_64")), - target_os = "macos")))] -extern crate allocator1; - -fn main() { - allocator_dylib2::foo(); -} diff --git a/src/test/compile-fail/auxiliary/allocator3.rs b/src/test/compile-fail/allocator/auxiliary/system-allocator.rs similarity index 71% rename from src/test/compile-fail/auxiliary/allocator3.rs rename to src/test/compile-fail/allocator/auxiliary/system-allocator.rs index d3eb1f6f7ab..4761dc421d7 100644 --- a/src/test/compile-fail/auxiliary/allocator3.rs +++ b/src/test/compile-fail/allocator/auxiliary/system-allocator.rs @@ -1,4 +1,4 @@ -// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution and at // http://rust-lang.org/COPYRIGHT. // @@ -10,10 +10,10 @@ // no-prefer-dynamic -#![feature(allocator)] -#![no_std] -#![allocator] +#![feature(global_allocator, allocator_api)] #![crate_type = "rlib"] -extern crate needs_allocator; +use std::heap::System; +#[global_allocator] +static A: System = System; diff --git a/src/test/compile-fail/auxiliary/allocator2.rs b/src/test/compile-fail/allocator/auxiliary/system-allocator2.rs similarity index 71% rename from src/test/compile-fail/auxiliary/allocator2.rs rename to src/test/compile-fail/allocator/auxiliary/system-allocator2.rs index b24784838d0..4761dc421d7 100644 --- a/src/test/compile-fail/auxiliary/allocator2.rs +++ b/src/test/compile-fail/allocator/auxiliary/system-allocator2.rs @@ -1,4 +1,4 @@ -// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution and at // http://rust-lang.org/COPYRIGHT. // @@ -10,7 +10,10 @@ // no-prefer-dynamic -#![feature(allocator)] -#![allocator] +#![feature(global_allocator, allocator_api)] #![crate_type = "rlib"] -#![no_std] + +use std::heap::System; + +#[global_allocator] +static A: System = System; diff --git a/src/test/compile-fail/allocator/function-allocator.rs b/src/test/compile-fail/allocator/function-allocator.rs new file mode 100644 index 00000000000..50f82607b53 --- /dev/null +++ b/src/test/compile-fail/allocator/function-allocator.rs @@ -0,0 +1,16 @@ +// Copyright 2016 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(global_allocator)] + +#[global_allocator] +fn foo() {} //~ ERROR: allocators must be statics + +fn main() {} diff --git a/src/test/compile-fail/allocator/not-an-allocator.rs b/src/test/compile-fail/allocator/not-an-allocator.rs new file mode 100644 index 00000000000..e4301435063 --- /dev/null +++ b/src/test/compile-fail/allocator/not-an-allocator.rs @@ -0,0 +1,26 @@ +// Copyright 2016 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(global_allocator, heap_api)] + +#[global_allocator] +static A: usize = 0; +//~^ the trait bound `&usize: +//~| the trait bound `&usize: +//~| the trait bound `&usize: +//~| the trait bound `&usize: +//~| the trait bound `&usize: +//~| the trait bound `&usize: +//~| the trait bound `&usize: +//~| the trait bound `&usize: +//~| the trait bound `&usize: +//~| the trait bound `&usize: + +fn main() {} diff --git a/src/test/compile-fail/two-allocators.rs b/src/test/compile-fail/allocator/two-allocators.rs similarity index 59% rename from src/test/compile-fail/two-allocators.rs rename to src/test/compile-fail/allocator/two-allocators.rs index a34f77de245..b46ba6334a2 100644 --- a/src/test/compile-fail/two-allocators.rs +++ b/src/test/compile-fail/allocator/two-allocators.rs @@ -1,4 +1,4 @@ -// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution and at // http://rust-lang.org/COPYRIGHT. // @@ -8,12 +8,15 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// error-pattern: cannot link together two allocators +#![feature(global_allocator, allocator_api)] -// aux-build:allocator1.rs -// aux-build:allocator2.rs +use std::heap::System; -extern crate allocator1; -extern crate allocator2; +#[global_allocator] +static A: System = System; +#[global_allocator] +static B: System = System; +//~^ ERROR: cannot define more than one #[global_allocator] fn main() {} + diff --git a/src/test/compile-fail/allocator/two-allocators2.rs b/src/test/compile-fail/allocator/two-allocators2.rs new file mode 100644 index 00000000000..e342c1f9c44 --- /dev/null +++ b/src/test/compile-fail/allocator/two-allocators2.rs @@ -0,0 +1,25 @@ +// Copyright 2016 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// aux-build:system-allocator.rs +// no-prefer-dynamic +// error-pattern: the #[global_allocator] in + +#![feature(global_allocator, allocator_api)] + +extern crate system_allocator; + +use std::heap::System; + +#[global_allocator] +static A: System = System; + +fn main() {} + diff --git a/src/test/run-pass/allocator-system.rs b/src/test/compile-fail/allocator/two-allocators3.rs similarity index 60% rename from src/test/run-pass/allocator-system.rs rename to src/test/compile-fail/allocator/two-allocators3.rs index 4585003d579..c310d94f6df 100644 --- a/src/test/run-pass/allocator-system.rs +++ b/src/test/compile-fail/allocator/two-allocators3.rs @@ -1,4 +1,4 @@ -// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution and at // http://rust-lang.org/COPYRIGHT. // @@ -8,12 +8,14 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +// aux-build:system-allocator.rs +// aux-build:system-allocator2.rs // no-prefer-dynamic +// error-pattern: the #[global_allocator] in -#![feature(alloc_system)] +#![feature(global_allocator)] -extern crate alloc_system; +extern crate system_allocator; +extern crate system_allocator2; -fn main() { - println!("{:?}", Box::new(3)); -} +fn main() {} diff --git a/src/test/compile-fail/auxiliary/allocator-dylib.rs b/src/test/compile-fail/feature-gate-allocator_internals.rs similarity index 72% rename from src/test/compile-fail/auxiliary/allocator-dylib.rs rename to src/test/compile-fail/feature-gate-allocator_internals.rs index 568b247ecdb..b519a985ec5 100644 --- a/src/test/compile-fail/auxiliary/allocator-dylib.rs +++ b/src/test/compile-fail/feature-gate-allocator_internals.rs @@ -1,4 +1,4 @@ -// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution and at // http://rust-lang.org/COPYRIGHT. // @@ -8,8 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// no-prefer-dynamic +#![default_lib_allocator] //~ ERROR: attribute is an experimental feature -#![crate_type = "dylib"] +fn main() {} -pub fn foo() {} diff --git a/src/test/compile-fail/auxiliary/allocator1.rs b/src/test/compile-fail/feature-gate-global_allocator.rs similarity index 70% rename from src/test/compile-fail/auxiliary/allocator1.rs rename to src/test/compile-fail/feature-gate-global_allocator.rs index b24784838d0..ff3c342f9e0 100644 --- a/src/test/compile-fail/auxiliary/allocator1.rs +++ b/src/test/compile-fail/feature-gate-global_allocator.rs @@ -1,4 +1,4 @@ -// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution and at // http://rust-lang.org/COPYRIGHT. // @@ -8,9 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// no-prefer-dynamic +#[global_allocator] //~ ERROR: attribute is an experimental feature +static A: usize = 0; -#![feature(allocator)] -#![allocator] -#![crate_type = "rlib"] -#![no_std] +fn main() {} diff --git a/src/test/compile-fail/two-allocators-2.rs b/src/test/compile-fail/two-allocators-2.rs deleted file mode 100644 index d6fcbcb513a..00000000000 --- a/src/test/compile-fail/two-allocators-2.rs +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright 2015 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 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -// error-pattern: cannot link together two allocators: allocator1 and allocator2 -// aux-build:allocator1.rs -// aux-build:allocator2.rs - -// Make sure we can't link together two explicit allocators. - -extern crate allocator1; -extern crate allocator2; - -fn main() {} - diff --git a/src/test/compile-fail/two-allocators-3.rs b/src/test/compile-fail/two-allocators-3.rs deleted file mode 100644 index 965e4e0e2ea..00000000000 --- a/src/test/compile-fail/two-allocators-3.rs +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright 2015 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 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -// aux-build:allocator1.rs -// error-pattern: cannot link together two allocators -// ignore-musl no dylibs on musl yet -// ignore-emscripten - -// We're linking std dynamically (via -C prefer-dynamic for this test) which -// has an allocator and then we're also linking in a new allocator (allocator1) -// and this should be an error - -extern crate allocator1; - -fn main() { -} diff --git a/src/test/run-make/no-duplicate-libs/Makefile b/src/test/run-make/no-duplicate-libs/Makefile index 3f6a28c251a..13d8366c60a 100644 --- a/src/test/run-make/no-duplicate-libs/Makefile +++ b/src/test/run-make/no-duplicate-libs/Makefile @@ -4,9 +4,7 @@ ifdef IS_MSVC # FIXME(#27979) all: else -all: - $(RUSTC) foo.rs - $(RUSTC) bar.rs +all: $(call STATICLIB,foo) $(call STATICLIB,bar) $(RUSTC) main.rs $(call RUN,main) endif diff --git a/src/test/compile-fail/feature-gate-allocator.rs b/src/test/run-make/no-duplicate-libs/bar.c similarity index 76% rename from src/test/compile-fail/feature-gate-allocator.rs rename to src/test/run-make/no-duplicate-libs/bar.c index 6490216d012..b9dcd0f5e5e 100644 --- a/src/test/compile-fail/feature-gate-allocator.rs +++ b/src/test/run-make/no-duplicate-libs/bar.c @@ -1,4 +1,4 @@ -// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// 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. // @@ -8,6 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -#![allocator] //~ ERROR: experimental feature +extern void foo(); -fn main() {} +void bar() { + foo(); +} diff --git a/src/test/run-make/no-duplicate-libs/bar.rs b/src/test/run-make/no-duplicate-libs/bar.rs deleted file mode 100644 index cb1c88689a8..00000000000 --- a/src/test/run-make/no-duplicate-libs/bar.rs +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright 2014 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 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -#![feature(lang_items, alloc_system, compiler_builtins_lib)] -#![crate_type = "dylib"] -#![no_std] - -extern crate alloc_system; -extern crate compiler_builtins; - -#[no_mangle] -pub extern fn bar() {} - -#[lang = "eh_personality"] fn eh_personality() {} -#[lang = "eh_unwind_resume"] fn eh_unwind_resume() {} -#[lang = "panic_fmt"] fn panic_fmt() -> ! { loop {} } -#[no_mangle] pub extern fn rust_eh_register_frames () {} -#[no_mangle] pub extern fn rust_eh_unregister_frames () {} diff --git a/src/test/compile-fail/auxiliary/allocator-dylib2.rs b/src/test/run-make/no-duplicate-libs/foo.c similarity index 83% rename from src/test/compile-fail/auxiliary/allocator-dylib2.rs rename to src/test/run-make/no-duplicate-libs/foo.c index 0d76c0e5eb8..906cd5682b8 100644 --- a/src/test/compile-fail/auxiliary/allocator-dylib2.rs +++ b/src/test/run-make/no-duplicate-libs/foo.c @@ -1,4 +1,4 @@ -// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// 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. // @@ -8,5 +8,4 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -pub fn foo() {} - +void foo() {} diff --git a/src/test/run-make/no-duplicate-libs/foo.rs b/src/test/run-make/no-duplicate-libs/foo.rs deleted file mode 100644 index 214fb156c2d..00000000000 --- a/src/test/run-make/no-duplicate-libs/foo.rs +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright 2014 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 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -#![feature(lang_items, alloc_system, compiler_builtins_lib)] -#![no_std] -#![crate_type = "dylib"] - -extern crate alloc_system; -extern crate compiler_builtins; - -#[no_mangle] -pub extern fn foo() {} - -#[lang = "eh_personality"] fn eh_personality() {} -#[lang = "eh_unwind_resume"] fn eh_unwind_resume() {} -#[lang = "panic_fmt"] fn panic_fmt() -> ! { loop {} } -#[no_mangle] pub extern fn rust_eh_register_frames () {} -#[no_mangle] pub extern fn rust_eh_unregister_frames () {} diff --git a/src/test/run-make/no-duplicate-libs/main.rs b/src/test/run-make/no-duplicate-libs/main.rs index 12ddce34582..824946fe9c2 100644 --- a/src/test/run-make/no-duplicate-libs/main.rs +++ b/src/test/run-make/no-duplicate-libs/main.rs @@ -8,9 +8,9 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -#[link(name = "foo")] -#[link(name = "bar")] -#[link(name = "foo")] +#[link(name = "foo")] // linker should drop this library, no symbols used +#[link(name = "bar")] // symbol comes from this library +#[link(name = "foo")] // now linker picks up `foo` b/c `bar` library needs it extern { fn bar(); } diff --git a/src/test/run-pass/allocator-alloc-one.rs b/src/test/run-pass/allocator-alloc-one.rs index 7cc547dcc04..712fa2d6001 100644 --- a/src/test/run-pass/allocator-alloc-one.rs +++ b/src/test/run-pass/allocator-alloc-one.rs @@ -8,20 +8,17 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -#![feature(alloc, allocator_api, heap_api, unique)] +#![feature(allocator_api, unique)] -extern crate alloc; - -use alloc::heap::HeapAlloc; -use alloc::allocator::Alloc; +use std::heap::{Heap, Alloc}; fn main() { unsafe { - let ptr = HeapAlloc.alloc_one::().unwrap_or_else(|e| { - HeapAlloc.oom(e) + let ptr = Heap.alloc_one::().unwrap_or_else(|e| { + Heap.oom(e) }); *ptr.as_ptr() = 4; assert_eq!(*ptr.as_ptr(), 4); - HeapAlloc.dealloc_one(ptr); + Heap.dealloc_one(ptr); } } diff --git a/src/test/run-pass/allocator-default.rs b/src/test/run-pass/allocator-default.rs deleted file mode 100644 index 0a02e8072b9..00000000000 --- a/src/test/run-pass/allocator-default.rs +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright 2015 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 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -#![feature(alloc_jemalloc)] - -#[cfg(any(all(target_os = "linux", any(target_arch = "x86", target_arch = "x86_64")), - target_os = "macos"))] -extern crate alloc_jemalloc; - -fn main() { - println!("{:?}", Box::new(3)); -} diff --git a/src/test/run-pass/allocator-override.rs b/src/test/run-pass/allocator-override.rs deleted file mode 100644 index ca2dbdf2b3d..00000000000 --- a/src/test/run-pass/allocator-override.rs +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright 2015 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 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -// no-prefer-dynamic -// aux-build:allocator-dummy.rs -// ignore-emscripten - -#![feature(test)] - -extern crate allocator_dummy; -extern crate test; - -fn main() { - unsafe { - let before = allocator_dummy::HITS; - let mut b = Box::new(3); - test::black_box(&mut b); // Make sure the allocation is not optimized away - assert_eq!(allocator_dummy::HITS - before, 1); - drop(b); - assert_eq!(allocator_dummy::HITS - before, 2); - } -} diff --git a/src/test/run-pass/allocator/auxiliary/custom-as-global.rs b/src/test/run-pass/allocator/auxiliary/custom-as-global.rs new file mode 100644 index 00000000000..538f36faadf --- /dev/null +++ b/src/test/run-pass/allocator/auxiliary/custom-as-global.rs @@ -0,0 +1,27 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// no-prefer-dynamic + +#![feature(global_allocator)] +#![crate_type = "rlib"] + +extern crate custom; + +use std::sync::atomic::{ATOMIC_USIZE_INIT, Ordering}; + +use custom::A; + +#[global_allocator] +static ALLOCATOR: A = A(ATOMIC_USIZE_INIT); + +pub fn get() -> usize { + ALLOCATOR.0.load(Ordering::SeqCst) +} diff --git a/src/test/run-pass/allocator/auxiliary/custom.rs b/src/test/run-pass/allocator/auxiliary/custom.rs new file mode 100644 index 00000000000..8f4fbcd5ab1 --- /dev/null +++ b/src/test/run-pass/allocator/auxiliary/custom.rs @@ -0,0 +1,31 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// no-prefer-dynamic + +#![feature(heap_api, allocator_api)] +#![crate_type = "rlib"] + +use std::heap::{Alloc, System, AllocErr, Layout}; +use std::sync::atomic::{AtomicUsize, Ordering}; + +pub struct A(pub AtomicUsize); + +unsafe impl<'a> Alloc for &'a A { + unsafe fn alloc(&mut self, layout: Layout) -> Result<*mut u8, AllocErr> { + self.0.fetch_add(1, Ordering::SeqCst); + System.alloc(layout) + } + + unsafe fn dealloc(&mut self, ptr: *mut u8, layout: Layout) { + self.0.fetch_add(1, Ordering::SeqCst); + System.dealloc(ptr, layout) + } +} diff --git a/src/test/compile-fail/auxiliary/needs_allocator.rs b/src/test/run-pass/allocator/auxiliary/helper.rs similarity index 77% rename from src/test/compile-fail/auxiliary/needs_allocator.rs rename to src/test/run-pass/allocator/auxiliary/helper.rs index 51003160427..e75a432710d 100644 --- a/src/test/compile-fail/auxiliary/needs_allocator.rs +++ b/src/test/run-pass/allocator/auxiliary/helper.rs @@ -1,4 +1,4 @@ -// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// 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. // @@ -10,7 +10,10 @@ // no-prefer-dynamic -#![feature(needs_allocator)] -#![no_std] -#![needs_allocator] #![crate_type = "rlib"] + +use std::fmt; + +pub fn work_with(p: &fmt::Debug) { + drop(p); +} diff --git a/src/test/run-pass/allocator/custom.rs b/src/test/run-pass/allocator/custom.rs new file mode 100644 index 00000000000..b46f024b5bf --- /dev/null +++ b/src/test/run-pass/allocator/custom.rs @@ -0,0 +1,68 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// aux-build:helper.rs +// no-prefer-dynamic + +#![feature(global_allocator, heap_api, allocator_api)] + +extern crate helper; + +use std::env; +use std::heap::{Heap, Alloc, System, Layout, AllocErr}; +use std::sync::atomic::{AtomicUsize, Ordering, ATOMIC_USIZE_INIT}; + +static HITS: AtomicUsize = ATOMIC_USIZE_INIT; + +struct A; + +unsafe impl<'a> Alloc for &'a A { + unsafe fn alloc(&mut self, layout: Layout) -> Result<*mut u8, AllocErr> { + HITS.fetch_add(1, Ordering::SeqCst); + System.alloc(layout) + } + + unsafe fn dealloc(&mut self, ptr: *mut u8, layout: Layout) { + HITS.fetch_add(1, Ordering::SeqCst); + System.dealloc(ptr, layout) + } +} + +#[global_allocator] +static GLOBAL: A = A; + +fn main() { + env::set_var("FOO", "bar"); + drop(env::var("FOO")); + + let n = HITS.load(Ordering::SeqCst); + assert!(n > 0); + unsafe { + let layout = Layout::from_size_align(4, 2).unwrap(); + + let ptr = Heap.alloc(layout.clone()).unwrap(); + helper::work_with(&ptr); + assert_eq!(HITS.load(Ordering::SeqCst), n + 1); + Heap.dealloc(ptr, layout.clone()); + assert_eq!(HITS.load(Ordering::SeqCst), n + 2); + + let s = String::with_capacity(10); + helper::work_with(&s); + assert_eq!(HITS.load(Ordering::SeqCst), n + 3); + drop(s); + assert_eq!(HITS.load(Ordering::SeqCst), n + 4); + + let ptr = System.alloc(layout.clone()).unwrap(); + assert_eq!(HITS.load(Ordering::SeqCst), n + 4); + helper::work_with(&ptr); + System.dealloc(ptr, layout); + assert_eq!(HITS.load(Ordering::SeqCst), n + 4); + } +} diff --git a/src/test/run-pass/allocator/xcrate-use.rs b/src/test/run-pass/allocator/xcrate-use.rs new file mode 100644 index 00000000000..4b987b9223d --- /dev/null +++ b/src/test/run-pass/allocator/xcrate-use.rs @@ -0,0 +1,44 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// aux-build:custom.rs +// aux-build:helper.rs +// no-prefer-dynamic + +#![feature(global_allocator, heap_api, allocator_api)] + +extern crate custom; +extern crate helper; + +use std::env; +use std::heap::{Heap, Alloc, System, Layout}; +use std::sync::atomic::{Ordering, ATOMIC_USIZE_INIT}; + +#[global_allocator] +static GLOBAL: custom::A = custom::A(ATOMIC_USIZE_INIT); + +fn main() { + unsafe { + let n = GLOBAL.0.load(Ordering::SeqCst); + let layout = Layout::from_size_align(4, 2).unwrap(); + + let ptr = Heap.alloc(layout.clone()).unwrap(); + helper::work_with(&ptr); + assert_eq!(GLOBAL.0.load(Ordering::SeqCst), n + 1); + Heap.dealloc(ptr, layout.clone()); + assert_eq!(GLOBAL.0.load(Ordering::SeqCst), n + 2); + + let ptr = System.alloc(layout.clone()).unwrap(); + assert_eq!(GLOBAL.0.load(Ordering::SeqCst), n + 2); + helper::work_with(&ptr); + System.dealloc(ptr, layout); + assert_eq!(GLOBAL.0.load(Ordering::SeqCst), n + 2); + } +} diff --git a/src/test/run-pass/allocator/xcrate-use2.rs b/src/test/run-pass/allocator/xcrate-use2.rs new file mode 100644 index 00000000000..7e6cd9fdf49 --- /dev/null +++ b/src/test/run-pass/allocator/xcrate-use2.rs @@ -0,0 +1,57 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// aux-build:custom.rs +// aux-build:custom-as-global.rs +// aux-build:helper.rs +// no-prefer-dynamic + +#![feature(heap_api, allocator_api)] + +extern crate custom; +extern crate custom_as_global; +extern crate helper; + +use std::env; +use std::heap::{Heap, Alloc, System, Layout}; +use std::sync::atomic::{Ordering, ATOMIC_USIZE_INIT}; + +static GLOBAL: custom::A = custom::A(ATOMIC_USIZE_INIT); + +fn main() { + unsafe { + let n = custom_as_global::get(); + let layout = Layout::from_size_align(4, 2).unwrap(); + + // Global allocator routes to the `custom_as_global` global + let ptr = Heap.alloc(layout.clone()).unwrap(); + helper::work_with(&ptr); + assert_eq!(custom_as_global::get(), n + 1); + Heap.dealloc(ptr, layout.clone()); + assert_eq!(custom_as_global::get(), n + 2); + + // Usage of the system allocator avoids all globals + let ptr = System.alloc(layout.clone()).unwrap(); + helper::work_with(&ptr); + assert_eq!(custom_as_global::get(), n + 2); + System.dealloc(ptr, layout.clone()); + assert_eq!(custom_as_global::get(), n + 2); + + // Usage of our personal allocator doesn't affect other instances + let ptr = (&GLOBAL).alloc(layout.clone()).unwrap(); + helper::work_with(&ptr); + assert_eq!(custom_as_global::get(), n + 2); + assert_eq!(GLOBAL.0.load(Ordering::SeqCst), 1); + (&GLOBAL).dealloc(ptr, layout); + assert_eq!(custom_as_global::get(), n + 2); + assert_eq!(GLOBAL.0.load(Ordering::SeqCst), 2); + } +} + diff --git a/src/test/run-pass/lib-defaults.rs b/src/test/run-pass/lib-defaults.rs index a38080f8cfe..6e5dccae0a0 100644 --- a/src/test/run-pass/lib-defaults.rs +++ b/src/test/run-pass/lib-defaults.rs @@ -8,16 +8,15 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// aux-build:clibrary.rs -// compile-flags: -lclibrary +// compile-flags: -lrust_test_helpers -#[link(name = "clibrary", kind = "static")] +#[link(name = "rust_test_helpers", kind = "static")] extern "C" { - pub fn foo(x:i32) -> i32; + pub fn rust_dbg_extern_identity_u32(x: u32) -> u32; } fn main() { unsafe { - foo(42); + rust_dbg_extern_identity_u32(42); } } diff --git a/src/test/run-pass/realloc-16687.rs b/src/test/run-pass/realloc-16687.rs index b32d42df6b1..eddcd5a584a 100644 --- a/src/test/run-pass/realloc-16687.rs +++ b/src/test/run-pass/realloc-16687.rs @@ -13,11 +13,9 @@ // Ideally this would be revised to use no_std, but for now it serves // well enough to reproduce (and illustrate) the bug from #16687. -#![feature(heap_api, alloc, oom)] +#![feature(heap_api, allocator_api)] -extern crate alloc; - -use alloc::heap; +use std::heap::{Heap, Alloc, Layout}; use std::ptr; fn main() { @@ -47,38 +45,39 @@ unsafe fn sanity_check(ascend: &[*mut u8]) { static PRINT : bool = false; - unsafe fn allocate(size: usize, align: usize) -> *mut u8 { - if PRINT { println!("allocate(size={} align={})", size, align); } + unsafe fn allocate(layout: Layout) -> *mut u8 { + if PRINT { + println!("allocate({:?})", layout); + } - let ret = heap::allocate(size, align); - if ret.is_null() { alloc::oom() } + let ret = Heap.alloc(layout.clone()).unwrap_or_else(|e| Heap.oom(e)); - if PRINT { println!("allocate(size={} align={}) ret: 0x{:010x}", - size, align, ret as usize); + if PRINT { + println!("allocate({:?}) = {:?}", layout, ret); } ret } - unsafe fn deallocate(ptr: *mut u8, size: usize, align: usize) { - if PRINT { println!("deallocate(ptr=0x{:010x} size={} align={})", - ptr as usize, size, align); + + unsafe fn deallocate(ptr: *mut u8, layout: Layout) { + if PRINT { + println!("deallocate({:?}, {:?}", ptr, layout); } - heap::deallocate(ptr, size, align); + Heap.dealloc(ptr, layout); } - unsafe fn reallocate(ptr: *mut u8, old_size: usize, size: usize, align: usize) -> *mut u8 { + + unsafe fn reallocate(ptr: *mut u8, old: Layout, new: Layout) -> *mut u8 { if PRINT { - println!("reallocate(ptr=0x{:010x} old_size={} size={} align={})", - ptr as usize, old_size, size, align); + println!("reallocate({:?}, old={:?}, new={:?})", ptr, old, new); } - let ret = heap::reallocate(ptr, old_size, size, align); - if ret.is_null() { alloc::oom() } + let ret = Heap.realloc(ptr, old.clone(), new.clone()) + .unwrap_or_else(|e| Heap.oom(e)); if PRINT { - println!("reallocate(ptr=0x{:010x} old_size={} size={} align={}) \ - ret: 0x{:010x}", - ptr as usize, old_size, size, align, ret as usize); + println!("reallocate({:?}, old={:?}, new={:?}) = {:?}", + ptr, old, new, ret); } ret } @@ -91,8 +90,8 @@ fn idx_to_size(i: usize) -> usize { (i+1) * 10 } // way.) for i in 0..COUNT / 2 { let size = idx_to_size(i); - ascend[2*i] = allocate(size, ALIGN); - ascend[2*i+1] = allocate(size, ALIGN); + ascend[2*i] = allocate(Layout::from_size_align(size, ALIGN).unwrap()); + ascend[2*i+1] = allocate(Layout::from_size_align(size, ALIGN).unwrap()); } // Initialize each pair of rows to distinct value. @@ -112,8 +111,8 @@ fn idx_to_size(i: usize) -> usize { (i+1) * 10 } for i in 0..COUNT / 2 { let size = idx_to_size(i); - deallocate(ascend[2*i], size, ALIGN); - deallocate(ascend[2*i+1], size, ALIGN); + deallocate(ascend[2*i], Layout::from_size_align(size, ALIGN).unwrap()); + deallocate(ascend[2*i+1], Layout::from_size_align(size, ALIGN).unwrap()); } return true; @@ -124,14 +123,16 @@ fn idx_to_size(i: usize) -> usize { (i+1) * 10 } // rows as we go. unsafe fn test_1(ascend: &mut [*mut u8]) { let new_size = idx_to_size(COUNT-1); + let new = Layout::from_size_align(new_size, ALIGN).unwrap(); for i in 0..COUNT / 2 { let (p0, p1, old_size) = (ascend[2*i], ascend[2*i+1], idx_to_size(i)); assert!(old_size < new_size); + let old = Layout::from_size_align(old_size, ALIGN).unwrap(); - ascend[2*i] = reallocate(p0, old_size, new_size, ALIGN); + ascend[2*i] = reallocate(p0, old.clone(), new.clone()); sanity_check(&*ascend); - ascend[2*i+1] = reallocate(p1, old_size, new_size, ALIGN); + ascend[2*i+1] = reallocate(p1, old.clone(), new.clone()); sanity_check(&*ascend); } } @@ -139,14 +140,16 @@ unsafe fn test_1(ascend: &mut [*mut u8]) { // Test 2: turn the square back into a triangle, top to bottom. unsafe fn test_2(ascend: &mut [*mut u8]) { let old_size = idx_to_size(COUNT-1); + let old = Layout::from_size_align(old_size, ALIGN).unwrap(); for i in 0..COUNT / 2 { let (p0, p1, new_size) = (ascend[2*i], ascend[2*i+1], idx_to_size(i)); assert!(new_size < old_size); + let new = Layout::from_size_align(new_size, ALIGN).unwrap(); - ascend[2*i] = reallocate(p0, old_size, new_size, ALIGN); + ascend[2*i] = reallocate(p0, old.clone(), new.clone()); sanity_check(&*ascend); - ascend[2*i+1] = reallocate(p1, old_size, new_size, ALIGN); + ascend[2*i+1] = reallocate(p1, old.clone(), new.clone()); sanity_check(&*ascend); } } @@ -154,14 +157,16 @@ unsafe fn test_2(ascend: &mut [*mut u8]) { // Test 3: turn triangle into a square, bottom to top. unsafe fn test_3(ascend: &mut [*mut u8]) { let new_size = idx_to_size(COUNT-1); + let new = Layout::from_size_align(new_size, ALIGN).unwrap(); for i in (0..COUNT / 2).rev() { let (p0, p1, old_size) = (ascend[2*i], ascend[2*i+1], idx_to_size(i)); assert!(old_size < new_size); + let old = Layout::from_size_align(old_size, ALIGN).unwrap(); - ascend[2*i+1] = reallocate(p1, old_size, new_size, ALIGN); + ascend[2*i+1] = reallocate(p1, old.clone(), new.clone()); sanity_check(&*ascend); - ascend[2*i] = reallocate(p0, old_size, new_size, ALIGN); + ascend[2*i] = reallocate(p0, old.clone(), new.clone()); sanity_check(&*ascend); } } @@ -169,14 +174,16 @@ unsafe fn test_3(ascend: &mut [*mut u8]) { // Test 4: turn the square back into a triangle, bottom to top. unsafe fn test_4(ascend: &mut [*mut u8]) { let old_size = idx_to_size(COUNT-1); + let old = Layout::from_size_align(old_size, ALIGN).unwrap(); for i in (0..COUNT / 2).rev() { let (p0, p1, new_size) = (ascend[2*i], ascend[2*i+1], idx_to_size(i)); assert!(new_size < old_size); + let new = Layout::from_size_align(new_size, ALIGN).unwrap(); - ascend[2*i+1] = reallocate(p1, old_size, new_size, ALIGN); + ascend[2*i+1] = reallocate(p1, old.clone(), new.clone()); sanity_check(&*ascend); - ascend[2*i] = reallocate(p0, old_size, new_size, ALIGN); + ascend[2*i] = reallocate(p0, old.clone(), new.clone()); sanity_check(&*ascend); } } diff --git a/src/test/run-pass/rfc1717/library-override.rs b/src/test/run-pass/rfc1717/library-override.rs index d6ef96c5add..26713a25543 100644 --- a/src/test/run-pass/rfc1717/library-override.rs +++ b/src/test/run-pass/rfc1717/library-override.rs @@ -8,16 +8,15 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// aux-build:clibrary.rs -// compile-flags: -lstatic=wronglibrary:clibrary +// compile-flags: -lstatic=wronglibrary:rust_test_helpers #[link(name = "wronglibrary", kind = "dylib")] extern "C" { - pub fn foo(x:i32) -> i32; + pub fn rust_dbg_extern_identity_u32(x: u32) -> u32; } fn main() { unsafe { - foo(42); + rust_dbg_extern_identity_u32(42); } } diff --git a/src/test/run-pass/smallest-hello-world.rs b/src/test/run-pass/smallest-hello-world.rs index 053ee8ee42e..bcbd3fd3786 100644 --- a/src/test/run-pass/smallest-hello-world.rs +++ b/src/test/run-pass/smallest-hello-world.rs @@ -10,32 +10,32 @@ // Smallest "hello world" with a libc runtime -// pretty-expanded FIXME #23616 // ignore-windows +// ignore-android #![feature(intrinsics, lang_items, start, no_core, alloc_system)] -#![no_core] +#![feature(global_allocator, allocator_api)] +#![no_std] extern crate alloc_system; -extern { fn puts(s: *const u8); } -extern "rust-intrinsic" { fn transmute(t: T) -> U; } +use alloc_system::System; -#[lang = "eh_personality"] extern fn eh_personality() {} -#[lang = "eh_unwind_resume"] extern fn eh_unwind_resume() {} +#[global_allocator] +static A: System = System; + +extern { + fn puts(s: *const u8); +} + +#[no_mangle] +#[lang = "eh_personality"] pub extern fn rust_eh_personality() {} #[lang = "panic_fmt"] fn panic_fmt() -> ! { loop {} } -#[no_mangle] pub extern fn rust_eh_register_frames () {} -#[no_mangle] pub extern fn rust_eh_unregister_frames () {} #[start] fn main(_: isize, _: *const *const u8) -> isize { unsafe { - let (ptr, _): (*const u8, usize) = transmute("Hello!\0"); - puts(ptr); + puts("Hello!\0".as_ptr() as *const u8); } - return 0; + return 0 } - -#[cfg(target_os = "android")] -#[link(name="gcc")] -extern { } -- 2.44.0