From 3ea68fb6eee930d7030704842e201c64fdf2ad5c Mon Sep 17 00:00:00 2001 From: Mohamed Ali Date: Fri, 10 Jul 2026 06:26:16 +0300 Subject: [PATCH 01/16] [Priroda] Render indirect local values Use `local_to_op` and the operand storage shape to render initialized immediate locals through Miri's Immediate display implementation. Keep mplace-backed locals as `` for now, preserve explicit dead/uninit states, and expose interpreter errors in `LocalDesc`.value. Add value-shape and metadata-sized local fixtures for the next indirect rendering slices. --- src/tools/miri/priroda/src/main.rs | 29 +++++++------- .../tests/ui/locals_access_field.stdout | 8 ++-- .../tests/ui/locals_corpus_async.stdout | 6 +-- .../tests/ui/locals_in_function.stdout | 2 +- .../tests/ui/locals_mplace_metadata.rs | 26 +++++++++++++ .../tests/ui/locals_mplace_metadata.stdin | 13 +++++++ .../tests/ui/locals_mplace_metadata.stdout | 25 ++++++++++++ .../miri/priroda/tests/ui/locals_sroa.stdout | 2 +- .../ui/locals_struct_field_fragment.stdout | 2 +- .../priroda/tests/ui/locals_value_shapes.rs | 30 +++++++++++++++ .../tests/ui/locals_value_shapes.stdin | 12 ++++++ .../tests/ui/locals_value_shapes.stdout | 38 +++++++++++++++++++ 12 files changed, 168 insertions(+), 25 deletions(-) create mode 100644 src/tools/miri/priroda/tests/ui/locals_mplace_metadata.rs create mode 100644 src/tools/miri/priroda/tests/ui/locals_mplace_metadata.stdin create mode 100644 src/tools/miri/priroda/tests/ui/locals_mplace_metadata.stdout create mode 100644 src/tools/miri/priroda/tests/ui/locals_value_shapes.rs create mode 100644 src/tools/miri/priroda/tests/ui/locals_value_shapes.stdin create mode 100644 src/tools/miri/priroda/tests/ui/locals_value_shapes.stdout diff --git a/src/tools/miri/priroda/src/main.rs b/src/tools/miri/priroda/src/main.rs index 98fcb937db9e7..6d9142c280875 100644 --- a/src/tools/miri/priroda/src/main.rs +++ b/src/tools/miri/priroda/src/main.rs @@ -19,7 +19,7 @@ use std::io::{self, Write}; use std::path::PathBuf; use miri::Immediate::{Scalar, ScalarPair, Uninit}; -use miri::*; +use miri::{interpret, *}; use rustc_driver::Compilation; use rustc_hir::attrs::CrateType; use rustc_interface::interface; @@ -486,22 +486,21 @@ 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(); + Some(Either::Right(Uninit)) => local_desc.value = "".to_string(), + + Some(Either::Left(_) | Either::Right(_)) => { + match self.ecx.local_to_op(local, None).report_err() { + Ok(op) => { + local_desc.value = match op.as_mplace_or_imm() { + Either::Right(imm) => format!("{imm}"), + Either::Left(_) => "".to_string(), + }; } - - Uninit => { - local_desc.value = "".to_string(); + Err(err) => { + local_desc.value = + format!("", interpret::format_interp_error(err)); } - }; + } } }; 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..1fb02e657e9e9 100644 --- a/src/tools/miri/priroda/tests/ui/locals_access_field.stdout +++ b/src/tools/miri/priroda/tests/ui/locals_access_field.stdout @@ -3,12 +3,12 @@ {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: _slice, Id: _2, Ty: &[u8], Value: (pointer to 0x20af8[alloc2]<328>, 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: _2, Ty: &[u8], Value: (pointer to 0x20af8[alloc2]<328>, 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.stdout b/src/tools/miri/priroda/tests/ui/locals_corpus_async.stdout index 03e00f88e488d..a2b94c788abad 100644 --- a/src/tools/miri/priroda/tests/ui/locals_corpus_async.stdout +++ b/src/tools/miri/priroda/tests/ui/locals_corpus_async.stdout @@ -7,13 +7,13 @@ (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: , Id: _1, Ty: (u32, i32), Value: (0x00000005, 0x00000006): (u32, i32) Name: first, Id: _1.0, Ty: u32, Value: Name: second, Id: _1.1, Ty: i32, Value: (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: , Id: _1, Ty: S, Value: {transmute(0x40a00000): S} Name: x, Id: _1.0, Ty: f32, Value: (priroda) Hit breakpoint {MANIFEST_DIR}/tests/ui/locals_corpus_async.rs:55 @@ -29,7 +29,7 @@ Name: deref, Id: _1 as variant#1.0.*, Ty: i32, Value: (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: , Id: _1, Ty: &mut std::option::Option, Value: {&_: &mut std::option::Option} Name: foo, Id: _1.* as variant#1.0, Ty: i32, Value: (priroda) Hit breakpoint {MANIFEST_DIR}/tests/ui/locals_corpus_async.rs:76 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..dcd9e0fb5508d --- /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: +Name: , Id: _2, Ty: &[u8], Value: +Name: , Id: _3, Ty: &[u8], Value: +(priroda) Id: _1, Ty: [u8], Value: +(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: +Name: , Id: _2, Ty: &dyn Marker, Value: +Name: , Id: _3, Ty: &dyn Marker, Value: +(priroda) Id: _1, Ty: dyn Marker, Value: +(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..b5bb004f56cf0 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 0x20af8[alloc2]<324>, 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..53dd04295e4f3 --- /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: +Name: consumed, Id: _2, Ty: std::boxed::Box, Value: +Name: , Id: _3, Ty: (), Value: +Name: , Id: _4, Ty: std::boxed::Box, Value: +Name: pointed_box, Id: _5, Ty: std::boxed::Box, Value: +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: +Name: scalar_pointer, Id: _9, Ty: &u8, Value: {&_: &u8} +Name: scalar_pair, Id: _10, Ty: &[u8], Value: (pointer to 0x212fa[alloc2]<489>, 0x0000000000000002): &[u8] +Name: , Id: _11, Ty: &[u8], Value: (pointer to 0x212fa[alloc2]<488>, 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: +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: +(priroda) Id: _7, Ty: u8, Value: 42_u8 +(priroda) Id: _8, Ty: u8, Value: +(priroda) Id: _13, Ty: [u8; 2], Value: +(priroda) Id: _15, Ty: Aggregate, Value: +(priroda) no local for this id +(priroda) quitting From 037fbe7661d3bd9350ac02d6c94d799a8fe06f02 Mon Sep 17 00:00:00 2001 From: Mohamed Ali Date: Thu, 16 Jul 2026 06:43:12 +0300 Subject: [PATCH 02/16] [Priroda] Render indirect locals with allocation dumps Resolve allocation-backed mplace locals and display their allocation contents with dump_alloc. Keep unsupported provenance as and update the fixtures for the temporary diagnostic allocation output. --- src/tools/miri/priroda/src/main.rs | 9 +++- .../tests/ui/locals_access_field.stdout | 14 +++++- .../tests/ui/locals_corpus_async.stdout | 13 ++++- .../tests/ui/locals_mplace_metadata.stdout | 30 +++++++++--- .../tests/ui/locals_value_shapes.stdout | 48 +++++++++++++++---- 5 files changed, 95 insertions(+), 19 deletions(-) diff --git a/src/tools/miri/priroda/src/main.rs b/src/tools/miri/priroda/src/main.rs index 6d9142c280875..47f0b9e1fee37 100644 --- a/src/tools/miri/priroda/src/main.rs +++ b/src/tools/miri/priroda/src/main.rs @@ -493,7 +493,14 @@ impl<'tcx> PrirodaContext<'tcx> { Ok(op) => { local_desc.value = match op.as_mplace_or_imm() { Either::Right(imm) => format!("{imm}"), - Either::Left(_) => "".to_string(), + + Either::Left(mplace_ty) => { + match mplace_ty.ptr().provenance.and_then(|p| p.get_alloc_id()) { + Some(alloc_id) => + format!("{:#?}", self.ecx.dump_alloc(alloc_id)), + None => "".to_string(), + } + } }; } Err(err) => { 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 1fb02e657e9e9..18c9fdd968374 100644 --- a/src/tools/miri/priroda/tests/ui/locals_access_field.stdout +++ b/src/tools/miri/priroda/tests/ui/locals_access_field.stdout @@ -2,11 +2,21 @@ (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: extraslice, Id: _1, Ty: ExtraSlice<'_>, Value: alloc153 (stack variable, size: 24, align: 8) { + 0x00 │ ╾0x20af8[alloc2]<327>─╼ 00 00 00 00 00 00 00 00 │ ╾──────╼........ + 0x10 │ 00 00 00 00 __ __ __ __ │ ....░░░░ +} +alloc2 (global (static or const), size: 0, align: 1) {} + Name: _slice, Id: _2, Ty: &[u8], Value: (pointer to 0x20af8[alloc2]<328>, 0x0000000000000000): &[u8] Name: _extra, Id: _3, Ty: u32, Value: 0_u32 (priroda) Id: _0, Ty: (), Value: -(priroda) Id: _1, Ty: ExtraSlice<'_>, Value: +(priroda) Id: _1, Ty: ExtraSlice<'_>, Value: alloc153 (stack variable, size: 24, align: 8) { + 0x00 │ ╾0x20af8[alloc2]<327>─╼ 00 00 00 00 00 00 00 00 │ ╾──────╼........ + 0x10 │ 00 00 00 00 __ __ __ __ │ ....░░░░ +} +alloc2 (global (static or const), size: 0, align: 1) {} + (priroda) Id: _2, Ty: &[u8], Value: (pointer to 0x20af8[alloc2]<328>, 0x0000000000000000): &[u8] (priroda) Id: _3, Ty: u32, Value: 0_u32 (priroda) no local for this id 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 a2b94c788abad..15b02f668ac36 100644 --- a/src/tools/miri/priroda/tests/ui/locals_corpus_async.stdout +++ b/src/tools/miri/priroda/tests/ui/locals_corpus_async.stdout @@ -18,12 +18,21 @@ Name: x, Id: _1.0, Ty: f32, Value: (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: , Id: _1, Ty: std::option::Option, Value: alloc155 (stack variable, size: 8, align: 4) { + 01 00 00 00 05 00 00 00 │ ........ +} + Name: inner, Id: _1 as variant#1.0, Ty: i32, Value: (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: , Id: _1, Ty: std::option::Option<&i32>, Value: alloc157 (stack variable, size: 8, align: 8) { + ╾0x20b3c[alloc2]<332>─╼ │ ╾──────╼ +} +alloc2 (global (static or const), size: 4, align: 4) { + 05 00 00 00 │ .... +} + Name: pointer, Id: _1 as variant#1.0, Ty: &i32, Value: Name: deref, Id: _1 as variant#1.0.*, Ty: i32, Value: (priroda) Hit breakpoint diff --git a/src/tools/miri/priroda/tests/ui/locals_mplace_metadata.stdout b/src/tools/miri/priroda/tests/ui/locals_mplace_metadata.stdout index dcd9e0fb5508d..fb7a9aa86f00d 100644 --- a/src/tools/miri/priroda/tests/ui/locals_mplace_metadata.stdout +++ b/src/tools/miri/priroda/tests/ui/locals_mplace_metadata.stdout @@ -4,22 +4,40 @@ (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: +Name: slice, Id: _1, Ty: [u8], Value: alloc273 (stack variable, size: 3, align: 1) { + 01 02 03 │ ... +} + Name: , Id: _2, Ty: &[u8], Value: Name: , Id: _3, Ty: &[u8], Value: -(priroda) Id: _1, Ty: [u8], Value: +(priroda) Id: _1, Ty: [u8], Value: alloc273 (stack variable, size: 3, align: 1) { + 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: text, Id: _1, Ty: str, Value: alloc478 (stack variable, size: 3, align: 1) { + __ __ __ │ ░░░ +} + Name: , Id: _2, Ty: &str, Value: Name: , Id: _3, Ty: &str, Value: -(priroda) Id: _1, Ty: str, Value: +(priroda) Id: _1, Ty: str, Value: alloc478 (stack variable, size: 3, align: 1) { + __ __ __ │ ░░░ +} + (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: +Name: value, Id: _1, Ty: dyn Marker, Value: alloc555 (stack variable, size: 8, align: 4) { + 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: +(priroda) Id: _1, Ty: dyn Marker, Value: alloc555 (stack variable, size: 8, align: 4) { + 01 00 00 00 02 00 00 00 │ ........ +} + (priroda) quitting diff --git a/src/tools/miri/priroda/tests/ui/locals_value_shapes.stdout b/src/tools/miri/priroda/tests/ui/locals_value_shapes.stdout index 53dd04295e4f3..0c9b33e366883 100644 --- a/src/tools/miri/priroda/tests/ui/locals_value_shapes.stdout +++ b/src/tools/miri/priroda/tests/ui/locals_value_shapes.stdout @@ -2,21 +2,41 @@ (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: -Name: consumed, Id: _2, Ty: std::boxed::Box, Value: +Name: dead_box, Id: _1, Ty: std::boxed::Box, Value: alloc266 (stack variable, size: 8, align: 8) { + ╾─0x20b95[a236]<374>──╼ │ ╾──────╼ +} +alloc236 (deallocated) + +Name: consumed, Id: _2, Ty: std::boxed::Box, Value: alloc267 (stack variable, size: 8, align: 8) { + ╾─0x20b95[a236]<376>──╼ │ ╾──────╼ +} +alloc236 (deallocated) + Name: , Id: _3, Ty: (), Value: Name: , Id: _4, Ty: std::boxed::Box, Value: -Name: pointed_box, Id: _5, Ty: std::boxed::Box, Value: +Name: pointed_box, Id: _5, Ty: std::boxed::Box, Value: alloc319 (stack variable, size: 8, align: 8) { + ╾─0x21030[a289]<468>──╼ │ ╾──────╼ +} +alloc289 (Rust heap, size: 1, align: 1) { + 11 │ . +} + 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: +Name: pointed, Id: _8, Ty: u8, Value: alloc320 (stack variable, size: 1, align: 1) { + 33 │ 3 +} + Name: scalar_pointer, Id: _9, Ty: &u8, Value: {&_: &u8} Name: scalar_pair, Id: _10, Ty: &[u8], Value: (pointer to 0x212fa[alloc2]<489>, 0x0000000000000002): &[u8] Name: , Id: _11, Ty: &[u8], Value: (pointer to 0x212fa[alloc2]<488>, 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: +Name: mplace, Id: _15, Ty: Aggregate, Value: alloc321 (stack variable, size: 4, align: 2) { + 34 12 2a __ │ 4.*░ +} + Name: , Id: _16, Ty: u8, Value: Name: uninit_scalar, Id: _17, Ty: u32, Value: Name: , Id: _18, Ty: (u8, &u8, usize, &Aggregate), Value: @@ -29,10 +49,22 @@ 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: +(priroda) Id: _5, Ty: std::boxed::Box, Value: alloc319 (stack variable, size: 8, align: 8) { + ╾─0x21030[a289]<468>──╼ │ ╾──────╼ +} +alloc289 (Rust heap, size: 1, align: 1) { + 11 │ . +} + (priroda) Id: _7, Ty: u8, Value: 42_u8 -(priroda) Id: _8, Ty: u8, Value: +(priroda) Id: _8, Ty: u8, Value: alloc320 (stack variable, size: 1, align: 1) { + 33 │ 3 +} + (priroda) Id: _13, Ty: [u8; 2], Value: -(priroda) Id: _15, Ty: Aggregate, Value: +(priroda) Id: _15, Ty: Aggregate, Value: alloc321 (stack variable, size: 4, align: 2) { + 34 12 2a __ │ 4.*░ +} + (priroda) no local for this id (priroda) quitting From cc00f34968aee7d14929714cf659ef16aa32938b Mon Sep 17 00:00:00 2001 From: The Miri Cronjob Bot Date: Sun, 19 Jul 2026 05:25:53 +0000 Subject: [PATCH 03/16] Prepare for merging from rust-lang/rust This updates the rust-version file to c904ba32e22c9d6fd31140b4283c5cc6c6b2fb5c. --- src/tools/miri/rust-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/miri/rust-version b/src/tools/miri/rust-version index 3dd271b8b5762..7dab7b32196a0 100644 --- a/src/tools/miri/rust-version +++ b/src/tools/miri/rust-version @@ -1 +1 @@ -4a9d5368df6450bbd2fc8dde4508c3e5d83bb19d +c904ba32e22c9d6fd31140b4283c5cc6c6b2fb5c From 18ba44950d62939f56f3851466d5d5de8f526725 Mon Sep 17 00:00:00 2001 From: Mohamed Ali Date: Thu, 16 Jul 2026 11:23:26 +0300 Subject: [PATCH 04/16] [Priroda] Render mplace local byte ranges Replace the diagnostic dump_alloc output with a Priroda-owned byte renderer for mplace-backed locals. Compute the current value size with size_and_align_of_val, inspect only the mplace byte range, and render uninitialized bytes with an explicit marker. --- src/tools/miri/priroda/README.md | 28 +++++++ src/tools/miri/priroda/src/main.rs | 77 ++++++++++++++----- .../tests/ui/locals_access_field.stdout | 14 +--- .../tests/ui/locals_corpus_async.stdout | 13 +--- .../tests/ui/locals_mplace_metadata.stdout | 30 ++------ .../tests/ui/locals_value_shapes.stdout | 48 ++---------- 6 files changed, 103 insertions(+), 107 deletions(-) diff --git a/src/tools/miri/priroda/README.md b/src/tools/miri/priroda/README.md index b9d946e08e544..aa5db4cae290e 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,34 @@ 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. | | `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 47f0b9e1fee37..c86a9832f6d75 100644 --- a/src/tools/miri/priroda/src/main.rs +++ b/src/tools/miri/priroda/src/main.rs @@ -18,7 +18,7 @@ use std::collections::{HashMap, HashSet}; use std::io::{self, Write}; use std::path::PathBuf; -use miri::Immediate::{Scalar, ScalarPair, Uninit}; +use miri::Immediate::Uninit; use miri::{interpret, *}; use rustc_driver::Compilation; use rustc_hir::attrs::CrateType; @@ -399,6 +399,49 @@ impl<'tcx> PrirodaContext<'tcx> { self.build_local_descs(frame) } + 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); + let alloc = self.ecx.get_alloc_raw(alloc_id)?; + + let mut rendered = Vec::with_capacity(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() { + rendered.extend( + alloc + .inspect_with_uninit_and_ptr_outside_interpreter(chunk_range) + .iter() + .map(|byte| format!("{byte:02x}")), + ); + } else { + rendered.extend(std::iter::repeat_n("__".to_string(), chunk_range.len())); + } + } + + interp_ok(format!("[{}]", rendered.join(" "))) + } + /// Render the source-side path from composite debug info, such as `.field`. fn render_source_projection( fragment: Option<&VarDebugInfoFragment<'tcx>>, @@ -489,25 +532,19 @@ impl<'tcx> PrirodaContext<'tcx> { Some(Either::Right(Uninit)) => local_desc.value = "".to_string(), Some(Either::Left(_) | Either::Right(_)) => { - match self.ecx.local_to_op(local, None).report_err() { - Ok(op) => { - local_desc.value = match op.as_mplace_or_imm() { - Either::Right(imm) => format!("{imm}"), - - Either::Left(mplace_ty) => { - match mplace_ty.ptr().provenance.and_then(|p| p.get_alloc_id()) { - Some(alloc_id) => - format!("{:#?}", self.ecx.dump_alloc(alloc_id)), - None => "".to_string(), - } - } - }; - } - Err(err) => { - local_desc.value = - format!("", interpret::format_interp_error(err)); - } - } + let op = self + .ecx + .local_to_op(local, None) + .expect("this error can only occur in CTFE on generic code"); + local_desc.value = 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)), + }, + }; } }; 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 18c9fdd968374..51bd1c7fa42fb 100644 --- a/src/tools/miri/priroda/tests/ui/locals_access_field.stdout +++ b/src/tools/miri/priroda/tests/ui/locals_access_field.stdout @@ -2,21 +2,11 @@ (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: alloc153 (stack variable, size: 24, align: 8) { - 0x00 │ ╾0x20af8[alloc2]<327>─╼ 00 00 00 00 00 00 00 00 │ ╾──────╼........ - 0x10 │ 00 00 00 00 __ __ __ __ │ ....░░░░ -} -alloc2 (global (static or const), size: 0, align: 1) {} - +Name: extraslice, Id: _1, Ty: ExtraSlice<'_>, Value: [f8 0a 02 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 __ __ __ __] Name: _slice, Id: _2, Ty: &[u8], Value: (pointer to 0x20af8[alloc2]<328>, 0x0000000000000000): &[u8] Name: _extra, Id: _3, Ty: u32, Value: 0_u32 (priroda) Id: _0, Ty: (), Value: -(priroda) Id: _1, Ty: ExtraSlice<'_>, Value: alloc153 (stack variable, size: 24, align: 8) { - 0x00 │ ╾0x20af8[alloc2]<327>─╼ 00 00 00 00 00 00 00 00 │ ╾──────╼........ - 0x10 │ 00 00 00 00 __ __ __ __ │ ....░░░░ -} -alloc2 (global (static or const), size: 0, align: 1) {} - +(priroda) Id: _1, Ty: ExtraSlice<'_>, Value: [f8 0a 02 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 __ __ __ __] (priroda) Id: _2, Ty: &[u8], Value: (pointer to 0x20af8[alloc2]<328>, 0x0000000000000000): &[u8] (priroda) Id: _3, Ty: u32, Value: 0_u32 (priroda) no local for this id 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 15b02f668ac36..2d6bda11505e7 100644 --- a/src/tools/miri/priroda/tests/ui/locals_corpus_async.stdout +++ b/src/tools/miri/priroda/tests/ui/locals_corpus_async.stdout @@ -18,21 +18,12 @@ Name: x, Id: _1.0, Ty: f32, Value: (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: alloc155 (stack variable, size: 8, align: 4) { - 01 00 00 00 05 00 00 00 │ ........ -} - +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: (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: alloc157 (stack variable, size: 8, align: 8) { - ╾0x20b3c[alloc2]<332>─╼ │ ╾──────╼ -} -alloc2 (global (static or const), size: 4, align: 4) { - 05 00 00 00 │ .... -} - +Name: , Id: _1, Ty: std::option::Option<&i32>, Value: [3c 0b 02 00 00 00 00 00] Name: pointer, Id: _1 as variant#1.0, Ty: &i32, Value: Name: deref, Id: _1 as variant#1.0.*, Ty: i32, Value: (priroda) Hit breakpoint diff --git a/src/tools/miri/priroda/tests/ui/locals_mplace_metadata.stdout b/src/tools/miri/priroda/tests/ui/locals_mplace_metadata.stdout index fb7a9aa86f00d..3a92ca95a5b68 100644 --- a/src/tools/miri/priroda/tests/ui/locals_mplace_metadata.stdout +++ b/src/tools/miri/priroda/tests/ui/locals_mplace_metadata.stdout @@ -4,40 +4,22 @@ (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: alloc273 (stack variable, size: 3, align: 1) { - 01 02 03 │ ... -} - +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: alloc273 (stack variable, size: 3, align: 1) { - 01 02 03 │ ... -} - +(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: alloc478 (stack variable, size: 3, align: 1) { - __ __ __ │ ░░░ -} - +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: alloc478 (stack variable, size: 3, align: 1) { - __ __ __ │ ░░░ -} - +(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: alloc555 (stack variable, size: 8, align: 4) { - 01 00 00 00 02 00 00 00 │ ........ -} - +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: alloc555 (stack variable, size: 8, align: 4) { - 01 00 00 00 02 00 00 00 │ ........ -} - +(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_value_shapes.stdout b/src/tools/miri/priroda/tests/ui/locals_value_shapes.stdout index 0c9b33e366883..f403756841ebb 100644 --- a/src/tools/miri/priroda/tests/ui/locals_value_shapes.stdout +++ b/src/tools/miri/priroda/tests/ui/locals_value_shapes.stdout @@ -2,41 +2,21 @@ (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: alloc266 (stack variable, size: 8, align: 8) { - ╾─0x20b95[a236]<374>──╼ │ ╾──────╼ -} -alloc236 (deallocated) - -Name: consumed, Id: _2, Ty: std::boxed::Box, Value: alloc267 (stack variable, size: 8, align: 8) { - ╾─0x20b95[a236]<376>──╼ │ ╾──────╼ -} -alloc236 (deallocated) - +Name: dead_box, Id: _1, Ty: std::boxed::Box, Value: [95 0b 02 00 00 00 00 00] +Name: consumed, Id: _2, Ty: std::boxed::Box, Value: [95 0b 02 00 00 00 00 00] Name: , Id: _3, Ty: (), Value: Name: , Id: _4, Ty: std::boxed::Box, Value: -Name: pointed_box, Id: _5, Ty: std::boxed::Box, Value: alloc319 (stack variable, size: 8, align: 8) { - ╾─0x21030[a289]<468>──╼ │ ╾──────╼ -} -alloc289 (Rust heap, size: 1, align: 1) { - 11 │ . -} - +Name: pointed_box, Id: _5, Ty: std::boxed::Box, Value: [30 10 02 00 00 00 00 00] 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: alloc320 (stack variable, size: 1, align: 1) { - 33 │ 3 -} - +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 0x212fa[alloc2]<489>, 0x0000000000000002): &[u8] Name: , Id: _11, Ty: &[u8], Value: (pointer to 0x212fa[alloc2]<488>, 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: alloc321 (stack variable, size: 4, align: 2) { - 34 12 2a __ │ 4.*░ -} - +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: @@ -49,22 +29,10 @@ 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: alloc319 (stack variable, size: 8, align: 8) { - ╾─0x21030[a289]<468>──╼ │ ╾──────╼ -} -alloc289 (Rust heap, size: 1, align: 1) { - 11 │ . -} - +(priroda) Id: _5, Ty: std::boxed::Box, Value: [30 10 02 00 00 00 00 00] (priroda) Id: _7, Ty: u8, Value: 42_u8 -(priroda) Id: _8, Ty: u8, Value: alloc320 (stack variable, size: 1, align: 1) { - 33 │ 3 -} - +(priroda) Id: _8, Ty: u8, Value: [33] (priroda) Id: _13, Ty: [u8; 2], Value: -(priroda) Id: _15, Ty: Aggregate, Value: alloc321 (stack variable, size: 4, align: 2) { - 34 12 2a __ │ 4.*░ -} - +(priroda) Id: _15, Ty: Aggregate, Value: [34 12 2a __] (priroda) no local for this id (priroda) quitting From 52cc3b1ac629d9911ad95384a51629f71f2cabed Mon Sep 17 00:00:00 2001 From: Mohamed Ali Date: Sun, 19 Jul 2026 04:44:53 +0300 Subject: [PATCH 05/16] [Priroda] Render provenance-aware local bytes Render complete pointer provenance while dumping indirect local values. Preserve initialized and uninitialized byte ranges, including padding and partial pointer bytes, and report unsupported unsized values explicitly. Add UI coverage for pointer positions, nonzero target offsets, uninitialized regions, and partial provenance. --- src/tools/miri/priroda/src/main.rs | 60 ++++++++++++++-- src/tools/miri/priroda/tests/cli.rs | 3 +- .../tests/ui/locals_access_field.stdout | 8 +-- .../tests/ui/locals_corpus_async.stdout | 2 +- .../tests/ui/locals_pointer_rendering.rs | 68 +++++++++++++++++++ .../tests/ui/locals_pointer_rendering.stderr | 0 .../tests/ui/locals_pointer_rendering.stdin | 5 ++ .../tests/ui/locals_pointer_rendering.stdout | 56 +++++++++++++++ .../miri/priroda/tests/ui/locals_sroa.stdout | 2 +- .../tests/ui/locals_value_shapes.stdout | 12 ++-- .../tests/ui/locals_wildcard_pointer.rs | 16 +++++ .../tests/ui/locals_wildcard_pointer.stderr | 0 .../tests/ui/locals_wildcard_pointer.stdin | 5 ++ .../tests/ui/locals_wildcard_pointer.stdout | 11 +++ 14 files changed, 228 insertions(+), 20 deletions(-) create mode 100644 src/tools/miri/priroda/tests/ui/locals_pointer_rendering.rs create mode 100644 src/tools/miri/priroda/tests/ui/locals_pointer_rendering.stderr create mode 100644 src/tools/miri/priroda/tests/ui/locals_pointer_rendering.stdin create mode 100644 src/tools/miri/priroda/tests/ui/locals_pointer_rendering.stdout create mode 100644 src/tools/miri/priroda/tests/ui/locals_wildcard_pointer.rs create mode 100644 src/tools/miri/priroda/tests/ui/locals_wildcard_pointer.stderr create mode 100644 src/tools/miri/priroda/tests/ui/locals_wildcard_pointer.stdin create mode 100644 src/tools/miri/priroda/tests/ui/locals_wildcard_pointer.stdout diff --git a/src/tools/miri/priroda/src/main.rs b/src/tools/miri/priroda/src/main.rs index c86a9832f6d75..3ce576585403a 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,10 +17,12 @@ extern crate rustc_type_ir; use std::collections::{HashMap, HashSet}; use std::io::{self, Write}; +use std::ops::Range; use std::path::PathBuf; use miri::Immediate::Uninit; use miri::{interpret, *}; +use rustc_abi::Size; use rustc_driver::Compilation; use rustc_hir::attrs::CrateType; use rustc_interface::interface; @@ -399,6 +402,10 @@ 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, @@ -419,21 +426,60 @@ impl<'tcx> PrirodaContext<'tcx> { 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(size); + 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() { - rendered.extend( - alloc - .inspect_with_uninit_and_ptr_outside_interpreter(chunk_range) - .iter() - .map(|byte| format!("{byte:02x}")), - ); + 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())); } 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 51bd1c7fa42fb..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,12 +2,12 @@ (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: [f8 0a 02 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 __ __ __ __] -Name: _slice, Id: _2, Ty: &[u8], Value: (pointer to 0x20af8[alloc2]<328>, 0x0000000000000000): &[u8] +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: [f8 0a 02 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 __ __ __ __] -(priroda) Id: _2, Ty: &[u8], Value: (pointer to 0x20af8[alloc2]<328>, 0x0000000000000000): &[u8] +(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 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 2d6bda11505e7..5dd879d6c43fc 100644 --- a/src/tools/miri/priroda/tests/ui/locals_corpus_async.stdout +++ b/src/tools/miri/priroda/tests/ui/locals_corpus_async.stdout @@ -23,7 +23,7 @@ Name: inner, Id: _1 as variant#1.0, Ty: i32, Value: (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: [3c 0b 02 00 00 00 00 00] +Name: , Id: _1, Ty: std::option::Option<&i32>, Value: [{ALLOC_PTR}] Name: pointer, Id: _1 as variant#1.0, Ty: &i32, Value: Name: deref, Id: _1 as variant#1.0.*, Ty: i32, Value: (priroda) Hit breakpoint 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_sroa.stdout b/src/tools/miri/priroda/tests/ui/locals_sroa.stdout index b5bb004f56cf0..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: (pointer to 0x20af8[alloc2]<324>, 0x0000000000000002): &[u8] +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_value_shapes.stdout b/src/tools/miri/priroda/tests/ui/locals_value_shapes.stdout index f403756841ebb..4250c443221f4 100644 --- a/src/tools/miri/priroda/tests/ui/locals_value_shapes.stdout +++ b/src/tools/miri/priroda/tests/ui/locals_value_shapes.stdout @@ -2,17 +2,17 @@ (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: [95 0b 02 00 00 00 00 00] -Name: consumed, Id: _2, Ty: std::boxed::Box, Value: [95 0b 02 00 00 00 00 00] +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: [30 10 02 00 00 00 00 00] +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 0x212fa[alloc2]<489>, 0x0000000000000002): &[u8] -Name: , Id: _11, Ty: &[u8], Value: (pointer to 0x212fa[alloc2]<488>, 0x0000000000000002): &[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: @@ -29,7 +29,7 @@ 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: [30 10 02 00 00 00 00 00] +(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: 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 From 0726da7124a777b50aec877a58a12992788dbc33 Mon Sep 17 00:00:00 2001 From: Mohamed Ali Date: Sun, 19 Jul 2026 06:40:26 +0300 Subject: [PATCH 06/16] [Priroda] Add follow command for allocation bytes Add a `follow` command that renders allocation bytes starting at a given allocation offset, reusing the provenance-aware allocation byte renderer used for indirect locals. Document the command and cover following a rendered pointer allocation in the pointer-rendering UI test. --- src/tools/miri/priroda/README.md | 1 + src/tools/miri/priroda/src/main.rs | 35 +++++++++++++++++++ .../tests/ui/locals_corpus_async.stdin | 1 + .../tests/ui/locals_corpus_async.stdout | 1 + 4 files changed, 38 insertions(+) diff --git a/src/tools/miri/priroda/README.md b/src/tools/miri/priroda/README.md index aa5db4cae290e..a8c25bf279868 100644 --- a/src/tools/miri/priroda/README.md +++ b/src/tools/miri/priroda/README.md @@ -68,6 +68,7 @@ RUSTC_BLESS=1 cargo test | `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 diff --git a/src/tools/miri/priroda/src/main.rs b/src/tools/miri/priroda/src/main.rs index 3ce576585403a..4fae8e02d9f0a 100644 --- a/src/tools/miri/priroda/src/main.rs +++ b/src/tools/miri/priroda/src/main.rs @@ -17,6 +17,7 @@ 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; @@ -26,6 +27,7 @@ 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; @@ -380,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()?; @@ -681,6 +698,7 @@ enum DebuggerCommand { Breakpoint(PathBuf, usize), ListLocals, Print(usize), + Follow(AllocId, usize), } enum BreakpointSetResult { @@ -694,6 +712,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, @@ -782,6 +801,7 @@ impl Cli { } None => println!("no local for this id"), }, + CommandResult::Memory(memory) => println!("{memory}"), CommandResult::TerminateSession => { println!("quitting"); return interp_ok(()); @@ -814,6 +834,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, } } @@ -846,4 +867,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/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 5dd879d6c43fc..54f10f1e97ad4 100644 --- a/src/tools/miri/priroda/tests/ui/locals_corpus_async.stdout +++ b/src/tools/miri/priroda/tests/ui/locals_corpus_async.stdout @@ -26,6 +26,7 @@ Name: inner, 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: Name: deref, Id: _1 as variant#1.0.*, Ty: i32, Value: +(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: From f9c7888cf5c31fe6b4986627d7ae058aacf0a5b7 Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Mon, 20 Jul 2026 15:00:51 +0200 Subject: [PATCH 07/16] support `neon.tbl*` intrinsics --- src/tools/miri/src/intrinsics/aarch64.rs | 63 +++++-- .../shims/aarch64/intrinsics-aarch64-neon.rs | 170 ++++++++++++++---- 2 files changed, 185 insertions(+), 48 deletions(-) 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/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]); } } From 763fe4d6af76efca012919161bc90a2162de14d6 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 18 Jul 2026 10:09:52 +0200 Subject: [PATCH 08/16] add a riscv target to CI --- src/tools/miri/ci/ci.sh | 1 + src/tools/miri/tests/pass-dep/libc/pthread-sync.rs | 8 +++++--- 2 files changed, 6 insertions(+), 3 deletions(-) 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/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(); From 810cdc03c3fea095c7c0a6af60029788ec714e2b Mon Sep 17 00:00:00 2001 From: Nicolas Iooss Date: Tue, 21 Jul 2026 12:06:34 +0200 Subject: [PATCH 09/16] readme: fix misspelling of `-Ctarget-feature` When using `RUSTFLAGS="-Ctarget-features=+avx512f"`, the Rust compiler complains: error: unknown codegen option: `target-features` The actual option uses the singular form. --- src/tools/miri/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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`. From afefb4f75633c781ea754c8c01f054e8f8eb2c3d Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Tue, 21 Jul 2026 09:57:02 +0200 Subject: [PATCH 10/16] bump libc --- src/tools/miri/tests/deps/Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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" From 6ca51276e7fb33b59dda79e1ff4eabc053b422af Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Wed, 22 Jul 2026 08:23:12 +0200 Subject: [PATCH 11/16] Prepare for merging from rust-lang/rust This updates the rust-version file to 1af98b7cdf86932b76182a387f52f1be6f0c1be0. --- src/tools/miri/rust-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/miri/rust-version b/src/tools/miri/rust-version index 7dab7b32196a0..f6d7daa057750 100644 --- a/src/tools/miri/rust-version +++ b/src/tools/miri/rust-version @@ -1 +1 @@ -c904ba32e22c9d6fd31140b4283c5cc6c6b2fb5c +1af98b7cdf86932b76182a387f52f1be6f0c1be0 From 94fde0cb52c1d77d77a6b1f69000dee1cf47ecbb Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Wed, 22 Jul 2026 08:26:11 +0200 Subject: [PATCH 12/16] Revert "disable `portable_simd` miri test that needs additional intrinsic support" This reverts commit 785e740bb5c1d9166717c93612f7b9f99fee1caf. The additional intrinsic support has meanwhile been implemented. --- src/tools/miri/tests/pass/intrinsics/portable-simd.rs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/tools/miri/tests/pass/intrinsics/portable-simd.rs b/src/tools/miri/tests/pass/intrinsics/portable-simd.rs index e507e92624bde..ac7838247f288 100644 --- a/src/tools/miri/tests/pass/intrinsics/portable-simd.rs +++ b/src/tools/miri/tests/pass/intrinsics/portable-simd.rs @@ -766,11 +766,6 @@ 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") { - return; - } - fn check_swizzle_dyn() { assert_eq!( Simd::::default().swizzle_dyn(Simd::::default()), From cabd1f14d6f56d490b37a2674982adb8445ab3a3 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Wed, 22 Jul 2026 09:11:11 +0200 Subject: [PATCH 13/16] disable swizzle_dyn test on loongarch --- src/tools/miri/tests/pass/intrinsics/portable-simd.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/tools/miri/tests/pass/intrinsics/portable-simd.rs b/src/tools/miri/tests/pass/intrinsics/portable-simd.rs index ac7838247f288..a3991f78b1166 100644 --- a/src/tools/miri/tests/pass/intrinsics/portable-simd.rs +++ b/src/tools/miri/tests/pass/intrinsics/portable-simd.rs @@ -766,6 +766,11 @@ fn simd_swizzle() { } fn simd_swizzle_dyn() { + if cfg!(target_arch = "loongarch64") { + // We don't support the required intrinsic here. + return; + } + fn check_swizzle_dyn() { assert_eq!( Simd::::default().swizzle_dyn(Simd::::default()), From 525e1aaf891eefaa57cc8265688c9ea9706304e6 Mon Sep 17 00:00:00 2001 From: The Miri Cronjob Bot Date: Thu, 23 Jul 2026 05:28:39 +0000 Subject: [PATCH 14/16] Prepare for merging from rust-lang/rust This updates the rust-version file to 390279b302ca98ae270f434100ae3730531d1246. --- src/tools/miri/rust-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/miri/rust-version b/src/tools/miri/rust-version index f6d7daa057750..25a5238f538ee 100644 --- a/src/tools/miri/rust-version +++ b/src/tools/miri/rust-version @@ -1 +1 @@ -1af98b7cdf86932b76182a387f52f1be6f0c1be0 +390279b302ca98ae270f434100ae3730531d1246 From 21b23c279f515a13640f80735bd45c50d7263092 Mon Sep 17 00:00:00 2001 From: Mohamed Ali Date: Thu, 23 Jul 2026 09:13:13 +0300 Subject: [PATCH 15/16] [Priroda] Test projected mplace value range rendering Add a CLI fixture covering a projected memory-backed field inside a larger aggregate. This locks in that projected values render only their own MPlace layout bytes instead of trailing bytes from the parent allocation. --- .../tests/ui/locals_projected_mplace_size.rs | 48 +++++++++++++++++++ .../ui/locals_projected_mplace_size.stdin | 4 ++ .../ui/locals_projected_mplace_size.stdout | 10 ++++ 3 files changed, 62 insertions(+) create mode 100644 src/tools/miri/priroda/tests/ui/locals_projected_mplace_size.rs create mode 100644 src/tools/miri/priroda/tests/ui/locals_projected_mplace_size.stdin create mode 100644 src/tools/miri/priroda/tests/ui/locals_projected_mplace_size.stdout 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..46e84edf0d929 --- /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: +Name: target, Id: _1.1, Ty: Payload, Value: +Name: trailer, Id: _1.2, Ty: u64, Value: +Name: checksum, Id: _1.3, Ty: u16, Value: +(priroda) quitting From d37c84d1d30559c470b8cc0f62e1f6f53b2ba8fb Mon Sep 17 00:00:00 2001 From: Mohamed Ali Date: Tue, 21 Jul 2026 10:02:19 +0300 Subject: [PATCH 16/16] [Priroda] Render projected debug-info values Keep projected MIR Places long enough to evaluate them with `eval_place_to_op`, then reuse the existing raw operand renderer for immediate and memory-backed values. This removes `` from projected locals, including fields, enum projections, references, and dereferences. Update the projected debug-info UI expectations and verify the full Priroda suite. --- src/tools/miri/priroda/src/main.rs | 37 +++++++++++++------ .../tests/ui/locals_corpus_async.stdout | 14 +++---- .../ui/locals_projected_mplace_size.stdout | 8 ++-- 3 files changed, 36 insertions(+), 23 deletions(-) diff --git a/src/tools/miri/priroda/src/main.rs b/src/tools/miri/priroda/src/main.rs index ff517079aa815..e748ae7340aa2 100644 --- a/src/tools/miri/priroda/src/main.rs +++ b/src/tools/miri/priroda/src/main.rs @@ -505,6 +505,20 @@ impl<'tcx> PrirodaContext<'tcx> { 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>>, @@ -599,15 +613,7 @@ impl<'tcx> PrirodaContext<'tcx> { .ecx .local_to_op(local, None) .expect("this error can only occur in CTFE on generic code"); - local_desc.value = 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)), - }, - }; + local_desc.value = self.render_op(op); } }; @@ -658,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() @@ -672,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), @@ -679,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, }); } } 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 54f10f1e97ad4..6ccfbc997c590 100644 --- a/src/tools/miri/priroda/tests/ui/locals_corpus_async.stdout +++ b/src/tools/miri/priroda/tests/ui/locals_corpus_async.stdout @@ -8,30 +8,30 @@ {MANIFEST_DIR}/tests/ui/locals_corpus_async.rs:31 (priroda) Name: , Id: _0, Ty: (), Value: Name: , Id: _1, Ty: (u32, i32), Value: (0x00000005, 0x00000006): (u32, i32) -Name: first, Id: _1.0, Ty: u32, Value: -Name: second, Id: _1.1, Ty: i32, Value: +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: {transmute(0x40a00000): S} -Name: x, Id: _1.0, Ty: f32, Value: +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: [01 00 00 00 05 00 00 00] -Name: inner, Id: _1 as variant#1.0, Ty: i32, Value: +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: [{ALLOC_PTR}] -Name: pointer, Id: _1 as variant#1.0, Ty: &i32, Value: -Name: deref, Id: _1 as variant#1.0.*, Ty: i32, Value: +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: {&_: &mut std::option::Option} -Name: foo, Id: _1.* as variant#1.0, Ty: i32, Value: +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_projected_mplace_size.stdout b/src/tools/miri/priroda/tests/ui/locals_projected_mplace_size.stdout index 46e84edf0d929..a2111bed511aa 100644 --- a/src/tools/miri/priroda/tests/ui/locals_projected_mplace_size.stdout +++ b/src/tools/miri/priroda/tests/ui/locals_projected_mplace_size.stdout @@ -3,8 +3,8 @@ {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: -Name: target, Id: _1.1, Ty: Payload, Value: -Name: trailer, Id: _1.2, Ty: u64, Value: -Name: checksum, Id: _1.3, Ty: u16, Value: +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