From 09e8db1e4f33ec82316e1eeaaedad94fe6e1acb5 Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Tue, 3 Apr 2018 14:41:15 +0200 Subject: Rename `heap` modules in the core, alloc, and std crates to `alloc` --- src/liballoc/alloc.rs | 291 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 291 insertions(+) create mode 100644 src/liballoc/alloc.rs (limited to 'src/liballoc/alloc.rs') diff --git a/src/liballoc/alloc.rs b/src/liballoc/alloc.rs new file mode 100644 index 00000000000..000c0123d9f --- /dev/null +++ b/src/liballoc/alloc.rs @@ -0,0 +1,291 @@ +// Copyright 2014-2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![unstable(feature = "allocator_api", + reason = "the precise API and guarantees it provides may be tweaked \ + slightly, especially to possibly take into account the \ + types being stored to make room for a future \ + tracing garbage collector", + issue = "32838")] + +use core::intrinsics::{min_align_of_val, size_of_val}; +use core::mem::{self, ManuallyDrop}; +use core::usize; + +#[doc(inline)] +pub use core::heap::*; + +#[doc(hidden)] +pub mod __core { + pub use core::*; +} + +extern "Rust" { + #[allocator] + #[rustc_allocator_nounwind] + fn __rust_alloc(size: usize, align: usize, err: *mut u8) -> *mut u8; + #[cold] + #[rustc_allocator_nounwind] + fn __rust_oom(err: *const u8) -> !; + #[rustc_allocator_nounwind] + fn __rust_dealloc(ptr: *mut u8, size: usize, align: usize); + #[rustc_allocator_nounwind] + fn __rust_usable_size(layout: *const u8, + min: *mut usize, + max: *mut usize); + #[rustc_allocator_nounwind] + fn __rust_realloc(ptr: *mut u8, + old_size: usize, + old_align: usize, + new_size: usize, + new_align: usize, + err: *mut u8) -> *mut u8; + #[rustc_allocator_nounwind] + fn __rust_alloc_zeroed(size: usize, align: usize, err: *mut u8) -> *mut u8; + #[rustc_allocator_nounwind] + fn __rust_alloc_excess(size: usize, + align: usize, + excess: *mut usize, + err: *mut u8) -> *mut u8; + #[rustc_allocator_nounwind] + fn __rust_realloc_excess(ptr: *mut u8, + old_size: usize, + old_align: usize, + new_size: usize, + new_align: usize, + excess: *mut usize, + err: *mut u8) -> *mut u8; + #[rustc_allocator_nounwind] + fn __rust_grow_in_place(ptr: *mut u8, + old_size: usize, + old_align: usize, + new_size: usize, + new_align: usize) -> u8; + #[rustc_allocator_nounwind] + fn __rust_shrink_in_place(ptr: *mut u8, + old_size: usize, + old_align: usize, + new_size: usize, + new_align: usize) -> u8; +} + +#[derive(Copy, Clone, Default, Debug)] +pub struct Heap; + +unsafe impl Alloc for Heap { + #[inline] + unsafe fn alloc(&mut self, layout: Layout) -> Result<*mut u8, AllocErr> { + let mut err = ManuallyDrop::new(mem::uninitialized::()); + let ptr = __rust_alloc(layout.size(), + layout.align(), + &mut *err as *mut AllocErr as *mut u8); + if ptr.is_null() { + Err(ManuallyDrop::into_inner(err)) + } else { + Ok(ptr) + } + } + + #[inline] + #[cold] + fn oom(&mut self, err: AllocErr) -> ! { + unsafe { + __rust_oom(&err as *const AllocErr as *const u8) + } + } + + #[inline] + unsafe fn dealloc(&mut self, ptr: *mut u8, layout: Layout) { + __rust_dealloc(ptr, layout.size(), layout.align()) + } + + #[inline] + fn usable_size(&self, layout: &Layout) -> (usize, usize) { + let mut min = 0; + let mut max = 0; + unsafe { + __rust_usable_size(layout as *const Layout as *const u8, + &mut min, + &mut max); + } + (min, max) + } + + #[inline] + unsafe fn realloc(&mut self, + ptr: *mut u8, + layout: Layout, + new_layout: Layout) + -> Result<*mut u8, AllocErr> + { + let mut err = ManuallyDrop::new(mem::uninitialized::()); + let ptr = __rust_realloc(ptr, + layout.size(), + layout.align(), + new_layout.size(), + new_layout.align(), + &mut *err as *mut AllocErr as *mut u8); + if ptr.is_null() { + Err(ManuallyDrop::into_inner(err)) + } else { + mem::forget(err); + Ok(ptr) + } + } + + #[inline] + unsafe fn alloc_zeroed(&mut self, layout: Layout) -> Result<*mut u8, AllocErr> { + let mut err = ManuallyDrop::new(mem::uninitialized::()); + let ptr = __rust_alloc_zeroed(layout.size(), + layout.align(), + &mut *err as *mut AllocErr as *mut u8); + if ptr.is_null() { + Err(ManuallyDrop::into_inner(err)) + } else { + Ok(ptr) + } + } + + #[inline] + unsafe fn alloc_excess(&mut self, layout: Layout) -> Result { + let mut err = ManuallyDrop::new(mem::uninitialized::()); + let mut size = 0; + let ptr = __rust_alloc_excess(layout.size(), + layout.align(), + &mut size, + &mut *err as *mut AllocErr as *mut u8); + if ptr.is_null() { + Err(ManuallyDrop::into_inner(err)) + } else { + Ok(Excess(ptr, size)) + } + } + + #[inline] + unsafe fn realloc_excess(&mut self, + ptr: *mut u8, + layout: Layout, + new_layout: Layout) -> Result { + let mut err = ManuallyDrop::new(mem::uninitialized::()); + let mut size = 0; + let ptr = __rust_realloc_excess(ptr, + layout.size(), + layout.align(), + new_layout.size(), + new_layout.align(), + &mut size, + &mut *err as *mut AllocErr as *mut u8); + if ptr.is_null() { + Err(ManuallyDrop::into_inner(err)) + } else { + Ok(Excess(ptr, size)) + } + } + + #[inline] + unsafe fn grow_in_place(&mut self, + ptr: *mut u8, + layout: Layout, + new_layout: Layout) + -> Result<(), CannotReallocInPlace> + { + debug_assert!(new_layout.size() >= layout.size()); + debug_assert!(new_layout.align() == layout.align()); + let ret = __rust_grow_in_place(ptr, + layout.size(), + layout.align(), + new_layout.size(), + new_layout.align()); + if ret != 0 { + Ok(()) + } else { + Err(CannotReallocInPlace) + } + } + + #[inline] + unsafe fn shrink_in_place(&mut self, + ptr: *mut u8, + layout: Layout, + new_layout: Layout) -> Result<(), CannotReallocInPlace> { + debug_assert!(new_layout.size() <= layout.size()); + debug_assert!(new_layout.align() == layout.align()); + let ret = __rust_shrink_in_place(ptr, + layout.size(), + layout.align(), + new_layout.size(), + new_layout.align()); + if ret != 0 { + Ok(()) + } else { + Err(CannotReallocInPlace) + } + } +} + +/// The allocator for unique pointers. +// This function must not unwind. If it does, MIR trans will fail. +#[cfg(not(test))] +#[lang = "exchange_malloc"] +#[inline] +unsafe fn exchange_malloc(size: usize, align: usize) -> *mut u8 { + if size == 0 { + align as *mut u8 + } else { + let layout = Layout::from_size_align_unchecked(size, align); + Heap.alloc(layout).unwrap_or_else(|err| { + Heap.oom(err) + }) + } +} + +#[cfg_attr(not(test), lang = "box_free")] +#[inline] +pub(crate) unsafe fn box_free(ptr: *mut T) { + let size = size_of_val(&*ptr); + let align = min_align_of_val(&*ptr); + // We do not allocate for Box when T is ZST, so deallocation is also not necessary. + if size != 0 { + let layout = Layout::from_size_align_unchecked(size, align); + Heap.dealloc(ptr as *mut u8, layout); + } +} + +#[cfg(test)] +mod tests { + extern crate test; + use self::test::Bencher; + use boxed::Box; + use heap::{Heap, Alloc, Layout}; + + #[test] + fn allocate_zeroed() { + unsafe { + let layout = Layout::from_size_align(1024, 1).unwrap(); + let ptr = Heap.alloc_zeroed(layout.clone()) + .unwrap_or_else(|e| Heap.oom(e)); + + let end = ptr.offset(layout.size() as isize); + let mut i = ptr; + while i < end { + assert_eq!(*i, 0); + i = i.offset(1); + } + Heap.dealloc(ptr, layout); + } + } + + #[bench] + fn alloc_owned_small(b: &mut Bencher) { + b.iter(|| { + let _: Box<_> = box 10; + }) + } +} -- cgit 1.4.1-3-g733a5 From 743c29bdc5b0a75c648e1317aa5d1d816007f176 Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Tue, 3 Apr 2018 21:05:10 +0200 Subject: Actually deprecate heap modules. --- src/liballoc/alloc.rs | 2 +- src/liballoc/lib.rs | 10 ++++++++-- src/libcore/lib.rs | 5 ++++- src/libstd/alloc.rs | 6 +++--- src/libstd/lib.rs | 5 ++++- 5 files changed, 20 insertions(+), 8 deletions(-) (limited to 'src/liballoc/alloc.rs') diff --git a/src/liballoc/alloc.rs b/src/liballoc/alloc.rs index 000c0123d9f..2477166966e 100644 --- a/src/liballoc/alloc.rs +++ b/src/liballoc/alloc.rs @@ -20,7 +20,7 @@ use core::mem::{self, ManuallyDrop}; use core::usize; #[doc(inline)] -pub use core::heap::*; +pub use core::alloc::*; #[doc(hidden)] pub mod __core { diff --git a/src/liballoc/lib.rs b/src/liballoc/lib.rs index 617bc5c52b3..066698a71df 100644 --- a/src/liballoc/lib.rs +++ b/src/liballoc/lib.rs @@ -141,7 +141,10 @@ mod macros; #[rustc_deprecated(since = "1.27.0", reason = "use the heap module in core, alloc, or std instead")] #[unstable(feature = "allocator_api", issue = "32838")] -pub use core::heap as allocator; +/// Use the `alloc` module instead. +pub mod allocator { + pub use alloc::*; +} // Heaps provided for low-level allocation strategies @@ -149,7 +152,10 @@ pub mod alloc; #[unstable(feature = "allocator_api", issue = "32838")] #[rustc_deprecated(since = "1.27.0", reason = "module renamed to `alloc`")] -pub use alloc as heap; +/// Use the `alloc` module instead. +pub mod heap { + pub use alloc::*; +} // Primitive types using the heaps above diff --git a/src/libcore/lib.rs b/src/libcore/lib.rs index 56d4e65d3ac..5ebd9e4334c 100644 --- a/src/libcore/lib.rs +++ b/src/libcore/lib.rs @@ -189,7 +189,10 @@ pub mod alloc; #[unstable(feature = "allocator_api", issue = "32838")] #[rustc_deprecated(since = "1.27.0", reason = "module renamed to `alloc`")] -pub use alloc as heap; +/// Use the `alloc` module instead. +pub mod heap { + pub use alloc::*; +} // note: does not need to be public mod iter_private; diff --git a/src/libstd/alloc.rs b/src/libstd/alloc.rs index b42a1052c49..77be3e52d76 100644 --- a/src/libstd/alloc.rs +++ b/src/libstd/alloc.rs @@ -12,9 +12,9 @@ #![unstable(issue = "32838", feature = "allocator_api")] -pub use alloc_crate::heap::Heap; -pub use alloc_system::System; -#[doc(inline)] pub use core::heap::*; +#[doc(inline)] pub use alloc_crate::alloc::Heap; +#[doc(inline)] pub use alloc_system::System; +#[doc(inline)] pub use core::alloc::*; #[cfg(not(test))] #[doc(hidden)] diff --git a/src/libstd/lib.rs b/src/libstd/lib.rs index 3a99e845a16..25ba75fd35e 100644 --- a/src/libstd/lib.rs +++ b/src/libstd/lib.rs @@ -481,7 +481,10 @@ pub mod alloc; #[unstable(feature = "allocator_api", issue = "32838")] #[rustc_deprecated(since = "1.27.0", reason = "module renamed to `alloc`")] -pub use alloc as heap; +/// Use the `alloc` module instead. +pub mod heap { + pub use alloc::*; +} // Platform-abstraction modules #[macro_use] -- cgit 1.4.1-3-g733a5 From 88ebd2d752831860d8824849cf6f5ae656a2c3eb Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Tue, 3 Apr 2018 14:43:34 +0200 Subject: Rename the Heap type to Global MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit … since it is the entry point for what’s registered with `#[global_allocator]` --- src/liballoc/alloc.rs | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) (limited to 'src/liballoc/alloc.rs') diff --git a/src/liballoc/alloc.rs b/src/liballoc/alloc.rs index 2477166966e..1bd95cfd08c 100644 --- a/src/liballoc/alloc.rs +++ b/src/liballoc/alloc.rs @@ -77,9 +77,14 @@ extern "Rust" { } #[derive(Copy, Clone, Default, Debug)] -pub struct Heap; +pub struct Global; -unsafe impl Alloc for Heap { +#[unstable(feature = "allocator_api", issue = "32838")] +#[rustc_deprecated(since = "1.27.0", reason = "type renamed to `Global`")] +pub use self::Global as Heap; + + +unsafe impl Alloc for Global { #[inline] unsafe fn alloc(&mut self, layout: Layout) -> Result<*mut u8, AllocErr> { let mut err = ManuallyDrop::new(mem::uninitialized::()); @@ -240,8 +245,8 @@ unsafe fn exchange_malloc(size: usize, align: usize) -> *mut u8 { align as *mut u8 } else { let layout = Layout::from_size_align_unchecked(size, align); - Heap.alloc(layout).unwrap_or_else(|err| { - Heap.oom(err) + Global.alloc(layout).unwrap_or_else(|err| { + Global.oom(err) }) } } @@ -254,7 +259,7 @@ pub(crate) unsafe fn box_free(ptr: *mut T) { // We do not allocate for Box when T is ZST, so deallocation is also not necessary. if size != 0 { let layout = Layout::from_size_align_unchecked(size, align); - Heap.dealloc(ptr as *mut u8, layout); + Global.dealloc(ptr as *mut u8, layout); } } @@ -263,14 +268,14 @@ mod tests { extern crate test; use self::test::Bencher; use boxed::Box; - use heap::{Heap, Alloc, Layout}; + use heap::{Global, Alloc, Layout}; #[test] fn allocate_zeroed() { unsafe { let layout = Layout::from_size_align(1024, 1).unwrap(); - let ptr = Heap.alloc_zeroed(layout.clone()) - .unwrap_or_else(|e| Heap.oom(e)); + let ptr = Global.alloc_zeroed(layout.clone()) + .unwrap_or_else(|e| Global.oom(e)); let end = ptr.offset(layout.size() as isize); let mut i = ptr; @@ -278,7 +283,7 @@ mod tests { assert_eq!(*i, 0); i = i.offset(1); } - Heap.dealloc(ptr, layout); + Global.dealloc(ptr, layout); } } -- cgit 1.4.1-3-g733a5 From e521b8b472dfe058f6d0f62f2e1ab5f291c220ee Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Tue, 3 Apr 2018 21:15:06 +0200 Subject: Actually deprecate the Heap type --- src/liballoc/alloc.rs | 8 ++++++-- src/liballoc/arc.rs | 13 ++++++------- src/liballoc/btree/node.rs | 13 ++++++------- src/liballoc/raw_vec.rs | 23 +++++++++++------------ src/liballoc/rc.rs | 13 ++++++------- src/liballoc/tests/heap.rs | 4 ++-- src/libstd/alloc.rs | 3 ++- src/libstd/collections/hash/map.rs | 4 ++-- src/libstd/collections/hash/table.rs | 12 ++++++------ 9 files changed, 47 insertions(+), 46 deletions(-) (limited to 'src/liballoc/alloc.rs') diff --git a/src/liballoc/alloc.rs b/src/liballoc/alloc.rs index 1bd95cfd08c..12ee7701903 100644 --- a/src/liballoc/alloc.rs +++ b/src/liballoc/alloc.rs @@ -81,8 +81,12 @@ pub struct Global; #[unstable(feature = "allocator_api", issue = "32838")] #[rustc_deprecated(since = "1.27.0", reason = "type renamed to `Global`")] -pub use self::Global as Heap; +pub type Heap = Global; +#[unstable(feature = "allocator_api", issue = "32838")] +#[rustc_deprecated(since = "1.27.0", reason = "type renamed to `Global`")] +#[allow(non_upper_case_globals)] +pub const Heap: Global = Global; unsafe impl Alloc for Global { #[inline] @@ -268,7 +272,7 @@ mod tests { extern crate test; use self::test::Bencher; use boxed::Box; - use heap::{Global, Alloc, Layout}; + use alloc::{Global, Alloc, Layout}; #[test] fn allocate_zeroed() { diff --git a/src/liballoc/arc.rs b/src/liballoc/arc.rs index ccf2e2768d1..d63ed24aa4f 100644 --- a/src/liballoc/arc.rs +++ b/src/liballoc/arc.rs @@ -21,7 +21,6 @@ use core::sync::atomic::Ordering::{Acquire, Relaxed, Release, SeqCst}; use core::borrow; use core::fmt; use core::cmp::Ordering; -use core::heap::{Alloc, Layout}; use core::intrinsics::abort; use core::mem::{self, align_of_val, size_of_val, uninitialized}; use core::ops::Deref; @@ -32,7 +31,7 @@ use core::hash::{Hash, Hasher}; use core::{isize, usize}; use core::convert::From; -use heap::{Heap, box_free}; +use alloc::{Global, Alloc, Layout, box_free}; use boxed::Box; use string::String; use vec::Vec; @@ -521,7 +520,7 @@ impl Arc { if self.inner().weak.fetch_sub(1, Release) == 1 { atomic::fence(Acquire); - Heap.dealloc(ptr as *mut u8, Layout::for_value(&*ptr)) + Global.dealloc(ptr as *mut u8, Layout::for_value(&*ptr)) } } @@ -555,8 +554,8 @@ impl Arc { let layout = Layout::for_value(&*fake_ptr); - let mem = Heap.alloc(layout) - .unwrap_or_else(|e| Heap.oom(e)); + let mem = Global.alloc(layout) + .unwrap_or_else(|e| Global.oom(e)); // Initialize the real ArcInner let inner = set_data_ptr(ptr as *mut T, mem) as *mut ArcInner; @@ -640,7 +639,7 @@ impl ArcFromSlice for Arc<[T]> { let slice = from_raw_parts_mut(self.elems, self.n_elems); ptr::drop_in_place(slice); - Heap.dealloc(self.mem, self.layout.clone()); + Global.dealloc(self.mem, self.layout.clone()); } } } @@ -1161,7 +1160,7 @@ impl Drop for Weak { if self.inner().weak.fetch_sub(1, Release) == 1 { atomic::fence(Acquire); unsafe { - Heap.dealloc(ptr as *mut u8, Layout::for_value(&*ptr)) + Global.dealloc(ptr as *mut u8, Layout::for_value(&*ptr)) } } } diff --git a/src/liballoc/btree/node.rs b/src/liballoc/btree/node.rs index 49109d522e9..8e23228bd28 100644 --- a/src/liballoc/btree/node.rs +++ b/src/liballoc/btree/node.rs @@ -41,14 +41,13 @@ // - A node of length `n` has `n` keys, `n` values, and (in an internal node) `n + 1` edges. // This implies that even an empty internal node has at least one edge. -use core::heap::{Alloc, Layout}; use core::marker::PhantomData; use core::mem; use core::ptr::{self, Unique, NonNull}; use core::slice; +use alloc::{Global, Alloc, Layout}; use boxed::Box; -use heap::Heap; const B: usize = 6; pub const MIN_LEN: usize = B - 1; @@ -250,7 +249,7 @@ impl Root { self.as_mut().as_leaf_mut().parent = ptr::null(); unsafe { - Heap.dealloc(top, Layout::new::>()); + Global.dealloc(top, Layout::new::>()); } } } @@ -436,7 +435,7 @@ impl NodeRef { > { let ptr = self.as_leaf() as *const LeafNode as *const u8 as *mut u8; let ret = self.ascend().ok(); - Heap.dealloc(ptr, Layout::new::>()); + Global.dealloc(ptr, Layout::new::>()); ret } } @@ -457,7 +456,7 @@ impl NodeRef { > { let ptr = self.as_internal() as *const InternalNode as *const u8 as *mut u8; let ret = self.ascend().ok(); - Heap.dealloc(ptr, Layout::new::>()); + Global.dealloc(ptr, Layout::new::>()); ret } } @@ -1239,12 +1238,12 @@ impl<'a, K, V> Handle, K, V, marker::Internal>, marker:: ).correct_parent_link(); } - Heap.dealloc( + Global.dealloc( right_node.node.as_ptr() as *mut u8, Layout::new::>(), ); } else { - Heap.dealloc( + Global.dealloc( right_node.node.as_ptr() as *mut u8, Layout::new::>(), ); diff --git a/src/liballoc/raw_vec.rs b/src/liballoc/raw_vec.rs index 3edce8aebdf..51f39dc6cc7 100644 --- a/src/liballoc/raw_vec.rs +++ b/src/liballoc/raw_vec.rs @@ -8,13 +8,12 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +use alloc::{Alloc, Layout, Global}; use core::cmp; -use core::heap::{Alloc, Layout}; use core::mem; use core::ops::Drop; use core::ptr::{self, Unique}; use core::slice; -use heap::Heap; use super::boxed::Box; use super::allocator::CollectionAllocErr; use super::allocator::CollectionAllocErr::*; @@ -47,7 +46,7 @@ use super::allocator::CollectionAllocErr::*; /// field. This allows zero-sized types to not be special-cased by consumers of /// this type. #[allow(missing_debug_implementations)] -pub struct RawVec { +pub struct RawVec { ptr: Unique, cap: usize, a: A, @@ -114,14 +113,14 @@ impl RawVec { } } -impl RawVec { +impl RawVec { /// Creates the biggest possible RawVec (on the system heap) /// without allocating. If T has positive size, then this makes a /// RawVec with capacity 0. If T has 0 size, then it makes a /// RawVec with capacity `usize::MAX`. Useful for implementing /// delayed allocation. pub fn new() -> Self { - Self::new_in(Heap) + Self::new_in(Global) } /// Creates a RawVec (on the system heap) with exactly the @@ -141,13 +140,13 @@ impl RawVec { /// Aborts on OOM #[inline] pub fn with_capacity(cap: usize) -> Self { - RawVec::allocate_in(cap, false, Heap) + RawVec::allocate_in(cap, false, Global) } /// Like `with_capacity` but guarantees the buffer is zeroed. #[inline] pub fn with_capacity_zeroed(cap: usize) -> Self { - RawVec::allocate_in(cap, true, Heap) + RawVec::allocate_in(cap, true, Global) } } @@ -168,7 +167,7 @@ impl RawVec { } } -impl RawVec { +impl RawVec { /// Reconstitutes a RawVec from a pointer, capacity. /// /// # Undefined Behavior @@ -180,7 +179,7 @@ impl RawVec { RawVec { ptr: Unique::new_unchecked(ptr), cap, - a: Heap, + a: Global, } } @@ -678,7 +677,7 @@ impl RawVec { } } -impl RawVec { +impl RawVec { /// Converts the entire buffer into `Box<[T]>`. /// /// While it is not *strictly* Undefined Behavior to call @@ -763,13 +762,13 @@ mod tests { if size > self.fuel { return Err(AllocErr::Unsupported { details: "fuel exhausted" }); } - match Heap.alloc(layout) { + match Global.alloc(layout) { ok @ Ok(_) => { self.fuel -= size; ok } err @ Err(_) => err, } } unsafe fn dealloc(&mut self, ptr: *mut u8, layout: Layout) { - Heap.dealloc(ptr, layout) + Global.dealloc(ptr, layout) } } diff --git a/src/liballoc/rc.rs b/src/liballoc/rc.rs index 8bdc57f96a6..c134b181158 100644 --- a/src/liballoc/rc.rs +++ b/src/liballoc/rc.rs @@ -250,7 +250,6 @@ use core::cell::Cell; use core::cmp::Ordering; use core::fmt; use core::hash::{Hash, Hasher}; -use core::heap::{Alloc, Layout}; use core::intrinsics::abort; use core::marker; use core::marker::{Unsize, PhantomData}; @@ -260,7 +259,7 @@ use core::ops::CoerceUnsized; use core::ptr::{self, NonNull}; use core::convert::From; -use heap::{Heap, box_free}; +use alloc::{Global, Alloc, Layout, box_free}; use string::String; use vec::Vec; @@ -668,8 +667,8 @@ impl Rc { let layout = Layout::for_value(&*fake_ptr); - let mem = Heap.alloc(layout) - .unwrap_or_else(|e| Heap.oom(e)); + let mem = Global.alloc(layout) + .unwrap_or_else(|e| Global.oom(e)); // Initialize the real RcBox let inner = set_data_ptr(ptr as *mut T, mem) as *mut RcBox; @@ -752,7 +751,7 @@ impl RcFromSlice for Rc<[T]> { let slice = from_raw_parts_mut(self.elems, self.n_elems); ptr::drop_in_place(slice); - Heap.dealloc(self.mem, self.layout.clone()); + Global.dealloc(self.mem, self.layout.clone()); } } } @@ -847,7 +846,7 @@ unsafe impl<#[may_dangle] T: ?Sized> Drop for Rc { self.dec_weak(); if self.weak() == 0 { - Heap.dealloc(ptr as *mut u8, Layout::for_value(&*ptr)); + Global.dealloc(ptr as *mut u8, Layout::for_value(&*ptr)); } } } @@ -1273,7 +1272,7 @@ impl Drop for Weak { // the weak count starts at 1, and will only go to zero if all // the strong pointers have disappeared. if self.weak() == 0 { - Heap.dealloc(ptr as *mut u8, Layout::for_value(&*ptr)); + Global.dealloc(ptr as *mut u8, Layout::for_value(&*ptr)); } } } diff --git a/src/liballoc/tests/heap.rs b/src/liballoc/tests/heap.rs index d3ce12056bb..328131e2fef 100644 --- a/src/liballoc/tests/heap.rs +++ b/src/liballoc/tests/heap.rs @@ -9,7 +9,7 @@ // except according to those terms. use alloc_system::System; -use std::heap::{Heap, Alloc, Layout}; +use std::alloc::{Global, Alloc, Layout}; /// https://github.com/rust-lang/rust/issues/45955 /// @@ -22,7 +22,7 @@ fn alloc_system_overaligned_request() { #[test] fn std_heap_overaligned_request() { - check_overalign_requests(Heap) + check_overalign_requests(Global) } fn check_overalign_requests(mut allocator: T) { diff --git a/src/libstd/alloc.rs b/src/libstd/alloc.rs index 77be3e52d76..eb0c960732d 100644 --- a/src/libstd/alloc.rs +++ b/src/libstd/alloc.rs @@ -12,7 +12,8 @@ #![unstable(issue = "32838", feature = "allocator_api")] -#[doc(inline)] pub use alloc_crate::alloc::Heap; +#[doc(inline)] #[allow(deprecated)] pub use alloc_crate::alloc::Heap; +#[doc(inline)] pub use alloc_crate::alloc::Global; #[doc(inline)] pub use alloc_system::System; #[doc(inline)] pub use core::alloc::*; diff --git a/src/libstd/collections/hash/map.rs b/src/libstd/collections/hash/map.rs index 73a5df8dc28..c4ef9e62577 100644 --- a/src/libstd/collections/hash/map.rs +++ b/src/libstd/collections/hash/map.rs @@ -11,13 +11,13 @@ use self::Entry::*; use self::VacantEntryState::*; +use alloc::{Global, Alloc, CollectionAllocErr}; use cell::Cell; use borrow::Borrow; use cmp::max; use fmt::{self, Debug}; #[allow(deprecated)] use hash::{Hash, Hasher, BuildHasher, SipHasher13}; -use heap::{Heap, Alloc, CollectionAllocErr}; use iter::{FromIterator, FusedIterator}; use mem::{self, replace}; use ops::{Deref, Index}; @@ -784,7 +784,7 @@ impl HashMap pub fn reserve(&mut self, additional: usize) { match self.try_reserve(additional) { Err(CollectionAllocErr::CapacityOverflow) => panic!("capacity overflow"), - Err(CollectionAllocErr::AllocErr(e)) => Heap.oom(e), + Err(CollectionAllocErr::AllocErr(e)) => Global.oom(e), Ok(()) => { /* yay */ } } } diff --git a/src/libstd/collections/hash/table.rs b/src/libstd/collections/hash/table.rs index 878cd82a258..10bab5df8b5 100644 --- a/src/libstd/collections/hash/table.rs +++ b/src/libstd/collections/hash/table.rs @@ -8,9 +8,9 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +use alloc::{Global, Alloc, Layout, CollectionAllocErr}; use cmp; use hash::{BuildHasher, Hash, Hasher}; -use heap::{Heap, Alloc, Layout, CollectionAllocErr}; use marker; use mem::{align_of, size_of, needs_drop}; use mem; @@ -754,7 +754,7 @@ impl RawTable { return Err(CollectionAllocErr::CapacityOverflow); } - let buffer = Heap.alloc(Layout::from_size_align(size, alignment) + let buffer = Global.alloc(Layout::from_size_align(size, alignment) .ok_or(CollectionAllocErr::CapacityOverflow)?)?; let hashes = buffer as *mut HashUint; @@ -772,7 +772,7 @@ impl RawTable { unsafe fn new_uninitialized(capacity: usize) -> RawTable { match Self::try_new_uninitialized(capacity) { Err(CollectionAllocErr::CapacityOverflow) => panic!("capacity overflow"), - Err(CollectionAllocErr::AllocErr(e)) => Heap.oom(e), + Err(CollectionAllocErr::AllocErr(e)) => Global.oom(e), Ok(table) => { table } } } @@ -811,7 +811,7 @@ impl RawTable { pub fn new(capacity: usize) -> RawTable { match Self::try_new(capacity) { Err(CollectionAllocErr::CapacityOverflow) => panic!("capacity overflow"), - Err(CollectionAllocErr::AllocErr(e)) => Heap.oom(e), + Err(CollectionAllocErr::AllocErr(e)) => Global.oom(e), Ok(table) => { table } } } @@ -1185,8 +1185,8 @@ unsafe impl<#[may_dangle] K, #[may_dangle] V> Drop for RawTable { debug_assert!(!oflo, "should be impossible"); unsafe { - Heap.dealloc(self.hashes.ptr() as *mut u8, - Layout::from_size_align(size, align).unwrap()); + Global.dealloc(self.hashes.ptr() as *mut u8, + Layout::from_size_align(size, align).unwrap()); // Remember how everything was allocated out of one buffer // during initialization? We only need one call to free here. } -- cgit 1.4.1-3-g733a5 From 5e5a0c21fc1416e77ae8e4db74b93e3601241e22 Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Tue, 3 Apr 2018 20:58:50 +0200 Subject: Separate alloc::heap::Alloc trait for stage0 #[global_allocator] --- src/Cargo.lock | 2 - src/liballoc/alloc.rs | 5 -- src/liballoc/heap.rs | 98 ++++++++++++++++++++++++++++++++++++++++ src/liballoc/lib.rs | 6 +++ src/liballoc_jemalloc/Cargo.toml | 1 - src/liballoc_jemalloc/lib.rs | 2 +- src/liballoc_system/Cargo.toml | 1 - src/liballoc_system/lib.rs | 8 ++-- 8 files changed, 109 insertions(+), 14 deletions(-) create mode 100644 src/liballoc/heap.rs (limited to 'src/liballoc/alloc.rs') diff --git a/src/Cargo.lock b/src/Cargo.lock index 6e7c4b67acf..f573abadc31 100644 --- a/src/Cargo.lock +++ b/src/Cargo.lock @@ -19,7 +19,6 @@ dependencies = [ name = "alloc_jemalloc" version = "0.0.0" dependencies = [ - "alloc 0.0.0", "alloc_system 0.0.0", "build_helper 0.1.0", "cc 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", @@ -32,7 +31,6 @@ dependencies = [ name = "alloc_system" version = "0.0.0" dependencies = [ - "alloc 0.0.0", "compiler_builtins 0.0.0", "core 0.0.0", "dlmalloc 0.0.0", diff --git a/src/liballoc/alloc.rs b/src/liballoc/alloc.rs index 12ee7701903..00a8b2c0e25 100644 --- a/src/liballoc/alloc.rs +++ b/src/liballoc/alloc.rs @@ -22,11 +22,6 @@ use core::usize; #[doc(inline)] pub use core::alloc::*; -#[doc(hidden)] -pub mod __core { - pub use core::*; -} - extern "Rust" { #[allocator] #[rustc_allocator_nounwind] diff --git a/src/liballoc/heap.rs b/src/liballoc/heap.rs new file mode 100644 index 00000000000..a44ff04bd1b --- /dev/null +++ b/src/liballoc/heap.rs @@ -0,0 +1,98 @@ +// Copyright 2014-2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +pub use alloc::{Excess, Layout, AllocErr, CannotReallocInPlace}; +use core::alloc::Alloc as CoreAlloc; + +#[doc(hidden)] +pub mod __core { + pub use core::*; +} + +/// Compatibility with older versions of #[global_allocator] during bootstrap +pub unsafe trait Alloc { + unsafe fn alloc(&mut self, layout: Layout) -> Result<*mut u8, AllocErr>; + unsafe fn dealloc(&mut self, ptr: *mut u8, layout: Layout); + fn oom(&mut self, err: AllocErr) -> !; + fn usable_size(&self, layout: &Layout) -> (usize, usize); + unsafe fn realloc(&mut self, + ptr: *mut u8, + layout: Layout, + new_layout: Layout) -> Result<*mut u8, AllocErr>; + unsafe fn alloc_zeroed(&mut self, layout: Layout) -> Result<*mut u8, AllocErr>; + unsafe fn alloc_excess(&mut self, layout: Layout) -> Result; + unsafe fn realloc_excess(&mut self, + ptr: *mut u8, + layout: Layout, + new_layout: Layout) -> Result; + unsafe fn grow_in_place(&mut self, + ptr: *mut u8, + layout: Layout, + new_layout: Layout) -> Result<(), CannotReallocInPlace>; + unsafe fn shrink_in_place(&mut self, + ptr: *mut u8, + layout: Layout, + new_layout: Layout) -> Result<(), CannotReallocInPlace>; +} + +#[allow(deprecated)] +unsafe impl Alloc for T where T: CoreAlloc { + unsafe fn alloc(&mut self, layout: Layout) -> Result<*mut u8, AllocErr> { + CoreAlloc::alloc(self, layout) + } + + unsafe fn dealloc(&mut self, ptr: *mut u8, layout: Layout) { + CoreAlloc::dealloc(self, ptr, layout) + } + + fn oom(&mut self, err: AllocErr) -> ! { + CoreAlloc::oom(self, err) + } + + fn usable_size(&self, layout: &Layout) -> (usize, usize) { + CoreAlloc::usable_size(self, layout) + } + + unsafe fn realloc(&mut self, + ptr: *mut u8, + layout: Layout, + new_layout: Layout) -> Result<*mut u8, AllocErr> { + CoreAlloc::realloc(self, ptr, layout, new_layout) + } + + unsafe fn alloc_zeroed(&mut self, layout: Layout) -> Result<*mut u8, AllocErr> { + CoreAlloc::alloc_zeroed(self, layout) + } + + unsafe fn alloc_excess(&mut self, layout: Layout) -> Result { + CoreAlloc::alloc_excess(self, layout) + } + + unsafe fn realloc_excess(&mut self, + ptr: *mut u8, + layout: Layout, + new_layout: Layout) -> Result { + CoreAlloc::realloc_excess(self, ptr, layout, new_layout) + } + + unsafe fn grow_in_place(&mut self, + ptr: *mut u8, + layout: Layout, + new_layout: Layout) -> Result<(), CannotReallocInPlace> { + CoreAlloc::grow_in_place(self, ptr, layout, new_layout) + } + + unsafe fn shrink_in_place(&mut self, + ptr: *mut u8, + layout: Layout, + new_layout: Layout) -> Result<(), CannotReallocInPlace> { + CoreAlloc::shrink_in_place(self, ptr, layout, new_layout) + } +} diff --git a/src/liballoc/lib.rs b/src/liballoc/lib.rs index 066698a71df..f6598fe5e89 100644 --- a/src/liballoc/lib.rs +++ b/src/liballoc/lib.rs @@ -153,10 +153,16 @@ pub mod alloc; #[unstable(feature = "allocator_api", issue = "32838")] #[rustc_deprecated(since = "1.27.0", reason = "module renamed to `alloc`")] /// Use the `alloc` module instead. +#[cfg(not(stage0))] pub mod heap { pub use alloc::*; } +#[unstable(feature = "allocator_api", issue = "32838")] +#[rustc_deprecated(since = "1.27.0", reason = "module renamed to `alloc`")] +#[cfg(stage0)] +pub mod heap; + // Primitive types using the heaps above // Need to conditionally define the mod from `boxed.rs` to avoid diff --git a/src/liballoc_jemalloc/Cargo.toml b/src/liballoc_jemalloc/Cargo.toml index fd4a4553046..02435170374 100644 --- a/src/liballoc_jemalloc/Cargo.toml +++ b/src/liballoc_jemalloc/Cargo.toml @@ -12,7 +12,6 @@ test = false doc = false [dependencies] -alloc = { path = "../liballoc" } alloc_system = { path = "../liballoc_system" } core = { path = "../libcore" } libc = { path = "../rustc/libc_shim" } diff --git a/src/liballoc_jemalloc/lib.rs b/src/liballoc_jemalloc/lib.rs index df7e3f61f5f..616181d99bc 100644 --- a/src/liballoc_jemalloc/lib.rs +++ b/src/liballoc_jemalloc/lib.rs @@ -32,7 +32,7 @@ pub use contents::*; mod contents { use core::ptr; - use core::heap::{Alloc, AllocErr, Layout}; + use core::alloc::{Alloc, AllocErr, Layout}; use alloc_system::System; use libc::{c_int, c_void, size_t}; diff --git a/src/liballoc_system/Cargo.toml b/src/liballoc_system/Cargo.toml index 936e20a32e1..c34e2f203a8 100644 --- a/src/liballoc_system/Cargo.toml +++ b/src/liballoc_system/Cargo.toml @@ -10,7 +10,6 @@ test = false doc = false [dependencies] -alloc = { path = "../liballoc" } core = { path = "../libcore" } libc = { path = "../rustc/libc_shim" } compiler_builtins = { path = "../rustc/compiler_builtins_shim" } diff --git a/src/liballoc_system/lib.rs b/src/liballoc_system/lib.rs index cdcb732f635..2d5adca7fcb 100644 --- a/src/liballoc_system/lib.rs +++ b/src/liballoc_system/lib.rs @@ -41,7 +41,7 @@ const MIN_ALIGN: usize = 8; #[allow(dead_code)] const MIN_ALIGN: usize = 16; -use core::heap::{Alloc, AllocErr, Layout, Excess, CannotReallocInPlace}; +use core::alloc::{Alloc, AllocErr, Layout, Excess, CannotReallocInPlace}; #[unstable(feature = "allocator_api", issue = "32838")] pub struct System; @@ -121,7 +121,7 @@ mod platform { use MIN_ALIGN; use System; - use core::heap::{Alloc, AllocErr, Layout}; + use core::alloc::{Alloc, AllocErr, Layout}; #[unstable(feature = "allocator_api", issue = "32838")] unsafe impl<'a> Alloc for &'a System { @@ -283,7 +283,7 @@ mod platform { use MIN_ALIGN; use System; - use core::heap::{Alloc, AllocErr, Layout, CannotReallocInPlace}; + use core::alloc::{Alloc, AllocErr, Layout, CannotReallocInPlace}; type LPVOID = *mut u8; type HANDLE = LPVOID; @@ -495,7 +495,7 @@ mod platform { mod platform { extern crate dlmalloc; - use core::heap::{Alloc, AllocErr, Layout, Excess, CannotReallocInPlace}; + use core::alloc::{Alloc, AllocErr, Layout, Excess, CannotReallocInPlace}; use System; use self::dlmalloc::GlobalDlmalloc; -- cgit 1.4.1-3-g733a5 From ba7081a033de4981ccad1e1525c8b5191ce02208 Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Tue, 3 Apr 2018 15:41:09 +0200 Subject: Make AllocErr a zero-size unit struct --- src/liballoc/alloc.rs | 32 ++++++++++++------------ src/liballoc/raw_vec.rs | 2 +- src/liballoc_jemalloc/lib.rs | 25 +++---------------- src/liballoc_system/lib.rs | 24 +++++++----------- src/libcore/alloc.rs | 58 ++++++-------------------------------------- src/libstd/alloc.rs | 43 ++++++++++---------------------- src/libstd/error.rs | 2 +- 7 files changed, 51 insertions(+), 135 deletions(-) (limited to 'src/liballoc/alloc.rs') diff --git a/src/liballoc/alloc.rs b/src/liballoc/alloc.rs index 00a8b2c0e25..b975ff6be58 100644 --- a/src/liballoc/alloc.rs +++ b/src/liballoc/alloc.rs @@ -16,7 +16,7 @@ issue = "32838")] use core::intrinsics::{min_align_of_val, size_of_val}; -use core::mem::{self, ManuallyDrop}; +use core::mem; use core::usize; #[doc(inline)] @@ -86,12 +86,12 @@ pub const Heap: Global = Global; unsafe impl Alloc for Global { #[inline] unsafe fn alloc(&mut self, layout: Layout) -> Result<*mut u8, AllocErr> { - let mut err = ManuallyDrop::new(mem::uninitialized::()); + let mut err = AllocErr; let ptr = __rust_alloc(layout.size(), layout.align(), - &mut *err as *mut AllocErr as *mut u8); + &mut err as *mut AllocErr as *mut u8); if ptr.is_null() { - Err(ManuallyDrop::into_inner(err)) + Err(AllocErr) } else { Ok(ptr) } @@ -129,15 +129,15 @@ unsafe impl Alloc for Global { new_layout: Layout) -> Result<*mut u8, AllocErr> { - let mut err = ManuallyDrop::new(mem::uninitialized::()); + let mut err = AllocErr; let ptr = __rust_realloc(ptr, layout.size(), layout.align(), new_layout.size(), new_layout.align(), - &mut *err as *mut AllocErr as *mut u8); + &mut err as *mut AllocErr as *mut u8); if ptr.is_null() { - Err(ManuallyDrop::into_inner(err)) + Err(AllocErr) } else { mem::forget(err); Ok(ptr) @@ -146,12 +146,12 @@ unsafe impl Alloc for Global { #[inline] unsafe fn alloc_zeroed(&mut self, layout: Layout) -> Result<*mut u8, AllocErr> { - let mut err = ManuallyDrop::new(mem::uninitialized::()); + let mut err = AllocErr; let ptr = __rust_alloc_zeroed(layout.size(), layout.align(), - &mut *err as *mut AllocErr as *mut u8); + &mut err as *mut AllocErr as *mut u8); if ptr.is_null() { - Err(ManuallyDrop::into_inner(err)) + Err(AllocErr) } else { Ok(ptr) } @@ -159,14 +159,14 @@ unsafe impl Alloc for Global { #[inline] unsafe fn alloc_excess(&mut self, layout: Layout) -> Result { - let mut err = ManuallyDrop::new(mem::uninitialized::()); + let mut err = AllocErr; let mut size = 0; let ptr = __rust_alloc_excess(layout.size(), layout.align(), &mut size, - &mut *err as *mut AllocErr as *mut u8); + &mut err as *mut AllocErr as *mut u8); if ptr.is_null() { - Err(ManuallyDrop::into_inner(err)) + Err(AllocErr) } else { Ok(Excess(ptr, size)) } @@ -177,7 +177,7 @@ unsafe impl Alloc for Global { ptr: *mut u8, layout: Layout, new_layout: Layout) -> Result { - let mut err = ManuallyDrop::new(mem::uninitialized::()); + let mut err = AllocErr; let mut size = 0; let ptr = __rust_realloc_excess(ptr, layout.size(), @@ -185,9 +185,9 @@ unsafe impl Alloc for Global { new_layout.size(), new_layout.align(), &mut size, - &mut *err as *mut AllocErr as *mut u8); + &mut err as *mut AllocErr as *mut u8); if ptr.is_null() { - Err(ManuallyDrop::into_inner(err)) + Err(AllocErr) } else { Ok(Excess(ptr, size)) } diff --git a/src/liballoc/raw_vec.rs b/src/liballoc/raw_vec.rs index 51f39dc6cc7..caedb971ddc 100644 --- a/src/liballoc/raw_vec.rs +++ b/src/liballoc/raw_vec.rs @@ -760,7 +760,7 @@ mod tests { unsafe fn alloc(&mut self, layout: Layout) -> Result<*mut u8, AllocErr> { let size = layout.size(); if size > self.fuel { - return Err(AllocErr::Unsupported { details: "fuel exhausted" }); + return Err(AllocErr); } match Global.alloc(layout) { ok @ Ok(_) => { self.fuel -= size; ok } diff --git a/src/liballoc_jemalloc/lib.rs b/src/liballoc_jemalloc/lib.rs index 616181d99bc..59a7e87e1ec 100644 --- a/src/liballoc_jemalloc/lib.rs +++ b/src/liballoc_jemalloc/lib.rs @@ -30,8 +30,6 @@ extern crate libc; pub use contents::*; #[cfg(not(dummy_jemalloc))] mod contents { - use core::ptr; - use core::alloc::{Alloc, AllocErr, Layout}; use alloc_system::System; use libc::{c_int, c_void, size_t}; @@ -106,14 +104,9 @@ mod contents { #[rustc_std_internal_symbol] pub unsafe extern fn __rde_alloc(size: usize, align: usize, - err: *mut u8) -> *mut u8 { + _err: *mut u8) -> *mut u8 { let flags = align_to_flags(align, size); let ptr = mallocx(size as size_t, flags) as *mut u8; - if ptr.is_null() { - let layout = Layout::from_size_align_unchecked(size, align); - ptr::write(err as *mut AllocErr, - AllocErr::Exhausted { request: layout }); - } ptr } @@ -155,20 +148,13 @@ mod contents { old_align: usize, new_size: usize, new_align: usize, - err: *mut u8) -> *mut u8 { + _err: *mut u8) -> *mut u8 { if new_align != old_align { - ptr::write(err as *mut AllocErr, - AllocErr::Unsupported { details: "can't change alignments" }); return 0 as *mut u8 } let flags = align_to_flags(new_align, new_size); let ptr = rallocx(ptr as *mut c_void, new_size, flags) as *mut u8; - if ptr.is_null() { - let layout = Layout::from_size_align_unchecked(new_size, new_align); - ptr::write(err as *mut AllocErr, - AllocErr::Exhausted { request: layout }); - } ptr } @@ -176,18 +162,13 @@ mod contents { #[rustc_std_internal_symbol] pub unsafe extern fn __rde_alloc_zeroed(size: usize, align: usize, - err: *mut u8) -> *mut u8 { + _err: *mut u8) -> *mut u8 { let ptr = if align <= MIN_ALIGN && align <= size { calloc(size as size_t, 1) as *mut u8 } else { let flags = align_to_flags(align, size) | MALLOCX_ZERO; mallocx(size as size_t, flags) as *mut u8 }; - if ptr.is_null() { - let layout = Layout::from_size_align_unchecked(size, align); - ptr::write(err as *mut AllocErr, - AllocErr::Exhausted { request: layout }); - } ptr } diff --git a/src/liballoc_system/lib.rs b/src/liballoc_system/lib.rs index 6f928287ef2..5dca05cf085 100644 --- a/src/liballoc_system/lib.rs +++ b/src/liballoc_system/lib.rs @@ -133,9 +133,7 @@ mod platform { #[cfg(target_os = "macos")] { if layout.align() > (1 << 31) { - return Err(AllocErr::Unsupported { - details: "requested alignment too large" - }) + return Err(AllocErr) } } aligned_malloc(&layout) @@ -143,7 +141,7 @@ mod platform { if !ptr.is_null() { Ok(ptr) } else { - Err(AllocErr::Exhausted { request: layout }) + Err(AllocErr) } } @@ -156,7 +154,7 @@ mod platform { if !ptr.is_null() { Ok(ptr) } else { - Err(AllocErr::Exhausted { request: layout }) + Err(AllocErr) } } else { let ret = self.alloc(layout.clone()); @@ -178,9 +176,7 @@ mod platform { old_layout: Layout, new_layout: Layout) -> Result<*mut u8, AllocErr> { if old_layout.align() != new_layout.align() { - return Err(AllocErr::Unsupported { - details: "cannot change alignment on `realloc`", - }) + return Err(AllocErr) } if new_layout.align() <= MIN_ALIGN && new_layout.align() <= new_layout.size(){ @@ -188,7 +184,7 @@ mod platform { if !ptr.is_null() { Ok(ptr as *mut u8) } else { - Err(AllocErr::Exhausted { request: new_layout }) + Err(AllocErr) } } else { let res = self.alloc(new_layout.clone()); @@ -342,7 +338,7 @@ mod platform { } }; if ptr.is_null() { - Err(AllocErr::Exhausted { request: layout }) + Err(AllocErr) } else { Ok(ptr as *mut u8) } @@ -382,9 +378,7 @@ mod platform { old_layout: Layout, new_layout: Layout) -> Result<*mut u8, AllocErr> { if old_layout.align() != new_layout.align() { - return Err(AllocErr::Unsupported { - details: "cannot change alignment on `realloc`", - }) + return Err(AllocErr) } if new_layout.align() <= MIN_ALIGN { @@ -395,7 +389,7 @@ mod platform { if !ptr.is_null() { Ok(ptr as *mut u8) } else { - Err(AllocErr::Exhausted { request: new_layout }) + Err(AllocErr) } } else { let res = self.alloc(new_layout.clone()); @@ -505,7 +499,7 @@ mod platform { if !ptr.is_null() { Ok(ptr) } else { - Err(AllocErr::Unsupported { details: "" }) + Err(AllocErr) } } diff --git a/src/libcore/alloc.rs b/src/libcore/alloc.rs index 5c51bb2b51b..b6626ff9f26 100644 --- a/src/libcore/alloc.rs +++ b/src/libcore/alloc.rs @@ -320,50 +320,12 @@ impl Layout { /// something wrong when combining the given input arguments with this /// allocator. #[derive(Clone, PartialEq, Eq, Debug)] -pub enum AllocErr { - /// Error due to hitting some resource limit or otherwise running - /// out of memory. This condition strongly implies that *some* - /// series of deallocations would allow a subsequent reissuing of - /// the original allocation request to succeed. - Exhausted { request: Layout }, - - /// Error due to allocator being fundamentally incapable of - /// satisfying the original request. This condition implies that - /// such an allocation request will never succeed on the given - /// allocator, regardless of environment, memory pressure, or - /// other contextual conditions. - /// - /// For example, an allocator that does not support requests for - /// large memory blocks might return this error variant. - Unsupported { details: &'static str }, -} - -impl AllocErr { - #[inline] - pub fn invalid_input(details: &'static str) -> Self { - AllocErr::Unsupported { details: details } - } - #[inline] - pub fn is_memory_exhausted(&self) -> bool { - if let AllocErr::Exhausted { .. } = *self { true } else { false } - } - #[inline] - pub fn is_request_unsupported(&self) -> bool { - if let AllocErr::Unsupported { .. } = *self { true } else { false } - } - #[inline] - pub fn description(&self) -> &str { - match *self { - AllocErr::Exhausted { .. } => "allocator memory exhausted", - AllocErr::Unsupported { .. } => "unsupported allocator request", - } - } -} +pub struct AllocErr; // (we need this for downstream impl of trait Error) impl fmt::Display for AllocErr { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}", self.description()) + f.write_str("memory allocation failed") } } @@ -592,12 +554,8 @@ pub unsafe trait Alloc { /// aborting. /// /// `oom` is meant to be used by clients unable to cope with an - /// unsatisfied allocation request (signaled by an error such as - /// `AllocErr::Exhausted`), and wish to abandon computation rather - /// than attempt to recover locally. Such clients should pass the - /// signaling error value back into `oom`, where the allocator - /// may incorporate that error value into its diagnostic report - /// before aborting. + /// unsatisfied allocation request, and wish to abandon + /// computation rather than attempt to recover locally. /// /// Implementations of the `oom` method are discouraged from /// infinitely regressing in nested calls to `oom`. In @@ -963,7 +921,7 @@ pub unsafe trait Alloc { if k.size() > 0 { unsafe { self.alloc(k).map(|p| NonNull::new_unchecked(p as *mut T)) } } else { - Err(AllocErr::invalid_input("zero-sized type invalid for alloc_one")) + Err(AllocErr) } } @@ -1036,7 +994,7 @@ pub unsafe trait Alloc { }) } } - _ => Err(AllocErr::invalid_input("invalid layout for alloc_array")), + _ => Err(AllocErr), } } @@ -1084,7 +1042,7 @@ pub unsafe trait Alloc { .map(|p| NonNull::new_unchecked(p as *mut T)) } _ => { - Err(AllocErr::invalid_input("invalid layout for realloc_array")) + Err(AllocErr) } } } @@ -1118,7 +1076,7 @@ pub unsafe trait Alloc { Ok(self.dealloc(raw_ptr, k.clone())) } _ => { - Err(AllocErr::invalid_input("invalid layout for dealloc_array")) + Err(AllocErr) } } } diff --git a/src/libstd/alloc.rs b/src/libstd/alloc.rs index eb0c960732d..533ad3ad473 100644 --- a/src/libstd/alloc.rs +++ b/src/libstd/alloc.rs @@ -21,9 +21,7 @@ #[doc(hidden)] #[allow(unused_attributes)] pub mod __default_lib_allocator { - use super::{System, Layout, Alloc, AllocErr}; - use ptr; - + use super::{System, Layout, Alloc, AllocErr, CannotReallocInPlace}; // for symbol names src/librustc/middle/allocator.rs // for signatures src/librustc_allocator/lib.rs @@ -34,14 +32,11 @@ pub mod __default_lib_allocator { #[rustc_std_internal_symbol] pub unsafe extern fn __rdl_alloc(size: usize, align: usize, - err: *mut u8) -> *mut u8 { + _err: *mut u8) -> *mut u8 { let layout = Layout::from_size_align_unchecked(size, align); match System.alloc(layout) { Ok(p) => p, - Err(e) => { - ptr::write(err as *mut AllocErr, e); - 0 as *mut u8 - } + Err(AllocErr) => 0 as *mut u8, } } @@ -76,15 +71,12 @@ pub mod __default_lib_allocator { old_align: usize, new_size: usize, new_align: usize, - err: *mut u8) -> *mut u8 { + _err: *mut u8) -> *mut u8 { let old_layout = Layout::from_size_align_unchecked(old_size, old_align); let new_layout = Layout::from_size_align_unchecked(new_size, new_align); match System.realloc(ptr, old_layout, new_layout) { Ok(p) => p, - Err(e) => { - ptr::write(err as *mut AllocErr, e); - 0 as *mut u8 - } + Err(AllocErr) => 0 as *mut u8, } } @@ -92,14 +84,11 @@ pub mod __default_lib_allocator { #[rustc_std_internal_symbol] pub unsafe extern fn __rdl_alloc_zeroed(size: usize, align: usize, - err: *mut u8) -> *mut u8 { + _err: *mut u8) -> *mut u8 { let layout = Layout::from_size_align_unchecked(size, align); match System.alloc_zeroed(layout) { Ok(p) => p, - Err(e) => { - ptr::write(err as *mut AllocErr, e); - 0 as *mut u8 - } + Err(AllocErr) => 0 as *mut u8, } } @@ -108,17 +97,14 @@ pub mod __default_lib_allocator { pub unsafe extern fn __rdl_alloc_excess(size: usize, align: usize, excess: *mut usize, - err: *mut u8) -> *mut u8 { + _err: *mut u8) -> *mut u8 { let layout = Layout::from_size_align_unchecked(size, align); match System.alloc_excess(layout) { Ok(p) => { *excess = p.1; p.0 } - Err(e) => { - ptr::write(err as *mut AllocErr, e); - 0 as *mut u8 - } + Err(AllocErr) => 0 as *mut u8, } } @@ -130,7 +116,7 @@ pub mod __default_lib_allocator { new_size: usize, new_align: usize, excess: *mut usize, - err: *mut u8) -> *mut u8 { + _err: *mut u8) -> *mut u8 { let old_layout = Layout::from_size_align_unchecked(old_size, old_align); let new_layout = Layout::from_size_align_unchecked(new_size, new_align); match System.realloc_excess(ptr, old_layout, new_layout) { @@ -138,10 +124,7 @@ pub mod __default_lib_allocator { *excess = p.1; p.0 } - Err(e) => { - ptr::write(err as *mut AllocErr, e); - 0 as *mut u8 - } + Err(AllocErr) => 0 as *mut u8, } } @@ -156,7 +139,7 @@ pub mod __default_lib_allocator { let new_layout = Layout::from_size_align_unchecked(new_size, new_align); match System.grow_in_place(ptr, old_layout, new_layout) { Ok(()) => 1, - Err(_) => 0, + Err(CannotReallocInPlace) => 0, } } @@ -171,7 +154,7 @@ pub mod __default_lib_allocator { let new_layout = Layout::from_size_align_unchecked(new_size, new_align); match System.shrink_in_place(ptr, old_layout, new_layout) { Ok(()) => 1, - Err(_) => 0, + Err(CannotReallocInPlace) => 0, } } } diff --git a/src/libstd/error.rs b/src/libstd/error.rs index 4edb897350e..ec55a3c021a 100644 --- a/src/libstd/error.rs +++ b/src/libstd/error.rs @@ -243,7 +243,7 @@ impl Error for ! { issue = "32838")] impl Error for AllocErr { fn description(&self) -> &str { - AllocErr::description(self) + "memory allocation failed" } } -- cgit 1.4.1-3-g733a5 From 86753ce1cc520bfe50ae89f09ec47f313ce900eb Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Tue, 3 Apr 2018 17:12:57 +0200 Subject: Use the GlobalAlloc trait for #[global_allocator] --- src/Cargo.lock | 1 - .../src/language-features/global-allocator.md | 8 +- src/liballoc/alloc.rs | 188 +++----------- src/liballoc_jemalloc/Cargo.toml | 1 - src/liballoc_jemalloc/lib.rs | 110 +------- src/librustc_allocator/expand.rs | 283 +++------------------ src/librustc_allocator/lib.rs | 39 +-- src/librustc_trans/allocator.rs | 28 +- src/libstd/alloc.rs | 153 ++++------- src/llvm | 2 +- src/rustllvm/llvm-rebuild-trigger | 2 +- .../compile-fail/allocator/not-an-allocator.rs | 14 +- src/test/run-make-fulldeps/std-core-cycle/bar.rs | 8 +- src/test/run-pass/allocator/auxiliary/custom.rs | 8 +- src/test/run-pass/allocator/custom.rs | 12 +- src/test/run-pass/allocator/xcrate-use.rs | 6 +- src/test/run-pass/allocator/xcrate-use2.rs | 12 +- 17 files changed, 168 insertions(+), 707 deletions(-) (limited to 'src/liballoc/alloc.rs') diff --git a/src/Cargo.lock b/src/Cargo.lock index e5297d1482e..2e969f4ec2b 100644 --- a/src/Cargo.lock +++ b/src/Cargo.lock @@ -19,7 +19,6 @@ dependencies = [ name = "alloc_jemalloc" version = "0.0.0" dependencies = [ - "alloc_system 0.0.0", "build_helper 0.1.0", "cc 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", "compiler_builtins 0.0.0", diff --git a/src/doc/unstable-book/src/language-features/global-allocator.md b/src/doc/unstable-book/src/language-features/global-allocator.md index b3e6925b666..6ce12ba684d 100644 --- a/src/doc/unstable-book/src/language-features/global-allocator.md +++ b/src/doc/unstable-book/src/language-features/global-allocator.md @@ -29,16 +29,16 @@ looks like: ```rust #![feature(global_allocator, allocator_api, heap_api)] -use std::heap::{Alloc, System, Layout, AllocErr}; +use std::alloc::{GlobalAlloc, System, Layout, Void}; struct MyAllocator; -unsafe impl<'a> Alloc for &'a MyAllocator { - unsafe fn alloc(&mut self, layout: Layout) -> Result<*mut u8, AllocErr> { +unsafe impl GlobalAlloc for MyAllocator { + unsafe fn alloc(&self, layout: Layout) -> *mut Void { System.alloc(layout) } - unsafe fn dealloc(&mut self, ptr: *mut u8, layout: Layout) { + unsafe fn dealloc(&self, ptr: *mut Void, layout: Layout) { System.dealloc(ptr, layout) } } diff --git a/src/liballoc/alloc.rs b/src/liballoc/alloc.rs index b975ff6be58..73bc78eb8a2 100644 --- a/src/liballoc/alloc.rs +++ b/src/liballoc/alloc.rs @@ -16,26 +16,19 @@ issue = "32838")] use core::intrinsics::{min_align_of_val, size_of_val}; -use core::mem; use core::usize; #[doc(inline)] pub use core::alloc::*; +#[cfg(stage0)] extern "Rust" { #[allocator] #[rustc_allocator_nounwind] fn __rust_alloc(size: usize, align: usize, err: *mut u8) -> *mut u8; - #[cold] - #[rustc_allocator_nounwind] - fn __rust_oom(err: *const u8) -> !; #[rustc_allocator_nounwind] fn __rust_dealloc(ptr: *mut u8, size: usize, align: usize); #[rustc_allocator_nounwind] - fn __rust_usable_size(layout: *const u8, - min: *mut usize, - max: *mut usize); - #[rustc_allocator_nounwind] fn __rust_realloc(ptr: *mut u8, old_size: usize, old_align: usize, @@ -44,31 +37,22 @@ extern "Rust" { err: *mut u8) -> *mut u8; #[rustc_allocator_nounwind] fn __rust_alloc_zeroed(size: usize, align: usize, err: *mut u8) -> *mut u8; +} + +#[cfg(not(stage0))] +extern "Rust" { + #[allocator] #[rustc_allocator_nounwind] - fn __rust_alloc_excess(size: usize, - align: usize, - excess: *mut usize, - err: *mut u8) -> *mut u8; + fn __rust_alloc(size: usize, align: usize) -> *mut u8; #[rustc_allocator_nounwind] - fn __rust_realloc_excess(ptr: *mut u8, - old_size: usize, - old_align: usize, - new_size: usize, - new_align: usize, - excess: *mut usize, - err: *mut u8) -> *mut u8; + fn __rust_dealloc(ptr: *mut u8, size: usize, align: usize); #[rustc_allocator_nounwind] - fn __rust_grow_in_place(ptr: *mut u8, - old_size: usize, - old_align: usize, - new_size: usize, - new_align: usize) -> u8; + fn __rust_realloc(ptr: *mut u8, + old_size: usize, + align: usize, + new_size: usize) -> *mut u8; #[rustc_allocator_nounwind] - fn __rust_shrink_in_place(ptr: *mut u8, - old_size: usize, - old_align: usize, - new_size: usize, - new_align: usize) -> u8; + fn __rust_alloc_zeroed(size: usize, align: usize) -> *mut u8; } #[derive(Copy, Clone, Default, Debug)] @@ -86,22 +70,15 @@ pub const Heap: Global = Global; unsafe impl Alloc for Global { #[inline] unsafe fn alloc(&mut self, layout: Layout) -> Result<*mut u8, AllocErr> { - let mut err = AllocErr; - let ptr = __rust_alloc(layout.size(), - layout.align(), - &mut err as *mut AllocErr as *mut u8); - if ptr.is_null() { - Err(AllocErr) - } else { - Ok(ptr) - } - } + #[cfg(not(stage0))] + let ptr = __rust_alloc(layout.size(), layout.align()); + #[cfg(stage0)] + let ptr = __rust_alloc(layout.size(), layout.align(), &mut 0); - #[inline] - #[cold] - fn oom(&mut self, err: AllocErr) -> ! { - unsafe { - __rust_oom(&err as *const AllocErr as *const u8) + if !ptr.is_null() { + Ok(ptr) + } else { + Err(AllocErr) } } @@ -110,18 +87,6 @@ unsafe impl Alloc for Global { __rust_dealloc(ptr, layout.size(), layout.align()) } - #[inline] - fn usable_size(&self, layout: &Layout) -> (usize, usize) { - let mut min = 0; - let mut max = 0; - unsafe { - __rust_usable_size(layout as *const Layout as *const u8, - &mut min, - &mut max); - } - (min, max) - } - #[inline] unsafe fn realloc(&mut self, ptr: *mut u8, @@ -129,107 +94,34 @@ unsafe impl Alloc for Global { new_layout: Layout) -> Result<*mut u8, AllocErr> { - let mut err = AllocErr; - let ptr = __rust_realloc(ptr, - layout.size(), - layout.align(), - new_layout.size(), - new_layout.align(), - &mut err as *mut AllocErr as *mut u8); - if ptr.is_null() { - Err(AllocErr) + if layout.align() == new_layout.align() { + #[cfg(not(stage0))] + let ptr = __rust_realloc(ptr, layout.size(), layout.align(), new_layout.size()); + #[cfg(stage0)] + let ptr = __rust_realloc(ptr, layout.size(), layout.align(), + new_layout.size(), new_layout.align(), &mut 0); + + if !ptr.is_null() { + Ok(ptr) + } else { + Err(AllocErr) + } } else { - mem::forget(err); - Ok(ptr) + Err(AllocErr) } } #[inline] unsafe fn alloc_zeroed(&mut self, layout: Layout) -> Result<*mut u8, AllocErr> { - let mut err = AllocErr; - let ptr = __rust_alloc_zeroed(layout.size(), - layout.align(), - &mut err as *mut AllocErr as *mut u8); - if ptr.is_null() { - Err(AllocErr) - } else { - Ok(ptr) - } - } + #[cfg(not(stage0))] + let ptr = __rust_alloc_zeroed(layout.size(), layout.align()); + #[cfg(stage0)] + let ptr = __rust_alloc_zeroed(layout.size(), layout.align(), &mut 0); - #[inline] - unsafe fn alloc_excess(&mut self, layout: Layout) -> Result { - let mut err = AllocErr; - let mut size = 0; - let ptr = __rust_alloc_excess(layout.size(), - layout.align(), - &mut size, - &mut err as *mut AllocErr as *mut u8); - if ptr.is_null() { - Err(AllocErr) + if !ptr.is_null() { + Ok(ptr) } else { - Ok(Excess(ptr, size)) - } - } - - #[inline] - unsafe fn realloc_excess(&mut self, - ptr: *mut u8, - layout: Layout, - new_layout: Layout) -> Result { - let mut err = AllocErr; - let mut size = 0; - let ptr = __rust_realloc_excess(ptr, - layout.size(), - layout.align(), - new_layout.size(), - new_layout.align(), - &mut size, - &mut err as *mut AllocErr as *mut u8); - if ptr.is_null() { Err(AllocErr) - } else { - Ok(Excess(ptr, size)) - } - } - - #[inline] - unsafe fn grow_in_place(&mut self, - ptr: *mut u8, - layout: Layout, - new_layout: Layout) - -> Result<(), CannotReallocInPlace> - { - debug_assert!(new_layout.size() >= layout.size()); - debug_assert!(new_layout.align() == layout.align()); - let ret = __rust_grow_in_place(ptr, - layout.size(), - layout.align(), - new_layout.size(), - new_layout.align()); - if ret != 0 { - Ok(()) - } else { - Err(CannotReallocInPlace) - } - } - - #[inline] - unsafe fn shrink_in_place(&mut self, - ptr: *mut u8, - layout: Layout, - new_layout: Layout) -> Result<(), CannotReallocInPlace> { - debug_assert!(new_layout.size() <= layout.size()); - debug_assert!(new_layout.align() == layout.align()); - let ret = __rust_shrink_in_place(ptr, - layout.size(), - layout.align(), - new_layout.size(), - new_layout.align()); - if ret != 0 { - Ok(()) - } else { - Err(CannotReallocInPlace) } } } diff --git a/src/liballoc_jemalloc/Cargo.toml b/src/liballoc_jemalloc/Cargo.toml index 02435170374..7986d5dd2eb 100644 --- a/src/liballoc_jemalloc/Cargo.toml +++ b/src/liballoc_jemalloc/Cargo.toml @@ -12,7 +12,6 @@ test = false doc = false [dependencies] -alloc_system = { path = "../liballoc_system" } core = { path = "../libcore" } libc = { path = "../rustc/libc_shim" } compiler_builtins = { path = "../rustc/compiler_builtins_shim" } diff --git a/src/liballoc_jemalloc/lib.rs b/src/liballoc_jemalloc/lib.rs index 59a7e87e1ec..661d7ab78da 100644 --- a/src/liballoc_jemalloc/lib.rs +++ b/src/liballoc_jemalloc/lib.rs @@ -14,7 +14,6 @@ reason = "this library is unlikely to be stabilized in its current \ form or name", issue = "27783")] -#![feature(alloc_system)] #![feature(libc)] #![feature(linkage)] #![feature(staged_api)] @@ -23,15 +22,12 @@ #![cfg_attr(not(dummy_jemalloc), feature(allocator_api))] #![rustc_alloc_kind = "exe"] -extern crate alloc_system; extern crate libc; #[cfg(not(dummy_jemalloc))] pub use contents::*; #[cfg(not(dummy_jemalloc))] mod contents { - use core::alloc::{Alloc, AllocErr, Layout}; - use alloc_system::System; use libc::{c_int, c_void, size_t}; // Note that the symbols here are prefixed by default on macOS and Windows (we @@ -50,18 +46,10 @@ mod contents { target_os = "dragonfly", target_os = "windows", target_env = "musl"), 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", target_env = "musl"), - 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", target_env = "musl"), 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", target_env = "musl"), - link_name = "je_nallocx")] - fn nallocx(size: size_t, flags: c_int) -> size_t; } const MALLOCX_ZERO: c_int = 0x40; @@ -102,20 +90,12 @@ mod contents { #[no_mangle] #[rustc_std_internal_symbol] - pub unsafe extern fn __rde_alloc(size: usize, - align: usize, - _err: *mut u8) -> *mut u8 { + pub unsafe extern fn __rde_alloc(size: usize, align: usize) -> *mut u8 { let flags = align_to_flags(align, size); let ptr = mallocx(size as size_t, flags) as *mut u8; ptr } - #[no_mangle] - #[rustc_std_internal_symbol] - pub unsafe extern fn __rde_oom(err: *const u8) -> ! { - System.oom((*(err as *const AllocErr)).clone()) - } - #[no_mangle] #[rustc_std_internal_symbol] pub unsafe extern fn __rde_dealloc(ptr: *mut u8, @@ -125,44 +105,20 @@ mod contents { sdallocx(ptr as *mut c_void, size, flags); } - #[no_mangle] - #[rustc_std_internal_symbol] - pub unsafe extern fn __rde_usable_size(layout: *const u8, - min: *mut usize, - max: *mut usize) { - let layout = &*(layout as *const Layout); - let flags = align_to_flags(layout.align(), layout.size()); - let size = nallocx(layout.size(), flags) as usize; - *min = layout.size(); - if size > 0 { - *max = size; - } else { - *max = layout.size(); - } - } - #[no_mangle] #[rustc_std_internal_symbol] pub unsafe extern fn __rde_realloc(ptr: *mut u8, _old_size: usize, - old_align: usize, - new_size: usize, - new_align: usize, - _err: *mut u8) -> *mut u8 { - if new_align != old_align { - return 0 as *mut u8 - } - - let flags = align_to_flags(new_align, new_size); + align: usize, + new_size: usize) -> *mut u8 { + let flags = align_to_flags(align, new_size); let ptr = rallocx(ptr as *mut c_void, new_size, flags) as *mut u8; ptr } #[no_mangle] #[rustc_std_internal_symbol] - pub unsafe extern fn __rde_alloc_zeroed(size: usize, - align: usize, - _err: *mut u8) -> *mut u8 { + pub unsafe extern fn __rde_alloc_zeroed(size: usize, align: usize) -> *mut u8 { let ptr = if align <= MIN_ALIGN && align <= size { calloc(size as size_t, 1) as *mut u8 } else { @@ -171,60 +127,4 @@ mod contents { }; ptr } - - #[no_mangle] - #[rustc_std_internal_symbol] - pub unsafe extern fn __rde_alloc_excess(size: usize, - align: usize, - excess: *mut usize, - err: *mut u8) -> *mut u8 { - let p = __rde_alloc(size, align, err); - if !p.is_null() { - let flags = align_to_flags(align, size); - *excess = nallocx(size, flags) as usize; - } - return p - } - - #[no_mangle] - #[rustc_std_internal_symbol] - pub unsafe extern fn __rde_realloc_excess(ptr: *mut u8, - old_size: usize, - old_align: usize, - new_size: usize, - new_align: usize, - excess: *mut usize, - err: *mut u8) -> *mut u8 { - let p = __rde_realloc(ptr, old_size, old_align, new_size, new_align, err); - if !p.is_null() { - let flags = align_to_flags(new_align, new_size); - *excess = nallocx(new_size, flags) as usize; - } - p - } - - #[no_mangle] - #[rustc_std_internal_symbol] - pub unsafe extern fn __rde_grow_in_place(ptr: *mut u8, - old_size: usize, - old_align: usize, - new_size: usize, - new_align: usize) -> u8 { - __rde_shrink_in_place(ptr, old_size, old_align, new_size, new_align) - } - - #[no_mangle] - #[rustc_std_internal_symbol] - pub unsafe extern fn __rde_shrink_in_place(ptr: *mut u8, - _old_size: usize, - old_align: usize, - new_size: usize, - new_align: usize) -> u8 { - if old_align == new_align { - let flags = align_to_flags(new_align, new_size); - (xallocx(ptr as *mut c_void, new_size, 0, flags) == new_size) as u8 - } else { - 0 - } - } } diff --git a/src/librustc_allocator/expand.rs b/src/librustc_allocator/expand.rs index ee38cca7828..ce41fe1f3bc 100644 --- a/src/librustc_allocator/expand.rs +++ b/src/librustc_allocator/expand.rs @@ -11,7 +11,7 @@ use rustc::middle::allocator::AllocatorKind; use rustc_errors; use syntax::abi::Abi; -use syntax::ast::{Crate, Attribute, LitKind, StrStyle, ExprKind}; +use syntax::ast::{Crate, Attribute, LitKind, StrStyle}; use syntax::ast::{Unsafety, Constness, Generics, Mutability, Ty, Mac, Arg}; use syntax::ast::{self, Ident, Item, ItemKind, TyKind, VisibilityKind, Expr}; use syntax::attr; @@ -88,7 +88,7 @@ impl<'a> Folder for ExpandAllocatorDirectives<'a> { span, kind: AllocatorKind::Global, global: item.ident, - alloc: Ident::from_str("alloc"), + core: Ident::from_str("core"), cx: ExtCtxt::new(self.sess, ecfg, self.resolver), }; let super_path = f.cx.path(f.span, vec![ @@ -96,7 +96,7 @@ impl<'a> Folder for ExpandAllocatorDirectives<'a> { f.global, ]); let mut items = vec![ - f.cx.item_extern_crate(f.span, f.alloc), + f.cx.item_extern_crate(f.span, f.core), f.cx.item_use_simple( f.span, respan(f.span.shrink_to_lo(), VisibilityKind::Inherited), @@ -126,7 +126,7 @@ struct AllocFnFactory<'a> { span: Span, kind: AllocatorKind, global: Ident, - alloc: Ident, + core: Ident, cx: ExtCtxt<'a>, } @@ -143,8 +143,7 @@ impl<'a> AllocFnFactory<'a> { self.arg_ty(ty, &mut abi_args, mk) }).collect(); let result = self.call_allocator(method.name, args); - let (output_ty, output_expr) = - self.ret_ty(&method.output, &mut abi_args, mk, result); + let (output_ty, output_expr) = self.ret_ty(&method.output, result); let kind = ItemKind::Fn(self.cx.fn_decl(abi_args, ast::FunctionRetTy::Ty(output_ty)), Unsafety::Unsafe, dummy_spanned(Constness::NotConst), @@ -159,16 +158,15 @@ impl<'a> AllocFnFactory<'a> { fn call_allocator(&self, method: &str, mut args: Vec>) -> P { let method = self.cx.path(self.span, vec![ - self.alloc, - Ident::from_str("heap"), - Ident::from_str("Alloc"), + self.core, + Ident::from_str("alloc"), + Ident::from_str("GlobalAlloc"), Ident::from_str(method), ]); let method = self.cx.expr_path(method); let allocator = self.cx.path_ident(self.span, self.global); let allocator = self.cx.expr_path(allocator); let allocator = self.cx.expr_addr_of(self.span, allocator); - let allocator = self.cx.expr_mut_addr_of(self.span, allocator); args.insert(0, allocator); self.cx.expr_call(self.span, method, args) @@ -205,8 +203,8 @@ impl<'a> AllocFnFactory<'a> { args.push(self.cx.arg(self.span, align, ty_usize)); let layout_new = self.cx.path(self.span, vec![ - self.alloc, - Ident::from_str("heap"), + self.core, + Ident::from_str("alloc"), Ident::from_str("Layout"), Ident::from_str("from_size_align_unchecked"), ]); @@ -219,286 +217,67 @@ impl<'a> AllocFnFactory<'a> { layout } - AllocatorTy::LayoutRef => { - let ident = ident(); - args.push(self.cx.arg(self.span, ident, self.ptr_u8())); - - // Convert our `arg: *const u8` via: - // - // &*(arg as *const Layout) - let expr = self.cx.expr_ident(self.span, ident); - let expr = self.cx.expr_cast(self.span, expr, self.layout_ptr()); - let expr = self.cx.expr_deref(self.span, expr); - self.cx.expr_addr_of(self.span, expr) - } - - AllocatorTy::AllocErr => { - // We're creating: - // - // (*(arg as *const AllocErr)).clone() + AllocatorTy::Ptr => { let ident = ident(); args.push(self.cx.arg(self.span, ident, self.ptr_u8())); - let expr = self.cx.expr_ident(self.span, ident); - let expr = self.cx.expr_cast(self.span, expr, self.alloc_err_ptr()); - let expr = self.cx.expr_deref(self.span, expr); - self.cx.expr_method_call( - self.span, - expr, - Ident::from_str("clone"), - Vec::new() - ) + let arg = self.cx.expr_ident(self.span, ident); + self.cx.expr_cast(self.span, arg, self.ptr_void()) } - AllocatorTy::Ptr => { + AllocatorTy::Usize => { let ident = ident(); - args.push(self.cx.arg(self.span, ident, self.ptr_u8())); + args.push(self.cx.arg(self.span, ident, self.usize())); self.cx.expr_ident(self.span, ident) } AllocatorTy::ResultPtr | - AllocatorTy::ResultExcess | - AllocatorTy::ResultUnit | - AllocatorTy::Bang | - AllocatorTy::UsizePair | AllocatorTy::Unit => { panic!("can't convert AllocatorTy to an argument") } } } - fn ret_ty(&self, - ty: &AllocatorTy, - args: &mut Vec, - ident: &mut FnMut() -> Ident, - expr: P) -> (P, P) - { + fn ret_ty(&self, ty: &AllocatorTy, expr: P) -> (P, P) { match *ty { - AllocatorTy::UsizePair => { - // We're creating: - // - // let arg = #expr; - // *min = arg.0; - // *max = arg.1; - - let min = ident(); - let max = ident(); - - args.push(self.cx.arg(self.span, min, self.ptr_usize())); - args.push(self.cx.arg(self.span, max, self.ptr_usize())); - - let ident = ident(); - let stmt = self.cx.stmt_let(self.span, false, ident, expr); - let min = self.cx.expr_ident(self.span, min); - let max = self.cx.expr_ident(self.span, max); - let layout = self.cx.expr_ident(self.span, ident); - let assign_min = self.cx.expr(self.span, ExprKind::Assign( - self.cx.expr_deref(self.span, min), - self.cx.expr_tup_field_access(self.span, layout.clone(), 0), - )); - let assign_min = self.cx.stmt_semi(assign_min); - let assign_max = self.cx.expr(self.span, ExprKind::Assign( - self.cx.expr_deref(self.span, max), - self.cx.expr_tup_field_access(self.span, layout.clone(), 1), - )); - let assign_max = self.cx.stmt_semi(assign_max); - - let stmts = vec![stmt, assign_min, assign_max]; - let block = self.cx.block(self.span, stmts); - let ty_unit = self.cx.ty(self.span, TyKind::Tup(Vec::new())); - (ty_unit, self.cx.expr_block(block)) - } - - AllocatorTy::ResultExcess => { - // We're creating: - // - // match #expr { - // Ok(ptr) => { - // *excess = ptr.1; - // ptr.0 - // } - // Err(e) => { - // ptr::write(err_ptr, e); - // 0 as *mut u8 - // } - // } - - let excess_ptr = ident(); - args.push(self.cx.arg(self.span, excess_ptr, self.ptr_usize())); - let excess_ptr = self.cx.expr_ident(self.span, excess_ptr); - - let err_ptr = ident(); - args.push(self.cx.arg(self.span, err_ptr, self.ptr_u8())); - let err_ptr = self.cx.expr_ident(self.span, err_ptr); - let err_ptr = self.cx.expr_cast(self.span, - err_ptr, - self.alloc_err_ptr()); - - let name = ident(); - let ok_expr = { - let ptr = self.cx.expr_ident(self.span, name); - let write = self.cx.expr(self.span, ExprKind::Assign( - self.cx.expr_deref(self.span, excess_ptr), - self.cx.expr_tup_field_access(self.span, ptr.clone(), 1), - )); - let write = self.cx.stmt_semi(write); - let ret = self.cx.expr_tup_field_access(self.span, - ptr.clone(), - 0); - let ret = self.cx.stmt_expr(ret); - let block = self.cx.block(self.span, vec![write, ret]); - self.cx.expr_block(block) - }; - let pat = self.cx.pat_ident(self.span, name); - let ok = self.cx.path_ident(self.span, Ident::from_str("Ok")); - let ok = self.cx.pat_tuple_struct(self.span, ok, vec![pat]); - let ok = self.cx.arm(self.span, vec![ok], ok_expr); - - let name = ident(); - let err_expr = { - let err = self.cx.expr_ident(self.span, name); - let write = self.cx.path(self.span, vec![ - self.alloc, - Ident::from_str("heap"), - Ident::from_str("__core"), - Ident::from_str("ptr"), - Ident::from_str("write"), - ]); - let write = self.cx.expr_path(write); - let write = self.cx.expr_call(self.span, write, - vec![err_ptr, err]); - let write = self.cx.stmt_semi(write); - let null = self.cx.expr_usize(self.span, 0); - let null = self.cx.expr_cast(self.span, null, self.ptr_u8()); - let null = self.cx.stmt_expr(null); - let block = self.cx.block(self.span, vec![write, null]); - self.cx.expr_block(block) - }; - let pat = self.cx.pat_ident(self.span, name); - let err = self.cx.path_ident(self.span, Ident::from_str("Err")); - let err = self.cx.pat_tuple_struct(self.span, err, vec![pat]); - let err = self.cx.arm(self.span, vec![err], err_expr); - - let expr = self.cx.expr_match(self.span, expr, vec![ok, err]); - (self.ptr_u8(), expr) - } - AllocatorTy::ResultPtr => { // We're creating: // - // match #expr { - // Ok(ptr) => ptr, - // Err(e) => { - // ptr::write(err_ptr, e); - // 0 as *mut u8 - // } - // } - - let err_ptr = ident(); - args.push(self.cx.arg(self.span, err_ptr, self.ptr_u8())); - let err_ptr = self.cx.expr_ident(self.span, err_ptr); - let err_ptr = self.cx.expr_cast(self.span, - err_ptr, - self.alloc_err_ptr()); + // #expr as *mut u8 - let name = ident(); - let ok_expr = self.cx.expr_ident(self.span, name); - let pat = self.cx.pat_ident(self.span, name); - let ok = self.cx.path_ident(self.span, Ident::from_str("Ok")); - let ok = self.cx.pat_tuple_struct(self.span, ok, vec![pat]); - let ok = self.cx.arm(self.span, vec![ok], ok_expr); - - let name = ident(); - let err_expr = { - let err = self.cx.expr_ident(self.span, name); - let write = self.cx.path(self.span, vec![ - self.alloc, - Ident::from_str("heap"), - Ident::from_str("__core"), - Ident::from_str("ptr"), - Ident::from_str("write"), - ]); - let write = self.cx.expr_path(write); - let write = self.cx.expr_call(self.span, write, - vec![err_ptr, err]); - let write = self.cx.stmt_semi(write); - let null = self.cx.expr_usize(self.span, 0); - let null = self.cx.expr_cast(self.span, null, self.ptr_u8()); - let null = self.cx.stmt_expr(null); - let block = self.cx.block(self.span, vec![write, null]); - self.cx.expr_block(block) - }; - let pat = self.cx.pat_ident(self.span, name); - let err = self.cx.path_ident(self.span, Ident::from_str("Err")); - let err = self.cx.pat_tuple_struct(self.span, err, vec![pat]); - let err = self.cx.arm(self.span, vec![err], err_expr); - - let expr = self.cx.expr_match(self.span, expr, vec![ok, err]); + let expr = self.cx.expr_cast(self.span, expr, self.ptr_u8()); (self.ptr_u8(), expr) } - AllocatorTy::ResultUnit => { - // We're creating: - // - // #expr.is_ok() as u8 - - let cast = self.cx.expr_method_call( - self.span, - expr, - Ident::from_str("is_ok"), - Vec::new() - ); - let u8 = self.cx.path_ident(self.span, Ident::from_str("u8")); - let u8 = self.cx.ty_path(u8); - let cast = self.cx.expr_cast(self.span, cast, u8.clone()); - (u8, cast) - } - - AllocatorTy::Bang => { - (self.cx.ty(self.span, TyKind::Never), expr) - } - AllocatorTy::Unit => { (self.cx.ty(self.span, TyKind::Tup(Vec::new())), expr) } - AllocatorTy::AllocErr | AllocatorTy::Layout | - AllocatorTy::LayoutRef | + AllocatorTy::Usize | AllocatorTy::Ptr => { panic!("can't convert AllocatorTy to an output") } } } + fn usize(&self) -> P { + let usize = self.cx.path_ident(self.span, Ident::from_str("usize")); + self.cx.ty_path(usize) + } + fn ptr_u8(&self) -> P { let u8 = self.cx.path_ident(self.span, Ident::from_str("u8")); let ty_u8 = self.cx.ty_path(u8); self.cx.ty_ptr(self.span, ty_u8, Mutability::Mutable) } - fn ptr_usize(&self) -> P { - let usize = self.cx.path_ident(self.span, Ident::from_str("usize")); - let ty_usize = self.cx.ty_path(usize); - self.cx.ty_ptr(self.span, ty_usize, Mutability::Mutable) - } - - fn layout_ptr(&self) -> P { - let layout = self.cx.path(self.span, vec![ - self.alloc, - Ident::from_str("heap"), - Ident::from_str("Layout"), - ]); - let layout = self.cx.ty_path(layout); - self.cx.ty_ptr(self.span, layout, Mutability::Mutable) - } - - fn alloc_err_ptr(&self) -> P { - let err = self.cx.path(self.span, vec![ - self.alloc, - Ident::from_str("heap"), - Ident::from_str("AllocErr"), + fn ptr_void(&self) -> P { + let void = self.cx.path(self.span, vec![ + self.core, + Ident::from_str("alloc"), + Ident::from_str("Void"), ]); - let err = self.cx.ty_path(err); - self.cx.ty_ptr(self.span, err, Mutability::Mutable) + let ty_void = self.cx.ty_path(void); + self.cx.ty_ptr(self.span, ty_void, Mutability::Mutable) } } diff --git a/src/librustc_allocator/lib.rs b/src/librustc_allocator/lib.rs index 0c7a9a91711..969086815de 100644 --- a/src/librustc_allocator/lib.rs +++ b/src/librustc_allocator/lib.rs @@ -23,24 +23,14 @@ pub static ALLOCATOR_METHODS: &[AllocatorMethod] = &[ inputs: &[AllocatorTy::Layout], output: AllocatorTy::ResultPtr, }, - AllocatorMethod { - name: "oom", - inputs: &[AllocatorTy::AllocErr], - output: AllocatorTy::Bang, - }, AllocatorMethod { name: "dealloc", inputs: &[AllocatorTy::Ptr, AllocatorTy::Layout], output: AllocatorTy::Unit, }, - AllocatorMethod { - name: "usable_size", - inputs: &[AllocatorTy::LayoutRef], - output: AllocatorTy::UsizePair, - }, AllocatorMethod { name: "realloc", - inputs: &[AllocatorTy::Ptr, AllocatorTy::Layout, AllocatorTy::Layout], + inputs: &[AllocatorTy::Ptr, AllocatorTy::Layout, AllocatorTy::Usize], output: AllocatorTy::ResultPtr, }, AllocatorMethod { @@ -48,26 +38,6 @@ pub static ALLOCATOR_METHODS: &[AllocatorMethod] = &[ inputs: &[AllocatorTy::Layout], output: AllocatorTy::ResultPtr, }, - AllocatorMethod { - name: "alloc_excess", - inputs: &[AllocatorTy::Layout], - output: AllocatorTy::ResultExcess, - }, - AllocatorMethod { - name: "realloc_excess", - inputs: &[AllocatorTy::Ptr, AllocatorTy::Layout, AllocatorTy::Layout], - output: AllocatorTy::ResultExcess, - }, - AllocatorMethod { - name: "grow_in_place", - inputs: &[AllocatorTy::Ptr, AllocatorTy::Layout, AllocatorTy::Layout], - output: AllocatorTy::ResultUnit, - }, - AllocatorMethod { - name: "shrink_in_place", - inputs: &[AllocatorTy::Ptr, AllocatorTy::Layout, AllocatorTy::Layout], - output: AllocatorTy::ResultUnit, - }, ]; pub struct AllocatorMethod { @@ -77,14 +47,9 @@ pub struct AllocatorMethod { } pub enum AllocatorTy { - AllocErr, - Bang, Layout, - LayoutRef, Ptr, - ResultExcess, ResultPtr, - ResultUnit, Unit, - UsizePair, + Usize, } diff --git a/src/librustc_trans/allocator.rs b/src/librustc_trans/allocator.rs index e1c145b122d..ffebb959ebf 100644 --- a/src/librustc_trans/allocator.rs +++ b/src/librustc_trans/allocator.rs @@ -30,7 +30,6 @@ pub(crate) unsafe fn trans(tcx: TyCtxt, mods: &ModuleLlvm, kind: AllocatorKind) }; let i8 = llvm::LLVMInt8TypeInContext(llcx); let i8p = llvm::LLVMPointerType(i8, 0); - let usizep = llvm::LLVMPointerType(usize, 0); let void = llvm::LLVMVoidTypeInContext(llcx); for method in ALLOCATOR_METHODS { @@ -41,40 +40,19 @@ pub(crate) unsafe fn trans(tcx: TyCtxt, mods: &ModuleLlvm, kind: AllocatorKind) args.push(usize); // size args.push(usize); // align } - AllocatorTy::LayoutRef => args.push(i8p), AllocatorTy::Ptr => args.push(i8p), - AllocatorTy::AllocErr => args.push(i8p), + AllocatorTy::Usize => args.push(usize), - AllocatorTy::Bang | - AllocatorTy::ResultExcess | AllocatorTy::ResultPtr | - AllocatorTy::ResultUnit | - AllocatorTy::UsizePair | AllocatorTy::Unit => panic!("invalid allocator arg"), } } let output = match method.output { - AllocatorTy::UsizePair => { - args.push(usizep); // min - args.push(usizep); // max - None - } - AllocatorTy::Bang => None, - AllocatorTy::ResultExcess => { - args.push(i8p); // excess_ptr - args.push(i8p); // err_ptr - Some(i8p) - } - AllocatorTy::ResultPtr => { - args.push(i8p); // err_ptr - Some(i8p) - } - AllocatorTy::ResultUnit => Some(i8), + AllocatorTy::ResultPtr => Some(i8p), AllocatorTy::Unit => None, - AllocatorTy::AllocErr | AllocatorTy::Layout | - AllocatorTy::LayoutRef | + AllocatorTy::Usize | AllocatorTy::Ptr => panic!("invalid allocator output"), }; let ty = llvm::LLVMFunctionType(output.unwrap_or(void), diff --git a/src/libstd/alloc.rs b/src/libstd/alloc.rs index 533ad3ad473..335dc7e0412 100644 --- a/src/libstd/alloc.rs +++ b/src/libstd/alloc.rs @@ -21,7 +21,7 @@ #[doc(hidden)] #[allow(unused_attributes)] pub mod __default_lib_allocator { - use super::{System, Layout, Alloc, AllocErr, CannotReallocInPlace}; + use super::{System, Layout, GlobalAlloc, Void}; // for symbol names src/librustc/middle/allocator.rs // for signatures src/librustc_allocator/lib.rs @@ -30,20 +30,9 @@ pub mod __default_lib_allocator { #[no_mangle] #[rustc_std_internal_symbol] - pub unsafe extern fn __rdl_alloc(size: usize, - align: usize, - _err: *mut u8) -> *mut u8 { + pub unsafe extern fn __rdl_alloc(size: usize, align: usize) -> *mut u8 { let layout = Layout::from_size_align_unchecked(size, align); - match System.alloc(layout) { - Ok(p) => p, - Err(AllocErr) => 0 as *mut u8, - } - } - - #[no_mangle] - #[rustc_std_internal_symbol] - pub unsafe extern fn __rdl_oom(err: *const u8) -> ! { - System.oom((*(err as *const AllocErr)).clone()) + System.alloc(layout) as *mut u8 } #[no_mangle] @@ -51,110 +40,76 @@ pub mod __default_lib_allocator { pub unsafe extern fn __rdl_dealloc(ptr: *mut u8, size: usize, align: usize) { - System.dealloc(ptr, Layout::from_size_align_unchecked(size, align)) - } - - #[no_mangle] - #[rustc_std_internal_symbol] - pub unsafe extern fn __rdl_usable_size(layout: *const u8, - min: *mut usize, - max: *mut usize) { - let pair = System.usable_size(&*(layout as *const Layout)); - *min = pair.0; - *max = pair.1; + System.dealloc(ptr as *mut Void, Layout::from_size_align_unchecked(size, align)) } #[no_mangle] #[rustc_std_internal_symbol] pub unsafe extern fn __rdl_realloc(ptr: *mut u8, old_size: usize, - old_align: usize, - new_size: usize, - new_align: usize, - _err: *mut u8) -> *mut u8 { - let old_layout = Layout::from_size_align_unchecked(old_size, old_align); - let new_layout = Layout::from_size_align_unchecked(new_size, new_align); - match System.realloc(ptr, old_layout, new_layout) { - Ok(p) => p, - Err(AllocErr) => 0 as *mut u8, - } + align: usize, + new_size: usize) -> *mut u8 { + let old_layout = Layout::from_size_align_unchecked(old_size, align); + System.realloc(ptr as *mut Void, old_layout, new_size) as *mut u8 } #[no_mangle] #[rustc_std_internal_symbol] - pub unsafe extern fn __rdl_alloc_zeroed(size: usize, - align: usize, - _err: *mut u8) -> *mut u8 { + pub unsafe extern fn __rdl_alloc_zeroed(size: usize, align: usize) -> *mut u8 { let layout = Layout::from_size_align_unchecked(size, align); - match System.alloc_zeroed(layout) { - Ok(p) => p, - Err(AllocErr) => 0 as *mut u8, - } + System.alloc_zeroed(layout) as *mut u8 } - #[no_mangle] - #[rustc_std_internal_symbol] - pub unsafe extern fn __rdl_alloc_excess(size: usize, - align: usize, - excess: *mut usize, - _err: *mut u8) -> *mut u8 { - let layout = Layout::from_size_align_unchecked(size, align); - match System.alloc_excess(layout) { - Ok(p) => { - *excess = p.1; - p.0 - } - Err(AllocErr) => 0 as *mut u8, + #[cfg(stage0)] + pub mod stage0 { + #[no_mangle] + #[rustc_std_internal_symbol] + pub unsafe extern fn __rdl_usable_size(_layout: *const u8, + _min: *mut usize, + _max: *mut usize) { + unimplemented!() } - } - #[no_mangle] - #[rustc_std_internal_symbol] - pub unsafe extern fn __rdl_realloc_excess(ptr: *mut u8, - old_size: usize, - old_align: usize, - new_size: usize, - new_align: usize, - excess: *mut usize, - _err: *mut u8) -> *mut u8 { - let old_layout = Layout::from_size_align_unchecked(old_size, old_align); - let new_layout = Layout::from_size_align_unchecked(new_size, new_align); - match System.realloc_excess(ptr, old_layout, new_layout) { - Ok(p) => { - *excess = p.1; - p.0 - } - Err(AllocErr) => 0 as *mut u8, + #[no_mangle] + #[rustc_std_internal_symbol] + pub unsafe extern fn __rdl_alloc_excess(_size: usize, + _align: usize, + _excess: *mut usize, + _err: *mut u8) -> *mut u8 { + unimplemented!() } - } - #[no_mangle] - #[rustc_std_internal_symbol] - pub unsafe extern fn __rdl_grow_in_place(ptr: *mut u8, - old_size: usize, - old_align: usize, - new_size: usize, - new_align: usize) -> u8 { - let old_layout = Layout::from_size_align_unchecked(old_size, old_align); - let new_layout = Layout::from_size_align_unchecked(new_size, new_align); - match System.grow_in_place(ptr, old_layout, new_layout) { - Ok(()) => 1, - Err(CannotReallocInPlace) => 0, + #[no_mangle] + #[rustc_std_internal_symbol] + pub unsafe extern fn __rdl_realloc_excess(_ptr: *mut u8, + _old_size: usize, + _old_align: usize, + _new_size: usize, + _new_align: usize, + _excess: *mut usize, + _err: *mut u8) -> *mut u8 { + unimplemented!() } - } - #[no_mangle] - #[rustc_std_internal_symbol] - pub unsafe extern fn __rdl_shrink_in_place(ptr: *mut u8, - old_size: usize, - old_align: usize, - new_size: usize, - new_align: usize) -> u8 { - let old_layout = Layout::from_size_align_unchecked(old_size, old_align); - let new_layout = Layout::from_size_align_unchecked(new_size, new_align); - match System.shrink_in_place(ptr, old_layout, new_layout) { - Ok(()) => 1, - Err(CannotReallocInPlace) => 0, + #[no_mangle] + #[rustc_std_internal_symbol] + pub unsafe extern fn __rdl_grow_in_place(_ptr: *mut u8, + _old_size: usize, + _old_align: usize, + _new_size: usize, + _new_align: usize) -> u8 { + unimplemented!() } + + #[no_mangle] + #[rustc_std_internal_symbol] + pub unsafe extern fn __rdl_shrink_in_place(_ptr: *mut u8, + _old_size: usize, + _old_align: usize, + _new_size: usize, + _new_align: usize) -> u8 { + unimplemented!() + } + } } diff --git a/src/llvm b/src/llvm index 6ceaaa4b017..7243155b1c3 160000 --- a/src/llvm +++ b/src/llvm @@ -1 +1 @@ -Subproject commit 6ceaaa4b0176a200e4bbd347d6a991ab6c776ede +Subproject commit 7243155b1c3da0a980c868a87adebf00e0b33989 diff --git a/src/rustllvm/llvm-rebuild-trigger b/src/rustllvm/llvm-rebuild-trigger index c4c0f1ab6e6..c3fc3e5452c 100644 --- a/src/rustllvm/llvm-rebuild-trigger +++ b/src/rustllvm/llvm-rebuild-trigger @@ -1,4 +1,4 @@ # If this file is modified, then llvm will be (optionally) cleaned and then rebuilt. # The actual contents of this file do not matter, but to trigger a change on the # build bots then the contents should be changed so git updates the mtime. -2018-03-10 +2018-04-05 diff --git a/src/test/compile-fail/allocator/not-an-allocator.rs b/src/test/compile-fail/allocator/not-an-allocator.rs index e4301435063..140cad22f34 100644 --- a/src/test/compile-fail/allocator/not-an-allocator.rs +++ b/src/test/compile-fail/allocator/not-an-allocator.rs @@ -12,15 +12,9 @@ #[global_allocator] static A: usize = 0; -//~^ the trait bound `&usize: -//~| the trait bound `&usize: -//~| the trait bound `&usize: -//~| the trait bound `&usize: -//~| the trait bound `&usize: -//~| the trait bound `&usize: -//~| the trait bound `&usize: -//~| the trait bound `&usize: -//~| the trait bound `&usize: -//~| the trait bound `&usize: +//~^ the trait bound `usize: +//~| the trait bound `usize: +//~| the trait bound `usize: +//~| the trait bound `usize: fn main() {} diff --git a/src/test/run-make-fulldeps/std-core-cycle/bar.rs b/src/test/run-make-fulldeps/std-core-cycle/bar.rs index 6def5b6f5e1..20b87028fd1 100644 --- a/src/test/run-make-fulldeps/std-core-cycle/bar.rs +++ b/src/test/run-make-fulldeps/std-core-cycle/bar.rs @@ -11,16 +11,16 @@ #![feature(allocator_api)] #![crate_type = "rlib"] -use std::heap::*; +use std::alloc::*; pub struct A; -unsafe impl<'a> Alloc for &'a A { - unsafe fn alloc(&mut self, _: Layout) -> Result<*mut u8, AllocErr> { +unsafe impl GlobalAlloc for A { + unsafe fn alloc(&self, _: Layout) -> *mut Void { loop {} } - unsafe fn dealloc(&mut self, _ptr: *mut u8, _: Layout) { + unsafe fn dealloc(&self, _ptr: *mut Void, _: Layout) { loop {} } } diff --git a/src/test/run-pass/allocator/auxiliary/custom.rs b/src/test/run-pass/allocator/auxiliary/custom.rs index 8f4fbcd5ab1..95096efc7ef 100644 --- a/src/test/run-pass/allocator/auxiliary/custom.rs +++ b/src/test/run-pass/allocator/auxiliary/custom.rs @@ -13,18 +13,18 @@ #![feature(heap_api, allocator_api)] #![crate_type = "rlib"] -use std::heap::{Alloc, System, AllocErr, Layout}; +use std::heap::{GlobalAlloc, System, Layout, Void}; use std::sync::atomic::{AtomicUsize, Ordering}; pub struct A(pub AtomicUsize); -unsafe impl<'a> Alloc for &'a A { - unsafe fn alloc(&mut self, layout: Layout) -> Result<*mut u8, AllocErr> { +unsafe impl GlobalAlloc for A { + unsafe fn alloc(&self, layout: Layout) -> *mut Void { self.0.fetch_add(1, Ordering::SeqCst); System.alloc(layout) } - unsafe fn dealloc(&mut self, ptr: *mut u8, layout: Layout) { + unsafe fn dealloc(&self, ptr: *mut Void, layout: Layout) { self.0.fetch_add(1, Ordering::SeqCst); System.dealloc(ptr, layout) } diff --git a/src/test/run-pass/allocator/custom.rs b/src/test/run-pass/allocator/custom.rs index 22081678fb9..f7b2fd73c87 100644 --- a/src/test/run-pass/allocator/custom.rs +++ b/src/test/run-pass/allocator/custom.rs @@ -15,20 +15,20 @@ extern crate helper; -use std::heap::{Heap, Alloc, System, Layout, AllocErr}; +use std::alloc::{self, Global, Alloc, System, Layout, Void}; use std::sync::atomic::{AtomicUsize, Ordering, ATOMIC_USIZE_INIT}; static HITS: AtomicUsize = ATOMIC_USIZE_INIT; struct A; -unsafe impl<'a> Alloc for &'a A { - unsafe fn alloc(&mut self, layout: Layout) -> Result<*mut u8, AllocErr> { +unsafe impl alloc::GlobalAlloc for A { + unsafe fn alloc(&self, layout: Layout) -> *mut Void { HITS.fetch_add(1, Ordering::SeqCst); System.alloc(layout) } - unsafe fn dealloc(&mut self, ptr: *mut u8, layout: Layout) { + unsafe fn dealloc(&self, ptr: *mut Void, layout: Layout) { HITS.fetch_add(1, Ordering::SeqCst); System.dealloc(ptr, layout) } @@ -45,10 +45,10 @@ fn main() { unsafe { let layout = Layout::from_size_align(4, 2).unwrap(); - let ptr = Heap.alloc(layout.clone()).unwrap(); + let ptr = Global.alloc(layout.clone()).unwrap(); helper::work_with(&ptr); assert_eq!(HITS.load(Ordering::SeqCst), n + 1); - Heap.dealloc(ptr, layout.clone()); + Global.dealloc(ptr, layout.clone()); assert_eq!(HITS.load(Ordering::SeqCst), n + 2); let s = String::with_capacity(10); diff --git a/src/test/run-pass/allocator/xcrate-use.rs b/src/test/run-pass/allocator/xcrate-use.rs index 04d2ef466e7..78d604a7108 100644 --- a/src/test/run-pass/allocator/xcrate-use.rs +++ b/src/test/run-pass/allocator/xcrate-use.rs @@ -17,7 +17,7 @@ extern crate custom; extern crate helper; -use std::heap::{Heap, Alloc, System, Layout}; +use std::alloc::{Global, Alloc, System, Layout}; use std::sync::atomic::{Ordering, ATOMIC_USIZE_INIT}; #[global_allocator] @@ -28,10 +28,10 @@ fn main() { let n = GLOBAL.0.load(Ordering::SeqCst); let layout = Layout::from_size_align(4, 2).unwrap(); - let ptr = Heap.alloc(layout.clone()).unwrap(); + let ptr = Global.alloc(layout.clone()).unwrap(); helper::work_with(&ptr); assert_eq!(GLOBAL.0.load(Ordering::SeqCst), n + 1); - Heap.dealloc(ptr, layout.clone()); + Global.dealloc(ptr, layout.clone()); assert_eq!(GLOBAL.0.load(Ordering::SeqCst), n + 2); let ptr = System.alloc(layout.clone()).unwrap(); diff --git a/src/test/run-pass/allocator/xcrate-use2.rs b/src/test/run-pass/allocator/xcrate-use2.rs index 155fb5d6c5d..52eb963efdb 100644 --- a/src/test/run-pass/allocator/xcrate-use2.rs +++ b/src/test/run-pass/allocator/xcrate-use2.rs @@ -19,7 +19,7 @@ extern crate custom; extern crate custom_as_global; extern crate helper; -use std::heap::{Heap, Alloc, System, Layout}; +use std::alloc::{Global, Alloc, GlobalAlloc, System, Layout}; use std::sync::atomic::{Ordering, ATOMIC_USIZE_INIT}; static GLOBAL: custom::A = custom::A(ATOMIC_USIZE_INIT); @@ -30,25 +30,25 @@ fn main() { let layout = Layout::from_size_align(4, 2).unwrap(); // Global allocator routes to the `custom_as_global` global - let ptr = Heap.alloc(layout.clone()).unwrap(); + let ptr = Global.alloc(layout.clone()).unwrap(); helper::work_with(&ptr); assert_eq!(custom_as_global::get(), n + 1); - Heap.dealloc(ptr, layout.clone()); + Global.dealloc(ptr, layout.clone()); assert_eq!(custom_as_global::get(), n + 2); // Usage of the system allocator avoids all globals - let ptr = System.alloc(layout.clone()).unwrap(); + let ptr = System.alloc(layout.clone()); helper::work_with(&ptr); assert_eq!(custom_as_global::get(), n + 2); System.dealloc(ptr, layout.clone()); assert_eq!(custom_as_global::get(), n + 2); // Usage of our personal allocator doesn't affect other instances - let ptr = (&GLOBAL).alloc(layout.clone()).unwrap(); + let ptr = GLOBAL.alloc(layout.clone()); helper::work_with(&ptr); assert_eq!(custom_as_global::get(), n + 2); assert_eq!(GLOBAL.0.load(Ordering::SeqCst), 1); - (&GLOBAL).dealloc(ptr, layout); + GLOBAL.dealloc(ptr, layout); assert_eq!(custom_as_global::get(), n + 2); assert_eq!(GLOBAL.0.load(Ordering::SeqCst), 2); } -- cgit 1.4.1-3-g733a5 From 157ff8cd0562eefdd7aa296395c38a7bc259a4b9 Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Tue, 3 Apr 2018 16:00:04 +0200 Subject: Remove the now-unit-struct AllocErr parameter of oom() --- src/liballoc/alloc.rs | 6 +++--- src/liballoc/arc.rs | 2 +- src/liballoc/heap.rs | 4 ++-- src/liballoc/raw_vec.rs | 12 ++++++------ src/liballoc/rc.rs | 2 +- src/liballoc_system/lib.rs | 12 ++++++------ src/libcore/alloc.rs | 2 +- src/libstd/collections/hash/map.rs | 2 +- src/libstd/collections/hash/table.rs | 4 ++-- src/test/run-pass/allocator-alloc-one.rs | 4 ++-- src/test/run-pass/realloc-16687.rs | 4 ++-- src/test/run-pass/regions-mock-trans.rs | 2 +- 12 files changed, 28 insertions(+), 28 deletions(-) (limited to 'src/liballoc/alloc.rs') diff --git a/src/liballoc/alloc.rs b/src/liballoc/alloc.rs index 73bc78eb8a2..a7b5864016c 100644 --- a/src/liballoc/alloc.rs +++ b/src/liballoc/alloc.rs @@ -136,8 +136,8 @@ unsafe fn exchange_malloc(size: usize, align: usize) -> *mut u8 { align as *mut u8 } else { let layout = Layout::from_size_align_unchecked(size, align); - Global.alloc(layout).unwrap_or_else(|err| { - Global.oom(err) + Global.alloc(layout).unwrap_or_else(|_| { + Global.oom() }) } } @@ -166,7 +166,7 @@ mod tests { unsafe { let layout = Layout::from_size_align(1024, 1).unwrap(); let ptr = Global.alloc_zeroed(layout.clone()) - .unwrap_or_else(|e| Global.oom(e)); + .unwrap_or_else(|_| Global.oom()); let end = ptr.offset(layout.size() as isize); let mut i = ptr; diff --git a/src/liballoc/arc.rs b/src/liballoc/arc.rs index d63ed24aa4f..f0a325530ba 100644 --- a/src/liballoc/arc.rs +++ b/src/liballoc/arc.rs @@ -555,7 +555,7 @@ impl Arc { let layout = Layout::for_value(&*fake_ptr); let mem = Global.alloc(layout) - .unwrap_or_else(|e| Global.oom(e)); + .unwrap_or_else(|_| Global.oom()); // Initialize the real ArcInner let inner = set_data_ptr(ptr as *mut T, mem) as *mut ArcInner; diff --git a/src/liballoc/heap.rs b/src/liballoc/heap.rs index a44ff04bd1b..765fb8458d1 100644 --- a/src/liballoc/heap.rs +++ b/src/liballoc/heap.rs @@ -52,8 +52,8 @@ unsafe impl Alloc for T where T: CoreAlloc { CoreAlloc::dealloc(self, ptr, layout) } - fn oom(&mut self, err: AllocErr) -> ! { - CoreAlloc::oom(self, err) + fn oom(&mut self, _: AllocErr) -> ! { + CoreAlloc::oom(self) } fn usable_size(&self, layout: &Layout) -> (usize, usize) { diff --git a/src/liballoc/raw_vec.rs b/src/liballoc/raw_vec.rs index caedb971ddc..25d759764a5 100644 --- a/src/liballoc/raw_vec.rs +++ b/src/liballoc/raw_vec.rs @@ -100,7 +100,7 @@ impl RawVec { }; match result { Ok(ptr) => ptr, - Err(err) => a.oom(err), + Err(_) => a.oom(), } }; @@ -316,7 +316,7 @@ impl RawVec { new_layout); match ptr_res { Ok(ptr) => (new_cap, Unique::new_unchecked(ptr as *mut T)), - Err(e) => self.a.oom(e), + Err(_) => self.a.oom(), } } None => { @@ -325,7 +325,7 @@ impl RawVec { let new_cap = if elem_size > (!0) / 8 { 1 } else { 4 }; match self.a.alloc_array::(new_cap) { Ok(ptr) => (new_cap, ptr.into()), - Err(e) => self.a.oom(e), + Err(_) => self.a.oom(), } } }; @@ -444,7 +444,7 @@ impl RawVec { pub fn reserve_exact(&mut self, used_cap: usize, needed_extra_cap: usize) { match self.try_reserve_exact(used_cap, needed_extra_cap) { Err(CapacityOverflow) => panic!("capacity overflow"), - Err(AllocErr(e)) => self.a.oom(e), + Err(AllocErr(_)) => self.a.oom(), Ok(()) => { /* yay */ } } } @@ -554,7 +554,7 @@ impl RawVec { pub fn reserve(&mut self, used_cap: usize, needed_extra_cap: usize) { match self.try_reserve(used_cap, needed_extra_cap) { Err(CapacityOverflow) => panic!("capacity overflow"), - Err(AllocErr(e)) => self.a.oom(e), + Err(AllocErr(_)) => self.a.oom(), Ok(()) => { /* yay */ } } } @@ -669,7 +669,7 @@ impl RawVec { old_layout, new_layout) { Ok(p) => self.ptr = Unique::new_unchecked(p as *mut T), - Err(err) => self.a.oom(err), + Err(_) => self.a.oom(), } } self.cap = amount; diff --git a/src/liballoc/rc.rs b/src/liballoc/rc.rs index c134b181158..3c0b11bfe74 100644 --- a/src/liballoc/rc.rs +++ b/src/liballoc/rc.rs @@ -668,7 +668,7 @@ impl Rc { let layout = Layout::for_value(&*fake_ptr); let mem = Global.alloc(layout) - .unwrap_or_else(|e| Global.oom(e)); + .unwrap_or_else(|_| Global.oom()); // Initialize the real RcBox let inner = set_data_ptr(ptr as *mut T, mem) as *mut RcBox; diff --git a/src/liballoc_system/lib.rs b/src/liballoc_system/lib.rs index 0480be8d913..5e6b3b5ca11 100644 --- a/src/liballoc_system/lib.rs +++ b/src/liballoc_system/lib.rs @@ -73,8 +73,8 @@ unsafe impl Alloc for System { Alloc::realloc(&mut &*self, ptr, old_layout, new_layout) } - fn oom(&mut self, err: AllocErr) -> ! { - Alloc::oom(&mut &*self, err) + fn oom(&mut self) -> ! { + Alloc::oom(&mut &*self) } #[inline] @@ -242,7 +242,7 @@ mod platform { unsafe impl<'a> Alloc for &'a System { alloc_methods_based_on_global_alloc!(); - fn oom(&mut self, err: AllocErr) -> ! { + fn oom(&mut self) -> ! { use core::fmt::{self, Write}; // Print a message to stderr before aborting to assist with @@ -250,7 +250,7 @@ mod platform { // memory since we are in an OOM situation. Any errors are ignored // while printing since there's nothing we can do about them and we // are about to exit anyways. - drop(writeln!(Stderr, "fatal runtime error: {}", err)); + drop(writeln!(Stderr, "fatal runtime error: {}", AllocErr)); unsafe { ::core::intrinsics::abort(); } @@ -459,11 +459,11 @@ mod platform { } } - fn oom(&mut self, err: AllocErr) -> ! { + fn oom(&mut self) -> ! { use core::fmt::{self, Write}; // Same as with unix we ignore all errors here - drop(writeln!(Stderr, "fatal runtime error: {}", err)); + drop(writeln!(Stderr, "fatal runtime error: {}", AllocErr)); unsafe { ::core::intrinsics::abort(); } diff --git a/src/libcore/alloc.rs b/src/libcore/alloc.rs index 1c764dab000..1ba4c641065 100644 --- a/src/libcore/alloc.rs +++ b/src/libcore/alloc.rs @@ -572,7 +572,7 @@ pub unsafe trait Alloc { /// instead they should return an appropriate error from the /// invoked method, and let the client decide whether to invoke /// this `oom` method in response. - fn oom(&mut self, _: AllocErr) -> ! { + fn oom(&mut self) -> ! { unsafe { ::intrinsics::abort() } } diff --git a/src/libstd/collections/hash/map.rs b/src/libstd/collections/hash/map.rs index c4ef9e62577..2a00241afc6 100644 --- a/src/libstd/collections/hash/map.rs +++ b/src/libstd/collections/hash/map.rs @@ -784,7 +784,7 @@ impl HashMap pub fn reserve(&mut self, additional: usize) { match self.try_reserve(additional) { Err(CollectionAllocErr::CapacityOverflow) => panic!("capacity overflow"), - Err(CollectionAllocErr::AllocErr(e)) => Global.oom(e), + Err(CollectionAllocErr::AllocErr(_)) => Global.oom(), Ok(()) => { /* yay */ } } } diff --git a/src/libstd/collections/hash/table.rs b/src/libstd/collections/hash/table.rs index 10bab5df8b5..fcc2eb8fef2 100644 --- a/src/libstd/collections/hash/table.rs +++ b/src/libstd/collections/hash/table.rs @@ -772,7 +772,7 @@ impl RawTable { unsafe fn new_uninitialized(capacity: usize) -> RawTable { match Self::try_new_uninitialized(capacity) { Err(CollectionAllocErr::CapacityOverflow) => panic!("capacity overflow"), - Err(CollectionAllocErr::AllocErr(e)) => Global.oom(e), + Err(CollectionAllocErr::AllocErr(_)) => Global.oom(), Ok(table) => { table } } } @@ -811,7 +811,7 @@ impl RawTable { pub fn new(capacity: usize) -> RawTable { match Self::try_new(capacity) { Err(CollectionAllocErr::CapacityOverflow) => panic!("capacity overflow"), - Err(CollectionAllocErr::AllocErr(e)) => Global.oom(e), + Err(CollectionAllocErr::AllocErr(_)) => Global.oom(), Ok(table) => { table } } } diff --git a/src/test/run-pass/allocator-alloc-one.rs b/src/test/run-pass/allocator-alloc-one.rs index eaa5bc90805..38b8ab50cc7 100644 --- a/src/test/run-pass/allocator-alloc-one.rs +++ b/src/test/run-pass/allocator-alloc-one.rs @@ -14,8 +14,8 @@ use std::heap::{Heap, Alloc}; fn main() { unsafe { - let ptr = Heap.alloc_one::().unwrap_or_else(|e| { - Heap.oom(e) + let ptr = Heap.alloc_one::().unwrap_or_else(|_| { + Heap.oom() }); *ptr.as_ptr() = 4; assert_eq!(*ptr.as_ptr(), 4); diff --git a/src/test/run-pass/realloc-16687.rs b/src/test/run-pass/realloc-16687.rs index eddcd5a584a..a562165d21b 100644 --- a/src/test/run-pass/realloc-16687.rs +++ b/src/test/run-pass/realloc-16687.rs @@ -50,7 +50,7 @@ unsafe fn test_triangle() -> bool { println!("allocate({:?})", layout); } - let ret = Heap.alloc(layout.clone()).unwrap_or_else(|e| Heap.oom(e)); + let ret = Heap.alloc(layout.clone()).unwrap_or_else(|_| Heap.oom()); if PRINT { println!("allocate({:?}) = {:?}", layout, ret); @@ -73,7 +73,7 @@ unsafe fn test_triangle() -> bool { } let ret = Heap.realloc(ptr, old.clone(), new.clone()) - .unwrap_or_else(|e| Heap.oom(e)); + .unwrap_or_else(|_| Heap.oom()); if PRINT { println!("reallocate({:?}, old={:?}, new={:?}) = {:?}", diff --git a/src/test/run-pass/regions-mock-trans.rs b/src/test/run-pass/regions-mock-trans.rs index 8f278a315d1..7d34b8fd00f 100644 --- a/src/test/run-pass/regions-mock-trans.rs +++ b/src/test/run-pass/regions-mock-trans.rs @@ -32,7 +32,7 @@ struct Ccx { fn alloc<'a>(_bcx : &'a arena) -> &'a Bcx<'a> { unsafe { let ptr = Heap.alloc(Layout::new::()) - .unwrap_or_else(|e| Heap.oom(e)); + .unwrap_or_else(|_| Heap.oom()); &*(ptr as *const _) } } -- cgit 1.4.1-3-g733a5 From c957e99b305ecee113442a7ce0edd6b565200ca9 Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Wed, 4 Apr 2018 17:19:16 +0200 Subject: realloc with a new size only, not a full new layout. Changing the alignment with realloc is not supported. --- src/liballoc/alloc.rs | 22 +++++------- src/liballoc/heap.rs | 8 ++--- src/liballoc/raw_vec.rs | 17 +++++---- src/liballoc_system/lib.rs | 42 +++++++++------------- src/libcore/alloc.rs | 87 +++++++++++++++++++--------------------------- 5 files changed, 74 insertions(+), 102 deletions(-) (limited to 'src/liballoc/alloc.rs') diff --git a/src/liballoc/alloc.rs b/src/liballoc/alloc.rs index a7b5864016c..a6fc8d5004c 100644 --- a/src/liballoc/alloc.rs +++ b/src/liballoc/alloc.rs @@ -91,21 +91,17 @@ unsafe impl Alloc for Global { unsafe fn realloc(&mut self, ptr: *mut u8, layout: Layout, - new_layout: Layout) + new_size: usize) -> Result<*mut u8, AllocErr> { - if layout.align() == new_layout.align() { - #[cfg(not(stage0))] - let ptr = __rust_realloc(ptr, layout.size(), layout.align(), new_layout.size()); - #[cfg(stage0)] - let ptr = __rust_realloc(ptr, layout.size(), layout.align(), - new_layout.size(), new_layout.align(), &mut 0); - - if !ptr.is_null() { - Ok(ptr) - } else { - Err(AllocErr) - } + #[cfg(not(stage0))] + let ptr = __rust_realloc(ptr, layout.size(), layout.align(), new_size); + #[cfg(stage0)] + let ptr = __rust_realloc(ptr, layout.size(), layout.align(), + new_size, layout.align(), &mut 0); + + if !ptr.is_null() { + Ok(ptr) } else { Err(AllocErr) } diff --git a/src/liballoc/heap.rs b/src/liballoc/heap.rs index 765fb8458d1..e79383331e1 100644 --- a/src/liballoc/heap.rs +++ b/src/liballoc/heap.rs @@ -64,7 +64,7 @@ unsafe impl Alloc for T where T: CoreAlloc { ptr: *mut u8, layout: Layout, new_layout: Layout) -> Result<*mut u8, AllocErr> { - CoreAlloc::realloc(self, ptr, layout, new_layout) + CoreAlloc::realloc(self, ptr, layout, new_layout.size()) } unsafe fn alloc_zeroed(&mut self, layout: Layout) -> Result<*mut u8, AllocErr> { @@ -79,20 +79,20 @@ unsafe impl Alloc for T where T: CoreAlloc { ptr: *mut u8, layout: Layout, new_layout: Layout) -> Result { - CoreAlloc::realloc_excess(self, ptr, layout, new_layout) + CoreAlloc::realloc_excess(self, ptr, layout, new_layout.size()) } unsafe fn grow_in_place(&mut self, ptr: *mut u8, layout: Layout, new_layout: Layout) -> Result<(), CannotReallocInPlace> { - CoreAlloc::grow_in_place(self, ptr, layout, new_layout) + CoreAlloc::grow_in_place(self, ptr, layout, new_layout.size()) } unsafe fn shrink_in_place(&mut self, ptr: *mut u8, layout: Layout, new_layout: Layout) -> Result<(), CannotReallocInPlace> { - CoreAlloc::shrink_in_place(self, ptr, layout, new_layout) + CoreAlloc::shrink_in_place(self, ptr, layout, new_layout.size()) } } diff --git a/src/liballoc/raw_vec.rs b/src/liballoc/raw_vec.rs index 18aaf1de08e..80b816878fb 100644 --- a/src/liballoc/raw_vec.rs +++ b/src/liballoc/raw_vec.rs @@ -309,11 +309,10 @@ impl RawVec { // `from_size_align_unchecked`. let new_cap = 2 * self.cap; let new_size = new_cap * elem_size; - let new_layout = Layout::from_size_align_unchecked(new_size, cur.align()); alloc_guard(new_size).expect("capacity overflow"); let ptr_res = self.a.realloc(self.ptr.as_ptr() as *mut u8, cur, - new_layout); + new_size); match ptr_res { Ok(ptr) => (new_cap, Unique::new_unchecked(ptr as *mut T)), Err(_) => self.a.oom(), @@ -371,8 +370,7 @@ impl RawVec { let new_size = new_cap * elem_size; alloc_guard(new_size).expect("capacity overflow"); let ptr = self.ptr() as *mut _; - let new_layout = Layout::from_size_align_unchecked(new_size, old_layout.align()); - match self.a.grow_in_place(ptr, old_layout, new_layout) { + match self.a.grow_in_place(ptr, old_layout, new_size) { Ok(_) => { // We can't directly divide `size`. self.cap = new_cap; @@ -428,8 +426,9 @@ impl RawVec { let res = match self.current_layout() { Some(layout) => { + debug_assert!(new_layout.align() == layout.align()); let old_ptr = self.ptr.as_ptr() as *mut u8; - self.a.realloc(old_ptr, layout, new_layout) + self.a.realloc(old_ptr, layout, new_layout.size()) } None => self.a.alloc(new_layout), }; @@ -537,8 +536,9 @@ impl RawVec { let res = match self.current_layout() { Some(layout) => { + debug_assert!(new_layout.align() == layout.align()); let old_ptr = self.ptr.as_ptr() as *mut u8; - self.a.realloc(old_ptr, layout, new_layout) + self.a.realloc(old_ptr, layout, new_layout.size()) } None => self.a.alloc(new_layout), }; @@ -604,7 +604,7 @@ impl RawVec { let new_layout = Layout::new::().repeat(new_cap).unwrap().0; // FIXME: may crash and burn on over-reserve alloc_guard(new_layout.size()).expect("capacity overflow"); - match self.a.grow_in_place(ptr, old_layout, new_layout) { + match self.a.grow_in_place(ptr, old_layout, new_layout.size()) { Ok(_) => { self.cap = new_cap; true @@ -664,10 +664,9 @@ impl RawVec { let new_size = elem_size * amount; let align = mem::align_of::(); let old_layout = Layout::from_size_align_unchecked(old_size, align); - let new_layout = Layout::from_size_align_unchecked(new_size, align); match self.a.realloc(self.ptr.as_ptr() as *mut u8, old_layout, - new_layout) { + new_size) { Ok(p) => self.ptr = Unique::new_unchecked(p as *mut T), Err(_) => self.a.oom(), } diff --git a/src/liballoc_system/lib.rs b/src/liballoc_system/lib.rs index 48a6c1e150a..7b788a5f989 100644 --- a/src/liballoc_system/lib.rs +++ b/src/liballoc_system/lib.rs @@ -69,8 +69,8 @@ unsafe impl Alloc for System { unsafe fn realloc(&mut self, ptr: *mut u8, old_layout: Layout, - new_layout: Layout) -> Result<*mut u8, AllocErr> { - Alloc::realloc(&mut &*self, ptr, old_layout, new_layout) + new_size: usize) -> Result<*mut u8, AllocErr> { + Alloc::realloc(&mut &*self, ptr, old_layout, new_size) } fn oom(&mut self) -> ! { @@ -91,24 +91,24 @@ unsafe impl Alloc for System { unsafe fn realloc_excess(&mut self, ptr: *mut u8, layout: Layout, - new_layout: Layout) -> Result { - Alloc::realloc_excess(&mut &*self, ptr, layout, new_layout) + new_size: usize) -> Result { + Alloc::realloc_excess(&mut &*self, ptr, layout, new_size) } #[inline] unsafe fn grow_in_place(&mut self, ptr: *mut u8, layout: Layout, - new_layout: Layout) -> Result<(), CannotReallocInPlace> { - Alloc::grow_in_place(&mut &*self, ptr, layout, new_layout) + new_size: usize) -> Result<(), CannotReallocInPlace> { + Alloc::grow_in_place(&mut &*self, ptr, layout, new_size) } #[inline] unsafe fn shrink_in_place(&mut self, ptr: *mut u8, layout: Layout, - new_layout: Layout) -> Result<(), CannotReallocInPlace> { - Alloc::shrink_in_place(&mut &*self, ptr, layout, new_layout) + new_size: usize) -> Result<(), CannotReallocInPlace> { + Alloc::shrink_in_place(&mut &*self, ptr, layout, new_size) } } @@ -166,12 +166,8 @@ macro_rules! alloc_methods_based_on_global_alloc { unsafe fn realloc(&mut self, ptr: *mut u8, old_layout: Layout, - new_layout: Layout) -> Result<*mut u8, AllocErr> { - if old_layout.align() != new_layout.align() { - return Err(AllocErr) - } - - let ptr = GlobalAlloc::realloc(*self, ptr as *mut Void, old_layout, new_layout.size()); + new_size: usize) -> Result<*mut u8, AllocErr> { + let ptr = GlobalAlloc::realloc(*self, ptr as *mut Void, old_layout, new_size); if !ptr.is_null() { Ok(ptr as *mut u8) } else { @@ -428,30 +424,26 @@ mod platform { unsafe fn grow_in_place(&mut self, ptr: *mut u8, layout: Layout, - new_layout: Layout) -> Result<(), CannotReallocInPlace> { - self.shrink_in_place(ptr, layout, new_layout) + new_size: usize) -> Result<(), CannotReallocInPlace> { + self.shrink_in_place(ptr, layout, new_size) } #[inline] unsafe fn shrink_in_place(&mut self, ptr: *mut u8, - old_layout: Layout, - new_layout: Layout) -> Result<(), CannotReallocInPlace> { - if old_layout.align() != new_layout.align() { - return Err(CannotReallocInPlace) - } - - let new = if new_layout.align() <= MIN_ALIGN { + layout: Layout, + new_size: usize) -> Result<(), CannotReallocInPlace> { + let new = if layout.align() <= MIN_ALIGN { HeapReAlloc(GetProcessHeap(), HEAP_REALLOC_IN_PLACE_ONLY, ptr as LPVOID, - new_layout.size()) + new_size) } else { let header = get_header(ptr); HeapReAlloc(GetProcessHeap(), HEAP_REALLOC_IN_PLACE_ONLY, header.0 as LPVOID, - new_layout.size() + new_layout.align()) + new_size + layout.align()) }; if new.is_null() { Err(CannotReallocInPlace) diff --git a/src/libcore/alloc.rs b/src/libcore/alloc.rs index 0acaf54e0d9..757f06e731f 100644 --- a/src/libcore/alloc.rs +++ b/src/libcore/alloc.rs @@ -633,9 +633,10 @@ pub unsafe trait Alloc { // realloc. alloc_excess, realloc_excess /// Returns a pointer suitable for holding data described by - /// `new_layout`, meeting its size and alignment guarantees. To + /// a new layout with `layout`’s alginment and a size given + /// by `new_size`. To /// accomplish this, this may extend or shrink the allocation - /// referenced by `ptr` to fit `new_layout`. + /// referenced by `ptr` to fit the new layout. /// /// If this returns `Ok`, then ownership of the memory block /// referenced by `ptr` has been transferred to this @@ -648,12 +649,6 @@ pub unsafe trait Alloc { /// block has not been transferred to this allocator, and the /// contents of the memory block are unaltered. /// - /// For best results, `new_layout` should not impose a different - /// alignment constraint than `layout`. (In other words, - /// `new_layout.align()` should equal `layout.align()`.) However, - /// behavior is well-defined (though underspecified) when this - /// constraint is violated; further discussion below. - /// /// # Safety /// /// This function is unsafe because undefined behavior can result @@ -661,12 +656,13 @@ pub unsafe trait Alloc { /// /// * `ptr` must be currently allocated via this allocator, /// - /// * `layout` must *fit* the `ptr` (see above). (The `new_layout` + /// * `layout` must *fit* the `ptr` (see above). (The `new_size` /// argument need not fit it.) /// - /// * `new_layout` must have size greater than zero. + /// * `new_size` must be greater than zero. /// - /// * the alignment of `new_layout` is non-zero. + /// * `new_size`, when rounded up to the nearest multiple of `layout.align()`, + /// must not overflow (i.e. the rounded value must be less than `usize::MAX`). /// /// (Extension subtraits might provide more specific bounds on /// behavior, e.g. guarantee a sentinel address or a null pointer @@ -674,18 +670,11 @@ pub unsafe trait Alloc { /// /// # Errors /// - /// Returns `Err` only if `new_layout` does not match the - /// alignment of `layout`, or does not meet the allocator's size + /// Returns `Err` only if the new layout + /// does not meet the allocator's size /// and alignment constraints of the allocator, or if reallocation /// otherwise fails. /// - /// (Note the previous sentence did not say "if and only if" -- in - /// particular, an implementation of this method *can* return `Ok` - /// if `new_layout.align() != old_layout.align()`; or it can - /// return `Err` in that scenario, depending on whether this - /// allocator can dynamically adjust the alignment constraint for - /// the block.) - /// /// Implementations are encouraged to return `Err` on memory /// exhaustion rather than panicking or aborting, but this is not /// a strict requirement. (Specifically: it is *legal* to @@ -698,22 +687,21 @@ pub unsafe trait Alloc { unsafe fn realloc(&mut self, ptr: *mut u8, layout: Layout, - new_layout: Layout) -> Result<*mut u8, AllocErr> { - let new_size = new_layout.size(); + new_size: usize) -> Result<*mut u8, AllocErr> { let old_size = layout.size(); - let aligns_match = layout.align == new_layout.align; - if new_size >= old_size && aligns_match { - if let Ok(()) = self.grow_in_place(ptr, layout.clone(), new_layout.clone()) { + if new_size >= old_size { + if let Ok(()) = self.grow_in_place(ptr, layout.clone(), new_size) { return Ok(ptr); } - } else if new_size < old_size && aligns_match { - if let Ok(()) = self.shrink_in_place(ptr, layout.clone(), new_layout.clone()) { + } else if new_size < old_size { + if let Ok(()) = self.shrink_in_place(ptr, layout.clone(), new_size) { return Ok(ptr); } } // otherwise, fall back on alloc + copy + dealloc. + let new_layout = Layout::from_size_align_unchecked(new_size, layout.align()); let result = self.alloc(new_layout); if let Ok(new_ptr) = result { ptr::copy_nonoverlapping(ptr as *const u8, new_ptr, cmp::min(old_size, new_size)); @@ -789,17 +777,19 @@ pub unsafe trait Alloc { unsafe fn realloc_excess(&mut self, ptr: *mut u8, layout: Layout, - new_layout: Layout) -> Result { + new_size: usize) -> Result { + let new_layout = Layout::from_size_align_unchecked(new_size, layout.align()); let usable_size = self.usable_size(&new_layout); - self.realloc(ptr, layout, new_layout) + self.realloc(ptr, layout, new_size) .map(|p| Excess(p, usable_size.1)) } - /// Attempts to extend the allocation referenced by `ptr` to fit `new_layout`. + /// Attempts to extend the allocation referenced by `ptr` to fit `new_size`. /// /// If this returns `Ok`, then the allocator has asserted that the - /// memory block referenced by `ptr` now fits `new_layout`, and thus can - /// be used to carry data of that layout. (The allocator is allowed to + /// memory block referenced by `ptr` now fits `new_size`, and thus can + /// be used to carry data of a layout of that size and same alignment as + /// `layout`. (The allocator is allowed to /// expend effort to accomplish this, such as extending the memory block to /// include successor blocks, or virtual memory tricks.) /// @@ -815,11 +805,9 @@ pub unsafe trait Alloc { /// * `ptr` must be currently allocated via this allocator, /// /// * `layout` must *fit* the `ptr` (see above); note the - /// `new_layout` argument need not fit it, + /// `new_size` argument need not fit it, /// - /// * `new_layout.size()` must not be less than `layout.size()`, - /// - /// * `new_layout.align()` must equal `layout.align()`. + /// * `new_size` must not be less than `layout.size()`, /// /// # Errors /// @@ -834,24 +822,23 @@ pub unsafe trait Alloc { unsafe fn grow_in_place(&mut self, ptr: *mut u8, layout: Layout, - new_layout: Layout) -> Result<(), CannotReallocInPlace> { + new_size: usize) -> Result<(), CannotReallocInPlace> { let _ = ptr; // this default implementation doesn't care about the actual address. - debug_assert!(new_layout.size >= layout.size); - debug_assert!(new_layout.align == layout.align); + debug_assert!(new_size >= layout.size); let (_l, u) = self.usable_size(&layout); // _l <= layout.size() [guaranteed by usable_size()] // layout.size() <= new_layout.size() [required by this method] - if new_layout.size <= u { + if new_size <= u { return Ok(()); } else { return Err(CannotReallocInPlace); } } - /// Attempts to shrink the allocation referenced by `ptr` to fit `new_layout`. + /// Attempts to shrink the allocation referenced by `ptr` to fit `new_size`. /// /// If this returns `Ok`, then the allocator has asserted that the - /// memory block referenced by `ptr` now fits `new_layout`, and + /// memory block referenced by `ptr` now fits `new_size`, and /// thus can only be used to carry data of that smaller /// layout. (The allocator is allowed to take advantage of this, /// carving off portions of the block for reuse elsewhere.) The @@ -872,13 +859,11 @@ pub unsafe trait Alloc { /// * `ptr` must be currently allocated via this allocator, /// /// * `layout` must *fit* the `ptr` (see above); note the - /// `new_layout` argument need not fit it, + /// `new_size` argument need not fit it, /// - /// * `new_layout.size()` must not be greater than `layout.size()` + /// * `new_size` must not be greater than `layout.size()` /// (and must be greater than zero), /// - /// * `new_layout.align()` must equal `layout.align()`. - /// /// # Errors /// /// Returns `Err(CannotReallocInPlace)` when the allocator is @@ -892,14 +877,13 @@ pub unsafe trait Alloc { unsafe fn shrink_in_place(&mut self, ptr: *mut u8, layout: Layout, - new_layout: Layout) -> Result<(), CannotReallocInPlace> { + new_size: usize) -> Result<(), CannotReallocInPlace> { let _ = ptr; // this default implementation doesn't care about the actual address. - debug_assert!(new_layout.size <= layout.size); - debug_assert!(new_layout.align == layout.align); + debug_assert!(new_size <= layout.size); let (l, _u) = self.usable_size(&layout); // layout.size() <= _u [guaranteed by usable_size()] // new_layout.size() <= layout.size() [required by this method] - if l <= new_layout.size { + if l <= new_size { return Ok(()); } else { return Err(CannotReallocInPlace); @@ -1061,7 +1045,8 @@ pub unsafe trait Alloc { { match (Layout::array::(n_old), Layout::array::(n_new), ptr.as_ptr()) { (Ok(ref k_old), Ok(ref k_new), ptr) if k_old.size() > 0 && k_new.size() > 0 => { - self.realloc(ptr as *mut u8, k_old.clone(), k_new.clone()) + debug_assert!(k_old.align() == k_new.align()); + self.realloc(ptr as *mut u8, k_old.clone(), k_new.size()) .map(|p| NonNull::new_unchecked(p as *mut T)) } _ => { -- cgit 1.4.1-3-g733a5 From eae0d468932660ca383e35bb9d8b0cb4943a82ae Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Wed, 4 Apr 2018 18:57:48 +0200 Subject: Restore Global.oom() functionality MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit … now that #[global_allocator] does not define a symbol for it --- src/Cargo.lock | 1 + src/liballoc/alloc.rs | 16 ++++++++++++++++ src/liballoc/lib.rs | 1 + src/liballoc_jemalloc/Cargo.toml | 1 + src/liballoc_jemalloc/lib.rs | 10 ++++++++++ src/liballoc_system/lib.rs | 4 ++++ src/libcore/alloc.rs | 4 ++++ src/librustc_allocator/expand.rs | 5 +++++ src/librustc_allocator/lib.rs | 6 ++++++ src/librustc_trans/allocator.rs | 2 ++ src/libstd/alloc.rs | 6 ++++++ src/test/compile-fail/allocator/not-an-allocator.rs | 1 + 12 files changed, 57 insertions(+) (limited to 'src/liballoc/alloc.rs') diff --git a/src/Cargo.lock b/src/Cargo.lock index 2e969f4ec2b..e5297d1482e 100644 --- a/src/Cargo.lock +++ b/src/Cargo.lock @@ -19,6 +19,7 @@ dependencies = [ name = "alloc_jemalloc" version = "0.0.0" dependencies = [ + "alloc_system 0.0.0", "build_helper 0.1.0", "cc 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", "compiler_builtins 0.0.0", diff --git a/src/liballoc/alloc.rs b/src/liballoc/alloc.rs index a6fc8d5004c..beae52726a6 100644 --- a/src/liballoc/alloc.rs +++ b/src/liballoc/alloc.rs @@ -26,6 +26,9 @@ extern "Rust" { #[allocator] #[rustc_allocator_nounwind] fn __rust_alloc(size: usize, align: usize, err: *mut u8) -> *mut u8; + #[cold] + #[rustc_allocator_nounwind] + fn __rust_oom(err: *const u8) -> !; #[rustc_allocator_nounwind] fn __rust_dealloc(ptr: *mut u8, size: usize, align: usize); #[rustc_allocator_nounwind] @@ -44,6 +47,9 @@ extern "Rust" { #[allocator] #[rustc_allocator_nounwind] fn __rust_alloc(size: usize, align: usize) -> *mut u8; + #[cold] + #[rustc_allocator_nounwind] + fn __rust_oom() -> !; #[rustc_allocator_nounwind] fn __rust_dealloc(ptr: *mut u8, size: usize, align: usize); #[rustc_allocator_nounwind] @@ -120,6 +126,16 @@ unsafe impl Alloc for Global { Err(AllocErr) } } + + #[inline] + fn oom(&mut self) -> ! { + unsafe { + #[cfg(not(stage0))] + __rust_oom(); + #[cfg(stage0)] + __rust_oom(&mut 0); + } + } } /// The allocator for unique pointers. diff --git a/src/liballoc/lib.rs b/src/liballoc/lib.rs index f6598fe5e89..a10820ebefd 100644 --- a/src/liballoc/lib.rs +++ b/src/liballoc/lib.rs @@ -97,6 +97,7 @@ #![feature(from_ref)] #![feature(fundamental)] #![feature(lang_items)] +#![feature(libc)] #![feature(needs_allocator)] #![feature(nonzero)] #![feature(optin_builtin_traits)] diff --git a/src/liballoc_jemalloc/Cargo.toml b/src/liballoc_jemalloc/Cargo.toml index 7986d5dd2eb..02435170374 100644 --- a/src/liballoc_jemalloc/Cargo.toml +++ b/src/liballoc_jemalloc/Cargo.toml @@ -12,6 +12,7 @@ test = false doc = false [dependencies] +alloc_system = { path = "../liballoc_system" } core = { path = "../libcore" } libc = { path = "../rustc/libc_shim" } compiler_builtins = { path = "../rustc/compiler_builtins_shim" } diff --git a/src/liballoc_jemalloc/lib.rs b/src/liballoc_jemalloc/lib.rs index 661d7ab78da..2b66c293f21 100644 --- a/src/liballoc_jemalloc/lib.rs +++ b/src/liballoc_jemalloc/lib.rs @@ -14,6 +14,7 @@ reason = "this library is unlikely to be stabilized in its current \ form or name", issue = "27783")] +#![feature(alloc_system)] #![feature(libc)] #![feature(linkage)] #![feature(staged_api)] @@ -22,12 +23,15 @@ #![cfg_attr(not(dummy_jemalloc), feature(allocator_api))] #![rustc_alloc_kind = "exe"] +extern crate alloc_system; extern crate libc; #[cfg(not(dummy_jemalloc))] pub use contents::*; #[cfg(not(dummy_jemalloc))] mod contents { + use core::alloc::GlobalAlloc; + use alloc_system::System; use libc::{c_int, c_void, size_t}; // Note that the symbols here are prefixed by default on macOS and Windows (we @@ -96,6 +100,12 @@ mod contents { ptr } + #[no_mangle] + #[rustc_std_internal_symbol] + pub unsafe extern fn __rde_oom() -> ! { + System.oom() + } + #[no_mangle] #[rustc_std_internal_symbol] pub unsafe extern fn __rde_dealloc(ptr: *mut u8, diff --git a/src/liballoc_system/lib.rs b/src/liballoc_system/lib.rs index 4516664e97c..c6507282b24 100644 --- a/src/liballoc_system/lib.rs +++ b/src/liballoc_system/lib.rs @@ -367,6 +367,7 @@ mod platform { } } +#[inline] fn oom() -> ! { write_to_stderr("fatal runtime error: memory allocation failed"); unsafe { @@ -375,6 +376,7 @@ fn oom() -> ! { } #[cfg(any(unix, target_os = "redox"))] +#[inline] fn write_to_stderr(s: &str) { extern crate libc; @@ -386,6 +388,7 @@ fn write_to_stderr(s: &str) { } #[cfg(windows)] +#[inline] fn write_to_stderr(s: &str) { use core::ptr; @@ -421,4 +424,5 @@ fn write_to_stderr(s: &str) { } #[cfg(not(any(windows, unix, target_os = "redox")))] +#[inline] fn write_to_stderr(_: &str) {} diff --git a/src/libcore/alloc.rs b/src/libcore/alloc.rs index cfa7df06a40..7334f986f2b 100644 --- a/src/libcore/alloc.rs +++ b/src/libcore/alloc.rs @@ -438,6 +438,10 @@ pub unsafe trait GlobalAlloc { } new_ptr } + + fn oom(&self) -> ! { + unsafe { ::intrinsics::abort() } + } } /// An implementation of `Alloc` can allocate, reallocate, and diff --git a/src/librustc_allocator/expand.rs b/src/librustc_allocator/expand.rs index ce41fe1f3bc..58d4c7f289c 100644 --- a/src/librustc_allocator/expand.rs +++ b/src/librustc_allocator/expand.rs @@ -231,6 +231,7 @@ impl<'a> AllocFnFactory<'a> { } AllocatorTy::ResultPtr | + AllocatorTy::Bang | AllocatorTy::Unit => { panic!("can't convert AllocatorTy to an argument") } @@ -248,6 +249,10 @@ impl<'a> AllocFnFactory<'a> { (self.ptr_u8(), expr) } + AllocatorTy::Bang => { + (self.cx.ty(self.span, TyKind::Never), expr) + } + AllocatorTy::Unit => { (self.cx.ty(self.span, TyKind::Tup(Vec::new())), expr) } diff --git a/src/librustc_allocator/lib.rs b/src/librustc_allocator/lib.rs index 969086815de..706eab72d44 100644 --- a/src/librustc_allocator/lib.rs +++ b/src/librustc_allocator/lib.rs @@ -23,6 +23,11 @@ pub static ALLOCATOR_METHODS: &[AllocatorMethod] = &[ inputs: &[AllocatorTy::Layout], output: AllocatorTy::ResultPtr, }, + AllocatorMethod { + name: "oom", + inputs: &[], + output: AllocatorTy::Bang, + }, AllocatorMethod { name: "dealloc", inputs: &[AllocatorTy::Ptr, AllocatorTy::Layout], @@ -47,6 +52,7 @@ pub struct AllocatorMethod { } pub enum AllocatorTy { + Bang, Layout, Ptr, ResultPtr, diff --git a/src/librustc_trans/allocator.rs b/src/librustc_trans/allocator.rs index ffebb959ebf..f2dd2ed8460 100644 --- a/src/librustc_trans/allocator.rs +++ b/src/librustc_trans/allocator.rs @@ -43,11 +43,13 @@ pub(crate) unsafe fn trans(tcx: TyCtxt, mods: &ModuleLlvm, kind: AllocatorKind) AllocatorTy::Ptr => args.push(i8p), AllocatorTy::Usize => args.push(usize), + AllocatorTy::Bang | AllocatorTy::ResultPtr | AllocatorTy::Unit => panic!("invalid allocator arg"), } } let output = match method.output { + AllocatorTy::Bang => None, AllocatorTy::ResultPtr => Some(i8p), AllocatorTy::Unit => None, diff --git a/src/libstd/alloc.rs b/src/libstd/alloc.rs index 335dc7e0412..4e728df010a 100644 --- a/src/libstd/alloc.rs +++ b/src/libstd/alloc.rs @@ -35,6 +35,12 @@ pub mod __default_lib_allocator { System.alloc(layout) as *mut u8 } + #[no_mangle] + #[rustc_std_internal_symbol] + pub unsafe extern fn __rdl_oom() -> ! { + System.oom() + } + #[no_mangle] #[rustc_std_internal_symbol] pub unsafe extern fn __rdl_dealloc(ptr: *mut u8, diff --git a/src/test/compile-fail/allocator/not-an-allocator.rs b/src/test/compile-fail/allocator/not-an-allocator.rs index 140cad22f34..1479d0b6264 100644 --- a/src/test/compile-fail/allocator/not-an-allocator.rs +++ b/src/test/compile-fail/allocator/not-an-allocator.rs @@ -16,5 +16,6 @@ static A: usize = 0; //~| the trait bound `usize: //~| the trait bound `usize: //~| the trait bound `usize: +//~| the trait bound `usize: fn main() {} -- cgit 1.4.1-3-g733a5 From fd242ee64c5488e64e2bb677d90f2460e017b7cb Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Wed, 4 Apr 2018 19:15:22 +0200 Subject: impl GlobalAlloc for Global --- src/liballoc/alloc.rs | 85 ++++++++++++++++++++++++++++++--------------------- 1 file changed, 50 insertions(+), 35 deletions(-) (limited to 'src/liballoc/alloc.rs') diff --git a/src/liballoc/alloc.rs b/src/liballoc/alloc.rs index beae52726a6..063f0543ec4 100644 --- a/src/liballoc/alloc.rs +++ b/src/liballoc/alloc.rs @@ -73,62 +73,42 @@ pub type Heap = Global; #[allow(non_upper_case_globals)] pub const Heap: Global = Global; -unsafe impl Alloc for Global { +unsafe impl GlobalAlloc for Global { #[inline] - unsafe fn alloc(&mut self, layout: Layout) -> Result<*mut u8, AllocErr> { + unsafe fn alloc(&self, layout: Layout) -> *mut Void { #[cfg(not(stage0))] let ptr = __rust_alloc(layout.size(), layout.align()); #[cfg(stage0)] let ptr = __rust_alloc(layout.size(), layout.align(), &mut 0); - - if !ptr.is_null() { - Ok(ptr) - } else { - Err(AllocErr) - } + ptr as *mut Void } #[inline] - unsafe fn dealloc(&mut self, ptr: *mut u8, layout: Layout) { - __rust_dealloc(ptr, layout.size(), layout.align()) + unsafe fn dealloc(&self, ptr: *mut Void, layout: Layout) { + __rust_dealloc(ptr as *mut u8, layout.size(), layout.align()) } #[inline] - unsafe fn realloc(&mut self, - ptr: *mut u8, - layout: Layout, - new_size: usize) - -> Result<*mut u8, AllocErr> - { + unsafe fn realloc(&self, ptr: *mut Void, layout: Layout, new_size: usize) -> *mut Void { #[cfg(not(stage0))] - let ptr = __rust_realloc(ptr, layout.size(), layout.align(), new_size); + let ptr = __rust_realloc(ptr as *mut u8, layout.size(), layout.align(), new_size); #[cfg(stage0)] - let ptr = __rust_realloc(ptr, layout.size(), layout.align(), + let ptr = __rust_realloc(ptr as *mut u8, layout.size(), layout.align(), new_size, layout.align(), &mut 0); - - if !ptr.is_null() { - Ok(ptr) - } else { - Err(AllocErr) - } + ptr as *mut Void } #[inline] - unsafe fn alloc_zeroed(&mut self, layout: Layout) -> Result<*mut u8, AllocErr> { + unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut Void { #[cfg(not(stage0))] let ptr = __rust_alloc_zeroed(layout.size(), layout.align()); #[cfg(stage0)] let ptr = __rust_alloc_zeroed(layout.size(), layout.align(), &mut 0); - - if !ptr.is_null() { - Ok(ptr) - } else { - Err(AllocErr) - } + ptr as *mut Void } #[inline] - fn oom(&mut self) -> ! { + fn oom(&self) -> ! { unsafe { #[cfg(not(stage0))] __rust_oom(); @@ -138,6 +118,38 @@ unsafe impl Alloc for Global { } } +unsafe impl Alloc for Global { + #[inline] + unsafe fn alloc(&mut self, layout: Layout) -> Result<*mut u8, AllocErr> { + GlobalAlloc::alloc(self, layout).into() + } + + #[inline] + unsafe fn dealloc(&mut self, ptr: *mut u8, layout: Layout) { + GlobalAlloc::dealloc(self, ptr as *mut Void, layout) + } + + #[inline] + unsafe fn realloc(&mut self, + ptr: *mut u8, + layout: Layout, + new_size: usize) + -> Result<*mut u8, AllocErr> + { + GlobalAlloc::realloc(self, ptr as *mut Void, layout, new_size).into() + } + + #[inline] + unsafe fn alloc_zeroed(&mut self, layout: Layout) -> Result<*mut u8, AllocErr> { + GlobalAlloc::alloc_zeroed(self, layout).into() + } + + #[inline] + fn oom(&mut self) -> ! { + GlobalAlloc::oom(self) + } +} + /// The allocator for unique pointers. // This function must not unwind. If it does, MIR trans will fail. #[cfg(not(test))] @@ -148,9 +160,12 @@ unsafe fn exchange_malloc(size: usize, align: usize) -> *mut u8 { align as *mut u8 } else { let layout = Layout::from_size_align_unchecked(size, align); - Global.alloc(layout).unwrap_or_else(|_| { + let ptr = Global.alloc(layout); + if !ptr.is_null() { + ptr as *mut u8 + } else { Global.oom() - }) + } } } @@ -162,7 +177,7 @@ pub(crate) unsafe fn box_free(ptr: *mut T) { // We do not allocate for Box when T is ZST, so deallocation is also not necessary. if size != 0 { let layout = Layout::from_size_align_unchecked(size, align); - Global.dealloc(ptr as *mut u8, layout); + Global.dealloc(ptr as *mut Void, layout); } } -- cgit 1.4.1-3-g733a5 From fddf51ee0b9765484fc316dbf3d4feb8ceea715d Mon Sep 17 00:00:00 2001 From: Mike Hommey Date: Tue, 3 Apr 2018 08:51:02 +0900 Subject: Use NonNull instead of *mut u8 in the Alloc trait Fixes #49608 --- src/doc/nomicon | 2 +- .../src/language-features/global-allocator.md | 1 + src/liballoc/alloc.rs | 19 +++---- src/liballoc/arc.rs | 16 +++--- src/liballoc/btree/node.rs | 16 +++--- src/liballoc/heap.rs | 22 ++++++-- src/liballoc/lib.rs | 1 + src/liballoc/raw_vec.rs | 40 +++++++-------- src/liballoc/rc.rs | 18 +++---- src/liballoc/tests/heap.rs | 3 +- src/liballoc_system/lib.rs | 29 +++++------ src/libcore/alloc.rs | 58 ++++++++++------------ src/libcore/ptr.rs | 8 +++ src/libstd/collections/hash/table.rs | 6 +-- src/libstd/lib.rs | 1 + src/test/run-pass/allocator/xcrate-use2.rs | 2 +- src/test/run-pass/realloc-16687.rs | 18 +++---- src/test/run-pass/regions-mock-trans.rs | 5 +- 18 files changed, 136 insertions(+), 129 deletions(-) (limited to 'src/liballoc/alloc.rs') diff --git a/src/doc/nomicon b/src/doc/nomicon index 6a8f0a27e9a..498ac299742 160000 --- a/src/doc/nomicon +++ b/src/doc/nomicon @@ -1 +1 @@ -Subproject commit 6a8f0a27e9a58c55c89d07bc43a176fdae5e051c +Subproject commit 498ac2997420f7b25f7cd0a3f8202950d8ad93ec diff --git a/src/doc/unstable-book/src/language-features/global-allocator.md b/src/doc/unstable-book/src/language-features/global-allocator.md index 6ce12ba684d..a3f3ee65bf0 100644 --- a/src/doc/unstable-book/src/language-features/global-allocator.md +++ b/src/doc/unstable-book/src/language-features/global-allocator.md @@ -30,6 +30,7 @@ looks like: #![feature(global_allocator, allocator_api, heap_api)] use std::alloc::{GlobalAlloc, System, Layout, Void}; +use std::ptr::NonNull; struct MyAllocator; diff --git a/src/liballoc/alloc.rs b/src/liballoc/alloc.rs index 063f0543ec4..af48aa7961e 100644 --- a/src/liballoc/alloc.rs +++ b/src/liballoc/alloc.rs @@ -16,6 +16,7 @@ issue = "32838")] use core::intrinsics::{min_align_of_val, size_of_val}; +use core::ptr::NonNull; use core::usize; #[doc(inline)] @@ -120,27 +121,27 @@ unsafe impl GlobalAlloc for Global { unsafe impl Alloc for Global { #[inline] - unsafe fn alloc(&mut self, layout: Layout) -> Result<*mut u8, AllocErr> { + unsafe fn alloc(&mut self, layout: Layout) -> Result, AllocErr> { GlobalAlloc::alloc(self, layout).into() } #[inline] - unsafe fn dealloc(&mut self, ptr: *mut u8, layout: Layout) { - GlobalAlloc::dealloc(self, ptr as *mut Void, layout) + unsafe fn dealloc(&mut self, ptr: NonNull, layout: Layout) { + GlobalAlloc::dealloc(self, ptr.as_ptr(), layout) } #[inline] unsafe fn realloc(&mut self, - ptr: *mut u8, + ptr: NonNull, layout: Layout, new_size: usize) - -> Result<*mut u8, AllocErr> + -> Result, AllocErr> { - GlobalAlloc::realloc(self, ptr as *mut Void, layout, new_size).into() + GlobalAlloc::realloc(self, ptr.as_ptr(), layout, new_size).into() } #[inline] - unsafe fn alloc_zeroed(&mut self, layout: Layout) -> Result<*mut u8, AllocErr> { + unsafe fn alloc_zeroed(&mut self, layout: Layout) -> Result, AllocErr> { GlobalAlloc::alloc_zeroed(self, layout).into() } @@ -195,8 +196,8 @@ mod tests { let ptr = Global.alloc_zeroed(layout.clone()) .unwrap_or_else(|_| Global.oom()); - let end = ptr.offset(layout.size() as isize); - let mut i = ptr; + let mut i = ptr.cast::().as_ptr(); + let end = i.offset(layout.size() as isize); while i < end { assert_eq!(*i, 0); i = i.offset(1); diff --git a/src/liballoc/arc.rs b/src/liballoc/arc.rs index f0a325530ba..88754ace3ce 100644 --- a/src/liballoc/arc.rs +++ b/src/liballoc/arc.rs @@ -512,15 +512,13 @@ impl Arc { // Non-inlined part of `drop`. #[inline(never)] unsafe fn drop_slow(&mut self) { - let ptr = self.ptr.as_ptr(); - // Destroy the data at this time, even though we may not free the box // allocation itself (there may still be weak pointers lying around). ptr::drop_in_place(&mut self.ptr.as_mut().data); if self.inner().weak.fetch_sub(1, Release) == 1 { atomic::fence(Acquire); - Global.dealloc(ptr as *mut u8, Layout::for_value(&*ptr)) + Global.dealloc(self.ptr.as_void(), Layout::for_value(self.ptr.as_ref())) } } @@ -558,7 +556,7 @@ impl Arc { .unwrap_or_else(|_| Global.oom()); // Initialize the real ArcInner - let inner = set_data_ptr(ptr as *mut T, mem) as *mut ArcInner; + let inner = set_data_ptr(ptr as *mut T, mem.as_ptr() as *mut u8) as *mut ArcInner; ptr::write(&mut (*inner).strong, atomic::AtomicUsize::new(1)); ptr::write(&mut (*inner).weak, atomic::AtomicUsize::new(1)); @@ -625,7 +623,7 @@ impl ArcFromSlice for Arc<[T]> { // In the event of a panic, elements that have been written // into the new ArcInner will be dropped, then the memory freed. struct Guard { - mem: *mut u8, + mem: NonNull, elems: *mut T, layout: Layout, n_elems: usize, @@ -639,7 +637,7 @@ impl ArcFromSlice for Arc<[T]> { let slice = from_raw_parts_mut(self.elems, self.n_elems); ptr::drop_in_place(slice); - Global.dealloc(self.mem, self.layout.clone()); + Global.dealloc(self.mem.as_void(), self.layout.clone()); } } } @@ -655,7 +653,7 @@ impl ArcFromSlice for Arc<[T]> { let elems = &mut (*ptr).data as *mut [T] as *mut T; let mut guard = Guard{ - mem: mem, + mem: NonNull::new_unchecked(mem), elems: elems, layout: layout, n_elems: 0, @@ -1147,8 +1145,6 @@ impl Drop for Weak { /// assert!(other_weak_foo.upgrade().is_none()); /// ``` fn drop(&mut self) { - let ptr = self.ptr.as_ptr(); - // If we find out that we were the last weak pointer, then its time to // deallocate the data entirely. See the discussion in Arc::drop() about // the memory orderings @@ -1160,7 +1156,7 @@ impl Drop for Weak { if self.inner().weak.fetch_sub(1, Release) == 1 { atomic::fence(Acquire); unsafe { - Global.dealloc(ptr as *mut u8, Layout::for_value(&*ptr)) + Global.dealloc(self.ptr.as_void(), Layout::for_value(self.ptr.as_ref())) } } } diff --git a/src/liballoc/btree/node.rs b/src/liballoc/btree/node.rs index 8e23228bd28..64aa40ac166 100644 --- a/src/liballoc/btree/node.rs +++ b/src/liballoc/btree/node.rs @@ -236,7 +236,7 @@ impl Root { pub fn pop_level(&mut self) { debug_assert!(self.height > 0); - let top = self.node.ptr.as_ptr() as *mut u8; + let top = self.node.ptr; self.node = unsafe { BoxedNode::from_ptr(self.as_mut() @@ -249,7 +249,7 @@ impl Root { self.as_mut().as_leaf_mut().parent = ptr::null(); unsafe { - Global.dealloc(top, Layout::new::>()); + Global.dealloc(NonNull::from(top).as_void(), Layout::new::>()); } } } @@ -433,9 +433,9 @@ impl NodeRef { marker::Edge > > { - let ptr = self.as_leaf() as *const LeafNode as *const u8 as *mut u8; + let node = self.node; let ret = self.ascend().ok(); - Global.dealloc(ptr, Layout::new::>()); + Global.dealloc(node.as_void(), Layout::new::>()); ret } } @@ -454,9 +454,9 @@ impl NodeRef { marker::Edge > > { - let ptr = self.as_internal() as *const InternalNode as *const u8 as *mut u8; + let node = self.node; let ret = self.ascend().ok(); - Global.dealloc(ptr, Layout::new::>()); + Global.dealloc(node.as_void(), Layout::new::>()); ret } } @@ -1239,12 +1239,12 @@ impl<'a, K, V> Handle, K, V, marker::Internal>, marker:: } Global.dealloc( - right_node.node.as_ptr() as *mut u8, + right_node.node.as_void(), Layout::new::>(), ); } else { Global.dealloc( - right_node.node.as_ptr() as *mut u8, + right_node.node.as_void(), Layout::new::>(), ); } diff --git a/src/liballoc/heap.rs b/src/liballoc/heap.rs index e79383331e1..cfb6504e743 100644 --- a/src/liballoc/heap.rs +++ b/src/liballoc/heap.rs @@ -8,14 +8,20 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -pub use alloc::{Excess, Layout, AllocErr, CannotReallocInPlace}; +#![allow(deprecated)] + +pub use alloc::{Layout, AllocErr, CannotReallocInPlace, Void}; use core::alloc::Alloc as CoreAlloc; +use core::ptr::NonNull; #[doc(hidden)] pub mod __core { pub use core::*; } +#[derive(Debug)] +pub struct Excess(pub *mut u8, pub usize); + /// Compatibility with older versions of #[global_allocator] during bootstrap pub unsafe trait Alloc { unsafe fn alloc(&mut self, layout: Layout) -> Result<*mut u8, AllocErr>; @@ -42,13 +48,13 @@ pub unsafe trait Alloc { new_layout: Layout) -> Result<(), CannotReallocInPlace>; } -#[allow(deprecated)] unsafe impl Alloc for T where T: CoreAlloc { unsafe fn alloc(&mut self, layout: Layout) -> Result<*mut u8, AllocErr> { - CoreAlloc::alloc(self, layout) + CoreAlloc::alloc(self, layout).map(|ptr| ptr.cast().as_ptr()) } unsafe fn dealloc(&mut self, ptr: *mut u8, layout: Layout) { + let ptr = NonNull::new_unchecked(ptr as *mut Void); CoreAlloc::dealloc(self, ptr, layout) } @@ -64,28 +70,33 @@ unsafe impl Alloc for T where T: CoreAlloc { ptr: *mut u8, layout: Layout, new_layout: Layout) -> Result<*mut u8, AllocErr> { - CoreAlloc::realloc(self, ptr, layout, new_layout.size()) + let ptr = NonNull::new_unchecked(ptr as *mut Void); + CoreAlloc::realloc(self, ptr, layout, new_layout.size()).map(|ptr| ptr.cast().as_ptr()) } unsafe fn alloc_zeroed(&mut self, layout: Layout) -> Result<*mut u8, AllocErr> { - CoreAlloc::alloc_zeroed(self, layout) + CoreAlloc::alloc_zeroed(self, layout).map(|ptr| ptr.cast().as_ptr()) } unsafe fn alloc_excess(&mut self, layout: Layout) -> Result { CoreAlloc::alloc_excess(self, layout) + .map(|e| Excess(e.0 .cast().as_ptr(), e.1)) } unsafe fn realloc_excess(&mut self, ptr: *mut u8, layout: Layout, new_layout: Layout) -> Result { + let ptr = NonNull::new_unchecked(ptr as *mut Void); CoreAlloc::realloc_excess(self, ptr, layout, new_layout.size()) + .map(|e| Excess(e.0 .cast().as_ptr(), e.1)) } unsafe fn grow_in_place(&mut self, ptr: *mut u8, layout: Layout, new_layout: Layout) -> Result<(), CannotReallocInPlace> { + let ptr = NonNull::new_unchecked(ptr as *mut Void); CoreAlloc::grow_in_place(self, ptr, layout, new_layout.size()) } @@ -93,6 +104,7 @@ unsafe impl Alloc for T where T: CoreAlloc { ptr: *mut u8, layout: Layout, new_layout: Layout) -> Result<(), CannotReallocInPlace> { + let ptr = NonNull::new_unchecked(ptr as *mut Void); CoreAlloc::shrink_in_place(self, ptr, layout, new_layout.size()) } } diff --git a/src/liballoc/lib.rs b/src/liballoc/lib.rs index a10820ebefd..3a106a2ff5c 100644 --- a/src/liballoc/lib.rs +++ b/src/liballoc/lib.rs @@ -99,6 +99,7 @@ #![feature(lang_items)] #![feature(libc)] #![feature(needs_allocator)] +#![feature(nonnull_cast)] #![feature(nonzero)] #![feature(optin_builtin_traits)] #![feature(pattern)] diff --git a/src/liballoc/raw_vec.rs b/src/liballoc/raw_vec.rs index 80b816878fb..d72301f5ad6 100644 --- a/src/liballoc/raw_vec.rs +++ b/src/liballoc/raw_vec.rs @@ -12,7 +12,7 @@ use alloc::{Alloc, Layout, Global}; use core::cmp; use core::mem; use core::ops::Drop; -use core::ptr::{self, Unique}; +use core::ptr::{self, NonNull, Unique}; use core::slice; use super::boxed::Box; use super::allocator::CollectionAllocErr; @@ -90,7 +90,7 @@ impl RawVec { // handles ZSTs and `cap = 0` alike let ptr = if alloc_size == 0 { - mem::align_of::() as *mut u8 + NonNull::::dangling().as_void() } else { let align = mem::align_of::(); let result = if zeroed { @@ -105,7 +105,7 @@ impl RawVec { }; RawVec { - ptr: Unique::new_unchecked(ptr as *mut _), + ptr: ptr.cast().into(), cap, a, } @@ -310,11 +310,11 @@ impl RawVec { let new_cap = 2 * self.cap; let new_size = new_cap * elem_size; alloc_guard(new_size).expect("capacity overflow"); - let ptr_res = self.a.realloc(self.ptr.as_ptr() as *mut u8, + let ptr_res = self.a.realloc(NonNull::from(self.ptr).as_void(), cur, new_size); match ptr_res { - Ok(ptr) => (new_cap, Unique::new_unchecked(ptr as *mut T)), + Ok(ptr) => (new_cap, ptr.cast().into()), Err(_) => self.a.oom(), } } @@ -369,8 +369,7 @@ impl RawVec { let new_cap = 2 * self.cap; let new_size = new_cap * elem_size; alloc_guard(new_size).expect("capacity overflow"); - let ptr = self.ptr() as *mut _; - match self.a.grow_in_place(ptr, old_layout, new_size) { + match self.a.grow_in_place(NonNull::from(self.ptr).as_void(), old_layout, new_size) { Ok(_) => { // We can't directly divide `size`. self.cap = new_cap; @@ -427,13 +426,12 @@ impl RawVec { let res = match self.current_layout() { Some(layout) => { debug_assert!(new_layout.align() == layout.align()); - let old_ptr = self.ptr.as_ptr() as *mut u8; - self.a.realloc(old_ptr, layout, new_layout.size()) + self.a.realloc(NonNull::from(self.ptr).as_void(), layout, new_layout.size()) } None => self.a.alloc(new_layout), }; - self.ptr = Unique::new_unchecked(res? as *mut T); + self.ptr = res?.cast().into(); self.cap = new_cap; Ok(()) @@ -537,13 +535,12 @@ impl RawVec { let res = match self.current_layout() { Some(layout) => { debug_assert!(new_layout.align() == layout.align()); - let old_ptr = self.ptr.as_ptr() as *mut u8; - self.a.realloc(old_ptr, layout, new_layout.size()) + self.a.realloc(NonNull::from(self.ptr).as_void(), layout, new_layout.size()) } None => self.a.alloc(new_layout), }; - self.ptr = Unique::new_unchecked(res? as *mut T); + self.ptr = res?.cast().into(); self.cap = new_cap; Ok(()) @@ -600,11 +597,12 @@ impl RawVec { // (regardless of whether `self.cap - used_cap` wrapped). // Therefore we can safely call grow_in_place. - let ptr = self.ptr() as *mut _; let new_layout = Layout::new::().repeat(new_cap).unwrap().0; // FIXME: may crash and burn on over-reserve alloc_guard(new_layout.size()).expect("capacity overflow"); - match self.a.grow_in_place(ptr, old_layout, new_layout.size()) { + match self.a.grow_in_place( + NonNull::from(self.ptr).as_void(), old_layout, new_layout.size(), + ) { Ok(_) => { self.cap = new_cap; true @@ -664,10 +662,10 @@ impl RawVec { let new_size = elem_size * amount; let align = mem::align_of::(); let old_layout = Layout::from_size_align_unchecked(old_size, align); - match self.a.realloc(self.ptr.as_ptr() as *mut u8, + match self.a.realloc(NonNull::from(self.ptr).as_void(), old_layout, new_size) { - Ok(p) => self.ptr = Unique::new_unchecked(p as *mut T), + Ok(p) => self.ptr = p.cast().into(), Err(_) => self.a.oom(), } } @@ -700,8 +698,7 @@ impl RawVec { let elem_size = mem::size_of::(); if elem_size != 0 { if let Some(layout) = self.current_layout() { - let ptr = self.ptr() as *mut u8; - self.a.dealloc(ptr, layout); + self.a.dealloc(NonNull::from(self.ptr).as_void(), layout); } } } @@ -737,6 +734,7 @@ fn alloc_guard(alloc_size: usize) -> Result<(), CollectionAllocErr> { #[cfg(test)] mod tests { use super::*; + use alloc::Void; #[test] fn allocator_param() { @@ -756,7 +754,7 @@ mod tests { // before allocation attempts start failing. struct BoundedAlloc { fuel: usize } unsafe impl Alloc for BoundedAlloc { - unsafe fn alloc(&mut self, layout: Layout) -> Result<*mut u8, AllocErr> { + unsafe fn alloc(&mut self, layout: Layout) -> Result, AllocErr> { let size = layout.size(); if size > self.fuel { return Err(AllocErr); @@ -766,7 +764,7 @@ mod tests { err @ Err(_) => err, } } - unsafe fn dealloc(&mut self, ptr: *mut u8, layout: Layout) { + unsafe fn dealloc(&mut self, ptr: NonNull, layout: Layout) { Global.dealloc(ptr, layout) } } diff --git a/src/liballoc/rc.rs b/src/liballoc/rc.rs index 3c0b11bfe74..1c835fe50de 100644 --- a/src/liballoc/rc.rs +++ b/src/liballoc/rc.rs @@ -259,7 +259,7 @@ use core::ops::CoerceUnsized; use core::ptr::{self, NonNull}; use core::convert::From; -use alloc::{Global, Alloc, Layout, box_free}; +use alloc::{Global, Alloc, Layout, Void, box_free}; use string::String; use vec::Vec; @@ -671,7 +671,7 @@ impl Rc { .unwrap_or_else(|_| Global.oom()); // Initialize the real RcBox - let inner = set_data_ptr(ptr as *mut T, mem) as *mut RcBox; + let inner = set_data_ptr(ptr as *mut T, mem.as_ptr() as *mut u8) as *mut RcBox; ptr::write(&mut (*inner).strong, Cell::new(1)); ptr::write(&mut (*inner).weak, Cell::new(1)); @@ -737,7 +737,7 @@ impl RcFromSlice for Rc<[T]> { // In the event of a panic, elements that have been written // into the new RcBox will be dropped, then the memory freed. struct Guard { - mem: *mut u8, + mem: NonNull, elems: *mut T, layout: Layout, n_elems: usize, @@ -760,14 +760,14 @@ impl RcFromSlice for Rc<[T]> { let v_ptr = v as *const [T]; let ptr = Self::allocate_for_ptr(v_ptr); - let mem = ptr as *mut _ as *mut u8; + let mem = ptr as *mut _ as *mut Void; let layout = Layout::for_value(&*ptr); // Pointer to first element let elems = &mut (*ptr).value as *mut [T] as *mut T; let mut guard = Guard{ - mem: mem, + mem: NonNull::new_unchecked(mem), elems: elems, layout: layout, n_elems: 0, @@ -834,8 +834,6 @@ unsafe impl<#[may_dangle] T: ?Sized> Drop for Rc { /// ``` fn drop(&mut self) { unsafe { - let ptr = self.ptr.as_ptr(); - self.dec_strong(); if self.strong() == 0 { // destroy the contained object @@ -846,7 +844,7 @@ unsafe impl<#[may_dangle] T: ?Sized> Drop for Rc { self.dec_weak(); if self.weak() == 0 { - Global.dealloc(ptr as *mut u8, Layout::for_value(&*ptr)); + Global.dealloc(self.ptr.as_void(), Layout::for_value(self.ptr.as_ref())); } } } @@ -1266,13 +1264,11 @@ impl Drop for Weak { /// ``` fn drop(&mut self) { unsafe { - let ptr = self.ptr.as_ptr(); - self.dec_weak(); // the weak count starts at 1, and will only go to zero if all // the strong pointers have disappeared. if self.weak() == 0 { - Global.dealloc(ptr as *mut u8, Layout::for_value(&*ptr)); + Global.dealloc(self.ptr.as_void(), Layout::for_value(self.ptr.as_ref())); } } } diff --git a/src/liballoc/tests/heap.rs b/src/liballoc/tests/heap.rs index 328131e2fef..6fa88ce969a 100644 --- a/src/liballoc/tests/heap.rs +++ b/src/liballoc/tests/heap.rs @@ -34,7 +34,8 @@ fn check_overalign_requests(mut allocator: T) { allocator.alloc(Layout::from_size_align(size, align).unwrap()).unwrap() }).collect(); for &ptr in &pointers { - assert_eq!((ptr as usize) % align, 0, "Got a pointer less aligned than requested") + assert_eq!((ptr.as_ptr() as usize) % align, 0, + "Got a pointer less aligned than requested") } // Clean up diff --git a/src/liballoc_system/lib.rs b/src/liballoc_system/lib.rs index c6507282b24..bf27e972177 100644 --- a/src/liballoc_system/lib.rs +++ b/src/liballoc_system/lib.rs @@ -42,6 +42,7 @@ const MIN_ALIGN: usize = 8; const MIN_ALIGN: usize = 16; use core::alloc::{Alloc, GlobalAlloc, AllocErr, Layout, Void}; +use core::ptr::NonNull; #[unstable(feature = "allocator_api", issue = "32838")] pub struct System; @@ -49,26 +50,26 @@ pub struct System; #[unstable(feature = "allocator_api", issue = "32838")] unsafe impl Alloc for System { #[inline] - unsafe fn alloc(&mut self, layout: Layout) -> Result<*mut u8, AllocErr> { + unsafe fn alloc(&mut self, layout: Layout) -> Result, AllocErr> { GlobalAlloc::alloc(self, layout).into() } #[inline] - unsafe fn alloc_zeroed(&mut self, layout: Layout) -> Result<*mut u8, AllocErr> { + unsafe fn alloc_zeroed(&mut self, layout: Layout) -> Result, AllocErr> { GlobalAlloc::alloc_zeroed(self, layout).into() } #[inline] - unsafe fn dealloc(&mut self, ptr: *mut u8, layout: Layout) { - GlobalAlloc::dealloc(self, ptr as *mut Void, layout) + unsafe fn dealloc(&mut self, ptr: NonNull, layout: Layout) { + GlobalAlloc::dealloc(self, ptr.as_ptr(), layout) } #[inline] unsafe fn realloc(&mut self, - ptr: *mut u8, + ptr: NonNull, old_layout: Layout, - new_size: usize) -> Result<*mut u8, AllocErr> { - GlobalAlloc::realloc(self, ptr as *mut Void, old_layout, new_size).into() + new_size: usize) -> Result, AllocErr> { + GlobalAlloc::realloc(self, ptr.as_ptr(), old_layout, new_size).into() } #[inline] @@ -81,26 +82,26 @@ unsafe impl Alloc for System { #[unstable(feature = "allocator_api", issue = "32838")] unsafe impl<'a> Alloc for &'a System { #[inline] - unsafe fn alloc(&mut self, layout: Layout) -> Result<*mut u8, AllocErr> { + unsafe fn alloc(&mut self, layout: Layout) -> Result, AllocErr> { GlobalAlloc::alloc(*self, layout).into() } #[inline] - unsafe fn alloc_zeroed(&mut self, layout: Layout) -> Result<*mut u8, AllocErr> { + unsafe fn alloc_zeroed(&mut self, layout: Layout) -> Result, AllocErr> { GlobalAlloc::alloc_zeroed(*self, layout).into() } #[inline] - unsafe fn dealloc(&mut self, ptr: *mut u8, layout: Layout) { - GlobalAlloc::dealloc(*self, ptr as *mut Void, layout) + unsafe fn dealloc(&mut self, ptr: NonNull, layout: Layout) { + GlobalAlloc::dealloc(*self, ptr.as_ptr(), layout) } #[inline] unsafe fn realloc(&mut self, - ptr: *mut u8, + ptr: NonNull, old_layout: Layout, - new_size: usize) -> Result<*mut u8, AllocErr> { - GlobalAlloc::realloc(*self, ptr as *mut Void, old_layout, new_size).into() + new_size: usize) -> Result, AllocErr> { + GlobalAlloc::realloc(*self, ptr.as_ptr(), old_layout, new_size).into() } #[inline] diff --git a/src/libcore/alloc.rs b/src/libcore/alloc.rs index 7334f986f2b..632eed96049 100644 --- a/src/libcore/alloc.rs +++ b/src/libcore/alloc.rs @@ -42,21 +42,17 @@ impl Void { } /// Convert from a return value of GlobalAlloc::alloc to that of Alloc::alloc -impl From<*mut Void> for Result<*mut u8, AllocErr> { +impl From<*mut Void> for Result, AllocErr> { fn from(ptr: *mut Void) -> Self { - if !ptr.is_null() { - Ok(ptr as *mut u8) - } else { - Err(AllocErr) - } + NonNull::new(ptr).ok_or(AllocErr) } } /// Convert from a return value of Alloc::alloc to that of GlobalAlloc::alloc -impl From> for *mut Void { - fn from(result: Result<*mut u8, AllocErr>) -> Self { +impl From, AllocErr>> for *mut Void { + fn from(result: Result, AllocErr>) -> Self { match result { - Ok(ptr) => ptr as *mut Void, + Ok(ptr) => ptr.as_ptr(), Err(_) => Void::null_mut(), } } @@ -65,7 +61,7 @@ impl From> for *mut Void { /// Represents the combination of a starting address and /// a total capacity of the returned block. #[derive(Debug)] -pub struct Excess(pub *mut u8, pub usize); +pub struct Excess(pub NonNull, pub usize); fn size_align() -> (usize, usize) { (mem::size_of::(), mem::align_of::()) @@ -575,7 +571,7 @@ pub unsafe trait Alloc { /// Clients wishing to abort computation in response to an /// allocation error are encouraged to call the allocator's `oom` /// method, rather than directly invoking `panic!` or similar. - unsafe fn alloc(&mut self, layout: Layout) -> Result<*mut u8, AllocErr>; + unsafe fn alloc(&mut self, layout: Layout) -> Result, AllocErr>; /// Deallocate the memory referenced by `ptr`. /// @@ -592,7 +588,7 @@ pub unsafe trait Alloc { /// * In addition to fitting the block of memory `layout`, the /// alignment of the `layout` must match the alignment used /// to allocate that block of memory. - unsafe fn dealloc(&mut self, ptr: *mut u8, layout: Layout); + unsafe fn dealloc(&mut self, ptr: NonNull, layout: Layout); /// Allocator-specific method for signaling an out-of-memory /// condition. @@ -710,9 +706,9 @@ pub unsafe trait Alloc { /// reallocation error are encouraged to call the allocator's `oom` /// method, rather than directly invoking `panic!` or similar. unsafe fn realloc(&mut self, - ptr: *mut u8, + ptr: NonNull, layout: Layout, - new_size: usize) -> Result<*mut u8, AllocErr> { + new_size: usize) -> Result, AllocErr> { let old_size = layout.size(); if new_size >= old_size { @@ -729,7 +725,9 @@ pub unsafe trait Alloc { let new_layout = Layout::from_size_align_unchecked(new_size, layout.align()); let result = self.alloc(new_layout); if let Ok(new_ptr) = result { - ptr::copy_nonoverlapping(ptr as *const u8, new_ptr, cmp::min(old_size, new_size)); + ptr::copy_nonoverlapping(ptr.as_ptr() as *const u8, + new_ptr.as_ptr() as *mut u8, + cmp::min(old_size, new_size)); self.dealloc(ptr, layout); } result @@ -751,11 +749,11 @@ pub unsafe trait Alloc { /// Clients wishing to abort computation in response to an /// allocation error are encouraged to call the allocator's `oom` /// method, rather than directly invoking `panic!` or similar. - unsafe fn alloc_zeroed(&mut self, layout: Layout) -> Result<*mut u8, AllocErr> { + unsafe fn alloc_zeroed(&mut self, layout: Layout) -> Result, AllocErr> { let size = layout.size(); let p = self.alloc(layout); if let Ok(p) = p { - ptr::write_bytes(p, 0, size); + ptr::write_bytes(p.as_ptr() as *mut u8, 0, size); } p } @@ -800,7 +798,7 @@ pub unsafe trait Alloc { /// reallocation error are encouraged to call the allocator's `oom` /// method, rather than directly invoking `panic!` or similar. unsafe fn realloc_excess(&mut self, - ptr: *mut u8, + ptr: NonNull, layout: Layout, new_size: usize) -> Result { let new_layout = Layout::from_size_align_unchecked(new_size, layout.align()); @@ -845,7 +843,7 @@ pub unsafe trait Alloc { /// `grow_in_place` failures without aborting, or to fall back on /// another reallocation method before resorting to an abort. unsafe fn grow_in_place(&mut self, - ptr: *mut u8, + ptr: NonNull, layout: Layout, new_size: usize) -> Result<(), CannotReallocInPlace> { let _ = ptr; // this default implementation doesn't care about the actual address. @@ -900,7 +898,7 @@ pub unsafe trait Alloc { /// `shrink_in_place` failures without aborting, or to fall back /// on another reallocation method before resorting to an abort. unsafe fn shrink_in_place(&mut self, - ptr: *mut u8, + ptr: NonNull, layout: Layout, new_size: usize) -> Result<(), CannotReallocInPlace> { let _ = ptr; // this default implementation doesn't care about the actual address. @@ -951,7 +949,7 @@ pub unsafe trait Alloc { { let k = Layout::new::(); if k.size() > 0 { - unsafe { self.alloc(k).map(|p| NonNull::new_unchecked(p as *mut T)) } + unsafe { self.alloc(k).map(|p| p.cast()) } } else { Err(AllocErr) } @@ -977,10 +975,9 @@ pub unsafe trait Alloc { unsafe fn dealloc_one(&mut self, ptr: NonNull) where Self: Sized { - let raw_ptr = ptr.as_ptr() as *mut u8; let k = Layout::new::(); if k.size() > 0 { - self.dealloc(raw_ptr, k); + self.dealloc(ptr.as_void(), k); } } @@ -1020,10 +1017,7 @@ pub unsafe trait Alloc { match Layout::array::(n) { Ok(ref layout) if layout.size() > 0 => { unsafe { - self.alloc(layout.clone()) - .map(|p| { - NonNull::new_unchecked(p as *mut T) - }) + self.alloc(layout.clone()).map(|p| p.cast()) } } _ => Err(AllocErr), @@ -1068,11 +1062,10 @@ pub unsafe trait Alloc { n_new: usize) -> Result, AllocErr> where Self: Sized { - match (Layout::array::(n_old), Layout::array::(n_new), ptr.as_ptr()) { - (Ok(ref k_old), Ok(ref k_new), ptr) if k_old.size() > 0 && k_new.size() > 0 => { + match (Layout::array::(n_old), Layout::array::(n_new)) { + (Ok(ref k_old), Ok(ref k_new)) if k_old.size() > 0 && k_new.size() > 0 => { debug_assert!(k_old.align() == k_new.align()); - self.realloc(ptr as *mut u8, k_old.clone(), k_new.size()) - .map(|p| NonNull::new_unchecked(p as *mut T)) + self.realloc(ptr.as_void(), k_old.clone(), k_new.size()).map(NonNull::cast) } _ => { Err(AllocErr) @@ -1103,10 +1096,9 @@ pub unsafe trait Alloc { unsafe fn dealloc_array(&mut self, ptr: NonNull, n: usize) -> Result<(), AllocErr> where Self: Sized { - let raw_ptr = ptr.as_ptr() as *mut u8; match Layout::array::(n) { Ok(ref k) if k.size() > 0 => { - Ok(self.dealloc(raw_ptr, k.clone())) + Ok(self.dealloc(ptr.as_void(), k.clone())) } _ => { Err(AllocErr) diff --git a/src/libcore/ptr.rs b/src/libcore/ptr.rs index c1e150e9fb9..f4e668328ce 100644 --- a/src/libcore/ptr.rs +++ b/src/libcore/ptr.rs @@ -2750,6 +2750,14 @@ impl NonNull { NonNull::new_unchecked(self.as_ptr() as *mut U) } } + + /// Cast to a `Void` pointer + #[unstable(feature = "allocator_api", issue = "32838")] + pub fn as_void(self) -> NonNull<::alloc::Void> { + unsafe { + NonNull::new_unchecked(self.as_ptr() as _) + } + } } #[stable(feature = "nonnull", since = "1.25.0")] diff --git a/src/libstd/collections/hash/table.rs b/src/libstd/collections/hash/table.rs index 50263705143..38c99373788 100644 --- a/src/libstd/collections/hash/table.rs +++ b/src/libstd/collections/hash/table.rs @@ -757,12 +757,10 @@ impl RawTable { let buffer = Global.alloc(Layout::from_size_align(size, alignment) .map_err(|_| CollectionAllocErr::CapacityOverflow)?)?; - let hashes = buffer as *mut HashUint; - Ok(RawTable { capacity_mask: capacity.wrapping_sub(1), size: 0, - hashes: TaggedHashUintPtr::new(hashes), + hashes: TaggedHashUintPtr::new(buffer.cast().as_ptr()), marker: marker::PhantomData, }) } @@ -1185,7 +1183,7 @@ unsafe impl<#[may_dangle] K, #[may_dangle] V> Drop for RawTable { debug_assert!(!oflo, "should be impossible"); unsafe { - Global.dealloc(self.hashes.ptr() as *mut u8, + Global.dealloc(NonNull::new_unchecked(self.hashes.ptr()).as_void(), Layout::from_size_align(size, align).unwrap()); // Remember how everything was allocated out of one buffer // during initialization? We only need one call to free here. diff --git a/src/libstd/lib.rs b/src/libstd/lib.rs index 25ba75fd35e..a34fcb5a7f9 100644 --- a/src/libstd/lib.rs +++ b/src/libstd/lib.rs @@ -275,6 +275,7 @@ #![feature(macro_reexport)] #![feature(macro_vis_matcher)] #![feature(needs_panic_runtime)] +#![feature(nonnull_cast)] #![feature(exhaustive_patterns)] #![feature(nonzero)] #![feature(num_bits_bytes)] diff --git a/src/test/run-pass/allocator/xcrate-use2.rs b/src/test/run-pass/allocator/xcrate-use2.rs index 52eb963efdb..b8e844522dc 100644 --- a/src/test/run-pass/allocator/xcrate-use2.rs +++ b/src/test/run-pass/allocator/xcrate-use2.rs @@ -30,7 +30,7 @@ fn main() { let layout = Layout::from_size_align(4, 2).unwrap(); // Global allocator routes to the `custom_as_global` global - let ptr = Global.alloc(layout.clone()).unwrap(); + let ptr = Global.alloc(layout.clone()); helper::work_with(&ptr); assert_eq!(custom_as_global::get(), n + 1); Global.dealloc(ptr, layout.clone()); diff --git a/src/test/run-pass/realloc-16687.rs b/src/test/run-pass/realloc-16687.rs index a562165d21b..49ab0ee3310 100644 --- a/src/test/run-pass/realloc-16687.rs +++ b/src/test/run-pass/realloc-16687.rs @@ -13,10 +13,10 @@ // Ideally this would be revised to use no_std, but for now it serves // well enough to reproduce (and illustrate) the bug from #16687. -#![feature(heap_api, allocator_api)] +#![feature(heap_api, allocator_api, nonnull_cast)] -use std::heap::{Heap, Alloc, Layout}; -use std::ptr; +use std::alloc::{Global, Alloc, Layout}; +use std::ptr::{self, NonNull}; fn main() { unsafe { @@ -50,13 +50,13 @@ unsafe fn test_triangle() -> bool { println!("allocate({:?})", layout); } - let ret = Heap.alloc(layout.clone()).unwrap_or_else(|_| Heap.oom()); + let ret = Global.alloc(layout.clone()).unwrap_or_else(|_| Global.oom()); if PRINT { println!("allocate({:?}) = {:?}", layout, ret); } - ret + ret.cast().as_ptr() } unsafe fn deallocate(ptr: *mut u8, layout: Layout) { @@ -64,7 +64,7 @@ unsafe fn test_triangle() -> bool { println!("deallocate({:?}, {:?}", ptr, layout); } - Heap.dealloc(ptr, layout); + Global.dealloc(NonNull::new_unchecked(ptr).as_void(), layout); } unsafe fn reallocate(ptr: *mut u8, old: Layout, new: Layout) -> *mut u8 { @@ -72,14 +72,14 @@ unsafe fn test_triangle() -> bool { println!("reallocate({:?}, old={:?}, new={:?})", ptr, old, new); } - let ret = Heap.realloc(ptr, old.clone(), new.clone()) - .unwrap_or_else(|_| Heap.oom()); + let ret = Global.realloc(NonNull::new_unchecked(ptr).as_void(), old.clone(), new.size()) + .unwrap_or_else(|_| Global.oom()); if PRINT { println!("reallocate({:?}, old={:?}, new={:?}) = {:?}", ptr, old, new, ret); } - ret + ret.cast().as_ptr() } fn idx_to_size(i: usize) -> usize { (i+1) * 10 } diff --git a/src/test/run-pass/regions-mock-trans.rs b/src/test/run-pass/regions-mock-trans.rs index 7d34b8fd00f..3c37243c8b9 100644 --- a/src/test/run-pass/regions-mock-trans.rs +++ b/src/test/run-pass/regions-mock-trans.rs @@ -13,6 +13,7 @@ #![feature(allocator_api)] use std::heap::{Alloc, Heap, Layout}; +use std::ptr::NonNull; struct arena(()); @@ -33,7 +34,7 @@ fn alloc<'a>(_bcx : &'a arena) -> &'a Bcx<'a> { unsafe { let ptr = Heap.alloc(Layout::new::()) .unwrap_or_else(|_| Heap.oom()); - &*(ptr as *const _) + &*(ptr.as_ptr() as *const _) } } @@ -45,7 +46,7 @@ fn g(fcx : &Fcx) { let bcx = Bcx { fcx: fcx }; let bcx2 = h(&bcx); unsafe { - Heap.dealloc(bcx2 as *const _ as *mut _, Layout::new::()); + Heap.dealloc(NonNull::new_unchecked(bcx2 as *const _ as *mut _), Layout::new::()); } } -- cgit 1.4.1-3-g733a5 From ed297777599081d11c4a337cf19c9b1a1112136b Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Wed, 11 Apr 2018 16:28:37 +0200 Subject: Remove conversions for allocated pointers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit One was now unused, and `NonNull::new(…).ok_or(AllocErr)` feels short enough for the few cases that need the other conversion. --- src/liballoc/alloc.rs | 6 +++--- src/liballoc_system/lib.rs | 16 ++++++++-------- src/libcore/alloc.rs | 17 ----------------- 3 files changed, 11 insertions(+), 28 deletions(-) (limited to 'src/liballoc/alloc.rs') diff --git a/src/liballoc/alloc.rs b/src/liballoc/alloc.rs index af48aa7961e..031babe5f6d 100644 --- a/src/liballoc/alloc.rs +++ b/src/liballoc/alloc.rs @@ -122,7 +122,7 @@ unsafe impl GlobalAlloc for Global { unsafe impl Alloc for Global { #[inline] unsafe fn alloc(&mut self, layout: Layout) -> Result, AllocErr> { - GlobalAlloc::alloc(self, layout).into() + NonNull::new(GlobalAlloc::alloc(self, layout)).ok_or(AllocErr) } #[inline] @@ -137,12 +137,12 @@ unsafe impl Alloc for Global { new_size: usize) -> Result, AllocErr> { - GlobalAlloc::realloc(self, ptr.as_ptr(), layout, new_size).into() + NonNull::new(GlobalAlloc::realloc(self, ptr.as_ptr(), layout, new_size)).ok_or(AllocErr) } #[inline] unsafe fn alloc_zeroed(&mut self, layout: Layout) -> Result, AllocErr> { - GlobalAlloc::alloc_zeroed(self, layout).into() + NonNull::new(GlobalAlloc::alloc_zeroed(self, layout)).ok_or(AllocErr) } #[inline] diff --git a/src/liballoc_system/lib.rs b/src/liballoc_system/lib.rs index bf27e972177..7fea6061169 100644 --- a/src/liballoc_system/lib.rs +++ b/src/liballoc_system/lib.rs @@ -51,12 +51,12 @@ pub struct System; unsafe impl Alloc for System { #[inline] unsafe fn alloc(&mut self, layout: Layout) -> Result, AllocErr> { - GlobalAlloc::alloc(self, layout).into() + NonNull::new(GlobalAlloc::alloc(self, layout)).ok_or(AllocErr) } #[inline] unsafe fn alloc_zeroed(&mut self, layout: Layout) -> Result, AllocErr> { - GlobalAlloc::alloc_zeroed(self, layout).into() + NonNull::new(GlobalAlloc::alloc_zeroed(self, layout)).ok_or(AllocErr) } #[inline] @@ -67,9 +67,9 @@ unsafe impl Alloc for System { #[inline] unsafe fn realloc(&mut self, ptr: NonNull, - old_layout: Layout, + layout: Layout, new_size: usize) -> Result, AllocErr> { - GlobalAlloc::realloc(self, ptr.as_ptr(), old_layout, new_size).into() + NonNull::new(GlobalAlloc::realloc(self, ptr.as_ptr(), layout, new_size)).ok_or(AllocErr) } #[inline] @@ -83,12 +83,12 @@ unsafe impl Alloc for System { unsafe impl<'a> Alloc for &'a System { #[inline] unsafe fn alloc(&mut self, layout: Layout) -> Result, AllocErr> { - GlobalAlloc::alloc(*self, layout).into() + NonNull::new(GlobalAlloc::alloc(*self, layout)).ok_or(AllocErr) } #[inline] unsafe fn alloc_zeroed(&mut self, layout: Layout) -> Result, AllocErr> { - GlobalAlloc::alloc_zeroed(*self, layout).into() + NonNull::new(GlobalAlloc::alloc_zeroed(*self, layout)).ok_or(AllocErr) } #[inline] @@ -99,9 +99,9 @@ unsafe impl<'a> Alloc for &'a System { #[inline] unsafe fn realloc(&mut self, ptr: NonNull, - old_layout: Layout, + layout: Layout, new_size: usize) -> Result, AllocErr> { - GlobalAlloc::realloc(*self, ptr.as_ptr(), old_layout, new_size).into() + NonNull::new(GlobalAlloc::realloc(*self, ptr.as_ptr(), layout, new_size)).ok_or(AllocErr) } #[inline] diff --git a/src/libcore/alloc.rs b/src/libcore/alloc.rs index 632eed96049..97a49703baf 100644 --- a/src/libcore/alloc.rs +++ b/src/libcore/alloc.rs @@ -41,23 +41,6 @@ impl Void { } } -/// Convert from a return value of GlobalAlloc::alloc to that of Alloc::alloc -impl From<*mut Void> for Result, AllocErr> { - fn from(ptr: *mut Void) -> Self { - NonNull::new(ptr).ok_or(AllocErr) - } -} - -/// Convert from a return value of Alloc::alloc to that of GlobalAlloc::alloc -impl From, AllocErr>> for *mut Void { - fn from(result: Result, AllocErr>) -> Self { - match result { - Ok(ptr) => ptr.as_ptr(), - Err(_) => Void::null_mut(), - } - } -} - /// Represents the combination of a starting address and /// a total capacity of the returned block. #[derive(Debug)] -- cgit 1.4.1-3-g733a5 From f607a3872addf380846cae28661a777ec3e3c9a2 Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Wed, 11 Apr 2018 17:19:48 +0200 Subject: Rename alloc::Void to alloc::Opaque --- src/doc/nomicon | 2 +- .../src/language-features/global-allocator.md | 6 +- src/liballoc/alloc.rs | 26 +++--- src/liballoc/arc.rs | 6 +- src/liballoc/btree/node.rs | 10 +- src/liballoc/heap.rs | 12 +-- src/liballoc/raw_vec.rs | 22 ++--- src/liballoc/rc.rs | 10 +- src/liballoc_system/lib.rs | 103 ++++++++++----------- src/libcore/alloc.rs | 38 ++++---- src/libcore/ptr.rs | 4 +- src/librustc_allocator/expand.rs | 12 +-- src/libstd/alloc.rs | 6 +- src/libstd/collections/hash/table.rs | 2 +- src/test/run-make-fulldeps/std-core-cycle/bar.rs | 4 +- src/test/run-pass/allocator/auxiliary/custom.rs | 6 +- src/test/run-pass/allocator/custom.rs | 6 +- src/test/run-pass/realloc-16687.rs | 4 +- 18 files changed, 139 insertions(+), 140 deletions(-) (limited to 'src/liballoc/alloc.rs') diff --git a/src/doc/nomicon b/src/doc/nomicon index 498ac299742..3c56329d1bd 160000 --- a/src/doc/nomicon +++ b/src/doc/nomicon @@ -1 +1 @@ -Subproject commit 498ac2997420f7b25f7cd0a3f8202950d8ad93ec +Subproject commit 3c56329d1bd9038e5341f1962bcd8d043312a712 diff --git a/src/doc/unstable-book/src/language-features/global-allocator.md b/src/doc/unstable-book/src/language-features/global-allocator.md index a3f3ee65bf0..031b6347445 100644 --- a/src/doc/unstable-book/src/language-features/global-allocator.md +++ b/src/doc/unstable-book/src/language-features/global-allocator.md @@ -29,17 +29,17 @@ looks like: ```rust #![feature(global_allocator, allocator_api, heap_api)] -use std::alloc::{GlobalAlloc, System, Layout, Void}; +use std::alloc::{GlobalAlloc, System, Layout, Opaque}; use std::ptr::NonNull; struct MyAllocator; unsafe impl GlobalAlloc for MyAllocator { - unsafe fn alloc(&self, layout: Layout) -> *mut Void { + unsafe fn alloc(&self, layout: Layout) -> *mut Opaque { System.alloc(layout) } - unsafe fn dealloc(&self, ptr: *mut Void, layout: Layout) { + unsafe fn dealloc(&self, ptr: *mut Opaque, layout: Layout) { System.dealloc(ptr, layout) } } diff --git a/src/liballoc/alloc.rs b/src/liballoc/alloc.rs index 031babe5f6d..68a617e0ffe 100644 --- a/src/liballoc/alloc.rs +++ b/src/liballoc/alloc.rs @@ -76,36 +76,36 @@ pub const Heap: Global = Global; unsafe impl GlobalAlloc for Global { #[inline] - unsafe fn alloc(&self, layout: Layout) -> *mut Void { + unsafe fn alloc(&self, layout: Layout) -> *mut Opaque { #[cfg(not(stage0))] let ptr = __rust_alloc(layout.size(), layout.align()); #[cfg(stage0)] let ptr = __rust_alloc(layout.size(), layout.align(), &mut 0); - ptr as *mut Void + ptr as *mut Opaque } #[inline] - unsafe fn dealloc(&self, ptr: *mut Void, layout: Layout) { + unsafe fn dealloc(&self, ptr: *mut Opaque, layout: Layout) { __rust_dealloc(ptr as *mut u8, layout.size(), layout.align()) } #[inline] - unsafe fn realloc(&self, ptr: *mut Void, layout: Layout, new_size: usize) -> *mut Void { + unsafe fn realloc(&self, ptr: *mut Opaque, layout: Layout, new_size: usize) -> *mut Opaque { #[cfg(not(stage0))] let ptr = __rust_realloc(ptr as *mut u8, layout.size(), layout.align(), new_size); #[cfg(stage0)] let ptr = __rust_realloc(ptr as *mut u8, layout.size(), layout.align(), new_size, layout.align(), &mut 0); - ptr as *mut Void + ptr as *mut Opaque } #[inline] - unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut Void { + unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut Opaque { #[cfg(not(stage0))] let ptr = __rust_alloc_zeroed(layout.size(), layout.align()); #[cfg(stage0)] let ptr = __rust_alloc_zeroed(layout.size(), layout.align(), &mut 0); - ptr as *mut Void + ptr as *mut Opaque } #[inline] @@ -121,27 +121,27 @@ unsafe impl GlobalAlloc for Global { unsafe impl Alloc for Global { #[inline] - unsafe fn alloc(&mut self, layout: Layout) -> Result, AllocErr> { + unsafe fn alloc(&mut self, layout: Layout) -> Result, AllocErr> { NonNull::new(GlobalAlloc::alloc(self, layout)).ok_or(AllocErr) } #[inline] - unsafe fn dealloc(&mut self, ptr: NonNull, layout: Layout) { + unsafe fn dealloc(&mut self, ptr: NonNull, layout: Layout) { GlobalAlloc::dealloc(self, ptr.as_ptr(), layout) } #[inline] unsafe fn realloc(&mut self, - ptr: NonNull, + ptr: NonNull, layout: Layout, new_size: usize) - -> Result, AllocErr> + -> Result, AllocErr> { NonNull::new(GlobalAlloc::realloc(self, ptr.as_ptr(), layout, new_size)).ok_or(AllocErr) } #[inline] - unsafe fn alloc_zeroed(&mut self, layout: Layout) -> Result, AllocErr> { + unsafe fn alloc_zeroed(&mut self, layout: Layout) -> Result, AllocErr> { NonNull::new(GlobalAlloc::alloc_zeroed(self, layout)).ok_or(AllocErr) } @@ -178,7 +178,7 @@ pub(crate) unsafe fn box_free(ptr: *mut T) { // We do not allocate for Box when T is ZST, so deallocation is also not necessary. if size != 0 { let layout = Layout::from_size_align_unchecked(size, align); - Global.dealloc(ptr as *mut Void, layout); + Global.dealloc(ptr as *mut Opaque, layout); } } diff --git a/src/liballoc/arc.rs b/src/liballoc/arc.rs index 88754ace3ce..225b055d8ee 100644 --- a/src/liballoc/arc.rs +++ b/src/liballoc/arc.rs @@ -518,7 +518,7 @@ impl Arc { if self.inner().weak.fetch_sub(1, Release) == 1 { atomic::fence(Acquire); - Global.dealloc(self.ptr.as_void(), Layout::for_value(self.ptr.as_ref())) + Global.dealloc(self.ptr.as_opaque(), Layout::for_value(self.ptr.as_ref())) } } @@ -637,7 +637,7 @@ impl ArcFromSlice for Arc<[T]> { let slice = from_raw_parts_mut(self.elems, self.n_elems); ptr::drop_in_place(slice); - Global.dealloc(self.mem.as_void(), self.layout.clone()); + Global.dealloc(self.mem.as_opaque(), self.layout.clone()); } } } @@ -1156,7 +1156,7 @@ impl Drop for Weak { if self.inner().weak.fetch_sub(1, Release) == 1 { atomic::fence(Acquire); unsafe { - Global.dealloc(self.ptr.as_void(), Layout::for_value(self.ptr.as_ref())) + Global.dealloc(self.ptr.as_opaque(), Layout::for_value(self.ptr.as_ref())) } } } diff --git a/src/liballoc/btree/node.rs b/src/liballoc/btree/node.rs index 64aa40ac166..d6346662314 100644 --- a/src/liballoc/btree/node.rs +++ b/src/liballoc/btree/node.rs @@ -249,7 +249,7 @@ impl Root { self.as_mut().as_leaf_mut().parent = ptr::null(); unsafe { - Global.dealloc(NonNull::from(top).as_void(), Layout::new::>()); + Global.dealloc(NonNull::from(top).as_opaque(), Layout::new::>()); } } } @@ -435,7 +435,7 @@ impl NodeRef { > { let node = self.node; let ret = self.ascend().ok(); - Global.dealloc(node.as_void(), Layout::new::>()); + Global.dealloc(node.as_opaque(), Layout::new::>()); ret } } @@ -456,7 +456,7 @@ impl NodeRef { > { let node = self.node; let ret = self.ascend().ok(); - Global.dealloc(node.as_void(), Layout::new::>()); + Global.dealloc(node.as_opaque(), Layout::new::>()); ret } } @@ -1239,12 +1239,12 @@ impl<'a, K, V> Handle, K, V, marker::Internal>, marker:: } Global.dealloc( - right_node.node.as_void(), + right_node.node.as_opaque(), Layout::new::>(), ); } else { Global.dealloc( - right_node.node.as_void(), + right_node.node.as_opaque(), Layout::new::>(), ); } diff --git a/src/liballoc/heap.rs b/src/liballoc/heap.rs index cfb6504e743..faac38ca7ce 100644 --- a/src/liballoc/heap.rs +++ b/src/liballoc/heap.rs @@ -10,7 +10,7 @@ #![allow(deprecated)] -pub use alloc::{Layout, AllocErr, CannotReallocInPlace, Void}; +pub use alloc::{Layout, AllocErr, CannotReallocInPlace, Opaque}; use core::alloc::Alloc as CoreAlloc; use core::ptr::NonNull; @@ -54,7 +54,7 @@ unsafe impl Alloc for T where T: CoreAlloc { } unsafe fn dealloc(&mut self, ptr: *mut u8, layout: Layout) { - let ptr = NonNull::new_unchecked(ptr as *mut Void); + let ptr = NonNull::new_unchecked(ptr as *mut Opaque); CoreAlloc::dealloc(self, ptr, layout) } @@ -70,7 +70,7 @@ unsafe impl Alloc for T where T: CoreAlloc { ptr: *mut u8, layout: Layout, new_layout: Layout) -> Result<*mut u8, AllocErr> { - let ptr = NonNull::new_unchecked(ptr as *mut Void); + let ptr = NonNull::new_unchecked(ptr as *mut Opaque); CoreAlloc::realloc(self, ptr, layout, new_layout.size()).map(|ptr| ptr.cast().as_ptr()) } @@ -87,7 +87,7 @@ unsafe impl Alloc for T where T: CoreAlloc { ptr: *mut u8, layout: Layout, new_layout: Layout) -> Result { - let ptr = NonNull::new_unchecked(ptr as *mut Void); + let ptr = NonNull::new_unchecked(ptr as *mut Opaque); CoreAlloc::realloc_excess(self, ptr, layout, new_layout.size()) .map(|e| Excess(e.0 .cast().as_ptr(), e.1)) } @@ -96,7 +96,7 @@ unsafe impl Alloc for T where T: CoreAlloc { ptr: *mut u8, layout: Layout, new_layout: Layout) -> Result<(), CannotReallocInPlace> { - let ptr = NonNull::new_unchecked(ptr as *mut Void); + let ptr = NonNull::new_unchecked(ptr as *mut Opaque); CoreAlloc::grow_in_place(self, ptr, layout, new_layout.size()) } @@ -104,7 +104,7 @@ unsafe impl Alloc for T where T: CoreAlloc { ptr: *mut u8, layout: Layout, new_layout: Layout) -> Result<(), CannotReallocInPlace> { - let ptr = NonNull::new_unchecked(ptr as *mut Void); + let ptr = NonNull::new_unchecked(ptr as *mut Opaque); CoreAlloc::shrink_in_place(self, ptr, layout, new_layout.size()) } } diff --git a/src/liballoc/raw_vec.rs b/src/liballoc/raw_vec.rs index d72301f5ad6..214cc7d7d0c 100644 --- a/src/liballoc/raw_vec.rs +++ b/src/liballoc/raw_vec.rs @@ -90,7 +90,7 @@ impl RawVec { // handles ZSTs and `cap = 0` alike let ptr = if alloc_size == 0 { - NonNull::::dangling().as_void() + NonNull::::dangling().as_opaque() } else { let align = mem::align_of::(); let result = if zeroed { @@ -310,7 +310,7 @@ impl RawVec { let new_cap = 2 * self.cap; let new_size = new_cap * elem_size; alloc_guard(new_size).expect("capacity overflow"); - let ptr_res = self.a.realloc(NonNull::from(self.ptr).as_void(), + let ptr_res = self.a.realloc(NonNull::from(self.ptr).as_opaque(), cur, new_size); match ptr_res { @@ -369,7 +369,7 @@ impl RawVec { let new_cap = 2 * self.cap; let new_size = new_cap * elem_size; alloc_guard(new_size).expect("capacity overflow"); - match self.a.grow_in_place(NonNull::from(self.ptr).as_void(), old_layout, new_size) { + match self.a.grow_in_place(NonNull::from(self.ptr).as_opaque(), old_layout, new_size) { Ok(_) => { // We can't directly divide `size`. self.cap = new_cap; @@ -426,7 +426,7 @@ impl RawVec { let res = match self.current_layout() { Some(layout) => { debug_assert!(new_layout.align() == layout.align()); - self.a.realloc(NonNull::from(self.ptr).as_void(), layout, new_layout.size()) + self.a.realloc(NonNull::from(self.ptr).as_opaque(), layout, new_layout.size()) } None => self.a.alloc(new_layout), }; @@ -535,7 +535,7 @@ impl RawVec { let res = match self.current_layout() { Some(layout) => { debug_assert!(new_layout.align() == layout.align()); - self.a.realloc(NonNull::from(self.ptr).as_void(), layout, new_layout.size()) + self.a.realloc(NonNull::from(self.ptr).as_opaque(), layout, new_layout.size()) } None => self.a.alloc(new_layout), }; @@ -601,7 +601,7 @@ impl RawVec { // FIXME: may crash and burn on over-reserve alloc_guard(new_layout.size()).expect("capacity overflow"); match self.a.grow_in_place( - NonNull::from(self.ptr).as_void(), old_layout, new_layout.size(), + NonNull::from(self.ptr).as_opaque(), old_layout, new_layout.size(), ) { Ok(_) => { self.cap = new_cap; @@ -662,7 +662,7 @@ impl RawVec { let new_size = elem_size * amount; let align = mem::align_of::(); let old_layout = Layout::from_size_align_unchecked(old_size, align); - match self.a.realloc(NonNull::from(self.ptr).as_void(), + match self.a.realloc(NonNull::from(self.ptr).as_opaque(), old_layout, new_size) { Ok(p) => self.ptr = p.cast().into(), @@ -698,7 +698,7 @@ impl RawVec { let elem_size = mem::size_of::(); if elem_size != 0 { if let Some(layout) = self.current_layout() { - self.a.dealloc(NonNull::from(self.ptr).as_void(), layout); + self.a.dealloc(NonNull::from(self.ptr).as_opaque(), layout); } } } @@ -734,7 +734,7 @@ fn alloc_guard(alloc_size: usize) -> Result<(), CollectionAllocErr> { #[cfg(test)] mod tests { use super::*; - use alloc::Void; + use alloc::Opaque; #[test] fn allocator_param() { @@ -754,7 +754,7 @@ mod tests { // before allocation attempts start failing. struct BoundedAlloc { fuel: usize } unsafe impl Alloc for BoundedAlloc { - unsafe fn alloc(&mut self, layout: Layout) -> Result, AllocErr> { + unsafe fn alloc(&mut self, layout: Layout) -> Result, AllocErr> { let size = layout.size(); if size > self.fuel { return Err(AllocErr); @@ -764,7 +764,7 @@ mod tests { err @ Err(_) => err, } } - unsafe fn dealloc(&mut self, ptr: NonNull, layout: Layout) { + unsafe fn dealloc(&mut self, ptr: NonNull, layout: Layout) { Global.dealloc(ptr, layout) } } diff --git a/src/liballoc/rc.rs b/src/liballoc/rc.rs index 1c835fe50de..de0422d82bb 100644 --- a/src/liballoc/rc.rs +++ b/src/liballoc/rc.rs @@ -259,7 +259,7 @@ use core::ops::CoerceUnsized; use core::ptr::{self, NonNull}; use core::convert::From; -use alloc::{Global, Alloc, Layout, Void, box_free}; +use alloc::{Global, Alloc, Layout, Opaque, box_free}; use string::String; use vec::Vec; @@ -737,7 +737,7 @@ impl RcFromSlice for Rc<[T]> { // In the event of a panic, elements that have been written // into the new RcBox will be dropped, then the memory freed. struct Guard { - mem: NonNull, + mem: NonNull, elems: *mut T, layout: Layout, n_elems: usize, @@ -760,7 +760,7 @@ impl RcFromSlice for Rc<[T]> { let v_ptr = v as *const [T]; let ptr = Self::allocate_for_ptr(v_ptr); - let mem = ptr as *mut _ as *mut Void; + let mem = ptr as *mut _ as *mut Opaque; let layout = Layout::for_value(&*ptr); // Pointer to first element @@ -844,7 +844,7 @@ unsafe impl<#[may_dangle] T: ?Sized> Drop for Rc { self.dec_weak(); if self.weak() == 0 { - Global.dealloc(self.ptr.as_void(), Layout::for_value(self.ptr.as_ref())); + Global.dealloc(self.ptr.as_opaque(), Layout::for_value(self.ptr.as_ref())); } } } @@ -1268,7 +1268,7 @@ impl Drop for Weak { // the weak count starts at 1, and will only go to zero if all // the strong pointers have disappeared. if self.weak() == 0 { - Global.dealloc(self.ptr.as_void(), Layout::for_value(self.ptr.as_ref())); + Global.dealloc(self.ptr.as_opaque(), Layout::for_value(self.ptr.as_ref())); } } } diff --git a/src/liballoc_system/lib.rs b/src/liballoc_system/lib.rs index 7fea6061169..fd8109e2a4a 100644 --- a/src/liballoc_system/lib.rs +++ b/src/liballoc_system/lib.rs @@ -41,7 +41,7 @@ const MIN_ALIGN: usize = 8; #[allow(dead_code)] const MIN_ALIGN: usize = 16; -use core::alloc::{Alloc, GlobalAlloc, AllocErr, Layout, Void}; +use core::alloc::{Alloc, GlobalAlloc, AllocErr, Layout, Opaque}; use core::ptr::NonNull; #[unstable(feature = "allocator_api", issue = "32838")] @@ -50,25 +50,25 @@ pub struct System; #[unstable(feature = "allocator_api", issue = "32838")] unsafe impl Alloc for System { #[inline] - unsafe fn alloc(&mut self, layout: Layout) -> Result, AllocErr> { + unsafe fn alloc(&mut self, layout: Layout) -> Result, AllocErr> { NonNull::new(GlobalAlloc::alloc(self, layout)).ok_or(AllocErr) } #[inline] - unsafe fn alloc_zeroed(&mut self, layout: Layout) -> Result, AllocErr> { + unsafe fn alloc_zeroed(&mut self, layout: Layout) -> Result, AllocErr> { NonNull::new(GlobalAlloc::alloc_zeroed(self, layout)).ok_or(AllocErr) } #[inline] - unsafe fn dealloc(&mut self, ptr: NonNull, layout: Layout) { + unsafe fn dealloc(&mut self, ptr: NonNull, layout: Layout) { GlobalAlloc::dealloc(self, ptr.as_ptr(), layout) } #[inline] unsafe fn realloc(&mut self, - ptr: NonNull, + ptr: NonNull, layout: Layout, - new_size: usize) -> Result, AllocErr> { + new_size: usize) -> Result, AllocErr> { NonNull::new(GlobalAlloc::realloc(self, ptr.as_ptr(), layout, new_size)).ok_or(AllocErr) } @@ -82,25 +82,25 @@ unsafe impl Alloc for System { #[unstable(feature = "allocator_api", issue = "32838")] unsafe impl<'a> Alloc for &'a System { #[inline] - unsafe fn alloc(&mut self, layout: Layout) -> Result, AllocErr> { + unsafe fn alloc(&mut self, layout: Layout) -> Result, AllocErr> { NonNull::new(GlobalAlloc::alloc(*self, layout)).ok_or(AllocErr) } #[inline] - unsafe fn alloc_zeroed(&mut self, layout: Layout) -> Result, AllocErr> { + unsafe fn alloc_zeroed(&mut self, layout: Layout) -> Result, AllocErr> { NonNull::new(GlobalAlloc::alloc_zeroed(*self, layout)).ok_or(AllocErr) } #[inline] - unsafe fn dealloc(&mut self, ptr: NonNull, layout: Layout) { + unsafe fn dealloc(&mut self, ptr: NonNull, layout: Layout) { GlobalAlloc::dealloc(*self, ptr.as_ptr(), layout) } #[inline] unsafe fn realloc(&mut self, - ptr: NonNull, + ptr: NonNull, layout: Layout, - new_size: usize) -> Result, AllocErr> { + new_size: usize) -> Result, AllocErr> { NonNull::new(GlobalAlloc::realloc(*self, ptr.as_ptr(), layout, new_size)).ok_or(AllocErr) } @@ -112,13 +112,13 @@ unsafe impl<'a> Alloc for &'a System { #[cfg(any(windows, unix, target_os = "cloudabi", target_os = "redox"))] mod realloc_fallback { - use core::alloc::{GlobalAlloc, Void, Layout}; + use core::alloc::{GlobalAlloc, Opaque, Layout}; use core::cmp; use core::ptr; impl super::System { - pub(crate) unsafe fn realloc_fallback(&self, ptr: *mut Void, old_layout: Layout, - new_size: usize) -> *mut Void { + pub(crate) unsafe fn realloc_fallback(&self, ptr: *mut Opaque, old_layout: Layout, + new_size: usize) -> *mut Opaque { // Docs for GlobalAlloc::realloc require this to be valid: let new_layout = Layout::from_size_align_unchecked(new_size, old_layout.align()); @@ -141,20 +141,21 @@ mod platform { use MIN_ALIGN; use System; - use core::alloc::{GlobalAlloc, Layout, Void}; + use core::alloc::{GlobalAlloc, Layout, Opaque}; #[unstable(feature = "allocator_api", issue = "32838")] unsafe impl GlobalAlloc for System { #[inline] - unsafe fn alloc(&self, layout: Layout) -> *mut Void { + unsafe fn alloc(&self, layout: Layout) -> *mut Opaque { if layout.align() <= MIN_ALIGN && layout.align() <= layout.size() { - libc::malloc(layout.size()) as *mut Void + libc::malloc(layout.size()) as *mut Opaque } else { #[cfg(target_os = "macos")] { if layout.align() > (1 << 31) { - // FIXME: use Void::null_mut https://github.com/rust-lang/rust/issues/49659 - return 0 as *mut Void + // FIXME: use Opaque::null_mut + // https://github.com/rust-lang/rust/issues/49659 + return 0 as *mut Opaque } } aligned_malloc(&layout) @@ -162,9 +163,9 @@ mod platform { } #[inline] - unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut Void { + unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut Opaque { if layout.align() <= MIN_ALIGN && layout.align() <= layout.size() { - libc::calloc(layout.size(), 1) as *mut Void + libc::calloc(layout.size(), 1) as *mut Opaque } else { let ptr = self.alloc(layout.clone()); if !ptr.is_null() { @@ -175,24 +176,23 @@ mod platform { } #[inline] - unsafe fn dealloc(&self, ptr: *mut Void, _layout: Layout) { + unsafe fn dealloc(&self, ptr: *mut Opaque, _layout: Layout) { libc::free(ptr as *mut libc::c_void) } #[inline] - unsafe fn realloc(&self, ptr: *mut Void, old_layout: Layout, new_size: usize) -> *mut Void { - let align = old_layout.align(); - if align <= MIN_ALIGN && align <= new_size { - libc::realloc(ptr as *mut libc::c_void, new_size) as *mut Void + unsafe fn realloc(&self, ptr: *mut Opaque, layout: Layout, new_size: usize) -> *mut Opaque { + if layout.align() <= MIN_ALIGN && layout.align() <= new_size { + libc::realloc(ptr as *mut libc::c_void, new_size) as *mut Opaque } else { - self.realloc_fallback(ptr, old_layout, new_size) + self.realloc_fallback(ptr, layout, new_size) } } } #[cfg(any(target_os = "android", target_os = "redox", target_os = "solaris"))] #[inline] - unsafe fn aligned_malloc(layout: &Layout) -> *mut Void { + unsafe fn aligned_malloc(layout: &Layout) -> *mut Opaque { // On android we currently target API level 9 which unfortunately // doesn't have the `posix_memalign` API used below. Instead we use // `memalign`, but this unfortunately has the property on some systems @@ -210,19 +210,19 @@ mod platform { // [3]: https://bugs.chromium.org/p/chromium/issues/detail?id=138579 // [4]: https://chromium.googlesource.com/chromium/src/base/+/master/ // /memory/aligned_memory.cc - libc::memalign(layout.align(), layout.size()) as *mut Void + libc::memalign(layout.align(), layout.size()) as *mut Opaque } #[cfg(not(any(target_os = "android", target_os = "redox", target_os = "solaris")))] #[inline] - unsafe fn aligned_malloc(layout: &Layout) -> *mut Void { + unsafe fn aligned_malloc(layout: &Layout) -> *mut Opaque { let mut out = ptr::null_mut(); let ret = libc::posix_memalign(&mut out, layout.align(), layout.size()); if ret != 0 { - // FIXME: use Void::null_mut https://github.com/rust-lang/rust/issues/49659 - 0 as *mut Void + // FIXME: use Opaque::null_mut https://github.com/rust-lang/rust/issues/49659 + 0 as *mut Opaque } else { - out as *mut Void + out as *mut Opaque } } } @@ -232,7 +232,7 @@ mod platform { mod platform { use MIN_ALIGN; use System; - use core::alloc::{GlobalAlloc, Void, Layout}; + use core::alloc::{GlobalAlloc, Opaque, Layout}; type LPVOID = *mut u8; type HANDLE = LPVOID; @@ -264,7 +264,7 @@ mod platform { } #[inline] - unsafe fn allocate_with_flags(layout: Layout, flags: DWORD) -> *mut Void { + unsafe fn allocate_with_flags(layout: Layout, flags: DWORD) -> *mut Opaque { let ptr = if layout.align() <= MIN_ALIGN { HeapAlloc(GetProcessHeap(), flags, layout.size()) } else { @@ -276,23 +276,23 @@ mod platform { align_ptr(ptr, layout.align()) } }; - ptr as *mut Void + ptr as *mut Opaque } #[unstable(feature = "allocator_api", issue = "32838")] unsafe impl GlobalAlloc for System { #[inline] - unsafe fn alloc(&self, layout: Layout) -> *mut Void { + unsafe fn alloc(&self, layout: Layout) -> *mut Opaque { allocate_with_flags(layout, 0) } #[inline] - unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut Void { + unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut Opaque { allocate_with_flags(layout, HEAP_ZERO_MEMORY) } #[inline] - unsafe fn dealloc(&self, ptr: *mut Void, layout: Layout) { + unsafe fn dealloc(&self, ptr: *mut Opaque, layout: Layout) { if layout.align() <= MIN_ALIGN { let err = HeapFree(GetProcessHeap(), 0, ptr as LPVOID); debug_assert!(err != 0, "Failed to free heap memory: {}", @@ -306,12 +306,11 @@ mod platform { } #[inline] - unsafe fn realloc(&self, ptr: *mut Void, old_layout: Layout, new_size: usize) -> *mut Void { - let align = old_layout.align(); - if align <= MIN_ALIGN { - HeapReAlloc(GetProcessHeap(), 0, ptr as LPVOID, new_size) as *mut Void + unsafe fn realloc(&self, ptr: *mut Opaque, layout: Layout, new_size: usize) -> *mut Opaque { + if layout.align() <= MIN_ALIGN { + HeapReAlloc(GetProcessHeap(), 0, ptr as LPVOID, new_size) as *mut Opaque } else { - self.realloc_fallback(ptr, old_layout, new_size) + self.realloc_fallback(ptr, layout, new_size) } } } @@ -338,7 +337,7 @@ mod platform { mod platform { extern crate dlmalloc; - use core::alloc::{GlobalAlloc, Layout, Void}; + use core::alloc::{GlobalAlloc, Layout, Opaque}; use System; // No need for synchronization here as wasm is currently single-threaded @@ -347,23 +346,23 @@ mod platform { #[unstable(feature = "allocator_api", issue = "32838")] unsafe impl GlobalAlloc for System { #[inline] - unsafe fn alloc(&self, layout: Layout) -> *mut Void { - DLMALLOC.malloc(layout.size(), layout.align()) as *mut Void + unsafe fn alloc(&self, layout: Layout) -> *mut Opaque { + DLMALLOC.malloc(layout.size(), layout.align()) as *mut Opaque } #[inline] - unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut Void { - DLMALLOC.calloc(layout.size(), layout.align()) as *mut Void + unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut Opaque { + DLMALLOC.calloc(layout.size(), layout.align()) as *mut Opaque } #[inline] - unsafe fn dealloc(&self, ptr: *mut Void, layout: Layout) { + unsafe fn dealloc(&self, ptr: *mut Opaque, layout: Layout) { DLMALLOC.free(ptr as *mut u8, layout.size(), layout.align()) } #[inline] - unsafe fn realloc(&self, ptr: *mut Void, layout: Layout, new_size: usize) -> *mut Void { - DLMALLOC.realloc(ptr as *mut u8, layout.size(), layout.align(), new_size) as *mut Void + unsafe fn realloc(&self, ptr: *mut Opaque, layout: Layout, new_size: usize) -> *mut Opaque { + DLMALLOC.realloc(ptr as *mut u8, layout.size(), layout.align(), new_size) as *mut Opaque } } } diff --git a/src/libcore/alloc.rs b/src/libcore/alloc.rs index 97a49703baf..fdba91bec80 100644 --- a/src/libcore/alloc.rs +++ b/src/libcore/alloc.rs @@ -24,12 +24,12 @@ use ptr::{self, NonNull}; extern { /// An opaque, unsized type. Used for pointers to allocated memory. /// - /// This type can only be used behind a pointer like `*mut Void` or `ptr::NonNull`. + /// This type can only be used behind a pointer like `*mut Opaque` or `ptr::NonNull`. /// Such pointers are similar to C’s `void*` type. - pub type Void; + pub type Opaque; } -impl Void { +impl Opaque { /// Similar to `std::ptr::null`, which requires `T: Sized`. pub fn null() -> *const Self { 0 as _ @@ -44,7 +44,7 @@ impl Void { /// Represents the combination of a starting address and /// a total capacity of the returned block. #[derive(Debug)] -pub struct Excess(pub NonNull, pub usize); +pub struct Excess(pub NonNull, pub usize); fn size_align() -> (usize, usize) { (mem::size_of::(), mem::align_of::()) @@ -387,11 +387,11 @@ impl From for CollectionAllocErr { // FIXME: docs pub unsafe trait GlobalAlloc { - unsafe fn alloc(&self, layout: Layout) -> *mut Void; + unsafe fn alloc(&self, layout: Layout) -> *mut Opaque; - unsafe fn dealloc(&self, ptr: *mut Void, layout: Layout); + unsafe fn dealloc(&self, ptr: *mut Opaque, layout: Layout); - unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut Void { + unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut Opaque { let size = layout.size(); let ptr = self.alloc(layout); if !ptr.is_null() { @@ -404,7 +404,7 @@ pub unsafe trait GlobalAlloc { /// /// `new_size`, when rounded up to the nearest multiple of `old_layout.align()`, /// must not overflow (i.e. the rounded value must be less than `usize::MAX`). - unsafe fn realloc(&self, ptr: *mut Void, old_layout: Layout, new_size: usize) -> *mut Void { + unsafe fn realloc(&self, ptr: *mut Opaque, old_layout: Layout, new_size: usize) -> *mut Opaque { let new_layout = Layout::from_size_align_unchecked(new_size, old_layout.align()); let new_ptr = self.alloc(new_layout); if !new_ptr.is_null() { @@ -554,7 +554,7 @@ pub unsafe trait Alloc { /// Clients wishing to abort computation in response to an /// allocation error are encouraged to call the allocator's `oom` /// method, rather than directly invoking `panic!` or similar. - unsafe fn alloc(&mut self, layout: Layout) -> Result, AllocErr>; + unsafe fn alloc(&mut self, layout: Layout) -> Result, AllocErr>; /// Deallocate the memory referenced by `ptr`. /// @@ -571,7 +571,7 @@ pub unsafe trait Alloc { /// * In addition to fitting the block of memory `layout`, the /// alignment of the `layout` must match the alignment used /// to allocate that block of memory. - unsafe fn dealloc(&mut self, ptr: NonNull, layout: Layout); + unsafe fn dealloc(&mut self, ptr: NonNull, layout: Layout); /// Allocator-specific method for signaling an out-of-memory /// condition. @@ -689,9 +689,9 @@ pub unsafe trait Alloc { /// reallocation error are encouraged to call the allocator's `oom` /// method, rather than directly invoking `panic!` or similar. unsafe fn realloc(&mut self, - ptr: NonNull, + ptr: NonNull, layout: Layout, - new_size: usize) -> Result, AllocErr> { + new_size: usize) -> Result, AllocErr> { let old_size = layout.size(); if new_size >= old_size { @@ -732,7 +732,7 @@ pub unsafe trait Alloc { /// Clients wishing to abort computation in response to an /// allocation error are encouraged to call the allocator's `oom` /// method, rather than directly invoking `panic!` or similar. - unsafe fn alloc_zeroed(&mut self, layout: Layout) -> Result, AllocErr> { + unsafe fn alloc_zeroed(&mut self, layout: Layout) -> Result, AllocErr> { let size = layout.size(); let p = self.alloc(layout); if let Ok(p) = p { @@ -781,7 +781,7 @@ pub unsafe trait Alloc { /// reallocation error are encouraged to call the allocator's `oom` /// method, rather than directly invoking `panic!` or similar. unsafe fn realloc_excess(&mut self, - ptr: NonNull, + ptr: NonNull, layout: Layout, new_size: usize) -> Result { let new_layout = Layout::from_size_align_unchecked(new_size, layout.align()); @@ -826,7 +826,7 @@ pub unsafe trait Alloc { /// `grow_in_place` failures without aborting, or to fall back on /// another reallocation method before resorting to an abort. unsafe fn grow_in_place(&mut self, - ptr: NonNull, + ptr: NonNull, layout: Layout, new_size: usize) -> Result<(), CannotReallocInPlace> { let _ = ptr; // this default implementation doesn't care about the actual address. @@ -881,7 +881,7 @@ pub unsafe trait Alloc { /// `shrink_in_place` failures without aborting, or to fall back /// on another reallocation method before resorting to an abort. unsafe fn shrink_in_place(&mut self, - ptr: NonNull, + ptr: NonNull, layout: Layout, new_size: usize) -> Result<(), CannotReallocInPlace> { let _ = ptr; // this default implementation doesn't care about the actual address. @@ -960,7 +960,7 @@ pub unsafe trait Alloc { { let k = Layout::new::(); if k.size() > 0 { - self.dealloc(ptr.as_void(), k); + self.dealloc(ptr.as_opaque(), k); } } @@ -1048,7 +1048,7 @@ pub unsafe trait Alloc { match (Layout::array::(n_old), Layout::array::(n_new)) { (Ok(ref k_old), Ok(ref k_new)) if k_old.size() > 0 && k_new.size() > 0 => { debug_assert!(k_old.align() == k_new.align()); - self.realloc(ptr.as_void(), k_old.clone(), k_new.size()).map(NonNull::cast) + self.realloc(ptr.as_opaque(), k_old.clone(), k_new.size()).map(NonNull::cast) } _ => { Err(AllocErr) @@ -1081,7 +1081,7 @@ pub unsafe trait Alloc { { match Layout::array::(n) { Ok(ref k) if k.size() > 0 => { - Ok(self.dealloc(ptr.as_void(), k.clone())) + Ok(self.dealloc(ptr.as_opaque(), k.clone())) } _ => { Err(AllocErr) diff --git a/src/libcore/ptr.rs b/src/libcore/ptr.rs index f4e668328ce..4a7d7c410eb 100644 --- a/src/libcore/ptr.rs +++ b/src/libcore/ptr.rs @@ -2751,9 +2751,9 @@ impl NonNull { } } - /// Cast to a `Void` pointer + /// Cast to an `Opaque` pointer #[unstable(feature = "allocator_api", issue = "32838")] - pub fn as_void(self) -> NonNull<::alloc::Void> { + pub fn as_opaque(self) -> NonNull<::alloc::Opaque> { unsafe { NonNull::new_unchecked(self.as_ptr() as _) } diff --git a/src/librustc_allocator/expand.rs b/src/librustc_allocator/expand.rs index 58d4c7f289c..305502e7f06 100644 --- a/src/librustc_allocator/expand.rs +++ b/src/librustc_allocator/expand.rs @@ -221,7 +221,7 @@ impl<'a> AllocFnFactory<'a> { let ident = ident(); args.push(self.cx.arg(self.span, ident, self.ptr_u8())); let arg = self.cx.expr_ident(self.span, ident); - self.cx.expr_cast(self.span, arg, self.ptr_void()) + self.cx.expr_cast(self.span, arg, self.ptr_opaque()) } AllocatorTy::Usize => { @@ -276,13 +276,13 @@ impl<'a> AllocFnFactory<'a> { self.cx.ty_ptr(self.span, ty_u8, Mutability::Mutable) } - fn ptr_void(&self) -> P { - let void = self.cx.path(self.span, vec![ + fn ptr_opaque(&self) -> P { + let opaque = self.cx.path(self.span, vec![ self.core, Ident::from_str("alloc"), - Ident::from_str("Void"), + Ident::from_str("Opaque"), ]); - let ty_void = self.cx.ty_path(void); - self.cx.ty_ptr(self.span, ty_void, Mutability::Mutable) + let ty_opaque = self.cx.ty_path(opaque); + self.cx.ty_ptr(self.span, ty_opaque, Mutability::Mutable) } } diff --git a/src/libstd/alloc.rs b/src/libstd/alloc.rs index 4e728df010a..ff578ec42d2 100644 --- a/src/libstd/alloc.rs +++ b/src/libstd/alloc.rs @@ -21,7 +21,7 @@ #[doc(hidden)] #[allow(unused_attributes)] pub mod __default_lib_allocator { - use super::{System, Layout, GlobalAlloc, Void}; + use super::{System, Layout, GlobalAlloc, Opaque}; // for symbol names src/librustc/middle/allocator.rs // for signatures src/librustc_allocator/lib.rs @@ -46,7 +46,7 @@ pub mod __default_lib_allocator { pub unsafe extern fn __rdl_dealloc(ptr: *mut u8, size: usize, align: usize) { - System.dealloc(ptr as *mut Void, Layout::from_size_align_unchecked(size, align)) + System.dealloc(ptr as *mut Opaque, Layout::from_size_align_unchecked(size, align)) } #[no_mangle] @@ -56,7 +56,7 @@ pub mod __default_lib_allocator { align: usize, new_size: usize) -> *mut u8 { let old_layout = Layout::from_size_align_unchecked(old_size, align); - System.realloc(ptr as *mut Void, old_layout, new_size) as *mut u8 + System.realloc(ptr as *mut Opaque, old_layout, new_size) as *mut u8 } #[no_mangle] diff --git a/src/libstd/collections/hash/table.rs b/src/libstd/collections/hash/table.rs index 38c99373788..93f059076d7 100644 --- a/src/libstd/collections/hash/table.rs +++ b/src/libstd/collections/hash/table.rs @@ -1183,7 +1183,7 @@ unsafe impl<#[may_dangle] K, #[may_dangle] V> Drop for RawTable { debug_assert!(!oflo, "should be impossible"); unsafe { - Global.dealloc(NonNull::new_unchecked(self.hashes.ptr()).as_void(), + Global.dealloc(NonNull::new_unchecked(self.hashes.ptr()).as_opaque(), Layout::from_size_align(size, align).unwrap()); // Remember how everything was allocated out of one buffer // during initialization? We only need one call to free here. diff --git a/src/test/run-make-fulldeps/std-core-cycle/bar.rs b/src/test/run-make-fulldeps/std-core-cycle/bar.rs index 20b87028fd1..62fd2ade1ca 100644 --- a/src/test/run-make-fulldeps/std-core-cycle/bar.rs +++ b/src/test/run-make-fulldeps/std-core-cycle/bar.rs @@ -16,11 +16,11 @@ use std::alloc::*; pub struct A; unsafe impl GlobalAlloc for A { - unsafe fn alloc(&self, _: Layout) -> *mut Void { + unsafe fn alloc(&self, _: Layout) -> *mut Opaque { loop {} } - unsafe fn dealloc(&self, _ptr: *mut Void, _: Layout) { + unsafe fn dealloc(&self, _ptr: *mut Opaque, _: Layout) { loop {} } } diff --git a/src/test/run-pass/allocator/auxiliary/custom.rs b/src/test/run-pass/allocator/auxiliary/custom.rs index 95096efc7ef..e6a2e22983b 100644 --- a/src/test/run-pass/allocator/auxiliary/custom.rs +++ b/src/test/run-pass/allocator/auxiliary/custom.rs @@ -13,18 +13,18 @@ #![feature(heap_api, allocator_api)] #![crate_type = "rlib"] -use std::heap::{GlobalAlloc, System, Layout, Void}; +use std::heap::{GlobalAlloc, System, Layout, Opaque}; use std::sync::atomic::{AtomicUsize, Ordering}; pub struct A(pub AtomicUsize); unsafe impl GlobalAlloc for A { - unsafe fn alloc(&self, layout: Layout) -> *mut Void { + unsafe fn alloc(&self, layout: Layout) -> *mut Opaque { self.0.fetch_add(1, Ordering::SeqCst); System.alloc(layout) } - unsafe fn dealloc(&self, ptr: *mut Void, layout: Layout) { + unsafe fn dealloc(&self, ptr: *mut Opaque, layout: Layout) { self.0.fetch_add(1, Ordering::SeqCst); System.dealloc(ptr, layout) } diff --git a/src/test/run-pass/allocator/custom.rs b/src/test/run-pass/allocator/custom.rs index f7b2fd73c87..415d39a593e 100644 --- a/src/test/run-pass/allocator/custom.rs +++ b/src/test/run-pass/allocator/custom.rs @@ -15,7 +15,7 @@ extern crate helper; -use std::alloc::{self, Global, Alloc, System, Layout, Void}; +use std::alloc::{self, Global, Alloc, System, Layout, Opaque}; use std::sync::atomic::{AtomicUsize, Ordering, ATOMIC_USIZE_INIT}; static HITS: AtomicUsize = ATOMIC_USIZE_INIT; @@ -23,12 +23,12 @@ static HITS: AtomicUsize = ATOMIC_USIZE_INIT; struct A; unsafe impl alloc::GlobalAlloc for A { - unsafe fn alloc(&self, layout: Layout) -> *mut Void { + unsafe fn alloc(&self, layout: Layout) -> *mut Opaque { HITS.fetch_add(1, Ordering::SeqCst); System.alloc(layout) } - unsafe fn dealloc(&self, ptr: *mut Void, layout: Layout) { + unsafe fn dealloc(&self, ptr: *mut Opaque, layout: Layout) { HITS.fetch_add(1, Ordering::SeqCst); System.dealloc(ptr, layout) } diff --git a/src/test/run-pass/realloc-16687.rs b/src/test/run-pass/realloc-16687.rs index 49ab0ee3310..38cc23c16a9 100644 --- a/src/test/run-pass/realloc-16687.rs +++ b/src/test/run-pass/realloc-16687.rs @@ -64,7 +64,7 @@ unsafe fn test_triangle() -> bool { println!("deallocate({:?}, {:?}", ptr, layout); } - Global.dealloc(NonNull::new_unchecked(ptr).as_void(), layout); + Global.dealloc(NonNull::new_unchecked(ptr).as_opaque(), layout); } unsafe fn reallocate(ptr: *mut u8, old: Layout, new: Layout) -> *mut u8 { @@ -72,7 +72,7 @@ unsafe fn test_triangle() -> bool { println!("reallocate({:?}, old={:?}, new={:?})", ptr, old, new); } - let ret = Global.realloc(NonNull::new_unchecked(ptr).as_void(), old.clone(), new.size()) + let ret = Global.realloc(NonNull::new_unchecked(ptr).as_opaque(), old.clone(), new.size()) .unwrap_or_else(|_| Global.oom()); if PRINT { -- cgit 1.4.1-3-g733a5 From e513c1bd314bbeb6295a7a759de8833b52ff854d Mon Sep 17 00:00:00 2001 From: Steven Fackler Date: Fri, 20 Apr 2018 21:05:13 -0700 Subject: Replace GlobalAlloc::oom with a lang item --- src/liballoc/alloc.rs | 35 ++++++++++++++++---------------- src/liballoc/arc.rs | 4 ++-- src/liballoc/rc.rs | 4 ++-- src/liballoc_jemalloc/lib.rs | 5 ++--- src/liballoc_system/lib.rs | 2 +- src/libcore/alloc.rs | 11 ---------- src/librustc/middle/lang_items.rs | 3 ++- src/librustc/middle/weak_lang_items.rs | 1 + src/librustc_allocator/lib.rs | 5 ----- src/libstd/alloc.rs | 13 ++++++++++-- src/libstd/collections/hash/map.rs | 4 ++-- src/libstd/collections/hash/table.rs | 6 +++--- src/libstd/lib.rs | 3 ++- src/test/run-pass/allocator-alloc-one.rs | 6 ++---- src/test/run-pass/realloc-16687.rs | 6 +++--- src/test/run-pass/regions-mock-trans.rs | 4 ++-- 16 files changed, 53 insertions(+), 59 deletions(-) (limited to 'src/liballoc/alloc.rs') diff --git a/src/liballoc/alloc.rs b/src/liballoc/alloc.rs index 68a617e0ffe..a9c06523718 100644 --- a/src/liballoc/alloc.rs +++ b/src/liballoc/alloc.rs @@ -48,9 +48,6 @@ extern "Rust" { #[allocator] #[rustc_allocator_nounwind] fn __rust_alloc(size: usize, align: usize) -> *mut u8; - #[cold] - #[rustc_allocator_nounwind] - fn __rust_oom() -> !; #[rustc_allocator_nounwind] fn __rust_dealloc(ptr: *mut u8, size: usize, align: usize); #[rustc_allocator_nounwind] @@ -107,16 +104,6 @@ unsafe impl GlobalAlloc for Global { let ptr = __rust_alloc_zeroed(layout.size(), layout.align(), &mut 0); ptr as *mut Opaque } - - #[inline] - fn oom(&self) -> ! { - unsafe { - #[cfg(not(stage0))] - __rust_oom(); - #[cfg(stage0)] - __rust_oom(&mut 0); - } - } } unsafe impl Alloc for Global { @@ -147,7 +134,7 @@ unsafe impl Alloc for Global { #[inline] fn oom(&mut self) -> ! { - GlobalAlloc::oom(self) + oom() } } @@ -165,7 +152,7 @@ unsafe fn exchange_malloc(size: usize, align: usize) -> *mut u8 { if !ptr.is_null() { ptr as *mut u8 } else { - Global.oom() + oom() } } } @@ -182,19 +169,33 @@ pub(crate) unsafe fn box_free(ptr: *mut T) { } } +#[cfg(stage0)] +pub fn oom() -> ! { + unsafe { ::core::intrinsics::abort() } +} + +#[cfg(not(stage0))] +pub fn oom() -> ! { + extern { + #[lang = "oom"] + fn oom_impl() -> !; + } + unsafe { oom_impl() } +} + #[cfg(test)] mod tests { extern crate test; use self::test::Bencher; use boxed::Box; - use alloc::{Global, Alloc, Layout}; + use alloc::{Global, Alloc, Layout, oom}; #[test] fn allocate_zeroed() { unsafe { let layout = Layout::from_size_align(1024, 1).unwrap(); let ptr = Global.alloc_zeroed(layout.clone()) - .unwrap_or_else(|_| Global.oom()); + .unwrap_or_else(|_| oom()); let mut i = ptr.cast::().as_ptr(); let end = i.offset(layout.size() as isize); diff --git a/src/liballoc/arc.rs b/src/liballoc/arc.rs index 225b055d8ee..f5980f4599e 100644 --- a/src/liballoc/arc.rs +++ b/src/liballoc/arc.rs @@ -31,7 +31,7 @@ use core::hash::{Hash, Hasher}; use core::{isize, usize}; use core::convert::From; -use alloc::{Global, Alloc, Layout, box_free}; +use alloc::{Global, Alloc, Layout, box_free, oom}; use boxed::Box; use string::String; use vec::Vec; @@ -553,7 +553,7 @@ impl Arc { let layout = Layout::for_value(&*fake_ptr); let mem = Global.alloc(layout) - .unwrap_or_else(|_| Global.oom()); + .unwrap_or_else(|_| oom()); // Initialize the real ArcInner let inner = set_data_ptr(ptr as *mut T, mem.as_ptr() as *mut u8) as *mut ArcInner; diff --git a/src/liballoc/rc.rs b/src/liballoc/rc.rs index de0422d82bb..8fb8e111754 100644 --- a/src/liballoc/rc.rs +++ b/src/liballoc/rc.rs @@ -259,7 +259,7 @@ use core::ops::CoerceUnsized; use core::ptr::{self, NonNull}; use core::convert::From; -use alloc::{Global, Alloc, Layout, Opaque, box_free}; +use alloc::{Global, Alloc, Layout, Opaque, box_free, oom}; use string::String; use vec::Vec; @@ -668,7 +668,7 @@ impl Rc { let layout = Layout::for_value(&*fake_ptr); let mem = Global.alloc(layout) - .unwrap_or_else(|_| Global.oom()); + .unwrap_or_else(|_| oom()); // Initialize the real RcBox let inner = set_data_ptr(ptr as *mut T, mem.as_ptr() as *mut u8) as *mut RcBox; diff --git a/src/liballoc_jemalloc/lib.rs b/src/liballoc_jemalloc/lib.rs index 2b66c293f21..8b118a2cabb 100644 --- a/src/liballoc_jemalloc/lib.rs +++ b/src/liballoc_jemalloc/lib.rs @@ -30,8 +30,6 @@ extern crate libc; pub use contents::*; #[cfg(not(dummy_jemalloc))] mod contents { - use core::alloc::GlobalAlloc; - use alloc_system::System; use libc::{c_int, c_void, size_t}; // Note that the symbols here are prefixed by default on macOS and Windows (we @@ -100,10 +98,11 @@ mod contents { ptr } + #[cfg(stage0)] #[no_mangle] #[rustc_std_internal_symbol] pub unsafe extern fn __rde_oom() -> ! { - System.oom() + ::alloc_system::oom() } #[no_mangle] diff --git a/src/liballoc_system/lib.rs b/src/liballoc_system/lib.rs index fd8109e2a4a..aff98ae2f10 100644 --- a/src/liballoc_system/lib.rs +++ b/src/liballoc_system/lib.rs @@ -368,7 +368,7 @@ mod platform { } #[inline] -fn oom() -> ! { +pub fn oom() -> ! { write_to_stderr("fatal runtime error: memory allocation failed"); unsafe { ::core::intrinsics::abort(); diff --git a/src/libcore/alloc.rs b/src/libcore/alloc.rs index 54440eaa40f..7d893676a6c 100644 --- a/src/libcore/alloc.rs +++ b/src/libcore/alloc.rs @@ -451,17 +451,6 @@ pub unsafe trait GlobalAlloc { } new_ptr } - - /// Aborts the thread or process, optionally performing - /// cleanup or logging diagnostic information before panicking or - /// aborting. - /// - /// `oom` is meant to be used by clients unable to cope with an - /// unsatisfied allocation request, and wish to abandon - /// computation rather than attempt to recover locally. - fn oom(&self) -> ! { - unsafe { ::intrinsics::abort() } - } } /// An implementation of `Alloc` can allocate, reallocate, and diff --git a/src/librustc/middle/lang_items.rs b/src/librustc/middle/lang_items.rs index c7412dbeeb3..95e92e21b09 100644 --- a/src/librustc/middle/lang_items.rs +++ b/src/librustc/middle/lang_items.rs @@ -303,7 +303,8 @@ language_item_table! { ExchangeMallocFnLangItem, "exchange_malloc", exchange_malloc_fn; BoxFreeFnLangItem, "box_free", box_free_fn; - DropInPlaceFnLangItem, "drop_in_place", drop_in_place_fn; + DropInPlaceFnLangItem, "drop_in_place", drop_in_place_fn; + OomLangItem, "oom", oom; StartFnLangItem, "start", start_fn; diff --git a/src/librustc/middle/weak_lang_items.rs b/src/librustc/middle/weak_lang_items.rs index e19f4483f65..a2bceb19102 100644 --- a/src/librustc/middle/weak_lang_items.rs +++ b/src/librustc/middle/weak_lang_items.rs @@ -151,4 +151,5 @@ weak_lang_items! { panic_fmt, PanicFmtLangItem, rust_begin_unwind; eh_personality, EhPersonalityLangItem, rust_eh_personality; eh_unwind_resume, EhUnwindResumeLangItem, rust_eh_unwind_resume; + oom, OomLangItem, rust_oom; } diff --git a/src/librustc_allocator/lib.rs b/src/librustc_allocator/lib.rs index 706eab72d44..f3103e21606 100644 --- a/src/librustc_allocator/lib.rs +++ b/src/librustc_allocator/lib.rs @@ -23,11 +23,6 @@ pub static ALLOCATOR_METHODS: &[AllocatorMethod] = &[ inputs: &[AllocatorTy::Layout], output: AllocatorTy::ResultPtr, }, - AllocatorMethod { - name: "oom", - inputs: &[], - output: AllocatorTy::Bang, - }, AllocatorMethod { name: "dealloc", inputs: &[AllocatorTy::Ptr, AllocatorTy::Layout], diff --git a/src/libstd/alloc.rs b/src/libstd/alloc.rs index ff578ec42d2..a8578404467 100644 --- a/src/libstd/alloc.rs +++ b/src/libstd/alloc.rs @@ -13,10 +13,18 @@ #![unstable(issue = "32838", feature = "allocator_api")] #[doc(inline)] #[allow(deprecated)] pub use alloc_crate::alloc::Heap; -#[doc(inline)] pub use alloc_crate::alloc::Global; +#[doc(inline)] pub use alloc_crate::alloc::{Global, oom}; #[doc(inline)] pub use alloc_system::System; #[doc(inline)] pub use core::alloc::*; +#[cfg(not(stage0))] +#[cfg(not(test))] +#[doc(hidden)] +#[lang = "oom"] +pub extern fn rust_oom() -> ! { + rtabort!("memory allocation failed"); +} + #[cfg(not(test))] #[doc(hidden)] #[allow(unused_attributes)] @@ -35,10 +43,11 @@ pub mod __default_lib_allocator { System.alloc(layout) as *mut u8 } + #[cfg(stage0)] #[no_mangle] #[rustc_std_internal_symbol] pub unsafe extern fn __rdl_oom() -> ! { - System.oom() + super::oom() } #[no_mangle] diff --git a/src/libstd/collections/hash/map.rs b/src/libstd/collections/hash/map.rs index 4fe5c11beb4..a8c70489f44 100644 --- a/src/libstd/collections/hash/map.rs +++ b/src/libstd/collections/hash/map.rs @@ -11,7 +11,7 @@ use self::Entry::*; use self::VacantEntryState::*; -use alloc::{Global, Alloc, CollectionAllocErr}; +use alloc::{CollectionAllocErr, oom}; use cell::Cell; use borrow::Borrow; use cmp::max; @@ -784,7 +784,7 @@ impl HashMap pub fn reserve(&mut self, additional: usize) { match self.try_reserve(additional) { Err(CollectionAllocErr::CapacityOverflow) => panic!("capacity overflow"), - Err(CollectionAllocErr::AllocErr) => Global.oom(), + Err(CollectionAllocErr::AllocErr) => oom(), Ok(()) => { /* yay */ } } } diff --git a/src/libstd/collections/hash/table.rs b/src/libstd/collections/hash/table.rs index 115f9628a23..52c53dc3b12 100644 --- a/src/libstd/collections/hash/table.rs +++ b/src/libstd/collections/hash/table.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use alloc::{Global, Alloc, Layout, CollectionAllocErr}; +use alloc::{Global, Alloc, Layout, CollectionAllocErr, oom}; use cmp; use hash::{BuildHasher, Hash, Hasher}; use marker; @@ -770,7 +770,7 @@ impl RawTable { unsafe fn new_uninitialized(capacity: usize) -> RawTable { match Self::try_new_uninitialized(capacity) { Err(CollectionAllocErr::CapacityOverflow) => panic!("capacity overflow"), - Err(CollectionAllocErr::AllocErr) => Global.oom(), + Err(CollectionAllocErr::AllocErr) => oom(), Ok(table) => { table } } } @@ -809,7 +809,7 @@ impl RawTable { pub fn new(capacity: usize) -> RawTable { match Self::try_new(capacity) { Err(CollectionAllocErr::CapacityOverflow) => panic!("capacity overflow"), - Err(CollectionAllocErr::AllocErr) => Global.oom(), + Err(CollectionAllocErr::AllocErr) => oom(), Ok(table) => { table } } } diff --git a/src/libstd/lib.rs b/src/libstd/lib.rs index 43a8d4446fa..1df7bc777d1 100644 --- a/src/libstd/lib.rs +++ b/src/libstd/lib.rs @@ -482,7 +482,6 @@ pub mod path; pub mod process; pub mod sync; pub mod time; -pub mod alloc; #[unstable(feature = "allocator_api", issue = "32838")] #[rustc_deprecated(since = "1.27.0", reason = "module renamed to `alloc`")] @@ -496,6 +495,8 @@ pub mod heap { mod sys_common; mod sys; +pub mod alloc; + // Private support modules mod panicking; mod memchr; diff --git a/src/test/run-pass/allocator-alloc-one.rs b/src/test/run-pass/allocator-alloc-one.rs index d4fcdcf743b..12b115d0938 100644 --- a/src/test/run-pass/allocator-alloc-one.rs +++ b/src/test/run-pass/allocator-alloc-one.rs @@ -10,13 +10,11 @@ #![feature(allocator_api, nonnull)] -use std::alloc::{Alloc, Global}; +use std::alloc::{Alloc, Global, oom}; fn main() { unsafe { - let ptr = Global.alloc_one::().unwrap_or_else(|_| { - Global.oom() - }); + let ptr = Global.alloc_one::().unwrap_or_else(|_| oom()); *ptr.as_ptr() = 4; assert_eq!(*ptr.as_ptr(), 4); Global.dealloc_one(ptr); diff --git a/src/test/run-pass/realloc-16687.rs b/src/test/run-pass/realloc-16687.rs index afa3494c389..308792e5d89 100644 --- a/src/test/run-pass/realloc-16687.rs +++ b/src/test/run-pass/realloc-16687.rs @@ -15,7 +15,7 @@ #![feature(heap_api, allocator_api)] -use std::alloc::{Global, Alloc, Layout}; +use std::alloc::{Global, Alloc, Layout, oom}; use std::ptr::{self, NonNull}; fn main() { @@ -50,7 +50,7 @@ unsafe fn test_triangle() -> bool { println!("allocate({:?})", layout); } - let ret = Global.alloc(layout.clone()).unwrap_or_else(|_| Global.oom()); + let ret = Global.alloc(layout.clone()).unwrap_or_else(|_| oom()); if PRINT { println!("allocate({:?}) = {:?}", layout, ret); @@ -73,7 +73,7 @@ unsafe fn test_triangle() -> bool { } let ret = Global.realloc(NonNull::new_unchecked(ptr).as_opaque(), old.clone(), new.size()) - .unwrap_or_else(|_| Global.oom()); + .unwrap_or_else(|_| oom()); if PRINT { println!("reallocate({:?}, old={:?}, new={:?}) = {:?}", diff --git a/src/test/run-pass/regions-mock-trans.rs b/src/test/run-pass/regions-mock-trans.rs index 44be59f5c5b..60a7f70931d 100644 --- a/src/test/run-pass/regions-mock-trans.rs +++ b/src/test/run-pass/regions-mock-trans.rs @@ -12,7 +12,7 @@ #![feature(allocator_api)] -use std::alloc::{Alloc, Global, Layout}; +use std::alloc::{Alloc, Global, Layout, oom}; use std::ptr::NonNull; struct arena(()); @@ -33,7 +33,7 @@ struct Ccx { fn alloc<'a>(_bcx : &'a arena) -> &'a Bcx<'a> { unsafe { let ptr = Global.alloc(Layout::new::()) - .unwrap_or_else(|_| Global.oom()); + .unwrap_or_else(|_| oom()); &*(ptr.as_ptr() as *const _) } } -- cgit 1.4.1-3-g733a5 From 9e8f683476d5a9d72c6c1e9383a519cf0fb27494 Mon Sep 17 00:00:00 2001 From: Steven Fackler Date: Sat, 21 Apr 2018 09:47:41 -0700 Subject: Remove Alloc::oom --- src/Cargo.lock | 1 - src/liballoc/alloc.rs | 5 -- src/liballoc/heap.rs | 2 +- src/liballoc/raw_vec.rs | 14 ++--- src/liballoc_jemalloc/Cargo.toml | 1 - src/liballoc_jemalloc/lib.rs | 5 +- src/liballoc_system/lib.rs | 70 ---------------------- src/libcore/alloc.rs | 26 -------- .../compile-fail/allocator/not-an-allocator.rs | 1 - 9 files changed, 10 insertions(+), 115 deletions(-) (limited to 'src/liballoc/alloc.rs') diff --git a/src/Cargo.lock b/src/Cargo.lock index d7d23f0fa80..6f054dc61a5 100644 --- a/src/Cargo.lock +++ b/src/Cargo.lock @@ -19,7 +19,6 @@ dependencies = [ name = "alloc_jemalloc" version = "0.0.0" dependencies = [ - "alloc_system 0.0.0", "build_helper 0.1.0", "cc 1.0.10 (registry+https://github.com/rust-lang/crates.io-index)", "compiler_builtins 0.0.0", diff --git a/src/liballoc/alloc.rs b/src/liballoc/alloc.rs index a9c06523718..c0372d24ed5 100644 --- a/src/liballoc/alloc.rs +++ b/src/liballoc/alloc.rs @@ -131,11 +131,6 @@ unsafe impl Alloc for Global { unsafe fn alloc_zeroed(&mut self, layout: Layout) -> Result, AllocErr> { NonNull::new(GlobalAlloc::alloc_zeroed(self, layout)).ok_or(AllocErr) } - - #[inline] - fn oom(&mut self) -> ! { - oom() - } } /// The allocator for unique pointers. diff --git a/src/liballoc/heap.rs b/src/liballoc/heap.rs index faac38ca7ce..16f0630b911 100644 --- a/src/liballoc/heap.rs +++ b/src/liballoc/heap.rs @@ -59,7 +59,7 @@ unsafe impl Alloc for T where T: CoreAlloc { } fn oom(&mut self, _: AllocErr) -> ! { - CoreAlloc::oom(self) + unsafe { ::core::intrinsics::abort() } } fn usable_size(&self, layout: &Layout) -> (usize, usize) { diff --git a/src/liballoc/raw_vec.rs b/src/liballoc/raw_vec.rs index 24b7cd3db0c..7ef0a27fc72 100644 --- a/src/liballoc/raw_vec.rs +++ b/src/liballoc/raw_vec.rs @@ -14,7 +14,7 @@ use core::ops::Drop; use core::ptr::{self, NonNull, Unique}; use core::slice; -use alloc::{Alloc, Layout, Global}; +use alloc::{Alloc, Layout, Global, oom}; use alloc::CollectionAllocErr; use alloc::CollectionAllocErr::*; use boxed::Box; @@ -101,7 +101,7 @@ impl RawVec { }; match result { Ok(ptr) => ptr, - Err(_) => a.oom(), + Err(_) => oom(), } }; @@ -316,7 +316,7 @@ impl RawVec { new_size); match ptr_res { Ok(ptr) => (new_cap, ptr.cast().into()), - Err(_) => self.a.oom(), + Err(_) => oom(), } } None => { @@ -325,7 +325,7 @@ impl RawVec { let new_cap = if elem_size > (!0) / 8 { 1 } else { 4 }; match self.a.alloc_array::(new_cap) { Ok(ptr) => (new_cap, ptr.into()), - Err(_) => self.a.oom(), + Err(_) => oom(), } } }; @@ -442,7 +442,7 @@ impl RawVec { pub fn reserve_exact(&mut self, used_cap: usize, needed_extra_cap: usize) { match self.try_reserve_exact(used_cap, needed_extra_cap) { Err(CapacityOverflow) => capacity_overflow(), - Err(AllocErr) => self.a.oom(), + Err(AllocErr) => oom(), Ok(()) => { /* yay */ } } } @@ -552,7 +552,7 @@ impl RawVec { pub fn reserve(&mut self, used_cap: usize, needed_extra_cap: usize) { match self.try_reserve(used_cap, needed_extra_cap) { Err(CapacityOverflow) => capacity_overflow(), - Err(AllocErr) => self.a.oom(), + Err(AllocErr) => oom(), Ok(()) => { /* yay */ } } } @@ -667,7 +667,7 @@ impl RawVec { old_layout, new_size) { Ok(p) => self.ptr = p.cast().into(), - Err(_) => self.a.oom(), + Err(_) => oom(), } } self.cap = amount; diff --git a/src/liballoc_jemalloc/Cargo.toml b/src/liballoc_jemalloc/Cargo.toml index 02435170374..7986d5dd2eb 100644 --- a/src/liballoc_jemalloc/Cargo.toml +++ b/src/liballoc_jemalloc/Cargo.toml @@ -12,7 +12,6 @@ test = false doc = false [dependencies] -alloc_system = { path = "../liballoc_system" } core = { path = "../libcore" } libc = { path = "../rustc/libc_shim" } compiler_builtins = { path = "../rustc/compiler_builtins_shim" } diff --git a/src/liballoc_jemalloc/lib.rs b/src/liballoc_jemalloc/lib.rs index 8b118a2cabb..4b8755877de 100644 --- a/src/liballoc_jemalloc/lib.rs +++ b/src/liballoc_jemalloc/lib.rs @@ -14,7 +14,7 @@ reason = "this library is unlikely to be stabilized in its current \ form or name", issue = "27783")] -#![feature(alloc_system)] +#![feature(core_intrinsics)] #![feature(libc)] #![feature(linkage)] #![feature(staged_api)] @@ -23,7 +23,6 @@ #![cfg_attr(not(dummy_jemalloc), feature(allocator_api))] #![rustc_alloc_kind = "exe"] -extern crate alloc_system; extern crate libc; #[cfg(not(dummy_jemalloc))] @@ -102,7 +101,7 @@ mod contents { #[no_mangle] #[rustc_std_internal_symbol] pub unsafe extern fn __rde_oom() -> ! { - ::alloc_system::oom() + ::core::intrinsics::abort(); } #[no_mangle] diff --git a/src/liballoc_system/lib.rs b/src/liballoc_system/lib.rs index aff98ae2f10..7376ac0f15d 100644 --- a/src/liballoc_system/lib.rs +++ b/src/liballoc_system/lib.rs @@ -71,11 +71,6 @@ unsafe impl Alloc for System { new_size: usize) -> Result, AllocErr> { NonNull::new(GlobalAlloc::realloc(self, ptr.as_ptr(), layout, new_size)).ok_or(AllocErr) } - - #[inline] - fn oom(&mut self) -> ! { - ::oom() - } } #[cfg(stage0)] @@ -103,11 +98,6 @@ unsafe impl<'a> Alloc for &'a System { new_size: usize) -> Result, AllocErr> { NonNull::new(GlobalAlloc::realloc(*self, ptr.as_ptr(), layout, new_size)).ok_or(AllocErr) } - - #[inline] - fn oom(&mut self) -> ! { - ::oom() - } } #[cfg(any(windows, unix, target_os = "cloudabi", target_os = "redox"))] @@ -366,63 +356,3 @@ mod platform { } } } - -#[inline] -pub fn oom() -> ! { - write_to_stderr("fatal runtime error: memory allocation failed"); - unsafe { - ::core::intrinsics::abort(); - } -} - -#[cfg(any(unix, target_os = "redox"))] -#[inline] -fn write_to_stderr(s: &str) { - extern crate libc; - - unsafe { - libc::write(libc::STDERR_FILENO, - s.as_ptr() as *const libc::c_void, - s.len()); - } -} - -#[cfg(windows)] -#[inline] -fn write_to_stderr(s: &str) { - use core::ptr; - - type LPVOID = *mut u8; - type HANDLE = LPVOID; - type DWORD = u32; - type BOOL = i32; - type LPDWORD = *mut DWORD; - type LPOVERLAPPED = *mut u8; - - const STD_ERROR_HANDLE: DWORD = -12i32 as DWORD; - - extern "system" { - fn WriteFile(hFile: HANDLE, - lpBuffer: LPVOID, - nNumberOfBytesToWrite: DWORD, - lpNumberOfBytesWritten: LPDWORD, - lpOverlapped: LPOVERLAPPED) - -> BOOL; - fn GetStdHandle(which: DWORD) -> HANDLE; - } - - unsafe { - // WriteFile silently fails if it is passed an invalid - // handle, so there is no need to check the result of - // GetStdHandle. - WriteFile(GetStdHandle(STD_ERROR_HANDLE), - s.as_ptr() as LPVOID, - s.len() as DWORD, - ptr::null_mut(), - ptr::null_mut()); - } -} - -#[cfg(not(any(windows, unix, target_os = "redox")))] -#[inline] -fn write_to_stderr(_: &str) {} diff --git a/src/libcore/alloc.rs b/src/libcore/alloc.rs index 7d893676a6c..674c4fb57c7 100644 --- a/src/libcore/alloc.rs +++ b/src/libcore/alloc.rs @@ -603,32 +603,6 @@ pub unsafe trait Alloc { /// to allocate that block of memory. unsafe fn dealloc(&mut self, ptr: NonNull, layout: Layout); - /// Allocator-specific method for signaling an out-of-memory - /// condition. - /// - /// `oom` aborts the thread or process, optionally performing - /// cleanup or logging diagnostic information before panicking or - /// aborting. - /// - /// `oom` is meant to be used by clients unable to cope with an - /// unsatisfied allocation request, and wish to abandon - /// computation rather than attempt to recover locally. - /// - /// Implementations of the `oom` method are discouraged from - /// infinitely regressing in nested calls to `oom`. In - /// practice this means implementors should eschew allocating, - /// especially from `self` (directly or indirectly). - /// - /// Implementations of the allocation and reallocation methods - /// (e.g. `alloc`, `alloc_one`, `realloc`) are discouraged from - /// panicking (or aborting) in the event of memory exhaustion; - /// instead they should return an appropriate error from the - /// invoked method, and let the client decide whether to invoke - /// this `oom` method in response. - fn oom(&mut self) -> ! { - unsafe { ::intrinsics::abort() } - } - // == ALLOCATOR-SPECIFIC QUANTITIES AND LIMITS == // usable_size diff --git a/src/test/compile-fail/allocator/not-an-allocator.rs b/src/test/compile-fail/allocator/not-an-allocator.rs index 1479d0b6264..140cad22f34 100644 --- a/src/test/compile-fail/allocator/not-an-allocator.rs +++ b/src/test/compile-fail/allocator/not-an-allocator.rs @@ -16,6 +16,5 @@ static A: usize = 0; //~| the trait bound `usize: //~| the trait bound `usize: //~| the trait bound `usize: -//~| the trait bound `usize: fn main() {} -- cgit 1.4.1-3-g733a5 From bd8c177d49c95d94f163e9bb3c3397f38ab72640 Mon Sep 17 00:00:00 2001 From: Mike Hommey Date: Fri, 20 Apr 2018 10:24:53 +0900 Subject: Switch box_free to take the destructured contents of Box As of now, Box only contains a Unique pointer, so this is the sole argument to box_free. Consequently, we remove the code supporting the previous box_free signature. We however keep the old definition for bootstrapping purpose. --- src/liballoc/alloc.rs | 14 ++++++-- src/liballoc/arc.rs | 5 +-- src/liballoc/rc.rs | 5 +-- src/librustc_mir/util/elaborate_drops.rs | 61 +++++++------------------------- 4 files changed, 29 insertions(+), 56 deletions(-) (limited to 'src/liballoc/alloc.rs') diff --git a/src/liballoc/alloc.rs b/src/liballoc/alloc.rs index 68a617e0ffe..ed860678765 100644 --- a/src/liballoc/alloc.rs +++ b/src/liballoc/alloc.rs @@ -16,7 +16,7 @@ issue = "32838")] use core::intrinsics::{min_align_of_val, size_of_val}; -use core::ptr::NonNull; +use core::ptr::{NonNull, Unique}; use core::usize; #[doc(inline)] @@ -170,9 +170,17 @@ unsafe fn exchange_malloc(size: usize, align: usize) -> *mut u8 { } } -#[cfg_attr(not(test), lang = "box_free")] +#[cfg(stage0)] +#[lang = "box_free"] +#[inline] +unsafe fn old_box_free(ptr: *mut T) { + box_free(Unique::new_unchecked(ptr)) +} + +#[cfg_attr(not(any(test, stage0)), lang = "box_free")] #[inline] -pub(crate) unsafe fn box_free(ptr: *mut T) { +pub(crate) unsafe fn box_free(ptr: Unique) { + let ptr = ptr.as_ptr(); let size = size_of_val(&*ptr); let align = min_align_of_val(&*ptr); // We do not allocate for Box when T is ZST, so deallocation is also not necessary. diff --git a/src/liballoc/arc.rs b/src/liballoc/arc.rs index 225b055d8ee..a1ec5cd2208 100644 --- a/src/liballoc/arc.rs +++ b/src/liballoc/arc.rs @@ -566,7 +566,8 @@ impl Arc { fn from_box(v: Box) -> Arc { unsafe { - let bptr = Box::into_raw(v); + let box_unique = Box::into_unique(v); + let bptr = box_unique.as_ptr(); let value_size = size_of_val(&*bptr); let ptr = Self::allocate_for_ptr(bptr); @@ -578,7 +579,7 @@ impl Arc { value_size); // Free the allocation without dropping its contents - box_free(bptr); + box_free(box_unique); Arc { ptr: NonNull::new_unchecked(ptr), phantom: PhantomData } } diff --git a/src/liballoc/rc.rs b/src/liballoc/rc.rs index de0422d82bb..c495d300805 100644 --- a/src/liballoc/rc.rs +++ b/src/liballoc/rc.rs @@ -681,7 +681,8 @@ impl Rc { fn from_box(v: Box) -> Rc { unsafe { - let bptr = Box::into_raw(v); + let box_unique = Box::into_unique(v); + let bptr = box_unique.as_ptr(); let value_size = size_of_val(&*bptr); let ptr = Self::allocate_for_ptr(bptr); @@ -693,7 +694,7 @@ impl Rc { value_size); // Free the allocation without dropping its contents - box_free(bptr); + box_free(box_unique); Rc { ptr: NonNull::new_unchecked(ptr), phantom: PhantomData } } diff --git a/src/librustc_mir/util/elaborate_drops.rs b/src/librustc_mir/util/elaborate_drops.rs index cc91bbf9061..02020c3b7a4 100644 --- a/src/librustc_mir/util/elaborate_drops.rs +++ b/src/librustc_mir/util/elaborate_drops.rs @@ -879,56 +879,19 @@ impl<'l, 'b, 'tcx, D> DropCtxt<'l, 'b, 'tcx, D> let tcx = self.tcx(); let unit_temp = Place::Local(self.new_temp(tcx.mk_nil())); let free_func = tcx.require_lang_item(lang_items::BoxFreeFnLangItem); - let free_sig = tcx.fn_sig(free_func).skip_binder().clone(); - let free_inputs = free_sig.inputs(); - // If the box_free function takes a *mut T, transform the Box into - // such a pointer before calling box_free. Otherwise, pass it all - // the fields in the Box as individual arguments. - let (stmts, args) = if free_inputs.len() == 1 && free_inputs[0].is_mutable_pointer() { - let ty = substs.type_at(0); - let ref_ty = tcx.mk_ref(tcx.types.re_erased, ty::TypeAndMut { - ty: ty, - mutbl: hir::Mutability::MutMutable - }); - let ptr_ty = tcx.mk_mut_ptr(ty); - let ref_tmp = Place::Local(self.new_temp(ref_ty)); - let ptr_tmp = Place::Local(self.new_temp(ptr_ty)); - let stmts = vec![ - self.assign(&ref_tmp, Rvalue::Ref( - tcx.types.re_erased, - BorrowKind::Mut { allow_two_phase_borrow: false }, - self.place.clone().deref() - )), - self.assign(&ptr_tmp, Rvalue::Cast( - CastKind::Misc, - Operand::Move(ref_tmp), - ptr_ty, - )), - ]; - (stmts, vec![Operand::Move(ptr_tmp)]) - } else { - let args = adt.variants[0].fields.iter().enumerate().map(|(i, f)| { - let field = Field::new(i); - let field_ty = f.ty(self.tcx(), substs); - Operand::Move(self.place.clone().field(field, field_ty)) - }).collect(); - (vec![], args) - }; + let args = adt.variants[0].fields.iter().enumerate().map(|(i, f)| { + let field = Field::new(i); + let field_ty = f.ty(self.tcx(), substs); + Operand::Move(self.place.clone().field(field, field_ty)) + }).collect(); - let free_block = BasicBlockData { - statements: stmts, - terminator: Some(Terminator { - kind: TerminatorKind::Call { - func: Operand::function_handle(tcx, free_func, substs, self.source_info.span), - args: args, - destination: Some((unit_temp, target)), - cleanup: None - }, // FIXME(#43234) - source_info: self.source_info, - }), - is_cleanup: unwind.is_cleanup() - }; - let free_block = self.elaborator.patch().new_block(free_block); + let call = TerminatorKind::Call { + func: Operand::function_handle(tcx, free_func, substs, self.source_info.span), + args: args, + destination: Some((unit_temp, target)), + cleanup: None + }; // FIXME(#43234) + let free_block = self.new_block(unwind, call); let block_start = Location { block: free_block, statement_index: 0 }; self.elaborator.clear_drop_flag(block_start, self.path, DropFlagMode::Shallow); -- cgit 1.4.1-3-g733a5 From b63d7e2b1c4019e40051036bcb1fd5f254a8f6e2 Mon Sep 17 00:00:00 2001 From: Irina Popa Date: Tue, 8 May 2018 16:10:16 +0300 Subject: Rename trans to codegen everywhere. --- src/Cargo.lock | 94 +- src/Cargo.toml | 2 +- src/bootstrap/builder.rs | 2 +- src/bootstrap/check.rs | 10 +- src/bootstrap/compile.rs | 16 +- src/doc/rustc-ux-guidelines.md | 2 +- src/liballoc/alloc.rs | 2 +- src/libcore/intrinsics.rs | 2 +- src/librustc/Cargo.toml | 6 +- src/librustc/cfg/construct.rs | 6 +- src/librustc/dep_graph/dep_node.rs | 6 +- src/librustc/dep_graph/graph.rs | 4 +- src/librustc/hir/check_attr.rs | 2 +- src/librustc/hir/mod.rs | 17 +- src/librustc/ich/impls_hir.rs | 6 +- src/librustc/ich/mod.rs | 6 +- src/librustc/infer/anon_types/mod.rs | 2 +- src/librustc/lint/context.rs | 8 +- src/librustc/lint/mod.rs | 6 +- src/librustc/middle/dependency_format.rs | 2 +- src/librustc/middle/mem_categorization.rs | 4 +- src/librustc/middle/reachable.rs | 18 +- src/librustc/middle/region.rs | 2 +- src/librustc/mir/interpret/mod.rs | 2 +- src/librustc/mir/interpret/value.rs | 2 +- src/librustc/mir/mod.rs | 4 +- src/librustc/mir/mono.rs | 9 +- src/librustc/session/config.rs | 32 +- src/librustc/session/mod.rs | 12 +- src/librustc/traits/codegen/mod.rs | 184 ++ src/librustc/traits/mod.rs | 10 +- src/librustc/traits/project.rs | 6 +- src/librustc/traits/query/normalize.rs | 2 +- .../traits/query/normalize_erasing_regions.rs | 2 +- src/librustc/traits/structural_impls.rs | 2 +- src/librustc/traits/trans/mod.rs | 184 -- src/librustc/ty/adjustment.rs | 4 +- src/librustc/ty/cast.rs | 2 +- src/librustc/ty/context.rs | 4 +- src/librustc/ty/erase_regions.rs | 3 +- src/librustc/ty/fold.rs | 2 +- src/librustc/ty/instance.rs | 16 +- src/librustc/ty/layout.rs | 6 +- src/librustc/ty/maps/config.rs | 8 +- src/librustc/ty/maps/mod.rs | 22 +- src/librustc/ty/maps/on_disk_cache.rs | 4 +- src/librustc/ty/maps/plumbing.rs | 12 +- src/librustc/ty/mod.rs | 8 +- src/librustc/ty/sty.rs | 6 +- src/librustc/util/ppaux.rs | 4 +- src/librustc_borrowck/borrowck/README.md | 2 +- src/librustc_codegen_llvm/Cargo.toml | 49 + src/librustc_codegen_llvm/README.md | 7 + src/librustc_codegen_llvm/abi.rs | 696 ++++++ src/librustc_codegen_llvm/allocator.rs | 103 + src/librustc_codegen_llvm/asm.rs | 127 ++ src/librustc_codegen_llvm/attributes.rs | 259 +++ src/librustc_codegen_llvm/back/archive.rs | 325 +++ src/librustc_codegen_llvm/back/bytecode.rs | 160 ++ src/librustc_codegen_llvm/back/command.rs | 175 ++ src/librustc_codegen_llvm/back/link.rs | 1630 +++++++++++++ src/librustc_codegen_llvm/back/linker.rs | 1037 +++++++++ src/librustc_codegen_llvm/back/lto.rs | 773 +++++++ src/librustc_codegen_llvm/back/rpath.rs | 282 +++ src/librustc_codegen_llvm/back/symbol_export.rs | 396 ++++ src/librustc_codegen_llvm/back/wasm.rs | 261 +++ src/librustc_codegen_llvm/back/write.rs | 2390 +++++++++++++++++++ src/librustc_codegen_llvm/base.rs | 1411 ++++++++++++ src/librustc_codegen_llvm/build.rs | 16 + src/librustc_codegen_llvm/builder.rs | 1425 ++++++++++++ src/librustc_codegen_llvm/callee.rs | 232 ++ src/librustc_codegen_llvm/common.rs | 448 ++++ src/librustc_codegen_llvm/consts.rs | 322 +++ src/librustc_codegen_llvm/context.rs | 666 ++++++ .../debuginfo/create_scope_map.rs | 136 ++ src/librustc_codegen_llvm/debuginfo/doc.rs | 189 ++ src/librustc_codegen_llvm/debuginfo/gdb.rs | 88 + src/librustc_codegen_llvm/debuginfo/metadata.rs | 1773 +++++++++++++++ src/librustc_codegen_llvm/debuginfo/mod.rs | 542 +++++ src/librustc_codegen_llvm/debuginfo/namespace.rs | 66 + src/librustc_codegen_llvm/debuginfo/source_loc.rs | 107 + src/librustc_codegen_llvm/debuginfo/type_names.rs | 224 ++ src/librustc_codegen_llvm/debuginfo/utils.rs | 65 + src/librustc_codegen_llvm/declare.rs | 223 ++ src/librustc_codegen_llvm/diagnostics.rs | 55 + src/librustc_codegen_llvm/glue.rs | 122 + src/librustc_codegen_llvm/intrinsic.rs | 1465 ++++++++++++ src/librustc_codegen_llvm/lib.rs | 392 ++++ src/librustc_codegen_llvm/llvm_util.rs | 255 +++ src/librustc_codegen_llvm/metadata.rs | 124 + src/librustc_codegen_llvm/meth.rs | 115 + src/librustc_codegen_llvm/mir/analyze.rs | 361 +++ src/librustc_codegen_llvm/mir/block.rs | 895 ++++++++ src/librustc_codegen_llvm/mir/constant.rs | 271 +++ src/librustc_codegen_llvm/mir/mod.rs | 652 ++++++ src/librustc_codegen_llvm/mir/operand.rs | 427 ++++ src/librustc_codegen_llvm/mir/place.rs | 498 ++++ src/librustc_codegen_llvm/mir/rvalue.rs | 952 ++++++++ src/librustc_codegen_llvm/mir/statement.rs | 91 + src/librustc_codegen_llvm/mono_item.rs | 193 ++ src/librustc_codegen_llvm/time_graph.rs | 278 +++ src/librustc_codegen_llvm/type_.rs | 300 +++ src/librustc_codegen_llvm/type_of.rs | 506 +++++ src/librustc_codegen_llvm/value.rs | 24 + src/librustc_codegen_utils/Cargo.toml | 23 + src/librustc_codegen_utils/codegen_backend.rs | 296 +++ src/librustc_codegen_utils/lib.rs | 63 + src/librustc_codegen_utils/link.rs | 191 ++ src/librustc_codegen_utils/symbol_names.rs | 435 ++++ src/librustc_codegen_utils/symbol_names_test.rs | 79 + src/librustc_driver/Cargo.toml | 2 +- src/librustc_driver/driver.rs | 42 +- src/librustc_driver/lib.rs | 74 +- src/librustc_driver/pretty.rs | 8 +- src/librustc_driver/test.rs | 2 +- src/librustc_incremental/assert_dep_graph.rs | 6 +- src/librustc_incremental/assert_module_sources.rs | 18 +- src/librustc_incremental/persist/dirty_clean.rs | 4 +- src/librustc_lint/types.rs | 2 +- src/librustc_llvm/ffi.rs | 2 +- src/librustc_llvm/lib.rs | 2 +- src/librustc_metadata/diagnostics.rs | 6 +- src/librustc_metadata/encoder.rs | 9 +- src/librustc_mir/build/expr/as_rvalue.rs | 2 +- src/librustc_mir/build/expr/mod.rs | 4 +- src/librustc_mir/build/matches/mod.rs | 4 +- src/librustc_mir/build/mod.rs | 2 +- src/librustc_mir/build/scope.rs | 6 +- src/librustc_mir/hair/cx/block.rs | 2 +- src/librustc_mir/hair/cx/mod.rs | 2 +- src/librustc_mir/hair/mod.rs | 8 +- src/librustc_mir/monomorphize/collector.rs | 14 +- src/librustc_mir/monomorphize/item.rs | 18 +- src/librustc_mir/monomorphize/mod.rs | 20 +- src/librustc_mir/monomorphize/partitioning.rs | 120 +- src/librustc_mir/shim.rs | 4 +- src/librustc_mir/transform/add_call_guards.rs | 2 +- src/librustc_mir/transform/elaborate_drops.rs | 2 +- src/librustc_mir/transform/erase_regions.rs | 2 +- src/librustc_mir/transform/inline.rs | 16 +- src/librustc_mir/transform/mod.rs | 2 +- src/librustc_mir/transform/simplify.rs | 2 +- src/librustc_trans/Cargo.toml | 49 - src/librustc_trans/README.md | 7 - src/librustc_trans/abi.rs | 696 ------ src/librustc_trans/allocator.rs | 103 - src/librustc_trans/asm.rs | 129 -- src/librustc_trans/attributes.rs | 259 --- src/librustc_trans/back/archive.rs | 325 --- src/librustc_trans/back/bytecode.rs | 160 -- src/librustc_trans/back/command.rs | 175 -- src/librustc_trans/back/link.rs | 1626 ------------- src/librustc_trans/back/linker.rs | 1037 --------- src/librustc_trans/back/lto.rs | 773 ------- src/librustc_trans/back/rpath.rs | 282 --- src/librustc_trans/back/symbol_export.rs | 395 ---- src/librustc_trans/back/wasm.rs | 261 --- src/librustc_trans/back/write.rs | 2392 -------------------- src/librustc_trans/base.rs | 1411 ------------ src/librustc_trans/build.rs | 16 - src/librustc_trans/builder.rs | 1425 ------------ src/librustc_trans/callee.rs | 232 -- src/librustc_trans/common.rs | 449 ---- src/librustc_trans/consts.rs | 322 --- src/librustc_trans/context.rs | 666 ------ src/librustc_trans/debuginfo/create_scope_map.rs | 136 -- src/librustc_trans/debuginfo/doc.rs | 189 -- src/librustc_trans/debuginfo/gdb.rs | 88 - src/librustc_trans/debuginfo/metadata.rs | 1773 --------------- src/librustc_trans/debuginfo/mod.rs | 542 ----- src/librustc_trans/debuginfo/namespace.rs | 66 - src/librustc_trans/debuginfo/source_loc.rs | 107 - src/librustc_trans/debuginfo/type_names.rs | 224 -- src/librustc_trans/debuginfo/utils.rs | 65 - src/librustc_trans/declare.rs | 223 -- src/librustc_trans/diagnostics.rs | 55 - src/librustc_trans/glue.rs | 122 - src/librustc_trans/intrinsic.rs | 1465 ------------ src/librustc_trans/lib.rs | 390 ---- src/librustc_trans/llvm_util.rs | 255 --- src/librustc_trans/metadata.rs | 124 - src/librustc_trans/meth.rs | 115 - src/librustc_trans/mir/analyze.rs | 361 --- src/librustc_trans/mir/block.rs | 895 -------- src/librustc_trans/mir/constant.rs | 271 --- src/librustc_trans/mir/mod.rs | 652 ------ src/librustc_trans/mir/operand.rs | 427 ---- src/librustc_trans/mir/place.rs | 499 ---- src/librustc_trans/mir/rvalue.rs | 952 -------- src/librustc_trans/mir/statement.rs | 91 - src/librustc_trans/time_graph.rs | 278 --- src/librustc_trans/trans_item.rs | 193 -- src/librustc_trans/type_.rs | 300 --- src/librustc_trans/type_of.rs | 506 ----- src/librustc_trans/value.rs | 24 - src/librustc_trans_utils/Cargo.toml | 23 - src/librustc_trans_utils/lib.rs | 63 - src/librustc_trans_utils/link.rs | 191 -- src/librustc_trans_utils/symbol_names.rs | 435 ---- src/librustc_trans_utils/symbol_names_test.rs | 79 - src/librustc_trans_utils/trans_crate.rs | 296 --- src/librustc_typeck/check/intrinsic.rs | 2 +- src/librustc_typeck/check/op.rs | 2 +- src/librustc_typeck/coherence/builtin.rs | 2 +- src/librustc_typeck/collect.rs | 38 +- src/librustc_typeck/diagnostics.rs | 4 +- src/librustdoc/core.rs | 10 +- src/librustdoc/lib.rs | 2 +- src/librustdoc/test.rs | 16 +- src/libsyntax/feature_gate.rs | 4 +- src/libsyntax_ext/format.rs | 28 +- src/libsyntax_ext/format_foreign.rs | 4 +- src/libsyntax_ext/global_asm.rs | 2 +- src/rustllvm/PassWrapper.cpp | 2 +- .../item-collection/cross-crate-closures.rs | 18 +- .../cross-crate-generic-functions.rs | 12 +- .../item-collection/cross-crate-trait-method.rs | 24 +- .../item-collection/drop_in_place_intrinsic.rs | 14 +- .../item-collection/function-as-argument.rs | 24 +- .../item-collection/generic-drop-glue.rs | 26 +- .../item-collection/generic-functions.rs | 30 +- .../codegen-units/item-collection/generic-impl.rs | 38 +- .../impl-in-non-instantiated-generic.rs | 6 +- .../instantiation-through-vtable.rs | 16 +- .../item-collection/items-within-generic-items.rs | 14 +- .../item-collection/non-generic-closures.rs | 26 +- .../item-collection/non-generic-drop-glue.rs | 12 +- .../item-collection/non-generic-functions.rs | 26 +- .../item-collection/overloaded-operators.rs | 14 +- .../codegen-units/item-collection/static-init.rs | 9 +- .../item-collection/statics-and-consts.rs | 14 +- .../item-collection/trait-implementations.rs | 24 +- .../item-collection/trait-method-as-argument.rs | 32 +- .../item-collection/trait-method-default-impl.rs | 20 +- .../item-collection/transitive-drop-glue.rs | 28 +- .../item-collection/tuple-drop-glue.rs | 14 +- .../item-collection/unreferenced-const-fn.rs | 4 +- .../unreferenced-inline-function.rs | 5 +- src/test/codegen-units/item-collection/unsizing.rs | 20 +- .../item-collection/unused-traits-and-generics.rs | 4 +- .../codegen-units/partitioning/extern-drop-glue.rs | 12 +- .../codegen-units/partitioning/extern-generic.rs | 16 +- .../partitioning/inlining-from-extern-crate.rs | 18 +- .../codegen-units/partitioning/local-drop-glue.rs | 16 +- .../codegen-units/partitioning/local-generic.rs | 18 +- .../partitioning/local-inlining-but-not-all.rs | 10 +- .../codegen-units/partitioning/local-inlining.rs | 10 +- .../partitioning/local-transitive-inlining.rs | 10 +- .../partitioning/methods-are-with-self-type.rs | 22 +- .../codegen-units/partitioning/regular-modules.rs | 44 +- .../codegen-units/partitioning/shared-generics.rs | 8 +- src/test/codegen-units/partitioning/statics.rs | 22 +- .../partitioning/vtable-through-const.rs | 20 +- src/test/compile-fail/const-err.rs | 2 +- src/test/compile-fail/const-eval-overflow2.rs | 2 +- src/test/compile-fail/const-eval-overflow2b.rs | 2 +- src/test/compile-fail/const-eval-overflow2c.rs | 2 +- .../compile-fail/dep-graph-assoc-type-codegen.rs | 47 + .../compile-fail/dep-graph-assoc-type-trans.rs | 47 - src/test/debuginfo/struct-with-destructor.rs | 2 +- .../debuginfo/var-captured-in-sendable-closure.rs | 2 +- src/test/incremental/cache_file_headers.rs | 4 +- .../incremental/change_add_field/struct_point.rs | 16 +- .../incremental/change_private_fn/struct_point.rs | 2 +- .../change_private_impl_method/struct_point.rs | 2 +- .../struct_point.rs | 2 +- .../change_pub_inherent_method_sig/struct_point.rs | 6 +- .../incremental/change_symbol_export_status.rs | 2 +- src/test/incremental/commandline-args.rs | 2 +- src/test/incremental/inlined_hir_34991/main.rs | 2 +- src/test/incremental/issue-38222.rs | 2 +- src/test/incremental/issue-49595/issue_49595.rs | 4 +- src/test/incremental/remapped_paths_cc/main.rs | 2 +- src/test/incremental/spike-neg1.rs | 2 +- src/test/incremental/spike-neg2.rs | 8 +- src/test/incremental/spike.rs | 2 +- .../run-fail/mir_codegen_calls_converging_drops.rs | 34 + .../mir_codegen_calls_converging_drops_2.rs | 38 + src/test/run-fail/mir_codegen_calls_diverging.rs | 23 + .../run-fail/mir_codegen_calls_diverging_drops.rs | 32 + src/test/run-fail/mir_codegen_no_landing_pads.rs | 37 + .../mir_codegen_no_landing_pads_diverging.rs | 37 + .../run-fail/mir_trans_calls_converging_drops.rs | 34 - .../run-fail/mir_trans_calls_converging_drops_2.rs | 38 - src/test/run-fail/mir_trans_calls_diverging.rs | 23 - .../run-fail/mir_trans_calls_diverging_drops.rs | 32 - src/test/run-fail/mir_trans_no_landing_pads.rs | 37 - .../mir_trans_no_landing_pads_diverging.rs | 37 - src/test/run-fail/rhs-type.rs | 2 +- .../hotplug_codegen_backend/the_backend.rs | 26 +- src/test/run-make-fulldeps/issue-19371/foo.rs | 16 +- src/test/run-make-fulldeps/issue-7349/Makefile | 2 +- src/test/run-pass-fulldeps/compiler-calls.rs | 6 +- .../associated-types-region-erasure-issue-20582.rs | 2 +- src/test/run-pass/atomic-compare_exchange.rs | 2 +- src/test/run-pass/codegen-object-shim.rs | 14 + src/test/run-pass/codegen-tag-static-padding.rs | 67 + src/test/run-pass/compiletest-skip-codegen.rs | 17 + src/test/run-pass/compiletest-skip-trans.rs | 17 - src/test/run-pass/conditional-compile.rs | 2 +- src/test/run-pass/issue-18425.rs | 2 +- src/test/run-pass/issue-18514.rs | 2 +- src/test/run-pass/issue-18652.rs | 2 +- src/test/run-pass/issue-18661.rs | 2 +- src/test/run-pass/issue-20644.rs | 2 +- src/test/run-pass/issue-24085.rs | 2 +- src/test/run-pass/issue-34569.rs | 2 +- src/test/run-pass/issue-36381.rs | 2 +- src/test/run-pass/issue-38002.rs | 2 +- src/test/run-pass/issue-5243.rs | 2 +- .../run-pass/issue24687-embed-debuginfo/main.rs | 2 +- .../method-two-trait-defer-resolution-2.rs | 2 +- src/test/run-pass/mir_codegen_array.rs | 19 + src/test/run-pass/mir_codegen_array_2.rs | 18 + src/test/run-pass/mir_codegen_call_converging.rs | 26 + src/test/run-pass/mir_codegen_calls.rs | 200 ++ src/test/run-pass/mir_codegen_calls_variadic.rs | 31 + src/test/run-pass/mir_codegen_critical_edge.rs | 52 + src/test/run-pass/mir_codegen_spike1.rs | 21 + src/test/run-pass/mir_codegen_switch.rs | 44 + src/test/run-pass/mir_codegen_switchint.rs | 21 + src/test/run-pass/mir_overflow_off.rs | 2 +- src/test/run-pass/mir_trans_array.rs | 19 - src/test/run-pass/mir_trans_array_2.rs | 18 - src/test/run-pass/mir_trans_call_converging.rs | 26 - src/test/run-pass/mir_trans_calls.rs | 200 -- src/test/run-pass/mir_trans_calls_variadic.rs | 31 - src/test/run-pass/mir_trans_critical_edge.rs | 52 - src/test/run-pass/mir_trans_spike1.rs | 21 - src/test/run-pass/mir_trans_switch.rs | 44 - src/test/run-pass/mir_trans_switchint.rs | 21 - src/test/run-pass/pattern-bound-var-in-for-each.rs | 4 +- src/test/run-pass/regions-mock-codegen.rs | 62 + src/test/run-pass/regions-mock-trans.rs | 62 - src/test/run-pass/sepcomp-fns-backwards.rs | 2 +- src/test/run-pass/small-enum-range-edge.rs | 2 +- src/test/run-pass/trans-object-shim.rs | 14 - src/test/run-pass/trans-tag-static-padding.rs | 67 - src/test/run-pass/union/union-const-codegen.rs | 25 + src/test/run-pass/union/union-const-trans.rs | 25 - src/test/run-pass/zero-sized-tuple-struct.rs | 2 +- src/tools/compiletest/src/header.rs | 12 +- src/tools/compiletest/src/runtest.rs | 24 +- src/tools/tidy/src/deps.rs | 2 +- 344 files changed, 27633 insertions(+), 27634 deletions(-) create mode 100644 src/librustc/traits/codegen/mod.rs delete mode 100644 src/librustc/traits/trans/mod.rs create mode 100644 src/librustc_codegen_llvm/Cargo.toml create mode 100644 src/librustc_codegen_llvm/README.md create mode 100644 src/librustc_codegen_llvm/abi.rs create mode 100644 src/librustc_codegen_llvm/allocator.rs create mode 100644 src/librustc_codegen_llvm/asm.rs create mode 100644 src/librustc_codegen_llvm/attributes.rs create mode 100644 src/librustc_codegen_llvm/back/archive.rs create mode 100644 src/librustc_codegen_llvm/back/bytecode.rs create mode 100644 src/librustc_codegen_llvm/back/command.rs create mode 100644 src/librustc_codegen_llvm/back/link.rs create mode 100644 src/librustc_codegen_llvm/back/linker.rs create mode 100644 src/librustc_codegen_llvm/back/lto.rs create mode 100644 src/librustc_codegen_llvm/back/rpath.rs create mode 100644 src/librustc_codegen_llvm/back/symbol_export.rs create mode 100644 src/librustc_codegen_llvm/back/wasm.rs create mode 100644 src/librustc_codegen_llvm/back/write.rs create mode 100644 src/librustc_codegen_llvm/base.rs create mode 100644 src/librustc_codegen_llvm/build.rs create mode 100644 src/librustc_codegen_llvm/builder.rs create mode 100644 src/librustc_codegen_llvm/callee.rs create mode 100644 src/librustc_codegen_llvm/common.rs create mode 100644 src/librustc_codegen_llvm/consts.rs create mode 100644 src/librustc_codegen_llvm/context.rs create mode 100644 src/librustc_codegen_llvm/debuginfo/create_scope_map.rs create mode 100644 src/librustc_codegen_llvm/debuginfo/doc.rs create mode 100644 src/librustc_codegen_llvm/debuginfo/gdb.rs create mode 100644 src/librustc_codegen_llvm/debuginfo/metadata.rs create mode 100644 src/librustc_codegen_llvm/debuginfo/mod.rs create mode 100644 src/librustc_codegen_llvm/debuginfo/namespace.rs create mode 100644 src/librustc_codegen_llvm/debuginfo/source_loc.rs create mode 100644 src/librustc_codegen_llvm/debuginfo/type_names.rs create mode 100644 src/librustc_codegen_llvm/debuginfo/utils.rs create mode 100644 src/librustc_codegen_llvm/declare.rs create mode 100644 src/librustc_codegen_llvm/diagnostics.rs create mode 100644 src/librustc_codegen_llvm/glue.rs create mode 100644 src/librustc_codegen_llvm/intrinsic.rs create mode 100644 src/librustc_codegen_llvm/lib.rs create mode 100644 src/librustc_codegen_llvm/llvm_util.rs create mode 100644 src/librustc_codegen_llvm/metadata.rs create mode 100644 src/librustc_codegen_llvm/meth.rs create mode 100644 src/librustc_codegen_llvm/mir/analyze.rs create mode 100644 src/librustc_codegen_llvm/mir/block.rs create mode 100644 src/librustc_codegen_llvm/mir/constant.rs create mode 100644 src/librustc_codegen_llvm/mir/mod.rs create mode 100644 src/librustc_codegen_llvm/mir/operand.rs create mode 100644 src/librustc_codegen_llvm/mir/place.rs create mode 100644 src/librustc_codegen_llvm/mir/rvalue.rs create mode 100644 src/librustc_codegen_llvm/mir/statement.rs create mode 100644 src/librustc_codegen_llvm/mono_item.rs create mode 100644 src/librustc_codegen_llvm/time_graph.rs create mode 100644 src/librustc_codegen_llvm/type_.rs create mode 100644 src/librustc_codegen_llvm/type_of.rs create mode 100644 src/librustc_codegen_llvm/value.rs create mode 100644 src/librustc_codegen_utils/Cargo.toml create mode 100644 src/librustc_codegen_utils/codegen_backend.rs create mode 100644 src/librustc_codegen_utils/lib.rs create mode 100644 src/librustc_codegen_utils/link.rs create mode 100644 src/librustc_codegen_utils/symbol_names.rs create mode 100644 src/librustc_codegen_utils/symbol_names_test.rs delete mode 100644 src/librustc_trans/Cargo.toml delete mode 100644 src/librustc_trans/README.md delete mode 100644 src/librustc_trans/abi.rs delete mode 100644 src/librustc_trans/allocator.rs delete mode 100644 src/librustc_trans/asm.rs delete mode 100644 src/librustc_trans/attributes.rs delete mode 100644 src/librustc_trans/back/archive.rs delete mode 100644 src/librustc_trans/back/bytecode.rs delete mode 100644 src/librustc_trans/back/command.rs delete mode 100644 src/librustc_trans/back/link.rs delete mode 100644 src/librustc_trans/back/linker.rs delete mode 100644 src/librustc_trans/back/lto.rs delete mode 100644 src/librustc_trans/back/rpath.rs delete mode 100644 src/librustc_trans/back/symbol_export.rs delete mode 100644 src/librustc_trans/back/wasm.rs delete mode 100644 src/librustc_trans/back/write.rs delete mode 100644 src/librustc_trans/base.rs delete mode 100644 src/librustc_trans/build.rs delete mode 100644 src/librustc_trans/builder.rs delete mode 100644 src/librustc_trans/callee.rs delete mode 100644 src/librustc_trans/common.rs delete mode 100644 src/librustc_trans/consts.rs delete mode 100644 src/librustc_trans/context.rs delete mode 100644 src/librustc_trans/debuginfo/create_scope_map.rs delete mode 100644 src/librustc_trans/debuginfo/doc.rs delete mode 100644 src/librustc_trans/debuginfo/gdb.rs delete mode 100644 src/librustc_trans/debuginfo/metadata.rs delete mode 100644 src/librustc_trans/debuginfo/mod.rs delete mode 100644 src/librustc_trans/debuginfo/namespace.rs delete mode 100644 src/librustc_trans/debuginfo/source_loc.rs delete mode 100644 src/librustc_trans/debuginfo/type_names.rs delete mode 100644 src/librustc_trans/debuginfo/utils.rs delete mode 100644 src/librustc_trans/declare.rs delete mode 100644 src/librustc_trans/diagnostics.rs delete mode 100644 src/librustc_trans/glue.rs delete mode 100644 src/librustc_trans/intrinsic.rs delete mode 100644 src/librustc_trans/lib.rs delete mode 100644 src/librustc_trans/llvm_util.rs delete mode 100644 src/librustc_trans/metadata.rs delete mode 100644 src/librustc_trans/meth.rs delete mode 100644 src/librustc_trans/mir/analyze.rs delete mode 100644 src/librustc_trans/mir/block.rs delete mode 100644 src/librustc_trans/mir/constant.rs delete mode 100644 src/librustc_trans/mir/mod.rs delete mode 100644 src/librustc_trans/mir/operand.rs delete mode 100644 src/librustc_trans/mir/place.rs delete mode 100644 src/librustc_trans/mir/rvalue.rs delete mode 100644 src/librustc_trans/mir/statement.rs delete mode 100644 src/librustc_trans/time_graph.rs delete mode 100644 src/librustc_trans/trans_item.rs delete mode 100644 src/librustc_trans/type_.rs delete mode 100644 src/librustc_trans/type_of.rs delete mode 100644 src/librustc_trans/value.rs delete mode 100644 src/librustc_trans_utils/Cargo.toml delete mode 100644 src/librustc_trans_utils/lib.rs delete mode 100644 src/librustc_trans_utils/link.rs delete mode 100644 src/librustc_trans_utils/symbol_names.rs delete mode 100644 src/librustc_trans_utils/symbol_names_test.rs delete mode 100644 src/librustc_trans_utils/trans_crate.rs create mode 100644 src/test/compile-fail/dep-graph-assoc-type-codegen.rs delete mode 100644 src/test/compile-fail/dep-graph-assoc-type-trans.rs create mode 100644 src/test/run-fail/mir_codegen_calls_converging_drops.rs create mode 100644 src/test/run-fail/mir_codegen_calls_converging_drops_2.rs create mode 100644 src/test/run-fail/mir_codegen_calls_diverging.rs create mode 100644 src/test/run-fail/mir_codegen_calls_diverging_drops.rs create mode 100644 src/test/run-fail/mir_codegen_no_landing_pads.rs create mode 100644 src/test/run-fail/mir_codegen_no_landing_pads_diverging.rs delete mode 100644 src/test/run-fail/mir_trans_calls_converging_drops.rs delete mode 100644 src/test/run-fail/mir_trans_calls_converging_drops_2.rs delete mode 100644 src/test/run-fail/mir_trans_calls_diverging.rs delete mode 100644 src/test/run-fail/mir_trans_calls_diverging_drops.rs delete mode 100644 src/test/run-fail/mir_trans_no_landing_pads.rs delete mode 100644 src/test/run-fail/mir_trans_no_landing_pads_diverging.rs create mode 100644 src/test/run-pass/codegen-object-shim.rs create mode 100644 src/test/run-pass/codegen-tag-static-padding.rs create mode 100644 src/test/run-pass/compiletest-skip-codegen.rs delete mode 100644 src/test/run-pass/compiletest-skip-trans.rs create mode 100644 src/test/run-pass/mir_codegen_array.rs create mode 100644 src/test/run-pass/mir_codegen_array_2.rs create mode 100644 src/test/run-pass/mir_codegen_call_converging.rs create mode 100644 src/test/run-pass/mir_codegen_calls.rs create mode 100644 src/test/run-pass/mir_codegen_calls_variadic.rs create mode 100644 src/test/run-pass/mir_codegen_critical_edge.rs create mode 100644 src/test/run-pass/mir_codegen_spike1.rs create mode 100644 src/test/run-pass/mir_codegen_switch.rs create mode 100644 src/test/run-pass/mir_codegen_switchint.rs delete mode 100644 src/test/run-pass/mir_trans_array.rs delete mode 100644 src/test/run-pass/mir_trans_array_2.rs delete mode 100644 src/test/run-pass/mir_trans_call_converging.rs delete mode 100644 src/test/run-pass/mir_trans_calls.rs delete mode 100644 src/test/run-pass/mir_trans_calls_variadic.rs delete mode 100644 src/test/run-pass/mir_trans_critical_edge.rs delete mode 100644 src/test/run-pass/mir_trans_spike1.rs delete mode 100644 src/test/run-pass/mir_trans_switch.rs delete mode 100644 src/test/run-pass/mir_trans_switchint.rs create mode 100644 src/test/run-pass/regions-mock-codegen.rs delete mode 100644 src/test/run-pass/regions-mock-trans.rs delete mode 100644 src/test/run-pass/trans-object-shim.rs delete mode 100644 src/test/run-pass/trans-tag-static-padding.rs create mode 100644 src/test/run-pass/union/union-const-codegen.rs delete mode 100644 src/test/run-pass/union/union-const-trans.rs (limited to 'src/liballoc/alloc.rs') diff --git a/src/Cargo.lock b/src/Cargo.lock index 76ca287b815..00cc530e632 100644 --- a/src/Cargo.lock +++ b/src/Cargo.lock @@ -1896,6 +1896,52 @@ dependencies = [ "syntax_pos 0.0.0", ] +[[package]] +name = "rustc_codegen_llvm" +version = "0.0.0" +dependencies = [ + "bitflags 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "cc 1.0.15 (registry+https://github.com/rust-lang/crates.io-index)", + "env_logger 0.5.8 (registry+https://github.com/rust-lang/crates.io-index)", + "flate2 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "jobserver 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.40 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", + "num_cpus 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc 0.0.0", + "rustc-demangle 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc_allocator 0.0.0", + "rustc_apfloat 0.0.0", + "rustc_codegen_utils 0.0.0", + "rustc_data_structures 0.0.0", + "rustc_errors 0.0.0", + "rustc_incremental 0.0.0", + "rustc_llvm 0.0.0", + "rustc_mir 0.0.0", + "rustc_platform_intrinsics 0.0.0", + "rustc_target 0.0.0", + "serialize 0.0.0", + "syntax 0.0.0", + "syntax_pos 0.0.0", + "tempdir 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rustc_codegen_utils" +version = "0.0.0" +dependencies = [ + "ar 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "flate2 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc 0.0.0", + "rustc_data_structures 0.0.0", + "rustc_incremental 0.0.0", + "rustc_mir 0.0.0", + "rustc_target 0.0.0", + "syntax 0.0.0", + "syntax_pos 0.0.0", +] + [[package]] name = "rustc_cratesio_shim" version = "0.0.0" @@ -1932,6 +1978,7 @@ dependencies = [ "rustc-rayon 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "rustc_allocator 0.0.0", "rustc_borrowck 0.0.0", + "rustc_codegen_utils 0.0.0", "rustc_data_structures 0.0.0", "rustc_errors 0.0.0", "rustc_incremental 0.0.0", @@ -1945,7 +1992,6 @@ dependencies = [ "rustc_save_analysis 0.0.0", "rustc_target 0.0.0", "rustc_traits 0.0.0", - "rustc_trans_utils 0.0.0", "rustc_typeck 0.0.0", "scoped-tls 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "serialize 0.0.0", @@ -2155,52 +2201,6 @@ dependencies = [ "syntax_pos 0.0.0", ] -[[package]] -name = "rustc_trans" -version = "0.0.0" -dependencies = [ - "bitflags 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", - "cc 1.0.15 (registry+https://github.com/rust-lang/crates.io-index)", - "env_logger 0.5.8 (registry+https://github.com/rust-lang/crates.io-index)", - "flate2 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", - "jobserver 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.40 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", - "num_cpus 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc 0.0.0", - "rustc-demangle 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc_allocator 0.0.0", - "rustc_apfloat 0.0.0", - "rustc_data_structures 0.0.0", - "rustc_errors 0.0.0", - "rustc_incremental 0.0.0", - "rustc_llvm 0.0.0", - "rustc_mir 0.0.0", - "rustc_platform_intrinsics 0.0.0", - "rustc_target 0.0.0", - "rustc_trans_utils 0.0.0", - "serialize 0.0.0", - "syntax 0.0.0", - "syntax_pos 0.0.0", - "tempdir 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "rustc_trans_utils" -version = "0.0.0" -dependencies = [ - "ar 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "flate2 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc 0.0.0", - "rustc_data_structures 0.0.0", - "rustc_incremental 0.0.0", - "rustc_mir 0.0.0", - "rustc_target 0.0.0", - "syntax 0.0.0", - "syntax_pos 0.0.0", -] - [[package]] name = "rustc_tsan" version = "0.0.0" diff --git a/src/Cargo.toml b/src/Cargo.toml index 35858ee2868..1518e8d910f 100644 --- a/src/Cargo.toml +++ b/src/Cargo.toml @@ -4,7 +4,7 @@ members = [ "rustc", "libstd", "libtest", - "librustc_trans", + "librustc_codegen_llvm", "tools/cargotest", "tools/clippy", "tools/compiletest", diff --git a/src/bootstrap/builder.rs b/src/bootstrap/builder.rs index e5824010ef2..cc1e66332a3 100644 --- a/src/bootstrap/builder.rs +++ b/src/bootstrap/builder.rs @@ -844,7 +844,7 @@ impl<'a> Builder<'a> { // default via `-ldylib=winapi_foo`. That is, they're linked with the // `dylib` type with a `winapi_` prefix (so the winapi ones don't // conflict with the system MinGW ones). This consequently means that - // the binaries we ship of things like rustc_trans (aka the rustc_trans + // the binaries we ship of things like rustc_codegen_llvm (aka the rustc_codegen_llvm // DLL) when linked against *again*, for example with procedural macros // or plugins, will trigger the propagation logic of `-ldylib`, passing // `-lwinapi_foo` to the linker again. This isn't actually available in diff --git a/src/bootstrap/check.rs b/src/bootstrap/check.rs index 64354ae29aa..a516af58b1e 100644 --- a/src/bootstrap/check.rs +++ b/src/bootstrap/check.rs @@ -118,7 +118,7 @@ impl Step for CodegenBackend { const DEFAULT: bool = true; fn should_run(run: ShouldRun) -> ShouldRun { - run.all_krates("rustc_trans") + run.all_krates("rustc_codegen_llvm") } fn make_run(run: RunConfig) { @@ -139,12 +139,12 @@ impl Step for CodegenBackend { let mut cargo = builder.cargo(compiler, Mode::Librustc, target, "check"); let features = builder.rustc_features().to_string(); - cargo.arg("--manifest-path").arg(builder.src.join("src/librustc_trans/Cargo.toml")); + cargo.arg("--manifest-path").arg(builder.src.join("src/librustc_codegen_llvm/Cargo.toml")); rustc_cargo_env(builder, &mut cargo); // We won't build LLVM if it's not available, as it shouldn't affect `check`. - let _folder = builder.fold_output(|| format!("stage{}-rustc_trans", compiler.stage)); + let _folder = builder.fold_output(|| format!("stage{}-rustc_codegen_llvm", compiler.stage)); run_cargo(builder, cargo.arg("--features").arg(features), &codegen_backend_stamp(builder, compiler, target, backend), @@ -259,14 +259,14 @@ pub fn librustc_stamp(builder: &Builder, compiler: Compiler, target: Interned, backend: Interned) -> PathBuf { builder.cargo_out(compiler, Mode::Librustc, target) - .join(format!(".librustc_trans-{}-check.stamp", backend)) + .join(format!(".librustc_codegen_llvm-{}-check.stamp", backend)) } /// Cargo's output path for rustdoc in a given stage, compiled by a particular diff --git a/src/bootstrap/compile.rs b/src/bootstrap/compile.rs index 1248c2b50be..8e0227f8fe1 100644 --- a/src/bootstrap/compile.rs +++ b/src/bootstrap/compile.rs @@ -603,7 +603,7 @@ impl Step for CodegenBackend { const DEFAULT: bool = true; fn should_run(run: ShouldRun) -> ShouldRun { - run.all_krates("rustc_trans") + run.all_krates("rustc_codegen_llvm") } fn make_run(run: RunConfig) { @@ -637,7 +637,7 @@ impl Step for CodegenBackend { let mut cargo = builder.cargo(compiler, Mode::Librustc, target, "build"); let mut features = builder.rustc_features().to_string(); cargo.arg("--manifest-path") - .arg(builder.src.join("src/librustc_trans/Cargo.toml")); + .arg(builder.src.join("src/librustc_codegen_llvm/Cargo.toml")); rustc_cargo_env(builder, &mut cargo); features += &build_codegen_backend(&builder, &mut cargo, &compiler, target, backend); @@ -645,7 +645,7 @@ impl Step for CodegenBackend { let tmp_stamp = builder.cargo_out(compiler, Mode::Librustc, target) .join(".tmp.stamp"); - let _folder = builder.fold_output(|| format!("stage{}-rustc_trans", compiler.stage)); + let _folder = builder.fold_output(|| format!("stage{}-rustc_codegen_llvm", compiler.stage)); let files = run_cargo(builder, cargo.arg("--features").arg(features), &tmp_stamp, @@ -656,7 +656,7 @@ impl Step for CodegenBackend { let mut files = files.into_iter() .filter(|f| { let filename = f.file_name().unwrap().to_str().unwrap(); - is_dylib(filename) && filename.contains("rustc_trans-") + is_dylib(filename) && filename.contains("rustc_codegen_llvm-") }); let codegen_backend = match files.next() { Some(f) => f, @@ -697,7 +697,7 @@ pub fn build_codegen_backend(builder: &Builder, compiler.stage, &compiler.host, target, backend)); // Pass down configuration from the LLVM build into the build of - // librustc_llvm and librustc_trans. + // librustc_llvm and librustc_codegen_llvm. if builder.is_rust_llvm(target) { cargo.env("LLVM_RUSTLLVM", "1"); } @@ -762,7 +762,7 @@ fn copy_codegen_backends_to_sysroot(builder: &Builder, t!(t!(File::open(&stamp)).read_to_string(&mut dylib)); let file = Path::new(&dylib); let filename = file.file_name().unwrap().to_str().unwrap(); - // change `librustc_trans-xxxxxx.so` to `librustc_trans-llvm.so` + // change `librustc_codegen_llvm-xxxxxx.so` to `librustc_codegen_llvm-llvm.so` let target_filename = { let dash = filename.find("-").unwrap(); let dot = filename.find(".").unwrap(); @@ -808,14 +808,14 @@ pub fn librustc_stamp(builder: &Builder, compiler: Compiler, target: Interned, backend: Interned) -> PathBuf { builder.cargo_out(compiler, Mode::Librustc, target) - .join(format!(".librustc_trans-{}.stamp", backend)) + .join(format!(".librustc_codegen_llvm-{}.stamp", backend)) } pub fn compiler_file(builder: &Builder, diff --git a/src/doc/rustc-ux-guidelines.md b/src/doc/rustc-ux-guidelines.md index b62762ef69e..93e94e55863 100644 --- a/src/doc/rustc-ux-guidelines.md +++ b/src/doc/rustc-ux-guidelines.md @@ -69,7 +69,7 @@ for details on how to format and write long error codes. [librustc_passes](https://github.com/rust-lang/rust/blob/master/src/librustc_passes/diagnostics.rs), [librustc_privacy](https://github.com/rust-lang/rust/blob/master/src/librustc_privacy/diagnostics.rs), [librustc_resolve](https://github.com/rust-lang/rust/blob/master/src/librustc_resolve/diagnostics.rs), - [librustc_trans](https://github.com/rust-lang/rust/blob/master/src/librustc_trans/diagnostics.rs), + [librustc_codegen_llvm](https://github.com/rust-lang/rust/blob/master/src/librustc_codegen_llvm/diagnostics.rs), [librustc_plugin](https://github.com/rust-lang/rust/blob/master/src/librustc_plugin/diagnostics.rs), [librustc_typeck](https://github.com/rust-lang/rust/blob/master/src/librustc_typeck/diagnostics.rs). * Explanations have full markdown support. Use it, especially to highlight diff --git a/src/liballoc/alloc.rs b/src/liballoc/alloc.rs index f59c9f7fd61..09d16b26520 100644 --- a/src/liballoc/alloc.rs +++ b/src/liballoc/alloc.rs @@ -134,7 +134,7 @@ unsafe impl Alloc for Global { } /// The allocator for unique pointers. -// This function must not unwind. If it does, MIR trans will fail. +// This function must not unwind. If it does, MIR codegen will fail. #[cfg(not(test))] #[lang = "exchange_malloc"] #[inline] diff --git a/src/libcore/intrinsics.rs b/src/libcore/intrinsics.rs index 7d3e7af1a18..5ec6cb6c710 100644 --- a/src/libcore/intrinsics.rs +++ b/src/libcore/intrinsics.rs @@ -10,7 +10,7 @@ //! rustc compiler intrinsics. //! -//! The corresponding definitions are in librustc_trans/intrinsic.rs. +//! The corresponding definitions are in librustc_codegen_llvm/intrinsic.rs. //! //! # Volatiles //! diff --git a/src/librustc/Cargo.toml b/src/librustc/Cargo.toml index af108188ce0..8f9a8bd5c01 100644 --- a/src/librustc/Cargo.toml +++ b/src/librustc/Cargo.toml @@ -35,13 +35,13 @@ byteorder = { version = "1.1", features = ["i128"]} # rlib/dylib pair but all crates.io crates tend to just be rlibs. This means # we've got a problem for dependency graphs that look like: # -# foo - rustc_trans +# foo - rustc_codegen_llvm # / \ # rustc ---- rustc_driver # \ / # foo - rustc_metadata # -# Here the crate `foo` is linked into the `rustc_trans` and the +# Here the crate `foo` is linked into the `rustc_codegen_llvm` and the # `rustc_metadata` dylibs, meaning we've got duplicate copies! When we then # go to link `rustc_driver` the compiler notices this and gives us a compiler # error. @@ -49,7 +49,7 @@ byteorder = { version = "1.1", features = ["i128"]} # To work around this problem we just add these crates.io dependencies to the # `rustc` crate which is a shared dependency above. That way the crate `foo` # shows up in the dylib for the `rustc` crate, deduplicating it and allowing -# crates like `rustc_trans` to use `foo` *through* the `rustc` crate. +# crates like `rustc_codegen_llvm` to use `foo` *through* the `rustc` crate. # # tl;dr; this is not needed to get `rustc` to compile, but if you remove it then # later crate stop compiling. If you can remove this and everything diff --git a/src/librustc/cfg/construct.rs b/src/librustc/cfg/construct.rs index 7e03288f572..20e4b1343c1 100644 --- a/src/librustc/cfg/construct.rs +++ b/src/librustc/cfg/construct.rs @@ -452,13 +452,13 @@ impl<'a, 'tcx> CFGBuilder<'a, 'tcx> { // The CFG for match expression is quite complex, so no ASCII // art for it (yet). // - // The CFG generated below matches roughly what trans puts - // out. Each pattern and guard is visited in parallel, with + // The CFG generated below matches roughly what MIR contains. + // Each pattern and guard is visited in parallel, with // arms containing multiple patterns generating multiple nodes // for the same guard expression. The guard expressions chain // into each other from top to bottom, with a specific // exception to allow some additional valid programs - // (explained below). Trans differs slightly in that the + // (explained below). MIR differs slightly in that the // pattern matching may continue after a guard but the visible // behaviour should be the same. // diff --git a/src/librustc/dep_graph/dep_node.rs b/src/librustc/dep_graph/dep_node.rs index 4847a7f4ddb..9cd9d44874b 100644 --- a/src/librustc/dep_graph/dep_node.rs +++ b/src/librustc/dep_graph/dep_node.rs @@ -565,7 +565,7 @@ define_dep_nodes!( <'tcx> [] IsUnreachableLocalDefinition(DefId), [] IsMirAvailable(DefId), [] ItemAttrs(DefId), - [] TransFnAttrs(DefId), + [] CodegenFnAttrs(DefId), [] FnArgNames(DefId), [] RenderedConst(DefId), [] DylibDepFormats(CrateNum), @@ -637,8 +637,8 @@ define_dep_nodes!( <'tcx> [eval_always] AllTraits, [input] AllCrateNums, [] ExportedSymbols(CrateNum), - [eval_always] CollectAndPartitionTranslationItems, - [] IsTranslatedItem(DefId), + [eval_always] CollectAndPartitionMonoItems, + [] IsCodegenedItem(DefId), [] CodegenUnit(InternedString), [] CompileCodegenUnit(InternedString), [input] OutputFilenames, diff --git a/src/librustc/dep_graph/graph.rs b/src/librustc/dep_graph/graph.rs index 797332e699d..26470ddc82a 100644 --- a/src/librustc/dep_graph/graph.rs +++ b/src/librustc/dep_graph/graph.rs @@ -856,10 +856,10 @@ impl DepGraph { /// each partition. In the first run, we create partitions based on /// the symbols that need to be compiled. For each partition P, we /// hash the symbols in P and create a `WorkProduct` record associated -/// with `DepNode::TransPartition(P)`; the hash is the set of symbols +/// with `DepNode::CodegenUnit(P)`; the hash is the set of symbols /// in P. /// -/// The next time we compile, if the `DepNode::TransPartition(P)` is +/// The next time we compile, if the `DepNode::CodegenUnit(P)` is /// judged to be clean (which means none of the things we read to /// generate the partition were found to be dirty), it will be loaded /// into previous work products. We will then regenerate the set of diff --git a/src/librustc/hir/check_attr.rs b/src/librustc/hir/check_attr.rs index 24a1256c9d3..591cb9d5ad6 100644 --- a/src/librustc/hir/check_attr.rs +++ b/src/librustc/hir/check_attr.rs @@ -58,7 +58,7 @@ impl<'a, 'tcx> CheckAttrVisitor<'a, 'tcx> { /// Check any attribute. fn check_attributes(&self, item: &hir::Item, target: Target) { if target == Target::Fn { - self.tcx.trans_fn_attrs(self.tcx.hir.local_def_id(item.id)); + self.tcx.codegen_fn_attrs(self.tcx.hir.local_def_id(item.id)); } else if let Some(a) = item.attrs.iter().find(|a| a.check_name("target_feature")) { self.tcx.sess.struct_span_err(a.span, "attribute should be applied to a function") .span_label(item.span, "not a function") diff --git a/src/librustc/hir/mod.rs b/src/librustc/hir/mod.rs index c48fb7ab7eb..edd5e668d5e 100644 --- a/src/librustc/hir/mod.rs +++ b/src/librustc/hir/mod.rs @@ -2244,8 +2244,8 @@ pub fn provide(providers: &mut Providers) { } #[derive(Clone, RustcEncodable, RustcDecodable, Hash)] -pub struct TransFnAttrs { - pub flags: TransFnAttrFlags, +pub struct CodegenFnAttrs { + pub flags: CodegenFnAttrFlags, pub inline: InlineAttr, pub export_name: Option, pub target_features: Vec, @@ -2254,7 +2254,7 @@ pub struct TransFnAttrs { bitflags! { #[derive(RustcEncodable, RustcDecodable)] - pub struct TransFnAttrFlags: u8 { + pub struct CodegenFnAttrFlags: u8 { const COLD = 0b0000_0001; const ALLOCATOR = 0b0000_0010; const UNWIND = 0b0000_0100; @@ -2266,10 +2266,10 @@ bitflags! { } } -impl TransFnAttrs { - pub fn new() -> TransFnAttrs { - TransFnAttrs { - flags: TransFnAttrFlags::empty(), +impl CodegenFnAttrs { + pub fn new() -> CodegenFnAttrs { + CodegenFnAttrs { + flags: CodegenFnAttrFlags::empty(), inline: InlineAttr::None, export_name: None, target_features: vec![], @@ -2287,7 +2287,6 @@ impl TransFnAttrs { /// True if `#[no_mangle]` or `#[export_name(...)]` is present. pub fn contains_extern_indicator(&self) -> bool { - self.flags.contains(TransFnAttrFlags::NO_MANGLE) || self.export_name.is_some() + self.flags.contains(CodegenFnAttrFlags::NO_MANGLE) || self.export_name.is_some() } } - diff --git a/src/librustc/ich/impls_hir.rs b/src/librustc/ich/impls_hir.rs index ed01704e6a4..30f725a6b50 100644 --- a/src/librustc/ich/impls_hir.rs +++ b/src/librustc/ich/impls_hir.rs @@ -1155,12 +1155,12 @@ impl<'a> ToStableHashKey> for hir::TraitCandidate { } } -impl<'hir> HashStable> for hir::TransFnAttrs +impl<'hir> HashStable> for hir::CodegenFnAttrs { fn hash_stable(&self, hcx: &mut StableHashingContext<'hir>, hasher: &mut StableHasher) { - let hir::TransFnAttrs { + let hir::CodegenFnAttrs { flags, inline, export_name, @@ -1176,7 +1176,7 @@ impl<'hir> HashStable> for hir::TransFnAttrs } } -impl<'hir> HashStable> for hir::TransFnAttrFlags +impl<'hir> HashStable> for hir::CodegenFnAttrFlags { fn hash_stable(&self, hcx: &mut StableHashingContext<'hir>, diff --git a/src/librustc/ich/mod.rs b/src/librustc/ich/mod.rs index a0c6bbbb239..d58eb64c366 100644 --- a/src/librustc/ich/mod.rs +++ b/src/librustc/ich/mod.rs @@ -30,7 +30,7 @@ pub const ATTR_CLEAN: &'static str = "rustc_clean"; pub const ATTR_IF_THIS_CHANGED: &'static str = "rustc_if_this_changed"; pub const ATTR_THEN_THIS_WOULD_NEED: &'static str = "rustc_then_this_would_need"; pub const ATTR_PARTITION_REUSED: &'static str = "rustc_partition_reused"; -pub const ATTR_PARTITION_TRANSLATED: &'static str = "rustc_partition_translated"; +pub const ATTR_PARTITION_CODEGENED: &'static str = "rustc_partition_codegened"; pub const DEP_GRAPH_ASSERT_ATTRS: &'static [&'static str] = &[ @@ -39,7 +39,7 @@ pub const DEP_GRAPH_ASSERT_ATTRS: &'static [&'static str] = &[ ATTR_DIRTY, ATTR_CLEAN, ATTR_PARTITION_REUSED, - ATTR_PARTITION_TRANSLATED, + ATTR_PARTITION_CODEGENED, ]; pub const IGNORED_ATTRIBUTES: &'static [&'static str] = &[ @@ -49,5 +49,5 @@ pub const IGNORED_ATTRIBUTES: &'static [&'static str] = &[ ATTR_DIRTY, ATTR_CLEAN, ATTR_PARTITION_REUSED, - ATTR_PARTITION_TRANSLATED, + ATTR_PARTITION_CODEGENED, ]; diff --git a/src/librustc/infer/anon_types/mod.rs b/src/librustc/infer/anon_types/mod.rs index d681e2e3d34..4cc5e885b8a 100644 --- a/src/librustc/infer/anon_types/mod.rs +++ b/src/librustc/infer/anon_types/mod.rs @@ -614,7 +614,7 @@ impl<'cx, 'gcx, 'tcx> TypeFolder<'gcx, 'tcx> for ReverseMapper<'cx, 'gcx, 'tcx> // compiler; those regions are ignored for the // outlives relation, and hence don't affect trait // selection or auto traits, and they are erased - // during trans. + // during codegen. let generics = self.tcx.generics_of(def_id); let substs = self.tcx.mk_substs(substs.substs.iter().enumerate().map( diff --git a/src/librustc/lint/context.rs b/src/librustc/lint/context.rs index f90baa2ccd9..381bb161e42 100644 --- a/src/librustc/lint/context.rs +++ b/src/librustc/lint/context.rs @@ -10,15 +10,15 @@ //! Implementation of lint checking. //! -//! The lint checking is mostly consolidated into one pass which runs just -//! before translation to LLVM bytecode. Throughout compilation, lint warnings +//! The lint checking is mostly consolidated into one pass which runs +//! after all other analyses. Throughout compilation, lint warnings //! can be added via the `add_lint` method on the Session structure. This //! requires a span and an id of the node that the lint is being added to. The //! lint isn't actually emitted at that time because it is unknown what the //! actual lint level at that location is. //! -//! To actually emit lint warnings/errors, a separate pass is used just before -//! translation. A context keeps track of the current state of all lint levels. +//! To actually emit lint warnings/errors, a separate pass is used. +//! A context keeps track of the current state of all lint levels. //! Upon entering a node of the ast which can modify the lint settings, the //! previous lint state is pushed onto a stack and the ast is then recursed //! upon. As the ast is traversed, this keeps track of the current lint level diff --git a/src/librustc/lint/mod.rs b/src/librustc/lint/mod.rs index d6c6f9dc0f6..7645d3486c2 100644 --- a/src/librustc/lint/mod.rs +++ b/src/librustc/lint/mod.rs @@ -16,15 +16,15 @@ //! other phases of the compiler, which are generally required to hold in order //! to compile the program at all. //! -//! Most lints can be written as `LintPass` instances. These run just before -//! translation to LLVM bytecode. The `LintPass`es built into rustc are defined +//! Most lints can be written as `LintPass` instances. These run after +//! all other analyses. The `LintPass`es built into rustc are defined //! within `builtin.rs`, which has further comments on how to add such a lint. //! rustc can also load user-defined lint plugins via the plugin mechanism. //! //! Some of rustc's lints are defined elsewhere in the compiler and work by //! calling `add_lint()` on the overall `Session` object. This works when //! it happens before the main lint pass, which emits the lints stored by -//! `add_lint()`. To emit lints after the main lint pass (from trans, for +//! `add_lint()`. To emit lints after the main lint pass (from codegen, for //! example) requires more effort. See `emit_lint` and `GatherNodeLevels` //! in `context.rs`. diff --git a/src/librustc/middle/dependency_format.rs b/src/librustc/middle/dependency_format.rs index 4996a6acff8..4c99b46ddff 100644 --- a/src/librustc/middle/dependency_format.rs +++ b/src/librustc/middle/dependency_format.rs @@ -109,7 +109,7 @@ fn calculate_type<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, let sess = &tcx.sess; - if !sess.opts.output_types.should_trans() { + if !sess.opts.output_types.should_codegen() { return Vec::new(); } diff --git a/src/librustc/middle/mem_categorization.rs b/src/librustc/middle/mem_categorization.rs index a4c38333da1..e9d27c182a2 100644 --- a/src/librustc/middle/mem_categorization.rs +++ b/src/librustc/middle/mem_categorization.rs @@ -46,9 +46,9 @@ //! //! ## By-reference upvars //! -//! One part of the translation which may be non-obvious is that we translate +//! One part of the codegen which may be non-obvious is that we translate //! closure upvars into the dereference of a borrowed pointer; this more closely -//! resembles the runtime translation. So, for example, if we had: +//! resembles the runtime codegen. So, for example, if we had: //! //! let mut x = 3; //! let y = 5; diff --git a/src/librustc/middle/reachable.rs b/src/librustc/middle/reachable.rs index 0aeb15b49fb..249651ef65d 100644 --- a/src/librustc/middle/reachable.rs +++ b/src/librustc/middle/reachable.rs @@ -15,7 +15,7 @@ // makes all other generics or inline functions that it references // reachable as well. -use hir::TransFnAttrs; +use hir::CodegenFnAttrs; use hir::map as hir_map; use hir::def::Def; use hir::def_id::{DefId, CrateNum}; @@ -44,7 +44,7 @@ fn generics_require_inlining(generics: &hir::Generics) -> bool { // Returns true if the given item must be inlined because it may be // monomorphized or it was marked with `#[inline]`. This will only return // true for functions. -fn item_might_be_inlined(item: &hir::Item, attrs: TransFnAttrs) -> bool { +fn item_might_be_inlined(item: &hir::Item, attrs: CodegenFnAttrs) -> bool { if attrs.requests_inline() { return true } @@ -61,15 +61,15 @@ fn item_might_be_inlined(item: &hir::Item, attrs: TransFnAttrs) -> bool { fn method_might_be_inlined<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, impl_item: &hir::ImplItem, impl_src: DefId) -> bool { - let trans_fn_attrs = tcx.trans_fn_attrs(impl_item.hir_id.owner_def_id()); - if trans_fn_attrs.requests_inline() || + let codegen_fn_attrs = tcx.codegen_fn_attrs(impl_item.hir_id.owner_def_id()); + if codegen_fn_attrs.requests_inline() || generics_require_inlining(&impl_item.generics) { return true } if let Some(impl_node_id) = tcx.hir.as_local_node_id(impl_src) { match tcx.hir.find(impl_node_id) { Some(hir_map::NodeItem(item)) => - item_might_be_inlined(&item, trans_fn_attrs), + item_might_be_inlined(&item, codegen_fn_attrs), Some(..) | None => span_bug!(impl_item.span, "impl did is not an item") } @@ -163,7 +163,7 @@ impl<'a, 'tcx> ReachableContext<'a, 'tcx> { Some(hir_map::NodeItem(item)) => { match item.node { hir::ItemFn(..) => - item_might_be_inlined(&item, self.tcx.trans_fn_attrs(def_id)), + item_might_be_inlined(&item, self.tcx.codegen_fn_attrs(def_id)), _ => false, } } @@ -179,7 +179,7 @@ impl<'a, 'tcx> ReachableContext<'a, 'tcx> { match impl_item.node { hir::ImplItemKind::Const(..) => true, hir::ImplItemKind::Method(..) => { - let attrs = self.tcx.trans_fn_attrs(def_id); + let attrs = self.tcx.codegen_fn_attrs(def_id); if generics_require_inlining(&impl_item.generics) || attrs.requests_inline() { true @@ -233,7 +233,7 @@ impl<'a, 'tcx> ReachableContext<'a, 'tcx> { false }; let def_id = self.tcx.hir.local_def_id(item.id); - let is_extern = self.tcx.trans_fn_attrs(def_id).contains_extern_indicator(); + let is_extern = self.tcx.codegen_fn_attrs(def_id).contains_extern_indicator(); if reachable || is_extern { self.reachable_symbols.insert(search_item); } @@ -251,7 +251,7 @@ impl<'a, 'tcx> ReachableContext<'a, 'tcx> { match item.node { hir::ItemFn(.., body) => { let def_id = self.tcx.hir.local_def_id(item.id); - if item_might_be_inlined(&item, self.tcx.trans_fn_attrs(def_id)) { + if item_might_be_inlined(&item, self.tcx.codegen_fn_attrs(def_id)) { self.visit_nested_body(body); } } diff --git a/src/librustc/middle/region.rs b/src/librustc/middle/region.rs index 52217600759..71d7abed6c9 100644 --- a/src/librustc/middle/region.rs +++ b/src/librustc/middle/region.rs @@ -1279,7 +1279,7 @@ fn resolve_local<'a, 'tcx>(visitor: &mut RegionResolutionVisitor<'a, 'tcx>, loop { // Note: give all the expressions matching `ET` with the // extended temporary lifetime, not just the innermost rvalue, - // because in trans if we must compile e.g. `*rvalue()` + // because in codegen if we must compile e.g. `*rvalue()` // into a temporary, we request the temporary scope of the // outer expression. visitor.scope_tree.record_rvalue_scope(expr.hir_id.local_id, blk_scope); diff --git a/src/librustc/mir/interpret/mod.rs b/src/librustc/mir/interpret/mod.rs index afdd1c167c6..31885c1e020 100644 --- a/src/librustc/mir/interpret/mod.rs +++ b/src/librustc/mir/interpret/mod.rs @@ -249,7 +249,7 @@ pub struct Allocation { pub undef_mask: UndefMask, /// The alignment of the allocation to detect unaligned reads. pub align: Align, - /// Whether the allocation (of a static) should be put into mutable memory when translating + /// Whether the allocation (of a static) should be put into mutable memory when codegenning /// /// Only happens for `static mut` or `static` with interior mutability pub runtime_mutability: Mutability, diff --git a/src/librustc/mir/interpret/value.rs b/src/librustc/mir/interpret/value.rs index 2cd4bf9d18c..7bd84a607e7 100644 --- a/src/librustc/mir/interpret/value.rs +++ b/src/librustc/mir/interpret/value.rs @@ -75,7 +75,7 @@ impl<'tcx> ConstValue<'tcx> { /// /// For optimization of a few very common cases, there is also a representation for a pair of /// primitive values (`ByValPair`). It allows Miri to avoid making allocations for checked binary -/// operations and fat pointers. This idea was taken from rustc's trans. +/// operations and fat pointers. This idea was taken from rustc's codegen. #[derive(Clone, Copy, Debug, Eq, PartialEq, RustcEncodable, RustcDecodable, Hash)] pub enum Value { ByRef(Pointer, Align), diff --git a/src/librustc/mir/mod.rs b/src/librustc/mir/mod.rs index eb12444bcb4..40e9b687f0c 100644 --- a/src/librustc/mir/mod.rs +++ b/src/librustc/mir/mod.rs @@ -696,7 +696,7 @@ pub struct BasicBlockData<'tcx> { pub terminator: Option>, /// If true, this block lies on an unwind path. This is used - /// during trans where distinct kinds of basic blocks may be + /// during codegen where distinct kinds of basic blocks may be /// generated (particularly for MSVC cleanup). Unwind blocks must /// only branch to other unwind blocks. pub is_cleanup: bool, @@ -1614,7 +1614,7 @@ pub enum CastKind { UnsafeFnPointer, /// "Unsize" -- convert a thin-or-fat pointer to a fat pointer. - /// trans must figure out the details once full monomorphization + /// codegen must figure out the details once full monomorphization /// is known. For example, this could be used to cast from a /// `&[i32;N]` to a `&[i32]`, or a `Box` to a `Box` /// (presuming `T: Trait`). diff --git a/src/librustc/mir/mono.rs b/src/librustc/mir/mono.rs index d01059a3e01..9de9347d0ce 100644 --- a/src/librustc/mir/mono.rs +++ b/src/librustc/mir/mono.rs @@ -184,11 +184,11 @@ impl<'a, 'tcx> HashStable> for CodegenUnit<'tcx> { name.hash_stable(hcx, hasher); - let mut items: Vec<(Fingerprint, _)> = items.iter().map(|(trans_item, &attrs)| { + let mut items: Vec<(Fingerprint, _)> = items.iter().map(|(mono_item, &attrs)| { let mut hasher = StableHasher::new(); - trans_item.hash_stable(hcx, &mut hasher); - let trans_item_fingerprint = hasher.finish(); - (trans_item_fingerprint, attrs) + mono_item.hash_stable(hcx, &mut hasher); + let mono_item_fingerprint = hasher.finish(); + (mono_item_fingerprint, attrs) }).collect(); items.sort_unstable_by_key(|i| i.0); @@ -238,4 +238,3 @@ impl Stats { self.fn_stats.extend(stats.fn_stats); } } - diff --git a/src/librustc/session/config.rs b/src/librustc/session/config.rs index 489197e0381..2344cd11f66 100644 --- a/src/librustc/session/config.rs +++ b/src/librustc/session/config.rs @@ -270,7 +270,7 @@ impl OutputTypes { } // True if any of the output types require codegen or linking. - pub fn should_trans(&self) -> bool { + pub fn should_codegen(&self) -> bool { self.0.keys().any(|k| match *k { OutputType::Bitcode | OutputType::Assembly @@ -1135,14 +1135,14 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options, "count where LLVM instrs originate"), time_llvm_passes: bool = (false, parse_bool, [UNTRACKED_WITH_WARNING(true, "The output of `-Z time-llvm-passes` will only reflect timings of \ - re-translated modules when used with incremental compilation" )], + re-codegened modules when used with incremental compilation" )], "measure time of each LLVM pass"), input_stats: bool = (false, parse_bool, [UNTRACKED], "gather statistics about the input"), - trans_stats: bool = (false, parse_bool, [UNTRACKED_WITH_WARNING(true, - "The output of `-Z trans-stats` might not be accurate when incremental \ + codegen_stats: bool = (false, parse_bool, [UNTRACKED_WITH_WARNING(true, + "The output of `-Z codegen-stats` might not be accurate when incremental \ compilation is enabled")], - "gather trans statistics"), + "gather codegen statistics"), asm_comments: bool = (false, parse_bool, [TRACKED], "generate comments into the assembly (may change behavior)"), no_verify: bool = (false, parse_bool, [TRACKED], @@ -1183,8 +1183,8 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options, Use with RUST_REGION_GRAPH=help for more info"), parse_only: bool = (false, parse_bool, [UNTRACKED], "parse only; do not compile, assemble, or link"), - no_trans: bool = (false, parse_bool, [TRACKED], - "run all passes except translation; no output"), + no_codegen: bool = (false, parse_bool, [TRACKED], + "run all passes except codegen; no output"), treat_err_as_bug: bool = (false, parse_bool, [TRACKED], "treat all errors that occur as bugs"), external_macro_backtrace: bool = (false, parse_bool, [UNTRACKED], @@ -1235,8 +1235,8 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options, "show spans for compiler debugging (expr|pat|ty)"), print_type_sizes: bool = (false, parse_bool, [UNTRACKED], "print layout information for each type encountered"), - print_trans_items: Option = (None, parse_opt_string, [UNTRACKED], - "print the result of the translation item collection pass"), + print_mono_items: Option = (None, parse_opt_string, [UNTRACKED], + "print the result of the monomorphization collection pass"), mir_opt_level: usize = (1, parse_uint, [TRACKED], "set the MIR optimization level (0-3, default: 1)"), mutable_noalias: bool = (false, parse_bool, [TRACKED], @@ -1244,7 +1244,7 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options, arg_align_attributes: bool = (false, parse_bool, [TRACKED], "emit align metadata for reference arguments"), dump_mir: Option = (None, parse_opt_string, [UNTRACKED], - "dump MIR state at various points in translation"), + "dump MIR state at various points in transforms"), dump_mir_dir: String = (String::from("mir_dump"), parse_string, [UNTRACKED], "the directory the MIR is dumped into"), dump_mir_graphviz: bool = (false, parse_bool, [UNTRACKED], @@ -1296,8 +1296,8 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options, "dump facts from NLL analysis into side files"), disable_nll_user_type_assert: bool = (false, parse_bool, [UNTRACKED], "disable user provided type assertion in NLL"), - trans_time_graph: bool = (false, parse_bool, [UNTRACKED], - "generate a graphical HTML report of time spent in trans and LLVM"), + codegen_time_graph: bool = (false, parse_bool, [UNTRACKED], + "generate a graphical HTML report of time spent in codegen and LLVM"), thinlto: Option = (None, parse_opt_bool, [TRACKED], "enable ThinLTO when possible"), inline_in_all_cgus: Option = (None, parse_opt_bool, [TRACKED], @@ -1309,7 +1309,7 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options, the max/min integer respectively, and NaN is mapped to 0"), lower_128bit_ops: Option = (None, parse_opt_bool, [TRACKED], "rewrite operators on i128 and u128 into lang item calls (typically provided \ - by compiler-builtins) so translation doesn't need to support them, + by compiler-builtins) so codegen doesn't need to support them, overriding the default for the current target"), human_readable_cgu_names: bool = (false, parse_bool, [TRACKED], "generate human-readable, predictable names for codegen units"), @@ -3047,7 +3047,7 @@ mod tests { assert_eq!(reference.dep_tracking_hash(), opts.dep_tracking_hash()); opts.debugging_opts.input_stats = true; assert_eq!(reference.dep_tracking_hash(), opts.dep_tracking_hash()); - opts.debugging_opts.trans_stats = true; + opts.debugging_opts.codegen_stats = true; assert_eq!(reference.dep_tracking_hash(), opts.dep_tracking_hash()); opts.debugging_opts.borrowck_stats = true; assert_eq!(reference.dep_tracking_hash(), opts.dep_tracking_hash()); @@ -3093,7 +3093,7 @@ mod tests { assert_eq!(reference.dep_tracking_hash(), opts.dep_tracking_hash()); opts.debugging_opts.keep_ast = true; assert_eq!(reference.dep_tracking_hash(), opts.dep_tracking_hash()); - opts.debugging_opts.print_trans_items = Some(String::from("abc")); + opts.debugging_opts.print_mono_items = Some(String::from("abc")); assert_eq!(reference.dep_tracking_hash(), opts.dep_tracking_hash()); opts.debugging_opts.dump_mir = Some(String::from("abc")); assert_eq!(reference.dep_tracking_hash(), opts.dep_tracking_hash()); @@ -3120,7 +3120,7 @@ mod tests { assert!(reference.dep_tracking_hash() != opts.dep_tracking_hash()); opts = reference.clone(); - opts.debugging_opts.no_trans = true; + opts.debugging_opts.no_codegen = true; assert!(reference.dep_tracking_hash() != opts.dep_tracking_hash()); opts = reference.clone(); diff --git a/src/librustc/session/mod.rs b/src/librustc/session/mod.rs index bbf873290a9..6c8d4f5669e 100644 --- a/src/librustc/session/mod.rs +++ b/src/librustc/session/mod.rs @@ -98,7 +98,7 @@ pub struct Session { /// arguments passed to the compiler. Its value together with the crate-name /// forms a unique global identifier for the crate. It is used to allow /// multiple crates with the same name to coexist. See the - /// trans::back::symbol_names module for more information. + /// rustc_codegen_llvm::back::symbol_names module for more information. pub crate_disambiguator: Once, features: Once, @@ -504,8 +504,8 @@ impl Session { pub fn time_llvm_passes(&self) -> bool { self.opts.debugging_opts.time_llvm_passes } - pub fn trans_stats(&self) -> bool { - self.opts.debugging_opts.trans_stats + pub fn codegen_stats(&self) -> bool { + self.opts.debugging_opts.codegen_stats } pub fn meta_stats(&self) -> bool { self.opts.debugging_opts.meta_stats @@ -894,11 +894,11 @@ impl Session { // Why is 16 codegen units the default all the time? // // The main reason for enabling multiple codegen units by default is to - // leverage the ability for the trans backend to do translation and - // codegen in parallel. This allows us, especially for large crates, to + // leverage the ability for the codegen backend to do codegen and + // optimization in parallel. This allows us, especially for large crates, to // make good use of all available resources on the machine once we've // hit that stage of compilation. Large crates especially then often - // take a long time in trans/codegen and this helps us amortize that + // take a long time in codegen/optimization and this helps us amortize that // cost. // // Note that a high number here doesn't mean that we'll be spawning a diff --git a/src/librustc/traits/codegen/mod.rs b/src/librustc/traits/codegen/mod.rs new file mode 100644 index 00000000000..cf404202ac1 --- /dev/null +++ b/src/librustc/traits/codegen/mod.rs @@ -0,0 +1,184 @@ +// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// This file contains various trait resolution methods used by codegen. +// They all assume regions can be erased and monomorphic types. It +// seems likely that they should eventually be merged into more +// general routines. + +use dep_graph::{DepKind, DepTrackingMapConfig}; +use std::marker::PhantomData; +use syntax_pos::DUMMY_SP; +use infer::InferCtxt; +use syntax_pos::Span; +use traits::{FulfillmentContext, Obligation, ObligationCause, SelectionContext, + TraitEngine, Vtable}; +use ty::{self, Ty, TyCtxt}; +use ty::subst::{Subst, Substs}; +use ty::fold::TypeFoldable; + +/// Attempts to resolve an obligation to a vtable.. The result is +/// a shallow vtable resolution -- meaning that we do not +/// (necessarily) resolve all nested obligations on the impl. Note +/// that type check should guarantee to us that all nested +/// obligations *could be* resolved if we wanted to. +/// Assumes that this is run after the entire crate has been successfully type-checked. +pub fn codegen_fulfill_obligation<'a, 'tcx>(ty: TyCtxt<'a, 'tcx, 'tcx>, + (param_env, trait_ref): + (ty::ParamEnv<'tcx>, ty::PolyTraitRef<'tcx>)) + -> Vtable<'tcx, ()> +{ + // Remove any references to regions; this helps improve caching. + let trait_ref = ty.erase_regions(&trait_ref); + + debug!("codegen_fulfill_obligation(trait_ref={:?}, def_id={:?})", + (param_env, trait_ref), trait_ref.def_id()); + + // Do the initial selection for the obligation. This yields the + // shallow result we are looking for -- that is, what specific impl. + ty.infer_ctxt().enter(|infcx| { + let mut selcx = SelectionContext::new(&infcx); + + let obligation_cause = ObligationCause::dummy(); + let obligation = Obligation::new(obligation_cause, + param_env, + trait_ref.to_poly_trait_predicate()); + + let selection = match selcx.select(&obligation) { + Ok(Some(selection)) => selection, + Ok(None) => { + // Ambiguity can happen when monomorphizing during trans + // expands to some humongo type that never occurred + // statically -- this humongo type can then overflow, + // leading to an ambiguous result. So report this as an + // overflow bug, since I believe this is the only case + // where ambiguity can result. + bug!("Encountered ambiguity selecting `{:?}` during codegen, \ + presuming due to overflow", + trait_ref) + } + Err(e) => { + bug!("Encountered error `{:?}` selecting `{:?}` during codegen", + e, trait_ref) + } + }; + + debug!("fulfill_obligation: selection={:?}", selection); + + // Currently, we use a fulfillment context to completely resolve + // all nested obligations. This is because they can inform the + // inference of the impl's type parameters. + let mut fulfill_cx = FulfillmentContext::new(); + let vtable = selection.map(|predicate| { + debug!("fulfill_obligation: register_predicate_obligation {:?}", predicate); + fulfill_cx.register_predicate_obligation(&infcx, predicate); + }); + let vtable = infcx.drain_fulfillment_cx_or_panic(DUMMY_SP, &mut fulfill_cx, &vtable); + + info!("Cache miss: {:?} => {:?}", trait_ref, vtable); + vtable + }) +} + +impl<'a, 'tcx> TyCtxt<'a, 'tcx, 'tcx> { + /// Monomorphizes a type from the AST by first applying the + /// in-scope substitutions and then normalizing any associated + /// types. + pub fn subst_and_normalize_erasing_regions( + self, + param_substs: &Substs<'tcx>, + param_env: ty::ParamEnv<'tcx>, + value: &T + ) -> T + where + T: TypeFoldable<'tcx>, + { + debug!( + "subst_and_normalize_erasing_regions(\ + param_substs={:?}, \ + value={:?}, \ + param_env={:?})", + param_substs, + value, + param_env, + ); + let substituted = value.subst(self, param_substs); + self.normalize_erasing_regions(param_env, substituted) + } +} + +// Implement DepTrackingMapConfig for `trait_cache` +pub struct TraitSelectionCache<'tcx> { + data: PhantomData<&'tcx ()> +} + +impl<'tcx> DepTrackingMapConfig for TraitSelectionCache<'tcx> { + type Key = (ty::ParamEnv<'tcx>, ty::PolyTraitRef<'tcx>); + type Value = Vtable<'tcx, ()>; + fn to_dep_kind() -> DepKind { + DepKind::TraitSelect + } +} + +// # Global Cache + +pub struct ProjectionCache<'gcx> { + data: PhantomData<&'gcx ()> +} + +impl<'gcx> DepTrackingMapConfig for ProjectionCache<'gcx> { + type Key = Ty<'gcx>; + type Value = Ty<'gcx>; + fn to_dep_kind() -> DepKind { + DepKind::TraitSelect + } +} + +impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { + /// Finishes processes any obligations that remain in the + /// fulfillment context, and then returns the result with all type + /// variables removed and regions erased. Because this is intended + /// for use after type-check has completed, if any errors occur, + /// it will panic. It is used during normalization and other cases + /// where processing the obligations in `fulfill_cx` may cause + /// type inference variables that appear in `result` to be + /// unified, and hence we need to process those obligations to get + /// the complete picture of the type. + fn drain_fulfillment_cx_or_panic(&self, + span: Span, + fulfill_cx: &mut FulfillmentContext<'tcx>, + result: &T) + -> T::Lifted + where T: TypeFoldable<'tcx> + ty::Lift<'gcx> + { + debug!("drain_fulfillment_cx_or_panic()"); + + // In principle, we only need to do this so long as `result` + // contains unbound type parameters. It could be a slight + // optimization to stop iterating early. + match fulfill_cx.select_all_or_error(self) { + Ok(()) => { } + Err(errors) => { + span_bug!(span, "Encountered errors `{:?}` resolving bounds after type-checking", + errors); + } + } + + let result = self.resolve_type_vars_if_possible(result); + let result = self.tcx.erase_regions(&result); + + match self.tcx.lift_to_global(&result) { + Some(result) => result, + None => { + span_bug!(span, "Uninferred types/regions in `{:?}`", result); + } + } + } +} diff --git a/src/librustc/traits/mod.rs b/src/librustc/traits/mod.rs index f4f0c47899d..b5115eab7dc 100644 --- a/src/librustc/traits/mod.rs +++ b/src/librustc/traits/mod.rs @@ -64,7 +64,7 @@ mod on_unimplemented; mod select; mod specialize; mod structural_impls; -pub mod trans; +pub mod codegen; mod util; pub mod query; @@ -473,8 +473,8 @@ pub enum Vtable<'tcx, N> { /// /// The type parameter `N` indicates the type used for "nested /// obligations" that are required by the impl. During type check, this -/// is `Obligation`, as one might expect. During trans, however, this -/// is `()`, because trans only requires a shallow resolution of an +/// is `Obligation`, as one might expect. During codegen, however, this +/// is `()`, because codegen only requires a shallow resolution of an /// impl, and nested obligations are satisfied later. #[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable)] pub struct VtableImplData<'tcx, N> { @@ -856,7 +856,7 @@ fn vtable_methods<'a, 'tcx>( // It's possible that the method relies on where clauses that // do not hold for this particular set of type parameters. // Note that this method could then never be called, so we - // do not want to try and trans it, in that case (see #23435). + // do not want to try and codegen it, in that case (see #23435). let predicates = tcx.predicates_of(def_id).instantiate_own(tcx, substs); if !normalize_and_test_predicates(tcx, predicates.predicates) { debug!("vtable_methods: predicates do not hold"); @@ -992,7 +992,7 @@ pub fn provide(providers: &mut ty::maps::Providers) { is_object_safe: object_safety::is_object_safe_provider, specialization_graph_of: specialize::specialization_graph_provider, specializes: specialize::specializes, - trans_fulfill_obligation: trans::trans_fulfill_obligation, + codegen_fulfill_obligation: codegen::codegen_fulfill_obligation, vtable_methods, substitute_normalize_and_test_predicates, ..*providers diff --git a/src/librustc/traits/project.rs b/src/librustc/traits/project.rs index bfa32f8e7fa..270e06a872b 100644 --- a/src/librustc/traits/project.rs +++ b/src/librustc/traits/project.rs @@ -65,7 +65,7 @@ pub enum Reveal { /// } UserFacing, - /// At trans time, all monomorphic projections will succeed. + /// At codegen time, all monomorphic projections will succeed. /// Also, `impl Trait` is normalized to the concrete type, /// which has to be already collected by type-checking. /// @@ -346,7 +346,7 @@ impl<'a, 'b, 'gcx, 'tcx> TypeFolder<'gcx, 'tcx> for AssociatedTypeNormalizer<'a, let ty = ty.super_fold_with(self); match ty.sty { ty::TyAnon(def_id, substs) if !substs.has_escaping_regions() => { // (*) - // Only normalize `impl Trait` after type-checking, usually in trans. + // Only normalize `impl Trait` after type-checking, usually in codegen. match self.param_env.reveal { Reveal::UserFacing => ty, @@ -1054,7 +1054,7 @@ fn assemble_candidates_from_impls<'cx, 'gcx, 'tcx>( super::VtableImpl(impl_data) => { // We have to be careful when projecting out of an // impl because of specialization. If we are not in - // trans (i.e., projection mode is not "any"), and the + // codegen (i.e., projection mode is not "any"), and the // impl's type is declared as default, then we disable // projection (even if the trait ref is fully // monomorphic). In the case where trait ref is not diff --git a/src/librustc/traits/query/normalize.rs b/src/librustc/traits/query/normalize.rs index f074e061653..1e9e9c056c9 100644 --- a/src/librustc/traits/query/normalize.rs +++ b/src/librustc/traits/query/normalize.rs @@ -104,7 +104,7 @@ impl<'cx, 'gcx, 'tcx> TypeFolder<'gcx, 'tcx> for QueryNormalizer<'cx, 'gcx, 'tcx match ty.sty { ty::TyAnon(def_id, substs) if !substs.has_escaping_regions() => { // (*) - // Only normalize `impl Trait` after type-checking, usually in trans. + // Only normalize `impl Trait` after type-checking, usually in codegen. match self.param_env.reveal { Reveal::UserFacing => ty, diff --git a/src/librustc/traits/query/normalize_erasing_regions.rs b/src/librustc/traits/query/normalize_erasing_regions.rs index a9734e9c298..1cb96a3e33f 100644 --- a/src/librustc/traits/query/normalize_erasing_regions.rs +++ b/src/librustc/traits/query/normalize_erasing_regions.rs @@ -57,7 +57,7 @@ impl<'cx, 'tcx> TyCtxt<'cx, 'tcx, 'tcx> { /// /// NB. Currently, higher-ranked type bounds inhibit /// normalization. Therefore, each time we erase them in - /// translation, we need to normalize the contents. + /// codegen, we need to normalize the contents. pub fn normalize_erasing_late_bound_regions( self, param_env: ty::ParamEnv<'tcx>, diff --git a/src/librustc/traits/structural_impls.rs b/src/librustc/traits/structural_impls.rs index b9593047af4..2e2f7a2ad31 100644 --- a/src/librustc/traits/structural_impls.rs +++ b/src/librustc/traits/structural_impls.rs @@ -275,7 +275,7 @@ impl<'a, 'tcx> Lift<'tcx> for traits::ObligationCause<'a> { } } -// For trans only. +// For codegen only. impl<'a, 'tcx> Lift<'tcx> for traits::Vtable<'a, ()> { type Lifted = traits::Vtable<'tcx, ()>; fn lift_to_tcx<'b, 'gcx>(&self, tcx: TyCtxt<'b, 'gcx, 'tcx>) -> Option { diff --git a/src/librustc/traits/trans/mod.rs b/src/librustc/traits/trans/mod.rs deleted file mode 100644 index 31e851126d7..00000000000 --- a/src/librustc/traits/trans/mod.rs +++ /dev/null @@ -1,184 +0,0 @@ -// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -// This file contains various trait resolution methods used by trans. -// They all assume regions can be erased and monomorphic types. It -// seems likely that they should eventually be merged into more -// general routines. - -use dep_graph::{DepKind, DepTrackingMapConfig}; -use std::marker::PhantomData; -use syntax_pos::DUMMY_SP; -use infer::InferCtxt; -use syntax_pos::Span; -use traits::{FulfillmentContext, Obligation, ObligationCause, SelectionContext, - TraitEngine, Vtable}; -use ty::{self, Ty, TyCtxt}; -use ty::subst::{Subst, Substs}; -use ty::fold::TypeFoldable; - -/// Attempts to resolve an obligation to a vtable.. The result is -/// a shallow vtable resolution -- meaning that we do not -/// (necessarily) resolve all nested obligations on the impl. Note -/// that type check should guarantee to us that all nested -/// obligations *could be* resolved if we wanted to. -/// Assumes that this is run after the entire crate has been successfully type-checked. -pub fn trans_fulfill_obligation<'a, 'tcx>(ty: TyCtxt<'a, 'tcx, 'tcx>, - (param_env, trait_ref): - (ty::ParamEnv<'tcx>, ty::PolyTraitRef<'tcx>)) - -> Vtable<'tcx, ()> -{ - // Remove any references to regions; this helps improve caching. - let trait_ref = ty.erase_regions(&trait_ref); - - debug!("trans::fulfill_obligation(trait_ref={:?}, def_id={:?})", - (param_env, trait_ref), trait_ref.def_id()); - - // Do the initial selection for the obligation. This yields the - // shallow result we are looking for -- that is, what specific impl. - ty.infer_ctxt().enter(|infcx| { - let mut selcx = SelectionContext::new(&infcx); - - let obligation_cause = ObligationCause::dummy(); - let obligation = Obligation::new(obligation_cause, - param_env, - trait_ref.to_poly_trait_predicate()); - - let selection = match selcx.select(&obligation) { - Ok(Some(selection)) => selection, - Ok(None) => { - // Ambiguity can happen when monomorphizing during trans - // expands to some humongo type that never occurred - // statically -- this humongo type can then overflow, - // leading to an ambiguous result. So report this as an - // overflow bug, since I believe this is the only case - // where ambiguity can result. - bug!("Encountered ambiguity selecting `{:?}` during trans, \ - presuming due to overflow", - trait_ref) - } - Err(e) => { - bug!("Encountered error `{:?}` selecting `{:?}` during trans", - e, trait_ref) - } - }; - - debug!("fulfill_obligation: selection={:?}", selection); - - // Currently, we use a fulfillment context to completely resolve - // all nested obligations. This is because they can inform the - // inference of the impl's type parameters. - let mut fulfill_cx = FulfillmentContext::new(); - let vtable = selection.map(|predicate| { - debug!("fulfill_obligation: register_predicate_obligation {:?}", predicate); - fulfill_cx.register_predicate_obligation(&infcx, predicate); - }); - let vtable = infcx.drain_fulfillment_cx_or_panic(DUMMY_SP, &mut fulfill_cx, &vtable); - - info!("Cache miss: {:?} => {:?}", trait_ref, vtable); - vtable - }) -} - -impl<'a, 'tcx> TyCtxt<'a, 'tcx, 'tcx> { - /// Monomorphizes a type from the AST by first applying the - /// in-scope substitutions and then normalizing any associated - /// types. - pub fn subst_and_normalize_erasing_regions( - self, - param_substs: &Substs<'tcx>, - param_env: ty::ParamEnv<'tcx>, - value: &T - ) -> T - where - T: TypeFoldable<'tcx>, - { - debug!( - "subst_and_normalize_erasing_regions(\ - param_substs={:?}, \ - value={:?}, \ - param_env={:?})", - param_substs, - value, - param_env, - ); - let substituted = value.subst(self, param_substs); - self.normalize_erasing_regions(param_env, substituted) - } -} - -// Implement DepTrackingMapConfig for `trait_cache` -pub struct TraitSelectionCache<'tcx> { - data: PhantomData<&'tcx ()> -} - -impl<'tcx> DepTrackingMapConfig for TraitSelectionCache<'tcx> { - type Key = (ty::ParamEnv<'tcx>, ty::PolyTraitRef<'tcx>); - type Value = Vtable<'tcx, ()>; - fn to_dep_kind() -> DepKind { - DepKind::TraitSelect - } -} - -// # Global Cache - -pub struct ProjectionCache<'gcx> { - data: PhantomData<&'gcx ()> -} - -impl<'gcx> DepTrackingMapConfig for ProjectionCache<'gcx> { - type Key = Ty<'gcx>; - type Value = Ty<'gcx>; - fn to_dep_kind() -> DepKind { - DepKind::TraitSelect - } -} - -impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { - /// Finishes processes any obligations that remain in the - /// fulfillment context, and then returns the result with all type - /// variables removed and regions erased. Because this is intended - /// for use after type-check has completed, if any errors occur, - /// it will panic. It is used during normalization and other cases - /// where processing the obligations in `fulfill_cx` may cause - /// type inference variables that appear in `result` to be - /// unified, and hence we need to process those obligations to get - /// the complete picture of the type. - fn drain_fulfillment_cx_or_panic(&self, - span: Span, - fulfill_cx: &mut FulfillmentContext<'tcx>, - result: &T) - -> T::Lifted - where T: TypeFoldable<'tcx> + ty::Lift<'gcx> - { - debug!("drain_fulfillment_cx_or_panic()"); - - // In principle, we only need to do this so long as `result` - // contains unbound type parameters. It could be a slight - // optimization to stop iterating early. - match fulfill_cx.select_all_or_error(self) { - Ok(()) => { } - Err(errors) => { - span_bug!(span, "Encountered errors `{:?}` resolving bounds after type-checking", - errors); - } - } - - let result = self.resolve_type_vars_if_possible(result); - let result = self.tcx.erase_regions(&result); - - match self.tcx.lift_to_global(&result) { - Some(result) => result, - None => { - span_bug!(span, "Uninferred types/regions in `{:?}`", result); - } - } - } -} diff --git a/src/librustc/ty/adjustment.rs b/src/librustc/ty/adjustment.rs index a0c31e8b509..3263da8fda3 100644 --- a/src/librustc/ty/adjustment.rs +++ b/src/librustc/ty/adjustment.rs @@ -91,8 +91,8 @@ pub enum Adjust<'tcx> { /// pointers. We don't store the details of how the transform is /// done (in fact, we don't know that, because it might depend on /// the precise type parameters). We just store the target - /// type. Trans figures out what has to be done at monomorphization - /// time based on the precise source/target type at hand. + /// type. Codegen backends and miri figure out what has to be done + /// based on the precise source/target type at hand. Unsize, } diff --git a/src/librustc/ty/cast.rs b/src/librustc/ty/cast.rs index 908335424e7..7593d4ed24e 100644 --- a/src/librustc/ty/cast.rs +++ b/src/librustc/ty/cast.rs @@ -9,7 +9,7 @@ // except according to those terms. // Helpers for handling cast expressions, used in both -// typeck and trans. +// typeck and codegen. use ty::{self, Ty}; diff --git a/src/librustc/ty/context.rs b/src/librustc/ty/context.rs index 3c345fcd9ee..ee55b8dd767 100644 --- a/src/librustc/ty/context.rs +++ b/src/librustc/ty/context.rs @@ -401,7 +401,7 @@ pub struct TypeckTables<'tcx> { /// For each fn, records the "liberated" types of its arguments /// and return type. Liberated means that all bound regions /// (including late-bound regions) are replaced with free - /// equivalents. This table is not used in trans (since regions + /// equivalents. This table is not used in codegen (since regions /// are erased there) and hence is not serialized to metadata. liberated_fn_sigs: ItemLocalMap>, @@ -921,7 +921,7 @@ pub struct GlobalCtxt<'tcx> { /// A general purpose channel to throw data out the back towards LLVM worker /// threads. /// - /// This is intended to only get used during the trans phase of the compiler + /// This is intended to only get used during the codegen phase of the compiler /// when satisfying the query for a particular codegen unit. Internally in /// the query it'll send data along this channel to get processed later. pub tx_to_llvm_workers: Lock>>, diff --git a/src/librustc/ty/erase_regions.rs b/src/librustc/ty/erase_regions.rs index 4f8fca67949..f483b4c174a 100644 --- a/src/librustc/ty/erase_regions.rs +++ b/src/librustc/ty/erase_regions.rs @@ -68,7 +68,7 @@ impl<'a, 'gcx, 'tcx> TypeFolder<'gcx, 'tcx> for RegionEraserVisitor<'a, 'gcx, 't // // Note that we *CAN* replace early-bound regions -- the // type system never "sees" those, they get substituted - // away. In trans, they will always be erased to 'erased + // away. In codegen, they will always be erased to 'erased // whenever a substitution occurs. match *r { ty::ReLateBound(..) => r, @@ -76,4 +76,3 @@ impl<'a, 'gcx, 'tcx> TypeFolder<'gcx, 'tcx> for RegionEraserVisitor<'a, 'gcx, 't } } } - diff --git a/src/librustc/ty/fold.rs b/src/librustc/ty/fold.rs index 250f33d9dba..8aca7177d55 100644 --- a/src/librustc/ty/fold.rs +++ b/src/librustc/ty/fold.rs @@ -424,7 +424,7 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { collector.regions } - /// Replace any late-bound regions bound in `value` with `'erased`. Useful in trans but also + /// Replace any late-bound regions bound in `value` with `'erased`. Useful in codegen but also /// method lookup and a few other places where precise region relationships are not required. pub fn erase_late_bound_regions(self, value: &Binder) -> T where T : TypeFoldable<'tcx> diff --git a/src/librustc/ty/instance.rs b/src/librustc/ty/instance.rs index e7b71ca2b22..e97f782fccf 100644 --- a/src/librustc/ty/instance.rs +++ b/src/librustc/ty/instance.rs @@ -104,13 +104,13 @@ impl<'tcx> InstanceDef<'tcx> { return true } if let ty::InstanceDef::DropGlue(..) = *self { - // Drop glue wants to be instantiated at every translation + // Drop glue wants to be instantiated at every codegen // unit, but without an #[inline] hint. We should make this // available to normal end-users. return true } - let trans_fn_attrs = tcx.trans_fn_attrs(self.def_id()); - trans_fn_attrs.requests_inline() || tcx.is_const_fn(self.def_id()) + let codegen_fn_attrs = tcx.codegen_fn_attrs(self.def_id()); + codegen_fn_attrs.requests_inline() || tcx.is_const_fn(self.def_id()) } } @@ -145,7 +145,7 @@ impl<'a, 'b, 'tcx> Instance<'tcx> { pub fn new(def_id: DefId, substs: &'tcx Substs<'tcx>) -> Instance<'tcx> { assert!(!substs.has_escaping_regions(), - "substs of instance {:?} not normalized for trans: {:?}", + "substs of instance {:?} not normalized for codegen: {:?}", def_id, substs); Instance { def: InstanceDef::Item(def_id), substs: substs } } @@ -175,7 +175,7 @@ impl<'a, 'b, 'tcx> Instance<'tcx> { /// `RevealMode` in the parameter environment.) /// /// Presuming that coherence and type-check have succeeded, if this method is invoked - /// in a monomorphic context (i.e., like during trans), then it is guaranteed to return + /// in a monomorphic context (i.e., like during codegen), then it is guaranteed to return /// `Some`. pub fn resolve(tcx: TyCtxt<'a, 'tcx, 'tcx>, param_env: ty::ParamEnv<'tcx>, @@ -259,7 +259,7 @@ fn resolve_associated_item<'a, 'tcx>( def_id, trait_id, rcvr_substs); let trait_ref = ty::TraitRef::from_method(tcx, trait_id, rcvr_substs); - let vtbl = tcx.trans_fulfill_obligation((param_env, ty::Binder::bind(trait_ref))); + let vtbl = tcx.codegen_fulfill_obligation((param_env, ty::Binder::bind(trait_ref))); // Now that we know which impl is being used, we can dispatch to // the actual function: @@ -321,7 +321,7 @@ fn needs_fn_once_adapter_shim<'a, 'tcx>(actual_closure_kind: ty::ClosureKind, } (ty::ClosureKind::Fn, ty::ClosureKind::FnMut) => { // The closure fn `llfn` is a `fn(&self, ...)`. We want a - // `fn(&mut self, ...)`. In fact, at trans time, these are + // `fn(&mut self, ...)`. In fact, at codegen time, these are // basically the same thing, so we can just return llfn. Ok(false) } @@ -334,7 +334,7 @@ fn needs_fn_once_adapter_shim<'a, 'tcx>(actual_closure_kind: ty::ClosureKind, // fn call_once(self, ...) { call_mut(&self, ...) } // fn call_once(mut self, ...) { call_mut(&mut self, ...) } // - // These are both the same at trans time. + // These are both the same at codegen time. Ok(true) } (ty::ClosureKind::FnMut, _) | diff --git a/src/librustc/ty/layout.rs b/src/librustc/ty/layout.rs index bbfc6d883e9..dbc32f4dbdd 100644 --- a/src/librustc/ty/layout.rs +++ b/src/librustc/ty/layout.rs @@ -949,14 +949,14 @@ impl<'a, 'tcx> LayoutCx<'tcx, TyCtxt<'a, 'tcx, 'tcx>> { // because this discriminant will be loaded, and then stored into variable of // type calculated by typeck. Consider such case (a bug): typeck decided on // byte-sized discriminant, but layout thinks we need a 16-bit to store all - // discriminant values. That would be a bug, because then, in trans, in order + // discriminant values. That would be a bug, because then, in codegen, in order // to store this 16-bit discriminant into 8-bit sized temporary some of the // space necessary to represent would have to be discarded (or layout is wrong // on thinking it needs 16 bits) bug!("layout decided on a larger discriminant type ({:?}) than typeck ({:?})", min_ity, typeck_ity); // However, it is fine to make discr type however large (as an optimisation) - // after this point – we’ll just truncate the value we load in trans. + // after this point – we’ll just truncate the value we load in codegen. } // Check to see if we should use a different type for the @@ -1121,7 +1121,7 @@ impl<'a, 'tcx> LayoutCx<'tcx, TyCtxt<'a, 'tcx, 'tcx>> { // If we are running with `-Zprint-type-sizes`, record layouts for // dumping later. Ignore layouts that are done with non-empty // environments or non-monomorphic layouts, as the user only wants - // to see the stuff resulting from the final trans session. + // to see the stuff resulting from the final codegen session. if !self.tcx.sess.opts.debugging_opts.print_type_sizes || layout.ty.has_param_types() || diff --git a/src/librustc/ty/maps/config.rs b/src/librustc/ty/maps/config.rs index ad48519e136..19c97a918bb 100644 --- a/src/librustc/ty/maps/config.rs +++ b/src/librustc/ty/maps/config.rs @@ -349,7 +349,7 @@ impl<'tcx> QueryDescription<'tcx> for queries::is_mir_available<'tcx> { } } -impl<'tcx> QueryDescription<'tcx> for queries::trans_fulfill_obligation<'tcx> { +impl<'tcx> QueryDescription<'tcx> for queries::codegen_fulfill_obligation<'tcx> { fn describe(tcx: TyCtxt, key: (ty::ParamEnv<'tcx>, ty::PolyTraitRef<'tcx>)) -> String { format!("checking if `{}` fulfills its obligations", tcx.item_path_str(key.1.def_id())) } @@ -637,9 +637,9 @@ impl<'tcx> QueryDescription<'tcx> for queries::exported_symbols<'tcx> { } } -impl<'tcx> QueryDescription<'tcx> for queries::collect_and_partition_translation_items<'tcx> { +impl<'tcx> QueryDescription<'tcx> for queries::collect_and_partition_mono_items<'tcx> { fn describe(_tcx: TyCtxt, _: CrateNum) -> String { - format!("collect_and_partition_translation_items") + format!("collect_and_partition_mono_items") } } @@ -795,5 +795,5 @@ impl_disk_cacheable_query!(def_symbol_name, |_| true); impl_disk_cacheable_query!(type_of, |def_id| def_id.is_local()); impl_disk_cacheable_query!(predicates_of, |def_id| def_id.is_local()); impl_disk_cacheable_query!(used_trait_imports, |def_id| def_id.is_local()); -impl_disk_cacheable_query!(trans_fn_attrs, |_| true); +impl_disk_cacheable_query!(codegen_fn_attrs, |_| true); impl_disk_cacheable_query!(specialization_graph_of, |_| true); diff --git a/src/librustc/ty/maps/mod.rs b/src/librustc/ty/maps/mod.rs index 6e419627dd8..6556e47720c 100644 --- a/src/librustc/ty/maps/mod.rs +++ b/src/librustc/ty/maps/mod.rs @@ -11,7 +11,7 @@ use dep_graph::{DepConstructor, DepNode}; use hir::def_id::{CrateNum, DefId, DefIndex}; use hir::def::{Def, Export}; -use hir::{self, TraitCandidate, ItemLocalId, TransFnAttrs}; +use hir::{self, TraitCandidate, ItemLocalId, CodegenFnAttrs}; use hir::svh::Svh; use infer::canonical::{self, Canonical}; use lint; @@ -181,7 +181,7 @@ define_maps! { <'tcx> [] fn mir_validated: MirValidated(DefId) -> &'tcx Steal>, /// MIR after our optimization passes have run. This is MIR that is ready - /// for trans. This is also the only query that can fetch non-local MIR, at present. + /// for codegen. This is also the only query that can fetch non-local MIR, at present. [] fn optimized_mir: MirOptimized(DefId) -> &'tcx mir::Mir<'tcx>, /// The result of unsafety-checking this def-id. @@ -255,7 +255,7 @@ define_maps! { <'tcx> [] fn lookup_stability: LookupStability(DefId) -> Option<&'tcx attr::Stability>, [] fn lookup_deprecation_entry: LookupDeprecationEntry(DefId) -> Option, [] fn item_attrs: ItemAttrs(DefId) -> Lrc<[ast::Attribute]>, - [] fn trans_fn_attrs: trans_fn_attrs(DefId) -> TransFnAttrs, + [] fn codegen_fn_attrs: codegen_fn_attrs(DefId) -> CodegenFnAttrs, [] fn fn_arg_names: FnArgNames(DefId) -> Vec, /// Gets the rendered value of the specified constant or associated constant. /// Used by rustdoc. @@ -268,7 +268,7 @@ define_maps! { <'tcx> [] fn vtable_methods: vtable_methods_node(ty::PolyTraitRef<'tcx>) -> Lrc)>>>, - [] fn trans_fulfill_obligation: fulfill_obligation_dep_node( + [] fn codegen_fulfill_obligation: fulfill_obligation_dep_node( (ty::ParamEnv<'tcx>, ty::PolyTraitRef<'tcx>)) -> Vtable<'tcx, ()>, [] fn trait_impls_of: TraitImpls(DefId) -> Lrc, [] fn specialization_graph_of: SpecializationGraph(DefId) -> Lrc, @@ -402,10 +402,10 @@ define_maps! { <'tcx> [] fn exported_symbols: ExportedSymbols(CrateNum) -> Arc, SymbolExportLevel)>>, - [] fn collect_and_partition_translation_items: - collect_and_partition_translation_items_node(CrateNum) + [] fn collect_and_partition_mono_items: + collect_and_partition_mono_items_node(CrateNum) -> (Arc, Arc>>>), - [] fn is_translated_item: IsTranslatedItem(DefId) -> bool, + [] fn is_codegened_item: IsCodegenedItem(DefId) -> bool, [] fn codegen_unit: CodegenUnit(InternedString) -> Arc>, [] fn compile_codegen_unit: CompileCodegenUnit(InternedString) -> Stats, [] fn output_filenames: output_filenames_node(CrateNum) @@ -475,8 +475,8 @@ fn features_node<'tcx>(_: CrateNum) -> DepConstructor<'tcx> { DepConstructor::Features } -fn trans_fn_attrs<'tcx>(id: DefId) -> DepConstructor<'tcx> { - DepConstructor::TransFnAttrs { 0: id } +fn codegen_fn_attrs<'tcx>(id: DefId) -> DepConstructor<'tcx> { + DepConstructor::CodegenFnAttrs { 0: id } } fn erase_regions_ty<'tcx>(ty: Ty<'tcx>) -> DepConstructor<'tcx> { @@ -609,8 +609,8 @@ fn all_traits_node<'tcx>(_: CrateNum) -> DepConstructor<'tcx> { DepConstructor::AllTraits } -fn collect_and_partition_translation_items_node<'tcx>(_: CrateNum) -> DepConstructor<'tcx> { - DepConstructor::CollectAndPartitionTranslationItems +fn collect_and_partition_mono_items_node<'tcx>(_: CrateNum) -> DepConstructor<'tcx> { + DepConstructor::CollectAndPartitionMonoItems } fn output_filenames_node<'tcx>(_: CrateNum) -> DepConstructor<'tcx> { diff --git a/src/librustc/ty/maps/on_disk_cache.rs b/src/librustc/ty/maps/on_disk_cache.rs index cea2a03fd53..9ca90a06c4e 100644 --- a/src/librustc/ty/maps/on_disk_cache.rs +++ b/src/librustc/ty/maps/on_disk_cache.rs @@ -224,7 +224,7 @@ impl<'sess> OnDiskCache<'sess> { encode_query_results::(tcx, enc, qri)?; encode_query_results::(tcx, enc, qri)?; encode_query_results::(tcx, enc, qri)?; - encode_query_results::(tcx, enc, qri)?; + encode_query_results::(tcx, enc, qri)?; encode_query_results::(tcx, enc, qri)?; encode_query_results::(tcx, enc, qri)?; encode_query_results::(tcx, enc, qri)?; @@ -234,7 +234,7 @@ impl<'sess> OnDiskCache<'sess> { encode_query_results::(tcx, enc, qri)?; encode_query_results::(tcx, enc, qri)?; encode_query_results::(tcx, enc, qri)?; - encode_query_results::(tcx, enc, qri)?; + encode_query_results::(tcx, enc, qri)?; encode_query_results::(tcx, enc, qri)?; // const eval is special, it only encodes successfully evaluated constants diff --git a/src/librustc/ty/maps/plumbing.rs b/src/librustc/ty/maps/plumbing.rs index 65dcb7311d3..065b7939ac3 100644 --- a/src/librustc/ty/maps/plumbing.rs +++ b/src/librustc/ty/maps/plumbing.rs @@ -871,7 +871,7 @@ pub fn force_from_dep_node<'a, 'gcx, 'lcx>(tcx: TyCtxt<'a, 'gcx, 'lcx>, // Since we cannot reconstruct the query key of a DepNode::CodegenUnit, we // would always end up having to evaluate the first caller of the // `codegen_unit` query that *is* reconstructible. This might very well be - // the `compile_codegen_unit` query, thus re-translating the whole CGU just + // the `compile_codegen_unit` query, thus re-codegenning the whole CGU just // to re-trigger calling the `codegen_unit` query with the right key. At // that point we would already have re-done all the work we are trying to // avoid doing in the first place. @@ -1046,7 +1046,7 @@ pub fn force_from_dep_node<'a, 'gcx, 'lcx>(tcx: TyCtxt<'a, 'gcx, 'lcx>, } DepKind::IsMirAvailable => { force!(is_mir_available, def_id!()); } DepKind::ItemAttrs => { force!(item_attrs, def_id!()); } - DepKind::TransFnAttrs => { force!(trans_fn_attrs, def_id!()); } + DepKind::CodegenFnAttrs => { force!(codegen_fn_attrs, def_id!()); } DepKind::FnArgNames => { force!(fn_arg_names, def_id!()); } DepKind::RenderedConst => { force!(rendered_const, def_id!()); } DepKind::DylibDepFormats => { force!(dylib_dependency_formats, krate!()); } @@ -1121,10 +1121,10 @@ pub fn force_from_dep_node<'a, 'gcx, 'lcx>(tcx: TyCtxt<'a, 'gcx, 'lcx>, DepKind::AllTraits => { force!(all_traits, LOCAL_CRATE); } DepKind::AllCrateNums => { force!(all_crate_nums, LOCAL_CRATE); } DepKind::ExportedSymbols => { force!(exported_symbols, krate!()); } - DepKind::CollectAndPartitionTranslationItems => { - force!(collect_and_partition_translation_items, LOCAL_CRATE); + DepKind::CollectAndPartitionMonoItems => { + force!(collect_and_partition_mono_items, LOCAL_CRATE); } - DepKind::IsTranslatedItem => { force!(is_translated_item, def_id!()); } + DepKind::IsCodegenedItem => { force!(is_codegened_item, def_id!()); } DepKind::OutputFilenames => { force!(output_filenames, LOCAL_CRATE); } DepKind::TargetFeaturesWhitelist => { force!(target_features_whitelist, LOCAL_CRATE); } @@ -1207,6 +1207,6 @@ impl_load_from_cache!( GenericsOfItem => generics_of, PredicatesOfItem => predicates_of, UsedTraitImports => used_trait_imports, - TransFnAttrs => trans_fn_attrs, + CodegenFnAttrs => codegen_fn_attrs, SpecializationGraph => specialization_graph_of, ); diff --git a/src/librustc/ty/mod.rs b/src/librustc/ty/mod.rs index eb638d7c9a1..ce7feae0719 100644 --- a/src/librustc/ty/mod.rs +++ b/src/librustc/ty/mod.rs @@ -118,7 +118,7 @@ mod sty; // Data types /// The complete set of all analyses described in this module. This is -/// produced by the driver and fed to trans and later passes. +/// produced by the driver and fed to codegen and later passes. /// /// NB: These contents are being migrated into queries using the /// *on-demand* infrastructure. @@ -1426,7 +1426,7 @@ pub struct ParamEnv<'tcx> { /// into Obligations, and elaborated and normalized. pub caller_bounds: &'tcx Slice>, - /// Typically, this is `Reveal::UserFacing`, but during trans we + /// Typically, this is `Reveal::UserFacing`, but during codegen we /// want `Reveal::All` -- note that this is always paired with an /// empty environment. To get that, use `ParamEnv::reveal()`. pub reveal: traits::Reveal, @@ -1444,7 +1444,7 @@ impl<'tcx> ParamEnv<'tcx> { /// Construct a trait environment with no where clauses in scope /// where the values of all `impl Trait` and other hidden types /// are revealed. This is suitable for monomorphized, post-typeck - /// environments like trans or doing optimizations. + /// environments like codegen or doing optimizations. /// /// NB. If you want to have predicates in scope, use `ParamEnv::new`, /// or invoke `param_env.with_reveal_all()`. @@ -1462,7 +1462,7 @@ impl<'tcx> ParamEnv<'tcx> { /// Returns a new parameter environment with the same clauses, but /// which "reveals" the true results of projections in all cases /// (even for associated types that are specializable). This is - /// the desired behavior during trans and certain other special + /// the desired behavior during codegen and certain other special /// contexts; normally though we want to use `Reveal::UserFacing`, /// which is the default. pub fn with_reveal_all(self) -> Self { diff --git a/src/librustc/ty/sty.rs b/src/librustc/ty/sty.rs index 5c0217fc3f5..4493c668593 100644 --- a/src/librustc/ty/sty.rs +++ b/src/librustc/ty/sty.rs @@ -243,7 +243,7 @@ pub enum TypeVariants<'tcx> { /// out later. /// /// All right, you say, but why include the type parameters from the -/// original function then? The answer is that trans may need them +/// original function then? The answer is that codegen may need them /// when monomorphizing, and they may not appear in the upvars. A /// closure could capture no variables but still make use of some /// in-scope type parameter with a bound (e.g., if our example above @@ -273,7 +273,7 @@ pub struct ClosureSubsts<'tcx> { /// Lifetime and type parameters from the enclosing function, /// concatenated with the types of the upvars. /// - /// These are separated out because trans wants to pass them around + /// These are separated out because codegen wants to pass them around /// when monomorphizing. pub substs: &'tcx Substs<'tcx>, } @@ -1093,7 +1093,7 @@ pub enum RegionKind { /// variable with no constraints. ReEmpty, - /// Erased region, used by trait selection, in MIR and during trans. + /// Erased region, used by trait selection, in MIR and during codegen. ReErased, /// These are regions bound in the "defining type" for a diff --git a/src/librustc/util/ppaux.rs b/src/librustc/util/ppaux.rs index eaae874635f..c66dd79cbf5 100644 --- a/src/librustc/util/ppaux.rs +++ b/src/librustc/util/ppaux.rs @@ -1135,7 +1135,7 @@ define_print! { })? } else { // cross-crate closure types should only be - // visible in trans bug reports, I imagine. + // visible in codegen bug reports, I imagine. write!(f, "@{:?}", did)?; let mut sep = " "; for (index, upvar_ty) in upvar_tys.enumerate() { @@ -1175,7 +1175,7 @@ define_print! { })? } else { // cross-crate closure types should only be - // visible in trans bug reports, I imagine. + // visible in codegen bug reports, I imagine. write!(f, "@{:?}", did)?; let mut sep = " "; for (index, upvar_ty) in upvar_tys.enumerate() { diff --git a/src/librustc_borrowck/borrowck/README.md b/src/librustc_borrowck/borrowck/README.md index 29f03c06ab7..6fc0ed47b80 100644 --- a/src/librustc_borrowck/borrowck/README.md +++ b/src/librustc_borrowck/borrowck/README.md @@ -1126,7 +1126,7 @@ fn foo(a: [D; 10], b: [D; 10], i: i32, t: bool) -> D { } ``` -There are a number of ways that the trans backend could choose to +There are a number of ways that the codegen backend could choose to compile this (e.g. a `[bool; 10]` array for each such moved array; or an `Option` for each moved array). From the viewpoint of the borrow-checker, the important thing is to record what kind of fragment diff --git a/src/librustc_codegen_llvm/Cargo.toml b/src/librustc_codegen_llvm/Cargo.toml new file mode 100644 index 00000000000..7cf0a1c1bec --- /dev/null +++ b/src/librustc_codegen_llvm/Cargo.toml @@ -0,0 +1,49 @@ +[package] +authors = ["The Rust Project Developers"] +name = "rustc_codegen_llvm" +version = "0.0.0" + +[lib] +name = "rustc_codegen_llvm" +path = "lib.rs" +crate-type = ["dylib"] +test = false + +[dependencies] +bitflags = "1.0.1" +cc = "1.0.1" +flate2 = "1.0" +jobserver = "0.1.5" +libc = "0.2" +log = "0.4" +num_cpus = "1.0" +rustc = { path = "../librustc" } +rustc-demangle = "0.1.4" +rustc_allocator = { path = "../librustc_allocator" } +rustc_apfloat = { path = "../librustc_apfloat" } +rustc_target = { path = "../librustc_target" } +rustc_data_structures = { path = "../librustc_data_structures" } +rustc_errors = { path = "../librustc_errors" } +rustc_incremental = { path = "../librustc_incremental" } +rustc_llvm = { path = "../librustc_llvm" } +rustc_platform_intrinsics = { path = "../librustc_platform_intrinsics" } +rustc_codegen_utils = { path = "../librustc_codegen_utils" } +rustc_mir = { path = "../librustc_mir" } +serialize = { path = "../libserialize" } +syntax = { path = "../libsyntax" } +syntax_pos = { path = "../libsyntax_pos" } +tempdir = "0.3" + +# not actually used but needed to make sure we enable the same feature set as +# winapi used in librustc +env_logger = { version = "0.5", default-features = false } + +[features] +# Used to communicate the feature to `rustc_target` in the same manner that the +# `rustc` driver script communicate this. +jemalloc = ["rustc_target/jemalloc"] + +# This is used to convince Cargo to separately cache builds of `rustc_codegen_llvm` +# when this option is enabled or not. That way we can build two, cache two +# artifacts, and have nice speedy rebuilds. +emscripten = ["rustc_llvm/emscripten"] diff --git a/src/librustc_codegen_llvm/README.md b/src/librustc_codegen_llvm/README.md new file mode 100644 index 00000000000..8d1c9a52b24 --- /dev/null +++ b/src/librustc_codegen_llvm/README.md @@ -0,0 +1,7 @@ +The `codegen` crate contains the code to convert from MIR into LLVM IR, +and then from LLVM IR into machine code. In general it contains code +that runs towards the end of the compilation process. + +For more information about how codegen works, see the [rustc guide]. + +[rustc guide]: https://rust-lang-nursery.github.io/rustc-guide/codegen.html diff --git a/src/librustc_codegen_llvm/abi.rs b/src/librustc_codegen_llvm/abi.rs new file mode 100644 index 00000000000..25c598c532c --- /dev/null +++ b/src/librustc_codegen_llvm/abi.rs @@ -0,0 +1,696 @@ +// Copyright 2012-2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use llvm::{self, ValueRef, AttributePlace}; +use base; +use builder::{Builder, MemFlags}; +use common::{ty_fn_sig, C_usize}; +use context::CodegenCx; +use mir::place::PlaceRef; +use mir::operand::OperandValue; +use type_::Type; +use type_of::{LayoutLlvmExt, PointerKind}; + +use rustc_target::abi::{LayoutOf, Size, TyLayout}; +use rustc::ty::{self, Ty}; +use rustc::ty::layout; + +use libc::c_uint; + +pub use rustc_target::spec::abi::Abi; +pub use rustc::ty::layout::{FAT_PTR_ADDR, FAT_PTR_EXTRA}; +pub use rustc_target::abi::call::*; + +macro_rules! for_each_kind { + ($flags: ident, $f: ident, $($kind: ident),+) => ({ + $(if $flags.contains(ArgAttribute::$kind) { $f(llvm::Attribute::$kind) })+ + }) +} + +trait ArgAttributeExt { + fn for_each_kind(&self, f: F) where F: FnMut(llvm::Attribute); +} + +impl ArgAttributeExt for ArgAttribute { + fn for_each_kind(&self, mut f: F) where F: FnMut(llvm::Attribute) { + for_each_kind!(self, f, + ByVal, NoAlias, NoCapture, NonNull, ReadOnly, SExt, StructRet, ZExt, InReg) + } +} + +pub trait ArgAttributesExt { + fn apply_llfn(&self, idx: AttributePlace, llfn: ValueRef); + fn apply_callsite(&self, idx: AttributePlace, callsite: ValueRef); +} + +impl ArgAttributesExt for ArgAttributes { + fn apply_llfn(&self, idx: AttributePlace, llfn: ValueRef) { + let mut regular = self.regular; + unsafe { + let deref = self.pointee_size.bytes(); + if deref != 0 { + if regular.contains(ArgAttribute::NonNull) { + llvm::LLVMRustAddDereferenceableAttr(llfn, + idx.as_uint(), + deref); + } else { + llvm::LLVMRustAddDereferenceableOrNullAttr(llfn, + idx.as_uint(), + deref); + } + regular -= ArgAttribute::NonNull; + } + if let Some(align) = self.pointee_align { + llvm::LLVMRustAddAlignmentAttr(llfn, + idx.as_uint(), + align.abi() as u32); + } + regular.for_each_kind(|attr| attr.apply_llfn(idx, llfn)); + } + } + + fn apply_callsite(&self, idx: AttributePlace, callsite: ValueRef) { + let mut regular = self.regular; + unsafe { + let deref = self.pointee_size.bytes(); + if deref != 0 { + if regular.contains(ArgAttribute::NonNull) { + llvm::LLVMRustAddDereferenceableCallSiteAttr(callsite, + idx.as_uint(), + deref); + } else { + llvm::LLVMRustAddDereferenceableOrNullCallSiteAttr(callsite, + idx.as_uint(), + deref); + } + regular -= ArgAttribute::NonNull; + } + if let Some(align) = self.pointee_align { + llvm::LLVMRustAddAlignmentCallSiteAttr(callsite, + idx.as_uint(), + align.abi() as u32); + } + regular.for_each_kind(|attr| attr.apply_callsite(idx, callsite)); + } + } +} + +pub trait LlvmType { + fn llvm_type(&self, cx: &CodegenCx) -> Type; +} + +impl LlvmType for Reg { + fn llvm_type(&self, cx: &CodegenCx) -> Type { + match self.kind { + RegKind::Integer => Type::ix(cx, self.size.bits()), + RegKind::Float => { + match self.size.bits() { + 32 => Type::f32(cx), + 64 => Type::f64(cx), + _ => bug!("unsupported float: {:?}", self) + } + } + RegKind::Vector => { + Type::vector(&Type::i8(cx), self.size.bytes()) + } + } + } +} + +impl LlvmType for CastTarget { + fn llvm_type(&self, cx: &CodegenCx) -> Type { + let rest_ll_unit = self.rest.unit.llvm_type(cx); + let rest_count = self.rest.total.bytes() / self.rest.unit.size.bytes(); + let rem_bytes = self.rest.total.bytes() % self.rest.unit.size.bytes(); + + if self.prefix.iter().all(|x| x.is_none()) { + // Simplify to a single unit when there is no prefix and size <= unit size + if self.rest.total <= self.rest.unit.size { + return rest_ll_unit; + } + + // Simplify to array when all chunks are the same size and type + if rem_bytes == 0 { + return Type::array(&rest_ll_unit, rest_count); + } + } + + // Create list of fields in the main structure + let mut args: Vec<_> = + self.prefix.iter().flat_map(|option_kind| option_kind.map( + |kind| Reg { kind: kind, size: self.prefix_chunk }.llvm_type(cx))) + .chain((0..rest_count).map(|_| rest_ll_unit)) + .collect(); + + // Append final integer + if rem_bytes != 0 { + // Only integers can be really split further. + assert_eq!(self.rest.unit.kind, RegKind::Integer); + args.push(Type::ix(cx, rem_bytes * 8)); + } + + Type::struct_(cx, &args, false) + } +} + +pub trait ArgTypeExt<'a, 'tcx> { + fn memory_ty(&self, cx: &CodegenCx<'a, 'tcx>) -> Type; + fn store(&self, bx: &Builder<'a, 'tcx>, val: ValueRef, dst: PlaceRef<'tcx>); + fn store_fn_arg(&self, bx: &Builder<'a, 'tcx>, idx: &mut usize, dst: PlaceRef<'tcx>); +} + +impl<'a, 'tcx> ArgTypeExt<'a, 'tcx> for ArgType<'tcx, Ty<'tcx>> { + /// Get the LLVM type for a place of the original Rust type of + /// this argument/return, i.e. the result of `type_of::type_of`. + fn memory_ty(&self, cx: &CodegenCx<'a, 'tcx>) -> Type { + self.layout.llvm_type(cx) + } + + /// Store a direct/indirect value described by this ArgType into a + /// place for the original Rust type of this argument/return. + /// Can be used for both storing formal arguments into Rust variables + /// or results of call/invoke instructions into their destinations. + fn store(&self, bx: &Builder<'a, 'tcx>, val: ValueRef, dst: PlaceRef<'tcx>) { + if self.is_ignore() { + return; + } + let cx = bx.cx; + if self.is_indirect() { + OperandValue::Ref(val, self.layout.align).store(bx, dst) + } else if let PassMode::Cast(cast) = self.mode { + // FIXME(eddyb): Figure out when the simpler Store is safe, clang + // uses it for i16 -> {i8, i8}, but not for i24 -> {i8, i8, i8}. + let can_store_through_cast_ptr = false; + if can_store_through_cast_ptr { + let cast_dst = bx.pointercast(dst.llval, cast.llvm_type(cx).ptr_to()); + bx.store(val, cast_dst, self.layout.align); + } else { + // The actual return type is a struct, but the ABI + // adaptation code has cast it into some scalar type. The + // code that follows is the only reliable way I have + // found to do a transform like i64 -> {i32,i32}. + // Basically we dump the data onto the stack then memcpy it. + // + // Other approaches I tried: + // - Casting rust ret pointer to the foreign type and using Store + // is (a) unsafe if size of foreign type > size of rust type and + // (b) runs afoul of strict aliasing rules, yielding invalid + // assembly under -O (specifically, the store gets removed). + // - Truncating foreign type to correct integral type and then + // bitcasting to the struct type yields invalid cast errors. + + // We instead thus allocate some scratch space... + let scratch_size = cast.size(cx); + let scratch_align = cast.align(cx); + let llscratch = bx.alloca(cast.llvm_type(cx), "abi_cast", scratch_align); + bx.lifetime_start(llscratch, scratch_size); + + // ...where we first store the value... + bx.store(val, llscratch, scratch_align); + + // ...and then memcpy it to the intended destination. + base::call_memcpy(bx, + bx.pointercast(dst.llval, Type::i8p(cx)), + bx.pointercast(llscratch, Type::i8p(cx)), + C_usize(cx, self.layout.size.bytes()), + self.layout.align.min(scratch_align), + MemFlags::empty()); + + bx.lifetime_end(llscratch, scratch_size); + } + } else { + OperandValue::Immediate(val).store(bx, dst); + } + } + + fn store_fn_arg(&self, bx: &Builder<'a, 'tcx>, idx: &mut usize, dst: PlaceRef<'tcx>) { + let mut next = || { + let val = llvm::get_param(bx.llfn(), *idx as c_uint); + *idx += 1; + val + }; + match self.mode { + PassMode::Ignore => {}, + PassMode::Pair(..) => { + OperandValue::Pair(next(), next()).store(bx, dst); + } + PassMode::Direct(_) | PassMode::Indirect(_) | PassMode::Cast(_) => { + self.store(bx, next(), dst); + } + } + } +} + +pub trait FnTypeExt<'a, 'tcx> { + fn of_instance(cx: &CodegenCx<'a, 'tcx>, instance: &ty::Instance<'tcx>) + -> Self; + fn new(cx: &CodegenCx<'a, 'tcx>, + sig: ty::FnSig<'tcx>, + extra_args: &[Ty<'tcx>]) -> Self; + fn new_vtable(cx: &CodegenCx<'a, 'tcx>, + sig: ty::FnSig<'tcx>, + extra_args: &[Ty<'tcx>]) -> Self; + fn unadjusted(cx: &CodegenCx<'a, 'tcx>, + sig: ty::FnSig<'tcx>, + extra_args: &[Ty<'tcx>]) -> Self; + fn adjust_for_abi(&mut self, + cx: &CodegenCx<'a, 'tcx>, + abi: Abi); + fn llvm_type(&self, cx: &CodegenCx<'a, 'tcx>) -> Type; + fn llvm_cconv(&self) -> llvm::CallConv; + fn apply_attrs_llfn(&self, llfn: ValueRef); + fn apply_attrs_callsite(&self, bx: &Builder<'a, 'tcx>, callsite: ValueRef); +} + +impl<'a, 'tcx> FnTypeExt<'a, 'tcx> for FnType<'tcx, Ty<'tcx>> { + fn of_instance(cx: &CodegenCx<'a, 'tcx>, instance: &ty::Instance<'tcx>) + -> Self { + let fn_ty = instance.ty(cx.tcx); + let sig = ty_fn_sig(cx, fn_ty); + let sig = cx.tcx.normalize_erasing_late_bound_regions(ty::ParamEnv::reveal_all(), &sig); + FnType::new(cx, sig, &[]) + } + + fn new(cx: &CodegenCx<'a, 'tcx>, + sig: ty::FnSig<'tcx>, + extra_args: &[Ty<'tcx>]) -> Self { + let mut fn_ty = FnType::unadjusted(cx, sig, extra_args); + fn_ty.adjust_for_abi(cx, sig.abi); + fn_ty + } + + fn new_vtable(cx: &CodegenCx<'a, 'tcx>, + sig: ty::FnSig<'tcx>, + extra_args: &[Ty<'tcx>]) -> Self { + let mut fn_ty = FnType::unadjusted(cx, sig, extra_args); + // Don't pass the vtable, it's not an argument of the virtual fn. + { + let self_arg = &mut fn_ty.args[0]; + match self_arg.mode { + PassMode::Pair(data_ptr, _) => { + self_arg.mode = PassMode::Direct(data_ptr); + } + _ => bug!("FnType::new_vtable: non-pair self {:?}", self_arg) + } + + let pointee = self_arg.layout.ty.builtin_deref(true) + .unwrap_or_else(|| { + bug!("FnType::new_vtable: non-pointer self {:?}", self_arg) + }).ty; + let fat_ptr_ty = cx.tcx.mk_mut_ptr(pointee); + self_arg.layout = cx.layout_of(fat_ptr_ty).field(cx, 0); + } + fn_ty.adjust_for_abi(cx, sig.abi); + fn_ty + } + + fn unadjusted(cx: &CodegenCx<'a, 'tcx>, + sig: ty::FnSig<'tcx>, + extra_args: &[Ty<'tcx>]) -> Self { + debug!("FnType::unadjusted({:?}, {:?})", sig, extra_args); + + use self::Abi::*; + let conv = match cx.sess().target.target.adjust_abi(sig.abi) { + RustIntrinsic | PlatformIntrinsic | + Rust | RustCall => Conv::C, + + // It's the ABI's job to select this, not us. + System => bug!("system abi should be selected elsewhere"), + + Stdcall => Conv::X86Stdcall, + Fastcall => Conv::X86Fastcall, + Vectorcall => Conv::X86VectorCall, + Thiscall => Conv::X86ThisCall, + C => Conv::C, + Unadjusted => Conv::C, + Win64 => Conv::X86_64Win64, + SysV64 => Conv::X86_64SysV, + Aapcs => Conv::ArmAapcs, + PtxKernel => Conv::PtxKernel, + Msp430Interrupt => Conv::Msp430Intr, + X86Interrupt => Conv::X86Intr, + + // These API constants ought to be more specific... + Cdecl => Conv::C, + }; + + let mut inputs = sig.inputs(); + let extra_args = if sig.abi == RustCall { + assert!(!sig.variadic && extra_args.is_empty()); + + match sig.inputs().last().unwrap().sty { + ty::TyTuple(ref tupled_arguments) => { + inputs = &sig.inputs()[0..sig.inputs().len() - 1]; + tupled_arguments + } + _ => { + bug!("argument to function with \"rust-call\" ABI \ + is not a tuple"); + } + } + } else { + assert!(sig.variadic || extra_args.is_empty()); + extra_args + }; + + let target = &cx.sess().target.target; + let win_x64_gnu = target.target_os == "windows" + && target.arch == "x86_64" + && target.target_env == "gnu"; + let linux_s390x = target.target_os == "linux" + && target.arch == "s390x" + && target.target_env == "gnu"; + let rust_abi = match sig.abi { + RustIntrinsic | PlatformIntrinsic | Rust | RustCall => true, + _ => false + }; + + // Handle safe Rust thin and fat pointers. + let adjust_for_rust_scalar = |attrs: &mut ArgAttributes, + scalar: &layout::Scalar, + layout: TyLayout<'tcx, Ty<'tcx>>, + offset: Size, + is_return: bool| { + // Booleans are always an i1 that needs to be zero-extended. + if scalar.is_bool() { + attrs.set(ArgAttribute::ZExt); + return; + } + + // Only pointer types handled below. + if scalar.value != layout::Pointer { + return; + } + + if scalar.valid_range.start() < scalar.valid_range.end() { + if *scalar.valid_range.start() > 0 { + attrs.set(ArgAttribute::NonNull); + } + } + + if let Some(pointee) = layout.pointee_info_at(cx, offset) { + if let Some(kind) = pointee.safe { + attrs.pointee_size = pointee.size; + attrs.pointee_align = Some(pointee.align); + + // HACK(eddyb) LLVM inserts `llvm.assume` calls when inlining functions + // with align attributes, and those calls later block optimizations. + if !is_return && !cx.tcx.sess.opts.debugging_opts.arg_align_attributes { + attrs.pointee_align = None; + } + + // `Box` pointer parameters never alias because ownership is transferred + // `&mut` pointer parameters never alias other parameters, + // or mutable global data + // + // `&T` where `T` contains no `UnsafeCell` is immutable, + // and can be marked as both `readonly` and `noalias`, as + // LLVM's definition of `noalias` is based solely on memory + // dependencies rather than pointer equality + let no_alias = match kind { + PointerKind::Shared => false, + PointerKind::UniqueOwned => true, + PointerKind::Frozen | + PointerKind::UniqueBorrowed => !is_return + }; + if no_alias { + attrs.set(ArgAttribute::NoAlias); + } + + if kind == PointerKind::Frozen && !is_return { + attrs.set(ArgAttribute::ReadOnly); + } + } + } + }; + + let arg_of = |ty: Ty<'tcx>, is_return: bool| { + let mut arg = ArgType::new(cx.layout_of(ty)); + if arg.layout.is_zst() { + // For some forsaken reason, x86_64-pc-windows-gnu + // doesn't ignore zero-sized struct arguments. + // The same is true for s390x-unknown-linux-gnu. + if is_return || rust_abi || (!win_x64_gnu && !linux_s390x) { + arg.mode = PassMode::Ignore; + } + } + + // FIXME(eddyb) other ABIs don't have logic for scalar pairs. + if !is_return && rust_abi { + if let layout::Abi::ScalarPair(ref a, ref b) = arg.layout.abi { + let mut a_attrs = ArgAttributes::new(); + let mut b_attrs = ArgAttributes::new(); + adjust_for_rust_scalar(&mut a_attrs, + a, + arg.layout, + Size::from_bytes(0), + false); + adjust_for_rust_scalar(&mut b_attrs, + b, + arg.layout, + a.value.size(cx).abi_align(b.value.align(cx)), + false); + arg.mode = PassMode::Pair(a_attrs, b_attrs); + return arg; + } + } + + if let layout::Abi::Scalar(ref scalar) = arg.layout.abi { + if let PassMode::Direct(ref mut attrs) = arg.mode { + adjust_for_rust_scalar(attrs, + scalar, + arg.layout, + Size::from_bytes(0), + is_return); + } + } + + arg + }; + + FnType { + ret: arg_of(sig.output(), true), + args: inputs.iter().chain(extra_args.iter()).map(|ty| { + arg_of(ty, false) + }).collect(), + variadic: sig.variadic, + conv, + } + } + + fn adjust_for_abi(&mut self, + cx: &CodegenCx<'a, 'tcx>, + abi: Abi) { + if abi == Abi::Unadjusted { return } + + if abi == Abi::Rust || abi == Abi::RustCall || + abi == Abi::RustIntrinsic || abi == Abi::PlatformIntrinsic { + let fixup = |arg: &mut ArgType<'tcx, Ty<'tcx>>| { + if arg.is_ignore() { return; } + + match arg.layout.abi { + layout::Abi::Aggregate { .. } => {} + + // This is a fun case! The gist of what this is doing is + // that we want callers and callees to always agree on the + // ABI of how they pass SIMD arguments. If we were to *not* + // make these arguments indirect then they'd be immediates + // in LLVM, which means that they'd used whatever the + // appropriate ABI is for the callee and the caller. That + // means, for example, if the caller doesn't have AVX + // enabled but the callee does, then passing an AVX argument + // across this boundary would cause corrupt data to show up. + // + // This problem is fixed by unconditionally passing SIMD + // arguments through memory between callers and callees + // which should get them all to agree on ABI regardless of + // target feature sets. Some more information about this + // issue can be found in #44367. + // + // Note that the platform intrinsic ABI is exempt here as + // that's how we connect up to LLVM and it's unstable + // anyway, we control all calls to it in libstd. + layout::Abi::Vector { .. } if abi != Abi::PlatformIntrinsic => { + arg.make_indirect(); + return + } + + _ => return + } + + let size = arg.layout.size; + if size > layout::Pointer.size(cx) { + arg.make_indirect(); + } else { + // We want to pass small aggregates as immediates, but using + // a LLVM aggregate type for this leads to bad optimizations, + // so we pick an appropriately sized integer type instead. + arg.cast_to(Reg { + kind: RegKind::Integer, + size + }); + } + }; + fixup(&mut self.ret); + for arg in &mut self.args { + fixup(arg); + } + if let PassMode::Indirect(ref mut attrs) = self.ret.mode { + attrs.set(ArgAttribute::StructRet); + } + return; + } + + if let Err(msg) = self.adjust_for_cabi(cx, abi) { + cx.sess().fatal(&msg); + } + } + + fn llvm_type(&self, cx: &CodegenCx<'a, 'tcx>) -> Type { + let mut llargument_tys = Vec::new(); + + let llreturn_ty = match self.ret.mode { + PassMode::Ignore => Type::void(cx), + PassMode::Direct(_) | PassMode::Pair(..) => { + self.ret.layout.immediate_llvm_type(cx) + } + PassMode::Cast(cast) => cast.llvm_type(cx), + PassMode::Indirect(_) => { + llargument_tys.push(self.ret.memory_ty(cx).ptr_to()); + Type::void(cx) + } + }; + + for arg in &self.args { + // add padding + if let Some(ty) = arg.pad { + llargument_tys.push(ty.llvm_type(cx)); + } + + let llarg_ty = match arg.mode { + PassMode::Ignore => continue, + PassMode::Direct(_) => arg.layout.immediate_llvm_type(cx), + PassMode::Pair(..) => { + llargument_tys.push(arg.layout.scalar_pair_element_llvm_type(cx, 0)); + llargument_tys.push(arg.layout.scalar_pair_element_llvm_type(cx, 1)); + continue; + } + PassMode::Cast(cast) => cast.llvm_type(cx), + PassMode::Indirect(_) => arg.memory_ty(cx).ptr_to(), + }; + llargument_tys.push(llarg_ty); + } + + if self.variadic { + Type::variadic_func(&llargument_tys, &llreturn_ty) + } else { + Type::func(&llargument_tys, &llreturn_ty) + } + } + + fn llvm_cconv(&self) -> llvm::CallConv { + match self.conv { + Conv::C => llvm::CCallConv, + Conv::ArmAapcs => llvm::ArmAapcsCallConv, + Conv::Msp430Intr => llvm::Msp430Intr, + Conv::PtxKernel => llvm::PtxKernel, + Conv::X86Fastcall => llvm::X86FastcallCallConv, + Conv::X86Intr => llvm::X86_Intr, + Conv::X86Stdcall => llvm::X86StdcallCallConv, + Conv::X86ThisCall => llvm::X86_ThisCall, + Conv::X86VectorCall => llvm::X86_VectorCall, + Conv::X86_64SysV => llvm::X86_64_SysV, + Conv::X86_64Win64 => llvm::X86_64_Win64, + } + } + + fn apply_attrs_llfn(&self, llfn: ValueRef) { + let mut i = 0; + let mut apply = |attrs: &ArgAttributes| { + attrs.apply_llfn(llvm::AttributePlace::Argument(i), llfn); + i += 1; + }; + match self.ret.mode { + PassMode::Direct(ref attrs) => { + attrs.apply_llfn(llvm::AttributePlace::ReturnValue, llfn); + } + PassMode::Indirect(ref attrs) => apply(attrs), + _ => {} + } + for arg in &self.args { + if arg.pad.is_some() { + apply(&ArgAttributes::new()); + } + match arg.mode { + PassMode::Ignore => {} + PassMode::Direct(ref attrs) | + PassMode::Indirect(ref attrs) => apply(attrs), + PassMode::Pair(ref a, ref b) => { + apply(a); + apply(b); + } + PassMode::Cast(_) => apply(&ArgAttributes::new()), + } + } + } + + fn apply_attrs_callsite(&self, bx: &Builder<'a, 'tcx>, callsite: ValueRef) { + let mut i = 0; + let mut apply = |attrs: &ArgAttributes| { + attrs.apply_callsite(llvm::AttributePlace::Argument(i), callsite); + i += 1; + }; + match self.ret.mode { + PassMode::Direct(ref attrs) => { + attrs.apply_callsite(llvm::AttributePlace::ReturnValue, callsite); + } + PassMode::Indirect(ref attrs) => apply(attrs), + _ => {} + } + if let layout::Abi::Scalar(ref scalar) = self.ret.layout.abi { + // If the value is a boolean, the range is 0..2 and that ultimately + // become 0..0 when the type becomes i1, which would be rejected + // by the LLVM verifier. + match scalar.value { + layout::Int(..) if !scalar.is_bool() => { + let range = scalar.valid_range_exclusive(bx.cx); + if range.start != range.end { + // FIXME(nox): This causes very weird type errors about + // SHL operators in constants in stage 2 with LLVM 3.9. + if unsafe { llvm::LLVMRustVersionMajor() >= 4 } { + bx.range_metadata(callsite, range); + } + } + } + _ => {} + } + } + for arg in &self.args { + if arg.pad.is_some() { + apply(&ArgAttributes::new()); + } + match arg.mode { + PassMode::Ignore => {} + PassMode::Direct(ref attrs) | + PassMode::Indirect(ref attrs) => apply(attrs), + PassMode::Pair(ref a, ref b) => { + apply(a); + apply(b); + } + PassMode::Cast(_) => apply(&ArgAttributes::new()), + } + } + + let cconv = self.llvm_cconv(); + if cconv != llvm::CCallConv { + llvm::SetInstructionCallConv(callsite, cconv); + } + } +} diff --git a/src/librustc_codegen_llvm/allocator.rs b/src/librustc_codegen_llvm/allocator.rs new file mode 100644 index 00000000000..eeb02e948f1 --- /dev/null +++ b/src/librustc_codegen_llvm/allocator.rs @@ -0,0 +1,103 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use std::ffi::CString; +use std::ptr; + +use attributes; +use libc::c_uint; +use rustc::middle::allocator::AllocatorKind; +use rustc::ty::TyCtxt; +use rustc_allocator::{ALLOCATOR_METHODS, AllocatorTy}; + +use ModuleLlvm; +use llvm::{self, False, True}; + +pub(crate) unsafe fn codegen(tcx: TyCtxt, mods: &ModuleLlvm, kind: AllocatorKind) { + let llcx = mods.llcx; + let llmod = mods.llmod; + let usize = match &tcx.sess.target.target.target_pointer_width[..] { + "16" => llvm::LLVMInt16TypeInContext(llcx), + "32" => llvm::LLVMInt32TypeInContext(llcx), + "64" => llvm::LLVMInt64TypeInContext(llcx), + tws => bug!("Unsupported target word size for int: {}", tws), + }; + let i8 = llvm::LLVMInt8TypeInContext(llcx); + let i8p = llvm::LLVMPointerType(i8, 0); + let void = llvm::LLVMVoidTypeInContext(llcx); + + for method in ALLOCATOR_METHODS { + let mut args = Vec::new(); + for ty in method.inputs.iter() { + match *ty { + AllocatorTy::Layout => { + args.push(usize); // size + args.push(usize); // align + } + AllocatorTy::Ptr => args.push(i8p), + AllocatorTy::Usize => args.push(usize), + + AllocatorTy::ResultPtr | + AllocatorTy::Unit => panic!("invalid allocator arg"), + } + } + let output = match method.output { + AllocatorTy::ResultPtr => Some(i8p), + AllocatorTy::Unit => None, + + AllocatorTy::Layout | + AllocatorTy::Usize | + AllocatorTy::Ptr => panic!("invalid allocator output"), + }; + let ty = llvm::LLVMFunctionType(output.unwrap_or(void), + args.as_ptr(), + args.len() as c_uint, + False); + let name = CString::new(format!("__rust_{}", method.name)).unwrap(); + let llfn = llvm::LLVMRustGetOrInsertFunction(llmod, + name.as_ptr(), + ty); + + if tcx.sess.target.target.options.default_hidden_visibility { + llvm::LLVMRustSetVisibility(llfn, llvm::Visibility::Hidden); + } + if tcx.sess.target.target.options.requires_uwtable { + attributes::emit_uwtable(llfn, true); + } + + let callee = CString::new(kind.fn_name(method.name)).unwrap(); + let callee = llvm::LLVMRustGetOrInsertFunction(llmod, + callee.as_ptr(), + ty); + + let llbb = llvm::LLVMAppendBasicBlockInContext(llcx, + llfn, + "entry\0".as_ptr() as *const _); + + let llbuilder = llvm::LLVMCreateBuilderInContext(llcx); + llvm::LLVMPositionBuilderAtEnd(llbuilder, llbb); + let args = args.iter().enumerate().map(|(i, _)| { + llvm::LLVMGetParam(llfn, i as c_uint) + }).collect::>(); + let ret = llvm::LLVMRustBuildCall(llbuilder, + callee, + args.as_ptr(), + args.len() as c_uint, + ptr::null_mut(), + "\0".as_ptr() as *const _); + llvm::LLVMSetTailCall(ret, True); + if output.is_some() { + llvm::LLVMBuildRet(llbuilder, ret); + } else { + llvm::LLVMBuildRetVoid(llbuilder); + } + llvm::LLVMDisposeBuilder(llbuilder); + } +} diff --git a/src/librustc_codegen_llvm/asm.rs b/src/librustc_codegen_llvm/asm.rs new file mode 100644 index 00000000000..82f068106ff --- /dev/null +++ b/src/librustc_codegen_llvm/asm.rs @@ -0,0 +1,127 @@ +// Copyright 2012-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. + +use llvm::{self, ValueRef}; +use common::*; +use type_::Type; +use type_of::LayoutLlvmExt; +use builder::Builder; + +use rustc::hir; + +use mir::place::PlaceRef; +use mir::operand::OperandValue; + +use std::ffi::CString; +use syntax::ast::AsmDialect; +use libc::{c_uint, c_char}; + +// Take an inline assembly expression and splat it out via LLVM +pub fn codegen_inline_asm<'a, 'tcx>( + bx: &Builder<'a, 'tcx>, + ia: &hir::InlineAsm, + outputs: Vec>, + mut inputs: Vec +) { + let mut ext_constraints = vec![]; + let mut output_types = vec![]; + + // Prepare the output operands + let mut indirect_outputs = vec![]; + for (i, (out, place)) in ia.outputs.iter().zip(&outputs).enumerate() { + if out.is_rw { + inputs.push(place.load(bx).immediate()); + ext_constraints.push(i.to_string()); + } + if out.is_indirect { + indirect_outputs.push(place.load(bx).immediate()); + } else { + output_types.push(place.layout.llvm_type(bx.cx)); + } + } + if !indirect_outputs.is_empty() { + indirect_outputs.extend_from_slice(&inputs); + inputs = indirect_outputs; + } + + let clobbers = ia.clobbers.iter() + .map(|s| format!("~{{{}}}", &s)); + + // Default per-arch clobbers + // Basically what clang does + let arch_clobbers = match &bx.sess().target.target.arch[..] { + "x86" | "x86_64" => vec!["~{dirflag}", "~{fpsr}", "~{flags}"], + "mips" | "mips64" => vec!["~{$1}"], + _ => Vec::new() + }; + + let all_constraints = + ia.outputs.iter().map(|out| out.constraint.to_string()) + .chain(ia.inputs.iter().map(|s| s.to_string())) + .chain(ext_constraints) + .chain(clobbers) + .chain(arch_clobbers.iter().map(|s| s.to_string())) + .collect::>().join(","); + + debug!("Asm Constraints: {}", &all_constraints); + + // Depending on how many outputs we have, the return type is different + let num_outputs = output_types.len(); + let output_type = match num_outputs { + 0 => Type::void(bx.cx), + 1 => output_types[0], + _ => Type::struct_(bx.cx, &output_types, false) + }; + + let dialect = match ia.dialect { + AsmDialect::Att => llvm::AsmDialect::Att, + AsmDialect::Intel => llvm::AsmDialect::Intel, + }; + + let asm = CString::new(ia.asm.as_str().as_bytes()).unwrap(); + let constraint_cstr = CString::new(all_constraints).unwrap(); + let r = bx.inline_asm_call( + asm.as_ptr(), + constraint_cstr.as_ptr(), + &inputs, + output_type, + ia.volatile, + ia.alignstack, + dialect + ); + + // Again, based on how many outputs we have + let outputs = ia.outputs.iter().zip(&outputs).filter(|&(ref o, _)| !o.is_indirect); + for (i, (_, &place)) in outputs.enumerate() { + let v = if num_outputs == 1 { r } else { bx.extract_value(r, i as u64) }; + OperandValue::Immediate(v).store(bx, place); + } + + // Store mark in a metadata node so we can map LLVM errors + // back to source locations. See #17552. + unsafe { + let key = "srcloc"; + let kind = llvm::LLVMGetMDKindIDInContext(bx.cx.llcx, + key.as_ptr() as *const c_char, key.len() as c_uint); + + let val: llvm::ValueRef = C_i32(bx.cx, ia.ctxt.outer().as_u32() as i32); + + llvm::LLVMSetMetadata(r, kind, + llvm::LLVMMDNodeInContext(bx.cx.llcx, &val, 1)); + } +} + +pub fn codegen_global_asm<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, + ga: &hir::GlobalAsm) { + let asm = CString::new(ga.asm.as_str().as_bytes()).unwrap(); + unsafe { + llvm::LLVMRustAppendModuleInlineAsm(cx.llmod, asm.as_ptr()); + } +} diff --git a/src/librustc_codegen_llvm/attributes.rs b/src/librustc_codegen_llvm/attributes.rs new file mode 100644 index 00000000000..b64e102ba78 --- /dev/null +++ b/src/librustc_codegen_llvm/attributes.rs @@ -0,0 +1,259 @@ +// Copyright 2012-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. +//! Set and unset common attributes on LLVM values. + +use std::ffi::{CStr, CString}; + +use rustc::hir::{self, CodegenFnAttrFlags}; +use rustc::hir::def_id::{DefId, LOCAL_CRATE}; +use rustc::hir::itemlikevisit::ItemLikeVisitor; +use rustc::session::Session; +use rustc::session::config::Sanitizer; +use rustc::ty::TyCtxt; +use rustc::ty::maps::Providers; +use rustc_data_structures::sync::Lrc; +use rustc_data_structures::fx::FxHashMap; + +use llvm::{self, Attribute, ValueRef}; +use llvm::AttributePlace::Function; +use llvm_util; +pub use syntax::attr::{self, InlineAttr}; +use context::CodegenCx; + +/// Mark LLVM function to use provided inline heuristic. +#[inline] +pub fn inline(val: ValueRef, inline: InlineAttr) { + use self::InlineAttr::*; + match inline { + Hint => Attribute::InlineHint.apply_llfn(Function, val), + Always => Attribute::AlwaysInline.apply_llfn(Function, val), + Never => Attribute::NoInline.apply_llfn(Function, val), + None => { + Attribute::InlineHint.unapply_llfn(Function, val); + Attribute::AlwaysInline.unapply_llfn(Function, val); + Attribute::NoInline.unapply_llfn(Function, val); + }, + }; +} + +/// Tell LLVM to emit or not emit the information necessary to unwind the stack for the function. +#[inline] +pub fn emit_uwtable(val: ValueRef, emit: bool) { + Attribute::UWTable.toggle_llfn(Function, val, emit); +} + +/// Tell LLVM whether the function can or cannot unwind. +#[inline] +pub fn unwind(val: ValueRef, can_unwind: bool) { + Attribute::NoUnwind.toggle_llfn(Function, val, !can_unwind); +} + +/// Tell LLVM whether it should optimize function for size. +#[inline] +#[allow(dead_code)] // possibly useful function +pub fn set_optimize_for_size(val: ValueRef, optimize: bool) { + Attribute::OptimizeForSize.toggle_llfn(Function, val, optimize); +} + +/// Tell LLVM if this function should be 'naked', i.e. skip the epilogue and prologue. +#[inline] +pub fn naked(val: ValueRef, is_naked: bool) { + Attribute::Naked.toggle_llfn(Function, val, is_naked); +} + +pub fn set_frame_pointer_elimination(cx: &CodegenCx, llfn: ValueRef) { + if cx.sess().must_not_eliminate_frame_pointers() { + llvm::AddFunctionAttrStringValue( + llfn, llvm::AttributePlace::Function, + cstr("no-frame-pointer-elim\0"), cstr("true\0")); + } +} + +pub fn set_probestack(cx: &CodegenCx, llfn: ValueRef) { + // Only use stack probes if the target specification indicates that we + // should be using stack probes + if !cx.sess().target.target.options.stack_probes { + return + } + + // Currently stack probes seem somewhat incompatible with the address + // sanitizer. With asan we're already protected from stack overflow anyway + // so we don't really need stack probes regardless. + match cx.sess().opts.debugging_opts.sanitizer { + Some(Sanitizer::Address) => return, + _ => {} + } + + // probestack doesn't play nice either with pgo-gen. + if cx.sess().opts.debugging_opts.pgo_gen.is_some() { + return; + } + + // Flag our internal `__rust_probestack` function as the stack probe symbol. + // This is defined in the `compiler-builtins` crate for each architecture. + llvm::AddFunctionAttrStringValue( + llfn, llvm::AttributePlace::Function, + cstr("probe-stack\0"), cstr("__rust_probestack\0")); +} + +pub fn llvm_target_features(sess: &Session) -> impl Iterator { + const RUSTC_SPECIFIC_FEATURES: &[&str] = &[ + "crt-static", + ]; + + let cmdline = sess.opts.cg.target_feature.split(',') + .filter(|f| !RUSTC_SPECIFIC_FEATURES.iter().any(|s| f.contains(s))); + sess.target.target.options.features.split(',') + .chain(cmdline) + .filter(|l| !l.is_empty()) +} + +/// Composite function which sets LLVM attributes for function depending on its AST (#[attribute]) +/// attributes. +pub fn from_fn_attrs(cx: &CodegenCx, llfn: ValueRef, id: DefId) { + let codegen_fn_attrs = cx.tcx.codegen_fn_attrs(id); + + inline(llfn, codegen_fn_attrs.inline); + + set_frame_pointer_elimination(cx, llfn); + set_probestack(cx, llfn); + + if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::COLD) { + Attribute::Cold.apply_llfn(Function, llfn); + } + if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::NAKED) { + naked(llfn, true); + } + if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::ALLOCATOR) { + Attribute::NoAlias.apply_llfn( + llvm::AttributePlace::ReturnValue, llfn); + } + if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::UNWIND) { + unwind(llfn, true); + } + if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::RUSTC_ALLOCATOR_NOUNWIND) { + unwind(llfn, false); + } + + let features = llvm_target_features(cx.tcx.sess) + .map(|s| s.to_string()) + .chain( + codegen_fn_attrs.target_features + .iter() + .map(|f| { + let feature = &*f.as_str(); + format!("+{}", llvm_util::to_llvm_feature(cx.tcx.sess, feature)) + }) + ) + .collect::>() + .join(","); + + if !features.is_empty() { + let val = CString::new(features).unwrap(); + llvm::AddFunctionAttrStringValue( + llfn, llvm::AttributePlace::Function, + cstr("target-features\0"), &val); + } + + // Note that currently the `wasm-import-module` doesn't do anything, but + // eventually LLVM 7 should read this and ferry the appropriate import + // module to the output file. + if cx.tcx.sess.target.target.arch == "wasm32" { + if let Some(module) = wasm_import_module(cx.tcx, id) { + llvm::AddFunctionAttrStringValue( + llfn, + llvm::AttributePlace::Function, + cstr("wasm-import-module\0"), + &module, + ); + } + } +} + +fn cstr(s: &'static str) -> &CStr { + CStr::from_bytes_with_nul(s.as_bytes()).expect("null-terminated string") +} + +pub fn provide(providers: &mut Providers) { + providers.target_features_whitelist = |tcx, cnum| { + assert_eq!(cnum, LOCAL_CRATE); + if tcx.sess.opts.actually_rustdoc { + // rustdoc needs to be able to document functions that use all the features, so + // whitelist them all + Lrc::new(llvm_util::all_known_features() + .map(|(a, b)| (a.to_string(), b.map(|s| s.to_string()))) + .collect()) + } else { + Lrc::new(llvm_util::target_feature_whitelist(tcx.sess) + .iter() + .map(|&(a, b)| (a.to_string(), b.map(|s| s.to_string()))) + .collect()) + } + }; + + providers.wasm_custom_sections = |tcx, cnum| { + assert_eq!(cnum, LOCAL_CRATE); + let mut finder = WasmSectionFinder { tcx, list: Vec::new() }; + tcx.hir.krate().visit_all_item_likes(&mut finder); + Lrc::new(finder.list) + }; + + provide_extern(providers); +} + +struct WasmSectionFinder<'a, 'tcx: 'a> { + tcx: TyCtxt<'a, 'tcx, 'tcx>, + list: Vec, +} + +impl<'a, 'tcx: 'a> ItemLikeVisitor<'tcx> for WasmSectionFinder<'a, 'tcx> { + fn visit_item(&mut self, i: &'tcx hir::Item) { + match i.node { + hir::ItemConst(..) => {} + _ => return, + } + if i.attrs.iter().any(|i| i.check_name("wasm_custom_section")) { + self.list.push(self.tcx.hir.local_def_id(i.id)); + } + } + + fn visit_trait_item(&mut self, _: &'tcx hir::TraitItem) {} + + fn visit_impl_item(&mut self, _: &'tcx hir::ImplItem) {} +} + +pub fn provide_extern(providers: &mut Providers) { + providers.wasm_import_module_map = |tcx, cnum| { + let mut ret = FxHashMap(); + for lib in tcx.foreign_modules(cnum).iter() { + let attrs = tcx.get_attrs(lib.def_id); + let mut module = None; + for attr in attrs.iter().filter(|a| a.check_name("wasm_import_module")) { + module = attr.value_str(); + } + let module = match module { + Some(s) => s, + None => continue, + }; + for id in lib.foreign_items.iter() { + assert_eq!(id.krate, cnum); + ret.insert(*id, module.to_string()); + } + } + + Lrc::new(ret) + } +} + +fn wasm_import_module(tcx: TyCtxt, id: DefId) -> Option { + tcx.wasm_import_module_map(id.krate) + .get(&id) + .map(|s| CString::new(&s[..]).unwrap()) +} diff --git a/src/librustc_codegen_llvm/back/archive.rs b/src/librustc_codegen_llvm/back/archive.rs new file mode 100644 index 00000000000..609629bffb9 --- /dev/null +++ b/src/librustc_codegen_llvm/back/archive.rs @@ -0,0 +1,325 @@ +// Copyright 2013-2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! A helper class for dealing with static archives + +use std::ffi::{CString, CStr}; +use std::io; +use std::mem; +use std::path::{Path, PathBuf}; +use std::ptr; +use std::str; + +use back::bytecode::RLIB_BYTECODE_EXTENSION; +use libc; +use llvm::archive_ro::{ArchiveRO, Child}; +use llvm::{self, ArchiveKind}; +use metadata::METADATA_FILENAME; +use rustc::session::Session; + +pub struct ArchiveConfig<'a> { + pub sess: &'a Session, + pub dst: PathBuf, + pub src: Option, + pub lib_search_paths: Vec, +} + +/// Helper for adding many files to an archive. +#[must_use = "must call build() to finish building the archive"] +pub struct ArchiveBuilder<'a> { + config: ArchiveConfig<'a>, + removals: Vec, + additions: Vec, + should_update_symbols: bool, + src_archive: Option>, +} + +enum Addition { + File { + path: PathBuf, + name_in_archive: String, + }, + Archive { + archive: ArchiveRO, + skip: Box bool>, + }, +} + +pub fn find_library(name: &str, search_paths: &[PathBuf], sess: &Session) + -> PathBuf { + // On Windows, static libraries sometimes show up as libfoo.a and other + // times show up as foo.lib + let oslibname = format!("{}{}{}", + sess.target.target.options.staticlib_prefix, + name, + sess.target.target.options.staticlib_suffix); + let unixlibname = format!("lib{}.a", name); + + for path in search_paths { + debug!("looking for {} inside {:?}", name, path); + let test = path.join(&oslibname); + if test.exists() { return test } + if oslibname != unixlibname { + let test = path.join(&unixlibname); + if test.exists() { return test } + } + } + sess.fatal(&format!("could not find native static library `{}`, \ + perhaps an -L flag is missing?", name)); +} + +fn is_relevant_child(c: &Child) -> bool { + match c.name() { + Some(name) => !name.contains("SYMDEF"), + None => false, + } +} + +impl<'a> ArchiveBuilder<'a> { + /// Create a new static archive, ready for modifying the archive specified + /// by `config`. + pub fn new(config: ArchiveConfig<'a>) -> ArchiveBuilder<'a> { + ArchiveBuilder { + config, + removals: Vec::new(), + additions: Vec::new(), + should_update_symbols: false, + src_archive: None, + } + } + + /// Removes a file from this archive + pub fn remove_file(&mut self, file: &str) { + self.removals.push(file.to_string()); + } + + /// Lists all files in an archive + pub fn src_files(&mut self) -> Vec { + if self.src_archive().is_none() { + return Vec::new() + } + let archive = self.src_archive.as_ref().unwrap().as_ref().unwrap(); + let ret = archive.iter() + .filter_map(|child| child.ok()) + .filter(is_relevant_child) + .filter_map(|child| child.name()) + .filter(|name| !self.removals.iter().any(|x| x == name)) + .map(|name| name.to_string()) + .collect(); + return ret; + } + + fn src_archive(&mut self) -> Option<&ArchiveRO> { + if let Some(ref a) = self.src_archive { + return a.as_ref() + } + let src = self.config.src.as_ref()?; + self.src_archive = Some(ArchiveRO::open(src).ok()); + self.src_archive.as_ref().unwrap().as_ref() + } + + /// Adds all of the contents of a native library to this archive. This will + /// search in the relevant locations for a library named `name`. + pub fn add_native_library(&mut self, name: &str) { + let location = find_library(name, &self.config.lib_search_paths, + self.config.sess); + self.add_archive(&location, |_| false).unwrap_or_else(|e| { + self.config.sess.fatal(&format!("failed to add native library {}: {}", + location.to_string_lossy(), e)); + }); + } + + /// Adds all of the contents of the rlib at the specified path to this + /// archive. + /// + /// This ignores adding the bytecode from the rlib, and if LTO is enabled + /// then the object file also isn't added. + pub fn add_rlib(&mut self, + rlib: &Path, + name: &str, + lto: bool, + skip_objects: bool) -> io::Result<()> { + // Ignoring obj file starting with the crate name + // as simple comparison is not enough - there + // might be also an extra name suffix + let obj_start = format!("{}", name); + + self.add_archive(rlib, move |fname: &str| { + // Ignore bytecode/metadata files, no matter the name. + if fname.ends_with(RLIB_BYTECODE_EXTENSION) || fname == METADATA_FILENAME { + return true + } + + // Don't include Rust objects if LTO is enabled + if lto && fname.starts_with(&obj_start) && fname.ends_with(".o") { + return true + } + + // Otherwise if this is *not* a rust object and we're skipping + // objects then skip this file + if skip_objects && (!fname.starts_with(&obj_start) || !fname.ends_with(".o")) { + return true + } + + // ok, don't skip this + return false + }) + } + + fn add_archive(&mut self, archive: &Path, skip: F) + -> io::Result<()> + where F: FnMut(&str) -> bool + 'static + { + let archive = match ArchiveRO::open(archive) { + Ok(ar) => ar, + Err(e) => return Err(io::Error::new(io::ErrorKind::Other, e)), + }; + self.additions.push(Addition::Archive { + archive, + skip: Box::new(skip), + }); + Ok(()) + } + + /// Adds an arbitrary file to this archive + pub fn add_file(&mut self, file: &Path) { + let name = file.file_name().unwrap().to_str().unwrap(); + self.additions.push(Addition::File { + path: file.to_path_buf(), + name_in_archive: name.to_string(), + }); + } + + /// Indicate that the next call to `build` should update all symbols in + /// the archive (equivalent to running 'ar s' over it). + pub fn update_symbols(&mut self) { + self.should_update_symbols = true; + } + + /// Combine the provided files, rlibs, and native libraries into a single + /// `Archive`. + pub fn build(&mut self) { + let kind = match self.llvm_archive_kind() { + Ok(kind) => kind, + Err(kind) => { + self.config.sess.fatal(&format!("Don't know how to build archive of type: {}", + kind)); + } + }; + + if let Err(e) = self.build_with_llvm(kind) { + self.config.sess.fatal(&format!("failed to build archive: {}", e)); + } + + } + + fn llvm_archive_kind(&self) -> Result { + let kind = &*self.config.sess.target.target.options.archive_format; + kind.parse().map_err(|_| kind) + } + + fn build_with_llvm(&mut self, kind: ArchiveKind) -> io::Result<()> { + let mut archives = Vec::new(); + let mut strings = Vec::new(); + let mut members = Vec::new(); + let removals = mem::replace(&mut self.removals, Vec::new()); + + unsafe { + if let Some(archive) = self.src_archive() { + for child in archive.iter() { + let child = child.map_err(string_to_io_error)?; + let child_name = match child.name() { + Some(s) => s, + None => continue, + }; + if removals.iter().any(|r| r == child_name) { + continue + } + + let name = CString::new(child_name)?; + members.push(llvm::LLVMRustArchiveMemberNew(ptr::null(), + name.as_ptr(), + child.raw())); + strings.push(name); + } + } + for addition in mem::replace(&mut self.additions, Vec::new()) { + match addition { + Addition::File { path, name_in_archive } => { + let path = CString::new(path.to_str().unwrap())?; + let name = CString::new(name_in_archive)?; + members.push(llvm::LLVMRustArchiveMemberNew(path.as_ptr(), + name.as_ptr(), + ptr::null_mut())); + strings.push(path); + strings.push(name); + } + Addition::Archive { archive, mut skip } => { + for child in archive.iter() { + let child = child.map_err(string_to_io_error)?; + if !is_relevant_child(&child) { + continue + } + let child_name = child.name().unwrap(); + if skip(child_name) { + continue + } + + // It appears that LLVM's archive writer is a little + // buggy if the name we pass down isn't just the + // filename component, so chop that off here and + // pass it in. + // + // See LLVM bug 25877 for more info. + let child_name = Path::new(child_name) + .file_name().unwrap() + .to_str().unwrap(); + let name = CString::new(child_name)?; + let m = llvm::LLVMRustArchiveMemberNew(ptr::null(), + name.as_ptr(), + child.raw()); + members.push(m); + strings.push(name); + } + archives.push(archive); + } + } + } + + let dst = self.config.dst.to_str().unwrap().as_bytes(); + let dst = CString::new(dst)?; + let r = llvm::LLVMRustWriteArchive(dst.as_ptr(), + members.len() as libc::size_t, + members.as_ptr(), + self.should_update_symbols, + kind); + let ret = if r.into_result().is_err() { + let err = llvm::LLVMRustGetLastError(); + let msg = if err.is_null() { + "failed to write archive".to_string() + } else { + String::from_utf8_lossy(CStr::from_ptr(err).to_bytes()) + .into_owned() + }; + Err(io::Error::new(io::ErrorKind::Other, msg)) + } else { + Ok(()) + }; + for member in members { + llvm::LLVMRustArchiveMemberFree(member); + } + return ret + } + } +} + +fn string_to_io_error(s: String) -> io::Error { + io::Error::new(io::ErrorKind::Other, format!("bad archive: {}", s)) +} diff --git a/src/librustc_codegen_llvm/back/bytecode.rs b/src/librustc_codegen_llvm/back/bytecode.rs new file mode 100644 index 00000000000..212d1aaf055 --- /dev/null +++ b/src/librustc_codegen_llvm/back/bytecode.rs @@ -0,0 +1,160 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Management of the encoding of LLVM bytecode into rlibs +//! +//! This module contains the management of encoding LLVM bytecode into rlibs, +//! primarily for the usage in LTO situations. Currently the compiler will +//! unconditionally encode LLVM-IR into rlibs regardless of what's happening +//! elsewhere, so we currently compress the bytecode via deflate to avoid taking +//! up too much space on disk. +//! +//! After compressing the bytecode we then have the rest of the format to +//! basically deal with various bugs in various archive implementations. The +//! format currently is: +//! +//! RLIB LLVM-BYTECODE OBJECT LAYOUT +//! Version 2 +//! Bytes Data +//! 0..10 "RUST_OBJECT" encoded in ASCII +//! 11..14 format version as little-endian u32 +//! 15..19 the length of the module identifier string +//! 20..n the module identifier string +//! n..n+8 size in bytes of deflate compressed LLVM bitcode as +//! little-endian u64 +//! n+9.. compressed LLVM bitcode +//! ? maybe a byte to make this whole thing even length + +use std::io::{Read, Write}; +use std::ptr; +use std::str; + +use flate2::Compression; +use flate2::read::DeflateDecoder; +use flate2::write::DeflateEncoder; + +// This is the "magic number" expected at the beginning of a LLVM bytecode +// object in an rlib. +pub const RLIB_BYTECODE_OBJECT_MAGIC: &'static [u8] = b"RUST_OBJECT"; + +// The version number this compiler will write to bytecode objects in rlibs +pub const RLIB_BYTECODE_OBJECT_VERSION: u8 = 2; + +pub const RLIB_BYTECODE_EXTENSION: &str = "bc.z"; + +pub fn encode(identifier: &str, bytecode: &[u8]) -> Vec { + let mut encoded = Vec::new(); + + // Start off with the magic string + encoded.extend_from_slice(RLIB_BYTECODE_OBJECT_MAGIC); + + // Next up is the version + encoded.extend_from_slice(&[RLIB_BYTECODE_OBJECT_VERSION, 0, 0, 0]); + + // Next is the LLVM module identifier length + contents + let identifier_len = identifier.len(); + encoded.extend_from_slice(&[ + (identifier_len >> 0) as u8, + (identifier_len >> 8) as u8, + (identifier_len >> 16) as u8, + (identifier_len >> 24) as u8, + ]); + encoded.extend_from_slice(identifier.as_bytes()); + + // Next is the LLVM module deflate compressed, prefixed with its length. We + // don't know its length yet, so fill in 0s + let deflated_size_pos = encoded.len(); + encoded.extend_from_slice(&[0, 0, 0, 0, 0, 0, 0, 0]); + + let before = encoded.len(); + DeflateEncoder::new(&mut encoded, Compression::fast()) + .write_all(bytecode) + .unwrap(); + let after = encoded.len(); + + // Fill in the length we reserved space for before + let bytecode_len = (after - before) as u64; + encoded[deflated_size_pos + 0] = (bytecode_len >> 0) as u8; + encoded[deflated_size_pos + 1] = (bytecode_len >> 8) as u8; + encoded[deflated_size_pos + 2] = (bytecode_len >> 16) as u8; + encoded[deflated_size_pos + 3] = (bytecode_len >> 24) as u8; + encoded[deflated_size_pos + 4] = (bytecode_len >> 32) as u8; + encoded[deflated_size_pos + 5] = (bytecode_len >> 40) as u8; + encoded[deflated_size_pos + 6] = (bytecode_len >> 48) as u8; + encoded[deflated_size_pos + 7] = (bytecode_len >> 56) as u8; + + // If the number of bytes written to the object so far is odd, add a + // padding byte to make it even. This works around a crash bug in LLDB + // (see issue #15950) + if encoded.len() % 2 == 1 { + encoded.push(0); + } + + return encoded +} + +pub struct DecodedBytecode<'a> { + identifier: &'a str, + encoded_bytecode: &'a [u8], +} + +impl<'a> DecodedBytecode<'a> { + pub fn new(data: &'a [u8]) -> Result, String> { + if !data.starts_with(RLIB_BYTECODE_OBJECT_MAGIC) { + return Err(format!("magic bytecode prefix not found")) + } + let data = &data[RLIB_BYTECODE_OBJECT_MAGIC.len()..]; + if !data.starts_with(&[RLIB_BYTECODE_OBJECT_VERSION, 0, 0, 0]) { + return Err(format!("wrong version prefix found in bytecode")) + } + let data = &data[4..]; + if data.len() < 4 { + return Err(format!("bytecode corrupted")) + } + let identifier_len = unsafe { + u32::from_le(ptr::read_unaligned(data.as_ptr() as *const u32)) as usize + }; + let data = &data[4..]; + if data.len() < identifier_len { + return Err(format!("bytecode corrupted")) + } + let identifier = match str::from_utf8(&data[..identifier_len]) { + Ok(s) => s, + Err(_) => return Err(format!("bytecode corrupted")) + }; + let data = &data[identifier_len..]; + if data.len() < 8 { + return Err(format!("bytecode corrupted")) + } + let bytecode_len = unsafe { + u64::from_le(ptr::read_unaligned(data.as_ptr() as *const u64)) as usize + }; + let data = &data[8..]; + if data.len() < bytecode_len { + return Err(format!("bytecode corrupted")) + } + let encoded_bytecode = &data[..bytecode_len]; + + Ok(DecodedBytecode { + identifier, + encoded_bytecode, + }) + } + + pub fn bytecode(&self) -> Vec { + let mut data = Vec::new(); + DeflateDecoder::new(self.encoded_bytecode).read_to_end(&mut data).unwrap(); + return data + } + + pub fn identifier(&self) -> &'a str { + self.identifier + } +} diff --git a/src/librustc_codegen_llvm/back/command.rs b/src/librustc_codegen_llvm/back/command.rs new file mode 100644 index 00000000000..9ebbdd7c3c9 --- /dev/null +++ b/src/librustc_codegen_llvm/back/command.rs @@ -0,0 +1,175 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! A thin wrapper around `Command` in the standard library which allows us to +//! read the arguments that are built up. + +use std::ffi::{OsStr, OsString}; +use std::fmt; +use std::io; +use std::mem; +use std::process::{self, Output}; + +use rustc_target::spec::LldFlavor; + +#[derive(Clone)] +pub struct Command { + program: Program, + args: Vec, + env: Vec<(OsString, OsString)>, +} + +#[derive(Clone)] +enum Program { + Normal(OsString), + CmdBatScript(OsString), + Lld(OsString, LldFlavor) +} + +impl Command { + pub fn new>(program: P) -> Command { + Command::_new(Program::Normal(program.as_ref().to_owned())) + } + + pub fn bat_script>(program: P) -> Command { + Command::_new(Program::CmdBatScript(program.as_ref().to_owned())) + } + + pub fn lld>(program: P, flavor: LldFlavor) -> Command { + Command::_new(Program::Lld(program.as_ref().to_owned(), flavor)) + } + + fn _new(program: Program) -> Command { + Command { + program, + args: Vec::new(), + env: Vec::new(), + } + } + + pub fn arg>(&mut self, arg: P) -> &mut Command { + self._arg(arg.as_ref()); + self + } + + pub fn args(&mut self, args: I) -> &mut Command + where I: IntoIterator, + I::Item: AsRef, + { + for arg in args { + self._arg(arg.as_ref()); + } + self + } + + fn _arg(&mut self, arg: &OsStr) { + self.args.push(arg.to_owned()); + } + + pub fn env(&mut self, key: K, value: V) -> &mut Command + where K: AsRef, + V: AsRef + { + self._env(key.as_ref(), value.as_ref()); + self + } + + fn _env(&mut self, key: &OsStr, value: &OsStr) { + self.env.push((key.to_owned(), value.to_owned())); + } + + pub fn output(&mut self) -> io::Result { + self.command().output() + } + + pub fn command(&self) -> process::Command { + let mut ret = match self.program { + Program::Normal(ref p) => process::Command::new(p), + Program::CmdBatScript(ref p) => { + let mut c = process::Command::new("cmd"); + c.arg("/c").arg(p); + c + } + Program::Lld(ref p, flavor) => { + let mut c = process::Command::new(p); + c.arg("-flavor").arg(match flavor { + LldFlavor::Wasm => "wasm", + LldFlavor::Ld => "gnu", + LldFlavor::Link => "link", + LldFlavor::Ld64 => "darwin", + }); + c + } + }; + ret.args(&self.args); + ret.envs(self.env.clone()); + return ret + } + + // extensions + + pub fn get_args(&self) -> &[OsString] { + &self.args + } + + pub fn take_args(&mut self) -> Vec { + mem::replace(&mut self.args, Vec::new()) + } + + /// Returns a `true` if we're pretty sure that this'll blow OS spawn limits, + /// or `false` if we should attempt to spawn and see what the OS says. + pub fn very_likely_to_exceed_some_spawn_limit(&self) -> bool { + // We mostly only care about Windows in this method, on Unix the limits + // can be gargantuan anyway so we're pretty unlikely to hit them + if cfg!(unix) { + return false + } + + // Right now LLD doesn't support the `@` syntax of passing an argument + // through files, so regardless of the platform we try to go to the OS + // on this one. + if let Program::Lld(..) = self.program { + return false + } + + // Ok so on Windows to spawn a process is 32,768 characters in its + // command line [1]. Unfortunately we don't actually have access to that + // as it's calculated just before spawning. Instead we perform a + // poor-man's guess as to how long our command line will be. We're + // assuming here that we don't have to escape every character... + // + // Turns out though that `cmd.exe` has even smaller limits, 8192 + // characters [2]. Linkers can often be batch scripts (for example + // Emscripten, Gecko's current build system) which means that we're + // running through batch scripts. These linkers often just forward + // arguments elsewhere (and maybe tack on more), so if we blow 8192 + // bytes we'll typically cause them to blow as well. + // + // Basically as a result just perform an inflated estimate of what our + // command line will look like and test if it's > 8192 (we actually + // test against 6k to artificially inflate our estimate). If all else + // fails we'll fall back to the normal unix logic of testing the OS + // error code if we fail to spawn and automatically re-spawning the + // linker with smaller arguments. + // + // [1]: https://msdn.microsoft.com/en-us/library/windows/desktop/ms682425(v=vs.85).aspx + // [2]: https://blogs.msdn.microsoft.com/oldnewthing/20031210-00/?p=41553 + + let estimated_command_line_len = + self.args.iter().map(|a| a.len()).sum::(); + estimated_command_line_len > 1024 * 6 + } +} + +impl fmt::Debug for Command { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + self.command().fmt(f) + } +} diff --git a/src/librustc_codegen_llvm/back/link.rs b/src/librustc_codegen_llvm/back/link.rs new file mode 100644 index 00000000000..dbfd430a3e2 --- /dev/null +++ b/src/librustc_codegen_llvm/back/link.rs @@ -0,0 +1,1630 @@ +// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use back::wasm; +use cc::windows_registry; +use super::archive::{ArchiveBuilder, ArchiveConfig}; +use super::bytecode::RLIB_BYTECODE_EXTENSION; +use super::linker::Linker; +use super::command::Command; +use super::rpath::RPathConfig; +use super::rpath; +use metadata::METADATA_FILENAME; +use rustc::session::config::{self, NoDebugInfo, OutputFilenames, OutputType, PrintRequest}; +use rustc::session::config::{RUST_CGU_EXT, Lto}; +use rustc::session::filesearch; +use rustc::session::search_paths::PathKind; +use rustc::session::Session; +use rustc::middle::cstore::{NativeLibrary, LibSource, NativeLibraryKind}; +use rustc::middle::dependency_format::Linkage; +use {CodegenResults, CrateInfo}; +use rustc::util::common::time; +use rustc::util::fs::fix_windows_verbatim_for_gcc; +use rustc::hir::def_id::CrateNum; +use tempdir::TempDir; +use rustc_target::spec::{PanicStrategy, RelroLevel, LinkerFlavor, TargetTriple}; +use rustc_data_structures::fx::FxHashSet; +use context::get_reloc_model; +use llvm; + +use std::ascii; +use std::char; +use std::env; +use std::fmt; +use std::fs; +use std::io; +use std::path::{Path, PathBuf}; +use std::process::{Output, Stdio}; +use std::str; +use syntax::attr; + +/// The LLVM module name containing crate-metadata. This includes a `.` on +/// purpose, so it cannot clash with the name of a user-defined module. +pub const METADATA_MODULE_NAME: &'static str = "crate.metadata"; + +// same as for metadata above, but for allocator shim +pub const ALLOCATOR_MODULE_NAME: &'static str = "crate.allocator"; + +pub use rustc_codegen_utils::link::{find_crate_name, filename_for_input, default_output_for_target, + invalid_output_for_target, build_link_meta, out_filename, + check_file_is_writeable}; + +// The third parameter is for env vars, used on windows to set up the +// path for MSVC to find its DLLs, and gcc to find its bundled +// toolchain +pub fn get_linker(sess: &Session) -> (PathBuf, Command) { + // If our linker looks like a batch script on Windows then to execute this + // we'll need to spawn `cmd` explicitly. This is primarily done to handle + // emscripten where the linker is `emcc.bat` and needs to be spawned as + // `cmd /c emcc.bat ...`. + // + // This worked historically but is needed manually since #42436 (regression + // was tagged as #42791) and some more info can be found on #44443 for + // emscripten itself. + let cmd = |linker: &Path| { + if let Some(linker) = linker.to_str() { + if cfg!(windows) && linker.ends_with(".bat") { + return Command::bat_script(linker) + } + } + match sess.linker_flavor() { + LinkerFlavor::Lld(f) => Command::lld(linker, f), + _ => Command::new(linker), + + } + }; + + let msvc_tool = windows_registry::find_tool(&sess.opts.target_triple.triple(), "link.exe"); + + let linker_path = sess.opts.cg.linker.as_ref().map(|s| &**s) + .or(sess.target.target.options.linker.as_ref().map(|s| s.as_ref())) + .unwrap_or(match sess.linker_flavor() { + LinkerFlavor::Msvc => { + msvc_tool.as_ref().map(|t| t.path()).unwrap_or("link.exe".as_ref()) + } + LinkerFlavor::Em if cfg!(windows) => "emcc.bat".as_ref(), + LinkerFlavor::Em => "emcc".as_ref(), + LinkerFlavor::Gcc => "cc".as_ref(), + LinkerFlavor::Ld => "ld".as_ref(), + LinkerFlavor::Lld(_) => "lld".as_ref(), + }); + + let mut cmd = cmd(linker_path); + + // The compiler's sysroot often has some bundled tools, so add it to the + // PATH for the child. + let mut new_path = sess.host_filesearch(PathKind::All) + .get_tools_search_paths(); + let mut msvc_changed_path = false; + if sess.target.target.options.is_like_msvc { + if let Some(ref tool) = msvc_tool { + cmd.args(tool.args()); + for &(ref k, ref v) in tool.env() { + if k == "PATH" { + new_path.extend(env::split_paths(v)); + msvc_changed_path = true; + } else { + cmd.env(k, v); + } + } + } + } + + if !msvc_changed_path { + if let Some(path) = env::var_os("PATH") { + new_path.extend(env::split_paths(&path)); + } + } + cmd.env("PATH", env::join_paths(new_path).unwrap()); + + (linker_path.to_path_buf(), cmd) +} + +pub fn remove(sess: &Session, path: &Path) { + match fs::remove_file(path) { + Ok(..) => {} + Err(e) => { + sess.err(&format!("failed to remove {}: {}", + path.display(), + e)); + } + } +} + +/// Perform the linkage portion of the compilation phase. This will generate all +/// of the requested outputs for this compilation session. +pub(crate) fn link_binary(sess: &Session, + codegen_results: &CodegenResults, + outputs: &OutputFilenames, + crate_name: &str) -> Vec { + let mut out_filenames = Vec::new(); + for &crate_type in sess.crate_types.borrow().iter() { + // Ignore executable crates if we have -Z no-codegen, as they will error. + let output_metadata = sess.opts.output_types.contains_key(&OutputType::Metadata); + if (sess.opts.debugging_opts.no_codegen || !sess.opts.output_types.should_codegen()) && + !output_metadata && + crate_type == config::CrateTypeExecutable { + continue; + } + + if invalid_output_for_target(sess, crate_type) { + bug!("invalid output type `{:?}` for target os `{}`", + crate_type, sess.opts.target_triple); + } + let mut out_files = link_binary_output(sess, + codegen_results, + crate_type, + outputs, + crate_name); + out_filenames.append(&mut out_files); + } + + // Remove the temporary object file and metadata if we aren't saving temps + if !sess.opts.cg.save_temps { + if sess.opts.output_types.should_codegen() && + !preserve_objects_for_their_debuginfo(sess) + { + for obj in codegen_results.modules.iter().filter_map(|m| m.object.as_ref()) { + remove(sess, obj); + } + } + for obj in codegen_results.modules.iter().filter_map(|m| m.bytecode_compressed.as_ref()) { + remove(sess, obj); + } + if let Some(ref obj) = codegen_results.metadata_module.object { + remove(sess, obj); + } + if let Some(ref allocator) = codegen_results.allocator_module { + if let Some(ref obj) = allocator.object { + remove(sess, obj); + } + if let Some(ref bc) = allocator.bytecode_compressed { + remove(sess, bc); + } + } + } + + out_filenames +} + +/// Returns a boolean indicating whether we should preserve the object files on +/// the filesystem for their debug information. This is often useful with +/// split-dwarf like schemes. +fn preserve_objects_for_their_debuginfo(sess: &Session) -> bool { + // If the objects don't have debuginfo there's nothing to preserve. + if sess.opts.debuginfo == NoDebugInfo { + return false + } + + // If we're only producing artifacts that are archives, no need to preserve + // the objects as they're losslessly contained inside the archives. + let output_linked = sess.crate_types.borrow() + .iter() + .any(|x| *x != config::CrateTypeRlib && *x != config::CrateTypeStaticlib); + if !output_linked { + return false + } + + // If we're on OSX then the equivalent of split dwarf is turned on by + // default. The final executable won't actually have any debug information + // except it'll have pointers to elsewhere. Historically we've always run + // `dsymutil` to "link all the dwarf together" but this is actually sort of + // a bummer for incremental compilation! (the whole point of split dwarf is + // that you don't do this sort of dwarf link). + // + // Basically as a result this just means that if we're on OSX and we're + // *not* running dsymutil then the object files are the only source of truth + // for debug information, so we must preserve them. + if sess.target.target.options.is_like_osx { + match sess.opts.debugging_opts.run_dsymutil { + // dsymutil is not being run, preserve objects + Some(false) => return true, + + // dsymutil is being run, no need to preserve the objects + Some(true) => return false, + + // The default historical behavior was to always run dsymutil, so + // we're preserving that temporarily, but we're likely to switch the + // default soon. + None => return false, + } + } + + false +} + +fn filename_for_metadata(sess: &Session, crate_name: &str, outputs: &OutputFilenames) -> PathBuf { + let out_filename = outputs.single_output_file.clone() + .unwrap_or(outputs + .out_directory + .join(&format!("lib{}{}.rmeta", crate_name, sess.opts.cg.extra_filename))); + check_file_is_writeable(&out_filename, sess); + out_filename +} + +pub(crate) fn each_linked_rlib(sess: &Session, + info: &CrateInfo, + f: &mut FnMut(CrateNum, &Path)) -> Result<(), String> { + let crates = info.used_crates_static.iter(); + let fmts = sess.dependency_formats.borrow(); + let fmts = fmts.get(&config::CrateTypeExecutable) + .or_else(|| fmts.get(&config::CrateTypeStaticlib)) + .or_else(|| fmts.get(&config::CrateTypeCdylib)) + .or_else(|| fmts.get(&config::CrateTypeProcMacro)); + let fmts = match fmts { + Some(f) => f, + None => return Err(format!("could not find formats for rlibs")) + }; + for &(cnum, ref path) in crates { + match fmts.get(cnum.as_usize() - 1) { + Some(&Linkage::NotLinked) | + Some(&Linkage::IncludedFromDylib) => continue, + Some(_) => {} + None => return Err(format!("could not find formats for rlibs")) + } + let name = &info.crate_name[&cnum]; + let path = match *path { + LibSource::Some(ref p) => p, + LibSource::MetadataOnly => { + return Err(format!("could not find rlib for: `{}`, found rmeta (metadata) file", + name)) + } + LibSource::None => { + return Err(format!("could not find rlib for: `{}`", name)) + } + }; + f(cnum, &path); + } + Ok(()) +} + +/// Returns a boolean indicating whether the specified crate should be ignored +/// during LTO. +/// +/// Crates ignored during LTO are not lumped together in the "massive object +/// file" that we create and are linked in their normal rlib states. See +/// comments below for what crates do not participate in LTO. +/// +/// It's unusual for a crate to not participate in LTO. Typically only +/// compiler-specific and unstable crates have a reason to not participate in +/// LTO. +pub(crate) fn ignored_for_lto(sess: &Session, info: &CrateInfo, cnum: CrateNum) -> bool { + // If our target enables builtin function lowering in LLVM then the + // crates providing these functions don't participate in LTO (e.g. + // no_builtins or compiler builtins crates). + !sess.target.target.options.no_builtins && + (info.is_no_builtins.contains(&cnum) || info.compiler_builtins == Some(cnum)) +} + +fn link_binary_output(sess: &Session, + codegen_results: &CodegenResults, + crate_type: config::CrateType, + outputs: &OutputFilenames, + crate_name: &str) -> Vec { + for obj in codegen_results.modules.iter().filter_map(|m| m.object.as_ref()) { + check_file_is_writeable(obj, sess); + } + + let mut out_filenames = vec![]; + + if outputs.outputs.contains_key(&OutputType::Metadata) { + let out_filename = filename_for_metadata(sess, crate_name, outputs); + // To avoid races with another rustc process scanning the output directory, + // we need to write the file somewhere else and atomically move it to its + // final destination, with a `fs::rename` call. In order for the rename to + // always succeed, the temporary file needs to be on the same filesystem, + // which is why we create it inside the output directory specifically. + let metadata_tmpdir = match TempDir::new_in(out_filename.parent().unwrap(), "rmeta") { + Ok(tmpdir) => tmpdir, + Err(err) => sess.fatal(&format!("couldn't create a temp dir: {}", err)), + }; + let metadata = emit_metadata(sess, codegen_results, &metadata_tmpdir); + if let Err(e) = fs::rename(metadata, &out_filename) { + sess.fatal(&format!("failed to write {}: {}", out_filename.display(), e)); + } + out_filenames.push(out_filename); + } + + let tmpdir = match TempDir::new("rustc") { + Ok(tmpdir) => tmpdir, + Err(err) => sess.fatal(&format!("couldn't create a temp dir: {}", err)), + }; + + if outputs.outputs.should_codegen() { + let out_filename = out_filename(sess, crate_type, outputs, crate_name); + match crate_type { + config::CrateTypeRlib => { + link_rlib(sess, + codegen_results, + RlibFlavor::Normal, + &out_filename, + &tmpdir).build(); + } + config::CrateTypeStaticlib => { + link_staticlib(sess, codegen_results, &out_filename, &tmpdir); + } + _ => { + link_natively(sess, crate_type, &out_filename, codegen_results, tmpdir.path()); + } + } + out_filenames.push(out_filename); + } + + if sess.opts.cg.save_temps { + let _ = tmpdir.into_path(); + } + + out_filenames +} + +fn archive_search_paths(sess: &Session) -> Vec { + let mut search = Vec::new(); + sess.target_filesearch(PathKind::Native).for_each_lib_search_path(|path, _| { + search.push(path.to_path_buf()); + }); + return search; +} + +fn archive_config<'a>(sess: &'a Session, + output: &Path, + input: Option<&Path>) -> ArchiveConfig<'a> { + ArchiveConfig { + sess, + dst: output.to_path_buf(), + src: input.map(|p| p.to_path_buf()), + lib_search_paths: archive_search_paths(sess), + } +} + +/// We use a temp directory here to avoid races between concurrent rustc processes, +/// such as builds in the same directory using the same filename for metadata while +/// building an `.rlib` (stomping over one another), or writing an `.rmeta` into a +/// directory being searched for `extern crate` (observing an incomplete file). +/// The returned path is the temporary file containing the complete metadata. +fn emit_metadata<'a>(sess: &'a Session, codegen_results: &CodegenResults, tmpdir: &TempDir) + -> PathBuf { + let out_filename = tmpdir.path().join(METADATA_FILENAME); + let result = fs::write(&out_filename, &codegen_results.metadata.raw_data); + + if let Err(e) = result { + sess.fatal(&format!("failed to write {}: {}", out_filename.display(), e)); + } + + out_filename +} + +enum RlibFlavor { + Normal, + StaticlibBase, +} + +// Create an 'rlib' +// +// An rlib in its current incarnation is essentially a renamed .a file. The +// rlib primarily contains the object file of the crate, but it also contains +// all of the object files from native libraries. This is done by unzipping +// native libraries and inserting all of the contents into this archive. +fn link_rlib<'a>(sess: &'a Session, + codegen_results: &CodegenResults, + flavor: RlibFlavor, + out_filename: &Path, + tmpdir: &TempDir) -> ArchiveBuilder<'a> { + info!("preparing rlib to {:?}", out_filename); + let mut ab = ArchiveBuilder::new(archive_config(sess, out_filename, None)); + + for obj in codegen_results.modules.iter().filter_map(|m| m.object.as_ref()) { + ab.add_file(obj); + } + + // Note that in this loop we are ignoring the value of `lib.cfg`. That is, + // we may not be configured to actually include a static library if we're + // adding it here. That's because later when we consume this rlib we'll + // decide whether we actually needed the static library or not. + // + // To do this "correctly" we'd need to keep track of which libraries added + // which object files to the archive. We don't do that here, however. The + // #[link(cfg(..))] feature is unstable, though, and only intended to get + // liblibc working. In that sense the check below just indicates that if + // there are any libraries we want to omit object files for at link time we + // just exclude all custom object files. + // + // Eventually if we want to stabilize or flesh out the #[link(cfg(..))] + // feature then we'll need to figure out how to record what objects were + // loaded from the libraries found here and then encode that into the + // metadata of the rlib we're generating somehow. + for lib in codegen_results.crate_info.used_libraries.iter() { + match lib.kind { + NativeLibraryKind::NativeStatic => {} + NativeLibraryKind::NativeStaticNobundle | + NativeLibraryKind::NativeFramework | + NativeLibraryKind::NativeUnknown => continue, + } + ab.add_native_library(&lib.name.as_str()); + } + + // After adding all files to the archive, we need to update the + // symbol table of the archive. + ab.update_symbols(); + + // Note that it is important that we add all of our non-object "magical + // files" *after* all of the object files in the archive. The reason for + // this is as follows: + // + // * When performing LTO, this archive will be modified to remove + // objects from above. The reason for this is described below. + // + // * When the system linker looks at an archive, it will attempt to + // determine the architecture of the archive in order to see whether its + // linkable. + // + // The algorithm for this detection is: iterate over the files in the + // archive. Skip magical SYMDEF names. Interpret the first file as an + // object file. Read architecture from the object file. + // + // * As one can probably see, if "metadata" and "foo.bc" were placed + // before all of the objects, then the architecture of this archive would + // not be correctly inferred once 'foo.o' is removed. + // + // Basically, all this means is that this code should not move above the + // code above. + match flavor { + RlibFlavor::Normal => { + // Instead of putting the metadata in an object file section, rlibs + // contain the metadata in a separate file. + ab.add_file(&emit_metadata(sess, codegen_results, tmpdir)); + + // For LTO purposes, the bytecode of this library is also inserted + // into the archive. + for bytecode in codegen_results + .modules + .iter() + .filter_map(|m| m.bytecode_compressed.as_ref()) + { + ab.add_file(bytecode); + } + + // After adding all files to the archive, we need to update the + // symbol table of the archive. This currently dies on macOS (see + // #11162), and isn't necessary there anyway + if !sess.target.target.options.is_like_osx { + ab.update_symbols(); + } + } + + RlibFlavor::StaticlibBase => { + let obj = codegen_results.allocator_module + .as_ref() + .and_then(|m| m.object.as_ref()); + if let Some(obj) = obj { + ab.add_file(obj); + } + } + } + + ab +} + +// Create a static archive +// +// This is essentially the same thing as an rlib, but it also involves adding +// all of the upstream crates' objects into the archive. This will slurp in +// all of the native libraries of upstream dependencies as well. +// +// Additionally, there's no way for us to link dynamic libraries, so we warn +// about all dynamic library dependencies that they're not linked in. +// +// There's no need to include metadata in a static archive, so ensure to not +// link in the metadata object file (and also don't prepare the archive with a +// metadata file). +fn link_staticlib(sess: &Session, + codegen_results: &CodegenResults, + out_filename: &Path, + tempdir: &TempDir) { + let mut ab = link_rlib(sess, + codegen_results, + RlibFlavor::StaticlibBase, + out_filename, + tempdir); + let mut all_native_libs = vec![]; + + let res = each_linked_rlib(sess, &codegen_results.crate_info, &mut |cnum, path| { + let name = &codegen_results.crate_info.crate_name[&cnum]; + let native_libs = &codegen_results.crate_info.native_libraries[&cnum]; + + // Here when we include the rlib into our staticlib we need to make a + // decision whether to include the extra object files along the way. + // These extra object files come from statically included native + // libraries, but they may be cfg'd away with #[link(cfg(..))]. + // + // This unstable feature, though, only needs liblibc to work. The only + // use case there is where musl is statically included in liblibc.rlib, + // so if we don't want the included version we just need to skip it. As + // a result the logic here is that if *any* linked library is cfg'd away + // we just skip all object files. + // + // Clearly this is not sufficient for a general purpose feature, and + // we'd want to read from the library's metadata to determine which + // object files come from where and selectively skip them. + let skip_object_files = native_libs.iter().any(|lib| { + lib.kind == NativeLibraryKind::NativeStatic && !relevant_lib(sess, lib) + }); + ab.add_rlib(path, + &name.as_str(), + is_full_lto_enabled(sess) && + !ignored_for_lto(sess, &codegen_results.crate_info, cnum), + skip_object_files).unwrap(); + + all_native_libs.extend(codegen_results.crate_info.native_libraries[&cnum].iter().cloned()); + }); + if let Err(e) = res { + sess.fatal(&e); + } + + ab.update_symbols(); + ab.build(); + + if !all_native_libs.is_empty() { + if sess.opts.prints.contains(&PrintRequest::NativeStaticLibs) { + print_native_static_libs(sess, &all_native_libs); + } + } +} + +fn print_native_static_libs(sess: &Session, all_native_libs: &[NativeLibrary]) { + let lib_args: Vec<_> = all_native_libs.iter() + .filter(|l| relevant_lib(sess, l)) + .filter_map(|lib| match lib.kind { + NativeLibraryKind::NativeStaticNobundle | + NativeLibraryKind::NativeUnknown => { + if sess.target.target.options.is_like_msvc { + Some(format!("{}.lib", lib.name)) + } else { + Some(format!("-l{}", lib.name)) + } + }, + NativeLibraryKind::NativeFramework => { + // ld-only syntax, since there are no frameworks in MSVC + Some(format!("-framework {}", lib.name)) + }, + // These are included, no need to print them + NativeLibraryKind::NativeStatic => None, + }) + .collect(); + if !lib_args.is_empty() { + sess.note_without_error("Link against the following native artifacts when linking \ + against this static library. The order and any duplication \ + can be significant on some platforms."); + // Prefix for greppability + sess.note_without_error(&format!("native-static-libs: {}", &lib_args.join(" "))); + } +} + +// Create a dynamic library or executable +// +// 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, + crate_type: config::CrateType, + out_filename: &Path, + codegen_results: &CodegenResults, + tmpdir: &Path) { + info!("preparing {:?} to {:?}", crate_type, out_filename); + let flavor = sess.linker_flavor(); + + // The invocations of cc share some flags across platforms + let (pname, mut cmd) = get_linker(sess); + + let root = sess.target_filesearch(PathKind::Native).get_lib_path(); + if let Some(args) = sess.target.target.options.pre_link_args.get(&flavor) { + cmd.args(args); + } + if let Some(args) = sess.target.target.options.pre_link_args_crt.get(&flavor) { + if sess.crt_static() { + cmd.args(args); + } + } + if let Some(ref args) = sess.opts.debugging_opts.pre_link_args { + cmd.args(args); + } + cmd.args(&sess.opts.debugging_opts.pre_link_arg); + + let pre_link_objects = if crate_type == config::CrateTypeExecutable { + &sess.target.target.options.pre_link_objects_exe + } else { + &sess.target.target.options.pre_link_objects_dll + }; + for obj in pre_link_objects { + cmd.arg(root.join(obj)); + } + + if crate_type == config::CrateTypeExecutable && sess.crt_static() { + for obj in &sess.target.target.options.pre_link_objects_exe_crt { + cmd.arg(root.join(obj)); + } + + for obj in &sess.target.target.options.pre_link_objects_exe_crt_sys { + if flavor == LinkerFlavor::Gcc { + cmd.arg(format!("-l:{}", obj)); + } + } + } + + if sess.target.target.options.is_like_emscripten { + cmd.arg("-s"); + cmd.arg(if sess.panic_strategy() == PanicStrategy::Abort { + "DISABLE_EXCEPTION_CATCHING=1" + } else { + "DISABLE_EXCEPTION_CATCHING=0" + }); + } + + { + let mut linker = codegen_results.linker_info.to_linker(cmd, &sess); + link_args(&mut *linker, sess, crate_type, tmpdir, + out_filename, codegen_results); + cmd = linker.finalize(); + } + if let Some(args) = sess.target.target.options.late_link_args.get(&flavor) { + cmd.args(args); + } + for obj in &sess.target.target.options.post_link_objects { + cmd.arg(root.join(obj)); + } + if sess.crt_static() { + for obj in &sess.target.target.options.post_link_objects_crt_sys { + if flavor == LinkerFlavor::Gcc { + cmd.arg(format!("-l:{}", obj)); + } + } + for obj in &sess.target.target.options.post_link_objects_crt { + cmd.arg(root.join(obj)); + } + } + if let Some(args) = sess.target.target.options.post_link_args.get(&flavor) { + cmd.args(args); + } + for &(ref k, ref v) in &sess.target.target.options.link_env { + cmd.env(k, v); + } + + if sess.opts.debugging_opts.print_link_args { + println!("{:?}", &cmd); + } + + // May have not found libraries in the right formats. + sess.abort_if_errors(); + + // Invoke the system linker + // + // Note that there's a terribly awful hack that really shouldn't be present + // in any compiler. Here an environment variable is supported to + // automatically retry the linker invocation if the linker looks like it + // segfaulted. + // + // Gee that seems odd, normally segfaults are things we want to know about! + // Unfortunately though in rust-lang/rust#38878 we're experiencing the + // linker segfaulting on Travis quite a bit which is causing quite a bit of + // pain to land PRs when they spuriously fail due to a segfault. + // + // The issue #38878 has some more debugging information on it as well, but + // this unfortunately looks like it's just a race condition in macOS's linker + // with some thread pool working in the background. It seems that no one + // currently knows a fix for this so in the meantime we're left with this... + info!("{:?}", &cmd); + let retry_on_segfault = env::var("RUSTC_RETRY_LINKER_ON_SEGFAULT").is_ok(); + let mut prog; + let mut i = 0; + loop { + i += 1; + prog = time(sess, "running linker", || { + exec_linker(sess, &mut cmd, out_filename, tmpdir) + }); + let output = match prog { + Ok(ref output) => output, + Err(_) => break, + }; + if output.status.success() { + break + } + let mut out = output.stderr.clone(); + out.extend(&output.stdout); + let out = String::from_utf8_lossy(&out); + + // Check to see if the link failed with "unrecognized command line option: + // '-no-pie'" for gcc or "unknown argument: '-no-pie'" for clang. If so, + // reperform the link step without the -no-pie option. This is safe because + // if the linker doesn't support -no-pie then it should not default to + // linking executables as pie. Different versions of gcc seem to use + // different quotes in the error message so don't check for them. + if sess.target.target.options.linker_is_gnu && + sess.linker_flavor() != LinkerFlavor::Ld && + (out.contains("unrecognized command line option") || + out.contains("unknown argument")) && + out.contains("-no-pie") && + cmd.get_args().iter().any(|e| e.to_string_lossy() == "-no-pie") { + info!("linker output: {:?}", out); + warn!("Linker does not support -no-pie command line option. Retrying without."); + for arg in cmd.take_args() { + if arg.to_string_lossy() != "-no-pie" { + cmd.arg(arg); + } + } + info!("{:?}", &cmd); + continue; + } + if !retry_on_segfault || i > 3 { + break + } + let msg_segv = "clang: error: unable to execute command: Segmentation fault: 11"; + let msg_bus = "clang: error: unable to execute command: Bus error: 10"; + if !(out.contains(msg_segv) || out.contains(msg_bus)) { + break + } + + warn!( + "looks like the linker segfaulted when we tried to call it, \ + automatically retrying again. cmd = {:?}, out = {}.", + cmd, + out, + ); + } + + match prog { + Ok(prog) => { + fn escape_string(s: &[u8]) -> String { + str::from_utf8(s).map(|s| s.to_owned()) + .unwrap_or_else(|_| { + let mut x = "Non-UTF-8 output: ".to_string(); + x.extend(s.iter() + .flat_map(|&b| ascii::escape_default(b)) + .map(|b| char::from_u32(b as u32).unwrap())); + x + }) + } + if !prog.status.success() { + let mut output = prog.stderr.clone(); + output.extend_from_slice(&prog.stdout); + sess.struct_err(&format!("linking with `{}` failed: {}", + pname.display(), + prog.status)) + .note(&format!("{:?}", &cmd)) + .note(&escape_string(&output)) + .emit(); + sess.abort_if_errors(); + } + info!("linker stderr:\n{}", escape_string(&prog.stderr)); + info!("linker stdout:\n{}", escape_string(&prog.stdout)); + }, + Err(e) => { + let linker_not_found = e.kind() == io::ErrorKind::NotFound; + + let mut linker_error = { + if linker_not_found { + sess.struct_err(&format!("linker `{}` not found", pname.display())) + } else { + sess.struct_err(&format!("could not exec the linker `{}`", pname.display())) + } + }; + + linker_error.note(&format!("{}", e)); + + if !linker_not_found { + linker_error.note(&format!("{:?}", &cmd)); + } + + linker_error.emit(); + + if sess.target.target.options.is_like_msvc && linker_not_found { + sess.note_without_error("the msvc targets depend on the msvc linker \ + but `link.exe` was not found"); + sess.note_without_error("please ensure that VS 2013 or VS 2015 was installed \ + with the Visual C++ option"); + } + sess.abort_if_errors(); + } + } + + + // On macOS, debuggers need this utility to get run to do some munging of + // the symbols. Note, though, that if the object files are being preserved + // for their debug information there's no need for us to run dsymutil. + if sess.target.target.options.is_like_osx && + sess.opts.debuginfo != NoDebugInfo && + !preserve_objects_for_their_debuginfo(sess) + { + match Command::new("dsymutil").arg(out_filename).output() { + Ok(..) => {} + Err(e) => sess.fatal(&format!("failed to run dsymutil: {}", e)), + } + } + + if sess.opts.target_triple == TargetTriple::from_triple("wasm32-unknown-unknown") { + wasm::rewrite_imports(&out_filename, &codegen_results.crate_info.wasm_imports); + wasm::add_custom_sections(&out_filename, + &codegen_results.crate_info.wasm_custom_sections); + } +} + +fn exec_linker(sess: &Session, cmd: &mut Command, out_filename: &Path, tmpdir: &Path) + -> io::Result +{ + // When attempting to spawn the linker we run a risk of blowing out the + // size limits for spawning a new process with respect to the arguments + // we pass on the command line. + // + // Here we attempt to handle errors from the OS saying "your list of + // arguments is too big" by reinvoking the linker again with an `@`-file + // that contains all the arguments. The theory is that this is then + // accepted on all linkers and the linker will read all its options out of + // there instead of looking at the command line. + if !cmd.very_likely_to_exceed_some_spawn_limit() { + match cmd.command().stdout(Stdio::piped()).stderr(Stdio::piped()).spawn() { + Ok(child) => { + let output = child.wait_with_output(); + flush_linked_file(&output, out_filename)?; + return output; + } + Err(ref e) if command_line_too_big(e) => { + info!("command line to linker was too big: {}", e); + } + Err(e) => return Err(e) + } + } + + info!("falling back to passing arguments to linker via an @-file"); + let mut cmd2 = cmd.clone(); + let mut args = String::new(); + for arg in cmd2.take_args() { + args.push_str(&Escape { + arg: arg.to_str().unwrap(), + is_like_msvc: sess.target.target.options.is_like_msvc, + }.to_string()); + args.push_str("\n"); + } + let file = tmpdir.join("linker-arguments"); + let bytes = if sess.target.target.options.is_like_msvc { + let mut out = vec![]; + // start the stream with a UTF-16 BOM + for c in vec![0xFEFF].into_iter().chain(args.encode_utf16()) { + // encode in little endian + out.push(c as u8); + out.push((c >> 8) as u8); + } + out + } else { + args.into_bytes() + }; + fs::write(&file, &bytes)?; + cmd2.arg(format!("@{}", file.display())); + info!("invoking linker {:?}", cmd2); + let output = cmd2.output(); + flush_linked_file(&output, out_filename)?; + return output; + + #[cfg(unix)] + fn flush_linked_file(_: &io::Result, _: &Path) -> io::Result<()> { + Ok(()) + } + + #[cfg(windows)] + fn flush_linked_file(command_output: &io::Result, out_filename: &Path) + -> io::Result<()> + { + // On Windows, under high I/O load, output buffers are sometimes not flushed, + // even long after process exit, causing nasty, non-reproducible output bugs. + // + // File::sync_all() calls FlushFileBuffers() down the line, which solves the problem. + // + // А full writeup of the original Chrome bug can be found at + // randomascii.wordpress.com/2018/02/25/compiler-bug-linker-bug-windows-kernel-bug/amp + + if let &Ok(ref out) = command_output { + if out.status.success() { + if let Ok(of) = fs::OpenOptions::new().write(true).open(out_filename) { + of.sync_all()?; + } + } + } + + Ok(()) + } + + #[cfg(unix)] + fn command_line_too_big(err: &io::Error) -> bool { + err.raw_os_error() == Some(::libc::E2BIG) + } + + #[cfg(windows)] + fn command_line_too_big(err: &io::Error) -> bool { + const ERROR_FILENAME_EXCED_RANGE: i32 = 206; + err.raw_os_error() == Some(ERROR_FILENAME_EXCED_RANGE) + } + + struct Escape<'a> { + arg: &'a str, + is_like_msvc: bool, + } + + impl<'a> fmt::Display for Escape<'a> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + if self.is_like_msvc { + // This is "documented" at + // https://msdn.microsoft.com/en-us/library/4xdcbak7.aspx + // + // Unfortunately there's not a great specification of the + // syntax I could find online (at least) but some local + // testing showed that this seemed sufficient-ish to catch + // at least a few edge cases. + write!(f, "\"")?; + for c in self.arg.chars() { + match c { + '"' => write!(f, "\\{}", c)?, + c => write!(f, "{}", c)?, + } + } + write!(f, "\"")?; + } else { + // This is documented at https://linux.die.net/man/1/ld, namely: + // + // > Options in file are separated by whitespace. A whitespace + // > character may be included in an option by surrounding the + // > entire option in either single or double quotes. Any + // > character (including a backslash) may be included by + // > prefixing the character to be included with a backslash. + // + // We put an argument on each line, so all we need to do is + // ensure the line is interpreted as one whole argument. + for c in self.arg.chars() { + match c { + '\\' | + ' ' => write!(f, "\\{}", c)?, + c => write!(f, "{}", c)?, + } + } + } + Ok(()) + } + } +} + +fn link_args(cmd: &mut Linker, + sess: &Session, + crate_type: config::CrateType, + tmpdir: &Path, + out_filename: &Path, + codegen_results: &CodegenResults) { + + // Linker plugins should be specified early in the list of arguments + cmd.cross_lang_lto(); + + // The default library location, we need this to find the runtime. + // The location of crates will be determined as needed. + let lib_path = sess.target_filesearch(PathKind::All).get_lib_path(); + + // target descriptor + let t = &sess.target.target; + + cmd.include_path(&fix_windows_verbatim_for_gcc(&lib_path)); + for obj in codegen_results.modules.iter().filter_map(|m| m.object.as_ref()) { + cmd.add_object(obj); + } + cmd.output_filename(out_filename); + + if crate_type == config::CrateTypeExecutable && + sess.target.target.options.is_like_windows { + if let Some(ref s) = codegen_results.windows_subsystem { + cmd.subsystem(s); + } + } + + // If we're building a dynamic library then some platforms need to make sure + // that all symbols are exported correctly from the dynamic library. + if crate_type != config::CrateTypeExecutable || + sess.target.target.options.is_like_emscripten { + cmd.export_symbols(tmpdir, crate_type); + } + + // When linking a dynamic library, we put the metadata into a section of the + // executable. This metadata is in a separate object file from the main + // object file, so we link that in here. + if crate_type == config::CrateTypeDylib || + crate_type == config::CrateTypeProcMacro { + if let Some(obj) = codegen_results.metadata_module.object.as_ref() { + cmd.add_object(obj); + } + } + + let obj = codegen_results.allocator_module + .as_ref() + .and_then(|m| m.object.as_ref()); + if let Some(obj) = obj { + cmd.add_object(obj); + } + + // Try to strip as much out of the generated object by removing unused + // sections if possible. See more comments in linker.rs + if !sess.opts.cg.link_dead_code { + let keep_metadata = crate_type == config::CrateTypeDylib; + cmd.gc_sections(keep_metadata); + } + + let used_link_args = &codegen_results.crate_info.link_args; + + if crate_type == config::CrateTypeExecutable { + let mut position_independent_executable = false; + + if t.options.position_independent_executables { + let empty_vec = Vec::new(); + let args = sess.opts.cg.link_args.as_ref().unwrap_or(&empty_vec); + let more_args = &sess.opts.cg.link_arg; + let mut args = args.iter().chain(more_args.iter()).chain(used_link_args.iter()); + + if get_reloc_model(sess) == llvm::RelocMode::PIC + && !sess.crt_static() && !args.any(|x| *x == "-static") { + position_independent_executable = true; + } + } + + if position_independent_executable { + cmd.position_independent_executable(); + } else { + // recent versions of gcc can be configured to generate position + // independent executables by default. We have to pass -no-pie to + // explicitly turn that off. Not applicable to ld. + if sess.target.target.options.linker_is_gnu + && sess.linker_flavor() != LinkerFlavor::Ld { + cmd.no_position_independent_executable(); + } + } + } + + let relro_level = match sess.opts.debugging_opts.relro_level { + Some(level) => level, + None => t.options.relro_level, + }; + match relro_level { + RelroLevel::Full => { + cmd.full_relro(); + }, + RelroLevel::Partial => { + cmd.partial_relro(); + }, + RelroLevel::Off => { + cmd.no_relro(); + }, + RelroLevel::None => { + }, + } + + // Pass optimization flags down to the linker. + cmd.optimize(); + + // Pass debuginfo flags down to the linker. + cmd.debuginfo(); + + // We want to prevent the compiler from accidentally leaking in any system + // libraries, so we explicitly ask gcc to not link to any libraries by + // default. Note that this does not happen for windows because windows pulls + // in some large number of libraries and I couldn't quite figure out which + // subset we wanted. + if t.options.no_default_libraries { + cmd.no_default_libraries(); + } + + // Take careful note of the ordering of the arguments we pass to the linker + // here. Linkers will assume that things on the left depend on things to the + // right. Things on the right cannot depend on things on the left. This is + // all formally implemented in terms of resolving symbols (libs on the right + // resolve unknown symbols of libs on the left, but not vice versa). + // + // For this reason, we have organized the arguments we pass to the linker as + // such: + // + // 1. The local object that LLVM just generated + // 2. Local native libraries + // 3. Upstream rust libraries + // 4. Upstream native libraries + // + // The rationale behind this ordering is that those items lower down in the + // list can't depend on items higher up in the list. For example nothing can + // depend on what we just generated (e.g. that'd be a circular dependency). + // Upstream rust libraries are not allowed to depend on our local native + // libraries as that would violate the structure of the DAG, in that + // scenario they are required to link to them as well in a shared fashion. + // + // Note that upstream rust libraries may contain native dependencies as + // well, but they also can't depend on what we just started to add to the + // link line. And finally upstream native libraries can't depend on anything + // in this DAG so far because they're only dylibs and dylibs can only depend + // on other dylibs (e.g. other native deps). + add_local_native_libraries(cmd, sess, codegen_results); + add_upstream_rust_crates(cmd, sess, codegen_results, crate_type, tmpdir); + add_upstream_native_libraries(cmd, sess, codegen_results, crate_type); + + // Tell the linker what we're doing. + if crate_type != config::CrateTypeExecutable { + cmd.build_dylib(out_filename); + } + if crate_type == config::CrateTypeExecutable && sess.crt_static() { + cmd.build_static_executable(); + } + + if sess.opts.debugging_opts.pgo_gen.is_some() { + cmd.pgo_gen(); + } + + // FIXME (#2397): At some point we want to rpath our guesses as to + // where extern libraries might live, based on the + // addl_lib_search_paths + if sess.opts.cg.rpath { + let sysroot = sess.sysroot(); + let target_triple = sess.opts.target_triple.triple(); + let mut get_install_prefix_lib_path = || { + let install_prefix = option_env!("CFG_PREFIX").expect("CFG_PREFIX"); + let tlib = filesearch::relative_target_lib_path(sysroot, target_triple); + let mut path = PathBuf::from(install_prefix); + path.push(&tlib); + + path + }; + let mut rpath_config = RPathConfig { + used_crates: &codegen_results.crate_info.used_crates_dynamic, + out_filename: out_filename.to_path_buf(), + has_rpath: sess.target.target.options.has_rpath, + is_like_osx: sess.target.target.options.is_like_osx, + linker_is_gnu: sess.target.target.options.linker_is_gnu, + get_install_prefix_lib_path: &mut get_install_prefix_lib_path, + }; + cmd.args(&rpath::get_rpath_flags(&mut rpath_config)); + } + + // Finally add all the linker arguments provided on the command line along + // with any #[link_args] attributes found inside the crate + if let Some(ref args) = sess.opts.cg.link_args { + cmd.args(args); + } + cmd.args(&sess.opts.cg.link_arg); + cmd.args(&used_link_args); +} + +// # Native library linking +// +// User-supplied library search paths (-L on the command line). These are +// the same paths used to find Rust crates, so some of them may have been +// added already by the previous crate linking code. This only allows them +// to be found at compile time so it is still entirely up to outside +// forces to make sure that library can be found at runtime. +// +// Also note that the native libraries linked here are only the ones located +// in the current crate. Upstream crates with native library dependencies +// may have their native library pulled in above. +fn add_local_native_libraries(cmd: &mut Linker, + sess: &Session, + codegen_results: &CodegenResults) { + sess.target_filesearch(PathKind::All).for_each_lib_search_path(|path, k| { + match k { + PathKind::Framework => { cmd.framework_path(path); } + _ => { cmd.include_path(&fix_windows_verbatim_for_gcc(path)); } + } + }); + + let relevant_libs = codegen_results.crate_info.used_libraries.iter().filter(|l| { + relevant_lib(sess, l) + }); + + let search_path = archive_search_paths(sess); + for lib in relevant_libs { + match lib.kind { + NativeLibraryKind::NativeUnknown => cmd.link_dylib(&lib.name.as_str()), + NativeLibraryKind::NativeFramework => cmd.link_framework(&lib.name.as_str()), + NativeLibraryKind::NativeStaticNobundle => cmd.link_staticlib(&lib.name.as_str()), + NativeLibraryKind::NativeStatic => cmd.link_whole_staticlib(&lib.name.as_str(), + &search_path) + } + } +} + +// # Rust Crate linking +// +// Rust crates are not considered at all when creating an rlib output. All +// 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, + codegen_results: &CodegenResults, + crate_type: config::CrateType, + 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. + // + // Linking to a rlib involves just passing it to the linker (the linker + // 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 = formats.get(&crate_type).unwrap(); + + // Invoke get_used_crates to ensure that we get a topological sorting of + // crates. + let deps = &codegen_results.crate_info.used_crates_dynamic; + + // There's a few internal crates in the standard library (aka libcore and + // libstd) which actually have a circular dependence upon one another. This + // currently arises through "weak lang items" where libcore requires things + // like `rust_begin_unwind` but libstd ends up defining it. To get this + // circular dependence to work correctly in all situations we'll need to be + // sure to correctly apply the `--start-group` and `--end-group` options to + // GNU linkers, otherwise if we don't use any other symbol from the standard + // library it'll get discarded and the whole application won't link. + // + // In this loop we're calculating the `group_end`, after which crate to + // pass `--end-group` and `group_start`, before which crate to pass + // `--start-group`. We currently do this by passing `--end-group` after + // the first crate (when iterating backwards) that requires a lang item + // defined somewhere else. Once that's set then when we've defined all the + // necessary lang items we'll pass `--start-group`. + // + // Note that this isn't amazing logic for now but it should do the trick + // for the current implementation of the standard library. + let mut group_end = None; + let mut group_start = None; + let mut end_with = FxHashSet(); + let info = &codegen_results.crate_info; + for &(cnum, _) in deps.iter().rev() { + if let Some(missing) = info.missing_lang_items.get(&cnum) { + end_with.extend(missing.iter().cloned()); + if end_with.len() > 0 && group_end.is_none() { + group_end = Some(cnum); + } + } + end_with.retain(|item| info.lang_item_to_crate.get(item) != Some(&cnum)); + if end_with.len() == 0 && group_end.is_some() { + group_start = Some(cnum); + break + } + } + + // If we didn't end up filling in all lang items from upstream crates then + // we'll be filling it in with our crate. This probably means we're the + // standard library itself, so skip this for now. + if group_end.is_some() && group_start.is_none() { + group_end = None; + } + + let mut compiler_builtins = None; + + for &(cnum, _) in deps.iter() { + if group_start == Some(cnum) { + cmd.group_start(); + } + + // 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 src = &codegen_results.crate_info.used_crate_source[&cnum]; + match data[cnum.as_usize() - 1] { + _ if codegen_results.crate_info.profiler_runtime == Some(cnum) => { + add_static_crate(cmd, sess, codegen_results, tmpdir, crate_type, cnum); + } + _ if codegen_results.crate_info.sanitizer_runtime == Some(cnum) => { + link_sanitizer_runtime(cmd, sess, codegen_results, tmpdir, cnum); + } + // compiler-builtins are always placed last to ensure that they're + // linked correctly. + _ if codegen_results.crate_info.compiler_builtins == Some(cnum) => { + assert!(compiler_builtins.is_none()); + compiler_builtins = Some(cnum); + } + Linkage::NotLinked | + Linkage::IncludedFromDylib => {} + Linkage::Static => { + add_static_crate(cmd, sess, codegen_results, tmpdir, crate_type, cnum); + } + Linkage::Dynamic => { + add_dynamic_crate(cmd, sess, &src.dylib.as_ref().unwrap().0) + } + } + + if group_end == Some(cnum) { + cmd.group_end(); + } + } + + // compiler-builtins are always placed last to ensure that they're + // linked correctly. + // We must always link the `compiler_builtins` crate statically. Even if it + // was already "included" in a dylib (e.g. `libstd` when `-C prefer-dynamic` + // is used) + if let Some(cnum) = compiler_builtins { + add_static_crate(cmd, sess, codegen_results, tmpdir, crate_type, cnum); + } + + // Converts a library file-stem into a cc -l argument + fn unlib<'a>(config: &config::Config, stem: &'a str) -> &'a str { + if stem.starts_with("lib") && !config.target.options.is_like_windows { + &stem[3..] + } else { + stem + } + } + + // We must link the sanitizer runtime using -Wl,--whole-archive but since + // it's packed in a .rlib, it contains stuff that are not objects that will + // make the linker error. So we must remove those bits from the .rlib before + // linking it. + fn link_sanitizer_runtime(cmd: &mut Linker, + sess: &Session, + codegen_results: &CodegenResults, + tmpdir: &Path, + cnum: CrateNum) { + let src = &codegen_results.crate_info.used_crate_source[&cnum]; + let cratepath = &src.rlib.as_ref().unwrap().0; + + if sess.target.target.options.is_like_osx { + // On Apple platforms, the sanitizer is always built as a dylib, and + // LLVM will link to `@rpath/*.dylib`, so we need to specify an + // rpath to the library as well (the rpath should be absolute, see + // PR #41352 for details). + // + // FIXME: Remove this logic into librustc_*san once Cargo supports it + let rpath = cratepath.parent().unwrap(); + let rpath = rpath.to_str().expect("non-utf8 component in path"); + cmd.args(&["-Wl,-rpath".into(), "-Xlinker".into(), rpath.into()]); + } + + let dst = tmpdir.join(cratepath.file_name().unwrap()); + let cfg = archive_config(sess, &dst, Some(cratepath)); + let mut archive = ArchiveBuilder::new(cfg); + archive.update_symbols(); + + for f in archive.src_files() { + if f.ends_with(RLIB_BYTECODE_EXTENSION) || f == METADATA_FILENAME { + archive.remove_file(&f); + continue + } + } + + archive.build(); + + cmd.link_whole_rlib(&dst); + } + + // Adds the static "rlib" versions of all crates to the command line. + // There's a bit of magic which happens here specifically related to LTO and + // dynamic libraries. Specifically: + // + // * For LTO, we remove upstream object files. + // * For dylibs we remove metadata and bytecode from upstream rlibs + // + // When performing LTO, almost(*) all of the bytecode from the upstream + // libraries has already been included in our object file output. As a + // result we need to remove the object files in the upstream libraries so + // the linker doesn't try to include them twice (or whine about duplicate + // symbols). We must continue to include the rest of the rlib, however, as + // it may contain static native libraries which must be linked in. + // + // (*) Crates marked with `#![no_builtins]` don't participate in LTO and + // their bytecode wasn't included. The object files in those libraries must + // still be passed to the linker. + // + // When making a dynamic library, linkers by default don't include any + // object files in an archive if they're not necessary to resolve the link. + // We basically want to convert the archive (rlib) to a dylib, though, so we + // *do* want everything included in the output, regardless of whether the + // linker thinks it's needed or not. As a result we must use the + // --whole-archive option (or the platform equivalent). When using this + // option the linker will fail if there are non-objects in the archive (such + // as our own metadata and/or bytecode). All in all, for rlibs to be + // entirely included in dylibs, we need to remove all non-object files. + // + // Note, however, that if we're not doing LTO or we're not producing a dylib + // (aka we're making an executable), we can just pass the rlib blindly to + // the linker (fast) because it's fine if it's not actually included as + // we're at the end of the dependency chain. + fn add_static_crate(cmd: &mut Linker, + sess: &Session, + codegen_results: &CodegenResults, + tmpdir: &Path, + crate_type: config::CrateType, + cnum: CrateNum) { + let src = &codegen_results.crate_info.used_crate_source[&cnum]; + let cratepath = &src.rlib.as_ref().unwrap().0; + + // See the comment above in `link_staticlib` and `link_rlib` for why if + // there's a static library that's not relevant we skip all object + // files. + let native_libs = &codegen_results.crate_info.native_libraries[&cnum]; + let skip_native = native_libs.iter().any(|lib| { + lib.kind == NativeLibraryKind::NativeStatic && !relevant_lib(sess, lib) + }); + + if (!is_full_lto_enabled(sess) || + ignored_for_lto(sess, &codegen_results.crate_info, cnum)) && + crate_type != config::CrateTypeDylib && + !skip_native { + cmd.link_rlib(&fix_windows_verbatim_for_gcc(cratepath)); + return + } + + let dst = tmpdir.join(cratepath.file_name().unwrap()); + let name = cratepath.file_name().unwrap().to_str().unwrap(); + let name = &name[3..name.len() - 5]; // chop off lib/.rlib + + time(sess, &format!("altering {}.rlib", name), || { + let cfg = archive_config(sess, &dst, Some(cratepath)); + let mut archive = ArchiveBuilder::new(cfg); + archive.update_symbols(); + + let mut any_objects = false; + for f in archive.src_files() { + if f.ends_with(RLIB_BYTECODE_EXTENSION) || f == METADATA_FILENAME { + archive.remove_file(&f); + continue + } + + let canonical = f.replace("-", "_"); + let canonical_name = name.replace("-", "_"); + + // Look for `.rcgu.o` at the end of the filename to conclude + // that this is a Rust-related object file. + fn looks_like_rust(s: &str) -> bool { + let path = Path::new(s); + let ext = path.extension().and_then(|s| s.to_str()); + if ext != Some(OutputType::Object.extension()) { + return false + } + let ext2 = path.file_stem() + .and_then(|s| Path::new(s).extension()) + .and_then(|s| s.to_str()); + ext2 == Some(RUST_CGU_EXT) + } + + let is_rust_object = + canonical.starts_with(&canonical_name) && + looks_like_rust(&f); + + // If we've been requested to skip all native object files + // (those not generated by the rust compiler) then we can skip + // this file. See above for why we may want to do this. + let skip_because_cfg_say_so = skip_native && !is_rust_object; + + // If we're performing LTO and this is a rust-generated object + // file, then we don't need the object file as it's part of the + // LTO module. Note that `#![no_builtins]` is excluded from LTO, + // though, so we let that object file slide. + let skip_because_lto = is_full_lto_enabled(sess) && + is_rust_object && + (sess.target.target.options.no_builtins || + !codegen_results.crate_info.is_no_builtins.contains(&cnum)); + + if skip_because_cfg_say_so || skip_because_lto { + archive.remove_file(&f); + } else { + any_objects = true; + } + } + + if !any_objects { + return + } + archive.build(); + + // If we're creating a dylib, then we need to include the + // whole of each object in our archive into that artifact. This is + // because a `dylib` can be reused as an intermediate artifact. + // + // Note, though, that we don't want to include the whole of a + // compiler-builtins crate (e.g. compiler-rt) because it'll get + // repeatedly linked anyway. + if crate_type == config::CrateTypeDylib && + codegen_results.crate_info.compiler_builtins != Some(cnum) { + cmd.link_whole_rlib(&fix_windows_verbatim_for_gcc(&dst)); + } else { + cmd.link_rlib(&fix_windows_verbatim_for_gcc(&dst)); + } + }); + } + + // Same thing as above, but for dynamic crates instead of static crates. + fn add_dynamic_crate(cmd: &mut Linker, sess: &Session, cratepath: &Path) { + // If we're performing LTO, then it should have been previously required + // that all upstream rust dependencies were available in an rlib format. + assert!(!is_full_lto_enabled(sess)); + + // Just need to tell the linker about where the library lives and + // what its name is + let parent = cratepath.parent(); + if let Some(dir) = parent { + cmd.include_path(&fix_windows_verbatim_for_gcc(dir)); + } + let filestem = cratepath.file_stem().unwrap().to_str().unwrap(); + cmd.link_rust_dylib(&unlib(&sess.target, filestem), + parent.unwrap_or(Path::new(""))); + } +} + +// Link in all of our upstream crates' native dependencies. Remember that +// all of these upstream native dependencies are all non-static +// dependencies. We've got two cases then: +// +// 1. The upstream crate is an rlib. In this case we *must* link in the +// native dependency because the rlib is just an archive. +// +// 2. The upstream crate is a dylib. In order to use the dylib, we have to +// have the dependency present on the system somewhere. Thus, we don't +// gain a whole lot from not linking in the dynamic dependency to this +// crate as well. +// +// The use case for this is a little subtle. In theory the native +// dependencies of a crate are purely an implementation detail of the crate +// itself, but the problem arises with generic and inlined functions. If a +// generic function calls a native function, then the generic function must +// be instantiated in the target crate, meaning that the native symbol must +// also be resolved in the target crate. +fn add_upstream_native_libraries(cmd: &mut Linker, + sess: &Session, + codegen_results: &CodegenResults, + crate_type: config::CrateType) { + // Be sure to use a topological sorting of crates because there may be + // interdependencies between native libraries. When passing -nodefaultlibs, + // for example, almost all native libraries depend on libc, so we have to + // make sure that's all the way at the right (liblibc is near the base of + // the dependency chain). + // + // This passes RequireStatic, but the actual requirement doesn't matter, + // we're just getting an ordering of crate numbers, we're not worried about + // the paths. + let formats = sess.dependency_formats.borrow(); + let data = formats.get(&crate_type).unwrap(); + + let crates = &codegen_results.crate_info.used_crates_static; + for &(cnum, _) in crates { + for lib in codegen_results.crate_info.native_libraries[&cnum].iter() { + if !relevant_lib(sess, &lib) { + continue + } + match lib.kind { + NativeLibraryKind::NativeUnknown => cmd.link_dylib(&lib.name.as_str()), + NativeLibraryKind::NativeFramework => cmd.link_framework(&lib.name.as_str()), + NativeLibraryKind::NativeStaticNobundle => { + // Link "static-nobundle" native libs only if the crate they originate from + // is being linked statically to the current crate. If it's linked dynamically + // or is an rlib already included via some other dylib crate, the symbols from + // native libs will have already been included in that dylib. + if data[cnum.as_usize() - 1] == Linkage::Static { + cmd.link_staticlib(&lib.name.as_str()) + } + }, + // ignore statically included native libraries here as we've + // already included them when we included the rust library + // previously + NativeLibraryKind::NativeStatic => {} + } + } + } +} + +fn relevant_lib(sess: &Session, lib: &NativeLibrary) -> bool { + match lib.cfg { + Some(ref cfg) => attr::cfg_matches(cfg, &sess.parse_sess, None), + None => true, + } +} + +fn is_full_lto_enabled(sess: &Session) -> bool { + match sess.lto() { + Lto::Yes | + Lto::Thin | + Lto::Fat => true, + Lto::No | + Lto::ThinLocal => false, + } +} diff --git a/src/librustc_codegen_llvm/back/linker.rs b/src/librustc_codegen_llvm/back/linker.rs new file mode 100644 index 00000000000..dd1983bdc17 --- /dev/null +++ b/src/librustc_codegen_llvm/back/linker.rs @@ -0,0 +1,1037 @@ +// 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. + +use std::collections::HashMap; +use std::ffi::{OsStr, OsString}; +use std::fs::{self, File}; +use std::io::prelude::*; +use std::io::{self, BufWriter}; +use std::path::{Path, PathBuf}; + +use back::archive; +use back::command::Command; +use back::symbol_export; +use rustc::hir::def_id::{LOCAL_CRATE, CrateNum}; +use rustc::middle::dependency_format::Linkage; +use rustc::session::Session; +use rustc::session::config::{self, CrateType, OptLevel, DebugInfoLevel, + CrossLangLto}; +use rustc::ty::TyCtxt; +use rustc_target::spec::{LinkerFlavor, LldFlavor}; +use serialize::{json, Encoder}; + +/// For all the linkers we support, and information they might +/// need out of the shared crate context before we get rid of it. +pub struct LinkerInfo { + exports: HashMap>, +} + +impl LinkerInfo { + pub fn new(tcx: TyCtxt) -> LinkerInfo { + LinkerInfo { + exports: tcx.sess.crate_types.borrow().iter().map(|&c| { + (c, exported_symbols(tcx, c)) + }).collect(), + } + } + + pub fn to_linker<'a>(&'a self, + cmd: Command, + sess: &'a Session) -> Box { + match sess.linker_flavor() { + LinkerFlavor::Lld(LldFlavor::Link) | + LinkerFlavor::Msvc => { + Box::new(MsvcLinker { + cmd, + sess, + info: self + }) as Box + } + LinkerFlavor::Em => { + Box::new(EmLinker { + cmd, + sess, + info: self + }) as Box + } + LinkerFlavor::Gcc => { + Box::new(GccLinker { + cmd, + sess, + info: self, + hinted_static: false, + is_ld: false, + }) as Box + } + + LinkerFlavor::Lld(LldFlavor::Ld) | + LinkerFlavor::Lld(LldFlavor::Ld64) | + LinkerFlavor::Ld => { + Box::new(GccLinker { + cmd, + sess, + info: self, + hinted_static: false, + is_ld: true, + }) as Box + } + + LinkerFlavor::Lld(LldFlavor::Wasm) => { + Box::new(WasmLd { + cmd, + }) as Box + } + } + } +} + +/// Linker abstraction used by back::link to build up the command to invoke a +/// linker. +/// +/// This trait is the total list of requirements needed by `back::link` and +/// represents the meaning of each option being passed down. This trait is then +/// used to dispatch on whether a GNU-like linker (generally `ld.exe`) or an +/// MSVC linker (e.g. `link.exe`) is being used. +pub trait Linker { + fn link_dylib(&mut self, lib: &str); + fn link_rust_dylib(&mut self, lib: &str, path: &Path); + fn link_framework(&mut self, framework: &str); + fn link_staticlib(&mut self, lib: &str); + fn link_rlib(&mut self, lib: &Path); + fn link_whole_rlib(&mut self, lib: &Path); + fn link_whole_staticlib(&mut self, lib: &str, search_path: &[PathBuf]); + fn include_path(&mut self, path: &Path); + fn framework_path(&mut self, path: &Path); + fn output_filename(&mut self, path: &Path); + fn add_object(&mut self, path: &Path); + fn gc_sections(&mut self, keep_metadata: bool); + fn position_independent_executable(&mut self); + fn no_position_independent_executable(&mut self); + fn full_relro(&mut self); + fn partial_relro(&mut self); + fn no_relro(&mut self); + fn optimize(&mut self); + fn pgo_gen(&mut self); + fn debuginfo(&mut self); + fn no_default_libraries(&mut self); + fn build_dylib(&mut self, out_filename: &Path); + fn build_static_executable(&mut self); + fn args(&mut self, args: &[String]); + fn export_symbols(&mut self, tmpdir: &Path, crate_type: CrateType); + fn subsystem(&mut self, subsystem: &str); + fn group_start(&mut self); + fn group_end(&mut self); + fn cross_lang_lto(&mut self); + // Should have been finalize(self), but we don't support self-by-value on trait objects (yet?). + fn finalize(&mut self) -> Command; +} + +pub struct GccLinker<'a> { + cmd: Command, + sess: &'a Session, + info: &'a LinkerInfo, + hinted_static: bool, // Keeps track of the current hinting mode. + // Link as ld + is_ld: bool, +} + +impl<'a> GccLinker<'a> { + /// Argument that must be passed *directly* to the linker + /// + /// These arguments need to be prepended with '-Wl,' when a gcc-style linker is used + fn linker_arg(&mut self, arg: S) -> &mut Self + where S: AsRef + { + if !self.is_ld { + let mut os = OsString::from("-Wl,"); + os.push(arg.as_ref()); + self.cmd.arg(os); + } else { + self.cmd.arg(arg); + } + self + } + + fn takes_hints(&self) -> bool { + !self.sess.target.target.options.is_like_osx + } + + // Some platforms take hints about whether a library is static or dynamic. + // For those that support this, we ensure we pass the option if the library + // was flagged "static" (most defaults are dynamic) to ensure that if + // libfoo.a and libfoo.so both exist that the right one is chosen. + fn hint_static(&mut self) { + if !self.takes_hints() { return } + if !self.hinted_static { + self.linker_arg("-Bstatic"); + self.hinted_static = true; + } + } + + fn hint_dynamic(&mut self) { + if !self.takes_hints() { return } + if self.hinted_static { + self.linker_arg("-Bdynamic"); + self.hinted_static = false; + } + } +} + +impl<'a> Linker for GccLinker<'a> { + fn link_dylib(&mut self, lib: &str) { self.hint_dynamic(); self.cmd.arg("-l").arg(lib); } + fn link_staticlib(&mut self, lib: &str) { self.hint_static(); self.cmd.arg("-l").arg(lib); } + fn link_rlib(&mut self, lib: &Path) { self.hint_static(); self.cmd.arg(lib); } + fn include_path(&mut self, path: &Path) { self.cmd.arg("-L").arg(path); } + fn framework_path(&mut self, path: &Path) { self.cmd.arg("-F").arg(path); } + fn output_filename(&mut self, path: &Path) { self.cmd.arg("-o").arg(path); } + fn add_object(&mut self, path: &Path) { self.cmd.arg(path); } + fn position_independent_executable(&mut self) { self.cmd.arg("-pie"); } + fn no_position_independent_executable(&mut self) { self.cmd.arg("-no-pie"); } + fn full_relro(&mut self) { self.linker_arg("-z,relro,-z,now"); } + fn partial_relro(&mut self) { self.linker_arg("-z,relro"); } + fn no_relro(&mut self) { self.linker_arg("-z,norelro"); } + fn build_static_executable(&mut self) { self.cmd.arg("-static"); } + fn args(&mut self, args: &[String]) { self.cmd.args(args); } + + fn link_rust_dylib(&mut self, lib: &str, _path: &Path) { + self.hint_dynamic(); + self.cmd.arg("-l").arg(lib); + } + + fn link_framework(&mut self, framework: &str) { + self.hint_dynamic(); + self.cmd.arg("-framework").arg(framework); + } + + // Here we explicitly ask that the entire archive is included into the + // result artifact. For more details see #15460, but the gist is that + // the linker will strip away any unused objects in the archive if we + // don't otherwise explicitly reference them. This can occur for + // libraries which are just providing bindings, libraries with generic + // functions, etc. + fn link_whole_staticlib(&mut self, lib: &str, search_path: &[PathBuf]) { + self.hint_static(); + let target = &self.sess.target.target; + if !target.options.is_like_osx { + self.linker_arg("--whole-archive").cmd.arg("-l").arg(lib); + self.linker_arg("--no-whole-archive"); + } else { + // -force_load is the macOS equivalent of --whole-archive, but it + // involves passing the full path to the library to link. + let mut v = OsString::from("-force_load,"); + v.push(&archive::find_library(lib, search_path, &self.sess)); + self.linker_arg(&v); + } + } + + fn link_whole_rlib(&mut self, lib: &Path) { + self.hint_static(); + if self.sess.target.target.options.is_like_osx { + let mut v = OsString::from("-force_load,"); + v.push(lib); + self.linker_arg(&v); + } else { + self.linker_arg("--whole-archive").cmd.arg(lib); + self.linker_arg("--no-whole-archive"); + } + } + + fn gc_sections(&mut self, keep_metadata: bool) { + // The dead_strip option to the linker specifies that functions and data + // unreachable by the entry point will be removed. This is quite useful + // with Rust's compilation model of compiling libraries at a time into + // one object file. For example, this brings hello world from 1.7MB to + // 458K. + // + // Note that this is done for both executables and dynamic libraries. We + // won't get much benefit from dylibs because LLVM will have already + // stripped away as much as it could. This has not been seen to impact + // link times negatively. + // + // -dead_strip can't be part of the pre_link_args because it's also used + // for partial linking when using multiple codegen units (-r). So we + // insert it here. + if self.sess.target.target.options.is_like_osx { + self.linker_arg("-dead_strip"); + } else if self.sess.target.target.options.is_like_solaris { + self.linker_arg("-z"); + self.linker_arg("ignore"); + + // If we're building a dylib, we don't use --gc-sections because LLVM + // has already done the best it can do, and we also don't want to + // eliminate the metadata. If we're building an executable, however, + // --gc-sections drops the size of hello world from 1.8MB to 597K, a 67% + // reduction. + } else if !keep_metadata { + self.linker_arg("--gc-sections"); + } + } + + fn optimize(&mut self) { + if !self.sess.target.target.options.linker_is_gnu { return } + + // GNU-style linkers support optimization with -O. GNU ld doesn't + // need a numeric argument, but other linkers do. + if self.sess.opts.optimize == config::OptLevel::Default || + self.sess.opts.optimize == config::OptLevel::Aggressive { + self.linker_arg("-O1"); + } + } + + fn pgo_gen(&mut self) { + if !self.sess.target.target.options.linker_is_gnu { return } + + // If we're doing PGO generation stuff and on a GNU-like linker, use the + // "-u" flag to properly pull in the profiler runtime bits. + // + // This is because LLVM otherwise won't add the needed initialization + // for us on Linux (though the extra flag should be harmless if it + // does). + // + // See https://reviews.llvm.org/D14033 and https://reviews.llvm.org/D14030. + // + // Though it may be worth to try to revert those changes upstream, since + // the overhead of the initialization should be minor. + self.cmd.arg("-u"); + self.cmd.arg("__llvm_profile_runtime"); + } + + fn debuginfo(&mut self) { + match self.sess.opts.debuginfo { + DebugInfoLevel::NoDebugInfo => { + // If we are building without debuginfo enabled and we were called with + // `-Zstrip-debuginfo-if-disabled=yes`, tell the linker to strip any debuginfo + // found when linking to get rid of symbols from libstd. + match self.sess.opts.debugging_opts.strip_debuginfo_if_disabled { + Some(true) => { self.linker_arg("-S"); }, + _ => {}, + } + }, + _ => {}, + }; + } + + fn no_default_libraries(&mut self) { + if !self.is_ld { + self.cmd.arg("-nodefaultlibs"); + } + } + + fn build_dylib(&mut self, out_filename: &Path) { + // On mac we need to tell the linker to let this library be rpathed + if self.sess.target.target.options.is_like_osx { + self.cmd.arg("-dynamiclib"); + self.linker_arg("-dylib"); + + // Note that the `osx_rpath_install_name` option here is a hack + // purely to support rustbuild right now, we should get a more + // principled solution at some point to force the compiler to pass + // the right `-Wl,-install_name` with an `@rpath` in it. + if self.sess.opts.cg.rpath || + self.sess.opts.debugging_opts.osx_rpath_install_name { + let mut v = OsString::from("-install_name,@rpath/"); + v.push(out_filename.file_name().unwrap()); + self.linker_arg(&v); + } + } else { + self.cmd.arg("-shared"); + } + } + + fn export_symbols(&mut self, tmpdir: &Path, crate_type: CrateType) { + // If we're compiling a dylib, then we let symbol visibility in object + // files to take care of whether they're exported or not. + // + // If we're compiling a cdylib, however, we manually create a list of + // exported symbols to ensure we don't expose any more. The object files + // have far more public symbols than we actually want to export, so we + // hide them all here. + if crate_type == CrateType::CrateTypeDylib || + crate_type == CrateType::CrateTypeProcMacro { + return + } + + let mut arg = OsString::new(); + let path = tmpdir.join("list"); + + debug!("EXPORTED SYMBOLS:"); + + if self.sess.target.target.options.is_like_osx { + // Write a plain, newline-separated list of symbols + let res = (|| -> io::Result<()> { + let mut f = BufWriter::new(File::create(&path)?); + for sym in self.info.exports[&crate_type].iter() { + debug!(" _{}", sym); + writeln!(f, "_{}", sym)?; + } + Ok(()) + })(); + if let Err(e) = res { + self.sess.fatal(&format!("failed to write lib.def file: {}", e)); + } + } else { + // Write an LD version script + let res = (|| -> io::Result<()> { + let mut f = BufWriter::new(File::create(&path)?); + writeln!(f, "{{\n global:")?; + for sym in self.info.exports[&crate_type].iter() { + debug!(" {};", sym); + writeln!(f, " {};", sym)?; + } + writeln!(f, "\n local:\n *;\n}};")?; + Ok(()) + })(); + if let Err(e) = res { + self.sess.fatal(&format!("failed to write version script: {}", e)); + } + } + + if self.sess.target.target.options.is_like_osx { + if !self.is_ld { + arg.push("-Wl,") + } + arg.push("-exported_symbols_list,"); + } else if self.sess.target.target.options.is_like_solaris { + if !self.is_ld { + arg.push("-Wl,") + } + arg.push("-M,"); + } else { + if !self.is_ld { + arg.push("-Wl,") + } + arg.push("--version-script="); + } + + arg.push(&path); + self.cmd.arg(arg); + } + + fn subsystem(&mut self, subsystem: &str) { + self.linker_arg(&format!("--subsystem,{}", subsystem)); + } + + fn finalize(&mut self) -> Command { + self.hint_dynamic(); // Reset to default before returning the composed command line. + let mut cmd = Command::new(""); + ::std::mem::swap(&mut cmd, &mut self.cmd); + cmd + } + + fn group_start(&mut self) { + if !self.sess.target.target.options.is_like_osx { + self.linker_arg("--start-group"); + } + } + + fn group_end(&mut self) { + if !self.sess.target.target.options.is_like_osx { + self.linker_arg("--end-group"); + } + } + + fn cross_lang_lto(&mut self) { + match self.sess.opts.debugging_opts.cross_lang_lto { + CrossLangLto::Disabled | + CrossLangLto::NoLink => { + // Nothing to do + } + CrossLangLto::LinkerPlugin(ref path) => { + self.linker_arg(&format!("-plugin={}", path.display())); + + let opt_level = match self.sess.opts.optimize { + config::OptLevel::No => "O0", + config::OptLevel::Less => "O1", + config::OptLevel::Default => "O2", + config::OptLevel::Aggressive => "O3", + config::OptLevel::Size => "Os", + config::OptLevel::SizeMin => "Oz", + }; + + self.linker_arg(&format!("-plugin-opt={}", opt_level)); + self.linker_arg(&format!("-plugin-opt=mcpu={}", self.sess.target_cpu())); + + match self.sess.opts.cg.lto { + config::Lto::Thin | + config::Lto::ThinLocal => { + self.linker_arg(&format!("-plugin-opt=thin")); + } + config::Lto::Fat | + config::Lto::Yes | + config::Lto::No => { + // default to regular LTO + } + } + } + } + } +} + +pub struct MsvcLinker<'a> { + cmd: Command, + sess: &'a Session, + info: &'a LinkerInfo +} + +impl<'a> Linker for MsvcLinker<'a> { + fn link_rlib(&mut self, lib: &Path) { self.cmd.arg(lib); } + fn add_object(&mut self, path: &Path) { self.cmd.arg(path); } + fn args(&mut self, args: &[String]) { self.cmd.args(args); } + + fn build_dylib(&mut self, out_filename: &Path) { + self.cmd.arg("/DLL"); + let mut arg: OsString = "/IMPLIB:".into(); + arg.push(out_filename.with_extension("dll.lib")); + self.cmd.arg(arg); + } + + fn build_static_executable(&mut self) { + // noop + } + + fn gc_sections(&mut self, _keep_metadata: bool) { + // MSVC's ICF (Identical COMDAT Folding) link optimization is + // slow for Rust and thus we disable it by default when not in + // optimization build. + if self.sess.opts.optimize != config::OptLevel::No { + self.cmd.arg("/OPT:REF,ICF"); + } else { + // It is necessary to specify NOICF here, because /OPT:REF + // implies ICF by default. + self.cmd.arg("/OPT:REF,NOICF"); + } + } + + fn link_dylib(&mut self, lib: &str) { + self.cmd.arg(&format!("{}.lib", lib)); + } + + fn link_rust_dylib(&mut self, lib: &str, path: &Path) { + // When producing a dll, the MSVC linker may not actually emit a + // `foo.lib` file if the dll doesn't actually export any symbols, so we + // check to see if the file is there and just omit linking to it if it's + // not present. + let name = format!("{}.dll.lib", lib); + if fs::metadata(&path.join(&name)).is_ok() { + self.cmd.arg(name); + } + } + + fn link_staticlib(&mut self, lib: &str) { + self.cmd.arg(&format!("{}.lib", lib)); + } + + fn position_independent_executable(&mut self) { + // noop + } + + fn no_position_independent_executable(&mut self) { + // noop + } + + fn full_relro(&mut self) { + // noop + } + + fn partial_relro(&mut self) { + // noop + } + + fn no_relro(&mut self) { + // noop + } + + fn no_default_libraries(&mut self) { + // Currently we don't pass the /NODEFAULTLIB flag to the linker on MSVC + // as there's been trouble in the past of linking the C++ standard + // library required by LLVM. This likely needs to happen one day, but + // in general Windows is also a more controlled environment than + // Unix, so it's not necessarily as critical that this be implemented. + // + // Note that there are also some licensing worries about statically + // linking some libraries which require a specific agreement, so it may + // not ever be possible for us to pass this flag. + } + + fn include_path(&mut self, path: &Path) { + let mut arg = OsString::from("/LIBPATH:"); + arg.push(path); + self.cmd.arg(&arg); + } + + fn output_filename(&mut self, path: &Path) { + let mut arg = OsString::from("/OUT:"); + arg.push(path); + self.cmd.arg(&arg); + } + + fn framework_path(&mut self, _path: &Path) { + bug!("frameworks are not supported on windows") + } + fn link_framework(&mut self, _framework: &str) { + bug!("frameworks are not supported on windows") + } + + fn link_whole_staticlib(&mut self, lib: &str, _search_path: &[PathBuf]) { + // not supported? + self.link_staticlib(lib); + } + fn link_whole_rlib(&mut self, path: &Path) { + // not supported? + self.link_rlib(path); + } + fn optimize(&mut self) { + // Needs more investigation of `/OPT` arguments + } + + fn pgo_gen(&mut self) { + // Nothing needed here. + } + + fn debuginfo(&mut self) { + // This will cause the Microsoft linker to generate a PDB file + // from the CodeView line tables in the object files. + self.cmd.arg("/DEBUG"); + + // This will cause the Microsoft linker to embed .natvis info into the the PDB file + let sysroot = self.sess.sysroot(); + let natvis_dir_path = sysroot.join("lib\\rustlib\\etc"); + if let Ok(natvis_dir) = fs::read_dir(&natvis_dir_path) { + // LLVM 5.0.0's lld-link frontend doesn't yet recognize, and chokes + // on, the /NATVIS:... flags. LLVM 6 (or earlier) should at worst ignore + // them, eventually mooting this workaround, per this landed patch: + // https://github.com/llvm-mirror/lld/commit/27b9c4285364d8d76bb43839daa100 + if let Some(ref linker_path) = self.sess.opts.cg.linker { + if let Some(linker_name) = Path::new(&linker_path).file_stem() { + if linker_name.to_str().unwrap().to_lowercase() == "lld-link" { + self.sess.warn("not embedding natvis: lld-link may not support the flag"); + return; + } + } + } + for entry in natvis_dir { + match entry { + Ok(entry) => { + let path = entry.path(); + if path.extension() == Some("natvis".as_ref()) { + let mut arg = OsString::from("/NATVIS:"); + arg.push(path); + self.cmd.arg(arg); + } + }, + Err(err) => { + self.sess.warn(&format!("error enumerating natvis directory: {}", err)); + }, + } + } + } + } + + // Currently the compiler doesn't use `dllexport` (an LLVM attribute) to + // export symbols from a dynamic library. When building a dynamic library, + // however, we're going to want some symbols exported, so this function + // generates a DEF file which lists all the symbols. + // + // The linker will read this `*.def` file and export all the symbols from + // the dynamic library. Note that this is not as simple as just exporting + // all the symbols in the current crate (as specified by `codegen.reachable`) + // but rather we also need to possibly export the symbols of upstream + // crates. Upstream rlibs may be linked statically to this dynamic library, + // in which case they may continue to transitively be used and hence need + // their symbols exported. + fn export_symbols(&mut self, + tmpdir: &Path, + crate_type: CrateType) { + let path = tmpdir.join("lib.def"); + let res = (|| -> io::Result<()> { + let mut f = BufWriter::new(File::create(&path)?); + + // Start off with the standard module name header and then go + // straight to exports. + writeln!(f, "LIBRARY")?; + writeln!(f, "EXPORTS")?; + for symbol in self.info.exports[&crate_type].iter() { + debug!(" _{}", symbol); + writeln!(f, " {}", symbol)?; + } + Ok(()) + })(); + if let Err(e) = res { + self.sess.fatal(&format!("failed to write lib.def file: {}", e)); + } + let mut arg = OsString::from("/DEF:"); + arg.push(path); + self.cmd.arg(&arg); + } + + fn subsystem(&mut self, subsystem: &str) { + // Note that previous passes of the compiler validated this subsystem, + // so we just blindly pass it to the linker. + self.cmd.arg(&format!("/SUBSYSTEM:{}", subsystem)); + + // Windows has two subsystems we're interested in right now, the console + // and windows subsystems. These both implicitly have different entry + // points (starting symbols). The console entry point starts with + // `mainCRTStartup` and the windows entry point starts with + // `WinMainCRTStartup`. These entry points, defined in system libraries, + // will then later probe for either `main` or `WinMain`, respectively to + // start the application. + // + // In Rust we just always generate a `main` function so we want control + // to always start there, so we force the entry point on the windows + // subsystem to be `mainCRTStartup` to get everything booted up + // correctly. + // + // For more information see RFC #1665 + if subsystem == "windows" { + self.cmd.arg("/ENTRY:mainCRTStartup"); + } + } + + fn finalize(&mut self) -> Command { + let mut cmd = Command::new(""); + ::std::mem::swap(&mut cmd, &mut self.cmd); + cmd + } + + // MSVC doesn't need group indicators + fn group_start(&mut self) {} + fn group_end(&mut self) {} + + fn cross_lang_lto(&mut self) { + // Do nothing + } +} + +pub struct EmLinker<'a> { + cmd: Command, + sess: &'a Session, + info: &'a LinkerInfo +} + +impl<'a> Linker for EmLinker<'a> { + fn include_path(&mut self, path: &Path) { + self.cmd.arg("-L").arg(path); + } + + fn link_staticlib(&mut self, lib: &str) { + self.cmd.arg("-l").arg(lib); + } + + fn output_filename(&mut self, path: &Path) { + self.cmd.arg("-o").arg(path); + } + + fn add_object(&mut self, path: &Path) { + self.cmd.arg(path); + } + + fn link_dylib(&mut self, lib: &str) { + // Emscripten always links statically + self.link_staticlib(lib); + } + + fn link_whole_staticlib(&mut self, lib: &str, _search_path: &[PathBuf]) { + // not supported? + self.link_staticlib(lib); + } + + fn link_whole_rlib(&mut self, lib: &Path) { + // not supported? + self.link_rlib(lib); + } + + fn link_rust_dylib(&mut self, lib: &str, _path: &Path) { + self.link_dylib(lib); + } + + fn link_rlib(&mut self, lib: &Path) { + self.add_object(lib); + } + + fn position_independent_executable(&mut self) { + // noop + } + + fn no_position_independent_executable(&mut self) { + // noop + } + + fn full_relro(&mut self) { + // noop + } + + fn partial_relro(&mut self) { + // noop + } + + fn no_relro(&mut self) { + // noop + } + + fn args(&mut self, args: &[String]) { + self.cmd.args(args); + } + + fn framework_path(&mut self, _path: &Path) { + bug!("frameworks are not supported on Emscripten") + } + + fn link_framework(&mut self, _framework: &str) { + bug!("frameworks are not supported on Emscripten") + } + + fn gc_sections(&mut self, _keep_metadata: bool) { + // noop + } + + fn optimize(&mut self) { + // Emscripten performs own optimizations + self.cmd.arg(match self.sess.opts.optimize { + OptLevel::No => "-O0", + OptLevel::Less => "-O1", + OptLevel::Default => "-O2", + OptLevel::Aggressive => "-O3", + OptLevel::Size => "-Os", + OptLevel::SizeMin => "-Oz" + }); + // Unusable until https://github.com/rust-lang/rust/issues/38454 is resolved + self.cmd.args(&["--memory-init-file", "0"]); + } + + fn pgo_gen(&mut self) { + // noop, but maybe we need something like the gnu linker? + } + + fn debuginfo(&mut self) { + // Preserve names or generate source maps depending on debug info + self.cmd.arg(match self.sess.opts.debuginfo { + DebugInfoLevel::NoDebugInfo => "-g0", + DebugInfoLevel::LimitedDebugInfo => "-g3", + DebugInfoLevel::FullDebugInfo => "-g4" + }); + } + + fn no_default_libraries(&mut self) { + self.cmd.args(&["-s", "DEFAULT_LIBRARY_FUNCS_TO_INCLUDE=[]"]); + } + + fn build_dylib(&mut self, _out_filename: &Path) { + bug!("building dynamic library is unsupported on Emscripten") + } + + fn build_static_executable(&mut self) { + // noop + } + + fn export_symbols(&mut self, _tmpdir: &Path, crate_type: CrateType) { + let symbols = &self.info.exports[&crate_type]; + + debug!("EXPORTED SYMBOLS:"); + + self.cmd.arg("-s"); + + let mut arg = OsString::from("EXPORTED_FUNCTIONS="); + let mut encoded = String::new(); + + { + let mut encoder = json::Encoder::new(&mut encoded); + let res = encoder.emit_seq(symbols.len(), |encoder| { + for (i, sym) in symbols.iter().enumerate() { + encoder.emit_seq_elt(i, |encoder| { + encoder.emit_str(&("_".to_string() + sym)) + })?; + } + Ok(()) + }); + if let Err(e) = res { + self.sess.fatal(&format!("failed to encode exported symbols: {}", e)); + } + } + debug!("{}", encoded); + arg.push(encoded); + + self.cmd.arg(arg); + } + + fn subsystem(&mut self, _subsystem: &str) { + // noop + } + + fn finalize(&mut self) -> Command { + let mut cmd = Command::new(""); + ::std::mem::swap(&mut cmd, &mut self.cmd); + cmd + } + + // Appears not necessary on Emscripten + fn group_start(&mut self) {} + fn group_end(&mut self) {} + + fn cross_lang_lto(&mut self) { + // Do nothing + } +} + +fn exported_symbols(tcx: TyCtxt, crate_type: CrateType) -> Vec { + let mut symbols = Vec::new(); + + let export_threshold = symbol_export::crates_export_threshold(&[crate_type]); + for &(symbol, level) in tcx.exported_symbols(LOCAL_CRATE).iter() { + if level.is_below_threshold(export_threshold) { + symbols.push(symbol.symbol_name(tcx).to_string()); + } + } + + let formats = tcx.sess.dependency_formats.borrow(); + let deps = formats[&crate_type].iter(); + + for (index, dep_format) in deps.enumerate() { + let cnum = CrateNum::new(index + 1); + // For each dependency that we are linking to statically ... + if *dep_format == Linkage::Static { + // ... we add its symbol list to our export list. + for &(symbol, level) in tcx.exported_symbols(cnum).iter() { + if level.is_below_threshold(export_threshold) { + symbols.push(symbol.symbol_name(tcx).to_string()); + } + } + } + } + + symbols +} + +pub struct WasmLd { + cmd: Command, +} + +impl Linker for WasmLd { + fn link_dylib(&mut self, lib: &str) { + self.cmd.arg("-l").arg(lib); + } + + fn link_staticlib(&mut self, lib: &str) { + self.cmd.arg("-l").arg(lib); + } + + fn link_rlib(&mut self, lib: &Path) { + self.cmd.arg(lib); + } + + fn include_path(&mut self, path: &Path) { + self.cmd.arg("-L").arg(path); + } + + fn framework_path(&mut self, _path: &Path) { + panic!("frameworks not supported") + } + + fn output_filename(&mut self, path: &Path) { + self.cmd.arg("-o").arg(path); + } + + fn add_object(&mut self, path: &Path) { + self.cmd.arg(path); + } + + fn position_independent_executable(&mut self) { + } + + fn full_relro(&mut self) { + } + + fn partial_relro(&mut self) { + } + + fn no_relro(&mut self) { + } + + fn build_static_executable(&mut self) { + } + + fn args(&mut self, args: &[String]) { + self.cmd.args(args); + } + + fn link_rust_dylib(&mut self, lib: &str, _path: &Path) { + self.cmd.arg("-l").arg(lib); + } + + fn link_framework(&mut self, _framework: &str) { + panic!("frameworks not supported") + } + + fn link_whole_staticlib(&mut self, lib: &str, _search_path: &[PathBuf]) { + self.cmd.arg("-l").arg(lib); + } + + fn link_whole_rlib(&mut self, lib: &Path) { + self.cmd.arg(lib); + } + + fn gc_sections(&mut self, _keep_metadata: bool) { + } + + fn optimize(&mut self) { + } + + fn pgo_gen(&mut self) { + } + + fn debuginfo(&mut self) { + } + + fn no_default_libraries(&mut self) { + } + + fn build_dylib(&mut self, _out_filename: &Path) { + } + + fn export_symbols(&mut self, _tmpdir: &Path, _crate_type: CrateType) { + } + + fn subsystem(&mut self, _subsystem: &str) { + } + + fn no_position_independent_executable(&mut self) { + } + + fn finalize(&mut self) -> Command { + // There have been reports in the wild (rustwasm/wasm-bindgen#119) of + // using threads causing weird hangs and bugs. Disable it entirely as + // this isn't yet the bottleneck of compilation at all anyway. + self.cmd.arg("--no-threads"); + + self.cmd.arg("-z").arg("stack-size=1048576"); + + // FIXME we probably shouldn't pass this but instead pass an explicit + // whitelist of symbols we'll allow to be undefined. Unfortunately + // though we can't handle symbols like `log10` that LLVM injects at a + // super late date without actually parsing object files. For now let's + // stick to this and hopefully fix it before stabilization happens. + self.cmd.arg("--allow-undefined"); + + // For now we just never have an entry symbol + self.cmd.arg("--no-entry"); + + let mut cmd = Command::new(""); + ::std::mem::swap(&mut cmd, &mut self.cmd); + cmd + } + + // Not needed for now with LLD + fn group_start(&mut self) {} + fn group_end(&mut self) {} + + fn cross_lang_lto(&mut self) { + // Do nothing for now + } +} diff --git a/src/librustc_codegen_llvm/back/lto.rs b/src/librustc_codegen_llvm/back/lto.rs new file mode 100644 index 00000000000..96eda50d788 --- /dev/null +++ b/src/librustc_codegen_llvm/back/lto.rs @@ -0,0 +1,773 @@ +// Copyright 2013 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use back::bytecode::{DecodedBytecode, RLIB_BYTECODE_EXTENSION}; +use back::symbol_export; +use back::write::{ModuleConfig, with_llvm_pmb, CodegenContext}; +use back::write; +use errors::{FatalError, Handler}; +use llvm::archive_ro::ArchiveRO; +use llvm::{ModuleRef, TargetMachineRef, True, False}; +use llvm; +use rustc::hir::def_id::LOCAL_CRATE; +use rustc::middle::exported_symbols::SymbolExportLevel; +use rustc::session::config::{self, Lto}; +use rustc::util::common::time_ext; +use time_graph::Timeline; +use {ModuleCodegen, ModuleLlvm, ModuleKind, ModuleSource}; + +use libc; + +use std::ffi::CString; +use std::ptr; +use std::slice; +use std::sync::Arc; + +pub fn crate_type_allows_lto(crate_type: config::CrateType) -> bool { + match crate_type { + config::CrateTypeExecutable | + config::CrateTypeStaticlib | + config::CrateTypeCdylib => true, + + config::CrateTypeDylib | + config::CrateTypeRlib | + config::CrateTypeProcMacro => false, + } +} + +pub(crate) enum LtoModuleCodegen { + Fat { + module: Option, + _serialized_bitcode: Vec, + }, + + Thin(ThinModule), +} + +impl LtoModuleCodegen { + pub fn name(&self) -> &str { + match *self { + LtoModuleCodegen::Fat { .. } => "everything", + LtoModuleCodegen::Thin(ref m) => m.name(), + } + } + + /// Optimize this module within the given codegen context. + /// + /// This function is unsafe as it'll return a `ModuleCodegen` still + /// points to LLVM data structures owned by this `LtoModuleCodegen`. + /// It's intended that the module returned is immediately code generated and + /// dropped, and then this LTO module is dropped. + pub(crate) unsafe fn optimize(&mut self, + cgcx: &CodegenContext, + timeline: &mut Timeline) + -> Result + { + match *self { + LtoModuleCodegen::Fat { ref mut module, .. } => { + let module = module.take().unwrap(); + let config = cgcx.config(module.kind); + let llmod = module.llvm().unwrap().llmod; + let tm = module.llvm().unwrap().tm; + run_pass_manager(cgcx, tm, llmod, config, false); + timeline.record("fat-done"); + Ok(module) + } + LtoModuleCodegen::Thin(ref mut thin) => thin.optimize(cgcx, timeline), + } + } + + /// A "gauge" of how costly it is to optimize this module, used to sort + /// biggest modules first. + pub fn cost(&self) -> u64 { + match *self { + // Only one module with fat LTO, so the cost doesn't matter. + LtoModuleCodegen::Fat { .. } => 0, + LtoModuleCodegen::Thin(ref m) => m.cost(), + } + } +} + +pub(crate) fn run(cgcx: &CodegenContext, + modules: Vec, + timeline: &mut Timeline) + -> Result, FatalError> +{ + let diag_handler = cgcx.create_diag_handler(); + let export_threshold = match cgcx.lto { + // We're just doing LTO for our one crate + Lto::ThinLocal => SymbolExportLevel::Rust, + + // We're doing LTO for the entire crate graph + Lto::Yes | Lto::Fat | Lto::Thin => { + symbol_export::crates_export_threshold(&cgcx.crate_types) + } + + Lto::No => panic!("didn't request LTO but we're doing LTO"), + }; + + let symbol_filter = &|&(ref name, level): &(String, SymbolExportLevel)| { + if level.is_below_threshold(export_threshold) { + let mut bytes = Vec::with_capacity(name.len() + 1); + bytes.extend(name.bytes()); + Some(CString::new(bytes).unwrap()) + } else { + None + } + }; + let exported_symbols = cgcx.exported_symbols + .as_ref().expect("needs exported symbols for LTO"); + let mut symbol_white_list = exported_symbols[&LOCAL_CRATE] + .iter() + .filter_map(symbol_filter) + .collect::>(); + timeline.record("whitelist"); + info!("{} symbols to preserve in this crate", symbol_white_list.len()); + + // If we're performing LTO for the entire crate graph, then for each of our + // upstream dependencies, find the corresponding rlib and load the bitcode + // from the archive. + // + // We save off all the bytecode and LLVM module ids for later processing + // with either fat or thin LTO + let mut upstream_modules = Vec::new(); + if cgcx.lto != Lto::ThinLocal { + if cgcx.opts.cg.prefer_dynamic { + diag_handler.struct_err("cannot prefer dynamic linking when performing LTO") + .note("only 'staticlib', 'bin', and 'cdylib' outputs are \ + supported with LTO") + .emit(); + return Err(FatalError) + } + + // Make sure we actually can run LTO + for crate_type in cgcx.crate_types.iter() { + if !crate_type_allows_lto(*crate_type) { + let e = diag_handler.fatal("lto can only be run for executables, cdylibs and \ + static library outputs"); + return Err(e) + } + } + + for &(cnum, ref path) in cgcx.each_linked_rlib_for_lto.iter() { + let exported_symbols = cgcx.exported_symbols + .as_ref().expect("needs exported symbols for LTO"); + symbol_white_list.extend( + exported_symbols[&cnum] + .iter() + .filter_map(symbol_filter)); + + let archive = ArchiveRO::open(&path).expect("wanted an rlib"); + let bytecodes = archive.iter().filter_map(|child| { + child.ok().and_then(|c| c.name().map(|name| (name, c))) + }).filter(|&(name, _)| name.ends_with(RLIB_BYTECODE_EXTENSION)); + for (name, data) in bytecodes { + info!("adding bytecode {}", name); + let bc_encoded = data.data(); + + let (bc, id) = time_ext(cgcx.time_passes, None, &format!("decode {}", name), || { + match DecodedBytecode::new(bc_encoded) { + Ok(b) => Ok((b.bytecode(), b.identifier().to_string())), + Err(e) => Err(diag_handler.fatal(&e)), + } + })?; + let bc = SerializedModule::FromRlib(bc); + upstream_modules.push((bc, CString::new(id).unwrap())); + } + timeline.record(&format!("load: {}", path.display())); + } + } + + let arr = symbol_white_list.iter().map(|c| c.as_ptr()).collect::>(); + match cgcx.lto { + Lto::Yes | // `-C lto` == fat LTO by default + Lto::Fat => { + fat_lto(cgcx, &diag_handler, modules, upstream_modules, &arr, timeline) + } + Lto::Thin | + Lto::ThinLocal => { + thin_lto(&diag_handler, modules, upstream_modules, &arr, timeline) + } + Lto::No => unreachable!(), + } +} + +fn fat_lto(cgcx: &CodegenContext, + diag_handler: &Handler, + mut modules: Vec, + mut serialized_modules: Vec<(SerializedModule, CString)>, + symbol_white_list: &[*const libc::c_char], + timeline: &mut Timeline) + -> Result, FatalError> +{ + info!("going for a fat lto"); + + // Find the "costliest" module and merge everything into that codegen unit. + // All the other modules will be serialized and reparsed into the new + // context, so this hopefully avoids serializing and parsing the largest + // codegen unit. + // + // Additionally use a regular module as the base here to ensure that various + // file copy operations in the backend work correctly. The only other kind + // of module here should be an allocator one, and if your crate is smaller + // than the allocator module then the size doesn't really matter anyway. + let (_, costliest_module) = modules.iter() + .enumerate() + .filter(|&(_, module)| module.kind == ModuleKind::Regular) + .map(|(i, module)| { + let cost = unsafe { + llvm::LLVMRustModuleCost(module.llvm().unwrap().llmod) + }; + (cost, i) + }) + .max() + .expect("must be codegen'ing at least one module"); + let module = modules.remove(costliest_module); + let llmod = module.llvm().expect("can't lto pre-codegened modules").llmod; + info!("using {:?} as a base module", module.llmod_id); + + // For all other modules we codegened we'll need to link them into our own + // bitcode. All modules were codegened in their own LLVM context, however, + // and we want to move everything to the same LLVM context. Currently the + // way we know of to do that is to serialize them to a string and them parse + // them later. Not great but hey, that's why it's "fat" LTO, right? + for module in modules { + let llvm = module.llvm().expect("can't lto pre-codegened modules"); + let buffer = ModuleBuffer::new(llvm.llmod); + let llmod_id = CString::new(&module.llmod_id[..]).unwrap(); + serialized_modules.push((SerializedModule::Local(buffer), llmod_id)); + } + + // For all serialized bitcode files we parse them and link them in as we did + // above, this is all mostly handled in C++. Like above, though, we don't + // know much about the memory management here so we err on the side of being + // save and persist everything with the original module. + let mut serialized_bitcode = Vec::new(); + let mut linker = Linker::new(llmod); + for (bc_decoded, name) in serialized_modules { + info!("linking {:?}", name); + time_ext(cgcx.time_passes, None, &format!("ll link {:?}", name), || { + let data = bc_decoded.data(); + linker.add(&data).map_err(|()| { + let msg = format!("failed to load bc of {:?}", name); + write::llvm_err(&diag_handler, msg) + }) + })?; + timeline.record(&format!("link {:?}", name)); + serialized_bitcode.push(bc_decoded); + } + drop(linker); + cgcx.save_temp_bitcode(&module, "lto.input"); + + // Internalize everything that *isn't* in our whitelist to help strip out + // more modules and such + unsafe { + let ptr = symbol_white_list.as_ptr(); + llvm::LLVMRustRunRestrictionPass(llmod, + ptr as *const *const libc::c_char, + symbol_white_list.len() as libc::size_t); + cgcx.save_temp_bitcode(&module, "lto.after-restriction"); + } + + if cgcx.no_landing_pads { + unsafe { + llvm::LLVMRustMarkAllFunctionsNounwind(llmod); + } + cgcx.save_temp_bitcode(&module, "lto.after-nounwind"); + } + timeline.record("passes"); + + Ok(vec![LtoModuleCodegen::Fat { + module: Some(module), + _serialized_bitcode: serialized_bitcode, + }]) +} + +struct Linker(llvm::LinkerRef); + +impl Linker { + fn new(llmod: ModuleRef) -> Linker { + unsafe { Linker(llvm::LLVMRustLinkerNew(llmod)) } + } + + fn add(&mut self, bytecode: &[u8]) -> Result<(), ()> { + unsafe { + if llvm::LLVMRustLinkerAdd(self.0, + bytecode.as_ptr() as *const libc::c_char, + bytecode.len()) { + Ok(()) + } else { + Err(()) + } + } + } +} + +impl Drop for Linker { + fn drop(&mut self) { + unsafe { llvm::LLVMRustLinkerFree(self.0); } + } +} + +/// Prepare "thin" LTO to get run on these modules. +/// +/// The general structure of ThinLTO is quite different from the structure of +/// "fat" LTO above. With "fat" LTO all LLVM modules in question are merged into +/// one giant LLVM module, and then we run more optimization passes over this +/// big module after internalizing most symbols. Thin LTO, on the other hand, +/// avoid this large bottleneck through more targeted optimization. +/// +/// At a high level Thin LTO looks like: +/// +/// 1. Prepare a "summary" of each LLVM module in question which describes +/// the values inside, cost of the values, etc. +/// 2. Merge the summaries of all modules in question into one "index" +/// 3. Perform some global analysis on this index +/// 4. For each module, use the index and analysis calculated previously to +/// perform local transformations on the module, for example inlining +/// small functions from other modules. +/// 5. Run thin-specific optimization passes over each module, and then code +/// generate everything at the end. +/// +/// The summary for each module is intended to be quite cheap, and the global +/// index is relatively quite cheap to create as well. As a result, the goal of +/// ThinLTO is to reduce the bottleneck on LTO and enable LTO to be used in more +/// situations. For example one cheap optimization is that we can parallelize +/// all codegen modules, easily making use of all the cores on a machine. +/// +/// With all that in mind, the function here is designed at specifically just +/// calculating the *index* for ThinLTO. This index will then be shared amongst +/// all of the `LtoModuleCodegen` units returned below and destroyed once +/// they all go out of scope. +fn thin_lto(diag_handler: &Handler, + modules: Vec, + serialized_modules: Vec<(SerializedModule, CString)>, + symbol_white_list: &[*const libc::c_char], + timeline: &mut Timeline) + -> Result, FatalError> +{ + unsafe { + info!("going for that thin, thin LTO"); + + let mut thin_buffers = Vec::new(); + let mut module_names = Vec::new(); + let mut thin_modules = Vec::new(); + + // FIXME: right now, like with fat LTO, we serialize all in-memory + // modules before working with them and ThinLTO. We really + // shouldn't do this, however, and instead figure out how to + // extract a summary from an in-memory module and then merge that + // into the global index. It turns out that this loop is by far + // the most expensive portion of this small bit of global + // analysis! + for (i, module) in modules.iter().enumerate() { + info!("local module: {} - {}", i, module.llmod_id); + let llvm = module.llvm().expect("can't lto precodegened module"); + let name = CString::new(module.llmod_id.clone()).unwrap(); + let buffer = ThinBuffer::new(llvm.llmod); + thin_modules.push(llvm::ThinLTOModule { + identifier: name.as_ptr(), + data: buffer.data().as_ptr(), + len: buffer.data().len(), + }); + thin_buffers.push(buffer); + module_names.push(name); + timeline.record(&module.llmod_id); + } + + // FIXME: All upstream crates are deserialized internally in the + // function below to extract their summary and modules. Note that + // unlike the loop above we *must* decode and/or read something + // here as these are all just serialized files on disk. An + // improvement, however, to make here would be to store the + // module summary separately from the actual module itself. Right + // now this is store in one large bitcode file, and the entire + // file is deflate-compressed. We could try to bypass some of the + // decompression by storing the index uncompressed and only + // lazily decompressing the bytecode if necessary. + // + // Note that truly taking advantage of this optimization will + // likely be further down the road. We'd have to implement + // incremental ThinLTO first where we could actually avoid + // looking at upstream modules entirely sometimes (the contents, + // we must always unconditionally look at the index). + let mut serialized = Vec::new(); + for (module, name) in serialized_modules { + info!("foreign module {:?}", name); + thin_modules.push(llvm::ThinLTOModule { + identifier: name.as_ptr(), + data: module.data().as_ptr(), + len: module.data().len(), + }); + serialized.push(module); + module_names.push(name); + } + + // Delegate to the C++ bindings to create some data here. Once this is a + // tried-and-true interface we may wish to try to upstream some of this + // to LLVM itself, right now we reimplement a lot of what they do + // upstream... + let data = llvm::LLVMRustCreateThinLTOData( + thin_modules.as_ptr(), + thin_modules.len() as u32, + symbol_white_list.as_ptr(), + symbol_white_list.len() as u32, + ); + if data.is_null() { + let msg = format!("failed to prepare thin LTO context"); + return Err(write::llvm_err(&diag_handler, msg)) + } + let data = ThinData(data); + info!("thin LTO data created"); + timeline.record("data"); + + // Throw our data in an `Arc` as we'll be sharing it across threads. We + // also put all memory referenced by the C++ data (buffers, ids, etc) + // into the arc as well. After this we'll create a thin module + // codegen per module in this data. + let shared = Arc::new(ThinShared { + data, + thin_buffers, + serialized_modules: serialized, + module_names, + }); + Ok((0..shared.module_names.len()).map(|i| { + LtoModuleCodegen::Thin(ThinModule { + shared: shared.clone(), + idx: i, + }) + }).collect()) + } +} + +fn run_pass_manager(cgcx: &CodegenContext, + tm: TargetMachineRef, + llmod: ModuleRef, + config: &ModuleConfig, + thin: bool) { + // Now we have one massive module inside of llmod. Time to run the + // LTO-specific optimization passes that LLVM provides. + // + // This code is based off the code found in llvm's LTO code generator: + // tools/lto/LTOCodeGenerator.cpp + debug!("running the pass manager"); + unsafe { + let pm = llvm::LLVMCreatePassManager(); + llvm::LLVMRustAddAnalysisPasses(tm, pm, llmod); + let pass = llvm::LLVMRustFindAndCreatePass("verify\0".as_ptr() as *const _); + assert!(!pass.is_null()); + llvm::LLVMRustAddPass(pm, pass); + + // When optimizing for LTO we don't actually pass in `-O0`, but we force + // it to always happen at least with `-O1`. + // + // With ThinLTO we mess around a lot with symbol visibility in a way + // that will actually cause linking failures if we optimize at O0 which + // notable is lacking in dead code elimination. To ensure we at least + // get some optimizations and correctly link we forcibly switch to `-O1` + // to get dead code elimination. + // + // Note that in general this shouldn't matter too much as you typically + // only turn on ThinLTO when you're compiling with optimizations + // otherwise. + let opt_level = config.opt_level.unwrap_or(llvm::CodeGenOptLevel::None); + let opt_level = match opt_level { + llvm::CodeGenOptLevel::None => llvm::CodeGenOptLevel::Less, + level => level, + }; + with_llvm_pmb(llmod, config, opt_level, false, &mut |b| { + if thin { + if !llvm::LLVMRustPassManagerBuilderPopulateThinLTOPassManager(b, pm) { + panic!("this version of LLVM does not support ThinLTO"); + } + } else { + llvm::LLVMPassManagerBuilderPopulateLTOPassManager(b, pm, + /* Internalize = */ False, + /* RunInliner = */ True); + } + }); + + let pass = llvm::LLVMRustFindAndCreatePass("verify\0".as_ptr() as *const _); + assert!(!pass.is_null()); + llvm::LLVMRustAddPass(pm, pass); + + time_ext(cgcx.time_passes, None, "LTO passes", || + llvm::LLVMRunPassManager(pm, llmod)); + + llvm::LLVMDisposePassManager(pm); + } + debug!("lto done"); +} + +pub enum SerializedModule { + Local(ModuleBuffer), + FromRlib(Vec), +} + +impl SerializedModule { + fn data(&self) -> &[u8] { + match *self { + SerializedModule::Local(ref m) => m.data(), + SerializedModule::FromRlib(ref m) => m, + } + } +} + +pub struct ModuleBuffer(*mut llvm::ModuleBuffer); + +unsafe impl Send for ModuleBuffer {} +unsafe impl Sync for ModuleBuffer {} + +impl ModuleBuffer { + pub fn new(m: ModuleRef) -> ModuleBuffer { + ModuleBuffer(unsafe { + llvm::LLVMRustModuleBufferCreate(m) + }) + } + + pub fn data(&self) -> &[u8] { + unsafe { + let ptr = llvm::LLVMRustModuleBufferPtr(self.0); + let len = llvm::LLVMRustModuleBufferLen(self.0); + slice::from_raw_parts(ptr, len) + } + } +} + +impl Drop for ModuleBuffer { + fn drop(&mut self) { + unsafe { llvm::LLVMRustModuleBufferFree(self.0); } + } +} + +pub struct ThinModule { + shared: Arc, + idx: usize, +} + +struct ThinShared { + data: ThinData, + thin_buffers: Vec, + serialized_modules: Vec, + module_names: Vec, +} + +struct ThinData(*mut llvm::ThinLTOData); + +unsafe impl Send for ThinData {} +unsafe impl Sync for ThinData {} + +impl Drop for ThinData { + fn drop(&mut self) { + unsafe { + llvm::LLVMRustFreeThinLTOData(self.0); + } + } +} + +pub struct ThinBuffer(*mut llvm::ThinLTOBuffer); + +unsafe impl Send for ThinBuffer {} +unsafe impl Sync for ThinBuffer {} + +impl ThinBuffer { + pub fn new(m: ModuleRef) -> ThinBuffer { + unsafe { + let buffer = llvm::LLVMRustThinLTOBufferCreate(m); + ThinBuffer(buffer) + } + } + + pub fn data(&self) -> &[u8] { + unsafe { + let ptr = llvm::LLVMRustThinLTOBufferPtr(self.0) as *const _; + let len = llvm::LLVMRustThinLTOBufferLen(self.0); + slice::from_raw_parts(ptr, len) + } + } +} + +impl Drop for ThinBuffer { + fn drop(&mut self) { + unsafe { + llvm::LLVMRustThinLTOBufferFree(self.0); + } + } +} + +impl ThinModule { + fn name(&self) -> &str { + self.shared.module_names[self.idx].to_str().unwrap() + } + + fn cost(&self) -> u64 { + // Yes, that's correct, we're using the size of the bytecode as an + // indicator for how costly this codegen unit is. + self.data().len() as u64 + } + + fn data(&self) -> &[u8] { + let a = self.shared.thin_buffers.get(self.idx).map(|b| b.data()); + a.unwrap_or_else(|| { + let len = self.shared.thin_buffers.len(); + self.shared.serialized_modules[self.idx - len].data() + }) + } + + unsafe fn optimize(&mut self, cgcx: &CodegenContext, timeline: &mut Timeline) + -> Result + { + let diag_handler = cgcx.create_diag_handler(); + let tm = (cgcx.tm_factory)().map_err(|e| { + write::llvm_err(&diag_handler, e) + })?; + + // Right now the implementation we've got only works over serialized + // modules, so we create a fresh new LLVM context and parse the module + // into that context. One day, however, we may do this for upstream + // crates but for locally codegened modules we may be able to reuse + // that LLVM Context and Module. + let llcx = llvm::LLVMRustContextCreate(cgcx.fewer_names); + let llmod = llvm::LLVMRustParseBitcodeForThinLTO( + llcx, + self.data().as_ptr(), + self.data().len(), + self.shared.module_names[self.idx].as_ptr(), + ); + if llmod.is_null() { + let msg = format!("failed to parse bitcode for thin LTO module"); + return Err(write::llvm_err(&diag_handler, msg)); + } + let module = ModuleCodegen { + source: ModuleSource::Codegened(ModuleLlvm { + llmod, + llcx, + tm, + }), + llmod_id: self.name().to_string(), + name: self.name().to_string(), + kind: ModuleKind::Regular, + }; + cgcx.save_temp_bitcode(&module, "thin-lto-input"); + + // Before we do much else find the "main" `DICompileUnit` that we'll be + // using below. If we find more than one though then rustc has changed + // in a way we're not ready for, so generate an ICE by returning + // an error. + let mut cu1 = ptr::null_mut(); + let mut cu2 = ptr::null_mut(); + llvm::LLVMRustThinLTOGetDICompileUnit(llmod, &mut cu1, &mut cu2); + if !cu2.is_null() { + let msg = format!("multiple source DICompileUnits found"); + return Err(write::llvm_err(&diag_handler, msg)) + } + + // Like with "fat" LTO, get some better optimizations if landing pads + // are disabled by removing all landing pads. + if cgcx.no_landing_pads { + llvm::LLVMRustMarkAllFunctionsNounwind(llmod); + cgcx.save_temp_bitcode(&module, "thin-lto-after-nounwind"); + timeline.record("nounwind"); + } + + // Up next comes the per-module local analyses that we do for Thin LTO. + // Each of these functions is basically copied from the LLVM + // implementation and then tailored to suit this implementation. Ideally + // each of these would be supported by upstream LLVM but that's perhaps + // a patch for another day! + // + // You can find some more comments about these functions in the LLVM + // bindings we've got (currently `PassWrapper.cpp`) + if !llvm::LLVMRustPrepareThinLTORename(self.shared.data.0, llmod) { + let msg = format!("failed to prepare thin LTO module"); + return Err(write::llvm_err(&diag_handler, msg)) + } + cgcx.save_temp_bitcode(&module, "thin-lto-after-rename"); + timeline.record("rename"); + if !llvm::LLVMRustPrepareThinLTOResolveWeak(self.shared.data.0, llmod) { + let msg = format!("failed to prepare thin LTO module"); + return Err(write::llvm_err(&diag_handler, msg)) + } + cgcx.save_temp_bitcode(&module, "thin-lto-after-resolve"); + timeline.record("resolve"); + if !llvm::LLVMRustPrepareThinLTOInternalize(self.shared.data.0, llmod) { + let msg = format!("failed to prepare thin LTO module"); + return Err(write::llvm_err(&diag_handler, msg)) + } + cgcx.save_temp_bitcode(&module, "thin-lto-after-internalize"); + timeline.record("internalize"); + if !llvm::LLVMRustPrepareThinLTOImport(self.shared.data.0, llmod) { + let msg = format!("failed to prepare thin LTO module"); + return Err(write::llvm_err(&diag_handler, msg)) + } + cgcx.save_temp_bitcode(&module, "thin-lto-after-import"); + timeline.record("import"); + + // Ok now this is a bit unfortunate. This is also something you won't + // find upstream in LLVM's ThinLTO passes! This is a hack for now to + // work around bugs in LLVM. + // + // First discovered in #45511 it was found that as part of ThinLTO + // importing passes LLVM will import `DICompileUnit` metadata + // information across modules. This means that we'll be working with one + // LLVM module that has multiple `DICompileUnit` instances in it (a + // bunch of `llvm.dbg.cu` members). Unfortunately there's a number of + // bugs in LLVM's backend which generates invalid DWARF in a situation + // like this: + // + // https://bugs.llvm.org/show_bug.cgi?id=35212 + // https://bugs.llvm.org/show_bug.cgi?id=35562 + // + // While the first bug there is fixed the second ended up causing #46346 + // which was basically a resurgence of #45511 after LLVM's bug 35212 was + // fixed. + // + // This function below is a huge hack around this problem. The function + // below is defined in `PassWrapper.cpp` and will basically "merge" + // all `DICompileUnit` instances in a module. Basically it'll take all + // the objects, rewrite all pointers of `DISubprogram` to point to the + // first `DICompileUnit`, and then delete all the other units. + // + // This is probably mangling to the debug info slightly (but hopefully + // not too much) but for now at least gets LLVM to emit valid DWARF (or + // so it appears). Hopefully we can remove this once upstream bugs are + // fixed in LLVM. + llvm::LLVMRustThinLTOPatchDICompileUnit(llmod, cu1); + cgcx.save_temp_bitcode(&module, "thin-lto-after-patch"); + timeline.record("patch"); + + // Alright now that we've done everything related to the ThinLTO + // analysis it's time to run some optimizations! Here we use the same + // `run_pass_manager` as the "fat" LTO above except that we tell it to + // populate a thin-specific pass manager, which presumably LLVM treats a + // little differently. + info!("running thin lto passes over {}", module.name); + let config = cgcx.config(module.kind); + run_pass_manager(cgcx, tm, llmod, config, true); + cgcx.save_temp_bitcode(&module, "thin-lto-after-pm"); + timeline.record("thin-done"); + + // FIXME: this is a hack around a bug in LLVM right now. Discovered in + // #46910 it was found out that on 32-bit MSVC LLVM will hit a codegen + // error if there's an available_externally function in the LLVM module. + // Typically we don't actually use these functions but ThinLTO makes + // heavy use of them when inlining across modules. + // + // Tracked upstream at https://bugs.llvm.org/show_bug.cgi?id=35736 this + // function call (and its definition on the C++ side of things) + // shouldn't be necessary eventually and we can safetly delete these few + // lines. + llvm::LLVMRustThinLTORemoveAvailableExternally(llmod); + cgcx.save_temp_bitcode(&module, "thin-lto-after-rm-ae"); + timeline.record("no-ae"); + + Ok(module) + } +} diff --git a/src/librustc_codegen_llvm/back/rpath.rs b/src/librustc_codegen_llvm/back/rpath.rs new file mode 100644 index 00000000000..8e5e7d37648 --- /dev/null +++ b/src/librustc_codegen_llvm/back/rpath.rs @@ -0,0 +1,282 @@ +// Copyright 2012-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. + +use std::collections::HashSet; +use std::env; +use std::path::{Path, PathBuf}; +use std::fs; + +use rustc::hir::def_id::CrateNum; +use rustc::middle::cstore::LibSource; + +pub struct RPathConfig<'a> { + pub used_crates: &'a [(CrateNum, LibSource)], + pub out_filename: PathBuf, + pub is_like_osx: bool, + pub has_rpath: bool, + pub linker_is_gnu: bool, + pub get_install_prefix_lib_path: &'a mut FnMut() -> PathBuf, +} + +pub fn get_rpath_flags(config: &mut RPathConfig) -> Vec { + // No rpath on windows + if !config.has_rpath { + return Vec::new(); + } + + let mut flags = Vec::new(); + + debug!("preparing the RPATH!"); + + let libs = config.used_crates.clone(); + let libs = libs.iter().filter_map(|&(_, ref l)| l.option()).collect::>(); + let rpaths = get_rpaths(config, &libs); + flags.extend_from_slice(&rpaths_to_flags(&rpaths)); + + // Use DT_RUNPATH instead of DT_RPATH if available + if config.linker_is_gnu { + flags.push("-Wl,--enable-new-dtags".to_string()); + } + + flags +} + +fn rpaths_to_flags(rpaths: &[String]) -> Vec { + let mut ret = Vec::new(); + for rpath in rpaths { + if rpath.contains(',') { + ret.push("-Wl,-rpath".into()); + ret.push("-Xlinker".into()); + ret.push(rpath.clone()); + } else { + ret.push(format!("-Wl,-rpath,{}", &(*rpath))); + } + } + return ret; +} + +fn get_rpaths(config: &mut RPathConfig, libs: &[PathBuf]) -> Vec { + debug!("output: {:?}", config.out_filename.display()); + debug!("libs:"); + for libpath in libs { + debug!(" {:?}", libpath.display()); + } + + // Use relative paths to the libraries. Binaries can be moved + // as long as they maintain the relative relationship to the + // crates they depend on. + let rel_rpaths = get_rpaths_relative_to_output(config, libs); + + // And a final backup rpath to the global library location. + let fallback_rpaths = vec![get_install_prefix_rpath(config)]; + + fn log_rpaths(desc: &str, rpaths: &[String]) { + debug!("{} rpaths:", desc); + for rpath in rpaths { + debug!(" {}", *rpath); + } + } + + log_rpaths("relative", &rel_rpaths); + log_rpaths("fallback", &fallback_rpaths); + + let mut rpaths = rel_rpaths; + rpaths.extend_from_slice(&fallback_rpaths); + + // Remove duplicates + let rpaths = minimize_rpaths(&rpaths); + return rpaths; +} + +fn get_rpaths_relative_to_output(config: &mut RPathConfig, + libs: &[PathBuf]) -> Vec { + libs.iter().map(|a| get_rpath_relative_to_output(config, a)).collect() +} + +fn get_rpath_relative_to_output(config: &mut RPathConfig, lib: &Path) -> String { + // Mac doesn't appear to support $ORIGIN + let prefix = if config.is_like_osx { + "@loader_path" + } else { + "$ORIGIN" + }; + + let cwd = env::current_dir().unwrap(); + let mut lib = fs::canonicalize(&cwd.join(lib)).unwrap_or(cwd.join(lib)); + lib.pop(); + let mut output = cwd.join(&config.out_filename); + output.pop(); + let output = fs::canonicalize(&output).unwrap_or(output); + let relative = path_relative_from(&lib, &output) + .expect(&format!("couldn't create relative path from {:?} to {:?}", output, lib)); + // FIXME (#9639): This needs to handle non-utf8 paths + format!("{}/{}", prefix, + relative.to_str().expect("non-utf8 component in path")) +} + +// This routine is adapted from the *old* Path's `path_relative_from` +// function, which works differently from the new `relative_from` function. +// In particular, this handles the case on unix where both paths are +// absolute but with only the root as the common directory. +fn path_relative_from(path: &Path, base: &Path) -> Option { + use std::path::Component; + + if path.is_absolute() != base.is_absolute() { + if path.is_absolute() { + Some(PathBuf::from(path)) + } else { + None + } + } else { + let mut ita = path.components(); + let mut itb = base.components(); + let mut comps: Vec = vec![]; + loop { + match (ita.next(), itb.next()) { + (None, None) => break, + (Some(a), None) => { + comps.push(a); + comps.extend(ita.by_ref()); + break; + } + (None, _) => comps.push(Component::ParentDir), + (Some(a), Some(b)) if comps.is_empty() && a == b => (), + (Some(a), Some(b)) if b == Component::CurDir => comps.push(a), + (Some(_), Some(b)) if b == Component::ParentDir => return None, + (Some(a), Some(_)) => { + comps.push(Component::ParentDir); + for _ in itb { + comps.push(Component::ParentDir); + } + comps.push(a); + comps.extend(ita.by_ref()); + break; + } + } + } + Some(comps.iter().map(|c| c.as_os_str()).collect()) + } +} + + +fn get_install_prefix_rpath(config: &mut RPathConfig) -> String { + let path = (config.get_install_prefix_lib_path)(); + let path = env::current_dir().unwrap().join(&path); + // FIXME (#9639): This needs to handle non-utf8 paths + path.to_str().expect("non-utf8 component in rpath").to_string() +} + +fn minimize_rpaths(rpaths: &[String]) -> Vec { + let mut set = HashSet::new(); + let mut minimized = Vec::new(); + for rpath in rpaths { + if set.insert(rpath) { + minimized.push(rpath.clone()); + } + } + minimized +} + +#[cfg(all(unix, test))] +mod tests { + use super::{RPathConfig}; + use super::{minimize_rpaths, rpaths_to_flags, get_rpath_relative_to_output}; + use std::path::{Path, PathBuf}; + + #[test] + fn test_rpaths_to_flags() { + let flags = rpaths_to_flags(&[ + "path1".to_string(), + "path2".to_string() + ]); + assert_eq!(flags, + ["-Wl,-rpath,path1", + "-Wl,-rpath,path2"]); + } + + #[test] + fn test_minimize1() { + let res = minimize_rpaths(&[ + "rpath1".to_string(), + "rpath2".to_string(), + "rpath1".to_string() + ]); + assert!(res == [ + "rpath1", + "rpath2", + ]); + } + + #[test] + fn test_minimize2() { + let res = minimize_rpaths(&[ + "1a".to_string(), + "2".to_string(), + "2".to_string(), + "1a".to_string(), + "4a".to_string(), + "1a".to_string(), + "2".to_string(), + "3".to_string(), + "4a".to_string(), + "3".to_string() + ]); + assert!(res == [ + "1a", + "2", + "4a", + "3", + ]); + } + + #[test] + fn test_rpath_relative() { + if cfg!(target_os = "macos") { + let config = &mut RPathConfig { + used_crates: Vec::new(), + has_rpath: true, + is_like_osx: true, + linker_is_gnu: false, + out_filename: PathBuf::from("bin/rustc"), + get_install_prefix_lib_path: &mut || panic!(), + }; + let res = get_rpath_relative_to_output(config, + Path::new("lib/libstd.so")); + assert_eq!(res, "@loader_path/../lib"); + } else { + let config = &mut RPathConfig { + used_crates: Vec::new(), + out_filename: PathBuf::from("bin/rustc"), + get_install_prefix_lib_path: &mut || panic!(), + has_rpath: true, + is_like_osx: false, + linker_is_gnu: true, + }; + let res = get_rpath_relative_to_output(config, + Path::new("lib/libstd.so")); + assert_eq!(res, "$ORIGIN/../lib"); + } + } + + #[test] + fn test_xlinker() { + let args = rpaths_to_flags(&[ + "a/normal/path".to_string(), + "a,comma,path".to_string() + ]); + + assert_eq!(args, vec![ + "-Wl,-rpath,a/normal/path".to_string(), + "-Wl,-rpath".to_string(), + "-Xlinker".to_string(), + "a,comma,path".to_string() + ]); + } +} diff --git a/src/librustc_codegen_llvm/back/symbol_export.rs b/src/librustc_codegen_llvm/back/symbol_export.rs new file mode 100644 index 00000000000..81ac684aee2 --- /dev/null +++ b/src/librustc_codegen_llvm/back/symbol_export.rs @@ -0,0 +1,396 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use rustc_data_structures::sync::Lrc; +use std::sync::Arc; + +use monomorphize::Instance; +use rustc::hir; +use rustc::hir::CodegenFnAttrFlags; +use rustc::hir::def_id::{CrateNum, DefId, LOCAL_CRATE, CRATE_DEF_INDEX}; +use rustc::ich::Fingerprint; +use rustc::middle::exported_symbols::{SymbolExportLevel, ExportedSymbol, metadata_symbol_name}; +use rustc::session::config; +use rustc::ty::{TyCtxt, SymbolName}; +use rustc::ty::maps::Providers; +use rustc::ty::subst::Substs; +use rustc::util::nodemap::{FxHashMap, DefIdMap}; +use rustc_allocator::ALLOCATOR_METHODS; +use rustc_data_structures::indexed_vec::IndexVec; +use std::collections::hash_map::Entry::*; + +pub type ExportedSymbols = FxHashMap< + CrateNum, + Arc>, +>; + +pub fn threshold(tcx: TyCtxt) -> SymbolExportLevel { + crates_export_threshold(&tcx.sess.crate_types.borrow()) +} + +fn crate_export_threshold(crate_type: config::CrateType) -> SymbolExportLevel { + match crate_type { + config::CrateTypeExecutable | + config::CrateTypeStaticlib | + config::CrateTypeProcMacro | + config::CrateTypeCdylib => SymbolExportLevel::C, + config::CrateTypeRlib | + config::CrateTypeDylib => SymbolExportLevel::Rust, + } +} + +pub fn crates_export_threshold(crate_types: &[config::CrateType]) + -> SymbolExportLevel { + if crate_types.iter().any(|&crate_type| { + crate_export_threshold(crate_type) == SymbolExportLevel::Rust + }) { + SymbolExportLevel::Rust + } else { + SymbolExportLevel::C + } +} + +fn reachable_non_generics_provider<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, + cnum: CrateNum) + -> Lrc> +{ + assert_eq!(cnum, LOCAL_CRATE); + + if !tcx.sess.opts.output_types.should_codegen() { + return Lrc::new(DefIdMap()) + } + + // Check to see if this crate is a "special runtime crate". These + // crates, implementation details of the standard library, typically + // have a bunch of `pub extern` and `#[no_mangle]` functions as the + // ABI between them. We don't want their symbols to have a `C` + // export level, however, as they're just implementation details. + // Down below we'll hardwire all of the symbols to the `Rust` export + // level instead. + let special_runtime_crate = tcx.is_panic_runtime(LOCAL_CRATE) || + tcx.is_compiler_builtins(LOCAL_CRATE); + + let mut reachable_non_generics: DefIdMap<_> = tcx.reachable_set(LOCAL_CRATE).0 + .iter() + .filter_map(|&node_id| { + // We want to ignore some FFI functions that are not exposed from + // this crate. Reachable FFI functions can be lumped into two + // categories: + // + // 1. Those that are included statically via a static library + // 2. Those included otherwise (e.g. dynamically or via a framework) + // + // Although our LLVM module is not literally emitting code for the + // statically included symbols, it's an export of our library which + // needs to be passed on to the linker and encoded in the metadata. + // + // As a result, if this id is an FFI item (foreign item) then we only + // let it through if it's included statically. + match tcx.hir.get(node_id) { + hir::map::NodeForeignItem(..) => { + let def_id = tcx.hir.local_def_id(node_id); + if tcx.is_statically_included_foreign_item(def_id) { + Some(def_id) + } else { + None + } + } + + // Only consider nodes that actually have exported symbols. + hir::map::NodeItem(&hir::Item { + node: hir::ItemStatic(..), + .. + }) | + hir::map::NodeItem(&hir::Item { + node: hir::ItemFn(..), .. + }) | + hir::map::NodeImplItem(&hir::ImplItem { + node: hir::ImplItemKind::Method(..), + .. + }) => { + let def_id = tcx.hir.local_def_id(node_id); + let generics = tcx.generics_of(def_id); + if !generics.requires_monomorphization(tcx) && + // Functions marked with #[inline] are only ever codegened + // with "internal" linkage and are never exported. + !Instance::mono(tcx, def_id).def.requires_local(tcx) { + Some(def_id) + } else { + None + } + } + + _ => None + } + }) + .map(|def_id| { + let export_level = if special_runtime_crate { + let name = tcx.symbol_name(Instance::mono(tcx, def_id)).as_str(); + // We can probably do better here by just ensuring that + // it has hidden visibility rather than public + // visibility, as this is primarily here to ensure it's + // not stripped during LTO. + // + // In general though we won't link right if these + // symbols are stripped, and LTO currently strips them. + if &*name == "rust_eh_personality" || + &*name == "rust_eh_register_frames" || + &*name == "rust_eh_unregister_frames" { + SymbolExportLevel::C + } else { + SymbolExportLevel::Rust + } + } else { + symbol_export_level(tcx, def_id) + }; + debug!("EXPORTED SYMBOL (local): {} ({:?})", + tcx.symbol_name(Instance::mono(tcx, def_id)), + export_level); + (def_id, export_level) + }) + .collect(); + + if let Some(id) = *tcx.sess.derive_registrar_fn.get() { + let def_id = tcx.hir.local_def_id(id); + reachable_non_generics.insert(def_id, SymbolExportLevel::C); + } + + if let Some(id) = *tcx.sess.plugin_registrar_fn.get() { + let def_id = tcx.hir.local_def_id(id); + reachable_non_generics.insert(def_id, SymbolExportLevel::C); + } + + Lrc::new(reachable_non_generics) +} + +fn is_reachable_non_generic_provider_local<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, + def_id: DefId) + -> bool { + let export_threshold = threshold(tcx); + + if let Some(&level) = tcx.reachable_non_generics(def_id.krate).get(&def_id) { + level.is_below_threshold(export_threshold) + } else { + false + } +} + +fn is_reachable_non_generic_provider_extern<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, + def_id: DefId) + -> bool { + tcx.reachable_non_generics(def_id.krate).contains_key(&def_id) +} + +fn exported_symbols_provider_local<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, + cnum: CrateNum) + -> Arc, + SymbolExportLevel)>> +{ + assert_eq!(cnum, LOCAL_CRATE); + + if !tcx.sess.opts.output_types.should_codegen() { + return Arc::new(vec![]) + } + + let mut symbols: Vec<_> = tcx.reachable_non_generics(LOCAL_CRATE) + .iter() + .map(|(&def_id, &level)| { + (ExportedSymbol::NonGeneric(def_id), level) + }) + .collect(); + + if let Some(_) = *tcx.sess.entry_fn.borrow() { + let symbol_name = "main".to_string(); + let exported_symbol = ExportedSymbol::NoDefId(SymbolName::new(&symbol_name)); + + symbols.push((exported_symbol, SymbolExportLevel::C)); + } + + if tcx.sess.allocator_kind.get().is_some() { + for method in ALLOCATOR_METHODS { + let symbol_name = format!("__rust_{}", method.name); + let exported_symbol = ExportedSymbol::NoDefId(SymbolName::new(&symbol_name)); + + symbols.push((exported_symbol, SymbolExportLevel::Rust)); + } + } + + if tcx.sess.opts.debugging_opts.pgo_gen.is_some() { + // These are weak symbols that point to the profile version and the + // profile name, which need to be treated as exported so LTO doesn't nix + // them. + const PROFILER_WEAK_SYMBOLS: [&'static str; 2] = [ + "__llvm_profile_raw_version", + "__llvm_profile_filename", + ]; + for sym in &PROFILER_WEAK_SYMBOLS { + let exported_symbol = ExportedSymbol::NoDefId(SymbolName::new(sym)); + symbols.push((exported_symbol, SymbolExportLevel::C)); + } + } + + if tcx.sess.crate_types.borrow().contains(&config::CrateTypeDylib) { + let symbol_name = metadata_symbol_name(tcx); + let exported_symbol = ExportedSymbol::NoDefId(SymbolName::new(&symbol_name)); + + symbols.push((exported_symbol, SymbolExportLevel::Rust)); + } + + if tcx.share_generics() && tcx.local_crate_exports_generics() { + use rustc::mir::mono::{Linkage, Visibility, MonoItem}; + use rustc::ty::InstanceDef; + + // Normally, we require that shared monomorphizations are not hidden, + // because if we want to re-use a monomorphization from a Rust dylib, it + // needs to be exported. + // However, on platforms that don't allow for Rust dylibs, having + // external linkage is enough for monomorphization to be linked to. + let need_visibility = tcx.sess.target.target.options.dynamic_linking && + !tcx.sess.target.target.options.only_cdylib; + + let (_, cgus) = tcx.collect_and_partition_mono_items(LOCAL_CRATE); + + for (mono_item, &(linkage, visibility)) in cgus.iter() + .flat_map(|cgu| cgu.items().iter()) { + if linkage != Linkage::External { + // We can only re-use things with external linkage, otherwise + // we'll get a linker error + continue + } + + if need_visibility && visibility == Visibility::Hidden { + // If we potentially share things from Rust dylibs, they must + // not be hidden + continue + } + + if let &MonoItem::Fn(Instance { + def: InstanceDef::Item(def_id), + substs, + }) = mono_item { + if substs.types().next().is_some() { + symbols.push((ExportedSymbol::Generic(def_id, substs), + SymbolExportLevel::Rust)); + } + } + } + } + + // Sort so we get a stable incr. comp. hash. + symbols.sort_unstable_by(|&(ref symbol1, ..), &(ref symbol2, ..)| { + symbol1.compare_stable(tcx, symbol2) + }); + + Arc::new(symbols) +} + +fn upstream_monomorphizations_provider<'a, 'tcx>( + tcx: TyCtxt<'a, 'tcx, 'tcx>, + cnum: CrateNum) + -> Lrc, CrateNum>>>> +{ + debug_assert!(cnum == LOCAL_CRATE); + + let cnums = tcx.all_crate_nums(LOCAL_CRATE); + + let mut instances = DefIdMap(); + + let cnum_stable_ids: IndexVec = { + let mut cnum_stable_ids = IndexVec::from_elem_n(Fingerprint::ZERO, + cnums.len() + 1); + + for &cnum in cnums.iter() { + cnum_stable_ids[cnum] = tcx.def_path_hash(DefId { + krate: cnum, + index: CRATE_DEF_INDEX, + }).0; + } + + cnum_stable_ids + }; + + for &cnum in cnums.iter() { + for &(ref exported_symbol, _) in tcx.exported_symbols(cnum).iter() { + if let &ExportedSymbol::Generic(def_id, substs) = exported_symbol { + let substs_map = instances.entry(def_id) + .or_insert_with(|| FxHashMap()); + + match substs_map.entry(substs) { + Occupied(mut e) => { + // If there are multiple monomorphizations available, + // we select one deterministically. + let other_cnum = *e.get(); + if cnum_stable_ids[other_cnum] > cnum_stable_ids[cnum] { + e.insert(cnum); + } + } + Vacant(e) => { + e.insert(cnum); + } + } + } + } + } + + Lrc::new(instances.into_iter() + .map(|(key, value)| (key, Lrc::new(value))) + .collect()) +} + +fn upstream_monomorphizations_for_provider<'a, 'tcx>( + tcx: TyCtxt<'a, 'tcx, 'tcx>, + def_id: DefId) + -> Option, CrateNum>>> +{ + debug_assert!(!def_id.is_local()); + tcx.upstream_monomorphizations(LOCAL_CRATE) + .get(&def_id) + .cloned() +} + +fn is_unreachable_local_definition_provider(tcx: TyCtxt, def_id: DefId) -> bool { + if let Some(node_id) = tcx.hir.as_local_node_id(def_id) { + !tcx.reachable_set(LOCAL_CRATE).0.contains(&node_id) + } else { + bug!("is_unreachable_local_definition called with non-local DefId: {:?}", + def_id) + } +} + +pub fn provide(providers: &mut Providers) { + providers.reachable_non_generics = reachable_non_generics_provider; + providers.is_reachable_non_generic = is_reachable_non_generic_provider_local; + providers.exported_symbols = exported_symbols_provider_local; + providers.upstream_monomorphizations = upstream_monomorphizations_provider; + providers.is_unreachable_local_definition = is_unreachable_local_definition_provider; +} + +pub fn provide_extern(providers: &mut Providers) { + providers.is_reachable_non_generic = is_reachable_non_generic_provider_extern; + providers.upstream_monomorphizations_for = upstream_monomorphizations_for_provider; +} + +fn symbol_export_level(tcx: TyCtxt, sym_def_id: DefId) -> SymbolExportLevel { + // We export anything that's not mangled at the "C" layer as it probably has + // to do with ABI concerns. We do not, however, apply such treatment to + // special symbols in the standard library for various plumbing between + // core/std/allocators/etc. For example symbols used to hook up allocation + // are not considered for export + let codegen_fn_attrs = tcx.codegen_fn_attrs(sym_def_id); + let is_extern = codegen_fn_attrs.contains_extern_indicator(); + let std_internal = + codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL); + + if is_extern && !std_internal { + SymbolExportLevel::C + } else { + SymbolExportLevel::Rust + } +} diff --git a/src/librustc_codegen_llvm/back/wasm.rs b/src/librustc_codegen_llvm/back/wasm.rs new file mode 100644 index 00000000000..d6d386c9fbe --- /dev/null +++ b/src/librustc_codegen_llvm/back/wasm.rs @@ -0,0 +1,261 @@ +// Copyright 2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use std::collections::BTreeMap; +use std::fs; +use std::path::Path; +use std::str; + +use rustc_data_structures::fx::FxHashMap; +use serialize::leb128; + +// https://webassembly.github.io/spec/core/binary/modules.html#binary-importsec +const WASM_IMPORT_SECTION_ID: u8 = 2; + +const WASM_EXTERNAL_KIND_FUNCTION: u8 = 0; +const WASM_EXTERNAL_KIND_TABLE: u8 = 1; +const WASM_EXTERNAL_KIND_MEMORY: u8 = 2; +const WASM_EXTERNAL_KIND_GLOBAL: u8 = 3; + +/// Append all the custom sections listed in `sections` to the wasm binary +/// specified at `path`. +/// +/// LLVM 6 which we're using right now doesn't have the ability to create custom +/// sections in wasm files nor does LLD have the ability to merge these sections +/// into one larger section when linking. It's expected that this will +/// eventually get implemented, however! +/// +/// Until that time though this is a custom implementation in rustc to append +/// all sections to a wasm file to the finished product that LLD produces. +/// +/// Support for this is landing in LLVM in https://reviews.llvm.org/D43097, +/// although after that support will need to be in LLD as well. +pub fn add_custom_sections(path: &Path, sections: &BTreeMap>) { + if sections.len() == 0 { + return + } + + let wasm = fs::read(path).expect("failed to read wasm output"); + + // see https://webassembly.github.io/spec/core/binary/modules.html#custom-section + let mut wasm = WasmEncoder { data: wasm }; + for (section, bytes) in sections { + // write the `id` identifier, 0 for a custom section + wasm.byte(0); + + // figure out how long our name descriptor will be + let mut name = WasmEncoder::new(); + name.str(section); + + // write the length of the payload followed by all its contents + wasm.u32((bytes.len() + name.data.len()) as u32); + wasm.data.extend_from_slice(&name.data); + wasm.data.extend_from_slice(bytes); + } + + fs::write(path, &wasm.data).expect("failed to write wasm output"); +} + +/// Rewrite the module imports are listed from in a wasm module given the field +/// name to module name mapping in `import_map`. +/// +/// LLVM 6 which we're using right now doesn't have the ability to configure the +/// module a wasm symbol is import from. Rather all imported symbols come from +/// the bland `"env"` module unconditionally. Furthermore we'd *also* need +/// support in LLD for preserving these import modules, which it unfortunately +/// currently does not. +/// +/// This function is intended as a hack for now where we manually rewrite the +/// wasm output by LLVM to have the correct import modules listed. The +/// `#[wasm_import_module]` attribute in Rust translates to the module that each +/// symbol is imported from, so here we manually go through the wasm file, +/// decode it, rewrite imports, and then rewrite the wasm module. +/// +/// Support for this was added to LLVM in +/// https://github.com/llvm-mirror/llvm/commit/0f32e1365, although support still +/// needs to be added (AFAIK at the time of this writing) to LLD +pub fn rewrite_imports(path: &Path, import_map: &FxHashMap) { + if import_map.len() == 0 { + return + } + + let wasm = fs::read(path).expect("failed to read wasm output"); + let mut ret = WasmEncoder::new(); + ret.data.extend(&wasm[..8]); + + // skip the 8 byte wasm/version header + for (id, raw) in WasmSections(WasmDecoder::new(&wasm[8..])) { + ret.byte(id); + if id == WASM_IMPORT_SECTION_ID { + info!("rewriting import section"); + let data = rewrite_import_section( + &mut WasmDecoder::new(raw), + import_map, + ); + ret.bytes(&data); + } else { + info!("carry forward section {}, {} bytes long", id, raw.len()); + ret.bytes(raw); + } + } + + fs::write(path, &ret.data).expect("failed to write wasm output"); + + fn rewrite_import_section( + wasm: &mut WasmDecoder, + import_map: &FxHashMap, + ) + -> Vec + { + let mut dst = WasmEncoder::new(); + let n = wasm.u32(); + dst.u32(n); + info!("rewriting {} imports", n); + for _ in 0..n { + rewrite_import_entry(wasm, &mut dst, import_map); + } + return dst.data + } + + fn rewrite_import_entry(wasm: &mut WasmDecoder, + dst: &mut WasmEncoder, + import_map: &FxHashMap) { + // More info about the binary format here is available at: + // https://webassembly.github.io/spec/core/binary/modules.html#import-section + // + // Note that you can also find the whole point of existence of this + // function here, where we map the `module` name to a different one if + // we've got one listed. + let module = wasm.str(); + let field = wasm.str(); + let new_module = if module == "env" { + import_map.get(field).map(|s| &**s).unwrap_or(module) + } else { + module + }; + info!("import rewrite ({} => {}) / {}", module, new_module, field); + dst.str(new_module); + dst.str(field); + let kind = wasm.byte(); + dst.byte(kind); + match kind { + WASM_EXTERNAL_KIND_FUNCTION => dst.u32(wasm.u32()), + WASM_EXTERNAL_KIND_TABLE => { + dst.byte(wasm.byte()); // element_type + dst.limits(wasm.limits()); + } + WASM_EXTERNAL_KIND_MEMORY => dst.limits(wasm.limits()), + WASM_EXTERNAL_KIND_GLOBAL => { + dst.byte(wasm.byte()); // content_type + dst.bool(wasm.bool()); // mutable + } + b => panic!("unknown kind: {}", b), + } + } +} + +struct WasmSections<'a>(WasmDecoder<'a>); + +impl<'a> Iterator for WasmSections<'a> { + type Item = (u8, &'a [u8]); + + fn next(&mut self) -> Option<(u8, &'a [u8])> { + if self.0.data.len() == 0 { + return None + } + + // see https://webassembly.github.io/spec/core/binary/modules.html#sections + let id = self.0.byte(); + let section_len = self.0.u32(); + info!("new section {} / {} bytes", id, section_len); + let section = self.0.skip(section_len as usize); + Some((id, section)) + } +} + +struct WasmDecoder<'a> { + data: &'a [u8], +} + +impl<'a> WasmDecoder<'a> { + fn new(data: &'a [u8]) -> WasmDecoder<'a> { + WasmDecoder { data } + } + + fn byte(&mut self) -> u8 { + self.skip(1)[0] + } + + fn u32(&mut self) -> u32 { + let (n, l1) = leb128::read_u32_leb128(self.data); + self.data = &self.data[l1..]; + return n + } + + fn skip(&mut self, amt: usize) -> &'a [u8] { + let (data, rest) = self.data.split_at(amt); + self.data = rest; + data + } + + fn str(&mut self) -> &'a str { + let len = self.u32(); + str::from_utf8(self.skip(len as usize)).unwrap() + } + + fn bool(&mut self) -> bool { + self.byte() == 1 + } + + fn limits(&mut self) -> (u32, Option) { + let has_max = self.bool(); + (self.u32(), if has_max { Some(self.u32()) } else { None }) + } +} + +struct WasmEncoder { + data: Vec, +} + +impl WasmEncoder { + fn new() -> WasmEncoder { + WasmEncoder { data: Vec::new() } + } + + fn u32(&mut self, val: u32) { + let at = self.data.len(); + leb128::write_u32_leb128(&mut self.data, at, val); + } + + fn byte(&mut self, val: u8) { + self.data.push(val); + } + + fn bytes(&mut self, val: &[u8]) { + self.u32(val.len() as u32); + self.data.extend_from_slice(val); + } + + fn str(&mut self, val: &str) { + self.bytes(val.as_bytes()) + } + + fn bool(&mut self, b: bool) { + self.byte(b as u8); + } + + fn limits(&mut self, limits: (u32, Option)) { + self.bool(limits.1.is_some()); + self.u32(limits.0); + if let Some(c) = limits.1 { + self.u32(c); + } + } +} diff --git a/src/librustc_codegen_llvm/back/write.rs b/src/librustc_codegen_llvm/back/write.rs new file mode 100644 index 00000000000..1151e013131 --- /dev/null +++ b/src/librustc_codegen_llvm/back/write.rs @@ -0,0 +1,2390 @@ +// Copyright 2013-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. + +use attributes; +use back::bytecode::{self, RLIB_BYTECODE_EXTENSION}; +use back::lto::{self, ModuleBuffer, ThinBuffer}; +use back::link::{self, get_linker, remove}; +use back::command::Command; +use back::linker::LinkerInfo; +use back::symbol_export::ExportedSymbols; +use base; +use consts; +use rustc_incremental::{copy_cgu_workproducts_to_incr_comp_cache_dir, in_incr_comp_dir}; +use rustc::dep_graph::{WorkProduct, WorkProductId, WorkProductFileKind}; +use rustc::middle::cstore::{LinkMeta, EncodedMetadata}; +use rustc::session::config::{self, OutputFilenames, OutputType, Passes, SomePasses, + AllPasses, Sanitizer, Lto}; +use rustc::session::Session; +use rustc::util::nodemap::FxHashMap; +use time_graph::{self, TimeGraph, Timeline}; +use llvm; +use llvm::{ModuleRef, TargetMachineRef, PassManagerRef, DiagnosticInfoRef}; +use llvm::{SMDiagnosticRef, ContextRef}; +use {CodegenResults, ModuleSource, ModuleCodegen, CompiledModule, ModuleKind}; +use CrateInfo; +use rustc::hir::def_id::{CrateNum, LOCAL_CRATE}; +use rustc::ty::TyCtxt; +use rustc::util::common::{time_ext, time_depth, set_time_depth, print_time_passes_entry}; +use rustc::util::common::path2cstr; +use rustc::util::fs::{link_or_copy}; +use errors::{self, Handler, Level, DiagnosticBuilder, FatalError, DiagnosticId}; +use errors::emitter::{Emitter}; +use syntax::attr; +use syntax::ext::hygiene::Mark; +use syntax_pos::MultiSpan; +use syntax_pos::symbol::Symbol; +use type_::Type; +use context::{is_pie_binary, get_reloc_model}; +use common::{C_bytes_in_context, val_ty}; +use jobserver::{Client, Acquired}; +use rustc_demangle; + +use std::any::Any; +use std::ffi::{CString, CStr}; +use std::fs; +use std::io::{self, Write}; +use std::mem; +use std::path::{Path, PathBuf}; +use std::str; +use std::sync::Arc; +use std::sync::mpsc::{channel, Sender, Receiver}; +use std::slice; +use std::time::Instant; +use std::thread; +use libc::{c_uint, c_void, c_char, size_t}; + +pub const RELOC_MODEL_ARGS : [(&'static str, llvm::RelocMode); 7] = [ + ("pic", llvm::RelocMode::PIC), + ("static", llvm::RelocMode::Static), + ("default", llvm::RelocMode::Default), + ("dynamic-no-pic", llvm::RelocMode::DynamicNoPic), + ("ropi", llvm::RelocMode::ROPI), + ("rwpi", llvm::RelocMode::RWPI), + ("ropi-rwpi", llvm::RelocMode::ROPI_RWPI), +]; + +pub const CODE_GEN_MODEL_ARGS: &[(&str, llvm::CodeModel)] = &[ + ("small", llvm::CodeModel::Small), + ("kernel", llvm::CodeModel::Kernel), + ("medium", llvm::CodeModel::Medium), + ("large", llvm::CodeModel::Large), +]; + +pub const TLS_MODEL_ARGS : [(&'static str, llvm::ThreadLocalMode); 4] = [ + ("global-dynamic", llvm::ThreadLocalMode::GeneralDynamic), + ("local-dynamic", llvm::ThreadLocalMode::LocalDynamic), + ("initial-exec", llvm::ThreadLocalMode::InitialExec), + ("local-exec", llvm::ThreadLocalMode::LocalExec), +]; + +pub fn llvm_err(handler: &errors::Handler, msg: String) -> FatalError { + match llvm::last_error() { + Some(err) => handler.fatal(&format!("{}: {}", msg, err)), + None => handler.fatal(&msg), + } +} + +pub fn write_output_file( + handler: &errors::Handler, + target: llvm::TargetMachineRef, + pm: llvm::PassManagerRef, + m: ModuleRef, + output: &Path, + file_type: llvm::FileType) -> Result<(), FatalError> { + unsafe { + let output_c = path2cstr(output); + let result = llvm::LLVMRustWriteOutputFile( + target, pm, m, output_c.as_ptr(), file_type); + if result.into_result().is_err() { + let msg = format!("could not write output to {}", output.display()); + Err(llvm_err(handler, msg)) + } else { + Ok(()) + } + } +} + +fn get_llvm_opt_level(optimize: config::OptLevel) -> llvm::CodeGenOptLevel { + match optimize { + config::OptLevel::No => llvm::CodeGenOptLevel::None, + config::OptLevel::Less => llvm::CodeGenOptLevel::Less, + config::OptLevel::Default => llvm::CodeGenOptLevel::Default, + config::OptLevel::Aggressive => llvm::CodeGenOptLevel::Aggressive, + _ => llvm::CodeGenOptLevel::Default, + } +} + +fn get_llvm_opt_size(optimize: config::OptLevel) -> llvm::CodeGenOptSize { + match optimize { + config::OptLevel::Size => llvm::CodeGenOptSizeDefault, + config::OptLevel::SizeMin => llvm::CodeGenOptSizeAggressive, + _ => llvm::CodeGenOptSizeNone, + } +} + +pub fn create_target_machine(sess: &Session, find_features: bool) -> TargetMachineRef { + target_machine_factory(sess, find_features)().unwrap_or_else(|err| { + llvm_err(sess.diagnostic(), err).raise() + }) +} + +// If find_features is true this won't access `sess.crate_types` by assuming +// that `is_pie_binary` is false. When we discover LLVM target features +// `sess.crate_types` is uninitialized so we cannot access it. +pub fn target_machine_factory(sess: &Session, find_features: bool) + -> Arc Result + Send + Sync> +{ + let reloc_model = get_reloc_model(sess); + + let opt_level = get_llvm_opt_level(sess.opts.optimize); + let use_softfp = sess.opts.cg.soft_float; + + let ffunction_sections = sess.target.target.options.function_sections; + let fdata_sections = ffunction_sections; + + let code_model_arg = sess.opts.cg.code_model.as_ref().or( + sess.target.target.options.code_model.as_ref(), + ); + + let code_model = match code_model_arg { + Some(s) => { + match CODE_GEN_MODEL_ARGS.iter().find(|arg| arg.0 == s) { + Some(x) => x.1, + _ => { + sess.err(&format!("{:?} is not a valid code model", + code_model_arg)); + sess.abort_if_errors(); + bug!(); + } + } + } + None => llvm::CodeModel::None, + }; + + let singlethread = sess.target.target.options.singlethread; + + let triple = &sess.target.target.llvm_target; + + let triple = CString::new(triple.as_bytes()).unwrap(); + let cpu = sess.target_cpu(); + let cpu = CString::new(cpu.as_bytes()).unwrap(); + let features = attributes::llvm_target_features(sess) + .collect::>() + .join(","); + let features = CString::new(features).unwrap(); + let is_pie_binary = !find_features && is_pie_binary(sess); + let trap_unreachable = sess.target.target.options.trap_unreachable; + + Arc::new(move || { + let tm = unsafe { + llvm::LLVMRustCreateTargetMachine( + triple.as_ptr(), cpu.as_ptr(), features.as_ptr(), + code_model, + reloc_model, + opt_level, + use_softfp, + is_pie_binary, + ffunction_sections, + fdata_sections, + trap_unreachable, + singlethread, + ) + }; + + if tm.is_null() { + Err(format!("Could not create LLVM TargetMachine for triple: {}", + triple.to_str().unwrap())) + } else { + Ok(tm) + } + }) +} + +/// Module-specific configuration for `optimize_and_codegen`. +pub struct ModuleConfig { + /// Names of additional optimization passes to run. + passes: Vec, + /// Some(level) to optimize at a certain level, or None to run + /// absolutely no optimizations (used for the metadata module). + pub opt_level: Option, + + /// Some(level) to optimize binary size, or None to not affect program size. + opt_size: Option, + + pgo_gen: Option, + pgo_use: String, + + // Flags indicating which outputs to produce. + emit_no_opt_bc: bool, + emit_bc: bool, + emit_bc_compressed: bool, + emit_lto_bc: bool, + emit_ir: bool, + emit_asm: bool, + emit_obj: bool, + // Miscellaneous flags. These are mostly copied from command-line + // options. + no_verify: bool, + no_prepopulate_passes: bool, + no_builtins: bool, + time_passes: bool, + vectorize_loop: bool, + vectorize_slp: bool, + merge_functions: bool, + inline_threshold: Option, + // Instead of creating an object file by doing LLVM codegen, just + // make the object file bitcode. Provides easy compatibility with + // emscripten's ecc compiler, when used as the linker. + obj_is_bitcode: bool, + no_integrated_as: bool, + embed_bitcode: bool, + embed_bitcode_marker: bool, +} + +impl ModuleConfig { + fn new(passes: Vec) -> ModuleConfig { + ModuleConfig { + passes, + opt_level: None, + opt_size: None, + + pgo_gen: None, + pgo_use: String::new(), + + emit_no_opt_bc: false, + emit_bc: false, + emit_bc_compressed: false, + emit_lto_bc: false, + emit_ir: false, + emit_asm: false, + emit_obj: false, + obj_is_bitcode: false, + embed_bitcode: false, + embed_bitcode_marker: false, + no_integrated_as: false, + + no_verify: false, + no_prepopulate_passes: false, + no_builtins: false, + time_passes: false, + vectorize_loop: false, + vectorize_slp: false, + merge_functions: false, + inline_threshold: None + } + } + + fn set_flags(&mut self, sess: &Session, no_builtins: bool) { + self.no_verify = sess.no_verify(); + self.no_prepopulate_passes = sess.opts.cg.no_prepopulate_passes; + self.no_builtins = no_builtins || sess.target.target.options.no_builtins; + self.time_passes = sess.time_passes(); + self.inline_threshold = sess.opts.cg.inline_threshold; + self.obj_is_bitcode = sess.target.target.options.obj_is_bitcode; + let embed_bitcode = sess.target.target.options.embed_bitcode || + sess.opts.debugging_opts.embed_bitcode || + sess.opts.debugging_opts.cross_lang_lto.embed_bitcode(); + if embed_bitcode { + match sess.opts.optimize { + config::OptLevel::No | + config::OptLevel::Less => { + self.embed_bitcode_marker = embed_bitcode; + } + _ => self.embed_bitcode = embed_bitcode, + } + } + + // Copy what clang does by turning on loop vectorization at O2 and + // slp vectorization at O3. Otherwise configure other optimization aspects + // of this pass manager builder. + // Turn off vectorization for emscripten, as it's not very well supported. + self.vectorize_loop = !sess.opts.cg.no_vectorize_loops && + (sess.opts.optimize == config::OptLevel::Default || + sess.opts.optimize == config::OptLevel::Aggressive) && + !sess.target.target.options.is_like_emscripten; + + self.vectorize_slp = !sess.opts.cg.no_vectorize_slp && + sess.opts.optimize == config::OptLevel::Aggressive && + !sess.target.target.options.is_like_emscripten; + + self.merge_functions = sess.opts.optimize == config::OptLevel::Default || + sess.opts.optimize == config::OptLevel::Aggressive; + } +} + +/// Assembler name and command used by codegen when no_integrated_as is enabled +struct AssemblerCommand { + name: PathBuf, + cmd: Command, +} + +/// Additional resources used by optimize_and_codegen (not module specific) +#[derive(Clone)] +pub struct CodegenContext { + // Resouces needed when running LTO + pub time_passes: bool, + pub lto: Lto, + pub no_landing_pads: bool, + pub save_temps: bool, + pub fewer_names: bool, + pub exported_symbols: Option>, + pub opts: Arc, + pub crate_types: Vec, + pub each_linked_rlib_for_lto: Vec<(CrateNum, PathBuf)>, + output_filenames: Arc, + regular_module_config: Arc, + metadata_module_config: Arc, + allocator_module_config: Arc, + pub tm_factory: Arc Result + Send + Sync>, + pub msvc_imps_needed: bool, + pub target_pointer_width: String, + debuginfo: config::DebugInfoLevel, + + // Number of cgus excluding the allocator/metadata modules + pub total_cgus: usize, + // Handler to use for diagnostics produced during codegen. + pub diag_emitter: SharedEmitter, + // LLVM passes added by plugins. + pub plugin_passes: Vec, + // LLVM optimizations for which we want to print remarks. + pub remark: Passes, + // Worker thread number + pub worker: usize, + // The incremental compilation session directory, or None if we are not + // compiling incrementally + pub incr_comp_session_dir: Option, + // Channel back to the main control thread to send messages to + coordinator_send: Sender>, + // A reference to the TimeGraph so we can register timings. None means that + // measuring is disabled. + time_graph: Option, + // The assembler command if no_integrated_as option is enabled, None otherwise + assembler_cmd: Option>, +} + +impl CodegenContext { + pub fn create_diag_handler(&self) -> Handler { + Handler::with_emitter(true, false, Box::new(self.diag_emitter.clone())) + } + + pub(crate) fn config(&self, kind: ModuleKind) -> &ModuleConfig { + match kind { + ModuleKind::Regular => &self.regular_module_config, + ModuleKind::Metadata => &self.metadata_module_config, + ModuleKind::Allocator => &self.allocator_module_config, + } + } + + pub(crate) fn save_temp_bitcode(&self, module: &ModuleCodegen, name: &str) { + if !self.save_temps { + return + } + unsafe { + let ext = format!("{}.bc", name); + let cgu = Some(&module.name[..]); + let path = self.output_filenames.temp_path_ext(&ext, cgu); + let cstr = path2cstr(&path); + let llmod = module.llvm().unwrap().llmod; + llvm::LLVMWriteBitcodeToFile(llmod, cstr.as_ptr()); + } + } +} + +struct DiagnosticHandlers<'a> { + inner: Box<(&'a CodegenContext, &'a Handler)>, + llcx: ContextRef, +} + +impl<'a> DiagnosticHandlers<'a> { + fn new(cgcx: &'a CodegenContext, + handler: &'a Handler, + llcx: ContextRef) -> DiagnosticHandlers<'a> { + let data = Box::new((cgcx, handler)); + unsafe { + let arg = &*data as &(_, _) as *const _ as *mut _; + llvm::LLVMRustSetInlineAsmDiagnosticHandler(llcx, inline_asm_handler, arg); + llvm::LLVMContextSetDiagnosticHandler(llcx, diagnostic_handler, arg); + } + DiagnosticHandlers { + inner: data, + llcx: llcx, + } + } +} + +impl<'a> Drop for DiagnosticHandlers<'a> { + fn drop(&mut self) { + unsafe { + llvm::LLVMRustSetInlineAsmDiagnosticHandler(self.llcx, inline_asm_handler, 0 as *mut _); + llvm::LLVMContextSetDiagnosticHandler(self.llcx, diagnostic_handler, 0 as *mut _); + } + } +} + +unsafe extern "C" fn report_inline_asm<'a, 'b>(cgcx: &'a CodegenContext, + msg: &'b str, + cookie: c_uint) { + cgcx.diag_emitter.inline_asm_error(cookie as u32, msg.to_string()); +} + +unsafe extern "C" fn inline_asm_handler(diag: SMDiagnosticRef, + user: *const c_void, + cookie: c_uint) { + if user.is_null() { + return + } + let (cgcx, _) = *(user as *const (&CodegenContext, &Handler)); + + let msg = llvm::build_string(|s| llvm::LLVMRustWriteSMDiagnosticToString(diag, s)) + .expect("non-UTF8 SMDiagnostic"); + + report_inline_asm(cgcx, &msg, cookie); +} + +unsafe extern "C" fn diagnostic_handler(info: DiagnosticInfoRef, user: *mut c_void) { + if user.is_null() { + return + } + let (cgcx, diag_handler) = *(user as *const (&CodegenContext, &Handler)); + + match llvm::diagnostic::Diagnostic::unpack(info) { + llvm::diagnostic::InlineAsm(inline) => { + report_inline_asm(cgcx, + &llvm::twine_to_string(inline.message), + inline.cookie); + } + + llvm::diagnostic::Optimization(opt) => { + let enabled = match cgcx.remark { + AllPasses => true, + SomePasses(ref v) => v.iter().any(|s| *s == opt.pass_name), + }; + + if enabled { + diag_handler.note_without_error(&format!("optimization {} for {} at {}:{}:{}: {}", + opt.kind.describe(), + opt.pass_name, + opt.filename, + opt.line, + opt.column, + opt.message)); + } + } + llvm::diagnostic::PGO(diagnostic_ref) => { + let msg = llvm::build_string(|s| { + llvm::LLVMRustWriteDiagnosticInfoToString(diagnostic_ref, s) + }).expect("non-UTF8 PGO diagnostic"); + diag_handler.warn(&msg); + } + llvm::diagnostic::UnknownDiagnostic(..) => {}, + } +} + +// Unsafe due to LLVM calls. +unsafe fn optimize(cgcx: &CodegenContext, + diag_handler: &Handler, + module: &ModuleCodegen, + config: &ModuleConfig, + timeline: &mut Timeline) + -> Result<(), FatalError> +{ + let (llmod, llcx, tm) = match module.source { + ModuleSource::Codegened(ref llvm) => (llvm.llmod, llvm.llcx, llvm.tm), + ModuleSource::Preexisting(_) => { + bug!("optimize_and_codegen: called with ModuleSource::Preexisting") + } + }; + + let _handlers = DiagnosticHandlers::new(cgcx, diag_handler, llcx); + + let module_name = module.name.clone(); + let module_name = Some(&module_name[..]); + + if config.emit_no_opt_bc { + let out = cgcx.output_filenames.temp_path_ext("no-opt.bc", module_name); + let out = path2cstr(&out); + llvm::LLVMWriteBitcodeToFile(llmod, out.as_ptr()); + } + + if config.opt_level.is_some() { + // Create the two optimizing pass managers. These mirror what clang + // does, and are by populated by LLVM's default PassManagerBuilder. + // Each manager has a different set of passes, but they also share + // some common passes. + let fpm = llvm::LLVMCreateFunctionPassManagerForModule(llmod); + let mpm = llvm::LLVMCreatePassManager(); + + // If we're verifying or linting, add them to the function pass + // manager. + let addpass = |pass_name: &str| { + let pass_name = CString::new(pass_name).unwrap(); + let pass = llvm::LLVMRustFindAndCreatePass(pass_name.as_ptr()); + if pass.is_null() { + return false; + } + let pass_manager = match llvm::LLVMRustPassKind(pass) { + llvm::PassKind::Function => fpm, + llvm::PassKind::Module => mpm, + llvm::PassKind::Other => { + diag_handler.err("Encountered LLVM pass kind we can't handle"); + return true + }, + }; + llvm::LLVMRustAddPass(pass_manager, pass); + true + }; + + if !config.no_verify { assert!(addpass("verify")); } + if !config.no_prepopulate_passes { + llvm::LLVMRustAddAnalysisPasses(tm, fpm, llmod); + llvm::LLVMRustAddAnalysisPasses(tm, mpm, llmod); + let opt_level = config.opt_level.unwrap_or(llvm::CodeGenOptLevel::None); + let prepare_for_thin_lto = cgcx.lto == Lto::Thin || cgcx.lto == Lto::ThinLocal; + with_llvm_pmb(llmod, &config, opt_level, prepare_for_thin_lto, &mut |b| { + llvm::LLVMPassManagerBuilderPopulateFunctionPassManager(b, fpm); + llvm::LLVMPassManagerBuilderPopulateModulePassManager(b, mpm); + }) + } + + for pass in &config.passes { + if !addpass(pass) { + diag_handler.warn(&format!("unknown pass `{}`, ignoring", + pass)); + } + } + + for pass in &cgcx.plugin_passes { + if !addpass(pass) { + diag_handler.err(&format!("a plugin asked for LLVM pass \ + `{}` but LLVM does not \ + recognize it", pass)); + } + } + + diag_handler.abort_if_errors(); + + // Finally, run the actual optimization passes + time_ext(config.time_passes, + None, + &format!("llvm function passes [{}]", module_name.unwrap()), + || { + llvm::LLVMRustRunFunctionPassManager(fpm, llmod) + }); + timeline.record("fpm"); + time_ext(config.time_passes, + None, + &format!("llvm module passes [{}]", module_name.unwrap()), + || { + llvm::LLVMRunPassManager(mpm, llmod) + }); + + // Deallocate managers that we're now done with + llvm::LLVMDisposePassManager(fpm); + llvm::LLVMDisposePassManager(mpm); + } + Ok(()) +} + +fn generate_lto_work(cgcx: &CodegenContext, + modules: Vec) + -> Vec<(WorkItem, u64)> +{ + let mut timeline = cgcx.time_graph.as_ref().map(|tg| { + tg.start(CODEGEN_WORKER_TIMELINE, + CODEGEN_WORK_PACKAGE_KIND, + "generate lto") + }).unwrap_or(Timeline::noop()); + let lto_modules = lto::run(cgcx, modules, &mut timeline) + .unwrap_or_else(|e| e.raise()); + + lto_modules.into_iter().map(|module| { + let cost = module.cost(); + (WorkItem::LTO(module), cost) + }).collect() +} + +unsafe fn codegen(cgcx: &CodegenContext, + diag_handler: &Handler, + module: ModuleCodegen, + config: &ModuleConfig, + timeline: &mut Timeline) + -> Result +{ + timeline.record("codegen"); + let (llmod, llcx, tm) = match module.source { + ModuleSource::Codegened(ref llvm) => (llvm.llmod, llvm.llcx, llvm.tm), + ModuleSource::Preexisting(_) => { + bug!("codegen: called with ModuleSource::Preexisting") + } + }; + let module_name = module.name.clone(); + let module_name = Some(&module_name[..]); + let handlers = DiagnosticHandlers::new(cgcx, diag_handler, llcx); + + if cgcx.msvc_imps_needed { + create_msvc_imps(cgcx, llcx, llmod); + } + + // A codegen-specific pass manager is used to generate object + // files for an LLVM module. + // + // Apparently each of these pass managers is a one-shot kind of + // thing, so we create a new one for each type of output. The + // pass manager passed to the closure should be ensured to not + // escape the closure itself, and the manager should only be + // used once. + unsafe fn with_codegen(tm: TargetMachineRef, + llmod: ModuleRef, + no_builtins: bool, + f: F) -> R + where F: FnOnce(PassManagerRef) -> R, + { + let cpm = llvm::LLVMCreatePassManager(); + llvm::LLVMRustAddAnalysisPasses(tm, cpm, llmod); + llvm::LLVMRustAddLibraryInfo(cpm, llmod, no_builtins); + f(cpm) + } + + // If we don't have the integrated assembler, then we need to emit asm + // from LLVM and use `gcc` to create the object file. + let asm_to_obj = config.emit_obj && config.no_integrated_as; + + // Change what we write and cleanup based on whether obj files are + // just llvm bitcode. In that case write bitcode, and possibly + // delete the bitcode if it wasn't requested. Don't generate the + // machine code, instead copy the .o file from the .bc + let write_bc = config.emit_bc || config.obj_is_bitcode; + let rm_bc = !config.emit_bc && config.obj_is_bitcode; + let write_obj = config.emit_obj && !config.obj_is_bitcode && !asm_to_obj; + let copy_bc_to_obj = config.emit_obj && config.obj_is_bitcode; + + let bc_out = cgcx.output_filenames.temp_path(OutputType::Bitcode, module_name); + let obj_out = cgcx.output_filenames.temp_path(OutputType::Object, module_name); + + + if write_bc || config.emit_bc_compressed || config.embed_bitcode { + let thin; + let old; + let data = if llvm::LLVMRustThinLTOAvailable() { + thin = ThinBuffer::new(llmod); + thin.data() + } else { + old = ModuleBuffer::new(llmod); + old.data() + }; + timeline.record("make-bc"); + + if write_bc { + if let Err(e) = fs::write(&bc_out, data) { + diag_handler.err(&format!("failed to write bytecode: {}", e)); + } + timeline.record("write-bc"); + } + + if config.embed_bitcode { + embed_bitcode(cgcx, llcx, llmod, Some(data)); + timeline.record("embed-bc"); + } + + if config.emit_bc_compressed { + let dst = bc_out.with_extension(RLIB_BYTECODE_EXTENSION); + let data = bytecode::encode(&module.llmod_id, data); + if let Err(e) = fs::write(&dst, data) { + diag_handler.err(&format!("failed to write bytecode: {}", e)); + } + timeline.record("compress-bc"); + } + } else if config.embed_bitcode_marker { + embed_bitcode(cgcx, llcx, llmod, None); + } + + time_ext(config.time_passes, None, &format!("codegen passes [{}]", module_name.unwrap()), + || -> Result<(), FatalError> { + if config.emit_ir { + let out = cgcx.output_filenames.temp_path(OutputType::LlvmAssembly, module_name); + let out = path2cstr(&out); + + extern "C" fn demangle_callback(input_ptr: *const c_char, + input_len: size_t, + output_ptr: *mut c_char, + output_len: size_t) -> size_t { + let input = unsafe { + slice::from_raw_parts(input_ptr as *const u8, input_len as usize) + }; + + let input = match str::from_utf8(input) { + Ok(s) => s, + Err(_) => return 0, + }; + + let output = unsafe { + slice::from_raw_parts_mut(output_ptr as *mut u8, output_len as usize) + }; + let mut cursor = io::Cursor::new(output); + + let demangled = match rustc_demangle::try_demangle(input) { + Ok(d) => d, + Err(_) => return 0, + }; + + if let Err(_) = write!(cursor, "{:#}", demangled) { + // Possible only if provided buffer is not big enough + return 0; + } + + cursor.position() as size_t + } + + with_codegen(tm, llmod, config.no_builtins, |cpm| { + llvm::LLVMRustPrintModule(cpm, llmod, out.as_ptr(), demangle_callback); + llvm::LLVMDisposePassManager(cpm); + }); + timeline.record("ir"); + } + + if config.emit_asm || asm_to_obj { + let path = cgcx.output_filenames.temp_path(OutputType::Assembly, module_name); + + // We can't use the same module for asm and binary output, because that triggers + // various errors like invalid IR or broken binaries, so we might have to clone the + // module to produce the asm output + let llmod = if config.emit_obj { + llvm::LLVMCloneModule(llmod) + } else { + llmod + }; + with_codegen(tm, llmod, config.no_builtins, |cpm| { + write_output_file(diag_handler, tm, cpm, llmod, &path, + llvm::FileType::AssemblyFile) + })?; + if config.emit_obj { + llvm::LLVMDisposeModule(llmod); + } + timeline.record("asm"); + } + + if write_obj { + with_codegen(tm, llmod, config.no_builtins, |cpm| { + write_output_file(diag_handler, tm, cpm, llmod, &obj_out, + llvm::FileType::ObjectFile) + })?; + timeline.record("obj"); + } else if asm_to_obj { + let assembly = cgcx.output_filenames.temp_path(OutputType::Assembly, module_name); + run_assembler(cgcx, diag_handler, &assembly, &obj_out); + timeline.record("asm_to_obj"); + + if !config.emit_asm && !cgcx.save_temps { + drop(fs::remove_file(&assembly)); + } + } + + Ok(()) + })?; + + if copy_bc_to_obj { + debug!("copying bitcode {:?} to obj {:?}", bc_out, obj_out); + if let Err(e) = link_or_copy(&bc_out, &obj_out) { + diag_handler.err(&format!("failed to copy bitcode to object file: {}", e)); + } + } + + if rm_bc { + debug!("removing_bitcode {:?}", bc_out); + if let Err(e) = fs::remove_file(&bc_out) { + diag_handler.err(&format!("failed to remove bitcode: {}", e)); + } + } + + drop(handlers); + Ok(module.into_compiled_module(config.emit_obj, + config.emit_bc, + config.emit_bc_compressed, + &cgcx.output_filenames)) +} + +/// Embed the bitcode of an LLVM module in the LLVM module itself. +/// +/// This is done primarily for iOS where it appears to be standard to compile C +/// code at least with `-fembed-bitcode` which creates two sections in the +/// executable: +/// +/// * __LLVM,__bitcode +/// * __LLVM,__cmdline +/// +/// It appears *both* of these sections are necessary to get the linker to +/// recognize what's going on. For us though we just always throw in an empty +/// cmdline section. +/// +/// Furthermore debug/O1 builds don't actually embed bitcode but rather just +/// embed an empty section. +/// +/// Basically all of this is us attempting to follow in the footsteps of clang +/// on iOS. See #35968 for lots more info. +unsafe fn embed_bitcode(cgcx: &CodegenContext, + llcx: ContextRef, + llmod: ModuleRef, + bitcode: Option<&[u8]>) { + let llconst = C_bytes_in_context(llcx, bitcode.unwrap_or(&[])); + let llglobal = llvm::LLVMAddGlobal( + llmod, + val_ty(llconst).to_ref(), + "rustc.embedded.module\0".as_ptr() as *const _, + ); + llvm::LLVMSetInitializer(llglobal, llconst); + + let is_apple = cgcx.opts.target_triple.triple().contains("-ios") || + cgcx.opts.target_triple.triple().contains("-darwin"); + + let section = if is_apple { + "__LLVM,__bitcode\0" + } else { + ".llvmbc\0" + }; + llvm::LLVMSetSection(llglobal, section.as_ptr() as *const _); + llvm::LLVMRustSetLinkage(llglobal, llvm::Linkage::PrivateLinkage); + llvm::LLVMSetGlobalConstant(llglobal, llvm::True); + + let llconst = C_bytes_in_context(llcx, &[]); + let llglobal = llvm::LLVMAddGlobal( + llmod, + val_ty(llconst).to_ref(), + "rustc.embedded.cmdline\0".as_ptr() as *const _, + ); + llvm::LLVMSetInitializer(llglobal, llconst); + let section = if is_apple { + "__LLVM,__cmdline\0" + } else { + ".llvmcmd\0" + }; + llvm::LLVMSetSection(llglobal, section.as_ptr() as *const _); + llvm::LLVMRustSetLinkage(llglobal, llvm::Linkage::PrivateLinkage); +} + +pub(crate) struct CompiledModules { + pub modules: Vec, + pub metadata_module: CompiledModule, + pub allocator_module: Option, +} + +fn need_crate_bitcode_for_rlib(sess: &Session) -> bool { + sess.crate_types.borrow().contains(&config::CrateTypeRlib) && + sess.opts.output_types.contains_key(&OutputType::Exe) +} + +pub fn start_async_codegen(tcx: TyCtxt, + time_graph: Option, + link: LinkMeta, + metadata: EncodedMetadata, + coordinator_receive: Receiver>, + total_cgus: usize) + -> OngoingCodegen { + let sess = tcx.sess; + let crate_name = tcx.crate_name(LOCAL_CRATE); + let no_builtins = attr::contains_name(&tcx.hir.krate().attrs, "no_builtins"); + let subsystem = attr::first_attr_value_str_by_name(&tcx.hir.krate().attrs, + "windows_subsystem"); + let windows_subsystem = subsystem.map(|subsystem| { + if subsystem != "windows" && subsystem != "console" { + tcx.sess.fatal(&format!("invalid windows subsystem `{}`, only \ + `windows` and `console` are allowed", + subsystem)); + } + subsystem.to_string() + }); + + let linker_info = LinkerInfo::new(tcx); + let crate_info = CrateInfo::new(tcx); + + // Figure out what we actually need to build. + let mut modules_config = ModuleConfig::new(sess.opts.cg.passes.clone()); + let mut metadata_config = ModuleConfig::new(vec![]); + let mut allocator_config = ModuleConfig::new(vec![]); + + if let Some(ref sanitizer) = sess.opts.debugging_opts.sanitizer { + match *sanitizer { + Sanitizer::Address => { + modules_config.passes.push("asan".to_owned()); + modules_config.passes.push("asan-module".to_owned()); + } + Sanitizer::Memory => { + modules_config.passes.push("msan".to_owned()) + } + Sanitizer::Thread => { + modules_config.passes.push("tsan".to_owned()) + } + _ => {} + } + } + + if sess.opts.debugging_opts.profile { + modules_config.passes.push("insert-gcov-profiling".to_owned()) + } + + modules_config.pgo_gen = sess.opts.debugging_opts.pgo_gen.clone(); + modules_config.pgo_use = sess.opts.debugging_opts.pgo_use.clone(); + + modules_config.opt_level = Some(get_llvm_opt_level(sess.opts.optimize)); + modules_config.opt_size = Some(get_llvm_opt_size(sess.opts.optimize)); + + // Save all versions of the bytecode if we're saving our temporaries. + if sess.opts.cg.save_temps { + modules_config.emit_no_opt_bc = true; + modules_config.emit_bc = true; + modules_config.emit_lto_bc = true; + metadata_config.emit_bc = true; + allocator_config.emit_bc = true; + } + + // Emit compressed bitcode files for the crate if we're emitting an rlib. + // Whenever an rlib is created, the bitcode is inserted into the archive in + // order to allow LTO against it. + if need_crate_bitcode_for_rlib(sess) { + modules_config.emit_bc_compressed = true; + allocator_config.emit_bc_compressed = true; + } + + modules_config.no_integrated_as = tcx.sess.opts.cg.no_integrated_as || + tcx.sess.target.target.options.no_integrated_as; + + for output_type in sess.opts.output_types.keys() { + match *output_type { + OutputType::Bitcode => { modules_config.emit_bc = true; } + OutputType::LlvmAssembly => { modules_config.emit_ir = true; } + OutputType::Assembly => { + modules_config.emit_asm = true; + // If we're not using the LLVM assembler, this function + // could be invoked specially with output_type_assembly, so + // in this case we still want the metadata object file. + if !sess.opts.output_types.contains_key(&OutputType::Assembly) { + metadata_config.emit_obj = true; + allocator_config.emit_obj = true; + } + } + OutputType::Object => { modules_config.emit_obj = true; } + OutputType::Metadata => { metadata_config.emit_obj = true; } + OutputType::Exe => { + modules_config.emit_obj = true; + metadata_config.emit_obj = true; + allocator_config.emit_obj = true; + }, + OutputType::Mir => {} + OutputType::DepInfo => {} + } + } + + modules_config.set_flags(sess, no_builtins); + metadata_config.set_flags(sess, no_builtins); + allocator_config.set_flags(sess, no_builtins); + + // Exclude metadata and allocator modules from time_passes output, since + // they throw off the "LLVM passes" measurement. + metadata_config.time_passes = false; + allocator_config.time_passes = false; + + let (shared_emitter, shared_emitter_main) = SharedEmitter::new(); + let (codegen_worker_send, codegen_worker_receive) = channel(); + + let coordinator_thread = start_executing_work(tcx, + &crate_info, + shared_emitter, + codegen_worker_send, + coordinator_receive, + total_cgus, + sess.jobserver.clone(), + time_graph.clone(), + Arc::new(modules_config), + Arc::new(metadata_config), + Arc::new(allocator_config)); + + OngoingCodegen { + crate_name, + link, + metadata, + windows_subsystem, + linker_info, + crate_info, + + time_graph, + coordinator_send: tcx.tx_to_llvm_workers.lock().clone(), + codegen_worker_receive, + shared_emitter_main, + future: coordinator_thread, + output_filenames: tcx.output_filenames(LOCAL_CRATE), + } +} + +fn copy_all_cgu_workproducts_to_incr_comp_cache_dir( + sess: &Session, + compiled_modules: &CompiledModules +) -> FxHashMap { + let mut work_products = FxHashMap::default(); + + if sess.opts.incremental.is_none() { + return work_products; + } + + for module in compiled_modules.modules.iter() { + let mut files = vec![]; + + if let Some(ref path) = module.object { + files.push((WorkProductFileKind::Object, path.clone())); + } + if let Some(ref path) = module.bytecode { + files.push((WorkProductFileKind::Bytecode, path.clone())); + } + if let Some(ref path) = module.bytecode_compressed { + files.push((WorkProductFileKind::BytecodeCompressed, path.clone())); + } + + if let Some((id, product)) = + copy_cgu_workproducts_to_incr_comp_cache_dir(sess, &module.name, &files) { + work_products.insert(id, product); + } + } + + work_products +} + +fn produce_final_output_artifacts(sess: &Session, + compiled_modules: &CompiledModules, + crate_output: &OutputFilenames) { + let mut user_wants_bitcode = false; + let mut user_wants_objects = false; + + // Produce final compile outputs. + let copy_gracefully = |from: &Path, to: &Path| { + if let Err(e) = fs::copy(from, to) { + sess.err(&format!("could not copy {:?} to {:?}: {}", from, to, e)); + } + }; + + let copy_if_one_unit = |output_type: OutputType, + keep_numbered: bool| { + if compiled_modules.modules.len() == 1 { + // 1) Only one codegen unit. In this case it's no difficulty + // to copy `foo.0.x` to `foo.x`. + let module_name = Some(&compiled_modules.modules[0].name[..]); + let path = crate_output.temp_path(output_type, module_name); + copy_gracefully(&path, + &crate_output.path(output_type)); + if !sess.opts.cg.save_temps && !keep_numbered { + // The user just wants `foo.x`, not `foo.#module-name#.x`. + remove(sess, &path); + } + } else { + let ext = crate_output.temp_path(output_type, None) + .extension() + .unwrap() + .to_str() + .unwrap() + .to_owned(); + + if crate_output.outputs.contains_key(&output_type) { + // 2) Multiple codegen units, with `--emit foo=some_name`. We have + // no good solution for this case, so warn the user. + sess.warn(&format!("ignoring emit path because multiple .{} files \ + were produced", ext)); + } else if crate_output.single_output_file.is_some() { + // 3) Multiple codegen units, with `-o some_name`. We have + // no good solution for this case, so warn the user. + sess.warn(&format!("ignoring -o because multiple .{} files \ + were produced", ext)); + } else { + // 4) Multiple codegen units, but no explicit name. We + // just leave the `foo.0.x` files in place. + // (We don't have to do any work in this case.) + } + } + }; + + // Flag to indicate whether the user explicitly requested bitcode. + // Otherwise, we produced it only as a temporary output, and will need + // to get rid of it. + for output_type in crate_output.outputs.keys() { + match *output_type { + OutputType::Bitcode => { + user_wants_bitcode = true; + // Copy to .bc, but always keep the .0.bc. There is a later + // check to figure out if we should delete .0.bc files, or keep + // them for making an rlib. + copy_if_one_unit(OutputType::Bitcode, true); + } + OutputType::LlvmAssembly => { + copy_if_one_unit(OutputType::LlvmAssembly, false); + } + OutputType::Assembly => { + copy_if_one_unit(OutputType::Assembly, false); + } + OutputType::Object => { + user_wants_objects = true; + copy_if_one_unit(OutputType::Object, true); + } + OutputType::Mir | + OutputType::Metadata | + OutputType::Exe | + OutputType::DepInfo => {} + } + } + + // Clean up unwanted temporary files. + + // We create the following files by default: + // - #crate#.#module-name#.bc + // - #crate#.#module-name#.o + // - #crate#.crate.metadata.bc + // - #crate#.crate.metadata.o + // - #crate#.o (linked from crate.##.o) + // - #crate#.bc (copied from crate.##.bc) + // We may create additional files if requested by the user (through + // `-C save-temps` or `--emit=` flags). + + if !sess.opts.cg.save_temps { + // Remove the temporary .#module-name#.o objects. If the user didn't + // explicitly request bitcode (with --emit=bc), and the bitcode is not + // needed for building an rlib, then we must remove .#module-name#.bc as + // well. + + // Specific rules for keeping .#module-name#.bc: + // - If the user requested bitcode (`user_wants_bitcode`), and + // codegen_units > 1, then keep it. + // - If the user requested bitcode but codegen_units == 1, then we + // can toss .#module-name#.bc because we copied it to .bc earlier. + // - If we're not building an rlib and the user didn't request + // bitcode, then delete .#module-name#.bc. + // If you change how this works, also update back::link::link_rlib, + // where .#module-name#.bc files are (maybe) deleted after making an + // rlib. + let needs_crate_object = crate_output.outputs.contains_key(&OutputType::Exe); + + let keep_numbered_bitcode = user_wants_bitcode && sess.codegen_units() > 1; + + let keep_numbered_objects = needs_crate_object || + (user_wants_objects && sess.codegen_units() > 1); + + for module in compiled_modules.modules.iter() { + if let Some(ref path) = module.object { + if !keep_numbered_objects { + remove(sess, path); + } + } + + if let Some(ref path) = module.bytecode { + if !keep_numbered_bitcode { + remove(sess, path); + } + } + } + + if !user_wants_bitcode { + if let Some(ref path) = compiled_modules.metadata_module.bytecode { + remove(sess, &path); + } + + if let Some(ref allocator_module) = compiled_modules.allocator_module { + if let Some(ref path) = allocator_module.bytecode { + remove(sess, path); + } + } + } + } + + // We leave the following files around by default: + // - #crate#.o + // - #crate#.crate.metadata.o + // - #crate#.bc + // These are used in linking steps and will be cleaned up afterward. +} + +pub(crate) fn dump_incremental_data(codegen_results: &CodegenResults) { + println!("[incremental] Re-using {} out of {} modules", + codegen_results.modules.iter().filter(|m| m.pre_existing).count(), + codegen_results.modules.len()); +} + +enum WorkItem { + Optimize(ModuleCodegen), + LTO(lto::LtoModuleCodegen), +} + +impl WorkItem { + fn kind(&self) -> ModuleKind { + match *self { + WorkItem::Optimize(ref m) => m.kind, + WorkItem::LTO(_) => ModuleKind::Regular, + } + } + + fn name(&self) -> String { + match *self { + WorkItem::Optimize(ref m) => format!("optimize: {}", m.name), + WorkItem::LTO(ref m) => format!("lto: {}", m.name()), + } + } +} + +enum WorkItemResult { + Compiled(CompiledModule), + NeedsLTO(ModuleCodegen), +} + +fn execute_work_item(cgcx: &CodegenContext, + work_item: WorkItem, + timeline: &mut Timeline) + -> Result +{ + let diag_handler = cgcx.create_diag_handler(); + let config = cgcx.config(work_item.kind()); + let module = match work_item { + WorkItem::Optimize(module) => module, + WorkItem::LTO(mut lto) => { + unsafe { + let module = lto.optimize(cgcx, timeline)?; + let module = codegen(cgcx, &diag_handler, module, config, timeline)?; + return Ok(WorkItemResult::Compiled(module)) + } + } + }; + let module_name = module.name.clone(); + + let pre_existing = match module.source { + ModuleSource::Codegened(_) => None, + ModuleSource::Preexisting(ref wp) => Some(wp.clone()), + }; + + if let Some(wp) = pre_existing { + let incr_comp_session_dir = cgcx.incr_comp_session_dir + .as_ref() + .unwrap(); + let name = &module.name; + let mut object = None; + let mut bytecode = None; + let mut bytecode_compressed = None; + for (kind, saved_file) in wp.saved_files { + let obj_out = match kind { + WorkProductFileKind::Object => { + let path = cgcx.output_filenames.temp_path(OutputType::Object, Some(name)); + object = Some(path.clone()); + path + } + WorkProductFileKind::Bytecode => { + let path = cgcx.output_filenames.temp_path(OutputType::Bitcode, Some(name)); + bytecode = Some(path.clone()); + path + } + WorkProductFileKind::BytecodeCompressed => { + let path = cgcx.output_filenames.temp_path(OutputType::Bitcode, Some(name)) + .with_extension(RLIB_BYTECODE_EXTENSION); + bytecode_compressed = Some(path.clone()); + path + } + }; + let source_file = in_incr_comp_dir(&incr_comp_session_dir, + &saved_file); + debug!("copying pre-existing module `{}` from {:?} to {}", + module.name, + source_file, + obj_out.display()); + match link_or_copy(&source_file, &obj_out) { + Ok(_) => { } + Err(err) => { + diag_handler.err(&format!("unable to copy {} to {}: {}", + source_file.display(), + obj_out.display(), + err)); + } + } + } + assert_eq!(object.is_some(), config.emit_obj); + assert_eq!(bytecode.is_some(), config.emit_bc); + assert_eq!(bytecode_compressed.is_some(), config.emit_bc_compressed); + + Ok(WorkItemResult::Compiled(CompiledModule { + llmod_id: module.llmod_id.clone(), + name: module_name, + kind: ModuleKind::Regular, + pre_existing: true, + object, + bytecode, + bytecode_compressed, + })) + } else { + debug!("llvm-optimizing {:?}", module_name); + + unsafe { + optimize(cgcx, &diag_handler, &module, config, timeline)?; + + // After we've done the initial round of optimizations we need to + // decide whether to synchronously codegen this module or ship it + // back to the coordinator thread for further LTO processing (which + // has to wait for all the initial modules to be optimized). + // + // Here we dispatch based on the `cgcx.lto` and kind of module we're + // codegenning... + let needs_lto = match cgcx.lto { + Lto::No => false, + + // Here we've got a full crate graph LTO requested. We ignore + // this, however, if the crate type is only an rlib as there's + // no full crate graph to process, that'll happen later. + // + // This use case currently comes up primarily for targets that + // require LTO so the request for LTO is always unconditionally + // passed down to the backend, but we don't actually want to do + // anything about it yet until we've got a final product. + Lto::Yes | Lto::Fat | Lto::Thin => { + cgcx.crate_types.len() != 1 || + cgcx.crate_types[0] != config::CrateTypeRlib + } + + // When we're automatically doing ThinLTO for multi-codegen-unit + // builds we don't actually want to LTO the allocator modules if + // it shows up. This is due to various linker shenanigans that + // we'll encounter later. + // + // Additionally here's where we also factor in the current LLVM + // version. If it doesn't support ThinLTO we skip this. + Lto::ThinLocal => { + module.kind != ModuleKind::Allocator && + llvm::LLVMRustThinLTOAvailable() + } + }; + + // Metadata modules never participate in LTO regardless of the lto + // settings. + let needs_lto = needs_lto && module.kind != ModuleKind::Metadata; + + // Don't run LTO passes when cross-lang LTO is enabled. The linker + // will do that for us in this case. + let needs_lto = needs_lto && + !cgcx.opts.debugging_opts.cross_lang_lto.embed_bitcode(); + + if needs_lto { + Ok(WorkItemResult::NeedsLTO(module)) + } else { + let module = codegen(cgcx, &diag_handler, module, config, timeline)?; + Ok(WorkItemResult::Compiled(module)) + } + } + } +} + +enum Message { + Token(io::Result), + NeedsLTO { + result: ModuleCodegen, + worker_id: usize, + }, + Done { + result: Result, + worker_id: usize, + }, + CodegenDone { + llvm_work_item: WorkItem, + cost: u64, + }, + CodegenComplete, + CodegenItem, +} + +struct Diagnostic { + msg: String, + code: Option, + lvl: Level, +} + +#[derive(PartialEq, Clone, Copy, Debug)] +enum MainThreadWorkerState { + Idle, + Codegenning, + LLVMing, +} + +fn start_executing_work(tcx: TyCtxt, + crate_info: &CrateInfo, + shared_emitter: SharedEmitter, + codegen_worker_send: Sender, + coordinator_receive: Receiver>, + total_cgus: usize, + jobserver: Client, + time_graph: Option, + modules_config: Arc, + metadata_config: Arc, + allocator_config: Arc) + -> thread::JoinHandle> { + let coordinator_send = tcx.tx_to_llvm_workers.lock().clone(); + let sess = tcx.sess; + + // Compute the set of symbols we need to retain when doing LTO (if we need to) + let exported_symbols = { + let mut exported_symbols = FxHashMap(); + + let copy_symbols = |cnum| { + let symbols = tcx.exported_symbols(cnum) + .iter() + .map(|&(s, lvl)| (s.symbol_name(tcx).to_string(), lvl)) + .collect(); + Arc::new(symbols) + }; + + match sess.lto() { + Lto::No => None, + Lto::ThinLocal => { + exported_symbols.insert(LOCAL_CRATE, copy_symbols(LOCAL_CRATE)); + Some(Arc::new(exported_symbols)) + } + Lto::Yes | Lto::Fat | Lto::Thin => { + exported_symbols.insert(LOCAL_CRATE, copy_symbols(LOCAL_CRATE)); + for &cnum in tcx.crates().iter() { + exported_symbols.insert(cnum, copy_symbols(cnum)); + } + Some(Arc::new(exported_symbols)) + } + } + }; + + // First up, convert our jobserver into a helper thread so we can use normal + // mpsc channels to manage our messages and such. + // After we've requested tokens then we'll, when we can, + // get tokens on `coordinator_receive` which will + // get managed in the main loop below. + let coordinator_send2 = coordinator_send.clone(); + let helper = jobserver.into_helper_thread(move |token| { + drop(coordinator_send2.send(Box::new(Message::Token(token)))); + }).expect("failed to spawn helper thread"); + + let mut each_linked_rlib_for_lto = Vec::new(); + drop(link::each_linked_rlib(sess, crate_info, &mut |cnum, path| { + if link::ignored_for_lto(sess, crate_info, cnum) { + return + } + each_linked_rlib_for_lto.push((cnum, path.to_path_buf())); + })); + + let assembler_cmd = if modules_config.no_integrated_as { + // HACK: currently we use linker (gcc) as our assembler + let (name, mut cmd) = get_linker(sess); + cmd.args(&sess.target.target.options.asm_args); + Some(Arc::new(AssemblerCommand { + name, + cmd, + })) + } else { + None + }; + + let cgcx = CodegenContext { + crate_types: sess.crate_types.borrow().clone(), + each_linked_rlib_for_lto, + lto: sess.lto(), + no_landing_pads: sess.no_landing_pads(), + fewer_names: sess.fewer_names(), + save_temps: sess.opts.cg.save_temps, + opts: Arc::new(sess.opts.clone()), + time_passes: sess.time_passes(), + exported_symbols, + plugin_passes: sess.plugin_llvm_passes.borrow().clone(), + remark: sess.opts.cg.remark.clone(), + worker: 0, + incr_comp_session_dir: sess.incr_comp_session_dir_opt().map(|r| r.clone()), + coordinator_send, + diag_emitter: shared_emitter.clone(), + time_graph, + output_filenames: tcx.output_filenames(LOCAL_CRATE), + regular_module_config: modules_config, + metadata_module_config: metadata_config, + allocator_module_config: allocator_config, + tm_factory: target_machine_factory(tcx.sess, false), + total_cgus, + msvc_imps_needed: msvc_imps_needed(tcx), + target_pointer_width: tcx.sess.target.target.target_pointer_width.clone(), + debuginfo: tcx.sess.opts.debuginfo, + assembler_cmd, + }; + + // This is the "main loop" of parallel work happening for parallel codegen. + // It's here that we manage parallelism, schedule work, and work with + // messages coming from clients. + // + // There are a few environmental pre-conditions that shape how the system + // is set up: + // + // - Error reporting only can happen on the main thread because that's the + // only place where we have access to the compiler `Session`. + // - LLVM work can be done on any thread. + // - Codegen can only happen on the main thread. + // - Each thread doing substantial work most be in possession of a `Token` + // from the `Jobserver`. + // - The compiler process always holds one `Token`. Any additional `Tokens` + // have to be requested from the `Jobserver`. + // + // Error Reporting + // =============== + // The error reporting restriction is handled separately from the rest: We + // set up a `SharedEmitter` the holds an open channel to the main thread. + // When an error occurs on any thread, the shared emitter will send the + // error message to the receiver main thread (`SharedEmitterMain`). The + // main thread will periodically query this error message queue and emit + // any error messages it has received. It might even abort compilation if + // has received a fatal error. In this case we rely on all other threads + // being torn down automatically with the main thread. + // Since the main thread will often be busy doing codegen work, error + // reporting will be somewhat delayed, since the message queue can only be + // checked in between to work packages. + // + // Work Processing Infrastructure + // ============================== + // The work processing infrastructure knows three major actors: + // + // - the coordinator thread, + // - the main thread, and + // - LLVM worker threads + // + // The coordinator thread is running a message loop. It instructs the main + // thread about what work to do when, and it will spawn off LLVM worker + // threads as open LLVM WorkItems become available. + // + // The job of the main thread is to codegen CGUs into LLVM work package + // (since the main thread is the only thread that can do this). The main + // thread will block until it receives a message from the coordinator, upon + // which it will codegen one CGU, send it to the coordinator and block + // again. This way the coordinator can control what the main thread is + // doing. + // + // The coordinator keeps a queue of LLVM WorkItems, and when a `Token` is + // available, it will spawn off a new LLVM worker thread and let it process + // that a WorkItem. When a LLVM worker thread is done with its WorkItem, + // it will just shut down, which also frees all resources associated with + // the given LLVM module, and sends a message to the coordinator that the + // has been completed. + // + // Work Scheduling + // =============== + // The scheduler's goal is to minimize the time it takes to complete all + // work there is, however, we also want to keep memory consumption low + // if possible. These two goals are at odds with each other: If memory + // consumption were not an issue, we could just let the main thread produce + // LLVM WorkItems at full speed, assuring maximal utilization of + // Tokens/LLVM worker threads. However, since codegen usual is faster + // than LLVM processing, the queue of LLVM WorkItems would fill up and each + // WorkItem potentially holds on to a substantial amount of memory. + // + // So the actual goal is to always produce just enough LLVM WorkItems as + // not to starve our LLVM worker threads. That means, once we have enough + // WorkItems in our queue, we can block the main thread, so it does not + // produce more until we need them. + // + // Doing LLVM Work on the Main Thread + // ---------------------------------- + // Since the main thread owns the compiler processes implicit `Token`, it is + // wasteful to keep it blocked without doing any work. Therefore, what we do + // in this case is: We spawn off an additional LLVM worker thread that helps + // reduce the queue. The work it is doing corresponds to the implicit + // `Token`. The coordinator will mark the main thread as being busy with + // LLVM work. (The actual work happens on another OS thread but we just care + // about `Tokens`, not actual threads). + // + // When any LLVM worker thread finishes while the main thread is marked as + // "busy with LLVM work", we can do a little switcheroo: We give the Token + // of the just finished thread to the LLVM worker thread that is working on + // behalf of the main thread's implicit Token, thus freeing up the main + // thread again. The coordinator can then again decide what the main thread + // should do. This allows the coordinator to make decisions at more points + // in time. + // + // Striking a Balance between Throughput and Memory Consumption + // ------------------------------------------------------------ + // Since our two goals, (1) use as many Tokens as possible and (2) keep + // memory consumption as low as possible, are in conflict with each other, + // we have to find a trade off between them. Right now, the goal is to keep + // all workers busy, which means that no worker should find the queue empty + // when it is ready to start. + // How do we do achieve this? Good question :) We actually never know how + // many `Tokens` are potentially available so it's hard to say how much to + // fill up the queue before switching the main thread to LLVM work. Also we + // currently don't have a means to estimate how long a running LLVM worker + // will still be busy with it's current WorkItem. However, we know the + // maximal count of available Tokens that makes sense (=the number of CPU + // cores), so we can take a conservative guess. The heuristic we use here + // is implemented in the `queue_full_enough()` function. + // + // Some Background on Jobservers + // ----------------------------- + // It's worth also touching on the management of parallelism here. We don't + // want to just spawn a thread per work item because while that's optimal + // parallelism it may overload a system with too many threads or violate our + // configuration for the maximum amount of cpu to use for this process. To + // manage this we use the `jobserver` crate. + // + // Job servers are an artifact of GNU make and are used to manage + // parallelism between processes. A jobserver is a glorified IPC semaphore + // basically. Whenever we want to run some work we acquire the semaphore, + // and whenever we're done with that work we release the semaphore. In this + // manner we can ensure that the maximum number of parallel workers is + // capped at any one point in time. + // + // LTO and the coordinator thread + // ------------------------------ + // + // The final job the coordinator thread is responsible for is managing LTO + // and how that works. When LTO is requested what we'll to is collect all + // optimized LLVM modules into a local vector on the coordinator. Once all + // modules have been codegened and optimized we hand this to the `lto` + // module for further optimization. The `lto` module will return back a list + // of more modules to work on, which the coordinator will continue to spawn + // work for. + // + // Each LLVM module is automatically sent back to the coordinator for LTO if + // necessary. There's already optimizations in place to avoid sending work + // back to the coordinator if LTO isn't requested. + return thread::spawn(move || { + // We pretend to be within the top-level LLVM time-passes task here: + set_time_depth(1); + + let max_workers = ::num_cpus::get(); + let mut worker_id_counter = 0; + let mut free_worker_ids = Vec::new(); + let mut get_worker_id = |free_worker_ids: &mut Vec| { + if let Some(id) = free_worker_ids.pop() { + id + } else { + let id = worker_id_counter; + worker_id_counter += 1; + id + } + }; + + // This is where we collect codegen units that have gone all the way + // through codegen and LLVM. + let mut compiled_modules = vec![]; + let mut compiled_metadata_module = None; + let mut compiled_allocator_module = None; + let mut needs_lto = Vec::new(); + let mut started_lto = false; + + // This flag tracks whether all items have gone through codegens + let mut codegen_done = false; + + // This is the queue of LLVM work items that still need processing. + let mut work_items = Vec::<(WorkItem, u64)>::new(); + + // This are the Jobserver Tokens we currently hold. Does not include + // the implicit Token the compiler process owns no matter what. + let mut tokens = Vec::new(); + + let mut main_thread_worker_state = MainThreadWorkerState::Idle; + let mut running = 0; + + let mut llvm_start_time = None; + + // Run the message loop while there's still anything that needs message + // processing: + while !codegen_done || + work_items.len() > 0 || + running > 0 || + needs_lto.len() > 0 || + main_thread_worker_state != MainThreadWorkerState::Idle { + + // While there are still CGUs to be codegened, the coordinator has + // to decide how to utilize the compiler processes implicit Token: + // For codegenning more CGU or for running them through LLVM. + if !codegen_done { + if main_thread_worker_state == MainThreadWorkerState::Idle { + if !queue_full_enough(work_items.len(), running, max_workers) { + // The queue is not full enough, codegen more items: + if let Err(_) = codegen_worker_send.send(Message::CodegenItem) { + panic!("Could not send Message::CodegenItem to main thread") + } + main_thread_worker_state = MainThreadWorkerState::Codegenning; + } else { + // The queue is full enough to not let the worker + // threads starve. Use the implicit Token to do some + // LLVM work too. + let (item, _) = work_items.pop() + .expect("queue empty - queue_full_enough() broken?"); + let cgcx = CodegenContext { + worker: get_worker_id(&mut free_worker_ids), + .. cgcx.clone() + }; + maybe_start_llvm_timer(cgcx.config(item.kind()), + &mut llvm_start_time); + main_thread_worker_state = MainThreadWorkerState::LLVMing; + spawn_work(cgcx, item); + } + } + } else { + // If we've finished everything related to normal codegen + // then it must be the case that we've got some LTO work to do. + // Perform the serial work here of figuring out what we're + // going to LTO and then push a bunch of work items onto our + // queue to do LTO + if work_items.len() == 0 && + running == 0 && + main_thread_worker_state == MainThreadWorkerState::Idle { + assert!(!started_lto); + assert!(needs_lto.len() > 0); + started_lto = true; + let modules = mem::replace(&mut needs_lto, Vec::new()); + for (work, cost) in generate_lto_work(&cgcx, modules) { + let insertion_index = work_items + .binary_search_by_key(&cost, |&(_, cost)| cost) + .unwrap_or_else(|e| e); + work_items.insert(insertion_index, (work, cost)); + helper.request_token(); + } + } + + // In this branch, we know that everything has been codegened, + // so it's just a matter of determining whether the implicit + // Token is free to use for LLVM work. + match main_thread_worker_state { + MainThreadWorkerState::Idle => { + if let Some((item, _)) = work_items.pop() { + let cgcx = CodegenContext { + worker: get_worker_id(&mut free_worker_ids), + .. cgcx.clone() + }; + maybe_start_llvm_timer(cgcx.config(item.kind()), + &mut llvm_start_time); + main_thread_worker_state = MainThreadWorkerState::LLVMing; + spawn_work(cgcx, item); + } else { + // There is no unstarted work, so let the main thread + // take over for a running worker. Otherwise the + // implicit token would just go to waste. + // We reduce the `running` counter by one. The + // `tokens.truncate()` below will take care of + // giving the Token back. + debug_assert!(running > 0); + running -= 1; + main_thread_worker_state = MainThreadWorkerState::LLVMing; + } + } + MainThreadWorkerState::Codegenning => { + bug!("codegen worker should not be codegenning after \ + codegen was already completed") + } + MainThreadWorkerState::LLVMing => { + // Already making good use of that token + } + } + } + + // Spin up what work we can, only doing this while we've got available + // parallelism slots and work left to spawn. + while work_items.len() > 0 && running < tokens.len() { + let (item, _) = work_items.pop().unwrap(); + + maybe_start_llvm_timer(cgcx.config(item.kind()), + &mut llvm_start_time); + + let cgcx = CodegenContext { + worker: get_worker_id(&mut free_worker_ids), + .. cgcx.clone() + }; + + spawn_work(cgcx, item); + running += 1; + } + + // Relinquish accidentally acquired extra tokens + tokens.truncate(running); + + let msg = coordinator_receive.recv().unwrap(); + match *msg.downcast::().ok().unwrap() { + // Save the token locally and the next turn of the loop will use + // this to spawn a new unit of work, or it may get dropped + // immediately if we have no more work to spawn. + Message::Token(token) => { + match token { + Ok(token) => { + tokens.push(token); + + if main_thread_worker_state == MainThreadWorkerState::LLVMing { + // If the main thread token is used for LLVM work + // at the moment, we turn that thread into a regular + // LLVM worker thread, so the main thread is free + // to react to codegen demand. + main_thread_worker_state = MainThreadWorkerState::Idle; + running += 1; + } + } + Err(e) => { + let msg = &format!("failed to acquire jobserver token: {}", e); + shared_emitter.fatal(msg); + // Exit the coordinator thread + panic!("{}", msg) + } + } + } + + Message::CodegenDone { llvm_work_item, cost } => { + // We keep the queue sorted by estimated processing cost, + // so that more expensive items are processed earlier. This + // is good for throughput as it gives the main thread more + // time to fill up the queue and it avoids scheduling + // expensive items to the end. + // Note, however, that this is not ideal for memory + // consumption, as LLVM module sizes are not evenly + // distributed. + let insertion_index = + work_items.binary_search_by_key(&cost, |&(_, cost)| cost); + let insertion_index = match insertion_index { + Ok(idx) | Err(idx) => idx + }; + work_items.insert(insertion_index, (llvm_work_item, cost)); + + helper.request_token(); + assert_eq!(main_thread_worker_state, + MainThreadWorkerState::Codegenning); + main_thread_worker_state = MainThreadWorkerState::Idle; + } + + Message::CodegenComplete => { + codegen_done = true; + assert_eq!(main_thread_worker_state, + MainThreadWorkerState::Codegenning); + main_thread_worker_state = MainThreadWorkerState::Idle; + } + + // If a thread exits successfully then we drop a token associated + // with that worker and update our `running` count. We may later + // re-acquire a token to continue running more work. We may also not + // actually drop a token here if the worker was running with an + // "ephemeral token" + // + // Note that if the thread failed that means it panicked, so we + // abort immediately. + Message::Done { result: Ok(compiled_module), worker_id } => { + if main_thread_worker_state == MainThreadWorkerState::LLVMing { + main_thread_worker_state = MainThreadWorkerState::Idle; + } else { + running -= 1; + } + + free_worker_ids.push(worker_id); + + match compiled_module.kind { + ModuleKind::Regular => { + compiled_modules.push(compiled_module); + } + ModuleKind::Metadata => { + assert!(compiled_metadata_module.is_none()); + compiled_metadata_module = Some(compiled_module); + } + ModuleKind::Allocator => { + assert!(compiled_allocator_module.is_none()); + compiled_allocator_module = Some(compiled_module); + } + } + } + Message::NeedsLTO { result, worker_id } => { + assert!(!started_lto); + if main_thread_worker_state == MainThreadWorkerState::LLVMing { + main_thread_worker_state = MainThreadWorkerState::Idle; + } else { + running -= 1; + } + + free_worker_ids.push(worker_id); + needs_lto.push(result); + } + Message::Done { result: Err(()), worker_id: _ } => { + shared_emitter.fatal("aborting due to worker thread failure"); + // Exit the coordinator thread + return Err(()) + } + Message::CodegenItem => { + bug!("the coordinator should not receive codegen requests") + } + } + } + + if let Some(llvm_start_time) = llvm_start_time { + let total_llvm_time = Instant::now().duration_since(llvm_start_time); + // This is the top-level timing for all of LLVM, set the time-depth + // to zero. + set_time_depth(0); + print_time_passes_entry(cgcx.time_passes, + "LLVM passes", + total_llvm_time); + } + + // Regardless of what order these modules completed in, report them to + // the backend in the same order every time to ensure that we're handing + // out deterministic results. + compiled_modules.sort_by(|a, b| a.name.cmp(&b.name)); + + let compiled_metadata_module = compiled_metadata_module + .expect("Metadata module not compiled?"); + + Ok(CompiledModules { + modules: compiled_modules, + metadata_module: compiled_metadata_module, + allocator_module: compiled_allocator_module, + }) + }); + + // A heuristic that determines if we have enough LLVM WorkItems in the + // queue so that the main thread can do LLVM work instead of codegen + fn queue_full_enough(items_in_queue: usize, + workers_running: usize, + max_workers: usize) -> bool { + // Tune me, plz. + items_in_queue > 0 && + items_in_queue >= max_workers.saturating_sub(workers_running / 2) + } + + fn maybe_start_llvm_timer(config: &ModuleConfig, + llvm_start_time: &mut Option) { + // We keep track of the -Ztime-passes output manually, + // since the closure-based interface does not fit well here. + if config.time_passes { + if llvm_start_time.is_none() { + *llvm_start_time = Some(Instant::now()); + } + } + } +} + +pub const CODEGEN_WORKER_ID: usize = ::std::usize::MAX; +pub const CODEGEN_WORKER_TIMELINE: time_graph::TimelineId = + time_graph::TimelineId(CODEGEN_WORKER_ID); +pub const CODEGEN_WORK_PACKAGE_KIND: time_graph::WorkPackageKind = + time_graph::WorkPackageKind(&["#DE9597", "#FED1D3", "#FDC5C7", "#B46668", "#88494B"]); +const LLVM_WORK_PACKAGE_KIND: time_graph::WorkPackageKind = + time_graph::WorkPackageKind(&["#7DB67A", "#C6EEC4", "#ACDAAA", "#579354", "#3E6F3C"]); + +fn spawn_work(cgcx: CodegenContext, work: WorkItem) { + let depth = time_depth(); + + thread::spawn(move || { + set_time_depth(depth); + + // Set up a destructor which will fire off a message that we're done as + // we exit. + struct Bomb { + coordinator_send: Sender>, + result: Option, + worker_id: usize, + } + impl Drop for Bomb { + fn drop(&mut self) { + let worker_id = self.worker_id; + let msg = match self.result.take() { + Some(WorkItemResult::Compiled(m)) => { + Message::Done { result: Ok(m), worker_id } + } + Some(WorkItemResult::NeedsLTO(m)) => { + Message::NeedsLTO { result: m, worker_id } + } + None => Message::Done { result: Err(()), worker_id } + }; + drop(self.coordinator_send.send(Box::new(msg))); + } + } + + let mut bomb = Bomb { + coordinator_send: cgcx.coordinator_send.clone(), + result: None, + worker_id: cgcx.worker, + }; + + // Execute the work itself, and if it finishes successfully then flag + // ourselves as a success as well. + // + // Note that we ignore any `FatalError` coming out of `execute_work_item`, + // as a diagnostic was already sent off to the main thread - just + // surface that there was an error in this worker. + bomb.result = { + let timeline = cgcx.time_graph.as_ref().map(|tg| { + tg.start(time_graph::TimelineId(cgcx.worker), + LLVM_WORK_PACKAGE_KIND, + &work.name()) + }); + let mut timeline = timeline.unwrap_or(Timeline::noop()); + execute_work_item(&cgcx, work, &mut timeline).ok() + }; + }); +} + +pub fn run_assembler(cgcx: &CodegenContext, handler: &Handler, assembly: &Path, object: &Path) { + let assembler = cgcx.assembler_cmd + .as_ref() + .expect("cgcx.assembler_cmd is missing?"); + + let pname = &assembler.name; + let mut cmd = assembler.cmd.clone(); + cmd.arg("-c").arg("-o").arg(object).arg(assembly); + debug!("{:?}", cmd); + + match cmd.output() { + Ok(prog) => { + if !prog.status.success() { + let mut note = prog.stderr.clone(); + note.extend_from_slice(&prog.stdout); + + handler.struct_err(&format!("linking with `{}` failed: {}", + pname.display(), + prog.status)) + .note(&format!("{:?}", &cmd)) + .note(str::from_utf8(¬e[..]).unwrap()) + .emit(); + handler.abort_if_errors(); + } + }, + Err(e) => { + handler.err(&format!("could not exec the linker `{}`: {}", pname.display(), e)); + handler.abort_if_errors(); + } + } +} + +pub unsafe fn with_llvm_pmb(llmod: ModuleRef, + config: &ModuleConfig, + opt_level: llvm::CodeGenOptLevel, + prepare_for_thin_lto: bool, + f: &mut FnMut(llvm::PassManagerBuilderRef)) { + use std::ptr; + + // Create the PassManagerBuilder for LLVM. We configure it with + // reasonable defaults and prepare it to actually populate the pass + // manager. + let builder = llvm::LLVMPassManagerBuilderCreate(); + let opt_size = config.opt_size.unwrap_or(llvm::CodeGenOptSizeNone); + let inline_threshold = config.inline_threshold; + + let pgo_gen_path = config.pgo_gen.as_ref().map(|s| { + let s = if s.is_empty() { "default_%m.profraw" } else { s }; + CString::new(s.as_bytes()).unwrap() + }); + + let pgo_use_path = if config.pgo_use.is_empty() { + None + } else { + Some(CString::new(config.pgo_use.as_bytes()).unwrap()) + }; + + llvm::LLVMRustConfigurePassManagerBuilder( + builder, + opt_level, + config.merge_functions, + config.vectorize_slp, + config.vectorize_loop, + prepare_for_thin_lto, + pgo_gen_path.as_ref().map_or(ptr::null(), |s| s.as_ptr()), + pgo_use_path.as_ref().map_or(ptr::null(), |s| s.as_ptr()), + ); + + llvm::LLVMPassManagerBuilderSetSizeLevel(builder, opt_size as u32); + + if opt_size != llvm::CodeGenOptSizeNone { + llvm::LLVMPassManagerBuilderSetDisableUnrollLoops(builder, 1); + } + + llvm::LLVMRustAddBuilderLibraryInfo(builder, llmod, config.no_builtins); + + // Here we match what clang does (kinda). For O0 we only inline + // always-inline functions (but don't add lifetime intrinsics), at O1 we + // inline with lifetime intrinsics, and O2+ we add an inliner with a + // thresholds copied from clang. + match (opt_level, opt_size, inline_threshold) { + (.., Some(t)) => { + llvm::LLVMPassManagerBuilderUseInlinerWithThreshold(builder, t as u32); + } + (llvm::CodeGenOptLevel::Aggressive, ..) => { + llvm::LLVMPassManagerBuilderUseInlinerWithThreshold(builder, 275); + } + (_, llvm::CodeGenOptSizeDefault, _) => { + llvm::LLVMPassManagerBuilderUseInlinerWithThreshold(builder, 75); + } + (_, llvm::CodeGenOptSizeAggressive, _) => { + llvm::LLVMPassManagerBuilderUseInlinerWithThreshold(builder, 25); + } + (llvm::CodeGenOptLevel::None, ..) => { + llvm::LLVMRustAddAlwaysInlinePass(builder, false); + } + (llvm::CodeGenOptLevel::Less, ..) => { + llvm::LLVMRustAddAlwaysInlinePass(builder, true); + } + (llvm::CodeGenOptLevel::Default, ..) => { + llvm::LLVMPassManagerBuilderUseInlinerWithThreshold(builder, 225); + } + (llvm::CodeGenOptLevel::Other, ..) => { + bug!("CodeGenOptLevel::Other selected") + } + } + + f(builder); + llvm::LLVMPassManagerBuilderDispose(builder); +} + + +enum SharedEmitterMessage { + Diagnostic(Diagnostic), + InlineAsmError(u32, String), + AbortIfErrors, + Fatal(String), +} + +#[derive(Clone)] +pub struct SharedEmitter { + sender: Sender, +} + +pub struct SharedEmitterMain { + receiver: Receiver, +} + +impl SharedEmitter { + pub fn new() -> (SharedEmitter, SharedEmitterMain) { + let (sender, receiver) = channel(); + + (SharedEmitter { sender }, SharedEmitterMain { receiver }) + } + + fn inline_asm_error(&self, cookie: u32, msg: String) { + drop(self.sender.send(SharedEmitterMessage::InlineAsmError(cookie, msg))); + } + + fn fatal(&self, msg: &str) { + drop(self.sender.send(SharedEmitterMessage::Fatal(msg.to_string()))); + } +} + +impl Emitter for SharedEmitter { + fn emit(&mut self, db: &DiagnosticBuilder) { + drop(self.sender.send(SharedEmitterMessage::Diagnostic(Diagnostic { + msg: db.message(), + code: db.code.clone(), + lvl: db.level, + }))); + for child in &db.children { + drop(self.sender.send(SharedEmitterMessage::Diagnostic(Diagnostic { + msg: child.message(), + code: None, + lvl: child.level, + }))); + } + drop(self.sender.send(SharedEmitterMessage::AbortIfErrors)); + } +} + +impl SharedEmitterMain { + pub fn check(&self, sess: &Session, blocking: bool) { + loop { + let message = if blocking { + match self.receiver.recv() { + Ok(message) => Ok(message), + Err(_) => Err(()), + } + } else { + match self.receiver.try_recv() { + Ok(message) => Ok(message), + Err(_) => Err(()), + } + }; + + match message { + Ok(SharedEmitterMessage::Diagnostic(diag)) => { + let handler = sess.diagnostic(); + match diag.code { + Some(ref code) => { + handler.emit_with_code(&MultiSpan::new(), + &diag.msg, + code.clone(), + diag.lvl); + } + None => { + handler.emit(&MultiSpan::new(), + &diag.msg, + diag.lvl); + } + } + } + Ok(SharedEmitterMessage::InlineAsmError(cookie, msg)) => { + match Mark::from_u32(cookie).expn_info() { + Some(ei) => sess.span_err(ei.call_site, &msg), + None => sess.err(&msg), + } + } + Ok(SharedEmitterMessage::AbortIfErrors) => { + sess.abort_if_errors(); + } + Ok(SharedEmitterMessage::Fatal(msg)) => { + sess.fatal(&msg); + } + Err(_) => { + break; + } + } + + } + } +} + +pub struct OngoingCodegen { + crate_name: Symbol, + link: LinkMeta, + metadata: EncodedMetadata, + windows_subsystem: Option, + linker_info: LinkerInfo, + crate_info: CrateInfo, + time_graph: Option, + coordinator_send: Sender>, + codegen_worker_receive: Receiver, + shared_emitter_main: SharedEmitterMain, + future: thread::JoinHandle>, + output_filenames: Arc, +} + +impl OngoingCodegen { + pub(crate) fn join( + self, + sess: &Session + ) -> (CodegenResults, FxHashMap) { + self.shared_emitter_main.check(sess, true); + let compiled_modules = match self.future.join() { + Ok(Ok(compiled_modules)) => compiled_modules, + Ok(Err(())) => { + sess.abort_if_errors(); + panic!("expected abort due to worker thread errors") + }, + Err(_) => { + sess.fatal("Error during codegen/LLVM phase."); + } + }; + + sess.abort_if_errors(); + + if let Some(time_graph) = self.time_graph { + time_graph.dump(&format!("{}-timings", self.crate_name)); + } + + let work_products = copy_all_cgu_workproducts_to_incr_comp_cache_dir(sess, + &compiled_modules); + + produce_final_output_artifacts(sess, + &compiled_modules, + &self.output_filenames); + + // FIXME: time_llvm_passes support - does this use a global context or + // something? + if sess.codegen_units() == 1 && sess.time_llvm_passes() { + unsafe { llvm::LLVMRustPrintPassTimings(); } + } + + (CodegenResults { + crate_name: self.crate_name, + link: self.link, + metadata: self.metadata, + windows_subsystem: self.windows_subsystem, + linker_info: self.linker_info, + crate_info: self.crate_info, + + modules: compiled_modules.modules, + allocator_module: compiled_modules.allocator_module, + metadata_module: compiled_modules.metadata_module, + }, work_products) + } + + pub(crate) fn submit_pre_codegened_module_to_llvm(&self, + tcx: TyCtxt, + module: ModuleCodegen) { + self.wait_for_signal_to_codegen_item(); + self.check_for_errors(tcx.sess); + + // These are generally cheap and won't through off scheduling. + let cost = 0; + submit_codegened_module_to_llvm(tcx, module, cost); + } + + pub fn codegen_finished(&self, tcx: TyCtxt) { + self.wait_for_signal_to_codegen_item(); + self.check_for_errors(tcx.sess); + drop(self.coordinator_send.send(Box::new(Message::CodegenComplete))); + } + + pub fn check_for_errors(&self, sess: &Session) { + self.shared_emitter_main.check(sess, false); + } + + pub fn wait_for_signal_to_codegen_item(&self) { + match self.codegen_worker_receive.recv() { + Ok(Message::CodegenItem) => { + // Nothing to do + } + Ok(_) => panic!("unexpected message"), + Err(_) => { + // One of the LLVM threads must have panicked, fall through so + // error handling can be reached. + } + } + } +} + +pub(crate) fn submit_codegened_module_to_llvm(tcx: TyCtxt, + module: ModuleCodegen, + cost: u64) { + let llvm_work_item = WorkItem::Optimize(module); + drop(tcx.tx_to_llvm_workers.lock().send(Box::new(Message::CodegenDone { + llvm_work_item, + cost, + }))); +} + +fn msvc_imps_needed(tcx: TyCtxt) -> bool { + tcx.sess.target.target.options.is_like_msvc && + tcx.sess.crate_types.borrow().iter().any(|ct| *ct == config::CrateTypeRlib) +} + +// Create a `__imp_ = &symbol` global for every public static `symbol`. +// This is required to satisfy `dllimport` references to static data in .rlibs +// when using MSVC linker. We do this only for data, as linker can fix up +// code references on its own. +// See #26591, #27438 +fn create_msvc_imps(cgcx: &CodegenContext, llcx: ContextRef, llmod: ModuleRef) { + if !cgcx.msvc_imps_needed { + return + } + // The x86 ABI seems to require that leading underscores are added to symbol + // names, so we need an extra underscore on 32-bit. There's also a leading + // '\x01' here which disables LLVM's symbol mangling (e.g. no extra + // underscores added in front). + let prefix = if cgcx.target_pointer_width == "32" { + "\x01__imp__" + } else { + "\x01__imp_" + }; + unsafe { + let i8p_ty = Type::i8p_llcx(llcx); + let globals = base::iter_globals(llmod) + .filter(|&val| { + llvm::LLVMRustGetLinkage(val) == llvm::Linkage::ExternalLinkage && + llvm::LLVMIsDeclaration(val) == 0 + }) + .map(move |val| { + let name = CStr::from_ptr(llvm::LLVMGetValueName(val)); + let mut imp_name = prefix.as_bytes().to_vec(); + imp_name.extend(name.to_bytes()); + let imp_name = CString::new(imp_name).unwrap(); + (imp_name, val) + }) + .collect::>(); + for (imp_name, val) in globals { + let imp = llvm::LLVMAddGlobal(llmod, + i8p_ty.to_ref(), + imp_name.as_ptr() as *const _); + llvm::LLVMSetInitializer(imp, consts::ptrcast(val, i8p_ty)); + llvm::LLVMRustSetLinkage(imp, llvm::Linkage::ExternalLinkage); + } + } +} diff --git a/src/librustc_codegen_llvm/base.rs b/src/librustc_codegen_llvm/base.rs new file mode 100644 index 00000000000..d04cb72d52d --- /dev/null +++ b/src/librustc_codegen_llvm/base.rs @@ -0,0 +1,1411 @@ +// Copyright 2012-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. + +//! Codegen the completed AST to the LLVM IR. +//! +//! Some functions here, such as codegen_block and codegen_expr, return a value -- +//! the result of the codegen to LLVM -- while others, such as codegen_fn +//! and mono_item, are called only for the side effect of adding a +//! particular definition to the LLVM IR output we're producing. +//! +//! Hopefully useful general knowledge about codegen: +//! +//! * There's no way to find out the Ty type of a ValueRef. Doing so +//! would be "trying to get the eggs out of an omelette" (credit: +//! pcwalton). You can, instead, find out its TypeRef by calling val_ty, +//! but one TypeRef corresponds to many `Ty`s; for instance, tup(int, int, +//! int) and rec(x=int, y=int, z=int) will have the same TypeRef. + +use super::ModuleLlvm; +use super::ModuleSource; +use super::ModuleCodegen; +use super::ModuleKind; + +use abi; +use back::link; +use back::write::{self, OngoingCodegen, create_target_machine}; +use llvm::{ContextRef, ModuleRef, ValueRef, Vector, get_param}; +use llvm; +use metadata; +use rustc::hir::def_id::{CrateNum, DefId, LOCAL_CRATE}; +use rustc::middle::lang_items::StartFnLangItem; +use rustc::middle::weak_lang_items; +use rustc::mir::mono::{Linkage, Visibility, Stats}; +use rustc::middle::cstore::{EncodedMetadata}; +use rustc::ty::{self, Ty, TyCtxt}; +use rustc::ty::layout::{self, Align, TyLayout, LayoutOf}; +use rustc::ty::maps::Providers; +use rustc::dep_graph::{DepNode, DepConstructor}; +use rustc::ty::subst::Kind; +use rustc::middle::cstore::{self, LinkMeta, LinkagePreference}; +use rustc::middle::exported_symbols; +use rustc::util::common::{time, print_time_passes_entry}; +use rustc::session::config::{self, NoDebugInfo}; +use rustc::session::Session; +use rustc_incremental; +use allocator; +use mir::place::PlaceRef; +use attributes; +use builder::{Builder, MemFlags}; +use callee; +use common::{C_bool, C_bytes_in_context, C_i32, C_usize}; +use rustc_mir::monomorphize::collector::{self, MonoItemCollectionMode}; +use common::{self, C_struct_in_context, C_array, val_ty}; +use consts; +use context::{self, CodegenCx}; +use debuginfo; +use declare; +use meth; +use mir; +use monomorphize::Instance; +use monomorphize::partitioning::{self, PartitioningStrategy, CodegenUnit, CodegenUnitExt}; +use rustc_codegen_utils::symbol_names_test; +use time_graph; +use mono_item::{MonoItem, BaseMonoItemExt, MonoItemExt, DefPathBasedNames}; +use type_::Type; +use type_of::LayoutLlvmExt; +use rustc::util::nodemap::{FxHashMap, FxHashSet, DefIdSet}; +use CrateInfo; +use rustc_data_structures::sync::Lrc; +use rustc_target::spec::TargetTriple; + +use std::any::Any; +use std::collections::BTreeMap; +use std::ffi::CString; +use std::str; +use std::sync::Arc; +use std::time::{Instant, Duration}; +use std::i32; +use std::cmp; +use std::sync::mpsc; +use syntax_pos::Span; +use syntax_pos::symbol::InternedString; +use syntax::attr; +use rustc::hir; +use syntax::ast; + +use mir::operand::OperandValue; + +pub use rustc_codegen_utils::check_for_rustc_errors_attr; + +pub struct StatRecorder<'a, 'tcx: 'a> { + cx: &'a CodegenCx<'a, 'tcx>, + name: Option, + istart: usize, +} + +impl<'a, 'tcx> StatRecorder<'a, 'tcx> { + pub fn new(cx: &'a CodegenCx<'a, 'tcx>, name: String) -> StatRecorder<'a, 'tcx> { + let istart = cx.stats.borrow().n_llvm_insns; + StatRecorder { + cx, + name: Some(name), + istart, + } + } +} + +impl<'a, 'tcx> Drop for StatRecorder<'a, 'tcx> { + fn drop(&mut self) { + if self.cx.sess().codegen_stats() { + let mut stats = self.cx.stats.borrow_mut(); + let iend = stats.n_llvm_insns; + stats.fn_stats.push((self.name.take().unwrap(), iend - self.istart)); + stats.n_fns += 1; + // Reset LLVM insn count to avoid compound costs. + stats.n_llvm_insns = self.istart; + } + } +} + +pub fn bin_op_to_icmp_predicate(op: hir::BinOp_, + signed: bool) + -> llvm::IntPredicate { + match op { + hir::BiEq => llvm::IntEQ, + hir::BiNe => llvm::IntNE, + hir::BiLt => if signed { llvm::IntSLT } else { llvm::IntULT }, + hir::BiLe => if signed { llvm::IntSLE } else { llvm::IntULE }, + hir::BiGt => if signed { llvm::IntSGT } else { llvm::IntUGT }, + hir::BiGe => if signed { llvm::IntSGE } else { llvm::IntUGE }, + op => { + bug!("comparison_op_to_icmp_predicate: expected comparison operator, \ + found {:?}", + op) + } + } +} + +pub fn bin_op_to_fcmp_predicate(op: hir::BinOp_) -> llvm::RealPredicate { + match op { + hir::BiEq => llvm::RealOEQ, + hir::BiNe => llvm::RealUNE, + hir::BiLt => llvm::RealOLT, + hir::BiLe => llvm::RealOLE, + hir::BiGt => llvm::RealOGT, + hir::BiGe => llvm::RealOGE, + op => { + bug!("comparison_op_to_fcmp_predicate: expected comparison operator, \ + found {:?}", + op); + } + } +} + +pub fn compare_simd_types<'a, 'tcx>( + bx: &Builder<'a, 'tcx>, + lhs: ValueRef, + rhs: ValueRef, + t: Ty<'tcx>, + ret_ty: Type, + op: hir::BinOp_ +) -> ValueRef { + let signed = match t.sty { + ty::TyFloat(_) => { + let cmp = bin_op_to_fcmp_predicate(op); + return bx.sext(bx.fcmp(cmp, lhs, rhs), ret_ty); + }, + ty::TyUint(_) => false, + ty::TyInt(_) => true, + _ => bug!("compare_simd_types: invalid SIMD type"), + }; + + let cmp = bin_op_to_icmp_predicate(op, signed); + // LLVM outputs an `< size x i1 >`, so we need to perform a sign extension + // to get the correctly sized type. This will compile to a single instruction + // once the IR is converted to assembly if the SIMD instruction is supported + // by the target architecture. + bx.sext(bx.icmp(cmp, lhs, rhs), ret_ty) +} + +/// Retrieve the information we are losing (making dynamic) in an unsizing +/// adjustment. +/// +/// The `old_info` argument is a bit funny. It is intended for use +/// in an upcast, where the new vtable for an object will be derived +/// from the old one. +pub fn unsized_info<'cx, 'tcx>(cx: &CodegenCx<'cx, 'tcx>, + source: Ty<'tcx>, + target: Ty<'tcx>, + old_info: Option) + -> ValueRef { + let (source, target) = cx.tcx.struct_lockstep_tails(source, target); + match (&source.sty, &target.sty) { + (&ty::TyArray(_, len), &ty::TySlice(_)) => { + C_usize(cx, len.unwrap_usize(cx.tcx)) + } + (&ty::TyDynamic(..), &ty::TyDynamic(..)) => { + // For now, upcasts are limited to changes in marker + // traits, and hence never actually require an actual + // change to the vtable. + old_info.expect("unsized_info: missing old info for trait upcast") + } + (_, &ty::TyDynamic(ref data, ..)) => { + let vtable_ptr = cx.layout_of(cx.tcx.mk_mut_ptr(target)) + .field(cx, abi::FAT_PTR_EXTRA); + consts::ptrcast(meth::get_vtable(cx, source, data.principal()), + vtable_ptr.llvm_type(cx)) + } + _ => bug!("unsized_info: invalid unsizing {:?} -> {:?}", + source, + target), + } +} + +/// Coerce `src` to `dst_ty`. `src_ty` must be a thin pointer. +pub fn unsize_thin_ptr<'a, 'tcx>( + bx: &Builder<'a, 'tcx>, + src: ValueRef, + src_ty: Ty<'tcx>, + dst_ty: Ty<'tcx> +) -> (ValueRef, ValueRef) { + debug!("unsize_thin_ptr: {:?} => {:?}", src_ty, dst_ty); + match (&src_ty.sty, &dst_ty.sty) { + (&ty::TyRef(_, a, _), + &ty::TyRef(_, b, _)) | + (&ty::TyRef(_, a, _), + &ty::TyRawPtr(ty::TypeAndMut { ty: b, .. })) | + (&ty::TyRawPtr(ty::TypeAndMut { ty: a, .. }), + &ty::TyRawPtr(ty::TypeAndMut { ty: b, .. })) => { + assert!(bx.cx.type_is_sized(a)); + let ptr_ty = bx.cx.layout_of(b).llvm_type(bx.cx).ptr_to(); + (bx.pointercast(src, ptr_ty), unsized_info(bx.cx, a, b, None)) + } + (&ty::TyAdt(def_a, _), &ty::TyAdt(def_b, _)) if def_a.is_box() && def_b.is_box() => { + let (a, b) = (src_ty.boxed_ty(), dst_ty.boxed_ty()); + assert!(bx.cx.type_is_sized(a)); + let ptr_ty = bx.cx.layout_of(b).llvm_type(bx.cx).ptr_to(); + (bx.pointercast(src, ptr_ty), unsized_info(bx.cx, a, b, None)) + } + (&ty::TyAdt(def_a, _), &ty::TyAdt(def_b, _)) => { + assert_eq!(def_a, def_b); + + let src_layout = bx.cx.layout_of(src_ty); + let dst_layout = bx.cx.layout_of(dst_ty); + let mut result = None; + for i in 0..src_layout.fields.count() { + let src_f = src_layout.field(bx.cx, i); + assert_eq!(src_layout.fields.offset(i).bytes(), 0); + assert_eq!(dst_layout.fields.offset(i).bytes(), 0); + if src_f.is_zst() { + continue; + } + assert_eq!(src_layout.size, src_f.size); + + let dst_f = dst_layout.field(bx.cx, i); + assert_ne!(src_f.ty, dst_f.ty); + assert_eq!(result, None); + result = Some(unsize_thin_ptr(bx, src, src_f.ty, dst_f.ty)); + } + let (lldata, llextra) = result.unwrap(); + // HACK(eddyb) have to bitcast pointers until LLVM removes pointee types. + (bx.bitcast(lldata, dst_layout.scalar_pair_element_llvm_type(bx.cx, 0)), + bx.bitcast(llextra, dst_layout.scalar_pair_element_llvm_type(bx.cx, 1))) + } + _ => bug!("unsize_thin_ptr: called on bad types"), + } +} + +/// Coerce `src`, which is a reference to a value of type `src_ty`, +/// to a value of type `dst_ty` and store the result in `dst` +pub fn coerce_unsized_into<'a, 'tcx>(bx: &Builder<'a, 'tcx>, + src: PlaceRef<'tcx>, + dst: PlaceRef<'tcx>) { + let src_ty = src.layout.ty; + let dst_ty = dst.layout.ty; + let coerce_ptr = || { + let (base, info) = match src.load(bx).val { + OperandValue::Pair(base, info) => { + // fat-ptr to fat-ptr unsize preserves the vtable + // i.e. &'a fmt::Debug+Send => &'a fmt::Debug + // So we need to pointercast the base to ensure + // the types match up. + let thin_ptr = dst.layout.field(bx.cx, abi::FAT_PTR_ADDR); + (bx.pointercast(base, thin_ptr.llvm_type(bx.cx)), info) + } + OperandValue::Immediate(base) => { + unsize_thin_ptr(bx, base, src_ty, dst_ty) + } + OperandValue::Ref(..) => bug!() + }; + OperandValue::Pair(base, info).store(bx, dst); + }; + match (&src_ty.sty, &dst_ty.sty) { + (&ty::TyRef(..), &ty::TyRef(..)) | + (&ty::TyRef(..), &ty::TyRawPtr(..)) | + (&ty::TyRawPtr(..), &ty::TyRawPtr(..)) => { + coerce_ptr() + } + (&ty::TyAdt(def_a, _), &ty::TyAdt(def_b, _)) if def_a.is_box() && def_b.is_box() => { + coerce_ptr() + } + + (&ty::TyAdt(def_a, _), &ty::TyAdt(def_b, _)) => { + assert_eq!(def_a, def_b); + + for i in 0..def_a.variants[0].fields.len() { + let src_f = src.project_field(bx, i); + let dst_f = dst.project_field(bx, i); + + if dst_f.layout.is_zst() { + continue; + } + + if src_f.layout.ty == dst_f.layout.ty { + memcpy_ty(bx, dst_f.llval, src_f.llval, src_f.layout, + src_f.align.min(dst_f.align), MemFlags::empty()); + } else { + coerce_unsized_into(bx, src_f, dst_f); + } + } + } + _ => bug!("coerce_unsized_into: invalid coercion {:?} -> {:?}", + src_ty, + dst_ty), + } +} + +pub fn cast_shift_expr_rhs( + cx: &Builder, op: hir::BinOp_, lhs: ValueRef, rhs: ValueRef +) -> ValueRef { + cast_shift_rhs(op, lhs, rhs, |a, b| cx.trunc(a, b), |a, b| cx.zext(a, b)) +} + +fn cast_shift_rhs(op: hir::BinOp_, + lhs: ValueRef, + rhs: ValueRef, + trunc: F, + zext: G) + -> ValueRef + where F: FnOnce(ValueRef, Type) -> ValueRef, + G: FnOnce(ValueRef, Type) -> ValueRef +{ + // Shifts may have any size int on the rhs + if op.is_shift() { + let mut rhs_llty = val_ty(rhs); + let mut lhs_llty = val_ty(lhs); + if rhs_llty.kind() == Vector { + rhs_llty = rhs_llty.element_type() + } + if lhs_llty.kind() == Vector { + lhs_llty = lhs_llty.element_type() + } + let rhs_sz = rhs_llty.int_width(); + let lhs_sz = lhs_llty.int_width(); + if lhs_sz < rhs_sz { + trunc(rhs, lhs_llty) + } else if lhs_sz > rhs_sz { + // FIXME (#1877: If shifting by negative + // values becomes not undefined then this is wrong. + zext(rhs, lhs_llty) + } else { + rhs + } + } else { + rhs + } +} + +/// Returns whether this session's target will use SEH-based unwinding. +/// +/// This is only true for MSVC targets, and even then the 64-bit MSVC target +/// currently uses SEH-ish unwinding with DWARF info tables to the side (same as +/// 64-bit MinGW) instead of "full SEH". +pub fn wants_msvc_seh(sess: &Session) -> bool { + sess.target.target.options.is_like_msvc +} + +pub fn call_assume<'a, 'tcx>(bx: &Builder<'a, 'tcx>, val: ValueRef) { + let assume_intrinsic = bx.cx.get_intrinsic("llvm.assume"); + bx.call(assume_intrinsic, &[val], None); +} + +pub fn from_immediate(bx: &Builder, val: ValueRef) -> ValueRef { + if val_ty(val) == Type::i1(bx.cx) { + bx.zext(val, Type::i8(bx.cx)) + } else { + val + } +} + +pub fn to_immediate(bx: &Builder, val: ValueRef, layout: layout::TyLayout) -> ValueRef { + if let layout::Abi::Scalar(ref scalar) = layout.abi { + if scalar.is_bool() { + return bx.trunc(val, Type::i1(bx.cx)); + } + } + val +} + +pub fn call_memcpy(bx: &Builder, + dst: ValueRef, + src: ValueRef, + n_bytes: ValueRef, + align: Align, + flags: MemFlags) { + if flags.contains(MemFlags::NONTEMPORAL) { + // HACK(nox): This is inefficient but there is no nontemporal memcpy. + let val = bx.load(src, align); + let ptr = bx.pointercast(dst, val_ty(val).ptr_to()); + bx.store_with_flags(val, ptr, align, flags); + return; + } + let cx = bx.cx; + let ptr_width = &cx.sess().target.target.target_pointer_width; + let key = format!("llvm.memcpy.p0i8.p0i8.i{}", ptr_width); + let memcpy = cx.get_intrinsic(&key); + let src_ptr = bx.pointercast(src, Type::i8p(cx)); + let dst_ptr = bx.pointercast(dst, Type::i8p(cx)); + let size = bx.intcast(n_bytes, cx.isize_ty, false); + let align = C_i32(cx, align.abi() as i32); + let volatile = C_bool(cx, flags.contains(MemFlags::VOLATILE)); + bx.call(memcpy, &[dst_ptr, src_ptr, size, align, volatile], None); +} + +pub fn memcpy_ty<'a, 'tcx>( + bx: &Builder<'a, 'tcx>, + dst: ValueRef, + src: ValueRef, + layout: TyLayout<'tcx>, + align: Align, + flags: MemFlags, +) { + let size = layout.size.bytes(); + if size == 0 { + return; + } + + call_memcpy(bx, dst, src, C_usize(bx.cx, size), align, flags); +} + +pub fn call_memset<'a, 'tcx>(bx: &Builder<'a, 'tcx>, + ptr: ValueRef, + fill_byte: ValueRef, + size: ValueRef, + align: ValueRef, + volatile: bool) -> ValueRef { + let ptr_width = &bx.cx.sess().target.target.target_pointer_width; + let intrinsic_key = format!("llvm.memset.p0i8.i{}", ptr_width); + let llintrinsicfn = bx.cx.get_intrinsic(&intrinsic_key); + let volatile = C_bool(bx.cx, volatile); + bx.call(llintrinsicfn, &[ptr, fill_byte, size, align, volatile], None) +} + +pub fn codegen_instance<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, instance: Instance<'tcx>) { + let _s = if cx.sess().codegen_stats() { + let mut instance_name = String::new(); + DefPathBasedNames::new(cx.tcx, true, true) + .push_def_path(instance.def_id(), &mut instance_name); + Some(StatRecorder::new(cx, instance_name)) + } else { + None + }; + + // this is an info! to allow collecting monomorphization statistics + // and to allow finding the last function before LLVM aborts from + // release builds. + info!("codegen_instance({})", instance); + + let fn_ty = instance.ty(cx.tcx); + let sig = common::ty_fn_sig(cx, fn_ty); + let sig = cx.tcx.normalize_erasing_late_bound_regions(ty::ParamEnv::reveal_all(), &sig); + + let lldecl = match cx.instances.borrow().get(&instance) { + Some(&val) => val, + None => bug!("Instance `{:?}` not already declared", instance) + }; + + cx.stats.borrow_mut().n_closures += 1; + + // The `uwtable` attribute according to LLVM is: + // + // This attribute indicates that the ABI being targeted requires that an + // unwind table entry be produced for this function even if we can show + // that no exceptions passes by it. This is normally the case for the + // ELF x86-64 abi, but it can be disabled for some compilation units. + // + // Typically when we're compiling with `-C panic=abort` (which implies this + // `no_landing_pads` check) we don't need `uwtable` because we can't + // generate any exceptions! On Windows, however, exceptions include other + // events such as illegal instructions, segfaults, etc. This means that on + // Windows we end up still needing the `uwtable` attribute even if the `-C + // panic=abort` flag is passed. + // + // You can also find more info on why Windows is whitelisted here in: + // https://bugzilla.mozilla.org/show_bug.cgi?id=1302078 + if !cx.sess().no_landing_pads() || + cx.sess().target.target.options.requires_uwtable { + attributes::emit_uwtable(lldecl, true); + } + + let mir = cx.tcx.instance_mir(instance.def); + mir::codegen_mir(cx, lldecl, &mir, instance, sig); +} + +pub fn set_link_section(cx: &CodegenCx, + llval: ValueRef, + attrs: &[ast::Attribute]) { + if let Some(sect) = attr::first_attr_value_str_by_name(attrs, "link_section") { + if contains_null(§.as_str()) { + cx.sess().fatal(&format!("Illegal null byte in link_section value: `{}`", §)); + } + unsafe { + let buf = CString::new(sect.as_str().as_bytes()).unwrap(); + llvm::LLVMSetSection(llval, buf.as_ptr()); + } + } +} + +/// Create the `main` function which will initialize the rust runtime and call +/// users main function. +fn maybe_create_entry_wrapper(cx: &CodegenCx) { + let (main_def_id, span) = match *cx.sess().entry_fn.borrow() { + Some((id, span, _)) => { + (cx.tcx.hir.local_def_id(id), span) + } + None => return, + }; + + let instance = Instance::mono(cx.tcx, main_def_id); + + if !cx.codegen_unit.contains_item(&MonoItem::Fn(instance)) { + // We want to create the wrapper in the same codegen unit as Rust's main + // function. + return; + } + + let main_llfn = callee::get_fn(cx, instance); + + let et = cx.sess().entry_fn.get().map(|e| e.2); + match et { + Some(config::EntryMain) => create_entry_fn(cx, span, main_llfn, main_def_id, true), + Some(config::EntryStart) => create_entry_fn(cx, span, main_llfn, main_def_id, false), + None => {} // Do nothing. + } + + fn create_entry_fn<'cx>(cx: &'cx CodegenCx, + sp: Span, + rust_main: ValueRef, + rust_main_def_id: DefId, + use_start_lang_item: bool) { + let llfty = Type::func(&[Type::c_int(cx), Type::i8p(cx).ptr_to()], &Type::c_int(cx)); + + let main_ret_ty = cx.tcx.fn_sig(rust_main_def_id).output(); + // Given that `main()` has no arguments, + // then its return type cannot have + // late-bound regions, since late-bound + // regions must appear in the argument + // listing. + let main_ret_ty = cx.tcx.erase_regions( + &main_ret_ty.no_late_bound_regions().unwrap(), + ); + + if declare::get_defined_value(cx, "main").is_some() { + // FIXME: We should be smart and show a better diagnostic here. + cx.sess().struct_span_err(sp, "entry symbol `main` defined multiple times") + .help("did you use #[no_mangle] on `fn main`? Use #[start] instead") + .emit(); + cx.sess().abort_if_errors(); + bug!(); + } + let llfn = declare::declare_cfn(cx, "main", llfty); + + // `main` should respect same config for frame pointer elimination as rest of code + attributes::set_frame_pointer_elimination(cx, llfn); + + let bx = Builder::new_block(cx, llfn, "top"); + + debuginfo::gdb::insert_reference_to_gdb_debug_scripts_section_global(&bx); + + // Params from native main() used as args for rust start function + let param_argc = get_param(llfn, 0); + let param_argv = get_param(llfn, 1); + let arg_argc = bx.intcast(param_argc, cx.isize_ty, true); + let arg_argv = param_argv; + + let (start_fn, args) = if use_start_lang_item { + let start_def_id = cx.tcx.require_lang_item(StartFnLangItem); + let start_fn = callee::resolve_and_get_fn( + cx, + start_def_id, + cx.tcx.intern_substs(&[Kind::from(main_ret_ty)]), + ); + (start_fn, vec![bx.pointercast(rust_main, Type::i8p(cx).ptr_to()), + arg_argc, arg_argv]) + } else { + debug!("using user-defined start fn"); + (rust_main, vec![arg_argc, arg_argv]) + }; + + let result = bx.call(start_fn, &args, None); + bx.ret(bx.intcast(result, Type::c_int(cx), true)); + } +} + +fn contains_null(s: &str) -> bool { + s.bytes().any(|b| b == 0) +} + +fn write_metadata<'a, 'gcx>(tcx: TyCtxt<'a, 'gcx, 'gcx>, + llmod_id: &str, + link_meta: &LinkMeta) + -> (ContextRef, ModuleRef, EncodedMetadata) { + use std::io::Write; + use flate2::Compression; + use flate2::write::DeflateEncoder; + + let (metadata_llcx, metadata_llmod) = unsafe { + context::create_context_and_module(tcx.sess, llmod_id) + }; + + #[derive(PartialEq, Eq, PartialOrd, Ord)] + enum MetadataKind { + None, + Uncompressed, + Compressed + } + + let kind = tcx.sess.crate_types.borrow().iter().map(|ty| { + match *ty { + config::CrateTypeExecutable | + config::CrateTypeStaticlib | + config::CrateTypeCdylib => MetadataKind::None, + + config::CrateTypeRlib => MetadataKind::Uncompressed, + + config::CrateTypeDylib | + config::CrateTypeProcMacro => MetadataKind::Compressed, + } + }).max().unwrap(); + + if kind == MetadataKind::None { + return (metadata_llcx, + metadata_llmod, + EncodedMetadata::new()); + } + + let metadata = tcx.encode_metadata(link_meta); + if kind == MetadataKind::Uncompressed { + return (metadata_llcx, metadata_llmod, metadata); + } + + assert!(kind == MetadataKind::Compressed); + let mut compressed = tcx.metadata_encoding_version(); + DeflateEncoder::new(&mut compressed, Compression::fast()) + .write_all(&metadata.raw_data).unwrap(); + + let llmeta = C_bytes_in_context(metadata_llcx, &compressed); + let llconst = C_struct_in_context(metadata_llcx, &[llmeta], false); + let name = exported_symbols::metadata_symbol_name(tcx); + let buf = CString::new(name).unwrap(); + let llglobal = unsafe { + llvm::LLVMAddGlobal(metadata_llmod, val_ty(llconst).to_ref(), buf.as_ptr()) + }; + unsafe { + llvm::LLVMSetInitializer(llglobal, llconst); + let section_name = metadata::metadata_section_name(&tcx.sess.target.target); + let name = CString::new(section_name).unwrap(); + llvm::LLVMSetSection(llglobal, name.as_ptr()); + + // Also generate a .section directive to force no + // flags, at least for ELF outputs, so that the + // metadata doesn't get loaded into memory. + let directive = format!(".section {}", section_name); + let directive = CString::new(directive).unwrap(); + llvm::LLVMSetModuleInlineAsm(metadata_llmod, directive.as_ptr()) + } + return (metadata_llcx, metadata_llmod, metadata); +} + +pub struct ValueIter { + cur: ValueRef, + step: unsafe extern "C" fn(ValueRef) -> ValueRef, +} + +impl Iterator for ValueIter { + type Item = ValueRef; + + fn next(&mut self) -> Option { + let old = self.cur; + if !old.is_null() { + self.cur = unsafe { (self.step)(old) }; + Some(old) + } else { + None + } + } +} + +pub fn iter_globals(llmod: llvm::ModuleRef) -> ValueIter { + unsafe { + ValueIter { + cur: llvm::LLVMGetFirstGlobal(llmod), + step: llvm::LLVMGetNextGlobal, + } + } +} + +pub fn codegen_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, + rx: mpsc::Receiver>) + -> OngoingCodegen { + + check_for_rustc_errors_attr(tcx); + + if let Some(true) = tcx.sess.opts.debugging_opts.thinlto { + if unsafe { !llvm::LLVMRustThinLTOAvailable() } { + tcx.sess.fatal("this compiler's LLVM does not support ThinLTO"); + } + } + + if (tcx.sess.opts.debugging_opts.pgo_gen.is_some() || + !tcx.sess.opts.debugging_opts.pgo_use.is_empty()) && + unsafe { !llvm::LLVMRustPGOAvailable() } + { + tcx.sess.fatal("this compiler's LLVM does not support PGO"); + } + + let crate_hash = tcx.crate_hash(LOCAL_CRATE); + let link_meta = link::build_link_meta(crate_hash); + + // Codegen the metadata. + let llmod_id = "metadata"; + let (metadata_llcx, metadata_llmod, metadata) = + time(tcx.sess, "write metadata", || { + write_metadata(tcx, llmod_id, &link_meta) + }); + + let metadata_module = ModuleCodegen { + name: link::METADATA_MODULE_NAME.to_string(), + llmod_id: llmod_id.to_string(), + source: ModuleSource::Codegened(ModuleLlvm { + llcx: metadata_llcx, + llmod: metadata_llmod, + tm: create_target_machine(tcx.sess, false), + }), + kind: ModuleKind::Metadata, + }; + + let time_graph = if tcx.sess.opts.debugging_opts.codegen_time_graph { + Some(time_graph::TimeGraph::new()) + } else { + None + }; + + // Skip crate items and just output metadata in -Z no-codegen mode. + if tcx.sess.opts.debugging_opts.no_codegen || + !tcx.sess.opts.output_types.should_codegen() { + let ongoing_codegen = write::start_async_codegen( + tcx, + time_graph.clone(), + link_meta, + metadata, + rx, + 1); + + ongoing_codegen.submit_pre_codegened_module_to_llvm(tcx, metadata_module); + ongoing_codegen.codegen_finished(tcx); + + assert_and_save_dep_graph(tcx); + + ongoing_codegen.check_for_errors(tcx.sess); + + return ongoing_codegen; + } + + // Run the monomorphization collector and partition the collected items into + // codegen units. + let codegen_units = + tcx.collect_and_partition_mono_items(LOCAL_CRATE).1; + let codegen_units = (*codegen_units).clone(); + + // Force all codegen_unit queries so they are already either red or green + // when compile_codegen_unit accesses them. We are not able to re-execute + // the codegen_unit query from just the DepNode, so an unknown color would + // lead to having to re-execute compile_codegen_unit, possibly + // unnecessarily. + if tcx.dep_graph.is_fully_enabled() { + for cgu in &codegen_units { + tcx.codegen_unit(cgu.name().clone()); + } + } + + let ongoing_codegen = write::start_async_codegen( + tcx, + time_graph.clone(), + link_meta, + metadata, + rx, + codegen_units.len()); + + // Codegen an allocator shim, if any + let allocator_module = if let Some(kind) = *tcx.sess.allocator_kind.get() { + unsafe { + let llmod_id = "allocator"; + let (llcx, llmod) = + context::create_context_and_module(tcx.sess, llmod_id); + let modules = ModuleLlvm { + llmod, + llcx, + tm: create_target_machine(tcx.sess, false), + }; + time(tcx.sess, "write allocator module", || { + allocator::codegen(tcx, &modules, kind) + }); + + Some(ModuleCodegen { + name: link::ALLOCATOR_MODULE_NAME.to_string(), + llmod_id: llmod_id.to_string(), + source: ModuleSource::Codegened(modules), + kind: ModuleKind::Allocator, + }) + } + } else { + None + }; + + if let Some(allocator_module) = allocator_module { + ongoing_codegen.submit_pre_codegened_module_to_llvm(tcx, allocator_module); + } + + ongoing_codegen.submit_pre_codegened_module_to_llvm(tcx, metadata_module); + + // We sort the codegen units by size. This way we can schedule work for LLVM + // a bit more efficiently. + let codegen_units = { + let mut codegen_units = codegen_units; + codegen_units.sort_by_cached_key(|cgu| cmp::Reverse(cgu.size_estimate())); + codegen_units + }; + + let mut total_codegen_time = Duration::new(0, 0); + let mut all_stats = Stats::default(); + + for cgu in codegen_units.into_iter() { + ongoing_codegen.wait_for_signal_to_codegen_item(); + ongoing_codegen.check_for_errors(tcx.sess); + + // First, if incremental compilation is enabled, we try to re-use the + // codegen unit from the cache. + if tcx.dep_graph.is_fully_enabled() { + let cgu_id = cgu.work_product_id(); + + // Check whether there is a previous work-product we can + // re-use. Not only must the file exist, and the inputs not + // be dirty, but the hash of the symbols we will generate must + // be the same. + if let Some(buf) = tcx.dep_graph.previous_work_product(&cgu_id) { + let dep_node = &DepNode::new(tcx, + DepConstructor::CompileCodegenUnit(cgu.name().clone())); + + // We try to mark the DepNode::CompileCodegenUnit green. If we + // succeed it means that none of the dependencies has changed + // and we can safely re-use. + if let Some(dep_node_index) = tcx.dep_graph.try_mark_green(tcx, dep_node) { + // Append ".rs" to LLVM module identifier. + // + // LLVM code generator emits a ".file filename" directive + // for ELF backends. Value of the "filename" is set as the + // LLVM module identifier. Due to a LLVM MC bug[1], LLVM + // crashes if the module identifier is same as other symbols + // such as a function name in the module. + // 1. http://llvm.org/bugs/show_bug.cgi?id=11479 + let llmod_id = format!("{}.rs", cgu.name()); + + let module = ModuleCodegen { + name: cgu.name().to_string(), + source: ModuleSource::Preexisting(buf), + kind: ModuleKind::Regular, + llmod_id, + }; + tcx.dep_graph.mark_loaded_from_cache(dep_node_index, true); + write::submit_codegened_module_to_llvm(tcx, module, 0); + // Continue to next cgu, this one is done. + continue + } + } else { + // This can happen if files were deleted from the cache + // directory for some reason. We just re-compile then. + } + } + + let _timing_guard = time_graph.as_ref().map(|time_graph| { + time_graph.start(write::CODEGEN_WORKER_TIMELINE, + write::CODEGEN_WORK_PACKAGE_KIND, + &format!("codegen {}", cgu.name())) + }); + let start_time = Instant::now(); + all_stats.extend(tcx.compile_codegen_unit(*cgu.name())); + total_codegen_time += start_time.elapsed(); + ongoing_codegen.check_for_errors(tcx.sess); + } + + ongoing_codegen.codegen_finished(tcx); + + // Since the main thread is sometimes blocked during codegen, we keep track + // -Ztime-passes output manually. + print_time_passes_entry(tcx.sess.time_passes(), + "codegen to LLVM IR", + total_codegen_time); + + if tcx.sess.opts.incremental.is_some() { + ::rustc_incremental::assert_module_sources::assert_module_sources(tcx); + } + + symbol_names_test::report_symbol_names(tcx); + + if tcx.sess.codegen_stats() { + println!("--- codegen stats ---"); + println!("n_glues_created: {}", all_stats.n_glues_created); + println!("n_null_glues: {}", all_stats.n_null_glues); + println!("n_real_glues: {}", all_stats.n_real_glues); + + println!("n_fns: {}", all_stats.n_fns); + println!("n_inlines: {}", all_stats.n_inlines); + println!("n_closures: {}", all_stats.n_closures); + println!("fn stats:"); + all_stats.fn_stats.sort_by_key(|&(_, insns)| insns); + for &(ref name, insns) in all_stats.fn_stats.iter() { + println!("{} insns, {}", insns, *name); + } + } + + if tcx.sess.count_llvm_insns() { + for (k, v) in all_stats.llvm_insns.iter() { + println!("{:7} {}", *v, *k); + } + } + + ongoing_codegen.check_for_errors(tcx.sess); + + assert_and_save_dep_graph(tcx); + ongoing_codegen +} + +fn assert_and_save_dep_graph<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>) { + time(tcx.sess, + "assert dep graph", + || rustc_incremental::assert_dep_graph(tcx)); + + time(tcx.sess, + "serialize dep graph", + || rustc_incremental::save_dep_graph(tcx)); +} + +fn collect_and_partition_mono_items<'a, 'tcx>( + tcx: TyCtxt<'a, 'tcx, 'tcx>, + cnum: CrateNum, +) -> (Arc, Arc>>>) +{ + assert_eq!(cnum, LOCAL_CRATE); + + let collection_mode = match tcx.sess.opts.debugging_opts.print_mono_items { + Some(ref s) => { + let mode_string = s.to_lowercase(); + let mode_string = mode_string.trim(); + if mode_string == "eager" { + MonoItemCollectionMode::Eager + } else { + if mode_string != "lazy" { + let message = format!("Unknown codegen-item collection mode '{}'. \ + Falling back to 'lazy' mode.", + mode_string); + tcx.sess.warn(&message); + } + + MonoItemCollectionMode::Lazy + } + } + None => { + if tcx.sess.opts.cg.link_dead_code { + MonoItemCollectionMode::Eager + } else { + MonoItemCollectionMode::Lazy + } + } + }; + + let (items, inlining_map) = + time(tcx.sess, "monomorphization collection", || { + collector::collect_crate_mono_items(tcx, collection_mode) + }); + + tcx.sess.abort_if_errors(); + + ::rustc_mir::monomorphize::assert_symbols_are_distinct(tcx, items.iter()); + + let strategy = if tcx.sess.opts.incremental.is_some() { + PartitioningStrategy::PerModule + } else { + PartitioningStrategy::FixedUnitCount(tcx.sess.codegen_units()) + }; + + let codegen_units = time(tcx.sess, "codegen unit partitioning", || { + partitioning::partition(tcx, + items.iter().cloned(), + strategy, + &inlining_map) + .into_iter() + .map(Arc::new) + .collect::>() + }); + + let mono_items: DefIdSet = items.iter().filter_map(|mono_item| { + match *mono_item { + MonoItem::Fn(ref instance) => Some(instance.def_id()), + MonoItem::Static(def_id) => Some(def_id), + _ => None, + } + }).collect(); + + if tcx.sess.opts.debugging_opts.print_mono_items.is_some() { + let mut item_to_cgus = FxHashMap(); + + for cgu in &codegen_units { + for (&mono_item, &linkage) in cgu.items() { + item_to_cgus.entry(mono_item) + .or_insert(Vec::new()) + .push((cgu.name().clone(), linkage)); + } + } + + let mut item_keys: Vec<_> = items + .iter() + .map(|i| { + let mut output = i.to_string(tcx); + output.push_str(" @@"); + let mut empty = Vec::new(); + let cgus = item_to_cgus.get_mut(i).unwrap_or(&mut empty); + cgus.as_mut_slice().sort_by_key(|&(ref name, _)| name.clone()); + cgus.dedup(); + for &(ref cgu_name, (linkage, _)) in cgus.iter() { + output.push_str(" "); + output.push_str(&cgu_name.as_str()); + + let linkage_abbrev = match linkage { + Linkage::External => "External", + Linkage::AvailableExternally => "Available", + Linkage::LinkOnceAny => "OnceAny", + Linkage::LinkOnceODR => "OnceODR", + Linkage::WeakAny => "WeakAny", + Linkage::WeakODR => "WeakODR", + Linkage::Appending => "Appending", + Linkage::Internal => "Internal", + Linkage::Private => "Private", + Linkage::ExternalWeak => "ExternalWeak", + Linkage::Common => "Common", + }; + + output.push_str("["); + output.push_str(linkage_abbrev); + output.push_str("]"); + } + output + }) + .collect(); + + item_keys.sort(); + + for item in item_keys { + println!("MONO_ITEM {}", item); + } + } + + (Arc::new(mono_items), Arc::new(codegen_units)) +} + +impl CrateInfo { + pub fn new(tcx: TyCtxt) -> CrateInfo { + let mut info = CrateInfo { + panic_runtime: None, + compiler_builtins: None, + profiler_runtime: None, + sanitizer_runtime: None, + is_no_builtins: FxHashSet(), + native_libraries: FxHashMap(), + used_libraries: tcx.native_libraries(LOCAL_CRATE), + link_args: tcx.link_args(LOCAL_CRATE), + crate_name: FxHashMap(), + used_crates_dynamic: cstore::used_crates(tcx, LinkagePreference::RequireDynamic), + used_crates_static: cstore::used_crates(tcx, LinkagePreference::RequireStatic), + used_crate_source: FxHashMap(), + wasm_custom_sections: BTreeMap::new(), + wasm_imports: FxHashMap(), + lang_item_to_crate: FxHashMap(), + missing_lang_items: FxHashMap(), + }; + let lang_items = tcx.lang_items(); + + let load_wasm_items = tcx.sess.crate_types.borrow() + .iter() + .any(|c| *c != config::CrateTypeRlib) && + tcx.sess.opts.target_triple == TargetTriple::from_triple("wasm32-unknown-unknown"); + + if load_wasm_items { + info!("attempting to load all wasm sections"); + for &id in tcx.wasm_custom_sections(LOCAL_CRATE).iter() { + let (name, contents) = fetch_wasm_section(tcx, id); + info.wasm_custom_sections.entry(name) + .or_insert(Vec::new()) + .extend(contents); + } + info.load_wasm_imports(tcx, LOCAL_CRATE); + } + + for &cnum in tcx.crates().iter() { + info.native_libraries.insert(cnum, tcx.native_libraries(cnum)); + info.crate_name.insert(cnum, tcx.crate_name(cnum).to_string()); + info.used_crate_source.insert(cnum, tcx.used_crate_source(cnum)); + if tcx.is_panic_runtime(cnum) { + info.panic_runtime = Some(cnum); + } + if tcx.is_compiler_builtins(cnum) { + info.compiler_builtins = Some(cnum); + } + if tcx.is_profiler_runtime(cnum) { + info.profiler_runtime = Some(cnum); + } + if tcx.is_sanitizer_runtime(cnum) { + info.sanitizer_runtime = Some(cnum); + } + if tcx.is_no_builtins(cnum) { + info.is_no_builtins.insert(cnum); + } + if load_wasm_items { + for &id in tcx.wasm_custom_sections(cnum).iter() { + let (name, contents) = fetch_wasm_section(tcx, id); + info.wasm_custom_sections.entry(name) + .or_insert(Vec::new()) + .extend(contents); + } + info.load_wasm_imports(tcx, cnum); + } + let missing = tcx.missing_lang_items(cnum); + for &item in missing.iter() { + if let Ok(id) = lang_items.require(item) { + info.lang_item_to_crate.insert(item, id.krate); + } + } + + // No need to look for lang items that are whitelisted and don't + // actually need to exist. + let missing = missing.iter() + .cloned() + .filter(|&l| !weak_lang_items::whitelisted(tcx, l)) + .collect(); + info.missing_lang_items.insert(cnum, missing); + } + + return info + } + + fn load_wasm_imports(&mut self, tcx: TyCtxt, cnum: CrateNum) { + for (&id, module) in tcx.wasm_import_module_map(cnum).iter() { + let instance = Instance::mono(tcx, id); + let import_name = tcx.symbol_name(instance); + self.wasm_imports.insert(import_name.to_string(), module.clone()); + } + } +} + +fn is_codegened_item(tcx: TyCtxt, id: DefId) -> bool { + let (all_mono_items, _) = + tcx.collect_and_partition_mono_items(LOCAL_CRATE); + all_mono_items.contains(&id) +} + +fn compile_codegen_unit<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, + cgu: InternedString) -> Stats { + let cgu = tcx.codegen_unit(cgu); + + let start_time = Instant::now(); + let (stats, module) = module_codegen(tcx, cgu); + let time_to_codegen = start_time.elapsed(); + + // We assume that the cost to run LLVM on a CGU is proportional to + // the time we needed for codegenning it. + let cost = time_to_codegen.as_secs() * 1_000_000_000 + + time_to_codegen.subsec_nanos() as u64; + + write::submit_codegened_module_to_llvm(tcx, + module, + cost); + return stats; + + fn module_codegen<'a, 'tcx>( + tcx: TyCtxt<'a, 'tcx, 'tcx>, + cgu: Arc>) + -> (Stats, ModuleCodegen) + { + let cgu_name = cgu.name().to_string(); + + // Append ".rs" to LLVM module identifier. + // + // LLVM code generator emits a ".file filename" directive + // for ELF backends. Value of the "filename" is set as the + // LLVM module identifier. Due to a LLVM MC bug[1], LLVM + // crashes if the module identifier is same as other symbols + // such as a function name in the module. + // 1. http://llvm.org/bugs/show_bug.cgi?id=11479 + let llmod_id = format!("{}-{}.rs", + cgu.name(), + tcx.crate_disambiguator(LOCAL_CRATE) + .to_fingerprint().to_hex()); + + // Instantiate monomorphizations without filling out definitions yet... + let cx = CodegenCx::new(tcx, cgu, &llmod_id); + let module = { + let mono_items = cx.codegen_unit + .items_in_deterministic_order(cx.tcx); + for &(mono_item, (linkage, visibility)) in &mono_items { + mono_item.predefine(&cx, linkage, visibility); + } + + // ... and now that we have everything pre-defined, fill out those definitions. + for &(mono_item, _) in &mono_items { + mono_item.define(&cx); + } + + // If this codegen unit contains the main function, also create the + // wrapper here + maybe_create_entry_wrapper(&cx); + + // Run replace-all-uses-with for statics that need it + for &(old_g, new_g) in cx.statics_to_rauw.borrow().iter() { + unsafe { + let bitcast = llvm::LLVMConstPointerCast(new_g, llvm::LLVMTypeOf(old_g)); + llvm::LLVMReplaceAllUsesWith(old_g, bitcast); + llvm::LLVMDeleteGlobal(old_g); + } + } + + // Create the llvm.used variable + // This variable has type [N x i8*] and is stored in the llvm.metadata section + if !cx.used_statics.borrow().is_empty() { + let name = CString::new("llvm.used").unwrap(); + let section = CString::new("llvm.metadata").unwrap(); + let array = C_array(Type::i8(&cx).ptr_to(), &*cx.used_statics.borrow()); + + unsafe { + let g = llvm::LLVMAddGlobal(cx.llmod, + val_ty(array).to_ref(), + name.as_ptr()); + llvm::LLVMSetInitializer(g, array); + llvm::LLVMRustSetLinkage(g, llvm::Linkage::AppendingLinkage); + llvm::LLVMSetSection(g, section.as_ptr()); + } + } + + // Finalize debuginfo + if cx.sess().opts.debuginfo != NoDebugInfo { + debuginfo::finalize(&cx); + } + + let llvm_module = ModuleLlvm { + llcx: cx.llcx, + llmod: cx.llmod, + tm: create_target_machine(cx.sess(), false), + }; + + ModuleCodegen { + name: cgu_name, + source: ModuleSource::Codegened(llvm_module), + kind: ModuleKind::Regular, + llmod_id, + } + }; + + (cx.into_stats(), module) + } +} + +pub fn provide(providers: &mut Providers) { + providers.collect_and_partition_mono_items = + collect_and_partition_mono_items; + + providers.is_codegened_item = is_codegened_item; + + providers.codegen_unit = |tcx, name| { + let (_, all) = tcx.collect_and_partition_mono_items(LOCAL_CRATE); + all.iter() + .find(|cgu| *cgu.name() == name) + .cloned() + .expect(&format!("failed to find cgu with name {:?}", name)) + }; + providers.compile_codegen_unit = compile_codegen_unit; + + provide_extern(providers); +} + +pub fn provide_extern(providers: &mut Providers) { + providers.dllimport_foreign_items = |tcx, krate| { + let module_map = tcx.foreign_modules(krate); + let module_map = module_map.iter() + .map(|lib| (lib.def_id, lib)) + .collect::>(); + + let dllimports = tcx.native_libraries(krate) + .iter() + .filter(|lib| { + if lib.kind != cstore::NativeLibraryKind::NativeUnknown { + return false + } + let cfg = match lib.cfg { + Some(ref cfg) => cfg, + None => return true, + }; + attr::cfg_matches(cfg, &tcx.sess.parse_sess, None) + }) + .filter_map(|lib| lib.foreign_module) + .map(|id| &module_map[&id]) + .flat_map(|module| module.foreign_items.iter().cloned()) + .collect(); + Lrc::new(dllimports) + }; + + providers.is_dllimport_foreign_item = |tcx, def_id| { + tcx.dllimport_foreign_items(def_id.krate).contains(&def_id) + }; +} + +pub fn linkage_to_llvm(linkage: Linkage) -> llvm::Linkage { + match linkage { + Linkage::External => llvm::Linkage::ExternalLinkage, + Linkage::AvailableExternally => llvm::Linkage::AvailableExternallyLinkage, + Linkage::LinkOnceAny => llvm::Linkage::LinkOnceAnyLinkage, + Linkage::LinkOnceODR => llvm::Linkage::LinkOnceODRLinkage, + Linkage::WeakAny => llvm::Linkage::WeakAnyLinkage, + Linkage::WeakODR => llvm::Linkage::WeakODRLinkage, + Linkage::Appending => llvm::Linkage::AppendingLinkage, + Linkage::Internal => llvm::Linkage::InternalLinkage, + Linkage::Private => llvm::Linkage::PrivateLinkage, + Linkage::ExternalWeak => llvm::Linkage::ExternalWeakLinkage, + Linkage::Common => llvm::Linkage::CommonLinkage, + } +} + +pub fn visibility_to_llvm(linkage: Visibility) -> llvm::Visibility { + match linkage { + Visibility::Default => llvm::Visibility::Default, + Visibility::Hidden => llvm::Visibility::Hidden, + Visibility::Protected => llvm::Visibility::Protected, + } +} + +// FIXME(mw): Anything that is produced via DepGraph::with_task() must implement +// the HashStable trait. Normally DepGraph::with_task() calls are +// hidden behind queries, but CGU creation is a special case in two +// ways: (1) it's not a query and (2) CGU are output nodes, so their +// Fingerprints are not actually needed. It remains to be clarified +// how exactly this case will be handled in the red/green system but +// for now we content ourselves with providing a no-op HashStable +// implementation for CGUs. +mod temp_stable_hash_impls { + use rustc_data_structures::stable_hasher::{StableHasherResult, StableHasher, + HashStable}; + use ModuleCodegen; + + impl HashStable for ModuleCodegen { + fn hash_stable(&self, + _: &mut HCX, + _: &mut StableHasher) { + // do nothing + } + } +} + +fn fetch_wasm_section(tcx: TyCtxt, id: DefId) -> (String, Vec) { + use rustc::mir::interpret::GlobalId; + use rustc::middle::const_val::ConstVal; + + info!("loading wasm section {:?}", id); + + let section = tcx.get_attrs(id) + .iter() + .find(|a| a.check_name("wasm_custom_section")) + .expect("missing #[wasm_custom_section] attribute") + .value_str() + .expect("malformed #[wasm_custom_section] attribute"); + + let instance = ty::Instance::mono(tcx, id); + let cid = GlobalId { + instance, + promoted: None + }; + let param_env = ty::ParamEnv::reveal_all(); + let val = tcx.const_eval(param_env.and(cid)).unwrap(); + + let const_val = match val.val { + ConstVal::Value(val) => val, + ConstVal::Unevaluated(..) => bug!("should be evaluated"), + }; + + let alloc = tcx.const_value_to_allocation((const_val, val.ty)); + (section.to_string(), alloc.bytes.clone()) +} diff --git a/src/librustc_codegen_llvm/build.rs b/src/librustc_codegen_llvm/build.rs new file mode 100644 index 00000000000..97accbb4b8f --- /dev/null +++ b/src/librustc_codegen_llvm/build.rs @@ -0,0 +1,16 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +fn main() { + println!("cargo:rerun-if-changed=build.rs"); + println!("cargo:rerun-if-env-changed=CFG_VERSION"); + println!("cargo:rerun-if-env-changed=CFG_PREFIX"); + println!("cargo:rerun-if-env-changed=CFG_LLVM_ROOT"); +} diff --git a/src/librustc_codegen_llvm/builder.rs b/src/librustc_codegen_llvm/builder.rs new file mode 100644 index 00000000000..7b4998e8588 --- /dev/null +++ b/src/librustc_codegen_llvm/builder.rs @@ -0,0 +1,1425 @@ +// Copyright 2013 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. + +#![allow(dead_code)] // FFI wrappers + +use llvm; +use llvm::{AtomicRmwBinOp, AtomicOrdering, SynchronizationScope, AsmDialect}; +use llvm::{Opcode, IntPredicate, RealPredicate, False, OperandBundleDef}; +use llvm::{ValueRef, BasicBlockRef, BuilderRef, ModuleRef}; +use common::*; +use type_::Type; +use value::Value; +use libc::{c_uint, c_char}; +use rustc::ty::TyCtxt; +use rustc::ty::layout::{Align, Size}; +use rustc::session::{config, Session}; + +use std::borrow::Cow; +use std::ffi::CString; +use std::ops::Range; +use std::ptr; +use syntax_pos::Span; + +// All Builders must have an llfn associated with them +#[must_use] +pub struct Builder<'a, 'tcx: 'a> { + pub llbuilder: BuilderRef, + pub cx: &'a CodegenCx<'a, 'tcx>, +} + +impl<'a, 'tcx> Drop for Builder<'a, 'tcx> { + fn drop(&mut self) { + unsafe { + llvm::LLVMDisposeBuilder(self.llbuilder); + } + } +} + +// This is a really awful way to get a zero-length c-string, but better (and a +// lot more efficient) than doing str::as_c_str("", ...) every time. +fn noname() -> *const c_char { + static CNULL: c_char = 0; + &CNULL +} + +bitflags! { + pub struct MemFlags: u8 { + const VOLATILE = 1 << 0; + const NONTEMPORAL = 1 << 1; + } +} + +impl<'a, 'tcx> Builder<'a, 'tcx> { + pub fn new_block<'b>(cx: &'a CodegenCx<'a, 'tcx>, llfn: ValueRef, name: &'b str) -> Self { + let bx = Builder::with_cx(cx); + let llbb = unsafe { + let name = CString::new(name).unwrap(); + llvm::LLVMAppendBasicBlockInContext( + cx.llcx, + llfn, + name.as_ptr() + ) + }; + bx.position_at_end(llbb); + bx + } + + pub fn with_cx(cx: &'a CodegenCx<'a, 'tcx>) -> Self { + // Create a fresh builder from the crate context. + let llbuilder = unsafe { + llvm::LLVMCreateBuilderInContext(cx.llcx) + }; + Builder { + llbuilder, + cx, + } + } + + pub fn build_sibling_block<'b>(&self, name: &'b str) -> Builder<'a, 'tcx> { + Builder::new_block(self.cx, self.llfn(), name) + } + + pub fn sess(&self) -> &Session { + self.cx.sess() + } + + pub fn tcx(&self) -> TyCtxt<'a, 'tcx, 'tcx> { + self.cx.tcx + } + + pub fn llfn(&self) -> ValueRef { + unsafe { + llvm::LLVMGetBasicBlockParent(self.llbb()) + } + } + + pub fn llbb(&self) -> BasicBlockRef { + unsafe { + llvm::LLVMGetInsertBlock(self.llbuilder) + } + } + + fn count_insn(&self, category: &str) { + if self.cx.sess().codegen_stats() { + self.cx.stats.borrow_mut().n_llvm_insns += 1; + } + if self.cx.sess().count_llvm_insns() { + *self.cx.stats + .borrow_mut() + .llvm_insns + .entry(category.to_string()) + .or_insert(0) += 1; + } + } + + pub fn set_value_name(&self, value: ValueRef, name: &str) { + let cname = CString::new(name.as_bytes()).unwrap(); + unsafe { + llvm::LLVMSetValueName(value, cname.as_ptr()); + } + } + + pub fn position_before(&self, insn: ValueRef) { + unsafe { + llvm::LLVMPositionBuilderBefore(self.llbuilder, insn); + } + } + + pub fn position_at_end(&self, llbb: BasicBlockRef) { + unsafe { + llvm::LLVMPositionBuilderAtEnd(self.llbuilder, llbb); + } + } + + pub fn position_at_start(&self, llbb: BasicBlockRef) { + unsafe { + llvm::LLVMRustPositionBuilderAtStart(self.llbuilder, llbb); + } + } + + pub fn ret_void(&self) { + self.count_insn("retvoid"); + unsafe { + llvm::LLVMBuildRetVoid(self.llbuilder); + } + } + + pub fn ret(&self, v: ValueRef) { + self.count_insn("ret"); + unsafe { + llvm::LLVMBuildRet(self.llbuilder, v); + } + } + + pub fn aggregate_ret(&self, ret_vals: &[ValueRef]) { + unsafe { + llvm::LLVMBuildAggregateRet(self.llbuilder, + ret_vals.as_ptr(), + ret_vals.len() as c_uint); + } + } + + pub fn br(&self, dest: BasicBlockRef) { + self.count_insn("br"); + unsafe { + llvm::LLVMBuildBr(self.llbuilder, dest); + } + } + + pub fn cond_br(&self, cond: ValueRef, then_llbb: BasicBlockRef, else_llbb: BasicBlockRef) { + self.count_insn("condbr"); + unsafe { + llvm::LLVMBuildCondBr(self.llbuilder, cond, then_llbb, else_llbb); + } + } + + pub fn switch(&self, v: ValueRef, else_llbb: BasicBlockRef, num_cases: usize) -> ValueRef { + unsafe { + llvm::LLVMBuildSwitch(self.llbuilder, v, else_llbb, num_cases as c_uint) + } + } + + pub fn indirect_br(&self, addr: ValueRef, num_dests: usize) { + self.count_insn("indirectbr"); + unsafe { + llvm::LLVMBuildIndirectBr(self.llbuilder, addr, num_dests as c_uint); + } + } + + pub fn invoke(&self, + llfn: ValueRef, + args: &[ValueRef], + then: BasicBlockRef, + catch: BasicBlockRef, + bundle: Option<&OperandBundleDef>) -> ValueRef { + self.count_insn("invoke"); + + debug!("Invoke {:?} with args ({})", + Value(llfn), + args.iter() + .map(|&v| format!("{:?}", Value(v))) + .collect::>() + .join(", ")); + + let args = self.check_call("invoke", llfn, args); + let bundle = bundle.as_ref().map(|b| b.raw()).unwrap_or(ptr::null_mut()); + + unsafe { + llvm::LLVMRustBuildInvoke(self.llbuilder, + llfn, + args.as_ptr(), + args.len() as c_uint, + then, + catch, + bundle, + noname()) + } + } + + pub fn unreachable(&self) { + self.count_insn("unreachable"); + unsafe { + llvm::LLVMBuildUnreachable(self.llbuilder); + } + } + + /* Arithmetic */ + pub fn add(&self, lhs: ValueRef, rhs: ValueRef) -> ValueRef { + self.count_insn("add"); + unsafe { + llvm::LLVMBuildAdd(self.llbuilder, lhs, rhs, noname()) + } + } + + pub fn nswadd(&self, lhs: ValueRef, rhs: ValueRef) -> ValueRef { + self.count_insn("nswadd"); + unsafe { + llvm::LLVMBuildNSWAdd(self.llbuilder, lhs, rhs, noname()) + } + } + + pub fn nuwadd(&self, lhs: ValueRef, rhs: ValueRef) -> ValueRef { + self.count_insn("nuwadd"); + unsafe { + llvm::LLVMBuildNUWAdd(self.llbuilder, lhs, rhs, noname()) + } + } + + pub fn fadd(&self, lhs: ValueRef, rhs: ValueRef) -> ValueRef { + self.count_insn("fadd"); + unsafe { + llvm::LLVMBuildFAdd(self.llbuilder, lhs, rhs, noname()) + } + } + + pub fn fadd_fast(&self, lhs: ValueRef, rhs: ValueRef) -> ValueRef { + self.count_insn("fadd"); + unsafe { + let instr = llvm::LLVMBuildFAdd(self.llbuilder, lhs, rhs, noname()); + llvm::LLVMRustSetHasUnsafeAlgebra(instr); + instr + } + } + + pub fn sub(&self, lhs: ValueRef, rhs: ValueRef) -> ValueRef { + self.count_insn("sub"); + unsafe { + llvm::LLVMBuildSub(self.llbuilder, lhs, rhs, noname()) + } + } + + pub fn nswsub(&self, lhs: ValueRef, rhs: ValueRef) -> ValueRef { + self.count_insn("nwsub"); + unsafe { + llvm::LLVMBuildNSWSub(self.llbuilder, lhs, rhs, noname()) + } + } + + pub fn nuwsub(&self, lhs: ValueRef, rhs: ValueRef) -> ValueRef { + self.count_insn("nuwsub"); + unsafe { + llvm::LLVMBuildNUWSub(self.llbuilder, lhs, rhs, noname()) + } + } + + pub fn fsub(&self, lhs: ValueRef, rhs: ValueRef) -> ValueRef { + self.count_insn("sub"); + unsafe { + llvm::LLVMBuildFSub(self.llbuilder, lhs, rhs, noname()) + } + } + + pub fn fsub_fast(&self, lhs: ValueRef, rhs: ValueRef) -> ValueRef { + self.count_insn("sub"); + unsafe { + let instr = llvm::LLVMBuildFSub(self.llbuilder, lhs, rhs, noname()); + llvm::LLVMRustSetHasUnsafeAlgebra(instr); + instr + } + } + + pub fn mul(&self, lhs: ValueRef, rhs: ValueRef) -> ValueRef { + self.count_insn("mul"); + unsafe { + llvm::LLVMBuildMul(self.llbuilder, lhs, rhs, noname()) + } + } + + pub fn nswmul(&self, lhs: ValueRef, rhs: ValueRef) -> ValueRef { + self.count_insn("nswmul"); + unsafe { + llvm::LLVMBuildNSWMul(self.llbuilder, lhs, rhs, noname()) + } + } + + pub fn nuwmul(&self, lhs: ValueRef, rhs: ValueRef) -> ValueRef { + self.count_insn("nuwmul"); + unsafe { + llvm::LLVMBuildNUWMul(self.llbuilder, lhs, rhs, noname()) + } + } + + pub fn fmul(&self, lhs: ValueRef, rhs: ValueRef) -> ValueRef { + self.count_insn("fmul"); + unsafe { + llvm::LLVMBuildFMul(self.llbuilder, lhs, rhs, noname()) + } + } + + pub fn fmul_fast(&self, lhs: ValueRef, rhs: ValueRef) -> ValueRef { + self.count_insn("fmul"); + unsafe { + let instr = llvm::LLVMBuildFMul(self.llbuilder, lhs, rhs, noname()); + llvm::LLVMRustSetHasUnsafeAlgebra(instr); + instr + } + } + + + pub fn udiv(&self, lhs: ValueRef, rhs: ValueRef) -> ValueRef { + self.count_insn("udiv"); + unsafe { + llvm::LLVMBuildUDiv(self.llbuilder, lhs, rhs, noname()) + } + } + + pub fn exactudiv(&self, lhs: ValueRef, rhs: ValueRef) -> ValueRef { + self.count_insn("exactudiv"); + unsafe { + llvm::LLVMBuildExactUDiv(self.llbuilder, lhs, rhs, noname()) + } + } + + pub fn sdiv(&self, lhs: ValueRef, rhs: ValueRef) -> ValueRef { + self.count_insn("sdiv"); + unsafe { + llvm::LLVMBuildSDiv(self.llbuilder, lhs, rhs, noname()) + } + } + + pub fn exactsdiv(&self, lhs: ValueRef, rhs: ValueRef) -> ValueRef { + self.count_insn("exactsdiv"); + unsafe { + llvm::LLVMBuildExactSDiv(self.llbuilder, lhs, rhs, noname()) + } + } + + pub fn fdiv(&self, lhs: ValueRef, rhs: ValueRef) -> ValueRef { + self.count_insn("fdiv"); + unsafe { + llvm::LLVMBuildFDiv(self.llbuilder, lhs, rhs, noname()) + } + } + + pub fn fdiv_fast(&self, lhs: ValueRef, rhs: ValueRef) -> ValueRef { + self.count_insn("fdiv"); + unsafe { + let instr = llvm::LLVMBuildFDiv(self.llbuilder, lhs, rhs, noname()); + llvm::LLVMRustSetHasUnsafeAlgebra(instr); + instr + } + } + + pub fn urem(&self, lhs: ValueRef, rhs: ValueRef) -> ValueRef { + self.count_insn("urem"); + unsafe { + llvm::LLVMBuildURem(self.llbuilder, lhs, rhs, noname()) + } + } + + pub fn srem(&self, lhs: ValueRef, rhs: ValueRef) -> ValueRef { + self.count_insn("srem"); + unsafe { + llvm::LLVMBuildSRem(self.llbuilder, lhs, rhs, noname()) + } + } + + pub fn frem(&self, lhs: ValueRef, rhs: ValueRef) -> ValueRef { + self.count_insn("frem"); + unsafe { + llvm::LLVMBuildFRem(self.llbuilder, lhs, rhs, noname()) + } + } + + pub fn frem_fast(&self, lhs: ValueRef, rhs: ValueRef) -> ValueRef { + self.count_insn("frem"); + unsafe { + let instr = llvm::LLVMBuildFRem(self.llbuilder, lhs, rhs, noname()); + llvm::LLVMRustSetHasUnsafeAlgebra(instr); + instr + } + } + + pub fn shl(&self, lhs: ValueRef, rhs: ValueRef) -> ValueRef { + self.count_insn("shl"); + unsafe { + llvm::LLVMBuildShl(self.llbuilder, lhs, rhs, noname()) + } + } + + pub fn lshr(&self, lhs: ValueRef, rhs: ValueRef) -> ValueRef { + self.count_insn("lshr"); + unsafe { + llvm::LLVMBuildLShr(self.llbuilder, lhs, rhs, noname()) + } + } + + pub fn ashr(&self, lhs: ValueRef, rhs: ValueRef) -> ValueRef { + self.count_insn("ashr"); + unsafe { + llvm::LLVMBuildAShr(self.llbuilder, lhs, rhs, noname()) + } + } + + pub fn and(&self, lhs: ValueRef, rhs: ValueRef) -> ValueRef { + self.count_insn("and"); + unsafe { + llvm::LLVMBuildAnd(self.llbuilder, lhs, rhs, noname()) + } + } + + pub fn or(&self, lhs: ValueRef, rhs: ValueRef) -> ValueRef { + self.count_insn("or"); + unsafe { + llvm::LLVMBuildOr(self.llbuilder, lhs, rhs, noname()) + } + } + + pub fn xor(&self, lhs: ValueRef, rhs: ValueRef) -> ValueRef { + self.count_insn("xor"); + unsafe { + llvm::LLVMBuildXor(self.llbuilder, lhs, rhs, noname()) + } + } + + pub fn binop(&self, op: Opcode, lhs: ValueRef, rhs: ValueRef) + -> ValueRef { + self.count_insn("binop"); + unsafe { + llvm::LLVMBuildBinOp(self.llbuilder, op, lhs, rhs, noname()) + } + } + + pub fn neg(&self, v: ValueRef) -> ValueRef { + self.count_insn("neg"); + unsafe { + llvm::LLVMBuildNeg(self.llbuilder, v, noname()) + } + } + + pub fn nswneg(&self, v: ValueRef) -> ValueRef { + self.count_insn("nswneg"); + unsafe { + llvm::LLVMBuildNSWNeg(self.llbuilder, v, noname()) + } + } + + pub fn nuwneg(&self, v: ValueRef) -> ValueRef { + self.count_insn("nuwneg"); + unsafe { + llvm::LLVMBuildNUWNeg(self.llbuilder, v, noname()) + } + } + pub fn fneg(&self, v: ValueRef) -> ValueRef { + self.count_insn("fneg"); + unsafe { + llvm::LLVMBuildFNeg(self.llbuilder, v, noname()) + } + } + + pub fn not(&self, v: ValueRef) -> ValueRef { + self.count_insn("not"); + unsafe { + llvm::LLVMBuildNot(self.llbuilder, v, noname()) + } + } + + pub fn alloca(&self, ty: Type, name: &str, align: Align) -> ValueRef { + let bx = Builder::with_cx(self.cx); + bx.position_at_start(unsafe { + llvm::LLVMGetFirstBasicBlock(self.llfn()) + }); + bx.dynamic_alloca(ty, name, align) + } + + pub fn dynamic_alloca(&self, ty: Type, name: &str, align: Align) -> ValueRef { + self.count_insn("alloca"); + unsafe { + let alloca = if name.is_empty() { + llvm::LLVMBuildAlloca(self.llbuilder, ty.to_ref(), noname()) + } else { + let name = CString::new(name).unwrap(); + llvm::LLVMBuildAlloca(self.llbuilder, ty.to_ref(), + name.as_ptr()) + }; + llvm::LLVMSetAlignment(alloca, align.abi() as c_uint); + alloca + } + } + + pub fn free(&self, ptr: ValueRef) { + self.count_insn("free"); + unsafe { + llvm::LLVMBuildFree(self.llbuilder, ptr); + } + } + + pub fn load(&self, ptr: ValueRef, align: Align) -> ValueRef { + self.count_insn("load"); + unsafe { + let load = llvm::LLVMBuildLoad(self.llbuilder, ptr, noname()); + llvm::LLVMSetAlignment(load, align.abi() as c_uint); + load + } + } + + pub fn volatile_load(&self, ptr: ValueRef) -> ValueRef { + self.count_insn("load.volatile"); + unsafe { + let insn = llvm::LLVMBuildLoad(self.llbuilder, ptr, noname()); + llvm::LLVMSetVolatile(insn, llvm::True); + insn + } + } + + pub fn atomic_load(&self, ptr: ValueRef, order: AtomicOrdering, align: Align) -> ValueRef { + self.count_insn("load.atomic"); + unsafe { + let load = llvm::LLVMRustBuildAtomicLoad(self.llbuilder, ptr, noname(), order); + // FIXME(eddyb) Isn't it UB to use `pref` instead of `abi` here? + // However, 64-bit atomic loads on `i686-apple-darwin` appear to + // require `___atomic_load` with ABI-alignment, so it's staying. + llvm::LLVMSetAlignment(load, align.pref() as c_uint); + load + } + } + + + pub fn range_metadata(&self, load: ValueRef, range: Range) { + unsafe { + let llty = val_ty(load); + let v = [ + C_uint_big(llty, range.start), + C_uint_big(llty, range.end) + ]; + + llvm::LLVMSetMetadata(load, llvm::MD_range as c_uint, + llvm::LLVMMDNodeInContext(self.cx.llcx, + v.as_ptr(), + v.len() as c_uint)); + } + } + + pub fn nonnull_metadata(&self, load: ValueRef) { + unsafe { + llvm::LLVMSetMetadata(load, llvm::MD_nonnull as c_uint, + llvm::LLVMMDNodeInContext(self.cx.llcx, ptr::null(), 0)); + } + } + + pub fn store(&self, val: ValueRef, ptr: ValueRef, align: Align) -> ValueRef { + self.store_with_flags(val, ptr, align, MemFlags::empty()) + } + + pub fn store_with_flags( + &self, + val: ValueRef, + ptr: ValueRef, + align: Align, + flags: MemFlags, + ) -> ValueRef { + debug!("Store {:?} -> {:?} ({:?})", Value(val), Value(ptr), flags); + assert!(!self.llbuilder.is_null()); + self.count_insn("store"); + let ptr = self.check_store(val, ptr); + unsafe { + let store = llvm::LLVMBuildStore(self.llbuilder, val, ptr); + llvm::LLVMSetAlignment(store, align.abi() as c_uint); + if flags.contains(MemFlags::VOLATILE) { + llvm::LLVMSetVolatile(store, llvm::True); + } + if flags.contains(MemFlags::NONTEMPORAL) { + // According to LLVM [1] building a nontemporal store must + // *always* point to a metadata value of the integer 1. + // + // [1]: http://llvm.org/docs/LangRef.html#store-instruction + let one = C_i32(self.cx, 1); + let node = llvm::LLVMMDNodeInContext(self.cx.llcx, &one, 1); + llvm::LLVMSetMetadata(store, llvm::MD_nontemporal as c_uint, node); + } + store + } + } + + pub fn atomic_store(&self, val: ValueRef, ptr: ValueRef, + order: AtomicOrdering, align: Align) { + debug!("Store {:?} -> {:?}", Value(val), Value(ptr)); + self.count_insn("store.atomic"); + let ptr = self.check_store(val, ptr); + unsafe { + let store = llvm::LLVMRustBuildAtomicStore(self.llbuilder, val, ptr, order); + // FIXME(eddyb) Isn't it UB to use `pref` instead of `abi` here? + // Also see `atomic_load` for more context. + llvm::LLVMSetAlignment(store, align.pref() as c_uint); + } + } + + pub fn gep(&self, ptr: ValueRef, indices: &[ValueRef]) -> ValueRef { + self.count_insn("gep"); + unsafe { + llvm::LLVMBuildGEP(self.llbuilder, ptr, indices.as_ptr(), + indices.len() as c_uint, noname()) + } + } + + pub fn inbounds_gep(&self, ptr: ValueRef, indices: &[ValueRef]) -> ValueRef { + self.count_insn("inboundsgep"); + unsafe { + llvm::LLVMBuildInBoundsGEP( + self.llbuilder, ptr, indices.as_ptr(), indices.len() as c_uint, noname()) + } + } + + pub fn struct_gep(&self, ptr: ValueRef, idx: u64) -> ValueRef { + self.count_insn("structgep"); + assert_eq!(idx as c_uint as u64, idx); + unsafe { + llvm::LLVMBuildStructGEP(self.llbuilder, ptr, idx as c_uint, noname()) + } + } + + pub fn global_string(&self, _str: *const c_char) -> ValueRef { + self.count_insn("globalstring"); + unsafe { + llvm::LLVMBuildGlobalString(self.llbuilder, _str, noname()) + } + } + + pub fn global_string_ptr(&self, _str: *const c_char) -> ValueRef { + self.count_insn("globalstringptr"); + unsafe { + llvm::LLVMBuildGlobalStringPtr(self.llbuilder, _str, noname()) + } + } + + /* Casts */ + pub fn trunc(&self, val: ValueRef, dest_ty: Type) -> ValueRef { + self.count_insn("trunc"); + unsafe { + llvm::LLVMBuildTrunc(self.llbuilder, val, dest_ty.to_ref(), noname()) + } + } + + pub fn zext(&self, val: ValueRef, dest_ty: Type) -> ValueRef { + self.count_insn("zext"); + unsafe { + llvm::LLVMBuildZExt(self.llbuilder, val, dest_ty.to_ref(), noname()) + } + } + + pub fn sext(&self, val: ValueRef, dest_ty: Type) -> ValueRef { + self.count_insn("sext"); + unsafe { + llvm::LLVMBuildSExt(self.llbuilder, val, dest_ty.to_ref(), noname()) + } + } + + pub fn fptoui(&self, val: ValueRef, dest_ty: Type) -> ValueRef { + self.count_insn("fptoui"); + unsafe { + llvm::LLVMBuildFPToUI(self.llbuilder, val, dest_ty.to_ref(), noname()) + } + } + + pub fn fptosi(&self, val: ValueRef, dest_ty: Type) -> ValueRef { + self.count_insn("fptosi"); + unsafe { + llvm::LLVMBuildFPToSI(self.llbuilder, val, dest_ty.to_ref(),noname()) + } + } + + pub fn uitofp(&self, val: ValueRef, dest_ty: Type) -> ValueRef { + self.count_insn("uitofp"); + unsafe { + llvm::LLVMBuildUIToFP(self.llbuilder, val, dest_ty.to_ref(), noname()) + } + } + + pub fn sitofp(&self, val: ValueRef, dest_ty: Type) -> ValueRef { + self.count_insn("sitofp"); + unsafe { + llvm::LLVMBuildSIToFP(self.llbuilder, val, dest_ty.to_ref(), noname()) + } + } + + pub fn fptrunc(&self, val: ValueRef, dest_ty: Type) -> ValueRef { + self.count_insn("fptrunc"); + unsafe { + llvm::LLVMBuildFPTrunc(self.llbuilder, val, dest_ty.to_ref(), noname()) + } + } + + pub fn fpext(&self, val: ValueRef, dest_ty: Type) -> ValueRef { + self.count_insn("fpext"); + unsafe { + llvm::LLVMBuildFPExt(self.llbuilder, val, dest_ty.to_ref(), noname()) + } + } + + pub fn ptrtoint(&self, val: ValueRef, dest_ty: Type) -> ValueRef { + self.count_insn("ptrtoint"); + unsafe { + llvm::LLVMBuildPtrToInt(self.llbuilder, val, dest_ty.to_ref(), noname()) + } + } + + pub fn inttoptr(&self, val: ValueRef, dest_ty: Type) -> ValueRef { + self.count_insn("inttoptr"); + unsafe { + llvm::LLVMBuildIntToPtr(self.llbuilder, val, dest_ty.to_ref(), noname()) + } + } + + pub fn bitcast(&self, val: ValueRef, dest_ty: Type) -> ValueRef { + self.count_insn("bitcast"); + unsafe { + llvm::LLVMBuildBitCast(self.llbuilder, val, dest_ty.to_ref(), noname()) + } + } + + pub fn zext_or_bitcast(&self, val: ValueRef, dest_ty: Type) -> ValueRef { + self.count_insn("zextorbitcast"); + unsafe { + llvm::LLVMBuildZExtOrBitCast(self.llbuilder, val, dest_ty.to_ref(), noname()) + } + } + + pub fn sext_or_bitcast(&self, val: ValueRef, dest_ty: Type) -> ValueRef { + self.count_insn("sextorbitcast"); + unsafe { + llvm::LLVMBuildSExtOrBitCast(self.llbuilder, val, dest_ty.to_ref(), noname()) + } + } + + pub fn trunc_or_bitcast(&self, val: ValueRef, dest_ty: Type) -> ValueRef { + self.count_insn("truncorbitcast"); + unsafe { + llvm::LLVMBuildTruncOrBitCast(self.llbuilder, val, dest_ty.to_ref(), noname()) + } + } + + pub fn cast(&self, op: Opcode, val: ValueRef, dest_ty: Type) -> ValueRef { + self.count_insn("cast"); + unsafe { + llvm::LLVMBuildCast(self.llbuilder, op, val, dest_ty.to_ref(), noname()) + } + } + + pub fn pointercast(&self, val: ValueRef, dest_ty: Type) -> ValueRef { + self.count_insn("pointercast"); + unsafe { + llvm::LLVMBuildPointerCast(self.llbuilder, val, dest_ty.to_ref(), noname()) + } + } + + pub fn intcast(&self, val: ValueRef, dest_ty: Type, is_signed: bool) -> ValueRef { + self.count_insn("intcast"); + unsafe { + llvm::LLVMRustBuildIntCast(self.llbuilder, val, dest_ty.to_ref(), is_signed) + } + } + + pub fn fpcast(&self, val: ValueRef, dest_ty: Type) -> ValueRef { + self.count_insn("fpcast"); + unsafe { + llvm::LLVMBuildFPCast(self.llbuilder, val, dest_ty.to_ref(), noname()) + } + } + + + /* Comparisons */ + pub fn icmp(&self, op: IntPredicate, lhs: ValueRef, rhs: ValueRef) -> ValueRef { + self.count_insn("icmp"); + unsafe { + llvm::LLVMBuildICmp(self.llbuilder, op as c_uint, lhs, rhs, noname()) + } + } + + pub fn fcmp(&self, op: RealPredicate, lhs: ValueRef, rhs: ValueRef) -> ValueRef { + self.count_insn("fcmp"); + unsafe { + llvm::LLVMBuildFCmp(self.llbuilder, op as c_uint, lhs, rhs, noname()) + } + } + + /* Miscellaneous instructions */ + pub fn empty_phi(&self, ty: Type) -> ValueRef { + self.count_insn("emptyphi"); + unsafe { + llvm::LLVMBuildPhi(self.llbuilder, ty.to_ref(), noname()) + } + } + + pub fn phi(&self, ty: Type, vals: &[ValueRef], bbs: &[BasicBlockRef]) -> ValueRef { + assert_eq!(vals.len(), bbs.len()); + let phi = self.empty_phi(ty); + self.count_insn("addincoming"); + unsafe { + llvm::LLVMAddIncoming(phi, vals.as_ptr(), + bbs.as_ptr(), + vals.len() as c_uint); + phi + } + } + + pub fn add_span_comment(&self, sp: Span, text: &str) { + if self.cx.sess().asm_comments() { + let s = format!("{} ({})", + text, + self.cx.sess().codemap().span_to_string(sp)); + debug!("{}", s); + self.add_comment(&s); + } + } + + pub fn add_comment(&self, text: &str) { + if self.cx.sess().asm_comments() { + let sanitized = text.replace("$", ""); + let comment_text = format!("{} {}", "#", + sanitized.replace("\n", "\n\t# ")); + self.count_insn("inlineasm"); + let comment_text = CString::new(comment_text).unwrap(); + let asm = unsafe { + llvm::LLVMConstInlineAsm(Type::func(&[], &Type::void(self.cx)).to_ref(), + comment_text.as_ptr(), noname(), False, + False) + }; + self.call(asm, &[], None); + } + } + + pub fn inline_asm_call(&self, asm: *const c_char, cons: *const c_char, + inputs: &[ValueRef], output: Type, + volatile: bool, alignstack: bool, + dia: AsmDialect) -> ValueRef { + self.count_insn("inlineasm"); + + let volatile = if volatile { llvm::True } + else { llvm::False }; + let alignstack = if alignstack { llvm::True } + else { llvm::False }; + + let argtys = inputs.iter().map(|v| { + debug!("Asm Input Type: {:?}", Value(*v)); + val_ty(*v) + }).collect::>(); + + debug!("Asm Output Type: {:?}", output); + let fty = Type::func(&argtys[..], &output); + unsafe { + let v = llvm::LLVMRustInlineAsm( + fty.to_ref(), asm, cons, volatile, alignstack, dia); + self.call(v, inputs, None) + } + } + + pub fn call(&self, llfn: ValueRef, args: &[ValueRef], + bundle: Option<&OperandBundleDef>) -> ValueRef { + self.count_insn("call"); + + debug!("Call {:?} with args ({})", + Value(llfn), + args.iter() + .map(|&v| format!("{:?}", Value(v))) + .collect::>() + .join(", ")); + + let args = self.check_call("call", llfn, args); + let bundle = bundle.as_ref().map(|b| b.raw()).unwrap_or(ptr::null_mut()); + + unsafe { + llvm::LLVMRustBuildCall(self.llbuilder, llfn, args.as_ptr(), + args.len() as c_uint, bundle, noname()) + } + } + + pub fn minnum(&self, lhs: ValueRef, rhs: ValueRef) -> ValueRef { + self.count_insn("minnum"); + unsafe { + let instr = llvm::LLVMRustBuildMinNum(self.llbuilder, lhs, rhs); + if instr.is_null() { + bug!("LLVMRustBuildMinNum is not available in LLVM version < 6.0"); + } + instr + } + } + pub fn maxnum(&self, lhs: ValueRef, rhs: ValueRef) -> ValueRef { + self.count_insn("maxnum"); + unsafe { + let instr = llvm::LLVMRustBuildMaxNum(self.llbuilder, lhs, rhs); + if instr.is_null() { + bug!("LLVMRustBuildMaxNum is not available in LLVM version < 6.0"); + } + instr + } + } + + pub fn select(&self, cond: ValueRef, then_val: ValueRef, else_val: ValueRef) -> ValueRef { + self.count_insn("select"); + unsafe { + llvm::LLVMBuildSelect(self.llbuilder, cond, then_val, else_val, noname()) + } + } + + pub fn va_arg(&self, list: ValueRef, ty: Type) -> ValueRef { + self.count_insn("vaarg"); + unsafe { + llvm::LLVMBuildVAArg(self.llbuilder, list, ty.to_ref(), noname()) + } + } + + pub fn extract_element(&self, vec: ValueRef, idx: ValueRef) -> ValueRef { + self.count_insn("extractelement"); + unsafe { + llvm::LLVMBuildExtractElement(self.llbuilder, vec, idx, noname()) + } + } + + pub fn insert_element(&self, vec: ValueRef, elt: ValueRef, idx: ValueRef) -> ValueRef { + self.count_insn("insertelement"); + unsafe { + llvm::LLVMBuildInsertElement(self.llbuilder, vec, elt, idx, noname()) + } + } + + pub fn shuffle_vector(&self, v1: ValueRef, v2: ValueRef, mask: ValueRef) -> ValueRef { + self.count_insn("shufflevector"); + unsafe { + llvm::LLVMBuildShuffleVector(self.llbuilder, v1, v2, mask, noname()) + } + } + + pub fn vector_splat(&self, num_elts: usize, elt: ValueRef) -> ValueRef { + unsafe { + let elt_ty = val_ty(elt); + let undef = llvm::LLVMGetUndef(Type::vector(&elt_ty, num_elts as u64).to_ref()); + let vec = self.insert_element(undef, elt, C_i32(self.cx, 0)); + let vec_i32_ty = Type::vector(&Type::i32(self.cx), num_elts as u64); + self.shuffle_vector(vec, undef, C_null(vec_i32_ty)) + } + } + + pub fn vector_reduce_fadd_fast(&self, acc: ValueRef, src: ValueRef) -> ValueRef { + self.count_insn("vector.reduce.fadd_fast"); + unsafe { + // FIXME: add a non-fast math version once + // https://bugs.llvm.org/show_bug.cgi?id=36732 + // is fixed. + let instr = llvm::LLVMRustBuildVectorReduceFAdd(self.llbuilder, acc, src); + if instr.is_null() { + bug!("LLVMRustBuildVectorReduceFAdd is not available in LLVM version < 5.0"); + } + llvm::LLVMRustSetHasUnsafeAlgebra(instr); + instr + } + } + pub fn vector_reduce_fmul_fast(&self, acc: ValueRef, src: ValueRef) -> ValueRef { + self.count_insn("vector.reduce.fmul_fast"); + unsafe { + // FIXME: add a non-fast math version once + // https://bugs.llvm.org/show_bug.cgi?id=36732 + // is fixed. + let instr = llvm::LLVMRustBuildVectorReduceFMul(self.llbuilder, acc, src); + if instr.is_null() { + bug!("LLVMRustBuildVectorReduceFMul is not available in LLVM version < 5.0"); + } + llvm::LLVMRustSetHasUnsafeAlgebra(instr); + instr + } + } + pub fn vector_reduce_add(&self, src: ValueRef) -> ValueRef { + self.count_insn("vector.reduce.add"); + unsafe { + let instr = llvm::LLVMRustBuildVectorReduceAdd(self.llbuilder, src); + if instr.is_null() { + bug!("LLVMRustBuildVectorReduceAdd is not available in LLVM version < 5.0"); + } + instr + } + } + pub fn vector_reduce_mul(&self, src: ValueRef) -> ValueRef { + self.count_insn("vector.reduce.mul"); + unsafe { + let instr = llvm::LLVMRustBuildVectorReduceMul(self.llbuilder, src); + if instr.is_null() { + bug!("LLVMRustBuildVectorReduceMul is not available in LLVM version < 5.0"); + } + instr + } + } + pub fn vector_reduce_and(&self, src: ValueRef) -> ValueRef { + self.count_insn("vector.reduce.and"); + unsafe { + let instr = llvm::LLVMRustBuildVectorReduceAnd(self.llbuilder, src); + if instr.is_null() { + bug!("LLVMRustBuildVectorReduceAnd is not available in LLVM version < 5.0"); + } + instr + } + } + pub fn vector_reduce_or(&self, src: ValueRef) -> ValueRef { + self.count_insn("vector.reduce.or"); + unsafe { + let instr = llvm::LLVMRustBuildVectorReduceOr(self.llbuilder, src); + if instr.is_null() { + bug!("LLVMRustBuildVectorReduceOr is not available in LLVM version < 5.0"); + } + instr + } + } + pub fn vector_reduce_xor(&self, src: ValueRef) -> ValueRef { + self.count_insn("vector.reduce.xor"); + unsafe { + let instr = llvm::LLVMRustBuildVectorReduceXor(self.llbuilder, src); + if instr.is_null() { + bug!("LLVMRustBuildVectorReduceXor is not available in LLVM version < 5.0"); + } + instr + } + } + pub fn vector_reduce_fmin(&self, src: ValueRef) -> ValueRef { + self.count_insn("vector.reduce.fmin"); + unsafe { + let instr = llvm::LLVMRustBuildVectorReduceFMin(self.llbuilder, src, /*NoNaNs:*/ false); + if instr.is_null() { + bug!("LLVMRustBuildVectorReduceFMin is not available in LLVM version < 5.0"); + } + instr + } + } + pub fn vector_reduce_fmax(&self, src: ValueRef) -> ValueRef { + self.count_insn("vector.reduce.fmax"); + unsafe { + let instr = llvm::LLVMRustBuildVectorReduceFMax(self.llbuilder, src, /*NoNaNs:*/ false); + if instr.is_null() { + bug!("LLVMRustBuildVectorReduceFMax is not available in LLVM version < 5.0"); + } + instr + } + } + pub fn vector_reduce_fmin_fast(&self, src: ValueRef) -> ValueRef { + self.count_insn("vector.reduce.fmin_fast"); + unsafe { + let instr = llvm::LLVMRustBuildVectorReduceFMin(self.llbuilder, src, /*NoNaNs:*/ true); + if instr.is_null() { + bug!("LLVMRustBuildVectorReduceFMin is not available in LLVM version < 5.0"); + } + llvm::LLVMRustSetHasUnsafeAlgebra(instr); + instr + } + } + pub fn vector_reduce_fmax_fast(&self, src: ValueRef) -> ValueRef { + self.count_insn("vector.reduce.fmax_fast"); + unsafe { + let instr = llvm::LLVMRustBuildVectorReduceFMax(self.llbuilder, src, /*NoNaNs:*/ true); + if instr.is_null() { + bug!("LLVMRustBuildVectorReduceFMax is not available in LLVM version < 5.0"); + } + llvm::LLVMRustSetHasUnsafeAlgebra(instr); + instr + } + } + pub fn vector_reduce_min(&self, src: ValueRef, is_signed: bool) -> ValueRef { + self.count_insn("vector.reduce.min"); + unsafe { + let instr = llvm::LLVMRustBuildVectorReduceMin(self.llbuilder, src, is_signed); + if instr.is_null() { + bug!("LLVMRustBuildVectorReduceMin is not available in LLVM version < 5.0"); + } + instr + } + } + pub fn vector_reduce_max(&self, src: ValueRef, is_signed: bool) -> ValueRef { + self.count_insn("vector.reduce.max"); + unsafe { + let instr = llvm::LLVMRustBuildVectorReduceMax(self.llbuilder, src, is_signed); + if instr.is_null() { + bug!("LLVMRustBuildVectorReduceMax is not available in LLVM version < 5.0"); + } + instr + } + } + + pub fn extract_value(&self, agg_val: ValueRef, idx: u64) -> ValueRef { + self.count_insn("extractvalue"); + assert_eq!(idx as c_uint as u64, idx); + unsafe { + llvm::LLVMBuildExtractValue(self.llbuilder, agg_val, idx as c_uint, noname()) + } + } + + pub fn insert_value(&self, agg_val: ValueRef, elt: ValueRef, + idx: u64) -> ValueRef { + self.count_insn("insertvalue"); + assert_eq!(idx as c_uint as u64, idx); + unsafe { + llvm::LLVMBuildInsertValue(self.llbuilder, agg_val, elt, idx as c_uint, + noname()) + } + } + + pub fn is_null(&self, val: ValueRef) -> ValueRef { + self.count_insn("isnull"); + unsafe { + llvm::LLVMBuildIsNull(self.llbuilder, val, noname()) + } + } + + pub fn is_not_null(&self, val: ValueRef) -> ValueRef { + self.count_insn("isnotnull"); + unsafe { + llvm::LLVMBuildIsNotNull(self.llbuilder, val, noname()) + } + } + + pub fn ptrdiff(&self, lhs: ValueRef, rhs: ValueRef) -> ValueRef { + self.count_insn("ptrdiff"); + unsafe { + llvm::LLVMBuildPtrDiff(self.llbuilder, lhs, rhs, noname()) + } + } + + pub fn trap(&self) { + unsafe { + let bb: BasicBlockRef = llvm::LLVMGetInsertBlock(self.llbuilder); + let fn_: ValueRef = llvm::LLVMGetBasicBlockParent(bb); + let m: ModuleRef = llvm::LLVMGetGlobalParent(fn_); + let p = "llvm.trap\0".as_ptr(); + let t: ValueRef = llvm::LLVMGetNamedFunction(m, p as *const _); + assert!((t as isize != 0)); + let args: &[ValueRef] = &[]; + self.count_insn("trap"); + llvm::LLVMRustBuildCall(self.llbuilder, t, + args.as_ptr(), args.len() as c_uint, + ptr::null_mut(), + noname()); + } + } + + pub fn landing_pad(&self, ty: Type, pers_fn: ValueRef, + num_clauses: usize) -> ValueRef { + self.count_insn("landingpad"); + unsafe { + llvm::LLVMBuildLandingPad(self.llbuilder, ty.to_ref(), pers_fn, + num_clauses as c_uint, noname()) + } + } + + pub fn add_clause(&self, landing_pad: ValueRef, clause: ValueRef) { + unsafe { + llvm::LLVMAddClause(landing_pad, clause); + } + } + + pub fn set_cleanup(&self, landing_pad: ValueRef) { + self.count_insn("setcleanup"); + unsafe { + llvm::LLVMSetCleanup(landing_pad, llvm::True); + } + } + + pub fn resume(&self, exn: ValueRef) -> ValueRef { + self.count_insn("resume"); + unsafe { + llvm::LLVMBuildResume(self.llbuilder, exn) + } + } + + pub fn cleanup_pad(&self, + parent: Option, + args: &[ValueRef]) -> ValueRef { + self.count_insn("cleanuppad"); + let parent = parent.unwrap_or(ptr::null_mut()); + let name = CString::new("cleanuppad").unwrap(); + let ret = unsafe { + llvm::LLVMRustBuildCleanupPad(self.llbuilder, + parent, + args.len() as c_uint, + args.as_ptr(), + name.as_ptr()) + }; + assert!(!ret.is_null(), "LLVM does not have support for cleanuppad"); + return ret + } + + pub fn cleanup_ret(&self, cleanup: ValueRef, + unwind: Option) -> ValueRef { + self.count_insn("cleanupret"); + let unwind = unwind.unwrap_or(ptr::null_mut()); + let ret = unsafe { + llvm::LLVMRustBuildCleanupRet(self.llbuilder, cleanup, unwind) + }; + assert!(!ret.is_null(), "LLVM does not have support for cleanupret"); + return ret + } + + pub fn catch_pad(&self, + parent: ValueRef, + args: &[ValueRef]) -> ValueRef { + self.count_insn("catchpad"); + let name = CString::new("catchpad").unwrap(); + let ret = unsafe { + llvm::LLVMRustBuildCatchPad(self.llbuilder, parent, + args.len() as c_uint, args.as_ptr(), + name.as_ptr()) + }; + assert!(!ret.is_null(), "LLVM does not have support for catchpad"); + return ret + } + + pub fn catch_ret(&self, pad: ValueRef, unwind: BasicBlockRef) -> ValueRef { + self.count_insn("catchret"); + let ret = unsafe { + llvm::LLVMRustBuildCatchRet(self.llbuilder, pad, unwind) + }; + assert!(!ret.is_null(), "LLVM does not have support for catchret"); + return ret + } + + pub fn catch_switch(&self, + parent: Option, + unwind: Option, + num_handlers: usize) -> ValueRef { + self.count_insn("catchswitch"); + let parent = parent.unwrap_or(ptr::null_mut()); + let unwind = unwind.unwrap_or(ptr::null_mut()); + let name = CString::new("catchswitch").unwrap(); + let ret = unsafe { + llvm::LLVMRustBuildCatchSwitch(self.llbuilder, parent, unwind, + num_handlers as c_uint, + name.as_ptr()) + }; + assert!(!ret.is_null(), "LLVM does not have support for catchswitch"); + return ret + } + + pub fn add_handler(&self, catch_switch: ValueRef, handler: BasicBlockRef) { + unsafe { + llvm::LLVMRustAddHandler(catch_switch, handler); + } + } + + pub fn set_personality_fn(&self, personality: ValueRef) { + unsafe { + llvm::LLVMSetPersonalityFn(self.llfn(), personality); + } + } + + // Atomic Operations + pub fn atomic_cmpxchg(&self, dst: ValueRef, + cmp: ValueRef, src: ValueRef, + order: AtomicOrdering, + failure_order: AtomicOrdering, + weak: llvm::Bool) -> ValueRef { + unsafe { + llvm::LLVMRustBuildAtomicCmpXchg(self.llbuilder, dst, cmp, src, + order, failure_order, weak) + } + } + pub fn atomic_rmw(&self, op: AtomicRmwBinOp, + dst: ValueRef, src: ValueRef, + order: AtomicOrdering) -> ValueRef { + unsafe { + llvm::LLVMBuildAtomicRMW(self.llbuilder, op, dst, src, order, False) + } + } + + pub fn atomic_fence(&self, order: AtomicOrdering, scope: SynchronizationScope) { + unsafe { + llvm::LLVMRustBuildAtomicFence(self.llbuilder, order, scope); + } + } + + pub fn add_case(&self, s: ValueRef, on_val: ValueRef, dest: BasicBlockRef) { + unsafe { + llvm::LLVMAddCase(s, on_val, dest) + } + } + + pub fn add_incoming_to_phi(&self, phi: ValueRef, val: ValueRef, bb: BasicBlockRef) { + unsafe { + llvm::LLVMAddIncoming(phi, &val, &bb, 1 as c_uint); + } + } + + pub fn set_invariant_load(&self, load: ValueRef) { + unsafe { + llvm::LLVMSetMetadata(load, llvm::MD_invariant_load as c_uint, + llvm::LLVMMDNodeInContext(self.cx.llcx, ptr::null(), 0)); + } + } + + /// Returns the ptr value that should be used for storing `val`. + fn check_store<'b>(&self, + val: ValueRef, + ptr: ValueRef) -> ValueRef { + let dest_ptr_ty = val_ty(ptr); + let stored_ty = val_ty(val); + let stored_ptr_ty = stored_ty.ptr_to(); + + assert_eq!(dest_ptr_ty.kind(), llvm::TypeKind::Pointer); + + if dest_ptr_ty == stored_ptr_ty { + ptr + } else { + debug!("Type mismatch in store. \ + Expected {:?}, got {:?}; inserting bitcast", + dest_ptr_ty, stored_ptr_ty); + self.bitcast(ptr, stored_ptr_ty) + } + } + + /// Returns the args that should be used for a call to `llfn`. + fn check_call<'b>(&self, + typ: &str, + llfn: ValueRef, + args: &'b [ValueRef]) -> Cow<'b, [ValueRef]> { + let mut fn_ty = val_ty(llfn); + // Strip off pointers + while fn_ty.kind() == llvm::TypeKind::Pointer { + fn_ty = fn_ty.element_type(); + } + + assert!(fn_ty.kind() == llvm::TypeKind::Function, + "builder::{} not passed a function, but {:?}", typ, fn_ty); + + let param_tys = fn_ty.func_params(); + + let all_args_match = param_tys.iter() + .zip(args.iter().map(|&v| val_ty(v))) + .all(|(expected_ty, actual_ty)| *expected_ty == actual_ty); + + if all_args_match { + return Cow::Borrowed(args); + } + + let casted_args: Vec<_> = param_tys.into_iter() + .zip(args.iter()) + .enumerate() + .map(|(i, (expected_ty, &actual_val))| { + let actual_ty = val_ty(actual_val); + if expected_ty != actual_ty { + debug!("Type mismatch in function call of {:?}. \ + Expected {:?} for param {}, got {:?}; injecting bitcast", + Value(llfn), + expected_ty, i, actual_ty); + self.bitcast(actual_val, expected_ty) + } else { + actual_val + } + }) + .collect(); + + return Cow::Owned(casted_args); + } + + pub fn lifetime_start(&self, ptr: ValueRef, size: Size) { + self.call_lifetime_intrinsic("llvm.lifetime.start", ptr, size); + } + + pub fn lifetime_end(&self, ptr: ValueRef, size: Size) { + self.call_lifetime_intrinsic("llvm.lifetime.end", ptr, size); + } + + /// If LLVM lifetime intrinsic support is enabled (i.e. optimizations + /// on), and `ptr` is nonzero-sized, then extracts the size of `ptr` + /// and the intrinsic for `lt` and passes them to `emit`, which is in + /// charge of generating code to call the passed intrinsic on whatever + /// block of generated code is targeted for the intrinsic. + /// + /// If LLVM lifetime intrinsic support is disabled (i.e. optimizations + /// off) or `ptr` is zero-sized, then no-op (does not call `emit`). + fn call_lifetime_intrinsic(&self, intrinsic: &str, ptr: ValueRef, size: Size) { + if self.cx.sess().opts.optimize == config::OptLevel::No { + return; + } + + let size = size.bytes(); + if size == 0 { + return; + } + + let lifetime_intrinsic = self.cx.get_intrinsic(intrinsic); + + let ptr = self.pointercast(ptr, Type::i8p(self.cx)); + self.call(lifetime_intrinsic, &[C_u64(self.cx, size), ptr], None); + } +} diff --git a/src/librustc_codegen_llvm/callee.rs b/src/librustc_codegen_llvm/callee.rs new file mode 100644 index 00000000000..a3dbc450ce7 --- /dev/null +++ b/src/librustc_codegen_llvm/callee.rs @@ -0,0 +1,232 @@ +// Copyright 2012 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. + +//! Handles codegen of callees as well as other call-related +//! things. Callees are a superset of normal rust values and sometimes +//! have different representations. In particular, top-level fn items +//! and methods are represented as just a fn ptr and not a full +//! closure. + +use attributes; +use common::{self, CodegenCx}; +use consts; +use declare; +use llvm::{self, ValueRef}; +use monomorphize::Instance; +use type_of::LayoutLlvmExt; + +use rustc::hir::def_id::DefId; +use rustc::ty::{self, TypeFoldable}; +use rustc::ty::layout::LayoutOf; +use rustc::ty::subst::Substs; +use rustc_target::spec::PanicStrategy; + +/// Codegens a reference to a fn/method item, monomorphizing and +/// inlining as it goes. +/// +/// # Parameters +/// +/// - `cx`: the crate context +/// - `instance`: the instance to be instantiated +pub fn get_fn<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, + instance: Instance<'tcx>) + -> ValueRef +{ + let tcx = cx.tcx; + + debug!("get_fn(instance={:?})", instance); + + assert!(!instance.substs.needs_infer()); + assert!(!instance.substs.has_escaping_regions()); + assert!(!instance.substs.has_param_types()); + + let fn_ty = instance.ty(cx.tcx); + if let Some(&llfn) = cx.instances.borrow().get(&instance) { + return llfn; + } + + let sym = tcx.symbol_name(instance).as_str(); + debug!("get_fn({:?}: {:?}) => {}", instance, fn_ty, sym); + + // Create a fn pointer with the substituted signature. + let fn_ptr_ty = tcx.mk_fn_ptr(common::ty_fn_sig(cx, fn_ty)); + let llptrty = cx.layout_of(fn_ptr_ty).llvm_type(cx); + + let llfn = if let Some(llfn) = declare::get_declared_value(cx, &sym) { + // This is subtle and surprising, but sometimes we have to bitcast + // the resulting fn pointer. The reason has to do with external + // functions. If you have two crates that both bind the same C + // library, they may not use precisely the same types: for + // example, they will probably each declare their own structs, + // which are distinct types from LLVM's point of view (nominal + // types). + // + // Now, if those two crates are linked into an application, and + // they contain inlined code, you can wind up with a situation + // where both of those functions wind up being loaded into this + // application simultaneously. In that case, the same function + // (from LLVM's point of view) requires two types. But of course + // LLVM won't allow one function to have two types. + // + // What we currently do, therefore, is declare the function with + // one of the two types (whichever happens to come first) and then + // bitcast as needed when the function is referenced to make sure + // it has the type we expect. + // + // This can occur on either a crate-local or crate-external + // reference. It also occurs when testing libcore and in some + // other weird situations. Annoying. + if common::val_ty(llfn) != llptrty { + debug!("get_fn: casting {:?} to {:?}", llfn, llptrty); + consts::ptrcast(llfn, llptrty) + } else { + debug!("get_fn: not casting pointer!"); + llfn + } + } else { + let llfn = declare::declare_fn(cx, &sym, fn_ty); + assert_eq!(common::val_ty(llfn), llptrty); + debug!("get_fn: not casting pointer!"); + + if instance.def.is_inline(tcx) { + attributes::inline(llfn, attributes::InlineAttr::Hint); + } + attributes::from_fn_attrs(cx, llfn, instance.def.def_id()); + + let instance_def_id = instance.def_id(); + + // Perhaps questionable, but we assume that anything defined + // *in Rust code* may unwind. Foreign items like `extern "C" { + // fn foo(); }` are assumed not to unwind **unless** they have + // a `#[unwind]` attribute. + if tcx.sess.panic_strategy() == PanicStrategy::Unwind { + if !tcx.is_foreign_item(instance_def_id) { + attributes::unwind(llfn, true); + } + } + + // Apply an appropriate linkage/visibility value to our item that we + // just declared. + // + // This is sort of subtle. Inside our codegen unit we started off + // compilation by predefining all our own `MonoItem` instances. That + // is, everything we're codegenning ourselves is already defined. That + // means that anything we're actually codegenning in this codegen unit + // will have hit the above branch in `get_declared_value`. As a result, + // we're guaranteed here that we're declaring a symbol that won't get + // defined, or in other words we're referencing a value from another + // codegen unit or even another crate. + // + // So because this is a foreign value we blanket apply an external + // linkage directive because it's coming from a different object file. + // The visibility here is where it gets tricky. This symbol could be + // referencing some foreign crate or foreign library (an `extern` + // block) in which case we want to leave the default visibility. We may + // also, though, have multiple codegen units. It could be a + // monomorphization, in which case its expected visibility depends on + // whether we are sharing generics or not. The important thing here is + // that the visibility we apply to the declaration is the same one that + // has been applied to the definition (wherever that definition may be). + unsafe { + llvm::LLVMRustSetLinkage(llfn, llvm::Linkage::ExternalLinkage); + + let is_generic = instance.substs.types().next().is_some(); + + if is_generic { + // This is a monomorphization. Its expected visibility depends + // on whether we are in share-generics mode. + + if cx.tcx.share_generics() { + // We are in share_generics mode. + + if instance_def_id.is_local() { + // This is a definition from the current crate. If the + // definition is unreachable for downstream crates or + // the current crate does not re-export generics, the + // definition of the instance will have been declared + // as `hidden`. + if cx.tcx.is_unreachable_local_definition(instance_def_id) || + !cx.tcx.local_crate_exports_generics() { + llvm::LLVMRustSetVisibility(llfn, llvm::Visibility::Hidden); + } + } else { + // This is a monomorphization of a generic function + // defined in an upstream crate. + if cx.tcx.upstream_monomorphizations_for(instance_def_id) + .map(|set| set.contains_key(instance.substs)) + .unwrap_or(false) { + // This is instantiated in another crate. It cannot + // be `hidden`. + } else { + // This is a local instantiation of an upstream definition. + // If the current crate does not re-export it + // (because it is a C library or an executable), it + // will have been declared `hidden`. + if !cx.tcx.local_crate_exports_generics() { + llvm::LLVMRustSetVisibility(llfn, llvm::Visibility::Hidden); + } + } + } + } else { + // When not sharing generics, all instances are in the same + // crate and have hidden visibility + llvm::LLVMRustSetVisibility(llfn, llvm::Visibility::Hidden); + } + } else { + // This is a non-generic function + if cx.tcx.is_codegened_item(instance_def_id) { + // This is a function that is instantiated in the local crate + + if instance_def_id.is_local() { + // This is function that is defined in the local crate. + // If it is not reachable, it is hidden. + if !cx.tcx.is_reachable_non_generic(instance_def_id) { + llvm::LLVMRustSetVisibility(llfn, llvm::Visibility::Hidden); + } + } else { + // This is a function from an upstream crate that has + // been instantiated here. These are always hidden. + llvm::LLVMRustSetVisibility(llfn, llvm::Visibility::Hidden); + } + } + } + } + + if cx.use_dll_storage_attrs && + tcx.is_dllimport_foreign_item(instance_def_id) + { + unsafe { + llvm::LLVMSetDLLStorageClass(llfn, llvm::DLLStorageClass::DllImport); + } + } + + llfn + }; + + cx.instances.borrow_mut().insert(instance, llfn); + + llfn +} + +pub fn resolve_and_get_fn<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, + def_id: DefId, + substs: &'tcx Substs<'tcx>) + -> ValueRef +{ + get_fn( + cx, + ty::Instance::resolve( + cx.tcx, + ty::ParamEnv::reveal_all(), + def_id, + substs + ).unwrap() + ) +} diff --git a/src/librustc_codegen_llvm/common.rs b/src/librustc_codegen_llvm/common.rs new file mode 100644 index 00000000000..10ca8e43ce6 --- /dev/null +++ b/src/librustc_codegen_llvm/common.rs @@ -0,0 +1,448 @@ +// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![allow(non_camel_case_types, non_snake_case)] + +//! Code that is useful in various codegen modules. + +use llvm; +use llvm::{ValueRef, ContextRef, TypeKind}; +use llvm::{True, False, Bool, OperandBundleDef}; +use rustc::hir::def_id::DefId; +use rustc::middle::lang_items::LangItem; +use abi; +use base; +use builder::Builder; +use consts; +use declare; +use type_::Type; +use type_of::LayoutLlvmExt; +use value::Value; +use rustc::ty::{self, Ty, TyCtxt}; +use rustc::ty::layout::{HasDataLayout, LayoutOf}; +use rustc::hir; + +use libc::{c_uint, c_char}; +use std::iter; + +use rustc_target::spec::abi::Abi; +use syntax::symbol::LocalInternedString; +use syntax_pos::{Span, DUMMY_SP}; + +pub use context::CodegenCx; + +pub fn type_needs_drop<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, ty: Ty<'tcx>) -> bool { + ty.needs_drop(tcx, ty::ParamEnv::reveal_all()) +} + +pub fn type_is_sized<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, ty: Ty<'tcx>) -> bool { + ty.is_sized(tcx.at(DUMMY_SP), ty::ParamEnv::reveal_all()) +} + +pub fn type_is_freeze<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, ty: Ty<'tcx>) -> bool { + ty.is_freeze(tcx, ty::ParamEnv::reveal_all(), DUMMY_SP) +} + +/* +* A note on nomenclature of linking: "extern", "foreign", and "upcall". +* +* An "extern" is an LLVM symbol we wind up emitting an undefined external +* reference to. This means "we don't have the thing in this compilation unit, +* please make sure you link it in at runtime". This could be a reference to +* C code found in a C library, or rust code found in a rust crate. +* +* Most "externs" are implicitly declared (automatically) as a result of a +* user declaring an extern _module_ dependency; this causes the rust driver +* to locate an extern crate, scan its compilation metadata, and emit extern +* declarations for any symbols used by the declaring crate. +* +* A "foreign" is an extern that references C (or other non-rust ABI) code. +* There is no metadata to scan for extern references so in these cases either +* a header-digester like bindgen, or manual function prototypes, have to +* serve as declarators. So these are usually given explicitly as prototype +* declarations, in rust code, with ABI attributes on them noting which ABI to +* link via. +* +* An "upcall" is a foreign call generated by the compiler (not corresponding +* to any user-written call in the code) into the runtime library, to perform +* some helper task such as bringing a task to life, allocating memory, etc. +* +*/ + +/// A structure representing an active landing pad for the duration of a basic +/// block. +/// +/// Each `Block` may contain an instance of this, indicating whether the block +/// is part of a landing pad or not. This is used to make decision about whether +/// to emit `invoke` instructions (e.g. in a landing pad we don't continue to +/// use `invoke`) and also about various function call metadata. +/// +/// For GNU exceptions (`landingpad` + `resume` instructions) this structure is +/// just a bunch of `None` instances (not too interesting), but for MSVC +/// exceptions (`cleanuppad` + `cleanupret` instructions) this contains data. +/// When inside of a landing pad, each function call in LLVM IR needs to be +/// annotated with which landing pad it's a part of. This is accomplished via +/// the `OperandBundleDef` value created for MSVC landing pads. +pub struct Funclet { + cleanuppad: ValueRef, + operand: OperandBundleDef, +} + +impl Funclet { + pub fn new(cleanuppad: ValueRef) -> Funclet { + Funclet { + cleanuppad, + operand: OperandBundleDef::new("funclet", &[cleanuppad]), + } + } + + pub fn cleanuppad(&self) -> ValueRef { + self.cleanuppad + } + + pub fn bundle(&self) -> &OperandBundleDef { + &self.operand + } +} + +pub fn val_ty(v: ValueRef) -> Type { + unsafe { + Type::from_ref(llvm::LLVMTypeOf(v)) + } +} + +// LLVM constant constructors. +pub fn C_null(t: Type) -> ValueRef { + unsafe { + llvm::LLVMConstNull(t.to_ref()) + } +} + +pub fn C_undef(t: Type) -> ValueRef { + unsafe { + llvm::LLVMGetUndef(t.to_ref()) + } +} + +pub fn C_int(t: Type, i: i64) -> ValueRef { + unsafe { + llvm::LLVMConstInt(t.to_ref(), i as u64, True) + } +} + +pub fn C_uint(t: Type, i: u64) -> ValueRef { + unsafe { + llvm::LLVMConstInt(t.to_ref(), i, False) + } +} + +pub fn C_uint_big(t: Type, u: u128) -> ValueRef { + unsafe { + let words = [u as u64, (u >> 64) as u64]; + llvm::LLVMConstIntOfArbitraryPrecision(t.to_ref(), 2, words.as_ptr()) + } +} + +pub fn C_bool(cx: &CodegenCx, val: bool) -> ValueRef { + C_uint(Type::i1(cx), val as u64) +} + +pub fn C_i32(cx: &CodegenCx, i: i32) -> ValueRef { + C_int(Type::i32(cx), i as i64) +} + +pub fn C_u32(cx: &CodegenCx, i: u32) -> ValueRef { + C_uint(Type::i32(cx), i as u64) +} + +pub fn C_u64(cx: &CodegenCx, i: u64) -> ValueRef { + C_uint(Type::i64(cx), i) +} + +pub fn C_usize(cx: &CodegenCx, i: u64) -> ValueRef { + let bit_size = cx.data_layout().pointer_size.bits(); + if bit_size < 64 { + // make sure it doesn't overflow + assert!(i < (1< ValueRef { + C_uint(Type::i8(cx), i as u64) +} + + +// This is a 'c-like' raw string, which differs from +// our boxed-and-length-annotated strings. +pub fn C_cstr(cx: &CodegenCx, s: LocalInternedString, null_terminated: bool) -> ValueRef { + unsafe { + if let Some(&llval) = cx.const_cstr_cache.borrow().get(&s) { + return llval; + } + + let sc = llvm::LLVMConstStringInContext(cx.llcx, + s.as_ptr() as *const c_char, + s.len() as c_uint, + !null_terminated as Bool); + let sym = cx.generate_local_symbol_name("str"); + let g = declare::define_global(cx, &sym[..], val_ty(sc)).unwrap_or_else(||{ + bug!("symbol `{}` is already defined", sym); + }); + llvm::LLVMSetInitializer(g, sc); + llvm::LLVMSetGlobalConstant(g, True); + llvm::LLVMRustSetLinkage(g, llvm::Linkage::InternalLinkage); + + cx.const_cstr_cache.borrow_mut().insert(s, g); + g + } +} + +// NB: Do not use `do_spill_noroot` to make this into a constant string, or +// you will be kicked off fast isel. See issue #4352 for an example of this. +pub fn C_str_slice(cx: &CodegenCx, s: LocalInternedString) -> ValueRef { + let len = s.len(); + let cs = consts::ptrcast(C_cstr(cx, s, false), + cx.layout_of(cx.tcx.mk_str()).llvm_type(cx).ptr_to()); + C_fat_ptr(cx, cs, C_usize(cx, len as u64)) +} + +pub fn C_fat_ptr(cx: &CodegenCx, ptr: ValueRef, meta: ValueRef) -> ValueRef { + assert_eq!(abi::FAT_PTR_ADDR, 0); + assert_eq!(abi::FAT_PTR_EXTRA, 1); + C_struct(cx, &[ptr, meta], false) +} + +pub fn C_struct(cx: &CodegenCx, elts: &[ValueRef], packed: bool) -> ValueRef { + C_struct_in_context(cx.llcx, elts, packed) +} + +pub fn C_struct_in_context(llcx: ContextRef, elts: &[ValueRef], packed: bool) -> ValueRef { + unsafe { + llvm::LLVMConstStructInContext(llcx, + elts.as_ptr(), elts.len() as c_uint, + packed as Bool) + } +} + +pub fn C_array(ty: Type, elts: &[ValueRef]) -> ValueRef { + unsafe { + return llvm::LLVMConstArray(ty.to_ref(), elts.as_ptr(), elts.len() as c_uint); + } +} + +pub fn C_vector(elts: &[ValueRef]) -> ValueRef { + unsafe { + return llvm::LLVMConstVector(elts.as_ptr(), elts.len() as c_uint); + } +} + +pub fn C_bytes(cx: &CodegenCx, bytes: &[u8]) -> ValueRef { + C_bytes_in_context(cx.llcx, bytes) +} + +pub fn C_bytes_in_context(llcx: ContextRef, bytes: &[u8]) -> ValueRef { + unsafe { + let ptr = bytes.as_ptr() as *const c_char; + return llvm::LLVMConstStringInContext(llcx, ptr, bytes.len() as c_uint, True); + } +} + +pub fn const_get_elt(v: ValueRef, idx: u64) -> ValueRef { + unsafe { + assert_eq!(idx as c_uint as u64, idx); + let us = &[idx as c_uint]; + let r = llvm::LLVMConstExtractValue(v, us.as_ptr(), us.len() as c_uint); + + debug!("const_get_elt(v={:?}, idx={}, r={:?})", + Value(v), idx, Value(r)); + + r + } +} + +pub fn const_get_real(v: ValueRef) -> Option<(f64, bool)> { + unsafe { + if is_const_real(v) { + let mut loses_info: llvm::Bool = ::std::mem::uninitialized(); + let r = llvm::LLVMConstRealGetDouble(v, &mut loses_info as *mut llvm::Bool); + let loses_info = if loses_info == 1 { true } else { false }; + Some((r, loses_info)) + } else { + None + } + } +} + +pub fn const_to_uint(v: ValueRef) -> u64 { + unsafe { + llvm::LLVMConstIntGetZExtValue(v) + } +} + +pub fn is_const_integral(v: ValueRef) -> bool { + unsafe { + !llvm::LLVMIsAConstantInt(v).is_null() + } +} + +pub fn is_const_real(v: ValueRef) -> bool { + unsafe { + !llvm::LLVMIsAConstantFP(v).is_null() + } +} + + +#[inline] +fn hi_lo_to_u128(lo: u64, hi: u64) -> u128 { + ((hi as u128) << 64) | (lo as u128) +} + +pub fn const_to_opt_u128(v: ValueRef, sign_ext: bool) -> Option { + unsafe { + if is_const_integral(v) { + let (mut lo, mut hi) = (0u64, 0u64); + let success = llvm::LLVMRustConstInt128Get(v, sign_ext, + &mut hi as *mut u64, &mut lo as *mut u64); + if success { + Some(hi_lo_to_u128(lo, hi)) + } else { + None + } + } else { + None + } + } +} + +pub fn langcall(tcx: TyCtxt, + span: Option, + msg: &str, + li: LangItem) + -> DefId { + match tcx.lang_items().require(li) { + Ok(id) => id, + Err(s) => { + let msg = format!("{} {}", msg, s); + match span { + Some(span) => tcx.sess.span_fatal(span, &msg[..]), + None => tcx.sess.fatal(&msg[..]), + } + } + } +} + +// To avoid UB from LLVM, these two functions mask RHS with an +// appropriate mask unconditionally (i.e. the fallback behavior for +// all shifts). For 32- and 64-bit types, this matches the semantics +// of Java. (See related discussion on #1877 and #10183.) + +pub fn build_unchecked_lshift<'a, 'tcx>( + bx: &Builder<'a, 'tcx>, + lhs: ValueRef, + rhs: ValueRef +) -> ValueRef { + let rhs = base::cast_shift_expr_rhs(bx, hir::BinOp_::BiShl, lhs, rhs); + // #1877, #10183: Ensure that input is always valid + let rhs = shift_mask_rhs(bx, rhs); + bx.shl(lhs, rhs) +} + +pub fn build_unchecked_rshift<'a, 'tcx>( + bx: &Builder<'a, 'tcx>, lhs_t: Ty<'tcx>, lhs: ValueRef, rhs: ValueRef +) -> ValueRef { + let rhs = base::cast_shift_expr_rhs(bx, hir::BinOp_::BiShr, lhs, rhs); + // #1877, #10183: Ensure that input is always valid + let rhs = shift_mask_rhs(bx, rhs); + let is_signed = lhs_t.is_signed(); + if is_signed { + bx.ashr(lhs, rhs) + } else { + bx.lshr(lhs, rhs) + } +} + +fn shift_mask_rhs<'a, 'tcx>(bx: &Builder<'a, 'tcx>, rhs: ValueRef) -> ValueRef { + let rhs_llty = val_ty(rhs); + bx.and(rhs, shift_mask_val(bx, rhs_llty, rhs_llty, false)) +} + +pub fn shift_mask_val<'a, 'tcx>( + bx: &Builder<'a, 'tcx>, + llty: Type, + mask_llty: Type, + invert: bool +) -> ValueRef { + let kind = llty.kind(); + match kind { + TypeKind::Integer => { + // i8/u8 can shift by at most 7, i16/u16 by at most 15, etc. + let val = llty.int_width() - 1; + if invert { + C_int(mask_llty, !val as i64) + } else { + C_uint(mask_llty, val) + } + }, + TypeKind::Vector => { + let mask = shift_mask_val(bx, llty.element_type(), mask_llty.element_type(), invert); + bx.vector_splat(mask_llty.vector_length(), mask) + }, + _ => bug!("shift_mask_val: expected Integer or Vector, found {:?}", kind), + } +} + +pub fn ty_fn_sig<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, + ty: Ty<'tcx>) + -> ty::PolyFnSig<'tcx> +{ + match ty.sty { + ty::TyFnDef(..) | + // Shims currently have type TyFnPtr. Not sure this should remain. + ty::TyFnPtr(_) => ty.fn_sig(cx.tcx), + ty::TyClosure(def_id, substs) => { + let tcx = cx.tcx; + let sig = substs.closure_sig(def_id, tcx); + + let env_ty = tcx.closure_env_ty(def_id, substs).unwrap(); + sig.map_bound(|sig| tcx.mk_fn_sig( + iter::once(*env_ty.skip_binder()).chain(sig.inputs().iter().cloned()), + sig.output(), + sig.variadic, + sig.unsafety, + sig.abi + )) + } + ty::TyGenerator(def_id, substs, _) => { + let tcx = cx.tcx; + let sig = substs.poly_sig(def_id, cx.tcx); + + let env_region = ty::ReLateBound(ty::DebruijnIndex::new(1), ty::BrEnv); + let env_ty = tcx.mk_mut_ref(tcx.mk_region(env_region), ty); + + sig.map_bound(|sig| { + let state_did = tcx.lang_items().gen_state().unwrap(); + let state_adt_ref = tcx.adt_def(state_did); + let state_substs = tcx.mk_substs([sig.yield_ty.into(), + sig.return_ty.into()].iter()); + let ret_ty = tcx.mk_adt(state_adt_ref, state_substs); + + tcx.mk_fn_sig(iter::once(env_ty), + ret_ty, + false, + hir::Unsafety::Normal, + Abi::Rust + ) + }) + } + _ => bug!("unexpected type {:?} to ty_fn_sig", ty) + } +} diff --git a/src/librustc_codegen_llvm/consts.rs b/src/librustc_codegen_llvm/consts.rs new file mode 100644 index 00000000000..afa81465ea2 --- /dev/null +++ b/src/librustc_codegen_llvm/consts.rs @@ -0,0 +1,322 @@ +// Copyright 2012 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use llvm; +use llvm::{SetUnnamedAddr}; +use llvm::{ValueRef, True}; +use rustc::hir::def_id::DefId; +use rustc::hir::map as hir_map; +use debuginfo; +use base; +use monomorphize::MonoItem; +use common::{CodegenCx, val_ty}; +use declare; +use monomorphize::Instance; +use type_::Type; +use type_of::LayoutLlvmExt; +use rustc::ty; +use rustc::ty::layout::{Align, LayoutOf}; + +use rustc::hir; + +use std::ffi::{CStr, CString}; +use syntax::ast; +use syntax::attr; + +pub fn ptrcast(val: ValueRef, ty: Type) -> ValueRef { + unsafe { + llvm::LLVMConstPointerCast(val, ty.to_ref()) + } +} + +pub fn bitcast(val: ValueRef, ty: Type) -> ValueRef { + unsafe { + llvm::LLVMConstBitCast(val, ty.to_ref()) + } +} + +fn set_global_alignment(cx: &CodegenCx, + gv: ValueRef, + mut align: Align) { + // The target may require greater alignment for globals than the type does. + // Note: GCC and Clang also allow `__attribute__((aligned))` on variables, + // which can force it to be smaller. Rust doesn't support this yet. + if let Some(min) = cx.sess().target.target.options.min_global_align { + match ty::layout::Align::from_bits(min, min) { + Ok(min) => align = align.max(min), + Err(err) => { + cx.sess().err(&format!("invalid minimum global alignment: {}", err)); + } + } + } + unsafe { + llvm::LLVMSetAlignment(gv, align.abi() as u32); + } +} + +pub fn addr_of_mut(cx: &CodegenCx, + cv: ValueRef, + align: Align, + kind: &str) + -> ValueRef { + unsafe { + let name = cx.generate_local_symbol_name(kind); + let gv = declare::define_global(cx, &name[..], val_ty(cv)).unwrap_or_else(||{ + bug!("symbol `{}` is already defined", name); + }); + llvm::LLVMSetInitializer(gv, cv); + set_global_alignment(cx, gv, align); + llvm::LLVMRustSetLinkage(gv, llvm::Linkage::PrivateLinkage); + SetUnnamedAddr(gv, true); + gv + } +} + +pub fn addr_of(cx: &CodegenCx, + cv: ValueRef, + align: Align, + kind: &str) + -> ValueRef { + if let Some(&gv) = cx.const_globals.borrow().get(&cv) { + unsafe { + // Upgrade the alignment in cases where the same constant is used with different + // alignment requirements + let llalign = align.abi() as u32; + if llalign > llvm::LLVMGetAlignment(gv) { + llvm::LLVMSetAlignment(gv, llalign); + } + } + return gv; + } + let gv = addr_of_mut(cx, cv, align, kind); + unsafe { + llvm::LLVMSetGlobalConstant(gv, True); + } + cx.const_globals.borrow_mut().insert(cv, gv); + gv +} + +pub fn get_static(cx: &CodegenCx, def_id: DefId) -> ValueRef { + let instance = Instance::mono(cx.tcx, def_id); + if let Some(&g) = cx.instances.borrow().get(&instance) { + return g; + } + + let defined_in_current_codegen_unit = cx.codegen_unit + .items() + .contains_key(&MonoItem::Static(def_id)); + assert!(!defined_in_current_codegen_unit, + "consts::get_static() should always hit the cache for \ + statics defined in the same CGU, but did not for `{:?}`", + def_id); + + let ty = instance.ty(cx.tcx); + let sym = cx.tcx.symbol_name(instance).as_str(); + + let g = if let Some(id) = cx.tcx.hir.as_local_node_id(def_id) { + + let llty = cx.layout_of(ty).llvm_type(cx); + let (g, attrs) = match cx.tcx.hir.get(id) { + hir_map::NodeItem(&hir::Item { + ref attrs, span, node: hir::ItemStatic(..), .. + }) => { + if declare::get_declared_value(cx, &sym[..]).is_some() { + span_bug!(span, "Conflicting symbol names for static?"); + } + + let g = declare::define_global(cx, &sym[..], llty).unwrap(); + + if !cx.tcx.is_reachable_non_generic(def_id) { + unsafe { + llvm::LLVMRustSetVisibility(g, llvm::Visibility::Hidden); + } + } + + (g, attrs) + } + + hir_map::NodeForeignItem(&hir::ForeignItem { + ref attrs, span, node: hir::ForeignItemStatic(..), .. + }) => { + let g = if let Some(linkage) = cx.tcx.codegen_fn_attrs(def_id).linkage { + // If this is a static with a linkage specified, then we need to handle + // it a little specially. The typesystem prevents things like &T and + // extern "C" fn() from being non-null, so we can't just declare a + // static and call it a day. Some linkages (like weak) will make it such + // that the static actually has a null value. + let llty2 = match ty.sty { + ty::TyRawPtr(ref mt) => cx.layout_of(mt.ty).llvm_type(cx), + _ => { + cx.sess().span_fatal(span, "must have type `*const T` or `*mut T`"); + } + }; + unsafe { + // Declare a symbol `foo` with the desired linkage. + let g1 = declare::declare_global(cx, &sym, llty2); + llvm::LLVMRustSetLinkage(g1, base::linkage_to_llvm(linkage)); + + // Declare an internal global `extern_with_linkage_foo` which + // is initialized with the address of `foo`. If `foo` is + // discarded during linking (for example, if `foo` has weak + // linkage and there are no definitions), then + // `extern_with_linkage_foo` will instead be initialized to + // zero. + let mut real_name = "_rust_extern_with_linkage_".to_string(); + real_name.push_str(&sym); + let g2 = declare::define_global(cx, &real_name, llty).unwrap_or_else(||{ + cx.sess().span_fatal(span, + &format!("symbol `{}` is already defined", &sym)) + }); + llvm::LLVMRustSetLinkage(g2, llvm::Linkage::InternalLinkage); + llvm::LLVMSetInitializer(g2, g1); + g2 + } + } else { + // Generate an external declaration. + declare::declare_global(cx, &sym, llty) + }; + + (g, attrs) + } + + item => bug!("get_static: expected static, found {:?}", item) + }; + + for attr in attrs { + if attr.check_name("thread_local") { + llvm::set_thread_local_mode(g, cx.tls_model); + } + } + + g + } else { + // FIXME(nagisa): perhaps the map of externs could be offloaded to llvm somehow? + // FIXME(nagisa): investigate whether it can be changed into define_global + let g = declare::declare_global(cx, &sym, cx.layout_of(ty).llvm_type(cx)); + // Thread-local statics in some other crate need to *always* be linked + // against in a thread-local fashion, so we need to be sure to apply the + // thread-local attribute locally if it was present remotely. If we + // don't do this then linker errors can be generated where the linker + // complains that one object files has a thread local version of the + // symbol and another one doesn't. + for attr in cx.tcx.get_attrs(def_id).iter() { + if attr.check_name("thread_local") { + llvm::set_thread_local_mode(g, cx.tls_model); + } + } + if cx.use_dll_storage_attrs && !cx.tcx.is_foreign_item(def_id) { + // This item is external but not foreign, i.e. it originates from an external Rust + // crate. Since we don't know whether this crate will be linked dynamically or + // statically in the final application, we always mark such symbols as 'dllimport'. + // If final linkage happens to be static, we rely on compiler-emitted __imp_ stubs to + // make things work. + // + // However, in some scenarios we defer emission of statics to downstream + // crates, so there are cases where a static with an upstream DefId + // is actually present in the current crate. We can find out via the + // is_codegened_item query. + if !cx.tcx.is_codegened_item(def_id) { + unsafe { + llvm::LLVMSetDLLStorageClass(g, llvm::DLLStorageClass::DllImport); + } + } + } + g + }; + + if cx.use_dll_storage_attrs && cx.tcx.is_dllimport_foreign_item(def_id) { + // For foreign (native) libs we know the exact storage type to use. + unsafe { + llvm::LLVMSetDLLStorageClass(g, llvm::DLLStorageClass::DllImport); + } + } + + cx.instances.borrow_mut().insert(instance, g); + cx.statics.borrow_mut().insert(g, def_id); + g +} + +pub fn codegen_static<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, + def_id: DefId, + is_mutable: bool, + attrs: &[ast::Attribute]) { + unsafe { + let g = get_static(cx, def_id); + + let v = match ::mir::codegen_static_initializer(cx, def_id) { + Ok(v) => v, + // Error has already been reported + Err(_) => return, + }; + + // boolean SSA values are i1, but they have to be stored in i8 slots, + // otherwise some LLVM optimization passes don't work as expected + let mut val_llty = val_ty(v); + let v = if val_llty == Type::i1(cx) { + val_llty = Type::i8(cx); + llvm::LLVMConstZExt(v, val_llty.to_ref()) + } else { + v + }; + + let instance = Instance::mono(cx.tcx, def_id); + let ty = instance.ty(cx.tcx); + let llty = cx.layout_of(ty).llvm_type(cx); + let g = if val_llty == llty { + g + } else { + // If we created the global with the wrong type, + // correct the type. + let empty_string = CString::new("").unwrap(); + let name_str_ref = CStr::from_ptr(llvm::LLVMGetValueName(g)); + let name_string = CString::new(name_str_ref.to_bytes()).unwrap(); + llvm::LLVMSetValueName(g, empty_string.as_ptr()); + + let linkage = llvm::LLVMRustGetLinkage(g); + let visibility = llvm::LLVMRustGetVisibility(g); + + let new_g = llvm::LLVMRustGetOrInsertGlobal( + cx.llmod, name_string.as_ptr(), val_llty.to_ref()); + + llvm::LLVMRustSetLinkage(new_g, linkage); + llvm::LLVMRustSetVisibility(new_g, visibility); + + // To avoid breaking any invariants, we leave around the old + // global for the moment; we'll replace all references to it + // with the new global later. (See base::codegen_backend.) + cx.statics_to_rauw.borrow_mut().push((g, new_g)); + new_g + }; + set_global_alignment(cx, g, cx.align_of(ty)); + llvm::LLVMSetInitializer(g, v); + + // As an optimization, all shared statics which do not have interior + // mutability are placed into read-only memory. + if !is_mutable { + if cx.type_is_freeze(ty) { + llvm::LLVMSetGlobalConstant(g, llvm::True); + } + } + + debuginfo::create_global_var_metadata(cx, def_id, g); + + if attr::contains_name(attrs, "thread_local") { + llvm::set_thread_local_mode(g, cx.tls_model); + } + + base::set_link_section(cx, g, attrs); + + if attr::contains_name(attrs, "used") { + // This static will be stored in the llvm.used variable which is an array of i8* + let cast = llvm::LLVMConstPointerCast(g, Type::i8p(cx).to_ref()); + cx.used_statics.borrow_mut().push(cast); + } + } +} diff --git a/src/librustc_codegen_llvm/context.rs b/src/librustc_codegen_llvm/context.rs new file mode 100644 index 00000000000..9b1de3d44ea --- /dev/null +++ b/src/librustc_codegen_llvm/context.rs @@ -0,0 +1,666 @@ +// Copyright 2013 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use common; +use llvm; +use llvm::{ContextRef, ModuleRef, ValueRef}; +use rustc::dep_graph::DepGraphSafe; +use rustc::hir; +use rustc::hir::def_id::DefId; +use debuginfo; +use callee; +use base; +use declare; +use monomorphize::Instance; + +use monomorphize::partitioning::CodegenUnit; +use type_::Type; +use type_of::PointeeInfo; + +use rustc_data_structures::base_n; +use rustc::mir::mono::Stats; +use rustc::session::config::{self, NoDebugInfo}; +use rustc::session::Session; +use rustc::ty::layout::{LayoutError, LayoutOf, Size, TyLayout}; +use rustc::ty::{self, Ty, TyCtxt}; +use rustc::util::nodemap::FxHashMap; +use rustc_target::spec::{HasTargetSpec, Target}; + +use std::ffi::{CStr, CString}; +use std::cell::{Cell, RefCell}; +use std::ptr; +use std::iter; +use std::str; +use std::sync::Arc; +use syntax::symbol::LocalInternedString; +use abi::Abi; + +/// There is one `CodegenCx` per compilation unit. Each one has its own LLVM +/// `ContextRef` so that several compilation units may be optimized in parallel. +/// All other LLVM data structures in the `CodegenCx` are tied to that `ContextRef`. +pub struct CodegenCx<'a, 'tcx: 'a> { + pub tcx: TyCtxt<'a, 'tcx, 'tcx>, + pub check_overflow: bool, + pub use_dll_storage_attrs: bool, + pub tls_model: llvm::ThreadLocalMode, + + pub llmod: ModuleRef, + pub llcx: ContextRef, + pub stats: RefCell, + pub codegen_unit: Arc>, + + /// Cache instances of monomorphic and polymorphic items + pub instances: RefCell, ValueRef>>, + /// Cache generated vtables + pub vtables: RefCell, + Option>), ValueRef>>, + /// Cache of constant strings, + pub const_cstr_cache: RefCell>, + + /// Reverse-direction for const ptrs cast from globals. + /// Key is a ValueRef holding a *T, + /// Val is a ValueRef holding a *[T]. + /// + /// Needed because LLVM loses pointer->pointee association + /// when we ptrcast, and we have to ptrcast during codegen + /// of a [T] const because we form a slice, a (*T,usize) pair, not + /// a pointer to an LLVM array type. Similar for trait objects. + pub const_unsized: RefCell>, + + /// Cache of emitted const globals (value -> global) + pub const_globals: RefCell>, + + /// Mapping from static definitions to their DefId's. + pub statics: RefCell>, + + /// List of globals for static variables which need to be passed to the + /// LLVM function ReplaceAllUsesWith (RAUW) when codegen is complete. + /// (We have to make sure we don't invalidate any ValueRefs referring + /// to constants.) + pub statics_to_rauw: RefCell>, + + /// Statics that will be placed in the llvm.used variable + /// See http://llvm.org/docs/LangRef.html#the-llvm-used-global-variable for details + pub used_statics: RefCell>, + + pub lltypes: RefCell, Option), Type>>, + pub scalar_lltypes: RefCell, Type>>, + pub pointee_infos: RefCell, Size), Option>>, + pub isize_ty: Type, + + pub dbg_cx: Option>, + + eh_personality: Cell>, + eh_unwind_resume: Cell>, + pub rust_try_fn: Cell>, + + intrinsics: RefCell>, + + /// A counter that is used for generating local symbol names + local_gen_sym_counter: Cell, +} + +impl<'a, 'tcx> DepGraphSafe for CodegenCx<'a, 'tcx> { +} + +pub fn get_reloc_model(sess: &Session) -> llvm::RelocMode { + let reloc_model_arg = match sess.opts.cg.relocation_model { + Some(ref s) => &s[..], + None => &sess.target.target.options.relocation_model[..], + }; + + match ::back::write::RELOC_MODEL_ARGS.iter().find( + |&&arg| arg.0 == reloc_model_arg) { + Some(x) => x.1, + _ => { + sess.err(&format!("{:?} is not a valid relocation mode", + reloc_model_arg)); + sess.abort_if_errors(); + bug!(); + } + } +} + +fn get_tls_model(sess: &Session) -> llvm::ThreadLocalMode { + let tls_model_arg = match sess.opts.debugging_opts.tls_model { + Some(ref s) => &s[..], + None => &sess.target.target.options.tls_model[..], + }; + + match ::back::write::TLS_MODEL_ARGS.iter().find( + |&&arg| arg.0 == tls_model_arg) { + Some(x) => x.1, + _ => { + sess.err(&format!("{:?} is not a valid TLS model", + tls_model_arg)); + sess.abort_if_errors(); + bug!(); + } + } +} + +fn is_any_library(sess: &Session) -> bool { + sess.crate_types.borrow().iter().any(|ty| { + *ty != config::CrateTypeExecutable + }) +} + +pub fn is_pie_binary(sess: &Session) -> bool { + !is_any_library(sess) && get_reloc_model(sess) == llvm::RelocMode::PIC +} + +pub unsafe fn create_context_and_module(sess: &Session, mod_name: &str) -> (ContextRef, ModuleRef) { + let llcx = llvm::LLVMRustContextCreate(sess.fewer_names()); + let mod_name = CString::new(mod_name).unwrap(); + let llmod = llvm::LLVMModuleCreateWithNameInContext(mod_name.as_ptr(), llcx); + + // Ensure the data-layout values hardcoded remain the defaults. + if sess.target.target.options.is_builtin { + let tm = ::back::write::create_target_machine(sess, false); + llvm::LLVMRustSetDataLayoutFromTargetMachine(llmod, tm); + llvm::LLVMRustDisposeTargetMachine(tm); + + let data_layout = llvm::LLVMGetDataLayout(llmod); + let data_layout = str::from_utf8(CStr::from_ptr(data_layout).to_bytes()) + .ok().expect("got a non-UTF8 data-layout from LLVM"); + + // Unfortunately LLVM target specs change over time, and right now we + // don't have proper support to work with any more than one + // `data_layout` than the one that is in the rust-lang/rust repo. If + // this compiler is configured against a custom LLVM, we may have a + // differing data layout, even though we should update our own to use + // that one. + // + // As an interim hack, if CFG_LLVM_ROOT is not an empty string then we + // disable this check entirely as we may be configured with something + // that has a different target layout. + // + // Unsure if this will actually cause breakage when rustc is configured + // as such. + // + // FIXME(#34960) + let cfg_llvm_root = option_env!("CFG_LLVM_ROOT").unwrap_or(""); + let custom_llvm_used = cfg_llvm_root.trim() != ""; + + if !custom_llvm_used && sess.target.target.data_layout != data_layout { + bug!("data-layout for builtin `{}` target, `{}`, \ + differs from LLVM default, `{}`", + sess.target.target.llvm_target, + sess.target.target.data_layout, + data_layout); + } + } + + let data_layout = CString::new(&sess.target.target.data_layout[..]).unwrap(); + llvm::LLVMSetDataLayout(llmod, data_layout.as_ptr()); + + let llvm_target = sess.target.target.llvm_target.as_bytes(); + let llvm_target = CString::new(llvm_target).unwrap(); + llvm::LLVMRustSetNormalizedTarget(llmod, llvm_target.as_ptr()); + + if is_pie_binary(sess) { + llvm::LLVMRustSetModulePIELevel(llmod); + } + + (llcx, llmod) +} + +impl<'a, 'tcx> CodegenCx<'a, 'tcx> { + pub fn new(tcx: TyCtxt<'a, 'tcx, 'tcx>, + codegen_unit: Arc>, + llmod_id: &str) + -> CodegenCx<'a, 'tcx> { + // An interesting part of Windows which MSVC forces our hand on (and + // apparently MinGW didn't) is the usage of `dllimport` and `dllexport` + // attributes in LLVM IR as well as native dependencies (in C these + // correspond to `__declspec(dllimport)`). + // + // Whenever a dynamic library is built by MSVC it must have its public + // interface specified by functions tagged with `dllexport` or otherwise + // they're not available to be linked against. This poses a few problems + // for the compiler, some of which are somewhat fundamental, but we use + // the `use_dll_storage_attrs` variable below to attach the `dllexport` + // attribute to all LLVM functions that are exported e.g. they're + // already tagged with external linkage). This is suboptimal for a few + // reasons: + // + // * If an object file will never be included in a dynamic library, + // there's no need to attach the dllexport attribute. Most object + // files in Rust are not destined to become part of a dll as binaries + // are statically linked by default. + // * If the compiler is emitting both an rlib and a dylib, the same + // source object file is currently used but with MSVC this may be less + // feasible. The compiler may be able to get around this, but it may + // involve some invasive changes to deal with this. + // + // The flipside of this situation is that whenever you link to a dll and + // you import a function from it, the import should be tagged with + // `dllimport`. At this time, however, the compiler does not emit + // `dllimport` for any declarations other than constants (where it is + // required), which is again suboptimal for even more reasons! + // + // * Calling a function imported from another dll without using + // `dllimport` causes the linker/compiler to have extra overhead (one + // `jmp` instruction on x86) when calling the function. + // * The same object file may be used in different circumstances, so a + // function may be imported from a dll if the object is linked into a + // dll, but it may be just linked against if linked into an rlib. + // * The compiler has no knowledge about whether native functions should + // be tagged dllimport or not. + // + // For now the compiler takes the perf hit (I do not have any numbers to + // this effect) by marking very little as `dllimport` and praying the + // linker will take care of everything. Fixing this problem will likely + // require adding a few attributes to Rust itself (feature gated at the + // start) and then strongly recommending static linkage on MSVC! + let use_dll_storage_attrs = tcx.sess.target.target.options.is_like_msvc; + + let check_overflow = tcx.sess.overflow_checks(); + + let tls_model = get_tls_model(&tcx.sess); + + unsafe { + let (llcx, llmod) = create_context_and_module(&tcx.sess, + &llmod_id[..]); + + let dbg_cx = if tcx.sess.opts.debuginfo != NoDebugInfo { + let dctx = debuginfo::CrateDebugContext::new(llmod); + debuginfo::metadata::compile_unit_metadata(tcx, + &codegen_unit.name().as_str(), + &dctx); + Some(dctx) + } else { + None + }; + + let mut cx = CodegenCx { + tcx, + check_overflow, + use_dll_storage_attrs, + tls_model, + llmod, + llcx, + stats: RefCell::new(Stats::default()), + codegen_unit, + instances: RefCell::new(FxHashMap()), + vtables: RefCell::new(FxHashMap()), + const_cstr_cache: RefCell::new(FxHashMap()), + const_unsized: RefCell::new(FxHashMap()), + const_globals: RefCell::new(FxHashMap()), + statics: RefCell::new(FxHashMap()), + statics_to_rauw: RefCell::new(Vec::new()), + used_statics: RefCell::new(Vec::new()), + lltypes: RefCell::new(FxHashMap()), + scalar_lltypes: RefCell::new(FxHashMap()), + pointee_infos: RefCell::new(FxHashMap()), + isize_ty: Type::from_ref(ptr::null_mut()), + dbg_cx, + eh_personality: Cell::new(None), + eh_unwind_resume: Cell::new(None), + rust_try_fn: Cell::new(None), + intrinsics: RefCell::new(FxHashMap()), + local_gen_sym_counter: Cell::new(0), + }; + cx.isize_ty = Type::isize(&cx); + cx + } + } + + pub fn into_stats(self) -> Stats { + self.stats.into_inner() + } +} + +impl<'b, 'tcx> CodegenCx<'b, 'tcx> { + pub fn sess<'a>(&'a self) -> &'a Session { + &self.tcx.sess + } + + pub fn get_intrinsic(&self, key: &str) -> ValueRef { + if let Some(v) = self.intrinsics.borrow().get(key).cloned() { + return v; + } + match declare_intrinsic(self, key) { + Some(v) => return v, + None => bug!("unknown intrinsic '{}'", key) + } + } + + /// Generate a new symbol name with the given prefix. This symbol name must + /// only be used for definitions with `internal` or `private` linkage. + pub fn generate_local_symbol_name(&self, prefix: &str) -> String { + let idx = self.local_gen_sym_counter.get(); + self.local_gen_sym_counter.set(idx + 1); + // Include a '.' character, so there can be no accidental conflicts with + // user defined names + let mut name = String::with_capacity(prefix.len() + 6); + name.push_str(prefix); + name.push_str("."); + base_n::push_str(idx as u128, base_n::ALPHANUMERIC_ONLY, &mut name); + name + } + + pub fn eh_personality(&self) -> ValueRef { + // The exception handling personality function. + // + // If our compilation unit has the `eh_personality` lang item somewhere + // within it, then we just need to codegen that. Otherwise, we're + // building an rlib which will depend on some upstream implementation of + // this function, so we just codegen a generic reference to it. We don't + // specify any of the types for the function, we just make it a symbol + // that LLVM can later use. + // + // Note that MSVC is a little special here in that we don't use the + // `eh_personality` lang item at all. Currently LLVM has support for + // both Dwarf and SEH unwind mechanisms for MSVC targets and uses the + // *name of the personality function* to decide what kind of unwind side + // tables/landing pads to emit. It looks like Dwarf is used by default, + // injecting a dependency on the `_Unwind_Resume` symbol for resuming + // an "exception", but for MSVC we want to force SEH. This means that we + // can't actually have the personality function be our standard + // `rust_eh_personality` function, but rather we wired it up to the + // CRT's custom personality function, which forces LLVM to consider + // landing pads as "landing pads for SEH". + if let Some(llpersonality) = self.eh_personality.get() { + return llpersonality + } + let tcx = self.tcx; + let llfn = match tcx.lang_items().eh_personality() { + Some(def_id) if !base::wants_msvc_seh(self.sess()) => { + callee::resolve_and_get_fn(self, def_id, tcx.intern_substs(&[])) + } + _ => { + let name = if base::wants_msvc_seh(self.sess()) { + "__CxxFrameHandler3" + } else { + "rust_eh_personality" + }; + let fty = Type::variadic_func(&[], &Type::i32(self)); + declare::declare_cfn(self, name, fty) + } + }; + self.eh_personality.set(Some(llfn)); + llfn + } + + // Returns a ValueRef of the "eh_unwind_resume" lang item if one is defined, + // otherwise declares it as an external function. + pub fn eh_unwind_resume(&self) -> ValueRef { + use attributes; + let unwresume = &self.eh_unwind_resume; + if let Some(llfn) = unwresume.get() { + return llfn; + } + + let tcx = self.tcx; + assert!(self.sess().target.target.options.custom_unwind_resume); + if let Some(def_id) = tcx.lang_items().eh_unwind_resume() { + let llfn = callee::resolve_and_get_fn(self, def_id, tcx.intern_substs(&[])); + unwresume.set(Some(llfn)); + return llfn; + } + + let ty = tcx.mk_fn_ptr(ty::Binder::bind(tcx.mk_fn_sig( + iter::once(tcx.mk_mut_ptr(tcx.types.u8)), + tcx.types.never, + false, + hir::Unsafety::Unsafe, + Abi::C + ))); + + let llfn = declare::declare_fn(self, "rust_eh_unwind_resume", ty); + attributes::unwind(llfn, true); + unwresume.set(Some(llfn)); + llfn + } + + pub fn type_needs_drop(&self, ty: Ty<'tcx>) -> bool { + common::type_needs_drop(self.tcx, ty) + } + + pub fn type_is_sized(&self, ty: Ty<'tcx>) -> bool { + common::type_is_sized(self.tcx, ty) + } + + pub fn type_is_freeze(&self, ty: Ty<'tcx>) -> bool { + common::type_is_freeze(self.tcx, ty) + } + + pub fn type_has_metadata(&self, ty: Ty<'tcx>) -> bool { + use syntax_pos::DUMMY_SP; + if ty.is_sized(self.tcx.at(DUMMY_SP), ty::ParamEnv::reveal_all()) { + return false; + } + + let tail = self.tcx.struct_tail(ty); + match tail.sty { + ty::TyForeign(..) => false, + ty::TyStr | ty::TySlice(..) | ty::TyDynamic(..) => true, + _ => bug!("unexpected unsized tail: {:?}", tail.sty), + } + } +} + +impl<'a, 'tcx> ty::layout::HasDataLayout for &'a CodegenCx<'a, 'tcx> { + fn data_layout(&self) -> &ty::layout::TargetDataLayout { + &self.tcx.data_layout + } +} + +impl<'a, 'tcx> HasTargetSpec for &'a CodegenCx<'a, 'tcx> { + fn target_spec(&self) -> &Target { + &self.tcx.sess.target.target + } +} + +impl<'a, 'tcx> ty::layout::HasTyCtxt<'tcx> for &'a CodegenCx<'a, 'tcx> { + fn tcx<'b>(&'b self) -> TyCtxt<'b, 'tcx, 'tcx> { + self.tcx + } +} + +impl<'a, 'tcx> LayoutOf for &'a CodegenCx<'a, 'tcx> { + type Ty = Ty<'tcx>; + type TyLayout = TyLayout<'tcx>; + + fn layout_of(self, ty: Ty<'tcx>) -> Self::TyLayout { + self.tcx.layout_of(ty::ParamEnv::reveal_all().and(ty)) + .unwrap_or_else(|e| match e { + LayoutError::SizeOverflow(_) => self.sess().fatal(&e.to_string()), + _ => bug!("failed to get layout for `{}`: {}", ty, e) + }) + } +} + +/// Declare any llvm intrinsics that you might need +fn declare_intrinsic(cx: &CodegenCx, key: &str) -> Option { + macro_rules! ifn { + ($name:expr, fn() -> $ret:expr) => ( + if key == $name { + let f = declare::declare_cfn(cx, $name, Type::func(&[], &$ret)); + llvm::SetUnnamedAddr(f, false); + cx.intrinsics.borrow_mut().insert($name, f.clone()); + return Some(f); + } + ); + ($name:expr, fn(...) -> $ret:expr) => ( + if key == $name { + let f = declare::declare_cfn(cx, $name, Type::variadic_func(&[], &$ret)); + llvm::SetUnnamedAddr(f, false); + cx.intrinsics.borrow_mut().insert($name, f.clone()); + return Some(f); + } + ); + ($name:expr, fn($($arg:expr),*) -> $ret:expr) => ( + if key == $name { + let f = declare::declare_cfn(cx, $name, Type::func(&[$($arg),*], &$ret)); + llvm::SetUnnamedAddr(f, false); + cx.intrinsics.borrow_mut().insert($name, f.clone()); + return Some(f); + } + ); + } + macro_rules! mk_struct { + ($($field_ty:expr),*) => (Type::struct_(cx, &[$($field_ty),*], false)) + } + + let i8p = Type::i8p(cx); + let void = Type::void(cx); + let i1 = Type::i1(cx); + let t_i8 = Type::i8(cx); + let t_i16 = Type::i16(cx); + let t_i32 = Type::i32(cx); + let t_i64 = Type::i64(cx); + let t_i128 = Type::i128(cx); + let t_f32 = Type::f32(cx); + let t_f64 = Type::f64(cx); + + ifn!("llvm.memcpy.p0i8.p0i8.i16", fn(i8p, i8p, t_i16, t_i32, i1) -> void); + ifn!("llvm.memcpy.p0i8.p0i8.i32", fn(i8p, i8p, t_i32, t_i32, i1) -> void); + ifn!("llvm.memcpy.p0i8.p0i8.i64", fn(i8p, i8p, t_i64, t_i32, i1) -> void); + ifn!("llvm.memmove.p0i8.p0i8.i16", fn(i8p, i8p, t_i16, t_i32, i1) -> void); + ifn!("llvm.memmove.p0i8.p0i8.i32", fn(i8p, i8p, t_i32, t_i32, i1) -> void); + ifn!("llvm.memmove.p0i8.p0i8.i64", fn(i8p, i8p, t_i64, t_i32, i1) -> void); + ifn!("llvm.memset.p0i8.i16", fn(i8p, t_i8, t_i16, t_i32, i1) -> void); + ifn!("llvm.memset.p0i8.i32", fn(i8p, t_i8, t_i32, t_i32, i1) -> void); + ifn!("llvm.memset.p0i8.i64", fn(i8p, t_i8, t_i64, t_i32, i1) -> void); + + ifn!("llvm.trap", fn() -> void); + ifn!("llvm.debugtrap", fn() -> void); + ifn!("llvm.frameaddress", fn(t_i32) -> i8p); + + ifn!("llvm.powi.f32", fn(t_f32, t_i32) -> t_f32); + ifn!("llvm.powi.f64", fn(t_f64, t_i32) -> t_f64); + ifn!("llvm.pow.f32", fn(t_f32, t_f32) -> t_f32); + ifn!("llvm.pow.f64", fn(t_f64, t_f64) -> t_f64); + + ifn!("llvm.sqrt.f32", fn(t_f32) -> t_f32); + ifn!("llvm.sqrt.f64", fn(t_f64) -> t_f64); + ifn!("llvm.sin.f32", fn(t_f32) -> t_f32); + ifn!("llvm.sin.f64", fn(t_f64) -> t_f64); + ifn!("llvm.cos.f32", fn(t_f32) -> t_f32); + ifn!("llvm.cos.f64", fn(t_f64) -> t_f64); + ifn!("llvm.exp.f32", fn(t_f32) -> t_f32); + ifn!("llvm.exp.f64", fn(t_f64) -> t_f64); + ifn!("llvm.exp2.f32", fn(t_f32) -> t_f32); + ifn!("llvm.exp2.f64", fn(t_f64) -> t_f64); + ifn!("llvm.log.f32", fn(t_f32) -> t_f32); + ifn!("llvm.log.f64", fn(t_f64) -> t_f64); + ifn!("llvm.log10.f32", fn(t_f32) -> t_f32); + ifn!("llvm.log10.f64", fn(t_f64) -> t_f64); + ifn!("llvm.log2.f32", fn(t_f32) -> t_f32); + ifn!("llvm.log2.f64", fn(t_f64) -> t_f64); + + ifn!("llvm.fma.f32", fn(t_f32, t_f32, t_f32) -> t_f32); + ifn!("llvm.fma.f64", fn(t_f64, t_f64, t_f64) -> t_f64); + + ifn!("llvm.fabs.f32", fn(t_f32) -> t_f32); + ifn!("llvm.fabs.f64", fn(t_f64) -> t_f64); + + ifn!("llvm.floor.f32", fn(t_f32) -> t_f32); + ifn!("llvm.floor.f64", fn(t_f64) -> t_f64); + ifn!("llvm.ceil.f32", fn(t_f32) -> t_f32); + ifn!("llvm.ceil.f64", fn(t_f64) -> t_f64); + ifn!("llvm.trunc.f32", fn(t_f32) -> t_f32); + ifn!("llvm.trunc.f64", fn(t_f64) -> t_f64); + + ifn!("llvm.copysign.f32", fn(t_f32, t_f32) -> t_f32); + ifn!("llvm.copysign.f64", fn(t_f64, t_f64) -> t_f64); + ifn!("llvm.round.f32", fn(t_f32) -> t_f32); + ifn!("llvm.round.f64", fn(t_f64) -> t_f64); + + ifn!("llvm.rint.f32", fn(t_f32) -> t_f32); + ifn!("llvm.rint.f64", fn(t_f64) -> t_f64); + ifn!("llvm.nearbyint.f32", fn(t_f32) -> t_f32); + ifn!("llvm.nearbyint.f64", fn(t_f64) -> t_f64); + + ifn!("llvm.ctpop.i8", fn(t_i8) -> t_i8); + ifn!("llvm.ctpop.i16", fn(t_i16) -> t_i16); + ifn!("llvm.ctpop.i32", fn(t_i32) -> t_i32); + ifn!("llvm.ctpop.i64", fn(t_i64) -> t_i64); + ifn!("llvm.ctpop.i128", fn(t_i128) -> t_i128); + + ifn!("llvm.ctlz.i8", fn(t_i8 , i1) -> t_i8); + ifn!("llvm.ctlz.i16", fn(t_i16, i1) -> t_i16); + ifn!("llvm.ctlz.i32", fn(t_i32, i1) -> t_i32); + ifn!("llvm.ctlz.i64", fn(t_i64, i1) -> t_i64); + ifn!("llvm.ctlz.i128", fn(t_i128, i1) -> t_i128); + + ifn!("llvm.cttz.i8", fn(t_i8 , i1) -> t_i8); + ifn!("llvm.cttz.i16", fn(t_i16, i1) -> t_i16); + ifn!("llvm.cttz.i32", fn(t_i32, i1) -> t_i32); + ifn!("llvm.cttz.i64", fn(t_i64, i1) -> t_i64); + ifn!("llvm.cttz.i128", fn(t_i128, i1) -> t_i128); + + ifn!("llvm.bswap.i16", fn(t_i16) -> t_i16); + ifn!("llvm.bswap.i32", fn(t_i32) -> t_i32); + ifn!("llvm.bswap.i64", fn(t_i64) -> t_i64); + ifn!("llvm.bswap.i128", fn(t_i128) -> t_i128); + + ifn!("llvm.bitreverse.i8", fn(t_i8) -> t_i8); + ifn!("llvm.bitreverse.i16", fn(t_i16) -> t_i16); + ifn!("llvm.bitreverse.i32", fn(t_i32) -> t_i32); + ifn!("llvm.bitreverse.i64", fn(t_i64) -> t_i64); + ifn!("llvm.bitreverse.i128", fn(t_i128) -> t_i128); + + ifn!("llvm.sadd.with.overflow.i8", fn(t_i8, t_i8) -> mk_struct!{t_i8, i1}); + ifn!("llvm.sadd.with.overflow.i16", fn(t_i16, t_i16) -> mk_struct!{t_i16, i1}); + ifn!("llvm.sadd.with.overflow.i32", fn(t_i32, t_i32) -> mk_struct!{t_i32, i1}); + ifn!("llvm.sadd.with.overflow.i64", fn(t_i64, t_i64) -> mk_struct!{t_i64, i1}); + ifn!("llvm.sadd.with.overflow.i128", fn(t_i128, t_i128) -> mk_struct!{t_i128, i1}); + + ifn!("llvm.uadd.with.overflow.i8", fn(t_i8, t_i8) -> mk_struct!{t_i8, i1}); + ifn!("llvm.uadd.with.overflow.i16", fn(t_i16, t_i16) -> mk_struct!{t_i16, i1}); + ifn!("llvm.uadd.with.overflow.i32", fn(t_i32, t_i32) -> mk_struct!{t_i32, i1}); + ifn!("llvm.uadd.with.overflow.i64", fn(t_i64, t_i64) -> mk_struct!{t_i64, i1}); + ifn!("llvm.uadd.with.overflow.i128", fn(t_i128, t_i128) -> mk_struct!{t_i128, i1}); + + ifn!("llvm.ssub.with.overflow.i8", fn(t_i8, t_i8) -> mk_struct!{t_i8, i1}); + ifn!("llvm.ssub.with.overflow.i16", fn(t_i16, t_i16) -> mk_struct!{t_i16, i1}); + ifn!("llvm.ssub.with.overflow.i32", fn(t_i32, t_i32) -> mk_struct!{t_i32, i1}); + ifn!("llvm.ssub.with.overflow.i64", fn(t_i64, t_i64) -> mk_struct!{t_i64, i1}); + ifn!("llvm.ssub.with.overflow.i128", fn(t_i128, t_i128) -> mk_struct!{t_i128, i1}); + + ifn!("llvm.usub.with.overflow.i8", fn(t_i8, t_i8) -> mk_struct!{t_i8, i1}); + ifn!("llvm.usub.with.overflow.i16", fn(t_i16, t_i16) -> mk_struct!{t_i16, i1}); + ifn!("llvm.usub.with.overflow.i32", fn(t_i32, t_i32) -> mk_struct!{t_i32, i1}); + ifn!("llvm.usub.with.overflow.i64", fn(t_i64, t_i64) -> mk_struct!{t_i64, i1}); + ifn!("llvm.usub.with.overflow.i128", fn(t_i128, t_i128) -> mk_struct!{t_i128, i1}); + + ifn!("llvm.smul.with.overflow.i8", fn(t_i8, t_i8) -> mk_struct!{t_i8, i1}); + ifn!("llvm.smul.with.overflow.i16", fn(t_i16, t_i16) -> mk_struct!{t_i16, i1}); + ifn!("llvm.smul.with.overflow.i32", fn(t_i32, t_i32) -> mk_struct!{t_i32, i1}); + ifn!("llvm.smul.with.overflow.i64", fn(t_i64, t_i64) -> mk_struct!{t_i64, i1}); + ifn!("llvm.smul.with.overflow.i128", fn(t_i128, t_i128) -> mk_struct!{t_i128, i1}); + + ifn!("llvm.umul.with.overflow.i8", fn(t_i8, t_i8) -> mk_struct!{t_i8, i1}); + ifn!("llvm.umul.with.overflow.i16", fn(t_i16, t_i16) -> mk_struct!{t_i16, i1}); + ifn!("llvm.umul.with.overflow.i32", fn(t_i32, t_i32) -> mk_struct!{t_i32, i1}); + ifn!("llvm.umul.with.overflow.i64", fn(t_i64, t_i64) -> mk_struct!{t_i64, i1}); + ifn!("llvm.umul.with.overflow.i128", fn(t_i128, t_i128) -> mk_struct!{t_i128, i1}); + + ifn!("llvm.lifetime.start", fn(t_i64,i8p) -> void); + ifn!("llvm.lifetime.end", fn(t_i64, i8p) -> void); + + ifn!("llvm.expect.i1", fn(i1, i1) -> i1); + ifn!("llvm.eh.typeid.for", fn(i8p) -> t_i32); + ifn!("llvm.localescape", fn(...) -> void); + ifn!("llvm.localrecover", fn(i8p, i8p, t_i32) -> i8p); + ifn!("llvm.x86.seh.recoverfp", fn(i8p, i8p) -> i8p); + + ifn!("llvm.assume", fn(i1) -> void); + ifn!("llvm.prefetch", fn(i8p, t_i32, t_i32, t_i32) -> void); + + if cx.sess().opts.debuginfo != NoDebugInfo { + ifn!("llvm.dbg.declare", fn(Type::metadata(cx), Type::metadata(cx)) -> void); + ifn!("llvm.dbg.value", fn(Type::metadata(cx), t_i64, Type::metadata(cx)) -> void); + } + return None; +} diff --git a/src/librustc_codegen_llvm/debuginfo/create_scope_map.rs b/src/librustc_codegen_llvm/debuginfo/create_scope_map.rs new file mode 100644 index 00000000000..bddb3d90940 --- /dev/null +++ b/src/librustc_codegen_llvm/debuginfo/create_scope_map.rs @@ -0,0 +1,136 @@ +// 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. + +use super::{FunctionDebugContext, FunctionDebugContextData}; +use super::metadata::file_metadata; +use super::utils::{DIB, span_start}; + +use llvm; +use llvm::debuginfo::DIScope; +use common::CodegenCx; +use rustc::mir::{Mir, VisibilityScope}; + +use libc::c_uint; +use std::ptr; + +use syntax_pos::Pos; + +use rustc_data_structures::bitvec::BitVector; +use rustc_data_structures::indexed_vec::{Idx, IndexVec}; + +use syntax_pos::BytePos; + +#[derive(Clone, Copy, Debug)] +pub struct MirDebugScope { + pub scope_metadata: DIScope, + // Start and end offsets of the file to which this DIScope belongs. + // These are used to quickly determine whether some span refers to the same file. + pub file_start_pos: BytePos, + pub file_end_pos: BytePos, +} + +impl MirDebugScope { + pub fn is_valid(&self) -> bool { + !self.scope_metadata.is_null() + } +} + +/// Produce DIScope DIEs for each MIR Scope which has variables defined in it. +/// If debuginfo is disabled, the returned vector is empty. +pub fn create_mir_scopes(cx: &CodegenCx, mir: &Mir, debug_context: &FunctionDebugContext) + -> IndexVec { + let null_scope = MirDebugScope { + scope_metadata: ptr::null_mut(), + file_start_pos: BytePos(0), + file_end_pos: BytePos(0) + }; + let mut scopes = IndexVec::from_elem(null_scope, &mir.visibility_scopes); + + let debug_context = match *debug_context { + FunctionDebugContext::RegularContext(ref data) => data, + FunctionDebugContext::DebugInfoDisabled | + FunctionDebugContext::FunctionWithoutDebugInfo => { + return scopes; + } + }; + + // Find all the scopes with variables defined in them. + let mut has_variables = BitVector::new(mir.visibility_scopes.len()); + for var in mir.vars_iter() { + let decl = &mir.local_decls[var]; + has_variables.insert(decl.source_info.scope.index()); + } + + // Instantiate all scopes. + for idx in 0..mir.visibility_scopes.len() { + let scope = VisibilityScope::new(idx); + make_mir_scope(cx, &mir, &has_variables, debug_context, scope, &mut scopes); + } + + scopes +} + +fn make_mir_scope(cx: &CodegenCx, + mir: &Mir, + has_variables: &BitVector, + debug_context: &FunctionDebugContextData, + scope: VisibilityScope, + scopes: &mut IndexVec) { + if scopes[scope].is_valid() { + return; + } + + let scope_data = &mir.visibility_scopes[scope]; + let parent_scope = if let Some(parent) = scope_data.parent_scope { + make_mir_scope(cx, mir, has_variables, debug_context, parent, scopes); + scopes[parent] + } else { + // The root is the function itself. + let loc = span_start(cx, mir.span); + scopes[scope] = MirDebugScope { + scope_metadata: debug_context.fn_metadata, + file_start_pos: loc.file.start_pos, + file_end_pos: loc.file.end_pos, + }; + return; + }; + + if !has_variables.contains(scope.index()) { + // Do not create a DIScope if there are no variables + // defined in this MIR Scope, to avoid debuginfo bloat. + + // However, we don't skip creating a nested scope if + // our parent is the root, because we might want to + // put arguments in the root and not have shadowing. + if parent_scope.scope_metadata != debug_context.fn_metadata { + scopes[scope] = parent_scope; + return; + } + } + + let loc = span_start(cx, scope_data.span); + let file_metadata = file_metadata(cx, + &loc.file.name, + debug_context.defining_crate); + + let scope_metadata = unsafe { + llvm::LLVMRustDIBuilderCreateLexicalBlock( + DIB(cx), + parent_scope.scope_metadata, + file_metadata, + loc.line as c_uint, + loc.col.to_usize() as c_uint) + }; + scopes[scope] = MirDebugScope { + scope_metadata, + file_start_pos: loc.file.start_pos, + file_end_pos: loc.file.end_pos, + }; +} diff --git a/src/librustc_codegen_llvm/debuginfo/doc.rs b/src/librustc_codegen_llvm/debuginfo/doc.rs new file mode 100644 index 00000000000..ce0476b07eb --- /dev/null +++ b/src/librustc_codegen_llvm/debuginfo/doc.rs @@ -0,0 +1,189 @@ +// 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. + +//! # Debug Info Module +//! +//! This module serves the purpose of generating debug symbols. We use LLVM's +//! [source level debugging](http://!llvm.org/docs/SourceLevelDebugging.html) +//! features for generating the debug information. The general principle is +//! this: +//! +//! Given the right metadata in the LLVM IR, the LLVM code generator is able to +//! create DWARF debug symbols for the given code. The +//! [metadata](http://!llvm.org/docs/LangRef.html#metadata-type) is structured +//! much like DWARF *debugging information entries* (DIE), representing type +//! information such as datatype layout, function signatures, block layout, +//! variable location and scope information, etc. It is the purpose of this +//! module to generate correct metadata and insert it into the LLVM IR. +//! +//! As the exact format of metadata trees may change between different LLVM +//! versions, we now use LLVM +//! [DIBuilder](http://!llvm.org/docs/doxygen/html/classllvm_1_1DIBuilder.html) +//! to create metadata where possible. This will hopefully ease the adaption of +//! this module to future LLVM versions. +//! +//! The public API of the module is a set of functions that will insert the +//! correct metadata into the LLVM IR when called with the right parameters. +//! The module is thus driven from an outside client with functions like +//! `debuginfo::create_local_var_metadata(bx: block, local: &ast::local)`. +//! +//! Internally the module will try to reuse already created metadata by +//! utilizing a cache. The way to get a shared metadata node when needed is +//! thus to just call the corresponding function in this module: +//! +//! let file_metadata = file_metadata(crate_context, path); +//! +//! The function will take care of probing the cache for an existing node for +//! that exact file path. +//! +//! All private state used by the module is stored within either the +//! CrateDebugContext struct (owned by the CodegenCx) or the +//! FunctionDebugContext (owned by the FunctionCx). +//! +//! This file consists of three conceptual sections: +//! 1. The public interface of the module +//! 2. Module-internal metadata creation functions +//! 3. Minor utility functions +//! +//! +//! ## Recursive Types +//! +//! Some kinds of types, such as structs and enums can be recursive. That means +//! that the type definition of some type X refers to some other type which in +//! turn (transitively) refers to X. This introduces cycles into the type +//! referral graph. A naive algorithm doing an on-demand, depth-first traversal +//! of this graph when describing types, can get trapped in an endless loop +//! when it reaches such a cycle. +//! +//! For example, the following simple type for a singly-linked list... +//! +//! ``` +//! struct List { +//! value: i32, +//! tail: Option>, +//! } +//! ``` +//! +//! will generate the following callstack with a naive DFS algorithm: +//! +//! ``` +//! describe(t = List) +//! describe(t = i32) +//! describe(t = Option>) +//! describe(t = Box) +//! describe(t = List) // at the beginning again... +//! ... +//! ``` +//! +//! To break cycles like these, we use "forward declarations". That is, when +//! the algorithm encounters a possibly recursive type (any struct or enum), it +//! immediately creates a type description node and inserts it into the cache +//! *before* describing the members of the type. This type description is just +//! a stub (as type members are not described and added to it yet) but it +//! allows the algorithm to already refer to the type. After the stub is +//! inserted into the cache, the algorithm continues as before. If it now +//! encounters a recursive reference, it will hit the cache and does not try to +//! describe the type anew. +//! +//! This behavior is encapsulated in the 'RecursiveTypeDescription' enum, +//! which represents a kind of continuation, storing all state needed to +//! continue traversal at the type members after the type has been registered +//! with the cache. (This implementation approach might be a tad over- +//! engineered and may change in the future) +//! +//! +//! ## Source Locations and Line Information +//! +//! In addition to data type descriptions the debugging information must also +//! allow to map machine code locations back to source code locations in order +//! to be useful. This functionality is also handled in this module. The +//! following functions allow to control source mappings: +//! +//! + set_source_location() +//! + clear_source_location() +//! + start_emitting_source_locations() +//! +//! `set_source_location()` allows to set the current source location. All IR +//! instructions created after a call to this function will be linked to the +//! given source location, until another location is specified with +//! `set_source_location()` or the source location is cleared with +//! `clear_source_location()`. In the later case, subsequent IR instruction +//! will not be linked to any source location. As you can see, this is a +//! stateful API (mimicking the one in LLVM), so be careful with source +//! locations set by previous calls. It's probably best to not rely on any +//! specific state being present at a given point in code. +//! +//! One topic that deserves some extra attention is *function prologues*. At +//! the beginning of a function's machine code there are typically a few +//! instructions for loading argument values into allocas and checking if +//! there's enough stack space for the function to execute. This *prologue* is +//! not visible in the source code and LLVM puts a special PROLOGUE END marker +//! into the line table at the first non-prologue instruction of the function. +//! In order to find out where the prologue ends, LLVM looks for the first +//! instruction in the function body that is linked to a source location. So, +//! when generating prologue instructions we have to make sure that we don't +//! emit source location information until the 'real' function body begins. For +//! this reason, source location emission is disabled by default for any new +//! function being codegened and is only activated after a call to the third +//! function from the list above, `start_emitting_source_locations()`. This +//! function should be called right before regularly starting to codegen the +//! top-level block of the given function. +//! +//! There is one exception to the above rule: `llvm.dbg.declare` instruction +//! must be linked to the source location of the variable being declared. For +//! function parameters these `llvm.dbg.declare` instructions typically occur +//! in the middle of the prologue, however, they are ignored by LLVM's prologue +//! detection. The `create_argument_metadata()` and related functions take care +//! of linking the `llvm.dbg.declare` instructions to the correct source +//! locations even while source location emission is still disabled, so there +//! is no need to do anything special with source location handling here. +//! +//! ## Unique Type Identification +//! +//! In order for link-time optimization to work properly, LLVM needs a unique +//! type identifier that tells it across compilation units which types are the +//! same as others. This type identifier is created by +//! TypeMap::get_unique_type_id_of_type() using the following algorithm: +//! +//! (1) Primitive types have their name as ID +//! (2) Structs, enums and traits have a multipart identifier +//! +//! (1) The first part is the SVH (strict version hash) of the crate they +//! were originally defined in +//! +//! (2) The second part is the ast::NodeId of the definition in their +//! original crate +//! +//! (3) The final part is a concatenation of the type IDs of their concrete +//! type arguments if they are generic types. +//! +//! (3) Tuple-, pointer and function types are structurally identified, which +//! means that they are equivalent if their component types are equivalent +//! (i.e. (i32, i32) is the same regardless in which crate it is used). +//! +//! This algorithm also provides a stable ID for types that are defined in one +//! crate but instantiated from metadata within another crate. We just have to +//! take care to always map crate and node IDs back to the original crate +//! context. +//! +//! As a side-effect these unique type IDs also help to solve a problem arising +//! from lifetime parameters. Since lifetime parameters are completely omitted +//! in debuginfo, more than one `Ty` instance may map to the same debuginfo +//! type metadata, that is, some struct `Struct<'a>` may have N instantiations +//! with different concrete substitutions for `'a`, and thus there will be N +//! `Ty` instances for the type `Struct<'a>` even though it is not generic +//! otherwise. Unfortunately this means that we cannot use `ty::type_id()` as +//! cheap identifier for type metadata---we have done this in the past, but it +//! led to unnecessary metadata duplication in the best case and LLVM +//! assertions in the worst. However, the unique type ID as described above +//! *can* be used as identifier. Since it is comparatively expensive to +//! construct, though, `ty::type_id()` is still used additionally as an +//! optimization for cases where the exact same type has been seen before +//! (which is most of the time). diff --git a/src/librustc_codegen_llvm/debuginfo/gdb.rs b/src/librustc_codegen_llvm/debuginfo/gdb.rs new file mode 100644 index 00000000000..0b4858c7ab0 --- /dev/null +++ b/src/librustc_codegen_llvm/debuginfo/gdb.rs @@ -0,0 +1,88 @@ +// 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. + +// .debug_gdb_scripts binary section. + +use llvm; + +use common::{C_bytes, CodegenCx, C_i32}; +use builder::Builder; +use declare; +use type_::Type; +use rustc::session::config::NoDebugInfo; + +use std::ptr; +use syntax::attr; + + +/// Inserts a side-effect free instruction sequence that makes sure that the +/// .debug_gdb_scripts global is referenced, so it isn't removed by the linker. +pub fn insert_reference_to_gdb_debug_scripts_section_global(bx: &Builder) { + if needs_gdb_debug_scripts_section(bx.cx) { + let gdb_debug_scripts_section = get_or_insert_gdb_debug_scripts_section_global(bx.cx); + // Load just the first byte as that's all that's necessary to force + // LLVM to keep around the reference to the global. + let indices = [C_i32(bx.cx, 0), C_i32(bx.cx, 0)]; + let element = bx.inbounds_gep(gdb_debug_scripts_section, &indices); + let volative_load_instruction = bx.volatile_load(element); + unsafe { + llvm::LLVMSetAlignment(volative_load_instruction, 1); + } + } +} + +/// Allocates the global variable responsible for the .debug_gdb_scripts binary +/// section. +pub fn get_or_insert_gdb_debug_scripts_section_global(cx: &CodegenCx) + -> llvm::ValueRef { + let c_section_var_name = "__rustc_debug_gdb_scripts_section__\0"; + let section_var_name = &c_section_var_name[..c_section_var_name.len()-1]; + + let section_var = unsafe { + llvm::LLVMGetNamedGlobal(cx.llmod, + c_section_var_name.as_ptr() as *const _) + }; + + if section_var == ptr::null_mut() { + let section_name = b".debug_gdb_scripts\0"; + let section_contents = b"\x01gdb_load_rust_pretty_printers.py\0"; + + unsafe { + let llvm_type = Type::array(&Type::i8(cx), + section_contents.len() as u64); + + let section_var = declare::define_global(cx, section_var_name, + llvm_type).unwrap_or_else(||{ + bug!("symbol `{}` is already defined", section_var_name) + }); + llvm::LLVMSetSection(section_var, section_name.as_ptr() as *const _); + llvm::LLVMSetInitializer(section_var, C_bytes(cx, section_contents)); + llvm::LLVMSetGlobalConstant(section_var, llvm::True); + llvm::LLVMSetUnnamedAddr(section_var, llvm::True); + llvm::LLVMRustSetLinkage(section_var, llvm::Linkage::LinkOnceODRLinkage); + // This should make sure that the whole section is not larger than + // the string it contains. Otherwise we get a warning from GDB. + llvm::LLVMSetAlignment(section_var, 1); + section_var + } + } else { + section_var + } +} + +pub fn needs_gdb_debug_scripts_section(cx: &CodegenCx) -> bool { + let omit_gdb_pretty_printer_section = + attr::contains_name(&cx.tcx.hir.krate_attrs(), + "omit_gdb_pretty_printer_section"); + + !omit_gdb_pretty_printer_section && + cx.sess().opts.debuginfo != NoDebugInfo && + cx.sess().target.target.options.emit_debug_gdb_scripts +} diff --git a/src/librustc_codegen_llvm/debuginfo/metadata.rs b/src/librustc_codegen_llvm/debuginfo/metadata.rs new file mode 100644 index 00000000000..ee60711c11d --- /dev/null +++ b/src/librustc_codegen_llvm/debuginfo/metadata.rs @@ -0,0 +1,1773 @@ +// 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. + +use self::RecursiveTypeDescription::*; +use self::MemberDescriptionFactory::*; +use self::EnumDiscriminantInfo::*; + +use super::utils::{debug_context, DIB, span_start, + get_namespace_for_item, create_DIArray, is_node_local_to_unit}; +use super::namespace::mangled_name_of_instance; +use super::type_names::compute_debuginfo_type_name; +use super::{CrateDebugContext}; +use abi; + +use llvm::{self, ValueRef}; +use llvm::debuginfo::{DIType, DIFile, DIScope, DIDescriptor, + DICompositeType, DILexicalBlock, DIFlags}; + +use rustc::hir::CodegenFnAttrFlags; +use rustc::hir::def::CtorKind; +use rustc::hir::def_id::{DefId, CrateNum, LOCAL_CRATE}; +use rustc::ty::fold::TypeVisitor; +use rustc::ty::util::TypeIdHasher; +use rustc::ich::Fingerprint; +use rustc::ty::Instance; +use common::CodegenCx; +use rustc::ty::{self, AdtKind, ParamEnv, Ty, TyCtxt}; +use rustc::ty::layout::{self, Align, LayoutOf, PrimitiveExt, Size, TyLayout}; +use rustc::session::config; +use rustc::util::nodemap::FxHashMap; +use rustc::util::common::path2cstr; + +use libc::{c_uint, c_longlong}; +use std::ffi::CString; +use std::fmt::Write; +use std::ptr; +use std::path::{Path, PathBuf}; +use syntax::ast; +use syntax::symbol::{Interner, InternedString, Symbol}; +use syntax_pos::{self, Span, FileName}; + + +// From DWARF 5. +// See http://www.dwarfstd.org/ShowIssue.php?issue=140129.1 +const DW_LANG_RUST: c_uint = 0x1c; +#[allow(non_upper_case_globals)] +const DW_ATE_boolean: c_uint = 0x02; +#[allow(non_upper_case_globals)] +const DW_ATE_float: c_uint = 0x04; +#[allow(non_upper_case_globals)] +const DW_ATE_signed: c_uint = 0x05; +#[allow(non_upper_case_globals)] +const DW_ATE_unsigned: c_uint = 0x07; +#[allow(non_upper_case_globals)] +const DW_ATE_unsigned_char: c_uint = 0x08; + +pub const UNKNOWN_LINE_NUMBER: c_uint = 0; +pub const UNKNOWN_COLUMN_NUMBER: c_uint = 0; + +// ptr::null() doesn't work :( +pub const NO_SCOPE_METADATA: DIScope = (0 as DIScope); + +#[derive(Copy, Debug, Hash, Eq, PartialEq, Clone)] +pub struct UniqueTypeId(ast::Name); + +// The TypeMap is where the CrateDebugContext holds the type metadata nodes +// created so far. The metadata nodes are indexed by UniqueTypeId, and, for +// faster lookup, also by Ty. The TypeMap is responsible for creating +// UniqueTypeIds. +pub struct TypeMap<'tcx> { + // The UniqueTypeIds created so far + unique_id_interner: Interner, + // A map from UniqueTypeId to debuginfo metadata for that type. This is a 1:1 mapping. + unique_id_to_metadata: FxHashMap, + // A map from types to debuginfo metadata. This is a N:1 mapping. + type_to_metadata: FxHashMap, DIType>, + // A map from types to UniqueTypeId. This is a N:1 mapping. + type_to_unique_id: FxHashMap, UniqueTypeId> +} + +impl<'tcx> TypeMap<'tcx> { + pub fn new() -> TypeMap<'tcx> { + TypeMap { + unique_id_interner: Interner::new(), + type_to_metadata: FxHashMap(), + unique_id_to_metadata: FxHashMap(), + type_to_unique_id: FxHashMap(), + } + } + + // Adds a Ty to metadata mapping to the TypeMap. The method will fail if + // the mapping already exists. + fn register_type_with_metadata<'a>(&mut self, + type_: Ty<'tcx>, + metadata: DIType) { + if self.type_to_metadata.insert(type_, metadata).is_some() { + bug!("Type metadata for Ty '{}' is already in the TypeMap!", type_); + } + } + + // Adds a UniqueTypeId to metadata mapping to the TypeMap. The method will + // fail if the mapping already exists. + fn register_unique_id_with_metadata(&mut self, + unique_type_id: UniqueTypeId, + metadata: DIType) { + if self.unique_id_to_metadata.insert(unique_type_id, metadata).is_some() { + bug!("Type metadata for unique id '{}' is already in the TypeMap!", + self.get_unique_type_id_as_string(unique_type_id)); + } + } + + fn find_metadata_for_type(&self, type_: Ty<'tcx>) -> Option { + self.type_to_metadata.get(&type_).cloned() + } + + fn find_metadata_for_unique_id(&self, unique_type_id: UniqueTypeId) -> Option { + self.unique_id_to_metadata.get(&unique_type_id).cloned() + } + + // Get the string representation of a UniqueTypeId. This method will fail if + // the id is unknown. + fn get_unique_type_id_as_string(&self, unique_type_id: UniqueTypeId) -> &str { + let UniqueTypeId(interner_key) = unique_type_id; + self.unique_id_interner.get(interner_key) + } + + // Get the UniqueTypeId for the given type. If the UniqueTypeId for the given + // type has been requested before, this is just a table lookup. Otherwise an + // ID will be generated and stored for later lookup. + fn get_unique_type_id_of_type<'a>(&mut self, cx: &CodegenCx<'a, 'tcx>, + type_: Ty<'tcx>) -> UniqueTypeId { + // Let's see if we already have something in the cache + match self.type_to_unique_id.get(&type_).cloned() { + Some(unique_type_id) => return unique_type_id, + None => { /* generate one */} + }; + + // The hasher we are using to generate the UniqueTypeId. We want + // something that provides more than the 64 bits of the DefaultHasher. + let mut type_id_hasher = TypeIdHasher::::new(cx.tcx); + type_id_hasher.visit_ty(type_); + let unique_type_id = type_id_hasher.finish().to_hex(); + + let key = self.unique_id_interner.intern(&unique_type_id); + self.type_to_unique_id.insert(type_, UniqueTypeId(key)); + + return UniqueTypeId(key); + } + + // Get the UniqueTypeId for an enum variant. Enum variants are not really + // types of their own, so they need special handling. We still need a + // UniqueTypeId for them, since to debuginfo they *are* real types. + fn get_unique_type_id_of_enum_variant<'a>(&mut self, + cx: &CodegenCx<'a, 'tcx>, + enum_type: Ty<'tcx>, + variant_name: &str) + -> UniqueTypeId { + let enum_type_id = self.get_unique_type_id_of_type(cx, enum_type); + let enum_variant_type_id = format!("{}::{}", + self.get_unique_type_id_as_string(enum_type_id), + variant_name); + let interner_key = self.unique_id_interner.intern(&enum_variant_type_id); + UniqueTypeId(interner_key) + } +} + +// A description of some recursive type. It can either be already finished (as +// with FinalMetadata) or it is not yet finished, but contains all information +// needed to generate the missing parts of the description. See the +// documentation section on Recursive Types at the top of this file for more +// information. +enum RecursiveTypeDescription<'tcx> { + UnfinishedMetadata { + unfinished_type: Ty<'tcx>, + unique_type_id: UniqueTypeId, + metadata_stub: DICompositeType, + member_description_factory: MemberDescriptionFactory<'tcx>, + }, + FinalMetadata(DICompositeType) +} + +fn create_and_register_recursive_type_forward_declaration<'a, 'tcx>( + cx: &CodegenCx<'a, 'tcx>, + unfinished_type: Ty<'tcx>, + unique_type_id: UniqueTypeId, + metadata_stub: DICompositeType, + member_description_factory: MemberDescriptionFactory<'tcx>) + -> RecursiveTypeDescription<'tcx> { + + // Insert the stub into the TypeMap in order to allow for recursive references + let mut type_map = debug_context(cx).type_map.borrow_mut(); + type_map.register_unique_id_with_metadata(unique_type_id, metadata_stub); + type_map.register_type_with_metadata(unfinished_type, metadata_stub); + + UnfinishedMetadata { + unfinished_type, + unique_type_id, + metadata_stub, + member_description_factory, + } +} + +impl<'tcx> RecursiveTypeDescription<'tcx> { + // Finishes up the description of the type in question (mostly by providing + // descriptions of the fields of the given type) and returns the final type + // metadata. + fn finalize<'a>(&self, cx: &CodegenCx<'a, 'tcx>) -> MetadataCreationResult { + match *self { + FinalMetadata(metadata) => MetadataCreationResult::new(metadata, false), + UnfinishedMetadata { + unfinished_type, + unique_type_id, + metadata_stub, + ref member_description_factory, + } => { + // Make sure that we have a forward declaration of the type in + // the TypeMap so that recursive references are possible. This + // will always be the case if the RecursiveTypeDescription has + // been properly created through the + // create_and_register_recursive_type_forward_declaration() + // function. + { + let type_map = debug_context(cx).type_map.borrow(); + if type_map.find_metadata_for_unique_id(unique_type_id).is_none() || + type_map.find_metadata_for_type(unfinished_type).is_none() { + bug!("Forward declaration of potentially recursive type \ + '{:?}' was not found in TypeMap!", + unfinished_type); + } + } + + // ... then create the member descriptions ... + let member_descriptions = + member_description_factory.create_member_descriptions(cx); + + // ... and attach them to the stub to complete it. + set_members_of_composite_type(cx, + metadata_stub, + &member_descriptions[..]); + return MetadataCreationResult::new(metadata_stub, true); + } + } + } +} + +// Returns from the enclosing function if the type metadata with the given +// unique id can be found in the type map +macro_rules! return_if_metadata_created_in_meantime { + ($cx: expr, $unique_type_id: expr) => ( + match debug_context($cx).type_map + .borrow() + .find_metadata_for_unique_id($unique_type_id) { + Some(metadata) => return MetadataCreationResult::new(metadata, true), + None => { /* proceed normally */ } + } + ) +} + +fn fixed_vec_metadata<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, + unique_type_id: UniqueTypeId, + array_or_slice_type: Ty<'tcx>, + element_type: Ty<'tcx>, + span: Span) + -> MetadataCreationResult { + let element_type_metadata = type_metadata(cx, element_type, span); + + return_if_metadata_created_in_meantime!(cx, unique_type_id); + + let (size, align) = cx.size_and_align_of(array_or_slice_type); + + let upper_bound = match array_or_slice_type.sty { + ty::TyArray(_, len) => { + len.unwrap_usize(cx.tcx) as c_longlong + } + _ => -1 + }; + + let subrange = unsafe { + llvm::LLVMRustDIBuilderGetOrCreateSubrange(DIB(cx), 0, upper_bound) + }; + + let subscripts = create_DIArray(DIB(cx), &[subrange]); + let metadata = unsafe { + llvm::LLVMRustDIBuilderCreateArrayType( + DIB(cx), + size.bits(), + align.abi_bits() as u32, + element_type_metadata, + subscripts) + }; + + return MetadataCreationResult::new(metadata, false); +} + +fn vec_slice_metadata<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, + slice_ptr_type: Ty<'tcx>, + element_type: Ty<'tcx>, + unique_type_id: UniqueTypeId, + span: Span) + -> MetadataCreationResult { + let data_ptr_type = cx.tcx.mk_imm_ptr(element_type); + + let data_ptr_metadata = type_metadata(cx, data_ptr_type, span); + + return_if_metadata_created_in_meantime!(cx, unique_type_id); + + let slice_type_name = compute_debuginfo_type_name(cx, slice_ptr_type, true); + + let (pointer_size, pointer_align) = cx.size_and_align_of(data_ptr_type); + let (usize_size, usize_align) = cx.size_and_align_of(cx.tcx.types.usize); + + let member_descriptions = [ + MemberDescription { + name: "data_ptr".to_string(), + type_metadata: data_ptr_metadata, + offset: Size::from_bytes(0), + size: pointer_size, + align: pointer_align, + flags: DIFlags::FlagZero, + }, + MemberDescription { + name: "length".to_string(), + type_metadata: type_metadata(cx, cx.tcx.types.usize, span), + offset: pointer_size, + size: usize_size, + align: usize_align, + flags: DIFlags::FlagZero, + }, + ]; + + let file_metadata = unknown_file_metadata(cx); + + let metadata = composite_type_metadata(cx, + slice_ptr_type, + &slice_type_name[..], + unique_type_id, + &member_descriptions, + NO_SCOPE_METADATA, + file_metadata, + span); + MetadataCreationResult::new(metadata, false) +} + +fn subroutine_type_metadata<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, + unique_type_id: UniqueTypeId, + signature: ty::PolyFnSig<'tcx>, + span: Span) + -> MetadataCreationResult +{ + let signature = cx.tcx.normalize_erasing_late_bound_regions( + ty::ParamEnv::reveal_all(), + &signature, + ); + + let mut signature_metadata: Vec = Vec::with_capacity(signature.inputs().len() + 1); + + // return type + signature_metadata.push(match signature.output().sty { + ty::TyTuple(ref tys) if tys.is_empty() => ptr::null_mut(), + _ => type_metadata(cx, signature.output(), span) + }); + + // regular arguments + for &argument_type in signature.inputs() { + signature_metadata.push(type_metadata(cx, argument_type, span)); + } + + return_if_metadata_created_in_meantime!(cx, unique_type_id); + + return MetadataCreationResult::new( + unsafe { + llvm::LLVMRustDIBuilderCreateSubroutineType( + DIB(cx), + unknown_file_metadata(cx), + create_DIArray(DIB(cx), &signature_metadata[..])) + }, + false); +} + +// FIXME(1563) This is all a bit of a hack because 'trait pointer' is an ill- +// defined concept. For the case of an actual trait pointer (i.e., Box, +// &Trait), trait_object_type should be the whole thing (e.g, Box) and +// trait_type should be the actual trait (e.g., Trait). Where the trait is part +// of a DST struct, there is no trait_object_type and the results of this +// function will be a little bit weird. +fn trait_pointer_metadata<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, + trait_type: Ty<'tcx>, + trait_object_type: Option>, + unique_type_id: UniqueTypeId) + -> DIType { + // The implementation provided here is a stub. It makes sure that the trait + // type is assigned the correct name, size, namespace, and source location. + // But it does not describe the trait's methods. + + let containing_scope = match trait_type.sty { + ty::TyDynamic(ref data, ..) => if let Some(principal) = data.principal() { + let def_id = principal.def_id(); + get_namespace_for_item(cx, def_id) + } else { + NO_SCOPE_METADATA + }, + _ => { + bug!("debuginfo: Unexpected trait-object type in \ + trait_pointer_metadata(): {:?}", + trait_type); + } + }; + + let trait_object_type = trait_object_type.unwrap_or(trait_type); + let trait_type_name = + compute_debuginfo_type_name(cx, trait_object_type, false); + + let file_metadata = unknown_file_metadata(cx); + + let layout = cx.layout_of(cx.tcx.mk_mut_ptr(trait_type)); + + assert_eq!(abi::FAT_PTR_ADDR, 0); + assert_eq!(abi::FAT_PTR_EXTRA, 1); + + let data_ptr_field = layout.field(cx, 0); + let vtable_field = layout.field(cx, 1); + let member_descriptions = [ + MemberDescription { + name: "pointer".to_string(), + type_metadata: type_metadata(cx, + cx.tcx.mk_mut_ptr(cx.tcx.types.u8), + syntax_pos::DUMMY_SP), + offset: layout.fields.offset(0), + size: data_ptr_field.size, + align: data_ptr_field.align, + flags: DIFlags::FlagArtificial, + }, + MemberDescription { + name: "vtable".to_string(), + type_metadata: type_metadata(cx, vtable_field.ty, syntax_pos::DUMMY_SP), + offset: layout.fields.offset(1), + size: vtable_field.size, + align: vtable_field.align, + flags: DIFlags::FlagArtificial, + }, + ]; + + composite_type_metadata(cx, + trait_object_type, + &trait_type_name[..], + unique_type_id, + &member_descriptions, + containing_scope, + file_metadata, + syntax_pos::DUMMY_SP) +} + +pub fn type_metadata<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, + t: Ty<'tcx>, + usage_site_span: Span) + -> DIType { + // Get the unique type id of this type. + let unique_type_id = { + let mut type_map = debug_context(cx).type_map.borrow_mut(); + // First, try to find the type in TypeMap. If we have seen it before, we + // can exit early here. + match type_map.find_metadata_for_type(t) { + Some(metadata) => { + return metadata; + }, + None => { + // The Ty is not in the TypeMap but maybe we have already seen + // an equivalent type (e.g. only differing in region arguments). + // In order to find out, generate the unique type id and look + // that up. + let unique_type_id = type_map.get_unique_type_id_of_type(cx, t); + match type_map.find_metadata_for_unique_id(unique_type_id) { + Some(metadata) => { + // There is already an equivalent type in the TypeMap. + // Register this Ty as an alias in the cache and + // return the cached metadata. + type_map.register_type_with_metadata(t, metadata); + return metadata; + }, + None => { + // There really is no type metadata for this type, so + // proceed by creating it. + unique_type_id + } + } + } + } + }; + + debug!("type_metadata: {:?}", t); + + let ptr_metadata = |ty: Ty<'tcx>| { + match ty.sty { + ty::TySlice(typ) => { + Ok(vec_slice_metadata(cx, t, typ, unique_type_id, usage_site_span)) + } + ty::TyStr => { + Ok(vec_slice_metadata(cx, t, cx.tcx.types.u8, unique_type_id, usage_site_span)) + } + ty::TyDynamic(..) => { + Ok(MetadataCreationResult::new( + trait_pointer_metadata(cx, ty, Some(t), unique_type_id), + false)) + } + _ => { + let pointee_metadata = type_metadata(cx, ty, usage_site_span); + + match debug_context(cx).type_map + .borrow() + .find_metadata_for_unique_id(unique_type_id) { + Some(metadata) => return Err(metadata), + None => { /* proceed normally */ } + }; + + Ok(MetadataCreationResult::new(pointer_type_metadata(cx, t, pointee_metadata), + false)) + } + } + }; + + let MetadataCreationResult { metadata, already_stored_in_typemap } = match t.sty { + ty::TyNever | + ty::TyBool | + ty::TyChar | + ty::TyInt(_) | + ty::TyUint(_) | + ty::TyFloat(_) => { + MetadataCreationResult::new(basic_type_metadata(cx, t), false) + } + ty::TyTuple(ref elements) if elements.is_empty() => { + MetadataCreationResult::new(basic_type_metadata(cx, t), false) + } + ty::TyArray(typ, _) | + ty::TySlice(typ) => { + fixed_vec_metadata(cx, unique_type_id, t, typ, usage_site_span) + } + ty::TyStr => { + fixed_vec_metadata(cx, unique_type_id, t, cx.tcx.types.i8, usage_site_span) + } + ty::TyDynamic(..) => { + MetadataCreationResult::new( + trait_pointer_metadata(cx, t, None, unique_type_id), + false) + } + ty::TyForeign(..) => { + MetadataCreationResult::new( + foreign_type_metadata(cx, t, unique_type_id), + false) + } + ty::TyRawPtr(ty::TypeAndMut{ty, ..}) | + ty::TyRef(_, ty, _) => { + match ptr_metadata(ty) { + Ok(res) => res, + Err(metadata) => return metadata, + } + } + ty::TyAdt(def, _) if def.is_box() => { + match ptr_metadata(t.boxed_ty()) { + Ok(res) => res, + Err(metadata) => return metadata, + } + } + ty::TyFnDef(..) | ty::TyFnPtr(_) => { + let fn_metadata = subroutine_type_metadata(cx, + unique_type_id, + t.fn_sig(cx.tcx), + usage_site_span).metadata; + match debug_context(cx).type_map + .borrow() + .find_metadata_for_unique_id(unique_type_id) { + Some(metadata) => return metadata, + None => { /* proceed normally */ } + }; + + // This is actually a function pointer, so wrap it in pointer DI + MetadataCreationResult::new(pointer_type_metadata(cx, t, fn_metadata), false) + + } + ty::TyClosure(def_id, substs) => { + let upvar_tys : Vec<_> = substs.upvar_tys(def_id, cx.tcx).collect(); + prepare_tuple_metadata(cx, + t, + &upvar_tys, + unique_type_id, + usage_site_span).finalize(cx) + } + ty::TyGenerator(def_id, substs, _) => { + let upvar_tys : Vec<_> = substs.field_tys(def_id, cx.tcx).map(|t| { + cx.tcx.normalize_erasing_regions(ParamEnv::reveal_all(), t) + }).collect(); + prepare_tuple_metadata(cx, + t, + &upvar_tys, + unique_type_id, + usage_site_span).finalize(cx) + } + ty::TyAdt(def, ..) => match def.adt_kind() { + AdtKind::Struct => { + prepare_struct_metadata(cx, + t, + unique_type_id, + usage_site_span).finalize(cx) + } + AdtKind::Union => { + prepare_union_metadata(cx, + t, + unique_type_id, + usage_site_span).finalize(cx) + } + AdtKind::Enum => { + prepare_enum_metadata(cx, + t, + def.did, + unique_type_id, + usage_site_span).finalize(cx) + } + }, + ty::TyTuple(ref elements) => { + prepare_tuple_metadata(cx, + t, + &elements[..], + unique_type_id, + usage_site_span).finalize(cx) + } + _ => { + bug!("debuginfo: unexpected type in type_metadata: {:?}", t) + } + }; + + { + let mut type_map = debug_context(cx).type_map.borrow_mut(); + + if already_stored_in_typemap { + // Also make sure that we already have a TypeMap entry for the unique type id. + let metadata_for_uid = match type_map.find_metadata_for_unique_id(unique_type_id) { + Some(metadata) => metadata, + None => { + span_bug!(usage_site_span, + "Expected type metadata for unique \ + type id '{}' to already be in \ + the debuginfo::TypeMap but it \ + was not. (Ty = {})", + type_map.get_unique_type_id_as_string(unique_type_id), + t); + } + }; + + match type_map.find_metadata_for_type(t) { + Some(metadata) => { + if metadata != metadata_for_uid { + span_bug!(usage_site_span, + "Mismatch between Ty and \ + UniqueTypeId maps in \ + debuginfo::TypeMap. \ + UniqueTypeId={}, Ty={}", + type_map.get_unique_type_id_as_string(unique_type_id), + t); + } + } + None => { + type_map.register_type_with_metadata(t, metadata); + } + } + } else { + type_map.register_type_with_metadata(t, metadata); + type_map.register_unique_id_with_metadata(unique_type_id, metadata); + } + } + + metadata +} + +pub fn file_metadata(cx: &CodegenCx, + file_name: &FileName, + defining_crate: CrateNum) -> DIFile { + debug!("file_metadata: file_name: {}, defining_crate: {}", + file_name, + defining_crate); + + let directory = if defining_crate == LOCAL_CRATE { + &cx.sess().working_dir.0 + } else { + // If the path comes from an upstream crate we assume it has been made + // independent of the compiler's working directory one way or another. + Path::new("") + }; + + file_metadata_raw(cx, &file_name.to_string(), &directory.to_string_lossy()) +} + +pub fn unknown_file_metadata(cx: &CodegenCx) -> DIFile { + file_metadata_raw(cx, "", "") +} + +fn file_metadata_raw(cx: &CodegenCx, + file_name: &str, + directory: &str) + -> DIFile { + let key = (Symbol::intern(file_name), Symbol::intern(directory)); + + if let Some(file_metadata) = debug_context(cx).created_files.borrow().get(&key) { + return *file_metadata; + } + + debug!("file_metadata: file_name: {}, directory: {}", file_name, directory); + + let file_name = CString::new(file_name).unwrap(); + let directory = CString::new(directory).unwrap(); + + let file_metadata = unsafe { + llvm::LLVMRustDIBuilderCreateFile(DIB(cx), + file_name.as_ptr(), + directory.as_ptr()) + }; + + let mut created_files = debug_context(cx).created_files.borrow_mut(); + created_files.insert(key, file_metadata); + file_metadata +} + +fn basic_type_metadata<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, + t: Ty<'tcx>) -> DIType { + + debug!("basic_type_metadata: {:?}", t); + + let (name, encoding) = match t.sty { + ty::TyNever => ("!", DW_ATE_unsigned), + ty::TyTuple(ref elements) if elements.is_empty() => + ("()", DW_ATE_unsigned), + ty::TyBool => ("bool", DW_ATE_boolean), + ty::TyChar => ("char", DW_ATE_unsigned_char), + ty::TyInt(int_ty) => { + (int_ty.ty_to_string(), DW_ATE_signed) + }, + ty::TyUint(uint_ty) => { + (uint_ty.ty_to_string(), DW_ATE_unsigned) + }, + ty::TyFloat(float_ty) => { + (float_ty.ty_to_string(), DW_ATE_float) + }, + _ => bug!("debuginfo::basic_type_metadata - t is invalid type") + }; + + let (size, align) = cx.size_and_align_of(t); + let name = CString::new(name).unwrap(); + let ty_metadata = unsafe { + llvm::LLVMRustDIBuilderCreateBasicType( + DIB(cx), + name.as_ptr(), + size.bits(), + align.abi_bits() as u32, + encoding) + }; + + return ty_metadata; +} + +fn foreign_type_metadata<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, + t: Ty<'tcx>, + unique_type_id: UniqueTypeId) -> DIType { + debug!("foreign_type_metadata: {:?}", t); + + let name = compute_debuginfo_type_name(cx, t, false); + create_struct_stub(cx, t, &name, unique_type_id, NO_SCOPE_METADATA) +} + +fn pointer_type_metadata<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, + pointer_type: Ty<'tcx>, + pointee_type_metadata: DIType) + -> DIType { + let (pointer_size, pointer_align) = cx.size_and_align_of(pointer_type); + let name = compute_debuginfo_type_name(cx, pointer_type, false); + let name = CString::new(name).unwrap(); + unsafe { + llvm::LLVMRustDIBuilderCreatePointerType( + DIB(cx), + pointee_type_metadata, + pointer_size.bits(), + pointer_align.abi_bits() as u32, + name.as_ptr()) + } +} + +pub fn compile_unit_metadata(tcx: TyCtxt, + codegen_unit_name: &str, + debug_context: &CrateDebugContext) + -> DIDescriptor { + let mut name_in_debuginfo = match tcx.sess.local_crate_source_file { + Some(ref path) => path.clone(), + None => PathBuf::from(&*tcx.crate_name(LOCAL_CRATE).as_str()), + }; + + // The OSX linker has an idiosyncrasy where it will ignore some debuginfo + // if multiple object files with the same DW_AT_name are linked together. + // As a workaround we generate unique names for each object file. Those do + // not correspond to an actual source file but that should be harmless. + if tcx.sess.target.target.options.is_like_osx { + name_in_debuginfo.push("@"); + name_in_debuginfo.push(codegen_unit_name); + } + + debug!("compile_unit_metadata: {:?}", name_in_debuginfo); + // FIXME(#41252) Remove "clang LLVM" if we can get GDB and LLVM to play nice. + let producer = format!("clang LLVM (rustc version {})", + (option_env!("CFG_VERSION")).expect("CFG_VERSION")); + + let name_in_debuginfo = name_in_debuginfo.to_string_lossy().into_owned(); + let name_in_debuginfo = CString::new(name_in_debuginfo).unwrap(); + let work_dir = CString::new(&tcx.sess.working_dir.0.to_string_lossy()[..]).unwrap(); + let producer = CString::new(producer).unwrap(); + let flags = "\0"; + let split_name = "\0"; + + unsafe { + let file_metadata = llvm::LLVMRustDIBuilderCreateFile( + debug_context.builder, name_in_debuginfo.as_ptr(), work_dir.as_ptr()); + + let unit_metadata = llvm::LLVMRustDIBuilderCreateCompileUnit( + debug_context.builder, + DW_LANG_RUST, + file_metadata, + producer.as_ptr(), + tcx.sess.opts.optimize != config::OptLevel::No, + flags.as_ptr() as *const _, + 0, + split_name.as_ptr() as *const _); + + if tcx.sess.opts.debugging_opts.profile { + let cu_desc_metadata = llvm::LLVMRustMetadataAsValue(debug_context.llcontext, + unit_metadata); + + let gcov_cu_info = [ + path_to_mdstring(debug_context.llcontext, + &tcx.output_filenames(LOCAL_CRATE).with_extension("gcno")), + path_to_mdstring(debug_context.llcontext, + &tcx.output_filenames(LOCAL_CRATE).with_extension("gcda")), + cu_desc_metadata, + ]; + let gcov_metadata = llvm::LLVMMDNodeInContext(debug_context.llcontext, + gcov_cu_info.as_ptr(), + gcov_cu_info.len() as c_uint); + + let llvm_gcov_ident = CString::new("llvm.gcov").unwrap(); + llvm::LLVMAddNamedMetadataOperand(debug_context.llmod, + llvm_gcov_ident.as_ptr(), + gcov_metadata); + } + + return unit_metadata; + }; + + fn path_to_mdstring(llcx: llvm::ContextRef, path: &Path) -> llvm::ValueRef { + let path_str = path2cstr(path); + unsafe { + llvm::LLVMMDStringInContext(llcx, + path_str.as_ptr(), + path_str.as_bytes().len() as c_uint) + } + } +} + +struct MetadataCreationResult { + metadata: DIType, + already_stored_in_typemap: bool +} + +impl MetadataCreationResult { + fn new(metadata: DIType, already_stored_in_typemap: bool) -> MetadataCreationResult { + MetadataCreationResult { + metadata, + already_stored_in_typemap, + } + } +} + +// Description of a type member, which can either be a regular field (as in +// structs or tuples) or an enum variant. +#[derive(Debug)] +struct MemberDescription { + name: String, + type_metadata: DIType, + offset: Size, + size: Size, + align: Align, + flags: DIFlags, +} + +// A factory for MemberDescriptions. It produces a list of member descriptions +// for some record-like type. MemberDescriptionFactories are used to defer the +// creation of type member descriptions in order to break cycles arising from +// recursive type definitions. +enum MemberDescriptionFactory<'tcx> { + StructMDF(StructMemberDescriptionFactory<'tcx>), + TupleMDF(TupleMemberDescriptionFactory<'tcx>), + EnumMDF(EnumMemberDescriptionFactory<'tcx>), + UnionMDF(UnionMemberDescriptionFactory<'tcx>), + VariantMDF(VariantMemberDescriptionFactory<'tcx>) +} + +impl<'tcx> MemberDescriptionFactory<'tcx> { + fn create_member_descriptions<'a>(&self, cx: &CodegenCx<'a, 'tcx>) + -> Vec { + match *self { + StructMDF(ref this) => { + this.create_member_descriptions(cx) + } + TupleMDF(ref this) => { + this.create_member_descriptions(cx) + } + EnumMDF(ref this) => { + this.create_member_descriptions(cx) + } + UnionMDF(ref this) => { + this.create_member_descriptions(cx) + } + VariantMDF(ref this) => { + this.create_member_descriptions(cx) + } + } + } +} + +//=----------------------------------------------------------------------------- +// Structs +//=----------------------------------------------------------------------------- + +// Creates MemberDescriptions for the fields of a struct +struct StructMemberDescriptionFactory<'tcx> { + ty: Ty<'tcx>, + variant: &'tcx ty::VariantDef, + span: Span, +} + +impl<'tcx> StructMemberDescriptionFactory<'tcx> { + fn create_member_descriptions<'a>(&self, cx: &CodegenCx<'a, 'tcx>) + -> Vec { + let layout = cx.layout_of(self.ty); + self.variant.fields.iter().enumerate().map(|(i, f)| { + let name = if self.variant.ctor_kind == CtorKind::Fn { + format!("__{}", i) + } else { + f.name.to_string() + }; + let field = layout.field(cx, i); + let (size, align) = field.size_and_align(); + MemberDescription { + name, + type_metadata: type_metadata(cx, field.ty, self.span), + offset: layout.fields.offset(i), + size, + align, + flags: DIFlags::FlagZero, + } + }).collect() + } +} + + +fn prepare_struct_metadata<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, + struct_type: Ty<'tcx>, + unique_type_id: UniqueTypeId, + span: Span) + -> RecursiveTypeDescription<'tcx> { + let struct_name = compute_debuginfo_type_name(cx, struct_type, false); + + let (struct_def_id, variant) = match struct_type.sty { + ty::TyAdt(def, _) => (def.did, def.non_enum_variant()), + _ => bug!("prepare_struct_metadata on a non-ADT") + }; + + let containing_scope = get_namespace_for_item(cx, struct_def_id); + + let struct_metadata_stub = create_struct_stub(cx, + struct_type, + &struct_name, + unique_type_id, + containing_scope); + + create_and_register_recursive_type_forward_declaration( + cx, + struct_type, + unique_type_id, + struct_metadata_stub, + StructMDF(StructMemberDescriptionFactory { + ty: struct_type, + variant, + span, + }) + ) +} + +//=----------------------------------------------------------------------------- +// Tuples +//=----------------------------------------------------------------------------- + +// Creates MemberDescriptions for the fields of a tuple +struct TupleMemberDescriptionFactory<'tcx> { + ty: Ty<'tcx>, + component_types: Vec>, + span: Span, +} + +impl<'tcx> TupleMemberDescriptionFactory<'tcx> { + fn create_member_descriptions<'a>(&self, cx: &CodegenCx<'a, 'tcx>) + -> Vec { + let layout = cx.layout_of(self.ty); + self.component_types.iter().enumerate().map(|(i, &component_type)| { + let (size, align) = cx.size_and_align_of(component_type); + MemberDescription { + name: format!("__{}", i), + type_metadata: type_metadata(cx, component_type, self.span), + offset: layout.fields.offset(i), + size, + align, + flags: DIFlags::FlagZero, + } + }).collect() + } +} + +fn prepare_tuple_metadata<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, + tuple_type: Ty<'tcx>, + component_types: &[Ty<'tcx>], + unique_type_id: UniqueTypeId, + span: Span) + -> RecursiveTypeDescription<'tcx> { + let tuple_name = compute_debuginfo_type_name(cx, tuple_type, false); + + create_and_register_recursive_type_forward_declaration( + cx, + tuple_type, + unique_type_id, + create_struct_stub(cx, + tuple_type, + &tuple_name[..], + unique_type_id, + NO_SCOPE_METADATA), + TupleMDF(TupleMemberDescriptionFactory { + ty: tuple_type, + component_types: component_types.to_vec(), + span, + }) + ) +} + +//=----------------------------------------------------------------------------- +// Unions +//=----------------------------------------------------------------------------- + +struct UnionMemberDescriptionFactory<'tcx> { + layout: TyLayout<'tcx>, + variant: &'tcx ty::VariantDef, + span: Span, +} + +impl<'tcx> UnionMemberDescriptionFactory<'tcx> { + fn create_member_descriptions<'a>(&self, cx: &CodegenCx<'a, 'tcx>) + -> Vec { + self.variant.fields.iter().enumerate().map(|(i, f)| { + let field = self.layout.field(cx, i); + let (size, align) = field.size_and_align(); + MemberDescription { + name: f.name.to_string(), + type_metadata: type_metadata(cx, field.ty, self.span), + offset: Size::from_bytes(0), + size, + align, + flags: DIFlags::FlagZero, + } + }).collect() + } +} + +fn prepare_union_metadata<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, + union_type: Ty<'tcx>, + unique_type_id: UniqueTypeId, + span: Span) + -> RecursiveTypeDescription<'tcx> { + let union_name = compute_debuginfo_type_name(cx, union_type, false); + + let (union_def_id, variant) = match union_type.sty { + ty::TyAdt(def, _) => (def.did, def.non_enum_variant()), + _ => bug!("prepare_union_metadata on a non-ADT") + }; + + let containing_scope = get_namespace_for_item(cx, union_def_id); + + let union_metadata_stub = create_union_stub(cx, + union_type, + &union_name, + unique_type_id, + containing_scope); + + create_and_register_recursive_type_forward_declaration( + cx, + union_type, + unique_type_id, + union_metadata_stub, + UnionMDF(UnionMemberDescriptionFactory { + layout: cx.layout_of(union_type), + variant, + span, + }) + ) +} + +//=----------------------------------------------------------------------------- +// Enums +//=----------------------------------------------------------------------------- + +// Describes the members of an enum value: An enum is described as a union of +// structs in DWARF. This MemberDescriptionFactory provides the description for +// the members of this union; so for every variant of the given enum, this +// factory will produce one MemberDescription (all with no name and a fixed +// offset of zero bytes). +struct EnumMemberDescriptionFactory<'tcx> { + enum_type: Ty<'tcx>, + layout: TyLayout<'tcx>, + discriminant_type_metadata: Option, + containing_scope: DIScope, + span: Span, +} + +impl<'tcx> EnumMemberDescriptionFactory<'tcx> { + fn create_member_descriptions<'a>(&self, cx: &CodegenCx<'a, 'tcx>) + -> Vec { + let adt = &self.enum_type.ty_adt_def().unwrap(); + match self.layout.variants { + layout::Variants::Single { .. } if adt.variants.is_empty() => vec![], + layout::Variants::Single { index } => { + let (variant_type_metadata, member_description_factory) = + describe_enum_variant(cx, + self.layout, + &adt.variants[index], + NoDiscriminant, + self.containing_scope, + self.span); + + let member_descriptions = + member_description_factory.create_member_descriptions(cx); + + set_members_of_composite_type(cx, + variant_type_metadata, + &member_descriptions[..]); + vec![ + MemberDescription { + name: "".to_string(), + type_metadata: variant_type_metadata, + offset: Size::from_bytes(0), + size: self.layout.size, + align: self.layout.align, + flags: DIFlags::FlagZero + } + ] + } + layout::Variants::Tagged { ref variants, .. } => { + let discriminant_info = RegularDiscriminant(self.discriminant_type_metadata + .expect("")); + (0..variants.len()).map(|i| { + let variant = self.layout.for_variant(cx, i); + let (variant_type_metadata, member_desc_factory) = + describe_enum_variant(cx, + variant, + &adt.variants[i], + discriminant_info, + self.containing_scope, + self.span); + + let member_descriptions = member_desc_factory + .create_member_descriptions(cx); + + set_members_of_composite_type(cx, + variant_type_metadata, + &member_descriptions); + MemberDescription { + name: "".to_string(), + type_metadata: variant_type_metadata, + offset: Size::from_bytes(0), + size: variant.size, + align: variant.align, + flags: DIFlags::FlagZero + } + }).collect() + } + layout::Variants::NicheFilling { dataful_variant, ref niche_variants, .. } => { + let variant = self.layout.for_variant(cx, dataful_variant); + // Create a description of the non-null variant + let (variant_type_metadata, member_description_factory) = + describe_enum_variant(cx, + variant, + &adt.variants[dataful_variant], + OptimizedDiscriminant, + self.containing_scope, + self.span); + + let variant_member_descriptions = + member_description_factory.create_member_descriptions(cx); + + set_members_of_composite_type(cx, + variant_type_metadata, + &variant_member_descriptions[..]); + + // Encode the information about the null variant in the union + // member's name. + let mut name = String::from("RUST$ENCODED$ENUM$"); + // HACK(eddyb) the debuggers should just handle offset+size + // of discriminant instead of us having to recover its path. + // Right now it's not even going to work for `niche_start > 0`, + // and for multiple niche variants it only supports the first. + fn compute_field_path<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, + name: &mut String, + layout: TyLayout<'tcx>, + offset: Size, + size: Size) { + for i in 0..layout.fields.count() { + let field_offset = layout.fields.offset(i); + if field_offset > offset { + continue; + } + let inner_offset = offset - field_offset; + let field = layout.field(cx, i); + if inner_offset + size <= field.size { + write!(name, "{}$", i).unwrap(); + compute_field_path(cx, name, field, inner_offset, size); + } + } + } + compute_field_path(cx, &mut name, + self.layout, + self.layout.fields.offset(0), + self.layout.field(cx, 0).size); + name.push_str(&adt.variants[*niche_variants.start()].name.as_str()); + + // Create the (singleton) list of descriptions of union members. + vec![ + MemberDescription { + name, + type_metadata: variant_type_metadata, + offset: Size::from_bytes(0), + size: variant.size, + align: variant.align, + flags: DIFlags::FlagZero + } + ] + } + } + } +} + +// Creates MemberDescriptions for the fields of a single enum variant. +struct VariantMemberDescriptionFactory<'tcx> { + // Cloned from the layout::Struct describing the variant. + offsets: Vec, + args: Vec<(String, Ty<'tcx>)>, + discriminant_type_metadata: Option, + span: Span, +} + +impl<'tcx> VariantMemberDescriptionFactory<'tcx> { + fn create_member_descriptions<'a>(&self, cx: &CodegenCx<'a, 'tcx>) + -> Vec { + self.args.iter().enumerate().map(|(i, &(ref name, ty))| { + let (size, align) = cx.size_and_align_of(ty); + MemberDescription { + name: name.to_string(), + type_metadata: match self.discriminant_type_metadata { + Some(metadata) if i == 0 => metadata, + _ => type_metadata(cx, ty, self.span) + }, + offset: self.offsets[i], + size, + align, + flags: DIFlags::FlagZero + } + }).collect() + } +} + +#[derive(Copy, Clone)] +enum EnumDiscriminantInfo { + RegularDiscriminant(DIType), + OptimizedDiscriminant, + NoDiscriminant +} + +// Returns a tuple of (1) type_metadata_stub of the variant, (2) the llvm_type +// of the variant, and (3) a MemberDescriptionFactory for producing the +// descriptions of the fields of the variant. This is a rudimentary version of a +// full RecursiveTypeDescription. +fn describe_enum_variant<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, + layout: layout::TyLayout<'tcx>, + variant: &'tcx ty::VariantDef, + discriminant_info: EnumDiscriminantInfo, + containing_scope: DIScope, + span: Span) + -> (DICompositeType, MemberDescriptionFactory<'tcx>) { + let variant_name = variant.name.as_str(); + let unique_type_id = debug_context(cx).type_map + .borrow_mut() + .get_unique_type_id_of_enum_variant( + cx, + layout.ty, + &variant_name); + + let metadata_stub = create_struct_stub(cx, + layout.ty, + &variant_name, + unique_type_id, + containing_scope); + + // If this is not a univariant enum, there is also the discriminant field. + let (discr_offset, discr_arg) = match discriminant_info { + RegularDiscriminant(_) => { + let enum_layout = cx.layout_of(layout.ty); + (Some(enum_layout.fields.offset(0)), + Some(("RUST$ENUM$DISR".to_string(), enum_layout.field(cx, 0).ty))) + } + _ => (None, None), + }; + let offsets = discr_offset.into_iter().chain((0..layout.fields.count()).map(|i| { + layout.fields.offset(i) + })).collect(); + + // Build an array of (field name, field type) pairs to be captured in the factory closure. + let args = discr_arg.into_iter().chain((0..layout.fields.count()).map(|i| { + let name = if variant.ctor_kind == CtorKind::Fn { + format!("__{}", i) + } else { + variant.fields[i].name.to_string() + }; + (name, layout.field(cx, i).ty) + })).collect(); + + let member_description_factory = + VariantMDF(VariantMemberDescriptionFactory { + offsets, + args, + discriminant_type_metadata: match discriminant_info { + RegularDiscriminant(discriminant_type_metadata) => { + Some(discriminant_type_metadata) + } + _ => None + }, + span, + }); + + (metadata_stub, member_description_factory) +} + +fn prepare_enum_metadata<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, + enum_type: Ty<'tcx>, + enum_def_id: DefId, + unique_type_id: UniqueTypeId, + span: Span) + -> RecursiveTypeDescription<'tcx> { + let enum_name = compute_debuginfo_type_name(cx, enum_type, false); + + let containing_scope = get_namespace_for_item(cx, enum_def_id); + // FIXME: This should emit actual file metadata for the enum, but we + // currently can't get the necessary information when it comes to types + // imported from other crates. Formerly we violated the ODR when performing + // LTO because we emitted debuginfo for the same type with varying file + // metadata, so as a workaround we pretend that the type comes from + // + let file_metadata = unknown_file_metadata(cx); + + let def = enum_type.ty_adt_def().unwrap(); + let enumerators_metadata: Vec = def.discriminants(cx.tcx) + .zip(&def.variants) + .map(|(discr, v)| { + let token = v.name.as_str(); + let name = CString::new(token.as_bytes()).unwrap(); + unsafe { + llvm::LLVMRustDIBuilderCreateEnumerator( + DIB(cx), + name.as_ptr(), + // FIXME: what if enumeration has i128 discriminant? + discr.val as u64) + } + }) + .collect(); + + let discriminant_type_metadata = |discr: layout::Primitive| { + let disr_type_key = (enum_def_id, discr); + let cached_discriminant_type_metadata = debug_context(cx).created_enum_disr_types + .borrow() + .get(&disr_type_key).cloned(); + match cached_discriminant_type_metadata { + Some(discriminant_type_metadata) => discriminant_type_metadata, + None => { + let (discriminant_size, discriminant_align) = + (discr.size(cx), discr.align(cx)); + let discriminant_base_type_metadata = + type_metadata(cx, discr.to_ty(cx.tcx), syntax_pos::DUMMY_SP); + let discriminant_name = get_enum_discriminant_name(cx, enum_def_id).as_str(); + + let name = CString::new(discriminant_name.as_bytes()).unwrap(); + let discriminant_type_metadata = unsafe { + llvm::LLVMRustDIBuilderCreateEnumerationType( + DIB(cx), + containing_scope, + name.as_ptr(), + file_metadata, + UNKNOWN_LINE_NUMBER, + discriminant_size.bits(), + discriminant_align.abi_bits() as u32, + create_DIArray(DIB(cx), &enumerators_metadata), + discriminant_base_type_metadata) + }; + + debug_context(cx).created_enum_disr_types + .borrow_mut() + .insert(disr_type_key, discriminant_type_metadata); + + discriminant_type_metadata + } + } + }; + + let layout = cx.layout_of(enum_type); + + let discriminant_type_metadata = match layout.variants { + layout::Variants::Single { .. } | + layout::Variants::NicheFilling { .. } => None, + layout::Variants::Tagged { ref tag, .. } => { + Some(discriminant_type_metadata(tag.value)) + } + }; + + match (&layout.abi, discriminant_type_metadata) { + (&layout::Abi::Scalar(_), Some(discr)) => return FinalMetadata(discr), + _ => {} + } + + let (enum_type_size, enum_type_align) = layout.size_and_align(); + + let enum_name = CString::new(enum_name).unwrap(); + let unique_type_id_str = CString::new( + debug_context(cx).type_map.borrow().get_unique_type_id_as_string(unique_type_id).as_bytes() + ).unwrap(); + let enum_metadata = unsafe { + llvm::LLVMRustDIBuilderCreateUnionType( + DIB(cx), + containing_scope, + enum_name.as_ptr(), + file_metadata, + UNKNOWN_LINE_NUMBER, + enum_type_size.bits(), + enum_type_align.abi_bits() as u32, + DIFlags::FlagZero, + ptr::null_mut(), + 0, // RuntimeLang + unique_type_id_str.as_ptr()) + }; + + return create_and_register_recursive_type_forward_declaration( + cx, + enum_type, + unique_type_id, + enum_metadata, + EnumMDF(EnumMemberDescriptionFactory { + enum_type, + layout, + discriminant_type_metadata, + containing_scope, + span, + }), + ); + + fn get_enum_discriminant_name(cx: &CodegenCx, + def_id: DefId) + -> InternedString { + cx.tcx.item_name(def_id) + } +} + +/// Creates debug information for a composite type, that is, anything that +/// results in a LLVM struct. +/// +/// Examples of Rust types to use this are: structs, tuples, boxes, vecs, and enums. +fn composite_type_metadata<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, + composite_type: Ty<'tcx>, + composite_type_name: &str, + composite_type_unique_id: UniqueTypeId, + member_descriptions: &[MemberDescription], + containing_scope: DIScope, + + // Ignore source location information as long as it + // can't be reconstructed for non-local crates. + _file_metadata: DIFile, + _definition_span: Span) + -> DICompositeType { + // Create the (empty) struct metadata node ... + let composite_type_metadata = create_struct_stub(cx, + composite_type, + composite_type_name, + composite_type_unique_id, + containing_scope); + // ... and immediately create and add the member descriptions. + set_members_of_composite_type(cx, + composite_type_metadata, + member_descriptions); + + return composite_type_metadata; +} + +fn set_members_of_composite_type(cx: &CodegenCx, + composite_type_metadata: DICompositeType, + member_descriptions: &[MemberDescription]) { + // In some rare cases LLVM metadata uniquing would lead to an existing type + // description being used instead of a new one created in + // create_struct_stub. This would cause a hard to trace assertion in + // DICompositeType::SetTypeArray(). The following check makes sure that we + // get a better error message if this should happen again due to some + // regression. + { + let mut composite_types_completed = + debug_context(cx).composite_types_completed.borrow_mut(); + if composite_types_completed.contains(&composite_type_metadata) { + bug!("debuginfo::set_members_of_composite_type() - \ + Already completed forward declaration re-encountered."); + } else { + composite_types_completed.insert(composite_type_metadata); + } + } + + let member_metadata: Vec = member_descriptions + .iter() + .map(|member_description| { + let member_name = member_description.name.as_bytes(); + let member_name = CString::new(member_name).unwrap(); + unsafe { + llvm::LLVMRustDIBuilderCreateMemberType( + DIB(cx), + composite_type_metadata, + member_name.as_ptr(), + unknown_file_metadata(cx), + UNKNOWN_LINE_NUMBER, + member_description.size.bits(), + member_description.align.abi_bits() as u32, + member_description.offset.bits(), + member_description.flags, + member_description.type_metadata) + } + }) + .collect(); + + unsafe { + let type_array = create_DIArray(DIB(cx), &member_metadata[..]); + llvm::LLVMRustDICompositeTypeSetTypeArray( + DIB(cx), composite_type_metadata, type_array); + } +} + +// A convenience wrapper around LLVMRustDIBuilderCreateStructType(). Does not do +// any caching, does not add any fields to the struct. This can be done later +// with set_members_of_composite_type(). +fn create_struct_stub<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, + struct_type: Ty<'tcx>, + struct_type_name: &str, + unique_type_id: UniqueTypeId, + containing_scope: DIScope) + -> DICompositeType { + let (struct_size, struct_align) = cx.size_and_align_of(struct_type); + + let name = CString::new(struct_type_name).unwrap(); + let unique_type_id = CString::new( + debug_context(cx).type_map.borrow().get_unique_type_id_as_string(unique_type_id).as_bytes() + ).unwrap(); + let metadata_stub = unsafe { + // LLVMRustDIBuilderCreateStructType() wants an empty array. A null + // pointer will lead to hard to trace and debug LLVM assertions + // later on in llvm/lib/IR/Value.cpp. + let empty_array = create_DIArray(DIB(cx), &[]); + + llvm::LLVMRustDIBuilderCreateStructType( + DIB(cx), + containing_scope, + name.as_ptr(), + unknown_file_metadata(cx), + UNKNOWN_LINE_NUMBER, + struct_size.bits(), + struct_align.abi_bits() as u32, + DIFlags::FlagZero, + ptr::null_mut(), + empty_array, + 0, + ptr::null_mut(), + unique_type_id.as_ptr()) + }; + + return metadata_stub; +} + +fn create_union_stub<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, + union_type: Ty<'tcx>, + union_type_name: &str, + unique_type_id: UniqueTypeId, + containing_scope: DIScope) + -> DICompositeType { + let (union_size, union_align) = cx.size_and_align_of(union_type); + + let name = CString::new(union_type_name).unwrap(); + let unique_type_id = CString::new( + debug_context(cx).type_map.borrow().get_unique_type_id_as_string(unique_type_id).as_bytes() + ).unwrap(); + let metadata_stub = unsafe { + // LLVMRustDIBuilderCreateUnionType() wants an empty array. A null + // pointer will lead to hard to trace and debug LLVM assertions + // later on in llvm/lib/IR/Value.cpp. + let empty_array = create_DIArray(DIB(cx), &[]); + + llvm::LLVMRustDIBuilderCreateUnionType( + DIB(cx), + containing_scope, + name.as_ptr(), + unknown_file_metadata(cx), + UNKNOWN_LINE_NUMBER, + union_size.bits(), + union_align.abi_bits() as u32, + DIFlags::FlagZero, + empty_array, + 0, // RuntimeLang + unique_type_id.as_ptr()) + }; + + return metadata_stub; +} + +/// Creates debug information for the given global variable. +/// +/// Adds the created metadata nodes directly to the crate's IR. +pub fn create_global_var_metadata(cx: &CodegenCx, + def_id: DefId, + global: ValueRef) { + if cx.dbg_cx.is_none() { + return; + } + + let tcx = cx.tcx; + let attrs = tcx.codegen_fn_attrs(def_id); + + if attrs.flags.contains(CodegenFnAttrFlags::NO_DEBUG) { + return; + } + + let no_mangle = attrs.flags.contains(CodegenFnAttrFlags::NO_MANGLE); + // We may want to remove the namespace scope if we're in an extern block, see: + // https://github.com/rust-lang/rust/pull/46457#issuecomment-351750952 + let var_scope = get_namespace_for_item(cx, def_id); + let span = tcx.def_span(def_id); + + let (file_metadata, line_number) = if span != syntax_pos::DUMMY_SP { + let loc = span_start(cx, span); + (file_metadata(cx, &loc.file.name, LOCAL_CRATE), loc.line as c_uint) + } else { + (unknown_file_metadata(cx), UNKNOWN_LINE_NUMBER) + }; + + let is_local_to_unit = is_node_local_to_unit(cx, def_id); + let variable_type = Instance::mono(cx.tcx, def_id).ty(cx.tcx); + let type_metadata = type_metadata(cx, variable_type, span); + let var_name = tcx.item_name(def_id).to_string(); + let var_name = CString::new(var_name).unwrap(); + let linkage_name = if no_mangle { + None + } else { + let linkage_name = mangled_name_of_instance(cx, Instance::mono(tcx, def_id)); + Some(CString::new(linkage_name.to_string()).unwrap()) + }; + + let global_align = cx.align_of(variable_type); + + unsafe { + llvm::LLVMRustDIBuilderCreateStaticVariable(DIB(cx), + var_scope, + var_name.as_ptr(), + // If null, linkage_name field is omitted, + // which is what we want for no_mangle statics + linkage_name.as_ref() + .map_or(ptr::null(), |name| name.as_ptr()), + file_metadata, + line_number, + type_metadata, + is_local_to_unit, + global, + ptr::null_mut(), + global_align.abi() as u32, + ); + } +} + +// Creates an "extension" of an existing DIScope into another file. +pub fn extend_scope_to_file(cx: &CodegenCx, + scope_metadata: DIScope, + file: &syntax_pos::FileMap, + defining_crate: CrateNum) + -> DILexicalBlock { + let file_metadata = file_metadata(cx, &file.name, defining_crate); + unsafe { + llvm::LLVMRustDIBuilderCreateLexicalBlockFile( + DIB(cx), + scope_metadata, + file_metadata) + } +} + +/// Creates debug information for the given vtable, which is for the +/// given type. +/// +/// Adds the created metadata nodes directly to the crate's IR. +pub fn create_vtable_metadata<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, + ty: ty::Ty<'tcx>, + vtable: ValueRef) { + if cx.dbg_cx.is_none() { + return; + } + + let type_metadata = type_metadata(cx, ty, syntax_pos::DUMMY_SP); + + unsafe { + // LLVMRustDIBuilderCreateStructType() wants an empty array. A null + // pointer will lead to hard to trace and debug LLVM assertions + // later on in llvm/lib/IR/Value.cpp. + let empty_array = create_DIArray(DIB(cx), &[]); + + let name = CString::new("vtable").unwrap(); + + // Create a new one each time. We don't want metadata caching + // here, because each vtable will refer to a unique containing + // type. + let vtable_type = llvm::LLVMRustDIBuilderCreateStructType( + DIB(cx), + NO_SCOPE_METADATA, + name.as_ptr(), + unknown_file_metadata(cx), + UNKNOWN_LINE_NUMBER, + Size::from_bytes(0).bits(), + cx.tcx.data_layout.pointer_align.abi_bits() as u32, + DIFlags::FlagArtificial, + ptr::null_mut(), + empty_array, + 0, + type_metadata, + name.as_ptr() + ); + + llvm::LLVMRustDIBuilderCreateStaticVariable(DIB(cx), + NO_SCOPE_METADATA, + name.as_ptr(), + // LLVM 3.9 + // doesn't accept + // null here, so + // pass the name + // as the linkage + // name. + name.as_ptr(), + unknown_file_metadata(cx), + UNKNOWN_LINE_NUMBER, + vtable_type, + true, + vtable, + ptr::null_mut(), + 0); + } +} diff --git a/src/librustc_codegen_llvm/debuginfo/mod.rs b/src/librustc_codegen_llvm/debuginfo/mod.rs new file mode 100644 index 00000000000..294d8cbbd93 --- /dev/null +++ b/src/librustc_codegen_llvm/debuginfo/mod.rs @@ -0,0 +1,542 @@ +// Copyright 2012-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. + +// See doc.rs for documentation. +mod doc; + +use self::VariableAccess::*; +use self::VariableKind::*; + +use self::utils::{DIB, span_start, create_DIArray, is_node_local_to_unit}; +use self::namespace::mangled_name_of_instance; +use self::type_names::compute_debuginfo_type_name; +use self::metadata::{type_metadata, file_metadata, TypeMap}; +use self::source_loc::InternalDebugLocation::{self, UnknownLocation}; + +use llvm; +use llvm::{ModuleRef, ContextRef, ValueRef}; +use llvm::debuginfo::{DIFile, DIType, DIScope, DIBuilderRef, DISubprogram, DIArray, DIFlags}; +use rustc::hir::CodegenFnAttrFlags; +use rustc::hir::def_id::{DefId, CrateNum}; +use rustc::ty::subst::{Substs, UnpackedKind}; + +use abi::Abi; +use common::CodegenCx; +use builder::Builder; +use monomorphize::Instance; +use rustc::ty::{self, ParamEnv, Ty, InstanceDef}; +use rustc::mir; +use rustc::session::config::{self, FullDebugInfo, LimitedDebugInfo, NoDebugInfo}; +use rustc::util::nodemap::{DefIdMap, FxHashMap, FxHashSet}; + +use libc::c_uint; +use std::cell::{Cell, RefCell}; +use std::ffi::CString; +use std::ptr; + +use syntax_pos::{self, Span, Pos}; +use syntax::ast; +use syntax::symbol::{Symbol, InternedString}; +use rustc::ty::layout::{self, LayoutOf}; + +pub mod gdb; +mod utils; +mod namespace; +mod type_names; +pub mod metadata; +mod create_scope_map; +mod source_loc; + +pub use self::create_scope_map::{create_mir_scopes, MirDebugScope}; +pub use self::source_loc::start_emitting_source_locations; +pub use self::metadata::create_global_var_metadata; +pub use self::metadata::create_vtable_metadata; +pub use self::metadata::extend_scope_to_file; +pub use self::source_loc::set_source_location; + +#[allow(non_upper_case_globals)] +const DW_TAG_auto_variable: c_uint = 0x100; +#[allow(non_upper_case_globals)] +const DW_TAG_arg_variable: c_uint = 0x101; + +/// A context object for maintaining all state needed by the debuginfo module. +pub struct CrateDebugContext<'tcx> { + llcontext: ContextRef, + llmod: ModuleRef, + builder: DIBuilderRef, + created_files: RefCell>, + created_enum_disr_types: RefCell>, + + type_map: RefCell>, + namespace_map: RefCell>, + + // This collection is used to assert that composite types (structs, enums, + // ...) have their members only set once: + composite_types_completed: RefCell>, +} + +impl<'tcx> CrateDebugContext<'tcx> { + pub fn new(llmod: ModuleRef) -> CrateDebugContext<'tcx> { + debug!("CrateDebugContext::new"); + let builder = unsafe { llvm::LLVMRustDIBuilderCreate(llmod) }; + // DIBuilder inherits context from the module, so we'd better use the same one + let llcontext = unsafe { llvm::LLVMGetModuleContext(llmod) }; + CrateDebugContext { + llcontext, + llmod, + builder, + created_files: RefCell::new(FxHashMap()), + created_enum_disr_types: RefCell::new(FxHashMap()), + type_map: RefCell::new(TypeMap::new()), + namespace_map: RefCell::new(DefIdMap()), + composite_types_completed: RefCell::new(FxHashSet()), + } + } +} + +pub enum FunctionDebugContext { + RegularContext(FunctionDebugContextData), + DebugInfoDisabled, + FunctionWithoutDebugInfo, +} + +impl FunctionDebugContext { + pub fn get_ref<'a>(&'a self, span: Span) -> &'a FunctionDebugContextData { + match *self { + FunctionDebugContext::RegularContext(ref data) => data, + FunctionDebugContext::DebugInfoDisabled => { + span_bug!(span, "{}", FunctionDebugContext::debuginfo_disabled_message()); + } + FunctionDebugContext::FunctionWithoutDebugInfo => { + span_bug!(span, "{}", FunctionDebugContext::should_be_ignored_message()); + } + } + } + + fn debuginfo_disabled_message() -> &'static str { + "debuginfo: Error trying to access FunctionDebugContext although debug info is disabled!" + } + + fn should_be_ignored_message() -> &'static str { + "debuginfo: Error trying to access FunctionDebugContext for function that should be \ + ignored by debug info!" + } +} + +pub struct FunctionDebugContextData { + fn_metadata: DISubprogram, + source_locations_enabled: Cell, + pub defining_crate: CrateNum, +} + +pub enum VariableAccess<'a> { + // The llptr given is an alloca containing the variable's value + DirectVariable { alloca: ValueRef }, + // The llptr given is an alloca containing the start of some pointer chain + // leading to the variable's content. + IndirectVariable { alloca: ValueRef, address_operations: &'a [i64] } +} + +pub enum VariableKind { + ArgumentVariable(usize /*index*/), + LocalVariable, + CapturedVariable, +} + +/// Create any deferred debug metadata nodes +pub fn finalize(cx: &CodegenCx) { + if cx.dbg_cx.is_none() { + return; + } + + debug!("finalize"); + + if gdb::needs_gdb_debug_scripts_section(cx) { + // Add a .debug_gdb_scripts section to this compile-unit. This will + // cause GDB to try and load the gdb_load_rust_pretty_printers.py file, + // which activates the Rust pretty printers for binary this section is + // contained in. + gdb::get_or_insert_gdb_debug_scripts_section_global(cx); + } + + unsafe { + llvm::LLVMRustDIBuilderFinalize(DIB(cx)); + llvm::LLVMRustDIBuilderDispose(DIB(cx)); + // Debuginfo generation in LLVM by default uses a higher + // version of dwarf than macOS currently understands. We can + // instruct LLVM to emit an older version of dwarf, however, + // for macOS to understand. For more info see #11352 + // This can be overridden using --llvm-opts -dwarf-version,N. + // Android has the same issue (#22398) + if cx.sess().target.target.options.is_like_osx || + cx.sess().target.target.options.is_like_android { + llvm::LLVMRustAddModuleFlag(cx.llmod, + "Dwarf Version\0".as_ptr() as *const _, + 2) + } + + // Indicate that we want CodeView debug information on MSVC + if cx.sess().target.target.options.is_like_msvc { + llvm::LLVMRustAddModuleFlag(cx.llmod, + "CodeView\0".as_ptr() as *const _, + 1) + } + + // Prevent bitcode readers from deleting the debug info. + let ptr = "Debug Info Version\0".as_ptr(); + llvm::LLVMRustAddModuleFlag(cx.llmod, ptr as *const _, + llvm::LLVMRustDebugMetadataVersion()); + }; +} + +/// Creates the function-specific debug context. +/// +/// Returns the FunctionDebugContext for the function which holds state needed +/// for debug info creation. The function may also return another variant of the +/// FunctionDebugContext enum which indicates why no debuginfo should be created +/// for the function. +pub fn create_function_debug_context<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, + instance: Instance<'tcx>, + sig: ty::FnSig<'tcx>, + llfn: ValueRef, + mir: &mir::Mir) -> FunctionDebugContext { + if cx.sess().opts.debuginfo == NoDebugInfo { + return FunctionDebugContext::DebugInfoDisabled; + } + + if let InstanceDef::Item(def_id) = instance.def { + if cx.tcx.codegen_fn_attrs(def_id).flags.contains(CodegenFnAttrFlags::NO_DEBUG) { + return FunctionDebugContext::FunctionWithoutDebugInfo; + } + } + + let span = mir.span; + + // This can be the case for functions inlined from another crate + if span == syntax_pos::DUMMY_SP { + // FIXME(simulacrum): Probably can't happen; remove. + return FunctionDebugContext::FunctionWithoutDebugInfo; + } + + let def_id = instance.def_id(); + let containing_scope = get_containing_scope(cx, instance); + let loc = span_start(cx, span); + let file_metadata = file_metadata(cx, &loc.file.name, def_id.krate); + + let function_type_metadata = unsafe { + let fn_signature = get_function_signature(cx, sig); + llvm::LLVMRustDIBuilderCreateSubroutineType(DIB(cx), file_metadata, fn_signature) + }; + + // Find the enclosing function, in case this is a closure. + let def_key = cx.tcx.def_key(def_id); + let mut name = def_key.disambiguated_data.data.to_string(); + + let enclosing_fn_def_id = cx.tcx.closure_base_def_id(def_id); + + // Get_template_parameters() will append a `<...>` clause to the function + // name if necessary. + let generics = cx.tcx.generics_of(enclosing_fn_def_id); + let substs = instance.substs.truncate_to(cx.tcx, generics); + let template_parameters = get_template_parameters(cx, + &generics, + substs, + file_metadata, + &mut name); + + // Get the linkage_name, which is just the symbol name + let linkage_name = mangled_name_of_instance(cx, instance); + + let scope_line = span_start(cx, span).line; + let is_local_to_unit = is_node_local_to_unit(cx, def_id); + + let function_name = CString::new(name).unwrap(); + let linkage_name = CString::new(linkage_name.to_string()).unwrap(); + + let mut flags = DIFlags::FlagPrototyped; + + let local_id = cx.tcx.hir.as_local_node_id(def_id); + match *cx.sess().entry_fn.borrow() { + Some((id, _, _)) => { + if local_id == Some(id) { + flags = flags | DIFlags::FlagMainSubprogram; + } + } + None => {} + }; + if cx.layout_of(sig.output()).abi == ty::layout::Abi::Uninhabited { + flags = flags | DIFlags::FlagNoReturn; + } + + let fn_metadata = unsafe { + llvm::LLVMRustDIBuilderCreateFunction( + DIB(cx), + containing_scope, + function_name.as_ptr(), + linkage_name.as_ptr(), + file_metadata, + loc.line as c_uint, + function_type_metadata, + is_local_to_unit, + true, + scope_line as c_uint, + flags, + cx.sess().opts.optimize != config::OptLevel::No, + llfn, + template_parameters, + ptr::null_mut()) + }; + + // Initialize fn debug context (including scope map and namespace map) + let fn_debug_context = FunctionDebugContextData { + fn_metadata, + source_locations_enabled: Cell::new(false), + defining_crate: def_id.krate, + }; + + return FunctionDebugContext::RegularContext(fn_debug_context); + + fn get_function_signature<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, + sig: ty::FnSig<'tcx>) -> DIArray { + if cx.sess().opts.debuginfo == LimitedDebugInfo { + return create_DIArray(DIB(cx), &[]); + } + + let mut signature = Vec::with_capacity(sig.inputs().len() + 1); + + // Return type -- llvm::DIBuilder wants this at index 0 + signature.push(match sig.output().sty { + ty::TyTuple(ref tys) if tys.is_empty() => ptr::null_mut(), + _ => type_metadata(cx, sig.output(), syntax_pos::DUMMY_SP) + }); + + let inputs = if sig.abi == Abi::RustCall { + &sig.inputs()[..sig.inputs().len() - 1] + } else { + sig.inputs() + }; + + // Arguments types + if cx.sess().target.target.options.is_like_msvc { + // FIXME(#42800): + // There is a bug in MSDIA that leads to a crash when it encounters + // a fixed-size array of `u8` or something zero-sized in a + // function-type (see #40477). + // As a workaround, we replace those fixed-size arrays with a + // pointer-type. So a function `fn foo(a: u8, b: [u8; 4])` would + // appear as `fn foo(a: u8, b: *const u8)` in debuginfo, + // and a function `fn bar(x: [(); 7])` as `fn bar(x: *const ())`. + // This transformed type is wrong, but these function types are + // already inaccurate due to ABI adjustments (see #42800). + signature.extend(inputs.iter().map(|&t| { + let t = match t.sty { + ty::TyArray(ct, _) + if (ct == cx.tcx.types.u8) || cx.layout_of(ct).is_zst() => { + cx.tcx.mk_imm_ptr(ct) + } + _ => t + }; + type_metadata(cx, t, syntax_pos::DUMMY_SP) + })); + } else { + signature.extend(inputs.iter().map(|t| { + type_metadata(cx, t, syntax_pos::DUMMY_SP) + })); + } + + if sig.abi == Abi::RustCall && !sig.inputs().is_empty() { + if let ty::TyTuple(args) = sig.inputs()[sig.inputs().len() - 1].sty { + for &argument_type in args { + signature.push(type_metadata(cx, argument_type, syntax_pos::DUMMY_SP)); + } + } + } + + return create_DIArray(DIB(cx), &signature[..]); + } + + fn get_template_parameters<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, + generics: &ty::Generics, + substs: &Substs<'tcx>, + file_metadata: DIFile, + name_to_append_suffix_to: &mut String) + -> DIArray + { + if substs.types().next().is_none() { + return create_DIArray(DIB(cx), &[]); + } + + name_to_append_suffix_to.push('<'); + for (i, actual_type) in substs.types().enumerate() { + if i != 0 { + name_to_append_suffix_to.push_str(","); + } + + let actual_type = cx.tcx.normalize_erasing_regions(ParamEnv::reveal_all(), actual_type); + // Add actual type name to <...> clause of function name + let actual_type_name = compute_debuginfo_type_name(cx, + actual_type, + true); + name_to_append_suffix_to.push_str(&actual_type_name[..]); + } + name_to_append_suffix_to.push('>'); + + // Again, only create type information if full debuginfo is enabled + let template_params: Vec<_> = if cx.sess().opts.debuginfo == FullDebugInfo { + let names = get_parameter_names(cx, generics); + substs.iter().zip(names).filter_map(|(kind, name)| { + if let UnpackedKind::Type(ty) = kind.unpack() { + let actual_type = cx.tcx.normalize_erasing_regions(ParamEnv::reveal_all(), ty); + let actual_type_metadata = + type_metadata(cx, actual_type, syntax_pos::DUMMY_SP); + let name = CString::new(name.as_str().as_bytes()).unwrap(); + Some(unsafe { + llvm::LLVMRustDIBuilderCreateTemplateTypeParameter( + DIB(cx), + ptr::null_mut(), + name.as_ptr(), + actual_type_metadata, + file_metadata, + 0, + 0) + }) + } else { + None + } + }).collect() + } else { + vec![] + }; + + return create_DIArray(DIB(cx), &template_params[..]); + } + + fn get_parameter_names(cx: &CodegenCx, + generics: &ty::Generics) + -> Vec { + let mut names = generics.parent.map_or(vec![], |def_id| { + get_parameter_names(cx, cx.tcx.generics_of(def_id)) + }); + names.extend(generics.params.iter().map(|param| param.name)); + names + } + + fn get_containing_scope<'cx, 'tcx>(cx: &CodegenCx<'cx, 'tcx>, + instance: Instance<'tcx>) + -> DIScope { + // First, let's see if this is a method within an inherent impl. Because + // if yes, we want to make the result subroutine DIE a child of the + // subroutine's self-type. + let self_type = cx.tcx.impl_of_method(instance.def_id()).and_then(|impl_def_id| { + // If the method does *not* belong to a trait, proceed + if cx.tcx.trait_id_of_impl(impl_def_id).is_none() { + let impl_self_ty = cx.tcx.subst_and_normalize_erasing_regions( + instance.substs, + ty::ParamEnv::reveal_all(), + &cx.tcx.type_of(impl_def_id), + ); + + // Only "class" methods are generally understood by LLVM, + // so avoid methods on other types (e.g. `<*mut T>::null`). + match impl_self_ty.sty { + ty::TyAdt(def, ..) if !def.is_box() => { + Some(type_metadata(cx, impl_self_ty, syntax_pos::DUMMY_SP)) + } + _ => None + } + } else { + // For trait method impls we still use the "parallel namespace" + // strategy + None + } + }); + + self_type.unwrap_or_else(|| { + namespace::item_namespace(cx, DefId { + krate: instance.def_id().krate, + index: cx.tcx + .def_key(instance.def_id()) + .parent + .expect("get_containing_scope: missing parent?") + }) + }) + } +} + +pub fn declare_local<'a, 'tcx>(bx: &Builder<'a, 'tcx>, + dbg_context: &FunctionDebugContext, + variable_name: ast::Name, + variable_type: Ty<'tcx>, + scope_metadata: DIScope, + variable_access: VariableAccess, + variable_kind: VariableKind, + span: Span) { + let cx = bx.cx; + + let file = span_start(cx, span).file; + let file_metadata = file_metadata(cx, + &file.name, + dbg_context.get_ref(span).defining_crate); + + let loc = span_start(cx, span); + let type_metadata = type_metadata(cx, variable_type, span); + + let (argument_index, dwarf_tag) = match variable_kind { + ArgumentVariable(index) => (index as c_uint, DW_TAG_arg_variable), + LocalVariable | + CapturedVariable => (0, DW_TAG_auto_variable) + }; + let align = cx.align_of(variable_type); + + let name = CString::new(variable_name.as_str().as_bytes()).unwrap(); + match (variable_access, &[][..]) { + (DirectVariable { alloca }, address_operations) | + (IndirectVariable {alloca, address_operations}, _) => { + let metadata = unsafe { + llvm::LLVMRustDIBuilderCreateVariable( + DIB(cx), + dwarf_tag, + scope_metadata, + name.as_ptr(), + file_metadata, + loc.line as c_uint, + type_metadata, + cx.sess().opts.optimize != config::OptLevel::No, + DIFlags::FlagZero, + argument_index, + align.abi() as u32, + ) + }; + source_loc::set_debug_location(bx, + InternalDebugLocation::new(scope_metadata, loc.line, loc.col.to_usize())); + unsafe { + let debug_loc = llvm::LLVMGetCurrentDebugLocation(bx.llbuilder); + let instr = llvm::LLVMRustDIBuilderInsertDeclareAtEnd( + DIB(cx), + alloca, + metadata, + address_operations.as_ptr(), + address_operations.len() as c_uint, + debug_loc, + bx.llbb()); + + llvm::LLVMSetInstDebugLocation(bx.llbuilder, instr); + } + } + } + + match variable_kind { + ArgumentVariable(_) | CapturedVariable => { + assert!(!dbg_context.get_ref(span).source_locations_enabled.get()); + source_loc::set_debug_location(bx, UnknownLocation); + } + _ => { /* nothing to do */ } + } +} diff --git a/src/librustc_codegen_llvm/debuginfo/namespace.rs b/src/librustc_codegen_llvm/debuginfo/namespace.rs new file mode 100644 index 00000000000..51c45de9dc2 --- /dev/null +++ b/src/librustc_codegen_llvm/debuginfo/namespace.rs @@ -0,0 +1,66 @@ +// 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. + +// Namespace Handling. + +use super::metadata::{unknown_file_metadata, UNKNOWN_LINE_NUMBER}; +use super::utils::{DIB, debug_context}; +use monomorphize::Instance; +use rustc::ty; + +use llvm; +use llvm::debuginfo::DIScope; +use rustc::hir::def_id::DefId; +use rustc::hir::map::DefPathData; +use common::CodegenCx; + +use std::ffi::CString; +use std::ptr; + +pub fn mangled_name_of_instance<'a, 'tcx>( + cx: &CodegenCx<'a, 'tcx>, + instance: Instance<'tcx>, +) -> ty::SymbolName { + let tcx = cx.tcx; + tcx.symbol_name(instance) +} + +pub fn item_namespace(cx: &CodegenCx, def_id: DefId) -> DIScope { + if let Some(&scope) = debug_context(cx).namespace_map.borrow().get(&def_id) { + return scope; + } + + let def_key = cx.tcx.def_key(def_id); + let parent_scope = def_key.parent.map_or(ptr::null_mut(), |parent| { + item_namespace(cx, DefId { + krate: def_id.krate, + index: parent + }) + }); + + let namespace_name = match def_key.disambiguated_data.data { + DefPathData::CrateRoot => cx.tcx.crate_name(def_id.krate).as_str(), + data => data.as_interned_str().as_str() + }; + + let namespace_name = CString::new(namespace_name.as_bytes()).unwrap(); + + let scope = unsafe { + llvm::LLVMRustDIBuilderCreateNameSpace( + DIB(cx), + parent_scope, + namespace_name.as_ptr(), + unknown_file_metadata(cx), + UNKNOWN_LINE_NUMBER) + }; + + debug_context(cx).namespace_map.borrow_mut().insert(def_id, scope); + scope +} diff --git a/src/librustc_codegen_llvm/debuginfo/source_loc.rs b/src/librustc_codegen_llvm/debuginfo/source_loc.rs new file mode 100644 index 00000000000..eb37e7f931c --- /dev/null +++ b/src/librustc_codegen_llvm/debuginfo/source_loc.rs @@ -0,0 +1,107 @@ +// 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. + +use self::InternalDebugLocation::*; + +use super::utils::{debug_context, span_start}; +use super::metadata::UNKNOWN_COLUMN_NUMBER; +use super::FunctionDebugContext; + +use llvm; +use llvm::debuginfo::DIScope; +use builder::Builder; + +use libc::c_uint; +use std::ptr; +use syntax_pos::{Span, Pos}; + +/// Sets the current debug location at the beginning of the span. +/// +/// Maps to a call to llvm::LLVMSetCurrentDebugLocation(...). +pub fn set_source_location( + debug_context: &FunctionDebugContext, bx: &Builder, scope: DIScope, span: Span +) { + let function_debug_context = match *debug_context { + FunctionDebugContext::DebugInfoDisabled => return, + FunctionDebugContext::FunctionWithoutDebugInfo => { + set_debug_location(bx, UnknownLocation); + return; + } + FunctionDebugContext::RegularContext(ref data) => data + }; + + let dbg_loc = if function_debug_context.source_locations_enabled.get() { + debug!("set_source_location: {}", bx.sess().codemap().span_to_string(span)); + let loc = span_start(bx.cx, span); + InternalDebugLocation::new(scope, loc.line, loc.col.to_usize()) + } else { + UnknownLocation + }; + set_debug_location(bx, dbg_loc); +} + +/// Enables emitting source locations for the given functions. +/// +/// Since we don't want source locations to be emitted for the function prelude, +/// they are disabled when beginning to codegen a new function. This functions +/// switches source location emitting on and must therefore be called before the +/// first real statement/expression of the function is codegened. +pub fn start_emitting_source_locations(dbg_context: &FunctionDebugContext) { + match *dbg_context { + FunctionDebugContext::RegularContext(ref data) => { + data.source_locations_enabled.set(true) + }, + _ => { /* safe to ignore */ } + } +} + + +#[derive(Copy, Clone, PartialEq)] +pub enum InternalDebugLocation { + KnownLocation { scope: DIScope, line: usize, col: usize }, + UnknownLocation +} + +impl InternalDebugLocation { + pub fn new(scope: DIScope, line: usize, col: usize) -> InternalDebugLocation { + KnownLocation { + scope, + line, + col, + } + } +} + +pub fn set_debug_location(bx: &Builder, debug_location: InternalDebugLocation) { + let metadata_node = match debug_location { + KnownLocation { scope, line, .. } => { + // Always set the column to zero like Clang and GCC + let col = UNKNOWN_COLUMN_NUMBER; + debug!("setting debug location to {} {}", line, col); + + unsafe { + llvm::LLVMRustDIBuilderCreateDebugLocation( + debug_context(bx.cx).llcontext, + line as c_uint, + col as c_uint, + scope, + ptr::null_mut()) + } + } + UnknownLocation => { + debug!("clearing debug location "); + ptr::null_mut() + } + }; + + unsafe { + llvm::LLVMSetCurrentDebugLocation(bx.llbuilder, metadata_node); + } +} diff --git a/src/librustc_codegen_llvm/debuginfo/type_names.rs b/src/librustc_codegen_llvm/debuginfo/type_names.rs new file mode 100644 index 00000000000..05a74db3a6c --- /dev/null +++ b/src/librustc_codegen_llvm/debuginfo/type_names.rs @@ -0,0 +1,224 @@ +// 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. + +// Type Names for Debug Info. + +use common::CodegenCx; +use rustc::hir::def_id::DefId; +use rustc::ty::subst::Substs; +use rustc::ty::{self, Ty}; + +use rustc::hir; + +// Compute the name of the type as it should be stored in debuginfo. Does not do +// any caching, i.e. calling the function twice with the same type will also do +// the work twice. The `qualified` parameter only affects the first level of the +// type name, further levels (i.e. type parameters) are always fully qualified. +pub fn compute_debuginfo_type_name<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, + t: Ty<'tcx>, + qualified: bool) + -> String { + let mut result = String::with_capacity(64); + push_debuginfo_type_name(cx, t, qualified, &mut result); + result +} + +// Pushes the name of the type as it should be stored in debuginfo on the +// `output` String. See also compute_debuginfo_type_name(). +pub fn push_debuginfo_type_name<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, + t: Ty<'tcx>, + qualified: bool, + output: &mut String) { + // When targeting MSVC, emit C++ style type names for compatibility with + // .natvis visualizers (and perhaps other existing native debuggers?) + let cpp_like_names = cx.sess().target.target.options.is_like_msvc; + + match t.sty { + ty::TyBool => output.push_str("bool"), + ty::TyChar => output.push_str("char"), + ty::TyStr => output.push_str("str"), + ty::TyNever => output.push_str("!"), + ty::TyInt(int_ty) => output.push_str(int_ty.ty_to_string()), + ty::TyUint(uint_ty) => output.push_str(uint_ty.ty_to_string()), + ty::TyFloat(float_ty) => output.push_str(float_ty.ty_to_string()), + ty::TyForeign(def_id) => push_item_name(cx, def_id, qualified, output), + ty::TyAdt(def, substs) => { + push_item_name(cx, def.did, qualified, output); + push_type_params(cx, substs, output); + }, + ty::TyTuple(component_types) => { + output.push('('); + for &component_type in component_types { + push_debuginfo_type_name(cx, component_type, true, output); + output.push_str(", "); + } + if !component_types.is_empty() { + output.pop(); + output.pop(); + } + output.push(')'); + }, + ty::TyRawPtr(ty::TypeAndMut { ty: inner_type, mutbl } ) => { + if !cpp_like_names { + output.push('*'); + } + match mutbl { + hir::MutImmutable => output.push_str("const "), + hir::MutMutable => output.push_str("mut "), + } + + push_debuginfo_type_name(cx, inner_type, true, output); + + if cpp_like_names { + output.push('*'); + } + }, + ty::TyRef(_, inner_type, mutbl) => { + if !cpp_like_names { + output.push('&'); + } + if mutbl == hir::MutMutable { + output.push_str("mut "); + } + + push_debuginfo_type_name(cx, inner_type, true, output); + + if cpp_like_names { + output.push('*'); + } + }, + ty::TyArray(inner_type, len) => { + output.push('['); + push_debuginfo_type_name(cx, inner_type, true, output); + output.push_str(&format!("; {}", len.unwrap_usize(cx.tcx))); + output.push(']'); + }, + ty::TySlice(inner_type) => { + if cpp_like_names { + output.push_str("slice<"); + } else { + output.push('['); + } + + push_debuginfo_type_name(cx, inner_type, true, output); + + if cpp_like_names { + output.push('>'); + } else { + output.push(']'); + } + }, + ty::TyDynamic(ref trait_data, ..) => { + if let Some(principal) = trait_data.principal() { + let principal = cx.tcx.normalize_erasing_late_bound_regions( + ty::ParamEnv::reveal_all(), + &principal, + ); + push_item_name(cx, principal.def_id, false, output); + push_type_params(cx, principal.substs, output); + } + }, + ty::TyFnDef(..) | ty::TyFnPtr(_) => { + let sig = t.fn_sig(cx.tcx); + if sig.unsafety() == hir::Unsafety::Unsafe { + output.push_str("unsafe "); + } + + let abi = sig.abi(); + if abi != ::abi::Abi::Rust { + output.push_str("extern \""); + output.push_str(abi.name()); + output.push_str("\" "); + } + + output.push_str("fn("); + + let sig = cx.tcx.normalize_erasing_late_bound_regions(ty::ParamEnv::reveal_all(), &sig); + if !sig.inputs().is_empty() { + for ¶meter_type in sig.inputs() { + push_debuginfo_type_name(cx, parameter_type, true, output); + output.push_str(", "); + } + output.pop(); + output.pop(); + } + + if sig.variadic { + if !sig.inputs().is_empty() { + output.push_str(", ..."); + } else { + output.push_str("..."); + } + } + + output.push(')'); + + if !sig.output().is_nil() { + output.push_str(" -> "); + push_debuginfo_type_name(cx, sig.output(), true, output); + } + }, + ty::TyClosure(..) => { + output.push_str("closure"); + } + ty::TyGenerator(..) => { + output.push_str("generator"); + } + ty::TyError | + ty::TyInfer(_) | + ty::TyProjection(..) | + ty::TyAnon(..) | + ty::TyGeneratorWitness(..) | + ty::TyParam(_) => { + bug!("debuginfo: Trying to create type name for \ + unexpected type: {:?}", t); + } + } + + fn push_item_name(cx: &CodegenCx, + def_id: DefId, + qualified: bool, + output: &mut String) { + if qualified { + output.push_str(&cx.tcx.crate_name(def_id.krate).as_str()); + for path_element in cx.tcx.def_path(def_id).data { + output.push_str("::"); + output.push_str(&path_element.data.as_interned_str().as_str()); + } + } else { + output.push_str(&cx.tcx.item_name(def_id).as_str()); + } + } + + // Pushes the type parameters in the given `Substs` to the output string. + // This ignores region parameters, since they can't reliably be + // reconstructed for items from non-local crates. For local crates, this + // would be possible but with inlining and LTO we have to use the least + // common denominator - otherwise we would run into conflicts. + fn push_type_params<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, + substs: &Substs<'tcx>, + output: &mut String) { + if substs.types().next().is_none() { + return; + } + + output.push('<'); + + for type_parameter in substs.types() { + push_debuginfo_type_name(cx, type_parameter, true, output); + output.push_str(", "); + } + + output.pop(); + output.pop(); + + output.push('>'); + } +} diff --git a/src/librustc_codegen_llvm/debuginfo/utils.rs b/src/librustc_codegen_llvm/debuginfo/utils.rs new file mode 100644 index 00000000000..9d37f99cb2a --- /dev/null +++ b/src/librustc_codegen_llvm/debuginfo/utils.rs @@ -0,0 +1,65 @@ +// 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. + +// Utility Functions. + +use super::{CrateDebugContext}; +use super::namespace::item_namespace; + +use rustc::hir::def_id::DefId; +use rustc::ty::DefIdTree; + +use llvm; +use llvm::debuginfo::{DIScope, DIBuilderRef, DIDescriptor, DIArray}; +use common::{CodegenCx}; + +use syntax_pos::{self, Span}; + +pub fn is_node_local_to_unit(cx: &CodegenCx, def_id: DefId) -> bool +{ + // The is_local_to_unit flag indicates whether a function is local to the + // current compilation unit (i.e. if it is *static* in the C-sense). The + // *reachable* set should provide a good approximation of this, as it + // contains everything that might leak out of the current crate (by being + // externally visible or by being inlined into something externally + // visible). It might better to use the `exported_items` set from + // `driver::CrateAnalysis` in the future, but (atm) this set is not + // available in the codegen pass. + !cx.tcx.is_reachable_non_generic(def_id) +} + +#[allow(non_snake_case)] +pub fn create_DIArray(builder: DIBuilderRef, arr: &[DIDescriptor]) -> DIArray { + return unsafe { + llvm::LLVMRustDIBuilderGetOrCreateArray(builder, arr.as_ptr(), arr.len() as u32) + }; +} + +/// Return syntax_pos::Loc corresponding to the beginning of the span +pub fn span_start(cx: &CodegenCx, span: Span) -> syntax_pos::Loc { + cx.sess().codemap().lookup_char_pos(span.lo()) +} + +#[inline] +pub fn debug_context<'a, 'tcx>(cx: &'a CodegenCx<'a, 'tcx>) + -> &'a CrateDebugContext<'tcx> { + cx.dbg_cx.as_ref().unwrap() +} + +#[inline] +#[allow(non_snake_case)] +pub fn DIB(cx: &CodegenCx) -> DIBuilderRef { + cx.dbg_cx.as_ref().unwrap().builder +} + +pub fn get_namespace_for_item(cx: &CodegenCx, def_id: DefId) -> DIScope { + item_namespace(cx, cx.tcx.parent(def_id) + .expect("get_namespace_for_item: missing parent?")) +} diff --git a/src/librustc_codegen_llvm/declare.rs b/src/librustc_codegen_llvm/declare.rs new file mode 100644 index 00000000000..fdc84f914f5 --- /dev/null +++ b/src/librustc_codegen_llvm/declare.rs @@ -0,0 +1,223 @@ +// Copyright 2012-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. +//! Declare various LLVM values. +//! +//! Prefer using functions and methods from this module rather than calling LLVM +//! functions directly. These functions do some additional work to ensure we do +//! the right thing given the preconceptions of codegen. +//! +//! Some useful guidelines: +//! +//! * Use declare_* family of methods if you are declaring, but are not +//! interested in defining the ValueRef they return. +//! * Use define_* family of methods when you might be defining the ValueRef. +//! * When in doubt, define. + +use llvm::{self, ValueRef}; +use llvm::AttributePlace::Function; +use rustc::ty::{self, Ty}; +use rustc::ty::layout::{self, LayoutOf}; +use rustc::session::config::Sanitizer; +use rustc_target::spec::PanicStrategy; +use abi::{Abi, FnType, FnTypeExt}; +use attributes; +use context::CodegenCx; +use common; +use type_::Type; +use value::Value; + +use std::ffi::CString; + + +/// Declare a global value. +/// +/// If there’s a value with the same name already declared, the function will +/// return its ValueRef instead. +pub fn declare_global(cx: &CodegenCx, name: &str, ty: Type) -> llvm::ValueRef { + debug!("declare_global(name={:?})", name); + let namebuf = CString::new(name).unwrap_or_else(|_|{ + bug!("name {:?} contains an interior null byte", name) + }); + unsafe { + llvm::LLVMRustGetOrInsertGlobal(cx.llmod, namebuf.as_ptr(), ty.to_ref()) + } +} + + +/// Declare a function. +/// +/// If there’s a value with the same name already declared, the function will +/// update the declaration and return existing ValueRef instead. +fn declare_raw_fn(cx: &CodegenCx, name: &str, callconv: llvm::CallConv, ty: Type) -> ValueRef { + debug!("declare_raw_fn(name={:?}, ty={:?})", name, ty); + let namebuf = CString::new(name).unwrap_or_else(|_|{ + bug!("name {:?} contains an interior null byte", name) + }); + let llfn = unsafe { + llvm::LLVMRustGetOrInsertFunction(cx.llmod, namebuf.as_ptr(), ty.to_ref()) + }; + + llvm::SetFunctionCallConv(llfn, callconv); + // Function addresses in Rust are never significant, allowing functions to + // be merged. + llvm::SetUnnamedAddr(llfn, true); + + if cx.tcx.sess.opts.cg.no_redzone + .unwrap_or(cx.tcx.sess.target.target.options.disable_redzone) { + llvm::Attribute::NoRedZone.apply_llfn(Function, llfn); + } + + if let Some(ref sanitizer) = cx.tcx.sess.opts.debugging_opts.sanitizer { + match *sanitizer { + Sanitizer::Address => { + llvm::Attribute::SanitizeAddress.apply_llfn(Function, llfn); + }, + Sanitizer::Memory => { + llvm::Attribute::SanitizeMemory.apply_llfn(Function, llfn); + }, + Sanitizer::Thread => { + llvm::Attribute::SanitizeThread.apply_llfn(Function, llfn); + }, + _ => {} + } + } + + match cx.tcx.sess.opts.cg.opt_level.as_ref().map(String::as_ref) { + Some("s") => { + llvm::Attribute::OptimizeForSize.apply_llfn(Function, llfn); + }, + Some("z") => { + llvm::Attribute::MinSize.apply_llfn(Function, llfn); + llvm::Attribute::OptimizeForSize.apply_llfn(Function, llfn); + }, + _ => {}, + } + + if cx.tcx.sess.panic_strategy() != PanicStrategy::Unwind { + attributes::unwind(llfn, false); + } + + llfn +} + + +/// Declare a C ABI function. +/// +/// Only use this for foreign function ABIs and glue. For Rust functions use +/// `declare_fn` instead. +/// +/// If there’s a value with the same name already declared, the function will +/// update the declaration and return existing ValueRef instead. +pub fn declare_cfn(cx: &CodegenCx, name: &str, fn_type: Type) -> ValueRef { + declare_raw_fn(cx, name, llvm::CCallConv, fn_type) +} + + +/// Declare a Rust function. +/// +/// If there’s a value with the same name already declared, the function will +/// update the declaration and return existing ValueRef instead. +pub fn declare_fn<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, name: &str, + fn_type: Ty<'tcx>) -> ValueRef { + debug!("declare_rust_fn(name={:?}, fn_type={:?})", name, fn_type); + let sig = common::ty_fn_sig(cx, fn_type); + let sig = cx.tcx.normalize_erasing_late_bound_regions(ty::ParamEnv::reveal_all(), &sig); + debug!("declare_rust_fn (after region erasure) sig={:?}", sig); + + let fty = FnType::new(cx, sig, &[]); + let llfn = declare_raw_fn(cx, name, fty.llvm_cconv(), fty.llvm_type(cx)); + + if cx.layout_of(sig.output()).abi == layout::Abi::Uninhabited { + llvm::Attribute::NoReturn.apply_llfn(Function, llfn); + } + + if sig.abi != Abi::Rust && sig.abi != Abi::RustCall { + attributes::unwind(llfn, false); + } + + fty.apply_attrs_llfn(llfn); + + llfn +} + + +/// Declare a global with an intention to define it. +/// +/// Use this function when you intend to define a global. This function will +/// return None if the name already has a definition associated with it. In that +/// case an error should be reported to the user, because it usually happens due +/// to user’s fault (e.g. misuse of #[no_mangle] or #[export_name] attributes). +pub fn define_global(cx: &CodegenCx, name: &str, ty: Type) -> Option { + if get_defined_value(cx, name).is_some() { + None + } else { + Some(declare_global(cx, name, ty)) + } +} + +/// Declare a Rust function with an intention to define it. +/// +/// Use this function when you intend to define a function. This function will +/// return panic if the name already has a definition associated with it. This +/// can happen with #[no_mangle] or #[export_name], for example. +pub fn define_fn<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, + name: &str, + fn_type: Ty<'tcx>) -> ValueRef { + if get_defined_value(cx, name).is_some() { + cx.sess().fatal(&format!("symbol `{}` already defined", name)) + } else { + declare_fn(cx, name, fn_type) + } +} + +/// Declare a Rust function with an intention to define it. +/// +/// Use this function when you intend to define a function. This function will +/// return panic if the name already has a definition associated with it. This +/// can happen with #[no_mangle] or #[export_name], for example. +pub fn define_internal_fn<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, + name: &str, + fn_type: Ty<'tcx>) -> ValueRef { + let llfn = define_fn(cx, name, fn_type); + unsafe { llvm::LLVMRustSetLinkage(llfn, llvm::Linkage::InternalLinkage) }; + llfn +} + + +/// Get declared value by name. +pub fn get_declared_value(cx: &CodegenCx, name: &str) -> Option { + debug!("get_declared_value(name={:?})", name); + let namebuf = CString::new(name).unwrap_or_else(|_|{ + bug!("name {:?} contains an interior null byte", name) + }); + let val = unsafe { llvm::LLVMRustGetNamedValue(cx.llmod, namebuf.as_ptr()) }; + if val.is_null() { + debug!("get_declared_value: {:?} value is null", name); + None + } else { + debug!("get_declared_value: {:?} => {:?}", name, Value(val)); + Some(val) + } +} + +/// Get defined or externally defined (AvailableExternally linkage) value by +/// name. +pub fn get_defined_value(cx: &CodegenCx, name: &str) -> Option { + get_declared_value(cx, name).and_then(|val|{ + let declaration = unsafe { + llvm::LLVMIsDeclaration(val) != 0 + }; + if !declaration { + Some(val) + } else { + None + } + }) +} diff --git a/src/librustc_codegen_llvm/diagnostics.rs b/src/librustc_codegen_llvm/diagnostics.rs new file mode 100644 index 00000000000..57cc33d09bb --- /dev/null +++ b/src/librustc_codegen_llvm/diagnostics.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. + +#![allow(non_snake_case)] + +register_long_diagnostics! { + +E0511: r##" +Invalid monomorphization of an intrinsic function was used. Erroneous code +example: + +```ignore (error-emitted-at-codegen-which-cannot-be-handled-by-compile_fail) +#![feature(platform_intrinsics)] + +extern "platform-intrinsic" { + fn simd_add(a: T, b: T) -> T; +} + +fn main() { + unsafe { simd_add(0, 1); } + // error: invalid monomorphization of `simd_add` intrinsic +} +``` + +The generic type has to be a SIMD type. Example: + +``` +#![feature(repr_simd)] +#![feature(platform_intrinsics)] + +#[repr(simd)] +#[derive(Copy, Clone)] +struct i32x2(i32, i32); + +extern "platform-intrinsic" { + fn simd_add(a: T, b: T) -> T; +} + +unsafe { simd_add(i32x2(0, 0), i32x2(1, 2)); } // ok! +``` +"##, + +} + + +register_diagnostics! { + E0558 +} diff --git a/src/librustc_codegen_llvm/glue.rs b/src/librustc_codegen_llvm/glue.rs new file mode 100644 index 00000000000..c7275d09401 --- /dev/null +++ b/src/librustc_codegen_llvm/glue.rs @@ -0,0 +1,122 @@ +// Copyright 2012-2013 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. + +//! +// +// Code relating to drop glue. + +use std; + +use builder::Builder; +use common::*; +use llvm::{ValueRef}; +use llvm; +use meth; +use rustc::ty::layout::LayoutOf; +use rustc::ty::{self, Ty}; +use value::Value; + +pub fn size_and_align_of_dst<'a, 'tcx>(bx: &Builder<'a, 'tcx>, t: Ty<'tcx>, info: ValueRef) + -> (ValueRef, ValueRef) { + debug!("calculate size of DST: {}; with lost info: {:?}", + t, Value(info)); + if bx.cx.type_is_sized(t) { + let (size, align) = bx.cx.size_and_align_of(t); + debug!("size_and_align_of_dst t={} info={:?} size: {:?} align: {:?}", + t, Value(info), size, align); + let size = C_usize(bx.cx, size.bytes()); + let align = C_usize(bx.cx, align.abi()); + return (size, align); + } + assert!(!info.is_null()); + match t.sty { + ty::TyDynamic(..) => { + // load size/align from vtable + (meth::SIZE.get_usize(bx, info), meth::ALIGN.get_usize(bx, info)) + } + ty::TySlice(_) | ty::TyStr => { + let unit = t.sequence_element_type(bx.tcx()); + // The info in this case is the length of the str, so the size is that + // times the unit size. + let (size, align) = bx.cx.size_and_align_of(unit); + (bx.mul(info, C_usize(bx.cx, size.bytes())), + C_usize(bx.cx, align.abi())) + } + _ => { + let cx = bx.cx; + // First get the size of all statically known fields. + // Don't use size_of because it also rounds up to alignment, which we + // want to avoid, as the unsized field's alignment could be smaller. + assert!(!t.is_simd()); + let layout = cx.layout_of(t); + debug!("DST {} layout: {:?}", t, layout); + + let i = layout.fields.count() - 1; + let sized_size = layout.fields.offset(i).bytes(); + let sized_align = layout.align.abi(); + debug!("DST {} statically sized prefix size: {} align: {}", + t, sized_size, sized_align); + let sized_size = C_usize(cx, sized_size); + let sized_align = C_usize(cx, sized_align); + + // Recurse to get the size of the dynamically sized field (must be + // the last field). + let field_ty = layout.field(cx, i).ty; + let (unsized_size, mut unsized_align) = size_and_align_of_dst(bx, field_ty, info); + + // FIXME (#26403, #27023): We should be adding padding + // to `sized_size` (to accommodate the `unsized_align` + // required of the unsized field that follows) before + // summing it with `sized_size`. (Note that since #26403 + // is unfixed, we do not yet add the necessary padding + // here. But this is where the add would go.) + + // Return the sum of sizes and max of aligns. + let size = bx.add(sized_size, unsized_size); + + // Packed types ignore the alignment of their fields. + if let ty::TyAdt(def, _) = t.sty { + if def.repr.packed() { + unsized_align = sized_align; + } + } + + // Choose max of two known alignments (combined value must + // be aligned according to more restrictive of the two). + let align = match (const_to_opt_u128(sized_align, false), + const_to_opt_u128(unsized_align, false)) { + (Some(sized_align), Some(unsized_align)) => { + // If both alignments are constant, (the sized_align should always be), then + // pick the correct alignment statically. + C_usize(cx, std::cmp::max(sized_align, unsized_align) as u64) + } + _ => bx.select(bx.icmp(llvm::IntUGT, sized_align, unsized_align), + sized_align, + unsized_align) + }; + + // Issue #27023: must add any necessary padding to `size` + // (to make it a multiple of `align`) before returning it. + // + // Namely, the returned size should be, in C notation: + // + // `size + ((size & (align-1)) ? align : 0)` + // + // emulated via the semi-standard fast bit trick: + // + // `(size + (align-1)) & -align` + + let addend = bx.sub(align, C_usize(bx.cx, 1)); + let size = bx.and(bx.add(size, addend), bx.neg(align)); + + (size, align) + } + } +} diff --git a/src/librustc_codegen_llvm/intrinsic.rs b/src/librustc_codegen_llvm/intrinsic.rs new file mode 100644 index 00000000000..ba04cc7fad5 --- /dev/null +++ b/src/librustc_codegen_llvm/intrinsic.rs @@ -0,0 +1,1465 @@ +// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![allow(non_upper_case_globals)] + +use intrinsics::{self, Intrinsic}; +use llvm; +use llvm::{ValueRef}; +use abi::{Abi, FnType, LlvmType, PassMode}; +use mir::place::PlaceRef; +use mir::operand::{OperandRef, OperandValue}; +use base::*; +use common::*; +use declare; +use glue; +use type_::Type; +use type_of::LayoutLlvmExt; +use rustc::ty::{self, Ty}; +use rustc::ty::layout::{HasDataLayout, LayoutOf}; +use rustc::hir; +use syntax::ast; +use syntax::symbol::Symbol; +use builder::Builder; + +use rustc::session::Session; +use syntax_pos::Span; + +use std::cmp::Ordering; +use std::iter; + +fn get_simple_intrinsic(cx: &CodegenCx, name: &str) -> Option { + let llvm_name = match name { + "sqrtf32" => "llvm.sqrt.f32", + "sqrtf64" => "llvm.sqrt.f64", + "powif32" => "llvm.powi.f32", + "powif64" => "llvm.powi.f64", + "sinf32" => "llvm.sin.f32", + "sinf64" => "llvm.sin.f64", + "cosf32" => "llvm.cos.f32", + "cosf64" => "llvm.cos.f64", + "powf32" => "llvm.pow.f32", + "powf64" => "llvm.pow.f64", + "expf32" => "llvm.exp.f32", + "expf64" => "llvm.exp.f64", + "exp2f32" => "llvm.exp2.f32", + "exp2f64" => "llvm.exp2.f64", + "logf32" => "llvm.log.f32", + "logf64" => "llvm.log.f64", + "log10f32" => "llvm.log10.f32", + "log10f64" => "llvm.log10.f64", + "log2f32" => "llvm.log2.f32", + "log2f64" => "llvm.log2.f64", + "fmaf32" => "llvm.fma.f32", + "fmaf64" => "llvm.fma.f64", + "fabsf32" => "llvm.fabs.f32", + "fabsf64" => "llvm.fabs.f64", + "copysignf32" => "llvm.copysign.f32", + "copysignf64" => "llvm.copysign.f64", + "floorf32" => "llvm.floor.f32", + "floorf64" => "llvm.floor.f64", + "ceilf32" => "llvm.ceil.f32", + "ceilf64" => "llvm.ceil.f64", + "truncf32" => "llvm.trunc.f32", + "truncf64" => "llvm.trunc.f64", + "rintf32" => "llvm.rint.f32", + "rintf64" => "llvm.rint.f64", + "nearbyintf32" => "llvm.nearbyint.f32", + "nearbyintf64" => "llvm.nearbyint.f64", + "roundf32" => "llvm.round.f32", + "roundf64" => "llvm.round.f64", + "assume" => "llvm.assume", + "abort" => "llvm.trap", + _ => return None + }; + Some(cx.get_intrinsic(&llvm_name)) +} + +/// Remember to add all intrinsics here, in librustc_typeck/check/mod.rs, +/// and in libcore/intrinsics.rs; if you need access to any llvm intrinsics, +/// add them to librustc_codegen_llvm/context.rs +pub fn codegen_intrinsic_call<'a, 'tcx>(bx: &Builder<'a, 'tcx>, + callee_ty: Ty<'tcx>, + fn_ty: &FnType<'tcx, Ty<'tcx>>, + args: &[OperandRef<'tcx>], + llresult: ValueRef, + span: Span) { + let cx = bx.cx; + let tcx = cx.tcx; + + let (def_id, substs) = match callee_ty.sty { + ty::TyFnDef(def_id, substs) => (def_id, substs), + _ => bug!("expected fn item type, found {}", callee_ty) + }; + + let sig = callee_ty.fn_sig(tcx); + let sig = tcx.normalize_erasing_late_bound_regions(ty::ParamEnv::reveal_all(), &sig); + let arg_tys = sig.inputs(); + let ret_ty = sig.output(); + let name = &*tcx.item_name(def_id).as_str(); + + let llret_ty = cx.layout_of(ret_ty).llvm_type(cx); + let result = PlaceRef::new_sized(llresult, fn_ty.ret.layout, fn_ty.ret.layout.align); + + let simple = get_simple_intrinsic(cx, name); + let llval = match name { + _ if simple.is_some() => { + bx.call(simple.unwrap(), + &args.iter().map(|arg| arg.immediate()).collect::>(), + None) + } + "unreachable" => { + return; + }, + "likely" => { + let expect = cx.get_intrinsic(&("llvm.expect.i1")); + bx.call(expect, &[args[0].immediate(), C_bool(cx, true)], None) + } + "unlikely" => { + let expect = cx.get_intrinsic(&("llvm.expect.i1")); + bx.call(expect, &[args[0].immediate(), C_bool(cx, false)], None) + } + "try" => { + try_intrinsic(bx, cx, + args[0].immediate(), + args[1].immediate(), + args[2].immediate(), + llresult); + return; + } + "breakpoint" => { + let llfn = cx.get_intrinsic(&("llvm.debugtrap")); + bx.call(llfn, &[], None) + } + "size_of" => { + let tp_ty = substs.type_at(0); + C_usize(cx, cx.size_of(tp_ty).bytes()) + } + "size_of_val" => { + let tp_ty = substs.type_at(0); + if let OperandValue::Pair(_, meta) = args[0].val { + let (llsize, _) = + glue::size_and_align_of_dst(bx, tp_ty, meta); + llsize + } else { + C_usize(cx, cx.size_of(tp_ty).bytes()) + } + } + "min_align_of" => { + let tp_ty = substs.type_at(0); + C_usize(cx, cx.align_of(tp_ty).abi()) + } + "min_align_of_val" => { + let tp_ty = substs.type_at(0); + if let OperandValue::Pair(_, meta) = args[0].val { + let (_, llalign) = + glue::size_and_align_of_dst(bx, tp_ty, meta); + llalign + } else { + C_usize(cx, cx.align_of(tp_ty).abi()) + } + } + "pref_align_of" => { + let tp_ty = substs.type_at(0); + C_usize(cx, cx.align_of(tp_ty).pref()) + } + "type_name" => { + let tp_ty = substs.type_at(0); + let ty_name = Symbol::intern(&tp_ty.to_string()).as_str(); + C_str_slice(cx, ty_name) + } + "type_id" => { + C_u64(cx, cx.tcx.type_id_hash(substs.type_at(0))) + } + "init" => { + let ty = substs.type_at(0); + if !cx.layout_of(ty).is_zst() { + // Just zero out the stack slot. + // If we store a zero constant, LLVM will drown in vreg allocation for large data + // structures, and the generated code will be awful. (A telltale sign of this is + // large quantities of `mov [byte ptr foo],0` in the generated code.) + memset_intrinsic(bx, false, ty, llresult, C_u8(cx, 0), C_usize(cx, 1)); + } + return; + } + // Effectively no-ops + "uninit" => { + return; + } + "needs_drop" => { + let tp_ty = substs.type_at(0); + + C_bool(cx, bx.cx.type_needs_drop(tp_ty)) + } + "offset" => { + let ptr = args[0].immediate(); + let offset = args[1].immediate(); + bx.inbounds_gep(ptr, &[offset]) + } + "arith_offset" => { + let ptr = args[0].immediate(); + let offset = args[1].immediate(); + bx.gep(ptr, &[offset]) + } + + "copy_nonoverlapping" => { + copy_intrinsic(bx, false, false, substs.type_at(0), + args[1].immediate(), args[0].immediate(), args[2].immediate()) + } + "copy" => { + copy_intrinsic(bx, true, false, substs.type_at(0), + args[1].immediate(), args[0].immediate(), args[2].immediate()) + } + "write_bytes" => { + memset_intrinsic(bx, false, substs.type_at(0), + args[0].immediate(), args[1].immediate(), args[2].immediate()) + } + + "volatile_copy_nonoverlapping_memory" => { + copy_intrinsic(bx, false, true, substs.type_at(0), + args[0].immediate(), args[1].immediate(), args[2].immediate()) + } + "volatile_copy_memory" => { + copy_intrinsic(bx, true, true, substs.type_at(0), + args[0].immediate(), args[1].immediate(), args[2].immediate()) + } + "volatile_set_memory" => { + memset_intrinsic(bx, true, substs.type_at(0), + args[0].immediate(), args[1].immediate(), args[2].immediate()) + } + "volatile_load" => { + let tp_ty = substs.type_at(0); + let mut ptr = args[0].immediate(); + if let PassMode::Cast(ty) = fn_ty.ret.mode { + ptr = bx.pointercast(ptr, ty.llvm_type(cx).ptr_to()); + } + let load = bx.volatile_load(ptr); + unsafe { + llvm::LLVMSetAlignment(load, cx.align_of(tp_ty).abi() as u32); + } + to_immediate(bx, load, cx.layout_of(tp_ty)) + }, + "volatile_store" => { + let dst = args[0].deref(bx.cx); + args[1].val.volatile_store(bx, dst); + return; + }, + "prefetch_read_data" | "prefetch_write_data" | + "prefetch_read_instruction" | "prefetch_write_instruction" => { + let expect = cx.get_intrinsic(&("llvm.prefetch")); + let (rw, cache_type) = match name { + "prefetch_read_data" => (0, 1), + "prefetch_write_data" => (1, 1), + "prefetch_read_instruction" => (0, 0), + "prefetch_write_instruction" => (1, 0), + _ => bug!() + }; + bx.call(expect, &[ + args[0].immediate(), + C_i32(cx, rw), + args[1].immediate(), + C_i32(cx, cache_type) + ], None) + }, + "ctlz" | "ctlz_nonzero" | "cttz" | "cttz_nonzero" | "ctpop" | "bswap" | + "bitreverse" | "add_with_overflow" | "sub_with_overflow" | + "mul_with_overflow" | "overflowing_add" | "overflowing_sub" | "overflowing_mul" | + "unchecked_div" | "unchecked_rem" | "unchecked_shl" | "unchecked_shr" | "exact_div" => { + let ty = arg_tys[0]; + match int_type_width_signed(ty, cx) { + Some((width, signed)) => + match name { + "ctlz" | "cttz" => { + let y = C_bool(bx.cx, false); + let llfn = cx.get_intrinsic(&format!("llvm.{}.i{}", name, width)); + bx.call(llfn, &[args[0].immediate(), y], None) + } + "ctlz_nonzero" | "cttz_nonzero" => { + let y = C_bool(bx.cx, true); + let llvm_name = &format!("llvm.{}.i{}", &name[..4], width); + let llfn = cx.get_intrinsic(llvm_name); + bx.call(llfn, &[args[0].immediate(), y], None) + } + "ctpop" => bx.call(cx.get_intrinsic(&format!("llvm.ctpop.i{}", width)), + &[args[0].immediate()], None), + "bswap" => { + if width == 8 { + args[0].immediate() // byte swap a u8/i8 is just a no-op + } else { + bx.call(cx.get_intrinsic(&format!("llvm.bswap.i{}", width)), + &[args[0].immediate()], None) + } + } + "bitreverse" => { + bx.call(cx.get_intrinsic(&format!("llvm.bitreverse.i{}", width)), + &[args[0].immediate()], None) + } + "add_with_overflow" | "sub_with_overflow" | "mul_with_overflow" => { + let intrinsic = format!("llvm.{}{}.with.overflow.i{}", + if signed { 's' } else { 'u' }, + &name[..3], width); + let llfn = bx.cx.get_intrinsic(&intrinsic); + + // Convert `i1` to a `bool`, and write it to the out parameter + let pair = bx.call(llfn, &[ + args[0].immediate(), + args[1].immediate() + ], None); + let val = bx.extract_value(pair, 0); + let overflow = bx.zext(bx.extract_value(pair, 1), Type::bool(cx)); + + let dest = result.project_field(bx, 0); + bx.store(val, dest.llval, dest.align); + let dest = result.project_field(bx, 1); + bx.store(overflow, dest.llval, dest.align); + + return; + }, + "overflowing_add" => bx.add(args[0].immediate(), args[1].immediate()), + "overflowing_sub" => bx.sub(args[0].immediate(), args[1].immediate()), + "overflowing_mul" => bx.mul(args[0].immediate(), args[1].immediate()), + "exact_div" => + if signed { + bx.exactsdiv(args[0].immediate(), args[1].immediate()) + } else { + bx.exactudiv(args[0].immediate(), args[1].immediate()) + }, + "unchecked_div" => + if signed { + bx.sdiv(args[0].immediate(), args[1].immediate()) + } else { + bx.udiv(args[0].immediate(), args[1].immediate()) + }, + "unchecked_rem" => + if signed { + bx.srem(args[0].immediate(), args[1].immediate()) + } else { + bx.urem(args[0].immediate(), args[1].immediate()) + }, + "unchecked_shl" => bx.shl(args[0].immediate(), args[1].immediate()), + "unchecked_shr" => + if signed { + bx.ashr(args[0].immediate(), args[1].immediate()) + } else { + bx.lshr(args[0].immediate(), args[1].immediate()) + }, + _ => bug!(), + }, + None => { + span_invalid_monomorphization_error( + tcx.sess, span, + &format!("invalid monomorphization of `{}` intrinsic: \ + expected basic integer type, found `{}`", name, ty)); + return; + } + } + + }, + "fadd_fast" | "fsub_fast" | "fmul_fast" | "fdiv_fast" | "frem_fast" => { + let sty = &arg_tys[0].sty; + match float_type_width(sty) { + Some(_width) => + match name { + "fadd_fast" => bx.fadd_fast(args[0].immediate(), args[1].immediate()), + "fsub_fast" => bx.fsub_fast(args[0].immediate(), args[1].immediate()), + "fmul_fast" => bx.fmul_fast(args[0].immediate(), args[1].immediate()), + "fdiv_fast" => bx.fdiv_fast(args[0].immediate(), args[1].immediate()), + "frem_fast" => bx.frem_fast(args[0].immediate(), args[1].immediate()), + _ => bug!(), + }, + None => { + span_invalid_monomorphization_error( + tcx.sess, span, + &format!("invalid monomorphization of `{}` intrinsic: \ + expected basic float type, found `{}`", name, sty)); + return; + } + } + + }, + + "discriminant_value" => { + args[0].deref(bx.cx).codegen_get_discr(bx, ret_ty) + } + + "align_offset" => { + // `ptr as usize` + let ptr_val = bx.ptrtoint(args[0].immediate(), bx.cx.isize_ty); + // `ptr_val % align` + let align = args[1].immediate(); + let offset = bx.urem(ptr_val, align); + let zero = C_null(bx.cx.isize_ty); + // `offset == 0` + let is_zero = bx.icmp(llvm::IntPredicate::IntEQ, offset, zero); + // `if offset == 0 { 0 } else { align - offset }` + bx.select(is_zero, zero, bx.sub(align, offset)) + } + name if name.starts_with("simd_") => { + match generic_simd_intrinsic(bx, name, + callee_ty, + args, + ret_ty, llret_ty, + span) { + Ok(llval) => llval, + Err(()) => return + } + } + // This requires that atomic intrinsics follow a specific naming pattern: + // "atomic_[_]", and no ordering means SeqCst + name if name.starts_with("atomic_") => { + use llvm::AtomicOrdering::*; + + let split: Vec<&str> = name.split('_').collect(); + + let is_cxchg = split[1] == "cxchg" || split[1] == "cxchgweak"; + let (order, failorder) = match split.len() { + 2 => (SequentiallyConsistent, SequentiallyConsistent), + 3 => match split[2] { + "unordered" => (Unordered, Unordered), + "relaxed" => (Monotonic, Monotonic), + "acq" => (Acquire, Acquire), + "rel" => (Release, Monotonic), + "acqrel" => (AcquireRelease, Acquire), + "failrelaxed" if is_cxchg => + (SequentiallyConsistent, Monotonic), + "failacq" if is_cxchg => + (SequentiallyConsistent, Acquire), + _ => cx.sess().fatal("unknown ordering in atomic intrinsic") + }, + 4 => match (split[2], split[3]) { + ("acq", "failrelaxed") if is_cxchg => + (Acquire, Monotonic), + ("acqrel", "failrelaxed") if is_cxchg => + (AcquireRelease, Monotonic), + _ => cx.sess().fatal("unknown ordering in atomic intrinsic") + }, + _ => cx.sess().fatal("Atomic intrinsic not in correct format"), + }; + + let invalid_monomorphization = |ty| { + span_invalid_monomorphization_error(tcx.sess, span, + &format!("invalid monomorphization of `{}` intrinsic: \ + expected basic integer type, found `{}`", name, ty)); + }; + + match split[1] { + "cxchg" | "cxchgweak" => { + let ty = substs.type_at(0); + if int_type_width_signed(ty, cx).is_some() { + let weak = if split[1] == "cxchgweak" { llvm::True } else { llvm::False }; + let pair = bx.atomic_cmpxchg( + args[0].immediate(), + args[1].immediate(), + args[2].immediate(), + order, + failorder, + weak); + let val = bx.extract_value(pair, 0); + let success = bx.zext(bx.extract_value(pair, 1), Type::bool(bx.cx)); + + let dest = result.project_field(bx, 0); + bx.store(val, dest.llval, dest.align); + let dest = result.project_field(bx, 1); + bx.store(success, dest.llval, dest.align); + return; + } else { + return invalid_monomorphization(ty); + } + } + + "load" => { + let ty = substs.type_at(0); + if int_type_width_signed(ty, cx).is_some() { + let align = cx.align_of(ty); + bx.atomic_load(args[0].immediate(), order, align) + } else { + return invalid_monomorphization(ty); + } + } + + "store" => { + let ty = substs.type_at(0); + if int_type_width_signed(ty, cx).is_some() { + let align = cx.align_of(ty); + bx.atomic_store(args[1].immediate(), args[0].immediate(), order, align); + return; + } else { + return invalid_monomorphization(ty); + } + } + + "fence" => { + bx.atomic_fence(order, llvm::SynchronizationScope::CrossThread); + return; + } + + "singlethreadfence" => { + bx.atomic_fence(order, llvm::SynchronizationScope::SingleThread); + return; + } + + // These are all AtomicRMW ops + op => { + let atom_op = match op { + "xchg" => llvm::AtomicXchg, + "xadd" => llvm::AtomicAdd, + "xsub" => llvm::AtomicSub, + "and" => llvm::AtomicAnd, + "nand" => llvm::AtomicNand, + "or" => llvm::AtomicOr, + "xor" => llvm::AtomicXor, + "max" => llvm::AtomicMax, + "min" => llvm::AtomicMin, + "umax" => llvm::AtomicUMax, + "umin" => llvm::AtomicUMin, + _ => cx.sess().fatal("unknown atomic operation") + }; + + let ty = substs.type_at(0); + if int_type_width_signed(ty, cx).is_some() { + bx.atomic_rmw(atom_op, args[0].immediate(), args[1].immediate(), order) + } else { + return invalid_monomorphization(ty); + } + } + } + } + + "nontemporal_store" => { + let dst = args[0].deref(bx.cx); + args[1].val.nontemporal_store(bx, dst); + return; + } + + _ => { + let intr = match Intrinsic::find(&name) { + Some(intr) => intr, + None => bug!("unknown intrinsic '{}'", name), + }; + fn one(x: Vec) -> T { + assert_eq!(x.len(), 1); + x.into_iter().next().unwrap() + } + fn ty_to_type(cx: &CodegenCx, t: &intrinsics::Type) -> Vec { + use intrinsics::Type::*; + match *t { + Void => vec![Type::void(cx)], + Integer(_signed, _width, llvm_width) => { + vec![Type::ix(cx, llvm_width as u64)] + } + Float(x) => { + match x { + 32 => vec![Type::f32(cx)], + 64 => vec![Type::f64(cx)], + _ => bug!() + } + } + Pointer(ref t, ref llvm_elem, _const) => { + let t = llvm_elem.as_ref().unwrap_or(t); + let elem = one(ty_to_type(cx, t)); + vec![elem.ptr_to()] + } + Vector(ref t, ref llvm_elem, length) => { + let t = llvm_elem.as_ref().unwrap_or(t); + let elem = one(ty_to_type(cx, t)); + vec![Type::vector(&elem, length as u64)] + } + Aggregate(false, ref contents) => { + let elems = contents.iter() + .map(|t| one(ty_to_type(cx, t))) + .collect::>(); + vec![Type::struct_(cx, &elems, false)] + } + Aggregate(true, ref contents) => { + contents.iter() + .flat_map(|t| ty_to_type(cx, t)) + .collect() + } + } + } + + // This allows an argument list like `foo, (bar, baz), + // qux` to be converted into `foo, bar, baz, qux`, integer + // arguments to be truncated as needed and pointers to be + // cast. + fn modify_as_needed<'a, 'tcx>(bx: &Builder<'a, 'tcx>, + t: &intrinsics::Type, + arg: &OperandRef<'tcx>) + -> Vec + { + match *t { + intrinsics::Type::Aggregate(true, ref contents) => { + // We found a tuple that needs squishing! So + // run over the tuple and load each field. + // + // This assumes the type is "simple", i.e. no + // destructors, and the contents are SIMD + // etc. + assert!(!bx.cx.type_needs_drop(arg.layout.ty)); + let (ptr, align) = match arg.val { + OperandValue::Ref(ptr, align) => (ptr, align), + _ => bug!() + }; + let arg = PlaceRef::new_sized(ptr, arg.layout, align); + (0..contents.len()).map(|i| { + arg.project_field(bx, i).load(bx).immediate() + }).collect() + } + intrinsics::Type::Pointer(_, Some(ref llvm_elem), _) => { + let llvm_elem = one(ty_to_type(bx.cx, llvm_elem)); + vec![bx.pointercast(arg.immediate(), llvm_elem.ptr_to())] + } + intrinsics::Type::Vector(_, Some(ref llvm_elem), length) => { + let llvm_elem = one(ty_to_type(bx.cx, llvm_elem)); + vec![bx.bitcast(arg.immediate(), Type::vector(&llvm_elem, length as u64))] + } + intrinsics::Type::Integer(_, width, llvm_width) if width != llvm_width => { + // the LLVM intrinsic uses a smaller integer + // size than the C intrinsic's signature, so + // we have to trim it down here. + vec![bx.trunc(arg.immediate(), Type::ix(bx.cx, llvm_width as u64))] + } + _ => vec![arg.immediate()], + } + } + + + let inputs = intr.inputs.iter() + .flat_map(|t| ty_to_type(cx, t)) + .collect::>(); + + let outputs = one(ty_to_type(cx, &intr.output)); + + let llargs: Vec<_> = intr.inputs.iter().zip(args).flat_map(|(t, arg)| { + modify_as_needed(bx, t, arg) + }).collect(); + assert_eq!(inputs.len(), llargs.len()); + + let val = match intr.definition { + intrinsics::IntrinsicDef::Named(name) => { + let f = declare::declare_cfn(cx, + name, + Type::func(&inputs, &outputs)); + bx.call(f, &llargs, None) + } + }; + + match *intr.output { + intrinsics::Type::Aggregate(flatten, ref elems) => { + // the output is a tuple so we need to munge it properly + assert!(!flatten); + + for i in 0..elems.len() { + let dest = result.project_field(bx, i); + let val = bx.extract_value(val, i as u64); + bx.store(val, dest.llval, dest.align); + } + return; + } + _ => val, + } + } + }; + + if !fn_ty.ret.is_ignore() { + if let PassMode::Cast(ty) = fn_ty.ret.mode { + let ptr = bx.pointercast(result.llval, ty.llvm_type(cx).ptr_to()); + bx.store(llval, ptr, result.align); + } else { + OperandRef::from_immediate_or_packed_pair(bx, llval, result.layout) + .val.store(bx, result); + } + } +} + +fn copy_intrinsic<'a, 'tcx>(bx: &Builder<'a, 'tcx>, + allow_overlap: bool, + volatile: bool, + ty: Ty<'tcx>, + dst: ValueRef, + src: ValueRef, + count: ValueRef) + -> ValueRef { + let cx = bx.cx; + let (size, align) = cx.size_and_align_of(ty); + let size = C_usize(cx, size.bytes()); + let align = C_i32(cx, align.abi() as i32); + + let operation = if allow_overlap { + "memmove" + } else { + "memcpy" + }; + + let name = format!("llvm.{}.p0i8.p0i8.i{}", operation, + cx.data_layout().pointer_size.bits()); + + let dst_ptr = bx.pointercast(dst, Type::i8p(cx)); + let src_ptr = bx.pointercast(src, Type::i8p(cx)); + let llfn = cx.get_intrinsic(&name); + + bx.call(llfn, + &[dst_ptr, + src_ptr, + bx.mul(size, count), + align, + C_bool(cx, volatile)], + None) +} + +fn memset_intrinsic<'a, 'tcx>( + bx: &Builder<'a, 'tcx>, + volatile: bool, + ty: Ty<'tcx>, + dst: ValueRef, + val: ValueRef, + count: ValueRef +) -> ValueRef { + let cx = bx.cx; + let (size, align) = cx.size_and_align_of(ty); + let size = C_usize(cx, size.bytes()); + let align = C_i32(cx, align.abi() as i32); + let dst = bx.pointercast(dst, Type::i8p(cx)); + call_memset(bx, dst, val, bx.mul(size, count), align, volatile) +} + +fn try_intrinsic<'a, 'tcx>( + bx: &Builder<'a, 'tcx>, + cx: &CodegenCx, + func: ValueRef, + data: ValueRef, + local_ptr: ValueRef, + dest: ValueRef, +) { + if bx.sess().no_landing_pads() { + bx.call(func, &[data], None); + let ptr_align = bx.tcx().data_layout.pointer_align; + bx.store(C_null(Type::i8p(&bx.cx)), dest, ptr_align); + } else if wants_msvc_seh(bx.sess()) { + codegen_msvc_try(bx, cx, func, data, local_ptr, dest); + } else { + codegen_gnu_try(bx, cx, func, data, local_ptr, dest); + } +} + +// MSVC's definition of the `rust_try` function. +// +// This implementation uses the new exception handling instructions in LLVM +// which have support in LLVM for SEH on MSVC targets. Although these +// instructions are meant to work for all targets, as of the time of this +// writing, however, LLVM does not recommend the usage of these new instructions +// as the old ones are still more optimized. +fn codegen_msvc_try<'a, 'tcx>(bx: &Builder<'a, 'tcx>, + cx: &CodegenCx, + func: ValueRef, + data: ValueRef, + local_ptr: ValueRef, + dest: ValueRef) { + let llfn = get_rust_try_fn(cx, &mut |bx| { + let cx = bx.cx; + + bx.set_personality_fn(bx.cx.eh_personality()); + + let normal = bx.build_sibling_block("normal"); + let catchswitch = bx.build_sibling_block("catchswitch"); + let catchpad = bx.build_sibling_block("catchpad"); + let caught = bx.build_sibling_block("caught"); + + let func = llvm::get_param(bx.llfn(), 0); + let data = llvm::get_param(bx.llfn(), 1); + let local_ptr = llvm::get_param(bx.llfn(), 2); + + // We're generating an IR snippet that looks like: + // + // declare i32 @rust_try(%func, %data, %ptr) { + // %slot = alloca i64* + // invoke %func(%data) to label %normal unwind label %catchswitch + // + // normal: + // ret i32 0 + // + // catchswitch: + // %cs = catchswitch within none [%catchpad] unwind to caller + // + // catchpad: + // %tok = catchpad within %cs [%type_descriptor, 0, %slot] + // %ptr[0] = %slot[0] + // %ptr[1] = %slot[1] + // catchret from %tok to label %caught + // + // caught: + // ret i32 1 + // } + // + // This structure follows the basic usage of throw/try/catch in LLVM. + // For example, compile this C++ snippet to see what LLVM generates: + // + // #include + // + // int bar(void (*foo)(void), uint64_t *ret) { + // try { + // foo(); + // return 0; + // } catch(uint64_t a[2]) { + // ret[0] = a[0]; + // ret[1] = a[1]; + // return 1; + // } + // } + // + // More information can be found in libstd's seh.rs implementation. + let i64p = Type::i64(cx).ptr_to(); + let ptr_align = bx.tcx().data_layout.pointer_align; + let slot = bx.alloca(i64p, "slot", ptr_align); + bx.invoke(func, &[data], normal.llbb(), catchswitch.llbb(), + None); + + normal.ret(C_i32(cx, 0)); + + let cs = catchswitch.catch_switch(None, None, 1); + catchswitch.add_handler(cs, catchpad.llbb()); + + let tcx = cx.tcx; + let tydesc = match tcx.lang_items().msvc_try_filter() { + Some(did) => ::consts::get_static(cx, did), + None => bug!("msvc_try_filter not defined"), + }; + let tok = catchpad.catch_pad(cs, &[tydesc, C_i32(cx, 0), slot]); + let addr = catchpad.load(slot, ptr_align); + + let i64_align = bx.tcx().data_layout.i64_align; + let arg1 = catchpad.load(addr, i64_align); + let val1 = C_i32(cx, 1); + let arg2 = catchpad.load(catchpad.inbounds_gep(addr, &[val1]), i64_align); + let local_ptr = catchpad.bitcast(local_ptr, i64p); + catchpad.store(arg1, local_ptr, i64_align); + catchpad.store(arg2, catchpad.inbounds_gep(local_ptr, &[val1]), i64_align); + catchpad.catch_ret(tok, caught.llbb()); + + caught.ret(C_i32(cx, 1)); + }); + + // Note that no invoke is used here because by definition this function + // can't panic (that's what it's catching). + let ret = bx.call(llfn, &[func, data, local_ptr], None); + let i32_align = bx.tcx().data_layout.i32_align; + bx.store(ret, dest, i32_align); +} + +// Definition of the standard "try" function for Rust using the GNU-like model +// of exceptions (e.g. the normal semantics of LLVM's landingpad and invoke +// instructions). +// +// This codegen is a little surprising because we always call a shim +// function instead of inlining the call to `invoke` manually here. This is done +// because in LLVM we're only allowed to have one personality per function +// definition. The call to the `try` intrinsic is being inlined into the +// function calling it, and that function may already have other personality +// functions in play. By calling a shim we're guaranteed that our shim will have +// the right personality function. +fn codegen_gnu_try<'a, 'tcx>(bx: &Builder<'a, 'tcx>, + cx: &CodegenCx, + func: ValueRef, + data: ValueRef, + local_ptr: ValueRef, + dest: ValueRef) { + let llfn = get_rust_try_fn(cx, &mut |bx| { + let cx = bx.cx; + + // Codegens the shims described above: + // + // bx: + // invoke %func(%args...) normal %normal unwind %catch + // + // normal: + // ret 0 + // + // catch: + // (ptr, _) = landingpad + // store ptr, %local_ptr + // ret 1 + // + // Note that the `local_ptr` data passed into the `try` intrinsic is + // expected to be `*mut *mut u8` for this to actually work, but that's + // managed by the standard library. + + let then = bx.build_sibling_block("then"); + let catch = bx.build_sibling_block("catch"); + + let func = llvm::get_param(bx.llfn(), 0); + let data = llvm::get_param(bx.llfn(), 1); + let local_ptr = llvm::get_param(bx.llfn(), 2); + bx.invoke(func, &[data], then.llbb(), catch.llbb(), None); + then.ret(C_i32(cx, 0)); + + // Type indicator for the exception being thrown. + // + // The first value in this tuple is a pointer to the exception object + // being thrown. The second value is a "selector" indicating which of + // the landing pad clauses the exception's type had been matched to. + // rust_try ignores the selector. + let lpad_ty = Type::struct_(cx, &[Type::i8p(cx), Type::i32(cx)], + false); + let vals = catch.landing_pad(lpad_ty, bx.cx.eh_personality(), 1); + catch.add_clause(vals, C_null(Type::i8p(cx))); + let ptr = catch.extract_value(vals, 0); + let ptr_align = bx.tcx().data_layout.pointer_align; + catch.store(ptr, catch.bitcast(local_ptr, Type::i8p(cx).ptr_to()), ptr_align); + catch.ret(C_i32(cx, 1)); + }); + + // Note that no invoke is used here because by definition this function + // can't panic (that's what it's catching). + let ret = bx.call(llfn, &[func, data, local_ptr], None); + let i32_align = bx.tcx().data_layout.i32_align; + bx.store(ret, dest, i32_align); +} + +// Helper function to give a Block to a closure to codegen a shim function. +// This is currently primarily used for the `try` intrinsic functions above. +fn gen_fn<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, + name: &str, + inputs: Vec>, + output: Ty<'tcx>, + codegen: &mut for<'b> FnMut(Builder<'b, 'tcx>)) + -> ValueRef { + let rust_fn_ty = cx.tcx.mk_fn_ptr(ty::Binder::bind(cx.tcx.mk_fn_sig( + inputs.into_iter(), + output, + false, + hir::Unsafety::Unsafe, + Abi::Rust + ))); + let llfn = declare::define_internal_fn(cx, name, rust_fn_ty); + let bx = Builder::new_block(cx, llfn, "entry-block"); + codegen(bx); + llfn +} + +// Helper function used to get a handle to the `__rust_try` function used to +// catch exceptions. +// +// This function is only generated once and is then cached. +fn get_rust_try_fn<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, + codegen: &mut for<'b> FnMut(Builder<'b, 'tcx>)) + -> ValueRef { + if let Some(llfn) = cx.rust_try_fn.get() { + return llfn; + } + + // Define the type up front for the signature of the rust_try function. + let tcx = cx.tcx; + let i8p = tcx.mk_mut_ptr(tcx.types.i8); + let fn_ty = tcx.mk_fn_ptr(ty::Binder::bind(tcx.mk_fn_sig( + iter::once(i8p), + tcx.mk_nil(), + false, + hir::Unsafety::Unsafe, + Abi::Rust + ))); + let output = tcx.types.i32; + let rust_try = gen_fn(cx, "__rust_try", vec![fn_ty, i8p, i8p], output, codegen); + cx.rust_try_fn.set(Some(rust_try)); + return rust_try +} + +fn span_invalid_monomorphization_error(a: &Session, b: Span, c: &str) { + span_err!(a, b, E0511, "{}", c); +} + +fn generic_simd_intrinsic<'a, 'tcx>( + bx: &Builder<'a, 'tcx>, + name: &str, + callee_ty: Ty<'tcx>, + args: &[OperandRef<'tcx>], + ret_ty: Ty<'tcx>, + llret_ty: Type, + span: Span +) -> Result { + // macros for error handling: + macro_rules! emit_error { + ($msg: tt) => { + emit_error!($msg, ) + }; + ($msg: tt, $($fmt: tt)*) => { + span_invalid_monomorphization_error( + bx.sess(), span, + &format!(concat!("invalid monomorphization of `{}` intrinsic: ", + $msg), + name, $($fmt)*)); + } + } + macro_rules! return_error { + ($($fmt: tt)*) => { + { + emit_error!($($fmt)*); + return Err(()); + } + } + } + + macro_rules! require { + ($cond: expr, $($fmt: tt)*) => { + if !$cond { + return_error!($($fmt)*); + } + }; + } + macro_rules! require_simd { + ($ty: expr, $position: expr) => { + require!($ty.is_simd(), "expected SIMD {} type, found non-SIMD `{}`", $position, $ty) + } + } + + + + let tcx = bx.tcx(); + let sig = tcx.normalize_erasing_late_bound_regions( + ty::ParamEnv::reveal_all(), + &callee_ty.fn_sig(tcx), + ); + let arg_tys = sig.inputs(); + + // every intrinsic takes a SIMD vector as its first argument + require_simd!(arg_tys[0], "input"); + let in_ty = arg_tys[0]; + let in_elem = arg_tys[0].simd_type(tcx); + let in_len = arg_tys[0].simd_size(tcx); + + let comparison = match name { + "simd_eq" => Some(hir::BiEq), + "simd_ne" => Some(hir::BiNe), + "simd_lt" => Some(hir::BiLt), + "simd_le" => Some(hir::BiLe), + "simd_gt" => Some(hir::BiGt), + "simd_ge" => Some(hir::BiGe), + _ => None + }; + + if let Some(cmp_op) = comparison { + require_simd!(ret_ty, "return"); + + let out_len = ret_ty.simd_size(tcx); + require!(in_len == out_len, + "expected return type with length {} (same as input type `{}`), \ + found `{}` with length {}", + in_len, in_ty, + ret_ty, out_len); + require!(llret_ty.element_type().kind() == llvm::Integer, + "expected return type with integer elements, found `{}` with non-integer `{}`", + ret_ty, + ret_ty.simd_type(tcx)); + + return Ok(compare_simd_types(bx, + args[0].immediate(), + args[1].immediate(), + in_elem, + llret_ty, + cmp_op)) + } + + if name.starts_with("simd_shuffle") { + let n: usize = match name["simd_shuffle".len()..].parse() { + Ok(n) => n, + Err(_) => span_bug!(span, + "bad `simd_shuffle` instruction only caught in codegen?") + }; + + require_simd!(ret_ty, "return"); + + let out_len = ret_ty.simd_size(tcx); + require!(out_len == n, + "expected return type of length {}, found `{}` with length {}", + n, ret_ty, out_len); + require!(in_elem == ret_ty.simd_type(tcx), + "expected return element type `{}` (element of input `{}`), \ + found `{}` with element type `{}`", + in_elem, in_ty, + ret_ty, ret_ty.simd_type(tcx)); + + let total_len = in_len as u128 * 2; + + let vector = args[2].immediate(); + + let indices: Option> = (0..n) + .map(|i| { + let arg_idx = i; + let val = const_get_elt(vector, i as u64); + match const_to_opt_u128(val, true) { + None => { + emit_error!("shuffle index #{} is not a constant", arg_idx); + None + } + Some(idx) if idx >= total_len => { + emit_error!("shuffle index #{} is out of bounds (limit {})", + arg_idx, total_len); + None + } + Some(idx) => Some(C_i32(bx.cx, idx as i32)), + } + }) + .collect(); + let indices = match indices { + Some(i) => i, + None => return Ok(C_null(llret_ty)) + }; + + return Ok(bx.shuffle_vector(args[0].immediate(), + args[1].immediate(), + C_vector(&indices))) + } + + if name == "simd_insert" { + require!(in_elem == arg_tys[2], + "expected inserted type `{}` (element of input `{}`), found `{}`", + in_elem, in_ty, arg_tys[2]); + return Ok(bx.insert_element(args[0].immediate(), + args[2].immediate(), + args[1].immediate())) + } + if name == "simd_extract" { + require!(ret_ty == in_elem, + "expected return type `{}` (element of input `{}`), found `{}`", + in_elem, in_ty, ret_ty); + return Ok(bx.extract_element(args[0].immediate(), args[1].immediate())) + } + + if name == "simd_select" { + let m_elem_ty = in_elem; + let m_len = in_len; + let v_len = arg_tys[1].simd_size(tcx); + require!(m_len == v_len, + "mismatched lengths: mask length `{}` != other vector length `{}`", + m_len, v_len + ); + match m_elem_ty.sty { + ty::TyInt(_) => {}, + _ => { + return_error!("mask element type is `{}`, expected `i_`", m_elem_ty); + } + } + // truncate the mask to a vector of i1s + let i1 = Type::i1(bx.cx); + let i1xn = Type::vector(&i1, m_len as u64); + let m_i1s = bx.trunc(args[0].immediate(), i1xn); + return Ok(bx.select(m_i1s, args[1].immediate(), args[2].immediate())); + } + + macro_rules! arith_red { + ($name:tt : $integer_reduce:ident, $float_reduce:ident, $ordered:expr) => { + if name == $name { + require!(ret_ty == in_elem, + "expected return type `{}` (element of input `{}`), found `{}`", + in_elem, in_ty, ret_ty); + return match in_elem.sty { + ty::TyInt(_) | ty::TyUint(_) => { + let r = bx.$integer_reduce(args[0].immediate()); + if $ordered { + // if overflow occurs, the result is the + // mathematical result modulo 2^n: + if name.contains("mul") { + Ok(bx.mul(args[1].immediate(), r)) + } else { + Ok(bx.add(args[1].immediate(), r)) + } + } else { + Ok(bx.$integer_reduce(args[0].immediate())) + } + }, + ty::TyFloat(f) => { + // ordered arithmetic reductions take an accumulator + let acc = if $ordered { + let acc = args[1].immediate(); + // FIXME: https://bugs.llvm.org/show_bug.cgi?id=36734 + // * if the accumulator of the fadd isn't 0, incorrect + // code is generated + // * if the accumulator of the fmul isn't 1, incorrect + // code is generated + match const_get_real(acc) { + None => return_error!("accumulator of {} is not a constant", $name), + Some((v, loses_info)) => { + if $name.contains("mul") && v != 1.0_f64 { + return_error!("accumulator of {} is not 1.0", $name); + } else if $name.contains("add") && v != 0.0_f64 { + return_error!("accumulator of {} is not 0.0", $name); + } else if loses_info { + return_error!("accumulator of {} loses information", $name); + } + } + } + acc + } else { + // unordered arithmetic reductions do not: + match f.bit_width() { + 32 => C_undef(Type::f32(bx.cx)), + 64 => C_undef(Type::f64(bx.cx)), + v => { + return_error!(r#" +unsupported {} from `{}` with element `{}` of size `{}` to `{}`"#, + $name, in_ty, in_elem, v, ret_ty + ) + } + } + + }; + Ok(bx.$float_reduce(acc, args[0].immediate())) + } + _ => { + return_error!( + "unsupported {} from `{}` with element `{}` to `{}`", + $name, in_ty, in_elem, ret_ty + ) + }, + } + } + } + } + + arith_red!("simd_reduce_add_ordered": vector_reduce_add, vector_reduce_fadd_fast, true); + arith_red!("simd_reduce_mul_ordered": vector_reduce_mul, vector_reduce_fmul_fast, true); + arith_red!("simd_reduce_add_unordered": vector_reduce_add, vector_reduce_fadd_fast, false); + arith_red!("simd_reduce_mul_unordered": vector_reduce_mul, vector_reduce_fmul_fast, false); + + macro_rules! minmax_red { + ($name:tt: $int_red:ident, $float_red:ident) => { + if name == $name { + require!(ret_ty == in_elem, + "expected return type `{}` (element of input `{}`), found `{}`", + in_elem, in_ty, ret_ty); + return match in_elem.sty { + ty::TyInt(_i) => { + Ok(bx.$int_red(args[0].immediate(), true)) + }, + ty::TyUint(_u) => { + Ok(bx.$int_red(args[0].immediate(), false)) + }, + ty::TyFloat(_f) => { + Ok(bx.$float_red(args[0].immediate())) + } + _ => { + return_error!("unsupported {} from `{}` with element `{}` to `{}`", + $name, in_ty, in_elem, ret_ty) + }, + } + } + + } + } + + minmax_red!("simd_reduce_min": vector_reduce_min, vector_reduce_fmin); + minmax_red!("simd_reduce_max": vector_reduce_max, vector_reduce_fmax); + + minmax_red!("simd_reduce_min_nanless": vector_reduce_min, vector_reduce_fmin_fast); + minmax_red!("simd_reduce_max_nanless": vector_reduce_max, vector_reduce_fmax_fast); + + macro_rules! bitwise_red { + ($name:tt : $red:ident, $boolean:expr) => { + if name == $name { + let input = if !$boolean { + require!(ret_ty == in_elem, + "expected return type `{}` (element of input `{}`), found `{}`", + in_elem, in_ty, ret_ty); + args[0].immediate() + } else { + match in_elem.sty { + ty::TyInt(_) | ty::TyUint(_) => {}, + _ => { + return_error!("unsupported {} from `{}` with element `{}` to `{}`", + $name, in_ty, in_elem, ret_ty) + } + } + + // boolean reductions operate on vectors of i1s: + let i1 = Type::i1(bx.cx); + let i1xn = Type::vector(&i1, in_len as u64); + bx.trunc(args[0].immediate(), i1xn) + }; + return match in_elem.sty { + ty::TyInt(_) | ty::TyUint(_) => { + let r = bx.$red(input); + Ok( + if !$boolean { + r + } else { + bx.zext(r, Type::bool(bx.cx)) + } + ) + }, + _ => { + return_error!("unsupported {} from `{}` with element `{}` to `{}`", + $name, in_ty, in_elem, ret_ty) + }, + } + } + } + } + + bitwise_red!("simd_reduce_and": vector_reduce_and, false); + bitwise_red!("simd_reduce_or": vector_reduce_or, false); + bitwise_red!("simd_reduce_xor": vector_reduce_xor, false); + bitwise_red!("simd_reduce_all": vector_reduce_and, true); + bitwise_red!("simd_reduce_any": vector_reduce_or, true); + + if name == "simd_cast" { + require_simd!(ret_ty, "return"); + let out_len = ret_ty.simd_size(tcx); + require!(in_len == out_len, + "expected return type with length {} (same as input type `{}`), \ + found `{}` with length {}", + in_len, in_ty, + ret_ty, out_len); + // casting cares about nominal type, not just structural type + let out_elem = ret_ty.simd_type(tcx); + + if in_elem == out_elem { return Ok(args[0].immediate()); } + + enum Style { Float, Int(/* is signed? */ bool), Unsupported } + + let (in_style, in_width) = match in_elem.sty { + // vectors of pointer-sized integers should've been + // disallowed before here, so this unwrap is safe. + ty::TyInt(i) => (Style::Int(true), i.bit_width().unwrap()), + ty::TyUint(u) => (Style::Int(false), u.bit_width().unwrap()), + ty::TyFloat(f) => (Style::Float, f.bit_width()), + _ => (Style::Unsupported, 0) + }; + let (out_style, out_width) = match out_elem.sty { + ty::TyInt(i) => (Style::Int(true), i.bit_width().unwrap()), + ty::TyUint(u) => (Style::Int(false), u.bit_width().unwrap()), + ty::TyFloat(f) => (Style::Float, f.bit_width()), + _ => (Style::Unsupported, 0) + }; + + match (in_style, out_style) { + (Style::Int(in_is_signed), Style::Int(_)) => { + return Ok(match in_width.cmp(&out_width) { + Ordering::Greater => bx.trunc(args[0].immediate(), llret_ty), + Ordering::Equal => args[0].immediate(), + Ordering::Less => if in_is_signed { + bx.sext(args[0].immediate(), llret_ty) + } else { + bx.zext(args[0].immediate(), llret_ty) + } + }) + } + (Style::Int(in_is_signed), Style::Float) => { + return Ok(if in_is_signed { + bx.sitofp(args[0].immediate(), llret_ty) + } else { + bx.uitofp(args[0].immediate(), llret_ty) + }) + } + (Style::Float, Style::Int(out_is_signed)) => { + return Ok(if out_is_signed { + bx.fptosi(args[0].immediate(), llret_ty) + } else { + bx.fptoui(args[0].immediate(), llret_ty) + }) + } + (Style::Float, Style::Float) => { + return Ok(match in_width.cmp(&out_width) { + Ordering::Greater => bx.fptrunc(args[0].immediate(), llret_ty), + Ordering::Equal => args[0].immediate(), + Ordering::Less => bx.fpext(args[0].immediate(), llret_ty) + }) + } + _ => {/* Unsupported. Fallthrough. */} + } + require!(false, + "unsupported cast from `{}` with element `{}` to `{}` with element `{}`", + in_ty, in_elem, + ret_ty, out_elem); + } + macro_rules! arith { + ($($name: ident: $($($p: ident),* => $call: ident),*;)*) => { + $(if name == stringify!($name) { + match in_elem.sty { + $($(ty::$p(_))|* => { + return Ok(bx.$call(args[0].immediate(), args[1].immediate())) + })* + _ => {}, + } + require!(false, + "unsupported operation on `{}` with element `{}`", + in_ty, + in_elem) + })* + } + } + arith! { + simd_add: TyUint, TyInt => add, TyFloat => fadd; + simd_sub: TyUint, TyInt => sub, TyFloat => fsub; + simd_mul: TyUint, TyInt => mul, TyFloat => fmul; + simd_div: TyUint => udiv, TyInt => sdiv, TyFloat => fdiv; + simd_rem: TyUint => urem, TyInt => srem, TyFloat => frem; + simd_shl: TyUint, TyInt => shl; + simd_shr: TyUint => lshr, TyInt => ashr; + simd_and: TyUint, TyInt => and; + simd_or: TyUint, TyInt => or; + simd_xor: TyUint, TyInt => xor; + simd_fmax: TyFloat => maxnum; + simd_fmin: TyFloat => minnum; + } + span_bug!(span, "unknown SIMD intrinsic"); +} + +// Returns the width of an int Ty, and if it's signed or not +// Returns None if the type is not an integer +// FIXME: there’s multiple of this functions, investigate using some of the already existing +// stuffs. +fn int_type_width_signed(ty: Ty, cx: &CodegenCx) -> Option<(u64, bool)> { + match ty.sty { + ty::TyInt(t) => Some((match t { + ast::IntTy::Isize => { + match &cx.tcx.sess.target.target.target_pointer_width[..] { + "16" => 16, + "32" => 32, + "64" => 64, + tws => bug!("Unsupported target word size for isize: {}", tws), + } + }, + ast::IntTy::I8 => 8, + ast::IntTy::I16 => 16, + ast::IntTy::I32 => 32, + ast::IntTy::I64 => 64, + ast::IntTy::I128 => 128, + }, true)), + ty::TyUint(t) => Some((match t { + ast::UintTy::Usize => { + match &cx.tcx.sess.target.target.target_pointer_width[..] { + "16" => 16, + "32" => 32, + "64" => 64, + tws => bug!("Unsupported target word size for usize: {}", tws), + } + }, + ast::UintTy::U8 => 8, + ast::UintTy::U16 => 16, + ast::UintTy::U32 => 32, + ast::UintTy::U64 => 64, + ast::UintTy::U128 => 128, + }, false)), + _ => None, + } +} + +// Returns the width of a float TypeVariant +// Returns None if the type is not a float +fn float_type_width<'tcx>(sty: &ty::TypeVariants<'tcx>) + -> Option { + use rustc::ty::TyFloat; + match *sty { + TyFloat(t) => Some(match t { + ast::FloatTy::F32 => 32, + ast::FloatTy::F64 => 64, + }), + _ => None, + } +} diff --git a/src/librustc_codegen_llvm/lib.rs b/src/librustc_codegen_llvm/lib.rs new file mode 100644 index 00000000000..bd053da4bd3 --- /dev/null +++ b/src/librustc_codegen_llvm/lib.rs @@ -0,0 +1,392 @@ +// Copyright 2012-2013 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. + +//! The Rust compiler. +//! +//! # Note +//! +//! This API is completely unstable and subject to change. + +#![doc(html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png", + html_favicon_url = "https://doc.rust-lang.org/favicon.ico", + html_root_url = "https://doc.rust-lang.org/nightly/")] + +#![feature(box_patterns)] +#![feature(box_syntax)] +#![feature(custom_attribute)] +#![feature(fs_read_write)] +#![allow(unused_attributes)] +#![feature(libc)] +#![feature(quote)] +#![feature(range_contains)] +#![feature(rustc_diagnostic_macros)] +#![feature(slice_sort_by_cached_key)] +#![feature(optin_builtin_traits)] +#![feature(inclusive_range_methods)] + +use rustc::dep_graph::WorkProduct; +use syntax_pos::symbol::Symbol; + +#[macro_use] extern crate bitflags; +extern crate flate2; +extern crate libc; +#[macro_use] extern crate rustc; +extern crate jobserver; +extern crate num_cpus; +extern crate rustc_mir; +extern crate rustc_allocator; +extern crate rustc_apfloat; +extern crate rustc_target; +#[macro_use] extern crate rustc_data_structures; +extern crate rustc_demangle; +extern crate rustc_incremental; +extern crate rustc_llvm as llvm; +extern crate rustc_platform_intrinsics as intrinsics; +extern crate rustc_codegen_utils; + +#[macro_use] extern crate log; +#[macro_use] extern crate syntax; +extern crate syntax_pos; +extern crate rustc_errors as errors; +extern crate serialize; +extern crate cc; // Used to locate MSVC +extern crate tempdir; + +use back::bytecode::RLIB_BYTECODE_EXTENSION; + +pub use llvm_util::target_features; + +use std::any::Any; +use std::path::PathBuf; +use std::sync::mpsc; +use std::collections::BTreeMap; +use rustc_data_structures::sync::Lrc; + +use rustc::dep_graph::DepGraph; +use rustc::hir::def_id::CrateNum; +use rustc::middle::cstore::MetadataLoader; +use rustc::middle::cstore::{NativeLibrary, CrateSource, LibSource}; +use rustc::middle::lang_items::LangItem; +use rustc::session::{Session, CompileIncomplete}; +use rustc::session::config::{OutputFilenames, OutputType, PrintRequest}; +use rustc::ty::{self, TyCtxt}; +use rustc::util::nodemap::{FxHashSet, FxHashMap}; +use rustc_mir::monomorphize; +use rustc_codegen_utils::codegen_backend::CodegenBackend; + +mod diagnostics; + +mod back { + pub use rustc_codegen_utils::symbol_names; + mod archive; + pub mod bytecode; + mod command; + pub mod linker; + pub mod link; + mod lto; + pub mod symbol_export; + pub mod write; + mod rpath; + mod wasm; +} + +mod abi; +mod allocator; +mod asm; +mod attributes; +mod base; +mod builder; +mod callee; +mod common; +mod consts; +mod context; +mod debuginfo; +mod declare; +mod glue; +mod intrinsic; +mod llvm_util; +mod metadata; +mod meth; +mod mir; +mod time_graph; +mod mono_item; +mod type_; +mod type_of; +mod value; + +pub struct LlvmCodegenBackend(()); + +impl !Send for LlvmCodegenBackend {} // Llvm is on a per-thread basis +impl !Sync for LlvmCodegenBackend {} + +impl LlvmCodegenBackend { + pub fn new() -> Box { + box LlvmCodegenBackend(()) + } +} + +impl CodegenBackend for LlvmCodegenBackend { + fn init(&self, sess: &Session) { + llvm_util::init(sess); // Make sure llvm is inited + } + + fn print(&self, req: PrintRequest, sess: &Session) { + match req { + PrintRequest::RelocationModels => { + println!("Available relocation models:"); + for &(name, _) in back::write::RELOC_MODEL_ARGS.iter() { + println!(" {}", name); + } + println!(""); + } + PrintRequest::CodeModels => { + println!("Available code models:"); + for &(name, _) in back::write::CODE_GEN_MODEL_ARGS.iter(){ + println!(" {}", name); + } + println!(""); + } + PrintRequest::TlsModels => { + println!("Available TLS models:"); + for &(name, _) in back::write::TLS_MODEL_ARGS.iter(){ + println!(" {}", name); + } + println!(""); + } + req => llvm_util::print(req, sess), + } + } + + fn print_passes(&self) { + llvm_util::print_passes(); + } + + fn print_version(&self) { + llvm_util::print_version(); + } + + fn diagnostics(&self) -> &[(&'static str, &'static str)] { + &DIAGNOSTICS + } + + fn target_features(&self, sess: &Session) -> Vec { + target_features(sess) + } + + fn metadata_loader(&self) -> Box { + box metadata::LlvmMetadataLoader + } + + fn provide(&self, providers: &mut ty::maps::Providers) { + back::symbol_names::provide(providers); + back::symbol_export::provide(providers); + base::provide(providers); + attributes::provide(providers); + } + + fn provide_extern(&self, providers: &mut ty::maps::Providers) { + back::symbol_export::provide_extern(providers); + base::provide_extern(providers); + attributes::provide_extern(providers); + } + + fn codegen_crate<'a, 'tcx>( + &self, + tcx: TyCtxt<'a, 'tcx, 'tcx>, + rx: mpsc::Receiver> + ) -> Box { + box base::codegen_crate(tcx, rx) + } + + fn join_codegen_and_link( + &self, + ongoing_codegen: Box, + sess: &Session, + dep_graph: &DepGraph, + outputs: &OutputFilenames, + ) -> Result<(), CompileIncomplete>{ + use rustc::util::common::time; + let (ongoing_codegen, work_products) = + ongoing_codegen.downcast::<::back::write::OngoingCodegen>() + .expect("Expected LlvmCodegenBackend's OngoingCodegen, found Box") + .join(sess); + if sess.opts.debugging_opts.incremental_info { + back::write::dump_incremental_data(&ongoing_codegen); + } + + time(sess, + "serialize work products", + move || rustc_incremental::save_work_product_index(sess, &dep_graph, work_products)); + + sess.compile_status()?; + + if !sess.opts.output_types.keys().any(|&i| i == OutputType::Exe || + i == OutputType::Metadata) { + return Ok(()); + } + + // Run the linker on any artifacts that resulted from the LLVM run. + // This should produce either a finished executable or library. + time(sess, "linking", || { + back::link::link_binary(sess, &ongoing_codegen, + outputs, &ongoing_codegen.crate_name.as_str()); + }); + + // Now that we won't touch anything in the incremental compilation directory + // any more, we can finalize it (which involves renaming it) + rustc_incremental::finalize_session_directory(sess, ongoing_codegen.link.crate_hash); + + Ok(()) + } +} + +/// This is the entrypoint for a hot plugged rustc_codegen_llvm +#[no_mangle] +pub fn __rustc_codegen_backend() -> Box { + LlvmCodegenBackend::new() +} + +struct ModuleCodegen { + /// The name of the module. When the crate may be saved between + /// compilations, incremental compilation requires that name be + /// unique amongst **all** crates. Therefore, it should contain + /// something unique to this crate (e.g., a module path) as well + /// as the crate name and disambiguator. + name: String, + llmod_id: String, + source: ModuleSource, + kind: ModuleKind, +} + +#[derive(Copy, Clone, Debug, PartialEq)] +enum ModuleKind { + Regular, + Metadata, + Allocator, +} + +impl ModuleCodegen { + fn llvm(&self) -> Option<&ModuleLlvm> { + match self.source { + ModuleSource::Codegened(ref llvm) => Some(llvm), + ModuleSource::Preexisting(_) => None, + } + } + + fn into_compiled_module(self, + emit_obj: bool, + emit_bc: bool, + emit_bc_compressed: bool, + outputs: &OutputFilenames) -> CompiledModule { + let pre_existing = match self.source { + ModuleSource::Preexisting(_) => true, + ModuleSource::Codegened(_) => false, + }; + let object = if emit_obj { + Some(outputs.temp_path(OutputType::Object, Some(&self.name))) + } else { + None + }; + let bytecode = if emit_bc { + Some(outputs.temp_path(OutputType::Bitcode, Some(&self.name))) + } else { + None + }; + let bytecode_compressed = if emit_bc_compressed { + Some(outputs.temp_path(OutputType::Bitcode, Some(&self.name)) + .with_extension(RLIB_BYTECODE_EXTENSION)) + } else { + None + }; + + CompiledModule { + llmod_id: self.llmod_id, + name: self.name.clone(), + kind: self.kind, + pre_existing, + object, + bytecode, + bytecode_compressed, + } + } +} + +#[derive(Debug)] +struct CompiledModule { + name: String, + llmod_id: String, + kind: ModuleKind, + pre_existing: bool, + object: Option, + bytecode: Option, + bytecode_compressed: Option, +} + +enum ModuleSource { + /// Copy the `.o` files or whatever from the incr. comp. directory. + Preexisting(WorkProduct), + + /// Rebuild from this LLVM module. + Codegened(ModuleLlvm), +} + +#[derive(Debug)] +struct ModuleLlvm { + llcx: llvm::ContextRef, + llmod: llvm::ModuleRef, + tm: llvm::TargetMachineRef, +} + +unsafe impl Send for ModuleLlvm { } +unsafe impl Sync for ModuleLlvm { } + +impl Drop for ModuleLlvm { + fn drop(&mut self) { + unsafe { + llvm::LLVMDisposeModule(self.llmod); + llvm::LLVMContextDispose(self.llcx); + llvm::LLVMRustDisposeTargetMachine(self.tm); + } + } +} + +struct CodegenResults { + crate_name: Symbol, + modules: Vec, + allocator_module: Option, + metadata_module: CompiledModule, + link: rustc::middle::cstore::LinkMeta, + metadata: rustc::middle::cstore::EncodedMetadata, + windows_subsystem: Option, + linker_info: back::linker::LinkerInfo, + crate_info: CrateInfo, +} + +// Misc info we load from metadata to persist beyond the tcx +struct CrateInfo { + panic_runtime: Option, + compiler_builtins: Option, + profiler_runtime: Option, + sanitizer_runtime: Option, + is_no_builtins: FxHashSet, + native_libraries: FxHashMap>>, + crate_name: FxHashMap, + used_libraries: Lrc>, + link_args: Lrc>, + used_crate_source: FxHashMap>, + used_crates_static: Vec<(CrateNum, LibSource)>, + used_crates_dynamic: Vec<(CrateNum, LibSource)>, + wasm_custom_sections: BTreeMap>, + wasm_imports: FxHashMap, + lang_item_to_crate: FxHashMap, + missing_lang_items: FxHashMap>, +} + +__build_diagnostic_array! { librustc_codegen_llvm, DIAGNOSTICS } diff --git a/src/librustc_codegen_llvm/llvm_util.rs b/src/librustc_codegen_llvm/llvm_util.rs new file mode 100644 index 00000000000..357b639e788 --- /dev/null +++ b/src/librustc_codegen_llvm/llvm_util.rs @@ -0,0 +1,255 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use syntax_pos::symbol::Symbol; +use back::write::create_target_machine; +use llvm; +use rustc::session::Session; +use rustc::session::config::PrintRequest; +use libc::c_int; +use std::ffi::CString; +use syntax::feature_gate::UnstableFeatures; + +use std::sync::atomic::{AtomicBool, Ordering}; +use std::sync::Once; + +static POISONED: AtomicBool = AtomicBool::new(false); +static INIT: Once = Once::new(); + +pub(crate) fn init(sess: &Session) { + unsafe { + // Before we touch LLVM, make sure that multithreading is enabled. + INIT.call_once(|| { + if llvm::LLVMStartMultithreaded() != 1 { + // use an extra bool to make sure that all future usage of LLVM + // cannot proceed despite the Once not running more than once. + POISONED.store(true, Ordering::SeqCst); + } + + configure_llvm(sess); + }); + + if POISONED.load(Ordering::SeqCst) { + bug!("couldn't enable multi-threaded LLVM"); + } + } +} + +fn require_inited() { + INIT.call_once(|| bug!("llvm is not initialized")); + if POISONED.load(Ordering::SeqCst) { + bug!("couldn't enable multi-threaded LLVM"); + } +} + +unsafe fn configure_llvm(sess: &Session) { + let mut llvm_c_strs = Vec::new(); + let mut llvm_args = Vec::new(); + + { + let mut add = |arg: &str| { + let s = CString::new(arg).unwrap(); + llvm_args.push(s.as_ptr()); + llvm_c_strs.push(s); + }; + add("rustc"); // fake program name + if sess.time_llvm_passes() { add("-time-passes"); } + if sess.print_llvm_passes() { add("-debug-pass=Structure"); } + if sess.opts.debugging_opts.disable_instrumentation_preinliner { + add("-disable-preinline"); + } + + for arg in &sess.opts.cg.llvm_args { + add(&(*arg)); + } + } + + llvm::LLVMInitializePasses(); + + llvm::initialize_available_targets(); + + llvm::LLVMRustSetLLVMOptions(llvm_args.len() as c_int, + llvm_args.as_ptr()); +} + +// WARNING: the features after applying `to_llvm_feature` must be known +// to LLVM or the feature detection code will walk past the end of the feature +// array, leading to crashes. + +const ARM_WHITELIST: &[(&str, Option<&str>)] = &[ + ("neon", Some("arm_target_feature")), + ("v7", Some("arm_target_feature")), + ("vfp2", Some("arm_target_feature")), + ("vfp3", Some("arm_target_feature")), + ("vfp4", Some("arm_target_feature")), +]; + +const AARCH64_WHITELIST: &[(&str, Option<&str>)] = &[ + ("fp", Some("aarch64_target_feature")), + ("neon", Some("aarch64_target_feature")), + ("sve", Some("aarch64_target_feature")), + ("crc", Some("aarch64_target_feature")), + ("crypto", Some("aarch64_target_feature")), + ("ras", Some("aarch64_target_feature")), + ("lse", Some("aarch64_target_feature")), + ("rdm", Some("aarch64_target_feature")), + ("fp16", Some("aarch64_target_feature")), + ("rcpc", Some("aarch64_target_feature")), + ("dotprod", Some("aarch64_target_feature")), + ("v8.1a", Some("aarch64_target_feature")), + ("v8.2a", Some("aarch64_target_feature")), + ("v8.3a", Some("aarch64_target_feature")), +]; + +const X86_WHITELIST: &[(&str, Option<&str>)] = &[ + ("aes", None), + ("avx", None), + ("avx2", None), + ("avx512bw", Some("avx512_target_feature")), + ("avx512cd", Some("avx512_target_feature")), + ("avx512dq", Some("avx512_target_feature")), + ("avx512er", Some("avx512_target_feature")), + ("avx512f", Some("avx512_target_feature")), + ("avx512ifma", Some("avx512_target_feature")), + ("avx512pf", Some("avx512_target_feature")), + ("avx512vbmi", Some("avx512_target_feature")), + ("avx512vl", Some("avx512_target_feature")), + ("avx512vpopcntdq", Some("avx512_target_feature")), + ("bmi1", None), + ("bmi2", None), + ("fma", None), + ("fxsr", None), + ("lzcnt", None), + ("mmx", Some("mmx_target_feature")), + ("pclmulqdq", None), + ("popcnt", None), + ("rdrand", None), + ("rdseed", None), + ("sha", None), + ("sse", None), + ("sse2", None), + ("sse3", None), + ("sse4.1", None), + ("sse4.2", None), + ("sse4a", Some("sse4a_target_feature")), + ("ssse3", None), + ("tbm", Some("tbm_target_feature")), + ("xsave", None), + ("xsavec", None), + ("xsaveopt", None), + ("xsaves", None), +]; + +const HEXAGON_WHITELIST: &[(&str, Option<&str>)] = &[ + ("hvx", Some("hexagon_target_feature")), + ("hvx-double", Some("hexagon_target_feature")), +]; + +const POWERPC_WHITELIST: &[(&str, Option<&str>)] = &[ + ("altivec", Some("powerpc_target_feature")), + ("power8-altivec", Some("powerpc_target_feature")), + ("power9-altivec", Some("powerpc_target_feature")), + ("power8-vector", Some("powerpc_target_feature")), + ("power9-vector", Some("powerpc_target_feature")), + ("vsx", Some("powerpc_target_feature")), +]; + +const MIPS_WHITELIST: &[(&str, Option<&str>)] = &[ + ("fp64", Some("mips_target_feature")), + ("msa", Some("mips_target_feature")), +]; + +/// When rustdoc is running, provide a list of all known features so that all their respective +/// primtives may be documented. +/// +/// IMPORTANT: If you're adding another whitelist to the above lists, make sure to add it to this +/// iterator! +pub fn all_known_features() -> impl Iterator)> { + ARM_WHITELIST.iter().cloned() + .chain(AARCH64_WHITELIST.iter().cloned()) + .chain(X86_WHITELIST.iter().cloned()) + .chain(HEXAGON_WHITELIST.iter().cloned()) + .chain(POWERPC_WHITELIST.iter().cloned()) + .chain(MIPS_WHITELIST.iter().cloned()) +} + +pub fn to_llvm_feature<'a>(sess: &Session, s: &'a str) -> &'a str { + let arch = if sess.target.target.arch == "x86_64" { + "x86" + } else { + &*sess.target.target.arch + }; + match (arch, s) { + ("x86", "pclmulqdq") => "pclmul", + ("x86", "rdrand") => "rdrnd", + ("x86", "bmi1") => "bmi", + ("aarch64", "fp") => "fp-armv8", + ("aarch64", "fp16") => "fullfp16", + (_, s) => s, + } +} + +pub fn target_features(sess: &Session) -> Vec { + let target_machine = create_target_machine(sess, true); + target_feature_whitelist(sess) + .iter() + .filter_map(|&(feature, gate)| { + if UnstableFeatures::from_environment().is_nightly_build() || gate.is_none() { + Some(feature) + } else { + None + } + }) + .filter(|feature| { + let llvm_feature = to_llvm_feature(sess, feature); + let cstr = CString::new(llvm_feature).unwrap(); + unsafe { llvm::LLVMRustHasFeature(target_machine, cstr.as_ptr()) } + }) + .map(|feature| Symbol::intern(feature)).collect() +} + +pub fn target_feature_whitelist(sess: &Session) + -> &'static [(&'static str, Option<&'static str>)] +{ + match &*sess.target.target.arch { + "arm" => ARM_WHITELIST, + "aarch64" => AARCH64_WHITELIST, + "x86" | "x86_64" => X86_WHITELIST, + "hexagon" => HEXAGON_WHITELIST, + "mips" | "mips64" => MIPS_WHITELIST, + "powerpc" | "powerpc64" => POWERPC_WHITELIST, + _ => &[], + } +} + +pub fn print_version() { + // Can be called without initializing LLVM + unsafe { + println!("LLVM version: {}.{}", + llvm::LLVMRustVersionMajor(), llvm::LLVMRustVersionMinor()); + } +} + +pub fn print_passes() { + // Can be called without initializing LLVM + unsafe { llvm::LLVMRustPrintPasses(); } +} + +pub(crate) fn print(req: PrintRequest, sess: &Session) { + require_inited(); + let tm = create_target_machine(sess, true); + unsafe { + match req { + PrintRequest::TargetCPUs => llvm::LLVMRustPrintTargetCPUs(tm), + PrintRequest::TargetFeatures => llvm::LLVMRustPrintTargetFeatures(tm), + _ => bug!("rustc_codegen_llvm can't handle print request: {:?}", req), + } + } +} diff --git a/src/librustc_codegen_llvm/metadata.rs b/src/librustc_codegen_llvm/metadata.rs new file mode 100644 index 00000000000..144baa65c1b --- /dev/null +++ b/src/librustc_codegen_llvm/metadata.rs @@ -0,0 +1,124 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use rustc::util::common; +use rustc::middle::cstore::MetadataLoader; +use rustc_target::spec::Target; +use llvm; +use llvm::{False, ObjectFile, mk_section_iter}; +use llvm::archive_ro::ArchiveRO; + +use rustc_data_structures::owning_ref::OwningRef; +use std::path::Path; +use std::ptr; +use std::slice; + +pub use rustc_data_structures::sync::MetadataRef; + +pub const METADATA_FILENAME: &str = "rust.metadata.bin"; + +pub struct LlvmMetadataLoader; + +impl MetadataLoader for LlvmMetadataLoader { + fn get_rlib_metadata(&self, _: &Target, filename: &Path) -> Result { + // Use ArchiveRO for speed here, it's backed by LLVM and uses mmap + // internally to read the file. We also avoid even using a memcpy by + // just keeping the archive along while the metadata is in use. + let archive = ArchiveRO::open(filename) + .map(|ar| OwningRef::new(box ar)) + .map_err(|e| { + debug!("llvm didn't like `{}`: {}", filename.display(), e); + format!("failed to read rlib metadata in '{}': {}", filename.display(), e) + })?; + let buf: OwningRef<_, [u8]> = archive + .try_map(|ar| { + ar.iter() + .filter_map(|s| s.ok()) + .find(|sect| sect.name() == Some(METADATA_FILENAME)) + .map(|s| s.data()) + .ok_or_else(|| { + debug!("didn't find '{}' in the archive", METADATA_FILENAME); + format!("failed to read rlib metadata: '{}'", + filename.display()) + }) + })?; + Ok(rustc_erase_owner!(buf)) + } + + fn get_dylib_metadata(&self, + target: &Target, + filename: &Path) + -> Result { + unsafe { + let buf = common::path2cstr(filename); + let mb = llvm::LLVMRustCreateMemoryBufferWithContentsOfFile(buf.as_ptr()); + if mb as isize == 0 { + return Err(format!("error reading library: '{}'", filename.display())); + } + let of = ObjectFile::new(mb) + .map(|of| OwningRef::new(box of)) + .ok_or_else(|| format!("provided path not an object file: '{}'", + filename.display()))?; + let buf = of.try_map(|of| search_meta_section(of, target, filename))?; + Ok(rustc_erase_owner!(buf)) + } + } +} + +fn search_meta_section<'a>(of: &'a ObjectFile, + target: &Target, + filename: &Path) + -> Result<&'a [u8], String> { + unsafe { + let si = mk_section_iter(of.llof); + while llvm::LLVMIsSectionIteratorAtEnd(of.llof, si.llsi) == False { + let mut name_buf = ptr::null(); + let name_len = llvm::LLVMRustGetSectionName(si.llsi, &mut name_buf); + let name = slice::from_raw_parts(name_buf as *const u8, name_len as usize).to_vec(); + let name = String::from_utf8(name).unwrap(); + debug!("get_metadata_section: name {}", name); + if read_metadata_section_name(target) == name { + let cbuf = llvm::LLVMGetSectionContents(si.llsi); + let csz = llvm::LLVMGetSectionSize(si.llsi) as usize; + // The buffer is valid while the object file is around + let buf: &'a [u8] = slice::from_raw_parts(cbuf as *const u8, csz); + return Ok(buf); + } + llvm::LLVMMoveToNextSection(si.llsi); + } + } + Err(format!("metadata not found: '{}'", filename.display())) +} + +pub fn metadata_section_name(target: &Target) -> &'static str { + // Historical note: + // + // When using link.exe it was seen that the section name `.note.rustc` + // was getting shortened to `.note.ru`, and according to the PE and COFF + // specification: + // + // > Executable images do not use a string table and do not support + // > section names longer than 8 characters + // + // https://msdn.microsoft.com/en-us/library/windows/hardware/gg463119.aspx + // + // As a result, we choose a slightly shorter name! As to why + // `.note.rustc` works on MinGW, that's another good question... + + if target.options.is_like_osx { + "__DATA,.rustc" + } else { + ".rustc" + } +} + +fn read_metadata_section_name(_target: &Target) -> &'static str { + ".rustc" +} diff --git a/src/librustc_codegen_llvm/meth.rs b/src/librustc_codegen_llvm/meth.rs new file mode 100644 index 00000000000..21bbdf31dcb --- /dev/null +++ b/src/librustc_codegen_llvm/meth.rs @@ -0,0 +1,115 @@ +// Copyright 2012 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use llvm::ValueRef; +use abi::{FnType, FnTypeExt}; +use callee; +use common::*; +use builder::Builder; +use consts; +use monomorphize; +use type_::Type; +use value::Value; +use rustc::ty::{self, Ty}; +use rustc::ty::layout::HasDataLayout; +use debuginfo; + +#[derive(Copy, Clone, Debug)] +pub struct VirtualIndex(u64); + +pub const DESTRUCTOR: VirtualIndex = VirtualIndex(0); +pub const SIZE: VirtualIndex = VirtualIndex(1); +pub const ALIGN: VirtualIndex = VirtualIndex(2); + +impl<'a, 'tcx> VirtualIndex { + pub fn from_index(index: usize) -> Self { + VirtualIndex(index as u64 + 3) + } + + pub fn get_fn(self, bx: &Builder<'a, 'tcx>, + llvtable: ValueRef, + fn_ty: &FnType<'tcx, Ty<'tcx>>) -> ValueRef { + // Load the data pointer from the object. + debug!("get_fn({:?}, {:?})", Value(llvtable), self); + + let llvtable = bx.pointercast(llvtable, fn_ty.llvm_type(bx.cx).ptr_to().ptr_to()); + let ptr_align = bx.tcx().data_layout.pointer_align; + let ptr = bx.load(bx.inbounds_gep(llvtable, &[C_usize(bx.cx, self.0)]), ptr_align); + bx.nonnull_metadata(ptr); + // Vtable loads are invariant + bx.set_invariant_load(ptr); + ptr + } + + pub fn get_usize(self, bx: &Builder<'a, 'tcx>, llvtable: ValueRef) -> ValueRef { + // Load the data pointer from the object. + debug!("get_int({:?}, {:?})", Value(llvtable), self); + + let llvtable = bx.pointercast(llvtable, Type::isize(bx.cx).ptr_to()); + let usize_align = bx.tcx().data_layout.pointer_align; + let ptr = bx.load(bx.inbounds_gep(llvtable, &[C_usize(bx.cx, self.0)]), usize_align); + // Vtable loads are invariant + bx.set_invariant_load(ptr); + ptr + } +} + +/// Creates a dynamic vtable for the given type and vtable origin. +/// This is used only for objects. +/// +/// The vtables are cached instead of created on every call. +/// +/// The `trait_ref` encodes the erased self type. Hence if we are +/// making an object `Foo` from a value of type `Foo`, then +/// `trait_ref` would map `T:Trait`. +pub fn get_vtable<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, + ty: Ty<'tcx>, + trait_ref: Option>) + -> ValueRef +{ + let tcx = cx.tcx; + + debug!("get_vtable(ty={:?}, trait_ref={:?})", ty, trait_ref); + + // Check the cache. + if let Some(&val) = cx.vtables.borrow().get(&(ty, trait_ref)) { + return val; + } + + // Not in the cache. Build it. + let nullptr = C_null(Type::i8p(cx)); + + let (size, align) = cx.size_and_align_of(ty); + let mut components: Vec<_> = [ + callee::get_fn(cx, monomorphize::resolve_drop_in_place(cx.tcx, ty)), + C_usize(cx, size.bytes()), + C_usize(cx, align.abi()) + ].iter().cloned().collect(); + + if let Some(trait_ref) = trait_ref { + let trait_ref = trait_ref.with_self_ty(tcx, ty); + let methods = tcx.vtable_methods(trait_ref); + let methods = methods.iter().cloned().map(|opt_mth| { + opt_mth.map_or(nullptr, |(def_id, substs)| { + callee::resolve_and_get_fn(cx, def_id, substs) + }) + }); + components.extend(methods); + } + + let vtable_const = C_struct(cx, &components, false); + let align = cx.data_layout().pointer_align; + let vtable = consts::addr_of(cx, vtable_const, align, "vtable"); + + debuginfo::create_vtable_metadata(cx, ty, vtable); + + cx.vtables.borrow_mut().insert((ty, trait_ref), vtable); + vtable +} diff --git a/src/librustc_codegen_llvm/mir/analyze.rs b/src/librustc_codegen_llvm/mir/analyze.rs new file mode 100644 index 00000000000..9e5298eb736 --- /dev/null +++ b/src/librustc_codegen_llvm/mir/analyze.rs @@ -0,0 +1,361 @@ +// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! An analysis to determine which locals require allocas and +//! which do not. + +use rustc_data_structures::bitvec::BitVector; +use rustc_data_structures::control_flow_graph::dominators::Dominators; +use rustc_data_structures::indexed_vec::{Idx, IndexVec}; +use rustc::mir::{self, Location, TerminatorKind}; +use rustc::mir::visit::{Visitor, PlaceContext}; +use rustc::mir::traversal; +use rustc::ty; +use rustc::ty::layout::LayoutOf; +use type_of::LayoutLlvmExt; +use super::FunctionCx; + +pub fn non_ssa_locals<'a, 'tcx>(fx: &FunctionCx<'a, 'tcx>) -> BitVector { + let mir = fx.mir; + let mut analyzer = LocalAnalyzer::new(fx); + + analyzer.visit_mir(mir); + + for (index, ty) in mir.local_decls.iter().map(|l| l.ty).enumerate() { + let ty = fx.monomorphize(&ty); + debug!("local {} has type {:?}", index, ty); + let layout = fx.cx.layout_of(ty); + if layout.is_llvm_immediate() { + // These sorts of types are immediates that we can store + // in an ValueRef without an alloca. + } else if layout.is_llvm_scalar_pair() { + // We allow pairs and uses of any of their 2 fields. + } else { + // These sorts of types require an alloca. Note that + // is_llvm_immediate() may *still* be true, particularly + // for newtypes, but we currently force some types + // (e.g. structs) into an alloca unconditionally, just so + // that we don't have to deal with having two pathways + // (gep vs extractvalue etc). + analyzer.not_ssa(mir::Local::new(index)); + } + } + + analyzer.non_ssa_locals +} + +struct LocalAnalyzer<'mir, 'a: 'mir, 'tcx: 'a> { + fx: &'mir FunctionCx<'a, 'tcx>, + dominators: Dominators, + non_ssa_locals: BitVector, + // The location of the first visited direct assignment to each + // local, or an invalid location (out of bounds `block` index). + first_assignment: IndexVec +} + +impl<'mir, 'a, 'tcx> LocalAnalyzer<'mir, 'a, 'tcx> { + fn new(fx: &'mir FunctionCx<'a, 'tcx>) -> LocalAnalyzer<'mir, 'a, 'tcx> { + let invalid_location = + mir::BasicBlock::new(fx.mir.basic_blocks().len()).start_location(); + let mut analyzer = LocalAnalyzer { + fx, + dominators: fx.mir.dominators(), + non_ssa_locals: BitVector::new(fx.mir.local_decls.len()), + first_assignment: IndexVec::from_elem(invalid_location, &fx.mir.local_decls) + }; + + // Arguments get assigned to by means of the function being called + for arg in fx.mir.args_iter() { + analyzer.first_assignment[arg] = mir::START_BLOCK.start_location(); + } + + analyzer + } + + fn first_assignment(&self, local: mir::Local) -> Option { + let location = self.first_assignment[local]; + if location.block.index() < self.fx.mir.basic_blocks().len() { + Some(location) + } else { + None + } + } + + fn not_ssa(&mut self, local: mir::Local) { + debug!("marking {:?} as non-SSA", local); + self.non_ssa_locals.insert(local.index()); + } + + fn assign(&mut self, local: mir::Local, location: Location) { + if self.first_assignment(local).is_some() { + self.not_ssa(local); + } else { + self.first_assignment[local] = location; + } + } +} + +impl<'mir, 'a, 'tcx> Visitor<'tcx> for LocalAnalyzer<'mir, 'a, 'tcx> { + fn visit_assign(&mut self, + block: mir::BasicBlock, + place: &mir::Place<'tcx>, + rvalue: &mir::Rvalue<'tcx>, + location: Location) { + debug!("visit_assign(block={:?}, place={:?}, rvalue={:?})", block, place, rvalue); + + if let mir::Place::Local(index) = *place { + self.assign(index, location); + if !self.fx.rvalue_creates_operand(rvalue) { + self.not_ssa(index); + } + } else { + self.visit_place(place, PlaceContext::Store, location); + } + + self.visit_rvalue(rvalue, location); + } + + fn visit_terminator_kind(&mut self, + block: mir::BasicBlock, + kind: &mir::TerminatorKind<'tcx>, + location: Location) { + let check = match *kind { + mir::TerminatorKind::Call { + func: mir::Operand::Constant(ref c), + ref args, .. + } => match c.ty.sty { + ty::TyFnDef(did, _) => Some((did, args)), + _ => None, + }, + _ => None, + }; + if let Some((def_id, args)) = check { + if Some(def_id) == self.fx.cx.tcx.lang_items().box_free_fn() { + // box_free(x) shares with `drop x` the property that it + // is not guaranteed to be statically dominated by the + // definition of x, so x must always be in an alloca. + if let mir::Operand::Move(ref place) = args[0] { + self.visit_place(place, PlaceContext::Drop, location); + } + } + } + + self.super_terminator_kind(block, kind, location); + } + + fn visit_place(&mut self, + place: &mir::Place<'tcx>, + context: PlaceContext<'tcx>, + location: Location) { + debug!("visit_place(place={:?}, context={:?})", place, context); + let cx = self.fx.cx; + + if let mir::Place::Projection(ref proj) = *place { + // Allow uses of projections that are ZSTs or from scalar fields. + let is_consume = match context { + PlaceContext::Copy | PlaceContext::Move => true, + _ => false + }; + if is_consume { + let base_ty = proj.base.ty(self.fx.mir, cx.tcx); + let base_ty = self.fx.monomorphize(&base_ty); + + // ZSTs don't require any actual memory access. + let elem_ty = base_ty.projection_ty(cx.tcx, &proj.elem).to_ty(cx.tcx); + let elem_ty = self.fx.monomorphize(&elem_ty); + if cx.layout_of(elem_ty).is_zst() { + return; + } + + if let mir::ProjectionElem::Field(..) = proj.elem { + let layout = cx.layout_of(base_ty.to_ty(cx.tcx)); + if layout.is_llvm_immediate() || layout.is_llvm_scalar_pair() { + // Recurse with the same context, instead of `Projection`, + // potentially stopping at non-operand projections, + // which would trigger `not_ssa` on locals. + self.visit_place(&proj.base, context, location); + return; + } + } + } + + // A deref projection only reads the pointer, never needs the place. + if let mir::ProjectionElem::Deref = proj.elem { + return self.visit_place(&proj.base, PlaceContext::Copy, location); + } + } + + self.super_place(place, context, location); + } + + fn visit_local(&mut self, + &local: &mir::Local, + context: PlaceContext<'tcx>, + location: Location) { + match context { + PlaceContext::Call => { + self.assign(local, location); + } + + PlaceContext::StorageLive | + PlaceContext::StorageDead | + PlaceContext::Validate => {} + + PlaceContext::Copy | + PlaceContext::Move => { + // Reads from uninitialized variables (e.g. in dead code, after + // optimizations) require locals to be in (uninitialized) memory. + // NB: there can be uninitialized reads of a local visited after + // an assignment to that local, if they happen on disjoint paths. + let ssa_read = match self.first_assignment(local) { + Some(assignment_location) => { + assignment_location.dominates(location, &self.dominators) + } + None => false + }; + if !ssa_read { + self.not_ssa(local); + } + } + + PlaceContext::Inspect | + PlaceContext::Store | + PlaceContext::AsmOutput | + PlaceContext::Borrow { .. } | + PlaceContext::Projection(..) => { + self.not_ssa(local); + } + + PlaceContext::Drop => { + let ty = mir::Place::Local(local).ty(self.fx.mir, self.fx.cx.tcx); + let ty = self.fx.monomorphize(&ty.to_ty(self.fx.cx.tcx)); + + // Only need the place if we're actually dropping it. + if self.fx.cx.type_needs_drop(ty) { + self.not_ssa(local); + } + } + } + } +} + +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub enum CleanupKind { + NotCleanup, + Funclet, + Internal { funclet: mir::BasicBlock } +} + +impl CleanupKind { + pub fn funclet_bb(self, for_bb: mir::BasicBlock) -> Option { + match self { + CleanupKind::NotCleanup => None, + CleanupKind::Funclet => Some(for_bb), + CleanupKind::Internal { funclet } => Some(funclet), + } + } +} + +pub fn cleanup_kinds<'a, 'tcx>(mir: &mir::Mir<'tcx>) -> IndexVec { + fn discover_masters<'tcx>(result: &mut IndexVec, + mir: &mir::Mir<'tcx>) { + for (bb, data) in mir.basic_blocks().iter_enumerated() { + match data.terminator().kind { + TerminatorKind::Goto { .. } | + TerminatorKind::Resume | + TerminatorKind::Abort | + TerminatorKind::Return | + TerminatorKind::GeneratorDrop | + TerminatorKind::Unreachable | + TerminatorKind::SwitchInt { .. } | + TerminatorKind::Yield { .. } | + TerminatorKind::FalseEdges { .. } | + TerminatorKind::FalseUnwind { .. } => { + /* nothing to do */ + } + TerminatorKind::Call { cleanup: unwind, .. } | + TerminatorKind::Assert { cleanup: unwind, .. } | + TerminatorKind::DropAndReplace { unwind, .. } | + TerminatorKind::Drop { unwind, .. } => { + if let Some(unwind) = unwind { + debug!("cleanup_kinds: {:?}/{:?} registering {:?} as funclet", + bb, data, unwind); + result[unwind] = CleanupKind::Funclet; + } + } + } + } + } + + fn propagate<'tcx>(result: &mut IndexVec, + mir: &mir::Mir<'tcx>) { + let mut funclet_succs = IndexVec::from_elem(None, mir.basic_blocks()); + + let mut set_successor = |funclet: mir::BasicBlock, succ| { + match funclet_succs[funclet] { + ref mut s @ None => { + debug!("set_successor: updating successor of {:?} to {:?}", + funclet, succ); + *s = Some(succ); + }, + Some(s) => if s != succ { + span_bug!(mir.span, "funclet {:?} has 2 parents - {:?} and {:?}", + funclet, s, succ); + } + } + }; + + for (bb, data) in traversal::reverse_postorder(mir) { + let funclet = match result[bb] { + CleanupKind::NotCleanup => continue, + CleanupKind::Funclet => bb, + CleanupKind::Internal { funclet } => funclet, + }; + + debug!("cleanup_kinds: {:?}/{:?}/{:?} propagating funclet {:?}", + bb, data, result[bb], funclet); + + for &succ in data.terminator().successors() { + let kind = result[succ]; + debug!("cleanup_kinds: propagating {:?} to {:?}/{:?}", + funclet, succ, kind); + match kind { + CleanupKind::NotCleanup => { + result[succ] = CleanupKind::Internal { funclet: funclet }; + } + CleanupKind::Funclet => { + if funclet != succ { + set_successor(funclet, succ); + } + } + CleanupKind::Internal { funclet: succ_funclet } => { + if funclet != succ_funclet { + // `succ` has 2 different funclet going into it, so it must + // be a funclet by itself. + + debug!("promoting {:?} to a funclet and updating {:?}", succ, + succ_funclet); + result[succ] = CleanupKind::Funclet; + set_successor(succ_funclet, succ); + set_successor(funclet, succ); + } + } + } + } + } + } + + let mut result = IndexVec::from_elem(CleanupKind::NotCleanup, mir.basic_blocks()); + + discover_masters(&mut result, mir); + propagate(&mut result, mir); + debug!("cleanup_kinds: result={:?}", result); + result +} diff --git a/src/librustc_codegen_llvm/mir/block.rs b/src/librustc_codegen_llvm/mir/block.rs new file mode 100644 index 00000000000..556baeba39e --- /dev/null +++ b/src/librustc_codegen_llvm/mir/block.rs @@ -0,0 +1,895 @@ +// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use llvm::{self, ValueRef, BasicBlockRef}; +use rustc::middle::lang_items; +use rustc::ty::{self, Ty, TypeFoldable}; +use rustc::ty::layout::{self, LayoutOf}; +use rustc::mir; +use rustc::mir::interpret::EvalErrorKind; +use abi::{Abi, ArgType, ArgTypeExt, FnType, FnTypeExt, LlvmType, PassMode}; +use base; +use callee; +use builder::{Builder, MemFlags}; +use common::{self, C_bool, C_str_slice, C_struct, C_u32, C_uint_big, C_undef}; +use consts; +use meth; +use monomorphize; +use type_of::LayoutLlvmExt; +use type_::Type; + +use syntax::symbol::Symbol; +use syntax_pos::Pos; + +use super::{FunctionCx, LocalRef}; +use super::place::PlaceRef; +use super::operand::OperandRef; +use super::operand::OperandValue::{Pair, Ref, Immediate}; + +impl<'a, 'tcx> FunctionCx<'a, 'tcx> { + pub fn codegen_block(&mut self, bb: mir::BasicBlock) { + let mut bx = self.build_block(bb); + let data = &self.mir[bb]; + + debug!("codegen_block({:?}={:?})", bb, data); + + for statement in &data.statements { + bx = self.codegen_statement(bx, statement); + } + + self.codegen_terminator(bx, bb, data.terminator()); + } + + fn codegen_terminator(&mut self, + mut bx: Builder<'a, 'tcx>, + bb: mir::BasicBlock, + terminator: &mir::Terminator<'tcx>) + { + debug!("codegen_terminator: {:?}", terminator); + + // Create the cleanup bundle, if needed. + let tcx = bx.tcx(); + let span = terminator.source_info.span; + let funclet_bb = self.cleanup_kinds[bb].funclet_bb(bb); + let funclet = funclet_bb.and_then(|funclet_bb| self.funclets[funclet_bb].as_ref()); + + let cleanup_pad = funclet.map(|lp| lp.cleanuppad()); + let cleanup_bundle = funclet.map(|l| l.bundle()); + + let lltarget = |this: &mut Self, target: mir::BasicBlock| { + let lltarget = this.blocks[target]; + let target_funclet = this.cleanup_kinds[target].funclet_bb(target); + match (funclet_bb, target_funclet) { + (None, None) => (lltarget, false), + (Some(f), Some(t_f)) + if f == t_f || !base::wants_msvc_seh(tcx.sess) + => (lltarget, false), + (None, Some(_)) => { + // jump *into* cleanup - need a landing pad if GNU + (this.landing_pad_to(target), false) + } + (Some(_), None) => span_bug!(span, "{:?} - jump out of cleanup?", terminator), + (Some(_), Some(_)) => { + (this.landing_pad_to(target), true) + } + } + }; + + let llblock = |this: &mut Self, target: mir::BasicBlock| { + let (lltarget, is_cleanupret) = lltarget(this, target); + if is_cleanupret { + // MSVC cross-funclet jump - need a trampoline + + debug!("llblock: creating cleanup trampoline for {:?}", target); + let name = &format!("{:?}_cleanup_trampoline_{:?}", bb, target); + let trampoline = this.new_block(name); + trampoline.cleanup_ret(cleanup_pad.unwrap(), Some(lltarget)); + trampoline.llbb() + } else { + lltarget + } + }; + + let funclet_br = |this: &mut Self, bx: Builder, target: mir::BasicBlock| { + let (lltarget, is_cleanupret) = lltarget(this, target); + if is_cleanupret { + // micro-optimization: generate a `ret` rather than a jump + // to a trampoline. + bx.cleanup_ret(cleanup_pad.unwrap(), Some(lltarget)); + } else { + bx.br(lltarget); + } + }; + + let do_call = | + this: &mut Self, + bx: Builder<'a, 'tcx>, + fn_ty: FnType<'tcx, Ty<'tcx>>, + fn_ptr: ValueRef, + llargs: &[ValueRef], + destination: Option<(ReturnDest<'tcx>, mir::BasicBlock)>, + cleanup: Option + | { + if let Some(cleanup) = cleanup { + let ret_bx = if let Some((_, target)) = destination { + this.blocks[target] + } else { + this.unreachable_block() + }; + let invokeret = bx.invoke(fn_ptr, + &llargs, + ret_bx, + llblock(this, cleanup), + cleanup_bundle); + fn_ty.apply_attrs_callsite(&bx, invokeret); + + if let Some((ret_dest, target)) = destination { + let ret_bx = this.build_block(target); + this.set_debug_loc(&ret_bx, terminator.source_info); + this.store_return(&ret_bx, ret_dest, &fn_ty.ret, invokeret); + } + } else { + let llret = bx.call(fn_ptr, &llargs, cleanup_bundle); + fn_ty.apply_attrs_callsite(&bx, llret); + if this.mir[bb].is_cleanup { + // Cleanup is always the cold path. Don't inline + // drop glue. Also, when there is a deeply-nested + // struct, there are "symmetry" issues that cause + // exponential inlining - see issue #41696. + llvm::Attribute::NoInline.apply_callsite(llvm::AttributePlace::Function, llret); + } + + if let Some((ret_dest, target)) = destination { + this.store_return(&bx, ret_dest, &fn_ty.ret, llret); + funclet_br(this, bx, target); + } else { + bx.unreachable(); + } + } + }; + + self.set_debug_loc(&bx, terminator.source_info); + match terminator.kind { + mir::TerminatorKind::Resume => { + if let Some(cleanup_pad) = cleanup_pad { + bx.cleanup_ret(cleanup_pad, None); + } else { + let slot = self.get_personality_slot(&bx); + let lp0 = slot.project_field(&bx, 0).load(&bx).immediate(); + let lp1 = slot.project_field(&bx, 1).load(&bx).immediate(); + slot.storage_dead(&bx); + + if !bx.sess().target.target.options.custom_unwind_resume { + let mut lp = C_undef(self.landing_pad_type()); + lp = bx.insert_value(lp, lp0, 0); + lp = bx.insert_value(lp, lp1, 1); + bx.resume(lp); + } else { + bx.call(bx.cx.eh_unwind_resume(), &[lp0], cleanup_bundle); + bx.unreachable(); + } + } + } + + mir::TerminatorKind::Abort => { + // Call core::intrinsics::abort() + let fnname = bx.cx.get_intrinsic(&("llvm.trap")); + bx.call(fnname, &[], None); + bx.unreachable(); + } + + mir::TerminatorKind::Goto { target } => { + funclet_br(self, bx, target); + } + + mir::TerminatorKind::SwitchInt { ref discr, switch_ty, ref values, ref targets } => { + let discr = self.codegen_operand(&bx, discr); + if switch_ty == bx.tcx().types.bool { + let lltrue = llblock(self, targets[0]); + let llfalse = llblock(self, targets[1]); + if let [0] = values[..] { + bx.cond_br(discr.immediate(), llfalse, lltrue); + } else { + assert_eq!(&values[..], &[1]); + bx.cond_br(discr.immediate(), lltrue, llfalse); + } + } else { + let (otherwise, targets) = targets.split_last().unwrap(); + let switch = bx.switch(discr.immediate(), + llblock(self, *otherwise), values.len()); + let switch_llty = bx.cx.layout_of(switch_ty).immediate_llvm_type(bx.cx); + for (&value, target) in values.iter().zip(targets) { + let llval = C_uint_big(switch_llty, value); + let llbb = llblock(self, *target); + bx.add_case(switch, llval, llbb) + } + } + } + + mir::TerminatorKind::Return => { + let llval = match self.fn_ty.ret.mode { + PassMode::Ignore | PassMode::Indirect(_) => { + bx.ret_void(); + return; + } + + PassMode::Direct(_) | PassMode::Pair(..) => { + let op = self.codegen_consume(&bx, &mir::Place::Local(mir::RETURN_PLACE)); + if let Ref(llval, align) = op.val { + bx.load(llval, align) + } else { + op.immediate_or_packed_pair(&bx) + } + } + + PassMode::Cast(cast_ty) => { + let op = match self.locals[mir::RETURN_PLACE] { + LocalRef::Operand(Some(op)) => op, + LocalRef::Operand(None) => bug!("use of return before def"), + LocalRef::Place(cg_place) => { + OperandRef { + val: Ref(cg_place.llval, cg_place.align), + layout: cg_place.layout + } + } + }; + let llslot = match op.val { + Immediate(_) | Pair(..) => { + let scratch = PlaceRef::alloca(&bx, self.fn_ty.ret.layout, "ret"); + op.val.store(&bx, scratch); + scratch.llval + } + Ref(llval, align) => { + assert_eq!(align.abi(), op.layout.align.abi(), + "return place is unaligned!"); + llval + } + }; + bx.load( + bx.pointercast(llslot, cast_ty.llvm_type(bx.cx).ptr_to()), + self.fn_ty.ret.layout.align) + } + }; + bx.ret(llval); + } + + mir::TerminatorKind::Unreachable => { + bx.unreachable(); + } + + mir::TerminatorKind::Drop { ref location, target, unwind } => { + let ty = location.ty(self.mir, bx.tcx()).to_ty(bx.tcx()); + let ty = self.monomorphize(&ty); + let drop_fn = monomorphize::resolve_drop_in_place(bx.cx.tcx, ty); + + if let ty::InstanceDef::DropGlue(_, None) = drop_fn.def { + // we don't actually need to drop anything. + funclet_br(self, bx, target); + return + } + + let place = self.codegen_place(&bx, location); + let mut args: &[_] = &[place.llval, place.llextra]; + args = &args[..1 + place.has_extra() as usize]; + let (drop_fn, fn_ty) = match ty.sty { + ty::TyDynamic(..) => { + let fn_ty = drop_fn.ty(bx.cx.tcx); + let sig = common::ty_fn_sig(bx.cx, fn_ty); + let sig = bx.tcx().normalize_erasing_late_bound_regions( + ty::ParamEnv::reveal_all(), + &sig, + ); + let fn_ty = FnType::new_vtable(bx.cx, sig, &[]); + args = &args[..1]; + (meth::DESTRUCTOR.get_fn(&bx, place.llextra, &fn_ty), fn_ty) + } + _ => { + (callee::get_fn(bx.cx, drop_fn), + FnType::of_instance(bx.cx, &drop_fn)) + } + }; + do_call(self, bx, fn_ty, drop_fn, args, + Some((ReturnDest::Nothing, target)), + unwind); + } + + mir::TerminatorKind::Assert { ref cond, expected, ref msg, target, cleanup } => { + let cond = self.codegen_operand(&bx, cond).immediate(); + let mut const_cond = common::const_to_opt_u128(cond, false).map(|c| c == 1); + + // This case can currently arise only from functions marked + // with #[rustc_inherit_overflow_checks] and inlined from + // another crate (mostly core::num generic/#[inline] fns), + // while the current crate doesn't use overflow checks. + // NOTE: Unlike binops, negation doesn't have its own + // checked operation, just a comparison with the minimum + // value, so we have to check for the assert message. + if !bx.cx.check_overflow { + if let mir::interpret::EvalErrorKind::OverflowNeg = *msg { + const_cond = Some(expected); + } + } + + // Don't codegen the panic block if success if known. + if const_cond == Some(expected) { + funclet_br(self, bx, target); + return; + } + + // Pass the condition through llvm.expect for branch hinting. + let expect = bx.cx.get_intrinsic(&"llvm.expect.i1"); + let cond = bx.call(expect, &[cond, C_bool(bx.cx, expected)], None); + + // Create the failure block and the conditional branch to it. + let lltarget = llblock(self, target); + let panic_block = self.new_block("panic"); + if expected { + bx.cond_br(cond, lltarget, panic_block.llbb()); + } else { + bx.cond_br(cond, panic_block.llbb(), lltarget); + } + + // After this point, bx is the block for the call to panic. + bx = panic_block; + self.set_debug_loc(&bx, terminator.source_info); + + // Get the location information. + let loc = bx.sess().codemap().lookup_char_pos(span.lo()); + let filename = Symbol::intern(&loc.file.name.to_string()).as_str(); + let filename = C_str_slice(bx.cx, filename); + let line = C_u32(bx.cx, loc.line as u32); + let col = C_u32(bx.cx, loc.col.to_usize() as u32 + 1); + let align = tcx.data_layout.aggregate_align + .max(tcx.data_layout.i32_align) + .max(tcx.data_layout.pointer_align); + + // Put together the arguments to the panic entry point. + let (lang_item, args) = match *msg { + EvalErrorKind::BoundsCheck { ref len, ref index } => { + let len = self.codegen_operand(&mut bx, len).immediate(); + let index = self.codegen_operand(&mut bx, index).immediate(); + + let file_line_col = C_struct(bx.cx, &[filename, line, col], false); + let file_line_col = consts::addr_of(bx.cx, + file_line_col, + align, + "panic_bounds_check_loc"); + (lang_items::PanicBoundsCheckFnLangItem, + vec![file_line_col, index, len]) + } + _ => { + let str = msg.description(); + let msg_str = Symbol::intern(str).as_str(); + let msg_str = C_str_slice(bx.cx, msg_str); + let msg_file_line_col = C_struct(bx.cx, + &[msg_str, filename, line, col], + false); + let msg_file_line_col = consts::addr_of(bx.cx, + msg_file_line_col, + align, + "panic_loc"); + (lang_items::PanicFnLangItem, + vec![msg_file_line_col]) + } + }; + + // Obtain the panic entry point. + let def_id = common::langcall(bx.tcx(), Some(span), "", lang_item); + let instance = ty::Instance::mono(bx.tcx(), def_id); + let fn_ty = FnType::of_instance(bx.cx, &instance); + let llfn = callee::get_fn(bx.cx, instance); + + // Codegen the actual panic invoke/call. + do_call(self, bx, fn_ty, llfn, &args, None, cleanup); + } + + mir::TerminatorKind::DropAndReplace { .. } => { + bug!("undesugared DropAndReplace in codegen: {:?}", terminator); + } + + mir::TerminatorKind::Call { ref func, ref args, ref destination, cleanup } => { + // Create the callee. This is a fn ptr or zero-sized and hence a kind of scalar. + let callee = self.codegen_operand(&bx, func); + + let (instance, mut llfn) = match callee.layout.ty.sty { + ty::TyFnDef(def_id, substs) => { + (Some(ty::Instance::resolve(bx.cx.tcx, + ty::ParamEnv::reveal_all(), + def_id, + substs).unwrap()), + None) + } + ty::TyFnPtr(_) => { + (None, Some(callee.immediate())) + } + _ => bug!("{} is not callable", callee.layout.ty) + }; + let def = instance.map(|i| i.def); + let sig = callee.layout.ty.fn_sig(bx.tcx()); + let sig = bx.tcx().normalize_erasing_late_bound_regions( + ty::ParamEnv::reveal_all(), + &sig, + ); + let abi = sig.abi; + + // Handle intrinsics old codegen wants Expr's for, ourselves. + let intrinsic = match def { + Some(ty::InstanceDef::Intrinsic(def_id)) + => Some(bx.tcx().item_name(def_id).as_str()), + _ => None + }; + let intrinsic = intrinsic.as_ref().map(|s| &s[..]); + + if intrinsic == Some("transmute") { + let &(ref dest, target) = destination.as_ref().unwrap(); + self.codegen_transmute(&bx, &args[0], dest); + funclet_br(self, bx, target); + return; + } + + let extra_args = &args[sig.inputs().len()..]; + let extra_args = extra_args.iter().map(|op_arg| { + let op_ty = op_arg.ty(self.mir, bx.tcx()); + self.monomorphize(&op_ty) + }).collect::>(); + + let fn_ty = match def { + Some(ty::InstanceDef::Virtual(..)) => { + FnType::new_vtable(bx.cx, sig, &extra_args) + } + Some(ty::InstanceDef::DropGlue(_, None)) => { + // empty drop glue - a nop. + let &(_, target) = destination.as_ref().unwrap(); + funclet_br(self, bx, target); + return; + } + _ => FnType::new(bx.cx, sig, &extra_args) + }; + + // The arguments we'll be passing. Plus one to account for outptr, if used. + let arg_count = fn_ty.args.len() + fn_ty.ret.is_indirect() as usize; + let mut llargs = Vec::with_capacity(arg_count); + + // Prepare the return value destination + let ret_dest = if let Some((ref dest, _)) = *destination { + let is_intrinsic = intrinsic.is_some(); + self.make_return_dest(&bx, dest, &fn_ty.ret, &mut llargs, + is_intrinsic) + } else { + ReturnDest::Nothing + }; + + if intrinsic.is_some() && intrinsic != Some("drop_in_place") { + use intrinsic::codegen_intrinsic_call; + + let dest = match ret_dest { + _ if fn_ty.ret.is_indirect() => llargs[0], + ReturnDest::Nothing => { + C_undef(fn_ty.ret.memory_ty(bx.cx).ptr_to()) + } + ReturnDest::IndirectOperand(dst, _) | + ReturnDest::Store(dst) => dst.llval, + ReturnDest::DirectOperand(_) => + bug!("Cannot use direct operand with an intrinsic call") + }; + + let args: Vec<_> = args.iter().enumerate().map(|(i, arg)| { + // The indices passed to simd_shuffle* in the + // third argument must be constant. This is + // checked by const-qualification, which also + // promotes any complex rvalues to constants. + if i == 2 && intrinsic.unwrap().starts_with("simd_shuffle") { + match *arg { + mir::Operand::Copy(_) | + mir::Operand::Move(_) => { + span_bug!(span, "shuffle indices must be constant"); + } + mir::Operand::Constant(ref constant) => { + let (llval, ty) = self.simd_shuffle_indices( + &bx, + constant, + ); + return OperandRef { + val: Immediate(llval), + layout: bx.cx.layout_of(ty) + }; + } + } + } + + self.codegen_operand(&bx, arg) + }).collect(); + + + let callee_ty = instance.as_ref().unwrap().ty(bx.cx.tcx); + codegen_intrinsic_call(&bx, callee_ty, &fn_ty, &args, dest, + terminator.source_info.span); + + if let ReturnDest::IndirectOperand(dst, _) = ret_dest { + self.store_return(&bx, ret_dest, &fn_ty.ret, dst.llval); + } + + if let Some((_, target)) = *destination { + funclet_br(self, bx, target); + } else { + bx.unreachable(); + } + + return; + } + + // Split the rust-call tupled arguments off. + let (first_args, untuple) = if abi == Abi::RustCall && !args.is_empty() { + let (tup, args) = args.split_last().unwrap(); + (args, Some(tup)) + } else { + (&args[..], None) + }; + + for (i, arg) in first_args.iter().enumerate() { + let mut op = self.codegen_operand(&bx, arg); + if let (0, Some(ty::InstanceDef::Virtual(_, idx))) = (i, def) { + if let Pair(data_ptr, meta) = op.val { + llfn = Some(meth::VirtualIndex::from_index(idx) + .get_fn(&bx, meta, &fn_ty)); + llargs.push(data_ptr); + continue; + } + } + + // The callee needs to own the argument memory if we pass it + // by-ref, so make a local copy of non-immediate constants. + match (arg, op.val) { + (&mir::Operand::Copy(_), Ref(..)) | + (&mir::Operand::Constant(_), Ref(..)) => { + let tmp = PlaceRef::alloca(&bx, op.layout, "const"); + op.val.store(&bx, tmp); + op.val = Ref(tmp.llval, tmp.align); + } + _ => {} + } + + self.codegen_argument(&bx, op, &mut llargs, &fn_ty.args[i]); + } + if let Some(tup) = untuple { + self.codegen_arguments_untupled(&bx, tup, &mut llargs, + &fn_ty.args[first_args.len()..]) + } + + let fn_ptr = match (llfn, instance) { + (Some(llfn), _) => llfn, + (None, Some(instance)) => callee::get_fn(bx.cx, instance), + _ => span_bug!(span, "no llfn for call"), + }; + + do_call(self, bx, fn_ty, fn_ptr, &llargs, + destination.as_ref().map(|&(_, target)| (ret_dest, target)), + cleanup); + } + mir::TerminatorKind::GeneratorDrop | + mir::TerminatorKind::Yield { .. } => bug!("generator ops in codegen"), + mir::TerminatorKind::FalseEdges { .. } | + mir::TerminatorKind::FalseUnwind { .. } => bug!("borrowck false edges in codegen"), + } + } + + fn codegen_argument(&mut self, + bx: &Builder<'a, 'tcx>, + op: OperandRef<'tcx>, + llargs: &mut Vec, + arg: &ArgType<'tcx, Ty<'tcx>>) { + // Fill padding with undef value, where applicable. + if let Some(ty) = arg.pad { + llargs.push(C_undef(ty.llvm_type(bx.cx))); + } + + if arg.is_ignore() { + return; + } + + if let PassMode::Pair(..) = arg.mode { + match op.val { + Pair(a, b) => { + llargs.push(a); + llargs.push(b); + return; + } + _ => bug!("codegen_argument: {:?} invalid for pair arugment", op) + } + } + + // Force by-ref if we have to load through a cast pointer. + let (mut llval, align, by_ref) = match op.val { + Immediate(_) | Pair(..) => { + match arg.mode { + PassMode::Indirect(_) | PassMode::Cast(_) => { + let scratch = PlaceRef::alloca(bx, arg.layout, "arg"); + op.val.store(bx, scratch); + (scratch.llval, scratch.align, true) + } + _ => { + (op.immediate_or_packed_pair(bx), arg.layout.align, false) + } + } + } + Ref(llval, align) => { + if arg.is_indirect() && align.abi() < arg.layout.align.abi() { + // `foo(packed.large_field)`. We can't pass the (unaligned) field directly. I + // think that ATM (Rust 1.16) we only pass temporaries, but we shouldn't + // have scary latent bugs around. + + let scratch = PlaceRef::alloca(bx, arg.layout, "arg"); + base::memcpy_ty(bx, scratch.llval, llval, op.layout, align, MemFlags::empty()); + (scratch.llval, scratch.align, true) + } else { + (llval, align, true) + } + } + }; + + if by_ref && !arg.is_indirect() { + // Have to load the argument, maybe while casting it. + if let PassMode::Cast(ty) = arg.mode { + llval = bx.load(bx.pointercast(llval, ty.llvm_type(bx.cx).ptr_to()), + align.min(arg.layout.align)); + } else { + // We can't use `PlaceRef::load` here because the argument + // may have a type we don't treat as immediate, but the ABI + // used for this call is passing it by-value. In that case, + // the load would just produce `OperandValue::Ref` instead + // of the `OperandValue::Immediate` we need for the call. + llval = bx.load(llval, align); + if let layout::Abi::Scalar(ref scalar) = arg.layout.abi { + if scalar.is_bool() { + bx.range_metadata(llval, 0..2); + } + } + // We store bools as i8 so we need to truncate to i1. + llval = base::to_immediate(bx, llval, arg.layout); + } + } + + llargs.push(llval); + } + + fn codegen_arguments_untupled(&mut self, + bx: &Builder<'a, 'tcx>, + operand: &mir::Operand<'tcx>, + llargs: &mut Vec, + args: &[ArgType<'tcx, Ty<'tcx>>]) { + let tuple = self.codegen_operand(bx, operand); + + // Handle both by-ref and immediate tuples. + if let Ref(llval, align) = tuple.val { + let tuple_ptr = PlaceRef::new_sized(llval, tuple.layout, align); + for i in 0..tuple.layout.fields.count() { + let field_ptr = tuple_ptr.project_field(bx, i); + self.codegen_argument(bx, field_ptr.load(bx), llargs, &args[i]); + } + } else { + // If the tuple is immediate, the elements are as well. + for i in 0..tuple.layout.fields.count() { + let op = tuple.extract_field(bx, i); + self.codegen_argument(bx, op, llargs, &args[i]); + } + } + } + + fn get_personality_slot(&mut self, bx: &Builder<'a, 'tcx>) -> PlaceRef<'tcx> { + let cx = bx.cx; + if let Some(slot) = self.personality_slot { + slot + } else { + let layout = cx.layout_of(cx.tcx.intern_tup(&[ + cx.tcx.mk_mut_ptr(cx.tcx.types.u8), + cx.tcx.types.i32 + ])); + let slot = PlaceRef::alloca(bx, layout, "personalityslot"); + self.personality_slot = Some(slot); + slot + } + } + + /// Return the landingpad wrapper around the given basic block + /// + /// No-op in MSVC SEH scheme. + fn landing_pad_to(&mut self, target_bb: mir::BasicBlock) -> BasicBlockRef { + if let Some(block) = self.landing_pads[target_bb] { + return block; + } + + let block = self.blocks[target_bb]; + let landing_pad = self.landing_pad_uncached(block); + self.landing_pads[target_bb] = Some(landing_pad); + landing_pad + } + + fn landing_pad_uncached(&mut self, target_bb: BasicBlockRef) -> BasicBlockRef { + if base::wants_msvc_seh(self.cx.sess()) { + span_bug!(self.mir.span, "landing pad was not inserted?") + } + + let bx = self.new_block("cleanup"); + + let llpersonality = self.cx.eh_personality(); + let llretty = self.landing_pad_type(); + let lp = bx.landing_pad(llretty, llpersonality, 1); + bx.set_cleanup(lp); + + let slot = self.get_personality_slot(&bx); + slot.storage_live(&bx); + Pair(bx.extract_value(lp, 0), bx.extract_value(lp, 1)).store(&bx, slot); + + bx.br(target_bb); + bx.llbb() + } + + fn landing_pad_type(&self) -> Type { + let cx = self.cx; + Type::struct_(cx, &[Type::i8p(cx), Type::i32(cx)], false) + } + + fn unreachable_block(&mut self) -> BasicBlockRef { + self.unreachable_block.unwrap_or_else(|| { + let bl = self.new_block("unreachable"); + bl.unreachable(); + self.unreachable_block = Some(bl.llbb()); + bl.llbb() + }) + } + + pub fn new_block(&self, name: &str) -> Builder<'a, 'tcx> { + Builder::new_block(self.cx, self.llfn, name) + } + + pub fn build_block(&self, bb: mir::BasicBlock) -> Builder<'a, 'tcx> { + let bx = Builder::with_cx(self.cx); + bx.position_at_end(self.blocks[bb]); + bx + } + + fn make_return_dest(&mut self, bx: &Builder<'a, 'tcx>, + dest: &mir::Place<'tcx>, fn_ret: &ArgType<'tcx, Ty<'tcx>>, + llargs: &mut Vec, is_intrinsic: bool) + -> ReturnDest<'tcx> { + // If the return is ignored, we can just return a do-nothing ReturnDest + if fn_ret.is_ignore() { + return ReturnDest::Nothing; + } + let dest = if let mir::Place::Local(index) = *dest { + match self.locals[index] { + LocalRef::Place(dest) => dest, + LocalRef::Operand(None) => { + // Handle temporary places, specifically Operand ones, as + // they don't have allocas + return if fn_ret.is_indirect() { + // Odd, but possible, case, we have an operand temporary, + // but the calling convention has an indirect return. + let tmp = PlaceRef::alloca(bx, fn_ret.layout, "tmp_ret"); + tmp.storage_live(bx); + llargs.push(tmp.llval); + ReturnDest::IndirectOperand(tmp, index) + } else if is_intrinsic { + // Currently, intrinsics always need a location to store + // the result. so we create a temporary alloca for the + // result + let tmp = PlaceRef::alloca(bx, fn_ret.layout, "tmp_ret"); + tmp.storage_live(bx); + ReturnDest::IndirectOperand(tmp, index) + } else { + ReturnDest::DirectOperand(index) + }; + } + LocalRef::Operand(Some(_)) => { + bug!("place local already assigned to"); + } + } + } else { + self.codegen_place(bx, dest) + }; + if fn_ret.is_indirect() { + if dest.align.abi() < dest.layout.align.abi() { + // Currently, MIR code generation does not create calls + // that store directly to fields of packed structs (in + // fact, the calls it creates write only to temps), + // + // If someone changes that, please update this code path + // to create a temporary. + span_bug!(self.mir.span, "can't directly store to unaligned value"); + } + llargs.push(dest.llval); + ReturnDest::Nothing + } else { + ReturnDest::Store(dest) + } + } + + fn codegen_transmute(&mut self, bx: &Builder<'a, 'tcx>, + src: &mir::Operand<'tcx>, + dst: &mir::Place<'tcx>) { + if let mir::Place::Local(index) = *dst { + match self.locals[index] { + LocalRef::Place(place) => self.codegen_transmute_into(bx, src, place), + LocalRef::Operand(None) => { + let dst_layout = bx.cx.layout_of(self.monomorphized_place_ty(dst)); + assert!(!dst_layout.ty.has_erasable_regions()); + let place = PlaceRef::alloca(bx, dst_layout, "transmute_temp"); + place.storage_live(bx); + self.codegen_transmute_into(bx, src, place); + let op = place.load(bx); + place.storage_dead(bx); + self.locals[index] = LocalRef::Operand(Some(op)); + } + LocalRef::Operand(Some(op)) => { + assert!(op.layout.is_zst(), + "assigning to initialized SSAtemp"); + } + } + } else { + let dst = self.codegen_place(bx, dst); + self.codegen_transmute_into(bx, src, dst); + } + } + + fn codegen_transmute_into(&mut self, bx: &Builder<'a, 'tcx>, + src: &mir::Operand<'tcx>, + dst: PlaceRef<'tcx>) { + let src = self.codegen_operand(bx, src); + let llty = src.layout.llvm_type(bx.cx); + let cast_ptr = bx.pointercast(dst.llval, llty.ptr_to()); + let align = src.layout.align.min(dst.layout.align); + src.val.store(bx, PlaceRef::new_sized(cast_ptr, src.layout, align)); + } + + + // Stores the return value of a function call into it's final location. + fn store_return(&mut self, + bx: &Builder<'a, 'tcx>, + dest: ReturnDest<'tcx>, + ret_ty: &ArgType<'tcx, Ty<'tcx>>, + llval: ValueRef) { + use self::ReturnDest::*; + + match dest { + Nothing => (), + Store(dst) => ret_ty.store(bx, llval, dst), + IndirectOperand(tmp, index) => { + let op = tmp.load(bx); + tmp.storage_dead(bx); + self.locals[index] = LocalRef::Operand(Some(op)); + } + DirectOperand(index) => { + // If there is a cast, we have to store and reload. + let op = if let PassMode::Cast(_) = ret_ty.mode { + let tmp = PlaceRef::alloca(bx, ret_ty.layout, "tmp_ret"); + tmp.storage_live(bx); + ret_ty.store(bx, llval, tmp); + let op = tmp.load(bx); + tmp.storage_dead(bx); + op + } else { + OperandRef::from_immediate_or_packed_pair(bx, llval, ret_ty.layout) + }; + self.locals[index] = LocalRef::Operand(Some(op)); + } + } + } +} + +enum ReturnDest<'tcx> { + // Do nothing, the return value is indirect or ignored + Nothing, + // Store the return value to the pointer + Store(PlaceRef<'tcx>), + // Stores an indirect return value to an operand local place + IndirectOperand(PlaceRef<'tcx>, mir::Local), + // Stores a direct return value to an operand local place + DirectOperand(mir::Local) +} diff --git a/src/librustc_codegen_llvm/mir/constant.rs b/src/librustc_codegen_llvm/mir/constant.rs new file mode 100644 index 00000000000..c2638d2d410 --- /dev/null +++ b/src/librustc_codegen_llvm/mir/constant.rs @@ -0,0 +1,271 @@ +// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use llvm::{self, ValueRef}; +use rustc::middle::const_val::{ConstVal, ConstEvalErr}; +use rustc_mir::interpret::{read_target_uint, const_val_field}; +use rustc::hir::def_id::DefId; +use rustc::mir; +use rustc_data_structures::indexed_vec::Idx; +use rustc::mir::interpret::{GlobalId, MemoryPointer, PrimVal, Allocation, ConstValue}; +use rustc::ty::{self, Ty}; +use rustc::ty::layout::{self, HasDataLayout, LayoutOf, Scalar}; +use builder::Builder; +use common::{CodegenCx}; +use common::{C_bytes, C_struct, C_uint_big, C_undef, C_usize}; +use consts; +use type_of::LayoutLlvmExt; +use type_::Type; +use syntax::ast::Mutability; + +use super::super::callee; +use super::FunctionCx; + +pub fn primval_to_llvm(cx: &CodegenCx, + cv: PrimVal, + scalar: &Scalar, + llty: Type) -> ValueRef { + let bits = if scalar.is_bool() { 1 } else { scalar.value.size(cx).bits() }; + match cv { + PrimVal::Undef => C_undef(Type::ix(cx, bits)), + PrimVal::Bytes(b) => { + let llval = C_uint_big(Type::ix(cx, bits), b); + if scalar.value == layout::Pointer { + unsafe { llvm::LLVMConstIntToPtr(llval, llty.to_ref()) } + } else { + consts::bitcast(llval, llty) + } + }, + PrimVal::Ptr(ptr) => { + if let Some(fn_instance) = cx.tcx.interpret_interner.get_fn(ptr.alloc_id) { + callee::get_fn(cx, fn_instance) + } else { + let static_ = cx + .tcx + .interpret_interner + .get_static(ptr.alloc_id); + let base_addr = if let Some(def_id) = static_ { + assert!(cx.tcx.is_static(def_id).is_some()); + consts::get_static(cx, def_id) + } else if let Some(alloc) = cx.tcx.interpret_interner + .get_alloc(ptr.alloc_id) { + let init = const_alloc_to_llvm(cx, alloc); + if alloc.runtime_mutability == Mutability::Mutable { + consts::addr_of_mut(cx, init, alloc.align, "byte_str") + } else { + consts::addr_of(cx, init, alloc.align, "byte_str") + } + } else { + bug!("missing allocation {:?}", ptr.alloc_id); + }; + + let llval = unsafe { llvm::LLVMConstInBoundsGEP( + consts::bitcast(base_addr, Type::i8p(cx)), + &C_usize(cx, ptr.offset), + 1, + ) }; + if scalar.value != layout::Pointer { + unsafe { llvm::LLVMConstPtrToInt(llval, llty.to_ref()) } + } else { + consts::bitcast(llval, llty) + } + } + } + } +} + +fn const_value_to_llvm<'tcx>(cx: &CodegenCx<'_, 'tcx>, val: ConstValue, ty: Ty<'tcx>) -> ValueRef { + let layout = cx.layout_of(ty); + + if layout.is_zst() { + return C_undef(layout.immediate_llvm_type(cx)); + } + + match val { + ConstValue::ByVal(x) => { + let scalar = match layout.abi { + layout::Abi::Scalar(ref x) => x, + _ => bug!("const_value_to_llvm: invalid ByVal layout: {:#?}", layout) + }; + primval_to_llvm( + cx, + x, + scalar, + layout.immediate_llvm_type(cx), + ) + }, + ConstValue::ByValPair(a, b) => { + let (a_scalar, b_scalar) = match layout.abi { + layout::Abi::ScalarPair(ref a, ref b) => (a, b), + _ => bug!("const_value_to_llvm: invalid ByValPair layout: {:#?}", layout) + }; + let a_llval = primval_to_llvm( + cx, + a, + a_scalar, + layout.scalar_pair_element_llvm_type(cx, 0), + ); + let b_llval = primval_to_llvm( + cx, + b, + b_scalar, + layout.scalar_pair_element_llvm_type(cx, 1), + ); + C_struct(cx, &[a_llval, b_llval], false) + }, + ConstValue::ByRef(alloc) => const_alloc_to_llvm(cx, alloc), + } +} + +pub fn const_alloc_to_llvm(cx: &CodegenCx, alloc: &Allocation) -> ValueRef { + let mut llvals = Vec::with_capacity(alloc.relocations.len() + 1); + let layout = cx.data_layout(); + let pointer_size = layout.pointer_size.bytes() as usize; + + let mut next_offset = 0; + for (&offset, &alloc_id) in &alloc.relocations { + assert_eq!(offset as usize as u64, offset); + let offset = offset as usize; + if offset > next_offset { + llvals.push(C_bytes(cx, &alloc.bytes[next_offset..offset])); + } + let ptr_offset = read_target_uint( + layout.endian, + &alloc.bytes[offset..(offset + pointer_size)], + ).expect("const_alloc_to_llvm: could not read relocation pointer") as u64; + llvals.push(primval_to_llvm( + cx, + PrimVal::Ptr(MemoryPointer { alloc_id, offset: ptr_offset }), + &Scalar { + value: layout::Primitive::Pointer, + valid_range: 0..=!0 + }, + Type::i8p(cx) + )); + next_offset = offset + pointer_size; + } + if alloc.bytes.len() >= next_offset { + llvals.push(C_bytes(cx, &alloc.bytes[next_offset ..])); + } + + C_struct(cx, &llvals, true) +} + +pub fn codegen_static_initializer<'a, 'tcx>( + cx: &CodegenCx<'a, 'tcx>, + def_id: DefId) + -> Result> +{ + let instance = ty::Instance::mono(cx.tcx, def_id); + let cid = GlobalId { + instance, + promoted: None + }; + let param_env = ty::ParamEnv::reveal_all(); + let static_ = cx.tcx.const_eval(param_env.and(cid))?; + + let val = match static_.val { + ConstVal::Value(val) => val, + _ => bug!("static const eval returned {:#?}", static_), + }; + Ok(const_value_to_llvm(cx, val, static_.ty)) +} + +impl<'a, 'tcx> FunctionCx<'a, 'tcx> { + fn const_to_const_value( + &mut self, + bx: &Builder<'a, 'tcx>, + constant: &'tcx ty::Const<'tcx>, + ) -> Result, ConstEvalErr<'tcx>> { + match constant.val { + ConstVal::Unevaluated(def_id, ref substs) => { + let tcx = bx.tcx(); + let param_env = ty::ParamEnv::reveal_all(); + let instance = ty::Instance::resolve(tcx, param_env, def_id, substs).unwrap(); + let cid = GlobalId { + instance, + promoted: None, + }; + let c = tcx.const_eval(param_env.and(cid))?; + self.const_to_const_value(bx, c) + }, + ConstVal::Value(val) => Ok(val), + } + } + + pub fn mir_constant_to_const_value( + &mut self, + bx: &Builder<'a, 'tcx>, + constant: &mir::Constant<'tcx>, + ) -> Result, ConstEvalErr<'tcx>> { + match constant.literal { + mir::Literal::Promoted { index } => { + let param_env = ty::ParamEnv::reveal_all(); + let cid = mir::interpret::GlobalId { + instance: self.instance, + promoted: Some(index), + }; + bx.tcx().const_eval(param_env.and(cid)) + } + mir::Literal::Value { value } => { + Ok(self.monomorphize(&value)) + } + }.and_then(|c| self.const_to_const_value(bx, c)) + } + + /// process constant containing SIMD shuffle indices + pub fn simd_shuffle_indices( + &mut self, + bx: &Builder<'a, 'tcx>, + constant: &mir::Constant<'tcx>, + ) -> (ValueRef, Ty<'tcx>) { + self.mir_constant_to_const_value(bx, constant) + .and_then(|c| { + let field_ty = constant.ty.builtin_index().unwrap(); + let fields = match constant.ty.sty { + ty::TyArray(_, n) => n.unwrap_usize(bx.tcx()), + ref other => bug!("invalid simd shuffle type: {}", other), + }; + let values: Result, _> = (0..fields).map(|field| { + let field = const_val_field( + bx.tcx(), + ty::ParamEnv::reveal_all(), + self.instance, + None, + mir::Field::new(field as usize), + c, + constant.ty, + )?; + if let Some(prim) = field.to_primval() { + let layout = bx.cx.layout_of(field_ty); + let scalar = match layout.abi { + layout::Abi::Scalar(ref x) => x, + _ => bug!("from_const: invalid ByVal layout: {:#?}", layout) + }; + Ok(primval_to_llvm( + bx.cx, prim, scalar, + layout.immediate_llvm_type(bx.cx), + )) + } else { + bug!("simd shuffle field {:?}", field) + } + }).collect(); + let llval = C_struct(bx.cx, &values?, false); + Ok((llval, constant.ty)) + }) + .unwrap_or_else(|e| { + e.report(bx.tcx(), constant.span, "shuffle_indices"); + // We've errored, so we don't have to produce working code. + let ty = self.monomorphize(&constant.ty); + let llty = bx.cx.layout_of(ty).llvm_type(bx.cx); + (C_undef(llty), ty) + }) + } +} diff --git a/src/librustc_codegen_llvm/mir/mod.rs b/src/librustc_codegen_llvm/mir/mod.rs new file mode 100644 index 00000000000..47b15320311 --- /dev/null +++ b/src/librustc_codegen_llvm/mir/mod.rs @@ -0,0 +1,652 @@ +// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use common::{C_i32, C_null}; +use libc::c_uint; +use llvm::{self, ValueRef, BasicBlockRef}; +use llvm::debuginfo::DIScope; +use rustc::ty::{self, Ty, TypeFoldable, UpvarSubsts}; +use rustc::ty::layout::{LayoutOf, TyLayout}; +use rustc::mir::{self, Mir}; +use rustc::ty::subst::Substs; +use rustc::session::config::FullDebugInfo; +use base; +use builder::Builder; +use common::{CodegenCx, Funclet}; +use debuginfo::{self, declare_local, VariableAccess, VariableKind, FunctionDebugContext}; +use monomorphize::Instance; +use abi::{ArgAttribute, ArgTypeExt, FnType, FnTypeExt, PassMode}; +use type_::Type; + +use syntax_pos::{DUMMY_SP, NO_EXPANSION, BytePos, Span}; +use syntax::symbol::keywords; + +use std::iter; + +use rustc_data_structures::bitvec::BitVector; +use rustc_data_structures::indexed_vec::{IndexVec, Idx}; + +pub use self::constant::codegen_static_initializer; + +use self::analyze::CleanupKind; +use self::place::PlaceRef; +use rustc::mir::traversal; + +use self::operand::{OperandRef, OperandValue}; + +/// Master context for codegenning from MIR. +pub struct FunctionCx<'a, 'tcx:'a> { + instance: Instance<'tcx>, + + mir: &'a mir::Mir<'tcx>, + + debug_context: debuginfo::FunctionDebugContext, + + llfn: ValueRef, + + cx: &'a CodegenCx<'a, 'tcx>, + + fn_ty: FnType<'tcx, Ty<'tcx>>, + + /// When unwinding is initiated, we have to store this personality + /// value somewhere so that we can load it and re-use it in the + /// resume instruction. The personality is (afaik) some kind of + /// value used for C++ unwinding, which must filter by type: we + /// don't really care about it very much. Anyway, this value + /// contains an alloca into which the personality is stored and + /// then later loaded when generating the DIVERGE_BLOCK. + personality_slot: Option>, + + /// A `Block` for each MIR `BasicBlock` + blocks: IndexVec, + + /// The funclet status of each basic block + cleanup_kinds: IndexVec, + + /// When targeting MSVC, this stores the cleanup info for each funclet + /// BB. This is initialized as we compute the funclets' head block in RPO. + funclets: &'a IndexVec>, + + /// This stores the landing-pad block for a given BB, computed lazily on GNU + /// and eagerly on MSVC. + landing_pads: IndexVec>, + + /// Cached unreachable block + unreachable_block: Option, + + /// The location where each MIR arg/var/tmp/ret is stored. This is + /// usually an `PlaceRef` representing an alloca, but not always: + /// sometimes we can skip the alloca and just store the value + /// directly using an `OperandRef`, which makes for tighter LLVM + /// IR. The conditions for using an `OperandRef` are as follows: + /// + /// - the type of the local must be judged "immediate" by `is_llvm_immediate` + /// - the operand must never be referenced indirectly + /// - we should not take its address using the `&` operator + /// - nor should it appear in a place path like `tmp.a` + /// - the operand must be defined by an rvalue that can generate immediate + /// values + /// + /// Avoiding allocs can also be important for certain intrinsics, + /// notably `expect`. + locals: IndexVec>, + + /// Debug information for MIR scopes. + scopes: IndexVec, + + /// If this function is being monomorphized, this contains the type substitutions used. + param_substs: &'tcx Substs<'tcx>, +} + +impl<'a, 'tcx> FunctionCx<'a, 'tcx> { + pub fn monomorphize(&self, value: &T) -> T + where T: TypeFoldable<'tcx> + { + self.cx.tcx.subst_and_normalize_erasing_regions( + self.param_substs, + ty::ParamEnv::reveal_all(), + value, + ) + } + + pub fn set_debug_loc(&mut self, bx: &Builder, source_info: mir::SourceInfo) { + let (scope, span) = self.debug_loc(source_info); + debuginfo::set_source_location(&self.debug_context, bx, scope, span); + } + + pub fn debug_loc(&mut self, source_info: mir::SourceInfo) -> (DIScope, Span) { + // Bail out if debug info emission is not enabled. + match self.debug_context { + FunctionDebugContext::DebugInfoDisabled | + FunctionDebugContext::FunctionWithoutDebugInfo => { + return (self.scopes[source_info.scope].scope_metadata, source_info.span); + } + FunctionDebugContext::RegularContext(_) =>{} + } + + // In order to have a good line stepping behavior in debugger, we overwrite debug + // locations of macro expansions with that of the outermost expansion site + // (unless the crate is being compiled with `-Z debug-macros`). + if source_info.span.ctxt() == NO_EXPANSION || + self.cx.sess().opts.debugging_opts.debug_macros { + let scope = self.scope_metadata_for_loc(source_info.scope, source_info.span.lo()); + (scope, source_info.span) + } else { + // Walk up the macro expansion chain until we reach a non-expanded span. + // We also stop at the function body level because no line stepping can occur + // at the level above that. + let mut span = source_info.span; + while span.ctxt() != NO_EXPANSION && span.ctxt() != self.mir.span.ctxt() { + if let Some(info) = span.ctxt().outer().expn_info() { + span = info.call_site; + } else { + break; + } + } + let scope = self.scope_metadata_for_loc(source_info.scope, span.lo()); + // Use span of the outermost expansion site, while keeping the original lexical scope. + (scope, span) + } + } + + // DILocations inherit source file name from the parent DIScope. Due to macro expansions + // it may so happen that the current span belongs to a different file than the DIScope + // corresponding to span's containing visibility scope. If so, we need to create a DIScope + // "extension" into that file. + fn scope_metadata_for_loc(&self, scope_id: mir::VisibilityScope, pos: BytePos) + -> llvm::debuginfo::DIScope { + let scope_metadata = self.scopes[scope_id].scope_metadata; + if pos < self.scopes[scope_id].file_start_pos || + pos >= self.scopes[scope_id].file_end_pos { + let cm = self.cx.sess().codemap(); + let defining_crate = self.debug_context.get_ref(DUMMY_SP).defining_crate; + debuginfo::extend_scope_to_file(self.cx, + scope_metadata, + &cm.lookup_char_pos(pos).file, + defining_crate) + } else { + scope_metadata + } + } +} + +enum LocalRef<'tcx> { + Place(PlaceRef<'tcx>), + Operand(Option>), +} + +impl<'a, 'tcx> LocalRef<'tcx> { + fn new_operand(cx: &CodegenCx<'a, 'tcx>, layout: TyLayout<'tcx>) -> LocalRef<'tcx> { + if layout.is_zst() { + // Zero-size temporaries aren't always initialized, which + // doesn't matter because they don't contain data, but + // we need something in the operand. + LocalRef::Operand(Some(OperandRef::new_zst(cx, layout))) + } else { + LocalRef::Operand(None) + } + } +} + +/////////////////////////////////////////////////////////////////////////// + +pub fn codegen_mir<'a, 'tcx: 'a>( + cx: &'a CodegenCx<'a, 'tcx>, + llfn: ValueRef, + mir: &'a Mir<'tcx>, + instance: Instance<'tcx>, + sig: ty::FnSig<'tcx>, +) { + let fn_ty = FnType::new(cx, sig, &[]); + debug!("fn_ty: {:?}", fn_ty); + let debug_context = + debuginfo::create_function_debug_context(cx, instance, sig, llfn, mir); + let bx = Builder::new_block(cx, llfn, "start"); + + if mir.basic_blocks().iter().any(|bb| bb.is_cleanup) { + bx.set_personality_fn(cx.eh_personality()); + } + + let cleanup_kinds = analyze::cleanup_kinds(&mir); + // Allocate a `Block` for every basic block, except + // the start block, if nothing loops back to it. + let reentrant_start_block = !mir.predecessors_for(mir::START_BLOCK).is_empty(); + let block_bxs: IndexVec = + mir.basic_blocks().indices().map(|bb| { + if bb == mir::START_BLOCK && !reentrant_start_block { + bx.llbb() + } else { + bx.build_sibling_block(&format!("{:?}", bb)).llbb() + } + }).collect(); + + // Compute debuginfo scopes from MIR scopes. + let scopes = debuginfo::create_mir_scopes(cx, mir, &debug_context); + let (landing_pads, funclets) = create_funclets(mir, &bx, &cleanup_kinds, &block_bxs); + + let mut fx = FunctionCx { + instance, + mir, + llfn, + fn_ty, + cx, + personality_slot: None, + blocks: block_bxs, + unreachable_block: None, + cleanup_kinds, + landing_pads, + funclets: &funclets, + scopes, + locals: IndexVec::new(), + debug_context, + param_substs: { + assert!(!instance.substs.needs_infer()); + instance.substs + }, + }; + + let memory_locals = analyze::non_ssa_locals(&fx); + + // Allocate variable and temp allocas + fx.locals = { + let args = arg_local_refs(&bx, &fx, &fx.scopes, &memory_locals); + + let mut allocate_local = |local| { + let decl = &mir.local_decls[local]; + let layout = bx.cx.layout_of(fx.monomorphize(&decl.ty)); + assert!(!layout.ty.has_erasable_regions()); + + if let Some(name) = decl.name { + // User variable + let debug_scope = fx.scopes[decl.source_info.scope]; + let dbg = debug_scope.is_valid() && bx.sess().opts.debuginfo == FullDebugInfo; + + if !memory_locals.contains(local.index()) && !dbg { + debug!("alloc: {:?} ({}) -> operand", local, name); + return LocalRef::new_operand(bx.cx, layout); + } + + debug!("alloc: {:?} ({}) -> place", local, name); + let place = PlaceRef::alloca(&bx, layout, &name.as_str()); + if dbg { + let (scope, span) = fx.debug_loc(decl.source_info); + declare_local(&bx, &fx.debug_context, name, layout.ty, scope, + VariableAccess::DirectVariable { alloca: place.llval }, + VariableKind::LocalVariable, span); + } + LocalRef::Place(place) + } else { + // Temporary or return place + if local == mir::RETURN_PLACE && fx.fn_ty.ret.is_indirect() { + debug!("alloc: {:?} (return place) -> place", local); + let llretptr = llvm::get_param(llfn, 0); + LocalRef::Place(PlaceRef::new_sized(llretptr, layout, layout.align)) + } else if memory_locals.contains(local.index()) { + debug!("alloc: {:?} -> place", local); + LocalRef::Place(PlaceRef::alloca(&bx, layout, &format!("{:?}", local))) + } else { + // If this is an immediate local, we do not create an + // alloca in advance. Instead we wait until we see the + // definition and update the operand there. + debug!("alloc: {:?} -> operand", local); + LocalRef::new_operand(bx.cx, layout) + } + } + }; + + let retptr = allocate_local(mir::RETURN_PLACE); + iter::once(retptr) + .chain(args.into_iter()) + .chain(mir.vars_and_temps_iter().map(allocate_local)) + .collect() + }; + + // Branch to the START block, if it's not the entry block. + if reentrant_start_block { + bx.br(fx.blocks[mir::START_BLOCK]); + } + + // Up until here, IR instructions for this function have explicitly not been annotated with + // source code location, so we don't step into call setup code. From here on, source location + // emitting should be enabled. + debuginfo::start_emitting_source_locations(&fx.debug_context); + + let rpo = traversal::reverse_postorder(&mir); + let mut visited = BitVector::new(mir.basic_blocks().len()); + + // Codegen the body of each block using reverse postorder + for (bb, _) in rpo { + visited.insert(bb.index()); + fx.codegen_block(bb); + } + + // Remove blocks that haven't been visited, or have no + // predecessors. + for bb in mir.basic_blocks().indices() { + // Unreachable block + if !visited.contains(bb.index()) { + debug!("codegen_mir: block {:?} was not visited", bb); + unsafe { + llvm::LLVMDeleteBasicBlock(fx.blocks[bb]); + } + } + } +} + +fn create_funclets<'a, 'tcx>( + mir: &'a Mir<'tcx>, + bx: &Builder<'a, 'tcx>, + cleanup_kinds: &IndexVec, + block_bxs: &IndexVec) + -> (IndexVec>, + IndexVec>) +{ + block_bxs.iter_enumerated().zip(cleanup_kinds).map(|((bb, &llbb), cleanup_kind)| { + match *cleanup_kind { + CleanupKind::Funclet if base::wants_msvc_seh(bx.sess()) => {} + _ => return (None, None) + } + + let cleanup; + let ret_llbb; + match mir[bb].terminator.as_ref().map(|t| &t.kind) { + // This is a basic block that we're aborting the program for, + // notably in an `extern` function. These basic blocks are inserted + // so that we assert that `extern` functions do indeed not panic, + // and if they do we abort the process. + // + // On MSVC these are tricky though (where we're doing funclets). If + // we were to do a cleanuppad (like below) the normal functions like + // `longjmp` would trigger the abort logic, terminating the + // program. Instead we insert the equivalent of `catch(...)` for C++ + // which magically doesn't trigger when `longjmp` files over this + // frame. + // + // Lots more discussion can be found on #48251 but this codegen is + // modeled after clang's for: + // + // try { + // foo(); + // } catch (...) { + // bar(); + // } + Some(&mir::TerminatorKind::Abort) => { + let cs_bx = bx.build_sibling_block(&format!("cs_funclet{:?}", bb)); + let cp_bx = bx.build_sibling_block(&format!("cp_funclet{:?}", bb)); + ret_llbb = cs_bx.llbb(); + + let cs = cs_bx.catch_switch(None, None, 1); + cs_bx.add_handler(cs, cp_bx.llbb()); + + // The "null" here is actually a RTTI type descriptor for the + // C++ personality function, but `catch (...)` has no type so + // it's null. The 64 here is actually a bitfield which + // represents that this is a catch-all block. + let null = C_null(Type::i8p(bx.cx)); + let sixty_four = C_i32(bx.cx, 64); + cleanup = cp_bx.catch_pad(cs, &[null, sixty_four, null]); + cp_bx.br(llbb); + } + _ => { + let cleanup_bx = bx.build_sibling_block(&format!("funclet_{:?}", bb)); + ret_llbb = cleanup_bx.llbb(); + cleanup = cleanup_bx.cleanup_pad(None, &[]); + cleanup_bx.br(llbb); + } + }; + + (Some(ret_llbb), Some(Funclet::new(cleanup))) + }).unzip() +} + +/// Produce, for each argument, a `ValueRef` pointing at the +/// argument's value. As arguments are places, these are always +/// indirect. +fn arg_local_refs<'a, 'tcx>(bx: &Builder<'a, 'tcx>, + fx: &FunctionCx<'a, 'tcx>, + scopes: &IndexVec, + memory_locals: &BitVector) + -> Vec> { + let mir = fx.mir; + let tcx = bx.tcx(); + let mut idx = 0; + let mut llarg_idx = fx.fn_ty.ret.is_indirect() as usize; + + // Get the argument scope, if it exists and if we need it. + let arg_scope = scopes[mir::ARGUMENT_VISIBILITY_SCOPE]; + let arg_scope = if arg_scope.is_valid() && bx.sess().opts.debuginfo == FullDebugInfo { + Some(arg_scope.scope_metadata) + } else { + None + }; + + let deref_op = unsafe { + [llvm::LLVMRustDIBuilderCreateOpDeref()] + }; + + mir.args_iter().enumerate().map(|(arg_index, local)| { + let arg_decl = &mir.local_decls[local]; + + let name = if let Some(name) = arg_decl.name { + name.as_str().to_string() + } else { + format!("arg{}", arg_index) + }; + + if Some(local) == mir.spread_arg { + // This argument (e.g. the last argument in the "rust-call" ABI) + // is a tuple that was spread at the ABI level and now we have + // to reconstruct it into a tuple local variable, from multiple + // individual LLVM function arguments. + + let arg_ty = fx.monomorphize(&arg_decl.ty); + let tupled_arg_tys = match arg_ty.sty { + ty::TyTuple(ref tys) => tys, + _ => bug!("spread argument isn't a tuple?!") + }; + + let place = PlaceRef::alloca(bx, bx.cx.layout_of(arg_ty), &name); + for i in 0..tupled_arg_tys.len() { + let arg = &fx.fn_ty.args[idx]; + idx += 1; + if arg.pad.is_some() { + llarg_idx += 1; + } + arg.store_fn_arg(bx, &mut llarg_idx, place.project_field(bx, i)); + } + + // Now that we have one alloca that contains the aggregate value, + // we can create one debuginfo entry for the argument. + arg_scope.map(|scope| { + let variable_access = VariableAccess::DirectVariable { + alloca: place.llval + }; + declare_local( + bx, + &fx.debug_context, + arg_decl.name.unwrap_or(keywords::Invalid.name()), + arg_ty, scope, + variable_access, + VariableKind::ArgumentVariable(arg_index + 1), + DUMMY_SP + ); + }); + + return LocalRef::Place(place); + } + + let arg = &fx.fn_ty.args[idx]; + idx += 1; + if arg.pad.is_some() { + llarg_idx += 1; + } + + if arg_scope.is_none() && !memory_locals.contains(local.index()) { + // We don't have to cast or keep the argument in the alloca. + // FIXME(eddyb): We should figure out how to use llvm.dbg.value instead + // of putting everything in allocas just so we can use llvm.dbg.declare. + let local = |op| LocalRef::Operand(Some(op)); + match arg.mode { + PassMode::Ignore => { + return local(OperandRef::new_zst(bx.cx, arg.layout)); + } + PassMode::Direct(_) => { + let llarg = llvm::get_param(bx.llfn(), llarg_idx as c_uint); + bx.set_value_name(llarg, &name); + llarg_idx += 1; + return local( + OperandRef::from_immediate_or_packed_pair(bx, llarg, arg.layout)); + } + PassMode::Pair(..) => { + let a = llvm::get_param(bx.llfn(), llarg_idx as c_uint); + bx.set_value_name(a, &(name.clone() + ".0")); + llarg_idx += 1; + + let b = llvm::get_param(bx.llfn(), llarg_idx as c_uint); + bx.set_value_name(b, &(name + ".1")); + llarg_idx += 1; + + return local(OperandRef { + val: OperandValue::Pair(a, b), + layout: arg.layout + }); + } + _ => {} + } + } + + let place = if arg.is_indirect() { + // Don't copy an indirect argument to an alloca, the caller + // already put it in a temporary alloca and gave it up. + // FIXME: lifetimes + let llarg = llvm::get_param(bx.llfn(), llarg_idx as c_uint); + bx.set_value_name(llarg, &name); + llarg_idx += 1; + PlaceRef::new_sized(llarg, arg.layout, arg.layout.align) + } else { + let tmp = PlaceRef::alloca(bx, arg.layout, &name); + arg.store_fn_arg(bx, &mut llarg_idx, tmp); + tmp + }; + arg_scope.map(|scope| { + // Is this a regular argument? + if arg_index > 0 || mir.upvar_decls.is_empty() { + // The Rust ABI passes indirect variables using a pointer and a manual copy, so we + // need to insert a deref here, but the C ABI uses a pointer and a copy using the + // byval attribute, for which LLVM does the deref itself, so we must not add it. + // Starting with D31439 in LLVM 5, it *always* does the deref itself. + let mut variable_access = VariableAccess::DirectVariable { + alloca: place.llval + }; + if unsafe { llvm::LLVMRustVersionMajor() < 5 } { + if let PassMode::Indirect(ref attrs) = arg.mode { + if !attrs.contains(ArgAttribute::ByVal) { + variable_access = VariableAccess::IndirectVariable { + alloca: place.llval, + address_operations: &deref_op, + }; + } + } + } + + declare_local( + bx, + &fx.debug_context, + arg_decl.name.unwrap_or(keywords::Invalid.name()), + arg.layout.ty, + scope, + variable_access, + VariableKind::ArgumentVariable(arg_index + 1), + DUMMY_SP + ); + return; + } + + // Or is it the closure environment? + let (closure_layout, env_ref) = match arg.layout.ty.sty { + ty::TyRawPtr(ty::TypeAndMut { ty, .. }) | + ty::TyRef(_, ty, _) => (bx.cx.layout_of(ty), true), + _ => (arg.layout, false) + }; + + let (def_id, upvar_substs) = match closure_layout.ty.sty { + ty::TyClosure(def_id, substs) => (def_id, UpvarSubsts::Closure(substs)), + ty::TyGenerator(def_id, substs, _) => (def_id, UpvarSubsts::Generator(substs)), + _ => bug!("upvar_decls with non-closure arg0 type `{}`", closure_layout.ty) + }; + let upvar_tys = upvar_substs.upvar_tys(def_id, tcx); + + // Store the pointer to closure data in an alloca for debuginfo + // because that's what the llvm.dbg.declare intrinsic expects. + + // FIXME(eddyb) this shouldn't be necessary but SROA seems to + // mishandle DW_OP_plus not preceded by DW_OP_deref, i.e. it + // doesn't actually strip the offset when splitting the closure + // environment into its components so it ends up out of bounds. + let env_ptr = if !env_ref { + let scratch = PlaceRef::alloca(bx, + bx.cx.layout_of(tcx.mk_mut_ptr(arg.layout.ty)), + "__debuginfo_env_ptr"); + bx.store(place.llval, scratch.llval, scratch.align); + scratch.llval + } else { + place.llval + }; + + for (i, (decl, ty)) in mir.upvar_decls.iter().zip(upvar_tys).enumerate() { + let byte_offset_of_var_in_env = closure_layout.fields.offset(i).bytes(); + + let ops = unsafe { + [llvm::LLVMRustDIBuilderCreateOpDeref(), + llvm::LLVMRustDIBuilderCreateOpPlusUconst(), + byte_offset_of_var_in_env as i64, + llvm::LLVMRustDIBuilderCreateOpDeref()] + }; + + // The environment and the capture can each be indirect. + + // FIXME(eddyb) see above why we have to keep + // a pointer in an alloca for debuginfo atm. + let mut ops = if env_ref || true { &ops[..] } else { &ops[1..] }; + + let ty = if let (true, &ty::TyRef(_, ty, _)) = (decl.by_ref, &ty.sty) { + ty + } else { + ops = &ops[..ops.len() - 1]; + ty + }; + + let variable_access = VariableAccess::IndirectVariable { + alloca: env_ptr, + address_operations: &ops + }; + declare_local( + bx, + &fx.debug_context, + decl.debug_name, + ty, + scope, + variable_access, + VariableKind::CapturedVariable, + DUMMY_SP + ); + } + }); + LocalRef::Place(place) + }).collect() +} + +mod analyze; +mod block; +mod constant; +pub mod place; +pub mod operand; +mod rvalue; +mod statement; diff --git a/src/librustc_codegen_llvm/mir/operand.rs b/src/librustc_codegen_llvm/mir/operand.rs new file mode 100644 index 00000000000..62ef58f8255 --- /dev/null +++ b/src/librustc_codegen_llvm/mir/operand.rs @@ -0,0 +1,427 @@ +// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use llvm::ValueRef; +use rustc::middle::const_val::ConstEvalErr; +use rustc::mir; +use rustc::mir::interpret::ConstValue; +use rustc::ty; +use rustc::ty::layout::{self, Align, LayoutOf, TyLayout}; +use rustc_data_structures::indexed_vec::Idx; + +use base; +use common::{self, CodegenCx, C_null, C_undef, C_usize}; +use builder::{Builder, MemFlags}; +use value::Value; +use type_of::LayoutLlvmExt; +use type_::Type; +use consts; + +use std::fmt; +use std::ptr; + +use super::{FunctionCx, LocalRef}; +use super::constant::{primval_to_llvm, const_alloc_to_llvm}; +use super::place::PlaceRef; + +/// The representation of a Rust value. The enum variant is in fact +/// uniquely determined by the value's type, but is kept as a +/// safety check. +#[derive(Copy, Clone)] +pub enum OperandValue { + /// A reference to the actual operand. The data is guaranteed + /// to be valid for the operand's lifetime. + Ref(ValueRef, Align), + /// A single LLVM value. + Immediate(ValueRef), + /// A pair of immediate LLVM values. Used by fat pointers too. + Pair(ValueRef, ValueRef) +} + +impl fmt::Debug for OperandValue { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match *self { + OperandValue::Ref(r, align) => { + write!(f, "Ref({:?}, {:?})", Value(r), align) + } + OperandValue::Immediate(i) => { + write!(f, "Immediate({:?})", Value(i)) + } + OperandValue::Pair(a, b) => { + write!(f, "Pair({:?}, {:?})", Value(a), Value(b)) + } + } + } +} + +/// An `OperandRef` is an "SSA" reference to a Rust value, along with +/// its type. +/// +/// NOTE: unless you know a value's type exactly, you should not +/// generate LLVM opcodes acting on it and instead act via methods, +/// to avoid nasty edge cases. In particular, using `Builder::store` +/// directly is sure to cause problems -- use `OperandRef::store` +/// instead. +#[derive(Copy, Clone)] +pub struct OperandRef<'tcx> { + // The value. + pub val: OperandValue, + + // The layout of value, based on its Rust type. + pub layout: TyLayout<'tcx>, +} + +impl<'tcx> fmt::Debug for OperandRef<'tcx> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "OperandRef({:?} @ {:?})", self.val, self.layout) + } +} + +impl<'a, 'tcx> OperandRef<'tcx> { + pub fn new_zst(cx: &CodegenCx<'a, 'tcx>, + layout: TyLayout<'tcx>) -> OperandRef<'tcx> { + assert!(layout.is_zst()); + OperandRef { + val: OperandValue::Immediate(C_undef(layout.immediate_llvm_type(cx))), + layout + } + } + + pub fn from_const(bx: &Builder<'a, 'tcx>, + val: ConstValue<'tcx>, + ty: ty::Ty<'tcx>) + -> Result, ConstEvalErr<'tcx>> { + let layout = bx.cx.layout_of(ty); + + if layout.is_zst() { + return Ok(OperandRef::new_zst(bx.cx, layout)); + } + + let val = match val { + ConstValue::ByVal(x) => { + let scalar = match layout.abi { + layout::Abi::Scalar(ref x) => x, + _ => bug!("from_const: invalid ByVal layout: {:#?}", layout) + }; + let llval = primval_to_llvm( + bx.cx, + x, + scalar, + layout.immediate_llvm_type(bx.cx), + ); + OperandValue::Immediate(llval) + }, + ConstValue::ByValPair(a, b) => { + let (a_scalar, b_scalar) = match layout.abi { + layout::Abi::ScalarPair(ref a, ref b) => (a, b), + _ => bug!("from_const: invalid ByValPair layout: {:#?}", layout) + }; + let a_llval = primval_to_llvm( + bx.cx, + a, + a_scalar, + layout.scalar_pair_element_llvm_type(bx.cx, 0), + ); + let b_llval = primval_to_llvm( + bx.cx, + b, + b_scalar, + layout.scalar_pair_element_llvm_type(bx.cx, 1), + ); + OperandValue::Pair(a_llval, b_llval) + }, + ConstValue::ByRef(alloc) => { + let init = const_alloc_to_llvm(bx.cx, alloc); + let llval = consts::addr_of(bx.cx, init, layout.align, "byte_str"); + let llval = consts::bitcast(llval, layout.llvm_type(bx.cx).ptr_to()); + return Ok(PlaceRef::new_sized(llval, layout, alloc.align).load(bx)); + }, + }; + + Ok(OperandRef { + val, + layout + }) + } + + /// Asserts that this operand refers to a scalar and returns + /// a reference to its value. + pub fn immediate(self) -> ValueRef { + match self.val { + OperandValue::Immediate(s) => s, + _ => bug!("not immediate: {:?}", self) + } + } + + pub fn deref(self, cx: &CodegenCx<'a, 'tcx>) -> PlaceRef<'tcx> { + let projected_ty = self.layout.ty.builtin_deref(true) + .unwrap_or_else(|| bug!("deref of non-pointer {:?}", self)).ty; + let (llptr, llextra) = match self.val { + OperandValue::Immediate(llptr) => (llptr, ptr::null_mut()), + OperandValue::Pair(llptr, llextra) => (llptr, llextra), + OperandValue::Ref(..) => bug!("Deref of by-Ref operand {:?}", self) + }; + let layout = cx.layout_of(projected_ty); + PlaceRef { + llval: llptr, + llextra, + layout, + align: layout.align, + } + } + + /// If this operand is a `Pair`, we return an aggregate with the two values. + /// For other cases, see `immediate`. + pub fn immediate_or_packed_pair(self, bx: &Builder<'a, 'tcx>) -> ValueRef { + if let OperandValue::Pair(a, b) = self.val { + let llty = self.layout.llvm_type(bx.cx); + debug!("Operand::immediate_or_packed_pair: packing {:?} into {:?}", + self, llty); + // Reconstruct the immediate aggregate. + let mut llpair = C_undef(llty); + llpair = bx.insert_value(llpair, a, 0); + llpair = bx.insert_value(llpair, b, 1); + llpair + } else { + self.immediate() + } + } + + /// If the type is a pair, we return a `Pair`, otherwise, an `Immediate`. + pub fn from_immediate_or_packed_pair(bx: &Builder<'a, 'tcx>, + llval: ValueRef, + layout: TyLayout<'tcx>) + -> OperandRef<'tcx> { + let val = if layout.is_llvm_scalar_pair() { + debug!("Operand::from_immediate_or_packed_pair: unpacking {:?} @ {:?}", + llval, layout); + + // Deconstruct the immediate aggregate. + OperandValue::Pair(bx.extract_value(llval, 0), + bx.extract_value(llval, 1)) + } else { + OperandValue::Immediate(llval) + }; + OperandRef { val, layout } + } + + pub fn extract_field(&self, bx: &Builder<'a, 'tcx>, i: usize) -> OperandRef<'tcx> { + let field = self.layout.field(bx.cx, i); + let offset = self.layout.fields.offset(i); + + let mut val = match (self.val, &self.layout.abi) { + // If the field is ZST, it has no data. + _ if field.is_zst() => { + return OperandRef::new_zst(bx.cx, field); + } + + // Newtype of a scalar, scalar pair or vector. + (OperandValue::Immediate(_), _) | + (OperandValue::Pair(..), _) if field.size == self.layout.size => { + assert_eq!(offset.bytes(), 0); + self.val + } + + // Extract a scalar component from a pair. + (OperandValue::Pair(a_llval, b_llval), &layout::Abi::ScalarPair(ref a, ref b)) => { + if offset.bytes() == 0 { + assert_eq!(field.size, a.value.size(bx.cx)); + OperandValue::Immediate(a_llval) + } else { + assert_eq!(offset, a.value.size(bx.cx) + .abi_align(b.value.align(bx.cx))); + assert_eq!(field.size, b.value.size(bx.cx)); + OperandValue::Immediate(b_llval) + } + } + + // `#[repr(simd)]` types are also immediate. + (OperandValue::Immediate(llval), &layout::Abi::Vector { .. }) => { + OperandValue::Immediate( + bx.extract_element(llval, C_usize(bx.cx, i as u64))) + } + + _ => bug!("OperandRef::extract_field({:?}): not applicable", self) + }; + + // HACK(eddyb) have to bitcast pointers until LLVM removes pointee types. + match val { + OperandValue::Immediate(ref mut llval) => { + *llval = bx.bitcast(*llval, field.immediate_llvm_type(bx.cx)); + } + OperandValue::Pair(ref mut a, ref mut b) => { + *a = bx.bitcast(*a, field.scalar_pair_element_llvm_type(bx.cx, 0)); + *b = bx.bitcast(*b, field.scalar_pair_element_llvm_type(bx.cx, 1)); + } + OperandValue::Ref(..) => bug!() + } + + OperandRef { + val, + layout: field + } + } +} + +impl<'a, 'tcx> OperandValue { + pub fn store(self, bx: &Builder<'a, 'tcx>, dest: PlaceRef<'tcx>) { + self.store_with_flags(bx, dest, MemFlags::empty()); + } + + pub fn volatile_store(self, bx: &Builder<'a, 'tcx>, dest: PlaceRef<'tcx>) { + self.store_with_flags(bx, dest, MemFlags::VOLATILE); + } + + pub fn nontemporal_store(self, bx: &Builder<'a, 'tcx>, dest: PlaceRef<'tcx>) { + self.store_with_flags(bx, dest, MemFlags::NONTEMPORAL); + } + + fn store_with_flags(self, bx: &Builder<'a, 'tcx>, dest: PlaceRef<'tcx>, flags: MemFlags) { + debug!("OperandRef::store: operand={:?}, dest={:?}", self, dest); + // Avoid generating stores of zero-sized values, because the only way to have a zero-sized + // value is through `undef`, and store itself is useless. + if dest.layout.is_zst() { + return; + } + match self { + OperandValue::Ref(r, source_align) => { + base::memcpy_ty(bx, dest.llval, r, dest.layout, + source_align.min(dest.align), flags) + } + OperandValue::Immediate(s) => { + let val = base::from_immediate(bx, s); + bx.store_with_flags(val, dest.llval, dest.align, flags); + } + OperandValue::Pair(a, b) => { + for (i, &x) in [a, b].iter().enumerate() { + let mut llptr = bx.struct_gep(dest.llval, i as u64); + // Make sure to always store i1 as i8. + if common::val_ty(x) == Type::i1(bx.cx) { + llptr = bx.pointercast(llptr, Type::i8p(bx.cx)); + } + let val = base::from_immediate(bx, x); + bx.store_with_flags(val, llptr, dest.align, flags); + } + } + } + } +} + +impl<'a, 'tcx> FunctionCx<'a, 'tcx> { + fn maybe_codegen_consume_direct(&mut self, + bx: &Builder<'a, 'tcx>, + place: &mir::Place<'tcx>) + -> Option> + { + debug!("maybe_codegen_consume_direct(place={:?})", place); + + // watch out for locals that do not have an + // alloca; they are handled somewhat differently + if let mir::Place::Local(index) = *place { + match self.locals[index] { + LocalRef::Operand(Some(o)) => { + return Some(o); + } + LocalRef::Operand(None) => { + bug!("use of {:?} before def", place); + } + LocalRef::Place(..) => { + // use path below + } + } + } + + // Moves out of scalar and scalar pair fields are trivial. + if let &mir::Place::Projection(ref proj) = place { + if let Some(o) = self.maybe_codegen_consume_direct(bx, &proj.base) { + match proj.elem { + mir::ProjectionElem::Field(ref f, _) => { + return Some(o.extract_field(bx, f.index())); + } + mir::ProjectionElem::Index(_) | + mir::ProjectionElem::ConstantIndex { .. } => { + // ZSTs don't require any actual memory access. + // FIXME(eddyb) deduplicate this with the identical + // checks in `codegen_consume` and `extract_field`. + let elem = o.layout.field(bx.cx, 0); + if elem.is_zst() { + return Some(OperandRef::new_zst(bx.cx, elem)); + } + } + _ => {} + } + } + } + + None + } + + pub fn codegen_consume(&mut self, + bx: &Builder<'a, 'tcx>, + place: &mir::Place<'tcx>) + -> OperandRef<'tcx> + { + debug!("codegen_consume(place={:?})", place); + + let ty = self.monomorphized_place_ty(place); + let layout = bx.cx.layout_of(ty); + + // ZSTs don't require any actual memory access. + if layout.is_zst() { + return OperandRef::new_zst(bx.cx, layout); + } + + if let Some(o) = self.maybe_codegen_consume_direct(bx, place) { + return o; + } + + // for most places, to consume them we just load them + // out from their home + self.codegen_place(bx, place).load(bx) + } + + pub fn codegen_operand(&mut self, + bx: &Builder<'a, 'tcx>, + operand: &mir::Operand<'tcx>) + -> OperandRef<'tcx> + { + debug!("codegen_operand(operand={:?})", operand); + + match *operand { + mir::Operand::Copy(ref place) | + mir::Operand::Move(ref place) => { + self.codegen_consume(bx, place) + } + + mir::Operand::Constant(ref constant) => { + let ty = self.monomorphize(&constant.ty); + self.mir_constant_to_const_value(bx, constant) + .and_then(|c| OperandRef::from_const(bx, c, ty)) + .unwrap_or_else(|err| { + match constant.literal { + mir::Literal::Promoted { .. } => { + // don't report errors inside promoteds, just warnings. + }, + mir::Literal::Value { .. } => { + err.report(bx.tcx(), constant.span, "const operand") + }, + } + // We've errored, so we don't have to produce working code. + let layout = bx.cx.layout_of(ty); + PlaceRef::new_sized( + C_null(layout.llvm_type(bx.cx).ptr_to()), + layout, + layout.align, + ).load(bx) + }) + } + } + } +} diff --git a/src/librustc_codegen_llvm/mir/place.rs b/src/librustc_codegen_llvm/mir/place.rs new file mode 100644 index 00000000000..bda8c758750 --- /dev/null +++ b/src/librustc_codegen_llvm/mir/place.rs @@ -0,0 +1,498 @@ +// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use llvm::{self, ValueRef}; +use rustc::ty::{self, Ty}; +use rustc::ty::layout::{self, Align, TyLayout, LayoutOf}; +use rustc::mir; +use rustc::mir::tcx::PlaceTy; +use rustc_data_structures::indexed_vec::Idx; +use base; +use builder::Builder; +use common::{CodegenCx, C_undef, C_usize, C_u8, C_u32, C_uint, C_null, C_uint_big}; +use consts; +use type_of::LayoutLlvmExt; +use type_::Type; +use value::Value; +use glue; + +use std::ptr; + +use super::{FunctionCx, LocalRef}; +use super::operand::{OperandRef, OperandValue}; + +#[derive(Copy, Clone, Debug)] +pub struct PlaceRef<'tcx> { + /// Pointer to the contents of the place + pub llval: ValueRef, + + /// This place's extra data if it is unsized, or null + pub llextra: ValueRef, + + /// Monomorphized type of this place, including variant information + pub layout: TyLayout<'tcx>, + + /// What alignment we know for this place + pub align: Align, +} + +impl<'a, 'tcx> PlaceRef<'tcx> { + pub fn new_sized(llval: ValueRef, + layout: TyLayout<'tcx>, + align: Align) + -> PlaceRef<'tcx> { + PlaceRef { + llval, + llextra: ptr::null_mut(), + layout, + align + } + } + + pub fn alloca(bx: &Builder<'a, 'tcx>, layout: TyLayout<'tcx>, name: &str) + -> PlaceRef<'tcx> { + debug!("alloca({:?}: {:?})", name, layout); + let tmp = bx.alloca(layout.llvm_type(bx.cx), name, layout.align); + Self::new_sized(tmp, layout, layout.align) + } + + pub fn len(&self, cx: &CodegenCx<'a, 'tcx>) -> ValueRef { + if let layout::FieldPlacement::Array { count, .. } = self.layout.fields { + if self.layout.is_unsized() { + assert!(self.has_extra()); + assert_eq!(count, 0); + self.llextra + } else { + C_usize(cx, count) + } + } else { + bug!("unexpected layout `{:#?}` in PlaceRef::len", self.layout) + } + } + + pub fn has_extra(&self) -> bool { + !self.llextra.is_null() + } + + pub fn load(&self, bx: &Builder<'a, 'tcx>) -> OperandRef<'tcx> { + debug!("PlaceRef::load: {:?}", self); + + assert!(!self.has_extra()); + + if self.layout.is_zst() { + return OperandRef::new_zst(bx.cx, self.layout); + } + + let scalar_load_metadata = |load, scalar: &layout::Scalar| { + let vr = scalar.valid_range.clone(); + match scalar.value { + layout::Int(..) => { + let range = scalar.valid_range_exclusive(bx.cx); + if range.start != range.end { + bx.range_metadata(load, range); + } + } + layout::Pointer if vr.start() < vr.end() && !vr.contains(&0) => { + bx.nonnull_metadata(load); + } + _ => {} + } + }; + + let val = if self.layout.is_llvm_immediate() { + let mut const_llval = ptr::null_mut(); + unsafe { + let global = llvm::LLVMIsAGlobalVariable(self.llval); + if !global.is_null() && llvm::LLVMIsGlobalConstant(global) == llvm::True { + const_llval = llvm::LLVMGetInitializer(global); + } + } + + let llval = if !const_llval.is_null() { + const_llval + } else { + let load = bx.load(self.llval, self.align); + if let layout::Abi::Scalar(ref scalar) = self.layout.abi { + scalar_load_metadata(load, scalar); + } + load + }; + OperandValue::Immediate(base::to_immediate(bx, llval, self.layout)) + } else if let layout::Abi::ScalarPair(ref a, ref b) = self.layout.abi { + let load = |i, scalar: &layout::Scalar| { + let mut llptr = bx.struct_gep(self.llval, i as u64); + // Make sure to always load i1 as i8. + if scalar.is_bool() { + llptr = bx.pointercast(llptr, Type::i8p(bx.cx)); + } + let load = bx.load(llptr, self.align); + scalar_load_metadata(load, scalar); + if scalar.is_bool() { + bx.trunc(load, Type::i1(bx.cx)) + } else { + load + } + }; + OperandValue::Pair(load(0, a), load(1, b)) + } else { + OperandValue::Ref(self.llval, self.align) + }; + + OperandRef { val, layout: self.layout } + } + + /// Access a field, at a point when the value's case is known. + pub fn project_field(self, bx: &Builder<'a, 'tcx>, ix: usize) -> PlaceRef<'tcx> { + let cx = bx.cx; + let field = self.layout.field(cx, ix); + let offset = self.layout.fields.offset(ix); + let align = self.align.min(self.layout.align).min(field.align); + + let simple = || { + // Unions and newtypes only use an offset of 0. + let llval = if offset.bytes() == 0 { + self.llval + } else if let layout::Abi::ScalarPair(ref a, ref b) = self.layout.abi { + // Offsets have to match either first or second field. + assert_eq!(offset, a.value.size(cx).abi_align(b.value.align(cx))); + bx.struct_gep(self.llval, 1) + } else { + bx.struct_gep(self.llval, self.layout.llvm_field_index(ix)) + }; + PlaceRef { + // HACK(eddyb) have to bitcast pointers until LLVM removes pointee types. + llval: bx.pointercast(llval, field.llvm_type(cx).ptr_to()), + llextra: if cx.type_has_metadata(field.ty) { + self.llextra + } else { + ptr::null_mut() + }, + layout: field, + align, + } + }; + + // Simple cases, which don't need DST adjustment: + // * no metadata available - just log the case + // * known alignment - sized types, [T], str or a foreign type + // * packed struct - there is no alignment padding + match field.ty.sty { + _ if !self.has_extra() => { + debug!("Unsized field `{}`, of `{:?}` has no metadata for adjustment", + ix, Value(self.llval)); + return simple(); + } + _ if !field.is_unsized() => return simple(), + ty::TySlice(..) | ty::TyStr | ty::TyForeign(..) => return simple(), + ty::TyAdt(def, _) => { + if def.repr.packed() { + // FIXME(eddyb) generalize the adjustment when we + // start supporting packing to larger alignments. + assert_eq!(self.layout.align.abi(), 1); + return simple(); + } + } + _ => {} + } + + // We need to get the pointer manually now. + // We do this by casting to a *i8, then offsetting it by the appropriate amount. + // We do this instead of, say, simply adjusting the pointer from the result of a GEP + // because the field may have an arbitrary alignment in the LLVM representation + // anyway. + // + // To demonstrate: + // struct Foo { + // x: u16, + // y: T + // } + // + // The type Foo> is represented in LLVM as { u16, { u16, u8 }}, meaning that + // the `y` field has 16-bit alignment. + + let meta = self.llextra; + + let unaligned_offset = C_usize(cx, offset.bytes()); + + // Get the alignment of the field + let (_, unsized_align) = glue::size_and_align_of_dst(bx, field.ty, meta); + + // Bump the unaligned offset up to the appropriate alignment using the + // following expression: + // + // (unaligned offset + (align - 1)) & -align + + // Calculate offset + let align_sub_1 = bx.sub(unsized_align, C_usize(cx, 1u64)); + let offset = bx.and(bx.add(unaligned_offset, align_sub_1), + bx.neg(unsized_align)); + + debug!("struct_field_ptr: DST field offset: {:?}", Value(offset)); + + // Cast and adjust pointer + let byte_ptr = bx.pointercast(self.llval, Type::i8p(cx)); + let byte_ptr = bx.gep(byte_ptr, &[offset]); + + // Finally, cast back to the type expected + let ll_fty = field.llvm_type(cx); + debug!("struct_field_ptr: Field type is {:?}", ll_fty); + + PlaceRef { + llval: bx.pointercast(byte_ptr, ll_fty.ptr_to()), + llextra: self.llextra, + layout: field, + align, + } + } + + /// Obtain the actual discriminant of a value. + pub fn codegen_get_discr(self, bx: &Builder<'a, 'tcx>, cast_to: Ty<'tcx>) -> ValueRef { + let cast_to = bx.cx.layout_of(cast_to).immediate_llvm_type(bx.cx); + if self.layout.abi == layout::Abi::Uninhabited { + return C_undef(cast_to); + } + match self.layout.variants { + layout::Variants::Single { index } => { + let discr_val = self.layout.ty.ty_adt_def().map_or( + index as u128, + |def| def.discriminant_for_variant(bx.cx.tcx, index).val); + return C_uint_big(cast_to, discr_val); + } + layout::Variants::Tagged { .. } | + layout::Variants::NicheFilling { .. } => {}, + } + + let discr = self.project_field(bx, 0); + let lldiscr = discr.load(bx).immediate(); + match self.layout.variants { + layout::Variants::Single { .. } => bug!(), + layout::Variants::Tagged { ref tag, .. } => { + let signed = match tag.value { + layout::Int(_, signed) => signed, + _ => false + }; + bx.intcast(lldiscr, cast_to, signed) + } + layout::Variants::NicheFilling { + dataful_variant, + ref niche_variants, + niche_start, + .. + } => { + let niche_llty = discr.layout.immediate_llvm_type(bx.cx); + if niche_variants.start() == niche_variants.end() { + // FIXME(eddyb) Check the actual primitive type here. + let niche_llval = if niche_start == 0 { + // HACK(eddyb) Using `C_null` as it works on all types. + C_null(niche_llty) + } else { + C_uint_big(niche_llty, niche_start) + }; + bx.select(bx.icmp(llvm::IntEQ, lldiscr, niche_llval), + C_uint(cast_to, *niche_variants.start() as u64), + C_uint(cast_to, dataful_variant as u64)) + } else { + // Rebase from niche values to discriminant values. + let delta = niche_start.wrapping_sub(*niche_variants.start() as u128); + let lldiscr = bx.sub(lldiscr, C_uint_big(niche_llty, delta)); + let lldiscr_max = C_uint(niche_llty, *niche_variants.end() as u64); + bx.select(bx.icmp(llvm::IntULE, lldiscr, lldiscr_max), + bx.intcast(lldiscr, cast_to, false), + C_uint(cast_to, dataful_variant as u64)) + } + } + } + } + + /// Set the discriminant for a new value of the given case of the given + /// representation. + pub fn codegen_set_discr(&self, bx: &Builder<'a, 'tcx>, variant_index: usize) { + if self.layout.for_variant(bx.cx, variant_index).abi == layout::Abi::Uninhabited { + return; + } + match self.layout.variants { + layout::Variants::Single { index } => { + assert_eq!(index, variant_index); + } + layout::Variants::Tagged { .. } => { + let ptr = self.project_field(bx, 0); + let to = self.layout.ty.ty_adt_def().unwrap() + .discriminant_for_variant(bx.tcx(), variant_index) + .val; + bx.store( + C_uint_big(ptr.layout.llvm_type(bx.cx), to), + ptr.llval, + ptr.align); + } + layout::Variants::NicheFilling { + dataful_variant, + ref niche_variants, + niche_start, + .. + } => { + if variant_index != dataful_variant { + if bx.sess().target.target.arch == "arm" || + bx.sess().target.target.arch == "aarch64" { + // Issue #34427: As workaround for LLVM bug on ARM, + // use memset of 0 before assigning niche value. + let llptr = bx.pointercast(self.llval, Type::i8(bx.cx).ptr_to()); + let fill_byte = C_u8(bx.cx, 0); + let (size, align) = self.layout.size_and_align(); + let size = C_usize(bx.cx, size.bytes()); + let align = C_u32(bx.cx, align.abi() as u32); + base::call_memset(bx, llptr, fill_byte, size, align, false); + } + + let niche = self.project_field(bx, 0); + let niche_llty = niche.layout.immediate_llvm_type(bx.cx); + let niche_value = ((variant_index - *niche_variants.start()) as u128) + .wrapping_add(niche_start); + // FIXME(eddyb) Check the actual primitive type here. + let niche_llval = if niche_value == 0 { + // HACK(eddyb) Using `C_null` as it works on all types. + C_null(niche_llty) + } else { + C_uint_big(niche_llty, niche_value) + }; + OperandValue::Immediate(niche_llval).store(bx, niche); + } + } + } + } + + pub fn project_index(&self, bx: &Builder<'a, 'tcx>, llindex: ValueRef) + -> PlaceRef<'tcx> { + PlaceRef { + llval: bx.inbounds_gep(self.llval, &[C_usize(bx.cx, 0), llindex]), + llextra: ptr::null_mut(), + layout: self.layout.field(bx.cx, 0), + align: self.align + } + } + + pub fn project_downcast(&self, bx: &Builder<'a, 'tcx>, variant_index: usize) + -> PlaceRef<'tcx> { + let mut downcast = *self; + downcast.layout = self.layout.for_variant(bx.cx, variant_index); + + // Cast to the appropriate variant struct type. + let variant_ty = downcast.layout.llvm_type(bx.cx); + downcast.llval = bx.pointercast(downcast.llval, variant_ty.ptr_to()); + + downcast + } + + pub fn storage_live(&self, bx: &Builder<'a, 'tcx>) { + bx.lifetime_start(self.llval, self.layout.size); + } + + pub fn storage_dead(&self, bx: &Builder<'a, 'tcx>) { + bx.lifetime_end(self.llval, self.layout.size); + } +} + +impl<'a, 'tcx> FunctionCx<'a, 'tcx> { + pub fn codegen_place(&mut self, + bx: &Builder<'a, 'tcx>, + place: &mir::Place<'tcx>) + -> PlaceRef<'tcx> { + debug!("codegen_place(place={:?})", place); + + let cx = bx.cx; + let tcx = cx.tcx; + + if let mir::Place::Local(index) = *place { + match self.locals[index] { + LocalRef::Place(place) => { + return place; + } + LocalRef::Operand(..) => { + bug!("using operand local {:?} as place", place); + } + } + } + + let result = match *place { + mir::Place::Local(_) => bug!(), // handled above + mir::Place::Static(box mir::Static { def_id, ty }) => { + let layout = cx.layout_of(self.monomorphize(&ty)); + PlaceRef::new_sized(consts::get_static(cx, def_id), layout, layout.align) + }, + mir::Place::Projection(box mir::Projection { + ref base, + elem: mir::ProjectionElem::Deref + }) => { + // Load the pointer from its location. + self.codegen_consume(bx, base).deref(bx.cx) + } + mir::Place::Projection(ref projection) => { + let cg_base = self.codegen_place(bx, &projection.base); + + match projection.elem { + mir::ProjectionElem::Deref => bug!(), + mir::ProjectionElem::Field(ref field, _) => { + cg_base.project_field(bx, field.index()) + } + mir::ProjectionElem::Index(index) => { + let index = &mir::Operand::Copy(mir::Place::Local(index)); + let index = self.codegen_operand(bx, index); + let llindex = index.immediate(); + cg_base.project_index(bx, llindex) + } + mir::ProjectionElem::ConstantIndex { offset, + from_end: false, + min_length: _ } => { + let lloffset = C_usize(bx.cx, offset as u64); + cg_base.project_index(bx, lloffset) + } + mir::ProjectionElem::ConstantIndex { offset, + from_end: true, + min_length: _ } => { + let lloffset = C_usize(bx.cx, offset as u64); + let lllen = cg_base.len(bx.cx); + let llindex = bx.sub(lllen, lloffset); + cg_base.project_index(bx, llindex) + } + mir::ProjectionElem::Subslice { from, to } => { + let mut subslice = cg_base.project_index(bx, + C_usize(bx.cx, from as u64)); + let projected_ty = PlaceTy::Ty { ty: cg_base.layout.ty } + .projection_ty(tcx, &projection.elem).to_ty(bx.tcx()); + subslice.layout = bx.cx.layout_of(self.monomorphize(&projected_ty)); + + if subslice.layout.is_unsized() { + assert!(cg_base.has_extra()); + subslice.llextra = bx.sub(cg_base.llextra, + C_usize(bx.cx, (from as u64) + (to as u64))); + } + + // Cast the place pointer type to the new + // array or slice type (*[%_; new_len]). + subslice.llval = bx.pointercast(subslice.llval, + subslice.layout.llvm_type(bx.cx).ptr_to()); + + subslice + } + mir::ProjectionElem::Downcast(_, v) => { + cg_base.project_downcast(bx, v) + } + } + } + }; + debug!("codegen_place(place={:?}) => {:?}", place, result); + result + } + + pub fn monomorphized_place_ty(&self, place: &mir::Place<'tcx>) -> Ty<'tcx> { + let tcx = self.cx.tcx; + let place_ty = place.ty(self.mir, tcx); + self.monomorphize(&place_ty.to_ty(tcx)) + } +} diff --git a/src/librustc_codegen_llvm/mir/rvalue.rs b/src/librustc_codegen_llvm/mir/rvalue.rs new file mode 100644 index 00000000000..d1b949d4f73 --- /dev/null +++ b/src/librustc_codegen_llvm/mir/rvalue.rs @@ -0,0 +1,952 @@ +// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use llvm::{self, ValueRef}; +use rustc::ty::{self, Ty}; +use rustc::ty::cast::{CastTy, IntTy}; +use rustc::ty::layout::{self, LayoutOf}; +use rustc::mir; +use rustc::middle::lang_items::ExchangeMallocFnLangItem; +use rustc_apfloat::{ieee, Float, Status, Round}; +use std::{u128, i128}; + +use base; +use builder::Builder; +use callee; +use common::{self, val_ty}; +use common::{C_bool, C_u8, C_i32, C_u32, C_u64, C_undef, C_null, C_usize, C_uint, C_uint_big}; +use consts; +use monomorphize; +use type_::Type; +use type_of::LayoutLlvmExt; +use value::Value; + +use super::{FunctionCx, LocalRef}; +use super::operand::{OperandRef, OperandValue}; +use super::place::PlaceRef; + +impl<'a, 'tcx> FunctionCx<'a, 'tcx> { + pub fn codegen_rvalue(&mut self, + bx: Builder<'a, 'tcx>, + dest: PlaceRef<'tcx>, + rvalue: &mir::Rvalue<'tcx>) + -> Builder<'a, 'tcx> + { + debug!("codegen_rvalue(dest.llval={:?}, rvalue={:?})", + Value(dest.llval), rvalue); + + match *rvalue { + mir::Rvalue::Use(ref operand) => { + let cg_operand = self.codegen_operand(&bx, operand); + // FIXME: consider not copying constants through stack. (fixable by codegenning + // constants into OperandValue::Ref, why don’t we do that yet if we don’t?) + cg_operand.val.store(&bx, dest); + bx + } + + mir::Rvalue::Cast(mir::CastKind::Unsize, ref source, _) => { + // The destination necessarily contains a fat pointer, so if + // it's a scalar pair, it's a fat pointer or newtype thereof. + if dest.layout.is_llvm_scalar_pair() { + // into-coerce of a thin pointer to a fat pointer - just + // use the operand path. + let (bx, temp) = self.codegen_rvalue_operand(bx, rvalue); + temp.val.store(&bx, dest); + return bx; + } + + // Unsize of a nontrivial struct. I would prefer for + // this to be eliminated by MIR building, but + // `CoerceUnsized` can be passed by a where-clause, + // so the (generic) MIR may not be able to expand it. + let operand = self.codegen_operand(&bx, source); + match operand.val { + OperandValue::Pair(..) | + OperandValue::Immediate(_) => { + // unsize from an immediate structure. We don't + // really need a temporary alloca here, but + // avoiding it would require us to have + // `coerce_unsized_into` use extractvalue to + // index into the struct, and this case isn't + // important enough for it. + debug!("codegen_rvalue: creating ugly alloca"); + let scratch = PlaceRef::alloca(&bx, operand.layout, "__unsize_temp"); + scratch.storage_live(&bx); + operand.val.store(&bx, scratch); + base::coerce_unsized_into(&bx, scratch, dest); + scratch.storage_dead(&bx); + } + OperandValue::Ref(llref, align) => { + let source = PlaceRef::new_sized(llref, operand.layout, align); + base::coerce_unsized_into(&bx, source, dest); + } + } + bx + } + + mir::Rvalue::Repeat(ref elem, count) => { + let cg_elem = self.codegen_operand(&bx, elem); + + // Do not generate the loop for zero-sized elements or empty arrays. + if dest.layout.is_zst() { + return bx; + } + + let start = dest.project_index(&bx, C_usize(bx.cx, 0)).llval; + + if let OperandValue::Immediate(v) = cg_elem.val { + let align = C_i32(bx.cx, dest.align.abi() as i32); + let size = C_usize(bx.cx, dest.layout.size.bytes()); + + // Use llvm.memset.p0i8.* to initialize all zero arrays + if common::is_const_integral(v) && common::const_to_uint(v) == 0 { + let fill = C_u8(bx.cx, 0); + base::call_memset(&bx, start, fill, size, align, false); + return bx; + } + + // Use llvm.memset.p0i8.* to initialize byte arrays + let v = base::from_immediate(&bx, v); + if common::val_ty(v) == Type::i8(bx.cx) { + base::call_memset(&bx, start, v, size, align, false); + return bx; + } + } + + let count = C_usize(bx.cx, count); + let end = dest.project_index(&bx, count).llval; + + let header_bx = bx.build_sibling_block("repeat_loop_header"); + let body_bx = bx.build_sibling_block("repeat_loop_body"); + let next_bx = bx.build_sibling_block("repeat_loop_next"); + + bx.br(header_bx.llbb()); + let current = header_bx.phi(common::val_ty(start), &[start], &[bx.llbb()]); + + let keep_going = header_bx.icmp(llvm::IntNE, current, end); + header_bx.cond_br(keep_going, body_bx.llbb(), next_bx.llbb()); + + cg_elem.val.store(&body_bx, + PlaceRef::new_sized(current, cg_elem.layout, dest.align)); + + let next = body_bx.inbounds_gep(current, &[C_usize(bx.cx, 1)]); + body_bx.br(header_bx.llbb()); + header_bx.add_incoming_to_phi(current, next, body_bx.llbb()); + + next_bx + } + + mir::Rvalue::Aggregate(ref kind, ref operands) => { + let (dest, active_field_index) = match **kind { + mir::AggregateKind::Adt(adt_def, variant_index, _, active_field_index) => { + dest.codegen_set_discr(&bx, variant_index); + if adt_def.is_enum() { + (dest.project_downcast(&bx, variant_index), active_field_index) + } else { + (dest, active_field_index) + } + } + _ => (dest, None) + }; + for (i, operand) in operands.iter().enumerate() { + let op = self.codegen_operand(&bx, operand); + // Do not generate stores and GEPis for zero-sized fields. + if !op.layout.is_zst() { + let field_index = active_field_index.unwrap_or(i); + op.val.store(&bx, dest.project_field(&bx, field_index)); + } + } + bx + } + + _ => { + assert!(self.rvalue_creates_operand(rvalue)); + let (bx, temp) = self.codegen_rvalue_operand(bx, rvalue); + temp.val.store(&bx, dest); + bx + } + } + } + + pub fn codegen_rvalue_operand(&mut self, + bx: Builder<'a, 'tcx>, + rvalue: &mir::Rvalue<'tcx>) + -> (Builder<'a, 'tcx>, OperandRef<'tcx>) + { + assert!(self.rvalue_creates_operand(rvalue), "cannot codegen {:?} to operand", rvalue); + + match *rvalue { + mir::Rvalue::Cast(ref kind, ref source, mir_cast_ty) => { + let operand = self.codegen_operand(&bx, source); + debug!("cast operand is {:?}", operand); + let cast = bx.cx.layout_of(self.monomorphize(&mir_cast_ty)); + + let val = match *kind { + mir::CastKind::ReifyFnPointer => { + match operand.layout.ty.sty { + ty::TyFnDef(def_id, substs) => { + if bx.cx.tcx.has_attr(def_id, "rustc_args_required_const") { + bug!("reifying a fn ptr that requires \ + const arguments"); + } + OperandValue::Immediate( + callee::resolve_and_get_fn(bx.cx, def_id, substs)) + } + _ => { + bug!("{} cannot be reified to a fn ptr", operand.layout.ty) + } + } + } + mir::CastKind::ClosureFnPointer => { + match operand.layout.ty.sty { + ty::TyClosure(def_id, substs) => { + let instance = monomorphize::resolve_closure( + bx.cx.tcx, def_id, substs, ty::ClosureKind::FnOnce); + OperandValue::Immediate(callee::get_fn(bx.cx, instance)) + } + _ => { + bug!("{} cannot be cast to a fn ptr", operand.layout.ty) + } + } + } + mir::CastKind::UnsafeFnPointer => { + // this is a no-op at the LLVM level + operand.val + } + mir::CastKind::Unsize => { + assert!(cast.is_llvm_scalar_pair()); + match operand.val { + OperandValue::Pair(lldata, llextra) => { + // unsize from a fat pointer - this is a + // "trait-object-to-supertrait" coercion, for + // example, + // &'a fmt::Debug+Send => &'a fmt::Debug, + + // HACK(eddyb) have to bitcast pointers + // until LLVM removes pointee types. + let lldata = bx.pointercast(lldata, + cast.scalar_pair_element_llvm_type(bx.cx, 0)); + OperandValue::Pair(lldata, llextra) + } + OperandValue::Immediate(lldata) => { + // "standard" unsize + let (lldata, llextra) = base::unsize_thin_ptr(&bx, lldata, + operand.layout.ty, cast.ty); + OperandValue::Pair(lldata, llextra) + } + OperandValue::Ref(..) => { + bug!("by-ref operand {:?} in codegen_rvalue_operand", + operand); + } + } + } + mir::CastKind::Misc if operand.layout.is_llvm_scalar_pair() => { + if let OperandValue::Pair(data_ptr, meta) = operand.val { + if cast.is_llvm_scalar_pair() { + let data_cast = bx.pointercast(data_ptr, + cast.scalar_pair_element_llvm_type(bx.cx, 0)); + OperandValue::Pair(data_cast, meta) + } else { // cast to thin-ptr + // Cast of fat-ptr to thin-ptr is an extraction of data-ptr and + // pointer-cast of that pointer to desired pointer type. + let llcast_ty = cast.immediate_llvm_type(bx.cx); + let llval = bx.pointercast(data_ptr, llcast_ty); + OperandValue::Immediate(llval) + } + } else { + bug!("Unexpected non-Pair operand") + } + } + mir::CastKind::Misc => { + assert!(cast.is_llvm_immediate()); + let ll_t_out = cast.immediate_llvm_type(bx.cx); + if operand.layout.abi == layout::Abi::Uninhabited { + return (bx, OperandRef { + val: OperandValue::Immediate(C_undef(ll_t_out)), + layout: cast, + }); + } + let r_t_in = CastTy::from_ty(operand.layout.ty) + .expect("bad input type for cast"); + let r_t_out = CastTy::from_ty(cast.ty).expect("bad output type for cast"); + let ll_t_in = operand.layout.immediate_llvm_type(bx.cx); + match operand.layout.variants { + layout::Variants::Single { index } => { + if let Some(def) = operand.layout.ty.ty_adt_def() { + let discr_val = def + .discriminant_for_variant(bx.cx.tcx, index) + .val; + let discr = C_uint_big(ll_t_out, discr_val); + return (bx, OperandRef { + val: OperandValue::Immediate(discr), + layout: cast, + }); + } + } + layout::Variants::Tagged { .. } | + layout::Variants::NicheFilling { .. } => {}, + } + let llval = operand.immediate(); + + let mut signed = false; + if let layout::Abi::Scalar(ref scalar) = operand.layout.abi { + if let layout::Int(_, s) = scalar.value { + signed = s; + + if scalar.valid_range.end() > scalar.valid_range.start() { + // We want `table[e as usize]` to not + // have bound checks, and this is the most + // convenient place to put the `assume`. + + base::call_assume(&bx, bx.icmp( + llvm::IntULE, + llval, + C_uint_big(ll_t_in, *scalar.valid_range.end()) + )); + } + } + } + + let newval = match (r_t_in, r_t_out) { + (CastTy::Int(_), CastTy::Int(_)) => { + bx.intcast(llval, ll_t_out, signed) + } + (CastTy::Float, CastTy::Float) => { + let srcsz = ll_t_in.float_width(); + let dstsz = ll_t_out.float_width(); + if dstsz > srcsz { + bx.fpext(llval, ll_t_out) + } else if srcsz > dstsz { + bx.fptrunc(llval, ll_t_out) + } else { + llval + } + } + (CastTy::Ptr(_), CastTy::Ptr(_)) | + (CastTy::FnPtr, CastTy::Ptr(_)) | + (CastTy::RPtr(_), CastTy::Ptr(_)) => + bx.pointercast(llval, ll_t_out), + (CastTy::Ptr(_), CastTy::Int(_)) | + (CastTy::FnPtr, CastTy::Int(_)) => + bx.ptrtoint(llval, ll_t_out), + (CastTy::Int(_), CastTy::Ptr(_)) => { + let usize_llval = bx.intcast(llval, bx.cx.isize_ty, signed); + bx.inttoptr(usize_llval, ll_t_out) + } + (CastTy::Int(_), CastTy::Float) => + cast_int_to_float(&bx, signed, llval, ll_t_in, ll_t_out), + (CastTy::Float, CastTy::Int(IntTy::I)) => + cast_float_to_int(&bx, true, llval, ll_t_in, ll_t_out), + (CastTy::Float, CastTy::Int(_)) => + cast_float_to_int(&bx, false, llval, ll_t_in, ll_t_out), + _ => bug!("unsupported cast: {:?} to {:?}", operand.layout.ty, cast.ty) + }; + OperandValue::Immediate(newval) + } + }; + (bx, OperandRef { + val, + layout: cast + }) + } + + mir::Rvalue::Ref(_, bk, ref place) => { + let cg_place = self.codegen_place(&bx, place); + + let ty = cg_place.layout.ty; + + // Note: places are indirect, so storing the `llval` into the + // destination effectively creates a reference. + let val = if !bx.cx.type_has_metadata(ty) { + OperandValue::Immediate(cg_place.llval) + } else { + OperandValue::Pair(cg_place.llval, cg_place.llextra) + }; + (bx, OperandRef { + val, + layout: self.cx.layout_of(self.cx.tcx.mk_ref( + self.cx.tcx.types.re_erased, + ty::TypeAndMut { ty, mutbl: bk.to_mutbl_lossy() } + )), + }) + } + + mir::Rvalue::Len(ref place) => { + let size = self.evaluate_array_len(&bx, place); + let operand = OperandRef { + val: OperandValue::Immediate(size), + layout: bx.cx.layout_of(bx.tcx().types.usize), + }; + (bx, operand) + } + + mir::Rvalue::BinaryOp(op, ref lhs, ref rhs) => { + let lhs = self.codegen_operand(&bx, lhs); + let rhs = self.codegen_operand(&bx, rhs); + let llresult = match (lhs.val, rhs.val) { + (OperandValue::Pair(lhs_addr, lhs_extra), + OperandValue::Pair(rhs_addr, rhs_extra)) => { + self.codegen_fat_ptr_binop(&bx, op, + lhs_addr, lhs_extra, + rhs_addr, rhs_extra, + lhs.layout.ty) + } + + (OperandValue::Immediate(lhs_val), + OperandValue::Immediate(rhs_val)) => { + self.codegen_scalar_binop(&bx, op, lhs_val, rhs_val, lhs.layout.ty) + } + + _ => bug!() + }; + let operand = OperandRef { + val: OperandValue::Immediate(llresult), + layout: bx.cx.layout_of( + op.ty(bx.tcx(), lhs.layout.ty, rhs.layout.ty)), + }; + (bx, operand) + } + mir::Rvalue::CheckedBinaryOp(op, ref lhs, ref rhs) => { + let lhs = self.codegen_operand(&bx, lhs); + let rhs = self.codegen_operand(&bx, rhs); + let result = self.codegen_scalar_checked_binop(&bx, op, + lhs.immediate(), rhs.immediate(), + lhs.layout.ty); + let val_ty = op.ty(bx.tcx(), lhs.layout.ty, rhs.layout.ty); + let operand_ty = bx.tcx().intern_tup(&[val_ty, bx.tcx().types.bool]); + let operand = OperandRef { + val: result, + layout: bx.cx.layout_of(operand_ty) + }; + + (bx, operand) + } + + mir::Rvalue::UnaryOp(op, ref operand) => { + let operand = self.codegen_operand(&bx, operand); + let lloperand = operand.immediate(); + let is_float = operand.layout.ty.is_fp(); + let llval = match op { + mir::UnOp::Not => bx.not(lloperand), + mir::UnOp::Neg => if is_float { + bx.fneg(lloperand) + } else { + bx.neg(lloperand) + } + }; + (bx, OperandRef { + val: OperandValue::Immediate(llval), + layout: operand.layout, + }) + } + + mir::Rvalue::Discriminant(ref place) => { + let discr_ty = rvalue.ty(&*self.mir, bx.tcx()); + let discr = self.codegen_place(&bx, place) + .codegen_get_discr(&bx, discr_ty); + (bx, OperandRef { + val: OperandValue::Immediate(discr), + layout: self.cx.layout_of(discr_ty) + }) + } + + mir::Rvalue::NullaryOp(mir::NullOp::SizeOf, ty) => { + assert!(bx.cx.type_is_sized(ty)); + let val = C_usize(bx.cx, bx.cx.size_of(ty).bytes()); + let tcx = bx.tcx(); + (bx, OperandRef { + val: OperandValue::Immediate(val), + layout: self.cx.layout_of(tcx.types.usize), + }) + } + + mir::Rvalue::NullaryOp(mir::NullOp::Box, content_ty) => { + let content_ty: Ty<'tcx> = self.monomorphize(&content_ty); + let (size, align) = bx.cx.size_and_align_of(content_ty); + let llsize = C_usize(bx.cx, size.bytes()); + let llalign = C_usize(bx.cx, align.abi()); + let box_layout = bx.cx.layout_of(bx.tcx().mk_box(content_ty)); + let llty_ptr = box_layout.llvm_type(bx.cx); + + // Allocate space: + let def_id = match bx.tcx().lang_items().require(ExchangeMallocFnLangItem) { + Ok(id) => id, + Err(s) => { + bx.sess().fatal(&format!("allocation of `{}` {}", box_layout.ty, s)); + } + }; + let instance = ty::Instance::mono(bx.tcx(), def_id); + let r = callee::get_fn(bx.cx, instance); + let val = bx.pointercast(bx.call(r, &[llsize, llalign], None), llty_ptr); + + let operand = OperandRef { + val: OperandValue::Immediate(val), + layout: box_layout, + }; + (bx, operand) + } + mir::Rvalue::Use(ref operand) => { + let operand = self.codegen_operand(&bx, operand); + (bx, operand) + } + mir::Rvalue::Repeat(..) | + mir::Rvalue::Aggregate(..) => { + // According to `rvalue_creates_operand`, only ZST + // aggregate rvalues are allowed to be operands. + let ty = rvalue.ty(self.mir, self.cx.tcx); + (bx, OperandRef::new_zst(self.cx, + self.cx.layout_of(self.monomorphize(&ty)))) + } + } + } + + fn evaluate_array_len(&mut self, + bx: &Builder<'a, 'tcx>, + place: &mir::Place<'tcx>) -> ValueRef + { + // ZST are passed as operands and require special handling + // because codegen_place() panics if Local is operand. + if let mir::Place::Local(index) = *place { + if let LocalRef::Operand(Some(op)) = self.locals[index] { + if let ty::TyArray(_, n) = op.layout.ty.sty { + let n = n.unwrap_usize(bx.cx.tcx); + return common::C_usize(bx.cx, n); + } + } + } + // use common size calculation for non zero-sized types + let cg_value = self.codegen_place(&bx, place); + return cg_value.len(bx.cx); + } + + pub fn codegen_scalar_binop(&mut self, + bx: &Builder<'a, 'tcx>, + op: mir::BinOp, + lhs: ValueRef, + rhs: ValueRef, + input_ty: Ty<'tcx>) -> ValueRef { + let is_float = input_ty.is_fp(); + let is_signed = input_ty.is_signed(); + let is_nil = input_ty.is_nil(); + match op { + mir::BinOp::Add => if is_float { + bx.fadd(lhs, rhs) + } else { + bx.add(lhs, rhs) + }, + mir::BinOp::Sub => if is_float { + bx.fsub(lhs, rhs) + } else { + bx.sub(lhs, rhs) + }, + mir::BinOp::Mul => if is_float { + bx.fmul(lhs, rhs) + } else { + bx.mul(lhs, rhs) + }, + mir::BinOp::Div => if is_float { + bx.fdiv(lhs, rhs) + } else if is_signed { + bx.sdiv(lhs, rhs) + } else { + bx.udiv(lhs, rhs) + }, + mir::BinOp::Rem => if is_float { + bx.frem(lhs, rhs) + } else if is_signed { + bx.srem(lhs, rhs) + } else { + bx.urem(lhs, rhs) + }, + mir::BinOp::BitOr => bx.or(lhs, rhs), + mir::BinOp::BitAnd => bx.and(lhs, rhs), + mir::BinOp::BitXor => bx.xor(lhs, rhs), + mir::BinOp::Offset => bx.inbounds_gep(lhs, &[rhs]), + mir::BinOp::Shl => common::build_unchecked_lshift(bx, lhs, rhs), + mir::BinOp::Shr => common::build_unchecked_rshift(bx, input_ty, lhs, rhs), + mir::BinOp::Ne | mir::BinOp::Lt | mir::BinOp::Gt | + mir::BinOp::Eq | mir::BinOp::Le | mir::BinOp::Ge => if is_nil { + C_bool(bx.cx, match op { + mir::BinOp::Ne | mir::BinOp::Lt | mir::BinOp::Gt => false, + mir::BinOp::Eq | mir::BinOp::Le | mir::BinOp::Ge => true, + _ => unreachable!() + }) + } else if is_float { + bx.fcmp( + base::bin_op_to_fcmp_predicate(op.to_hir_binop()), + lhs, rhs + ) + } else { + bx.icmp( + base::bin_op_to_icmp_predicate(op.to_hir_binop(), is_signed), + lhs, rhs + ) + } + } + } + + pub fn codegen_fat_ptr_binop(&mut self, + bx: &Builder<'a, 'tcx>, + op: mir::BinOp, + lhs_addr: ValueRef, + lhs_extra: ValueRef, + rhs_addr: ValueRef, + rhs_extra: ValueRef, + _input_ty: Ty<'tcx>) + -> ValueRef { + match op { + mir::BinOp::Eq => { + bx.and( + bx.icmp(llvm::IntEQ, lhs_addr, rhs_addr), + bx.icmp(llvm::IntEQ, lhs_extra, rhs_extra) + ) + } + mir::BinOp::Ne => { + bx.or( + bx.icmp(llvm::IntNE, lhs_addr, rhs_addr), + bx.icmp(llvm::IntNE, lhs_extra, rhs_extra) + ) + } + mir::BinOp::Le | mir::BinOp::Lt | + mir::BinOp::Ge | mir::BinOp::Gt => { + // a OP b ~ a.0 STRICT(OP) b.0 | (a.0 == b.0 && a.1 OP a.1) + let (op, strict_op) = match op { + mir::BinOp::Lt => (llvm::IntULT, llvm::IntULT), + mir::BinOp::Le => (llvm::IntULE, llvm::IntULT), + mir::BinOp::Gt => (llvm::IntUGT, llvm::IntUGT), + mir::BinOp::Ge => (llvm::IntUGE, llvm::IntUGT), + _ => bug!(), + }; + + bx.or( + bx.icmp(strict_op, lhs_addr, rhs_addr), + bx.and( + bx.icmp(llvm::IntEQ, lhs_addr, rhs_addr), + bx.icmp(op, lhs_extra, rhs_extra) + ) + ) + } + _ => { + bug!("unexpected fat ptr binop"); + } + } + } + + pub fn codegen_scalar_checked_binop(&mut self, + bx: &Builder<'a, 'tcx>, + op: mir::BinOp, + lhs: ValueRef, + rhs: ValueRef, + input_ty: Ty<'tcx>) -> OperandValue { + // This case can currently arise only from functions marked + // with #[rustc_inherit_overflow_checks] and inlined from + // another crate (mostly core::num generic/#[inline] fns), + // while the current crate doesn't use overflow checks. + if !bx.cx.check_overflow { + let val = self.codegen_scalar_binop(bx, op, lhs, rhs, input_ty); + return OperandValue::Pair(val, C_bool(bx.cx, false)); + } + + let (val, of) = match op { + // These are checked using intrinsics + mir::BinOp::Add | mir::BinOp::Sub | mir::BinOp::Mul => { + let oop = match op { + mir::BinOp::Add => OverflowOp::Add, + mir::BinOp::Sub => OverflowOp::Sub, + mir::BinOp::Mul => OverflowOp::Mul, + _ => unreachable!() + }; + let intrinsic = get_overflow_intrinsic(oop, bx, input_ty); + let res = bx.call(intrinsic, &[lhs, rhs], None); + + (bx.extract_value(res, 0), + bx.extract_value(res, 1)) + } + mir::BinOp::Shl | mir::BinOp::Shr => { + let lhs_llty = val_ty(lhs); + let rhs_llty = val_ty(rhs); + let invert_mask = common::shift_mask_val(&bx, lhs_llty, rhs_llty, true); + let outer_bits = bx.and(rhs, invert_mask); + + let of = bx.icmp(llvm::IntNE, outer_bits, C_null(rhs_llty)); + let val = self.codegen_scalar_binop(bx, op, lhs, rhs, input_ty); + + (val, of) + } + _ => { + bug!("Operator `{:?}` is not a checkable operator", op) + } + }; + + OperandValue::Pair(val, of) + } + + pub fn rvalue_creates_operand(&self, rvalue: &mir::Rvalue<'tcx>) -> bool { + match *rvalue { + mir::Rvalue::Ref(..) | + mir::Rvalue::Len(..) | + mir::Rvalue::Cast(..) | // (*) + mir::Rvalue::BinaryOp(..) | + mir::Rvalue::CheckedBinaryOp(..) | + mir::Rvalue::UnaryOp(..) | + mir::Rvalue::Discriminant(..) | + mir::Rvalue::NullaryOp(..) | + mir::Rvalue::Use(..) => // (*) + true, + mir::Rvalue::Repeat(..) | + mir::Rvalue::Aggregate(..) => { + let ty = rvalue.ty(self.mir, self.cx.tcx); + let ty = self.monomorphize(&ty); + self.cx.layout_of(ty).is_zst() + } + } + + // (*) this is only true if the type is suitable + } +} + +#[derive(Copy, Clone)] +enum OverflowOp { + Add, Sub, Mul +} + +fn get_overflow_intrinsic(oop: OverflowOp, bx: &Builder, ty: Ty) -> ValueRef { + use syntax::ast::IntTy::*; + use syntax::ast::UintTy::*; + use rustc::ty::{TyInt, TyUint}; + + let tcx = bx.tcx(); + + let new_sty = match ty.sty { + TyInt(Isize) => match &tcx.sess.target.target.target_pointer_width[..] { + "16" => TyInt(I16), + "32" => TyInt(I32), + "64" => TyInt(I64), + _ => panic!("unsupported target word size") + }, + TyUint(Usize) => match &tcx.sess.target.target.target_pointer_width[..] { + "16" => TyUint(U16), + "32" => TyUint(U32), + "64" => TyUint(U64), + _ => panic!("unsupported target word size") + }, + ref t @ TyUint(_) | ref t @ TyInt(_) => t.clone(), + _ => panic!("tried to get overflow intrinsic for op applied to non-int type") + }; + + let name = match oop { + OverflowOp::Add => match new_sty { + TyInt(I8) => "llvm.sadd.with.overflow.i8", + TyInt(I16) => "llvm.sadd.with.overflow.i16", + TyInt(I32) => "llvm.sadd.with.overflow.i32", + TyInt(I64) => "llvm.sadd.with.overflow.i64", + TyInt(I128) => "llvm.sadd.with.overflow.i128", + + TyUint(U8) => "llvm.uadd.with.overflow.i8", + TyUint(U16) => "llvm.uadd.with.overflow.i16", + TyUint(U32) => "llvm.uadd.with.overflow.i32", + TyUint(U64) => "llvm.uadd.with.overflow.i64", + TyUint(U128) => "llvm.uadd.with.overflow.i128", + + _ => unreachable!(), + }, + OverflowOp::Sub => match new_sty { + TyInt(I8) => "llvm.ssub.with.overflow.i8", + TyInt(I16) => "llvm.ssub.with.overflow.i16", + TyInt(I32) => "llvm.ssub.with.overflow.i32", + TyInt(I64) => "llvm.ssub.with.overflow.i64", + TyInt(I128) => "llvm.ssub.with.overflow.i128", + + TyUint(U8) => "llvm.usub.with.overflow.i8", + TyUint(U16) => "llvm.usub.with.overflow.i16", + TyUint(U32) => "llvm.usub.with.overflow.i32", + TyUint(U64) => "llvm.usub.with.overflow.i64", + TyUint(U128) => "llvm.usub.with.overflow.i128", + + _ => unreachable!(), + }, + OverflowOp::Mul => match new_sty { + TyInt(I8) => "llvm.smul.with.overflow.i8", + TyInt(I16) => "llvm.smul.with.overflow.i16", + TyInt(I32) => "llvm.smul.with.overflow.i32", + TyInt(I64) => "llvm.smul.with.overflow.i64", + TyInt(I128) => "llvm.smul.with.overflow.i128", + + TyUint(U8) => "llvm.umul.with.overflow.i8", + TyUint(U16) => "llvm.umul.with.overflow.i16", + TyUint(U32) => "llvm.umul.with.overflow.i32", + TyUint(U64) => "llvm.umul.with.overflow.i64", + TyUint(U128) => "llvm.umul.with.overflow.i128", + + _ => unreachable!(), + }, + }; + + bx.cx.get_intrinsic(&name) +} + +fn cast_int_to_float(bx: &Builder, + signed: bool, + x: ValueRef, + int_ty: Type, + float_ty: Type) -> ValueRef { + // Most integer types, even i128, fit into [-f32::MAX, f32::MAX] after rounding. + // It's only u128 -> f32 that can cause overflows (i.e., should yield infinity). + // LLVM's uitofp produces undef in those cases, so we manually check for that case. + let is_u128_to_f32 = !signed && int_ty.int_width() == 128 && float_ty.float_width() == 32; + if is_u128_to_f32 { + // All inputs greater or equal to (f32::MAX + 0.5 ULP) are rounded to infinity, + // and for everything else LLVM's uitofp works just fine. + use rustc_apfloat::ieee::Single; + use rustc_apfloat::Float; + const MAX_F32_PLUS_HALF_ULP: u128 = ((1 << (Single::PRECISION + 1)) - 1) + << (Single::MAX_EXP - Single::PRECISION as i16); + let max = C_uint_big(int_ty, MAX_F32_PLUS_HALF_ULP); + let overflow = bx.icmp(llvm::IntUGE, x, max); + let infinity_bits = C_u32(bx.cx, ieee::Single::INFINITY.to_bits() as u32); + let infinity = consts::bitcast(infinity_bits, float_ty); + bx.select(overflow, infinity, bx.uitofp(x, float_ty)) + } else { + if signed { + bx.sitofp(x, float_ty) + } else { + bx.uitofp(x, float_ty) + } + } +} + +fn cast_float_to_int(bx: &Builder, + signed: bool, + x: ValueRef, + float_ty: Type, + int_ty: Type) -> ValueRef { + let fptosui_result = if signed { + bx.fptosi(x, int_ty) + } else { + bx.fptoui(x, int_ty) + }; + + if !bx.sess().opts.debugging_opts.saturating_float_casts { + return fptosui_result; + } + // LLVM's fpto[su]i returns undef when the input x is infinite, NaN, or does not fit into the + // destination integer type after rounding towards zero. This `undef` value can cause UB in + // safe code (see issue #10184), so we implement a saturating conversion on top of it: + // Semantically, the mathematical value of the input is rounded towards zero to the next + // mathematical integer, and then the result is clamped into the range of the destination + // integer type. Positive and negative infinity are mapped to the maximum and minimum value of + // the destination integer type. NaN is mapped to 0. + // + // Define f_min and f_max as the largest and smallest (finite) floats that are exactly equal to + // a value representable in int_ty. + // They are exactly equal to int_ty::{MIN,MAX} if float_ty has enough significand bits. + // Otherwise, int_ty::MAX must be rounded towards zero, as it is one less than a power of two. + // int_ty::MIN, however, is either zero or a negative power of two and is thus exactly + // representable. Note that this only works if float_ty's exponent range is sufficiently large. + // f16 or 256 bit integers would break this property. Right now the smallest float type is f32 + // with exponents ranging up to 127, which is barely enough for i128::MIN = -2^127. + // On the other hand, f_max works even if int_ty::MAX is greater than float_ty::MAX. Because + // we're rounding towards zero, we just get float_ty::MAX (which is always an integer). + // This already happens today with u128::MAX = 2^128 - 1 > f32::MAX. + fn compute_clamp_bounds(signed: bool, int_ty: Type) -> (u128, u128) { + let rounded_min = F::from_i128_r(int_min(signed, int_ty), Round::TowardZero); + assert_eq!(rounded_min.status, Status::OK); + let rounded_max = F::from_u128_r(int_max(signed, int_ty), Round::TowardZero); + assert!(rounded_max.value.is_finite()); + (rounded_min.value.to_bits(), rounded_max.value.to_bits()) + } + fn int_max(signed: bool, int_ty: Type) -> u128 { + let shift_amount = 128 - int_ty.int_width(); + if signed { + i128::MAX as u128 >> shift_amount + } else { + u128::MAX >> shift_amount + } + } + fn int_min(signed: bool, int_ty: Type) -> i128 { + if signed { + i128::MIN >> (128 - int_ty.int_width()) + } else { + 0 + } + } + let float_bits_to_llval = |bits| { + let bits_llval = match float_ty.float_width() { + 32 => C_u32(bx.cx, bits as u32), + 64 => C_u64(bx.cx, bits as u64), + n => bug!("unsupported float width {}", n), + }; + consts::bitcast(bits_llval, float_ty) + }; + let (f_min, f_max) = match float_ty.float_width() { + 32 => compute_clamp_bounds::(signed, int_ty), + 64 => compute_clamp_bounds::(signed, int_ty), + n => bug!("unsupported float width {}", n), + }; + let f_min = float_bits_to_llval(f_min); + let f_max = float_bits_to_llval(f_max); + // To implement saturation, we perform the following steps: + // + // 1. Cast x to an integer with fpto[su]i. This may result in undef. + // 2. Compare x to f_min and f_max, and use the comparison results to select: + // a) int_ty::MIN if x < f_min or x is NaN + // b) int_ty::MAX if x > f_max + // c) the result of fpto[su]i otherwise + // 3. If x is NaN, return 0.0, otherwise return the result of step 2. + // + // This avoids resulting undef because values in range [f_min, f_max] by definition fit into the + // destination type. It creates an undef temporary, but *producing* undef is not UB. Our use of + // undef does not introduce any non-determinism either. + // More importantly, the above procedure correctly implements saturating conversion. + // Proof (sketch): + // If x is NaN, 0 is returned by definition. + // Otherwise, x is finite or infinite and thus can be compared with f_min and f_max. + // This yields three cases to consider: + // (1) if x in [f_min, f_max], the result of fpto[su]i is returned, which agrees with + // saturating conversion for inputs in that range. + // (2) if x > f_max, then x is larger than int_ty::MAX. This holds even if f_max is rounded + // (i.e., if f_max < int_ty::MAX) because in those cases, nextUp(f_max) is already larger + // than int_ty::MAX. Because x is larger than int_ty::MAX, the return value of int_ty::MAX + // is correct. + // (3) if x < f_min, then x is smaller than int_ty::MIN. As shown earlier, f_min exactly equals + // int_ty::MIN and therefore the return value of int_ty::MIN is correct. + // QED. + + // Step 1 was already performed above. + + // Step 2: We use two comparisons and two selects, with %s1 being the result: + // %less_or_nan = fcmp ult %x, %f_min + // %greater = fcmp olt %x, %f_max + // %s0 = select %less_or_nan, int_ty::MIN, %fptosi_result + // %s1 = select %greater, int_ty::MAX, %s0 + // Note that %less_or_nan uses an *unordered* comparison. This comparison is true if the + // operands are not comparable (i.e., if x is NaN). The unordered comparison ensures that s1 + // becomes int_ty::MIN if x is NaN. + // Performance note: Unordered comparison can be lowered to a "flipped" comparison and a + // negation, and the negation can be merged into the select. Therefore, it not necessarily any + // more expensive than a ordered ("normal") comparison. Whether these optimizations will be + // performed is ultimately up to the backend, but at least x86 does perform them. + let less_or_nan = bx.fcmp(llvm::RealULT, x, f_min); + let greater = bx.fcmp(llvm::RealOGT, x, f_max); + let int_max = C_uint_big(int_ty, int_max(signed, int_ty)); + let int_min = C_uint_big(int_ty, int_min(signed, int_ty) as u128); + let s0 = bx.select(less_or_nan, int_min, fptosui_result); + let s1 = bx.select(greater, int_max, s0); + + // Step 3: NaN replacement. + // For unsigned types, the above step already yielded int_ty::MIN == 0 if x is NaN. + // Therefore we only need to execute this step for signed integer types. + if signed { + // LLVM has no isNaN predicate, so we use (x == x) instead + bx.select(bx.fcmp(llvm::RealOEQ, x, x), s1, C_uint(int_ty, 0)) + } else { + s1 + } +} diff --git a/src/librustc_codegen_llvm/mir/statement.rs b/src/librustc_codegen_llvm/mir/statement.rs new file mode 100644 index 00000000000..578481df157 --- /dev/null +++ b/src/librustc_codegen_llvm/mir/statement.rs @@ -0,0 +1,91 @@ +// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use rustc::mir; + +use asm; +use builder::Builder; + +use super::FunctionCx; +use super::LocalRef; + +impl<'a, 'tcx> FunctionCx<'a, 'tcx> { + pub fn codegen_statement(&mut self, + bx: Builder<'a, 'tcx>, + statement: &mir::Statement<'tcx>) + -> Builder<'a, 'tcx> { + debug!("codegen_statement(statement={:?})", statement); + + self.set_debug_loc(&bx, statement.source_info); + match statement.kind { + mir::StatementKind::Assign(ref place, ref rvalue) => { + if let mir::Place::Local(index) = *place { + match self.locals[index] { + LocalRef::Place(cg_dest) => { + self.codegen_rvalue(bx, cg_dest, rvalue) + } + LocalRef::Operand(None) => { + let (bx, operand) = self.codegen_rvalue_operand(bx, rvalue); + self.locals[index] = LocalRef::Operand(Some(operand)); + bx + } + LocalRef::Operand(Some(op)) => { + if !op.layout.is_zst() { + span_bug!(statement.source_info.span, + "operand {:?} already assigned", + rvalue); + } + + // If the type is zero-sized, it's already been set here, + // but we still need to make sure we codegen the operand + self.codegen_rvalue_operand(bx, rvalue).0 + } + } + } else { + let cg_dest = self.codegen_place(&bx, place); + self.codegen_rvalue(bx, cg_dest, rvalue) + } + } + mir::StatementKind::SetDiscriminant{ref place, variant_index} => { + self.codegen_place(&bx, place) + .codegen_set_discr(&bx, variant_index); + bx + } + mir::StatementKind::StorageLive(local) => { + if let LocalRef::Place(cg_place) = self.locals[local] { + cg_place.storage_live(&bx); + } + bx + } + mir::StatementKind::StorageDead(local) => { + if let LocalRef::Place(cg_place) = self.locals[local] { + cg_place.storage_dead(&bx); + } + bx + } + mir::StatementKind::InlineAsm { ref asm, ref outputs, ref inputs } => { + let outputs = outputs.iter().map(|output| { + self.codegen_place(&bx, output) + }).collect(); + + let input_vals = inputs.iter().map(|input| { + self.codegen_operand(&bx, input).immediate() + }).collect(); + + asm::codegen_inline_asm(&bx, asm, outputs, input_vals); + bx + } + mir::StatementKind::EndRegion(_) | + mir::StatementKind::Validate(..) | + mir::StatementKind::UserAssertTy(..) | + mir::StatementKind::Nop => bx, + } + } +} diff --git a/src/librustc_codegen_llvm/mono_item.rs b/src/librustc_codegen_llvm/mono_item.rs new file mode 100644 index 00000000000..6ba3582f014 --- /dev/null +++ b/src/librustc_codegen_llvm/mono_item.rs @@ -0,0 +1,193 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Walks the crate looking for items/impl-items/trait-items that have +//! either a `rustc_symbol_name` or `rustc_item_path` attribute and +//! generates an error giving, respectively, the symbol name or +//! item-path. This is used for unit testing the code that generates +//! paths etc in all kinds of annoying scenarios. + +use asm; +use attributes; +use base; +use consts; +use context::CodegenCx; +use declare; +use llvm; +use monomorphize::Instance; +use type_of::LayoutLlvmExt; +use rustc::hir; +use rustc::hir::def::Def; +use rustc::hir::def_id::DefId; +use rustc::mir::mono::{Linkage, Visibility}; +use rustc::ty::TypeFoldable; +use rustc::ty::layout::LayoutOf; +use syntax::attr; +use std::fmt; + +pub use rustc::mir::mono::MonoItem; + +pub use rustc_mir::monomorphize::item::*; +pub use rustc_mir::monomorphize::item::MonoItemExt as BaseMonoItemExt; + +pub trait MonoItemExt<'a, 'tcx>: fmt::Debug + BaseMonoItemExt<'a, 'tcx> { + fn define(&self, cx: &CodegenCx<'a, 'tcx>) { + debug!("BEGIN IMPLEMENTING '{} ({})' in cgu {}", + self.to_string(cx.tcx), + self.to_raw_string(), + cx.codegen_unit.name()); + + match *self.as_mono_item() { + MonoItem::Static(def_id) => { + let tcx = cx.tcx; + let is_mutable = match tcx.describe_def(def_id) { + Some(Def::Static(_, is_mutable)) => is_mutable, + Some(other) => { + bug!("Expected Def::Static, found {:?}", other) + } + None => { + bug!("Expected Def::Static for {:?}, found nothing", def_id) + } + }; + let attrs = tcx.get_attrs(def_id); + + consts::codegen_static(&cx, def_id, is_mutable, &attrs); + } + MonoItem::GlobalAsm(node_id) => { + let item = cx.tcx.hir.expect_item(node_id); + if let hir::ItemGlobalAsm(ref ga) = item.node { + asm::codegen_global_asm(cx, ga); + } else { + span_bug!(item.span, "Mismatch between hir::Item type and MonoItem type") + } + } + MonoItem::Fn(instance) => { + base::codegen_instance(&cx, instance); + } + } + + debug!("END IMPLEMENTING '{} ({})' in cgu {}", + self.to_string(cx.tcx), + self.to_raw_string(), + cx.codegen_unit.name()); + } + + fn predefine(&self, + cx: &CodegenCx<'a, 'tcx>, + linkage: Linkage, + visibility: Visibility) { + debug!("BEGIN PREDEFINING '{} ({})' in cgu {}", + self.to_string(cx.tcx), + self.to_raw_string(), + cx.codegen_unit.name()); + + let symbol_name = self.symbol_name(cx.tcx).as_str(); + + debug!("symbol {}", &symbol_name); + + match *self.as_mono_item() { + MonoItem::Static(def_id) => { + predefine_static(cx, def_id, linkage, visibility, &symbol_name); + } + MonoItem::Fn(instance) => { + predefine_fn(cx, instance, linkage, visibility, &symbol_name); + } + MonoItem::GlobalAsm(..) => {} + } + + debug!("END PREDEFINING '{} ({})' in cgu {}", + self.to_string(cx.tcx), + self.to_raw_string(), + cx.codegen_unit.name()); + } + + fn to_raw_string(&self) -> String { + match *self.as_mono_item() { + MonoItem::Fn(instance) => { + format!("Fn({:?}, {})", + instance.def, + instance.substs.as_ptr() as usize) + } + MonoItem::Static(id) => { + format!("Static({:?})", id) + } + MonoItem::GlobalAsm(id) => { + format!("GlobalAsm({:?})", id) + } + } + } +} + +impl<'a, 'tcx> MonoItemExt<'a, 'tcx> for MonoItem<'tcx> {} + +fn predefine_static<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, + def_id: DefId, + linkage: Linkage, + visibility: Visibility, + symbol_name: &str) { + let instance = Instance::mono(cx.tcx, def_id); + let ty = instance.ty(cx.tcx); + let llty = cx.layout_of(ty).llvm_type(cx); + + let g = declare::define_global(cx, symbol_name, llty).unwrap_or_else(|| { + cx.sess().span_fatal(cx.tcx.def_span(def_id), + &format!("symbol `{}` is already defined", symbol_name)) + }); + + unsafe { + llvm::LLVMRustSetLinkage(g, base::linkage_to_llvm(linkage)); + llvm::LLVMRustSetVisibility(g, base::visibility_to_llvm(visibility)); + } + + cx.instances.borrow_mut().insert(instance, g); + cx.statics.borrow_mut().insert(g, def_id); +} + +fn predefine_fn<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, + instance: Instance<'tcx>, + linkage: Linkage, + visibility: Visibility, + symbol_name: &str) { + assert!(!instance.substs.needs_infer() && + !instance.substs.has_param_types()); + + let mono_ty = instance.ty(cx.tcx); + let attrs = instance.def.attrs(cx.tcx); + let lldecl = declare::declare_fn(cx, symbol_name, mono_ty); + unsafe { llvm::LLVMRustSetLinkage(lldecl, base::linkage_to_llvm(linkage)) }; + base::set_link_section(cx, lldecl, &attrs); + if linkage == Linkage::LinkOnceODR || + linkage == Linkage::WeakODR { + llvm::SetUniqueComdat(cx.llmod, lldecl); + } + + // If we're compiling the compiler-builtins crate, e.g. the equivalent of + // compiler-rt, then we want to implicitly compile everything with hidden + // visibility as we're going to link this object all over the place but + // don't want the symbols to get exported. + if linkage != Linkage::Internal && linkage != Linkage::Private && + attr::contains_name(cx.tcx.hir.krate_attrs(), "compiler_builtins") { + unsafe { + llvm::LLVMRustSetVisibility(lldecl, llvm::Visibility::Hidden); + } + } else { + unsafe { + llvm::LLVMRustSetVisibility(lldecl, base::visibility_to_llvm(visibility)); + } + } + + debug!("predefine_fn: mono_ty = {:?} instance = {:?}", mono_ty, instance); + if instance.def.is_inline(cx.tcx) { + attributes::inline(lldecl, attributes::InlineAttr::Hint); + } + attributes::from_fn_attrs(cx, lldecl, instance.def.def_id()); + + cx.instances.borrow_mut().insert(instance, lldecl); +} diff --git a/src/librustc_codegen_llvm/time_graph.rs b/src/librustc_codegen_llvm/time_graph.rs new file mode 100644 index 00000000000..a8502682a80 --- /dev/null +++ b/src/librustc_codegen_llvm/time_graph.rs @@ -0,0 +1,278 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use std::collections::HashMap; +use std::fs::File; +use std::io::prelude::*; +use std::marker::PhantomData; +use std::mem; +use std::sync::{Arc, Mutex}; +use std::time::Instant; + +const OUTPUT_WIDTH_IN_PX: u64 = 1000; +const TIME_LINE_HEIGHT_IN_PX: u64 = 20; +const TIME_LINE_HEIGHT_STRIDE_IN_PX: usize = 30; + +#[derive(Clone)] +struct Timing { + start: Instant, + end: Instant, + work_package_kind: WorkPackageKind, + name: String, + events: Vec<(String, Instant)>, +} + +#[derive(Clone, Copy, Hash, Eq, PartialEq, Debug)] +pub struct TimelineId(pub usize); + +#[derive(Clone)] +struct PerThread { + timings: Vec, + open_work_package: Option<(Instant, WorkPackageKind, String)>, +} + +#[derive(Clone)] +pub struct TimeGraph { + data: Arc>>, +} + +#[derive(Clone, Copy)] +pub struct WorkPackageKind(pub &'static [&'static str]); + +pub struct Timeline { + token: Option, +} + +struct RaiiToken { + graph: TimeGraph, + timeline: TimelineId, + events: Vec<(String, Instant)>, + // The token must not be Send: + _marker: PhantomData<*const ()> +} + + +impl Drop for RaiiToken { + fn drop(&mut self) { + self.graph.end(self.timeline, mem::replace(&mut self.events, Vec::new())); + } +} + +impl TimeGraph { + pub fn new() -> TimeGraph { + TimeGraph { + data: Arc::new(Mutex::new(HashMap::new())) + } + } + + pub fn start(&self, + timeline: TimelineId, + work_package_kind: WorkPackageKind, + name: &str) -> Timeline { + { + let mut table = self.data.lock().unwrap(); + + let data = table.entry(timeline).or_insert(PerThread { + timings: Vec::new(), + open_work_package: None, + }); + + assert!(data.open_work_package.is_none()); + data.open_work_package = Some((Instant::now(), work_package_kind, name.to_string())); + } + + Timeline { + token: Some(RaiiToken { + graph: self.clone(), + timeline, + events: Vec::new(), + _marker: PhantomData, + }), + } + } + + fn end(&self, timeline: TimelineId, events: Vec<(String, Instant)>) { + let end = Instant::now(); + + let mut table = self.data.lock().unwrap(); + let data = table.get_mut(&timeline).unwrap(); + + if let Some((start, work_package_kind, name)) = data.open_work_package.take() { + data.timings.push(Timing { + start, + end, + work_package_kind, + name, + events, + }); + } else { + bug!("end timing without start?") + } + } + + pub fn dump(&self, output_filename: &str) { + let table = self.data.lock().unwrap(); + + for data in table.values() { + assert!(data.open_work_package.is_none()); + } + + let mut threads: Vec = + table.values().map(|data| data.clone()).collect(); + + threads.sort_by_key(|timeline| timeline.timings[0].start); + + let earliest_instant = threads[0].timings[0].start; + let latest_instant = threads.iter() + .map(|timeline| timeline.timings + .last() + .unwrap() + .end) + .max() + .unwrap(); + let max_distance = distance(earliest_instant, latest_instant); + + let mut file = File::create(format!("{}.html", output_filename)).unwrap(); + + writeln!(file, " + + + + + +
+ ", + total_height = threads.len() * TIME_LINE_HEIGHT_STRIDE_IN_PX, + width = OUTPUT_WIDTH_IN_PX, + ).unwrap(); + + let mut color = 0; + for (line_index, thread) in threads.iter().enumerate() { + let line_top = line_index * TIME_LINE_HEIGHT_STRIDE_IN_PX; + + for span in &thread.timings { + let start = distance(earliest_instant, span.start); + let end = distance(earliest_instant, span.end); + + let start = normalize(start, max_distance, OUTPUT_WIDTH_IN_PX); + let end = normalize(end, max_distance, OUTPUT_WIDTH_IN_PX); + + let colors = span.work_package_kind.0; + + writeln!(file, "{}", + color, + line_top, + start, + end - start, + TIME_LINE_HEIGHT_IN_PX, + colors[color % colors.len()], + span.name, + ).unwrap(); + + color += 1; + } + } + + writeln!(file, " +
+ ").unwrap(); + + let mut idx = 0; + for thread in threads.iter() { + for timing in &thread.timings { + let colors = timing.work_package_kind.0; + let height = TIME_LINE_HEIGHT_STRIDE_IN_PX * timing.events.len(); + writeln!(file, "
", + idx, + colors[idx % colors.len()], + height).unwrap(); + idx += 1; + let max = distance(timing.start, timing.end); + for (i, &(ref event, time)) in timing.events.iter().enumerate() { + let i = i as u64; + let time = distance(timing.start, time); + let at = normalize(time, max, OUTPUT_WIDTH_IN_PX); + writeln!(file, "{}", + at, + TIME_LINE_HEIGHT_IN_PX * i, + event).unwrap(); + } + writeln!(file, "
").unwrap(); + } + } + + writeln!(file, " + + + ").unwrap(); + } +} + +impl Timeline { + pub fn noop() -> Timeline { + Timeline { token: None } + } + + /// Record an event which happened at this moment on this timeline. + /// + /// Events are displayed in the eventual HTML output where you can click on + /// a particular timeline and it'll expand to all of the events that + /// happened on that timeline. This can then be used to drill into a + /// particular timeline and see what events are happening and taking the + /// most time. + pub fn record(&mut self, name: &str) { + if let Some(ref mut token) = self.token { + token.events.push((name.to_string(), Instant::now())); + } + } +} + +fn distance(zero: Instant, x: Instant) -> u64 { + + let duration = x.duration_since(zero); + (duration.as_secs() * 1_000_000_000 + duration.subsec_nanos() as u64) // / div +} + +fn normalize(distance: u64, max: u64, max_pixels: u64) -> u64 { + (max_pixels * distance) / max +} + diff --git a/src/librustc_codegen_llvm/type_.rs b/src/librustc_codegen_llvm/type_.rs new file mode 100644 index 00000000000..a77acc4f175 --- /dev/null +++ b/src/librustc_codegen_llvm/type_.rs @@ -0,0 +1,300 @@ +// Copyright 2013 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. + +#![allow(non_upper_case_globals)] + +use llvm; +use llvm::{ContextRef, TypeRef, Bool, False, True, TypeKind}; +use llvm::{Float, Double, X86_FP80, PPC_FP128, FP128}; + +use context::CodegenCx; + +use syntax::ast; +use rustc::ty::layout::{self, Align, Size}; + +use std::ffi::CString; +use std::fmt; +use std::mem; +use std::ptr; + +use libc::c_uint; + +#[derive(Clone, Copy, PartialEq)] +#[repr(C)] +pub struct Type { + rf: TypeRef +} + +impl fmt::Debug for Type { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.write_str(&llvm::build_string(|s| unsafe { + llvm::LLVMRustWriteTypeToString(self.to_ref(), s); + }).expect("non-UTF8 type description from LLVM")) + } +} + +macro_rules! ty { + ($e:expr) => ( Type::from_ref(unsafe { $e })) +} + +/// Wrapper for LLVM TypeRef +impl Type { + #[inline(always)] + pub fn from_ref(r: TypeRef) -> Type { + Type { + rf: r + } + } + + #[inline(always)] // So it doesn't kill --opt-level=0 builds of the compiler + pub fn to_ref(&self) -> TypeRef { + self.rf + } + + pub fn to_ref_slice(slice: &[Type]) -> &[TypeRef] { + unsafe { mem::transmute(slice) } + } + + pub fn void(cx: &CodegenCx) -> Type { + ty!(llvm::LLVMVoidTypeInContext(cx.llcx)) + } + + pub fn metadata(cx: &CodegenCx) -> Type { + ty!(llvm::LLVMRustMetadataTypeInContext(cx.llcx)) + } + + pub fn i1(cx: &CodegenCx) -> Type { + ty!(llvm::LLVMInt1TypeInContext(cx.llcx)) + } + + pub fn i8(cx: &CodegenCx) -> Type { + ty!(llvm::LLVMInt8TypeInContext(cx.llcx)) + } + + pub fn i8_llcx(llcx: ContextRef) -> Type { + ty!(llvm::LLVMInt8TypeInContext(llcx)) + } + + pub fn i16(cx: &CodegenCx) -> Type { + ty!(llvm::LLVMInt16TypeInContext(cx.llcx)) + } + + pub fn i32(cx: &CodegenCx) -> Type { + ty!(llvm::LLVMInt32TypeInContext(cx.llcx)) + } + + pub fn i64(cx: &CodegenCx) -> Type { + ty!(llvm::LLVMInt64TypeInContext(cx.llcx)) + } + + pub fn i128(cx: &CodegenCx) -> Type { + ty!(llvm::LLVMIntTypeInContext(cx.llcx, 128)) + } + + // Creates an integer type with the given number of bits, e.g. i24 + pub fn ix(cx: &CodegenCx, num_bits: u64) -> Type { + ty!(llvm::LLVMIntTypeInContext(cx.llcx, num_bits as c_uint)) + } + + pub fn f32(cx: &CodegenCx) -> Type { + ty!(llvm::LLVMFloatTypeInContext(cx.llcx)) + } + + pub fn f64(cx: &CodegenCx) -> Type { + ty!(llvm::LLVMDoubleTypeInContext(cx.llcx)) + } + + pub fn bool(cx: &CodegenCx) -> Type { + Type::i8(cx) + } + + pub fn char(cx: &CodegenCx) -> Type { + Type::i32(cx) + } + + pub fn i8p(cx: &CodegenCx) -> Type { + Type::i8(cx).ptr_to() + } + + pub fn i8p_llcx(llcx: ContextRef) -> Type { + Type::i8_llcx(llcx).ptr_to() + } + + pub fn isize(cx: &CodegenCx) -> Type { + match &cx.tcx.sess.target.target.target_pointer_width[..] { + "16" => Type::i16(cx), + "32" => Type::i32(cx), + "64" => Type::i64(cx), + tws => bug!("Unsupported target word size for int: {}", tws), + } + } + + pub fn c_int(cx: &CodegenCx) -> Type { + match &cx.tcx.sess.target.target.target_c_int_width[..] { + "16" => Type::i16(cx), + "32" => Type::i32(cx), + "64" => Type::i64(cx), + width => bug!("Unsupported target_c_int_width: {}", width), + } + } + + pub fn int_from_ty(cx: &CodegenCx, t: ast::IntTy) -> Type { + match t { + ast::IntTy::Isize => cx.isize_ty, + ast::IntTy::I8 => Type::i8(cx), + ast::IntTy::I16 => Type::i16(cx), + ast::IntTy::I32 => Type::i32(cx), + ast::IntTy::I64 => Type::i64(cx), + ast::IntTy::I128 => Type::i128(cx), + } + } + + pub fn uint_from_ty(cx: &CodegenCx, t: ast::UintTy) -> Type { + match t { + ast::UintTy::Usize => cx.isize_ty, + ast::UintTy::U8 => Type::i8(cx), + ast::UintTy::U16 => Type::i16(cx), + ast::UintTy::U32 => Type::i32(cx), + ast::UintTy::U64 => Type::i64(cx), + ast::UintTy::U128 => Type::i128(cx), + } + } + + pub fn float_from_ty(cx: &CodegenCx, t: ast::FloatTy) -> Type { + match t { + ast::FloatTy::F32 => Type::f32(cx), + ast::FloatTy::F64 => Type::f64(cx), + } + } + + pub fn func(args: &[Type], ret: &Type) -> Type { + let slice: &[TypeRef] = Type::to_ref_slice(args); + ty!(llvm::LLVMFunctionType(ret.to_ref(), slice.as_ptr(), + args.len() as c_uint, False)) + } + + pub fn variadic_func(args: &[Type], ret: &Type) -> Type { + let slice: &[TypeRef] = Type::to_ref_slice(args); + ty!(llvm::LLVMFunctionType(ret.to_ref(), slice.as_ptr(), + args.len() as c_uint, True)) + } + + pub fn struct_(cx: &CodegenCx, els: &[Type], packed: bool) -> Type { + let els: &[TypeRef] = Type::to_ref_slice(els); + ty!(llvm::LLVMStructTypeInContext(cx.llcx, els.as_ptr(), + els.len() as c_uint, + packed as Bool)) + } + + pub fn named_struct(cx: &CodegenCx, name: &str) -> Type { + let name = CString::new(name).unwrap(); + ty!(llvm::LLVMStructCreateNamed(cx.llcx, name.as_ptr())) + } + + + pub fn array(ty: &Type, len: u64) -> Type { + ty!(llvm::LLVMRustArrayType(ty.to_ref(), len)) + } + + pub fn vector(ty: &Type, len: u64) -> Type { + ty!(llvm::LLVMVectorType(ty.to_ref(), len as c_uint)) + } + + pub fn kind(&self) -> TypeKind { + unsafe { + llvm::LLVMRustGetTypeKind(self.to_ref()) + } + } + + pub fn set_struct_body(&mut self, els: &[Type], packed: bool) { + let slice: &[TypeRef] = Type::to_ref_slice(els); + unsafe { + llvm::LLVMStructSetBody(self.to_ref(), slice.as_ptr(), + els.len() as c_uint, packed as Bool) + } + } + + pub fn ptr_to(&self) -> Type { + ty!(llvm::LLVMPointerType(self.to_ref(), 0)) + } + + pub fn element_type(&self) -> Type { + unsafe { + Type::from_ref(llvm::LLVMGetElementType(self.to_ref())) + } + } + + /// Return the number of elements in `self` if it is a LLVM vector type. + pub fn vector_length(&self) -> usize { + unsafe { + llvm::LLVMGetVectorSize(self.to_ref()) as usize + } + } + + pub fn func_params(&self) -> Vec { + unsafe { + let n_args = llvm::LLVMCountParamTypes(self.to_ref()) as usize; + let mut args = vec![Type { rf: ptr::null_mut() }; n_args]; + llvm::LLVMGetParamTypes(self.to_ref(), + args.as_mut_ptr() as *mut TypeRef); + args + } + } + + pub fn float_width(&self) -> usize { + match self.kind() { + Float => 32, + Double => 64, + X86_FP80 => 80, + FP128 | PPC_FP128 => 128, + _ => bug!("llvm_float_width called on a non-float type") + } + } + + /// Retrieve the bit width of the integer type `self`. + pub fn int_width(&self) -> u64 { + unsafe { + llvm::LLVMGetIntTypeWidth(self.to_ref()) as u64 + } + } + + pub fn from_integer(cx: &CodegenCx, i: layout::Integer) -> Type { + use rustc::ty::layout::Integer::*; + match i { + I8 => Type::i8(cx), + I16 => Type::i16(cx), + I32 => Type::i32(cx), + I64 => Type::i64(cx), + I128 => Type::i128(cx), + } + } + + /// Return a LLVM type that has at most the required alignment, + /// as a conservative approximation for unknown pointee types. + pub fn pointee_for_abi_align(cx: &CodegenCx, align: Align) -> Type { + // FIXME(eddyb) We could find a better approximation if ity.align < align. + let ity = layout::Integer::approximate_abi_align(cx, align); + Type::from_integer(cx, ity) + } + + /// Return a LLVM type that has at most the required alignment, + /// and exactly the required size, as a best-effort padding array. + pub fn padding_filler(cx: &CodegenCx, size: Size, align: Align) -> Type { + let unit = layout::Integer::approximate_abi_align(cx, align); + let size = size.bytes(); + let unit_size = unit.size().bytes(); + assert_eq!(size % unit_size, 0); + Type::array(&Type::from_integer(cx, unit), size / unit_size) + } + + pub fn x86_mmx(cx: &CodegenCx) -> Type { + ty!(llvm::LLVMX86MMXTypeInContext(cx.llcx)) + } +} diff --git a/src/librustc_codegen_llvm/type_of.rs b/src/librustc_codegen_llvm/type_of.rs new file mode 100644 index 00000000000..5f186e7514e --- /dev/null +++ b/src/librustc_codegen_llvm/type_of.rs @@ -0,0 +1,506 @@ +// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use abi::{FnType, FnTypeExt}; +use common::*; +use rustc::hir; +use rustc::ty::{self, Ty, TypeFoldable}; +use rustc::ty::layout::{self, Align, LayoutOf, Size, TyLayout}; +use rustc_target::spec::PanicStrategy; +use mono_item::DefPathBasedNames; +use type_::Type; + +use std::fmt::Write; + +fn uncached_llvm_type<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, + layout: TyLayout<'tcx>, + defer: &mut Option<(Type, TyLayout<'tcx>)>) + -> Type { + match layout.abi { + layout::Abi::Scalar(_) => bug!("handled elsewhere"), + layout::Abi::Vector { ref element, count } => { + // LLVM has a separate type for 64-bit SIMD vectors on X86 called + // `x86_mmx` which is needed for some SIMD operations. As a bit of a + // hack (all SIMD definitions are super unstable anyway) we + // recognize any one-element SIMD vector as "this should be an + // x86_mmx" type. In general there shouldn't be a need for other + // one-element SIMD vectors, so it's assumed this won't clash with + // much else. + let use_x86_mmx = count == 1 && layout.size.bits() == 64 && + (cx.sess().target.target.arch == "x86" || + cx.sess().target.target.arch == "x86_64"); + if use_x86_mmx { + return Type::x86_mmx(cx) + } else { + let element = layout.scalar_llvm_type_at(cx, element, Size::from_bytes(0)); + return Type::vector(&element, count); + } + } + layout::Abi::ScalarPair(..) => { + return Type::struct_(cx, &[ + layout.scalar_pair_element_llvm_type(cx, 0), + layout.scalar_pair_element_llvm_type(cx, 1), + ], false); + } + layout::Abi::Uninhabited | + layout::Abi::Aggregate { .. } => {} + } + + let name = match layout.ty.sty { + ty::TyClosure(..) | + ty::TyGenerator(..) | + ty::TyAdt(..) | + // FIXME(eddyb) producing readable type names for trait objects can result + // in problematically distinct types due to HRTB and subtyping (see #47638). + // ty::TyDynamic(..) | + ty::TyForeign(..) | + ty::TyStr => { + let mut name = String::with_capacity(32); + let printer = DefPathBasedNames::new(cx.tcx, true, true); + printer.push_type_name(layout.ty, &mut name); + match (&layout.ty.sty, &layout.variants) { + (&ty::TyAdt(def, _), &layout::Variants::Single { index }) => { + if def.is_enum() && !def.variants.is_empty() { + write!(&mut name, "::{}", def.variants[index].name).unwrap(); + } + } + _ => {} + } + Some(name) + } + _ => None + }; + + match layout.fields { + layout::FieldPlacement::Union(_) => { + let fill = Type::padding_filler(cx, layout.size, layout.align); + let packed = false; + match name { + None => { + Type::struct_(cx, &[fill], packed) + } + Some(ref name) => { + let mut llty = Type::named_struct(cx, name); + llty.set_struct_body(&[fill], packed); + llty + } + } + } + layout::FieldPlacement::Array { count, .. } => { + Type::array(&layout.field(cx, 0).llvm_type(cx), count) + } + layout::FieldPlacement::Arbitrary { .. } => { + match name { + None => { + let (llfields, packed) = struct_llfields(cx, layout); + Type::struct_(cx, &llfields, packed) + } + Some(ref name) => { + let llty = Type::named_struct(cx, name); + *defer = Some((llty, layout)); + llty + } + } + } + } +} + +fn struct_llfields<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, + layout: TyLayout<'tcx>) + -> (Vec, bool) { + debug!("struct_llfields: {:#?}", layout); + let field_count = layout.fields.count(); + + let mut packed = false; + let mut offset = Size::from_bytes(0); + let mut prev_align = layout.align; + let mut result: Vec = Vec::with_capacity(1 + field_count * 2); + for i in layout.fields.index_by_increasing_offset() { + let field = layout.field(cx, i); + packed |= layout.align.abi() < field.align.abi(); + + let target_offset = layout.fields.offset(i as usize); + debug!("struct_llfields: {}: {:?} offset: {:?} target_offset: {:?}", + i, field, offset, target_offset); + assert!(target_offset >= offset); + let padding = target_offset - offset; + let padding_align = layout.align.min(prev_align).min(field.align); + assert_eq!(offset.abi_align(padding_align) + padding, target_offset); + result.push(Type::padding_filler(cx, padding, padding_align)); + debug!(" padding before: {:?}", padding); + + result.push(field.llvm_type(cx)); + offset = target_offset + field.size; + prev_align = field.align; + } + if !layout.is_unsized() && field_count > 0 { + if offset > layout.size { + bug!("layout: {:#?} stride: {:?} offset: {:?}", + layout, layout.size, offset); + } + let padding = layout.size - offset; + let padding_align = layout.align.min(prev_align); + assert_eq!(offset.abi_align(padding_align) + padding, layout.size); + debug!("struct_llfields: pad_bytes: {:?} offset: {:?} stride: {:?}", + padding, offset, layout.size); + result.push(Type::padding_filler(cx, padding, padding_align)); + assert!(result.len() == 1 + field_count * 2); + } else { + debug!("struct_llfields: offset: {:?} stride: {:?}", + offset, layout.size); + } + + (result, packed) +} + +impl<'a, 'tcx> CodegenCx<'a, 'tcx> { + pub fn align_of(&self, ty: Ty<'tcx>) -> Align { + self.layout_of(ty).align + } + + pub fn size_of(&self, ty: Ty<'tcx>) -> Size { + self.layout_of(ty).size + } + + pub fn size_and_align_of(&self, ty: Ty<'tcx>) -> (Size, Align) { + self.layout_of(ty).size_and_align() + } +} + +#[derive(Copy, Clone, PartialEq, Eq)] +pub enum PointerKind { + /// Most general case, we know no restrictions to tell LLVM. + Shared, + + /// `&T` where `T` contains no `UnsafeCell`, is `noalias` and `readonly`. + Frozen, + + /// `&mut T`, when we know `noalias` is safe for LLVM. + UniqueBorrowed, + + /// `Box`, unlike `UniqueBorrowed`, it also has `noalias` on returns. + UniqueOwned +} + +#[derive(Copy, Clone)] +pub struct PointeeInfo { + pub size: Size, + pub align: Align, + pub safe: Option, +} + +pub trait LayoutLlvmExt<'tcx> { + fn is_llvm_immediate(&self) -> bool; + fn is_llvm_scalar_pair<'a>(&self) -> bool; + fn llvm_type<'a>(&self, cx: &CodegenCx<'a, 'tcx>) -> Type; + fn immediate_llvm_type<'a>(&self, cx: &CodegenCx<'a, 'tcx>) -> Type; + fn scalar_llvm_type_at<'a>(&self, cx: &CodegenCx<'a, 'tcx>, + scalar: &layout::Scalar, offset: Size) -> Type; + fn scalar_pair_element_llvm_type<'a>(&self, cx: &CodegenCx<'a, 'tcx>, + index: usize) -> Type; + fn llvm_field_index(&self, index: usize) -> u64; + fn pointee_info_at<'a>(&self, cx: &CodegenCx<'a, 'tcx>, offset: Size) + -> Option; +} + +impl<'tcx> LayoutLlvmExt<'tcx> for TyLayout<'tcx> { + fn is_llvm_immediate(&self) -> bool { + match self.abi { + layout::Abi::Scalar(_) | + layout::Abi::Vector { .. } => true, + layout::Abi::ScalarPair(..) => false, + layout::Abi::Uninhabited | + layout::Abi::Aggregate { .. } => self.is_zst() + } + } + + fn is_llvm_scalar_pair<'a>(&self) -> bool { + match self.abi { + layout::Abi::ScalarPair(..) => true, + layout::Abi::Uninhabited | + layout::Abi::Scalar(_) | + layout::Abi::Vector { .. } | + layout::Abi::Aggregate { .. } => false + } + } + + /// Get the LLVM type corresponding to a Rust type, i.e. `rustc::ty::Ty`. + /// The pointee type of the pointer in `PlaceRef` is always this type. + /// For sized types, it is also the right LLVM type for an `alloca` + /// containing a value of that type, and most immediates (except `bool`). + /// Unsized types, however, are represented by a "minimal unit", e.g. + /// `[T]` becomes `T`, while `str` and `Trait` turn into `i8` - this + /// is useful for indexing slices, as `&[T]`'s data pointer is `T*`. + /// If the type is an unsized struct, the regular layout is generated, + /// with the inner-most trailing unsized field using the "minimal unit" + /// of that field's type - this is useful for taking the address of + /// that field and ensuring the struct has the right alignment. + fn llvm_type<'a>(&self, cx: &CodegenCx<'a, 'tcx>) -> Type { + if let layout::Abi::Scalar(ref scalar) = self.abi { + // Use a different cache for scalars because pointers to DSTs + // can be either fat or thin (data pointers of fat pointers). + if let Some(&llty) = cx.scalar_lltypes.borrow().get(&self.ty) { + return llty; + } + let llty = match self.ty.sty { + ty::TyRef(_, ty, _) | + ty::TyRawPtr(ty::TypeAndMut { ty, .. }) => { + cx.layout_of(ty).llvm_type(cx).ptr_to() + } + ty::TyAdt(def, _) if def.is_box() => { + cx.layout_of(self.ty.boxed_ty()).llvm_type(cx).ptr_to() + } + ty::TyFnPtr(sig) => { + let sig = cx.tcx.normalize_erasing_late_bound_regions( + ty::ParamEnv::reveal_all(), + &sig, + ); + FnType::new(cx, sig, &[]).llvm_type(cx).ptr_to() + } + _ => self.scalar_llvm_type_at(cx, scalar, Size::from_bytes(0)) + }; + cx.scalar_lltypes.borrow_mut().insert(self.ty, llty); + return llty; + } + + + // Check the cache. + let variant_index = match self.variants { + layout::Variants::Single { index } => Some(index), + _ => None + }; + if let Some(&llty) = cx.lltypes.borrow().get(&(self.ty, variant_index)) { + return llty; + } + + debug!("llvm_type({:#?})", self); + + assert!(!self.ty.has_escaping_regions(), "{:?} has escaping regions", self.ty); + + // Make sure lifetimes are erased, to avoid generating distinct LLVM + // types for Rust types that only differ in the choice of lifetimes. + let normal_ty = cx.tcx.erase_regions(&self.ty); + + let mut defer = None; + let llty = if self.ty != normal_ty { + let mut layout = cx.layout_of(normal_ty); + if let Some(v) = variant_index { + layout = layout.for_variant(cx, v); + } + layout.llvm_type(cx) + } else { + uncached_llvm_type(cx, *self, &mut defer) + }; + debug!("--> mapped {:#?} to llty={:?}", self, llty); + + cx.lltypes.borrow_mut().insert((self.ty, variant_index), llty); + + if let Some((mut llty, layout)) = defer { + let (llfields, packed) = struct_llfields(cx, layout); + llty.set_struct_body(&llfields, packed) + } + + llty + } + + fn immediate_llvm_type<'a>(&self, cx: &CodegenCx<'a, 'tcx>) -> Type { + if let layout::Abi::Scalar(ref scalar) = self.abi { + if scalar.is_bool() { + return Type::i1(cx); + } + } + self.llvm_type(cx) + } + + fn scalar_llvm_type_at<'a>(&self, cx: &CodegenCx<'a, 'tcx>, + scalar: &layout::Scalar, offset: Size) -> Type { + match scalar.value { + layout::Int(i, _) => Type::from_integer(cx, i), + layout::F32 => Type::f32(cx), + layout::F64 => Type::f64(cx), + layout::Pointer => { + // If we know the alignment, pick something better than i8. + let pointee = if let Some(pointee) = self.pointee_info_at(cx, offset) { + Type::pointee_for_abi_align(cx, pointee.align) + } else { + Type::i8(cx) + }; + pointee.ptr_to() + } + } + } + + fn scalar_pair_element_llvm_type<'a>(&self, cx: &CodegenCx<'a, 'tcx>, + index: usize) -> Type { + // HACK(eddyb) special-case fat pointers until LLVM removes + // pointee types, to avoid bitcasting every `OperandRef::deref`. + match self.ty.sty { + ty::TyRef(..) | + ty::TyRawPtr(_) => { + return self.field(cx, index).llvm_type(cx); + } + ty::TyAdt(def, _) if def.is_box() => { + let ptr_ty = cx.tcx.mk_mut_ptr(self.ty.boxed_ty()); + return cx.layout_of(ptr_ty).scalar_pair_element_llvm_type(cx, index); + } + _ => {} + } + + let (a, b) = match self.abi { + layout::Abi::ScalarPair(ref a, ref b) => (a, b), + _ => bug!("TyLayout::scalar_pair_element_llty({:?}): not applicable", self) + }; + let scalar = [a, b][index]; + + // Make sure to return the same type `immediate_llvm_type` would, + // to avoid dealing with two types and the associated conversions. + // This means that `(bool, bool)` is represented as `{i1, i1}`, + // both in memory and as an immediate, while `bool` is typically + // `i8` in memory and only `i1` when immediate. While we need to + // load/store `bool` as `i8` to avoid crippling LLVM optimizations, + // `i1` in a LLVM aggregate is valid and mostly equivalent to `i8`. + if scalar.is_bool() { + return Type::i1(cx); + } + + let offset = if index == 0 { + Size::from_bytes(0) + } else { + a.value.size(cx).abi_align(b.value.align(cx)) + }; + self.scalar_llvm_type_at(cx, scalar, offset) + } + + fn llvm_field_index(&self, index: usize) -> u64 { + match self.abi { + layout::Abi::Scalar(_) | + layout::Abi::ScalarPair(..) => { + bug!("TyLayout::llvm_field_index({:?}): not applicable", self) + } + _ => {} + } + match self.fields { + layout::FieldPlacement::Union(_) => { + bug!("TyLayout::llvm_field_index({:?}): not applicable", self) + } + + layout::FieldPlacement::Array { .. } => { + index as u64 + } + + layout::FieldPlacement::Arbitrary { .. } => { + 1 + (self.fields.memory_index(index) as u64) * 2 + } + } + } + + fn pointee_info_at<'a>(&self, cx: &CodegenCx<'a, 'tcx>, offset: Size) + -> Option { + if let Some(&pointee) = cx.pointee_infos.borrow().get(&(self.ty, offset)) { + return pointee; + } + + let mut result = None; + match self.ty.sty { + ty::TyRawPtr(mt) if offset.bytes() == 0 => { + let (size, align) = cx.size_and_align_of(mt.ty); + result = Some(PointeeInfo { + size, + align, + safe: None + }); + } + + ty::TyRef(_, ty, mt) if offset.bytes() == 0 => { + let (size, align) = cx.size_and_align_of(ty); + + let kind = match mt { + hir::MutImmutable => if cx.type_is_freeze(ty) { + PointerKind::Frozen + } else { + PointerKind::Shared + }, + hir::MutMutable => { + if cx.tcx.sess.opts.debugging_opts.mutable_noalias || + cx.tcx.sess.panic_strategy() == PanicStrategy::Abort { + PointerKind::UniqueBorrowed + } else { + PointerKind::Shared + } + } + }; + + result = Some(PointeeInfo { + size, + align, + safe: Some(kind) + }); + } + + _ => { + let mut data_variant = match self.variants { + layout::Variants::NicheFilling { dataful_variant, .. } => { + // Only the niche itself is always initialized, + // so only check for a pointer at its offset. + // + // If the niche is a pointer, it's either valid + // (according to its type), or null (which the + // niche field's scalar validity range encodes). + // This allows using `dereferenceable_or_null` + // for e.g. `Option<&T>`, and this will continue + // to work as long as we don't start using more + // niches than just null (e.g. the first page + // of the address space, or unaligned pointers). + if self.fields.offset(0) == offset { + Some(self.for_variant(cx, dataful_variant)) + } else { + None + } + } + _ => Some(*self) + }; + + if let Some(variant) = data_variant { + // We're not interested in any unions. + if let layout::FieldPlacement::Union(_) = variant.fields { + data_variant = None; + } + } + + if let Some(variant) = data_variant { + let ptr_end = offset + layout::Pointer.size(cx); + for i in 0..variant.fields.count() { + let field_start = variant.fields.offset(i); + if field_start <= offset { + let field = variant.field(cx, i); + if ptr_end <= field_start + field.size { + // We found the right field, look inside it. + result = field.pointee_info_at(cx, offset - field_start); + break; + } + } + } + } + + // FIXME(eddyb) This should be for `ptr::Unique`, not `Box`. + if let Some(ref mut pointee) = result { + if let ty::TyAdt(def, _) = self.ty.sty { + if def.is_box() && offset.bytes() == 0 { + pointee.safe = Some(PointerKind::UniqueOwned); + } + } + } + } + } + + cx.pointee_infos.borrow_mut().insert((self.ty, offset), result); + result + } +} diff --git a/src/librustc_codegen_llvm/value.rs b/src/librustc_codegen_llvm/value.rs new file mode 100644 index 00000000000..287ad87caac --- /dev/null +++ b/src/librustc_codegen_llvm/value.rs @@ -0,0 +1,24 @@ +// Copyright 2013 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use llvm; + +use std::fmt; + +#[derive(Copy, Clone, PartialEq)] +pub struct Value(pub llvm::ValueRef); + +impl fmt::Debug for Value { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.write_str(&llvm::build_string(|s| unsafe { + llvm::LLVMRustWriteValueToString(self.0, s); + }).expect("nun-UTF8 value description from LLVM")) + } +} diff --git a/src/librustc_codegen_utils/Cargo.toml b/src/librustc_codegen_utils/Cargo.toml new file mode 100644 index 00000000000..690fb260390 --- /dev/null +++ b/src/librustc_codegen_utils/Cargo.toml @@ -0,0 +1,23 @@ +[package] +authors = ["The Rust Project Developers"] +name = "rustc_codegen_utils" +version = "0.0.0" + +[lib] +name = "rustc_codegen_utils" +path = "lib.rs" +crate-type = ["dylib"] +test = false + +[dependencies] +ar = "0.3.0" +flate2 = "1.0" +log = "0.4" + +syntax = { path = "../libsyntax" } +syntax_pos = { path = "../libsyntax_pos" } +rustc = { path = "../librustc" } +rustc_target = { path = "../librustc_target" } +rustc_data_structures = { path = "../librustc_data_structures" } +rustc_mir = { path = "../librustc_mir" } +rustc_incremental = { path = "../librustc_incremental" } diff --git a/src/librustc_codegen_utils/codegen_backend.rs b/src/librustc_codegen_utils/codegen_backend.rs new file mode 100644 index 00000000000..15aab680289 --- /dev/null +++ b/src/librustc_codegen_utils/codegen_backend.rs @@ -0,0 +1,296 @@ +// Copyright 2014-2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! The Rust compiler. +//! +//! # Note +//! +//! This API is completely unstable and subject to change. + +#![doc(html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png", + html_favicon_url = "https://doc.rust-lang.org/favicon.ico", + html_root_url = "https://doc.rust-lang.org/nightly/")] +#![deny(warnings)] + +#![feature(box_syntax)] + +use std::any::Any; +use std::io::prelude::*; +use std::io::{self, Cursor}; +use std::fs::File; +use std::path::Path; +use std::sync::mpsc; + +use rustc_data_structures::owning_ref::OwningRef; +use rustc_data_structures::sync::Lrc; +use ar::{Archive, Builder, Header}; +use flate2::Compression; +use flate2::write::DeflateEncoder; + +use syntax::symbol::Symbol; +use rustc::hir::def_id::LOCAL_CRATE; +use rustc::session::{Session, CompileIncomplete}; +use rustc::session::config::{CrateType, OutputFilenames, PrintRequest}; +use rustc::ty::TyCtxt; +use rustc::ty::maps::Providers; +use rustc::middle::cstore::EncodedMetadata; +use rustc::middle::cstore::MetadataLoader; +use rustc::dep_graph::DepGraph; +use rustc_target::spec::Target; +use rustc_data_structures::fx::FxHashMap; +use rustc_mir::monomorphize::collector; +use link::{build_link_meta, out_filename}; + +pub use rustc_data_structures::sync::MetadataRef; + +pub trait CodegenBackend { + fn init(&self, _sess: &Session) {} + fn print(&self, _req: PrintRequest, _sess: &Session) {} + fn target_features(&self, _sess: &Session) -> Vec { vec![] } + fn print_passes(&self) {} + fn print_version(&self) {} + fn diagnostics(&self) -> &[(&'static str, &'static str)] { &[] } + + fn metadata_loader(&self) -> Box; + fn provide(&self, _providers: &mut Providers); + fn provide_extern(&self, _providers: &mut Providers); + fn codegen_crate<'a, 'tcx>( + &self, + tcx: TyCtxt<'a, 'tcx, 'tcx>, + rx: mpsc::Receiver> + ) -> Box; + + /// This is called on the returned `Box` from `codegen_backend` + /// + /// # Panics + /// + /// Panics when the passed `Box` was not returned by `codegen_backend`. + fn join_codegen_and_link( + &self, + ongoing_codegen: Box, + sess: &Session, + dep_graph: &DepGraph, + outputs: &OutputFilenames, + ) -> Result<(), CompileIncomplete>; +} + +pub struct DummyCodegenBackend; + +impl CodegenBackend for DummyCodegenBackend { + fn metadata_loader(&self) -> Box { + box DummyMetadataLoader(()) + } + + fn provide(&self, _providers: &mut Providers) { + bug!("DummyCodegenBackend::provide"); + } + + fn provide_extern(&self, _providers: &mut Providers) { + bug!("DummyCodegenBackend::provide_extern"); + } + + fn codegen_crate<'a, 'tcx>( + &self, + _tcx: TyCtxt<'a, 'tcx, 'tcx>, + _rx: mpsc::Receiver> + ) -> Box { + bug!("DummyCodegenBackend::codegen_backend"); + } + + fn join_codegen_and_link( + &self, + _ongoing_codegen: Box, + _sess: &Session, + _dep_graph: &DepGraph, + _outputs: &OutputFilenames, + ) -> Result<(), CompileIncomplete> { + bug!("DummyCodegenBackend::join_codegen_and_link"); + } +} + +pub struct DummyMetadataLoader(()); + +impl MetadataLoader for DummyMetadataLoader { + fn get_rlib_metadata( + &self, + _target: &Target, + _filename: &Path + ) -> Result { + bug!("DummyMetadataLoader::get_rlib_metadata"); + } + + fn get_dylib_metadata( + &self, + _target: &Target, + _filename: &Path + ) -> Result { + bug!("DummyMetadataLoader::get_dylib_metadata"); + } +} + +pub struct NoLlvmMetadataLoader; + +impl MetadataLoader for NoLlvmMetadataLoader { + fn get_rlib_metadata(&self, _: &Target, filename: &Path) -> Result { + let file = File::open(filename) + .map_err(|e| format!("metadata file open err: {:?}", e))?; + let mut archive = Archive::new(file); + + while let Some(entry_result) = archive.next_entry() { + let mut entry = entry_result + .map_err(|e| format!("metadata section read err: {:?}", e))?; + if entry.header().identifier() == "rust.metadata.bin" { + let mut buf = Vec::new(); + io::copy(&mut entry, &mut buf).unwrap(); + let buf: OwningRef, [u8]> = OwningRef::new(buf).into(); + return Ok(rustc_erase_owner!(buf.map_owner_box())); + } + } + + Err("Couldn't find metadata section".to_string()) + } + + fn get_dylib_metadata( + &self, + _target: &Target, + _filename: &Path, + ) -> Result { + // FIXME: Support reading dylibs from llvm enabled rustc + self.get_rlib_metadata(_target, _filename) + } +} + +pub struct MetadataOnlyCodegenBackend(()); +pub struct OngoingCodegen { + metadata: EncodedMetadata, + metadata_version: Vec, + crate_name: Symbol, +} + +impl MetadataOnlyCodegenBackend { + pub fn new() -> Box { + box MetadataOnlyCodegenBackend(()) + } +} + +impl CodegenBackend for MetadataOnlyCodegenBackend { + fn init(&self, sess: &Session) { + for cty in sess.opts.crate_types.iter() { + match *cty { + CrateType::CrateTypeRlib | CrateType::CrateTypeDylib | + CrateType::CrateTypeExecutable => {}, + _ => { + sess.parse_sess.span_diagnostic.warn( + &format!("LLVM unsupported, so output type {} is not supported", cty) + ); + }, + } + } + } + + fn metadata_loader(&self) -> Box { + box NoLlvmMetadataLoader + } + + fn provide(&self, providers: &mut Providers) { + ::symbol_names::provide(providers); + + providers.target_features_whitelist = |_tcx, _cnum| { + Lrc::new(FxHashMap()) // Just a dummy + }; + } + fn provide_extern(&self, _providers: &mut Providers) {} + + fn codegen_crate<'a, 'tcx>( + &self, + tcx: TyCtxt<'a, 'tcx, 'tcx>, + _rx: mpsc::Receiver> + ) -> Box { + use rustc_mir::monomorphize::item::MonoItem; + + ::check_for_rustc_errors_attr(tcx); + ::symbol_names_test::report_symbol_names(tcx); + ::rustc_incremental::assert_dep_graph(tcx); + ::rustc_incremental::assert_module_sources::assert_module_sources(tcx); + ::rustc_mir::monomorphize::assert_symbols_are_distinct(tcx, + collector::collect_crate_mono_items( + tcx, + collector::MonoItemCollectionMode::Eager + ).0.iter() + ); + ::rustc::middle::dependency_format::calculate(tcx); + let _ = tcx.link_args(LOCAL_CRATE); + let _ = tcx.native_libraries(LOCAL_CRATE); + for mono_item in + collector::collect_crate_mono_items( + tcx, + collector::MonoItemCollectionMode::Eager + ).0 { + match mono_item { + MonoItem::Fn(inst) => { + let def_id = inst.def_id(); + if def_id.is_local() { + let _ = inst.def.is_inline(tcx); + let _ = tcx.codegen_fn_attrs(def_id); + } + } + _ => {} + } + } + tcx.sess.abort_if_errors(); + + let link_meta = build_link_meta(tcx.crate_hash(LOCAL_CRATE)); + let metadata = tcx.encode_metadata(&link_meta); + + box OngoingCodegen { + metadata: metadata, + metadata_version: tcx.metadata_encoding_version().to_vec(), + crate_name: tcx.crate_name(LOCAL_CRATE), + } + } + + fn join_codegen_and_link( + &self, + ongoing_codegen: Box, + sess: &Session, + _dep_graph: &DepGraph, + outputs: &OutputFilenames, + ) -> Result<(), CompileIncomplete> { + let ongoing_codegen = ongoing_codegen.downcast::() + .expect("Expected MetadataOnlyCodegenBackend's OngoingCodegen, found Box"); + for &crate_type in sess.opts.crate_types.iter() { + if crate_type != CrateType::CrateTypeRlib && crate_type != CrateType::CrateTypeDylib { + continue; + } + let output_name = + out_filename(sess, crate_type, &outputs, &ongoing_codegen.crate_name.as_str()); + let mut compressed = ongoing_codegen.metadata_version.clone(); + let metadata = if crate_type == CrateType::CrateTypeDylib { + DeflateEncoder::new(&mut compressed, Compression::fast()) + .write_all(&ongoing_codegen.metadata.raw_data) + .unwrap(); + &compressed + } else { + &ongoing_codegen.metadata.raw_data + }; + let mut builder = Builder::new(File::create(&output_name).unwrap()); + let header = Header::new("rust.metadata.bin".to_string(), metadata.len() as u64); + builder.append(&header, Cursor::new(metadata)).unwrap(); + } + + sess.abort_if_errors(); + if !sess.opts.crate_types.contains(&CrateType::CrateTypeRlib) + && !sess.opts.crate_types.contains(&CrateType::CrateTypeDylib) + { + sess.fatal("Executables are not supported by the metadata-only backend."); + } + Ok(()) + } +} diff --git a/src/librustc_codegen_utils/lib.rs b/src/librustc_codegen_utils/lib.rs new file mode 100644 index 00000000000..0c18571f4ff --- /dev/null +++ b/src/librustc_codegen_utils/lib.rs @@ -0,0 +1,63 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! # Note +//! +//! This API is completely unstable and subject to change. + +#![doc(html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png", + html_favicon_url = "https://doc.rust-lang.org/favicon.ico", + html_root_url = "https://doc.rust-lang.org/nightly/")] + +#![feature(box_patterns)] +#![feature(box_syntax)] +#![feature(custom_attribute)] +#![allow(unused_attributes)] +#![feature(quote)] +#![feature(rustc_diagnostic_macros)] + +extern crate ar; +extern crate flate2; +#[macro_use] +extern crate log; + +#[macro_use] +extern crate rustc; +extern crate rustc_target; +extern crate rustc_mir; +extern crate rustc_incremental; +extern crate syntax; +extern crate syntax_pos; +#[macro_use] extern crate rustc_data_structures; + +pub extern crate rustc as __rustc; + +use rustc::ty::TyCtxt; + +pub mod link; +pub mod codegen_backend; +pub mod symbol_names; +pub mod symbol_names_test; + +/// check for the #[rustc_error] annotation, which forces an +/// error in codegen. This is used to write compile-fail tests +/// that actually test that compilation succeeds without +/// reporting an error. +pub fn check_for_rustc_errors_attr(tcx: TyCtxt) { + if let Some((id, span, _)) = *tcx.sess.entry_fn.borrow() { + let main_def_id = tcx.hir.local_def_id(id); + + if tcx.has_attr(main_def_id, "rustc_error") { + tcx.sess.span_fatal(span, "compilation successful"); + } + } +} + +__build_diagnostic_array! { librustc_codegen_utils, DIAGNOSTICS } diff --git a/src/librustc_codegen_utils/link.rs b/src/librustc_codegen_utils/link.rs new file mode 100644 index 00000000000..aabe931d79c --- /dev/null +++ b/src/librustc_codegen_utils/link.rs @@ -0,0 +1,191 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use rustc::session::config::{self, OutputFilenames, Input, OutputType}; +use rustc::session::Session; +use rustc::middle::cstore::{self, LinkMeta}; +use rustc::hir::svh::Svh; +use std::path::{Path, PathBuf}; +use syntax::{ast, attr}; +use syntax_pos::Span; + +pub fn out_filename(sess: &Session, + crate_type: config::CrateType, + outputs: &OutputFilenames, + crate_name: &str) + -> PathBuf { + let default_filename = filename_for_input(sess, crate_type, crate_name, outputs); + let out_filename = outputs.outputs.get(&OutputType::Exe) + .and_then(|s| s.to_owned()) + .or_else(|| outputs.single_output_file.clone()) + .unwrap_or(default_filename); + + check_file_is_writeable(&out_filename, sess); + + out_filename +} + +// Make sure files are writeable. Mac, FreeBSD, and Windows system linkers +// check this already -- however, the Linux linker will happily overwrite a +// read-only file. We should be consistent. +pub fn check_file_is_writeable(file: &Path, sess: &Session) { + if !is_writeable(file) { + sess.fatal(&format!("output file {} is not writeable -- check its \ + permissions", file.display())); + } +} + +fn is_writeable(p: &Path) -> bool { + match p.metadata() { + Err(..) => true, + Ok(m) => !m.permissions().readonly() + } +} + +pub fn build_link_meta(crate_hash: Svh) -> LinkMeta { + let r = LinkMeta { + crate_hash, + }; + info!("{:?}", r); + return r; +} + +pub fn find_crate_name(sess: Option<&Session>, + attrs: &[ast::Attribute], + input: &Input) -> String { + let validate = |s: String, span: Option| { + cstore::validate_crate_name(sess, &s, span); + s + }; + + // Look in attributes 100% of the time to make sure the attribute is marked + // as used. After doing this, however, we still prioritize a crate name from + // the command line over one found in the #[crate_name] attribute. If we + // find both we ensure that they're the same later on as well. + let attr_crate_name = attr::find_by_name(attrs, "crate_name") + .and_then(|at| at.value_str().map(|s| (at, s))); + + if let Some(sess) = sess { + if let Some(ref s) = sess.opts.crate_name { + if let Some((attr, name)) = attr_crate_name { + if name != &**s { + let msg = format!("--crate-name and #[crate_name] are \ + required to match, but `{}` != `{}`", + s, name); + sess.span_err(attr.span, &msg); + } + } + return validate(s.clone(), None); + } + } + + if let Some((attr, s)) = attr_crate_name { + return validate(s.to_string(), Some(attr.span)); + } + if let Input::File(ref path) = *input { + if let Some(s) = path.file_stem().and_then(|s| s.to_str()) { + if s.starts_with("-") { + let msg = format!("crate names cannot start with a `-`, but \ + `{}` has a leading hyphen", s); + if let Some(sess) = sess { + sess.err(&msg); + } + } else { + return validate(s.replace("-", "_"), None); + } + } + } + + "rust_out".to_string() +} + +pub fn filename_for_input(sess: &Session, + crate_type: config::CrateType, + crate_name: &str, + outputs: &OutputFilenames) -> PathBuf { + let libname = format!("{}{}", crate_name, sess.opts.cg.extra_filename); + + match crate_type { + config::CrateTypeRlib => { + outputs.out_directory.join(&format!("lib{}.rlib", libname)) + } + config::CrateTypeCdylib | + config::CrateTypeProcMacro | + config::CrateTypeDylib => { + let (prefix, suffix) = (&sess.target.target.options.dll_prefix, + &sess.target.target.options.dll_suffix); + outputs.out_directory.join(&format!("{}{}{}", prefix, libname, + suffix)) + } + config::CrateTypeStaticlib => { + let (prefix, suffix) = (&sess.target.target.options.staticlib_prefix, + &sess.target.target.options.staticlib_suffix); + outputs.out_directory.join(&format!("{}{}{}", prefix, libname, + suffix)) + } + config::CrateTypeExecutable => { + let suffix = &sess.target.target.options.exe_suffix; + let out_filename = outputs.path(OutputType::Exe); + if suffix.is_empty() { + out_filename.to_path_buf() + } else { + out_filename.with_extension(&suffix[1..]) + } + } + } +} + +/// Returns default crate type for target +/// +/// Default crate type is used when crate type isn't provided neither +/// through cmd line arguments nor through crate attributes +/// +/// It is CrateTypeExecutable for all platforms but iOS as there is no +/// way to run iOS binaries anyway without jailbreaking and +/// interaction with Rust code through static library is the only +/// option for now +pub fn default_output_for_target(sess: &Session) -> config::CrateType { + if !sess.target.target.options.executables { + config::CrateTypeStaticlib + } else { + config::CrateTypeExecutable + } +} + +/// Checks if target supports crate_type as output +pub fn invalid_output_for_target(sess: &Session, + crate_type: config::CrateType) -> bool { + match crate_type { + config::CrateTypeCdylib | + config::CrateTypeDylib | + config::CrateTypeProcMacro => { + if !sess.target.target.options.dynamic_linking { + return true + } + if sess.crt_static() && !sess.target.target.options.crt_static_allows_dylibs { + return true + } + } + _ => {} + } + if sess.target.target.options.only_cdylib { + match crate_type { + config::CrateTypeProcMacro | config::CrateTypeDylib => return true, + _ => {} + } + } + if !sess.target.target.options.executables { + if crate_type == config::CrateTypeExecutable { + return true + } + } + + false +} diff --git a/src/librustc_codegen_utils/symbol_names.rs b/src/librustc_codegen_utils/symbol_names.rs new file mode 100644 index 00000000000..1a62f39ae3d --- /dev/null +++ b/src/librustc_codegen_utils/symbol_names.rs @@ -0,0 +1,435 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! The Rust Linkage Model and Symbol Names +//! ======================================= +//! +//! The semantic model of Rust linkage is, broadly, that "there's no global +//! namespace" between crates. Our aim is to preserve the illusion of this +//! model despite the fact that it's not *quite* possible to implement on +//! modern linkers. We initially didn't use system linkers at all, but have +//! been convinced of their utility. +//! +//! There are a few issues to handle: +//! +//! - Linkers operate on a flat namespace, so we have to flatten names. +//! We do this using the C++ namespace-mangling technique. Foo::bar +//! symbols and such. +//! +//! - Symbols for distinct items with the same *name* need to get different +//! linkage-names. Examples of this are monomorphizations of functions or +//! items within anonymous scopes that end up having the same path. +//! +//! - Symbols in different crates but with same names "within" the crate need +//! to get different linkage-names. +//! +//! - Symbol names should be deterministic: Two consecutive runs of the +//! compiler over the same code base should produce the same symbol names for +//! the same items. +//! +//! - Symbol names should not depend on any global properties of the code base, +//! so that small modifications to the code base do not result in all symbols +//! changing. In previous versions of the compiler, symbol names incorporated +//! the SVH (Stable Version Hash) of the crate. This scheme turned out to be +//! infeasible when used in conjunction with incremental compilation because +//! small code changes would invalidate all symbols generated previously. +//! +//! - Even symbols from different versions of the same crate should be able to +//! live next to each other without conflict. +//! +//! In order to fulfill the above requirements the following scheme is used by +//! the compiler: +//! +//! The main tool for avoiding naming conflicts is the incorporation of a 64-bit +//! hash value into every exported symbol name. Anything that makes a difference +//! to the symbol being named, but does not show up in the regular path needs to +//! be fed into this hash: +//! +//! - Different monomorphizations of the same item have the same path but differ +//! in their concrete type parameters, so these parameters are part of the +//! data being digested for the symbol hash. +//! +//! - Rust allows items to be defined in anonymous scopes, such as in +//! `fn foo() { { fn bar() {} } { fn bar() {} } }`. Both `bar` functions have +//! the path `foo::bar`, since the anonymous scopes do not contribute to the +//! path of an item. The compiler already handles this case via so-called +//! disambiguating `DefPaths` which use indices to distinguish items with the +//! same name. The DefPaths of the functions above are thus `foo[0]::bar[0]` +//! and `foo[0]::bar[1]`. In order to incorporate this disambiguation +//! information into the symbol name too, these indices are fed into the +//! symbol hash, so that the above two symbols would end up with different +//! hash values. +//! +//! The two measures described above suffice to avoid intra-crate conflicts. In +//! order to also avoid inter-crate conflicts two more measures are taken: +//! +//! - The name of the crate containing the symbol is prepended to the symbol +//! name, i.e. symbols are "crate qualified". For example, a function `foo` in +//! module `bar` in crate `baz` would get a symbol name like +//! `baz::bar::foo::{hash}` instead of just `bar::foo::{hash}`. This avoids +//! simple conflicts between functions from different crates. +//! +//! - In order to be able to also use symbols from two versions of the same +//! crate (which naturally also have the same name), a stronger measure is +//! required: The compiler accepts an arbitrary "disambiguator" value via the +//! `-C metadata` commandline argument. This disambiguator is then fed into +//! the symbol hash of every exported item. Consequently, the symbols in two +//! identical crates but with different disambiguators are not in conflict +//! with each other. This facility is mainly intended to be used by build +//! tools like Cargo. +//! +//! A note on symbol name stability +//! ------------------------------- +//! Previous versions of the compiler resorted to feeding NodeIds into the +//! symbol hash in order to disambiguate between items with the same path. The +//! current version of the name generation algorithm takes great care not to do +//! that, since NodeIds are notoriously unstable: A small change to the +//! code base will offset all NodeIds after the change and thus, much as using +//! the SVH in the hash, invalidate an unbounded number of symbol names. This +//! makes re-using previously compiled code for incremental compilation +//! virtually impossible. Thus, symbol hash generation exclusively relies on +//! DefPaths which are much more robust in the face of changes to the code base. + +use rustc::middle::weak_lang_items; +use rustc_mir::monomorphize::Instance; +use rustc_mir::monomorphize::item::{MonoItem, MonoItemExt, InstantiationMode}; +use rustc::hir::def_id::{DefId, LOCAL_CRATE}; +use rustc::hir::map as hir_map; +use rustc::ty::{self, Ty, TyCtxt, TypeFoldable}; +use rustc::ty::fold::TypeVisitor; +use rustc::ty::item_path::{self, ItemPathBuffer, RootMode}; +use rustc::ty::maps::Providers; +use rustc::ty::subst::Substs; +use rustc::hir::map::definitions::DefPathData; +use rustc::util::common::record_time; + +use syntax::attr; +use syntax_pos::symbol::Symbol; + +use std::fmt::Write; + +pub fn provide(providers: &mut Providers) { + *providers = Providers { + def_symbol_name, + symbol_name, + + ..*providers + }; +} + +fn get_symbol_hash<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, + + // the DefId of the item this name is for + def_id: DefId, + + // instance this name will be for + instance: Instance<'tcx>, + + // type of the item, without any generic + // parameters substituted; this is + // included in the hash as a kind of + // safeguard. + item_type: Ty<'tcx>, + + // values for generic type parameters, + // if any. + substs: &'tcx Substs<'tcx>) + -> u64 { + debug!("get_symbol_hash(def_id={:?}, parameters={:?})", def_id, substs); + + let mut hasher = ty::util::TypeIdHasher::::new(tcx); + + record_time(&tcx.sess.perf_stats.symbol_hash_time, || { + // the main symbol name is not necessarily unique; hash in the + // compiler's internal def-path, guaranteeing each symbol has a + // truly unique path + hasher.hash(tcx.def_path_hash(def_id)); + + // Include the main item-type. Note that, in this case, the + // assertions about `needs_subst` may not hold, but this item-type + // ought to be the same for every reference anyway. + assert!(!item_type.has_erasable_regions()); + hasher.visit_ty(item_type); + + // If this is a function, we hash the signature as well. + // This is not *strictly* needed, but it may help in some + // situations, see the `run-make/a-b-a-linker-guard` test. + if let ty::TyFnDef(..) = item_type.sty { + item_type.fn_sig(tcx).visit_with(&mut hasher); + } + + // also include any type parameters (for generic items) + assert!(!substs.has_erasable_regions()); + assert!(!substs.needs_subst()); + substs.visit_with(&mut hasher); + + let is_generic = substs.types().next().is_some(); + let avoid_cross_crate_conflicts = + // If this is an instance of a generic function, we also hash in + // the ID of the instantiating crate. This avoids symbol conflicts + // in case the same instances is emitted in two crates of the same + // project. + is_generic || + + // If we're dealing with an instance of a function that's inlined from + // another crate but we're marking it as globally shared to our + // compliation (aka we're not making an internal copy in each of our + // codegen units) then this symbol may become an exported (but hidden + // visibility) symbol. This means that multiple crates may do the same + // and we want to be sure to avoid any symbol conflicts here. + match MonoItem::Fn(instance).instantiation_mode(tcx) { + InstantiationMode::GloballyShared { may_conflict: true } => true, + _ => false, + }; + + if avoid_cross_crate_conflicts { + let instantiating_crate = if is_generic { + if !def_id.is_local() && tcx.share_generics() { + // If we are re-using a monomorphization from another crate, + // we have to compute the symbol hash accordingly. + let upstream_monomorphizations = + tcx.upstream_monomorphizations_for(def_id); + + upstream_monomorphizations.and_then(|monos| monos.get(&substs) + .cloned()) + .unwrap_or(LOCAL_CRATE) + } else { + LOCAL_CRATE + } + } else { + LOCAL_CRATE + }; + + hasher.hash(&tcx.original_crate_name(instantiating_crate).as_str()[..]); + hasher.hash(&tcx.crate_disambiguator(instantiating_crate)); + } + }); + + // 64 bits should be enough to avoid collisions. + hasher.finish() +} + +fn def_symbol_name<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) + -> ty::SymbolName +{ + let mut buffer = SymbolPathBuffer::new(); + item_path::with_forced_absolute_paths(|| { + tcx.push_item_path(&mut buffer, def_id); + }); + buffer.into_interned() +} + +fn symbol_name<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, instance: Instance<'tcx>) + -> ty::SymbolName +{ + ty::SymbolName { name: Symbol::intern(&compute_symbol_name(tcx, instance)).as_interned_str() } +} + +fn compute_symbol_name<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, instance: Instance<'tcx>) + -> String +{ + let def_id = instance.def_id(); + let substs = instance.substs; + + debug!("symbol_name(def_id={:?}, substs={:?})", + def_id, substs); + + let node_id = tcx.hir.as_local_node_id(def_id); + + if let Some(id) = node_id { + if *tcx.sess.plugin_registrar_fn.get() == Some(id) { + let disambiguator = tcx.sess.local_crate_disambiguator(); + return tcx.sess.generate_plugin_registrar_symbol(disambiguator); + } + if *tcx.sess.derive_registrar_fn.get() == Some(id) { + let disambiguator = tcx.sess.local_crate_disambiguator(); + return tcx.sess.generate_derive_registrar_symbol(disambiguator); + } + } + + // FIXME(eddyb) Precompute a custom symbol name based on attributes. + let attrs = tcx.get_attrs(def_id); + let is_foreign = if let Some(id) = node_id { + match tcx.hir.get(id) { + hir_map::NodeForeignItem(_) => true, + _ => false + } + } else { + tcx.is_foreign_item(def_id) + }; + + if let Some(name) = weak_lang_items::link_name(&attrs) { + return name.to_string(); + } + + if is_foreign { + if let Some(name) = attr::first_attr_value_str_by_name(&attrs, "link_name") { + return name.to_string(); + } + // Don't mangle foreign items. + return tcx.item_name(def_id).to_string(); + } + + if let Some(name) = tcx.codegen_fn_attrs(def_id).export_name { + // Use provided name + return name.to_string(); + } + + if attr::contains_name(&attrs, "no_mangle") { + // Don't mangle + return tcx.item_name(def_id).to_string(); + } + + // We want to compute the "type" of this item. Unfortunately, some + // kinds of items (e.g., closures) don't have an entry in the + // item-type array. So walk back up the find the closest parent + // that DOES have an entry. + let mut ty_def_id = def_id; + let instance_ty; + loop { + let key = tcx.def_key(ty_def_id); + match key.disambiguated_data.data { + DefPathData::TypeNs(_) | + DefPathData::ValueNs(_) => { + instance_ty = tcx.type_of(ty_def_id); + break; + } + _ => { + // if we're making a symbol for something, there ought + // to be a value or type-def or something in there + // *somewhere* + ty_def_id.index = key.parent.unwrap_or_else(|| { + bug!("finding type for {:?}, encountered def-id {:?} with no \ + parent", def_id, ty_def_id); + }); + } + } + } + + // Erase regions because they may not be deterministic when hashed + // and should not matter anyhow. + let instance_ty = tcx.erase_regions(&instance_ty); + + let hash = get_symbol_hash(tcx, def_id, instance, instance_ty, substs); + + SymbolPathBuffer::from_interned(tcx.def_symbol_name(def_id)).finish(hash) +} + +// Follow C++ namespace-mangling style, see +// http://en.wikipedia.org/wiki/Name_mangling for more info. +// +// It turns out that on macOS you can actually have arbitrary symbols in +// function names (at least when given to LLVM), but this is not possible +// when using unix's linker. Perhaps one day when we just use a linker from LLVM +// we won't need to do this name mangling. The problem with name mangling is +// that it seriously limits the available characters. For example we can't +// have things like &T in symbol names when one would theoretically +// want them for things like impls of traits on that type. +// +// To be able to work on all platforms and get *some* reasonable output, we +// use C++ name-mangling. +struct SymbolPathBuffer { + result: String, + temp_buf: String +} + +impl SymbolPathBuffer { + fn new() -> Self { + let mut result = SymbolPathBuffer { + result: String::with_capacity(64), + temp_buf: String::with_capacity(16) + }; + result.result.push_str("_ZN"); // _Z == Begin name-sequence, N == nested + result + } + + fn from_interned(symbol: ty::SymbolName) -> Self { + let mut result = SymbolPathBuffer { + result: String::with_capacity(64), + temp_buf: String::with_capacity(16) + }; + result.result.push_str(&symbol.name.as_str()); + result + } + + fn into_interned(self) -> ty::SymbolName { + ty::SymbolName { name: Symbol::intern(&self.result).as_interned_str() } + } + + fn finish(mut self, hash: u64) -> String { + // E = end name-sequence + let _ = write!(self.result, "17h{:016x}E", hash); + self.result + } +} + +impl ItemPathBuffer for SymbolPathBuffer { + fn root_mode(&self) -> &RootMode { + const ABSOLUTE: &'static RootMode = &RootMode::Absolute; + ABSOLUTE + } + + fn push(&mut self, text: &str) { + self.temp_buf.clear(); + let need_underscore = sanitize(&mut self.temp_buf, text); + let _ = write!(self.result, "{}", self.temp_buf.len() + (need_underscore as usize)); + if need_underscore { + self.result.push('_'); + } + self.result.push_str(&self.temp_buf); + } +} + +// Name sanitation. LLVM will happily accept identifiers with weird names, but +// gas doesn't! +// gas accepts the following characters in symbols: a-z, A-Z, 0-9, ., _, $ +// +// returns true if an underscore must be added at the start +pub fn sanitize(result: &mut String, s: &str) -> bool { + for c in s.chars() { + match c { + // Escape these with $ sequences + '@' => result.push_str("$SP$"), + '*' => result.push_str("$BP$"), + '&' => result.push_str("$RF$"), + '<' => result.push_str("$LT$"), + '>' => result.push_str("$GT$"), + '(' => result.push_str("$LP$"), + ')' => result.push_str("$RP$"), + ',' => result.push_str("$C$"), + + // '.' doesn't occur in types and functions, so reuse it + // for ':' and '-' + '-' | ':' => result.push('.'), + + // These are legal symbols + 'a' ... 'z' + | 'A' ... 'Z' + | '0' ... '9' + | '_' | '.' | '$' => result.push(c), + + _ => { + result.push('$'); + for c in c.escape_unicode().skip(1) { + match c { + '{' => {}, + '}' => result.push('$'), + c => result.push(c), + } + } + } + } + } + + // Underscore-qualify anything that didn't start as an ident. + !result.is_empty() && + result.as_bytes()[0] != '_' as u8 && + ! (result.as_bytes()[0] as char).is_xid_start() +} diff --git a/src/librustc_codegen_utils/symbol_names_test.rs b/src/librustc_codegen_utils/symbol_names_test.rs new file mode 100644 index 00000000000..47bbd67fb5c --- /dev/null +++ b/src/librustc_codegen_utils/symbol_names_test.rs @@ -0,0 +1,79 @@ +// Copyright 2012-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. + +//! Walks the crate looking for items/impl-items/trait-items that have +//! either a `rustc_symbol_name` or `rustc_item_path` attribute and +//! generates an error giving, respectively, the symbol name or +//! item-path. This is used for unit testing the code that generates +//! paths etc in all kinds of annoying scenarios. + +use rustc::hir; +use rustc::ty::TyCtxt; +use syntax::ast; + +use rustc_mir::monomorphize::Instance; + +const SYMBOL_NAME: &'static str = "rustc_symbol_name"; +const ITEM_PATH: &'static str = "rustc_item_path"; + +pub fn report_symbol_names<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>) { + // if the `rustc_attrs` feature is not enabled, then the + // attributes we are interested in cannot be present anyway, so + // skip the walk. + if !tcx.features().rustc_attrs { + return; + } + + tcx.dep_graph.with_ignore(|| { + let mut visitor = SymbolNamesTest { tcx: tcx }; + tcx.hir.krate().visit_all_item_likes(&mut visitor); + }) +} + +struct SymbolNamesTest<'a, 'tcx:'a> { + tcx: TyCtxt<'a, 'tcx, 'tcx>, +} + +impl<'a, 'tcx> SymbolNamesTest<'a, 'tcx> { + fn process_attrs(&mut self, + node_id: ast::NodeId) { + let tcx = self.tcx; + let def_id = tcx.hir.local_def_id(node_id); + for attr in tcx.get_attrs(def_id).iter() { + if attr.check_name(SYMBOL_NAME) { + // for now, can only use on monomorphic names + let instance = Instance::mono(tcx, def_id); + let name = self.tcx.symbol_name(instance); + tcx.sess.span_err(attr.span, &format!("symbol-name({})", name)); + } else if attr.check_name(ITEM_PATH) { + let path = tcx.item_path_str(def_id); + tcx.sess.span_err(attr.span, &format!("item-path({})", path)); + } + + // (*) The formatting of `tag({})` is chosen so that tests can elect + // to test the entirety of the string, if they choose, or else just + // some subset. + } + } +} + +impl<'a, 'tcx> hir::itemlikevisit::ItemLikeVisitor<'tcx> for SymbolNamesTest<'a, 'tcx> { + fn visit_item(&mut self, item: &'tcx hir::Item) { + self.process_attrs(item.id); + } + + fn visit_trait_item(&mut self, trait_item: &'tcx hir::TraitItem) { + self.process_attrs(trait_item.id); + } + + fn visit_impl_item(&mut self, impl_item: &'tcx hir::ImplItem) { + self.process_attrs(impl_item.id); + } +} diff --git a/src/librustc_driver/Cargo.toml b/src/librustc_driver/Cargo.toml index 1827533f0ac..24bf07d793f 100644 --- a/src/librustc_driver/Cargo.toml +++ b/src/librustc_driver/Cargo.toml @@ -31,7 +31,7 @@ rustc_privacy = { path = "../librustc_privacy" } rustc_resolve = { path = "../librustc_resolve" } rustc_save_analysis = { path = "../librustc_save_analysis" } rustc_traits = { path = "../librustc_traits" } -rustc_trans_utils = { path = "../librustc_trans_utils" } +rustc_codegen_utils = { path = "../librustc_codegen_utils" } rustc_typeck = { path = "../librustc_typeck" } serialize = { path = "../libserialize" } syntax = { path = "../libsyntax" } diff --git a/src/librustc_driver/driver.rs b/src/librustc_driver/driver.rs index 62b3accc46f..ed28b05c125 100644 --- a/src/librustc_driver/driver.rs +++ b/src/librustc_driver/driver.rs @@ -32,7 +32,7 @@ use rustc_resolve::{MakeGlobMap, Resolver, ResolverArenas}; use rustc_metadata::creader::CrateLoader; use rustc_metadata::cstore::{self, CStore}; use rustc_traits; -use rustc_trans_utils::trans_crate::TransCrate; +use rustc_codegen_utils::codegen_backend::CodegenBackend; use rustc_typeck as typeck; use rustc_privacy; use rustc_plugin::registry::Registry; @@ -110,7 +110,7 @@ pub fn spawn_thread_pool R + sync::Send, R: sync:: } pub fn compile_input( - trans: Box, + codegen_backend: Box, sess: &Session, cstore: &CStore, input_path: &Option, @@ -143,7 +143,7 @@ pub fn compile_input( // We need nested scopes here, because the intermediate results can keep // large chunks of memory alive and we want to free them as soon as // possible to keep the peak memory usage low - let (outputs, ongoing_trans, dep_graph) = { + let (outputs, ongoing_codegen, dep_graph) = { let krate = match phase_1_parse_input(control, sess, input) { Ok(krate) => krate, Err(mut parse_error) => { @@ -162,7 +162,7 @@ pub fn compile_input( let outputs = build_output_filenames(input, outdir, output, &krate.attrs, sess); let crate_name = - ::rustc_trans_utils::link::find_crate_name(Some(sess), &krate.attrs, input); + ::rustc_codegen_utils::link::find_crate_name(Some(sess), &krate.attrs, input); install_panic_hook(); let ExpansionResult { @@ -274,7 +274,7 @@ pub fn compile_input( }; phase_3_run_analysis_passes( - &*trans, + &*codegen_backend, control, sess, cstore, @@ -310,14 +310,14 @@ pub fn compile_input( result?; if log_enabled!(::log::Level::Info) { - println!("Pre-trans"); + println!("Pre-codegen"); tcx.print_debug_stats(); } - let ongoing_trans = phase_4_translate_to_llvm(&*trans, tcx, rx); + let ongoing_codegen = phase_4_codegen(&*codegen_backend, tcx, rx); if log_enabled!(::log::Level::Info) { - println!("Post-trans"); + println!("Post-codegen"); tcx.print_debug_stats(); } @@ -328,7 +328,7 @@ pub fn compile_input( } } - Ok((outputs.clone(), ongoing_trans, tcx.dep_graph.clone())) + Ok((outputs.clone(), ongoing_codegen, tcx.dep_graph.clone())) }, )?? }; @@ -337,7 +337,7 @@ pub fn compile_input( sess.code_stats.borrow().print_type_sizes(); } - trans.join_trans_and_link(ongoing_trans, sess, &dep_graph, &outputs)?; + codegen_backend.join_codegen_and_link(ongoing_codegen, sess, &dep_graph, &outputs)?; if sess.opts.debugging_opts.perf_stats { sess.print_perf_stats(); @@ -1089,7 +1089,7 @@ pub fn default_provide_extern(providers: &mut ty::maps::Providers) { /// miscellaneous analysis passes on the crate. Return various /// structures carrying the results of the analysis. pub fn phase_3_run_analysis_passes<'tcx, F, R>( - trans: &TransCrate, + codegen_backend: &CodegenBackend, control: &CompileController, sess: &'tcx Session, cstore: &'tcx CrateStoreDyn, @@ -1128,12 +1128,12 @@ where let mut local_providers = ty::maps::Providers::default(); default_provide(&mut local_providers); - trans.provide(&mut local_providers); + codegen_backend.provide(&mut local_providers); (control.provide)(&mut local_providers); let mut extern_providers = local_providers; default_provide_extern(&mut extern_providers); - trans.provide_extern(&mut extern_providers); + codegen_backend.provide_extern(&mut extern_providers); (control.provide_extern)(&mut extern_providers); let (tx, rx) = mpsc::channel(); @@ -1233,10 +1233,10 @@ where ) } -/// Run the translation phase to LLVM, after which the AST and analysis can +/// Run the codegen backend, after which the AST and analysis can /// be discarded. -pub fn phase_4_translate_to_llvm<'a, 'tcx>( - trans: &TransCrate, +pub fn phase_4_codegen<'a, 'tcx>( + codegen_backend: &CodegenBackend, tcx: TyCtxt<'a, 'tcx, 'tcx>, rx: mpsc::Receiver>, ) -> Box { @@ -1244,12 +1244,12 @@ pub fn phase_4_translate_to_llvm<'a, 'tcx>( ::rustc::middle::dependency_format::calculate(tcx) }); - let translation = time(tcx.sess, "translation", move || trans.trans_crate(tcx, rx)); + let codegen = time(tcx.sess, "codegen", move || codegen_backend.codegen_crate(tcx, rx)); if tcx.sess.profile_queries() { profile::dump(&tcx.sess, "profile_queries".to_string()) } - translation + codegen } fn escape_dep_filename(filename: &FileName) -> String { @@ -1272,7 +1272,7 @@ fn generated_output_paths( // If the filename has been overridden using `-o`, it will not be modified // by appending `.rlib`, `.exe`, etc., so we can skip this transformation. OutputType::Exe if !exact_name => for crate_type in sess.crate_types.borrow().iter() { - let p = ::rustc_trans_utils::link::filename_for_input( + let p = ::rustc_codegen_utils::link::filename_for_input( sess, *crate_type, crate_name, @@ -1424,7 +1424,7 @@ pub fn collect_crate_types(session: &Session, attrs: &[ast::Attribute]) -> Vec Vec(run_compiler: F) -> isize 0 } -fn load_backend_from_dylib(path: &Path) -> fn() -> Box { +fn load_backend_from_dylib(path: &Path) -> fn() -> Box { // Note that we're specifically using `open_global_now` here rather than // `open`, namely we want the behavior on Unix of RTLD_GLOBAL and RTLD_NOW, // where NOW means "bind everything right now" because we don't want @@ -233,24 +235,24 @@ fn load_backend_from_dylib(path: &Path) -> fn() -> Box { } } -pub fn get_trans(sess: &Session) -> Box { +pub fn get_codegen_backend(sess: &Session) -> Box { static INIT: Once = ONCE_INIT; #[allow(deprecated)] #[no_debug] - static mut LOAD: fn() -> Box = || unreachable!(); + static mut LOAD: fn() -> Box = || unreachable!(); INIT.call_once(|| { - let trans_name = sess.opts.debugging_opts.codegen_backend.as_ref() + let codegen_name = sess.opts.debugging_opts.codegen_backend.as_ref() .unwrap_or(&sess.target.target.options.codegen_backend); - let backend = match &trans_name[..] { + let backend = match &codegen_name[..] { "metadata_only" => { - rustc_trans_utils::trans_crate::MetadataOnlyTransCrate::new + rustc_codegen_utils::codegen_backend::MetadataOnlyCodegenBackend::new } filename if filename.contains(".") => { load_backend_from_dylib(filename.as_ref()) } - trans_name => get_trans_sysroot(trans_name), + codegen_name => get_codegen_sysroot(codegen_name), }; unsafe { @@ -262,15 +264,15 @@ pub fn get_trans(sess: &Session) -> Box { backend } -fn get_trans_sysroot(backend_name: &str) -> fn() -> Box { +fn get_codegen_sysroot(backend_name: &str) -> fn() -> Box { // For now we only allow this function to be called once as it'll dlopen a // few things, which seems to work best if we only do that once. In - // general this assertion never trips due to the once guard in `get_trans`, + // general this assertion never trips due to the once guard in `get_codegen_backend`, // but there's a few manual calls to this function in this file we protect // against. static LOADED: AtomicBool = ATOMIC_BOOL_INIT; assert!(!LOADED.fetch_or(true, Ordering::SeqCst), - "cannot load the default trans backend twice"); + "cannot load the default codegen backend twice"); // When we're compiling this library with `--test` it'll run as a binary but // not actually exercise much functionality. As a result most of the logic @@ -278,7 +280,7 @@ fn get_trans_sysroot(backend_name: &str) -> fn() -> Box { // let's just return a dummy creation function which won't be used in // general anyway. if cfg!(test) { - return rustc_trans_utils::trans_crate::MetadataOnlyTransCrate::new + return rustc_codegen_utils::codegen_backend::MetadataOnlyCodegenBackend::new } let target = session::config::host_triple(); @@ -346,7 +348,7 @@ fn get_trans_sysroot(backend_name: &str) -> fn() -> Box { let mut file: Option = None; - let expected_name = format!("rustc_trans-{}", backend_name); + let expected_name = format!("rustc_codegen_llvm-{}", backend_name); for entry in d.filter_map(|e| e.ok()) { let path = entry.path(); let filename = match path.file_name().and_then(|s| s.to_str()) { @@ -520,20 +522,20 @@ fn run_compiler_with_pool<'a>( return (Err(CompileIncomplete::Stopped), Some(sess)); } - let trans = get_trans(&sess); + let codegen_backend = get_codegen_backend(&sess); rustc_lint::register_builtins(&mut sess.lint_store.borrow_mut(), Some(&sess)); let mut cfg = config::build_configuration(&sess, cfg); - target_features::add_configuration(&mut cfg, &sess, &*trans); + target_features::add_configuration(&mut cfg, &sess, &*codegen_backend); sess.parse_sess.config = cfg; let result = { let plugins = sess.opts.debugging_opts.extra_plugins.clone(); - let cstore = CStore::new(trans.metadata_loader()); + let cstore = CStore::new(codegen_backend.metadata_loader()); - do_or_return!(callbacks.late_callback(&*trans, + do_or_return!(callbacks.late_callback(&*codegen_backend, &matches, &sess, &cstore, @@ -545,7 +547,7 @@ fn run_compiler_with_pool<'a>( let control = callbacks.build_controller(&sess, &matches); - driver::compile_input(trans, + driver::compile_input(codegen_backend, &sess, &cstore, &input_file_path, @@ -659,7 +661,7 @@ pub trait CompilerCalls<'a> { // be called just before actual compilation starts (and before build_controller // is called), after all arguments etc. have been completely handled. fn late_callback(&mut self, - _: &TransCrate, + _: &CodegenBackend, _: &getopts::Matches, _: &Session, _: &CrateStore, @@ -841,11 +843,11 @@ impl<'a> CompilerCalls<'a> for RustcDefaultCalls { } rustc_lint::register_builtins(&mut sess.lint_store.borrow_mut(), Some(&sess)); let mut cfg = config::build_configuration(&sess, cfg.clone()); - let trans = get_trans(&sess); - target_features::add_configuration(&mut cfg, &sess, &*trans); + let codegen_backend = get_codegen_backend(&sess); + target_features::add_configuration(&mut cfg, &sess, &*codegen_backend); sess.parse_sess.config = cfg; let should_stop = RustcDefaultCalls::print_crate_info( - &*trans, + &*codegen_backend, &sess, None, odir, @@ -863,7 +865,7 @@ impl<'a> CompilerCalls<'a> for RustcDefaultCalls { } fn late_callback(&mut self, - trans: &TransCrate, + codegen_backend: &CodegenBackend, matches: &getopts::Matches, sess: &Session, cstore: &CrateStore, @@ -871,7 +873,7 @@ impl<'a> CompilerCalls<'a> for RustcDefaultCalls { odir: &Option, ofile: &Option) -> Compilation { - RustcDefaultCalls::print_crate_info(trans, sess, Some(input), odir, ofile) + RustcDefaultCalls::print_crate_info(codegen_backend, sess, Some(input), odir, ofile) .and_then(|| RustcDefaultCalls::list_metadata(sess, cstore, matches, input)) } @@ -1000,7 +1002,7 @@ impl RustcDefaultCalls { } - fn print_crate_info(trans: &TransCrate, + fn print_crate_info(codegen_backend: &CodegenBackend, sess: &Session, input: Option<&Input>, odir: &Option, @@ -1042,14 +1044,14 @@ impl RustcDefaultCalls { }; let attrs = attrs.as_ref().unwrap(); let t_outputs = driver::build_output_filenames(input, odir, ofile, attrs, sess); - let id = rustc_trans_utils::link::find_crate_name(Some(sess), attrs, input); + let id = rustc_codegen_utils::link::find_crate_name(Some(sess), attrs, input); if *req == PrintRequest::CrateName { println!("{}", id); continue; } let crate_types = driver::collect_crate_types(sess, attrs); for &style in &crate_types { - let fname = rustc_trans_utils::link::filename_for_input( + let fname = rustc_codegen_utils::link::filename_for_input( sess, style, &id, @@ -1102,7 +1104,7 @@ impl RustcDefaultCalls { } } RelocationModels | CodeModels | TlsModels | TargetCPUs | TargetFeatures => { - trans.print(*req, sess); + codegen_backend.print(*req, sess); } // Any output here interferes with Cargo's parsing of other printed output PrintRequest::NativeStaticLibs => {} @@ -1143,7 +1145,7 @@ pub fn version(binary: &str, matches: &getopts::Matches) { println!("commit-date: {}", unw(commit_date_str())); println!("host: {}", config::host_triple()); println!("release: {}", unw(release_str())); - get_trans_sysroot("llvm")().print_version(); + get_codegen_sysroot("llvm")().print_version(); } } @@ -1451,7 +1453,7 @@ pub fn handle_options(args: &[String]) -> Option { } if cg_flags.contains(&"passes=list".to_string()) { - get_trans_sysroot("llvm")().print_passes(); + get_codegen_sysroot("llvm")().print_passes(); return None; } @@ -1663,8 +1665,8 @@ pub fn diagnostics_registry() -> errors::registry::Registry { all_errors.extend_from_slice(&rustc_resolve::DIAGNOSTICS); all_errors.extend_from_slice(&rustc_privacy::DIAGNOSTICS); // FIXME: need to figure out a way to get these back in here - // all_errors.extend_from_slice(get_trans(sess).diagnostics()); - all_errors.extend_from_slice(&rustc_trans_utils::DIAGNOSTICS); + // all_errors.extend_from_slice(get_codegen_backend(sess).diagnostics()); + all_errors.extend_from_slice(&rustc_codegen_utils::DIAGNOSTICS); all_errors.extend_from_slice(&rustc_metadata::DIAGNOSTICS); all_errors.extend_from_slice(&rustc_passes::DIAGNOSTICS); all_errors.extend_from_slice(&rustc_plugin::DIAGNOSTICS); diff --git a/src/librustc_driver/pretty.rs b/src/librustc_driver/pretty.rs index 108b4762338..f1ad14237ee 100644 --- a/src/librustc_driver/pretty.rs +++ b/src/librustc_driver/pretty.rs @@ -228,8 +228,8 @@ impl PpSourceMode { } PpmTyped => { let control = &driver::CompileController::basic(); - let trans = ::get_trans(sess); - abort_on_err(driver::phase_3_run_analysis_passes(&*trans, + let codegen_backend = ::get_codegen_backend(sess); + abort_on_err(driver::phase_3_run_analysis_passes(&*codegen_backend, control, sess, cstore, @@ -1089,8 +1089,8 @@ fn print_with_analysis<'tcx, 'a: 'tcx>(sess: &'a Session, let mut out = Vec::new(); let control = &driver::CompileController::basic(); - let trans = ::get_trans(sess); - abort_on_err(driver::phase_3_run_analysis_passes(&*trans, + let codegen_backend = ::get_codegen_backend(sess); + abort_on_err(driver::phase_3_run_analysis_passes(&*codegen_backend, control, sess, cstore, diff --git a/src/librustc_driver/test.rs b/src/librustc_driver/test.rs index 78c95a5ce05..46f552ed28d 100644 --- a/src/librustc_driver/test.rs +++ b/src/librustc_driver/test.rs @@ -122,7 +122,7 @@ fn test_env_with_pool( None, diagnostic_handler, Lrc::new(CodeMap::new(FilePathMapping::empty()))); - let cstore = CStore::new(::get_trans(&sess).metadata_loader()); + let cstore = CStore::new(::get_codegen_backend(&sess).metadata_loader()); rustc_lint::register_builtins(&mut sess.lint_store.borrow_mut(), Some(&sess)); let input = config::Input::Str { name: FileName::Anon, diff --git a/src/librustc_incremental/assert_dep_graph.rs b/src/librustc_incremental/assert_dep_graph.rs index 38e891008f7..c317d31b95a 100644 --- a/src/librustc_incremental/assert_dep_graph.rs +++ b/src/librustc_incremental/assert_dep_graph.rs @@ -13,7 +13,7 @@ //! will dump graphs in graphviz form to disk, and it searches for //! `#[rustc_if_this_changed]` and `#[rustc_then_this_would_need]` //! annotations. These annotations can be used to test whether paths -//! exist in the graph. These checks run after trans, so they view the +//! exist in the graph. These checks run after codegen, so they view the //! the final state of the dependency graph. Note that there are //! similar assertions found in `persist::dirty_clean` which check the //! **initial** state of the dependency graph, just after it has been @@ -36,10 +36,10 @@ //! #[rustc_if_this_changed(Hir)] //! fn foo() { } //! -//! #[rustc_then_this_would_need(trans)] //~ ERROR no path from `foo` +//! #[rustc_then_this_would_need(codegen)] //~ ERROR no path from `foo` //! fn bar() { } //! -//! #[rustc_then_this_would_need(trans)] //~ ERROR OK +//! #[rustc_then_this_would_need(codegen)] //~ ERROR OK //! fn baz() { foo(); } //! ``` diff --git a/src/librustc_incremental/assert_module_sources.rs b/src/librustc_incremental/assert_module_sources.rs index 6906dacfc5e..df8e0f056af 100644 --- a/src/librustc_incremental/assert_module_sources.rs +++ b/src/librustc_incremental/assert_module_sources.rs @@ -16,7 +16,7 @@ //! //! ``` //! #![rustc_partition_reused(module="spike", cfg="rpass2")] -//! #![rustc_partition_translated(module="spike-x", cfg="rpass2")] +//! #![rustc_partition_codegened(module="spike-x", cfg="rpass2")] //! ``` //! //! The first indicates (in the cfg `rpass2`) that `spike.o` will be @@ -32,13 +32,13 @@ use rustc::mir::mono::CodegenUnit; use rustc::ty::TyCtxt; use syntax::ast; use syntax_pos::symbol::Symbol; -use rustc::ich::{ATTR_PARTITION_REUSED, ATTR_PARTITION_TRANSLATED}; +use rustc::ich::{ATTR_PARTITION_REUSED, ATTR_PARTITION_CODEGENED}; const MODULE: &'static str = "module"; const CFG: &'static str = "cfg"; #[derive(Debug, PartialEq, Clone, Copy)] -enum Disposition { Reused, Translated } +enum Disposition { Reused, Codegened } pub fn assert_module_sources<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>) { tcx.dep_graph.with_ignore(|| { @@ -61,8 +61,8 @@ impl<'a, 'tcx> AssertModuleSource<'a, 'tcx> { fn check_attr(&self, attr: &ast::Attribute) { let disposition = if attr.check_name(ATTR_PARTITION_REUSED) { Disposition::Reused - } else if attr.check_name(ATTR_PARTITION_TRANSLATED) { - Disposition::Translated + } else if attr.check_name(ATTR_PARTITION_CODEGENED) { + Disposition::Codegened } else { return; }; @@ -84,17 +84,17 @@ impl<'a, 'tcx> AssertModuleSource<'a, 'tcx> { (Disposition::Reused, false) => { self.tcx.sess.span_err( attr.span, - &format!("expected module named `{}` to be Reused but is Translated", + &format!("expected module named `{}` to be Reused but is Codegened", mname)); } - (Disposition::Translated, true) => { + (Disposition::Codegened, true) => { self.tcx.sess.span_err( attr.span, - &format!("expected module named `{}` to be Translated but is Reused", + &format!("expected module named `{}` to be Codegened but is Reused", mname)); } (Disposition::Reused, true) | - (Disposition::Translated, false) => { + (Disposition::Codegened, false) => { // These are what we would expect. } } diff --git a/src/librustc_incremental/persist/dirty_clean.rs b/src/librustc_incremental/persist/dirty_clean.rs index e114606a631..8f406161831 100644 --- a/src/librustc_incremental/persist/dirty_clean.rs +++ b/src/librustc_incremental/persist/dirty_clean.rs @@ -239,8 +239,8 @@ pub fn check_dirty_clean_annotations<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>) { intravisit::walk_crate(&mut all_attrs, krate); // Note that we cannot use the existing "unused attribute"-infrastructure - // here, since that is running before trans. This is also the reason why - // all trans-specific attributes are `Whitelisted` in syntax::feature_gate. + // here, since that is running before codegen. This is also the reason why + // all codegen-specific attributes are `Whitelisted` in syntax::feature_gate. all_attrs.report_unchecked_attrs(&dirty_clean_visitor.checked_attrs); }) } diff --git a/src/librustc_lint/types.rs b/src/librustc_lint/types.rs index 32963146893..2abe361233f 100644 --- a/src/librustc_lint/types.rs +++ b/src/librustc_lint/types.rs @@ -432,7 +432,7 @@ enum FfiResult<'tcx> { /// "nullable pointer optimization". Currently restricted /// to function pointers and references, but could be /// expanded to cover NonZero raw pointers and newtypes. -/// FIXME: This duplicates code in trans. +/// FIXME: This duplicates code in codegen. fn is_repr_nullable_ptr<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def: &'tcx ty::AdtDef, substs: &Substs<'tcx>) diff --git a/src/librustc_llvm/ffi.rs b/src/librustc_llvm/ffi.rs index c1e32c7c022..0497de940ca 100644 --- a/src/librustc_llvm/ffi.rs +++ b/src/librustc_llvm/ffi.rs @@ -515,7 +515,7 @@ pub enum ModuleBuffer {} // dllimport/dllexport are applied and need to be correct for everything to // link successfully. The #[link] annotation here says "these symbols are // included statically" which means that they're all exported with dllexport -// and from the rustc_llvm dynamic library. Otherwise the rustc_trans dynamic +// and from the rustc_llvm dynamic library. Otherwise the rustc_codegen_llvm dynamic // library would not be able to access these symbols. #[link(name = "rustllvm", kind = "static")] extern "C" { diff --git a/src/librustc_llvm/lib.rs b/src/librustc_llvm/lib.rs index bf8a087ab55..07830d54d0c 100644 --- a/src/librustc_llvm/lib.rs +++ b/src/librustc_llvm/lib.rs @@ -138,7 +138,7 @@ pub fn SetFunctionCallConv(fn_: ValueRef, cc: CallConv) { } } -// Externally visible symbols that might appear in multiple translation units need to appear in +// Externally visible symbols that might appear in multiple codegen units need to appear in // their own comdat section so that the duplicates can be discarded at link time. This can for // example happen for generics when using multiple codegen units. This function simply uses the // value's name as the comdat value to make sure that it is in a 1-to-1 relationship to the diff --git a/src/librustc_metadata/diagnostics.rs b/src/librustc_metadata/diagnostics.rs index 0a1662dd42d..b38c1235573 100644 --- a/src/librustc_metadata/diagnostics.rs +++ b/src/librustc_metadata/diagnostics.rs @@ -14,7 +14,7 @@ register_long_diagnostics! { E0454: r##" A link name was given with an empty name. Erroneous code example: -```ignore (cannot-test-this-because-rustdoc-stops-compile-fail-before-trans) +```ignore (cannot-test-this-because-rustdoc-stops-compile-fail-before-codegen) #[link(name = "")] extern {} // error: #[link(name = "")] given with empty name ``` @@ -51,7 +51,7 @@ https://doc.rust-lang.org/book/first-edition/conditional-compilation.html E0458: r##" An unknown "kind" was specified for a link attribute. Erroneous code example: -```ignore (cannot-test-this-because-rustdoc-stops-compile-fail-before-trans) +```ignore (cannot-test-this-because-rustdoc-stops-compile-fail-before-codegen) #[link(kind = "wonderful_unicorn")] extern {} // error: unknown kind: `wonderful_unicorn` ``` @@ -67,7 +67,7 @@ Please specify a valid "kind" value, from one of the following: E0459: r##" A link was used without a name parameter. Erroneous code example: -```ignore (cannot-test-this-because-rustdoc-stops-compile-fail-before-trans) +```ignore (cannot-test-this-because-rustdoc-stops-compile-fail-before-codegen) #[link(kind = "dylib")] extern {} // error: #[link(...)] specified without `name = "foo"` ``` diff --git a/src/librustc_metadata/encoder.rs b/src/librustc_metadata/encoder.rs index 3de90abd966..bbc4120f060 100644 --- a/src/librustc_metadata/encoder.rs +++ b/src/librustc_metadata/encoder.rs @@ -868,7 +868,7 @@ impl<'a, 'b: 'a, 'tcx: 'b> IsolatedEncoder<'a, 'b, 'tcx> { fn metadata_output_only(&self) -> bool { // MIR optimisation can be skipped when we're just interested in the metadata. - !self.tcx.sess.opts.output_types.should_trans() + !self.tcx.sess.opts.output_types.should_codegen() } fn const_qualif(&self, mir: u8, body_id: hir::BodyId) -> ConstQualif { @@ -930,7 +930,7 @@ impl<'a, 'b: 'a, 'tcx: 'b> IsolatedEncoder<'a, 'b, 'tcx> { hir::ImplItemKind::Method(ref sig, _) => { let generics = self.tcx.generics_of(def_id); let needs_inline = (generics.requires_monomorphization(self.tcx) || - tcx.trans_fn_attrs(def_id).requests_inline()) && + tcx.codegen_fn_attrs(def_id).requests_inline()) && !self.metadata_output_only(); let is_const_fn = sig.constness == hir::Constness::Const; let always_encode_mir = self.tcx.sess.opts.debugging_opts.always_encode_mir; @@ -1224,8 +1224,9 @@ impl<'a, 'b: 'a, 'tcx: 'b> IsolatedEncoder<'a, 'b, 'tcx> { hir::ItemConst(..) => self.encode_optimized_mir(def_id), hir::ItemFn(_, _, constness, _, ref generics, _) => { let has_tps = generics.ty_params().next().is_some(); - let needs_inline = (has_tps || tcx.trans_fn_attrs(def_id).requests_inline()) && - !self.metadata_output_only(); + let needs_inline = + (has_tps || tcx.codegen_fn_attrs(def_id).requests_inline()) && + !self.metadata_output_only(); let always_encode_mir = self.tcx.sess.opts.debugging_opts.always_encode_mir; if needs_inline || constness == hir::Constness::Const || always_encode_mir { self.encode_optimized_mir(def_id) diff --git a/src/librustc_mir/build/expr/as_rvalue.rs b/src/librustc_mir/build/expr/as_rvalue.rs index 20e11abca9f..4115dbe6274 100644 --- a/src/librustc_mir/build/expr/as_rvalue.rs +++ b/src/librustc_mir/build/expr/as_rvalue.rs @@ -140,7 +140,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { block.and(Rvalue::Cast(CastKind::Unsize, source, expr.ty)) } ExprKind::Array { fields } => { - // (*) We would (maybe) be closer to trans if we + // (*) We would (maybe) be closer to codegen if we // handled this and other aggregate cases via // `into()`, not `as_rvalue` -- in that case, instead // of generating diff --git a/src/librustc_mir/build/expr/mod.rs b/src/librustc_mir/build/expr/mod.rs index 025e77343e7..0fd4b8e7e23 100644 --- a/src/librustc_mir/build/expr/mod.rs +++ b/src/librustc_mir/build/expr/mod.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -//! Translates expressions into MIR. As a caller into this module, you +//! Builds MIR from expressions. As a caller into this module, you //! have many options, but the first thing you have to decide is //! whether you are evaluating this expression for its *value*, its //! *location*, or as a *constant*. @@ -41,7 +41,7 @@ //! ### Implementation notes //! //! For any given kind of expression, there is generally one way that -//! can be translated most naturally. This is specified by the +//! can be lowered most naturally. This is specified by the //! `Category::of` function in the `category` module. For example, a //! struct expression (or other expression that creates a new value) //! is typically easiest to write in terms of `as_rvalue` or `into`, diff --git a/src/librustc_mir/build/matches/mod.rs b/src/librustc_mir/build/matches/mod.rs index 6946ac4c7b2..f3953d0877c 100644 --- a/src/librustc_mir/build/matches/mod.rs +++ b/src/librustc_mir/build/matches/mod.rs @@ -896,7 +896,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { .map(|b| GuardFrameLocal::new(b.var_id, b.binding_mode)) .collect(), }; - debug!("Entering guard translation context: {:?}", guard_frame); + debug!("Entering guard building context: {:?}", guard_frame); self.guard_context.push(guard_frame); } else { self.bind_matched_candidate_for_arm_body(block, &candidate.bindings, false); @@ -909,7 +909,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { let cond = unpack!(block = self.as_local_operand(block, guard)); if autoref { let guard_frame = self.guard_context.pop().unwrap(); - debug!("Exiting guard translation context with locals: {:?}", guard_frame); + debug!("Exiting guard building context with locals: {:?}", guard_frame); } let false_edge_block = self.cfg.start_new_block(); diff --git a/src/librustc_mir/build/mod.rs b/src/librustc_mir/build/mod.rs index afbcf100b05..3bbf73ec8b5 100644 --- a/src/librustc_mir/build/mod.rs +++ b/src/librustc_mir/build/mod.rs @@ -775,7 +775,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { /////////////////////////////////////////////////////////////////////////// // Builder methods are broken up into modules, depending on what kind -// of thing is being translated. Note that they use the `unpack` macro +// of thing is being lowered. Note that they use the `unpack` macro // above extensively. mod block; diff --git a/src/librustc_mir/build/scope.rs b/src/librustc_mir/build/scope.rs index a631ab27d1c..5dc59ac9f41 100644 --- a/src/librustc_mir/build/scope.rs +++ b/src/librustc_mir/build/scope.rs @@ -10,7 +10,7 @@ /*! Managing the scope stack. The scopes are tied to lexical scopes, so as -we descend the HAIR, we push a scope on the stack, translate ite +we descend the HAIR, we push a scope on the stack, build its contents, and then pop it off. Every scope is named by a `region::Scope`. @@ -30,7 +30,7 @@ them. Eventually, when we shift to non-lexical lifetimes, there should be no need to remember this mapping. There is one additional wrinkle, actually, that I wanted to hide from -you but duty compels me to mention. In the course of translating +you but duty compels me to mention. In the course of building matches, it sometimes happen that certain code (namely guards) gets executed multiple times. This means that the scope lexical scope may in fact correspond to multiple, disjoint SEME regions. So in fact our @@ -38,7 +38,7 @@ mapping is from one scope to a vector of SEME regions. ### Drops -The primary purpose for scopes is to insert drops: while translating +The primary purpose for scopes is to insert drops: while building the contents, we also accumulate places that need to be dropped upon exit from each scope. This is done by calling `schedule_drop`. Once a drop is scheduled, whenever we branch out we will insert drops of all diff --git a/src/librustc_mir/hair/cx/block.rs b/src/librustc_mir/hair/cx/block.rs index 14aa307f0ae..5ef1eef133d 100644 --- a/src/librustc_mir/hair/cx/block.rs +++ b/src/librustc_mir/hair/cx/block.rs @@ -20,7 +20,7 @@ impl<'tcx> Mirror<'tcx> for &'tcx hir::Block { type Output = Block<'tcx>; fn make_mirror<'a, 'gcx>(self, cx: &mut Cx<'a, 'gcx, 'tcx>) -> Block<'tcx> { - // We have to eagerly translate the "spine" of the statements + // We have to eagerly lower the "spine" of the statements // in order to get the lexical scoping correctly. let stmts = mirror_stmts(cx, self.hir_id.local_id, &*self.stmts); let opt_destruction_scope = diff --git a/src/librustc_mir/hair/cx/mod.rs b/src/librustc_mir/hair/cx/mod.rs index 4765a82d85b..b01b3542136 100644 --- a/src/librustc_mir/hair/cx/mod.rs +++ b/src/librustc_mir/hair/cx/mod.rs @@ -77,7 +77,7 @@ impl<'a, 'gcx, 'tcx> Cx<'a, 'gcx, 'tcx> { // Some functions always have overflow checks enabled, // however, they may not get codegen'd, depending on - // the settings for the crate they are translated in. + // the settings for the crate they are codegened in. let mut check_overflow = attr::contains_name(attrs, "rustc_inherit_overflow_checks"); // Respect -C overflow-checks. diff --git a/src/librustc_mir/hair/mod.rs b/src/librustc_mir/hair/mod.rs index c27250267bb..ab9acdc4950 100644 --- a/src/librustc_mir/hair/mod.rs +++ b/src/librustc_mir/hair/mod.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -//! The MIR is translated from some high-level abstract IR +//! The MIR is built from some high-level abstract IR //! (HAIR). This section defines the HAIR along with a trait for //! accessing it. The intention is to allow MIR construction to be //! unit-tested and separated from the Rust source and compiler data @@ -107,12 +107,12 @@ pub enum StmtKind<'tcx> { }, } -/// The Hair trait implementor translates their expressions (`&'tcx H::Expr`) -/// into instances of this `Expr` enum. This translation can be done +/// The Hair trait implementor lowers their expressions (`&'tcx H::Expr`) +/// into instances of this `Expr` enum. This lowering can be done /// basically as lazily or as eagerly as desired: every recursive /// reference to an expression in this enum is an `ExprRef<'tcx>`, which /// may in turn be another instance of this enum (boxed), or else an -/// untranslated `&'tcx H::Expr`. Note that instances of `Expr` are very +/// unlowered `&'tcx H::Expr`. Note that instances of `Expr` are very /// shortlived. They are created by `Hair::to_expr`, analyzed and /// converted into MIR, and then discarded. /// diff --git a/src/librustc_mir/monomorphize/collector.rs b/src/librustc_mir/monomorphize/collector.rs index dc2009a0260..c62eb1cf185 100644 --- a/src/librustc_mir/monomorphize/collector.rs +++ b/src/librustc_mir/monomorphize/collector.rs @@ -188,7 +188,7 @@ //! this is not implemented however: a mono item will be produced //! regardless of whether it is actually needed or not. -use rustc::hir::{self, TransFnAttrFlags}; +use rustc::hir::{self, CodegenFnAttrFlags}; use rustc::hir::itemlikevisit::ItemLikeVisitor; use rustc::hir::map as hir_map; @@ -343,9 +343,9 @@ fn collect_roots<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, visitor.push_extra_entry_roots(); } - // We can only translate items that are instantiable - items all of + // We can only codegen items that are instantiable - items all of // whose predicates hold. Luckily, items that aren't instantiable - // can't actually be used, so we can just skip translating them. + // can't actually be used, so we can just skip codegenning them. roots.retain(|root| root.is_instantiable(tcx)); roots @@ -717,7 +717,7 @@ fn visit_instance_use<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, } } -// Returns true if we should translate an instance in the local crate. +// Returns true if we should codegen an instance in the local crate. // Returns false if we can just link to the upstream crate and therefore don't // need a mono item. fn should_monomorphize_locally<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, instance: &Instance<'tcx>) @@ -734,7 +734,7 @@ fn should_monomorphize_locally<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, instance: return match tcx.hir.get_if_local(def_id) { Some(hir_map::NodeForeignItem(..)) => { - false // foreign items are linked against, not translated. + false // foreign items are linked against, not codegened. } Some(_) => true, None => { @@ -1015,8 +1015,8 @@ impl<'b, 'a, 'v> RootCollector<'b, 'a, 'v> { MonoItemCollectionMode::Lazy => { self.entry_fn == Some(def_id) || self.tcx.is_reachable_non_generic(def_id) || - self.tcx.trans_fn_attrs(def_id).flags.contains( - TransFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL) + self.tcx.codegen_fn_attrs(def_id).flags.contains( + CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL) } } } diff --git a/src/librustc_mir/monomorphize/item.rs b/src/librustc_mir/monomorphize/item.rs index a569ad00d0c..27f8254bf8a 100644 --- a/src/librustc_mir/monomorphize/item.rs +++ b/src/librustc_mir/monomorphize/item.rs @@ -29,7 +29,7 @@ use syntax_pos::symbol::Symbol; use syntax::codemap::Span; pub use rustc::mir::mono::MonoItem; -/// Describes how a translation item will be instantiated in object files. +/// Describes how a monomorphization will be instantiated in object files. #[derive(PartialEq, Eq, Clone, Copy, Debug, Hash)] pub enum InstantiationMode { /// There will be exactly one instance of the given MonoItem. It will have @@ -37,7 +37,7 @@ pub enum InstantiationMode { GloballyShared { /// In some compilation scenarios we may decide to take functions that /// are typically `LocalCopy` and instead move them to `GloballyShared` - /// to avoid translating them a bunch of times. In this situation, + /// to avoid codegenning them a bunch of times. In this situation, /// however, our local copy may conflict with other crates also /// inlining the same function. /// @@ -114,7 +114,7 @@ pub trait MonoItemExt<'a, 'tcx>: fmt::Debug { // creating one copy of this `#[inline]` function which may // conflict with upstream crates as it could be an exported // symbol. - match tcx.trans_fn_attrs(instance.def_id()).inline { + match tcx.codegen_fn_attrs(instance.def_id()).inline { InlineAttr::Always => InstantiationMode::LocalCopy, _ => { InstantiationMode::GloballyShared { may_conflict: true } @@ -137,19 +137,19 @@ pub trait MonoItemExt<'a, 'tcx>: fmt::Debug { MonoItem::GlobalAsm(..) => return None, }; - let trans_fn_attrs = tcx.trans_fn_attrs(def_id); - trans_fn_attrs.linkage + let codegen_fn_attrs = tcx.codegen_fn_attrs(def_id); + codegen_fn_attrs.linkage } /// Returns whether this instance is instantiable - whether it has no unsatisfied /// predicates. /// - /// In order to translate an item, all of its predicates must hold, because + /// In order to codegen an item, all of its predicates must hold, because /// otherwise the item does not make sense. Type-checking ensures that /// the predicates of every item that is *used by* a valid item *do* /// hold, so we can rely on that. /// - /// However, we translate collector roots (reachable items) and functions + /// However, we codegen collector roots (reachable items) and functions /// in vtables when they are seen, even if they are not used, and so they /// might not be instantiable. For example, a programmer can define this /// public function: @@ -158,7 +158,7 @@ pub trait MonoItemExt<'a, 'tcx>: fmt::Debug { /// <&mut () as Clone>::clone(&s); /// } /// - /// That function can't be translated, because the method `<&mut () as Clone>::clone` + /// That function can't be codegened, because the method `<&mut () as Clone>::clone` /// does not exist. Luckily for us, that function can't ever be used, /// because that would require for `&'a mut (): Clone` to hold, so we /// can just not emit any code, or even a linker reference for it. @@ -229,7 +229,7 @@ impl<'a, 'tcx> MonoItemExt<'a, 'tcx> for MonoItem<'tcx> { // MonoItem String Keys //=----------------------------------------------------------------------------- -// The code below allows for producing a unique string key for a trans item. +// The code below allows for producing a unique string key for a mono item. // These keys are used by the handwritten auto-tests, so they need to be // predictable and human-readable. // diff --git a/src/librustc_mir/monomorphize/mod.rs b/src/librustc_mir/monomorphize/mod.rs index 04d4f7a3968..51793305513 100644 --- a/src/librustc_mir/monomorphize/mod.rs +++ b/src/librustc_mir/monomorphize/mod.rs @@ -23,11 +23,11 @@ pub mod item; pub mod partitioning; #[inline(never)] // give this a place in the profiler -pub fn assert_symbols_are_distinct<'a, 'tcx, I>(tcx: TyCtxt<'a, 'tcx, 'tcx>, trans_items: I) +pub fn assert_symbols_are_distinct<'a, 'tcx, I>(tcx: TyCtxt<'a, 'tcx, 'tcx>, mono_items: I) where I: Iterator> { - let mut symbols: Vec<_> = trans_items.map(|trans_item| { - (trans_item, trans_item.symbol_name(tcx)) + let mut symbols: Vec<_> = mono_items.map(|mono_item| { + (mono_item, mono_item.symbol_name(tcx)) }).collect(); (&mut symbols[..]).sort_by(|&(_, ref sym1), &(_, ref sym2)|{ @@ -39,11 +39,11 @@ pub fn assert_symbols_are_distinct<'a, 'tcx, I>(tcx: TyCtxt<'a, 'tcx, 'tcx>, tra let sym2 = &pair[1].1; if *sym1 == *sym2 { - let trans_item1 = pair[0].0; - let trans_item2 = pair[1].0; + let mono_item1 = pair[0].0; + let mono_item2 = pair[1].0; - let span1 = trans_item1.local_span(tcx); - let span2 = trans_item2.local_span(tcx); + let span1 = mono_item1.local_span(tcx); + let span2 = mono_item2.local_span(tcx); // Deterministically select one of the spans for error reporting let span = match (span1, span2) { @@ -111,7 +111,7 @@ fn needs_fn_once_adapter_shim(actual_closure_kind: ty::ClosureKind, } (ty::ClosureKind::Fn, ty::ClosureKind::FnMut) => { // The closure fn `llfn` is a `fn(&self, ...)`. We want a - // `fn(&mut self, ...)`. In fact, at trans time, these are + // `fn(&mut self, ...)`. In fact, at codegen time, these are // basically the same thing, so we can just return llfn. Ok(false) } @@ -124,7 +124,7 @@ fn needs_fn_once_adapter_shim(actual_closure_kind: ty::ClosureKind, // fn call_once(self, ...) { call_mut(&self, ...) } // fn call_once(mut self, ...) { call_mut(&mut self, ...) } // - // These are both the same at trans time. + // These are both the same at codegen time. Ok(true) } _ => Err(()), @@ -167,7 +167,7 @@ pub fn custom_coerce_unsize_info<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, substs: tcx.mk_substs_trait(source_ty, &[target_ty]) }); - match tcx.trans_fulfill_obligation( (ty::ParamEnv::reveal_all(), trait_ref)) { + match tcx.codegen_fulfill_obligation( (ty::ParamEnv::reveal_all(), trait_ref)) { traits::VtableImpl(traits::VtableImplData { impl_def_id, .. }) => { tcx.coerce_unsized_info(impl_def_id).custom_kind.unwrap() } diff --git a/src/librustc_mir/monomorphize/partitioning.rs b/src/librustc_mir/monomorphize/partitioning.rs index 3a65cd4ea77..9607a84124a 100644 --- a/src/librustc_mir/monomorphize/partitioning.rs +++ b/src/librustc_mir/monomorphize/partitioning.rs @@ -11,16 +11,16 @@ //! Partitioning Codegen Units for Incremental Compilation //! ====================================================== //! -//! The task of this module is to take the complete set of translation items of +//! The task of this module is to take the complete set of monomorphizations of //! a crate and produce a set of codegen units from it, where a codegen unit -//! is a named set of (translation-item, linkage) pairs. That is, this module -//! decides which translation item appears in which codegen units with which +//! is a named set of (mono-item, linkage) pairs. That is, this module +//! decides which monomorphization appears in which codegen units with which //! linkage. The following paragraphs describe some of the background on the //! partitioning scheme. //! //! The most important opportunity for saving on compilation time with -//! incremental compilation is to avoid re-translating and re-optimizing code. -//! Since the unit of translation and optimization for LLVM is "modules" or, how +//! incremental compilation is to avoid re-codegenning and re-optimizing code. +//! Since the unit of codegen and optimization for LLVM is "modules" or, how //! we call them "codegen units", the particulars of how much time can be saved //! by incremental compilation are tightly linked to how the output program is //! partitioned into these codegen units prior to passing it to LLVM -- @@ -214,17 +214,17 @@ fn fallback_cgu_name(tcx: TyCtxt) -> InternedString { pub fn partition<'a, 'tcx, I>(tcx: TyCtxt<'a, 'tcx, 'tcx>, - trans_items: I, + mono_items: I, strategy: PartitioningStrategy, inlining_map: &InliningMap<'tcx>) -> Vec> where I: Iterator> { - // In the first step, we place all regular translation items into their - // respective 'home' codegen unit. Regular translation items are all + // In the first step, we place all regular monomorphizations into their + // respective 'home' codegen unit. Regular monomorphizations are all // functions and statics defined in the local crate. - let mut initial_partitioning = place_root_translation_items(tcx, - trans_items); + let mut initial_partitioning = place_root_mono_items(tcx, + mono_items); initial_partitioning.codegen_units.iter_mut().for_each(|cgu| cgu.estimate_size(&tcx)); @@ -239,10 +239,10 @@ pub fn partition<'a, 'tcx, I>(tcx: TyCtxt<'a, 'tcx, 'tcx>, } // In the next step, we use the inlining map to determine which additional - // translation items have to go into each codegen unit. These additional - // translation items can be drop-glue, functions from external crates, and + // monomorphizations have to go into each codegen unit. These additional + // monomorphizations can be drop-glue, functions from external crates, and // local functions the definition of which is marked with #[inline]. - let mut post_inlining = place_inlined_translation_items(initial_partitioning, + let mut post_inlining = place_inlined_mono_items(initial_partitioning, inlining_map); post_inlining.codegen_units.iter_mut().for_each(|cgu| cgu.estimate_size(&tcx)); @@ -258,7 +258,7 @@ pub fn partition<'a, 'tcx, I>(tcx: TyCtxt<'a, 'tcx, 'tcx>, // Finally, sort by codegen unit name, so that we get deterministic results let PostInliningPartitioning { codegen_units: mut result, - trans_item_placements: _, + mono_item_placements: _, internalization_candidates: _, } = post_inlining; @@ -275,23 +275,23 @@ struct PreInliningPartitioning<'tcx> { internalization_candidates: FxHashSet>, } -/// For symbol internalization, we need to know whether a symbol/trans-item is +/// For symbol internalization, we need to know whether a symbol/mono-item is /// accessed from outside the codegen unit it is defined in. This type is used /// to keep track of that. #[derive(Clone, PartialEq, Eq, Debug)] -enum TransItemPlacement { +enum MonoItemPlacement { SingleCgu { cgu_name: InternedString }, MultipleCgus, } struct PostInliningPartitioning<'tcx> { codegen_units: Vec>, - trans_item_placements: FxHashMap, TransItemPlacement>, + mono_item_placements: FxHashMap, MonoItemPlacement>, internalization_candidates: FxHashSet>, } -fn place_root_translation_items<'a, 'tcx, I>(tcx: TyCtxt<'a, 'tcx, 'tcx>, - trans_items: I) +fn place_root_mono_items<'a, 'tcx, I>(tcx: TyCtxt<'a, 'tcx, 'tcx>, + mono_items: I) -> PreInliningPartitioning<'tcx> where I: Iterator> { @@ -307,15 +307,15 @@ fn place_root_translation_items<'a, 'tcx, I>(tcx: TyCtxt<'a, 'tcx, 'tcx>, let export_generics = tcx.share_generics() && tcx.local_crate_exports_generics(); - for trans_item in trans_items { - match trans_item.instantiation_mode(tcx) { + for mono_item in mono_items { + match mono_item.instantiation_mode(tcx) { InstantiationMode::GloballyShared { .. } => {} InstantiationMode::LocalCopy => continue, } - let characteristic_def_id = characteristic_def_id_of_trans_item(tcx, trans_item); + let characteristic_def_id = characteristic_def_id_of_mono_item(tcx, mono_item); let is_volatile = is_incremental_build && - trans_item.is_generic_fn(); + mono_item.is_generic_fn(); let codegen_unit_name = match characteristic_def_id { Some(def_id) => compute_codegen_unit_name(tcx, def_id, is_volatile), @@ -353,10 +353,10 @@ fn place_root_translation_items<'a, 'tcx, I>(tcx: TyCtxt<'a, 'tcx, 'tcx>, Visibility::Hidden } }; - let (linkage, mut visibility) = match trans_item.explicit_linkage(tcx) { + let (linkage, mut visibility) = match mono_item.explicit_linkage(tcx) { Some(explicit_linkage) => (explicit_linkage, Visibility::Default), None => { - match trans_item { + match mono_item { MonoItem::Fn(ref instance) => { let visibility = match instance.def { InstanceDef::Item(def_id) => { @@ -463,11 +463,11 @@ fn place_root_translation_items<'a, 'tcx, I>(tcx: TyCtxt<'a, 'tcx, 'tcx>, } }; if visibility == Visibility::Hidden && can_be_internalized { - internalization_candidates.insert(trans_item); + internalization_candidates.insert(mono_item); } - codegen_unit.items_mut().insert(trans_item, (linkage, visibility)); - roots.insert(trans_item); + codegen_unit.items_mut().insert(mono_item, (linkage, visibility)); + roots.insert(mono_item); } // always ensure we have at least one CGU; otherwise, if we have a @@ -522,11 +522,11 @@ fn merge_codegen_units<'tcx>(initial_partitioning: &mut PreInliningPartitioning< } } -fn place_inlined_translation_items<'tcx>(initial_partitioning: PreInliningPartitioning<'tcx>, +fn place_inlined_mono_items<'tcx>(initial_partitioning: PreInliningPartitioning<'tcx>, inlining_map: &InliningMap<'tcx>) -> PostInliningPartitioning<'tcx> { let mut new_partitioning = Vec::new(); - let mut trans_item_placements = FxHashMap(); + let mut mono_item_placements = FxHashMap(); let PreInliningPartitioning { codegen_units: initial_cgus, @@ -545,40 +545,40 @@ fn place_inlined_translation_items<'tcx>(initial_partitioning: PreInliningPartit let mut new_codegen_unit = CodegenUnit::new(old_codegen_unit.name().clone()); - // Add all translation items that are not already there - for trans_item in reachable { - if let Some(linkage) = old_codegen_unit.items().get(&trans_item) { + // Add all monomorphizations that are not already there + for mono_item in reachable { + if let Some(linkage) = old_codegen_unit.items().get(&mono_item) { // This is a root, just copy it over - new_codegen_unit.items_mut().insert(trans_item, *linkage); + new_codegen_unit.items_mut().insert(mono_item, *linkage); } else { - if roots.contains(&trans_item) { - bug!("GloballyShared trans-item inlined into other CGU: \ - {:?}", trans_item); + if roots.contains(&mono_item) { + bug!("GloballyShared mono-item inlined into other CGU: \ + {:?}", mono_item); } // This is a cgu-private copy new_codegen_unit.items_mut().insert( - trans_item, + mono_item, (Linkage::Internal, Visibility::Default), ); } if !single_codegen_unit { // If there is more than one codegen unit, we need to keep track - // in which codegen units each translation item is placed: - match trans_item_placements.entry(trans_item) { + // in which codegen units each monomorphization is placed: + match mono_item_placements.entry(mono_item) { Entry::Occupied(e) => { let placement = e.into_mut(); debug_assert!(match *placement { - TransItemPlacement::SingleCgu { ref cgu_name } => { + MonoItemPlacement::SingleCgu { ref cgu_name } => { *cgu_name != *new_codegen_unit.name() } - TransItemPlacement::MultipleCgus => true, + MonoItemPlacement::MultipleCgus => true, }); - *placement = TransItemPlacement::MultipleCgus; + *placement = MonoItemPlacement::MultipleCgus; } Entry::Vacant(e) => { - e.insert(TransItemPlacement::SingleCgu { + e.insert(MonoItemPlacement::SingleCgu { cgu_name: new_codegen_unit.name().clone() }); } @@ -591,18 +591,18 @@ fn place_inlined_translation_items<'tcx>(initial_partitioning: PreInliningPartit return PostInliningPartitioning { codegen_units: new_partitioning, - trans_item_placements, + mono_item_placements, internalization_candidates, }; - fn follow_inlining<'tcx>(trans_item: MonoItem<'tcx>, + fn follow_inlining<'tcx>(mono_item: MonoItem<'tcx>, inlining_map: &InliningMap<'tcx>, visited: &mut FxHashSet>) { - if !visited.insert(trans_item) { + if !visited.insert(mono_item) { return; } - inlining_map.with_inlining_candidates(trans_item, |target| { + inlining_map.with_inlining_candidates(mono_item, |target| { follow_inlining(target, inlining_map, visited); }); } @@ -625,7 +625,7 @@ fn internalize_symbols<'a, 'tcx>(_tcx: TyCtxt<'a, 'tcx, 'tcx>, return; } - // Build a map from every translation item to all the translation items that + // Build a map from every monomorphization to all the monomorphizations that // reference it. let mut accessor_map: FxHashMap, Vec>> = FxHashMap(); inlining_map.iter_accesses(|accessor, accessees| { @@ -636,12 +636,12 @@ fn internalize_symbols<'a, 'tcx>(_tcx: TyCtxt<'a, 'tcx, 'tcx>, } }); - let trans_item_placements = &partitioning.trans_item_placements; + let mono_item_placements = &partitioning.mono_item_placements; // For each internalization candidates in each codegen unit, check if it is // accessed from outside its defining codegen unit. for cgu in &mut partitioning.codegen_units { - let home_cgu = TransItemPlacement::SingleCgu { + let home_cgu = MonoItemPlacement::SingleCgu { cgu_name: cgu.name().clone() }; @@ -650,14 +650,14 @@ fn internalize_symbols<'a, 'tcx>(_tcx: TyCtxt<'a, 'tcx, 'tcx>, // This item is no candidate for internalizing, so skip it. continue } - debug_assert_eq!(trans_item_placements[accessee], home_cgu); + debug_assert_eq!(mono_item_placements[accessee], home_cgu); if let Some(accessors) = accessor_map.get(accessee) { if accessors.iter() .filter_map(|accessor| { // Some accessors might not have been // instantiated. We can safely ignore those. - trans_item_placements.get(accessor) + mono_item_placements.get(accessor) }) .any(|placement| *placement != home_cgu) { // Found an accessor from another CGU, so skip to the next @@ -667,16 +667,16 @@ fn internalize_symbols<'a, 'tcx>(_tcx: TyCtxt<'a, 'tcx, 'tcx>, } // If we got here, we did not find any accesses from other CGUs, - // so it's fine to make this translation item internal. + // so it's fine to make this monomorphization internal. *linkage_and_visibility = (Linkage::Internal, Visibility::Default); } } } -fn characteristic_def_id_of_trans_item<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, - trans_item: MonoItem<'tcx>) +fn characteristic_def_id_of_mono_item<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, + mono_item: MonoItem<'tcx>) -> Option { - match trans_item { + match mono_item { MonoItem::Fn(instance) => { let def_id = match instance.def { ty::InstanceDef::Item(def_id) => def_id, @@ -771,14 +771,14 @@ fn debug_dump<'a, 'b, 'tcx, I>(tcx: TyCtxt<'a, 'tcx, 'tcx>, for cgu in cgus { debug!("CodegenUnit {}:", cgu.name()); - for (trans_item, linkage) in cgu.items() { - let symbol_name = trans_item.symbol_name(tcx).name.as_str(); + for (mono_item, linkage) in cgu.items() { + let symbol_name = mono_item.symbol_name(tcx).name.as_str(); let symbol_hash_start = symbol_name.rfind('h'); let symbol_hash = symbol_hash_start.map(|i| &symbol_name[i ..]) .unwrap_or(""); debug!(" - {} [{:?}] [{}]", - trans_item.to_string(tcx), + mono_item.to_string(tcx), linkage, symbol_hash); } diff --git a/src/librustc_mir/shim.rs b/src/librustc_mir/shim.rs index c79298d8dd2..2d2663bc7c3 100644 --- a/src/librustc_mir/shim.rs +++ b/src/librustc_mir/shim.rs @@ -69,8 +69,8 @@ fn make_shim<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, ) } ty::InstanceDef::Virtual(def_id, _) => { - // We are translating a call back to our def-id, which - // trans::mir knows to turn to an actual virtual call. + // We are generating a call back to our def-id, which the + // codegen backend knows to turn to an actual virtual call. build_call_shim( tcx, def_id, diff --git a/src/librustc_mir/transform/add_call_guards.rs b/src/librustc_mir/transform/add_call_guards.rs index 5be369f85bc..26927904440 100644 --- a/src/librustc_mir/transform/add_call_guards.rs +++ b/src/librustc_mir/transform/add_call_guards.rs @@ -30,7 +30,7 @@ pub use self::AddCallGuards::*; * do at the end of the predecessor block, or at the start of the * successor block. Critical edges have to be broken in order to prevent * "edge actions" from affecting other edges. We need this for calls that are - * translated to LLVM invoke instructions, because invoke is a block terminator + * codegened to LLVM invoke instructions, because invoke is a block terminator * in LLVM so we can't insert any code to handle the call's result into the * block that performs the call. * diff --git a/src/librustc_mir/transform/elaborate_drops.rs b/src/librustc_mir/transform/elaborate_drops.rs index 56050318ca7..15b0c4a8bf7 100644 --- a/src/librustc_mir/transform/elaborate_drops.rs +++ b/src/librustc_mir/transform/elaborate_drops.rs @@ -442,7 +442,7 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> { } /// Elaborate a MIR `replace` terminator. This instruction - /// is not directly handled by translation, and therefore + /// is not directly handled by codegen, and therefore /// must be desugared. /// /// The desugaring drops the location if needed, and then writes diff --git a/src/librustc_mir/transform/erase_regions.rs b/src/librustc_mir/transform/erase_regions.rs index cd5ebae2d9d..c697391d867 100644 --- a/src/librustc_mir/transform/erase_regions.rs +++ b/src/librustc_mir/transform/erase_regions.rs @@ -9,7 +9,7 @@ // except according to those terms. //! This pass erases all early-bound regions from the types occurring in the MIR. -//! We want to do this once just before trans, so trans does not have to take +//! We want to do this once just before codegen, so codegen does not have to take //! care erasing regions all over the place. //! NOTE: We do NOT erase regions of statements that are relevant for //! "types-as-contracts"-validation, namely, AcquireValid, ReleaseValid, and EndRegion. diff --git a/src/librustc_mir/transform/inline.rs b/src/librustc_mir/transform/inline.rs index ee6d42b1fe5..5fb9148d49a 100644 --- a/src/librustc_mir/transform/inline.rs +++ b/src/librustc_mir/transform/inline.rs @@ -11,7 +11,7 @@ //! Inlining pass for MIR functions use rustc::hir; -use rustc::hir::TransFnAttrFlags; +use rustc::hir::CodegenFnAttrFlags; use rustc::hir::def_id::DefId; use rustc_data_structures::bitvec::BitVector; @@ -211,16 +211,16 @@ impl<'a, 'tcx> Inliner<'a, 'tcx> { return false; } - // Do not inline {u,i}128 lang items, trans const eval depends + // Do not inline {u,i}128 lang items, codegen const eval depends // on detecting calls to these lang items and intercepting them if tcx.is_binop_lang_item(callsite.callee).is_some() { debug!(" not inlining 128bit integer lang item"); return false; } - let trans_fn_attrs = tcx.trans_fn_attrs(callsite.callee); + let codegen_fn_attrs = tcx.codegen_fn_attrs(callsite.callee); - let hinted = match trans_fn_attrs.inline { + let hinted = match codegen_fn_attrs.inline { // Just treat inline(always) as a hint for now, // there are cases that prevent inlining that we // need to check for first. @@ -250,7 +250,7 @@ impl<'a, 'tcx> Inliner<'a, 'tcx> { }; // Significantly lower the threshold for inlining cold functions - if trans_fn_attrs.flags.contains(TransFnAttrFlags::COLD) { + if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::COLD) { threshold /= 5; } @@ -355,7 +355,7 @@ impl<'a, 'tcx> Inliner<'a, 'tcx> { } } - if let attr::InlineAttr::Always = trans_fn_attrs.inline { + if let attr::InlineAttr::Always = codegen_fn_attrs.inline { debug!("INLINING {:?} because inline(always) [cost={}]", callsite, cost); true } else { @@ -515,8 +515,8 @@ impl<'a, 'tcx> Inliner<'a, 'tcx> { // Fn::call(closure_ref, tuple_tmp) // // meanwhile the closure body expects the arguments (here, `a`, `b`, and `c`) - // as distinct arguments. (This is the "rust-call" ABI hack.) Normally, trans has - // the job of unpacking this tuple. But here, we are trans. =) So we want to create + // as distinct arguments. (This is the "rust-call" ABI hack.) Normally, codegen has + // the job of unpacking this tuple. But here, we are codegen. =) So we want to create // a vector like // // [closure_ref, tuple_tmp.0, tuple_tmp.1, tuple_tmp.2] diff --git a/src/librustc_mir/transform/mod.rs b/src/librustc_mir/transform/mod.rs index 63ca35aa0e7..e2f2312dbd2 100644 --- a/src/librustc_mir/transform/mod.rs +++ b/src/librustc_mir/transform/mod.rs @@ -277,7 +277,7 @@ fn optimized_mir<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) -> &'tcx simplify::SimplifyLocals, add_call_guards::CriticalCallEdges, - dump_mir::Marker("PreTrans"), + dump_mir::Marker("PreCodegen"), ]; tcx.alloc_mir(mir) } diff --git a/src/librustc_mir/transform/simplify.rs b/src/librustc_mir/transform/simplify.rs index 691fdd130e5..7e23a5cb1a8 100644 --- a/src/librustc_mir/transform/simplify.rs +++ b/src/librustc_mir/transform/simplify.rs @@ -15,7 +15,7 @@ //! //! The `SimplifyLocals` pass is kinda expensive and therefore not very suitable to be run often. //! Most of the passes should not care or be impacted in meaningful ways due to extra locals -//! either, so running the pass once, right before translation, should suffice. +//! either, so running the pass once, right before codegen, should suffice. //! //! On the other side of the spectrum, the `SimplifyCfg` pass is considerably cheap to run, thus //! one should run it after every pass which may modify CFG in significant ways. This pass must diff --git a/src/librustc_trans/Cargo.toml b/src/librustc_trans/Cargo.toml deleted file mode 100644 index 64d3a4f4d53..00000000000 --- a/src/librustc_trans/Cargo.toml +++ /dev/null @@ -1,49 +0,0 @@ -[package] -authors = ["The Rust Project Developers"] -name = "rustc_trans" -version = "0.0.0" - -[lib] -name = "rustc_trans" -path = "lib.rs" -crate-type = ["dylib"] -test = false - -[dependencies] -bitflags = "1.0.1" -cc = "1.0.1" -flate2 = "1.0" -jobserver = "0.1.5" -libc = "0.2" -log = "0.4" -num_cpus = "1.0" -rustc = { path = "../librustc" } -rustc-demangle = "0.1.4" -rustc_allocator = { path = "../librustc_allocator" } -rustc_apfloat = { path = "../librustc_apfloat" } -rustc_target = { path = "../librustc_target" } -rustc_data_structures = { path = "../librustc_data_structures" } -rustc_errors = { path = "../librustc_errors" } -rustc_incremental = { path = "../librustc_incremental" } -rustc_llvm = { path = "../librustc_llvm" } -rustc_platform_intrinsics = { path = "../librustc_platform_intrinsics" } -rustc_trans_utils = { path = "../librustc_trans_utils" } -rustc_mir = { path = "../librustc_mir" } -serialize = { path = "../libserialize" } -syntax = { path = "../libsyntax" } -syntax_pos = { path = "../libsyntax_pos" } -tempdir = "0.3" - -# not actually used but needed to make sure we enable the same feature set as -# winapi used in librustc -env_logger = { version = "0.5", default-features = false } - -[features] -# Used to communicate the feature to `rustc_target` in the same manner that the -# `rustc` driver script communicate this. -jemalloc = ["rustc_target/jemalloc"] - -# This is used to convince Cargo to separately cache builds of `rustc_trans` -# when this option is enabled or not. That way we can build two, cache two -# artifacts, and have nice speedy rebuilds. -emscripten = ["rustc_llvm/emscripten"] diff --git a/src/librustc_trans/README.md b/src/librustc_trans/README.md deleted file mode 100644 index d1868ba2abb..00000000000 --- a/src/librustc_trans/README.md +++ /dev/null @@ -1,7 +0,0 @@ -The `trans` crate contains the code to convert from MIR into LLVM IR, -and then from LLVM IR into machine code. In general it contains code -that runs towards the end of the compilation process. - -For more information about how trans works, see the [rustc guide]. - -[rustc guide]: https://rust-lang-nursery.github.io/rustc-guide/trans.html diff --git a/src/librustc_trans/abi.rs b/src/librustc_trans/abi.rs deleted file mode 100644 index 25c598c532c..00000000000 --- a/src/librustc_trans/abi.rs +++ /dev/null @@ -1,696 +0,0 @@ -// Copyright 2012-2016 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -use llvm::{self, ValueRef, AttributePlace}; -use base; -use builder::{Builder, MemFlags}; -use common::{ty_fn_sig, C_usize}; -use context::CodegenCx; -use mir::place::PlaceRef; -use mir::operand::OperandValue; -use type_::Type; -use type_of::{LayoutLlvmExt, PointerKind}; - -use rustc_target::abi::{LayoutOf, Size, TyLayout}; -use rustc::ty::{self, Ty}; -use rustc::ty::layout; - -use libc::c_uint; - -pub use rustc_target::spec::abi::Abi; -pub use rustc::ty::layout::{FAT_PTR_ADDR, FAT_PTR_EXTRA}; -pub use rustc_target::abi::call::*; - -macro_rules! for_each_kind { - ($flags: ident, $f: ident, $($kind: ident),+) => ({ - $(if $flags.contains(ArgAttribute::$kind) { $f(llvm::Attribute::$kind) })+ - }) -} - -trait ArgAttributeExt { - fn for_each_kind(&self, f: F) where F: FnMut(llvm::Attribute); -} - -impl ArgAttributeExt for ArgAttribute { - fn for_each_kind(&self, mut f: F) where F: FnMut(llvm::Attribute) { - for_each_kind!(self, f, - ByVal, NoAlias, NoCapture, NonNull, ReadOnly, SExt, StructRet, ZExt, InReg) - } -} - -pub trait ArgAttributesExt { - fn apply_llfn(&self, idx: AttributePlace, llfn: ValueRef); - fn apply_callsite(&self, idx: AttributePlace, callsite: ValueRef); -} - -impl ArgAttributesExt for ArgAttributes { - fn apply_llfn(&self, idx: AttributePlace, llfn: ValueRef) { - let mut regular = self.regular; - unsafe { - let deref = self.pointee_size.bytes(); - if deref != 0 { - if regular.contains(ArgAttribute::NonNull) { - llvm::LLVMRustAddDereferenceableAttr(llfn, - idx.as_uint(), - deref); - } else { - llvm::LLVMRustAddDereferenceableOrNullAttr(llfn, - idx.as_uint(), - deref); - } - regular -= ArgAttribute::NonNull; - } - if let Some(align) = self.pointee_align { - llvm::LLVMRustAddAlignmentAttr(llfn, - idx.as_uint(), - align.abi() as u32); - } - regular.for_each_kind(|attr| attr.apply_llfn(idx, llfn)); - } - } - - fn apply_callsite(&self, idx: AttributePlace, callsite: ValueRef) { - let mut regular = self.regular; - unsafe { - let deref = self.pointee_size.bytes(); - if deref != 0 { - if regular.contains(ArgAttribute::NonNull) { - llvm::LLVMRustAddDereferenceableCallSiteAttr(callsite, - idx.as_uint(), - deref); - } else { - llvm::LLVMRustAddDereferenceableOrNullCallSiteAttr(callsite, - idx.as_uint(), - deref); - } - regular -= ArgAttribute::NonNull; - } - if let Some(align) = self.pointee_align { - llvm::LLVMRustAddAlignmentCallSiteAttr(callsite, - idx.as_uint(), - align.abi() as u32); - } - regular.for_each_kind(|attr| attr.apply_callsite(idx, callsite)); - } - } -} - -pub trait LlvmType { - fn llvm_type(&self, cx: &CodegenCx) -> Type; -} - -impl LlvmType for Reg { - fn llvm_type(&self, cx: &CodegenCx) -> Type { - match self.kind { - RegKind::Integer => Type::ix(cx, self.size.bits()), - RegKind::Float => { - match self.size.bits() { - 32 => Type::f32(cx), - 64 => Type::f64(cx), - _ => bug!("unsupported float: {:?}", self) - } - } - RegKind::Vector => { - Type::vector(&Type::i8(cx), self.size.bytes()) - } - } - } -} - -impl LlvmType for CastTarget { - fn llvm_type(&self, cx: &CodegenCx) -> Type { - let rest_ll_unit = self.rest.unit.llvm_type(cx); - let rest_count = self.rest.total.bytes() / self.rest.unit.size.bytes(); - let rem_bytes = self.rest.total.bytes() % self.rest.unit.size.bytes(); - - if self.prefix.iter().all(|x| x.is_none()) { - // Simplify to a single unit when there is no prefix and size <= unit size - if self.rest.total <= self.rest.unit.size { - return rest_ll_unit; - } - - // Simplify to array when all chunks are the same size and type - if rem_bytes == 0 { - return Type::array(&rest_ll_unit, rest_count); - } - } - - // Create list of fields in the main structure - let mut args: Vec<_> = - self.prefix.iter().flat_map(|option_kind| option_kind.map( - |kind| Reg { kind: kind, size: self.prefix_chunk }.llvm_type(cx))) - .chain((0..rest_count).map(|_| rest_ll_unit)) - .collect(); - - // Append final integer - if rem_bytes != 0 { - // Only integers can be really split further. - assert_eq!(self.rest.unit.kind, RegKind::Integer); - args.push(Type::ix(cx, rem_bytes * 8)); - } - - Type::struct_(cx, &args, false) - } -} - -pub trait ArgTypeExt<'a, 'tcx> { - fn memory_ty(&self, cx: &CodegenCx<'a, 'tcx>) -> Type; - fn store(&self, bx: &Builder<'a, 'tcx>, val: ValueRef, dst: PlaceRef<'tcx>); - fn store_fn_arg(&self, bx: &Builder<'a, 'tcx>, idx: &mut usize, dst: PlaceRef<'tcx>); -} - -impl<'a, 'tcx> ArgTypeExt<'a, 'tcx> for ArgType<'tcx, Ty<'tcx>> { - /// Get the LLVM type for a place of the original Rust type of - /// this argument/return, i.e. the result of `type_of::type_of`. - fn memory_ty(&self, cx: &CodegenCx<'a, 'tcx>) -> Type { - self.layout.llvm_type(cx) - } - - /// Store a direct/indirect value described by this ArgType into a - /// place for the original Rust type of this argument/return. - /// Can be used for both storing formal arguments into Rust variables - /// or results of call/invoke instructions into their destinations. - fn store(&self, bx: &Builder<'a, 'tcx>, val: ValueRef, dst: PlaceRef<'tcx>) { - if self.is_ignore() { - return; - } - let cx = bx.cx; - if self.is_indirect() { - OperandValue::Ref(val, self.layout.align).store(bx, dst) - } else if let PassMode::Cast(cast) = self.mode { - // FIXME(eddyb): Figure out when the simpler Store is safe, clang - // uses it for i16 -> {i8, i8}, but not for i24 -> {i8, i8, i8}. - let can_store_through_cast_ptr = false; - if can_store_through_cast_ptr { - let cast_dst = bx.pointercast(dst.llval, cast.llvm_type(cx).ptr_to()); - bx.store(val, cast_dst, self.layout.align); - } else { - // The actual return type is a struct, but the ABI - // adaptation code has cast it into some scalar type. The - // code that follows is the only reliable way I have - // found to do a transform like i64 -> {i32,i32}. - // Basically we dump the data onto the stack then memcpy it. - // - // Other approaches I tried: - // - Casting rust ret pointer to the foreign type and using Store - // is (a) unsafe if size of foreign type > size of rust type and - // (b) runs afoul of strict aliasing rules, yielding invalid - // assembly under -O (specifically, the store gets removed). - // - Truncating foreign type to correct integral type and then - // bitcasting to the struct type yields invalid cast errors. - - // We instead thus allocate some scratch space... - let scratch_size = cast.size(cx); - let scratch_align = cast.align(cx); - let llscratch = bx.alloca(cast.llvm_type(cx), "abi_cast", scratch_align); - bx.lifetime_start(llscratch, scratch_size); - - // ...where we first store the value... - bx.store(val, llscratch, scratch_align); - - // ...and then memcpy it to the intended destination. - base::call_memcpy(bx, - bx.pointercast(dst.llval, Type::i8p(cx)), - bx.pointercast(llscratch, Type::i8p(cx)), - C_usize(cx, self.layout.size.bytes()), - self.layout.align.min(scratch_align), - MemFlags::empty()); - - bx.lifetime_end(llscratch, scratch_size); - } - } else { - OperandValue::Immediate(val).store(bx, dst); - } - } - - fn store_fn_arg(&self, bx: &Builder<'a, 'tcx>, idx: &mut usize, dst: PlaceRef<'tcx>) { - let mut next = || { - let val = llvm::get_param(bx.llfn(), *idx as c_uint); - *idx += 1; - val - }; - match self.mode { - PassMode::Ignore => {}, - PassMode::Pair(..) => { - OperandValue::Pair(next(), next()).store(bx, dst); - } - PassMode::Direct(_) | PassMode::Indirect(_) | PassMode::Cast(_) => { - self.store(bx, next(), dst); - } - } - } -} - -pub trait FnTypeExt<'a, 'tcx> { - fn of_instance(cx: &CodegenCx<'a, 'tcx>, instance: &ty::Instance<'tcx>) - -> Self; - fn new(cx: &CodegenCx<'a, 'tcx>, - sig: ty::FnSig<'tcx>, - extra_args: &[Ty<'tcx>]) -> Self; - fn new_vtable(cx: &CodegenCx<'a, 'tcx>, - sig: ty::FnSig<'tcx>, - extra_args: &[Ty<'tcx>]) -> Self; - fn unadjusted(cx: &CodegenCx<'a, 'tcx>, - sig: ty::FnSig<'tcx>, - extra_args: &[Ty<'tcx>]) -> Self; - fn adjust_for_abi(&mut self, - cx: &CodegenCx<'a, 'tcx>, - abi: Abi); - fn llvm_type(&self, cx: &CodegenCx<'a, 'tcx>) -> Type; - fn llvm_cconv(&self) -> llvm::CallConv; - fn apply_attrs_llfn(&self, llfn: ValueRef); - fn apply_attrs_callsite(&self, bx: &Builder<'a, 'tcx>, callsite: ValueRef); -} - -impl<'a, 'tcx> FnTypeExt<'a, 'tcx> for FnType<'tcx, Ty<'tcx>> { - fn of_instance(cx: &CodegenCx<'a, 'tcx>, instance: &ty::Instance<'tcx>) - -> Self { - let fn_ty = instance.ty(cx.tcx); - let sig = ty_fn_sig(cx, fn_ty); - let sig = cx.tcx.normalize_erasing_late_bound_regions(ty::ParamEnv::reveal_all(), &sig); - FnType::new(cx, sig, &[]) - } - - fn new(cx: &CodegenCx<'a, 'tcx>, - sig: ty::FnSig<'tcx>, - extra_args: &[Ty<'tcx>]) -> Self { - let mut fn_ty = FnType::unadjusted(cx, sig, extra_args); - fn_ty.adjust_for_abi(cx, sig.abi); - fn_ty - } - - fn new_vtable(cx: &CodegenCx<'a, 'tcx>, - sig: ty::FnSig<'tcx>, - extra_args: &[Ty<'tcx>]) -> Self { - let mut fn_ty = FnType::unadjusted(cx, sig, extra_args); - // Don't pass the vtable, it's not an argument of the virtual fn. - { - let self_arg = &mut fn_ty.args[0]; - match self_arg.mode { - PassMode::Pair(data_ptr, _) => { - self_arg.mode = PassMode::Direct(data_ptr); - } - _ => bug!("FnType::new_vtable: non-pair self {:?}", self_arg) - } - - let pointee = self_arg.layout.ty.builtin_deref(true) - .unwrap_or_else(|| { - bug!("FnType::new_vtable: non-pointer self {:?}", self_arg) - }).ty; - let fat_ptr_ty = cx.tcx.mk_mut_ptr(pointee); - self_arg.layout = cx.layout_of(fat_ptr_ty).field(cx, 0); - } - fn_ty.adjust_for_abi(cx, sig.abi); - fn_ty - } - - fn unadjusted(cx: &CodegenCx<'a, 'tcx>, - sig: ty::FnSig<'tcx>, - extra_args: &[Ty<'tcx>]) -> Self { - debug!("FnType::unadjusted({:?}, {:?})", sig, extra_args); - - use self::Abi::*; - let conv = match cx.sess().target.target.adjust_abi(sig.abi) { - RustIntrinsic | PlatformIntrinsic | - Rust | RustCall => Conv::C, - - // It's the ABI's job to select this, not us. - System => bug!("system abi should be selected elsewhere"), - - Stdcall => Conv::X86Stdcall, - Fastcall => Conv::X86Fastcall, - Vectorcall => Conv::X86VectorCall, - Thiscall => Conv::X86ThisCall, - C => Conv::C, - Unadjusted => Conv::C, - Win64 => Conv::X86_64Win64, - SysV64 => Conv::X86_64SysV, - Aapcs => Conv::ArmAapcs, - PtxKernel => Conv::PtxKernel, - Msp430Interrupt => Conv::Msp430Intr, - X86Interrupt => Conv::X86Intr, - - // These API constants ought to be more specific... - Cdecl => Conv::C, - }; - - let mut inputs = sig.inputs(); - let extra_args = if sig.abi == RustCall { - assert!(!sig.variadic && extra_args.is_empty()); - - match sig.inputs().last().unwrap().sty { - ty::TyTuple(ref tupled_arguments) => { - inputs = &sig.inputs()[0..sig.inputs().len() - 1]; - tupled_arguments - } - _ => { - bug!("argument to function with \"rust-call\" ABI \ - is not a tuple"); - } - } - } else { - assert!(sig.variadic || extra_args.is_empty()); - extra_args - }; - - let target = &cx.sess().target.target; - let win_x64_gnu = target.target_os == "windows" - && target.arch == "x86_64" - && target.target_env == "gnu"; - let linux_s390x = target.target_os == "linux" - && target.arch == "s390x" - && target.target_env == "gnu"; - let rust_abi = match sig.abi { - RustIntrinsic | PlatformIntrinsic | Rust | RustCall => true, - _ => false - }; - - // Handle safe Rust thin and fat pointers. - let adjust_for_rust_scalar = |attrs: &mut ArgAttributes, - scalar: &layout::Scalar, - layout: TyLayout<'tcx, Ty<'tcx>>, - offset: Size, - is_return: bool| { - // Booleans are always an i1 that needs to be zero-extended. - if scalar.is_bool() { - attrs.set(ArgAttribute::ZExt); - return; - } - - // Only pointer types handled below. - if scalar.value != layout::Pointer { - return; - } - - if scalar.valid_range.start() < scalar.valid_range.end() { - if *scalar.valid_range.start() > 0 { - attrs.set(ArgAttribute::NonNull); - } - } - - if let Some(pointee) = layout.pointee_info_at(cx, offset) { - if let Some(kind) = pointee.safe { - attrs.pointee_size = pointee.size; - attrs.pointee_align = Some(pointee.align); - - // HACK(eddyb) LLVM inserts `llvm.assume` calls when inlining functions - // with align attributes, and those calls later block optimizations. - if !is_return && !cx.tcx.sess.opts.debugging_opts.arg_align_attributes { - attrs.pointee_align = None; - } - - // `Box` pointer parameters never alias because ownership is transferred - // `&mut` pointer parameters never alias other parameters, - // or mutable global data - // - // `&T` where `T` contains no `UnsafeCell` is immutable, - // and can be marked as both `readonly` and `noalias`, as - // LLVM's definition of `noalias` is based solely on memory - // dependencies rather than pointer equality - let no_alias = match kind { - PointerKind::Shared => false, - PointerKind::UniqueOwned => true, - PointerKind::Frozen | - PointerKind::UniqueBorrowed => !is_return - }; - if no_alias { - attrs.set(ArgAttribute::NoAlias); - } - - if kind == PointerKind::Frozen && !is_return { - attrs.set(ArgAttribute::ReadOnly); - } - } - } - }; - - let arg_of = |ty: Ty<'tcx>, is_return: bool| { - let mut arg = ArgType::new(cx.layout_of(ty)); - if arg.layout.is_zst() { - // For some forsaken reason, x86_64-pc-windows-gnu - // doesn't ignore zero-sized struct arguments. - // The same is true for s390x-unknown-linux-gnu. - if is_return || rust_abi || (!win_x64_gnu && !linux_s390x) { - arg.mode = PassMode::Ignore; - } - } - - // FIXME(eddyb) other ABIs don't have logic for scalar pairs. - if !is_return && rust_abi { - if let layout::Abi::ScalarPair(ref a, ref b) = arg.layout.abi { - let mut a_attrs = ArgAttributes::new(); - let mut b_attrs = ArgAttributes::new(); - adjust_for_rust_scalar(&mut a_attrs, - a, - arg.layout, - Size::from_bytes(0), - false); - adjust_for_rust_scalar(&mut b_attrs, - b, - arg.layout, - a.value.size(cx).abi_align(b.value.align(cx)), - false); - arg.mode = PassMode::Pair(a_attrs, b_attrs); - return arg; - } - } - - if let layout::Abi::Scalar(ref scalar) = arg.layout.abi { - if let PassMode::Direct(ref mut attrs) = arg.mode { - adjust_for_rust_scalar(attrs, - scalar, - arg.layout, - Size::from_bytes(0), - is_return); - } - } - - arg - }; - - FnType { - ret: arg_of(sig.output(), true), - args: inputs.iter().chain(extra_args.iter()).map(|ty| { - arg_of(ty, false) - }).collect(), - variadic: sig.variadic, - conv, - } - } - - fn adjust_for_abi(&mut self, - cx: &CodegenCx<'a, 'tcx>, - abi: Abi) { - if abi == Abi::Unadjusted { return } - - if abi == Abi::Rust || abi == Abi::RustCall || - abi == Abi::RustIntrinsic || abi == Abi::PlatformIntrinsic { - let fixup = |arg: &mut ArgType<'tcx, Ty<'tcx>>| { - if arg.is_ignore() { return; } - - match arg.layout.abi { - layout::Abi::Aggregate { .. } => {} - - // This is a fun case! The gist of what this is doing is - // that we want callers and callees to always agree on the - // ABI of how they pass SIMD arguments. If we were to *not* - // make these arguments indirect then they'd be immediates - // in LLVM, which means that they'd used whatever the - // appropriate ABI is for the callee and the caller. That - // means, for example, if the caller doesn't have AVX - // enabled but the callee does, then passing an AVX argument - // across this boundary would cause corrupt data to show up. - // - // This problem is fixed by unconditionally passing SIMD - // arguments through memory between callers and callees - // which should get them all to agree on ABI regardless of - // target feature sets. Some more information about this - // issue can be found in #44367. - // - // Note that the platform intrinsic ABI is exempt here as - // that's how we connect up to LLVM and it's unstable - // anyway, we control all calls to it in libstd. - layout::Abi::Vector { .. } if abi != Abi::PlatformIntrinsic => { - arg.make_indirect(); - return - } - - _ => return - } - - let size = arg.layout.size; - if size > layout::Pointer.size(cx) { - arg.make_indirect(); - } else { - // We want to pass small aggregates as immediates, but using - // a LLVM aggregate type for this leads to bad optimizations, - // so we pick an appropriately sized integer type instead. - arg.cast_to(Reg { - kind: RegKind::Integer, - size - }); - } - }; - fixup(&mut self.ret); - for arg in &mut self.args { - fixup(arg); - } - if let PassMode::Indirect(ref mut attrs) = self.ret.mode { - attrs.set(ArgAttribute::StructRet); - } - return; - } - - if let Err(msg) = self.adjust_for_cabi(cx, abi) { - cx.sess().fatal(&msg); - } - } - - fn llvm_type(&self, cx: &CodegenCx<'a, 'tcx>) -> Type { - let mut llargument_tys = Vec::new(); - - let llreturn_ty = match self.ret.mode { - PassMode::Ignore => Type::void(cx), - PassMode::Direct(_) | PassMode::Pair(..) => { - self.ret.layout.immediate_llvm_type(cx) - } - PassMode::Cast(cast) => cast.llvm_type(cx), - PassMode::Indirect(_) => { - llargument_tys.push(self.ret.memory_ty(cx).ptr_to()); - Type::void(cx) - } - }; - - for arg in &self.args { - // add padding - if let Some(ty) = arg.pad { - llargument_tys.push(ty.llvm_type(cx)); - } - - let llarg_ty = match arg.mode { - PassMode::Ignore => continue, - PassMode::Direct(_) => arg.layout.immediate_llvm_type(cx), - PassMode::Pair(..) => { - llargument_tys.push(arg.layout.scalar_pair_element_llvm_type(cx, 0)); - llargument_tys.push(arg.layout.scalar_pair_element_llvm_type(cx, 1)); - continue; - } - PassMode::Cast(cast) => cast.llvm_type(cx), - PassMode::Indirect(_) => arg.memory_ty(cx).ptr_to(), - }; - llargument_tys.push(llarg_ty); - } - - if self.variadic { - Type::variadic_func(&llargument_tys, &llreturn_ty) - } else { - Type::func(&llargument_tys, &llreturn_ty) - } - } - - fn llvm_cconv(&self) -> llvm::CallConv { - match self.conv { - Conv::C => llvm::CCallConv, - Conv::ArmAapcs => llvm::ArmAapcsCallConv, - Conv::Msp430Intr => llvm::Msp430Intr, - Conv::PtxKernel => llvm::PtxKernel, - Conv::X86Fastcall => llvm::X86FastcallCallConv, - Conv::X86Intr => llvm::X86_Intr, - Conv::X86Stdcall => llvm::X86StdcallCallConv, - Conv::X86ThisCall => llvm::X86_ThisCall, - Conv::X86VectorCall => llvm::X86_VectorCall, - Conv::X86_64SysV => llvm::X86_64_SysV, - Conv::X86_64Win64 => llvm::X86_64_Win64, - } - } - - fn apply_attrs_llfn(&self, llfn: ValueRef) { - let mut i = 0; - let mut apply = |attrs: &ArgAttributes| { - attrs.apply_llfn(llvm::AttributePlace::Argument(i), llfn); - i += 1; - }; - match self.ret.mode { - PassMode::Direct(ref attrs) => { - attrs.apply_llfn(llvm::AttributePlace::ReturnValue, llfn); - } - PassMode::Indirect(ref attrs) => apply(attrs), - _ => {} - } - for arg in &self.args { - if arg.pad.is_some() { - apply(&ArgAttributes::new()); - } - match arg.mode { - PassMode::Ignore => {} - PassMode::Direct(ref attrs) | - PassMode::Indirect(ref attrs) => apply(attrs), - PassMode::Pair(ref a, ref b) => { - apply(a); - apply(b); - } - PassMode::Cast(_) => apply(&ArgAttributes::new()), - } - } - } - - fn apply_attrs_callsite(&self, bx: &Builder<'a, 'tcx>, callsite: ValueRef) { - let mut i = 0; - let mut apply = |attrs: &ArgAttributes| { - attrs.apply_callsite(llvm::AttributePlace::Argument(i), callsite); - i += 1; - }; - match self.ret.mode { - PassMode::Direct(ref attrs) => { - attrs.apply_callsite(llvm::AttributePlace::ReturnValue, callsite); - } - PassMode::Indirect(ref attrs) => apply(attrs), - _ => {} - } - if let layout::Abi::Scalar(ref scalar) = self.ret.layout.abi { - // If the value is a boolean, the range is 0..2 and that ultimately - // become 0..0 when the type becomes i1, which would be rejected - // by the LLVM verifier. - match scalar.value { - layout::Int(..) if !scalar.is_bool() => { - let range = scalar.valid_range_exclusive(bx.cx); - if range.start != range.end { - // FIXME(nox): This causes very weird type errors about - // SHL operators in constants in stage 2 with LLVM 3.9. - if unsafe { llvm::LLVMRustVersionMajor() >= 4 } { - bx.range_metadata(callsite, range); - } - } - } - _ => {} - } - } - for arg in &self.args { - if arg.pad.is_some() { - apply(&ArgAttributes::new()); - } - match arg.mode { - PassMode::Ignore => {} - PassMode::Direct(ref attrs) | - PassMode::Indirect(ref attrs) => apply(attrs), - PassMode::Pair(ref a, ref b) => { - apply(a); - apply(b); - } - PassMode::Cast(_) => apply(&ArgAttributes::new()), - } - } - - let cconv = self.llvm_cconv(); - if cconv != llvm::CCallConv { - llvm::SetInstructionCallConv(callsite, cconv); - } - } -} diff --git a/src/librustc_trans/allocator.rs b/src/librustc_trans/allocator.rs deleted file mode 100644 index 871fe98ec01..00000000000 --- a/src/librustc_trans/allocator.rs +++ /dev/null @@ -1,103 +0,0 @@ -// Copyright 2017 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -use std::ffi::CString; -use std::ptr; - -use attributes; -use libc::c_uint; -use rustc::middle::allocator::AllocatorKind; -use rustc::ty::TyCtxt; -use rustc_allocator::{ALLOCATOR_METHODS, AllocatorTy}; - -use ModuleLlvm; -use llvm::{self, False, True}; - -pub(crate) unsafe fn trans(tcx: TyCtxt, mods: &ModuleLlvm, kind: AllocatorKind) { - let llcx = mods.llcx; - let llmod = mods.llmod; - let usize = match &tcx.sess.target.target.target_pointer_width[..] { - "16" => llvm::LLVMInt16TypeInContext(llcx), - "32" => llvm::LLVMInt32TypeInContext(llcx), - "64" => llvm::LLVMInt64TypeInContext(llcx), - tws => bug!("Unsupported target word size for int: {}", tws), - }; - let i8 = llvm::LLVMInt8TypeInContext(llcx); - let i8p = llvm::LLVMPointerType(i8, 0); - let void = llvm::LLVMVoidTypeInContext(llcx); - - for method in ALLOCATOR_METHODS { - let mut args = Vec::new(); - for ty in method.inputs.iter() { - match *ty { - AllocatorTy::Layout => { - args.push(usize); // size - args.push(usize); // align - } - AllocatorTy::Ptr => args.push(i8p), - AllocatorTy::Usize => args.push(usize), - - AllocatorTy::ResultPtr | - AllocatorTy::Unit => panic!("invalid allocator arg"), - } - } - let output = match method.output { - AllocatorTy::ResultPtr => Some(i8p), - AllocatorTy::Unit => None, - - AllocatorTy::Layout | - AllocatorTy::Usize | - AllocatorTy::Ptr => panic!("invalid allocator output"), - }; - let ty = llvm::LLVMFunctionType(output.unwrap_or(void), - args.as_ptr(), - args.len() as c_uint, - False); - let name = CString::new(format!("__rust_{}", method.name)).unwrap(); - let llfn = llvm::LLVMRustGetOrInsertFunction(llmod, - name.as_ptr(), - ty); - - if tcx.sess.target.target.options.default_hidden_visibility { - llvm::LLVMRustSetVisibility(llfn, llvm::Visibility::Hidden); - } - if tcx.sess.target.target.options.requires_uwtable { - attributes::emit_uwtable(llfn, true); - } - - let callee = CString::new(kind.fn_name(method.name)).unwrap(); - let callee = llvm::LLVMRustGetOrInsertFunction(llmod, - callee.as_ptr(), - ty); - - let llbb = llvm::LLVMAppendBasicBlockInContext(llcx, - llfn, - "entry\0".as_ptr() as *const _); - - let llbuilder = llvm::LLVMCreateBuilderInContext(llcx); - llvm::LLVMPositionBuilderAtEnd(llbuilder, llbb); - let args = args.iter().enumerate().map(|(i, _)| { - llvm::LLVMGetParam(llfn, i as c_uint) - }).collect::>(); - let ret = llvm::LLVMRustBuildCall(llbuilder, - callee, - args.as_ptr(), - args.len() as c_uint, - ptr::null_mut(), - "\0".as_ptr() as *const _); - llvm::LLVMSetTailCall(ret, True); - if output.is_some() { - llvm::LLVMBuildRet(llbuilder, ret); - } else { - llvm::LLVMBuildRetVoid(llbuilder); - } - llvm::LLVMDisposeBuilder(llbuilder); - } -} diff --git a/src/librustc_trans/asm.rs b/src/librustc_trans/asm.rs deleted file mode 100644 index 751f8148a2a..00000000000 --- a/src/librustc_trans/asm.rs +++ /dev/null @@ -1,129 +0,0 @@ -// Copyright 2012-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. - -//! # Translation of inline assembly. - -use llvm::{self, ValueRef}; -use common::*; -use type_::Type; -use type_of::LayoutLlvmExt; -use builder::Builder; - -use rustc::hir; - -use mir::place::PlaceRef; -use mir::operand::OperandValue; - -use std::ffi::CString; -use syntax::ast::AsmDialect; -use libc::{c_uint, c_char}; - -// Take an inline assembly expression and splat it out via LLVM -pub fn trans_inline_asm<'a, 'tcx>( - bx: &Builder<'a, 'tcx>, - ia: &hir::InlineAsm, - outputs: Vec>, - mut inputs: Vec -) { - let mut ext_constraints = vec![]; - let mut output_types = vec![]; - - // Prepare the output operands - let mut indirect_outputs = vec![]; - for (i, (out, place)) in ia.outputs.iter().zip(&outputs).enumerate() { - if out.is_rw { - inputs.push(place.load(bx).immediate()); - ext_constraints.push(i.to_string()); - } - if out.is_indirect { - indirect_outputs.push(place.load(bx).immediate()); - } else { - output_types.push(place.layout.llvm_type(bx.cx)); - } - } - if !indirect_outputs.is_empty() { - indirect_outputs.extend_from_slice(&inputs); - inputs = indirect_outputs; - } - - let clobbers = ia.clobbers.iter() - .map(|s| format!("~{{{}}}", &s)); - - // Default per-arch clobbers - // Basically what clang does - let arch_clobbers = match &bx.sess().target.target.arch[..] { - "x86" | "x86_64" => vec!["~{dirflag}", "~{fpsr}", "~{flags}"], - "mips" | "mips64" => vec!["~{$1}"], - _ => Vec::new() - }; - - let all_constraints = - ia.outputs.iter().map(|out| out.constraint.to_string()) - .chain(ia.inputs.iter().map(|s| s.to_string())) - .chain(ext_constraints) - .chain(clobbers) - .chain(arch_clobbers.iter().map(|s| s.to_string())) - .collect::>().join(","); - - debug!("Asm Constraints: {}", &all_constraints); - - // Depending on how many outputs we have, the return type is different - let num_outputs = output_types.len(); - let output_type = match num_outputs { - 0 => Type::void(bx.cx), - 1 => output_types[0], - _ => Type::struct_(bx.cx, &output_types, false) - }; - - let dialect = match ia.dialect { - AsmDialect::Att => llvm::AsmDialect::Att, - AsmDialect::Intel => llvm::AsmDialect::Intel, - }; - - let asm = CString::new(ia.asm.as_str().as_bytes()).unwrap(); - let constraint_cstr = CString::new(all_constraints).unwrap(); - let r = bx.inline_asm_call( - asm.as_ptr(), - constraint_cstr.as_ptr(), - &inputs, - output_type, - ia.volatile, - ia.alignstack, - dialect - ); - - // Again, based on how many outputs we have - let outputs = ia.outputs.iter().zip(&outputs).filter(|&(ref o, _)| !o.is_indirect); - for (i, (_, &place)) in outputs.enumerate() { - let v = if num_outputs == 1 { r } else { bx.extract_value(r, i as u64) }; - OperandValue::Immediate(v).store(bx, place); - } - - // Store mark in a metadata node so we can map LLVM errors - // back to source locations. See #17552. - unsafe { - let key = "srcloc"; - let kind = llvm::LLVMGetMDKindIDInContext(bx.cx.llcx, - key.as_ptr() as *const c_char, key.len() as c_uint); - - let val: llvm::ValueRef = C_i32(bx.cx, ia.ctxt.outer().as_u32() as i32); - - llvm::LLVMSetMetadata(r, kind, - llvm::LLVMMDNodeInContext(bx.cx.llcx, &val, 1)); - } -} - -pub fn trans_global_asm<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, - ga: &hir::GlobalAsm) { - let asm = CString::new(ga.asm.as_str().as_bytes()).unwrap(); - unsafe { - llvm::LLVMRustAppendModuleInlineAsm(cx.llmod, asm.as_ptr()); - } -} diff --git a/src/librustc_trans/attributes.rs b/src/librustc_trans/attributes.rs deleted file mode 100644 index 5baed57092d..00000000000 --- a/src/librustc_trans/attributes.rs +++ /dev/null @@ -1,259 +0,0 @@ -// Copyright 2012-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. -//! Set and unset common attributes on LLVM values. - -use std::ffi::{CStr, CString}; - -use rustc::hir::{self, TransFnAttrFlags}; -use rustc::hir::def_id::{DefId, LOCAL_CRATE}; -use rustc::hir::itemlikevisit::ItemLikeVisitor; -use rustc::session::Session; -use rustc::session::config::Sanitizer; -use rustc::ty::TyCtxt; -use rustc::ty::maps::Providers; -use rustc_data_structures::sync::Lrc; -use rustc_data_structures::fx::FxHashMap; - -use llvm::{self, Attribute, ValueRef}; -use llvm::AttributePlace::Function; -use llvm_util; -pub use syntax::attr::{self, InlineAttr}; -use context::CodegenCx; - -/// Mark LLVM function to use provided inline heuristic. -#[inline] -pub fn inline(val: ValueRef, inline: InlineAttr) { - use self::InlineAttr::*; - match inline { - Hint => Attribute::InlineHint.apply_llfn(Function, val), - Always => Attribute::AlwaysInline.apply_llfn(Function, val), - Never => Attribute::NoInline.apply_llfn(Function, val), - None => { - Attribute::InlineHint.unapply_llfn(Function, val); - Attribute::AlwaysInline.unapply_llfn(Function, val); - Attribute::NoInline.unapply_llfn(Function, val); - }, - }; -} - -/// Tell LLVM to emit or not emit the information necessary to unwind the stack for the function. -#[inline] -pub fn emit_uwtable(val: ValueRef, emit: bool) { - Attribute::UWTable.toggle_llfn(Function, val, emit); -} - -/// Tell LLVM whether the function can or cannot unwind. -#[inline] -pub fn unwind(val: ValueRef, can_unwind: bool) { - Attribute::NoUnwind.toggle_llfn(Function, val, !can_unwind); -} - -/// Tell LLVM whether it should optimize function for size. -#[inline] -#[allow(dead_code)] // possibly useful function -pub fn set_optimize_for_size(val: ValueRef, optimize: bool) { - Attribute::OptimizeForSize.toggle_llfn(Function, val, optimize); -} - -/// Tell LLVM if this function should be 'naked', i.e. skip the epilogue and prologue. -#[inline] -pub fn naked(val: ValueRef, is_naked: bool) { - Attribute::Naked.toggle_llfn(Function, val, is_naked); -} - -pub fn set_frame_pointer_elimination(cx: &CodegenCx, llfn: ValueRef) { - if cx.sess().must_not_eliminate_frame_pointers() { - llvm::AddFunctionAttrStringValue( - llfn, llvm::AttributePlace::Function, - cstr("no-frame-pointer-elim\0"), cstr("true\0")); - } -} - -pub fn set_probestack(cx: &CodegenCx, llfn: ValueRef) { - // Only use stack probes if the target specification indicates that we - // should be using stack probes - if !cx.sess().target.target.options.stack_probes { - return - } - - // Currently stack probes seem somewhat incompatible with the address - // sanitizer. With asan we're already protected from stack overflow anyway - // so we don't really need stack probes regardless. - match cx.sess().opts.debugging_opts.sanitizer { - Some(Sanitizer::Address) => return, - _ => {} - } - - // probestack doesn't play nice either with pgo-gen. - if cx.sess().opts.debugging_opts.pgo_gen.is_some() { - return; - } - - // Flag our internal `__rust_probestack` function as the stack probe symbol. - // This is defined in the `compiler-builtins` crate for each architecture. - llvm::AddFunctionAttrStringValue( - llfn, llvm::AttributePlace::Function, - cstr("probe-stack\0"), cstr("__rust_probestack\0")); -} - -pub fn llvm_target_features(sess: &Session) -> impl Iterator { - const RUSTC_SPECIFIC_FEATURES: &[&str] = &[ - "crt-static", - ]; - - let cmdline = sess.opts.cg.target_feature.split(',') - .filter(|f| !RUSTC_SPECIFIC_FEATURES.iter().any(|s| f.contains(s))); - sess.target.target.options.features.split(',') - .chain(cmdline) - .filter(|l| !l.is_empty()) -} - -/// Composite function which sets LLVM attributes for function depending on its AST (#[attribute]) -/// attributes. -pub fn from_fn_attrs(cx: &CodegenCx, llfn: ValueRef, id: DefId) { - let trans_fn_attrs = cx.tcx.trans_fn_attrs(id); - - inline(llfn, trans_fn_attrs.inline); - - set_frame_pointer_elimination(cx, llfn); - set_probestack(cx, llfn); - - if trans_fn_attrs.flags.contains(TransFnAttrFlags::COLD) { - Attribute::Cold.apply_llfn(Function, llfn); - } - if trans_fn_attrs.flags.contains(TransFnAttrFlags::NAKED) { - naked(llfn, true); - } - if trans_fn_attrs.flags.contains(TransFnAttrFlags::ALLOCATOR) { - Attribute::NoAlias.apply_llfn( - llvm::AttributePlace::ReturnValue, llfn); - } - if trans_fn_attrs.flags.contains(TransFnAttrFlags::UNWIND) { - unwind(llfn, true); - } - if trans_fn_attrs.flags.contains(TransFnAttrFlags::RUSTC_ALLOCATOR_NOUNWIND) { - unwind(llfn, false); - } - - let features = llvm_target_features(cx.tcx.sess) - .map(|s| s.to_string()) - .chain( - trans_fn_attrs.target_features - .iter() - .map(|f| { - let feature = &*f.as_str(); - format!("+{}", llvm_util::to_llvm_feature(cx.tcx.sess, feature)) - }) - ) - .collect::>() - .join(","); - - if !features.is_empty() { - let val = CString::new(features).unwrap(); - llvm::AddFunctionAttrStringValue( - llfn, llvm::AttributePlace::Function, - cstr("target-features\0"), &val); - } - - // Note that currently the `wasm-import-module` doesn't do anything, but - // eventually LLVM 7 should read this and ferry the appropriate import - // module to the output file. - if cx.tcx.sess.target.target.arch == "wasm32" { - if let Some(module) = wasm_import_module(cx.tcx, id) { - llvm::AddFunctionAttrStringValue( - llfn, - llvm::AttributePlace::Function, - cstr("wasm-import-module\0"), - &module, - ); - } - } -} - -fn cstr(s: &'static str) -> &CStr { - CStr::from_bytes_with_nul(s.as_bytes()).expect("null-terminated string") -} - -pub fn provide(providers: &mut Providers) { - providers.target_features_whitelist = |tcx, cnum| { - assert_eq!(cnum, LOCAL_CRATE); - if tcx.sess.opts.actually_rustdoc { - // rustdoc needs to be able to document functions that use all the features, so - // whitelist them all - Lrc::new(llvm_util::all_known_features() - .map(|(a, b)| (a.to_string(), b.map(|s| s.to_string()))) - .collect()) - } else { - Lrc::new(llvm_util::target_feature_whitelist(tcx.sess) - .iter() - .map(|&(a, b)| (a.to_string(), b.map(|s| s.to_string()))) - .collect()) - } - }; - - providers.wasm_custom_sections = |tcx, cnum| { - assert_eq!(cnum, LOCAL_CRATE); - let mut finder = WasmSectionFinder { tcx, list: Vec::new() }; - tcx.hir.krate().visit_all_item_likes(&mut finder); - Lrc::new(finder.list) - }; - - provide_extern(providers); -} - -struct WasmSectionFinder<'a, 'tcx: 'a> { - tcx: TyCtxt<'a, 'tcx, 'tcx>, - list: Vec, -} - -impl<'a, 'tcx: 'a> ItemLikeVisitor<'tcx> for WasmSectionFinder<'a, 'tcx> { - fn visit_item(&mut self, i: &'tcx hir::Item) { - match i.node { - hir::ItemConst(..) => {} - _ => return, - } - if i.attrs.iter().any(|i| i.check_name("wasm_custom_section")) { - self.list.push(self.tcx.hir.local_def_id(i.id)); - } - } - - fn visit_trait_item(&mut self, _: &'tcx hir::TraitItem) {} - - fn visit_impl_item(&mut self, _: &'tcx hir::ImplItem) {} -} - -pub fn provide_extern(providers: &mut Providers) { - providers.wasm_import_module_map = |tcx, cnum| { - let mut ret = FxHashMap(); - for lib in tcx.foreign_modules(cnum).iter() { - let attrs = tcx.get_attrs(lib.def_id); - let mut module = None; - for attr in attrs.iter().filter(|a| a.check_name("wasm_import_module")) { - module = attr.value_str(); - } - let module = match module { - Some(s) => s, - None => continue, - }; - for id in lib.foreign_items.iter() { - assert_eq!(id.krate, cnum); - ret.insert(*id, module.to_string()); - } - } - - Lrc::new(ret) - } -} - -fn wasm_import_module(tcx: TyCtxt, id: DefId) -> Option { - tcx.wasm_import_module_map(id.krate) - .get(&id) - .map(|s| CString::new(&s[..]).unwrap()) -} diff --git a/src/librustc_trans/back/archive.rs b/src/librustc_trans/back/archive.rs deleted file mode 100644 index 609629bffb9..00000000000 --- a/src/librustc_trans/back/archive.rs +++ /dev/null @@ -1,325 +0,0 @@ -// Copyright 2013-2014 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! A helper class for dealing with static archives - -use std::ffi::{CString, CStr}; -use std::io; -use std::mem; -use std::path::{Path, PathBuf}; -use std::ptr; -use std::str; - -use back::bytecode::RLIB_BYTECODE_EXTENSION; -use libc; -use llvm::archive_ro::{ArchiveRO, Child}; -use llvm::{self, ArchiveKind}; -use metadata::METADATA_FILENAME; -use rustc::session::Session; - -pub struct ArchiveConfig<'a> { - pub sess: &'a Session, - pub dst: PathBuf, - pub src: Option, - pub lib_search_paths: Vec, -} - -/// Helper for adding many files to an archive. -#[must_use = "must call build() to finish building the archive"] -pub struct ArchiveBuilder<'a> { - config: ArchiveConfig<'a>, - removals: Vec, - additions: Vec, - should_update_symbols: bool, - src_archive: Option>, -} - -enum Addition { - File { - path: PathBuf, - name_in_archive: String, - }, - Archive { - archive: ArchiveRO, - skip: Box bool>, - }, -} - -pub fn find_library(name: &str, search_paths: &[PathBuf], sess: &Session) - -> PathBuf { - // On Windows, static libraries sometimes show up as libfoo.a and other - // times show up as foo.lib - let oslibname = format!("{}{}{}", - sess.target.target.options.staticlib_prefix, - name, - sess.target.target.options.staticlib_suffix); - let unixlibname = format!("lib{}.a", name); - - for path in search_paths { - debug!("looking for {} inside {:?}", name, path); - let test = path.join(&oslibname); - if test.exists() { return test } - if oslibname != unixlibname { - let test = path.join(&unixlibname); - if test.exists() { return test } - } - } - sess.fatal(&format!("could not find native static library `{}`, \ - perhaps an -L flag is missing?", name)); -} - -fn is_relevant_child(c: &Child) -> bool { - match c.name() { - Some(name) => !name.contains("SYMDEF"), - None => false, - } -} - -impl<'a> ArchiveBuilder<'a> { - /// Create a new static archive, ready for modifying the archive specified - /// by `config`. - pub fn new(config: ArchiveConfig<'a>) -> ArchiveBuilder<'a> { - ArchiveBuilder { - config, - removals: Vec::new(), - additions: Vec::new(), - should_update_symbols: false, - src_archive: None, - } - } - - /// Removes a file from this archive - pub fn remove_file(&mut self, file: &str) { - self.removals.push(file.to_string()); - } - - /// Lists all files in an archive - pub fn src_files(&mut self) -> Vec { - if self.src_archive().is_none() { - return Vec::new() - } - let archive = self.src_archive.as_ref().unwrap().as_ref().unwrap(); - let ret = archive.iter() - .filter_map(|child| child.ok()) - .filter(is_relevant_child) - .filter_map(|child| child.name()) - .filter(|name| !self.removals.iter().any(|x| x == name)) - .map(|name| name.to_string()) - .collect(); - return ret; - } - - fn src_archive(&mut self) -> Option<&ArchiveRO> { - if let Some(ref a) = self.src_archive { - return a.as_ref() - } - let src = self.config.src.as_ref()?; - self.src_archive = Some(ArchiveRO::open(src).ok()); - self.src_archive.as_ref().unwrap().as_ref() - } - - /// Adds all of the contents of a native library to this archive. This will - /// search in the relevant locations for a library named `name`. - pub fn add_native_library(&mut self, name: &str) { - let location = find_library(name, &self.config.lib_search_paths, - self.config.sess); - self.add_archive(&location, |_| false).unwrap_or_else(|e| { - self.config.sess.fatal(&format!("failed to add native library {}: {}", - location.to_string_lossy(), e)); - }); - } - - /// Adds all of the contents of the rlib at the specified path to this - /// archive. - /// - /// This ignores adding the bytecode from the rlib, and if LTO is enabled - /// then the object file also isn't added. - pub fn add_rlib(&mut self, - rlib: &Path, - name: &str, - lto: bool, - skip_objects: bool) -> io::Result<()> { - // Ignoring obj file starting with the crate name - // as simple comparison is not enough - there - // might be also an extra name suffix - let obj_start = format!("{}", name); - - self.add_archive(rlib, move |fname: &str| { - // Ignore bytecode/metadata files, no matter the name. - if fname.ends_with(RLIB_BYTECODE_EXTENSION) || fname == METADATA_FILENAME { - return true - } - - // Don't include Rust objects if LTO is enabled - if lto && fname.starts_with(&obj_start) && fname.ends_with(".o") { - return true - } - - // Otherwise if this is *not* a rust object and we're skipping - // objects then skip this file - if skip_objects && (!fname.starts_with(&obj_start) || !fname.ends_with(".o")) { - return true - } - - // ok, don't skip this - return false - }) - } - - fn add_archive(&mut self, archive: &Path, skip: F) - -> io::Result<()> - where F: FnMut(&str) -> bool + 'static - { - let archive = match ArchiveRO::open(archive) { - Ok(ar) => ar, - Err(e) => return Err(io::Error::new(io::ErrorKind::Other, e)), - }; - self.additions.push(Addition::Archive { - archive, - skip: Box::new(skip), - }); - Ok(()) - } - - /// Adds an arbitrary file to this archive - pub fn add_file(&mut self, file: &Path) { - let name = file.file_name().unwrap().to_str().unwrap(); - self.additions.push(Addition::File { - path: file.to_path_buf(), - name_in_archive: name.to_string(), - }); - } - - /// Indicate that the next call to `build` should update all symbols in - /// the archive (equivalent to running 'ar s' over it). - pub fn update_symbols(&mut self) { - self.should_update_symbols = true; - } - - /// Combine the provided files, rlibs, and native libraries into a single - /// `Archive`. - pub fn build(&mut self) { - let kind = match self.llvm_archive_kind() { - Ok(kind) => kind, - Err(kind) => { - self.config.sess.fatal(&format!("Don't know how to build archive of type: {}", - kind)); - } - }; - - if let Err(e) = self.build_with_llvm(kind) { - self.config.sess.fatal(&format!("failed to build archive: {}", e)); - } - - } - - fn llvm_archive_kind(&self) -> Result { - let kind = &*self.config.sess.target.target.options.archive_format; - kind.parse().map_err(|_| kind) - } - - fn build_with_llvm(&mut self, kind: ArchiveKind) -> io::Result<()> { - let mut archives = Vec::new(); - let mut strings = Vec::new(); - let mut members = Vec::new(); - let removals = mem::replace(&mut self.removals, Vec::new()); - - unsafe { - if let Some(archive) = self.src_archive() { - for child in archive.iter() { - let child = child.map_err(string_to_io_error)?; - let child_name = match child.name() { - Some(s) => s, - None => continue, - }; - if removals.iter().any(|r| r == child_name) { - continue - } - - let name = CString::new(child_name)?; - members.push(llvm::LLVMRustArchiveMemberNew(ptr::null(), - name.as_ptr(), - child.raw())); - strings.push(name); - } - } - for addition in mem::replace(&mut self.additions, Vec::new()) { - match addition { - Addition::File { path, name_in_archive } => { - let path = CString::new(path.to_str().unwrap())?; - let name = CString::new(name_in_archive)?; - members.push(llvm::LLVMRustArchiveMemberNew(path.as_ptr(), - name.as_ptr(), - ptr::null_mut())); - strings.push(path); - strings.push(name); - } - Addition::Archive { archive, mut skip } => { - for child in archive.iter() { - let child = child.map_err(string_to_io_error)?; - if !is_relevant_child(&child) { - continue - } - let child_name = child.name().unwrap(); - if skip(child_name) { - continue - } - - // It appears that LLVM's archive writer is a little - // buggy if the name we pass down isn't just the - // filename component, so chop that off here and - // pass it in. - // - // See LLVM bug 25877 for more info. - let child_name = Path::new(child_name) - .file_name().unwrap() - .to_str().unwrap(); - let name = CString::new(child_name)?; - let m = llvm::LLVMRustArchiveMemberNew(ptr::null(), - name.as_ptr(), - child.raw()); - members.push(m); - strings.push(name); - } - archives.push(archive); - } - } - } - - let dst = self.config.dst.to_str().unwrap().as_bytes(); - let dst = CString::new(dst)?; - let r = llvm::LLVMRustWriteArchive(dst.as_ptr(), - members.len() as libc::size_t, - members.as_ptr(), - self.should_update_symbols, - kind); - let ret = if r.into_result().is_err() { - let err = llvm::LLVMRustGetLastError(); - let msg = if err.is_null() { - "failed to write archive".to_string() - } else { - String::from_utf8_lossy(CStr::from_ptr(err).to_bytes()) - .into_owned() - }; - Err(io::Error::new(io::ErrorKind::Other, msg)) - } else { - Ok(()) - }; - for member in members { - llvm::LLVMRustArchiveMemberFree(member); - } - return ret - } - } -} - -fn string_to_io_error(s: String) -> io::Error { - io::Error::new(io::ErrorKind::Other, format!("bad archive: {}", s)) -} diff --git a/src/librustc_trans/back/bytecode.rs b/src/librustc_trans/back/bytecode.rs deleted file mode 100644 index 212d1aaf055..00000000000 --- a/src/librustc_trans/back/bytecode.rs +++ /dev/null @@ -1,160 +0,0 @@ -// Copyright 2017 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! Management of the encoding of LLVM bytecode into rlibs -//! -//! This module contains the management of encoding LLVM bytecode into rlibs, -//! primarily for the usage in LTO situations. Currently the compiler will -//! unconditionally encode LLVM-IR into rlibs regardless of what's happening -//! elsewhere, so we currently compress the bytecode via deflate to avoid taking -//! up too much space on disk. -//! -//! After compressing the bytecode we then have the rest of the format to -//! basically deal with various bugs in various archive implementations. The -//! format currently is: -//! -//! RLIB LLVM-BYTECODE OBJECT LAYOUT -//! Version 2 -//! Bytes Data -//! 0..10 "RUST_OBJECT" encoded in ASCII -//! 11..14 format version as little-endian u32 -//! 15..19 the length of the module identifier string -//! 20..n the module identifier string -//! n..n+8 size in bytes of deflate compressed LLVM bitcode as -//! little-endian u64 -//! n+9.. compressed LLVM bitcode -//! ? maybe a byte to make this whole thing even length - -use std::io::{Read, Write}; -use std::ptr; -use std::str; - -use flate2::Compression; -use flate2::read::DeflateDecoder; -use flate2::write::DeflateEncoder; - -// This is the "magic number" expected at the beginning of a LLVM bytecode -// object in an rlib. -pub const RLIB_BYTECODE_OBJECT_MAGIC: &'static [u8] = b"RUST_OBJECT"; - -// The version number this compiler will write to bytecode objects in rlibs -pub const RLIB_BYTECODE_OBJECT_VERSION: u8 = 2; - -pub const RLIB_BYTECODE_EXTENSION: &str = "bc.z"; - -pub fn encode(identifier: &str, bytecode: &[u8]) -> Vec { - let mut encoded = Vec::new(); - - // Start off with the magic string - encoded.extend_from_slice(RLIB_BYTECODE_OBJECT_MAGIC); - - // Next up is the version - encoded.extend_from_slice(&[RLIB_BYTECODE_OBJECT_VERSION, 0, 0, 0]); - - // Next is the LLVM module identifier length + contents - let identifier_len = identifier.len(); - encoded.extend_from_slice(&[ - (identifier_len >> 0) as u8, - (identifier_len >> 8) as u8, - (identifier_len >> 16) as u8, - (identifier_len >> 24) as u8, - ]); - encoded.extend_from_slice(identifier.as_bytes()); - - // Next is the LLVM module deflate compressed, prefixed with its length. We - // don't know its length yet, so fill in 0s - let deflated_size_pos = encoded.len(); - encoded.extend_from_slice(&[0, 0, 0, 0, 0, 0, 0, 0]); - - let before = encoded.len(); - DeflateEncoder::new(&mut encoded, Compression::fast()) - .write_all(bytecode) - .unwrap(); - let after = encoded.len(); - - // Fill in the length we reserved space for before - let bytecode_len = (after - before) as u64; - encoded[deflated_size_pos + 0] = (bytecode_len >> 0) as u8; - encoded[deflated_size_pos + 1] = (bytecode_len >> 8) as u8; - encoded[deflated_size_pos + 2] = (bytecode_len >> 16) as u8; - encoded[deflated_size_pos + 3] = (bytecode_len >> 24) as u8; - encoded[deflated_size_pos + 4] = (bytecode_len >> 32) as u8; - encoded[deflated_size_pos + 5] = (bytecode_len >> 40) as u8; - encoded[deflated_size_pos + 6] = (bytecode_len >> 48) as u8; - encoded[deflated_size_pos + 7] = (bytecode_len >> 56) as u8; - - // If the number of bytes written to the object so far is odd, add a - // padding byte to make it even. This works around a crash bug in LLDB - // (see issue #15950) - if encoded.len() % 2 == 1 { - encoded.push(0); - } - - return encoded -} - -pub struct DecodedBytecode<'a> { - identifier: &'a str, - encoded_bytecode: &'a [u8], -} - -impl<'a> DecodedBytecode<'a> { - pub fn new(data: &'a [u8]) -> Result, String> { - if !data.starts_with(RLIB_BYTECODE_OBJECT_MAGIC) { - return Err(format!("magic bytecode prefix not found")) - } - let data = &data[RLIB_BYTECODE_OBJECT_MAGIC.len()..]; - if !data.starts_with(&[RLIB_BYTECODE_OBJECT_VERSION, 0, 0, 0]) { - return Err(format!("wrong version prefix found in bytecode")) - } - let data = &data[4..]; - if data.len() < 4 { - return Err(format!("bytecode corrupted")) - } - let identifier_len = unsafe { - u32::from_le(ptr::read_unaligned(data.as_ptr() as *const u32)) as usize - }; - let data = &data[4..]; - if data.len() < identifier_len { - return Err(format!("bytecode corrupted")) - } - let identifier = match str::from_utf8(&data[..identifier_len]) { - Ok(s) => s, - Err(_) => return Err(format!("bytecode corrupted")) - }; - let data = &data[identifier_len..]; - if data.len() < 8 { - return Err(format!("bytecode corrupted")) - } - let bytecode_len = unsafe { - u64::from_le(ptr::read_unaligned(data.as_ptr() as *const u64)) as usize - }; - let data = &data[8..]; - if data.len() < bytecode_len { - return Err(format!("bytecode corrupted")) - } - let encoded_bytecode = &data[..bytecode_len]; - - Ok(DecodedBytecode { - identifier, - encoded_bytecode, - }) - } - - pub fn bytecode(&self) -> Vec { - let mut data = Vec::new(); - DeflateDecoder::new(self.encoded_bytecode).read_to_end(&mut data).unwrap(); - return data - } - - pub fn identifier(&self) -> &'a str { - self.identifier - } -} diff --git a/src/librustc_trans/back/command.rs b/src/librustc_trans/back/command.rs deleted file mode 100644 index 9ebbdd7c3c9..00000000000 --- a/src/librustc_trans/back/command.rs +++ /dev/null @@ -1,175 +0,0 @@ -// Copyright 2017 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! A thin wrapper around `Command` in the standard library which allows us to -//! read the arguments that are built up. - -use std::ffi::{OsStr, OsString}; -use std::fmt; -use std::io; -use std::mem; -use std::process::{self, Output}; - -use rustc_target::spec::LldFlavor; - -#[derive(Clone)] -pub struct Command { - program: Program, - args: Vec, - env: Vec<(OsString, OsString)>, -} - -#[derive(Clone)] -enum Program { - Normal(OsString), - CmdBatScript(OsString), - Lld(OsString, LldFlavor) -} - -impl Command { - pub fn new>(program: P) -> Command { - Command::_new(Program::Normal(program.as_ref().to_owned())) - } - - pub fn bat_script>(program: P) -> Command { - Command::_new(Program::CmdBatScript(program.as_ref().to_owned())) - } - - pub fn lld>(program: P, flavor: LldFlavor) -> Command { - Command::_new(Program::Lld(program.as_ref().to_owned(), flavor)) - } - - fn _new(program: Program) -> Command { - Command { - program, - args: Vec::new(), - env: Vec::new(), - } - } - - pub fn arg>(&mut self, arg: P) -> &mut Command { - self._arg(arg.as_ref()); - self - } - - pub fn args(&mut self, args: I) -> &mut Command - where I: IntoIterator, - I::Item: AsRef, - { - for arg in args { - self._arg(arg.as_ref()); - } - self - } - - fn _arg(&mut self, arg: &OsStr) { - self.args.push(arg.to_owned()); - } - - pub fn env(&mut self, key: K, value: V) -> &mut Command - where K: AsRef, - V: AsRef - { - self._env(key.as_ref(), value.as_ref()); - self - } - - fn _env(&mut self, key: &OsStr, value: &OsStr) { - self.env.push((key.to_owned(), value.to_owned())); - } - - pub fn output(&mut self) -> io::Result { - self.command().output() - } - - pub fn command(&self) -> process::Command { - let mut ret = match self.program { - Program::Normal(ref p) => process::Command::new(p), - Program::CmdBatScript(ref p) => { - let mut c = process::Command::new("cmd"); - c.arg("/c").arg(p); - c - } - Program::Lld(ref p, flavor) => { - let mut c = process::Command::new(p); - c.arg("-flavor").arg(match flavor { - LldFlavor::Wasm => "wasm", - LldFlavor::Ld => "gnu", - LldFlavor::Link => "link", - LldFlavor::Ld64 => "darwin", - }); - c - } - }; - ret.args(&self.args); - ret.envs(self.env.clone()); - return ret - } - - // extensions - - pub fn get_args(&self) -> &[OsString] { - &self.args - } - - pub fn take_args(&mut self) -> Vec { - mem::replace(&mut self.args, Vec::new()) - } - - /// Returns a `true` if we're pretty sure that this'll blow OS spawn limits, - /// or `false` if we should attempt to spawn and see what the OS says. - pub fn very_likely_to_exceed_some_spawn_limit(&self) -> bool { - // We mostly only care about Windows in this method, on Unix the limits - // can be gargantuan anyway so we're pretty unlikely to hit them - if cfg!(unix) { - return false - } - - // Right now LLD doesn't support the `@` syntax of passing an argument - // through files, so regardless of the platform we try to go to the OS - // on this one. - if let Program::Lld(..) = self.program { - return false - } - - // Ok so on Windows to spawn a process is 32,768 characters in its - // command line [1]. Unfortunately we don't actually have access to that - // as it's calculated just before spawning. Instead we perform a - // poor-man's guess as to how long our command line will be. We're - // assuming here that we don't have to escape every character... - // - // Turns out though that `cmd.exe` has even smaller limits, 8192 - // characters [2]. Linkers can often be batch scripts (for example - // Emscripten, Gecko's current build system) which means that we're - // running through batch scripts. These linkers often just forward - // arguments elsewhere (and maybe tack on more), so if we blow 8192 - // bytes we'll typically cause them to blow as well. - // - // Basically as a result just perform an inflated estimate of what our - // command line will look like and test if it's > 8192 (we actually - // test against 6k to artificially inflate our estimate). If all else - // fails we'll fall back to the normal unix logic of testing the OS - // error code if we fail to spawn and automatically re-spawning the - // linker with smaller arguments. - // - // [1]: https://msdn.microsoft.com/en-us/library/windows/desktop/ms682425(v=vs.85).aspx - // [2]: https://blogs.msdn.microsoft.com/oldnewthing/20031210-00/?p=41553 - - let estimated_command_line_len = - self.args.iter().map(|a| a.len()).sum::(); - estimated_command_line_len > 1024 * 6 - } -} - -impl fmt::Debug for Command { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - self.command().fmt(f) - } -} diff --git a/src/librustc_trans/back/link.rs b/src/librustc_trans/back/link.rs deleted file mode 100644 index 299075ed03a..00000000000 --- a/src/librustc_trans/back/link.rs +++ /dev/null @@ -1,1626 +0,0 @@ -// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -use back::wasm; -use cc::windows_registry; -use super::archive::{ArchiveBuilder, ArchiveConfig}; -use super::bytecode::RLIB_BYTECODE_EXTENSION; -use super::linker::Linker; -use super::command::Command; -use super::rpath::RPathConfig; -use super::rpath; -use metadata::METADATA_FILENAME; -use rustc::session::config::{self, NoDebugInfo, OutputFilenames, OutputType, PrintRequest}; -use rustc::session::config::{RUST_CGU_EXT, Lto}; -use rustc::session::filesearch; -use rustc::session::search_paths::PathKind; -use rustc::session::Session; -use rustc::middle::cstore::{NativeLibrary, LibSource, NativeLibraryKind}; -use rustc::middle::dependency_format::Linkage; -use {CrateTranslation, CrateInfo}; -use rustc::util::common::time; -use rustc::util::fs::fix_windows_verbatim_for_gcc; -use rustc::hir::def_id::CrateNum; -use tempdir::TempDir; -use rustc_target::spec::{PanicStrategy, RelroLevel, LinkerFlavor, TargetTriple}; -use rustc_data_structures::fx::FxHashSet; -use context::get_reloc_model; -use llvm; - -use std::ascii; -use std::char; -use std::env; -use std::fmt; -use std::fs; -use std::io; -use std::path::{Path, PathBuf}; -use std::process::{Output, Stdio}; -use std::str; -use syntax::attr; - -/// The LLVM module name containing crate-metadata. This includes a `.` on -/// purpose, so it cannot clash with the name of a user-defined module. -pub const METADATA_MODULE_NAME: &'static str = "crate.metadata"; - -// same as for metadata above, but for allocator shim -pub const ALLOCATOR_MODULE_NAME: &'static str = "crate.allocator"; - -pub use rustc_trans_utils::link::{find_crate_name, filename_for_input, default_output_for_target, - invalid_output_for_target, build_link_meta, out_filename, - check_file_is_writeable}; - -// The third parameter is for env vars, used on windows to set up the -// path for MSVC to find its DLLs, and gcc to find its bundled -// toolchain -pub fn get_linker(sess: &Session) -> (PathBuf, Command) { - // If our linker looks like a batch script on Windows then to execute this - // we'll need to spawn `cmd` explicitly. This is primarily done to handle - // emscripten where the linker is `emcc.bat` and needs to be spawned as - // `cmd /c emcc.bat ...`. - // - // This worked historically but is needed manually since #42436 (regression - // was tagged as #42791) and some more info can be found on #44443 for - // emscripten itself. - let cmd = |linker: &Path| { - if let Some(linker) = linker.to_str() { - if cfg!(windows) && linker.ends_with(".bat") { - return Command::bat_script(linker) - } - } - match sess.linker_flavor() { - LinkerFlavor::Lld(f) => Command::lld(linker, f), - _ => Command::new(linker), - - } - }; - - let msvc_tool = windows_registry::find_tool(&sess.opts.target_triple.triple(), "link.exe"); - - let linker_path = sess.opts.cg.linker.as_ref().map(|s| &**s) - .or(sess.target.target.options.linker.as_ref().map(|s| s.as_ref())) - .unwrap_or(match sess.linker_flavor() { - LinkerFlavor::Msvc => { - msvc_tool.as_ref().map(|t| t.path()).unwrap_or("link.exe".as_ref()) - } - LinkerFlavor::Em if cfg!(windows) => "emcc.bat".as_ref(), - LinkerFlavor::Em => "emcc".as_ref(), - LinkerFlavor::Gcc => "cc".as_ref(), - LinkerFlavor::Ld => "ld".as_ref(), - LinkerFlavor::Lld(_) => "lld".as_ref(), - }); - - let mut cmd = cmd(linker_path); - - // The compiler's sysroot often has some bundled tools, so add it to the - // PATH for the child. - let mut new_path = sess.host_filesearch(PathKind::All) - .get_tools_search_paths(); - let mut msvc_changed_path = false; - if sess.target.target.options.is_like_msvc { - if let Some(ref tool) = msvc_tool { - cmd.args(tool.args()); - for &(ref k, ref v) in tool.env() { - if k == "PATH" { - new_path.extend(env::split_paths(v)); - msvc_changed_path = true; - } else { - cmd.env(k, v); - } - } - } - } - - if !msvc_changed_path { - if let Some(path) = env::var_os("PATH") { - new_path.extend(env::split_paths(&path)); - } - } - cmd.env("PATH", env::join_paths(new_path).unwrap()); - - (linker_path.to_path_buf(), cmd) -} - -pub fn remove(sess: &Session, path: &Path) { - match fs::remove_file(path) { - Ok(..) => {} - Err(e) => { - sess.err(&format!("failed to remove {}: {}", - path.display(), - e)); - } - } -} - -/// Perform the linkage portion of the compilation phase. This will generate all -/// of the requested outputs for this compilation session. -pub(crate) fn link_binary(sess: &Session, - trans: &CrateTranslation, - outputs: &OutputFilenames, - crate_name: &str) -> Vec { - let mut out_filenames = Vec::new(); - for &crate_type in sess.crate_types.borrow().iter() { - // Ignore executable crates if we have -Z no-trans, as they will error. - let output_metadata = sess.opts.output_types.contains_key(&OutputType::Metadata); - if (sess.opts.debugging_opts.no_trans || !sess.opts.output_types.should_trans()) && - !output_metadata && - crate_type == config::CrateTypeExecutable { - continue; - } - - if invalid_output_for_target(sess, crate_type) { - bug!("invalid output type `{:?}` for target os `{}`", - crate_type, sess.opts.target_triple); - } - let mut out_files = link_binary_output(sess, - trans, - crate_type, - outputs, - crate_name); - out_filenames.append(&mut out_files); - } - - // Remove the temporary object file and metadata if we aren't saving temps - if !sess.opts.cg.save_temps { - if sess.opts.output_types.should_trans() && - !preserve_objects_for_their_debuginfo(sess) - { - for obj in trans.modules.iter().filter_map(|m| m.object.as_ref()) { - remove(sess, obj); - } - } - for obj in trans.modules.iter().filter_map(|m| m.bytecode_compressed.as_ref()) { - remove(sess, obj); - } - if let Some(ref obj) = trans.metadata_module.object { - remove(sess, obj); - } - if let Some(ref allocator) = trans.allocator_module { - if let Some(ref obj) = allocator.object { - remove(sess, obj); - } - if let Some(ref bc) = allocator.bytecode_compressed { - remove(sess, bc); - } - } - } - - out_filenames -} - -/// Returns a boolean indicating whether we should preserve the object files on -/// the filesystem for their debug information. This is often useful with -/// split-dwarf like schemes. -fn preserve_objects_for_their_debuginfo(sess: &Session) -> bool { - // If the objects don't have debuginfo there's nothing to preserve. - if sess.opts.debuginfo == NoDebugInfo { - return false - } - - // If we're only producing artifacts that are archives, no need to preserve - // the objects as they're losslessly contained inside the archives. - let output_linked = sess.crate_types.borrow() - .iter() - .any(|x| *x != config::CrateTypeRlib && *x != config::CrateTypeStaticlib); - if !output_linked { - return false - } - - // If we're on OSX then the equivalent of split dwarf is turned on by - // default. The final executable won't actually have any debug information - // except it'll have pointers to elsewhere. Historically we've always run - // `dsymutil` to "link all the dwarf together" but this is actually sort of - // a bummer for incremental compilation! (the whole point of split dwarf is - // that you don't do this sort of dwarf link). - // - // Basically as a result this just means that if we're on OSX and we're - // *not* running dsymutil then the object files are the only source of truth - // for debug information, so we must preserve them. - if sess.target.target.options.is_like_osx { - match sess.opts.debugging_opts.run_dsymutil { - // dsymutil is not being run, preserve objects - Some(false) => return true, - - // dsymutil is being run, no need to preserve the objects - Some(true) => return false, - - // The default historical behavior was to always run dsymutil, so - // we're preserving that temporarily, but we're likely to switch the - // default soon. - None => return false, - } - } - - false -} - -fn filename_for_metadata(sess: &Session, crate_name: &str, outputs: &OutputFilenames) -> PathBuf { - let out_filename = outputs.single_output_file.clone() - .unwrap_or(outputs - .out_directory - .join(&format!("lib{}{}.rmeta", crate_name, sess.opts.cg.extra_filename))); - check_file_is_writeable(&out_filename, sess); - out_filename -} - -pub(crate) fn each_linked_rlib(sess: &Session, - info: &CrateInfo, - f: &mut FnMut(CrateNum, &Path)) -> Result<(), String> { - let crates = info.used_crates_static.iter(); - let fmts = sess.dependency_formats.borrow(); - let fmts = fmts.get(&config::CrateTypeExecutable) - .or_else(|| fmts.get(&config::CrateTypeStaticlib)) - .or_else(|| fmts.get(&config::CrateTypeCdylib)) - .or_else(|| fmts.get(&config::CrateTypeProcMacro)); - let fmts = match fmts { - Some(f) => f, - None => return Err(format!("could not find formats for rlibs")) - }; - for &(cnum, ref path) in crates { - match fmts.get(cnum.as_usize() - 1) { - Some(&Linkage::NotLinked) | - Some(&Linkage::IncludedFromDylib) => continue, - Some(_) => {} - None => return Err(format!("could not find formats for rlibs")) - } - let name = &info.crate_name[&cnum]; - let path = match *path { - LibSource::Some(ref p) => p, - LibSource::MetadataOnly => { - return Err(format!("could not find rlib for: `{}`, found rmeta (metadata) file", - name)) - } - LibSource::None => { - return Err(format!("could not find rlib for: `{}`", name)) - } - }; - f(cnum, &path); - } - Ok(()) -} - -/// Returns a boolean indicating whether the specified crate should be ignored -/// during LTO. -/// -/// Crates ignored during LTO are not lumped together in the "massive object -/// file" that we create and are linked in their normal rlib states. See -/// comments below for what crates do not participate in LTO. -/// -/// It's unusual for a crate to not participate in LTO. Typically only -/// compiler-specific and unstable crates have a reason to not participate in -/// LTO. -pub(crate) fn ignored_for_lto(sess: &Session, info: &CrateInfo, cnum: CrateNum) -> bool { - // If our target enables builtin function lowering in LLVM then the - // crates providing these functions don't participate in LTO (e.g. - // no_builtins or compiler builtins crates). - !sess.target.target.options.no_builtins && - (info.is_no_builtins.contains(&cnum) || info.compiler_builtins == Some(cnum)) -} - -fn link_binary_output(sess: &Session, - trans: &CrateTranslation, - crate_type: config::CrateType, - outputs: &OutputFilenames, - crate_name: &str) -> Vec { - for obj in trans.modules.iter().filter_map(|m| m.object.as_ref()) { - check_file_is_writeable(obj, sess); - } - - let mut out_filenames = vec![]; - - if outputs.outputs.contains_key(&OutputType::Metadata) { - let out_filename = filename_for_metadata(sess, crate_name, outputs); - // To avoid races with another rustc process scanning the output directory, - // we need to write the file somewhere else and atomically move it to its - // final destination, with a `fs::rename` call. In order for the rename to - // always succeed, the temporary file needs to be on the same filesystem, - // which is why we create it inside the output directory specifically. - let metadata_tmpdir = match TempDir::new_in(out_filename.parent().unwrap(), "rmeta") { - Ok(tmpdir) => tmpdir, - Err(err) => sess.fatal(&format!("couldn't create a temp dir: {}", err)), - }; - let metadata = emit_metadata(sess, trans, &metadata_tmpdir); - if let Err(e) = fs::rename(metadata, &out_filename) { - sess.fatal(&format!("failed to write {}: {}", out_filename.display(), e)); - } - out_filenames.push(out_filename); - } - - let tmpdir = match TempDir::new("rustc") { - Ok(tmpdir) => tmpdir, - Err(err) => sess.fatal(&format!("couldn't create a temp dir: {}", err)), - }; - - if outputs.outputs.should_trans() { - let out_filename = out_filename(sess, crate_type, outputs, crate_name); - match crate_type { - config::CrateTypeRlib => { - link_rlib(sess, - trans, - RlibFlavor::Normal, - &out_filename, - &tmpdir).build(); - } - config::CrateTypeStaticlib => { - link_staticlib(sess, trans, &out_filename, &tmpdir); - } - _ => { - link_natively(sess, crate_type, &out_filename, trans, tmpdir.path()); - } - } - out_filenames.push(out_filename); - } - - if sess.opts.cg.save_temps { - let _ = tmpdir.into_path(); - } - - out_filenames -} - -fn archive_search_paths(sess: &Session) -> Vec { - let mut search = Vec::new(); - sess.target_filesearch(PathKind::Native).for_each_lib_search_path(|path, _| { - search.push(path.to_path_buf()); - }); - return search; -} - -fn archive_config<'a>(sess: &'a Session, - output: &Path, - input: Option<&Path>) -> ArchiveConfig<'a> { - ArchiveConfig { - sess, - dst: output.to_path_buf(), - src: input.map(|p| p.to_path_buf()), - lib_search_paths: archive_search_paths(sess), - } -} - -/// We use a temp directory here to avoid races between concurrent rustc processes, -/// such as builds in the same directory using the same filename for metadata while -/// building an `.rlib` (stomping over one another), or writing an `.rmeta` into a -/// directory being searched for `extern crate` (observing an incomplete file). -/// The returned path is the temporary file containing the complete metadata. -fn emit_metadata<'a>(sess: &'a Session, trans: &CrateTranslation, tmpdir: &TempDir) - -> PathBuf { - let out_filename = tmpdir.path().join(METADATA_FILENAME); - let result = fs::write(&out_filename, &trans.metadata.raw_data); - - if let Err(e) = result { - sess.fatal(&format!("failed to write {}: {}", out_filename.display(), e)); - } - - out_filename -} - -enum RlibFlavor { - Normal, - StaticlibBase, -} - -// Create an 'rlib' -// -// An rlib in its current incarnation is essentially a renamed .a file. The -// rlib primarily contains the object file of the crate, but it also contains -// all of the object files from native libraries. This is done by unzipping -// native libraries and inserting all of the contents into this archive. -fn link_rlib<'a>(sess: &'a Session, - trans: &CrateTranslation, - flavor: RlibFlavor, - out_filename: &Path, - tmpdir: &TempDir) -> ArchiveBuilder<'a> { - info!("preparing rlib to {:?}", out_filename); - let mut ab = ArchiveBuilder::new(archive_config(sess, out_filename, None)); - - for obj in trans.modules.iter().filter_map(|m| m.object.as_ref()) { - ab.add_file(obj); - } - - // Note that in this loop we are ignoring the value of `lib.cfg`. That is, - // we may not be configured to actually include a static library if we're - // adding it here. That's because later when we consume this rlib we'll - // decide whether we actually needed the static library or not. - // - // To do this "correctly" we'd need to keep track of which libraries added - // which object files to the archive. We don't do that here, however. The - // #[link(cfg(..))] feature is unstable, though, and only intended to get - // liblibc working. In that sense the check below just indicates that if - // there are any libraries we want to omit object files for at link time we - // just exclude all custom object files. - // - // Eventually if we want to stabilize or flesh out the #[link(cfg(..))] - // feature then we'll need to figure out how to record what objects were - // loaded from the libraries found here and then encode that into the - // metadata of the rlib we're generating somehow. - for lib in trans.crate_info.used_libraries.iter() { - match lib.kind { - NativeLibraryKind::NativeStatic => {} - NativeLibraryKind::NativeStaticNobundle | - NativeLibraryKind::NativeFramework | - NativeLibraryKind::NativeUnknown => continue, - } - ab.add_native_library(&lib.name.as_str()); - } - - // After adding all files to the archive, we need to update the - // symbol table of the archive. - ab.update_symbols(); - - // Note that it is important that we add all of our non-object "magical - // files" *after* all of the object files in the archive. The reason for - // this is as follows: - // - // * When performing LTO, this archive will be modified to remove - // objects from above. The reason for this is described below. - // - // * When the system linker looks at an archive, it will attempt to - // determine the architecture of the archive in order to see whether its - // linkable. - // - // The algorithm for this detection is: iterate over the files in the - // archive. Skip magical SYMDEF names. Interpret the first file as an - // object file. Read architecture from the object file. - // - // * As one can probably see, if "metadata" and "foo.bc" were placed - // before all of the objects, then the architecture of this archive would - // not be correctly inferred once 'foo.o' is removed. - // - // Basically, all this means is that this code should not move above the - // code above. - match flavor { - RlibFlavor::Normal => { - // Instead of putting the metadata in an object file section, rlibs - // contain the metadata in a separate file. - ab.add_file(&emit_metadata(sess, trans, tmpdir)); - - // For LTO purposes, the bytecode of this library is also inserted - // into the archive. - for bytecode in trans.modules.iter().filter_map(|m| m.bytecode_compressed.as_ref()) { - ab.add_file(bytecode); - } - - // After adding all files to the archive, we need to update the - // symbol table of the archive. This currently dies on macOS (see - // #11162), and isn't necessary there anyway - if !sess.target.target.options.is_like_osx { - ab.update_symbols(); - } - } - - RlibFlavor::StaticlibBase => { - let obj = trans.allocator_module - .as_ref() - .and_then(|m| m.object.as_ref()); - if let Some(obj) = obj { - ab.add_file(obj); - } - } - } - - ab -} - -// Create a static archive -// -// This is essentially the same thing as an rlib, but it also involves adding -// all of the upstream crates' objects into the archive. This will slurp in -// all of the native libraries of upstream dependencies as well. -// -// Additionally, there's no way for us to link dynamic libraries, so we warn -// about all dynamic library dependencies that they're not linked in. -// -// There's no need to include metadata in a static archive, so ensure to not -// link in the metadata object file (and also don't prepare the archive with a -// metadata file). -fn link_staticlib(sess: &Session, - trans: &CrateTranslation, - out_filename: &Path, - tempdir: &TempDir) { - let mut ab = link_rlib(sess, - trans, - RlibFlavor::StaticlibBase, - out_filename, - tempdir); - let mut all_native_libs = vec![]; - - let res = each_linked_rlib(sess, &trans.crate_info, &mut |cnum, path| { - let name = &trans.crate_info.crate_name[&cnum]; - let native_libs = &trans.crate_info.native_libraries[&cnum]; - - // Here when we include the rlib into our staticlib we need to make a - // decision whether to include the extra object files along the way. - // These extra object files come from statically included native - // libraries, but they may be cfg'd away with #[link(cfg(..))]. - // - // This unstable feature, though, only needs liblibc to work. The only - // use case there is where musl is statically included in liblibc.rlib, - // so if we don't want the included version we just need to skip it. As - // a result the logic here is that if *any* linked library is cfg'd away - // we just skip all object files. - // - // Clearly this is not sufficient for a general purpose feature, and - // we'd want to read from the library's metadata to determine which - // object files come from where and selectively skip them. - let skip_object_files = native_libs.iter().any(|lib| { - lib.kind == NativeLibraryKind::NativeStatic && !relevant_lib(sess, lib) - }); - ab.add_rlib(path, - &name.as_str(), - is_full_lto_enabled(sess) && - !ignored_for_lto(sess, &trans.crate_info, cnum), - skip_object_files).unwrap(); - - all_native_libs.extend(trans.crate_info.native_libraries[&cnum].iter().cloned()); - }); - if let Err(e) = res { - sess.fatal(&e); - } - - ab.update_symbols(); - ab.build(); - - if !all_native_libs.is_empty() { - if sess.opts.prints.contains(&PrintRequest::NativeStaticLibs) { - print_native_static_libs(sess, &all_native_libs); - } - } -} - -fn print_native_static_libs(sess: &Session, all_native_libs: &[NativeLibrary]) { - let lib_args: Vec<_> = all_native_libs.iter() - .filter(|l| relevant_lib(sess, l)) - .filter_map(|lib| match lib.kind { - NativeLibraryKind::NativeStaticNobundle | - NativeLibraryKind::NativeUnknown => { - if sess.target.target.options.is_like_msvc { - Some(format!("{}.lib", lib.name)) - } else { - Some(format!("-l{}", lib.name)) - } - }, - NativeLibraryKind::NativeFramework => { - // ld-only syntax, since there are no frameworks in MSVC - Some(format!("-framework {}", lib.name)) - }, - // These are included, no need to print them - NativeLibraryKind::NativeStatic => None, - }) - .collect(); - if !lib_args.is_empty() { - sess.note_without_error("Link against the following native artifacts when linking \ - against this static library. The order and any duplication \ - can be significant on some platforms."); - // Prefix for greppability - sess.note_without_error(&format!("native-static-libs: {}", &lib_args.join(" "))); - } -} - -// Create a dynamic library or executable -// -// 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, - crate_type: config::CrateType, - out_filename: &Path, - trans: &CrateTranslation, - tmpdir: &Path) { - info!("preparing {:?} to {:?}", crate_type, out_filename); - let flavor = sess.linker_flavor(); - - // The invocations of cc share some flags across platforms - let (pname, mut cmd) = get_linker(sess); - - let root = sess.target_filesearch(PathKind::Native).get_lib_path(); - if let Some(args) = sess.target.target.options.pre_link_args.get(&flavor) { - cmd.args(args); - } - if let Some(args) = sess.target.target.options.pre_link_args_crt.get(&flavor) { - if sess.crt_static() { - cmd.args(args); - } - } - if let Some(ref args) = sess.opts.debugging_opts.pre_link_args { - cmd.args(args); - } - cmd.args(&sess.opts.debugging_opts.pre_link_arg); - - let pre_link_objects = if crate_type == config::CrateTypeExecutable { - &sess.target.target.options.pre_link_objects_exe - } else { - &sess.target.target.options.pre_link_objects_dll - }; - for obj in pre_link_objects { - cmd.arg(root.join(obj)); - } - - if crate_type == config::CrateTypeExecutable && sess.crt_static() { - for obj in &sess.target.target.options.pre_link_objects_exe_crt { - cmd.arg(root.join(obj)); - } - - for obj in &sess.target.target.options.pre_link_objects_exe_crt_sys { - if flavor == LinkerFlavor::Gcc { - cmd.arg(format!("-l:{}", obj)); - } - } - } - - if sess.target.target.options.is_like_emscripten { - cmd.arg("-s"); - cmd.arg(if sess.panic_strategy() == PanicStrategy::Abort { - "DISABLE_EXCEPTION_CATCHING=1" - } else { - "DISABLE_EXCEPTION_CATCHING=0" - }); - } - - { - let mut linker = trans.linker_info.to_linker(cmd, &sess); - link_args(&mut *linker, sess, crate_type, tmpdir, - out_filename, trans); - cmd = linker.finalize(); - } - if let Some(args) = sess.target.target.options.late_link_args.get(&flavor) { - cmd.args(args); - } - for obj in &sess.target.target.options.post_link_objects { - cmd.arg(root.join(obj)); - } - if sess.crt_static() { - for obj in &sess.target.target.options.post_link_objects_crt_sys { - if flavor == LinkerFlavor::Gcc { - cmd.arg(format!("-l:{}", obj)); - } - } - for obj in &sess.target.target.options.post_link_objects_crt { - cmd.arg(root.join(obj)); - } - } - if let Some(args) = sess.target.target.options.post_link_args.get(&flavor) { - cmd.args(args); - } - for &(ref k, ref v) in &sess.target.target.options.link_env { - cmd.env(k, v); - } - - if sess.opts.debugging_opts.print_link_args { - println!("{:?}", &cmd); - } - - // May have not found libraries in the right formats. - sess.abort_if_errors(); - - // Invoke the system linker - // - // Note that there's a terribly awful hack that really shouldn't be present - // in any compiler. Here an environment variable is supported to - // automatically retry the linker invocation if the linker looks like it - // segfaulted. - // - // Gee that seems odd, normally segfaults are things we want to know about! - // Unfortunately though in rust-lang/rust#38878 we're experiencing the - // linker segfaulting on Travis quite a bit which is causing quite a bit of - // pain to land PRs when they spuriously fail due to a segfault. - // - // The issue #38878 has some more debugging information on it as well, but - // this unfortunately looks like it's just a race condition in macOS's linker - // with some thread pool working in the background. It seems that no one - // currently knows a fix for this so in the meantime we're left with this... - info!("{:?}", &cmd); - let retry_on_segfault = env::var("RUSTC_RETRY_LINKER_ON_SEGFAULT").is_ok(); - let mut prog; - let mut i = 0; - loop { - i += 1; - prog = time(sess, "running linker", || { - exec_linker(sess, &mut cmd, out_filename, tmpdir) - }); - let output = match prog { - Ok(ref output) => output, - Err(_) => break, - }; - if output.status.success() { - break - } - let mut out = output.stderr.clone(); - out.extend(&output.stdout); - let out = String::from_utf8_lossy(&out); - - // Check to see if the link failed with "unrecognized command line option: - // '-no-pie'" for gcc or "unknown argument: '-no-pie'" for clang. If so, - // reperform the link step without the -no-pie option. This is safe because - // if the linker doesn't support -no-pie then it should not default to - // linking executables as pie. Different versions of gcc seem to use - // different quotes in the error message so don't check for them. - if sess.target.target.options.linker_is_gnu && - sess.linker_flavor() != LinkerFlavor::Ld && - (out.contains("unrecognized command line option") || - out.contains("unknown argument")) && - out.contains("-no-pie") && - cmd.get_args().iter().any(|e| e.to_string_lossy() == "-no-pie") { - info!("linker output: {:?}", out); - warn!("Linker does not support -no-pie command line option. Retrying without."); - for arg in cmd.take_args() { - if arg.to_string_lossy() != "-no-pie" { - cmd.arg(arg); - } - } - info!("{:?}", &cmd); - continue; - } - if !retry_on_segfault || i > 3 { - break - } - let msg_segv = "clang: error: unable to execute command: Segmentation fault: 11"; - let msg_bus = "clang: error: unable to execute command: Bus error: 10"; - if !(out.contains(msg_segv) || out.contains(msg_bus)) { - break - } - - warn!( - "looks like the linker segfaulted when we tried to call it, \ - automatically retrying again. cmd = {:?}, out = {}.", - cmd, - out, - ); - } - - match prog { - Ok(prog) => { - fn escape_string(s: &[u8]) -> String { - str::from_utf8(s).map(|s| s.to_owned()) - .unwrap_or_else(|_| { - let mut x = "Non-UTF-8 output: ".to_string(); - x.extend(s.iter() - .flat_map(|&b| ascii::escape_default(b)) - .map(|b| char::from_u32(b as u32).unwrap())); - x - }) - } - if !prog.status.success() { - let mut output = prog.stderr.clone(); - output.extend_from_slice(&prog.stdout); - sess.struct_err(&format!("linking with `{}` failed: {}", - pname.display(), - prog.status)) - .note(&format!("{:?}", &cmd)) - .note(&escape_string(&output)) - .emit(); - sess.abort_if_errors(); - } - info!("linker stderr:\n{}", escape_string(&prog.stderr)); - info!("linker stdout:\n{}", escape_string(&prog.stdout)); - }, - Err(e) => { - let linker_not_found = e.kind() == io::ErrorKind::NotFound; - - let mut linker_error = { - if linker_not_found { - sess.struct_err(&format!("linker `{}` not found", pname.display())) - } else { - sess.struct_err(&format!("could not exec the linker `{}`", pname.display())) - } - }; - - linker_error.note(&format!("{}", e)); - - if !linker_not_found { - linker_error.note(&format!("{:?}", &cmd)); - } - - linker_error.emit(); - - if sess.target.target.options.is_like_msvc && linker_not_found { - sess.note_without_error("the msvc targets depend on the msvc linker \ - but `link.exe` was not found"); - sess.note_without_error("please ensure that VS 2013 or VS 2015 was installed \ - with the Visual C++ option"); - } - sess.abort_if_errors(); - } - } - - - // On macOS, debuggers need this utility to get run to do some munging of - // the symbols. Note, though, that if the object files are being preserved - // for their debug information there's no need for us to run dsymutil. - if sess.target.target.options.is_like_osx && - sess.opts.debuginfo != NoDebugInfo && - !preserve_objects_for_their_debuginfo(sess) - { - match Command::new("dsymutil").arg(out_filename).output() { - Ok(..) => {} - Err(e) => sess.fatal(&format!("failed to run dsymutil: {}", e)), - } - } - - if sess.opts.target_triple == TargetTriple::from_triple("wasm32-unknown-unknown") { - wasm::rewrite_imports(&out_filename, &trans.crate_info.wasm_imports); - wasm::add_custom_sections(&out_filename, - &trans.crate_info.wasm_custom_sections); - } -} - -fn exec_linker(sess: &Session, cmd: &mut Command, out_filename: &Path, tmpdir: &Path) - -> io::Result -{ - // When attempting to spawn the linker we run a risk of blowing out the - // size limits for spawning a new process with respect to the arguments - // we pass on the command line. - // - // Here we attempt to handle errors from the OS saying "your list of - // arguments is too big" by reinvoking the linker again with an `@`-file - // that contains all the arguments. The theory is that this is then - // accepted on all linkers and the linker will read all its options out of - // there instead of looking at the command line. - if !cmd.very_likely_to_exceed_some_spawn_limit() { - match cmd.command().stdout(Stdio::piped()).stderr(Stdio::piped()).spawn() { - Ok(child) => { - let output = child.wait_with_output(); - flush_linked_file(&output, out_filename)?; - return output; - } - Err(ref e) if command_line_too_big(e) => { - info!("command line to linker was too big: {}", e); - } - Err(e) => return Err(e) - } - } - - info!("falling back to passing arguments to linker via an @-file"); - let mut cmd2 = cmd.clone(); - let mut args = String::new(); - for arg in cmd2.take_args() { - args.push_str(&Escape { - arg: arg.to_str().unwrap(), - is_like_msvc: sess.target.target.options.is_like_msvc, - }.to_string()); - args.push_str("\n"); - } - let file = tmpdir.join("linker-arguments"); - let bytes = if sess.target.target.options.is_like_msvc { - let mut out = vec![]; - // start the stream with a UTF-16 BOM - for c in vec![0xFEFF].into_iter().chain(args.encode_utf16()) { - // encode in little endian - out.push(c as u8); - out.push((c >> 8) as u8); - } - out - } else { - args.into_bytes() - }; - fs::write(&file, &bytes)?; - cmd2.arg(format!("@{}", file.display())); - info!("invoking linker {:?}", cmd2); - let output = cmd2.output(); - flush_linked_file(&output, out_filename)?; - return output; - - #[cfg(unix)] - fn flush_linked_file(_: &io::Result, _: &Path) -> io::Result<()> { - Ok(()) - } - - #[cfg(windows)] - fn flush_linked_file(command_output: &io::Result, out_filename: &Path) - -> io::Result<()> - { - // On Windows, under high I/O load, output buffers are sometimes not flushed, - // even long after process exit, causing nasty, non-reproducible output bugs. - // - // File::sync_all() calls FlushFileBuffers() down the line, which solves the problem. - // - // А full writeup of the original Chrome bug can be found at - // randomascii.wordpress.com/2018/02/25/compiler-bug-linker-bug-windows-kernel-bug/amp - - if let &Ok(ref out) = command_output { - if out.status.success() { - if let Ok(of) = fs::OpenOptions::new().write(true).open(out_filename) { - of.sync_all()?; - } - } - } - - Ok(()) - } - - #[cfg(unix)] - fn command_line_too_big(err: &io::Error) -> bool { - err.raw_os_error() == Some(::libc::E2BIG) - } - - #[cfg(windows)] - fn command_line_too_big(err: &io::Error) -> bool { - const ERROR_FILENAME_EXCED_RANGE: i32 = 206; - err.raw_os_error() == Some(ERROR_FILENAME_EXCED_RANGE) - } - - struct Escape<'a> { - arg: &'a str, - is_like_msvc: bool, - } - - impl<'a> fmt::Display for Escape<'a> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - if self.is_like_msvc { - // This is "documented" at - // https://msdn.microsoft.com/en-us/library/4xdcbak7.aspx - // - // Unfortunately there's not a great specification of the - // syntax I could find online (at least) but some local - // testing showed that this seemed sufficient-ish to catch - // at least a few edge cases. - write!(f, "\"")?; - for c in self.arg.chars() { - match c { - '"' => write!(f, "\\{}", c)?, - c => write!(f, "{}", c)?, - } - } - write!(f, "\"")?; - } else { - // This is documented at https://linux.die.net/man/1/ld, namely: - // - // > Options in file are separated by whitespace. A whitespace - // > character may be included in an option by surrounding the - // > entire option in either single or double quotes. Any - // > character (including a backslash) may be included by - // > prefixing the character to be included with a backslash. - // - // We put an argument on each line, so all we need to do is - // ensure the line is interpreted as one whole argument. - for c in self.arg.chars() { - match c { - '\\' | - ' ' => write!(f, "\\{}", c)?, - c => write!(f, "{}", c)?, - } - } - } - Ok(()) - } - } -} - -fn link_args(cmd: &mut Linker, - sess: &Session, - crate_type: config::CrateType, - tmpdir: &Path, - out_filename: &Path, - trans: &CrateTranslation) { - - // Linker plugins should be specified early in the list of arguments - cmd.cross_lang_lto(); - - // The default library location, we need this to find the runtime. - // The location of crates will be determined as needed. - let lib_path = sess.target_filesearch(PathKind::All).get_lib_path(); - - // target descriptor - let t = &sess.target.target; - - cmd.include_path(&fix_windows_verbatim_for_gcc(&lib_path)); - for obj in trans.modules.iter().filter_map(|m| m.object.as_ref()) { - cmd.add_object(obj); - } - cmd.output_filename(out_filename); - - if crate_type == config::CrateTypeExecutable && - sess.target.target.options.is_like_windows { - if let Some(ref s) = trans.windows_subsystem { - cmd.subsystem(s); - } - } - - // If we're building a dynamic library then some platforms need to make sure - // that all symbols are exported correctly from the dynamic library. - if crate_type != config::CrateTypeExecutable || - sess.target.target.options.is_like_emscripten { - cmd.export_symbols(tmpdir, crate_type); - } - - // When linking a dynamic library, we put the metadata into a section of the - // executable. This metadata is in a separate object file from the main - // object file, so we link that in here. - if crate_type == config::CrateTypeDylib || - crate_type == config::CrateTypeProcMacro { - if let Some(obj) = trans.metadata_module.object.as_ref() { - cmd.add_object(obj); - } - } - - let obj = trans.allocator_module - .as_ref() - .and_then(|m| m.object.as_ref()); - if let Some(obj) = obj { - cmd.add_object(obj); - } - - // Try to strip as much out of the generated object by removing unused - // sections if possible. See more comments in linker.rs - if !sess.opts.cg.link_dead_code { - let keep_metadata = crate_type == config::CrateTypeDylib; - cmd.gc_sections(keep_metadata); - } - - let used_link_args = &trans.crate_info.link_args; - - if crate_type == config::CrateTypeExecutable { - let mut position_independent_executable = false; - - if t.options.position_independent_executables { - let empty_vec = Vec::new(); - let args = sess.opts.cg.link_args.as_ref().unwrap_or(&empty_vec); - let more_args = &sess.opts.cg.link_arg; - let mut args = args.iter().chain(more_args.iter()).chain(used_link_args.iter()); - - if get_reloc_model(sess) == llvm::RelocMode::PIC - && !sess.crt_static() && !args.any(|x| *x == "-static") { - position_independent_executable = true; - } - } - - if position_independent_executable { - cmd.position_independent_executable(); - } else { - // recent versions of gcc can be configured to generate position - // independent executables by default. We have to pass -no-pie to - // explicitly turn that off. Not applicable to ld. - if sess.target.target.options.linker_is_gnu - && sess.linker_flavor() != LinkerFlavor::Ld { - cmd.no_position_independent_executable(); - } - } - } - - let relro_level = match sess.opts.debugging_opts.relro_level { - Some(level) => level, - None => t.options.relro_level, - }; - match relro_level { - RelroLevel::Full => { - cmd.full_relro(); - }, - RelroLevel::Partial => { - cmd.partial_relro(); - }, - RelroLevel::Off => { - cmd.no_relro(); - }, - RelroLevel::None => { - }, - } - - // Pass optimization flags down to the linker. - cmd.optimize(); - - // Pass debuginfo flags down to the linker. - cmd.debuginfo(); - - // We want to prevent the compiler from accidentally leaking in any system - // libraries, so we explicitly ask gcc to not link to any libraries by - // default. Note that this does not happen for windows because windows pulls - // in some large number of libraries and I couldn't quite figure out which - // subset we wanted. - if t.options.no_default_libraries { - cmd.no_default_libraries(); - } - - // Take careful note of the ordering of the arguments we pass to the linker - // here. Linkers will assume that things on the left depend on things to the - // right. Things on the right cannot depend on things on the left. This is - // all formally implemented in terms of resolving symbols (libs on the right - // resolve unknown symbols of libs on the left, but not vice versa). - // - // For this reason, we have organized the arguments we pass to the linker as - // such: - // - // 1. The local object that LLVM just generated - // 2. Local native libraries - // 3. Upstream rust libraries - // 4. Upstream native libraries - // - // The rationale behind this ordering is that those items lower down in the - // list can't depend on items higher up in the list. For example nothing can - // depend on what we just generated (e.g. that'd be a circular dependency). - // Upstream rust libraries are not allowed to depend on our local native - // libraries as that would violate the structure of the DAG, in that - // scenario they are required to link to them as well in a shared fashion. - // - // Note that upstream rust libraries may contain native dependencies as - // well, but they also can't depend on what we just started to add to the - // link line. And finally upstream native libraries can't depend on anything - // in this DAG so far because they're only dylibs and dylibs can only depend - // on other dylibs (e.g. other native deps). - add_local_native_libraries(cmd, sess, trans); - add_upstream_rust_crates(cmd, sess, trans, crate_type, tmpdir); - add_upstream_native_libraries(cmd, sess, trans, crate_type); - - // Tell the linker what we're doing. - if crate_type != config::CrateTypeExecutable { - cmd.build_dylib(out_filename); - } - if crate_type == config::CrateTypeExecutable && sess.crt_static() { - cmd.build_static_executable(); - } - - if sess.opts.debugging_opts.pgo_gen.is_some() { - cmd.pgo_gen(); - } - - // FIXME (#2397): At some point we want to rpath our guesses as to - // where extern libraries might live, based on the - // addl_lib_search_paths - if sess.opts.cg.rpath { - let sysroot = sess.sysroot(); - let target_triple = sess.opts.target_triple.triple(); - let mut get_install_prefix_lib_path = || { - let install_prefix = option_env!("CFG_PREFIX").expect("CFG_PREFIX"); - let tlib = filesearch::relative_target_lib_path(sysroot, target_triple); - let mut path = PathBuf::from(install_prefix); - path.push(&tlib); - - path - }; - let mut rpath_config = RPathConfig { - used_crates: &trans.crate_info.used_crates_dynamic, - out_filename: out_filename.to_path_buf(), - has_rpath: sess.target.target.options.has_rpath, - is_like_osx: sess.target.target.options.is_like_osx, - linker_is_gnu: sess.target.target.options.linker_is_gnu, - get_install_prefix_lib_path: &mut get_install_prefix_lib_path, - }; - cmd.args(&rpath::get_rpath_flags(&mut rpath_config)); - } - - // Finally add all the linker arguments provided on the command line along - // with any #[link_args] attributes found inside the crate - if let Some(ref args) = sess.opts.cg.link_args { - cmd.args(args); - } - cmd.args(&sess.opts.cg.link_arg); - cmd.args(&used_link_args); -} - -// # Native library linking -// -// User-supplied library search paths (-L on the command line). These are -// the same paths used to find Rust crates, so some of them may have been -// added already by the previous crate linking code. This only allows them -// to be found at compile time so it is still entirely up to outside -// forces to make sure that library can be found at runtime. -// -// Also note that the native libraries linked here are only the ones located -// in the current crate. Upstream crates with native library dependencies -// may have their native library pulled in above. -fn add_local_native_libraries(cmd: &mut Linker, - sess: &Session, - trans: &CrateTranslation) { - sess.target_filesearch(PathKind::All).for_each_lib_search_path(|path, k| { - match k { - PathKind::Framework => { cmd.framework_path(path); } - _ => { cmd.include_path(&fix_windows_verbatim_for_gcc(path)); } - } - }); - - let relevant_libs = trans.crate_info.used_libraries.iter().filter(|l| { - relevant_lib(sess, l) - }); - - let search_path = archive_search_paths(sess); - for lib in relevant_libs { - match lib.kind { - NativeLibraryKind::NativeUnknown => cmd.link_dylib(&lib.name.as_str()), - NativeLibraryKind::NativeFramework => cmd.link_framework(&lib.name.as_str()), - NativeLibraryKind::NativeStaticNobundle => cmd.link_staticlib(&lib.name.as_str()), - NativeLibraryKind::NativeStatic => cmd.link_whole_staticlib(&lib.name.as_str(), - &search_path) - } - } -} - -// # Rust Crate linking -// -// Rust crates are not considered at all when creating an rlib output. All -// 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, - trans: &CrateTranslation, - crate_type: config::CrateType, - 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. - // - // Linking to a rlib involves just passing it to the linker (the linker - // 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 = formats.get(&crate_type).unwrap(); - - // Invoke get_used_crates to ensure that we get a topological sorting of - // crates. - let deps = &trans.crate_info.used_crates_dynamic; - - // There's a few internal crates in the standard library (aka libcore and - // libstd) which actually have a circular dependence upon one another. This - // currently arises through "weak lang items" where libcore requires things - // like `rust_begin_unwind` but libstd ends up defining it. To get this - // circular dependence to work correctly in all situations we'll need to be - // sure to correctly apply the `--start-group` and `--end-group` options to - // GNU linkers, otherwise if we don't use any other symbol from the standard - // library it'll get discarded and the whole application won't link. - // - // In this loop we're calculating the `group_end`, after which crate to - // pass `--end-group` and `group_start`, before which crate to pass - // `--start-group`. We currently do this by passing `--end-group` after - // the first crate (when iterating backwards) that requires a lang item - // defined somewhere else. Once that's set then when we've defined all the - // necessary lang items we'll pass `--start-group`. - // - // Note that this isn't amazing logic for now but it should do the trick - // for the current implementation of the standard library. - let mut group_end = None; - let mut group_start = None; - let mut end_with = FxHashSet(); - let info = &trans.crate_info; - for &(cnum, _) in deps.iter().rev() { - if let Some(missing) = info.missing_lang_items.get(&cnum) { - end_with.extend(missing.iter().cloned()); - if end_with.len() > 0 && group_end.is_none() { - group_end = Some(cnum); - } - } - end_with.retain(|item| info.lang_item_to_crate.get(item) != Some(&cnum)); - if end_with.len() == 0 && group_end.is_some() { - group_start = Some(cnum); - break - } - } - - // If we didn't end up filling in all lang items from upstream crates then - // we'll be filling it in with our crate. This probably means we're the - // standard library itself, so skip this for now. - if group_end.is_some() && group_start.is_none() { - group_end = None; - } - - let mut compiler_builtins = None; - - for &(cnum, _) in deps.iter() { - if group_start == Some(cnum) { - cmd.group_start(); - } - - // 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 src = &trans.crate_info.used_crate_source[&cnum]; - match data[cnum.as_usize() - 1] { - _ if trans.crate_info.profiler_runtime == Some(cnum) => { - add_static_crate(cmd, sess, trans, tmpdir, crate_type, cnum); - } - _ if trans.crate_info.sanitizer_runtime == Some(cnum) => { - link_sanitizer_runtime(cmd, sess, trans, tmpdir, cnum); - } - // compiler-builtins are always placed last to ensure that they're - // linked correctly. - _ if trans.crate_info.compiler_builtins == Some(cnum) => { - assert!(compiler_builtins.is_none()); - compiler_builtins = Some(cnum); - } - Linkage::NotLinked | - Linkage::IncludedFromDylib => {} - Linkage::Static => { - add_static_crate(cmd, sess, trans, tmpdir, crate_type, cnum); - } - Linkage::Dynamic => { - add_dynamic_crate(cmd, sess, &src.dylib.as_ref().unwrap().0) - } - } - - if group_end == Some(cnum) { - cmd.group_end(); - } - } - - // compiler-builtins are always placed last to ensure that they're - // linked correctly. - // We must always link the `compiler_builtins` crate statically. Even if it - // was already "included" in a dylib (e.g. `libstd` when `-C prefer-dynamic` - // is used) - if let Some(cnum) = compiler_builtins { - add_static_crate(cmd, sess, trans, tmpdir, crate_type, cnum); - } - - // Converts a library file-stem into a cc -l argument - fn unlib<'a>(config: &config::Config, stem: &'a str) -> &'a str { - if stem.starts_with("lib") && !config.target.options.is_like_windows { - &stem[3..] - } else { - stem - } - } - - // We must link the sanitizer runtime using -Wl,--whole-archive but since - // it's packed in a .rlib, it contains stuff that are not objects that will - // make the linker error. So we must remove those bits from the .rlib before - // linking it. - fn link_sanitizer_runtime(cmd: &mut Linker, - sess: &Session, - trans: &CrateTranslation, - tmpdir: &Path, - cnum: CrateNum) { - let src = &trans.crate_info.used_crate_source[&cnum]; - let cratepath = &src.rlib.as_ref().unwrap().0; - - if sess.target.target.options.is_like_osx { - // On Apple platforms, the sanitizer is always built as a dylib, and - // LLVM will link to `@rpath/*.dylib`, so we need to specify an - // rpath to the library as well (the rpath should be absolute, see - // PR #41352 for details). - // - // FIXME: Remove this logic into librustc_*san once Cargo supports it - let rpath = cratepath.parent().unwrap(); - let rpath = rpath.to_str().expect("non-utf8 component in path"); - cmd.args(&["-Wl,-rpath".into(), "-Xlinker".into(), rpath.into()]); - } - - let dst = tmpdir.join(cratepath.file_name().unwrap()); - let cfg = archive_config(sess, &dst, Some(cratepath)); - let mut archive = ArchiveBuilder::new(cfg); - archive.update_symbols(); - - for f in archive.src_files() { - if f.ends_with(RLIB_BYTECODE_EXTENSION) || f == METADATA_FILENAME { - archive.remove_file(&f); - continue - } - } - - archive.build(); - - cmd.link_whole_rlib(&dst); - } - - // Adds the static "rlib" versions of all crates to the command line. - // There's a bit of magic which happens here specifically related to LTO and - // dynamic libraries. Specifically: - // - // * For LTO, we remove upstream object files. - // * For dylibs we remove metadata and bytecode from upstream rlibs - // - // When performing LTO, almost(*) all of the bytecode from the upstream - // libraries has already been included in our object file output. As a - // result we need to remove the object files in the upstream libraries so - // the linker doesn't try to include them twice (or whine about duplicate - // symbols). We must continue to include the rest of the rlib, however, as - // it may contain static native libraries which must be linked in. - // - // (*) Crates marked with `#![no_builtins]` don't participate in LTO and - // their bytecode wasn't included. The object files in those libraries must - // still be passed to the linker. - // - // When making a dynamic library, linkers by default don't include any - // object files in an archive if they're not necessary to resolve the link. - // We basically want to convert the archive (rlib) to a dylib, though, so we - // *do* want everything included in the output, regardless of whether the - // linker thinks it's needed or not. As a result we must use the - // --whole-archive option (or the platform equivalent). When using this - // option the linker will fail if there are non-objects in the archive (such - // as our own metadata and/or bytecode). All in all, for rlibs to be - // entirely included in dylibs, we need to remove all non-object files. - // - // Note, however, that if we're not doing LTO or we're not producing a dylib - // (aka we're making an executable), we can just pass the rlib blindly to - // the linker (fast) because it's fine if it's not actually included as - // we're at the end of the dependency chain. - fn add_static_crate(cmd: &mut Linker, - sess: &Session, - trans: &CrateTranslation, - tmpdir: &Path, - crate_type: config::CrateType, - cnum: CrateNum) { - let src = &trans.crate_info.used_crate_source[&cnum]; - let cratepath = &src.rlib.as_ref().unwrap().0; - - // See the comment above in `link_staticlib` and `link_rlib` for why if - // there's a static library that's not relevant we skip all object - // files. - let native_libs = &trans.crate_info.native_libraries[&cnum]; - let skip_native = native_libs.iter().any(|lib| { - lib.kind == NativeLibraryKind::NativeStatic && !relevant_lib(sess, lib) - }); - - if (!is_full_lto_enabled(sess) || - ignored_for_lto(sess, &trans.crate_info, cnum)) && - crate_type != config::CrateTypeDylib && - !skip_native { - cmd.link_rlib(&fix_windows_verbatim_for_gcc(cratepath)); - return - } - - let dst = tmpdir.join(cratepath.file_name().unwrap()); - let name = cratepath.file_name().unwrap().to_str().unwrap(); - let name = &name[3..name.len() - 5]; // chop off lib/.rlib - - time(sess, &format!("altering {}.rlib", name), || { - let cfg = archive_config(sess, &dst, Some(cratepath)); - let mut archive = ArchiveBuilder::new(cfg); - archive.update_symbols(); - - let mut any_objects = false; - for f in archive.src_files() { - if f.ends_with(RLIB_BYTECODE_EXTENSION) || f == METADATA_FILENAME { - archive.remove_file(&f); - continue - } - - let canonical = f.replace("-", "_"); - let canonical_name = name.replace("-", "_"); - - // Look for `.rcgu.o` at the end of the filename to conclude - // that this is a Rust-related object file. - fn looks_like_rust(s: &str) -> bool { - let path = Path::new(s); - let ext = path.extension().and_then(|s| s.to_str()); - if ext != Some(OutputType::Object.extension()) { - return false - } - let ext2 = path.file_stem() - .and_then(|s| Path::new(s).extension()) - .and_then(|s| s.to_str()); - ext2 == Some(RUST_CGU_EXT) - } - - let is_rust_object = - canonical.starts_with(&canonical_name) && - looks_like_rust(&f); - - // If we've been requested to skip all native object files - // (those not generated by the rust compiler) then we can skip - // this file. See above for why we may want to do this. - let skip_because_cfg_say_so = skip_native && !is_rust_object; - - // If we're performing LTO and this is a rust-generated object - // file, then we don't need the object file as it's part of the - // LTO module. Note that `#![no_builtins]` is excluded from LTO, - // though, so we let that object file slide. - let skip_because_lto = is_full_lto_enabled(sess) && - is_rust_object && - (sess.target.target.options.no_builtins || - !trans.crate_info.is_no_builtins.contains(&cnum)); - - if skip_because_cfg_say_so || skip_because_lto { - archive.remove_file(&f); - } else { - any_objects = true; - } - } - - if !any_objects { - return - } - archive.build(); - - // If we're creating a dylib, then we need to include the - // whole of each object in our archive into that artifact. This is - // because a `dylib` can be reused as an intermediate artifact. - // - // Note, though, that we don't want to include the whole of a - // compiler-builtins crate (e.g. compiler-rt) because it'll get - // repeatedly linked anyway. - if crate_type == config::CrateTypeDylib && - trans.crate_info.compiler_builtins != Some(cnum) { - cmd.link_whole_rlib(&fix_windows_verbatim_for_gcc(&dst)); - } else { - cmd.link_rlib(&fix_windows_verbatim_for_gcc(&dst)); - } - }); - } - - // Same thing as above, but for dynamic crates instead of static crates. - fn add_dynamic_crate(cmd: &mut Linker, sess: &Session, cratepath: &Path) { - // If we're performing LTO, then it should have been previously required - // that all upstream rust dependencies were available in an rlib format. - assert!(!is_full_lto_enabled(sess)); - - // Just need to tell the linker about where the library lives and - // what its name is - let parent = cratepath.parent(); - if let Some(dir) = parent { - cmd.include_path(&fix_windows_verbatim_for_gcc(dir)); - } - let filestem = cratepath.file_stem().unwrap().to_str().unwrap(); - cmd.link_rust_dylib(&unlib(&sess.target, filestem), - parent.unwrap_or(Path::new(""))); - } -} - -// Link in all of our upstream crates' native dependencies. Remember that -// all of these upstream native dependencies are all non-static -// dependencies. We've got two cases then: -// -// 1. The upstream crate is an rlib. In this case we *must* link in the -// native dependency because the rlib is just an archive. -// -// 2. The upstream crate is a dylib. In order to use the dylib, we have to -// have the dependency present on the system somewhere. Thus, we don't -// gain a whole lot from not linking in the dynamic dependency to this -// crate as well. -// -// The use case for this is a little subtle. In theory the native -// dependencies of a crate are purely an implementation detail of the crate -// itself, but the problem arises with generic and inlined functions. If a -// generic function calls a native function, then the generic function must -// be instantiated in the target crate, meaning that the native symbol must -// also be resolved in the target crate. -fn add_upstream_native_libraries(cmd: &mut Linker, - sess: &Session, - trans: &CrateTranslation, - crate_type: config::CrateType) { - // Be sure to use a topological sorting of crates because there may be - // interdependencies between native libraries. When passing -nodefaultlibs, - // for example, almost all native libraries depend on libc, so we have to - // make sure that's all the way at the right (liblibc is near the base of - // the dependency chain). - // - // This passes RequireStatic, but the actual requirement doesn't matter, - // we're just getting an ordering of crate numbers, we're not worried about - // the paths. - let formats = sess.dependency_formats.borrow(); - let data = formats.get(&crate_type).unwrap(); - - let crates = &trans.crate_info.used_crates_static; - for &(cnum, _) in crates { - for lib in trans.crate_info.native_libraries[&cnum].iter() { - if !relevant_lib(sess, &lib) { - continue - } - match lib.kind { - NativeLibraryKind::NativeUnknown => cmd.link_dylib(&lib.name.as_str()), - NativeLibraryKind::NativeFramework => cmd.link_framework(&lib.name.as_str()), - NativeLibraryKind::NativeStaticNobundle => { - // Link "static-nobundle" native libs only if the crate they originate from - // is being linked statically to the current crate. If it's linked dynamically - // or is an rlib already included via some other dylib crate, the symbols from - // native libs will have already been included in that dylib. - if data[cnum.as_usize() - 1] == Linkage::Static { - cmd.link_staticlib(&lib.name.as_str()) - } - }, - // ignore statically included native libraries here as we've - // already included them when we included the rust library - // previously - NativeLibraryKind::NativeStatic => {} - } - } - } -} - -fn relevant_lib(sess: &Session, lib: &NativeLibrary) -> bool { - match lib.cfg { - Some(ref cfg) => attr::cfg_matches(cfg, &sess.parse_sess, None), - None => true, - } -} - -fn is_full_lto_enabled(sess: &Session) -> bool { - match sess.lto() { - Lto::Yes | - Lto::Thin | - Lto::Fat => true, - Lto::No | - Lto::ThinLocal => false, - } -} diff --git a/src/librustc_trans/back/linker.rs b/src/librustc_trans/back/linker.rs deleted file mode 100644 index 2a84ffe79b2..00000000000 --- a/src/librustc_trans/back/linker.rs +++ /dev/null @@ -1,1037 +0,0 @@ -// Copyright 2015 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -use std::collections::HashMap; -use std::ffi::{OsStr, OsString}; -use std::fs::{self, File}; -use std::io::prelude::*; -use std::io::{self, BufWriter}; -use std::path::{Path, PathBuf}; - -use back::archive; -use back::command::Command; -use back::symbol_export; -use rustc::hir::def_id::{LOCAL_CRATE, CrateNum}; -use rustc::middle::dependency_format::Linkage; -use rustc::session::Session; -use rustc::session::config::{self, CrateType, OptLevel, DebugInfoLevel, - CrossLangLto}; -use rustc::ty::TyCtxt; -use rustc_target::spec::{LinkerFlavor, LldFlavor}; -use serialize::{json, Encoder}; - -/// For all the linkers we support, and information they might -/// need out of the shared crate context before we get rid of it. -pub struct LinkerInfo { - exports: HashMap>, -} - -impl LinkerInfo { - pub fn new(tcx: TyCtxt) -> LinkerInfo { - LinkerInfo { - exports: tcx.sess.crate_types.borrow().iter().map(|&c| { - (c, exported_symbols(tcx, c)) - }).collect(), - } - } - - pub fn to_linker<'a>(&'a self, - cmd: Command, - sess: &'a Session) -> Box { - match sess.linker_flavor() { - LinkerFlavor::Lld(LldFlavor::Link) | - LinkerFlavor::Msvc => { - Box::new(MsvcLinker { - cmd, - sess, - info: self - }) as Box - } - LinkerFlavor::Em => { - Box::new(EmLinker { - cmd, - sess, - info: self - }) as Box - } - LinkerFlavor::Gcc => { - Box::new(GccLinker { - cmd, - sess, - info: self, - hinted_static: false, - is_ld: false, - }) as Box - } - - LinkerFlavor::Lld(LldFlavor::Ld) | - LinkerFlavor::Lld(LldFlavor::Ld64) | - LinkerFlavor::Ld => { - Box::new(GccLinker { - cmd, - sess, - info: self, - hinted_static: false, - is_ld: true, - }) as Box - } - - LinkerFlavor::Lld(LldFlavor::Wasm) => { - Box::new(WasmLd { - cmd, - }) as Box - } - } - } -} - -/// Linker abstraction used by back::link to build up the command to invoke a -/// linker. -/// -/// This trait is the total list of requirements needed by `back::link` and -/// represents the meaning of each option being passed down. This trait is then -/// used to dispatch on whether a GNU-like linker (generally `ld.exe`) or an -/// MSVC linker (e.g. `link.exe`) is being used. -pub trait Linker { - fn link_dylib(&mut self, lib: &str); - fn link_rust_dylib(&mut self, lib: &str, path: &Path); - fn link_framework(&mut self, framework: &str); - fn link_staticlib(&mut self, lib: &str); - fn link_rlib(&mut self, lib: &Path); - fn link_whole_rlib(&mut self, lib: &Path); - fn link_whole_staticlib(&mut self, lib: &str, search_path: &[PathBuf]); - fn include_path(&mut self, path: &Path); - fn framework_path(&mut self, path: &Path); - fn output_filename(&mut self, path: &Path); - fn add_object(&mut self, path: &Path); - fn gc_sections(&mut self, keep_metadata: bool); - fn position_independent_executable(&mut self); - fn no_position_independent_executable(&mut self); - fn full_relro(&mut self); - fn partial_relro(&mut self); - fn no_relro(&mut self); - fn optimize(&mut self); - fn pgo_gen(&mut self); - fn debuginfo(&mut self); - fn no_default_libraries(&mut self); - fn build_dylib(&mut self, out_filename: &Path); - fn build_static_executable(&mut self); - fn args(&mut self, args: &[String]); - fn export_symbols(&mut self, tmpdir: &Path, crate_type: CrateType); - fn subsystem(&mut self, subsystem: &str); - fn group_start(&mut self); - fn group_end(&mut self); - fn cross_lang_lto(&mut self); - // Should have been finalize(self), but we don't support self-by-value on trait objects (yet?). - fn finalize(&mut self) -> Command; -} - -pub struct GccLinker<'a> { - cmd: Command, - sess: &'a Session, - info: &'a LinkerInfo, - hinted_static: bool, // Keeps track of the current hinting mode. - // Link as ld - is_ld: bool, -} - -impl<'a> GccLinker<'a> { - /// Argument that must be passed *directly* to the linker - /// - /// These arguments need to be prepended with '-Wl,' when a gcc-style linker is used - fn linker_arg(&mut self, arg: S) -> &mut Self - where S: AsRef - { - if !self.is_ld { - let mut os = OsString::from("-Wl,"); - os.push(arg.as_ref()); - self.cmd.arg(os); - } else { - self.cmd.arg(arg); - } - self - } - - fn takes_hints(&self) -> bool { - !self.sess.target.target.options.is_like_osx - } - - // Some platforms take hints about whether a library is static or dynamic. - // For those that support this, we ensure we pass the option if the library - // was flagged "static" (most defaults are dynamic) to ensure that if - // libfoo.a and libfoo.so both exist that the right one is chosen. - fn hint_static(&mut self) { - if !self.takes_hints() { return } - if !self.hinted_static { - self.linker_arg("-Bstatic"); - self.hinted_static = true; - } - } - - fn hint_dynamic(&mut self) { - if !self.takes_hints() { return } - if self.hinted_static { - self.linker_arg("-Bdynamic"); - self.hinted_static = false; - } - } -} - -impl<'a> Linker for GccLinker<'a> { - fn link_dylib(&mut self, lib: &str) { self.hint_dynamic(); self.cmd.arg("-l").arg(lib); } - fn link_staticlib(&mut self, lib: &str) { self.hint_static(); self.cmd.arg("-l").arg(lib); } - fn link_rlib(&mut self, lib: &Path) { self.hint_static(); self.cmd.arg(lib); } - fn include_path(&mut self, path: &Path) { self.cmd.arg("-L").arg(path); } - fn framework_path(&mut self, path: &Path) { self.cmd.arg("-F").arg(path); } - fn output_filename(&mut self, path: &Path) { self.cmd.arg("-o").arg(path); } - fn add_object(&mut self, path: &Path) { self.cmd.arg(path); } - fn position_independent_executable(&mut self) { self.cmd.arg("-pie"); } - fn no_position_independent_executable(&mut self) { self.cmd.arg("-no-pie"); } - fn full_relro(&mut self) { self.linker_arg("-z,relro,-z,now"); } - fn partial_relro(&mut self) { self.linker_arg("-z,relro"); } - fn no_relro(&mut self) { self.linker_arg("-z,norelro"); } - fn build_static_executable(&mut self) { self.cmd.arg("-static"); } - fn args(&mut self, args: &[String]) { self.cmd.args(args); } - - fn link_rust_dylib(&mut self, lib: &str, _path: &Path) { - self.hint_dynamic(); - self.cmd.arg("-l").arg(lib); - } - - fn link_framework(&mut self, framework: &str) { - self.hint_dynamic(); - self.cmd.arg("-framework").arg(framework); - } - - // Here we explicitly ask that the entire archive is included into the - // result artifact. For more details see #15460, but the gist is that - // the linker will strip away any unused objects in the archive if we - // don't otherwise explicitly reference them. This can occur for - // libraries which are just providing bindings, libraries with generic - // functions, etc. - fn link_whole_staticlib(&mut self, lib: &str, search_path: &[PathBuf]) { - self.hint_static(); - let target = &self.sess.target.target; - if !target.options.is_like_osx { - self.linker_arg("--whole-archive").cmd.arg("-l").arg(lib); - self.linker_arg("--no-whole-archive"); - } else { - // -force_load is the macOS equivalent of --whole-archive, but it - // involves passing the full path to the library to link. - let mut v = OsString::from("-force_load,"); - v.push(&archive::find_library(lib, search_path, &self.sess)); - self.linker_arg(&v); - } - } - - fn link_whole_rlib(&mut self, lib: &Path) { - self.hint_static(); - if self.sess.target.target.options.is_like_osx { - let mut v = OsString::from("-force_load,"); - v.push(lib); - self.linker_arg(&v); - } else { - self.linker_arg("--whole-archive").cmd.arg(lib); - self.linker_arg("--no-whole-archive"); - } - } - - fn gc_sections(&mut self, keep_metadata: bool) { - // The dead_strip option to the linker specifies that functions and data - // unreachable by the entry point will be removed. This is quite useful - // with Rust's compilation model of compiling libraries at a time into - // one object file. For example, this brings hello world from 1.7MB to - // 458K. - // - // Note that this is done for both executables and dynamic libraries. We - // won't get much benefit from dylibs because LLVM will have already - // stripped away as much as it could. This has not been seen to impact - // link times negatively. - // - // -dead_strip can't be part of the pre_link_args because it's also used - // for partial linking when using multiple codegen units (-r). So we - // insert it here. - if self.sess.target.target.options.is_like_osx { - self.linker_arg("-dead_strip"); - } else if self.sess.target.target.options.is_like_solaris { - self.linker_arg("-z"); - self.linker_arg("ignore"); - - // If we're building a dylib, we don't use --gc-sections because LLVM - // has already done the best it can do, and we also don't want to - // eliminate the metadata. If we're building an executable, however, - // --gc-sections drops the size of hello world from 1.8MB to 597K, a 67% - // reduction. - } else if !keep_metadata { - self.linker_arg("--gc-sections"); - } - } - - fn optimize(&mut self) { - if !self.sess.target.target.options.linker_is_gnu { return } - - // GNU-style linkers support optimization with -O. GNU ld doesn't - // need a numeric argument, but other linkers do. - if self.sess.opts.optimize == config::OptLevel::Default || - self.sess.opts.optimize == config::OptLevel::Aggressive { - self.linker_arg("-O1"); - } - } - - fn pgo_gen(&mut self) { - if !self.sess.target.target.options.linker_is_gnu { return } - - // If we're doing PGO generation stuff and on a GNU-like linker, use the - // "-u" flag to properly pull in the profiler runtime bits. - // - // This is because LLVM otherwise won't add the needed initialization - // for us on Linux (though the extra flag should be harmless if it - // does). - // - // See https://reviews.llvm.org/D14033 and https://reviews.llvm.org/D14030. - // - // Though it may be worth to try to revert those changes upstream, since - // the overhead of the initialization should be minor. - self.cmd.arg("-u"); - self.cmd.arg("__llvm_profile_runtime"); - } - - fn debuginfo(&mut self) { - match self.sess.opts.debuginfo { - DebugInfoLevel::NoDebugInfo => { - // If we are building without debuginfo enabled and we were called with - // `-Zstrip-debuginfo-if-disabled=yes`, tell the linker to strip any debuginfo - // found when linking to get rid of symbols from libstd. - match self.sess.opts.debugging_opts.strip_debuginfo_if_disabled { - Some(true) => { self.linker_arg("-S"); }, - _ => {}, - } - }, - _ => {}, - }; - } - - fn no_default_libraries(&mut self) { - if !self.is_ld { - self.cmd.arg("-nodefaultlibs"); - } - } - - fn build_dylib(&mut self, out_filename: &Path) { - // On mac we need to tell the linker to let this library be rpathed - if self.sess.target.target.options.is_like_osx { - self.cmd.arg("-dynamiclib"); - self.linker_arg("-dylib"); - - // Note that the `osx_rpath_install_name` option here is a hack - // purely to support rustbuild right now, we should get a more - // principled solution at some point to force the compiler to pass - // the right `-Wl,-install_name` with an `@rpath` in it. - if self.sess.opts.cg.rpath || - self.sess.opts.debugging_opts.osx_rpath_install_name { - let mut v = OsString::from("-install_name,@rpath/"); - v.push(out_filename.file_name().unwrap()); - self.linker_arg(&v); - } - } else { - self.cmd.arg("-shared"); - } - } - - fn export_symbols(&mut self, tmpdir: &Path, crate_type: CrateType) { - // If we're compiling a dylib, then we let symbol visibility in object - // files to take care of whether they're exported or not. - // - // If we're compiling a cdylib, however, we manually create a list of - // exported symbols to ensure we don't expose any more. The object files - // have far more public symbols than we actually want to export, so we - // hide them all here. - if crate_type == CrateType::CrateTypeDylib || - crate_type == CrateType::CrateTypeProcMacro { - return - } - - let mut arg = OsString::new(); - let path = tmpdir.join("list"); - - debug!("EXPORTED SYMBOLS:"); - - if self.sess.target.target.options.is_like_osx { - // Write a plain, newline-separated list of symbols - let res = (|| -> io::Result<()> { - let mut f = BufWriter::new(File::create(&path)?); - for sym in self.info.exports[&crate_type].iter() { - debug!(" _{}", sym); - writeln!(f, "_{}", sym)?; - } - Ok(()) - })(); - if let Err(e) = res { - self.sess.fatal(&format!("failed to write lib.def file: {}", e)); - } - } else { - // Write an LD version script - let res = (|| -> io::Result<()> { - let mut f = BufWriter::new(File::create(&path)?); - writeln!(f, "{{\n global:")?; - for sym in self.info.exports[&crate_type].iter() { - debug!(" {};", sym); - writeln!(f, " {};", sym)?; - } - writeln!(f, "\n local:\n *;\n}};")?; - Ok(()) - })(); - if let Err(e) = res { - self.sess.fatal(&format!("failed to write version script: {}", e)); - } - } - - if self.sess.target.target.options.is_like_osx { - if !self.is_ld { - arg.push("-Wl,") - } - arg.push("-exported_symbols_list,"); - } else if self.sess.target.target.options.is_like_solaris { - if !self.is_ld { - arg.push("-Wl,") - } - arg.push("-M,"); - } else { - if !self.is_ld { - arg.push("-Wl,") - } - arg.push("--version-script="); - } - - arg.push(&path); - self.cmd.arg(arg); - } - - fn subsystem(&mut self, subsystem: &str) { - self.linker_arg(&format!("--subsystem,{}", subsystem)); - } - - fn finalize(&mut self) -> Command { - self.hint_dynamic(); // Reset to default before returning the composed command line. - let mut cmd = Command::new(""); - ::std::mem::swap(&mut cmd, &mut self.cmd); - cmd - } - - fn group_start(&mut self) { - if !self.sess.target.target.options.is_like_osx { - self.linker_arg("--start-group"); - } - } - - fn group_end(&mut self) { - if !self.sess.target.target.options.is_like_osx { - self.linker_arg("--end-group"); - } - } - - fn cross_lang_lto(&mut self) { - match self.sess.opts.debugging_opts.cross_lang_lto { - CrossLangLto::Disabled | - CrossLangLto::NoLink => { - // Nothing to do - } - CrossLangLto::LinkerPlugin(ref path) => { - self.linker_arg(&format!("-plugin={}", path.display())); - - let opt_level = match self.sess.opts.optimize { - config::OptLevel::No => "O0", - config::OptLevel::Less => "O1", - config::OptLevel::Default => "O2", - config::OptLevel::Aggressive => "O3", - config::OptLevel::Size => "Os", - config::OptLevel::SizeMin => "Oz", - }; - - self.linker_arg(&format!("-plugin-opt={}", opt_level)); - self.linker_arg(&format!("-plugin-opt=mcpu={}", self.sess.target_cpu())); - - match self.sess.opts.cg.lto { - config::Lto::Thin | - config::Lto::ThinLocal => { - self.linker_arg(&format!("-plugin-opt=thin")); - } - config::Lto::Fat | - config::Lto::Yes | - config::Lto::No => { - // default to regular LTO - } - } - } - } - } -} - -pub struct MsvcLinker<'a> { - cmd: Command, - sess: &'a Session, - info: &'a LinkerInfo -} - -impl<'a> Linker for MsvcLinker<'a> { - fn link_rlib(&mut self, lib: &Path) { self.cmd.arg(lib); } - fn add_object(&mut self, path: &Path) { self.cmd.arg(path); } - fn args(&mut self, args: &[String]) { self.cmd.args(args); } - - fn build_dylib(&mut self, out_filename: &Path) { - self.cmd.arg("/DLL"); - let mut arg: OsString = "/IMPLIB:".into(); - arg.push(out_filename.with_extension("dll.lib")); - self.cmd.arg(arg); - } - - fn build_static_executable(&mut self) { - // noop - } - - fn gc_sections(&mut self, _keep_metadata: bool) { - // MSVC's ICF (Identical COMDAT Folding) link optimization is - // slow for Rust and thus we disable it by default when not in - // optimization build. - if self.sess.opts.optimize != config::OptLevel::No { - self.cmd.arg("/OPT:REF,ICF"); - } else { - // It is necessary to specify NOICF here, because /OPT:REF - // implies ICF by default. - self.cmd.arg("/OPT:REF,NOICF"); - } - } - - fn link_dylib(&mut self, lib: &str) { - self.cmd.arg(&format!("{}.lib", lib)); - } - - fn link_rust_dylib(&mut self, lib: &str, path: &Path) { - // When producing a dll, the MSVC linker may not actually emit a - // `foo.lib` file if the dll doesn't actually export any symbols, so we - // check to see if the file is there and just omit linking to it if it's - // not present. - let name = format!("{}.dll.lib", lib); - if fs::metadata(&path.join(&name)).is_ok() { - self.cmd.arg(name); - } - } - - fn link_staticlib(&mut self, lib: &str) { - self.cmd.arg(&format!("{}.lib", lib)); - } - - fn position_independent_executable(&mut self) { - // noop - } - - fn no_position_independent_executable(&mut self) { - // noop - } - - fn full_relro(&mut self) { - // noop - } - - fn partial_relro(&mut self) { - // noop - } - - fn no_relro(&mut self) { - // noop - } - - fn no_default_libraries(&mut self) { - // Currently we don't pass the /NODEFAULTLIB flag to the linker on MSVC - // as there's been trouble in the past of linking the C++ standard - // library required by LLVM. This likely needs to happen one day, but - // in general Windows is also a more controlled environment than - // Unix, so it's not necessarily as critical that this be implemented. - // - // Note that there are also some licensing worries about statically - // linking some libraries which require a specific agreement, so it may - // not ever be possible for us to pass this flag. - } - - fn include_path(&mut self, path: &Path) { - let mut arg = OsString::from("/LIBPATH:"); - arg.push(path); - self.cmd.arg(&arg); - } - - fn output_filename(&mut self, path: &Path) { - let mut arg = OsString::from("/OUT:"); - arg.push(path); - self.cmd.arg(&arg); - } - - fn framework_path(&mut self, _path: &Path) { - bug!("frameworks are not supported on windows") - } - fn link_framework(&mut self, _framework: &str) { - bug!("frameworks are not supported on windows") - } - - fn link_whole_staticlib(&mut self, lib: &str, _search_path: &[PathBuf]) { - // not supported? - self.link_staticlib(lib); - } - fn link_whole_rlib(&mut self, path: &Path) { - // not supported? - self.link_rlib(path); - } - fn optimize(&mut self) { - // Needs more investigation of `/OPT` arguments - } - - fn pgo_gen(&mut self) { - // Nothing needed here. - } - - fn debuginfo(&mut self) { - // This will cause the Microsoft linker to generate a PDB file - // from the CodeView line tables in the object files. - self.cmd.arg("/DEBUG"); - - // This will cause the Microsoft linker to embed .natvis info into the the PDB file - let sysroot = self.sess.sysroot(); - let natvis_dir_path = sysroot.join("lib\\rustlib\\etc"); - if let Ok(natvis_dir) = fs::read_dir(&natvis_dir_path) { - // LLVM 5.0.0's lld-link frontend doesn't yet recognize, and chokes - // on, the /NATVIS:... flags. LLVM 6 (or earlier) should at worst ignore - // them, eventually mooting this workaround, per this landed patch: - // https://github.com/llvm-mirror/lld/commit/27b9c4285364d8d76bb43839daa100 - if let Some(ref linker_path) = self.sess.opts.cg.linker { - if let Some(linker_name) = Path::new(&linker_path).file_stem() { - if linker_name.to_str().unwrap().to_lowercase() == "lld-link" { - self.sess.warn("not embedding natvis: lld-link may not support the flag"); - return; - } - } - } - for entry in natvis_dir { - match entry { - Ok(entry) => { - let path = entry.path(); - if path.extension() == Some("natvis".as_ref()) { - let mut arg = OsString::from("/NATVIS:"); - arg.push(path); - self.cmd.arg(arg); - } - }, - Err(err) => { - self.sess.warn(&format!("error enumerating natvis directory: {}", err)); - }, - } - } - } - } - - // Currently the compiler doesn't use `dllexport` (an LLVM attribute) to - // export symbols from a dynamic library. When building a dynamic library, - // however, we're going to want some symbols exported, so this function - // generates a DEF file which lists all the symbols. - // - // The linker will read this `*.def` file and export all the symbols from - // the dynamic library. Note that this is not as simple as just exporting - // all the symbols in the current crate (as specified by `trans.reachable`) - // but rather we also need to possibly export the symbols of upstream - // crates. Upstream rlibs may be linked statically to this dynamic library, - // in which case they may continue to transitively be used and hence need - // their symbols exported. - fn export_symbols(&mut self, - tmpdir: &Path, - crate_type: CrateType) { - let path = tmpdir.join("lib.def"); - let res = (|| -> io::Result<()> { - let mut f = BufWriter::new(File::create(&path)?); - - // Start off with the standard module name header and then go - // straight to exports. - writeln!(f, "LIBRARY")?; - writeln!(f, "EXPORTS")?; - for symbol in self.info.exports[&crate_type].iter() { - debug!(" _{}", symbol); - writeln!(f, " {}", symbol)?; - } - Ok(()) - })(); - if let Err(e) = res { - self.sess.fatal(&format!("failed to write lib.def file: {}", e)); - } - let mut arg = OsString::from("/DEF:"); - arg.push(path); - self.cmd.arg(&arg); - } - - fn subsystem(&mut self, subsystem: &str) { - // Note that previous passes of the compiler validated this subsystem, - // so we just blindly pass it to the linker. - self.cmd.arg(&format!("/SUBSYSTEM:{}", subsystem)); - - // Windows has two subsystems we're interested in right now, the console - // and windows subsystems. These both implicitly have different entry - // points (starting symbols). The console entry point starts with - // `mainCRTStartup` and the windows entry point starts with - // `WinMainCRTStartup`. These entry points, defined in system libraries, - // will then later probe for either `main` or `WinMain`, respectively to - // start the application. - // - // In Rust we just always generate a `main` function so we want control - // to always start there, so we force the entry point on the windows - // subsystem to be `mainCRTStartup` to get everything booted up - // correctly. - // - // For more information see RFC #1665 - if subsystem == "windows" { - self.cmd.arg("/ENTRY:mainCRTStartup"); - } - } - - fn finalize(&mut self) -> Command { - let mut cmd = Command::new(""); - ::std::mem::swap(&mut cmd, &mut self.cmd); - cmd - } - - // MSVC doesn't need group indicators - fn group_start(&mut self) {} - fn group_end(&mut self) {} - - fn cross_lang_lto(&mut self) { - // Do nothing - } -} - -pub struct EmLinker<'a> { - cmd: Command, - sess: &'a Session, - info: &'a LinkerInfo -} - -impl<'a> Linker for EmLinker<'a> { - fn include_path(&mut self, path: &Path) { - self.cmd.arg("-L").arg(path); - } - - fn link_staticlib(&mut self, lib: &str) { - self.cmd.arg("-l").arg(lib); - } - - fn output_filename(&mut self, path: &Path) { - self.cmd.arg("-o").arg(path); - } - - fn add_object(&mut self, path: &Path) { - self.cmd.arg(path); - } - - fn link_dylib(&mut self, lib: &str) { - // Emscripten always links statically - self.link_staticlib(lib); - } - - fn link_whole_staticlib(&mut self, lib: &str, _search_path: &[PathBuf]) { - // not supported? - self.link_staticlib(lib); - } - - fn link_whole_rlib(&mut self, lib: &Path) { - // not supported? - self.link_rlib(lib); - } - - fn link_rust_dylib(&mut self, lib: &str, _path: &Path) { - self.link_dylib(lib); - } - - fn link_rlib(&mut self, lib: &Path) { - self.add_object(lib); - } - - fn position_independent_executable(&mut self) { - // noop - } - - fn no_position_independent_executable(&mut self) { - // noop - } - - fn full_relro(&mut self) { - // noop - } - - fn partial_relro(&mut self) { - // noop - } - - fn no_relro(&mut self) { - // noop - } - - fn args(&mut self, args: &[String]) { - self.cmd.args(args); - } - - fn framework_path(&mut self, _path: &Path) { - bug!("frameworks are not supported on Emscripten") - } - - fn link_framework(&mut self, _framework: &str) { - bug!("frameworks are not supported on Emscripten") - } - - fn gc_sections(&mut self, _keep_metadata: bool) { - // noop - } - - fn optimize(&mut self) { - // Emscripten performs own optimizations - self.cmd.arg(match self.sess.opts.optimize { - OptLevel::No => "-O0", - OptLevel::Less => "-O1", - OptLevel::Default => "-O2", - OptLevel::Aggressive => "-O3", - OptLevel::Size => "-Os", - OptLevel::SizeMin => "-Oz" - }); - // Unusable until https://github.com/rust-lang/rust/issues/38454 is resolved - self.cmd.args(&["--memory-init-file", "0"]); - } - - fn pgo_gen(&mut self) { - // noop, but maybe we need something like the gnu linker? - } - - fn debuginfo(&mut self) { - // Preserve names or generate source maps depending on debug info - self.cmd.arg(match self.sess.opts.debuginfo { - DebugInfoLevel::NoDebugInfo => "-g0", - DebugInfoLevel::LimitedDebugInfo => "-g3", - DebugInfoLevel::FullDebugInfo => "-g4" - }); - } - - fn no_default_libraries(&mut self) { - self.cmd.args(&["-s", "DEFAULT_LIBRARY_FUNCS_TO_INCLUDE=[]"]); - } - - fn build_dylib(&mut self, _out_filename: &Path) { - bug!("building dynamic library is unsupported on Emscripten") - } - - fn build_static_executable(&mut self) { - // noop - } - - fn export_symbols(&mut self, _tmpdir: &Path, crate_type: CrateType) { - let symbols = &self.info.exports[&crate_type]; - - debug!("EXPORTED SYMBOLS:"); - - self.cmd.arg("-s"); - - let mut arg = OsString::from("EXPORTED_FUNCTIONS="); - let mut encoded = String::new(); - - { - let mut encoder = json::Encoder::new(&mut encoded); - let res = encoder.emit_seq(symbols.len(), |encoder| { - for (i, sym) in symbols.iter().enumerate() { - encoder.emit_seq_elt(i, |encoder| { - encoder.emit_str(&("_".to_string() + sym)) - })?; - } - Ok(()) - }); - if let Err(e) = res { - self.sess.fatal(&format!("failed to encode exported symbols: {}", e)); - } - } - debug!("{}", encoded); - arg.push(encoded); - - self.cmd.arg(arg); - } - - fn subsystem(&mut self, _subsystem: &str) { - // noop - } - - fn finalize(&mut self) -> Command { - let mut cmd = Command::new(""); - ::std::mem::swap(&mut cmd, &mut self.cmd); - cmd - } - - // Appears not necessary on Emscripten - fn group_start(&mut self) {} - fn group_end(&mut self) {} - - fn cross_lang_lto(&mut self) { - // Do nothing - } -} - -fn exported_symbols(tcx: TyCtxt, crate_type: CrateType) -> Vec { - let mut symbols = Vec::new(); - - let export_threshold = symbol_export::crates_export_threshold(&[crate_type]); - for &(symbol, level) in tcx.exported_symbols(LOCAL_CRATE).iter() { - if level.is_below_threshold(export_threshold) { - symbols.push(symbol.symbol_name(tcx).to_string()); - } - } - - let formats = tcx.sess.dependency_formats.borrow(); - let deps = formats[&crate_type].iter(); - - for (index, dep_format) in deps.enumerate() { - let cnum = CrateNum::new(index + 1); - // For each dependency that we are linking to statically ... - if *dep_format == Linkage::Static { - // ... we add its symbol list to our export list. - for &(symbol, level) in tcx.exported_symbols(cnum).iter() { - if level.is_below_threshold(export_threshold) { - symbols.push(symbol.symbol_name(tcx).to_string()); - } - } - } - } - - symbols -} - -pub struct WasmLd { - cmd: Command, -} - -impl Linker for WasmLd { - fn link_dylib(&mut self, lib: &str) { - self.cmd.arg("-l").arg(lib); - } - - fn link_staticlib(&mut self, lib: &str) { - self.cmd.arg("-l").arg(lib); - } - - fn link_rlib(&mut self, lib: &Path) { - self.cmd.arg(lib); - } - - fn include_path(&mut self, path: &Path) { - self.cmd.arg("-L").arg(path); - } - - fn framework_path(&mut self, _path: &Path) { - panic!("frameworks not supported") - } - - fn output_filename(&mut self, path: &Path) { - self.cmd.arg("-o").arg(path); - } - - fn add_object(&mut self, path: &Path) { - self.cmd.arg(path); - } - - fn position_independent_executable(&mut self) { - } - - fn full_relro(&mut self) { - } - - fn partial_relro(&mut self) { - } - - fn no_relro(&mut self) { - } - - fn build_static_executable(&mut self) { - } - - fn args(&mut self, args: &[String]) { - self.cmd.args(args); - } - - fn link_rust_dylib(&mut self, lib: &str, _path: &Path) { - self.cmd.arg("-l").arg(lib); - } - - fn link_framework(&mut self, _framework: &str) { - panic!("frameworks not supported") - } - - fn link_whole_staticlib(&mut self, lib: &str, _search_path: &[PathBuf]) { - self.cmd.arg("-l").arg(lib); - } - - fn link_whole_rlib(&mut self, lib: &Path) { - self.cmd.arg(lib); - } - - fn gc_sections(&mut self, _keep_metadata: bool) { - } - - fn optimize(&mut self) { - } - - fn pgo_gen(&mut self) { - } - - fn debuginfo(&mut self) { - } - - fn no_default_libraries(&mut self) { - } - - fn build_dylib(&mut self, _out_filename: &Path) { - } - - fn export_symbols(&mut self, _tmpdir: &Path, _crate_type: CrateType) { - } - - fn subsystem(&mut self, _subsystem: &str) { - } - - fn no_position_independent_executable(&mut self) { - } - - fn finalize(&mut self) -> Command { - // There have been reports in the wild (rustwasm/wasm-bindgen#119) of - // using threads causing weird hangs and bugs. Disable it entirely as - // this isn't yet the bottleneck of compilation at all anyway. - self.cmd.arg("--no-threads"); - - self.cmd.arg("-z").arg("stack-size=1048576"); - - // FIXME we probably shouldn't pass this but instead pass an explicit - // whitelist of symbols we'll allow to be undefined. Unfortunately - // though we can't handle symbols like `log10` that LLVM injects at a - // super late date without actually parsing object files. For now let's - // stick to this and hopefully fix it before stabilization happens. - self.cmd.arg("--allow-undefined"); - - // For now we just never have an entry symbol - self.cmd.arg("--no-entry"); - - let mut cmd = Command::new(""); - ::std::mem::swap(&mut cmd, &mut self.cmd); - cmd - } - - // Not needed for now with LLD - fn group_start(&mut self) {} - fn group_end(&mut self) {} - - fn cross_lang_lto(&mut self) { - // Do nothing for now - } -} diff --git a/src/librustc_trans/back/lto.rs b/src/librustc_trans/back/lto.rs deleted file mode 100644 index bbb5f7eecc8..00000000000 --- a/src/librustc_trans/back/lto.rs +++ /dev/null @@ -1,773 +0,0 @@ -// Copyright 2013 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -use back::bytecode::{DecodedBytecode, RLIB_BYTECODE_EXTENSION}; -use back::symbol_export; -use back::write::{ModuleConfig, with_llvm_pmb, CodegenContext}; -use back::write; -use errors::{FatalError, Handler}; -use llvm::archive_ro::ArchiveRO; -use llvm::{ModuleRef, TargetMachineRef, True, False}; -use llvm; -use rustc::hir::def_id::LOCAL_CRATE; -use rustc::middle::exported_symbols::SymbolExportLevel; -use rustc::session::config::{self, Lto}; -use rustc::util::common::time_ext; -use time_graph::Timeline; -use {ModuleTranslation, ModuleLlvm, ModuleKind, ModuleSource}; - -use libc; - -use std::ffi::CString; -use std::ptr; -use std::slice; -use std::sync::Arc; - -pub fn crate_type_allows_lto(crate_type: config::CrateType) -> bool { - match crate_type { - config::CrateTypeExecutable | - config::CrateTypeStaticlib | - config::CrateTypeCdylib => true, - - config::CrateTypeDylib | - config::CrateTypeRlib | - config::CrateTypeProcMacro => false, - } -} - -pub(crate) enum LtoModuleTranslation { - Fat { - module: Option, - _serialized_bitcode: Vec, - }, - - Thin(ThinModule), -} - -impl LtoModuleTranslation { - pub fn name(&self) -> &str { - match *self { - LtoModuleTranslation::Fat { .. } => "everything", - LtoModuleTranslation::Thin(ref m) => m.name(), - } - } - - /// Optimize this module within the given codegen context. - /// - /// This function is unsafe as it'll return a `ModuleTranslation` still - /// points to LLVM data structures owned by this `LtoModuleTranslation`. - /// It's intended that the module returned is immediately code generated and - /// dropped, and then this LTO module is dropped. - pub(crate) unsafe fn optimize(&mut self, - cgcx: &CodegenContext, - timeline: &mut Timeline) - -> Result - { - match *self { - LtoModuleTranslation::Fat { ref mut module, .. } => { - let trans = module.take().unwrap(); - let config = cgcx.config(trans.kind); - let llmod = trans.llvm().unwrap().llmod; - let tm = trans.llvm().unwrap().tm; - run_pass_manager(cgcx, tm, llmod, config, false); - timeline.record("fat-done"); - Ok(trans) - } - LtoModuleTranslation::Thin(ref mut thin) => thin.optimize(cgcx, timeline), - } - } - - /// A "gauge" of how costly it is to optimize this module, used to sort - /// biggest modules first. - pub fn cost(&self) -> u64 { - match *self { - // Only one module with fat LTO, so the cost doesn't matter. - LtoModuleTranslation::Fat { .. } => 0, - LtoModuleTranslation::Thin(ref m) => m.cost(), - } - } -} - -pub(crate) fn run(cgcx: &CodegenContext, - modules: Vec, - timeline: &mut Timeline) - -> Result, FatalError> -{ - let diag_handler = cgcx.create_diag_handler(); - let export_threshold = match cgcx.lto { - // We're just doing LTO for our one crate - Lto::ThinLocal => SymbolExportLevel::Rust, - - // We're doing LTO for the entire crate graph - Lto::Yes | Lto::Fat | Lto::Thin => { - symbol_export::crates_export_threshold(&cgcx.crate_types) - } - - Lto::No => panic!("didn't request LTO but we're doing LTO"), - }; - - let symbol_filter = &|&(ref name, level): &(String, SymbolExportLevel)| { - if level.is_below_threshold(export_threshold) { - let mut bytes = Vec::with_capacity(name.len() + 1); - bytes.extend(name.bytes()); - Some(CString::new(bytes).unwrap()) - } else { - None - } - }; - let exported_symbols = cgcx.exported_symbols - .as_ref().expect("needs exported symbols for LTO"); - let mut symbol_white_list = exported_symbols[&LOCAL_CRATE] - .iter() - .filter_map(symbol_filter) - .collect::>(); - timeline.record("whitelist"); - info!("{} symbols to preserve in this crate", symbol_white_list.len()); - - // If we're performing LTO for the entire crate graph, then for each of our - // upstream dependencies, find the corresponding rlib and load the bitcode - // from the archive. - // - // We save off all the bytecode and LLVM module ids for later processing - // with either fat or thin LTO - let mut upstream_modules = Vec::new(); - if cgcx.lto != Lto::ThinLocal { - if cgcx.opts.cg.prefer_dynamic { - diag_handler.struct_err("cannot prefer dynamic linking when performing LTO") - .note("only 'staticlib', 'bin', and 'cdylib' outputs are \ - supported with LTO") - .emit(); - return Err(FatalError) - } - - // Make sure we actually can run LTO - for crate_type in cgcx.crate_types.iter() { - if !crate_type_allows_lto(*crate_type) { - let e = diag_handler.fatal("lto can only be run for executables, cdylibs and \ - static library outputs"); - return Err(e) - } - } - - for &(cnum, ref path) in cgcx.each_linked_rlib_for_lto.iter() { - let exported_symbols = cgcx.exported_symbols - .as_ref().expect("needs exported symbols for LTO"); - symbol_white_list.extend( - exported_symbols[&cnum] - .iter() - .filter_map(symbol_filter)); - - let archive = ArchiveRO::open(&path).expect("wanted an rlib"); - let bytecodes = archive.iter().filter_map(|child| { - child.ok().and_then(|c| c.name().map(|name| (name, c))) - }).filter(|&(name, _)| name.ends_with(RLIB_BYTECODE_EXTENSION)); - for (name, data) in bytecodes { - info!("adding bytecode {}", name); - let bc_encoded = data.data(); - - let (bc, id) = time_ext(cgcx.time_passes, None, &format!("decode {}", name), || { - match DecodedBytecode::new(bc_encoded) { - Ok(b) => Ok((b.bytecode(), b.identifier().to_string())), - Err(e) => Err(diag_handler.fatal(&e)), - } - })?; - let bc = SerializedModule::FromRlib(bc); - upstream_modules.push((bc, CString::new(id).unwrap())); - } - timeline.record(&format!("load: {}", path.display())); - } - } - - let arr = symbol_white_list.iter().map(|c| c.as_ptr()).collect::>(); - match cgcx.lto { - Lto::Yes | // `-C lto` == fat LTO by default - Lto::Fat => { - fat_lto(cgcx, &diag_handler, modules, upstream_modules, &arr, timeline) - } - Lto::Thin | - Lto::ThinLocal => { - thin_lto(&diag_handler, modules, upstream_modules, &arr, timeline) - } - Lto::No => unreachable!(), - } -} - -fn fat_lto(cgcx: &CodegenContext, - diag_handler: &Handler, - mut modules: Vec, - mut serialized_modules: Vec<(SerializedModule, CString)>, - symbol_white_list: &[*const libc::c_char], - timeline: &mut Timeline) - -> Result, FatalError> -{ - info!("going for a fat lto"); - - // Find the "costliest" module and merge everything into that codegen unit. - // All the other modules will be serialized and reparsed into the new - // context, so this hopefully avoids serializing and parsing the largest - // codegen unit. - // - // Additionally use a regular module as the base here to ensure that various - // file copy operations in the backend work correctly. The only other kind - // of module here should be an allocator one, and if your crate is smaller - // than the allocator module then the size doesn't really matter anyway. - let (_, costliest_module) = modules.iter() - .enumerate() - .filter(|&(_, module)| module.kind == ModuleKind::Regular) - .map(|(i, module)| { - let cost = unsafe { - llvm::LLVMRustModuleCost(module.llvm().unwrap().llmod) - }; - (cost, i) - }) - .max() - .expect("must be trans'ing at least one module"); - let module = modules.remove(costliest_module); - let llmod = module.llvm().expect("can't lto pre-translated modules").llmod; - info!("using {:?} as a base module", module.llmod_id); - - // For all other modules we translated we'll need to link them into our own - // bitcode. All modules were translated in their own LLVM context, however, - // and we want to move everything to the same LLVM context. Currently the - // way we know of to do that is to serialize them to a string and them parse - // them later. Not great but hey, that's why it's "fat" LTO, right? - for module in modules { - let llvm = module.llvm().expect("can't lto pre-translated modules"); - let buffer = ModuleBuffer::new(llvm.llmod); - let llmod_id = CString::new(&module.llmod_id[..]).unwrap(); - serialized_modules.push((SerializedModule::Local(buffer), llmod_id)); - } - - // For all serialized bitcode files we parse them and link them in as we did - // above, this is all mostly handled in C++. Like above, though, we don't - // know much about the memory management here so we err on the side of being - // save and persist everything with the original module. - let mut serialized_bitcode = Vec::new(); - let mut linker = Linker::new(llmod); - for (bc_decoded, name) in serialized_modules { - info!("linking {:?}", name); - time_ext(cgcx.time_passes, None, &format!("ll link {:?}", name), || { - let data = bc_decoded.data(); - linker.add(&data).map_err(|()| { - let msg = format!("failed to load bc of {:?}", name); - write::llvm_err(&diag_handler, msg) - }) - })?; - timeline.record(&format!("link {:?}", name)); - serialized_bitcode.push(bc_decoded); - } - drop(linker); - cgcx.save_temp_bitcode(&module, "lto.input"); - - // Internalize everything that *isn't* in our whitelist to help strip out - // more modules and such - unsafe { - let ptr = symbol_white_list.as_ptr(); - llvm::LLVMRustRunRestrictionPass(llmod, - ptr as *const *const libc::c_char, - symbol_white_list.len() as libc::size_t); - cgcx.save_temp_bitcode(&module, "lto.after-restriction"); - } - - if cgcx.no_landing_pads { - unsafe { - llvm::LLVMRustMarkAllFunctionsNounwind(llmod); - } - cgcx.save_temp_bitcode(&module, "lto.after-nounwind"); - } - timeline.record("passes"); - - Ok(vec![LtoModuleTranslation::Fat { - module: Some(module), - _serialized_bitcode: serialized_bitcode, - }]) -} - -struct Linker(llvm::LinkerRef); - -impl Linker { - fn new(llmod: ModuleRef) -> Linker { - unsafe { Linker(llvm::LLVMRustLinkerNew(llmod)) } - } - - fn add(&mut self, bytecode: &[u8]) -> Result<(), ()> { - unsafe { - if llvm::LLVMRustLinkerAdd(self.0, - bytecode.as_ptr() as *const libc::c_char, - bytecode.len()) { - Ok(()) - } else { - Err(()) - } - } - } -} - -impl Drop for Linker { - fn drop(&mut self) { - unsafe { llvm::LLVMRustLinkerFree(self.0); } - } -} - -/// Prepare "thin" LTO to get run on these modules. -/// -/// The general structure of ThinLTO is quite different from the structure of -/// "fat" LTO above. With "fat" LTO all LLVM modules in question are merged into -/// one giant LLVM module, and then we run more optimization passes over this -/// big module after internalizing most symbols. Thin LTO, on the other hand, -/// avoid this large bottleneck through more targeted optimization. -/// -/// At a high level Thin LTO looks like: -/// -/// 1. Prepare a "summary" of each LLVM module in question which describes -/// the values inside, cost of the values, etc. -/// 2. Merge the summaries of all modules in question into one "index" -/// 3. Perform some global analysis on this index -/// 4. For each module, use the index and analysis calculated previously to -/// perform local transformations on the module, for example inlining -/// small functions from other modules. -/// 5. Run thin-specific optimization passes over each module, and then code -/// generate everything at the end. -/// -/// The summary for each module is intended to be quite cheap, and the global -/// index is relatively quite cheap to create as well. As a result, the goal of -/// ThinLTO is to reduce the bottleneck on LTO and enable LTO to be used in more -/// situations. For example one cheap optimization is that we can parallelize -/// all codegen modules, easily making use of all the cores on a machine. -/// -/// With all that in mind, the function here is designed at specifically just -/// calculating the *index* for ThinLTO. This index will then be shared amongst -/// all of the `LtoModuleTranslation` units returned below and destroyed once -/// they all go out of scope. -fn thin_lto(diag_handler: &Handler, - modules: Vec, - serialized_modules: Vec<(SerializedModule, CString)>, - symbol_white_list: &[*const libc::c_char], - timeline: &mut Timeline) - -> Result, FatalError> -{ - unsafe { - info!("going for that thin, thin LTO"); - - let mut thin_buffers = Vec::new(); - let mut module_names = Vec::new(); - let mut thin_modules = Vec::new(); - - // FIXME: right now, like with fat LTO, we serialize all in-memory - // modules before working with them and ThinLTO. We really - // shouldn't do this, however, and instead figure out how to - // extract a summary from an in-memory module and then merge that - // into the global index. It turns out that this loop is by far - // the most expensive portion of this small bit of global - // analysis! - for (i, module) in modules.iter().enumerate() { - info!("local module: {} - {}", i, module.llmod_id); - let llvm = module.llvm().expect("can't lto pretranslated module"); - let name = CString::new(module.llmod_id.clone()).unwrap(); - let buffer = ThinBuffer::new(llvm.llmod); - thin_modules.push(llvm::ThinLTOModule { - identifier: name.as_ptr(), - data: buffer.data().as_ptr(), - len: buffer.data().len(), - }); - thin_buffers.push(buffer); - module_names.push(name); - timeline.record(&module.llmod_id); - } - - // FIXME: All upstream crates are deserialized internally in the - // function below to extract their summary and modules. Note that - // unlike the loop above we *must* decode and/or read something - // here as these are all just serialized files on disk. An - // improvement, however, to make here would be to store the - // module summary separately from the actual module itself. Right - // now this is store in one large bitcode file, and the entire - // file is deflate-compressed. We could try to bypass some of the - // decompression by storing the index uncompressed and only - // lazily decompressing the bytecode if necessary. - // - // Note that truly taking advantage of this optimization will - // likely be further down the road. We'd have to implement - // incremental ThinLTO first where we could actually avoid - // looking at upstream modules entirely sometimes (the contents, - // we must always unconditionally look at the index). - let mut serialized = Vec::new(); - for (module, name) in serialized_modules { - info!("foreign module {:?}", name); - thin_modules.push(llvm::ThinLTOModule { - identifier: name.as_ptr(), - data: module.data().as_ptr(), - len: module.data().len(), - }); - serialized.push(module); - module_names.push(name); - } - - // Delegate to the C++ bindings to create some data here. Once this is a - // tried-and-true interface we may wish to try to upstream some of this - // to LLVM itself, right now we reimplement a lot of what they do - // upstream... - let data = llvm::LLVMRustCreateThinLTOData( - thin_modules.as_ptr(), - thin_modules.len() as u32, - symbol_white_list.as_ptr(), - symbol_white_list.len() as u32, - ); - if data.is_null() { - let msg = format!("failed to prepare thin LTO context"); - return Err(write::llvm_err(&diag_handler, msg)) - } - let data = ThinData(data); - info!("thin LTO data created"); - timeline.record("data"); - - // Throw our data in an `Arc` as we'll be sharing it across threads. We - // also put all memory referenced by the C++ data (buffers, ids, etc) - // into the arc as well. After this we'll create a thin module - // translation per module in this data. - let shared = Arc::new(ThinShared { - data, - thin_buffers, - serialized_modules: serialized, - module_names, - }); - Ok((0..shared.module_names.len()).map(|i| { - LtoModuleTranslation::Thin(ThinModule { - shared: shared.clone(), - idx: i, - }) - }).collect()) - } -} - -fn run_pass_manager(cgcx: &CodegenContext, - tm: TargetMachineRef, - llmod: ModuleRef, - config: &ModuleConfig, - thin: bool) { - // Now we have one massive module inside of llmod. Time to run the - // LTO-specific optimization passes that LLVM provides. - // - // This code is based off the code found in llvm's LTO code generator: - // tools/lto/LTOCodeGenerator.cpp - debug!("running the pass manager"); - unsafe { - let pm = llvm::LLVMCreatePassManager(); - llvm::LLVMRustAddAnalysisPasses(tm, pm, llmod); - let pass = llvm::LLVMRustFindAndCreatePass("verify\0".as_ptr() as *const _); - assert!(!pass.is_null()); - llvm::LLVMRustAddPass(pm, pass); - - // When optimizing for LTO we don't actually pass in `-O0`, but we force - // it to always happen at least with `-O1`. - // - // With ThinLTO we mess around a lot with symbol visibility in a way - // that will actually cause linking failures if we optimize at O0 which - // notable is lacking in dead code elimination. To ensure we at least - // get some optimizations and correctly link we forcibly switch to `-O1` - // to get dead code elimination. - // - // Note that in general this shouldn't matter too much as you typically - // only turn on ThinLTO when you're compiling with optimizations - // otherwise. - let opt_level = config.opt_level.unwrap_or(llvm::CodeGenOptLevel::None); - let opt_level = match opt_level { - llvm::CodeGenOptLevel::None => llvm::CodeGenOptLevel::Less, - level => level, - }; - with_llvm_pmb(llmod, config, opt_level, false, &mut |b| { - if thin { - if !llvm::LLVMRustPassManagerBuilderPopulateThinLTOPassManager(b, pm) { - panic!("this version of LLVM does not support ThinLTO"); - } - } else { - llvm::LLVMPassManagerBuilderPopulateLTOPassManager(b, pm, - /* Internalize = */ False, - /* RunInliner = */ True); - } - }); - - let pass = llvm::LLVMRustFindAndCreatePass("verify\0".as_ptr() as *const _); - assert!(!pass.is_null()); - llvm::LLVMRustAddPass(pm, pass); - - time_ext(cgcx.time_passes, None, "LTO passes", || - llvm::LLVMRunPassManager(pm, llmod)); - - llvm::LLVMDisposePassManager(pm); - } - debug!("lto done"); -} - -pub enum SerializedModule { - Local(ModuleBuffer), - FromRlib(Vec), -} - -impl SerializedModule { - fn data(&self) -> &[u8] { - match *self { - SerializedModule::Local(ref m) => m.data(), - SerializedModule::FromRlib(ref m) => m, - } - } -} - -pub struct ModuleBuffer(*mut llvm::ModuleBuffer); - -unsafe impl Send for ModuleBuffer {} -unsafe impl Sync for ModuleBuffer {} - -impl ModuleBuffer { - pub fn new(m: ModuleRef) -> ModuleBuffer { - ModuleBuffer(unsafe { - llvm::LLVMRustModuleBufferCreate(m) - }) - } - - pub fn data(&self) -> &[u8] { - unsafe { - let ptr = llvm::LLVMRustModuleBufferPtr(self.0); - let len = llvm::LLVMRustModuleBufferLen(self.0); - slice::from_raw_parts(ptr, len) - } - } -} - -impl Drop for ModuleBuffer { - fn drop(&mut self) { - unsafe { llvm::LLVMRustModuleBufferFree(self.0); } - } -} - -pub struct ThinModule { - shared: Arc, - idx: usize, -} - -struct ThinShared { - data: ThinData, - thin_buffers: Vec, - serialized_modules: Vec, - module_names: Vec, -} - -struct ThinData(*mut llvm::ThinLTOData); - -unsafe impl Send for ThinData {} -unsafe impl Sync for ThinData {} - -impl Drop for ThinData { - fn drop(&mut self) { - unsafe { - llvm::LLVMRustFreeThinLTOData(self.0); - } - } -} - -pub struct ThinBuffer(*mut llvm::ThinLTOBuffer); - -unsafe impl Send for ThinBuffer {} -unsafe impl Sync for ThinBuffer {} - -impl ThinBuffer { - pub fn new(m: ModuleRef) -> ThinBuffer { - unsafe { - let buffer = llvm::LLVMRustThinLTOBufferCreate(m); - ThinBuffer(buffer) - } - } - - pub fn data(&self) -> &[u8] { - unsafe { - let ptr = llvm::LLVMRustThinLTOBufferPtr(self.0) as *const _; - let len = llvm::LLVMRustThinLTOBufferLen(self.0); - slice::from_raw_parts(ptr, len) - } - } -} - -impl Drop for ThinBuffer { - fn drop(&mut self) { - unsafe { - llvm::LLVMRustThinLTOBufferFree(self.0); - } - } -} - -impl ThinModule { - fn name(&self) -> &str { - self.shared.module_names[self.idx].to_str().unwrap() - } - - fn cost(&self) -> u64 { - // Yes, that's correct, we're using the size of the bytecode as an - // indicator for how costly this codegen unit is. - self.data().len() as u64 - } - - fn data(&self) -> &[u8] { - let a = self.shared.thin_buffers.get(self.idx).map(|b| b.data()); - a.unwrap_or_else(|| { - let len = self.shared.thin_buffers.len(); - self.shared.serialized_modules[self.idx - len].data() - }) - } - - unsafe fn optimize(&mut self, cgcx: &CodegenContext, timeline: &mut Timeline) - -> Result - { - let diag_handler = cgcx.create_diag_handler(); - let tm = (cgcx.tm_factory)().map_err(|e| { - write::llvm_err(&diag_handler, e) - })?; - - // Right now the implementation we've got only works over serialized - // modules, so we create a fresh new LLVM context and parse the module - // into that context. One day, however, we may do this for upstream - // crates but for locally translated modules we may be able to reuse - // that LLVM Context and Module. - let llcx = llvm::LLVMRustContextCreate(cgcx.fewer_names); - let llmod = llvm::LLVMRustParseBitcodeForThinLTO( - llcx, - self.data().as_ptr(), - self.data().len(), - self.shared.module_names[self.idx].as_ptr(), - ); - if llmod.is_null() { - let msg = format!("failed to parse bitcode for thin LTO module"); - return Err(write::llvm_err(&diag_handler, msg)); - } - let mtrans = ModuleTranslation { - source: ModuleSource::Translated(ModuleLlvm { - llmod, - llcx, - tm, - }), - llmod_id: self.name().to_string(), - name: self.name().to_string(), - kind: ModuleKind::Regular, - }; - cgcx.save_temp_bitcode(&mtrans, "thin-lto-input"); - - // Before we do much else find the "main" `DICompileUnit` that we'll be - // using below. If we find more than one though then rustc has changed - // in a way we're not ready for, so generate an ICE by returning - // an error. - let mut cu1 = ptr::null_mut(); - let mut cu2 = ptr::null_mut(); - llvm::LLVMRustThinLTOGetDICompileUnit(llmod, &mut cu1, &mut cu2); - if !cu2.is_null() { - let msg = format!("multiple source DICompileUnits found"); - return Err(write::llvm_err(&diag_handler, msg)) - } - - // Like with "fat" LTO, get some better optimizations if landing pads - // are disabled by removing all landing pads. - if cgcx.no_landing_pads { - llvm::LLVMRustMarkAllFunctionsNounwind(llmod); - cgcx.save_temp_bitcode(&mtrans, "thin-lto-after-nounwind"); - timeline.record("nounwind"); - } - - // Up next comes the per-module local analyses that we do for Thin LTO. - // Each of these functions is basically copied from the LLVM - // implementation and then tailored to suit this implementation. Ideally - // each of these would be supported by upstream LLVM but that's perhaps - // a patch for another day! - // - // You can find some more comments about these functions in the LLVM - // bindings we've got (currently `PassWrapper.cpp`) - if !llvm::LLVMRustPrepareThinLTORename(self.shared.data.0, llmod) { - let msg = format!("failed to prepare thin LTO module"); - return Err(write::llvm_err(&diag_handler, msg)) - } - cgcx.save_temp_bitcode(&mtrans, "thin-lto-after-rename"); - timeline.record("rename"); - if !llvm::LLVMRustPrepareThinLTOResolveWeak(self.shared.data.0, llmod) { - let msg = format!("failed to prepare thin LTO module"); - return Err(write::llvm_err(&diag_handler, msg)) - } - cgcx.save_temp_bitcode(&mtrans, "thin-lto-after-resolve"); - timeline.record("resolve"); - if !llvm::LLVMRustPrepareThinLTOInternalize(self.shared.data.0, llmod) { - let msg = format!("failed to prepare thin LTO module"); - return Err(write::llvm_err(&diag_handler, msg)) - } - cgcx.save_temp_bitcode(&mtrans, "thin-lto-after-internalize"); - timeline.record("internalize"); - if !llvm::LLVMRustPrepareThinLTOImport(self.shared.data.0, llmod) { - let msg = format!("failed to prepare thin LTO module"); - return Err(write::llvm_err(&diag_handler, msg)) - } - cgcx.save_temp_bitcode(&mtrans, "thin-lto-after-import"); - timeline.record("import"); - - // Ok now this is a bit unfortunate. This is also something you won't - // find upstream in LLVM's ThinLTO passes! This is a hack for now to - // work around bugs in LLVM. - // - // First discovered in #45511 it was found that as part of ThinLTO - // importing passes LLVM will import `DICompileUnit` metadata - // information across modules. This means that we'll be working with one - // LLVM module that has multiple `DICompileUnit` instances in it (a - // bunch of `llvm.dbg.cu` members). Unfortunately there's a number of - // bugs in LLVM's backend which generates invalid DWARF in a situation - // like this: - // - // https://bugs.llvm.org/show_bug.cgi?id=35212 - // https://bugs.llvm.org/show_bug.cgi?id=35562 - // - // While the first bug there is fixed the second ended up causing #46346 - // which was basically a resurgence of #45511 after LLVM's bug 35212 was - // fixed. - // - // This function below is a huge hack around this problem. The function - // below is defined in `PassWrapper.cpp` and will basically "merge" - // all `DICompileUnit` instances in a module. Basically it'll take all - // the objects, rewrite all pointers of `DISubprogram` to point to the - // first `DICompileUnit`, and then delete all the other units. - // - // This is probably mangling to the debug info slightly (but hopefully - // not too much) but for now at least gets LLVM to emit valid DWARF (or - // so it appears). Hopefully we can remove this once upstream bugs are - // fixed in LLVM. - llvm::LLVMRustThinLTOPatchDICompileUnit(llmod, cu1); - cgcx.save_temp_bitcode(&mtrans, "thin-lto-after-patch"); - timeline.record("patch"); - - // Alright now that we've done everything related to the ThinLTO - // analysis it's time to run some optimizations! Here we use the same - // `run_pass_manager` as the "fat" LTO above except that we tell it to - // populate a thin-specific pass manager, which presumably LLVM treats a - // little differently. - info!("running thin lto passes over {}", mtrans.name); - let config = cgcx.config(mtrans.kind); - run_pass_manager(cgcx, tm, llmod, config, true); - cgcx.save_temp_bitcode(&mtrans, "thin-lto-after-pm"); - timeline.record("thin-done"); - - // FIXME: this is a hack around a bug in LLVM right now. Discovered in - // #46910 it was found out that on 32-bit MSVC LLVM will hit a codegen - // error if there's an available_externally function in the LLVM module. - // Typically we don't actually use these functions but ThinLTO makes - // heavy use of them when inlining across modules. - // - // Tracked upstream at https://bugs.llvm.org/show_bug.cgi?id=35736 this - // function call (and its definition on the C++ side of things) - // shouldn't be necessary eventually and we can safetly delete these few - // lines. - llvm::LLVMRustThinLTORemoveAvailableExternally(llmod); - cgcx.save_temp_bitcode(&mtrans, "thin-lto-after-rm-ae"); - timeline.record("no-ae"); - - Ok(mtrans) - } -} diff --git a/src/librustc_trans/back/rpath.rs b/src/librustc_trans/back/rpath.rs deleted file mode 100644 index 8e5e7d37648..00000000000 --- a/src/librustc_trans/back/rpath.rs +++ /dev/null @@ -1,282 +0,0 @@ -// Copyright 2012-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. - -use std::collections::HashSet; -use std::env; -use std::path::{Path, PathBuf}; -use std::fs; - -use rustc::hir::def_id::CrateNum; -use rustc::middle::cstore::LibSource; - -pub struct RPathConfig<'a> { - pub used_crates: &'a [(CrateNum, LibSource)], - pub out_filename: PathBuf, - pub is_like_osx: bool, - pub has_rpath: bool, - pub linker_is_gnu: bool, - pub get_install_prefix_lib_path: &'a mut FnMut() -> PathBuf, -} - -pub fn get_rpath_flags(config: &mut RPathConfig) -> Vec { - // No rpath on windows - if !config.has_rpath { - return Vec::new(); - } - - let mut flags = Vec::new(); - - debug!("preparing the RPATH!"); - - let libs = config.used_crates.clone(); - let libs = libs.iter().filter_map(|&(_, ref l)| l.option()).collect::>(); - let rpaths = get_rpaths(config, &libs); - flags.extend_from_slice(&rpaths_to_flags(&rpaths)); - - // Use DT_RUNPATH instead of DT_RPATH if available - if config.linker_is_gnu { - flags.push("-Wl,--enable-new-dtags".to_string()); - } - - flags -} - -fn rpaths_to_flags(rpaths: &[String]) -> Vec { - let mut ret = Vec::new(); - for rpath in rpaths { - if rpath.contains(',') { - ret.push("-Wl,-rpath".into()); - ret.push("-Xlinker".into()); - ret.push(rpath.clone()); - } else { - ret.push(format!("-Wl,-rpath,{}", &(*rpath))); - } - } - return ret; -} - -fn get_rpaths(config: &mut RPathConfig, libs: &[PathBuf]) -> Vec { - debug!("output: {:?}", config.out_filename.display()); - debug!("libs:"); - for libpath in libs { - debug!(" {:?}", libpath.display()); - } - - // Use relative paths to the libraries. Binaries can be moved - // as long as they maintain the relative relationship to the - // crates they depend on. - let rel_rpaths = get_rpaths_relative_to_output(config, libs); - - // And a final backup rpath to the global library location. - let fallback_rpaths = vec![get_install_prefix_rpath(config)]; - - fn log_rpaths(desc: &str, rpaths: &[String]) { - debug!("{} rpaths:", desc); - for rpath in rpaths { - debug!(" {}", *rpath); - } - } - - log_rpaths("relative", &rel_rpaths); - log_rpaths("fallback", &fallback_rpaths); - - let mut rpaths = rel_rpaths; - rpaths.extend_from_slice(&fallback_rpaths); - - // Remove duplicates - let rpaths = minimize_rpaths(&rpaths); - return rpaths; -} - -fn get_rpaths_relative_to_output(config: &mut RPathConfig, - libs: &[PathBuf]) -> Vec { - libs.iter().map(|a| get_rpath_relative_to_output(config, a)).collect() -} - -fn get_rpath_relative_to_output(config: &mut RPathConfig, lib: &Path) -> String { - // Mac doesn't appear to support $ORIGIN - let prefix = if config.is_like_osx { - "@loader_path" - } else { - "$ORIGIN" - }; - - let cwd = env::current_dir().unwrap(); - let mut lib = fs::canonicalize(&cwd.join(lib)).unwrap_or(cwd.join(lib)); - lib.pop(); - let mut output = cwd.join(&config.out_filename); - output.pop(); - let output = fs::canonicalize(&output).unwrap_or(output); - let relative = path_relative_from(&lib, &output) - .expect(&format!("couldn't create relative path from {:?} to {:?}", output, lib)); - // FIXME (#9639): This needs to handle non-utf8 paths - format!("{}/{}", prefix, - relative.to_str().expect("non-utf8 component in path")) -} - -// This routine is adapted from the *old* Path's `path_relative_from` -// function, which works differently from the new `relative_from` function. -// In particular, this handles the case on unix where both paths are -// absolute but with only the root as the common directory. -fn path_relative_from(path: &Path, base: &Path) -> Option { - use std::path::Component; - - if path.is_absolute() != base.is_absolute() { - if path.is_absolute() { - Some(PathBuf::from(path)) - } else { - None - } - } else { - let mut ita = path.components(); - let mut itb = base.components(); - let mut comps: Vec = vec![]; - loop { - match (ita.next(), itb.next()) { - (None, None) => break, - (Some(a), None) => { - comps.push(a); - comps.extend(ita.by_ref()); - break; - } - (None, _) => comps.push(Component::ParentDir), - (Some(a), Some(b)) if comps.is_empty() && a == b => (), - (Some(a), Some(b)) if b == Component::CurDir => comps.push(a), - (Some(_), Some(b)) if b == Component::ParentDir => return None, - (Some(a), Some(_)) => { - comps.push(Component::ParentDir); - for _ in itb { - comps.push(Component::ParentDir); - } - comps.push(a); - comps.extend(ita.by_ref()); - break; - } - } - } - Some(comps.iter().map(|c| c.as_os_str()).collect()) - } -} - - -fn get_install_prefix_rpath(config: &mut RPathConfig) -> String { - let path = (config.get_install_prefix_lib_path)(); - let path = env::current_dir().unwrap().join(&path); - // FIXME (#9639): This needs to handle non-utf8 paths - path.to_str().expect("non-utf8 component in rpath").to_string() -} - -fn minimize_rpaths(rpaths: &[String]) -> Vec { - let mut set = HashSet::new(); - let mut minimized = Vec::new(); - for rpath in rpaths { - if set.insert(rpath) { - minimized.push(rpath.clone()); - } - } - minimized -} - -#[cfg(all(unix, test))] -mod tests { - use super::{RPathConfig}; - use super::{minimize_rpaths, rpaths_to_flags, get_rpath_relative_to_output}; - use std::path::{Path, PathBuf}; - - #[test] - fn test_rpaths_to_flags() { - let flags = rpaths_to_flags(&[ - "path1".to_string(), - "path2".to_string() - ]); - assert_eq!(flags, - ["-Wl,-rpath,path1", - "-Wl,-rpath,path2"]); - } - - #[test] - fn test_minimize1() { - let res = minimize_rpaths(&[ - "rpath1".to_string(), - "rpath2".to_string(), - "rpath1".to_string() - ]); - assert!(res == [ - "rpath1", - "rpath2", - ]); - } - - #[test] - fn test_minimize2() { - let res = minimize_rpaths(&[ - "1a".to_string(), - "2".to_string(), - "2".to_string(), - "1a".to_string(), - "4a".to_string(), - "1a".to_string(), - "2".to_string(), - "3".to_string(), - "4a".to_string(), - "3".to_string() - ]); - assert!(res == [ - "1a", - "2", - "4a", - "3", - ]); - } - - #[test] - fn test_rpath_relative() { - if cfg!(target_os = "macos") { - let config = &mut RPathConfig { - used_crates: Vec::new(), - has_rpath: true, - is_like_osx: true, - linker_is_gnu: false, - out_filename: PathBuf::from("bin/rustc"), - get_install_prefix_lib_path: &mut || panic!(), - }; - let res = get_rpath_relative_to_output(config, - Path::new("lib/libstd.so")); - assert_eq!(res, "@loader_path/../lib"); - } else { - let config = &mut RPathConfig { - used_crates: Vec::new(), - out_filename: PathBuf::from("bin/rustc"), - get_install_prefix_lib_path: &mut || panic!(), - has_rpath: true, - is_like_osx: false, - linker_is_gnu: true, - }; - let res = get_rpath_relative_to_output(config, - Path::new("lib/libstd.so")); - assert_eq!(res, "$ORIGIN/../lib"); - } - } - - #[test] - fn test_xlinker() { - let args = rpaths_to_flags(&[ - "a/normal/path".to_string(), - "a,comma,path".to_string() - ]); - - assert_eq!(args, vec![ - "-Wl,-rpath,a/normal/path".to_string(), - "-Wl,-rpath".to_string(), - "-Xlinker".to_string(), - "a,comma,path".to_string() - ]); - } -} diff --git a/src/librustc_trans/back/symbol_export.rs b/src/librustc_trans/back/symbol_export.rs deleted file mode 100644 index d7785522069..00000000000 --- a/src/librustc_trans/back/symbol_export.rs +++ /dev/null @@ -1,395 +0,0 @@ -// Copyright 2016 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -use rustc_data_structures::sync::Lrc; -use std::sync::Arc; - -use monomorphize::Instance; -use rustc::hir; -use rustc::hir::TransFnAttrFlags; -use rustc::hir::def_id::{CrateNum, DefId, LOCAL_CRATE, CRATE_DEF_INDEX}; -use rustc::ich::Fingerprint; -use rustc::middle::exported_symbols::{SymbolExportLevel, ExportedSymbol, metadata_symbol_name}; -use rustc::session::config; -use rustc::ty::{TyCtxt, SymbolName}; -use rustc::ty::maps::Providers; -use rustc::ty::subst::Substs; -use rustc::util::nodemap::{FxHashMap, DefIdMap}; -use rustc_allocator::ALLOCATOR_METHODS; -use rustc_data_structures::indexed_vec::IndexVec; -use std::collections::hash_map::Entry::*; - -pub type ExportedSymbols = FxHashMap< - CrateNum, - Arc>, ->; - -pub fn threshold(tcx: TyCtxt) -> SymbolExportLevel { - crates_export_threshold(&tcx.sess.crate_types.borrow()) -} - -fn crate_export_threshold(crate_type: config::CrateType) -> SymbolExportLevel { - match crate_type { - config::CrateTypeExecutable | - config::CrateTypeStaticlib | - config::CrateTypeProcMacro | - config::CrateTypeCdylib => SymbolExportLevel::C, - config::CrateTypeRlib | - config::CrateTypeDylib => SymbolExportLevel::Rust, - } -} - -pub fn crates_export_threshold(crate_types: &[config::CrateType]) - -> SymbolExportLevel { - if crate_types.iter().any(|&crate_type| { - crate_export_threshold(crate_type) == SymbolExportLevel::Rust - }) { - SymbolExportLevel::Rust - } else { - SymbolExportLevel::C - } -} - -fn reachable_non_generics_provider<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, - cnum: CrateNum) - -> Lrc> -{ - assert_eq!(cnum, LOCAL_CRATE); - - if !tcx.sess.opts.output_types.should_trans() { - return Lrc::new(DefIdMap()) - } - - // Check to see if this crate is a "special runtime crate". These - // crates, implementation details of the standard library, typically - // have a bunch of `pub extern` and `#[no_mangle]` functions as the - // ABI between them. We don't want their symbols to have a `C` - // export level, however, as they're just implementation details. - // Down below we'll hardwire all of the symbols to the `Rust` export - // level instead. - let special_runtime_crate = tcx.is_panic_runtime(LOCAL_CRATE) || - tcx.is_compiler_builtins(LOCAL_CRATE); - - let mut reachable_non_generics: DefIdMap<_> = tcx.reachable_set(LOCAL_CRATE).0 - .iter() - .filter_map(|&node_id| { - // We want to ignore some FFI functions that are not exposed from - // this crate. Reachable FFI functions can be lumped into two - // categories: - // - // 1. Those that are included statically via a static library - // 2. Those included otherwise (e.g. dynamically or via a framework) - // - // Although our LLVM module is not literally emitting code for the - // statically included symbols, it's an export of our library which - // needs to be passed on to the linker and encoded in the metadata. - // - // As a result, if this id is an FFI item (foreign item) then we only - // let it through if it's included statically. - match tcx.hir.get(node_id) { - hir::map::NodeForeignItem(..) => { - let def_id = tcx.hir.local_def_id(node_id); - if tcx.is_statically_included_foreign_item(def_id) { - Some(def_id) - } else { - None - } - } - - // Only consider nodes that actually have exported symbols. - hir::map::NodeItem(&hir::Item { - node: hir::ItemStatic(..), - .. - }) | - hir::map::NodeItem(&hir::Item { - node: hir::ItemFn(..), .. - }) | - hir::map::NodeImplItem(&hir::ImplItem { - node: hir::ImplItemKind::Method(..), - .. - }) => { - let def_id = tcx.hir.local_def_id(node_id); - let generics = tcx.generics_of(def_id); - if !generics.requires_monomorphization(tcx) && - // Functions marked with #[inline] are only ever translated - // with "internal" linkage and are never exported. - !Instance::mono(tcx, def_id).def.requires_local(tcx) { - Some(def_id) - } else { - None - } - } - - _ => None - } - }) - .map(|def_id| { - let export_level = if special_runtime_crate { - let name = tcx.symbol_name(Instance::mono(tcx, def_id)).as_str(); - // We can probably do better here by just ensuring that - // it has hidden visibility rather than public - // visibility, as this is primarily here to ensure it's - // not stripped during LTO. - // - // In general though we won't link right if these - // symbols are stripped, and LTO currently strips them. - if &*name == "rust_eh_personality" || - &*name == "rust_eh_register_frames" || - &*name == "rust_eh_unregister_frames" { - SymbolExportLevel::C - } else { - SymbolExportLevel::Rust - } - } else { - symbol_export_level(tcx, def_id) - }; - debug!("EXPORTED SYMBOL (local): {} ({:?})", - tcx.symbol_name(Instance::mono(tcx, def_id)), - export_level); - (def_id, export_level) - }) - .collect(); - - if let Some(id) = *tcx.sess.derive_registrar_fn.get() { - let def_id = tcx.hir.local_def_id(id); - reachable_non_generics.insert(def_id, SymbolExportLevel::C); - } - - if let Some(id) = *tcx.sess.plugin_registrar_fn.get() { - let def_id = tcx.hir.local_def_id(id); - reachable_non_generics.insert(def_id, SymbolExportLevel::C); - } - - Lrc::new(reachable_non_generics) -} - -fn is_reachable_non_generic_provider_local<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, - def_id: DefId) - -> bool { - let export_threshold = threshold(tcx); - - if let Some(&level) = tcx.reachable_non_generics(def_id.krate).get(&def_id) { - level.is_below_threshold(export_threshold) - } else { - false - } -} - -fn is_reachable_non_generic_provider_extern<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, - def_id: DefId) - -> bool { - tcx.reachable_non_generics(def_id.krate).contains_key(&def_id) -} - -fn exported_symbols_provider_local<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, - cnum: CrateNum) - -> Arc, - SymbolExportLevel)>> -{ - assert_eq!(cnum, LOCAL_CRATE); - - if !tcx.sess.opts.output_types.should_trans() { - return Arc::new(vec![]) - } - - let mut symbols: Vec<_> = tcx.reachable_non_generics(LOCAL_CRATE) - .iter() - .map(|(&def_id, &level)| { - (ExportedSymbol::NonGeneric(def_id), level) - }) - .collect(); - - if let Some(_) = *tcx.sess.entry_fn.borrow() { - let symbol_name = "main".to_string(); - let exported_symbol = ExportedSymbol::NoDefId(SymbolName::new(&symbol_name)); - - symbols.push((exported_symbol, SymbolExportLevel::C)); - } - - if tcx.sess.allocator_kind.get().is_some() { - for method in ALLOCATOR_METHODS { - let symbol_name = format!("__rust_{}", method.name); - let exported_symbol = ExportedSymbol::NoDefId(SymbolName::new(&symbol_name)); - - symbols.push((exported_symbol, SymbolExportLevel::Rust)); - } - } - - if tcx.sess.opts.debugging_opts.pgo_gen.is_some() { - // These are weak symbols that point to the profile version and the - // profile name, which need to be treated as exported so LTO doesn't nix - // them. - const PROFILER_WEAK_SYMBOLS: [&'static str; 2] = [ - "__llvm_profile_raw_version", - "__llvm_profile_filename", - ]; - for sym in &PROFILER_WEAK_SYMBOLS { - let exported_symbol = ExportedSymbol::NoDefId(SymbolName::new(sym)); - symbols.push((exported_symbol, SymbolExportLevel::C)); - } - } - - if tcx.sess.crate_types.borrow().contains(&config::CrateTypeDylib) { - let symbol_name = metadata_symbol_name(tcx); - let exported_symbol = ExportedSymbol::NoDefId(SymbolName::new(&symbol_name)); - - symbols.push((exported_symbol, SymbolExportLevel::Rust)); - } - - if tcx.share_generics() && tcx.local_crate_exports_generics() { - use rustc::mir::mono::{Linkage, Visibility, MonoItem}; - use rustc::ty::InstanceDef; - - // Normally, we require that shared monomorphizations are not hidden, - // because if we want to re-use a monomorphization from a Rust dylib, it - // needs to be exported. - // However, on platforms that don't allow for Rust dylibs, having - // external linkage is enough for monomorphization to be linked to. - let need_visibility = tcx.sess.target.target.options.dynamic_linking && - !tcx.sess.target.target.options.only_cdylib; - - let (_, cgus) = tcx.collect_and_partition_translation_items(LOCAL_CRATE); - - for (mono_item, &(linkage, visibility)) in cgus.iter() - .flat_map(|cgu| cgu.items().iter()) { - if linkage != Linkage::External { - // We can only re-use things with external linkage, otherwise - // we'll get a linker error - continue - } - - if need_visibility && visibility == Visibility::Hidden { - // If we potentially share things from Rust dylibs, they must - // not be hidden - continue - } - - if let &MonoItem::Fn(Instance { - def: InstanceDef::Item(def_id), - substs, - }) = mono_item { - if substs.types().next().is_some() { - symbols.push((ExportedSymbol::Generic(def_id, substs), - SymbolExportLevel::Rust)); - } - } - } - } - - // Sort so we get a stable incr. comp. hash. - symbols.sort_unstable_by(|&(ref symbol1, ..), &(ref symbol2, ..)| { - symbol1.compare_stable(tcx, symbol2) - }); - - Arc::new(symbols) -} - -fn upstream_monomorphizations_provider<'a, 'tcx>( - tcx: TyCtxt<'a, 'tcx, 'tcx>, - cnum: CrateNum) - -> Lrc, CrateNum>>>> -{ - debug_assert!(cnum == LOCAL_CRATE); - - let cnums = tcx.all_crate_nums(LOCAL_CRATE); - - let mut instances = DefIdMap(); - - let cnum_stable_ids: IndexVec = { - let mut cnum_stable_ids = IndexVec::from_elem_n(Fingerprint::ZERO, - cnums.len() + 1); - - for &cnum in cnums.iter() { - cnum_stable_ids[cnum] = tcx.def_path_hash(DefId { - krate: cnum, - index: CRATE_DEF_INDEX, - }).0; - } - - cnum_stable_ids - }; - - for &cnum in cnums.iter() { - for &(ref exported_symbol, _) in tcx.exported_symbols(cnum).iter() { - if let &ExportedSymbol::Generic(def_id, substs) = exported_symbol { - let substs_map = instances.entry(def_id) - .or_insert_with(|| FxHashMap()); - - match substs_map.entry(substs) { - Occupied(mut e) => { - // If there are multiple monomorphizations available, - // we select one deterministically. - let other_cnum = *e.get(); - if cnum_stable_ids[other_cnum] > cnum_stable_ids[cnum] { - e.insert(cnum); - } - } - Vacant(e) => { - e.insert(cnum); - } - } - } - } - } - - Lrc::new(instances.into_iter() - .map(|(key, value)| (key, Lrc::new(value))) - .collect()) -} - -fn upstream_monomorphizations_for_provider<'a, 'tcx>( - tcx: TyCtxt<'a, 'tcx, 'tcx>, - def_id: DefId) - -> Option, CrateNum>>> -{ - debug_assert!(!def_id.is_local()); - tcx.upstream_monomorphizations(LOCAL_CRATE) - .get(&def_id) - .cloned() -} - -fn is_unreachable_local_definition_provider(tcx: TyCtxt, def_id: DefId) -> bool { - if let Some(node_id) = tcx.hir.as_local_node_id(def_id) { - !tcx.reachable_set(LOCAL_CRATE).0.contains(&node_id) - } else { - bug!("is_unreachable_local_definition called with non-local DefId: {:?}", - def_id) - } -} - -pub fn provide(providers: &mut Providers) { - providers.reachable_non_generics = reachable_non_generics_provider; - providers.is_reachable_non_generic = is_reachable_non_generic_provider_local; - providers.exported_symbols = exported_symbols_provider_local; - providers.upstream_monomorphizations = upstream_monomorphizations_provider; - providers.is_unreachable_local_definition = is_unreachable_local_definition_provider; -} - -pub fn provide_extern(providers: &mut Providers) { - providers.is_reachable_non_generic = is_reachable_non_generic_provider_extern; - providers.upstream_monomorphizations_for = upstream_monomorphizations_for_provider; -} - -fn symbol_export_level(tcx: TyCtxt, sym_def_id: DefId) -> SymbolExportLevel { - // We export anything that's not mangled at the "C" layer as it probably has - // to do with ABI concerns. We do not, however, apply such treatment to - // special symbols in the standard library for various plumbing between - // core/std/allocators/etc. For example symbols used to hook up allocation - // are not considered for export - let trans_fn_attrs = tcx.trans_fn_attrs(sym_def_id); - let is_extern = trans_fn_attrs.contains_extern_indicator(); - let std_internal = trans_fn_attrs.flags.contains(TransFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL); - - if is_extern && !std_internal { - SymbolExportLevel::C - } else { - SymbolExportLevel::Rust - } -} diff --git a/src/librustc_trans/back/wasm.rs b/src/librustc_trans/back/wasm.rs deleted file mode 100644 index d6d386c9fbe..00000000000 --- a/src/librustc_trans/back/wasm.rs +++ /dev/null @@ -1,261 +0,0 @@ -// Copyright 2018 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -use std::collections::BTreeMap; -use std::fs; -use std::path::Path; -use std::str; - -use rustc_data_structures::fx::FxHashMap; -use serialize::leb128; - -// https://webassembly.github.io/spec/core/binary/modules.html#binary-importsec -const WASM_IMPORT_SECTION_ID: u8 = 2; - -const WASM_EXTERNAL_KIND_FUNCTION: u8 = 0; -const WASM_EXTERNAL_KIND_TABLE: u8 = 1; -const WASM_EXTERNAL_KIND_MEMORY: u8 = 2; -const WASM_EXTERNAL_KIND_GLOBAL: u8 = 3; - -/// Append all the custom sections listed in `sections` to the wasm binary -/// specified at `path`. -/// -/// LLVM 6 which we're using right now doesn't have the ability to create custom -/// sections in wasm files nor does LLD have the ability to merge these sections -/// into one larger section when linking. It's expected that this will -/// eventually get implemented, however! -/// -/// Until that time though this is a custom implementation in rustc to append -/// all sections to a wasm file to the finished product that LLD produces. -/// -/// Support for this is landing in LLVM in https://reviews.llvm.org/D43097, -/// although after that support will need to be in LLD as well. -pub fn add_custom_sections(path: &Path, sections: &BTreeMap>) { - if sections.len() == 0 { - return - } - - let wasm = fs::read(path).expect("failed to read wasm output"); - - // see https://webassembly.github.io/spec/core/binary/modules.html#custom-section - let mut wasm = WasmEncoder { data: wasm }; - for (section, bytes) in sections { - // write the `id` identifier, 0 for a custom section - wasm.byte(0); - - // figure out how long our name descriptor will be - let mut name = WasmEncoder::new(); - name.str(section); - - // write the length of the payload followed by all its contents - wasm.u32((bytes.len() + name.data.len()) as u32); - wasm.data.extend_from_slice(&name.data); - wasm.data.extend_from_slice(bytes); - } - - fs::write(path, &wasm.data).expect("failed to write wasm output"); -} - -/// Rewrite the module imports are listed from in a wasm module given the field -/// name to module name mapping in `import_map`. -/// -/// LLVM 6 which we're using right now doesn't have the ability to configure the -/// module a wasm symbol is import from. Rather all imported symbols come from -/// the bland `"env"` module unconditionally. Furthermore we'd *also* need -/// support in LLD for preserving these import modules, which it unfortunately -/// currently does not. -/// -/// This function is intended as a hack for now where we manually rewrite the -/// wasm output by LLVM to have the correct import modules listed. The -/// `#[wasm_import_module]` attribute in Rust translates to the module that each -/// symbol is imported from, so here we manually go through the wasm file, -/// decode it, rewrite imports, and then rewrite the wasm module. -/// -/// Support for this was added to LLVM in -/// https://github.com/llvm-mirror/llvm/commit/0f32e1365, although support still -/// needs to be added (AFAIK at the time of this writing) to LLD -pub fn rewrite_imports(path: &Path, import_map: &FxHashMap) { - if import_map.len() == 0 { - return - } - - let wasm = fs::read(path).expect("failed to read wasm output"); - let mut ret = WasmEncoder::new(); - ret.data.extend(&wasm[..8]); - - // skip the 8 byte wasm/version header - for (id, raw) in WasmSections(WasmDecoder::new(&wasm[8..])) { - ret.byte(id); - if id == WASM_IMPORT_SECTION_ID { - info!("rewriting import section"); - let data = rewrite_import_section( - &mut WasmDecoder::new(raw), - import_map, - ); - ret.bytes(&data); - } else { - info!("carry forward section {}, {} bytes long", id, raw.len()); - ret.bytes(raw); - } - } - - fs::write(path, &ret.data).expect("failed to write wasm output"); - - fn rewrite_import_section( - wasm: &mut WasmDecoder, - import_map: &FxHashMap, - ) - -> Vec - { - let mut dst = WasmEncoder::new(); - let n = wasm.u32(); - dst.u32(n); - info!("rewriting {} imports", n); - for _ in 0..n { - rewrite_import_entry(wasm, &mut dst, import_map); - } - return dst.data - } - - fn rewrite_import_entry(wasm: &mut WasmDecoder, - dst: &mut WasmEncoder, - import_map: &FxHashMap) { - // More info about the binary format here is available at: - // https://webassembly.github.io/spec/core/binary/modules.html#import-section - // - // Note that you can also find the whole point of existence of this - // function here, where we map the `module` name to a different one if - // we've got one listed. - let module = wasm.str(); - let field = wasm.str(); - let new_module = if module == "env" { - import_map.get(field).map(|s| &**s).unwrap_or(module) - } else { - module - }; - info!("import rewrite ({} => {}) / {}", module, new_module, field); - dst.str(new_module); - dst.str(field); - let kind = wasm.byte(); - dst.byte(kind); - match kind { - WASM_EXTERNAL_KIND_FUNCTION => dst.u32(wasm.u32()), - WASM_EXTERNAL_KIND_TABLE => { - dst.byte(wasm.byte()); // element_type - dst.limits(wasm.limits()); - } - WASM_EXTERNAL_KIND_MEMORY => dst.limits(wasm.limits()), - WASM_EXTERNAL_KIND_GLOBAL => { - dst.byte(wasm.byte()); // content_type - dst.bool(wasm.bool()); // mutable - } - b => panic!("unknown kind: {}", b), - } - } -} - -struct WasmSections<'a>(WasmDecoder<'a>); - -impl<'a> Iterator for WasmSections<'a> { - type Item = (u8, &'a [u8]); - - fn next(&mut self) -> Option<(u8, &'a [u8])> { - if self.0.data.len() == 0 { - return None - } - - // see https://webassembly.github.io/spec/core/binary/modules.html#sections - let id = self.0.byte(); - let section_len = self.0.u32(); - info!("new section {} / {} bytes", id, section_len); - let section = self.0.skip(section_len as usize); - Some((id, section)) - } -} - -struct WasmDecoder<'a> { - data: &'a [u8], -} - -impl<'a> WasmDecoder<'a> { - fn new(data: &'a [u8]) -> WasmDecoder<'a> { - WasmDecoder { data } - } - - fn byte(&mut self) -> u8 { - self.skip(1)[0] - } - - fn u32(&mut self) -> u32 { - let (n, l1) = leb128::read_u32_leb128(self.data); - self.data = &self.data[l1..]; - return n - } - - fn skip(&mut self, amt: usize) -> &'a [u8] { - let (data, rest) = self.data.split_at(amt); - self.data = rest; - data - } - - fn str(&mut self) -> &'a str { - let len = self.u32(); - str::from_utf8(self.skip(len as usize)).unwrap() - } - - fn bool(&mut self) -> bool { - self.byte() == 1 - } - - fn limits(&mut self) -> (u32, Option) { - let has_max = self.bool(); - (self.u32(), if has_max { Some(self.u32()) } else { None }) - } -} - -struct WasmEncoder { - data: Vec, -} - -impl WasmEncoder { - fn new() -> WasmEncoder { - WasmEncoder { data: Vec::new() } - } - - fn u32(&mut self, val: u32) { - let at = self.data.len(); - leb128::write_u32_leb128(&mut self.data, at, val); - } - - fn byte(&mut self, val: u8) { - self.data.push(val); - } - - fn bytes(&mut self, val: &[u8]) { - self.u32(val.len() as u32); - self.data.extend_from_slice(val); - } - - fn str(&mut self, val: &str) { - self.bytes(val.as_bytes()) - } - - fn bool(&mut self, b: bool) { - self.byte(b as u8); - } - - fn limits(&mut self, limits: (u32, Option)) { - self.bool(limits.1.is_some()); - self.u32(limits.0); - if let Some(c) = limits.1 { - self.u32(c); - } - } -} diff --git a/src/librustc_trans/back/write.rs b/src/librustc_trans/back/write.rs deleted file mode 100644 index acfe7c33028..00000000000 --- a/src/librustc_trans/back/write.rs +++ /dev/null @@ -1,2392 +0,0 @@ -// Copyright 2013-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. - -use attributes; -use back::bytecode::{self, RLIB_BYTECODE_EXTENSION}; -use back::lto::{self, ModuleBuffer, ThinBuffer}; -use back::link::{self, get_linker, remove}; -use back::command::Command; -use back::linker::LinkerInfo; -use back::symbol_export::ExportedSymbols; -use base; -use consts; -use rustc_incremental::{copy_cgu_workproducts_to_incr_comp_cache_dir, in_incr_comp_dir}; -use rustc::dep_graph::{WorkProduct, WorkProductId, WorkProductFileKind}; -use rustc::middle::cstore::{LinkMeta, EncodedMetadata}; -use rustc::session::config::{self, OutputFilenames, OutputType, Passes, SomePasses, - AllPasses, Sanitizer, Lto}; -use rustc::session::Session; -use rustc::util::nodemap::FxHashMap; -use time_graph::{self, TimeGraph, Timeline}; -use llvm; -use llvm::{ModuleRef, TargetMachineRef, PassManagerRef, DiagnosticInfoRef}; -use llvm::{SMDiagnosticRef, ContextRef}; -use {CrateTranslation, ModuleSource, ModuleTranslation, CompiledModule, ModuleKind}; -use CrateInfo; -use rustc::hir::def_id::{CrateNum, LOCAL_CRATE}; -use rustc::ty::TyCtxt; -use rustc::util::common::{time_ext, time_depth, set_time_depth, print_time_passes_entry}; -use rustc::util::common::path2cstr; -use rustc::util::fs::{link_or_copy}; -use errors::{self, Handler, Level, DiagnosticBuilder, FatalError, DiagnosticId}; -use errors::emitter::{Emitter}; -use syntax::attr; -use syntax::ext::hygiene::Mark; -use syntax_pos::MultiSpan; -use syntax_pos::symbol::Symbol; -use type_::Type; -use context::{is_pie_binary, get_reloc_model}; -use common::{C_bytes_in_context, val_ty}; -use jobserver::{Client, Acquired}; -use rustc_demangle; - -use std::any::Any; -use std::ffi::{CString, CStr}; -use std::fs; -use std::io::{self, Write}; -use std::mem; -use std::path::{Path, PathBuf}; -use std::str; -use std::sync::Arc; -use std::sync::mpsc::{channel, Sender, Receiver}; -use std::slice; -use std::time::Instant; -use std::thread; -use libc::{c_uint, c_void, c_char, size_t}; - -pub const RELOC_MODEL_ARGS : [(&'static str, llvm::RelocMode); 7] = [ - ("pic", llvm::RelocMode::PIC), - ("static", llvm::RelocMode::Static), - ("default", llvm::RelocMode::Default), - ("dynamic-no-pic", llvm::RelocMode::DynamicNoPic), - ("ropi", llvm::RelocMode::ROPI), - ("rwpi", llvm::RelocMode::RWPI), - ("ropi-rwpi", llvm::RelocMode::ROPI_RWPI), -]; - -pub const CODE_GEN_MODEL_ARGS: &[(&str, llvm::CodeModel)] = &[ - ("small", llvm::CodeModel::Small), - ("kernel", llvm::CodeModel::Kernel), - ("medium", llvm::CodeModel::Medium), - ("large", llvm::CodeModel::Large), -]; - -pub const TLS_MODEL_ARGS : [(&'static str, llvm::ThreadLocalMode); 4] = [ - ("global-dynamic", llvm::ThreadLocalMode::GeneralDynamic), - ("local-dynamic", llvm::ThreadLocalMode::LocalDynamic), - ("initial-exec", llvm::ThreadLocalMode::InitialExec), - ("local-exec", llvm::ThreadLocalMode::LocalExec), -]; - -pub fn llvm_err(handler: &errors::Handler, msg: String) -> FatalError { - match llvm::last_error() { - Some(err) => handler.fatal(&format!("{}: {}", msg, err)), - None => handler.fatal(&msg), - } -} - -pub fn write_output_file( - handler: &errors::Handler, - target: llvm::TargetMachineRef, - pm: llvm::PassManagerRef, - m: ModuleRef, - output: &Path, - file_type: llvm::FileType) -> Result<(), FatalError> { - unsafe { - let output_c = path2cstr(output); - let result = llvm::LLVMRustWriteOutputFile( - target, pm, m, output_c.as_ptr(), file_type); - if result.into_result().is_err() { - let msg = format!("could not write output to {}", output.display()); - Err(llvm_err(handler, msg)) - } else { - Ok(()) - } - } -} - -fn get_llvm_opt_level(optimize: config::OptLevel) -> llvm::CodeGenOptLevel { - match optimize { - config::OptLevel::No => llvm::CodeGenOptLevel::None, - config::OptLevel::Less => llvm::CodeGenOptLevel::Less, - config::OptLevel::Default => llvm::CodeGenOptLevel::Default, - config::OptLevel::Aggressive => llvm::CodeGenOptLevel::Aggressive, - _ => llvm::CodeGenOptLevel::Default, - } -} - -fn get_llvm_opt_size(optimize: config::OptLevel) -> llvm::CodeGenOptSize { - match optimize { - config::OptLevel::Size => llvm::CodeGenOptSizeDefault, - config::OptLevel::SizeMin => llvm::CodeGenOptSizeAggressive, - _ => llvm::CodeGenOptSizeNone, - } -} - -pub fn create_target_machine(sess: &Session, find_features: bool) -> TargetMachineRef { - target_machine_factory(sess, find_features)().unwrap_or_else(|err| { - llvm_err(sess.diagnostic(), err).raise() - }) -} - -// If find_features is true this won't access `sess.crate_types` by assuming -// that `is_pie_binary` is false. When we discover LLVM target features -// `sess.crate_types` is uninitialized so we cannot access it. -pub fn target_machine_factory(sess: &Session, find_features: bool) - -> Arc Result + Send + Sync> -{ - let reloc_model = get_reloc_model(sess); - - let opt_level = get_llvm_opt_level(sess.opts.optimize); - let use_softfp = sess.opts.cg.soft_float; - - let ffunction_sections = sess.target.target.options.function_sections; - let fdata_sections = ffunction_sections; - - let code_model_arg = sess.opts.cg.code_model.as_ref().or( - sess.target.target.options.code_model.as_ref(), - ); - - let code_model = match code_model_arg { - Some(s) => { - match CODE_GEN_MODEL_ARGS.iter().find(|arg| arg.0 == s) { - Some(x) => x.1, - _ => { - sess.err(&format!("{:?} is not a valid code model", - code_model_arg)); - sess.abort_if_errors(); - bug!(); - } - } - } - None => llvm::CodeModel::None, - }; - - let singlethread = sess.target.target.options.singlethread; - - let triple = &sess.target.target.llvm_target; - - let triple = CString::new(triple.as_bytes()).unwrap(); - let cpu = sess.target_cpu(); - let cpu = CString::new(cpu.as_bytes()).unwrap(); - let features = attributes::llvm_target_features(sess) - .collect::>() - .join(","); - let features = CString::new(features).unwrap(); - let is_pie_binary = !find_features && is_pie_binary(sess); - let trap_unreachable = sess.target.target.options.trap_unreachable; - - Arc::new(move || { - let tm = unsafe { - llvm::LLVMRustCreateTargetMachine( - triple.as_ptr(), cpu.as_ptr(), features.as_ptr(), - code_model, - reloc_model, - opt_level, - use_softfp, - is_pie_binary, - ffunction_sections, - fdata_sections, - trap_unreachable, - singlethread, - ) - }; - - if tm.is_null() { - Err(format!("Could not create LLVM TargetMachine for triple: {}", - triple.to_str().unwrap())) - } else { - Ok(tm) - } - }) -} - -/// Module-specific configuration for `optimize_and_codegen`. -pub struct ModuleConfig { - /// Names of additional optimization passes to run. - passes: Vec, - /// Some(level) to optimize at a certain level, or None to run - /// absolutely no optimizations (used for the metadata module). - pub opt_level: Option, - - /// Some(level) to optimize binary size, or None to not affect program size. - opt_size: Option, - - pgo_gen: Option, - pgo_use: String, - - // Flags indicating which outputs to produce. - emit_no_opt_bc: bool, - emit_bc: bool, - emit_bc_compressed: bool, - emit_lto_bc: bool, - emit_ir: bool, - emit_asm: bool, - emit_obj: bool, - // Miscellaneous flags. These are mostly copied from command-line - // options. - no_verify: bool, - no_prepopulate_passes: bool, - no_builtins: bool, - time_passes: bool, - vectorize_loop: bool, - vectorize_slp: bool, - merge_functions: bool, - inline_threshold: Option, - // Instead of creating an object file by doing LLVM codegen, just - // make the object file bitcode. Provides easy compatibility with - // emscripten's ecc compiler, when used as the linker. - obj_is_bitcode: bool, - no_integrated_as: bool, - embed_bitcode: bool, - embed_bitcode_marker: bool, -} - -impl ModuleConfig { - fn new(passes: Vec) -> ModuleConfig { - ModuleConfig { - passes, - opt_level: None, - opt_size: None, - - pgo_gen: None, - pgo_use: String::new(), - - emit_no_opt_bc: false, - emit_bc: false, - emit_bc_compressed: false, - emit_lto_bc: false, - emit_ir: false, - emit_asm: false, - emit_obj: false, - obj_is_bitcode: false, - embed_bitcode: false, - embed_bitcode_marker: false, - no_integrated_as: false, - - no_verify: false, - no_prepopulate_passes: false, - no_builtins: false, - time_passes: false, - vectorize_loop: false, - vectorize_slp: false, - merge_functions: false, - inline_threshold: None - } - } - - fn set_flags(&mut self, sess: &Session, no_builtins: bool) { - self.no_verify = sess.no_verify(); - self.no_prepopulate_passes = sess.opts.cg.no_prepopulate_passes; - self.no_builtins = no_builtins || sess.target.target.options.no_builtins; - self.time_passes = sess.time_passes(); - self.inline_threshold = sess.opts.cg.inline_threshold; - self.obj_is_bitcode = sess.target.target.options.obj_is_bitcode; - let embed_bitcode = sess.target.target.options.embed_bitcode || - sess.opts.debugging_opts.embed_bitcode || - sess.opts.debugging_opts.cross_lang_lto.embed_bitcode(); - if embed_bitcode { - match sess.opts.optimize { - config::OptLevel::No | - config::OptLevel::Less => { - self.embed_bitcode_marker = embed_bitcode; - } - _ => self.embed_bitcode = embed_bitcode, - } - } - - // Copy what clang does by turning on loop vectorization at O2 and - // slp vectorization at O3. Otherwise configure other optimization aspects - // of this pass manager builder. - // Turn off vectorization for emscripten, as it's not very well supported. - self.vectorize_loop = !sess.opts.cg.no_vectorize_loops && - (sess.opts.optimize == config::OptLevel::Default || - sess.opts.optimize == config::OptLevel::Aggressive) && - !sess.target.target.options.is_like_emscripten; - - self.vectorize_slp = !sess.opts.cg.no_vectorize_slp && - sess.opts.optimize == config::OptLevel::Aggressive && - !sess.target.target.options.is_like_emscripten; - - self.merge_functions = sess.opts.optimize == config::OptLevel::Default || - sess.opts.optimize == config::OptLevel::Aggressive; - } -} - -/// Assembler name and command used by codegen when no_integrated_as is enabled -struct AssemblerCommand { - name: PathBuf, - cmd: Command, -} - -/// Additional resources used by optimize_and_codegen (not module specific) -#[derive(Clone)] -pub struct CodegenContext { - // Resouces needed when running LTO - pub time_passes: bool, - pub lto: Lto, - pub no_landing_pads: bool, - pub save_temps: bool, - pub fewer_names: bool, - pub exported_symbols: Option>, - pub opts: Arc, - pub crate_types: Vec, - pub each_linked_rlib_for_lto: Vec<(CrateNum, PathBuf)>, - output_filenames: Arc, - regular_module_config: Arc, - metadata_module_config: Arc, - allocator_module_config: Arc, - pub tm_factory: Arc Result + Send + Sync>, - pub msvc_imps_needed: bool, - pub target_pointer_width: String, - debuginfo: config::DebugInfoLevel, - - // Number of cgus excluding the allocator/metadata modules - pub total_cgus: usize, - // Handler to use for diagnostics produced during codegen. - pub diag_emitter: SharedEmitter, - // LLVM passes added by plugins. - pub plugin_passes: Vec, - // LLVM optimizations for which we want to print remarks. - pub remark: Passes, - // Worker thread number - pub worker: usize, - // The incremental compilation session directory, or None if we are not - // compiling incrementally - pub incr_comp_session_dir: Option, - // Channel back to the main control thread to send messages to - coordinator_send: Sender>, - // A reference to the TimeGraph so we can register timings. None means that - // measuring is disabled. - time_graph: Option, - // The assembler command if no_integrated_as option is enabled, None otherwise - assembler_cmd: Option>, -} - -impl CodegenContext { - pub fn create_diag_handler(&self) -> Handler { - Handler::with_emitter(true, false, Box::new(self.diag_emitter.clone())) - } - - pub(crate) fn config(&self, kind: ModuleKind) -> &ModuleConfig { - match kind { - ModuleKind::Regular => &self.regular_module_config, - ModuleKind::Metadata => &self.metadata_module_config, - ModuleKind::Allocator => &self.allocator_module_config, - } - } - - pub(crate) fn save_temp_bitcode(&self, trans: &ModuleTranslation, name: &str) { - if !self.save_temps { - return - } - unsafe { - let ext = format!("{}.bc", name); - let cgu = Some(&trans.name[..]); - let path = self.output_filenames.temp_path_ext(&ext, cgu); - let cstr = path2cstr(&path); - let llmod = trans.llvm().unwrap().llmod; - llvm::LLVMWriteBitcodeToFile(llmod, cstr.as_ptr()); - } - } -} - -struct DiagnosticHandlers<'a> { - inner: Box<(&'a CodegenContext, &'a Handler)>, - llcx: ContextRef, -} - -impl<'a> DiagnosticHandlers<'a> { - fn new(cgcx: &'a CodegenContext, - handler: &'a Handler, - llcx: ContextRef) -> DiagnosticHandlers<'a> { - let data = Box::new((cgcx, handler)); - unsafe { - let arg = &*data as &(_, _) as *const _ as *mut _; - llvm::LLVMRustSetInlineAsmDiagnosticHandler(llcx, inline_asm_handler, arg); - llvm::LLVMContextSetDiagnosticHandler(llcx, diagnostic_handler, arg); - } - DiagnosticHandlers { - inner: data, - llcx: llcx, - } - } -} - -impl<'a> Drop for DiagnosticHandlers<'a> { - fn drop(&mut self) { - unsafe { - llvm::LLVMRustSetInlineAsmDiagnosticHandler(self.llcx, inline_asm_handler, 0 as *mut _); - llvm::LLVMContextSetDiagnosticHandler(self.llcx, diagnostic_handler, 0 as *mut _); - } - } -} - -unsafe extern "C" fn report_inline_asm<'a, 'b>(cgcx: &'a CodegenContext, - msg: &'b str, - cookie: c_uint) { - cgcx.diag_emitter.inline_asm_error(cookie as u32, msg.to_string()); -} - -unsafe extern "C" fn inline_asm_handler(diag: SMDiagnosticRef, - user: *const c_void, - cookie: c_uint) { - if user.is_null() { - return - } - let (cgcx, _) = *(user as *const (&CodegenContext, &Handler)); - - let msg = llvm::build_string(|s| llvm::LLVMRustWriteSMDiagnosticToString(diag, s)) - .expect("non-UTF8 SMDiagnostic"); - - report_inline_asm(cgcx, &msg, cookie); -} - -unsafe extern "C" fn diagnostic_handler(info: DiagnosticInfoRef, user: *mut c_void) { - if user.is_null() { - return - } - let (cgcx, diag_handler) = *(user as *const (&CodegenContext, &Handler)); - - match llvm::diagnostic::Diagnostic::unpack(info) { - llvm::diagnostic::InlineAsm(inline) => { - report_inline_asm(cgcx, - &llvm::twine_to_string(inline.message), - inline.cookie); - } - - llvm::diagnostic::Optimization(opt) => { - let enabled = match cgcx.remark { - AllPasses => true, - SomePasses(ref v) => v.iter().any(|s| *s == opt.pass_name), - }; - - if enabled { - diag_handler.note_without_error(&format!("optimization {} for {} at {}:{}:{}: {}", - opt.kind.describe(), - opt.pass_name, - opt.filename, - opt.line, - opt.column, - opt.message)); - } - } - llvm::diagnostic::PGO(diagnostic_ref) => { - let msg = llvm::build_string(|s| { - llvm::LLVMRustWriteDiagnosticInfoToString(diagnostic_ref, s) - }).expect("non-UTF8 PGO diagnostic"); - diag_handler.warn(&msg); - } - llvm::diagnostic::UnknownDiagnostic(..) => {}, - } -} - -// Unsafe due to LLVM calls. -unsafe fn optimize(cgcx: &CodegenContext, - diag_handler: &Handler, - mtrans: &ModuleTranslation, - config: &ModuleConfig, - timeline: &mut Timeline) - -> Result<(), FatalError> -{ - let (llmod, llcx, tm) = match mtrans.source { - ModuleSource::Translated(ref llvm) => (llvm.llmod, llvm.llcx, llvm.tm), - ModuleSource::Preexisting(_) => { - bug!("optimize_and_codegen: called with ModuleSource::Preexisting") - } - }; - - let _handlers = DiagnosticHandlers::new(cgcx, diag_handler, llcx); - - let module_name = mtrans.name.clone(); - let module_name = Some(&module_name[..]); - - if config.emit_no_opt_bc { - let out = cgcx.output_filenames.temp_path_ext("no-opt.bc", module_name); - let out = path2cstr(&out); - llvm::LLVMWriteBitcodeToFile(llmod, out.as_ptr()); - } - - if config.opt_level.is_some() { - // Create the two optimizing pass managers. These mirror what clang - // does, and are by populated by LLVM's default PassManagerBuilder. - // Each manager has a different set of passes, but they also share - // some common passes. - let fpm = llvm::LLVMCreateFunctionPassManagerForModule(llmod); - let mpm = llvm::LLVMCreatePassManager(); - - // If we're verifying or linting, add them to the function pass - // manager. - let addpass = |pass_name: &str| { - let pass_name = CString::new(pass_name).unwrap(); - let pass = llvm::LLVMRustFindAndCreatePass(pass_name.as_ptr()); - if pass.is_null() { - return false; - } - let pass_manager = match llvm::LLVMRustPassKind(pass) { - llvm::PassKind::Function => fpm, - llvm::PassKind::Module => mpm, - llvm::PassKind::Other => { - diag_handler.err("Encountered LLVM pass kind we can't handle"); - return true - }, - }; - llvm::LLVMRustAddPass(pass_manager, pass); - true - }; - - if !config.no_verify { assert!(addpass("verify")); } - if !config.no_prepopulate_passes { - llvm::LLVMRustAddAnalysisPasses(tm, fpm, llmod); - llvm::LLVMRustAddAnalysisPasses(tm, mpm, llmod); - let opt_level = config.opt_level.unwrap_or(llvm::CodeGenOptLevel::None); - let prepare_for_thin_lto = cgcx.lto == Lto::Thin || cgcx.lto == Lto::ThinLocal; - with_llvm_pmb(llmod, &config, opt_level, prepare_for_thin_lto, &mut |b| { - llvm::LLVMPassManagerBuilderPopulateFunctionPassManager(b, fpm); - llvm::LLVMPassManagerBuilderPopulateModulePassManager(b, mpm); - }) - } - - for pass in &config.passes { - if !addpass(pass) { - diag_handler.warn(&format!("unknown pass `{}`, ignoring", - pass)); - } - } - - for pass in &cgcx.plugin_passes { - if !addpass(pass) { - diag_handler.err(&format!("a plugin asked for LLVM pass \ - `{}` but LLVM does not \ - recognize it", pass)); - } - } - - diag_handler.abort_if_errors(); - - // Finally, run the actual optimization passes - time_ext(config.time_passes, - None, - &format!("llvm function passes [{}]", module_name.unwrap()), - || { - llvm::LLVMRustRunFunctionPassManager(fpm, llmod) - }); - timeline.record("fpm"); - time_ext(config.time_passes, - None, - &format!("llvm module passes [{}]", module_name.unwrap()), - || { - llvm::LLVMRunPassManager(mpm, llmod) - }); - - // Deallocate managers that we're now done with - llvm::LLVMDisposePassManager(fpm); - llvm::LLVMDisposePassManager(mpm); - } - Ok(()) -} - -fn generate_lto_work(cgcx: &CodegenContext, - modules: Vec) - -> Vec<(WorkItem, u64)> -{ - let mut timeline = cgcx.time_graph.as_ref().map(|tg| { - tg.start(TRANS_WORKER_TIMELINE, - TRANS_WORK_PACKAGE_KIND, - "generate lto") - }).unwrap_or(Timeline::noop()); - let lto_modules = lto::run(cgcx, modules, &mut timeline) - .unwrap_or_else(|e| e.raise()); - - lto_modules.into_iter().map(|module| { - let cost = module.cost(); - (WorkItem::LTO(module), cost) - }).collect() -} - -unsafe fn codegen(cgcx: &CodegenContext, - diag_handler: &Handler, - mtrans: ModuleTranslation, - config: &ModuleConfig, - timeline: &mut Timeline) - -> Result -{ - timeline.record("codegen"); - let (llmod, llcx, tm) = match mtrans.source { - ModuleSource::Translated(ref llvm) => (llvm.llmod, llvm.llcx, llvm.tm), - ModuleSource::Preexisting(_) => { - bug!("codegen: called with ModuleSource::Preexisting") - } - }; - let module_name = mtrans.name.clone(); - let module_name = Some(&module_name[..]); - let handlers = DiagnosticHandlers::new(cgcx, diag_handler, llcx); - - if cgcx.msvc_imps_needed { - create_msvc_imps(cgcx, llcx, llmod); - } - - // A codegen-specific pass manager is used to generate object - // files for an LLVM module. - // - // Apparently each of these pass managers is a one-shot kind of - // thing, so we create a new one for each type of output. The - // pass manager passed to the closure should be ensured to not - // escape the closure itself, and the manager should only be - // used once. - unsafe fn with_codegen(tm: TargetMachineRef, - llmod: ModuleRef, - no_builtins: bool, - f: F) -> R - where F: FnOnce(PassManagerRef) -> R, - { - let cpm = llvm::LLVMCreatePassManager(); - llvm::LLVMRustAddAnalysisPasses(tm, cpm, llmod); - llvm::LLVMRustAddLibraryInfo(cpm, llmod, no_builtins); - f(cpm) - } - - // If we don't have the integrated assembler, then we need to emit asm - // from LLVM and use `gcc` to create the object file. - let asm_to_obj = config.emit_obj && config.no_integrated_as; - - // Change what we write and cleanup based on whether obj files are - // just llvm bitcode. In that case write bitcode, and possibly - // delete the bitcode if it wasn't requested. Don't generate the - // machine code, instead copy the .o file from the .bc - let write_bc = config.emit_bc || config.obj_is_bitcode; - let rm_bc = !config.emit_bc && config.obj_is_bitcode; - let write_obj = config.emit_obj && !config.obj_is_bitcode && !asm_to_obj; - let copy_bc_to_obj = config.emit_obj && config.obj_is_bitcode; - - let bc_out = cgcx.output_filenames.temp_path(OutputType::Bitcode, module_name); - let obj_out = cgcx.output_filenames.temp_path(OutputType::Object, module_name); - - - if write_bc || config.emit_bc_compressed || config.embed_bitcode { - let thin; - let old; - let data = if llvm::LLVMRustThinLTOAvailable() { - thin = ThinBuffer::new(llmod); - thin.data() - } else { - old = ModuleBuffer::new(llmod); - old.data() - }; - timeline.record("make-bc"); - - if write_bc { - if let Err(e) = fs::write(&bc_out, data) { - diag_handler.err(&format!("failed to write bytecode: {}", e)); - } - timeline.record("write-bc"); - } - - if config.embed_bitcode { - embed_bitcode(cgcx, llcx, llmod, Some(data)); - timeline.record("embed-bc"); - } - - if config.emit_bc_compressed { - let dst = bc_out.with_extension(RLIB_BYTECODE_EXTENSION); - let data = bytecode::encode(&mtrans.llmod_id, data); - if let Err(e) = fs::write(&dst, data) { - diag_handler.err(&format!("failed to write bytecode: {}", e)); - } - timeline.record("compress-bc"); - } - } else if config.embed_bitcode_marker { - embed_bitcode(cgcx, llcx, llmod, None); - } - - time_ext(config.time_passes, None, &format!("codegen passes [{}]", module_name.unwrap()), - || -> Result<(), FatalError> { - if config.emit_ir { - let out = cgcx.output_filenames.temp_path(OutputType::LlvmAssembly, module_name); - let out = path2cstr(&out); - - extern "C" fn demangle_callback(input_ptr: *const c_char, - input_len: size_t, - output_ptr: *mut c_char, - output_len: size_t) -> size_t { - let input = unsafe { - slice::from_raw_parts(input_ptr as *const u8, input_len as usize) - }; - - let input = match str::from_utf8(input) { - Ok(s) => s, - Err(_) => return 0, - }; - - let output = unsafe { - slice::from_raw_parts_mut(output_ptr as *mut u8, output_len as usize) - }; - let mut cursor = io::Cursor::new(output); - - let demangled = match rustc_demangle::try_demangle(input) { - Ok(d) => d, - Err(_) => return 0, - }; - - if let Err(_) = write!(cursor, "{:#}", demangled) { - // Possible only if provided buffer is not big enough - return 0; - } - - cursor.position() as size_t - } - - with_codegen(tm, llmod, config.no_builtins, |cpm| { - llvm::LLVMRustPrintModule(cpm, llmod, out.as_ptr(), demangle_callback); - llvm::LLVMDisposePassManager(cpm); - }); - timeline.record("ir"); - } - - if config.emit_asm || asm_to_obj { - let path = cgcx.output_filenames.temp_path(OutputType::Assembly, module_name); - - // We can't use the same module for asm and binary output, because that triggers - // various errors like invalid IR or broken binaries, so we might have to clone the - // module to produce the asm output - let llmod = if config.emit_obj { - llvm::LLVMCloneModule(llmod) - } else { - llmod - }; - with_codegen(tm, llmod, config.no_builtins, |cpm| { - write_output_file(diag_handler, tm, cpm, llmod, &path, - llvm::FileType::AssemblyFile) - })?; - if config.emit_obj { - llvm::LLVMDisposeModule(llmod); - } - timeline.record("asm"); - } - - if write_obj { - with_codegen(tm, llmod, config.no_builtins, |cpm| { - write_output_file(diag_handler, tm, cpm, llmod, &obj_out, - llvm::FileType::ObjectFile) - })?; - timeline.record("obj"); - } else if asm_to_obj { - let assembly = cgcx.output_filenames.temp_path(OutputType::Assembly, module_name); - run_assembler(cgcx, diag_handler, &assembly, &obj_out); - timeline.record("asm_to_obj"); - - if !config.emit_asm && !cgcx.save_temps { - drop(fs::remove_file(&assembly)); - } - } - - Ok(()) - })?; - - if copy_bc_to_obj { - debug!("copying bitcode {:?} to obj {:?}", bc_out, obj_out); - if let Err(e) = link_or_copy(&bc_out, &obj_out) { - diag_handler.err(&format!("failed to copy bitcode to object file: {}", e)); - } - } - - if rm_bc { - debug!("removing_bitcode {:?}", bc_out); - if let Err(e) = fs::remove_file(&bc_out) { - diag_handler.err(&format!("failed to remove bitcode: {}", e)); - } - } - - drop(handlers); - Ok(mtrans.into_compiled_module(config.emit_obj, - config.emit_bc, - config.emit_bc_compressed, - &cgcx.output_filenames)) -} - -/// Embed the bitcode of an LLVM module in the LLVM module itself. -/// -/// This is done primarily for iOS where it appears to be standard to compile C -/// code at least with `-fembed-bitcode` which creates two sections in the -/// executable: -/// -/// * __LLVM,__bitcode -/// * __LLVM,__cmdline -/// -/// It appears *both* of these sections are necessary to get the linker to -/// recognize what's going on. For us though we just always throw in an empty -/// cmdline section. -/// -/// Furthermore debug/O1 builds don't actually embed bitcode but rather just -/// embed an empty section. -/// -/// Basically all of this is us attempting to follow in the footsteps of clang -/// on iOS. See #35968 for lots more info. -unsafe fn embed_bitcode(cgcx: &CodegenContext, - llcx: ContextRef, - llmod: ModuleRef, - bitcode: Option<&[u8]>) { - let llconst = C_bytes_in_context(llcx, bitcode.unwrap_or(&[])); - let llglobal = llvm::LLVMAddGlobal( - llmod, - val_ty(llconst).to_ref(), - "rustc.embedded.module\0".as_ptr() as *const _, - ); - llvm::LLVMSetInitializer(llglobal, llconst); - - let is_apple = cgcx.opts.target_triple.triple().contains("-ios") || - cgcx.opts.target_triple.triple().contains("-darwin"); - - let section = if is_apple { - "__LLVM,__bitcode\0" - } else { - ".llvmbc\0" - }; - llvm::LLVMSetSection(llglobal, section.as_ptr() as *const _); - llvm::LLVMRustSetLinkage(llglobal, llvm::Linkage::PrivateLinkage); - llvm::LLVMSetGlobalConstant(llglobal, llvm::True); - - let llconst = C_bytes_in_context(llcx, &[]); - let llglobal = llvm::LLVMAddGlobal( - llmod, - val_ty(llconst).to_ref(), - "rustc.embedded.cmdline\0".as_ptr() as *const _, - ); - llvm::LLVMSetInitializer(llglobal, llconst); - let section = if is_apple { - "__LLVM,__cmdline\0" - } else { - ".llvmcmd\0" - }; - llvm::LLVMSetSection(llglobal, section.as_ptr() as *const _); - llvm::LLVMRustSetLinkage(llglobal, llvm::Linkage::PrivateLinkage); -} - -pub(crate) struct CompiledModules { - pub modules: Vec, - pub metadata_module: CompiledModule, - pub allocator_module: Option, -} - -fn need_crate_bitcode_for_rlib(sess: &Session) -> bool { - sess.crate_types.borrow().contains(&config::CrateTypeRlib) && - sess.opts.output_types.contains_key(&OutputType::Exe) -} - -pub fn start_async_translation(tcx: TyCtxt, - time_graph: Option, - link: LinkMeta, - metadata: EncodedMetadata, - coordinator_receive: Receiver>, - total_cgus: usize) - -> OngoingCrateTranslation { - let sess = tcx.sess; - let crate_name = tcx.crate_name(LOCAL_CRATE); - let no_builtins = attr::contains_name(&tcx.hir.krate().attrs, "no_builtins"); - let subsystem = attr::first_attr_value_str_by_name(&tcx.hir.krate().attrs, - "windows_subsystem"); - let windows_subsystem = subsystem.map(|subsystem| { - if subsystem != "windows" && subsystem != "console" { - tcx.sess.fatal(&format!("invalid windows subsystem `{}`, only \ - `windows` and `console` are allowed", - subsystem)); - } - subsystem.to_string() - }); - - let linker_info = LinkerInfo::new(tcx); - let crate_info = CrateInfo::new(tcx); - - // Figure out what we actually need to build. - let mut modules_config = ModuleConfig::new(sess.opts.cg.passes.clone()); - let mut metadata_config = ModuleConfig::new(vec![]); - let mut allocator_config = ModuleConfig::new(vec![]); - - if let Some(ref sanitizer) = sess.opts.debugging_opts.sanitizer { - match *sanitizer { - Sanitizer::Address => { - modules_config.passes.push("asan".to_owned()); - modules_config.passes.push("asan-module".to_owned()); - } - Sanitizer::Memory => { - modules_config.passes.push("msan".to_owned()) - } - Sanitizer::Thread => { - modules_config.passes.push("tsan".to_owned()) - } - _ => {} - } - } - - if sess.opts.debugging_opts.profile { - modules_config.passes.push("insert-gcov-profiling".to_owned()) - } - - modules_config.pgo_gen = sess.opts.debugging_opts.pgo_gen.clone(); - modules_config.pgo_use = sess.opts.debugging_opts.pgo_use.clone(); - - modules_config.opt_level = Some(get_llvm_opt_level(sess.opts.optimize)); - modules_config.opt_size = Some(get_llvm_opt_size(sess.opts.optimize)); - - // Save all versions of the bytecode if we're saving our temporaries. - if sess.opts.cg.save_temps { - modules_config.emit_no_opt_bc = true; - modules_config.emit_bc = true; - modules_config.emit_lto_bc = true; - metadata_config.emit_bc = true; - allocator_config.emit_bc = true; - } - - // Emit compressed bitcode files for the crate if we're emitting an rlib. - // Whenever an rlib is created, the bitcode is inserted into the archive in - // order to allow LTO against it. - if need_crate_bitcode_for_rlib(sess) { - modules_config.emit_bc_compressed = true; - allocator_config.emit_bc_compressed = true; - } - - modules_config.no_integrated_as = tcx.sess.opts.cg.no_integrated_as || - tcx.sess.target.target.options.no_integrated_as; - - for output_type in sess.opts.output_types.keys() { - match *output_type { - OutputType::Bitcode => { modules_config.emit_bc = true; } - OutputType::LlvmAssembly => { modules_config.emit_ir = true; } - OutputType::Assembly => { - modules_config.emit_asm = true; - // If we're not using the LLVM assembler, this function - // could be invoked specially with output_type_assembly, so - // in this case we still want the metadata object file. - if !sess.opts.output_types.contains_key(&OutputType::Assembly) { - metadata_config.emit_obj = true; - allocator_config.emit_obj = true; - } - } - OutputType::Object => { modules_config.emit_obj = true; } - OutputType::Metadata => { metadata_config.emit_obj = true; } - OutputType::Exe => { - modules_config.emit_obj = true; - metadata_config.emit_obj = true; - allocator_config.emit_obj = true; - }, - OutputType::Mir => {} - OutputType::DepInfo => {} - } - } - - modules_config.set_flags(sess, no_builtins); - metadata_config.set_flags(sess, no_builtins); - allocator_config.set_flags(sess, no_builtins); - - // Exclude metadata and allocator modules from time_passes output, since - // they throw off the "LLVM passes" measurement. - metadata_config.time_passes = false; - allocator_config.time_passes = false; - - let (shared_emitter, shared_emitter_main) = SharedEmitter::new(); - let (trans_worker_send, trans_worker_receive) = channel(); - - let coordinator_thread = start_executing_work(tcx, - &crate_info, - shared_emitter, - trans_worker_send, - coordinator_receive, - total_cgus, - sess.jobserver.clone(), - time_graph.clone(), - Arc::new(modules_config), - Arc::new(metadata_config), - Arc::new(allocator_config)); - - OngoingCrateTranslation { - crate_name, - link, - metadata, - windows_subsystem, - linker_info, - crate_info, - - time_graph, - coordinator_send: tcx.tx_to_llvm_workers.lock().clone(), - trans_worker_receive, - shared_emitter_main, - future: coordinator_thread, - output_filenames: tcx.output_filenames(LOCAL_CRATE), - } -} - -fn copy_all_cgu_workproducts_to_incr_comp_cache_dir( - sess: &Session, - compiled_modules: &CompiledModules -) -> FxHashMap { - let mut work_products = FxHashMap::default(); - - if sess.opts.incremental.is_none() { - return work_products; - } - - for module in compiled_modules.modules.iter() { - let mut files = vec![]; - - if let Some(ref path) = module.object { - files.push((WorkProductFileKind::Object, path.clone())); - } - if let Some(ref path) = module.bytecode { - files.push((WorkProductFileKind::Bytecode, path.clone())); - } - if let Some(ref path) = module.bytecode_compressed { - files.push((WorkProductFileKind::BytecodeCompressed, path.clone())); - } - - if let Some((id, product)) = - copy_cgu_workproducts_to_incr_comp_cache_dir(sess, &module.name, &files) { - work_products.insert(id, product); - } - } - - work_products -} - -fn produce_final_output_artifacts(sess: &Session, - compiled_modules: &CompiledModules, - crate_output: &OutputFilenames) { - let mut user_wants_bitcode = false; - let mut user_wants_objects = false; - - // Produce final compile outputs. - let copy_gracefully = |from: &Path, to: &Path| { - if let Err(e) = fs::copy(from, to) { - sess.err(&format!("could not copy {:?} to {:?}: {}", from, to, e)); - } - }; - - let copy_if_one_unit = |output_type: OutputType, - keep_numbered: bool| { - if compiled_modules.modules.len() == 1 { - // 1) Only one codegen unit. In this case it's no difficulty - // to copy `foo.0.x` to `foo.x`. - let module_name = Some(&compiled_modules.modules[0].name[..]); - let path = crate_output.temp_path(output_type, module_name); - copy_gracefully(&path, - &crate_output.path(output_type)); - if !sess.opts.cg.save_temps && !keep_numbered { - // The user just wants `foo.x`, not `foo.#module-name#.x`. - remove(sess, &path); - } - } else { - let ext = crate_output.temp_path(output_type, None) - .extension() - .unwrap() - .to_str() - .unwrap() - .to_owned(); - - if crate_output.outputs.contains_key(&output_type) { - // 2) Multiple codegen units, with `--emit foo=some_name`. We have - // no good solution for this case, so warn the user. - sess.warn(&format!("ignoring emit path because multiple .{} files \ - were produced", ext)); - } else if crate_output.single_output_file.is_some() { - // 3) Multiple codegen units, with `-o some_name`. We have - // no good solution for this case, so warn the user. - sess.warn(&format!("ignoring -o because multiple .{} files \ - were produced", ext)); - } else { - // 4) Multiple codegen units, but no explicit name. We - // just leave the `foo.0.x` files in place. - // (We don't have to do any work in this case.) - } - } - }; - - // Flag to indicate whether the user explicitly requested bitcode. - // Otherwise, we produced it only as a temporary output, and will need - // to get rid of it. - for output_type in crate_output.outputs.keys() { - match *output_type { - OutputType::Bitcode => { - user_wants_bitcode = true; - // Copy to .bc, but always keep the .0.bc. There is a later - // check to figure out if we should delete .0.bc files, or keep - // them for making an rlib. - copy_if_one_unit(OutputType::Bitcode, true); - } - OutputType::LlvmAssembly => { - copy_if_one_unit(OutputType::LlvmAssembly, false); - } - OutputType::Assembly => { - copy_if_one_unit(OutputType::Assembly, false); - } - OutputType::Object => { - user_wants_objects = true; - copy_if_one_unit(OutputType::Object, true); - } - OutputType::Mir | - OutputType::Metadata | - OutputType::Exe | - OutputType::DepInfo => {} - } - } - - // Clean up unwanted temporary files. - - // We create the following files by default: - // - #crate#.#module-name#.bc - // - #crate#.#module-name#.o - // - #crate#.crate.metadata.bc - // - #crate#.crate.metadata.o - // - #crate#.o (linked from crate.##.o) - // - #crate#.bc (copied from crate.##.bc) - // We may create additional files if requested by the user (through - // `-C save-temps` or `--emit=` flags). - - if !sess.opts.cg.save_temps { - // Remove the temporary .#module-name#.o objects. If the user didn't - // explicitly request bitcode (with --emit=bc), and the bitcode is not - // needed for building an rlib, then we must remove .#module-name#.bc as - // well. - - // Specific rules for keeping .#module-name#.bc: - // - If the user requested bitcode (`user_wants_bitcode`), and - // codegen_units > 1, then keep it. - // - If the user requested bitcode but codegen_units == 1, then we - // can toss .#module-name#.bc because we copied it to .bc earlier. - // - If we're not building an rlib and the user didn't request - // bitcode, then delete .#module-name#.bc. - // If you change how this works, also update back::link::link_rlib, - // where .#module-name#.bc files are (maybe) deleted after making an - // rlib. - let needs_crate_object = crate_output.outputs.contains_key(&OutputType::Exe); - - let keep_numbered_bitcode = user_wants_bitcode && sess.codegen_units() > 1; - - let keep_numbered_objects = needs_crate_object || - (user_wants_objects && sess.codegen_units() > 1); - - for module in compiled_modules.modules.iter() { - if let Some(ref path) = module.object { - if !keep_numbered_objects { - remove(sess, path); - } - } - - if let Some(ref path) = module.bytecode { - if !keep_numbered_bitcode { - remove(sess, path); - } - } - } - - if !user_wants_bitcode { - if let Some(ref path) = compiled_modules.metadata_module.bytecode { - remove(sess, &path); - } - - if let Some(ref allocator_module) = compiled_modules.allocator_module { - if let Some(ref path) = allocator_module.bytecode { - remove(sess, path); - } - } - } - } - - // We leave the following files around by default: - // - #crate#.o - // - #crate#.crate.metadata.o - // - #crate#.bc - // These are used in linking steps and will be cleaned up afterward. -} - -pub(crate) fn dump_incremental_data(trans: &CrateTranslation) { - println!("[incremental] Re-using {} out of {} modules", - trans.modules.iter().filter(|m| m.pre_existing).count(), - trans.modules.len()); -} - -enum WorkItem { - Optimize(ModuleTranslation), - LTO(lto::LtoModuleTranslation), -} - -impl WorkItem { - fn kind(&self) -> ModuleKind { - match *self { - WorkItem::Optimize(ref m) => m.kind, - WorkItem::LTO(_) => ModuleKind::Regular, - } - } - - fn name(&self) -> String { - match *self { - WorkItem::Optimize(ref m) => format!("optimize: {}", m.name), - WorkItem::LTO(ref m) => format!("lto: {}", m.name()), - } - } -} - -enum WorkItemResult { - Compiled(CompiledModule), - NeedsLTO(ModuleTranslation), -} - -fn execute_work_item(cgcx: &CodegenContext, - work_item: WorkItem, - timeline: &mut Timeline) - -> Result -{ - let diag_handler = cgcx.create_diag_handler(); - let config = cgcx.config(work_item.kind()); - let mtrans = match work_item { - WorkItem::Optimize(mtrans) => mtrans, - WorkItem::LTO(mut lto) => { - unsafe { - let module = lto.optimize(cgcx, timeline)?; - let module = codegen(cgcx, &diag_handler, module, config, timeline)?; - return Ok(WorkItemResult::Compiled(module)) - } - } - }; - let module_name = mtrans.name.clone(); - - let pre_existing = match mtrans.source { - ModuleSource::Translated(_) => None, - ModuleSource::Preexisting(ref wp) => Some(wp.clone()), - }; - - if let Some(wp) = pre_existing { - let incr_comp_session_dir = cgcx.incr_comp_session_dir - .as_ref() - .unwrap(); - let name = &mtrans.name; - let mut object = None; - let mut bytecode = None; - let mut bytecode_compressed = None; - for (kind, saved_file) in wp.saved_files { - let obj_out = match kind { - WorkProductFileKind::Object => { - let path = cgcx.output_filenames.temp_path(OutputType::Object, Some(name)); - object = Some(path.clone()); - path - } - WorkProductFileKind::Bytecode => { - let path = cgcx.output_filenames.temp_path(OutputType::Bitcode, Some(name)); - bytecode = Some(path.clone()); - path - } - WorkProductFileKind::BytecodeCompressed => { - let path = cgcx.output_filenames.temp_path(OutputType::Bitcode, Some(name)) - .with_extension(RLIB_BYTECODE_EXTENSION); - bytecode_compressed = Some(path.clone()); - path - } - }; - let source_file = in_incr_comp_dir(&incr_comp_session_dir, - &saved_file); - debug!("copying pre-existing module `{}` from {:?} to {}", - mtrans.name, - source_file, - obj_out.display()); - match link_or_copy(&source_file, &obj_out) { - Ok(_) => { } - Err(err) => { - diag_handler.err(&format!("unable to copy {} to {}: {}", - source_file.display(), - obj_out.display(), - err)); - } - } - } - assert_eq!(object.is_some(), config.emit_obj); - assert_eq!(bytecode.is_some(), config.emit_bc); - assert_eq!(bytecode_compressed.is_some(), config.emit_bc_compressed); - - Ok(WorkItemResult::Compiled(CompiledModule { - llmod_id: mtrans.llmod_id.clone(), - name: module_name, - kind: ModuleKind::Regular, - pre_existing: true, - object, - bytecode, - bytecode_compressed, - })) - } else { - debug!("llvm-optimizing {:?}", module_name); - - unsafe { - optimize(cgcx, &diag_handler, &mtrans, config, timeline)?; - - // After we've done the initial round of optimizations we need to - // decide whether to synchronously codegen this module or ship it - // back to the coordinator thread for further LTO processing (which - // has to wait for all the initial modules to be optimized). - // - // Here we dispatch based on the `cgcx.lto` and kind of module we're - // translating... - let needs_lto = match cgcx.lto { - Lto::No => false, - - // Here we've got a full crate graph LTO requested. We ignore - // this, however, if the crate type is only an rlib as there's - // no full crate graph to process, that'll happen later. - // - // This use case currently comes up primarily for targets that - // require LTO so the request for LTO is always unconditionally - // passed down to the backend, but we don't actually want to do - // anything about it yet until we've got a final product. - Lto::Yes | Lto::Fat | Lto::Thin => { - cgcx.crate_types.len() != 1 || - cgcx.crate_types[0] != config::CrateTypeRlib - } - - // When we're automatically doing ThinLTO for multi-codegen-unit - // builds we don't actually want to LTO the allocator modules if - // it shows up. This is due to various linker shenanigans that - // we'll encounter later. - // - // Additionally here's where we also factor in the current LLVM - // version. If it doesn't support ThinLTO we skip this. - Lto::ThinLocal => { - mtrans.kind != ModuleKind::Allocator && - llvm::LLVMRustThinLTOAvailable() - } - }; - - // Metadata modules never participate in LTO regardless of the lto - // settings. - let needs_lto = needs_lto && mtrans.kind != ModuleKind::Metadata; - - // Don't run LTO passes when cross-lang LTO is enabled. The linker - // will do that for us in this case. - let needs_lto = needs_lto && - !cgcx.opts.debugging_opts.cross_lang_lto.embed_bitcode(); - - if needs_lto { - Ok(WorkItemResult::NeedsLTO(mtrans)) - } else { - let module = codegen(cgcx, &diag_handler, mtrans, config, timeline)?; - Ok(WorkItemResult::Compiled(module)) - } - } - } -} - -enum Message { - Token(io::Result), - NeedsLTO { - result: ModuleTranslation, - worker_id: usize, - }, - Done { - result: Result, - worker_id: usize, - }, - TranslationDone { - llvm_work_item: WorkItem, - cost: u64, - }, - TranslationComplete, - TranslateItem, -} - -struct Diagnostic { - msg: String, - code: Option, - lvl: Level, -} - -#[derive(PartialEq, Clone, Copy, Debug)] -enum MainThreadWorkerState { - Idle, - Translating, - LLVMing, -} - -fn start_executing_work(tcx: TyCtxt, - crate_info: &CrateInfo, - shared_emitter: SharedEmitter, - trans_worker_send: Sender, - coordinator_receive: Receiver>, - total_cgus: usize, - jobserver: Client, - time_graph: Option, - modules_config: Arc, - metadata_config: Arc, - allocator_config: Arc) - -> thread::JoinHandle> { - let coordinator_send = tcx.tx_to_llvm_workers.lock().clone(); - let sess = tcx.sess; - - // Compute the set of symbols we need to retain when doing LTO (if we need to) - let exported_symbols = { - let mut exported_symbols = FxHashMap(); - - let copy_symbols = |cnum| { - let symbols = tcx.exported_symbols(cnum) - .iter() - .map(|&(s, lvl)| (s.symbol_name(tcx).to_string(), lvl)) - .collect(); - Arc::new(symbols) - }; - - match sess.lto() { - Lto::No => None, - Lto::ThinLocal => { - exported_symbols.insert(LOCAL_CRATE, copy_symbols(LOCAL_CRATE)); - Some(Arc::new(exported_symbols)) - } - Lto::Yes | Lto::Fat | Lto::Thin => { - exported_symbols.insert(LOCAL_CRATE, copy_symbols(LOCAL_CRATE)); - for &cnum in tcx.crates().iter() { - exported_symbols.insert(cnum, copy_symbols(cnum)); - } - Some(Arc::new(exported_symbols)) - } - } - }; - - // First up, convert our jobserver into a helper thread so we can use normal - // mpsc channels to manage our messages and such. - // After we've requested tokens then we'll, when we can, - // get tokens on `coordinator_receive` which will - // get managed in the main loop below. - let coordinator_send2 = coordinator_send.clone(); - let helper = jobserver.into_helper_thread(move |token| { - drop(coordinator_send2.send(Box::new(Message::Token(token)))); - }).expect("failed to spawn helper thread"); - - let mut each_linked_rlib_for_lto = Vec::new(); - drop(link::each_linked_rlib(sess, crate_info, &mut |cnum, path| { - if link::ignored_for_lto(sess, crate_info, cnum) { - return - } - each_linked_rlib_for_lto.push((cnum, path.to_path_buf())); - })); - - let assembler_cmd = if modules_config.no_integrated_as { - // HACK: currently we use linker (gcc) as our assembler - let (name, mut cmd) = get_linker(sess); - cmd.args(&sess.target.target.options.asm_args); - Some(Arc::new(AssemblerCommand { - name, - cmd, - })) - } else { - None - }; - - let cgcx = CodegenContext { - crate_types: sess.crate_types.borrow().clone(), - each_linked_rlib_for_lto, - lto: sess.lto(), - no_landing_pads: sess.no_landing_pads(), - fewer_names: sess.fewer_names(), - save_temps: sess.opts.cg.save_temps, - opts: Arc::new(sess.opts.clone()), - time_passes: sess.time_passes(), - exported_symbols, - plugin_passes: sess.plugin_llvm_passes.borrow().clone(), - remark: sess.opts.cg.remark.clone(), - worker: 0, - incr_comp_session_dir: sess.incr_comp_session_dir_opt().map(|r| r.clone()), - coordinator_send, - diag_emitter: shared_emitter.clone(), - time_graph, - output_filenames: tcx.output_filenames(LOCAL_CRATE), - regular_module_config: modules_config, - metadata_module_config: metadata_config, - allocator_module_config: allocator_config, - tm_factory: target_machine_factory(tcx.sess, false), - total_cgus, - msvc_imps_needed: msvc_imps_needed(tcx), - target_pointer_width: tcx.sess.target.target.target_pointer_width.clone(), - debuginfo: tcx.sess.opts.debuginfo, - assembler_cmd, - }; - - // This is the "main loop" of parallel work happening for parallel codegen. - // It's here that we manage parallelism, schedule work, and work with - // messages coming from clients. - // - // There are a few environmental pre-conditions that shape how the system - // is set up: - // - // - Error reporting only can happen on the main thread because that's the - // only place where we have access to the compiler `Session`. - // - LLVM work can be done on any thread. - // - Translation can only happen on the main thread. - // - Each thread doing substantial work most be in possession of a `Token` - // from the `Jobserver`. - // - The compiler process always holds one `Token`. Any additional `Tokens` - // have to be requested from the `Jobserver`. - // - // Error Reporting - // =============== - // The error reporting restriction is handled separately from the rest: We - // set up a `SharedEmitter` the holds an open channel to the main thread. - // When an error occurs on any thread, the shared emitter will send the - // error message to the receiver main thread (`SharedEmitterMain`). The - // main thread will periodically query this error message queue and emit - // any error messages it has received. It might even abort compilation if - // has received a fatal error. In this case we rely on all other threads - // being torn down automatically with the main thread. - // Since the main thread will often be busy doing translation work, error - // reporting will be somewhat delayed, since the message queue can only be - // checked in between to work packages. - // - // Work Processing Infrastructure - // ============================== - // The work processing infrastructure knows three major actors: - // - // - the coordinator thread, - // - the main thread, and - // - LLVM worker threads - // - // The coordinator thread is running a message loop. It instructs the main - // thread about what work to do when, and it will spawn off LLVM worker - // threads as open LLVM WorkItems become available. - // - // The job of the main thread is to translate CGUs into LLVM work package - // (since the main thread is the only thread that can do this). The main - // thread will block until it receives a message from the coordinator, upon - // which it will translate one CGU, send it to the coordinator and block - // again. This way the coordinator can control what the main thread is - // doing. - // - // The coordinator keeps a queue of LLVM WorkItems, and when a `Token` is - // available, it will spawn off a new LLVM worker thread and let it process - // that a WorkItem. When a LLVM worker thread is done with its WorkItem, - // it will just shut down, which also frees all resources associated with - // the given LLVM module, and sends a message to the coordinator that the - // has been completed. - // - // Work Scheduling - // =============== - // The scheduler's goal is to minimize the time it takes to complete all - // work there is, however, we also want to keep memory consumption low - // if possible. These two goals are at odds with each other: If memory - // consumption were not an issue, we could just let the main thread produce - // LLVM WorkItems at full speed, assuring maximal utilization of - // Tokens/LLVM worker threads. However, since translation usual is faster - // than LLVM processing, the queue of LLVM WorkItems would fill up and each - // WorkItem potentially holds on to a substantial amount of memory. - // - // So the actual goal is to always produce just enough LLVM WorkItems as - // not to starve our LLVM worker threads. That means, once we have enough - // WorkItems in our queue, we can block the main thread, so it does not - // produce more until we need them. - // - // Doing LLVM Work on the Main Thread - // ---------------------------------- - // Since the main thread owns the compiler processes implicit `Token`, it is - // wasteful to keep it blocked without doing any work. Therefore, what we do - // in this case is: We spawn off an additional LLVM worker thread that helps - // reduce the queue. The work it is doing corresponds to the implicit - // `Token`. The coordinator will mark the main thread as being busy with - // LLVM work. (The actual work happens on another OS thread but we just care - // about `Tokens`, not actual threads). - // - // When any LLVM worker thread finishes while the main thread is marked as - // "busy with LLVM work", we can do a little switcheroo: We give the Token - // of the just finished thread to the LLVM worker thread that is working on - // behalf of the main thread's implicit Token, thus freeing up the main - // thread again. The coordinator can then again decide what the main thread - // should do. This allows the coordinator to make decisions at more points - // in time. - // - // Striking a Balance between Throughput and Memory Consumption - // ------------------------------------------------------------ - // Since our two goals, (1) use as many Tokens as possible and (2) keep - // memory consumption as low as possible, are in conflict with each other, - // we have to find a trade off between them. Right now, the goal is to keep - // all workers busy, which means that no worker should find the queue empty - // when it is ready to start. - // How do we do achieve this? Good question :) We actually never know how - // many `Tokens` are potentially available so it's hard to say how much to - // fill up the queue before switching the main thread to LLVM work. Also we - // currently don't have a means to estimate how long a running LLVM worker - // will still be busy with it's current WorkItem. However, we know the - // maximal count of available Tokens that makes sense (=the number of CPU - // cores), so we can take a conservative guess. The heuristic we use here - // is implemented in the `queue_full_enough()` function. - // - // Some Background on Jobservers - // ----------------------------- - // It's worth also touching on the management of parallelism here. We don't - // want to just spawn a thread per work item because while that's optimal - // parallelism it may overload a system with too many threads or violate our - // configuration for the maximum amount of cpu to use for this process. To - // manage this we use the `jobserver` crate. - // - // Job servers are an artifact of GNU make and are used to manage - // parallelism between processes. A jobserver is a glorified IPC semaphore - // basically. Whenever we want to run some work we acquire the semaphore, - // and whenever we're done with that work we release the semaphore. In this - // manner we can ensure that the maximum number of parallel workers is - // capped at any one point in time. - // - // LTO and the coordinator thread - // ------------------------------ - // - // The final job the coordinator thread is responsible for is managing LTO - // and how that works. When LTO is requested what we'll to is collect all - // optimized LLVM modules into a local vector on the coordinator. Once all - // modules have been translated and optimized we hand this to the `lto` - // module for further optimization. The `lto` module will return back a list - // of more modules to work on, which the coordinator will continue to spawn - // work for. - // - // Each LLVM module is automatically sent back to the coordinator for LTO if - // necessary. There's already optimizations in place to avoid sending work - // back to the coordinator if LTO isn't requested. - return thread::spawn(move || { - // We pretend to be within the top-level LLVM time-passes task here: - set_time_depth(1); - - let max_workers = ::num_cpus::get(); - let mut worker_id_counter = 0; - let mut free_worker_ids = Vec::new(); - let mut get_worker_id = |free_worker_ids: &mut Vec| { - if let Some(id) = free_worker_ids.pop() { - id - } else { - let id = worker_id_counter; - worker_id_counter += 1; - id - } - }; - - // This is where we collect codegen units that have gone all the way - // through translation and LLVM. - let mut compiled_modules = vec![]; - let mut compiled_metadata_module = None; - let mut compiled_allocator_module = None; - let mut needs_lto = Vec::new(); - let mut started_lto = false; - - // This flag tracks whether all items have gone through translations - let mut translation_done = false; - - // This is the queue of LLVM work items that still need processing. - let mut work_items = Vec::<(WorkItem, u64)>::new(); - - // This are the Jobserver Tokens we currently hold. Does not include - // the implicit Token the compiler process owns no matter what. - let mut tokens = Vec::new(); - - let mut main_thread_worker_state = MainThreadWorkerState::Idle; - let mut running = 0; - - let mut llvm_start_time = None; - - // Run the message loop while there's still anything that needs message - // processing: - while !translation_done || - work_items.len() > 0 || - running > 0 || - needs_lto.len() > 0 || - main_thread_worker_state != MainThreadWorkerState::Idle { - - // While there are still CGUs to be translated, the coordinator has - // to decide how to utilize the compiler processes implicit Token: - // For translating more CGU or for running them through LLVM. - if !translation_done { - if main_thread_worker_state == MainThreadWorkerState::Idle { - if !queue_full_enough(work_items.len(), running, max_workers) { - // The queue is not full enough, translate more items: - if let Err(_) = trans_worker_send.send(Message::TranslateItem) { - panic!("Could not send Message::TranslateItem to main thread") - } - main_thread_worker_state = MainThreadWorkerState::Translating; - } else { - // The queue is full enough to not let the worker - // threads starve. Use the implicit Token to do some - // LLVM work too. - let (item, _) = work_items.pop() - .expect("queue empty - queue_full_enough() broken?"); - let cgcx = CodegenContext { - worker: get_worker_id(&mut free_worker_ids), - .. cgcx.clone() - }; - maybe_start_llvm_timer(cgcx.config(item.kind()), - &mut llvm_start_time); - main_thread_worker_state = MainThreadWorkerState::LLVMing; - spawn_work(cgcx, item); - } - } - } else { - // If we've finished everything related to normal translation - // then it must be the case that we've got some LTO work to do. - // Perform the serial work here of figuring out what we're - // going to LTO and then push a bunch of work items onto our - // queue to do LTO - if work_items.len() == 0 && - running == 0 && - main_thread_worker_state == MainThreadWorkerState::Idle { - assert!(!started_lto); - assert!(needs_lto.len() > 0); - started_lto = true; - let modules = mem::replace(&mut needs_lto, Vec::new()); - for (work, cost) in generate_lto_work(&cgcx, modules) { - let insertion_index = work_items - .binary_search_by_key(&cost, |&(_, cost)| cost) - .unwrap_or_else(|e| e); - work_items.insert(insertion_index, (work, cost)); - helper.request_token(); - } - } - - // In this branch, we know that everything has been translated, - // so it's just a matter of determining whether the implicit - // Token is free to use for LLVM work. - match main_thread_worker_state { - MainThreadWorkerState::Idle => { - if let Some((item, _)) = work_items.pop() { - let cgcx = CodegenContext { - worker: get_worker_id(&mut free_worker_ids), - .. cgcx.clone() - }; - maybe_start_llvm_timer(cgcx.config(item.kind()), - &mut llvm_start_time); - main_thread_worker_state = MainThreadWorkerState::LLVMing; - spawn_work(cgcx, item); - } else { - // There is no unstarted work, so let the main thread - // take over for a running worker. Otherwise the - // implicit token would just go to waste. - // We reduce the `running` counter by one. The - // `tokens.truncate()` below will take care of - // giving the Token back. - debug_assert!(running > 0); - running -= 1; - main_thread_worker_state = MainThreadWorkerState::LLVMing; - } - } - MainThreadWorkerState::Translating => { - bug!("trans worker should not be translating after \ - translation was already completed") - } - MainThreadWorkerState::LLVMing => { - // Already making good use of that token - } - } - } - - // Spin up what work we can, only doing this while we've got available - // parallelism slots and work left to spawn. - while work_items.len() > 0 && running < tokens.len() { - let (item, _) = work_items.pop().unwrap(); - - maybe_start_llvm_timer(cgcx.config(item.kind()), - &mut llvm_start_time); - - let cgcx = CodegenContext { - worker: get_worker_id(&mut free_worker_ids), - .. cgcx.clone() - }; - - spawn_work(cgcx, item); - running += 1; - } - - // Relinquish accidentally acquired extra tokens - tokens.truncate(running); - - let msg = coordinator_receive.recv().unwrap(); - match *msg.downcast::().ok().unwrap() { - // Save the token locally and the next turn of the loop will use - // this to spawn a new unit of work, or it may get dropped - // immediately if we have no more work to spawn. - Message::Token(token) => { - match token { - Ok(token) => { - tokens.push(token); - - if main_thread_worker_state == MainThreadWorkerState::LLVMing { - // If the main thread token is used for LLVM work - // at the moment, we turn that thread into a regular - // LLVM worker thread, so the main thread is free - // to react to translation demand. - main_thread_worker_state = MainThreadWorkerState::Idle; - running += 1; - } - } - Err(e) => { - let msg = &format!("failed to acquire jobserver token: {}", e); - shared_emitter.fatal(msg); - // Exit the coordinator thread - panic!("{}", msg) - } - } - } - - Message::TranslationDone { llvm_work_item, cost } => { - // We keep the queue sorted by estimated processing cost, - // so that more expensive items are processed earlier. This - // is good for throughput as it gives the main thread more - // time to fill up the queue and it avoids scheduling - // expensive items to the end. - // Note, however, that this is not ideal for memory - // consumption, as LLVM module sizes are not evenly - // distributed. - let insertion_index = - work_items.binary_search_by_key(&cost, |&(_, cost)| cost); - let insertion_index = match insertion_index { - Ok(idx) | Err(idx) => idx - }; - work_items.insert(insertion_index, (llvm_work_item, cost)); - - helper.request_token(); - assert_eq!(main_thread_worker_state, - MainThreadWorkerState::Translating); - main_thread_worker_state = MainThreadWorkerState::Idle; - } - - Message::TranslationComplete => { - translation_done = true; - assert_eq!(main_thread_worker_state, - MainThreadWorkerState::Translating); - main_thread_worker_state = MainThreadWorkerState::Idle; - } - - // If a thread exits successfully then we drop a token associated - // with that worker and update our `running` count. We may later - // re-acquire a token to continue running more work. We may also not - // actually drop a token here if the worker was running with an - // "ephemeral token" - // - // Note that if the thread failed that means it panicked, so we - // abort immediately. - Message::Done { result: Ok(compiled_module), worker_id } => { - if main_thread_worker_state == MainThreadWorkerState::LLVMing { - main_thread_worker_state = MainThreadWorkerState::Idle; - } else { - running -= 1; - } - - free_worker_ids.push(worker_id); - - match compiled_module.kind { - ModuleKind::Regular => { - compiled_modules.push(compiled_module); - } - ModuleKind::Metadata => { - assert!(compiled_metadata_module.is_none()); - compiled_metadata_module = Some(compiled_module); - } - ModuleKind::Allocator => { - assert!(compiled_allocator_module.is_none()); - compiled_allocator_module = Some(compiled_module); - } - } - } - Message::NeedsLTO { result, worker_id } => { - assert!(!started_lto); - if main_thread_worker_state == MainThreadWorkerState::LLVMing { - main_thread_worker_state = MainThreadWorkerState::Idle; - } else { - running -= 1; - } - - free_worker_ids.push(worker_id); - needs_lto.push(result); - } - Message::Done { result: Err(()), worker_id: _ } => { - shared_emitter.fatal("aborting due to worker thread failure"); - // Exit the coordinator thread - return Err(()) - } - Message::TranslateItem => { - bug!("the coordinator should not receive translation requests") - } - } - } - - if let Some(llvm_start_time) = llvm_start_time { - let total_llvm_time = Instant::now().duration_since(llvm_start_time); - // This is the top-level timing for all of LLVM, set the time-depth - // to zero. - set_time_depth(0); - print_time_passes_entry(cgcx.time_passes, - "LLVM passes", - total_llvm_time); - } - - // Regardless of what order these modules completed in, report them to - // the backend in the same order every time to ensure that we're handing - // out deterministic results. - compiled_modules.sort_by(|a, b| a.name.cmp(&b.name)); - - let compiled_metadata_module = compiled_metadata_module - .expect("Metadata module not compiled?"); - - Ok(CompiledModules { - modules: compiled_modules, - metadata_module: compiled_metadata_module, - allocator_module: compiled_allocator_module, - }) - }); - - // A heuristic that determines if we have enough LLVM WorkItems in the - // queue so that the main thread can do LLVM work instead of translation - fn queue_full_enough(items_in_queue: usize, - workers_running: usize, - max_workers: usize) -> bool { - // Tune me, plz. - items_in_queue > 0 && - items_in_queue >= max_workers.saturating_sub(workers_running / 2) - } - - fn maybe_start_llvm_timer(config: &ModuleConfig, - llvm_start_time: &mut Option) { - // We keep track of the -Ztime-passes output manually, - // since the closure-based interface does not fit well here. - if config.time_passes { - if llvm_start_time.is_none() { - *llvm_start_time = Some(Instant::now()); - } - } - } -} - -pub const TRANS_WORKER_ID: usize = ::std::usize::MAX; -pub const TRANS_WORKER_TIMELINE: time_graph::TimelineId = - time_graph::TimelineId(TRANS_WORKER_ID); -pub const TRANS_WORK_PACKAGE_KIND: time_graph::WorkPackageKind = - time_graph::WorkPackageKind(&["#DE9597", "#FED1D3", "#FDC5C7", "#B46668", "#88494B"]); -const LLVM_WORK_PACKAGE_KIND: time_graph::WorkPackageKind = - time_graph::WorkPackageKind(&["#7DB67A", "#C6EEC4", "#ACDAAA", "#579354", "#3E6F3C"]); - -fn spawn_work(cgcx: CodegenContext, work: WorkItem) { - let depth = time_depth(); - - thread::spawn(move || { - set_time_depth(depth); - - // Set up a destructor which will fire off a message that we're done as - // we exit. - struct Bomb { - coordinator_send: Sender>, - result: Option, - worker_id: usize, - } - impl Drop for Bomb { - fn drop(&mut self) { - let worker_id = self.worker_id; - let msg = match self.result.take() { - Some(WorkItemResult::Compiled(m)) => { - Message::Done { result: Ok(m), worker_id } - } - Some(WorkItemResult::NeedsLTO(m)) => { - Message::NeedsLTO { result: m, worker_id } - } - None => Message::Done { result: Err(()), worker_id } - }; - drop(self.coordinator_send.send(Box::new(msg))); - } - } - - let mut bomb = Bomb { - coordinator_send: cgcx.coordinator_send.clone(), - result: None, - worker_id: cgcx.worker, - }; - - // Execute the work itself, and if it finishes successfully then flag - // ourselves as a success as well. - // - // Note that we ignore any `FatalError` coming out of `execute_work_item`, - // as a diagnostic was already sent off to the main thread - just - // surface that there was an error in this worker. - bomb.result = { - let timeline = cgcx.time_graph.as_ref().map(|tg| { - tg.start(time_graph::TimelineId(cgcx.worker), - LLVM_WORK_PACKAGE_KIND, - &work.name()) - }); - let mut timeline = timeline.unwrap_or(Timeline::noop()); - execute_work_item(&cgcx, work, &mut timeline).ok() - }; - }); -} - -pub fn run_assembler(cgcx: &CodegenContext, handler: &Handler, assembly: &Path, object: &Path) { - let assembler = cgcx.assembler_cmd - .as_ref() - .expect("cgcx.assembler_cmd is missing?"); - - let pname = &assembler.name; - let mut cmd = assembler.cmd.clone(); - cmd.arg("-c").arg("-o").arg(object).arg(assembly); - debug!("{:?}", cmd); - - match cmd.output() { - Ok(prog) => { - if !prog.status.success() { - let mut note = prog.stderr.clone(); - note.extend_from_slice(&prog.stdout); - - handler.struct_err(&format!("linking with `{}` failed: {}", - pname.display(), - prog.status)) - .note(&format!("{:?}", &cmd)) - .note(str::from_utf8(¬e[..]).unwrap()) - .emit(); - handler.abort_if_errors(); - } - }, - Err(e) => { - handler.err(&format!("could not exec the linker `{}`: {}", pname.display(), e)); - handler.abort_if_errors(); - } - } -} - -pub unsafe fn with_llvm_pmb(llmod: ModuleRef, - config: &ModuleConfig, - opt_level: llvm::CodeGenOptLevel, - prepare_for_thin_lto: bool, - f: &mut FnMut(llvm::PassManagerBuilderRef)) { - use std::ptr; - - // Create the PassManagerBuilder for LLVM. We configure it with - // reasonable defaults and prepare it to actually populate the pass - // manager. - let builder = llvm::LLVMPassManagerBuilderCreate(); - let opt_size = config.opt_size.unwrap_or(llvm::CodeGenOptSizeNone); - let inline_threshold = config.inline_threshold; - - let pgo_gen_path = config.pgo_gen.as_ref().map(|s| { - let s = if s.is_empty() { "default_%m.profraw" } else { s }; - CString::new(s.as_bytes()).unwrap() - }); - - let pgo_use_path = if config.pgo_use.is_empty() { - None - } else { - Some(CString::new(config.pgo_use.as_bytes()).unwrap()) - }; - - llvm::LLVMRustConfigurePassManagerBuilder( - builder, - opt_level, - config.merge_functions, - config.vectorize_slp, - config.vectorize_loop, - prepare_for_thin_lto, - pgo_gen_path.as_ref().map_or(ptr::null(), |s| s.as_ptr()), - pgo_use_path.as_ref().map_or(ptr::null(), |s| s.as_ptr()), - ); - - llvm::LLVMPassManagerBuilderSetSizeLevel(builder, opt_size as u32); - - if opt_size != llvm::CodeGenOptSizeNone { - llvm::LLVMPassManagerBuilderSetDisableUnrollLoops(builder, 1); - } - - llvm::LLVMRustAddBuilderLibraryInfo(builder, llmod, config.no_builtins); - - // Here we match what clang does (kinda). For O0 we only inline - // always-inline functions (but don't add lifetime intrinsics), at O1 we - // inline with lifetime intrinsics, and O2+ we add an inliner with a - // thresholds copied from clang. - match (opt_level, opt_size, inline_threshold) { - (.., Some(t)) => { - llvm::LLVMPassManagerBuilderUseInlinerWithThreshold(builder, t as u32); - } - (llvm::CodeGenOptLevel::Aggressive, ..) => { - llvm::LLVMPassManagerBuilderUseInlinerWithThreshold(builder, 275); - } - (_, llvm::CodeGenOptSizeDefault, _) => { - llvm::LLVMPassManagerBuilderUseInlinerWithThreshold(builder, 75); - } - (_, llvm::CodeGenOptSizeAggressive, _) => { - llvm::LLVMPassManagerBuilderUseInlinerWithThreshold(builder, 25); - } - (llvm::CodeGenOptLevel::None, ..) => { - llvm::LLVMRustAddAlwaysInlinePass(builder, false); - } - (llvm::CodeGenOptLevel::Less, ..) => { - llvm::LLVMRustAddAlwaysInlinePass(builder, true); - } - (llvm::CodeGenOptLevel::Default, ..) => { - llvm::LLVMPassManagerBuilderUseInlinerWithThreshold(builder, 225); - } - (llvm::CodeGenOptLevel::Other, ..) => { - bug!("CodeGenOptLevel::Other selected") - } - } - - f(builder); - llvm::LLVMPassManagerBuilderDispose(builder); -} - - -enum SharedEmitterMessage { - Diagnostic(Diagnostic), - InlineAsmError(u32, String), - AbortIfErrors, - Fatal(String), -} - -#[derive(Clone)] -pub struct SharedEmitter { - sender: Sender, -} - -pub struct SharedEmitterMain { - receiver: Receiver, -} - -impl SharedEmitter { - pub fn new() -> (SharedEmitter, SharedEmitterMain) { - let (sender, receiver) = channel(); - - (SharedEmitter { sender }, SharedEmitterMain { receiver }) - } - - fn inline_asm_error(&self, cookie: u32, msg: String) { - drop(self.sender.send(SharedEmitterMessage::InlineAsmError(cookie, msg))); - } - - fn fatal(&self, msg: &str) { - drop(self.sender.send(SharedEmitterMessage::Fatal(msg.to_string()))); - } -} - -impl Emitter for SharedEmitter { - fn emit(&mut self, db: &DiagnosticBuilder) { - drop(self.sender.send(SharedEmitterMessage::Diagnostic(Diagnostic { - msg: db.message(), - code: db.code.clone(), - lvl: db.level, - }))); - for child in &db.children { - drop(self.sender.send(SharedEmitterMessage::Diagnostic(Diagnostic { - msg: child.message(), - code: None, - lvl: child.level, - }))); - } - drop(self.sender.send(SharedEmitterMessage::AbortIfErrors)); - } -} - -impl SharedEmitterMain { - pub fn check(&self, sess: &Session, blocking: bool) { - loop { - let message = if blocking { - match self.receiver.recv() { - Ok(message) => Ok(message), - Err(_) => Err(()), - } - } else { - match self.receiver.try_recv() { - Ok(message) => Ok(message), - Err(_) => Err(()), - } - }; - - match message { - Ok(SharedEmitterMessage::Diagnostic(diag)) => { - let handler = sess.diagnostic(); - match diag.code { - Some(ref code) => { - handler.emit_with_code(&MultiSpan::new(), - &diag.msg, - code.clone(), - diag.lvl); - } - None => { - handler.emit(&MultiSpan::new(), - &diag.msg, - diag.lvl); - } - } - } - Ok(SharedEmitterMessage::InlineAsmError(cookie, msg)) => { - match Mark::from_u32(cookie).expn_info() { - Some(ei) => sess.span_err(ei.call_site, &msg), - None => sess.err(&msg), - } - } - Ok(SharedEmitterMessage::AbortIfErrors) => { - sess.abort_if_errors(); - } - Ok(SharedEmitterMessage::Fatal(msg)) => { - sess.fatal(&msg); - } - Err(_) => { - break; - } - } - - } - } -} - -pub struct OngoingCrateTranslation { - crate_name: Symbol, - link: LinkMeta, - metadata: EncodedMetadata, - windows_subsystem: Option, - linker_info: LinkerInfo, - crate_info: CrateInfo, - time_graph: Option, - coordinator_send: Sender>, - trans_worker_receive: Receiver, - shared_emitter_main: SharedEmitterMain, - future: thread::JoinHandle>, - output_filenames: Arc, -} - -impl OngoingCrateTranslation { - pub(crate) fn join( - self, - sess: &Session - ) -> (CrateTranslation, FxHashMap) { - self.shared_emitter_main.check(sess, true); - let compiled_modules = match self.future.join() { - Ok(Ok(compiled_modules)) => compiled_modules, - Ok(Err(())) => { - sess.abort_if_errors(); - panic!("expected abort due to worker thread errors") - }, - Err(_) => { - sess.fatal("Error during translation/LLVM phase."); - } - }; - - sess.abort_if_errors(); - - if let Some(time_graph) = self.time_graph { - time_graph.dump(&format!("{}-timings", self.crate_name)); - } - - let work_products = copy_all_cgu_workproducts_to_incr_comp_cache_dir(sess, - &compiled_modules); - - produce_final_output_artifacts(sess, - &compiled_modules, - &self.output_filenames); - - // FIXME: time_llvm_passes support - does this use a global context or - // something? - if sess.codegen_units() == 1 && sess.time_llvm_passes() { - unsafe { llvm::LLVMRustPrintPassTimings(); } - } - - let trans = CrateTranslation { - crate_name: self.crate_name, - link: self.link, - metadata: self.metadata, - windows_subsystem: self.windows_subsystem, - linker_info: self.linker_info, - crate_info: self.crate_info, - - modules: compiled_modules.modules, - allocator_module: compiled_modules.allocator_module, - metadata_module: compiled_modules.metadata_module, - }; - - (trans, work_products) - } - - pub(crate) fn submit_pre_translated_module_to_llvm(&self, - tcx: TyCtxt, - mtrans: ModuleTranslation) { - self.wait_for_signal_to_translate_item(); - self.check_for_errors(tcx.sess); - - // These are generally cheap and won't through off scheduling. - let cost = 0; - submit_translated_module_to_llvm(tcx, mtrans, cost); - } - - pub fn translation_finished(&self, tcx: TyCtxt) { - self.wait_for_signal_to_translate_item(); - self.check_for_errors(tcx.sess); - drop(self.coordinator_send.send(Box::new(Message::TranslationComplete))); - } - - pub fn check_for_errors(&self, sess: &Session) { - self.shared_emitter_main.check(sess, false); - } - - pub fn wait_for_signal_to_translate_item(&self) { - match self.trans_worker_receive.recv() { - Ok(Message::TranslateItem) => { - // Nothing to do - } - Ok(_) => panic!("unexpected message"), - Err(_) => { - // One of the LLVM threads must have panicked, fall through so - // error handling can be reached. - } - } - } -} - -pub(crate) fn submit_translated_module_to_llvm(tcx: TyCtxt, - mtrans: ModuleTranslation, - cost: u64) { - let llvm_work_item = WorkItem::Optimize(mtrans); - drop(tcx.tx_to_llvm_workers.lock().send(Box::new(Message::TranslationDone { - llvm_work_item, - cost, - }))); -} - -fn msvc_imps_needed(tcx: TyCtxt) -> bool { - tcx.sess.target.target.options.is_like_msvc && - tcx.sess.crate_types.borrow().iter().any(|ct| *ct == config::CrateTypeRlib) -} - -// Create a `__imp_ = &symbol` global for every public static `symbol`. -// This is required to satisfy `dllimport` references to static data in .rlibs -// when using MSVC linker. We do this only for data, as linker can fix up -// code references on its own. -// See #26591, #27438 -fn create_msvc_imps(cgcx: &CodegenContext, llcx: ContextRef, llmod: ModuleRef) { - if !cgcx.msvc_imps_needed { - return - } - // The x86 ABI seems to require that leading underscores are added to symbol - // names, so we need an extra underscore on 32-bit. There's also a leading - // '\x01' here which disables LLVM's symbol mangling (e.g. no extra - // underscores added in front). - let prefix = if cgcx.target_pointer_width == "32" { - "\x01__imp__" - } else { - "\x01__imp_" - }; - unsafe { - let i8p_ty = Type::i8p_llcx(llcx); - let globals = base::iter_globals(llmod) - .filter(|&val| { - llvm::LLVMRustGetLinkage(val) == llvm::Linkage::ExternalLinkage && - llvm::LLVMIsDeclaration(val) == 0 - }) - .map(move |val| { - let name = CStr::from_ptr(llvm::LLVMGetValueName(val)); - let mut imp_name = prefix.as_bytes().to_vec(); - imp_name.extend(name.to_bytes()); - let imp_name = CString::new(imp_name).unwrap(); - (imp_name, val) - }) - .collect::>(); - for (imp_name, val) in globals { - let imp = llvm::LLVMAddGlobal(llmod, - i8p_ty.to_ref(), - imp_name.as_ptr() as *const _); - llvm::LLVMSetInitializer(imp, consts::ptrcast(val, i8p_ty)); - llvm::LLVMRustSetLinkage(imp, llvm::Linkage::ExternalLinkage); - } - } -} diff --git a/src/librustc_trans/base.rs b/src/librustc_trans/base.rs deleted file mode 100644 index feca36fa6c2..00000000000 --- a/src/librustc_trans/base.rs +++ /dev/null @@ -1,1411 +0,0 @@ -// Copyright 2012-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. - -//! Translate the completed AST to the LLVM IR. -//! -//! Some functions here, such as trans_block and trans_expr, return a value -- -//! the result of the translation to LLVM -- while others, such as trans_fn -//! and trans_item, are called only for the side effect of adding a -//! particular definition to the LLVM IR output we're producing. -//! -//! Hopefully useful general knowledge about trans: -//! -//! * There's no way to find out the Ty type of a ValueRef. Doing so -//! would be "trying to get the eggs out of an omelette" (credit: -//! pcwalton). You can, instead, find out its TypeRef by calling val_ty, -//! but one TypeRef corresponds to many `Ty`s; for instance, tup(int, int, -//! int) and rec(x=int, y=int, z=int) will have the same TypeRef. - -use super::ModuleLlvm; -use super::ModuleSource; -use super::ModuleTranslation; -use super::ModuleKind; - -use abi; -use back::link; -use back::write::{self, OngoingCrateTranslation, create_target_machine}; -use llvm::{ContextRef, ModuleRef, ValueRef, Vector, get_param}; -use llvm; -use metadata; -use rustc::hir::def_id::{CrateNum, DefId, LOCAL_CRATE}; -use rustc::middle::lang_items::StartFnLangItem; -use rustc::middle::weak_lang_items; -use rustc::mir::mono::{Linkage, Visibility, Stats}; -use rustc::middle::cstore::{EncodedMetadata}; -use rustc::ty::{self, Ty, TyCtxt}; -use rustc::ty::layout::{self, Align, TyLayout, LayoutOf}; -use rustc::ty::maps::Providers; -use rustc::dep_graph::{DepNode, DepConstructor}; -use rustc::ty::subst::Kind; -use rustc::middle::cstore::{self, LinkMeta, LinkagePreference}; -use rustc::middle::exported_symbols; -use rustc::util::common::{time, print_time_passes_entry}; -use rustc::session::config::{self, NoDebugInfo}; -use rustc::session::Session; -use rustc_incremental; -use allocator; -use mir::place::PlaceRef; -use attributes; -use builder::{Builder, MemFlags}; -use callee; -use common::{C_bool, C_bytes_in_context, C_i32, C_usize}; -use rustc_mir::monomorphize::collector::{self, MonoItemCollectionMode}; -use common::{self, C_struct_in_context, C_array, val_ty}; -use consts; -use context::{self, CodegenCx}; -use debuginfo; -use declare; -use meth; -use mir; -use monomorphize::Instance; -use monomorphize::partitioning::{self, PartitioningStrategy, CodegenUnit, CodegenUnitExt}; -use rustc_trans_utils::symbol_names_test; -use time_graph; -use trans_item::{MonoItem, BaseMonoItemExt, MonoItemExt, DefPathBasedNames}; -use type_::Type; -use type_of::LayoutLlvmExt; -use rustc::util::nodemap::{FxHashMap, FxHashSet, DefIdSet}; -use CrateInfo; -use rustc_data_structures::sync::Lrc; -use rustc_target::spec::TargetTriple; - -use std::any::Any; -use std::collections::BTreeMap; -use std::ffi::CString; -use std::str; -use std::sync::Arc; -use std::time::{Instant, Duration}; -use std::i32; -use std::cmp; -use std::sync::mpsc; -use syntax_pos::Span; -use syntax_pos::symbol::InternedString; -use syntax::attr; -use rustc::hir; -use syntax::ast; - -use mir::operand::OperandValue; - -pub use rustc_trans_utils::check_for_rustc_errors_attr; - -pub struct StatRecorder<'a, 'tcx: 'a> { - cx: &'a CodegenCx<'a, 'tcx>, - name: Option, - istart: usize, -} - -impl<'a, 'tcx> StatRecorder<'a, 'tcx> { - pub fn new(cx: &'a CodegenCx<'a, 'tcx>, name: String) -> StatRecorder<'a, 'tcx> { - let istart = cx.stats.borrow().n_llvm_insns; - StatRecorder { - cx, - name: Some(name), - istart, - } - } -} - -impl<'a, 'tcx> Drop for StatRecorder<'a, 'tcx> { - fn drop(&mut self) { - if self.cx.sess().trans_stats() { - let mut stats = self.cx.stats.borrow_mut(); - let iend = stats.n_llvm_insns; - stats.fn_stats.push((self.name.take().unwrap(), iend - self.istart)); - stats.n_fns += 1; - // Reset LLVM insn count to avoid compound costs. - stats.n_llvm_insns = self.istart; - } - } -} - -pub fn bin_op_to_icmp_predicate(op: hir::BinOp_, - signed: bool) - -> llvm::IntPredicate { - match op { - hir::BiEq => llvm::IntEQ, - hir::BiNe => llvm::IntNE, - hir::BiLt => if signed { llvm::IntSLT } else { llvm::IntULT }, - hir::BiLe => if signed { llvm::IntSLE } else { llvm::IntULE }, - hir::BiGt => if signed { llvm::IntSGT } else { llvm::IntUGT }, - hir::BiGe => if signed { llvm::IntSGE } else { llvm::IntUGE }, - op => { - bug!("comparison_op_to_icmp_predicate: expected comparison operator, \ - found {:?}", - op) - } - } -} - -pub fn bin_op_to_fcmp_predicate(op: hir::BinOp_) -> llvm::RealPredicate { - match op { - hir::BiEq => llvm::RealOEQ, - hir::BiNe => llvm::RealUNE, - hir::BiLt => llvm::RealOLT, - hir::BiLe => llvm::RealOLE, - hir::BiGt => llvm::RealOGT, - hir::BiGe => llvm::RealOGE, - op => { - bug!("comparison_op_to_fcmp_predicate: expected comparison operator, \ - found {:?}", - op); - } - } -} - -pub fn compare_simd_types<'a, 'tcx>( - bx: &Builder<'a, 'tcx>, - lhs: ValueRef, - rhs: ValueRef, - t: Ty<'tcx>, - ret_ty: Type, - op: hir::BinOp_ -) -> ValueRef { - let signed = match t.sty { - ty::TyFloat(_) => { - let cmp = bin_op_to_fcmp_predicate(op); - return bx.sext(bx.fcmp(cmp, lhs, rhs), ret_ty); - }, - ty::TyUint(_) => false, - ty::TyInt(_) => true, - _ => bug!("compare_simd_types: invalid SIMD type"), - }; - - let cmp = bin_op_to_icmp_predicate(op, signed); - // LLVM outputs an `< size x i1 >`, so we need to perform a sign extension - // to get the correctly sized type. This will compile to a single instruction - // once the IR is converted to assembly if the SIMD instruction is supported - // by the target architecture. - bx.sext(bx.icmp(cmp, lhs, rhs), ret_ty) -} - -/// Retrieve the information we are losing (making dynamic) in an unsizing -/// adjustment. -/// -/// The `old_info` argument is a bit funny. It is intended for use -/// in an upcast, where the new vtable for an object will be derived -/// from the old one. -pub fn unsized_info<'cx, 'tcx>(cx: &CodegenCx<'cx, 'tcx>, - source: Ty<'tcx>, - target: Ty<'tcx>, - old_info: Option) - -> ValueRef { - let (source, target) = cx.tcx.struct_lockstep_tails(source, target); - match (&source.sty, &target.sty) { - (&ty::TyArray(_, len), &ty::TySlice(_)) => { - C_usize(cx, len.unwrap_usize(cx.tcx)) - } - (&ty::TyDynamic(..), &ty::TyDynamic(..)) => { - // For now, upcasts are limited to changes in marker - // traits, and hence never actually require an actual - // change to the vtable. - old_info.expect("unsized_info: missing old info for trait upcast") - } - (_, &ty::TyDynamic(ref data, ..)) => { - let vtable_ptr = cx.layout_of(cx.tcx.mk_mut_ptr(target)) - .field(cx, abi::FAT_PTR_EXTRA); - consts::ptrcast(meth::get_vtable(cx, source, data.principal()), - vtable_ptr.llvm_type(cx)) - } - _ => bug!("unsized_info: invalid unsizing {:?} -> {:?}", - source, - target), - } -} - -/// Coerce `src` to `dst_ty`. `src_ty` must be a thin pointer. -pub fn unsize_thin_ptr<'a, 'tcx>( - bx: &Builder<'a, 'tcx>, - src: ValueRef, - src_ty: Ty<'tcx>, - dst_ty: Ty<'tcx> -) -> (ValueRef, ValueRef) { - debug!("unsize_thin_ptr: {:?} => {:?}", src_ty, dst_ty); - match (&src_ty.sty, &dst_ty.sty) { - (&ty::TyRef(_, a, _), - &ty::TyRef(_, b, _)) | - (&ty::TyRef(_, a, _), - &ty::TyRawPtr(ty::TypeAndMut { ty: b, .. })) | - (&ty::TyRawPtr(ty::TypeAndMut { ty: a, .. }), - &ty::TyRawPtr(ty::TypeAndMut { ty: b, .. })) => { - assert!(bx.cx.type_is_sized(a)); - let ptr_ty = bx.cx.layout_of(b).llvm_type(bx.cx).ptr_to(); - (bx.pointercast(src, ptr_ty), unsized_info(bx.cx, a, b, None)) - } - (&ty::TyAdt(def_a, _), &ty::TyAdt(def_b, _)) if def_a.is_box() && def_b.is_box() => { - let (a, b) = (src_ty.boxed_ty(), dst_ty.boxed_ty()); - assert!(bx.cx.type_is_sized(a)); - let ptr_ty = bx.cx.layout_of(b).llvm_type(bx.cx).ptr_to(); - (bx.pointercast(src, ptr_ty), unsized_info(bx.cx, a, b, None)) - } - (&ty::TyAdt(def_a, _), &ty::TyAdt(def_b, _)) => { - assert_eq!(def_a, def_b); - - let src_layout = bx.cx.layout_of(src_ty); - let dst_layout = bx.cx.layout_of(dst_ty); - let mut result = None; - for i in 0..src_layout.fields.count() { - let src_f = src_layout.field(bx.cx, i); - assert_eq!(src_layout.fields.offset(i).bytes(), 0); - assert_eq!(dst_layout.fields.offset(i).bytes(), 0); - if src_f.is_zst() { - continue; - } - assert_eq!(src_layout.size, src_f.size); - - let dst_f = dst_layout.field(bx.cx, i); - assert_ne!(src_f.ty, dst_f.ty); - assert_eq!(result, None); - result = Some(unsize_thin_ptr(bx, src, src_f.ty, dst_f.ty)); - } - let (lldata, llextra) = result.unwrap(); - // HACK(eddyb) have to bitcast pointers until LLVM removes pointee types. - (bx.bitcast(lldata, dst_layout.scalar_pair_element_llvm_type(bx.cx, 0)), - bx.bitcast(llextra, dst_layout.scalar_pair_element_llvm_type(bx.cx, 1))) - } - _ => bug!("unsize_thin_ptr: called on bad types"), - } -} - -/// Coerce `src`, which is a reference to a value of type `src_ty`, -/// to a value of type `dst_ty` and store the result in `dst` -pub fn coerce_unsized_into<'a, 'tcx>(bx: &Builder<'a, 'tcx>, - src: PlaceRef<'tcx>, - dst: PlaceRef<'tcx>) { - let src_ty = src.layout.ty; - let dst_ty = dst.layout.ty; - let coerce_ptr = || { - let (base, info) = match src.load(bx).val { - OperandValue::Pair(base, info) => { - // fat-ptr to fat-ptr unsize preserves the vtable - // i.e. &'a fmt::Debug+Send => &'a fmt::Debug - // So we need to pointercast the base to ensure - // the types match up. - let thin_ptr = dst.layout.field(bx.cx, abi::FAT_PTR_ADDR); - (bx.pointercast(base, thin_ptr.llvm_type(bx.cx)), info) - } - OperandValue::Immediate(base) => { - unsize_thin_ptr(bx, base, src_ty, dst_ty) - } - OperandValue::Ref(..) => bug!() - }; - OperandValue::Pair(base, info).store(bx, dst); - }; - match (&src_ty.sty, &dst_ty.sty) { - (&ty::TyRef(..), &ty::TyRef(..)) | - (&ty::TyRef(..), &ty::TyRawPtr(..)) | - (&ty::TyRawPtr(..), &ty::TyRawPtr(..)) => { - coerce_ptr() - } - (&ty::TyAdt(def_a, _), &ty::TyAdt(def_b, _)) if def_a.is_box() && def_b.is_box() => { - coerce_ptr() - } - - (&ty::TyAdt(def_a, _), &ty::TyAdt(def_b, _)) => { - assert_eq!(def_a, def_b); - - for i in 0..def_a.variants[0].fields.len() { - let src_f = src.project_field(bx, i); - let dst_f = dst.project_field(bx, i); - - if dst_f.layout.is_zst() { - continue; - } - - if src_f.layout.ty == dst_f.layout.ty { - memcpy_ty(bx, dst_f.llval, src_f.llval, src_f.layout, - src_f.align.min(dst_f.align), MemFlags::empty()); - } else { - coerce_unsized_into(bx, src_f, dst_f); - } - } - } - _ => bug!("coerce_unsized_into: invalid coercion {:?} -> {:?}", - src_ty, - dst_ty), - } -} - -pub fn cast_shift_expr_rhs( - cx: &Builder, op: hir::BinOp_, lhs: ValueRef, rhs: ValueRef -) -> ValueRef { - cast_shift_rhs(op, lhs, rhs, |a, b| cx.trunc(a, b), |a, b| cx.zext(a, b)) -} - -fn cast_shift_rhs(op: hir::BinOp_, - lhs: ValueRef, - rhs: ValueRef, - trunc: F, - zext: G) - -> ValueRef - where F: FnOnce(ValueRef, Type) -> ValueRef, - G: FnOnce(ValueRef, Type) -> ValueRef -{ - // Shifts may have any size int on the rhs - if op.is_shift() { - let mut rhs_llty = val_ty(rhs); - let mut lhs_llty = val_ty(lhs); - if rhs_llty.kind() == Vector { - rhs_llty = rhs_llty.element_type() - } - if lhs_llty.kind() == Vector { - lhs_llty = lhs_llty.element_type() - } - let rhs_sz = rhs_llty.int_width(); - let lhs_sz = lhs_llty.int_width(); - if lhs_sz < rhs_sz { - trunc(rhs, lhs_llty) - } else if lhs_sz > rhs_sz { - // FIXME (#1877: If shifting by negative - // values becomes not undefined then this is wrong. - zext(rhs, lhs_llty) - } else { - rhs - } - } else { - rhs - } -} - -/// Returns whether this session's target will use SEH-based unwinding. -/// -/// This is only true for MSVC targets, and even then the 64-bit MSVC target -/// currently uses SEH-ish unwinding with DWARF info tables to the side (same as -/// 64-bit MinGW) instead of "full SEH". -pub fn wants_msvc_seh(sess: &Session) -> bool { - sess.target.target.options.is_like_msvc -} - -pub fn call_assume<'a, 'tcx>(bx: &Builder<'a, 'tcx>, val: ValueRef) { - let assume_intrinsic = bx.cx.get_intrinsic("llvm.assume"); - bx.call(assume_intrinsic, &[val], None); -} - -pub fn from_immediate(bx: &Builder, val: ValueRef) -> ValueRef { - if val_ty(val) == Type::i1(bx.cx) { - bx.zext(val, Type::i8(bx.cx)) - } else { - val - } -} - -pub fn to_immediate(bx: &Builder, val: ValueRef, layout: layout::TyLayout) -> ValueRef { - if let layout::Abi::Scalar(ref scalar) = layout.abi { - if scalar.is_bool() { - return bx.trunc(val, Type::i1(bx.cx)); - } - } - val -} - -pub fn call_memcpy(bx: &Builder, - dst: ValueRef, - src: ValueRef, - n_bytes: ValueRef, - align: Align, - flags: MemFlags) { - if flags.contains(MemFlags::NONTEMPORAL) { - // HACK(nox): This is inefficient but there is no nontemporal memcpy. - let val = bx.load(src, align); - let ptr = bx.pointercast(dst, val_ty(val).ptr_to()); - bx.store_with_flags(val, ptr, align, flags); - return; - } - let cx = bx.cx; - let ptr_width = &cx.sess().target.target.target_pointer_width; - let key = format!("llvm.memcpy.p0i8.p0i8.i{}", ptr_width); - let memcpy = cx.get_intrinsic(&key); - let src_ptr = bx.pointercast(src, Type::i8p(cx)); - let dst_ptr = bx.pointercast(dst, Type::i8p(cx)); - let size = bx.intcast(n_bytes, cx.isize_ty, false); - let align = C_i32(cx, align.abi() as i32); - let volatile = C_bool(cx, flags.contains(MemFlags::VOLATILE)); - bx.call(memcpy, &[dst_ptr, src_ptr, size, align, volatile], None); -} - -pub fn memcpy_ty<'a, 'tcx>( - bx: &Builder<'a, 'tcx>, - dst: ValueRef, - src: ValueRef, - layout: TyLayout<'tcx>, - align: Align, - flags: MemFlags, -) { - let size = layout.size.bytes(); - if size == 0 { - return; - } - - call_memcpy(bx, dst, src, C_usize(bx.cx, size), align, flags); -} - -pub fn call_memset<'a, 'tcx>(bx: &Builder<'a, 'tcx>, - ptr: ValueRef, - fill_byte: ValueRef, - size: ValueRef, - align: ValueRef, - volatile: bool) -> ValueRef { - let ptr_width = &bx.cx.sess().target.target.target_pointer_width; - let intrinsic_key = format!("llvm.memset.p0i8.i{}", ptr_width); - let llintrinsicfn = bx.cx.get_intrinsic(&intrinsic_key); - let volatile = C_bool(bx.cx, volatile); - bx.call(llintrinsicfn, &[ptr, fill_byte, size, align, volatile], None) -} - -pub fn trans_instance<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, instance: Instance<'tcx>) { - let _s = if cx.sess().trans_stats() { - let mut instance_name = String::new(); - DefPathBasedNames::new(cx.tcx, true, true) - .push_def_path(instance.def_id(), &mut instance_name); - Some(StatRecorder::new(cx, instance_name)) - } else { - None - }; - - // this is an info! to allow collecting monomorphization statistics - // and to allow finding the last function before LLVM aborts from - // release builds. - info!("trans_instance({})", instance); - - let fn_ty = instance.ty(cx.tcx); - let sig = common::ty_fn_sig(cx, fn_ty); - let sig = cx.tcx.normalize_erasing_late_bound_regions(ty::ParamEnv::reveal_all(), &sig); - - let lldecl = match cx.instances.borrow().get(&instance) { - Some(&val) => val, - None => bug!("Instance `{:?}` not already declared", instance) - }; - - cx.stats.borrow_mut().n_closures += 1; - - // The `uwtable` attribute according to LLVM is: - // - // This attribute indicates that the ABI being targeted requires that an - // unwind table entry be produced for this function even if we can show - // that no exceptions passes by it. This is normally the case for the - // ELF x86-64 abi, but it can be disabled for some compilation units. - // - // Typically when we're compiling with `-C panic=abort` (which implies this - // `no_landing_pads` check) we don't need `uwtable` because we can't - // generate any exceptions! On Windows, however, exceptions include other - // events such as illegal instructions, segfaults, etc. This means that on - // Windows we end up still needing the `uwtable` attribute even if the `-C - // panic=abort` flag is passed. - // - // You can also find more info on why Windows is whitelisted here in: - // https://bugzilla.mozilla.org/show_bug.cgi?id=1302078 - if !cx.sess().no_landing_pads() || - cx.sess().target.target.options.requires_uwtable { - attributes::emit_uwtable(lldecl, true); - } - - let mir = cx.tcx.instance_mir(instance.def); - mir::trans_mir(cx, lldecl, &mir, instance, sig); -} - -pub fn set_link_section(cx: &CodegenCx, - llval: ValueRef, - attrs: &[ast::Attribute]) { - if let Some(sect) = attr::first_attr_value_str_by_name(attrs, "link_section") { - if contains_null(§.as_str()) { - cx.sess().fatal(&format!("Illegal null byte in link_section value: `{}`", §)); - } - unsafe { - let buf = CString::new(sect.as_str().as_bytes()).unwrap(); - llvm::LLVMSetSection(llval, buf.as_ptr()); - } - } -} - -/// Create the `main` function which will initialize the rust runtime and call -/// users main function. -fn maybe_create_entry_wrapper(cx: &CodegenCx) { - let (main_def_id, span) = match *cx.sess().entry_fn.borrow() { - Some((id, span, _)) => { - (cx.tcx.hir.local_def_id(id), span) - } - None => return, - }; - - let instance = Instance::mono(cx.tcx, main_def_id); - - if !cx.codegen_unit.contains_item(&MonoItem::Fn(instance)) { - // We want to create the wrapper in the same codegen unit as Rust's main - // function. - return; - } - - let main_llfn = callee::get_fn(cx, instance); - - let et = cx.sess().entry_fn.get().map(|e| e.2); - match et { - Some(config::EntryMain) => create_entry_fn(cx, span, main_llfn, main_def_id, true), - Some(config::EntryStart) => create_entry_fn(cx, span, main_llfn, main_def_id, false), - None => {} // Do nothing. - } - - fn create_entry_fn<'cx>(cx: &'cx CodegenCx, - sp: Span, - rust_main: ValueRef, - rust_main_def_id: DefId, - use_start_lang_item: bool) { - let llfty = Type::func(&[Type::c_int(cx), Type::i8p(cx).ptr_to()], &Type::c_int(cx)); - - let main_ret_ty = cx.tcx.fn_sig(rust_main_def_id).output(); - // Given that `main()` has no arguments, - // then its return type cannot have - // late-bound regions, since late-bound - // regions must appear in the argument - // listing. - let main_ret_ty = cx.tcx.erase_regions( - &main_ret_ty.no_late_bound_regions().unwrap(), - ); - - if declare::get_defined_value(cx, "main").is_some() { - // FIXME: We should be smart and show a better diagnostic here. - cx.sess().struct_span_err(sp, "entry symbol `main` defined multiple times") - .help("did you use #[no_mangle] on `fn main`? Use #[start] instead") - .emit(); - cx.sess().abort_if_errors(); - bug!(); - } - let llfn = declare::declare_cfn(cx, "main", llfty); - - // `main` should respect same config for frame pointer elimination as rest of code - attributes::set_frame_pointer_elimination(cx, llfn); - - let bx = Builder::new_block(cx, llfn, "top"); - - debuginfo::gdb::insert_reference_to_gdb_debug_scripts_section_global(&bx); - - // Params from native main() used as args for rust start function - let param_argc = get_param(llfn, 0); - let param_argv = get_param(llfn, 1); - let arg_argc = bx.intcast(param_argc, cx.isize_ty, true); - let arg_argv = param_argv; - - let (start_fn, args) = if use_start_lang_item { - let start_def_id = cx.tcx.require_lang_item(StartFnLangItem); - let start_fn = callee::resolve_and_get_fn( - cx, - start_def_id, - cx.tcx.intern_substs(&[Kind::from(main_ret_ty)]), - ); - (start_fn, vec![bx.pointercast(rust_main, Type::i8p(cx).ptr_to()), - arg_argc, arg_argv]) - } else { - debug!("using user-defined start fn"); - (rust_main, vec![arg_argc, arg_argv]) - }; - - let result = bx.call(start_fn, &args, None); - bx.ret(bx.intcast(result, Type::c_int(cx), true)); - } -} - -fn contains_null(s: &str) -> bool { - s.bytes().any(|b| b == 0) -} - -fn write_metadata<'a, 'gcx>(tcx: TyCtxt<'a, 'gcx, 'gcx>, - llmod_id: &str, - link_meta: &LinkMeta) - -> (ContextRef, ModuleRef, EncodedMetadata) { - use std::io::Write; - use flate2::Compression; - use flate2::write::DeflateEncoder; - - let (metadata_llcx, metadata_llmod) = unsafe { - context::create_context_and_module(tcx.sess, llmod_id) - }; - - #[derive(PartialEq, Eq, PartialOrd, Ord)] - enum MetadataKind { - None, - Uncompressed, - Compressed - } - - let kind = tcx.sess.crate_types.borrow().iter().map(|ty| { - match *ty { - config::CrateTypeExecutable | - config::CrateTypeStaticlib | - config::CrateTypeCdylib => MetadataKind::None, - - config::CrateTypeRlib => MetadataKind::Uncompressed, - - config::CrateTypeDylib | - config::CrateTypeProcMacro => MetadataKind::Compressed, - } - }).max().unwrap(); - - if kind == MetadataKind::None { - return (metadata_llcx, - metadata_llmod, - EncodedMetadata::new()); - } - - let metadata = tcx.encode_metadata(link_meta); - if kind == MetadataKind::Uncompressed { - return (metadata_llcx, metadata_llmod, metadata); - } - - assert!(kind == MetadataKind::Compressed); - let mut compressed = tcx.metadata_encoding_version(); - DeflateEncoder::new(&mut compressed, Compression::fast()) - .write_all(&metadata.raw_data).unwrap(); - - let llmeta = C_bytes_in_context(metadata_llcx, &compressed); - let llconst = C_struct_in_context(metadata_llcx, &[llmeta], false); - let name = exported_symbols::metadata_symbol_name(tcx); - let buf = CString::new(name).unwrap(); - let llglobal = unsafe { - llvm::LLVMAddGlobal(metadata_llmod, val_ty(llconst).to_ref(), buf.as_ptr()) - }; - unsafe { - llvm::LLVMSetInitializer(llglobal, llconst); - let section_name = metadata::metadata_section_name(&tcx.sess.target.target); - let name = CString::new(section_name).unwrap(); - llvm::LLVMSetSection(llglobal, name.as_ptr()); - - // Also generate a .section directive to force no - // flags, at least for ELF outputs, so that the - // metadata doesn't get loaded into memory. - let directive = format!(".section {}", section_name); - let directive = CString::new(directive).unwrap(); - llvm::LLVMSetModuleInlineAsm(metadata_llmod, directive.as_ptr()) - } - return (metadata_llcx, metadata_llmod, metadata); -} - -pub struct ValueIter { - cur: ValueRef, - step: unsafe extern "C" fn(ValueRef) -> ValueRef, -} - -impl Iterator for ValueIter { - type Item = ValueRef; - - fn next(&mut self) -> Option { - let old = self.cur; - if !old.is_null() { - self.cur = unsafe { (self.step)(old) }; - Some(old) - } else { - None - } - } -} - -pub fn iter_globals(llmod: llvm::ModuleRef) -> ValueIter { - unsafe { - ValueIter { - cur: llvm::LLVMGetFirstGlobal(llmod), - step: llvm::LLVMGetNextGlobal, - } - } -} - -pub fn trans_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, - rx: mpsc::Receiver>) - -> OngoingCrateTranslation { - - check_for_rustc_errors_attr(tcx); - - if let Some(true) = tcx.sess.opts.debugging_opts.thinlto { - if unsafe { !llvm::LLVMRustThinLTOAvailable() } { - tcx.sess.fatal("this compiler's LLVM does not support ThinLTO"); - } - } - - if (tcx.sess.opts.debugging_opts.pgo_gen.is_some() || - !tcx.sess.opts.debugging_opts.pgo_use.is_empty()) && - unsafe { !llvm::LLVMRustPGOAvailable() } - { - tcx.sess.fatal("this compiler's LLVM does not support PGO"); - } - - let crate_hash = tcx.crate_hash(LOCAL_CRATE); - let link_meta = link::build_link_meta(crate_hash); - - // Translate the metadata. - let llmod_id = "metadata"; - let (metadata_llcx, metadata_llmod, metadata) = - time(tcx.sess, "write metadata", || { - write_metadata(tcx, llmod_id, &link_meta) - }); - - let metadata_module = ModuleTranslation { - name: link::METADATA_MODULE_NAME.to_string(), - llmod_id: llmod_id.to_string(), - source: ModuleSource::Translated(ModuleLlvm { - llcx: metadata_llcx, - llmod: metadata_llmod, - tm: create_target_machine(tcx.sess, false), - }), - kind: ModuleKind::Metadata, - }; - - let time_graph = if tcx.sess.opts.debugging_opts.trans_time_graph { - Some(time_graph::TimeGraph::new()) - } else { - None - }; - - // Skip crate items and just output metadata in -Z no-trans mode. - if tcx.sess.opts.debugging_opts.no_trans || - !tcx.sess.opts.output_types.should_trans() { - let ongoing_translation = write::start_async_translation( - tcx, - time_graph.clone(), - link_meta, - metadata, - rx, - 1); - - ongoing_translation.submit_pre_translated_module_to_llvm(tcx, metadata_module); - ongoing_translation.translation_finished(tcx); - - assert_and_save_dep_graph(tcx); - - ongoing_translation.check_for_errors(tcx.sess); - - return ongoing_translation; - } - - // Run the translation item collector and partition the collected items into - // codegen units. - let codegen_units = - tcx.collect_and_partition_translation_items(LOCAL_CRATE).1; - let codegen_units = (*codegen_units).clone(); - - // Force all codegen_unit queries so they are already either red or green - // when compile_codegen_unit accesses them. We are not able to re-execute - // the codegen_unit query from just the DepNode, so an unknown color would - // lead to having to re-execute compile_codegen_unit, possibly - // unnecessarily. - if tcx.dep_graph.is_fully_enabled() { - for cgu in &codegen_units { - tcx.codegen_unit(cgu.name().clone()); - } - } - - let ongoing_translation = write::start_async_translation( - tcx, - time_graph.clone(), - link_meta, - metadata, - rx, - codegen_units.len()); - - // Translate an allocator shim, if any - let allocator_module = if let Some(kind) = *tcx.sess.allocator_kind.get() { - unsafe { - let llmod_id = "allocator"; - let (llcx, llmod) = - context::create_context_and_module(tcx.sess, llmod_id); - let modules = ModuleLlvm { - llmod, - llcx, - tm: create_target_machine(tcx.sess, false), - }; - time(tcx.sess, "write allocator module", || { - allocator::trans(tcx, &modules, kind) - }); - - Some(ModuleTranslation { - name: link::ALLOCATOR_MODULE_NAME.to_string(), - llmod_id: llmod_id.to_string(), - source: ModuleSource::Translated(modules), - kind: ModuleKind::Allocator, - }) - } - } else { - None - }; - - if let Some(allocator_module) = allocator_module { - ongoing_translation.submit_pre_translated_module_to_llvm(tcx, allocator_module); - } - - ongoing_translation.submit_pre_translated_module_to_llvm(tcx, metadata_module); - - // We sort the codegen units by size. This way we can schedule work for LLVM - // a bit more efficiently. - let codegen_units = { - let mut codegen_units = codegen_units; - codegen_units.sort_by_cached_key(|cgu| cmp::Reverse(cgu.size_estimate())); - codegen_units - }; - - let mut total_trans_time = Duration::new(0, 0); - let mut all_stats = Stats::default(); - - for cgu in codegen_units.into_iter() { - ongoing_translation.wait_for_signal_to_translate_item(); - ongoing_translation.check_for_errors(tcx.sess); - - // First, if incremental compilation is enabled, we try to re-use the - // codegen unit from the cache. - if tcx.dep_graph.is_fully_enabled() { - let cgu_id = cgu.work_product_id(); - - // Check whether there is a previous work-product we can - // re-use. Not only must the file exist, and the inputs not - // be dirty, but the hash of the symbols we will generate must - // be the same. - if let Some(buf) = tcx.dep_graph.previous_work_product(&cgu_id) { - let dep_node = &DepNode::new(tcx, - DepConstructor::CompileCodegenUnit(cgu.name().clone())); - - // We try to mark the DepNode::CompileCodegenUnit green. If we - // succeed it means that none of the dependencies has changed - // and we can safely re-use. - if let Some(dep_node_index) = tcx.dep_graph.try_mark_green(tcx, dep_node) { - // Append ".rs" to LLVM module identifier. - // - // LLVM code generator emits a ".file filename" directive - // for ELF backends. Value of the "filename" is set as the - // LLVM module identifier. Due to a LLVM MC bug[1], LLVM - // crashes if the module identifier is same as other symbols - // such as a function name in the module. - // 1. http://llvm.org/bugs/show_bug.cgi?id=11479 - let llmod_id = format!("{}.rs", cgu.name()); - - let module = ModuleTranslation { - name: cgu.name().to_string(), - source: ModuleSource::Preexisting(buf), - kind: ModuleKind::Regular, - llmod_id, - }; - tcx.dep_graph.mark_loaded_from_cache(dep_node_index, true); - write::submit_translated_module_to_llvm(tcx, module, 0); - // Continue to next cgu, this one is done. - continue - } - } else { - // This can happen if files were deleted from the cache - // directory for some reason. We just re-compile then. - } - } - - let _timing_guard = time_graph.as_ref().map(|time_graph| { - time_graph.start(write::TRANS_WORKER_TIMELINE, - write::TRANS_WORK_PACKAGE_KIND, - &format!("codegen {}", cgu.name())) - }); - let start_time = Instant::now(); - all_stats.extend(tcx.compile_codegen_unit(*cgu.name())); - total_trans_time += start_time.elapsed(); - ongoing_translation.check_for_errors(tcx.sess); - } - - ongoing_translation.translation_finished(tcx); - - // Since the main thread is sometimes blocked during trans, we keep track - // -Ztime-passes output manually. - print_time_passes_entry(tcx.sess.time_passes(), - "translate to LLVM IR", - total_trans_time); - - if tcx.sess.opts.incremental.is_some() { - ::rustc_incremental::assert_module_sources::assert_module_sources(tcx); - } - - symbol_names_test::report_symbol_names(tcx); - - if tcx.sess.trans_stats() { - println!("--- trans stats ---"); - println!("n_glues_created: {}", all_stats.n_glues_created); - println!("n_null_glues: {}", all_stats.n_null_glues); - println!("n_real_glues: {}", all_stats.n_real_glues); - - println!("n_fns: {}", all_stats.n_fns); - println!("n_inlines: {}", all_stats.n_inlines); - println!("n_closures: {}", all_stats.n_closures); - println!("fn stats:"); - all_stats.fn_stats.sort_by_key(|&(_, insns)| insns); - for &(ref name, insns) in all_stats.fn_stats.iter() { - println!("{} insns, {}", insns, *name); - } - } - - if tcx.sess.count_llvm_insns() { - for (k, v) in all_stats.llvm_insns.iter() { - println!("{:7} {}", *v, *k); - } - } - - ongoing_translation.check_for_errors(tcx.sess); - - assert_and_save_dep_graph(tcx); - ongoing_translation -} - -fn assert_and_save_dep_graph<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>) { - time(tcx.sess, - "assert dep graph", - || rustc_incremental::assert_dep_graph(tcx)); - - time(tcx.sess, - "serialize dep graph", - || rustc_incremental::save_dep_graph(tcx)); -} - -fn collect_and_partition_translation_items<'a, 'tcx>( - tcx: TyCtxt<'a, 'tcx, 'tcx>, - cnum: CrateNum, -) -> (Arc, Arc>>>) -{ - assert_eq!(cnum, LOCAL_CRATE); - - let collection_mode = match tcx.sess.opts.debugging_opts.print_trans_items { - Some(ref s) => { - let mode_string = s.to_lowercase(); - let mode_string = mode_string.trim(); - if mode_string == "eager" { - MonoItemCollectionMode::Eager - } else { - if mode_string != "lazy" { - let message = format!("Unknown codegen-item collection mode '{}'. \ - Falling back to 'lazy' mode.", - mode_string); - tcx.sess.warn(&message); - } - - MonoItemCollectionMode::Lazy - } - } - None => { - if tcx.sess.opts.cg.link_dead_code { - MonoItemCollectionMode::Eager - } else { - MonoItemCollectionMode::Lazy - } - } - }; - - let (items, inlining_map) = - time(tcx.sess, "translation item collection", || { - collector::collect_crate_mono_items(tcx, collection_mode) - }); - - tcx.sess.abort_if_errors(); - - ::rustc_mir::monomorphize::assert_symbols_are_distinct(tcx, items.iter()); - - let strategy = if tcx.sess.opts.incremental.is_some() { - PartitioningStrategy::PerModule - } else { - PartitioningStrategy::FixedUnitCount(tcx.sess.codegen_units()) - }; - - let codegen_units = time(tcx.sess, "codegen unit partitioning", || { - partitioning::partition(tcx, - items.iter().cloned(), - strategy, - &inlining_map) - .into_iter() - .map(Arc::new) - .collect::>() - }); - - let translation_items: DefIdSet = items.iter().filter_map(|trans_item| { - match *trans_item { - MonoItem::Fn(ref instance) => Some(instance.def_id()), - MonoItem::Static(def_id) => Some(def_id), - _ => None, - } - }).collect(); - - if tcx.sess.opts.debugging_opts.print_trans_items.is_some() { - let mut item_to_cgus = FxHashMap(); - - for cgu in &codegen_units { - for (&trans_item, &linkage) in cgu.items() { - item_to_cgus.entry(trans_item) - .or_insert(Vec::new()) - .push((cgu.name().clone(), linkage)); - } - } - - let mut item_keys: Vec<_> = items - .iter() - .map(|i| { - let mut output = i.to_string(tcx); - output.push_str(" @@"); - let mut empty = Vec::new(); - let cgus = item_to_cgus.get_mut(i).unwrap_or(&mut empty); - cgus.as_mut_slice().sort_by_key(|&(ref name, _)| name.clone()); - cgus.dedup(); - for &(ref cgu_name, (linkage, _)) in cgus.iter() { - output.push_str(" "); - output.push_str(&cgu_name.as_str()); - - let linkage_abbrev = match linkage { - Linkage::External => "External", - Linkage::AvailableExternally => "Available", - Linkage::LinkOnceAny => "OnceAny", - Linkage::LinkOnceODR => "OnceODR", - Linkage::WeakAny => "WeakAny", - Linkage::WeakODR => "WeakODR", - Linkage::Appending => "Appending", - Linkage::Internal => "Internal", - Linkage::Private => "Private", - Linkage::ExternalWeak => "ExternalWeak", - Linkage::Common => "Common", - }; - - output.push_str("["); - output.push_str(linkage_abbrev); - output.push_str("]"); - } - output - }) - .collect(); - - item_keys.sort(); - - for item in item_keys { - println!("TRANS_ITEM {}", item); - } - } - - (Arc::new(translation_items), Arc::new(codegen_units)) -} - -impl CrateInfo { - pub fn new(tcx: TyCtxt) -> CrateInfo { - let mut info = CrateInfo { - panic_runtime: None, - compiler_builtins: None, - profiler_runtime: None, - sanitizer_runtime: None, - is_no_builtins: FxHashSet(), - native_libraries: FxHashMap(), - used_libraries: tcx.native_libraries(LOCAL_CRATE), - link_args: tcx.link_args(LOCAL_CRATE), - crate_name: FxHashMap(), - used_crates_dynamic: cstore::used_crates(tcx, LinkagePreference::RequireDynamic), - used_crates_static: cstore::used_crates(tcx, LinkagePreference::RequireStatic), - used_crate_source: FxHashMap(), - wasm_custom_sections: BTreeMap::new(), - wasm_imports: FxHashMap(), - lang_item_to_crate: FxHashMap(), - missing_lang_items: FxHashMap(), - }; - let lang_items = tcx.lang_items(); - - let load_wasm_items = tcx.sess.crate_types.borrow() - .iter() - .any(|c| *c != config::CrateTypeRlib) && - tcx.sess.opts.target_triple == TargetTriple::from_triple("wasm32-unknown-unknown"); - - if load_wasm_items { - info!("attempting to load all wasm sections"); - for &id in tcx.wasm_custom_sections(LOCAL_CRATE).iter() { - let (name, contents) = fetch_wasm_section(tcx, id); - info.wasm_custom_sections.entry(name) - .or_insert(Vec::new()) - .extend(contents); - } - info.load_wasm_imports(tcx, LOCAL_CRATE); - } - - for &cnum in tcx.crates().iter() { - info.native_libraries.insert(cnum, tcx.native_libraries(cnum)); - info.crate_name.insert(cnum, tcx.crate_name(cnum).to_string()); - info.used_crate_source.insert(cnum, tcx.used_crate_source(cnum)); - if tcx.is_panic_runtime(cnum) { - info.panic_runtime = Some(cnum); - } - if tcx.is_compiler_builtins(cnum) { - info.compiler_builtins = Some(cnum); - } - if tcx.is_profiler_runtime(cnum) { - info.profiler_runtime = Some(cnum); - } - if tcx.is_sanitizer_runtime(cnum) { - info.sanitizer_runtime = Some(cnum); - } - if tcx.is_no_builtins(cnum) { - info.is_no_builtins.insert(cnum); - } - if load_wasm_items { - for &id in tcx.wasm_custom_sections(cnum).iter() { - let (name, contents) = fetch_wasm_section(tcx, id); - info.wasm_custom_sections.entry(name) - .or_insert(Vec::new()) - .extend(contents); - } - info.load_wasm_imports(tcx, cnum); - } - let missing = tcx.missing_lang_items(cnum); - for &item in missing.iter() { - if let Ok(id) = lang_items.require(item) { - info.lang_item_to_crate.insert(item, id.krate); - } - } - - // No need to look for lang items that are whitelisted and don't - // actually need to exist. - let missing = missing.iter() - .cloned() - .filter(|&l| !weak_lang_items::whitelisted(tcx, l)) - .collect(); - info.missing_lang_items.insert(cnum, missing); - } - - return info - } - - fn load_wasm_imports(&mut self, tcx: TyCtxt, cnum: CrateNum) { - for (&id, module) in tcx.wasm_import_module_map(cnum).iter() { - let instance = Instance::mono(tcx, id); - let import_name = tcx.symbol_name(instance); - self.wasm_imports.insert(import_name.to_string(), module.clone()); - } - } -} - -fn is_translated_item(tcx: TyCtxt, id: DefId) -> bool { - let (all_trans_items, _) = - tcx.collect_and_partition_translation_items(LOCAL_CRATE); - all_trans_items.contains(&id) -} - -fn compile_codegen_unit<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, - cgu: InternedString) -> Stats { - let cgu = tcx.codegen_unit(cgu); - - let start_time = Instant::now(); - let (stats, module) = module_translation(tcx, cgu); - let time_to_translate = start_time.elapsed(); - - // We assume that the cost to run LLVM on a CGU is proportional to - // the time we needed for translating it. - let cost = time_to_translate.as_secs() * 1_000_000_000 + - time_to_translate.subsec_nanos() as u64; - - write::submit_translated_module_to_llvm(tcx, - module, - cost); - return stats; - - fn module_translation<'a, 'tcx>( - tcx: TyCtxt<'a, 'tcx, 'tcx>, - cgu: Arc>) - -> (Stats, ModuleTranslation) - { - let cgu_name = cgu.name().to_string(); - - // Append ".rs" to LLVM module identifier. - // - // LLVM code generator emits a ".file filename" directive - // for ELF backends. Value of the "filename" is set as the - // LLVM module identifier. Due to a LLVM MC bug[1], LLVM - // crashes if the module identifier is same as other symbols - // such as a function name in the module. - // 1. http://llvm.org/bugs/show_bug.cgi?id=11479 - let llmod_id = format!("{}-{}.rs", - cgu.name(), - tcx.crate_disambiguator(LOCAL_CRATE) - .to_fingerprint().to_hex()); - - // Instantiate translation items without filling out definitions yet... - let cx = CodegenCx::new(tcx, cgu, &llmod_id); - let module = { - let trans_items = cx.codegen_unit - .items_in_deterministic_order(cx.tcx); - for &(trans_item, (linkage, visibility)) in &trans_items { - trans_item.predefine(&cx, linkage, visibility); - } - - // ... and now that we have everything pre-defined, fill out those definitions. - for &(trans_item, _) in &trans_items { - trans_item.define(&cx); - } - - // If this codegen unit contains the main function, also create the - // wrapper here - maybe_create_entry_wrapper(&cx); - - // Run replace-all-uses-with for statics that need it - for &(old_g, new_g) in cx.statics_to_rauw.borrow().iter() { - unsafe { - let bitcast = llvm::LLVMConstPointerCast(new_g, llvm::LLVMTypeOf(old_g)); - llvm::LLVMReplaceAllUsesWith(old_g, bitcast); - llvm::LLVMDeleteGlobal(old_g); - } - } - - // Create the llvm.used variable - // This variable has type [N x i8*] and is stored in the llvm.metadata section - if !cx.used_statics.borrow().is_empty() { - let name = CString::new("llvm.used").unwrap(); - let section = CString::new("llvm.metadata").unwrap(); - let array = C_array(Type::i8(&cx).ptr_to(), &*cx.used_statics.borrow()); - - unsafe { - let g = llvm::LLVMAddGlobal(cx.llmod, - val_ty(array).to_ref(), - name.as_ptr()); - llvm::LLVMSetInitializer(g, array); - llvm::LLVMRustSetLinkage(g, llvm::Linkage::AppendingLinkage); - llvm::LLVMSetSection(g, section.as_ptr()); - } - } - - // Finalize debuginfo - if cx.sess().opts.debuginfo != NoDebugInfo { - debuginfo::finalize(&cx); - } - - let llvm_module = ModuleLlvm { - llcx: cx.llcx, - llmod: cx.llmod, - tm: create_target_machine(cx.sess(), false), - }; - - ModuleTranslation { - name: cgu_name, - source: ModuleSource::Translated(llvm_module), - kind: ModuleKind::Regular, - llmod_id, - } - }; - - (cx.into_stats(), module) - } -} - -pub fn provide(providers: &mut Providers) { - providers.collect_and_partition_translation_items = - collect_and_partition_translation_items; - - providers.is_translated_item = is_translated_item; - - providers.codegen_unit = |tcx, name| { - let (_, all) = tcx.collect_and_partition_translation_items(LOCAL_CRATE); - all.iter() - .find(|cgu| *cgu.name() == name) - .cloned() - .expect(&format!("failed to find cgu with name {:?}", name)) - }; - providers.compile_codegen_unit = compile_codegen_unit; - - provide_extern(providers); -} - -pub fn provide_extern(providers: &mut Providers) { - providers.dllimport_foreign_items = |tcx, krate| { - let module_map = tcx.foreign_modules(krate); - let module_map = module_map.iter() - .map(|lib| (lib.def_id, lib)) - .collect::>(); - - let dllimports = tcx.native_libraries(krate) - .iter() - .filter(|lib| { - if lib.kind != cstore::NativeLibraryKind::NativeUnknown { - return false - } - let cfg = match lib.cfg { - Some(ref cfg) => cfg, - None => return true, - }; - attr::cfg_matches(cfg, &tcx.sess.parse_sess, None) - }) - .filter_map(|lib| lib.foreign_module) - .map(|id| &module_map[&id]) - .flat_map(|module| module.foreign_items.iter().cloned()) - .collect(); - Lrc::new(dllimports) - }; - - providers.is_dllimport_foreign_item = |tcx, def_id| { - tcx.dllimport_foreign_items(def_id.krate).contains(&def_id) - }; -} - -pub fn linkage_to_llvm(linkage: Linkage) -> llvm::Linkage { - match linkage { - Linkage::External => llvm::Linkage::ExternalLinkage, - Linkage::AvailableExternally => llvm::Linkage::AvailableExternallyLinkage, - Linkage::LinkOnceAny => llvm::Linkage::LinkOnceAnyLinkage, - Linkage::LinkOnceODR => llvm::Linkage::LinkOnceODRLinkage, - Linkage::WeakAny => llvm::Linkage::WeakAnyLinkage, - Linkage::WeakODR => llvm::Linkage::WeakODRLinkage, - Linkage::Appending => llvm::Linkage::AppendingLinkage, - Linkage::Internal => llvm::Linkage::InternalLinkage, - Linkage::Private => llvm::Linkage::PrivateLinkage, - Linkage::ExternalWeak => llvm::Linkage::ExternalWeakLinkage, - Linkage::Common => llvm::Linkage::CommonLinkage, - } -} - -pub fn visibility_to_llvm(linkage: Visibility) -> llvm::Visibility { - match linkage { - Visibility::Default => llvm::Visibility::Default, - Visibility::Hidden => llvm::Visibility::Hidden, - Visibility::Protected => llvm::Visibility::Protected, - } -} - -// FIXME(mw): Anything that is produced via DepGraph::with_task() must implement -// the HashStable trait. Normally DepGraph::with_task() calls are -// hidden behind queries, but CGU creation is a special case in two -// ways: (1) it's not a query and (2) CGU are output nodes, so their -// Fingerprints are not actually needed. It remains to be clarified -// how exactly this case will be handled in the red/green system but -// for now we content ourselves with providing a no-op HashStable -// implementation for CGUs. -mod temp_stable_hash_impls { - use rustc_data_structures::stable_hasher::{StableHasherResult, StableHasher, - HashStable}; - use ModuleTranslation; - - impl HashStable for ModuleTranslation { - fn hash_stable(&self, - _: &mut HCX, - _: &mut StableHasher) { - // do nothing - } - } -} - -fn fetch_wasm_section(tcx: TyCtxt, id: DefId) -> (String, Vec) { - use rustc::mir::interpret::GlobalId; - use rustc::middle::const_val::ConstVal; - - info!("loading wasm section {:?}", id); - - let section = tcx.get_attrs(id) - .iter() - .find(|a| a.check_name("wasm_custom_section")) - .expect("missing #[wasm_custom_section] attribute") - .value_str() - .expect("malformed #[wasm_custom_section] attribute"); - - let instance = ty::Instance::mono(tcx, id); - let cid = GlobalId { - instance, - promoted: None - }; - let param_env = ty::ParamEnv::reveal_all(); - let val = tcx.const_eval(param_env.and(cid)).unwrap(); - - let const_val = match val.val { - ConstVal::Value(val) => val, - ConstVal::Unevaluated(..) => bug!("should be evaluated"), - }; - - let alloc = tcx.const_value_to_allocation((const_val, val.ty)); - (section.to_string(), alloc.bytes.clone()) -} diff --git a/src/librustc_trans/build.rs b/src/librustc_trans/build.rs deleted file mode 100644 index 97accbb4b8f..00000000000 --- a/src/librustc_trans/build.rs +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright 2017 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -fn main() { - println!("cargo:rerun-if-changed=build.rs"); - println!("cargo:rerun-if-env-changed=CFG_VERSION"); - println!("cargo:rerun-if-env-changed=CFG_PREFIX"); - println!("cargo:rerun-if-env-changed=CFG_LLVM_ROOT"); -} diff --git a/src/librustc_trans/builder.rs b/src/librustc_trans/builder.rs deleted file mode 100644 index 4153c61e526..00000000000 --- a/src/librustc_trans/builder.rs +++ /dev/null @@ -1,1425 +0,0 @@ -// Copyright 2013 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. - -#![allow(dead_code)] // FFI wrappers - -use llvm; -use llvm::{AtomicRmwBinOp, AtomicOrdering, SynchronizationScope, AsmDialect}; -use llvm::{Opcode, IntPredicate, RealPredicate, False, OperandBundleDef}; -use llvm::{ValueRef, BasicBlockRef, BuilderRef, ModuleRef}; -use common::*; -use type_::Type; -use value::Value; -use libc::{c_uint, c_char}; -use rustc::ty::TyCtxt; -use rustc::ty::layout::{Align, Size}; -use rustc::session::{config, Session}; - -use std::borrow::Cow; -use std::ffi::CString; -use std::ops::Range; -use std::ptr; -use syntax_pos::Span; - -// All Builders must have an llfn associated with them -#[must_use] -pub struct Builder<'a, 'tcx: 'a> { - pub llbuilder: BuilderRef, - pub cx: &'a CodegenCx<'a, 'tcx>, -} - -impl<'a, 'tcx> Drop for Builder<'a, 'tcx> { - fn drop(&mut self) { - unsafe { - llvm::LLVMDisposeBuilder(self.llbuilder); - } - } -} - -// This is a really awful way to get a zero-length c-string, but better (and a -// lot more efficient) than doing str::as_c_str("", ...) every time. -fn noname() -> *const c_char { - static CNULL: c_char = 0; - &CNULL -} - -bitflags! { - pub struct MemFlags: u8 { - const VOLATILE = 1 << 0; - const NONTEMPORAL = 1 << 1; - } -} - -impl<'a, 'tcx> Builder<'a, 'tcx> { - pub fn new_block<'b>(cx: &'a CodegenCx<'a, 'tcx>, llfn: ValueRef, name: &'b str) -> Self { - let bx = Builder::with_cx(cx); - let llbb = unsafe { - let name = CString::new(name).unwrap(); - llvm::LLVMAppendBasicBlockInContext( - cx.llcx, - llfn, - name.as_ptr() - ) - }; - bx.position_at_end(llbb); - bx - } - - pub fn with_cx(cx: &'a CodegenCx<'a, 'tcx>) -> Self { - // Create a fresh builder from the crate context. - let llbuilder = unsafe { - llvm::LLVMCreateBuilderInContext(cx.llcx) - }; - Builder { - llbuilder, - cx, - } - } - - pub fn build_sibling_block<'b>(&self, name: &'b str) -> Builder<'a, 'tcx> { - Builder::new_block(self.cx, self.llfn(), name) - } - - pub fn sess(&self) -> &Session { - self.cx.sess() - } - - pub fn tcx(&self) -> TyCtxt<'a, 'tcx, 'tcx> { - self.cx.tcx - } - - pub fn llfn(&self) -> ValueRef { - unsafe { - llvm::LLVMGetBasicBlockParent(self.llbb()) - } - } - - pub fn llbb(&self) -> BasicBlockRef { - unsafe { - llvm::LLVMGetInsertBlock(self.llbuilder) - } - } - - fn count_insn(&self, category: &str) { - if self.cx.sess().trans_stats() { - self.cx.stats.borrow_mut().n_llvm_insns += 1; - } - if self.cx.sess().count_llvm_insns() { - *self.cx.stats - .borrow_mut() - .llvm_insns - .entry(category.to_string()) - .or_insert(0) += 1; - } - } - - pub fn set_value_name(&self, value: ValueRef, name: &str) { - let cname = CString::new(name.as_bytes()).unwrap(); - unsafe { - llvm::LLVMSetValueName(value, cname.as_ptr()); - } - } - - pub fn position_before(&self, insn: ValueRef) { - unsafe { - llvm::LLVMPositionBuilderBefore(self.llbuilder, insn); - } - } - - pub fn position_at_end(&self, llbb: BasicBlockRef) { - unsafe { - llvm::LLVMPositionBuilderAtEnd(self.llbuilder, llbb); - } - } - - pub fn position_at_start(&self, llbb: BasicBlockRef) { - unsafe { - llvm::LLVMRustPositionBuilderAtStart(self.llbuilder, llbb); - } - } - - pub fn ret_void(&self) { - self.count_insn("retvoid"); - unsafe { - llvm::LLVMBuildRetVoid(self.llbuilder); - } - } - - pub fn ret(&self, v: ValueRef) { - self.count_insn("ret"); - unsafe { - llvm::LLVMBuildRet(self.llbuilder, v); - } - } - - pub fn aggregate_ret(&self, ret_vals: &[ValueRef]) { - unsafe { - llvm::LLVMBuildAggregateRet(self.llbuilder, - ret_vals.as_ptr(), - ret_vals.len() as c_uint); - } - } - - pub fn br(&self, dest: BasicBlockRef) { - self.count_insn("br"); - unsafe { - llvm::LLVMBuildBr(self.llbuilder, dest); - } - } - - pub fn cond_br(&self, cond: ValueRef, then_llbb: BasicBlockRef, else_llbb: BasicBlockRef) { - self.count_insn("condbr"); - unsafe { - llvm::LLVMBuildCondBr(self.llbuilder, cond, then_llbb, else_llbb); - } - } - - pub fn switch(&self, v: ValueRef, else_llbb: BasicBlockRef, num_cases: usize) -> ValueRef { - unsafe { - llvm::LLVMBuildSwitch(self.llbuilder, v, else_llbb, num_cases as c_uint) - } - } - - pub fn indirect_br(&self, addr: ValueRef, num_dests: usize) { - self.count_insn("indirectbr"); - unsafe { - llvm::LLVMBuildIndirectBr(self.llbuilder, addr, num_dests as c_uint); - } - } - - pub fn invoke(&self, - llfn: ValueRef, - args: &[ValueRef], - then: BasicBlockRef, - catch: BasicBlockRef, - bundle: Option<&OperandBundleDef>) -> ValueRef { - self.count_insn("invoke"); - - debug!("Invoke {:?} with args ({})", - Value(llfn), - args.iter() - .map(|&v| format!("{:?}", Value(v))) - .collect::>() - .join(", ")); - - let args = self.check_call("invoke", llfn, args); - let bundle = bundle.as_ref().map(|b| b.raw()).unwrap_or(ptr::null_mut()); - - unsafe { - llvm::LLVMRustBuildInvoke(self.llbuilder, - llfn, - args.as_ptr(), - args.len() as c_uint, - then, - catch, - bundle, - noname()) - } - } - - pub fn unreachable(&self) { - self.count_insn("unreachable"); - unsafe { - llvm::LLVMBuildUnreachable(self.llbuilder); - } - } - - /* Arithmetic */ - pub fn add(&self, lhs: ValueRef, rhs: ValueRef) -> ValueRef { - self.count_insn("add"); - unsafe { - llvm::LLVMBuildAdd(self.llbuilder, lhs, rhs, noname()) - } - } - - pub fn nswadd(&self, lhs: ValueRef, rhs: ValueRef) -> ValueRef { - self.count_insn("nswadd"); - unsafe { - llvm::LLVMBuildNSWAdd(self.llbuilder, lhs, rhs, noname()) - } - } - - pub fn nuwadd(&self, lhs: ValueRef, rhs: ValueRef) -> ValueRef { - self.count_insn("nuwadd"); - unsafe { - llvm::LLVMBuildNUWAdd(self.llbuilder, lhs, rhs, noname()) - } - } - - pub fn fadd(&self, lhs: ValueRef, rhs: ValueRef) -> ValueRef { - self.count_insn("fadd"); - unsafe { - llvm::LLVMBuildFAdd(self.llbuilder, lhs, rhs, noname()) - } - } - - pub fn fadd_fast(&self, lhs: ValueRef, rhs: ValueRef) -> ValueRef { - self.count_insn("fadd"); - unsafe { - let instr = llvm::LLVMBuildFAdd(self.llbuilder, lhs, rhs, noname()); - llvm::LLVMRustSetHasUnsafeAlgebra(instr); - instr - } - } - - pub fn sub(&self, lhs: ValueRef, rhs: ValueRef) -> ValueRef { - self.count_insn("sub"); - unsafe { - llvm::LLVMBuildSub(self.llbuilder, lhs, rhs, noname()) - } - } - - pub fn nswsub(&self, lhs: ValueRef, rhs: ValueRef) -> ValueRef { - self.count_insn("nwsub"); - unsafe { - llvm::LLVMBuildNSWSub(self.llbuilder, lhs, rhs, noname()) - } - } - - pub fn nuwsub(&self, lhs: ValueRef, rhs: ValueRef) -> ValueRef { - self.count_insn("nuwsub"); - unsafe { - llvm::LLVMBuildNUWSub(self.llbuilder, lhs, rhs, noname()) - } - } - - pub fn fsub(&self, lhs: ValueRef, rhs: ValueRef) -> ValueRef { - self.count_insn("sub"); - unsafe { - llvm::LLVMBuildFSub(self.llbuilder, lhs, rhs, noname()) - } - } - - pub fn fsub_fast(&self, lhs: ValueRef, rhs: ValueRef) -> ValueRef { - self.count_insn("sub"); - unsafe { - let instr = llvm::LLVMBuildFSub(self.llbuilder, lhs, rhs, noname()); - llvm::LLVMRustSetHasUnsafeAlgebra(instr); - instr - } - } - - pub fn mul(&self, lhs: ValueRef, rhs: ValueRef) -> ValueRef { - self.count_insn("mul"); - unsafe { - llvm::LLVMBuildMul(self.llbuilder, lhs, rhs, noname()) - } - } - - pub fn nswmul(&self, lhs: ValueRef, rhs: ValueRef) -> ValueRef { - self.count_insn("nswmul"); - unsafe { - llvm::LLVMBuildNSWMul(self.llbuilder, lhs, rhs, noname()) - } - } - - pub fn nuwmul(&self, lhs: ValueRef, rhs: ValueRef) -> ValueRef { - self.count_insn("nuwmul"); - unsafe { - llvm::LLVMBuildNUWMul(self.llbuilder, lhs, rhs, noname()) - } - } - - pub fn fmul(&self, lhs: ValueRef, rhs: ValueRef) -> ValueRef { - self.count_insn("fmul"); - unsafe { - llvm::LLVMBuildFMul(self.llbuilder, lhs, rhs, noname()) - } - } - - pub fn fmul_fast(&self, lhs: ValueRef, rhs: ValueRef) -> ValueRef { - self.count_insn("fmul"); - unsafe { - let instr = llvm::LLVMBuildFMul(self.llbuilder, lhs, rhs, noname()); - llvm::LLVMRustSetHasUnsafeAlgebra(instr); - instr - } - } - - - pub fn udiv(&self, lhs: ValueRef, rhs: ValueRef) -> ValueRef { - self.count_insn("udiv"); - unsafe { - llvm::LLVMBuildUDiv(self.llbuilder, lhs, rhs, noname()) - } - } - - pub fn exactudiv(&self, lhs: ValueRef, rhs: ValueRef) -> ValueRef { - self.count_insn("exactudiv"); - unsafe { - llvm::LLVMBuildExactUDiv(self.llbuilder, lhs, rhs, noname()) - } - } - - pub fn sdiv(&self, lhs: ValueRef, rhs: ValueRef) -> ValueRef { - self.count_insn("sdiv"); - unsafe { - llvm::LLVMBuildSDiv(self.llbuilder, lhs, rhs, noname()) - } - } - - pub fn exactsdiv(&self, lhs: ValueRef, rhs: ValueRef) -> ValueRef { - self.count_insn("exactsdiv"); - unsafe { - llvm::LLVMBuildExactSDiv(self.llbuilder, lhs, rhs, noname()) - } - } - - pub fn fdiv(&self, lhs: ValueRef, rhs: ValueRef) -> ValueRef { - self.count_insn("fdiv"); - unsafe { - llvm::LLVMBuildFDiv(self.llbuilder, lhs, rhs, noname()) - } - } - - pub fn fdiv_fast(&self, lhs: ValueRef, rhs: ValueRef) -> ValueRef { - self.count_insn("fdiv"); - unsafe { - let instr = llvm::LLVMBuildFDiv(self.llbuilder, lhs, rhs, noname()); - llvm::LLVMRustSetHasUnsafeAlgebra(instr); - instr - } - } - - pub fn urem(&self, lhs: ValueRef, rhs: ValueRef) -> ValueRef { - self.count_insn("urem"); - unsafe { - llvm::LLVMBuildURem(self.llbuilder, lhs, rhs, noname()) - } - } - - pub fn srem(&self, lhs: ValueRef, rhs: ValueRef) -> ValueRef { - self.count_insn("srem"); - unsafe { - llvm::LLVMBuildSRem(self.llbuilder, lhs, rhs, noname()) - } - } - - pub fn frem(&self, lhs: ValueRef, rhs: ValueRef) -> ValueRef { - self.count_insn("frem"); - unsafe { - llvm::LLVMBuildFRem(self.llbuilder, lhs, rhs, noname()) - } - } - - pub fn frem_fast(&self, lhs: ValueRef, rhs: ValueRef) -> ValueRef { - self.count_insn("frem"); - unsafe { - let instr = llvm::LLVMBuildFRem(self.llbuilder, lhs, rhs, noname()); - llvm::LLVMRustSetHasUnsafeAlgebra(instr); - instr - } - } - - pub fn shl(&self, lhs: ValueRef, rhs: ValueRef) -> ValueRef { - self.count_insn("shl"); - unsafe { - llvm::LLVMBuildShl(self.llbuilder, lhs, rhs, noname()) - } - } - - pub fn lshr(&self, lhs: ValueRef, rhs: ValueRef) -> ValueRef { - self.count_insn("lshr"); - unsafe { - llvm::LLVMBuildLShr(self.llbuilder, lhs, rhs, noname()) - } - } - - pub fn ashr(&self, lhs: ValueRef, rhs: ValueRef) -> ValueRef { - self.count_insn("ashr"); - unsafe { - llvm::LLVMBuildAShr(self.llbuilder, lhs, rhs, noname()) - } - } - - pub fn and(&self, lhs: ValueRef, rhs: ValueRef) -> ValueRef { - self.count_insn("and"); - unsafe { - llvm::LLVMBuildAnd(self.llbuilder, lhs, rhs, noname()) - } - } - - pub fn or(&self, lhs: ValueRef, rhs: ValueRef) -> ValueRef { - self.count_insn("or"); - unsafe { - llvm::LLVMBuildOr(self.llbuilder, lhs, rhs, noname()) - } - } - - pub fn xor(&self, lhs: ValueRef, rhs: ValueRef) -> ValueRef { - self.count_insn("xor"); - unsafe { - llvm::LLVMBuildXor(self.llbuilder, lhs, rhs, noname()) - } - } - - pub fn binop(&self, op: Opcode, lhs: ValueRef, rhs: ValueRef) - -> ValueRef { - self.count_insn("binop"); - unsafe { - llvm::LLVMBuildBinOp(self.llbuilder, op, lhs, rhs, noname()) - } - } - - pub fn neg(&self, v: ValueRef) -> ValueRef { - self.count_insn("neg"); - unsafe { - llvm::LLVMBuildNeg(self.llbuilder, v, noname()) - } - } - - pub fn nswneg(&self, v: ValueRef) -> ValueRef { - self.count_insn("nswneg"); - unsafe { - llvm::LLVMBuildNSWNeg(self.llbuilder, v, noname()) - } - } - - pub fn nuwneg(&self, v: ValueRef) -> ValueRef { - self.count_insn("nuwneg"); - unsafe { - llvm::LLVMBuildNUWNeg(self.llbuilder, v, noname()) - } - } - pub fn fneg(&self, v: ValueRef) -> ValueRef { - self.count_insn("fneg"); - unsafe { - llvm::LLVMBuildFNeg(self.llbuilder, v, noname()) - } - } - - pub fn not(&self, v: ValueRef) -> ValueRef { - self.count_insn("not"); - unsafe { - llvm::LLVMBuildNot(self.llbuilder, v, noname()) - } - } - - pub fn alloca(&self, ty: Type, name: &str, align: Align) -> ValueRef { - let bx = Builder::with_cx(self.cx); - bx.position_at_start(unsafe { - llvm::LLVMGetFirstBasicBlock(self.llfn()) - }); - bx.dynamic_alloca(ty, name, align) - } - - pub fn dynamic_alloca(&self, ty: Type, name: &str, align: Align) -> ValueRef { - self.count_insn("alloca"); - unsafe { - let alloca = if name.is_empty() { - llvm::LLVMBuildAlloca(self.llbuilder, ty.to_ref(), noname()) - } else { - let name = CString::new(name).unwrap(); - llvm::LLVMBuildAlloca(self.llbuilder, ty.to_ref(), - name.as_ptr()) - }; - llvm::LLVMSetAlignment(alloca, align.abi() as c_uint); - alloca - } - } - - pub fn free(&self, ptr: ValueRef) { - self.count_insn("free"); - unsafe { - llvm::LLVMBuildFree(self.llbuilder, ptr); - } - } - - pub fn load(&self, ptr: ValueRef, align: Align) -> ValueRef { - self.count_insn("load"); - unsafe { - let load = llvm::LLVMBuildLoad(self.llbuilder, ptr, noname()); - llvm::LLVMSetAlignment(load, align.abi() as c_uint); - load - } - } - - pub fn volatile_load(&self, ptr: ValueRef) -> ValueRef { - self.count_insn("load.volatile"); - unsafe { - let insn = llvm::LLVMBuildLoad(self.llbuilder, ptr, noname()); - llvm::LLVMSetVolatile(insn, llvm::True); - insn - } - } - - pub fn atomic_load(&self, ptr: ValueRef, order: AtomicOrdering, align: Align) -> ValueRef { - self.count_insn("load.atomic"); - unsafe { - let load = llvm::LLVMRustBuildAtomicLoad(self.llbuilder, ptr, noname(), order); - // FIXME(eddyb) Isn't it UB to use `pref` instead of `abi` here? - // However, 64-bit atomic loads on `i686-apple-darwin` appear to - // require `___atomic_load` with ABI-alignment, so it's staying. - llvm::LLVMSetAlignment(load, align.pref() as c_uint); - load - } - } - - - pub fn range_metadata(&self, load: ValueRef, range: Range) { - unsafe { - let llty = val_ty(load); - let v = [ - C_uint_big(llty, range.start), - C_uint_big(llty, range.end) - ]; - - llvm::LLVMSetMetadata(load, llvm::MD_range as c_uint, - llvm::LLVMMDNodeInContext(self.cx.llcx, - v.as_ptr(), - v.len() as c_uint)); - } - } - - pub fn nonnull_metadata(&self, load: ValueRef) { - unsafe { - llvm::LLVMSetMetadata(load, llvm::MD_nonnull as c_uint, - llvm::LLVMMDNodeInContext(self.cx.llcx, ptr::null(), 0)); - } - } - - pub fn store(&self, val: ValueRef, ptr: ValueRef, align: Align) -> ValueRef { - self.store_with_flags(val, ptr, align, MemFlags::empty()) - } - - pub fn store_with_flags( - &self, - val: ValueRef, - ptr: ValueRef, - align: Align, - flags: MemFlags, - ) -> ValueRef { - debug!("Store {:?} -> {:?} ({:?})", Value(val), Value(ptr), flags); - assert!(!self.llbuilder.is_null()); - self.count_insn("store"); - let ptr = self.check_store(val, ptr); - unsafe { - let store = llvm::LLVMBuildStore(self.llbuilder, val, ptr); - llvm::LLVMSetAlignment(store, align.abi() as c_uint); - if flags.contains(MemFlags::VOLATILE) { - llvm::LLVMSetVolatile(store, llvm::True); - } - if flags.contains(MemFlags::NONTEMPORAL) { - // According to LLVM [1] building a nontemporal store must - // *always* point to a metadata value of the integer 1. - // - // [1]: http://llvm.org/docs/LangRef.html#store-instruction - let one = C_i32(self.cx, 1); - let node = llvm::LLVMMDNodeInContext(self.cx.llcx, &one, 1); - llvm::LLVMSetMetadata(store, llvm::MD_nontemporal as c_uint, node); - } - store - } - } - - pub fn atomic_store(&self, val: ValueRef, ptr: ValueRef, - order: AtomicOrdering, align: Align) { - debug!("Store {:?} -> {:?}", Value(val), Value(ptr)); - self.count_insn("store.atomic"); - let ptr = self.check_store(val, ptr); - unsafe { - let store = llvm::LLVMRustBuildAtomicStore(self.llbuilder, val, ptr, order); - // FIXME(eddyb) Isn't it UB to use `pref` instead of `abi` here? - // Also see `atomic_load` for more context. - llvm::LLVMSetAlignment(store, align.pref() as c_uint); - } - } - - pub fn gep(&self, ptr: ValueRef, indices: &[ValueRef]) -> ValueRef { - self.count_insn("gep"); - unsafe { - llvm::LLVMBuildGEP(self.llbuilder, ptr, indices.as_ptr(), - indices.len() as c_uint, noname()) - } - } - - pub fn inbounds_gep(&self, ptr: ValueRef, indices: &[ValueRef]) -> ValueRef { - self.count_insn("inboundsgep"); - unsafe { - llvm::LLVMBuildInBoundsGEP( - self.llbuilder, ptr, indices.as_ptr(), indices.len() as c_uint, noname()) - } - } - - pub fn struct_gep(&self, ptr: ValueRef, idx: u64) -> ValueRef { - self.count_insn("structgep"); - assert_eq!(idx as c_uint as u64, idx); - unsafe { - llvm::LLVMBuildStructGEP(self.llbuilder, ptr, idx as c_uint, noname()) - } - } - - pub fn global_string(&self, _str: *const c_char) -> ValueRef { - self.count_insn("globalstring"); - unsafe { - llvm::LLVMBuildGlobalString(self.llbuilder, _str, noname()) - } - } - - pub fn global_string_ptr(&self, _str: *const c_char) -> ValueRef { - self.count_insn("globalstringptr"); - unsafe { - llvm::LLVMBuildGlobalStringPtr(self.llbuilder, _str, noname()) - } - } - - /* Casts */ - pub fn trunc(&self, val: ValueRef, dest_ty: Type) -> ValueRef { - self.count_insn("trunc"); - unsafe { - llvm::LLVMBuildTrunc(self.llbuilder, val, dest_ty.to_ref(), noname()) - } - } - - pub fn zext(&self, val: ValueRef, dest_ty: Type) -> ValueRef { - self.count_insn("zext"); - unsafe { - llvm::LLVMBuildZExt(self.llbuilder, val, dest_ty.to_ref(), noname()) - } - } - - pub fn sext(&self, val: ValueRef, dest_ty: Type) -> ValueRef { - self.count_insn("sext"); - unsafe { - llvm::LLVMBuildSExt(self.llbuilder, val, dest_ty.to_ref(), noname()) - } - } - - pub fn fptoui(&self, val: ValueRef, dest_ty: Type) -> ValueRef { - self.count_insn("fptoui"); - unsafe { - llvm::LLVMBuildFPToUI(self.llbuilder, val, dest_ty.to_ref(), noname()) - } - } - - pub fn fptosi(&self, val: ValueRef, dest_ty: Type) -> ValueRef { - self.count_insn("fptosi"); - unsafe { - llvm::LLVMBuildFPToSI(self.llbuilder, val, dest_ty.to_ref(),noname()) - } - } - - pub fn uitofp(&self, val: ValueRef, dest_ty: Type) -> ValueRef { - self.count_insn("uitofp"); - unsafe { - llvm::LLVMBuildUIToFP(self.llbuilder, val, dest_ty.to_ref(), noname()) - } - } - - pub fn sitofp(&self, val: ValueRef, dest_ty: Type) -> ValueRef { - self.count_insn("sitofp"); - unsafe { - llvm::LLVMBuildSIToFP(self.llbuilder, val, dest_ty.to_ref(), noname()) - } - } - - pub fn fptrunc(&self, val: ValueRef, dest_ty: Type) -> ValueRef { - self.count_insn("fptrunc"); - unsafe { - llvm::LLVMBuildFPTrunc(self.llbuilder, val, dest_ty.to_ref(), noname()) - } - } - - pub fn fpext(&self, val: ValueRef, dest_ty: Type) -> ValueRef { - self.count_insn("fpext"); - unsafe { - llvm::LLVMBuildFPExt(self.llbuilder, val, dest_ty.to_ref(), noname()) - } - } - - pub fn ptrtoint(&self, val: ValueRef, dest_ty: Type) -> ValueRef { - self.count_insn("ptrtoint"); - unsafe { - llvm::LLVMBuildPtrToInt(self.llbuilder, val, dest_ty.to_ref(), noname()) - } - } - - pub fn inttoptr(&self, val: ValueRef, dest_ty: Type) -> ValueRef { - self.count_insn("inttoptr"); - unsafe { - llvm::LLVMBuildIntToPtr(self.llbuilder, val, dest_ty.to_ref(), noname()) - } - } - - pub fn bitcast(&self, val: ValueRef, dest_ty: Type) -> ValueRef { - self.count_insn("bitcast"); - unsafe { - llvm::LLVMBuildBitCast(self.llbuilder, val, dest_ty.to_ref(), noname()) - } - } - - pub fn zext_or_bitcast(&self, val: ValueRef, dest_ty: Type) -> ValueRef { - self.count_insn("zextorbitcast"); - unsafe { - llvm::LLVMBuildZExtOrBitCast(self.llbuilder, val, dest_ty.to_ref(), noname()) - } - } - - pub fn sext_or_bitcast(&self, val: ValueRef, dest_ty: Type) -> ValueRef { - self.count_insn("sextorbitcast"); - unsafe { - llvm::LLVMBuildSExtOrBitCast(self.llbuilder, val, dest_ty.to_ref(), noname()) - } - } - - pub fn trunc_or_bitcast(&self, val: ValueRef, dest_ty: Type) -> ValueRef { - self.count_insn("truncorbitcast"); - unsafe { - llvm::LLVMBuildTruncOrBitCast(self.llbuilder, val, dest_ty.to_ref(), noname()) - } - } - - pub fn cast(&self, op: Opcode, val: ValueRef, dest_ty: Type) -> ValueRef { - self.count_insn("cast"); - unsafe { - llvm::LLVMBuildCast(self.llbuilder, op, val, dest_ty.to_ref(), noname()) - } - } - - pub fn pointercast(&self, val: ValueRef, dest_ty: Type) -> ValueRef { - self.count_insn("pointercast"); - unsafe { - llvm::LLVMBuildPointerCast(self.llbuilder, val, dest_ty.to_ref(), noname()) - } - } - - pub fn intcast(&self, val: ValueRef, dest_ty: Type, is_signed: bool) -> ValueRef { - self.count_insn("intcast"); - unsafe { - llvm::LLVMRustBuildIntCast(self.llbuilder, val, dest_ty.to_ref(), is_signed) - } - } - - pub fn fpcast(&self, val: ValueRef, dest_ty: Type) -> ValueRef { - self.count_insn("fpcast"); - unsafe { - llvm::LLVMBuildFPCast(self.llbuilder, val, dest_ty.to_ref(), noname()) - } - } - - - /* Comparisons */ - pub fn icmp(&self, op: IntPredicate, lhs: ValueRef, rhs: ValueRef) -> ValueRef { - self.count_insn("icmp"); - unsafe { - llvm::LLVMBuildICmp(self.llbuilder, op as c_uint, lhs, rhs, noname()) - } - } - - pub fn fcmp(&self, op: RealPredicate, lhs: ValueRef, rhs: ValueRef) -> ValueRef { - self.count_insn("fcmp"); - unsafe { - llvm::LLVMBuildFCmp(self.llbuilder, op as c_uint, lhs, rhs, noname()) - } - } - - /* Miscellaneous instructions */ - pub fn empty_phi(&self, ty: Type) -> ValueRef { - self.count_insn("emptyphi"); - unsafe { - llvm::LLVMBuildPhi(self.llbuilder, ty.to_ref(), noname()) - } - } - - pub fn phi(&self, ty: Type, vals: &[ValueRef], bbs: &[BasicBlockRef]) -> ValueRef { - assert_eq!(vals.len(), bbs.len()); - let phi = self.empty_phi(ty); - self.count_insn("addincoming"); - unsafe { - llvm::LLVMAddIncoming(phi, vals.as_ptr(), - bbs.as_ptr(), - vals.len() as c_uint); - phi - } - } - - pub fn add_span_comment(&self, sp: Span, text: &str) { - if self.cx.sess().asm_comments() { - let s = format!("{} ({})", - text, - self.cx.sess().codemap().span_to_string(sp)); - debug!("{}", s); - self.add_comment(&s); - } - } - - pub fn add_comment(&self, text: &str) { - if self.cx.sess().asm_comments() { - let sanitized = text.replace("$", ""); - let comment_text = format!("{} {}", "#", - sanitized.replace("\n", "\n\t# ")); - self.count_insn("inlineasm"); - let comment_text = CString::new(comment_text).unwrap(); - let asm = unsafe { - llvm::LLVMConstInlineAsm(Type::func(&[], &Type::void(self.cx)).to_ref(), - comment_text.as_ptr(), noname(), False, - False) - }; - self.call(asm, &[], None); - } - } - - pub fn inline_asm_call(&self, asm: *const c_char, cons: *const c_char, - inputs: &[ValueRef], output: Type, - volatile: bool, alignstack: bool, - dia: AsmDialect) -> ValueRef { - self.count_insn("inlineasm"); - - let volatile = if volatile { llvm::True } - else { llvm::False }; - let alignstack = if alignstack { llvm::True } - else { llvm::False }; - - let argtys = inputs.iter().map(|v| { - debug!("Asm Input Type: {:?}", Value(*v)); - val_ty(*v) - }).collect::>(); - - debug!("Asm Output Type: {:?}", output); - let fty = Type::func(&argtys[..], &output); - unsafe { - let v = llvm::LLVMRustInlineAsm( - fty.to_ref(), asm, cons, volatile, alignstack, dia); - self.call(v, inputs, None) - } - } - - pub fn call(&self, llfn: ValueRef, args: &[ValueRef], - bundle: Option<&OperandBundleDef>) -> ValueRef { - self.count_insn("call"); - - debug!("Call {:?} with args ({})", - Value(llfn), - args.iter() - .map(|&v| format!("{:?}", Value(v))) - .collect::>() - .join(", ")); - - let args = self.check_call("call", llfn, args); - let bundle = bundle.as_ref().map(|b| b.raw()).unwrap_or(ptr::null_mut()); - - unsafe { - llvm::LLVMRustBuildCall(self.llbuilder, llfn, args.as_ptr(), - args.len() as c_uint, bundle, noname()) - } - } - - pub fn minnum(&self, lhs: ValueRef, rhs: ValueRef) -> ValueRef { - self.count_insn("minnum"); - unsafe { - let instr = llvm::LLVMRustBuildMinNum(self.llbuilder, lhs, rhs); - if instr.is_null() { - bug!("LLVMRustBuildMinNum is not available in LLVM version < 6.0"); - } - instr - } - } - pub fn maxnum(&self, lhs: ValueRef, rhs: ValueRef) -> ValueRef { - self.count_insn("maxnum"); - unsafe { - let instr = llvm::LLVMRustBuildMaxNum(self.llbuilder, lhs, rhs); - if instr.is_null() { - bug!("LLVMRustBuildMaxNum is not available in LLVM version < 6.0"); - } - instr - } - } - - pub fn select(&self, cond: ValueRef, then_val: ValueRef, else_val: ValueRef) -> ValueRef { - self.count_insn("select"); - unsafe { - llvm::LLVMBuildSelect(self.llbuilder, cond, then_val, else_val, noname()) - } - } - - pub fn va_arg(&self, list: ValueRef, ty: Type) -> ValueRef { - self.count_insn("vaarg"); - unsafe { - llvm::LLVMBuildVAArg(self.llbuilder, list, ty.to_ref(), noname()) - } - } - - pub fn extract_element(&self, vec: ValueRef, idx: ValueRef) -> ValueRef { - self.count_insn("extractelement"); - unsafe { - llvm::LLVMBuildExtractElement(self.llbuilder, vec, idx, noname()) - } - } - - pub fn insert_element(&self, vec: ValueRef, elt: ValueRef, idx: ValueRef) -> ValueRef { - self.count_insn("insertelement"); - unsafe { - llvm::LLVMBuildInsertElement(self.llbuilder, vec, elt, idx, noname()) - } - } - - pub fn shuffle_vector(&self, v1: ValueRef, v2: ValueRef, mask: ValueRef) -> ValueRef { - self.count_insn("shufflevector"); - unsafe { - llvm::LLVMBuildShuffleVector(self.llbuilder, v1, v2, mask, noname()) - } - } - - pub fn vector_splat(&self, num_elts: usize, elt: ValueRef) -> ValueRef { - unsafe { - let elt_ty = val_ty(elt); - let undef = llvm::LLVMGetUndef(Type::vector(&elt_ty, num_elts as u64).to_ref()); - let vec = self.insert_element(undef, elt, C_i32(self.cx, 0)); - let vec_i32_ty = Type::vector(&Type::i32(self.cx), num_elts as u64); - self.shuffle_vector(vec, undef, C_null(vec_i32_ty)) - } - } - - pub fn vector_reduce_fadd_fast(&self, acc: ValueRef, src: ValueRef) -> ValueRef { - self.count_insn("vector.reduce.fadd_fast"); - unsafe { - // FIXME: add a non-fast math version once - // https://bugs.llvm.org/show_bug.cgi?id=36732 - // is fixed. - let instr = llvm::LLVMRustBuildVectorReduceFAdd(self.llbuilder, acc, src); - if instr.is_null() { - bug!("LLVMRustBuildVectorReduceFAdd is not available in LLVM version < 5.0"); - } - llvm::LLVMRustSetHasUnsafeAlgebra(instr); - instr - } - } - pub fn vector_reduce_fmul_fast(&self, acc: ValueRef, src: ValueRef) -> ValueRef { - self.count_insn("vector.reduce.fmul_fast"); - unsafe { - // FIXME: add a non-fast math version once - // https://bugs.llvm.org/show_bug.cgi?id=36732 - // is fixed. - let instr = llvm::LLVMRustBuildVectorReduceFMul(self.llbuilder, acc, src); - if instr.is_null() { - bug!("LLVMRustBuildVectorReduceFMul is not available in LLVM version < 5.0"); - } - llvm::LLVMRustSetHasUnsafeAlgebra(instr); - instr - } - } - pub fn vector_reduce_add(&self, src: ValueRef) -> ValueRef { - self.count_insn("vector.reduce.add"); - unsafe { - let instr = llvm::LLVMRustBuildVectorReduceAdd(self.llbuilder, src); - if instr.is_null() { - bug!("LLVMRustBuildVectorReduceAdd is not available in LLVM version < 5.0"); - } - instr - } - } - pub fn vector_reduce_mul(&self, src: ValueRef) -> ValueRef { - self.count_insn("vector.reduce.mul"); - unsafe { - let instr = llvm::LLVMRustBuildVectorReduceMul(self.llbuilder, src); - if instr.is_null() { - bug!("LLVMRustBuildVectorReduceMul is not available in LLVM version < 5.0"); - } - instr - } - } - pub fn vector_reduce_and(&self, src: ValueRef) -> ValueRef { - self.count_insn("vector.reduce.and"); - unsafe { - let instr = llvm::LLVMRustBuildVectorReduceAnd(self.llbuilder, src); - if instr.is_null() { - bug!("LLVMRustBuildVectorReduceAnd is not available in LLVM version < 5.0"); - } - instr - } - } - pub fn vector_reduce_or(&self, src: ValueRef) -> ValueRef { - self.count_insn("vector.reduce.or"); - unsafe { - let instr = llvm::LLVMRustBuildVectorReduceOr(self.llbuilder, src); - if instr.is_null() { - bug!("LLVMRustBuildVectorReduceOr is not available in LLVM version < 5.0"); - } - instr - } - } - pub fn vector_reduce_xor(&self, src: ValueRef) -> ValueRef { - self.count_insn("vector.reduce.xor"); - unsafe { - let instr = llvm::LLVMRustBuildVectorReduceXor(self.llbuilder, src); - if instr.is_null() { - bug!("LLVMRustBuildVectorReduceXor is not available in LLVM version < 5.0"); - } - instr - } - } - pub fn vector_reduce_fmin(&self, src: ValueRef) -> ValueRef { - self.count_insn("vector.reduce.fmin"); - unsafe { - let instr = llvm::LLVMRustBuildVectorReduceFMin(self.llbuilder, src, /*NoNaNs:*/ false); - if instr.is_null() { - bug!("LLVMRustBuildVectorReduceFMin is not available in LLVM version < 5.0"); - } - instr - } - } - pub fn vector_reduce_fmax(&self, src: ValueRef) -> ValueRef { - self.count_insn("vector.reduce.fmax"); - unsafe { - let instr = llvm::LLVMRustBuildVectorReduceFMax(self.llbuilder, src, /*NoNaNs:*/ false); - if instr.is_null() { - bug!("LLVMRustBuildVectorReduceFMax is not available in LLVM version < 5.0"); - } - instr - } - } - pub fn vector_reduce_fmin_fast(&self, src: ValueRef) -> ValueRef { - self.count_insn("vector.reduce.fmin_fast"); - unsafe { - let instr = llvm::LLVMRustBuildVectorReduceFMin(self.llbuilder, src, /*NoNaNs:*/ true); - if instr.is_null() { - bug!("LLVMRustBuildVectorReduceFMin is not available in LLVM version < 5.0"); - } - llvm::LLVMRustSetHasUnsafeAlgebra(instr); - instr - } - } - pub fn vector_reduce_fmax_fast(&self, src: ValueRef) -> ValueRef { - self.count_insn("vector.reduce.fmax_fast"); - unsafe { - let instr = llvm::LLVMRustBuildVectorReduceFMax(self.llbuilder, src, /*NoNaNs:*/ true); - if instr.is_null() { - bug!("LLVMRustBuildVectorReduceFMax is not available in LLVM version < 5.0"); - } - llvm::LLVMRustSetHasUnsafeAlgebra(instr); - instr - } - } - pub fn vector_reduce_min(&self, src: ValueRef, is_signed: bool) -> ValueRef { - self.count_insn("vector.reduce.min"); - unsafe { - let instr = llvm::LLVMRustBuildVectorReduceMin(self.llbuilder, src, is_signed); - if instr.is_null() { - bug!("LLVMRustBuildVectorReduceMin is not available in LLVM version < 5.0"); - } - instr - } - } - pub fn vector_reduce_max(&self, src: ValueRef, is_signed: bool) -> ValueRef { - self.count_insn("vector.reduce.max"); - unsafe { - let instr = llvm::LLVMRustBuildVectorReduceMax(self.llbuilder, src, is_signed); - if instr.is_null() { - bug!("LLVMRustBuildVectorReduceMax is not available in LLVM version < 5.0"); - } - instr - } - } - - pub fn extract_value(&self, agg_val: ValueRef, idx: u64) -> ValueRef { - self.count_insn("extractvalue"); - assert_eq!(idx as c_uint as u64, idx); - unsafe { - llvm::LLVMBuildExtractValue(self.llbuilder, agg_val, idx as c_uint, noname()) - } - } - - pub fn insert_value(&self, agg_val: ValueRef, elt: ValueRef, - idx: u64) -> ValueRef { - self.count_insn("insertvalue"); - assert_eq!(idx as c_uint as u64, idx); - unsafe { - llvm::LLVMBuildInsertValue(self.llbuilder, agg_val, elt, idx as c_uint, - noname()) - } - } - - pub fn is_null(&self, val: ValueRef) -> ValueRef { - self.count_insn("isnull"); - unsafe { - llvm::LLVMBuildIsNull(self.llbuilder, val, noname()) - } - } - - pub fn is_not_null(&self, val: ValueRef) -> ValueRef { - self.count_insn("isnotnull"); - unsafe { - llvm::LLVMBuildIsNotNull(self.llbuilder, val, noname()) - } - } - - pub fn ptrdiff(&self, lhs: ValueRef, rhs: ValueRef) -> ValueRef { - self.count_insn("ptrdiff"); - unsafe { - llvm::LLVMBuildPtrDiff(self.llbuilder, lhs, rhs, noname()) - } - } - - pub fn trap(&self) { - unsafe { - let bb: BasicBlockRef = llvm::LLVMGetInsertBlock(self.llbuilder); - let fn_: ValueRef = llvm::LLVMGetBasicBlockParent(bb); - let m: ModuleRef = llvm::LLVMGetGlobalParent(fn_); - let p = "llvm.trap\0".as_ptr(); - let t: ValueRef = llvm::LLVMGetNamedFunction(m, p as *const _); - assert!((t as isize != 0)); - let args: &[ValueRef] = &[]; - self.count_insn("trap"); - llvm::LLVMRustBuildCall(self.llbuilder, t, - args.as_ptr(), args.len() as c_uint, - ptr::null_mut(), - noname()); - } - } - - pub fn landing_pad(&self, ty: Type, pers_fn: ValueRef, - num_clauses: usize) -> ValueRef { - self.count_insn("landingpad"); - unsafe { - llvm::LLVMBuildLandingPad(self.llbuilder, ty.to_ref(), pers_fn, - num_clauses as c_uint, noname()) - } - } - - pub fn add_clause(&self, landing_pad: ValueRef, clause: ValueRef) { - unsafe { - llvm::LLVMAddClause(landing_pad, clause); - } - } - - pub fn set_cleanup(&self, landing_pad: ValueRef) { - self.count_insn("setcleanup"); - unsafe { - llvm::LLVMSetCleanup(landing_pad, llvm::True); - } - } - - pub fn resume(&self, exn: ValueRef) -> ValueRef { - self.count_insn("resume"); - unsafe { - llvm::LLVMBuildResume(self.llbuilder, exn) - } - } - - pub fn cleanup_pad(&self, - parent: Option, - args: &[ValueRef]) -> ValueRef { - self.count_insn("cleanuppad"); - let parent = parent.unwrap_or(ptr::null_mut()); - let name = CString::new("cleanuppad").unwrap(); - let ret = unsafe { - llvm::LLVMRustBuildCleanupPad(self.llbuilder, - parent, - args.len() as c_uint, - args.as_ptr(), - name.as_ptr()) - }; - assert!(!ret.is_null(), "LLVM does not have support for cleanuppad"); - return ret - } - - pub fn cleanup_ret(&self, cleanup: ValueRef, - unwind: Option) -> ValueRef { - self.count_insn("cleanupret"); - let unwind = unwind.unwrap_or(ptr::null_mut()); - let ret = unsafe { - llvm::LLVMRustBuildCleanupRet(self.llbuilder, cleanup, unwind) - }; - assert!(!ret.is_null(), "LLVM does not have support for cleanupret"); - return ret - } - - pub fn catch_pad(&self, - parent: ValueRef, - args: &[ValueRef]) -> ValueRef { - self.count_insn("catchpad"); - let name = CString::new("catchpad").unwrap(); - let ret = unsafe { - llvm::LLVMRustBuildCatchPad(self.llbuilder, parent, - args.len() as c_uint, args.as_ptr(), - name.as_ptr()) - }; - assert!(!ret.is_null(), "LLVM does not have support for catchpad"); - return ret - } - - pub fn catch_ret(&self, pad: ValueRef, unwind: BasicBlockRef) -> ValueRef { - self.count_insn("catchret"); - let ret = unsafe { - llvm::LLVMRustBuildCatchRet(self.llbuilder, pad, unwind) - }; - assert!(!ret.is_null(), "LLVM does not have support for catchret"); - return ret - } - - pub fn catch_switch(&self, - parent: Option, - unwind: Option, - num_handlers: usize) -> ValueRef { - self.count_insn("catchswitch"); - let parent = parent.unwrap_or(ptr::null_mut()); - let unwind = unwind.unwrap_or(ptr::null_mut()); - let name = CString::new("catchswitch").unwrap(); - let ret = unsafe { - llvm::LLVMRustBuildCatchSwitch(self.llbuilder, parent, unwind, - num_handlers as c_uint, - name.as_ptr()) - }; - assert!(!ret.is_null(), "LLVM does not have support for catchswitch"); - return ret - } - - pub fn add_handler(&self, catch_switch: ValueRef, handler: BasicBlockRef) { - unsafe { - llvm::LLVMRustAddHandler(catch_switch, handler); - } - } - - pub fn set_personality_fn(&self, personality: ValueRef) { - unsafe { - llvm::LLVMSetPersonalityFn(self.llfn(), personality); - } - } - - // Atomic Operations - pub fn atomic_cmpxchg(&self, dst: ValueRef, - cmp: ValueRef, src: ValueRef, - order: AtomicOrdering, - failure_order: AtomicOrdering, - weak: llvm::Bool) -> ValueRef { - unsafe { - llvm::LLVMRustBuildAtomicCmpXchg(self.llbuilder, dst, cmp, src, - order, failure_order, weak) - } - } - pub fn atomic_rmw(&self, op: AtomicRmwBinOp, - dst: ValueRef, src: ValueRef, - order: AtomicOrdering) -> ValueRef { - unsafe { - llvm::LLVMBuildAtomicRMW(self.llbuilder, op, dst, src, order, False) - } - } - - pub fn atomic_fence(&self, order: AtomicOrdering, scope: SynchronizationScope) { - unsafe { - llvm::LLVMRustBuildAtomicFence(self.llbuilder, order, scope); - } - } - - pub fn add_case(&self, s: ValueRef, on_val: ValueRef, dest: BasicBlockRef) { - unsafe { - llvm::LLVMAddCase(s, on_val, dest) - } - } - - pub fn add_incoming_to_phi(&self, phi: ValueRef, val: ValueRef, bb: BasicBlockRef) { - unsafe { - llvm::LLVMAddIncoming(phi, &val, &bb, 1 as c_uint); - } - } - - pub fn set_invariant_load(&self, load: ValueRef) { - unsafe { - llvm::LLVMSetMetadata(load, llvm::MD_invariant_load as c_uint, - llvm::LLVMMDNodeInContext(self.cx.llcx, ptr::null(), 0)); - } - } - - /// Returns the ptr value that should be used for storing `val`. - fn check_store<'b>(&self, - val: ValueRef, - ptr: ValueRef) -> ValueRef { - let dest_ptr_ty = val_ty(ptr); - let stored_ty = val_ty(val); - let stored_ptr_ty = stored_ty.ptr_to(); - - assert_eq!(dest_ptr_ty.kind(), llvm::TypeKind::Pointer); - - if dest_ptr_ty == stored_ptr_ty { - ptr - } else { - debug!("Type mismatch in store. \ - Expected {:?}, got {:?}; inserting bitcast", - dest_ptr_ty, stored_ptr_ty); - self.bitcast(ptr, stored_ptr_ty) - } - } - - /// Returns the args that should be used for a call to `llfn`. - fn check_call<'b>(&self, - typ: &str, - llfn: ValueRef, - args: &'b [ValueRef]) -> Cow<'b, [ValueRef]> { - let mut fn_ty = val_ty(llfn); - // Strip off pointers - while fn_ty.kind() == llvm::TypeKind::Pointer { - fn_ty = fn_ty.element_type(); - } - - assert!(fn_ty.kind() == llvm::TypeKind::Function, - "builder::{} not passed a function, but {:?}", typ, fn_ty); - - let param_tys = fn_ty.func_params(); - - let all_args_match = param_tys.iter() - .zip(args.iter().map(|&v| val_ty(v))) - .all(|(expected_ty, actual_ty)| *expected_ty == actual_ty); - - if all_args_match { - return Cow::Borrowed(args); - } - - let casted_args: Vec<_> = param_tys.into_iter() - .zip(args.iter()) - .enumerate() - .map(|(i, (expected_ty, &actual_val))| { - let actual_ty = val_ty(actual_val); - if expected_ty != actual_ty { - debug!("Type mismatch in function call of {:?}. \ - Expected {:?} for param {}, got {:?}; injecting bitcast", - Value(llfn), - expected_ty, i, actual_ty); - self.bitcast(actual_val, expected_ty) - } else { - actual_val - } - }) - .collect(); - - return Cow::Owned(casted_args); - } - - pub fn lifetime_start(&self, ptr: ValueRef, size: Size) { - self.call_lifetime_intrinsic("llvm.lifetime.start", ptr, size); - } - - pub fn lifetime_end(&self, ptr: ValueRef, size: Size) { - self.call_lifetime_intrinsic("llvm.lifetime.end", ptr, size); - } - - /// If LLVM lifetime intrinsic support is enabled (i.e. optimizations - /// on), and `ptr` is nonzero-sized, then extracts the size of `ptr` - /// and the intrinsic for `lt` and passes them to `emit`, which is in - /// charge of generating code to call the passed intrinsic on whatever - /// block of generated code is targeted for the intrinsic. - /// - /// If LLVM lifetime intrinsic support is disabled (i.e. optimizations - /// off) or `ptr` is zero-sized, then no-op (does not call `emit`). - fn call_lifetime_intrinsic(&self, intrinsic: &str, ptr: ValueRef, size: Size) { - if self.cx.sess().opts.optimize == config::OptLevel::No { - return; - } - - let size = size.bytes(); - if size == 0 { - return; - } - - let lifetime_intrinsic = self.cx.get_intrinsic(intrinsic); - - let ptr = self.pointercast(ptr, Type::i8p(self.cx)); - self.call(lifetime_intrinsic, &[C_u64(self.cx, size), ptr], None); - } -} diff --git a/src/librustc_trans/callee.rs b/src/librustc_trans/callee.rs deleted file mode 100644 index 9263d9a5f5d..00000000000 --- a/src/librustc_trans/callee.rs +++ /dev/null @@ -1,232 +0,0 @@ -// Copyright 2012 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. - -//! Handles translation of callees as well as other call-related -//! things. Callees are a superset of normal rust values and sometimes -//! have different representations. In particular, top-level fn items -//! and methods are represented as just a fn ptr and not a full -//! closure. - -use attributes; -use common::{self, CodegenCx}; -use consts; -use declare; -use llvm::{self, ValueRef}; -use monomorphize::Instance; -use type_of::LayoutLlvmExt; - -use rustc::hir::def_id::DefId; -use rustc::ty::{self, TypeFoldable}; -use rustc::ty::layout::LayoutOf; -use rustc::ty::subst::Substs; -use rustc_target::spec::PanicStrategy; - -/// Translates a reference to a fn/method item, monomorphizing and -/// inlining as it goes. -/// -/// # Parameters -/// -/// - `cx`: the crate context -/// - `instance`: the instance to be instantiated -pub fn get_fn<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, - instance: Instance<'tcx>) - -> ValueRef -{ - let tcx = cx.tcx; - - debug!("get_fn(instance={:?})", instance); - - assert!(!instance.substs.needs_infer()); - assert!(!instance.substs.has_escaping_regions()); - assert!(!instance.substs.has_param_types()); - - let fn_ty = instance.ty(cx.tcx); - if let Some(&llfn) = cx.instances.borrow().get(&instance) { - return llfn; - } - - let sym = tcx.symbol_name(instance).as_str(); - debug!("get_fn({:?}: {:?}) => {}", instance, fn_ty, sym); - - // Create a fn pointer with the substituted signature. - let fn_ptr_ty = tcx.mk_fn_ptr(common::ty_fn_sig(cx, fn_ty)); - let llptrty = cx.layout_of(fn_ptr_ty).llvm_type(cx); - - let llfn = if let Some(llfn) = declare::get_declared_value(cx, &sym) { - // This is subtle and surprising, but sometimes we have to bitcast - // the resulting fn pointer. The reason has to do with external - // functions. If you have two crates that both bind the same C - // library, they may not use precisely the same types: for - // example, they will probably each declare their own structs, - // which are distinct types from LLVM's point of view (nominal - // types). - // - // Now, if those two crates are linked into an application, and - // they contain inlined code, you can wind up with a situation - // where both of those functions wind up being loaded into this - // application simultaneously. In that case, the same function - // (from LLVM's point of view) requires two types. But of course - // LLVM won't allow one function to have two types. - // - // What we currently do, therefore, is declare the function with - // one of the two types (whichever happens to come first) and then - // bitcast as needed when the function is referenced to make sure - // it has the type we expect. - // - // This can occur on either a crate-local or crate-external - // reference. It also occurs when testing libcore and in some - // other weird situations. Annoying. - if common::val_ty(llfn) != llptrty { - debug!("get_fn: casting {:?} to {:?}", llfn, llptrty); - consts::ptrcast(llfn, llptrty) - } else { - debug!("get_fn: not casting pointer!"); - llfn - } - } else { - let llfn = declare::declare_fn(cx, &sym, fn_ty); - assert_eq!(common::val_ty(llfn), llptrty); - debug!("get_fn: not casting pointer!"); - - if instance.def.is_inline(tcx) { - attributes::inline(llfn, attributes::InlineAttr::Hint); - } - attributes::from_fn_attrs(cx, llfn, instance.def.def_id()); - - let instance_def_id = instance.def_id(); - - // Perhaps questionable, but we assume that anything defined - // *in Rust code* may unwind. Foreign items like `extern "C" { - // fn foo(); }` are assumed not to unwind **unless** they have - // a `#[unwind]` attribute. - if tcx.sess.panic_strategy() == PanicStrategy::Unwind { - if !tcx.is_foreign_item(instance_def_id) { - attributes::unwind(llfn, true); - } - } - - // Apply an appropriate linkage/visibility value to our item that we - // just declared. - // - // This is sort of subtle. Inside our codegen unit we started off - // compilation by predefining all our own `TransItem` instances. That - // is, everything we're translating ourselves is already defined. That - // means that anything we're actually translating in this codegen unit - // will have hit the above branch in `get_declared_value`. As a result, - // we're guaranteed here that we're declaring a symbol that won't get - // defined, or in other words we're referencing a value from another - // codegen unit or even another crate. - // - // So because this is a foreign value we blanket apply an external - // linkage directive because it's coming from a different object file. - // The visibility here is where it gets tricky. This symbol could be - // referencing some foreign crate or foreign library (an `extern` - // block) in which case we want to leave the default visibility. We may - // also, though, have multiple codegen units. It could be a - // monomorphization, in which case its expected visibility depends on - // whether we are sharing generics or not. The important thing here is - // that the visibility we apply to the declaration is the same one that - // has been applied to the definition (wherever that definition may be). - unsafe { - llvm::LLVMRustSetLinkage(llfn, llvm::Linkage::ExternalLinkage); - - let is_generic = instance.substs.types().next().is_some(); - - if is_generic { - // This is a monomorphization. Its expected visibility depends - // on whether we are in share-generics mode. - - if cx.tcx.share_generics() { - // We are in share_generics mode. - - if instance_def_id.is_local() { - // This is a definition from the current crate. If the - // definition is unreachable for downstream crates or - // the current crate does not re-export generics, the - // definition of the instance will have been declared - // as `hidden`. - if cx.tcx.is_unreachable_local_definition(instance_def_id) || - !cx.tcx.local_crate_exports_generics() { - llvm::LLVMRustSetVisibility(llfn, llvm::Visibility::Hidden); - } - } else { - // This is a monomorphization of a generic function - // defined in an upstream crate. - if cx.tcx.upstream_monomorphizations_for(instance_def_id) - .map(|set| set.contains_key(instance.substs)) - .unwrap_or(false) { - // This is instantiated in another crate. It cannot - // be `hidden`. - } else { - // This is a local instantiation of an upstream definition. - // If the current crate does not re-export it - // (because it is a C library or an executable), it - // will have been declared `hidden`. - if !cx.tcx.local_crate_exports_generics() { - llvm::LLVMRustSetVisibility(llfn, llvm::Visibility::Hidden); - } - } - } - } else { - // When not sharing generics, all instances are in the same - // crate and have hidden visibility - llvm::LLVMRustSetVisibility(llfn, llvm::Visibility::Hidden); - } - } else { - // This is a non-generic function - if cx.tcx.is_translated_item(instance_def_id) { - // This is a function that is instantiated in the local crate - - if instance_def_id.is_local() { - // This is function that is defined in the local crate. - // If it is not reachable, it is hidden. - if !cx.tcx.is_reachable_non_generic(instance_def_id) { - llvm::LLVMRustSetVisibility(llfn, llvm::Visibility::Hidden); - } - } else { - // This is a function from an upstream crate that has - // been instantiated here. These are always hidden. - llvm::LLVMRustSetVisibility(llfn, llvm::Visibility::Hidden); - } - } - } - } - - if cx.use_dll_storage_attrs && - tcx.is_dllimport_foreign_item(instance_def_id) - { - unsafe { - llvm::LLVMSetDLLStorageClass(llfn, llvm::DLLStorageClass::DllImport); - } - } - - llfn - }; - - cx.instances.borrow_mut().insert(instance, llfn); - - llfn -} - -pub fn resolve_and_get_fn<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, - def_id: DefId, - substs: &'tcx Substs<'tcx>) - -> ValueRef -{ - get_fn( - cx, - ty::Instance::resolve( - cx.tcx, - ty::ParamEnv::reveal_all(), - def_id, - substs - ).unwrap() - ) -} diff --git a/src/librustc_trans/common.rs b/src/librustc_trans/common.rs deleted file mode 100644 index 75b56be3c16..00000000000 --- a/src/librustc_trans/common.rs +++ /dev/null @@ -1,449 +0,0 @@ -// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -#![allow(non_camel_case_types, non_snake_case)] - -//! Code that is useful in various trans modules. - -use llvm; -use llvm::{ValueRef, ContextRef, TypeKind}; -use llvm::{True, False, Bool, OperandBundleDef}; -use rustc::hir::def_id::DefId; -use rustc::middle::lang_items::LangItem; -use abi; -use base; -use builder::Builder; -use consts; -use declare; -use type_::Type; -use type_of::LayoutLlvmExt; -use value::Value; -use rustc::ty::{self, Ty, TyCtxt}; -use rustc::ty::layout::{HasDataLayout, LayoutOf}; -use rustc::hir; - -use libc::{c_uint, c_char}; -use std::iter; - -use rustc_target::spec::abi::Abi; -use syntax::symbol::LocalInternedString; -use syntax_pos::{Span, DUMMY_SP}; - -pub use context::CodegenCx; - -pub fn type_needs_drop<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, ty: Ty<'tcx>) -> bool { - ty.needs_drop(tcx, ty::ParamEnv::reveal_all()) -} - -pub fn type_is_sized<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, ty: Ty<'tcx>) -> bool { - ty.is_sized(tcx.at(DUMMY_SP), ty::ParamEnv::reveal_all()) -} - -pub fn type_is_freeze<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, ty: Ty<'tcx>) -> bool { - ty.is_freeze(tcx, ty::ParamEnv::reveal_all(), DUMMY_SP) -} - -/* -* A note on nomenclature of linking: "extern", "foreign", and "upcall". -* -* An "extern" is an LLVM symbol we wind up emitting an undefined external -* reference to. This means "we don't have the thing in this compilation unit, -* please make sure you link it in at runtime". This could be a reference to -* C code found in a C library, or rust code found in a rust crate. -* -* Most "externs" are implicitly declared (automatically) as a result of a -* user declaring an extern _module_ dependency; this causes the rust driver -* to locate an extern crate, scan its compilation metadata, and emit extern -* declarations for any symbols used by the declaring crate. -* -* A "foreign" is an extern that references C (or other non-rust ABI) code. -* There is no metadata to scan for extern references so in these cases either -* a header-digester like bindgen, or manual function prototypes, have to -* serve as declarators. So these are usually given explicitly as prototype -* declarations, in rust code, with ABI attributes on them noting which ABI to -* link via. -* -* An "upcall" is a foreign call generated by the compiler (not corresponding -* to any user-written call in the code) into the runtime library, to perform -* some helper task such as bringing a task to life, allocating memory, etc. -* -*/ - -/// A structure representing an active landing pad for the duration of a basic -/// block. -/// -/// Each `Block` may contain an instance of this, indicating whether the block -/// is part of a landing pad or not. This is used to make decision about whether -/// to emit `invoke` instructions (e.g. in a landing pad we don't continue to -/// use `invoke`) and also about various function call metadata. -/// -/// For GNU exceptions (`landingpad` + `resume` instructions) this structure is -/// just a bunch of `None` instances (not too interesting), but for MSVC -/// exceptions (`cleanuppad` + `cleanupret` instructions) this contains data. -/// When inside of a landing pad, each function call in LLVM IR needs to be -/// annotated with which landing pad it's a part of. This is accomplished via -/// the `OperandBundleDef` value created for MSVC landing pads. -pub struct Funclet { - cleanuppad: ValueRef, - operand: OperandBundleDef, -} - -impl Funclet { - pub fn new(cleanuppad: ValueRef) -> Funclet { - Funclet { - cleanuppad, - operand: OperandBundleDef::new("funclet", &[cleanuppad]), - } - } - - pub fn cleanuppad(&self) -> ValueRef { - self.cleanuppad - } - - pub fn bundle(&self) -> &OperandBundleDef { - &self.operand - } -} - -pub fn val_ty(v: ValueRef) -> Type { - unsafe { - Type::from_ref(llvm::LLVMTypeOf(v)) - } -} - -// LLVM constant constructors. -pub fn C_null(t: Type) -> ValueRef { - unsafe { - llvm::LLVMConstNull(t.to_ref()) - } -} - -pub fn C_undef(t: Type) -> ValueRef { - unsafe { - llvm::LLVMGetUndef(t.to_ref()) - } -} - -pub fn C_int(t: Type, i: i64) -> ValueRef { - unsafe { - llvm::LLVMConstInt(t.to_ref(), i as u64, True) - } -} - -pub fn C_uint(t: Type, i: u64) -> ValueRef { - unsafe { - llvm::LLVMConstInt(t.to_ref(), i, False) - } -} - -pub fn C_uint_big(t: Type, u: u128) -> ValueRef { - unsafe { - let words = [u as u64, (u >> 64) as u64]; - llvm::LLVMConstIntOfArbitraryPrecision(t.to_ref(), 2, words.as_ptr()) - } -} - -pub fn C_bool(cx: &CodegenCx, val: bool) -> ValueRef { - C_uint(Type::i1(cx), val as u64) -} - -pub fn C_i32(cx: &CodegenCx, i: i32) -> ValueRef { - C_int(Type::i32(cx), i as i64) -} - -pub fn C_u32(cx: &CodegenCx, i: u32) -> ValueRef { - C_uint(Type::i32(cx), i as u64) -} - -pub fn C_u64(cx: &CodegenCx, i: u64) -> ValueRef { - C_uint(Type::i64(cx), i) -} - -pub fn C_usize(cx: &CodegenCx, i: u64) -> ValueRef { - let bit_size = cx.data_layout().pointer_size.bits(); - if bit_size < 64 { - // make sure it doesn't overflow - assert!(i < (1< ValueRef { - C_uint(Type::i8(cx), i as u64) -} - - -// This is a 'c-like' raw string, which differs from -// our boxed-and-length-annotated strings. -pub fn C_cstr(cx: &CodegenCx, s: LocalInternedString, null_terminated: bool) -> ValueRef { - unsafe { - if let Some(&llval) = cx.const_cstr_cache.borrow().get(&s) { - return llval; - } - - let sc = llvm::LLVMConstStringInContext(cx.llcx, - s.as_ptr() as *const c_char, - s.len() as c_uint, - !null_terminated as Bool); - let sym = cx.generate_local_symbol_name("str"); - let g = declare::define_global(cx, &sym[..], val_ty(sc)).unwrap_or_else(||{ - bug!("symbol `{}` is already defined", sym); - }); - llvm::LLVMSetInitializer(g, sc); - llvm::LLVMSetGlobalConstant(g, True); - llvm::LLVMRustSetLinkage(g, llvm::Linkage::InternalLinkage); - - cx.const_cstr_cache.borrow_mut().insert(s, g); - g - } -} - -// NB: Do not use `do_spill_noroot` to make this into a constant string, or -// you will be kicked off fast isel. See issue #4352 for an example of this. -pub fn C_str_slice(cx: &CodegenCx, s: LocalInternedString) -> ValueRef { - let len = s.len(); - let cs = consts::ptrcast(C_cstr(cx, s, false), - cx.layout_of(cx.tcx.mk_str()).llvm_type(cx).ptr_to()); - C_fat_ptr(cx, cs, C_usize(cx, len as u64)) -} - -pub fn C_fat_ptr(cx: &CodegenCx, ptr: ValueRef, meta: ValueRef) -> ValueRef { - assert_eq!(abi::FAT_PTR_ADDR, 0); - assert_eq!(abi::FAT_PTR_EXTRA, 1); - C_struct(cx, &[ptr, meta], false) -} - -pub fn C_struct(cx: &CodegenCx, elts: &[ValueRef], packed: bool) -> ValueRef { - C_struct_in_context(cx.llcx, elts, packed) -} - -pub fn C_struct_in_context(llcx: ContextRef, elts: &[ValueRef], packed: bool) -> ValueRef { - unsafe { - llvm::LLVMConstStructInContext(llcx, - elts.as_ptr(), elts.len() as c_uint, - packed as Bool) - } -} - -pub fn C_array(ty: Type, elts: &[ValueRef]) -> ValueRef { - unsafe { - return llvm::LLVMConstArray(ty.to_ref(), elts.as_ptr(), elts.len() as c_uint); - } -} - -pub fn C_vector(elts: &[ValueRef]) -> ValueRef { - unsafe { - return llvm::LLVMConstVector(elts.as_ptr(), elts.len() as c_uint); - } -} - -pub fn C_bytes(cx: &CodegenCx, bytes: &[u8]) -> ValueRef { - C_bytes_in_context(cx.llcx, bytes) -} - -pub fn C_bytes_in_context(llcx: ContextRef, bytes: &[u8]) -> ValueRef { - unsafe { - let ptr = bytes.as_ptr() as *const c_char; - return llvm::LLVMConstStringInContext(llcx, ptr, bytes.len() as c_uint, True); - } -} - -pub fn const_get_elt(v: ValueRef, idx: u64) -> ValueRef { - unsafe { - assert_eq!(idx as c_uint as u64, idx); - let us = &[idx as c_uint]; - let r = llvm::LLVMConstExtractValue(v, us.as_ptr(), us.len() as c_uint); - - debug!("const_get_elt(v={:?}, idx={}, r={:?})", - Value(v), idx, Value(r)); - - r - } -} - -pub fn const_get_real(v: ValueRef) -> Option<(f64, bool)> { - unsafe { - if is_const_real(v) { - let mut loses_info: llvm::Bool = ::std::mem::uninitialized(); - let r = llvm::LLVMConstRealGetDouble(v, &mut loses_info as *mut llvm::Bool); - let loses_info = if loses_info == 1 { true } else { false }; - Some((r, loses_info)) - } else { - None - } - } -} - -pub fn const_to_uint(v: ValueRef) -> u64 { - unsafe { - llvm::LLVMConstIntGetZExtValue(v) - } -} - -pub fn is_const_integral(v: ValueRef) -> bool { - unsafe { - !llvm::LLVMIsAConstantInt(v).is_null() - } -} - -pub fn is_const_real(v: ValueRef) -> bool { - unsafe { - !llvm::LLVMIsAConstantFP(v).is_null() - } -} - - -#[inline] -fn hi_lo_to_u128(lo: u64, hi: u64) -> u128 { - ((hi as u128) << 64) | (lo as u128) -} - -pub fn const_to_opt_u128(v: ValueRef, sign_ext: bool) -> Option { - unsafe { - if is_const_integral(v) { - let (mut lo, mut hi) = (0u64, 0u64); - let success = llvm::LLVMRustConstInt128Get(v, sign_ext, - &mut hi as *mut u64, &mut lo as *mut u64); - if success { - Some(hi_lo_to_u128(lo, hi)) - } else { - None - } - } else { - None - } - } -} - -pub fn langcall(tcx: TyCtxt, - span: Option, - msg: &str, - li: LangItem) - -> DefId { - match tcx.lang_items().require(li) { - Ok(id) => id, - Err(s) => { - let msg = format!("{} {}", msg, s); - match span { - Some(span) => tcx.sess.span_fatal(span, &msg[..]), - None => tcx.sess.fatal(&msg[..]), - } - } - } -} - -// To avoid UB from LLVM, these two functions mask RHS with an -// appropriate mask unconditionally (i.e. the fallback behavior for -// all shifts). For 32- and 64-bit types, this matches the semantics -// of Java. (See related discussion on #1877 and #10183.) - -pub fn build_unchecked_lshift<'a, 'tcx>( - bx: &Builder<'a, 'tcx>, - lhs: ValueRef, - rhs: ValueRef -) -> ValueRef { - let rhs = base::cast_shift_expr_rhs(bx, hir::BinOp_::BiShl, lhs, rhs); - // #1877, #10183: Ensure that input is always valid - let rhs = shift_mask_rhs(bx, rhs); - bx.shl(lhs, rhs) -} - -pub fn build_unchecked_rshift<'a, 'tcx>( - bx: &Builder<'a, 'tcx>, lhs_t: Ty<'tcx>, lhs: ValueRef, rhs: ValueRef -) -> ValueRef { - let rhs = base::cast_shift_expr_rhs(bx, hir::BinOp_::BiShr, lhs, rhs); - // #1877, #10183: Ensure that input is always valid - let rhs = shift_mask_rhs(bx, rhs); - let is_signed = lhs_t.is_signed(); - if is_signed { - bx.ashr(lhs, rhs) - } else { - bx.lshr(lhs, rhs) - } -} - -fn shift_mask_rhs<'a, 'tcx>(bx: &Builder<'a, 'tcx>, rhs: ValueRef) -> ValueRef { - let rhs_llty = val_ty(rhs); - bx.and(rhs, shift_mask_val(bx, rhs_llty, rhs_llty, false)) -} - -pub fn shift_mask_val<'a, 'tcx>( - bx: &Builder<'a, 'tcx>, - llty: Type, - mask_llty: Type, - invert: bool -) -> ValueRef { - let kind = llty.kind(); - match kind { - TypeKind::Integer => { - // i8/u8 can shift by at most 7, i16/u16 by at most 15, etc. - let val = llty.int_width() - 1; - if invert { - C_int(mask_llty, !val as i64) - } else { - C_uint(mask_llty, val) - } - }, - TypeKind::Vector => { - let mask = shift_mask_val(bx, llty.element_type(), mask_llty.element_type(), invert); - bx.vector_splat(mask_llty.vector_length(), mask) - }, - _ => bug!("shift_mask_val: expected Integer or Vector, found {:?}", kind), - } -} - -pub fn ty_fn_sig<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, - ty: Ty<'tcx>) - -> ty::PolyFnSig<'tcx> -{ - match ty.sty { - ty::TyFnDef(..) | - // Shims currently have type TyFnPtr. Not sure this should remain. - ty::TyFnPtr(_) => ty.fn_sig(cx.tcx), - ty::TyClosure(def_id, substs) => { - let tcx = cx.tcx; - let sig = substs.closure_sig(def_id, tcx); - - let env_ty = tcx.closure_env_ty(def_id, substs).unwrap(); - sig.map_bound(|sig| tcx.mk_fn_sig( - iter::once(*env_ty.skip_binder()).chain(sig.inputs().iter().cloned()), - sig.output(), - sig.variadic, - sig.unsafety, - sig.abi - )) - } - ty::TyGenerator(def_id, substs, _) => { - let tcx = cx.tcx; - let sig = substs.poly_sig(def_id, cx.tcx); - - let env_region = ty::ReLateBound(ty::DebruijnIndex::new(1), ty::BrEnv); - let env_ty = tcx.mk_mut_ref(tcx.mk_region(env_region), ty); - - sig.map_bound(|sig| { - let state_did = tcx.lang_items().gen_state().unwrap(); - let state_adt_ref = tcx.adt_def(state_did); - let state_substs = tcx.mk_substs([sig.yield_ty.into(), - sig.return_ty.into()].iter()); - let ret_ty = tcx.mk_adt(state_adt_ref, state_substs); - - tcx.mk_fn_sig(iter::once(env_ty), - ret_ty, - false, - hir::Unsafety::Normal, - Abi::Rust - ) - }) - } - _ => bug!("unexpected type {:?} to ty_fn_sig", ty) - } -} - diff --git a/src/librustc_trans/consts.rs b/src/librustc_trans/consts.rs deleted file mode 100644 index 405cb83ad4d..00000000000 --- a/src/librustc_trans/consts.rs +++ /dev/null @@ -1,322 +0,0 @@ -// Copyright 2012 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -use llvm; -use llvm::{SetUnnamedAddr}; -use llvm::{ValueRef, True}; -use rustc::hir::def_id::DefId; -use rustc::hir::map as hir_map; -use debuginfo; -use base; -use monomorphize::MonoItem; -use common::{CodegenCx, val_ty}; -use declare; -use monomorphize::Instance; -use type_::Type; -use type_of::LayoutLlvmExt; -use rustc::ty; -use rustc::ty::layout::{Align, LayoutOf}; - -use rustc::hir; - -use std::ffi::{CStr, CString}; -use syntax::ast; -use syntax::attr; - -pub fn ptrcast(val: ValueRef, ty: Type) -> ValueRef { - unsafe { - llvm::LLVMConstPointerCast(val, ty.to_ref()) - } -} - -pub fn bitcast(val: ValueRef, ty: Type) -> ValueRef { - unsafe { - llvm::LLVMConstBitCast(val, ty.to_ref()) - } -} - -fn set_global_alignment(cx: &CodegenCx, - gv: ValueRef, - mut align: Align) { - // The target may require greater alignment for globals than the type does. - // Note: GCC and Clang also allow `__attribute__((aligned))` on variables, - // which can force it to be smaller. Rust doesn't support this yet. - if let Some(min) = cx.sess().target.target.options.min_global_align { - match ty::layout::Align::from_bits(min, min) { - Ok(min) => align = align.max(min), - Err(err) => { - cx.sess().err(&format!("invalid minimum global alignment: {}", err)); - } - } - } - unsafe { - llvm::LLVMSetAlignment(gv, align.abi() as u32); - } -} - -pub fn addr_of_mut(cx: &CodegenCx, - cv: ValueRef, - align: Align, - kind: &str) - -> ValueRef { - unsafe { - let name = cx.generate_local_symbol_name(kind); - let gv = declare::define_global(cx, &name[..], val_ty(cv)).unwrap_or_else(||{ - bug!("symbol `{}` is already defined", name); - }); - llvm::LLVMSetInitializer(gv, cv); - set_global_alignment(cx, gv, align); - llvm::LLVMRustSetLinkage(gv, llvm::Linkage::PrivateLinkage); - SetUnnamedAddr(gv, true); - gv - } -} - -pub fn addr_of(cx: &CodegenCx, - cv: ValueRef, - align: Align, - kind: &str) - -> ValueRef { - if let Some(&gv) = cx.const_globals.borrow().get(&cv) { - unsafe { - // Upgrade the alignment in cases where the same constant is used with different - // alignment requirements - let llalign = align.abi() as u32; - if llalign > llvm::LLVMGetAlignment(gv) { - llvm::LLVMSetAlignment(gv, llalign); - } - } - return gv; - } - let gv = addr_of_mut(cx, cv, align, kind); - unsafe { - llvm::LLVMSetGlobalConstant(gv, True); - } - cx.const_globals.borrow_mut().insert(cv, gv); - gv -} - -pub fn get_static(cx: &CodegenCx, def_id: DefId) -> ValueRef { - let instance = Instance::mono(cx.tcx, def_id); - if let Some(&g) = cx.instances.borrow().get(&instance) { - return g; - } - - let defined_in_current_codegen_unit = cx.codegen_unit - .items() - .contains_key(&MonoItem::Static(def_id)); - assert!(!defined_in_current_codegen_unit, - "consts::get_static() should always hit the cache for \ - statics defined in the same CGU, but did not for `{:?}`", - def_id); - - let ty = instance.ty(cx.tcx); - let sym = cx.tcx.symbol_name(instance).as_str(); - - let g = if let Some(id) = cx.tcx.hir.as_local_node_id(def_id) { - - let llty = cx.layout_of(ty).llvm_type(cx); - let (g, attrs) = match cx.tcx.hir.get(id) { - hir_map::NodeItem(&hir::Item { - ref attrs, span, node: hir::ItemStatic(..), .. - }) => { - if declare::get_declared_value(cx, &sym[..]).is_some() { - span_bug!(span, "trans: Conflicting symbol names for static?"); - } - - let g = declare::define_global(cx, &sym[..], llty).unwrap(); - - if !cx.tcx.is_reachable_non_generic(def_id) { - unsafe { - llvm::LLVMRustSetVisibility(g, llvm::Visibility::Hidden); - } - } - - (g, attrs) - } - - hir_map::NodeForeignItem(&hir::ForeignItem { - ref attrs, span, node: hir::ForeignItemStatic(..), .. - }) => { - let g = if let Some(linkage) = cx.tcx.trans_fn_attrs(def_id).linkage { - // If this is a static with a linkage specified, then we need to handle - // it a little specially. The typesystem prevents things like &T and - // extern "C" fn() from being non-null, so we can't just declare a - // static and call it a day. Some linkages (like weak) will make it such - // that the static actually has a null value. - let llty2 = match ty.sty { - ty::TyRawPtr(ref mt) => cx.layout_of(mt.ty).llvm_type(cx), - _ => { - cx.sess().span_fatal(span, "must have type `*const T` or `*mut T`"); - } - }; - unsafe { - // Declare a symbol `foo` with the desired linkage. - let g1 = declare::declare_global(cx, &sym, llty2); - llvm::LLVMRustSetLinkage(g1, base::linkage_to_llvm(linkage)); - - // Declare an internal global `extern_with_linkage_foo` which - // is initialized with the address of `foo`. If `foo` is - // discarded during linking (for example, if `foo` has weak - // linkage and there are no definitions), then - // `extern_with_linkage_foo` will instead be initialized to - // zero. - let mut real_name = "_rust_extern_with_linkage_".to_string(); - real_name.push_str(&sym); - let g2 = declare::define_global(cx, &real_name, llty).unwrap_or_else(||{ - cx.sess().span_fatal(span, - &format!("symbol `{}` is already defined", &sym)) - }); - llvm::LLVMRustSetLinkage(g2, llvm::Linkage::InternalLinkage); - llvm::LLVMSetInitializer(g2, g1); - g2 - } - } else { - // Generate an external declaration. - declare::declare_global(cx, &sym, llty) - }; - - (g, attrs) - } - - item => bug!("get_static: expected static, found {:?}", item) - }; - - for attr in attrs { - if attr.check_name("thread_local") { - llvm::set_thread_local_mode(g, cx.tls_model); - } - } - - g - } else { - // FIXME(nagisa): perhaps the map of externs could be offloaded to llvm somehow? - // FIXME(nagisa): investigate whether it can be changed into define_global - let g = declare::declare_global(cx, &sym, cx.layout_of(ty).llvm_type(cx)); - // Thread-local statics in some other crate need to *always* be linked - // against in a thread-local fashion, so we need to be sure to apply the - // thread-local attribute locally if it was present remotely. If we - // don't do this then linker errors can be generated where the linker - // complains that one object files has a thread local version of the - // symbol and another one doesn't. - for attr in cx.tcx.get_attrs(def_id).iter() { - if attr.check_name("thread_local") { - llvm::set_thread_local_mode(g, cx.tls_model); - } - } - if cx.use_dll_storage_attrs && !cx.tcx.is_foreign_item(def_id) { - // This item is external but not foreign, i.e. it originates from an external Rust - // crate. Since we don't know whether this crate will be linked dynamically or - // statically in the final application, we always mark such symbols as 'dllimport'. - // If final linkage happens to be static, we rely on compiler-emitted __imp_ stubs to - // make things work. - // - // However, in some scenarios we defer emission of statics to downstream - // crates, so there are cases where a static with an upstream DefId - // is actually present in the current crate. We can find out via the - // is_translated_item query. - if !cx.tcx.is_translated_item(def_id) { - unsafe { - llvm::LLVMSetDLLStorageClass(g, llvm::DLLStorageClass::DllImport); - } - } - } - g - }; - - if cx.use_dll_storage_attrs && cx.tcx.is_dllimport_foreign_item(def_id) { - // For foreign (native) libs we know the exact storage type to use. - unsafe { - llvm::LLVMSetDLLStorageClass(g, llvm::DLLStorageClass::DllImport); - } - } - - cx.instances.borrow_mut().insert(instance, g); - cx.statics.borrow_mut().insert(g, def_id); - g -} - -pub fn trans_static<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, - def_id: DefId, - is_mutable: bool, - attrs: &[ast::Attribute]) { - unsafe { - let g = get_static(cx, def_id); - - let v = match ::mir::trans_static_initializer(cx, def_id) { - Ok(v) => v, - // Error has already been reported - Err(_) => return, - }; - - // boolean SSA values are i1, but they have to be stored in i8 slots, - // otherwise some LLVM optimization passes don't work as expected - let mut val_llty = val_ty(v); - let v = if val_llty == Type::i1(cx) { - val_llty = Type::i8(cx); - llvm::LLVMConstZExt(v, val_llty.to_ref()) - } else { - v - }; - - let instance = Instance::mono(cx.tcx, def_id); - let ty = instance.ty(cx.tcx); - let llty = cx.layout_of(ty).llvm_type(cx); - let g = if val_llty == llty { - g - } else { - // If we created the global with the wrong type, - // correct the type. - let empty_string = CString::new("").unwrap(); - let name_str_ref = CStr::from_ptr(llvm::LLVMGetValueName(g)); - let name_string = CString::new(name_str_ref.to_bytes()).unwrap(); - llvm::LLVMSetValueName(g, empty_string.as_ptr()); - - let linkage = llvm::LLVMRustGetLinkage(g); - let visibility = llvm::LLVMRustGetVisibility(g); - - let new_g = llvm::LLVMRustGetOrInsertGlobal( - cx.llmod, name_string.as_ptr(), val_llty.to_ref()); - - llvm::LLVMRustSetLinkage(new_g, linkage); - llvm::LLVMRustSetVisibility(new_g, visibility); - - // To avoid breaking any invariants, we leave around the old - // global for the moment; we'll replace all references to it - // with the new global later. (See base::trans_crate.) - cx.statics_to_rauw.borrow_mut().push((g, new_g)); - new_g - }; - set_global_alignment(cx, g, cx.align_of(ty)); - llvm::LLVMSetInitializer(g, v); - - // As an optimization, all shared statics which do not have interior - // mutability are placed into read-only memory. - if !is_mutable { - if cx.type_is_freeze(ty) { - llvm::LLVMSetGlobalConstant(g, llvm::True); - } - } - - debuginfo::create_global_var_metadata(cx, def_id, g); - - if attr::contains_name(attrs, "thread_local") { - llvm::set_thread_local_mode(g, cx.tls_model); - } - - base::set_link_section(cx, g, attrs); - - if attr::contains_name(attrs, "used") { - // This static will be stored in the llvm.used variable which is an array of i8* - let cast = llvm::LLVMConstPointerCast(g, Type::i8p(cx).to_ref()); - cx.used_statics.borrow_mut().push(cast); - } - } -} diff --git a/src/librustc_trans/context.rs b/src/librustc_trans/context.rs deleted file mode 100644 index 90b2fb4b59a..00000000000 --- a/src/librustc_trans/context.rs +++ /dev/null @@ -1,666 +0,0 @@ -// Copyright 2013 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -use common; -use llvm; -use llvm::{ContextRef, ModuleRef, ValueRef}; -use rustc::dep_graph::DepGraphSafe; -use rustc::hir; -use rustc::hir::def_id::DefId; -use debuginfo; -use callee; -use base; -use declare; -use monomorphize::Instance; - -use monomorphize::partitioning::CodegenUnit; -use type_::Type; -use type_of::PointeeInfo; - -use rustc_data_structures::base_n; -use rustc::mir::mono::Stats; -use rustc::session::config::{self, NoDebugInfo}; -use rustc::session::Session; -use rustc::ty::layout::{LayoutError, LayoutOf, Size, TyLayout}; -use rustc::ty::{self, Ty, TyCtxt}; -use rustc::util::nodemap::FxHashMap; -use rustc_target::spec::{HasTargetSpec, Target}; - -use std::ffi::{CStr, CString}; -use std::cell::{Cell, RefCell}; -use std::ptr; -use std::iter; -use std::str; -use std::sync::Arc; -use syntax::symbol::LocalInternedString; -use abi::Abi; - -/// There is one `CodegenCx` per compilation unit. Each one has its own LLVM -/// `ContextRef` so that several compilation units may be optimized in parallel. -/// All other LLVM data structures in the `CodegenCx` are tied to that `ContextRef`. -pub struct CodegenCx<'a, 'tcx: 'a> { - pub tcx: TyCtxt<'a, 'tcx, 'tcx>, - pub check_overflow: bool, - pub use_dll_storage_attrs: bool, - pub tls_model: llvm::ThreadLocalMode, - - pub llmod: ModuleRef, - pub llcx: ContextRef, - pub stats: RefCell, - pub codegen_unit: Arc>, - - /// Cache instances of monomorphic and polymorphic items - pub instances: RefCell, ValueRef>>, - /// Cache generated vtables - pub vtables: RefCell, - Option>), ValueRef>>, - /// Cache of constant strings, - pub const_cstr_cache: RefCell>, - - /// Reverse-direction for const ptrs cast from globals. - /// Key is a ValueRef holding a *T, - /// Val is a ValueRef holding a *[T]. - /// - /// Needed because LLVM loses pointer->pointee association - /// when we ptrcast, and we have to ptrcast during translation - /// of a [T] const because we form a slice, a (*T,usize) pair, not - /// a pointer to an LLVM array type. Similar for trait objects. - pub const_unsized: RefCell>, - - /// Cache of emitted const globals (value -> global) - pub const_globals: RefCell>, - - /// Mapping from static definitions to their DefId's. - pub statics: RefCell>, - - /// List of globals for static variables which need to be passed to the - /// LLVM function ReplaceAllUsesWith (RAUW) when translation is complete. - /// (We have to make sure we don't invalidate any ValueRefs referring - /// to constants.) - pub statics_to_rauw: RefCell>, - - /// Statics that will be placed in the llvm.used variable - /// See http://llvm.org/docs/LangRef.html#the-llvm-used-global-variable for details - pub used_statics: RefCell>, - - pub lltypes: RefCell, Option), Type>>, - pub scalar_lltypes: RefCell, Type>>, - pub pointee_infos: RefCell, Size), Option>>, - pub isize_ty: Type, - - pub dbg_cx: Option>, - - eh_personality: Cell>, - eh_unwind_resume: Cell>, - pub rust_try_fn: Cell>, - - intrinsics: RefCell>, - - /// A counter that is used for generating local symbol names - local_gen_sym_counter: Cell, -} - -impl<'a, 'tcx> DepGraphSafe for CodegenCx<'a, 'tcx> { -} - -pub fn get_reloc_model(sess: &Session) -> llvm::RelocMode { - let reloc_model_arg = match sess.opts.cg.relocation_model { - Some(ref s) => &s[..], - None => &sess.target.target.options.relocation_model[..], - }; - - match ::back::write::RELOC_MODEL_ARGS.iter().find( - |&&arg| arg.0 == reloc_model_arg) { - Some(x) => x.1, - _ => { - sess.err(&format!("{:?} is not a valid relocation mode", - reloc_model_arg)); - sess.abort_if_errors(); - bug!(); - } - } -} - -fn get_tls_model(sess: &Session) -> llvm::ThreadLocalMode { - let tls_model_arg = match sess.opts.debugging_opts.tls_model { - Some(ref s) => &s[..], - None => &sess.target.target.options.tls_model[..], - }; - - match ::back::write::TLS_MODEL_ARGS.iter().find( - |&&arg| arg.0 == tls_model_arg) { - Some(x) => x.1, - _ => { - sess.err(&format!("{:?} is not a valid TLS model", - tls_model_arg)); - sess.abort_if_errors(); - bug!(); - } - } -} - -fn is_any_library(sess: &Session) -> bool { - sess.crate_types.borrow().iter().any(|ty| { - *ty != config::CrateTypeExecutable - }) -} - -pub fn is_pie_binary(sess: &Session) -> bool { - !is_any_library(sess) && get_reloc_model(sess) == llvm::RelocMode::PIC -} - -pub unsafe fn create_context_and_module(sess: &Session, mod_name: &str) -> (ContextRef, ModuleRef) { - let llcx = llvm::LLVMRustContextCreate(sess.fewer_names()); - let mod_name = CString::new(mod_name).unwrap(); - let llmod = llvm::LLVMModuleCreateWithNameInContext(mod_name.as_ptr(), llcx); - - // Ensure the data-layout values hardcoded remain the defaults. - if sess.target.target.options.is_builtin { - let tm = ::back::write::create_target_machine(sess, false); - llvm::LLVMRustSetDataLayoutFromTargetMachine(llmod, tm); - llvm::LLVMRustDisposeTargetMachine(tm); - - let data_layout = llvm::LLVMGetDataLayout(llmod); - let data_layout = str::from_utf8(CStr::from_ptr(data_layout).to_bytes()) - .ok().expect("got a non-UTF8 data-layout from LLVM"); - - // Unfortunately LLVM target specs change over time, and right now we - // don't have proper support to work with any more than one - // `data_layout` than the one that is in the rust-lang/rust repo. If - // this compiler is configured against a custom LLVM, we may have a - // differing data layout, even though we should update our own to use - // that one. - // - // As an interim hack, if CFG_LLVM_ROOT is not an empty string then we - // disable this check entirely as we may be configured with something - // that has a different target layout. - // - // Unsure if this will actually cause breakage when rustc is configured - // as such. - // - // FIXME(#34960) - let cfg_llvm_root = option_env!("CFG_LLVM_ROOT").unwrap_or(""); - let custom_llvm_used = cfg_llvm_root.trim() != ""; - - if !custom_llvm_used && sess.target.target.data_layout != data_layout { - bug!("data-layout for builtin `{}` target, `{}`, \ - differs from LLVM default, `{}`", - sess.target.target.llvm_target, - sess.target.target.data_layout, - data_layout); - } - } - - let data_layout = CString::new(&sess.target.target.data_layout[..]).unwrap(); - llvm::LLVMSetDataLayout(llmod, data_layout.as_ptr()); - - let llvm_target = sess.target.target.llvm_target.as_bytes(); - let llvm_target = CString::new(llvm_target).unwrap(); - llvm::LLVMRustSetNormalizedTarget(llmod, llvm_target.as_ptr()); - - if is_pie_binary(sess) { - llvm::LLVMRustSetModulePIELevel(llmod); - } - - (llcx, llmod) -} - -impl<'a, 'tcx> CodegenCx<'a, 'tcx> { - pub fn new(tcx: TyCtxt<'a, 'tcx, 'tcx>, - codegen_unit: Arc>, - llmod_id: &str) - -> CodegenCx<'a, 'tcx> { - // An interesting part of Windows which MSVC forces our hand on (and - // apparently MinGW didn't) is the usage of `dllimport` and `dllexport` - // attributes in LLVM IR as well as native dependencies (in C these - // correspond to `__declspec(dllimport)`). - // - // Whenever a dynamic library is built by MSVC it must have its public - // interface specified by functions tagged with `dllexport` or otherwise - // they're not available to be linked against. This poses a few problems - // for the compiler, some of which are somewhat fundamental, but we use - // the `use_dll_storage_attrs` variable below to attach the `dllexport` - // attribute to all LLVM functions that are exported e.g. they're - // already tagged with external linkage). This is suboptimal for a few - // reasons: - // - // * If an object file will never be included in a dynamic library, - // there's no need to attach the dllexport attribute. Most object - // files in Rust are not destined to become part of a dll as binaries - // are statically linked by default. - // * If the compiler is emitting both an rlib and a dylib, the same - // source object file is currently used but with MSVC this may be less - // feasible. The compiler may be able to get around this, but it may - // involve some invasive changes to deal with this. - // - // The flipside of this situation is that whenever you link to a dll and - // you import a function from it, the import should be tagged with - // `dllimport`. At this time, however, the compiler does not emit - // `dllimport` for any declarations other than constants (where it is - // required), which is again suboptimal for even more reasons! - // - // * Calling a function imported from another dll without using - // `dllimport` causes the linker/compiler to have extra overhead (one - // `jmp` instruction on x86) when calling the function. - // * The same object file may be used in different circumstances, so a - // function may be imported from a dll if the object is linked into a - // dll, but it may be just linked against if linked into an rlib. - // * The compiler has no knowledge about whether native functions should - // be tagged dllimport or not. - // - // For now the compiler takes the perf hit (I do not have any numbers to - // this effect) by marking very little as `dllimport` and praying the - // linker will take care of everything. Fixing this problem will likely - // require adding a few attributes to Rust itself (feature gated at the - // start) and then strongly recommending static linkage on MSVC! - let use_dll_storage_attrs = tcx.sess.target.target.options.is_like_msvc; - - let check_overflow = tcx.sess.overflow_checks(); - - let tls_model = get_tls_model(&tcx.sess); - - unsafe { - let (llcx, llmod) = create_context_and_module(&tcx.sess, - &llmod_id[..]); - - let dbg_cx = if tcx.sess.opts.debuginfo != NoDebugInfo { - let dctx = debuginfo::CrateDebugContext::new(llmod); - debuginfo::metadata::compile_unit_metadata(tcx, - &codegen_unit.name().as_str(), - &dctx); - Some(dctx) - } else { - None - }; - - let mut cx = CodegenCx { - tcx, - check_overflow, - use_dll_storage_attrs, - tls_model, - llmod, - llcx, - stats: RefCell::new(Stats::default()), - codegen_unit, - instances: RefCell::new(FxHashMap()), - vtables: RefCell::new(FxHashMap()), - const_cstr_cache: RefCell::new(FxHashMap()), - const_unsized: RefCell::new(FxHashMap()), - const_globals: RefCell::new(FxHashMap()), - statics: RefCell::new(FxHashMap()), - statics_to_rauw: RefCell::new(Vec::new()), - used_statics: RefCell::new(Vec::new()), - lltypes: RefCell::new(FxHashMap()), - scalar_lltypes: RefCell::new(FxHashMap()), - pointee_infos: RefCell::new(FxHashMap()), - isize_ty: Type::from_ref(ptr::null_mut()), - dbg_cx, - eh_personality: Cell::new(None), - eh_unwind_resume: Cell::new(None), - rust_try_fn: Cell::new(None), - intrinsics: RefCell::new(FxHashMap()), - local_gen_sym_counter: Cell::new(0), - }; - cx.isize_ty = Type::isize(&cx); - cx - } - } - - pub fn into_stats(self) -> Stats { - self.stats.into_inner() - } -} - -impl<'b, 'tcx> CodegenCx<'b, 'tcx> { - pub fn sess<'a>(&'a self) -> &'a Session { - &self.tcx.sess - } - - pub fn get_intrinsic(&self, key: &str) -> ValueRef { - if let Some(v) = self.intrinsics.borrow().get(key).cloned() { - return v; - } - match declare_intrinsic(self, key) { - Some(v) => return v, - None => bug!("unknown intrinsic '{}'", key) - } - } - - /// Generate a new symbol name with the given prefix. This symbol name must - /// only be used for definitions with `internal` or `private` linkage. - pub fn generate_local_symbol_name(&self, prefix: &str) -> String { - let idx = self.local_gen_sym_counter.get(); - self.local_gen_sym_counter.set(idx + 1); - // Include a '.' character, so there can be no accidental conflicts with - // user defined names - let mut name = String::with_capacity(prefix.len() + 6); - name.push_str(prefix); - name.push_str("."); - base_n::push_str(idx as u128, base_n::ALPHANUMERIC_ONLY, &mut name); - name - } - - pub fn eh_personality(&self) -> ValueRef { - // The exception handling personality function. - // - // If our compilation unit has the `eh_personality` lang item somewhere - // within it, then we just need to translate that. Otherwise, we're - // building an rlib which will depend on some upstream implementation of - // this function, so we just codegen a generic reference to it. We don't - // specify any of the types for the function, we just make it a symbol - // that LLVM can later use. - // - // Note that MSVC is a little special here in that we don't use the - // `eh_personality` lang item at all. Currently LLVM has support for - // both Dwarf and SEH unwind mechanisms for MSVC targets and uses the - // *name of the personality function* to decide what kind of unwind side - // tables/landing pads to emit. It looks like Dwarf is used by default, - // injecting a dependency on the `_Unwind_Resume` symbol for resuming - // an "exception", but for MSVC we want to force SEH. This means that we - // can't actually have the personality function be our standard - // `rust_eh_personality` function, but rather we wired it up to the - // CRT's custom personality function, which forces LLVM to consider - // landing pads as "landing pads for SEH". - if let Some(llpersonality) = self.eh_personality.get() { - return llpersonality - } - let tcx = self.tcx; - let llfn = match tcx.lang_items().eh_personality() { - Some(def_id) if !base::wants_msvc_seh(self.sess()) => { - callee::resolve_and_get_fn(self, def_id, tcx.intern_substs(&[])) - } - _ => { - let name = if base::wants_msvc_seh(self.sess()) { - "__CxxFrameHandler3" - } else { - "rust_eh_personality" - }; - let fty = Type::variadic_func(&[], &Type::i32(self)); - declare::declare_cfn(self, name, fty) - } - }; - self.eh_personality.set(Some(llfn)); - llfn - } - - // Returns a ValueRef of the "eh_unwind_resume" lang item if one is defined, - // otherwise declares it as an external function. - pub fn eh_unwind_resume(&self) -> ValueRef { - use attributes; - let unwresume = &self.eh_unwind_resume; - if let Some(llfn) = unwresume.get() { - return llfn; - } - - let tcx = self.tcx; - assert!(self.sess().target.target.options.custom_unwind_resume); - if let Some(def_id) = tcx.lang_items().eh_unwind_resume() { - let llfn = callee::resolve_and_get_fn(self, def_id, tcx.intern_substs(&[])); - unwresume.set(Some(llfn)); - return llfn; - } - - let ty = tcx.mk_fn_ptr(ty::Binder::bind(tcx.mk_fn_sig( - iter::once(tcx.mk_mut_ptr(tcx.types.u8)), - tcx.types.never, - false, - hir::Unsafety::Unsafe, - Abi::C - ))); - - let llfn = declare::declare_fn(self, "rust_eh_unwind_resume", ty); - attributes::unwind(llfn, true); - unwresume.set(Some(llfn)); - llfn - } - - pub fn type_needs_drop(&self, ty: Ty<'tcx>) -> bool { - common::type_needs_drop(self.tcx, ty) - } - - pub fn type_is_sized(&self, ty: Ty<'tcx>) -> bool { - common::type_is_sized(self.tcx, ty) - } - - pub fn type_is_freeze(&self, ty: Ty<'tcx>) -> bool { - common::type_is_freeze(self.tcx, ty) - } - - pub fn type_has_metadata(&self, ty: Ty<'tcx>) -> bool { - use syntax_pos::DUMMY_SP; - if ty.is_sized(self.tcx.at(DUMMY_SP), ty::ParamEnv::reveal_all()) { - return false; - } - - let tail = self.tcx.struct_tail(ty); - match tail.sty { - ty::TyForeign(..) => false, - ty::TyStr | ty::TySlice(..) | ty::TyDynamic(..) => true, - _ => bug!("unexpected unsized tail: {:?}", tail.sty), - } - } -} - -impl<'a, 'tcx> ty::layout::HasDataLayout for &'a CodegenCx<'a, 'tcx> { - fn data_layout(&self) -> &ty::layout::TargetDataLayout { - &self.tcx.data_layout - } -} - -impl<'a, 'tcx> HasTargetSpec for &'a CodegenCx<'a, 'tcx> { - fn target_spec(&self) -> &Target { - &self.tcx.sess.target.target - } -} - -impl<'a, 'tcx> ty::layout::HasTyCtxt<'tcx> for &'a CodegenCx<'a, 'tcx> { - fn tcx<'b>(&'b self) -> TyCtxt<'b, 'tcx, 'tcx> { - self.tcx - } -} - -impl<'a, 'tcx> LayoutOf for &'a CodegenCx<'a, 'tcx> { - type Ty = Ty<'tcx>; - type TyLayout = TyLayout<'tcx>; - - fn layout_of(self, ty: Ty<'tcx>) -> Self::TyLayout { - self.tcx.layout_of(ty::ParamEnv::reveal_all().and(ty)) - .unwrap_or_else(|e| match e { - LayoutError::SizeOverflow(_) => self.sess().fatal(&e.to_string()), - _ => bug!("failed to get layout for `{}`: {}", ty, e) - }) - } -} - -/// Declare any llvm intrinsics that you might need -fn declare_intrinsic(cx: &CodegenCx, key: &str) -> Option { - macro_rules! ifn { - ($name:expr, fn() -> $ret:expr) => ( - if key == $name { - let f = declare::declare_cfn(cx, $name, Type::func(&[], &$ret)); - llvm::SetUnnamedAddr(f, false); - cx.intrinsics.borrow_mut().insert($name, f.clone()); - return Some(f); - } - ); - ($name:expr, fn(...) -> $ret:expr) => ( - if key == $name { - let f = declare::declare_cfn(cx, $name, Type::variadic_func(&[], &$ret)); - llvm::SetUnnamedAddr(f, false); - cx.intrinsics.borrow_mut().insert($name, f.clone()); - return Some(f); - } - ); - ($name:expr, fn($($arg:expr),*) -> $ret:expr) => ( - if key == $name { - let f = declare::declare_cfn(cx, $name, Type::func(&[$($arg),*], &$ret)); - llvm::SetUnnamedAddr(f, false); - cx.intrinsics.borrow_mut().insert($name, f.clone()); - return Some(f); - } - ); - } - macro_rules! mk_struct { - ($($field_ty:expr),*) => (Type::struct_(cx, &[$($field_ty),*], false)) - } - - let i8p = Type::i8p(cx); - let void = Type::void(cx); - let i1 = Type::i1(cx); - let t_i8 = Type::i8(cx); - let t_i16 = Type::i16(cx); - let t_i32 = Type::i32(cx); - let t_i64 = Type::i64(cx); - let t_i128 = Type::i128(cx); - let t_f32 = Type::f32(cx); - let t_f64 = Type::f64(cx); - - ifn!("llvm.memcpy.p0i8.p0i8.i16", fn(i8p, i8p, t_i16, t_i32, i1) -> void); - ifn!("llvm.memcpy.p0i8.p0i8.i32", fn(i8p, i8p, t_i32, t_i32, i1) -> void); - ifn!("llvm.memcpy.p0i8.p0i8.i64", fn(i8p, i8p, t_i64, t_i32, i1) -> void); - ifn!("llvm.memmove.p0i8.p0i8.i16", fn(i8p, i8p, t_i16, t_i32, i1) -> void); - ifn!("llvm.memmove.p0i8.p0i8.i32", fn(i8p, i8p, t_i32, t_i32, i1) -> void); - ifn!("llvm.memmove.p0i8.p0i8.i64", fn(i8p, i8p, t_i64, t_i32, i1) -> void); - ifn!("llvm.memset.p0i8.i16", fn(i8p, t_i8, t_i16, t_i32, i1) -> void); - ifn!("llvm.memset.p0i8.i32", fn(i8p, t_i8, t_i32, t_i32, i1) -> void); - ifn!("llvm.memset.p0i8.i64", fn(i8p, t_i8, t_i64, t_i32, i1) -> void); - - ifn!("llvm.trap", fn() -> void); - ifn!("llvm.debugtrap", fn() -> void); - ifn!("llvm.frameaddress", fn(t_i32) -> i8p); - - ifn!("llvm.powi.f32", fn(t_f32, t_i32) -> t_f32); - ifn!("llvm.powi.f64", fn(t_f64, t_i32) -> t_f64); - ifn!("llvm.pow.f32", fn(t_f32, t_f32) -> t_f32); - ifn!("llvm.pow.f64", fn(t_f64, t_f64) -> t_f64); - - ifn!("llvm.sqrt.f32", fn(t_f32) -> t_f32); - ifn!("llvm.sqrt.f64", fn(t_f64) -> t_f64); - ifn!("llvm.sin.f32", fn(t_f32) -> t_f32); - ifn!("llvm.sin.f64", fn(t_f64) -> t_f64); - ifn!("llvm.cos.f32", fn(t_f32) -> t_f32); - ifn!("llvm.cos.f64", fn(t_f64) -> t_f64); - ifn!("llvm.exp.f32", fn(t_f32) -> t_f32); - ifn!("llvm.exp.f64", fn(t_f64) -> t_f64); - ifn!("llvm.exp2.f32", fn(t_f32) -> t_f32); - ifn!("llvm.exp2.f64", fn(t_f64) -> t_f64); - ifn!("llvm.log.f32", fn(t_f32) -> t_f32); - ifn!("llvm.log.f64", fn(t_f64) -> t_f64); - ifn!("llvm.log10.f32", fn(t_f32) -> t_f32); - ifn!("llvm.log10.f64", fn(t_f64) -> t_f64); - ifn!("llvm.log2.f32", fn(t_f32) -> t_f32); - ifn!("llvm.log2.f64", fn(t_f64) -> t_f64); - - ifn!("llvm.fma.f32", fn(t_f32, t_f32, t_f32) -> t_f32); - ifn!("llvm.fma.f64", fn(t_f64, t_f64, t_f64) -> t_f64); - - ifn!("llvm.fabs.f32", fn(t_f32) -> t_f32); - ifn!("llvm.fabs.f64", fn(t_f64) -> t_f64); - - ifn!("llvm.floor.f32", fn(t_f32) -> t_f32); - ifn!("llvm.floor.f64", fn(t_f64) -> t_f64); - ifn!("llvm.ceil.f32", fn(t_f32) -> t_f32); - ifn!("llvm.ceil.f64", fn(t_f64) -> t_f64); - ifn!("llvm.trunc.f32", fn(t_f32) -> t_f32); - ifn!("llvm.trunc.f64", fn(t_f64) -> t_f64); - - ifn!("llvm.copysign.f32", fn(t_f32, t_f32) -> t_f32); - ifn!("llvm.copysign.f64", fn(t_f64, t_f64) -> t_f64); - ifn!("llvm.round.f32", fn(t_f32) -> t_f32); - ifn!("llvm.round.f64", fn(t_f64) -> t_f64); - - ifn!("llvm.rint.f32", fn(t_f32) -> t_f32); - ifn!("llvm.rint.f64", fn(t_f64) -> t_f64); - ifn!("llvm.nearbyint.f32", fn(t_f32) -> t_f32); - ifn!("llvm.nearbyint.f64", fn(t_f64) -> t_f64); - - ifn!("llvm.ctpop.i8", fn(t_i8) -> t_i8); - ifn!("llvm.ctpop.i16", fn(t_i16) -> t_i16); - ifn!("llvm.ctpop.i32", fn(t_i32) -> t_i32); - ifn!("llvm.ctpop.i64", fn(t_i64) -> t_i64); - ifn!("llvm.ctpop.i128", fn(t_i128) -> t_i128); - - ifn!("llvm.ctlz.i8", fn(t_i8 , i1) -> t_i8); - ifn!("llvm.ctlz.i16", fn(t_i16, i1) -> t_i16); - ifn!("llvm.ctlz.i32", fn(t_i32, i1) -> t_i32); - ifn!("llvm.ctlz.i64", fn(t_i64, i1) -> t_i64); - ifn!("llvm.ctlz.i128", fn(t_i128, i1) -> t_i128); - - ifn!("llvm.cttz.i8", fn(t_i8 , i1) -> t_i8); - ifn!("llvm.cttz.i16", fn(t_i16, i1) -> t_i16); - ifn!("llvm.cttz.i32", fn(t_i32, i1) -> t_i32); - ifn!("llvm.cttz.i64", fn(t_i64, i1) -> t_i64); - ifn!("llvm.cttz.i128", fn(t_i128, i1) -> t_i128); - - ifn!("llvm.bswap.i16", fn(t_i16) -> t_i16); - ifn!("llvm.bswap.i32", fn(t_i32) -> t_i32); - ifn!("llvm.bswap.i64", fn(t_i64) -> t_i64); - ifn!("llvm.bswap.i128", fn(t_i128) -> t_i128); - - ifn!("llvm.bitreverse.i8", fn(t_i8) -> t_i8); - ifn!("llvm.bitreverse.i16", fn(t_i16) -> t_i16); - ifn!("llvm.bitreverse.i32", fn(t_i32) -> t_i32); - ifn!("llvm.bitreverse.i64", fn(t_i64) -> t_i64); - ifn!("llvm.bitreverse.i128", fn(t_i128) -> t_i128); - - ifn!("llvm.sadd.with.overflow.i8", fn(t_i8, t_i8) -> mk_struct!{t_i8, i1}); - ifn!("llvm.sadd.with.overflow.i16", fn(t_i16, t_i16) -> mk_struct!{t_i16, i1}); - ifn!("llvm.sadd.with.overflow.i32", fn(t_i32, t_i32) -> mk_struct!{t_i32, i1}); - ifn!("llvm.sadd.with.overflow.i64", fn(t_i64, t_i64) -> mk_struct!{t_i64, i1}); - ifn!("llvm.sadd.with.overflow.i128", fn(t_i128, t_i128) -> mk_struct!{t_i128, i1}); - - ifn!("llvm.uadd.with.overflow.i8", fn(t_i8, t_i8) -> mk_struct!{t_i8, i1}); - ifn!("llvm.uadd.with.overflow.i16", fn(t_i16, t_i16) -> mk_struct!{t_i16, i1}); - ifn!("llvm.uadd.with.overflow.i32", fn(t_i32, t_i32) -> mk_struct!{t_i32, i1}); - ifn!("llvm.uadd.with.overflow.i64", fn(t_i64, t_i64) -> mk_struct!{t_i64, i1}); - ifn!("llvm.uadd.with.overflow.i128", fn(t_i128, t_i128) -> mk_struct!{t_i128, i1}); - - ifn!("llvm.ssub.with.overflow.i8", fn(t_i8, t_i8) -> mk_struct!{t_i8, i1}); - ifn!("llvm.ssub.with.overflow.i16", fn(t_i16, t_i16) -> mk_struct!{t_i16, i1}); - ifn!("llvm.ssub.with.overflow.i32", fn(t_i32, t_i32) -> mk_struct!{t_i32, i1}); - ifn!("llvm.ssub.with.overflow.i64", fn(t_i64, t_i64) -> mk_struct!{t_i64, i1}); - ifn!("llvm.ssub.with.overflow.i128", fn(t_i128, t_i128) -> mk_struct!{t_i128, i1}); - - ifn!("llvm.usub.with.overflow.i8", fn(t_i8, t_i8) -> mk_struct!{t_i8, i1}); - ifn!("llvm.usub.with.overflow.i16", fn(t_i16, t_i16) -> mk_struct!{t_i16, i1}); - ifn!("llvm.usub.with.overflow.i32", fn(t_i32, t_i32) -> mk_struct!{t_i32, i1}); - ifn!("llvm.usub.with.overflow.i64", fn(t_i64, t_i64) -> mk_struct!{t_i64, i1}); - ifn!("llvm.usub.with.overflow.i128", fn(t_i128, t_i128) -> mk_struct!{t_i128, i1}); - - ifn!("llvm.smul.with.overflow.i8", fn(t_i8, t_i8) -> mk_struct!{t_i8, i1}); - ifn!("llvm.smul.with.overflow.i16", fn(t_i16, t_i16) -> mk_struct!{t_i16, i1}); - ifn!("llvm.smul.with.overflow.i32", fn(t_i32, t_i32) -> mk_struct!{t_i32, i1}); - ifn!("llvm.smul.with.overflow.i64", fn(t_i64, t_i64) -> mk_struct!{t_i64, i1}); - ifn!("llvm.smul.with.overflow.i128", fn(t_i128, t_i128) -> mk_struct!{t_i128, i1}); - - ifn!("llvm.umul.with.overflow.i8", fn(t_i8, t_i8) -> mk_struct!{t_i8, i1}); - ifn!("llvm.umul.with.overflow.i16", fn(t_i16, t_i16) -> mk_struct!{t_i16, i1}); - ifn!("llvm.umul.with.overflow.i32", fn(t_i32, t_i32) -> mk_struct!{t_i32, i1}); - ifn!("llvm.umul.with.overflow.i64", fn(t_i64, t_i64) -> mk_struct!{t_i64, i1}); - ifn!("llvm.umul.with.overflow.i128", fn(t_i128, t_i128) -> mk_struct!{t_i128, i1}); - - ifn!("llvm.lifetime.start", fn(t_i64,i8p) -> void); - ifn!("llvm.lifetime.end", fn(t_i64, i8p) -> void); - - ifn!("llvm.expect.i1", fn(i1, i1) -> i1); - ifn!("llvm.eh.typeid.for", fn(i8p) -> t_i32); - ifn!("llvm.localescape", fn(...) -> void); - ifn!("llvm.localrecover", fn(i8p, i8p, t_i32) -> i8p); - ifn!("llvm.x86.seh.recoverfp", fn(i8p, i8p) -> i8p); - - ifn!("llvm.assume", fn(i1) -> void); - ifn!("llvm.prefetch", fn(i8p, t_i32, t_i32, t_i32) -> void); - - if cx.sess().opts.debuginfo != NoDebugInfo { - ifn!("llvm.dbg.declare", fn(Type::metadata(cx), Type::metadata(cx)) -> void); - ifn!("llvm.dbg.value", fn(Type::metadata(cx), t_i64, Type::metadata(cx)) -> void); - } - return None; -} diff --git a/src/librustc_trans/debuginfo/create_scope_map.rs b/src/librustc_trans/debuginfo/create_scope_map.rs deleted file mode 100644 index bddb3d90940..00000000000 --- a/src/librustc_trans/debuginfo/create_scope_map.rs +++ /dev/null @@ -1,136 +0,0 @@ -// Copyright 2015 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -use super::{FunctionDebugContext, FunctionDebugContextData}; -use super::metadata::file_metadata; -use super::utils::{DIB, span_start}; - -use llvm; -use llvm::debuginfo::DIScope; -use common::CodegenCx; -use rustc::mir::{Mir, VisibilityScope}; - -use libc::c_uint; -use std::ptr; - -use syntax_pos::Pos; - -use rustc_data_structures::bitvec::BitVector; -use rustc_data_structures::indexed_vec::{Idx, IndexVec}; - -use syntax_pos::BytePos; - -#[derive(Clone, Copy, Debug)] -pub struct MirDebugScope { - pub scope_metadata: DIScope, - // Start and end offsets of the file to which this DIScope belongs. - // These are used to quickly determine whether some span refers to the same file. - pub file_start_pos: BytePos, - pub file_end_pos: BytePos, -} - -impl MirDebugScope { - pub fn is_valid(&self) -> bool { - !self.scope_metadata.is_null() - } -} - -/// Produce DIScope DIEs for each MIR Scope which has variables defined in it. -/// If debuginfo is disabled, the returned vector is empty. -pub fn create_mir_scopes(cx: &CodegenCx, mir: &Mir, debug_context: &FunctionDebugContext) - -> IndexVec { - let null_scope = MirDebugScope { - scope_metadata: ptr::null_mut(), - file_start_pos: BytePos(0), - file_end_pos: BytePos(0) - }; - let mut scopes = IndexVec::from_elem(null_scope, &mir.visibility_scopes); - - let debug_context = match *debug_context { - FunctionDebugContext::RegularContext(ref data) => data, - FunctionDebugContext::DebugInfoDisabled | - FunctionDebugContext::FunctionWithoutDebugInfo => { - return scopes; - } - }; - - // Find all the scopes with variables defined in them. - let mut has_variables = BitVector::new(mir.visibility_scopes.len()); - for var in mir.vars_iter() { - let decl = &mir.local_decls[var]; - has_variables.insert(decl.source_info.scope.index()); - } - - // Instantiate all scopes. - for idx in 0..mir.visibility_scopes.len() { - let scope = VisibilityScope::new(idx); - make_mir_scope(cx, &mir, &has_variables, debug_context, scope, &mut scopes); - } - - scopes -} - -fn make_mir_scope(cx: &CodegenCx, - mir: &Mir, - has_variables: &BitVector, - debug_context: &FunctionDebugContextData, - scope: VisibilityScope, - scopes: &mut IndexVec) { - if scopes[scope].is_valid() { - return; - } - - let scope_data = &mir.visibility_scopes[scope]; - let parent_scope = if let Some(parent) = scope_data.parent_scope { - make_mir_scope(cx, mir, has_variables, debug_context, parent, scopes); - scopes[parent] - } else { - // The root is the function itself. - let loc = span_start(cx, mir.span); - scopes[scope] = MirDebugScope { - scope_metadata: debug_context.fn_metadata, - file_start_pos: loc.file.start_pos, - file_end_pos: loc.file.end_pos, - }; - return; - }; - - if !has_variables.contains(scope.index()) { - // Do not create a DIScope if there are no variables - // defined in this MIR Scope, to avoid debuginfo bloat. - - // However, we don't skip creating a nested scope if - // our parent is the root, because we might want to - // put arguments in the root and not have shadowing. - if parent_scope.scope_metadata != debug_context.fn_metadata { - scopes[scope] = parent_scope; - return; - } - } - - let loc = span_start(cx, scope_data.span); - let file_metadata = file_metadata(cx, - &loc.file.name, - debug_context.defining_crate); - - let scope_metadata = unsafe { - llvm::LLVMRustDIBuilderCreateLexicalBlock( - DIB(cx), - parent_scope.scope_metadata, - file_metadata, - loc.line as c_uint, - loc.col.to_usize() as c_uint) - }; - scopes[scope] = MirDebugScope { - scope_metadata, - file_start_pos: loc.file.start_pos, - file_end_pos: loc.file.end_pos, - }; -} diff --git a/src/librustc_trans/debuginfo/doc.rs b/src/librustc_trans/debuginfo/doc.rs deleted file mode 100644 index cbecc0eb7d1..00000000000 --- a/src/librustc_trans/debuginfo/doc.rs +++ /dev/null @@ -1,189 +0,0 @@ -// Copyright 2015 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! # Debug Info Module -//! -//! This module serves the purpose of generating debug symbols. We use LLVM's -//! [source level debugging](http://!llvm.org/docs/SourceLevelDebugging.html) -//! features for generating the debug information. The general principle is -//! this: -//! -//! Given the right metadata in the LLVM IR, the LLVM code generator is able to -//! create DWARF debug symbols for the given code. The -//! [metadata](http://!llvm.org/docs/LangRef.html#metadata-type) is structured -//! much like DWARF *debugging information entries* (DIE), representing type -//! information such as datatype layout, function signatures, block layout, -//! variable location and scope information, etc. It is the purpose of this -//! module to generate correct metadata and insert it into the LLVM IR. -//! -//! As the exact format of metadata trees may change between different LLVM -//! versions, we now use LLVM -//! [DIBuilder](http://!llvm.org/docs/doxygen/html/classllvm_1_1DIBuilder.html) -//! to create metadata where possible. This will hopefully ease the adaption of -//! this module to future LLVM versions. -//! -//! The public API of the module is a set of functions that will insert the -//! correct metadata into the LLVM IR when called with the right parameters. -//! The module is thus driven from an outside client with functions like -//! `debuginfo::create_local_var_metadata(bx: block, local: &ast::local)`. -//! -//! Internally the module will try to reuse already created metadata by -//! utilizing a cache. The way to get a shared metadata node when needed is -//! thus to just call the corresponding function in this module: -//! -//! let file_metadata = file_metadata(crate_context, path); -//! -//! The function will take care of probing the cache for an existing node for -//! that exact file path. -//! -//! All private state used by the module is stored within either the -//! CrateDebugContext struct (owned by the CodegenCx) or the -//! FunctionDebugContext (owned by the FunctionCx). -//! -//! This file consists of three conceptual sections: -//! 1. The public interface of the module -//! 2. Module-internal metadata creation functions -//! 3. Minor utility functions -//! -//! -//! ## Recursive Types -//! -//! Some kinds of types, such as structs and enums can be recursive. That means -//! that the type definition of some type X refers to some other type which in -//! turn (transitively) refers to X. This introduces cycles into the type -//! referral graph. A naive algorithm doing an on-demand, depth-first traversal -//! of this graph when describing types, can get trapped in an endless loop -//! when it reaches such a cycle. -//! -//! For example, the following simple type for a singly-linked list... -//! -//! ``` -//! struct List { -//! value: i32, -//! tail: Option>, -//! } -//! ``` -//! -//! will generate the following callstack with a naive DFS algorithm: -//! -//! ``` -//! describe(t = List) -//! describe(t = i32) -//! describe(t = Option>) -//! describe(t = Box) -//! describe(t = List) // at the beginning again... -//! ... -//! ``` -//! -//! To break cycles like these, we use "forward declarations". That is, when -//! the algorithm encounters a possibly recursive type (any struct or enum), it -//! immediately creates a type description node and inserts it into the cache -//! *before* describing the members of the type. This type description is just -//! a stub (as type members are not described and added to it yet) but it -//! allows the algorithm to already refer to the type. After the stub is -//! inserted into the cache, the algorithm continues as before. If it now -//! encounters a recursive reference, it will hit the cache and does not try to -//! describe the type anew. -//! -//! This behavior is encapsulated in the 'RecursiveTypeDescription' enum, -//! which represents a kind of continuation, storing all state needed to -//! continue traversal at the type members after the type has been registered -//! with the cache. (This implementation approach might be a tad over- -//! engineered and may change in the future) -//! -//! -//! ## Source Locations and Line Information -//! -//! In addition to data type descriptions the debugging information must also -//! allow to map machine code locations back to source code locations in order -//! to be useful. This functionality is also handled in this module. The -//! following functions allow to control source mappings: -//! -//! + set_source_location() -//! + clear_source_location() -//! + start_emitting_source_locations() -//! -//! `set_source_location()` allows to set the current source location. All IR -//! instructions created after a call to this function will be linked to the -//! given source location, until another location is specified with -//! `set_source_location()` or the source location is cleared with -//! `clear_source_location()`. In the later case, subsequent IR instruction -//! will not be linked to any source location. As you can see, this is a -//! stateful API (mimicking the one in LLVM), so be careful with source -//! locations set by previous calls. It's probably best to not rely on any -//! specific state being present at a given point in code. -//! -//! One topic that deserves some extra attention is *function prologues*. At -//! the beginning of a function's machine code there are typically a few -//! instructions for loading argument values into allocas and checking if -//! there's enough stack space for the function to execute. This *prologue* is -//! not visible in the source code and LLVM puts a special PROLOGUE END marker -//! into the line table at the first non-prologue instruction of the function. -//! In order to find out where the prologue ends, LLVM looks for the first -//! instruction in the function body that is linked to a source location. So, -//! when generating prologue instructions we have to make sure that we don't -//! emit source location information until the 'real' function body begins. For -//! this reason, source location emission is disabled by default for any new -//! function being translated and is only activated after a call to the third -//! function from the list above, `start_emitting_source_locations()`. This -//! function should be called right before regularly starting to translate the -//! top-level block of the given function. -//! -//! There is one exception to the above rule: `llvm.dbg.declare` instruction -//! must be linked to the source location of the variable being declared. For -//! function parameters these `llvm.dbg.declare` instructions typically occur -//! in the middle of the prologue, however, they are ignored by LLVM's prologue -//! detection. The `create_argument_metadata()` and related functions take care -//! of linking the `llvm.dbg.declare` instructions to the correct source -//! locations even while source location emission is still disabled, so there -//! is no need to do anything special with source location handling here. -//! -//! ## Unique Type Identification -//! -//! In order for link-time optimization to work properly, LLVM needs a unique -//! type identifier that tells it across compilation units which types are the -//! same as others. This type identifier is created by -//! TypeMap::get_unique_type_id_of_type() using the following algorithm: -//! -//! (1) Primitive types have their name as ID -//! (2) Structs, enums and traits have a multipart identifier -//! -//! (1) The first part is the SVH (strict version hash) of the crate they -//! were originally defined in -//! -//! (2) The second part is the ast::NodeId of the definition in their -//! original crate -//! -//! (3) The final part is a concatenation of the type IDs of their concrete -//! type arguments if they are generic types. -//! -//! (3) Tuple-, pointer and function types are structurally identified, which -//! means that they are equivalent if their component types are equivalent -//! (i.e. (i32, i32) is the same regardless in which crate it is used). -//! -//! This algorithm also provides a stable ID for types that are defined in one -//! crate but instantiated from metadata within another crate. We just have to -//! take care to always map crate and node IDs back to the original crate -//! context. -//! -//! As a side-effect these unique type IDs also help to solve a problem arising -//! from lifetime parameters. Since lifetime parameters are completely omitted -//! in debuginfo, more than one `Ty` instance may map to the same debuginfo -//! type metadata, that is, some struct `Struct<'a>` may have N instantiations -//! with different concrete substitutions for `'a`, and thus there will be N -//! `Ty` instances for the type `Struct<'a>` even though it is not generic -//! otherwise. Unfortunately this means that we cannot use `ty::type_id()` as -//! cheap identifier for type metadata---we have done this in the past, but it -//! led to unnecessary metadata duplication in the best case and LLVM -//! assertions in the worst. However, the unique type ID as described above -//! *can* be used as identifier. Since it is comparatively expensive to -//! construct, though, `ty::type_id()` is still used additionally as an -//! optimization for cases where the exact same type has been seen before -//! (which is most of the time). diff --git a/src/librustc_trans/debuginfo/gdb.rs b/src/librustc_trans/debuginfo/gdb.rs deleted file mode 100644 index 0b4858c7ab0..00000000000 --- a/src/librustc_trans/debuginfo/gdb.rs +++ /dev/null @@ -1,88 +0,0 @@ -// Copyright 2015 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -// .debug_gdb_scripts binary section. - -use llvm; - -use common::{C_bytes, CodegenCx, C_i32}; -use builder::Builder; -use declare; -use type_::Type; -use rustc::session::config::NoDebugInfo; - -use std::ptr; -use syntax::attr; - - -/// Inserts a side-effect free instruction sequence that makes sure that the -/// .debug_gdb_scripts global is referenced, so it isn't removed by the linker. -pub fn insert_reference_to_gdb_debug_scripts_section_global(bx: &Builder) { - if needs_gdb_debug_scripts_section(bx.cx) { - let gdb_debug_scripts_section = get_or_insert_gdb_debug_scripts_section_global(bx.cx); - // Load just the first byte as that's all that's necessary to force - // LLVM to keep around the reference to the global. - let indices = [C_i32(bx.cx, 0), C_i32(bx.cx, 0)]; - let element = bx.inbounds_gep(gdb_debug_scripts_section, &indices); - let volative_load_instruction = bx.volatile_load(element); - unsafe { - llvm::LLVMSetAlignment(volative_load_instruction, 1); - } - } -} - -/// Allocates the global variable responsible for the .debug_gdb_scripts binary -/// section. -pub fn get_or_insert_gdb_debug_scripts_section_global(cx: &CodegenCx) - -> llvm::ValueRef { - let c_section_var_name = "__rustc_debug_gdb_scripts_section__\0"; - let section_var_name = &c_section_var_name[..c_section_var_name.len()-1]; - - let section_var = unsafe { - llvm::LLVMGetNamedGlobal(cx.llmod, - c_section_var_name.as_ptr() as *const _) - }; - - if section_var == ptr::null_mut() { - let section_name = b".debug_gdb_scripts\0"; - let section_contents = b"\x01gdb_load_rust_pretty_printers.py\0"; - - unsafe { - let llvm_type = Type::array(&Type::i8(cx), - section_contents.len() as u64); - - let section_var = declare::define_global(cx, section_var_name, - llvm_type).unwrap_or_else(||{ - bug!("symbol `{}` is already defined", section_var_name) - }); - llvm::LLVMSetSection(section_var, section_name.as_ptr() as *const _); - llvm::LLVMSetInitializer(section_var, C_bytes(cx, section_contents)); - llvm::LLVMSetGlobalConstant(section_var, llvm::True); - llvm::LLVMSetUnnamedAddr(section_var, llvm::True); - llvm::LLVMRustSetLinkage(section_var, llvm::Linkage::LinkOnceODRLinkage); - // This should make sure that the whole section is not larger than - // the string it contains. Otherwise we get a warning from GDB. - llvm::LLVMSetAlignment(section_var, 1); - section_var - } - } else { - section_var - } -} - -pub fn needs_gdb_debug_scripts_section(cx: &CodegenCx) -> bool { - let omit_gdb_pretty_printer_section = - attr::contains_name(&cx.tcx.hir.krate_attrs(), - "omit_gdb_pretty_printer_section"); - - !omit_gdb_pretty_printer_section && - cx.sess().opts.debuginfo != NoDebugInfo && - cx.sess().target.target.options.emit_debug_gdb_scripts -} diff --git a/src/librustc_trans/debuginfo/metadata.rs b/src/librustc_trans/debuginfo/metadata.rs deleted file mode 100644 index 4e77c0df65e..00000000000 --- a/src/librustc_trans/debuginfo/metadata.rs +++ /dev/null @@ -1,1773 +0,0 @@ -// Copyright 2015 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -use self::RecursiveTypeDescription::*; -use self::MemberDescriptionFactory::*; -use self::EnumDiscriminantInfo::*; - -use super::utils::{debug_context, DIB, span_start, - get_namespace_for_item, create_DIArray, is_node_local_to_unit}; -use super::namespace::mangled_name_of_instance; -use super::type_names::compute_debuginfo_type_name; -use super::{CrateDebugContext}; -use abi; - -use llvm::{self, ValueRef}; -use llvm::debuginfo::{DIType, DIFile, DIScope, DIDescriptor, - DICompositeType, DILexicalBlock, DIFlags}; - -use rustc::hir::TransFnAttrFlags; -use rustc::hir::def::CtorKind; -use rustc::hir::def_id::{DefId, CrateNum, LOCAL_CRATE}; -use rustc::ty::fold::TypeVisitor; -use rustc::ty::util::TypeIdHasher; -use rustc::ich::Fingerprint; -use rustc::ty::Instance; -use common::CodegenCx; -use rustc::ty::{self, AdtKind, ParamEnv, Ty, TyCtxt}; -use rustc::ty::layout::{self, Align, LayoutOf, PrimitiveExt, Size, TyLayout}; -use rustc::session::config; -use rustc::util::nodemap::FxHashMap; -use rustc::util::common::path2cstr; - -use libc::{c_uint, c_longlong}; -use std::ffi::CString; -use std::fmt::Write; -use std::ptr; -use std::path::{Path, PathBuf}; -use syntax::ast; -use syntax::symbol::{Interner, InternedString, Symbol}; -use syntax_pos::{self, Span, FileName}; - - -// From DWARF 5. -// See http://www.dwarfstd.org/ShowIssue.php?issue=140129.1 -const DW_LANG_RUST: c_uint = 0x1c; -#[allow(non_upper_case_globals)] -const DW_ATE_boolean: c_uint = 0x02; -#[allow(non_upper_case_globals)] -const DW_ATE_float: c_uint = 0x04; -#[allow(non_upper_case_globals)] -const DW_ATE_signed: c_uint = 0x05; -#[allow(non_upper_case_globals)] -const DW_ATE_unsigned: c_uint = 0x07; -#[allow(non_upper_case_globals)] -const DW_ATE_unsigned_char: c_uint = 0x08; - -pub const UNKNOWN_LINE_NUMBER: c_uint = 0; -pub const UNKNOWN_COLUMN_NUMBER: c_uint = 0; - -// ptr::null() doesn't work :( -pub const NO_SCOPE_METADATA: DIScope = (0 as DIScope); - -#[derive(Copy, Debug, Hash, Eq, PartialEq, Clone)] -pub struct UniqueTypeId(ast::Name); - -// The TypeMap is where the CrateDebugContext holds the type metadata nodes -// created so far. The metadata nodes are indexed by UniqueTypeId, and, for -// faster lookup, also by Ty. The TypeMap is responsible for creating -// UniqueTypeIds. -pub struct TypeMap<'tcx> { - // The UniqueTypeIds created so far - unique_id_interner: Interner, - // A map from UniqueTypeId to debuginfo metadata for that type. This is a 1:1 mapping. - unique_id_to_metadata: FxHashMap, - // A map from types to debuginfo metadata. This is a N:1 mapping. - type_to_metadata: FxHashMap, DIType>, - // A map from types to UniqueTypeId. This is a N:1 mapping. - type_to_unique_id: FxHashMap, UniqueTypeId> -} - -impl<'tcx> TypeMap<'tcx> { - pub fn new() -> TypeMap<'tcx> { - TypeMap { - unique_id_interner: Interner::new(), - type_to_metadata: FxHashMap(), - unique_id_to_metadata: FxHashMap(), - type_to_unique_id: FxHashMap(), - } - } - - // Adds a Ty to metadata mapping to the TypeMap. The method will fail if - // the mapping already exists. - fn register_type_with_metadata<'a>(&mut self, - type_: Ty<'tcx>, - metadata: DIType) { - if self.type_to_metadata.insert(type_, metadata).is_some() { - bug!("Type metadata for Ty '{}' is already in the TypeMap!", type_); - } - } - - // Adds a UniqueTypeId to metadata mapping to the TypeMap. The method will - // fail if the mapping already exists. - fn register_unique_id_with_metadata(&mut self, - unique_type_id: UniqueTypeId, - metadata: DIType) { - if self.unique_id_to_metadata.insert(unique_type_id, metadata).is_some() { - bug!("Type metadata for unique id '{}' is already in the TypeMap!", - self.get_unique_type_id_as_string(unique_type_id)); - } - } - - fn find_metadata_for_type(&self, type_: Ty<'tcx>) -> Option { - self.type_to_metadata.get(&type_).cloned() - } - - fn find_metadata_for_unique_id(&self, unique_type_id: UniqueTypeId) -> Option { - self.unique_id_to_metadata.get(&unique_type_id).cloned() - } - - // Get the string representation of a UniqueTypeId. This method will fail if - // the id is unknown. - fn get_unique_type_id_as_string(&self, unique_type_id: UniqueTypeId) -> &str { - let UniqueTypeId(interner_key) = unique_type_id; - self.unique_id_interner.get(interner_key) - } - - // Get the UniqueTypeId for the given type. If the UniqueTypeId for the given - // type has been requested before, this is just a table lookup. Otherwise an - // ID will be generated and stored for later lookup. - fn get_unique_type_id_of_type<'a>(&mut self, cx: &CodegenCx<'a, 'tcx>, - type_: Ty<'tcx>) -> UniqueTypeId { - // Let's see if we already have something in the cache - match self.type_to_unique_id.get(&type_).cloned() { - Some(unique_type_id) => return unique_type_id, - None => { /* generate one */} - }; - - // The hasher we are using to generate the UniqueTypeId. We want - // something that provides more than the 64 bits of the DefaultHasher. - let mut type_id_hasher = TypeIdHasher::::new(cx.tcx); - type_id_hasher.visit_ty(type_); - let unique_type_id = type_id_hasher.finish().to_hex(); - - let key = self.unique_id_interner.intern(&unique_type_id); - self.type_to_unique_id.insert(type_, UniqueTypeId(key)); - - return UniqueTypeId(key); - } - - // Get the UniqueTypeId for an enum variant. Enum variants are not really - // types of their own, so they need special handling. We still need a - // UniqueTypeId for them, since to debuginfo they *are* real types. - fn get_unique_type_id_of_enum_variant<'a>(&mut self, - cx: &CodegenCx<'a, 'tcx>, - enum_type: Ty<'tcx>, - variant_name: &str) - -> UniqueTypeId { - let enum_type_id = self.get_unique_type_id_of_type(cx, enum_type); - let enum_variant_type_id = format!("{}::{}", - self.get_unique_type_id_as_string(enum_type_id), - variant_name); - let interner_key = self.unique_id_interner.intern(&enum_variant_type_id); - UniqueTypeId(interner_key) - } -} - -// A description of some recursive type. It can either be already finished (as -// with FinalMetadata) or it is not yet finished, but contains all information -// needed to generate the missing parts of the description. See the -// documentation section on Recursive Types at the top of this file for more -// information. -enum RecursiveTypeDescription<'tcx> { - UnfinishedMetadata { - unfinished_type: Ty<'tcx>, - unique_type_id: UniqueTypeId, - metadata_stub: DICompositeType, - member_description_factory: MemberDescriptionFactory<'tcx>, - }, - FinalMetadata(DICompositeType) -} - -fn create_and_register_recursive_type_forward_declaration<'a, 'tcx>( - cx: &CodegenCx<'a, 'tcx>, - unfinished_type: Ty<'tcx>, - unique_type_id: UniqueTypeId, - metadata_stub: DICompositeType, - member_description_factory: MemberDescriptionFactory<'tcx>) - -> RecursiveTypeDescription<'tcx> { - - // Insert the stub into the TypeMap in order to allow for recursive references - let mut type_map = debug_context(cx).type_map.borrow_mut(); - type_map.register_unique_id_with_metadata(unique_type_id, metadata_stub); - type_map.register_type_with_metadata(unfinished_type, metadata_stub); - - UnfinishedMetadata { - unfinished_type, - unique_type_id, - metadata_stub, - member_description_factory, - } -} - -impl<'tcx> RecursiveTypeDescription<'tcx> { - // Finishes up the description of the type in question (mostly by providing - // descriptions of the fields of the given type) and returns the final type - // metadata. - fn finalize<'a>(&self, cx: &CodegenCx<'a, 'tcx>) -> MetadataCreationResult { - match *self { - FinalMetadata(metadata) => MetadataCreationResult::new(metadata, false), - UnfinishedMetadata { - unfinished_type, - unique_type_id, - metadata_stub, - ref member_description_factory, - } => { - // Make sure that we have a forward declaration of the type in - // the TypeMap so that recursive references are possible. This - // will always be the case if the RecursiveTypeDescription has - // been properly created through the - // create_and_register_recursive_type_forward_declaration() - // function. - { - let type_map = debug_context(cx).type_map.borrow(); - if type_map.find_metadata_for_unique_id(unique_type_id).is_none() || - type_map.find_metadata_for_type(unfinished_type).is_none() { - bug!("Forward declaration of potentially recursive type \ - '{:?}' was not found in TypeMap!", - unfinished_type); - } - } - - // ... then create the member descriptions ... - let member_descriptions = - member_description_factory.create_member_descriptions(cx); - - // ... and attach them to the stub to complete it. - set_members_of_composite_type(cx, - metadata_stub, - &member_descriptions[..]); - return MetadataCreationResult::new(metadata_stub, true); - } - } - } -} - -// Returns from the enclosing function if the type metadata with the given -// unique id can be found in the type map -macro_rules! return_if_metadata_created_in_meantime { - ($cx: expr, $unique_type_id: expr) => ( - match debug_context($cx).type_map - .borrow() - .find_metadata_for_unique_id($unique_type_id) { - Some(metadata) => return MetadataCreationResult::new(metadata, true), - None => { /* proceed normally */ } - } - ) -} - -fn fixed_vec_metadata<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, - unique_type_id: UniqueTypeId, - array_or_slice_type: Ty<'tcx>, - element_type: Ty<'tcx>, - span: Span) - -> MetadataCreationResult { - let element_type_metadata = type_metadata(cx, element_type, span); - - return_if_metadata_created_in_meantime!(cx, unique_type_id); - - let (size, align) = cx.size_and_align_of(array_or_slice_type); - - let upper_bound = match array_or_slice_type.sty { - ty::TyArray(_, len) => { - len.unwrap_usize(cx.tcx) as c_longlong - } - _ => -1 - }; - - let subrange = unsafe { - llvm::LLVMRustDIBuilderGetOrCreateSubrange(DIB(cx), 0, upper_bound) - }; - - let subscripts = create_DIArray(DIB(cx), &[subrange]); - let metadata = unsafe { - llvm::LLVMRustDIBuilderCreateArrayType( - DIB(cx), - size.bits(), - align.abi_bits() as u32, - element_type_metadata, - subscripts) - }; - - return MetadataCreationResult::new(metadata, false); -} - -fn vec_slice_metadata<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, - slice_ptr_type: Ty<'tcx>, - element_type: Ty<'tcx>, - unique_type_id: UniqueTypeId, - span: Span) - -> MetadataCreationResult { - let data_ptr_type = cx.tcx.mk_imm_ptr(element_type); - - let data_ptr_metadata = type_metadata(cx, data_ptr_type, span); - - return_if_metadata_created_in_meantime!(cx, unique_type_id); - - let slice_type_name = compute_debuginfo_type_name(cx, slice_ptr_type, true); - - let (pointer_size, pointer_align) = cx.size_and_align_of(data_ptr_type); - let (usize_size, usize_align) = cx.size_and_align_of(cx.tcx.types.usize); - - let member_descriptions = [ - MemberDescription { - name: "data_ptr".to_string(), - type_metadata: data_ptr_metadata, - offset: Size::from_bytes(0), - size: pointer_size, - align: pointer_align, - flags: DIFlags::FlagZero, - }, - MemberDescription { - name: "length".to_string(), - type_metadata: type_metadata(cx, cx.tcx.types.usize, span), - offset: pointer_size, - size: usize_size, - align: usize_align, - flags: DIFlags::FlagZero, - }, - ]; - - let file_metadata = unknown_file_metadata(cx); - - let metadata = composite_type_metadata(cx, - slice_ptr_type, - &slice_type_name[..], - unique_type_id, - &member_descriptions, - NO_SCOPE_METADATA, - file_metadata, - span); - MetadataCreationResult::new(metadata, false) -} - -fn subroutine_type_metadata<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, - unique_type_id: UniqueTypeId, - signature: ty::PolyFnSig<'tcx>, - span: Span) - -> MetadataCreationResult -{ - let signature = cx.tcx.normalize_erasing_late_bound_regions( - ty::ParamEnv::reveal_all(), - &signature, - ); - - let mut signature_metadata: Vec = Vec::with_capacity(signature.inputs().len() + 1); - - // return type - signature_metadata.push(match signature.output().sty { - ty::TyTuple(ref tys) if tys.is_empty() => ptr::null_mut(), - _ => type_metadata(cx, signature.output(), span) - }); - - // regular arguments - for &argument_type in signature.inputs() { - signature_metadata.push(type_metadata(cx, argument_type, span)); - } - - return_if_metadata_created_in_meantime!(cx, unique_type_id); - - return MetadataCreationResult::new( - unsafe { - llvm::LLVMRustDIBuilderCreateSubroutineType( - DIB(cx), - unknown_file_metadata(cx), - create_DIArray(DIB(cx), &signature_metadata[..])) - }, - false); -} - -// FIXME(1563) This is all a bit of a hack because 'trait pointer' is an ill- -// defined concept. For the case of an actual trait pointer (i.e., Box, -// &Trait), trait_object_type should be the whole thing (e.g, Box) and -// trait_type should be the actual trait (e.g., Trait). Where the trait is part -// of a DST struct, there is no trait_object_type and the results of this -// function will be a little bit weird. -fn trait_pointer_metadata<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, - trait_type: Ty<'tcx>, - trait_object_type: Option>, - unique_type_id: UniqueTypeId) - -> DIType { - // The implementation provided here is a stub. It makes sure that the trait - // type is assigned the correct name, size, namespace, and source location. - // But it does not describe the trait's methods. - - let containing_scope = match trait_type.sty { - ty::TyDynamic(ref data, ..) => if let Some(principal) = data.principal() { - let def_id = principal.def_id(); - get_namespace_for_item(cx, def_id) - } else { - NO_SCOPE_METADATA - }, - _ => { - bug!("debuginfo: Unexpected trait-object type in \ - trait_pointer_metadata(): {:?}", - trait_type); - } - }; - - let trait_object_type = trait_object_type.unwrap_or(trait_type); - let trait_type_name = - compute_debuginfo_type_name(cx, trait_object_type, false); - - let file_metadata = unknown_file_metadata(cx); - - let layout = cx.layout_of(cx.tcx.mk_mut_ptr(trait_type)); - - assert_eq!(abi::FAT_PTR_ADDR, 0); - assert_eq!(abi::FAT_PTR_EXTRA, 1); - - let data_ptr_field = layout.field(cx, 0); - let vtable_field = layout.field(cx, 1); - let member_descriptions = [ - MemberDescription { - name: "pointer".to_string(), - type_metadata: type_metadata(cx, - cx.tcx.mk_mut_ptr(cx.tcx.types.u8), - syntax_pos::DUMMY_SP), - offset: layout.fields.offset(0), - size: data_ptr_field.size, - align: data_ptr_field.align, - flags: DIFlags::FlagArtificial, - }, - MemberDescription { - name: "vtable".to_string(), - type_metadata: type_metadata(cx, vtable_field.ty, syntax_pos::DUMMY_SP), - offset: layout.fields.offset(1), - size: vtable_field.size, - align: vtable_field.align, - flags: DIFlags::FlagArtificial, - }, - ]; - - composite_type_metadata(cx, - trait_object_type, - &trait_type_name[..], - unique_type_id, - &member_descriptions, - containing_scope, - file_metadata, - syntax_pos::DUMMY_SP) -} - -pub fn type_metadata<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, - t: Ty<'tcx>, - usage_site_span: Span) - -> DIType { - // Get the unique type id of this type. - let unique_type_id = { - let mut type_map = debug_context(cx).type_map.borrow_mut(); - // First, try to find the type in TypeMap. If we have seen it before, we - // can exit early here. - match type_map.find_metadata_for_type(t) { - Some(metadata) => { - return metadata; - }, - None => { - // The Ty is not in the TypeMap but maybe we have already seen - // an equivalent type (e.g. only differing in region arguments). - // In order to find out, generate the unique type id and look - // that up. - let unique_type_id = type_map.get_unique_type_id_of_type(cx, t); - match type_map.find_metadata_for_unique_id(unique_type_id) { - Some(metadata) => { - // There is already an equivalent type in the TypeMap. - // Register this Ty as an alias in the cache and - // return the cached metadata. - type_map.register_type_with_metadata(t, metadata); - return metadata; - }, - None => { - // There really is no type metadata for this type, so - // proceed by creating it. - unique_type_id - } - } - } - } - }; - - debug!("type_metadata: {:?}", t); - - let ptr_metadata = |ty: Ty<'tcx>| { - match ty.sty { - ty::TySlice(typ) => { - Ok(vec_slice_metadata(cx, t, typ, unique_type_id, usage_site_span)) - } - ty::TyStr => { - Ok(vec_slice_metadata(cx, t, cx.tcx.types.u8, unique_type_id, usage_site_span)) - } - ty::TyDynamic(..) => { - Ok(MetadataCreationResult::new( - trait_pointer_metadata(cx, ty, Some(t), unique_type_id), - false)) - } - _ => { - let pointee_metadata = type_metadata(cx, ty, usage_site_span); - - match debug_context(cx).type_map - .borrow() - .find_metadata_for_unique_id(unique_type_id) { - Some(metadata) => return Err(metadata), - None => { /* proceed normally */ } - }; - - Ok(MetadataCreationResult::new(pointer_type_metadata(cx, t, pointee_metadata), - false)) - } - } - }; - - let MetadataCreationResult { metadata, already_stored_in_typemap } = match t.sty { - ty::TyNever | - ty::TyBool | - ty::TyChar | - ty::TyInt(_) | - ty::TyUint(_) | - ty::TyFloat(_) => { - MetadataCreationResult::new(basic_type_metadata(cx, t), false) - } - ty::TyTuple(ref elements) if elements.is_empty() => { - MetadataCreationResult::new(basic_type_metadata(cx, t), false) - } - ty::TyArray(typ, _) | - ty::TySlice(typ) => { - fixed_vec_metadata(cx, unique_type_id, t, typ, usage_site_span) - } - ty::TyStr => { - fixed_vec_metadata(cx, unique_type_id, t, cx.tcx.types.i8, usage_site_span) - } - ty::TyDynamic(..) => { - MetadataCreationResult::new( - trait_pointer_metadata(cx, t, None, unique_type_id), - false) - } - ty::TyForeign(..) => { - MetadataCreationResult::new( - foreign_type_metadata(cx, t, unique_type_id), - false) - } - ty::TyRawPtr(ty::TypeAndMut{ty, ..}) | - ty::TyRef(_, ty, _) => { - match ptr_metadata(ty) { - Ok(res) => res, - Err(metadata) => return metadata, - } - } - ty::TyAdt(def, _) if def.is_box() => { - match ptr_metadata(t.boxed_ty()) { - Ok(res) => res, - Err(metadata) => return metadata, - } - } - ty::TyFnDef(..) | ty::TyFnPtr(_) => { - let fn_metadata = subroutine_type_metadata(cx, - unique_type_id, - t.fn_sig(cx.tcx), - usage_site_span).metadata; - match debug_context(cx).type_map - .borrow() - .find_metadata_for_unique_id(unique_type_id) { - Some(metadata) => return metadata, - None => { /* proceed normally */ } - }; - - // This is actually a function pointer, so wrap it in pointer DI - MetadataCreationResult::new(pointer_type_metadata(cx, t, fn_metadata), false) - - } - ty::TyClosure(def_id, substs) => { - let upvar_tys : Vec<_> = substs.upvar_tys(def_id, cx.tcx).collect(); - prepare_tuple_metadata(cx, - t, - &upvar_tys, - unique_type_id, - usage_site_span).finalize(cx) - } - ty::TyGenerator(def_id, substs, _) => { - let upvar_tys : Vec<_> = substs.field_tys(def_id, cx.tcx).map(|t| { - cx.tcx.normalize_erasing_regions(ParamEnv::reveal_all(), t) - }).collect(); - prepare_tuple_metadata(cx, - t, - &upvar_tys, - unique_type_id, - usage_site_span).finalize(cx) - } - ty::TyAdt(def, ..) => match def.adt_kind() { - AdtKind::Struct => { - prepare_struct_metadata(cx, - t, - unique_type_id, - usage_site_span).finalize(cx) - } - AdtKind::Union => { - prepare_union_metadata(cx, - t, - unique_type_id, - usage_site_span).finalize(cx) - } - AdtKind::Enum => { - prepare_enum_metadata(cx, - t, - def.did, - unique_type_id, - usage_site_span).finalize(cx) - } - }, - ty::TyTuple(ref elements) => { - prepare_tuple_metadata(cx, - t, - &elements[..], - unique_type_id, - usage_site_span).finalize(cx) - } - _ => { - bug!("debuginfo: unexpected type in type_metadata: {:?}", t) - } - }; - - { - let mut type_map = debug_context(cx).type_map.borrow_mut(); - - if already_stored_in_typemap { - // Also make sure that we already have a TypeMap entry for the unique type id. - let metadata_for_uid = match type_map.find_metadata_for_unique_id(unique_type_id) { - Some(metadata) => metadata, - None => { - span_bug!(usage_site_span, - "Expected type metadata for unique \ - type id '{}' to already be in \ - the debuginfo::TypeMap but it \ - was not. (Ty = {})", - type_map.get_unique_type_id_as_string(unique_type_id), - t); - } - }; - - match type_map.find_metadata_for_type(t) { - Some(metadata) => { - if metadata != metadata_for_uid { - span_bug!(usage_site_span, - "Mismatch between Ty and \ - UniqueTypeId maps in \ - debuginfo::TypeMap. \ - UniqueTypeId={}, Ty={}", - type_map.get_unique_type_id_as_string(unique_type_id), - t); - } - } - None => { - type_map.register_type_with_metadata(t, metadata); - } - } - } else { - type_map.register_type_with_metadata(t, metadata); - type_map.register_unique_id_with_metadata(unique_type_id, metadata); - } - } - - metadata -} - -pub fn file_metadata(cx: &CodegenCx, - file_name: &FileName, - defining_crate: CrateNum) -> DIFile { - debug!("file_metadata: file_name: {}, defining_crate: {}", - file_name, - defining_crate); - - let directory = if defining_crate == LOCAL_CRATE { - &cx.sess().working_dir.0 - } else { - // If the path comes from an upstream crate we assume it has been made - // independent of the compiler's working directory one way or another. - Path::new("") - }; - - file_metadata_raw(cx, &file_name.to_string(), &directory.to_string_lossy()) -} - -pub fn unknown_file_metadata(cx: &CodegenCx) -> DIFile { - file_metadata_raw(cx, "", "") -} - -fn file_metadata_raw(cx: &CodegenCx, - file_name: &str, - directory: &str) - -> DIFile { - let key = (Symbol::intern(file_name), Symbol::intern(directory)); - - if let Some(file_metadata) = debug_context(cx).created_files.borrow().get(&key) { - return *file_metadata; - } - - debug!("file_metadata: file_name: {}, directory: {}", file_name, directory); - - let file_name = CString::new(file_name).unwrap(); - let directory = CString::new(directory).unwrap(); - - let file_metadata = unsafe { - llvm::LLVMRustDIBuilderCreateFile(DIB(cx), - file_name.as_ptr(), - directory.as_ptr()) - }; - - let mut created_files = debug_context(cx).created_files.borrow_mut(); - created_files.insert(key, file_metadata); - file_metadata -} - -fn basic_type_metadata<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, - t: Ty<'tcx>) -> DIType { - - debug!("basic_type_metadata: {:?}", t); - - let (name, encoding) = match t.sty { - ty::TyNever => ("!", DW_ATE_unsigned), - ty::TyTuple(ref elements) if elements.is_empty() => - ("()", DW_ATE_unsigned), - ty::TyBool => ("bool", DW_ATE_boolean), - ty::TyChar => ("char", DW_ATE_unsigned_char), - ty::TyInt(int_ty) => { - (int_ty.ty_to_string(), DW_ATE_signed) - }, - ty::TyUint(uint_ty) => { - (uint_ty.ty_to_string(), DW_ATE_unsigned) - }, - ty::TyFloat(float_ty) => { - (float_ty.ty_to_string(), DW_ATE_float) - }, - _ => bug!("debuginfo::basic_type_metadata - t is invalid type") - }; - - let (size, align) = cx.size_and_align_of(t); - let name = CString::new(name).unwrap(); - let ty_metadata = unsafe { - llvm::LLVMRustDIBuilderCreateBasicType( - DIB(cx), - name.as_ptr(), - size.bits(), - align.abi_bits() as u32, - encoding) - }; - - return ty_metadata; -} - -fn foreign_type_metadata<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, - t: Ty<'tcx>, - unique_type_id: UniqueTypeId) -> DIType { - debug!("foreign_type_metadata: {:?}", t); - - let name = compute_debuginfo_type_name(cx, t, false); - create_struct_stub(cx, t, &name, unique_type_id, NO_SCOPE_METADATA) -} - -fn pointer_type_metadata<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, - pointer_type: Ty<'tcx>, - pointee_type_metadata: DIType) - -> DIType { - let (pointer_size, pointer_align) = cx.size_and_align_of(pointer_type); - let name = compute_debuginfo_type_name(cx, pointer_type, false); - let name = CString::new(name).unwrap(); - unsafe { - llvm::LLVMRustDIBuilderCreatePointerType( - DIB(cx), - pointee_type_metadata, - pointer_size.bits(), - pointer_align.abi_bits() as u32, - name.as_ptr()) - } -} - -pub fn compile_unit_metadata(tcx: TyCtxt, - codegen_unit_name: &str, - debug_context: &CrateDebugContext) - -> DIDescriptor { - let mut name_in_debuginfo = match tcx.sess.local_crate_source_file { - Some(ref path) => path.clone(), - None => PathBuf::from(&*tcx.crate_name(LOCAL_CRATE).as_str()), - }; - - // The OSX linker has an idiosyncrasy where it will ignore some debuginfo - // if multiple object files with the same DW_AT_name are linked together. - // As a workaround we generate unique names for each object file. Those do - // not correspond to an actual source file but that should be harmless. - if tcx.sess.target.target.options.is_like_osx { - name_in_debuginfo.push("@"); - name_in_debuginfo.push(codegen_unit_name); - } - - debug!("compile_unit_metadata: {:?}", name_in_debuginfo); - // FIXME(#41252) Remove "clang LLVM" if we can get GDB and LLVM to play nice. - let producer = format!("clang LLVM (rustc version {})", - (option_env!("CFG_VERSION")).expect("CFG_VERSION")); - - let name_in_debuginfo = name_in_debuginfo.to_string_lossy().into_owned(); - let name_in_debuginfo = CString::new(name_in_debuginfo).unwrap(); - let work_dir = CString::new(&tcx.sess.working_dir.0.to_string_lossy()[..]).unwrap(); - let producer = CString::new(producer).unwrap(); - let flags = "\0"; - let split_name = "\0"; - - unsafe { - let file_metadata = llvm::LLVMRustDIBuilderCreateFile( - debug_context.builder, name_in_debuginfo.as_ptr(), work_dir.as_ptr()); - - let unit_metadata = llvm::LLVMRustDIBuilderCreateCompileUnit( - debug_context.builder, - DW_LANG_RUST, - file_metadata, - producer.as_ptr(), - tcx.sess.opts.optimize != config::OptLevel::No, - flags.as_ptr() as *const _, - 0, - split_name.as_ptr() as *const _); - - if tcx.sess.opts.debugging_opts.profile { - let cu_desc_metadata = llvm::LLVMRustMetadataAsValue(debug_context.llcontext, - unit_metadata); - - let gcov_cu_info = [ - path_to_mdstring(debug_context.llcontext, - &tcx.output_filenames(LOCAL_CRATE).with_extension("gcno")), - path_to_mdstring(debug_context.llcontext, - &tcx.output_filenames(LOCAL_CRATE).with_extension("gcda")), - cu_desc_metadata, - ]; - let gcov_metadata = llvm::LLVMMDNodeInContext(debug_context.llcontext, - gcov_cu_info.as_ptr(), - gcov_cu_info.len() as c_uint); - - let llvm_gcov_ident = CString::new("llvm.gcov").unwrap(); - llvm::LLVMAddNamedMetadataOperand(debug_context.llmod, - llvm_gcov_ident.as_ptr(), - gcov_metadata); - } - - return unit_metadata; - }; - - fn path_to_mdstring(llcx: llvm::ContextRef, path: &Path) -> llvm::ValueRef { - let path_str = path2cstr(path); - unsafe { - llvm::LLVMMDStringInContext(llcx, - path_str.as_ptr(), - path_str.as_bytes().len() as c_uint) - } - } -} - -struct MetadataCreationResult { - metadata: DIType, - already_stored_in_typemap: bool -} - -impl MetadataCreationResult { - fn new(metadata: DIType, already_stored_in_typemap: bool) -> MetadataCreationResult { - MetadataCreationResult { - metadata, - already_stored_in_typemap, - } - } -} - -// Description of a type member, which can either be a regular field (as in -// structs or tuples) or an enum variant. -#[derive(Debug)] -struct MemberDescription { - name: String, - type_metadata: DIType, - offset: Size, - size: Size, - align: Align, - flags: DIFlags, -} - -// A factory for MemberDescriptions. It produces a list of member descriptions -// for some record-like type. MemberDescriptionFactories are used to defer the -// creation of type member descriptions in order to break cycles arising from -// recursive type definitions. -enum MemberDescriptionFactory<'tcx> { - StructMDF(StructMemberDescriptionFactory<'tcx>), - TupleMDF(TupleMemberDescriptionFactory<'tcx>), - EnumMDF(EnumMemberDescriptionFactory<'tcx>), - UnionMDF(UnionMemberDescriptionFactory<'tcx>), - VariantMDF(VariantMemberDescriptionFactory<'tcx>) -} - -impl<'tcx> MemberDescriptionFactory<'tcx> { - fn create_member_descriptions<'a>(&self, cx: &CodegenCx<'a, 'tcx>) - -> Vec { - match *self { - StructMDF(ref this) => { - this.create_member_descriptions(cx) - } - TupleMDF(ref this) => { - this.create_member_descriptions(cx) - } - EnumMDF(ref this) => { - this.create_member_descriptions(cx) - } - UnionMDF(ref this) => { - this.create_member_descriptions(cx) - } - VariantMDF(ref this) => { - this.create_member_descriptions(cx) - } - } - } -} - -//=----------------------------------------------------------------------------- -// Structs -//=----------------------------------------------------------------------------- - -// Creates MemberDescriptions for the fields of a struct -struct StructMemberDescriptionFactory<'tcx> { - ty: Ty<'tcx>, - variant: &'tcx ty::VariantDef, - span: Span, -} - -impl<'tcx> StructMemberDescriptionFactory<'tcx> { - fn create_member_descriptions<'a>(&self, cx: &CodegenCx<'a, 'tcx>) - -> Vec { - let layout = cx.layout_of(self.ty); - self.variant.fields.iter().enumerate().map(|(i, f)| { - let name = if self.variant.ctor_kind == CtorKind::Fn { - format!("__{}", i) - } else { - f.name.to_string() - }; - let field = layout.field(cx, i); - let (size, align) = field.size_and_align(); - MemberDescription { - name, - type_metadata: type_metadata(cx, field.ty, self.span), - offset: layout.fields.offset(i), - size, - align, - flags: DIFlags::FlagZero, - } - }).collect() - } -} - - -fn prepare_struct_metadata<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, - struct_type: Ty<'tcx>, - unique_type_id: UniqueTypeId, - span: Span) - -> RecursiveTypeDescription<'tcx> { - let struct_name = compute_debuginfo_type_name(cx, struct_type, false); - - let (struct_def_id, variant) = match struct_type.sty { - ty::TyAdt(def, _) => (def.did, def.non_enum_variant()), - _ => bug!("prepare_struct_metadata on a non-ADT") - }; - - let containing_scope = get_namespace_for_item(cx, struct_def_id); - - let struct_metadata_stub = create_struct_stub(cx, - struct_type, - &struct_name, - unique_type_id, - containing_scope); - - create_and_register_recursive_type_forward_declaration( - cx, - struct_type, - unique_type_id, - struct_metadata_stub, - StructMDF(StructMemberDescriptionFactory { - ty: struct_type, - variant, - span, - }) - ) -} - -//=----------------------------------------------------------------------------- -// Tuples -//=----------------------------------------------------------------------------- - -// Creates MemberDescriptions for the fields of a tuple -struct TupleMemberDescriptionFactory<'tcx> { - ty: Ty<'tcx>, - component_types: Vec>, - span: Span, -} - -impl<'tcx> TupleMemberDescriptionFactory<'tcx> { - fn create_member_descriptions<'a>(&self, cx: &CodegenCx<'a, 'tcx>) - -> Vec { - let layout = cx.layout_of(self.ty); - self.component_types.iter().enumerate().map(|(i, &component_type)| { - let (size, align) = cx.size_and_align_of(component_type); - MemberDescription { - name: format!("__{}", i), - type_metadata: type_metadata(cx, component_type, self.span), - offset: layout.fields.offset(i), - size, - align, - flags: DIFlags::FlagZero, - } - }).collect() - } -} - -fn prepare_tuple_metadata<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, - tuple_type: Ty<'tcx>, - component_types: &[Ty<'tcx>], - unique_type_id: UniqueTypeId, - span: Span) - -> RecursiveTypeDescription<'tcx> { - let tuple_name = compute_debuginfo_type_name(cx, tuple_type, false); - - create_and_register_recursive_type_forward_declaration( - cx, - tuple_type, - unique_type_id, - create_struct_stub(cx, - tuple_type, - &tuple_name[..], - unique_type_id, - NO_SCOPE_METADATA), - TupleMDF(TupleMemberDescriptionFactory { - ty: tuple_type, - component_types: component_types.to_vec(), - span, - }) - ) -} - -//=----------------------------------------------------------------------------- -// Unions -//=----------------------------------------------------------------------------- - -struct UnionMemberDescriptionFactory<'tcx> { - layout: TyLayout<'tcx>, - variant: &'tcx ty::VariantDef, - span: Span, -} - -impl<'tcx> UnionMemberDescriptionFactory<'tcx> { - fn create_member_descriptions<'a>(&self, cx: &CodegenCx<'a, 'tcx>) - -> Vec { - self.variant.fields.iter().enumerate().map(|(i, f)| { - let field = self.layout.field(cx, i); - let (size, align) = field.size_and_align(); - MemberDescription { - name: f.name.to_string(), - type_metadata: type_metadata(cx, field.ty, self.span), - offset: Size::from_bytes(0), - size, - align, - flags: DIFlags::FlagZero, - } - }).collect() - } -} - -fn prepare_union_metadata<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, - union_type: Ty<'tcx>, - unique_type_id: UniqueTypeId, - span: Span) - -> RecursiveTypeDescription<'tcx> { - let union_name = compute_debuginfo_type_name(cx, union_type, false); - - let (union_def_id, variant) = match union_type.sty { - ty::TyAdt(def, _) => (def.did, def.non_enum_variant()), - _ => bug!("prepare_union_metadata on a non-ADT") - }; - - let containing_scope = get_namespace_for_item(cx, union_def_id); - - let union_metadata_stub = create_union_stub(cx, - union_type, - &union_name, - unique_type_id, - containing_scope); - - create_and_register_recursive_type_forward_declaration( - cx, - union_type, - unique_type_id, - union_metadata_stub, - UnionMDF(UnionMemberDescriptionFactory { - layout: cx.layout_of(union_type), - variant, - span, - }) - ) -} - -//=----------------------------------------------------------------------------- -// Enums -//=----------------------------------------------------------------------------- - -// Describes the members of an enum value: An enum is described as a union of -// structs in DWARF. This MemberDescriptionFactory provides the description for -// the members of this union; so for every variant of the given enum, this -// factory will produce one MemberDescription (all with no name and a fixed -// offset of zero bytes). -struct EnumMemberDescriptionFactory<'tcx> { - enum_type: Ty<'tcx>, - layout: TyLayout<'tcx>, - discriminant_type_metadata: Option, - containing_scope: DIScope, - span: Span, -} - -impl<'tcx> EnumMemberDescriptionFactory<'tcx> { - fn create_member_descriptions<'a>(&self, cx: &CodegenCx<'a, 'tcx>) - -> Vec { - let adt = &self.enum_type.ty_adt_def().unwrap(); - match self.layout.variants { - layout::Variants::Single { .. } if adt.variants.is_empty() => vec![], - layout::Variants::Single { index } => { - let (variant_type_metadata, member_description_factory) = - describe_enum_variant(cx, - self.layout, - &adt.variants[index], - NoDiscriminant, - self.containing_scope, - self.span); - - let member_descriptions = - member_description_factory.create_member_descriptions(cx); - - set_members_of_composite_type(cx, - variant_type_metadata, - &member_descriptions[..]); - vec![ - MemberDescription { - name: "".to_string(), - type_metadata: variant_type_metadata, - offset: Size::from_bytes(0), - size: self.layout.size, - align: self.layout.align, - flags: DIFlags::FlagZero - } - ] - } - layout::Variants::Tagged { ref variants, .. } => { - let discriminant_info = RegularDiscriminant(self.discriminant_type_metadata - .expect("")); - (0..variants.len()).map(|i| { - let variant = self.layout.for_variant(cx, i); - let (variant_type_metadata, member_desc_factory) = - describe_enum_variant(cx, - variant, - &adt.variants[i], - discriminant_info, - self.containing_scope, - self.span); - - let member_descriptions = member_desc_factory - .create_member_descriptions(cx); - - set_members_of_composite_type(cx, - variant_type_metadata, - &member_descriptions); - MemberDescription { - name: "".to_string(), - type_metadata: variant_type_metadata, - offset: Size::from_bytes(0), - size: variant.size, - align: variant.align, - flags: DIFlags::FlagZero - } - }).collect() - } - layout::Variants::NicheFilling { dataful_variant, ref niche_variants, .. } => { - let variant = self.layout.for_variant(cx, dataful_variant); - // Create a description of the non-null variant - let (variant_type_metadata, member_description_factory) = - describe_enum_variant(cx, - variant, - &adt.variants[dataful_variant], - OptimizedDiscriminant, - self.containing_scope, - self.span); - - let variant_member_descriptions = - member_description_factory.create_member_descriptions(cx); - - set_members_of_composite_type(cx, - variant_type_metadata, - &variant_member_descriptions[..]); - - // Encode the information about the null variant in the union - // member's name. - let mut name = String::from("RUST$ENCODED$ENUM$"); - // HACK(eddyb) the debuggers should just handle offset+size - // of discriminant instead of us having to recover its path. - // Right now it's not even going to work for `niche_start > 0`, - // and for multiple niche variants it only supports the first. - fn compute_field_path<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, - name: &mut String, - layout: TyLayout<'tcx>, - offset: Size, - size: Size) { - for i in 0..layout.fields.count() { - let field_offset = layout.fields.offset(i); - if field_offset > offset { - continue; - } - let inner_offset = offset - field_offset; - let field = layout.field(cx, i); - if inner_offset + size <= field.size { - write!(name, "{}$", i).unwrap(); - compute_field_path(cx, name, field, inner_offset, size); - } - } - } - compute_field_path(cx, &mut name, - self.layout, - self.layout.fields.offset(0), - self.layout.field(cx, 0).size); - name.push_str(&adt.variants[*niche_variants.start()].name.as_str()); - - // Create the (singleton) list of descriptions of union members. - vec![ - MemberDescription { - name, - type_metadata: variant_type_metadata, - offset: Size::from_bytes(0), - size: variant.size, - align: variant.align, - flags: DIFlags::FlagZero - } - ] - } - } - } -} - -// Creates MemberDescriptions for the fields of a single enum variant. -struct VariantMemberDescriptionFactory<'tcx> { - // Cloned from the layout::Struct describing the variant. - offsets: Vec, - args: Vec<(String, Ty<'tcx>)>, - discriminant_type_metadata: Option, - span: Span, -} - -impl<'tcx> VariantMemberDescriptionFactory<'tcx> { - fn create_member_descriptions<'a>(&self, cx: &CodegenCx<'a, 'tcx>) - -> Vec { - self.args.iter().enumerate().map(|(i, &(ref name, ty))| { - let (size, align) = cx.size_and_align_of(ty); - MemberDescription { - name: name.to_string(), - type_metadata: match self.discriminant_type_metadata { - Some(metadata) if i == 0 => metadata, - _ => type_metadata(cx, ty, self.span) - }, - offset: self.offsets[i], - size, - align, - flags: DIFlags::FlagZero - } - }).collect() - } -} - -#[derive(Copy, Clone)] -enum EnumDiscriminantInfo { - RegularDiscriminant(DIType), - OptimizedDiscriminant, - NoDiscriminant -} - -// Returns a tuple of (1) type_metadata_stub of the variant, (2) the llvm_type -// of the variant, and (3) a MemberDescriptionFactory for producing the -// descriptions of the fields of the variant. This is a rudimentary version of a -// full RecursiveTypeDescription. -fn describe_enum_variant<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, - layout: layout::TyLayout<'tcx>, - variant: &'tcx ty::VariantDef, - discriminant_info: EnumDiscriminantInfo, - containing_scope: DIScope, - span: Span) - -> (DICompositeType, MemberDescriptionFactory<'tcx>) { - let variant_name = variant.name.as_str(); - let unique_type_id = debug_context(cx).type_map - .borrow_mut() - .get_unique_type_id_of_enum_variant( - cx, - layout.ty, - &variant_name); - - let metadata_stub = create_struct_stub(cx, - layout.ty, - &variant_name, - unique_type_id, - containing_scope); - - // If this is not a univariant enum, there is also the discriminant field. - let (discr_offset, discr_arg) = match discriminant_info { - RegularDiscriminant(_) => { - let enum_layout = cx.layout_of(layout.ty); - (Some(enum_layout.fields.offset(0)), - Some(("RUST$ENUM$DISR".to_string(), enum_layout.field(cx, 0).ty))) - } - _ => (None, None), - }; - let offsets = discr_offset.into_iter().chain((0..layout.fields.count()).map(|i| { - layout.fields.offset(i) - })).collect(); - - // Build an array of (field name, field type) pairs to be captured in the factory closure. - let args = discr_arg.into_iter().chain((0..layout.fields.count()).map(|i| { - let name = if variant.ctor_kind == CtorKind::Fn { - format!("__{}", i) - } else { - variant.fields[i].name.to_string() - }; - (name, layout.field(cx, i).ty) - })).collect(); - - let member_description_factory = - VariantMDF(VariantMemberDescriptionFactory { - offsets, - args, - discriminant_type_metadata: match discriminant_info { - RegularDiscriminant(discriminant_type_metadata) => { - Some(discriminant_type_metadata) - } - _ => None - }, - span, - }); - - (metadata_stub, member_description_factory) -} - -fn prepare_enum_metadata<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, - enum_type: Ty<'tcx>, - enum_def_id: DefId, - unique_type_id: UniqueTypeId, - span: Span) - -> RecursiveTypeDescription<'tcx> { - let enum_name = compute_debuginfo_type_name(cx, enum_type, false); - - let containing_scope = get_namespace_for_item(cx, enum_def_id); - // FIXME: This should emit actual file metadata for the enum, but we - // currently can't get the necessary information when it comes to types - // imported from other crates. Formerly we violated the ODR when performing - // LTO because we emitted debuginfo for the same type with varying file - // metadata, so as a workaround we pretend that the type comes from - // - let file_metadata = unknown_file_metadata(cx); - - let def = enum_type.ty_adt_def().unwrap(); - let enumerators_metadata: Vec = def.discriminants(cx.tcx) - .zip(&def.variants) - .map(|(discr, v)| { - let token = v.name.as_str(); - let name = CString::new(token.as_bytes()).unwrap(); - unsafe { - llvm::LLVMRustDIBuilderCreateEnumerator( - DIB(cx), - name.as_ptr(), - // FIXME: what if enumeration has i128 discriminant? - discr.val as u64) - } - }) - .collect(); - - let discriminant_type_metadata = |discr: layout::Primitive| { - let disr_type_key = (enum_def_id, discr); - let cached_discriminant_type_metadata = debug_context(cx).created_enum_disr_types - .borrow() - .get(&disr_type_key).cloned(); - match cached_discriminant_type_metadata { - Some(discriminant_type_metadata) => discriminant_type_metadata, - None => { - let (discriminant_size, discriminant_align) = - (discr.size(cx), discr.align(cx)); - let discriminant_base_type_metadata = - type_metadata(cx, discr.to_ty(cx.tcx), syntax_pos::DUMMY_SP); - let discriminant_name = get_enum_discriminant_name(cx, enum_def_id).as_str(); - - let name = CString::new(discriminant_name.as_bytes()).unwrap(); - let discriminant_type_metadata = unsafe { - llvm::LLVMRustDIBuilderCreateEnumerationType( - DIB(cx), - containing_scope, - name.as_ptr(), - file_metadata, - UNKNOWN_LINE_NUMBER, - discriminant_size.bits(), - discriminant_align.abi_bits() as u32, - create_DIArray(DIB(cx), &enumerators_metadata), - discriminant_base_type_metadata) - }; - - debug_context(cx).created_enum_disr_types - .borrow_mut() - .insert(disr_type_key, discriminant_type_metadata); - - discriminant_type_metadata - } - } - }; - - let layout = cx.layout_of(enum_type); - - let discriminant_type_metadata = match layout.variants { - layout::Variants::Single { .. } | - layout::Variants::NicheFilling { .. } => None, - layout::Variants::Tagged { ref tag, .. } => { - Some(discriminant_type_metadata(tag.value)) - } - }; - - match (&layout.abi, discriminant_type_metadata) { - (&layout::Abi::Scalar(_), Some(discr)) => return FinalMetadata(discr), - _ => {} - } - - let (enum_type_size, enum_type_align) = layout.size_and_align(); - - let enum_name = CString::new(enum_name).unwrap(); - let unique_type_id_str = CString::new( - debug_context(cx).type_map.borrow().get_unique_type_id_as_string(unique_type_id).as_bytes() - ).unwrap(); - let enum_metadata = unsafe { - llvm::LLVMRustDIBuilderCreateUnionType( - DIB(cx), - containing_scope, - enum_name.as_ptr(), - file_metadata, - UNKNOWN_LINE_NUMBER, - enum_type_size.bits(), - enum_type_align.abi_bits() as u32, - DIFlags::FlagZero, - ptr::null_mut(), - 0, // RuntimeLang - unique_type_id_str.as_ptr()) - }; - - return create_and_register_recursive_type_forward_declaration( - cx, - enum_type, - unique_type_id, - enum_metadata, - EnumMDF(EnumMemberDescriptionFactory { - enum_type, - layout, - discriminant_type_metadata, - containing_scope, - span, - }), - ); - - fn get_enum_discriminant_name(cx: &CodegenCx, - def_id: DefId) - -> InternedString { - cx.tcx.item_name(def_id) - } -} - -/// Creates debug information for a composite type, that is, anything that -/// results in a LLVM struct. -/// -/// Examples of Rust types to use this are: structs, tuples, boxes, vecs, and enums. -fn composite_type_metadata<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, - composite_type: Ty<'tcx>, - composite_type_name: &str, - composite_type_unique_id: UniqueTypeId, - member_descriptions: &[MemberDescription], - containing_scope: DIScope, - - // Ignore source location information as long as it - // can't be reconstructed for non-local crates. - _file_metadata: DIFile, - _definition_span: Span) - -> DICompositeType { - // Create the (empty) struct metadata node ... - let composite_type_metadata = create_struct_stub(cx, - composite_type, - composite_type_name, - composite_type_unique_id, - containing_scope); - // ... and immediately create and add the member descriptions. - set_members_of_composite_type(cx, - composite_type_metadata, - member_descriptions); - - return composite_type_metadata; -} - -fn set_members_of_composite_type(cx: &CodegenCx, - composite_type_metadata: DICompositeType, - member_descriptions: &[MemberDescription]) { - // In some rare cases LLVM metadata uniquing would lead to an existing type - // description being used instead of a new one created in - // create_struct_stub. This would cause a hard to trace assertion in - // DICompositeType::SetTypeArray(). The following check makes sure that we - // get a better error message if this should happen again due to some - // regression. - { - let mut composite_types_completed = - debug_context(cx).composite_types_completed.borrow_mut(); - if composite_types_completed.contains(&composite_type_metadata) { - bug!("debuginfo::set_members_of_composite_type() - \ - Already completed forward declaration re-encountered."); - } else { - composite_types_completed.insert(composite_type_metadata); - } - } - - let member_metadata: Vec = member_descriptions - .iter() - .map(|member_description| { - let member_name = member_description.name.as_bytes(); - let member_name = CString::new(member_name).unwrap(); - unsafe { - llvm::LLVMRustDIBuilderCreateMemberType( - DIB(cx), - composite_type_metadata, - member_name.as_ptr(), - unknown_file_metadata(cx), - UNKNOWN_LINE_NUMBER, - member_description.size.bits(), - member_description.align.abi_bits() as u32, - member_description.offset.bits(), - member_description.flags, - member_description.type_metadata) - } - }) - .collect(); - - unsafe { - let type_array = create_DIArray(DIB(cx), &member_metadata[..]); - llvm::LLVMRustDICompositeTypeSetTypeArray( - DIB(cx), composite_type_metadata, type_array); - } -} - -// A convenience wrapper around LLVMRustDIBuilderCreateStructType(). Does not do -// any caching, does not add any fields to the struct. This can be done later -// with set_members_of_composite_type(). -fn create_struct_stub<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, - struct_type: Ty<'tcx>, - struct_type_name: &str, - unique_type_id: UniqueTypeId, - containing_scope: DIScope) - -> DICompositeType { - let (struct_size, struct_align) = cx.size_and_align_of(struct_type); - - let name = CString::new(struct_type_name).unwrap(); - let unique_type_id = CString::new( - debug_context(cx).type_map.borrow().get_unique_type_id_as_string(unique_type_id).as_bytes() - ).unwrap(); - let metadata_stub = unsafe { - // LLVMRustDIBuilderCreateStructType() wants an empty array. A null - // pointer will lead to hard to trace and debug LLVM assertions - // later on in llvm/lib/IR/Value.cpp. - let empty_array = create_DIArray(DIB(cx), &[]); - - llvm::LLVMRustDIBuilderCreateStructType( - DIB(cx), - containing_scope, - name.as_ptr(), - unknown_file_metadata(cx), - UNKNOWN_LINE_NUMBER, - struct_size.bits(), - struct_align.abi_bits() as u32, - DIFlags::FlagZero, - ptr::null_mut(), - empty_array, - 0, - ptr::null_mut(), - unique_type_id.as_ptr()) - }; - - return metadata_stub; -} - -fn create_union_stub<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, - union_type: Ty<'tcx>, - union_type_name: &str, - unique_type_id: UniqueTypeId, - containing_scope: DIScope) - -> DICompositeType { - let (union_size, union_align) = cx.size_and_align_of(union_type); - - let name = CString::new(union_type_name).unwrap(); - let unique_type_id = CString::new( - debug_context(cx).type_map.borrow().get_unique_type_id_as_string(unique_type_id).as_bytes() - ).unwrap(); - let metadata_stub = unsafe { - // LLVMRustDIBuilderCreateUnionType() wants an empty array. A null - // pointer will lead to hard to trace and debug LLVM assertions - // later on in llvm/lib/IR/Value.cpp. - let empty_array = create_DIArray(DIB(cx), &[]); - - llvm::LLVMRustDIBuilderCreateUnionType( - DIB(cx), - containing_scope, - name.as_ptr(), - unknown_file_metadata(cx), - UNKNOWN_LINE_NUMBER, - union_size.bits(), - union_align.abi_bits() as u32, - DIFlags::FlagZero, - empty_array, - 0, // RuntimeLang - unique_type_id.as_ptr()) - }; - - return metadata_stub; -} - -/// Creates debug information for the given global variable. -/// -/// Adds the created metadata nodes directly to the crate's IR. -pub fn create_global_var_metadata(cx: &CodegenCx, - def_id: DefId, - global: ValueRef) { - if cx.dbg_cx.is_none() { - return; - } - - let tcx = cx.tcx; - let attrs = tcx.trans_fn_attrs(def_id); - - if attrs.flags.contains(TransFnAttrFlags::NO_DEBUG) { - return; - } - - let no_mangle = attrs.flags.contains(TransFnAttrFlags::NO_MANGLE); - // We may want to remove the namespace scope if we're in an extern block, see: - // https://github.com/rust-lang/rust/pull/46457#issuecomment-351750952 - let var_scope = get_namespace_for_item(cx, def_id); - let span = tcx.def_span(def_id); - - let (file_metadata, line_number) = if span != syntax_pos::DUMMY_SP { - let loc = span_start(cx, span); - (file_metadata(cx, &loc.file.name, LOCAL_CRATE), loc.line as c_uint) - } else { - (unknown_file_metadata(cx), UNKNOWN_LINE_NUMBER) - }; - - let is_local_to_unit = is_node_local_to_unit(cx, def_id); - let variable_type = Instance::mono(cx.tcx, def_id).ty(cx.tcx); - let type_metadata = type_metadata(cx, variable_type, span); - let var_name = tcx.item_name(def_id).to_string(); - let var_name = CString::new(var_name).unwrap(); - let linkage_name = if no_mangle { - None - } else { - let linkage_name = mangled_name_of_instance(cx, Instance::mono(tcx, def_id)); - Some(CString::new(linkage_name.to_string()).unwrap()) - }; - - let global_align = cx.align_of(variable_type); - - unsafe { - llvm::LLVMRustDIBuilderCreateStaticVariable(DIB(cx), - var_scope, - var_name.as_ptr(), - // If null, linkage_name field is omitted, - // which is what we want for no_mangle statics - linkage_name.as_ref() - .map_or(ptr::null(), |name| name.as_ptr()), - file_metadata, - line_number, - type_metadata, - is_local_to_unit, - global, - ptr::null_mut(), - global_align.abi() as u32, - ); - } -} - -// Creates an "extension" of an existing DIScope into another file. -pub fn extend_scope_to_file(cx: &CodegenCx, - scope_metadata: DIScope, - file: &syntax_pos::FileMap, - defining_crate: CrateNum) - -> DILexicalBlock { - let file_metadata = file_metadata(cx, &file.name, defining_crate); - unsafe { - llvm::LLVMRustDIBuilderCreateLexicalBlockFile( - DIB(cx), - scope_metadata, - file_metadata) - } -} - -/// Creates debug information for the given vtable, which is for the -/// given type. -/// -/// Adds the created metadata nodes directly to the crate's IR. -pub fn create_vtable_metadata<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, - ty: ty::Ty<'tcx>, - vtable: ValueRef) { - if cx.dbg_cx.is_none() { - return; - } - - let type_metadata = type_metadata(cx, ty, syntax_pos::DUMMY_SP); - - unsafe { - // LLVMRustDIBuilderCreateStructType() wants an empty array. A null - // pointer will lead to hard to trace and debug LLVM assertions - // later on in llvm/lib/IR/Value.cpp. - let empty_array = create_DIArray(DIB(cx), &[]); - - let name = CString::new("vtable").unwrap(); - - // Create a new one each time. We don't want metadata caching - // here, because each vtable will refer to a unique containing - // type. - let vtable_type = llvm::LLVMRustDIBuilderCreateStructType( - DIB(cx), - NO_SCOPE_METADATA, - name.as_ptr(), - unknown_file_metadata(cx), - UNKNOWN_LINE_NUMBER, - Size::from_bytes(0).bits(), - cx.tcx.data_layout.pointer_align.abi_bits() as u32, - DIFlags::FlagArtificial, - ptr::null_mut(), - empty_array, - 0, - type_metadata, - name.as_ptr() - ); - - llvm::LLVMRustDIBuilderCreateStaticVariable(DIB(cx), - NO_SCOPE_METADATA, - name.as_ptr(), - // LLVM 3.9 - // doesn't accept - // null here, so - // pass the name - // as the linkage - // name. - name.as_ptr(), - unknown_file_metadata(cx), - UNKNOWN_LINE_NUMBER, - vtable_type, - true, - vtable, - ptr::null_mut(), - 0); - } -} diff --git a/src/librustc_trans/debuginfo/mod.rs b/src/librustc_trans/debuginfo/mod.rs deleted file mode 100644 index 2039a90a043..00000000000 --- a/src/librustc_trans/debuginfo/mod.rs +++ /dev/null @@ -1,542 +0,0 @@ -// Copyright 2012-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. - -// See doc.rs for documentation. -mod doc; - -use self::VariableAccess::*; -use self::VariableKind::*; - -use self::utils::{DIB, span_start, create_DIArray, is_node_local_to_unit}; -use self::namespace::mangled_name_of_instance; -use self::type_names::compute_debuginfo_type_name; -use self::metadata::{type_metadata, file_metadata, TypeMap}; -use self::source_loc::InternalDebugLocation::{self, UnknownLocation}; - -use llvm; -use llvm::{ModuleRef, ContextRef, ValueRef}; -use llvm::debuginfo::{DIFile, DIType, DIScope, DIBuilderRef, DISubprogram, DIArray, DIFlags}; -use rustc::hir::TransFnAttrFlags; -use rustc::hir::def_id::{DefId, CrateNum}; -use rustc::ty::subst::{Substs, UnpackedKind}; - -use abi::Abi; -use common::CodegenCx; -use builder::Builder; -use monomorphize::Instance; -use rustc::ty::{self, ParamEnv, Ty, InstanceDef}; -use rustc::mir; -use rustc::session::config::{self, FullDebugInfo, LimitedDebugInfo, NoDebugInfo}; -use rustc::util::nodemap::{DefIdMap, FxHashMap, FxHashSet}; - -use libc::c_uint; -use std::cell::{Cell, RefCell}; -use std::ffi::CString; -use std::ptr; - -use syntax_pos::{self, Span, Pos}; -use syntax::ast; -use syntax::symbol::{Symbol, InternedString}; -use rustc::ty::layout::{self, LayoutOf}; - -pub mod gdb; -mod utils; -mod namespace; -mod type_names; -pub mod metadata; -mod create_scope_map; -mod source_loc; - -pub use self::create_scope_map::{create_mir_scopes, MirDebugScope}; -pub use self::source_loc::start_emitting_source_locations; -pub use self::metadata::create_global_var_metadata; -pub use self::metadata::create_vtable_metadata; -pub use self::metadata::extend_scope_to_file; -pub use self::source_loc::set_source_location; - -#[allow(non_upper_case_globals)] -const DW_TAG_auto_variable: c_uint = 0x100; -#[allow(non_upper_case_globals)] -const DW_TAG_arg_variable: c_uint = 0x101; - -/// A context object for maintaining all state needed by the debuginfo module. -pub struct CrateDebugContext<'tcx> { - llcontext: ContextRef, - llmod: ModuleRef, - builder: DIBuilderRef, - created_files: RefCell>, - created_enum_disr_types: RefCell>, - - type_map: RefCell>, - namespace_map: RefCell>, - - // This collection is used to assert that composite types (structs, enums, - // ...) have their members only set once: - composite_types_completed: RefCell>, -} - -impl<'tcx> CrateDebugContext<'tcx> { - pub fn new(llmod: ModuleRef) -> CrateDebugContext<'tcx> { - debug!("CrateDebugContext::new"); - let builder = unsafe { llvm::LLVMRustDIBuilderCreate(llmod) }; - // DIBuilder inherits context from the module, so we'd better use the same one - let llcontext = unsafe { llvm::LLVMGetModuleContext(llmod) }; - CrateDebugContext { - llcontext, - llmod, - builder, - created_files: RefCell::new(FxHashMap()), - created_enum_disr_types: RefCell::new(FxHashMap()), - type_map: RefCell::new(TypeMap::new()), - namespace_map: RefCell::new(DefIdMap()), - composite_types_completed: RefCell::new(FxHashSet()), - } - } -} - -pub enum FunctionDebugContext { - RegularContext(FunctionDebugContextData), - DebugInfoDisabled, - FunctionWithoutDebugInfo, -} - -impl FunctionDebugContext { - pub fn get_ref<'a>(&'a self, span: Span) -> &'a FunctionDebugContextData { - match *self { - FunctionDebugContext::RegularContext(ref data) => data, - FunctionDebugContext::DebugInfoDisabled => { - span_bug!(span, "{}", FunctionDebugContext::debuginfo_disabled_message()); - } - FunctionDebugContext::FunctionWithoutDebugInfo => { - span_bug!(span, "{}", FunctionDebugContext::should_be_ignored_message()); - } - } - } - - fn debuginfo_disabled_message() -> &'static str { - "debuginfo: Error trying to access FunctionDebugContext although debug info is disabled!" - } - - fn should_be_ignored_message() -> &'static str { - "debuginfo: Error trying to access FunctionDebugContext for function that should be \ - ignored by debug info!" - } -} - -pub struct FunctionDebugContextData { - fn_metadata: DISubprogram, - source_locations_enabled: Cell, - pub defining_crate: CrateNum, -} - -pub enum VariableAccess<'a> { - // The llptr given is an alloca containing the variable's value - DirectVariable { alloca: ValueRef }, - // The llptr given is an alloca containing the start of some pointer chain - // leading to the variable's content. - IndirectVariable { alloca: ValueRef, address_operations: &'a [i64] } -} - -pub enum VariableKind { - ArgumentVariable(usize /*index*/), - LocalVariable, - CapturedVariable, -} - -/// Create any deferred debug metadata nodes -pub fn finalize(cx: &CodegenCx) { - if cx.dbg_cx.is_none() { - return; - } - - debug!("finalize"); - - if gdb::needs_gdb_debug_scripts_section(cx) { - // Add a .debug_gdb_scripts section to this compile-unit. This will - // cause GDB to try and load the gdb_load_rust_pretty_printers.py file, - // which activates the Rust pretty printers for binary this section is - // contained in. - gdb::get_or_insert_gdb_debug_scripts_section_global(cx); - } - - unsafe { - llvm::LLVMRustDIBuilderFinalize(DIB(cx)); - llvm::LLVMRustDIBuilderDispose(DIB(cx)); - // Debuginfo generation in LLVM by default uses a higher - // version of dwarf than macOS currently understands. We can - // instruct LLVM to emit an older version of dwarf, however, - // for macOS to understand. For more info see #11352 - // This can be overridden using --llvm-opts -dwarf-version,N. - // Android has the same issue (#22398) - if cx.sess().target.target.options.is_like_osx || - cx.sess().target.target.options.is_like_android { - llvm::LLVMRustAddModuleFlag(cx.llmod, - "Dwarf Version\0".as_ptr() as *const _, - 2) - } - - // Indicate that we want CodeView debug information on MSVC - if cx.sess().target.target.options.is_like_msvc { - llvm::LLVMRustAddModuleFlag(cx.llmod, - "CodeView\0".as_ptr() as *const _, - 1) - } - - // Prevent bitcode readers from deleting the debug info. - let ptr = "Debug Info Version\0".as_ptr(); - llvm::LLVMRustAddModuleFlag(cx.llmod, ptr as *const _, - llvm::LLVMRustDebugMetadataVersion()); - }; -} - -/// Creates the function-specific debug context. -/// -/// Returns the FunctionDebugContext for the function which holds state needed -/// for debug info creation. The function may also return another variant of the -/// FunctionDebugContext enum which indicates why no debuginfo should be created -/// for the function. -pub fn create_function_debug_context<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, - instance: Instance<'tcx>, - sig: ty::FnSig<'tcx>, - llfn: ValueRef, - mir: &mir::Mir) -> FunctionDebugContext { - if cx.sess().opts.debuginfo == NoDebugInfo { - return FunctionDebugContext::DebugInfoDisabled; - } - - if let InstanceDef::Item(def_id) = instance.def { - if cx.tcx.trans_fn_attrs(def_id).flags.contains(TransFnAttrFlags::NO_DEBUG) { - return FunctionDebugContext::FunctionWithoutDebugInfo; - } - } - - let span = mir.span; - - // This can be the case for functions inlined from another crate - if span == syntax_pos::DUMMY_SP { - // FIXME(simulacrum): Probably can't happen; remove. - return FunctionDebugContext::FunctionWithoutDebugInfo; - } - - let def_id = instance.def_id(); - let containing_scope = get_containing_scope(cx, instance); - let loc = span_start(cx, span); - let file_metadata = file_metadata(cx, &loc.file.name, def_id.krate); - - let function_type_metadata = unsafe { - let fn_signature = get_function_signature(cx, sig); - llvm::LLVMRustDIBuilderCreateSubroutineType(DIB(cx), file_metadata, fn_signature) - }; - - // Find the enclosing function, in case this is a closure. - let def_key = cx.tcx.def_key(def_id); - let mut name = def_key.disambiguated_data.data.to_string(); - - let enclosing_fn_def_id = cx.tcx.closure_base_def_id(def_id); - - // Get_template_parameters() will append a `<...>` clause to the function - // name if necessary. - let generics = cx.tcx.generics_of(enclosing_fn_def_id); - let substs = instance.substs.truncate_to(cx.tcx, generics); - let template_parameters = get_template_parameters(cx, - &generics, - substs, - file_metadata, - &mut name); - - // Get the linkage_name, which is just the symbol name - let linkage_name = mangled_name_of_instance(cx, instance); - - let scope_line = span_start(cx, span).line; - let is_local_to_unit = is_node_local_to_unit(cx, def_id); - - let function_name = CString::new(name).unwrap(); - let linkage_name = CString::new(linkage_name.to_string()).unwrap(); - - let mut flags = DIFlags::FlagPrototyped; - - let local_id = cx.tcx.hir.as_local_node_id(def_id); - match *cx.sess().entry_fn.borrow() { - Some((id, _, _)) => { - if local_id == Some(id) { - flags = flags | DIFlags::FlagMainSubprogram; - } - } - None => {} - }; - if cx.layout_of(sig.output()).abi == ty::layout::Abi::Uninhabited { - flags = flags | DIFlags::FlagNoReturn; - } - - let fn_metadata = unsafe { - llvm::LLVMRustDIBuilderCreateFunction( - DIB(cx), - containing_scope, - function_name.as_ptr(), - linkage_name.as_ptr(), - file_metadata, - loc.line as c_uint, - function_type_metadata, - is_local_to_unit, - true, - scope_line as c_uint, - flags, - cx.sess().opts.optimize != config::OptLevel::No, - llfn, - template_parameters, - ptr::null_mut()) - }; - - // Initialize fn debug context (including scope map and namespace map) - let fn_debug_context = FunctionDebugContextData { - fn_metadata, - source_locations_enabled: Cell::new(false), - defining_crate: def_id.krate, - }; - - return FunctionDebugContext::RegularContext(fn_debug_context); - - fn get_function_signature<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, - sig: ty::FnSig<'tcx>) -> DIArray { - if cx.sess().opts.debuginfo == LimitedDebugInfo { - return create_DIArray(DIB(cx), &[]); - } - - let mut signature = Vec::with_capacity(sig.inputs().len() + 1); - - // Return type -- llvm::DIBuilder wants this at index 0 - signature.push(match sig.output().sty { - ty::TyTuple(ref tys) if tys.is_empty() => ptr::null_mut(), - _ => type_metadata(cx, sig.output(), syntax_pos::DUMMY_SP) - }); - - let inputs = if sig.abi == Abi::RustCall { - &sig.inputs()[..sig.inputs().len() - 1] - } else { - sig.inputs() - }; - - // Arguments types - if cx.sess().target.target.options.is_like_msvc { - // FIXME(#42800): - // There is a bug in MSDIA that leads to a crash when it encounters - // a fixed-size array of `u8` or something zero-sized in a - // function-type (see #40477). - // As a workaround, we replace those fixed-size arrays with a - // pointer-type. So a function `fn foo(a: u8, b: [u8; 4])` would - // appear as `fn foo(a: u8, b: *const u8)` in debuginfo, - // and a function `fn bar(x: [(); 7])` as `fn bar(x: *const ())`. - // This transformed type is wrong, but these function types are - // already inaccurate due to ABI adjustments (see #42800). - signature.extend(inputs.iter().map(|&t| { - let t = match t.sty { - ty::TyArray(ct, _) - if (ct == cx.tcx.types.u8) || cx.layout_of(ct).is_zst() => { - cx.tcx.mk_imm_ptr(ct) - } - _ => t - }; - type_metadata(cx, t, syntax_pos::DUMMY_SP) - })); - } else { - signature.extend(inputs.iter().map(|t| { - type_metadata(cx, t, syntax_pos::DUMMY_SP) - })); - } - - if sig.abi == Abi::RustCall && !sig.inputs().is_empty() { - if let ty::TyTuple(args) = sig.inputs()[sig.inputs().len() - 1].sty { - for &argument_type in args { - signature.push(type_metadata(cx, argument_type, syntax_pos::DUMMY_SP)); - } - } - } - - return create_DIArray(DIB(cx), &signature[..]); - } - - fn get_template_parameters<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, - generics: &ty::Generics, - substs: &Substs<'tcx>, - file_metadata: DIFile, - name_to_append_suffix_to: &mut String) - -> DIArray - { - if substs.types().next().is_none() { - return create_DIArray(DIB(cx), &[]); - } - - name_to_append_suffix_to.push('<'); - for (i, actual_type) in substs.types().enumerate() { - if i != 0 { - name_to_append_suffix_to.push_str(","); - } - - let actual_type = cx.tcx.normalize_erasing_regions(ParamEnv::reveal_all(), actual_type); - // Add actual type name to <...> clause of function name - let actual_type_name = compute_debuginfo_type_name(cx, - actual_type, - true); - name_to_append_suffix_to.push_str(&actual_type_name[..]); - } - name_to_append_suffix_to.push('>'); - - // Again, only create type information if full debuginfo is enabled - let template_params: Vec<_> = if cx.sess().opts.debuginfo == FullDebugInfo { - let names = get_parameter_names(cx, generics); - substs.iter().zip(names).filter_map(|(kind, name)| { - if let UnpackedKind::Type(ty) = kind.unpack() { - let actual_type = cx.tcx.normalize_erasing_regions(ParamEnv::reveal_all(), ty); - let actual_type_metadata = - type_metadata(cx, actual_type, syntax_pos::DUMMY_SP); - let name = CString::new(name.as_str().as_bytes()).unwrap(); - Some(unsafe { - llvm::LLVMRustDIBuilderCreateTemplateTypeParameter( - DIB(cx), - ptr::null_mut(), - name.as_ptr(), - actual_type_metadata, - file_metadata, - 0, - 0) - }) - } else { - None - } - }).collect() - } else { - vec![] - }; - - return create_DIArray(DIB(cx), &template_params[..]); - } - - fn get_parameter_names(cx: &CodegenCx, - generics: &ty::Generics) - -> Vec { - let mut names = generics.parent.map_or(vec![], |def_id| { - get_parameter_names(cx, cx.tcx.generics_of(def_id)) - }); - names.extend(generics.params.iter().map(|param| param.name)); - names - } - - fn get_containing_scope<'cx, 'tcx>(cx: &CodegenCx<'cx, 'tcx>, - instance: Instance<'tcx>) - -> DIScope { - // First, let's see if this is a method within an inherent impl. Because - // if yes, we want to make the result subroutine DIE a child of the - // subroutine's self-type. - let self_type = cx.tcx.impl_of_method(instance.def_id()).and_then(|impl_def_id| { - // If the method does *not* belong to a trait, proceed - if cx.tcx.trait_id_of_impl(impl_def_id).is_none() { - let impl_self_ty = cx.tcx.subst_and_normalize_erasing_regions( - instance.substs, - ty::ParamEnv::reveal_all(), - &cx.tcx.type_of(impl_def_id), - ); - - // Only "class" methods are generally understood by LLVM, - // so avoid methods on other types (e.g. `<*mut T>::null`). - match impl_self_ty.sty { - ty::TyAdt(def, ..) if !def.is_box() => { - Some(type_metadata(cx, impl_self_ty, syntax_pos::DUMMY_SP)) - } - _ => None - } - } else { - // For trait method impls we still use the "parallel namespace" - // strategy - None - } - }); - - self_type.unwrap_or_else(|| { - namespace::item_namespace(cx, DefId { - krate: instance.def_id().krate, - index: cx.tcx - .def_key(instance.def_id()) - .parent - .expect("get_containing_scope: missing parent?") - }) - }) - } -} - -pub fn declare_local<'a, 'tcx>(bx: &Builder<'a, 'tcx>, - dbg_context: &FunctionDebugContext, - variable_name: ast::Name, - variable_type: Ty<'tcx>, - scope_metadata: DIScope, - variable_access: VariableAccess, - variable_kind: VariableKind, - span: Span) { - let cx = bx.cx; - - let file = span_start(cx, span).file; - let file_metadata = file_metadata(cx, - &file.name, - dbg_context.get_ref(span).defining_crate); - - let loc = span_start(cx, span); - let type_metadata = type_metadata(cx, variable_type, span); - - let (argument_index, dwarf_tag) = match variable_kind { - ArgumentVariable(index) => (index as c_uint, DW_TAG_arg_variable), - LocalVariable | - CapturedVariable => (0, DW_TAG_auto_variable) - }; - let align = cx.align_of(variable_type); - - let name = CString::new(variable_name.as_str().as_bytes()).unwrap(); - match (variable_access, &[][..]) { - (DirectVariable { alloca }, address_operations) | - (IndirectVariable {alloca, address_operations}, _) => { - let metadata = unsafe { - llvm::LLVMRustDIBuilderCreateVariable( - DIB(cx), - dwarf_tag, - scope_metadata, - name.as_ptr(), - file_metadata, - loc.line as c_uint, - type_metadata, - cx.sess().opts.optimize != config::OptLevel::No, - DIFlags::FlagZero, - argument_index, - align.abi() as u32, - ) - }; - source_loc::set_debug_location(bx, - InternalDebugLocation::new(scope_metadata, loc.line, loc.col.to_usize())); - unsafe { - let debug_loc = llvm::LLVMGetCurrentDebugLocation(bx.llbuilder); - let instr = llvm::LLVMRustDIBuilderInsertDeclareAtEnd( - DIB(cx), - alloca, - metadata, - address_operations.as_ptr(), - address_operations.len() as c_uint, - debug_loc, - bx.llbb()); - - llvm::LLVMSetInstDebugLocation(bx.llbuilder, instr); - } - } - } - - match variable_kind { - ArgumentVariable(_) | CapturedVariable => { - assert!(!dbg_context.get_ref(span).source_locations_enabled.get()); - source_loc::set_debug_location(bx, UnknownLocation); - } - _ => { /* nothing to do */ } - } -} diff --git a/src/librustc_trans/debuginfo/namespace.rs b/src/librustc_trans/debuginfo/namespace.rs deleted file mode 100644 index 51c45de9dc2..00000000000 --- a/src/librustc_trans/debuginfo/namespace.rs +++ /dev/null @@ -1,66 +0,0 @@ -// Copyright 2015 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -// Namespace Handling. - -use super::metadata::{unknown_file_metadata, UNKNOWN_LINE_NUMBER}; -use super::utils::{DIB, debug_context}; -use monomorphize::Instance; -use rustc::ty; - -use llvm; -use llvm::debuginfo::DIScope; -use rustc::hir::def_id::DefId; -use rustc::hir::map::DefPathData; -use common::CodegenCx; - -use std::ffi::CString; -use std::ptr; - -pub fn mangled_name_of_instance<'a, 'tcx>( - cx: &CodegenCx<'a, 'tcx>, - instance: Instance<'tcx>, -) -> ty::SymbolName { - let tcx = cx.tcx; - tcx.symbol_name(instance) -} - -pub fn item_namespace(cx: &CodegenCx, def_id: DefId) -> DIScope { - if let Some(&scope) = debug_context(cx).namespace_map.borrow().get(&def_id) { - return scope; - } - - let def_key = cx.tcx.def_key(def_id); - let parent_scope = def_key.parent.map_or(ptr::null_mut(), |parent| { - item_namespace(cx, DefId { - krate: def_id.krate, - index: parent - }) - }); - - let namespace_name = match def_key.disambiguated_data.data { - DefPathData::CrateRoot => cx.tcx.crate_name(def_id.krate).as_str(), - data => data.as_interned_str().as_str() - }; - - let namespace_name = CString::new(namespace_name.as_bytes()).unwrap(); - - let scope = unsafe { - llvm::LLVMRustDIBuilderCreateNameSpace( - DIB(cx), - parent_scope, - namespace_name.as_ptr(), - unknown_file_metadata(cx), - UNKNOWN_LINE_NUMBER) - }; - - debug_context(cx).namespace_map.borrow_mut().insert(def_id, scope); - scope -} diff --git a/src/librustc_trans/debuginfo/source_loc.rs b/src/librustc_trans/debuginfo/source_loc.rs deleted file mode 100644 index 7440296ce5d..00000000000 --- a/src/librustc_trans/debuginfo/source_loc.rs +++ /dev/null @@ -1,107 +0,0 @@ -// Copyright 2015 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -use self::InternalDebugLocation::*; - -use super::utils::{debug_context, span_start}; -use super::metadata::UNKNOWN_COLUMN_NUMBER; -use super::FunctionDebugContext; - -use llvm; -use llvm::debuginfo::DIScope; -use builder::Builder; - -use libc::c_uint; -use std::ptr; -use syntax_pos::{Span, Pos}; - -/// Sets the current debug location at the beginning of the span. -/// -/// Maps to a call to llvm::LLVMSetCurrentDebugLocation(...). -pub fn set_source_location( - debug_context: &FunctionDebugContext, bx: &Builder, scope: DIScope, span: Span -) { - let function_debug_context = match *debug_context { - FunctionDebugContext::DebugInfoDisabled => return, - FunctionDebugContext::FunctionWithoutDebugInfo => { - set_debug_location(bx, UnknownLocation); - return; - } - FunctionDebugContext::RegularContext(ref data) => data - }; - - let dbg_loc = if function_debug_context.source_locations_enabled.get() { - debug!("set_source_location: {}", bx.sess().codemap().span_to_string(span)); - let loc = span_start(bx.cx, span); - InternalDebugLocation::new(scope, loc.line, loc.col.to_usize()) - } else { - UnknownLocation - }; - set_debug_location(bx, dbg_loc); -} - -/// Enables emitting source locations for the given functions. -/// -/// Since we don't want source locations to be emitted for the function prelude, -/// they are disabled when beginning to translate a new function. This functions -/// switches source location emitting on and must therefore be called before the -/// first real statement/expression of the function is translated. -pub fn start_emitting_source_locations(dbg_context: &FunctionDebugContext) { - match *dbg_context { - FunctionDebugContext::RegularContext(ref data) => { - data.source_locations_enabled.set(true) - }, - _ => { /* safe to ignore */ } - } -} - - -#[derive(Copy, Clone, PartialEq)] -pub enum InternalDebugLocation { - KnownLocation { scope: DIScope, line: usize, col: usize }, - UnknownLocation -} - -impl InternalDebugLocation { - pub fn new(scope: DIScope, line: usize, col: usize) -> InternalDebugLocation { - KnownLocation { - scope, - line, - col, - } - } -} - -pub fn set_debug_location(bx: &Builder, debug_location: InternalDebugLocation) { - let metadata_node = match debug_location { - KnownLocation { scope, line, .. } => { - // Always set the column to zero like Clang and GCC - let col = UNKNOWN_COLUMN_NUMBER; - debug!("setting debug location to {} {}", line, col); - - unsafe { - llvm::LLVMRustDIBuilderCreateDebugLocation( - debug_context(bx.cx).llcontext, - line as c_uint, - col as c_uint, - scope, - ptr::null_mut()) - } - } - UnknownLocation => { - debug!("clearing debug location "); - ptr::null_mut() - } - }; - - unsafe { - llvm::LLVMSetCurrentDebugLocation(bx.llbuilder, metadata_node); - } -} diff --git a/src/librustc_trans/debuginfo/type_names.rs b/src/librustc_trans/debuginfo/type_names.rs deleted file mode 100644 index 05a74db3a6c..00000000000 --- a/src/librustc_trans/debuginfo/type_names.rs +++ /dev/null @@ -1,224 +0,0 @@ -// Copyright 2015 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -// Type Names for Debug Info. - -use common::CodegenCx; -use rustc::hir::def_id::DefId; -use rustc::ty::subst::Substs; -use rustc::ty::{self, Ty}; - -use rustc::hir; - -// Compute the name of the type as it should be stored in debuginfo. Does not do -// any caching, i.e. calling the function twice with the same type will also do -// the work twice. The `qualified` parameter only affects the first level of the -// type name, further levels (i.e. type parameters) are always fully qualified. -pub fn compute_debuginfo_type_name<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, - t: Ty<'tcx>, - qualified: bool) - -> String { - let mut result = String::with_capacity(64); - push_debuginfo_type_name(cx, t, qualified, &mut result); - result -} - -// Pushes the name of the type as it should be stored in debuginfo on the -// `output` String. See also compute_debuginfo_type_name(). -pub fn push_debuginfo_type_name<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, - t: Ty<'tcx>, - qualified: bool, - output: &mut String) { - // When targeting MSVC, emit C++ style type names for compatibility with - // .natvis visualizers (and perhaps other existing native debuggers?) - let cpp_like_names = cx.sess().target.target.options.is_like_msvc; - - match t.sty { - ty::TyBool => output.push_str("bool"), - ty::TyChar => output.push_str("char"), - ty::TyStr => output.push_str("str"), - ty::TyNever => output.push_str("!"), - ty::TyInt(int_ty) => output.push_str(int_ty.ty_to_string()), - ty::TyUint(uint_ty) => output.push_str(uint_ty.ty_to_string()), - ty::TyFloat(float_ty) => output.push_str(float_ty.ty_to_string()), - ty::TyForeign(def_id) => push_item_name(cx, def_id, qualified, output), - ty::TyAdt(def, substs) => { - push_item_name(cx, def.did, qualified, output); - push_type_params(cx, substs, output); - }, - ty::TyTuple(component_types) => { - output.push('('); - for &component_type in component_types { - push_debuginfo_type_name(cx, component_type, true, output); - output.push_str(", "); - } - if !component_types.is_empty() { - output.pop(); - output.pop(); - } - output.push(')'); - }, - ty::TyRawPtr(ty::TypeAndMut { ty: inner_type, mutbl } ) => { - if !cpp_like_names { - output.push('*'); - } - match mutbl { - hir::MutImmutable => output.push_str("const "), - hir::MutMutable => output.push_str("mut "), - } - - push_debuginfo_type_name(cx, inner_type, true, output); - - if cpp_like_names { - output.push('*'); - } - }, - ty::TyRef(_, inner_type, mutbl) => { - if !cpp_like_names { - output.push('&'); - } - if mutbl == hir::MutMutable { - output.push_str("mut "); - } - - push_debuginfo_type_name(cx, inner_type, true, output); - - if cpp_like_names { - output.push('*'); - } - }, - ty::TyArray(inner_type, len) => { - output.push('['); - push_debuginfo_type_name(cx, inner_type, true, output); - output.push_str(&format!("; {}", len.unwrap_usize(cx.tcx))); - output.push(']'); - }, - ty::TySlice(inner_type) => { - if cpp_like_names { - output.push_str("slice<"); - } else { - output.push('['); - } - - push_debuginfo_type_name(cx, inner_type, true, output); - - if cpp_like_names { - output.push('>'); - } else { - output.push(']'); - } - }, - ty::TyDynamic(ref trait_data, ..) => { - if let Some(principal) = trait_data.principal() { - let principal = cx.tcx.normalize_erasing_late_bound_regions( - ty::ParamEnv::reveal_all(), - &principal, - ); - push_item_name(cx, principal.def_id, false, output); - push_type_params(cx, principal.substs, output); - } - }, - ty::TyFnDef(..) | ty::TyFnPtr(_) => { - let sig = t.fn_sig(cx.tcx); - if sig.unsafety() == hir::Unsafety::Unsafe { - output.push_str("unsafe "); - } - - let abi = sig.abi(); - if abi != ::abi::Abi::Rust { - output.push_str("extern \""); - output.push_str(abi.name()); - output.push_str("\" "); - } - - output.push_str("fn("); - - let sig = cx.tcx.normalize_erasing_late_bound_regions(ty::ParamEnv::reveal_all(), &sig); - if !sig.inputs().is_empty() { - for ¶meter_type in sig.inputs() { - push_debuginfo_type_name(cx, parameter_type, true, output); - output.push_str(", "); - } - output.pop(); - output.pop(); - } - - if sig.variadic { - if !sig.inputs().is_empty() { - output.push_str(", ..."); - } else { - output.push_str("..."); - } - } - - output.push(')'); - - if !sig.output().is_nil() { - output.push_str(" -> "); - push_debuginfo_type_name(cx, sig.output(), true, output); - } - }, - ty::TyClosure(..) => { - output.push_str("closure"); - } - ty::TyGenerator(..) => { - output.push_str("generator"); - } - ty::TyError | - ty::TyInfer(_) | - ty::TyProjection(..) | - ty::TyAnon(..) | - ty::TyGeneratorWitness(..) | - ty::TyParam(_) => { - bug!("debuginfo: Trying to create type name for \ - unexpected type: {:?}", t); - } - } - - fn push_item_name(cx: &CodegenCx, - def_id: DefId, - qualified: bool, - output: &mut String) { - if qualified { - output.push_str(&cx.tcx.crate_name(def_id.krate).as_str()); - for path_element in cx.tcx.def_path(def_id).data { - output.push_str("::"); - output.push_str(&path_element.data.as_interned_str().as_str()); - } - } else { - output.push_str(&cx.tcx.item_name(def_id).as_str()); - } - } - - // Pushes the type parameters in the given `Substs` to the output string. - // This ignores region parameters, since they can't reliably be - // reconstructed for items from non-local crates. For local crates, this - // would be possible but with inlining and LTO we have to use the least - // common denominator - otherwise we would run into conflicts. - fn push_type_params<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, - substs: &Substs<'tcx>, - output: &mut String) { - if substs.types().next().is_none() { - return; - } - - output.push('<'); - - for type_parameter in substs.types() { - push_debuginfo_type_name(cx, type_parameter, true, output); - output.push_str(", "); - } - - output.pop(); - output.pop(); - - output.push('>'); - } -} diff --git a/src/librustc_trans/debuginfo/utils.rs b/src/librustc_trans/debuginfo/utils.rs deleted file mode 100644 index 0a3f06b55f1..00000000000 --- a/src/librustc_trans/debuginfo/utils.rs +++ /dev/null @@ -1,65 +0,0 @@ -// Copyright 2015 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -// Utility Functions. - -use super::{CrateDebugContext}; -use super::namespace::item_namespace; - -use rustc::hir::def_id::DefId; -use rustc::ty::DefIdTree; - -use llvm; -use llvm::debuginfo::{DIScope, DIBuilderRef, DIDescriptor, DIArray}; -use common::{CodegenCx}; - -use syntax_pos::{self, Span}; - -pub fn is_node_local_to_unit(cx: &CodegenCx, def_id: DefId) -> bool -{ - // The is_local_to_unit flag indicates whether a function is local to the - // current compilation unit (i.e. if it is *static* in the C-sense). The - // *reachable* set should provide a good approximation of this, as it - // contains everything that might leak out of the current crate (by being - // externally visible or by being inlined into something externally - // visible). It might better to use the `exported_items` set from - // `driver::CrateAnalysis` in the future, but (atm) this set is not - // available in the translation pass. - !cx.tcx.is_reachable_non_generic(def_id) -} - -#[allow(non_snake_case)] -pub fn create_DIArray(builder: DIBuilderRef, arr: &[DIDescriptor]) -> DIArray { - return unsafe { - llvm::LLVMRustDIBuilderGetOrCreateArray(builder, arr.as_ptr(), arr.len() as u32) - }; -} - -/// Return syntax_pos::Loc corresponding to the beginning of the span -pub fn span_start(cx: &CodegenCx, span: Span) -> syntax_pos::Loc { - cx.sess().codemap().lookup_char_pos(span.lo()) -} - -#[inline] -pub fn debug_context<'a, 'tcx>(cx: &'a CodegenCx<'a, 'tcx>) - -> &'a CrateDebugContext<'tcx> { - cx.dbg_cx.as_ref().unwrap() -} - -#[inline] -#[allow(non_snake_case)] -pub fn DIB(cx: &CodegenCx) -> DIBuilderRef { - cx.dbg_cx.as_ref().unwrap().builder -} - -pub fn get_namespace_for_item(cx: &CodegenCx, def_id: DefId) -> DIScope { - item_namespace(cx, cx.tcx.parent(def_id) - .expect("get_namespace_for_item: missing parent?")) -} diff --git a/src/librustc_trans/declare.rs b/src/librustc_trans/declare.rs deleted file mode 100644 index 97721ffbf06..00000000000 --- a/src/librustc_trans/declare.rs +++ /dev/null @@ -1,223 +0,0 @@ -// Copyright 2012-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. -//! Declare various LLVM values. -//! -//! Prefer using functions and methods from this module rather than calling LLVM -//! functions directly. These functions do some additional work to ensure we do -//! the right thing given the preconceptions of trans. -//! -//! Some useful guidelines: -//! -//! * Use declare_* family of methods if you are declaring, but are not -//! interested in defining the ValueRef they return. -//! * Use define_* family of methods when you might be defining the ValueRef. -//! * When in doubt, define. - -use llvm::{self, ValueRef}; -use llvm::AttributePlace::Function; -use rustc::ty::{self, Ty}; -use rustc::ty::layout::{self, LayoutOf}; -use rustc::session::config::Sanitizer; -use rustc_target::spec::PanicStrategy; -use abi::{Abi, FnType, FnTypeExt}; -use attributes; -use context::CodegenCx; -use common; -use type_::Type; -use value::Value; - -use std::ffi::CString; - - -/// Declare a global value. -/// -/// If there’s a value with the same name already declared, the function will -/// return its ValueRef instead. -pub fn declare_global(cx: &CodegenCx, name: &str, ty: Type) -> llvm::ValueRef { - debug!("declare_global(name={:?})", name); - let namebuf = CString::new(name).unwrap_or_else(|_|{ - bug!("name {:?} contains an interior null byte", name) - }); - unsafe { - llvm::LLVMRustGetOrInsertGlobal(cx.llmod, namebuf.as_ptr(), ty.to_ref()) - } -} - - -/// Declare a function. -/// -/// If there’s a value with the same name already declared, the function will -/// update the declaration and return existing ValueRef instead. -fn declare_raw_fn(cx: &CodegenCx, name: &str, callconv: llvm::CallConv, ty: Type) -> ValueRef { - debug!("declare_raw_fn(name={:?}, ty={:?})", name, ty); - let namebuf = CString::new(name).unwrap_or_else(|_|{ - bug!("name {:?} contains an interior null byte", name) - }); - let llfn = unsafe { - llvm::LLVMRustGetOrInsertFunction(cx.llmod, namebuf.as_ptr(), ty.to_ref()) - }; - - llvm::SetFunctionCallConv(llfn, callconv); - // Function addresses in Rust are never significant, allowing functions to - // be merged. - llvm::SetUnnamedAddr(llfn, true); - - if cx.tcx.sess.opts.cg.no_redzone - .unwrap_or(cx.tcx.sess.target.target.options.disable_redzone) { - llvm::Attribute::NoRedZone.apply_llfn(Function, llfn); - } - - if let Some(ref sanitizer) = cx.tcx.sess.opts.debugging_opts.sanitizer { - match *sanitizer { - Sanitizer::Address => { - llvm::Attribute::SanitizeAddress.apply_llfn(Function, llfn); - }, - Sanitizer::Memory => { - llvm::Attribute::SanitizeMemory.apply_llfn(Function, llfn); - }, - Sanitizer::Thread => { - llvm::Attribute::SanitizeThread.apply_llfn(Function, llfn); - }, - _ => {} - } - } - - match cx.tcx.sess.opts.cg.opt_level.as_ref().map(String::as_ref) { - Some("s") => { - llvm::Attribute::OptimizeForSize.apply_llfn(Function, llfn); - }, - Some("z") => { - llvm::Attribute::MinSize.apply_llfn(Function, llfn); - llvm::Attribute::OptimizeForSize.apply_llfn(Function, llfn); - }, - _ => {}, - } - - if cx.tcx.sess.panic_strategy() != PanicStrategy::Unwind { - attributes::unwind(llfn, false); - } - - llfn -} - - -/// Declare a C ABI function. -/// -/// Only use this for foreign function ABIs and glue. For Rust functions use -/// `declare_fn` instead. -/// -/// If there’s a value with the same name already declared, the function will -/// update the declaration and return existing ValueRef instead. -pub fn declare_cfn(cx: &CodegenCx, name: &str, fn_type: Type) -> ValueRef { - declare_raw_fn(cx, name, llvm::CCallConv, fn_type) -} - - -/// Declare a Rust function. -/// -/// If there’s a value with the same name already declared, the function will -/// update the declaration and return existing ValueRef instead. -pub fn declare_fn<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, name: &str, - fn_type: Ty<'tcx>) -> ValueRef { - debug!("declare_rust_fn(name={:?}, fn_type={:?})", name, fn_type); - let sig = common::ty_fn_sig(cx, fn_type); - let sig = cx.tcx.normalize_erasing_late_bound_regions(ty::ParamEnv::reveal_all(), &sig); - debug!("declare_rust_fn (after region erasure) sig={:?}", sig); - - let fty = FnType::new(cx, sig, &[]); - let llfn = declare_raw_fn(cx, name, fty.llvm_cconv(), fty.llvm_type(cx)); - - if cx.layout_of(sig.output()).abi == layout::Abi::Uninhabited { - llvm::Attribute::NoReturn.apply_llfn(Function, llfn); - } - - if sig.abi != Abi::Rust && sig.abi != Abi::RustCall { - attributes::unwind(llfn, false); - } - - fty.apply_attrs_llfn(llfn); - - llfn -} - - -/// Declare a global with an intention to define it. -/// -/// Use this function when you intend to define a global. This function will -/// return None if the name already has a definition associated with it. In that -/// case an error should be reported to the user, because it usually happens due -/// to user’s fault (e.g. misuse of #[no_mangle] or #[export_name] attributes). -pub fn define_global(cx: &CodegenCx, name: &str, ty: Type) -> Option { - if get_defined_value(cx, name).is_some() { - None - } else { - Some(declare_global(cx, name, ty)) - } -} - -/// Declare a Rust function with an intention to define it. -/// -/// Use this function when you intend to define a function. This function will -/// return panic if the name already has a definition associated with it. This -/// can happen with #[no_mangle] or #[export_name], for example. -pub fn define_fn<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, - name: &str, - fn_type: Ty<'tcx>) -> ValueRef { - if get_defined_value(cx, name).is_some() { - cx.sess().fatal(&format!("symbol `{}` already defined", name)) - } else { - declare_fn(cx, name, fn_type) - } -} - -/// Declare a Rust function with an intention to define it. -/// -/// Use this function when you intend to define a function. This function will -/// return panic if the name already has a definition associated with it. This -/// can happen with #[no_mangle] or #[export_name], for example. -pub fn define_internal_fn<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, - name: &str, - fn_type: Ty<'tcx>) -> ValueRef { - let llfn = define_fn(cx, name, fn_type); - unsafe { llvm::LLVMRustSetLinkage(llfn, llvm::Linkage::InternalLinkage) }; - llfn -} - - -/// Get declared value by name. -pub fn get_declared_value(cx: &CodegenCx, name: &str) -> Option { - debug!("get_declared_value(name={:?})", name); - let namebuf = CString::new(name).unwrap_or_else(|_|{ - bug!("name {:?} contains an interior null byte", name) - }); - let val = unsafe { llvm::LLVMRustGetNamedValue(cx.llmod, namebuf.as_ptr()) }; - if val.is_null() { - debug!("get_declared_value: {:?} value is null", name); - None - } else { - debug!("get_declared_value: {:?} => {:?}", name, Value(val)); - Some(val) - } -} - -/// Get defined or externally defined (AvailableExternally linkage) value by -/// name. -pub fn get_defined_value(cx: &CodegenCx, name: &str) -> Option { - get_declared_value(cx, name).and_then(|val|{ - let declaration = unsafe { - llvm::LLVMIsDeclaration(val) != 0 - }; - if !declaration { - Some(val) - } else { - None - } - }) -} diff --git a/src/librustc_trans/diagnostics.rs b/src/librustc_trans/diagnostics.rs deleted file mode 100644 index 57cc33d09bb..00000000000 --- a/src/librustc_trans/diagnostics.rs +++ /dev/null @@ -1,55 +0,0 @@ -// Copyright 2015 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -#![allow(non_snake_case)] - -register_long_diagnostics! { - -E0511: r##" -Invalid monomorphization of an intrinsic function was used. Erroneous code -example: - -```ignore (error-emitted-at-codegen-which-cannot-be-handled-by-compile_fail) -#![feature(platform_intrinsics)] - -extern "platform-intrinsic" { - fn simd_add(a: T, b: T) -> T; -} - -fn main() { - unsafe { simd_add(0, 1); } - // error: invalid monomorphization of `simd_add` intrinsic -} -``` - -The generic type has to be a SIMD type. Example: - -``` -#![feature(repr_simd)] -#![feature(platform_intrinsics)] - -#[repr(simd)] -#[derive(Copy, Clone)] -struct i32x2(i32, i32); - -extern "platform-intrinsic" { - fn simd_add(a: T, b: T) -> T; -} - -unsafe { simd_add(i32x2(0, 0), i32x2(1, 2)); } // ok! -``` -"##, - -} - - -register_diagnostics! { - E0558 -} diff --git a/src/librustc_trans/glue.rs b/src/librustc_trans/glue.rs deleted file mode 100644 index c7275d09401..00000000000 --- a/src/librustc_trans/glue.rs +++ /dev/null @@ -1,122 +0,0 @@ -// Copyright 2012-2013 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. - -//! -// -// Code relating to drop glue. - -use std; - -use builder::Builder; -use common::*; -use llvm::{ValueRef}; -use llvm; -use meth; -use rustc::ty::layout::LayoutOf; -use rustc::ty::{self, Ty}; -use value::Value; - -pub fn size_and_align_of_dst<'a, 'tcx>(bx: &Builder<'a, 'tcx>, t: Ty<'tcx>, info: ValueRef) - -> (ValueRef, ValueRef) { - debug!("calculate size of DST: {}; with lost info: {:?}", - t, Value(info)); - if bx.cx.type_is_sized(t) { - let (size, align) = bx.cx.size_and_align_of(t); - debug!("size_and_align_of_dst t={} info={:?} size: {:?} align: {:?}", - t, Value(info), size, align); - let size = C_usize(bx.cx, size.bytes()); - let align = C_usize(bx.cx, align.abi()); - return (size, align); - } - assert!(!info.is_null()); - match t.sty { - ty::TyDynamic(..) => { - // load size/align from vtable - (meth::SIZE.get_usize(bx, info), meth::ALIGN.get_usize(bx, info)) - } - ty::TySlice(_) | ty::TyStr => { - let unit = t.sequence_element_type(bx.tcx()); - // The info in this case is the length of the str, so the size is that - // times the unit size. - let (size, align) = bx.cx.size_and_align_of(unit); - (bx.mul(info, C_usize(bx.cx, size.bytes())), - C_usize(bx.cx, align.abi())) - } - _ => { - let cx = bx.cx; - // First get the size of all statically known fields. - // Don't use size_of because it also rounds up to alignment, which we - // want to avoid, as the unsized field's alignment could be smaller. - assert!(!t.is_simd()); - let layout = cx.layout_of(t); - debug!("DST {} layout: {:?}", t, layout); - - let i = layout.fields.count() - 1; - let sized_size = layout.fields.offset(i).bytes(); - let sized_align = layout.align.abi(); - debug!("DST {} statically sized prefix size: {} align: {}", - t, sized_size, sized_align); - let sized_size = C_usize(cx, sized_size); - let sized_align = C_usize(cx, sized_align); - - // Recurse to get the size of the dynamically sized field (must be - // the last field). - let field_ty = layout.field(cx, i).ty; - let (unsized_size, mut unsized_align) = size_and_align_of_dst(bx, field_ty, info); - - // FIXME (#26403, #27023): We should be adding padding - // to `sized_size` (to accommodate the `unsized_align` - // required of the unsized field that follows) before - // summing it with `sized_size`. (Note that since #26403 - // is unfixed, we do not yet add the necessary padding - // here. But this is where the add would go.) - - // Return the sum of sizes and max of aligns. - let size = bx.add(sized_size, unsized_size); - - // Packed types ignore the alignment of their fields. - if let ty::TyAdt(def, _) = t.sty { - if def.repr.packed() { - unsized_align = sized_align; - } - } - - // Choose max of two known alignments (combined value must - // be aligned according to more restrictive of the two). - let align = match (const_to_opt_u128(sized_align, false), - const_to_opt_u128(unsized_align, false)) { - (Some(sized_align), Some(unsized_align)) => { - // If both alignments are constant, (the sized_align should always be), then - // pick the correct alignment statically. - C_usize(cx, std::cmp::max(sized_align, unsized_align) as u64) - } - _ => bx.select(bx.icmp(llvm::IntUGT, sized_align, unsized_align), - sized_align, - unsized_align) - }; - - // Issue #27023: must add any necessary padding to `size` - // (to make it a multiple of `align`) before returning it. - // - // Namely, the returned size should be, in C notation: - // - // `size + ((size & (align-1)) ? align : 0)` - // - // emulated via the semi-standard fast bit trick: - // - // `(size + (align-1)) & -align` - - let addend = bx.sub(align, C_usize(bx.cx, 1)); - let size = bx.and(bx.add(size, addend), bx.neg(align)); - - (size, align) - } - } -} diff --git a/src/librustc_trans/intrinsic.rs b/src/librustc_trans/intrinsic.rs deleted file mode 100644 index 86aa48b6a9e..00000000000 --- a/src/librustc_trans/intrinsic.rs +++ /dev/null @@ -1,1465 +0,0 @@ -// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -#![allow(non_upper_case_globals)] - -use intrinsics::{self, Intrinsic}; -use llvm; -use llvm::{ValueRef}; -use abi::{Abi, FnType, LlvmType, PassMode}; -use mir::place::PlaceRef; -use mir::operand::{OperandRef, OperandValue}; -use base::*; -use common::*; -use declare; -use glue; -use type_::Type; -use type_of::LayoutLlvmExt; -use rustc::ty::{self, Ty}; -use rustc::ty::layout::{HasDataLayout, LayoutOf}; -use rustc::hir; -use syntax::ast; -use syntax::symbol::Symbol; -use builder::Builder; - -use rustc::session::Session; -use syntax_pos::Span; - -use std::cmp::Ordering; -use std::iter; - -fn get_simple_intrinsic(cx: &CodegenCx, name: &str) -> Option { - let llvm_name = match name { - "sqrtf32" => "llvm.sqrt.f32", - "sqrtf64" => "llvm.sqrt.f64", - "powif32" => "llvm.powi.f32", - "powif64" => "llvm.powi.f64", - "sinf32" => "llvm.sin.f32", - "sinf64" => "llvm.sin.f64", - "cosf32" => "llvm.cos.f32", - "cosf64" => "llvm.cos.f64", - "powf32" => "llvm.pow.f32", - "powf64" => "llvm.pow.f64", - "expf32" => "llvm.exp.f32", - "expf64" => "llvm.exp.f64", - "exp2f32" => "llvm.exp2.f32", - "exp2f64" => "llvm.exp2.f64", - "logf32" => "llvm.log.f32", - "logf64" => "llvm.log.f64", - "log10f32" => "llvm.log10.f32", - "log10f64" => "llvm.log10.f64", - "log2f32" => "llvm.log2.f32", - "log2f64" => "llvm.log2.f64", - "fmaf32" => "llvm.fma.f32", - "fmaf64" => "llvm.fma.f64", - "fabsf32" => "llvm.fabs.f32", - "fabsf64" => "llvm.fabs.f64", - "copysignf32" => "llvm.copysign.f32", - "copysignf64" => "llvm.copysign.f64", - "floorf32" => "llvm.floor.f32", - "floorf64" => "llvm.floor.f64", - "ceilf32" => "llvm.ceil.f32", - "ceilf64" => "llvm.ceil.f64", - "truncf32" => "llvm.trunc.f32", - "truncf64" => "llvm.trunc.f64", - "rintf32" => "llvm.rint.f32", - "rintf64" => "llvm.rint.f64", - "nearbyintf32" => "llvm.nearbyint.f32", - "nearbyintf64" => "llvm.nearbyint.f64", - "roundf32" => "llvm.round.f32", - "roundf64" => "llvm.round.f64", - "assume" => "llvm.assume", - "abort" => "llvm.trap", - _ => return None - }; - Some(cx.get_intrinsic(&llvm_name)) -} - -/// Remember to add all intrinsics here, in librustc_typeck/check/mod.rs, -/// and in libcore/intrinsics.rs; if you need access to any llvm intrinsics, -/// add them to librustc_trans/trans/context.rs -pub fn trans_intrinsic_call<'a, 'tcx>(bx: &Builder<'a, 'tcx>, - callee_ty: Ty<'tcx>, - fn_ty: &FnType<'tcx, Ty<'tcx>>, - args: &[OperandRef<'tcx>], - llresult: ValueRef, - span: Span) { - let cx = bx.cx; - let tcx = cx.tcx; - - let (def_id, substs) = match callee_ty.sty { - ty::TyFnDef(def_id, substs) => (def_id, substs), - _ => bug!("expected fn item type, found {}", callee_ty) - }; - - let sig = callee_ty.fn_sig(tcx); - let sig = tcx.normalize_erasing_late_bound_regions(ty::ParamEnv::reveal_all(), &sig); - let arg_tys = sig.inputs(); - let ret_ty = sig.output(); - let name = &*tcx.item_name(def_id).as_str(); - - let llret_ty = cx.layout_of(ret_ty).llvm_type(cx); - let result = PlaceRef::new_sized(llresult, fn_ty.ret.layout, fn_ty.ret.layout.align); - - let simple = get_simple_intrinsic(cx, name); - let llval = match name { - _ if simple.is_some() => { - bx.call(simple.unwrap(), - &args.iter().map(|arg| arg.immediate()).collect::>(), - None) - } - "unreachable" => { - return; - }, - "likely" => { - let expect = cx.get_intrinsic(&("llvm.expect.i1")); - bx.call(expect, &[args[0].immediate(), C_bool(cx, true)], None) - } - "unlikely" => { - let expect = cx.get_intrinsic(&("llvm.expect.i1")); - bx.call(expect, &[args[0].immediate(), C_bool(cx, false)], None) - } - "try" => { - try_intrinsic(bx, cx, - args[0].immediate(), - args[1].immediate(), - args[2].immediate(), - llresult); - return; - } - "breakpoint" => { - let llfn = cx.get_intrinsic(&("llvm.debugtrap")); - bx.call(llfn, &[], None) - } - "size_of" => { - let tp_ty = substs.type_at(0); - C_usize(cx, cx.size_of(tp_ty).bytes()) - } - "size_of_val" => { - let tp_ty = substs.type_at(0); - if let OperandValue::Pair(_, meta) = args[0].val { - let (llsize, _) = - glue::size_and_align_of_dst(bx, tp_ty, meta); - llsize - } else { - C_usize(cx, cx.size_of(tp_ty).bytes()) - } - } - "min_align_of" => { - let tp_ty = substs.type_at(0); - C_usize(cx, cx.align_of(tp_ty).abi()) - } - "min_align_of_val" => { - let tp_ty = substs.type_at(0); - if let OperandValue::Pair(_, meta) = args[0].val { - let (_, llalign) = - glue::size_and_align_of_dst(bx, tp_ty, meta); - llalign - } else { - C_usize(cx, cx.align_of(tp_ty).abi()) - } - } - "pref_align_of" => { - let tp_ty = substs.type_at(0); - C_usize(cx, cx.align_of(tp_ty).pref()) - } - "type_name" => { - let tp_ty = substs.type_at(0); - let ty_name = Symbol::intern(&tp_ty.to_string()).as_str(); - C_str_slice(cx, ty_name) - } - "type_id" => { - C_u64(cx, cx.tcx.type_id_hash(substs.type_at(0))) - } - "init" => { - let ty = substs.type_at(0); - if !cx.layout_of(ty).is_zst() { - // Just zero out the stack slot. - // If we store a zero constant, LLVM will drown in vreg allocation for large data - // structures, and the generated code will be awful. (A telltale sign of this is - // large quantities of `mov [byte ptr foo],0` in the generated code.) - memset_intrinsic(bx, false, ty, llresult, C_u8(cx, 0), C_usize(cx, 1)); - } - return; - } - // Effectively no-ops - "uninit" => { - return; - } - "needs_drop" => { - let tp_ty = substs.type_at(0); - - C_bool(cx, bx.cx.type_needs_drop(tp_ty)) - } - "offset" => { - let ptr = args[0].immediate(); - let offset = args[1].immediate(); - bx.inbounds_gep(ptr, &[offset]) - } - "arith_offset" => { - let ptr = args[0].immediate(); - let offset = args[1].immediate(); - bx.gep(ptr, &[offset]) - } - - "copy_nonoverlapping" => { - copy_intrinsic(bx, false, false, substs.type_at(0), - args[1].immediate(), args[0].immediate(), args[2].immediate()) - } - "copy" => { - copy_intrinsic(bx, true, false, substs.type_at(0), - args[1].immediate(), args[0].immediate(), args[2].immediate()) - } - "write_bytes" => { - memset_intrinsic(bx, false, substs.type_at(0), - args[0].immediate(), args[1].immediate(), args[2].immediate()) - } - - "volatile_copy_nonoverlapping_memory" => { - copy_intrinsic(bx, false, true, substs.type_at(0), - args[0].immediate(), args[1].immediate(), args[2].immediate()) - } - "volatile_copy_memory" => { - copy_intrinsic(bx, true, true, substs.type_at(0), - args[0].immediate(), args[1].immediate(), args[2].immediate()) - } - "volatile_set_memory" => { - memset_intrinsic(bx, true, substs.type_at(0), - args[0].immediate(), args[1].immediate(), args[2].immediate()) - } - "volatile_load" => { - let tp_ty = substs.type_at(0); - let mut ptr = args[0].immediate(); - if let PassMode::Cast(ty) = fn_ty.ret.mode { - ptr = bx.pointercast(ptr, ty.llvm_type(cx).ptr_to()); - } - let load = bx.volatile_load(ptr); - unsafe { - llvm::LLVMSetAlignment(load, cx.align_of(tp_ty).abi() as u32); - } - to_immediate(bx, load, cx.layout_of(tp_ty)) - }, - "volatile_store" => { - let dst = args[0].deref(bx.cx); - args[1].val.volatile_store(bx, dst); - return; - }, - "prefetch_read_data" | "prefetch_write_data" | - "prefetch_read_instruction" | "prefetch_write_instruction" => { - let expect = cx.get_intrinsic(&("llvm.prefetch")); - let (rw, cache_type) = match name { - "prefetch_read_data" => (0, 1), - "prefetch_write_data" => (1, 1), - "prefetch_read_instruction" => (0, 0), - "prefetch_write_instruction" => (1, 0), - _ => bug!() - }; - bx.call(expect, &[ - args[0].immediate(), - C_i32(cx, rw), - args[1].immediate(), - C_i32(cx, cache_type) - ], None) - }, - "ctlz" | "ctlz_nonzero" | "cttz" | "cttz_nonzero" | "ctpop" | "bswap" | - "bitreverse" | "add_with_overflow" | "sub_with_overflow" | - "mul_with_overflow" | "overflowing_add" | "overflowing_sub" | "overflowing_mul" | - "unchecked_div" | "unchecked_rem" | "unchecked_shl" | "unchecked_shr" | "exact_div" => { - let ty = arg_tys[0]; - match int_type_width_signed(ty, cx) { - Some((width, signed)) => - match name { - "ctlz" | "cttz" => { - let y = C_bool(bx.cx, false); - let llfn = cx.get_intrinsic(&format!("llvm.{}.i{}", name, width)); - bx.call(llfn, &[args[0].immediate(), y], None) - } - "ctlz_nonzero" | "cttz_nonzero" => { - let y = C_bool(bx.cx, true); - let llvm_name = &format!("llvm.{}.i{}", &name[..4], width); - let llfn = cx.get_intrinsic(llvm_name); - bx.call(llfn, &[args[0].immediate(), y], None) - } - "ctpop" => bx.call(cx.get_intrinsic(&format!("llvm.ctpop.i{}", width)), - &[args[0].immediate()], None), - "bswap" => { - if width == 8 { - args[0].immediate() // byte swap a u8/i8 is just a no-op - } else { - bx.call(cx.get_intrinsic(&format!("llvm.bswap.i{}", width)), - &[args[0].immediate()], None) - } - } - "bitreverse" => { - bx.call(cx.get_intrinsic(&format!("llvm.bitreverse.i{}", width)), - &[args[0].immediate()], None) - } - "add_with_overflow" | "sub_with_overflow" | "mul_with_overflow" => { - let intrinsic = format!("llvm.{}{}.with.overflow.i{}", - if signed { 's' } else { 'u' }, - &name[..3], width); - let llfn = bx.cx.get_intrinsic(&intrinsic); - - // Convert `i1` to a `bool`, and write it to the out parameter - let pair = bx.call(llfn, &[ - args[0].immediate(), - args[1].immediate() - ], None); - let val = bx.extract_value(pair, 0); - let overflow = bx.zext(bx.extract_value(pair, 1), Type::bool(cx)); - - let dest = result.project_field(bx, 0); - bx.store(val, dest.llval, dest.align); - let dest = result.project_field(bx, 1); - bx.store(overflow, dest.llval, dest.align); - - return; - }, - "overflowing_add" => bx.add(args[0].immediate(), args[1].immediate()), - "overflowing_sub" => bx.sub(args[0].immediate(), args[1].immediate()), - "overflowing_mul" => bx.mul(args[0].immediate(), args[1].immediate()), - "exact_div" => - if signed { - bx.exactsdiv(args[0].immediate(), args[1].immediate()) - } else { - bx.exactudiv(args[0].immediate(), args[1].immediate()) - }, - "unchecked_div" => - if signed { - bx.sdiv(args[0].immediate(), args[1].immediate()) - } else { - bx.udiv(args[0].immediate(), args[1].immediate()) - }, - "unchecked_rem" => - if signed { - bx.srem(args[0].immediate(), args[1].immediate()) - } else { - bx.urem(args[0].immediate(), args[1].immediate()) - }, - "unchecked_shl" => bx.shl(args[0].immediate(), args[1].immediate()), - "unchecked_shr" => - if signed { - bx.ashr(args[0].immediate(), args[1].immediate()) - } else { - bx.lshr(args[0].immediate(), args[1].immediate()) - }, - _ => bug!(), - }, - None => { - span_invalid_monomorphization_error( - tcx.sess, span, - &format!("invalid monomorphization of `{}` intrinsic: \ - expected basic integer type, found `{}`", name, ty)); - return; - } - } - - }, - "fadd_fast" | "fsub_fast" | "fmul_fast" | "fdiv_fast" | "frem_fast" => { - let sty = &arg_tys[0].sty; - match float_type_width(sty) { - Some(_width) => - match name { - "fadd_fast" => bx.fadd_fast(args[0].immediate(), args[1].immediate()), - "fsub_fast" => bx.fsub_fast(args[0].immediate(), args[1].immediate()), - "fmul_fast" => bx.fmul_fast(args[0].immediate(), args[1].immediate()), - "fdiv_fast" => bx.fdiv_fast(args[0].immediate(), args[1].immediate()), - "frem_fast" => bx.frem_fast(args[0].immediate(), args[1].immediate()), - _ => bug!(), - }, - None => { - span_invalid_monomorphization_error( - tcx.sess, span, - &format!("invalid monomorphization of `{}` intrinsic: \ - expected basic float type, found `{}`", name, sty)); - return; - } - } - - }, - - "discriminant_value" => { - args[0].deref(bx.cx).trans_get_discr(bx, ret_ty) - } - - "align_offset" => { - // `ptr as usize` - let ptr_val = bx.ptrtoint(args[0].immediate(), bx.cx.isize_ty); - // `ptr_val % align` - let align = args[1].immediate(); - let offset = bx.urem(ptr_val, align); - let zero = C_null(bx.cx.isize_ty); - // `offset == 0` - let is_zero = bx.icmp(llvm::IntPredicate::IntEQ, offset, zero); - // `if offset == 0 { 0 } else { align - offset }` - bx.select(is_zero, zero, bx.sub(align, offset)) - } - name if name.starts_with("simd_") => { - match generic_simd_intrinsic(bx, name, - callee_ty, - args, - ret_ty, llret_ty, - span) { - Ok(llval) => llval, - Err(()) => return - } - } - // This requires that atomic intrinsics follow a specific naming pattern: - // "atomic_[_]", and no ordering means SeqCst - name if name.starts_with("atomic_") => { - use llvm::AtomicOrdering::*; - - let split: Vec<&str> = name.split('_').collect(); - - let is_cxchg = split[1] == "cxchg" || split[1] == "cxchgweak"; - let (order, failorder) = match split.len() { - 2 => (SequentiallyConsistent, SequentiallyConsistent), - 3 => match split[2] { - "unordered" => (Unordered, Unordered), - "relaxed" => (Monotonic, Monotonic), - "acq" => (Acquire, Acquire), - "rel" => (Release, Monotonic), - "acqrel" => (AcquireRelease, Acquire), - "failrelaxed" if is_cxchg => - (SequentiallyConsistent, Monotonic), - "failacq" if is_cxchg => - (SequentiallyConsistent, Acquire), - _ => cx.sess().fatal("unknown ordering in atomic intrinsic") - }, - 4 => match (split[2], split[3]) { - ("acq", "failrelaxed") if is_cxchg => - (Acquire, Monotonic), - ("acqrel", "failrelaxed") if is_cxchg => - (AcquireRelease, Monotonic), - _ => cx.sess().fatal("unknown ordering in atomic intrinsic") - }, - _ => cx.sess().fatal("Atomic intrinsic not in correct format"), - }; - - let invalid_monomorphization = |ty| { - span_invalid_monomorphization_error(tcx.sess, span, - &format!("invalid monomorphization of `{}` intrinsic: \ - expected basic integer type, found `{}`", name, ty)); - }; - - match split[1] { - "cxchg" | "cxchgweak" => { - let ty = substs.type_at(0); - if int_type_width_signed(ty, cx).is_some() { - let weak = if split[1] == "cxchgweak" { llvm::True } else { llvm::False }; - let pair = bx.atomic_cmpxchg( - args[0].immediate(), - args[1].immediate(), - args[2].immediate(), - order, - failorder, - weak); - let val = bx.extract_value(pair, 0); - let success = bx.zext(bx.extract_value(pair, 1), Type::bool(bx.cx)); - - let dest = result.project_field(bx, 0); - bx.store(val, dest.llval, dest.align); - let dest = result.project_field(bx, 1); - bx.store(success, dest.llval, dest.align); - return; - } else { - return invalid_monomorphization(ty); - } - } - - "load" => { - let ty = substs.type_at(0); - if int_type_width_signed(ty, cx).is_some() { - let align = cx.align_of(ty); - bx.atomic_load(args[0].immediate(), order, align) - } else { - return invalid_monomorphization(ty); - } - } - - "store" => { - let ty = substs.type_at(0); - if int_type_width_signed(ty, cx).is_some() { - let align = cx.align_of(ty); - bx.atomic_store(args[1].immediate(), args[0].immediate(), order, align); - return; - } else { - return invalid_monomorphization(ty); - } - } - - "fence" => { - bx.atomic_fence(order, llvm::SynchronizationScope::CrossThread); - return; - } - - "singlethreadfence" => { - bx.atomic_fence(order, llvm::SynchronizationScope::SingleThread); - return; - } - - // These are all AtomicRMW ops - op => { - let atom_op = match op { - "xchg" => llvm::AtomicXchg, - "xadd" => llvm::AtomicAdd, - "xsub" => llvm::AtomicSub, - "and" => llvm::AtomicAnd, - "nand" => llvm::AtomicNand, - "or" => llvm::AtomicOr, - "xor" => llvm::AtomicXor, - "max" => llvm::AtomicMax, - "min" => llvm::AtomicMin, - "umax" => llvm::AtomicUMax, - "umin" => llvm::AtomicUMin, - _ => cx.sess().fatal("unknown atomic operation") - }; - - let ty = substs.type_at(0); - if int_type_width_signed(ty, cx).is_some() { - bx.atomic_rmw(atom_op, args[0].immediate(), args[1].immediate(), order) - } else { - return invalid_monomorphization(ty); - } - } - } - } - - "nontemporal_store" => { - let dst = args[0].deref(bx.cx); - args[1].val.nontemporal_store(bx, dst); - return; - } - - _ => { - let intr = match Intrinsic::find(&name) { - Some(intr) => intr, - None => bug!("unknown intrinsic '{}'", name), - }; - fn one(x: Vec) -> T { - assert_eq!(x.len(), 1); - x.into_iter().next().unwrap() - } - fn ty_to_type(cx: &CodegenCx, t: &intrinsics::Type) -> Vec { - use intrinsics::Type::*; - match *t { - Void => vec![Type::void(cx)], - Integer(_signed, _width, llvm_width) => { - vec![Type::ix(cx, llvm_width as u64)] - } - Float(x) => { - match x { - 32 => vec![Type::f32(cx)], - 64 => vec![Type::f64(cx)], - _ => bug!() - } - } - Pointer(ref t, ref llvm_elem, _const) => { - let t = llvm_elem.as_ref().unwrap_or(t); - let elem = one(ty_to_type(cx, t)); - vec![elem.ptr_to()] - } - Vector(ref t, ref llvm_elem, length) => { - let t = llvm_elem.as_ref().unwrap_or(t); - let elem = one(ty_to_type(cx, t)); - vec![Type::vector(&elem, length as u64)] - } - Aggregate(false, ref contents) => { - let elems = contents.iter() - .map(|t| one(ty_to_type(cx, t))) - .collect::>(); - vec![Type::struct_(cx, &elems, false)] - } - Aggregate(true, ref contents) => { - contents.iter() - .flat_map(|t| ty_to_type(cx, t)) - .collect() - } - } - } - - // This allows an argument list like `foo, (bar, baz), - // qux` to be converted into `foo, bar, baz, qux`, integer - // arguments to be truncated as needed and pointers to be - // cast. - fn modify_as_needed<'a, 'tcx>(bx: &Builder<'a, 'tcx>, - t: &intrinsics::Type, - arg: &OperandRef<'tcx>) - -> Vec - { - match *t { - intrinsics::Type::Aggregate(true, ref contents) => { - // We found a tuple that needs squishing! So - // run over the tuple and load each field. - // - // This assumes the type is "simple", i.e. no - // destructors, and the contents are SIMD - // etc. - assert!(!bx.cx.type_needs_drop(arg.layout.ty)); - let (ptr, align) = match arg.val { - OperandValue::Ref(ptr, align) => (ptr, align), - _ => bug!() - }; - let arg = PlaceRef::new_sized(ptr, arg.layout, align); - (0..contents.len()).map(|i| { - arg.project_field(bx, i).load(bx).immediate() - }).collect() - } - intrinsics::Type::Pointer(_, Some(ref llvm_elem), _) => { - let llvm_elem = one(ty_to_type(bx.cx, llvm_elem)); - vec![bx.pointercast(arg.immediate(), llvm_elem.ptr_to())] - } - intrinsics::Type::Vector(_, Some(ref llvm_elem), length) => { - let llvm_elem = one(ty_to_type(bx.cx, llvm_elem)); - vec![bx.bitcast(arg.immediate(), Type::vector(&llvm_elem, length as u64))] - } - intrinsics::Type::Integer(_, width, llvm_width) if width != llvm_width => { - // the LLVM intrinsic uses a smaller integer - // size than the C intrinsic's signature, so - // we have to trim it down here. - vec![bx.trunc(arg.immediate(), Type::ix(bx.cx, llvm_width as u64))] - } - _ => vec![arg.immediate()], - } - } - - - let inputs = intr.inputs.iter() - .flat_map(|t| ty_to_type(cx, t)) - .collect::>(); - - let outputs = one(ty_to_type(cx, &intr.output)); - - let llargs: Vec<_> = intr.inputs.iter().zip(args).flat_map(|(t, arg)| { - modify_as_needed(bx, t, arg) - }).collect(); - assert_eq!(inputs.len(), llargs.len()); - - let val = match intr.definition { - intrinsics::IntrinsicDef::Named(name) => { - let f = declare::declare_cfn(cx, - name, - Type::func(&inputs, &outputs)); - bx.call(f, &llargs, None) - } - }; - - match *intr.output { - intrinsics::Type::Aggregate(flatten, ref elems) => { - // the output is a tuple so we need to munge it properly - assert!(!flatten); - - for i in 0..elems.len() { - let dest = result.project_field(bx, i); - let val = bx.extract_value(val, i as u64); - bx.store(val, dest.llval, dest.align); - } - return; - } - _ => val, - } - } - }; - - if !fn_ty.ret.is_ignore() { - if let PassMode::Cast(ty) = fn_ty.ret.mode { - let ptr = bx.pointercast(result.llval, ty.llvm_type(cx).ptr_to()); - bx.store(llval, ptr, result.align); - } else { - OperandRef::from_immediate_or_packed_pair(bx, llval, result.layout) - .val.store(bx, result); - } - } -} - -fn copy_intrinsic<'a, 'tcx>(bx: &Builder<'a, 'tcx>, - allow_overlap: bool, - volatile: bool, - ty: Ty<'tcx>, - dst: ValueRef, - src: ValueRef, - count: ValueRef) - -> ValueRef { - let cx = bx.cx; - let (size, align) = cx.size_and_align_of(ty); - let size = C_usize(cx, size.bytes()); - let align = C_i32(cx, align.abi() as i32); - - let operation = if allow_overlap { - "memmove" - } else { - "memcpy" - }; - - let name = format!("llvm.{}.p0i8.p0i8.i{}", operation, - cx.data_layout().pointer_size.bits()); - - let dst_ptr = bx.pointercast(dst, Type::i8p(cx)); - let src_ptr = bx.pointercast(src, Type::i8p(cx)); - let llfn = cx.get_intrinsic(&name); - - bx.call(llfn, - &[dst_ptr, - src_ptr, - bx.mul(size, count), - align, - C_bool(cx, volatile)], - None) -} - -fn memset_intrinsic<'a, 'tcx>( - bx: &Builder<'a, 'tcx>, - volatile: bool, - ty: Ty<'tcx>, - dst: ValueRef, - val: ValueRef, - count: ValueRef -) -> ValueRef { - let cx = bx.cx; - let (size, align) = cx.size_and_align_of(ty); - let size = C_usize(cx, size.bytes()); - let align = C_i32(cx, align.abi() as i32); - let dst = bx.pointercast(dst, Type::i8p(cx)); - call_memset(bx, dst, val, bx.mul(size, count), align, volatile) -} - -fn try_intrinsic<'a, 'tcx>( - bx: &Builder<'a, 'tcx>, - cx: &CodegenCx, - func: ValueRef, - data: ValueRef, - local_ptr: ValueRef, - dest: ValueRef, -) { - if bx.sess().no_landing_pads() { - bx.call(func, &[data], None); - let ptr_align = bx.tcx().data_layout.pointer_align; - bx.store(C_null(Type::i8p(&bx.cx)), dest, ptr_align); - } else if wants_msvc_seh(bx.sess()) { - trans_msvc_try(bx, cx, func, data, local_ptr, dest); - } else { - trans_gnu_try(bx, cx, func, data, local_ptr, dest); - } -} - -// MSVC's definition of the `rust_try` function. -// -// This implementation uses the new exception handling instructions in LLVM -// which have support in LLVM for SEH on MSVC targets. Although these -// instructions are meant to work for all targets, as of the time of this -// writing, however, LLVM does not recommend the usage of these new instructions -// as the old ones are still more optimized. -fn trans_msvc_try<'a, 'tcx>(bx: &Builder<'a, 'tcx>, - cx: &CodegenCx, - func: ValueRef, - data: ValueRef, - local_ptr: ValueRef, - dest: ValueRef) { - let llfn = get_rust_try_fn(cx, &mut |bx| { - let cx = bx.cx; - - bx.set_personality_fn(bx.cx.eh_personality()); - - let normal = bx.build_sibling_block("normal"); - let catchswitch = bx.build_sibling_block("catchswitch"); - let catchpad = bx.build_sibling_block("catchpad"); - let caught = bx.build_sibling_block("caught"); - - let func = llvm::get_param(bx.llfn(), 0); - let data = llvm::get_param(bx.llfn(), 1); - let local_ptr = llvm::get_param(bx.llfn(), 2); - - // We're generating an IR snippet that looks like: - // - // declare i32 @rust_try(%func, %data, %ptr) { - // %slot = alloca i64* - // invoke %func(%data) to label %normal unwind label %catchswitch - // - // normal: - // ret i32 0 - // - // catchswitch: - // %cs = catchswitch within none [%catchpad] unwind to caller - // - // catchpad: - // %tok = catchpad within %cs [%type_descriptor, 0, %slot] - // %ptr[0] = %slot[0] - // %ptr[1] = %slot[1] - // catchret from %tok to label %caught - // - // caught: - // ret i32 1 - // } - // - // This structure follows the basic usage of throw/try/catch in LLVM. - // For example, compile this C++ snippet to see what LLVM generates: - // - // #include - // - // int bar(void (*foo)(void), uint64_t *ret) { - // try { - // foo(); - // return 0; - // } catch(uint64_t a[2]) { - // ret[0] = a[0]; - // ret[1] = a[1]; - // return 1; - // } - // } - // - // More information can be found in libstd's seh.rs implementation. - let i64p = Type::i64(cx).ptr_to(); - let ptr_align = bx.tcx().data_layout.pointer_align; - let slot = bx.alloca(i64p, "slot", ptr_align); - bx.invoke(func, &[data], normal.llbb(), catchswitch.llbb(), - None); - - normal.ret(C_i32(cx, 0)); - - let cs = catchswitch.catch_switch(None, None, 1); - catchswitch.add_handler(cs, catchpad.llbb()); - - let tcx = cx.tcx; - let tydesc = match tcx.lang_items().msvc_try_filter() { - Some(did) => ::consts::get_static(cx, did), - None => bug!("msvc_try_filter not defined"), - }; - let tok = catchpad.catch_pad(cs, &[tydesc, C_i32(cx, 0), slot]); - let addr = catchpad.load(slot, ptr_align); - - let i64_align = bx.tcx().data_layout.i64_align; - let arg1 = catchpad.load(addr, i64_align); - let val1 = C_i32(cx, 1); - let arg2 = catchpad.load(catchpad.inbounds_gep(addr, &[val1]), i64_align); - let local_ptr = catchpad.bitcast(local_ptr, i64p); - catchpad.store(arg1, local_ptr, i64_align); - catchpad.store(arg2, catchpad.inbounds_gep(local_ptr, &[val1]), i64_align); - catchpad.catch_ret(tok, caught.llbb()); - - caught.ret(C_i32(cx, 1)); - }); - - // Note that no invoke is used here because by definition this function - // can't panic (that's what it's catching). - let ret = bx.call(llfn, &[func, data, local_ptr], None); - let i32_align = bx.tcx().data_layout.i32_align; - bx.store(ret, dest, i32_align); -} - -// Definition of the standard "try" function for Rust using the GNU-like model -// of exceptions (e.g. the normal semantics of LLVM's landingpad and invoke -// instructions). -// -// This translation is a little surprising because we always call a shim -// function instead of inlining the call to `invoke` manually here. This is done -// because in LLVM we're only allowed to have one personality per function -// definition. The call to the `try` intrinsic is being inlined into the -// function calling it, and that function may already have other personality -// functions in play. By calling a shim we're guaranteed that our shim will have -// the right personality function. -fn trans_gnu_try<'a, 'tcx>(bx: &Builder<'a, 'tcx>, - cx: &CodegenCx, - func: ValueRef, - data: ValueRef, - local_ptr: ValueRef, - dest: ValueRef) { - let llfn = get_rust_try_fn(cx, &mut |bx| { - let cx = bx.cx; - - // Translates the shims described above: - // - // bx: - // invoke %func(%args...) normal %normal unwind %catch - // - // normal: - // ret 0 - // - // catch: - // (ptr, _) = landingpad - // store ptr, %local_ptr - // ret 1 - // - // Note that the `local_ptr` data passed into the `try` intrinsic is - // expected to be `*mut *mut u8` for this to actually work, but that's - // managed by the standard library. - - let then = bx.build_sibling_block("then"); - let catch = bx.build_sibling_block("catch"); - - let func = llvm::get_param(bx.llfn(), 0); - let data = llvm::get_param(bx.llfn(), 1); - let local_ptr = llvm::get_param(bx.llfn(), 2); - bx.invoke(func, &[data], then.llbb(), catch.llbb(), None); - then.ret(C_i32(cx, 0)); - - // Type indicator for the exception being thrown. - // - // The first value in this tuple is a pointer to the exception object - // being thrown. The second value is a "selector" indicating which of - // the landing pad clauses the exception's type had been matched to. - // rust_try ignores the selector. - let lpad_ty = Type::struct_(cx, &[Type::i8p(cx), Type::i32(cx)], - false); - let vals = catch.landing_pad(lpad_ty, bx.cx.eh_personality(), 1); - catch.add_clause(vals, C_null(Type::i8p(cx))); - let ptr = catch.extract_value(vals, 0); - let ptr_align = bx.tcx().data_layout.pointer_align; - catch.store(ptr, catch.bitcast(local_ptr, Type::i8p(cx).ptr_to()), ptr_align); - catch.ret(C_i32(cx, 1)); - }); - - // Note that no invoke is used here because by definition this function - // can't panic (that's what it's catching). - let ret = bx.call(llfn, &[func, data, local_ptr], None); - let i32_align = bx.tcx().data_layout.i32_align; - bx.store(ret, dest, i32_align); -} - -// Helper function to give a Block to a closure to translate a shim function. -// This is currently primarily used for the `try` intrinsic functions above. -fn gen_fn<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, - name: &str, - inputs: Vec>, - output: Ty<'tcx>, - trans: &mut for<'b> FnMut(Builder<'b, 'tcx>)) - -> ValueRef { - let rust_fn_ty = cx.tcx.mk_fn_ptr(ty::Binder::bind(cx.tcx.mk_fn_sig( - inputs.into_iter(), - output, - false, - hir::Unsafety::Unsafe, - Abi::Rust - ))); - let llfn = declare::define_internal_fn(cx, name, rust_fn_ty); - let bx = Builder::new_block(cx, llfn, "entry-block"); - trans(bx); - llfn -} - -// Helper function used to get a handle to the `__rust_try` function used to -// catch exceptions. -// -// This function is only generated once and is then cached. -fn get_rust_try_fn<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, - trans: &mut for<'b> FnMut(Builder<'b, 'tcx>)) - -> ValueRef { - if let Some(llfn) = cx.rust_try_fn.get() { - return llfn; - } - - // Define the type up front for the signature of the rust_try function. - let tcx = cx.tcx; - let i8p = tcx.mk_mut_ptr(tcx.types.i8); - let fn_ty = tcx.mk_fn_ptr(ty::Binder::bind(tcx.mk_fn_sig( - iter::once(i8p), - tcx.mk_nil(), - false, - hir::Unsafety::Unsafe, - Abi::Rust - ))); - let output = tcx.types.i32; - let rust_try = gen_fn(cx, "__rust_try", vec![fn_ty, i8p, i8p], output, trans); - cx.rust_try_fn.set(Some(rust_try)); - return rust_try -} - -fn span_invalid_monomorphization_error(a: &Session, b: Span, c: &str) { - span_err!(a, b, E0511, "{}", c); -} - -fn generic_simd_intrinsic<'a, 'tcx>( - bx: &Builder<'a, 'tcx>, - name: &str, - callee_ty: Ty<'tcx>, - args: &[OperandRef<'tcx>], - ret_ty: Ty<'tcx>, - llret_ty: Type, - span: Span -) -> Result { - // macros for error handling: - macro_rules! emit_error { - ($msg: tt) => { - emit_error!($msg, ) - }; - ($msg: tt, $($fmt: tt)*) => { - span_invalid_monomorphization_error( - bx.sess(), span, - &format!(concat!("invalid monomorphization of `{}` intrinsic: ", - $msg), - name, $($fmt)*)); - } - } - macro_rules! return_error { - ($($fmt: tt)*) => { - { - emit_error!($($fmt)*); - return Err(()); - } - } - } - - macro_rules! require { - ($cond: expr, $($fmt: tt)*) => { - if !$cond { - return_error!($($fmt)*); - } - }; - } - macro_rules! require_simd { - ($ty: expr, $position: expr) => { - require!($ty.is_simd(), "expected SIMD {} type, found non-SIMD `{}`", $position, $ty) - } - } - - - - let tcx = bx.tcx(); - let sig = tcx.normalize_erasing_late_bound_regions( - ty::ParamEnv::reveal_all(), - &callee_ty.fn_sig(tcx), - ); - let arg_tys = sig.inputs(); - - // every intrinsic takes a SIMD vector as its first argument - require_simd!(arg_tys[0], "input"); - let in_ty = arg_tys[0]; - let in_elem = arg_tys[0].simd_type(tcx); - let in_len = arg_tys[0].simd_size(tcx); - - let comparison = match name { - "simd_eq" => Some(hir::BiEq), - "simd_ne" => Some(hir::BiNe), - "simd_lt" => Some(hir::BiLt), - "simd_le" => Some(hir::BiLe), - "simd_gt" => Some(hir::BiGt), - "simd_ge" => Some(hir::BiGe), - _ => None - }; - - if let Some(cmp_op) = comparison { - require_simd!(ret_ty, "return"); - - let out_len = ret_ty.simd_size(tcx); - require!(in_len == out_len, - "expected return type with length {} (same as input type `{}`), \ - found `{}` with length {}", - in_len, in_ty, - ret_ty, out_len); - require!(llret_ty.element_type().kind() == llvm::Integer, - "expected return type with integer elements, found `{}` with non-integer `{}`", - ret_ty, - ret_ty.simd_type(tcx)); - - return Ok(compare_simd_types(bx, - args[0].immediate(), - args[1].immediate(), - in_elem, - llret_ty, - cmp_op)) - } - - if name.starts_with("simd_shuffle") { - let n: usize = match name["simd_shuffle".len()..].parse() { - Ok(n) => n, - Err(_) => span_bug!(span, - "bad `simd_shuffle` instruction only caught in trans?") - }; - - require_simd!(ret_ty, "return"); - - let out_len = ret_ty.simd_size(tcx); - require!(out_len == n, - "expected return type of length {}, found `{}` with length {}", - n, ret_ty, out_len); - require!(in_elem == ret_ty.simd_type(tcx), - "expected return element type `{}` (element of input `{}`), \ - found `{}` with element type `{}`", - in_elem, in_ty, - ret_ty, ret_ty.simd_type(tcx)); - - let total_len = in_len as u128 * 2; - - let vector = args[2].immediate(); - - let indices: Option> = (0..n) - .map(|i| { - let arg_idx = i; - let val = const_get_elt(vector, i as u64); - match const_to_opt_u128(val, true) { - None => { - emit_error!("shuffle index #{} is not a constant", arg_idx); - None - } - Some(idx) if idx >= total_len => { - emit_error!("shuffle index #{} is out of bounds (limit {})", - arg_idx, total_len); - None - } - Some(idx) => Some(C_i32(bx.cx, idx as i32)), - } - }) - .collect(); - let indices = match indices { - Some(i) => i, - None => return Ok(C_null(llret_ty)) - }; - - return Ok(bx.shuffle_vector(args[0].immediate(), - args[1].immediate(), - C_vector(&indices))) - } - - if name == "simd_insert" { - require!(in_elem == arg_tys[2], - "expected inserted type `{}` (element of input `{}`), found `{}`", - in_elem, in_ty, arg_tys[2]); - return Ok(bx.insert_element(args[0].immediate(), - args[2].immediate(), - args[1].immediate())) - } - if name == "simd_extract" { - require!(ret_ty == in_elem, - "expected return type `{}` (element of input `{}`), found `{}`", - in_elem, in_ty, ret_ty); - return Ok(bx.extract_element(args[0].immediate(), args[1].immediate())) - } - - if name == "simd_select" { - let m_elem_ty = in_elem; - let m_len = in_len; - let v_len = arg_tys[1].simd_size(tcx); - require!(m_len == v_len, - "mismatched lengths: mask length `{}` != other vector length `{}`", - m_len, v_len - ); - match m_elem_ty.sty { - ty::TyInt(_) => {}, - _ => { - return_error!("mask element type is `{}`, expected `i_`", m_elem_ty); - } - } - // truncate the mask to a vector of i1s - let i1 = Type::i1(bx.cx); - let i1xn = Type::vector(&i1, m_len as u64); - let m_i1s = bx.trunc(args[0].immediate(), i1xn); - return Ok(bx.select(m_i1s, args[1].immediate(), args[2].immediate())); - } - - macro_rules! arith_red { - ($name:tt : $integer_reduce:ident, $float_reduce:ident, $ordered:expr) => { - if name == $name { - require!(ret_ty == in_elem, - "expected return type `{}` (element of input `{}`), found `{}`", - in_elem, in_ty, ret_ty); - return match in_elem.sty { - ty::TyInt(_) | ty::TyUint(_) => { - let r = bx.$integer_reduce(args[0].immediate()); - if $ordered { - // if overflow occurs, the result is the - // mathematical result modulo 2^n: - if name.contains("mul") { - Ok(bx.mul(args[1].immediate(), r)) - } else { - Ok(bx.add(args[1].immediate(), r)) - } - } else { - Ok(bx.$integer_reduce(args[0].immediate())) - } - }, - ty::TyFloat(f) => { - // ordered arithmetic reductions take an accumulator - let acc = if $ordered { - let acc = args[1].immediate(); - // FIXME: https://bugs.llvm.org/show_bug.cgi?id=36734 - // * if the accumulator of the fadd isn't 0, incorrect - // code is generated - // * if the accumulator of the fmul isn't 1, incorrect - // code is generated - match const_get_real(acc) { - None => return_error!("accumulator of {} is not a constant", $name), - Some((v, loses_info)) => { - if $name.contains("mul") && v != 1.0_f64 { - return_error!("accumulator of {} is not 1.0", $name); - } else if $name.contains("add") && v != 0.0_f64 { - return_error!("accumulator of {} is not 0.0", $name); - } else if loses_info { - return_error!("accumulator of {} loses information", $name); - } - } - } - acc - } else { - // unordered arithmetic reductions do not: - match f.bit_width() { - 32 => C_undef(Type::f32(bx.cx)), - 64 => C_undef(Type::f64(bx.cx)), - v => { - return_error!(r#" -unsupported {} from `{}` with element `{}` of size `{}` to `{}`"#, - $name, in_ty, in_elem, v, ret_ty - ) - } - } - - }; - Ok(bx.$float_reduce(acc, args[0].immediate())) - } - _ => { - return_error!( - "unsupported {} from `{}` with element `{}` to `{}`", - $name, in_ty, in_elem, ret_ty - ) - }, - } - } - } - } - - arith_red!("simd_reduce_add_ordered": vector_reduce_add, vector_reduce_fadd_fast, true); - arith_red!("simd_reduce_mul_ordered": vector_reduce_mul, vector_reduce_fmul_fast, true); - arith_red!("simd_reduce_add_unordered": vector_reduce_add, vector_reduce_fadd_fast, false); - arith_red!("simd_reduce_mul_unordered": vector_reduce_mul, vector_reduce_fmul_fast, false); - - macro_rules! minmax_red { - ($name:tt: $int_red:ident, $float_red:ident) => { - if name == $name { - require!(ret_ty == in_elem, - "expected return type `{}` (element of input `{}`), found `{}`", - in_elem, in_ty, ret_ty); - return match in_elem.sty { - ty::TyInt(_i) => { - Ok(bx.$int_red(args[0].immediate(), true)) - }, - ty::TyUint(_u) => { - Ok(bx.$int_red(args[0].immediate(), false)) - }, - ty::TyFloat(_f) => { - Ok(bx.$float_red(args[0].immediate())) - } - _ => { - return_error!("unsupported {} from `{}` with element `{}` to `{}`", - $name, in_ty, in_elem, ret_ty) - }, - } - } - - } - } - - minmax_red!("simd_reduce_min": vector_reduce_min, vector_reduce_fmin); - minmax_red!("simd_reduce_max": vector_reduce_max, vector_reduce_fmax); - - minmax_red!("simd_reduce_min_nanless": vector_reduce_min, vector_reduce_fmin_fast); - minmax_red!("simd_reduce_max_nanless": vector_reduce_max, vector_reduce_fmax_fast); - - macro_rules! bitwise_red { - ($name:tt : $red:ident, $boolean:expr) => { - if name == $name { - let input = if !$boolean { - require!(ret_ty == in_elem, - "expected return type `{}` (element of input `{}`), found `{}`", - in_elem, in_ty, ret_ty); - args[0].immediate() - } else { - match in_elem.sty { - ty::TyInt(_) | ty::TyUint(_) => {}, - _ => { - return_error!("unsupported {} from `{}` with element `{}` to `{}`", - $name, in_ty, in_elem, ret_ty) - } - } - - // boolean reductions operate on vectors of i1s: - let i1 = Type::i1(bx.cx); - let i1xn = Type::vector(&i1, in_len as u64); - bx.trunc(args[0].immediate(), i1xn) - }; - return match in_elem.sty { - ty::TyInt(_) | ty::TyUint(_) => { - let r = bx.$red(input); - Ok( - if !$boolean { - r - } else { - bx.zext(r, Type::bool(bx.cx)) - } - ) - }, - _ => { - return_error!("unsupported {} from `{}` with element `{}` to `{}`", - $name, in_ty, in_elem, ret_ty) - }, - } - } - } - } - - bitwise_red!("simd_reduce_and": vector_reduce_and, false); - bitwise_red!("simd_reduce_or": vector_reduce_or, false); - bitwise_red!("simd_reduce_xor": vector_reduce_xor, false); - bitwise_red!("simd_reduce_all": vector_reduce_and, true); - bitwise_red!("simd_reduce_any": vector_reduce_or, true); - - if name == "simd_cast" { - require_simd!(ret_ty, "return"); - let out_len = ret_ty.simd_size(tcx); - require!(in_len == out_len, - "expected return type with length {} (same as input type `{}`), \ - found `{}` with length {}", - in_len, in_ty, - ret_ty, out_len); - // casting cares about nominal type, not just structural type - let out_elem = ret_ty.simd_type(tcx); - - if in_elem == out_elem { return Ok(args[0].immediate()); } - - enum Style { Float, Int(/* is signed? */ bool), Unsupported } - - let (in_style, in_width) = match in_elem.sty { - // vectors of pointer-sized integers should've been - // disallowed before here, so this unwrap is safe. - ty::TyInt(i) => (Style::Int(true), i.bit_width().unwrap()), - ty::TyUint(u) => (Style::Int(false), u.bit_width().unwrap()), - ty::TyFloat(f) => (Style::Float, f.bit_width()), - _ => (Style::Unsupported, 0) - }; - let (out_style, out_width) = match out_elem.sty { - ty::TyInt(i) => (Style::Int(true), i.bit_width().unwrap()), - ty::TyUint(u) => (Style::Int(false), u.bit_width().unwrap()), - ty::TyFloat(f) => (Style::Float, f.bit_width()), - _ => (Style::Unsupported, 0) - }; - - match (in_style, out_style) { - (Style::Int(in_is_signed), Style::Int(_)) => { - return Ok(match in_width.cmp(&out_width) { - Ordering::Greater => bx.trunc(args[0].immediate(), llret_ty), - Ordering::Equal => args[0].immediate(), - Ordering::Less => if in_is_signed { - bx.sext(args[0].immediate(), llret_ty) - } else { - bx.zext(args[0].immediate(), llret_ty) - } - }) - } - (Style::Int(in_is_signed), Style::Float) => { - return Ok(if in_is_signed { - bx.sitofp(args[0].immediate(), llret_ty) - } else { - bx.uitofp(args[0].immediate(), llret_ty) - }) - } - (Style::Float, Style::Int(out_is_signed)) => { - return Ok(if out_is_signed { - bx.fptosi(args[0].immediate(), llret_ty) - } else { - bx.fptoui(args[0].immediate(), llret_ty) - }) - } - (Style::Float, Style::Float) => { - return Ok(match in_width.cmp(&out_width) { - Ordering::Greater => bx.fptrunc(args[0].immediate(), llret_ty), - Ordering::Equal => args[0].immediate(), - Ordering::Less => bx.fpext(args[0].immediate(), llret_ty) - }) - } - _ => {/* Unsupported. Fallthrough. */} - } - require!(false, - "unsupported cast from `{}` with element `{}` to `{}` with element `{}`", - in_ty, in_elem, - ret_ty, out_elem); - } - macro_rules! arith { - ($($name: ident: $($($p: ident),* => $call: ident),*;)*) => { - $(if name == stringify!($name) { - match in_elem.sty { - $($(ty::$p(_))|* => { - return Ok(bx.$call(args[0].immediate(), args[1].immediate())) - })* - _ => {}, - } - require!(false, - "unsupported operation on `{}` with element `{}`", - in_ty, - in_elem) - })* - } - } - arith! { - simd_add: TyUint, TyInt => add, TyFloat => fadd; - simd_sub: TyUint, TyInt => sub, TyFloat => fsub; - simd_mul: TyUint, TyInt => mul, TyFloat => fmul; - simd_div: TyUint => udiv, TyInt => sdiv, TyFloat => fdiv; - simd_rem: TyUint => urem, TyInt => srem, TyFloat => frem; - simd_shl: TyUint, TyInt => shl; - simd_shr: TyUint => lshr, TyInt => ashr; - simd_and: TyUint, TyInt => and; - simd_or: TyUint, TyInt => or; - simd_xor: TyUint, TyInt => xor; - simd_fmax: TyFloat => maxnum; - simd_fmin: TyFloat => minnum; - } - span_bug!(span, "unknown SIMD intrinsic"); -} - -// Returns the width of an int Ty, and if it's signed or not -// Returns None if the type is not an integer -// FIXME: there’s multiple of this functions, investigate using some of the already existing -// stuffs. -fn int_type_width_signed(ty: Ty, cx: &CodegenCx) -> Option<(u64, bool)> { - match ty.sty { - ty::TyInt(t) => Some((match t { - ast::IntTy::Isize => { - match &cx.tcx.sess.target.target.target_pointer_width[..] { - "16" => 16, - "32" => 32, - "64" => 64, - tws => bug!("Unsupported target word size for isize: {}", tws), - } - }, - ast::IntTy::I8 => 8, - ast::IntTy::I16 => 16, - ast::IntTy::I32 => 32, - ast::IntTy::I64 => 64, - ast::IntTy::I128 => 128, - }, true)), - ty::TyUint(t) => Some((match t { - ast::UintTy::Usize => { - match &cx.tcx.sess.target.target.target_pointer_width[..] { - "16" => 16, - "32" => 32, - "64" => 64, - tws => bug!("Unsupported target word size for usize: {}", tws), - } - }, - ast::UintTy::U8 => 8, - ast::UintTy::U16 => 16, - ast::UintTy::U32 => 32, - ast::UintTy::U64 => 64, - ast::UintTy::U128 => 128, - }, false)), - _ => None, - } -} - -// Returns the width of a float TypeVariant -// Returns None if the type is not a float -fn float_type_width<'tcx>(sty: &ty::TypeVariants<'tcx>) - -> Option { - use rustc::ty::TyFloat; - match *sty { - TyFloat(t) => Some(match t { - ast::FloatTy::F32 => 32, - ast::FloatTy::F64 => 64, - }), - _ => None, - } -} diff --git a/src/librustc_trans/lib.rs b/src/librustc_trans/lib.rs deleted file mode 100644 index 6db95657ce0..00000000000 --- a/src/librustc_trans/lib.rs +++ /dev/null @@ -1,390 +0,0 @@ -// Copyright 2012-2013 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. - -//! The Rust compiler. -//! -//! # Note -//! -//! This API is completely unstable and subject to change. - -#![doc(html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png", - html_favicon_url = "https://doc.rust-lang.org/favicon.ico", - html_root_url = "https://doc.rust-lang.org/nightly/")] - -#![feature(box_patterns)] -#![feature(box_syntax)] -#![feature(custom_attribute)] -#![feature(fs_read_write)] -#![allow(unused_attributes)] -#![feature(libc)] -#![feature(quote)] -#![feature(range_contains)] -#![feature(rustc_diagnostic_macros)] -#![feature(slice_sort_by_cached_key)] -#![feature(optin_builtin_traits)] -#![feature(inclusive_range_methods)] - -use rustc::dep_graph::WorkProduct; -use syntax_pos::symbol::Symbol; - -#[macro_use] extern crate bitflags; -extern crate flate2; -extern crate libc; -#[macro_use] extern crate rustc; -extern crate jobserver; -extern crate num_cpus; -extern crate rustc_mir; -extern crate rustc_allocator; -extern crate rustc_apfloat; -extern crate rustc_target; -#[macro_use] extern crate rustc_data_structures; -extern crate rustc_demangle; -extern crate rustc_incremental; -extern crate rustc_llvm as llvm; -extern crate rustc_platform_intrinsics as intrinsics; -extern crate rustc_trans_utils; - -#[macro_use] extern crate log; -#[macro_use] extern crate syntax; -extern crate syntax_pos; -extern crate rustc_errors as errors; -extern crate serialize; -extern crate cc; // Used to locate MSVC -extern crate tempdir; - -use back::bytecode::RLIB_BYTECODE_EXTENSION; - -pub use llvm_util::target_features; - -use std::any::Any; -use std::path::PathBuf; -use std::sync::mpsc; -use std::collections::BTreeMap; -use rustc_data_structures::sync::Lrc; - -use rustc::dep_graph::DepGraph; -use rustc::hir::def_id::CrateNum; -use rustc::middle::cstore::MetadataLoader; -use rustc::middle::cstore::{NativeLibrary, CrateSource, LibSource}; -use rustc::middle::lang_items::LangItem; -use rustc::session::{Session, CompileIncomplete}; -use rustc::session::config::{OutputFilenames, OutputType, PrintRequest}; -use rustc::ty::{self, TyCtxt}; -use rustc::util::nodemap::{FxHashSet, FxHashMap}; -use rustc_mir::monomorphize; -use rustc_trans_utils::trans_crate::TransCrate; - -mod diagnostics; - -mod back { - pub use rustc_trans_utils::symbol_names; - mod archive; - pub mod bytecode; - mod command; - pub mod linker; - pub mod link; - mod lto; - pub mod symbol_export; - pub mod write; - mod rpath; - mod wasm; -} - -mod abi; -mod allocator; -mod asm; -mod attributes; -mod base; -mod builder; -mod callee; -mod common; -mod consts; -mod context; -mod debuginfo; -mod declare; -mod glue; -mod intrinsic; -mod llvm_util; -mod metadata; -mod meth; -mod mir; -mod time_graph; -mod trans_item; -mod type_; -mod type_of; -mod value; - -pub struct LlvmTransCrate(()); - -impl !Send for LlvmTransCrate {} // Llvm is on a per-thread basis -impl !Sync for LlvmTransCrate {} - -impl LlvmTransCrate { - pub fn new() -> Box { - box LlvmTransCrate(()) - } -} - -impl TransCrate for LlvmTransCrate { - fn init(&self, sess: &Session) { - llvm_util::init(sess); // Make sure llvm is inited - } - - fn print(&self, req: PrintRequest, sess: &Session) { - match req { - PrintRequest::RelocationModels => { - println!("Available relocation models:"); - for &(name, _) in back::write::RELOC_MODEL_ARGS.iter() { - println!(" {}", name); - } - println!(""); - } - PrintRequest::CodeModels => { - println!("Available code models:"); - for &(name, _) in back::write::CODE_GEN_MODEL_ARGS.iter(){ - println!(" {}", name); - } - println!(""); - } - PrintRequest::TlsModels => { - println!("Available TLS models:"); - for &(name, _) in back::write::TLS_MODEL_ARGS.iter(){ - println!(" {}", name); - } - println!(""); - } - req => llvm_util::print(req, sess), - } - } - - fn print_passes(&self) { - llvm_util::print_passes(); - } - - fn print_version(&self) { - llvm_util::print_version(); - } - - fn diagnostics(&self) -> &[(&'static str, &'static str)] { - &DIAGNOSTICS - } - - fn target_features(&self, sess: &Session) -> Vec { - target_features(sess) - } - - fn metadata_loader(&self) -> Box { - box metadata::LlvmMetadataLoader - } - - fn provide(&self, providers: &mut ty::maps::Providers) { - back::symbol_names::provide(providers); - back::symbol_export::provide(providers); - base::provide(providers); - attributes::provide(providers); - } - - fn provide_extern(&self, providers: &mut ty::maps::Providers) { - back::symbol_export::provide_extern(providers); - base::provide_extern(providers); - attributes::provide_extern(providers); - } - - fn trans_crate<'a, 'tcx>( - &self, - tcx: TyCtxt<'a, 'tcx, 'tcx>, - rx: mpsc::Receiver> - ) -> Box { - box base::trans_crate(tcx, rx) - } - - fn join_trans_and_link( - &self, - trans: Box, - sess: &Session, - dep_graph: &DepGraph, - outputs: &OutputFilenames, - ) -> Result<(), CompileIncomplete>{ - use rustc::util::common::time; - let (trans, work_products) = trans.downcast::<::back::write::OngoingCrateTranslation>() - .expect("Expected LlvmTransCrate's OngoingCrateTranslation, found Box") - .join(sess); - if sess.opts.debugging_opts.incremental_info { - back::write::dump_incremental_data(&trans); - } - - time(sess, - "serialize work products", - move || rustc_incremental::save_work_product_index(sess, &dep_graph, work_products)); - - sess.compile_status()?; - - if !sess.opts.output_types.keys().any(|&i| i == OutputType::Exe || - i == OutputType::Metadata) { - return Ok(()); - } - - // Run the linker on any artifacts that resulted from the LLVM run. - // This should produce either a finished executable or library. - time(sess, "linking", || { - back::link::link_binary(sess, &trans, outputs, &trans.crate_name.as_str()); - }); - - // Now that we won't touch anything in the incremental compilation directory - // any more, we can finalize it (which involves renaming it) - rustc_incremental::finalize_session_directory(sess, trans.link.crate_hash); - - Ok(()) - } -} - -/// This is the entrypoint for a hot plugged rustc_trans -#[no_mangle] -pub fn __rustc_codegen_backend() -> Box { - LlvmTransCrate::new() -} - -struct ModuleTranslation { - /// The name of the module. When the crate may be saved between - /// compilations, incremental compilation requires that name be - /// unique amongst **all** crates. Therefore, it should contain - /// something unique to this crate (e.g., a module path) as well - /// as the crate name and disambiguator. - name: String, - llmod_id: String, - source: ModuleSource, - kind: ModuleKind, -} - -#[derive(Copy, Clone, Debug, PartialEq)] -enum ModuleKind { - Regular, - Metadata, - Allocator, -} - -impl ModuleTranslation { - fn llvm(&self) -> Option<&ModuleLlvm> { - match self.source { - ModuleSource::Translated(ref llvm) => Some(llvm), - ModuleSource::Preexisting(_) => None, - } - } - - fn into_compiled_module(self, - emit_obj: bool, - emit_bc: bool, - emit_bc_compressed: bool, - outputs: &OutputFilenames) -> CompiledModule { - let pre_existing = match self.source { - ModuleSource::Preexisting(_) => true, - ModuleSource::Translated(_) => false, - }; - let object = if emit_obj { - Some(outputs.temp_path(OutputType::Object, Some(&self.name))) - } else { - None - }; - let bytecode = if emit_bc { - Some(outputs.temp_path(OutputType::Bitcode, Some(&self.name))) - } else { - None - }; - let bytecode_compressed = if emit_bc_compressed { - Some(outputs.temp_path(OutputType::Bitcode, Some(&self.name)) - .with_extension(RLIB_BYTECODE_EXTENSION)) - } else { - None - }; - - CompiledModule { - llmod_id: self.llmod_id, - name: self.name.clone(), - kind: self.kind, - pre_existing, - object, - bytecode, - bytecode_compressed, - } - } -} - -#[derive(Debug)] -struct CompiledModule { - name: String, - llmod_id: String, - kind: ModuleKind, - pre_existing: bool, - object: Option, - bytecode: Option, - bytecode_compressed: Option, -} - -enum ModuleSource { - /// Copy the `.o` files or whatever from the incr. comp. directory. - Preexisting(WorkProduct), - - /// Rebuild from this LLVM module. - Translated(ModuleLlvm), -} - -#[derive(Debug)] -struct ModuleLlvm { - llcx: llvm::ContextRef, - llmod: llvm::ModuleRef, - tm: llvm::TargetMachineRef, -} - -unsafe impl Send for ModuleLlvm { } -unsafe impl Sync for ModuleLlvm { } - -impl Drop for ModuleLlvm { - fn drop(&mut self) { - unsafe { - llvm::LLVMDisposeModule(self.llmod); - llvm::LLVMContextDispose(self.llcx); - llvm::LLVMRustDisposeTargetMachine(self.tm); - } - } -} - -struct CrateTranslation { - crate_name: Symbol, - modules: Vec, - allocator_module: Option, - metadata_module: CompiledModule, - link: rustc::middle::cstore::LinkMeta, - metadata: rustc::middle::cstore::EncodedMetadata, - windows_subsystem: Option, - linker_info: back::linker::LinkerInfo, - crate_info: CrateInfo, -} - -// Misc info we load from metadata to persist beyond the tcx -struct CrateInfo { - panic_runtime: Option, - compiler_builtins: Option, - profiler_runtime: Option, - sanitizer_runtime: Option, - is_no_builtins: FxHashSet, - native_libraries: FxHashMap>>, - crate_name: FxHashMap, - used_libraries: Lrc>, - link_args: Lrc>, - used_crate_source: FxHashMap>, - used_crates_static: Vec<(CrateNum, LibSource)>, - used_crates_dynamic: Vec<(CrateNum, LibSource)>, - wasm_custom_sections: BTreeMap>, - wasm_imports: FxHashMap, - lang_item_to_crate: FxHashMap, - missing_lang_items: FxHashMap>, -} - -__build_diagnostic_array! { librustc_trans, DIAGNOSTICS } diff --git a/src/librustc_trans/llvm_util.rs b/src/librustc_trans/llvm_util.rs deleted file mode 100644 index bbd1c39a19e..00000000000 --- a/src/librustc_trans/llvm_util.rs +++ /dev/null @@ -1,255 +0,0 @@ -// Copyright 2017 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -use syntax_pos::symbol::Symbol; -use back::write::create_target_machine; -use llvm; -use rustc::session::Session; -use rustc::session::config::PrintRequest; -use libc::c_int; -use std::ffi::CString; -use syntax::feature_gate::UnstableFeatures; - -use std::sync::atomic::{AtomicBool, Ordering}; -use std::sync::Once; - -static POISONED: AtomicBool = AtomicBool::new(false); -static INIT: Once = Once::new(); - -pub(crate) fn init(sess: &Session) { - unsafe { - // Before we touch LLVM, make sure that multithreading is enabled. - INIT.call_once(|| { - if llvm::LLVMStartMultithreaded() != 1 { - // use an extra bool to make sure that all future usage of LLVM - // cannot proceed despite the Once not running more than once. - POISONED.store(true, Ordering::SeqCst); - } - - configure_llvm(sess); - }); - - if POISONED.load(Ordering::SeqCst) { - bug!("couldn't enable multi-threaded LLVM"); - } - } -} - -fn require_inited() { - INIT.call_once(|| bug!("llvm is not initialized")); - if POISONED.load(Ordering::SeqCst) { - bug!("couldn't enable multi-threaded LLVM"); - } -} - -unsafe fn configure_llvm(sess: &Session) { - let mut llvm_c_strs = Vec::new(); - let mut llvm_args = Vec::new(); - - { - let mut add = |arg: &str| { - let s = CString::new(arg).unwrap(); - llvm_args.push(s.as_ptr()); - llvm_c_strs.push(s); - }; - add("rustc"); // fake program name - if sess.time_llvm_passes() { add("-time-passes"); } - if sess.print_llvm_passes() { add("-debug-pass=Structure"); } - if sess.opts.debugging_opts.disable_instrumentation_preinliner { - add("-disable-preinline"); - } - - for arg in &sess.opts.cg.llvm_args { - add(&(*arg)); - } - } - - llvm::LLVMInitializePasses(); - - llvm::initialize_available_targets(); - - llvm::LLVMRustSetLLVMOptions(llvm_args.len() as c_int, - llvm_args.as_ptr()); -} - -// WARNING: the features after applying `to_llvm_feature` must be known -// to LLVM or the feature detection code will walk past the end of the feature -// array, leading to crashes. - -const ARM_WHITELIST: &[(&str, Option<&str>)] = &[ - ("neon", Some("arm_target_feature")), - ("v7", Some("arm_target_feature")), - ("vfp2", Some("arm_target_feature")), - ("vfp3", Some("arm_target_feature")), - ("vfp4", Some("arm_target_feature")), -]; - -const AARCH64_WHITELIST: &[(&str, Option<&str>)] = &[ - ("fp", Some("aarch64_target_feature")), - ("neon", Some("aarch64_target_feature")), - ("sve", Some("aarch64_target_feature")), - ("crc", Some("aarch64_target_feature")), - ("crypto", Some("aarch64_target_feature")), - ("ras", Some("aarch64_target_feature")), - ("lse", Some("aarch64_target_feature")), - ("rdm", Some("aarch64_target_feature")), - ("fp16", Some("aarch64_target_feature")), - ("rcpc", Some("aarch64_target_feature")), - ("dotprod", Some("aarch64_target_feature")), - ("v8.1a", Some("aarch64_target_feature")), - ("v8.2a", Some("aarch64_target_feature")), - ("v8.3a", Some("aarch64_target_feature")), -]; - -const X86_WHITELIST: &[(&str, Option<&str>)] = &[ - ("aes", None), - ("avx", None), - ("avx2", None), - ("avx512bw", Some("avx512_target_feature")), - ("avx512cd", Some("avx512_target_feature")), - ("avx512dq", Some("avx512_target_feature")), - ("avx512er", Some("avx512_target_feature")), - ("avx512f", Some("avx512_target_feature")), - ("avx512ifma", Some("avx512_target_feature")), - ("avx512pf", Some("avx512_target_feature")), - ("avx512vbmi", Some("avx512_target_feature")), - ("avx512vl", Some("avx512_target_feature")), - ("avx512vpopcntdq", Some("avx512_target_feature")), - ("bmi1", None), - ("bmi2", None), - ("fma", None), - ("fxsr", None), - ("lzcnt", None), - ("mmx", Some("mmx_target_feature")), - ("pclmulqdq", None), - ("popcnt", None), - ("rdrand", None), - ("rdseed", None), - ("sha", None), - ("sse", None), - ("sse2", None), - ("sse3", None), - ("sse4.1", None), - ("sse4.2", None), - ("sse4a", Some("sse4a_target_feature")), - ("ssse3", None), - ("tbm", Some("tbm_target_feature")), - ("xsave", None), - ("xsavec", None), - ("xsaveopt", None), - ("xsaves", None), -]; - -const HEXAGON_WHITELIST: &[(&str, Option<&str>)] = &[ - ("hvx", Some("hexagon_target_feature")), - ("hvx-double", Some("hexagon_target_feature")), -]; - -const POWERPC_WHITELIST: &[(&str, Option<&str>)] = &[ - ("altivec", Some("powerpc_target_feature")), - ("power8-altivec", Some("powerpc_target_feature")), - ("power9-altivec", Some("powerpc_target_feature")), - ("power8-vector", Some("powerpc_target_feature")), - ("power9-vector", Some("powerpc_target_feature")), - ("vsx", Some("powerpc_target_feature")), -]; - -const MIPS_WHITELIST: &[(&str, Option<&str>)] = &[ - ("fp64", Some("mips_target_feature")), - ("msa", Some("mips_target_feature")), -]; - -/// When rustdoc is running, provide a list of all known features so that all their respective -/// primtives may be documented. -/// -/// IMPORTANT: If you're adding another whitelist to the above lists, make sure to add it to this -/// iterator! -pub fn all_known_features() -> impl Iterator)> { - ARM_WHITELIST.iter().cloned() - .chain(AARCH64_WHITELIST.iter().cloned()) - .chain(X86_WHITELIST.iter().cloned()) - .chain(HEXAGON_WHITELIST.iter().cloned()) - .chain(POWERPC_WHITELIST.iter().cloned()) - .chain(MIPS_WHITELIST.iter().cloned()) -} - -pub fn to_llvm_feature<'a>(sess: &Session, s: &'a str) -> &'a str { - let arch = if sess.target.target.arch == "x86_64" { - "x86" - } else { - &*sess.target.target.arch - }; - match (arch, s) { - ("x86", "pclmulqdq") => "pclmul", - ("x86", "rdrand") => "rdrnd", - ("x86", "bmi1") => "bmi", - ("aarch64", "fp") => "fp-armv8", - ("aarch64", "fp16") => "fullfp16", - (_, s) => s, - } -} - -pub fn target_features(sess: &Session) -> Vec { - let target_machine = create_target_machine(sess, true); - target_feature_whitelist(sess) - .iter() - .filter_map(|&(feature, gate)| { - if UnstableFeatures::from_environment().is_nightly_build() || gate.is_none() { - Some(feature) - } else { - None - } - }) - .filter(|feature| { - let llvm_feature = to_llvm_feature(sess, feature); - let cstr = CString::new(llvm_feature).unwrap(); - unsafe { llvm::LLVMRustHasFeature(target_machine, cstr.as_ptr()) } - }) - .map(|feature| Symbol::intern(feature)).collect() -} - -pub fn target_feature_whitelist(sess: &Session) - -> &'static [(&'static str, Option<&'static str>)] -{ - match &*sess.target.target.arch { - "arm" => ARM_WHITELIST, - "aarch64" => AARCH64_WHITELIST, - "x86" | "x86_64" => X86_WHITELIST, - "hexagon" => HEXAGON_WHITELIST, - "mips" | "mips64" => MIPS_WHITELIST, - "powerpc" | "powerpc64" => POWERPC_WHITELIST, - _ => &[], - } -} - -pub fn print_version() { - // Can be called without initializing LLVM - unsafe { - println!("LLVM version: {}.{}", - llvm::LLVMRustVersionMajor(), llvm::LLVMRustVersionMinor()); - } -} - -pub fn print_passes() { - // Can be called without initializing LLVM - unsafe { llvm::LLVMRustPrintPasses(); } -} - -pub(crate) fn print(req: PrintRequest, sess: &Session) { - require_inited(); - let tm = create_target_machine(sess, true); - unsafe { - match req { - PrintRequest::TargetCPUs => llvm::LLVMRustPrintTargetCPUs(tm), - PrintRequest::TargetFeatures => llvm::LLVMRustPrintTargetFeatures(tm), - _ => bug!("rustc_trans can't handle print request: {:?}", req), - } - } -} diff --git a/src/librustc_trans/metadata.rs b/src/librustc_trans/metadata.rs deleted file mode 100644 index 144baa65c1b..00000000000 --- a/src/librustc_trans/metadata.rs +++ /dev/null @@ -1,124 +0,0 @@ -// Copyright 2017 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -use rustc::util::common; -use rustc::middle::cstore::MetadataLoader; -use rustc_target::spec::Target; -use llvm; -use llvm::{False, ObjectFile, mk_section_iter}; -use llvm::archive_ro::ArchiveRO; - -use rustc_data_structures::owning_ref::OwningRef; -use std::path::Path; -use std::ptr; -use std::slice; - -pub use rustc_data_structures::sync::MetadataRef; - -pub const METADATA_FILENAME: &str = "rust.metadata.bin"; - -pub struct LlvmMetadataLoader; - -impl MetadataLoader for LlvmMetadataLoader { - fn get_rlib_metadata(&self, _: &Target, filename: &Path) -> Result { - // Use ArchiveRO for speed here, it's backed by LLVM and uses mmap - // internally to read the file. We also avoid even using a memcpy by - // just keeping the archive along while the metadata is in use. - let archive = ArchiveRO::open(filename) - .map(|ar| OwningRef::new(box ar)) - .map_err(|e| { - debug!("llvm didn't like `{}`: {}", filename.display(), e); - format!("failed to read rlib metadata in '{}': {}", filename.display(), e) - })?; - let buf: OwningRef<_, [u8]> = archive - .try_map(|ar| { - ar.iter() - .filter_map(|s| s.ok()) - .find(|sect| sect.name() == Some(METADATA_FILENAME)) - .map(|s| s.data()) - .ok_or_else(|| { - debug!("didn't find '{}' in the archive", METADATA_FILENAME); - format!("failed to read rlib metadata: '{}'", - filename.display()) - }) - })?; - Ok(rustc_erase_owner!(buf)) - } - - fn get_dylib_metadata(&self, - target: &Target, - filename: &Path) - -> Result { - unsafe { - let buf = common::path2cstr(filename); - let mb = llvm::LLVMRustCreateMemoryBufferWithContentsOfFile(buf.as_ptr()); - if mb as isize == 0 { - return Err(format!("error reading library: '{}'", filename.display())); - } - let of = ObjectFile::new(mb) - .map(|of| OwningRef::new(box of)) - .ok_or_else(|| format!("provided path not an object file: '{}'", - filename.display()))?; - let buf = of.try_map(|of| search_meta_section(of, target, filename))?; - Ok(rustc_erase_owner!(buf)) - } - } -} - -fn search_meta_section<'a>(of: &'a ObjectFile, - target: &Target, - filename: &Path) - -> Result<&'a [u8], String> { - unsafe { - let si = mk_section_iter(of.llof); - while llvm::LLVMIsSectionIteratorAtEnd(of.llof, si.llsi) == False { - let mut name_buf = ptr::null(); - let name_len = llvm::LLVMRustGetSectionName(si.llsi, &mut name_buf); - let name = slice::from_raw_parts(name_buf as *const u8, name_len as usize).to_vec(); - let name = String::from_utf8(name).unwrap(); - debug!("get_metadata_section: name {}", name); - if read_metadata_section_name(target) == name { - let cbuf = llvm::LLVMGetSectionContents(si.llsi); - let csz = llvm::LLVMGetSectionSize(si.llsi) as usize; - // The buffer is valid while the object file is around - let buf: &'a [u8] = slice::from_raw_parts(cbuf as *const u8, csz); - return Ok(buf); - } - llvm::LLVMMoveToNextSection(si.llsi); - } - } - Err(format!("metadata not found: '{}'", filename.display())) -} - -pub fn metadata_section_name(target: &Target) -> &'static str { - // Historical note: - // - // When using link.exe it was seen that the section name `.note.rustc` - // was getting shortened to `.note.ru`, and according to the PE and COFF - // specification: - // - // > Executable images do not use a string table and do not support - // > section names longer than 8 characters - // - // https://msdn.microsoft.com/en-us/library/windows/hardware/gg463119.aspx - // - // As a result, we choose a slightly shorter name! As to why - // `.note.rustc` works on MinGW, that's another good question... - - if target.options.is_like_osx { - "__DATA,.rustc" - } else { - ".rustc" - } -} - -fn read_metadata_section_name(_target: &Target) -> &'static str { - ".rustc" -} diff --git a/src/librustc_trans/meth.rs b/src/librustc_trans/meth.rs deleted file mode 100644 index 21bbdf31dcb..00000000000 --- a/src/librustc_trans/meth.rs +++ /dev/null @@ -1,115 +0,0 @@ -// Copyright 2012 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -use llvm::ValueRef; -use abi::{FnType, FnTypeExt}; -use callee; -use common::*; -use builder::Builder; -use consts; -use monomorphize; -use type_::Type; -use value::Value; -use rustc::ty::{self, Ty}; -use rustc::ty::layout::HasDataLayout; -use debuginfo; - -#[derive(Copy, Clone, Debug)] -pub struct VirtualIndex(u64); - -pub const DESTRUCTOR: VirtualIndex = VirtualIndex(0); -pub const SIZE: VirtualIndex = VirtualIndex(1); -pub const ALIGN: VirtualIndex = VirtualIndex(2); - -impl<'a, 'tcx> VirtualIndex { - pub fn from_index(index: usize) -> Self { - VirtualIndex(index as u64 + 3) - } - - pub fn get_fn(self, bx: &Builder<'a, 'tcx>, - llvtable: ValueRef, - fn_ty: &FnType<'tcx, Ty<'tcx>>) -> ValueRef { - // Load the data pointer from the object. - debug!("get_fn({:?}, {:?})", Value(llvtable), self); - - let llvtable = bx.pointercast(llvtable, fn_ty.llvm_type(bx.cx).ptr_to().ptr_to()); - let ptr_align = bx.tcx().data_layout.pointer_align; - let ptr = bx.load(bx.inbounds_gep(llvtable, &[C_usize(bx.cx, self.0)]), ptr_align); - bx.nonnull_metadata(ptr); - // Vtable loads are invariant - bx.set_invariant_load(ptr); - ptr - } - - pub fn get_usize(self, bx: &Builder<'a, 'tcx>, llvtable: ValueRef) -> ValueRef { - // Load the data pointer from the object. - debug!("get_int({:?}, {:?})", Value(llvtable), self); - - let llvtable = bx.pointercast(llvtable, Type::isize(bx.cx).ptr_to()); - let usize_align = bx.tcx().data_layout.pointer_align; - let ptr = bx.load(bx.inbounds_gep(llvtable, &[C_usize(bx.cx, self.0)]), usize_align); - // Vtable loads are invariant - bx.set_invariant_load(ptr); - ptr - } -} - -/// Creates a dynamic vtable for the given type and vtable origin. -/// This is used only for objects. -/// -/// The vtables are cached instead of created on every call. -/// -/// The `trait_ref` encodes the erased self type. Hence if we are -/// making an object `Foo` from a value of type `Foo`, then -/// `trait_ref` would map `T:Trait`. -pub fn get_vtable<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, - ty: Ty<'tcx>, - trait_ref: Option>) - -> ValueRef -{ - let tcx = cx.tcx; - - debug!("get_vtable(ty={:?}, trait_ref={:?})", ty, trait_ref); - - // Check the cache. - if let Some(&val) = cx.vtables.borrow().get(&(ty, trait_ref)) { - return val; - } - - // Not in the cache. Build it. - let nullptr = C_null(Type::i8p(cx)); - - let (size, align) = cx.size_and_align_of(ty); - let mut components: Vec<_> = [ - callee::get_fn(cx, monomorphize::resolve_drop_in_place(cx.tcx, ty)), - C_usize(cx, size.bytes()), - C_usize(cx, align.abi()) - ].iter().cloned().collect(); - - if let Some(trait_ref) = trait_ref { - let trait_ref = trait_ref.with_self_ty(tcx, ty); - let methods = tcx.vtable_methods(trait_ref); - let methods = methods.iter().cloned().map(|opt_mth| { - opt_mth.map_or(nullptr, |(def_id, substs)| { - callee::resolve_and_get_fn(cx, def_id, substs) - }) - }); - components.extend(methods); - } - - let vtable_const = C_struct(cx, &components, false); - let align = cx.data_layout().pointer_align; - let vtable = consts::addr_of(cx, vtable_const, align, "vtable"); - - debuginfo::create_vtable_metadata(cx, ty, vtable); - - cx.vtables.borrow_mut().insert((ty, trait_ref), vtable); - vtable -} diff --git a/src/librustc_trans/mir/analyze.rs b/src/librustc_trans/mir/analyze.rs deleted file mode 100644 index 9e5298eb736..00000000000 --- a/src/librustc_trans/mir/analyze.rs +++ /dev/null @@ -1,361 +0,0 @@ -// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! An analysis to determine which locals require allocas and -//! which do not. - -use rustc_data_structures::bitvec::BitVector; -use rustc_data_structures::control_flow_graph::dominators::Dominators; -use rustc_data_structures::indexed_vec::{Idx, IndexVec}; -use rustc::mir::{self, Location, TerminatorKind}; -use rustc::mir::visit::{Visitor, PlaceContext}; -use rustc::mir::traversal; -use rustc::ty; -use rustc::ty::layout::LayoutOf; -use type_of::LayoutLlvmExt; -use super::FunctionCx; - -pub fn non_ssa_locals<'a, 'tcx>(fx: &FunctionCx<'a, 'tcx>) -> BitVector { - let mir = fx.mir; - let mut analyzer = LocalAnalyzer::new(fx); - - analyzer.visit_mir(mir); - - for (index, ty) in mir.local_decls.iter().map(|l| l.ty).enumerate() { - let ty = fx.monomorphize(&ty); - debug!("local {} has type {:?}", index, ty); - let layout = fx.cx.layout_of(ty); - if layout.is_llvm_immediate() { - // These sorts of types are immediates that we can store - // in an ValueRef without an alloca. - } else if layout.is_llvm_scalar_pair() { - // We allow pairs and uses of any of their 2 fields. - } else { - // These sorts of types require an alloca. Note that - // is_llvm_immediate() may *still* be true, particularly - // for newtypes, but we currently force some types - // (e.g. structs) into an alloca unconditionally, just so - // that we don't have to deal with having two pathways - // (gep vs extractvalue etc). - analyzer.not_ssa(mir::Local::new(index)); - } - } - - analyzer.non_ssa_locals -} - -struct LocalAnalyzer<'mir, 'a: 'mir, 'tcx: 'a> { - fx: &'mir FunctionCx<'a, 'tcx>, - dominators: Dominators, - non_ssa_locals: BitVector, - // The location of the first visited direct assignment to each - // local, or an invalid location (out of bounds `block` index). - first_assignment: IndexVec -} - -impl<'mir, 'a, 'tcx> LocalAnalyzer<'mir, 'a, 'tcx> { - fn new(fx: &'mir FunctionCx<'a, 'tcx>) -> LocalAnalyzer<'mir, 'a, 'tcx> { - let invalid_location = - mir::BasicBlock::new(fx.mir.basic_blocks().len()).start_location(); - let mut analyzer = LocalAnalyzer { - fx, - dominators: fx.mir.dominators(), - non_ssa_locals: BitVector::new(fx.mir.local_decls.len()), - first_assignment: IndexVec::from_elem(invalid_location, &fx.mir.local_decls) - }; - - // Arguments get assigned to by means of the function being called - for arg in fx.mir.args_iter() { - analyzer.first_assignment[arg] = mir::START_BLOCK.start_location(); - } - - analyzer - } - - fn first_assignment(&self, local: mir::Local) -> Option { - let location = self.first_assignment[local]; - if location.block.index() < self.fx.mir.basic_blocks().len() { - Some(location) - } else { - None - } - } - - fn not_ssa(&mut self, local: mir::Local) { - debug!("marking {:?} as non-SSA", local); - self.non_ssa_locals.insert(local.index()); - } - - fn assign(&mut self, local: mir::Local, location: Location) { - if self.first_assignment(local).is_some() { - self.not_ssa(local); - } else { - self.first_assignment[local] = location; - } - } -} - -impl<'mir, 'a, 'tcx> Visitor<'tcx> for LocalAnalyzer<'mir, 'a, 'tcx> { - fn visit_assign(&mut self, - block: mir::BasicBlock, - place: &mir::Place<'tcx>, - rvalue: &mir::Rvalue<'tcx>, - location: Location) { - debug!("visit_assign(block={:?}, place={:?}, rvalue={:?})", block, place, rvalue); - - if let mir::Place::Local(index) = *place { - self.assign(index, location); - if !self.fx.rvalue_creates_operand(rvalue) { - self.not_ssa(index); - } - } else { - self.visit_place(place, PlaceContext::Store, location); - } - - self.visit_rvalue(rvalue, location); - } - - fn visit_terminator_kind(&mut self, - block: mir::BasicBlock, - kind: &mir::TerminatorKind<'tcx>, - location: Location) { - let check = match *kind { - mir::TerminatorKind::Call { - func: mir::Operand::Constant(ref c), - ref args, .. - } => match c.ty.sty { - ty::TyFnDef(did, _) => Some((did, args)), - _ => None, - }, - _ => None, - }; - if let Some((def_id, args)) = check { - if Some(def_id) == self.fx.cx.tcx.lang_items().box_free_fn() { - // box_free(x) shares with `drop x` the property that it - // is not guaranteed to be statically dominated by the - // definition of x, so x must always be in an alloca. - if let mir::Operand::Move(ref place) = args[0] { - self.visit_place(place, PlaceContext::Drop, location); - } - } - } - - self.super_terminator_kind(block, kind, location); - } - - fn visit_place(&mut self, - place: &mir::Place<'tcx>, - context: PlaceContext<'tcx>, - location: Location) { - debug!("visit_place(place={:?}, context={:?})", place, context); - let cx = self.fx.cx; - - if let mir::Place::Projection(ref proj) = *place { - // Allow uses of projections that are ZSTs or from scalar fields. - let is_consume = match context { - PlaceContext::Copy | PlaceContext::Move => true, - _ => false - }; - if is_consume { - let base_ty = proj.base.ty(self.fx.mir, cx.tcx); - let base_ty = self.fx.monomorphize(&base_ty); - - // ZSTs don't require any actual memory access. - let elem_ty = base_ty.projection_ty(cx.tcx, &proj.elem).to_ty(cx.tcx); - let elem_ty = self.fx.monomorphize(&elem_ty); - if cx.layout_of(elem_ty).is_zst() { - return; - } - - if let mir::ProjectionElem::Field(..) = proj.elem { - let layout = cx.layout_of(base_ty.to_ty(cx.tcx)); - if layout.is_llvm_immediate() || layout.is_llvm_scalar_pair() { - // Recurse with the same context, instead of `Projection`, - // potentially stopping at non-operand projections, - // which would trigger `not_ssa` on locals. - self.visit_place(&proj.base, context, location); - return; - } - } - } - - // A deref projection only reads the pointer, never needs the place. - if let mir::ProjectionElem::Deref = proj.elem { - return self.visit_place(&proj.base, PlaceContext::Copy, location); - } - } - - self.super_place(place, context, location); - } - - fn visit_local(&mut self, - &local: &mir::Local, - context: PlaceContext<'tcx>, - location: Location) { - match context { - PlaceContext::Call => { - self.assign(local, location); - } - - PlaceContext::StorageLive | - PlaceContext::StorageDead | - PlaceContext::Validate => {} - - PlaceContext::Copy | - PlaceContext::Move => { - // Reads from uninitialized variables (e.g. in dead code, after - // optimizations) require locals to be in (uninitialized) memory. - // NB: there can be uninitialized reads of a local visited after - // an assignment to that local, if they happen on disjoint paths. - let ssa_read = match self.first_assignment(local) { - Some(assignment_location) => { - assignment_location.dominates(location, &self.dominators) - } - None => false - }; - if !ssa_read { - self.not_ssa(local); - } - } - - PlaceContext::Inspect | - PlaceContext::Store | - PlaceContext::AsmOutput | - PlaceContext::Borrow { .. } | - PlaceContext::Projection(..) => { - self.not_ssa(local); - } - - PlaceContext::Drop => { - let ty = mir::Place::Local(local).ty(self.fx.mir, self.fx.cx.tcx); - let ty = self.fx.monomorphize(&ty.to_ty(self.fx.cx.tcx)); - - // Only need the place if we're actually dropping it. - if self.fx.cx.type_needs_drop(ty) { - self.not_ssa(local); - } - } - } - } -} - -#[derive(Copy, Clone, Debug, PartialEq, Eq)] -pub enum CleanupKind { - NotCleanup, - Funclet, - Internal { funclet: mir::BasicBlock } -} - -impl CleanupKind { - pub fn funclet_bb(self, for_bb: mir::BasicBlock) -> Option { - match self { - CleanupKind::NotCleanup => None, - CleanupKind::Funclet => Some(for_bb), - CleanupKind::Internal { funclet } => Some(funclet), - } - } -} - -pub fn cleanup_kinds<'a, 'tcx>(mir: &mir::Mir<'tcx>) -> IndexVec { - fn discover_masters<'tcx>(result: &mut IndexVec, - mir: &mir::Mir<'tcx>) { - for (bb, data) in mir.basic_blocks().iter_enumerated() { - match data.terminator().kind { - TerminatorKind::Goto { .. } | - TerminatorKind::Resume | - TerminatorKind::Abort | - TerminatorKind::Return | - TerminatorKind::GeneratorDrop | - TerminatorKind::Unreachable | - TerminatorKind::SwitchInt { .. } | - TerminatorKind::Yield { .. } | - TerminatorKind::FalseEdges { .. } | - TerminatorKind::FalseUnwind { .. } => { - /* nothing to do */ - } - TerminatorKind::Call { cleanup: unwind, .. } | - TerminatorKind::Assert { cleanup: unwind, .. } | - TerminatorKind::DropAndReplace { unwind, .. } | - TerminatorKind::Drop { unwind, .. } => { - if let Some(unwind) = unwind { - debug!("cleanup_kinds: {:?}/{:?} registering {:?} as funclet", - bb, data, unwind); - result[unwind] = CleanupKind::Funclet; - } - } - } - } - } - - fn propagate<'tcx>(result: &mut IndexVec, - mir: &mir::Mir<'tcx>) { - let mut funclet_succs = IndexVec::from_elem(None, mir.basic_blocks()); - - let mut set_successor = |funclet: mir::BasicBlock, succ| { - match funclet_succs[funclet] { - ref mut s @ None => { - debug!("set_successor: updating successor of {:?} to {:?}", - funclet, succ); - *s = Some(succ); - }, - Some(s) => if s != succ { - span_bug!(mir.span, "funclet {:?} has 2 parents - {:?} and {:?}", - funclet, s, succ); - } - } - }; - - for (bb, data) in traversal::reverse_postorder(mir) { - let funclet = match result[bb] { - CleanupKind::NotCleanup => continue, - CleanupKind::Funclet => bb, - CleanupKind::Internal { funclet } => funclet, - }; - - debug!("cleanup_kinds: {:?}/{:?}/{:?} propagating funclet {:?}", - bb, data, result[bb], funclet); - - for &succ in data.terminator().successors() { - let kind = result[succ]; - debug!("cleanup_kinds: propagating {:?} to {:?}/{:?}", - funclet, succ, kind); - match kind { - CleanupKind::NotCleanup => { - result[succ] = CleanupKind::Internal { funclet: funclet }; - } - CleanupKind::Funclet => { - if funclet != succ { - set_successor(funclet, succ); - } - } - CleanupKind::Internal { funclet: succ_funclet } => { - if funclet != succ_funclet { - // `succ` has 2 different funclet going into it, so it must - // be a funclet by itself. - - debug!("promoting {:?} to a funclet and updating {:?}", succ, - succ_funclet); - result[succ] = CleanupKind::Funclet; - set_successor(succ_funclet, succ); - set_successor(funclet, succ); - } - } - } - } - } - } - - let mut result = IndexVec::from_elem(CleanupKind::NotCleanup, mir.basic_blocks()); - - discover_masters(&mut result, mir); - propagate(&mut result, mir); - debug!("cleanup_kinds: result={:?}", result); - result -} diff --git a/src/librustc_trans/mir/block.rs b/src/librustc_trans/mir/block.rs deleted file mode 100644 index e4989da36c0..00000000000 --- a/src/librustc_trans/mir/block.rs +++ /dev/null @@ -1,895 +0,0 @@ -// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -use llvm::{self, ValueRef, BasicBlockRef}; -use rustc::middle::lang_items; -use rustc::ty::{self, Ty, TypeFoldable}; -use rustc::ty::layout::{self, LayoutOf}; -use rustc::mir; -use rustc::mir::interpret::EvalErrorKind; -use abi::{Abi, ArgType, ArgTypeExt, FnType, FnTypeExt, LlvmType, PassMode}; -use base; -use callee; -use builder::{Builder, MemFlags}; -use common::{self, C_bool, C_str_slice, C_struct, C_u32, C_uint_big, C_undef}; -use consts; -use meth; -use monomorphize; -use type_of::LayoutLlvmExt; -use type_::Type; - -use syntax::symbol::Symbol; -use syntax_pos::Pos; - -use super::{FunctionCx, LocalRef}; -use super::place::PlaceRef; -use super::operand::OperandRef; -use super::operand::OperandValue::{Pair, Ref, Immediate}; - -impl<'a, 'tcx> FunctionCx<'a, 'tcx> { - pub fn trans_block(&mut self, bb: mir::BasicBlock) { - let mut bx = self.build_block(bb); - let data = &self.mir[bb]; - - debug!("trans_block({:?}={:?})", bb, data); - - for statement in &data.statements { - bx = self.trans_statement(bx, statement); - } - - self.trans_terminator(bx, bb, data.terminator()); - } - - fn trans_terminator(&mut self, - mut bx: Builder<'a, 'tcx>, - bb: mir::BasicBlock, - terminator: &mir::Terminator<'tcx>) - { - debug!("trans_terminator: {:?}", terminator); - - // Create the cleanup bundle, if needed. - let tcx = bx.tcx(); - let span = terminator.source_info.span; - let funclet_bb = self.cleanup_kinds[bb].funclet_bb(bb); - let funclet = funclet_bb.and_then(|funclet_bb| self.funclets[funclet_bb].as_ref()); - - let cleanup_pad = funclet.map(|lp| lp.cleanuppad()); - let cleanup_bundle = funclet.map(|l| l.bundle()); - - let lltarget = |this: &mut Self, target: mir::BasicBlock| { - let lltarget = this.blocks[target]; - let target_funclet = this.cleanup_kinds[target].funclet_bb(target); - match (funclet_bb, target_funclet) { - (None, None) => (lltarget, false), - (Some(f), Some(t_f)) - if f == t_f || !base::wants_msvc_seh(tcx.sess) - => (lltarget, false), - (None, Some(_)) => { - // jump *into* cleanup - need a landing pad if GNU - (this.landing_pad_to(target), false) - } - (Some(_), None) => span_bug!(span, "{:?} - jump out of cleanup?", terminator), - (Some(_), Some(_)) => { - (this.landing_pad_to(target), true) - } - } - }; - - let llblock = |this: &mut Self, target: mir::BasicBlock| { - let (lltarget, is_cleanupret) = lltarget(this, target); - if is_cleanupret { - // MSVC cross-funclet jump - need a trampoline - - debug!("llblock: creating cleanup trampoline for {:?}", target); - let name = &format!("{:?}_cleanup_trampoline_{:?}", bb, target); - let trampoline = this.new_block(name); - trampoline.cleanup_ret(cleanup_pad.unwrap(), Some(lltarget)); - trampoline.llbb() - } else { - lltarget - } - }; - - let funclet_br = |this: &mut Self, bx: Builder, target: mir::BasicBlock| { - let (lltarget, is_cleanupret) = lltarget(this, target); - if is_cleanupret { - // micro-optimization: generate a `ret` rather than a jump - // to a trampoline. - bx.cleanup_ret(cleanup_pad.unwrap(), Some(lltarget)); - } else { - bx.br(lltarget); - } - }; - - let do_call = | - this: &mut Self, - bx: Builder<'a, 'tcx>, - fn_ty: FnType<'tcx, Ty<'tcx>>, - fn_ptr: ValueRef, - llargs: &[ValueRef], - destination: Option<(ReturnDest<'tcx>, mir::BasicBlock)>, - cleanup: Option - | { - if let Some(cleanup) = cleanup { - let ret_bx = if let Some((_, target)) = destination { - this.blocks[target] - } else { - this.unreachable_block() - }; - let invokeret = bx.invoke(fn_ptr, - &llargs, - ret_bx, - llblock(this, cleanup), - cleanup_bundle); - fn_ty.apply_attrs_callsite(&bx, invokeret); - - if let Some((ret_dest, target)) = destination { - let ret_bx = this.build_block(target); - this.set_debug_loc(&ret_bx, terminator.source_info); - this.store_return(&ret_bx, ret_dest, &fn_ty.ret, invokeret); - } - } else { - let llret = bx.call(fn_ptr, &llargs, cleanup_bundle); - fn_ty.apply_attrs_callsite(&bx, llret); - if this.mir[bb].is_cleanup { - // Cleanup is always the cold path. Don't inline - // drop glue. Also, when there is a deeply-nested - // struct, there are "symmetry" issues that cause - // exponential inlining - see issue #41696. - llvm::Attribute::NoInline.apply_callsite(llvm::AttributePlace::Function, llret); - } - - if let Some((ret_dest, target)) = destination { - this.store_return(&bx, ret_dest, &fn_ty.ret, llret); - funclet_br(this, bx, target); - } else { - bx.unreachable(); - } - } - }; - - self.set_debug_loc(&bx, terminator.source_info); - match terminator.kind { - mir::TerminatorKind::Resume => { - if let Some(cleanup_pad) = cleanup_pad { - bx.cleanup_ret(cleanup_pad, None); - } else { - let slot = self.get_personality_slot(&bx); - let lp0 = slot.project_field(&bx, 0).load(&bx).immediate(); - let lp1 = slot.project_field(&bx, 1).load(&bx).immediate(); - slot.storage_dead(&bx); - - if !bx.sess().target.target.options.custom_unwind_resume { - let mut lp = C_undef(self.landing_pad_type()); - lp = bx.insert_value(lp, lp0, 0); - lp = bx.insert_value(lp, lp1, 1); - bx.resume(lp); - } else { - bx.call(bx.cx.eh_unwind_resume(), &[lp0], cleanup_bundle); - bx.unreachable(); - } - } - } - - mir::TerminatorKind::Abort => { - // Call core::intrinsics::abort() - let fnname = bx.cx.get_intrinsic(&("llvm.trap")); - bx.call(fnname, &[], None); - bx.unreachable(); - } - - mir::TerminatorKind::Goto { target } => { - funclet_br(self, bx, target); - } - - mir::TerminatorKind::SwitchInt { ref discr, switch_ty, ref values, ref targets } => { - let discr = self.trans_operand(&bx, discr); - if switch_ty == bx.tcx().types.bool { - let lltrue = llblock(self, targets[0]); - let llfalse = llblock(self, targets[1]); - if let [0] = values[..] { - bx.cond_br(discr.immediate(), llfalse, lltrue); - } else { - assert_eq!(&values[..], &[1]); - bx.cond_br(discr.immediate(), lltrue, llfalse); - } - } else { - let (otherwise, targets) = targets.split_last().unwrap(); - let switch = bx.switch(discr.immediate(), - llblock(self, *otherwise), values.len()); - let switch_llty = bx.cx.layout_of(switch_ty).immediate_llvm_type(bx.cx); - for (&value, target) in values.iter().zip(targets) { - let llval = C_uint_big(switch_llty, value); - let llbb = llblock(self, *target); - bx.add_case(switch, llval, llbb) - } - } - } - - mir::TerminatorKind::Return => { - let llval = match self.fn_ty.ret.mode { - PassMode::Ignore | PassMode::Indirect(_) => { - bx.ret_void(); - return; - } - - PassMode::Direct(_) | PassMode::Pair(..) => { - let op = self.trans_consume(&bx, &mir::Place::Local(mir::RETURN_PLACE)); - if let Ref(llval, align) = op.val { - bx.load(llval, align) - } else { - op.immediate_or_packed_pair(&bx) - } - } - - PassMode::Cast(cast_ty) => { - let op = match self.locals[mir::RETURN_PLACE] { - LocalRef::Operand(Some(op)) => op, - LocalRef::Operand(None) => bug!("use of return before def"), - LocalRef::Place(tr_place) => { - OperandRef { - val: Ref(tr_place.llval, tr_place.align), - layout: tr_place.layout - } - } - }; - let llslot = match op.val { - Immediate(_) | Pair(..) => { - let scratch = PlaceRef::alloca(&bx, self.fn_ty.ret.layout, "ret"); - op.val.store(&bx, scratch); - scratch.llval - } - Ref(llval, align) => { - assert_eq!(align.abi(), op.layout.align.abi(), - "return place is unaligned!"); - llval - } - }; - bx.load( - bx.pointercast(llslot, cast_ty.llvm_type(bx.cx).ptr_to()), - self.fn_ty.ret.layout.align) - } - }; - bx.ret(llval); - } - - mir::TerminatorKind::Unreachable => { - bx.unreachable(); - } - - mir::TerminatorKind::Drop { ref location, target, unwind } => { - let ty = location.ty(self.mir, bx.tcx()).to_ty(bx.tcx()); - let ty = self.monomorphize(&ty); - let drop_fn = monomorphize::resolve_drop_in_place(bx.cx.tcx, ty); - - if let ty::InstanceDef::DropGlue(_, None) = drop_fn.def { - // we don't actually need to drop anything. - funclet_br(self, bx, target); - return - } - - let place = self.trans_place(&bx, location); - let mut args: &[_] = &[place.llval, place.llextra]; - args = &args[..1 + place.has_extra() as usize]; - let (drop_fn, fn_ty) = match ty.sty { - ty::TyDynamic(..) => { - let fn_ty = drop_fn.ty(bx.cx.tcx); - let sig = common::ty_fn_sig(bx.cx, fn_ty); - let sig = bx.tcx().normalize_erasing_late_bound_regions( - ty::ParamEnv::reveal_all(), - &sig, - ); - let fn_ty = FnType::new_vtable(bx.cx, sig, &[]); - args = &args[..1]; - (meth::DESTRUCTOR.get_fn(&bx, place.llextra, &fn_ty), fn_ty) - } - _ => { - (callee::get_fn(bx.cx, drop_fn), - FnType::of_instance(bx.cx, &drop_fn)) - } - }; - do_call(self, bx, fn_ty, drop_fn, args, - Some((ReturnDest::Nothing, target)), - unwind); - } - - mir::TerminatorKind::Assert { ref cond, expected, ref msg, target, cleanup } => { - let cond = self.trans_operand(&bx, cond).immediate(); - let mut const_cond = common::const_to_opt_u128(cond, false).map(|c| c == 1); - - // This case can currently arise only from functions marked - // with #[rustc_inherit_overflow_checks] and inlined from - // another crate (mostly core::num generic/#[inline] fns), - // while the current crate doesn't use overflow checks. - // NOTE: Unlike binops, negation doesn't have its own - // checked operation, just a comparison with the minimum - // value, so we have to check for the assert message. - if !bx.cx.check_overflow { - if let mir::interpret::EvalErrorKind::OverflowNeg = *msg { - const_cond = Some(expected); - } - } - - // Don't translate the panic block if success if known. - if const_cond == Some(expected) { - funclet_br(self, bx, target); - return; - } - - // Pass the condition through llvm.expect for branch hinting. - let expect = bx.cx.get_intrinsic(&"llvm.expect.i1"); - let cond = bx.call(expect, &[cond, C_bool(bx.cx, expected)], None); - - // Create the failure block and the conditional branch to it. - let lltarget = llblock(self, target); - let panic_block = self.new_block("panic"); - if expected { - bx.cond_br(cond, lltarget, panic_block.llbb()); - } else { - bx.cond_br(cond, panic_block.llbb(), lltarget); - } - - // After this point, bx is the block for the call to panic. - bx = panic_block; - self.set_debug_loc(&bx, terminator.source_info); - - // Get the location information. - let loc = bx.sess().codemap().lookup_char_pos(span.lo()); - let filename = Symbol::intern(&loc.file.name.to_string()).as_str(); - let filename = C_str_slice(bx.cx, filename); - let line = C_u32(bx.cx, loc.line as u32); - let col = C_u32(bx.cx, loc.col.to_usize() as u32 + 1); - let align = tcx.data_layout.aggregate_align - .max(tcx.data_layout.i32_align) - .max(tcx.data_layout.pointer_align); - - // Put together the arguments to the panic entry point. - let (lang_item, args) = match *msg { - EvalErrorKind::BoundsCheck { ref len, ref index } => { - let len = self.trans_operand(&mut bx, len).immediate(); - let index = self.trans_operand(&mut bx, index).immediate(); - - let file_line_col = C_struct(bx.cx, &[filename, line, col], false); - let file_line_col = consts::addr_of(bx.cx, - file_line_col, - align, - "panic_bounds_check_loc"); - (lang_items::PanicBoundsCheckFnLangItem, - vec![file_line_col, index, len]) - } - _ => { - let str = msg.description(); - let msg_str = Symbol::intern(str).as_str(); - let msg_str = C_str_slice(bx.cx, msg_str); - let msg_file_line_col = C_struct(bx.cx, - &[msg_str, filename, line, col], - false); - let msg_file_line_col = consts::addr_of(bx.cx, - msg_file_line_col, - align, - "panic_loc"); - (lang_items::PanicFnLangItem, - vec![msg_file_line_col]) - } - }; - - // Obtain the panic entry point. - let def_id = common::langcall(bx.tcx(), Some(span), "", lang_item); - let instance = ty::Instance::mono(bx.tcx(), def_id); - let fn_ty = FnType::of_instance(bx.cx, &instance); - let llfn = callee::get_fn(bx.cx, instance); - - // Translate the actual panic invoke/call. - do_call(self, bx, fn_ty, llfn, &args, None, cleanup); - } - - mir::TerminatorKind::DropAndReplace { .. } => { - bug!("undesugared DropAndReplace in trans: {:?}", terminator); - } - - mir::TerminatorKind::Call { ref func, ref args, ref destination, cleanup } => { - // Create the callee. This is a fn ptr or zero-sized and hence a kind of scalar. - let callee = self.trans_operand(&bx, func); - - let (instance, mut llfn) = match callee.layout.ty.sty { - ty::TyFnDef(def_id, substs) => { - (Some(ty::Instance::resolve(bx.cx.tcx, - ty::ParamEnv::reveal_all(), - def_id, - substs).unwrap()), - None) - } - ty::TyFnPtr(_) => { - (None, Some(callee.immediate())) - } - _ => bug!("{} is not callable", callee.layout.ty) - }; - let def = instance.map(|i| i.def); - let sig = callee.layout.ty.fn_sig(bx.tcx()); - let sig = bx.tcx().normalize_erasing_late_bound_regions( - ty::ParamEnv::reveal_all(), - &sig, - ); - let abi = sig.abi; - - // Handle intrinsics old trans wants Expr's for, ourselves. - let intrinsic = match def { - Some(ty::InstanceDef::Intrinsic(def_id)) - => Some(bx.tcx().item_name(def_id).as_str()), - _ => None - }; - let intrinsic = intrinsic.as_ref().map(|s| &s[..]); - - if intrinsic == Some("transmute") { - let &(ref dest, target) = destination.as_ref().unwrap(); - self.trans_transmute(&bx, &args[0], dest); - funclet_br(self, bx, target); - return; - } - - let extra_args = &args[sig.inputs().len()..]; - let extra_args = extra_args.iter().map(|op_arg| { - let op_ty = op_arg.ty(self.mir, bx.tcx()); - self.monomorphize(&op_ty) - }).collect::>(); - - let fn_ty = match def { - Some(ty::InstanceDef::Virtual(..)) => { - FnType::new_vtable(bx.cx, sig, &extra_args) - } - Some(ty::InstanceDef::DropGlue(_, None)) => { - // empty drop glue - a nop. - let &(_, target) = destination.as_ref().unwrap(); - funclet_br(self, bx, target); - return; - } - _ => FnType::new(bx.cx, sig, &extra_args) - }; - - // The arguments we'll be passing. Plus one to account for outptr, if used. - let arg_count = fn_ty.args.len() + fn_ty.ret.is_indirect() as usize; - let mut llargs = Vec::with_capacity(arg_count); - - // Prepare the return value destination - let ret_dest = if let Some((ref dest, _)) = *destination { - let is_intrinsic = intrinsic.is_some(); - self.make_return_dest(&bx, dest, &fn_ty.ret, &mut llargs, - is_intrinsic) - } else { - ReturnDest::Nothing - }; - - if intrinsic.is_some() && intrinsic != Some("drop_in_place") { - use intrinsic::trans_intrinsic_call; - - let dest = match ret_dest { - _ if fn_ty.ret.is_indirect() => llargs[0], - ReturnDest::Nothing => { - C_undef(fn_ty.ret.memory_ty(bx.cx).ptr_to()) - } - ReturnDest::IndirectOperand(dst, _) | - ReturnDest::Store(dst) => dst.llval, - ReturnDest::DirectOperand(_) => - bug!("Cannot use direct operand with an intrinsic call") - }; - - let args: Vec<_> = args.iter().enumerate().map(|(i, arg)| { - // The indices passed to simd_shuffle* in the - // third argument must be constant. This is - // checked by const-qualification, which also - // promotes any complex rvalues to constants. - if i == 2 && intrinsic.unwrap().starts_with("simd_shuffle") { - match *arg { - mir::Operand::Copy(_) | - mir::Operand::Move(_) => { - span_bug!(span, "shuffle indices must be constant"); - } - mir::Operand::Constant(ref constant) => { - let (llval, ty) = self.simd_shuffle_indices( - &bx, - constant, - ); - return OperandRef { - val: Immediate(llval), - layout: bx.cx.layout_of(ty) - }; - } - } - } - - self.trans_operand(&bx, arg) - }).collect(); - - - let callee_ty = instance.as_ref().unwrap().ty(bx.cx.tcx); - trans_intrinsic_call(&bx, callee_ty, &fn_ty, &args, dest, - terminator.source_info.span); - - if let ReturnDest::IndirectOperand(dst, _) = ret_dest { - self.store_return(&bx, ret_dest, &fn_ty.ret, dst.llval); - } - - if let Some((_, target)) = *destination { - funclet_br(self, bx, target); - } else { - bx.unreachable(); - } - - return; - } - - // Split the rust-call tupled arguments off. - let (first_args, untuple) = if abi == Abi::RustCall && !args.is_empty() { - let (tup, args) = args.split_last().unwrap(); - (args, Some(tup)) - } else { - (&args[..], None) - }; - - for (i, arg) in first_args.iter().enumerate() { - let mut op = self.trans_operand(&bx, arg); - if let (0, Some(ty::InstanceDef::Virtual(_, idx))) = (i, def) { - if let Pair(data_ptr, meta) = op.val { - llfn = Some(meth::VirtualIndex::from_index(idx) - .get_fn(&bx, meta, &fn_ty)); - llargs.push(data_ptr); - continue; - } - } - - // The callee needs to own the argument memory if we pass it - // by-ref, so make a local copy of non-immediate constants. - match (arg, op.val) { - (&mir::Operand::Copy(_), Ref(..)) | - (&mir::Operand::Constant(_), Ref(..)) => { - let tmp = PlaceRef::alloca(&bx, op.layout, "const"); - op.val.store(&bx, tmp); - op.val = Ref(tmp.llval, tmp.align); - } - _ => {} - } - - self.trans_argument(&bx, op, &mut llargs, &fn_ty.args[i]); - } - if let Some(tup) = untuple { - self.trans_arguments_untupled(&bx, tup, &mut llargs, - &fn_ty.args[first_args.len()..]) - } - - let fn_ptr = match (llfn, instance) { - (Some(llfn), _) => llfn, - (None, Some(instance)) => callee::get_fn(bx.cx, instance), - _ => span_bug!(span, "no llfn for call"), - }; - - do_call(self, bx, fn_ty, fn_ptr, &llargs, - destination.as_ref().map(|&(_, target)| (ret_dest, target)), - cleanup); - } - mir::TerminatorKind::GeneratorDrop | - mir::TerminatorKind::Yield { .. } => bug!("generator ops in trans"), - mir::TerminatorKind::FalseEdges { .. } | - mir::TerminatorKind::FalseUnwind { .. } => bug!("borrowck false edges in trans"), - } - } - - fn trans_argument(&mut self, - bx: &Builder<'a, 'tcx>, - op: OperandRef<'tcx>, - llargs: &mut Vec, - arg: &ArgType<'tcx, Ty<'tcx>>) { - // Fill padding with undef value, where applicable. - if let Some(ty) = arg.pad { - llargs.push(C_undef(ty.llvm_type(bx.cx))); - } - - if arg.is_ignore() { - return; - } - - if let PassMode::Pair(..) = arg.mode { - match op.val { - Pair(a, b) => { - llargs.push(a); - llargs.push(b); - return; - } - _ => bug!("trans_argument: {:?} invalid for pair arugment", op) - } - } - - // Force by-ref if we have to load through a cast pointer. - let (mut llval, align, by_ref) = match op.val { - Immediate(_) | Pair(..) => { - match arg.mode { - PassMode::Indirect(_) | PassMode::Cast(_) => { - let scratch = PlaceRef::alloca(bx, arg.layout, "arg"); - op.val.store(bx, scratch); - (scratch.llval, scratch.align, true) - } - _ => { - (op.immediate_or_packed_pair(bx), arg.layout.align, false) - } - } - } - Ref(llval, align) => { - if arg.is_indirect() && align.abi() < arg.layout.align.abi() { - // `foo(packed.large_field)`. We can't pass the (unaligned) field directly. I - // think that ATM (Rust 1.16) we only pass temporaries, but we shouldn't - // have scary latent bugs around. - - let scratch = PlaceRef::alloca(bx, arg.layout, "arg"); - base::memcpy_ty(bx, scratch.llval, llval, op.layout, align, MemFlags::empty()); - (scratch.llval, scratch.align, true) - } else { - (llval, align, true) - } - } - }; - - if by_ref && !arg.is_indirect() { - // Have to load the argument, maybe while casting it. - if let PassMode::Cast(ty) = arg.mode { - llval = bx.load(bx.pointercast(llval, ty.llvm_type(bx.cx).ptr_to()), - align.min(arg.layout.align)); - } else { - // We can't use `PlaceRef::load` here because the argument - // may have a type we don't treat as immediate, but the ABI - // used for this call is passing it by-value. In that case, - // the load would just produce `OperandValue::Ref` instead - // of the `OperandValue::Immediate` we need for the call. - llval = bx.load(llval, align); - if let layout::Abi::Scalar(ref scalar) = arg.layout.abi { - if scalar.is_bool() { - bx.range_metadata(llval, 0..2); - } - } - // We store bools as i8 so we need to truncate to i1. - llval = base::to_immediate(bx, llval, arg.layout); - } - } - - llargs.push(llval); - } - - fn trans_arguments_untupled(&mut self, - bx: &Builder<'a, 'tcx>, - operand: &mir::Operand<'tcx>, - llargs: &mut Vec, - args: &[ArgType<'tcx, Ty<'tcx>>]) { - let tuple = self.trans_operand(bx, operand); - - // Handle both by-ref and immediate tuples. - if let Ref(llval, align) = tuple.val { - let tuple_ptr = PlaceRef::new_sized(llval, tuple.layout, align); - for i in 0..tuple.layout.fields.count() { - let field_ptr = tuple_ptr.project_field(bx, i); - self.trans_argument(bx, field_ptr.load(bx), llargs, &args[i]); - } - } else { - // If the tuple is immediate, the elements are as well. - for i in 0..tuple.layout.fields.count() { - let op = tuple.extract_field(bx, i); - self.trans_argument(bx, op, llargs, &args[i]); - } - } - } - - fn get_personality_slot(&mut self, bx: &Builder<'a, 'tcx>) -> PlaceRef<'tcx> { - let cx = bx.cx; - if let Some(slot) = self.personality_slot { - slot - } else { - let layout = cx.layout_of(cx.tcx.intern_tup(&[ - cx.tcx.mk_mut_ptr(cx.tcx.types.u8), - cx.tcx.types.i32 - ])); - let slot = PlaceRef::alloca(bx, layout, "personalityslot"); - self.personality_slot = Some(slot); - slot - } - } - - /// Return the landingpad wrapper around the given basic block - /// - /// No-op in MSVC SEH scheme. - fn landing_pad_to(&mut self, target_bb: mir::BasicBlock) -> BasicBlockRef { - if let Some(block) = self.landing_pads[target_bb] { - return block; - } - - let block = self.blocks[target_bb]; - let landing_pad = self.landing_pad_uncached(block); - self.landing_pads[target_bb] = Some(landing_pad); - landing_pad - } - - fn landing_pad_uncached(&mut self, target_bb: BasicBlockRef) -> BasicBlockRef { - if base::wants_msvc_seh(self.cx.sess()) { - span_bug!(self.mir.span, "landing pad was not inserted?") - } - - let bx = self.new_block("cleanup"); - - let llpersonality = self.cx.eh_personality(); - let llretty = self.landing_pad_type(); - let lp = bx.landing_pad(llretty, llpersonality, 1); - bx.set_cleanup(lp); - - let slot = self.get_personality_slot(&bx); - slot.storage_live(&bx); - Pair(bx.extract_value(lp, 0), bx.extract_value(lp, 1)).store(&bx, slot); - - bx.br(target_bb); - bx.llbb() - } - - fn landing_pad_type(&self) -> Type { - let cx = self.cx; - Type::struct_(cx, &[Type::i8p(cx), Type::i32(cx)], false) - } - - fn unreachable_block(&mut self) -> BasicBlockRef { - self.unreachable_block.unwrap_or_else(|| { - let bl = self.new_block("unreachable"); - bl.unreachable(); - self.unreachable_block = Some(bl.llbb()); - bl.llbb() - }) - } - - pub fn new_block(&self, name: &str) -> Builder<'a, 'tcx> { - Builder::new_block(self.cx, self.llfn, name) - } - - pub fn build_block(&self, bb: mir::BasicBlock) -> Builder<'a, 'tcx> { - let bx = Builder::with_cx(self.cx); - bx.position_at_end(self.blocks[bb]); - bx - } - - fn make_return_dest(&mut self, bx: &Builder<'a, 'tcx>, - dest: &mir::Place<'tcx>, fn_ret: &ArgType<'tcx, Ty<'tcx>>, - llargs: &mut Vec, is_intrinsic: bool) - -> ReturnDest<'tcx> { - // If the return is ignored, we can just return a do-nothing ReturnDest - if fn_ret.is_ignore() { - return ReturnDest::Nothing; - } - let dest = if let mir::Place::Local(index) = *dest { - match self.locals[index] { - LocalRef::Place(dest) => dest, - LocalRef::Operand(None) => { - // Handle temporary places, specifically Operand ones, as - // they don't have allocas - return if fn_ret.is_indirect() { - // Odd, but possible, case, we have an operand temporary, - // but the calling convention has an indirect return. - let tmp = PlaceRef::alloca(bx, fn_ret.layout, "tmp_ret"); - tmp.storage_live(bx); - llargs.push(tmp.llval); - ReturnDest::IndirectOperand(tmp, index) - } else if is_intrinsic { - // Currently, intrinsics always need a location to store - // the result. so we create a temporary alloca for the - // result - let tmp = PlaceRef::alloca(bx, fn_ret.layout, "tmp_ret"); - tmp.storage_live(bx); - ReturnDest::IndirectOperand(tmp, index) - } else { - ReturnDest::DirectOperand(index) - }; - } - LocalRef::Operand(Some(_)) => { - bug!("place local already assigned to"); - } - } - } else { - self.trans_place(bx, dest) - }; - if fn_ret.is_indirect() { - if dest.align.abi() < dest.layout.align.abi() { - // Currently, MIR code generation does not create calls - // that store directly to fields of packed structs (in - // fact, the calls it creates write only to temps), - // - // If someone changes that, please update this code path - // to create a temporary. - span_bug!(self.mir.span, "can't directly store to unaligned value"); - } - llargs.push(dest.llval); - ReturnDest::Nothing - } else { - ReturnDest::Store(dest) - } - } - - fn trans_transmute(&mut self, bx: &Builder<'a, 'tcx>, - src: &mir::Operand<'tcx>, - dst: &mir::Place<'tcx>) { - if let mir::Place::Local(index) = *dst { - match self.locals[index] { - LocalRef::Place(place) => self.trans_transmute_into(bx, src, place), - LocalRef::Operand(None) => { - let dst_layout = bx.cx.layout_of(self.monomorphized_place_ty(dst)); - assert!(!dst_layout.ty.has_erasable_regions()); - let place = PlaceRef::alloca(bx, dst_layout, "transmute_temp"); - place.storage_live(bx); - self.trans_transmute_into(bx, src, place); - let op = place.load(bx); - place.storage_dead(bx); - self.locals[index] = LocalRef::Operand(Some(op)); - } - LocalRef::Operand(Some(op)) => { - assert!(op.layout.is_zst(), - "assigning to initialized SSAtemp"); - } - } - } else { - let dst = self.trans_place(bx, dst); - self.trans_transmute_into(bx, src, dst); - } - } - - fn trans_transmute_into(&mut self, bx: &Builder<'a, 'tcx>, - src: &mir::Operand<'tcx>, - dst: PlaceRef<'tcx>) { - let src = self.trans_operand(bx, src); - let llty = src.layout.llvm_type(bx.cx); - let cast_ptr = bx.pointercast(dst.llval, llty.ptr_to()); - let align = src.layout.align.min(dst.layout.align); - src.val.store(bx, PlaceRef::new_sized(cast_ptr, src.layout, align)); - } - - - // Stores the return value of a function call into it's final location. - fn store_return(&mut self, - bx: &Builder<'a, 'tcx>, - dest: ReturnDest<'tcx>, - ret_ty: &ArgType<'tcx, Ty<'tcx>>, - llval: ValueRef) { - use self::ReturnDest::*; - - match dest { - Nothing => (), - Store(dst) => ret_ty.store(bx, llval, dst), - IndirectOperand(tmp, index) => { - let op = tmp.load(bx); - tmp.storage_dead(bx); - self.locals[index] = LocalRef::Operand(Some(op)); - } - DirectOperand(index) => { - // If there is a cast, we have to store and reload. - let op = if let PassMode::Cast(_) = ret_ty.mode { - let tmp = PlaceRef::alloca(bx, ret_ty.layout, "tmp_ret"); - tmp.storage_live(bx); - ret_ty.store(bx, llval, tmp); - let op = tmp.load(bx); - tmp.storage_dead(bx); - op - } else { - OperandRef::from_immediate_or_packed_pair(bx, llval, ret_ty.layout) - }; - self.locals[index] = LocalRef::Operand(Some(op)); - } - } - } -} - -enum ReturnDest<'tcx> { - // Do nothing, the return value is indirect or ignored - Nothing, - // Store the return value to the pointer - Store(PlaceRef<'tcx>), - // Stores an indirect return value to an operand local place - IndirectOperand(PlaceRef<'tcx>, mir::Local), - // Stores a direct return value to an operand local place - DirectOperand(mir::Local) -} diff --git a/src/librustc_trans/mir/constant.rs b/src/librustc_trans/mir/constant.rs deleted file mode 100644 index a10b7c9c9f1..00000000000 --- a/src/librustc_trans/mir/constant.rs +++ /dev/null @@ -1,271 +0,0 @@ -// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -use llvm::{self, ValueRef}; -use rustc::middle::const_val::{ConstVal, ConstEvalErr}; -use rustc_mir::interpret::{read_target_uint, const_val_field}; -use rustc::hir::def_id::DefId; -use rustc::mir; -use rustc_data_structures::indexed_vec::Idx; -use rustc::mir::interpret::{GlobalId, MemoryPointer, PrimVal, Allocation, ConstValue}; -use rustc::ty::{self, Ty}; -use rustc::ty::layout::{self, HasDataLayout, LayoutOf, Scalar}; -use builder::Builder; -use common::{CodegenCx}; -use common::{C_bytes, C_struct, C_uint_big, C_undef, C_usize}; -use consts; -use type_of::LayoutLlvmExt; -use type_::Type; -use syntax::ast::Mutability; - -use super::super::callee; -use super::FunctionCx; - -pub fn primval_to_llvm(cx: &CodegenCx, - cv: PrimVal, - scalar: &Scalar, - llty: Type) -> ValueRef { - let bits = if scalar.is_bool() { 1 } else { scalar.value.size(cx).bits() }; - match cv { - PrimVal::Undef => C_undef(Type::ix(cx, bits)), - PrimVal::Bytes(b) => { - let llval = C_uint_big(Type::ix(cx, bits), b); - if scalar.value == layout::Pointer { - unsafe { llvm::LLVMConstIntToPtr(llval, llty.to_ref()) } - } else { - consts::bitcast(llval, llty) - } - }, - PrimVal::Ptr(ptr) => { - if let Some(fn_instance) = cx.tcx.interpret_interner.get_fn(ptr.alloc_id) { - callee::get_fn(cx, fn_instance) - } else { - let static_ = cx - .tcx - .interpret_interner - .get_static(ptr.alloc_id); - let base_addr = if let Some(def_id) = static_ { - assert!(cx.tcx.is_static(def_id).is_some()); - consts::get_static(cx, def_id) - } else if let Some(alloc) = cx.tcx.interpret_interner - .get_alloc(ptr.alloc_id) { - let init = const_alloc_to_llvm(cx, alloc); - if alloc.runtime_mutability == Mutability::Mutable { - consts::addr_of_mut(cx, init, alloc.align, "byte_str") - } else { - consts::addr_of(cx, init, alloc.align, "byte_str") - } - } else { - bug!("missing allocation {:?}", ptr.alloc_id); - }; - - let llval = unsafe { llvm::LLVMConstInBoundsGEP( - consts::bitcast(base_addr, Type::i8p(cx)), - &C_usize(cx, ptr.offset), - 1, - ) }; - if scalar.value != layout::Pointer { - unsafe { llvm::LLVMConstPtrToInt(llval, llty.to_ref()) } - } else { - consts::bitcast(llval, llty) - } - } - } - } -} - -fn const_value_to_llvm<'tcx>(cx: &CodegenCx<'_, 'tcx>, val: ConstValue, ty: Ty<'tcx>) -> ValueRef { - let layout = cx.layout_of(ty); - - if layout.is_zst() { - return C_undef(layout.immediate_llvm_type(cx)); - } - - match val { - ConstValue::ByVal(x) => { - let scalar = match layout.abi { - layout::Abi::Scalar(ref x) => x, - _ => bug!("const_value_to_llvm: invalid ByVal layout: {:#?}", layout) - }; - primval_to_llvm( - cx, - x, - scalar, - layout.immediate_llvm_type(cx), - ) - }, - ConstValue::ByValPair(a, b) => { - let (a_scalar, b_scalar) = match layout.abi { - layout::Abi::ScalarPair(ref a, ref b) => (a, b), - _ => bug!("const_value_to_llvm: invalid ByValPair layout: {:#?}", layout) - }; - let a_llval = primval_to_llvm( - cx, - a, - a_scalar, - layout.scalar_pair_element_llvm_type(cx, 0), - ); - let b_llval = primval_to_llvm( - cx, - b, - b_scalar, - layout.scalar_pair_element_llvm_type(cx, 1), - ); - C_struct(cx, &[a_llval, b_llval], false) - }, - ConstValue::ByRef(alloc) => const_alloc_to_llvm(cx, alloc), - } -} - -pub fn const_alloc_to_llvm(cx: &CodegenCx, alloc: &Allocation) -> ValueRef { - let mut llvals = Vec::with_capacity(alloc.relocations.len() + 1); - let layout = cx.data_layout(); - let pointer_size = layout.pointer_size.bytes() as usize; - - let mut next_offset = 0; - for (&offset, &alloc_id) in &alloc.relocations { - assert_eq!(offset as usize as u64, offset); - let offset = offset as usize; - if offset > next_offset { - llvals.push(C_bytes(cx, &alloc.bytes[next_offset..offset])); - } - let ptr_offset = read_target_uint( - layout.endian, - &alloc.bytes[offset..(offset + pointer_size)], - ).expect("const_alloc_to_llvm: could not read relocation pointer") as u64; - llvals.push(primval_to_llvm( - cx, - PrimVal::Ptr(MemoryPointer { alloc_id, offset: ptr_offset }), - &Scalar { - value: layout::Primitive::Pointer, - valid_range: 0..=!0 - }, - Type::i8p(cx) - )); - next_offset = offset + pointer_size; - } - if alloc.bytes.len() >= next_offset { - llvals.push(C_bytes(cx, &alloc.bytes[next_offset ..])); - } - - C_struct(cx, &llvals, true) -} - -pub fn trans_static_initializer<'a, 'tcx>( - cx: &CodegenCx<'a, 'tcx>, - def_id: DefId) - -> Result> -{ - let instance = ty::Instance::mono(cx.tcx, def_id); - let cid = GlobalId { - instance, - promoted: None - }; - let param_env = ty::ParamEnv::reveal_all(); - let static_ = cx.tcx.const_eval(param_env.and(cid))?; - - let val = match static_.val { - ConstVal::Value(val) => val, - _ => bug!("static const eval returned {:#?}", static_), - }; - Ok(const_value_to_llvm(cx, val, static_.ty)) -} - -impl<'a, 'tcx> FunctionCx<'a, 'tcx> { - fn const_to_const_value( - &mut self, - bx: &Builder<'a, 'tcx>, - constant: &'tcx ty::Const<'tcx>, - ) -> Result, ConstEvalErr<'tcx>> { - match constant.val { - ConstVal::Unevaluated(def_id, ref substs) => { - let tcx = bx.tcx(); - let param_env = ty::ParamEnv::reveal_all(); - let instance = ty::Instance::resolve(tcx, param_env, def_id, substs).unwrap(); - let cid = GlobalId { - instance, - promoted: None, - }; - let c = tcx.const_eval(param_env.and(cid))?; - self.const_to_const_value(bx, c) - }, - ConstVal::Value(val) => Ok(val), - } - } - - pub fn mir_constant_to_const_value( - &mut self, - bx: &Builder<'a, 'tcx>, - constant: &mir::Constant<'tcx>, - ) -> Result, ConstEvalErr<'tcx>> { - match constant.literal { - mir::Literal::Promoted { index } => { - let param_env = ty::ParamEnv::reveal_all(); - let cid = mir::interpret::GlobalId { - instance: self.instance, - promoted: Some(index), - }; - bx.tcx().const_eval(param_env.and(cid)) - } - mir::Literal::Value { value } => { - Ok(self.monomorphize(&value)) - } - }.and_then(|c| self.const_to_const_value(bx, c)) - } - - /// process constant containing SIMD shuffle indices - pub fn simd_shuffle_indices( - &mut self, - bx: &Builder<'a, 'tcx>, - constant: &mir::Constant<'tcx>, - ) -> (ValueRef, Ty<'tcx>) { - self.mir_constant_to_const_value(bx, constant) - .and_then(|c| { - let field_ty = constant.ty.builtin_index().unwrap(); - let fields = match constant.ty.sty { - ty::TyArray(_, n) => n.unwrap_usize(bx.tcx()), - ref other => bug!("invalid simd shuffle type: {}", other), - }; - let values: Result, _> = (0..fields).map(|field| { - let field = const_val_field( - bx.tcx(), - ty::ParamEnv::reveal_all(), - self.instance, - None, - mir::Field::new(field as usize), - c, - constant.ty, - )?; - if let Some(prim) = field.to_primval() { - let layout = bx.cx.layout_of(field_ty); - let scalar = match layout.abi { - layout::Abi::Scalar(ref x) => x, - _ => bug!("from_const: invalid ByVal layout: {:#?}", layout) - }; - Ok(primval_to_llvm( - bx.cx, prim, scalar, - layout.immediate_llvm_type(bx.cx), - )) - } else { - bug!("simd shuffle field {:?}", field) - } - }).collect(); - let llval = C_struct(bx.cx, &values?, false); - Ok((llval, constant.ty)) - }) - .unwrap_or_else(|e| { - e.report(bx.tcx(), constant.span, "shuffle_indices"); - // We've errored, so we don't have to produce working code. - let ty = self.monomorphize(&constant.ty); - let llty = bx.cx.layout_of(ty).llvm_type(bx.cx); - (C_undef(llty), ty) - }) - } -} diff --git a/src/librustc_trans/mir/mod.rs b/src/librustc_trans/mir/mod.rs deleted file mode 100644 index 8ea0983075d..00000000000 --- a/src/librustc_trans/mir/mod.rs +++ /dev/null @@ -1,652 +0,0 @@ -// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -use common::{C_i32, C_null}; -use libc::c_uint; -use llvm::{self, ValueRef, BasicBlockRef}; -use llvm::debuginfo::DIScope; -use rustc::ty::{self, Ty, TypeFoldable, UpvarSubsts}; -use rustc::ty::layout::{LayoutOf, TyLayout}; -use rustc::mir::{self, Mir}; -use rustc::ty::subst::Substs; -use rustc::session::config::FullDebugInfo; -use base; -use builder::Builder; -use common::{CodegenCx, Funclet}; -use debuginfo::{self, declare_local, VariableAccess, VariableKind, FunctionDebugContext}; -use monomorphize::Instance; -use abi::{ArgAttribute, ArgTypeExt, FnType, FnTypeExt, PassMode}; -use type_::Type; - -use syntax_pos::{DUMMY_SP, NO_EXPANSION, BytePos, Span}; -use syntax::symbol::keywords; - -use std::iter; - -use rustc_data_structures::bitvec::BitVector; -use rustc_data_structures::indexed_vec::{IndexVec, Idx}; - -pub use self::constant::trans_static_initializer; - -use self::analyze::CleanupKind; -use self::place::PlaceRef; -use rustc::mir::traversal; - -use self::operand::{OperandRef, OperandValue}; - -/// Master context for translating MIR. -pub struct FunctionCx<'a, 'tcx:'a> { - instance: Instance<'tcx>, - - mir: &'a mir::Mir<'tcx>, - - debug_context: debuginfo::FunctionDebugContext, - - llfn: ValueRef, - - cx: &'a CodegenCx<'a, 'tcx>, - - fn_ty: FnType<'tcx, Ty<'tcx>>, - - /// When unwinding is initiated, we have to store this personality - /// value somewhere so that we can load it and re-use it in the - /// resume instruction. The personality is (afaik) some kind of - /// value used for C++ unwinding, which must filter by type: we - /// don't really care about it very much. Anyway, this value - /// contains an alloca into which the personality is stored and - /// then later loaded when generating the DIVERGE_BLOCK. - personality_slot: Option>, - - /// A `Block` for each MIR `BasicBlock` - blocks: IndexVec, - - /// The funclet status of each basic block - cleanup_kinds: IndexVec, - - /// When targeting MSVC, this stores the cleanup info for each funclet - /// BB. This is initialized as we compute the funclets' head block in RPO. - funclets: &'a IndexVec>, - - /// This stores the landing-pad block for a given BB, computed lazily on GNU - /// and eagerly on MSVC. - landing_pads: IndexVec>, - - /// Cached unreachable block - unreachable_block: Option, - - /// The location where each MIR arg/var/tmp/ret is stored. This is - /// usually an `PlaceRef` representing an alloca, but not always: - /// sometimes we can skip the alloca and just store the value - /// directly using an `OperandRef`, which makes for tighter LLVM - /// IR. The conditions for using an `OperandRef` are as follows: - /// - /// - the type of the local must be judged "immediate" by `is_llvm_immediate` - /// - the operand must never be referenced indirectly - /// - we should not take its address using the `&` operator - /// - nor should it appear in a place path like `tmp.a` - /// - the operand must be defined by an rvalue that can generate immediate - /// values - /// - /// Avoiding allocs can also be important for certain intrinsics, - /// notably `expect`. - locals: IndexVec>, - - /// Debug information for MIR scopes. - scopes: IndexVec, - - /// If this function is being monomorphized, this contains the type substitutions used. - param_substs: &'tcx Substs<'tcx>, -} - -impl<'a, 'tcx> FunctionCx<'a, 'tcx> { - pub fn monomorphize(&self, value: &T) -> T - where T: TypeFoldable<'tcx> - { - self.cx.tcx.subst_and_normalize_erasing_regions( - self.param_substs, - ty::ParamEnv::reveal_all(), - value, - ) - } - - pub fn set_debug_loc(&mut self, bx: &Builder, source_info: mir::SourceInfo) { - let (scope, span) = self.debug_loc(source_info); - debuginfo::set_source_location(&self.debug_context, bx, scope, span); - } - - pub fn debug_loc(&mut self, source_info: mir::SourceInfo) -> (DIScope, Span) { - // Bail out if debug info emission is not enabled. - match self.debug_context { - FunctionDebugContext::DebugInfoDisabled | - FunctionDebugContext::FunctionWithoutDebugInfo => { - return (self.scopes[source_info.scope].scope_metadata, source_info.span); - } - FunctionDebugContext::RegularContext(_) =>{} - } - - // In order to have a good line stepping behavior in debugger, we overwrite debug - // locations of macro expansions with that of the outermost expansion site - // (unless the crate is being compiled with `-Z debug-macros`). - if source_info.span.ctxt() == NO_EXPANSION || - self.cx.sess().opts.debugging_opts.debug_macros { - let scope = self.scope_metadata_for_loc(source_info.scope, source_info.span.lo()); - (scope, source_info.span) - } else { - // Walk up the macro expansion chain until we reach a non-expanded span. - // We also stop at the function body level because no line stepping can occur - // at the level above that. - let mut span = source_info.span; - while span.ctxt() != NO_EXPANSION && span.ctxt() != self.mir.span.ctxt() { - if let Some(info) = span.ctxt().outer().expn_info() { - span = info.call_site; - } else { - break; - } - } - let scope = self.scope_metadata_for_loc(source_info.scope, span.lo()); - // Use span of the outermost expansion site, while keeping the original lexical scope. - (scope, span) - } - } - - // DILocations inherit source file name from the parent DIScope. Due to macro expansions - // it may so happen that the current span belongs to a different file than the DIScope - // corresponding to span's containing visibility scope. If so, we need to create a DIScope - // "extension" into that file. - fn scope_metadata_for_loc(&self, scope_id: mir::VisibilityScope, pos: BytePos) - -> llvm::debuginfo::DIScope { - let scope_metadata = self.scopes[scope_id].scope_metadata; - if pos < self.scopes[scope_id].file_start_pos || - pos >= self.scopes[scope_id].file_end_pos { - let cm = self.cx.sess().codemap(); - let defining_crate = self.debug_context.get_ref(DUMMY_SP).defining_crate; - debuginfo::extend_scope_to_file(self.cx, - scope_metadata, - &cm.lookup_char_pos(pos).file, - defining_crate) - } else { - scope_metadata - } - } -} - -enum LocalRef<'tcx> { - Place(PlaceRef<'tcx>), - Operand(Option>), -} - -impl<'a, 'tcx> LocalRef<'tcx> { - fn new_operand(cx: &CodegenCx<'a, 'tcx>, layout: TyLayout<'tcx>) -> LocalRef<'tcx> { - if layout.is_zst() { - // Zero-size temporaries aren't always initialized, which - // doesn't matter because they don't contain data, but - // we need something in the operand. - LocalRef::Operand(Some(OperandRef::new_zst(cx, layout))) - } else { - LocalRef::Operand(None) - } - } -} - -/////////////////////////////////////////////////////////////////////////// - -pub fn trans_mir<'a, 'tcx: 'a>( - cx: &'a CodegenCx<'a, 'tcx>, - llfn: ValueRef, - mir: &'a Mir<'tcx>, - instance: Instance<'tcx>, - sig: ty::FnSig<'tcx>, -) { - let fn_ty = FnType::new(cx, sig, &[]); - debug!("fn_ty: {:?}", fn_ty); - let debug_context = - debuginfo::create_function_debug_context(cx, instance, sig, llfn, mir); - let bx = Builder::new_block(cx, llfn, "start"); - - if mir.basic_blocks().iter().any(|bb| bb.is_cleanup) { - bx.set_personality_fn(cx.eh_personality()); - } - - let cleanup_kinds = analyze::cleanup_kinds(&mir); - // Allocate a `Block` for every basic block, except - // the start block, if nothing loops back to it. - let reentrant_start_block = !mir.predecessors_for(mir::START_BLOCK).is_empty(); - let block_bxs: IndexVec = - mir.basic_blocks().indices().map(|bb| { - if bb == mir::START_BLOCK && !reentrant_start_block { - bx.llbb() - } else { - bx.build_sibling_block(&format!("{:?}", bb)).llbb() - } - }).collect(); - - // Compute debuginfo scopes from MIR scopes. - let scopes = debuginfo::create_mir_scopes(cx, mir, &debug_context); - let (landing_pads, funclets) = create_funclets(mir, &bx, &cleanup_kinds, &block_bxs); - - let mut fx = FunctionCx { - instance, - mir, - llfn, - fn_ty, - cx, - personality_slot: None, - blocks: block_bxs, - unreachable_block: None, - cleanup_kinds, - landing_pads, - funclets: &funclets, - scopes, - locals: IndexVec::new(), - debug_context, - param_substs: { - assert!(!instance.substs.needs_infer()); - instance.substs - }, - }; - - let memory_locals = analyze::non_ssa_locals(&fx); - - // Allocate variable and temp allocas - fx.locals = { - let args = arg_local_refs(&bx, &fx, &fx.scopes, &memory_locals); - - let mut allocate_local = |local| { - let decl = &mir.local_decls[local]; - let layout = bx.cx.layout_of(fx.monomorphize(&decl.ty)); - assert!(!layout.ty.has_erasable_regions()); - - if let Some(name) = decl.name { - // User variable - let debug_scope = fx.scopes[decl.source_info.scope]; - let dbg = debug_scope.is_valid() && bx.sess().opts.debuginfo == FullDebugInfo; - - if !memory_locals.contains(local.index()) && !dbg { - debug!("alloc: {:?} ({}) -> operand", local, name); - return LocalRef::new_operand(bx.cx, layout); - } - - debug!("alloc: {:?} ({}) -> place", local, name); - let place = PlaceRef::alloca(&bx, layout, &name.as_str()); - if dbg { - let (scope, span) = fx.debug_loc(decl.source_info); - declare_local(&bx, &fx.debug_context, name, layout.ty, scope, - VariableAccess::DirectVariable { alloca: place.llval }, - VariableKind::LocalVariable, span); - } - LocalRef::Place(place) - } else { - // Temporary or return place - if local == mir::RETURN_PLACE && fx.fn_ty.ret.is_indirect() { - debug!("alloc: {:?} (return place) -> place", local); - let llretptr = llvm::get_param(llfn, 0); - LocalRef::Place(PlaceRef::new_sized(llretptr, layout, layout.align)) - } else if memory_locals.contains(local.index()) { - debug!("alloc: {:?} -> place", local); - LocalRef::Place(PlaceRef::alloca(&bx, layout, &format!("{:?}", local))) - } else { - // If this is an immediate local, we do not create an - // alloca in advance. Instead we wait until we see the - // definition and update the operand there. - debug!("alloc: {:?} -> operand", local); - LocalRef::new_operand(bx.cx, layout) - } - } - }; - - let retptr = allocate_local(mir::RETURN_PLACE); - iter::once(retptr) - .chain(args.into_iter()) - .chain(mir.vars_and_temps_iter().map(allocate_local)) - .collect() - }; - - // Branch to the START block, if it's not the entry block. - if reentrant_start_block { - bx.br(fx.blocks[mir::START_BLOCK]); - } - - // Up until here, IR instructions for this function have explicitly not been annotated with - // source code location, so we don't step into call setup code. From here on, source location - // emitting should be enabled. - debuginfo::start_emitting_source_locations(&fx.debug_context); - - let rpo = traversal::reverse_postorder(&mir); - let mut visited = BitVector::new(mir.basic_blocks().len()); - - // Translate the body of each block using reverse postorder - for (bb, _) in rpo { - visited.insert(bb.index()); - fx.trans_block(bb); - } - - // Remove blocks that haven't been visited, or have no - // predecessors. - for bb in mir.basic_blocks().indices() { - // Unreachable block - if !visited.contains(bb.index()) { - debug!("trans_mir: block {:?} was not visited", bb); - unsafe { - llvm::LLVMDeleteBasicBlock(fx.blocks[bb]); - } - } - } -} - -fn create_funclets<'a, 'tcx>( - mir: &'a Mir<'tcx>, - bx: &Builder<'a, 'tcx>, - cleanup_kinds: &IndexVec, - block_bxs: &IndexVec) - -> (IndexVec>, - IndexVec>) -{ - block_bxs.iter_enumerated().zip(cleanup_kinds).map(|((bb, &llbb), cleanup_kind)| { - match *cleanup_kind { - CleanupKind::Funclet if base::wants_msvc_seh(bx.sess()) => {} - _ => return (None, None) - } - - let cleanup; - let ret_llbb; - match mir[bb].terminator.as_ref().map(|t| &t.kind) { - // This is a basic block that we're aborting the program for, - // notably in an `extern` function. These basic blocks are inserted - // so that we assert that `extern` functions do indeed not panic, - // and if they do we abort the process. - // - // On MSVC these are tricky though (where we're doing funclets). If - // we were to do a cleanuppad (like below) the normal functions like - // `longjmp` would trigger the abort logic, terminating the - // program. Instead we insert the equivalent of `catch(...)` for C++ - // which magically doesn't trigger when `longjmp` files over this - // frame. - // - // Lots more discussion can be found on #48251 but this codegen is - // modeled after clang's for: - // - // try { - // foo(); - // } catch (...) { - // bar(); - // } - Some(&mir::TerminatorKind::Abort) => { - let cs_bx = bx.build_sibling_block(&format!("cs_funclet{:?}", bb)); - let cp_bx = bx.build_sibling_block(&format!("cp_funclet{:?}", bb)); - ret_llbb = cs_bx.llbb(); - - let cs = cs_bx.catch_switch(None, None, 1); - cs_bx.add_handler(cs, cp_bx.llbb()); - - // The "null" here is actually a RTTI type descriptor for the - // C++ personality function, but `catch (...)` has no type so - // it's null. The 64 here is actually a bitfield which - // represents that this is a catch-all block. - let null = C_null(Type::i8p(bx.cx)); - let sixty_four = C_i32(bx.cx, 64); - cleanup = cp_bx.catch_pad(cs, &[null, sixty_four, null]); - cp_bx.br(llbb); - } - _ => { - let cleanup_bx = bx.build_sibling_block(&format!("funclet_{:?}", bb)); - ret_llbb = cleanup_bx.llbb(); - cleanup = cleanup_bx.cleanup_pad(None, &[]); - cleanup_bx.br(llbb); - } - }; - - (Some(ret_llbb), Some(Funclet::new(cleanup))) - }).unzip() -} - -/// Produce, for each argument, a `ValueRef` pointing at the -/// argument's value. As arguments are places, these are always -/// indirect. -fn arg_local_refs<'a, 'tcx>(bx: &Builder<'a, 'tcx>, - fx: &FunctionCx<'a, 'tcx>, - scopes: &IndexVec, - memory_locals: &BitVector) - -> Vec> { - let mir = fx.mir; - let tcx = bx.tcx(); - let mut idx = 0; - let mut llarg_idx = fx.fn_ty.ret.is_indirect() as usize; - - // Get the argument scope, if it exists and if we need it. - let arg_scope = scopes[mir::ARGUMENT_VISIBILITY_SCOPE]; - let arg_scope = if arg_scope.is_valid() && bx.sess().opts.debuginfo == FullDebugInfo { - Some(arg_scope.scope_metadata) - } else { - None - }; - - let deref_op = unsafe { - [llvm::LLVMRustDIBuilderCreateOpDeref()] - }; - - mir.args_iter().enumerate().map(|(arg_index, local)| { - let arg_decl = &mir.local_decls[local]; - - let name = if let Some(name) = arg_decl.name { - name.as_str().to_string() - } else { - format!("arg{}", arg_index) - }; - - if Some(local) == mir.spread_arg { - // This argument (e.g. the last argument in the "rust-call" ABI) - // is a tuple that was spread at the ABI level and now we have - // to reconstruct it into a tuple local variable, from multiple - // individual LLVM function arguments. - - let arg_ty = fx.monomorphize(&arg_decl.ty); - let tupled_arg_tys = match arg_ty.sty { - ty::TyTuple(ref tys) => tys, - _ => bug!("spread argument isn't a tuple?!") - }; - - let place = PlaceRef::alloca(bx, bx.cx.layout_of(arg_ty), &name); - for i in 0..tupled_arg_tys.len() { - let arg = &fx.fn_ty.args[idx]; - idx += 1; - if arg.pad.is_some() { - llarg_idx += 1; - } - arg.store_fn_arg(bx, &mut llarg_idx, place.project_field(bx, i)); - } - - // Now that we have one alloca that contains the aggregate value, - // we can create one debuginfo entry for the argument. - arg_scope.map(|scope| { - let variable_access = VariableAccess::DirectVariable { - alloca: place.llval - }; - declare_local( - bx, - &fx.debug_context, - arg_decl.name.unwrap_or(keywords::Invalid.name()), - arg_ty, scope, - variable_access, - VariableKind::ArgumentVariable(arg_index + 1), - DUMMY_SP - ); - }); - - return LocalRef::Place(place); - } - - let arg = &fx.fn_ty.args[idx]; - idx += 1; - if arg.pad.is_some() { - llarg_idx += 1; - } - - if arg_scope.is_none() && !memory_locals.contains(local.index()) { - // We don't have to cast or keep the argument in the alloca. - // FIXME(eddyb): We should figure out how to use llvm.dbg.value instead - // of putting everything in allocas just so we can use llvm.dbg.declare. - let local = |op| LocalRef::Operand(Some(op)); - match arg.mode { - PassMode::Ignore => { - return local(OperandRef::new_zst(bx.cx, arg.layout)); - } - PassMode::Direct(_) => { - let llarg = llvm::get_param(bx.llfn(), llarg_idx as c_uint); - bx.set_value_name(llarg, &name); - llarg_idx += 1; - return local( - OperandRef::from_immediate_or_packed_pair(bx, llarg, arg.layout)); - } - PassMode::Pair(..) => { - let a = llvm::get_param(bx.llfn(), llarg_idx as c_uint); - bx.set_value_name(a, &(name.clone() + ".0")); - llarg_idx += 1; - - let b = llvm::get_param(bx.llfn(), llarg_idx as c_uint); - bx.set_value_name(b, &(name + ".1")); - llarg_idx += 1; - - return local(OperandRef { - val: OperandValue::Pair(a, b), - layout: arg.layout - }); - } - _ => {} - } - } - - let place = if arg.is_indirect() { - // Don't copy an indirect argument to an alloca, the caller - // already put it in a temporary alloca and gave it up. - // FIXME: lifetimes - let llarg = llvm::get_param(bx.llfn(), llarg_idx as c_uint); - bx.set_value_name(llarg, &name); - llarg_idx += 1; - PlaceRef::new_sized(llarg, arg.layout, arg.layout.align) - } else { - let tmp = PlaceRef::alloca(bx, arg.layout, &name); - arg.store_fn_arg(bx, &mut llarg_idx, tmp); - tmp - }; - arg_scope.map(|scope| { - // Is this a regular argument? - if arg_index > 0 || mir.upvar_decls.is_empty() { - // The Rust ABI passes indirect variables using a pointer and a manual copy, so we - // need to insert a deref here, but the C ABI uses a pointer and a copy using the - // byval attribute, for which LLVM does the deref itself, so we must not add it. - // Starting with D31439 in LLVM 5, it *always* does the deref itself. - let mut variable_access = VariableAccess::DirectVariable { - alloca: place.llval - }; - if unsafe { llvm::LLVMRustVersionMajor() < 5 } { - if let PassMode::Indirect(ref attrs) = arg.mode { - if !attrs.contains(ArgAttribute::ByVal) { - variable_access = VariableAccess::IndirectVariable { - alloca: place.llval, - address_operations: &deref_op, - }; - } - } - } - - declare_local( - bx, - &fx.debug_context, - arg_decl.name.unwrap_or(keywords::Invalid.name()), - arg.layout.ty, - scope, - variable_access, - VariableKind::ArgumentVariable(arg_index + 1), - DUMMY_SP - ); - return; - } - - // Or is it the closure environment? - let (closure_layout, env_ref) = match arg.layout.ty.sty { - ty::TyRawPtr(ty::TypeAndMut { ty, .. }) | - ty::TyRef(_, ty, _) => (bx.cx.layout_of(ty), true), - _ => (arg.layout, false) - }; - - let (def_id, upvar_substs) = match closure_layout.ty.sty { - ty::TyClosure(def_id, substs) => (def_id, UpvarSubsts::Closure(substs)), - ty::TyGenerator(def_id, substs, _) => (def_id, UpvarSubsts::Generator(substs)), - _ => bug!("upvar_decls with non-closure arg0 type `{}`", closure_layout.ty) - }; - let upvar_tys = upvar_substs.upvar_tys(def_id, tcx); - - // Store the pointer to closure data in an alloca for debuginfo - // because that's what the llvm.dbg.declare intrinsic expects. - - // FIXME(eddyb) this shouldn't be necessary but SROA seems to - // mishandle DW_OP_plus not preceded by DW_OP_deref, i.e. it - // doesn't actually strip the offset when splitting the closure - // environment into its components so it ends up out of bounds. - let env_ptr = if !env_ref { - let scratch = PlaceRef::alloca(bx, - bx.cx.layout_of(tcx.mk_mut_ptr(arg.layout.ty)), - "__debuginfo_env_ptr"); - bx.store(place.llval, scratch.llval, scratch.align); - scratch.llval - } else { - place.llval - }; - - for (i, (decl, ty)) in mir.upvar_decls.iter().zip(upvar_tys).enumerate() { - let byte_offset_of_var_in_env = closure_layout.fields.offset(i).bytes(); - - let ops = unsafe { - [llvm::LLVMRustDIBuilderCreateOpDeref(), - llvm::LLVMRustDIBuilderCreateOpPlusUconst(), - byte_offset_of_var_in_env as i64, - llvm::LLVMRustDIBuilderCreateOpDeref()] - }; - - // The environment and the capture can each be indirect. - - // FIXME(eddyb) see above why we have to keep - // a pointer in an alloca for debuginfo atm. - let mut ops = if env_ref || true { &ops[..] } else { &ops[1..] }; - - let ty = if let (true, &ty::TyRef(_, ty, _)) = (decl.by_ref, &ty.sty) { - ty - } else { - ops = &ops[..ops.len() - 1]; - ty - }; - - let variable_access = VariableAccess::IndirectVariable { - alloca: env_ptr, - address_operations: &ops - }; - declare_local( - bx, - &fx.debug_context, - decl.debug_name, - ty, - scope, - variable_access, - VariableKind::CapturedVariable, - DUMMY_SP - ); - } - }); - LocalRef::Place(place) - }).collect() -} - -mod analyze; -mod block; -mod constant; -pub mod place; -pub mod operand; -mod rvalue; -mod statement; diff --git a/src/librustc_trans/mir/operand.rs b/src/librustc_trans/mir/operand.rs deleted file mode 100644 index be14da1a195..00000000000 --- a/src/librustc_trans/mir/operand.rs +++ /dev/null @@ -1,427 +0,0 @@ -// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -use llvm::ValueRef; -use rustc::middle::const_val::ConstEvalErr; -use rustc::mir; -use rustc::mir::interpret::ConstValue; -use rustc::ty; -use rustc::ty::layout::{self, Align, LayoutOf, TyLayout}; -use rustc_data_structures::indexed_vec::Idx; - -use base; -use common::{self, CodegenCx, C_null, C_undef, C_usize}; -use builder::{Builder, MemFlags}; -use value::Value; -use type_of::LayoutLlvmExt; -use type_::Type; -use consts; - -use std::fmt; -use std::ptr; - -use super::{FunctionCx, LocalRef}; -use super::constant::{primval_to_llvm, const_alloc_to_llvm}; -use super::place::PlaceRef; - -/// The representation of a Rust value. The enum variant is in fact -/// uniquely determined by the value's type, but is kept as a -/// safety check. -#[derive(Copy, Clone)] -pub enum OperandValue { - /// A reference to the actual operand. The data is guaranteed - /// to be valid for the operand's lifetime. - Ref(ValueRef, Align), - /// A single LLVM value. - Immediate(ValueRef), - /// A pair of immediate LLVM values. Used by fat pointers too. - Pair(ValueRef, ValueRef) -} - -impl fmt::Debug for OperandValue { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match *self { - OperandValue::Ref(r, align) => { - write!(f, "Ref({:?}, {:?})", Value(r), align) - } - OperandValue::Immediate(i) => { - write!(f, "Immediate({:?})", Value(i)) - } - OperandValue::Pair(a, b) => { - write!(f, "Pair({:?}, {:?})", Value(a), Value(b)) - } - } - } -} - -/// An `OperandRef` is an "SSA" reference to a Rust value, along with -/// its type. -/// -/// NOTE: unless you know a value's type exactly, you should not -/// generate LLVM opcodes acting on it and instead act via methods, -/// to avoid nasty edge cases. In particular, using `Builder::store` -/// directly is sure to cause problems -- use `OperandRef::store` -/// instead. -#[derive(Copy, Clone)] -pub struct OperandRef<'tcx> { - // The value. - pub val: OperandValue, - - // The layout of value, based on its Rust type. - pub layout: TyLayout<'tcx>, -} - -impl<'tcx> fmt::Debug for OperandRef<'tcx> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "OperandRef({:?} @ {:?})", self.val, self.layout) - } -} - -impl<'a, 'tcx> OperandRef<'tcx> { - pub fn new_zst(cx: &CodegenCx<'a, 'tcx>, - layout: TyLayout<'tcx>) -> OperandRef<'tcx> { - assert!(layout.is_zst()); - OperandRef { - val: OperandValue::Immediate(C_undef(layout.immediate_llvm_type(cx))), - layout - } - } - - pub fn from_const(bx: &Builder<'a, 'tcx>, - val: ConstValue<'tcx>, - ty: ty::Ty<'tcx>) - -> Result, ConstEvalErr<'tcx>> { - let layout = bx.cx.layout_of(ty); - - if layout.is_zst() { - return Ok(OperandRef::new_zst(bx.cx, layout)); - } - - let val = match val { - ConstValue::ByVal(x) => { - let scalar = match layout.abi { - layout::Abi::Scalar(ref x) => x, - _ => bug!("from_const: invalid ByVal layout: {:#?}", layout) - }; - let llval = primval_to_llvm( - bx.cx, - x, - scalar, - layout.immediate_llvm_type(bx.cx), - ); - OperandValue::Immediate(llval) - }, - ConstValue::ByValPair(a, b) => { - let (a_scalar, b_scalar) = match layout.abi { - layout::Abi::ScalarPair(ref a, ref b) => (a, b), - _ => bug!("from_const: invalid ByValPair layout: {:#?}", layout) - }; - let a_llval = primval_to_llvm( - bx.cx, - a, - a_scalar, - layout.scalar_pair_element_llvm_type(bx.cx, 0), - ); - let b_llval = primval_to_llvm( - bx.cx, - b, - b_scalar, - layout.scalar_pair_element_llvm_type(bx.cx, 1), - ); - OperandValue::Pair(a_llval, b_llval) - }, - ConstValue::ByRef(alloc) => { - let init = const_alloc_to_llvm(bx.cx, alloc); - let llval = consts::addr_of(bx.cx, init, layout.align, "byte_str"); - let llval = consts::bitcast(llval, layout.llvm_type(bx.cx).ptr_to()); - return Ok(PlaceRef::new_sized(llval, layout, alloc.align).load(bx)); - }, - }; - - Ok(OperandRef { - val, - layout - }) - } - - /// Asserts that this operand refers to a scalar and returns - /// a reference to its value. - pub fn immediate(self) -> ValueRef { - match self.val { - OperandValue::Immediate(s) => s, - _ => bug!("not immediate: {:?}", self) - } - } - - pub fn deref(self, cx: &CodegenCx<'a, 'tcx>) -> PlaceRef<'tcx> { - let projected_ty = self.layout.ty.builtin_deref(true) - .unwrap_or_else(|| bug!("deref of non-pointer {:?}", self)).ty; - let (llptr, llextra) = match self.val { - OperandValue::Immediate(llptr) => (llptr, ptr::null_mut()), - OperandValue::Pair(llptr, llextra) => (llptr, llextra), - OperandValue::Ref(..) => bug!("Deref of by-Ref operand {:?}", self) - }; - let layout = cx.layout_of(projected_ty); - PlaceRef { - llval: llptr, - llextra, - layout, - align: layout.align, - } - } - - /// If this operand is a `Pair`, we return an aggregate with the two values. - /// For other cases, see `immediate`. - pub fn immediate_or_packed_pair(self, bx: &Builder<'a, 'tcx>) -> ValueRef { - if let OperandValue::Pair(a, b) = self.val { - let llty = self.layout.llvm_type(bx.cx); - debug!("Operand::immediate_or_packed_pair: packing {:?} into {:?}", - self, llty); - // Reconstruct the immediate aggregate. - let mut llpair = C_undef(llty); - llpair = bx.insert_value(llpair, a, 0); - llpair = bx.insert_value(llpair, b, 1); - llpair - } else { - self.immediate() - } - } - - /// If the type is a pair, we return a `Pair`, otherwise, an `Immediate`. - pub fn from_immediate_or_packed_pair(bx: &Builder<'a, 'tcx>, - llval: ValueRef, - layout: TyLayout<'tcx>) - -> OperandRef<'tcx> { - let val = if layout.is_llvm_scalar_pair() { - debug!("Operand::from_immediate_or_packed_pair: unpacking {:?} @ {:?}", - llval, layout); - - // Deconstruct the immediate aggregate. - OperandValue::Pair(bx.extract_value(llval, 0), - bx.extract_value(llval, 1)) - } else { - OperandValue::Immediate(llval) - }; - OperandRef { val, layout } - } - - pub fn extract_field(&self, bx: &Builder<'a, 'tcx>, i: usize) -> OperandRef<'tcx> { - let field = self.layout.field(bx.cx, i); - let offset = self.layout.fields.offset(i); - - let mut val = match (self.val, &self.layout.abi) { - // If the field is ZST, it has no data. - _ if field.is_zst() => { - return OperandRef::new_zst(bx.cx, field); - } - - // Newtype of a scalar, scalar pair or vector. - (OperandValue::Immediate(_), _) | - (OperandValue::Pair(..), _) if field.size == self.layout.size => { - assert_eq!(offset.bytes(), 0); - self.val - } - - // Extract a scalar component from a pair. - (OperandValue::Pair(a_llval, b_llval), &layout::Abi::ScalarPair(ref a, ref b)) => { - if offset.bytes() == 0 { - assert_eq!(field.size, a.value.size(bx.cx)); - OperandValue::Immediate(a_llval) - } else { - assert_eq!(offset, a.value.size(bx.cx) - .abi_align(b.value.align(bx.cx))); - assert_eq!(field.size, b.value.size(bx.cx)); - OperandValue::Immediate(b_llval) - } - } - - // `#[repr(simd)]` types are also immediate. - (OperandValue::Immediate(llval), &layout::Abi::Vector { .. }) => { - OperandValue::Immediate( - bx.extract_element(llval, C_usize(bx.cx, i as u64))) - } - - _ => bug!("OperandRef::extract_field({:?}): not applicable", self) - }; - - // HACK(eddyb) have to bitcast pointers until LLVM removes pointee types. - match val { - OperandValue::Immediate(ref mut llval) => { - *llval = bx.bitcast(*llval, field.immediate_llvm_type(bx.cx)); - } - OperandValue::Pair(ref mut a, ref mut b) => { - *a = bx.bitcast(*a, field.scalar_pair_element_llvm_type(bx.cx, 0)); - *b = bx.bitcast(*b, field.scalar_pair_element_llvm_type(bx.cx, 1)); - } - OperandValue::Ref(..) => bug!() - } - - OperandRef { - val, - layout: field - } - } -} - -impl<'a, 'tcx> OperandValue { - pub fn store(self, bx: &Builder<'a, 'tcx>, dest: PlaceRef<'tcx>) { - self.store_with_flags(bx, dest, MemFlags::empty()); - } - - pub fn volatile_store(self, bx: &Builder<'a, 'tcx>, dest: PlaceRef<'tcx>) { - self.store_with_flags(bx, dest, MemFlags::VOLATILE); - } - - pub fn nontemporal_store(self, bx: &Builder<'a, 'tcx>, dest: PlaceRef<'tcx>) { - self.store_with_flags(bx, dest, MemFlags::NONTEMPORAL); - } - - fn store_with_flags(self, bx: &Builder<'a, 'tcx>, dest: PlaceRef<'tcx>, flags: MemFlags) { - debug!("OperandRef::store: operand={:?}, dest={:?}", self, dest); - // Avoid generating stores of zero-sized values, because the only way to have a zero-sized - // value is through `undef`, and store itself is useless. - if dest.layout.is_zst() { - return; - } - match self { - OperandValue::Ref(r, source_align) => { - base::memcpy_ty(bx, dest.llval, r, dest.layout, - source_align.min(dest.align), flags) - } - OperandValue::Immediate(s) => { - let val = base::from_immediate(bx, s); - bx.store_with_flags(val, dest.llval, dest.align, flags); - } - OperandValue::Pair(a, b) => { - for (i, &x) in [a, b].iter().enumerate() { - let mut llptr = bx.struct_gep(dest.llval, i as u64); - // Make sure to always store i1 as i8. - if common::val_ty(x) == Type::i1(bx.cx) { - llptr = bx.pointercast(llptr, Type::i8p(bx.cx)); - } - let val = base::from_immediate(bx, x); - bx.store_with_flags(val, llptr, dest.align, flags); - } - } - } - } -} - -impl<'a, 'tcx> FunctionCx<'a, 'tcx> { - fn maybe_trans_consume_direct(&mut self, - bx: &Builder<'a, 'tcx>, - place: &mir::Place<'tcx>) - -> Option> - { - debug!("maybe_trans_consume_direct(place={:?})", place); - - // watch out for locals that do not have an - // alloca; they are handled somewhat differently - if let mir::Place::Local(index) = *place { - match self.locals[index] { - LocalRef::Operand(Some(o)) => { - return Some(o); - } - LocalRef::Operand(None) => { - bug!("use of {:?} before def", place); - } - LocalRef::Place(..) => { - // use path below - } - } - } - - // Moves out of scalar and scalar pair fields are trivial. - if let &mir::Place::Projection(ref proj) = place { - if let Some(o) = self.maybe_trans_consume_direct(bx, &proj.base) { - match proj.elem { - mir::ProjectionElem::Field(ref f, _) => { - return Some(o.extract_field(bx, f.index())); - } - mir::ProjectionElem::Index(_) | - mir::ProjectionElem::ConstantIndex { .. } => { - // ZSTs don't require any actual memory access. - // FIXME(eddyb) deduplicate this with the identical - // checks in `trans_consume` and `extract_field`. - let elem = o.layout.field(bx.cx, 0); - if elem.is_zst() { - return Some(OperandRef::new_zst(bx.cx, elem)); - } - } - _ => {} - } - } - } - - None - } - - pub fn trans_consume(&mut self, - bx: &Builder<'a, 'tcx>, - place: &mir::Place<'tcx>) - -> OperandRef<'tcx> - { - debug!("trans_consume(place={:?})", place); - - let ty = self.monomorphized_place_ty(place); - let layout = bx.cx.layout_of(ty); - - // ZSTs don't require any actual memory access. - if layout.is_zst() { - return OperandRef::new_zst(bx.cx, layout); - } - - if let Some(o) = self.maybe_trans_consume_direct(bx, place) { - return o; - } - - // for most places, to consume them we just load them - // out from their home - self.trans_place(bx, place).load(bx) - } - - pub fn trans_operand(&mut self, - bx: &Builder<'a, 'tcx>, - operand: &mir::Operand<'tcx>) - -> OperandRef<'tcx> - { - debug!("trans_operand(operand={:?})", operand); - - match *operand { - mir::Operand::Copy(ref place) | - mir::Operand::Move(ref place) => { - self.trans_consume(bx, place) - } - - mir::Operand::Constant(ref constant) => { - let ty = self.monomorphize(&constant.ty); - self.mir_constant_to_const_value(bx, constant) - .and_then(|c| OperandRef::from_const(bx, c, ty)) - .unwrap_or_else(|err| { - match constant.literal { - mir::Literal::Promoted { .. } => { - // don't report errors inside promoteds, just warnings. - }, - mir::Literal::Value { .. } => { - err.report(bx.tcx(), constant.span, "const operand") - }, - } - // We've errored, so we don't have to produce working code. - let layout = bx.cx.layout_of(ty); - PlaceRef::new_sized( - C_null(layout.llvm_type(bx.cx).ptr_to()), - layout, - layout.align, - ).load(bx) - }) - } - } - } -} diff --git a/src/librustc_trans/mir/place.rs b/src/librustc_trans/mir/place.rs deleted file mode 100644 index d4abd5fa88d..00000000000 --- a/src/librustc_trans/mir/place.rs +++ /dev/null @@ -1,499 +0,0 @@ -// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -use llvm::{self, ValueRef}; -use rustc::ty::{self, Ty}; -use rustc::ty::layout::{self, Align, TyLayout, LayoutOf}; -use rustc::mir; -use rustc::mir::tcx::PlaceTy; -use rustc_data_structures::indexed_vec::Idx; -use base; -use builder::Builder; -use common::{CodegenCx, C_undef, C_usize, C_u8, C_u32, C_uint, C_null, C_uint_big}; -use consts; -use type_of::LayoutLlvmExt; -use type_::Type; -use value::Value; -use glue; - -use std::ptr; - -use super::{FunctionCx, LocalRef}; -use super::operand::{OperandRef, OperandValue}; - -#[derive(Copy, Clone, Debug)] -pub struct PlaceRef<'tcx> { - /// Pointer to the contents of the place - pub llval: ValueRef, - - /// This place's extra data if it is unsized, or null - pub llextra: ValueRef, - - /// Monomorphized type of this place, including variant information - pub layout: TyLayout<'tcx>, - - /// What alignment we know for this place - pub align: Align, -} - -impl<'a, 'tcx> PlaceRef<'tcx> { - pub fn new_sized(llval: ValueRef, - layout: TyLayout<'tcx>, - align: Align) - -> PlaceRef<'tcx> { - PlaceRef { - llval, - llextra: ptr::null_mut(), - layout, - align - } - } - - pub fn alloca(bx: &Builder<'a, 'tcx>, layout: TyLayout<'tcx>, name: &str) - -> PlaceRef<'tcx> { - debug!("alloca({:?}: {:?})", name, layout); - let tmp = bx.alloca(layout.llvm_type(bx.cx), name, layout.align); - Self::new_sized(tmp, layout, layout.align) - } - - pub fn len(&self, cx: &CodegenCx<'a, 'tcx>) -> ValueRef { - if let layout::FieldPlacement::Array { count, .. } = self.layout.fields { - if self.layout.is_unsized() { - assert!(self.has_extra()); - assert_eq!(count, 0); - self.llextra - } else { - C_usize(cx, count) - } - } else { - bug!("unexpected layout `{:#?}` in PlaceRef::len", self.layout) - } - } - - pub fn has_extra(&self) -> bool { - !self.llextra.is_null() - } - - pub fn load(&self, bx: &Builder<'a, 'tcx>) -> OperandRef<'tcx> { - debug!("PlaceRef::load: {:?}", self); - - assert!(!self.has_extra()); - - if self.layout.is_zst() { - return OperandRef::new_zst(bx.cx, self.layout); - } - - let scalar_load_metadata = |load, scalar: &layout::Scalar| { - let vr = scalar.valid_range.clone(); - match scalar.value { - layout::Int(..) => { - let range = scalar.valid_range_exclusive(bx.cx); - if range.start != range.end { - bx.range_metadata(load, range); - } - } - layout::Pointer if vr.start() < vr.end() && !vr.contains(&0) => { - bx.nonnull_metadata(load); - } - _ => {} - } - }; - - let val = if self.layout.is_llvm_immediate() { - let mut const_llval = ptr::null_mut(); - unsafe { - let global = llvm::LLVMIsAGlobalVariable(self.llval); - if !global.is_null() && llvm::LLVMIsGlobalConstant(global) == llvm::True { - const_llval = llvm::LLVMGetInitializer(global); - } - } - - let llval = if !const_llval.is_null() { - const_llval - } else { - let load = bx.load(self.llval, self.align); - if let layout::Abi::Scalar(ref scalar) = self.layout.abi { - scalar_load_metadata(load, scalar); - } - load - }; - OperandValue::Immediate(base::to_immediate(bx, llval, self.layout)) - } else if let layout::Abi::ScalarPair(ref a, ref b) = self.layout.abi { - let load = |i, scalar: &layout::Scalar| { - let mut llptr = bx.struct_gep(self.llval, i as u64); - // Make sure to always load i1 as i8. - if scalar.is_bool() { - llptr = bx.pointercast(llptr, Type::i8p(bx.cx)); - } - let load = bx.load(llptr, self.align); - scalar_load_metadata(load, scalar); - if scalar.is_bool() { - bx.trunc(load, Type::i1(bx.cx)) - } else { - load - } - }; - OperandValue::Pair(load(0, a), load(1, b)) - } else { - OperandValue::Ref(self.llval, self.align) - }; - - OperandRef { val, layout: self.layout } - } - - /// Access a field, at a point when the value's case is known. - pub fn project_field(self, bx: &Builder<'a, 'tcx>, ix: usize) -> PlaceRef<'tcx> { - let cx = bx.cx; - let field = self.layout.field(cx, ix); - let offset = self.layout.fields.offset(ix); - let align = self.align.min(self.layout.align).min(field.align); - - let simple = || { - // Unions and newtypes only use an offset of 0. - let llval = if offset.bytes() == 0 { - self.llval - } else if let layout::Abi::ScalarPair(ref a, ref b) = self.layout.abi { - // Offsets have to match either first or second field. - assert_eq!(offset, a.value.size(cx).abi_align(b.value.align(cx))); - bx.struct_gep(self.llval, 1) - } else { - bx.struct_gep(self.llval, self.layout.llvm_field_index(ix)) - }; - PlaceRef { - // HACK(eddyb) have to bitcast pointers until LLVM removes pointee types. - llval: bx.pointercast(llval, field.llvm_type(cx).ptr_to()), - llextra: if cx.type_has_metadata(field.ty) { - self.llextra - } else { - ptr::null_mut() - }, - layout: field, - align, - } - }; - - // Simple cases, which don't need DST adjustment: - // * no metadata available - just log the case - // * known alignment - sized types, [T], str or a foreign type - // * packed struct - there is no alignment padding - match field.ty.sty { - _ if !self.has_extra() => { - debug!("Unsized field `{}`, of `{:?}` has no metadata for adjustment", - ix, Value(self.llval)); - return simple(); - } - _ if !field.is_unsized() => return simple(), - ty::TySlice(..) | ty::TyStr | ty::TyForeign(..) => return simple(), - ty::TyAdt(def, _) => { - if def.repr.packed() { - // FIXME(eddyb) generalize the adjustment when we - // start supporting packing to larger alignments. - assert_eq!(self.layout.align.abi(), 1); - return simple(); - } - } - _ => {} - } - - // We need to get the pointer manually now. - // We do this by casting to a *i8, then offsetting it by the appropriate amount. - // We do this instead of, say, simply adjusting the pointer from the result of a GEP - // because the field may have an arbitrary alignment in the LLVM representation - // anyway. - // - // To demonstrate: - // struct Foo { - // x: u16, - // y: T - // } - // - // The type Foo> is represented in LLVM as { u16, { u16, u8 }}, meaning that - // the `y` field has 16-bit alignment. - - let meta = self.llextra; - - let unaligned_offset = C_usize(cx, offset.bytes()); - - // Get the alignment of the field - let (_, unsized_align) = glue::size_and_align_of_dst(bx, field.ty, meta); - - // Bump the unaligned offset up to the appropriate alignment using the - // following expression: - // - // (unaligned offset + (align - 1)) & -align - - // Calculate offset - let align_sub_1 = bx.sub(unsized_align, C_usize(cx, 1u64)); - let offset = bx.and(bx.add(unaligned_offset, align_sub_1), - bx.neg(unsized_align)); - - debug!("struct_field_ptr: DST field offset: {:?}", Value(offset)); - - // Cast and adjust pointer - let byte_ptr = bx.pointercast(self.llval, Type::i8p(cx)); - let byte_ptr = bx.gep(byte_ptr, &[offset]); - - // Finally, cast back to the type expected - let ll_fty = field.llvm_type(cx); - debug!("struct_field_ptr: Field type is {:?}", ll_fty); - - PlaceRef { - llval: bx.pointercast(byte_ptr, ll_fty.ptr_to()), - llextra: self.llextra, - layout: field, - align, - } - } - - /// Obtain the actual discriminant of a value. - pub fn trans_get_discr(self, bx: &Builder<'a, 'tcx>, cast_to: Ty<'tcx>) -> ValueRef { - let cast_to = bx.cx.layout_of(cast_to).immediate_llvm_type(bx.cx); - if self.layout.abi == layout::Abi::Uninhabited { - return C_undef(cast_to); - } - match self.layout.variants { - layout::Variants::Single { index } => { - let discr_val = self.layout.ty.ty_adt_def().map_or( - index as u128, - |def| def.discriminant_for_variant(bx.cx.tcx, index).val); - return C_uint_big(cast_to, discr_val); - } - layout::Variants::Tagged { .. } | - layout::Variants::NicheFilling { .. } => {}, - } - - let discr = self.project_field(bx, 0); - let lldiscr = discr.load(bx).immediate(); - match self.layout.variants { - layout::Variants::Single { .. } => bug!(), - layout::Variants::Tagged { ref tag, .. } => { - let signed = match tag.value { - layout::Int(_, signed) => signed, - _ => false - }; - bx.intcast(lldiscr, cast_to, signed) - } - layout::Variants::NicheFilling { - dataful_variant, - ref niche_variants, - niche_start, - .. - } => { - let niche_llty = discr.layout.immediate_llvm_type(bx.cx); - if niche_variants.start() == niche_variants.end() { - // FIXME(eddyb) Check the actual primitive type here. - let niche_llval = if niche_start == 0 { - // HACK(eddyb) Using `C_null` as it works on all types. - C_null(niche_llty) - } else { - C_uint_big(niche_llty, niche_start) - }; - bx.select(bx.icmp(llvm::IntEQ, lldiscr, niche_llval), - C_uint(cast_to, *niche_variants.start() as u64), - C_uint(cast_to, dataful_variant as u64)) - } else { - // Rebase from niche values to discriminant values. - let delta = niche_start.wrapping_sub(*niche_variants.start() as u128); - let lldiscr = bx.sub(lldiscr, C_uint_big(niche_llty, delta)); - let lldiscr_max = C_uint(niche_llty, *niche_variants.end() as u64); - bx.select(bx.icmp(llvm::IntULE, lldiscr, lldiscr_max), - bx.intcast(lldiscr, cast_to, false), - C_uint(cast_to, dataful_variant as u64)) - } - } - } - } - - /// Set the discriminant for a new value of the given case of the given - /// representation. - pub fn trans_set_discr(&self, bx: &Builder<'a, 'tcx>, variant_index: usize) { - if self.layout.for_variant(bx.cx, variant_index).abi == layout::Abi::Uninhabited { - return; - } - match self.layout.variants { - layout::Variants::Single { index } => { - assert_eq!(index, variant_index); - } - layout::Variants::Tagged { .. } => { - let ptr = self.project_field(bx, 0); - let to = self.layout.ty.ty_adt_def().unwrap() - .discriminant_for_variant(bx.tcx(), variant_index) - .val; - bx.store( - C_uint_big(ptr.layout.llvm_type(bx.cx), to), - ptr.llval, - ptr.align); - } - layout::Variants::NicheFilling { - dataful_variant, - ref niche_variants, - niche_start, - .. - } => { - if variant_index != dataful_variant { - if bx.sess().target.target.arch == "arm" || - bx.sess().target.target.arch == "aarch64" { - // Issue #34427: As workaround for LLVM bug on ARM, - // use memset of 0 before assigning niche value. - let llptr = bx.pointercast(self.llval, Type::i8(bx.cx).ptr_to()); - let fill_byte = C_u8(bx.cx, 0); - let (size, align) = self.layout.size_and_align(); - let size = C_usize(bx.cx, size.bytes()); - let align = C_u32(bx.cx, align.abi() as u32); - base::call_memset(bx, llptr, fill_byte, size, align, false); - } - - let niche = self.project_field(bx, 0); - let niche_llty = niche.layout.immediate_llvm_type(bx.cx); - let niche_value = ((variant_index - *niche_variants.start()) as u128) - .wrapping_add(niche_start); - // FIXME(eddyb) Check the actual primitive type here. - let niche_llval = if niche_value == 0 { - // HACK(eddyb) Using `C_null` as it works on all types. - C_null(niche_llty) - } else { - C_uint_big(niche_llty, niche_value) - }; - OperandValue::Immediate(niche_llval).store(bx, niche); - } - } - } - } - - pub fn project_index(&self, bx: &Builder<'a, 'tcx>, llindex: ValueRef) - -> PlaceRef<'tcx> { - PlaceRef { - llval: bx.inbounds_gep(self.llval, &[C_usize(bx.cx, 0), llindex]), - llextra: ptr::null_mut(), - layout: self.layout.field(bx.cx, 0), - align: self.align - } - } - - pub fn project_downcast(&self, bx: &Builder<'a, 'tcx>, variant_index: usize) - -> PlaceRef<'tcx> { - let mut downcast = *self; - downcast.layout = self.layout.for_variant(bx.cx, variant_index); - - // Cast to the appropriate variant struct type. - let variant_ty = downcast.layout.llvm_type(bx.cx); - downcast.llval = bx.pointercast(downcast.llval, variant_ty.ptr_to()); - - downcast - } - - pub fn storage_live(&self, bx: &Builder<'a, 'tcx>) { - bx.lifetime_start(self.llval, self.layout.size); - } - - pub fn storage_dead(&self, bx: &Builder<'a, 'tcx>) { - bx.lifetime_end(self.llval, self.layout.size); - } -} - -impl<'a, 'tcx> FunctionCx<'a, 'tcx> { - pub fn trans_place(&mut self, - bx: &Builder<'a, 'tcx>, - place: &mir::Place<'tcx>) - -> PlaceRef<'tcx> { - debug!("trans_place(place={:?})", place); - - let cx = bx.cx; - let tcx = cx.tcx; - - if let mir::Place::Local(index) = *place { - match self.locals[index] { - LocalRef::Place(place) => { - return place; - } - LocalRef::Operand(..) => { - bug!("using operand local {:?} as place", place); - } - } - } - - let result = match *place { - mir::Place::Local(_) => bug!(), // handled above - mir::Place::Static(box mir::Static { def_id, ty }) => { - let layout = cx.layout_of(self.monomorphize(&ty)); - PlaceRef::new_sized(consts::get_static(cx, def_id), layout, layout.align) - }, - mir::Place::Projection(box mir::Projection { - ref base, - elem: mir::ProjectionElem::Deref - }) => { - // Load the pointer from its location. - self.trans_consume(bx, base).deref(bx.cx) - } - mir::Place::Projection(ref projection) => { - let tr_base = self.trans_place(bx, &projection.base); - - match projection.elem { - mir::ProjectionElem::Deref => bug!(), - mir::ProjectionElem::Field(ref field, _) => { - tr_base.project_field(bx, field.index()) - } - mir::ProjectionElem::Index(index) => { - let index = &mir::Operand::Copy(mir::Place::Local(index)); - let index = self.trans_operand(bx, index); - let llindex = index.immediate(); - tr_base.project_index(bx, llindex) - } - mir::ProjectionElem::ConstantIndex { offset, - from_end: false, - min_length: _ } => { - let lloffset = C_usize(bx.cx, offset as u64); - tr_base.project_index(bx, lloffset) - } - mir::ProjectionElem::ConstantIndex { offset, - from_end: true, - min_length: _ } => { - let lloffset = C_usize(bx.cx, offset as u64); - let lllen = tr_base.len(bx.cx); - let llindex = bx.sub(lllen, lloffset); - tr_base.project_index(bx, llindex) - } - mir::ProjectionElem::Subslice { from, to } => { - let mut subslice = tr_base.project_index(bx, - C_usize(bx.cx, from as u64)); - let projected_ty = PlaceTy::Ty { ty: tr_base.layout.ty } - .projection_ty(tcx, &projection.elem).to_ty(bx.tcx()); - subslice.layout = bx.cx.layout_of(self.monomorphize(&projected_ty)); - - if subslice.layout.is_unsized() { - assert!(tr_base.has_extra()); - subslice.llextra = bx.sub(tr_base.llextra, - C_usize(bx.cx, (from as u64) + (to as u64))); - } - - // Cast the place pointer type to the new - // array or slice type (*[%_; new_len]). - subslice.llval = bx.pointercast(subslice.llval, - subslice.layout.llvm_type(bx.cx).ptr_to()); - - subslice - } - mir::ProjectionElem::Downcast(_, v) => { - tr_base.project_downcast(bx, v) - } - } - } - }; - debug!("trans_place(place={:?}) => {:?}", place, result); - result - } - - pub fn monomorphized_place_ty(&self, place: &mir::Place<'tcx>) -> Ty<'tcx> { - let tcx = self.cx.tcx; - let place_ty = place.ty(self.mir, tcx); - self.monomorphize(&place_ty.to_ty(tcx)) - } -} - diff --git a/src/librustc_trans/mir/rvalue.rs b/src/librustc_trans/mir/rvalue.rs deleted file mode 100644 index 3b447756450..00000000000 --- a/src/librustc_trans/mir/rvalue.rs +++ /dev/null @@ -1,952 +0,0 @@ -// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -use llvm::{self, ValueRef}; -use rustc::ty::{self, Ty}; -use rustc::ty::cast::{CastTy, IntTy}; -use rustc::ty::layout::{self, LayoutOf}; -use rustc::mir; -use rustc::middle::lang_items::ExchangeMallocFnLangItem; -use rustc_apfloat::{ieee, Float, Status, Round}; -use std::{u128, i128}; - -use base; -use builder::Builder; -use callee; -use common::{self, val_ty}; -use common::{C_bool, C_u8, C_i32, C_u32, C_u64, C_undef, C_null, C_usize, C_uint, C_uint_big}; -use consts; -use monomorphize; -use type_::Type; -use type_of::LayoutLlvmExt; -use value::Value; - -use super::{FunctionCx, LocalRef}; -use super::operand::{OperandRef, OperandValue}; -use super::place::PlaceRef; - -impl<'a, 'tcx> FunctionCx<'a, 'tcx> { - pub fn trans_rvalue(&mut self, - bx: Builder<'a, 'tcx>, - dest: PlaceRef<'tcx>, - rvalue: &mir::Rvalue<'tcx>) - -> Builder<'a, 'tcx> - { - debug!("trans_rvalue(dest.llval={:?}, rvalue={:?})", - Value(dest.llval), rvalue); - - match *rvalue { - mir::Rvalue::Use(ref operand) => { - let tr_operand = self.trans_operand(&bx, operand); - // FIXME: consider not copying constants through stack. (fixable by translating - // constants into OperandValue::Ref, why don’t we do that yet if we don’t?) - tr_operand.val.store(&bx, dest); - bx - } - - mir::Rvalue::Cast(mir::CastKind::Unsize, ref source, _) => { - // The destination necessarily contains a fat pointer, so if - // it's a scalar pair, it's a fat pointer or newtype thereof. - if dest.layout.is_llvm_scalar_pair() { - // into-coerce of a thin pointer to a fat pointer - just - // use the operand path. - let (bx, temp) = self.trans_rvalue_operand(bx, rvalue); - temp.val.store(&bx, dest); - return bx; - } - - // Unsize of a nontrivial struct. I would prefer for - // this to be eliminated by MIR translation, but - // `CoerceUnsized` can be passed by a where-clause, - // so the (generic) MIR may not be able to expand it. - let operand = self.trans_operand(&bx, source); - match operand.val { - OperandValue::Pair(..) | - OperandValue::Immediate(_) => { - // unsize from an immediate structure. We don't - // really need a temporary alloca here, but - // avoiding it would require us to have - // `coerce_unsized_into` use extractvalue to - // index into the struct, and this case isn't - // important enough for it. - debug!("trans_rvalue: creating ugly alloca"); - let scratch = PlaceRef::alloca(&bx, operand.layout, "__unsize_temp"); - scratch.storage_live(&bx); - operand.val.store(&bx, scratch); - base::coerce_unsized_into(&bx, scratch, dest); - scratch.storage_dead(&bx); - } - OperandValue::Ref(llref, align) => { - let source = PlaceRef::new_sized(llref, operand.layout, align); - base::coerce_unsized_into(&bx, source, dest); - } - } - bx - } - - mir::Rvalue::Repeat(ref elem, count) => { - let tr_elem = self.trans_operand(&bx, elem); - - // Do not generate the loop for zero-sized elements or empty arrays. - if dest.layout.is_zst() { - return bx; - } - - let start = dest.project_index(&bx, C_usize(bx.cx, 0)).llval; - - if let OperandValue::Immediate(v) = tr_elem.val { - let align = C_i32(bx.cx, dest.align.abi() as i32); - let size = C_usize(bx.cx, dest.layout.size.bytes()); - - // Use llvm.memset.p0i8.* to initialize all zero arrays - if common::is_const_integral(v) && common::const_to_uint(v) == 0 { - let fill = C_u8(bx.cx, 0); - base::call_memset(&bx, start, fill, size, align, false); - return bx; - } - - // Use llvm.memset.p0i8.* to initialize byte arrays - let v = base::from_immediate(&bx, v); - if common::val_ty(v) == Type::i8(bx.cx) { - base::call_memset(&bx, start, v, size, align, false); - return bx; - } - } - - let count = C_usize(bx.cx, count); - let end = dest.project_index(&bx, count).llval; - - let header_bx = bx.build_sibling_block("repeat_loop_header"); - let body_bx = bx.build_sibling_block("repeat_loop_body"); - let next_bx = bx.build_sibling_block("repeat_loop_next"); - - bx.br(header_bx.llbb()); - let current = header_bx.phi(common::val_ty(start), &[start], &[bx.llbb()]); - - let keep_going = header_bx.icmp(llvm::IntNE, current, end); - header_bx.cond_br(keep_going, body_bx.llbb(), next_bx.llbb()); - - tr_elem.val.store(&body_bx, - PlaceRef::new_sized(current, tr_elem.layout, dest.align)); - - let next = body_bx.inbounds_gep(current, &[C_usize(bx.cx, 1)]); - body_bx.br(header_bx.llbb()); - header_bx.add_incoming_to_phi(current, next, body_bx.llbb()); - - next_bx - } - - mir::Rvalue::Aggregate(ref kind, ref operands) => { - let (dest, active_field_index) = match **kind { - mir::AggregateKind::Adt(adt_def, variant_index, _, active_field_index) => { - dest.trans_set_discr(&bx, variant_index); - if adt_def.is_enum() { - (dest.project_downcast(&bx, variant_index), active_field_index) - } else { - (dest, active_field_index) - } - } - _ => (dest, None) - }; - for (i, operand) in operands.iter().enumerate() { - let op = self.trans_operand(&bx, operand); - // Do not generate stores and GEPis for zero-sized fields. - if !op.layout.is_zst() { - let field_index = active_field_index.unwrap_or(i); - op.val.store(&bx, dest.project_field(&bx, field_index)); - } - } - bx - } - - _ => { - assert!(self.rvalue_creates_operand(rvalue)); - let (bx, temp) = self.trans_rvalue_operand(bx, rvalue); - temp.val.store(&bx, dest); - bx - } - } - } - - pub fn trans_rvalue_operand(&mut self, - bx: Builder<'a, 'tcx>, - rvalue: &mir::Rvalue<'tcx>) - -> (Builder<'a, 'tcx>, OperandRef<'tcx>) - { - assert!(self.rvalue_creates_operand(rvalue), "cannot trans {:?} to operand", rvalue); - - match *rvalue { - mir::Rvalue::Cast(ref kind, ref source, mir_cast_ty) => { - let operand = self.trans_operand(&bx, source); - debug!("cast operand is {:?}", operand); - let cast = bx.cx.layout_of(self.monomorphize(&mir_cast_ty)); - - let val = match *kind { - mir::CastKind::ReifyFnPointer => { - match operand.layout.ty.sty { - ty::TyFnDef(def_id, substs) => { - if bx.cx.tcx.has_attr(def_id, "rustc_args_required_const") { - bug!("reifying a fn ptr that requires \ - const arguments"); - } - OperandValue::Immediate( - callee::resolve_and_get_fn(bx.cx, def_id, substs)) - } - _ => { - bug!("{} cannot be reified to a fn ptr", operand.layout.ty) - } - } - } - mir::CastKind::ClosureFnPointer => { - match operand.layout.ty.sty { - ty::TyClosure(def_id, substs) => { - let instance = monomorphize::resolve_closure( - bx.cx.tcx, def_id, substs, ty::ClosureKind::FnOnce); - OperandValue::Immediate(callee::get_fn(bx.cx, instance)) - } - _ => { - bug!("{} cannot be cast to a fn ptr", operand.layout.ty) - } - } - } - mir::CastKind::UnsafeFnPointer => { - // this is a no-op at the LLVM level - operand.val - } - mir::CastKind::Unsize => { - assert!(cast.is_llvm_scalar_pair()); - match operand.val { - OperandValue::Pair(lldata, llextra) => { - // unsize from a fat pointer - this is a - // "trait-object-to-supertrait" coercion, for - // example, - // &'a fmt::Debug+Send => &'a fmt::Debug, - - // HACK(eddyb) have to bitcast pointers - // until LLVM removes pointee types. - let lldata = bx.pointercast(lldata, - cast.scalar_pair_element_llvm_type(bx.cx, 0)); - OperandValue::Pair(lldata, llextra) - } - OperandValue::Immediate(lldata) => { - // "standard" unsize - let (lldata, llextra) = base::unsize_thin_ptr(&bx, lldata, - operand.layout.ty, cast.ty); - OperandValue::Pair(lldata, llextra) - } - OperandValue::Ref(..) => { - bug!("by-ref operand {:?} in trans_rvalue_operand", - operand); - } - } - } - mir::CastKind::Misc if operand.layout.is_llvm_scalar_pair() => { - if let OperandValue::Pair(data_ptr, meta) = operand.val { - if cast.is_llvm_scalar_pair() { - let data_cast = bx.pointercast(data_ptr, - cast.scalar_pair_element_llvm_type(bx.cx, 0)); - OperandValue::Pair(data_cast, meta) - } else { // cast to thin-ptr - // Cast of fat-ptr to thin-ptr is an extraction of data-ptr and - // pointer-cast of that pointer to desired pointer type. - let llcast_ty = cast.immediate_llvm_type(bx.cx); - let llval = bx.pointercast(data_ptr, llcast_ty); - OperandValue::Immediate(llval) - } - } else { - bug!("Unexpected non-Pair operand") - } - } - mir::CastKind::Misc => { - assert!(cast.is_llvm_immediate()); - let ll_t_out = cast.immediate_llvm_type(bx.cx); - if operand.layout.abi == layout::Abi::Uninhabited { - return (bx, OperandRef { - val: OperandValue::Immediate(C_undef(ll_t_out)), - layout: cast, - }); - } - let r_t_in = CastTy::from_ty(operand.layout.ty) - .expect("bad input type for cast"); - let r_t_out = CastTy::from_ty(cast.ty).expect("bad output type for cast"); - let ll_t_in = operand.layout.immediate_llvm_type(bx.cx); - match operand.layout.variants { - layout::Variants::Single { index } => { - if let Some(def) = operand.layout.ty.ty_adt_def() { - let discr_val = def - .discriminant_for_variant(bx.cx.tcx, index) - .val; - let discr = C_uint_big(ll_t_out, discr_val); - return (bx, OperandRef { - val: OperandValue::Immediate(discr), - layout: cast, - }); - } - } - layout::Variants::Tagged { .. } | - layout::Variants::NicheFilling { .. } => {}, - } - let llval = operand.immediate(); - - let mut signed = false; - if let layout::Abi::Scalar(ref scalar) = operand.layout.abi { - if let layout::Int(_, s) = scalar.value { - signed = s; - - if scalar.valid_range.end() > scalar.valid_range.start() { - // We want `table[e as usize]` to not - // have bound checks, and this is the most - // convenient place to put the `assume`. - - base::call_assume(&bx, bx.icmp( - llvm::IntULE, - llval, - C_uint_big(ll_t_in, *scalar.valid_range.end()) - )); - } - } - } - - let newval = match (r_t_in, r_t_out) { - (CastTy::Int(_), CastTy::Int(_)) => { - bx.intcast(llval, ll_t_out, signed) - } - (CastTy::Float, CastTy::Float) => { - let srcsz = ll_t_in.float_width(); - let dstsz = ll_t_out.float_width(); - if dstsz > srcsz { - bx.fpext(llval, ll_t_out) - } else if srcsz > dstsz { - bx.fptrunc(llval, ll_t_out) - } else { - llval - } - } - (CastTy::Ptr(_), CastTy::Ptr(_)) | - (CastTy::FnPtr, CastTy::Ptr(_)) | - (CastTy::RPtr(_), CastTy::Ptr(_)) => - bx.pointercast(llval, ll_t_out), - (CastTy::Ptr(_), CastTy::Int(_)) | - (CastTy::FnPtr, CastTy::Int(_)) => - bx.ptrtoint(llval, ll_t_out), - (CastTy::Int(_), CastTy::Ptr(_)) => { - let usize_llval = bx.intcast(llval, bx.cx.isize_ty, signed); - bx.inttoptr(usize_llval, ll_t_out) - } - (CastTy::Int(_), CastTy::Float) => - cast_int_to_float(&bx, signed, llval, ll_t_in, ll_t_out), - (CastTy::Float, CastTy::Int(IntTy::I)) => - cast_float_to_int(&bx, true, llval, ll_t_in, ll_t_out), - (CastTy::Float, CastTy::Int(_)) => - cast_float_to_int(&bx, false, llval, ll_t_in, ll_t_out), - _ => bug!("unsupported cast: {:?} to {:?}", operand.layout.ty, cast.ty) - }; - OperandValue::Immediate(newval) - } - }; - (bx, OperandRef { - val, - layout: cast - }) - } - - mir::Rvalue::Ref(_, bk, ref place) => { - let tr_place = self.trans_place(&bx, place); - - let ty = tr_place.layout.ty; - - // Note: places are indirect, so storing the `llval` into the - // destination effectively creates a reference. - let val = if !bx.cx.type_has_metadata(ty) { - OperandValue::Immediate(tr_place.llval) - } else { - OperandValue::Pair(tr_place.llval, tr_place.llextra) - }; - (bx, OperandRef { - val, - layout: self.cx.layout_of(self.cx.tcx.mk_ref( - self.cx.tcx.types.re_erased, - ty::TypeAndMut { ty, mutbl: bk.to_mutbl_lossy() } - )), - }) - } - - mir::Rvalue::Len(ref place) => { - let size = self.evaluate_array_len(&bx, place); - let operand = OperandRef { - val: OperandValue::Immediate(size), - layout: bx.cx.layout_of(bx.tcx().types.usize), - }; - (bx, operand) - } - - mir::Rvalue::BinaryOp(op, ref lhs, ref rhs) => { - let lhs = self.trans_operand(&bx, lhs); - let rhs = self.trans_operand(&bx, rhs); - let llresult = match (lhs.val, rhs.val) { - (OperandValue::Pair(lhs_addr, lhs_extra), - OperandValue::Pair(rhs_addr, rhs_extra)) => { - self.trans_fat_ptr_binop(&bx, op, - lhs_addr, lhs_extra, - rhs_addr, rhs_extra, - lhs.layout.ty) - } - - (OperandValue::Immediate(lhs_val), - OperandValue::Immediate(rhs_val)) => { - self.trans_scalar_binop(&bx, op, lhs_val, rhs_val, lhs.layout.ty) - } - - _ => bug!() - }; - let operand = OperandRef { - val: OperandValue::Immediate(llresult), - layout: bx.cx.layout_of( - op.ty(bx.tcx(), lhs.layout.ty, rhs.layout.ty)), - }; - (bx, operand) - } - mir::Rvalue::CheckedBinaryOp(op, ref lhs, ref rhs) => { - let lhs = self.trans_operand(&bx, lhs); - let rhs = self.trans_operand(&bx, rhs); - let result = self.trans_scalar_checked_binop(&bx, op, - lhs.immediate(), rhs.immediate(), - lhs.layout.ty); - let val_ty = op.ty(bx.tcx(), lhs.layout.ty, rhs.layout.ty); - let operand_ty = bx.tcx().intern_tup(&[val_ty, bx.tcx().types.bool]); - let operand = OperandRef { - val: result, - layout: bx.cx.layout_of(operand_ty) - }; - - (bx, operand) - } - - mir::Rvalue::UnaryOp(op, ref operand) => { - let operand = self.trans_operand(&bx, operand); - let lloperand = operand.immediate(); - let is_float = operand.layout.ty.is_fp(); - let llval = match op { - mir::UnOp::Not => bx.not(lloperand), - mir::UnOp::Neg => if is_float { - bx.fneg(lloperand) - } else { - bx.neg(lloperand) - } - }; - (bx, OperandRef { - val: OperandValue::Immediate(llval), - layout: operand.layout, - }) - } - - mir::Rvalue::Discriminant(ref place) => { - let discr_ty = rvalue.ty(&*self.mir, bx.tcx()); - let discr = self.trans_place(&bx, place) - .trans_get_discr(&bx, discr_ty); - (bx, OperandRef { - val: OperandValue::Immediate(discr), - layout: self.cx.layout_of(discr_ty) - }) - } - - mir::Rvalue::NullaryOp(mir::NullOp::SizeOf, ty) => { - assert!(bx.cx.type_is_sized(ty)); - let val = C_usize(bx.cx, bx.cx.size_of(ty).bytes()); - let tcx = bx.tcx(); - (bx, OperandRef { - val: OperandValue::Immediate(val), - layout: self.cx.layout_of(tcx.types.usize), - }) - } - - mir::Rvalue::NullaryOp(mir::NullOp::Box, content_ty) => { - let content_ty: Ty<'tcx> = self.monomorphize(&content_ty); - let (size, align) = bx.cx.size_and_align_of(content_ty); - let llsize = C_usize(bx.cx, size.bytes()); - let llalign = C_usize(bx.cx, align.abi()); - let box_layout = bx.cx.layout_of(bx.tcx().mk_box(content_ty)); - let llty_ptr = box_layout.llvm_type(bx.cx); - - // Allocate space: - let def_id = match bx.tcx().lang_items().require(ExchangeMallocFnLangItem) { - Ok(id) => id, - Err(s) => { - bx.sess().fatal(&format!("allocation of `{}` {}", box_layout.ty, s)); - } - }; - let instance = ty::Instance::mono(bx.tcx(), def_id); - let r = callee::get_fn(bx.cx, instance); - let val = bx.pointercast(bx.call(r, &[llsize, llalign], None), llty_ptr); - - let operand = OperandRef { - val: OperandValue::Immediate(val), - layout: box_layout, - }; - (bx, operand) - } - mir::Rvalue::Use(ref operand) => { - let operand = self.trans_operand(&bx, operand); - (bx, operand) - } - mir::Rvalue::Repeat(..) | - mir::Rvalue::Aggregate(..) => { - // According to `rvalue_creates_operand`, only ZST - // aggregate rvalues are allowed to be operands. - let ty = rvalue.ty(self.mir, self.cx.tcx); - (bx, OperandRef::new_zst(self.cx, - self.cx.layout_of(self.monomorphize(&ty)))) - } - } - } - - fn evaluate_array_len(&mut self, - bx: &Builder<'a, 'tcx>, - place: &mir::Place<'tcx>) -> ValueRef - { - // ZST are passed as operands and require special handling - // because trans_place() panics if Local is operand. - if let mir::Place::Local(index) = *place { - if let LocalRef::Operand(Some(op)) = self.locals[index] { - if let ty::TyArray(_, n) = op.layout.ty.sty { - let n = n.unwrap_usize(bx.cx.tcx); - return common::C_usize(bx.cx, n); - } - } - } - // use common size calculation for non zero-sized types - let tr_value = self.trans_place(&bx, place); - return tr_value.len(bx.cx); - } - - pub fn trans_scalar_binop(&mut self, - bx: &Builder<'a, 'tcx>, - op: mir::BinOp, - lhs: ValueRef, - rhs: ValueRef, - input_ty: Ty<'tcx>) -> ValueRef { - let is_float = input_ty.is_fp(); - let is_signed = input_ty.is_signed(); - let is_nil = input_ty.is_nil(); - match op { - mir::BinOp::Add => if is_float { - bx.fadd(lhs, rhs) - } else { - bx.add(lhs, rhs) - }, - mir::BinOp::Sub => if is_float { - bx.fsub(lhs, rhs) - } else { - bx.sub(lhs, rhs) - }, - mir::BinOp::Mul => if is_float { - bx.fmul(lhs, rhs) - } else { - bx.mul(lhs, rhs) - }, - mir::BinOp::Div => if is_float { - bx.fdiv(lhs, rhs) - } else if is_signed { - bx.sdiv(lhs, rhs) - } else { - bx.udiv(lhs, rhs) - }, - mir::BinOp::Rem => if is_float { - bx.frem(lhs, rhs) - } else if is_signed { - bx.srem(lhs, rhs) - } else { - bx.urem(lhs, rhs) - }, - mir::BinOp::BitOr => bx.or(lhs, rhs), - mir::BinOp::BitAnd => bx.and(lhs, rhs), - mir::BinOp::BitXor => bx.xor(lhs, rhs), - mir::BinOp::Offset => bx.inbounds_gep(lhs, &[rhs]), - mir::BinOp::Shl => common::build_unchecked_lshift(bx, lhs, rhs), - mir::BinOp::Shr => common::build_unchecked_rshift(bx, input_ty, lhs, rhs), - mir::BinOp::Ne | mir::BinOp::Lt | mir::BinOp::Gt | - mir::BinOp::Eq | mir::BinOp::Le | mir::BinOp::Ge => if is_nil { - C_bool(bx.cx, match op { - mir::BinOp::Ne | mir::BinOp::Lt | mir::BinOp::Gt => false, - mir::BinOp::Eq | mir::BinOp::Le | mir::BinOp::Ge => true, - _ => unreachable!() - }) - } else if is_float { - bx.fcmp( - base::bin_op_to_fcmp_predicate(op.to_hir_binop()), - lhs, rhs - ) - } else { - bx.icmp( - base::bin_op_to_icmp_predicate(op.to_hir_binop(), is_signed), - lhs, rhs - ) - } - } - } - - pub fn trans_fat_ptr_binop(&mut self, - bx: &Builder<'a, 'tcx>, - op: mir::BinOp, - lhs_addr: ValueRef, - lhs_extra: ValueRef, - rhs_addr: ValueRef, - rhs_extra: ValueRef, - _input_ty: Ty<'tcx>) - -> ValueRef { - match op { - mir::BinOp::Eq => { - bx.and( - bx.icmp(llvm::IntEQ, lhs_addr, rhs_addr), - bx.icmp(llvm::IntEQ, lhs_extra, rhs_extra) - ) - } - mir::BinOp::Ne => { - bx.or( - bx.icmp(llvm::IntNE, lhs_addr, rhs_addr), - bx.icmp(llvm::IntNE, lhs_extra, rhs_extra) - ) - } - mir::BinOp::Le | mir::BinOp::Lt | - mir::BinOp::Ge | mir::BinOp::Gt => { - // a OP b ~ a.0 STRICT(OP) b.0 | (a.0 == b.0 && a.1 OP a.1) - let (op, strict_op) = match op { - mir::BinOp::Lt => (llvm::IntULT, llvm::IntULT), - mir::BinOp::Le => (llvm::IntULE, llvm::IntULT), - mir::BinOp::Gt => (llvm::IntUGT, llvm::IntUGT), - mir::BinOp::Ge => (llvm::IntUGE, llvm::IntUGT), - _ => bug!(), - }; - - bx.or( - bx.icmp(strict_op, lhs_addr, rhs_addr), - bx.and( - bx.icmp(llvm::IntEQ, lhs_addr, rhs_addr), - bx.icmp(op, lhs_extra, rhs_extra) - ) - ) - } - _ => { - bug!("unexpected fat ptr binop"); - } - } - } - - pub fn trans_scalar_checked_binop(&mut self, - bx: &Builder<'a, 'tcx>, - op: mir::BinOp, - lhs: ValueRef, - rhs: ValueRef, - input_ty: Ty<'tcx>) -> OperandValue { - // This case can currently arise only from functions marked - // with #[rustc_inherit_overflow_checks] and inlined from - // another crate (mostly core::num generic/#[inline] fns), - // while the current crate doesn't use overflow checks. - if !bx.cx.check_overflow { - let val = self.trans_scalar_binop(bx, op, lhs, rhs, input_ty); - return OperandValue::Pair(val, C_bool(bx.cx, false)); - } - - let (val, of) = match op { - // These are checked using intrinsics - mir::BinOp::Add | mir::BinOp::Sub | mir::BinOp::Mul => { - let oop = match op { - mir::BinOp::Add => OverflowOp::Add, - mir::BinOp::Sub => OverflowOp::Sub, - mir::BinOp::Mul => OverflowOp::Mul, - _ => unreachable!() - }; - let intrinsic = get_overflow_intrinsic(oop, bx, input_ty); - let res = bx.call(intrinsic, &[lhs, rhs], None); - - (bx.extract_value(res, 0), - bx.extract_value(res, 1)) - } - mir::BinOp::Shl | mir::BinOp::Shr => { - let lhs_llty = val_ty(lhs); - let rhs_llty = val_ty(rhs); - let invert_mask = common::shift_mask_val(&bx, lhs_llty, rhs_llty, true); - let outer_bits = bx.and(rhs, invert_mask); - - let of = bx.icmp(llvm::IntNE, outer_bits, C_null(rhs_llty)); - let val = self.trans_scalar_binop(bx, op, lhs, rhs, input_ty); - - (val, of) - } - _ => { - bug!("Operator `{:?}` is not a checkable operator", op) - } - }; - - OperandValue::Pair(val, of) - } - - pub fn rvalue_creates_operand(&self, rvalue: &mir::Rvalue<'tcx>) -> bool { - match *rvalue { - mir::Rvalue::Ref(..) | - mir::Rvalue::Len(..) | - mir::Rvalue::Cast(..) | // (*) - mir::Rvalue::BinaryOp(..) | - mir::Rvalue::CheckedBinaryOp(..) | - mir::Rvalue::UnaryOp(..) | - mir::Rvalue::Discriminant(..) | - mir::Rvalue::NullaryOp(..) | - mir::Rvalue::Use(..) => // (*) - true, - mir::Rvalue::Repeat(..) | - mir::Rvalue::Aggregate(..) => { - let ty = rvalue.ty(self.mir, self.cx.tcx); - let ty = self.monomorphize(&ty); - self.cx.layout_of(ty).is_zst() - } - } - - // (*) this is only true if the type is suitable - } -} - -#[derive(Copy, Clone)] -enum OverflowOp { - Add, Sub, Mul -} - -fn get_overflow_intrinsic(oop: OverflowOp, bx: &Builder, ty: Ty) -> ValueRef { - use syntax::ast::IntTy::*; - use syntax::ast::UintTy::*; - use rustc::ty::{TyInt, TyUint}; - - let tcx = bx.tcx(); - - let new_sty = match ty.sty { - TyInt(Isize) => match &tcx.sess.target.target.target_pointer_width[..] { - "16" => TyInt(I16), - "32" => TyInt(I32), - "64" => TyInt(I64), - _ => panic!("unsupported target word size") - }, - TyUint(Usize) => match &tcx.sess.target.target.target_pointer_width[..] { - "16" => TyUint(U16), - "32" => TyUint(U32), - "64" => TyUint(U64), - _ => panic!("unsupported target word size") - }, - ref t @ TyUint(_) | ref t @ TyInt(_) => t.clone(), - _ => panic!("tried to get overflow intrinsic for op applied to non-int type") - }; - - let name = match oop { - OverflowOp::Add => match new_sty { - TyInt(I8) => "llvm.sadd.with.overflow.i8", - TyInt(I16) => "llvm.sadd.with.overflow.i16", - TyInt(I32) => "llvm.sadd.with.overflow.i32", - TyInt(I64) => "llvm.sadd.with.overflow.i64", - TyInt(I128) => "llvm.sadd.with.overflow.i128", - - TyUint(U8) => "llvm.uadd.with.overflow.i8", - TyUint(U16) => "llvm.uadd.with.overflow.i16", - TyUint(U32) => "llvm.uadd.with.overflow.i32", - TyUint(U64) => "llvm.uadd.with.overflow.i64", - TyUint(U128) => "llvm.uadd.with.overflow.i128", - - _ => unreachable!(), - }, - OverflowOp::Sub => match new_sty { - TyInt(I8) => "llvm.ssub.with.overflow.i8", - TyInt(I16) => "llvm.ssub.with.overflow.i16", - TyInt(I32) => "llvm.ssub.with.overflow.i32", - TyInt(I64) => "llvm.ssub.with.overflow.i64", - TyInt(I128) => "llvm.ssub.with.overflow.i128", - - TyUint(U8) => "llvm.usub.with.overflow.i8", - TyUint(U16) => "llvm.usub.with.overflow.i16", - TyUint(U32) => "llvm.usub.with.overflow.i32", - TyUint(U64) => "llvm.usub.with.overflow.i64", - TyUint(U128) => "llvm.usub.with.overflow.i128", - - _ => unreachable!(), - }, - OverflowOp::Mul => match new_sty { - TyInt(I8) => "llvm.smul.with.overflow.i8", - TyInt(I16) => "llvm.smul.with.overflow.i16", - TyInt(I32) => "llvm.smul.with.overflow.i32", - TyInt(I64) => "llvm.smul.with.overflow.i64", - TyInt(I128) => "llvm.smul.with.overflow.i128", - - TyUint(U8) => "llvm.umul.with.overflow.i8", - TyUint(U16) => "llvm.umul.with.overflow.i16", - TyUint(U32) => "llvm.umul.with.overflow.i32", - TyUint(U64) => "llvm.umul.with.overflow.i64", - TyUint(U128) => "llvm.umul.with.overflow.i128", - - _ => unreachable!(), - }, - }; - - bx.cx.get_intrinsic(&name) -} - -fn cast_int_to_float(bx: &Builder, - signed: bool, - x: ValueRef, - int_ty: Type, - float_ty: Type) -> ValueRef { - // Most integer types, even i128, fit into [-f32::MAX, f32::MAX] after rounding. - // It's only u128 -> f32 that can cause overflows (i.e., should yield infinity). - // LLVM's uitofp produces undef in those cases, so we manually check for that case. - let is_u128_to_f32 = !signed && int_ty.int_width() == 128 && float_ty.float_width() == 32; - if is_u128_to_f32 { - // All inputs greater or equal to (f32::MAX + 0.5 ULP) are rounded to infinity, - // and for everything else LLVM's uitofp works just fine. - use rustc_apfloat::ieee::Single; - use rustc_apfloat::Float; - const MAX_F32_PLUS_HALF_ULP: u128 = ((1 << (Single::PRECISION + 1)) - 1) - << (Single::MAX_EXP - Single::PRECISION as i16); - let max = C_uint_big(int_ty, MAX_F32_PLUS_HALF_ULP); - let overflow = bx.icmp(llvm::IntUGE, x, max); - let infinity_bits = C_u32(bx.cx, ieee::Single::INFINITY.to_bits() as u32); - let infinity = consts::bitcast(infinity_bits, float_ty); - bx.select(overflow, infinity, bx.uitofp(x, float_ty)) - } else { - if signed { - bx.sitofp(x, float_ty) - } else { - bx.uitofp(x, float_ty) - } - } -} - -fn cast_float_to_int(bx: &Builder, - signed: bool, - x: ValueRef, - float_ty: Type, - int_ty: Type) -> ValueRef { - let fptosui_result = if signed { - bx.fptosi(x, int_ty) - } else { - bx.fptoui(x, int_ty) - }; - - if !bx.sess().opts.debugging_opts.saturating_float_casts { - return fptosui_result; - } - // LLVM's fpto[su]i returns undef when the input x is infinite, NaN, or does not fit into the - // destination integer type after rounding towards zero. This `undef` value can cause UB in - // safe code (see issue #10184), so we implement a saturating conversion on top of it: - // Semantically, the mathematical value of the input is rounded towards zero to the next - // mathematical integer, and then the result is clamped into the range of the destination - // integer type. Positive and negative infinity are mapped to the maximum and minimum value of - // the destination integer type. NaN is mapped to 0. - // - // Define f_min and f_max as the largest and smallest (finite) floats that are exactly equal to - // a value representable in int_ty. - // They are exactly equal to int_ty::{MIN,MAX} if float_ty has enough significand bits. - // Otherwise, int_ty::MAX must be rounded towards zero, as it is one less than a power of two. - // int_ty::MIN, however, is either zero or a negative power of two and is thus exactly - // representable. Note that this only works if float_ty's exponent range is sufficiently large. - // f16 or 256 bit integers would break this property. Right now the smallest float type is f32 - // with exponents ranging up to 127, which is barely enough for i128::MIN = -2^127. - // On the other hand, f_max works even if int_ty::MAX is greater than float_ty::MAX. Because - // we're rounding towards zero, we just get float_ty::MAX (which is always an integer). - // This already happens today with u128::MAX = 2^128 - 1 > f32::MAX. - fn compute_clamp_bounds(signed: bool, int_ty: Type) -> (u128, u128) { - let rounded_min = F::from_i128_r(int_min(signed, int_ty), Round::TowardZero); - assert_eq!(rounded_min.status, Status::OK); - let rounded_max = F::from_u128_r(int_max(signed, int_ty), Round::TowardZero); - assert!(rounded_max.value.is_finite()); - (rounded_min.value.to_bits(), rounded_max.value.to_bits()) - } - fn int_max(signed: bool, int_ty: Type) -> u128 { - let shift_amount = 128 - int_ty.int_width(); - if signed { - i128::MAX as u128 >> shift_amount - } else { - u128::MAX >> shift_amount - } - } - fn int_min(signed: bool, int_ty: Type) -> i128 { - if signed { - i128::MIN >> (128 - int_ty.int_width()) - } else { - 0 - } - } - let float_bits_to_llval = |bits| { - let bits_llval = match float_ty.float_width() { - 32 => C_u32(bx.cx, bits as u32), - 64 => C_u64(bx.cx, bits as u64), - n => bug!("unsupported float width {}", n), - }; - consts::bitcast(bits_llval, float_ty) - }; - let (f_min, f_max) = match float_ty.float_width() { - 32 => compute_clamp_bounds::(signed, int_ty), - 64 => compute_clamp_bounds::(signed, int_ty), - n => bug!("unsupported float width {}", n), - }; - let f_min = float_bits_to_llval(f_min); - let f_max = float_bits_to_llval(f_max); - // To implement saturation, we perform the following steps: - // - // 1. Cast x to an integer with fpto[su]i. This may result in undef. - // 2. Compare x to f_min and f_max, and use the comparison results to select: - // a) int_ty::MIN if x < f_min or x is NaN - // b) int_ty::MAX if x > f_max - // c) the result of fpto[su]i otherwise - // 3. If x is NaN, return 0.0, otherwise return the result of step 2. - // - // This avoids resulting undef because values in range [f_min, f_max] by definition fit into the - // destination type. It creates an undef temporary, but *producing* undef is not UB. Our use of - // undef does not introduce any non-determinism either. - // More importantly, the above procedure correctly implements saturating conversion. - // Proof (sketch): - // If x is NaN, 0 is returned by definition. - // Otherwise, x is finite or infinite and thus can be compared with f_min and f_max. - // This yields three cases to consider: - // (1) if x in [f_min, f_max], the result of fpto[su]i is returned, which agrees with - // saturating conversion for inputs in that range. - // (2) if x > f_max, then x is larger than int_ty::MAX. This holds even if f_max is rounded - // (i.e., if f_max < int_ty::MAX) because in those cases, nextUp(f_max) is already larger - // than int_ty::MAX. Because x is larger than int_ty::MAX, the return value of int_ty::MAX - // is correct. - // (3) if x < f_min, then x is smaller than int_ty::MIN. As shown earlier, f_min exactly equals - // int_ty::MIN and therefore the return value of int_ty::MIN is correct. - // QED. - - // Step 1 was already performed above. - - // Step 2: We use two comparisons and two selects, with %s1 being the result: - // %less_or_nan = fcmp ult %x, %f_min - // %greater = fcmp olt %x, %f_max - // %s0 = select %less_or_nan, int_ty::MIN, %fptosi_result - // %s1 = select %greater, int_ty::MAX, %s0 - // Note that %less_or_nan uses an *unordered* comparison. This comparison is true if the - // operands are not comparable (i.e., if x is NaN). The unordered comparison ensures that s1 - // becomes int_ty::MIN if x is NaN. - // Performance note: Unordered comparison can be lowered to a "flipped" comparison and a - // negation, and the negation can be merged into the select. Therefore, it not necessarily any - // more expensive than a ordered ("normal") comparison. Whether these optimizations will be - // performed is ultimately up to the backend, but at least x86 does perform them. - let less_or_nan = bx.fcmp(llvm::RealULT, x, f_min); - let greater = bx.fcmp(llvm::RealOGT, x, f_max); - let int_max = C_uint_big(int_ty, int_max(signed, int_ty)); - let int_min = C_uint_big(int_ty, int_min(signed, int_ty) as u128); - let s0 = bx.select(less_or_nan, int_min, fptosui_result); - let s1 = bx.select(greater, int_max, s0); - - // Step 3: NaN replacement. - // For unsigned types, the above step already yielded int_ty::MIN == 0 if x is NaN. - // Therefore we only need to execute this step for signed integer types. - if signed { - // LLVM has no isNaN predicate, so we use (x == x) instead - bx.select(bx.fcmp(llvm::RealOEQ, x, x), s1, C_uint(int_ty, 0)) - } else { - s1 - } -} diff --git a/src/librustc_trans/mir/statement.rs b/src/librustc_trans/mir/statement.rs deleted file mode 100644 index 579b07929a2..00000000000 --- a/src/librustc_trans/mir/statement.rs +++ /dev/null @@ -1,91 +0,0 @@ -// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -use rustc::mir; - -use asm; -use builder::Builder; - -use super::FunctionCx; -use super::LocalRef; - -impl<'a, 'tcx> FunctionCx<'a, 'tcx> { - pub fn trans_statement(&mut self, - bx: Builder<'a, 'tcx>, - statement: &mir::Statement<'tcx>) - -> Builder<'a, 'tcx> { - debug!("trans_statement(statement={:?})", statement); - - self.set_debug_loc(&bx, statement.source_info); - match statement.kind { - mir::StatementKind::Assign(ref place, ref rvalue) => { - if let mir::Place::Local(index) = *place { - match self.locals[index] { - LocalRef::Place(tr_dest) => { - self.trans_rvalue(bx, tr_dest, rvalue) - } - LocalRef::Operand(None) => { - let (bx, operand) = self.trans_rvalue_operand(bx, rvalue); - self.locals[index] = LocalRef::Operand(Some(operand)); - bx - } - LocalRef::Operand(Some(op)) => { - if !op.layout.is_zst() { - span_bug!(statement.source_info.span, - "operand {:?} already assigned", - rvalue); - } - - // If the type is zero-sized, it's already been set here, - // but we still need to make sure we translate the operand - self.trans_rvalue_operand(bx, rvalue).0 - } - } - } else { - let tr_dest = self.trans_place(&bx, place); - self.trans_rvalue(bx, tr_dest, rvalue) - } - } - mir::StatementKind::SetDiscriminant{ref place, variant_index} => { - self.trans_place(&bx, place) - .trans_set_discr(&bx, variant_index); - bx - } - mir::StatementKind::StorageLive(local) => { - if let LocalRef::Place(tr_place) = self.locals[local] { - tr_place.storage_live(&bx); - } - bx - } - mir::StatementKind::StorageDead(local) => { - if let LocalRef::Place(tr_place) = self.locals[local] { - tr_place.storage_dead(&bx); - } - bx - } - mir::StatementKind::InlineAsm { ref asm, ref outputs, ref inputs } => { - let outputs = outputs.iter().map(|output| { - self.trans_place(&bx, output) - }).collect(); - - let input_vals = inputs.iter().map(|input| { - self.trans_operand(&bx, input).immediate() - }).collect(); - - asm::trans_inline_asm(&bx, asm, outputs, input_vals); - bx - } - mir::StatementKind::EndRegion(_) | - mir::StatementKind::Validate(..) | - mir::StatementKind::UserAssertTy(..) | - mir::StatementKind::Nop => bx, - } - } -} diff --git a/src/librustc_trans/time_graph.rs b/src/librustc_trans/time_graph.rs deleted file mode 100644 index a8502682a80..00000000000 --- a/src/librustc_trans/time_graph.rs +++ /dev/null @@ -1,278 +0,0 @@ -// Copyright 2017 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -use std::collections::HashMap; -use std::fs::File; -use std::io::prelude::*; -use std::marker::PhantomData; -use std::mem; -use std::sync::{Arc, Mutex}; -use std::time::Instant; - -const OUTPUT_WIDTH_IN_PX: u64 = 1000; -const TIME_LINE_HEIGHT_IN_PX: u64 = 20; -const TIME_LINE_HEIGHT_STRIDE_IN_PX: usize = 30; - -#[derive(Clone)] -struct Timing { - start: Instant, - end: Instant, - work_package_kind: WorkPackageKind, - name: String, - events: Vec<(String, Instant)>, -} - -#[derive(Clone, Copy, Hash, Eq, PartialEq, Debug)] -pub struct TimelineId(pub usize); - -#[derive(Clone)] -struct PerThread { - timings: Vec, - open_work_package: Option<(Instant, WorkPackageKind, String)>, -} - -#[derive(Clone)] -pub struct TimeGraph { - data: Arc>>, -} - -#[derive(Clone, Copy)] -pub struct WorkPackageKind(pub &'static [&'static str]); - -pub struct Timeline { - token: Option, -} - -struct RaiiToken { - graph: TimeGraph, - timeline: TimelineId, - events: Vec<(String, Instant)>, - // The token must not be Send: - _marker: PhantomData<*const ()> -} - - -impl Drop for RaiiToken { - fn drop(&mut self) { - self.graph.end(self.timeline, mem::replace(&mut self.events, Vec::new())); - } -} - -impl TimeGraph { - pub fn new() -> TimeGraph { - TimeGraph { - data: Arc::new(Mutex::new(HashMap::new())) - } - } - - pub fn start(&self, - timeline: TimelineId, - work_package_kind: WorkPackageKind, - name: &str) -> Timeline { - { - let mut table = self.data.lock().unwrap(); - - let data = table.entry(timeline).or_insert(PerThread { - timings: Vec::new(), - open_work_package: None, - }); - - assert!(data.open_work_package.is_none()); - data.open_work_package = Some((Instant::now(), work_package_kind, name.to_string())); - } - - Timeline { - token: Some(RaiiToken { - graph: self.clone(), - timeline, - events: Vec::new(), - _marker: PhantomData, - }), - } - } - - fn end(&self, timeline: TimelineId, events: Vec<(String, Instant)>) { - let end = Instant::now(); - - let mut table = self.data.lock().unwrap(); - let data = table.get_mut(&timeline).unwrap(); - - if let Some((start, work_package_kind, name)) = data.open_work_package.take() { - data.timings.push(Timing { - start, - end, - work_package_kind, - name, - events, - }); - } else { - bug!("end timing without start?") - } - } - - pub fn dump(&self, output_filename: &str) { - let table = self.data.lock().unwrap(); - - for data in table.values() { - assert!(data.open_work_package.is_none()); - } - - let mut threads: Vec = - table.values().map(|data| data.clone()).collect(); - - threads.sort_by_key(|timeline| timeline.timings[0].start); - - let earliest_instant = threads[0].timings[0].start; - let latest_instant = threads.iter() - .map(|timeline| timeline.timings - .last() - .unwrap() - .end) - .max() - .unwrap(); - let max_distance = distance(earliest_instant, latest_instant); - - let mut file = File::create(format!("{}.html", output_filename)).unwrap(); - - writeln!(file, " - - - - - -
- ", - total_height = threads.len() * TIME_LINE_HEIGHT_STRIDE_IN_PX, - width = OUTPUT_WIDTH_IN_PX, - ).unwrap(); - - let mut color = 0; - for (line_index, thread) in threads.iter().enumerate() { - let line_top = line_index * TIME_LINE_HEIGHT_STRIDE_IN_PX; - - for span in &thread.timings { - let start = distance(earliest_instant, span.start); - let end = distance(earliest_instant, span.end); - - let start = normalize(start, max_distance, OUTPUT_WIDTH_IN_PX); - let end = normalize(end, max_distance, OUTPUT_WIDTH_IN_PX); - - let colors = span.work_package_kind.0; - - writeln!(file, "{}", - color, - line_top, - start, - end - start, - TIME_LINE_HEIGHT_IN_PX, - colors[color % colors.len()], - span.name, - ).unwrap(); - - color += 1; - } - } - - writeln!(file, " -
- ").unwrap(); - - let mut idx = 0; - for thread in threads.iter() { - for timing in &thread.timings { - let colors = timing.work_package_kind.0; - let height = TIME_LINE_HEIGHT_STRIDE_IN_PX * timing.events.len(); - writeln!(file, "
", - idx, - colors[idx % colors.len()], - height).unwrap(); - idx += 1; - let max = distance(timing.start, timing.end); - for (i, &(ref event, time)) in timing.events.iter().enumerate() { - let i = i as u64; - let time = distance(timing.start, time); - let at = normalize(time, max, OUTPUT_WIDTH_IN_PX); - writeln!(file, "{}", - at, - TIME_LINE_HEIGHT_IN_PX * i, - event).unwrap(); - } - writeln!(file, "
").unwrap(); - } - } - - writeln!(file, " - - - ").unwrap(); - } -} - -impl Timeline { - pub fn noop() -> Timeline { - Timeline { token: None } - } - - /// Record an event which happened at this moment on this timeline. - /// - /// Events are displayed in the eventual HTML output where you can click on - /// a particular timeline and it'll expand to all of the events that - /// happened on that timeline. This can then be used to drill into a - /// particular timeline and see what events are happening and taking the - /// most time. - pub fn record(&mut self, name: &str) { - if let Some(ref mut token) = self.token { - token.events.push((name.to_string(), Instant::now())); - } - } -} - -fn distance(zero: Instant, x: Instant) -> u64 { - - let duration = x.duration_since(zero); - (duration.as_secs() * 1_000_000_000 + duration.subsec_nanos() as u64) // / div -} - -fn normalize(distance: u64, max: u64, max_pixels: u64) -> u64 { - (max_pixels * distance) / max -} - diff --git a/src/librustc_trans/trans_item.rs b/src/librustc_trans/trans_item.rs deleted file mode 100644 index d19b5af2527..00000000000 --- a/src/librustc_trans/trans_item.rs +++ /dev/null @@ -1,193 +0,0 @@ -// Copyright 2016 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! Walks the crate looking for items/impl-items/trait-items that have -//! either a `rustc_symbol_name` or `rustc_item_path` attribute and -//! generates an error giving, respectively, the symbol name or -//! item-path. This is used for unit testing the code that generates -//! paths etc in all kinds of annoying scenarios. - -use asm; -use attributes; -use base; -use consts; -use context::CodegenCx; -use declare; -use llvm; -use monomorphize::Instance; -use type_of::LayoutLlvmExt; -use rustc::hir; -use rustc::hir::def::Def; -use rustc::hir::def_id::DefId; -use rustc::mir::mono::{Linkage, Visibility}; -use rustc::ty::TypeFoldable; -use rustc::ty::layout::LayoutOf; -use syntax::attr; -use std::fmt; - -pub use rustc::mir::mono::MonoItem; - -pub use rustc_mir::monomorphize::item::*; -pub use rustc_mir::monomorphize::item::MonoItemExt as BaseMonoItemExt; - -pub trait MonoItemExt<'a, 'tcx>: fmt::Debug + BaseMonoItemExt<'a, 'tcx> { - fn define(&self, cx: &CodegenCx<'a, 'tcx>) { - debug!("BEGIN IMPLEMENTING '{} ({})' in cgu {}", - self.to_string(cx.tcx), - self.to_raw_string(), - cx.codegen_unit.name()); - - match *self.as_mono_item() { - MonoItem::Static(def_id) => { - let tcx = cx.tcx; - let is_mutable = match tcx.describe_def(def_id) { - Some(Def::Static(_, is_mutable)) => is_mutable, - Some(other) => { - bug!("Expected Def::Static, found {:?}", other) - } - None => { - bug!("Expected Def::Static for {:?}, found nothing", def_id) - } - }; - let attrs = tcx.get_attrs(def_id); - - consts::trans_static(&cx, def_id, is_mutable, &attrs); - } - MonoItem::GlobalAsm(node_id) => { - let item = cx.tcx.hir.expect_item(node_id); - if let hir::ItemGlobalAsm(ref ga) = item.node { - asm::trans_global_asm(cx, ga); - } else { - span_bug!(item.span, "Mismatch between hir::Item type and TransItem type") - } - } - MonoItem::Fn(instance) => { - base::trans_instance(&cx, instance); - } - } - - debug!("END IMPLEMENTING '{} ({})' in cgu {}", - self.to_string(cx.tcx), - self.to_raw_string(), - cx.codegen_unit.name()); - } - - fn predefine(&self, - cx: &CodegenCx<'a, 'tcx>, - linkage: Linkage, - visibility: Visibility) { - debug!("BEGIN PREDEFINING '{} ({})' in cgu {}", - self.to_string(cx.tcx), - self.to_raw_string(), - cx.codegen_unit.name()); - - let symbol_name = self.symbol_name(cx.tcx).as_str(); - - debug!("symbol {}", &symbol_name); - - match *self.as_mono_item() { - MonoItem::Static(def_id) => { - predefine_static(cx, def_id, linkage, visibility, &symbol_name); - } - MonoItem::Fn(instance) => { - predefine_fn(cx, instance, linkage, visibility, &symbol_name); - } - MonoItem::GlobalAsm(..) => {} - } - - debug!("END PREDEFINING '{} ({})' in cgu {}", - self.to_string(cx.tcx), - self.to_raw_string(), - cx.codegen_unit.name()); - } - - fn to_raw_string(&self) -> String { - match *self.as_mono_item() { - MonoItem::Fn(instance) => { - format!("Fn({:?}, {})", - instance.def, - instance.substs.as_ptr() as usize) - } - MonoItem::Static(id) => { - format!("Static({:?})", id) - } - MonoItem::GlobalAsm(id) => { - format!("GlobalAsm({:?})", id) - } - } - } -} - -impl<'a, 'tcx> MonoItemExt<'a, 'tcx> for MonoItem<'tcx> {} - -fn predefine_static<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, - def_id: DefId, - linkage: Linkage, - visibility: Visibility, - symbol_name: &str) { - let instance = Instance::mono(cx.tcx, def_id); - let ty = instance.ty(cx.tcx); - let llty = cx.layout_of(ty).llvm_type(cx); - - let g = declare::define_global(cx, symbol_name, llty).unwrap_or_else(|| { - cx.sess().span_fatal(cx.tcx.def_span(def_id), - &format!("symbol `{}` is already defined", symbol_name)) - }); - - unsafe { - llvm::LLVMRustSetLinkage(g, base::linkage_to_llvm(linkage)); - llvm::LLVMRustSetVisibility(g, base::visibility_to_llvm(visibility)); - } - - cx.instances.borrow_mut().insert(instance, g); - cx.statics.borrow_mut().insert(g, def_id); -} - -fn predefine_fn<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, - instance: Instance<'tcx>, - linkage: Linkage, - visibility: Visibility, - symbol_name: &str) { - assert!(!instance.substs.needs_infer() && - !instance.substs.has_param_types()); - - let mono_ty = instance.ty(cx.tcx); - let attrs = instance.def.attrs(cx.tcx); - let lldecl = declare::declare_fn(cx, symbol_name, mono_ty); - unsafe { llvm::LLVMRustSetLinkage(lldecl, base::linkage_to_llvm(linkage)) }; - base::set_link_section(cx, lldecl, &attrs); - if linkage == Linkage::LinkOnceODR || - linkage == Linkage::WeakODR { - llvm::SetUniqueComdat(cx.llmod, lldecl); - } - - // If we're compiling the compiler-builtins crate, e.g. the equivalent of - // compiler-rt, then we want to implicitly compile everything with hidden - // visibility as we're going to link this object all over the place but - // don't want the symbols to get exported. - if linkage != Linkage::Internal && linkage != Linkage::Private && - attr::contains_name(cx.tcx.hir.krate_attrs(), "compiler_builtins") { - unsafe { - llvm::LLVMRustSetVisibility(lldecl, llvm::Visibility::Hidden); - } - } else { - unsafe { - llvm::LLVMRustSetVisibility(lldecl, base::visibility_to_llvm(visibility)); - } - } - - debug!("predefine_fn: mono_ty = {:?} instance = {:?}", mono_ty, instance); - if instance.def.is_inline(cx.tcx) { - attributes::inline(lldecl, attributes::InlineAttr::Hint); - } - attributes::from_fn_attrs(cx, lldecl, instance.def.def_id()); - - cx.instances.borrow_mut().insert(instance, lldecl); -} diff --git a/src/librustc_trans/type_.rs b/src/librustc_trans/type_.rs deleted file mode 100644 index a77acc4f175..00000000000 --- a/src/librustc_trans/type_.rs +++ /dev/null @@ -1,300 +0,0 @@ -// Copyright 2013 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. - -#![allow(non_upper_case_globals)] - -use llvm; -use llvm::{ContextRef, TypeRef, Bool, False, True, TypeKind}; -use llvm::{Float, Double, X86_FP80, PPC_FP128, FP128}; - -use context::CodegenCx; - -use syntax::ast; -use rustc::ty::layout::{self, Align, Size}; - -use std::ffi::CString; -use std::fmt; -use std::mem; -use std::ptr; - -use libc::c_uint; - -#[derive(Clone, Copy, PartialEq)] -#[repr(C)] -pub struct Type { - rf: TypeRef -} - -impl fmt::Debug for Type { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.write_str(&llvm::build_string(|s| unsafe { - llvm::LLVMRustWriteTypeToString(self.to_ref(), s); - }).expect("non-UTF8 type description from LLVM")) - } -} - -macro_rules! ty { - ($e:expr) => ( Type::from_ref(unsafe { $e })) -} - -/// Wrapper for LLVM TypeRef -impl Type { - #[inline(always)] - pub fn from_ref(r: TypeRef) -> Type { - Type { - rf: r - } - } - - #[inline(always)] // So it doesn't kill --opt-level=0 builds of the compiler - pub fn to_ref(&self) -> TypeRef { - self.rf - } - - pub fn to_ref_slice(slice: &[Type]) -> &[TypeRef] { - unsafe { mem::transmute(slice) } - } - - pub fn void(cx: &CodegenCx) -> Type { - ty!(llvm::LLVMVoidTypeInContext(cx.llcx)) - } - - pub fn metadata(cx: &CodegenCx) -> Type { - ty!(llvm::LLVMRustMetadataTypeInContext(cx.llcx)) - } - - pub fn i1(cx: &CodegenCx) -> Type { - ty!(llvm::LLVMInt1TypeInContext(cx.llcx)) - } - - pub fn i8(cx: &CodegenCx) -> Type { - ty!(llvm::LLVMInt8TypeInContext(cx.llcx)) - } - - pub fn i8_llcx(llcx: ContextRef) -> Type { - ty!(llvm::LLVMInt8TypeInContext(llcx)) - } - - pub fn i16(cx: &CodegenCx) -> Type { - ty!(llvm::LLVMInt16TypeInContext(cx.llcx)) - } - - pub fn i32(cx: &CodegenCx) -> Type { - ty!(llvm::LLVMInt32TypeInContext(cx.llcx)) - } - - pub fn i64(cx: &CodegenCx) -> Type { - ty!(llvm::LLVMInt64TypeInContext(cx.llcx)) - } - - pub fn i128(cx: &CodegenCx) -> Type { - ty!(llvm::LLVMIntTypeInContext(cx.llcx, 128)) - } - - // Creates an integer type with the given number of bits, e.g. i24 - pub fn ix(cx: &CodegenCx, num_bits: u64) -> Type { - ty!(llvm::LLVMIntTypeInContext(cx.llcx, num_bits as c_uint)) - } - - pub fn f32(cx: &CodegenCx) -> Type { - ty!(llvm::LLVMFloatTypeInContext(cx.llcx)) - } - - pub fn f64(cx: &CodegenCx) -> Type { - ty!(llvm::LLVMDoubleTypeInContext(cx.llcx)) - } - - pub fn bool(cx: &CodegenCx) -> Type { - Type::i8(cx) - } - - pub fn char(cx: &CodegenCx) -> Type { - Type::i32(cx) - } - - pub fn i8p(cx: &CodegenCx) -> Type { - Type::i8(cx).ptr_to() - } - - pub fn i8p_llcx(llcx: ContextRef) -> Type { - Type::i8_llcx(llcx).ptr_to() - } - - pub fn isize(cx: &CodegenCx) -> Type { - match &cx.tcx.sess.target.target.target_pointer_width[..] { - "16" => Type::i16(cx), - "32" => Type::i32(cx), - "64" => Type::i64(cx), - tws => bug!("Unsupported target word size for int: {}", tws), - } - } - - pub fn c_int(cx: &CodegenCx) -> Type { - match &cx.tcx.sess.target.target.target_c_int_width[..] { - "16" => Type::i16(cx), - "32" => Type::i32(cx), - "64" => Type::i64(cx), - width => bug!("Unsupported target_c_int_width: {}", width), - } - } - - pub fn int_from_ty(cx: &CodegenCx, t: ast::IntTy) -> Type { - match t { - ast::IntTy::Isize => cx.isize_ty, - ast::IntTy::I8 => Type::i8(cx), - ast::IntTy::I16 => Type::i16(cx), - ast::IntTy::I32 => Type::i32(cx), - ast::IntTy::I64 => Type::i64(cx), - ast::IntTy::I128 => Type::i128(cx), - } - } - - pub fn uint_from_ty(cx: &CodegenCx, t: ast::UintTy) -> Type { - match t { - ast::UintTy::Usize => cx.isize_ty, - ast::UintTy::U8 => Type::i8(cx), - ast::UintTy::U16 => Type::i16(cx), - ast::UintTy::U32 => Type::i32(cx), - ast::UintTy::U64 => Type::i64(cx), - ast::UintTy::U128 => Type::i128(cx), - } - } - - pub fn float_from_ty(cx: &CodegenCx, t: ast::FloatTy) -> Type { - match t { - ast::FloatTy::F32 => Type::f32(cx), - ast::FloatTy::F64 => Type::f64(cx), - } - } - - pub fn func(args: &[Type], ret: &Type) -> Type { - let slice: &[TypeRef] = Type::to_ref_slice(args); - ty!(llvm::LLVMFunctionType(ret.to_ref(), slice.as_ptr(), - args.len() as c_uint, False)) - } - - pub fn variadic_func(args: &[Type], ret: &Type) -> Type { - let slice: &[TypeRef] = Type::to_ref_slice(args); - ty!(llvm::LLVMFunctionType(ret.to_ref(), slice.as_ptr(), - args.len() as c_uint, True)) - } - - pub fn struct_(cx: &CodegenCx, els: &[Type], packed: bool) -> Type { - let els: &[TypeRef] = Type::to_ref_slice(els); - ty!(llvm::LLVMStructTypeInContext(cx.llcx, els.as_ptr(), - els.len() as c_uint, - packed as Bool)) - } - - pub fn named_struct(cx: &CodegenCx, name: &str) -> Type { - let name = CString::new(name).unwrap(); - ty!(llvm::LLVMStructCreateNamed(cx.llcx, name.as_ptr())) - } - - - pub fn array(ty: &Type, len: u64) -> Type { - ty!(llvm::LLVMRustArrayType(ty.to_ref(), len)) - } - - pub fn vector(ty: &Type, len: u64) -> Type { - ty!(llvm::LLVMVectorType(ty.to_ref(), len as c_uint)) - } - - pub fn kind(&self) -> TypeKind { - unsafe { - llvm::LLVMRustGetTypeKind(self.to_ref()) - } - } - - pub fn set_struct_body(&mut self, els: &[Type], packed: bool) { - let slice: &[TypeRef] = Type::to_ref_slice(els); - unsafe { - llvm::LLVMStructSetBody(self.to_ref(), slice.as_ptr(), - els.len() as c_uint, packed as Bool) - } - } - - pub fn ptr_to(&self) -> Type { - ty!(llvm::LLVMPointerType(self.to_ref(), 0)) - } - - pub fn element_type(&self) -> Type { - unsafe { - Type::from_ref(llvm::LLVMGetElementType(self.to_ref())) - } - } - - /// Return the number of elements in `self` if it is a LLVM vector type. - pub fn vector_length(&self) -> usize { - unsafe { - llvm::LLVMGetVectorSize(self.to_ref()) as usize - } - } - - pub fn func_params(&self) -> Vec { - unsafe { - let n_args = llvm::LLVMCountParamTypes(self.to_ref()) as usize; - let mut args = vec![Type { rf: ptr::null_mut() }; n_args]; - llvm::LLVMGetParamTypes(self.to_ref(), - args.as_mut_ptr() as *mut TypeRef); - args - } - } - - pub fn float_width(&self) -> usize { - match self.kind() { - Float => 32, - Double => 64, - X86_FP80 => 80, - FP128 | PPC_FP128 => 128, - _ => bug!("llvm_float_width called on a non-float type") - } - } - - /// Retrieve the bit width of the integer type `self`. - pub fn int_width(&self) -> u64 { - unsafe { - llvm::LLVMGetIntTypeWidth(self.to_ref()) as u64 - } - } - - pub fn from_integer(cx: &CodegenCx, i: layout::Integer) -> Type { - use rustc::ty::layout::Integer::*; - match i { - I8 => Type::i8(cx), - I16 => Type::i16(cx), - I32 => Type::i32(cx), - I64 => Type::i64(cx), - I128 => Type::i128(cx), - } - } - - /// Return a LLVM type that has at most the required alignment, - /// as a conservative approximation for unknown pointee types. - pub fn pointee_for_abi_align(cx: &CodegenCx, align: Align) -> Type { - // FIXME(eddyb) We could find a better approximation if ity.align < align. - let ity = layout::Integer::approximate_abi_align(cx, align); - Type::from_integer(cx, ity) - } - - /// Return a LLVM type that has at most the required alignment, - /// and exactly the required size, as a best-effort padding array. - pub fn padding_filler(cx: &CodegenCx, size: Size, align: Align) -> Type { - let unit = layout::Integer::approximate_abi_align(cx, align); - let size = size.bytes(); - let unit_size = unit.size().bytes(); - assert_eq!(size % unit_size, 0); - Type::array(&Type::from_integer(cx, unit), size / unit_size) - } - - pub fn x86_mmx(cx: &CodegenCx) -> Type { - ty!(llvm::LLVMX86MMXTypeInContext(cx.llcx)) - } -} diff --git a/src/librustc_trans/type_of.rs b/src/librustc_trans/type_of.rs deleted file mode 100644 index 32d26052aff..00000000000 --- a/src/librustc_trans/type_of.rs +++ /dev/null @@ -1,506 +0,0 @@ -// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -use abi::{FnType, FnTypeExt}; -use common::*; -use rustc::hir; -use rustc::ty::{self, Ty, TypeFoldable}; -use rustc::ty::layout::{self, Align, LayoutOf, Size, TyLayout}; -use rustc_target::spec::PanicStrategy; -use trans_item::DefPathBasedNames; -use type_::Type; - -use std::fmt::Write; - -fn uncached_llvm_type<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, - layout: TyLayout<'tcx>, - defer: &mut Option<(Type, TyLayout<'tcx>)>) - -> Type { - match layout.abi { - layout::Abi::Scalar(_) => bug!("handled elsewhere"), - layout::Abi::Vector { ref element, count } => { - // LLVM has a separate type for 64-bit SIMD vectors on X86 called - // `x86_mmx` which is needed for some SIMD operations. As a bit of a - // hack (all SIMD definitions are super unstable anyway) we - // recognize any one-element SIMD vector as "this should be an - // x86_mmx" type. In general there shouldn't be a need for other - // one-element SIMD vectors, so it's assumed this won't clash with - // much else. - let use_x86_mmx = count == 1 && layout.size.bits() == 64 && - (cx.sess().target.target.arch == "x86" || - cx.sess().target.target.arch == "x86_64"); - if use_x86_mmx { - return Type::x86_mmx(cx) - } else { - let element = layout.scalar_llvm_type_at(cx, element, Size::from_bytes(0)); - return Type::vector(&element, count); - } - } - layout::Abi::ScalarPair(..) => { - return Type::struct_(cx, &[ - layout.scalar_pair_element_llvm_type(cx, 0), - layout.scalar_pair_element_llvm_type(cx, 1), - ], false); - } - layout::Abi::Uninhabited | - layout::Abi::Aggregate { .. } => {} - } - - let name = match layout.ty.sty { - ty::TyClosure(..) | - ty::TyGenerator(..) | - ty::TyAdt(..) | - // FIXME(eddyb) producing readable type names for trait objects can result - // in problematically distinct types due to HRTB and subtyping (see #47638). - // ty::TyDynamic(..) | - ty::TyForeign(..) | - ty::TyStr => { - let mut name = String::with_capacity(32); - let printer = DefPathBasedNames::new(cx.tcx, true, true); - printer.push_type_name(layout.ty, &mut name); - match (&layout.ty.sty, &layout.variants) { - (&ty::TyAdt(def, _), &layout::Variants::Single { index }) => { - if def.is_enum() && !def.variants.is_empty() { - write!(&mut name, "::{}", def.variants[index].name).unwrap(); - } - } - _ => {} - } - Some(name) - } - _ => None - }; - - match layout.fields { - layout::FieldPlacement::Union(_) => { - let fill = Type::padding_filler(cx, layout.size, layout.align); - let packed = false; - match name { - None => { - Type::struct_(cx, &[fill], packed) - } - Some(ref name) => { - let mut llty = Type::named_struct(cx, name); - llty.set_struct_body(&[fill], packed); - llty - } - } - } - layout::FieldPlacement::Array { count, .. } => { - Type::array(&layout.field(cx, 0).llvm_type(cx), count) - } - layout::FieldPlacement::Arbitrary { .. } => { - match name { - None => { - let (llfields, packed) = struct_llfields(cx, layout); - Type::struct_(cx, &llfields, packed) - } - Some(ref name) => { - let llty = Type::named_struct(cx, name); - *defer = Some((llty, layout)); - llty - } - } - } - } -} - -fn struct_llfields<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, - layout: TyLayout<'tcx>) - -> (Vec, bool) { - debug!("struct_llfields: {:#?}", layout); - let field_count = layout.fields.count(); - - let mut packed = false; - let mut offset = Size::from_bytes(0); - let mut prev_align = layout.align; - let mut result: Vec = Vec::with_capacity(1 + field_count * 2); - for i in layout.fields.index_by_increasing_offset() { - let field = layout.field(cx, i); - packed |= layout.align.abi() < field.align.abi(); - - let target_offset = layout.fields.offset(i as usize); - debug!("struct_llfields: {}: {:?} offset: {:?} target_offset: {:?}", - i, field, offset, target_offset); - assert!(target_offset >= offset); - let padding = target_offset - offset; - let padding_align = layout.align.min(prev_align).min(field.align); - assert_eq!(offset.abi_align(padding_align) + padding, target_offset); - result.push(Type::padding_filler(cx, padding, padding_align)); - debug!(" padding before: {:?}", padding); - - result.push(field.llvm_type(cx)); - offset = target_offset + field.size; - prev_align = field.align; - } - if !layout.is_unsized() && field_count > 0 { - if offset > layout.size { - bug!("layout: {:#?} stride: {:?} offset: {:?}", - layout, layout.size, offset); - } - let padding = layout.size - offset; - let padding_align = layout.align.min(prev_align); - assert_eq!(offset.abi_align(padding_align) + padding, layout.size); - debug!("struct_llfields: pad_bytes: {:?} offset: {:?} stride: {:?}", - padding, offset, layout.size); - result.push(Type::padding_filler(cx, padding, padding_align)); - assert!(result.len() == 1 + field_count * 2); - } else { - debug!("struct_llfields: offset: {:?} stride: {:?}", - offset, layout.size); - } - - (result, packed) -} - -impl<'a, 'tcx> CodegenCx<'a, 'tcx> { - pub fn align_of(&self, ty: Ty<'tcx>) -> Align { - self.layout_of(ty).align - } - - pub fn size_of(&self, ty: Ty<'tcx>) -> Size { - self.layout_of(ty).size - } - - pub fn size_and_align_of(&self, ty: Ty<'tcx>) -> (Size, Align) { - self.layout_of(ty).size_and_align() - } -} - -#[derive(Copy, Clone, PartialEq, Eq)] -pub enum PointerKind { - /// Most general case, we know no restrictions to tell LLVM. - Shared, - - /// `&T` where `T` contains no `UnsafeCell`, is `noalias` and `readonly`. - Frozen, - - /// `&mut T`, when we know `noalias` is safe for LLVM. - UniqueBorrowed, - - /// `Box`, unlike `UniqueBorrowed`, it also has `noalias` on returns. - UniqueOwned -} - -#[derive(Copy, Clone)] -pub struct PointeeInfo { - pub size: Size, - pub align: Align, - pub safe: Option, -} - -pub trait LayoutLlvmExt<'tcx> { - fn is_llvm_immediate(&self) -> bool; - fn is_llvm_scalar_pair<'a>(&self) -> bool; - fn llvm_type<'a>(&self, cx: &CodegenCx<'a, 'tcx>) -> Type; - fn immediate_llvm_type<'a>(&self, cx: &CodegenCx<'a, 'tcx>) -> Type; - fn scalar_llvm_type_at<'a>(&self, cx: &CodegenCx<'a, 'tcx>, - scalar: &layout::Scalar, offset: Size) -> Type; - fn scalar_pair_element_llvm_type<'a>(&self, cx: &CodegenCx<'a, 'tcx>, - index: usize) -> Type; - fn llvm_field_index(&self, index: usize) -> u64; - fn pointee_info_at<'a>(&self, cx: &CodegenCx<'a, 'tcx>, offset: Size) - -> Option; -} - -impl<'tcx> LayoutLlvmExt<'tcx> for TyLayout<'tcx> { - fn is_llvm_immediate(&self) -> bool { - match self.abi { - layout::Abi::Scalar(_) | - layout::Abi::Vector { .. } => true, - layout::Abi::ScalarPair(..) => false, - layout::Abi::Uninhabited | - layout::Abi::Aggregate { .. } => self.is_zst() - } - } - - fn is_llvm_scalar_pair<'a>(&self) -> bool { - match self.abi { - layout::Abi::ScalarPair(..) => true, - layout::Abi::Uninhabited | - layout::Abi::Scalar(_) | - layout::Abi::Vector { .. } | - layout::Abi::Aggregate { .. } => false - } - } - - /// Get the LLVM type corresponding to a Rust type, i.e. `rustc::ty::Ty`. - /// The pointee type of the pointer in `PlaceRef` is always this type. - /// For sized types, it is also the right LLVM type for an `alloca` - /// containing a value of that type, and most immediates (except `bool`). - /// Unsized types, however, are represented by a "minimal unit", e.g. - /// `[T]` becomes `T`, while `str` and `Trait` turn into `i8` - this - /// is useful for indexing slices, as `&[T]`'s data pointer is `T*`. - /// If the type is an unsized struct, the regular layout is generated, - /// with the inner-most trailing unsized field using the "minimal unit" - /// of that field's type - this is useful for taking the address of - /// that field and ensuring the struct has the right alignment. - fn llvm_type<'a>(&self, cx: &CodegenCx<'a, 'tcx>) -> Type { - if let layout::Abi::Scalar(ref scalar) = self.abi { - // Use a different cache for scalars because pointers to DSTs - // can be either fat or thin (data pointers of fat pointers). - if let Some(&llty) = cx.scalar_lltypes.borrow().get(&self.ty) { - return llty; - } - let llty = match self.ty.sty { - ty::TyRef(_, ty, _) | - ty::TyRawPtr(ty::TypeAndMut { ty, .. }) => { - cx.layout_of(ty).llvm_type(cx).ptr_to() - } - ty::TyAdt(def, _) if def.is_box() => { - cx.layout_of(self.ty.boxed_ty()).llvm_type(cx).ptr_to() - } - ty::TyFnPtr(sig) => { - let sig = cx.tcx.normalize_erasing_late_bound_regions( - ty::ParamEnv::reveal_all(), - &sig, - ); - FnType::new(cx, sig, &[]).llvm_type(cx).ptr_to() - } - _ => self.scalar_llvm_type_at(cx, scalar, Size::from_bytes(0)) - }; - cx.scalar_lltypes.borrow_mut().insert(self.ty, llty); - return llty; - } - - - // Check the cache. - let variant_index = match self.variants { - layout::Variants::Single { index } => Some(index), - _ => None - }; - if let Some(&llty) = cx.lltypes.borrow().get(&(self.ty, variant_index)) { - return llty; - } - - debug!("llvm_type({:#?})", self); - - assert!(!self.ty.has_escaping_regions(), "{:?} has escaping regions", self.ty); - - // Make sure lifetimes are erased, to avoid generating distinct LLVM - // types for Rust types that only differ in the choice of lifetimes. - let normal_ty = cx.tcx.erase_regions(&self.ty); - - let mut defer = None; - let llty = if self.ty != normal_ty { - let mut layout = cx.layout_of(normal_ty); - if let Some(v) = variant_index { - layout = layout.for_variant(cx, v); - } - layout.llvm_type(cx) - } else { - uncached_llvm_type(cx, *self, &mut defer) - }; - debug!("--> mapped {:#?} to llty={:?}", self, llty); - - cx.lltypes.borrow_mut().insert((self.ty, variant_index), llty); - - if let Some((mut llty, layout)) = defer { - let (llfields, packed) = struct_llfields(cx, layout); - llty.set_struct_body(&llfields, packed) - } - - llty - } - - fn immediate_llvm_type<'a>(&self, cx: &CodegenCx<'a, 'tcx>) -> Type { - if let layout::Abi::Scalar(ref scalar) = self.abi { - if scalar.is_bool() { - return Type::i1(cx); - } - } - self.llvm_type(cx) - } - - fn scalar_llvm_type_at<'a>(&self, cx: &CodegenCx<'a, 'tcx>, - scalar: &layout::Scalar, offset: Size) -> Type { - match scalar.value { - layout::Int(i, _) => Type::from_integer(cx, i), - layout::F32 => Type::f32(cx), - layout::F64 => Type::f64(cx), - layout::Pointer => { - // If we know the alignment, pick something better than i8. - let pointee = if let Some(pointee) = self.pointee_info_at(cx, offset) { - Type::pointee_for_abi_align(cx, pointee.align) - } else { - Type::i8(cx) - }; - pointee.ptr_to() - } - } - } - - fn scalar_pair_element_llvm_type<'a>(&self, cx: &CodegenCx<'a, 'tcx>, - index: usize) -> Type { - // HACK(eddyb) special-case fat pointers until LLVM removes - // pointee types, to avoid bitcasting every `OperandRef::deref`. - match self.ty.sty { - ty::TyRef(..) | - ty::TyRawPtr(_) => { - return self.field(cx, index).llvm_type(cx); - } - ty::TyAdt(def, _) if def.is_box() => { - let ptr_ty = cx.tcx.mk_mut_ptr(self.ty.boxed_ty()); - return cx.layout_of(ptr_ty).scalar_pair_element_llvm_type(cx, index); - } - _ => {} - } - - let (a, b) = match self.abi { - layout::Abi::ScalarPair(ref a, ref b) => (a, b), - _ => bug!("TyLayout::scalar_pair_element_llty({:?}): not applicable", self) - }; - let scalar = [a, b][index]; - - // Make sure to return the same type `immediate_llvm_type` would, - // to avoid dealing with two types and the associated conversions. - // This means that `(bool, bool)` is represented as `{i1, i1}`, - // both in memory and as an immediate, while `bool` is typically - // `i8` in memory and only `i1` when immediate. While we need to - // load/store `bool` as `i8` to avoid crippling LLVM optimizations, - // `i1` in a LLVM aggregate is valid and mostly equivalent to `i8`. - if scalar.is_bool() { - return Type::i1(cx); - } - - let offset = if index == 0 { - Size::from_bytes(0) - } else { - a.value.size(cx).abi_align(b.value.align(cx)) - }; - self.scalar_llvm_type_at(cx, scalar, offset) - } - - fn llvm_field_index(&self, index: usize) -> u64 { - match self.abi { - layout::Abi::Scalar(_) | - layout::Abi::ScalarPair(..) => { - bug!("TyLayout::llvm_field_index({:?}): not applicable", self) - } - _ => {} - } - match self.fields { - layout::FieldPlacement::Union(_) => { - bug!("TyLayout::llvm_field_index({:?}): not applicable", self) - } - - layout::FieldPlacement::Array { .. } => { - index as u64 - } - - layout::FieldPlacement::Arbitrary { .. } => { - 1 + (self.fields.memory_index(index) as u64) * 2 - } - } - } - - fn pointee_info_at<'a>(&self, cx: &CodegenCx<'a, 'tcx>, offset: Size) - -> Option { - if let Some(&pointee) = cx.pointee_infos.borrow().get(&(self.ty, offset)) { - return pointee; - } - - let mut result = None; - match self.ty.sty { - ty::TyRawPtr(mt) if offset.bytes() == 0 => { - let (size, align) = cx.size_and_align_of(mt.ty); - result = Some(PointeeInfo { - size, - align, - safe: None - }); - } - - ty::TyRef(_, ty, mt) if offset.bytes() == 0 => { - let (size, align) = cx.size_and_align_of(ty); - - let kind = match mt { - hir::MutImmutable => if cx.type_is_freeze(ty) { - PointerKind::Frozen - } else { - PointerKind::Shared - }, - hir::MutMutable => { - if cx.tcx.sess.opts.debugging_opts.mutable_noalias || - cx.tcx.sess.panic_strategy() == PanicStrategy::Abort { - PointerKind::UniqueBorrowed - } else { - PointerKind::Shared - } - } - }; - - result = Some(PointeeInfo { - size, - align, - safe: Some(kind) - }); - } - - _ => { - let mut data_variant = match self.variants { - layout::Variants::NicheFilling { dataful_variant, .. } => { - // Only the niche itself is always initialized, - // so only check for a pointer at its offset. - // - // If the niche is a pointer, it's either valid - // (according to its type), or null (which the - // niche field's scalar validity range encodes). - // This allows using `dereferenceable_or_null` - // for e.g. `Option<&T>`, and this will continue - // to work as long as we don't start using more - // niches than just null (e.g. the first page - // of the address space, or unaligned pointers). - if self.fields.offset(0) == offset { - Some(self.for_variant(cx, dataful_variant)) - } else { - None - } - } - _ => Some(*self) - }; - - if let Some(variant) = data_variant { - // We're not interested in any unions. - if let layout::FieldPlacement::Union(_) = variant.fields { - data_variant = None; - } - } - - if let Some(variant) = data_variant { - let ptr_end = offset + layout::Pointer.size(cx); - for i in 0..variant.fields.count() { - let field_start = variant.fields.offset(i); - if field_start <= offset { - let field = variant.field(cx, i); - if ptr_end <= field_start + field.size { - // We found the right field, look inside it. - result = field.pointee_info_at(cx, offset - field_start); - break; - } - } - } - } - - // FIXME(eddyb) This should be for `ptr::Unique`, not `Box`. - if let Some(ref mut pointee) = result { - if let ty::TyAdt(def, _) = self.ty.sty { - if def.is_box() && offset.bytes() == 0 { - pointee.safe = Some(PointerKind::UniqueOwned); - } - } - } - } - } - - cx.pointee_infos.borrow_mut().insert((self.ty, offset), result); - result - } -} diff --git a/src/librustc_trans/value.rs b/src/librustc_trans/value.rs deleted file mode 100644 index 287ad87caac..00000000000 --- a/src/librustc_trans/value.rs +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright 2013 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -use llvm; - -use std::fmt; - -#[derive(Copy, Clone, PartialEq)] -pub struct Value(pub llvm::ValueRef); - -impl fmt::Debug for Value { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.write_str(&llvm::build_string(|s| unsafe { - llvm::LLVMRustWriteValueToString(self.0, s); - }).expect("nun-UTF8 value description from LLVM")) - } -} diff --git a/src/librustc_trans_utils/Cargo.toml b/src/librustc_trans_utils/Cargo.toml deleted file mode 100644 index 323d9d1ceda..00000000000 --- a/src/librustc_trans_utils/Cargo.toml +++ /dev/null @@ -1,23 +0,0 @@ -[package] -authors = ["The Rust Project Developers"] -name = "rustc_trans_utils" -version = "0.0.0" - -[lib] -name = "rustc_trans_utils" -path = "lib.rs" -crate-type = ["dylib"] -test = false - -[dependencies] -ar = "0.3.0" -flate2 = "1.0" -log = "0.4" - -syntax = { path = "../libsyntax" } -syntax_pos = { path = "../libsyntax_pos" } -rustc = { path = "../librustc" } -rustc_target = { path = "../librustc_target" } -rustc_data_structures = { path = "../librustc_data_structures" } -rustc_mir = { path = "../librustc_mir" } -rustc_incremental = { path = "../librustc_incremental" } diff --git a/src/librustc_trans_utils/lib.rs b/src/librustc_trans_utils/lib.rs deleted file mode 100644 index b91f4e4dffb..00000000000 --- a/src/librustc_trans_utils/lib.rs +++ /dev/null @@ -1,63 +0,0 @@ -// Copyright 2017 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! # Note -//! -//! This API is completely unstable and subject to change. - -#![doc(html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png", - html_favicon_url = "https://doc.rust-lang.org/favicon.ico", - html_root_url = "https://doc.rust-lang.org/nightly/")] - -#![feature(box_patterns)] -#![feature(box_syntax)] -#![feature(custom_attribute)] -#![allow(unused_attributes)] -#![feature(quote)] -#![feature(rustc_diagnostic_macros)] - -extern crate ar; -extern crate flate2; -#[macro_use] -extern crate log; - -#[macro_use] -extern crate rustc; -extern crate rustc_target; -extern crate rustc_mir; -extern crate rustc_incremental; -extern crate syntax; -extern crate syntax_pos; -#[macro_use] extern crate rustc_data_structures; - -pub extern crate rustc as __rustc; - -use rustc::ty::TyCtxt; - -pub mod link; -pub mod trans_crate; -pub mod symbol_names; -pub mod symbol_names_test; - -/// check for the #[rustc_error] annotation, which forces an -/// error in trans. This is used to write compile-fail tests -/// that actually test that compilation succeeds without -/// reporting an error. -pub fn check_for_rustc_errors_attr(tcx: TyCtxt) { - if let Some((id, span, _)) = *tcx.sess.entry_fn.borrow() { - let main_def_id = tcx.hir.local_def_id(id); - - if tcx.has_attr(main_def_id, "rustc_error") { - tcx.sess.span_fatal(span, "compilation successful"); - } - } -} - -__build_diagnostic_array! { librustc_trans_utils, DIAGNOSTICS } diff --git a/src/librustc_trans_utils/link.rs b/src/librustc_trans_utils/link.rs deleted file mode 100644 index aabe931d79c..00000000000 --- a/src/librustc_trans_utils/link.rs +++ /dev/null @@ -1,191 +0,0 @@ -// Copyright 2017 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -use rustc::session::config::{self, OutputFilenames, Input, OutputType}; -use rustc::session::Session; -use rustc::middle::cstore::{self, LinkMeta}; -use rustc::hir::svh::Svh; -use std::path::{Path, PathBuf}; -use syntax::{ast, attr}; -use syntax_pos::Span; - -pub fn out_filename(sess: &Session, - crate_type: config::CrateType, - outputs: &OutputFilenames, - crate_name: &str) - -> PathBuf { - let default_filename = filename_for_input(sess, crate_type, crate_name, outputs); - let out_filename = outputs.outputs.get(&OutputType::Exe) - .and_then(|s| s.to_owned()) - .or_else(|| outputs.single_output_file.clone()) - .unwrap_or(default_filename); - - check_file_is_writeable(&out_filename, sess); - - out_filename -} - -// Make sure files are writeable. Mac, FreeBSD, and Windows system linkers -// check this already -- however, the Linux linker will happily overwrite a -// read-only file. We should be consistent. -pub fn check_file_is_writeable(file: &Path, sess: &Session) { - if !is_writeable(file) { - sess.fatal(&format!("output file {} is not writeable -- check its \ - permissions", file.display())); - } -} - -fn is_writeable(p: &Path) -> bool { - match p.metadata() { - Err(..) => true, - Ok(m) => !m.permissions().readonly() - } -} - -pub fn build_link_meta(crate_hash: Svh) -> LinkMeta { - let r = LinkMeta { - crate_hash, - }; - info!("{:?}", r); - return r; -} - -pub fn find_crate_name(sess: Option<&Session>, - attrs: &[ast::Attribute], - input: &Input) -> String { - let validate = |s: String, span: Option| { - cstore::validate_crate_name(sess, &s, span); - s - }; - - // Look in attributes 100% of the time to make sure the attribute is marked - // as used. After doing this, however, we still prioritize a crate name from - // the command line over one found in the #[crate_name] attribute. If we - // find both we ensure that they're the same later on as well. - let attr_crate_name = attr::find_by_name(attrs, "crate_name") - .and_then(|at| at.value_str().map(|s| (at, s))); - - if let Some(sess) = sess { - if let Some(ref s) = sess.opts.crate_name { - if let Some((attr, name)) = attr_crate_name { - if name != &**s { - let msg = format!("--crate-name and #[crate_name] are \ - required to match, but `{}` != `{}`", - s, name); - sess.span_err(attr.span, &msg); - } - } - return validate(s.clone(), None); - } - } - - if let Some((attr, s)) = attr_crate_name { - return validate(s.to_string(), Some(attr.span)); - } - if let Input::File(ref path) = *input { - if let Some(s) = path.file_stem().and_then(|s| s.to_str()) { - if s.starts_with("-") { - let msg = format!("crate names cannot start with a `-`, but \ - `{}` has a leading hyphen", s); - if let Some(sess) = sess { - sess.err(&msg); - } - } else { - return validate(s.replace("-", "_"), None); - } - } - } - - "rust_out".to_string() -} - -pub fn filename_for_input(sess: &Session, - crate_type: config::CrateType, - crate_name: &str, - outputs: &OutputFilenames) -> PathBuf { - let libname = format!("{}{}", crate_name, sess.opts.cg.extra_filename); - - match crate_type { - config::CrateTypeRlib => { - outputs.out_directory.join(&format!("lib{}.rlib", libname)) - } - config::CrateTypeCdylib | - config::CrateTypeProcMacro | - config::CrateTypeDylib => { - let (prefix, suffix) = (&sess.target.target.options.dll_prefix, - &sess.target.target.options.dll_suffix); - outputs.out_directory.join(&format!("{}{}{}", prefix, libname, - suffix)) - } - config::CrateTypeStaticlib => { - let (prefix, suffix) = (&sess.target.target.options.staticlib_prefix, - &sess.target.target.options.staticlib_suffix); - outputs.out_directory.join(&format!("{}{}{}", prefix, libname, - suffix)) - } - config::CrateTypeExecutable => { - let suffix = &sess.target.target.options.exe_suffix; - let out_filename = outputs.path(OutputType::Exe); - if suffix.is_empty() { - out_filename.to_path_buf() - } else { - out_filename.with_extension(&suffix[1..]) - } - } - } -} - -/// Returns default crate type for target -/// -/// Default crate type is used when crate type isn't provided neither -/// through cmd line arguments nor through crate attributes -/// -/// It is CrateTypeExecutable for all platforms but iOS as there is no -/// way to run iOS binaries anyway without jailbreaking and -/// interaction with Rust code through static library is the only -/// option for now -pub fn default_output_for_target(sess: &Session) -> config::CrateType { - if !sess.target.target.options.executables { - config::CrateTypeStaticlib - } else { - config::CrateTypeExecutable - } -} - -/// Checks if target supports crate_type as output -pub fn invalid_output_for_target(sess: &Session, - crate_type: config::CrateType) -> bool { - match crate_type { - config::CrateTypeCdylib | - config::CrateTypeDylib | - config::CrateTypeProcMacro => { - if !sess.target.target.options.dynamic_linking { - return true - } - if sess.crt_static() && !sess.target.target.options.crt_static_allows_dylibs { - return true - } - } - _ => {} - } - if sess.target.target.options.only_cdylib { - match crate_type { - config::CrateTypeProcMacro | config::CrateTypeDylib => return true, - _ => {} - } - } - if !sess.target.target.options.executables { - if crate_type == config::CrateTypeExecutable { - return true - } - } - - false -} diff --git a/src/librustc_trans_utils/symbol_names.rs b/src/librustc_trans_utils/symbol_names.rs deleted file mode 100644 index be5bff60805..00000000000 --- a/src/librustc_trans_utils/symbol_names.rs +++ /dev/null @@ -1,435 +0,0 @@ -// Copyright 2016 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! The Rust Linkage Model and Symbol Names -//! ======================================= -//! -//! The semantic model of Rust linkage is, broadly, that "there's no global -//! namespace" between crates. Our aim is to preserve the illusion of this -//! model despite the fact that it's not *quite* possible to implement on -//! modern linkers. We initially didn't use system linkers at all, but have -//! been convinced of their utility. -//! -//! There are a few issues to handle: -//! -//! - Linkers operate on a flat namespace, so we have to flatten names. -//! We do this using the C++ namespace-mangling technique. Foo::bar -//! symbols and such. -//! -//! - Symbols for distinct items with the same *name* need to get different -//! linkage-names. Examples of this are monomorphizations of functions or -//! items within anonymous scopes that end up having the same path. -//! -//! - Symbols in different crates but with same names "within" the crate need -//! to get different linkage-names. -//! -//! - Symbol names should be deterministic: Two consecutive runs of the -//! compiler over the same code base should produce the same symbol names for -//! the same items. -//! -//! - Symbol names should not depend on any global properties of the code base, -//! so that small modifications to the code base do not result in all symbols -//! changing. In previous versions of the compiler, symbol names incorporated -//! the SVH (Stable Version Hash) of the crate. This scheme turned out to be -//! infeasible when used in conjunction with incremental compilation because -//! small code changes would invalidate all symbols generated previously. -//! -//! - Even symbols from different versions of the same crate should be able to -//! live next to each other without conflict. -//! -//! In order to fulfill the above requirements the following scheme is used by -//! the compiler: -//! -//! The main tool for avoiding naming conflicts is the incorporation of a 64-bit -//! hash value into every exported symbol name. Anything that makes a difference -//! to the symbol being named, but does not show up in the regular path needs to -//! be fed into this hash: -//! -//! - Different monomorphizations of the same item have the same path but differ -//! in their concrete type parameters, so these parameters are part of the -//! data being digested for the symbol hash. -//! -//! - Rust allows items to be defined in anonymous scopes, such as in -//! `fn foo() { { fn bar() {} } { fn bar() {} } }`. Both `bar` functions have -//! the path `foo::bar`, since the anonymous scopes do not contribute to the -//! path of an item. The compiler already handles this case via so-called -//! disambiguating `DefPaths` which use indices to distinguish items with the -//! same name. The DefPaths of the functions above are thus `foo[0]::bar[0]` -//! and `foo[0]::bar[1]`. In order to incorporate this disambiguation -//! information into the symbol name too, these indices are fed into the -//! symbol hash, so that the above two symbols would end up with different -//! hash values. -//! -//! The two measures described above suffice to avoid intra-crate conflicts. In -//! order to also avoid inter-crate conflicts two more measures are taken: -//! -//! - The name of the crate containing the symbol is prepended to the symbol -//! name, i.e. symbols are "crate qualified". For example, a function `foo` in -//! module `bar` in crate `baz` would get a symbol name like -//! `baz::bar::foo::{hash}` instead of just `bar::foo::{hash}`. This avoids -//! simple conflicts between functions from different crates. -//! -//! - In order to be able to also use symbols from two versions of the same -//! crate (which naturally also have the same name), a stronger measure is -//! required: The compiler accepts an arbitrary "disambiguator" value via the -//! `-C metadata` commandline argument. This disambiguator is then fed into -//! the symbol hash of every exported item. Consequently, the symbols in two -//! identical crates but with different disambiguators are not in conflict -//! with each other. This facility is mainly intended to be used by build -//! tools like Cargo. -//! -//! A note on symbol name stability -//! ------------------------------- -//! Previous versions of the compiler resorted to feeding NodeIds into the -//! symbol hash in order to disambiguate between items with the same path. The -//! current version of the name generation algorithm takes great care not to do -//! that, since NodeIds are notoriously unstable: A small change to the -//! code base will offset all NodeIds after the change and thus, much as using -//! the SVH in the hash, invalidate an unbounded number of symbol names. This -//! makes re-using previously compiled code for incremental compilation -//! virtually impossible. Thus, symbol hash generation exclusively relies on -//! DefPaths which are much more robust in the face of changes to the code base. - -use rustc::middle::weak_lang_items; -use rustc_mir::monomorphize::Instance; -use rustc_mir::monomorphize::item::{MonoItem, MonoItemExt, InstantiationMode}; -use rustc::hir::def_id::{DefId, LOCAL_CRATE}; -use rustc::hir::map as hir_map; -use rustc::ty::{self, Ty, TyCtxt, TypeFoldable}; -use rustc::ty::fold::TypeVisitor; -use rustc::ty::item_path::{self, ItemPathBuffer, RootMode}; -use rustc::ty::maps::Providers; -use rustc::ty::subst::Substs; -use rustc::hir::map::definitions::DefPathData; -use rustc::util::common::record_time; - -use syntax::attr; -use syntax_pos::symbol::Symbol; - -use std::fmt::Write; - -pub fn provide(providers: &mut Providers) { - *providers = Providers { - def_symbol_name, - symbol_name, - - ..*providers - }; -} - -fn get_symbol_hash<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, - - // the DefId of the item this name is for - def_id: DefId, - - // instance this name will be for - instance: Instance<'tcx>, - - // type of the item, without any generic - // parameters substituted; this is - // included in the hash as a kind of - // safeguard. - item_type: Ty<'tcx>, - - // values for generic type parameters, - // if any. - substs: &'tcx Substs<'tcx>) - -> u64 { - debug!("get_symbol_hash(def_id={:?}, parameters={:?})", def_id, substs); - - let mut hasher = ty::util::TypeIdHasher::::new(tcx); - - record_time(&tcx.sess.perf_stats.symbol_hash_time, || { - // the main symbol name is not necessarily unique; hash in the - // compiler's internal def-path, guaranteeing each symbol has a - // truly unique path - hasher.hash(tcx.def_path_hash(def_id)); - - // Include the main item-type. Note that, in this case, the - // assertions about `needs_subst` may not hold, but this item-type - // ought to be the same for every reference anyway. - assert!(!item_type.has_erasable_regions()); - hasher.visit_ty(item_type); - - // If this is a function, we hash the signature as well. - // This is not *strictly* needed, but it may help in some - // situations, see the `run-make/a-b-a-linker-guard` test. - if let ty::TyFnDef(..) = item_type.sty { - item_type.fn_sig(tcx).visit_with(&mut hasher); - } - - // also include any type parameters (for generic items) - assert!(!substs.has_erasable_regions()); - assert!(!substs.needs_subst()); - substs.visit_with(&mut hasher); - - let is_generic = substs.types().next().is_some(); - let avoid_cross_crate_conflicts = - // If this is an instance of a generic function, we also hash in - // the ID of the instantiating crate. This avoids symbol conflicts - // in case the same instances is emitted in two crates of the same - // project. - is_generic || - - // If we're dealing with an instance of a function that's inlined from - // another crate but we're marking it as globally shared to our - // compliation (aka we're not making an internal copy in each of our - // codegen units) then this symbol may become an exported (but hidden - // visibility) symbol. This means that multiple crates may do the same - // and we want to be sure to avoid any symbol conflicts here. - match MonoItem::Fn(instance).instantiation_mode(tcx) { - InstantiationMode::GloballyShared { may_conflict: true } => true, - _ => false, - }; - - if avoid_cross_crate_conflicts { - let instantiating_crate = if is_generic { - if !def_id.is_local() && tcx.share_generics() { - // If we are re-using a monomorphization from another crate, - // we have to compute the symbol hash accordingly. - let upstream_monomorphizations = - tcx.upstream_monomorphizations_for(def_id); - - upstream_monomorphizations.and_then(|monos| monos.get(&substs) - .cloned()) - .unwrap_or(LOCAL_CRATE) - } else { - LOCAL_CRATE - } - } else { - LOCAL_CRATE - }; - - hasher.hash(&tcx.original_crate_name(instantiating_crate).as_str()[..]); - hasher.hash(&tcx.crate_disambiguator(instantiating_crate)); - } - }); - - // 64 bits should be enough to avoid collisions. - hasher.finish() -} - -fn def_symbol_name<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) - -> ty::SymbolName -{ - let mut buffer = SymbolPathBuffer::new(); - item_path::with_forced_absolute_paths(|| { - tcx.push_item_path(&mut buffer, def_id); - }); - buffer.into_interned() -} - -fn symbol_name<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, instance: Instance<'tcx>) - -> ty::SymbolName -{ - ty::SymbolName { name: Symbol::intern(&compute_symbol_name(tcx, instance)).as_interned_str() } -} - -fn compute_symbol_name<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, instance: Instance<'tcx>) - -> String -{ - let def_id = instance.def_id(); - let substs = instance.substs; - - debug!("symbol_name(def_id={:?}, substs={:?})", - def_id, substs); - - let node_id = tcx.hir.as_local_node_id(def_id); - - if let Some(id) = node_id { - if *tcx.sess.plugin_registrar_fn.get() == Some(id) { - let disambiguator = tcx.sess.local_crate_disambiguator(); - return tcx.sess.generate_plugin_registrar_symbol(disambiguator); - } - if *tcx.sess.derive_registrar_fn.get() == Some(id) { - let disambiguator = tcx.sess.local_crate_disambiguator(); - return tcx.sess.generate_derive_registrar_symbol(disambiguator); - } - } - - // FIXME(eddyb) Precompute a custom symbol name based on attributes. - let attrs = tcx.get_attrs(def_id); - let is_foreign = if let Some(id) = node_id { - match tcx.hir.get(id) { - hir_map::NodeForeignItem(_) => true, - _ => false - } - } else { - tcx.is_foreign_item(def_id) - }; - - if let Some(name) = weak_lang_items::link_name(&attrs) { - return name.to_string(); - } - - if is_foreign { - if let Some(name) = attr::first_attr_value_str_by_name(&attrs, "link_name") { - return name.to_string(); - } - // Don't mangle foreign items. - return tcx.item_name(def_id).to_string(); - } - - if let Some(name) = tcx.trans_fn_attrs(def_id).export_name { - // Use provided name - return name.to_string(); - } - - if attr::contains_name(&attrs, "no_mangle") { - // Don't mangle - return tcx.item_name(def_id).to_string(); - } - - // We want to compute the "type" of this item. Unfortunately, some - // kinds of items (e.g., closures) don't have an entry in the - // item-type array. So walk back up the find the closest parent - // that DOES have an entry. - let mut ty_def_id = def_id; - let instance_ty; - loop { - let key = tcx.def_key(ty_def_id); - match key.disambiguated_data.data { - DefPathData::TypeNs(_) | - DefPathData::ValueNs(_) => { - instance_ty = tcx.type_of(ty_def_id); - break; - } - _ => { - // if we're making a symbol for something, there ought - // to be a value or type-def or something in there - // *somewhere* - ty_def_id.index = key.parent.unwrap_or_else(|| { - bug!("finding type for {:?}, encountered def-id {:?} with no \ - parent", def_id, ty_def_id); - }); - } - } - } - - // Erase regions because they may not be deterministic when hashed - // and should not matter anyhow. - let instance_ty = tcx.erase_regions(&instance_ty); - - let hash = get_symbol_hash(tcx, def_id, instance, instance_ty, substs); - - SymbolPathBuffer::from_interned(tcx.def_symbol_name(def_id)).finish(hash) -} - -// Follow C++ namespace-mangling style, see -// http://en.wikipedia.org/wiki/Name_mangling for more info. -// -// It turns out that on macOS you can actually have arbitrary symbols in -// function names (at least when given to LLVM), but this is not possible -// when using unix's linker. Perhaps one day when we just use a linker from LLVM -// we won't need to do this name mangling. The problem with name mangling is -// that it seriously limits the available characters. For example we can't -// have things like &T in symbol names when one would theoretically -// want them for things like impls of traits on that type. -// -// To be able to work on all platforms and get *some* reasonable output, we -// use C++ name-mangling. -struct SymbolPathBuffer { - result: String, - temp_buf: String -} - -impl SymbolPathBuffer { - fn new() -> Self { - let mut result = SymbolPathBuffer { - result: String::with_capacity(64), - temp_buf: String::with_capacity(16) - }; - result.result.push_str("_ZN"); // _Z == Begin name-sequence, N == nested - result - } - - fn from_interned(symbol: ty::SymbolName) -> Self { - let mut result = SymbolPathBuffer { - result: String::with_capacity(64), - temp_buf: String::with_capacity(16) - }; - result.result.push_str(&symbol.name.as_str()); - result - } - - fn into_interned(self) -> ty::SymbolName { - ty::SymbolName { name: Symbol::intern(&self.result).as_interned_str() } - } - - fn finish(mut self, hash: u64) -> String { - // E = end name-sequence - let _ = write!(self.result, "17h{:016x}E", hash); - self.result - } -} - -impl ItemPathBuffer for SymbolPathBuffer { - fn root_mode(&self) -> &RootMode { - const ABSOLUTE: &'static RootMode = &RootMode::Absolute; - ABSOLUTE - } - - fn push(&mut self, text: &str) { - self.temp_buf.clear(); - let need_underscore = sanitize(&mut self.temp_buf, text); - let _ = write!(self.result, "{}", self.temp_buf.len() + (need_underscore as usize)); - if need_underscore { - self.result.push('_'); - } - self.result.push_str(&self.temp_buf); - } -} - -// Name sanitation. LLVM will happily accept identifiers with weird names, but -// gas doesn't! -// gas accepts the following characters in symbols: a-z, A-Z, 0-9, ., _, $ -// -// returns true if an underscore must be added at the start -pub fn sanitize(result: &mut String, s: &str) -> bool { - for c in s.chars() { - match c { - // Escape these with $ sequences - '@' => result.push_str("$SP$"), - '*' => result.push_str("$BP$"), - '&' => result.push_str("$RF$"), - '<' => result.push_str("$LT$"), - '>' => result.push_str("$GT$"), - '(' => result.push_str("$LP$"), - ')' => result.push_str("$RP$"), - ',' => result.push_str("$C$"), - - // '.' doesn't occur in types and functions, so reuse it - // for ':' and '-' - '-' | ':' => result.push('.'), - - // These are legal symbols - 'a' ... 'z' - | 'A' ... 'Z' - | '0' ... '9' - | '_' | '.' | '$' => result.push(c), - - _ => { - result.push('$'); - for c in c.escape_unicode().skip(1) { - match c { - '{' => {}, - '}' => result.push('$'), - c => result.push(c), - } - } - } - } - } - - // Underscore-qualify anything that didn't start as an ident. - !result.is_empty() && - result.as_bytes()[0] != '_' as u8 && - ! (result.as_bytes()[0] as char).is_xid_start() -} diff --git a/src/librustc_trans_utils/symbol_names_test.rs b/src/librustc_trans_utils/symbol_names_test.rs deleted file mode 100644 index 47bbd67fb5c..00000000000 --- a/src/librustc_trans_utils/symbol_names_test.rs +++ /dev/null @@ -1,79 +0,0 @@ -// Copyright 2012-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. - -//! Walks the crate looking for items/impl-items/trait-items that have -//! either a `rustc_symbol_name` or `rustc_item_path` attribute and -//! generates an error giving, respectively, the symbol name or -//! item-path. This is used for unit testing the code that generates -//! paths etc in all kinds of annoying scenarios. - -use rustc::hir; -use rustc::ty::TyCtxt; -use syntax::ast; - -use rustc_mir::monomorphize::Instance; - -const SYMBOL_NAME: &'static str = "rustc_symbol_name"; -const ITEM_PATH: &'static str = "rustc_item_path"; - -pub fn report_symbol_names<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>) { - // if the `rustc_attrs` feature is not enabled, then the - // attributes we are interested in cannot be present anyway, so - // skip the walk. - if !tcx.features().rustc_attrs { - return; - } - - tcx.dep_graph.with_ignore(|| { - let mut visitor = SymbolNamesTest { tcx: tcx }; - tcx.hir.krate().visit_all_item_likes(&mut visitor); - }) -} - -struct SymbolNamesTest<'a, 'tcx:'a> { - tcx: TyCtxt<'a, 'tcx, 'tcx>, -} - -impl<'a, 'tcx> SymbolNamesTest<'a, 'tcx> { - fn process_attrs(&mut self, - node_id: ast::NodeId) { - let tcx = self.tcx; - let def_id = tcx.hir.local_def_id(node_id); - for attr in tcx.get_attrs(def_id).iter() { - if attr.check_name(SYMBOL_NAME) { - // for now, can only use on monomorphic names - let instance = Instance::mono(tcx, def_id); - let name = self.tcx.symbol_name(instance); - tcx.sess.span_err(attr.span, &format!("symbol-name({})", name)); - } else if attr.check_name(ITEM_PATH) { - let path = tcx.item_path_str(def_id); - tcx.sess.span_err(attr.span, &format!("item-path({})", path)); - } - - // (*) The formatting of `tag({})` is chosen so that tests can elect - // to test the entirety of the string, if they choose, or else just - // some subset. - } - } -} - -impl<'a, 'tcx> hir::itemlikevisit::ItemLikeVisitor<'tcx> for SymbolNamesTest<'a, 'tcx> { - fn visit_item(&mut self, item: &'tcx hir::Item) { - self.process_attrs(item.id); - } - - fn visit_trait_item(&mut self, trait_item: &'tcx hir::TraitItem) { - self.process_attrs(trait_item.id); - } - - fn visit_impl_item(&mut self, impl_item: &'tcx hir::ImplItem) { - self.process_attrs(impl_item.id); - } -} diff --git a/src/librustc_trans_utils/trans_crate.rs b/src/librustc_trans_utils/trans_crate.rs deleted file mode 100644 index 7b585d1060f..00000000000 --- a/src/librustc_trans_utils/trans_crate.rs +++ /dev/null @@ -1,296 +0,0 @@ -// Copyright 2014-2015 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! The Rust compiler. -//! -//! # Note -//! -//! This API is completely unstable and subject to change. - -#![doc(html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png", - html_favicon_url = "https://doc.rust-lang.org/favicon.ico", - html_root_url = "https://doc.rust-lang.org/nightly/")] -#![deny(warnings)] - -#![feature(box_syntax)] - -use std::any::Any; -use std::io::prelude::*; -use std::io::{self, Cursor}; -use std::fs::File; -use std::path::Path; -use std::sync::mpsc; - -use rustc_data_structures::owning_ref::OwningRef; -use rustc_data_structures::sync::Lrc; -use ar::{Archive, Builder, Header}; -use flate2::Compression; -use flate2::write::DeflateEncoder; - -use syntax::symbol::Symbol; -use rustc::hir::def_id::LOCAL_CRATE; -use rustc::session::{Session, CompileIncomplete}; -use rustc::session::config::{CrateType, OutputFilenames, PrintRequest}; -use rustc::ty::TyCtxt; -use rustc::ty::maps::Providers; -use rustc::middle::cstore::EncodedMetadata; -use rustc::middle::cstore::MetadataLoader; -use rustc::dep_graph::DepGraph; -use rustc_target::spec::Target; -use rustc_data_structures::fx::FxHashMap; -use rustc_mir::monomorphize::collector; -use link::{build_link_meta, out_filename}; - -pub use rustc_data_structures::sync::MetadataRef; - -pub trait TransCrate { - fn init(&self, _sess: &Session) {} - fn print(&self, _req: PrintRequest, _sess: &Session) {} - fn target_features(&self, _sess: &Session) -> Vec { vec![] } - fn print_passes(&self) {} - fn print_version(&self) {} - fn diagnostics(&self) -> &[(&'static str, &'static str)] { &[] } - - fn metadata_loader(&self) -> Box; - fn provide(&self, _providers: &mut Providers); - fn provide_extern(&self, _providers: &mut Providers); - fn trans_crate<'a, 'tcx>( - &self, - tcx: TyCtxt<'a, 'tcx, 'tcx>, - rx: mpsc::Receiver> - ) -> Box; - - /// This is called on the returned `Box` from `trans_crate` - /// - /// # Panics - /// - /// Panics when the passed `Box` was not returned by `trans_crate`. - fn join_trans_and_link( - &self, - trans: Box, - sess: &Session, - dep_graph: &DepGraph, - outputs: &OutputFilenames, - ) -> Result<(), CompileIncomplete>; -} - -pub struct DummyTransCrate; - -impl TransCrate for DummyTransCrate { - fn metadata_loader(&self) -> Box { - box DummyMetadataLoader(()) - } - - fn provide(&self, _providers: &mut Providers) { - bug!("DummyTransCrate::provide"); - } - - fn provide_extern(&self, _providers: &mut Providers) { - bug!("DummyTransCrate::provide_extern"); - } - - fn trans_crate<'a, 'tcx>( - &self, - _tcx: TyCtxt<'a, 'tcx, 'tcx>, - _rx: mpsc::Receiver> - ) -> Box { - bug!("DummyTransCrate::trans_crate"); - } - - fn join_trans_and_link( - &self, - _trans: Box, - _sess: &Session, - _dep_graph: &DepGraph, - _outputs: &OutputFilenames, - ) -> Result<(), CompileIncomplete> { - bug!("DummyTransCrate::join_trans_and_link"); - } -} - -pub struct DummyMetadataLoader(()); - -impl MetadataLoader for DummyMetadataLoader { - fn get_rlib_metadata( - &self, - _target: &Target, - _filename: &Path - ) -> Result { - bug!("DummyMetadataLoader::get_rlib_metadata"); - } - - fn get_dylib_metadata( - &self, - _target: &Target, - _filename: &Path - ) -> Result { - bug!("DummyMetadataLoader::get_dylib_metadata"); - } -} - -pub struct NoLlvmMetadataLoader; - -impl MetadataLoader for NoLlvmMetadataLoader { - fn get_rlib_metadata(&self, _: &Target, filename: &Path) -> Result { - let file = File::open(filename) - .map_err(|e| format!("metadata file open err: {:?}", e))?; - let mut archive = Archive::new(file); - - while let Some(entry_result) = archive.next_entry() { - let mut entry = entry_result - .map_err(|e| format!("metadata section read err: {:?}", e))?; - if entry.header().identifier() == "rust.metadata.bin" { - let mut buf = Vec::new(); - io::copy(&mut entry, &mut buf).unwrap(); - let buf: OwningRef, [u8]> = OwningRef::new(buf).into(); - return Ok(rustc_erase_owner!(buf.map_owner_box())); - } - } - - Err("Couldn't find metadata section".to_string()) - } - - fn get_dylib_metadata( - &self, - _target: &Target, - _filename: &Path, - ) -> Result { - // FIXME: Support reading dylibs from llvm enabled rustc - self.get_rlib_metadata(_target, _filename) - } -} - -pub struct MetadataOnlyTransCrate(()); -pub struct OngoingCrateTranslation { - metadata: EncodedMetadata, - metadata_version: Vec, - crate_name: Symbol, -} - -impl MetadataOnlyTransCrate { - pub fn new() -> Box { - box MetadataOnlyTransCrate(()) - } -} - -impl TransCrate for MetadataOnlyTransCrate { - fn init(&self, sess: &Session) { - for cty in sess.opts.crate_types.iter() { - match *cty { - CrateType::CrateTypeRlib | CrateType::CrateTypeDylib | - CrateType::CrateTypeExecutable => {}, - _ => { - sess.parse_sess.span_diagnostic.warn( - &format!("LLVM unsupported, so output type {} is not supported", cty) - ); - }, - } - } - } - - fn metadata_loader(&self) -> Box { - box NoLlvmMetadataLoader - } - - fn provide(&self, providers: &mut Providers) { - ::symbol_names::provide(providers); - - providers.target_features_whitelist = |_tcx, _cnum| { - Lrc::new(FxHashMap()) // Just a dummy - }; - } - fn provide_extern(&self, _providers: &mut Providers) {} - - fn trans_crate<'a, 'tcx>( - &self, - tcx: TyCtxt<'a, 'tcx, 'tcx>, - _rx: mpsc::Receiver> - ) -> Box { - use rustc_mir::monomorphize::item::MonoItem; - - ::check_for_rustc_errors_attr(tcx); - ::symbol_names_test::report_symbol_names(tcx); - ::rustc_incremental::assert_dep_graph(tcx); - ::rustc_incremental::assert_module_sources::assert_module_sources(tcx); - ::rustc_mir::monomorphize::assert_symbols_are_distinct(tcx, - collector::collect_crate_mono_items( - tcx, - collector::MonoItemCollectionMode::Eager - ).0.iter() - ); - ::rustc::middle::dependency_format::calculate(tcx); - let _ = tcx.link_args(LOCAL_CRATE); - let _ = tcx.native_libraries(LOCAL_CRATE); - for trans_item in - collector::collect_crate_mono_items( - tcx, - collector::MonoItemCollectionMode::Eager - ).0 { - match trans_item { - MonoItem::Fn(inst) => { - let def_id = inst.def_id(); - if def_id.is_local() { - let _ = inst.def.is_inline(tcx); - let _ = tcx.trans_fn_attrs(def_id); - } - } - _ => {} - } - } - tcx.sess.abort_if_errors(); - - let link_meta = build_link_meta(tcx.crate_hash(LOCAL_CRATE)); - let metadata = tcx.encode_metadata(&link_meta); - - box OngoingCrateTranslation { - metadata: metadata, - metadata_version: tcx.metadata_encoding_version().to_vec(), - crate_name: tcx.crate_name(LOCAL_CRATE), - } - } - - fn join_trans_and_link( - &self, - trans: Box, - sess: &Session, - _dep_graph: &DepGraph, - outputs: &OutputFilenames, - ) -> Result<(), CompileIncomplete> { - let trans = trans.downcast::() - .expect("Expected MetadataOnlyTransCrate's OngoingCrateTranslation, found Box"); - for &crate_type in sess.opts.crate_types.iter() { - if crate_type != CrateType::CrateTypeRlib && crate_type != CrateType::CrateTypeDylib { - continue; - } - let output_name = - out_filename(sess, crate_type, &outputs, &trans.crate_name.as_str()); - let mut compressed = trans.metadata_version.clone(); - let metadata = if crate_type == CrateType::CrateTypeDylib { - DeflateEncoder::new(&mut compressed, Compression::fast()) - .write_all(&trans.metadata.raw_data) - .unwrap(); - &compressed - } else { - &trans.metadata.raw_data - }; - let mut builder = Builder::new(File::create(&output_name).unwrap()); - let header = Header::new("rust.metadata.bin".to_string(), metadata.len() as u64); - builder.append(&header, Cursor::new(metadata)).unwrap(); - } - - sess.abort_if_errors(); - if !sess.opts.crate_types.contains(&CrateType::CrateTypeRlib) - && !sess.opts.crate_types.contains(&CrateType::CrateTypeDylib) - { - sess.fatal("Executables are not supported by the metadata-only backend."); - } - Ok(()) - } -} diff --git a/src/librustc_typeck/check/intrinsic.rs b/src/librustc_typeck/check/intrinsic.rs index 97e4a35ea47..feb26e76162 100644 --- a/src/librustc_typeck/check/intrinsic.rs +++ b/src/librustc_typeck/check/intrinsic.rs @@ -72,7 +72,7 @@ fn equate_intrinsic_type<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, require_same_types(tcx, &cause, tcx.mk_fn_ptr(tcx.fn_sig(def_id)), fty); } -/// Remember to add all intrinsics here, in librustc_trans/trans/intrinsic.rs, +/// Remember to add all intrinsics here, in librustc_codegen_llvm/intrinsic.rs, /// and in libcore/intrinsics.rs pub fn check_intrinsic_type<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, it: &hir::ForeignItem) { diff --git a/src/librustc_typeck/check/op.rs b/src/librustc_typeck/check/op.rs index 23081b87d26..2dd22058d76 100644 --- a/src/librustc_typeck/check/op.rs +++ b/src/librustc_typeck/check/op.rs @@ -665,7 +665,7 @@ enum Op { /// `PartialEq` is not applicable. /// /// Reason #2 is the killer. I tried for a while to always use -/// overloaded logic and just check the types in constants/trans after +/// overloaded logic and just check the types in constants/codegen after /// the fact, and it worked fine, except for SIMD types. -nmatsakis fn is_builtin_binop(lhs: Ty, rhs: Ty, op: hir::BinOp) -> bool { match BinOpCategory::from(op) { diff --git a/src/librustc_typeck/coherence/builtin.rs b/src/librustc_typeck/coherence/builtin.rs index 2f08a54e10f..735ebbfcb3d 100644 --- a/src/librustc_typeck/coherence/builtin.rs +++ b/src/librustc_typeck/coherence/builtin.rs @@ -9,7 +9,7 @@ // except according to those terms. //! Check properties that are required by built-in traits and set -//! up data structures required by type-checking/translation. +//! up data structures required by type-checking/codegen. use rustc::infer::outlives::env::OutlivesEnvironment; use rustc::middle::region; diff --git a/src/librustc_typeck/collect.rs b/src/librustc_typeck/collect.rs index c2dde2d2e01..157f3ab76ca 100644 --- a/src/librustc_typeck/collect.rs +++ b/src/librustc_typeck/collect.rs @@ -48,7 +48,7 @@ use syntax::symbol::{Symbol, keywords}; use syntax::feature_gate; use syntax_pos::{Span, DUMMY_SP}; -use rustc::hir::{self, map as hir_map, TransFnAttrs, TransFnAttrFlags, Unsafety}; +use rustc::hir::{self, map as hir_map, CodegenFnAttrs, CodegenFnAttrFlags, Unsafety}; use rustc::hir::intravisit::{self, Visitor, NestedVisitorMap}; use rustc::hir::def::{Def, CtorKind}; use rustc::hir::def_id::{DefId, LOCAL_CRATE}; @@ -75,7 +75,7 @@ pub fn provide(providers: &mut Providers) { impl_trait_ref, impl_polarity, is_foreign_item, - trans_fn_attrs, + codegen_fn_attrs, ..*providers }; } @@ -1800,33 +1800,33 @@ fn linkage_by_name<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId, name: & } } -fn trans_fn_attrs<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, id: DefId) -> TransFnAttrs { +fn codegen_fn_attrs<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, id: DefId) -> CodegenFnAttrs { let attrs = tcx.get_attrs(id); - let mut trans_fn_attrs = TransFnAttrs::new(); + let mut codegen_fn_attrs = CodegenFnAttrs::new(); let whitelist = tcx.target_features_whitelist(LOCAL_CRATE); let mut inline_span = None; for attr in attrs.iter() { if attr.check_name("cold") { - trans_fn_attrs.flags |= TransFnAttrFlags::COLD; + codegen_fn_attrs.flags |= CodegenFnAttrFlags::COLD; } else if attr.check_name("allocator") { - trans_fn_attrs.flags |= TransFnAttrFlags::ALLOCATOR; + codegen_fn_attrs.flags |= CodegenFnAttrFlags::ALLOCATOR; } else if attr.check_name("unwind") { - trans_fn_attrs.flags |= TransFnAttrFlags::UNWIND; + codegen_fn_attrs.flags |= CodegenFnAttrFlags::UNWIND; } else if attr.check_name("rustc_allocator_nounwind") { - trans_fn_attrs.flags |= TransFnAttrFlags::RUSTC_ALLOCATOR_NOUNWIND; + codegen_fn_attrs.flags |= CodegenFnAttrFlags::RUSTC_ALLOCATOR_NOUNWIND; } else if attr.check_name("naked") { - trans_fn_attrs.flags |= TransFnAttrFlags::NAKED; + codegen_fn_attrs.flags |= CodegenFnAttrFlags::NAKED; } else if attr.check_name("no_mangle") { - trans_fn_attrs.flags |= TransFnAttrFlags::NO_MANGLE; + codegen_fn_attrs.flags |= CodegenFnAttrFlags::NO_MANGLE; } else if attr.check_name("rustc_std_internal_symbol") { - trans_fn_attrs.flags |= TransFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL; + codegen_fn_attrs.flags |= CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL; } else if attr.check_name("no_debug") { - trans_fn_attrs.flags |= TransFnAttrFlags::NO_DEBUG; + codegen_fn_attrs.flags |= CodegenFnAttrFlags::NO_DEBUG; } else if attr.check_name("inline") { - trans_fn_attrs.inline = attrs.iter().fold(InlineAttr::None, |ia, attr| { + codegen_fn_attrs.inline = attrs.iter().fold(InlineAttr::None, |ia, attr| { if attr.path != "inline" { return ia; } @@ -1862,7 +1862,7 @@ fn trans_fn_attrs<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, id: DefId) -> TransFnAt }); } else if attr.check_name("export_name") { if let s @ Some(_) = attr.value_str() { - trans_fn_attrs.export_name = s; + codegen_fn_attrs.export_name = s; } else { struct_span_err!(tcx.sess, attr.span, E0558, "export_name attribute has invalid format") @@ -1875,10 +1875,10 @@ fn trans_fn_attrs<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, id: DefId) -> TransFnAt `unsafe` function"; tcx.sess.span_err(attr.span, msg); } - from_target_feature(tcx, id, attr, &whitelist, &mut trans_fn_attrs.target_features); + from_target_feature(tcx, id, attr, &whitelist, &mut codegen_fn_attrs.target_features); } else if attr.check_name("linkage") { if let Some(val) = attr.value_str() { - trans_fn_attrs.linkage = Some(linkage_by_name(tcx, id, &val.as_str())); + codegen_fn_attrs.linkage = Some(linkage_by_name(tcx, id, &val.as_str())); } } } @@ -1887,8 +1887,8 @@ fn trans_fn_attrs<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, id: DefId) -> TransFnAt // purpose functions as they wouldn't have the right target features // enabled. For that reason we also forbid #[inline(always)] as it can't be // respected. - if trans_fn_attrs.target_features.len() > 0 { - if trans_fn_attrs.inline == InlineAttr::Always { + if codegen_fn_attrs.target_features.len() > 0 { + if codegen_fn_attrs.inline == InlineAttr::Always { if let Some(span) = inline_span { tcx.sess.span_err(span, "cannot use #[inline(always)] with \ #[target_feature]"); @@ -1896,5 +1896,5 @@ fn trans_fn_attrs<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, id: DefId) -> TransFnAt } } - trans_fn_attrs + codegen_fn_attrs } diff --git a/src/librustc_typeck/diagnostics.rs b/src/librustc_typeck/diagnostics.rs index e3f7ff5cb3f..99744d5b4b6 100644 --- a/src/librustc_typeck/diagnostics.rs +++ b/src/librustc_typeck/diagnostics.rs @@ -1179,7 +1179,7 @@ extern "rust-intrinsic" { ``` Please check you didn't make a mistake in the function's name. All intrinsic -functions are defined in librustc_trans/trans/intrinsic.rs and in +functions are defined in librustc_codegen_llvm/intrinsic.rs and in libcore/intrinsics.rs in the Rust source code. Example: ``` @@ -1209,7 +1209,7 @@ fn main() { ``` Please check you didn't make a mistake in the function's name. All intrinsic -functions are defined in librustc_trans/trans/intrinsic.rs and in +functions are defined in librustc_codegen_llvm/intrinsic.rs and in libcore/intrinsics.rs in the Rust source code. Example: ``` diff --git a/src/librustdoc/core.rs b/src/librustdoc/core.rs index c9a80d47791..90889890e0b 100644 --- a/src/librustdoc/core.rs +++ b/src/librustdoc/core.rs @@ -219,19 +219,19 @@ pub fn run_core(search_paths: SearchPaths, let mut sess = session::build_session_( sessopts, cpath, diagnostic_handler, codemap, ); - let trans = rustc_driver::get_trans(&sess); - let cstore = Rc::new(CStore::new(trans.metadata_loader())); + let codegen_backend = rustc_driver::get_codegen_backend(&sess); + let cstore = Rc::new(CStore::new(codegen_backend.metadata_loader())); rustc_lint::register_builtins(&mut sess.lint_store.borrow_mut(), Some(&sess)); let mut cfg = config::build_configuration(&sess, config::parse_cfgspecs(cfgs)); - target_features::add_configuration(&mut cfg, &sess, &*trans); + target_features::add_configuration(&mut cfg, &sess, &*codegen_backend); sess.parse_sess.config = cfg; let control = &driver::CompileController::basic(); let krate = panictry!(driver::phase_1_parse_input(control, &sess, &input)); - let name = ::rustc_trans_utils::link::find_crate_name(Some(&sess), &krate.attrs, &input); + let name = ::rustc_codegen_utils::link::find_crate_name(Some(&sess), &krate.attrs, &input); let mut crate_loader = CrateLoader::new(&sess, &cstore, &name); @@ -279,7 +279,7 @@ pub fn run_core(search_paths: SearchPaths, let resolver = RefCell::new(resolver); - abort_on_err(driver::phase_3_run_analysis_passes(&*trans, + abort_on_err(driver::phase_3_run_analysis_passes(&*codegen_backend, control, &sess, &*cstore, diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs index a14a27d5d61..3ce95c78a90 100644 --- a/src/librustdoc/lib.rs +++ b/src/librustdoc/lib.rs @@ -33,7 +33,7 @@ extern crate getopts; extern crate env_logger; extern crate rustc; extern crate rustc_data_structures; -extern crate rustc_trans_utils; +extern crate rustc_codegen_utils; extern crate rustc_driver; extern crate rustc_resolve; extern crate rustc_lint; diff --git a/src/librustdoc/test.rs b/src/librustdoc/test.rs index 7be7ce313fc..f507d0dc09d 100644 --- a/src/librustdoc/test.rs +++ b/src/librustdoc/test.rs @@ -95,12 +95,12 @@ pub fn run(input_path: &Path, let mut sess = session::build_session_( sessopts, Some(input_path.to_owned()), handler, codemap.clone(), ); - let trans = rustc_driver::get_trans(&sess); - let cstore = CStore::new(trans.metadata_loader()); + let codegen_backend = rustc_driver::get_codegen_backend(&sess); + let cstore = CStore::new(codegen_backend.metadata_loader()); rustc_lint::register_builtins(&mut sess.lint_store.borrow_mut(), Some(&sess)); let mut cfg = config::build_configuration(&sess, config::parse_cfgspecs(cfgs.clone())); - target_features::add_configuration(&mut cfg, &sess, &*trans); + target_features::add_configuration(&mut cfg, &sess, &*codegen_backend); sess.parse_sess.config = cfg; let krate = panictry!(driver::phase_1_parse_input(&driver::CompileController::basic(), @@ -120,7 +120,7 @@ pub fn run(input_path: &Path, }; let crate_name = crate_name.unwrap_or_else(|| { - ::rustc_trans_utils::link::find_crate_name(None, &hir_forest.krate().attrs, &input) + ::rustc_codegen_utils::link::find_crate_name(None, &hir_forest.krate().attrs, &input) }); let mut opts = scrape_test_config(hir_forest.krate()); opts.display_warnings |= display_warnings; @@ -273,8 +273,8 @@ fn run_test(test: &str, cratename: &str, filename: &FileName, line: usize, let mut sess = session::build_session_( sessopts, None, diagnostic_handler, codemap, ); - let trans = rustc_driver::get_trans(&sess); - let cstore = CStore::new(trans.metadata_loader()); + let codegen_backend = rustc_driver::get_codegen_backend(&sess); + let cstore = CStore::new(codegen_backend.metadata_loader()); rustc_lint::register_builtins(&mut sess.lint_store.borrow_mut(), Some(&sess)); let outdir = Mutex::new(TempDir::new("rustdoctest").ok().expect("rustdoc needs a tempdir")); @@ -282,7 +282,7 @@ fn run_test(test: &str, cratename: &str, filename: &FileName, line: usize, let mut control = driver::CompileController::basic(); let mut cfg = config::build_configuration(&sess, config::parse_cfgspecs(cfgs.clone())); - target_features::add_configuration(&mut cfg, &sess, &*trans); + target_features::add_configuration(&mut cfg, &sess, &*codegen_backend); sess.parse_sess.config = cfg; let out = Some(outdir.lock().unwrap().path().to_path_buf()); @@ -293,7 +293,7 @@ fn run_test(test: &str, cratename: &str, filename: &FileName, line: usize, let res = panic::catch_unwind(AssertUnwindSafe(|| { driver::compile_input( - trans, + codegen_backend, &sess, &cstore, &None, diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs index f1229520c77..d247b5402ef 100644 --- a/src/libsyntax/feature_gate.rs +++ b/src/libsyntax/feature_gate.rs @@ -839,7 +839,7 @@ pub const BUILTIN_ATTRIBUTES: &'static [(&'static str, AttributeType, AttributeG is just used for rustc unit tests \ and will never be stable", cfg_fn!(rustc_attrs))), - ("rustc_partition_translated", Whitelisted, Gated(Stability::Unstable, + ("rustc_partition_codegened", Whitelisted, Gated(Stability::Unstable, "rustc_attrs", "this attribute \ is just used for rustc unit tests \ @@ -938,7 +938,7 @@ pub const BUILTIN_ATTRIBUTES: &'static [(&'static str, AttributeType, AttributeG // FIXME: #14408 whitelist docs since rustdoc looks at them ("doc", Whitelisted, Ungated), - // FIXME: #14406 these are processed in trans, which happens after the + // FIXME: #14406 these are processed in codegen, which happens after the // lint pass ("cold", Whitelisted, Ungated), ("naked", Whitelisted, Gated(Stability::Unstable, diff --git a/src/libsyntax_ext/format.rs b/src/libsyntax_ext/format.rs index 6b155b6596d..f29cc75664d 100644 --- a/src/libsyntax_ext/format.rs +++ b/src/libsyntax_ext/format.rs @@ -79,7 +79,7 @@ struct Context<'a, 'b: 'a> { /// final generated static argument array. We record the starting indices /// corresponding to each positional argument, and number of references /// consumed so far for each argument, to facilitate correct `Position` - /// mapping in `trans_piece`. In effect this can be seen as a "flattened" + /// mapping in `build_piece`. In effect this can be seen as a "flattened" /// version of `arg_unique_types`. /// /// Again with the example described above in docstring for `args`: @@ -108,7 +108,7 @@ struct Context<'a, 'b: 'a> { /// Current position of the implicit positional arg pointer, as if it /// still existed in this phase of processing. - /// Used only for `all_pieces_simple` tracking in `trans_piece`. + /// Used only for `all_pieces_simple` tracking in `build_piece`. curarg: usize, /// Keep track of invalid references to positional arguments invalid_refs: Vec, @@ -373,7 +373,7 @@ impl<'a, 'b> Context<'a, 'b> { ecx.std_path(&["fmt", "rt", "v1", s]) } - fn trans_count(&self, c: parse::Count) -> P { + fn build_count(&self, c: parse::Count) -> P { let sp = self.macsp; let count = |c, arg| { let mut path = Context::rtpath(self.ecx, "Count"); @@ -401,17 +401,17 @@ impl<'a, 'b> Context<'a, 'b> { } } - /// Translate the accumulated string literals to a literal expression - fn trans_literal_string(&mut self) -> P { + /// Build a literal expression from the accumulated string literals + fn build_literal_string(&mut self) -> P { let sp = self.fmtsp; let s = Symbol::intern(&self.literal); self.literal.clear(); self.ecx.expr_str(sp, s) } - /// Translate a `parse::Piece` to a static `rt::Argument` or append + /// Build a static `rt::Argument` from a `parse::Piece` or append /// to the `literal` string. - fn trans_piece(&mut self, + fn build_piece(&mut self, piece: &parse::Piece, arg_index_consumed: &mut Vec) -> Option> { @@ -422,7 +422,7 @@ impl<'a, 'b> Context<'a, 'b> { None } parse::NextArgument(ref arg) => { - // Translate the position + // Build the position let pos = { let pos = |c, arg| { let mut path = Context::rtpath(self.ecx, "Position"); @@ -486,7 +486,7 @@ impl<'a, 'b> Context<'a, 'b> { self.all_pieces_simple = false; } - // Translate the format + // Build the format let fill = self.ecx.expr_lit(sp, ast::LitKind::Char(fill)); let align = |name| { let mut p = Context::rtpath(self.ecx, "Alignment"); @@ -501,8 +501,8 @@ impl<'a, 'b> Context<'a, 'b> { }; let align = self.ecx.expr_path(align); let flags = self.ecx.expr_u32(sp, arg.format.flags); - let prec = self.trans_count(arg.format.precision); - let width = self.trans_count(arg.format.width); + let prec = self.build_count(arg.format.precision); + let width = self.build_count(arg.format.width); let path = self.ecx.path_global(sp, Context::rtpath(self.ecx, "FormatSpec")); let fmt = self.ecx.expr_struct(sp, @@ -759,8 +759,8 @@ pub fn expand_preparsed_format_args(ecx: &mut ExtCtxt, let mut arg_index_consumed = vec![0usize; cx.arg_index_map.len()]; for piece in pieces { - if let Some(piece) = cx.trans_piece(&piece, &mut arg_index_consumed) { - let s = cx.trans_literal_string(); + if let Some(piece) = cx.build_piece(&piece, &mut arg_index_consumed) { + let s = cx.build_literal_string(); cx.str_pieces.push(s); cx.pieces.push(piece); } @@ -776,7 +776,7 @@ pub fn expand_preparsed_format_args(ecx: &mut ExtCtxt, return DummyResult::raw_expr(sp); } if !cx.literal.is_empty() { - let s = cx.trans_literal_string(); + let s = cx.build_literal_string(); cx.str_pieces.push(s); } diff --git a/src/libsyntax_ext/format_foreign.rs b/src/libsyntax_ext/format_foreign.rs index 2b8603c75a5..0dd8f0c55d8 100644 --- a/src/libsyntax_ext/format_foreign.rs +++ b/src/libsyntax_ext/format_foreign.rs @@ -697,7 +697,7 @@ pub mod printf { /// Check that the translations are what we expect. #[test] - fn test_trans() { + fn test_translation() { assert_eq_pnsat!("%c", Some("{}")); assert_eq_pnsat!("%d", Some("{}")); assert_eq_pnsat!("%u", Some("{}")); @@ -900,7 +900,7 @@ pub mod shell { } #[test] - fn test_trans() { + fn test_translation() { assert_eq_pnsat!("$0", Some("{0}")); assert_eq_pnsat!("$9", Some("{9}")); assert_eq_pnsat!("$1", Some("{1}")); diff --git a/src/libsyntax_ext/global_asm.rs b/src/libsyntax_ext/global_asm.rs index f01a0aacb0a..642aa0e5b12 100644 --- a/src/libsyntax_ext/global_asm.rs +++ b/src/libsyntax_ext/global_asm.rs @@ -14,7 +14,7 @@ /// "file-scoped", or "module-level" assembly. These synonyms /// all correspond to LLVM's module-level inline assembly instruction. /// -/// For example, `global_asm!("some assembly here")` translates to +/// For example, `global_asm!("some assembly here")` codegens to /// LLVM's `module asm "some assembly here"`. All of LLVM's caveats /// therefore apply. diff --git a/src/rustllvm/PassWrapper.cpp b/src/rustllvm/PassWrapper.cpp index 3b400b879eb..d5410feb254 100644 --- a/src/rustllvm/PassWrapper.cpp +++ b/src/rustllvm/PassWrapper.cpp @@ -388,7 +388,7 @@ extern "C" LLVMTargetMachineRef LLVMRustCreateTargetMachine( Options.FunctionSections = FunctionSections; if (TrapUnreachable) { - // Tell LLVM to translate `unreachable` into an explicit trap instruction. + // Tell LLVM to codegen `unreachable` into an explicit trap instruction. // This limits the extent of possible undefined behavior in some cases, as // it prevents control flow from "falling through" into whatever code // happens to be laid out next in memory. diff --git a/src/test/codegen-units/item-collection/cross-crate-closures.rs b/src/test/codegen-units/item-collection/cross-crate-closures.rs index 320be278198..a26604d3ce9 100644 --- a/src/test/codegen-units/item-collection/cross-crate-closures.rs +++ b/src/test/codegen-units/item-collection/cross-crate-closures.rs @@ -9,12 +9,12 @@ // except according to those terms. // In the current version of the collector that still has to support -// legacy-trans, closures do not generate their own TransItems, so we are -// ignoring this test until MIR trans has taken over completely +// legacy-codegen, closures do not generate their own MonoItems, so we are +// ignoring this test until MIR codegen has taken over completely // ignore-test // ignore-tidy-linelength -// compile-flags:-Zprint-trans-items=eager +// compile-flags:-Zprint-mono-items=eager #![deny(dead_code)] #![feature(start)] @@ -22,16 +22,16 @@ // aux-build:cgu_extern_closures.rs extern crate cgu_extern_closures; -//~ TRANS_ITEM fn cross_crate_closures::start[0] +//~ MONO_ITEM fn cross_crate_closures::start[0] #[start] fn start(_: isize, _: *const *const u8) -> isize { - //~ TRANS_ITEM fn cgu_extern_closures::inlined_fn[0] - //~ TRANS_ITEM fn cgu_extern_closures::inlined_fn[0]::{{closure}}[0] + //~ MONO_ITEM fn cgu_extern_closures::inlined_fn[0] + //~ MONO_ITEM fn cgu_extern_closures::inlined_fn[0]::{{closure}}[0] let _ = cgu_extern_closures::inlined_fn(1, 2); - //~ TRANS_ITEM fn cgu_extern_closures::inlined_fn_generic[0] - //~ TRANS_ITEM fn cgu_extern_closures::inlined_fn_generic[0]::{{closure}}[0] + //~ MONO_ITEM fn cgu_extern_closures::inlined_fn_generic[0] + //~ MONO_ITEM fn cgu_extern_closures::inlined_fn_generic[0]::{{closure}}[0] let _ = cgu_extern_closures::inlined_fn_generic(3, 4, 5i32); // Nothing should be generated for this call, we just link to the instance @@ -41,4 +41,4 @@ fn start(_: isize, _: *const *const u8) -> isize { 0 } -//~ TRANS_ITEM drop-glue i8 +//~ MONO_ITEM drop-glue i8 diff --git a/src/test/codegen-units/item-collection/cross-crate-generic-functions.rs b/src/test/codegen-units/item-collection/cross-crate-generic-functions.rs index bcb3b7b1dad..aa33c25da9a 100644 --- a/src/test/codegen-units/item-collection/cross-crate-generic-functions.rs +++ b/src/test/codegen-units/item-collection/cross-crate-generic-functions.rs @@ -9,7 +9,7 @@ // except according to those terms. // ignore-tidy-linelength -// compile-flags:-Zprint-trans-items=eager +// compile-flags:-Zprint-mono-items=eager #![deny(dead_code)] #![feature(start)] @@ -17,15 +17,15 @@ // aux-build:cgu_generic_function.rs extern crate cgu_generic_function; -//~ TRANS_ITEM fn cross_crate_generic_functions::start[0] +//~ MONO_ITEM fn cross_crate_generic_functions::start[0] #[start] fn start(_: isize, _: *const *const u8) -> isize { - //~ TRANS_ITEM fn cgu_generic_function::bar[0] - //~ TRANS_ITEM fn cgu_generic_function::foo[0] + //~ MONO_ITEM fn cgu_generic_function::bar[0] + //~ MONO_ITEM fn cgu_generic_function::foo[0] let _ = cgu_generic_function::foo(1u32); - //~ TRANS_ITEM fn cgu_generic_function::bar[0] - //~ TRANS_ITEM fn cgu_generic_function::foo[0] + //~ MONO_ITEM fn cgu_generic_function::bar[0] + //~ MONO_ITEM fn cgu_generic_function::foo[0] let _ = cgu_generic_function::foo(2u64); // This should not introduce a codegen item diff --git a/src/test/codegen-units/item-collection/cross-crate-trait-method.rs b/src/test/codegen-units/item-collection/cross-crate-trait-method.rs index 910ae000848..7a16b22a023 100644 --- a/src/test/codegen-units/item-collection/cross-crate-trait-method.rs +++ b/src/test/codegen-units/item-collection/cross-crate-trait-method.rs @@ -9,7 +9,7 @@ // except according to those terms. // ignore-tidy-linelength -// compile-flags:-Zprint-trans-items=eager +// compile-flags:-Zprint-mono-items=eager #![deny(dead_code)] #![feature(start)] @@ -19,7 +19,7 @@ extern crate cgu_export_trait_method; use cgu_export_trait_method::Trait; -//~ TRANS_ITEM fn cross_crate_trait_method::start[0] +//~ MONO_ITEM fn cross_crate_trait_method::start[0] #[start] fn start(_: isize, _: *const *const u8) -> isize { // The object code of these methods is contained in the external crate, so @@ -30,31 +30,31 @@ fn start(_: isize, _: *const *const u8) -> isize { // Currently, no object code is generated for trait methods with default // implemenations, unless they are actually called from somewhere. Therefore // we cannot import the implementations and have to create our own inline. - //~ TRANS_ITEM fn cgu_export_trait_method::Trait[0]::with_default_impl[0] + //~ MONO_ITEM fn cgu_export_trait_method::Trait[0]::with_default_impl[0] let _ = Trait::with_default_impl(0u32); - //~ TRANS_ITEM fn cgu_export_trait_method::Trait[0]::with_default_impl[0] + //~ MONO_ITEM fn cgu_export_trait_method::Trait[0]::with_default_impl[0] let _ = Trait::with_default_impl('c'); - //~ TRANS_ITEM fn cgu_export_trait_method::Trait[0]::with_default_impl_generic[0] + //~ MONO_ITEM fn cgu_export_trait_method::Trait[0]::with_default_impl_generic[0] let _ = Trait::with_default_impl_generic(0u32, "abc"); - //~ TRANS_ITEM fn cgu_export_trait_method::Trait[0]::with_default_impl_generic[0] + //~ MONO_ITEM fn cgu_export_trait_method::Trait[0]::with_default_impl_generic[0] let _ = Trait::with_default_impl_generic(0u32, false); - //~ TRANS_ITEM fn cgu_export_trait_method::Trait[0]::with_default_impl_generic[0] + //~ MONO_ITEM fn cgu_export_trait_method::Trait[0]::with_default_impl_generic[0] let _ = Trait::with_default_impl_generic('x', 1i16); - //~ TRANS_ITEM fn cgu_export_trait_method::Trait[0]::with_default_impl_generic[0] + //~ MONO_ITEM fn cgu_export_trait_method::Trait[0]::with_default_impl_generic[0] let _ = Trait::with_default_impl_generic('y', 0i32); - //~ TRANS_ITEM fn cgu_export_trait_method::{{impl}}[1]::without_default_impl_generic[0] + //~ MONO_ITEM fn cgu_export_trait_method::{{impl}}[1]::without_default_impl_generic[0] let _: (u32, char) = Trait::without_default_impl_generic('c'); - //~ TRANS_ITEM fn cgu_export_trait_method::{{impl}}[1]::without_default_impl_generic[0] + //~ MONO_ITEM fn cgu_export_trait_method::{{impl}}[1]::without_default_impl_generic[0] let _: (u32, bool) = Trait::without_default_impl_generic(false); - //~ TRANS_ITEM fn cgu_export_trait_method::{{impl}}[0]::without_default_impl_generic[0] + //~ MONO_ITEM fn cgu_export_trait_method::{{impl}}[0]::without_default_impl_generic[0] let _: (char, char) = Trait::without_default_impl_generic('c'); - //~ TRANS_ITEM fn cgu_export_trait_method::{{impl}}[0]::without_default_impl_generic[0] + //~ MONO_ITEM fn cgu_export_trait_method::{{impl}}[0]::without_default_impl_generic[0] let _: (char, bool) = Trait::without_default_impl_generic(false); 0 diff --git a/src/test/codegen-units/item-collection/drop_in_place_intrinsic.rs b/src/test/codegen-units/item-collection/drop_in_place_intrinsic.rs index 52af8165032..49e4b8d43c1 100644 --- a/src/test/codegen-units/item-collection/drop_in_place_intrinsic.rs +++ b/src/test/codegen-units/item-collection/drop_in_place_intrinsic.rs @@ -9,24 +9,24 @@ // except according to those terms. // ignore-tidy-linelength -// compile-flags:-Zprint-trans-items=eager +// compile-flags:-Zprint-mono-items=eager // compile-flags:-Zinline-in-all-cgus #![feature(start)] -//~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0] @@ drop_in_place_intrinsic0[Internal] +//~ MONO_ITEM fn core::ptr[0]::drop_in_place[0] @@ drop_in_place_intrinsic0[Internal] struct StructWithDtor(u32); impl Drop for StructWithDtor { - //~ TRANS_ITEM fn drop_in_place_intrinsic::{{impl}}[0]::drop[0] + //~ MONO_ITEM fn drop_in_place_intrinsic::{{impl}}[0]::drop[0] fn drop(&mut self) {} } -//~ TRANS_ITEM fn drop_in_place_intrinsic::start[0] +//~ MONO_ITEM fn drop_in_place_intrinsic::start[0] #[start] fn start(_: isize, _: *const *const u8) -> isize { - //~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0]<[drop_in_place_intrinsic::StructWithDtor[0]; 2]> @@ drop_in_place_intrinsic0[Internal] + //~ MONO_ITEM fn core::ptr[0]::drop_in_place[0]<[drop_in_place_intrinsic::StructWithDtor[0]; 2]> @@ drop_in_place_intrinsic0[Internal] let x = [StructWithDtor(0), StructWithDtor(1)]; drop_slice_in_place(&x); @@ -34,13 +34,13 @@ fn start(_: isize, _: *const *const u8) -> isize { 0 } -//~ TRANS_ITEM fn drop_in_place_intrinsic::drop_slice_in_place[0] +//~ MONO_ITEM fn drop_in_place_intrinsic::drop_slice_in_place[0] fn drop_slice_in_place(x: &[StructWithDtor]) { unsafe { // This is the interesting thing in this test case: Normally we would // not have drop-glue for the unsized [StructWithDtor]. This has to be // generated though when the drop_in_place() intrinsic is used. - //~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0]<[drop_in_place_intrinsic::StructWithDtor[0]]> @@ drop_in_place_intrinsic0[Internal] + //~ MONO_ITEM fn core::ptr[0]::drop_in_place[0]<[drop_in_place_intrinsic::StructWithDtor[0]]> @@ drop_in_place_intrinsic0[Internal] ::std::ptr::drop_in_place(x as *const _ as *mut [StructWithDtor]); } } diff --git a/src/test/codegen-units/item-collection/function-as-argument.rs b/src/test/codegen-units/item-collection/function-as-argument.rs index 65707c1aa4d..9a88336d1e5 100644 --- a/src/test/codegen-units/item-collection/function-as-argument.rs +++ b/src/test/codegen-units/item-collection/function-as-argument.rs @@ -9,7 +9,7 @@ // except according to those terms. // ignore-tidy-linelength -// compile-flags:-Zprint-trans-items=eager +// compile-flags:-Zprint-mono-items=eager #![deny(dead_code)] #![feature(start)] @@ -24,26 +24,26 @@ fn take_fn_pointer(f: fn(T1, T2), x: T1, y: T2) { (f)(x, y) } -//~ TRANS_ITEM fn function_as_argument::start[0] +//~ MONO_ITEM fn function_as_argument::start[0] #[start] fn start(_: isize, _: *const *const u8) -> isize { - //~ TRANS_ITEM fn function_as_argument::take_fn_once[0] - //~ TRANS_ITEM fn function_as_argument::function[0] - //~ TRANS_ITEM fn core::ops[0]::function[0]::FnOnce[0]::call_once[0] + //~ MONO_ITEM fn function_as_argument::take_fn_once[0] + //~ MONO_ITEM fn function_as_argument::function[0] + //~ MONO_ITEM fn core::ops[0]::function[0]::FnOnce[0]::call_once[0] take_fn_once(function, 0u32, "abc"); - //~ TRANS_ITEM fn function_as_argument::take_fn_once[0] - //~ TRANS_ITEM fn function_as_argument::function[0] - //~ TRANS_ITEM fn core::ops[0]::function[0]::FnOnce[0]::call_once[0] + //~ MONO_ITEM fn function_as_argument::take_fn_once[0] + //~ MONO_ITEM fn function_as_argument::function[0] + //~ MONO_ITEM fn core::ops[0]::function[0]::FnOnce[0]::call_once[0] take_fn_once(function, 'c', 0f64); - //~ TRANS_ITEM fn function_as_argument::take_fn_pointer[0] - //~ TRANS_ITEM fn function_as_argument::function[0] + //~ MONO_ITEM fn function_as_argument::take_fn_pointer[0] + //~ MONO_ITEM fn function_as_argument::function[0] take_fn_pointer(function, 0i32, ()); - //~ TRANS_ITEM fn function_as_argument::take_fn_pointer[0] - //~ TRANS_ITEM fn function_as_argument::function[0] + //~ MONO_ITEM fn function_as_argument::take_fn_pointer[0] + //~ MONO_ITEM fn function_as_argument::function[0] take_fn_pointer(function, 0f32, 0i64); 0 diff --git a/src/test/codegen-units/item-collection/generic-drop-glue.rs b/src/test/codegen-units/item-collection/generic-drop-glue.rs index d3d9aa3aefc..aad32d1eb7c 100644 --- a/src/test/codegen-units/item-collection/generic-drop-glue.rs +++ b/src/test/codegen-units/item-collection/generic-drop-glue.rs @@ -9,7 +9,7 @@ // except according to those terms. // ignore-tidy-linelength -// compile-flags:-Zprint-trans-items=eager +// compile-flags:-Zprint-mono-items=eager // compile-flags:-Zinline-in-all-cgus #![deny(dead_code)] @@ -47,22 +47,22 @@ enum EnumNoDrop { struct NonGenericNoDrop(i32); struct NonGenericWithDrop(i32); -//~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0] @@ generic_drop_glue0[Internal] +//~ MONO_ITEM fn core::ptr[0]::drop_in_place[0] @@ generic_drop_glue0[Internal] impl Drop for NonGenericWithDrop { - //~ TRANS_ITEM fn generic_drop_glue::{{impl}}[2]::drop[0] + //~ MONO_ITEM fn generic_drop_glue::{{impl}}[2]::drop[0] fn drop(&mut self) {} } -//~ TRANS_ITEM fn generic_drop_glue::start[0] +//~ MONO_ITEM fn generic_drop_glue::start[0] #[start] fn start(_: isize, _: *const *const u8) -> isize { - //~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0]> @@ generic_drop_glue0[Internal] - //~ TRANS_ITEM fn generic_drop_glue::{{impl}}[0]::drop[0] + //~ MONO_ITEM fn core::ptr[0]::drop_in_place[0]> @@ generic_drop_glue0[Internal] + //~ MONO_ITEM fn generic_drop_glue::{{impl}}[0]::drop[0] let _ = StructWithDrop { x: 0i8, y: 'a' }.x; - //~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0]> @@ generic_drop_glue0[Internal] - //~ TRANS_ITEM fn generic_drop_glue::{{impl}}[0]::drop[0]<&str, generic_drop_glue::NonGenericNoDrop[0]> + //~ MONO_ITEM fn core::ptr[0]::drop_in_place[0]> @@ generic_drop_glue0[Internal] + //~ MONO_ITEM fn generic_drop_glue::{{impl}}[0]::drop[0]<&str, generic_drop_glue::NonGenericNoDrop[0]> let _ = StructWithDrop { x: "&str", y: NonGenericNoDrop(0) }.y; // Should produce no drop glue @@ -70,18 +70,18 @@ fn start(_: isize, _: *const *const u8) -> isize { // This is supposed to generate drop-glue because it contains a field that // needs to be dropped. - //~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0]> @@ generic_drop_glue0[Internal] + //~ MONO_ITEM fn core::ptr[0]::drop_in_place[0]> @@ generic_drop_glue0[Internal] let _ = StructNoDrop { x: NonGenericWithDrop(0), y: 0f64 }.y; - //~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0]> @@ generic_drop_glue0[Internal] - //~ TRANS_ITEM fn generic_drop_glue::{{impl}}[1]::drop[0] + //~ MONO_ITEM fn core::ptr[0]::drop_in_place[0]> @@ generic_drop_glue0[Internal] + //~ MONO_ITEM fn generic_drop_glue::{{impl}}[1]::drop[0] let _ = match EnumWithDrop::A::(0) { EnumWithDrop::A(x) => x, EnumWithDrop::B(x) => x as i32 }; - //~TRANS_ITEM fn core::ptr[0]::drop_in_place[0]> @@ generic_drop_glue0[Internal] - //~ TRANS_ITEM fn generic_drop_glue::{{impl}}[1]::drop[0] + //~MONO_ITEM fn core::ptr[0]::drop_in_place[0]> @@ generic_drop_glue0[Internal] + //~ MONO_ITEM fn generic_drop_glue::{{impl}}[1]::drop[0] let _ = match EnumWithDrop::B::(1.0) { EnumWithDrop::A(x) => x, EnumWithDrop::B(x) => x as f64 diff --git a/src/test/codegen-units/item-collection/generic-functions.rs b/src/test/codegen-units/item-collection/generic-functions.rs index 8efe4b2762a..402d19f9996 100644 --- a/src/test/codegen-units/item-collection/generic-functions.rs +++ b/src/test/codegen-units/item-collection/generic-functions.rs @@ -9,7 +9,7 @@ // except according to those terms. // ignore-tidy-linelength -// compile-flags:-Zprint-trans-items=eager +// compile-flags:-Zprint-mono-items=eager #![deny(dead_code)] #![feature(start)] @@ -27,39 +27,39 @@ fn foo3(a: T1, b: T2, c: T3) -> (T1, T2, T3) { } // This function should be instantiated even if no used -//~ TRANS_ITEM fn generic_functions::lifetime_only[0] +//~ MONO_ITEM fn generic_functions::lifetime_only[0] pub fn lifetime_only<'a>(a: &'a u32) -> &'a u32 { a } -//~ TRANS_ITEM fn generic_functions::start[0] +//~ MONO_ITEM fn generic_functions::start[0] #[start] fn start(_: isize, _: *const *const u8) -> isize { - //~ TRANS_ITEM fn generic_functions::foo1[0] + //~ MONO_ITEM fn generic_functions::foo1[0] let _ = foo1(2i32); - //~ TRANS_ITEM fn generic_functions::foo1[0] + //~ MONO_ITEM fn generic_functions::foo1[0] let _ = foo1(2i64); - //~ TRANS_ITEM fn generic_functions::foo1[0]<&str> + //~ MONO_ITEM fn generic_functions::foo1[0]<&str> let _ = foo1("abc"); - //~ TRANS_ITEM fn generic_functions::foo1[0] + //~ MONO_ITEM fn generic_functions::foo1[0] let _ = foo1('v'); - //~ TRANS_ITEM fn generic_functions::foo2[0] + //~ MONO_ITEM fn generic_functions::foo2[0] let _ = foo2(2i32, 2i32); - //~ TRANS_ITEM fn generic_functions::foo2[0] + //~ MONO_ITEM fn generic_functions::foo2[0] let _ = foo2(2i64, "abc"); - //~ TRANS_ITEM fn generic_functions::foo2[0]<&str, usize> + //~ MONO_ITEM fn generic_functions::foo2[0]<&str, usize> let _ = foo2("a", 2usize); - //~ TRANS_ITEM fn generic_functions::foo2[0] + //~ MONO_ITEM fn generic_functions::foo2[0] let _ = foo2('v', ()); - //~ TRANS_ITEM fn generic_functions::foo3[0] + //~ MONO_ITEM fn generic_functions::foo3[0] let _ = foo3(2i32, 2i32, 2i32); - //~ TRANS_ITEM fn generic_functions::foo3[0] + //~ MONO_ITEM fn generic_functions::foo3[0] let _ = foo3(2i64, "abc", 'c'); - //~ TRANS_ITEM fn generic_functions::foo3[0] + //~ MONO_ITEM fn generic_functions::foo3[0] let _ = foo3(0i16, "a", 2usize); - //~ TRANS_ITEM fn generic_functions::foo3[0] + //~ MONO_ITEM fn generic_functions::foo3[0] let _ = foo3('v', (), ()); 0 diff --git a/src/test/codegen-units/item-collection/generic-impl.rs b/src/test/codegen-units/item-collection/generic-impl.rs index d1ee8ee624c..07e51a1e947 100644 --- a/src/test/codegen-units/item-collection/generic-impl.rs +++ b/src/test/codegen-units/item-collection/generic-impl.rs @@ -9,7 +9,7 @@ // except according to those terms. // ignore-tidy-linelength -// compile-flags:-Zprint-trans-items=eager +// compile-flags:-Zprint-mono-items=eager #![deny(dead_code)] #![feature(start)] @@ -41,41 +41,41 @@ pub struct LifeTimeOnly<'a> { impl<'a> LifeTimeOnly<'a> { - //~ TRANS_ITEM fn generic_impl::{{impl}}[1]::foo[0] + //~ MONO_ITEM fn generic_impl::{{impl}}[1]::foo[0] pub fn foo(&self) {} - //~ TRANS_ITEM fn generic_impl::{{impl}}[1]::bar[0] + //~ MONO_ITEM fn generic_impl::{{impl}}[1]::bar[0] pub fn bar(&'a self) {} - //~ TRANS_ITEM fn generic_impl::{{impl}}[1]::baz[0] + //~ MONO_ITEM fn generic_impl::{{impl}}[1]::baz[0] pub fn baz<'b>(&'b self) {} pub fn non_instantiated(&self) {} } -//~ TRANS_ITEM fn generic_impl::start[0] +//~ MONO_ITEM fn generic_impl::start[0] #[start] fn start(_: isize, _: *const *const u8) -> isize { - //~ TRANS_ITEM fn generic_impl::{{impl}}[0]::new[0] - //~ TRANS_ITEM fn generic_impl::id[0] - //~ TRANS_ITEM fn generic_impl::{{impl}}[0]::get[0] + //~ MONO_ITEM fn generic_impl::{{impl}}[0]::new[0] + //~ MONO_ITEM fn generic_impl::id[0] + //~ MONO_ITEM fn generic_impl::{{impl}}[0]::get[0] let _ = Struct::new(0i32).get(0i16); - //~ TRANS_ITEM fn generic_impl::{{impl}}[0]::new[0] - //~ TRANS_ITEM fn generic_impl::id[0] - //~ TRANS_ITEM fn generic_impl::{{impl}}[0]::get[0] + //~ MONO_ITEM fn generic_impl::{{impl}}[0]::new[0] + //~ MONO_ITEM fn generic_impl::id[0] + //~ MONO_ITEM fn generic_impl::{{impl}}[0]::get[0] let _ = Struct::new(0i64).get(0i16); - //~ TRANS_ITEM fn generic_impl::{{impl}}[0]::new[0] - //~ TRANS_ITEM fn generic_impl::id[0] - //~ TRANS_ITEM fn generic_impl::{{impl}}[0]::get[0] + //~ MONO_ITEM fn generic_impl::{{impl}}[0]::new[0] + //~ MONO_ITEM fn generic_impl::id[0] + //~ MONO_ITEM fn generic_impl::{{impl}}[0]::get[0] let _ = Struct::new('c').get(0i16); - //~ TRANS_ITEM fn generic_impl::{{impl}}[0]::new[0]<&str> - //~ TRANS_ITEM fn generic_impl::id[0]<&str> - //~ TRANS_ITEM fn generic_impl::{{impl}}[0]::get[0], i16> + //~ MONO_ITEM fn generic_impl::{{impl}}[0]::new[0]<&str> + //~ MONO_ITEM fn generic_impl::id[0]<&str> + //~ MONO_ITEM fn generic_impl::{{impl}}[0]::get[0], i16> let _ = Struct::new(Struct::new("str")).get(0i16); - //~ TRANS_ITEM fn generic_impl::{{impl}}[0]::new[0]> - //~ TRANS_ITEM fn generic_impl::id[0]> + //~ MONO_ITEM fn generic_impl::{{impl}}[0]::new[0]> + //~ MONO_ITEM fn generic_impl::id[0]> let _ = (Struct::new(Struct::new("str")).f)(Struct::new("str")); 0 diff --git a/src/test/codegen-units/item-collection/impl-in-non-instantiated-generic.rs b/src/test/codegen-units/item-collection/impl-in-non-instantiated-generic.rs index c07d26c3f8d..6d65c0d3aa8 100644 --- a/src/test/codegen-units/item-collection/impl-in-non-instantiated-generic.rs +++ b/src/test/codegen-units/item-collection/impl-in-non-instantiated-generic.rs @@ -9,7 +9,7 @@ // except according to those terms. // ignore-tidy-linelength -// compile-flags:-Zprint-trans-items=eager +// compile-flags:-Zprint-mono-items=eager #![deny(dead_code)] #![feature(start)] @@ -22,14 +22,14 @@ trait SomeTrait { // discovered. pub fn generic_function(x: T) -> (T, i32) { impl SomeTrait for i64 { - //~ TRANS_ITEM fn impl_in_non_instantiated_generic::generic_function[0]::{{impl}}[0]::foo[0] + //~ MONO_ITEM fn impl_in_non_instantiated_generic::generic_function[0]::{{impl}}[0]::foo[0] fn foo(&self) {} } (x, 0) } -//~ TRANS_ITEM fn impl_in_non_instantiated_generic::start[0] +//~ MONO_ITEM fn impl_in_non_instantiated_generic::start[0] #[start] fn start(_: isize, _: *const *const u8) -> isize { 0i64.foo(); diff --git a/src/test/codegen-units/item-collection/instantiation-through-vtable.rs b/src/test/codegen-units/item-collection/instantiation-through-vtable.rs index 2e1138ef128..5c6201da252 100644 --- a/src/test/codegen-units/item-collection/instantiation-through-vtable.rs +++ b/src/test/codegen-units/item-collection/instantiation-through-vtable.rs @@ -9,7 +9,7 @@ // except according to those terms. // ignore-tidy-linelength -// compile-flags:-Zprint-trans-items=eager +// compile-flags:-Zprint-mono-items=eager // compile-flags:-Zinline-in-all-cgus #![deny(dead_code)] @@ -29,20 +29,20 @@ impl Trait for Struct { fn bar(&self) {} } -//~ TRANS_ITEM fn instantiation_through_vtable::start[0] +//~ MONO_ITEM fn instantiation_through_vtable::start[0] #[start] fn start(_: isize, _: *const *const u8) -> isize { let s1 = Struct { _a: 0u32 }; - //~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0]> @@ instantiation_through_vtable0[Internal] - //~ TRANS_ITEM fn instantiation_through_vtable::{{impl}}[0]::foo[0] - //~ TRANS_ITEM fn instantiation_through_vtable::{{impl}}[0]::bar[0] + //~ MONO_ITEM fn core::ptr[0]::drop_in_place[0]> @@ instantiation_through_vtable0[Internal] + //~ MONO_ITEM fn instantiation_through_vtable::{{impl}}[0]::foo[0] + //~ MONO_ITEM fn instantiation_through_vtable::{{impl}}[0]::bar[0] let _ = &s1 as &Trait; let s1 = Struct { _a: 0u64 }; - //~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0]> @@ instantiation_through_vtable0[Internal] - //~ TRANS_ITEM fn instantiation_through_vtable::{{impl}}[0]::foo[0] - //~ TRANS_ITEM fn instantiation_through_vtable::{{impl}}[0]::bar[0] + //~ MONO_ITEM fn core::ptr[0]::drop_in_place[0]> @@ instantiation_through_vtable0[Internal] + //~ MONO_ITEM fn instantiation_through_vtable::{{impl}}[0]::foo[0] + //~ MONO_ITEM fn instantiation_through_vtable::{{impl}}[0]::bar[0] let _ = &s1 as &Trait; 0 diff --git a/src/test/codegen-units/item-collection/items-within-generic-items.rs b/src/test/codegen-units/item-collection/items-within-generic-items.rs index 04b54de3ce2..f9813063831 100644 --- a/src/test/codegen-units/item-collection/items-within-generic-items.rs +++ b/src/test/codegen-units/item-collection/items-within-generic-items.rs @@ -9,19 +9,19 @@ // except according to those terms. // ignore-tidy-linelength -// compile-flags:-Zprint-trans-items=eager +// compile-flags:-Zprint-mono-items=eager #![deny(dead_code)] #![feature(start)] fn generic_fn(a: T) -> (T, i32) { - //~ TRANS_ITEM fn items_within_generic_items::generic_fn[0]::nested_fn[0] + //~ MONO_ITEM fn items_within_generic_items::generic_fn[0]::nested_fn[0] fn nested_fn(a: i32) -> i32 { a + 1 } let x = { - //~ TRANS_ITEM fn items_within_generic_items::generic_fn[0]::nested_fn[1] + //~ MONO_ITEM fn items_within_generic_items::generic_fn[0]::nested_fn[1] fn nested_fn(a: i32) -> i32 { a + 2 } @@ -32,14 +32,14 @@ fn generic_fn(a: T) -> (T, i32) { return (a, x + nested_fn(0)); } -//~ TRANS_ITEM fn items_within_generic_items::start[0] +//~ MONO_ITEM fn items_within_generic_items::start[0] #[start] fn start(_: isize, _: *const *const u8) -> isize { - //~ TRANS_ITEM fn items_within_generic_items::generic_fn[0] + //~ MONO_ITEM fn items_within_generic_items::generic_fn[0] let _ = generic_fn(0i64); - //~ TRANS_ITEM fn items_within_generic_items::generic_fn[0] + //~ MONO_ITEM fn items_within_generic_items::generic_fn[0] let _ = generic_fn(0u16); - //~ TRANS_ITEM fn items_within_generic_items::generic_fn[0] + //~ MONO_ITEM fn items_within_generic_items::generic_fn[0] let _ = generic_fn(0i8); 0 diff --git a/src/test/codegen-units/item-collection/non-generic-closures.rs b/src/test/codegen-units/item-collection/non-generic-closures.rs index f0121d56cec..77ada23de71 100644 --- a/src/test/codegen-units/item-collection/non-generic-closures.rs +++ b/src/test/codegen-units/item-collection/non-generic-closures.rs @@ -9,51 +9,51 @@ // except according to those terms. // In the current version of the collector that still has to support -// legacy-trans, closures do not generate their own TransItems, so we are -// ignoring this test until MIR trans has taken over completely +// legacy-codegen, closures do not generate their own MonoItems, so we are +// ignoring this test until MIR codegen has taken over completely // ignore-test // ignore-tidy-linelength -// compile-flags:-Zprint-trans-items=eager +// compile-flags:-Zprint-mono-items=eager #![deny(dead_code)] #![feature(start)] -//~ TRANS_ITEM fn non_generic_closures::temporary[0] +//~ MONO_ITEM fn non_generic_closures::temporary[0] fn temporary() { - //~ TRANS_ITEM fn non_generic_closures::temporary[0]::{{closure}}[0] + //~ MONO_ITEM fn non_generic_closures::temporary[0]::{{closure}}[0] (|a: u32| { let _ = a; })(4); } -//~ TRANS_ITEM fn non_generic_closures::assigned_to_variable_but_not_executed[0] +//~ MONO_ITEM fn non_generic_closures::assigned_to_variable_but_not_executed[0] fn assigned_to_variable_but_not_executed() { - //~ TRANS_ITEM fn non_generic_closures::assigned_to_variable_but_not_executed[0]::{{closure}}[0] + //~ MONO_ITEM fn non_generic_closures::assigned_to_variable_but_not_executed[0]::{{closure}}[0] let _x = |a: i16| { let _ = a + 1; }; } -//~ TRANS_ITEM fn non_generic_closures::assigned_to_variable_executed_directly[0] +//~ MONO_ITEM fn non_generic_closures::assigned_to_variable_executed_directly[0] fn assigned_to_variable_executed_indirectly() { - //~ TRANS_ITEM fn non_generic_closures::assigned_to_variable_executed_directly[0]::{{closure}}[0] + //~ MONO_ITEM fn non_generic_closures::assigned_to_variable_executed_directly[0]::{{closure}}[0] let f = |a: i32| { let _ = a + 2; }; run_closure(&f); } -//~ TRANS_ITEM fn non_generic_closures::assigned_to_variable_executed_indirectly[0] +//~ MONO_ITEM fn non_generic_closures::assigned_to_variable_executed_indirectly[0] fn assigned_to_variable_executed_directly() { - //~ TRANS_ITEM fn non_generic_closures::assigned_to_variable_executed_indirectly[0]::{{closure}}[0] + //~ MONO_ITEM fn non_generic_closures::assigned_to_variable_executed_indirectly[0]::{{closure}}[0] let f = |a: i64| { let _ = a + 3; }; f(4); } -//~ TRANS_ITEM fn non_generic_closures::start[0] +//~ MONO_ITEM fn non_generic_closures::start[0] #[start] fn start(_: isize, _: *const *const u8) -> isize { temporary(); @@ -64,7 +64,7 @@ fn start(_: isize, _: *const *const u8) -> isize { 0 } -//~ TRANS_ITEM fn non_generic_closures::run_closure[0] +//~ MONO_ITEM fn non_generic_closures::run_closure[0] fn run_closure(f: &Fn(i32)) { f(3); } diff --git a/src/test/codegen-units/item-collection/non-generic-drop-glue.rs b/src/test/codegen-units/item-collection/non-generic-drop-glue.rs index bf084aa96ea..6ca24aa5b4b 100644 --- a/src/test/codegen-units/item-collection/non-generic-drop-glue.rs +++ b/src/test/codegen-units/item-collection/non-generic-drop-glue.rs @@ -9,19 +9,19 @@ // except according to those terms. // ignore-tidy-linelength -// compile-flags:-Zprint-trans-items=eager +// compile-flags:-Zprint-mono-items=eager // compile-flags:-Zinline-in-all-cgus #![deny(dead_code)] #![feature(start)] -//~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0] @@ non_generic_drop_glue0[Internal] +//~ MONO_ITEM fn core::ptr[0]::drop_in_place[0] @@ non_generic_drop_glue0[Internal] struct StructWithDrop { x: i32 } impl Drop for StructWithDrop { - //~ TRANS_ITEM fn non_generic_drop_glue::{{impl}}[0]::drop[0] + //~ MONO_ITEM fn non_generic_drop_glue::{{impl}}[0]::drop[0] fn drop(&mut self) {} } @@ -29,13 +29,13 @@ struct StructNoDrop { x: i32 } -//~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0] @@ non_generic_drop_glue0[Internal] +//~ MONO_ITEM fn core::ptr[0]::drop_in_place[0] @@ non_generic_drop_glue0[Internal] enum EnumWithDrop { A(i32) } impl Drop for EnumWithDrop { - //~ TRANS_ITEM fn non_generic_drop_glue::{{impl}}[1]::drop[0] + //~ MONO_ITEM fn non_generic_drop_glue::{{impl}}[1]::drop[0] fn drop(&mut self) {} } @@ -43,7 +43,7 @@ enum EnumNoDrop { A(i32) } -//~ TRANS_ITEM fn non_generic_drop_glue::start[0] +//~ MONO_ITEM fn non_generic_drop_glue::start[0] #[start] fn start(_: isize, _: *const *const u8) -> isize { let _ = StructWithDrop { x: 0 }.x; diff --git a/src/test/codegen-units/item-collection/non-generic-functions.rs b/src/test/codegen-units/item-collection/non-generic-functions.rs index 8c487db5c96..38d08c8a6ed 100644 --- a/src/test/codegen-units/item-collection/non-generic-functions.rs +++ b/src/test/codegen-units/item-collection/non-generic-functions.rs @@ -9,29 +9,29 @@ // except according to those terms. // ignore-tidy-linelength -// compile-flags:-Zprint-trans-items=eager +// compile-flags:-Zprint-mono-items=eager #![deny(dead_code)] #![feature(start)] -//~ TRANS_ITEM fn non_generic_functions::foo[0] +//~ MONO_ITEM fn non_generic_functions::foo[0] fn foo() { { - //~ TRANS_ITEM fn non_generic_functions::foo[0]::foo[0] + //~ MONO_ITEM fn non_generic_functions::foo[0]::foo[0] fn foo() {} foo(); } { - //~ TRANS_ITEM fn non_generic_functions::foo[0]::foo[1] + //~ MONO_ITEM fn non_generic_functions::foo[0]::foo[1] fn foo() {} foo(); } } -//~ TRANS_ITEM fn non_generic_functions::bar[0] +//~ MONO_ITEM fn non_generic_functions::bar[0] fn bar() { - //~ TRANS_ITEM fn non_generic_functions::bar[0]::baz[0] + //~ MONO_ITEM fn non_generic_functions::bar[0]::baz[0] fn baz() {} baz(); } @@ -39,38 +39,38 @@ fn bar() { struct Struct { _x: i32 } impl Struct { - //~ TRANS_ITEM fn non_generic_functions::{{impl}}[0]::foo[0] + //~ MONO_ITEM fn non_generic_functions::{{impl}}[0]::foo[0] fn foo() { { - //~ TRANS_ITEM fn non_generic_functions::{{impl}}[0]::foo[0]::foo[0] + //~ MONO_ITEM fn non_generic_functions::{{impl}}[0]::foo[0]::foo[0] fn foo() {} foo(); } { - //~ TRANS_ITEM fn non_generic_functions::{{impl}}[0]::foo[0]::foo[1] + //~ MONO_ITEM fn non_generic_functions::{{impl}}[0]::foo[0]::foo[1] fn foo() {} foo(); } } - //~ TRANS_ITEM fn non_generic_functions::{{impl}}[0]::bar[0] + //~ MONO_ITEM fn non_generic_functions::{{impl}}[0]::bar[0] fn bar(&self) { { - //~ TRANS_ITEM fn non_generic_functions::{{impl}}[0]::bar[0]::foo[0] + //~ MONO_ITEM fn non_generic_functions::{{impl}}[0]::bar[0]::foo[0] fn foo() {} foo(); } { - //~ TRANS_ITEM fn non_generic_functions::{{impl}}[0]::bar[0]::foo[1] + //~ MONO_ITEM fn non_generic_functions::{{impl}}[0]::bar[0]::foo[1] fn foo() {} foo(); } } } -//~ TRANS_ITEM fn non_generic_functions::start[0] +//~ MONO_ITEM fn non_generic_functions::start[0] #[start] fn start(_: isize, _: *const *const u8) -> isize { foo(); diff --git a/src/test/codegen-units/item-collection/overloaded-operators.rs b/src/test/codegen-units/item-collection/overloaded-operators.rs index 05848a727e9..dae6ef83551 100644 --- a/src/test/codegen-units/item-collection/overloaded-operators.rs +++ b/src/test/codegen-units/item-collection/overloaded-operators.rs @@ -9,7 +9,7 @@ // except according to those terms. // ignore-tidy-linelength -// compile-flags:-Zprint-trans-items=eager +// compile-flags:-Zprint-mono-items=eager #![deny(dead_code)] #![crate_type="lib"] @@ -23,7 +23,7 @@ pub struct Indexable { impl Index for Indexable { type Output = u8; - //~ TRANS_ITEM fn overloaded_operators::{{impl}}[0]::index[0] + //~ MONO_ITEM fn overloaded_operators::{{impl}}[0]::index[0] fn index(&self, index: usize) -> &Self::Output { if index >= 3 { &self.data[0] @@ -34,7 +34,7 @@ impl Index for Indexable { } impl IndexMut for Indexable { - //~ TRANS_ITEM fn overloaded_operators::{{impl}}[1]::index_mut[0] + //~ MONO_ITEM fn overloaded_operators::{{impl}}[1]::index_mut[0] fn index_mut(&mut self, index: usize) -> &mut Self::Output { if index >= 3 { &mut self.data[0] @@ -45,8 +45,8 @@ impl IndexMut for Indexable { } -//~ TRANS_ITEM fn overloaded_operators::{{impl}}[4]::eq[0] -//~ TRANS_ITEM fn overloaded_operators::{{impl}}[4]::ne[0] +//~ MONO_ITEM fn overloaded_operators::{{impl}}[4]::eq[0] +//~ MONO_ITEM fn overloaded_operators::{{impl}}[4]::ne[0] #[derive(PartialEq)] pub struct Equatable(u32); @@ -54,7 +54,7 @@ pub struct Equatable(u32); impl Add for Equatable { type Output = u32; - //~ TRANS_ITEM fn overloaded_operators::{{impl}}[2]::add[0] + //~ MONO_ITEM fn overloaded_operators::{{impl}}[2]::add[0] fn add(self, rhs: u32) -> u32 { self.0 + rhs } @@ -63,7 +63,7 @@ impl Add for Equatable { impl Deref for Equatable { type Target = u32; - //~ TRANS_ITEM fn overloaded_operators::{{impl}}[3]::deref[0] + //~ MONO_ITEM fn overloaded_operators::{{impl}}[3]::deref[0] fn deref(&self) -> &Self::Target { &self.0 } diff --git a/src/test/codegen-units/item-collection/static-init.rs b/src/test/codegen-units/item-collection/static-init.rs index 5ff7c3480b1..f36c4903458 100644 --- a/src/test/codegen-units/item-collection/static-init.rs +++ b/src/test/codegen-units/item-collection/static-init.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// compile-flags:-Zprint-trans-items=eager +// compile-flags:-Zprint-mono-items=eager // ignore-tidy-linelength #![feature(start)] @@ -17,12 +17,11 @@ pub static FN : fn() = foo::; pub fn foo() { } -//~ TRANS_ITEM fn static_init::foo[0] -//~ TRANS_ITEM static static_init::FN[0] +//~ MONO_ITEM fn static_init::foo[0] +//~ MONO_ITEM static static_init::FN[0] -//~ TRANS_ITEM fn static_init::start[0] +//~ MONO_ITEM fn static_init::start[0] #[start] fn start(_: isize, _: *const *const u8) -> isize { 0 } - diff --git a/src/test/codegen-units/item-collection/statics-and-consts.rs b/src/test/codegen-units/item-collection/statics-and-consts.rs index 11df1da3a78..883809ff059 100644 --- a/src/test/codegen-units/item-collection/statics-and-consts.rs +++ b/src/test/codegen-units/item-collection/statics-and-consts.rs @@ -9,7 +9,7 @@ // except according to those terms. // ignore-tidy-linelength -// compile-flags:-Zprint-trans-items=eager +// compile-flags:-Zprint-mono-items=eager #![deny(dead_code)] #![feature(start)] @@ -48,7 +48,7 @@ fn foo() { }; } -//~ TRANS_ITEM fn statics_and_consts::start[0] +//~ MONO_ITEM fn statics_and_consts::start[0] #[start] fn start(_: isize, _: *const *const u8) -> isize { foo(); @@ -57,9 +57,9 @@ fn start(_: isize, _: *const *const u8) -> isize { 0 } -//~ TRANS_ITEM static statics_and_consts::STATIC1[0] +//~ MONO_ITEM static statics_and_consts::STATIC1[0] -//~ TRANS_ITEM fn statics_and_consts::foo[0] -//~ TRANS_ITEM static statics_and_consts::foo[0]::STATIC2[0] -//~ TRANS_ITEM static statics_and_consts::foo[0]::STATIC2[1] -//~ TRANS_ITEM static statics_and_consts::foo[0]::STATIC2[2] +//~ MONO_ITEM fn statics_and_consts::foo[0] +//~ MONO_ITEM static statics_and_consts::foo[0]::STATIC2[0] +//~ MONO_ITEM static statics_and_consts::foo[0]::STATIC2[1] +//~ MONO_ITEM static statics_and_consts::foo[0]::STATIC2[2] diff --git a/src/test/codegen-units/item-collection/trait-implementations.rs b/src/test/codegen-units/item-collection/trait-implementations.rs index 8eb33dd647f..f85486b5d34 100644 --- a/src/test/codegen-units/item-collection/trait-implementations.rs +++ b/src/test/codegen-units/item-collection/trait-implementations.rs @@ -9,7 +9,7 @@ // except according to those terms. // ignore-tidy-linelength -// compile-flags:-Zprint-trans-items=eager +// compile-flags:-Zprint-mono-items=eager #![deny(dead_code)] #![feature(start)] @@ -21,7 +21,7 @@ pub trait SomeTrait { impl SomeTrait for i64 { - //~ TRANS_ITEM fn trait_implementations::{{impl}}[0]::foo[0] + //~ MONO_ITEM fn trait_implementations::{{impl}}[0]::foo[0] fn foo(&self) {} fn bar(&self, _: T) {} @@ -29,7 +29,7 @@ impl SomeTrait for i64 { impl SomeTrait for i32 { - //~ TRANS_ITEM fn trait_implementations::{{impl}}[1]::foo[0] + //~ MONO_ITEM fn trait_implementations::{{impl}}[1]::foo[0] fn foo(&self) {} fn bar(&self, _: T) {} @@ -43,7 +43,7 @@ pub trait SomeGenericTrait { // Concrete impl of generic trait impl SomeGenericTrait for f64 { - //~ TRANS_ITEM fn trait_implementations::{{impl}}[2]::foo[0] + //~ MONO_ITEM fn trait_implementations::{{impl}}[2]::foo[0] fn foo(&self, _: u32) {} fn bar(&self, _: u32, _: T2) {} @@ -56,28 +56,28 @@ impl SomeGenericTrait for f32 { fn bar(&self, _: T, _: T2) {} } -//~ TRANS_ITEM fn trait_implementations::start[0] +//~ MONO_ITEM fn trait_implementations::start[0] #[start] fn start(_: isize, _: *const *const u8) -> isize { - //~ TRANS_ITEM fn trait_implementations::{{impl}}[1]::bar[0] + //~ MONO_ITEM fn trait_implementations::{{impl}}[1]::bar[0] 0i32.bar('x'); - //~ TRANS_ITEM fn trait_implementations::{{impl}}[2]::bar[0]<&str> + //~ MONO_ITEM fn trait_implementations::{{impl}}[2]::bar[0]<&str> 0f64.bar(0u32, "&str"); - //~ TRANS_ITEM fn trait_implementations::{{impl}}[2]::bar[0]<()> + //~ MONO_ITEM fn trait_implementations::{{impl}}[2]::bar[0]<()> 0f64.bar(0u32, ()); - //~ TRANS_ITEM fn trait_implementations::{{impl}}[3]::foo[0] + //~ MONO_ITEM fn trait_implementations::{{impl}}[3]::foo[0] 0f32.foo('x'); - //~ TRANS_ITEM fn trait_implementations::{{impl}}[3]::foo[0] + //~ MONO_ITEM fn trait_implementations::{{impl}}[3]::foo[0] 0f32.foo(-1i64); - //~ TRANS_ITEM fn trait_implementations::{{impl}}[3]::bar[0] + //~ MONO_ITEM fn trait_implementations::{{impl}}[3]::bar[0] 0f32.bar(0u32, ()); - //~ TRANS_ITEM fn trait_implementations::{{impl}}[3]::bar[0]<&str, &str> + //~ MONO_ITEM fn trait_implementations::{{impl}}[3]::bar[0]<&str, &str> 0f32.bar("&str", "&str"); 0 diff --git a/src/test/codegen-units/item-collection/trait-method-as-argument.rs b/src/test/codegen-units/item-collection/trait-method-as-argument.rs index 10b21630308..1f08f0f8060 100644 --- a/src/test/codegen-units/item-collection/trait-method-as-argument.rs +++ b/src/test/codegen-units/item-collection/trait-method-as-argument.rs @@ -9,7 +9,7 @@ // except according to those terms. // ignore-tidy-linelength -// compile-flags:-Zprint-trans-items=eager +// compile-flags:-Zprint-mono-items=eager #![deny(dead_code)] #![feature(start)] @@ -37,33 +37,33 @@ fn take_foo_mut T>(mut f: F, arg: T) -> T { (f)(arg) } -//~ TRANS_ITEM fn trait_method_as_argument::start[0] +//~ MONO_ITEM fn trait_method_as_argument::start[0] #[start] fn start(_: isize, _: *const *const u8) -> isize { - //~ TRANS_ITEM fn trait_method_as_argument::take_foo_once[0] u32> - //~ TRANS_ITEM fn trait_method_as_argument::{{impl}}[0]::foo[0] - //~ TRANS_ITEM fn core::ops[0]::function[0]::FnOnce[0]::call_once[0] u32, (u32)> + //~ MONO_ITEM fn trait_method_as_argument::take_foo_once[0] u32> + //~ MONO_ITEM fn trait_method_as_argument::{{impl}}[0]::foo[0] + //~ MONO_ITEM fn core::ops[0]::function[0]::FnOnce[0]::call_once[0] u32, (u32)> take_foo_once(Trait::foo, 0u32); - //~ TRANS_ITEM fn trait_method_as_argument::take_foo_once[0] char> - //~ TRANS_ITEM fn trait_method_as_argument::Trait[0]::foo[0] - //~ TRANS_ITEM fn core::ops[0]::function[0]::FnOnce[0]::call_once[0] char, (char)> + //~ MONO_ITEM fn trait_method_as_argument::take_foo_once[0] char> + //~ MONO_ITEM fn trait_method_as_argument::Trait[0]::foo[0] + //~ MONO_ITEM fn core::ops[0]::function[0]::FnOnce[0]::call_once[0] char, (char)> take_foo_once(Trait::foo, 'c'); - //~ TRANS_ITEM fn trait_method_as_argument::take_foo[0] u32> - //~ TRANS_ITEM fn core::ops[0]::function[0]::Fn[0]::call[0] u32, (u32)> + //~ MONO_ITEM fn trait_method_as_argument::take_foo[0] u32> + //~ MONO_ITEM fn core::ops[0]::function[0]::Fn[0]::call[0] u32, (u32)> take_foo(Trait::foo, 0u32); - //~ TRANS_ITEM fn trait_method_as_argument::take_foo[0] char> - //~ TRANS_ITEM fn core::ops[0]::function[0]::Fn[0]::call[0] char, (char)> + //~ MONO_ITEM fn trait_method_as_argument::take_foo[0] char> + //~ MONO_ITEM fn core::ops[0]::function[0]::Fn[0]::call[0] char, (char)> take_foo(Trait::foo, 'c'); - //~ TRANS_ITEM fn trait_method_as_argument::take_foo_mut[0] u32> - //~ TRANS_ITEM fn core::ops[0]::function[0]::FnMut[0]::call_mut[0] char, (char)> + //~ MONO_ITEM fn trait_method_as_argument::take_foo_mut[0] u32> + //~ MONO_ITEM fn core::ops[0]::function[0]::FnMut[0]::call_mut[0] char, (char)> take_foo_mut(Trait::foo, 0u32); - //~ TRANS_ITEM fn trait_method_as_argument::take_foo_mut[0] char> - //~ TRANS_ITEM fn core::ops[0]::function[0]::FnMut[0]::call_mut[0] u32, (u32)> + //~ MONO_ITEM fn trait_method_as_argument::take_foo_mut[0] char> + //~ MONO_ITEM fn core::ops[0]::function[0]::FnMut[0]::call_mut[0] u32, (u32)> take_foo_mut(Trait::foo, 'c'); 0 diff --git a/src/test/codegen-units/item-collection/trait-method-default-impl.rs b/src/test/codegen-units/item-collection/trait-method-default-impl.rs index a6ae3765b2e..b130747972e 100644 --- a/src/test/codegen-units/item-collection/trait-method-default-impl.rs +++ b/src/test/codegen-units/item-collection/trait-method-default-impl.rs @@ -9,7 +9,7 @@ // except according to those terms. // ignore-tidy-linelength -// compile-flags:-Zprint-trans-items=eager +// compile-flags:-Zprint-mono-items=eager #![deny(dead_code)] #![feature(start)] @@ -24,7 +24,7 @@ impl SomeTrait for i8 { // For the non-generic foo(), we should generate a codegen-item even if it // is not called anywhere - //~ TRANS_ITEM fn trait_method_default_impl::SomeTrait[0]::foo[0] + //~ MONO_ITEM fn trait_method_default_impl::SomeTrait[0]::foo[0] } trait SomeGenericTrait { @@ -38,7 +38,7 @@ impl SomeGenericTrait for i32 { // For the non-generic foo(), we should generate a codegen-item even if it // is not called anywhere - //~ TRANS_ITEM fn trait_method_default_impl::SomeGenericTrait[0]::foo[0] + //~ MONO_ITEM fn trait_method_default_impl::SomeGenericTrait[0]::foo[0] } // Non-generic impl of generic trait @@ -47,25 +47,25 @@ impl SomeGenericTrait for u32 { // since nothing is monomorphic here, nothing should be generated unless used somewhere. } -//~ TRANS_ITEM fn trait_method_default_impl::start[0] +//~ MONO_ITEM fn trait_method_default_impl::start[0] #[start] fn start(_: isize, _: *const *const u8) -> isize { - //~ TRANS_ITEM fn trait_method_default_impl::SomeTrait[0]::bar[0] + //~ MONO_ITEM fn trait_method_default_impl::SomeTrait[0]::bar[0] let _ = 1i8.bar('c'); - //~ TRANS_ITEM fn trait_method_default_impl::SomeTrait[0]::bar[0] + //~ MONO_ITEM fn trait_method_default_impl::SomeTrait[0]::bar[0] let _ = 2i8.bar("&str"); - //~ TRANS_ITEM fn trait_method_default_impl::SomeGenericTrait[0]::bar[0] + //~ MONO_ITEM fn trait_method_default_impl::SomeGenericTrait[0]::bar[0] 0i32.bar(0u64, 'c'); - //~ TRANS_ITEM fn trait_method_default_impl::SomeGenericTrait[0]::bar[0] + //~ MONO_ITEM fn trait_method_default_impl::SomeGenericTrait[0]::bar[0] 0i32.bar(0u64, "&str"); - //~ TRANS_ITEM fn trait_method_default_impl::SomeGenericTrait[0]::bar[0] + //~ MONO_ITEM fn trait_method_default_impl::SomeGenericTrait[0]::bar[0] 0u32.bar(0i8, &['c']); - //~ TRANS_ITEM fn trait_method_default_impl::SomeGenericTrait[0]::bar[0] + //~ MONO_ITEM fn trait_method_default_impl::SomeGenericTrait[0]::bar[0] 0u32.bar(0i16, ()); 0 diff --git a/src/test/codegen-units/item-collection/transitive-drop-glue.rs b/src/test/codegen-units/item-collection/transitive-drop-glue.rs index 57cd10187a2..d20213c109b 100644 --- a/src/test/codegen-units/item-collection/transitive-drop-glue.rs +++ b/src/test/codegen-units/item-collection/transitive-drop-glue.rs @@ -9,21 +9,21 @@ // except according to those terms. // ignore-tidy-linelength -// compile-flags:-Zprint-trans-items=eager +// compile-flags:-Zprint-mono-items=eager // compile-flags:-Zinline-in-all-cgus #![deny(dead_code)] #![feature(start)] -//~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0] @@ transitive_drop_glue0[Internal] +//~ MONO_ITEM fn core::ptr[0]::drop_in_place[0] @@ transitive_drop_glue0[Internal] struct Root(Intermediate); -//~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0] @@ transitive_drop_glue0[Internal] +//~ MONO_ITEM fn core::ptr[0]::drop_in_place[0] @@ transitive_drop_glue0[Internal] struct Intermediate(Leaf); -//~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0] @@ transitive_drop_glue0[Internal] +//~ MONO_ITEM fn core::ptr[0]::drop_in_place[0] @@ transitive_drop_glue0[Internal] struct Leaf; impl Drop for Leaf { - //~ TRANS_ITEM fn transitive_drop_glue::{{impl}}[0]::drop[0] + //~ MONO_ITEM fn transitive_drop_glue::{{impl}}[0]::drop[0] fn drop(&mut self) {} } @@ -35,21 +35,21 @@ impl Drop for LeafGen { fn drop(&mut self) {} } -//~ TRANS_ITEM fn transitive_drop_glue::start[0] +//~ MONO_ITEM fn transitive_drop_glue::start[0] #[start] fn start(_: isize, _: *const *const u8) -> isize { let _ = Root(Intermediate(Leaf)); - //~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0]> @@ transitive_drop_glue0[Internal] - //~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0]> @@ transitive_drop_glue0[Internal] - //~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0]> @@ transitive_drop_glue0[Internal] - //~ TRANS_ITEM fn transitive_drop_glue::{{impl}}[1]::drop[0] + //~ MONO_ITEM fn core::ptr[0]::drop_in_place[0]> @@ transitive_drop_glue0[Internal] + //~ MONO_ITEM fn core::ptr[0]::drop_in_place[0]> @@ transitive_drop_glue0[Internal] + //~ MONO_ITEM fn core::ptr[0]::drop_in_place[0]> @@ transitive_drop_glue0[Internal] + //~ MONO_ITEM fn transitive_drop_glue::{{impl}}[1]::drop[0] let _ = RootGen(IntermediateGen(LeafGen(0u32))); - //~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0]> @@ transitive_drop_glue0[Internal] - //~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0]> @@ transitive_drop_glue0[Internal] - //~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0]> @@ transitive_drop_glue0[Internal] - //~ TRANS_ITEM fn transitive_drop_glue::{{impl}}[1]::drop[0] + //~ MONO_ITEM fn core::ptr[0]::drop_in_place[0]> @@ transitive_drop_glue0[Internal] + //~ MONO_ITEM fn core::ptr[0]::drop_in_place[0]> @@ transitive_drop_glue0[Internal] + //~ MONO_ITEM fn core::ptr[0]::drop_in_place[0]> @@ transitive_drop_glue0[Internal] + //~ MONO_ITEM fn transitive_drop_glue::{{impl}}[1]::drop[0] let _ = RootGen(IntermediateGen(LeafGen(0i16))); 0 diff --git a/src/test/codegen-units/item-collection/tuple-drop-glue.rs b/src/test/codegen-units/item-collection/tuple-drop-glue.rs index a5f2409b8ae..9e4cc6ea6f0 100644 --- a/src/test/codegen-units/item-collection/tuple-drop-glue.rs +++ b/src/test/codegen-units/item-collection/tuple-drop-glue.rs @@ -9,28 +9,28 @@ // except according to those terms. // ignore-tidy-linelength -// compile-flags:-Zprint-trans-items=eager +// compile-flags:-Zprint-mono-items=eager // compile-flags:-Zinline-in-all-cgus #![deny(dead_code)] #![feature(start)] -//~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0] @@ tuple_drop_glue0[Internal] +//~ MONO_ITEM fn core::ptr[0]::drop_in_place[0] @@ tuple_drop_glue0[Internal] struct Dropped; impl Drop for Dropped { - //~ TRANS_ITEM fn tuple_drop_glue::{{impl}}[0]::drop[0] + //~ MONO_ITEM fn tuple_drop_glue::{{impl}}[0]::drop[0] fn drop(&mut self) {} } -//~ TRANS_ITEM fn tuple_drop_glue::start[0] +//~ MONO_ITEM fn tuple_drop_glue::start[0] #[start] fn start(_: isize, _: *const *const u8) -> isize { - //~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0]<(u32, tuple_drop_glue::Dropped[0])> @@ tuple_drop_glue0[Internal] + //~ MONO_ITEM fn core::ptr[0]::drop_in_place[0]<(u32, tuple_drop_glue::Dropped[0])> @@ tuple_drop_glue0[Internal] let x = (0u32, Dropped); - //~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0]<(i16, (tuple_drop_glue::Dropped[0], bool))> @@ tuple_drop_glue0[Internal] - //~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0]<(tuple_drop_glue::Dropped[0], bool)> @@ tuple_drop_glue0[Internal] + //~ MONO_ITEM fn core::ptr[0]::drop_in_place[0]<(i16, (tuple_drop_glue::Dropped[0], bool))> @@ tuple_drop_glue0[Internal] + //~ MONO_ITEM fn core::ptr[0]::drop_in_place[0]<(tuple_drop_glue::Dropped[0], bool)> @@ tuple_drop_glue0[Internal] let x = (0i16, (Dropped, true)); 0 diff --git a/src/test/codegen-units/item-collection/unreferenced-const-fn.rs b/src/test/codegen-units/item-collection/unreferenced-const-fn.rs index 59b25d8beca..c2ff846721c 100644 --- a/src/test/codegen-units/item-collection/unreferenced-const-fn.rs +++ b/src/test/codegen-units/item-collection/unreferenced-const-fn.rs @@ -9,9 +9,9 @@ // except according to those terms. // ignore-tidy-linelength -// compile-flags:-Zprint-trans-items=lazy +// compile-flags:-Zprint-mono-items=lazy -// NB: We do not expect *any* translation item to be generated here. +// NB: We do not expect *any* monomorphization to be generated here. #![feature(const_fn)] #![deny(dead_code)] diff --git a/src/test/codegen-units/item-collection/unreferenced-inline-function.rs b/src/test/codegen-units/item-collection/unreferenced-inline-function.rs index 75d41a38012..829b4fbf3c9 100644 --- a/src/test/codegen-units/item-collection/unreferenced-inline-function.rs +++ b/src/test/codegen-units/item-collection/unreferenced-inline-function.rs @@ -9,9 +9,9 @@ // except according to those terms. // ignore-tidy-linelength -// compile-flags:-Zprint-trans-items=lazy +// compile-flags:-Zprint-mono-items=lazy -// NB: We do not expect *any* translation item to be generated here. +// NB: We do not expect *any* monomorphization to be generated here. #![deny(dead_code)] #![crate_type = "rlib"] @@ -20,4 +20,3 @@ pub fn foo() -> bool { [1, 2] == [3, 4] } - diff --git a/src/test/codegen-units/item-collection/unsizing.rs b/src/test/codegen-units/item-collection/unsizing.rs index 87d2581e1f8..adc0eb6c709 100644 --- a/src/test/codegen-units/item-collection/unsizing.rs +++ b/src/test/codegen-units/item-collection/unsizing.rs @@ -9,7 +9,7 @@ // except according to those terms. // ignore-tidy-linelength -// compile-flags:-Zprint-trans-items=eager +// compile-flags:-Zprint-mono-items=eager // compile-flags:-Zinline-in-all-cgus #![deny(dead_code)] @@ -54,19 +54,19 @@ struct Wrapper(*const T); impl, U: ?Sized> CoerceUnsized> for Wrapper {} -//~ TRANS_ITEM fn unsizing::start[0] +//~ MONO_ITEM fn unsizing::start[0] #[start] fn start(_: isize, _: *const *const u8) -> isize { // simple case let bool_sized = &true; - //~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0] @@ unsizing0[Internal] - //~ TRANS_ITEM fn unsizing::{{impl}}[0]::foo[0] + //~ MONO_ITEM fn core::ptr[0]::drop_in_place[0] @@ unsizing0[Internal] + //~ MONO_ITEM fn unsizing::{{impl}}[0]::foo[0] let _bool_unsized = bool_sized as &Trait; let char_sized = &'a'; - //~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0] @@ unsizing0[Internal] - //~ TRANS_ITEM fn unsizing::{{impl}}[1]::foo[0] + //~ MONO_ITEM fn core::ptr[0]::drop_in_place[0] @@ unsizing0[Internal] + //~ MONO_ITEM fn unsizing::{{impl}}[1]::foo[0] let _char_unsized = char_sized as &Trait; // struct field @@ -75,14 +75,14 @@ fn start(_: isize, _: *const *const u8) -> isize { _b: 2, _c: 3.0f64 }; - //~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0] @@ unsizing0[Internal] - //~ TRANS_ITEM fn unsizing::{{impl}}[2]::foo[0] + //~ MONO_ITEM fn core::ptr[0]::drop_in_place[0] @@ unsizing0[Internal] + //~ MONO_ITEM fn unsizing::{{impl}}[2]::foo[0] let _struct_unsized = struct_sized as &Struct; // custom coercion let wrapper_sized = Wrapper(&0u32); - //~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0] @@ unsizing0[Internal] - //~ TRANS_ITEM fn unsizing::{{impl}}[3]::foo[0] + //~ MONO_ITEM fn core::ptr[0]::drop_in_place[0] @@ unsizing0[Internal] + //~ MONO_ITEM fn unsizing::{{impl}}[3]::foo[0] let _wrapper_sized = wrapper_sized as Wrapper; 0 diff --git a/src/test/codegen-units/item-collection/unused-traits-and-generics.rs b/src/test/codegen-units/item-collection/unused-traits-and-generics.rs index ce85c4fc13c..2edc7b211a1 100644 --- a/src/test/codegen-units/item-collection/unused-traits-and-generics.rs +++ b/src/test/codegen-units/item-collection/unused-traits-and-generics.rs @@ -9,7 +9,7 @@ // except according to those terms. // ignore-tidy-linelength -// compile-flags:-Zprint-trans-items=eager +// compile-flags:-Zprint-mono-items=eager #![crate_type="lib"] #![deny(dead_code)] @@ -85,4 +85,4 @@ impl NonGeneric { } // Only the non-generic methods should be instantiated: -//~ TRANS_ITEM fn unused_traits_and_generics::{{impl}}[3]::foo[0] +//~ MONO_ITEM fn unused_traits_and_generics::{{impl}}[3]::foo[0] diff --git a/src/test/codegen-units/partitioning/extern-drop-glue.rs b/src/test/codegen-units/partitioning/extern-drop-glue.rs index da96c5e183d..cbad3a63884 100644 --- a/src/test/codegen-units/partitioning/extern-drop-glue.rs +++ b/src/test/codegen-units/partitioning/extern-drop-glue.rs @@ -12,7 +12,7 @@ // We specify -Z incremental here because we want to test the partitioning for // incremental compilation -// compile-flags:-Zprint-trans-items=lazy -Zincremental=tmp/partitioning-tests/extern-drop-glue +// compile-flags:-Zprint-mono-items=lazy -Zincremental=tmp/partitioning-tests/extern-drop-glue // compile-flags:-Zinline-in-all-cgus #![allow(dead_code)] @@ -21,14 +21,14 @@ // aux-build:cgu_extern_drop_glue.rs extern crate cgu_extern_drop_glue; -//~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0] @@ extern_drop_glue[Internal] extern_drop_glue-mod1[Internal] +//~ MONO_ITEM fn core::ptr[0]::drop_in_place[0] @@ extern_drop_glue[Internal] extern_drop_glue-mod1[Internal] struct LocalStruct(cgu_extern_drop_glue::Struct); -//~ TRANS_ITEM fn extern_drop_glue::user[0] @@ extern_drop_glue[External] +//~ MONO_ITEM fn extern_drop_glue::user[0] @@ extern_drop_glue[External] pub fn user() { - //~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0] @@ extern_drop_glue[Internal] + //~ MONO_ITEM fn core::ptr[0]::drop_in_place[0] @@ extern_drop_glue[Internal] let _ = LocalStruct(cgu_extern_drop_glue::Struct(0)); } @@ -37,10 +37,10 @@ pub mod mod1 { struct LocalStruct(cgu_extern_drop_glue::Struct); - //~ TRANS_ITEM fn extern_drop_glue::mod1[0]::user[0] @@ extern_drop_glue-mod1[External] + //~ MONO_ITEM fn extern_drop_glue::mod1[0]::user[0] @@ extern_drop_glue-mod1[External] pub fn user() { - //~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0] @@ extern_drop_glue-mod1[Internal] + //~ MONO_ITEM fn core::ptr[0]::drop_in_place[0] @@ extern_drop_glue-mod1[Internal] let _ = LocalStruct(cgu_extern_drop_glue::Struct(0)); } } diff --git a/src/test/codegen-units/partitioning/extern-generic.rs b/src/test/codegen-units/partitioning/extern-generic.rs index 140b43c85d5..a774376690a 100644 --- a/src/test/codegen-units/partitioning/extern-generic.rs +++ b/src/test/codegen-units/partitioning/extern-generic.rs @@ -11,7 +11,7 @@ // ignore-tidy-linelength // We specify -Z incremental here because we want to test the partitioning for // incremental compilation -// compile-flags:-Zprint-trans-items=eager -Zincremental=tmp/partitioning-tests/extern-generic -Zshare-generics=y +// compile-flags:-Zprint-mono-items=eager -Zincremental=tmp/partitioning-tests/extern-generic -Zshare-generics=y #![allow(dead_code)] #![crate_type="lib"] @@ -19,7 +19,7 @@ // aux-build:cgu_generic_function.rs extern crate cgu_generic_function; -//~ TRANS_ITEM fn extern_generic::user[0] @@ extern_generic[Internal] +//~ MONO_ITEM fn extern_generic::user[0] @@ extern_generic[Internal] fn user() { let _ = cgu_generic_function::foo("abc"); } @@ -27,7 +27,7 @@ fn user() { mod mod1 { use cgu_generic_function; - //~ TRANS_ITEM fn extern_generic::mod1[0]::user[0] @@ extern_generic-mod1[Internal] + //~ MONO_ITEM fn extern_generic::mod1[0]::user[0] @@ extern_generic-mod1[Internal] fn user() { let _ = cgu_generic_function::foo("abc"); } @@ -35,7 +35,7 @@ mod mod1 { mod mod1 { use cgu_generic_function; - //~ TRANS_ITEM fn extern_generic::mod1[0]::mod1[0]::user[0] @@ extern_generic-mod1-mod1[Internal] + //~ MONO_ITEM fn extern_generic::mod1[0]::mod1[0]::user[0] @@ extern_generic-mod1-mod1[Internal] fn user() { let _ = cgu_generic_function::foo("abc"); } @@ -45,18 +45,18 @@ mod mod1 { mod mod2 { use cgu_generic_function; - //~ TRANS_ITEM fn extern_generic::mod2[0]::user[0] @@ extern_generic-mod2[Internal] + //~ MONO_ITEM fn extern_generic::mod2[0]::user[0] @@ extern_generic-mod2[Internal] fn user() { let _ = cgu_generic_function::foo("abc"); } } mod mod3 { - //~ TRANS_ITEM fn extern_generic::mod3[0]::non_user[0] @@ extern_generic-mod3[Internal] + //~ MONO_ITEM fn extern_generic::mod3[0]::non_user[0] @@ extern_generic-mod3[Internal] fn non_user() {} } // Make sure the two generic functions from the extern crate get instantiated // once for the current crate -//~ TRANS_ITEM fn cgu_generic_function::foo[0]<&str> @@ cgu_generic_function.volatile[External] -//~ TRANS_ITEM fn cgu_generic_function::bar[0]<&str> @@ cgu_generic_function.volatile[External] +//~ MONO_ITEM fn cgu_generic_function::foo[0]<&str> @@ cgu_generic_function.volatile[External] +//~ MONO_ITEM fn cgu_generic_function::bar[0]<&str> @@ cgu_generic_function.volatile[External] diff --git a/src/test/codegen-units/partitioning/inlining-from-extern-crate.rs b/src/test/codegen-units/partitioning/inlining-from-extern-crate.rs index 01600c03ba2..4136557d800 100644 --- a/src/test/codegen-units/partitioning/inlining-from-extern-crate.rs +++ b/src/test/codegen-units/partitioning/inlining-from-extern-crate.rs @@ -11,7 +11,7 @@ // ignore-tidy-linelength // We specify -Z incremental here because we want to test the partitioning for // incremental compilation -// compile-flags:-Zprint-trans-items=lazy -Zincremental=tmp/partitioning-tests/inlining-from-extern-crate +// compile-flags:-Zprint-mono-items=lazy -Zincremental=tmp/partitioning-tests/inlining-from-extern-crate // compile-flags:-Zinline-in-all-cgus #![crate_type="lib"] @@ -22,28 +22,28 @@ extern crate cgu_explicit_inlining; // This test makes sure that items inlined from external crates are privately // instantiated in every codegen unit they are used in. -//~ TRANS_ITEM fn cgu_explicit_inlining::inlined[0] @@ inlining_from_extern_crate[Internal] inlining_from_extern_crate-mod1[Internal] -//~ TRANS_ITEM fn cgu_explicit_inlining::always_inlined[0] @@ inlining_from_extern_crate[Internal] inlining_from_extern_crate-mod2[Internal] +//~ MONO_ITEM fn cgu_explicit_inlining::inlined[0] @@ inlining_from_extern_crate[Internal] inlining_from_extern_crate-mod1[Internal] +//~ MONO_ITEM fn cgu_explicit_inlining::always_inlined[0] @@ inlining_from_extern_crate[Internal] inlining_from_extern_crate-mod2[Internal] -//~ TRANS_ITEM fn inlining_from_extern_crate::user[0] @@ inlining_from_extern_crate[External] +//~ MONO_ITEM fn inlining_from_extern_crate::user[0] @@ inlining_from_extern_crate[External] pub fn user() { cgu_explicit_inlining::inlined(); cgu_explicit_inlining::always_inlined(); - // does not generate a translation item in this crate + // does not generate a monomorphization in this crate cgu_explicit_inlining::never_inlined(); } pub mod mod1 { use cgu_explicit_inlining; - //~ TRANS_ITEM fn inlining_from_extern_crate::mod1[0]::user[0] @@ inlining_from_extern_crate-mod1[External] + //~ MONO_ITEM fn inlining_from_extern_crate::mod1[0]::user[0] @@ inlining_from_extern_crate-mod1[External] pub fn user() { cgu_explicit_inlining::inlined(); - // does not generate a translation item in this crate + // does not generate a monomorphization in this crate cgu_explicit_inlining::never_inlined(); } } @@ -51,12 +51,12 @@ pub mod mod1 { pub mod mod2 { use cgu_explicit_inlining; - //~ TRANS_ITEM fn inlining_from_extern_crate::mod2[0]::user[0] @@ inlining_from_extern_crate-mod2[External] + //~ MONO_ITEM fn inlining_from_extern_crate::mod2[0]::user[0] @@ inlining_from_extern_crate-mod2[External] pub fn user() { cgu_explicit_inlining::always_inlined(); - // does not generate a translation item in this crate + // does not generate a monomorphization in this crate cgu_explicit_inlining::never_inlined(); } } diff --git a/src/test/codegen-units/partitioning/local-drop-glue.rs b/src/test/codegen-units/partitioning/local-drop-glue.rs index f7c05285ed6..98729d99ea9 100644 --- a/src/test/codegen-units/partitioning/local-drop-glue.rs +++ b/src/test/codegen-units/partitioning/local-drop-glue.rs @@ -11,28 +11,28 @@ // ignore-tidy-linelength // We specify -Z incremental here because we want to test the partitioning for // incremental compilation -// compile-flags:-Zprint-trans-items=lazy -Zincremental=tmp/partitioning-tests/local-drop-glue +// compile-flags:-Zprint-mono-items=lazy -Zincremental=tmp/partitioning-tests/local-drop-glue // compile-flags:-Zinline-in-all-cgus #![allow(dead_code)] #![crate_type="rlib"] -//~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0] @@ local_drop_glue[Internal] local_drop_glue-mod1[Internal] +//~ MONO_ITEM fn core::ptr[0]::drop_in_place[0] @@ local_drop_glue[Internal] local_drop_glue-mod1[Internal] struct Struct { _a: u32 } impl Drop for Struct { - //~ TRANS_ITEM fn local_drop_glue::{{impl}}[0]::drop[0] @@ local_drop_glue[External] + //~ MONO_ITEM fn local_drop_glue::{{impl}}[0]::drop[0] @@ local_drop_glue[External] fn drop(&mut self) {} } -//~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0] @@ local_drop_glue[Internal] +//~ MONO_ITEM fn core::ptr[0]::drop_in_place[0] @@ local_drop_glue[Internal] struct Outer { _a: Struct } -//~ TRANS_ITEM fn local_drop_glue::user[0] @@ local_drop_glue[External] +//~ MONO_ITEM fn local_drop_glue::user[0] @@ local_drop_glue[External] pub fn user() { let _ = Outer { @@ -46,14 +46,14 @@ pub mod mod1 { use super::Struct; - //~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0] @@ local_drop_glue-mod1[Internal] + //~ MONO_ITEM fn core::ptr[0]::drop_in_place[0] @@ local_drop_glue-mod1[Internal] struct Struct2 { _a: Struct, - //~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0]<(u32, local_drop_glue::Struct[0])> @@ local_drop_glue-mod1[Internal] + //~ MONO_ITEM fn core::ptr[0]::drop_in_place[0]<(u32, local_drop_glue::Struct[0])> @@ local_drop_glue-mod1[Internal] _b: (u32, Struct), } - //~ TRANS_ITEM fn local_drop_glue::mod1[0]::user[0] @@ local_drop_glue-mod1[External] + //~ MONO_ITEM fn local_drop_glue::mod1[0]::user[0] @@ local_drop_glue-mod1[External] pub fn user() { let _ = Struct2 { diff --git a/src/test/codegen-units/partitioning/local-generic.rs b/src/test/codegen-units/partitioning/local-generic.rs index 33e3745502f..7c8ca20e1e3 100644 --- a/src/test/codegen-units/partitioning/local-generic.rs +++ b/src/test/codegen-units/partitioning/local-generic.rs @@ -11,18 +11,18 @@ // ignore-tidy-linelength // We specify -Z incremental here because we want to test the partitioning for // incremental compilation -// compile-flags:-Zprint-trans-items=eager -Zincremental=tmp/partitioning-tests/local-generic +// compile-flags:-Zprint-mono-items=eager -Zincremental=tmp/partitioning-tests/local-generic #![allow(dead_code)] #![crate_type="lib"] -//~ TRANS_ITEM fn local_generic::generic[0] @@ local_generic.volatile[External] -//~ TRANS_ITEM fn local_generic::generic[0] @@ local_generic.volatile[External] -//~ TRANS_ITEM fn local_generic::generic[0] @@ local_generic.volatile[External] -//~ TRANS_ITEM fn local_generic::generic[0]<&str> @@ local_generic.volatile[External] +//~ MONO_ITEM fn local_generic::generic[0] @@ local_generic.volatile[External] +//~ MONO_ITEM fn local_generic::generic[0] @@ local_generic.volatile[External] +//~ MONO_ITEM fn local_generic::generic[0] @@ local_generic.volatile[External] +//~ MONO_ITEM fn local_generic::generic[0]<&str> @@ local_generic.volatile[External] pub fn generic(x: T) -> T { x } -//~ TRANS_ITEM fn local_generic::user[0] @@ local_generic[Internal] +//~ MONO_ITEM fn local_generic::user[0] @@ local_generic[Internal] fn user() { let _ = generic(0u32); } @@ -30,7 +30,7 @@ fn user() { mod mod1 { pub use super::generic; - //~ TRANS_ITEM fn local_generic::mod1[0]::user[0] @@ local_generic-mod1[Internal] + //~ MONO_ITEM fn local_generic::mod1[0]::user[0] @@ local_generic-mod1[Internal] fn user() { let _ = generic(0u64); } @@ -38,7 +38,7 @@ mod mod1 { mod mod1 { use super::generic; - //~ TRANS_ITEM fn local_generic::mod1[0]::mod1[0]::user[0] @@ local_generic-mod1-mod1[Internal] + //~ MONO_ITEM fn local_generic::mod1[0]::mod1[0]::user[0] @@ local_generic-mod1-mod1[Internal] fn user() { let _ = generic('c'); } @@ -48,7 +48,7 @@ mod mod1 { mod mod2 { use super::generic; - //~ TRANS_ITEM fn local_generic::mod2[0]::user[0] @@ local_generic-mod2[Internal] + //~ MONO_ITEM fn local_generic::mod2[0]::user[0] @@ local_generic-mod2[Internal] fn user() { let _ = generic("abc"); } diff --git a/src/test/codegen-units/partitioning/local-inlining-but-not-all.rs b/src/test/codegen-units/partitioning/local-inlining-but-not-all.rs index cf197301eec..747f768c11f 100644 --- a/src/test/codegen-units/partitioning/local-inlining-but-not-all.rs +++ b/src/test/codegen-units/partitioning/local-inlining-but-not-all.rs @@ -11,7 +11,7 @@ // ignore-tidy-linelength // We specify -Z incremental here because we want to test the partitioning for // incremental compilation -// compile-flags:-Zprint-trans-items=lazy -Zincremental=tmp/partitioning-tests/local-inlining-but-not-all +// compile-flags:-Zprint-mono-items=lazy -Zincremental=tmp/partitioning-tests/local-inlining-but-not-all // compile-flags:-Zinline-in-all-cgus=no #![allow(dead_code)] @@ -19,7 +19,7 @@ mod inline { - //~ TRANS_ITEM fn local_inlining_but_not_all::inline[0]::inlined_function[0] @@ local_inlining_but_not_all-inline[External] + //~ MONO_ITEM fn local_inlining_but_not_all::inline[0]::inlined_function[0] @@ local_inlining_but_not_all-inline[External] #[inline] pub fn inlined_function() { @@ -30,7 +30,7 @@ mod inline { pub mod user1 { use super::inline; - //~ TRANS_ITEM fn local_inlining_but_not_all::user1[0]::foo[0] @@ local_inlining_but_not_all-user1[External] + //~ MONO_ITEM fn local_inlining_but_not_all::user1[0]::foo[0] @@ local_inlining_but_not_all-user1[External] pub fn foo() { inline::inlined_function(); } @@ -39,7 +39,7 @@ pub mod user1 { pub mod user2 { use super::inline; - //~ TRANS_ITEM fn local_inlining_but_not_all::user2[0]::bar[0] @@ local_inlining_but_not_all-user2[External] + //~ MONO_ITEM fn local_inlining_but_not_all::user2[0]::bar[0] @@ local_inlining_but_not_all-user2[External] pub fn bar() { inline::inlined_function(); } @@ -47,7 +47,7 @@ pub mod user2 { pub mod non_user { - //~ TRANS_ITEM fn local_inlining_but_not_all::non_user[0]::baz[0] @@ local_inlining_but_not_all-non_user[External] + //~ MONO_ITEM fn local_inlining_but_not_all::non_user[0]::baz[0] @@ local_inlining_but_not_all-non_user[External] pub fn baz() { } diff --git a/src/test/codegen-units/partitioning/local-inlining.rs b/src/test/codegen-units/partitioning/local-inlining.rs index 3502aa59fdc..f144f0d992b 100644 --- a/src/test/codegen-units/partitioning/local-inlining.rs +++ b/src/test/codegen-units/partitioning/local-inlining.rs @@ -11,7 +11,7 @@ // ignore-tidy-linelength // We specify -Z incremental here because we want to test the partitioning for // incremental compilation -// compile-flags:-Zprint-trans-items=lazy -Zincremental=tmp/partitioning-tests/local-inlining +// compile-flags:-Zprint-mono-items=lazy -Zincremental=tmp/partitioning-tests/local-inlining // compile-flags:-Zinline-in-all-cgus #![allow(dead_code)] @@ -20,7 +20,7 @@ mod inline { // Important: This function should show up in all codegen units where it is inlined - //~ TRANS_ITEM fn local_inlining::inline[0]::inlined_function[0] @@ local_inlining-user1[Internal] local_inlining-user2[Internal] + //~ MONO_ITEM fn local_inlining::inline[0]::inlined_function[0] @@ local_inlining-user1[Internal] local_inlining-user2[Internal] #[inline(always)] pub fn inlined_function() { @@ -31,7 +31,7 @@ mod inline { pub mod user1 { use super::inline; - //~ TRANS_ITEM fn local_inlining::user1[0]::foo[0] @@ local_inlining-user1[External] + //~ MONO_ITEM fn local_inlining::user1[0]::foo[0] @@ local_inlining-user1[External] pub fn foo() { inline::inlined_function(); } @@ -40,7 +40,7 @@ pub mod user1 { pub mod user2 { use super::inline; - //~ TRANS_ITEM fn local_inlining::user2[0]::bar[0] @@ local_inlining-user2[External] + //~ MONO_ITEM fn local_inlining::user2[0]::bar[0] @@ local_inlining-user2[External] pub fn bar() { inline::inlined_function(); } @@ -48,7 +48,7 @@ pub mod user2 { pub mod non_user { - //~ TRANS_ITEM fn local_inlining::non_user[0]::baz[0] @@ local_inlining-non_user[External] + //~ MONO_ITEM fn local_inlining::non_user[0]::baz[0] @@ local_inlining-non_user[External] pub fn baz() { } diff --git a/src/test/codegen-units/partitioning/local-transitive-inlining.rs b/src/test/codegen-units/partitioning/local-transitive-inlining.rs index ed883954f3f..8637844a83d 100644 --- a/src/test/codegen-units/partitioning/local-transitive-inlining.rs +++ b/src/test/codegen-units/partitioning/local-transitive-inlining.rs @@ -11,7 +11,7 @@ // ignore-tidy-linelength // We specify -Z incremental here because we want to test the partitioning for // incremental compilation -// compile-flags:-Zprint-trans-items=lazy -Zincremental=tmp/partitioning-tests/local-transitive-inlining +// compile-flags:-Zprint-mono-items=lazy -Zincremental=tmp/partitioning-tests/local-transitive-inlining // compile-flags:-Zinline-in-all-cgus #![allow(dead_code)] @@ -19,7 +19,7 @@ mod inline { - //~ TRANS_ITEM fn local_transitive_inlining::inline[0]::inlined_function[0] @@ local_transitive_inlining-indirect_user[Internal] + //~ MONO_ITEM fn local_transitive_inlining::inline[0]::inlined_function[0] @@ local_transitive_inlining-indirect_user[Internal] #[inline(always)] pub fn inlined_function() { @@ -30,7 +30,7 @@ mod inline { mod direct_user { use super::inline; - //~ TRANS_ITEM fn local_transitive_inlining::direct_user[0]::foo[0] @@ local_transitive_inlining-indirect_user[Internal] + //~ MONO_ITEM fn local_transitive_inlining::direct_user[0]::foo[0] @@ local_transitive_inlining-indirect_user[Internal] #[inline(always)] pub fn foo() { inline::inlined_function(); @@ -40,7 +40,7 @@ mod direct_user { pub mod indirect_user { use super::direct_user; - //~ TRANS_ITEM fn local_transitive_inlining::indirect_user[0]::bar[0] @@ local_transitive_inlining-indirect_user[External] + //~ MONO_ITEM fn local_transitive_inlining::indirect_user[0]::bar[0] @@ local_transitive_inlining-indirect_user[External] pub fn bar() { direct_user::foo(); } @@ -48,7 +48,7 @@ pub mod indirect_user { pub mod non_user { - //~ TRANS_ITEM fn local_transitive_inlining::non_user[0]::baz[0] @@ local_transitive_inlining-non_user[External] + //~ MONO_ITEM fn local_transitive_inlining::non_user[0]::baz[0] @@ local_transitive_inlining-non_user[External] pub fn baz() { } diff --git a/src/test/codegen-units/partitioning/methods-are-with-self-type.rs b/src/test/codegen-units/partitioning/methods-are-with-self-type.rs index aa01289de59..ff25a7194e0 100644 --- a/src/test/codegen-units/partitioning/methods-are-with-self-type.rs +++ b/src/test/codegen-units/partitioning/methods-are-with-self-type.rs @@ -16,7 +16,7 @@ // ignore-tidy-linelength // We specify -Z incremental here because we want to test the partitioning for // incremental compilation -// compile-flags:-Zprint-trans-items=lazy -Zincremental=tmp/partitioning-tests/methods-are-with-self-type +// compile-flags:-Zprint-mono-items=lazy -Zincremental=tmp/partitioning-tests/methods-are-with-self-type #![allow(dead_code)] #![feature(start)] @@ -31,10 +31,10 @@ mod mod1 { // Even though the impl is in `mod1`, the methods should end up in the // parent module, since that is where their self-type is. impl SomeType { - //~ TRANS_ITEM fn methods_are_with_self_type::mod1[0]::{{impl}}[0]::method[0] @@ methods_are_with_self_type[External] + //~ MONO_ITEM fn methods_are_with_self_type::mod1[0]::{{impl}}[0]::method[0] @@ methods_are_with_self_type[External] fn method(&self) {} - //~ TRANS_ITEM fn methods_are_with_self_type::mod1[0]::{{impl}}[0]::associated_fn[0] @@ methods_are_with_self_type[External] + //~ MONO_ITEM fn methods_are_with_self_type::mod1[0]::{{impl}}[0]::associated_fn[0] @@ methods_are_with_self_type[External] fn associated_fn() {} } @@ -64,25 +64,25 @@ mod type2 { pub struct Struct; } -//~ TRANS_ITEM fn methods_are_with_self_type::start[0] +//~ MONO_ITEM fn methods_are_with_self_type::start[0] #[start] fn start(_: isize, _: *const *const u8) -> isize { - //~ TRANS_ITEM fn methods_are_with_self_type::mod1[0]::{{impl}}[1]::method[0] @@ methods_are_with_self_type.volatile[WeakODR] + //~ MONO_ITEM fn methods_are_with_self_type::mod1[0]::{{impl}}[1]::method[0] @@ methods_are_with_self_type.volatile[WeakODR] SomeGenericType(0u32, 0u64).method(); - //~ TRANS_ITEM fn methods_are_with_self_type::mod1[0]::{{impl}}[1]::associated_fn[0] @@ methods_are_with_self_type.volatile[WeakODR] + //~ MONO_ITEM fn methods_are_with_self_type::mod1[0]::{{impl}}[1]::associated_fn[0] @@ methods_are_with_self_type.volatile[WeakODR] SomeGenericType::associated_fn('c', "&str"); - //~ TRANS_ITEM fn methods_are_with_self_type::{{impl}}[0]::foo[0] @@ methods_are_with_self_type-type1.volatile[WeakODR] + //~ MONO_ITEM fn methods_are_with_self_type::{{impl}}[0]::foo[0] @@ methods_are_with_self_type-type1.volatile[WeakODR] type1::Struct.foo(); - //~ TRANS_ITEM fn methods_are_with_self_type::{{impl}}[0]::foo[0] @@ methods_are_with_self_type-type2.volatile[WeakODR] + //~ MONO_ITEM fn methods_are_with_self_type::{{impl}}[0]::foo[0] @@ methods_are_with_self_type-type2.volatile[WeakODR] type2::Struct.foo(); - //~ TRANS_ITEM fn methods_are_with_self_type::Trait[0]::default[0] @@ methods_are_with_self_type-type1.volatile[WeakODR] + //~ MONO_ITEM fn methods_are_with_self_type::Trait[0]::default[0] @@ methods_are_with_self_type-type1.volatile[WeakODR] type1::Struct.default(); - //~ TRANS_ITEM fn methods_are_with_self_type::Trait[0]::default[0] @@ methods_are_with_self_type-type2.volatile[WeakODR] + //~ MONO_ITEM fn methods_are_with_self_type::Trait[0]::default[0] @@ methods_are_with_self_type-type2.volatile[WeakODR] type2::Struct.default(); 0 } -//~ TRANS_ITEM drop-glue i8 +//~ MONO_ITEM drop-glue i8 diff --git a/src/test/codegen-units/partitioning/regular-modules.rs b/src/test/codegen-units/partitioning/regular-modules.rs index 9bdbc8b85e8..e1ec6f51b30 100644 --- a/src/test/codegen-units/partitioning/regular-modules.rs +++ b/src/test/codegen-units/partitioning/regular-modules.rs @@ -11,72 +11,72 @@ // ignore-tidy-linelength // We specify -Z incremental here because we want to test the partitioning for // incremental compilation -// compile-flags:-Zprint-trans-items=eager -Zincremental=tmp/partitioning-tests/regular-modules +// compile-flags:-Zprint-mono-items=eager -Zincremental=tmp/partitioning-tests/regular-modules #![allow(dead_code)] #![crate_type="lib"] -//~ TRANS_ITEM fn regular_modules::foo[0] @@ regular_modules[Internal] +//~ MONO_ITEM fn regular_modules::foo[0] @@ regular_modules[Internal] fn foo() {} -//~ TRANS_ITEM fn regular_modules::bar[0] @@ regular_modules[Internal] +//~ MONO_ITEM fn regular_modules::bar[0] @@ regular_modules[Internal] fn bar() {} -//~ TRANS_ITEM static regular_modules::BAZ[0] @@ regular_modules[Internal] +//~ MONO_ITEM static regular_modules::BAZ[0] @@ regular_modules[Internal] static BAZ: u64 = 0; mod mod1 { - //~ TRANS_ITEM fn regular_modules::mod1[0]::foo[0] @@ regular_modules-mod1[Internal] + //~ MONO_ITEM fn regular_modules::mod1[0]::foo[0] @@ regular_modules-mod1[Internal] fn foo() {} - //~ TRANS_ITEM fn regular_modules::mod1[0]::bar[0] @@ regular_modules-mod1[Internal] + //~ MONO_ITEM fn regular_modules::mod1[0]::bar[0] @@ regular_modules-mod1[Internal] fn bar() {} - //~ TRANS_ITEM static regular_modules::mod1[0]::BAZ[0] @@ regular_modules-mod1[Internal] + //~ MONO_ITEM static regular_modules::mod1[0]::BAZ[0] @@ regular_modules-mod1[Internal] static BAZ: u64 = 0; mod mod1 { - //~ TRANS_ITEM fn regular_modules::mod1[0]::mod1[0]::foo[0] @@ regular_modules-mod1-mod1[Internal] + //~ MONO_ITEM fn regular_modules::mod1[0]::mod1[0]::foo[0] @@ regular_modules-mod1-mod1[Internal] fn foo() {} - //~ TRANS_ITEM fn regular_modules::mod1[0]::mod1[0]::bar[0] @@ regular_modules-mod1-mod1[Internal] + //~ MONO_ITEM fn regular_modules::mod1[0]::mod1[0]::bar[0] @@ regular_modules-mod1-mod1[Internal] fn bar() {} - //~ TRANS_ITEM static regular_modules::mod1[0]::mod1[0]::BAZ[0] @@ regular_modules-mod1-mod1[Internal] + //~ MONO_ITEM static regular_modules::mod1[0]::mod1[0]::BAZ[0] @@ regular_modules-mod1-mod1[Internal] static BAZ: u64 = 0; } mod mod2 { - //~ TRANS_ITEM fn regular_modules::mod1[0]::mod2[0]::foo[0] @@ regular_modules-mod1-mod2[Internal] + //~ MONO_ITEM fn regular_modules::mod1[0]::mod2[0]::foo[0] @@ regular_modules-mod1-mod2[Internal] fn foo() {} - //~ TRANS_ITEM fn regular_modules::mod1[0]::mod2[0]::bar[0] @@ regular_modules-mod1-mod2[Internal] + //~ MONO_ITEM fn regular_modules::mod1[0]::mod2[0]::bar[0] @@ regular_modules-mod1-mod2[Internal] fn bar() {} - //~ TRANS_ITEM static regular_modules::mod1[0]::mod2[0]::BAZ[0] @@ regular_modules-mod1-mod2[Internal] + //~ MONO_ITEM static regular_modules::mod1[0]::mod2[0]::BAZ[0] @@ regular_modules-mod1-mod2[Internal] static BAZ: u64 = 0; } } mod mod2 { - //~ TRANS_ITEM fn regular_modules::mod2[0]::foo[0] @@ regular_modules-mod2[Internal] + //~ MONO_ITEM fn regular_modules::mod2[0]::foo[0] @@ regular_modules-mod2[Internal] fn foo() {} - //~ TRANS_ITEM fn regular_modules::mod2[0]::bar[0] @@ regular_modules-mod2[Internal] + //~ MONO_ITEM fn regular_modules::mod2[0]::bar[0] @@ regular_modules-mod2[Internal] fn bar() {} - //~ TRANS_ITEM static regular_modules::mod2[0]::BAZ[0] @@ regular_modules-mod2[Internal] + //~ MONO_ITEM static regular_modules::mod2[0]::BAZ[0] @@ regular_modules-mod2[Internal] static BAZ: u64 = 0; mod mod1 { - //~ TRANS_ITEM fn regular_modules::mod2[0]::mod1[0]::foo[0] @@ regular_modules-mod2-mod1[Internal] + //~ MONO_ITEM fn regular_modules::mod2[0]::mod1[0]::foo[0] @@ regular_modules-mod2-mod1[Internal] fn foo() {} - //~ TRANS_ITEM fn regular_modules::mod2[0]::mod1[0]::bar[0] @@ regular_modules-mod2-mod1[Internal] + //~ MONO_ITEM fn regular_modules::mod2[0]::mod1[0]::bar[0] @@ regular_modules-mod2-mod1[Internal] fn bar() {} - //~ TRANS_ITEM static regular_modules::mod2[0]::mod1[0]::BAZ[0] @@ regular_modules-mod2-mod1[Internal] + //~ MONO_ITEM static regular_modules::mod2[0]::mod1[0]::BAZ[0] @@ regular_modules-mod2-mod1[Internal] static BAZ: u64 = 0; } mod mod2 { - //~ TRANS_ITEM fn regular_modules::mod2[0]::mod2[0]::foo[0] @@ regular_modules-mod2-mod2[Internal] + //~ MONO_ITEM fn regular_modules::mod2[0]::mod2[0]::foo[0] @@ regular_modules-mod2-mod2[Internal] fn foo() {} - //~ TRANS_ITEM fn regular_modules::mod2[0]::mod2[0]::bar[0] @@ regular_modules-mod2-mod2[Internal] + //~ MONO_ITEM fn regular_modules::mod2[0]::mod2[0]::bar[0] @@ regular_modules-mod2-mod2[Internal] fn bar() {} - //~ TRANS_ITEM static regular_modules::mod2[0]::mod2[0]::BAZ[0] @@ regular_modules-mod2-mod2[Internal] + //~ MONO_ITEM static regular_modules::mod2[0]::mod2[0]::BAZ[0] @@ regular_modules-mod2-mod2[Internal] static BAZ: u64 = 0; } } diff --git a/src/test/codegen-units/partitioning/shared-generics.rs b/src/test/codegen-units/partitioning/shared-generics.rs index d352609bfcb..880361fac2e 100644 --- a/src/test/codegen-units/partitioning/shared-generics.rs +++ b/src/test/codegen-units/partitioning/shared-generics.rs @@ -9,17 +9,17 @@ // except according to those terms. // ignore-tidy-linelength -// compile-flags:-Zprint-trans-items=eager -Zshare-generics=yes -Zincremental=tmp/partitioning-tests/shared-generics-exe +// compile-flags:-Zprint-mono-items=eager -Zshare-generics=yes -Zincremental=tmp/partitioning-tests/shared-generics-exe #![crate_type="rlib"] // aux-build:shared_generics_aux.rs extern crate shared_generics_aux; -//~ TRANS_ITEM fn shared_generics::foo[0] +//~ MONO_ITEM fn shared_generics::foo[0] pub fn foo() { - //~ TRANS_ITEM fn shared_generics_aux::generic_fn[0] @@ shared_generics_aux.volatile[External] + //~ MONO_ITEM fn shared_generics_aux::generic_fn[0] @@ shared_generics_aux.volatile[External] let _ = shared_generics_aux::generic_fn(0u16, 1u16); // This should not generate a monomorphization because it's already @@ -27,4 +27,4 @@ pub fn foo() { let _ = shared_generics_aux::generic_fn(0.0f32, 3.0f32); } -// TRANS_ITEM drop-glue i8 +// MONO_ITEM drop-glue i8 diff --git a/src/test/codegen-units/partitioning/statics.rs b/src/test/codegen-units/partitioning/statics.rs index 12ef34441ff..b6c1e5210da 100644 --- a/src/test/codegen-units/partitioning/statics.rs +++ b/src/test/codegen-units/partitioning/statics.rs @@ -11,38 +11,38 @@ // ignore-tidy-linelength // We specify -Z incremental here because we want to test the partitioning for // incremental compilation -// compile-flags:-Zprint-trans-items=lazy -Zincremental=tmp/partitioning-tests/statics +// compile-flags:-Zprint-mono-items=lazy -Zincremental=tmp/partitioning-tests/statics #![crate_type="rlib"] -//~ TRANS_ITEM static statics::FOO[0] @@ statics[Internal] +//~ MONO_ITEM static statics::FOO[0] @@ statics[Internal] static FOO: u32 = 0; -//~ TRANS_ITEM static statics::BAR[0] @@ statics[Internal] +//~ MONO_ITEM static statics::BAR[0] @@ statics[Internal] static BAR: u32 = 0; -//~ TRANS_ITEM fn statics::function[0] @@ statics[External] +//~ MONO_ITEM fn statics::function[0] @@ statics[External] pub fn function() { - //~ TRANS_ITEM static statics::function[0]::FOO[0] @@ statics[Internal] + //~ MONO_ITEM static statics::function[0]::FOO[0] @@ statics[Internal] static FOO: u32 = 0; - //~ TRANS_ITEM static statics::function[0]::BAR[0] @@ statics[Internal] + //~ MONO_ITEM static statics::function[0]::BAR[0] @@ statics[Internal] static BAR: u32 = 0; } pub mod mod1 { - //~ TRANS_ITEM static statics::mod1[0]::FOO[0] @@ statics-mod1[Internal] + //~ MONO_ITEM static statics::mod1[0]::FOO[0] @@ statics-mod1[Internal] static FOO: u32 = 0; - //~ TRANS_ITEM static statics::mod1[0]::BAR[0] @@ statics-mod1[Internal] + //~ MONO_ITEM static statics::mod1[0]::BAR[0] @@ statics-mod1[Internal] static BAR: u32 = 0; - //~ TRANS_ITEM fn statics::mod1[0]::function[0] @@ statics-mod1[External] + //~ MONO_ITEM fn statics::mod1[0]::function[0] @@ statics-mod1[External] pub fn function() { - //~ TRANS_ITEM static statics::mod1[0]::function[0]::FOO[0] @@ statics-mod1[Internal] + //~ MONO_ITEM static statics::mod1[0]::function[0]::FOO[0] @@ statics-mod1[Internal] static FOO: u32 = 0; - //~ TRANS_ITEM static statics::mod1[0]::function[0]::BAR[0] @@ statics-mod1[Internal] + //~ MONO_ITEM static statics::mod1[0]::function[0]::BAR[0] @@ statics-mod1[Internal] static BAR: u32 = 0; } } diff --git a/src/test/codegen-units/partitioning/vtable-through-const.rs b/src/test/codegen-units/partitioning/vtable-through-const.rs index d0acddda637..74533c1015b 100644 --- a/src/test/codegen-units/partitioning/vtable-through-const.rs +++ b/src/test/codegen-units/partitioning/vtable-through-const.rs @@ -12,7 +12,7 @@ // We specify -Z incremental here because we want to test the partitioning for // incremental compilation -// compile-flags:-Zprint-trans-items=lazy -Zincremental=tmp/partitioning-tests/vtable-through-const +// compile-flags:-Zprint-mono-items=lazy -Zincremental=tmp/partitioning-tests/vtable-through-const // compile-flags:-Zinline-in-all-cgus // This test case makes sure, that references made through constants are @@ -40,7 +40,7 @@ mod mod1 { fn id(x: T) -> T { x } - // These are referenced, so they produce trans-items (see start()) + // These are referenced, so they produce mono-items (see start()) pub const TRAIT1_REF: &'static Trait1 = &0u32 as &Trait1; pub const TRAIT1_GEN_REF: &'static Trait1Gen = &0u32 as &Trait1Gen; pub const ID_CHAR: fn(char) -> char = id::; @@ -64,34 +64,34 @@ mod mod1 { fn do_something_else(&self, x: T) -> T { x } } - // These are not referenced, so they do not produce trans-items + // These are not referenced, so they do not produce mono-items pub const TRAIT2_REF: &'static Trait2 = &0u32 as &Trait2; pub const TRAIT2_GEN_REF: &'static Trait2Gen = &0u32 as &Trait2Gen; pub const ID_I64: fn(i64) -> i64 = id::; } -//~ TRANS_ITEM fn vtable_through_const::start[0] +//~ MONO_ITEM fn vtable_through_const::start[0] #[start] fn start(_: isize, _: *const *const u8) -> isize { - //~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0] @@ vtable_through_const[Internal] + //~ MONO_ITEM fn core::ptr[0]::drop_in_place[0] @@ vtable_through_const[Internal] // Since Trait1::do_something() is instantiated via its default implementation, // it is considered a generic and is instantiated here only because it is // referenced in this module. - //~ TRANS_ITEM fn vtable_through_const::mod1[0]::Trait1[0]::do_something_else[0] @@ vtable_through_const-mod1.volatile[External] + //~ MONO_ITEM fn vtable_through_const::mod1[0]::Trait1[0]::do_something_else[0] @@ vtable_through_const-mod1.volatile[External] // Although it is never used, Trait1::do_something_else() has to be // instantiated locally here too, otherwise the <&u32 as &Trait1> vtable // could not be fully constructed. - //~ TRANS_ITEM fn vtable_through_const::mod1[0]::Trait1[0]::do_something[0] @@ vtable_through_const-mod1.volatile[External] + //~ MONO_ITEM fn vtable_through_const::mod1[0]::Trait1[0]::do_something[0] @@ vtable_through_const-mod1.volatile[External] mod1::TRAIT1_REF.do_something(); // Same as above - //~ TRANS_ITEM fn vtable_through_const::mod1[0]::{{impl}}[1]::do_something[0] @@ vtable_through_const-mod1.volatile[External] - //~ TRANS_ITEM fn vtable_through_const::mod1[0]::{{impl}}[1]::do_something_else[0] @@ vtable_through_const-mod1.volatile[External] + //~ MONO_ITEM fn vtable_through_const::mod1[0]::{{impl}}[1]::do_something[0] @@ vtable_through_const-mod1.volatile[External] + //~ MONO_ITEM fn vtable_through_const::mod1[0]::{{impl}}[1]::do_something_else[0] @@ vtable_through_const-mod1.volatile[External] mod1::TRAIT1_GEN_REF.do_something(0u8); - //~ TRANS_ITEM fn vtable_through_const::mod1[0]::id[0] @@ vtable_through_const-mod1.volatile[External] + //~ MONO_ITEM fn vtable_through_const::mod1[0]::id[0] @@ vtable_through_const-mod1.volatile[External] mod1::ID_CHAR('x'); 0 diff --git a/src/test/compile-fail/const-err.rs b/src/test/compile-fail/const-err.rs index 8bd759b6d37..f6a64bcba21 100644 --- a/src/test/compile-fail/const-err.rs +++ b/src/test/compile-fail/const-err.rs @@ -10,7 +10,7 @@ // compile-flags: -Zforce-overflow-checks=on -// these errors are not actually "const_err", they occur in trans/consts +// these errors are not actually "const_err", they occur in codegen/consts // and are unconditional warnings that can't be denied or allowed #![allow(exceeding_bitshifts)] diff --git a/src/test/compile-fail/const-eval-overflow2.rs b/src/test/compile-fail/const-eval-overflow2.rs index faa8c3039b7..7b5db7a4f6d 100644 --- a/src/test/compile-fail/const-eval-overflow2.rs +++ b/src/test/compile-fail/const-eval-overflow2.rs @@ -11,7 +11,7 @@ #![allow(unused_imports)] // Note: the relevant lint pass here runs before some of the constant -// evaluation below (e.g. that performed by trans and llvm), so if you +// evaluation below (e.g. that performed by codegen and llvm), so if you // change this warn to a deny, then the compiler will exit before // those errors are detected. diff --git a/src/test/compile-fail/const-eval-overflow2b.rs b/src/test/compile-fail/const-eval-overflow2b.rs index d827e680c5b..ce4dc72555d 100644 --- a/src/test/compile-fail/const-eval-overflow2b.rs +++ b/src/test/compile-fail/const-eval-overflow2b.rs @@ -11,7 +11,7 @@ #![allow(unused_imports)] // Note: the relevant lint pass here runs before some of the constant -// evaluation below (e.g. that performed by trans and llvm), so if you +// evaluation below (e.g. that performed by codegen and llvm), so if you // change this warn to a deny, then the compiler will exit before // those errors are detected. diff --git a/src/test/compile-fail/const-eval-overflow2c.rs b/src/test/compile-fail/const-eval-overflow2c.rs index 2fd46b038ef..88eb14a1330 100644 --- a/src/test/compile-fail/const-eval-overflow2c.rs +++ b/src/test/compile-fail/const-eval-overflow2c.rs @@ -11,7 +11,7 @@ #![allow(unused_imports)] // Note: the relevant lint pass here runs before some of the constant -// evaluation below (e.g. that performed by trans and llvm), so if you +// evaluation below (e.g. that performed by codegen and llvm), so if you // change this warn to a deny, then the compiler will exit before // those errors are detected. diff --git a/src/test/compile-fail/dep-graph-assoc-type-codegen.rs b/src/test/compile-fail/dep-graph-assoc-type-codegen.rs new file mode 100644 index 00000000000..c20cfbc7e23 --- /dev/null +++ b/src/test/compile-fail/dep-graph-assoc-type-codegen.rs @@ -0,0 +1,47 @@ +// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Test that when a trait impl changes, fns whose body uses that trait +// must also be recompiled. + +// compile-flags: -Z query-dep-graph + +#![feature(rustc_attrs)] +#![allow(warnings)] + +fn main() { } + +pub trait Foo: Sized { + type T; + fn method(self) { } +} + +mod x { + use Foo; + + #[rustc_if_this_changed] + impl Foo for char { type T = char; } + + impl Foo for u32 { type T = u32; } +} + +mod y { + use Foo; + + #[rustc_then_this_would_need(TypeckTables)] //~ ERROR OK + pub fn use_char_assoc() { + // Careful here: in the representation, ::T gets + // normalized away, so at a certain point we had no edge to + // codegen. (But now codegen just depends on typeck.) + let x: ::T = 'a'; + } + + pub fn take_foo(t: T) { } +} diff --git a/src/test/compile-fail/dep-graph-assoc-type-trans.rs b/src/test/compile-fail/dep-graph-assoc-type-trans.rs deleted file mode 100644 index 007a80008a8..00000000000 --- a/src/test/compile-fail/dep-graph-assoc-type-trans.rs +++ /dev/null @@ -1,47 +0,0 @@ -// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -// Test that when a trait impl changes, fns whose body uses that trait -// must also be recompiled. - -// compile-flags: -Z query-dep-graph - -#![feature(rustc_attrs)] -#![allow(warnings)] - -fn main() { } - -pub trait Foo: Sized { - type T; - fn method(self) { } -} - -mod x { - use Foo; - - #[rustc_if_this_changed] - impl Foo for char { type T = char; } - - impl Foo for u32 { type T = u32; } -} - -mod y { - use Foo; - - #[rustc_then_this_would_need(TypeckTables)] //~ ERROR OK - pub fn use_char_assoc() { - // Careful here: in the representation, ::T gets - // normalized away, so at a certain point we had no edge to - // trans. (But now trans just depends on typeck.) - let x: ::T = 'a'; - } - - pub fn take_foo(t: T) { } -} diff --git a/src/test/debuginfo/struct-with-destructor.rs b/src/test/debuginfo/struct-with-destructor.rs index af70b4a63fd..ab935a07d65 100644 --- a/src/test/debuginfo/struct-with-destructor.rs +++ b/src/test/debuginfo/struct-with-destructor.rs @@ -91,7 +91,7 @@ struct NestedOuter { // The compiler adds a 'destructed' boolean field to structs implementing Drop. This field is used -// at runtime to prevent drop() to be executed more than once (see middle::trans::adt). +// at runtime to prevent drop() to be executed more than once. // This field must be incorporated by the debug info generation. Otherwise the debugger assumes a // wrong size/layout for the struct. fn main() { diff --git a/src/test/debuginfo/var-captured-in-sendable-closure.rs b/src/test/debuginfo/var-captured-in-sendable-closure.rs index 120bbdd7ba9..9aeb3bc9133 100644 --- a/src/test/debuginfo/var-captured-in-sendable-closure.rs +++ b/src/test/debuginfo/var-captured-in-sendable-closure.rs @@ -72,7 +72,7 @@ fn main() { let constant2 = 6_usize; // The `self` argument of the following closure should be passed by value - // to FnOnce::call_once(self, args), which gets translated a bit differently + // to FnOnce::call_once(self, args), which gets codegened a bit differently // than the regular case. Let's make sure this is supported too. let immedate_env = move || { zzz(); // #break diff --git a/src/test/incremental/cache_file_headers.rs b/src/test/incremental/cache_file_headers.rs index feecfecd0b8..02ee06b4cf4 100644 --- a/src/test/incremental/cache_file_headers.rs +++ b/src/test/incremental/cache_file_headers.rs @@ -13,7 +13,7 @@ // different compiler version. This is tested by artificially forcing the // emission of a different compiler version in the header of rpass1 artifacts, // and then making sure that the only object file of the test program gets -// re-translated although the program stays unchanged. +// re-codegened although the program stays unchanged. // The `l33t haxx0r` Rust compiler is known to produce incr. comp. artifacts // that are outrageously incompatible with just about anything, even itself: @@ -23,7 +23,7 @@ // compile-flags: -Z query-dep-graph #![feature(rustc_attrs)] -#![rustc_partition_translated(module="cache_file_headers", cfg="rpass2")] +#![rustc_partition_codegened(module="cache_file_headers", cfg="rpass2")] fn main() { // empty diff --git a/src/test/incremental/change_add_field/struct_point.rs b/src/test/incremental/change_add_field/struct_point.rs index da3b9e4d6d6..37d1a397303 100644 --- a/src/test/incremental/change_add_field/struct_point.rs +++ b/src/test/incremental/change_add_field/struct_point.rs @@ -22,14 +22,14 @@ #![allow(dead_code)] #![crate_type = "rlib"] -// These are expected to require translation. -#![rustc_partition_translated(module="struct_point-point", cfg="cfail2")] -#![rustc_partition_translated(module="struct_point-fn_with_type_in_sig", cfg="cfail2")] -#![rustc_partition_translated(module="struct_point-call_fn_with_type_in_sig", cfg="cfail2")] -#![rustc_partition_translated(module="struct_point-fn_with_type_in_body", cfg="cfail2")] -#![rustc_partition_translated(module="struct_point-fn_make_struct", cfg="cfail2")] -#![rustc_partition_translated(module="struct_point-fn_read_field", cfg="cfail2")] -#![rustc_partition_translated(module="struct_point-fn_write_field", cfg="cfail2")] +// These are expected to require codegen. +#![rustc_partition_codegened(module="struct_point-point", cfg="cfail2")] +#![rustc_partition_codegened(module="struct_point-fn_with_type_in_sig", cfg="cfail2")] +#![rustc_partition_codegened(module="struct_point-call_fn_with_type_in_sig", cfg="cfail2")] +#![rustc_partition_codegened(module="struct_point-fn_with_type_in_body", cfg="cfail2")] +#![rustc_partition_codegened(module="struct_point-fn_make_struct", cfg="cfail2")] +#![rustc_partition_codegened(module="struct_point-fn_read_field", cfg="cfail2")] +#![rustc_partition_codegened(module="struct_point-fn_write_field", cfg="cfail2")] #![rustc_partition_reused(module="struct_point-call_fn_with_type_in_body", cfg="cfail2")] diff --git a/src/test/incremental/change_private_fn/struct_point.rs b/src/test/incremental/change_private_fn/struct_point.rs index 63e137a7e0b..d1b8399dbda 100644 --- a/src/test/incremental/change_private_fn/struct_point.rs +++ b/src/test/incremental/change_private_fn/struct_point.rs @@ -20,7 +20,7 @@ #![allow(dead_code)] #![crate_type = "rlib"] -#![rustc_partition_translated(module="struct_point-point", cfg="cfail2")] +#![rustc_partition_codegened(module="struct_point-point", cfg="cfail2")] #![rustc_partition_reused(module="struct_point-fn_calls_methods_in_same_impl", cfg="cfail2")] #![rustc_partition_reused(module="struct_point-fn_calls_methods_in_another_impl", cfg="cfail2")] diff --git a/src/test/incremental/change_private_impl_method/struct_point.rs b/src/test/incremental/change_private_impl_method/struct_point.rs index 7f2dd81d0ea..cf6eefd61d7 100644 --- a/src/test/incremental/change_private_impl_method/struct_point.rs +++ b/src/test/incremental/change_private_impl_method/struct_point.rs @@ -20,7 +20,7 @@ #![allow(dead_code)] #![crate_type = "rlib"] -#![rustc_partition_translated(module="struct_point-point", cfg="cfail2")] +#![rustc_partition_codegened(module="struct_point-point", cfg="cfail2")] #![rustc_partition_reused(module="struct_point-fn_calls_methods_in_same_impl", cfg="cfail2")] #![rustc_partition_reused(module="struct_point-fn_calls_methods_in_another_impl", cfg="cfail2")] diff --git a/src/test/incremental/change_pub_inherent_method_body/struct_point.rs b/src/test/incremental/change_pub_inherent_method_body/struct_point.rs index 412fe72e4e4..a204fe27da6 100644 --- a/src/test/incremental/change_pub_inherent_method_body/struct_point.rs +++ b/src/test/incremental/change_pub_inherent_method_body/struct_point.rs @@ -19,7 +19,7 @@ #![feature(stmt_expr_attributes)] #![allow(dead_code)] -#![rustc_partition_translated(module="struct_point-point", cfg="cfail2")] +#![rustc_partition_codegened(module="struct_point-point", cfg="cfail2")] #![rustc_partition_reused(module="struct_point-fn_calls_changed_method", cfg="cfail2")] #![rustc_partition_reused(module="struct_point-fn_calls_another_method", cfg="cfail2")] diff --git a/src/test/incremental/change_pub_inherent_method_sig/struct_point.rs b/src/test/incremental/change_pub_inherent_method_sig/struct_point.rs index c82f4645caf..76c9dfce93d 100644 --- a/src/test/incremental/change_pub_inherent_method_sig/struct_point.rs +++ b/src/test/incremental/change_pub_inherent_method_sig/struct_point.rs @@ -19,9 +19,9 @@ #![feature(stmt_expr_attributes)] #![allow(dead_code)] -// These are expected to require translation. -#![rustc_partition_translated(module="struct_point-point", cfg="cfail2")] -#![rustc_partition_translated(module="struct_point-fn_calls_changed_method", cfg="cfail2")] +// These are expected to require codegen. +#![rustc_partition_codegened(module="struct_point-point", cfg="cfail2")] +#![rustc_partition_codegened(module="struct_point-fn_calls_changed_method", cfg="cfail2")] #![rustc_partition_reused(module="struct_point-fn_calls_another_method", cfg="cfail2")] #![rustc_partition_reused(module="struct_point-fn_make_struct", cfg="cfail2")] diff --git a/src/test/incremental/change_symbol_export_status.rs b/src/test/incremental/change_symbol_export_status.rs index ab91a941a16..feb8026f501 100644 --- a/src/test/incremental/change_symbol_export_status.rs +++ b/src/test/incremental/change_symbol_export_status.rs @@ -14,7 +14,7 @@ #![feature(rustc_attrs)] #![allow(private_no_mangle_fns)] -#![rustc_partition_translated(module="change_symbol_export_status-mod1", cfg="rpass2")] +#![rustc_partition_codegened(module="change_symbol_export_status-mod1", cfg="rpass2")] #![rustc_partition_reused(module="change_symbol_export_status-mod2", cfg="rpass2")] // This test case makes sure that a change in symbol visibility is detected by diff --git a/src/test/incremental/commandline-args.rs b/src/test/incremental/commandline-args.rs index e29f2ec2a13..bb2f385e9c5 100644 --- a/src/test/incremental/commandline-args.rs +++ b/src/test/incremental/commandline-args.rs @@ -16,7 +16,7 @@ #![feature(rustc_attrs)] -#![rustc_partition_translated(module="commandline_args", cfg="rpass2")] +#![rustc_partition_codegened(module="commandline_args", cfg="rpass2")] #![rustc_partition_reused(module="commandline_args", cfg="rpass3")] // Between revisions 1 and 2, we are changing the debuginfo-level, which should diff --git a/src/test/incremental/inlined_hir_34991/main.rs b/src/test/incremental/inlined_hir_34991/main.rs index a150a8c4df7..baf4ae73932 100644 --- a/src/test/incremental/inlined_hir_34991/main.rs +++ b/src/test/incremental/inlined_hir_34991/main.rs @@ -10,7 +10,7 @@ // Regression test for #34991: an ICE occurred here because we inline // some of the vector routines and give them a local def-id `X`. This -// got hashed after trans (`Hir(X)`). When we load back up, we get an +// got hashed after codegen (`Hir(X)`). When we load back up, we get an // error because the `X` is remapped to the original def-id (in // libstd), and we can't hash a HIR node from std. diff --git a/src/test/incremental/issue-38222.rs b/src/test/incremental/issue-38222.rs index f890672aa8f..a19de6ef636 100644 --- a/src/test/incremental/issue-38222.rs +++ b/src/test/incremental/issue-38222.rs @@ -20,7 +20,7 @@ #![rustc_partition_reused(module="issue_38222-mod1", cfg="rpass2")] -// If trans had added a dependency edge to the Krate dep-node, nothing would +// If codegen had added a dependency edge to the Krate dep-node, nothing would // be re-used, so checking that this module was re-used is sufficient. #![rustc_partition_reused(module="issue_38222", cfg="rpass2")] diff --git a/src/test/incremental/issue-49595/issue_49595.rs b/src/test/incremental/issue-49595/issue_49595.rs index a5b0101c68f..dfa92d425f4 100644 --- a/src/test/incremental/issue-49595/issue_49595.rs +++ b/src/test/incremental/issue-49595/issue_49595.rs @@ -15,8 +15,8 @@ #![feature(rustc_attrs)] #![crate_type = "rlib"] -#![rustc_partition_translated(module="issue_49595-tests", cfg="cfail2")] -#![rustc_partition_translated(module="issue_49595-lit_test", cfg="cfail3")] +#![rustc_partition_codegened(module="issue_49595-tests", cfg="cfail2")] +#![rustc_partition_codegened(module="issue_49595-lit_test", cfg="cfail3")] mod tests { #[cfg_attr(not(cfail1), ignore)] diff --git a/src/test/incremental/remapped_paths_cc/main.rs b/src/test/incremental/remapped_paths_cc/main.rs index ce7f5792cea..cd66310dafe 100644 --- a/src/test/incremental/remapped_paths_cc/main.rs +++ b/src/test/incremental/remapped_paths_cc/main.rs @@ -20,7 +20,7 @@ #![rustc_partition_reused(module="main", cfg="rpass2")] #![rustc_partition_reused(module="main-some_mod", cfg="rpass2")] #![rustc_partition_reused(module="main", cfg="rpass3")] -#![rustc_partition_translated(module="main-some_mod", cfg="rpass3")] +#![rustc_partition_codegened(module="main-some_mod", cfg="rpass3")] extern crate extern_crate; diff --git a/src/test/incremental/spike-neg1.rs b/src/test/incremental/spike-neg1.rs index b00c68a184e..d4701b9a66e 100644 --- a/src/test/incremental/spike-neg1.rs +++ b/src/test/incremental/spike-neg1.rs @@ -9,7 +9,7 @@ // except according to those terms. // A variant of the first "spike" test that serves to test the -// `rustc_partition_reused` and `rustc_partition_translated` tests. +// `rustc_partition_reused` and `rustc_partition_codegened` tests. // Here we change and say that the `x` module will be reused (when in // fact it will not), and then indicate that the test itself // should-fail (because an error will be reported, and hence the diff --git a/src/test/incremental/spike-neg2.rs b/src/test/incremental/spike-neg2.rs index 472d11d7f90..da79237b1a6 100644 --- a/src/test/incremental/spike-neg2.rs +++ b/src/test/incremental/spike-neg2.rs @@ -9,8 +9,8 @@ // except according to those terms. // A variant of the first "spike" test that serves to test the -// `rustc_partition_reused` and `rustc_partition_translated` tests. -// Here we change and say that the `y` module will be translated (when +// `rustc_partition_reused` and `rustc_partition_codegened` tests. +// Here we change and say that the `y` module will be codegened (when // in fact it will not), and then indicate that the test itself // should-fail (because an error will be reported, and hence the // revision rpass2 will not compile, despite being named rpass). @@ -21,8 +21,8 @@ #![feature(rustc_attrs)] #![rustc_partition_reused(module="spike_neg2", cfg="rpass2")] -#![rustc_partition_translated(module="spike_neg2-x", cfg="rpass2")] -#![rustc_partition_translated(module="spike_neg2-y", cfg="rpass2")] // this is wrong! +#![rustc_partition_codegened(module="spike_neg2-x", cfg="rpass2")] +#![rustc_partition_codegened(module="spike_neg2-y", cfg="rpass2")] // this is wrong! mod x { pub struct X { diff --git a/src/test/incremental/spike.rs b/src/test/incremental/spike.rs index a820471b7d5..1756511dd37 100644 --- a/src/test/incremental/spike.rs +++ b/src/test/incremental/spike.rs @@ -18,7 +18,7 @@ #![feature(rustc_attrs)] #![rustc_partition_reused(module="spike", cfg="rpass2")] -#![rustc_partition_translated(module="spike-x", cfg="rpass2")] +#![rustc_partition_codegened(module="spike-x", cfg="rpass2")] #![rustc_partition_reused(module="spike-y", cfg="rpass2")] mod x { diff --git a/src/test/run-fail/mir_codegen_calls_converging_drops.rs b/src/test/run-fail/mir_codegen_calls_converging_drops.rs new file mode 100644 index 00000000000..9c851eb7346 --- /dev/null +++ b/src/test/run-fail/mir_codegen_calls_converging_drops.rs @@ -0,0 +1,34 @@ +// 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:converging_fn called +// error-pattern:0 dropped +// error-pattern:exit + +struct Droppable(u8); +impl Drop for Droppable { + fn drop(&mut self) { + eprintln!("{} dropped", self.0); + } +} + +fn converging_fn() { + eprintln!("converging_fn called"); +} + +fn mir(d: Droppable) { + converging_fn(); +} + +fn main() { + let d = Droppable(0); + mir(d); + panic!("exit"); +} diff --git a/src/test/run-fail/mir_codegen_calls_converging_drops_2.rs b/src/test/run-fail/mir_codegen_calls_converging_drops_2.rs new file mode 100644 index 00000000000..6f105211556 --- /dev/null +++ b/src/test/run-fail/mir_codegen_calls_converging_drops_2.rs @@ -0,0 +1,38 @@ +// 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:complex called +// error-pattern:dropped +// error-pattern:exit + +struct Droppable; +impl Drop for Droppable { + fn drop(&mut self) { + eprintln!("dropped"); + } +} + +// return value of this function is copied into the return slot +fn complex() -> u64 { + eprintln!("complex called"); + 42 +} + + +fn mir() -> u64 { + let x = Droppable; + return complex(); + drop(x); +} + +pub fn main() { + assert_eq!(mir(), 42); + panic!("exit"); +} diff --git a/src/test/run-fail/mir_codegen_calls_diverging.rs b/src/test/run-fail/mir_codegen_calls_diverging.rs new file mode 100644 index 00000000000..9dbf7de0d2d --- /dev/null +++ b/src/test/run-fail/mir_codegen_calls_diverging.rs @@ -0,0 +1,23 @@ +// 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:diverging_fn called + +fn diverging_fn() -> ! { + panic!("diverging_fn called") +} + +fn mir() { + diverging_fn(); +} + +fn main() { + mir(); +} diff --git a/src/test/run-fail/mir_codegen_calls_diverging_drops.rs b/src/test/run-fail/mir_codegen_calls_diverging_drops.rs new file mode 100644 index 00000000000..f8fbe8f79cc --- /dev/null +++ b/src/test/run-fail/mir_codegen_calls_diverging_drops.rs @@ -0,0 +1,32 @@ +// 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:diverging_fn called +// error-pattern:0 dropped + +struct Droppable(u8); +impl Drop for Droppable { + fn drop(&mut self) { + eprintln!("{} dropped", self.0); + } +} + +fn diverging_fn() -> ! { + panic!("diverging_fn called") +} + +fn mir(d: Droppable) { + diverging_fn(); +} + +fn main() { + let d = Droppable(0); + mir(d); +} diff --git a/src/test/run-fail/mir_codegen_no_landing_pads.rs b/src/test/run-fail/mir_codegen_no_landing_pads.rs new file mode 100644 index 00000000000..aded2739b10 --- /dev/null +++ b/src/test/run-fail/mir_codegen_no_landing_pads.rs @@ -0,0 +1,37 @@ +// 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. + +// compile-flags: -Z no-landing-pads -C codegen-units=1 +// error-pattern:converging_fn called +// ignore-cloudabi no std::process + +use std::io::{self, Write}; + +struct Droppable; +impl Drop for Droppable { + fn drop(&mut self) { + ::std::process::exit(1) + } +} + +fn converging_fn() { + panic!("converging_fn called") +} + +fn mir(d: Droppable) { + let x = Droppable; + converging_fn(); + drop(x); + drop(d); +} + +fn main() { + mir(Droppable); +} diff --git a/src/test/run-fail/mir_codegen_no_landing_pads_diverging.rs b/src/test/run-fail/mir_codegen_no_landing_pads_diverging.rs new file mode 100644 index 00000000000..d3a8613bbc4 --- /dev/null +++ b/src/test/run-fail/mir_codegen_no_landing_pads_diverging.rs @@ -0,0 +1,37 @@ +// 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. + +// compile-flags: -Z no-landing-pads -C codegen-units=1 +// error-pattern:diverging_fn called +// ignore-cloudabi no std::process + +use std::io::{self, Write}; + +struct Droppable; +impl Drop for Droppable { + fn drop(&mut self) { + ::std::process::exit(1) + } +} + +fn diverging_fn() -> ! { + panic!("diverging_fn called") +} + +fn mir(d: Droppable) { + let x = Droppable; + diverging_fn(); + drop(x); + drop(d); +} + +fn main() { + mir(Droppable); +} diff --git a/src/test/run-fail/mir_trans_calls_converging_drops.rs b/src/test/run-fail/mir_trans_calls_converging_drops.rs deleted file mode 100644 index 9c851eb7346..00000000000 --- a/src/test/run-fail/mir_trans_calls_converging_drops.rs +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright 2015 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -// error-pattern:converging_fn called -// error-pattern:0 dropped -// error-pattern:exit - -struct Droppable(u8); -impl Drop for Droppable { - fn drop(&mut self) { - eprintln!("{} dropped", self.0); - } -} - -fn converging_fn() { - eprintln!("converging_fn called"); -} - -fn mir(d: Droppable) { - converging_fn(); -} - -fn main() { - let d = Droppable(0); - mir(d); - panic!("exit"); -} diff --git a/src/test/run-fail/mir_trans_calls_converging_drops_2.rs b/src/test/run-fail/mir_trans_calls_converging_drops_2.rs deleted file mode 100644 index 6f105211556..00000000000 --- a/src/test/run-fail/mir_trans_calls_converging_drops_2.rs +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright 2015 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -// error-pattern:complex called -// error-pattern:dropped -// error-pattern:exit - -struct Droppable; -impl Drop for Droppable { - fn drop(&mut self) { - eprintln!("dropped"); - } -} - -// return value of this function is copied into the return slot -fn complex() -> u64 { - eprintln!("complex called"); - 42 -} - - -fn mir() -> u64 { - let x = Droppable; - return complex(); - drop(x); -} - -pub fn main() { - assert_eq!(mir(), 42); - panic!("exit"); -} diff --git a/src/test/run-fail/mir_trans_calls_diverging.rs b/src/test/run-fail/mir_trans_calls_diverging.rs deleted file mode 100644 index 9dbf7de0d2d..00000000000 --- a/src/test/run-fail/mir_trans_calls_diverging.rs +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright 2015 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -// error-pattern:diverging_fn called - -fn diverging_fn() -> ! { - panic!("diverging_fn called") -} - -fn mir() { - diverging_fn(); -} - -fn main() { - mir(); -} diff --git a/src/test/run-fail/mir_trans_calls_diverging_drops.rs b/src/test/run-fail/mir_trans_calls_diverging_drops.rs deleted file mode 100644 index f8fbe8f79cc..00000000000 --- a/src/test/run-fail/mir_trans_calls_diverging_drops.rs +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright 2015 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -// error-pattern:diverging_fn called -// error-pattern:0 dropped - -struct Droppable(u8); -impl Drop for Droppable { - fn drop(&mut self) { - eprintln!("{} dropped", self.0); - } -} - -fn diverging_fn() -> ! { - panic!("diverging_fn called") -} - -fn mir(d: Droppable) { - diverging_fn(); -} - -fn main() { - let d = Droppable(0); - mir(d); -} diff --git a/src/test/run-fail/mir_trans_no_landing_pads.rs b/src/test/run-fail/mir_trans_no_landing_pads.rs deleted file mode 100644 index aded2739b10..00000000000 --- a/src/test/run-fail/mir_trans_no_landing_pads.rs +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright 2015 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -// compile-flags: -Z no-landing-pads -C codegen-units=1 -// error-pattern:converging_fn called -// ignore-cloudabi no std::process - -use std::io::{self, Write}; - -struct Droppable; -impl Drop for Droppable { - fn drop(&mut self) { - ::std::process::exit(1) - } -} - -fn converging_fn() { - panic!("converging_fn called") -} - -fn mir(d: Droppable) { - let x = Droppable; - converging_fn(); - drop(x); - drop(d); -} - -fn main() { - mir(Droppable); -} diff --git a/src/test/run-fail/mir_trans_no_landing_pads_diverging.rs b/src/test/run-fail/mir_trans_no_landing_pads_diverging.rs deleted file mode 100644 index d3a8613bbc4..00000000000 --- a/src/test/run-fail/mir_trans_no_landing_pads_diverging.rs +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright 2015 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -// compile-flags: -Z no-landing-pads -C codegen-units=1 -// error-pattern:diverging_fn called -// ignore-cloudabi no std::process - -use std::io::{self, Write}; - -struct Droppable; -impl Drop for Droppable { - fn drop(&mut self) { - ::std::process::exit(1) - } -} - -fn diverging_fn() -> ! { - panic!("diverging_fn called") -} - -fn mir(d: Droppable) { - let x = Droppable; - diverging_fn(); - drop(x); - drop(d); -} - -fn main() { - mir(Droppable); -} diff --git a/src/test/run-fail/rhs-type.rs b/src/test/run-fail/rhs-type.rs index e16ce9c8edb..572b56d02ae 100644 --- a/src/test/run-fail/rhs-type.rs +++ b/src/test/run-fail/rhs-type.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// Tests that trans treats the rhs of pth's decl +// Tests that codegen treats the rhs of pth's decl // as a _|_-typed thing, not a str-typed thing // error-pattern:bye diff --git a/src/test/run-make-fulldeps/hotplug_codegen_backend/the_backend.rs b/src/test/run-make-fulldeps/hotplug_codegen_backend/the_backend.rs index e266b0f5e83..251fb78a985 100644 --- a/src/test/run-make-fulldeps/hotplug_codegen_backend/the_backend.rs +++ b/src/test/run-make-fulldeps/hotplug_codegen_backend/the_backend.rs @@ -12,7 +12,7 @@ extern crate syntax; extern crate rustc; -extern crate rustc_trans_utils; +extern crate rustc_codegen_utils; use std::any::Any; use std::sync::mpsc; @@ -23,11 +23,11 @@ use rustc::ty::TyCtxt; use rustc::ty::maps::Providers; use rustc::middle::cstore::MetadataLoader; use rustc::dep_graph::DepGraph; -use rustc_trans_utils::trans_crate::{TransCrate, MetadataOnlyTransCrate}; +use rustc_codegen_utils::codegen_backend::{CodegenBackend, MetadataOnlyCodegenBackend}; -struct TheBackend(Box); +struct TheBackend(Box); -impl TransCrate for TheBackend { +impl CodegenBackend for TheBackend { fn metadata_loader(&self) -> Box { self.0.metadata_loader() } @@ -40,7 +40,7 @@ impl TransCrate for TheBackend { self.0.provide_extern(providers); } - fn trans_crate<'a, 'tcx>( + fn codegen_crate<'a, 'tcx>( &self, tcx: TyCtxt<'a, 'tcx, 'tcx>, _rx: mpsc::Receiver> @@ -50,18 +50,18 @@ impl TransCrate for TheBackend { Box::new(tcx.crate_name(LOCAL_CRATE) as Symbol) } - fn join_trans_and_link( + fn join_codegen_and_link( &self, - trans: Box, + ongoing_codegen: Box, sess: &Session, _dep_graph: &DepGraph, outputs: &OutputFilenames, ) -> Result<(), CompileIncomplete> { use std::io::Write; use rustc::session::config::CrateType; - use rustc_trans_utils::link::out_filename; - let crate_name = trans.downcast::() - .expect("in join_trans_and_link: trans is not a Symbol"); + use rustc_codegen_utils::link::out_filename; + let crate_name = ongoing_codegen.downcast::() + .expect("in join_codegen_and_link: ongoing_codegen is not a Symbol"); for &crate_type in sess.opts.crate_types.iter() { if crate_type != CrateType::CrateTypeExecutable { sess.fatal(&format!("Crate type is {:?}", crate_type)); @@ -75,8 +75,8 @@ impl TransCrate for TheBackend { } } -/// This is the entrypoint for a hot plugged rustc_trans +/// This is the entrypoint for a hot plugged rustc_codegen_llvm #[no_mangle] -pub fn __rustc_codegen_backend() -> Box { - Box::new(TheBackend(MetadataOnlyTransCrate::new())) +pub fn __rustc_codegen_backend() -> Box { + Box::new(TheBackend(MetadataOnlyCodegenBackend::new())) } diff --git a/src/test/run-make-fulldeps/issue-19371/foo.rs b/src/test/run-make-fulldeps/issue-19371/foo.rs index e0db2627d85..403f4f79843 100644 --- a/src/test/run-make-fulldeps/issue-19371/foo.rs +++ b/src/test/run-make-fulldeps/issue-19371/foo.rs @@ -15,7 +15,7 @@ extern crate rustc_driver; extern crate rustc_lint; extern crate rustc_metadata; extern crate rustc_errors; -extern crate rustc_trans_utils; +extern crate rustc_codegen_utils; extern crate syntax; use rustc::session::{build_session, Session}; @@ -25,7 +25,7 @@ use rustc_driver::driver::{compile_input, CompileController}; use rustc_metadata::cstore::CStore; use rustc_errors::registry::Registry; use syntax::codemap::FileName; -use rustc_trans_utils::trans_crate::TransCrate; +use rustc_codegen_utils::codegen_backend::CodegenBackend; use std::path::PathBuf; use std::rc::Rc; @@ -52,7 +52,7 @@ fn main() { compile(src.to_string(), tmpdir.join("out"), sysroot.clone()); } -fn basic_sess(sysroot: PathBuf) -> (Session, Rc, Box) { +fn basic_sess(sysroot: PathBuf) -> (Session, Rc, Box) { let mut opts = basic_options(); opts.output_types = OutputTypes::new(&[(OutputType::Exe, None)]); opts.maybe_sysroot = Some(sysroot); @@ -62,19 +62,19 @@ fn basic_sess(sysroot: PathBuf) -> (Session, Rc, Box) { let descriptions = Registry::new(&rustc::DIAGNOSTICS); let sess = build_session(opts, None, descriptions); - let trans = rustc_driver::get_trans(&sess); - let cstore = Rc::new(CStore::new(trans.metadata_loader())); + let codegen_backend = rustc_driver::get_codegen_backend(&sess); + let cstore = Rc::new(CStore::new(codegen_backend.metadata_loader())); rustc_lint::register_builtins(&mut sess.lint_store.borrow_mut(), Some(&sess)); - (sess, cstore, trans) + (sess, cstore, codegen_backend) } fn compile(code: String, output: PathBuf, sysroot: PathBuf) { syntax::with_globals(|| { - let (sess, cstore, trans) = basic_sess(sysroot); + let (sess, cstore, codegen_backend) = basic_sess(sysroot); let control = CompileController::basic(); let input = Input::Str { name: FileName::Anon, input: code }; let _ = compile_input( - trans, + codegen_backend, &sess, &cstore, &None, diff --git a/src/test/run-make-fulldeps/issue-7349/Makefile b/src/test/run-make-fulldeps/issue-7349/Makefile index 50dc63b1deb..9658b99e3b0 100644 --- a/src/test/run-make-fulldeps/issue-7349/Makefile +++ b/src/test/run-make-fulldeps/issue-7349/Makefile @@ -1,7 +1,7 @@ -include ../tools.mk # Test to make sure that inner functions within a polymorphic outer function -# don't get re-translated when the outer function is monomorphized. The test +# don't get re-codegened when the outer function is monomorphized. The test # code monomorphizes the outer functions several times, but the magic constants # used in the inner functions should each appear only once in the generated IR. diff --git a/src/test/run-pass-fulldeps/compiler-calls.rs b/src/test/run-pass-fulldeps/compiler-calls.rs index 85aa92ce260..9aa4f42c8ac 100644 --- a/src/test/run-pass-fulldeps/compiler-calls.rs +++ b/src/test/run-pass-fulldeps/compiler-calls.rs @@ -18,7 +18,7 @@ extern crate getopts; extern crate rustc; extern crate rustc_driver; -extern crate rustc_trans_utils; +extern crate rustc_codegen_utils; extern crate syntax; extern crate rustc_errors as errors; @@ -26,7 +26,7 @@ use rustc::middle::cstore::CrateStore; use rustc::session::Session; use rustc::session::config::{self, Input}; use rustc_driver::{driver, CompilerCalls, Compilation}; -use rustc_trans_utils::trans_crate::TransCrate; +use rustc_codegen_utils::codegen_backend::CodegenBackend; use syntax::ast; use std::path::PathBuf; @@ -48,7 +48,7 @@ impl<'a> CompilerCalls<'a> for TestCalls { } fn late_callback(&mut self, - _: &TransCrate, + _: &CodegenBackend, _: &getopts::Matches, _: &Session, _: &CrateStore, diff --git a/src/test/run-pass/associated-types-region-erasure-issue-20582.rs b/src/test/run-pass/associated-types-region-erasure-issue-20582.rs index 16e49f146ab..40f352e2e1f 100644 --- a/src/test/run-pass/associated-types-region-erasure-issue-20582.rs +++ b/src/test/run-pass/associated-types-region-erasure-issue-20582.rs @@ -9,7 +9,7 @@ // except according to those terms. // Regression test for #20582. This test caused an ICE related to -// inconsistent region erasure in trans. +// inconsistent region erasure in codegen. // pretty-expanded FIXME #23616 diff --git a/src/test/run-pass/atomic-compare_exchange.rs b/src/test/run-pass/atomic-compare_exchange.rs index 1d9fa248e3d..2f33eb9ca40 100644 --- a/src/test/run-pass/atomic-compare_exchange.rs +++ b/src/test/run-pass/atomic-compare_exchange.rs @@ -15,7 +15,7 @@ use std::sync::atomic::Ordering::*; static ATOMIC: AtomicIsize = ATOMIC_ISIZE_INIT; fn main() { - // Make sure trans can emit all the intrinsics correctly + // Make sure codegen can emit all the intrinsics correctly ATOMIC.compare_exchange(0, 1, Relaxed, Relaxed).ok(); ATOMIC.compare_exchange(0, 1, Acquire, Relaxed).ok(); ATOMIC.compare_exchange(0, 1, Release, Relaxed).ok(); diff --git a/src/test/run-pass/codegen-object-shim.rs b/src/test/run-pass/codegen-object-shim.rs new file mode 100644 index 00000000000..5fbfef05e10 --- /dev/null +++ b/src/test/run-pass/codegen-object-shim.rs @@ -0,0 +1,14 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +fn main() { + assert_eq!((ToString::to_string as fn(&(ToString+'static)) -> String)(&"foo"), + String::from("foo")); +} diff --git a/src/test/run-pass/codegen-tag-static-padding.rs b/src/test/run-pass/codegen-tag-static-padding.rs new file mode 100644 index 00000000000..ba01d51dc6a --- /dev/null +++ b/src/test/run-pass/codegen-tag-static-padding.rs @@ -0,0 +1,67 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + + +// Issue #13186 + +// For simplicity of explanations assuming code is compiled for x86_64 +// Linux ABI. + +// Size of TestOption is 16, and alignment of TestOption is 8. +// Size of u8 is 1, and alignment of u8 is 1. +// So size of Request is 24, and alignment of Request must be 8: +// the maximum alignment of its fields. +// Last 7 bytes of Request struct are not occupied by any fields. + + + +enum TestOption { + TestNone, + TestSome(T), +} + +pub struct Request { + foo: TestOption, + bar: u8, +} + +fn default_instance() -> &'static Request { + static instance: Request = Request { + // LLVM does not allow to specify alignment of expressions, thus + // alignment of `foo` in constant is 1, not 8. + foo: TestOption::TestNone, + bar: 17, + // Space after last field is not occupied by any data, but it is + // reserved to make struct aligned properly. If compiler does + // not insert padding after last field when emitting constant, + // size of struct may be not equal to size of struct, and + // compiler crashes in internal assertion check. + }; + &instance +} + +fn non_default_instance() -> &'static Request { + static instance: Request = Request { + foo: TestOption::TestSome(0x1020304050607080), + bar: 19, + }; + &instance +} + +pub fn main() { + match default_instance() { + &Request { foo: TestOption::TestNone, bar: 17 } => {}, + _ => panic!(), + }; + match non_default_instance() { + &Request { foo: TestOption::TestSome(0x1020304050607080), bar: 19 } => {}, + _ => panic!(), + }; +} diff --git a/src/test/run-pass/compiletest-skip-codegen.rs b/src/test/run-pass/compiletest-skip-codegen.rs new file mode 100644 index 00000000000..d318b8fa44b --- /dev/null +++ b/src/test/run-pass/compiletest-skip-codegen.rs @@ -0,0 +1,17 @@ +// Copyright 2018 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. + +// Test that with the `skip-codegen` option the test isn't executed. + +// skip-codegen + +fn main() { + unreachable!(); +} diff --git a/src/test/run-pass/compiletest-skip-trans.rs b/src/test/run-pass/compiletest-skip-trans.rs deleted file mode 100644 index d24a6506c2c..00000000000 --- a/src/test/run-pass/compiletest-skip-trans.rs +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright 2018 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. - -// Test that with the `skip-trans` option the test isn't executed. - -// skip-trans - -fn main() { - unreachable!(); -} diff --git a/src/test/run-pass/conditional-compile.rs b/src/test/run-pass/conditional-compile.rs index c8e9cbdae1e..01bdcfeaefb 100644 --- a/src/test/run-pass/conditional-compile.rs +++ b/src/test/run-pass/conditional-compile.rs @@ -22,7 +22,7 @@ mod rustrt { #[cfg(bogus)] extern { // This symbol doesn't exist and would be a link error if this - // module was translated + // module was codegened pub fn bogus(); } diff --git a/src/test/run-pass/issue-18425.rs b/src/test/run-pass/issue-18425.rs index eb7e504ae14..797b3197182 100644 --- a/src/test/run-pass/issue-18425.rs +++ b/src/test/run-pass/issue-18425.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// Check that trans doesn't ICE when translating an array repeat +// Check that codegen doesn't ICE when codegenning an array repeat // expression with a count of 1 and a non-Copy element type. // pretty-expanded FIXME #23616 diff --git a/src/test/run-pass/issue-18514.rs b/src/test/run-pass/issue-18514.rs index 2a1e55d867f..f8bebb4a40b 100644 --- a/src/test/run-pass/issue-18514.rs +++ b/src/test/run-pass/issue-18514.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// Test that we don't ICE when translating a generic impl method from +// Test that we don't ICE when codegenning a generic impl method from // an extern crate that contains a match expression on a local // variable place where one of the match case bodies contains an // expression that autoderefs through an overloaded generic deref diff --git a/src/test/run-pass/issue-18652.rs b/src/test/run-pass/issue-18652.rs index cea0beaf5f0..1eddc34f5b1 100644 --- a/src/test/run-pass/issue-18652.rs +++ b/src/test/run-pass/issue-18652.rs @@ -9,7 +9,7 @@ // except according to those terms. // Tests multiple free variables being passed by value into an unboxed -// once closure as an optimization by trans. This used to hit an +// once closure as an optimization by codegen. This used to hit an // incorrect assert. fn main() { diff --git a/src/test/run-pass/issue-18661.rs b/src/test/run-pass/issue-18661.rs index 48d29095ec6..4287f449c6f 100644 --- a/src/test/run-pass/issue-18661.rs +++ b/src/test/run-pass/issue-18661.rs @@ -9,7 +9,7 @@ // except according to those terms. // Test that param substitutions from the correct environment are -// used when translating unboxed closure calls. +// used when codegenning unboxed closure calls. // pretty-expanded FIXME #23616 diff --git a/src/test/run-pass/issue-20644.rs b/src/test/run-pass/issue-20644.rs index 7cacc2e1146..65a67d0b41a 100644 --- a/src/test/run-pass/issue-20644.rs +++ b/src/test/run-pass/issue-20644.rs @@ -9,7 +9,7 @@ // except according to those terms. // A reduced version of the rustbook ice. The problem this encountered -// had to do with trans ignoring binders. +// had to do with codegen ignoring binders. // pretty-expanded FIXME #23616 // ignore-cloudabi no std::fs diff --git a/src/test/run-pass/issue-24085.rs b/src/test/run-pass/issue-24085.rs index fde32cb9023..b15ec2986c2 100644 --- a/src/test/run-pass/issue-24085.rs +++ b/src/test/run-pass/issue-24085.rs @@ -10,7 +10,7 @@ // Regression test for #24085. Errors were occurring in region // inference due to the requirement that `'a:b'`, which was getting -// incorrectly translated in connection with the closure below. +// incorrectly codegened in connection with the closure below. #[derive(Copy,Clone)] struct Path<'a:'b, 'b> { diff --git a/src/test/run-pass/issue-34569.rs b/src/test/run-pass/issue-34569.rs index 41d02e96cc2..5c7c5a2b3b9 100644 --- a/src/test/run-pass/issue-34569.rs +++ b/src/test/run-pass/issue-34569.rs @@ -12,7 +12,7 @@ // In this test we just want to make sure that the code below does not lead to // a debuginfo verification assertion during compilation. This was caused by the -// closure in the guard being translated twice due to how match expressions are +// closure in the guard being codegened twice due to how match expressions are // handled. // // See https://github.com/rust-lang/rust/issues/34569 for details. diff --git a/src/test/run-pass/issue-36381.rs b/src/test/run-pass/issue-36381.rs index 6cd991bd942..2694c98dd91 100644 --- a/src/test/run-pass/issue-36381.rs +++ b/src/test/run-pass/issue-36381.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// Regression test for #36381. The trans collector was asserting that +// Regression test for #36381. The monomorphization collector was asserting that // there are no projection types, but the `<&str as // StreamOnce>::Position` projection contained a late-bound region, // and we don't currently normalize in that case until the function is diff --git a/src/test/run-pass/issue-38002.rs b/src/test/run-pass/issue-38002.rs index dd6ccec973f..4eb381b9eac 100644 --- a/src/test/run-pass/issue-38002.rs +++ b/src/test/run-pass/issue-38002.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// Check that constant ADTs are translated OK, part k of N. +// Check that constant ADTs are codegened OK, part k of N. enum Bar { C diff --git a/src/test/run-pass/issue-5243.rs b/src/test/run-pass/issue-5243.rs index eda0eea7071..9bd13c4d3a0 100644 --- a/src/test/run-pass/issue-5243.rs +++ b/src/test/run-pass/issue-5243.rs @@ -9,7 +9,7 @@ // except according to those terms. // Check that merely having lifetime parameters is not -// enough for trans to consider this as non-monomorphic, +// enough for codegen to consider this as non-monomorphic, // which led to various assertions and failures in turn. // pretty-expanded FIXME #23616 diff --git a/src/test/run-pass/issue24687-embed-debuginfo/main.rs b/src/test/run-pass/issue24687-embed-debuginfo/main.rs index 7754e9c3ad7..abec252c74e 100644 --- a/src/test/run-pass/issue24687-embed-debuginfo/main.rs +++ b/src/test/run-pass/issue24687-embed-debuginfo/main.rs @@ -14,7 +14,7 @@ extern crate issue24687_lib as d; fn main() { - // Create a d, which has a destructor whose body will be trans'ed + // Create a d, which has a destructor whose body will be codegen'ed // into the generated code here, and thus the local debuginfo will // need references into the original source locations from // `importer` above. diff --git a/src/test/run-pass/method-two-trait-defer-resolution-2.rs b/src/test/run-pass/method-two-trait-defer-resolution-2.rs index cf9bc9bb56a..82d747b6c27 100644 --- a/src/test/run-pass/method-two-trait-defer-resolution-2.rs +++ b/src/test/run-pass/method-two-trait-defer-resolution-2.rs @@ -16,7 +16,7 @@ // whether `_1: MyCopy` or `_1 == Box`. However (and this is the // point of the test), we don't have to pick between the two impls -- // it is enough to know that `foo` comes from the `Foo` trait. We can -// translate the call as `Foo::foo(&x)` and let the specific impl get +// codegen the call as `Foo::foo(&x)` and let the specific impl get // chosen later. diff --git a/src/test/run-pass/mir_codegen_array.rs b/src/test/run-pass/mir_codegen_array.rs new file mode 100644 index 00000000000..b7f247012ce --- /dev/null +++ b/src/test/run-pass/mir_codegen_array.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. + +fn into_inner() -> [u64; 1024] { + let mut x = 10 + 20; + [x; 1024] +} + +fn main(){ + let x: &[u64] = &[30; 1024]; + assert_eq!(&into_inner()[..], x); +} diff --git a/src/test/run-pass/mir_codegen_array_2.rs b/src/test/run-pass/mir_codegen_array_2.rs new file mode 100644 index 00000000000..c7133fb0c0e --- /dev/null +++ b/src/test/run-pass/mir_codegen_array_2.rs @@ -0,0 +1,18 @@ +// 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. + +fn into_inner(x: u64) -> [u64; 1024] { + [x; 2*4*8*16] +} + +fn main(){ + let x: &[u64] = &[42; 1024]; + assert_eq!(&into_inner(42)[..], x); +} diff --git a/src/test/run-pass/mir_codegen_call_converging.rs b/src/test/run-pass/mir_codegen_call_converging.rs new file mode 100644 index 00000000000..7d420bb86c6 --- /dev/null +++ b/src/test/run-pass/mir_codegen_call_converging.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. + +fn converging_fn() -> u64 { + 43 +} + +fn mir() -> u64 { + let x; + loop { + x = converging_fn(); + break; + } + x +} + +fn main() { + assert_eq!(mir(), 43); +} diff --git a/src/test/run-pass/mir_codegen_calls.rs b/src/test/run-pass/mir_codegen_calls.rs new file mode 100644 index 00000000000..d02e3287bc3 --- /dev/null +++ b/src/test/run-pass/mir_codegen_calls.rs @@ -0,0 +1,200 @@ +// 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(fn_traits, test)] + +extern crate test; + +fn test1(a: isize, b: (i32, i32), c: &[i32]) -> (isize, (i32, i32), &[i32]) { + // Test passing a number of arguments including a fat pointer. + // Also returning via an out pointer + fn callee(a: isize, b: (i32, i32), c: &[i32]) -> (isize, (i32, i32), &[i32]) { + (a, b, c) + } + callee(a, b, c) +} + +fn test2(a: isize) -> isize { + // Test passing a single argument. + // Not using out pointer. + fn callee(a: isize) -> isize { + a + } + callee(a) +} + +#[derive(PartialEq, Eq, Debug)] +struct Foo; +impl Foo { + fn inherent_method(&self, a: isize) -> isize { a } +} + +fn test3(x: &Foo, a: isize) -> isize { + // Test calling inherent method + x.inherent_method(a) +} + +trait Bar { + fn extension_method(&self, a: isize) -> isize { a } +} +impl Bar for Foo {} + +fn test4(x: &Foo, a: isize) -> isize { + // Test calling extension method + x.extension_method(a) +} + +fn test5(x: &Bar, a: isize) -> isize { + // Test calling method on trait object + x.extension_method(a) +} + +fn test6(x: &T, a: isize) -> isize { + // Test calling extension method on generic callee + x.extension_method(a) +} + +trait One { + fn one() -> T; +} +impl One for isize { + fn one() -> isize { 1 } +} + +fn test7() -> isize { + // Test calling trait static method + ::one() +} + +struct Two; +impl Two { + fn two() -> isize { 2 } +} + +fn test8() -> isize { + // Test calling impl static method + Two::two() +} + +extern fn simple_extern(x: u32, y: (u32, u32)) -> u32 { + x + y.0 * y.1 +} + +fn test9() -> u32 { + simple_extern(41, (42, 43)) +} + +fn test_closure(f: &F, x: i32, y: i32) -> i32 + where F: Fn(i32, i32) -> i32 +{ + f(x, y) +} + +fn test_fn_object(f: &Fn(i32, i32) -> i32, x: i32, y: i32) -> i32 { + f(x, y) +} + +fn test_fn_impl(f: &&Fn(i32, i32) -> i32, x: i32, y: i32) -> i32 { + // This call goes through the Fn implementation for &Fn provided in + // core::ops::impls. It expands to a static Fn::call() that calls the + // Fn::call() implementation of the object shim underneath. + f(x, y) +} + +fn test_fn_direct_call(f: &F, x: i32, y: i32) -> i32 + where F: Fn(i32, i32) -> i32 +{ + f.call((x, y)) +} + +fn test_fn_const_call(f: &F) -> i32 + where F: Fn(i32, i32) -> i32 +{ + f.call((100, -1)) +} + +fn test_fn_nil_call(f: &F) -> i32 + where F: Fn() -> i32 +{ + f() +} + +fn test_fn_transmute_zst(x: ()) -> [(); 1] { + fn id(x: T) -> T {x} + + id(unsafe { + std::mem::transmute(x) + }) +} + +fn test_fn_ignored_pair() -> ((), ()) { + ((), ()) +} + +fn test_fn_ignored_pair_0() { + test_fn_ignored_pair().0 +} + +fn id(x: T) -> T { x } + +fn ignored_pair_named() -> (Foo, Foo) { + (Foo, Foo) +} + +fn test_fn_ignored_pair_named() -> (Foo, Foo) { + id(ignored_pair_named()) +} + +fn test_fn_nested_pair(x: &((f32, f32), u32)) -> (f32, f32) { + let y = *x; + let z = y.0; + (z.0, z.1) +} + +fn test_fn_const_arg_by_ref(mut a: [u64; 4]) -> u64 { + // Mutate the by-reference argument, which won't work with + // a non-immediate constant unless it's copied to the stack. + let a = test::black_box(&mut a); + a[0] += a[1]; + a[0] += a[2]; + a[0] += a[3]; + a[0] +} + +fn main() { + assert_eq!(test1(1, (2, 3), &[4, 5, 6]), (1, (2, 3), &[4, 5, 6][..])); + assert_eq!(test2(98), 98); + assert_eq!(test3(&Foo, 42), 42); + assert_eq!(test4(&Foo, 970), 970); + assert_eq!(test5(&Foo, 8576), 8576); + assert_eq!(test6(&Foo, 12367), 12367); + assert_eq!(test7(), 1); + assert_eq!(test8(), 2); + assert_eq!(test9(), 41 + 42 * 43); + + let r = 3; + let closure = |x: i32, y: i32| { r*(x + (y*2)) }; + assert_eq!(test_fn_const_call(&closure), 294); + assert_eq!(test_closure(&closure, 100, 1), 306); + let function_object = &closure as &Fn(i32, i32) -> i32; + assert_eq!(test_fn_object(function_object, 100, 2), 312); + assert_eq!(test_fn_impl(&function_object, 100, 3), 318); + assert_eq!(test_fn_direct_call(&closure, 100, 4), 324); + + assert_eq!(test_fn_nil_call(&(|| 42)), 42); + assert_eq!(test_fn_transmute_zst(()), [()]); + + assert_eq!(test_fn_ignored_pair_0(), ()); + assert_eq!(test_fn_ignored_pair_named(), (Foo, Foo)); + assert_eq!(test_fn_nested_pair(&((1.0, 2.0), 0)), (1.0, 2.0)); + + const ARRAY: [u64; 4] = [1, 2, 3, 4]; + assert_eq!(test_fn_const_arg_by_ref(ARRAY), 1 + 2 + 3 + 4); +} diff --git a/src/test/run-pass/mir_codegen_calls_variadic.rs b/src/test/run-pass/mir_codegen_calls_variadic.rs new file mode 100644 index 00000000000..7845c9426e2 --- /dev/null +++ b/src/test/run-pass/mir_codegen_calls_variadic.rs @@ -0,0 +1,31 @@ +// 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-wasm32-bare no libc to test ffi with + +#[link(name = "rust_test_helpers", kind = "static")] +extern { + fn rust_interesting_average(_: i64, ...) -> f64; +} + +fn test(a: i64, b: i64, c: i64, d: i64, e: i64, f: T, g: U) -> i64 { + unsafe { + rust_interesting_average(6, a, a as f64, + b, b as f64, + c, c as f64, + d, d as f64, + e, e as f64, + f, g) as i64 + } +} + +fn main(){ + assert_eq!(test(10, 20, 30, 40, 50, 60_i64, 60.0_f64), 70); +} diff --git a/src/test/run-pass/mir_codegen_critical_edge.rs b/src/test/run-pass/mir_codegen_critical_edge.rs new file mode 100644 index 00000000000..c742e71633f --- /dev/null +++ b/src/test/run-pass/mir_codegen_critical_edge.rs @@ -0,0 +1,52 @@ +// 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. + +// This code produces a CFG with critical edges that, if we don't +// handle properly, will cause invalid codegen. + +#![feature(rustc_attrs)] + +enum State { + Both, + Front, + Back +} + +pub struct Foo { + state: State, + a: A, + b: B +} + +impl Foo +where A: Iterator, B: Iterator +{ + // This is the function we care about + fn next(&mut self) -> Option { + match self.state { + State::Both => match self.a.next() { + elt @ Some(..) => elt, + None => { + self.state = State::Back; + self.b.next() + } + }, + State::Front => self.a.next(), + State::Back => self.b.next(), + } + } +} + +// Make sure we actually codegen a version of the function +pub fn do_stuff(mut f: Foo>, Box>>) { + let _x = f.next(); +} + +fn main() {} diff --git a/src/test/run-pass/mir_codegen_spike1.rs b/src/test/run-pass/mir_codegen_spike1.rs new file mode 100644 index 00000000000..27e1583af34 --- /dev/null +++ b/src/test/run-pass/mir_codegen_spike1.rs @@ -0,0 +1,21 @@ +// Copyright 2012 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. + +// A simple spike test for MIR version of codegen. + +fn sum(x: i32, y: i32) -> i32 { + x + y +} + +fn main() { + let x = sum(22, 44); + assert_eq!(x, 66); + println!("sum()={:?}", x); +} diff --git a/src/test/run-pass/mir_codegen_switch.rs b/src/test/run-pass/mir_codegen_switch.rs new file mode 100644 index 00000000000..b097bf46ad3 --- /dev/null +++ b/src/test/run-pass/mir_codegen_switch.rs @@ -0,0 +1,44 @@ +// 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. + +enum Abc { + A(u8), + B(i8), + C, + D, +} + +fn foo(x: Abc) -> i32 { + match x { + Abc::C => 3, + Abc::D => 4, + Abc::B(_) => 2, + Abc::A(_) => 1, + } +} + +fn foo2(x: Abc) -> bool { + match x { + Abc::D => true, + _ => false + } +} + +fn main() { + assert_eq!(1, foo(Abc::A(42))); + assert_eq!(2, foo(Abc::B(-100))); + assert_eq!(3, foo(Abc::C)); + assert_eq!(4, foo(Abc::D)); + + assert_eq!(false, foo2(Abc::A(1))); + assert_eq!(false, foo2(Abc::B(2))); + assert_eq!(false, foo2(Abc::C)); + assert_eq!(true, foo2(Abc::D)); +} diff --git a/src/test/run-pass/mir_codegen_switchint.rs b/src/test/run-pass/mir_codegen_switchint.rs new file mode 100644 index 00000000000..537734596a5 --- /dev/null +++ b/src/test/run-pass/mir_codegen_switchint.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. + +pub fn foo(x: i8) -> i32 { + match x { + 1 => 0, + _ => 1, + } +} + +fn main() { + assert_eq!(foo(0), 1); + assert_eq!(foo(1), 0); +} diff --git a/src/test/run-pass/mir_overflow_off.rs b/src/test/run-pass/mir_overflow_off.rs index 0db1e7b4563..a2cfca01dac 100644 --- a/src/test/run-pass/mir_overflow_off.rs +++ b/src/test/run-pass/mir_overflow_off.rs @@ -10,7 +10,7 @@ // compile-flags: -Z force-overflow-checks=off -// Test that with MIR trans, overflow checks can be +// Test that with MIR codegen, overflow checks can be // turned off, even when they're from core::ops::*. use std::ops::*; diff --git a/src/test/run-pass/mir_trans_array.rs b/src/test/run-pass/mir_trans_array.rs deleted file mode 100644 index b7f247012ce..00000000000 --- a/src/test/run-pass/mir_trans_array.rs +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright 2015 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -fn into_inner() -> [u64; 1024] { - let mut x = 10 + 20; - [x; 1024] -} - -fn main(){ - let x: &[u64] = &[30; 1024]; - assert_eq!(&into_inner()[..], x); -} diff --git a/src/test/run-pass/mir_trans_array_2.rs b/src/test/run-pass/mir_trans_array_2.rs deleted file mode 100644 index c7133fb0c0e..00000000000 --- a/src/test/run-pass/mir_trans_array_2.rs +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright 2015 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -fn into_inner(x: u64) -> [u64; 1024] { - [x; 2*4*8*16] -} - -fn main(){ - let x: &[u64] = &[42; 1024]; - assert_eq!(&into_inner(42)[..], x); -} diff --git a/src/test/run-pass/mir_trans_call_converging.rs b/src/test/run-pass/mir_trans_call_converging.rs deleted file mode 100644 index 7d420bb86c6..00000000000 --- a/src/test/run-pass/mir_trans_call_converging.rs +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright 2015 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -fn converging_fn() -> u64 { - 43 -} - -fn mir() -> u64 { - let x; - loop { - x = converging_fn(); - break; - } - x -} - -fn main() { - assert_eq!(mir(), 43); -} diff --git a/src/test/run-pass/mir_trans_calls.rs b/src/test/run-pass/mir_trans_calls.rs deleted file mode 100644 index d02e3287bc3..00000000000 --- a/src/test/run-pass/mir_trans_calls.rs +++ /dev/null @@ -1,200 +0,0 @@ -// Copyright 2015 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -#![feature(fn_traits, test)] - -extern crate test; - -fn test1(a: isize, b: (i32, i32), c: &[i32]) -> (isize, (i32, i32), &[i32]) { - // Test passing a number of arguments including a fat pointer. - // Also returning via an out pointer - fn callee(a: isize, b: (i32, i32), c: &[i32]) -> (isize, (i32, i32), &[i32]) { - (a, b, c) - } - callee(a, b, c) -} - -fn test2(a: isize) -> isize { - // Test passing a single argument. - // Not using out pointer. - fn callee(a: isize) -> isize { - a - } - callee(a) -} - -#[derive(PartialEq, Eq, Debug)] -struct Foo; -impl Foo { - fn inherent_method(&self, a: isize) -> isize { a } -} - -fn test3(x: &Foo, a: isize) -> isize { - // Test calling inherent method - x.inherent_method(a) -} - -trait Bar { - fn extension_method(&self, a: isize) -> isize { a } -} -impl Bar for Foo {} - -fn test4(x: &Foo, a: isize) -> isize { - // Test calling extension method - x.extension_method(a) -} - -fn test5(x: &Bar, a: isize) -> isize { - // Test calling method on trait object - x.extension_method(a) -} - -fn test6(x: &T, a: isize) -> isize { - // Test calling extension method on generic callee - x.extension_method(a) -} - -trait One { - fn one() -> T; -} -impl One for isize { - fn one() -> isize { 1 } -} - -fn test7() -> isize { - // Test calling trait static method - ::one() -} - -struct Two; -impl Two { - fn two() -> isize { 2 } -} - -fn test8() -> isize { - // Test calling impl static method - Two::two() -} - -extern fn simple_extern(x: u32, y: (u32, u32)) -> u32 { - x + y.0 * y.1 -} - -fn test9() -> u32 { - simple_extern(41, (42, 43)) -} - -fn test_closure(f: &F, x: i32, y: i32) -> i32 - where F: Fn(i32, i32) -> i32 -{ - f(x, y) -} - -fn test_fn_object(f: &Fn(i32, i32) -> i32, x: i32, y: i32) -> i32 { - f(x, y) -} - -fn test_fn_impl(f: &&Fn(i32, i32) -> i32, x: i32, y: i32) -> i32 { - // This call goes through the Fn implementation for &Fn provided in - // core::ops::impls. It expands to a static Fn::call() that calls the - // Fn::call() implementation of the object shim underneath. - f(x, y) -} - -fn test_fn_direct_call(f: &F, x: i32, y: i32) -> i32 - where F: Fn(i32, i32) -> i32 -{ - f.call((x, y)) -} - -fn test_fn_const_call(f: &F) -> i32 - where F: Fn(i32, i32) -> i32 -{ - f.call((100, -1)) -} - -fn test_fn_nil_call(f: &F) -> i32 - where F: Fn() -> i32 -{ - f() -} - -fn test_fn_transmute_zst(x: ()) -> [(); 1] { - fn id(x: T) -> T {x} - - id(unsafe { - std::mem::transmute(x) - }) -} - -fn test_fn_ignored_pair() -> ((), ()) { - ((), ()) -} - -fn test_fn_ignored_pair_0() { - test_fn_ignored_pair().0 -} - -fn id(x: T) -> T { x } - -fn ignored_pair_named() -> (Foo, Foo) { - (Foo, Foo) -} - -fn test_fn_ignored_pair_named() -> (Foo, Foo) { - id(ignored_pair_named()) -} - -fn test_fn_nested_pair(x: &((f32, f32), u32)) -> (f32, f32) { - let y = *x; - let z = y.0; - (z.0, z.1) -} - -fn test_fn_const_arg_by_ref(mut a: [u64; 4]) -> u64 { - // Mutate the by-reference argument, which won't work with - // a non-immediate constant unless it's copied to the stack. - let a = test::black_box(&mut a); - a[0] += a[1]; - a[0] += a[2]; - a[0] += a[3]; - a[0] -} - -fn main() { - assert_eq!(test1(1, (2, 3), &[4, 5, 6]), (1, (2, 3), &[4, 5, 6][..])); - assert_eq!(test2(98), 98); - assert_eq!(test3(&Foo, 42), 42); - assert_eq!(test4(&Foo, 970), 970); - assert_eq!(test5(&Foo, 8576), 8576); - assert_eq!(test6(&Foo, 12367), 12367); - assert_eq!(test7(), 1); - assert_eq!(test8(), 2); - assert_eq!(test9(), 41 + 42 * 43); - - let r = 3; - let closure = |x: i32, y: i32| { r*(x + (y*2)) }; - assert_eq!(test_fn_const_call(&closure), 294); - assert_eq!(test_closure(&closure, 100, 1), 306); - let function_object = &closure as &Fn(i32, i32) -> i32; - assert_eq!(test_fn_object(function_object, 100, 2), 312); - assert_eq!(test_fn_impl(&function_object, 100, 3), 318); - assert_eq!(test_fn_direct_call(&closure, 100, 4), 324); - - assert_eq!(test_fn_nil_call(&(|| 42)), 42); - assert_eq!(test_fn_transmute_zst(()), [()]); - - assert_eq!(test_fn_ignored_pair_0(), ()); - assert_eq!(test_fn_ignored_pair_named(), (Foo, Foo)); - assert_eq!(test_fn_nested_pair(&((1.0, 2.0), 0)), (1.0, 2.0)); - - const ARRAY: [u64; 4] = [1, 2, 3, 4]; - assert_eq!(test_fn_const_arg_by_ref(ARRAY), 1 + 2 + 3 + 4); -} diff --git a/src/test/run-pass/mir_trans_calls_variadic.rs b/src/test/run-pass/mir_trans_calls_variadic.rs deleted file mode 100644 index 7845c9426e2..00000000000 --- a/src/test/run-pass/mir_trans_calls_variadic.rs +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright 2015 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -// ignore-wasm32-bare no libc to test ffi with - -#[link(name = "rust_test_helpers", kind = "static")] -extern { - fn rust_interesting_average(_: i64, ...) -> f64; -} - -fn test(a: i64, b: i64, c: i64, d: i64, e: i64, f: T, g: U) -> i64 { - unsafe { - rust_interesting_average(6, a, a as f64, - b, b as f64, - c, c as f64, - d, d as f64, - e, e as f64, - f, g) as i64 - } -} - -fn main(){ - assert_eq!(test(10, 20, 30, 40, 50, 60_i64, 60.0_f64), 70); -} diff --git a/src/test/run-pass/mir_trans_critical_edge.rs b/src/test/run-pass/mir_trans_critical_edge.rs deleted file mode 100644 index f6fe19c4309..00000000000 --- a/src/test/run-pass/mir_trans_critical_edge.rs +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright 2015 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -// This code produces a CFG with critical edges that, if we don't -// handle properly, will cause invalid codegen. - -#![feature(rustc_attrs)] - -enum State { - Both, - Front, - Back -} - -pub struct Foo { - state: State, - a: A, - b: B -} - -impl Foo -where A: Iterator, B: Iterator -{ - // This is the function we care about - fn next(&mut self) -> Option { - match self.state { - State::Both => match self.a.next() { - elt @ Some(..) => elt, - None => { - self.state = State::Back; - self.b.next() - } - }, - State::Front => self.a.next(), - State::Back => self.b.next(), - } - } -} - -// Make sure we actually translate a version of the function -pub fn do_stuff(mut f: Foo>, Box>>) { - let _x = f.next(); -} - -fn main() {} diff --git a/src/test/run-pass/mir_trans_spike1.rs b/src/test/run-pass/mir_trans_spike1.rs deleted file mode 100644 index 8474e841e01..00000000000 --- a/src/test/run-pass/mir_trans_spike1.rs +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright 2012 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. - -// A simple spike test for MIR version of trans. - -fn sum(x: i32, y: i32) -> i32 { - x + y -} - -fn main() { - let x = sum(22, 44); - assert_eq!(x, 66); - println!("sum()={:?}", x); -} diff --git a/src/test/run-pass/mir_trans_switch.rs b/src/test/run-pass/mir_trans_switch.rs deleted file mode 100644 index b097bf46ad3..00000000000 --- a/src/test/run-pass/mir_trans_switch.rs +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright 2015 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -enum Abc { - A(u8), - B(i8), - C, - D, -} - -fn foo(x: Abc) -> i32 { - match x { - Abc::C => 3, - Abc::D => 4, - Abc::B(_) => 2, - Abc::A(_) => 1, - } -} - -fn foo2(x: Abc) -> bool { - match x { - Abc::D => true, - _ => false - } -} - -fn main() { - assert_eq!(1, foo(Abc::A(42))); - assert_eq!(2, foo(Abc::B(-100))); - assert_eq!(3, foo(Abc::C)); - assert_eq!(4, foo(Abc::D)); - - assert_eq!(false, foo2(Abc::A(1))); - assert_eq!(false, foo2(Abc::B(2))); - assert_eq!(false, foo2(Abc::C)); - assert_eq!(true, foo2(Abc::D)); -} diff --git a/src/test/run-pass/mir_trans_switchint.rs b/src/test/run-pass/mir_trans_switchint.rs deleted file mode 100644 index 537734596a5..00000000000 --- a/src/test/run-pass/mir_trans_switchint.rs +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright 2015 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -pub fn foo(x: i8) -> i32 { - match x { - 1 => 0, - _ => 1, - } -} - -fn main() { - assert_eq!(foo(0), 1); - assert_eq!(foo(1), 0); -} diff --git a/src/test/run-pass/pattern-bound-var-in-for-each.rs b/src/test/run-pass/pattern-bound-var-in-for-each.rs index 59ead3e3e98..778f355b24b 100644 --- a/src/test/run-pass/pattern-bound-var-in-for-each.rs +++ b/src/test/run-pass/pattern-bound-var-in-for-each.rs @@ -8,8 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// Tests that trans_path checks whether a -// pattern-bound var is an upvar (when translating +// Tests that codegen_path checks whether a +// pattern-bound var is an upvar (when codegenning // the for-each body) diff --git a/src/test/run-pass/regions-mock-codegen.rs b/src/test/run-pass/regions-mock-codegen.rs new file mode 100644 index 00000000000..60a7f70931d --- /dev/null +++ b/src/test/run-pass/regions-mock-codegen.rs @@ -0,0 +1,62 @@ +// Copyright 2012 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. + +// pretty-expanded FIXME #23616 + +#![feature(allocator_api)] + +use std::alloc::{Alloc, Global, Layout, oom}; +use std::ptr::NonNull; + +struct arena(()); + +struct Bcx<'a> { + fcx: &'a Fcx<'a> +} + +struct Fcx<'a> { + arena: &'a arena, + ccx: &'a Ccx +} + +struct Ccx { + x: isize +} + +fn alloc<'a>(_bcx : &'a arena) -> &'a Bcx<'a> { + unsafe { + let ptr = Global.alloc(Layout::new::()) + .unwrap_or_else(|_| oom()); + &*(ptr.as_ptr() as *const _) + } +} + +fn h<'a>(bcx : &'a Bcx<'a>) -> &'a Bcx<'a> { + return alloc(bcx.fcx.arena); +} + +fn g(fcx : &Fcx) { + let bcx = Bcx { fcx: fcx }; + let bcx2 = h(&bcx); + unsafe { + Global.dealloc(NonNull::new_unchecked(bcx2 as *const _ as *mut _), Layout::new::()); + } +} + +fn f(ccx : &Ccx) { + let a = arena(()); + let fcx = Fcx { arena: &a, ccx: ccx }; + return g(&fcx); +} + +pub fn main() { + let ccx = Ccx { x: 0 }; + f(&ccx); +} diff --git a/src/test/run-pass/regions-mock-trans.rs b/src/test/run-pass/regions-mock-trans.rs deleted file mode 100644 index 60a7f70931d..00000000000 --- a/src/test/run-pass/regions-mock-trans.rs +++ /dev/null @@ -1,62 +0,0 @@ -// Copyright 2012 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. - -// pretty-expanded FIXME #23616 - -#![feature(allocator_api)] - -use std::alloc::{Alloc, Global, Layout, oom}; -use std::ptr::NonNull; - -struct arena(()); - -struct Bcx<'a> { - fcx: &'a Fcx<'a> -} - -struct Fcx<'a> { - arena: &'a arena, - ccx: &'a Ccx -} - -struct Ccx { - x: isize -} - -fn alloc<'a>(_bcx : &'a arena) -> &'a Bcx<'a> { - unsafe { - let ptr = Global.alloc(Layout::new::()) - .unwrap_or_else(|_| oom()); - &*(ptr.as_ptr() as *const _) - } -} - -fn h<'a>(bcx : &'a Bcx<'a>) -> &'a Bcx<'a> { - return alloc(bcx.fcx.arena); -} - -fn g(fcx : &Fcx) { - let bcx = Bcx { fcx: fcx }; - let bcx2 = h(&bcx); - unsafe { - Global.dealloc(NonNull::new_unchecked(bcx2 as *const _ as *mut _), Layout::new::()); - } -} - -fn f(ccx : &Ccx) { - let a = arena(()); - let fcx = Fcx { arena: &a, ccx: ccx }; - return g(&fcx); -} - -pub fn main() { - let ccx = Ccx { x: 0 }; - f(&ccx); -} diff --git a/src/test/run-pass/sepcomp-fns-backwards.rs b/src/test/run-pass/sepcomp-fns-backwards.rs index 1ab8bc7f88c..4fea07028b6 100644 --- a/src/test/run-pass/sepcomp-fns-backwards.rs +++ b/src/test/run-pass/sepcomp-fns-backwards.rs @@ -11,7 +11,7 @@ // ignore-bitrig // compile-flags: -C codegen-units=3 -// Test references to items that haven't been translated yet. +// Test references to items that haven't been codegened yet. // Generate some code in the first compilation unit before declaring any // modules. This ensures that the first module doesn't go into the same diff --git a/src/test/run-pass/small-enum-range-edge.rs b/src/test/run-pass/small-enum-range-edge.rs index 56abdf6e20a..d2283da8bdd 100644 --- a/src/test/run-pass/small-enum-range-edge.rs +++ b/src/test/run-pass/small-enum-range-edge.rs @@ -13,7 +13,7 @@ #![feature(core)] /*! - * Tests the range assertion wraparound case in trans::middle::adt::load_discr. + * Tests the range assertion wraparound case when reading discriminants. */ #[repr(u8)] diff --git a/src/test/run-pass/trans-object-shim.rs b/src/test/run-pass/trans-object-shim.rs deleted file mode 100644 index 5fbfef05e10..00000000000 --- a/src/test/run-pass/trans-object-shim.rs +++ /dev/null @@ -1,14 +0,0 @@ -// Copyright 2016 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -fn main() { - assert_eq!((ToString::to_string as fn(&(ToString+'static)) -> String)(&"foo"), - String::from("foo")); -} diff --git a/src/test/run-pass/trans-tag-static-padding.rs b/src/test/run-pass/trans-tag-static-padding.rs deleted file mode 100644 index ba01d51dc6a..00000000000 --- a/src/test/run-pass/trans-tag-static-padding.rs +++ /dev/null @@ -1,67 +0,0 @@ -// Copyright 2014 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - - -// Issue #13186 - -// For simplicity of explanations assuming code is compiled for x86_64 -// Linux ABI. - -// Size of TestOption is 16, and alignment of TestOption is 8. -// Size of u8 is 1, and alignment of u8 is 1. -// So size of Request is 24, and alignment of Request must be 8: -// the maximum alignment of its fields. -// Last 7 bytes of Request struct are not occupied by any fields. - - - -enum TestOption { - TestNone, - TestSome(T), -} - -pub struct Request { - foo: TestOption, - bar: u8, -} - -fn default_instance() -> &'static Request { - static instance: Request = Request { - // LLVM does not allow to specify alignment of expressions, thus - // alignment of `foo` in constant is 1, not 8. - foo: TestOption::TestNone, - bar: 17, - // Space after last field is not occupied by any data, but it is - // reserved to make struct aligned properly. If compiler does - // not insert padding after last field when emitting constant, - // size of struct may be not equal to size of struct, and - // compiler crashes in internal assertion check. - }; - &instance -} - -fn non_default_instance() -> &'static Request { - static instance: Request = Request { - foo: TestOption::TestSome(0x1020304050607080), - bar: 19, - }; - &instance -} - -pub fn main() { - match default_instance() { - &Request { foo: TestOption::TestNone, bar: 17 } => {}, - _ => panic!(), - }; - match non_default_instance() { - &Request { foo: TestOption::TestSome(0x1020304050607080), bar: 19 } => {}, - _ => panic!(), - }; -} diff --git a/src/test/run-pass/union/union-const-codegen.rs b/src/test/run-pass/union/union-const-codegen.rs new file mode 100644 index 00000000000..77270364bb5 --- /dev/null +++ b/src/test/run-pass/union/union-const-codegen.rs @@ -0,0 +1,25 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +union U { + a: u64, + b: u64, +} + +const C: U = U { b: 10 }; + +fn main() { + unsafe { + let a = C.a; + let b = C.b; + assert_eq!(a, 10); + assert_eq!(b, 10); + } +} diff --git a/src/test/run-pass/union/union-const-trans.rs b/src/test/run-pass/union/union-const-trans.rs deleted file mode 100644 index 77270364bb5..00000000000 --- a/src/test/run-pass/union/union-const-trans.rs +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright 2016 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -union U { - a: u64, - b: u64, -} - -const C: U = U { b: 10 }; - -fn main() { - unsafe { - let a = C.a; - let b = C.b; - assert_eq!(a, 10); - assert_eq!(b, 10); - } -} diff --git a/src/test/run-pass/zero-sized-tuple-struct.rs b/src/test/run-pass/zero-sized-tuple-struct.rs index aaffdc4ec7c..9625d6a88ac 100644 --- a/src/test/run-pass/zero-sized-tuple-struct.rs +++ b/src/test/run-pass/zero-sized-tuple-struct.rs @@ -10,7 +10,7 @@ #![allow(unused_assignments)] -// Make sure that the constructor args are translated for zero-sized tuple structs +// Make sure that the constructor args are codegened for zero-sized tuple structs struct Foo(()); diff --git a/src/tools/compiletest/src/header.rs b/src/tools/compiletest/src/header.rs index 06ec9e893f0..f66f5c5b70e 100644 --- a/src/tools/compiletest/src/header.rs +++ b/src/tools/compiletest/src/header.rs @@ -224,7 +224,7 @@ pub struct TestProps { // The test must be compiled and run successfully. Only used in UI tests for now. pub run_pass: bool, // Skip any codegen step and running the executable. Only for run-pass. - pub skip_trans: bool, + pub skip_codegen: bool, // Do not pass `-Z ui-testing` to UI tests pub disable_ui_testing_normalization: bool, // customized normalization rules @@ -258,7 +258,7 @@ impl TestProps { compile_pass: false, check_test_line_numbers_match: false, run_pass: false, - skip_trans: false, + skip_codegen: false, disable_ui_testing_normalization: false, normalize_stdout: vec![], normalize_stderr: vec![], @@ -371,8 +371,8 @@ impl TestProps { self.compile_pass = config.parse_compile_pass(ln) || self.run_pass; } - if !self.skip_trans { - self.skip_trans = config.parse_skip_trans(ln); + if !self.skip_codegen { + self.skip_codegen = config.parse_skip_codegen(ln); } if !self.disable_ui_testing_normalization { @@ -532,8 +532,8 @@ impl Config { self.parse_name_directive(line, "run-pass") } - fn parse_skip_trans(&self, line: &str) -> bool { - self.parse_name_directive(line, "skip-trans") + fn parse_skip_codegen(&self, line: &str) -> bool { + self.parse_name_directive(line, "skip-codegen") } fn parse_env(&self, line: &str, name: &str) -> Option<(String, String)> { diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs index 59d94e1fa51..780c8122734 100644 --- a/src/tools/compiletest/src/runtest.rs +++ b/src/tools/compiletest/src/runtest.rs @@ -357,7 +357,7 @@ impl<'test> TestCx<'test> { "run-pass tests with expected warnings should be moved to ui/" ); - if !self.props.skip_trans { + if !self.props.skip_codegen { let proc_res = self.exec_compiled_test(); if !proc_res.status.success() { self.fatal_proc_rec("test run failed!", &proc_res); @@ -555,7 +555,7 @@ impl<'test> TestCx<'test> { rustc .arg("-") - .arg("-Zno-trans") + .arg("-Zno-codegen") .arg("--out-dir") .arg(&out_dir) .arg(&format!("--target={}", target)) @@ -1703,7 +1703,7 @@ impl<'test> TestCx<'test> { } } - if self.props.skip_trans { + if self.props.skip_codegen { assert!(!self.props.compile_flags.iter().any(|s| s.starts_with("--emit"))); rustc.args(&["--emit", "metadata"]); } @@ -2181,19 +2181,19 @@ impl<'test> TestCx<'test> { self.check_no_compiler_crash(&proc_res); - const PREFIX: &'static str = "TRANS_ITEM "; + const PREFIX: &'static str = "MONO_ITEM "; const CGU_MARKER: &'static str = "@@"; - let actual: Vec = proc_res + let actual: Vec = proc_res .stdout .lines() .filter(|line| line.starts_with(PREFIX)) - .map(str_to_trans_item) + .map(str_to_mono_item) .collect(); - let expected: Vec = errors::load_errors(&self.testpaths.file, None) + let expected: Vec = errors::load_errors(&self.testpaths.file, None) .iter() - .map(|e| str_to_trans_item(&e.msg[..])) + .map(|e| str_to_mono_item(&e.msg[..])) .collect(); let mut missing = Vec::new(); @@ -2271,14 +2271,14 @@ impl<'test> TestCx<'test> { } #[derive(Clone, Eq, PartialEq)] - struct TransItem { + struct MonoItem { name: String, codegen_units: HashSet, string: String, } - // [TRANS_ITEM] name [@@ (cgu)+] - fn str_to_trans_item(s: &str) -> TransItem { + // [MONO_ITEM] name [@@ (cgu)+] + fn str_to_mono_item(s: &str) -> MonoItem { let s = if s.starts_with(PREFIX) { (&s[PREFIX.len()..]).trim() } else { @@ -2307,7 +2307,7 @@ impl<'test> TestCx<'test> { HashSet::new() }; - TransItem { + MonoItem { name: name.to_owned(), codegen_units: cgus, string: full_string, diff --git a/src/tools/tidy/src/deps.rs b/src/tools/tidy/src/deps.rs index c34cf1bd5ec..5739ec5f325 100644 --- a/src/tools/tidy/src/deps.rs +++ b/src/tools/tidy/src/deps.rs @@ -52,7 +52,7 @@ static EXCEPTIONS: &'static [&'static str] = &[ /// Which crates to check against the whitelist? static WHITELIST_CRATES: &'static [CrateVersion] = &[ CrateVersion("rustc", "0.0.0"), - CrateVersion("rustc_trans", "0.0.0"), + CrateVersion("rustc_codegen_llvm", "0.0.0"), ]; /// Whitelist of crates rustc is allowed to depend on. Avoid adding to the list if possible. -- cgit 1.4.1-3-g733a5 From 9e3432447a9c6386443acdf731d488c159be3f66 Mon Sep 17 00:00:00 2001 From: Mark Simulacrum Date: Thu, 10 May 2018 12:02:19 -0600 Subject: Switch to 1.26 bootstrap compiler --- src/bootstrap/channel.rs | 2 +- src/liballoc/Cargo.toml | 2 + src/liballoc/alloc.rs | 47 +- src/liballoc/lib.rs | 10 - src/liballoc/slice.rs | 13 +- src/liballoc/str.rs | 7 +- src/liballoc/vec.rs | 3 - src/liballoc_jemalloc/lib.rs | 7 - src/liballoc_system/lib.rs | 27 - src/libarena/lib.rs | 1 - src/libcore/Cargo.toml | 2 + src/libcore/clone.rs | 1 - src/libcore/internal_macros.rs | 14 - src/libcore/lib.rs | 19 +- src/libcore/marker.rs | 1 - src/libcore/num/f32.rs | 4 +- src/libcore/num/f64.rs | 4 +- src/libcore/num/mod.rs | 2 - src/libcore/ops/range.rs | 10 - src/libcore/prelude/v1.rs | 10 - src/libcore/slice/mod.rs | 987 ++++++++----------------------- src/libcore/str/mod.rs | 590 ++++-------------- src/libcore/tests/lib.rs | 1 - src/libcore/tests/num/uint_macros.rs | 1 - src/librustc/lib.rs | 1 - src/librustc_mir/borrow_check/nll/mod.rs | 4 +- src/librustc_mir/lib.rs | 13 +- src/librustc_mir/util/pretty.rs | 8 +- src/librustc_typeck/lib.rs | 2 - src/librustdoc/lib.rs | 2 - src/libstd/alloc.rs | 61 -- src/libstd/f32.rs | 12 +- src/libstd/f64.rs | 12 +- src/libstd/lib.rs | 9 +- src/libsyntax_ext/asm.rs | 21 +- src/libsyntax_ext/lib.rs | 3 +- src/rtstartup/rsbegin.rs | 31 +- src/stage0.txt | 2 +- 38 files changed, 434 insertions(+), 1512 deletions(-) (limited to 'src/liballoc/alloc.rs') diff --git a/src/bootstrap/channel.rs b/src/bootstrap/channel.rs index 3453933a965..a2495f68c1f 100644 --- a/src/bootstrap/channel.rs +++ b/src/bootstrap/channel.rs @@ -24,7 +24,7 @@ use Build; use config::Config; // The version number -pub const CFG_RELEASE_NUM: &str = "1.27.0"; +pub const CFG_RELEASE_NUM: &str = "1.28.0"; pub struct GitInfo { inner: Option, diff --git a/src/liballoc/Cargo.toml b/src/liballoc/Cargo.toml index 6383bd1e941..ada21e04b30 100644 --- a/src/liballoc/Cargo.toml +++ b/src/liballoc/Cargo.toml @@ -2,6 +2,8 @@ authors = ["The Rust Project Developers"] name = "alloc" version = "0.0.0" +autotests = false +autobenches = false [lib] name = "alloc" diff --git a/src/liballoc/alloc.rs b/src/liballoc/alloc.rs index f59c9f7fd61..b4b82e6ecff 100644 --- a/src/liballoc/alloc.rs +++ b/src/liballoc/alloc.rs @@ -22,28 +22,6 @@ use core::usize; #[doc(inline)] pub use core::alloc::*; -#[cfg(stage0)] -extern "Rust" { - #[allocator] - #[rustc_allocator_nounwind] - fn __rust_alloc(size: usize, align: usize, err: *mut u8) -> *mut u8; - #[cold] - #[rustc_allocator_nounwind] - fn __rust_oom(err: *const u8) -> !; - #[rustc_allocator_nounwind] - fn __rust_dealloc(ptr: *mut u8, size: usize, align: usize); - #[rustc_allocator_nounwind] - fn __rust_realloc(ptr: *mut u8, - old_size: usize, - old_align: usize, - new_size: usize, - new_align: usize, - err: *mut u8) -> *mut u8; - #[rustc_allocator_nounwind] - fn __rust_alloc_zeroed(size: usize, align: usize, err: *mut u8) -> *mut u8; -} - -#[cfg(not(stage0))] extern "Rust" { #[allocator] #[rustc_allocator_nounwind] @@ -74,10 +52,7 @@ pub const Heap: Global = Global; unsafe impl GlobalAlloc for Global { #[inline] unsafe fn alloc(&self, layout: Layout) -> *mut Opaque { - #[cfg(not(stage0))] let ptr = __rust_alloc(layout.size(), layout.align()); - #[cfg(stage0)] - let ptr = __rust_alloc(layout.size(), layout.align(), &mut 0); ptr as *mut Opaque } @@ -88,20 +63,13 @@ unsafe impl GlobalAlloc for Global { #[inline] unsafe fn realloc(&self, ptr: *mut Opaque, layout: Layout, new_size: usize) -> *mut Opaque { - #[cfg(not(stage0))] let ptr = __rust_realloc(ptr as *mut u8, layout.size(), layout.align(), new_size); - #[cfg(stage0)] - let ptr = __rust_realloc(ptr as *mut u8, layout.size(), layout.align(), - new_size, layout.align(), &mut 0); ptr as *mut Opaque } #[inline] unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut Opaque { - #[cfg(not(stage0))] let ptr = __rust_alloc_zeroed(layout.size(), layout.align()); - #[cfg(stage0)] - let ptr = __rust_alloc_zeroed(layout.size(), layout.align(), &mut 0); ptr as *mut Opaque } } @@ -152,14 +120,7 @@ unsafe fn exchange_malloc(size: usize, align: usize) -> *mut u8 { } } -#[cfg(stage0)] -#[lang = "box_free"] -#[inline] -unsafe fn old_box_free(ptr: *mut T) { - box_free(Unique::new_unchecked(ptr)) -} - -#[cfg_attr(not(any(test, stage0)), lang = "box_free")] +#[cfg_attr(not(test), lang = "box_free")] #[inline] pub(crate) unsafe fn box_free(ptr: Unique) { let ptr = ptr.as_ptr(); @@ -172,12 +133,6 @@ pub(crate) unsafe fn box_free(ptr: Unique) { } } -#[cfg(stage0)] -pub fn oom() -> ! { - unsafe { ::core::intrinsics::abort() } -} - -#[cfg(not(stage0))] pub fn oom() -> ! { extern { #[lang = "oom"] diff --git a/src/liballoc/lib.rs b/src/liballoc/lib.rs index f7dd9d4f010..91de3ad0c39 100644 --- a/src/liballoc/lib.rs +++ b/src/liballoc/lib.rs @@ -75,7 +75,6 @@ #![deny(missing_debug_implementations)] #![cfg_attr(test, allow(deprecated))] // rand -#![cfg_attr(all(not(test), stage0), feature(float_internals))] #![cfg_attr(not(test), feature(exact_size_is_empty))] #![cfg_attr(not(test), feature(generator_trait))] #![cfg_attr(test, feature(rand, test))] @@ -90,13 +89,10 @@ #![feature(collections_range)] #![feature(const_fn)] #![feature(core_intrinsics)] -#![cfg_attr(stage0, feature(core_slice_ext))] -#![cfg_attr(stage0, feature(core_str_ext))] #![feature(custom_attribute)] #![feature(dropck_eyepatch)] #![feature(exact_size_is_empty)] #![feature(fmt_internals)] -#![cfg_attr(stage0, feature(fn_must_use))] #![feature(from_ref)] #![feature(fundamental)] #![feature(lang_items)] @@ -122,7 +118,6 @@ #![feature(exact_chunks)] #![feature(pointer_methods)] #![feature(inclusive_range_methods)] -#![cfg_attr(stage0, feature(generic_param_attrs))] #![feature(rustc_const_unstable)] #![feature(const_vec_new)] @@ -157,15 +152,10 @@ pub mod alloc; #[unstable(feature = "allocator_api", issue = "32838")] #[rustc_deprecated(since = "1.27.0", reason = "module renamed to `alloc`")] /// Use the `alloc` module instead. -#[cfg(not(stage0))] pub mod heap { pub use alloc::*; } -#[unstable(feature = "allocator_api", issue = "32838")] -#[rustc_deprecated(since = "1.27.0", reason = "module renamed to `alloc`")] -#[cfg(stage0)] -pub mod heap; // Primitive types using the heaps above diff --git a/src/liballoc/slice.rs b/src/liballoc/slice.rs index 6caf12aa7eb..4427ac004f9 100644 --- a/src/liballoc/slice.rs +++ b/src/liballoc/slice.rs @@ -101,7 +101,6 @@ use core::cmp::Ordering::{self, Less}; use core::mem::size_of; use core::mem; use core::ptr; -#[cfg(stage0)] use core::slice::SliceExt; use core::{u8, u16, u32}; use borrow::{Borrow, BorrowMut, ToOwned}; @@ -171,13 +170,9 @@ mod hack { } } -#[cfg_attr(stage0, lang = "slice")] -#[cfg_attr(not(stage0), lang = "slice_alloc")] +#[lang = "slice_alloc"] #[cfg(not(test))] impl [T] { - #[cfg(stage0)] - slice_core_methods!(); - /// Sorts the slice. /// /// This sort is stable (i.e. does not reorder equal elements) and `O(n log n)` worst-case. @@ -467,8 +462,7 @@ impl [T] { } } -#[cfg_attr(stage0, lang = "slice_u8")] -#[cfg_attr(not(stage0), lang = "slice_u8_alloc")] +#[lang = "slice_u8_alloc"] #[cfg(not(test))] impl [u8] { /// Returns a vector containing a copy of this slice where each byte @@ -504,9 +498,6 @@ impl [u8] { me.make_ascii_lowercase(); me } - - #[cfg(stage0)] - slice_u8_core_methods!(); } //////////////////////////////////////////////////////////////////////////////// diff --git a/src/liballoc/str.rs b/src/liballoc/str.rs index 42efdea74b1..c10c0a69433 100644 --- a/src/liballoc/str.rs +++ b/src/liballoc/str.rs @@ -40,7 +40,6 @@ use core::fmt; use core::str as core_str; -#[cfg(stage0)] use core::str::StrExt; use core::str::pattern::Pattern; use core::str::pattern::{Searcher, ReverseSearcher, DoubleEndedSearcher}; use core::mem; @@ -158,13 +157,9 @@ impl ToOwned for str { } /// Methods for string slices. -#[cfg_attr(stage0, lang = "str")] -#[cfg_attr(not(stage0), lang = "str_alloc")] +#[lang = "str_alloc"] #[cfg(not(test))] impl str { - #[cfg(stage0)] - str_core_methods!(); - /// Converts a `Box` into a `Box<[u8]>` without copying or allocating. /// /// # Examples diff --git a/src/liballoc/vec.rs b/src/liballoc/vec.rs index d30f8cd0fca..bf89b377b7e 100644 --- a/src/liballoc/vec.rs +++ b/src/liballoc/vec.rs @@ -73,9 +73,6 @@ use core::intrinsics::{arith_offset, assume}; use core::iter::{FromIterator, FusedIterator, TrustedLen}; use core::marker::PhantomData; use core::mem; -#[cfg(not(test))] -#[cfg(stage0)] -use core::num::Float; use core::ops::Bound::{Excluded, Included, Unbounded}; use core::ops::{Index, IndexMut, RangeBounds}; use core::ops; diff --git a/src/liballoc_jemalloc/lib.rs b/src/liballoc_jemalloc/lib.rs index 4b8755877de..ce856eccd83 100644 --- a/src/liballoc_jemalloc/lib.rs +++ b/src/liballoc_jemalloc/lib.rs @@ -97,13 +97,6 @@ mod contents { ptr } - #[cfg(stage0)] - #[no_mangle] - #[rustc_std_internal_symbol] - pub unsafe extern fn __rde_oom() -> ! { - ::core::intrinsics::abort(); - } - #[no_mangle] #[rustc_std_internal_symbol] pub unsafe extern fn __rde_dealloc(ptr: *mut u8, diff --git a/src/liballoc_system/lib.rs b/src/liballoc_system/lib.rs index 7376ac0f15d..9490b54e675 100644 --- a/src/liballoc_system/lib.rs +++ b/src/liballoc_system/lib.rs @@ -73,33 +73,6 @@ unsafe impl Alloc for System { } } -#[cfg(stage0)] -#[unstable(feature = "allocator_api", issue = "32838")] -unsafe impl<'a> Alloc for &'a System { - #[inline] - unsafe fn alloc(&mut self, layout: Layout) -> Result, AllocErr> { - NonNull::new(GlobalAlloc::alloc(*self, layout)).ok_or(AllocErr) - } - - #[inline] - unsafe fn alloc_zeroed(&mut self, layout: Layout) -> Result, AllocErr> { - NonNull::new(GlobalAlloc::alloc_zeroed(*self, layout)).ok_or(AllocErr) - } - - #[inline] - unsafe fn dealloc(&mut self, ptr: NonNull, layout: Layout) { - GlobalAlloc::dealloc(*self, ptr.as_ptr(), layout) - } - - #[inline] - unsafe fn realloc(&mut self, - ptr: NonNull, - layout: Layout, - new_size: usize) -> Result, AllocErr> { - NonNull::new(GlobalAlloc::realloc(*self, ptr.as_ptr(), layout, new_size)).ok_or(AllocErr) - } -} - #[cfg(any(windows, unix, target_os = "cloudabi", target_os = "redox"))] mod realloc_fallback { use core::alloc::{GlobalAlloc, Opaque, Layout}; diff --git a/src/libarena/lib.rs b/src/libarena/lib.rs index c79e0e14e3d..f7143a4f981 100644 --- a/src/libarena/lib.rs +++ b/src/libarena/lib.rs @@ -26,7 +26,6 @@ #![feature(alloc)] #![feature(core_intrinsics)] #![feature(dropck_eyepatch)] -#![cfg_attr(stage0, feature(generic_param_attrs))] #![cfg_attr(test, feature(test))] #![allow(deprecated)] diff --git a/src/libcore/Cargo.toml b/src/libcore/Cargo.toml index 24529f7a9d8..321ed892ea9 100644 --- a/src/libcore/Cargo.toml +++ b/src/libcore/Cargo.toml @@ -2,6 +2,8 @@ authors = ["The Rust Project Developers"] name = "core" version = "0.0.0" +autotests = false +autobenches = false [lib] name = "core" diff --git a/src/libcore/clone.rs b/src/libcore/clone.rs index f79f7351698..3b15ba2b4ab 100644 --- a/src/libcore/clone.rs +++ b/src/libcore/clone.rs @@ -153,7 +153,6 @@ pub struct AssertParamIsCopy { _field: ::marker::PhantomData { - $(#[$attr])* pub $($Item)* - } -} - -#[cfg(not(stage0))] -macro_rules! public_in_stage0 { - ( { $(#[$attr:meta])* } $($Item: tt)*) => { - $(#[$attr])* pub(crate) $($Item)* - } -} diff --git a/src/libcore/lib.rs b/src/libcore/lib.rs index 06fbfcecba8..77b5488084d 100644 --- a/src/libcore/lib.rs +++ b/src/libcore/lib.rs @@ -112,18 +112,13 @@ #![feature(unwind_attributes)] #![feature(doc_alias)] #![feature(inclusive_range_methods)] - -#![cfg_attr(not(stage0), feature(mmx_target_feature))] -#![cfg_attr(not(stage0), feature(tbm_target_feature))] -#![cfg_attr(not(stage0), feature(sse4a_target_feature))] -#![cfg_attr(not(stage0), feature(arm_target_feature))] -#![cfg_attr(not(stage0), feature(powerpc_target_feature))] -#![cfg_attr(not(stage0), feature(mips_target_feature))] -#![cfg_attr(not(stage0), feature(aarch64_target_feature))] - -#![cfg_attr(stage0, feature(target_feature))] -#![cfg_attr(stage0, feature(cfg_target_feature))] -#![cfg_attr(stage0, feature(fn_must_use))] +#![feature(mmx_target_feature)] +#![feature(tbm_target_feature)] +#![feature(sse4a_target_feature)] +#![feature(arm_target_feature)] +#![feature(powerpc_target_feature)] +#![feature(mips_target_feature)] +#![feature(aarch64_target_feature)] #[prelude_import] #[allow(unused)] diff --git a/src/libcore/marker.rs b/src/libcore/marker.rs index db5f50a99ca..6c8ee0eda11 100644 --- a/src/libcore/marker.rs +++ b/src/libcore/marker.rs @@ -611,7 +611,6 @@ pub unsafe auto trait Unpin {} /// /// Implementations that cannot be described in Rust /// are implemented in `SelectionContext::copy_clone_conditions()` in librustc. -#[cfg(not(stage0))] mod copy_impls { use super::Copy; diff --git a/src/libcore/num/f32.rs b/src/libcore/num/f32.rs index 4a7dc13f0f2..718dd42a615 100644 --- a/src/libcore/num/f32.rs +++ b/src/libcore/num/f32.rs @@ -19,7 +19,7 @@ use mem; use num::Float; -#[cfg(not(stage0))] use num::FpCategory; +use num::FpCategory; use num::FpCategory as Fp; /// The radix or base of the internal representation of `f32`. @@ -277,7 +277,6 @@ impl Float for f32 { // FIXME: remove (inline) this macro and the Float trait // when updating to a bootstrap compiler that has the new lang items. -#[cfg_attr(stage0, macro_export)] #[unstable(feature = "core_float", issue = "32110")] macro_rules! f32_core_methods { () => { /// Returns `true` if this value is `NaN` and false otherwise. @@ -553,7 +552,6 @@ macro_rules! f32_core_methods { () => { #[lang = "f32"] #[cfg(not(test))] -#[cfg(not(stage0))] impl f32 { f32_core_methods!(); } diff --git a/src/libcore/num/f64.rs b/src/libcore/num/f64.rs index 801de5e87bd..f128c55c78a 100644 --- a/src/libcore/num/f64.rs +++ b/src/libcore/num/f64.rs @@ -19,7 +19,7 @@ use mem; use num::Float; -#[cfg(not(stage0))] use num::FpCategory; +use num::FpCategory; use num::FpCategory as Fp; /// The radix or base of the internal representation of `f64`. @@ -276,7 +276,6 @@ impl Float for f64 { // FIXME: remove (inline) this macro and the Float trait // when updating to a bootstrap compiler that has the new lang items. -#[cfg_attr(stage0, macro_export)] #[unstable(feature = "core_float", issue = "32110")] macro_rules! f64_core_methods { () => { /// Returns `true` if this value is `NaN` and false otherwise. @@ -562,7 +561,6 @@ macro_rules! f64_core_methods { () => { #[lang = "f64"] #[cfg(not(test))] -#[cfg(not(stage0))] impl f64 { f64_core_methods!(); } diff --git a/src/libcore/num/mod.rs b/src/libcore/num/mod.rs index 6df8ca98ba9..58d45b107f1 100644 --- a/src/libcore/num/mod.rs +++ b/src/libcore/num/mod.rs @@ -422,7 +422,6 @@ $EndFeature, " /// assert_eq!(m, -22016); /// ``` #[unstable(feature = "reverse_bits", issue = "48763")] - #[cfg(not(stage0))] #[inline] pub fn reverse_bits(self) -> Self { (self as $UnsignedT).reverse_bits() as Self @@ -2194,7 +2193,6 @@ assert_eq!(n.trailing_zeros(), 3);", $EndFeature, " /// assert_eq!(m, 43520); /// ``` #[unstable(feature = "reverse_bits", issue = "48763")] - #[cfg(not(stage0))] #[inline] pub fn reverse_bits(self) -> Self { unsafe { intrinsics::bitreverse(self as $ActualT) as Self } diff --git a/src/libcore/ops/range.rs b/src/libcore/ops/range.rs index 697e6a3efde..1f84631ada3 100644 --- a/src/libcore/ops/range.rs +++ b/src/libcore/ops/range.rs @@ -335,18 +335,8 @@ pub struct RangeInclusive { // but it is known that LLVM is not able to optimize loops following that RFC. // Consider adding an extra `bool` field to indicate emptiness of the range. // See #45222 for performance test cases. - #[cfg(not(stage0))] pub(crate) start: Idx, - #[cfg(not(stage0))] pub(crate) end: Idx, - /// The lower bound of the range (inclusive). - #[cfg(stage0)] - #[unstable(feature = "inclusive_range_fields", issue = "49022")] - pub start: Idx, - /// The upper bound of the range (inclusive). - #[cfg(stage0)] - #[unstable(feature = "inclusive_range_fields", issue = "49022")] - pub end: Idx, } impl RangeInclusive { diff --git a/src/libcore/prelude/v1.rs b/src/libcore/prelude/v1.rs index 8212648f2d8..45f629a6442 100644 --- a/src/libcore/prelude/v1.rs +++ b/src/libcore/prelude/v1.rs @@ -54,13 +54,3 @@ pub use option::Option::{self, Some, None}; #[stable(feature = "core_prelude", since = "1.4.0")] #[doc(no_inline)] pub use result::Result::{self, Ok, Err}; - -// Re-exported extension traits for primitive types -#[stable(feature = "core_prelude", since = "1.4.0")] -#[doc(no_inline)] -#[cfg(stage0)] -pub use slice::SliceExt; -#[stable(feature = "core_prelude", since = "1.4.0")] -#[doc(no_inline)] -#[cfg(stage0)] -pub use str::StrExt; diff --git a/src/libcore/slice/mod.rs b/src/libcore/slice/mod.rs index 93ebc23ac0b..82891b691dc 100644 --- a/src/libcore/slice/mod.rs +++ b/src/libcore/slice/mod.rs @@ -68,181 +68,6 @@ struct Repr { // Extension traits // -public_in_stage0! { -{ -/// Extension methods for slices. -#[unstable(feature = "core_slice_ext", - reason = "stable interface provided by `impl [T]` in later crates", - issue = "32110")] -#[allow(missing_docs)] // documented elsewhere -} -trait SliceExt { - type Item; - - #[stable(feature = "core", since = "1.6.0")] - fn split_at(&self, mid: usize) -> (&[Self::Item], &[Self::Item]); - - #[stable(feature = "core", since = "1.6.0")] - fn iter(&self) -> Iter; - - #[stable(feature = "core", since = "1.6.0")] - fn split

(&self, pred: P) -> Split - where P: FnMut(&Self::Item) -> bool; - - #[stable(feature = "slice_rsplit", since = "1.27.0")] - fn rsplit

(&self, pred: P) -> RSplit - where P: FnMut(&Self::Item) -> bool; - - #[stable(feature = "core", since = "1.6.0")] - fn splitn

(&self, n: usize, pred: P) -> SplitN - where P: FnMut(&Self::Item) -> bool; - - #[stable(feature = "core", since = "1.6.0")] - fn rsplitn

(&self, n: usize, pred: P) -> RSplitN - where P: FnMut(&Self::Item) -> bool; - - #[stable(feature = "core", since = "1.6.0")] - fn windows(&self, size: usize) -> Windows; - - #[stable(feature = "core", since = "1.6.0")] - fn chunks(&self, size: usize) -> Chunks; - - #[unstable(feature = "exact_chunks", issue = "47115")] - fn exact_chunks(&self, size: usize) -> ExactChunks; - - #[stable(feature = "core", since = "1.6.0")] - fn get(&self, index: I) -> Option<&I::Output> - where I: SliceIndex; - #[stable(feature = "core", since = "1.6.0")] - fn first(&self) -> Option<&Self::Item>; - - #[stable(feature = "core", since = "1.6.0")] - fn split_first(&self) -> Option<(&Self::Item, &[Self::Item])>; - - #[stable(feature = "core", since = "1.6.0")] - fn split_last(&self) -> Option<(&Self::Item, &[Self::Item])>; - - #[stable(feature = "core", since = "1.6.0")] - fn last(&self) -> Option<&Self::Item>; - - #[stable(feature = "core", since = "1.6.0")] - unsafe fn get_unchecked(&self, index: I) -> &I::Output - where I: SliceIndex; - #[stable(feature = "core", since = "1.6.0")] - fn as_ptr(&self) -> *const Self::Item; - - #[stable(feature = "core", since = "1.6.0")] - fn binary_search(&self, x: &Self::Item) -> Result - where Self::Item: Ord; - - #[stable(feature = "core", since = "1.6.0")] - fn binary_search_by<'a, F>(&'a self, f: F) -> Result - where F: FnMut(&'a Self::Item) -> Ordering; - - #[stable(feature = "slice_binary_search_by_key", since = "1.10.0")] - fn binary_search_by_key<'a, B, F>(&'a self, b: &B, f: F) -> Result - where F: FnMut(&'a Self::Item) -> B, - B: Ord; - - #[stable(feature = "core", since = "1.6.0")] - fn len(&self) -> usize; - - #[stable(feature = "core", since = "1.6.0")] - fn is_empty(&self) -> bool { self.len() == 0 } - - #[stable(feature = "core", since = "1.6.0")] - fn get_mut(&mut self, index: I) -> Option<&mut I::Output> - where I: SliceIndex; - #[stable(feature = "core", since = "1.6.0")] - fn iter_mut(&mut self) -> IterMut; - - #[stable(feature = "core", since = "1.6.0")] - fn first_mut(&mut self) -> Option<&mut Self::Item>; - - #[stable(feature = "core", since = "1.6.0")] - fn split_first_mut(&mut self) -> Option<(&mut Self::Item, &mut [Self::Item])>; - - #[stable(feature = "core", since = "1.6.0")] - fn split_last_mut(&mut self) -> Option<(&mut Self::Item, &mut [Self::Item])>; - - #[stable(feature = "core", since = "1.6.0")] - fn last_mut(&mut self) -> Option<&mut Self::Item>; - - #[stable(feature = "core", since = "1.6.0")] - fn split_mut

(&mut self, pred: P) -> SplitMut - where P: FnMut(&Self::Item) -> bool; - - #[stable(feature = "slice_rsplit", since = "1.27.0")] - fn rsplit_mut

(&mut self, pred: P) -> RSplitMut - where P: FnMut(&Self::Item) -> bool; - - #[stable(feature = "core", since = "1.6.0")] - fn splitn_mut

(&mut self, n: usize, pred: P) -> SplitNMut - where P: FnMut(&Self::Item) -> bool; - - #[stable(feature = "core", since = "1.6.0")] - fn rsplitn_mut

(&mut self, n: usize, pred: P) -> RSplitNMut - where P: FnMut(&Self::Item) -> bool; - - #[stable(feature = "core", since = "1.6.0")] - fn chunks_mut(&mut self, chunk_size: usize) -> ChunksMut; - - #[unstable(feature = "exact_chunks", issue = "47115")] - fn exact_chunks_mut(&mut self, size: usize) -> ExactChunksMut; - - #[stable(feature = "core", since = "1.6.0")] - fn swap(&mut self, a: usize, b: usize); - - #[stable(feature = "core", since = "1.6.0")] - fn split_at_mut(&mut self, mid: usize) -> (&mut [Self::Item], &mut [Self::Item]); - - #[stable(feature = "core", since = "1.6.0")] - fn reverse(&mut self); - - #[stable(feature = "core", since = "1.6.0")] - unsafe fn get_unchecked_mut(&mut self, index: I) -> &mut I::Output - where I: SliceIndex; - #[stable(feature = "core", since = "1.6.0")] - fn as_mut_ptr(&mut self) -> *mut Self::Item; - - #[stable(feature = "core", since = "1.6.0")] - fn contains(&self, x: &Self::Item) -> bool where Self::Item: PartialEq; - - #[stable(feature = "core", since = "1.6.0")] - fn starts_with(&self, needle: &[Self::Item]) -> bool where Self::Item: PartialEq; - - #[stable(feature = "core", since = "1.6.0")] - fn ends_with(&self, needle: &[Self::Item]) -> bool where Self::Item: PartialEq; - - #[stable(feature = "slice_rotate", since = "1.26.0")] - fn rotate_left(&mut self, mid: usize); - - #[stable(feature = "slice_rotate", since = "1.26.0")] - fn rotate_right(&mut self, k: usize); - - #[stable(feature = "clone_from_slice", since = "1.7.0")] - fn clone_from_slice(&mut self, src: &[Self::Item]) where Self::Item: Clone; - - #[stable(feature = "copy_from_slice", since = "1.9.0")] - fn copy_from_slice(&mut self, src: &[Self::Item]) where Self::Item: Copy; - - #[stable(feature = "swap_with_slice", since = "1.27.0")] - fn swap_with_slice(&mut self, src: &mut [Self::Item]); - - #[stable(feature = "sort_unstable", since = "1.20.0")] - fn sort_unstable(&mut self) - where Self::Item: Ord; - - #[stable(feature = "sort_unstable", since = "1.20.0")] - fn sort_unstable_by(&mut self, compare: F) - where F: FnMut(&Self::Item, &Self::Item) -> Ordering; - - #[stable(feature = "sort_unstable", since = "1.20.0")] - fn sort_unstable_by_key(&mut self, f: F) - where F: FnMut(&Self::Item) -> B, - B: Ord; -}} - // Use macros to be generic over const/mut macro_rules! slice_offset { ($ptr:expr, $by:expr) => {{ @@ -281,488 +106,9 @@ macro_rules! make_ref_mut { }}; } -#[unstable(feature = "core_slice_ext", - reason = "stable interface provided by `impl [T]` in later crates", - issue = "32110")] -impl SliceExt for [T] { - type Item = T; - - #[inline] - fn split_at(&self, mid: usize) -> (&[T], &[T]) { - (&self[..mid], &self[mid..]) - } - - #[inline] - fn iter(&self) -> Iter { - unsafe { - let p = if mem::size_of::() == 0 { - 1 as *const _ - } else { - let p = self.as_ptr(); - assume(!p.is_null()); - p - }; - - Iter { - ptr: p, - end: slice_offset!(p, self.len() as isize), - _marker: marker::PhantomData - } - } - } - - #[inline] - fn split

(&self, pred: P) -> Split - where P: FnMut(&T) -> bool - { - Split { - v: self, - pred, - finished: false - } - } - - #[inline] - fn rsplit

(&self, pred: P) -> RSplit - where P: FnMut(&T) -> bool - { - RSplit { inner: self.split(pred) } - } - - #[inline] - fn splitn

(&self, n: usize, pred: P) -> SplitN - where P: FnMut(&T) -> bool - { - SplitN { - inner: GenericSplitN { - iter: self.split(pred), - count: n - } - } - } - - #[inline] - fn rsplitn

(&self, n: usize, pred: P) -> RSplitN - where P: FnMut(&T) -> bool - { - RSplitN { - inner: GenericSplitN { - iter: self.rsplit(pred), - count: n - } - } - } - - #[inline] - fn windows(&self, size: usize) -> Windows { - assert!(size != 0); - Windows { v: self, size: size } - } - - #[inline] - fn chunks(&self, chunk_size: usize) -> Chunks { - assert!(chunk_size != 0); - Chunks { v: self, chunk_size: chunk_size } - } - - #[inline] - fn exact_chunks(&self, chunk_size: usize) -> ExactChunks { - assert!(chunk_size != 0); - let rem = self.len() % chunk_size; - let len = self.len() - rem; - ExactChunks { v: &self[..len], chunk_size: chunk_size} - } - - #[inline] - fn get(&self, index: I) -> Option<&I::Output> - where I: SliceIndex<[T]> - { - index.get(self) - } - - #[inline] - fn first(&self) -> Option<&T> { - if self.is_empty() { None } else { Some(&self[0]) } - } - - #[inline] - fn split_first(&self) -> Option<(&T, &[T])> { - if self.is_empty() { None } else { Some((&self[0], &self[1..])) } - } - - #[inline] - fn split_last(&self) -> Option<(&T, &[T])> { - let len = self.len(); - if len == 0 { None } else { Some((&self[len - 1], &self[..(len - 1)])) } - } - - #[inline] - fn last(&self) -> Option<&T> { - if self.is_empty() { None } else { Some(&self[self.len() - 1]) } - } - - #[inline] - unsafe fn get_unchecked(&self, index: I) -> &I::Output - where I: SliceIndex<[T]> - { - index.get_unchecked(self) - } - - #[inline] - fn as_ptr(&self) -> *const T { - self as *const [T] as *const T - } - - fn binary_search_by<'a, F>(&'a self, mut f: F) -> Result - where F: FnMut(&'a T) -> Ordering - { - let s = self; - let mut size = s.len(); - if size == 0 { - return Err(0); - } - let mut base = 0usize; - while size > 1 { - let half = size / 2; - let mid = base + half; - // mid is always in [0, size), that means mid is >= 0 and < size. - // mid >= 0: by definition - // mid < size: mid = size / 2 + size / 4 + size / 8 ... - let cmp = f(unsafe { s.get_unchecked(mid) }); - base = if cmp == Greater { base } else { mid }; - size -= half; - } - // base is always in [0, size) because base <= mid. - let cmp = f(unsafe { s.get_unchecked(base) }); - if cmp == Equal { Ok(base) } else { Err(base + (cmp == Less) as usize) } - } - - #[inline] - fn len(&self) -> usize { - unsafe { - mem::transmute::<&[T], Repr>(self).len - } - } - - #[inline] - fn get_mut(&mut self, index: I) -> Option<&mut I::Output> - where I: SliceIndex<[T]> - { - index.get_mut(self) - } - - #[inline] - fn split_at_mut(&mut self, mid: usize) -> (&mut [T], &mut [T]) { - let len = self.len(); - let ptr = self.as_mut_ptr(); - - unsafe { - assert!(mid <= len); - - (from_raw_parts_mut(ptr, mid), - from_raw_parts_mut(ptr.offset(mid as isize), len - mid)) - } - } - - #[inline] - fn iter_mut(&mut self) -> IterMut { - unsafe { - let p = if mem::size_of::() == 0 { - 1 as *mut _ - } else { - let p = self.as_mut_ptr(); - assume(!p.is_null()); - p - }; - - IterMut { - ptr: p, - end: slice_offset!(p, self.len() as isize), - _marker: marker::PhantomData - } - } - } - - #[inline] - fn last_mut(&mut self) -> Option<&mut T> { - let len = self.len(); - if len == 0 { return None; } - Some(&mut self[len - 1]) - } - - #[inline] - fn first_mut(&mut self) -> Option<&mut T> { - if self.is_empty() { None } else { Some(&mut self[0]) } - } - - #[inline] - fn split_first_mut(&mut self) -> Option<(&mut T, &mut [T])> { - if self.is_empty() { None } else { - let split = self.split_at_mut(1); - Some((&mut split.0[0], split.1)) - } - } - - #[inline] - fn split_last_mut(&mut self) -> Option<(&mut T, &mut [T])> { - let len = self.len(); - if len == 0 { None } else { - let split = self.split_at_mut(len - 1); - Some((&mut split.1[0], split.0)) - } - } - - #[inline] - fn split_mut

(&mut self, pred: P) -> SplitMut - where P: FnMut(&T) -> bool - { - SplitMut { v: self, pred: pred, finished: false } - } - - #[inline] - fn rsplit_mut

(&mut self, pred: P) -> RSplitMut - where P: FnMut(&T) -> bool - { - RSplitMut { inner: self.split_mut(pred) } - } - - #[inline] - fn splitn_mut

(&mut self, n: usize, pred: P) -> SplitNMut - where P: FnMut(&T) -> bool - { - SplitNMut { - inner: GenericSplitN { - iter: self.split_mut(pred), - count: n - } - } - } - - #[inline] - fn rsplitn_mut

(&mut self, n: usize, pred: P) -> RSplitNMut where - P: FnMut(&T) -> bool, - { - RSplitNMut { - inner: GenericSplitN { - iter: self.rsplit_mut(pred), - count: n - } - } - } - - #[inline] - fn chunks_mut(&mut self, chunk_size: usize) -> ChunksMut { - assert!(chunk_size != 0); - ChunksMut { v: self, chunk_size: chunk_size } - } - - #[inline] - fn exact_chunks_mut(&mut self, chunk_size: usize) -> ExactChunksMut { - assert!(chunk_size != 0); - let rem = self.len() % chunk_size; - let len = self.len() - rem; - ExactChunksMut { v: &mut self[..len], chunk_size: chunk_size} - } - - #[inline] - fn swap(&mut self, a: usize, b: usize) { - unsafe { - // Can't take two mutable loans from one vector, so instead just cast - // them to their raw pointers to do the swap - let pa: *mut T = &mut self[a]; - let pb: *mut T = &mut self[b]; - ptr::swap(pa, pb); - } - } - - fn reverse(&mut self) { - let mut i: usize = 0; - let ln = self.len(); - - // For very small types, all the individual reads in the normal - // path perform poorly. We can do better, given efficient unaligned - // load/store, by loading a larger chunk and reversing a register. - - // Ideally LLVM would do this for us, as it knows better than we do - // whether unaligned reads are efficient (since that changes between - // different ARM versions, for example) and what the best chunk size - // would be. Unfortunately, as of LLVM 4.0 (2017-05) it only unrolls - // the loop, so we need to do this ourselves. (Hypothesis: reverse - // is troublesome because the sides can be aligned differently -- - // will be, when the length is odd -- so there's no way of emitting - // pre- and postludes to use fully-aligned SIMD in the middle.) - - let fast_unaligned = - cfg!(any(target_arch = "x86", target_arch = "x86_64")); - - if fast_unaligned && mem::size_of::() == 1 { - // Use the llvm.bswap intrinsic to reverse u8s in a usize - let chunk = mem::size_of::(); - while i + chunk - 1 < ln / 2 { - unsafe { - let pa: *mut T = self.get_unchecked_mut(i); - let pb: *mut T = self.get_unchecked_mut(ln - i - chunk); - let va = ptr::read_unaligned(pa as *mut usize); - let vb = ptr::read_unaligned(pb as *mut usize); - ptr::write_unaligned(pa as *mut usize, vb.swap_bytes()); - ptr::write_unaligned(pb as *mut usize, va.swap_bytes()); - } - i += chunk; - } - } - - if fast_unaligned && mem::size_of::() == 2 { - // Use rotate-by-16 to reverse u16s in a u32 - let chunk = mem::size_of::() / 2; - while i + chunk - 1 < ln / 2 { - unsafe { - let pa: *mut T = self.get_unchecked_mut(i); - let pb: *mut T = self.get_unchecked_mut(ln - i - chunk); - let va = ptr::read_unaligned(pa as *mut u32); - let vb = ptr::read_unaligned(pb as *mut u32); - ptr::write_unaligned(pa as *mut u32, vb.rotate_left(16)); - ptr::write_unaligned(pb as *mut u32, va.rotate_left(16)); - } - i += chunk; - } - } - - while i < ln / 2 { - // Unsafe swap to avoid the bounds check in safe swap. - unsafe { - let pa: *mut T = self.get_unchecked_mut(i); - let pb: *mut T = self.get_unchecked_mut(ln - i - 1); - ptr::swap(pa, pb); - } - i += 1; - } - } - - #[inline] - unsafe fn get_unchecked_mut(&mut self, index: I) -> &mut I::Output - where I: SliceIndex<[T]> - { - index.get_unchecked_mut(self) - } - - #[inline] - fn as_mut_ptr(&mut self) -> *mut T { - self as *mut [T] as *mut T - } - - #[inline] - fn contains(&self, x: &T) -> bool where T: PartialEq { - x.slice_contains(self) - } - - #[inline] - fn starts_with(&self, needle: &[T]) -> bool where T: PartialEq { - let n = needle.len(); - self.len() >= n && needle == &self[..n] - } - - #[inline] - fn ends_with(&self, needle: &[T]) -> bool where T: PartialEq { - let (m, n) = (self.len(), needle.len()); - m >= n && needle == &self[m-n..] - } - - fn binary_search(&self, x: &T) -> Result - where T: Ord - { - self.binary_search_by(|p| p.cmp(x)) - } - - fn rotate_left(&mut self, mid: usize) { - assert!(mid <= self.len()); - let k = self.len() - mid; - - unsafe { - let p = self.as_mut_ptr(); - rotate::ptr_rotate(mid, p.offset(mid as isize), k); - } - } - - fn rotate_right(&mut self, k: usize) { - assert!(k <= self.len()); - let mid = self.len() - k; - - unsafe { - let p = self.as_mut_ptr(); - rotate::ptr_rotate(mid, p.offset(mid as isize), k); - } - } - - #[inline] - fn clone_from_slice(&mut self, src: &[T]) where T: Clone { - assert!(self.len() == src.len(), - "destination and source slices have different lengths"); - // NOTE: We need to explicitly slice them to the same length - // for bounds checking to be elided, and the optimizer will - // generate memcpy for simple cases (for example T = u8). - let len = self.len(); - let src = &src[..len]; - for i in 0..len { - self[i].clone_from(&src[i]); - } - } - - #[inline] - fn copy_from_slice(&mut self, src: &[T]) where T: Copy { - assert!(self.len() == src.len(), - "destination and source slices have different lengths"); - unsafe { - ptr::copy_nonoverlapping( - src.as_ptr(), self.as_mut_ptr(), self.len()); - } - } - - #[inline] - fn swap_with_slice(&mut self, src: &mut [T]) { - assert!(self.len() == src.len(), - "destination and source slices have different lengths"); - unsafe { - ptr::swap_nonoverlapping( - self.as_mut_ptr(), src.as_mut_ptr(), self.len()); - } - } - - #[inline] - fn binary_search_by_key<'a, B, F>(&'a self, b: &B, mut f: F) -> Result - where F: FnMut(&'a Self::Item) -> B, - B: Ord - { - self.binary_search_by(|k| f(k).cmp(b)) - } - - #[inline] - fn sort_unstable(&mut self) - where Self::Item: Ord - { - sort::quicksort(self, |a, b| a.lt(b)); - } - - #[inline] - fn sort_unstable_by(&mut self, mut compare: F) - where F: FnMut(&Self::Item, &Self::Item) -> Ordering - { - sort::quicksort(self, |a, b| compare(a, b) == Ordering::Less); - } - - #[inline] - fn sort_unstable_by_key(&mut self, mut f: F) - where F: FnMut(&Self::Item) -> B, - B: Ord - { - sort::quicksort(self, |a, b| f(a).lt(&f(b))); - } -} - -// FIXME: remove (inline) this macro and the SliceExt trait -// when updating to a bootstrap compiler that has the new lang items. -#[cfg_attr(stage0, macro_export)] -#[unstable(feature = "core_slice_ext", issue = "32110")] -macro_rules! slice_core_methods { () => { +#[lang = "slice"] +#[cfg(not(test))] +impl [T] { /// Returns the number of elements in the slice. /// /// # Examples @@ -774,7 +120,9 @@ macro_rules! slice_core_methods { () => { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn len(&self) -> usize { - SliceExt::len(self) + unsafe { + mem::transmute::<&[T], Repr>(self).len + } } /// Returns `true` if the slice has a length of 0. @@ -788,7 +136,7 @@ macro_rules! slice_core_methods { () => { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn is_empty(&self) -> bool { - SliceExt::is_empty(self) + self.len() == 0 } /// Returns the first element of the slice, or `None` if it is empty. @@ -805,7 +153,7 @@ macro_rules! slice_core_methods { () => { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn first(&self) -> Option<&T> { - SliceExt::first(self) + if self.is_empty() { None } else { Some(&self[0]) } } /// Returns a mutable pointer to the first element of the slice, or `None` if it is empty. @@ -823,7 +171,7 @@ macro_rules! slice_core_methods { () => { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn first_mut(&mut self) -> Option<&mut T> { - SliceExt::first_mut(self) + if self.is_empty() { None } else { Some(&mut self[0]) } } /// Returns the first and all the rest of the elements of the slice, or `None` if it is empty. @@ -841,7 +189,7 @@ macro_rules! slice_core_methods { () => { #[stable(feature = "slice_splits", since = "1.5.0")] #[inline] pub fn split_first(&self) -> Option<(&T, &[T])> { - SliceExt::split_first(self) + if self.is_empty() { None } else { Some((&self[0], &self[1..])) } } /// Returns the first and all the rest of the elements of the slice, or `None` if it is empty. @@ -861,7 +209,10 @@ macro_rules! slice_core_methods { () => { #[stable(feature = "slice_splits", since = "1.5.0")] #[inline] pub fn split_first_mut(&mut self) -> Option<(&mut T, &mut [T])> { - SliceExt::split_first_mut(self) + if self.is_empty() { None } else { + let split = self.split_at_mut(1); + Some((&mut split.0[0], split.1)) + } } /// Returns the last and all the rest of the elements of the slice, or `None` if it is empty. @@ -879,7 +230,8 @@ macro_rules! slice_core_methods { () => { #[stable(feature = "slice_splits", since = "1.5.0")] #[inline] pub fn split_last(&self) -> Option<(&T, &[T])> { - SliceExt::split_last(self) + let len = self.len(); + if len == 0 { None } else { Some((&self[len - 1], &self[..(len - 1)])) } } /// Returns the last and all the rest of the elements of the slice, or `None` if it is empty. @@ -899,7 +251,12 @@ macro_rules! slice_core_methods { () => { #[stable(feature = "slice_splits", since = "1.5.0")] #[inline] pub fn split_last_mut(&mut self) -> Option<(&mut T, &mut [T])> { - SliceExt::split_last_mut(self) + let len = self.len(); + if len == 0 { None } else { + let split = self.split_at_mut(len - 1); + Some((&mut split.1[0], split.0)) + } + } /// Returns the last element of the slice, or `None` if it is empty. @@ -916,7 +273,7 @@ macro_rules! slice_core_methods { () => { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn last(&self) -> Option<&T> { - SliceExt::last(self) + if self.is_empty() { None } else { Some(&self[self.len() - 1]) } } /// Returns a mutable pointer to the last item in the slice. @@ -934,7 +291,9 @@ macro_rules! slice_core_methods { () => { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn last_mut(&mut self) -> Option<&mut T> { - SliceExt::last_mut(self) + let len = self.len(); + if len == 0 { return None; } + Some(&mut self[len - 1]) } /// Returns a reference to an element or subslice depending on the type of @@ -959,7 +318,7 @@ macro_rules! slice_core_methods { () => { pub fn get(&self, index: I) -> Option<&I::Output> where I: SliceIndex { - SliceExt::get(self, index) + index.get(self) } /// Returns a mutable reference to an element or subslice depending on the @@ -982,7 +341,7 @@ macro_rules! slice_core_methods { () => { pub fn get_mut(&mut self, index: I) -> Option<&mut I::Output> where I: SliceIndex { - SliceExt::get_mut(self, index) + index.get_mut(self) } /// Returns a reference to an element or subslice, without doing bounds @@ -1007,7 +366,7 @@ macro_rules! slice_core_methods { () => { pub unsafe fn get_unchecked(&self, index: I) -> &I::Output where I: SliceIndex { - SliceExt::get_unchecked(self, index) + index.get_unchecked(self) } /// Returns a mutable reference to an element or subslice, without doing @@ -1034,7 +393,7 @@ macro_rules! slice_core_methods { () => { pub unsafe fn get_unchecked_mut(&mut self, index: I) -> &mut I::Output where I: SliceIndex { - SliceExt::get_unchecked_mut(self, index) + index.get_unchecked_mut(self) } /// Returns a raw pointer to the slice's buffer. @@ -1060,7 +419,7 @@ macro_rules! slice_core_methods { () => { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn as_ptr(&self) -> *const T { - SliceExt::as_ptr(self) + self as *const [T] as *const T } /// Returns an unsafe mutable pointer to the slice's buffer. @@ -1087,7 +446,7 @@ macro_rules! slice_core_methods { () => { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn as_mut_ptr(&mut self) -> *mut T { - SliceExt::as_mut_ptr(self) + self as *mut [T] as *mut T } /// Swaps two elements in the slice. @@ -1111,7 +470,13 @@ macro_rules! slice_core_methods { () => { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn swap(&mut self, a: usize, b: usize) { - SliceExt::swap(self, a, b) + unsafe { + // Can't take two mutable loans from one vector, so instead just cast + // them to their raw pointers to do the swap + let pa: *mut T = &mut self[a]; + let pb: *mut T = &mut self[b]; + ptr::swap(pa, pb); + } } /// Reverses the order of elements in the slice, in place. @@ -1126,7 +491,66 @@ macro_rules! slice_core_methods { () => { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn reverse(&mut self) { - SliceExt::reverse(self) + let mut i: usize = 0; + let ln = self.len(); + + // For very small types, all the individual reads in the normal + // path perform poorly. We can do better, given efficient unaligned + // load/store, by loading a larger chunk and reversing a register. + + // Ideally LLVM would do this for us, as it knows better than we do + // whether unaligned reads are efficient (since that changes between + // different ARM versions, for example) and what the best chunk size + // would be. Unfortunately, as of LLVM 4.0 (2017-05) it only unrolls + // the loop, so we need to do this ourselves. (Hypothesis: reverse + // is troublesome because the sides can be aligned differently -- + // will be, when the length is odd -- so there's no way of emitting + // pre- and postludes to use fully-aligned SIMD in the middle.) + + let fast_unaligned = + cfg!(any(target_arch = "x86", target_arch = "x86_64")); + + if fast_unaligned && mem::size_of::() == 1 { + // Use the llvm.bswap intrinsic to reverse u8s in a usize + let chunk = mem::size_of::(); + while i + chunk - 1 < ln / 2 { + unsafe { + let pa: *mut T = self.get_unchecked_mut(i); + let pb: *mut T = self.get_unchecked_mut(ln - i - chunk); + let va = ptr::read_unaligned(pa as *mut usize); + let vb = ptr::read_unaligned(pb as *mut usize); + ptr::write_unaligned(pa as *mut usize, vb.swap_bytes()); + ptr::write_unaligned(pb as *mut usize, va.swap_bytes()); + } + i += chunk; + } + } + + if fast_unaligned && mem::size_of::() == 2 { + // Use rotate-by-16 to reverse u16s in a u32 + let chunk = mem::size_of::() / 2; + while i + chunk - 1 < ln / 2 { + unsafe { + let pa: *mut T = self.get_unchecked_mut(i); + let pb: *mut T = self.get_unchecked_mut(ln - i - chunk); + let va = ptr::read_unaligned(pa as *mut u32); + let vb = ptr::read_unaligned(pb as *mut u32); + ptr::write_unaligned(pa as *mut u32, vb.rotate_left(16)); + ptr::write_unaligned(pb as *mut u32, va.rotate_left(16)); + } + i += chunk; + } + } + + while i < ln / 2 { + // Unsafe swap to avoid the bounds check in safe swap. + unsafe { + let pa: *mut T = self.get_unchecked_mut(i); + let pb: *mut T = self.get_unchecked_mut(ln - i - 1); + ptr::swap(pa, pb); + } + i += 1; + } } /// Returns an iterator over the slice. @@ -1145,7 +569,21 @@ macro_rules! slice_core_methods { () => { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn iter(&self) -> Iter { - SliceExt::iter(self) + unsafe { + let p = if mem::size_of::() == 0 { + 1 as *const _ + } else { + let p = self.as_ptr(); + assume(!p.is_null()); + p + }; + + Iter { + ptr: p, + end: slice_offset!(p, self.len() as isize), + _marker: marker::PhantomData + } + } } /// Returns an iterator that allows modifying each value. @@ -1162,7 +600,21 @@ macro_rules! slice_core_methods { () => { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn iter_mut(&mut self) -> IterMut { - SliceExt::iter_mut(self) + unsafe { + let p = if mem::size_of::() == 0 { + 1 as *mut _ + } else { + let p = self.as_mut_ptr(); + assume(!p.is_null()); + p + }; + + IterMut { + ptr: p, + end: slice_offset!(p, self.len() as isize), + _marker: marker::PhantomData + } + } } /// Returns an iterator over all contiguous windows of length @@ -1194,7 +646,8 @@ macro_rules! slice_core_methods { () => { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn windows(&self, size: usize) -> Windows { - SliceExt::windows(self, size) + assert!(size != 0); + Windows { v: self, size: size } } /// Returns an iterator over `chunk_size` elements of the slice at a @@ -1224,7 +677,8 @@ macro_rules! slice_core_methods { () => { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn chunks(&self, chunk_size: usize) -> Chunks { - SliceExt::chunks(self, chunk_size) + assert!(chunk_size != 0); + Chunks { v: self, chunk_size: chunk_size } } /// Returns an iterator over `chunk_size` elements of the slice at a @@ -1256,7 +710,10 @@ macro_rules! slice_core_methods { () => { #[unstable(feature = "exact_chunks", issue = "47115")] #[inline] pub fn exact_chunks(&self, chunk_size: usize) -> ExactChunks { - SliceExt::exact_chunks(self, chunk_size) + assert!(chunk_size != 0); + let rem = self.len() % chunk_size; + let len = self.len() - rem; + ExactChunks { v: &self[..len], chunk_size: chunk_size} } /// Returns an iterator over `chunk_size` elements of the slice at a time. @@ -1290,7 +747,8 @@ macro_rules! slice_core_methods { () => { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn chunks_mut(&mut self, chunk_size: usize) -> ChunksMut { - SliceExt::chunks_mut(self, chunk_size) + assert!(chunk_size != 0); + ChunksMut { v: self, chunk_size: chunk_size } } /// Returns an iterator over `chunk_size` elements of the slice at a time. @@ -1328,7 +786,10 @@ macro_rules! slice_core_methods { () => { #[unstable(feature = "exact_chunks", issue = "47115")] #[inline] pub fn exact_chunks_mut(&mut self, chunk_size: usize) -> ExactChunksMut { - SliceExt::exact_chunks_mut(self, chunk_size) + assert!(chunk_size != 0); + let rem = self.len() % chunk_size; + let len = self.len() - rem; + ExactChunksMut { v: &mut self[..len], chunk_size: chunk_size} } /// Divides one slice into two at an index. @@ -1367,7 +828,7 @@ macro_rules! slice_core_methods { () => { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn split_at(&self, mid: usize) -> (&[T], &[T]) { - SliceExt::split_at(self, mid) + (&self[..mid], &self[mid..]) } /// Divides one mutable slice into two at an index. @@ -1397,7 +858,15 @@ macro_rules! slice_core_methods { () => { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn split_at_mut(&mut self, mid: usize) -> (&mut [T], &mut [T]) { - SliceExt::split_at_mut(self, mid) + let len = self.len(); + let ptr = self.as_mut_ptr(); + + unsafe { + assert!(mid <= len); + + (from_raw_parts_mut(ptr, mid), + from_raw_parts_mut(ptr.offset(mid as isize), len - mid)) + } } /// Returns an iterator over subslices separated by elements that match @@ -1445,7 +914,11 @@ macro_rules! slice_core_methods { () => { pub fn split(&self, pred: F) -> Split where F: FnMut(&T) -> bool { - SliceExt::split(self, pred) + Split { + v: self, + pred, + finished: false + } } /// Returns an iterator over mutable subslices separated by elements that @@ -1466,7 +939,7 @@ macro_rules! slice_core_methods { () => { pub fn split_mut(&mut self, pred: F) -> SplitMut where F: FnMut(&T) -> bool { - SliceExt::split_mut(self, pred) + SplitMut { v: self, pred: pred, finished: false } } /// Returns an iterator over subslices separated by elements that match @@ -1501,7 +974,7 @@ macro_rules! slice_core_methods { () => { pub fn rsplit(&self, pred: F) -> RSplit where F: FnMut(&T) -> bool { - SliceExt::rsplit(self, pred) + RSplit { inner: self.split(pred) } } /// Returns an iterator over mutable subslices separated by elements that @@ -1526,7 +999,7 @@ macro_rules! slice_core_methods { () => { pub fn rsplit_mut(&mut self, pred: F) -> RSplitMut where F: FnMut(&T) -> bool { - SliceExt::rsplit_mut(self, pred) + RSplitMut { inner: self.split_mut(pred) } } /// Returns an iterator over subslices separated by elements that match @@ -1553,7 +1026,12 @@ macro_rules! slice_core_methods { () => { pub fn splitn(&self, n: usize, pred: F) -> SplitN where F: FnMut(&T) -> bool { - SliceExt::splitn(self, n, pred) + SplitN { + inner: GenericSplitN { + iter: self.split(pred), + count: n + } + } } /// Returns an iterator over subslices separated by elements that match @@ -1578,7 +1056,12 @@ macro_rules! slice_core_methods { () => { pub fn splitn_mut(&mut self, n: usize, pred: F) -> SplitNMut where F: FnMut(&T) -> bool { - SliceExt::splitn_mut(self, n, pred) + SplitNMut { + inner: GenericSplitN { + iter: self.split_mut(pred), + count: n + } + } } /// Returns an iterator over subslices separated by elements that match @@ -1606,7 +1089,12 @@ macro_rules! slice_core_methods { () => { pub fn rsplitn(&self, n: usize, pred: F) -> RSplitN where F: FnMut(&T) -> bool { - SliceExt::rsplitn(self, n, pred) + RSplitN { + inner: GenericSplitN { + iter: self.rsplit(pred), + count: n + } + } } /// Returns an iterator over subslices separated by elements that match @@ -1632,7 +1120,12 @@ macro_rules! slice_core_methods { () => { pub fn rsplitn_mut(&mut self, n: usize, pred: F) -> RSplitNMut where F: FnMut(&T) -> bool { - SliceExt::rsplitn_mut(self, n, pred) + RSplitNMut { + inner: GenericSplitN { + iter: self.rsplit_mut(pred), + count: n + } + } } /// Returns `true` if the slice contains an element with the given value. @@ -1648,7 +1141,7 @@ macro_rules! slice_core_methods { () => { pub fn contains(&self, x: &T) -> bool where T: PartialEq { - SliceExt::contains(self, x) + x.slice_contains(self) } /// Returns `true` if `needle` is a prefix of the slice. @@ -1675,7 +1168,8 @@ macro_rules! slice_core_methods { () => { pub fn starts_with(&self, needle: &[T]) -> bool where T: PartialEq { - SliceExt::starts_with(self, needle) + let n = needle.len(); + self.len() >= n && needle == &self[..n] } /// Returns `true` if `needle` is a suffix of the slice. @@ -1702,7 +1196,8 @@ macro_rules! slice_core_methods { () => { pub fn ends_with(&self, needle: &[T]) -> bool where T: PartialEq { - SliceExt::ends_with(self, needle) + let (m, n) = (self.len(), needle.len()); + m >= n && needle == &self[m-n..] } /// Binary searches this sorted slice for a given element. @@ -1731,7 +1226,7 @@ macro_rules! slice_core_methods { () => { pub fn binary_search(&self, x: &T) -> Result where T: Ord { - SliceExt::binary_search(self, x) + self.binary_search_by(|p| p.cmp(x)) } /// Binary searches this sorted slice with a comparator function. @@ -1767,10 +1262,29 @@ macro_rules! slice_core_methods { () => { /// ``` #[stable(feature = "rust1", since = "1.0.0")] #[inline] - pub fn binary_search_by<'a, F>(&'a self, f: F) -> Result + pub fn binary_search_by<'a, F>(&'a self, mut f: F) -> Result where F: FnMut(&'a T) -> Ordering { - SliceExt::binary_search_by(self, f) + let s = self; + let mut size = s.len(); + if size == 0 { + return Err(0); + } + let mut base = 0usize; + while size > 1 { + let half = size / 2; + let mid = base + half; + // mid is always in [0, size), that means mid is >= 0 and < size. + // mid >= 0: by definition + // mid < size: mid = size / 2 + size / 4 + size / 8 ... + let cmp = f(unsafe { s.get_unchecked(mid) }); + base = if cmp == Greater { base } else { mid }; + size -= half; + } + // base is always in [0, size) because base <= mid. + let cmp = f(unsafe { s.get_unchecked(base) }); + if cmp == Equal { Ok(base) } else { Err(base + (cmp == Less) as usize) } + } /// Binary searches this sorted slice with a key extraction function. @@ -1805,11 +1319,11 @@ macro_rules! slice_core_methods { () => { /// ``` #[stable(feature = "slice_binary_search_by_key", since = "1.10.0")] #[inline] - pub fn binary_search_by_key<'a, B, F>(&'a self, b: &B, f: F) -> Result + pub fn binary_search_by_key<'a, B, F>(&'a self, b: &B, mut f: F) -> Result where F: FnMut(&'a T) -> B, B: Ord { - SliceExt::binary_search_by_key(self, b, f) + self.binary_search_by(|k| f(k).cmp(b)) } /// Sorts the slice, but may not preserve the order of equal elements. @@ -1843,7 +1357,7 @@ macro_rules! slice_core_methods { () => { pub fn sort_unstable(&mut self) where T: Ord { - SliceExt::sort_unstable(self); + sort::quicksort(self, |a, b| a.lt(b)); } /// Sorts the slice with a comparator function, but may not preserve the order of equal @@ -1878,10 +1392,10 @@ macro_rules! slice_core_methods { () => { /// [pdqsort]: https://github.com/orlp/pdqsort #[stable(feature = "sort_unstable", since = "1.20.0")] #[inline] - pub fn sort_unstable_by(&mut self, compare: F) + pub fn sort_unstable_by(&mut self, mut compare: F) where F: FnMut(&T, &T) -> Ordering { - SliceExt::sort_unstable_by(self, compare); + sort::quicksort(self, |a, b| compare(a, b) == Ordering::Less); } /// Sorts the slice with a key extraction function, but may not preserve the order of equal @@ -1910,10 +1424,10 @@ macro_rules! slice_core_methods { () => { /// [pdqsort]: https://github.com/orlp/pdqsort #[stable(feature = "sort_unstable", since = "1.20.0")] #[inline] - pub fn sort_unstable_by_key(&mut self, f: F) + pub fn sort_unstable_by_key(&mut self, mut f: F) where F: FnMut(&T) -> K, K: Ord { - SliceExt::sort_unstable_by_key(self, f); + sort::quicksort(self, |a, b| f(a).lt(&f(b))); } /// Rotates the slice in-place such that the first `mid` elements of the @@ -1948,7 +1462,13 @@ macro_rules! slice_core_methods { () => { /// ``` #[stable(feature = "slice_rotate", since = "1.26.0")] pub fn rotate_left(&mut self, mid: usize) { - SliceExt::rotate_left(self, mid); + assert!(mid <= self.len()); + let k = self.len() - mid; + + unsafe { + let p = self.as_mut_ptr(); + rotate::ptr_rotate(mid, p.offset(mid as isize), k); + } } /// Rotates the slice in-place such that the first `self.len() - k` @@ -1983,7 +1503,13 @@ macro_rules! slice_core_methods { () => { /// ``` #[stable(feature = "slice_rotate", since = "1.26.0")] pub fn rotate_right(&mut self, k: usize) { - SliceExt::rotate_right(self, k); + assert!(k <= self.len()); + let mid = self.len() - k; + + unsafe { + let p = self.as_mut_ptr(); + rotate::ptr_rotate(mid, p.offset(mid as isize), k); + } } /// Copies the elements from `src` into `self`. @@ -2040,7 +1566,17 @@ macro_rules! slice_core_methods { () => { /// [`split_at_mut`]: #method.split_at_mut #[stable(feature = "clone_from_slice", since = "1.7.0")] pub fn clone_from_slice(&mut self, src: &[T]) where T: Clone { - SliceExt::clone_from_slice(self, src) + assert!(self.len() == src.len(), + "destination and source slices have different lengths"); + // NOTE: We need to explicitly slice them to the same length + // for bounds checking to be elided, and the optimizer will + // generate memcpy for simple cases (for example T = u8). + let len = self.len(); + let src = &src[..len]; + for i in 0..len { + self[i].clone_from(&src[i]); + } + } /// Copies all elements from `src` into `self`, using a memcpy. @@ -2096,7 +1632,12 @@ macro_rules! slice_core_methods { () => { /// [`split_at_mut`]: #method.split_at_mut #[stable(feature = "copy_from_slice", since = "1.9.0")] pub fn copy_from_slice(&mut self, src: &[T]) where T: Copy { - SliceExt::copy_from_slice(self, src) + assert!(self.len() == src.len(), + "destination and source slices have different lengths"); + unsafe { + ptr::copy_nonoverlapping( + src.as_ptr(), self.as_mut_ptr(), self.len()); + } } /// Swaps all elements in `self` with those in `other`. @@ -2148,22 +1689,18 @@ macro_rules! slice_core_methods { () => { /// [`split_at_mut`]: #method.split_at_mut #[stable(feature = "swap_with_slice", since = "1.27.0")] pub fn swap_with_slice(&mut self, other: &mut [T]) { - SliceExt::swap_with_slice(self, other) + assert!(self.len() == other.len(), + "destination and source slices have different lengths"); + unsafe { + ptr::swap_nonoverlapping( + self.as_mut_ptr(), other.as_mut_ptr(), self.len()); + } } -}} - -#[lang = "slice"] -#[cfg(not(test))] -#[cfg(not(stage0))] -impl [T] { - slice_core_methods!(); } -// FIXME: remove (inline) this macro -// when updating to a bootstrap compiler that has the new lang items. -#[cfg_attr(stage0, macro_export)] -#[unstable(feature = "core_slice_ext", issue = "32110")] -macro_rules! slice_u8_core_methods { () => { +#[lang = "slice_u8"] +#[cfg(not(test))] +impl [u8] { /// Checks if all bytes in this slice are within the ASCII range. #[stable(feature = "ascii_methods_on_intrinsics", since = "1.23.0")] #[inline] @@ -2217,13 +1754,7 @@ macro_rules! slice_u8_core_methods { () => { byte.make_ascii_lowercase(); } } -}} -#[lang = "slice_u8"] -#[cfg(not(test))] -#[cfg(not(stage0))] -impl [u8] { - slice_u8_core_methods!(); } #[stable(feature = "rust1", since = "1.0.0")] diff --git a/src/libcore/str/mod.rs b/src/libcore/str/mod.rs index df7b2f25a86..82bead0ab46 100644 --- a/src/libcore/str/mod.rs +++ b/src/libcore/str/mod.rs @@ -2097,119 +2097,7 @@ mod traits { (..self.end+1).index_mut(slice) } } - -} - -public_in_stage0! { -{ -/// Methods for string slices -#[allow(missing_docs)] -#[doc(hidden)] -#[unstable(feature = "core_str_ext", - reason = "stable interface provided by `impl str` in later crates", - issue = "32110")] } -trait StrExt { - // NB there are no docs here are they're all located on the StrExt trait in - // liballoc, not here. - - #[stable(feature = "core", since = "1.6.0")] - fn contains<'a, P: Pattern<'a>>(&'a self, pat: P) -> bool; - #[stable(feature = "core", since = "1.6.0")] - fn chars(&self) -> Chars; - #[stable(feature = "core", since = "1.6.0")] - fn bytes(&self) -> Bytes; - #[stable(feature = "core", since = "1.6.0")] - fn char_indices(&self) -> CharIndices; - #[stable(feature = "core", since = "1.6.0")] - fn split<'a, P: Pattern<'a>>(&'a self, pat: P) -> Split<'a, P>; - #[stable(feature = "core", since = "1.6.0")] - fn rsplit<'a, P: Pattern<'a>>(&'a self, pat: P) -> RSplit<'a, P> - where P::Searcher: ReverseSearcher<'a>; - #[stable(feature = "core", since = "1.6.0")] - fn splitn<'a, P: Pattern<'a>>(&'a self, count: usize, pat: P) -> SplitN<'a, P>; - #[stable(feature = "core", since = "1.6.0")] - fn rsplitn<'a, P: Pattern<'a>>(&'a self, count: usize, pat: P) -> RSplitN<'a, P> - where P::Searcher: ReverseSearcher<'a>; - #[stable(feature = "core", since = "1.6.0")] - fn split_terminator<'a, P: Pattern<'a>>(&'a self, pat: P) -> SplitTerminator<'a, P>; - #[stable(feature = "core", since = "1.6.0")] - fn rsplit_terminator<'a, P: Pattern<'a>>(&'a self, pat: P) -> RSplitTerminator<'a, P> - where P::Searcher: ReverseSearcher<'a>; - #[stable(feature = "core", since = "1.6.0")] - fn matches<'a, P: Pattern<'a>>(&'a self, pat: P) -> Matches<'a, P>; - #[stable(feature = "core", since = "1.6.0")] - fn rmatches<'a, P: Pattern<'a>>(&'a self, pat: P) -> RMatches<'a, P> - where P::Searcher: ReverseSearcher<'a>; - #[stable(feature = "core", since = "1.6.0")] - fn match_indices<'a, P: Pattern<'a>>(&'a self, pat: P) -> MatchIndices<'a, P>; - #[stable(feature = "core", since = "1.6.0")] - fn rmatch_indices<'a, P: Pattern<'a>>(&'a self, pat: P) -> RMatchIndices<'a, P> - where P::Searcher: ReverseSearcher<'a>; - #[stable(feature = "core", since = "1.6.0")] - fn lines(&self) -> Lines; - #[stable(feature = "core", since = "1.6.0")] - #[rustc_deprecated(since = "1.6.0", reason = "use lines() instead now")] - #[allow(deprecated)] - fn lines_any(&self) -> LinesAny; - #[stable(feature = "str_checked_slicing", since = "1.20.0")] - fn get>(&self, i: I) -> Option<&I::Output>; - #[stable(feature = "str_checked_slicing", since = "1.20.0")] - fn get_mut>(&mut self, i: I) -> Option<&mut I::Output>; - #[stable(feature = "str_checked_slicing", since = "1.20.0")] - unsafe fn get_unchecked>(&self, i: I) -> &I::Output; - #[stable(feature = "str_checked_slicing", since = "1.20.0")] - unsafe fn get_unchecked_mut>(&mut self, i: I) -> &mut I::Output; - #[stable(feature = "core", since = "1.6.0")] - unsafe fn slice_unchecked(&self, begin: usize, end: usize) -> &str; - #[stable(feature = "core", since = "1.6.0")] - unsafe fn slice_mut_unchecked(&mut self, begin: usize, end: usize) -> &mut str; - #[stable(feature = "core", since = "1.6.0")] - fn starts_with<'a, P: Pattern<'a>>(&'a self, pat: P) -> bool; - #[stable(feature = "core", since = "1.6.0")] - fn ends_with<'a, P: Pattern<'a>>(&'a self, pat: P) -> bool - where P::Searcher: ReverseSearcher<'a>; - #[stable(feature = "core", since = "1.6.0")] - fn trim_matches<'a, P: Pattern<'a>>(&'a self, pat: P) -> &'a str - where P::Searcher: DoubleEndedSearcher<'a>; - #[stable(feature = "core", since = "1.6.0")] - fn trim_left_matches<'a, P: Pattern<'a>>(&'a self, pat: P) -> &'a str; - #[stable(feature = "core", since = "1.6.0")] - fn trim_right_matches<'a, P: Pattern<'a>>(&'a self, pat: P) -> &'a str - where P::Searcher: ReverseSearcher<'a>; - #[stable(feature = "is_char_boundary", since = "1.9.0")] - fn is_char_boundary(&self, index: usize) -> bool; - #[stable(feature = "core", since = "1.6.0")] - fn as_bytes(&self) -> &[u8]; - #[stable(feature = "str_mut_extras", since = "1.20.0")] - unsafe fn as_bytes_mut(&mut self) -> &mut [u8]; - #[stable(feature = "core", since = "1.6.0")] - fn find<'a, P: Pattern<'a>>(&'a self, pat: P) -> Option; - #[stable(feature = "core", since = "1.6.0")] - fn rfind<'a, P: Pattern<'a>>(&'a self, pat: P) -> Option - where P::Searcher: ReverseSearcher<'a>; - fn find_str<'a, P: Pattern<'a>>(&'a self, pat: P) -> Option; - #[stable(feature = "core", since = "1.6.0")] - fn split_at(&self, mid: usize) -> (&str, &str); - #[stable(feature = "core", since = "1.6.0")] - fn split_at_mut(&mut self, mid: usize) -> (&mut str, &mut str); - #[stable(feature = "core", since = "1.6.0")] - fn as_ptr(&self) -> *const u8; - #[stable(feature = "core", since = "1.6.0")] - fn len(&self) -> usize; - #[stable(feature = "core", since = "1.6.0")] - fn is_empty(&self) -> bool; - #[stable(feature = "core", since = "1.6.0")] - fn parse(&self) -> Result; - #[stable(feature = "split_whitespace", since = "1.1.0")] - fn split_whitespace<'a>(&'a self) -> SplitWhitespace<'a>; - #[stable(feature = "rust1", since = "1.0.0")] - fn trim(&self) -> &str; - #[stable(feature = "rust1", since = "1.0.0")] - fn trim_left(&self) -> &str; - #[stable(feature = "rust1", since = "1.0.0")] - fn trim_right(&self) -> &str; -}} // truncate `&str` to length at most equal to `max` // return `true` if it were truncated, and the new str. @@ -2255,307 +2143,9 @@ fn slice_error_fail(s: &str, begin: usize, end: usize) -> ! { index, ch, char_range, s_trunc, ellipsis); } -#[stable(feature = "core", since = "1.6.0")] -impl StrExt for str { - #[inline] - fn contains<'a, P: Pattern<'a>>(&'a self, pat: P) -> bool { - pat.is_contained_in(self) - } - - #[inline] - fn chars(&self) -> Chars { - Chars{iter: self.as_bytes().iter()} - } - - #[inline] - fn bytes(&self) -> Bytes { - Bytes(self.as_bytes().iter().cloned()) - } - - #[inline] - fn char_indices(&self) -> CharIndices { - CharIndices { front_offset: 0, iter: self.chars() } - } - - #[inline] - fn split<'a, P: Pattern<'a>>(&'a self, pat: P) -> Split<'a, P> { - Split(SplitInternal { - start: 0, - end: self.len(), - matcher: pat.into_searcher(self), - allow_trailing_empty: true, - finished: false, - }) - } - - #[inline] - fn rsplit<'a, P: Pattern<'a>>(&'a self, pat: P) -> RSplit<'a, P> - where P::Searcher: ReverseSearcher<'a> - { - RSplit(self.split(pat).0) - } - - #[inline] - fn splitn<'a, P: Pattern<'a>>(&'a self, count: usize, pat: P) -> SplitN<'a, P> { - SplitN(SplitNInternal { - iter: self.split(pat).0, - count, - }) - } - - #[inline] - fn rsplitn<'a, P: Pattern<'a>>(&'a self, count: usize, pat: P) -> RSplitN<'a, P> - where P::Searcher: ReverseSearcher<'a> - { - RSplitN(self.splitn(count, pat).0) - } - - #[inline] - fn split_terminator<'a, P: Pattern<'a>>(&'a self, pat: P) -> SplitTerminator<'a, P> { - SplitTerminator(SplitInternal { - allow_trailing_empty: false, - ..self.split(pat).0 - }) - } - - #[inline] - fn rsplit_terminator<'a, P: Pattern<'a>>(&'a self, pat: P) -> RSplitTerminator<'a, P> - where P::Searcher: ReverseSearcher<'a> - { - RSplitTerminator(self.split_terminator(pat).0) - } - - #[inline] - fn matches<'a, P: Pattern<'a>>(&'a self, pat: P) -> Matches<'a, P> { - Matches(MatchesInternal(pat.into_searcher(self))) - } - - #[inline] - fn rmatches<'a, P: Pattern<'a>>(&'a self, pat: P) -> RMatches<'a, P> - where P::Searcher: ReverseSearcher<'a> - { - RMatches(self.matches(pat).0) - } - - #[inline] - fn match_indices<'a, P: Pattern<'a>>(&'a self, pat: P) -> MatchIndices<'a, P> { - MatchIndices(MatchIndicesInternal(pat.into_searcher(self))) - } - - #[inline] - fn rmatch_indices<'a, P: Pattern<'a>>(&'a self, pat: P) -> RMatchIndices<'a, P> - where P::Searcher: ReverseSearcher<'a> - { - RMatchIndices(self.match_indices(pat).0) - } - #[inline] - fn lines(&self) -> Lines { - Lines(self.split_terminator('\n').map(LinesAnyMap)) - } - - #[inline] - #[allow(deprecated)] - fn lines_any(&self) -> LinesAny { - LinesAny(self.lines()) - } - - #[inline] - fn get>(&self, i: I) -> Option<&I::Output> { - i.get(self) - } - - #[inline] - fn get_mut>(&mut self, i: I) -> Option<&mut I::Output> { - i.get_mut(self) - } - - #[inline] - unsafe fn get_unchecked>(&self, i: I) -> &I::Output { - i.get_unchecked(self) - } - - #[inline] - unsafe fn get_unchecked_mut>(&mut self, i: I) -> &mut I::Output { - i.get_unchecked_mut(self) - } - - #[inline] - unsafe fn slice_unchecked(&self, begin: usize, end: usize) -> &str { - (begin..end).get_unchecked(self) - } - - #[inline] - unsafe fn slice_mut_unchecked(&mut self, begin: usize, end: usize) -> &mut str { - (begin..end).get_unchecked_mut(self) - } - - #[inline] - fn starts_with<'a, P: Pattern<'a>>(&'a self, pat: P) -> bool { - pat.is_prefix_of(self) - } - - #[inline] - fn ends_with<'a, P: Pattern<'a>>(&'a self, pat: P) -> bool - where P::Searcher: ReverseSearcher<'a> - { - pat.is_suffix_of(self) - } - - #[inline] - fn trim_matches<'a, P: Pattern<'a>>(&'a self, pat: P) -> &'a str - where P::Searcher: DoubleEndedSearcher<'a> - { - let mut i = 0; - let mut j = 0; - let mut matcher = pat.into_searcher(self); - if let Some((a, b)) = matcher.next_reject() { - i = a; - j = b; // Remember earliest known match, correct it below if - // last match is different - } - if let Some((_, b)) = matcher.next_reject_back() { - j = b; - } - unsafe { - // Searcher is known to return valid indices - self.slice_unchecked(i, j) - } - } - - #[inline] - fn trim_left_matches<'a, P: Pattern<'a>>(&'a self, pat: P) -> &'a str { - let mut i = self.len(); - let mut matcher = pat.into_searcher(self); - if let Some((a, _)) = matcher.next_reject() { - i = a; - } - unsafe { - // Searcher is known to return valid indices - self.slice_unchecked(i, self.len()) - } - } - - #[inline] - fn trim_right_matches<'a, P: Pattern<'a>>(&'a self, pat: P) -> &'a str - where P::Searcher: ReverseSearcher<'a> - { - let mut j = 0; - let mut matcher = pat.into_searcher(self); - if let Some((_, b)) = matcher.next_reject_back() { - j = b; - } - unsafe { - // Searcher is known to return valid indices - self.slice_unchecked(0, j) - } - } - - #[inline] - fn is_char_boundary(&self, index: usize) -> bool { - // 0 and len are always ok. - // Test for 0 explicitly so that it can optimize out the check - // easily and skip reading string data for that case. - if index == 0 || index == self.len() { return true; } - match self.as_bytes().get(index) { - None => false, - // This is bit magic equivalent to: b < 128 || b >= 192 - Some(&b) => (b as i8) >= -0x40, - } - } - - #[inline] - fn as_bytes(&self) -> &[u8] { - unsafe { &*(self as *const str as *const [u8]) } - } - - #[inline] - unsafe fn as_bytes_mut(&mut self) -> &mut [u8] { - &mut *(self as *mut str as *mut [u8]) - } - - fn find<'a, P: Pattern<'a>>(&'a self, pat: P) -> Option { - pat.into_searcher(self).next_match().map(|(i, _)| i) - } - - fn rfind<'a, P: Pattern<'a>>(&'a self, pat: P) -> Option - where P::Searcher: ReverseSearcher<'a> - { - pat.into_searcher(self).next_match_back().map(|(i, _)| i) - } - - fn find_str<'a, P: Pattern<'a>>(&'a self, pat: P) -> Option { - self.find(pat) - } - - #[inline] - fn split_at(&self, mid: usize) -> (&str, &str) { - // is_char_boundary checks that the index is in [0, .len()] - if self.is_char_boundary(mid) { - unsafe { - (self.slice_unchecked(0, mid), - self.slice_unchecked(mid, self.len())) - } - } else { - slice_error_fail(self, 0, mid) - } - } - - fn split_at_mut(&mut self, mid: usize) -> (&mut str, &mut str) { - // is_char_boundary checks that the index is in [0, .len()] - if self.is_char_boundary(mid) { - let len = self.len(); - let ptr = self.as_ptr() as *mut u8; - unsafe { - (from_raw_parts_mut(ptr, mid), - from_raw_parts_mut(ptr.offset(mid as isize), len - mid)) - } - } else { - slice_error_fail(self, 0, mid) - } - } - - #[inline] - fn as_ptr(&self) -> *const u8 { - self as *const str as *const u8 - } - - #[inline] - fn len(&self) -> usize { - self.as_bytes().len() - } - - #[inline] - fn is_empty(&self) -> bool { self.len() == 0 } - - #[inline] - fn parse(&self) -> Result { FromStr::from_str(self) } - - #[inline] - fn split_whitespace(&self) -> SplitWhitespace { - SplitWhitespace { inner: self.split(IsWhitespace).filter(IsNotEmpty) } - } - - #[inline] - fn trim(&self) -> &str { - self.trim_matches(|c: char| c.is_whitespace()) - } - - #[inline] - fn trim_left(&self) -> &str { - self.trim_left_matches(|c: char| c.is_whitespace()) - } - - #[inline] - fn trim_right(&self) -> &str { - self.trim_right_matches(|c: char| c.is_whitespace()) - } -} - -// FIXME: remove (inline) this macro and the SliceExt trait -// when updating to a bootstrap compiler that has the new lang items. -#[cfg_attr(stage0, macro_export)] -#[unstable(feature = "core_str_ext", issue = "32110")] -macro_rules! str_core_methods { () => { +#[lang = "str"] +#[cfg(not(test))] +impl str { /// Returns the length of `self`. /// /// This length is in bytes, not [`char`]s or graphemes. In other words, @@ -2577,7 +2167,7 @@ macro_rules! str_core_methods { () => { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn len(&self) -> usize { - StrExt::len(self) + self.as_bytes().len() } /// Returns `true` if `self` has a length of zero bytes. @@ -2596,7 +2186,7 @@ macro_rules! str_core_methods { () => { #[inline] #[stable(feature = "rust1", since = "1.0.0")] pub fn is_empty(&self) -> bool { - StrExt::is_empty(self) + self.len() == 0 } /// Checks that `index`-th byte lies at the start and/or end of a @@ -2626,7 +2216,15 @@ macro_rules! str_core_methods { () => { #[stable(feature = "is_char_boundary", since = "1.9.0")] #[inline] pub fn is_char_boundary(&self, index: usize) -> bool { - StrExt::is_char_boundary(self, index) + // 0 and len are always ok. + // Test for 0 explicitly so that it can optimize out the check + // easily and skip reading string data for that case. + if index == 0 || index == self.len() { return true; } + match self.as_bytes().get(index) { + None => false, + // This is bit magic equivalent to: b < 128 || b >= 192 + Some(&b) => (b as i8) >= -0x40, + } } /// Converts a string slice to a byte slice. To convert the byte slice back @@ -2645,7 +2243,7 @@ macro_rules! str_core_methods { () => { #[stable(feature = "rust1", since = "1.0.0")] #[inline(always)] pub fn as_bytes(&self) -> &[u8] { - StrExt::as_bytes(self) + unsafe { &*(self as *const str as *const [u8]) } } /// Converts a mutable string slice to a mutable byte slice. To convert the @@ -2684,7 +2282,7 @@ macro_rules! str_core_methods { () => { #[stable(feature = "str_mut_extras", since = "1.20.0")] #[inline(always)] pub unsafe fn as_bytes_mut(&mut self) -> &mut [u8] { - StrExt::as_bytes_mut(self) + &mut *(self as *mut str as *mut [u8]) } /// Converts a string slice to a raw pointer. @@ -2706,7 +2304,7 @@ macro_rules! str_core_methods { () => { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn as_ptr(&self) -> *const u8 { - StrExt::as_ptr(self) + self as *const str as *const u8 } /// Returns a subslice of `str`. @@ -2733,7 +2331,7 @@ macro_rules! str_core_methods { () => { #[stable(feature = "str_checked_slicing", since = "1.20.0")] #[inline] pub fn get>(&self, i: I) -> Option<&I::Output> { - StrExt::get(self, i) + i.get(self) } /// Returns a mutable subslice of `str`. @@ -2767,7 +2365,7 @@ macro_rules! str_core_methods { () => { #[stable(feature = "str_checked_slicing", since = "1.20.0")] #[inline] pub fn get_mut>(&mut self, i: I) -> Option<&mut I::Output> { - StrExt::get_mut(self, i) + i.get_mut(self) } /// Returns a unchecked subslice of `str`. @@ -2799,7 +2397,7 @@ macro_rules! str_core_methods { () => { #[stable(feature = "str_checked_slicing", since = "1.20.0")] #[inline] pub unsafe fn get_unchecked>(&self, i: I) -> &I::Output { - StrExt::get_unchecked(self, i) + i.get_unchecked(self) } /// Returns a mutable, unchecked subslice of `str`. @@ -2831,7 +2429,7 @@ macro_rules! str_core_methods { () => { #[stable(feature = "str_checked_slicing", since = "1.20.0")] #[inline] pub unsafe fn get_unchecked_mut>(&mut self, i: I) -> &mut I::Output { - StrExt::get_unchecked_mut(self, i) + i.get_unchecked_mut(self) } /// Creates a string slice from another string slice, bypassing safety @@ -2880,7 +2478,7 @@ macro_rules! str_core_methods { () => { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub unsafe fn slice_unchecked(&self, begin: usize, end: usize) -> &str { - StrExt::slice_unchecked(self, begin, end) + (begin..end).get_unchecked(self) } /// Creates a string slice from another string slice, bypassing safety @@ -2910,7 +2508,7 @@ macro_rules! str_core_methods { () => { #[stable(feature = "str_slice_mut", since = "1.5.0")] #[inline] pub unsafe fn slice_mut_unchecked(&mut self, begin: usize, end: usize) -> &mut str { - StrExt::slice_mut_unchecked(self, begin, end) + (begin..end).get_unchecked_mut(self) } /// Divide one string slice into two at an index. @@ -2946,7 +2544,15 @@ macro_rules! str_core_methods { () => { #[inline] #[stable(feature = "str_split_at", since = "1.4.0")] pub fn split_at(&self, mid: usize) -> (&str, &str) { - StrExt::split_at(self, mid) + // is_char_boundary checks that the index is in [0, .len()] + if self.is_char_boundary(mid) { + unsafe { + (self.slice_unchecked(0, mid), + self.slice_unchecked(mid, self.len())) + } + } else { + slice_error_fail(self, 0, mid) + } } /// Divide one mutable string slice into two at an index. @@ -2983,7 +2589,17 @@ macro_rules! str_core_methods { () => { #[inline] #[stable(feature = "str_split_at", since = "1.4.0")] pub fn split_at_mut(&mut self, mid: usize) -> (&mut str, &mut str) { - StrExt::split_at_mut(self, mid) + // is_char_boundary checks that the index is in [0, .len()] + if self.is_char_boundary(mid) { + let len = self.len(); + let ptr = self.as_ptr() as *mut u8; + unsafe { + (from_raw_parts_mut(ptr, mid), + from_raw_parts_mut(ptr.offset(mid as isize), len - mid)) + } + } else { + slice_error_fail(self, 0, mid) + } } /// Returns an iterator over the [`char`]s of a string slice. @@ -3035,8 +2651,9 @@ macro_rules! str_core_methods { () => { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn chars(&self) -> Chars { - StrExt::chars(self) + Chars{iter: self.as_bytes().iter()} } + /// Returns an iterator over the [`char`]s of a string slice, and their /// positions. /// @@ -3091,7 +2708,7 @@ macro_rules! str_core_methods { () => { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn char_indices(&self) -> CharIndices { - StrExt::char_indices(self) + CharIndices { front_offset: 0, iter: self.chars() } } /// An iterator over the bytes of a string slice. @@ -3116,7 +2733,7 @@ macro_rules! str_core_methods { () => { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn bytes(&self) -> Bytes { - StrExt::bytes(self) + Bytes(self.as_bytes().iter().cloned()) } /// Split a string slice by whitespace. @@ -3156,7 +2773,7 @@ macro_rules! str_core_methods { () => { #[stable(feature = "split_whitespace", since = "1.1.0")] #[inline] pub fn split_whitespace(&self) -> SplitWhitespace { - StrExt::split_whitespace(self) + SplitWhitespace { inner: self.split(IsWhitespace).filter(IsNotEmpty) } } /// An iterator over the lines of a string, as string slices. @@ -3198,7 +2815,7 @@ macro_rules! str_core_methods { () => { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn lines(&self) -> Lines { - StrExt::lines(self) + Lines(self.split_terminator('\n').map(LinesAnyMap)) } /// An iterator over the lines of a string. @@ -3207,7 +2824,7 @@ macro_rules! str_core_methods { () => { #[inline] #[allow(deprecated)] pub fn lines_any(&self) -> LinesAny { - StrExt::lines_any(self) + LinesAny(self.lines()) } /// Returns an iterator of `u16` over the string encoded as UTF-16. @@ -3226,7 +2843,7 @@ macro_rules! str_core_methods { () => { /// ``` #[stable(feature = "encode_utf16", since = "1.8.0")] pub fn encode_utf16(&self) -> EncodeUtf16 { - EncodeUtf16::new(self) + EncodeUtf16 { chars: self.chars(), extra: 0 } } /// Returns `true` if the given pattern matches a sub-slice of @@ -3247,7 +2864,7 @@ macro_rules! str_core_methods { () => { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn contains<'a, P: Pattern<'a>>(&'a self, pat: P) -> bool { - StrExt::contains(self, pat) + pat.is_contained_in(self) } /// Returns `true` if the given pattern matches a prefix of this @@ -3267,7 +2884,7 @@ macro_rules! str_core_methods { () => { /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub fn starts_with<'a, P: Pattern<'a>>(&'a self, pat: P) -> bool { - StrExt::starts_with(self, pat) + pat.is_prefix_of(self) } /// Returns `true` if the given pattern matches a suffix of this @@ -3289,7 +2906,7 @@ macro_rules! str_core_methods { () => { pub fn ends_with<'a, P: Pattern<'a>>(&'a self, pat: P) -> bool where P::Searcher: ReverseSearcher<'a> { - StrExt::ends_with(self, pat) + pat.is_suffix_of(self) } /// Returns the byte index of the first character of this string slice that @@ -3337,7 +2954,7 @@ macro_rules! str_core_methods { () => { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn find<'a, P: Pattern<'a>>(&'a self, pat: P) -> Option { - StrExt::find(self, pat) + pat.into_searcher(self).next_match().map(|(i, _)| i) } /// Returns the byte index of the last character of this string slice that @@ -3384,7 +3001,7 @@ macro_rules! str_core_methods { () => { pub fn rfind<'a, P: Pattern<'a>>(&'a self, pat: P) -> Option where P::Searcher: ReverseSearcher<'a> { - StrExt::rfind(self, pat) + pat.into_searcher(self).next_match_back().map(|(i, _)| i) } /// An iterator over substrings of this string slice, separated by @@ -3496,7 +3113,13 @@ macro_rules! str_core_methods { () => { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn split<'a, P: Pattern<'a>>(&'a self, pat: P) -> Split<'a, P> { - StrExt::split(self, pat) + Split(SplitInternal { + start: 0, + end: self.len(), + matcher: pat.into_searcher(self), + allow_trailing_empty: true, + finished: false, + }) } /// An iterator over substrings of the given string slice, separated by @@ -3548,7 +3171,7 @@ macro_rules! str_core_methods { () => { pub fn rsplit<'a, P: Pattern<'a>>(&'a self, pat: P) -> RSplit<'a, P> where P::Searcher: ReverseSearcher<'a> { - StrExt::rsplit(self, pat) + RSplit(self.split(pat).0) } /// An iterator over substrings of the given string slice, separated by @@ -3593,7 +3216,10 @@ macro_rules! str_core_methods { () => { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn split_terminator<'a, P: Pattern<'a>>(&'a self, pat: P) -> SplitTerminator<'a, P> { - StrExt::split_terminator(self, pat) + SplitTerminator(SplitInternal { + allow_trailing_empty: false, + ..self.split(pat).0 + }) } /// An iterator over substrings of `self`, separated by characters @@ -3639,7 +3265,7 @@ macro_rules! str_core_methods { () => { pub fn rsplit_terminator<'a, P: Pattern<'a>>(&'a self, pat: P) -> RSplitTerminator<'a, P> where P::Searcher: ReverseSearcher<'a> { - StrExt::rsplit_terminator(self, pat) + RSplitTerminator(self.split_terminator(pat).0) } /// An iterator over substrings of the given string slice, separated by a @@ -3690,7 +3316,10 @@ macro_rules! str_core_methods { () => { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn splitn<'a, P: Pattern<'a>>(&'a self, n: usize, pat: P) -> SplitN<'a, P> { - StrExt::splitn(self, n, pat) + SplitN(SplitNInternal { + iter: self.split(pat).0, + count: n, + }) } /// An iterator over substrings of this string slice, separated by a @@ -3740,7 +3369,7 @@ macro_rules! str_core_methods { () => { pub fn rsplitn<'a, P: Pattern<'a>>(&'a self, n: usize, pat: P) -> RSplitN<'a, P> where P::Searcher: ReverseSearcher<'a> { - StrExt::rsplitn(self, n, pat) + RSplitN(self.splitn(n, pat).0) } /// An iterator over the disjoint matches of a pattern within the given string @@ -3779,7 +3408,7 @@ macro_rules! str_core_methods { () => { #[stable(feature = "str_matches", since = "1.2.0")] #[inline] pub fn matches<'a, P: Pattern<'a>>(&'a self, pat: P) -> Matches<'a, P> { - StrExt::matches(self, pat) + Matches(MatchesInternal(pat.into_searcher(self))) } /// An iterator over the disjoint matches of a pattern within this string slice, @@ -3818,7 +3447,7 @@ macro_rules! str_core_methods { () => { pub fn rmatches<'a, P: Pattern<'a>>(&'a self, pat: P) -> RMatches<'a, P> where P::Searcher: ReverseSearcher<'a> { - StrExt::rmatches(self, pat) + RMatches(self.matches(pat).0) } /// An iterator over the disjoint matches of a pattern within this string @@ -3862,7 +3491,7 @@ macro_rules! str_core_methods { () => { #[stable(feature = "str_match_indices", since = "1.5.0")] #[inline] pub fn match_indices<'a, P: Pattern<'a>>(&'a self, pat: P) -> MatchIndices<'a, P> { - StrExt::match_indices(self, pat) + MatchIndices(MatchIndicesInternal(pat.into_searcher(self))) } /// An iterator over the disjoint matches of a pattern within `self`, @@ -3907,7 +3536,7 @@ macro_rules! str_core_methods { () => { pub fn rmatch_indices<'a, P: Pattern<'a>>(&'a self, pat: P) -> RMatchIndices<'a, P> where P::Searcher: ReverseSearcher<'a> { - StrExt::rmatch_indices(self, pat) + RMatchIndices(self.match_indices(pat).0) } /// Returns a string slice with leading and trailing whitespace removed. @@ -3926,7 +3555,7 @@ macro_rules! str_core_methods { () => { /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub fn trim(&self) -> &str { - StrExt::trim(self) + self.trim_matches(|c: char| c.is_whitespace()) } /// Returns a string slice with leading whitespace removed. @@ -3962,7 +3591,7 @@ macro_rules! str_core_methods { () => { /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub fn trim_left(&self) -> &str { - StrExt::trim_left(self) + self.trim_left_matches(|c: char| c.is_whitespace()) } /// Returns a string slice with trailing whitespace removed. @@ -3998,7 +3627,7 @@ macro_rules! str_core_methods { () => { /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub fn trim_right(&self) -> &str { - StrExt::trim_right(self) + self.trim_right_matches(|c: char| c.is_whitespace()) } /// Returns a string slice with all prefixes and suffixes that match a @@ -4030,7 +3659,21 @@ macro_rules! str_core_methods { () => { pub fn trim_matches<'a, P: Pattern<'a>>(&'a self, pat: P) -> &'a str where P::Searcher: DoubleEndedSearcher<'a> { - StrExt::trim_matches(self, pat) + let mut i = 0; + let mut j = 0; + let mut matcher = pat.into_searcher(self); + if let Some((a, b)) = matcher.next_reject() { + i = a; + j = b; // Remember earliest known match, correct it below if + // last match is different + } + if let Some((_, b)) = matcher.next_reject_back() { + j = b; + } + unsafe { + // Searcher is known to return valid indices + self.slice_unchecked(i, j) + } } /// Returns a string slice with all prefixes that match a pattern @@ -4061,7 +3704,15 @@ macro_rules! str_core_methods { () => { /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub fn trim_left_matches<'a, P: Pattern<'a>>(&'a self, pat: P) -> &'a str { - StrExt::trim_left_matches(self, pat) + let mut i = self.len(); + let mut matcher = pat.into_searcher(self); + if let Some((a, _)) = matcher.next_reject() { + i = a; + } + unsafe { + // Searcher is known to return valid indices + self.slice_unchecked(i, self.len()) + } } /// Returns a string slice with all suffixes that match a pattern @@ -4100,7 +3751,15 @@ macro_rules! str_core_methods { () => { pub fn trim_right_matches<'a, P: Pattern<'a>>(&'a self, pat: P) -> &'a str where P::Searcher: ReverseSearcher<'a> { - StrExt::trim_right_matches(self, pat) + let mut j = 0; + let mut matcher = pat.into_searcher(self); + if let Some((_, b)) = matcher.next_reject_back() { + j = b; + } + unsafe { + // Searcher is known to return valid indices + self.slice_unchecked(0, j) + } } /// Parses this string slice into another type. @@ -4150,7 +3809,7 @@ macro_rules! str_core_methods { () => { #[inline] #[stable(feature = "rust1", since = "1.0.0")] pub fn parse(&self) -> Result { - StrExt::parse(self) + FromStr::from_str(self) } /// Checks if all characters in this string are within the ASCII range. @@ -4220,16 +3879,8 @@ macro_rules! str_core_methods { () => { let me = unsafe { self.as_bytes_mut() }; me.make_ascii_lowercase() } -}} - -#[lang = "str"] -#[cfg(not(test))] -#[cfg(not(stage0))] -impl str { - str_core_methods!(); } - #[stable(feature = "rust1", since = "1.0.0")] impl AsRef<[u8]> for str { #[inline] @@ -4332,17 +3983,6 @@ pub struct EncodeUtf16<'a> { extra: u16, } -// FIXME: remove (inline) this method -// when updating to a bootstrap compiler that has the new lang items. -// For grepping purpose: #[cfg(stage0)] -impl<'a> EncodeUtf16<'a> { - #[unstable(feature = "core_str_ext", issue = "32110")] - #[doc(hidden)] - pub fn new(s: &'a str) -> Self { - EncodeUtf16 { chars: s.chars(), extra: 0 } - } -} - #[stable(feature = "collection_debug", since = "1.17.0")] impl<'a> fmt::Debug for EncodeUtf16<'a> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { diff --git a/src/libcore/tests/lib.rs b/src/libcore/tests/lib.rs index 7fb4b503c01..8c481338945 100644 --- a/src/libcore/tests/lib.rs +++ b/src/libcore/tests/lib.rs @@ -41,7 +41,6 @@ #![feature(try_from)] #![feature(try_trait)] #![feature(exact_chunks)] -#![cfg_attr(stage0, feature(atomic_nand))] #![feature(reverse_bits)] #![feature(inclusive_range_methods)] #![feature(iterator_find_map)] diff --git a/src/libcore/tests/num/uint_macros.rs b/src/libcore/tests/num/uint_macros.rs index 257f6ea20d4..ca6906f7310 100644 --- a/src/libcore/tests/num/uint_macros.rs +++ b/src/libcore/tests/num/uint_macros.rs @@ -98,7 +98,6 @@ mod tests { } #[test] - #[cfg(not(stage0))] fn test_reverse_bits() { assert_eq!(A.reverse_bits().reverse_bits(), A); assert_eq!(B.reverse_bits().reverse_bits(), B); diff --git a/src/librustc/lib.rs b/src/librustc/lib.rs index ac6ff6831ad..bbd684982fa 100644 --- a/src/librustc/lib.rs +++ b/src/librustc/lib.rs @@ -46,7 +46,6 @@ #![feature(core_intrinsics)] #![feature(drain_filter)] #![feature(entry_or_default)] -#![cfg_attr(stage0, feature(dyn_trait))] #![feature(from_ref)] #![feature(fs_read_write)] #![feature(iterator_find_map)] diff --git a/src/librustc_mir/borrow_check/nll/mod.rs b/src/librustc_mir/borrow_check/nll/mod.rs index 0b1729294d8..a162ef36a60 100644 --- a/src/librustc_mir/borrow_check/nll/mod.rs +++ b/src/librustc_mir/borrow_check/nll/mod.rs @@ -240,11 +240,11 @@ fn dump_mir_results<'a, 'gcx, 'tcx>( }); // Also dump the inference graph constraints as a graphviz file. - let _: io::Result<()> = do_catch! {{ + let _: io::Result<()> = do catch { let mut file = pretty::create_dump_file(infcx.tcx, "regioncx.dot", None, "nll", &0, source)?; regioncx.dump_graphviz(&mut file)?; - }}; + }; } fn dump_annotation<'a, 'gcx, 'tcx>( diff --git a/src/librustc_mir/lib.rs b/src/librustc_mir/lib.rs index ecced1b8168..d9b6c406e20 100644 --- a/src/librustc_mir/lib.rs +++ b/src/librustc_mir/lib.rs @@ -24,7 +24,6 @@ Rust MIR: a lowered representation of Rust. Also: an experiment! #![feature(const_fn)] #![feature(core_intrinsics)] #![feature(decl_macro)] -#![cfg_attr(stage0, feature(dyn_trait))] #![feature(fs_read_write)] #![feature(macro_vis_matcher)] #![feature(exhaustive_patterns)] @@ -34,7 +33,7 @@ Rust MIR: a lowered representation of Rust. Also: an experiment! #![feature(crate_visibility_modifier)] #![feature(never_type)] #![feature(specialization)] -#![cfg_attr(stage0, feature(try_trait))] +#![feature(try_trait)] extern crate arena; #[macro_use] @@ -54,16 +53,6 @@ extern crate log_settings; extern crate rustc_apfloat; extern crate byteorder; -#[cfg(stage0)] -macro_rules! do_catch { - ($t:expr) => { (|| ::std::ops::Try::from_ok($t) )() } -} - -#[cfg(not(stage0))] -macro_rules! do_catch { - ($t:expr) => { do catch { $t } } -} - mod diagnostics; mod borrow_check; diff --git a/src/librustc_mir/util/pretty.rs b/src/librustc_mir/util/pretty.rs index 9d74ad0830f..9e1ce9b2851 100644 --- a/src/librustc_mir/util/pretty.rs +++ b/src/librustc_mir/util/pretty.rs @@ -137,7 +137,7 @@ fn dump_matched_mir_node<'a, 'gcx, 'tcx, F>( ) where F: FnMut(PassWhere, &mut dyn Write) -> io::Result<()>, { - let _: io::Result<()> = do_catch! {{ + let _: io::Result<()> = do catch { let mut file = create_dump_file(tcx, "mir", pass_num, pass_name, disambiguator, source)?; writeln!(file, "// MIR for `{}`", node_path)?; writeln!(file, "// source = {:?}", source)?; @@ -150,14 +150,14 @@ fn dump_matched_mir_node<'a, 'gcx, 'tcx, F>( extra_data(PassWhere::BeforeCFG, &mut file)?; write_mir_fn(tcx, source, mir, &mut extra_data, &mut file)?; extra_data(PassWhere::AfterCFG, &mut file)?; - }}; + }; if tcx.sess.opts.debugging_opts.dump_mir_graphviz { - let _: io::Result<()> = do_catch! {{ + let _: io::Result<()> = do catch { let mut file = create_dump_file(tcx, "dot", pass_num, pass_name, disambiguator, source)?; write_mir_fn_graphviz(tcx, source.def_id, mir, &mut file)?; - }}; + }; } } diff --git a/src/librustc_typeck/lib.rs b/src/librustc_typeck/lib.rs index 350b53a406b..ef79517d06a 100644 --- a/src/librustc_typeck/lib.rs +++ b/src/librustc_typeck/lib.rs @@ -71,8 +71,6 @@ This API is completely unstable and subject to change. #![allow(non_camel_case_types)] -#![cfg_attr(stage0, feature(dyn_trait))] - #![feature(box_patterns)] #![feature(box_syntax)] #![feature(crate_visibility_modifier)] diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs index a14a27d5d61..e4b80b13aec 100644 --- a/src/librustdoc/lib.rs +++ b/src/librustdoc/lib.rs @@ -13,8 +13,6 @@ html_root_url = "https://doc.rust-lang.org/nightly/", html_playground_url = "https://play.rust-lang.org/")] -#![cfg_attr(stage0, feature(dyn_trait))] - #![feature(ascii_ctype)] #![feature(rustc_private)] #![feature(box_patterns)] diff --git a/src/libstd/alloc.rs b/src/libstd/alloc.rs index a8578404467..78d3d6d5e60 100644 --- a/src/libstd/alloc.rs +++ b/src/libstd/alloc.rs @@ -17,7 +17,6 @@ #[doc(inline)] pub use alloc_system::System; #[doc(inline)] pub use core::alloc::*; -#[cfg(not(stage0))] #[cfg(not(test))] #[doc(hidden)] #[lang = "oom"] @@ -43,13 +42,6 @@ pub mod __default_lib_allocator { System.alloc(layout) as *mut u8 } - #[cfg(stage0)] - #[no_mangle] - #[rustc_std_internal_symbol] - pub unsafe extern fn __rdl_oom() -> ! { - super::oom() - } - #[no_mangle] #[rustc_std_internal_symbol] pub unsafe extern fn __rdl_dealloc(ptr: *mut u8, @@ -74,57 +66,4 @@ pub mod __default_lib_allocator { let layout = Layout::from_size_align_unchecked(size, align); System.alloc_zeroed(layout) as *mut u8 } - - #[cfg(stage0)] - pub mod stage0 { - #[no_mangle] - #[rustc_std_internal_symbol] - pub unsafe extern fn __rdl_usable_size(_layout: *const u8, - _min: *mut usize, - _max: *mut usize) { - unimplemented!() - } - - #[no_mangle] - #[rustc_std_internal_symbol] - pub unsafe extern fn __rdl_alloc_excess(_size: usize, - _align: usize, - _excess: *mut usize, - _err: *mut u8) -> *mut u8 { - unimplemented!() - } - - #[no_mangle] - #[rustc_std_internal_symbol] - pub unsafe extern fn __rdl_realloc_excess(_ptr: *mut u8, - _old_size: usize, - _old_align: usize, - _new_size: usize, - _new_align: usize, - _excess: *mut usize, - _err: *mut u8) -> *mut u8 { - unimplemented!() - } - - #[no_mangle] - #[rustc_std_internal_symbol] - pub unsafe extern fn __rdl_grow_in_place(_ptr: *mut u8, - _old_size: usize, - _old_align: usize, - _new_size: usize, - _new_align: usize) -> u8 { - unimplemented!() - } - - #[no_mangle] - #[rustc_std_internal_symbol] - pub unsafe extern fn __rdl_shrink_in_place(_ptr: *mut u8, - _old_size: usize, - _old_align: usize, - _new_size: usize, - _new_align: usize) -> u8 { - unimplemented!() - } - - } } diff --git a/src/libstd/f32.rs b/src/libstd/f32.rs index 7314d32b020..ae30321f46d 100644 --- a/src/libstd/f32.rs +++ b/src/libstd/f32.rs @@ -18,15 +18,9 @@ #![stable(feature = "rust1", since = "1.0.0")] #![allow(missing_docs)] -#[cfg(not(test))] -#[cfg(stage0)] -use core::num::Float; #[cfg(not(test))] use intrinsics; #[cfg(not(test))] -#[cfg(stage0)] -use num::FpCategory; -#[cfg(not(test))] use sys::cmath; #[stable(feature = "rust1", since = "1.0.0")] @@ -41,12 +35,8 @@ pub use core::f32::{MIN, MIN_POSITIVE, MAX}; pub use core::f32::consts; #[cfg(not(test))] -#[cfg_attr(stage0, lang = "f32")] -#[cfg_attr(not(stage0), lang = "f32_runtime")] +#[lang = "f32_runtime"] impl f32 { - #[cfg(stage0)] - f32_core_methods!(); - /// Returns the largest integer less than or equal to a number. /// /// # Examples diff --git a/src/libstd/f64.rs b/src/libstd/f64.rs index 75edba8979f..7950d434b77 100644 --- a/src/libstd/f64.rs +++ b/src/libstd/f64.rs @@ -18,15 +18,9 @@ #![stable(feature = "rust1", since = "1.0.0")] #![allow(missing_docs)] -#[cfg(not(test))] -#[cfg(stage0)] -use core::num::Float; #[cfg(not(test))] use intrinsics; #[cfg(not(test))] -#[cfg(stage0)] -use num::FpCategory; -#[cfg(not(test))] use sys::cmath; #[stable(feature = "rust1", since = "1.0.0")] @@ -41,12 +35,8 @@ pub use core::f64::{MIN, MIN_POSITIVE, MAX}; pub use core::f64::consts; #[cfg(not(test))] -#[cfg_attr(stage0, lang = "f64")] -#[cfg_attr(not(stage0), lang = "f64_runtime")] +#[lang = "f64_runtime"] impl f64 { - #[cfg(stage0)] - f64_core_methods!(); - /// Returns the largest integer less than or equal to a number. /// /// # Examples diff --git a/src/libstd/lib.rs b/src/libstd/lib.rs index 9cdc6a21622..f7d06852f27 100644 --- a/src/libstd/lib.rs +++ b/src/libstd/lib.rs @@ -252,7 +252,6 @@ #![feature(collections_range)] #![feature(compiler_builtins_lib)] #![feature(const_fn)] -#![cfg_attr(stage0, feature(core_float))] #![feature(core_intrinsics)] #![feature(dropck_eyepatch)] #![feature(exact_size_is_empty)] @@ -260,10 +259,8 @@ #![feature(fs_read_write)] #![feature(fixed_size_array)] #![feature(float_from_str_radix)] -#![cfg_attr(stage0, feature(float_internals))] #![feature(fn_traits)] #![feature(fnbox)] -#![cfg_attr(stage0, feature(generic_param_attrs))] #![feature(hashmap_internals)] #![feature(heap_api)] #![feature(int_error_internals)] @@ -319,6 +316,7 @@ #![cfg_attr(test, feature(update_panic_count))] #![cfg_attr(windows, feature(used))] #![feature(doc_alias)] +#![feature(float_internals)] #![default_lib_allocator] @@ -364,11 +362,6 @@ extern crate libc; #[allow(unused_extern_crates)] extern crate unwind; -// compiler-rt intrinsics -#[doc(masked)] -#[cfg(stage0)] -extern crate compiler_builtins; - // During testing, this crate is not actually the "real" std library, but rather // it links to the real std library, which was compiled from this same source // code. So any lang items std defines are conditionally excluded (or else they diff --git a/src/libsyntax_ext/asm.rs b/src/libsyntax_ext/asm.rs index 369c5b1ff60..dd8f79d20ab 100644 --- a/src/libsyntax_ext/asm.rs +++ b/src/libsyntax_ext/asm.rs @@ -45,17 +45,6 @@ impl State { } } -macro_rules! span_err_if_not_stage0 { - ($cx:expr, $sp:expr, $code:ident, $text:tt) => { - #[cfg(not(stage0))] { - span_err!($cx, $sp, $code, $text) - } - #[cfg(stage0)] { - $cx.span_err($sp, $text) - } - } -} - const OPTIONS: &'static [&'static str] = &["volatile", "alignstack", "intel"]; pub fn expand_asm<'cx>(cx: &'cx mut ExtCtxt, @@ -100,7 +89,7 @@ pub fn expand_asm<'cx>(cx: &'cx mut ExtCtxt, if asm_str_style.is_some() { // If we already have a string with instructions, // ending up in Asm state again is an error. - span_err_if_not_stage0!(cx, sp, E0660, "malformed inline assembly"); + span_err!(cx, sp, E0660, "malformed inline assembly"); return DummyResult::expr(sp); } // Nested parser, stop before the first colon (see above). @@ -153,7 +142,7 @@ pub fn expand_asm<'cx>(cx: &'cx mut ExtCtxt, Some(Symbol::intern(&format!("={}", ch.as_str()))) } _ => { - span_err_if_not_stage0!(cx, span, E0661, + span_err!(cx, span, E0661, "output operand constraint lacks '=' or '+'"); None } @@ -179,10 +168,10 @@ pub fn expand_asm<'cx>(cx: &'cx mut ExtCtxt, let (constraint, _str_style) = panictry!(p.parse_str()); if constraint.as_str().starts_with("=") { - span_err_if_not_stage0!(cx, p.prev_span, E0662, + span_err!(cx, p.prev_span, E0662, "input operand constraint contains '='"); } else if constraint.as_str().starts_with("+") { - span_err_if_not_stage0!(cx, p.prev_span, E0663, + span_err!(cx, p.prev_span, E0663, "input operand constraint contains '+'"); } @@ -205,7 +194,7 @@ pub fn expand_asm<'cx>(cx: &'cx mut ExtCtxt, if OPTIONS.iter().any(|&opt| s == opt) { cx.span_warn(p.prev_span, "expected a clobber, found an option"); } else if s.as_str().starts_with("{") || s.as_str().ends_with("}") { - span_err_if_not_stage0!(cx, p.prev_span, E0664, + span_err!(cx, p.prev_span, E0664, "clobber should not be surrounded by braces"); } diff --git a/src/libsyntax_ext/lib.rs b/src/libsyntax_ext/lib.rs index b6721dd28f3..e100ef29225 100644 --- a/src/libsyntax_ext/lib.rs +++ b/src/libsyntax_ext/lib.rs @@ -18,7 +18,7 @@ #![feature(decl_macro)] #![feature(str_escape)] -#![cfg_attr(not(stage0), feature(rustc_diagnostic_macros))] +#![feature(rustc_diagnostic_macros)] extern crate fmt_macros; #[macro_use] @@ -29,7 +29,6 @@ extern crate rustc_data_structures; extern crate rustc_errors as errors; extern crate rustc_target; -#[cfg(not(stage0))] mod diagnostics; mod assert; diff --git a/src/rtstartup/rsbegin.rs b/src/rtstartup/rsbegin.rs index 7d5581bb774..8ff401164c1 100644 --- a/src/rtstartup/rsbegin.rs +++ b/src/rtstartup/rsbegin.rs @@ -23,7 +23,7 @@ // of other runtime components (registered via yet another special image section). #![feature(no_core, lang_items, optin_builtin_traits)] -#![crate_type="rlib"] +#![crate_type = "rlib"] #![no_core] #![allow(non_camel_case_types)] @@ -43,7 +43,7 @@ pub unsafe fn drop_in_place(to_drop: *mut T) { drop_in_place(to_drop); } -#[cfg(all(target_os="windows", target_arch = "x86", target_env="gnu"))] +#[cfg(all(target_os = "windows", target_arch = "x86", target_env = "gnu"))] pub mod eh_frames { #[no_mangle] #[link_section = ".eh_frame"] @@ -54,6 +54,21 @@ pub mod eh_frames { // This is defined as `struct object` in $GCC/libgcc/unwind-dw2-fde.h. static mut OBJ: [isize; 6] = [0; 6]; + macro_rules! impl_copy { + ($($t:ty)*) => { + $( + impl ::Copy for $t {} + )* + } + } + + impl_copy! { + usize u8 u16 u32 u64 u128 + isize i8 i16 i32 i64 i128 + f32 f64 + bool char + } + // Unwind info registration/deregistration routines. // See the docs of `unwind` module in libstd. extern "C" { @@ -63,14 +78,18 @@ pub mod eh_frames { unsafe fn init() { // register unwind info on module startup - rust_eh_register_frames(&__EH_FRAME_BEGIN__ as *const u8, - &mut OBJ as *mut _ as *mut u8); + rust_eh_register_frames( + &__EH_FRAME_BEGIN__ as *const u8, + &mut OBJ as *mut _ as *mut u8, + ); } unsafe fn uninit() { // unregister on shutdown - rust_eh_unregister_frames(&__EH_FRAME_BEGIN__ as *const u8, - &mut OBJ as *mut _ as *mut u8); + rust_eh_unregister_frames( + &__EH_FRAME_BEGIN__ as *const u8, + &mut OBJ as *mut _ as *mut u8, + ); } // MSVC-specific init/uninit routine registration diff --git a/src/stage0.txt b/src/stage0.txt index a5ad2b315a1..435cfd2f6db 100644 --- a/src/stage0.txt +++ b/src/stage0.txt @@ -12,7 +12,7 @@ # source tarball for a stable release you'll likely see `1.x.0` for rustc and # `0.x.0` for Cargo where they were released on `date`. -date: 2018-04-24 +date: 2018-05-10 rustc: beta cargo: beta -- cgit 1.4.1-3-g733a5 From f67453729c19b435686c94936d8145051e7f1284 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Thu, 24 May 2018 12:03:05 -0700 Subject: std: Ensure OOM is classified as `nounwind` OOM can't unwind today, and historically it's been optimized as if it can't unwind. This accidentally regressed with recent changes to the OOM handler, so this commit adds in a codegen test to assert that everything gets optimized away after the OOM function is approrpiately classified as nounwind Closes #50925 --- src/liballoc/alloc.rs | 1 + src/librustc_codegen_llvm/attributes.rs | 29 ++++++++++++++++++++++++----- src/librustc_codegen_llvm/callee.rs | 11 ----------- src/test/codegen/vec-iter-collect-len.rs | 21 +++++++++++++++++++++ 4 files changed, 46 insertions(+), 16 deletions(-) create mode 100644 src/test/codegen/vec-iter-collect-len.rs (limited to 'src/liballoc/alloc.rs') diff --git a/src/liballoc/alloc.rs b/src/liballoc/alloc.rs index 79607b06f94..4ae8fc649dd 100644 --- a/src/liballoc/alloc.rs +++ b/src/liballoc/alloc.rs @@ -133,6 +133,7 @@ pub(crate) unsafe fn box_free(ptr: Unique) { } } +#[rustc_allocator_nounwind] pub fn oom() -> ! { extern { #[lang = "oom"] diff --git a/src/librustc_codegen_llvm/attributes.rs b/src/librustc_codegen_llvm/attributes.rs index b64e102ba78..d6806e7afd3 100644 --- a/src/librustc_codegen_llvm/attributes.rs +++ b/src/librustc_codegen_llvm/attributes.rs @@ -20,7 +20,9 @@ use rustc::ty::TyCtxt; use rustc::ty::maps::Providers; use rustc_data_structures::sync::Lrc; use rustc_data_structures::fx::FxHashMap; +use rustc_target::spec::PanicStrategy; +use attributes; use llvm::{self, Attribute, ValueRef}; use llvm::AttributePlace::Function; use llvm_util; @@ -135,11 +137,28 @@ pub fn from_fn_attrs(cx: &CodegenCx, llfn: ValueRef, id: DefId) { Attribute::NoAlias.apply_llfn( llvm::AttributePlace::ReturnValue, llfn); } - if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::UNWIND) { - unwind(llfn, true); - } - if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::RUSTC_ALLOCATOR_NOUNWIND) { - unwind(llfn, false); + + let can_unwind = if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::UNWIND) { + Some(true) + } else if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::RUSTC_ALLOCATOR_NOUNWIND) { + Some(false) + + // Perhaps questionable, but we assume that anything defined + // *in Rust code* may unwind. Foreign items like `extern "C" { + // fn foo(); }` are assumed not to unwind **unless** they have + // a `#[unwind]` attribute. + } else if !cx.tcx.is_foreign_item(id) { + Some(true) + } else { + None + }; + + match can_unwind { + Some(false) => attributes::unwind(llfn, false), + Some(true) if cx.tcx.sess.panic_strategy() == PanicStrategy::Unwind => { + attributes::unwind(llfn, true); + } + Some(true) | None => {} } let features = llvm_target_features(cx.tcx.sess) diff --git a/src/librustc_codegen_llvm/callee.rs b/src/librustc_codegen_llvm/callee.rs index a3dbc450ce7..2c01bd42cc7 100644 --- a/src/librustc_codegen_llvm/callee.rs +++ b/src/librustc_codegen_llvm/callee.rs @@ -26,7 +26,6 @@ use rustc::hir::def_id::DefId; use rustc::ty::{self, TypeFoldable}; use rustc::ty::layout::LayoutOf; use rustc::ty::subst::Substs; -use rustc_target::spec::PanicStrategy; /// Codegens a reference to a fn/method item, monomorphizing and /// inlining as it goes. @@ -102,16 +101,6 @@ pub fn get_fn<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, let instance_def_id = instance.def_id(); - // Perhaps questionable, but we assume that anything defined - // *in Rust code* may unwind. Foreign items like `extern "C" { - // fn foo(); }` are assumed not to unwind **unless** they have - // a `#[unwind]` attribute. - if tcx.sess.panic_strategy() == PanicStrategy::Unwind { - if !tcx.is_foreign_item(instance_def_id) { - attributes::unwind(llfn, true); - } - } - // Apply an appropriate linkage/visibility value to our item that we // just declared. // diff --git a/src/test/codegen/vec-iter-collect-len.rs b/src/test/codegen/vec-iter-collect-len.rs new file mode 100644 index 00000000000..efb384d0afb --- /dev/null +++ b/src/test/codegen/vec-iter-collect-len.rs @@ -0,0 +1,21 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// no-system-llvm +// compile-flags: -O +#![crate_type="lib"] + +#[no_mangle] +pub fn get_len() -> usize { + // CHECK-LABEL: @get_len + // CHECK-NOT: call + // CHECK-NOT: invoke + [1, 2, 3].iter().collect::>().len() +} -- cgit 1.4.1-3-g733a5 From 0f4ef003ac1691d04f0ce519d1d78696689534aa Mon Sep 17 00:00:00 2001 From: Mike Hommey Date: Tue, 15 May 2018 09:56:46 +0900 Subject: Pass a `Layout` to `oom` As discussed in https://github.com/rust-lang/rust/issues/49668#issuecomment-384893456 and subsequent, there are use-cases where the OOM handler needs to know the size of the allocation that failed. The alignment might also be a cause for allocation failure, so providing it as well can be useful. --- src/liballoc/alloc.rs | 13 +-- src/liballoc/arc.rs | 2 +- src/liballoc/raw_vec.rs | 156 ++++++++++++++++-------------- src/liballoc/rc.rs | 2 +- src/libstd/alloc.rs | 4 +- src/libstd/collections/hash/map.rs | 39 ++++++-- src/libstd/collections/hash/table.rs | 43 +++++--- src/test/run-pass/allocator-alloc-one.rs | 4 +- src/test/run-pass/realloc-16687.rs | 6 +- src/test/run-pass/regions-mock-codegen.rs | 4 +- 10 files changed, 161 insertions(+), 112 deletions(-) (limited to 'src/liballoc/alloc.rs') diff --git a/src/liballoc/alloc.rs b/src/liballoc/alloc.rs index 4ae8fc649dd..8753c495737 100644 --- a/src/liballoc/alloc.rs +++ b/src/liballoc/alloc.rs @@ -115,7 +115,7 @@ unsafe fn exchange_malloc(size: usize, align: usize) -> *mut u8 { if !ptr.is_null() { ptr as *mut u8 } else { - oom() + oom(layout) } } } @@ -134,12 +134,13 @@ pub(crate) unsafe fn box_free(ptr: Unique) { } #[rustc_allocator_nounwind] -pub fn oom() -> ! { - extern { +pub fn oom(layout: Layout) -> ! { + #[allow(improper_ctypes)] + extern "Rust" { #[lang = "oom"] - fn oom_impl() -> !; + fn oom_impl(layout: Layout) -> !; } - unsafe { oom_impl() } + unsafe { oom_impl(layout) } } #[cfg(test)] @@ -154,7 +155,7 @@ mod tests { unsafe { let layout = Layout::from_size_align(1024, 1).unwrap(); let ptr = Global.alloc_zeroed(layout.clone()) - .unwrap_or_else(|_| oom()); + .unwrap_or_else(|_| oom(layout)); let mut i = ptr.cast::().as_ptr(); let end = i.offset(layout.size() as isize); diff --git a/src/liballoc/arc.rs b/src/liballoc/arc.rs index d0950bff9ce..f7513248784 100644 --- a/src/liballoc/arc.rs +++ b/src/liballoc/arc.rs @@ -553,7 +553,7 @@ impl Arc { let layout = Layout::for_value(&*fake_ptr); let mem = Global.alloc(layout) - .unwrap_or_else(|_| oom()); + .unwrap_or_else(|_| oom(layout)); // Initialize the real ArcInner let inner = set_data_ptr(ptr as *mut T, mem.as_ptr() as *mut u8) as *mut ArcInner; diff --git a/src/liballoc/raw_vec.rs b/src/liballoc/raw_vec.rs index 5c6f6b22aae..07bb7f1a3eb 100644 --- a/src/liballoc/raw_vec.rs +++ b/src/liballoc/raw_vec.rs @@ -96,14 +96,15 @@ impl RawVec { NonNull::::dangling().as_opaque() } else { let align = mem::align_of::(); + let layout = Layout::from_size_align(alloc_size, align).unwrap(); let result = if zeroed { - a.alloc_zeroed(Layout::from_size_align(alloc_size, align).unwrap()) + a.alloc_zeroed(layout) } else { - a.alloc(Layout::from_size_align(alloc_size, align).unwrap()) + a.alloc(layout) }; match result { Ok(ptr) => ptr, - Err(_) => oom(), + Err(_) => oom(layout), } }; @@ -318,7 +319,7 @@ impl RawVec { new_size); match ptr_res { Ok(ptr) => (new_cap, ptr.cast().into()), - Err(_) => oom(), + Err(_) => oom(Layout::from_size_align_unchecked(new_size, cur.align())), } } None => { @@ -327,7 +328,7 @@ impl RawVec { let new_cap = if elem_size > (!0) / 8 { 1 } else { 4 }; match self.a.alloc_array::(new_cap) { Ok(ptr) => (new_cap, ptr.into()), - Err(_) => oom(), + Err(_) => oom(Layout::array::(new_cap).unwrap()), } } }; @@ -389,37 +390,7 @@ impl RawVec { pub fn try_reserve_exact(&mut self, used_cap: usize, needed_extra_cap: usize) -> Result<(), CollectionAllocErr> { - unsafe { - // NOTE: we don't early branch on ZSTs here because we want this - // to actually catch "asking for more than usize::MAX" in that case. - // If we make it past the first branch then we are guaranteed to - // panic. - - // Don't actually need any more capacity. - // Wrapping in case they gave a bad `used_cap`. - if self.cap().wrapping_sub(used_cap) >= needed_extra_cap { - return Ok(()); - } - - // Nothing we can really do about these checks :( - let new_cap = used_cap.checked_add(needed_extra_cap).ok_or(CapacityOverflow)?; - let new_layout = Layout::array::(new_cap).map_err(|_| CapacityOverflow)?; - - alloc_guard(new_layout.size())?; - - let res = match self.current_layout() { - Some(layout) => { - debug_assert!(new_layout.align() == layout.align()); - self.a.realloc(NonNull::from(self.ptr).as_opaque(), layout, new_layout.size()) - } - None => self.a.alloc(new_layout), - }; - - self.ptr = res?.cast().into(); - self.cap = new_cap; - - Ok(()) - } + self.reserve_internal(used_cap, needed_extra_cap, Fallible, Exact) } /// Ensures that the buffer contains at least enough space to hold @@ -443,9 +414,9 @@ impl RawVec { /// /// Aborts on OOM pub fn reserve_exact(&mut self, used_cap: usize, needed_extra_cap: usize) { - match self.try_reserve_exact(used_cap, needed_extra_cap) { + match self.reserve_internal(used_cap, needed_extra_cap, Infallible, Exact) { Err(CapacityOverflow) => capacity_overflow(), - Err(AllocErr) => oom(), + Err(AllocErr) => unreachable!(), Ok(()) => { /* yay */ } } } @@ -467,37 +438,7 @@ impl RawVec { /// The same as `reserve`, but returns on errors instead of panicking or aborting. pub fn try_reserve(&mut self, used_cap: usize, needed_extra_cap: usize) -> Result<(), CollectionAllocErr> { - unsafe { - // NOTE: we don't early branch on ZSTs here because we want this - // to actually catch "asking for more than usize::MAX" in that case. - // If we make it past the first branch then we are guaranteed to - // panic. - - // Don't actually need any more capacity. - // Wrapping in case they give a bad `used_cap` - if self.cap().wrapping_sub(used_cap) >= needed_extra_cap { - return Ok(()); - } - - let new_cap = self.amortized_new_size(used_cap, needed_extra_cap)?; - let new_layout = Layout::array::(new_cap).map_err(|_| CapacityOverflow)?; - - // FIXME: may crash and burn on over-reserve - alloc_guard(new_layout.size())?; - - let res = match self.current_layout() { - Some(layout) => { - debug_assert!(new_layout.align() == layout.align()); - self.a.realloc(NonNull::from(self.ptr).as_opaque(), layout, new_layout.size()) - } - None => self.a.alloc(new_layout), - }; - - self.ptr = res?.cast().into(); - self.cap = new_cap; - - Ok(()) - } + self.reserve_internal(used_cap, needed_extra_cap, Fallible, Amortized) } /// Ensures that the buffer contains at least enough space to hold @@ -553,12 +494,12 @@ impl RawVec { /// # } /// ``` pub fn reserve(&mut self, used_cap: usize, needed_extra_cap: usize) { - match self.try_reserve(used_cap, needed_extra_cap) { + match self.reserve_internal(used_cap, needed_extra_cap, Infallible, Amortized) { Err(CapacityOverflow) => capacity_overflow(), - Err(AllocErr) => oom(), + Err(AllocErr) => unreachable!(), Ok(()) => { /* yay */ } - } - } + } + } /// Attempts to ensure that the buffer contains at least enough space to hold /// `used_cap + needed_extra_cap` elements. If it doesn't already have /// enough capacity, will reallocate in place enough space plus comfortable slack @@ -670,7 +611,7 @@ impl RawVec { old_layout, new_size) { Ok(p) => self.ptr = p.cast().into(), - Err(_) => oom(), + Err(_) => oom(Layout::from_size_align_unchecked(new_size, align)), } } self.cap = amount; @@ -678,6 +619,73 @@ impl RawVec { } } +enum Fallibility { + Fallible, + Infallible, +} + +use self::Fallibility::*; + +enum ReserveStrategy { + Exact, + Amortized, +} + +use self::ReserveStrategy::*; + +impl RawVec { + fn reserve_internal( + &mut self, + used_cap: usize, + needed_extra_cap: usize, + fallibility: Fallibility, + strategy: ReserveStrategy, + ) -> Result<(), CollectionAllocErr> { + unsafe { + use alloc::AllocErr; + + // NOTE: we don't early branch on ZSTs here because we want this + // to actually catch "asking for more than usize::MAX" in that case. + // If we make it past the first branch then we are guaranteed to + // panic. + + // Don't actually need any more capacity. + // Wrapping in case they gave a bad `used_cap`. + if self.cap().wrapping_sub(used_cap) >= needed_extra_cap { + return Ok(()); + } + + // Nothing we can really do about these checks :( + let new_cap = match strategy { + Exact => used_cap.checked_add(needed_extra_cap).ok_or(CapacityOverflow)?, + Amortized => self.amortized_new_size(used_cap, needed_extra_cap)?, + }; + let new_layout = Layout::array::(new_cap).map_err(|_| CapacityOverflow)?; + + alloc_guard(new_layout.size())?; + + let res = match self.current_layout() { + Some(layout) => { + debug_assert!(new_layout.align() == layout.align()); + self.a.realloc(NonNull::from(self.ptr).as_opaque(), layout, new_layout.size()) + } + None => self.a.alloc(new_layout), + }; + + match (&res, fallibility) { + (Err(AllocErr), Infallible) => oom(new_layout), + _ => {} + } + + self.ptr = res?.cast().into(); + self.cap = new_cap; + + Ok(()) + } + } + +} + impl RawVec { /// Converts the entire buffer into `Box<[T]>`. /// diff --git a/src/liballoc/rc.rs b/src/liballoc/rc.rs index d0188c6e828..1648fc6b7ef 100644 --- a/src/liballoc/rc.rs +++ b/src/liballoc/rc.rs @@ -668,7 +668,7 @@ impl Rc { let layout = Layout::for_value(&*fake_ptr); let mem = Global.alloc(layout) - .unwrap_or_else(|_| oom()); + .unwrap_or_else(|_| oom(layout)); // Initialize the real RcBox let inner = set_data_ptr(ptr as *mut T, mem.as_ptr() as *mut u8) as *mut RcBox; diff --git a/src/libstd/alloc.rs b/src/libstd/alloc.rs index 78d3d6d5e60..0c95ceff2e3 100644 --- a/src/libstd/alloc.rs +++ b/src/libstd/alloc.rs @@ -13,14 +13,14 @@ #![unstable(issue = "32838", feature = "allocator_api")] #[doc(inline)] #[allow(deprecated)] pub use alloc_crate::alloc::Heap; -#[doc(inline)] pub use alloc_crate::alloc::{Global, oom}; +#[doc(inline)] pub use alloc_crate::alloc::{Global, Layout, oom}; #[doc(inline)] pub use alloc_system::System; #[doc(inline)] pub use core::alloc::*; #[cfg(not(test))] #[doc(hidden)] #[lang = "oom"] -pub extern fn rust_oom() -> ! { +pub extern fn rust_oom(_: Layout) -> ! { rtabort!("memory allocation failed"); } diff --git a/src/libstd/collections/hash/map.rs b/src/libstd/collections/hash/map.rs index a7eb002d5a1..935ea4b62b5 100644 --- a/src/libstd/collections/hash/map.rs +++ b/src/libstd/collections/hash/map.rs @@ -11,7 +11,7 @@ use self::Entry::*; use self::VacantEntryState::*; -use alloc::{CollectionAllocErr, oom}; +use alloc::CollectionAllocErr; use cell::Cell; use borrow::Borrow; use cmp::max; @@ -23,8 +23,10 @@ use mem::{self, replace}; use ops::{Deref, Index}; use sys; -use super::table::{self, Bucket, EmptyBucket, FullBucket, FullBucketMut, RawTable, SafeHash}; +use super::table::{self, Bucket, EmptyBucket, Fallibility, FullBucket, FullBucketMut, RawTable, + SafeHash}; use super::table::BucketState::{Empty, Full}; +use super::table::Fallibility::{Fallible, Infallible}; const MIN_NONZERO_RAW_CAPACITY: usize = 32; // must be a power of two @@ -783,11 +785,11 @@ impl HashMap /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub fn reserve(&mut self, additional: usize) { - match self.try_reserve(additional) { + match self.reserve_internal(additional, Infallible) { Err(CollectionAllocErr::CapacityOverflow) => panic!("capacity overflow"), - Err(CollectionAllocErr::AllocErr) => oom(), + Err(CollectionAllocErr::AllocErr) => unreachable!(), Ok(()) => { /* yay */ } - } + } } /// Tries to reserve capacity for at least `additional` more elements to be inserted @@ -809,17 +811,24 @@ impl HashMap /// ``` #[unstable(feature = "try_reserve", reason = "new API", issue="48043")] pub fn try_reserve(&mut self, additional: usize) -> Result<(), CollectionAllocErr> { + self.reserve_internal(additional, Fallible) + } + + fn reserve_internal(&mut self, additional: usize, fallibility: Fallibility) + -> Result<(), CollectionAllocErr> { + let remaining = self.capacity() - self.len(); // this can't overflow if remaining < additional { - let min_cap = self.len().checked_add(additional) + let min_cap = self.len() + .checked_add(additional) .ok_or(CollectionAllocErr::CapacityOverflow)?; let raw_cap = self.resize_policy.try_raw_capacity(min_cap)?; - self.try_resize(raw_cap)?; + self.try_resize(raw_cap, fallibility)?; } else if self.table.tag() && remaining <= self.len() { // Probe sequence is too long and table is half full, // resize early to reduce probing length. let new_capacity = self.table.capacity() * 2; - self.try_resize(new_capacity)?; + self.try_resize(new_capacity, fallibility)?; } Ok(()) } @@ -831,11 +840,21 @@ impl HashMap /// 2) Ensure `new_raw_cap` is a power of two or zero. #[inline(never)] #[cold] - fn try_resize(&mut self, new_raw_cap: usize) -> Result<(), CollectionAllocErr> { + fn try_resize( + &mut self, + new_raw_cap: usize, + fallibility: Fallibility, + ) -> Result<(), CollectionAllocErr> { assert!(self.table.size() <= new_raw_cap); assert!(new_raw_cap.is_power_of_two() || new_raw_cap == 0); - let mut old_table = replace(&mut self.table, RawTable::try_new(new_raw_cap)?); + let mut old_table = replace( + &mut self.table, + match fallibility { + Infallible => RawTable::new(new_raw_cap), + Fallible => RawTable::try_new(new_raw_cap)?, + } + ); let old_size = old_table.size(); if old_table.size() == 0 { diff --git a/src/libstd/collections/hash/table.rs b/src/libstd/collections/hash/table.rs index b50652ed6b5..eed2debcaa2 100644 --- a/src/libstd/collections/hash/table.rs +++ b/src/libstd/collections/hash/table.rs @@ -711,11 +711,21 @@ fn test_offset_calculation() { assert_eq!(calculate_offsets(6, 12, 4), (8, 20, false)); } +pub(crate) enum Fallibility { + Fallible, + Infallible, +} + +use self::Fallibility::*; + impl RawTable { /// Does not initialize the buckets. The caller should ensure they, /// at the very least, set every hash to EMPTY_BUCKET. /// Returns an error if it cannot allocate or capacity overflows. - unsafe fn try_new_uninitialized(capacity: usize) -> Result, CollectionAllocErr> { + unsafe fn new_uninitialized_internal( + capacity: usize, + fallibility: Fallibility, + ) -> Result, CollectionAllocErr> { if capacity == 0 { return Ok(RawTable { size: 0, @@ -754,8 +764,12 @@ impl RawTable { return Err(CollectionAllocErr::CapacityOverflow); } - let buffer = Global.alloc(Layout::from_size_align(size, alignment) - .map_err(|_| CollectionAllocErr::CapacityOverflow)?)?; + let layout = Layout::from_size_align(size, alignment) + .map_err(|_| CollectionAllocErr::CapacityOverflow)?; + let buffer = Global.alloc(layout).map_err(|e| match fallibility { + Infallible => oom(layout), + Fallible => e, + })?; Ok(RawTable { capacity_mask: capacity.wrapping_sub(1), @@ -768,9 +782,9 @@ impl RawTable { /// Does not initialize the buckets. The caller should ensure they, /// at the very least, set every hash to EMPTY_BUCKET. unsafe fn new_uninitialized(capacity: usize) -> RawTable { - match Self::try_new_uninitialized(capacity) { + match Self::new_uninitialized_internal(capacity, Infallible) { Err(CollectionAllocErr::CapacityOverflow) => panic!("capacity overflow"), - Err(CollectionAllocErr::AllocErr) => oom(), + Err(CollectionAllocErr::AllocErr) => unreachable!(), Ok(table) => { table } } } @@ -794,22 +808,29 @@ impl RawTable { } } - /// Tries to create a new raw table from a given capacity. If it cannot allocate, - /// it returns with AllocErr. - pub fn try_new(capacity: usize) -> Result, CollectionAllocErr> { + fn new_internal( + capacity: usize, + fallibility: Fallibility, + ) -> Result, CollectionAllocErr> { unsafe { - let ret = RawTable::try_new_uninitialized(capacity)?; + let ret = RawTable::new_uninitialized_internal(capacity, fallibility)?; ptr::write_bytes(ret.hashes.ptr(), 0, capacity); Ok(ret) } } + /// Tries to create a new raw table from a given capacity. If it cannot allocate, + /// it returns with AllocErr. + pub fn try_new(capacity: usize) -> Result, CollectionAllocErr> { + Self::new_internal(capacity, Fallible) + } + /// Creates a new raw table from a given capacity. All buckets are /// initially empty. pub fn new(capacity: usize) -> RawTable { - match Self::try_new(capacity) { + match Self::new_internal(capacity, Infallible) { Err(CollectionAllocErr::CapacityOverflow) => panic!("capacity overflow"), - Err(CollectionAllocErr::AllocErr) => oom(), + Err(CollectionAllocErr::AllocErr) => unreachable!(), Ok(table) => { table } } } diff --git a/src/test/run-pass/allocator-alloc-one.rs b/src/test/run-pass/allocator-alloc-one.rs index 12b115d0938..f1fdbfc702d 100644 --- a/src/test/run-pass/allocator-alloc-one.rs +++ b/src/test/run-pass/allocator-alloc-one.rs @@ -10,11 +10,11 @@ #![feature(allocator_api, nonnull)] -use std::alloc::{Alloc, Global, oom}; +use std::alloc::{Alloc, Global, Layout, oom}; fn main() { unsafe { - let ptr = Global.alloc_one::().unwrap_or_else(|_| oom()); + let ptr = Global.alloc_one::().unwrap_or_else(|_| oom(Layout::new::())); *ptr.as_ptr() = 4; assert_eq!(*ptr.as_ptr(), 4); Global.dealloc_one(ptr); diff --git a/src/test/run-pass/realloc-16687.rs b/src/test/run-pass/realloc-16687.rs index 308792e5d89..febd249d776 100644 --- a/src/test/run-pass/realloc-16687.rs +++ b/src/test/run-pass/realloc-16687.rs @@ -50,7 +50,7 @@ unsafe fn test_triangle() -> bool { println!("allocate({:?})", layout); } - let ret = Global.alloc(layout.clone()).unwrap_or_else(|_| oom()); + let ret = Global.alloc(layout).unwrap_or_else(|_| oom(layout)); if PRINT { println!("allocate({:?}) = {:?}", layout, ret); @@ -72,8 +72,8 @@ unsafe fn test_triangle() -> bool { println!("reallocate({:?}, old={:?}, new={:?})", ptr, old, new); } - let ret = Global.realloc(NonNull::new_unchecked(ptr).as_opaque(), old.clone(), new.size()) - .unwrap_or_else(|_| oom()); + let ret = Global.realloc(NonNull::new_unchecked(ptr).as_opaque(), old, new.size()) + .unwrap_or_else(|_| oom(Layout::from_size_align_unchecked(new.size(), old.align()))); if PRINT { println!("reallocate({:?}, old={:?}, new={:?}) = {:?}", diff --git a/src/test/run-pass/regions-mock-codegen.rs b/src/test/run-pass/regions-mock-codegen.rs index 60a7f70931d..745a19dec4d 100644 --- a/src/test/run-pass/regions-mock-codegen.rs +++ b/src/test/run-pass/regions-mock-codegen.rs @@ -32,8 +32,8 @@ struct Ccx { fn alloc<'a>(_bcx : &'a arena) -> &'a Bcx<'a> { unsafe { - let ptr = Global.alloc(Layout::new::()) - .unwrap_or_else(|_| oom()); + let layout = Layout::new::(); + let ptr = Global.alloc(layout).unwrap_or_else(|_| oom(layout)); &*(ptr.as_ptr() as *const _) } } -- cgit 1.4.1-3-g733a5 From f6ab74b8e7efed01c1045773b6693f23f6ebd93c Mon Sep 17 00:00:00 2001 From: Mike Hommey Date: Thu, 31 May 2018 15:57:43 +0900 Subject: Remove alloc::Opaque and use *mut u8 as pointer type for GlobalAlloc --- .../src/language-features/global-allocator.md | 6 +- src/liballoc/alloc.rs | 33 ++++---- src/liballoc/arc.rs | 6 +- src/liballoc/btree/node.rs | 10 +-- src/liballoc/heap.rs | 12 +-- src/liballoc/raw_vec.rs | 19 +++-- src/liballoc/rc.rs | 10 +-- src/liballoc_system/lib.rs | 89 +++++++++++----------- src/libcore/alloc.rs | 65 ++++++---------- src/libcore/ptr.rs | 8 -- src/librustc_allocator/expand.rs | 15 +--- src/libstd/alloc.rs | 10 +-- src/libstd/collections/hash/table.rs | 2 +- src/test/run-make-fulldeps/std-core-cycle/bar.rs | 4 +- src/test/run-pass/allocator/auxiliary/custom.rs | 6 +- src/test/run-pass/allocator/custom.rs | 6 +- src/test/run-pass/realloc-16687.rs | 4 +- 17 files changed, 130 insertions(+), 175 deletions(-) (limited to 'src/liballoc/alloc.rs') diff --git a/src/doc/unstable-book/src/language-features/global-allocator.md b/src/doc/unstable-book/src/language-features/global-allocator.md index 8f1ba22de8c..7dfdc487731 100644 --- a/src/doc/unstable-book/src/language-features/global-allocator.md +++ b/src/doc/unstable-book/src/language-features/global-allocator.md @@ -29,17 +29,17 @@ looks like: ```rust #![feature(global_allocator, allocator_api, heap_api)] -use std::alloc::{GlobalAlloc, System, Layout, Opaque}; +use std::alloc::{GlobalAlloc, System, Layout}; use std::ptr::NonNull; struct MyAllocator; unsafe impl GlobalAlloc for MyAllocator { - unsafe fn alloc(&self, layout: Layout) -> *mut Opaque { + unsafe fn alloc(&self, layout: Layout) -> *mut u8 { System.alloc(layout) } - unsafe fn dealloc(&self, ptr: *mut Opaque, layout: Layout) { + unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) { System.dealloc(ptr, layout) } } diff --git a/src/liballoc/alloc.rs b/src/liballoc/alloc.rs index 8753c495737..102910f4198 100644 --- a/src/liballoc/alloc.rs +++ b/src/liballoc/alloc.rs @@ -51,52 +51,49 @@ pub const Heap: Global = Global; unsafe impl GlobalAlloc for Global { #[inline] - unsafe fn alloc(&self, layout: Layout) -> *mut Opaque { - let ptr = __rust_alloc(layout.size(), layout.align()); - ptr as *mut Opaque + unsafe fn alloc(&self, layout: Layout) -> *mut u8 { + __rust_alloc(layout.size(), layout.align()) } #[inline] - unsafe fn dealloc(&self, ptr: *mut Opaque, layout: Layout) { - __rust_dealloc(ptr as *mut u8, layout.size(), layout.align()) + unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) { + __rust_dealloc(ptr, layout.size(), layout.align()) } #[inline] - unsafe fn realloc(&self, ptr: *mut Opaque, layout: Layout, new_size: usize) -> *mut Opaque { - let ptr = __rust_realloc(ptr as *mut u8, layout.size(), layout.align(), new_size); - ptr as *mut Opaque + unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 { + __rust_realloc(ptr, layout.size(), layout.align(), new_size) } #[inline] - unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut Opaque { - let ptr = __rust_alloc_zeroed(layout.size(), layout.align()); - ptr as *mut Opaque + unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 { + __rust_alloc_zeroed(layout.size(), layout.align()) } } unsafe impl Alloc for Global { #[inline] - unsafe fn alloc(&mut self, layout: Layout) -> Result, AllocErr> { + unsafe fn alloc(&mut self, layout: Layout) -> Result, AllocErr> { NonNull::new(GlobalAlloc::alloc(self, layout)).ok_or(AllocErr) } #[inline] - unsafe fn dealloc(&mut self, ptr: NonNull, layout: Layout) { + unsafe fn dealloc(&mut self, ptr: NonNull, layout: Layout) { GlobalAlloc::dealloc(self, ptr.as_ptr(), layout) } #[inline] unsafe fn realloc(&mut self, - ptr: NonNull, + ptr: NonNull, layout: Layout, new_size: usize) - -> Result, AllocErr> + -> Result, AllocErr> { NonNull::new(GlobalAlloc::realloc(self, ptr.as_ptr(), layout, new_size)).ok_or(AllocErr) } #[inline] - unsafe fn alloc_zeroed(&mut self, layout: Layout) -> Result, AllocErr> { + unsafe fn alloc_zeroed(&mut self, layout: Layout) -> Result, AllocErr> { NonNull::new(GlobalAlloc::alloc_zeroed(self, layout)).ok_or(AllocErr) } } @@ -113,7 +110,7 @@ unsafe fn exchange_malloc(size: usize, align: usize) -> *mut u8 { let layout = Layout::from_size_align_unchecked(size, align); let ptr = Global.alloc(layout); if !ptr.is_null() { - ptr as *mut u8 + ptr } else { oom(layout) } @@ -129,7 +126,7 @@ pub(crate) unsafe fn box_free(ptr: Unique) { // We do not allocate for Box when T is ZST, so deallocation is also not necessary. if size != 0 { let layout = Layout::from_size_align_unchecked(size, align); - Global.dealloc(ptr as *mut Opaque, layout); + Global.dealloc(ptr as *mut u8, layout); } } diff --git a/src/liballoc/arc.rs b/src/liballoc/arc.rs index 4026b3ababa..e3369f0a5b5 100644 --- a/src/liballoc/arc.rs +++ b/src/liballoc/arc.rs @@ -519,7 +519,7 @@ impl Arc { if self.inner().weak.fetch_sub(1, Release) == 1 { atomic::fence(Acquire); - Global.dealloc(self.ptr.as_opaque(), Layout::for_value(self.ptr.as_ref())) + Global.dealloc(self.ptr.cast(), Layout::for_value(self.ptr.as_ref())) } } @@ -639,7 +639,7 @@ impl ArcFromSlice for Arc<[T]> { let slice = from_raw_parts_mut(self.elems, self.n_elems); ptr::drop_in_place(slice); - Global.dealloc(self.mem.as_opaque(), self.layout.clone()); + Global.dealloc(self.mem.cast(), self.layout.clone()); } } } @@ -1196,7 +1196,7 @@ impl Drop for Weak { if self.inner().weak.fetch_sub(1, Release) == 1 { atomic::fence(Acquire); unsafe { - Global.dealloc(self.ptr.as_opaque(), Layout::for_value(self.ptr.as_ref())) + Global.dealloc(self.ptr.cast(), Layout::for_value(self.ptr.as_ref())) } } } diff --git a/src/liballoc/btree/node.rs b/src/liballoc/btree/node.rs index 431695c32ab..19bdcbc6ad6 100644 --- a/src/liballoc/btree/node.rs +++ b/src/liballoc/btree/node.rs @@ -287,7 +287,7 @@ impl Root { self.as_mut().as_leaf_mut().parent = ptr::null(); unsafe { - Global.dealloc(NonNull::from(top).as_opaque(), Layout::new::>()); + Global.dealloc(NonNull::from(top).cast(), Layout::new::>()); } } } @@ -478,7 +478,7 @@ impl NodeRef { debug_assert!(!self.is_shared_root()); let node = self.node; let ret = self.ascend().ok(); - Global.dealloc(node.as_opaque(), Layout::new::>()); + Global.dealloc(node.cast(), Layout::new::>()); ret } } @@ -499,7 +499,7 @@ impl NodeRef { > { let node = self.node; let ret = self.ascend().ok(); - Global.dealloc(node.as_opaque(), Layout::new::>()); + Global.dealloc(node.cast(), Layout::new::>()); ret } } @@ -1321,12 +1321,12 @@ impl<'a, K, V> Handle, K, V, marker::Internal>, marker:: } Global.dealloc( - right_node.node.as_opaque(), + right_node.node.cast(), Layout::new::>(), ); } else { Global.dealloc( - right_node.node.as_opaque(), + right_node.node.cast(), Layout::new::>(), ); } diff --git a/src/liballoc/heap.rs b/src/liballoc/heap.rs index 16f0630b911..5ea37ceeb2b 100644 --- a/src/liballoc/heap.rs +++ b/src/liballoc/heap.rs @@ -10,7 +10,7 @@ #![allow(deprecated)] -pub use alloc::{Layout, AllocErr, CannotReallocInPlace, Opaque}; +pub use alloc::{Layout, AllocErr, CannotReallocInPlace}; use core::alloc::Alloc as CoreAlloc; use core::ptr::NonNull; @@ -54,7 +54,7 @@ unsafe impl Alloc for T where T: CoreAlloc { } unsafe fn dealloc(&mut self, ptr: *mut u8, layout: Layout) { - let ptr = NonNull::new_unchecked(ptr as *mut Opaque); + let ptr = NonNull::new_unchecked(ptr); CoreAlloc::dealloc(self, ptr, layout) } @@ -70,7 +70,7 @@ unsafe impl Alloc for T where T: CoreAlloc { ptr: *mut u8, layout: Layout, new_layout: Layout) -> Result<*mut u8, AllocErr> { - let ptr = NonNull::new_unchecked(ptr as *mut Opaque); + let ptr = NonNull::new_unchecked(ptr); CoreAlloc::realloc(self, ptr, layout, new_layout.size()).map(|ptr| ptr.cast().as_ptr()) } @@ -87,7 +87,7 @@ unsafe impl Alloc for T where T: CoreAlloc { ptr: *mut u8, layout: Layout, new_layout: Layout) -> Result { - let ptr = NonNull::new_unchecked(ptr as *mut Opaque); + let ptr = NonNull::new_unchecked(ptr); CoreAlloc::realloc_excess(self, ptr, layout, new_layout.size()) .map(|e| Excess(e.0 .cast().as_ptr(), e.1)) } @@ -96,7 +96,7 @@ unsafe impl Alloc for T where T: CoreAlloc { ptr: *mut u8, layout: Layout, new_layout: Layout) -> Result<(), CannotReallocInPlace> { - let ptr = NonNull::new_unchecked(ptr as *mut Opaque); + let ptr = NonNull::new_unchecked(ptr); CoreAlloc::grow_in_place(self, ptr, layout, new_layout.size()) } @@ -104,7 +104,7 @@ unsafe impl Alloc for T where T: CoreAlloc { ptr: *mut u8, layout: Layout, new_layout: Layout) -> Result<(), CannotReallocInPlace> { - let ptr = NonNull::new_unchecked(ptr as *mut Opaque); + let ptr = NonNull::new_unchecked(ptr); CoreAlloc::shrink_in_place(self, ptr, layout, new_layout.size()) } } diff --git a/src/liballoc/raw_vec.rs b/src/liballoc/raw_vec.rs index 07bb7f1a3eb..c09f21eeb92 100644 --- a/src/liballoc/raw_vec.rs +++ b/src/liballoc/raw_vec.rs @@ -93,7 +93,7 @@ impl RawVec { // handles ZSTs and `cap = 0` alike let ptr = if alloc_size == 0 { - NonNull::::dangling().as_opaque() + NonNull::::dangling().cast() } else { let align = mem::align_of::(); let layout = Layout::from_size_align(alloc_size, align).unwrap(); @@ -314,7 +314,7 @@ impl RawVec { let new_cap = 2 * self.cap; let new_size = new_cap * elem_size; alloc_guard(new_size).unwrap_or_else(|_| capacity_overflow()); - let ptr_res = self.a.realloc(NonNull::from(self.ptr).as_opaque(), + let ptr_res = self.a.realloc(NonNull::from(self.ptr).cast(), cur, new_size); match ptr_res { @@ -373,7 +373,7 @@ impl RawVec { let new_cap = 2 * self.cap; let new_size = new_cap * elem_size; alloc_guard(new_size).unwrap_or_else(|_| capacity_overflow()); - match self.a.grow_in_place(NonNull::from(self.ptr).as_opaque(), old_layout, new_size) { + match self.a.grow_in_place(NonNull::from(self.ptr).cast(), old_layout, new_size) { Ok(_) => { // We can't directly divide `size`. self.cap = new_cap; @@ -546,7 +546,7 @@ impl RawVec { // FIXME: may crash and burn on over-reserve alloc_guard(new_layout.size()).unwrap_or_else(|_| capacity_overflow()); match self.a.grow_in_place( - NonNull::from(self.ptr).as_opaque(), old_layout, new_layout.size(), + NonNull::from(self.ptr).cast(), old_layout, new_layout.size(), ) { Ok(_) => { self.cap = new_cap; @@ -607,7 +607,7 @@ impl RawVec { let new_size = elem_size * amount; let align = mem::align_of::(); let old_layout = Layout::from_size_align_unchecked(old_size, align); - match self.a.realloc(NonNull::from(self.ptr).as_opaque(), + match self.a.realloc(NonNull::from(self.ptr).cast(), old_layout, new_size) { Ok(p) => self.ptr = p.cast().into(), @@ -667,7 +667,7 @@ impl RawVec { let res = match self.current_layout() { Some(layout) => { debug_assert!(new_layout.align() == layout.align()); - self.a.realloc(NonNull::from(self.ptr).as_opaque(), layout, new_layout.size()) + self.a.realloc(NonNull::from(self.ptr).cast(), layout, new_layout.size()) } None => self.a.alloc(new_layout), }; @@ -710,7 +710,7 @@ impl RawVec { let elem_size = mem::size_of::(); if elem_size != 0 { if let Some(layout) = self.current_layout() { - self.a.dealloc(NonNull::from(self.ptr).as_opaque(), layout); + self.a.dealloc(NonNull::from(self.ptr).cast(), layout); } } } @@ -753,7 +753,6 @@ fn capacity_overflow() -> ! { #[cfg(test)] mod tests { use super::*; - use alloc::Opaque; #[test] fn allocator_param() { @@ -773,7 +772,7 @@ mod tests { // before allocation attempts start failing. struct BoundedAlloc { fuel: usize } unsafe impl Alloc for BoundedAlloc { - unsafe fn alloc(&mut self, layout: Layout) -> Result, AllocErr> { + unsafe fn alloc(&mut self, layout: Layout) -> Result, AllocErr> { let size = layout.size(); if size > self.fuel { return Err(AllocErr); @@ -783,7 +782,7 @@ mod tests { err @ Err(_) => err, } } - unsafe fn dealloc(&mut self, ptr: NonNull, layout: Layout) { + unsafe fn dealloc(&mut self, ptr: NonNull, layout: Layout) { Global.dealloc(ptr, layout) } } diff --git a/src/liballoc/rc.rs b/src/liballoc/rc.rs index 553c8b5ca32..84a6ecf7103 100644 --- a/src/liballoc/rc.rs +++ b/src/liballoc/rc.rs @@ -259,7 +259,7 @@ use core::ops::CoerceUnsized; use core::ptr::{self, NonNull}; use core::convert::From; -use alloc::{Global, Alloc, Layout, Opaque, box_free, oom}; +use alloc::{Global, Alloc, Layout, box_free, oom}; use string::String; use vec::Vec; @@ -732,7 +732,7 @@ impl RcFromSlice for Rc<[T]> { // In the event of a panic, elements that have been written // into the new RcBox will be dropped, then the memory freed. struct Guard { - mem: NonNull, + mem: NonNull, elems: *mut T, layout: Layout, n_elems: usize, @@ -755,7 +755,7 @@ impl RcFromSlice for Rc<[T]> { let v_ptr = v as *const [T]; let ptr = Self::allocate_for_ptr(v_ptr); - let mem = ptr as *mut _ as *mut Opaque; + let mem = ptr as *mut _ as *mut u8; let layout = Layout::for_value(&*ptr); // Pointer to first element @@ -839,7 +839,7 @@ unsafe impl<#[may_dangle] T: ?Sized> Drop for Rc { self.dec_weak(); if self.weak() == 0 { - Global.dealloc(self.ptr.as_opaque(), Layout::for_value(self.ptr.as_ref())); + Global.dealloc(self.ptr.cast(), Layout::for_value(self.ptr.as_ref())); } } } @@ -1263,7 +1263,7 @@ impl Drop for Weak { // the weak count starts at 1, and will only go to zero if all // the strong pointers have disappeared. if self.weak() == 0 { - Global.dealloc(self.ptr.as_opaque(), Layout::for_value(self.ptr.as_ref())); + Global.dealloc(self.ptr.cast(), Layout::for_value(self.ptr.as_ref())); } } } diff --git a/src/liballoc_system/lib.rs b/src/liballoc_system/lib.rs index 9490b54e675..82fda8d639e 100644 --- a/src/liballoc_system/lib.rs +++ b/src/liballoc_system/lib.rs @@ -41,7 +41,7 @@ const MIN_ALIGN: usize = 8; #[allow(dead_code)] const MIN_ALIGN: usize = 16; -use core::alloc::{Alloc, GlobalAlloc, AllocErr, Layout, Opaque}; +use core::alloc::{Alloc, GlobalAlloc, AllocErr, Layout}; use core::ptr::NonNull; #[unstable(feature = "allocator_api", issue = "32838")] @@ -50,45 +50,45 @@ pub struct System; #[unstable(feature = "allocator_api", issue = "32838")] unsafe impl Alloc for System { #[inline] - unsafe fn alloc(&mut self, layout: Layout) -> Result, AllocErr> { + unsafe fn alloc(&mut self, layout: Layout) -> Result, AllocErr> { NonNull::new(GlobalAlloc::alloc(self, layout)).ok_or(AllocErr) } #[inline] - unsafe fn alloc_zeroed(&mut self, layout: Layout) -> Result, AllocErr> { + unsafe fn alloc_zeroed(&mut self, layout: Layout) -> Result, AllocErr> { NonNull::new(GlobalAlloc::alloc_zeroed(self, layout)).ok_or(AllocErr) } #[inline] - unsafe fn dealloc(&mut self, ptr: NonNull, layout: Layout) { + unsafe fn dealloc(&mut self, ptr: NonNull, layout: Layout) { GlobalAlloc::dealloc(self, ptr.as_ptr(), layout) } #[inline] unsafe fn realloc(&mut self, - ptr: NonNull, + ptr: NonNull, layout: Layout, - new_size: usize) -> Result, AllocErr> { + new_size: usize) -> Result, AllocErr> { NonNull::new(GlobalAlloc::realloc(self, ptr.as_ptr(), layout, new_size)).ok_or(AllocErr) } } #[cfg(any(windows, unix, target_os = "cloudabi", target_os = "redox"))] mod realloc_fallback { - use core::alloc::{GlobalAlloc, Opaque, Layout}; + use core::alloc::{GlobalAlloc, Layout}; use core::cmp; use core::ptr; impl super::System { - pub(crate) unsafe fn realloc_fallback(&self, ptr: *mut Opaque, old_layout: Layout, - new_size: usize) -> *mut Opaque { + pub(crate) unsafe fn realloc_fallback(&self, ptr: *mut u8, old_layout: Layout, + new_size: usize) -> *mut u8 { // Docs for GlobalAlloc::realloc require this to be valid: let new_layout = Layout::from_size_align_unchecked(new_size, old_layout.align()); let new_ptr = GlobalAlloc::alloc(self, new_layout); if !new_ptr.is_null() { let size = cmp::min(old_layout.size(), new_size); - ptr::copy_nonoverlapping(ptr as *mut u8, new_ptr as *mut u8, size); + ptr::copy_nonoverlapping(ptr, new_ptr, size); GlobalAlloc::dealloc(self, ptr, old_layout); } new_ptr @@ -104,21 +104,19 @@ mod platform { use MIN_ALIGN; use System; - use core::alloc::{GlobalAlloc, Layout, Opaque}; + use core::alloc::{GlobalAlloc, Layout}; #[unstable(feature = "allocator_api", issue = "32838")] unsafe impl GlobalAlloc for System { #[inline] - unsafe fn alloc(&self, layout: Layout) -> *mut Opaque { + unsafe fn alloc(&self, layout: Layout) -> *mut u8 { if layout.align() <= MIN_ALIGN && layout.align() <= layout.size() { - libc::malloc(layout.size()) as *mut Opaque + libc::malloc(layout.size()) as *mut u8 } else { #[cfg(target_os = "macos")] { if layout.align() > (1 << 31) { - // FIXME: use Opaque::null_mut - // https://github.com/rust-lang/rust/issues/49659 - return 0 as *mut Opaque + return ptr::null_mut() } } aligned_malloc(&layout) @@ -126,27 +124,27 @@ mod platform { } #[inline] - unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut Opaque { + unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 { if layout.align() <= MIN_ALIGN && layout.align() <= layout.size() { - libc::calloc(layout.size(), 1) as *mut Opaque + libc::calloc(layout.size(), 1) as *mut u8 } else { let ptr = self.alloc(layout.clone()); if !ptr.is_null() { - ptr::write_bytes(ptr as *mut u8, 0, layout.size()); + ptr::write_bytes(ptr, 0, layout.size()); } ptr } } #[inline] - unsafe fn dealloc(&self, ptr: *mut Opaque, _layout: Layout) { + unsafe fn dealloc(&self, ptr: *mut u8, _layout: Layout) { libc::free(ptr as *mut libc::c_void) } #[inline] - unsafe fn realloc(&self, ptr: *mut Opaque, layout: Layout, new_size: usize) -> *mut Opaque { + unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 { if layout.align() <= MIN_ALIGN && layout.align() <= new_size { - libc::realloc(ptr as *mut libc::c_void, new_size) as *mut Opaque + libc::realloc(ptr as *mut libc::c_void, new_size) as *mut u8 } else { self.realloc_fallback(ptr, layout, new_size) } @@ -155,7 +153,7 @@ mod platform { #[cfg(any(target_os = "android", target_os = "redox", target_os = "solaris"))] #[inline] - unsafe fn aligned_malloc(layout: &Layout) -> *mut Opaque { + unsafe fn aligned_malloc(layout: &Layout) -> *mut u8 { // On android we currently target API level 9 which unfortunately // doesn't have the `posix_memalign` API used below. Instead we use // `memalign`, but this unfortunately has the property on some systems @@ -173,19 +171,18 @@ mod platform { // [3]: https://bugs.chromium.org/p/chromium/issues/detail?id=138579 // [4]: https://chromium.googlesource.com/chromium/src/base/+/master/ // /memory/aligned_memory.cc - libc::memalign(layout.align(), layout.size()) as *mut Opaque + libc::memalign(layout.align(), layout.size()) as *mut u8 } #[cfg(not(any(target_os = "android", target_os = "redox", target_os = "solaris")))] #[inline] - unsafe fn aligned_malloc(layout: &Layout) -> *mut Opaque { + unsafe fn aligned_malloc(layout: &Layout) -> *mut u8 { let mut out = ptr::null_mut(); let ret = libc::posix_memalign(&mut out, layout.align(), layout.size()); if ret != 0 { - // FIXME: use Opaque::null_mut https://github.com/rust-lang/rust/issues/49659 - 0 as *mut Opaque + ptr::null_mut() } else { - out as *mut Opaque + out as *mut u8 } } } @@ -195,7 +192,7 @@ mod platform { mod platform { use MIN_ALIGN; use System; - use core::alloc::{GlobalAlloc, Opaque, Layout}; + use core::alloc::{GlobalAlloc, Layout}; type LPVOID = *mut u8; type HANDLE = LPVOID; @@ -227,7 +224,7 @@ mod platform { } #[inline] - unsafe fn allocate_with_flags(layout: Layout, flags: DWORD) -> *mut Opaque { + unsafe fn allocate_with_flags(layout: Layout, flags: DWORD) -> *mut u8 { let ptr = if layout.align() <= MIN_ALIGN { HeapAlloc(GetProcessHeap(), flags, layout.size()) } else { @@ -239,29 +236,29 @@ mod platform { align_ptr(ptr, layout.align()) } }; - ptr as *mut Opaque + ptr as *mut u8 } #[unstable(feature = "allocator_api", issue = "32838")] unsafe impl GlobalAlloc for System { #[inline] - unsafe fn alloc(&self, layout: Layout) -> *mut Opaque { + unsafe fn alloc(&self, layout: Layout) -> *mut u8 { allocate_with_flags(layout, 0) } #[inline] - unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut Opaque { + unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 { allocate_with_flags(layout, HEAP_ZERO_MEMORY) } #[inline] - unsafe fn dealloc(&self, ptr: *mut Opaque, layout: Layout) { + unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) { if layout.align() <= MIN_ALIGN { let err = HeapFree(GetProcessHeap(), 0, ptr as LPVOID); debug_assert!(err != 0, "Failed to free heap memory: {}", GetLastError()); } else { - let header = get_header(ptr as *mut u8); + let header = get_header(ptr); let err = HeapFree(GetProcessHeap(), 0, header.0 as LPVOID); debug_assert!(err != 0, "Failed to free heap memory: {}", GetLastError()); @@ -269,9 +266,9 @@ mod platform { } #[inline] - unsafe fn realloc(&self, ptr: *mut Opaque, layout: Layout, new_size: usize) -> *mut Opaque { + unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 { if layout.align() <= MIN_ALIGN { - HeapReAlloc(GetProcessHeap(), 0, ptr as LPVOID, new_size) as *mut Opaque + HeapReAlloc(GetProcessHeap(), 0, ptr as LPVOID, new_size) as *mut u8 } else { self.realloc_fallback(ptr, layout, new_size) } @@ -300,7 +297,7 @@ mod platform { mod platform { extern crate dlmalloc; - use core::alloc::{GlobalAlloc, Layout, Opaque}; + use core::alloc::{GlobalAlloc, Layout}; use System; // No need for synchronization here as wasm is currently single-threaded @@ -309,23 +306,23 @@ mod platform { #[unstable(feature = "allocator_api", issue = "32838")] unsafe impl GlobalAlloc for System { #[inline] - unsafe fn alloc(&self, layout: Layout) -> *mut Opaque { - DLMALLOC.malloc(layout.size(), layout.align()) as *mut Opaque + unsafe fn alloc(&self, layout: Layout) -> *mut u8 { + DLMALLOC.malloc(layout.size(), layout.align()) } #[inline] - unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut Opaque { - DLMALLOC.calloc(layout.size(), layout.align()) as *mut Opaque + unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 { + DLMALLOC.calloc(layout.size(), layout.align()) } #[inline] - unsafe fn dealloc(&self, ptr: *mut Opaque, layout: Layout) { - DLMALLOC.free(ptr as *mut u8, layout.size(), layout.align()) + unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) { + DLMALLOC.free(ptr, layout.size(), layout.align()) } #[inline] - unsafe fn realloc(&self, ptr: *mut Opaque, layout: Layout, new_size: usize) -> *mut Opaque { - DLMALLOC.realloc(ptr as *mut u8, layout.size(), layout.align(), new_size) as *mut Opaque + unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 { + DLMALLOC.realloc(ptr, layout.size(), layout.align(), new_size) } } } diff --git a/src/libcore/alloc.rs b/src/libcore/alloc.rs index 229758803c8..2815ef6400d 100644 --- a/src/libcore/alloc.rs +++ b/src/libcore/alloc.rs @@ -22,30 +22,13 @@ use usize; use ptr::{self, NonNull}; use num::NonZeroUsize; -extern { - /// An opaque, unsized type. Used for pointers to allocated memory. - /// - /// This type can only be used behind a pointer like `*mut Opaque` or `ptr::NonNull`. - /// Such pointers are similar to C’s `void*` type. - pub type Opaque; -} - -impl Opaque { - /// Similar to `std::ptr::null`, which requires `T: Sized`. - pub fn null() -> *const Self { - 0 as _ - } - - /// Similar to `std::ptr::null_mut`, which requires `T: Sized`. - pub fn null_mut() -> *mut Self { - 0 as _ - } -} +#[cfg(stage0)] +pub type Opaque = u8; /// Represents the combination of a starting address and /// a total capacity of the returned block. #[derive(Debug)] -pub struct Excess(pub NonNull, pub usize); +pub struct Excess(pub NonNull, pub usize); fn size_align() -> (usize, usize) { (mem::size_of::(), mem::align_of::()) @@ -417,7 +400,7 @@ pub unsafe trait GlobalAlloc { /// # Safety /// /// **FIXME:** what are the exact requirements? - unsafe fn alloc(&self, layout: Layout) -> *mut Opaque; + unsafe fn alloc(&self, layout: Layout) -> *mut u8; /// Deallocate the block of memory at the given `ptr` pointer with the given `layout`. /// @@ -425,13 +408,13 @@ pub unsafe trait GlobalAlloc { /// /// **FIXME:** what are the exact requirements? /// In particular around layout *fit*. (See docs for the `Alloc` trait.) - unsafe fn dealloc(&self, ptr: *mut Opaque, layout: Layout); + unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout); - unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut Opaque { + unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 { let size = layout.size(); let ptr = self.alloc(layout); if !ptr.is_null() { - ptr::write_bytes(ptr as *mut u8, 0, size); + ptr::write_bytes(ptr, 0, size); } ptr } @@ -452,13 +435,13 @@ pub unsafe trait GlobalAlloc { /// /// **FIXME:** what are the exact requirements? /// In particular around layout *fit*. (See docs for the `Alloc` trait.) - unsafe fn realloc(&self, ptr: *mut Opaque, layout: Layout, new_size: usize) -> *mut Opaque { + unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 { let new_layout = Layout::from_size_align_unchecked(new_size, layout.align()); let new_ptr = self.alloc(new_layout); if !new_ptr.is_null() { ptr::copy_nonoverlapping( - ptr as *const u8, - new_ptr as *mut u8, + ptr, + new_ptr, cmp::min(layout.size(), new_size), ); self.dealloc(ptr, layout); @@ -598,7 +581,7 @@ pub unsafe trait Alloc { /// Clients wishing to abort computation in response to an /// allocation error are encouraged to call the allocator's `oom` /// method, rather than directly invoking `panic!` or similar. - unsafe fn alloc(&mut self, layout: Layout) -> Result, AllocErr>; + unsafe fn alloc(&mut self, layout: Layout) -> Result, AllocErr>; /// Deallocate the memory referenced by `ptr`. /// @@ -615,7 +598,7 @@ pub unsafe trait Alloc { /// * In addition to fitting the block of memory `layout`, the /// alignment of the `layout` must match the alignment used /// to allocate that block of memory. - unsafe fn dealloc(&mut self, ptr: NonNull, layout: Layout); + unsafe fn dealloc(&mut self, ptr: NonNull, layout: Layout); // == ALLOCATOR-SPECIFIC QUANTITIES AND LIMITS == // usable_size @@ -707,9 +690,9 @@ pub unsafe trait Alloc { /// reallocation error are encouraged to call the allocator's `oom` /// method, rather than directly invoking `panic!` or similar. unsafe fn realloc(&mut self, - ptr: NonNull, + ptr: NonNull, layout: Layout, - new_size: usize) -> Result, AllocErr> { + new_size: usize) -> Result, AllocErr> { let old_size = layout.size(); if new_size >= old_size { @@ -726,8 +709,8 @@ pub unsafe trait Alloc { let new_layout = Layout::from_size_align_unchecked(new_size, layout.align()); let result = self.alloc(new_layout); if let Ok(new_ptr) = result { - ptr::copy_nonoverlapping(ptr.as_ptr() as *const u8, - new_ptr.as_ptr() as *mut u8, + ptr::copy_nonoverlapping(ptr.as_ptr(), + new_ptr.as_ptr(), cmp::min(old_size, new_size)); self.dealloc(ptr, layout); } @@ -750,11 +733,11 @@ pub unsafe trait Alloc { /// Clients wishing to abort computation in response to an /// allocation error are encouraged to call the allocator's `oom` /// method, rather than directly invoking `panic!` or similar. - unsafe fn alloc_zeroed(&mut self, layout: Layout) -> Result, AllocErr> { + unsafe fn alloc_zeroed(&mut self, layout: Layout) -> Result, AllocErr> { let size = layout.size(); let p = self.alloc(layout); if let Ok(p) = p { - ptr::write_bytes(p.as_ptr() as *mut u8, 0, size); + ptr::write_bytes(p.as_ptr(), 0, size); } p } @@ -799,7 +782,7 @@ pub unsafe trait Alloc { /// reallocation error are encouraged to call the allocator's `oom` /// method, rather than directly invoking `panic!` or similar. unsafe fn realloc_excess(&mut self, - ptr: NonNull, + ptr: NonNull, layout: Layout, new_size: usize) -> Result { let new_layout = Layout::from_size_align_unchecked(new_size, layout.align()); @@ -844,7 +827,7 @@ pub unsafe trait Alloc { /// `grow_in_place` failures without aborting, or to fall back on /// another reallocation method before resorting to an abort. unsafe fn grow_in_place(&mut self, - ptr: NonNull, + ptr: NonNull, layout: Layout, new_size: usize) -> Result<(), CannotReallocInPlace> { let _ = ptr; // this default implementation doesn't care about the actual address. @@ -899,7 +882,7 @@ pub unsafe trait Alloc { /// `shrink_in_place` failures without aborting, or to fall back /// on another reallocation method before resorting to an abort. unsafe fn shrink_in_place(&mut self, - ptr: NonNull, + ptr: NonNull, layout: Layout, new_size: usize) -> Result<(), CannotReallocInPlace> { let _ = ptr; // this default implementation doesn't care about the actual address. @@ -978,7 +961,7 @@ pub unsafe trait Alloc { { let k = Layout::new::(); if k.size() > 0 { - self.dealloc(ptr.as_opaque(), k); + self.dealloc(ptr.cast(), k); } } @@ -1066,7 +1049,7 @@ pub unsafe trait Alloc { match (Layout::array::(n_old), Layout::array::(n_new)) { (Ok(ref k_old), Ok(ref k_new)) if k_old.size() > 0 && k_new.size() > 0 => { debug_assert!(k_old.align() == k_new.align()); - self.realloc(ptr.as_opaque(), k_old.clone(), k_new.size()).map(NonNull::cast) + self.realloc(ptr.cast(), k_old.clone(), k_new.size()).map(NonNull::cast) } _ => { Err(AllocErr) @@ -1099,7 +1082,7 @@ pub unsafe trait Alloc { { match Layout::array::(n) { Ok(ref k) if k.size() > 0 => { - Ok(self.dealloc(ptr.as_opaque(), k.clone())) + Ok(self.dealloc(ptr.cast(), k.clone())) } _ => { Err(AllocErr) diff --git a/src/libcore/ptr.rs b/src/libcore/ptr.rs index 39315d8f0c8..81a8b3ef047 100644 --- a/src/libcore/ptr.rs +++ b/src/libcore/ptr.rs @@ -2922,14 +2922,6 @@ impl NonNull { NonNull::new_unchecked(self.as_ptr() as *mut U) } } - - /// Cast to an `Opaque` pointer - #[unstable(feature = "allocator_api", issue = "32838")] - pub fn as_opaque(self) -> NonNull<::alloc::Opaque> { - unsafe { - NonNull::new_unchecked(self.as_ptr() as _) - } - } } #[stable(feature = "nonnull", since = "1.25.0")] diff --git a/src/librustc_allocator/expand.rs b/src/librustc_allocator/expand.rs index 497d5fdcac7..ec0676259ef 100644 --- a/src/librustc_allocator/expand.rs +++ b/src/librustc_allocator/expand.rs @@ -237,7 +237,7 @@ impl<'a> AllocFnFactory<'a> { let ident = ident(); args.push(self.cx.arg(self.span, ident, self.ptr_u8())); let arg = self.cx.expr_ident(self.span, ident); - self.cx.expr_cast(self.span, arg, self.ptr_opaque()) + self.cx.expr_cast(self.span, arg, self.ptr_u8()) } AllocatorTy::Usize => { @@ -281,17 +281,4 @@ impl<'a> AllocFnFactory<'a> { let ty_u8 = self.cx.ty_path(u8); self.cx.ty_ptr(self.span, ty_u8, Mutability::Mutable) } - - fn ptr_opaque(&self) -> P { - let opaque = self.cx.path( - self.span, - vec![ - self.core, - Ident::from_str("alloc"), - Ident::from_str("Opaque"), - ], - ); - let ty_opaque = self.cx.ty_path(opaque); - self.cx.ty_ptr(self.span, ty_opaque, Mutability::Mutable) - } } diff --git a/src/libstd/alloc.rs b/src/libstd/alloc.rs index 3b1a3a439e7..ac7da5e9dba 100644 --- a/src/libstd/alloc.rs +++ b/src/libstd/alloc.rs @@ -74,7 +74,7 @@ pub extern fn rust_oom(layout: Layout) -> ! { #[doc(hidden)] #[allow(unused_attributes)] pub mod __default_lib_allocator { - use super::{System, Layout, GlobalAlloc, Opaque}; + use super::{System, Layout, GlobalAlloc}; // for symbol names src/librustc/middle/allocator.rs // for signatures src/librustc_allocator/lib.rs @@ -85,7 +85,7 @@ pub mod __default_lib_allocator { #[rustc_std_internal_symbol] pub unsafe extern fn __rdl_alloc(size: usize, align: usize) -> *mut u8 { let layout = Layout::from_size_align_unchecked(size, align); - System.alloc(layout) as *mut u8 + System.alloc(layout) } #[no_mangle] @@ -93,7 +93,7 @@ pub mod __default_lib_allocator { pub unsafe extern fn __rdl_dealloc(ptr: *mut u8, size: usize, align: usize) { - System.dealloc(ptr as *mut Opaque, Layout::from_size_align_unchecked(size, align)) + System.dealloc(ptr, Layout::from_size_align_unchecked(size, align)) } #[no_mangle] @@ -103,13 +103,13 @@ pub mod __default_lib_allocator { align: usize, new_size: usize) -> *mut u8 { let old_layout = Layout::from_size_align_unchecked(old_size, align); - System.realloc(ptr as *mut Opaque, old_layout, new_size) as *mut u8 + System.realloc(ptr, old_layout, new_size) } #[no_mangle] #[rustc_std_internal_symbol] pub unsafe extern fn __rdl_alloc_zeroed(size: usize, align: usize) -> *mut u8 { let layout = Layout::from_size_align_unchecked(size, align); - System.alloc_zeroed(layout) as *mut u8 + System.alloc_zeroed(layout) } } diff --git a/src/libstd/collections/hash/table.rs b/src/libstd/collections/hash/table.rs index d997fb28d42..55f9f4f7cfe 100644 --- a/src/libstd/collections/hash/table.rs +++ b/src/libstd/collections/hash/table.rs @@ -1124,7 +1124,7 @@ unsafe impl<#[may_dangle] K, #[may_dangle] V> Drop for RawTable { let (layout, _) = calculate_layout::(self.capacity()) .unwrap_or_else(|_| unsafe { hint::unreachable_unchecked() }); unsafe { - Global.dealloc(NonNull::new_unchecked(self.hashes.ptr()).as_opaque(), layout); + Global.dealloc(NonNull::new_unchecked(self.hashes.ptr()).cast(), layout); // Remember how everything was allocated out of one buffer // during initialization? We only need one call to free here. } diff --git a/src/test/run-make-fulldeps/std-core-cycle/bar.rs b/src/test/run-make-fulldeps/std-core-cycle/bar.rs index 62fd2ade1ca..4b885e5e2bb 100644 --- a/src/test/run-make-fulldeps/std-core-cycle/bar.rs +++ b/src/test/run-make-fulldeps/std-core-cycle/bar.rs @@ -16,11 +16,11 @@ use std::alloc::*; pub struct A; unsafe impl GlobalAlloc for A { - unsafe fn alloc(&self, _: Layout) -> *mut Opaque { + unsafe fn alloc(&self, _: Layout) -> *mut u8 { loop {} } - unsafe fn dealloc(&self, _ptr: *mut Opaque, _: Layout) { + unsafe fn dealloc(&self, _ptr: *mut u8, _: Layout) { loop {} } } diff --git a/src/test/run-pass/allocator/auxiliary/custom.rs b/src/test/run-pass/allocator/auxiliary/custom.rs index 91f70aa83e8..02e86fa19f8 100644 --- a/src/test/run-pass/allocator/auxiliary/custom.rs +++ b/src/test/run-pass/allocator/auxiliary/custom.rs @@ -13,18 +13,18 @@ #![feature(heap_api, allocator_api)] #![crate_type = "rlib"] -use std::alloc::{GlobalAlloc, System, Layout, Opaque}; +use std::alloc::{GlobalAlloc, System, Layout}; use std::sync::atomic::{AtomicUsize, Ordering}; pub struct A(pub AtomicUsize); unsafe impl GlobalAlloc for A { - unsafe fn alloc(&self, layout: Layout) -> *mut Opaque { + unsafe fn alloc(&self, layout: Layout) -> *mut u8 { self.0.fetch_add(1, Ordering::SeqCst); System.alloc(layout) } - unsafe fn dealloc(&self, ptr: *mut Opaque, layout: Layout) { + unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) { self.0.fetch_add(1, Ordering::SeqCst); System.dealloc(ptr, layout) } diff --git a/src/test/run-pass/allocator/custom.rs b/src/test/run-pass/allocator/custom.rs index 415d39a593e..5da13bd4cb9 100644 --- a/src/test/run-pass/allocator/custom.rs +++ b/src/test/run-pass/allocator/custom.rs @@ -15,7 +15,7 @@ extern crate helper; -use std::alloc::{self, Global, Alloc, System, Layout, Opaque}; +use std::alloc::{self, Global, Alloc, System, Layout}; use std::sync::atomic::{AtomicUsize, Ordering, ATOMIC_USIZE_INIT}; static HITS: AtomicUsize = ATOMIC_USIZE_INIT; @@ -23,12 +23,12 @@ static HITS: AtomicUsize = ATOMIC_USIZE_INIT; struct A; unsafe impl alloc::GlobalAlloc for A { - unsafe fn alloc(&self, layout: Layout) -> *mut Opaque { + unsafe fn alloc(&self, layout: Layout) -> *mut u8 { HITS.fetch_add(1, Ordering::SeqCst); System.alloc(layout) } - unsafe fn dealloc(&self, ptr: *mut Opaque, layout: Layout) { + unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) { HITS.fetch_add(1, Ordering::SeqCst); System.dealloc(ptr, layout) } diff --git a/src/test/run-pass/realloc-16687.rs b/src/test/run-pass/realloc-16687.rs index febd249d776..355053858cc 100644 --- a/src/test/run-pass/realloc-16687.rs +++ b/src/test/run-pass/realloc-16687.rs @@ -64,7 +64,7 @@ unsafe fn test_triangle() -> bool { println!("deallocate({:?}, {:?}", ptr, layout); } - Global.dealloc(NonNull::new_unchecked(ptr).as_opaque(), layout); + Global.dealloc(NonNull::new_unchecked(ptr).cast(), layout); } unsafe fn reallocate(ptr: *mut u8, old: Layout, new: Layout) -> *mut u8 { @@ -72,7 +72,7 @@ unsafe fn test_triangle() -> bool { println!("reallocate({:?}, old={:?}, new={:?})", ptr, old, new); } - let ret = Global.realloc(NonNull::new_unchecked(ptr).as_opaque(), old, new.size()) + let ret = Global.realloc(NonNull::new_unchecked(ptr).cast(), old, new.size()) .unwrap_or_else(|_| oom(Layout::from_size_align_unchecked(new.size(), old.align()))); if PRINT { -- cgit 1.4.1-3-g733a5 From 3373204ac49ebdb7194020ea9c556ce87910f7b7 Mon Sep 17 00:00:00 2001 From: Mike Hommey Date: Thu, 31 May 2018 16:10:01 +0900 Subject: Replace `impl GlobalAlloc for Global` with a set of free functions --- src/liballoc/alloc.rs | 48 ++++++++++++++++-------------- src/libstd/alloc.rs | 1 + src/test/run-pass/allocator/xcrate-use2.rs | 6 ++-- 3 files changed, 29 insertions(+), 26 deletions(-) (limited to 'src/liballoc/alloc.rs') diff --git a/src/liballoc/alloc.rs b/src/liballoc/alloc.rs index 102910f4198..8c2dda77226 100644 --- a/src/liballoc/alloc.rs +++ b/src/liballoc/alloc.rs @@ -49,37 +49,39 @@ pub type Heap = Global; #[allow(non_upper_case_globals)] pub const Heap: Global = Global; -unsafe impl GlobalAlloc for Global { - #[inline] - unsafe fn alloc(&self, layout: Layout) -> *mut u8 { - __rust_alloc(layout.size(), layout.align()) - } +#[unstable(feature = "allocator_api", issue = "32838")] +#[inline] +pub unsafe fn alloc(layout: Layout) -> *mut u8 { + __rust_alloc(layout.size(), layout.align()) +} - #[inline] - unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) { - __rust_dealloc(ptr, layout.size(), layout.align()) - } +#[unstable(feature = "allocator_api", issue = "32838")] +#[inline] +pub unsafe fn dealloc(ptr: *mut u8, layout: Layout) { + __rust_dealloc(ptr, layout.size(), layout.align()) +} - #[inline] - unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 { - __rust_realloc(ptr, layout.size(), layout.align(), new_size) - } +#[unstable(feature = "allocator_api", issue = "32838")] +#[inline] +pub unsafe fn realloc(ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 { + __rust_realloc(ptr, layout.size(), layout.align(), new_size) +} - #[inline] - unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 { - __rust_alloc_zeroed(layout.size(), layout.align()) - } +#[unstable(feature = "allocator_api", issue = "32838")] +#[inline] +pub unsafe fn alloc_zeroed(layout: Layout) -> *mut u8 { + __rust_alloc_zeroed(layout.size(), layout.align()) } unsafe impl Alloc for Global { #[inline] unsafe fn alloc(&mut self, layout: Layout) -> Result, AllocErr> { - NonNull::new(GlobalAlloc::alloc(self, layout)).ok_or(AllocErr) + NonNull::new(alloc(layout)).ok_or(AllocErr) } #[inline] unsafe fn dealloc(&mut self, ptr: NonNull, layout: Layout) { - GlobalAlloc::dealloc(self, ptr.as_ptr(), layout) + dealloc(ptr.as_ptr(), layout) } #[inline] @@ -89,12 +91,12 @@ unsafe impl Alloc for Global { new_size: usize) -> Result, AllocErr> { - NonNull::new(GlobalAlloc::realloc(self, ptr.as_ptr(), layout, new_size)).ok_or(AllocErr) + NonNull::new(realloc(ptr.as_ptr(), layout, new_size)).ok_or(AllocErr) } #[inline] unsafe fn alloc_zeroed(&mut self, layout: Layout) -> Result, AllocErr> { - NonNull::new(GlobalAlloc::alloc_zeroed(self, layout)).ok_or(AllocErr) + NonNull::new(alloc_zeroed(layout)).ok_or(AllocErr) } } @@ -108,7 +110,7 @@ unsafe fn exchange_malloc(size: usize, align: usize) -> *mut u8 { align as *mut u8 } else { let layout = Layout::from_size_align_unchecked(size, align); - let ptr = Global.alloc(layout); + let ptr = alloc(layout); if !ptr.is_null() { ptr } else { @@ -126,7 +128,7 @@ pub(crate) unsafe fn box_free(ptr: Unique) { // We do not allocate for Box when T is ZST, so deallocation is also not necessary. if size != 0 { let layout = Layout::from_size_align_unchecked(size, align); - Global.dealloc(ptr as *mut u8, layout); + dealloc(ptr as *mut u8, layout); } } diff --git a/src/libstd/alloc.rs b/src/libstd/alloc.rs index ac7da5e9dba..3f31fa8e1dd 100644 --- a/src/libstd/alloc.rs +++ b/src/libstd/alloc.rs @@ -14,6 +14,7 @@ #[doc(inline)] #[allow(deprecated)] pub use alloc_crate::alloc::Heap; #[doc(inline)] pub use alloc_crate::alloc::{Global, Layout, oom}; +#[doc(inline)] pub use alloc_crate::alloc::{alloc, alloc_zeroed, dealloc, realloc}; #[doc(inline)] pub use alloc_system::System; #[doc(inline)] pub use core::alloc::*; diff --git a/src/test/run-pass/allocator/xcrate-use2.rs b/src/test/run-pass/allocator/xcrate-use2.rs index b8e844522dc..fbde7e855c2 100644 --- a/src/test/run-pass/allocator/xcrate-use2.rs +++ b/src/test/run-pass/allocator/xcrate-use2.rs @@ -19,7 +19,7 @@ extern crate custom; extern crate custom_as_global; extern crate helper; -use std::alloc::{Global, Alloc, GlobalAlloc, System, Layout}; +use std::alloc::{alloc, dealloc, GlobalAlloc, System, Layout}; use std::sync::atomic::{Ordering, ATOMIC_USIZE_INIT}; static GLOBAL: custom::A = custom::A(ATOMIC_USIZE_INIT); @@ -30,10 +30,10 @@ fn main() { let layout = Layout::from_size_align(4, 2).unwrap(); // Global allocator routes to the `custom_as_global` global - let ptr = Global.alloc(layout.clone()); + let ptr = alloc(layout.clone()); helper::work_with(&ptr); assert_eq!(custom_as_global::get(), n + 1); - Global.dealloc(ptr, layout.clone()); + dealloc(ptr, layout.clone()); assert_eq!(custom_as_global::get(), n + 2); // Usage of the system allocator avoids all globals -- cgit 1.4.1-3-g733a5 From 11f992c9584f05f56664627ac1ec42e4cd1f0e3e Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Wed, 30 May 2018 21:15:40 +0200 Subject: Remove the deprecated Heap type/const --- src/liballoc/alloc.rs | 9 --------- src/libstd/alloc.rs | 1 - 2 files changed, 10 deletions(-) (limited to 'src/liballoc/alloc.rs') diff --git a/src/liballoc/alloc.rs b/src/liballoc/alloc.rs index 8c2dda77226..710da9a475b 100644 --- a/src/liballoc/alloc.rs +++ b/src/liballoc/alloc.rs @@ -40,15 +40,6 @@ extern "Rust" { #[derive(Copy, Clone, Default, Debug)] pub struct Global; -#[unstable(feature = "allocator_api", issue = "32838")] -#[rustc_deprecated(since = "1.27.0", reason = "type renamed to `Global`")] -pub type Heap = Global; - -#[unstable(feature = "allocator_api", issue = "32838")] -#[rustc_deprecated(since = "1.27.0", reason = "type renamed to `Global`")] -#[allow(non_upper_case_globals)] -pub const Heap: Global = Global; - #[unstable(feature = "allocator_api", issue = "32838")] #[inline] pub unsafe fn alloc(layout: Layout) -> *mut u8 { diff --git a/src/libstd/alloc.rs b/src/libstd/alloc.rs index 3f31fa8e1dd..9904634f1fa 100644 --- a/src/libstd/alloc.rs +++ b/src/libstd/alloc.rs @@ -12,7 +12,6 @@ #![unstable(issue = "32838", feature = "allocator_api")] -#[doc(inline)] #[allow(deprecated)] pub use alloc_crate::alloc::Heap; #[doc(inline)] pub use alloc_crate::alloc::{Global, Layout, oom}; #[doc(inline)] pub use alloc_crate::alloc::{alloc, alloc_zeroed, dealloc, realloc}; #[doc(inline)] pub use alloc_system::System; -- cgit 1.4.1-3-g733a5 From e9fd063edb4f6783fbd91a82a0f61626dacf8dad Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Thu, 31 May 2018 18:23:42 +0200 Subject: Document memory allocation APIs Add some docs where they were missing, attempt to fix them where they were out of date. --- src/liballoc/alloc.rs | 65 +++++++++++++ src/liballoc_system/lib.rs | 1 + src/libcore/alloc.rs | 227 +++++++++++++++++++++++++++++++++++---------- src/libstd/alloc.rs | 2 +- 4 files changed, 245 insertions(+), 50 deletions(-) (limited to 'src/liballoc/alloc.rs') diff --git a/src/liballoc/alloc.rs b/src/liballoc/alloc.rs index 710da9a475b..c9430a29e4c 100644 --- a/src/liballoc/alloc.rs +++ b/src/liballoc/alloc.rs @@ -8,6 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +//! Memory allocation APIs + #![unstable(feature = "allocator_api", reason = "the precise API and guarantees it provides may be tweaked \ slightly, especially to possibly take into account the \ @@ -37,27 +39,80 @@ extern "Rust" { fn __rust_alloc_zeroed(size: usize, align: usize) -> *mut u8; } +/// The global memory allocator. +/// +/// This type implements the [`Alloc`] trait by forwarding calls +/// to the allocator registered with the `#[global_allocator]` attribute +/// if there is one, or the `std` crate’s default. #[derive(Copy, Clone, Default, Debug)] pub struct Global; +/// Allocate memory with the global allocator. +/// +/// This function forwards calls to the [`GlobalAlloc::alloc`] method +/// of the allocator registered with the `#[global_allocator]` attribute +/// if there is one, or the `std` crate’s default. +/// +/// This function is expected to be deprecated in favor of the `alloc` method +/// of the [`Global`] type when it and the [`Alloc`] trait become stable. +/// +/// # Safety +/// +/// See [`GlobalAlloc::alloc`]. #[unstable(feature = "allocator_api", issue = "32838")] #[inline] pub unsafe fn alloc(layout: Layout) -> *mut u8 { __rust_alloc(layout.size(), layout.align()) } +/// Deallocate memory with the global allocator. +/// +/// This function forwards calls to the [`GlobalAlloc::dealloc`] method +/// of the allocator registered with the `#[global_allocator]` attribute +/// if there is one, or the `std` crate’s default. +/// +/// This function is expected to be deprecated in favor of the `dealloc` method +/// of the [`Global`] type when it and the [`Alloc`] trait become stable. +/// +/// # Safety +/// +/// See [`GlobalAlloc::dealloc`]. #[unstable(feature = "allocator_api", issue = "32838")] #[inline] pub unsafe fn dealloc(ptr: *mut u8, layout: Layout) { __rust_dealloc(ptr, layout.size(), layout.align()) } +/// Reallocate memory with the global allocator. +/// +/// This function forwards calls to the [`GlobalAlloc::realloc`] method +/// of the allocator registered with the `#[global_allocator]` attribute +/// if there is one, or the `std` crate’s default. +/// +/// This function is expected to be deprecated in favor of the `realloc` method +/// of the [`Global`] type when it and the [`Alloc`] trait become stable. +/// +/// # Safety +/// +/// See [`GlobalAlloc::realloc`]. #[unstable(feature = "allocator_api", issue = "32838")] #[inline] pub unsafe fn realloc(ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 { __rust_realloc(ptr, layout.size(), layout.align(), new_size) } +/// Allocate zero-initialized memory with the global allocator. +/// +/// This function forwards calls to the [`GlobalAlloc::alloc_zeroed`] method +/// of the allocator registered with the `#[global_allocator]` attribute +/// if there is one, or the `std` crate’s default. +/// +/// This function is expected to be deprecated in favor of the `alloc_zeroed` method +/// of the [`Global`] type when it and the [`Alloc`] trait become stable. +/// +/// # Safety +/// +/// See [`GlobalAlloc::alloc_zeroed`]. #[unstable(feature = "allocator_api", issue = "32838")] #[inline] pub unsafe fn alloc_zeroed(layout: Layout) -> *mut u8 { @@ -123,6 +178,16 @@ pub(crate) unsafe fn box_free(ptr: Unique) { } } +/// Abort on memory allocation error or failure. +/// +/// Callers of memory allocation APIs wishing to abort computation +/// in response to an allocation error are encouraged to call this function, +/// rather than directly invoking `panic!` or similar. +/// +/// The default behavior of this function is to print a message to standard error +/// and abort the process. +/// It can be replaced with [`std::alloc::set_oom_hook`] +/// and [`std::alloc::take_oom_hook`]. #[rustc_allocator_nounwind] pub fn oom(layout: Layout) -> ! { #[allow(improper_ctypes)] diff --git a/src/liballoc_system/lib.rs b/src/liballoc_system/lib.rs index 82fda8d639e..85bf43a5429 100644 --- a/src/liballoc_system/lib.rs +++ b/src/liballoc_system/lib.rs @@ -44,6 +44,7 @@ const MIN_ALIGN: usize = 16; use core::alloc::{Alloc, GlobalAlloc, AllocErr, Layout}; use core::ptr::NonNull; +/// The default memory allocator provided by the operating system. #[unstable(feature = "allocator_api", issue = "32838")] pub struct System; diff --git a/src/libcore/alloc.rs b/src/libcore/alloc.rs index 2815ef6400d..8fcd8555cdc 100644 --- a/src/libcore/alloc.rs +++ b/src/libcore/alloc.rs @@ -8,6 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +//! Memory allocation APIs + #![unstable(feature = "allocator_api", reason = "the precise API and guarantees it provides may be tweaked \ slightly, especially to possibly take into account the \ @@ -315,8 +317,9 @@ impl Layout { } } -/// The parameters given to `Layout::from_size_align` do not satisfy -/// its documented constraints. +/// The parameters given to `Layout::from_size_align` +/// or some other `Layout` constructor +/// do not satisfy its documented constraints. #[derive(Clone, PartialEq, Eq, Debug)] pub struct LayoutErr { private: () @@ -329,8 +332,8 @@ impl fmt::Display for LayoutErr { } } -/// The `AllocErr` error specifies whether an allocation failure is -/// specifically due to resource exhaustion or if it is due to +/// The `AllocErr` error indicates an allocation failure +/// that may be due to resource exhaustion or to /// something wrong when combining the given input arguments with this /// allocator. #[derive(Clone, PartialEq, Eq, Debug)] @@ -346,6 +349,7 @@ impl fmt::Display for AllocErr { /// The `CannotReallocInPlace` error is used when `grow_in_place` or /// `shrink_in_place` were unable to reuse the given memory block for /// a requested layout. +// FIXME: should this be in libcore or liballoc? #[derive(Clone, PartialEq, Eq, Debug)] pub struct CannotReallocInPlace; @@ -363,6 +367,7 @@ impl fmt::Display for CannotReallocInPlace { } /// Augments `AllocErr` with a CapacityOverflow variant. +// FIXME: should this be in libcore or liballoc? #[derive(Clone, PartialEq, Eq, Debug)] #[unstable(feature = "try_reserve", reason = "new API", issue="48043")] pub enum CollectionAllocErr { @@ -389,27 +394,125 @@ impl From for CollectionAllocErr { } } -/// A memory allocator that can be registered to be the one backing `std::alloc::Global` +/// A memory allocator that can be registered as the standard library’s default /// though the `#[global_allocator]` attributes. +/// +/// Some of the methods require that a memory block be *currently +/// allocated* via an allocator. This means that: +/// +/// * the starting address for that memory block was previously +/// returned by a previous call to an allocation method +/// such as `alloc`, and +/// +/// * the memory block has not been subsequently deallocated, where +/// blocks are deallocated either by being passed to a deallocation +/// method such as `dealloc` or by being +/// passed to a reallocation method that returns a non-null pointer. +/// +/// +/// # Example +/// +/// ```no_run +/// use std::alloc::{GlobalAlloc, Layout, alloc}; +/// use std::ptr::null_mut; +/// +/// struct MyAllocator; +/// +/// unsafe impl GlobalAlloc for MyAllocator { +/// unsafe fn alloc(&self, _layout: Layout) -> *mut u8 { null_mut() } +/// unsafe fn dealloc(&self, _ptr: *mut u8, _layout: Layout) {} +/// } +/// +/// #[global_allocator] +/// static A: MyAllocator = MyAllocator; +/// +/// fn main() { +/// unsafe { +/// assert!(alloc(Layout::new::()).is_null()) +/// } +/// } +/// ``` +/// +/// # Unsafety +/// +/// The `GlobalAlloc` trait is an `unsafe` trait for a number of reasons, and +/// implementors must ensure that they adhere to these contracts: +/// +/// * Pointers returned from allocation functions must point to valid memory and +/// retain their validity until at least the instance of `GlobalAlloc` is dropped +/// itself. +/// +/// * It's undefined behavior if global allocators unwind. This restriction may +/// be lifted in the future, but currently a panic from any of these +/// functions may lead to memory unsafety. +/// +/// * `Layout` queries and calculations in general must be correct. Callers of +/// this trait are allowed to rely on the contracts defined on each method, +/// and implementors must ensure such contracts remain true. pub unsafe trait GlobalAlloc { /// Allocate memory as described by the given `layout`. /// /// Returns a pointer to newly-allocated memory, - /// or NULL to indicate allocation failure. + /// or null to indicate allocation failure. /// /// # Safety /// - /// **FIXME:** what are the exact requirements? + /// This function is unsafe because undefined behavior can result + /// if the caller does not ensure that `layout` has non-zero size. + /// + /// (Extension subtraits might provide more specific bounds on + /// behavior, e.g. guarantee a sentinel address or a null pointer + /// in response to a zero-size allocation request.) + /// + /// The allocated block of memory may or may not be initialized. + /// + /// # Errors + /// + /// Returning a null pointer indicates that either memory is exhausted + /// or `layout` does not meet allocator's size or alignment constraints. + /// + /// Implementations are encouraged to return null on memory + /// exhaustion rather than aborting, but this is not + /// a strict requirement. (Specifically: it is *legal* to + /// implement this trait atop an underlying native allocation + /// library that aborts on memory exhaustion.) + /// + /// Clients wishing to abort computation in response to an + /// allocation error are encouraged to call the [`oom`] function, + /// rather than directly invoking `panic!` or similar. unsafe fn alloc(&self, layout: Layout) -> *mut u8; /// Deallocate the block of memory at the given `ptr` pointer with the given `layout`. /// /// # Safety /// - /// **FIXME:** what are the exact requirements? - /// In particular around layout *fit*. (See docs for the `Alloc` trait.) + /// This function is unsafe because undefined behavior can result + /// if the caller does not ensure all of the following: + /// + /// * `ptr` must denote a block of memory currently allocated via + /// this allocator, + /// + /// * `layout` must be the same layout that was used + /// to allocated that block of memory, unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout); + /// Behaves like `alloc`, but also ensures that the contents + /// are set to zero before being returned. + /// + /// # Safety + /// + /// This function is unsafe for the same reasons that `alloc` is. + /// However the allocated block of memory is guaranteed to be initialized. + /// + /// # Errors + /// + /// Returning a null pointer indicates that either memory is exhausted + /// or `layout` does not meet allocator's size or alignment constraints, + /// just as in `alloc`. + /// + /// Clients wishing to abort computation in response to an + /// allocation error are encouraged to call the [`oom`] function, + /// rather than directly invoking `panic!` or similar. unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 { let size = layout.size(); let ptr = self.alloc(layout); @@ -422,19 +525,51 @@ pub unsafe trait GlobalAlloc { /// Shink or grow a block of memory to the given `new_size`. /// The block is described by the given `ptr` pointer and `layout`. /// - /// Return a new pointer (which may or may not be the same as `ptr`), - /// or NULL to indicate reallocation failure. + /// If this returns a non-null pointer, then ownership of the memory block + /// referenced by `ptr` has been transferred to this alloctor. + /// The memory may or may not have been deallocated, + /// and should be considered unusable (unless of course it was + /// transferred back to the caller again via the return value of + /// this method). /// - /// If reallocation is successful, the old `ptr` pointer is considered - /// to have been deallocated. + /// If this method returns null, then ownership of the memory + /// block has not been transferred to this allocator, and the + /// contents of the memory block are unaltered. /// /// # Safety /// - /// `new_size`, when rounded up to the nearest multiple of `old_layout.align()`, - /// must not overflow (i.e. the rounded value must be less than `usize::MAX`). + /// This function is unsafe because undefined behavior can result + /// if the caller does not ensure all of the following: + /// + /// * `ptr` must be currently allocated via this allocator, + /// + /// * `layout` must be the same layout that was used + /// to allocated that block of memory, + /// + /// * `new_size` must be greater than zero. + /// + /// * `new_size`, when rounded up to the nearest multiple of `layout.align()`, + /// must not overflow (i.e. the rounded value must be less than `usize::MAX`). + /// + /// (Extension subtraits might provide more specific bounds on + /// behavior, e.g. guarantee a sentinel address or a null pointer + /// in response to a zero-size allocation request.) + /// + /// # Errors + /// + /// Returns null if the new layout does not meet the size + /// and alignment constraints of the allocator, or if reallocation + /// otherwise fails. + /// + /// Implementations are encouraged to return null on memory + /// exhaustion rather than panicking or aborting, but this is not + /// a strict requirement. (Specifically: it is *legal* to + /// implement this trait atop an underlying native allocation + /// library that aborts on memory exhaustion.) /// - /// **FIXME:** what are the exact requirements? - /// In particular around layout *fit*. (See docs for the `Alloc` trait.) + /// Clients wishing to abort computation in response to a + /// reallocation error are encouraged to call the [`oom`] function, + /// rather than directly invoking `panic!` or similar. unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 { let new_layout = Layout::from_size_align_unchecked(new_size, layout.align()); let new_ptr = self.alloc(new_layout); @@ -523,27 +658,21 @@ pub unsafe trait GlobalAlloc { /// retain their validity until at least the instance of `Alloc` is dropped /// itself. /// -/// * It's undefined behavior if global allocators unwind. This restriction may -/// be lifted in the future, but currently a panic from any of these -/// functions may lead to memory unsafety. Note that as of the time of this -/// writing allocators *not* intending to be global allocators can still panic -/// in their implementation without violating memory safety. -/// /// * `Layout` queries and calculations in general must be correct. Callers of /// this trait are allowed to rely on the contracts defined on each method, /// and implementors must ensure such contracts remain true. /// /// Note that this list may get tweaked over time as clarifications are made in -/// the future. Additionally global allocators may gain unique requirements for -/// how to safely implement one in the future as well. +/// the future. pub unsafe trait Alloc { - // (Note: existing allocators have unspecified but well-defined + // (Note: some existing allocators have unspecified but well-defined // behavior in response to a zero size allocation request ; // e.g. in C, `malloc` of 0 will either return a null pointer or a // unique pointer, but will not have arbitrary undefined - // behavior. Rust should consider revising the alloc::heap crate - // to reflect this reality.) + // behavior. + // However in jemalloc for example, + // `mallocx(0)` is documented as undefined behavior.) /// Returns a pointer meeting the size and alignment guarantees of /// `layout`. @@ -579,8 +708,8 @@ pub unsafe trait Alloc { /// library that aborts on memory exhaustion.) /// /// Clients wishing to abort computation in response to an - /// allocation error are encouraged to call the allocator's `oom` - /// method, rather than directly invoking `panic!` or similar. + /// allocation error are encouraged to call the `oom` function, + /// rather than directly invoking `panic!` or similar. unsafe fn alloc(&mut self, layout: Layout) -> Result, AllocErr>; /// Deallocate the memory referenced by `ptr`. @@ -686,9 +815,9 @@ pub unsafe trait Alloc { /// implement this trait atop an underlying native allocation /// library that aborts on memory exhaustion.) /// - /// Clients wishing to abort computation in response to an - /// reallocation error are encouraged to call the allocator's `oom` - /// method, rather than directly invoking `panic!` or similar. + /// Clients wishing to abort computation in response to a + /// reallocation error are encouraged to call the [`oom`] function, + /// rather than directly invoking `panic!` or similar. unsafe fn realloc(&mut self, ptr: NonNull, layout: Layout, @@ -731,8 +860,8 @@ pub unsafe trait Alloc { /// constraints, just as in `alloc`. /// /// Clients wishing to abort computation in response to an - /// allocation error are encouraged to call the allocator's `oom` - /// method, rather than directly invoking `panic!` or similar. + /// allocation error are encouraged to call the [`oom`] function, + /// rather than directly invoking `panic!` or similar. unsafe fn alloc_zeroed(&mut self, layout: Layout) -> Result, AllocErr> { let size = layout.size(); let p = self.alloc(layout); @@ -757,8 +886,8 @@ pub unsafe trait Alloc { /// constraints, just as in `alloc`. /// /// Clients wishing to abort computation in response to an - /// allocation error are encouraged to call the allocator's `oom` - /// method, rather than directly invoking `panic!` or similar. + /// allocation error are encouraged to call the [`oom`] function, + /// rather than directly invoking `panic!` or similar. unsafe fn alloc_excess(&mut self, layout: Layout) -> Result { let usable_size = self.usable_size(&layout); self.alloc(layout).map(|p| Excess(p, usable_size.1)) @@ -778,9 +907,9 @@ pub unsafe trait Alloc { /// `layout` does not meet allocator's size or alignment /// constraints, just as in `realloc`. /// - /// Clients wishing to abort computation in response to an - /// reallocation error are encouraged to call the allocator's `oom` - /// method, rather than directly invoking `panic!` or similar. + /// Clients wishing to abort computation in response to a + /// reallocation error are encouraged to call the [`oom`] function, + /// rather than directly invoking `panic!` or similar. unsafe fn realloc_excess(&mut self, ptr: NonNull, layout: Layout, @@ -823,7 +952,7 @@ pub unsafe trait Alloc { /// could fit `layout`. /// /// Note that one cannot pass `CannotReallocInPlace` to the `oom` - /// method; clients are expected either to be able to recover from + /// function; clients are expected either to be able to recover from /// `grow_in_place` failures without aborting, or to fall back on /// another reallocation method before resorting to an abort. unsafe fn grow_in_place(&mut self, @@ -878,7 +1007,7 @@ pub unsafe trait Alloc { /// could fit `layout`. /// /// Note that one cannot pass `CannotReallocInPlace` to the `oom` - /// method; clients are expected either to be able to recover from + /// function; clients are expected either to be able to recover from /// `shrink_in_place` failures without aborting, or to fall back /// on another reallocation method before resorting to an abort. unsafe fn shrink_in_place(&mut self, @@ -926,8 +1055,8 @@ pub unsafe trait Alloc { /// will *not* yield undefined behavior. /// /// Clients wishing to abort computation in response to an - /// allocation error are encouraged to call the allocator's `oom` - /// method, rather than directly invoking `panic!` or similar. + /// allocation error are encouraged to call the [`oom`] function, + /// rather than directly invoking `panic!` or similar. fn alloc_one(&mut self) -> Result, AllocErr> where Self: Sized { @@ -993,8 +1122,8 @@ pub unsafe trait Alloc { /// Always returns `Err` on arithmetic overflow. /// /// Clients wishing to abort computation in response to an - /// allocation error are encouraged to call the allocator's `oom` - /// method, rather than directly invoking `panic!` or similar. + /// allocation error are encouraged to call the [`oom`] function, + /// rather than directly invoking `panic!` or similar. fn alloc_array(&mut self, n: usize) -> Result, AllocErr> where Self: Sized { @@ -1037,9 +1166,9 @@ pub unsafe trait Alloc { /// /// Always returns `Err` on arithmetic overflow. /// - /// Clients wishing to abort computation in response to an - /// reallocation error are encouraged to call the allocator's `oom` - /// method, rather than directly invoking `panic!` or similar. + /// Clients wishing to abort computation in response to a + /// reallocation error are encouraged to call the [`oom`] function, + /// rather than directly invoking `panic!` or similar. unsafe fn realloc_array(&mut self, ptr: NonNull, n_old: usize, diff --git a/src/libstd/alloc.rs b/src/libstd/alloc.rs index 9904634f1fa..9126155a7c9 100644 --- a/src/libstd/alloc.rs +++ b/src/libstd/alloc.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -//! dox +//! Memory allocation APIs #![unstable(issue = "32838", feature = "allocator_api")] -- cgit 1.4.1-3-g733a5 From 951bc28fd09f5fed793021c6be1645e034394491 Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Thu, 31 May 2018 18:36:51 +0200 Subject: Stablize the alloc module without changing stability of its contents. --- src/liballoc/alloc.rs | 11 ++++----- src/libcore/alloc.rs | 32 +++++++++++++++++++++----- src/libstd/alloc.rs | 19 ++++++++++----- src/test/compile-fail/lint-stability-fields.rs | 7 ++++++ 4 files changed, 51 insertions(+), 18 deletions(-) (limited to 'src/liballoc/alloc.rs') diff --git a/src/liballoc/alloc.rs b/src/liballoc/alloc.rs index c9430a29e4c..fc3d0151ec0 100644 --- a/src/liballoc/alloc.rs +++ b/src/liballoc/alloc.rs @@ -10,17 +10,13 @@ //! Memory allocation APIs -#![unstable(feature = "allocator_api", - reason = "the precise API and guarantees it provides may be tweaked \ - slightly, especially to possibly take into account the \ - types being stored to make room for a future \ - tracing garbage collector", - issue = "32838")] +#![stable(feature = "alloc_module", since = "1.28.0")] use core::intrinsics::{min_align_of_val, size_of_val}; use core::ptr::{NonNull, Unique}; use core::usize; +#[stable(feature = "alloc_module", since = "1.28.0")] #[doc(inline)] pub use core::alloc::*; @@ -44,6 +40,7 @@ extern "Rust" { /// This type implements the [`Alloc`] trait by forwarding calls /// to the allocator registered with the `#[global_allocator]` attribute /// if there is one, or the `std` crate’s default. +#[unstable(feature = "allocator_api", issue = "32838")] #[derive(Copy, Clone, Default, Debug)] pub struct Global; @@ -119,6 +116,7 @@ pub unsafe fn alloc_zeroed(layout: Layout) -> *mut u8 { __rust_alloc_zeroed(layout.size(), layout.align()) } +#[unstable(feature = "allocator_api", issue = "32838")] unsafe impl Alloc for Global { #[inline] unsafe fn alloc(&mut self, layout: Layout) -> Result, AllocErr> { @@ -188,6 +186,7 @@ pub(crate) unsafe fn box_free(ptr: Unique) { /// and abort the process. /// It can be replaced with [`std::alloc::set_oom_hook`] /// and [`std::alloc::take_oom_hook`]. +#[unstable(feature = "allocator_api", issue = "32838")] #[rustc_allocator_nounwind] pub fn oom(layout: Layout) -> ! { #[allow(improper_ctypes)] diff --git a/src/libcore/alloc.rs b/src/libcore/alloc.rs index 8fcd8555cdc..ae9f77d35cf 100644 --- a/src/libcore/alloc.rs +++ b/src/libcore/alloc.rs @@ -10,12 +10,7 @@ //! Memory allocation APIs -#![unstable(feature = "allocator_api", - reason = "the precise API and guarantees it provides may be tweaked \ - slightly, especially to possibly take into account the \ - types being stored to make room for a future \ - tracing garbage collector", - issue = "32838")] +#![stable(feature = "alloc_module", since = "1.28.0")] use cmp; use fmt; @@ -24,11 +19,13 @@ use usize; use ptr::{self, NonNull}; use num::NonZeroUsize; +#[unstable(feature = "allocator_api", issue = "32838")] #[cfg(stage0)] pub type Opaque = u8; /// Represents the combination of a starting address and /// a total capacity of the returned block. +#[unstable(feature = "allocator_api", issue = "32838")] #[derive(Debug)] pub struct Excess(pub NonNull, pub usize); @@ -49,6 +46,7 @@ fn size_align() -> (usize, usize) { /// requests have positive size. A caller to the `Alloc::alloc` /// method must either ensure that conditions like this are met, or /// use specific allocators with looser requirements.) +#[unstable(feature = "allocator_api", issue = "32838")] #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub struct Layout { // size of the requested block of memory, measured in bytes. @@ -74,6 +72,7 @@ impl Layout { /// * `size`, when rounded up to the nearest multiple of `align`, /// must not overflow (i.e. the rounded value must be less than /// `usize::MAX`). + #[unstable(feature = "allocator_api", issue = "32838")] #[inline] pub fn from_size_align(size: usize, align: usize) -> Result { if !align.is_power_of_two() { @@ -109,20 +108,24 @@ impl Layout { /// /// This function is unsafe as it does not verify the preconditions from /// [`Layout::from_size_align`](#method.from_size_align). + #[unstable(feature = "allocator_api", issue = "32838")] #[inline] pub unsafe fn from_size_align_unchecked(size: usize, align: usize) -> Self { Layout { size_: size, align_: NonZeroUsize::new_unchecked(align) } } /// The minimum size in bytes for a memory block of this layout. + #[unstable(feature = "allocator_api", issue = "32838")] #[inline] pub fn size(&self) -> usize { self.size_ } /// The minimum byte alignment for a memory block of this layout. + #[unstable(feature = "allocator_api", issue = "32838")] #[inline] pub fn align(&self) -> usize { self.align_.get() } /// Constructs a `Layout` suitable for holding a value of type `T`. + #[unstable(feature = "allocator_api", issue = "32838")] #[inline] pub fn new() -> Self { let (size, align) = size_align::(); @@ -139,6 +142,7 @@ impl Layout { /// Produces layout describing a record that could be used to /// allocate backing structure for `T` (which could be a trait /// or other unsized type like a slice). + #[unstable(feature = "allocator_api", issue = "32838")] #[inline] pub fn for_value(t: &T) -> Self { let (size, align) = (mem::size_of_val(t), mem::align_of_val(t)); @@ -166,6 +170,7 @@ impl Layout { /// Panics if the combination of `self.size()` and the given `align` /// violates the conditions listed in /// [`Layout::from_size_align`](#method.from_size_align). + #[unstable(feature = "allocator_api", issue = "32838")] #[inline] pub fn align_to(&self, align: usize) -> Self { Layout::from_size_align(self.size(), cmp::max(self.align(), align)).unwrap() @@ -187,6 +192,7 @@ impl Layout { /// to be less than or equal to the alignment of the starting /// address for the whole allocated block of memory. One way to /// satisfy this constraint is to ensure `align <= self.align()`. + #[unstable(feature = "allocator_api", issue = "32838")] #[inline] pub fn padding_needed_for(&self, align: usize) -> usize { let len = self.size(); @@ -223,6 +229,7 @@ impl Layout { /// of each element in the array. /// /// On arithmetic overflow, returns `LayoutErr`. + #[unstable(feature = "allocator_api", issue = "32838")] #[inline] pub fn repeat(&self, n: usize) -> Result<(Self, usize), LayoutErr> { let padded_size = self.size().checked_add(self.padding_needed_for(self.align())) @@ -248,6 +255,7 @@ impl Layout { /// (assuming that the record itself starts at offset 0). /// /// On arithmetic overflow, returns `LayoutErr`. + #[unstable(feature = "allocator_api", issue = "32838")] #[inline] pub fn extend(&self, next: Self) -> Result<(Self, usize), LayoutErr> { let new_align = cmp::max(self.align(), next.align()); @@ -274,6 +282,7 @@ impl Layout { /// aligned. /// /// On arithmetic overflow, returns `LayoutErr`. + #[unstable(feature = "allocator_api", issue = "32838")] #[inline] pub fn repeat_packed(&self, n: usize) -> Result { let size = self.size().checked_mul(n).ok_or(LayoutErr { private: () })?; @@ -295,6 +304,7 @@ impl Layout { /// `extend`.) /// /// On arithmetic overflow, returns `LayoutErr`. + #[unstable(feature = "allocator_api", issue = "32838")] #[inline] pub fn extend_packed(&self, next: Self) -> Result<(Self, usize), LayoutErr> { let new_size = self.size().checked_add(next.size()) @@ -306,6 +316,7 @@ impl Layout { /// Creates a layout describing the record for a `[T; n]`. /// /// On arithmetic overflow, returns `LayoutErr`. + #[unstable(feature = "allocator_api", issue = "32838")] #[inline] pub fn array(n: usize) -> Result { Layout::new::() @@ -320,12 +331,14 @@ impl Layout { /// The parameters given to `Layout::from_size_align` /// or some other `Layout` constructor /// do not satisfy its documented constraints. +#[unstable(feature = "allocator_api", issue = "32838")] #[derive(Clone, PartialEq, Eq, Debug)] pub struct LayoutErr { private: () } // (we need this for downstream impl of trait Error) +#[unstable(feature = "allocator_api", issue = "32838")] impl fmt::Display for LayoutErr { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.write_str("invalid parameters to Layout::from_size_align") @@ -336,10 +349,12 @@ impl fmt::Display for LayoutErr { /// that may be due to resource exhaustion or to /// something wrong when combining the given input arguments with this /// allocator. +#[unstable(feature = "allocator_api", issue = "32838")] #[derive(Clone, PartialEq, Eq, Debug)] pub struct AllocErr; // (we need this for downstream impl of trait Error) +#[unstable(feature = "allocator_api", issue = "32838")] impl fmt::Display for AllocErr { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.write_str("memory allocation failed") @@ -350,9 +365,11 @@ impl fmt::Display for AllocErr { /// `shrink_in_place` were unable to reuse the given memory block for /// a requested layout. // FIXME: should this be in libcore or liballoc? +#[unstable(feature = "allocator_api", issue = "32838")] #[derive(Clone, PartialEq, Eq, Debug)] pub struct CannotReallocInPlace; +#[unstable(feature = "allocator_api", issue = "32838")] impl CannotReallocInPlace { pub fn description(&self) -> &str { "cannot reallocate allocator's memory in place" @@ -360,6 +377,7 @@ impl CannotReallocInPlace { } // (we need this for downstream impl of trait Error) +#[unstable(feature = "allocator_api", issue = "32838")] impl fmt::Display for CannotReallocInPlace { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}", self.description()) @@ -449,6 +467,7 @@ impl From for CollectionAllocErr { /// * `Layout` queries and calculations in general must be correct. Callers of /// this trait are allowed to rely on the contracts defined on each method, /// and implementors must ensure such contracts remain true. +#[unstable(feature = "allocator_api", issue = "32838")] pub unsafe trait GlobalAlloc { /// Allocate memory as described by the given `layout`. /// @@ -664,6 +683,7 @@ pub unsafe trait GlobalAlloc { /// /// Note that this list may get tweaked over time as clarifications are made in /// the future. +#[unstable(feature = "allocator_api", issue = "32838")] pub unsafe trait Alloc { // (Note: some existing allocators have unspecified but well-defined diff --git a/src/libstd/alloc.rs b/src/libstd/alloc.rs index 9126155a7c9..1a4869f87c9 100644 --- a/src/libstd/alloc.rs +++ b/src/libstd/alloc.rs @@ -10,17 +10,20 @@ //! Memory allocation APIs -#![unstable(issue = "32838", feature = "allocator_api")] - -#[doc(inline)] pub use alloc_crate::alloc::{Global, Layout, oom}; -#[doc(inline)] pub use alloc_crate::alloc::{alloc, alloc_zeroed, dealloc, realloc}; -#[doc(inline)] pub use alloc_system::System; -#[doc(inline)] pub use core::alloc::*; +#![stable(feature = "alloc_module", since = "1.28.0")] use core::sync::atomic::{AtomicPtr, Ordering}; use core::{mem, ptr}; use sys_common::util::dumb_print; +#[stable(feature = "alloc_module", since = "1.28.0")] +#[doc(inline)] +pub use alloc_crate::alloc::*; + +#[unstable(feature = "allocator_api", issue = "32838")] +#[doc(inline)] +pub use alloc_system::System; + static HOOK: AtomicPtr<()> = AtomicPtr::new(ptr::null_mut()); /// Registers a custom OOM hook, replacing any that was previously registered. @@ -34,6 +37,7 @@ static HOOK: AtomicPtr<()> = AtomicPtr::new(ptr::null_mut()); /// about the allocation that failed. /// /// The OOM hook is a global resource. +#[unstable(feature = "allocator_api", issue = "32838")] pub fn set_oom_hook(hook: fn(Layout)) { HOOK.store(hook as *mut (), Ordering::SeqCst); } @@ -43,6 +47,7 @@ pub fn set_oom_hook(hook: fn(Layout)) { /// *See also the function [`set_oom_hook`].* /// /// If no custom hook is registered, the default hook will be returned. +#[unstable(feature = "allocator_api", issue = "32838")] pub fn take_oom_hook() -> fn(Layout) { let hook = HOOK.swap(ptr::null_mut(), Ordering::SeqCst); if hook.is_null() { @@ -59,6 +64,7 @@ fn default_oom_hook(layout: Layout) { #[cfg(not(test))] #[doc(hidden)] #[lang = "oom"] +#[unstable(feature = "allocator_api", issue = "32838")] pub extern fn rust_oom(layout: Layout) -> ! { let hook = HOOK.load(Ordering::SeqCst); let hook: fn(Layout) = if hook.is_null() { @@ -73,6 +79,7 @@ pub extern fn rust_oom(layout: Layout) -> ! { #[cfg(not(test))] #[doc(hidden)] #[allow(unused_attributes)] +#[unstable(feature = "allocator_api", issue = "32838")] pub mod __default_lib_allocator { use super::{System, Layout, GlobalAlloc}; // for symbol names src/librustc/middle/allocator.rs diff --git a/src/test/compile-fail/lint-stability-fields.rs b/src/test/compile-fail/lint-stability-fields.rs index 1b605bdb893..b1b1a9a1fbf 100644 --- a/src/test/compile-fail/lint-stability-fields.rs +++ b/src/test/compile-fail/lint-stability-fields.rs @@ -18,6 +18,11 @@ mod cross_crate { extern crate lint_stability_fields; + mod reexport { + #[stable(feature = "rust1", since = "1.0.0")] + pub use super::lint_stability_fields::*; + } + use self::lint_stability_fields::*; pub fn foo() { @@ -73,6 +78,8 @@ mod cross_crate { // the patterns are all fine: { .. } = x; + // Unstable items are still unstable even when used through a stable "pub use". + let x = reexport::Unstable2(1, 2, 3); //~ ERROR use of unstable let x = Unstable2(1, 2, 3); //~ ERROR use of unstable -- cgit 1.4.1-3-g733a5 From 45d6d207b00cb1353d0f66d4319f2bdcf1fbc892 Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Thu, 31 May 2018 19:13:37 +0200 Subject: Stabilize alloc free functions for the global allocators. --- src/liballoc/alloc.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'src/liballoc/alloc.rs') diff --git a/src/liballoc/alloc.rs b/src/liballoc/alloc.rs index fc3d0151ec0..839b1bf99be 100644 --- a/src/liballoc/alloc.rs +++ b/src/liballoc/alloc.rs @@ -56,7 +56,7 @@ pub struct Global; /// # Safety /// /// See [`GlobalAlloc::alloc`]. -#[unstable(feature = "allocator_api", issue = "32838")] +#[stable(feature = "global_alloc", since = "1.28.0")] #[inline] pub unsafe fn alloc(layout: Layout) -> *mut u8 { __rust_alloc(layout.size(), layout.align()) @@ -74,7 +74,7 @@ pub unsafe fn alloc(layout: Layout) -> *mut u8 { /// # Safety /// /// See [`GlobalAlloc::dealloc`]. -#[unstable(feature = "allocator_api", issue = "32838")] +#[stable(feature = "global_alloc", since = "1.28.0")] #[inline] pub unsafe fn dealloc(ptr: *mut u8, layout: Layout) { __rust_dealloc(ptr, layout.size(), layout.align()) @@ -92,7 +92,7 @@ pub unsafe fn dealloc(ptr: *mut u8, layout: Layout) { /// # Safety /// /// See [`GlobalAlloc::realloc`]. -#[unstable(feature = "allocator_api", issue = "32838")] +#[stable(feature = "global_alloc", since = "1.28.0")] #[inline] pub unsafe fn realloc(ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 { __rust_realloc(ptr, layout.size(), layout.align(), new_size) @@ -110,7 +110,7 @@ pub unsafe fn realloc(ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 /// # Safety /// /// See [`GlobalAlloc::alloc_zeroed`]. -#[unstable(feature = "allocator_api", issue = "32838")] +#[stable(feature = "global_alloc", since = "1.28.0")] #[inline] pub unsafe fn alloc_zeroed(layout: Layout) -> *mut u8 { __rust_alloc_zeroed(layout.size(), layout.align()) -- cgit 1.4.1-3-g733a5 From 125b259b3567f6370d5c242eb102ca0957ffe017 Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Thu, 31 May 2018 19:13:57 +0200 Subject: Stabilize alloc::oom (but not set_oom_hook or take_oom_hook) --- src/liballoc/alloc.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/liballoc/alloc.rs') diff --git a/src/liballoc/alloc.rs b/src/liballoc/alloc.rs index 839b1bf99be..fef668d2365 100644 --- a/src/liballoc/alloc.rs +++ b/src/liballoc/alloc.rs @@ -186,7 +186,7 @@ pub(crate) unsafe fn box_free(ptr: Unique) { /// and abort the process. /// It can be replaced with [`std::alloc::set_oom_hook`] /// and [`std::alloc::take_oom_hook`]. -#[unstable(feature = "allocator_api", issue = "32838")] +#[stable(feature = "global_alloc", since = "1.28.0")] #[rustc_allocator_nounwind] pub fn oom(layout: Layout) -> ! { #[allow(improper_ctypes)] -- cgit 1.4.1-3-g733a5 From 7f0d54d98842c786ab7a140c17c3ea32aea6aead Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Fri, 1 Jun 2018 09:18:25 +0200 Subject: More alloc docs tweaks --- src/liballoc/alloc.rs | 6 ++++-- src/libcore/alloc.rs | 24 +++++++++++++++++++++++- 2 files changed, 27 insertions(+), 3 deletions(-) (limited to 'src/liballoc/alloc.rs') diff --git a/src/liballoc/alloc.rs b/src/liballoc/alloc.rs index fef668d2365..04c8063ffeb 100644 --- a/src/liballoc/alloc.rs +++ b/src/liballoc/alloc.rs @@ -184,8 +184,10 @@ pub(crate) unsafe fn box_free(ptr: Unique) { /// /// The default behavior of this function is to print a message to standard error /// and abort the process. -/// It can be replaced with [`std::alloc::set_oom_hook`] -/// and [`std::alloc::take_oom_hook`]. +/// It can be replaced with [`set_oom_hook`] and [`take_oom_hook`]. +/// +/// [`set_oom_hook`]: ../../std/alloc/fn.set_oom_hook.html +/// [`take_oom_hook`]: ../../std/alloc/fn.take_oom_hook.html #[stable(feature = "global_alloc", since = "1.28.0")] #[rustc_allocator_nounwind] pub fn oom(layout: Layout) -> ! { diff --git a/src/libcore/alloc.rs b/src/libcore/alloc.rs index b0ac43e50f5..353688d1b85 100644 --- a/src/libcore/alloc.rs +++ b/src/libcore/alloc.rs @@ -494,6 +494,8 @@ pub unsafe trait GlobalAlloc { /// Clients wishing to abort computation in response to an /// allocation error are encouraged to call the [`oom`] function, /// rather than directly invoking `panic!` or similar. + /// + /// [`oom`]: ../../alloc/alloc/fn.oom.html #[stable(feature = "global_alloc", since = "1.28.0")] unsafe fn alloc(&self, layout: Layout) -> *mut u8; @@ -529,6 +531,8 @@ pub unsafe trait GlobalAlloc { /// Clients wishing to abort computation in response to an /// allocation error are encouraged to call the [`oom`] function, /// rather than directly invoking `panic!` or similar. + /// + /// [`oom`]: ../../alloc/alloc/fn.oom.html #[stable(feature = "global_alloc", since = "1.28.0")] unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 { let size = layout.size(); @@ -587,6 +591,8 @@ pub unsafe trait GlobalAlloc { /// Clients wishing to abort computation in response to a /// reallocation error are encouraged to call the [`oom`] function, /// rather than directly invoking `panic!` or similar. + /// + /// [`oom`]: ../../alloc/alloc/fn.oom.html #[stable(feature = "global_alloc", since = "1.28.0")] unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 { let new_layout = Layout::from_size_align_unchecked(new_size, layout.align()); @@ -727,8 +733,10 @@ pub unsafe trait Alloc { /// library that aborts on memory exhaustion.) /// /// Clients wishing to abort computation in response to an - /// allocation error are encouraged to call the `oom` function, + /// allocation error are encouraged to call the [`oom`] function, /// rather than directly invoking `panic!` or similar. + /// + /// [`oom`]: ../../alloc/alloc/fn.oom.html unsafe fn alloc(&mut self, layout: Layout) -> Result, AllocErr>; /// Deallocate the memory referenced by `ptr`. @@ -837,6 +845,8 @@ pub unsafe trait Alloc { /// Clients wishing to abort computation in response to a /// reallocation error are encouraged to call the [`oom`] function, /// rather than directly invoking `panic!` or similar. + /// + /// [`oom`]: ../../alloc/alloc/fn.oom.html unsafe fn realloc(&mut self, ptr: NonNull, layout: Layout, @@ -881,6 +891,8 @@ pub unsafe trait Alloc { /// Clients wishing to abort computation in response to an /// allocation error are encouraged to call the [`oom`] function, /// rather than directly invoking `panic!` or similar. + /// + /// [`oom`]: ../../alloc/alloc/fn.oom.html unsafe fn alloc_zeroed(&mut self, layout: Layout) -> Result, AllocErr> { let size = layout.size(); let p = self.alloc(layout); @@ -907,6 +919,8 @@ pub unsafe trait Alloc { /// Clients wishing to abort computation in response to an /// allocation error are encouraged to call the [`oom`] function, /// rather than directly invoking `panic!` or similar. + /// + /// [`oom`]: ../../alloc/alloc/fn.oom.html unsafe fn alloc_excess(&mut self, layout: Layout) -> Result { let usable_size = self.usable_size(&layout); self.alloc(layout).map(|p| Excess(p, usable_size.1)) @@ -929,6 +943,8 @@ pub unsafe trait Alloc { /// Clients wishing to abort computation in response to a /// reallocation error are encouraged to call the [`oom`] function, /// rather than directly invoking `panic!` or similar. + /// + /// [`oom`]: ../../alloc/alloc/fn.oom.html unsafe fn realloc_excess(&mut self, ptr: NonNull, layout: Layout, @@ -1076,6 +1092,8 @@ pub unsafe trait Alloc { /// Clients wishing to abort computation in response to an /// allocation error are encouraged to call the [`oom`] function, /// rather than directly invoking `panic!` or similar. + /// + /// [`oom`]: ../../alloc/alloc/fn.oom.html fn alloc_one(&mut self) -> Result, AllocErr> where Self: Sized { @@ -1143,6 +1161,8 @@ pub unsafe trait Alloc { /// Clients wishing to abort computation in response to an /// allocation error are encouraged to call the [`oom`] function, /// rather than directly invoking `panic!` or similar. + /// + /// [`oom`]: ../../alloc/alloc/fn.oom.html fn alloc_array(&mut self, n: usize) -> Result, AllocErr> where Self: Sized { @@ -1188,6 +1208,8 @@ pub unsafe trait Alloc { /// Clients wishing to abort computation in response to a /// reallocation error are encouraged to call the [`oom`] function, /// rather than directly invoking `panic!` or similar. + /// + /// [`oom`]: ../../alloc/alloc/fn.oom.html unsafe fn realloc_array(&mut self, ptr: NonNull, n_old: usize, -- cgit 1.4.1-3-g733a5 From 2b789bd0570983e82533f9ed30c80312ac334694 Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Thu, 14 Jun 2018 00:32:30 +0200 Subject: Rename OOM to allocation error The acronym is not descriptive unless one has seen it before. * Rename the `oom` function to `handle_alloc_error`. It was **stabilized in 1.28**, so if we do this at all we need to land it this cycle. * Rename `set_oom_hook` to `set_alloc_error_hook` * Rename `take_oom_hook` to `take_alloc_error_hook` Bikeshed: `alloc` v.s. `allocator`, `error` v.s. `failure` --- src/liballoc/alloc.rs | 14 ++++----- src/liballoc/arc.rs | 4 +-- src/liballoc/raw_vec.rs | 16 +++++++---- src/liballoc/rc.rs | 4 +-- src/libcore/alloc.rs | 48 +++++++++++++++---------------- src/libstd/alloc.rs | 28 +++++++++--------- src/libstd/collections/hash/table.rs | 4 +-- src/test/run-pass/allocator-alloc-one.rs | 6 ++-- src/test/run-pass/realloc-16687.rs | 8 ++++-- src/test/run-pass/regions-mock-codegen.rs | 4 +-- 10 files changed, 72 insertions(+), 64 deletions(-) (limited to 'src/liballoc/alloc.rs') diff --git a/src/liballoc/alloc.rs b/src/liballoc/alloc.rs index 04c8063ffeb..84bd275df34 100644 --- a/src/liballoc/alloc.rs +++ b/src/liballoc/alloc.rs @@ -158,7 +158,7 @@ unsafe fn exchange_malloc(size: usize, align: usize) -> *mut u8 { if !ptr.is_null() { ptr } else { - oom(layout) + handle_alloc_error(layout) } } } @@ -184,13 +184,13 @@ pub(crate) unsafe fn box_free(ptr: Unique) { /// /// The default behavior of this function is to print a message to standard error /// and abort the process. -/// It can be replaced with [`set_oom_hook`] and [`take_oom_hook`]. +/// It can be replaced with [`set_alloc_error_hook`] and [`take_alloc_error_hook`]. /// -/// [`set_oom_hook`]: ../../std/alloc/fn.set_oom_hook.html -/// [`take_oom_hook`]: ../../std/alloc/fn.take_oom_hook.html +/// [`set_alloc_error_hook`]: ../../std/alloc/fn.set_alloc_error_hook.html +/// [`take_alloc_error_hook`]: ../../std/alloc/fn.take_alloc_error_hook.html #[stable(feature = "global_alloc", since = "1.28.0")] #[rustc_allocator_nounwind] -pub fn oom(layout: Layout) -> ! { +pub fn handle_alloc_error(layout: Layout) -> ! { #[allow(improper_ctypes)] extern "Rust" { #[lang = "oom"] @@ -204,14 +204,14 @@ mod tests { extern crate test; use self::test::Bencher; use boxed::Box; - use alloc::{Global, Alloc, Layout, oom}; + use alloc::{Global, Alloc, Layout, handle_alloc_error}; #[test] fn allocate_zeroed() { unsafe { let layout = Layout::from_size_align(1024, 1).unwrap(); let ptr = Global.alloc_zeroed(layout.clone()) - .unwrap_or_else(|_| oom(layout)); + .unwrap_or_else(|_| handle_alloc_error(layout)); let mut i = ptr.cast::().as_ptr(); let end = i.offset(layout.size() as isize); diff --git a/src/liballoc/arc.rs b/src/liballoc/arc.rs index e3369f0a5b5..0fbd1408f64 100644 --- a/src/liballoc/arc.rs +++ b/src/liballoc/arc.rs @@ -32,7 +32,7 @@ use core::hash::{Hash, Hasher}; use core::{isize, usize}; use core::convert::From; -use alloc::{Global, Alloc, Layout, box_free, oom}; +use alloc::{Global, Alloc, Layout, box_free, handle_alloc_error}; use boxed::Box; use string::String; use vec::Vec; @@ -554,7 +554,7 @@ impl Arc { let layout = Layout::for_value(&*fake_ptr); let mem = Global.alloc(layout) - .unwrap_or_else(|_| oom(layout)); + .unwrap_or_else(|_| handle_alloc_error(layout)); // Initialize the real ArcInner let inner = set_data_ptr(ptr as *mut T, mem.as_ptr() as *mut u8) as *mut ArcInner; diff --git a/src/liballoc/raw_vec.rs b/src/liballoc/raw_vec.rs index d1f140e96a3..2369ce648fd 100644 --- a/src/liballoc/raw_vec.rs +++ b/src/liballoc/raw_vec.rs @@ -14,7 +14,7 @@ use core::ops::Drop; use core::ptr::{self, NonNull, Unique}; use core::slice; -use alloc::{Alloc, Layout, Global, oom}; +use alloc::{Alloc, Layout, Global, handle_alloc_error}; use alloc::CollectionAllocErr; use alloc::CollectionAllocErr::*; use boxed::Box; @@ -104,7 +104,7 @@ impl RawVec { }; match result { Ok(ptr) => ptr.cast(), - Err(_) => oom(layout), + Err(_) => handle_alloc_error(layout), } }; @@ -319,7 +319,9 @@ impl RawVec { new_size); match ptr_res { Ok(ptr) => (new_cap, ptr.cast().into()), - Err(_) => oom(Layout::from_size_align_unchecked(new_size, cur.align())), + Err(_) => handle_alloc_error( + Layout::from_size_align_unchecked(new_size, cur.align()) + ), } } None => { @@ -328,7 +330,7 @@ impl RawVec { let new_cap = if elem_size > (!0) / 8 { 1 } else { 4 }; match self.a.alloc_array::(new_cap) { Ok(ptr) => (new_cap, ptr.into()), - Err(_) => oom(Layout::array::(new_cap).unwrap()), + Err(_) => handle_alloc_error(Layout::array::(new_cap).unwrap()), } } }; @@ -611,7 +613,9 @@ impl RawVec { old_layout, new_size) { Ok(p) => self.ptr = p.cast().into(), - Err(_) => oom(Layout::from_size_align_unchecked(new_size, align)), + Err(_) => handle_alloc_error( + Layout::from_size_align_unchecked(new_size, align) + ), } } self.cap = amount; @@ -673,7 +677,7 @@ impl RawVec { }; match (&res, fallibility) { - (Err(AllocErr), Infallible) => oom(new_layout), + (Err(AllocErr), Infallible) => handle_alloc_error(new_layout), _ => {} } diff --git a/src/liballoc/rc.rs b/src/liballoc/rc.rs index 84a6ecf7103..32d624e8fbc 100644 --- a/src/liballoc/rc.rs +++ b/src/liballoc/rc.rs @@ -259,7 +259,7 @@ use core::ops::CoerceUnsized; use core::ptr::{self, NonNull}; use core::convert::From; -use alloc::{Global, Alloc, Layout, box_free, oom}; +use alloc::{Global, Alloc, Layout, box_free, handle_alloc_error}; use string::String; use vec::Vec; @@ -662,7 +662,7 @@ impl Rc { let layout = Layout::for_value(&*fake_ptr); let mem = Global.alloc(layout) - .unwrap_or_else(|_| oom(layout)); + .unwrap_or_else(|_| handle_alloc_error(layout)); // Initialize the real RcBox let inner = set_data_ptr(ptr as *mut T, mem.as_ptr() as *mut u8) as *mut RcBox; diff --git a/src/libcore/alloc.rs b/src/libcore/alloc.rs index 353688d1b85..0c074582281 100644 --- a/src/libcore/alloc.rs +++ b/src/libcore/alloc.rs @@ -492,10 +492,10 @@ pub unsafe trait GlobalAlloc { /// library that aborts on memory exhaustion.) /// /// Clients wishing to abort computation in response to an - /// allocation error are encouraged to call the [`oom`] function, + /// allocation error are encouraged to call the [`handle_alloc_error`] function, /// rather than directly invoking `panic!` or similar. /// - /// [`oom`]: ../../alloc/alloc/fn.oom.html + /// [`handle_alloc_error`]: ../../alloc/alloc/fn.handle_alloc_error.html #[stable(feature = "global_alloc", since = "1.28.0")] unsafe fn alloc(&self, layout: Layout) -> *mut u8; @@ -529,10 +529,10 @@ pub unsafe trait GlobalAlloc { /// just as in `alloc`. /// /// Clients wishing to abort computation in response to an - /// allocation error are encouraged to call the [`oom`] function, + /// allocation error are encouraged to call the [`handle_alloc_error`] function, /// rather than directly invoking `panic!` or similar. /// - /// [`oom`]: ../../alloc/alloc/fn.oom.html + /// [`handle_alloc_error`]: ../../alloc/alloc/fn.handle_alloc_error.html #[stable(feature = "global_alloc", since = "1.28.0")] unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 { let size = layout.size(); @@ -589,10 +589,10 @@ pub unsafe trait GlobalAlloc { /// library that aborts on memory exhaustion.) /// /// Clients wishing to abort computation in response to a - /// reallocation error are encouraged to call the [`oom`] function, + /// reallocation error are encouraged to call the [`handle_alloc_error`] function, /// rather than directly invoking `panic!` or similar. /// - /// [`oom`]: ../../alloc/alloc/fn.oom.html + /// [`handle_alloc_error`]: ../../alloc/alloc/fn.handle_alloc_error.html #[stable(feature = "global_alloc", since = "1.28.0")] unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 { let new_layout = Layout::from_size_align_unchecked(new_size, layout.align()); @@ -733,10 +733,10 @@ pub unsafe trait Alloc { /// library that aborts on memory exhaustion.) /// /// Clients wishing to abort computation in response to an - /// allocation error are encouraged to call the [`oom`] function, + /// allocation error are encouraged to call the [`handle_alloc_error`] function, /// rather than directly invoking `panic!` or similar. /// - /// [`oom`]: ../../alloc/alloc/fn.oom.html + /// [`handle_alloc_error`]: ../../alloc/alloc/fn.handle_alloc_error.html unsafe fn alloc(&mut self, layout: Layout) -> Result, AllocErr>; /// Deallocate the memory referenced by `ptr`. @@ -843,10 +843,10 @@ pub unsafe trait Alloc { /// library that aborts on memory exhaustion.) /// /// Clients wishing to abort computation in response to a - /// reallocation error are encouraged to call the [`oom`] function, + /// reallocation error are encouraged to call the [`handle_alloc_error`] function, /// rather than directly invoking `panic!` or similar. /// - /// [`oom`]: ../../alloc/alloc/fn.oom.html + /// [`handle_alloc_error`]: ../../alloc/alloc/fn.handle_alloc_error.html unsafe fn realloc(&mut self, ptr: NonNull, layout: Layout, @@ -889,10 +889,10 @@ pub unsafe trait Alloc { /// constraints, just as in `alloc`. /// /// Clients wishing to abort computation in response to an - /// allocation error are encouraged to call the [`oom`] function, + /// allocation error are encouraged to call the [`handle_alloc_error`] function, /// rather than directly invoking `panic!` or similar. /// - /// [`oom`]: ../../alloc/alloc/fn.oom.html + /// [`handle_alloc_error`]: ../../alloc/alloc/fn.handle_alloc_error.html unsafe fn alloc_zeroed(&mut self, layout: Layout) -> Result, AllocErr> { let size = layout.size(); let p = self.alloc(layout); @@ -917,10 +917,10 @@ pub unsafe trait Alloc { /// constraints, just as in `alloc`. /// /// Clients wishing to abort computation in response to an - /// allocation error are encouraged to call the [`oom`] function, + /// allocation error are encouraged to call the [`handle_alloc_error`] function, /// rather than directly invoking `panic!` or similar. /// - /// [`oom`]: ../../alloc/alloc/fn.oom.html + /// [`handle_alloc_error`]: ../../alloc/alloc/fn.handle_alloc_error.html unsafe fn alloc_excess(&mut self, layout: Layout) -> Result { let usable_size = self.usable_size(&layout); self.alloc(layout).map(|p| Excess(p, usable_size.1)) @@ -941,10 +941,10 @@ pub unsafe trait Alloc { /// constraints, just as in `realloc`. /// /// Clients wishing to abort computation in response to a - /// reallocation error are encouraged to call the [`oom`] function, + /// reallocation error are encouraged to call the [`handle_alloc_error`] function, /// rather than directly invoking `panic!` or similar. /// - /// [`oom`]: ../../alloc/alloc/fn.oom.html + /// [`handle_alloc_error`]: ../../alloc/alloc/fn.handle_alloc_error.html unsafe fn realloc_excess(&mut self, ptr: NonNull, layout: Layout, @@ -986,7 +986,7 @@ pub unsafe trait Alloc { /// unable to assert that the memory block referenced by `ptr` /// could fit `layout`. /// - /// Note that one cannot pass `CannotReallocInPlace` to the `oom` + /// Note that one cannot pass `CannotReallocInPlace` to the `handle_alloc_error` /// function; clients are expected either to be able to recover from /// `grow_in_place` failures without aborting, or to fall back on /// another reallocation method before resorting to an abort. @@ -1041,7 +1041,7 @@ pub unsafe trait Alloc { /// unable to assert that the memory block referenced by `ptr` /// could fit `layout`. /// - /// Note that one cannot pass `CannotReallocInPlace` to the `oom` + /// Note that one cannot pass `CannotReallocInPlace` to the `handle_alloc_error` /// function; clients are expected either to be able to recover from /// `shrink_in_place` failures without aborting, or to fall back /// on another reallocation method before resorting to an abort. @@ -1090,10 +1090,10 @@ pub unsafe trait Alloc { /// will *not* yield undefined behavior. /// /// Clients wishing to abort computation in response to an - /// allocation error are encouraged to call the [`oom`] function, + /// allocation error are encouraged to call the [`handle_alloc_error`] function, /// rather than directly invoking `panic!` or similar. /// - /// [`oom`]: ../../alloc/alloc/fn.oom.html + /// [`handle_alloc_error`]: ../../alloc/alloc/fn.handle_alloc_error.html fn alloc_one(&mut self) -> Result, AllocErr> where Self: Sized { @@ -1159,10 +1159,10 @@ pub unsafe trait Alloc { /// Always returns `Err` on arithmetic overflow. /// /// Clients wishing to abort computation in response to an - /// allocation error are encouraged to call the [`oom`] function, + /// allocation error are encouraged to call the [`handle_alloc_error`] function, /// rather than directly invoking `panic!` or similar. /// - /// [`oom`]: ../../alloc/alloc/fn.oom.html + /// [`handle_alloc_error`]: ../../alloc/alloc/fn.handle_alloc_error.html fn alloc_array(&mut self, n: usize) -> Result, AllocErr> where Self: Sized { @@ -1206,10 +1206,10 @@ pub unsafe trait Alloc { /// Always returns `Err` on arithmetic overflow. /// /// Clients wishing to abort computation in response to a - /// reallocation error are encouraged to call the [`oom`] function, + /// reallocation error are encouraged to call the [`handle_alloc_error`] function, /// rather than directly invoking `panic!` or similar. /// - /// [`oom`]: ../../alloc/alloc/fn.oom.html + /// [`handle_alloc_error`]: ../../alloc/alloc/fn.handle_alloc_error.html unsafe fn realloc_array(&mut self, ptr: NonNull, n_old: usize, diff --git a/src/libstd/alloc.rs b/src/libstd/alloc.rs index ae74a71dd06..f28e91e19b7 100644 --- a/src/libstd/alloc.rs +++ b/src/libstd/alloc.rs @@ -88,38 +88,38 @@ pub use alloc_system::System; static HOOK: AtomicPtr<()> = AtomicPtr::new(ptr::null_mut()); -/// Registers a custom OOM hook, replacing any that was previously registered. +/// Registers a custom allocation error hook, replacing any that was previously registered. /// -/// The OOM hook is invoked when an infallible memory allocation fails, before +/// The allocation error hook is invoked when an infallible memory allocation fails, before /// the runtime aborts. The default hook prints a message to standard error, -/// but this behavior can be customized with the [`set_oom_hook`] and -/// [`take_oom_hook`] functions. +/// but this behavior can be customized with the [`set_alloc_error_hook`] and +/// [`take_alloc_error_hook`] functions. /// /// The hook is provided with a `Layout` struct which contains information /// about the allocation that failed. /// -/// The OOM hook is a global resource. -#[unstable(feature = "oom_hook", issue = "51245")] -pub fn set_oom_hook(hook: fn(Layout)) { +/// The allocation error hook is a global resource. +#[unstable(feature = "alloc_error_hook", issue = "51245")] +pub fn set_alloc_error_hook(hook: fn(Layout)) { HOOK.store(hook as *mut (), Ordering::SeqCst); } -/// Unregisters the current OOM hook, returning it. +/// Unregisters the current allocation error hook, returning it. /// -/// *See also the function [`set_oom_hook`].* +/// *See also the function [`set_alloc_error_hook`].* /// /// If no custom hook is registered, the default hook will be returned. -#[unstable(feature = "oom_hook", issue = "51245")] -pub fn take_oom_hook() -> fn(Layout) { +#[unstable(feature = "alloc_error_hook", issue = "51245")] +pub fn take_alloc_error_hook() -> fn(Layout) { let hook = HOOK.swap(ptr::null_mut(), Ordering::SeqCst); if hook.is_null() { - default_oom_hook + default_alloc_error_hook } else { unsafe { mem::transmute(hook) } } } -fn default_oom_hook(layout: Layout) { +fn default_alloc_error_hook(layout: Layout) { dumb_print(format_args!("memory allocation of {} bytes failed", layout.size())); } @@ -130,7 +130,7 @@ fn default_oom_hook(layout: Layout) { pub extern fn rust_oom(layout: Layout) -> ! { let hook = HOOK.load(Ordering::SeqCst); let hook: fn(Layout) = if hook.is_null() { - default_oom_hook + default_alloc_error_hook } else { unsafe { mem::transmute(hook) } }; diff --git a/src/libstd/collections/hash/table.rs b/src/libstd/collections/hash/table.rs index 55f9f4f7cfe..d14b754ddb6 100644 --- a/src/libstd/collections/hash/table.rs +++ b/src/libstd/collections/hash/table.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use alloc::{Global, Alloc, Layout, LayoutErr, CollectionAllocErr, oom}; +use alloc::{Global, Alloc, Layout, LayoutErr, CollectionAllocErr, handle_alloc_error}; use hash::{BuildHasher, Hash, Hasher}; use marker; use mem::{size_of, needs_drop}; @@ -699,7 +699,7 @@ impl RawTable { // point into it. let (layout, _) = calculate_layout::(capacity)?; let buffer = Global.alloc(layout).map_err(|e| match fallibility { - Infallible => oom(layout), + Infallible => handle_alloc_error(layout), Fallible => e, })?; diff --git a/src/test/run-pass/allocator-alloc-one.rs b/src/test/run-pass/allocator-alloc-one.rs index f1fdbfc702d..f15b013c07b 100644 --- a/src/test/run-pass/allocator-alloc-one.rs +++ b/src/test/run-pass/allocator-alloc-one.rs @@ -10,11 +10,13 @@ #![feature(allocator_api, nonnull)] -use std::alloc::{Alloc, Global, Layout, oom}; +use std::alloc::{Alloc, Global, Layout, handle_alloc_error}; fn main() { unsafe { - let ptr = Global.alloc_one::().unwrap_or_else(|_| oom(Layout::new::())); + let ptr = Global.alloc_one::().unwrap_or_else(|_| { + handle_alloc_error(Layout::new::()) + }); *ptr.as_ptr() = 4; assert_eq!(*ptr.as_ptr(), 4); Global.dealloc_one(ptr); diff --git a/src/test/run-pass/realloc-16687.rs b/src/test/run-pass/realloc-16687.rs index 7152e721eac..3b4b458bb04 100644 --- a/src/test/run-pass/realloc-16687.rs +++ b/src/test/run-pass/realloc-16687.rs @@ -15,7 +15,7 @@ #![feature(heap_api, allocator_api)] -use std::alloc::{Global, Alloc, Layout, oom}; +use std::alloc::{Global, Alloc, Layout, handle_alloc_error}; use std::ptr::{self, NonNull}; fn main() { @@ -50,7 +50,7 @@ unsafe fn test_triangle() -> bool { println!("allocate({:?})", layout); } - let ret = Global.alloc(layout).unwrap_or_else(|_| oom(layout)); + let ret = Global.alloc(layout).unwrap_or_else(|_| handle_alloc_error(layout)); if PRINT { println!("allocate({:?}) = {:?}", layout, ret); @@ -73,7 +73,9 @@ unsafe fn test_triangle() -> bool { } let ret = Global.realloc(NonNull::new_unchecked(ptr), old, new.size()) - .unwrap_or_else(|_| oom(Layout::from_size_align_unchecked(new.size(), old.align()))); + .unwrap_or_else(|_| handle_alloc_error( + Layout::from_size_align_unchecked(new.size(), old.align()) + )); if PRINT { println!("reallocate({:?}, old={:?}, new={:?}) = {:?}", diff --git a/src/test/run-pass/regions-mock-codegen.rs b/src/test/run-pass/regions-mock-codegen.rs index 745a19dec4d..b58e837f3bd 100644 --- a/src/test/run-pass/regions-mock-codegen.rs +++ b/src/test/run-pass/regions-mock-codegen.rs @@ -12,7 +12,7 @@ #![feature(allocator_api)] -use std::alloc::{Alloc, Global, Layout, oom}; +use std::alloc::{Alloc, Global, Layout, handle_alloc_error}; use std::ptr::NonNull; struct arena(()); @@ -33,7 +33,7 @@ struct Ccx { fn alloc<'a>(_bcx : &'a arena) -> &'a Bcx<'a> { unsafe { let layout = Layout::new::(); - let ptr = Global.alloc(layout).unwrap_or_else(|_| oom(layout)); + let ptr = Global.alloc(layout).unwrap_or_else(|_| handle_alloc_error(layout)); &*(ptr.as_ptr() as *const _) } } -- cgit 1.4.1-3-g733a5 From d1193bf95e9a51d842d8091ee3f30aebe697f75b Mon Sep 17 00:00:00 2001 From: Corey Farwell Date: Tue, 14 Aug 2018 08:37:12 -0400 Subject: Add doc examples for std::alloc::{alloc,alloc_zeroed}. --- src/liballoc/alloc.rs | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) (limited to 'src/liballoc/alloc.rs') diff --git a/src/liballoc/alloc.rs b/src/liballoc/alloc.rs index 84bd275df34..c69b2fb5e1c 100644 --- a/src/liballoc/alloc.rs +++ b/src/liballoc/alloc.rs @@ -56,6 +56,22 @@ pub struct Global; /// # Safety /// /// See [`GlobalAlloc::alloc`]. +/// +/// # Examples +/// +/// ``` +/// use std::alloc::{alloc, dealloc, Layout}; +/// +/// unsafe { +/// let layout = Layout::new::(); +/// let ptr = alloc(layout); +/// +/// *(ptr as *mut u16) = 42; +/// assert_eq!(*(ptr as *mut u16), 42); +/// +/// dealloc(ptr, layout); +/// } +/// ``` #[stable(feature = "global_alloc", since = "1.28.0")] #[inline] pub unsafe fn alloc(layout: Layout) -> *mut u8 { @@ -110,6 +126,21 @@ pub unsafe fn realloc(ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 /// # Safety /// /// See [`GlobalAlloc::alloc_zeroed`]. +/// +/// # Examples +/// +/// ``` +/// use std::alloc::{alloc_zeroed, dealloc, Layout}; +/// +/// unsafe { +/// let layout = Layout::new::(); +/// let ptr = alloc_zeroed(layout); +/// +/// assert_eq!(*(ptr as *mut u16), 0); +/// +/// dealloc(ptr, layout); +/// } +/// ``` #[stable(feature = "global_alloc", since = "1.28.0")] #[inline] pub unsafe fn alloc_zeroed(layout: Layout) -> *mut u8 { -- cgit 1.4.1-3-g733a5