diff --git a/src/doc/reference.md b/src/doc/reference.md index 397abfe563c1f..ebb111a2e2e73 100644 --- a/src/doc/reference.md +++ b/src/doc/reference.md @@ -2091,6 +2091,8 @@ The following configurations must be defined by the implementation: * `target_pointer_width = "..."` - Target pointer width in bits. This is set to `"32"` for targets with 32-bit pointers, and likewise set to `"64"` for 64-bit pointers. +* `target_has_atomic = "..."` - Set of integer sizes on which the target can perform + atomic operations. Values are `"8"`, `"16"`, `"32"`, `"64"` and `"ptr"`. * `target_vendor = "..."` - Vendor of the target, for example `apple`, `pc`, or simply `"unknown"`. * `test` - Enabled when compiling the test harness (using the `--test` flag). @@ -2295,6 +2297,9 @@ The currently implemented features of the reference compiler are: * `cfg_target_vendor` - Allows conditional compilation using the `target_vendor` matcher which is subject to change. +* `cfg_target_has_atomic` - Allows conditional compilation using the `target_has_atomic` + matcher which is subject to change. + * `concat_idents` - Allows use of the `concat_idents` macro, which is in many ways insufficient for concatenating identifiers, and may be removed entirely for something more wholesome. diff --git a/src/libcore/lib.rs b/src/libcore/lib.rs index fa5e90562d80e..873059d3ccceb 100644 --- a/src/libcore/lib.rs +++ b/src/libcore/lib.rs @@ -63,6 +63,7 @@ #![feature(associated_type_defaults)] #![feature(concat_idents)] #![feature(const_fn)] +#![feature(cfg_target_has_atomic)] #![feature(custom_attribute)] #![feature(fundamental)] #![feature(inclusive_range_syntax)] diff --git a/src/libcore/sync/atomic.rs b/src/libcore/sync/atomic.rs index e74dc08ea7121..cf3e45cf3de79 100644 --- a/src/libcore/sync/atomic.rs +++ b/src/libcore/sync/atomic.rs @@ -83,11 +83,13 @@ use default::Default; use fmt; /// A boolean type which can be safely shared between threads. +#[cfg(any(stage0, target_has_atomic = "ptr"))] #[stable(feature = "rust1", since = "1.0.0")] pub struct AtomicBool { v: UnsafeCell, } +#[cfg(any(stage0, target_has_atomic = "ptr"))] #[stable(feature = "rust1", since = "1.0.0")] impl Default for AtomicBool { fn default() -> Self { @@ -96,49 +98,18 @@ impl Default for AtomicBool { } // Send is implicitly implemented for AtomicBool. +#[cfg(any(stage0, target_has_atomic = "ptr"))] #[stable(feature = "rust1", since = "1.0.0")] unsafe impl Sync for AtomicBool {} -/// A signed integer type which can be safely shared between threads. -#[stable(feature = "rust1", since = "1.0.0")] -pub struct AtomicIsize { - v: UnsafeCell, -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Default for AtomicIsize { - fn default() -> Self { - Self::new(Default::default()) - } -} - -// Send is implicitly implemented for AtomicIsize. -#[stable(feature = "rust1", since = "1.0.0")] -unsafe impl Sync for AtomicIsize {} - -/// An unsigned integer type which can be safely shared between threads. -#[stable(feature = "rust1", since = "1.0.0")] -pub struct AtomicUsize { - v: UnsafeCell, -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Default for AtomicUsize { - fn default() -> Self { - Self::new(Default::default()) - } -} - -// Send is implicitly implemented for AtomicUsize. -#[stable(feature = "rust1", since = "1.0.0")] -unsafe impl Sync for AtomicUsize {} - /// A raw pointer type which can be safely shared between threads. +#[cfg(any(stage0, target_has_atomic = "ptr"))] #[stable(feature = "rust1", since = "1.0.0")] pub struct AtomicPtr { p: UnsafeCell<*mut T>, } +#[cfg(any(stage0, target_has_atomic = "ptr"))] #[stable(feature = "rust1", since = "1.0.0")] impl Default for AtomicPtr { fn default() -> AtomicPtr { @@ -146,8 +117,10 @@ impl Default for AtomicPtr { } } +#[cfg(any(stage0, target_has_atomic = "ptr"))] #[stable(feature = "rust1", since = "1.0.0")] unsafe impl Send for AtomicPtr {} +#[cfg(any(stage0, target_has_atomic = "ptr"))] #[stable(feature = "rust1", since = "1.0.0")] unsafe impl Sync for AtomicPtr {} @@ -189,18 +162,15 @@ pub enum Ordering { } /// An `AtomicBool` initialized to `false`. +#[cfg(any(stage0, target_has_atomic = "ptr"))] #[stable(feature = "rust1", since = "1.0.0")] pub const ATOMIC_BOOL_INIT: AtomicBool = AtomicBool::new(false); -/// An `AtomicIsize` initialized to `0`. -#[stable(feature = "rust1", since = "1.0.0")] -pub const ATOMIC_ISIZE_INIT: AtomicIsize = AtomicIsize::new(0); -/// An `AtomicUsize` initialized to `0`. -#[stable(feature = "rust1", since = "1.0.0")] -pub const ATOMIC_USIZE_INIT: AtomicUsize = AtomicUsize::new(0); // NB: Needs to be -1 (0b11111111...) to make fetch_nand work correctly +#[cfg(any(stage0, target_has_atomic = "ptr"))] const UINT_TRUE: usize = !0; +#[cfg(any(stage0, target_has_atomic = "ptr"))] impl AtomicBool { /// Creates a new `AtomicBool`. /// @@ -543,557 +513,7 @@ impl AtomicBool { } } -impl AtomicIsize { - /// Creates a new `AtomicIsize`. - /// - /// # Examples - /// - /// ``` - /// use std::sync::atomic::AtomicIsize; - /// - /// let atomic_forty_two = AtomicIsize::new(42); - /// ``` - #[inline] - #[stable(feature = "rust1", since = "1.0.0")] - pub const fn new(v: isize) -> AtomicIsize { - AtomicIsize {v: UnsafeCell::new(v)} - } - - /// Loads a value from the isize. - /// - /// `load` takes an `Ordering` argument which describes the memory ordering of this operation. - /// - /// # Panics - /// - /// Panics if `order` is `Release` or `AcqRel`. - /// - /// # Examples - /// - /// ``` - /// use std::sync::atomic::{AtomicIsize, Ordering}; - /// - /// let some_isize = AtomicIsize::new(5); - /// - /// assert_eq!(some_isize.load(Ordering::Relaxed), 5); - /// ``` - #[inline] - #[stable(feature = "rust1", since = "1.0.0")] - pub fn load(&self, order: Ordering) -> isize { - unsafe { atomic_load(self.v.get(), order) } - } - - /// Stores a value into the isize. - /// - /// `store` takes an `Ordering` argument which describes the memory ordering of this operation. - /// - /// # Examples - /// - /// ``` - /// use std::sync::atomic::{AtomicIsize, Ordering}; - /// - /// let some_isize = AtomicIsize::new(5); - /// - /// some_isize.store(10, Ordering::Relaxed); - /// assert_eq!(some_isize.load(Ordering::Relaxed), 10); - /// ``` - /// - /// # Panics - /// - /// Panics if `order` is `Acquire` or `AcqRel`. - #[inline] - #[stable(feature = "rust1", since = "1.0.0")] - pub fn store(&self, val: isize, order: Ordering) { - unsafe { atomic_store(self.v.get(), val, order); } - } - - /// Stores a value into the isize, returning the old value. - /// - /// `swap` takes an `Ordering` argument which describes the memory ordering of this operation. - /// - /// # Examples - /// - /// ``` - /// use std::sync::atomic::{AtomicIsize, Ordering}; - /// - /// let some_isize = AtomicIsize::new(5); - /// - /// assert_eq!(some_isize.swap(10, Ordering::Relaxed), 5); - /// ``` - #[inline] - #[stable(feature = "rust1", since = "1.0.0")] - pub fn swap(&self, val: isize, order: Ordering) -> isize { - unsafe { atomic_swap(self.v.get(), val, order) } - } - - /// Stores a value into the `isize` if the current value is the same as the `current` value. - /// - /// The return value is always the previous value. If it is equal to `current`, then the value - /// was updated. - /// - /// `compare_and_swap` also takes an `Ordering` argument which describes the memory ordering of - /// this operation. - /// - /// # Examples - /// - /// ``` - /// use std::sync::atomic::{AtomicIsize, Ordering}; - /// - /// let some_isize = AtomicIsize::new(5); - /// - /// assert_eq!(some_isize.compare_and_swap(5, 10, Ordering::Relaxed), 5); - /// assert_eq!(some_isize.load(Ordering::Relaxed), 10); - /// - /// assert_eq!(some_isize.compare_and_swap(6, 12, Ordering::Relaxed), 10); - /// assert_eq!(some_isize.load(Ordering::Relaxed), 10); - /// ``` - #[inline] - #[stable(feature = "rust1", since = "1.0.0")] - pub fn compare_and_swap(&self, current: isize, new: isize, order: Ordering) -> isize { - match self.compare_exchange(current, new, order, strongest_failure_ordering(order)) { - Ok(x) => x, - Err(x) => x, - } - } - - /// Stores a value into the `isize` if the current value is the same as the `current` value. - /// - /// The return value is a result indicating whether the new value was written and containing - /// the previous value. On success this value is guaranteed to be equal to `new`. - /// - /// `compare_exchange` takes two `Ordering` arguments to describe the memory ordering of this - /// operation. The first describes the required ordering if the operation succeeds while the - /// second describes the required ordering when the operation fails. The failure ordering can't - /// be `Release` or `AcqRel` and must be equivalent or weaker than the success ordering. - /// - /// # Examples - /// - /// ``` - /// # #![feature(extended_compare_and_swap)] - /// use std::sync::atomic::{AtomicIsize, Ordering}; - /// - /// let some_isize = AtomicIsize::new(5); - /// - /// assert_eq!(some_isize.compare_exchange(5, 10, - /// Ordering::Acquire, - /// Ordering::Relaxed), - /// Ok(5)); - /// assert_eq!(some_isize.load(Ordering::Relaxed), 10); - /// - /// assert_eq!(some_isize.compare_exchange(6, 12, - /// Ordering::SeqCst, - /// Ordering::Acquire), - /// Err(10)); - /// assert_eq!(some_isize.load(Ordering::Relaxed), 10); - /// ``` - #[inline] - #[unstable(feature = "extended_compare_and_swap", reason = "recently added", issue = "31767")] - pub fn compare_exchange(&self, - current: isize, - new: isize, - success: Ordering, - failure: Ordering) -> Result { - unsafe { atomic_compare_exchange(self.v.get(), current, new, success, failure) } - } - - /// Stores a value into the `isize` if the current value is the same as the `current` value. - /// - /// Unlike `compare_exchange`, this function is allowed to spuriously fail even when the - /// comparison succeeds, which can result in more efficient code on some platforms. The - /// return value is a result indicating whether the new value was written and containing the - /// previous value. - /// - /// `compare_exchange_weak` takes two `Ordering` arguments to describe the memory - /// ordering of this operation. The first describes the required ordering if the operation - /// succeeds while the second describes the required ordering when the operation fails. The - /// failure ordering can't be `Release` or `AcqRel` and must be equivalent or weaker than the - /// success ordering. - /// - /// # Examples - /// - /// ``` - /// # #![feature(extended_compare_and_swap)] - /// use std::sync::atomic::{AtomicIsize, Ordering}; - /// - /// let val = AtomicIsize::new(4); - /// - /// let mut old = val.load(Ordering::Relaxed); - /// loop { - /// let new = old * 2; - /// match val.compare_exchange_weak(old, new, Ordering::SeqCst, Ordering::Relaxed) { - /// Ok(_) => break, - /// Err(x) => old = x, - /// } - /// } - /// ``` - #[inline] - #[unstable(feature = "extended_compare_and_swap", reason = "recently added", issue = "31767")] - pub fn compare_exchange_weak(&self, - current: isize, - new: isize, - success: Ordering, - failure: Ordering) -> Result { - unsafe { atomic_compare_exchange_weak(self.v.get(), current, new, success, failure) } - } - - /// Add an isize to the current value, returning the previous value. - /// - /// # Examples - /// - /// ``` - /// use std::sync::atomic::{AtomicIsize, Ordering}; - /// - /// let foo = AtomicIsize::new(0); - /// assert_eq!(foo.fetch_add(10, Ordering::SeqCst), 0); - /// assert_eq!(foo.load(Ordering::SeqCst), 10); - /// ``` - #[inline] - #[stable(feature = "rust1", since = "1.0.0")] - pub fn fetch_add(&self, val: isize, order: Ordering) -> isize { - unsafe { atomic_add(self.v.get(), val, order) } - } - - /// Subtract an isize from the current value, returning the previous value. - /// - /// # Examples - /// - /// ``` - /// use std::sync::atomic::{AtomicIsize, Ordering}; - /// - /// let foo = AtomicIsize::new(0); - /// assert_eq!(foo.fetch_sub(10, Ordering::SeqCst), 0); - /// assert_eq!(foo.load(Ordering::SeqCst), -10); - /// ``` - #[inline] - #[stable(feature = "rust1", since = "1.0.0")] - pub fn fetch_sub(&self, val: isize, order: Ordering) -> isize { - unsafe { atomic_sub(self.v.get(), val, order) } - } - - /// Bitwise and with the current isize, returning the previous value. - /// - /// # Examples - /// - /// ``` - /// use std::sync::atomic::{AtomicIsize, Ordering}; - /// - /// let foo = AtomicIsize::new(0b101101); - /// assert_eq!(foo.fetch_and(0b110011, Ordering::SeqCst), 0b101101); - /// assert_eq!(foo.load(Ordering::SeqCst), 0b100001); - #[inline] - #[stable(feature = "rust1", since = "1.0.0")] - pub fn fetch_and(&self, val: isize, order: Ordering) -> isize { - unsafe { atomic_and(self.v.get(), val, order) } - } - - /// Bitwise or with the current isize, returning the previous value. - /// - /// # Examples - /// - /// ``` - /// use std::sync::atomic::{AtomicIsize, Ordering}; - /// - /// let foo = AtomicIsize::new(0b101101); - /// assert_eq!(foo.fetch_or(0b110011, Ordering::SeqCst), 0b101101); - /// assert_eq!(foo.load(Ordering::SeqCst), 0b111111); - #[inline] - #[stable(feature = "rust1", since = "1.0.0")] - pub fn fetch_or(&self, val: isize, order: Ordering) -> isize { - unsafe { atomic_or(self.v.get(), val, order) } - } - - /// Bitwise xor with the current isize, returning the previous value. - /// - /// # Examples - /// - /// ``` - /// use std::sync::atomic::{AtomicIsize, Ordering}; - /// - /// let foo = AtomicIsize::new(0b101101); - /// assert_eq!(foo.fetch_xor(0b110011, Ordering::SeqCst), 0b101101); - /// assert_eq!(foo.load(Ordering::SeqCst), 0b011110); - #[inline] - #[stable(feature = "rust1", since = "1.0.0")] - pub fn fetch_xor(&self, val: isize, order: Ordering) -> isize { - unsafe { atomic_xor(self.v.get(), val, order) } - } -} - -impl AtomicUsize { - /// Creates a new `AtomicUsize`. - /// - /// # Examples - /// - /// ``` - /// use std::sync::atomic::AtomicUsize; - /// - /// let atomic_forty_two = AtomicUsize::new(42); - /// ``` - #[inline] - #[stable(feature = "rust1", since = "1.0.0")] - pub const fn new(v: usize) -> AtomicUsize { - AtomicUsize { v: UnsafeCell::new(v) } - } - - /// Loads a value from the usize. - /// - /// `load` takes an `Ordering` argument which describes the memory ordering of this operation. - /// - /// # Panics - /// - /// Panics if `order` is `Release` or `AcqRel`. - /// - /// # Examples - /// - /// ``` - /// use std::sync::atomic::{AtomicUsize, Ordering}; - /// - /// let some_usize = AtomicUsize::new(5); - /// - /// assert_eq!(some_usize.load(Ordering::Relaxed), 5); - /// ``` - #[inline] - #[stable(feature = "rust1", since = "1.0.0")] - pub fn load(&self, order: Ordering) -> usize { - unsafe { atomic_load(self.v.get(), order) } - } - - /// Stores a value into the usize. - /// - /// `store` takes an `Ordering` argument which describes the memory ordering of this operation. - /// - /// # Examples - /// - /// ``` - /// use std::sync::atomic::{AtomicUsize, Ordering}; - /// - /// let some_usize = AtomicUsize::new(5); - /// - /// some_usize.store(10, Ordering::Relaxed); - /// assert_eq!(some_usize.load(Ordering::Relaxed), 10); - /// ``` - /// - /// # Panics - /// - /// Panics if `order` is `Acquire` or `AcqRel`. - #[inline] - #[stable(feature = "rust1", since = "1.0.0")] - pub fn store(&self, val: usize, order: Ordering) { - unsafe { atomic_store(self.v.get(), val, order); } - } - - /// Stores a value into the usize, returning the old value. - /// - /// `swap` takes an `Ordering` argument which describes the memory ordering of this operation. - /// - /// # Examples - /// - /// ``` - /// use std::sync::atomic::{AtomicUsize, Ordering}; - /// - /// let some_usize = AtomicUsize::new(5); - /// - /// assert_eq!(some_usize.swap(10, Ordering::Relaxed), 5); - /// assert_eq!(some_usize.load(Ordering::Relaxed), 10); - /// ``` - #[inline] - #[stable(feature = "rust1", since = "1.0.0")] - pub fn swap(&self, val: usize, order: Ordering) -> usize { - unsafe { atomic_swap(self.v.get(), val, order) } - } - - /// Stores a value into the `usize` if the current value is the same as the `current` value. - /// - /// The return value is always the previous value. If it is equal to `current`, then the value - /// was updated. - /// - /// `compare_and_swap` also takes an `Ordering` argument which describes the memory ordering of - /// this operation. - /// - /// # Examples - /// - /// ``` - /// use std::sync::atomic::{AtomicUsize, Ordering}; - /// - /// let some_usize = AtomicUsize::new(5); - /// - /// assert_eq!(some_usize.compare_and_swap(5, 10, Ordering::Relaxed), 5); - /// assert_eq!(some_usize.load(Ordering::Relaxed), 10); - /// - /// assert_eq!(some_usize.compare_and_swap(6, 12, Ordering::Relaxed), 10); - /// assert_eq!(some_usize.load(Ordering::Relaxed), 10); - /// ``` - #[inline] - #[stable(feature = "rust1", since = "1.0.0")] - pub fn compare_and_swap(&self, current: usize, new: usize, order: Ordering) -> usize { - match self.compare_exchange(current, new, order, strongest_failure_ordering(order)) { - Ok(x) => x, - Err(x) => x, - } - } - - /// Stores a value into the `usize` if the current value is the same as the `current` value. - /// - /// The return value is a result indicating whether the new value was written and containing - /// the previous value. On success this value is guaranteed to be equal to `new`. - /// - /// `compare_exchange` takes two `Ordering` arguments to describe the memory ordering of this - /// operation. The first describes the required ordering if the operation succeeds while the - /// second describes the required ordering when the operation fails. The failure ordering can't - /// be `Release` or `AcqRel` and must be equivalent or weaker than the success ordering. - /// - /// # Examples - /// - /// ``` - /// # #![feature(extended_compare_and_swap)] - /// use std::sync::atomic::{AtomicUsize, Ordering}; - /// - /// let some_isize = AtomicUsize::new(5); - /// - /// assert_eq!(some_isize.compare_exchange(5, 10, - /// Ordering::Acquire, - /// Ordering::Relaxed), - /// Ok(5)); - /// assert_eq!(some_isize.load(Ordering::Relaxed), 10); - /// - /// assert_eq!(some_isize.compare_exchange(6, 12, - /// Ordering::SeqCst, - /// Ordering::Acquire), - /// Err(10)); - /// assert_eq!(some_isize.load(Ordering::Relaxed), 10); - /// ``` - #[inline] - #[unstable(feature = "extended_compare_and_swap", reason = "recently added", issue = "31767")] - pub fn compare_exchange(&self, - current: usize, - new: usize, - success: Ordering, - failure: Ordering) -> Result { - unsafe { atomic_compare_exchange(self.v.get(), current, new, success, failure) } - } - - /// Stores a value into the `usize` if the current value is the same as the `current` value. - /// - /// Unlike `compare_exchange`, this function is allowed to spuriously fail even when the - /// comparison succeeds, which can result in more efficient code on some platforms. The - /// return value is a result indicating whether the new value was written and containing the - /// previous value. - /// - /// `compare_exchange_weak` takes two `Ordering` arguments to describe the memory - /// ordering of this operation. The first describes the required ordering if the operation - /// succeeds while the second describes the required ordering when the operation fails. The - /// failure ordering can't be `Release` or `AcqRel` and must be equivalent or weaker than the - /// success ordering. - /// - /// # Examples - /// - /// ``` - /// # #![feature(extended_compare_and_swap)] - /// use std::sync::atomic::{AtomicUsize, Ordering}; - /// - /// let val = AtomicUsize::new(4); - /// - /// let mut old = val.load(Ordering::Relaxed); - /// loop { - /// let new = old * 2; - /// match val.compare_exchange_weak(old, new, Ordering::SeqCst, Ordering::Relaxed) { - /// Ok(_) => break, - /// Err(x) => old = x, - /// } - /// } - /// ``` - #[inline] - #[unstable(feature = "extended_compare_and_swap", reason = "recently added", issue = "31767")] - pub fn compare_exchange_weak(&self, - current: usize, - new: usize, - success: Ordering, - failure: Ordering) -> Result { - unsafe { atomic_compare_exchange_weak(self.v.get(), current, new, success, failure) } - } - - /// Add to the current usize, returning the previous value. - /// - /// # Examples - /// - /// ``` - /// use std::sync::atomic::{AtomicUsize, Ordering}; - /// - /// let foo = AtomicUsize::new(0); - /// assert_eq!(foo.fetch_add(10, Ordering::SeqCst), 0); - /// assert_eq!(foo.load(Ordering::SeqCst), 10); - /// ``` - #[inline] - #[stable(feature = "rust1", since = "1.0.0")] - pub fn fetch_add(&self, val: usize, order: Ordering) -> usize { - unsafe { atomic_add(self.v.get(), val, order) } - } - - /// Subtract from the current usize, returning the previous value. - /// - /// # Examples - /// - /// ``` - /// use std::sync::atomic::{AtomicUsize, Ordering}; - /// - /// let foo = AtomicUsize::new(10); - /// assert_eq!(foo.fetch_sub(10, Ordering::SeqCst), 10); - /// assert_eq!(foo.load(Ordering::SeqCst), 0); - /// ``` - #[inline] - #[stable(feature = "rust1", since = "1.0.0")] - pub fn fetch_sub(&self, val: usize, order: Ordering) -> usize { - unsafe { atomic_sub(self.v.get(), val, order) } - } - - /// Bitwise and with the current usize, returning the previous value. - /// - /// # Examples - /// - /// ``` - /// use std::sync::atomic::{AtomicUsize, Ordering}; - /// - /// let foo = AtomicUsize::new(0b101101); - /// assert_eq!(foo.fetch_and(0b110011, Ordering::SeqCst), 0b101101); - /// assert_eq!(foo.load(Ordering::SeqCst), 0b100001); - #[inline] - #[stable(feature = "rust1", since = "1.0.0")] - pub fn fetch_and(&self, val: usize, order: Ordering) -> usize { - unsafe { atomic_and(self.v.get(), val, order) } - } - - /// Bitwise or with the current usize, returning the previous value. - /// - /// # Examples - /// - /// ``` - /// use std::sync::atomic::{AtomicUsize, Ordering}; - /// - /// let foo = AtomicUsize::new(0b101101); - /// assert_eq!(foo.fetch_or(0b110011, Ordering::SeqCst), 0b101101); - /// assert_eq!(foo.load(Ordering::SeqCst), 0b111111); - #[inline] - #[stable(feature = "rust1", since = "1.0.0")] - pub fn fetch_or(&self, val: usize, order: Ordering) -> usize { - unsafe { atomic_or(self.v.get(), val, order) } - } - - /// Bitwise xor with the current usize, returning the previous value. - /// - /// # Examples - /// - /// ``` - /// use std::sync::atomic::{AtomicUsize, Ordering}; - /// - /// let foo = AtomicUsize::new(0b101101); - /// assert_eq!(foo.fetch_xor(0b110011, Ordering::SeqCst), 0b101101); - /// assert_eq!(foo.load(Ordering::SeqCst), 0b011110); - #[inline] - #[stable(feature = "rust1", since = "1.0.0")] - pub fn fetch_xor(&self, val: usize, order: Ordering) -> usize { - unsafe { atomic_xor(self.v.get(), val, order) } - } -} - +#[cfg(any(stage0, target_has_atomic = "ptr"))] impl AtomicPtr { /// Creates a new `AtomicPtr`. /// @@ -1311,6 +731,405 @@ impl AtomicPtr { } } +macro_rules! atomic_int { + ($stable:meta, + $stable_cxchg:meta, + $stable_debug:meta, + $int_type:ident $atomic_type:ident $atomic_init:ident) => { + /// An integer type which can be safely shared between threads. + #[$stable] + pub struct $atomic_type { + v: UnsafeCell<$int_type>, + } + + /// An atomic integer initialized to `0`. + #[$stable] + pub const $atomic_init: $atomic_type = $atomic_type::new(0); + + #[$stable] + impl Default for $atomic_type { + fn default() -> Self { + Self::new(Default::default()) + } + } + + #[$stable_debug] + impl fmt::Debug for $atomic_type { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_tuple(stringify!($atomic_type)) + .field(&self.load(Ordering::SeqCst)) + .finish() + } + } + + // Send is implicitly implemented. + #[$stable] + unsafe impl Sync for $atomic_type {} + + impl $atomic_type { + /// Creates a new atomic integer. + /// + /// # Examples + /// + /// ``` + /// use std::sync::atomic::AtomicIsize; + /// + /// let atomic_forty_two = AtomicIsize::new(42); + /// ``` + #[inline] + #[$stable] + pub const fn new(v: $int_type) -> Self { + $atomic_type {v: UnsafeCell::new(v)} + } + + /// Loads a value from the atomic integer. + /// + /// `load` takes an `Ordering` argument which describes the memory ordering of this + /// operation. + /// + /// # Panics + /// + /// Panics if `order` is `Release` or `AcqRel`. + /// + /// # Examples + /// + /// ``` + /// use std::sync::atomic::{AtomicIsize, Ordering}; + /// + /// let some_isize = AtomicIsize::new(5); + /// + /// assert_eq!(some_isize.load(Ordering::Relaxed), 5); + /// ``` + #[inline] + #[$stable] + pub fn load(&self, order: Ordering) -> $int_type { + unsafe { atomic_load(self.v.get(), order) } + } + + /// Stores a value into the atomic integer. + /// + /// `store` takes an `Ordering` argument which describes the memory ordering of this + /// operation. + /// + /// # Examples + /// + /// ``` + /// use std::sync::atomic::{AtomicIsize, Ordering}; + /// + /// let some_isize = AtomicIsize::new(5); + /// + /// some_isize.store(10, Ordering::Relaxed); + /// assert_eq!(some_isize.load(Ordering::Relaxed), 10); + /// ``` + /// + /// # Panics + /// + /// Panics if `order` is `Acquire` or `AcqRel`. + #[inline] + #[$stable] + pub fn store(&self, val: $int_type, order: Ordering) { + unsafe { atomic_store(self.v.get(), val, order); } + } + + /// Stores a value into the atomic integer, returning the old value. + /// + /// `swap` takes an `Ordering` argument which describes the memory ordering of this + /// operation. + /// + /// # Examples + /// + /// ``` + /// use std::sync::atomic::{AtomicIsize, Ordering}; + /// + /// let some_isize = AtomicIsize::new(5); + /// + /// assert_eq!(some_isize.swap(10, Ordering::Relaxed), 5); + /// ``` + #[inline] + #[$stable] + pub fn swap(&self, val: $int_type, order: Ordering) -> $int_type { + unsafe { atomic_swap(self.v.get(), val, order) } + } + + /// Stores a value into the atomic integer if the current value is the same as the + /// `current` value. + /// + /// The return value is always the previous value. If it is equal to `current`, then the + /// value was updated. + /// + /// `compare_and_swap` also takes an `Ordering` argument which describes the memory + /// ordering of this operation. + /// + /// # Examples + /// + /// ``` + /// use std::sync::atomic::{AtomicIsize, Ordering}; + /// + /// let some_isize = AtomicIsize::new(5); + /// + /// assert_eq!(some_isize.compare_and_swap(5, 10, Ordering::Relaxed), 5); + /// assert_eq!(some_isize.load(Ordering::Relaxed), 10); + /// + /// assert_eq!(some_isize.compare_and_swap(6, 12, Ordering::Relaxed), 10); + /// assert_eq!(some_isize.load(Ordering::Relaxed), 10); + /// ``` + #[inline] + #[$stable] + pub fn compare_and_swap(&self, + current: $int_type, + new: $int_type, + order: Ordering) -> $int_type { + match self.compare_exchange(current, + new, + order, + strongest_failure_ordering(order)) { + Ok(x) => x, + Err(x) => x, + } + } + + /// Stores a value into the atomic integer if the current value is the same as the + /// `current` value. + /// + /// The return value is a result indicating whether the new value was written and + /// containing the previous value. On success this value is guaranteed to be equal to + /// `new`. + /// + /// `compare_exchange` takes two `Ordering` arguments to describe the memory ordering of + /// this operation. The first describes the required ordering if the operation succeeds + /// while the second describes the required ordering when the operation fails. The + /// failure ordering can't be `Release` or `AcqRel` and must be equivalent or weaker + /// than the success ordering. + /// + /// # Examples + /// + /// ``` + /// # #![feature(extended_compare_and_swap)] + /// use std::sync::atomic::{AtomicIsize, Ordering}; + /// + /// let some_isize = AtomicIsize::new(5); + /// + /// assert_eq!(some_isize.compare_exchange(5, 10, + /// Ordering::Acquire, + /// Ordering::Relaxed), + /// Ok(5)); + /// assert_eq!(some_isize.load(Ordering::Relaxed), 10); + /// + /// assert_eq!(some_isize.compare_exchange(6, 12, + /// Ordering::SeqCst, + /// Ordering::Acquire), + /// Err(10)); + /// assert_eq!(some_isize.load(Ordering::Relaxed), 10); + /// ``` + #[inline] + #[$stable_cxchg] + pub fn compare_exchange(&self, + current: $int_type, + new: $int_type, + success: Ordering, + failure: Ordering) -> Result<$int_type, $int_type> { + unsafe { atomic_compare_exchange(self.v.get(), current, new, success, failure) } + } + + /// Stores a value into the atomic integer if the current value is the same as the + /// `current` value. + /// + /// Unlike `compare_exchange`, this function is allowed to spuriously fail even when the + /// comparison succeeds, which can result in more efficient code on some platforms. The + /// return value is a result indicating whether the new value was written and containing + /// the previous value. + /// + /// `compare_exchange_weak` takes two `Ordering` arguments to describe the memory + /// ordering of this operation. The first describes the required ordering if the + /// operation succeeds while the second describes the required ordering when the + /// operation fails. The failure ordering can't be `Release` or `AcqRel` and must be + /// equivalent or weaker than the success ordering. + /// + /// # Examples + /// + /// ``` + /// # #![feature(extended_compare_and_swap)] + /// use std::sync::atomic::{AtomicIsize, Ordering}; + /// + /// let val = AtomicIsize::new(4); + /// + /// let mut old = val.load(Ordering::Relaxed); + /// loop { + /// let new = old * 2; + /// match val.compare_exchange_weak(old, new, Ordering::SeqCst, Ordering::Relaxed) { + /// Ok(_) => break, + /// Err(x) => old = x, + /// } + /// } + /// ``` + #[inline] + #[$stable_cxchg] + pub fn compare_exchange_weak(&self, + current: $int_type, + new: $int_type, + success: Ordering, + failure: Ordering) -> Result<$int_type, $int_type> { + unsafe { + atomic_compare_exchange_weak(self.v.get(), current, new, success, failure) + } + } + + /// Add to the current value, returning the previous value. + /// + /// # Examples + /// + /// ``` + /// use std::sync::atomic::{AtomicIsize, Ordering}; + /// + /// let foo = AtomicIsize::new(0); + /// assert_eq!(foo.fetch_add(10, Ordering::SeqCst), 0); + /// assert_eq!(foo.load(Ordering::SeqCst), 10); + /// ``` + #[inline] + #[$stable] + pub fn fetch_add(&self, val: $int_type, order: Ordering) -> $int_type { + unsafe { atomic_add(self.v.get(), val, order) } + } + + /// Subtract from the current value, returning the previous value. + /// + /// # Examples + /// + /// ``` + /// use std::sync::atomic::{AtomicIsize, Ordering}; + /// + /// let foo = AtomicIsize::new(0); + /// assert_eq!(foo.fetch_sub(10, Ordering::SeqCst), 0); + /// assert_eq!(foo.load(Ordering::SeqCst), -10); + /// ``` + #[inline] + #[$stable] + pub fn fetch_sub(&self, val: $int_type, order: Ordering) -> $int_type { + unsafe { atomic_sub(self.v.get(), val, order) } + } + + /// Bitwise and with the current value, returning the previous value. + /// + /// # Examples + /// + /// ``` + /// use std::sync::atomic::{AtomicIsize, Ordering}; + /// + /// let foo = AtomicIsize::new(0b101101); + /// assert_eq!(foo.fetch_and(0b110011, Ordering::SeqCst), 0b101101); + /// assert_eq!(foo.load(Ordering::SeqCst), 0b100001); + #[inline] + #[$stable] + pub fn fetch_and(&self, val: $int_type, order: Ordering) -> $int_type { + unsafe { atomic_and(self.v.get(), val, order) } + } + + /// Bitwise or with the current value, returning the previous value. + /// + /// # Examples + /// + /// ``` + /// use std::sync::atomic::{AtomicIsize, Ordering}; + /// + /// let foo = AtomicIsize::new(0b101101); + /// assert_eq!(foo.fetch_or(0b110011, Ordering::SeqCst), 0b101101); + /// assert_eq!(foo.load(Ordering::SeqCst), 0b111111); + #[inline] + #[$stable] + pub fn fetch_or(&self, val: $int_type, order: Ordering) -> $int_type { + unsafe { atomic_or(self.v.get(), val, order) } + } + + /// Bitwise xor with the current value, returning the previous value. + /// + /// # Examples + /// + /// ``` + /// use std::sync::atomic::{AtomicIsize, Ordering}; + /// + /// let foo = AtomicIsize::new(0b101101); + /// assert_eq!(foo.fetch_xor(0b110011, Ordering::SeqCst), 0b101101); + /// assert_eq!(foo.load(Ordering::SeqCst), 0b011110); + #[inline] + #[$stable] + pub fn fetch_xor(&self, val: $int_type, order: Ordering) -> $int_type { + unsafe { atomic_xor(self.v.get(), val, order) } + } + } + } +} + +#[cfg(target_has_atomic = "8")] +atomic_int! { + unstable(feature = "integer_atomics", issue = "32976"), + unstable(feature = "integer_atomics", issue = "32976"), + unstable(feature = "integer_atomics", issue = "32976"), + i8 AtomicI8 ATOMIC_I8_INIT +} +#[cfg(target_has_atomic = "8")] +atomic_int! { + unstable(feature = "integer_atomics", issue = "32976"), + unstable(feature = "integer_atomics", issue = "32976"), + unstable(feature = "integer_atomics", issue = "32976"), + u8 AtomicU8 ATOMIC_U8_INIT +} +#[cfg(target_has_atomic = "16")] +atomic_int! { + unstable(feature = "integer_atomics", issue = "32976"), + unstable(feature = "integer_atomics", issue = "32976"), + unstable(feature = "integer_atomics", issue = "32976"), + i16 AtomicI16 ATOMIC_I16_INIT +} +#[cfg(target_has_atomic = "16")] +atomic_int! { + unstable(feature = "integer_atomics", issue = "32976"), + unstable(feature = "integer_atomics", issue = "32976"), + unstable(feature = "integer_atomics", issue = "32976"), + u16 AtomicU16 ATOMIC_U16_INIT +} +#[cfg(target_has_atomic = "32")] +atomic_int! { + unstable(feature = "integer_atomics", issue = "32976"), + unstable(feature = "integer_atomics", issue = "32976"), + unstable(feature = "integer_atomics", issue = "32976"), + i32 AtomicI32 ATOMIC_I32_INIT +} +#[cfg(target_has_atomic = "32")] +atomic_int! { + unstable(feature = "integer_atomics", issue = "32976"), + unstable(feature = "integer_atomics", issue = "32976"), + unstable(feature = "integer_atomics", issue = "32976"), + u32 AtomicU32 ATOMIC_U32_INIT +} +#[cfg(target_has_atomic = "64")] +atomic_int! { + unstable(feature = "integer_atomics", issue = "32976"), + unstable(feature = "integer_atomics", issue = "32976"), + unstable(feature = "integer_atomics", issue = "32976"), + i64 AtomicI64 ATOMIC_I64_INIT +} +#[cfg(target_has_atomic = "64")] +atomic_int! { + unstable(feature = "integer_atomics", issue = "32976"), + unstable(feature = "integer_atomics", issue = "32976"), + unstable(feature = "integer_atomics", issue = "32976"), + u64 AtomicU64 ATOMIC_U64_INIT +} +#[cfg(any(stage0, target_has_atomic = "ptr"))] +atomic_int!{ + stable(feature = "rust1", since = "1.0.0"), + unstable(feature = "extended_compare_and_swap", reason = "recently added", issue = "31767"), + stable(feature = "atomic_debug", since = "1.3.0"), + isize AtomicIsize ATOMIC_ISIZE_INIT +} +#[cfg(any(stage0, target_has_atomic = "ptr"))] +atomic_int!{ + stable(feature = "rust1", since = "1.0.0"), + unstable(feature = "extended_compare_and_swap", reason = "recently added", issue = "31767"), + stable(feature = "atomic_debug", since = "1.3.0"), + usize AtomicUsize ATOMIC_USIZE_INIT +} + #[inline] fn strongest_failure_ordering(order: Ordering) -> Ordering { match order { @@ -1514,19 +1333,16 @@ pub fn fence(order: Ordering) { } } -macro_rules! impl_Debug { - ($($t:ident)*) => ($( - #[stable(feature = "atomic_debug", since = "1.3.0")] - impl fmt::Debug for $t { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.debug_tuple(stringify!($t)).field(&self.load(Ordering::SeqCst)).finish() - } - } - )*); -} -impl_Debug!{ AtomicUsize AtomicIsize AtomicBool } +#[cfg(any(stage0, target_has_atomic = "ptr"))] +#[stable(feature = "atomic_debug", since = "1.3.0")] +impl fmt::Debug for AtomicBool { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_tuple("AtomicBool").field(&self.load(Ordering::SeqCst)).finish() + } +} +#[cfg(any(stage0, target_has_atomic = "ptr"))] #[stable(feature = "atomic_debug", since = "1.3.0")] impl fmt::Debug for AtomicPtr { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { diff --git a/src/librustc/session/config.rs b/src/librustc/session/config.rs index 1a2c1b9a09528..82237e0abb763 100644 --- a/src/librustc/session/config.rs +++ b/src/librustc/session/config.rs @@ -695,6 +695,7 @@ pub fn default_configuration(sess: &Session) -> ast::CrateConfig { let os = &sess.target.target.target_os; let env = &sess.target.target.target_env; let vendor = &sess.target.target.target_vendor; + let max_atomic_width = sess.target.target.options.max_atomic_width; let fam = if let Some(ref fam) = sess.target.target.options.target_family { intern(fam) @@ -721,6 +722,15 @@ pub fn default_configuration(sess: &Session) -> ast::CrateConfig { if sess.target.target.options.has_elf_tls { ret.push(attr::mk_word_item(InternedString::new("target_thread_local"))); } + for &i in &[8, 16, 32, 64, 128] { + if i <= max_atomic_width { + let s = i.to_string(); + ret.push(mk(InternedString::new("target_has_atomic"), intern(&s))); + if &s == wordsz { + ret.push(mk(InternedString::new("target_has_atomic"), intern("ptr"))); + } + } + } if sess.opts.debug_assertions { ret.push(attr::mk_word_item(InternedString::new("debug_assertions"))); } diff --git a/src/librustc_back/target/aarch64_apple_ios.rs b/src/librustc_back/target/aarch64_apple_ios.rs index 2f0a043f9a7de..481338d1cee56 100644 --- a/src/librustc_back/target/aarch64_apple_ios.rs +++ b/src/librustc_back/target/aarch64_apple_ios.rs @@ -24,6 +24,7 @@ pub fn target() -> Target { options: TargetOptions { features: "+neon,+fp-armv8,+cyclone".to_string(), eliminate_frame_pointer: false, + max_atomic_width: 128, .. opts(Arch::Arm64) }, } diff --git a/src/librustc_back/target/aarch64_linux_android.rs b/src/librustc_back/target/aarch64_linux_android.rs index c4212e702129c..ed17182246466 100644 --- a/src/librustc_back/target/aarch64_linux_android.rs +++ b/src/librustc_back/target/aarch64_linux_android.rs @@ -11,6 +11,8 @@ use target::Target; pub fn target() -> Target { + let mut base = super::android_base::opts(); + base.max_atomic_width = 128; Target { llvm_target: "aarch64-linux-android".to_string(), target_endian: "little".to_string(), @@ -20,6 +22,6 @@ pub fn target() -> Target { target_os: "android".to_string(), target_env: "".to_string(), target_vendor: "unknown".to_string(), - options: super::android_base::opts(), + options: base, } } diff --git a/src/librustc_back/target/aarch64_unknown_linux_gnu.rs b/src/librustc_back/target/aarch64_unknown_linux_gnu.rs index 3bf4e92fb6aab..aec1bae60c812 100644 --- a/src/librustc_back/target/aarch64_unknown_linux_gnu.rs +++ b/src/librustc_back/target/aarch64_unknown_linux_gnu.rs @@ -11,7 +11,8 @@ use target::Target; pub fn target() -> Target { - let base = super::linux_base::opts(); + let mut base = super::linux_base::opts(); + base.max_atomic_width = 128; Target { llvm_target: "aarch64-unknown-linux-gnu".to_string(), target_endian: "little".to_string(), diff --git a/src/librustc_back/target/arm_linux_androideabi.rs b/src/librustc_back/target/arm_linux_androideabi.rs index 5be785951383d..ab662a97dc23c 100644 --- a/src/librustc_back/target/arm_linux_androideabi.rs +++ b/src/librustc_back/target/arm_linux_androideabi.rs @@ -13,6 +13,7 @@ use target::Target; pub fn target() -> Target { let mut base = super::android_base::opts(); base.features = "+v7,+vfp3,+d16".to_string(); + base.max_atomic_width = 64; Target { llvm_target: "arm-linux-androideabi".to_string(), diff --git a/src/librustc_back/target/arm_unknown_linux_gnueabi.rs b/src/librustc_back/target/arm_unknown_linux_gnueabi.rs index 29ab712038911..60c4a7c3c90ce 100644 --- a/src/librustc_back/target/arm_unknown_linux_gnueabi.rs +++ b/src/librustc_back/target/arm_unknown_linux_gnueabi.rs @@ -11,7 +11,8 @@ use target::{Target, TargetOptions}; pub fn target() -> Target { - let base = super::linux_base::opts(); + let mut base = super::linux_base::opts(); + base.max_atomic_width = 64; Target { llvm_target: "arm-unknown-linux-gnueabi".to_string(), target_endian: "little".to_string(), diff --git a/src/librustc_back/target/arm_unknown_linux_gnueabihf.rs b/src/librustc_back/target/arm_unknown_linux_gnueabihf.rs index 8ca7c23eb5537..72128e30641c6 100644 --- a/src/librustc_back/target/arm_unknown_linux_gnueabihf.rs +++ b/src/librustc_back/target/arm_unknown_linux_gnueabihf.rs @@ -11,7 +11,8 @@ use target::{Target, TargetOptions}; pub fn target() -> Target { - let base = super::linux_base::opts(); + let mut base = super::linux_base::opts(); + base.max_atomic_width = 64; Target { llvm_target: "arm-unknown-linux-gnueabihf".to_string(), target_endian: "little".to_string(), diff --git a/src/librustc_back/target/armv7_apple_ios.rs b/src/librustc_back/target/armv7_apple_ios.rs index d131f8b2ef002..a2486a1330a53 100644 --- a/src/librustc_back/target/armv7_apple_ios.rs +++ b/src/librustc_back/target/armv7_apple_ios.rs @@ -23,6 +23,7 @@ pub fn target() -> Target { target_vendor: "apple".to_string(), options: TargetOptions { features: "+v7,+vfp3,+neon".to_string(), + max_atomic_width: 64, .. opts(Arch::Armv7) } } diff --git a/src/librustc_back/target/armv7_unknown_linux_gnueabihf.rs b/src/librustc_back/target/armv7_unknown_linux_gnueabihf.rs index 549da058d518d..7bcca3a3934be 100644 --- a/src/librustc_back/target/armv7_unknown_linux_gnueabihf.rs +++ b/src/librustc_back/target/armv7_unknown_linux_gnueabihf.rs @@ -25,6 +25,7 @@ pub fn target() -> Target { options: TargetOptions { features: "+v7,+vfp3,+neon".to_string(), cpu: "cortex-a8".to_string(), + max_atomic_width: 64, .. base } } diff --git a/src/librustc_back/target/armv7s_apple_ios.rs b/src/librustc_back/target/armv7s_apple_ios.rs index d317589bf3680..e5379aa1b42cd 100644 --- a/src/librustc_back/target/armv7s_apple_ios.rs +++ b/src/librustc_back/target/armv7s_apple_ios.rs @@ -23,6 +23,7 @@ pub fn target() -> Target { target_vendor: "apple".to_string(), options: TargetOptions { features: "+v7,+vfp4,+neon".to_string(), + max_atomic_width: 64, .. opts(Arch::Armv7s) } } diff --git a/src/librustc_back/target/asmjs_unknown_emscripten.rs b/src/librustc_back/target/asmjs_unknown_emscripten.rs index 546f9df605b30..e6200177944bb 100644 --- a/src/librustc_back/target/asmjs_unknown_emscripten.rs +++ b/src/librustc_back/target/asmjs_unknown_emscripten.rs @@ -22,6 +22,7 @@ pub fn target() -> Target { linker_is_gnu: true, allow_asm: false, obj_is_bitcode: true, + max_atomic_width: 32, .. Default::default() }; Target { diff --git a/src/librustc_back/target/i386_apple_ios.rs b/src/librustc_back/target/i386_apple_ios.rs index d149d4bbdc2d6..cf4020eeb5874 100644 --- a/src/librustc_back/target/i386_apple_ios.rs +++ b/src/librustc_back/target/i386_apple_ios.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use target::Target; +use target::{Target, TargetOptions}; use super::apple_ios_base::{opts, Arch}; pub fn target() -> Target { @@ -21,6 +21,9 @@ pub fn target() -> Target { target_os: "ios".to_string(), target_env: "".to_string(), target_vendor: "apple".to_string(), - options: opts(Arch::I386) + options: TargetOptions { + max_atomic_width: 64, + .. opts(Arch::I386) + } } } diff --git a/src/librustc_back/target/i686_apple_darwin.rs b/src/librustc_back/target/i686_apple_darwin.rs index b6e2f4d8e8ac9..302691e9a5922 100644 --- a/src/librustc_back/target/i686_apple_darwin.rs +++ b/src/librustc_back/target/i686_apple_darwin.rs @@ -13,6 +13,7 @@ use target::Target; pub fn target() -> Target { let mut base = super::apple_base::opts(); base.cpu = "yonah".to_string(); + base.max_atomic_width = 64; base.pre_link_args.push("-m32".to_string()); Target { diff --git a/src/librustc_back/target/i686_linux_android.rs b/src/librustc_back/target/i686_linux_android.rs index b338a971ff74c..426be63cf2193 100644 --- a/src/librustc_back/target/i686_linux_android.rs +++ b/src/librustc_back/target/i686_linux_android.rs @@ -13,6 +13,7 @@ use target::Target; pub fn target() -> Target { let mut base = super::android_base::opts(); base.cpu = "pentium4".to_string(); + base.max_atomic_width = 64; Target { llvm_target: "i686-linux-android".to_string(), diff --git a/src/librustc_back/target/i686_pc_windows_gnu.rs b/src/librustc_back/target/i686_pc_windows_gnu.rs index 48203cc74d6a1..c2cc624c9f9e6 100644 --- a/src/librustc_back/target/i686_pc_windows_gnu.rs +++ b/src/librustc_back/target/i686_pc_windows_gnu.rs @@ -13,6 +13,7 @@ use target::Target; pub fn target() -> Target { let mut base = super::windows_base::opts(); base.cpu = "pentium4".to_string(); + base.max_atomic_width = 64; // Mark all dynamic libraries and executables as compatible with the larger 4GiB address // space available to x86 Windows binaries on x86_64. diff --git a/src/librustc_back/target/i686_pc_windows_msvc.rs b/src/librustc_back/target/i686_pc_windows_msvc.rs index 501219ad607cf..8c1bacc280767 100644 --- a/src/librustc_back/target/i686_pc_windows_msvc.rs +++ b/src/librustc_back/target/i686_pc_windows_msvc.rs @@ -13,6 +13,7 @@ use target::Target; pub fn target() -> Target { let mut base = super::windows_msvc_base::opts(); base.cpu = "pentium4".to_string(); + base.max_atomic_width = 64; // Mark all dynamic libraries and executables as compatible with the larger 4GiB address // space available to x86 Windows binaries on x86_64. diff --git a/src/librustc_back/target/i686_unknown_dragonfly.rs b/src/librustc_back/target/i686_unknown_dragonfly.rs index cdbbd5eafddbb..6446ac45f7d6e 100644 --- a/src/librustc_back/target/i686_unknown_dragonfly.rs +++ b/src/librustc_back/target/i686_unknown_dragonfly.rs @@ -13,6 +13,7 @@ use target::Target; pub fn target() -> Target { let mut base = super::dragonfly_base::opts(); base.cpu = "pentium4".to_string(); + base.max_atomic_width = 64; base.pre_link_args.push("-m32".to_string()); Target { diff --git a/src/librustc_back/target/i686_unknown_freebsd.rs b/src/librustc_back/target/i686_unknown_freebsd.rs index fadedc24149c7..a7903d5db6413 100644 --- a/src/librustc_back/target/i686_unknown_freebsd.rs +++ b/src/librustc_back/target/i686_unknown_freebsd.rs @@ -13,6 +13,7 @@ use target::Target; pub fn target() -> Target { let mut base = super::freebsd_base::opts(); base.cpu = "pentium4".to_string(); + base.max_atomic_width = 64; base.pre_link_args.push("-m32".to_string()); Target { diff --git a/src/librustc_back/target/i686_unknown_linux_gnu.rs b/src/librustc_back/target/i686_unknown_linux_gnu.rs index a1f3ab769070f..7813d5570762d 100644 --- a/src/librustc_back/target/i686_unknown_linux_gnu.rs +++ b/src/librustc_back/target/i686_unknown_linux_gnu.rs @@ -13,6 +13,7 @@ use target::Target; pub fn target() -> Target { let mut base = super::linux_base::opts(); base.cpu = "pentium4".to_string(); + base.max_atomic_width = 64; base.pre_link_args.push("-m32".to_string()); Target { diff --git a/src/librustc_back/target/i686_unknown_linux_musl.rs b/src/librustc_back/target/i686_unknown_linux_musl.rs index 95fc0abece01b..5274429583742 100644 --- a/src/librustc_back/target/i686_unknown_linux_musl.rs +++ b/src/librustc_back/target/i686_unknown_linux_musl.rs @@ -13,6 +13,7 @@ use target::Target; pub fn target() -> Target { let mut base = super::linux_musl_base::opts(); base.cpu = "pentium4".to_string(); + base.max_atomic_width = 64; base.pre_link_args.push("-m32".to_string()); base.pre_link_args.push("-Wl,-melf_i386".to_string()); diff --git a/src/librustc_back/target/le32_unknown_nacl.rs b/src/librustc_back/target/le32_unknown_nacl.rs index 472b73302a358..f4f0262d476da 100644 --- a/src/librustc_back/target/le32_unknown_nacl.rs +++ b/src/librustc_back/target/le32_unknown_nacl.rs @@ -25,6 +25,7 @@ pub fn target() -> Target { no_compiler_rt: false, linker_is_gnu: true, allow_asm: false, + max_atomic_width: 32, .. Default::default() }; Target { diff --git a/src/librustc_back/target/mips_unknown_linux_gnu.rs b/src/librustc_back/target/mips_unknown_linux_gnu.rs index 863f5ceab0ae0..794e4d4996c94 100644 --- a/src/librustc_back/target/mips_unknown_linux_gnu.rs +++ b/src/librustc_back/target/mips_unknown_linux_gnu.rs @@ -23,6 +23,7 @@ pub fn target() -> Target { options: TargetOptions { cpu: "mips32r2".to_string(), features: "+mips32r2,+soft-float".to_string(), + max_atomic_width: 32, ..super::linux_base::opts() }, } diff --git a/src/librustc_back/target/mips_unknown_linux_musl.rs b/src/librustc_back/target/mips_unknown_linux_musl.rs index ac0fde5449ff4..35366659d581f 100644 --- a/src/librustc_back/target/mips_unknown_linux_musl.rs +++ b/src/librustc_back/target/mips_unknown_linux_musl.rs @@ -23,6 +23,7 @@ pub fn target() -> Target { options: TargetOptions { cpu: "mips32r2".to_string(), features: "+mips32r2,+soft-float".to_string(), + max_atomic_width: 32, ..super::linux_base::opts() } } diff --git a/src/librustc_back/target/mipsel_unknown_linux_gnu.rs b/src/librustc_back/target/mipsel_unknown_linux_gnu.rs index ff33effa3e79b..ac1536b3d009b 100644 --- a/src/librustc_back/target/mipsel_unknown_linux_gnu.rs +++ b/src/librustc_back/target/mipsel_unknown_linux_gnu.rs @@ -24,6 +24,7 @@ pub fn target() -> Target { options: TargetOptions { cpu: "mips32".to_string(), features: "+mips32".to_string(), + max_atomic_width: 32, ..super::linux_base::opts() }, } diff --git a/src/librustc_back/target/mipsel_unknown_linux_musl.rs b/src/librustc_back/target/mipsel_unknown_linux_musl.rs index d9fb14050365d..a9ea52c427862 100644 --- a/src/librustc_back/target/mipsel_unknown_linux_musl.rs +++ b/src/librustc_back/target/mipsel_unknown_linux_musl.rs @@ -23,6 +23,7 @@ pub fn target() -> Target { options: TargetOptions { cpu: "mips32".to_string(), features: "+mips32".to_string(), + max_atomic_width: 32, ..super::linux_base::opts() } } diff --git a/src/librustc_back/target/mod.rs b/src/librustc_back/target/mod.rs index 30d0b47029dc3..2163a8a1689b6 100644 --- a/src/librustc_back/target/mod.rs +++ b/src/librustc_back/target/mod.rs @@ -292,6 +292,10 @@ pub struct TargetOptions { // If we give emcc .o files that are actually .bc files it // will 'just work'. pub obj_is_bitcode: bool, + + /// Maximum integer size in bits that this target can perform atomic + /// operations on. + pub max_atomic_width: u64, } impl Default for TargetOptions { @@ -340,6 +344,7 @@ impl Default for TargetOptions { allow_asm: true, has_elf_tls: false, obj_is_bitcode: false, + max_atomic_width: 0, } } } @@ -392,6 +397,9 @@ impl Target { options: Default::default(), }; + // Default max-atomic-width to target-pointer-width + base.options.max_atomic_width = base.target_pointer_width.parse().unwrap(); + macro_rules! key { ($key_name:ident) => ( { let name = (stringify!($key_name)).replace("_", "-"); @@ -404,6 +412,12 @@ impl Target { .map(|o| o.as_boolean() .map(|s| base.options.$key_name = s)); } ); + ($key_name:ident, u64) => ( { + let name = (stringify!($key_name)).replace("_", "-"); + obj.find(&name[..]) + .map(|o| o.as_u64() + .map(|s| base.options.$key_name = s)); + } ); ($key_name:ident, list) => ( { let name = (stringify!($key_name)).replace("_", "-"); obj.find(&name[..]).map(|o| o.as_array() @@ -451,6 +465,7 @@ impl Target { key!(archive_format); key!(allow_asm, bool); key!(custom_unwind_resume, bool); + key!(max_atomic_width, u64); base } diff --git a/src/librustc_back/target/powerpc64_unknown_linux_gnu.rs b/src/librustc_back/target/powerpc64_unknown_linux_gnu.rs index fe7daaec1cdeb..be4be8e6fc964 100644 --- a/src/librustc_back/target/powerpc64_unknown_linux_gnu.rs +++ b/src/librustc_back/target/powerpc64_unknown_linux_gnu.rs @@ -14,6 +14,7 @@ pub fn target() -> Target { let mut base = super::linux_base::opts(); base.cpu = "ppc64".to_string(); base.pre_link_args.push("-m64".to_string()); + base.max_atomic_width = 64; Target { llvm_target: "powerpc64-unknown-linux-gnu".to_string(), diff --git a/src/librustc_back/target/powerpc64le_unknown_linux_gnu.rs b/src/librustc_back/target/powerpc64le_unknown_linux_gnu.rs index 4aab2b1802c98..b0a81ce7ec501 100644 --- a/src/librustc_back/target/powerpc64le_unknown_linux_gnu.rs +++ b/src/librustc_back/target/powerpc64le_unknown_linux_gnu.rs @@ -14,6 +14,7 @@ pub fn target() -> Target { let mut base = super::linux_base::opts(); base.cpu = "ppc64le".to_string(); base.pre_link_args.push("-m64".to_string()); + base.max_atomic_width = 64; Target { llvm_target: "powerpc64le-unknown-linux-gnu".to_string(), diff --git a/src/librustc_back/target/powerpc_unknown_linux_gnu.rs b/src/librustc_back/target/powerpc_unknown_linux_gnu.rs index 1df36442c0688..aea57dc4b7f95 100644 --- a/src/librustc_back/target/powerpc_unknown_linux_gnu.rs +++ b/src/librustc_back/target/powerpc_unknown_linux_gnu.rs @@ -13,6 +13,7 @@ use target::Target; pub fn target() -> Target { let mut base = super::linux_base::opts(); base.pre_link_args.push("-m32".to_string()); + base.max_atomic_width = 32; Target { llvm_target: "powerpc-unknown-linux-gnu".to_string(), diff --git a/src/librustc_back/target/x86_64_apple_darwin.rs b/src/librustc_back/target/x86_64_apple_darwin.rs index c8b5dd0eccc5c..5542c9120a402 100644 --- a/src/librustc_back/target/x86_64_apple_darwin.rs +++ b/src/librustc_back/target/x86_64_apple_darwin.rs @@ -13,6 +13,7 @@ use target::Target; pub fn target() -> Target { let mut base = super::apple_base::opts(); base.cpu = "core2".to_string(); + base.max_atomic_width = 128; // core2 support cmpxchg16b base.eliminate_frame_pointer = false; base.pre_link_args.push("-m64".to_string()); diff --git a/src/librustc_back/target/x86_64_apple_ios.rs b/src/librustc_back/target/x86_64_apple_ios.rs index d038e88f2b4fd..8638241f8610b 100644 --- a/src/librustc_back/target/x86_64_apple_ios.rs +++ b/src/librustc_back/target/x86_64_apple_ios.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use target::Target; +use target::{Target, TargetOptions}; use super::apple_ios_base::{opts, Arch}; pub fn target() -> Target { @@ -21,6 +21,9 @@ pub fn target() -> Target { target_os: "ios".to_string(), target_env: "".to_string(), target_vendor: "apple".to_string(), - options: opts(Arch::X86_64) + options: TargetOptions { + max_atomic_width: 64, + .. opts(Arch::X86_64) + } } } diff --git a/src/librustc_back/target/x86_64_pc_windows_gnu.rs b/src/librustc_back/target/x86_64_pc_windows_gnu.rs index f0a09ae71efeb..e243054d0230e 100644 --- a/src/librustc_back/target/x86_64_pc_windows_gnu.rs +++ b/src/librustc_back/target/x86_64_pc_windows_gnu.rs @@ -14,6 +14,7 @@ pub fn target() -> Target { let mut base = super::windows_base::opts(); base.cpu = "x86-64".to_string(); base.pre_link_args.push("-m64".to_string()); + base.max_atomic_width = 64; Target { llvm_target: "x86_64-pc-windows-gnu".to_string(), diff --git a/src/librustc_back/target/x86_64_pc_windows_msvc.rs b/src/librustc_back/target/x86_64_pc_windows_msvc.rs index b3fbd6ef05170..a23a807a0257e 100644 --- a/src/librustc_back/target/x86_64_pc_windows_msvc.rs +++ b/src/librustc_back/target/x86_64_pc_windows_msvc.rs @@ -13,6 +13,7 @@ use target::Target; pub fn target() -> Target { let mut base = super::windows_msvc_base::opts(); base.cpu = "x86-64".to_string(); + base.max_atomic_width = 64; Target { llvm_target: "x86_64-pc-windows-msvc".to_string(), diff --git a/src/librustc_back/target/x86_64_rumprun_netbsd.rs b/src/librustc_back/target/x86_64_rumprun_netbsd.rs index 652159d10fda2..af5d21c4d93e4 100644 --- a/src/librustc_back/target/x86_64_rumprun_netbsd.rs +++ b/src/librustc_back/target/x86_64_rumprun_netbsd.rs @@ -15,6 +15,7 @@ pub fn target() -> Target { base.pre_link_args.push("-m64".to_string()); base.linker = "x86_64-rumprun-netbsd-gcc".to_string(); base.ar = "x86_64-rumprun-netbsd-ar".to_string(); + base.max_atomic_width = 64; base.dynamic_linking = false; base.has_rpath = false; diff --git a/src/librustc_back/target/x86_64_sun_solaris.rs b/src/librustc_back/target/x86_64_sun_solaris.rs index 5aa08ea9c8ca9..8f2c905cf2ee4 100644 --- a/src/librustc_back/target/x86_64_sun_solaris.rs +++ b/src/librustc_back/target/x86_64_sun_solaris.rs @@ -14,6 +14,7 @@ pub fn target() -> Target { let mut base = super::solaris_base::opts(); base.pre_link_args.push("-m64".to_string()); base.cpu = "x86-64".to_string(); + base.max_atomic_width = 64; Target { llvm_target: "x86_64-pc-solaris".to_string(), diff --git a/src/librustc_back/target/x86_64_unknown_bitrig.rs b/src/librustc_back/target/x86_64_unknown_bitrig.rs index e8b95ed80d9e6..87753da540a36 100644 --- a/src/librustc_back/target/x86_64_unknown_bitrig.rs +++ b/src/librustc_back/target/x86_64_unknown_bitrig.rs @@ -12,6 +12,7 @@ use target::Target; pub fn target() -> Target { let mut base = super::bitrig_base::opts(); + base.max_atomic_width = 64; base.pre_link_args.push("-m64".to_string()); Target { diff --git a/src/librustc_back/target/x86_64_unknown_dragonfly.rs b/src/librustc_back/target/x86_64_unknown_dragonfly.rs index 3fa46c31a5eb1..2535071f3089c 100644 --- a/src/librustc_back/target/x86_64_unknown_dragonfly.rs +++ b/src/librustc_back/target/x86_64_unknown_dragonfly.rs @@ -13,6 +13,7 @@ use target::Target; pub fn target() -> Target { let mut base = super::dragonfly_base::opts(); base.cpu = "x86-64".to_string(); + base.max_atomic_width = 64; base.pre_link_args.push("-m64".to_string()); Target { diff --git a/src/librustc_back/target/x86_64_unknown_freebsd.rs b/src/librustc_back/target/x86_64_unknown_freebsd.rs index d345a32179423..d3ad0578aeb6e 100644 --- a/src/librustc_back/target/x86_64_unknown_freebsd.rs +++ b/src/librustc_back/target/x86_64_unknown_freebsd.rs @@ -13,6 +13,7 @@ use target::Target; pub fn target() -> Target { let mut base = super::freebsd_base::opts(); base.cpu = "x86-64".to_string(); + base.max_atomic_width = 64; base.pre_link_args.push("-m64".to_string()); Target { diff --git a/src/librustc_back/target/x86_64_unknown_linux_gnu.rs b/src/librustc_back/target/x86_64_unknown_linux_gnu.rs index 69e333a135002..7908e0d581ba1 100644 --- a/src/librustc_back/target/x86_64_unknown_linux_gnu.rs +++ b/src/librustc_back/target/x86_64_unknown_linux_gnu.rs @@ -13,6 +13,7 @@ use target::Target; pub fn target() -> Target { let mut base = super::linux_base::opts(); base.cpu = "x86-64".to_string(); + base.max_atomic_width = 64; base.pre_link_args.push("-m64".to_string()); Target { diff --git a/src/librustc_back/target/x86_64_unknown_linux_musl.rs b/src/librustc_back/target/x86_64_unknown_linux_musl.rs index f8f85e5c06609..3301e0e0dc93f 100644 --- a/src/librustc_back/target/x86_64_unknown_linux_musl.rs +++ b/src/librustc_back/target/x86_64_unknown_linux_musl.rs @@ -13,6 +13,7 @@ use target::Target; pub fn target() -> Target { let mut base = super::linux_musl_base::opts(); base.cpu = "x86-64".to_string(); + base.max_atomic_width = 64; base.pre_link_args.push("-m64".to_string()); Target { diff --git a/src/librustc_back/target/x86_64_unknown_netbsd.rs b/src/librustc_back/target/x86_64_unknown_netbsd.rs index 74bf71891199a..7e6d1b78469e2 100644 --- a/src/librustc_back/target/x86_64_unknown_netbsd.rs +++ b/src/librustc_back/target/x86_64_unknown_netbsd.rs @@ -12,6 +12,7 @@ use target::Target; pub fn target() -> Target { let mut base = super::netbsd_base::opts(); + base.max_atomic_width = 64; base.pre_link_args.push("-m64".to_string()); Target { diff --git a/src/librustc_back/target/x86_64_unknown_openbsd.rs b/src/librustc_back/target/x86_64_unknown_openbsd.rs index 521de5373d0b1..823b0994b0a00 100644 --- a/src/librustc_back/target/x86_64_unknown_openbsd.rs +++ b/src/librustc_back/target/x86_64_unknown_openbsd.rs @@ -13,6 +13,7 @@ use target::Target; pub fn target() -> Target { let mut base = super::openbsd_base::opts(); base.cpu = "x86-64".to_string(); + base.max_atomic_width = 64; base.pre_link_args.push("-m64".to_string()); Target { diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs index 7f01821b00433..cf797b85f5486 100644 --- a/src/libsyntax/feature_gate.rs +++ b/src/libsyntax/feature_gate.rs @@ -268,8 +268,11 @@ declare_features! ( // pub(restricted) visibilities (RFC 1422) (active, pub_restricted, "1.9.0", Some(32409)), - // Allow Drop types in statics/const functions (RFC 1440) - (active, drop_types_in_const, "1.9.0", Some(33156)) + // Allow Drop types in statics/const functions (RFC 1440) + (active, drop_types_in_const, "1.9.0", Some(33156)), + + // Allows cfg(target_has_atomic = "..."). + (active, cfg_target_has_atomic, "1.9.0", Some(32976)) ); declare_features! ( @@ -577,6 +580,7 @@ const GATED_CFGS: &'static [(&'static str, &'static str, fn(&Features) -> bool)] ("target_feature", "cfg_target_feature", cfg_fn!(cfg_target_feature)), ("target_vendor", "cfg_target_vendor", cfg_fn!(cfg_target_vendor)), ("target_thread_local", "cfg_target_thread_local", cfg_fn!(cfg_target_thread_local)), + ("target_has_atomic", "cfg_target_has_atomic", cfg_fn!(cfg_target_has_atomic)), ]; #[derive(Debug, Eq, PartialEq)] diff --git a/src/test/run-make/atomic-lock-free/Makefile b/src/test/run-make/atomic-lock-free/Makefile new file mode 100644 index 0000000000000..78e7bb231372f --- /dev/null +++ b/src/test/run-make/atomic-lock-free/Makefile @@ -0,0 +1,30 @@ +-include ../tools.mk + +# This tests ensure that atomic types are never lowered into runtime library calls that are not +# guaranteed to be lock-free. + +all: +ifeq ($(UNAME),Linux) + $(RUSTC) --target=i686-unknown-linux-gnu atomic_lock_free.rs + nm "$(TMPDIR)/libatomic_lock_free.rlib" | grep -vq __atomic_fetch_add + $(RUSTC) --target=x86_64-unknown-linux-gnu atomic_lock_free.rs + nm "$(TMPDIR)/libatomic_lock_free.rlib" | grep -vq __atomic_fetch_add + $(RUSTC) --target=arm-unknown-linux-gnueabi atomic_lock_free.rs + nm "$(TMPDIR)/libatomic_lock_free.rlib" | grep -vq __atomic_fetch_add + $(RUSTC) --target=arm-unknown-linux-gnueabihf atomic_lock_free.rs + nm "$(TMPDIR)/libatomic_lock_free.rlib" | grep -vq __atomic_fetch_add + $(RUSTC) --target=armv7-unknown-linux-gnueabihf atomic_lock_free.rs + nm "$(TMPDIR)/libatomic_lock_free.rlib" | grep -vq __atomic_fetch_add + $(RUSTC) --target=aarch64-unknown-linux-gnu atomic_lock_free.rs + nm "$(TMPDIR)/libatomic_lock_free.rlib" | grep -vq __atomic_fetch_add + $(RUSTC) --target=mips-unknown-linux-gnu atomic_lock_free.rs + nm "$(TMPDIR)/libatomic_lock_free.rlib" | grep -vq __atomic_fetch_add + $(RUSTC) --target=mipsel-unknown-linux-gnu atomic_lock_free.rs + nm "$(TMPDIR)/libatomic_lock_free.rlib" | grep -vq __atomic_fetch_add + $(RUSTC) --target=powerpc-unknown-linux-gnu atomic_lock_free.rs + nm "$(TMPDIR)/libatomic_lock_free.rlib" | grep -vq __atomic_fetch_add + $(RUSTC) --target=powerpc64-unknown-linux-gnu atomic_lock_free.rs + nm "$(TMPDIR)/libatomic_lock_free.rlib" | grep -vq __atomic_fetch_add + $(RUSTC) --target=powerpc64le-unknown-linux-gnu atomic_lock_free.rs + nm "$(TMPDIR)/libatomic_lock_free.rlib" | grep -vq __atomic_fetch_add +endif diff --git a/src/test/run-make/atomic-lock-free/atomic_lock_free.rs b/src/test/run-make/atomic-lock-free/atomic_lock_free.rs new file mode 100644 index 0000000000000..8731cd960f33c --- /dev/null +++ b/src/test/run-make/atomic-lock-free/atomic_lock_free.rs @@ -0,0 +1,62 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(cfg_target_has_atomic, no_core, intrinsics, lang_items)] +#![crate_type="rlib"] +#![no_core] + +extern "rust-intrinsic" { + fn atomic_xadd(dst: *mut T, src: T) -> T; +} + +#[lang = "sized"] +trait Sized {} + +#[cfg(target_has_atomic = "8")] +pub unsafe fn atomic_u8(x: *mut u8) { + atomic_xadd(x, 1); + atomic_xadd(x, 1); +} +#[cfg(target_has_atomic = "8")] +pub unsafe fn atomic_i8(x: *mut i8) { + atomic_xadd(x, 1); +} +#[cfg(target_has_atomic = "16")] +pub unsafe fn atomic_u16(x: *mut u16) { + atomic_xadd(x, 1); +} +#[cfg(target_has_atomic = "16")] +pub unsafe fn atomic_i16(x: *mut i16) { + atomic_xadd(x, 1); +} +#[cfg(target_has_atomic = "32")] +pub unsafe fn atomic_u32(x: *mut u32) { + atomic_xadd(x, 1); +} +#[cfg(target_has_atomic = "32")] +pub unsafe fn atomic_i32(x: *mut i32) { + atomic_xadd(x, 1); +} +#[cfg(target_has_atomic = "64")] +pub unsafe fn atomic_u64(x: *mut u64) { + atomic_xadd(x, 1); +} +#[cfg(target_has_atomic = "64")] +pub unsafe fn atomic_i64(x: *mut i64) { + atomic_xadd(x, 1); +} +#[cfg(target_has_atomic = "ptr")] +pub unsafe fn atomic_usize(x: *mut usize) { + atomic_xadd(x, 1); +} +#[cfg(target_has_atomic = "ptr")] +pub unsafe fn atomic_isize(x: *mut isize) { + atomic_xadd(x, 1); +}