mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-27 01:04:03 +00:00
Auto merge of #122952 - RalfJung:miri, r=RalfJung
Miri subtree update r? `@ghost`
This commit is contained in:
commit
e50ab29471
2
src/tools/miri/.github/workflows/ci.yml
vendored
2
src/tools/miri/.github/workflows/ci.yml
vendored
@ -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 }}
|
||||
|
@ -3,5 +3,5 @@
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "invalidate"
|
||||
name = "range-iteration"
|
||||
version = "0.1.0"
|
@ -1,5 +1,5 @@
|
||||
[package]
|
||||
name = "invalidate"
|
||||
name = "range-iteration"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
@ -1,4 +1,5 @@
|
||||
//! 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..200_000 {}
|
||||
for _ in 0..50_000 {}
|
||||
}
|
@ -1 +1 @@
|
||||
ee03c286cfdca26fa5b2a4ee40957625d2c826ff
|
||||
c3b05c6e5b5b59613350b8c2875b0add67ed74df
|
||||
|
@ -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) -> ! {
|
||||
|
@ -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(())
|
||||
|
@ -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,
|
||||
)?;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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<Ty<'_>>, size: Option<Size>) -> 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<AllocId>,
|
||||
ty: Option<Ty<'_>>,
|
||||
) -> 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<Ty<'_>>,
|
||||
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<Ty<'_>>,
|
||||
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);
|
||||
}
|
||||
|
@ -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<VectorIdx> 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<Ordering>) {
|
||||
|
@ -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
|
||||
|
@ -1,5 +1,6 @@
|
||||
#![feature(rustc_private)]
|
||||
#![feature(cell_update)]
|
||||
#![feature(const_option)]
|
||||
#![feature(float_gamma)]
|
||||
#![feature(generic_nonzero)]
|
||||
#![feature(map_try_insert)]
|
||||
|
@ -35,6 +35,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.
|
||||
@ -372,10 +375,8 @@ pub struct PrimitiveLayouts<'tcx> {
|
||||
impl<'mir, 'tcx: 'mir> PrimitiveLayouts<'tcx> {
|
||||
fn new(layout_cx: LayoutCx<'tcx, TyCtxt<'tcx>>) -> Result<Self, &'tcx LayoutError<'tcx>> {
|
||||
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)?,
|
||||
@ -1240,7 +1241,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)?;
|
||||
@ -1264,7 +1265,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)?;
|
||||
@ -1288,7 +1289,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)?;
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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)
|
||||
|
@ -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>(T);
|
||||
/// impl<T> Fail<T> {
|
||||
/// const C: () = panic!();
|
||||
/// }
|
||||
///
|
||||
/// let _val = Fail::<i32>::C;
|
||||
/// ```
|
||||
#[no_mangle]
|
||||
pub fn make_true() -> bool {
|
||||
issue_1567::use_the_dependency();
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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]+`/
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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`:
|
||||
|
@ -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`:
|
||||
|
@ -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.
|
||||
|
@ -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`:
|
||||
|
@ -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`
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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`:
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
@ -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));
|
||||
|
@ -1,5 +0,0 @@
|
||||
mod foo {
|
||||
pub(crate) fn bar() {}
|
||||
}
|
||||
|
||||
use foo::bar as main;
|
@ -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();
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user