diff --git a/src/tools/miri/README.md b/src/tools/miri/README.md index ee77ea4d64597..d674774184a22 100644 --- a/src/tools/miri/README.md +++ b/src/tools/miri/README.md @@ -162,7 +162,7 @@ endian-sensitive code. ### Controlling target features Controlling target features works similar to regular rustc invocations: -`RUSTFLAGS="-Ctarget-features=+avx512f" cargo miri test` runs the tests with AVX512 enabled. (Miri +`RUSTFLAGS="-Ctarget-feature=+avx512f" cargo miri test` runs the tests with AVX512 enabled. (Miri only supports very few AVX512 intrinsics at the moment.) `-Ctarget-cpu` also works. If target features are also relevant for doctests, you have to also set `RUSTDOCFLAGS`. diff --git a/src/tools/miri/ci/ci.sh b/src/tools/miri/ci/ci.sh index 42107adfe566a..503cc68fbc834 100755 --- a/src/tools/miri/ci/ci.sh +++ b/src/tools/miri/ci/ci.sh @@ -178,6 +178,7 @@ case $HOST_TARGET in # Not officially supported tier 2 MANY_SEEDS=16 TEST_TARGET=x86_64-pc-solaris run_tests MANY_SEEDS=16 TEST_TARGET=mips-unknown-linux-gnu run_tests # a 32bit big-endian target, and also a target without 64bit atomics + MANY_SEEDS=16 TEST_TARGET=riscv64a23-unknown-linux-gnu run_tests ;; aarch64-apple-darwin) # Host diff --git a/src/tools/miri/priroda/README.md b/src/tools/miri/priroda/README.md index b9d946e08e544..a8c25bf279868 100644 --- a/src/tools/miri/priroda/README.md +++ b/src/tools/miri/priroda/README.md @@ -9,6 +9,8 @@ Current focus: - source-location output after stepping - source-location breakpoint prototype - source-local listing prototype +- runtime local state and value rendering +- range-limited byte output for indirect locals ## Setup @@ -65,8 +67,35 @@ RUSTC_BLESS=1 cargo test | `c`, `continue` | Continue until the program finishes or reaches a breakpoint. | | `b :`, `break :` | Add a source-location breakpoint. | | `l`, `locals` | List source-level locals in the current frame by name. | +| `p `, `print ` | Print one MIR local by numeric id. | +| `f `, `follow ` | Render allocation bytes from an offset, including the full allocation size. | | `q`, `quit` | Exit Priroda. | +## Value Output + +Immediate values use Miri's `Immediate` display representation. Indirect +locals are rendered as the bytes belonging to the current value range, not as +the entire backing allocation: + +```text +[01 02 03] +[?? ?? ??] +``` + +`??` means the byte is uninitialized. A value whose runtime size cannot be +determined is reported as ``. + +Pointer/provenance spans are planned as part of the raw byte output, using a +compact dump-like marker such as: + +```text +[ 2a 00 00 00] +``` + +Automatic pointer following is future work and should be explicit, not part of +ordinary value printing. Typed field rendering and dereference/projection-aware +printing are also future work. + EOF also exits Priroda cleanly. Example: diff --git a/src/tools/miri/priroda/src/main.rs b/src/tools/miri/priroda/src/main.rs index 43dba77018276..e748ae7340aa2 100644 --- a/src/tools/miri/priroda/src/main.rs +++ b/src/tools/miri/priroda/src/main.rs @@ -1,6 +1,7 @@ #![feature(rustc_private)] extern crate miri; +extern crate rustc_abi; extern crate rustc_codegen_ssa; extern crate rustc_data_structures; extern crate rustc_driver; @@ -16,13 +17,17 @@ extern crate rustc_type_ir; use std::collections::{HashMap, HashSet}; use std::io::{self, Write}; +use std::num::NonZeroU64; +use std::ops::Range; use std::path::PathBuf; -use miri::Immediate::{Scalar, ScalarPair, Uninit}; -use miri::*; +use miri::Immediate::Uninit; +use miri::{interpret, *}; +use rustc_abi::Size; use rustc_driver::Compilation; use rustc_hir::attrs::CrateType; use rustc_interface::interface; +use rustc_middle::mir::interpret::AllocId; use rustc_middle::mir::{self, Local, ProjectionElem, VarDebugInfoContents, VarDebugInfoFragment}; use rustc_middle::ty::{TyCtxt, TyKind}; use rustc_session::EarlyDiagCtxt; @@ -377,10 +382,25 @@ impl<'tcx> PrirodaContext<'tcx> { DebuggerCommand::ListLocals => interp_ok(CommandResult::Locals(self.list_locals())), DebuggerCommand::Print(local) => interp_ok(CommandResult::SingleLocal(self.get_local(local))), + DebuggerCommand::Follow(alloc_id, offset) => + self.follow_alloc(alloc_id, offset).map(CommandResult::Memory), DebuggerCommand::TerminateSession => interp_ok(CommandResult::TerminateSession), } } + fn follow_alloc(&self, alloc_id: AllocId, offset: usize) -> InterpResult<'tcx, String> { + let alloc = self.ecx.get_alloc_raw(alloc_id)?; + if offset > alloc.len() { + return Err(miri::err_unsup_format!( + "allocation offset {offset} is outside {alloc_id}" + )) + .into(); + } + + let memory = self.render_alloc_bytes(alloc_id, offset..alloc.len())?; + interp_ok(format!("Allocation {alloc_id}+{offset}: {memory}")) + } + fn get_local(&self, local: usize) -> Option { let frame = self.ecx.active_thread_stack().last()?; @@ -399,6 +419,106 @@ impl<'tcx> PrirodaContext<'tcx> { self.build_local_descs(frame) } + /// Renders the current byte range of an indirect MIR value. + /// + /// Initialized bytes are shown in hexadecimal, uninitialized bytes as `??`, + /// and complete pointer-sized provenance as pointer markers. + fn render_mplace_bytes(&self, mplace: &MPlaceTy<'tcx>) -> InterpResult<'tcx, String> { + let size = match self.ecx.size_and_align_of_val(mplace)? { + Some((size, _)) => size, + None => { + // Extern types cannot currently be executed as by-value locals, + // so this path cannot yet be covered by a Priroda UI fixture. + // FIXME: Add coverage once Priroda supports printing dereferenced places. + return interp_ok("".to_string()); + } + }; + + let size = size.bytes_usize(); + if size == 0 { + return interp_ok("[]".to_string()); + } + + let (alloc_id, offset, _) = + self.ecx.ptr_get_alloc_id(mplace.ptr(), size.try_into().unwrap())?; + let offset = offset.bytes_usize(); + let range = offset..offset.strict_add(size); + + self.render_alloc_bytes(alloc_id, range) + } + + /// Render a raw allocation range without requiring a typed memory place. + /// + /// This is also used by the future-facing `follow` command, where we have a + /// pointer target but do not yet know the target's type or size. + fn render_alloc_bytes( + &self, + alloc_id: AllocId, + range: Range, + ) -> InterpResult<'tcx, String> { + let alloc = self.ecx.get_alloc_raw(alloc_id)?; + + let mut rendered = Vec::with_capacity(range.len()); + + let ptr_size = self.ecx.tcx.data_layout.pointer_size(); + + for chunk in alloc.init_mask().range_as_init_chunks(range.into()) { + let chunk_range = chunk.range(); + let chunk_range = chunk_range.start.bytes_usize()..chunk_range.end.bytes_usize(); + + if chunk.is_init() { + let ptr_size = ptr_size.bytes_usize(); + let mut cursor = chunk_range.start; + + while cursor < chunk_range.end { + // Full pointer provenance is rendered as a pointer marker. Bytewise + // provenance fragments are intentionally left as raw bytes here: they do + // not represent a complete pointer-sized value. + if let Some(prov) = alloc.provenance().get_ptr(Size::from_bytes(cursor)) + && cursor + ptr_size <= chunk_range.end + { + let bytes = alloc.inspect_with_uninit_and_ptr_outside_interpreter( + cursor..cursor + ptr_size, + ); + let offset = read_target_uint(self.ecx.tcx.data_layout.endian, bytes) + .map_err(|err| { + miri::err_unsup_format!("invalid pointer representation: {err}") + })?; + + let offset = Size::from_bytes(offset); + rendered.push(format!("{:?}", Pointer::new(Some(prov), offset))); + + cursor += ptr_size; + } else { + let byte = alloc + .inspect_with_uninit_and_ptr_outside_interpreter(cursor..cursor + 1)[0]; + + rendered.push(format!("{byte:02x}")); + cursor += 1; + } + } + } else { + rendered.extend(std::iter::repeat_n("__".to_string(), chunk_range.len())); + } + } + + interp_ok(format!("[{}]", rendered.join(" "))) + } + + /// Render an evaluated operand using the same raw representation for + /// whole locals and projected MIR places. + fn render_op(&self, op: OpTy<'tcx>) -> String { + match op.as_mplace_or_imm() { + Either::Right(imm) => format!("{imm}"), + + Either::Left(mplace) => + match self.render_mplace_bytes(&mplace).report_err() { + Ok(bytes) => bytes, + Err(err) => format!("", interpret::format_interp_error(err)), + }, + } + } + /// Render the source-side path from composite debug info, such as `.field`. fn render_source_projection( fragment: Option<&VarDebugInfoFragment<'tcx>>, @@ -486,22 +606,14 @@ impl<'tcx> PrirodaContext<'tcx> { None => { local_desc.value = "".to_string(); } - Some(Either::Left(_)) => { - local_desc.value = "".to_string(); - } - Some(Either::Right(imm)) => { - match imm { - Scalar(_) => { - local_desc.value = "".to_string(); - } - ScalarPair(_, _) => { - local_desc.value = "".to_string(); - } - - Uninit => { - local_desc.value = "".to_string(); - } - }; + Some(Either::Right(Uninit)) => local_desc.value = "".to_string(), + + Some(Either::Left(_) | Either::Right(_)) => { + let op = self + .ecx + .local_to_op(local, None) + .expect("this error can only occur in CTFE on generic code"); + local_desc.value = self.render_op(op); } }; @@ -552,7 +664,8 @@ impl<'tcx> PrirodaContext<'tcx> { // and `_slice._extra`, not as two separate locals both named `_slice`. // Whole-place debug entries enrich the direct storage-local description. - // Projected places and constants are handled separately/deferred. + // Projected places are evaluated from their original MIR Place and use + // the same raw renderer as ordinary locals. for var_debug_info in &frame.body().var_debug_info { if let VarDebugInfoContents::Place(place) = &var_debug_info.value { if let Some(local_idx) = place.as_local() @@ -566,6 +679,13 @@ impl<'tcx> PrirodaContext<'tcx> { let storage_projection = Self::render_storage_projection(place.projection); let source_projection = Self::render_source_projection(var_debug_info.composite.as_deref()); + let value = self + .ecx + .eval_place_to_op(*place, None) + .map(|op| self.render_op(op)) + .unwrap_or_else(|err| { + format!("", interpret::format_interp_error(err)) + }); local_descs.push(LocalDesc { source_name: Some(var_debug_info.name), @@ -573,8 +693,7 @@ impl<'tcx> PrirodaContext<'tcx> { local: Some(place.local), storage_projection, ty: place.ty(local_decls, self.ecx.tcx.tcx).ty.to_string(), - // FIXME: projection not handled yet. - value: "".to_string(), + value, }); } } @@ -592,6 +711,7 @@ enum DebuggerCommand { Breakpoint(PathBuf, usize), ListLocals, Print(usize), + Follow(AllocId, usize), } enum BreakpointSetResult { @@ -605,6 +725,7 @@ enum CommandResult { BreakpointResult(BreakpointSetResult), Locals(Vec), SingleLocal(Option), + Memory(String), // FIXME: distinguish terminating the debugger session from disconnecting a // frontend and terminating the interpreted program once multiple frontends exist. TerminateSession, @@ -693,6 +814,7 @@ impl Cli { } None => println!("no local for this id"), }, + CommandResult::Memory(memory) => println!("{memory}"), CommandResult::TerminateSession => { println!("quitting"); return interp_ok(()); @@ -725,6 +847,7 @@ impl Cli { "b" | "break" => self.parse_breakpoint(args), "l" | "locals" => Some(DebuggerCommand::ListLocals), "p" | "print" => self.parse_print_local(args), + "f" | "follow" => self.parse_follow(args), _ => None, } } @@ -757,4 +880,18 @@ impl Cli { let local = input.parse().ok()?; Some(DebuggerCommand::Print(local)) } + + fn parse_follow(&self, input: &str) -> Option { + let mut parts = input.split_whitespace(); + let alloc_id = parts.next()?; + let offset = parts.next()?; + if parts.next().is_some() { + return None; + } + + let alloc_id = alloc_id.strip_prefix("alloc").unwrap_or(alloc_id).parse().ok()?; + let alloc_id = AllocId(NonZeroU64::new(alloc_id)?); + let offset = offset.parse().ok()?; + Some(DebuggerCommand::Follow(alloc_id, offset)) + } } diff --git a/src/tools/miri/priroda/tests/cli.rs b/src/tools/miri/priroda/tests/cli.rs index f4f9bf5d176ac..3b596fbf91f26 100644 --- a/src/tools/miri/priroda/tests/cli.rs +++ b/src/tools/miri/priroda/tests/cli.rs @@ -32,11 +32,12 @@ fn main() -> Result<(), Box> { Regex::new(®ex::escape(&manifest_dir.display().to_string())).unwrap(); let miri_dir_regex = Regex::new(®ex::escape(&miri_dir.display().to_string())).unwrap(); let rustc_sysroot_regex = Regex::new(®ex::escape(&rustc_sysroot)).unwrap(); - + let pointer_regex = Regex::new(r"0x[0-9a-f]+\[alloc[0-9]+\]<[0-9]+>").unwrap(); config.comment_defaults.base().normalize_stdout.extend([ (manifest_dir_regex.into(), b"{MANIFEST_DIR}".to_vec()), (miri_dir_regex.into(), b"{MIRI_DIR}".to_vec()), (rustc_sysroot_regex.into(), b"{RUSTC_SYSROOT}".to_vec()), + (pointer_regex.into(), b"{ALLOC_PTR}".to_vec()), ]); // Priroda CLI tests do not currently require annotation comments in the test files diff --git a/src/tools/miri/priroda/tests/ui/locals_access_field.stdout b/src/tools/miri/priroda/tests/ui/locals_access_field.stdout index 5aeff083cc9d7..65a40a5ab584d 100644 --- a/src/tools/miri/priroda/tests/ui/locals_access_field.stdout +++ b/src/tools/miri/priroda/tests/ui/locals_access_field.stdout @@ -2,13 +2,13 @@ (priroda) Hit breakpoint {MANIFEST_DIR}/tests/ui/locals_access_field.rs:14 (priroda) Name: , Id: _0, Ty: (), Value: -Name: extraslice, Id: _1, Ty: ExtraSlice<'_>, Value: -Name: _slice, Id: _2, Ty: &[u8], Value: -Name: _extra, Id: _3, Ty: u32, Value: +Name: extraslice, Id: _1, Ty: ExtraSlice<'_>, Value: [{ALLOC_PTR} 00 00 00 00 00 00 00 00 00 00 00 00 __ __ __ __] +Name: _slice, Id: _2, Ty: &[u8], Value: (pointer to {ALLOC_PTR}, 0x0000000000000000): &[u8] +Name: _extra, Id: _3, Ty: u32, Value: 0_u32 (priroda) Id: _0, Ty: (), Value: -(priroda) Id: _1, Ty: ExtraSlice<'_>, Value: -(priroda) Id: _2, Ty: &[u8], Value: -(priroda) Id: _3, Ty: u32, Value: +(priroda) Id: _1, Ty: ExtraSlice<'_>, Value: [{ALLOC_PTR} 00 00 00 00 00 00 00 00 00 00 00 00 __ __ __ __] +(priroda) Id: _2, Ty: &[u8], Value: (pointer to {ALLOC_PTR}, 0x0000000000000000): &[u8] +(priroda) Id: _3, Ty: u32, Value: 0_u32 (priroda) no local for this id (priroda) no local for this id (priroda) quitting diff --git a/src/tools/miri/priroda/tests/ui/locals_corpus_async.stdin b/src/tools/miri/priroda/tests/ui/locals_corpus_async.stdin index 7c686ffd5bffb..0bfe69e063043 100644 --- a/src/tools/miri/priroda/tests/ui/locals_corpus_async.stdin +++ b/src/tools/miri/priroda/tests/ui/locals_corpus_async.stdin @@ -12,6 +12,7 @@ continue locals continue locals +follow 2 0 continue locals continue diff --git a/src/tools/miri/priroda/tests/ui/locals_corpus_async.stdout b/src/tools/miri/priroda/tests/ui/locals_corpus_async.stdout index 03e00f88e488d..6ccfbc997c590 100644 --- a/src/tools/miri/priroda/tests/ui/locals_corpus_async.stdout +++ b/src/tools/miri/priroda/tests/ui/locals_corpus_async.stdout @@ -7,30 +7,31 @@ (priroda) Hit breakpoint {MANIFEST_DIR}/tests/ui/locals_corpus_async.rs:31 (priroda) Name: , Id: _0, Ty: (), Value: -Name: , Id: _1, Ty: (u32, i32), Value: -Name: first, Id: _1.0, Ty: u32, Value: -Name: second, Id: _1.1, Ty: i32, Value: +Name: , Id: _1, Ty: (u32, i32), Value: (0x00000005, 0x00000006): (u32, i32) +Name: first, Id: _1.0, Ty: u32, Value: 5_u32 +Name: second, Id: _1.1, Ty: i32, Value: 6_i32 (priroda) Hit breakpoint {MANIFEST_DIR}/tests/ui/locals_corpus_async.rs:45 (priroda) Name: , Id: _0, Ty: (), Value: -Name: , Id: _1, Ty: S, Value: -Name: x, Id: _1.0, Ty: f32, Value: +Name: , Id: _1, Ty: S, Value: {transmute(0x40a00000): S} +Name: x, Id: _1.0, Ty: f32, Value: 5f32 (priroda) Hit breakpoint {MANIFEST_DIR}/tests/ui/locals_corpus_async.rs:55 (priroda) Name: , Id: _0, Ty: (), Value: -Name: , Id: _1, Ty: std::option::Option, Value: -Name: inner, Id: _1 as variant#1.0, Ty: i32, Value: +Name: , Id: _1, Ty: std::option::Option, Value: [01 00 00 00 05 00 00 00] +Name: inner, Id: _1 as variant#1.0, Ty: i32, Value: [05 00 00 00] (priroda) Hit breakpoint {MANIFEST_DIR}/tests/ui/locals_corpus_async.rs:66 (priroda) Name: , Id: _0, Ty: (), Value: -Name: , Id: _1, Ty: std::option::Option<&i32>, Value: -Name: pointer, Id: _1 as variant#1.0, Ty: &i32, Value: -Name: deref, Id: _1 as variant#1.0.*, Ty: i32, Value: +Name: , Id: _1, Ty: std::option::Option<&i32>, Value: [{ALLOC_PTR}] +Name: pointer, Id: _1 as variant#1.0, Ty: &i32, Value: [{ALLOC_PTR}] +Name: deref, Id: _1 as variant#1.0.*, Ty: i32, Value: [05 00 00 00] +(priroda) Allocation alloc2+0: [05 00 00 00] (priroda) Hit breakpoint {MANIFEST_DIR}/tests/ui/locals_corpus_async.rs:20 (priroda) Name: , Id: _0, Ty: (), Value: -Name: , Id: _1, Ty: &mut std::option::Option, Value: -Name: foo, Id: _1.* as variant#1.0, Ty: i32, Value: +Name: , Id: _1, Ty: &mut std::option::Option, Value: {&_: &mut std::option::Option} +Name: foo, Id: _1.* as variant#1.0, Ty: i32, Value: [05 00 00 00] (priroda) Hit breakpoint {MANIFEST_DIR}/tests/ui/locals_corpus_async.rs:76 (priroda) Name: , Id: _0, Ty: (), Value: diff --git a/src/tools/miri/priroda/tests/ui/locals_in_function.stdout b/src/tools/miri/priroda/tests/ui/locals_in_function.stdout index f88cc173d3611..5161a3397092e 100644 --- a/src/tools/miri/priroda/tests/ui/locals_in_function.stdout +++ b/src/tools/miri/priroda/tests/ui/locals_in_function.stdout @@ -2,7 +2,7 @@ (priroda) Hit breakpoint {MANIFEST_DIR}/tests/ui/locals_in_function.rs:5 (priroda) Name: , Id: _0, Ty: (), Value: -Name: x, Id: _1, Ty: i32, Value: +Name: x, Id: _1, Ty: i32, Value: 1_i32 Name: y, Id: _2, Ty: bool, Value: Name: , Id: _3, Ty: (i32, bool), Value: Name: , Id: _4, Ty: i32, Value: diff --git a/src/tools/miri/priroda/tests/ui/locals_mplace_metadata.rs b/src/tools/miri/priroda/tests/ui/locals_mplace_metadata.rs new file mode 100644 index 0000000000000..b031f73328f74 --- /dev/null +++ b/src/tools/miri/priroda/tests/ui/locals_mplace_metadata.rs @@ -0,0 +1,26 @@ +#![feature(unsized_fn_params)] +#![allow(incomplete_features, internal_features, unused_variables)] + +trait Marker {} + +impl Marker for (i32, i32) {} + +fn slice_param(slice: [u8]) { + std::hint::black_box(&slice); +} + +fn str_param(text: str) { + std::hint::black_box(&text); +} + +fn dyn_param(value: dyn Marker) { + std::hint::black_box(&value); +} + +fn main() { + let slice: Box<[u8]> = Box::new([1_u8, 2, 3]); + slice_param(*slice); + str_param(*String::from("abc").into_boxed_str()); + let value: Box = Box::new((1_i32, 2_i32)); + dyn_param(*value); +} diff --git a/src/tools/miri/priroda/tests/ui/locals_mplace_metadata.stdin b/src/tools/miri/priroda/tests/ui/locals_mplace_metadata.stdin new file mode 100644 index 0000000000000..86a2cfcd68a3f --- /dev/null +++ b/src/tools/miri/priroda/tests/ui/locals_mplace_metadata.stdin @@ -0,0 +1,13 @@ +break tests/ui/locals_mplace_metadata.rs:8 +break tests/ui/locals_mplace_metadata.rs:12 +break tests/ui/locals_mplace_metadata.rs:16 +continue +locals +print 1 +continue +locals +print 1 +continue +locals +print 1 +quit diff --git a/src/tools/miri/priroda/tests/ui/locals_mplace_metadata.stdout b/src/tools/miri/priroda/tests/ui/locals_mplace_metadata.stdout new file mode 100644 index 0000000000000..3a92ca95a5b68 --- /dev/null +++ b/src/tools/miri/priroda/tests/ui/locals_mplace_metadata.stdout @@ -0,0 +1,25 @@ +(priroda) breakpoint added: {MANIFEST_DIR}/tests/ui/locals_mplace_metadata.rs:8 +(priroda) breakpoint added: {MANIFEST_DIR}/tests/ui/locals_mplace_metadata.rs:12 +(priroda) breakpoint added: {MANIFEST_DIR}/tests/ui/locals_mplace_metadata.rs:16 +(priroda) Hit breakpoint +{MANIFEST_DIR}/tests/ui/locals_mplace_metadata.rs:8 +(priroda) Name: , Id: _0, Ty: (), Value: +Name: slice, Id: _1, Ty: [u8], Value: [01 02 03] +Name: , Id: _2, Ty: &[u8], Value: +Name: , Id: _3, Ty: &[u8], Value: +(priroda) Id: _1, Ty: [u8], Value: [01 02 03] +(priroda) Hit breakpoint +{MANIFEST_DIR}/tests/ui/locals_mplace_metadata.rs:12 +(priroda) Name: , Id: _0, Ty: (), Value: +Name: text, Id: _1, Ty: str, Value: [__ __ __] +Name: , Id: _2, Ty: &str, Value: +Name: , Id: _3, Ty: &str, Value: +(priroda) Id: _1, Ty: str, Value: [__ __ __] +(priroda) Hit breakpoint +{MANIFEST_DIR}/tests/ui/locals_mplace_metadata.rs:16 +(priroda) Name: , Id: _0, Ty: (), Value: +Name: value, Id: _1, Ty: dyn Marker, Value: [01 00 00 00 02 00 00 00] +Name: , Id: _2, Ty: &dyn Marker, Value: +Name: , Id: _3, Ty: &dyn Marker, Value: +(priroda) Id: _1, Ty: dyn Marker, Value: [01 00 00 00 02 00 00 00] +(priroda) quitting diff --git a/src/tools/miri/priroda/tests/ui/locals_pointer_rendering.rs b/src/tools/miri/priroda/tests/ui/locals_pointer_rendering.rs new file mode 100644 index 0000000000000..63981c2080145 --- /dev/null +++ b/src/tools/miri/priroda/tests/ui/locals_pointer_rendering.rs @@ -0,0 +1,68 @@ +//@ normalize-stderr-test: "(?s)^warning: integer-to-pointer cast.*warning: 1 warning emitted\n\n$" -> "" +use std::mem::{self, MaybeUninit}; + +#[repr(C)] +struct PointerAtOffset0<'a> { + ptr: &'a u8, +} + +#[repr(C)] +struct PointerAfterBytes<'a> { + bytes: [u8; 3], + ptr: &'a u8, +} + +#[repr(C)] +struct PointerAtEnd<'a> { + byte: u8, + ptr: &'a u8, +} + +#[repr(C)] +struct UninitAroundPointer<'a> { + before: MaybeUninit<[u8; 2]>, + ptr: &'a u8, + after: MaybeUninit<[u8; 2]>, +} + +#[repr(C)] +struct IntegerAndPointer<'a> { + integer: u32, + ptr: &'a u8, +} + +fn main() { + let target = [10u8, 20]; + let pointer_at_offset0 = PointerAtOffset0 { ptr: &target[0] }; + let pointer_after_bytes = PointerAfterBytes { bytes: [1, 2, 3], ptr: &target[0] }; + let pointer_at_end = PointerAtEnd { byte: 4, ptr: &target[0] }; + let uninit_around_pointer = UninitAroundPointer { + before: MaybeUninit::uninit(), + ptr: &target[0], + after: MaybeUninit::uninit(), + }; + let integer_and_pointer = IntegerAndPointer { integer: 0x44332211, ptr: &target[1] }; + + // Keep only fewer than pointer-sized bytes from a pointer representation. + let fixed_addr_ptr = std::ptr::with_exposed_provenance::(0x1234); + let short_pointer_bytes = unsafe { + let mut bytes = MaybeUninit::<[u8; 1]>::uninit(); + std::ptr::copy_nonoverlapping( + (&fixed_addr_ptr as *const *const u8).cast::(), + bytes.as_mut_ptr().cast::(), + mem::size_of::<[u8; 1]>(), + ); + bytes.assume_init() + }; + + // Break here so all locals are still available to the debugger. + std::hint::black_box(( + &pointer_at_offset0, + &pointer_after_bytes, + &pointer_at_end, + &uninit_around_pointer, + &integer_and_pointer, + &fixed_addr_ptr, + &short_pointer_bytes, + )); +} diff --git a/src/tools/miri/priroda/tests/ui/locals_pointer_rendering.stderr b/src/tools/miri/priroda/tests/ui/locals_pointer_rendering.stderr new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/src/tools/miri/priroda/tests/ui/locals_pointer_rendering.stdin b/src/tools/miri/priroda/tests/ui/locals_pointer_rendering.stdin new file mode 100644 index 0000000000000..6bd9bbf3c514f --- /dev/null +++ b/src/tools/miri/priroda/tests/ui/locals_pointer_rendering.stdin @@ -0,0 +1,5 @@ +break tests/ui/locals_pointer_rendering.rs:59 +continue +locals +print 2 +quit diff --git a/src/tools/miri/priroda/tests/ui/locals_pointer_rendering.stdout b/src/tools/miri/priroda/tests/ui/locals_pointer_rendering.stdout new file mode 100644 index 0000000000000..f26a06e16ae0c --- /dev/null +++ b/src/tools/miri/priroda/tests/ui/locals_pointer_rendering.stdout @@ -0,0 +1,56 @@ +(priroda) breakpoint added: {MANIFEST_DIR}/tests/ui/locals_pointer_rendering.rs:59 +(priroda) Hit breakpoint +{MANIFEST_DIR}/tests/ui/locals_pointer_rendering.rs:59 +(priroda) Name: , Id: _0, Ty: (), Value: +Name: target, Id: _1, Ty: [u8; 2], Value: [0a 14] +Name: pointer_at_offset0, Id: _2, Ty: PointerAtOffset0<'_>, Value: [{ALLOC_PTR}] +Name: , Id: _3, Ty: &u8, Value: +Name: , Id: _4, Ty: &u8, Value: +Name: , Id: _5, Ty: usize, Value: +Name: , Id: _6, Ty: bool, Value: true +Name: pointer_after_bytes, Id: _7, Ty: PointerAfterBytes<'_>, Value: [01 02 03 __ __ __ __ __ {ALLOC_PTR}] +Name: , Id: _8, Ty: [u8; 3], Value: +Name: , Id: _9, Ty: &u8, Value: +Name: , Id: _10, Ty: &u8, Value: +Name: , Id: _11, Ty: usize, Value: +Name: , Id: _12, Ty: bool, Value: true +Name: pointer_at_end, Id: _13, Ty: PointerAtEnd<'_>, Value: [04 __ __ __ __ __ __ __ {ALLOC_PTR}] +Name: , Id: _14, Ty: &u8, Value: +Name: , Id: _15, Ty: &u8, Value: +Name: , Id: _16, Ty: usize, Value: +Name: , Id: _17, Ty: bool, Value: true +Name: uninit_around_pointer, Id: _18, Ty: UninitAroundPointer<'_>, Value: [__ __ __ __ __ __ __ __ {ALLOC_PTR} __ __ __ __ __ __ __ __] +Name: , Id: _19, Ty: std::mem::MaybeUninit<[u8; 2]>, Value: +Name: , Id: _20, Ty: &u8, Value: +Name: , Id: _21, Ty: &u8, Value: +Name: , Id: _22, Ty: usize, Value: +Name: , Id: _23, Ty: bool, Value: true +Name: , Id: _24, Ty: std::mem::MaybeUninit<[u8; 2]>, Value: +Name: integer_and_pointer, Id: _25, Ty: IntegerAndPointer<'_>, Value: [11 22 33 44 __ __ __ __ {ALLOC_PTR}] +Name: , Id: _26, Ty: &u8, Value: +Name: , Id: _27, Ty: &u8, Value: +Name: , Id: _28, Ty: usize, Value: +Name: , Id: _29, Ty: bool, Value: true +Name: fixed_addr_ptr, Id: _30, Ty: *const u8, Value: [0x1234[wildcard]] +Name: short_pointer_bytes, Id: _31, Ty: [u8; 1], Value: [34] +Name: bytes, Id: _32, Ty: std::mem::MaybeUninit<[u8; 1]>, Value: +Name: , Id: _33, Ty: (), Value: +Name: , Id: _34, Ty: *const u8, Value: +Name: , Id: _35, Ty: *const *const u8, Value: +Name: , Id: _36, Ty: &*const u8, Value: +Name: , Id: _37, Ty: *mut u8, Value: +Name: , Id: _38, Ty: *mut [u8; 1], Value: +Name: , Id: _39, Ty: &mut std::mem::MaybeUninit<[u8; 1]>, Value: +Name: , Id: _40, Ty: usize, Value: +Name: , Id: _41, Ty: std::mem::MaybeUninit<[u8; 1]>, Value: +Name: , Id: _42, Ty: (&PointerAtOffset0<'_>, &PointerAfterBytes<'_>, &PointerAtEnd<'_>, &UninitAroundPointer<'_>, &IntegerAndPointer<'_>, &*const u8, &[u8; 1]), Value: +Name: , Id: _43, Ty: (&PointerAtOffset0<'_>, &PointerAfterBytes<'_>, &PointerAtEnd<'_>, &UninitAroundPointer<'_>, &IntegerAndPointer<'_>, &*const u8, &[u8; 1]), Value: +Name: , Id: _44, Ty: &PointerAtOffset0<'_>, Value: +Name: , Id: _45, Ty: &PointerAfterBytes<'_>, Value: +Name: , Id: _46, Ty: &PointerAtEnd<'_>, Value: +Name: , Id: _47, Ty: &UninitAroundPointer<'_>, Value: +Name: , Id: _48, Ty: &IntegerAndPointer<'_>, Value: +Name: , Id: _49, Ty: &*const u8, Value: +Name: , Id: _50, Ty: &[u8; 1], Value: +(priroda) Id: _2, Ty: PointerAtOffset0<'_>, Value: [{ALLOC_PTR}] +(priroda) quitting diff --git a/src/tools/miri/priroda/tests/ui/locals_projected_mplace_size.rs b/src/tools/miri/priroda/tests/ui/locals_projected_mplace_size.rs new file mode 100644 index 0000000000000..561d786890aed --- /dev/null +++ b/src/tools/miri/priroda/tests/ui/locals_projected_mplace_size.rs @@ -0,0 +1,48 @@ +//@ compile-flags: -Zmir-opt-level=0 + +#![allow(internal_features)] +#![feature(custom_mir, core_intrinsics)] + +extern crate core; +use core::intrinsics::mir::*; + +// Regression test for projected values: each `item.` row must render +// only that field's own value/layout, and the memory-backed `item.target` row +// must not continue through the rest of the allocation backing `Envelope`. +#[repr(C)] +struct Payload { + a: u16, + b: u8, + c: u32, + d: u8, +} + +#[repr(C)] +struct Envelope { + prefix: u8, + target: Payload, + trailer: u64, + checksum: u16, +} + +#[custom_mir(dialect = "analysis", phase = "post-cleanup")] +fn projected_struct(item: Envelope) { + mir! { + debug prefix => item.prefix; + debug target => item.target; + debug trailer => item.trailer; + debug checksum => item.checksum; + { + Return() + } + } +} + +fn main() { + projected_struct(Envelope { + prefix: 0xaa, + target: Payload { a: 0x1122, b: 0x33, c: 0x44556677, d: 0x88 }, + trailer: 0x99aabbccddeeff00, + checksum: 0x1234, + }); +} diff --git a/src/tools/miri/priroda/tests/ui/locals_projected_mplace_size.stdin b/src/tools/miri/priroda/tests/ui/locals_projected_mplace_size.stdin new file mode 100644 index 0000000000000..9db90a1115764 --- /dev/null +++ b/src/tools/miri/priroda/tests/ui/locals_projected_mplace_size.stdin @@ -0,0 +1,4 @@ +break tests/ui/locals_projected_mplace_size.rs:36 +continue +locals +quit diff --git a/src/tools/miri/priroda/tests/ui/locals_projected_mplace_size.stdout b/src/tools/miri/priroda/tests/ui/locals_projected_mplace_size.stdout new file mode 100644 index 0000000000000..a2111bed511aa --- /dev/null +++ b/src/tools/miri/priroda/tests/ui/locals_projected_mplace_size.stdout @@ -0,0 +1,10 @@ +(priroda) breakpoint added: {MANIFEST_DIR}/tests/ui/locals_projected_mplace_size.rs:36 +(priroda) Hit breakpoint +{MANIFEST_DIR}/tests/ui/locals_projected_mplace_size.rs:36 +(priroda) Name: , Id: _0, Ty: (), Value: +Name: , Id: _1, Ty: Envelope, Value: [aa __ __ __ 22 11 33 __ 77 66 55 44 88 __ __ __ 00 ff ee dd cc bb aa 99 34 12 __ __ __ __ __ __] +Name: prefix, Id: _1.0, Ty: u8, Value: [aa] +Name: target, Id: _1.1, Ty: Payload, Value: [22 11 33 __ 77 66 55 44 88 __ __ __] +Name: trailer, Id: _1.2, Ty: u64, Value: [00 ff ee dd cc bb aa 99] +Name: checksum, Id: _1.3, Ty: u16, Value: [34 12] +(priroda) quitting diff --git a/src/tools/miri/priroda/tests/ui/locals_sroa.stdout b/src/tools/miri/priroda/tests/ui/locals_sroa.stdout index 7dcffd8b3c722..94c44adf6ae59 100644 --- a/src/tools/miri/priroda/tests/ui/locals_sroa.stdout +++ b/src/tools/miri/priroda/tests/ui/locals_sroa.stdout @@ -2,7 +2,7 @@ (priroda) Hit breakpoint {MANIFEST_DIR}/tests/ui/locals_sroa.rs:13 (priroda) Name: , Id: _0, Ty: (), Value: -Name: s, Id: _1, Ty: &[u8], Value: +Name: s, Id: _1, Ty: &[u8], Value: (pointer to {ALLOC_PTR}, 0x0000000000000002): &[u8] Name: , Id: _2, Ty: ExtraSlice<'_>, Value: Name: , Id: _3, Ty: &[u8], Value: Name: , Id: _4, Ty: u32, Value: diff --git a/src/tools/miri/priroda/tests/ui/locals_struct_field_fragment.stdout b/src/tools/miri/priroda/tests/ui/locals_struct_field_fragment.stdout index ffc3d24354e4b..cc717bfb97fe2 100644 --- a/src/tools/miri/priroda/tests/ui/locals_struct_field_fragment.stdout +++ b/src/tools/miri/priroda/tests/ui/locals_struct_field_fragment.stdout @@ -5,6 +5,6 @@ Name: , Id: _1, Ty: Outer, Value: Name: , Id: _2, Ty: Inner, Value: Name: , Id: _3, Ty: Inner, Value: -Name: x.inner.value, Id: _4, Ty: u8, Value: +Name: x.inner.value, Id: _4, Ty: u8, Value: 7_u8 Name: , Id: _5, Ty: u8, Value: (priroda) quitting diff --git a/src/tools/miri/priroda/tests/ui/locals_value_shapes.rs b/src/tools/miri/priroda/tests/ui/locals_value_shapes.rs new file mode 100644 index 0000000000000..b6f00bd1e08cc --- /dev/null +++ b/src/tools/miri/priroda/tests/ui/locals_value_shapes.rs @@ -0,0 +1,30 @@ +#![allow(dead_code, unused_variables)] + +struct Aggregate { + byte: u8, + word: u16, +} + +fn main() { + // Edge case for `None` from `as_mplace_or_imm`: moved/drop temporaries become dead. + let dead_box = Box::new(0x11_u8); + let consumed = dead_box; + drop(consumed); + + let pointed_box = Box::new(0x11_u8); + let pointer_box = &pointed_box; + + // Immediate::Scalar. + let scalar = 0x2a_u8; + let pointed = 0x33_u8; + // Immediate::Scalar with pointer provenance. + let scalar_pointer = &pointed; + // Immediate::ScalarPair. + let scalar_pair = &[10_u8, 20_u8][..]; + // Either::Left mplace/indirect storage. + let mplace = Aggregate { byte: scalar, word: 0x1234 }; + // Immediate::Uninit. + let uninit_scalar: u32; + + std::hint::black_box((scalar, scalar_pointer, scalar_pair.len(), &mplace)); +} diff --git a/src/tools/miri/priroda/tests/ui/locals_value_shapes.stdin b/src/tools/miri/priroda/tests/ui/locals_value_shapes.stdin new file mode 100644 index 0000000000000..fbd5687a45754 --- /dev/null +++ b/src/tools/miri/priroda/tests/ui/locals_value_shapes.stdin @@ -0,0 +1,12 @@ +break tests/ui/locals_value_shapes.rs:29 +continue +locals +print 0 +print 4 +print 5 +print 7 +print 8 +print 13 +print 15 +print 999 +quit diff --git a/src/tools/miri/priroda/tests/ui/locals_value_shapes.stdout b/src/tools/miri/priroda/tests/ui/locals_value_shapes.stdout new file mode 100644 index 0000000000000..4250c443221f4 --- /dev/null +++ b/src/tools/miri/priroda/tests/ui/locals_value_shapes.stdout @@ -0,0 +1,38 @@ +(priroda) breakpoint added: {MANIFEST_DIR}/tests/ui/locals_value_shapes.rs:29 +(priroda) Hit breakpoint +{MANIFEST_DIR}/tests/ui/locals_value_shapes.rs:29 +(priroda) Name: , Id: _0, Ty: (), Value: +Name: dead_box, Id: _1, Ty: std::boxed::Box, Value: [{ALLOC_PTR}] +Name: consumed, Id: _2, Ty: std::boxed::Box, Value: [{ALLOC_PTR}] +Name: , Id: _3, Ty: (), Value: +Name: , Id: _4, Ty: std::boxed::Box, Value: +Name: pointed_box, Id: _5, Ty: std::boxed::Box, Value: [{ALLOC_PTR}] +Name: pointer_box, Id: _6, Ty: &std::boxed::Box, Value: {&_: &std::boxed::Box} +Name: scalar, Id: _7, Ty: u8, Value: 42_u8 +Name: pointed, Id: _8, Ty: u8, Value: [33] +Name: scalar_pointer, Id: _9, Ty: &u8, Value: {&_: &u8} +Name: scalar_pair, Id: _10, Ty: &[u8], Value: (pointer to {ALLOC_PTR}, 0x0000000000000002): &[u8] +Name: , Id: _11, Ty: &[u8], Value: (pointer to {ALLOC_PTR}, 0x0000000000000002): &[u8] +Name: , Id: _12, Ty: &[u8; 2], Value: +Name: , Id: _13, Ty: [u8; 2], Value: +Name: , Id: _14, Ty: std::ops::RangeFull, Value: +Name: mplace, Id: _15, Ty: Aggregate, Value: [34 12 2a __] +Name: , Id: _16, Ty: u8, Value: +Name: uninit_scalar, Id: _17, Ty: u32, Value: +Name: , Id: _18, Ty: (u8, &u8, usize, &Aggregate), Value: +Name: , Id: _19, Ty: (u8, &u8, usize, &Aggregate), Value: +Name: , Id: _20, Ty: u8, Value: +Name: , Id: _21, Ty: &u8, Value: +Name: , Id: _22, Ty: usize, Value: +Name: , Id: _23, Ty: &[u8], Value: +Name: , Id: _24, Ty: &Aggregate, Value: +Name: , Id: _25, Ty: &[u8; 2], Value: {&_: &[u8; 2]} +(priroda) Id: _0, Ty: (), Value: +(priroda) Id: _4, Ty: std::boxed::Box, Value: +(priroda) Id: _5, Ty: std::boxed::Box, Value: [{ALLOC_PTR}] +(priroda) Id: _7, Ty: u8, Value: 42_u8 +(priroda) Id: _8, Ty: u8, Value: [33] +(priroda) Id: _13, Ty: [u8; 2], Value: +(priroda) Id: _15, Ty: Aggregate, Value: [34 12 2a __] +(priroda) no local for this id +(priroda) quitting diff --git a/src/tools/miri/priroda/tests/ui/locals_wildcard_pointer.rs b/src/tools/miri/priroda/tests/ui/locals_wildcard_pointer.rs new file mode 100644 index 0000000000000..5a8bb13c43df8 --- /dev/null +++ b/src/tools/miri/priroda/tests/ui/locals_wildcard_pointer.rs @@ -0,0 +1,16 @@ +//@ normalize-stderr-test: "(?s)^warning: integer-to-pointer cast.*warning: 1 warning emitted\n\n$" -> "" + +#[repr(C)] +struct WildcardPointer { + ptr: *const u8, +} + +fn main() { + // The pointer is never dereferenced: this keeps the wildcard provenance + // available for the debugger renderer without requiring it to resolve. + let ptr = 0x1234usize as *const u8; + let wildcard = WildcardPointer { ptr }; + + // Keep the local alive for inspection. + std::hint::black_box(&wildcard); +} diff --git a/src/tools/miri/priroda/tests/ui/locals_wildcard_pointer.stderr b/src/tools/miri/priroda/tests/ui/locals_wildcard_pointer.stderr new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/src/tools/miri/priroda/tests/ui/locals_wildcard_pointer.stdin b/src/tools/miri/priroda/tests/ui/locals_wildcard_pointer.stdin new file mode 100644 index 0000000000000..9731f603d6957 --- /dev/null +++ b/src/tools/miri/priroda/tests/ui/locals_wildcard_pointer.stdin @@ -0,0 +1,5 @@ +break tests/ui/locals_wildcard_pointer.rs:15 +continue +locals +print 2 +quit diff --git a/src/tools/miri/priroda/tests/ui/locals_wildcard_pointer.stdout b/src/tools/miri/priroda/tests/ui/locals_wildcard_pointer.stdout new file mode 100644 index 0000000000000..866c549f49a46 --- /dev/null +++ b/src/tools/miri/priroda/tests/ui/locals_wildcard_pointer.stdout @@ -0,0 +1,11 @@ +(priroda) breakpoint added: {MANIFEST_DIR}/tests/ui/locals_wildcard_pointer.rs:15 +(priroda) Hit breakpoint +{MANIFEST_DIR}/tests/ui/locals_wildcard_pointer.rs:15 +(priroda) Name: , Id: _0, Ty: (), Value: +Name: ptr, Id: _1, Ty: *const u8, Value: {&_: *const u8} +Name: wildcard, Id: _2, Ty: WildcardPointer, Value: [0x1234[wildcard]] +Name: , Id: _3, Ty: *const u8, Value: +Name: , Id: _4, Ty: &WildcardPointer, Value: +Name: , Id: _5, Ty: &WildcardPointer, Value: +(priroda) Id: _2, Ty: WildcardPointer, Value: [0x1234[wildcard]] +(priroda) quitting diff --git a/src/tools/miri/rust-version b/src/tools/miri/rust-version index 3dd271b8b5762..25a5238f538ee 100644 --- a/src/tools/miri/rust-version +++ b/src/tools/miri/rust-version @@ -1 +1 @@ -4a9d5368df6450bbd2fc8dde4508c3e5d83bb19d +390279b302ca98ae270f434100ae3730531d1246 diff --git a/src/tools/miri/src/intrinsics/aarch64.rs b/src/tools/miri/src/intrinsics/aarch64.rs index aabda2a997525..626a83a65a238 100644 --- a/src/tools/miri/src/intrinsics/aarch64.rs +++ b/src/tools/miri/src/intrinsics/aarch64.rs @@ -1,5 +1,3 @@ -use std::assert_matches; - use rustc_middle::mir::BinOp; use rustc_span::Symbol; @@ -180,34 +178,77 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } } - // Vector table lookup: each index selects a byte from the 8 or 16-byte table, + // Vector table lookup: each index selects a byte from the table, // out-of-range -> 0. // - // Used to implement the vqtbl1 and vqtbl1q set of functions, e.g.: + // Used to implement the vtblN, vqtblN and vqtblNq set of functions, e.g.: // // - https://developer.arm.com/architectures/instruction-sets/intrinsics/vtbl1_u8 + // - https://developer.arm.com/architectures/instruction-sets/intrinsics/vqtbl1_u8 // - https://developer.arm.com/architectures/instruction-sets/intrinsics/vqtbl1q_s8 + // - https://developer.arm.com/architectures/instruction-sets/intrinsics/vtbl2_u8 + // - https://developer.arm.com/architectures/instruction-sets/intrinsics/vqtbl4q_s8 // // LLVM does not have a portable shuffle that takes non-const indices // so we need to implement this ourselves. - "neon.tbl1.v8i8" | "neon.tbl1.v16i8" => { - let [table, indices] = this.check_shim_sig_unadjusted(link_name, args)?; + _ if unprefixed_name.starts_with("neon.tbl") => { + // The table segments always have 16 elements, the index vector and output vector + // have either 8 or 16 elements. + let (table_segments, indices) = match unprefixed_name { + "neon.tbl1.v8i8" | "neon.tbl1.v16i8" => { + let [table, indices] = this.check_shim_sig_unadjusted(link_name, args)?; + let (table, len) = this.project_to_simd(table)?; + assert_eq!(len, 16); + (vec![table], indices) + } + "neon.tbl2.v8i8" | "neon.tbl2.v16i8" => { + let [table0, table1, indices] = + this.check_shim_sig_unadjusted(link_name, args)?; + let (table0, len0) = this.project_to_simd(table0)?; + let (table1, len1) = this.project_to_simd(table1)?; + assert_eq!([len0, len1], [16; 2]); + (vec![table0, table1], indices) + } + "neon.tbl3.v8i8" | "neon.tbl3.v16i8" => { + let [table0, table1, table2, indices] = + this.check_shim_sig_unadjusted(link_name, args)?; + let (table0, len0) = this.project_to_simd(table0)?; + let (table1, len1) = this.project_to_simd(table1)?; + let (table2, len2) = this.project_to_simd(table2)?; + assert_eq!([len0, len1, len2], [16; 3]); + (vec![table0, table1, table2], indices) + } + "neon.tbl4.v8i8" | "neon.tbl4.v16i8" => { + let [table0, table1, table2, table3, indices] = + this.check_shim_sig_unadjusted(link_name, args)?; + let (table0, len0) = this.project_to_simd(table0)?; + let (table1, len1) = this.project_to_simd(table1)?; + let (table2, len2) = this.project_to_simd(table2)?; + let (table3, len3) = this.project_to_simd(table3)?; + assert_eq!([len0, len1, len2, len3], [16; 4]); + (vec![table0, table1, table2, table3], indices) + } + _ => unreachable!(), + }; - let (table, table_len) = this.project_to_simd(table)?; let (indices, idx_len) = this.project_to_simd(indices)?; let (dest, dest_len) = this.project_to_simd(dest)?; - assert_matches!(table_len, 8 | 16); assert_eq!(idx_len, dest_len); for i in 0..dest_len { let idx = this.read_immediate(&this.project_index(&indices, i)?)?; - let idx_u = idx.to_scalar().to_u8()?; - let val = if u64::from(idx_u) < table_len { - let t = this.read_immediate(&this.project_index(&table, idx_u.into())?)?; + let idx = idx.to_scalar().to_u8()?; + + // The LLVM intrinsic table segments always have 16 elements. + let val = if usize::from(idx) < table_segments.len().strict_mul(16) { + let table = &table_segments[usize::from(idx.strict_div(16))]; + let lane = u64::from(idx.strict_rem(16)); + let t = this.read_immediate(&this.project_index(table, lane)?)?; t.to_scalar() } else { Scalar::from_u8(0) }; + this.write_scalar(val, &this.project_index(&dest, i)?)?; } } diff --git a/src/tools/miri/tests/deps/Cargo.lock b/src/tools/miri/tests/deps/Cargo.lock index 4691588eefb40..5039054e04990 100644 --- a/src/tools/miri/tests/deps/Cargo.lock +++ b/src/tools/miri/tests/deps/Cargo.lock @@ -253,9 +253,9 @@ checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" [[package]] name = "libc" -version = "0.2.186" +version = "0.2.188" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" +checksum = "22053b6a34f84abc97f9129e61334f40174659a1b9bd18c970b83db6a9a6348b" [[package]] name = "linux-raw-sys" diff --git a/src/tools/miri/tests/pass-dep/libc/pthread-sync.rs b/src/tools/miri/tests/pass-dep/libc/pthread-sync.rs index a79d0656d85c4..849703d33b333 100644 --- a/src/tools/miri/tests/pass-dep/libc/pthread-sync.rs +++ b/src/tools/miri/tests/pass-dep/libc/pthread-sync.rs @@ -12,10 +12,12 @@ fn main() { test_mutex_libc_init_recursive(); test_mutex_libc_init_normal(); test_mutex_libc_init_errorcheck(); + #[cfg(target_os = "linux")] - test_mutex_libc_static_initializer_recursive(); - #[cfg(target_os = "linux")] - test_mutex_libc_static_initializer_errorcheck(); + { + test_mutex_libc_static_initializer_recursive(); + test_mutex_libc_static_initializer_errorcheck(); + } test_cond(); test_condattr(); diff --git a/src/tools/miri/tests/pass/intrinsics/portable-simd.rs b/src/tools/miri/tests/pass/intrinsics/portable-simd.rs index e507e92624bde..a3991f78b1166 100644 --- a/src/tools/miri/tests/pass/intrinsics/portable-simd.rs +++ b/src/tools/miri/tests/pass/intrinsics/portable-simd.rs @@ -766,8 +766,8 @@ fn simd_swizzle() { } fn simd_swizzle_dyn() { - // FIXME: needs llvm.neon.tbl2 support, see https://github.com/rust-lang/miri/pull/5219. - if cfg!(target_arch = "aarch64") { + if cfg!(target_arch = "loongarch64") { + // We don't support the required intrinsic here. return; } diff --git a/src/tools/miri/tests/pass/shims/aarch64/intrinsics-aarch64-neon.rs b/src/tools/miri/tests/pass/shims/aarch64/intrinsics-aarch64-neon.rs index 7b4c5f082ea5c..1bc6e3539222d 100644 --- a/src/tools/miri/tests/pass/shims/aarch64/intrinsics-aarch64-neon.rs +++ b/src/tools/miri/tests/pass/shims/aarch64/intrinsics-aarch64-neon.rs @@ -12,7 +12,7 @@ fn main() { unsafe { test_vpmaxq_u8(); - test_tbl1_basic(); + test_tbl(); test_vpadd(); test_vpaddl(); test_vqdmulh(); @@ -46,47 +46,143 @@ unsafe fn test_vpmaxq_u8() { } #[target_feature(enable = "neon")] -fn test_tbl1_basic() { +fn test_tbl() { unsafe { - // table = 0..7 - let table: uint8x8_t = transmute::<[u8; 8], _>([0, 1, 2, 3, 4, 5, 6, 7]); - - // indices - let idx: uint8x8_t = transmute::<[u8; 8], _>([0, 1, 2, 3, 4, 5, 6, 7]); - let got = vtbl1_u8(table, idx); - let got_arr: [u8; 8] = transmute(got); - assert_eq!(got_arr, [0, 1, 2, 3, 4, 5, 6, 7]); - - // Also try different order and out-of-range indices (8, 255). - let idx2: uint8x8_t = transmute::<[u8; 8], _>([7, 8, 255, 0, 1, 2, 3, 4]); - let got2 = vtbl1_u8(table, idx2); - let got2_arr: [u8; 8] = transmute(got2); - assert_eq!(got2_arr[0], 7); - assert_eq!(got2_arr[1], 0); // out-of-range - assert_eq!(got2_arr[2], 0); // out-of-range - assert_eq!(&got2_arr[3..8], &[0, 1, 2, 3, 4][..]); + let table_arr = [0, 1, 2, 3, 4, 5, 6, 7]; + let table = vld1_u8(table_arr.as_ptr()); + + let idx_arr = [0, 1, 2, 3, 4, 5, 6, 7]; + let idx = vld1_u8(idx_arr.as_ptr()); + + let got: [u8; 8] = transmute(vtbl1_u8(table, idx)); + assert_eq!(got, [0, 1, 2, 3, 4, 5, 6, 7]); + + // Also try a different order and out-of-range indices. + let idx_arr = [7, 8, 255, 0, 1, 2, 3, 4]; + let idx = vld1_u8(idx_arr.as_ptr()); + + let got: [u8; 8] = transmute(vtbl1_u8(table, idx)); + assert_eq!(got, [7, 0, 0, 0, 1, 2, 3, 4]); } unsafe { // table = 0..15 - let table: uint8x16_t = - transmute::<[u8; 16], _>([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]); - // indices - let idx: uint8x16_t = - transmute::<[u8; 16], _>([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]); - let got = vqtbl1q_u8(table, idx); - let got_arr: [u8; 16] = transmute(got); - assert_eq!(got_arr, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]); - - // Also try different order and out-of-range indices (16, 255). - let idx2: uint8x16_t = - transmute::<[u8; 16], _>([15, 16, 255, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]); - let got2 = vqtbl1q_u8(table, idx2); - let got2_arr: [u8; 16] = transmute(got2); - assert_eq!(got2_arr[0], 15); - assert_eq!(got2_arr[1], 0); // out-of-range - assert_eq!(got2_arr[2], 0); // out-of-range - assert_eq!(&got2_arr[3..16], &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12][..]); + let table0_arr = [0, 1, 2, 3, 4, 5, 6, 7]; + let table1_arr = [8, 9, 10, 11, 12, 13, 14, 15]; + let table0 = vld1_u8(table0_arr.as_ptr()); + let table1 = vld1_u8(table1_arr.as_ptr()); + let table = uint8x8x2_t(table0, table1); + + let idx_arr = [0, 7, 8, 15, 16, 255, 4, 12]; + let idx = vld1_u8(idx_arr.as_ptr()); + + let got: [u8; 8] = transmute(vtbl2_u8(table, idx)); + assert_eq!(got, [0, 7, 8, 15, 0, 0, 4, 12]); + } + + unsafe { + // table = 0..23 + let table0_arr = [0, 1, 2, 3, 4, 5, 6, 7]; + let table1_arr = [8, 9, 10, 11, 12, 13, 14, 15]; + let table2_arr = [16, 17, 18, 19, 20, 21, 22, 23]; + let table0 = vld1_u8(table0_arr.as_ptr()); + let table1 = vld1_u8(table1_arr.as_ptr()); + let table2 = vld1_u8(table2_arr.as_ptr()); + let table = uint8x8x3_t(table0, table1, table2); + + let idx_arr = [0, 7, 8, 15, 16, 23, 24, 255]; + let idx = vld1_u8(idx_arr.as_ptr()); + + let got: [u8; 8] = transmute(vtbl3_u8(table, idx)); + assert_eq!(got, [0, 7, 8, 15, 16, 23, 0, 0]); + } + + unsafe { + // table = 0..31 + let table0_arr = [0, 1, 2, 3, 4, 5, 6, 7]; + let table1_arr = [8, 9, 10, 11, 12, 13, 14, 15]; + let table2_arr = [16, 17, 18, 19, 20, 21, 22, 23]; + let table3_arr = [24, 25, 26, 27, 28, 29, 30, 31]; + let table0 = vld1_u8(table0_arr.as_ptr()); + let table1 = vld1_u8(table1_arr.as_ptr()); + let table2 = vld1_u8(table2_arr.as_ptr()); + let table3 = vld1_u8(table3_arr.as_ptr()); + let table = uint8x8x4_t(table0, table1, table2, table3); + + let idx_arr = [0, 8, 15, 16, 23, 24, 31, 32]; + let idx = vld1_u8(idx_arr.as_ptr()); + + let got: [u8; 8] = transmute(vtbl4_u8(table, idx)); + assert_eq!(got, [0, 8, 15, 16, 23, 24, 31, 0]); + } + + unsafe { + let table_arr = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]; + let table = vld1q_u8(table_arr.as_ptr()); + + let idx_arr = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]; + let idx = vld1q_u8(idx_arr.as_ptr()); + + let got: [u8; 16] = transmute(vqtbl1q_u8(table, idx)); + assert_eq!(got, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]); + + // Also try a different order and out-of-range indices. + let idx_arr = [15, 16, 255, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]; + let idx = vld1q_u8(idx_arr.as_ptr()); + + let got: [u8; 16] = transmute(vqtbl1q_u8(table, idx)); + assert_eq!(got, [15, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]); + } + + unsafe { + // table = 0..31 + let table0_arr = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]; + let table1_arr = [16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31]; + let table0 = vld1q_u8(table0_arr.as_ptr()); + let table1 = vld1q_u8(table1_arr.as_ptr()); + let table = uint8x16x2_t(table0, table1); + + let idx_arr = [0, 15, 16, 31, 32, 255, 7, 8, 23, 24, 1, 14, 17, 30, 4, 27]; + let idx = vld1q_u8(idx_arr.as_ptr()); + + let got: [u8; 16] = transmute(vqtbl2q_u8(table, idx)); + assert_eq!(got, [0, 15, 16, 31, 0, 0, 7, 8, 23, 24, 1, 14, 17, 30, 4, 27]); + } + + unsafe { + // table = 0..47 + let table0_arr = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]; + let table1_arr = [16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31]; + let table2_arr = [32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47]; + let table0 = vld1q_u8(table0_arr.as_ptr()); + let table1 = vld1q_u8(table1_arr.as_ptr()); + let table2 = vld1q_u8(table2_arr.as_ptr()); + let table = uint8x16x3_t(table0, table1, table2); + + let idx_arr = [0, 15, 16, 31, 32, 47, 48, 255, 7, 23, 39, 1, 17, 33, 30, 46]; + let idx = vld1q_u8(idx_arr.as_ptr()); + + let got: [u8; 16] = transmute(vqtbl3q_u8(table, idx)); + assert_eq!(got, [0, 15, 16, 31, 32, 47, 0, 0, 7, 23, 39, 1, 17, 33, 30, 46]); + } + + unsafe { + // table = 0..63 + let table0_arr = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]; + let table1_arr = [16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31]; + let table2_arr = [32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47]; + let table3_arr = [48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63]; + let table0 = vld1q_u8(table0_arr.as_ptr()); + let table1 = vld1q_u8(table1_arr.as_ptr()); + let table2 = vld1q_u8(table2_arr.as_ptr()); + let table3 = vld1q_u8(table3_arr.as_ptr()); + let table = uint8x16x4_t(table0, table1, table2, table3); + + let idx_arr = [0, 15, 16, 31, 32, 47, 48, 63, 64, 255, 7, 23, 39, 55, 1, 62]; + let idx = vld1q_u8(idx_arr.as_ptr()); + + let got: [u8; 16] = transmute(vqtbl4q_u8(table, idx)); + assert_eq!(got, [0, 15, 16, 31, 32, 47, 48, 63, 0, 0, 7, 23, 39, 55, 1, 62]); } }