From 8f5c66c6a30cb770da3da953fa911b6c7a5769bb Mon Sep 17 00:00:00 2001 From: Stein Somers Date: Thu, 23 Apr 2020 22:25:50 +0200 Subject: [PATCH 01/16] Introduce BTreeMap benches of iter itself --- src/liballoc/benches/btree/map.rs | 118 ++++++++++++++++++------------ 1 file changed, 72 insertions(+), 46 deletions(-) diff --git a/src/liballoc/benches/btree/map.rs b/src/liballoc/benches/btree/map.rs index 83cdebf0e3f4a..38d19c59ad186 100644 --- a/src/liballoc/benches/btree/map.rs +++ b/src/liballoc/benches/btree/map.rs @@ -1,6 +1,6 @@ use std::collections::BTreeMap; use std::iter::Iterator; -use std::ops::Bound::{Excluded, Unbounded}; +use std::ops::RangeBounds; use std::vec::Vec; use rand::{seq::SliceRandom, thread_rng, Rng}; @@ -117,7 +117,7 @@ map_find_rand_bench! {find_rand_10_000, 10_000, BTreeMap} map_find_seq_bench! {find_seq_100, 100, BTreeMap} map_find_seq_bench! {find_seq_10_000, 10_000, BTreeMap} -fn bench_iter(b: &mut Bencher, size: i32) { +fn bench_iteration(b: &mut Bencher, size: i32) { let mut map = BTreeMap::::new(); let mut rng = thread_rng(); @@ -133,21 +133,21 @@ fn bench_iter(b: &mut Bencher, size: i32) { } #[bench] -pub fn iter_20(b: &mut Bencher) { - bench_iter(b, 20); +pub fn iteration_20(b: &mut Bencher) { + bench_iteration(b, 20); } #[bench] -pub fn iter_1000(b: &mut Bencher) { - bench_iter(b, 1000); +pub fn iteration_1000(b: &mut Bencher) { + bench_iteration(b, 1000); } #[bench] -pub fn iter_100000(b: &mut Bencher) { - bench_iter(b, 100000); +pub fn iteration_100000(b: &mut Bencher) { + bench_iteration(b, 100000); } -fn bench_iter_mut(b: &mut Bencher, size: i32) { +fn bench_iteration_mut(b: &mut Bencher, size: i32) { let mut map = BTreeMap::::new(); let mut rng = thread_rng(); @@ -163,18 +163,18 @@ fn bench_iter_mut(b: &mut Bencher, size: i32) { } #[bench] -pub fn iter_mut_20(b: &mut Bencher) { - bench_iter_mut(b, 20); +pub fn iteration_mut_20(b: &mut Bencher) { + bench_iteration_mut(b, 20); } #[bench] -pub fn iter_mut_1000(b: &mut Bencher) { - bench_iter_mut(b, 1000); +pub fn iteration_mut_1000(b: &mut Bencher) { + bench_iteration_mut(b, 1000); } #[bench] -pub fn iter_mut_100000(b: &mut Bencher) { - bench_iter_mut(b, 100000); +pub fn iteration_mut_100000(b: &mut Bencher) { + bench_iteration_mut(b, 100000); } fn bench_first_and_last(b: &mut Bencher, size: i32) { @@ -202,57 +202,83 @@ pub fn first_and_last_10k(b: &mut Bencher) { bench_first_and_last(b, 10_000); } -#[bench] -pub fn range_excluded_excluded(b: &mut Bencher) { - let size = 144; - let map: BTreeMap<_, _> = (0..size).map(|i| (i, i)).collect(); +const BENCH_RANGE_SIZE: i32 = 145; +const BENCH_RANGE_COUNT: i32 = BENCH_RANGE_SIZE * (BENCH_RANGE_SIZE - 1) / 2; + +fn bench_range(b: &mut Bencher, f: F) +where + F: Fn(i32, i32) -> R, + R: RangeBounds, +{ + let map: BTreeMap<_, _> = (0..BENCH_RANGE_SIZE).map(|i| (i, i)).collect(); b.iter(|| { - for first in 0..size { - for last in first + 1..size { - black_box(map.range((Excluded(first), Excluded(last)))); + let mut c = 0; + for i in 0..BENCH_RANGE_SIZE { + for j in i + 1..BENCH_RANGE_SIZE { + black_box(map.range(f(i, j))); + c += 1; } } + debug_assert_eq!(c, BENCH_RANGE_COUNT); }); } #[bench] -pub fn range_excluded_unbounded(b: &mut Bencher) { - let size = 144; - let map: BTreeMap<_, _> = (0..size).map(|i| (i, i)).collect(); - b.iter(|| { - for first in 0..size { - black_box(map.range((Excluded(first), Unbounded))); - } - }); +pub fn range_included_excluded(b: &mut Bencher) { + bench_range(b, |i, j| i..j); } #[bench] pub fn range_included_included(b: &mut Bencher) { - let size = 144; - let map: BTreeMap<_, _> = (0..size).map(|i| (i, i)).collect(); - b.iter(|| { - for first in 0..size { - for last in first..size { - black_box(map.range(first..=last)); - } - } - }); + bench_range(b, |i, j| i..=j); } #[bench] pub fn range_included_unbounded(b: &mut Bencher) { - let size = 144; + bench_range(b, |i, _| i..); +} + +#[bench] +pub fn range_unbounded_unbounded(b: &mut Bencher) { + bench_range(b, |_, _| ..); +} + +fn bench_iter(b: &mut Bencher, repeats: i32, size: i32) { let map: BTreeMap<_, _> = (0..size).map(|i| (i, i)).collect(); b.iter(|| { - for first in 0..size { - black_box(map.range(first..)); + for _ in 0..repeats { + black_box(map.iter()); } }); } +/// Contrast range_unbounded_unbounded with `iter()`. #[bench] -pub fn range_unbounded_unbounded(b: &mut Bencher) { - let size = 144; - let map: BTreeMap<_, _> = (0..size).map(|i| (i, i)).collect(); - b.iter(|| map.range(..)); +pub fn range_unbounded_vs_iter(b: &mut Bencher) { + bench_iter(b, BENCH_RANGE_COUNT, BENCH_RANGE_SIZE); +} + +#[bench] +pub fn iter_0(b: &mut Bencher) { + bench_iter(b, 1_000, 0); +} + +#[bench] +pub fn iter_1(b: &mut Bencher) { + bench_iter(b, 1_000, 1); +} + +#[bench] +pub fn iter_100(b: &mut Bencher) { + bench_iter(b, 1_000, 100); +} + +#[bench] +pub fn iter_10k(b: &mut Bencher) { + bench_iter(b, 1_000, 10_000); +} + +#[bench] +pub fn iter_1m(b: &mut Bencher) { + bench_iter(b, 1_000, 1_000_000); } From 873022797ae7f09872738c7367d8d658a1a34ad5 Mon Sep 17 00:00:00 2001 From: Stein Somers Date: Wed, 22 Apr 2020 13:06:24 +0200 Subject: [PATCH 02/16] Speed up BTreeMap iteration by intertwined descend to the initial leaf edges --- src/liballoc/collections/btree/map.rs | 76 +++++++++++++++++---------- 1 file changed, 47 insertions(+), 29 deletions(-) diff --git a/src/liballoc/collections/btree/map.rs b/src/liballoc/collections/btree/map.rs index 91d93a1be1c98..b3158c97bfc51 100644 --- a/src/liballoc/collections/btree/map.rs +++ b/src/liballoc/collections/btree/map.rs @@ -1545,16 +1545,10 @@ impl IntoIterator for BTreeMap { fn into_iter(self) -> IntoIter { let mut me = ManuallyDrop::new(self); - if let Some(root) = me.root.as_mut() { - let root1 = unsafe { ptr::read(root).into_ref() }; - let root2 = unsafe { ptr::read(root).into_ref() }; - let len = me.length; - - IntoIter { - front: Some(root1.first_leaf_edge()), - back: Some(root2.last_leaf_edge()), - length: len, - } + if let Some(root) = me.root.take() { + let (f, b) = full_range_search(root.into_ref()); + + IntoIter { front: Some(f), back: Some(b), length: me.length } } else { IntoIter { front: None, back: None, length: 0 } } @@ -2042,6 +2036,7 @@ where } } +/// Finds the leaf edges delimiting a specified range in or underneath a node. fn range_search>( root1: NodeRef, root2: NodeRef, @@ -2126,6 +2121,33 @@ where } } +/// Equivalent to `range_search(k, v, ..)` without the `Ord` bound. +fn full_range_search( + root: NodeRef, +) -> ( + Handle, marker::Edge>, + Handle, marker::Edge>, +) { + // We duplicate the root NodeRef here -- we will never access it in a way + // that overlaps references obtained from the root. + let mut min_node = unsafe { ptr::read(&root) }; + let mut max_node = root; + loop { + let front = min_node.first_edge(); + let back = max_node.last_edge(); + match (front.force(), back.force()) { + (Leaf(f), Leaf(b)) => { + return (f, b); + } + (Internal(min_int), Internal(max_int)) => { + min_node = min_int.descend(); + max_node = max_int.descend(); + } + _ => unreachable!("BTreeMap has different depths"), + }; + } +} + impl BTreeMap { /// Gets an iterator over the entries of the map, sorted by key. /// @@ -2150,12 +2172,12 @@ impl BTreeMap { /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub fn iter(&self) -> Iter<'_, K, V> { - Iter { - range: Range { - front: self.root.as_ref().map(|r| r.as_ref().first_leaf_edge()), - back: self.root.as_ref().map(|r| r.as_ref().last_leaf_edge()), - }, - length: self.length, + if let Some(root) = &self.root { + let (f, b) = full_range_search(root.as_ref()); + + Iter { range: Range { front: Some(f), back: Some(b) }, length: self.length } + } else { + Iter { range: Range { front: None, back: None }, length: 0 } } } @@ -2182,19 +2204,15 @@ impl BTreeMap { /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub fn iter_mut(&mut self) -> IterMut<'_, K, V> { - IterMut { - range: if let Some(root) = &mut self.root { - let root1 = root.as_mut(); - let root2 = unsafe { ptr::read(&root1) }; - RangeMut { - front: Some(root1.first_leaf_edge()), - back: Some(root2.last_leaf_edge()), - _marker: PhantomData, - } - } else { - RangeMut { front: None, back: None, _marker: PhantomData } - }, - length: self.length, + if let Some(root) = &mut self.root { + let (f, b) = full_range_search(root.as_mut()); + + IterMut { + range: RangeMut { front: Some(f), back: Some(b), _marker: PhantomData }, + length: self.length, + } + } else { + IterMut { range: RangeMut { front: None, back: None, _marker: PhantomData }, length: 0 } } } From 4d3cf5bd9b6c48e5116fb8d9747a7a2dbde09d1d Mon Sep 17 00:00:00 2001 From: Stefan Lankes Date: Sun, 26 Apr 2020 19:07:13 +0200 Subject: [PATCH 03/16] use new interface to create threads on HermitCore - the new interface allows to define the stack size --- Cargo.lock | 4 ++-- src/libstd/Cargo.toml | 2 +- src/libstd/sys/hermit/thread.rs | 13 ++++++------- 3 files changed, 9 insertions(+), 10 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 816d65cb2ce23..0f9f2c6b4fb07 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1366,9 +1366,9 @@ dependencies = [ [[package]] name = "hermit-abi" -version = "0.1.10" +version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "725cf19794cf90aa94e65050cb4191ff5d8fa87a498383774c47b332e3af952e" +checksum = "61565ff7aaace3525556587bd2dc31d4a07071957be715e63ce7b1eccf51a8f4" dependencies = [ "compiler_builtins", "libc", diff --git a/src/libstd/Cargo.toml b/src/libstd/Cargo.toml index ceb39c01c6723..923d5fa8cacdb 100644 --- a/src/libstd/Cargo.toml +++ b/src/libstd/Cargo.toml @@ -41,7 +41,7 @@ dlmalloc = { version = "0.1", features = ['rustc-dep-of-std'] } fortanix-sgx-abi = { version = "0.3.2", features = ['rustc-dep-of-std'] } [target.'cfg(all(any(target_arch = "x86_64", target_arch = "aarch64"), target_os = "hermit"))'.dependencies] -hermit-abi = { version = "0.1.10", features = ['rustc-dep-of-std'] } +hermit-abi = { version = "0.1.12", features = ['rustc-dep-of-std'] } [target.wasm32-wasi.dependencies] wasi = { version = "0.9.0", features = ['rustc-dep-of-std'], default-features = false } diff --git a/src/libstd/sys/hermit/thread.rs b/src/libstd/sys/hermit/thread.rs index 7e3fb4c6d2052..55924ee0e6885 100644 --- a/src/libstd/sys/hermit/thread.rs +++ b/src/libstd/sys/hermit/thread.rs @@ -16,25 +16,24 @@ pub struct Thread { unsafe impl Send for Thread {} unsafe impl Sync for Thread {} -pub const DEFAULT_MIN_STACK_SIZE: usize = 262144; +pub const DEFAULT_MIN_STACK_SIZE: usize = 1_048_576; impl Thread { pub unsafe fn new_with_coreid( - _stack: usize, + stack: usize, p: Box, core_id: isize, ) -> io::Result { let p = Box::into_raw(box p); - let mut tid: Tid = u32::MAX; - let ret = abi::spawn( - &mut tid as *mut Tid, + let tid = abi::spawn2( thread_start, - &*p as *const _ as *const u8 as usize, + p as usize, abi::Priority::into(abi::NORMAL_PRIO), + stack, core_id, ); - return if ret != 0 { + return if tid == 0 { // The thread failed to start and as a result p was not consumed. Therefore, it is // safe to reconstruct the box so that it gets deallocated. drop(Box::from_raw(p)); From 2c43746758f4bc60bd4dda30a761cf48d1f60635 Mon Sep 17 00:00:00 2001 From: Stefan Lankes Date: Mon, 27 Apr 2020 13:18:33 +0200 Subject: [PATCH 04/16] use nicer code style to define DEFAULT_MIN_STACK_SIZE --- src/libstd/sys/hermit/thread.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libstd/sys/hermit/thread.rs b/src/libstd/sys/hermit/thread.rs index 55924ee0e6885..e11afed668728 100644 --- a/src/libstd/sys/hermit/thread.rs +++ b/src/libstd/sys/hermit/thread.rs @@ -16,7 +16,7 @@ pub struct Thread { unsafe impl Send for Thread {} unsafe impl Sync for Thread {} -pub const DEFAULT_MIN_STACK_SIZE: usize = 1_048_576; +pub const DEFAULT_MIN_STACK_SIZE: usize = 1 << 20; impl Thread { pub unsafe fn new_with_coreid( From 19e5da902bf6ade2c0558383051215459754b73d Mon Sep 17 00:00:00 2001 From: Hanif Bin Ariffin Date: Thu, 30 Apr 2020 20:41:51 -0400 Subject: [PATCH 05/16] SipHasher::new() is literally with SipHasher with both keys being 0 --- src/test/ui/deriving/deriving-hash.rs | 2 +- src/test/ui/issues/issue-16530.rs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/test/ui/deriving/deriving-hash.rs b/src/test/ui/deriving/deriving-hash.rs index 68c68c235ef4a..8b51370bca502 100644 --- a/src/test/ui/deriving/deriving-hash.rs +++ b/src/test/ui/deriving/deriving-hash.rs @@ -24,7 +24,7 @@ struct Person { enum E { A=1, B } fn hash(t: &T) -> u64 { - let mut s = SipHasher::new_with_keys(0, 0); + let mut s = SipHasher::new(); t.hash(&mut s); s.finish() } diff --git a/src/test/ui/issues/issue-16530.rs b/src/test/ui/issues/issue-16530.rs index 22a6ef7fa091f..25817a2a63d60 100644 --- a/src/test/ui/issues/issue-16530.rs +++ b/src/test/ui/issues/issue-16530.rs @@ -7,9 +7,9 @@ use std::hash::{SipHasher, Hasher, Hash}; struct Empty; pub fn main() { - let mut s1 = SipHasher::new_with_keys(0, 0); + let mut s1 = SipHasher::new(); Empty.hash(&mut s1); - let mut s2 = SipHasher::new_with_keys(0, 0); + let mut s2 = SipHasher::new(); Empty.hash(&mut s2); assert_eq!(s1.finish(), s2.finish()); } From 9bcf4097e33d8982b57c063f6481f7dd453bb0aa Mon Sep 17 00:00:00 2001 From: Joshua Nelson Date: Sat, 2 May 2020 20:54:28 -0400 Subject: [PATCH 06/16] x.py: Give a more helpful error message if curl isn't installed This also abstracts checking for a command into `require`. Before: ``` Updating only changed submodules Submodules updated in 0.01 seconds Traceback (most recent call last): File "./x.py", line 11, in bootstrap.main() ... File "/home/joshua/src/rust/src/bootstrap/bootstrap.py", line 137, in run ret = subprocess.Popen(args, **kwargs) File "/usr/lib/python2.7/subprocess.py", line 394, in __init__ errread, errwrite) File "/usr/lib/python2.7/subprocess.py", line 1047, in _execute_child raise child_exception OSError: [Errno 2] No such file or directory ``` After: ``` error: unable to run `curl --version`: [Errno 2] No such file or directory Please make sure it's installed and in the path. ``` --- src/bootstrap/bootstrap.py | 48 ++++++++++++++++++++------------------ 1 file changed, 25 insertions(+), 23 deletions(-) diff --git a/src/bootstrap/bootstrap.py b/src/bootstrap/bootstrap.py index 2aa3f9c7ec04b..9e56dd3770d46 100644 --- a/src/bootstrap/bootstrap.py +++ b/src/bootstrap/bootstrap.py @@ -79,6 +79,7 @@ def _download(path, url, probably_big, verbose, exception): option = "-#" else: option = "-s" + require(["curl", "--version"]) run(["curl", option, "-y", "30", "-Y", "10", # timeout if speed is < 10 bytes/sec for > 30 seconds "--connect-timeout", "30", # timeout if cannot connect within 30 seconds @@ -143,6 +144,21 @@ def run(args, verbose=False, exception=False, **kwargs): sys.exit(err) +def require(cmd, exit=True): + '''Run a command, returning its output. + On error, + If `exit` is `True`, exit the process. + Otherwise, return None.''' + try: + return subprocess.check_output(cmd).strip() + except (subprocess.CalledProcessError, OSError) as exc: + if not exit: + return None + print("error: unable to run `{}`: {}".format(' '.join(cmd), exc)) + print("Please make sure it's installed and in the path.") + sys.exit(1) + + def stage0_data(rust_root): """Build a dictionary from stage0.txt""" nightlies = os.path.join(rust_root, "src/stage0.txt") @@ -164,16 +180,12 @@ def format_build_time(duration): def default_build_triple(): """Build triple as in LLVM""" default_encoding = sys.getdefaultencoding() - try: - ostype = subprocess.check_output( - ['uname', '-s']).strip().decode(default_encoding) - cputype = subprocess.check_output( - ['uname', '-m']).strip().decode(default_encoding) - except (subprocess.CalledProcessError, OSError): - if sys.platform == 'win32': - return 'x86_64-pc-windows-msvc' - err = "uname not found" - sys.exit(err) + required = not sys.platform == 'win32' + ostype = require(["uname", "-s"], exit=required).decode(default_encoding) + cputype = require(['uname', '-m'], exit=required).decode(default_encoding) + + if ostype is None or cputype is None: + return 'x86_64-pc-windows-msvc' # The goal here is to come up with the same triple as LLVM would, # at least for the subset of platforms we're willing to target. @@ -203,12 +215,7 @@ def default_build_triple(): # output from that option is too generic for our purposes (it will # always emit 'i386' on x86/amd64 systems). As such, isainfo -k # must be used instead. - try: - cputype = subprocess.check_output( - ['isainfo', '-k']).strip().decode(default_encoding) - except (subprocess.CalledProcessError, OSError): - err = "isainfo not found" - sys.exit(err) + cputype = require(['isainfo', '-k']).decode(default_encoding) elif ostype.startswith('MINGW'): # msys' `uname` does not print gcc configuration, but prints msys # configuration. so we cannot believe `uname -m`: @@ -766,13 +773,8 @@ def update_submodules(self): default_encoding = sys.getdefaultencoding() # check the existence and version of 'git' command - try: - git_version_output = subprocess.check_output(['git', '--version']) - git_version_str = git_version_output.strip().split()[2].decode(default_encoding) - self.git_version = distutils.version.LooseVersion(git_version_str) - except (subprocess.CalledProcessError, OSError): - print("error: `git` is not found, please make sure it's installed and in the path.") - sys.exit(1) + git_version_str = require(['git', '--version']).split()[2].decode(default_encoding) + self.git_version = distutils.version.LooseVersion(git_version_str) slow_submodules = self.get_toml('fast-submodules') == "false" start_time = time() From 810c1b4fe4969f039342252535ab2c042ddcf2a4 Mon Sep 17 00:00:00 2001 From: Mark Rousskov Date: Fri, 17 Apr 2020 21:42:22 -0400 Subject: [PATCH 07/16] Define UB in float-to-int casts to saturate - Round to zero, and representable values cast directly. - `NaN` goes to 0 - Values beyond the limits of the type are saturated to the "nearest value" (essentially rounding to zero, in some sense) in the integral type, so e.g. `f32::INFINITY` would go to `{u,i}N::MAX.` --- src/librustc_codegen_ssa/mir/rvalue.rs | 2 +- src/librustc_interface/tests.rs | 2 +- src/librustc_session/options.rs | 4 ++-- src/test/codegen/unchecked-float-casts.rs | 12 +++++------- .../ui/numbers-arithmetic/saturating-float-casts.rs | 1 - 5 files changed, 9 insertions(+), 12 deletions(-) diff --git a/src/librustc_codegen_ssa/mir/rvalue.rs b/src/librustc_codegen_ssa/mir/rvalue.rs index 19a0138d9cb6d..bb532abd84bde 100644 --- a/src/librustc_codegen_ssa/mir/rvalue.rs +++ b/src/librustc_codegen_ssa/mir/rvalue.rs @@ -768,7 +768,7 @@ fn cast_float_to_int<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( ) -> Bx::Value { let fptosui_result = if signed { bx.fptosi(x, int_ty) } else { bx.fptoui(x, int_ty) }; - if !bx.cx().sess().opts.debugging_opts.saturating_float_casts { + if let Some(false) = bx.cx().sess().opts.debugging_opts.saturating_float_casts { return fptosui_result; } diff --git a/src/librustc_interface/tests.rs b/src/librustc_interface/tests.rs index 0a200426e38ea..ad1934c97865e 100644 --- a/src/librustc_interface/tests.rs +++ b/src/librustc_interface/tests.rs @@ -558,7 +558,7 @@ fn test_debugging_options_tracking_hash() { tracked!(sanitizer, Some(Sanitizer::Address)); tracked!(sanitizer_memory_track_origins, 2); tracked!(sanitizer_recover, vec![Sanitizer::Address]); - tracked!(saturating_float_casts, true); + tracked!(saturating_float_casts, Some(true)); tracked!(share_generics, Some(true)); tracked!(show_span, Some(String::from("abc"))); tracked!(src_hash_algorithm, Some(SourceFileHashAlgorithm::Sha1)); diff --git a/src/librustc_session/options.rs b/src/librustc_session/options.rs index b03fc00d93db2..7fcf6d1385060 100644 --- a/src/librustc_session/options.rs +++ b/src/librustc_session/options.rs @@ -936,9 +936,9 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options, "enable origins tracking in MemorySanitizer"), sanitizer_recover: Vec = (vec![], parse_sanitizer_list, [TRACKED], "enable recovery for selected sanitizers"), - saturating_float_casts: bool = (false, parse_bool, [TRACKED], + saturating_float_casts: Option = (None, parse_opt_bool, [TRACKED], "make float->int casts UB-free: numbers outside the integer type's range are clipped to \ - the max/min integer respectively, and NaN is mapped to 0 (default: no)"), + the max/min integer respectively, and NaN is mapped to 0 (default: yes)"), save_analysis: bool = (false, parse_bool, [UNTRACKED], "write syntax and type analysis (in JSON format) information, in \ addition to normal output (default: no)"), diff --git a/src/test/codegen/unchecked-float-casts.rs b/src/test/codegen/unchecked-float-casts.rs index 34e9612222309..61c9dc58c9d8c 100644 --- a/src/test/codegen/unchecked-float-casts.rs +++ b/src/test/codegen/unchecked-float-casts.rs @@ -1,7 +1,5 @@ -// compile-flags: -C no-prepopulate-passes - -// This file tests that we don't generate any code for saturation if -// -Z saturating-float-casts is not enabled. +// This file tests that we don't generate any code for saturation when using the +// unchecked intrinsics. #![crate_type = "lib"] @@ -12,7 +10,7 @@ pub fn f32_to_u32(x: f32) -> u32 { // CHECK-NOT: fcmp // CHECK-NOT: icmp // CHECK-NOT: select - x as u32 + unsafe { x.to_int_unchecked() } } // CHECK-LABEL: @f32_to_i32 @@ -22,7 +20,7 @@ pub fn f32_to_i32(x: f32) -> i32 { // CHECK-NOT: fcmp // CHECK-NOT: icmp // CHECK-NOT: select - x as i32 + unsafe { x.to_int_unchecked() } } #[no_mangle] @@ -31,5 +29,5 @@ pub fn f64_to_u16(x: f64) -> u16 { // CHECK-NOT: fcmp // CHECK-NOT: icmp // CHECK-NOT: select - x as u16 + unsafe { x.to_int_unchecked() } } diff --git a/src/test/ui/numbers-arithmetic/saturating-float-casts.rs b/src/test/ui/numbers-arithmetic/saturating-float-casts.rs index f13964fb38665..811efaefbb5bd 100644 --- a/src/test/ui/numbers-arithmetic/saturating-float-casts.rs +++ b/src/test/ui/numbers-arithmetic/saturating-float-casts.rs @@ -1,6 +1,5 @@ // run-pass // Tests saturating float->int casts. See u128-as-f32.rs for the opposite direction. -// compile-flags: -Z saturating-float-casts #![feature(test, stmt_expr_attributes)] #![deny(overflowing_literals)] From 5a759f91cb3990696a317e0cdc16fd48a80f5c9d Mon Sep 17 00:00:00 2001 From: Mark Rousskov Date: Sat, 18 Apr 2020 18:43:08 -0400 Subject: [PATCH 08/16] Pull in miri test cases --- .../saturating-float-casts.rs | 463 +++++++++++++++++- 1 file changed, 458 insertions(+), 5 deletions(-) diff --git a/src/test/ui/numbers-arithmetic/saturating-float-casts.rs b/src/test/ui/numbers-arithmetic/saturating-float-casts.rs index 811efaefbb5bd..09c044a1b7a98 100644 --- a/src/test/ui/numbers-arithmetic/saturating-float-casts.rs +++ b/src/test/ui/numbers-arithmetic/saturating-float-casts.rs @@ -1,14 +1,19 @@ // run-pass // Tests saturating float->int casts. See u128-as-f32.rs for the opposite direction. +// +// Some of these tests come from a similar file in miri, +// tests/run-pass/float.rs. They're just duplicated currently but we may want +// to merge this in the future. #![feature(test, stmt_expr_attributes)] +#![feature(track_caller)] #![deny(overflowing_literals)] extern crate test; use std::{f32, f64}; -use std::{u8, i8, u16, i16, u32, i32, u64, i64}; -#[cfg(not(target_os="emscripten"))] -use std::{u128, i128}; +#[cfg(not(target_os = "emscripten"))] +use std::{i128, u128}; +use std::{i16, i32, i64, i8, u16, u32, u64, u8}; use test::black_box; macro_rules! test { @@ -84,11 +89,459 @@ macro_rules! fptoui_tests { }) } +use std::fmt::Debug; + +// Helper function to avoid promotion so that this tests "run-time" casts, not CTFE. +#[track_caller] +#[inline(never)] +fn assert_eq(x: T, y: T) { + assert_eq!(x, y); +} + +trait FloatToInt: Copy { + fn cast(self) -> Int; + unsafe fn cast_unchecked(self) -> Int; +} + +impl FloatToInt for f32 { + fn cast(self) -> i8 { + self as _ + } + unsafe fn cast_unchecked(self) -> i8 { + self.to_int_unchecked() + } +} +impl FloatToInt for f32 { + fn cast(self) -> i32 { + self as _ + } + unsafe fn cast_unchecked(self) -> i32 { + self.to_int_unchecked() + } +} +impl FloatToInt for f32 { + fn cast(self) -> u32 { + self as _ + } + unsafe fn cast_unchecked(self) -> u32 { + self.to_int_unchecked() + } +} +impl FloatToInt for f32 { + fn cast(self) -> i64 { + self as _ + } + unsafe fn cast_unchecked(self) -> i64 { + self.to_int_unchecked() + } +} +impl FloatToInt for f32 { + fn cast(self) -> u64 { + self as _ + } + unsafe fn cast_unchecked(self) -> u64 { + self.to_int_unchecked() + } +} + +impl FloatToInt for f64 { + fn cast(self) -> i8 { + self as _ + } + unsafe fn cast_unchecked(self) -> i8 { + self.to_int_unchecked() + } +} +impl FloatToInt for f64 { + fn cast(self) -> i32 { + self as _ + } + unsafe fn cast_unchecked(self) -> i32 { + self.to_int_unchecked() + } +} +impl FloatToInt for f64 { + fn cast(self) -> u32 { + self as _ + } + unsafe fn cast_unchecked(self) -> u32 { + self.to_int_unchecked() + } +} +impl FloatToInt for f64 { + fn cast(self) -> i64 { + self as _ + } + unsafe fn cast_unchecked(self) -> i64 { + self.to_int_unchecked() + } +} +impl FloatToInt for f64 { + fn cast(self) -> u64 { + self as _ + } + unsafe fn cast_unchecked(self) -> u64 { + self.to_int_unchecked() + } +} +// FIXME emscripten does not support i128 +#[cfg(not(target_os = "emscripten"))] +impl FloatToInt for f64 { + fn cast(self) -> i128 { + self as _ + } + unsafe fn cast_unchecked(self) -> i128 { + self.to_int_unchecked() + } +} +// FIXME emscripten does not support i128 +#[cfg(not(target_os = "emscripten"))] +impl FloatToInt for f64 { + fn cast(self) -> u128 { + self as _ + } + unsafe fn cast_unchecked(self) -> u128 { + self.to_int_unchecked() + } +} + +/// Test this cast both via `as` and via `to_int_unchecked` (i.e., it must not saturate). +#[track_caller] +#[inline(never)] +fn test_both_cast(x: F, y: I) +where + F: FloatToInt, + I: PartialEq + Debug, +{ + assert_eq!(x.cast(), y); + assert_eq!(unsafe { x.cast_unchecked() }, y); +} + +fn basic() { + // basic arithmetic + assert_eq(6.0_f32 * 6.0_f32, 36.0_f32); + assert_eq(6.0_f64 * 6.0_f64, 36.0_f64); + assert_eq(-{ 5.0_f32 }, -5.0_f32); + assert_eq(-{ 5.0_f64 }, -5.0_f64); + // infinities, NaN + assert!((5.0_f32 / 0.0).is_infinite()); + assert_ne!({ 5.0_f32 / 0.0 }, { -5.0_f32 / 0.0 }); + assert!((5.0_f64 / 0.0).is_infinite()); + assert_ne!({ 5.0_f64 / 0.0 }, { 5.0_f64 / -0.0 }); + assert!((-5.0_f32).sqrt().is_nan()); + assert!((-5.0_f64).sqrt().is_nan()); + assert_ne!(f32::NAN, f32::NAN); + assert_ne!(f64::NAN, f64::NAN); + // negative zero + let posz = 0.0f32; + let negz = -0.0f32; + assert_eq(posz, negz); + assert_ne!(posz.to_bits(), negz.to_bits()); + let posz = 0.0f64; + let negz = -0.0f64; + assert_eq(posz, negz); + assert_ne!(posz.to_bits(), negz.to_bits()); + // byte-level transmute + let x: u64 = unsafe { std::mem::transmute(42.0_f64) }; + let y: f64 = unsafe { std::mem::transmute(x) }; + assert_eq(y, 42.0_f64); + let x: u32 = unsafe { std::mem::transmute(42.0_f32) }; + let y: f32 = unsafe { std::mem::transmute(x) }; + assert_eq(y, 42.0_f32); +} + +fn casts() { + // f32 -> i8 + test_both_cast::(127.99, 127); + test_both_cast::(-128.99, -128); + + // f32 -> i32 + test_both_cast::(0.0, 0); + test_both_cast::(-0.0, 0); + test_both_cast::(/*0x1p-149*/ f32::from_bits(0x00000001), 0); + test_both_cast::(/*-0x1p-149*/ f32::from_bits(0x80000001), 0); + test_both_cast::(/*0x1.19999ap+0*/ f32::from_bits(0x3f8ccccd), 1); + test_both_cast::(/*-0x1.19999ap+0*/ f32::from_bits(0xbf8ccccd), -1); + test_both_cast::(1.9, 1); + test_both_cast::(-1.9, -1); + test_both_cast::(5.0, 5); + test_both_cast::(-5.0, -5); + test_both_cast::(2147483520.0, 2147483520); + test_both_cast::(-2147483648.0, -2147483648); + // unrepresentable casts + assert_eq::(2147483648.0f32 as i32, i32::MAX); + assert_eq::(-2147483904.0f32 as i32, i32::MIN); + assert_eq::(f32::MAX as i32, i32::MAX); + assert_eq::(f32::MIN as i32, i32::MIN); + assert_eq::(f32::INFINITY as i32, i32::MAX); + assert_eq::(f32::NEG_INFINITY as i32, i32::MIN); + assert_eq::(f32::NAN as i32, 0); + assert_eq::((-f32::NAN) as i32, 0); + + // f32 -> u32 + test_both_cast::(0.0, 0); + test_both_cast::(-0.0, 0); + test_both_cast::(-0.9999999, 0); + test_both_cast::(/*0x1p-149*/ f32::from_bits(0x1), 0); + test_both_cast::(/*-0x1p-149*/ f32::from_bits(0x80000001), 0); + test_both_cast::(/*0x1.19999ap+0*/ f32::from_bits(0x3f8ccccd), 1); + test_both_cast::(1.9, 1); + test_both_cast::(5.0, 5); + test_both_cast::(2147483648.0, 0x8000_0000); + test_both_cast::(4294967040.0, 0u32.wrapping_sub(256)); + test_both_cast::(/*-0x1.ccccccp-1*/ f32::from_bits(0xbf666666), 0); + test_both_cast::(/*-0x1.fffffep-1*/ f32::from_bits(0xbf7fffff), 0); + test_both_cast::((u32::MAX - 128) as f32, u32::MAX - 255); // rounding loss + + // unrepresentable casts: + + // rounds up and then becomes unrepresentable + assert_eq::((u32::MAX - 127) as f32 as u32, u32::MAX); + + assert_eq::(4294967296.0f32 as u32, u32::MAX); + assert_eq::(-5.0f32 as u32, 0); + assert_eq::(f32::MAX as u32, u32::MAX); + assert_eq::(f32::MIN as u32, 0); + assert_eq::(f32::INFINITY as u32, u32::MAX); + assert_eq::(f32::NEG_INFINITY as u32, 0); + assert_eq::(f32::NAN as u32, 0); + assert_eq::((-f32::NAN) as u32, 0); + + // f32 -> i64 + test_both_cast::(4294967296.0, 4294967296); + test_both_cast::(-4294967296.0, -4294967296); + test_both_cast::(9223371487098961920.0, 9223371487098961920); + test_both_cast::(-9223372036854775808.0, -9223372036854775808); + + // f64 -> i8 + test_both_cast::(127.99, 127); + test_both_cast::(-128.99, -128); + + // f64 -> i32 + test_both_cast::(0.0, 0); + test_both_cast::(-0.0, 0); + test_both_cast::(/*0x1.199999999999ap+0*/ f64::from_bits(0x3ff199999999999a), 1); + test_both_cast::( + /*-0x1.199999999999ap+0*/ f64::from_bits(0xbff199999999999a), + -1, + ); + test_both_cast::(1.9, 1); + test_both_cast::(-1.9, -1); + test_both_cast::(1e8, 100_000_000); + test_both_cast::(2147483647.0, 2147483647); + test_both_cast::(-2147483648.0, -2147483648); + // unrepresentable casts + assert_eq::(2147483648.0f64 as i32, i32::MAX); + assert_eq::(-2147483649.0f64 as i32, i32::MIN); + + // f64 -> i64 + test_both_cast::(0.0, 0); + test_both_cast::(-0.0, 0); + test_both_cast::(/*0x0.0000000000001p-1022*/ f64::from_bits(0x1), 0); + test_both_cast::( + /*-0x0.0000000000001p-1022*/ f64::from_bits(0x8000000000000001), + 0, + ); + test_both_cast::(/*0x1.199999999999ap+0*/ f64::from_bits(0x3ff199999999999a), 1); + test_both_cast::( + /*-0x1.199999999999ap+0*/ f64::from_bits(0xbff199999999999a), + -1, + ); + test_both_cast::(5.0, 5); + test_both_cast::(5.9, 5); + test_both_cast::(-5.0, -5); + test_both_cast::(-5.9, -5); + test_both_cast::(4294967296.0, 4294967296); + test_both_cast::(-4294967296.0, -4294967296); + test_both_cast::(9223372036854774784.0, 9223372036854774784); + test_both_cast::(-9223372036854775808.0, -9223372036854775808); + // unrepresentable casts + assert_eq::(9223372036854775808.0f64 as i64, i64::MAX); + assert_eq::(-9223372036854777856.0f64 as i64, i64::MIN); + assert_eq::(f64::MAX as i64, i64::MAX); + assert_eq::(f64::MIN as i64, i64::MIN); + assert_eq::(f64::INFINITY as i64, i64::MAX); + assert_eq::(f64::NEG_INFINITY as i64, i64::MIN); + assert_eq::(f64::NAN as i64, 0); + assert_eq::((-f64::NAN) as i64, 0); + + // f64 -> u64 + test_both_cast::(0.0, 0); + test_both_cast::(-0.0, 0); + test_both_cast::(-0.99999999999, 0); + test_both_cast::(5.0, 5); + test_both_cast::(1e16, 10000000000000000); + test_both_cast::((u64::MAX - 1024) as f64, u64::MAX - 2047); // rounding loss + test_both_cast::(9223372036854775808.0, 9223372036854775808); + // unrepresentable casts + assert_eq::(-5.0f64 as u64, 0); + // rounds up and then becomes unrepresentable + assert_eq::((u64::MAX - 1023) as f64 as u64, u64::MAX); + assert_eq::(18446744073709551616.0f64 as u64, u64::MAX); + assert_eq::(f64::MAX as u64, u64::MAX); + assert_eq::(f64::MIN as u64, 0); + assert_eq::(f64::INFINITY as u64, u64::MAX); + assert_eq::(f64::NEG_INFINITY as u64, 0); + assert_eq::(f64::NAN as u64, 0); + assert_eq::((-f64::NAN) as u64, 0); + + // FIXME emscripten does not support i128 + #[cfg(not(target_os = "emscripten"))] + { + // f64 -> i128 + assert_eq::(f64::MAX as i128, i128::MAX); + assert_eq::(f64::MIN as i128, i128::MIN); + + // f64 -> u128 + assert_eq::(f64::MAX as u128, u128::MAX); + assert_eq::(f64::MIN as u128, 0); + } + + // int -> f32 + assert_eq::(127i8 as f32, 127.0); + assert_eq::(2147483647i32 as f32, 2147483648.0); + assert_eq::((-2147483648i32) as f32, -2147483648.0); + assert_eq::(1234567890i32 as f32, /*0x1.26580cp+30*/ f32::from_bits(0x4e932c06)); + assert_eq::(16777217i32 as f32, 16777216.0); + assert_eq::((-16777217i32) as f32, -16777216.0); + assert_eq::(16777219i32 as f32, 16777220.0); + assert_eq::((-16777219i32) as f32, -16777220.0); + assert_eq::( + 0x7fffff4000000001i64 as f32, + /*0x1.fffffep+62*/ f32::from_bits(0x5effffff), + ); + assert_eq::( + 0x8000004000000001u64 as i64 as f32, + /*-0x1.fffffep+62*/ f32::from_bits(0xdeffffff), + ); + assert_eq::( + 0x0020000020000001i64 as f32, + /*0x1.000002p+53*/ f32::from_bits(0x5a000001), + ); + assert_eq::( + 0xffdfffffdfffffffu64 as i64 as f32, + /*-0x1.000002p+53*/ f32::from_bits(0xda000001), + ); + // FIXME emscripten does not support i128 + #[cfg(not(target_os = "emscripten"))] + { + assert_eq::(i128::MIN as f32, -170141183460469231731687303715884105728.0f32); + assert_eq::(u128::MAX as f32, f32::INFINITY); // saturation + } + + // int -> f64 + assert_eq::(127i8 as f64, 127.0); + assert_eq::(i16::MIN as f64, -32768.0f64); + assert_eq::(2147483647i32 as f64, 2147483647.0); + assert_eq::(-2147483648i32 as f64, -2147483648.0); + assert_eq::(987654321i32 as f64, 987654321.0); + assert_eq::(9223372036854775807i64 as f64, 9223372036854775807.0); + assert_eq::(-9223372036854775808i64 as f64, -9223372036854775808.0); + assert_eq::(4669201609102990i64 as f64, 4669201609102990.0); // Feigenbaum (?) + assert_eq::(9007199254740993i64 as f64, 9007199254740992.0); + assert_eq::(-9007199254740993i64 as f64, -9007199254740992.0); + assert_eq::(9007199254740995i64 as f64, 9007199254740996.0); + assert_eq::(-9007199254740995i64 as f64, -9007199254740996.0); + // FIXME emscripten does not support i128 + #[cfg(not(target_os = "emscripten"))] + { + // even that fits... + assert_eq::(u128::MAX as f64, 340282366920938463463374607431768211455.0f64); + } + + // f32 -> f64 + assert_eq::((0.0f32 as f64).to_bits(), 0.0f64.to_bits()); + assert_eq::(((-0.0f32) as f64).to_bits(), (-0.0f64).to_bits()); + assert_eq::(5.0f32 as f64, 5.0f64); + assert_eq::( + /*0x1p-149*/ f32::from_bits(0x1) as f64, + /*0x1p-149*/ f64::from_bits(0x36a0000000000000), + ); + assert_eq::( + /*-0x1p-149*/ f32::from_bits(0x80000001) as f64, + /*-0x1p-149*/ f64::from_bits(0xb6a0000000000000), + ); + assert_eq::( + /*0x1.fffffep+127*/ f32::from_bits(0x7f7fffff) as f64, + /*0x1.fffffep+127*/ f64::from_bits(0x47efffffe0000000), + ); + assert_eq::( + /*-0x1.fffffep+127*/ (-f32::from_bits(0x7f7fffff)) as f64, + /*-0x1.fffffep+127*/ -f64::from_bits(0x47efffffe0000000), + ); + assert_eq::( + /*0x1p-119*/ f32::from_bits(0x4000000) as f64, + /*0x1p-119*/ f64::from_bits(0x3880000000000000), + ); + assert_eq::( + /*0x1.8f867ep+125*/ f32::from_bits(0x7e47c33f) as f64, + 6.6382536710104395e+37, + ); + assert_eq::(f32::INFINITY as f64, f64::INFINITY); + assert_eq::(f32::NEG_INFINITY as f64, f64::NEG_INFINITY); + + // f64 -> f32 + assert_eq::((0.0f64 as f32).to_bits(), 0.0f32.to_bits()); + assert_eq::(((-0.0f64) as f32).to_bits(), (-0.0f32).to_bits()); + assert_eq::(5.0f64 as f32, 5.0f32); + assert_eq::(/*0x0.0000000000001p-1022*/ f64::from_bits(0x1) as f32, 0.0); + assert_eq::(/*-0x0.0000000000001p-1022*/ (-f64::from_bits(0x1)) as f32, -0.0); + assert_eq::( + /*0x1.fffffe0000000p-127*/ f64::from_bits(0x380fffffe0000000) as f32, + /*0x1p-149*/ f32::from_bits(0x800000), + ); + assert_eq::( + /*0x1.4eae4f7024c7p+108*/ f64::from_bits(0x46b4eae4f7024c70) as f32, + /*0x1.4eae5p+108*/ f32::from_bits(0x75a75728), + ); + assert_eq::(f64::MAX as f32, f32::INFINITY); + assert_eq::(f64::MIN as f32, f32::NEG_INFINITY); + assert_eq::(f64::INFINITY as f32, f32::INFINITY); + assert_eq::(f64::NEG_INFINITY as f32, f32::NEG_INFINITY); +} + +fn ops() { + // f32 min/max + assert_eq((1.0 as f32).max(-1.0), 1.0); + assert_eq((1.0 as f32).min(-1.0), -1.0); + assert_eq(f32::NAN.min(9.0), 9.0); + assert_eq(f32::NAN.max(-9.0), -9.0); + assert_eq((9.0 as f32).min(f32::NAN), 9.0); + assert_eq((-9.0 as f32).max(f32::NAN), -9.0); + + // f64 min/max + assert_eq((1.0 as f64).max(-1.0), 1.0); + assert_eq((1.0 as f64).min(-1.0), -1.0); + assert_eq(f64::NAN.min(9.0), 9.0); + assert_eq(f64::NAN.max(-9.0), -9.0); + assert_eq((9.0 as f64).min(f64::NAN), 9.0); + assert_eq((-9.0 as f64).max(f64::NAN), -9.0); + + // f32 copysign + assert_eq(3.5_f32.copysign(0.42), 3.5_f32); + assert_eq(3.5_f32.copysign(-0.42), -3.5_f32); + assert_eq((-3.5_f32).copysign(0.42), 3.5_f32); + assert_eq((-3.5_f32).copysign(-0.42), -3.5_f32); + assert!(f32::NAN.copysign(1.0).is_nan()); + + // f64 copysign + assert_eq(3.5_f64.copysign(0.42), 3.5_f64); + assert_eq(3.5_f64.copysign(-0.42), -3.5_f64); + assert_eq((-3.5_f64).copysign(0.42), 3.5_f64); + assert_eq((-3.5_f64).copysign(-0.42), -3.5_f64); + assert!(f64::NAN.copysign(1.0).is_nan()); +} + pub fn main() { + basic(); + casts(); + ops(); + common_fptoi_tests!(f* -> i8 i16 i32 i64 u8 u16 u32 u64); fptoui_tests!(f* -> u8 u16 u32 u64); // FIXME emscripten does not support i128 - #[cfg(not(target_os="emscripten"))] { + #[cfg(not(target_os = "emscripten"))] + { common_fptoi_tests!(f* -> i128 u128); fptoui_tests!(f* -> u128); } @@ -123,7 +576,7 @@ pub fn main() { test!(4294967296., f* -> u32, 4294967295); // # u128 - #[cfg(not(target_os="emscripten"))] + #[cfg(not(target_os = "emscripten"))] { // float->int: test_c!(f32::MAX, f32 -> u128, 0xffffff00000000000000000000000000); From 2528fa38e47c7db3c9bdd041dc937d63ee87dc1d Mon Sep 17 00:00:00 2001 From: Mark Rousskov Date: Sun, 19 Apr 2020 11:53:51 -0400 Subject: [PATCH 09/16] Fixup tests to test both const-eval and runtime --- .../saturating-float-casts.rs | 121 ++++-------------- 1 file changed, 22 insertions(+), 99 deletions(-) diff --git a/src/test/ui/numbers-arithmetic/saturating-float-casts.rs b/src/test/ui/numbers-arithmetic/saturating-float-casts.rs index 09c044a1b7a98..e6d0c94a02fac 100644 --- a/src/test/ui/numbers-arithmetic/saturating-float-casts.rs +++ b/src/test/ui/numbers-arithmetic/saturating-float-casts.rs @@ -1,9 +1,12 @@ // run-pass +// compile-flags:-Zmir-opt-level=0 // Tests saturating float->int casts. See u128-as-f32.rs for the opposite direction. // // Some of these tests come from a similar file in miri, -// tests/run-pass/float.rs. They're just duplicated currently but we may want -// to merge this in the future. +// tests/run-pass/float.rs. Individual test cases are potentially duplicated +// with the previously existing tests, but since this runs so quickly anyway, +// we're not spending the time to figure out exactly which ones should be +// merged. #![feature(test, stmt_expr_attributes)] #![feature(track_caller)] @@ -21,31 +24,18 @@ macro_rules! test { // black_box disables constant evaluation to test run-time conversions: assert_eq!(black_box::<$src_ty>($val) as $dest_ty, $expected, "run-time {} -> {}", stringify!($src_ty), stringify!($dest_ty)); - ); - - ($fval:expr, f* -> $ity:ident, $ival:expr) => ( - test!($fval, f32 -> $ity, $ival); - test!($fval, f64 -> $ity, $ival); - ) -} -// This macro tests const eval in addition to run-time evaluation. -// If and when saturating casts are adopted, this macro should be merged with test!() to ensure -// that run-time and const eval agree on inputs that currently trigger a const eval error. -macro_rules! test_c { - ($val:expr, $src_ty:ident -> $dest_ty:ident, $expected:expr) => ({ - test!($val, $src_ty -> $dest_ty, $expected); { const X: $src_ty = $val; const Y: $dest_ty = X as $dest_ty; assert_eq!(Y, $expected, "const eval {} -> {}", stringify!($src_ty), stringify!($dest_ty)); } - }); + ); ($fval:expr, f* -> $ity:ident, $ival:expr) => ( - test_c!($fval, f32 -> $ity, $ival); - test_c!($fval, f64 -> $ity, $ival); + test!($fval, f32 -> $ity, $ival); + test!($fval, f64 -> $ity, $ival); ) } @@ -59,11 +49,11 @@ macro_rules! common_fptoi_tests { // as well, the test is just slightly misplaced. test!($ity::MIN as $fty, $fty -> $ity, $ity::MIN); test!($ity::MAX as $fty, $fty -> $ity, $ity::MAX); - test_c!(0., $fty -> $ity, 0); - test_c!($fty::MIN_POSITIVE, $fty -> $ity, 0); + test!(0., $fty -> $ity, 0); + test!($fty::MIN_POSITIVE, $fty -> $ity, 0); test!(-0.9, $fty -> $ity, 0); - test_c!(1., $fty -> $ity, 1); - test_c!(42., $fty -> $ity, 42); + test!(1., $fty -> $ity, 1); + test!(42., $fty -> $ity, 42); )+ }); (f* -> $($ity:ident)+) => ({ @@ -217,39 +207,6 @@ where assert_eq!(unsafe { x.cast_unchecked() }, y); } -fn basic() { - // basic arithmetic - assert_eq(6.0_f32 * 6.0_f32, 36.0_f32); - assert_eq(6.0_f64 * 6.0_f64, 36.0_f64); - assert_eq(-{ 5.0_f32 }, -5.0_f32); - assert_eq(-{ 5.0_f64 }, -5.0_f64); - // infinities, NaN - assert!((5.0_f32 / 0.0).is_infinite()); - assert_ne!({ 5.0_f32 / 0.0 }, { -5.0_f32 / 0.0 }); - assert!((5.0_f64 / 0.0).is_infinite()); - assert_ne!({ 5.0_f64 / 0.0 }, { 5.0_f64 / -0.0 }); - assert!((-5.0_f32).sqrt().is_nan()); - assert!((-5.0_f64).sqrt().is_nan()); - assert_ne!(f32::NAN, f32::NAN); - assert_ne!(f64::NAN, f64::NAN); - // negative zero - let posz = 0.0f32; - let negz = -0.0f32; - assert_eq(posz, negz); - assert_ne!(posz.to_bits(), negz.to_bits()); - let posz = 0.0f64; - let negz = -0.0f64; - assert_eq(posz, negz); - assert_ne!(posz.to_bits(), negz.to_bits()); - // byte-level transmute - let x: u64 = unsafe { std::mem::transmute(42.0_f64) }; - let y: f64 = unsafe { std::mem::transmute(x) }; - assert_eq(y, 42.0_f64); - let x: u32 = unsafe { std::mem::transmute(42.0_f32) }; - let y: f32 = unsafe { std::mem::transmute(x) }; - assert_eq(y, 42.0_f32); -} - fn casts() { // f32 -> i8 test_both_cast::(127.99, 127); @@ -500,42 +457,8 @@ fn casts() { assert_eq::(f64::NEG_INFINITY as f32, f32::NEG_INFINITY); } -fn ops() { - // f32 min/max - assert_eq((1.0 as f32).max(-1.0), 1.0); - assert_eq((1.0 as f32).min(-1.0), -1.0); - assert_eq(f32::NAN.min(9.0), 9.0); - assert_eq(f32::NAN.max(-9.0), -9.0); - assert_eq((9.0 as f32).min(f32::NAN), 9.0); - assert_eq((-9.0 as f32).max(f32::NAN), -9.0); - - // f64 min/max - assert_eq((1.0 as f64).max(-1.0), 1.0); - assert_eq((1.0 as f64).min(-1.0), -1.0); - assert_eq(f64::NAN.min(9.0), 9.0); - assert_eq(f64::NAN.max(-9.0), -9.0); - assert_eq((9.0 as f64).min(f64::NAN), 9.0); - assert_eq((-9.0 as f64).max(f64::NAN), -9.0); - - // f32 copysign - assert_eq(3.5_f32.copysign(0.42), 3.5_f32); - assert_eq(3.5_f32.copysign(-0.42), -3.5_f32); - assert_eq((-3.5_f32).copysign(0.42), 3.5_f32); - assert_eq((-3.5_f32).copysign(-0.42), -3.5_f32); - assert!(f32::NAN.copysign(1.0).is_nan()); - - // f64 copysign - assert_eq(3.5_f64.copysign(0.42), 3.5_f64); - assert_eq(3.5_f64.copysign(-0.42), -3.5_f64); - assert_eq((-3.5_f64).copysign(0.42), 3.5_f64); - assert_eq((-3.5_f64).copysign(-0.42), -3.5_f64); - assert!(f64::NAN.copysign(1.0).is_nan()); -} - pub fn main() { - basic(); - casts(); - ops(); + casts(); // from miri's tests common_fptoi_tests!(f* -> i8 i16 i32 i64 u8 u16 u32 u64); fptoui_tests!(f* -> u8 u16 u32 u64); @@ -549,39 +472,39 @@ pub fn main() { // The following tests cover edge cases for some integer types. // # u8 - test_c!(254., f* -> u8, 254); + test!(254., f* -> u8, 254); test!(256., f* -> u8, 255); // # i8 - test_c!(-127., f* -> i8, -127); + test!(-127., f* -> i8, -127); test!(-129., f* -> i8, -128); - test_c!(126., f* -> i8, 126); + test!(126., f* -> i8, 126); test!(128., f* -> i8, 127); // # i32 // -2147483648. is i32::MIN (exactly) - test_c!(-2147483648., f* -> i32, i32::MIN); + test!(-2147483648., f* -> i32, i32::MIN); // 2147483648. is i32::MAX rounded up test!(2147483648., f32 -> i32, 2147483647); // With 24 significand bits, floats with magnitude in [2^30 + 1, 2^31] are rounded to // multiples of 2^7. Therefore, nextDown(round(i32::MAX)) is 2^31 - 128: - test_c!(2147483520., f32 -> i32, 2147483520); + test!(2147483520., f32 -> i32, 2147483520); // Similarly, nextUp(i32::MIN) is i32::MIN + 2^8 and nextDown(i32::MIN) is i32::MIN - 2^7 test!(-2147483904., f* -> i32, i32::MIN); - test_c!(-2147483520., f* -> i32, -2147483520); + test!(-2147483520., f* -> i32, -2147483520); // # u32 // round(MAX) and nextUp(round(MAX)) - test_c!(4294967040., f* -> u32, 4294967040); + test!(4294967040., f* -> u32, 4294967040); test!(4294967296., f* -> u32, 4294967295); // # u128 #[cfg(not(target_os = "emscripten"))] { // float->int: - test_c!(f32::MAX, f32 -> u128, 0xffffff00000000000000000000000000); + test!(f32::MAX, f32 -> u128, 0xffffff00000000000000000000000000); // nextDown(f32::MAX) = 2^128 - 2 * 2^104 const SECOND_LARGEST_F32: f32 = 340282326356119256160033759537265639424.; - test_c!(SECOND_LARGEST_F32, f32 -> u128, 0xfffffe00000000000000000000000000); + test!(SECOND_LARGEST_F32, f32 -> u128, 0xfffffe00000000000000000000000000); } } From d65d1797f9ffc4d8003f8032e5fe8148a3a274b1 Mon Sep 17 00:00:00 2001 From: Mark Rousskov Date: Tue, 21 Apr 2020 09:00:36 -0400 Subject: [PATCH 10/16] Remove warning about UB --- src/librustc_typeck/check/demand.rs | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/librustc_typeck/check/demand.rs b/src/librustc_typeck/check/demand.rs index 65ef9cad87448..996c4c8a5b576 100644 --- a/src/librustc_typeck/check/demand.rs +++ b/src/librustc_typeck/check/demand.rs @@ -853,13 +853,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { cast_suggestion, Applicability::MaybeIncorrect, // lossy conversion ); - err.warn( - "if the rounded value cannot be represented by the target \ - integer type, including `Inf` and `NaN`, casting will cause \ - undefined behavior \ - (see issue #10184 \ - for more information)", - ); } true } From 61fdd3e2bee1ea0f517ff0bfe16f3816decab99a Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Mon, 4 May 2020 19:17:58 +0200 Subject: [PATCH 11/16] expand comment on default mutex behavior --- src/libstd/sys/unix/mutex.rs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/libstd/sys/unix/mutex.rs b/src/libstd/sys/unix/mutex.rs index 103d87e3d2f91..185615d01dd50 100644 --- a/src/libstd/sys/unix/mutex.rs +++ b/src/libstd/sys/unix/mutex.rs @@ -28,14 +28,18 @@ impl Mutex { // // A pthread mutex initialized with PTHREAD_MUTEX_INITIALIZER will have // a type of PTHREAD_MUTEX_DEFAULT, which has undefined behavior if you - // try to re-lock it from the same thread when you already hold a lock. + // try to re-lock it from the same thread when you already hold a lock + // (https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_mutex_init.html). // // In practice, glibc takes advantage of this undefined behavior to // implement hardware lock elision, which uses hardware transactional - // memory to avoid acquiring the lock. While a transaction is in + // memory to avoid acquiring the lock. + // This is the case even if PTHREAD_MUTEX_DEFAULT == PTHREAD_MUTEX_NORMAL + // (https://github.com/rust-lang/rust/issues/33770#issuecomment-220847521). + // As a consequence, while a transaction is in // progress, the lock appears to be unlocked. This isn't a problem for // other threads since the transactional memory will abort if a conflict - // is detected, however no abort is generated if re-locking from the + // is detected, however no abort is generated when re-locking from the // same thread. // // Since locking the same mutex twice will result in two aliasing &mut From 40a6b8c339376b7879016d5d2a015b6ac43c4c0c Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Mon, 4 May 2020 19:37:55 +0200 Subject: [PATCH 12/16] explain our rwlock implementation (and fix a potential data race) --- src/libstd/sys/unix/rwlock.rs | 44 +++++++++++++++++++---------------- 1 file changed, 24 insertions(+), 20 deletions(-) diff --git a/src/libstd/sys/unix/rwlock.rs b/src/libstd/sys/unix/rwlock.rs index 079dea671ef76..3e3a01b4ea395 100644 --- a/src/libstd/sys/unix/rwlock.rs +++ b/src/libstd/sys/unix/rwlock.rs @@ -22,27 +22,26 @@ impl RWLock { pub unsafe fn read(&self) { let r = libc::pthread_rwlock_rdlock(self.inner.get()); - // According to the pthread_rwlock_rdlock spec, this function **may** - // fail with EDEADLK if a deadlock is detected. On the other hand - // pthread mutexes will *never* return EDEADLK if they are initialized - // as the "fast" kind (which ours always are). As a result, a deadlock - // situation may actually return from the call to pthread_rwlock_rdlock - // instead of blocking forever (as mutexes and Windows rwlocks do). Note - // that not all unix implementations, however, will return EDEADLK for - // their rwlocks. + // According to POSIX, when a thread tries to acquire this read lock + // while it already holds the write lock + // (or vice versa, or tries to acquire the write lock twice), + // "the call shall either deadlock or return [EDEADLK]" + // (https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_rwlock_wrlock.html, + // https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_rwlock_rdlock.html). + // So, in principle, all we have to do here is check `r == 0` to be sure we properly + // got the lock. // - // We roughly maintain the deadlocking behavior by panicking to ensure - // that this lock acquisition does not succeed. - // - // We also check whether this lock is already write locked. This - // is only possible if it was write locked by the current thread and - // the implementation allows recursive locking. The POSIX standard - // doesn't require recursively locking a rwlock to deadlock, but we can't - // allow that because it could lead to aliasing issues. + // However, (at least) glibc before version 2.25 does not conform to this spec, + // and can return `r == 0` even when this thread already holds the write lock. + // We thus check for this situation ourselves and panic when detecting that a thread + // got the write lock more than once, or got a read and a write lock. if r == libc::EAGAIN { panic!("rwlock maximum reader count exceeded"); } else if r == libc::EDEADLK || (r == 0 && *self.write_locked.get()) { + // Above, we make sure to only access `write_locked` when `r == 0` to avoid + // data races. if r == 0 { + // `pthread_rwlock_rdlock` succeeded when it should not have. self.raw_unlock(); } panic!("rwlock read lock would result in deadlock"); @@ -56,6 +55,7 @@ impl RWLock { let r = libc::pthread_rwlock_tryrdlock(self.inner.get()); if r == 0 { if *self.write_locked.get() { + // `pthread_rwlock_tryrdlock` succeeded when it should not have. self.raw_unlock(); false } else { @@ -69,18 +69,21 @@ impl RWLock { #[inline] pub unsafe fn write(&self) { let r = libc::pthread_rwlock_wrlock(self.inner.get()); - // See comments above for why we check for EDEADLK and write_locked. We - // also need to check that num_readers is 0. + // See comments above for why we check for EDEADLK and write_locked. For the same reason, + // we also need to check that there are no readers (tracked in `num_readers`). if r == libc::EDEADLK - || *self.write_locked.get() + || (r == 0 && *self.write_locked.get()) || self.num_readers.load(Ordering::Relaxed) != 0 { + // Above, we make sure to only access `write_locked` when `r == 0` to avoid + // data races. if r == 0 { + // `pthread_rwlock_wrlock` succeeded when it should not have. self.raw_unlock(); } panic!("rwlock write lock would result in deadlock"); } else { - debug_assert_eq!(r, 0); + assert_eq!(r, 0); } *self.write_locked.get() = true; } @@ -89,6 +92,7 @@ impl RWLock { let r = libc::pthread_rwlock_trywrlock(self.inner.get()); if r == 0 { if *self.write_locked.get() || self.num_readers.load(Ordering::Relaxed) != 0 { + // `pthread_rwlock_trywrlock` succeeded when it should not have. self.raw_unlock(); false } else { From 3f50292edce92fb889bcd7318fead1a8905b6342 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Mon, 4 May 2020 20:47:46 +0200 Subject: [PATCH 13/16] edit Mutex comment --- src/libstd/sys/unix/mutex.rs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/libstd/sys/unix/mutex.rs b/src/libstd/sys/unix/mutex.rs index 185615d01dd50..45c600f75f5cf 100644 --- a/src/libstd/sys/unix/mutex.rs +++ b/src/libstd/sys/unix/mutex.rs @@ -30,13 +30,15 @@ impl Mutex { // a type of PTHREAD_MUTEX_DEFAULT, which has undefined behavior if you // try to re-lock it from the same thread when you already hold a lock // (https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_mutex_init.html). + // This is the case even if PTHREAD_MUTEX_DEFAULT == PTHREAD_MUTEX_NORMAL + // (https://github.com/rust-lang/rust/issues/33770#issuecomment-220847521) -- in that + // case, `pthread_mutexattr_settype(PTHREAD_MUTEX_DEFAULT)` will of course be the same + // as setting it to `PTHREAD_MUTEX_NORMAL`, but not setting any mode will result in + // a Mutex where re-locking is UB. // // In practice, glibc takes advantage of this undefined behavior to // implement hardware lock elision, which uses hardware transactional - // memory to avoid acquiring the lock. - // This is the case even if PTHREAD_MUTEX_DEFAULT == PTHREAD_MUTEX_NORMAL - // (https://github.com/rust-lang/rust/issues/33770#issuecomment-220847521). - // As a consequence, while a transaction is in + // memory to avoid acquiring the lock. While a transaction is in // progress, the lock appears to be unlocked. This isn't a problem for // other threads since the transactional memory will abort if a conflict // is detected, however no abort is generated when re-locking from the From b83853d61725a6de7f5563275733daa6a61accca Mon Sep 17 00:00:00 2001 From: mibac138 <5672750+mibac138@users.noreply.github.com> Date: Tue, 5 May 2020 00:31:37 +0200 Subject: [PATCH 14/16] Add command aliases from Cargo to x.py commands --- src/bootstrap/flags.rs | 30 +++++++++++++++++------------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/src/bootstrap/flags.rs b/src/bootstrap/flags.rs index 5d6e401d5b3fb..fb380af0a47e7 100644 --- a/src/bootstrap/flags.rs +++ b/src/bootstrap/flags.rs @@ -106,18 +106,18 @@ impl Flags { Usage: x.py [options] [...] Subcommands: - build Compile either the compiler or libraries - check Compile either the compiler or libraries, using cargo check + build, b Compile either the compiler or libraries + check, c Compile either the compiler or libraries, using cargo check clippy Run clippy (uses rustup/cargo-installed clippy binary) fix Run cargo fix fmt Run rustfmt - test Build and run some test suites + test, t Build and run some test suites bench Build and run some benchmarks doc Build documentation clean Clean out build directories dist Build distribution artifacts install Install distribution artifacts - run Run tools contained in this repository + run, r Run tools contained in this repository To learn more about a subcommand, run `./x.py -h`", ); @@ -184,17 +184,21 @@ To learn more about a subcommand, run `./x.py -h`", // there on out. let subcommand = args.iter().find(|&s| { (s == "build") + || (s == "b") || (s == "check") + || (s == "c") || (s == "clippy") || (s == "fix") || (s == "fmt") || (s == "test") + || (s == "t") || (s == "bench") || (s == "doc") || (s == "clean") || (s == "dist") || (s == "install") || (s == "run") + || (s == "r") }); let subcommand = match subcommand { Some(s) => s, @@ -210,7 +214,7 @@ To learn more about a subcommand, run `./x.py -h`", // Some subcommands get extra options match subcommand.as_str() { - "test" => { + "test" | "t" => { opts.optflag("", "no-fail-fast", "Run all tests regardless of failure"); opts.optmulti("", "test-args", "extra arguments", "ARGS"); opts.optmulti( @@ -285,7 +289,7 @@ To learn more about a subcommand, run `./x.py -h`", } // Extra help text for some commands match subcommand.as_str() { - "build" => { + "build" | "b" => { subcommand_help.push_str( "\n Arguments: @@ -312,7 +316,7 @@ Arguments: Once this is done, build/$ARCH/stage1 contains a usable compiler.", ); } - "check" => { + "check" | "c" => { subcommand_help.push_str( "\n Arguments: @@ -362,7 +366,7 @@ Arguments: ./x.py fmt --check", ); } - "test" => { + "test" | "t" => { subcommand_help.push_str( "\n Arguments: @@ -407,7 +411,7 @@ Arguments: ./x.py doc --stage 1", ); } - "run" => { + "run" | "r" => { subcommand_help.push_str( "\n Arguments: @@ -453,11 +457,11 @@ Arguments: } let cmd = match subcommand.as_str() { - "build" => Subcommand::Build { paths }, - "check" => Subcommand::Check { paths }, + "build" | "b" => Subcommand::Build { paths }, + "check" | "c" => Subcommand::Check { paths }, "clippy" => Subcommand::Clippy { paths }, "fix" => Subcommand::Fix { paths }, - "test" => Subcommand::Test { + "test" | "t" => Subcommand::Test { paths, bless: matches.opt_present("bless"), compare_mode: matches.opt_str("compare-mode"), @@ -487,7 +491,7 @@ Arguments: "fmt" => Subcommand::Format { check: matches.opt_present("check") }, "dist" => Subcommand::Dist { paths }, "install" => Subcommand::Install { paths }, - "run" => { + "run" | "r" => { if paths.is_empty() { println!("\nrun requires at least a path!\n"); usage(1, &opts, &subcommand_help, &extra_help); From f9866f95afad4ea97b975631c6016aa951fd4f83 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Tue, 5 May 2020 09:08:00 +0200 Subject: [PATCH 15/16] rely on rdlock/wrlock not returning anything but the specified error codes --- src/libstd/sys/unix/rwlock.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/libstd/sys/unix/rwlock.rs b/src/libstd/sys/unix/rwlock.rs index 3e3a01b4ea395..2b5067a34f648 100644 --- a/src/libstd/sys/unix/rwlock.rs +++ b/src/libstd/sys/unix/rwlock.rs @@ -46,7 +46,9 @@ impl RWLock { } panic!("rwlock read lock would result in deadlock"); } else { - assert_eq!(r, 0); + // According to POSIX, for a properly initialized rwlock this can only + // return EAGAIN or EDEADLK or 0. We rely on that. + debug_assert_eq!(r, 0); self.num_readers.fetch_add(1, Ordering::Relaxed); } } @@ -83,7 +85,9 @@ impl RWLock { } panic!("rwlock write lock would result in deadlock"); } else { - assert_eq!(r, 0); + // According to POSIX, for a properly initialized rwlock this can only + // return EDEADLK or 0. We rely on that. + debug_assert_eq!(r, 0); } *self.write_locked.get() = true; } From 3857506be56736d40b6aa70d4f278f5ee90d26a0 Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Fri, 1 May 2020 13:18:15 -0700 Subject: [PATCH 16/16] backport 1.43.1 release notes --- RELEASES.md | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/RELEASES.md b/RELEASES.md index 36597b1864f25..8ea481f7e18cd 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -1,3 +1,15 @@ +Version 1.43.1 (2020-05-07) +=========================== + +* [Updated openssl-src to 1.1.1g for CVE-2020-1967.][71430] +* [Fixed the stabilization of AVX-512 features.][71473] +* [Fixed `cargo package --list` not working with unpublished dependencies.][cargo/8151] + +[71430]: https://github.com/rust-lang/rust/pull/71430 +[71473]: https://github.com/rust-lang/rust/issues/71473 +[cargo/8151]: https://github.com/rust-lang/cargo/issues/8151 + + Version 1.43.0 (2020-04-23) ========================== @@ -14,7 +26,7 @@ Language - [Merge `fn` syntax + cleanup item parsing.][68728] - [`item` macro fragments can be interpolated into `trait`s, `impl`s, and `extern` blocks.][69366] For example, you may now write: - ```rust + ```rust macro_rules! mac_trait { ($i:item) => { trait T { $i } @@ -82,7 +94,7 @@ Misc - [Certain checks in the `const_err` lint were deemed unrelated to const evaluation][69185], and have been moved to the `unconditional_panic` and `arithmetic_overflow` lints. - + Compatibility Notes ------------------- @@ -173,7 +185,7 @@ Language (e.g. `type Foo: Ord;`). - `...` (the C-variadic type) may occur syntactically directly as the type of any function parameter. - + These are still rejected *semantically*, so you will likely receive an error but these changes can be seen and parsed by procedural macros and conditional compilation. @@ -465,7 +477,7 @@ Compatibility Notes - [Using `#[inline]` on function prototypes and consts now emits a warning under `unused_attribute` lint.][65294] Using `#[inline]` anywhere else inside traits or `extern` blocks now correctly emits a hard error. - + [65294]: https://github.com/rust-lang/rust/pull/65294/ [66103]: https://github.com/rust-lang/rust/pull/66103/ [65843]: https://github.com/rust-lang/rust/pull/65843/