Skip to content

Commit

Permalink
Build a dummy alloc_jemalloc crate on platforms that don't support it
Browse files Browse the repository at this point in the history
This is a hack to support building targets that don't support jemalloc
alongside hosts that do. The jemalloc build is controlled by a feature
of the std crate, and if that feature changes between targets, it
invalidates the fingerprint of std's build script (this is a cargo
bug); so we must ensure that the feature set used by std is the same
across all targets, which means we have to build the alloc_jemalloc
crate for targets like emscripten, even if we don't use it.
  • Loading branch information
brson committed Sep 30, 2016
1 parent 3c038c0 commit 4f5e73b
Show file tree
Hide file tree
Showing 3 changed files with 180 additions and 117 deletions.
17 changes: 8 additions & 9 deletions src/bootstrap/sanity.rs
Expand Up @@ -104,22 +104,21 @@ pub fn check(build: &mut Build) {
need_cmd(build.cxx(host).as_ref());
}

// The msvc hosts don't use jemalloc, turn it off globally to
// avoid packaging the dummy liballoc_jemalloc on that platform.
for host in build.config.host.iter() {
if host.contains("msvc") {
build.config.use_jemalloc = false;
}
}

// Externally configured LLVM requires FileCheck to exist
let filecheck = build.llvm_filecheck(&build.config.build);
if !filecheck.starts_with(&build.out) && !filecheck.exists() && build.config.codegen_tests {
panic!("filecheck executable {:?} does not exist", filecheck);
}

for target in build.config.target.iter() {
// Either can't build or don't want to run jemalloc on these targets
if target.contains("rumprun") ||
target.contains("bitrig") ||
target.contains("openbsd") ||
target.contains("msvc") ||
target.contains("emscripten") {
build.config.use_jemalloc = false;
}

// Can't compile for iOS unless we're on OSX
if target.contains("apple-ios") &&
!build.config.build.contains("apple-darwin") {
Expand Down
18 changes: 18 additions & 0 deletions src/liballoc_jemalloc/build.rs
Expand Up @@ -27,6 +27,24 @@ fn main() {
let build_dir = PathBuf::from(env::var_os("OUT_DIR").unwrap());
let src_dir = env::current_dir().unwrap();

// FIXME: This is a hack to support building targets that don't
// support jemalloc alongside hosts that do. The jemalloc build is
// controlled by a feature of the std crate, and if that feature
// changes between targets, it invalidates the fingerprint of
// std's build script (this is a cargo bug); so we must ensure
// that the feature set used by std is the same across all
// targets, which means we have to build the alloc_jemalloc crate
// for targets like emscripten, even if we don't use it.
if target.contains("rumprun") ||
target.contains("bitrig") ||
target.contains("openbsd") ||
target.contains("msvc") ||
target.contains("emscripten")
{
println!("cargo:rustc-cfg=dummy_jemalloc");
return;
}

if let Some(jemalloc) = env::var_os("JEMALLOC_OVERRIDE") {
let jemalloc = PathBuf::from(jemalloc);
println!("cargo:rustc-link-search=native={}",
Expand Down
262 changes: 154 additions & 108 deletions src/liballoc_jemalloc/lib.rs
Expand Up @@ -23,124 +23,170 @@

extern crate libc;

use libc::{c_int, c_void, size_t};
pub use imp::*;

// Linkage directives to pull in jemalloc and its dependencies.
//
// On some platforms we need to be sure to link in `pthread` which jemalloc
// depends on, and specifically on android we need to also link to libgcc.
// Currently jemalloc is compiled with gcc which will generate calls to
// intrinsics that are libgcc specific (e.g. those intrinsics aren't present in
// libcompiler-rt), so link that in to get that support.
#[link(name = "jemalloc", kind = "static")]
#[cfg_attr(target_os = "android", link(name = "gcc"))]
#[cfg_attr(all(not(windows),
not(target_os = "android"),
not(target_env = "musl")),
link(name = "pthread"))]
#[cfg(not(cargobuild))]
extern "C" {}

// Note that the symbols here are prefixed by default on OSX and Windows (we
// don't explicitly request it), and on Android and DragonFly we explicitly
// request it as unprefixing cause segfaults (mismatches in allocators).
extern "C" {
#[cfg_attr(any(target_os = "macos", target_os = "android", target_os = "ios",
target_os = "dragonfly", target_os = "windows"),
link_name = "je_mallocx")]
fn mallocx(size: size_t, flags: c_int) -> *mut c_void;
#[cfg_attr(any(target_os = "macos", target_os = "android", target_os = "ios",
target_os = "dragonfly", target_os = "windows"),
link_name = "je_rallocx")]
fn rallocx(ptr: *mut c_void, size: size_t, flags: c_int) -> *mut c_void;
#[cfg_attr(any(target_os = "macos", target_os = "android", target_os = "ios",
target_os = "dragonfly", target_os = "windows"),
link_name = "je_xallocx")]
fn xallocx(ptr: *mut c_void, size: size_t, extra: size_t, flags: c_int) -> size_t;
#[cfg_attr(any(target_os = "macos", target_os = "android", target_os = "ios",
target_os = "dragonfly", target_os = "windows"),
link_name = "je_sdallocx")]
fn sdallocx(ptr: *mut c_void, size: size_t, flags: c_int);
#[cfg_attr(any(target_os = "macos", target_os = "android", target_os = "ios",
target_os = "dragonfly", target_os = "windows"),
link_name = "je_nallocx")]
fn nallocx(size: size_t, flags: c_int) -> size_t;
}
// See comments in build.rs for why we sometimes build a crate that does nothing
#[cfg(not(dummy_jemalloc))]
mod imp {
use libc::{c_int, c_void, size_t};

// 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 = "powerpc")))]
const MIN_ALIGN: usize = 8;
#[cfg(all(any(target_arch = "x86",
target_arch = "x86_64",
target_arch = "aarch64",
target_arch = "powerpc64",
target_arch = "mips64",
target_arch = "s390x")))]
const MIN_ALIGN: usize = 16;

// MALLOCX_ALIGN(a) macro
fn mallocx_align(a: usize) -> c_int {
a.trailing_zeros() as c_int
}
// Linkage directives to pull in jemalloc and its dependencies.
//
// On some platforms we need to be sure to link in `pthread` which jemalloc
// depends on, and specifically on android we need to also link to libgcc.
// Currently jemalloc is compiled with gcc which will generate calls to
// intrinsics that are libgcc specific (e.g. those intrinsics aren't present in
// libcompiler-rt), so link that in to get that support.
#[link(name = "jemalloc", kind = "static")]
#[cfg_attr(target_os = "android", link(name = "gcc"))]
#[cfg_attr(all(not(windows),
not(target_os = "android"),
not(target_env = "musl")),
link(name = "pthread"))]
#[cfg(not(cargobuild))]
extern "C" {}

// Note that the symbols here are prefixed by default on OSX and Windows (we
// don't explicitly request it), and on Android and DragonFly we explicitly
// request it as unprefixing cause segfaults (mismatches in allocators).
extern "C" {
#[cfg_attr(any(target_os = "macos", target_os = "android", target_os = "ios",
target_os = "dragonfly", target_os = "windows"),
link_name = "je_mallocx")]
fn mallocx(size: size_t, flags: c_int) -> *mut c_void;
#[cfg_attr(any(target_os = "macos", target_os = "android", target_os = "ios",
target_os = "dragonfly", target_os = "windows"),
link_name = "je_rallocx")]
fn rallocx(ptr: *mut c_void, size: size_t, flags: c_int) -> *mut c_void;
#[cfg_attr(any(target_os = "macos", target_os = "android", target_os = "ios",
target_os = "dragonfly", target_os = "windows"),
link_name = "je_xallocx")]
fn xallocx(ptr: *mut c_void, size: size_t, extra: size_t, flags: c_int) -> size_t;
#[cfg_attr(any(target_os = "macos", target_os = "android", target_os = "ios",
target_os = "dragonfly", target_os = "windows"),
link_name = "je_sdallocx")]
fn sdallocx(ptr: *mut c_void, size: size_t, flags: c_int);
#[cfg_attr(any(target_os = "macos", target_os = "android", target_os = "ios",
target_os = "dragonfly", target_os = "windows"),
link_name = "je_nallocx")]
fn nallocx(size: size_t, flags: c_int) -> size_t;
}

// 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 = "powerpc")))]
const MIN_ALIGN: usize = 8;
#[cfg(all(any(target_arch = "x86",
target_arch = "x86_64",
target_arch = "aarch64",
target_arch = "powerpc64",
target_arch = "mips64",
target_arch = "s390x")))]
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 "C" fn __rust_allocate(size: usize, align: usize) -> *mut u8 {
let flags = align_to_flags(align);
unsafe { mallocx(size as size_t, flags) as *mut u8 }
}

#[no_mangle]
pub extern "C" fn __rust_reallocate(ptr: *mut u8,
_old_size: usize,
size: usize,
align: usize)
-> *mut u8 {
let flags = align_to_flags(align);
unsafe { rallocx(ptr as *mut c_void, size as size_t, flags) as *mut u8 }
}

#[no_mangle]
pub extern "C" fn __rust_reallocate_inplace(ptr: *mut u8,
_old_size: usize,
size: usize,
align: usize)
-> usize {
let flags = align_to_flags(align);
unsafe { xallocx(ptr as *mut c_void, size as size_t, 0, flags) as usize }
}

fn align_to_flags(align: usize) -> c_int {
if align <= MIN_ALIGN {
#[no_mangle]
pub extern "C" fn __rust_deallocate(ptr: *mut u8, old_size: usize, align: usize) {
let flags = align_to_flags(align);
unsafe { sdallocx(ptr as *mut c_void, old_size as size_t, flags) }
}

#[no_mangle]
pub extern "C" fn __rust_usable_size(size: usize, align: usize) -> usize {
let flags = align_to_flags(align);
unsafe { nallocx(size as size_t, flags) as usize }
}

// These symbols are used by jemalloc on android but the really old android
// we're building on doesn't have them defined, so just make sure the symbols
// are available.
#[no_mangle]
#[cfg(target_os = "android")]
pub extern "C" fn pthread_atfork(_prefork: *mut u8,
_postfork_parent: *mut u8,
_postfork_child: *mut u8)
-> i32 {
0
} else {
mallocx_align(align)
}
}

#[no_mangle]
pub extern "C" fn __rust_allocate(size: usize, align: usize) -> *mut u8 {
let flags = align_to_flags(align);
unsafe { mallocx(size as size_t, flags) as *mut u8 }
}
#[cfg(dummy_jemalloc)]
mod imp {
fn bogus() -> ! {
panic!("jemalloc is not implemented for this platform");
}

#[no_mangle]
pub extern "C" fn __rust_reallocate(ptr: *mut u8,
_old_size: usize,
size: usize,
align: usize)
-> *mut u8 {
let flags = align_to_flags(align);
unsafe { rallocx(ptr as *mut c_void, size as size_t, flags) as *mut u8 }
}
#[no_mangle]
pub extern "C" fn __rust_allocate(_size: usize, _align: usize) -> *mut u8 {
bogus()
}

#[no_mangle]
pub extern "C" fn __rust_reallocate_inplace(ptr: *mut u8,
_old_size: usize,
size: usize,
align: usize)
-> usize {
let flags = align_to_flags(align);
unsafe { xallocx(ptr as *mut c_void, size as size_t, 0, flags) as usize }
}
#[no_mangle]
pub extern "C" fn __rust_reallocate(_ptr: *mut u8,
_old_size: usize,
_size: usize,
_align: usize)
-> *mut u8 {
bogus()
}

#[no_mangle]
pub extern "C" fn __rust_deallocate(ptr: *mut u8, old_size: usize, align: usize) {
let flags = align_to_flags(align);
unsafe { sdallocx(ptr as *mut c_void, old_size as size_t, flags) }
}
#[no_mangle]
pub extern "C" fn __rust_reallocate_inplace(_ptr: *mut u8,
_old_size: usize,
_size: usize,
_align: usize)
-> usize {
bogus()
}

#[no_mangle]
pub extern "C" fn __rust_usable_size(size: usize, align: usize) -> usize {
let flags = align_to_flags(align);
unsafe { nallocx(size as size_t, flags) as usize }
}
#[no_mangle]
pub extern "C" fn __rust_deallocate(_ptr: *mut u8, _old_size: usize, _align: usize) {
bogus()
}

// These symbols are used by jemalloc on android but the really old android
// we're building on doesn't have them defined, so just make sure the symbols
// are available.
#[no_mangle]
#[cfg(target_os = "android")]
pub extern "C" fn pthread_atfork(_prefork: *mut u8,
_postfork_parent: *mut u8,
_postfork_child: *mut u8)
-> i32 {
0
#[no_mangle]
pub extern "C" fn __rust_usable_size(_size: usize, _align: usize) -> usize {
bogus()
}
}

0 comments on commit 4f5e73b

Please sign in to comment.