From 45bf1ed1a1123122ded05ae2eedaf0f190e52726 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Thu, 25 Jun 2015 10:07:01 -0700 Subject: [PATCH] rustc: Allow changing the default allocator This commit is an implementation of [RFC 1183][rfc] which allows swapping out the default allocator on nightly Rust. No new stable surface area should be added as a part of this commit. [rfc]: https://github.com/rust-lang/rfcs/pull/1183 Two new attributes have been added to the compiler: * `#![needs_allocator]` - this is used by liballoc (and likely only liballoc) to indicate that it requires an allocator crate to be in scope. * `#![allocator]` - this is a indicator that the crate is an allocator which can satisfy the `needs_allocator` attribute above. The ABI of the allocator crate is defined to be a set of symbols that implement the standard Rust allocation/deallocation functions. The symbols are not currently checked for exhaustiveness or typechecked. There are also a number of restrictions on these crates: * An allocator crate cannot transitively depend on a crate that is flagged as needing an allocator (e.g. allocator crates can't depend on liballoc). * There can only be one explicitly linked allocator in a final image. * If no allocator is explicitly requested one will be injected on behalf of the compiler. Binaries and Rust dylibs will use jemalloc by default where available and staticlibs/other dylibs will use the system allocator by default. Two allocators are provided by the distribution by default, `alloc_system` and `alloc_jemalloc` which operate as advertised. Closes #27389 --- mk/crates.mk | 25 +- mk/rt.mk | 6 - mk/tests.mk | 3 +- src/liballoc/heap.rs | 390 +----------------- src/liballoc/lib.rs | 10 +- src/liballoc_jemalloc/lib.rs | 96 +++++ src/liballoc_system/lib.rs | 212 ++++++++++ src/librustc/metadata/common.rs | 7 +- src/librustc/metadata/creader.rs | 171 +++++++- src/librustc/metadata/cstore.rs | 35 +- src/librustc/metadata/decoder.rs | 10 +- src/librustc/metadata/encoder.rs | 48 ++- src/librustc/middle/dependency_format.rs | 159 +++++-- src/librustc/middle/ty.rs | 4 - src/librustc/session/mod.rs | 14 +- src/librustc/util/common.rs | 8 +- src/librustc_back/target/apple_base.rs | 1 + src/librustc_back/target/bitrig_base.rs | 1 + src/librustc_back/target/dragonfly_base.rs | 1 + src/librustc_back/target/freebsd_base.rs | 1 + src/librustc_back/target/linux_base.rs | 1 + src/librustc_back/target/mod.rs | 14 + src/librustc_back/target/openbsd_base.rs | 1 + src/librustc_back/target/windows_base.rs | 1 + src/librustc_back/target/windows_msvc_base.rs | 1 + src/librustc_driver/driver.rs | 158 ++++--- src/librustc_driver/lib.rs | 8 +- src/librustc_trans/back/link.rs | 84 ++-- src/librustc_trans/back/linker.rs | 7 +- src/librustc_trans/back/lto.rs | 29 +- src/librustc_trans/back/write.rs | 8 +- src/librustc_trans/trans/base.rs | 2 - src/librustc_trans/trans/mod.rs | 2 - src/librustc_typeck/lib.rs | 16 +- src/libstd/lib.rs | 2 - src/libsyntax/feature_gate.rs | 4 + src/test/auxiliary/allocator-dummy.rs | 55 +++ src/test/auxiliary/allocator-dylib.rs | 15 + src/test/auxiliary/allocator-dylib2.rs | 12 + src/test/auxiliary/allocator1.rs | 16 + src/test/auxiliary/allocator2.rs | 16 + src/test/auxiliary/allocator3.rs | 19 + src/test/auxiliary/needs_allocator.rs | 16 + .../allocator-depends-on-needs-allocators.rs | 21 + .../compile-fail/allocator-dylib-is-system.rs | 27 ++ .../allocator-rust-dylib-is-jemalloc.rs | 26 ++ .../compile-fail/feature-gate-allocator.rs | 13 + .../feature-gate-needs-allocator.rs | 14 + src/test/compile-fail/two-allocators-2.rs | 21 + src/test/compile-fail/two-allocators-3.rs | 21 + src/test/compile-fail/two-allocators.rs | 19 + src/test/run-pass/allocator-default.rs | 20 + src/test/run-pass/allocator-jemalloc.rs | 20 + src/test/run-pass/allocator-override.rs | 24 ++ src/test/run-pass/allocator-system.rs | 19 + 55 files changed, 1287 insertions(+), 647 deletions(-) create mode 100644 src/liballoc_jemalloc/lib.rs create mode 100644 src/liballoc_system/lib.rs create mode 100644 src/test/auxiliary/allocator-dummy.rs create mode 100644 src/test/auxiliary/allocator-dylib.rs create mode 100644 src/test/auxiliary/allocator-dylib2.rs create mode 100644 src/test/auxiliary/allocator1.rs create mode 100644 src/test/auxiliary/allocator2.rs create mode 100644 src/test/auxiliary/allocator3.rs create mode 100644 src/test/auxiliary/needs_allocator.rs create mode 100644 src/test/compile-fail/allocator-depends-on-needs-allocators.rs create mode 100644 src/test/compile-fail/allocator-dylib-is-system.rs create mode 100644 src/test/compile-fail/allocator-rust-dylib-is-jemalloc.rs create mode 100644 src/test/compile-fail/feature-gate-allocator.rs create mode 100644 src/test/compile-fail/feature-gate-needs-allocator.rs create mode 100644 src/test/compile-fail/two-allocators-2.rs create mode 100644 src/test/compile-fail/two-allocators-3.rs create mode 100644 src/test/compile-fail/two-allocators.rs create mode 100644 src/test/run-pass/allocator-default.rs create mode 100644 src/test/run-pass/allocator-jemalloc.rs create mode 100644 src/test/run-pass/allocator-override.rs create mode 100644 src/test/run-pass/allocator-system.rs diff --git a/mk/crates.mk b/mk/crates.mk index d7667bef788d8..af2a663b61ded 100644 --- a/mk/crates.mk +++ b/mk/crates.mk @@ -52,23 +52,23 @@ TARGET_CRATES := libc std flate arena term \ serialize getopts collections test rand \ log graphviz core rbml alloc \ - rustc_unicode rustc_bitflags + rustc_unicode rustc_bitflags \ + alloc_system RUSTC_CRATES := rustc rustc_typeck rustc_borrowck rustc_resolve rustc_driver \ rustc_trans rustc_back rustc_llvm rustc_privacy rustc_lint \ rustc_data_structures HOST_CRATES := syntax $(RUSTC_CRATES) rustdoc fmt_macros -CRATES := $(TARGET_CRATES) $(HOST_CRATES) TOOLS := compiletest rustdoc rustc rustbook error-index-generator DEPS_core := DEPS_libc := core DEPS_rustc_unicode := core -DEPS_alloc := core libc native:jemalloc +DEPS_alloc := core libc alloc_system DEPS_std := core libc rand alloc collections rustc_unicode \ native:rust_builtin native:backtrace \ - rustc_bitflags + alloc_system DEPS_graphviz := std -DEPS_syntax := std term serialize log fmt_macros arena libc +DEPS_syntax := std term serialize log fmt_macros arena libc rustc_bitflags DEPS_rustc_driver := arena flate getopts graphviz libc rustc rustc_back rustc_borrowck \ rustc_typeck rustc_resolve log syntax serialize rustc_llvm \ rustc_trans rustc_privacy rustc_lint @@ -82,7 +82,7 @@ DEPS_rustc_privacy := rustc log syntax DEPS_rustc_lint := rustc log syntax DEPS_rustc := syntax flate arena serialize getopts rbml \ log graphviz rustc_llvm rustc_back rustc_data_structures -DEPS_rustc_llvm := native:rustllvm libc std +DEPS_rustc_llvm := native:rustllvm libc std rustc_bitflags DEPS_rustc_back := std syntax rustc_llvm flate log libc DEPS_rustc_data_structures := std log serialize DEPS_rustdoc := rustc rustc_driver native:hoedown serialize getopts \ @@ -102,6 +102,7 @@ DEPS_test := std getopts serialize rbml term native:rust_test_helpers DEPS_rand := core DEPS_log := std DEPS_fmt_macros = std +DEPS_alloc_system := core libc TOOL_DEPS_compiletest := test getopts TOOL_DEPS_rustdoc := rustdoc @@ -121,14 +122,26 @@ ONLY_RLIB_rand := 1 ONLY_RLIB_collections := 1 ONLY_RLIB_rustc_unicode := 1 ONLY_RLIB_rustc_bitflags := 1 +ONLY_RLIB_alloc_system := 1 # Documented-by-default crates DOC_CRATES := std alloc collections core libc rustc_unicode +ifeq ($(CFG_DISABLE_JEMALLOC),) +TARGET_CRATES += alloc_jemalloc +DEPS_std += alloc_jemalloc +DEPS_alloc_jemalloc := core libc native:jemalloc +ONLY_RLIB_alloc_jemalloc := 1 +else +RUSTFLAGS_rustc_back := --cfg disable_jemalloc +endif + ################################################################################ # You should not need to edit below this line ################################################################################ +CRATES := $(TARGET_CRATES) $(HOST_CRATES) + # This macro creates some simple definitions for each crate being built, just # some munging of all of the parameters above. # diff --git a/mk/rt.mk b/mk/rt.mk index b8f345699f45e..75051f9184fd2 100644 --- a/mk/rt.mk +++ b/mk/rt.mk @@ -184,8 +184,6 @@ $$(JEMALLOC_LOCAL_$(1)): $$(JEMALLOC_DEPS) $$(MKFILE_DEPS) EXTRA_CFLAGS="-g1 -ffunction-sections -fdata-sections" $$(Q)$$(MAKE) -C "$$(JEMALLOC_BUILD_DIR_$(1))" build_lib_static -ifeq ($$(CFG_DISABLE_JEMALLOC),) -RUSTFLAGS_alloc := --cfg jemalloc ifeq ($(1),$$(CFG_BUILD)) ifneq ($$(CFG_JEMALLOC_ROOT),) $$(JEMALLOC_LIB_$(1)): $$(CFG_JEMALLOC_ROOT)/libjemalloc_pic.a @@ -199,10 +197,6 @@ else $$(JEMALLOC_LIB_$(1)): $$(JEMALLOC_LOCAL_$(1)) $$(Q)cp $$< $$@ endif -else -$$(JEMALLOC_LIB_$(1)): $$(MKFILE_DEPS) - $$(Q)touch $$@ -endif ################################################################################ # compiler-rt diff --git a/mk/tests.mk b/mk/tests.mk index d4206acd27439..e0984cfe86f07 100644 --- a/mk/tests.mk +++ b/mk/tests.mk @@ -22,7 +22,8 @@ $(eval $(call RUST_CRATE,coretest)) DEPS_collectionstest := $(eval $(call RUST_CRATE,collectionstest)) -TEST_TARGET_CRATES = $(filter-out core rustc_unicode,$(TARGET_CRATES)) \ +TEST_TARGET_CRATES = $(filter-out core rustc_unicode alloc_system \ + alloc_jemalloc,$(TARGET_CRATES)) \ collectionstest coretest TEST_DOC_CRATES = $(DOC_CRATES) TEST_HOST_CRATES = $(filter-out rustc_typeck rustc_borrowck rustc_resolve \ diff --git a/src/liballoc/heap.rs b/src/liballoc/heap.rs index 14797d7f4b54d..fad8308f0f488 100644 --- a/src/liballoc/heap.rs +++ b/src/liballoc/heap.rs @@ -16,6 +16,18 @@ use core::{isize, usize}; +#[allow(improper_ctypes)] +extern { + #[allocator] + fn __rust_allocate(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; +} + #[inline(always)] fn check_size_and_alignment(size: usize, align: usize) { debug_assert!(size != 0); @@ -35,7 +47,7 @@ fn check_size_and_alignment(size: usize, align: usize) { #[inline] pub unsafe fn allocate(size: usize, align: usize) -> *mut u8 { check_size_and_alignment(size, align); - imp::allocate(size, align) + __rust_allocate(size, align) } /// Resize the allocation referenced by `ptr` to `size` bytes. @@ -55,7 +67,7 @@ pub unsafe fn allocate(size: usize, align: usize) -> *mut u8 { #[inline] pub unsafe fn reallocate(ptr: *mut u8, old_size: usize, size: usize, align: usize) -> *mut u8 { check_size_and_alignment(size, align); - imp::reallocate(ptr, old_size, size, align) + __rust_reallocate(ptr, old_size, size, align) } /// Resize the allocation referenced by `ptr` to `size` bytes. @@ -74,7 +86,7 @@ pub unsafe fn reallocate(ptr: *mut u8, old_size: usize, size: usize, align: usiz pub unsafe fn reallocate_inplace(ptr: *mut u8, old_size: usize, size: usize, align: usize) -> usize { check_size_and_alignment(size, align); - imp::reallocate_inplace(ptr, old_size, size, align) + __rust_reallocate_inplace(ptr, old_size, size, align) } /// Deallocates the memory referenced by `ptr`. @@ -86,28 +98,20 @@ pub unsafe fn reallocate_inplace(ptr: *mut u8, old_size: usize, size: usize, /// any value in range_inclusive(requested_size, usable_size). #[inline] pub unsafe fn deallocate(ptr: *mut u8, old_size: usize, align: usize) { - imp::deallocate(ptr, old_size, align) + __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 { - imp::usable_size(size, align) -} - -/// Prints implementation-defined allocator statistics. -/// -/// These statistics may be inconsistent if other threads use the allocator -/// during the call. -pub fn stats_print() { - imp::stats_print(); + unsafe { __rust_usable_size(size, align) } } /// An arbitrary non-null address to represent zero-size allocations. /// -/// This preserves the non-null invariant for types like `Box`. The address may overlap with -/// non-zero-size memory allocations. +/// This preserves the non-null invariant for types like `Box`. The address +/// may overlap with non-zero-size memory allocations. pub const EMPTY: *mut () = 0x1 as *mut (); /// The allocator for unique pointers. @@ -131,362 +135,6 @@ unsafe fn exchange_free(ptr: *mut u8, old_size: usize, align: usize) { deallocate(ptr, old_size, align); } -// The minimum alignment guaranteed by the architecture. This value is used to -// add fast paths for low alignment values. In practice, the alignment is a -// constant at the call site and the branch will be optimized out. -#[cfg(all(not(feature = "external_funcs"), - not(feature = "external_crate"), - any(target_arch = "arm", - target_arch = "mips", - target_arch = "mipsel", - target_arch = "powerpc")))] -const MIN_ALIGN: usize = 8; -#[cfg(all(not(feature = "external_funcs"), - not(feature = "external_crate"), - any(target_arch = "x86", - target_arch = "x86_64", - target_arch = "aarch64")))] -const MIN_ALIGN: usize = 16; - -#[cfg(feature = "external_funcs")] -mod imp { - #[allow(improper_ctypes)] - extern { - fn rust_allocate(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; - fn rust_stats_print(); - } - - #[inline] - pub unsafe fn allocate(size: usize, align: usize) -> *mut u8 { - rust_allocate(size, align) - } - - #[inline] - pub unsafe fn deallocate(ptr: *mut u8, old_size: usize, align: usize) { - rust_deallocate(ptr, old_size, align) - } - - #[inline] - pub unsafe fn reallocate(ptr: *mut u8, old_size: usize, size: usize, align: usize) -> *mut u8 { - rust_reallocate(ptr, old_size, size, align) - } - - #[inline] - pub unsafe fn reallocate_inplace(ptr: *mut u8, old_size: usize, size: usize, - align: usize) -> usize { - rust_reallocate_inplace(ptr, old_size, size, align) - } - - #[inline] - pub fn usable_size(size: usize, align: usize) -> usize { - unsafe { rust_usable_size(size, align) } - } - - #[inline] - pub fn stats_print() { - unsafe { rust_stats_print() } - } -} - -#[cfg(feature = "external_crate")] -mod imp { - extern crate external; - pub use self::external::{allocate, deallocate, reallocate_inplace, reallocate}; - pub use self::external::{usable_size, stats_print}; -} - -#[cfg(all(not(feature = "external_funcs"), - not(feature = "external_crate"), - jemalloc))] -mod imp { - use core::option::Option; - use core::option::Option::None; - use core::ptr::{null_mut, null}; - use libc::{c_char, c_int, c_void, size_t}; - use super::MIN_ALIGN; - - #[link(name = "jemalloc", kind = "static")] - #[cfg(not(test))] - extern {} - - extern { - #[allocator] - fn je_mallocx(size: size_t, flags: c_int) -> *mut c_void; - fn je_rallocx(ptr: *mut c_void, size: size_t, flags: c_int) -> *mut c_void; - fn je_xallocx(ptr: *mut c_void, size: size_t, extra: size_t, flags: c_int) -> size_t; - fn je_sdallocx(ptr: *mut c_void, size: size_t, flags: c_int); - fn je_nallocx(size: size_t, flags: c_int) -> size_t; - fn je_malloc_stats_print(write_cb: Option, - cbopaque: *mut c_void, - opts: *const c_char); - } - - // -lpthread needs to occur after -ljemalloc, the earlier argument isn't enough - #[cfg(all(not(windows), - not(target_os = "android"), - not(target_env = "musl")))] - #[link(name = "pthread")] - extern {} - - // MALLOCX_ALIGN(a) macro - #[inline(always)] - fn mallocx_align(a: usize) -> c_int { a.trailing_zeros() as c_int } - - #[inline(always)] - fn align_to_flags(align: usize) -> c_int { - if align <= MIN_ALIGN { 0 } else { mallocx_align(align) } - } - - #[inline] - pub unsafe fn allocate(size: usize, align: usize) -> *mut u8 { - let flags = align_to_flags(align); - je_mallocx(size as size_t, flags) as *mut u8 - } - - #[inline] - pub unsafe fn reallocate(ptr: *mut u8, _old_size: usize, size: usize, align: usize) -> *mut u8 { - let flags = align_to_flags(align); - je_rallocx(ptr as *mut c_void, size as size_t, flags) as *mut u8 - } - - #[inline] - pub unsafe fn reallocate_inplace(ptr: *mut u8, _old_size: usize, size: usize, - align: usize) -> usize { - let flags = align_to_flags(align); - je_xallocx(ptr as *mut c_void, size as size_t, 0, flags) as usize - } - - #[inline] - pub unsafe fn deallocate(ptr: *mut u8, old_size: usize, align: usize) { - let flags = align_to_flags(align); - je_sdallocx(ptr as *mut c_void, old_size as size_t, flags) - } - - #[inline] - pub fn usable_size(size: usize, align: usize) -> usize { - let flags = align_to_flags(align); - unsafe { je_nallocx(size as size_t, flags) as usize } - } - - pub fn stats_print() { - unsafe { - je_malloc_stats_print(None, null_mut(), null()) - } - } -} - -#[cfg(all(not(feature = "external_funcs"), - not(feature = "external_crate"), - not(jemalloc), - unix))] -mod imp { - use core::cmp; - use core::ptr; - use libc; - use super::MIN_ALIGN; - - extern { - fn posix_memalign(memptr: *mut *mut libc::c_void, - align: libc::size_t, - size: libc::size_t) -> libc::c_int; - } - - #[inline] - 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 { - let mut out = ptr::null_mut(); - let ret = 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 - } - } - } - - #[inline] - 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); - ptr::copy(ptr, new_ptr, cmp::min(size, old_size)); - deallocate(ptr, old_size, align); - new_ptr - } - } - - #[inline] - pub unsafe fn reallocate_inplace(_ptr: *mut u8, old_size: usize, _size: usize, - _align: usize) -> usize { - old_size - } - - #[inline] - pub unsafe fn deallocate(ptr: *mut u8, _old_size: usize, _align: usize) { - libc::free(ptr as *mut libc::c_void) - } - - #[inline] - pub fn usable_size(size: usize, _align: usize) -> usize { - size - } - - pub fn stats_print() {} -} - -#[cfg(all(not(feature = "external_funcs"), - not(feature = "external_crate"), - not(jemalloc), - windows))] -mod imp { - use core::mem::size_of; - use libc::{BOOL, DWORD, HANDLE, LPVOID, SIZE_T, INVALID_HANDLE_VALUE}; - use libc::{WriteFile}; - use super::MIN_ALIGN; - - extern "system" { - fn GetProcessHeap() -> HANDLE; - fn GetStdHandle(nStdHandle: DWORD) -> 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 HeapSummary(hHeap: HANDLE, dwFlags: DWORD, lpSummary: LPHEAP_SUMMARY) -> BOOL; - } - - #[repr(C)] #[allow(non_snake_case)] - struct HEAP_SUMMARY { - cb: DWORD, - cbAllocated: SIZE_T, - cbCommitted: SIZE_T, - cbReserved: SIZE_T, - cbMaxReserve: SIZE_T, - } - #[allow(non_camel_case_types)] - type LPHEAP_SUMMARY = *mut HEAP_SUMMARY; - - #[repr(C)] - struct Header(*mut u8); - - const HEAP_REALLOC_IN_PLACE_ONLY: DWORD = 0x00000010; - const STD_OUTPUT_HANDLE: DWORD = -11i32 as u32; - - #[inline] - unsafe fn get_header<'a>(ptr: *mut u8) -> &'a mut Header { - &mut *(ptr as *mut Header).offset(-1) - } - - #[inline] - 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] - pub unsafe fn allocate(size: usize, align: usize) -> *mut u8 { - if align <= MIN_ALIGN { - HeapAlloc(GetProcessHeap(), 0, size as SIZE_T) as *mut u8 - } else { - let ptr = HeapAlloc(GetProcessHeap(), 0, (size + align) as SIZE_T) as *mut u8; - if ptr.is_null() { return ptr } - align_ptr(ptr, align) - } - } - - #[inline] - 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 header = get_header(ptr); - let new = HeapReAlloc(GetProcessHeap(), 0, header.0 as LPVOID, - (size + align) as SIZE_T) as *mut u8; - if new.is_null() { return new } - align_ptr(new, align) - } - } - - #[inline] - pub unsafe fn reallocate_inplace(ptr: *mut u8, old_size: usize, size: usize, - align: usize) -> usize { - if align <= MIN_ALIGN { - let new = HeapReAlloc(GetProcessHeap(), HEAP_REALLOC_IN_PLACE_ONLY, ptr as LPVOID, - size as SIZE_T) as *mut u8; - if new.is_null() { old_size } else { size } - } else { - old_size - } - } - - #[inline] - 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); - } else { - let header = get_header(ptr); - let err = HeapFree(GetProcessHeap(), 0, header.0 as LPVOID); - debug_assert!(err != 0); - } - } - - #[inline] - pub fn usable_size(size: usize, _align: usize) -> usize { - size - } - - pub fn stats_print() { - use core::fmt::{Error, Result, Write}; - use core::ptr::null_mut; - use core::raw::Repr; - use core::result::Result::{Ok, Err}; - struct Console(HANDLE); - impl Write for Console { - fn write_str(&mut self, s: &str) -> Result { - let repr = s.repr(); - let mut written = 0; - let err = unsafe { WriteFile(self.0, repr.data as LPVOID, repr.len as DWORD, - &mut written, null_mut()) }; - if written as usize != repr.len { return Err(Error) } - if err == 0 { return Err(Error) } - Ok(()) - } - } - let mut hs = HEAP_SUMMARY { - cb: size_of::() as DWORD, - cbAllocated: 0, - cbCommitted: 0, - cbReserved: 0, - cbMaxReserve: 0, - }; - let err = unsafe { HeapSummary(GetProcessHeap(), 0, &mut hs) }; - assert!(err != 0); - let handle = unsafe { GetStdHandle(STD_OUTPUT_HANDLE) }; - if handle.is_null() || handle == INVALID_HANDLE_VALUE { panic!("Failed to open stdout") } - let mut out = Console(handle); - writeln!(&mut out, "Allocated: {}", hs.cbAllocated).unwrap(); - writeln!(&mut out, "Committed: {}", hs.cbCommitted).unwrap(); - writeln!(&mut out, "Reserved: {}", hs.cbReserved).unwrap(); - writeln!(&mut out, "MaxReserve: {}", hs.cbMaxReserve).unwrap(); - } - - #[test] - fn alignment_header_size() { - assert!(size_of::
() <= MIN_ALIGN); - } -} - #[cfg(test)] mod tests { extern crate test; diff --git a/src/liballoc/lib.rs b/src/liballoc/lib.rs index 05863c2ee5c9b..ca86850f5df6e 100644 --- a/src/liballoc/lib.rs +++ b/src/liballoc/lib.rs @@ -61,6 +61,7 @@ #![crate_name = "alloc"] #![crate_type = "rlib"] #![staged_api] +#![allow(unused_attributes)] #![unstable(feature = "alloc", reason = "this library is unlikely to be stabilized in its current \ form or name")] @@ -69,6 +70,7 @@ html_root_url = "https://doc.rust-lang.org/nightly/", test(no_crate_inject))] #![no_std] +#![cfg_attr(not(stage0), needs_allocator)] #![feature(allocator)] #![feature(box_syntax)] @@ -92,13 +94,13 @@ #![feature(unsize)] #![feature(core_slice_ext)] #![feature(core_str_ext)] +#![cfg_attr(stage0, feature(alloc_system))] +#![cfg_attr(not(stage0), feature(needs_allocator))] #![cfg_attr(test, feature(test, rustc_private, box_raw))] -#![cfg_attr(all(not(feature = "external_funcs"), not(feature = "external_crate")), - feature(libc))] -#[cfg(all(not(feature = "external_funcs"), not(feature = "external_crate")))] -extern crate libc; +#[cfg(stage0)] +extern crate alloc_system; // Allow testing this library diff --git a/src/liballoc_jemalloc/lib.rs b/src/liballoc_jemalloc/lib.rs new file mode 100644 index 0000000000000..2a30f88df49ac --- /dev/null +++ b/src/liballoc_jemalloc/lib.rs @@ -0,0 +1,96 @@ +// 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. + +#![cfg_attr(stage0, feature(custom_attribute))] +#![crate_name = "alloc_jemalloc"] +#![crate_type = "rlib"] +#![staged_api] +#![no_std] +#![cfg_attr(not(stage0), allocator)] +#![unstable(feature = "alloc_jemalloc", + reason = "this library is unlikely to be stabilized in its current \ + form or name")] +#![feature(allocator)] +#![feature(libc)] +#![feature(no_std)] +#![feature(staged_api)] + +extern crate libc; + +use libc::{c_int, c_void, size_t}; + +#[link(name = "jemalloc", kind = "static")] +extern { + fn je_mallocx(size: size_t, flags: c_int) -> *mut c_void; + fn je_rallocx(ptr: *mut c_void, size: size_t, flags: c_int) -> *mut c_void; + fn je_xallocx(ptr: *mut c_void, size: size_t, extra: size_t, + flags: c_int) -> size_t; + fn je_sdallocx(ptr: *mut c_void, size: size_t, flags: c_int); + fn je_nallocx(size: size_t, flags: c_int) -> size_t; +} + +// -lpthread needs to occur after -ljemalloc, the earlier argument isn't enough +#[cfg(all(not(windows), + not(target_os = "android"), + not(target_env = "musl")))] +#[link(name = "pthread")] +extern {} + +// The minimum alignment guaranteed by the architecture. This value is used to +// add fast paths for low alignment values. In practice, the alignment is a +// constant at the call site and the branch will be optimized out. +#[cfg(all(any(target_arch = "arm", + target_arch = "mips", + target_arch = "mipsel", + target_arch = "powerpc")))] +const MIN_ALIGN: usize = 8; +#[cfg(all(any(target_arch = "x86", + target_arch = "x86_64", + target_arch = "aarch64")))] +const MIN_ALIGN: usize = 16; + +// MALLOCX_ALIGN(a) macro +fn mallocx_align(a: usize) -> c_int { a.trailing_zeros() as c_int } + +fn align_to_flags(align: usize) -> c_int { + if align <= MIN_ALIGN { 0 } else { mallocx_align(align) } +} + +#[no_mangle] +pub extern fn __rust_allocate(size: usize, align: usize) -> *mut u8 { + let flags = align_to_flags(align); + unsafe { je_mallocx(size as size_t, flags) as *mut u8 } +} + +#[no_mangle] +pub extern fn __rust_reallocate(ptr: *mut u8, _old_size: usize, size: usize, + align: usize) -> *mut u8 { + let flags = align_to_flags(align); + unsafe { je_rallocx(ptr as *mut c_void, size as size_t, flags) as *mut u8 } +} + +#[no_mangle] +pub extern fn __rust_reallocate_inplace(ptr: *mut u8, _old_size: usize, + size: usize, align: usize) -> usize { + let flags = align_to_flags(align); + unsafe { je_xallocx(ptr as *mut c_void, size as size_t, 0, flags) as usize } +} + +#[no_mangle] +pub extern fn __rust_deallocate(ptr: *mut u8, old_size: usize, align: usize) { + let flags = align_to_flags(align); + unsafe { je_sdallocx(ptr as *mut c_void, old_size as size_t, flags) } +} + +#[no_mangle] +pub extern fn __rust_usable_size(size: usize, align: usize) -> usize { + let flags = align_to_flags(align); + unsafe { je_nallocx(size as size_t, flags) as usize } +} diff --git a/src/liballoc_system/lib.rs b/src/liballoc_system/lib.rs new file mode 100644 index 0000000000000..0687ced7da1eb --- /dev/null +++ b/src/liballoc_system/lib.rs @@ -0,0 +1,212 @@ +// 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. + +#![cfg_attr(stage0, feature(custom_attribute))] +#![crate_name = "alloc_system"] +#![crate_type = "rlib"] +#![staged_api] +#![no_std] +#![cfg_attr(not(stage0), allocator)] +#![unstable(feature = "alloc_system", + reason = "this library is unlikely to be stabilized in its current \ + form or name")] +#![feature(allocator)] +#![feature(libc)] +#![feature(no_std)] +#![feature(staged_api)] + +extern crate libc; + +// The minimum alignment guaranteed by the architecture. This value is used to +// add fast paths for low alignment values. In practice, the alignment is a +// constant at the call site and the branch will be optimized out. +#[cfg(all(any(target_arch = "arm", + target_arch = "mips", + target_arch = "mipsel", + target_arch = "powerpc")))] +const MIN_ALIGN: usize = 8; +#[cfg(all(any(target_arch = "x86", + target_arch = "x86_64", + target_arch = "aarch64")))] +const MIN_ALIGN: usize = 16; + +#[no_mangle] +pub extern fn __rust_allocate(size: usize, align: usize) -> *mut u8 { + unsafe { imp::allocate(size, align) } +} + +#[no_mangle] +pub extern fn __rust_deallocate(ptr: *mut u8, old_size: usize, align: usize) { + unsafe { imp::deallocate(ptr, old_size, align) } +} + +#[no_mangle] +pub extern fn __rust_reallocate(ptr: *mut u8, old_size: usize, size: usize, + align: usize) -> *mut u8 { + unsafe { imp::reallocate(ptr, old_size, size, align) } +} + +#[no_mangle] +pub extern fn __rust_reallocate_inplace(ptr: *mut u8, old_size: usize, + size: usize, align: usize) -> usize { + unsafe { imp::reallocate_inplace(ptr, old_size, size, align) } +} + +#[no_mangle] +pub extern fn __rust_usable_size(size: usize, align: usize) -> usize { + imp::usable_size(size, align) +} + +#[cfg(unix)] +mod imp { + use core::cmp; + use core::ptr; + use libc; + use MIN_ALIGN; + + extern { + // Apparently android doesn't have posix_memalign + #[cfg(target_os = "android")] + fn memalign(align: libc::size_t, size: libc::size_t) -> *mut libc::c_void; + + #[cfg(not(target_os = "android"))] + fn posix_memalign(memptr: *mut *mut libc::c_void, + align: libc::size_t, + size: libc::size_t) -> libc::c_int; + } + + 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 { + #[cfg(target_os = "android")] + unsafe fn more_aligned_malloc(size: usize, align: usize) -> *mut u8 { + memalign(align as libc::size_t, size as libc::size_t) as *mut u8 + } + #[cfg(not(target_os = "android"))] + unsafe fn more_aligned_malloc(size: usize, align: usize) -> *mut u8 { + let mut out = ptr::null_mut(); + let ret = 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 + } + } + more_aligned_malloc(size, align) + } + } + + 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); + 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)] +mod imp { + use libc::{BOOL, DWORD, HANDLE, LPVOID, SIZE_T}; + use MIN_ALIGN; + + 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; + } + + #[repr(C)] + struct Header(*mut u8); + + const HEAP_REALLOC_IN_PLACE_ONLY: DWORD = 0x00000010; + + 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 + } + + pub unsafe fn allocate(size: usize, align: usize) -> *mut u8 { + if align <= MIN_ALIGN { + HeapAlloc(GetProcessHeap(), 0, size as SIZE_T) as *mut u8 + } else { + let ptr = HeapAlloc(GetProcessHeap(), 0, + (size + align) as SIZE_T) as *mut u8; + if ptr.is_null() { return ptr } + align_ptr(ptr, align) + } + } + + 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 header = get_header(ptr); + let new = HeapReAlloc(GetProcessHeap(), 0, header.0 as LPVOID, + (size + align) as SIZE_T) as *mut u8; + if new.is_null() { return new } + align_ptr(new, align) + } + } + + pub unsafe fn reallocate_inplace(ptr: *mut u8, old_size: usize, size: usize, + align: usize) -> usize { + if align <= MIN_ALIGN { + let new = HeapReAlloc(GetProcessHeap(), HEAP_REALLOC_IN_PLACE_ONLY, + ptr as LPVOID, size as SIZE_T) as *mut u8; + if new.is_null() { old_size } else { size } + } else { + old_size + } + } + + 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); + } else { + let header = get_header(ptr); + let err = HeapFree(GetProcessHeap(), 0, header.0 as LPVOID); + debug_assert!(err != 0); + } + } + + pub fn usable_size(size: usize, _align: usize) -> usize { + size + } +} diff --git a/src/librustc/metadata/common.rs b/src/librustc/metadata/common.rs index abcff6e78e2c6..3e0cf30fe942f 100644 --- a/src/librustc/metadata/common.rs +++ b/src/librustc/metadata/common.rs @@ -78,12 +78,13 @@ pub const tag_crate_crate_name: usize = 0x104; // top-level only pub const tag_crate_dep_crate_name: usize = 0x36; pub const tag_crate_dep_hash: usize = 0x37; +pub const tag_crate_dep_explicitly_linked: usize = 0x38; // top-level only -pub const tag_mod_impl: usize = 0x38; +pub const tag_mod_impl: usize = 0x39; -pub const tag_item_trait_item: usize = 0x39; +pub const tag_item_trait_item: usize = 0x3a; -pub const tag_item_trait_ref: usize = 0x3a; +pub const tag_item_trait_ref: usize = 0x3b; // discriminator value for variants pub const tag_disr_val: usize = 0x3c; diff --git a/src/librustc/metadata/creader.rs b/src/librustc/metadata/creader.rs index 3226a99c6b3ae..c34b2ea58dcc8 100644 --- a/src/librustc/metadata/creader.rs +++ b/src/librustc/metadata/creader.rs @@ -22,7 +22,7 @@ use metadata::loader; use metadata::loader::CratePaths; use util::nodemap::FnvHashMap; -use std::cell::RefCell; +use std::cell::{RefCell, Cell}; use std::path::PathBuf; use std::rc::Rc; use std::fs; @@ -59,15 +59,16 @@ impl<'a, 'b, 'v> visit::Visitor<'v> for LocalCrateReader<'a, 'b> { } fn dump_crates(cstore: &CStore) { - debug!("resolved crates:"); + info!("resolved crates:"); cstore.iter_crate_data_origins(|_, data, opt_source| { - debug!(" name: {}", data.name()); - debug!(" cnum: {}", data.cnum); - debug!(" hash: {}", data.hash()); + info!(" name: {}", data.name()); + info!(" cnum: {}", data.cnum); + info!(" hash: {}", data.hash()); + info!(" reqd: {}", data.explicitly_linked.get()); opt_source.map(|cs| { let CrateSource { dylib, rlib, cnum: _ } = cs; - dylib.map(|dl| debug!(" dylib: {}", dl.0.display())); - rlib.map(|rl| debug!(" rlib: {}", rl.0.display())); + dylib.map(|dl| info!(" dylib: {}", dl.0.display())); + rlib.map(|rl| info!(" rlib: {}", rl.0.display())); }); }) } @@ -241,7 +242,8 @@ impl<'a> CrateReader<'a> { ident: &str, name: &str, span: Span, - lib: loader::Library) + lib: loader::Library, + explicitly_linked: bool) -> (ast::CrateNum, Rc, cstore::CrateSource) { // Claim this crate number and cache it @@ -266,15 +268,16 @@ impl<'a> CrateReader<'a> { let cnum_map = self.resolve_crate_deps(root, metadata.as_slice(), span); let staged_api = self.is_staged_api(metadata.as_slice()); - let cmeta = Rc::new( cstore::crate_metadata { + let cmeta = Rc::new(cstore::crate_metadata { name: name.to_string(), local_path: RefCell::new(SmallVector::zero()), data: metadata, - cnum_map: cnum_map, + cnum_map: RefCell::new(cnum_map), cnum: cnum, codemap_import_info: RefCell::new(vec![]), span: span, - staged_api: staged_api + staged_api: staged_api, + explicitly_linked: Cell::new(explicitly_linked), }); let source = cstore::CrateSource { @@ -305,7 +308,8 @@ impl<'a> CrateReader<'a> { name: &str, hash: Option<&Svh>, span: Span, - kind: PathKind) + kind: PathKind, + explicitly_linked: bool) -> (ast::CrateNum, Rc, cstore::CrateSource) { match self.existing_match(name, hash, kind) { @@ -326,11 +330,16 @@ impl<'a> CrateReader<'a> { should_match_name: true, }; let library = load_ctxt.load_library_crate(); - self.register_crate(root, ident, name, span, library) + self.register_crate(root, ident, name, span, library, + explicitly_linked) + } + Some(cnum) => { + let data = self.sess.cstore.get_crate_data(cnum); + if explicitly_linked && !data.explicitly_linked.get() { + data.explicitly_linked.set(explicitly_linked); + } + (cnum, data, self.sess.cstore.get_used_crate_source(cnum).unwrap()) } - Some(cnum) => (cnum, - self.sess.cstore.get_crate_data(cnum), - self.sess.cstore.get_used_crate_source(cnum).unwrap()) } } @@ -349,7 +358,8 @@ impl<'a> CrateReader<'a> { &dep.name, Some(&dep.hash), span, - PathKind::Dependency); + PathKind::Dependency, + dep.explicitly_linked); (dep.cnum, local_cnum) }).collect() } @@ -399,7 +409,8 @@ impl<'a> CrateReader<'a> { let metadata = if register { // Register crate now to avoid double-reading metadata let (_, cmd, _) = self.register_crate(&None, &info.ident, - &info.name, span, library); + &info.name, span, library, + true); PMDSource::Registered(cmd) } else { // Not registering the crate; just hold on to the metadata @@ -507,6 +518,124 @@ impl<'a> CrateReader<'a> { } } } + + 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; + self.sess.cstore.iter_crate_data(|cnum, data| { + needs_allocator = needs_allocator || data.needs_allocator(); + if data.is_allocator() { + debug!("{} required by rlib and is an allocator", data.name()); + self.inject_allocator_dependency(cnum); + found_required_allocator = found_required_allocator || + data.explicitly_linked.get(); + } + }); + if !needs_allocator || found_required_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. + let mut need_lib_alloc = false; + let mut need_exe_alloc = false; + for ct in self.sess.crate_types.borrow().iter() { + match *ct { + config::CrateTypeExecutable => need_exe_alloc = true, + config::CrateTypeDylib | + config::CrateTypeStaticlib => need_lib_alloc = true, + config::CrateTypeRlib => {} + } + } + 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. + // + // 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 { + &self.sess.target.target.options.lib_allocation_crate + } else { + &self.sess.target.target.options.exe_allocation_crate + }; + let (cnum, data, _) = self.resolve_crate(&None, name, name, None, + codemap::DUMMY_SP, + PathKind::Crate, false); + + // To ensure that the `-Z allocation-crate=foo` option isn't abused, and + // to ensure that the allocator is indeed an allocator, we verify that + // the crate loaded here is indeed tagged #![allocator]. + if !data.is_allocator() { + self.sess.err(&format!("the allocator crate `{}` is not tagged \ + with #![allocator]", data.name())); + } + + self.sess.injected_allocator.set(Some(cnum)); + self.inject_allocator_dependency(cnum); + } + + fn inject_allocator_dependency(&self, allocator: ast::CrateNum) { + // Before we inject any dependencies, make sure we don't inject a + // circular dependency by validating that this allocator crate doesn't + // transitively depend on any `#![needs_allocator]` crates. + validate(self, allocator, allocator); + + // All crates tagged with `needs_allocator` do not explicitly depend on + // the allocator selected for this compile, but in order for this + // compilation to be successfully linked we need to inject a dependency + // (to order the crates on the command line correctly). + // + // Here we inject a dependency from all crates with #![needs_allocator] + // to the crate tagged with #![allocator] for this compilation unit. + self.sess.cstore.iter_crate_data(|cnum, data| { + if !data.needs_allocator() { + return + } + + info!("injecting a dep from {} to {}", cnum, allocator); + let mut cnum_map = data.cnum_map.borrow_mut(); + let remote_cnum = cnum_map.len() + 1; + let prev = cnum_map.insert(remote_cnum as ast::CrateNum, allocator); + assert!(prev.is_none()); + }); + + fn validate(me: &CrateReader, krate: ast::CrateNum, + allocator: ast::CrateNum) { + let data = me.sess.cstore.get_crate_data(krate); + if data.needs_allocator() { + let krate_name = data.name(); + let data = me.sess.cstore.get_crate_data(allocator); + let alloc_name = data.name(); + me.sess.err(&format!("the allocator crate `{}` cannot depend \ + on a crate that needs an allocator, but \ + it depends on `{}`", alloc_name, + krate_name)); + } + + for (_, &dep) in data.cnum_map.borrow().iter() { + validate(me, dep, allocator); + } + } + } } impl<'a, 'b> LocalCrateReader<'a, 'b> { @@ -524,8 +653,9 @@ impl<'a, 'b> LocalCrateReader<'a, 'b> { pub fn read_crates(&mut self, krate: &ast::Crate) { self.process_crate(krate); visit::walk_crate(self, krate); + self.creader.inject_allocator_crate(); - if log_enabled!(log::DEBUG) { + if log_enabled!(log::INFO) { dump_crates(&self.sess.cstore); } @@ -558,7 +688,8 @@ impl<'a, 'b> LocalCrateReader<'a, 'b> { &info.name, None, i.span, - PathKind::Crate); + PathKind::Crate, + true); self.ast_map.with_path(i.id, |path| { cmeta.update_local_path(path) }); diff --git a/src/librustc/metadata/cstore.rs b/src/librustc/metadata/cstore.rs index ae5e797a0299b..9179a0a1871c7 100644 --- a/src/librustc/metadata/cstore.rs +++ b/src/librustc/metadata/cstore.rs @@ -22,11 +22,12 @@ use metadata::{creader, decoder, loader}; use session::search_paths::PathKind; use util::nodemap::{FnvHashMap, NodeMap, NodeSet}; -use std::cell::{RefCell, Ref}; +use std::cell::{RefCell, Ref, Cell}; use std::rc::Rc; use std::path::PathBuf; use flate::Bytes; use syntax::ast; +use syntax::attr; use syntax::codemap; use syntax::parse::token; use syntax::parse::token::IdentInterner; @@ -59,11 +60,17 @@ pub struct crate_metadata { pub name: String, pub local_path: RefCell>, pub data: MetadataBlob, - pub cnum_map: cnum_map, + pub cnum_map: RefCell, pub cnum: ast::CrateNum, pub codemap_import_info: RefCell>, pub span: codemap::Span, - pub staged_api: bool + pub staged_api: bool, + + /// Flag if this crate is required by an rlib version of this crate, or in + /// other words whether it was explicitly linked to. An example of a crate + /// where this is false is when an allocator crate is injected into the + /// dependency list, and therefore isn't actually needed to link an rlib. + pub explicitly_linked: Cell, } #[derive(Copy, Debug, PartialEq, Clone)] @@ -132,10 +139,10 @@ impl CStore { } pub fn iter_crate_data(&self, mut i: I) where - I: FnMut(ast::CrateNum, &crate_metadata), + I: FnMut(ast::CrateNum, &Rc), { for (&k, v) in self.metas.borrow().iter() { - i(k, &**v); + i(k, v); } } @@ -188,7 +195,7 @@ impl CStore { ordering: &mut Vec) { if ordering.contains(&cnum) { return } let meta = cstore.get_crate_data(cnum); - for (_, &dep) in &meta.cnum_map { + for (_, &dep) in meta.cnum_map.borrow().iter() { visit(cstore, dep, ordering); } ordering.push(cnum); @@ -196,6 +203,7 @@ impl CStore { for (&num, _) in self.metas.borrow().iter() { visit(self, num, &mut ordering); } + info!("topological ordering: {:?}", ordering); ordering.reverse(); let mut libs = self.used_crate_sources.borrow() .iter() @@ -271,8 +279,10 @@ impl crate_metadata { filemaps } } + pub fn with_local_path(&self, f: F) -> T - where F: Fn(&[ast_map::PathElem]) -> T { + where F: Fn(&[ast_map::PathElem]) -> T + { let cpath = self.local_path.borrow(); if cpath.is_empty() { let name = ast_map::PathMod(token::intern(&self.name)); @@ -281,6 +291,7 @@ impl crate_metadata { f(cpath.as_slice()) } } + pub fn update_local_path<'a, 'b>(&self, candidate: ast_map::PathElems<'a, 'b>) { let mut cpath = self.local_path.borrow_mut(); let cap = cpath.len(); @@ -295,6 +306,16 @@ impl crate_metadata { }, } } + + pub fn is_allocator(&self) -> bool { + let attrs = decoder::get_crate_attributes(self.data()); + attr::contains_name(&attrs, "allocator") + } + + pub fn needs_allocator(&self) -> bool { + let attrs = decoder::get_crate_attributes(self.data()); + attr::contains_name(&attrs, "needs_allocator") + } } impl MetadataBlob { diff --git a/src/librustc/metadata/decoder.rs b/src/librustc/metadata/decoder.rs index c6c18fa14a340..fdd45251711d2 100644 --- a/src/librustc/metadata/decoder.rs +++ b/src/librustc/metadata/decoder.rs @@ -1174,6 +1174,7 @@ pub struct CrateDep { pub cnum: ast::CrateNum, pub name: String, pub hash: Svh, + pub explicitly_linked: bool, } pub fn get_crate_deps(data: &[u8]) -> Vec { @@ -1188,10 +1189,13 @@ pub fn get_crate_deps(data: &[u8]) -> Vec { reader::tagged_docs(depsdoc, tag_crate_dep).enumerate().map(|(crate_num, depdoc)| { let name = docstr(depdoc, tag_crate_dep_crate_name); let hash = Svh::new(&docstr(depdoc, tag_crate_dep_hash)); + let doc = reader::get_doc(depdoc, tag_crate_dep_explicitly_linked); + let explicitly_linked = reader::doc_as_u8(doc) != 0; CrateDep { cnum: crate_num as u32 + 1, name: name, hash: hash, + explicitly_linked: explicitly_linked, } }).collect() } @@ -1252,7 +1256,7 @@ pub fn translate_def_id(cdata: Cmd, did: ast::DefId) -> ast::DefId { return ast::DefId { krate: cdata.cnum, node: did.node }; } - match cdata.cnum_map.get(&did.krate) { + match cdata.cnum_map.borrow().get(&did.krate) { Some(&n) => { ast::DefId { krate: n, @@ -1270,7 +1274,7 @@ fn reverse_translate_def_id(cdata: Cmd, did: ast::DefId) -> Option { return Some(ast::DefId { krate: ast::LOCAL_CRATE, node: did.node }); } - for (&local, &global) in &cdata.cnum_map { + for (&local, &global) in cdata.cnum_map.borrow().iter() { if global == did.krate { return Some(ast::DefId { krate: local, node: did.node }); } @@ -1385,7 +1389,7 @@ pub fn get_dylib_dependency_formats(cdata: Cmd) let cnum = spec.split(':').nth(0).unwrap(); let link = spec.split(':').nth(1).unwrap(); let cnum: ast::CrateNum = cnum.parse().unwrap(); - let cnum = match cdata.cnum_map.get(&cnum) { + let cnum = match cdata.cnum_map.borrow().get(&cnum) { Some(&n) => n, None => panic!("didn't find a crate in the cnum_map") }; diff --git a/src/librustc/metadata/encoder.rs b/src/librustc/metadata/encoder.rs index e0f35b6817b4f..cc723d94f74d1 100644 --- a/src/librustc/metadata/encoder.rs +++ b/src/librustc/metadata/encoder.rs @@ -23,8 +23,9 @@ use metadata::cstore; use metadata::decoder; use metadata::tyencode; use middle::def; -use middle::ty::{self, Ty}; +use middle::dependency_format::Linkage; use middle::stability; +use middle::ty::{self, Ty}; use util::nodemap::{FnvHashMap, NodeMap, NodeSet}; use serialize::Encodable; @@ -32,6 +33,7 @@ use std::cell::RefCell; use std::hash::{Hash, Hasher, SipHasher}; use std::io::prelude::*; use std::io::{Cursor, SeekFrom}; +use std::rc::Rc; use syntax::abi; use syntax::ast::{self, DefId, NodeId}; use syntax::ast_util::*; @@ -1751,25 +1753,21 @@ fn encode_polarity(rbml_w: &mut Encoder, polarity: ast::ImplPolarity) { } fn encode_crate_deps(rbml_w: &mut Encoder, cstore: &cstore::CStore) { - fn get_ordered_deps(cstore: &cstore::CStore) -> Vec { + fn get_ordered_deps(cstore: &cstore::CStore) + -> Vec<(ast::CrateNum, Rc)> { // Pull the cnums and name,vers,hash out of cstore let mut deps = Vec::new(); - cstore.iter_crate_data(|key, val| { - let dep = decoder::CrateDep { - cnum: key, - name: decoder::get_crate_name(val.data()), - hash: decoder::get_crate_hash(val.data()), - }; - deps.push(dep); + cstore.iter_crate_data(|cnum, val| { + deps.push((cnum, val.clone())); }); // Sort by cnum - deps.sort_by(|kv1, kv2| kv1.cnum.cmp(&kv2.cnum)); + deps.sort_by(|kv1, kv2| kv1.0.cmp(&kv2.0)); // Sanity-check the crate numbers let mut expected_cnum = 1; - for n in &deps { - assert_eq!(n.cnum, expected_cnum); + for &(n, _) in &deps { + assert_eq!(n, expected_cnum); expected_cnum += 1; } @@ -1781,8 +1779,8 @@ fn encode_crate_deps(rbml_w: &mut Encoder, cstore: &cstore::CStore) { // FIXME (#2166): This is not nearly enough to support correct versioning // but is enough to get transitive crate dependencies working. rbml_w.start_tag(tag_crate_deps); - for dep in &get_ordered_deps(cstore) { - encode_crate_dep(rbml_w, dep); + for (_cnum, dep) in get_ordered_deps(cstore) { + encode_crate_dep(rbml_w, &dep); } rbml_w.end_tag(); } @@ -1985,10 +1983,13 @@ fn encode_reachable(ecx: &EncodeContext, rbml_w: &mut Encoder) { } fn encode_crate_dep(rbml_w: &mut Encoder, - dep: &decoder::CrateDep) { + dep: &cstore::crate_metadata) { rbml_w.start_tag(tag_crate_dep); - rbml_w.wr_tagged_str(tag_crate_dep_crate_name, &dep.name); - rbml_w.wr_tagged_str(tag_crate_dep_hash, dep.hash.as_str()); + rbml_w.wr_tagged_str(tag_crate_dep_crate_name, &dep.name()); + let hash = decoder::get_crate_hash(dep.data()); + rbml_w.wr_tagged_str(tag_crate_dep_hash, hash.as_str()); + rbml_w.wr_tagged_u8(tag_crate_dep_explicitly_linked, + dep.explicitly_linked.get() as u8); rbml_w.end_tag(); } @@ -2006,13 +2007,16 @@ fn encode_crate_triple(rbml_w: &mut Encoder, triple: &str) { fn encode_dylib_dependency_formats(rbml_w: &mut Encoder, ecx: &EncodeContext) { let tag = tag_dylib_dependency_formats; - match ecx.tcx.dependency_formats.borrow().get(&config::CrateTypeDylib) { + match ecx.tcx.sess.dependency_formats.borrow().get(&config::CrateTypeDylib) { Some(arr) => { let s = arr.iter().enumerate().filter_map(|(i, slot)| { - slot.map(|kind| (format!("{}:{}", i + 1, match kind { - cstore::RequireDynamic => "d", - cstore::RequireStatic => "s", - })).to_string()) + let kind = match *slot { + Linkage::NotLinked | + Linkage::IncludedFromDylib => return None, + Linkage::Dynamic => "d", + Linkage::Static => "s", + }; + Some(format!("{}:{}", i + 1, kind)) }).collect::>(); rbml_w.wr_tagged_str(tag, &s.join(",")); } diff --git a/src/librustc/middle/dependency_format.rs b/src/librustc/middle/dependency_format.rs index ff5178fbefc10..125e9285b52b5 100644 --- a/src/librustc/middle/dependency_format.rs +++ b/src/librustc/middle/dependency_format.rs @@ -67,7 +67,6 @@ use session; use session::config; use metadata::cstore; use metadata::csearch; -use middle::ty; use util::nodemap::FnvHashMap; /// A list of dependencies for a certain crate type. @@ -76,19 +75,29 @@ use util::nodemap::FnvHashMap; /// The value is None if the crate does not need to be linked (it was found /// statically in another dylib), or Some(kind) if it needs to be linked as /// `kind` (either static or dynamic). -pub type DependencyList = Vec>; +pub type DependencyList = Vec; /// A mapping of all required dependencies for a particular flavor of output. /// /// This is local to the tcx, and is generally relevant to one session. pub type Dependencies = FnvHashMap; -pub fn calculate(tcx: &ty::ctxt) { - let mut fmts = tcx.dependency_formats.borrow_mut(); - for &ty in tcx.sess.crate_types.borrow().iter() { - fmts.insert(ty, calculate_type(&tcx.sess, ty)); +#[derive(Copy, Clone, PartialEq, Debug)] +pub enum Linkage { + NotLinked, + IncludedFromDylib, + Static, + Dynamic, +} + +pub fn calculate(sess: &session::Session) { + let mut fmts = sess.dependency_formats.borrow_mut(); + for &ty in sess.crate_types.borrow().iter() { + let linkage = calculate_type(sess, ty); + verify_ok(sess, &linkage); + fmts.insert(ty, linkage); } - tcx.sess.abort_if_errors(); + sess.abort_if_errors(); } fn calculate_type(sess: &session::Session, @@ -145,12 +154,12 @@ fn calculate_type(sess: &session::Session, sess.cstore.iter_crate_data(|cnum, data| { let src = sess.cstore.get_used_crate_source(cnum).unwrap(); if src.dylib.is_some() { - debug!("adding dylib: {}", data.name); + info!("adding dylib: {}", data.name); add_library(sess, cnum, cstore::RequireDynamic, &mut formats); let deps = csearch::get_dylib_dependency_formats(&sess.cstore, cnum); for &(depnum, style) in &deps { - debug!("adding {:?}: {}", style, - sess.cstore.get_crate_data(depnum).name.clone()); + info!("adding {:?}: {}", style, + sess.cstore.get_crate_data(depnum).name.clone()); add_library(sess, depnum, style, &mut formats); } } @@ -158,24 +167,36 @@ fn calculate_type(sess: &session::Session, // Collect what we've got so far in the return vector. let mut ret = (1..sess.cstore.next_crate_num()).map(|i| { - match formats.get(&i).cloned() { - v @ Some(cstore::RequireDynamic) => v, - _ => None, + match formats.get(&i) { + Some(&cstore::RequireDynamic) => Linkage::Dynamic, + Some(&cstore::RequireStatic) => Linkage::IncludedFromDylib, + None => Linkage::NotLinked, } }).collect::>(); // Run through the dependency list again, and add any missing libraries as // static libraries. + // + // If the crate hasn't been included yet and it's not actually required + // (e.g. it's an allocator) then we skip it here as well. sess.cstore.iter_crate_data(|cnum, data| { let src = sess.cstore.get_used_crate_source(cnum).unwrap(); - if src.dylib.is_none() && !formats.contains_key(&cnum) { + if src.dylib.is_none() && + !formats.contains_key(&cnum) && + data.explicitly_linked.get() { assert!(src.rlib.is_some()); - debug!("adding staticlib: {}", data.name); + info!("adding staticlib: {}", data.name); add_library(sess, cnum, cstore::RequireStatic, &mut formats); - ret[cnum as usize - 1] = Some(cstore::RequireStatic); + ret[cnum as usize - 1] = Linkage::Static; } }); + // We've gotten this far because we're emitting some form of a final + // artifact which means that we're going to need an allocator of some form. + // No allocator may have been required or linked so far, so activate one + // here if one isn't set. + activate_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 // found metadata), but the dylib for A has since been removed. @@ -183,21 +204,22 @@ fn calculate_type(sess: &session::Session, // For situations like this, we perform one last pass over the dependencies, // making sure that everything is available in the requested format. for (cnum, kind) in ret.iter().enumerate() { - let cnum = cnum as ast::CrateNum; - let src = sess.cstore.get_used_crate_source(cnum + 1).unwrap(); + let cnum = (cnum + 1) as ast::CrateNum; + let src = sess.cstore.get_used_crate_source(cnum).unwrap(); match *kind { - None => continue, - Some(cstore::RequireStatic) if src.rlib.is_some() => continue, - Some(cstore::RequireDynamic) if src.dylib.is_some() => continue, - Some(kind) => { - let data = sess.cstore.get_crate_data(cnum + 1); + Linkage::NotLinked | + Linkage::IncludedFromDylib => {} + Linkage::Static if src.rlib.is_some() => continue, + Linkage::Dynamic if src.dylib.is_some() => continue, + kind => { + let kind = match kind { + Linkage::Static => "rlib", + _ => "dylib", + }; + let data = sess.cstore.get_crate_data(cnum); sess.err(&format!("crate `{}` required to be available in {}, \ but it was not available in this form", - data.name, - match kind { - cstore::RequireStatic => "rlib", - cstore::RequireDynamic => "dylib", - })); + data.name, kind)); } } } @@ -221,8 +243,7 @@ fn add_library(sess: &session::Session, if link2 != link || link == cstore::RequireStatic { let data = sess.cstore.get_crate_data(cnum); sess.err(&format!("cannot satisfy dependencies so `{}` only \ - shows up once", - data.name)); + shows up once", data.name)); sess.help("having upstream crates all available in one format \ will likely make this go away"); } @@ -233,9 +254,79 @@ fn add_library(sess: &session::Session, fn attempt_static(sess: &session::Session) -> Option { let crates = sess.cstore.get_used_crates(cstore::RequireStatic); - if crates.iter().by_ref().all(|&(_, ref p)| p.is_some()) { - Some(crates.into_iter().map(|_| Some(cstore::RequireStatic)).collect()) - } else { - None + if !crates.iter().by_ref().all(|&(_, ref p)| p.is_some()) { + return None + } + + // All crates are available in an rlib format, so we're just going to link + // everything in explicitly so long as it's actually required. + let mut ret = (1..sess.cstore.next_crate_num()).map(|cnum| { + if sess.cstore.get_crate_data(cnum).explicitly_linked.get() { + Linkage::Static + } else { + Linkage::NotLinked + } + }).collect::>(); + + // Our allocator may not have been activated as it's not flagged with + // explicitly_linked, so flag it here if necessary. + activate_allocator(sess, &mut ret); + + Some(ret) +} + +// Given a list of how to link upstream dependencies so far, ensure that an +// allocator is activated. This will not do anything if one was transitively +// included already (e.g. via a dylib or explicitly so). +// +// If an allocator was not found then we're guaranteed the metadata::creader +// module has injected an allocator dependency (not listed as a required +// dependency) in the session's `injected_allocator` field. If this field is not +// set then this compilation doesn't actually need an allocator and we can also +// skip this step entirely. +fn activate_allocator(sess: &session::Session, list: &mut DependencyList) { + let mut allocator_found = false; + for (i, slot) in list.iter().enumerate() { + let cnum = (i + 1) as ast::CrateNum; + if !sess.cstore.get_crate_data(cnum).is_allocator() { + continue + } + if let Linkage::NotLinked = *slot { + continue + } + allocator_found = true; + } + if !allocator_found { + if let Some(injected_allocator) = sess.injected_allocator.get() { + let idx = injected_allocator as usize - 1; + assert_eq!(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(sess: &session::Session, list: &[Linkage]) { + if list.len() == 0 { + return + } + let mut allocator = None; + for (i, linkage) in list.iter().enumerate() { + let cnum = (i + 1) as ast::CrateNum; + let data = sess.cstore.get_crate_data(cnum); + if !data.is_allocator() { + continue + } + if let Linkage::NotLinked = *linkage { + continue + } + if let Some(prev_alloc) = allocator { + let prev = sess.cstore.get_crate_data(prev_alloc); + sess.err(&format!("cannot link together two \ + allocators: {} and {}", + prev.name(), data.name())); + } + allocator = Some(cnum); } } diff --git a/src/librustc/middle/ty.rs b/src/librustc/middle/ty.rs index 49bf0e580b9e0..fefd78560455c 100644 --- a/src/librustc/middle/ty.rs +++ b/src/librustc/middle/ty.rs @@ -47,7 +47,6 @@ use middle::check_const; use middle::const_eval::{self, ConstVal, ErrKind}; use middle::const_eval::EvalHint::UncheckedExprHint; use middle::def::{self, DefMap, ExportMap}; -use middle::dependency_format; use middle::fast_reject; use middle::free_region::FreeRegionMap; use middle::lang_items::{FnTraitLangItem, FnMutTraitLangItem, FnOnceTraitLangItem}; @@ -840,8 +839,6 @@ pub struct ctxt<'tcx> { pub extern_const_variants: RefCell>, pub extern_const_fns: RefCell>, - pub dependency_formats: RefCell, - pub node_lint_levels: RefCell>, @@ -3837,7 +3834,6 @@ impl<'tcx> ctxt<'tcx> { extern_const_statics: RefCell::new(DefIdMap()), extern_const_variants: RefCell::new(DefIdMap()), extern_const_fns: RefCell::new(DefIdMap()), - dependency_formats: RefCell::new(FnvHashMap()), node_lint_levels: RefCell::new(FnvHashMap()), transmute_restrictions: RefCell::new(Vec::new()), stability: RefCell::new(stability), diff --git a/src/librustc/session/mod.rs b/src/librustc/session/mod.rs index ffb3a8ccb36ba..18982215f588f 100644 --- a/src/librustc/session/mod.rs +++ b/src/librustc/session/mod.rs @@ -11,8 +11,9 @@ use lint; use metadata::cstore::CStore; use metadata::filesearch; +use middle::dependency_format; use session::search_paths::PathKind; -use util::nodemap::NodeMap; +use util::nodemap::{NodeMap, FnvHashMap}; use syntax::ast::NodeId; use syntax::codemap::Span; @@ -57,6 +58,7 @@ pub struct Session { pub plugin_llvm_passes: RefCell>, pub plugin_attributes: RefCell>, pub crate_types: RefCell>, + pub dependency_formats: RefCell, pub crate_metadata: RefCell>, pub features: RefCell, @@ -68,7 +70,11 @@ pub struct Session { pub can_print_warnings: bool, - next_node_id: Cell + /// The metadata::creader module may inject an allocator dependency if it + /// didn't already find one, and this tracks what was injected. + pub injected_allocator: Cell>, + + next_node_id: Cell, } impl Session { @@ -447,12 +453,14 @@ pub fn build_session_(sopts: config::Options, plugin_llvm_passes: RefCell::new(Vec::new()), plugin_attributes: RefCell::new(Vec::new()), crate_types: RefCell::new(Vec::new()), + dependency_formats: RefCell::new(FnvHashMap()), crate_metadata: RefCell::new(Vec::new()), delayed_span_bug: RefCell::new(None), features: RefCell::new(feature_gate::Features::new()), recursion_limit: Cell::new(64), can_print_warnings: can_print_warnings, - next_node_id: Cell::new(1) + next_node_id: Cell::new(1), + injected_allocator: Cell::new(None), }; sess diff --git a/src/librustc/util/common.rs b/src/librustc/util/common.rs index beb95697201d6..2314368177cb7 100644 --- a/src/librustc/util/common.rs +++ b/src/librustc/util/common.rs @@ -32,11 +32,11 @@ pub const FN_OUTPUT_NAME: &'static str = "Output"; #[derive(Clone, Copy, Debug)] pub struct ErrorReported; -pub fn time(do_it: bool, what: &str, u: U, f: F) -> T where - F: FnOnce(U) -> T, +pub fn time(do_it: bool, what: &str, f: F) -> T where + F: FnOnce() -> T, { thread_local!(static DEPTH: Cell = Cell::new(0)); - if !do_it { return f(u); } + if !do_it { return f(); } let old = DEPTH.with(|slot| { let r = slot.get(); @@ -49,7 +49,7 @@ pub fn time(do_it: bool, what: &str, u: U, f: F) -> T where let ref mut rvp = rv; Duration::span(move || { - *rvp = Some(f(u)) + *rvp = Some(f()) }) }; let rv = rv.unwrap(); diff --git a/src/librustc_back/target/apple_base.rs b/src/librustc_back/target/apple_base.rs index a1cbf838d0af3..0c4e28d970008 100644 --- a/src/librustc_back/target/apple_base.rs +++ b/src/librustc_back/target/apple_base.rs @@ -24,6 +24,7 @@ pub fn opts() -> TargetOptions { dll_suffix: ".dylib".to_string(), archive_format: "bsd".to_string(), pre_link_args: Vec::new(), + exe_allocation_crate: super::best_allocator(), .. Default::default() } } diff --git a/src/librustc_back/target/bitrig_base.rs b/src/librustc_back/target/bitrig_base.rs index 9e163f7b64f16..96680dc375965 100644 --- a/src/librustc_back/target/bitrig_base.rs +++ b/src/librustc_back/target/bitrig_base.rs @@ -20,6 +20,7 @@ pub fn opts() -> TargetOptions { has_rpath: true, position_independent_executables: true, archive_format: "gnu".to_string(), + exe_allocation_crate: super::best_allocator(), .. Default::default() } diff --git a/src/librustc_back/target/dragonfly_base.rs b/src/librustc_back/target/dragonfly_base.rs index 422aabd8c9853..736035c5389d7 100644 --- a/src/librustc_back/target/dragonfly_base.rs +++ b/src/librustc_back/target/dragonfly_base.rs @@ -29,6 +29,7 @@ pub fn opts() -> TargetOptions { ), position_independent_executables: true, archive_format: "bsd".to_string(), + exe_allocation_crate: super::best_allocator(), .. Default::default() } } diff --git a/src/librustc_back/target/freebsd_base.rs b/src/librustc_back/target/freebsd_base.rs index 28512ba08fa33..a5807d2787fdd 100644 --- a/src/librustc_back/target/freebsd_base.rs +++ b/src/librustc_back/target/freebsd_base.rs @@ -18,6 +18,7 @@ pub fn opts() -> TargetOptions { executables: true, has_rpath: true, archive_format: "gnu".to_string(), + exe_allocation_crate: super::best_allocator(), .. Default::default() } diff --git a/src/librustc_back/target/linux_base.rs b/src/librustc_back/target/linux_base.rs index a70cbc5797c68..d6b50a955b682 100644 --- a/src/librustc_back/target/linux_base.rs +++ b/src/librustc_back/target/linux_base.rs @@ -29,6 +29,7 @@ pub fn opts() -> TargetOptions { ], position_independent_executables: true, archive_format: "gnu".to_string(), + exe_allocation_crate: super::best_allocator(), .. Default::default() } } diff --git a/src/librustc_back/target/mod.rs b/src/librustc_back/target/mod.rs index 7ca46a1d169ca..d9cfdaacc9059 100644 --- a/src/librustc_back/target/mod.rs +++ b/src/librustc_back/target/mod.rs @@ -173,6 +173,10 @@ pub struct TargetOptions { /// defined in libgcc. If this option is enabled, the target must provide /// `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, } impl Default for TargetOptions { @@ -211,6 +215,8 @@ impl Default for TargetOptions { post_link_objects: Vec::new(), archive_format: String::new(), custom_unwind_resume: false, + lib_allocation_crate: "alloc_system".to_string(), + exe_allocation_crate: "alloc_system".to_string(), } } } @@ -424,3 +430,11 @@ impl Target { Err(format!("Could not find specification for target {:?}", target)) } } + +fn best_allocator() -> String { + if cfg!(disable_jemalloc) { + "alloc_system".to_string() + } else { + "alloc_jemalloc".to_string() + } +} diff --git a/src/librustc_back/target/openbsd_base.rs b/src/librustc_back/target/openbsd_base.rs index 361f71f699591..3b02111d93444 100644 --- a/src/librustc_back/target/openbsd_base.rs +++ b/src/librustc_back/target/openbsd_base.rs @@ -27,6 +27,7 @@ pub fn opts() -> TargetOptions { ), position_independent_executables: true, archive_format: "gnu".to_string(), + exe_allocation_crate: super::best_allocator(), .. Default::default() } } diff --git a/src/librustc_back/target/windows_base.rs b/src/librustc_back/target/windows_base.rs index d2217f902eac8..fedae51e0e894 100644 --- a/src/librustc_back/target/windows_base.rs +++ b/src/librustc_back/target/windows_base.rs @@ -60,6 +60,7 @@ pub fn opts() -> TargetOptions { // Always enable DEP (NX bit) when it is available "-Wl,--nxcompat".to_string(), ), + exe_allocation_crate: super::best_allocator(), .. Default::default() } diff --git a/src/librustc_back/target/windows_msvc_base.rs b/src/librustc_back/target/windows_msvc_base.rs index 2466c254e0517..fe9ac32ee8fb5 100644 --- a/src/librustc_back/target/windows_msvc_base.rs +++ b/src/librustc_back/target/windows_msvc_base.rs @@ -60,6 +60,7 @@ pub fn opts() -> TargetOptions { "/NXCOMPAT".to_string(), ], archive_format: "gnu".to_string(), + exe_allocation_crate: super::best_allocator(), .. Default::default() } diff --git a/src/librustc_driver/driver.rs b/src/librustc_driver/driver.rs index 80db6426917e0..263a8e1480702 100644 --- a/src/librustc_driver/driver.rs +++ b/src/librustc_driver/driver.rs @@ -356,7 +356,7 @@ pub fn phase_1_parse_input(sess: &Session, cfg: ast::CrateConfig, input: &Input) syntax::ext::mtwt::reset_tables(); token::reset_ident_interner(); - let krate = time(sess.time_passes(), "parsing", (), |_| { + let krate = time(sess.time_passes(), "parsing", || { match *input { Input::File(ref file) => { parse::parse_crate_from_file(&(*file), cfg.clone(), &sess.parse_sess) @@ -406,7 +406,7 @@ pub fn phase_2_configure_and_expand(sess: &Session, // // baz! should not use this definition unless foo is enabled. - krate = time(time_passes, "configuration 1", krate, |krate| + krate = time(time_passes, "configuration 1", move || syntax::config::strip_unconfigured_items(sess.diagnostic(), krate)); *sess.crate_types.borrow_mut() = @@ -414,11 +414,11 @@ pub fn phase_2_configure_and_expand(sess: &Session, *sess.crate_metadata.borrow_mut() = collect_crate_metadata(sess, &krate.attrs); - time(time_passes, "recursion limit", (), |_| { + time(time_passes, "recursion limit", || { middle::recursion_limit::update_recursion_limit(sess, &krate); }); - time(time_passes, "gated macro checking", (), |_| { + time(time_passes, "gated macro checking", || { let features = syntax::feature_gate::check_crate_macros(sess.codemap(), &sess.parse_sess.span_diagnostic, @@ -430,20 +430,20 @@ pub fn phase_2_configure_and_expand(sess: &Session, }); - krate = time(time_passes, "crate injection", krate, |krate| + krate = time(time_passes, "crate injection", || syntax::std_inject::maybe_inject_crates_ref(krate, sess.opts.alt_std_name.clone())); - let macros = time(time_passes, "macro loading", (), |_| + let macros = time(time_passes, "macro loading", || metadata::macro_import::read_macro_defs(sess, &krate)); let mut addl_plugins = Some(addl_plugins); - let registrars = time(time_passes, "plugin loading", (), |_| + let registrars = time(time_passes, "plugin loading", || plugin::load::load_plugins(sess, &krate, addl_plugins.take().unwrap())); let mut registry = Registry::new(sess, &krate); - time(time_passes, "plugin registration", registrars, |registrars| { + time(time_passes, "plugin registration", || { if sess.features.borrow().rustc_diagnostic_macros { registry.register_macro("__diagnostic_used", diagnostics::plugin::expand_diagnostic_used); @@ -486,45 +486,43 @@ pub fn phase_2_configure_and_expand(sess: &Session, // Abort if there are errors from lint processing or a plugin registrar. sess.abort_if_errors(); - krate = time(time_passes, "expansion", (krate, macros, syntax_exts), - |(krate, macros, syntax_exts)| { - // Windows dlls do not have rpaths, so they don't know how to find their - // dependencies. It's up to us to tell the system where to find all the - // dependent dlls. Note that this uses cfg!(windows) as opposed to - // targ_cfg because syntax extensions are always loaded for the host - // compiler, not for the target. - let mut _old_path = OsString::new(); - if cfg!(windows) { - _old_path = env::var_os("PATH").unwrap_or(_old_path); - let mut new_path = sess.host_filesearch(PathKind::All) - .get_dylib_search_paths(); - new_path.extend(env::split_paths(&_old_path)); - env::set_var("PATH", &env::join_paths(new_path).unwrap()); - } - let features = sess.features.borrow(); - let cfg = syntax::ext::expand::ExpansionConfig { - crate_name: crate_name.to_string(), - features: Some(&features), - recursion_limit: sess.recursion_limit.get(), - trace_mac: sess.opts.debugging_opts.trace_macros, - }; - let ret = syntax::ext::expand::expand_crate(&sess.parse_sess, - cfg, - macros, - syntax_exts, - krate); - if cfg!(windows) { - env::set_var("PATH", &_old_path); - } - ret + krate = time(time_passes, "expansion", || { + // Windows dlls do not have rpaths, so they don't know how to find their + // dependencies. It's up to us to tell the system where to find all the + // dependent dlls. Note that this uses cfg!(windows) as opposed to + // targ_cfg because syntax extensions are always loaded for the host + // compiler, not for the target. + let mut _old_path = OsString::new(); + if cfg!(windows) { + _old_path = env::var_os("PATH").unwrap_or(_old_path); + let mut new_path = sess.host_filesearch(PathKind::All) + .get_dylib_search_paths(); + new_path.extend(env::split_paths(&_old_path)); + env::set_var("PATH", &env::join_paths(new_path).unwrap()); } - ); + let features = sess.features.borrow(); + let cfg = syntax::ext::expand::ExpansionConfig { + crate_name: crate_name.to_string(), + features: Some(&features), + recursion_limit: sess.recursion_limit.get(), + trace_mac: sess.opts.debugging_opts.trace_macros, + }; + let ret = syntax::ext::expand::expand_crate(&sess.parse_sess, + cfg, + macros, + syntax_exts, + krate); + if cfg!(windows) { + env::set_var("PATH", &_old_path); + } + ret + }); // Needs to go *after* expansion to be able to check the results // of macro expansion. This runs before #[cfg] to try to catch as // much as possible (e.g. help the programmer avoid platform // specific differences) - time(time_passes, "complete gated feature checking 1", (), |_| { + time(time_passes, "complete gated feature checking 1", || { let features = syntax::feature_gate::check_crate(sess.codemap(), &sess.parse_sess.span_diagnostic, @@ -537,25 +535,25 @@ pub fn phase_2_configure_and_expand(sess: &Session, // JBC: make CFG processing part of expansion to avoid this problem: // strip again, in case expansion added anything with a #[cfg]. - krate = time(time_passes, "configuration 2", krate, |krate| + krate = time(time_passes, "configuration 2", || syntax::config::strip_unconfigured_items(sess.diagnostic(), krate)); - krate = time(time_passes, "maybe building test harness", krate, |krate| + krate = time(time_passes, "maybe building test harness", || syntax::test::modify_for_testing(&sess.parse_sess, &sess.opts.cfg, krate, sess.diagnostic())); - krate = time(time_passes, "prelude injection", krate, |krate| + krate = time(time_passes, "prelude injection", || syntax::std_inject::maybe_inject_prelude(&sess.parse_sess, krate)); - time(time_passes, "checking that all macro invocations are gone", &krate, |krate| - syntax::ext::expand::check_for_macros(&sess.parse_sess, krate)); + time(time_passes, "checking that all macro invocations are gone", || + syntax::ext::expand::check_for_macros(&sess.parse_sess, &krate)); // One final feature gating of the true AST that gets compiled // later, to make sure we've got everything (e.g. configuration // can insert new attributes via `cfg_attr`) - time(time_passes, "complete gated feature checking 2", (), |_| { + time(time_passes, "complete gated feature checking 2", || { let features = syntax::feature_gate::check_crate(sess.codemap(), &sess.parse_sess.span_diagnostic, @@ -582,7 +580,7 @@ pub fn assign_node_ids_and_map<'ast>(sess: &Session, } } - let map = time(sess.time_passes(), "assigning node ids and indexing ast", forest, |forest| + let map = time(sess.time_passes(), "assigning node ids and indexing ast", move || ast_map::map_crate(forest, NodeIdAssigner { sess: sess })); if sess.opts.debugging_opts.ast_json { @@ -608,10 +606,10 @@ pub fn phase_3_run_analysis_passes<'tcx, F, R>(sess: Session, let time_passes = sess.time_passes(); let krate = ast_map.krate(); - time(time_passes, "external crate/lib resolution", (), |_| + time(time_passes, "external crate/lib resolution", || LocalCrateReader::new(&sess, &ast_map).read_crates(krate)); - let lang_items = time(time_passes, "language item collection", (), |_| + let lang_items = time(time_passes, "language item collection", || middle::lang_items::collect_language_items(krate, &sess)); let resolve::CrateMap { @@ -622,30 +620,30 @@ pub fn phase_3_run_analysis_passes<'tcx, F, R>(sess: Session, external_exports, glob_map, } = - time(time_passes, "resolution", (), - |_| resolve::resolve_crate(&sess, &ast_map, make_glob_map)); + time(time_passes, "resolution", + || resolve::resolve_crate(&sess, &ast_map, make_glob_map)); // Discard MTWT tables that aren't required past resolution. syntax::ext::mtwt::clear_tables(); - let named_region_map = time(time_passes, "lifetime resolution", (), - |_| middle::resolve_lifetime::krate(&sess, krate, &def_map)); + let named_region_map = time(time_passes, "lifetime resolution", + || middle::resolve_lifetime::krate(&sess, krate, &def_map)); - time(time_passes, "looking for entry point", (), - |_| middle::entry::find_entry_point(&sess, &ast_map)); + time(time_passes, "looking for entry point", + || middle::entry::find_entry_point(&sess, &ast_map)); sess.plugin_registrar_fn.set( - time(time_passes, "looking for plugin registrar", (), |_| + time(time_passes, "looking for plugin registrar", || plugin::build::find_plugin_registrar( sess.diagnostic(), krate))); - let region_map = time(time_passes, "region resolution", (), |_| + let region_map = time(time_passes, "region resolution", || middle::region::resolve_crate(&sess, krate)); - time(time_passes, "loop checking", (), |_| + time(time_passes, "loop checking", || middle::check_loop::check_crate(&sess, krate)); - time(time_passes, "static item recursion checking", (), |_| + time(time_passes, "static item recursion checking", || middle::check_static_recursion::check_crate(&sess, krate, &def_map, &ast_map)); ty::ctxt::create_and_enter(sess, @@ -662,33 +660,33 @@ pub fn phase_3_run_analysis_passes<'tcx, F, R>(sess: Session, // passes are timed inside typeck typeck::check_crate(tcx, trait_map); - time(time_passes, "const checking", (), |_| + time(time_passes, "const checking", || middle::check_const::check_crate(tcx)); let (exported_items, public_items) = - time(time_passes, "privacy checking", (), |_| + time(time_passes, "privacy checking", || rustc_privacy::check_crate(tcx, &export_map, external_exports)); // Do not move this check past lint - time(time_passes, "stability index", (), |_| + time(time_passes, "stability index", || tcx.stability.borrow_mut().build(tcx, krate, &public_items)); - time(time_passes, "intrinsic checking", (), |_| + time(time_passes, "intrinsic checking", || middle::intrinsicck::check_crate(tcx)); - time(time_passes, "effect checking", (), |_| + time(time_passes, "effect checking", || middle::effect::check_crate(tcx)); - time(time_passes, "match checking", (), |_| + time(time_passes, "match checking", || middle::check_match::check_crate(tcx)); - time(time_passes, "liveness checking", (), |_| + time(time_passes, "liveness checking", || middle::liveness::check_crate(tcx)); - time(time_passes, "borrow checking", (), |_| + time(time_passes, "borrow checking", || borrowck::check_crate(tcx)); - time(time_passes, "rvalue checking", (), |_| + time(time_passes, "rvalue checking", || middle::check_rvalues::check_crate(tcx, krate)); // Avoid overwhelming user with errors if type checking failed. @@ -699,24 +697,24 @@ pub fn phase_3_run_analysis_passes<'tcx, F, R>(sess: Session, tcx.sess.abort_if_errors(); let reachable_map = - time(time_passes, "reachability checking", (), |_| + time(time_passes, "reachability checking", || reachable::find_reachable(tcx, &exported_items)); - time(time_passes, "death checking", (), |_| { + time(time_passes, "death checking", || { middle::dead::check_crate(tcx, &exported_items, &reachable_map) }); let ref lib_features_used = - time(time_passes, "stability checking", (), |_| + time(time_passes, "stability checking", || stability::check_unstable_api_usage(tcx)); - time(time_passes, "unused lib feature checking", (), |_| + time(time_passes, "unused lib feature checking", || stability::check_unused_or_stable_features( &tcx.sess, lib_features_used)); - time(time_passes, "lint checking", (), |_| + time(time_passes, "lint checking", || lint::check_crate(tcx, &exported_items)); // The above three passes generate errors w/o aborting @@ -739,11 +737,11 @@ pub fn phase_4_translate_to_llvm(tcx: &ty::ctxt, analysis: ty::CrateAnalysis) -> trans::CrateTranslation { let time_passes = tcx.sess.time_passes(); - time(time_passes, "resolving dependency formats", (), |_| - dependency_format::calculate(tcx)); + time(time_passes, "resolving dependency formats", || + dependency_format::calculate(&tcx.sess)); // Option dance to work around the lack of stack once closures. - time(time_passes, "translation", analysis, |analysis| + time(time_passes, "translation", move || trans::trans_crate(tcx, analysis)) } @@ -755,7 +753,7 @@ pub fn phase_5_run_llvm_passes(sess: &Session, if sess.opts.cg.no_integrated_as { let output_type = config::OutputTypeAssembly; - time(sess.time_passes(), "LLVM passes", (), |_| + time(sess.time_passes(), "LLVM passes", || write::run_passes(sess, trans, &[output_type], outputs)); write::run_assembler(sess, outputs); @@ -765,7 +763,7 @@ pub fn phase_5_run_llvm_passes(sess: &Session, fs::remove_file(&outputs.temp_path(config::OutputTypeAssembly)).unwrap(); } } else { - time(sess.time_passes(), "LLVM passes", (), |_| + time(sess.time_passes(), "LLVM passes", || write::run_passes(sess, trans, &sess.opts.output_types, @@ -780,7 +778,7 @@ pub fn phase_5_run_llvm_passes(sess: &Session, pub fn phase_6_link_output(sess: &Session, trans: &trans::CrateTranslation, outputs: &OutputFilenames) { - time(sess.time_passes(), "linking", (), |_| + time(sess.time_passes(), "linking", || link::link_binary(sess, trans, outputs, diff --git a/src/librustc_driver/lib.rs b/src/librustc_driver/lib.rs index 230307f0b1932..5bb013c5f9662 100644 --- a/src/librustc_driver/lib.rs +++ b/src/librustc_driver/lib.rs @@ -383,10 +383,10 @@ impl<'a> CompilerCalls<'a> for RustcDefaultCalls { if sess.opts.debugging_opts.save_analysis { control.after_analysis.callback = box |state| { time(state.session.time_passes(), - "save analysis", (), - |_| save::process_crate(state.tcx.unwrap(), - state.analysis.unwrap(), - state.out_dir)); + "save analysis", + || save::process_crate(state.tcx.unwrap(), + state.analysis.unwrap(), + state.out_dir)); }; control.make_glob_map = resolve::MakeGlobMap::Yes; } diff --git a/src/librustc_trans/back/link.rs b/src/librustc_trans/back/link.rs index 46c7b80670f3c..91c0edb901f69 100644 --- a/src/librustc_trans/back/link.rs +++ b/src/librustc_trans/back/link.rs @@ -23,6 +23,7 @@ use metadata::common::LinkMeta; use metadata::filesearch::FileDoesntMatch; use metadata::loader::METADATA_FILENAME; use metadata::{encoder, cstore, filesearch, csearch, creader}; +use middle::dependency_format::Linkage; use middle::ty::{self, Ty}; use rustc::ast_map::{PathElem, PathElems, PathName}; use trans::{CrateContext, CrateTranslation, gensym_name}; @@ -493,6 +494,31 @@ pub fn filename_for_input(sess: &Session, } } +pub fn each_linked_rlib(sess: &Session, + f: &mut FnMut(ast::CrateNum, &Path)) { + let crates = sess.cstore.get_used_crates(cstore::RequireStatic).into_iter(); + let fmts = sess.dependency_formats.borrow(); + let fmts = fmts.get(&config::CrateTypeExecutable).or_else(|| { + fmts.get(&config::CrateTypeStaticlib) + }).unwrap_or_else(|| { + sess.bug("could not find formats for rlibs") + }); + for (cnum, path) in crates { + match fmts[cnum as usize - 1] { + Linkage::NotLinked | Linkage::IncludedFromDylib => continue, + _ => {} + } + let name = sess.cstore.get_crate_data(cnum).name.clone(); + let path = match path { + Some(p) => p, + None => { + sess.fatal(&format!("could not find rlib for: `{}`", name)); + } + }; + f(cnum, &path); + } +} + fn link_binary_output(sess: &Session, trans: &CrateTranslation, crate_type: config::CrateType, @@ -524,11 +550,11 @@ fn link_binary_output(sess: &Session, link_staticlib(sess, &objects, &out_filename, tmpdir.path()); } config::CrateTypeExecutable => { - link_natively(sess, trans, false, &objects, &out_filename, outputs, + link_natively(sess, false, &objects, &out_filename, trans, outputs, tmpdir.path()); } config::CrateTypeDylib => { - link_natively(sess, trans, true, &objects, &out_filename, outputs, + link_natively(sess, true, &objects, &out_filename, trans, outputs, tmpdir.path()); } } @@ -763,23 +789,15 @@ fn link_staticlib(sess: &Session, objects: &[PathBuf], out_filename: &Path, ab.add_native_library("compiler-rt").unwrap(); } - let crates = sess.cstore.get_used_crates(cstore::RequireStatic); let mut all_native_libs = vec![]; - for &(cnum, ref path) in &crates { - let ref name = sess.cstore.get_crate_data(cnum).name; - let p = match *path { - Some(ref p) => p.clone(), None => { - sess.err(&format!("could not find rlib for: `{}`", - name)); - continue - } - }; - ab.add_rlib(&p, &name[..], sess.lto()).unwrap(); + each_linked_rlib(sess, &mut |cnum, path| { + let name = sess.cstore.get_crate_data(cnum).name(); + ab.add_rlib(path, &name, sess.lto()).unwrap(); let native_libs = csearch::get_native_libraries(&sess.cstore, cnum); all_native_libs.extend(native_libs); - } + }); ab.update_symbols(); ab.build(); @@ -805,8 +823,9 @@ fn link_staticlib(sess: &Session, objects: &[PathBuf], out_filename: &Path, // // This will invoke the system linker/cc to create the resulting file. This // links to all upstream files as well. -fn link_natively(sess: &Session, trans: &CrateTranslation, dylib: bool, +fn link_natively(sess: &Session, dylib: bool, objects: &[PathBuf], out_filename: &Path, + trans: &CrateTranslation, outputs: &OutputFilenames, tmpdir: &Path) { info!("preparing dylib? ({}) from {:?} to {:?}", dylib, objects, @@ -829,7 +848,7 @@ fn link_natively(sess: &Session, trans: &CrateTranslation, dylib: bool, Box::new(GnuLinker { cmd: &mut cmd, sess: &sess }) as Box }; link_args(&mut *linker, sess, dylib, tmpdir, - trans, objects, out_filename, outputs); + objects, out_filename, trans, outputs); if !sess.target.target.options.no_compiler_rt { linker.link_staticlib("compiler-rt"); } @@ -848,7 +867,7 @@ fn link_natively(sess: &Session, trans: &CrateTranslation, dylib: bool, // Invoke the system linker info!("{:?}", &cmd); - let prog = time(sess.time_passes(), "running linker", (), |()| cmd.output()); + let prog = time(sess.time_passes(), "running linker", || cmd.output()); match prog { Ok(prog) => { if !prog.status.success() { @@ -884,9 +903,9 @@ fn link_args(cmd: &mut Linker, sess: &Session, dylib: bool, tmpdir: &Path, - trans: &CrateTranslation, objects: &[PathBuf], out_filename: &Path, + trans: &CrateTranslation, outputs: &OutputFilenames) { // The default library location, we need this to find the runtime. @@ -980,7 +999,7 @@ fn link_args(cmd: &mut Linker, // this kind of behavior is pretty platform specific and generally not // recommended anyway, so I don't think we're shooting ourself in the foot // much with that. - add_upstream_rust_crates(cmd, sess, dylib, tmpdir, trans); + add_upstream_rust_crates(cmd, sess, dylib, tmpdir); add_local_native_libraries(cmd, sess); add_upstream_native_libraries(cmd, sess); @@ -1086,8 +1105,7 @@ fn add_local_native_libraries(cmd: &mut Linker, sess: &Session) { // dependencies will be linked when producing the final output (instead of // the intermediate rlib version) fn add_upstream_rust_crates(cmd: &mut Linker, sess: &Session, - dylib: bool, tmpdir: &Path, - trans: &CrateTranslation) { + dylib: bool, tmpdir: &Path) { // All of the heavy lifting has previously been accomplished by the // dependency_format module of the compiler. This is just crawling the // output of that module, adding crates as necessary. @@ -1096,10 +1114,11 @@ fn add_upstream_rust_crates(cmd: &mut Linker, sess: &Session, // will slurp up the object files inside), and linking to a dynamic library // involves just passing the right -l flag. + let formats = sess.dependency_formats.borrow(); let data = if dylib { - trans.crate_formats.get(&config::CrateTypeDylib).unwrap() + formats.get(&config::CrateTypeDylib).unwrap() } else { - trans.crate_formats.get(&config::CrateTypeExecutable).unwrap() + formats.get(&config::CrateTypeExecutable).unwrap() }; // Invoke get_used_crates to ensure that we get a topological sorting of @@ -1110,20 +1129,17 @@ fn add_upstream_rust_crates(cmd: &mut Linker, sess: &Session, // We may not pass all crates through to the linker. Some crates may // appear statically in an existing dylib, meaning we'll pick up all the // symbols from the dylib. - let kind = match data[cnum as usize - 1] { - Some(t) => t, - None => continue - }; let src = sess.cstore.get_used_crate_source(cnum).unwrap(); - match kind { - cstore::RequireDynamic => { - add_dynamic_crate(cmd, sess, &src.dylib.unwrap().0) - } - cstore::RequireStatic => { + match data[cnum as usize - 1] { + Linkage::NotLinked | + Linkage::IncludedFromDylib => {} + Linkage::Static => { add_static_crate(cmd, sess, tmpdir, dylib, &src.rlib.unwrap().0) } + Linkage::Dynamic => { + add_dynamic_crate(cmd, sess, &src.dylib.unwrap().0) + } } - } // Converts a library file-stem into a cc -l argument @@ -1174,7 +1190,7 @@ fn add_upstream_rust_crates(cmd: &mut Linker, sess: &Session, let name = cratepath.file_name().unwrap().to_str().unwrap(); let name = &name[3..name.len() - 5]; // chop off lib/.rlib - time(sess.time_passes(), &format!("altering {}.rlib", name), (), |()| { + time(sess.time_passes(), &format!("altering {}.rlib", name), || { let cfg = archive_config(sess, &dst, Some(cratepath)); let mut archive = ArchiveBuilder::new(cfg); archive.remove_file(METADATA_FILENAME); diff --git a/src/librustc_trans/back/linker.rs b/src/librustc_trans/back/linker.rs index 8bd86a3a34a19..a4333dc10d637 100644 --- a/src/librustc_trans/back/linker.rs +++ b/src/librustc_trans/back/linker.rs @@ -17,7 +17,7 @@ use std::process::Command; use back::archive; use metadata::csearch; -use metadata::cstore; +use middle::dependency_format::Linkage; use session::Session; use session::config::DebugInfoLevel::{NoDebugInfo, LimitedDebugInfo, FullDebugInfo}; use session::config::CrateTypeDylib; @@ -347,9 +347,10 @@ impl<'a> Linker for MsvcLinker<'a> { // dynamic library. For all statically linked libraries we take all // their reachable symbols and emit them as well. let cstore = &sess.cstore; - let symbols = trans.crate_formats[&CrateTypeDylib].iter(); + let formats = sess.dependency_formats.borrow(); + let symbols = formats[&CrateTypeDylib].iter(); let symbols = symbols.enumerate().filter_map(|(i, f)| { - if let Some(cstore::RequireStatic) = *f { + if *f == Linkage::Static { Some((i + 1) as ast::CrateNum) } else { None diff --git a/src/librustc_trans/back/lto.rs b/src/librustc_trans/back/lto.rs index 518add44b4c4a..00439c1fd5878 100644 --- a/src/librustc_trans/back/lto.rs +++ b/src/librustc_trans/back/lto.rs @@ -14,7 +14,6 @@ use rustc::session::{self, config}; use llvm; use llvm::archive_ro::ArchiveRO; use llvm::{ModuleRef, TargetMachineRef, True, False}; -use rustc::metadata::cstore; use rustc::util::common::time; use back::write::{ModuleConfig, with_llvm_pmb}; @@ -46,17 +45,7 @@ pub fn run(sess: &session::Session, llmod: ModuleRef, // For each of our upstream dependencies, find the corresponding rlib and // load the bitcode from the archive. Then merge it into the current LLVM // module that we've got. - let crates = sess.cstore.get_used_crates(cstore::RequireStatic); - for (cnum, path) in crates { - let name = sess.cstore.get_crate_data(cnum).name.clone(); - let path = match path { - Some(p) => p, - None => { - sess.fatal(&format!("could not find rlib for: `{}`", - name)); - } - }; - + link::each_linked_rlib(sess, &mut |_, path| { let archive = ArchiveRO::open(&path).expect("wanted an rlib"); let bytecodes = archive.iter().filter_map(|child| { child.name().map(|name| (name, child)) @@ -65,7 +54,7 @@ pub fn run(sess: &session::Session, llmod: ModuleRef, let bc_encoded = data.data(); let bc_decoded = if is_versioned_bytecode_format(bc_encoded) { - time(sess.time_passes(), &format!("decode {}", name), (), |_| { + time(sess.time_passes(), &format!("decode {}", name), || { // Read the version let version = extract_bytecode_format_version(bc_encoded); @@ -89,9 +78,10 @@ pub fn run(sess: &session::Session, llmod: ModuleRef, } }) } else { - time(sess.time_passes(), &format!("decode {}", name), (), |_| { - // the object must be in the old, pre-versioning format, so simply - // inflate everything and let LLVM decide if it can make sense of it + time(sess.time_passes(), &format!("decode {}", name), || { + // the object must be in the old, pre-versioning format, so + // simply inflate everything and let LLVM decide if it can + // make sense of it match flate::inflate_bytes(bc_encoded) { Ok(bc) => bc, Err(_) => { @@ -104,8 +94,7 @@ pub fn run(sess: &session::Session, llmod: ModuleRef, let ptr = bc_decoded.as_ptr(); debug!("linking {}", name); - time(sess.time_passes(), &format!("ll link {}", name), (), - |()| unsafe { + time(sess.time_passes(), &format!("ll link {}", name), || unsafe { if !llvm::LLVMRustLinkInExternalBitcode(llmod, ptr as *const libc::c_char, bc_decoded.len() as libc::size_t) { @@ -115,7 +104,7 @@ pub fn run(sess: &session::Session, llmod: ModuleRef, } }); } - } + }); // Internalize everything but the reachable symbols of the current module let cstrs: Vec = reachable.iter().map(|s| { @@ -154,7 +143,7 @@ pub fn run(sess: &session::Session, llmod: ModuleRef, llvm::LLVMRustAddPass(pm, "verify\0".as_ptr() as *const _); - time(sess.time_passes(), "LTO passes", (), |()| + time(sess.time_passes(), "LTO passes", || llvm::LLVMRunPassManager(pm, llmod)); llvm::LLVMDisposePassManager(pm); diff --git a/src/librustc_trans/back/write.rs b/src/librustc_trans/back/write.rs index d1fb0c2e8d79e..d75f2fcf126a9 100644 --- a/src/librustc_trans/back/write.rs +++ b/src/librustc_trans/back/write.rs @@ -484,9 +484,9 @@ unsafe fn optimize_and_codegen(cgcx: &CodegenContext, cgcx.handler.abort_if_errors(); // Finally, run the actual optimization passes - time(config.time_passes, "llvm function passes", (), |()| + time(config.time_passes, "llvm function passes", || llvm::LLVMRustRunFunctionPassManager(fpm, llmod)); - time(config.time_passes, "llvm module passes", (), |()| + time(config.time_passes, "llvm module passes", || llvm::LLVMRunPassManager(mpm, llmod)); // Deallocate managers that we're now done with @@ -495,7 +495,7 @@ unsafe fn optimize_and_codegen(cgcx: &CodegenContext, match cgcx.lto_ctxt { Some((sess, reachable)) if sess.lto() => { - time(sess.time_passes(), "all lto passes", (), |()| + time(sess.time_passes(), "all lto passes", || lto::run(sess, llmod, tm, reachable, &config)); if config.emit_lto_bc { @@ -536,7 +536,7 @@ unsafe fn optimize_and_codegen(cgcx: &CodegenContext, llvm::LLVMWriteBitcodeToFile(llmod, out.as_ptr()); } - time(config.time_passes, "codegen passes", (), |()| { + time(config.time_passes, "codegen passes", || { if config.emit_ir { let ext = format!("{}.ll", name_extra); let out = output_names.with_extension(&ext); diff --git a/src/librustc_trans/trans/base.rs b/src/librustc_trans/trans/base.rs index 947c902b2a91b..1b4e14d7ad79f 100644 --- a/src/librustc_trans/trans/base.rs +++ b/src/librustc_trans/trans/base.rs @@ -2822,7 +2822,6 @@ pub fn trans_crate(tcx: &ty::ctxt, analysis: ty::CrateAnalysis) -> CrateTranslat llcx: shared_ccx.metadata_llcx(), llmod: shared_ccx.metadata_llmod(), }; - let formats = shared_ccx.tcx().dependency_formats.borrow().clone(); let no_builtins = attr::contains_name(&krate.attrs, "no_builtins"); CrateTranslation { @@ -2831,7 +2830,6 @@ pub fn trans_crate(tcx: &ty::ctxt, analysis: ty::CrateAnalysis) -> CrateTranslat link: link_meta, metadata: metadata, reachable: reachable_symbols, - crate_formats: formats, no_builtins: no_builtins, } } diff --git a/src/librustc_trans/trans/mod.rs b/src/librustc_trans/trans/mod.rs index 7d568ff90ea43..04854501312bf 100644 --- a/src/librustc_trans/trans/mod.rs +++ b/src/librustc_trans/trans/mod.rs @@ -10,7 +10,6 @@ use llvm::{ContextRef, ModuleRef}; use metadata::common::LinkMeta; -use middle::dependency_format; pub use self::base::trans_crate; pub use self::context::CrateContext; @@ -74,6 +73,5 @@ pub struct CrateTranslation { pub link: LinkMeta, pub metadata: Vec, pub reachable: Vec, - pub crate_formats: dependency_format::Dependencies, pub no_builtins: bool, } diff --git a/src/librustc_typeck/lib.rs b/src/librustc_typeck/lib.rs index e6824c811c58e..1eff224219e5f 100644 --- a/src/librustc_typeck/lib.rs +++ b/src/librustc_typeck/lib.rs @@ -332,34 +332,34 @@ pub fn check_crate(tcx: &ty::ctxt, trait_map: ty::TraitMap) { tcx: tcx }; - time(time_passes, "type collecting", (), |_| + time(time_passes, "type collecting", || collect::collect_item_types(tcx)); // this ensures that later parts of type checking can assume that items // have valid types and not error tcx.sess.abort_if_errors(); - time(time_passes, "variance inference", (), |_| + time(time_passes, "variance inference", || variance::infer_variance(tcx)); - time(time_passes, "coherence checking", (), |_| + time(time_passes, "coherence checking", || coherence::check_coherence(&ccx)); - time(time_passes, "wf checking (old)", (), |_| + time(time_passes, "wf checking (old)", || check::check_wf_old(&ccx)); - time(time_passes, "item-types checking", (), |_| + time(time_passes, "item-types checking", || check::check_item_types(&ccx)); - time(time_passes, "item-bodies checking", (), |_| + time(time_passes, "item-bodies checking", || check::check_item_bodies(&ccx)); - time(time_passes, "drop-impl checking", (), |_| + time(time_passes, "drop-impl checking", || check::check_drop_impls(&ccx)); // Do this last so that if there are errors in the old code, they // get reported, and we don't get extra warnings. - time(time_passes, "wf checking (new)", (), |_| + time(time_passes, "wf checking (new)", || check::check_wf_new(&ccx)); check_for_entry_fn(&ccx); diff --git a/src/libstd/lib.rs b/src/libstd/lib.rs index 77c634e8090d3..3dcb157233d60 100644 --- a/src/libstd/lib.rs +++ b/src/libstd/lib.rs @@ -275,8 +275,6 @@ extern crate alloc; extern crate rustc_unicode; extern crate libc; -#[macro_use] #[no_link] extern crate rustc_bitflags; - // Make std testable by not duplicating lang items and other globals. See #2912 #[cfg(test)] extern crate std as realstd; diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs index 87149ff81da78..694a1a43f593d 100644 --- a/src/libsyntax/feature_gate.rs +++ b/src/libsyntax/feature_gate.rs @@ -85,6 +85,7 @@ const KNOWN_FEATURES: &'static [(&'static str, &'static str, Status)] = &[ ("on_unimplemented", "1.0.0", Active), ("simd_ffi", "1.0.0", Active), ("allocator", "1.0.0", Active), + ("needs_allocator", "1.4.0", Active), ("linked_from", "1.3.0", Active), ("if_let", "1.0.0", Accepted), @@ -253,6 +254,9 @@ pub const KNOWN_ATTRIBUTES: &'static [(&'static str, AttributeType)] = &[ is an experimental feature")), ("allocator", Gated("allocator", "the `#[allocator]` attribute is an experimental feature")), + ("needs_allocator", Gated("needs_allocator", "the `#[needs_allocator]` \ + attribute is an experimental \ + feature")), ("rustc_variance", Gated("rustc_attrs", "the `#[rustc_variance]` attribute \ is an experimental feature")), diff --git a/src/test/auxiliary/allocator-dummy.rs b/src/test/auxiliary/allocator-dummy.rs new file mode 100644 index 0000000000000..0194fb1dddad9 --- /dev/null +++ b/src/test/auxiliary/allocator-dummy.rs @@ -0,0 +1,55 @@ +// 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 + +#![feature(allocator, no_std, core, core_intrinsics, libc)] +#![allocator] +#![crate_type = "rlib"] +#![no_std] + +extern crate libc; + +pub static mut HITS: usize = 0; + +#[no_mangle] +pub extern fn __rust_allocate(size: usize, align: usize) -> *mut u8 { + unsafe { + HITS += 1; + libc::malloc(size as libc::size_t) as *mut u8 + } +} + +#[no_mangle] +pub extern fn __rust_deallocate(ptr: *mut u8, old_size: usize, align: usize) { + unsafe { + HITS += 1; + libc::free(ptr as *mut _) + } +} + +#[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 _, 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 { + unsafe { core::intrinsics::abort() } +} + +#[no_mangle] +pub extern fn __rust_usable_size(size: usize, align: usize) -> usize { + unsafe { core::intrinsics::abort() } +} diff --git a/src/test/auxiliary/allocator-dylib.rs b/src/test/auxiliary/allocator-dylib.rs new file mode 100644 index 0000000000000..568b247ecdbf3 --- /dev/null +++ b/src/test/auxiliary/allocator-dylib.rs @@ -0,0 +1,15 @@ +// 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 + +#![crate_type = "dylib"] + +pub fn foo() {} diff --git a/src/test/auxiliary/allocator-dylib2.rs b/src/test/auxiliary/allocator-dylib2.rs new file mode 100644 index 0000000000000..0d76c0e5eb8d1 --- /dev/null +++ b/src/test/auxiliary/allocator-dylib2.rs @@ -0,0 +1,12 @@ +// 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. + +pub fn foo() {} + diff --git a/src/test/auxiliary/allocator1.rs b/src/test/auxiliary/allocator1.rs new file mode 100644 index 0000000000000..4e9cef643484c --- /dev/null +++ b/src/test/auxiliary/allocator1.rs @@ -0,0 +1,16 @@ +// 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 + +#![feature(allocator, no_std, core)] +#![allocator] +#![crate_type = "rlib"] +#![no_std] diff --git a/src/test/auxiliary/allocator2.rs b/src/test/auxiliary/allocator2.rs new file mode 100644 index 0000000000000..4e9cef643484c --- /dev/null +++ b/src/test/auxiliary/allocator2.rs @@ -0,0 +1,16 @@ +// 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 + +#![feature(allocator, no_std, core)] +#![allocator] +#![crate_type = "rlib"] +#![no_std] diff --git a/src/test/auxiliary/allocator3.rs b/src/test/auxiliary/allocator3.rs new file mode 100644 index 0000000000000..1ee21bc7ddb35 --- /dev/null +++ b/src/test/auxiliary/allocator3.rs @@ -0,0 +1,19 @@ +// 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 + +#![feature(no_std, allocator)] +#![no_std] +#![allocator] +#![crate_type = "rlib"] + +extern crate needs_allocator; + diff --git a/src/test/auxiliary/needs_allocator.rs b/src/test/auxiliary/needs_allocator.rs new file mode 100644 index 0000000000000..c09b153d921da --- /dev/null +++ b/src/test/auxiliary/needs_allocator.rs @@ -0,0 +1,16 @@ +// 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 + +#![feature(no_std, needs_allocator)] +#![no_std] +#![needs_allocator] +#![crate_type = "rlib"] diff --git a/src/test/compile-fail/allocator-depends-on-needs-allocators.rs b/src/test/compile-fail/allocator-depends-on-needs-allocators.rs new file mode 100644 index 0000000000000..7f420ff735a3e --- /dev/null +++ b/src/test/compile-fail/allocator-depends-on-needs-allocators.rs @@ -0,0 +1,21 @@ +// 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 new file mode 100644 index 0000000000000..8fad2af42b9a2 --- /dev/null +++ b/src/test/compile-fail/allocator-dylib-is-system.rs @@ -0,0 +1,27 @@ +// 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-msvc everything is the system allocator on msvc +// aux-build:allocator-dylib.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. + +#![feature(alloc_jemalloc)] + +extern crate allocator_dylib; +extern crate alloc_jemalloc; + +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 new file mode 100644 index 0000000000000..224d9379ee158 --- /dev/null +++ b/src/test/compile-fail/allocator-rust-dylib-is-jemalloc.rs @@ -0,0 +1,26 @@ +// 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-msvc everything is the system allocator on msvc +// aux-build:allocator-dylib2.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. + +#![feature(alloc_system)] + +extern crate allocator_dylib2; +extern crate alloc_system; + +fn main() { + allocator_dylib2::foo(); +} + diff --git a/src/test/compile-fail/feature-gate-allocator.rs b/src/test/compile-fail/feature-gate-allocator.rs new file mode 100644 index 0000000000000..6490216d0126f --- /dev/null +++ b/src/test/compile-fail/feature-gate-allocator.rs @@ -0,0 +1,13 @@ +// 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. + +#![allocator] //~ ERROR: experimental feature + +fn main() {} diff --git a/src/test/compile-fail/feature-gate-needs-allocator.rs b/src/test/compile-fail/feature-gate-needs-allocator.rs new file mode 100644 index 0000000000000..1809564f5de1e --- /dev/null +++ b/src/test/compile-fail/feature-gate-needs-allocator.rs @@ -0,0 +1,14 @@ +// 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. + +#![needs_allocator] //~ ERROR the `#[needs_allocator]` attribute is + +fn main() {} + diff --git a/src/test/compile-fail/two-allocators-2.rs b/src/test/compile-fail/two-allocators-2.rs new file mode 100644 index 0000000000000..d6fcbcb513ae3 --- /dev/null +++ b/src/test/compile-fail/two-allocators-2.rs @@ -0,0 +1,21 @@ +// 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 new file mode 100644 index 0000000000000..70c9dfcafae75 --- /dev/null +++ b/src/test/compile-fail/two-allocators-3.rs @@ -0,0 +1,21 @@ +// 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 + +// 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/compile-fail/two-allocators.rs b/src/test/compile-fail/two-allocators.rs new file mode 100644 index 0000000000000..a34f77de2455b --- /dev/null +++ b/src/test/compile-fail/two-allocators.rs @@ -0,0 +1,19 @@ +// 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 + +// aux-build:allocator1.rs +// aux-build:allocator2.rs + +extern crate allocator1; +extern crate allocator2; + +fn main() {} diff --git a/src/test/run-pass/allocator-default.rs b/src/test/run-pass/allocator-default.rs new file mode 100644 index 0000000000000..1bca39de6635d --- /dev/null +++ b/src/test/run-pass/allocator-default.rs @@ -0,0 +1,20 @@ +// 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, alloc_system)] + +#[cfg(not(target_env = "msvc"))] +extern crate alloc_jemalloc; +#[cfg(target_env = "msvc")] +extern crate alloc_system; + +fn main() { + println!("{:?}", Box::new(3)); +} diff --git a/src/test/run-pass/allocator-jemalloc.rs b/src/test/run-pass/allocator-jemalloc.rs new file mode 100644 index 0000000000000..77fa64ec3db07 --- /dev/null +++ b/src/test/run-pass/allocator-jemalloc.rs @@ -0,0 +1,20 @@ +// 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 +// ignore-msvc no jemalloc on msvc + +#![feature(alloc_jemalloc)] + +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 new file mode 100644 index 0000000000000..993ea414914fb --- /dev/null +++ b/src/test/run-pass/allocator-override.rs @@ -0,0 +1,24 @@ +// 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 + +extern crate allocator_dummy; + +fn main() { + unsafe { + let before = allocator_dummy::HITS; + let b = Box::new(3); + assert_eq!(allocator_dummy::HITS - before, 1); + drop(b); + assert_eq!(allocator_dummy::HITS - before, 2); + } +} diff --git a/src/test/run-pass/allocator-system.rs b/src/test/run-pass/allocator-system.rs new file mode 100644 index 0000000000000..4585003d5793b --- /dev/null +++ b/src/test/run-pass/allocator-system.rs @@ -0,0 +1,19 @@ +// 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 + +#![feature(alloc_system)] + +extern crate alloc_system; + +fn main() { + println!("{:?}", Box::new(3)); +}