From 81957bf2886780d7efc365a6489603c33f7d336d Mon Sep 17 00:00:00 2001 From: The Miri Cronjob Bot Date: Sun, 17 Mar 2024 05:15:04 +0000 Subject: [PATCH 01/14] Preparing for merge from rustc --- rust-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-version b/rust-version index e32968d817..f04e6e3747 100644 --- a/rust-version +++ b/rust-version @@ -1 +1 @@ -ee03c286cfdca26fa5b2a4ee40957625d2c826ff +a615cea3339046c7ab2d75cd253727d1fd42dd0b From 9c0398798ec4515449471497670241b1e7b05e76 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sun, 17 Mar 2024 10:56:28 +0100 Subject: [PATCH 02/14] fix Zulip topic for PR-creation message --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c70005c2c5..1097fc6db7 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -206,7 +206,7 @@ jobs: run: | PR=$(gh pr create -B master --title 'Automatic Rustup' --body '') ~/.local/bin/zulip-send --user $ZULIP_BOT_EMAIL --api-key $ZULIP_API_TOKEN --site https://rust-lang.zulipchat.com \ - --stream miri --subject "Cron Job Failure (miri, $(date -u +%Y-%m))" \ + --stream miri --subject "Miri Build Failure ($(date -u +%Y-%m))" \ --message "A PR doing a rustc-pull [has been automatically created]($PR) for your convenience." env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} From 34c65ddc727b8208ba77c44d08083deddd66e572 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sun, 17 Mar 2024 14:30:03 +0100 Subject: [PATCH 03/14] make 'invalidate' benchmark shorter --- bench-cargo-miri/invalidate/src/main.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bench-cargo-miri/invalidate/src/main.rs b/bench-cargo-miri/invalidate/src/main.rs index fa8deb851c..42492b1a88 100644 --- a/bench-cargo-miri/invalidate/src/main.rs +++ b/bench-cargo-miri/invalidate/src/main.rs @@ -1,4 +1,4 @@ fn main() { // The end of the range is just chosen to make the benchmark run for a few seconds. - for _ in 0..200_000 {} + for _ in 0..50_000 {} } From bae33ca59af652da97794336a017cd0a66d828d5 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Mon, 18 Mar 2024 08:55:45 +0100 Subject: [PATCH 04/14] add exposed-provenance example where we miss UB --- tests/pass/intptrcast.rs | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/tests/pass/intptrcast.rs b/tests/pass/intptrcast.rs index 370b09f512..4e9fa12c18 100644 --- a/tests/pass/intptrcast.rs +++ b/tests/pass/intptrcast.rs @@ -149,6 +149,31 @@ fn functions() { } } +/// Example that should be UB but due to wildcard pointers being too permissive +/// we don't notice. +fn should_be_ub() { + let alloc1 = 1u8; + let alloc2 = 2u8; + // Expose both allocations + let addr1: usize = &alloc1 as *const u8 as usize; + let addr2: usize = &alloc2 as *const u8 as usize; + + // Cast addr1 back to a pointer. In Miri, this gives it Wildcard provenance. + let wildcard = addr1 as *const u8; + unsafe { + // Read through the wildcard + assert_eq!(*wildcard, 1); + // Offset the pointer to another allocation. + // Note that we are doing this arithmetic that does not require we stay within bounds of the allocation. + let wildcard = wildcard.wrapping_offset(addr2 as isize - addr1 as isize); + // This should report UB: + assert_eq!(*wildcard, 2); + // ... but it doesn't. A pointer's provenance specifies a single allocation that it is allowed to read from. + // And wrapping_offset only modifies the address, not the provenance. + // So which allocation is wildcard allowed to access? It cannot be both. + } +} + fn main() { cast(); cast_dangling(); @@ -162,4 +187,5 @@ fn main() { ptr_eq_integer(); zst_deref_of_dangling(); functions(); + should_be_ub(); } From b3481a48684f98de19f45678b37bb944da358808 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Mon, 18 Mar 2024 08:59:19 +0100 Subject: [PATCH 05/14] rename 'invalidate' bench to something more clear --- bench-cargo-miri/{invalidate => range-iteration}/Cargo.lock | 2 +- bench-cargo-miri/{invalidate => range-iteration}/Cargo.toml | 2 +- bench-cargo-miri/{invalidate => range-iteration}/src/main.rs | 1 + 3 files changed, 3 insertions(+), 2 deletions(-) rename bench-cargo-miri/{invalidate => range-iteration}/Cargo.lock (84%) rename bench-cargo-miri/{invalidate => range-iteration}/Cargo.toml (86%) rename bench-cargo-miri/{invalidate => range-iteration}/src/main.rs (66%) diff --git a/bench-cargo-miri/invalidate/Cargo.lock b/bench-cargo-miri/range-iteration/Cargo.lock similarity index 84% rename from bench-cargo-miri/invalidate/Cargo.lock rename to bench-cargo-miri/range-iteration/Cargo.lock index 7bf23225ea..2a5f8f3225 100644 --- a/bench-cargo-miri/invalidate/Cargo.lock +++ b/bench-cargo-miri/range-iteration/Cargo.lock @@ -3,5 +3,5 @@ version = 3 [[package]] -name = "invalidate" +name = "range-iteration" version = "0.1.0" diff --git a/bench-cargo-miri/invalidate/Cargo.toml b/bench-cargo-miri/range-iteration/Cargo.toml similarity index 86% rename from bench-cargo-miri/invalidate/Cargo.toml rename to bench-cargo-miri/range-iteration/Cargo.toml index 14cf0882f0..3f0146f625 100644 --- a/bench-cargo-miri/invalidate/Cargo.toml +++ b/bench-cargo-miri/range-iteration/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "invalidate" +name = "range-iteration" version = "0.1.0" edition = "2021" diff --git a/bench-cargo-miri/invalidate/src/main.rs b/bench-cargo-miri/range-iteration/src/main.rs similarity index 66% rename from bench-cargo-miri/invalidate/src/main.rs rename to bench-cargo-miri/range-iteration/src/main.rs index 42492b1a88..1ef35ee6b3 100644 --- a/bench-cargo-miri/invalidate/src/main.rs +++ b/bench-cargo-miri/range-iteration/src/main.rs @@ -1,3 +1,4 @@ +//! This generates a lot of work for the AllocId part of the GC. fn main() { // The end of the range is just chosen to make the benchmark run for a few seconds. for _ in 0..50_000 {} From efa876eb2fd3545211adeb97b8b527f0891cc511 Mon Sep 17 00:00:00 2001 From: The Miri Cronjob Bot Date: Tue, 19 Mar 2024 04:56:56 +0000 Subject: [PATCH 06/14] Preparing for merge from rustc --- rust-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-version b/rust-version index f04e6e3747..0cdb0de033 100644 --- a/rust-version +++ b/rust-version @@ -1 +1 @@ -a615cea3339046c7ab2d75cd253727d1fd42dd0b +148a41c6b5687f941c5256d9ef8145eb03b72094 From 7cd87299f2ec58a11cf013a6e6fce05509e8f09b Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Tue, 19 Mar 2024 07:55:28 +0100 Subject: [PATCH 07/14] remove duplicate test --- tests/pass/main_fn.rs | 5 ----- 1 file changed, 5 deletions(-) delete mode 100644 tests/pass/main_fn.rs diff --git a/tests/pass/main_fn.rs b/tests/pass/main_fn.rs deleted file mode 100644 index 4cdd034f30..0000000000 --- a/tests/pass/main_fn.rs +++ /dev/null @@ -1,5 +0,0 @@ -mod foo { - pub(crate) fn bar() {} -} - -use foo::bar as main; From 73ba941d0525633f845b767eb6927e2fe8d9522a Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Tue, 19 Mar 2024 19:35:35 +0100 Subject: [PATCH 08/14] run full mono-item collection on all MIRI_BE_RUSTC=target builds this fixes compile_fail doctests with post-mono errors --- src/bin/miri.rs | 20 ++++++++++++++++++++ test-cargo-miri/src/lib.rs | 18 ++++++++++++++++++ test-cargo-miri/test.default.stdout.ref | 6 +++--- test-cargo-miri/test.filter.stdout.ref | 2 +- 4 files changed, 42 insertions(+), 4 deletions(-) diff --git a/src/bin/miri.rs b/src/bin/miri.rs index 6955e649b4..c12382527e 100644 --- a/src/bin/miri.rs +++ b/src/bin/miri.rs @@ -179,6 +179,26 @@ impl rustc_driver::Callbacks for MiriBeRustCompilerCalls { }); } } + + fn after_analysis<'tcx>( + &mut self, + _: &rustc_interface::interface::Compiler, + queries: &'tcx rustc_interface::Queries<'tcx>, + ) -> Compilation { + queries.global_ctxt().unwrap().enter(|tcx| { + if self.target_crate { + // cargo-miri has patched the compiler flags to make these into check-only builds, + // but we are still emulating regular rustc builds, which would perform post-mono + // const-eval during collection. So let's also do that here, even if we might be + // running with `--emit=metadata`. In particular this is needed to make + // `compile_fail` doc tests trigger post-mono errors. + // In general `collect_and_partition_mono_items` is not safe to call in check-only + // builds, but we are setting `-Zalways-encode-mir` which avoids those issues. + let _ = tcx.collect_and_partition_mono_items(()); + } + }); + Compilation::Continue + } } fn show_error(msg: &impl std::fmt::Display) -> ! { diff --git a/test-cargo-miri/src/lib.rs b/test-cargo-miri/src/lib.rs index 66c8aa2eac..e6b8c4ef65 100644 --- a/test-cargo-miri/src/lib.rs +++ b/test-cargo-miri/src/lib.rs @@ -1,13 +1,31 @@ /// Doc-test test +/// /// ```rust /// assert!(cargo_miri_test::make_true()); /// ``` +/// +/// `no_run` test: +/// /// ```rust,no_run /// assert!(!cargo_miri_test::make_true()); /// ``` +/// +/// `compile_fail` test: +/// /// ```rust,compile_fail /// assert!(cargo_miri_test::make_true() == 5); /// ``` +/// +/// Post-monomorphization error in `compile_fail` test: +/// +/// ```rust,compile_fail +/// struct Fail(T); +/// impl Fail { +/// const C: () = panic!(); +/// } +/// +/// let _val = Fail::::C; +/// ``` #[no_mangle] pub fn make_true() -> bool { issue_1567::use_the_dependency(); diff --git a/test-cargo-miri/test.default.stdout.ref b/test-cargo-miri/test.default.stdout.ref index 9a17f3d61b..922d2120be 100644 --- a/test-cargo-miri/test.default.stdout.ref +++ b/test-cargo-miri/test.default.stdout.ref @@ -10,7 +10,7 @@ running 6 tests test result: ok. 5 passed; 0 failed; 1 ignored; 0 measured; 0 filtered out -running 4 tests -.... -test result: ok. 4 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME +running 5 tests +..... +test result: ok. 5 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME diff --git a/test-cargo-miri/test.filter.stdout.ref b/test-cargo-miri/test.filter.stdout.ref index c618956656..5c819dd532 100644 --- a/test-cargo-miri/test.filter.stdout.ref +++ b/test-cargo-miri/test.filter.stdout.ref @@ -13,5 +13,5 @@ test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 5 filtered out running 0 tests -test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 4 filtered out; finished in $TIME +test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 5 filtered out; finished in $TIME From d086522df70d3f8c0ad82da6363f17be48d2d7b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eduardo=20S=C3=A1nchez=20Mu=C3=B1oz?= Date: Thu, 21 Mar 2024 17:54:53 +0100 Subject: [PATCH 09/14] Allow `llvm.x86.sse2.pause` instrinsic to be called without SSE2 The instrinsic is compiled to a `pause` instruction, which behaves like a no-op when SSE2 is not available. https://www.felixcloutier.com/x86/pause.html --- src/shims/x86/mod.rs | 13 ++++++++++ src/shims/x86/sse2.rs | 6 ----- .../pass/intrinsics-x86-pause-without-sse2.rs | 25 +++++++++++++++++++ tests/pass/intrinsics-x86-sse2.rs | 5 ++++ 4 files changed, 43 insertions(+), 6 deletions(-) create mode 100644 tests/pass/intrinsics-x86-pause-without-sse2.rs diff --git a/src/shims/x86/mod.rs b/src/shims/x86/mod.rs index 7cd397625d..7b7921219e 100644 --- a/src/shims/x86/mod.rs +++ b/src/shims/x86/mod.rs @@ -88,6 +88,19 @@ pub(super) trait EvalContextExt<'mir, 'tcx: 'mir>: this.write_immediate(*sub, &this.project_field(dest, 1)?)?; } + // Used to implement the `_mm_pause` function. + // The intrinsic is used to hint the processor that the code is in a spin-loop. + // It is compiled down to a `pause` instruction. When SSE2 is not available, + // the instruction behaves like a no-op, so it is always safe to call the + // intrinsic. + "sse2.pause" => { + let [] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; + // Only exhibit the spin-loop hint behavior when SSE2 is enabled. + if this.tcx.sess.unstable_target_features.contains(&Symbol::intern("sse2")) { + this.yield_active_thread(); + } + } + name if name.starts_with("sse.") => { return sse::EvalContextExt::emulate_x86_sse_intrinsic( this, link_name, abi, args, dest, diff --git a/src/shims/x86/sse2.rs b/src/shims/x86/sse2.rs index 18ff5d809e..eb2cc9d37c 100644 --- a/src/shims/x86/sse2.rs +++ b/src/shims/x86/sse2.rs @@ -580,12 +580,6 @@ pub(super) trait EvalContextExt<'mir, 'tcx: 'mir>: this.copy_op(&this.project_index(&left, i)?, &this.project_index(&dest, i)?)?; } } - // Used to implement the `_mm_pause` function. - // The intrinsic is used to hint the processor that the code is in a spin-loop. - "pause" => { - let [] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; - this.yield_active_thread(); - } _ => return Ok(EmulateForeignItemResult::NotSupported), } Ok(EmulateForeignItemResult::NeedsJumping) diff --git a/tests/pass/intrinsics-x86-pause-without-sse2.rs b/tests/pass/intrinsics-x86-pause-without-sse2.rs new file mode 100644 index 0000000000..c8b92fd545 --- /dev/null +++ b/tests/pass/intrinsics-x86-pause-without-sse2.rs @@ -0,0 +1,25 @@ +// Ignore everything except x86 and x86_64 +// Any new targets that are added to CI should be ignored here. +// (We cannot use `cfg`-based tricks here since the `target-feature` flags below only work on x86.) +//@ignore-target-aarch64 +//@ignore-target-arm +//@ignore-target-avr +//@ignore-target-s390x +//@ignore-target-thumbv7em +//@ignore-target-wasm32 +//@compile-flags: -C target-feature=-sse2 + +#[cfg(target_arch = "x86")] +use std::arch::x86::*; +#[cfg(target_arch = "x86_64")] +use std::arch::x86_64::*; + +fn main() { + assert!(!is_x86_feature_detected!("sse2")); + + unsafe { + // This is a SSE2 intrinsic, but it behaves as a no-op when SSE2 + // is not available, so it is always safe to call. + _mm_pause(); + } +} diff --git a/tests/pass/intrinsics-x86-sse2.rs b/tests/pass/intrinsics-x86-sse2.rs index e636d6c8aa..e0088b9eb2 100644 --- a/tests/pass/intrinsics-x86-sse2.rs +++ b/tests/pass/intrinsics-x86-sse2.rs @@ -54,6 +54,11 @@ mod tests { } } + fn test_mm_pause() { + unsafe { _mm_pause() } + } + test_mm_pause(); + #[target_feature(enable = "sse2")] unsafe fn test_mm_avg_epu8() { let (a, b) = (_mm_set1_epi8(3), _mm_set1_epi8(9)); From 96f39202308468bc232486bcfcb93e365d39b210 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Fri, 22 Mar 2024 16:03:56 +0100 Subject: [PATCH 10/14] Preparing for merge from rustc --- rust-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-version b/rust-version index 0cdb0de033..78a07cd99a 100644 --- a/rust-version +++ b/rust-version @@ -1 +1 @@ -148a41c6b5687f941c5256d9ef8145eb03b72094 +1447f9d38ca388ca178a544534b3cff72945fa1e From 056d1d72340002b64a6e86d15e7fc6ea6f4c976b Mon Sep 17 00:00:00 2001 From: The Miri Cronjob Bot Date: Sat, 23 Mar 2024 04:54:00 +0000 Subject: [PATCH 11/14] Preparing for merge from rustc --- rust-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-version b/rust-version index 78a07cd99a..9b89f016a7 100644 --- a/rust-version +++ b/rust-version @@ -1 +1 @@ -1447f9d38ca388ca178a544534b3cff72945fa1e +c3b05c6e5b5b59613350b8c2875b0add67ed74df From 4ee553ed20059d564f69b498577fc54293206234 Mon Sep 17 00:00:00 2001 From: The Miri Cronjob Bot Date: Sat, 23 Mar 2024 05:03:40 +0000 Subject: [PATCH 12/14] fmt --- src/machine.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/machine.rs b/src/machine.rs index 29315c4933..89bfbe8afa 100644 --- a/src/machine.rs +++ b/src/machine.rs @@ -372,10 +372,8 @@ pub struct PrimitiveLayouts<'tcx> { impl<'mir, 'tcx: 'mir> PrimitiveLayouts<'tcx> { fn new(layout_cx: LayoutCx<'tcx, TyCtxt<'tcx>>) -> Result> { let tcx = layout_cx.tcx; - let mut_raw_ptr = - Ty::new_mut_ptr(tcx, tcx.types.unit); - let const_raw_ptr = - Ty::new_imm_ptr(tcx, tcx.types.unit); + let mut_raw_ptr = Ty::new_mut_ptr(tcx, tcx.types.unit); + let const_raw_ptr = Ty::new_imm_ptr(tcx, tcx.types.unit); Ok(Self { unit: layout_cx.layout_of(Ty::new_unit(tcx))?, i8: layout_cx.layout_of(tcx.types.i8)?, From 9509f21f615d3b8da396438dca044f7229fbbc2a Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 23 Mar 2024 11:32:36 +0100 Subject: [PATCH 13/14] add support for missing SIMD float intrinsics --- src/shims/intrinsics/simd.rs | 44 ++++++++++++++++++++++++++++++------ tests/pass/portable-simd.rs | 18 +++++++++++++++ 2 files changed, 55 insertions(+), 7 deletions(-) diff --git a/src/shims/intrinsics/simd.rs b/src/shims/intrinsics/simd.rs index c97a052f51..af98b38af8 100644 --- a/src/shims/intrinsics/simd.rs +++ b/src/shims/intrinsics/simd.rs @@ -33,6 +33,13 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { | "round" | "trunc" | "fsqrt" + | "fsin" + | "fcos" + | "fexp" + | "fexp2" + | "flog" + | "flog2" + | "flog10" | "ctlz" | "cttz" | "bswap" @@ -45,17 +52,16 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { assert_eq!(dest_len, op_len); #[derive(Copy, Clone)] - enum Op { + enum Op<'a> { MirOp(mir::UnOp), Abs, - Sqrt, Round(rustc_apfloat::Round), Numeric(Symbol), + HostOp(&'a str), } let which = match intrinsic_name { "neg" => Op::MirOp(mir::UnOp::Neg), "fabs" => Op::Abs, - "fsqrt" => Op::Sqrt, "ceil" => Op::Round(rustc_apfloat::Round::TowardPositive), "floor" => Op::Round(rustc_apfloat::Round::TowardNegative), "round" => Op::Round(rustc_apfloat::Round::NearestTiesToAway), @@ -64,7 +70,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { "cttz" => Op::Numeric(sym::cttz), "bswap" => Op::Numeric(sym::bswap), "bitreverse" => Op::Numeric(sym::bitreverse), - _ => unreachable!(), + _ => Op::HostOp(intrinsic_name), }; for i in 0..dest_len { @@ -89,7 +95,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { FloatTy::F128 => unimplemented!("f16_f128"), } } - Op::Sqrt => { + Op::HostOp(host_op) => { let ty::Float(float_ty) = op.layout.ty.kind() else { span_bug!(this.cur_span(), "{} operand is not a float", intrinsic_name) }; @@ -98,13 +104,37 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { FloatTy::F16 => unimplemented!("f16_f128"), FloatTy::F32 => { let f = op.to_scalar().to_f32()?; - let res = f.to_host().sqrt().to_soft(); + let f_host = f.to_host(); + let res = match host_op { + "fsqrt" => f_host.sqrt(), + "fsin" => f_host.sin(), + "fcos" => f_host.cos(), + "fexp" => f_host.exp(), + "fexp2" => f_host.exp2(), + "flog" => f_host.ln(), + "flog2" => f_host.log2(), + "flog10" => f_host.log10(), + _ => bug!(), + }; + let res = res.to_soft(); let res = this.adjust_nan(res, &[f]); Scalar::from(res) } FloatTy::F64 => { let f = op.to_scalar().to_f64()?; - let res = f.to_host().sqrt().to_soft(); + let f_host = f.to_host(); + let res = match host_op { + "fsqrt" => f_host.sqrt(), + "fsin" => f_host.sin(), + "fcos" => f_host.cos(), + "fexp" => f_host.exp(), + "fexp2" => f_host.exp2(), + "flog" => f_host.ln(), + "flog2" => f_host.log2(), + "flog10" => f_host.log10(), + _ => bug!(), + }; + let res = res.to_soft(); let res = this.adjust_nan(res, &[f]); Scalar::from(res) } diff --git a/tests/pass/portable-simd.rs b/tests/pass/portable-simd.rs index 399913a757..cdb441b450 100644 --- a/tests/pass/portable-simd.rs +++ b/tests/pass/portable-simd.rs @@ -526,6 +526,23 @@ fn simd_intrinsics() { } } +fn simd_float_intrinsics() { + use intrinsics::*; + + // These are just smoke tests to ensure the intrinsics can be called. + unsafe { + let a = f32x4::splat(10.0); + simd_fsqrt(a); + simd_fsin(a); + simd_fcos(a); + simd_fexp(a); + simd_fexp2(a); + simd_flog(a); + simd_flog2(a); + simd_flog10(a); + } +} + fn simd_masked_loadstore() { // The buffer is deliberarely too short, so reading the last element would be UB. let buf = [3i32; 3]; @@ -559,5 +576,6 @@ fn main() { simd_gather_scatter(); simd_round(); simd_intrinsics(); + simd_float_intrinsics(); simd_masked_loadstore(); } From 3cf20a2703fbad75ca22b118b94be6a8e3f26f85 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20K=C3=A5re=20Alsaker?= Date: Sun, 17 Mar 2024 09:59:43 +0100 Subject: [PATCH 14/14] Report retags as distinct from real memory accesses for data races --- src/borrow_tracker/stacked_borrows/mod.rs | 17 ++- src/borrow_tracker/tree_borrows/mod.rs | 13 +- src/concurrency/data_race.rs | 141 ++++++++++-------- src/concurrency/vector_clock.rs | 62 +++++++- src/diagnostics.rs | 8 +- src/lib.rs | 1 + src/machine.rs | 15 +- .../both_borrows/retag_data_race_write.rs | 2 +- .../retag_data_race_write.stack.stderr | 7 +- .../retag_data_race_write.tree.stderr | 7 +- .../retag_data_race_protected_read.rs | 2 +- .../retag_data_race_protected_read.stderr | 7 +- .../stacked_borrows/retag_data_race_read.rs | 2 +- .../retag_data_race_read.stderr | 7 +- 14 files changed, 202 insertions(+), 89 deletions(-) diff --git a/src/borrow_tracker/stacked_borrows/mod.rs b/src/borrow_tracker/stacked_borrows/mod.rs index 9130601bbd..96d631b1e1 100644 --- a/src/borrow_tracker/stacked_borrows/mod.rs +++ b/src/borrow_tracker/stacked_borrows/mod.rs @@ -18,6 +18,7 @@ use crate::borrow_tracker::{ stacked_borrows::diagnostics::{AllocHistory, DiagnosticCx, DiagnosticCxBuilder}, GlobalStateInner, ProtectorKind, }; +use crate::concurrency::data_race::{NaReadType, NaWriteType}; use crate::*; use diagnostics::{RetagCause, RetagInfo}; @@ -751,7 +752,13 @@ trait EvalContextPrivExt<'mir: 'ecx, 'tcx: 'mir, 'ecx>: crate::MiriInterpCxExt<' assert_eq!(access, AccessKind::Write); // Make sure the data race model also knows about this. if let Some(data_race) = alloc_extra.data_race.as_mut() { - data_race.write(alloc_id, range, machine)?; + data_race.write( + alloc_id, + range, + NaWriteType::Retag, + Some(place.layout.ty), + machine, + )?; } } } @@ -794,7 +801,13 @@ trait EvalContextPrivExt<'mir: 'ecx, 'tcx: 'mir, 'ecx>: crate::MiriInterpCxExt<' assert_eq!(access, AccessKind::Read); // Make sure the data race model also knows about this. if let Some(data_race) = alloc_extra.data_race.as_ref() { - data_race.read(alloc_id, range, &this.machine)?; + data_race.read( + alloc_id, + range, + NaReadType::Retag, + Some(place.layout.ty), + &this.machine, + )?; } } Ok(()) diff --git a/src/borrow_tracker/tree_borrows/mod.rs b/src/borrow_tracker/tree_borrows/mod.rs index 9eb78b08ef..0516393500 100644 --- a/src/borrow_tracker/tree_borrows/mod.rs +++ b/src/borrow_tracker/tree_borrows/mod.rs @@ -9,8 +9,11 @@ use rustc_middle::{ use rustc_span::def_id::DefId; use rustc_target::abi::{Abi, Size}; -use crate::borrow_tracker::{GlobalState, GlobalStateInner, ProtectorKind}; use crate::*; +use crate::{ + borrow_tracker::{GlobalState, GlobalStateInner, ProtectorKind}, + concurrency::data_race::NaReadType, +}; pub mod diagnostics; mod perms; @@ -312,7 +315,13 @@ trait EvalContextPrivExt<'mir: 'ecx, 'tcx: 'mir, 'ecx>: crate::MiriInterpCxExt<' // Also inform the data race model (but only if any bytes are actually affected). if range.size.bytes() > 0 { if let Some(data_race) = alloc_extra.data_race.as_ref() { - data_race.read(alloc_id, range, &this.machine)?; + data_race.read( + alloc_id, + range, + NaReadType::Retag, + Some(place.layout.ty), + &this.machine, + )?; } } diff --git a/src/concurrency/data_race.rs b/src/concurrency/data_race.rs index 127d97bd5a..d51160b283 100644 --- a/src/concurrency/data_race.rs +++ b/src/concurrency/data_race.rs @@ -49,7 +49,7 @@ use std::{ use rustc_ast::Mutability; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_index::{Idx, IndexVec}; -use rustc_middle::mir; +use rustc_middle::{mir, ty::Ty}; use rustc_span::Span; use rustc_target::abi::{Align, HasDataLayout, Size}; @@ -200,18 +200,38 @@ enum AtomicAccessType { Rmw, } -/// Type of write operation: allocating memory -/// non-atomic writes and deallocating memory -/// are all treated as writes for the purpose -/// of the data-race detector. +/// Type of a non-atomic read operation. #[derive(Copy, Clone, PartialEq, Eq, Debug)] -enum NaWriteType { +pub enum NaReadType { + /// Standard unsynchronized write. + Read, + + // An implicit read generated by a retag. + Retag, +} + +impl NaReadType { + fn description(self) -> &'static str { + match self { + NaReadType::Read => "non-atomic read", + NaReadType::Retag => "retag read", + } + } +} + +/// Type of a non-atomic write operation: allocating memory, non-atomic writes, and +/// deallocating memory are all treated as writes for the purpose of the data-race detector. +#[derive(Copy, Clone, PartialEq, Eq, Debug)] +pub enum NaWriteType { /// Allocate memory. Allocate, /// Standard unsynchronized write. Write, + // An implicit write generated by a retag. + Retag, + /// Deallocate memory. /// Note that when memory is deallocated first, later non-atomic accesses /// will be reported as use-after-free, not as data races. @@ -224,6 +244,7 @@ impl NaWriteType { match self { NaWriteType::Allocate => "creating a new allocation", NaWriteType::Write => "non-atomic write", + NaWriteType::Retag => "retag write", NaWriteType::Deallocate => "deallocation", } } @@ -231,7 +252,7 @@ impl NaWriteType { #[derive(Copy, Clone, PartialEq, Eq, Debug)] enum AccessType { - NaRead, + NaRead(NaReadType), NaWrite(NaWriteType), AtomicLoad, AtomicStore, @@ -239,29 +260,48 @@ enum AccessType { } impl AccessType { - fn description(self) -> &'static str { - match self { - AccessType::NaRead => "non-atomic read", + fn description(self, ty: Option>, size: Option) -> String { + let mut msg = String::new(); + + if let Some(size) = size { + msg.push_str(&format!("{}-byte {}", size.bytes(), msg)) + } + + msg.push_str(match self { + AccessType::NaRead(w) => w.description(), AccessType::NaWrite(w) => w.description(), AccessType::AtomicLoad => "atomic load", AccessType::AtomicStore => "atomic store", AccessType::AtomicRmw => "atomic read-modify-write", + }); + + if let Some(ty) = ty { + msg.push_str(&format!(" of type `{}`", ty)); } + + msg } fn is_atomic(self) -> bool { match self { AccessType::AtomicLoad | AccessType::AtomicStore | AccessType::AtomicRmw => true, - AccessType::NaRead | AccessType::NaWrite(_) => false, + AccessType::NaRead(_) | AccessType::NaWrite(_) => false, } } fn is_read(self) -> bool { match self { - AccessType::AtomicLoad | AccessType::NaRead => true, + AccessType::AtomicLoad | AccessType::NaRead(_) => true, AccessType::NaWrite(_) | AccessType::AtomicStore | AccessType::AtomicRmw => false, } } + + fn is_retag(self) -> bool { + matches!( + self, + AccessType::NaRead(NaReadType::Retag) | AccessType::NaWrite(NaWriteType::Retag) + ) + } } /// Memory Cell vector clock metadata @@ -502,12 +542,14 @@ impl MemoryCellClocks { &mut self, thread_clocks: &mut ThreadClockSet, index: VectorIdx, + read_type: NaReadType, current_span: Span, ) -> Result<(), DataRace> { trace!("Unsynchronized read with vectors: {:#?} :: {:#?}", self, thread_clocks); if !current_span.is_dummy() { thread_clocks.clock[index].span = current_span; } + thread_clocks.clock[index].set_read_type(read_type); if self.write_was_before(&thread_clocks.clock) { let race_free = if let Some(atomic) = self.atomic() { // We must be ordered-after all atomic accesses, reads and writes. @@ -875,7 +917,8 @@ impl VClockAlloc { /// This finds the two racing threads and the type /// of data-race that occurred. This will also /// return info about the memory location the data-race - /// occurred in. + /// occurred in. The `ty` parameter is used for diagnostics, letting + /// the user know which type was involved in the access. #[cold] #[inline(never)] fn report_data_race<'tcx>( @@ -885,6 +928,7 @@ impl VClockAlloc { access: AccessType, access_size: Size, ptr_dbg: Pointer, + ty: Option>, ) -> InterpResult<'tcx> { let (current_index, current_clocks) = global.current_thread_state(thread_mgr); let mut other_size = None; // if `Some`, this was a size-mismatch race @@ -908,7 +952,7 @@ impl VClockAlloc { write_clock = mem_clocks.write(); (AccessType::NaWrite(mem_clocks.write_type), mem_clocks.write.0, &write_clock) } else if let Some(idx) = Self::find_gt_index(&mem_clocks.read, ¤t_clocks.clock) { - (AccessType::NaRead, idx, &mem_clocks.read) + (AccessType::NaRead(mem_clocks.read[idx].read_type()), idx, &mem_clocks.read) // Finally, mixed-size races. } else if access.is_atomic() && let Some(atomic) = mem_clocks.atomic() && atomic.size != access_size { // This is only a race if we are not synchronized with all atomic accesses, so find @@ -950,37 +994,33 @@ impl VClockAlloc { Err(err_machine_stop!(TerminationInfo::DataRace { involves_non_atomic, extra, + retag_explain: access.is_retag() || other_access.is_retag(), ptr: ptr_dbg, op1: RacingOp { - action: if let Some(other_size) = other_size { - format!("{}-byte {}", other_size.bytes(), other_access.description()) - } else { - other_access.description().to_owned() - }, + action: other_access.description(None, other_size), thread_info: other_thread_info, span: other_clock.as_slice()[other_thread.index()].span_data(), }, op2: RacingOp { - action: if other_size.is_some() { - format!("{}-byte {}", access_size.bytes(), access.description()) - } else { - access.description().to_owned() - }, + action: access.description(ty, other_size.map(|_| access_size)), thread_info: current_thread_info, span: current_clocks.clock.as_slice()[current_index.index()].span_data(), }, }))? } - /// Detect data-races for an unsynchronized read operation, will not perform + /// Detect data-races for an unsynchronized read operation. It will not perform /// data-race detection if `race_detecting()` is false, either due to no threads /// being created or if it is temporarily disabled during a racy read or write /// operation for which data-race detection is handled separately, for example - /// atomic read operations. + /// atomic read operations. The `ty` parameter is used for diagnostics, letting + /// the user know which type was read. pub fn read<'tcx>( &self, alloc_id: AllocId, access_range: AllocRange, + read_type: NaReadType, + ty: Option>, machine: &MiriMachine<'_, '_>, ) -> InterpResult<'tcx> { let current_span = machine.current_span(); @@ -992,7 +1032,7 @@ impl VClockAlloc { alloc_ranges.iter_mut(access_range.start, access_range.size) { if let Err(DataRace) = - mem_clocks.read_race_detect(&mut thread_clocks, index, current_span) + mem_clocks.read_race_detect(&mut thread_clocks, index, read_type, current_span) { drop(thread_clocks); // Report data-race. @@ -1000,9 +1040,10 @@ impl VClockAlloc { global, &machine.threads, mem_clocks, - AccessType::NaRead, + AccessType::NaRead(read_type), access_range.size, Pointer::new(alloc_id, Size::from_bytes(mem_clocks_range.start)), + ty, ); } } @@ -1012,12 +1053,17 @@ impl VClockAlloc { } } - // Shared code for detecting data-races on unique access to a section of memory - fn unique_access<'tcx>( + /// Detect data-races for an unsynchronized write operation. It will not perform + /// data-race detection if `race_detecting()` is false, either due to no threads + /// being created or if it is temporarily disabled during a racy read or write + /// operation. The `ty` parameter is used for diagnostics, letting + /// the user know which type was written. + pub fn write<'tcx>( &mut self, alloc_id: AllocId, access_range: AllocRange, write_type: NaWriteType, + ty: Option>, machine: &mut MiriMachine<'_, '_>, ) -> InterpResult<'tcx> { let current_span = machine.current_span(); @@ -1042,6 +1088,7 @@ impl VClockAlloc { AccessType::NaWrite(write_type), access_range.size, Pointer::new(alloc_id, Size::from_bytes(mem_clocks_range.start)), + ty, ); } } @@ -1050,37 +1097,6 @@ impl VClockAlloc { Ok(()) } } - - /// Detect data-races for an unsynchronized write operation, will not perform - /// data-race threads if `race_detecting()` is false, either due to no threads - /// being created or if it is temporarily disabled during a racy read or write - /// operation - pub fn write<'tcx>( - &mut self, - alloc_id: AllocId, - range: AllocRange, - machine: &mut MiriMachine<'_, '_>, - ) -> InterpResult<'tcx> { - self.unique_access(alloc_id, range, NaWriteType::Write, machine) - } - - /// Detect data-races for an unsynchronized deallocate operation, will not perform - /// data-race threads if `race_detecting()` is false, either due to no threads - /// being created or if it is temporarily disabled during a racy read or write - /// operation - pub fn deallocate<'tcx>( - &mut self, - alloc_id: AllocId, - size: Size, - machine: &mut MiriMachine<'_, '_>, - ) -> InterpResult<'tcx> { - self.unique_access( - alloc_id, - alloc_range(Size::ZERO, size), - NaWriteType::Deallocate, - machine, - ) - } } impl<'mir, 'tcx: 'mir> EvalContextPrivExt<'mir, 'tcx> for MiriInterpCx<'mir, 'tcx> {} @@ -1279,7 +1295,7 @@ trait EvalContextPrivExt<'mir, 'tcx: 'mir>: MiriInterpCxExt<'mir, 'tcx> { let alloc_meta = this.get_alloc_extra(alloc_id)?.data_race.as_ref().unwrap(); trace!( "Atomic op({}) with ordering {:?} on {:?} (size={})", - access.description(), + access.description(None, None), &atomic, place.ptr(), size.bytes() @@ -1307,6 +1323,7 @@ trait EvalContextPrivExt<'mir, 'tcx: 'mir>: MiriInterpCxExt<'mir, 'tcx> { alloc_id, Size::from_bytes(mem_clocks_range.start), ), + None, ) .map(|_| true); } diff --git a/src/concurrency/vector_clock.rs b/src/concurrency/vector_clock.rs index fa93c9e00b..fe719943dc 100644 --- a/src/concurrency/vector_clock.rs +++ b/src/concurrency/vector_clock.rs @@ -4,9 +4,11 @@ use smallvec::SmallVec; use std::{ cmp::Ordering, fmt::Debug, - ops::{Index, IndexMut}, + ops::{Index, IndexMut, Shr}, }; +use super::data_race::NaReadType; + /// A vector clock index, this is associated with a thread id /// but in some cases one vector index may be shared with /// multiple thread ids if it's safe to do so. @@ -50,13 +52,51 @@ const SMALL_VECTOR: usize = 4; /// so that diagnostics can report what code was responsible for an operation. #[derive(Clone, Copy, Debug)] pub struct VTimestamp { - time: u32, + /// The lowest bit indicates read type, the rest is the time. + /// `1` indicates a retag read, `0` a regular read. + time_and_read_type: u32, pub span: Span, } impl VTimestamp { - pub const ZERO: VTimestamp = VTimestamp { time: 0, span: DUMMY_SP }; + pub const ZERO: VTimestamp = VTimestamp::new(0, NaReadType::Read, DUMMY_SP); + + #[inline] + const fn encode_time_and_read_type(time: u32, read_type: NaReadType) -> u32 { + let read_type_bit = match read_type { + NaReadType::Read => 0, + NaReadType::Retag => 1, + }; + // Put the `read_type` in the lowest bit and `time` in the rest + read_type_bit | time.checked_mul(2).expect("Vector clock overflow") + } + + #[inline] + const fn new(time: u32, read_type: NaReadType, span: Span) -> Self { + Self { time_and_read_type: Self::encode_time_and_read_type(time, read_type), span } + } + + #[inline] + fn time(&self) -> u32 { + self.time_and_read_type.shr(1) + } + #[inline] + fn set_time(&mut self, time: u32) { + self.time_and_read_type = Self::encode_time_and_read_type(time, self.read_type()); + } + + #[inline] + pub fn read_type(&self) -> NaReadType { + if self.time_and_read_type & 1 == 0 { NaReadType::Read } else { NaReadType::Retag } + } + + #[inline] + pub fn set_read_type(&mut self, read_type: NaReadType) { + self.time_and_read_type = Self::encode_time_and_read_type(self.time(), read_type); + } + + #[inline] pub fn span_data(&self) -> SpanData { self.span.data() } @@ -64,7 +104,7 @@ impl VTimestamp { impl PartialEq for VTimestamp { fn eq(&self, other: &Self) -> bool { - self.time == other.time + self.time() == other.time() } } @@ -78,7 +118,7 @@ impl PartialOrd for VTimestamp { impl Ord for VTimestamp { fn cmp(&self, other: &Self) -> Ordering { - self.time.cmp(&other.time) + self.time().cmp(&other.time()) } } @@ -130,7 +170,7 @@ impl VClock { let idx = idx.index(); let mut_slice = self.get_mut_with_min_len(idx + 1); let idx_ref = &mut mut_slice[idx]; - idx_ref.time = idx_ref.time.checked_add(1).expect("Vector clock overflow"); + idx_ref.set_time(idx_ref.time().checked_add(1).expect("Vector clock overflow")); if !current_span.is_dummy() { idx_ref.span = current_span; } @@ -379,8 +419,8 @@ impl IndexMut for VClock { /// test suite #[cfg(test)] mod tests { - use super::{VClock, VTimestamp, VectorIdx}; + use crate::concurrency::data_race::NaReadType; use rustc_span::DUMMY_SP; use std::cmp::Ordering; @@ -448,7 +488,13 @@ mod tests { while let Some(0) = slice.last() { slice = &slice[..slice.len() - 1] } - VClock(slice.iter().copied().map(|time| VTimestamp { time, span: DUMMY_SP }).collect()) + VClock( + slice + .iter() + .copied() + .map(|time| VTimestamp::new(time, NaReadType::Read, DUMMY_SP)) + .collect(), + ) } fn assert_order(l: &[u32], r: &[u32], o: Option) { diff --git a/src/diagnostics.rs b/src/diagnostics.rs index 03428b081c..99d37065ba 100644 --- a/src/diagnostics.rs +++ b/src/diagnostics.rs @@ -46,6 +46,7 @@ pub enum TerminationInfo { op1: RacingOp, op2: RacingOp, extra: Option<&'static str>, + retag_explain: bool, }, } @@ -263,12 +264,17 @@ pub fn report_error<'tcx, 'mir>( vec![(Some(*span), format!("the `{link_name}` symbol is defined here"))], Int2PtrWithStrictProvenance => vec![(None, format!("use Strict Provenance APIs (https://doc.rust-lang.org/nightly/std/ptr/index.html#strict-provenance, https://crates.io/crates/sptr) instead"))], - DataRace { op1, extra, .. } => { + DataRace { op1, extra, retag_explain, .. } => { let mut helps = vec![(Some(op1.span), format!("and (1) occurred earlier here"))]; if let Some(extra) = extra { helps.push((None, format!("{extra}"))); helps.push((None, format!("see https://doc.rust-lang.org/nightly/std/sync/atomic/index.html#memory-model-for-atomic-accesses for more information about the Rust memory model"))); } + if *retag_explain { + helps.push((None, "retags occur on all (re)borrows and as well as when references are copied or moved".to_owned())); + helps.push((None, "retags permit optimizations that insert speculative reads or writes".to_owned())); + helps.push((None, "therefore from the perspective of data races, a retag has the same implications as a read or write".to_owned())); + } helps.push((None, format!("this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior"))); helps.push((None, format!("see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information"))); helps diff --git a/src/lib.rs b/src/lib.rs index 416d0cda8f..7821aa9efd 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,5 +1,6 @@ #![feature(rustc_private)] #![feature(cell_update)] +#![feature(const_option)] #![feature(float_gamma)] #![feature(generic_nonzero)] #![feature(map_try_insert)] diff --git a/src/machine.rs b/src/machine.rs index 7e5518392d..71a4f2ca87 100644 --- a/src/machine.rs +++ b/src/machine.rs @@ -36,6 +36,9 @@ use crate::{ *, }; +use self::concurrency::data_race::NaReadType; +use self::concurrency::data_race::NaWriteType; + /// First real-time signal. /// `signal(7)` says this must be between 32 and 64 and specifies 34 or 35 /// as typical values. @@ -1241,7 +1244,7 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for MiriMachine<'mir, 'tcx> { .emit_diagnostic(NonHaltingDiagnostic::AccessedAlloc(alloc_id, AccessKind::Read)); } if let Some(data_race) = &alloc_extra.data_race { - data_race.read(alloc_id, range, machine)?; + data_race.read(alloc_id, range, NaReadType::Read, None, machine)?; } if let Some(borrow_tracker) = &alloc_extra.borrow_tracker { borrow_tracker.before_memory_read(alloc_id, prov_extra, range, machine)?; @@ -1265,7 +1268,7 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for MiriMachine<'mir, 'tcx> { .emit_diagnostic(NonHaltingDiagnostic::AccessedAlloc(alloc_id, AccessKind::Write)); } if let Some(data_race) = &mut alloc_extra.data_race { - data_race.write(alloc_id, range, machine)?; + data_race.write(alloc_id, range, NaWriteType::Write, None, machine)?; } if let Some(borrow_tracker) = &mut alloc_extra.borrow_tracker { borrow_tracker.before_memory_write(alloc_id, prov_extra, range, machine)?; @@ -1289,7 +1292,13 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for MiriMachine<'mir, 'tcx> { machine.emit_diagnostic(NonHaltingDiagnostic::FreedAlloc(alloc_id)); } if let Some(data_race) = &mut alloc_extra.data_race { - data_race.deallocate(alloc_id, size, machine)?; + data_race.write( + alloc_id, + alloc_range(Size::ZERO, size), + NaWriteType::Deallocate, + None, + machine, + )?; } if let Some(borrow_tracker) = &mut alloc_extra.borrow_tracker { borrow_tracker.before_memory_deallocation(alloc_id, prove_extra, size, machine)?; diff --git a/tests/fail/both_borrows/retag_data_race_write.rs b/tests/fail/both_borrows/retag_data_race_write.rs index eb1fe56df0..3edaf10f3d 100644 --- a/tests/fail/both_borrows/retag_data_race_write.rs +++ b/tests/fail/both_borrows/retag_data_race_write.rs @@ -17,7 +17,7 @@ fn thread_1(p: SendPtr) { fn thread_2(p: SendPtr) { let p = p.0; unsafe { - *p = 5; //~ ERROR: /Data race detected between \(1\) non-atomic (read|write) on thread `unnamed-[0-9]+` and \(2\) non-atomic write on thread `unnamed-[0-9]+`/ + *p = 5; //~ ERROR: /Data race detected between \(1\) retag (read|write) on thread `unnamed-[0-9]+` and \(2\) non-atomic write on thread `unnamed-[0-9]+`/ } } diff --git a/tests/fail/both_borrows/retag_data_race_write.stack.stderr b/tests/fail/both_borrows/retag_data_race_write.stack.stderr index c5b65e6f74..6f4b52fb88 100644 --- a/tests/fail/both_borrows/retag_data_race_write.stack.stderr +++ b/tests/fail/both_borrows/retag_data_race_write.stack.stderr @@ -1,14 +1,17 @@ -error: Undefined Behavior: Data race detected between (1) non-atomic write on thread `unnamed-ID` and (2) non-atomic write on thread `unnamed-ID` at ALLOC. (2) just happened here +error: Undefined Behavior: Data race detected between (1) retag write on thread `unnamed-ID` and (2) non-atomic write on thread `unnamed-ID` at ALLOC. (2) just happened here --> $DIR/retag_data_race_write.rs:LL:CC | LL | *p = 5; - | ^^^^^^ Data race detected between (1) non-atomic write on thread `unnamed-ID` and (2) non-atomic write on thread `unnamed-ID` at ALLOC. (2) just happened here + | ^^^^^^ Data race detected between (1) retag write on thread `unnamed-ID` and (2) non-atomic write on thread `unnamed-ID` at ALLOC. (2) just happened here | help: and (1) occurred earlier here --> $DIR/retag_data_race_write.rs:LL:CC | LL | let _r = &mut *p; | ^^^^^^^ + = help: retags occur on all (re)borrows and as well as when references are copied or moved + = help: retags permit optimizations that insert speculative reads or writes + = help: therefore from the perspective of data races, a retag has the same implications as a read or write = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information = note: BACKTRACE (of the first span) on thread `unnamed-ID`: diff --git a/tests/fail/both_borrows/retag_data_race_write.tree.stderr b/tests/fail/both_borrows/retag_data_race_write.tree.stderr index 62f139f6f0..fa0012f9b2 100644 --- a/tests/fail/both_borrows/retag_data_race_write.tree.stderr +++ b/tests/fail/both_borrows/retag_data_race_write.tree.stderr @@ -1,14 +1,17 @@ -error: Undefined Behavior: Data race detected between (1) non-atomic read on thread `unnamed-ID` and (2) non-atomic write on thread `unnamed-ID` at ALLOC. (2) just happened here +error: Undefined Behavior: Data race detected between (1) retag read on thread `unnamed-ID` and (2) non-atomic write on thread `unnamed-ID` at ALLOC. (2) just happened here --> $DIR/retag_data_race_write.rs:LL:CC | LL | *p = 5; - | ^^^^^^ Data race detected between (1) non-atomic read on thread `unnamed-ID` and (2) non-atomic write on thread `unnamed-ID` at ALLOC. (2) just happened here + | ^^^^^^ Data race detected between (1) retag read on thread `unnamed-ID` and (2) non-atomic write on thread `unnamed-ID` at ALLOC. (2) just happened here | help: and (1) occurred earlier here --> $DIR/retag_data_race_write.rs:LL:CC | LL | let _r = &mut *p; | ^^^^^^^ + = help: retags occur on all (re)borrows and as well as when references are copied or moved + = help: retags permit optimizations that insert speculative reads or writes + = help: therefore from the perspective of data races, a retag has the same implications as a read or write = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information = note: BACKTRACE (of the first span) on thread `unnamed-ID`: diff --git a/tests/fail/stacked_borrows/retag_data_race_protected_read.rs b/tests/fail/stacked_borrows/retag_data_race_protected_read.rs index 5db89c89b7..3de517055e 100644 --- a/tests/fail/stacked_borrows/retag_data_race_protected_read.rs +++ b/tests/fail/stacked_borrows/retag_data_race_protected_read.rs @@ -13,7 +13,7 @@ fn main() { let ptr = ptr; // We do a protected mutable retag (but no write!) in this thread. fn retag(_x: &mut i32) {} - retag(unsafe { &mut *ptr.0 }); //~ERROR: Data race detected between (1) non-atomic read on thread `main` and (2) non-atomic write on thread `unnamed-1` + retag(unsafe { &mut *ptr.0 }); //~ERROR: Data race detected between (1) non-atomic read on thread `main` and (2) retag write of type `i32` on thread `unnamed-1` }); // We do a read in the main thread. diff --git a/tests/fail/stacked_borrows/retag_data_race_protected_read.stderr b/tests/fail/stacked_borrows/retag_data_race_protected_read.stderr index 2ce757013d..47ae4b5d46 100644 --- a/tests/fail/stacked_borrows/retag_data_race_protected_read.stderr +++ b/tests/fail/stacked_borrows/retag_data_race_protected_read.stderr @@ -1,14 +1,17 @@ -error: Undefined Behavior: Data race detected between (1) non-atomic read on thread `main` and (2) non-atomic write on thread `unnamed-ID` at ALLOC. (2) just happened here +error: Undefined Behavior: Data race detected between (1) non-atomic read on thread `main` and (2) retag write of type `i32` on thread `unnamed-ID` at ALLOC. (2) just happened here --> $DIR/retag_data_race_protected_read.rs:LL:CC | LL | retag(unsafe { &mut *ptr.0 }); - | ^^^^^^^^^^^ Data race detected between (1) non-atomic read on thread `main` and (2) non-atomic write on thread `unnamed-ID` at ALLOC. (2) just happened here + | ^^^^^^^^^^^ Data race detected between (1) non-atomic read on thread `main` and (2) retag write of type `i32` on thread `unnamed-ID` at ALLOC. (2) just happened here | help: and (1) occurred earlier here --> $DIR/retag_data_race_protected_read.rs:LL:CC | LL | unsafe { ptr.0.read() }; | ^^^^^^^^^^^^ + = help: retags occur on all (re)borrows and as well as when references are copied or moved + = help: retags permit optimizations that insert speculative reads or writes + = help: therefore from the perspective of data races, a retag has the same implications as a read or write = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information = note: BACKTRACE (of the first span) on thread `unnamed-ID`: diff --git a/tests/fail/stacked_borrows/retag_data_race_read.rs b/tests/fail/stacked_borrows/retag_data_race_read.rs index 01a2e9ac47..25c92ddf6c 100644 --- a/tests/fail/stacked_borrows/retag_data_race_read.rs +++ b/tests/fail/stacked_borrows/retag_data_race_read.rs @@ -15,7 +15,7 @@ fn thread_1(p: SendPtr) { fn thread_2(p: SendPtr) { let p = p.0; unsafe { - *p = 5; //~ ERROR: Data race detected between (1) non-atomic read on thread `unnamed-1` and (2) non-atomic write on thread `unnamed-2` + *p = 5; //~ ERROR: Data race detected between (1) retag read on thread `unnamed-1` and (2) non-atomic write on thread `unnamed-2` } } diff --git a/tests/fail/stacked_borrows/retag_data_race_read.stderr b/tests/fail/stacked_borrows/retag_data_race_read.stderr index d3c8d14e2a..9fe9fbeda4 100644 --- a/tests/fail/stacked_borrows/retag_data_race_read.stderr +++ b/tests/fail/stacked_borrows/retag_data_race_read.stderr @@ -1,14 +1,17 @@ -error: Undefined Behavior: Data race detected between (1) non-atomic read on thread `unnamed-ID` and (2) non-atomic write on thread `unnamed-ID` at ALLOC. (2) just happened here +error: Undefined Behavior: Data race detected between (1) retag read on thread `unnamed-ID` and (2) non-atomic write on thread `unnamed-ID` at ALLOC. (2) just happened here --> $DIR/retag_data_race_read.rs:LL:CC | LL | *p = 5; - | ^^^^^^ Data race detected between (1) non-atomic read on thread `unnamed-ID` and (2) non-atomic write on thread `unnamed-ID` at ALLOC. (2) just happened here + | ^^^^^^ Data race detected between (1) retag read on thread `unnamed-ID` and (2) non-atomic write on thread `unnamed-ID` at ALLOC. (2) just happened here | help: and (1) occurred earlier here --> $DIR/retag_data_race_read.rs:LL:CC | LL | let _r = &*p; | ^^^ + = help: retags occur on all (re)borrows and as well as when references are copied or moved + = help: retags permit optimizations that insert speculative reads or writes + = help: therefore from the perspective of data races, a retag has the same implications as a read or write = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information = note: BACKTRACE (of the first span) on thread `unnamed-ID`: