From ea96b7905626aed8c7a8c25825151d1106983144 Mon Sep 17 00:00:00 2001 From: Kivooeo Date: Tue, 26 Aug 2025 20:18:13 +0000 Subject: [PATCH 01/28] add parser check for pointer typo --- compiler/rustc_parse/src/parser/ty.rs | 39 +++ .../did_you_mean/c-style-pointer-types.fixed | 113 +++++++ .../ui/did_you_mean/c-style-pointer-types.rs | 113 +++++++ .../did_you_mean/c-style-pointer-types.stderr | 302 ++++++++++++++++++ 4 files changed, 567 insertions(+) create mode 100644 tests/ui/did_you_mean/c-style-pointer-types.fixed create mode 100644 tests/ui/did_you_mean/c-style-pointer-types.rs create mode 100644 tests/ui/did_you_mean/c-style-pointer-types.stderr diff --git a/compiler/rustc_parse/src/parser/ty.rs b/compiler/rustc_parse/src/parser/ty.rs index 6168647183fdb..92703d1a913e6 100644 --- a/compiler/rustc_parse/src/parser/ty.rs +++ b/compiler/rustc_parse/src/parser/ty.rs @@ -398,6 +398,10 @@ impl<'a> Parser<'a> { // Qualified path let (qself, path) = self.parse_qpath(PathStyle::Type)?; TyKind::Path(Some(qself), path) + } else if (self.token.is_keyword(kw::Const) || self.token.is_keyword(kw::Mut)) + && self.look_ahead(1, |t| *t == token::Star) + { + self.parse_ty_c_style_pointer()? } else if self.check_path() { self.parse_path_start_ty(lo, allow_plus, ty_generics)? } else if self.can_begin_bound() { @@ -579,6 +583,41 @@ impl<'a> Parser<'a> { Ok(TyKind::TraitObject(bounds, TraitObjectSyntax::None)) } + /// Parses a raw pointer with a C-style typo + fn parse_ty_c_style_pointer(&mut self) -> PResult<'a, TyKind> { + let kw_span = self.token.span; + let mutbl = self.parse_const_or_mut(); + + if let Some(mutbl) = mutbl + && self.eat(exp!(Star)) + { + let star_span = self.prev_token.span; + + let mutability = match mutbl { + Mutability::Not => "const", + Mutability::Mut => "mut", + }; + + let ty = self.parse_ty_no_question_mark_recover()?; + + self.dcx() + .struct_span_err( + kw_span, + format!("raw pointer types must be written as `*{mutability} T`"), + ) + .with_multipart_suggestion( + format!("put the `*` before `{mutability}`"), + vec![(star_span, String::new()), (kw_span.shrink_to_lo(), "*".to_string())], + Applicability::MachineApplicable, + ) + .emit(); + + return Ok(TyKind::Ptr(MutTy { ty, mutbl })); + } + // This is unreachable because we always get into if above and return from it + unreachable!("this could never happen") + } + /// Parses a raw pointer type: `*[const | mut] $type`. fn parse_ty_ptr(&mut self) -> PResult<'a, TyKind> { let mutbl = self.parse_const_or_mut().unwrap_or_else(|| { diff --git a/tests/ui/did_you_mean/c-style-pointer-types.fixed b/tests/ui/did_you_mean/c-style-pointer-types.fixed new file mode 100644 index 0000000000000..2517c8f1954d9 --- /dev/null +++ b/tests/ui/did_you_mean/c-style-pointer-types.fixed @@ -0,0 +1,113 @@ +//@ run-rustfix + +#![allow(unused)] + +pub const P1: *const u8 = 0 as _; +//~^ ERROR: raw pointer types must be written as `*const T` +//~| HELP: put the `*` before `const` + +pub const P2: *mut u8 = 1 as _; +//~^ ERROR: raw pointer types must be written as `*mut T` +//~| HELP: put the `*` before `mut` + +pub const P3: *const i32 = std::ptr::null(); +//~^ ERROR: raw pointer types must be written as `*const T` +//~| HELP: put the `*` before `const` + +pub const P4: *const i32 = std::ptr::null(); +//~^ ERROR: raw pointer types must be written as `*const T` +//~| HELP: put the `*` before `const` + +pub const P5: *mut i32 = std::ptr::null_mut(); +//~^ ERROR: raw pointer types must be written as `*mut T` +//~| HELP: put the `*` before `mut` + +pub const P6: *mut i32 = std::ptr::null_mut(); +//~^ ERROR: raw pointer types must be written as `*mut T` +//~| HELP: put the `*` before `mut` + +pub const P7: *const Vec = std::ptr::null(); +//~^ ERROR: raw pointer types must be written as `*const T` +//~| HELP: put the `*` before `const` + +pub const P8: *const std::collections::HashMap = std::ptr::null(); +//~^ ERROR: raw pointer types must be written as `*const T` +//~| HELP: put the `*` before `const` + +fn func1(p: *const u8) {} +//~^ ERROR: raw pointer types must be written as `*const T` +//~| HELP: put the `*` before `const` + +fn func2(p: *mut u8) {} +//~^ ERROR: raw pointer types must be written as `*mut T` +//~| HELP: put the `*` before `mut` + +fn func3() -> *const u8 { std::ptr::null() } +//~^ ERROR: raw pointer types must be written as `*const T` +//~| HELP: put the `*` before `const` + +fn func4() -> *mut u8 { std::ptr::null_mut() } +//~^ ERROR: raw pointer types must be written as `*mut T` +//~| HELP: put the `*` before `mut` + +struct S1 { + field: *const u8, + //~^ ERROR: raw pointer types must be written as `*const T` + //~| HELP: put the `*` before `const` +} + +struct S2 { + field: *mut u8, + //~^ ERROR: raw pointer types must be written as `*mut T` + //~| HELP: put the `*` before `mut` +} + +type Tuple1 = (*const u8, i32); +//~^ ERROR: raw pointer types must be written as `*const T` +//~| HELP: put the `*` before `const` + +type Tuple2 = (*mut u8, i32); +//~^ ERROR: raw pointer types must be written as `*mut T` +//~| HELP: put the `*` before `mut` + +type Array1 = [*const u8; 10]; +//~^ ERROR: raw pointer types must be written as `*const T` +//~| HELP: put the `*` before `const` + +type Array2 = [*mut u8; 10]; +//~^ ERROR: raw pointer types must be written as `*mut T` +//~| HELP: put the `*` before `mut` + +type Alias1 = *const u8; +//~^ ERROR: raw pointer types must be written as `*const T` +//~| HELP: put the `*` before `const` + +type Alias2 = *mut u8; +//~^ ERROR: raw pointer types must be written as `*mut T` +//~| HELP: put the `*` before `mut` + +pub const P9: *const u8 = std::ptr::null(); +//~^ ERROR: raw pointer types must be written as `*const T` +//~| HELP: put the `*` before `const` + +pub const P10: *const u8 = std::ptr::null(); +//~^ ERROR: raw pointer types must be written as `*const T` +//~| HELP: put the `*` before `const` + +impl S1 { + fn method(self, size: *const u32) {} + //~^ ERROR: raw pointer types must be written as `*const T` + //~| HELP: put the `*` before `const` +} + +trait Trait1 { + fn method(p: *const u8); + //~^ ERROR: raw pointer types must be written as `*const T` + //~| HELP: put the `*` before `const` +} + +fn generic_func() -> *const T { std::ptr::null() } +//~^ ERROR: raw pointer types must be written as `*const T` +//~| HELP: put the `*` before `const` + +fn main() {} diff --git a/tests/ui/did_you_mean/c-style-pointer-types.rs b/tests/ui/did_you_mean/c-style-pointer-types.rs new file mode 100644 index 0000000000000..562d85fb75fb8 --- /dev/null +++ b/tests/ui/did_you_mean/c-style-pointer-types.rs @@ -0,0 +1,113 @@ +//@ run-rustfix + +#![allow(unused)] + +pub const P1: const* u8 = 0 as _; +//~^ ERROR: raw pointer types must be written as `*const T` +//~| HELP: put the `*` before `const` + +pub const P2: mut* u8 = 1 as _; +//~^ ERROR: raw pointer types must be written as `*mut T` +//~| HELP: put the `*` before `mut` + +pub const P3: const* i32 = std::ptr::null(); +//~^ ERROR: raw pointer types must be written as `*const T` +//~| HELP: put the `*` before `const` + +pub const P4: const* i32 = std::ptr::null(); +//~^ ERROR: raw pointer types must be written as `*const T` +//~| HELP: put the `*` before `const` + +pub const P5: mut* i32 = std::ptr::null_mut(); +//~^ ERROR: raw pointer types must be written as `*mut T` +//~| HELP: put the `*` before `mut` + +pub const P6: mut* i32 = std::ptr::null_mut(); +//~^ ERROR: raw pointer types must be written as `*mut T` +//~| HELP: put the `*` before `mut` + +pub const P7: const* Vec = std::ptr::null(); +//~^ ERROR: raw pointer types must be written as `*const T` +//~| HELP: put the `*` before `const` + +pub const P8: const* std::collections::HashMap = std::ptr::null(); +//~^ ERROR: raw pointer types must be written as `*const T` +//~| HELP: put the `*` before `const` + +fn func1(p: const* u8) {} +//~^ ERROR: raw pointer types must be written as `*const T` +//~| HELP: put the `*` before `const` + +fn func2(p: mut* u8) {} +//~^ ERROR: raw pointer types must be written as `*mut T` +//~| HELP: put the `*` before `mut` + +fn func3() -> const* u8 { std::ptr::null() } +//~^ ERROR: raw pointer types must be written as `*const T` +//~| HELP: put the `*` before `const` + +fn func4() -> mut* u8 { std::ptr::null_mut() } +//~^ ERROR: raw pointer types must be written as `*mut T` +//~| HELP: put the `*` before `mut` + +struct S1 { + field: const* u8, + //~^ ERROR: raw pointer types must be written as `*const T` + //~| HELP: put the `*` before `const` +} + +struct S2 { + field: mut* u8, + //~^ ERROR: raw pointer types must be written as `*mut T` + //~| HELP: put the `*` before `mut` +} + +type Tuple1 = (const* u8, i32); +//~^ ERROR: raw pointer types must be written as `*const T` +//~| HELP: put the `*` before `const` + +type Tuple2 = (mut* u8, i32); +//~^ ERROR: raw pointer types must be written as `*mut T` +//~| HELP: put the `*` before `mut` + +type Array1 = [const* u8; 10]; +//~^ ERROR: raw pointer types must be written as `*const T` +//~| HELP: put the `*` before `const` + +type Array2 = [mut* u8; 10]; +//~^ ERROR: raw pointer types must be written as `*mut T` +//~| HELP: put the `*` before `mut` + +type Alias1 = const* u8; +//~^ ERROR: raw pointer types must be written as `*const T` +//~| HELP: put the `*` before `const` + +type Alias2 = mut* u8; +//~^ ERROR: raw pointer types must be written as `*mut T` +//~| HELP: put the `*` before `mut` + +pub const P9: const *u8 = std::ptr::null(); +//~^ ERROR: raw pointer types must be written as `*const T` +//~| HELP: put the `*` before `const` + +pub const P10: const * u8 = std::ptr::null(); +//~^ ERROR: raw pointer types must be written as `*const T` +//~| HELP: put the `*` before `const` + +impl S1 { + fn method(self, size: const* u32) {} + //~^ ERROR: raw pointer types must be written as `*const T` + //~| HELP: put the `*` before `const` +} + +trait Trait1 { + fn method(p: const* u8); + //~^ ERROR: raw pointer types must be written as `*const T` + //~| HELP: put the `*` before `const` +} + +fn generic_func() -> const* T { std::ptr::null() } +//~^ ERROR: raw pointer types must be written as `*const T` +//~| HELP: put the `*` before `const` + +fn main() {} diff --git a/tests/ui/did_you_mean/c-style-pointer-types.stderr b/tests/ui/did_you_mean/c-style-pointer-types.stderr new file mode 100644 index 0000000000000..bbab72c016629 --- /dev/null +++ b/tests/ui/did_you_mean/c-style-pointer-types.stderr @@ -0,0 +1,302 @@ +error: raw pointer types must be written as `*const T` + --> $DIR/c-style-pointer-types.rs:5:15 + | +LL | pub const P1: const* u8 = 0 as _; + | ^^^^^ + | +help: put the `*` before `const` + | +LL - pub const P1: const* u8 = 0 as _; +LL + pub const P1: *const u8 = 0 as _; + | + +error: raw pointer types must be written as `*mut T` + --> $DIR/c-style-pointer-types.rs:9:15 + | +LL | pub const P2: mut* u8 = 1 as _; + | ^^^ + | +help: put the `*` before `mut` + | +LL - pub const P2: mut* u8 = 1 as _; +LL + pub const P2: *mut u8 = 1 as _; + | + +error: raw pointer types must be written as `*const T` + --> $DIR/c-style-pointer-types.rs:13:15 + | +LL | pub const P3: const* i32 = std::ptr::null(); + | ^^^^^ + | +help: put the `*` before `const` + | +LL - pub const P3: const* i32 = std::ptr::null(); +LL + pub const P3: *const i32 = std::ptr::null(); + | + +error: raw pointer types must be written as `*const T` + --> $DIR/c-style-pointer-types.rs:17:15 + | +LL | pub const P4: const* i32 = std::ptr::null(); + | ^^^^^ + | +help: put the `*` before `const` + | +LL - pub const P4: const* i32 = std::ptr::null(); +LL + pub const P4: *const i32 = std::ptr::null(); + | + +error: raw pointer types must be written as `*mut T` + --> $DIR/c-style-pointer-types.rs:21:15 + | +LL | pub const P5: mut* i32 = std::ptr::null_mut(); + | ^^^ + | +help: put the `*` before `mut` + | +LL - pub const P5: mut* i32 = std::ptr::null_mut(); +LL + pub const P5: *mut i32 = std::ptr::null_mut(); + | + +error: raw pointer types must be written as `*mut T` + --> $DIR/c-style-pointer-types.rs:25:15 + | +LL | pub const P6: mut* i32 = std::ptr::null_mut(); + | ^^^ + | +help: put the `*` before `mut` + | +LL - pub const P6: mut* i32 = std::ptr::null_mut(); +LL + pub const P6: *mut i32 = std::ptr::null_mut(); + | + +error: raw pointer types must be written as `*const T` + --> $DIR/c-style-pointer-types.rs:29:15 + | +LL | pub const P7: const* Vec = std::ptr::null(); + | ^^^^^ + | +help: put the `*` before `const` + | +LL - pub const P7: const* Vec = std::ptr::null(); +LL + pub const P7: *const Vec = std::ptr::null(); + | + +error: raw pointer types must be written as `*const T` + --> $DIR/c-style-pointer-types.rs:33:15 + | +LL | pub const P8: const* std::collections::HashMap = std::ptr::null(); + | ^^^^^ + | +help: put the `*` before `const` + | +LL - pub const P8: const* std::collections::HashMap = std::ptr::null(); +LL + pub const P8: *const std::collections::HashMap = std::ptr::null(); + | + +error: raw pointer types must be written as `*const T` + --> $DIR/c-style-pointer-types.rs:37:13 + | +LL | fn func1(p: const* u8) {} + | ^^^^^ + | +help: put the `*` before `const` + | +LL - fn func1(p: const* u8) {} +LL + fn func1(p: *const u8) {} + | + +error: raw pointer types must be written as `*mut T` + --> $DIR/c-style-pointer-types.rs:41:13 + | +LL | fn func2(p: mut* u8) {} + | ^^^ + | +help: put the `*` before `mut` + | +LL - fn func2(p: mut* u8) {} +LL + fn func2(p: *mut u8) {} + | + +error: raw pointer types must be written as `*const T` + --> $DIR/c-style-pointer-types.rs:45:15 + | +LL | fn func3() -> const* u8 { std::ptr::null() } + | ^^^^^ + | +help: put the `*` before `const` + | +LL - fn func3() -> const* u8 { std::ptr::null() } +LL + fn func3() -> *const u8 { std::ptr::null() } + | + +error: raw pointer types must be written as `*mut T` + --> $DIR/c-style-pointer-types.rs:49:15 + | +LL | fn func4() -> mut* u8 { std::ptr::null_mut() } + | ^^^ + | +help: put the `*` before `mut` + | +LL - fn func4() -> mut* u8 { std::ptr::null_mut() } +LL + fn func4() -> *mut u8 { std::ptr::null_mut() } + | + +error: raw pointer types must be written as `*const T` + --> $DIR/c-style-pointer-types.rs:54:12 + | +LL | field: const* u8, + | ^^^^^ + | +help: put the `*` before `const` + | +LL - field: const* u8, +LL + field: *const u8, + | + +error: raw pointer types must be written as `*mut T` + --> $DIR/c-style-pointer-types.rs:60:12 + | +LL | field: mut* u8, + | ^^^ + | +help: put the `*` before `mut` + | +LL - field: mut* u8, +LL + field: *mut u8, + | + +error: raw pointer types must be written as `*const T` + --> $DIR/c-style-pointer-types.rs:65:16 + | +LL | type Tuple1 = (const* u8, i32); + | ^^^^^ + | +help: put the `*` before `const` + | +LL - type Tuple1 = (const* u8, i32); +LL + type Tuple1 = (*const u8, i32); + | + +error: raw pointer types must be written as `*mut T` + --> $DIR/c-style-pointer-types.rs:69:16 + | +LL | type Tuple2 = (mut* u8, i32); + | ^^^ + | +help: put the `*` before `mut` + | +LL - type Tuple2 = (mut* u8, i32); +LL + type Tuple2 = (*mut u8, i32); + | + +error: raw pointer types must be written as `*const T` + --> $DIR/c-style-pointer-types.rs:73:16 + | +LL | type Array1 = [const* u8; 10]; + | ^^^^^ + | +help: put the `*` before `const` + | +LL - type Array1 = [const* u8; 10]; +LL + type Array1 = [*const u8; 10]; + | + +error: raw pointer types must be written as `*mut T` + --> $DIR/c-style-pointer-types.rs:77:16 + | +LL | type Array2 = [mut* u8; 10]; + | ^^^ + | +help: put the `*` before `mut` + | +LL - type Array2 = [mut* u8; 10]; +LL + type Array2 = [*mut u8; 10]; + | + +error: raw pointer types must be written as `*const T` + --> $DIR/c-style-pointer-types.rs:81:15 + | +LL | type Alias1 = const* u8; + | ^^^^^ + | +help: put the `*` before `const` + | +LL - type Alias1 = const* u8; +LL + type Alias1 = *const u8; + | + +error: raw pointer types must be written as `*mut T` + --> $DIR/c-style-pointer-types.rs:85:15 + | +LL | type Alias2 = mut* u8; + | ^^^ + | +help: put the `*` before `mut` + | +LL - type Alias2 = mut* u8; +LL + type Alias2 = *mut u8; + | + +error: raw pointer types must be written as `*const T` + --> $DIR/c-style-pointer-types.rs:89:15 + | +LL | pub const P9: const *u8 = std::ptr::null(); + | ^^^^^ + | +help: put the `*` before `const` + | +LL - pub const P9: const *u8 = std::ptr::null(); +LL + pub const P9: *const u8 = std::ptr::null(); + | + +error: raw pointer types must be written as `*const T` + --> $DIR/c-style-pointer-types.rs:93:16 + | +LL | pub const P10: const * u8 = std::ptr::null(); + | ^^^^^ + | +help: put the `*` before `const` + | +LL - pub const P10: const * u8 = std::ptr::null(); +LL + pub const P10: *const u8 = std::ptr::null(); + | + +error: raw pointer types must be written as `*const T` + --> $DIR/c-style-pointer-types.rs:98:27 + | +LL | fn method(self, size: const* u32) {} + | ^^^^^ + | +help: put the `*` before `const` + | +LL - fn method(self, size: const* u32) {} +LL + fn method(self, size: *const u32) {} + | + +error: raw pointer types must be written as `*const T` + --> $DIR/c-style-pointer-types.rs:104:18 + | +LL | fn method(p: const* u8); + | ^^^^^ + | +help: put the `*` before `const` + | +LL - fn method(p: const* u8); +LL + fn method(p: *const u8); + | + +error: raw pointer types must be written as `*const T` + --> $DIR/c-style-pointer-types.rs:109:25 + | +LL | fn generic_func() -> const* T { std::ptr::null() } + | ^^^^^ + | +help: put the `*` before `const` + | +LL - fn generic_func() -> const* T { std::ptr::null() } +LL + fn generic_func() -> *const T { std::ptr::null() } + | + +error: aborting due to 25 previous errors + From e36689aea7a5153c50d9db4fe4955ba96033a592 Mon Sep 17 00:00:00 2001 From: Shunpoco Date: Sat, 4 Jan 2025 11:18:22 +0000 Subject: [PATCH 02/28] Add FileCheck to branch.rs --- tests/mir-opt/copy-prop/branch.rs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/tests/mir-opt/copy-prop/branch.rs b/tests/mir-opt/copy-prop/branch.rs index fc9b8dc41b166..a26e224fa7088 100644 --- a/tests/mir-opt/copy-prop/branch.rs +++ b/tests/mir-opt/copy-prop/branch.rs @@ -1,4 +1,3 @@ -// skip-filecheck // EMIT_MIR_FOR_EACH_PANIC_STRATEGY //! Tests that we bail out when there are multiple assignments to the same local. //@ test-mir-pass: CopyProp @@ -12,6 +11,14 @@ fn cond() -> bool { // EMIT_MIR branch.foo.CopyProp.diff fn foo() -> i32 { + // CHECK-LABEL: fn foo( + // CHECK: debug x => [[x:_.*]]; + // CHECK: debug y => [[y:_.*]]; + // CHECK: bb3: { + // CHECK: [[y]] = copy [[x]]; + // CHECK: bb5: { + // CHECK: [[y]] = copy [[x]]; + // CHECK: _0 = copy [[y]]; let x = val(); let y = if cond() { From 3b1e20ce5d40640f35adb0f6714f5ced3a4b7288 Mon Sep 17 00:00:00 2001 From: Shunpoco Date: Sat, 4 Jan 2025 11:18:40 +0000 Subject: [PATCH 03/28] Add FileCheck to calls.rs --- tests/mir-opt/copy-prop/calls.rs | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/tests/mir-opt/copy-prop/calls.rs b/tests/mir-opt/copy-prop/calls.rs index 8937c0d2ecb22..f19b114523d3a 100644 --- a/tests/mir-opt/copy-prop/calls.rs +++ b/tests/mir-opt/copy-prop/calls.rs @@ -1,4 +1,3 @@ -// skip-filecheck // Check that CopyProp does propagate return values of call terminators. //@ test-mir-pass: CopyProp //@ needs-unwind @@ -13,6 +12,13 @@ fn dummy(x: u8) -> u8 { // EMIT_MIR calls.nrvo.CopyProp.diff fn nrvo() -> u8 { + // CHECK-LABEL: fn nrvo( + // CHECK: debug y => _0; + // CHECK-NOT: StorageLive(_1); + // CHECK-NOT: _1 = dummy(const 5_u8) + // CHECK: _0 = dummy(const 5_u8) + // CHECK-NOT: _0 = copy _1; + // CHECK-NOT: StorageDead(_1); let y = dummy(5); // this should get NRVO y } @@ -20,6 +26,11 @@ fn nrvo() -> u8 { // EMIT_MIR calls.multiple_edges.CopyProp.diff #[custom_mir(dialect = "runtime", phase = "initial")] fn multiple_edges(t: bool) -> u8 { + // CHECK-LABEL: fn multiple_edges( + // CHECK: bb1: { + // CHECK: _2 = dummy(const 13_u8) + // CHECK: bb2: { + // CHECK: _0 = copy _2; mir! { let x: u8; { From c204231d9685fd43d6e35b9d3865a9e9b6d9b07b Mon Sep 17 00:00:00 2001 From: Shunpoco Date: Sat, 4 Jan 2025 11:18:55 +0000 Subject: [PATCH 04/28] Add FileCheck to copy_propagation_arg.rs --- .../mir-opt/copy-prop/copy_propagation_arg.rs | 24 +++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/tests/mir-opt/copy-prop/copy_propagation_arg.rs b/tests/mir-opt/copy-prop/copy_propagation_arg.rs index e062e1e972887..6c234c3aac0f3 100644 --- a/tests/mir-opt/copy-prop/copy_propagation_arg.rs +++ b/tests/mir-opt/copy-prop/copy_propagation_arg.rs @@ -1,4 +1,3 @@ -// skip-filecheck // EMIT_MIR_FOR_EACH_PANIC_STRATEGY // Check that CopyProp does not propagate an assignment to a function argument // (doing so can break usages of the original argument value) @@ -9,25 +8,46 @@ fn dummy(x: u8) -> u8 { // EMIT_MIR copy_propagation_arg.foo.CopyProp.diff fn foo(mut x: u8) { + // CHECK-LABEL: fn foo( + // CHECK: debug x => [[x:_.*]]; + // CHECK: [[three:_.*]] = copy [[x]]; + // CHECK: [[two:_.*]] = dummy(move [[three]]) + // CHECK: [[x]] = move [[two]]; // calling `dummy` to make a use of `x` that copyprop cannot eliminate x = dummy(x); // this will assign a local to `x` } // EMIT_MIR copy_propagation_arg.bar.CopyProp.diff fn bar(mut x: u8) { + // CHECK-LABEL: fn bar( + // CHECK: debug x => [[x:_.*]]; + // CHECK: [[three:_.*]] = copy [[x]]; + // CHECK: dummy(move [[three]]) + // CHECK: [[x]] = const 5_u8; dummy(x); x = 5; } // EMIT_MIR copy_propagation_arg.baz.CopyProp.diff fn baz(mut x: i32) -> i32 { - // self-assignment to a function argument should be eliminated + // CHECK-LABEL: fn baz( + // CHECK: debug x => [[x:_.*]]; + // CHECK: [[x2:_.*]] = copy [[x]]; + // CHECK: [[x]] = move [[x2]]; + // CHECK: _0 = copy [[x]]; + // In the original case for DestProp, the self-assignment to a function argument is eliminated, + // but in CopyProp it is not eliminated. x = x; x } // EMIT_MIR copy_propagation_arg.arg_src.CopyProp.diff fn arg_src(mut x: i32) -> i32 { + // CHECK-LABEL: fn arg_src( + // CHECK: debug x => [[x:_.*]]; + // CHECK: debug y => [[y:_.*]]; + // CHECK: [[y]] = copy [[x]]; + // CHECK: [[x]] = const 123_i32; let y = x; x = 123; // Don't propagate this assignment to `y` y From 17b0d9036531ea67ed45efe4bb39b6ff0a597c40 Mon Sep 17 00:00:00 2001 From: Shunpoco Date: Sat, 4 Jan 2025 11:19:16 +0000 Subject: [PATCH 05/28] Add FileCheck to custom_move_arg.rs --- tests/mir-opt/copy-prop/custom_move_arg.rs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/tests/mir-opt/copy-prop/custom_move_arg.rs b/tests/mir-opt/copy-prop/custom_move_arg.rs index 3dce7807b3477..54490b07f32fd 100644 --- a/tests/mir-opt/copy-prop/custom_move_arg.rs +++ b/tests/mir-opt/copy-prop/custom_move_arg.rs @@ -1,4 +1,3 @@ -// skip-filecheck // EMIT_MIR_FOR_EACH_PANIC_STRATEGY //@ test-mir-pass: CopyProp @@ -12,6 +11,13 @@ struct NotCopy(bool); // EMIT_MIR custom_move_arg.f.CopyProp.diff #[custom_mir(dialect = "runtime")] fn f(_1: NotCopy) { + // CHECK-LABEL: fn f( + // CHECK: bb0: { + // CHECK-NOT: _2 = copy _1; + // CHECK: _0 = opaque::(copy _1) + // CHECK: bb1: { + // CHECK-NOT: _3 = move _2; + // CHECK: _0 = opaque::(copy _1) mir! { { let _2 = _1; From ff260c850c24a100fe5fa55d93fc6e4aef3b2186 Mon Sep 17 00:00:00 2001 From: Shunpoco Date: Sat, 4 Jan 2025 11:19:29 +0000 Subject: [PATCH 06/28] Add FileCheck to cycle.rs --- tests/mir-opt/copy-prop/cycle.rs | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/tests/mir-opt/copy-prop/cycle.rs b/tests/mir-opt/copy-prop/cycle.rs index 1c0c9eae7fead..9f8312cc8fcd4 100644 --- a/tests/mir-opt/copy-prop/cycle.rs +++ b/tests/mir-opt/copy-prop/cycle.rs @@ -1,4 +1,3 @@ -// skip-filecheck // EMIT_MIR_FOR_EACH_PANIC_STRATEGY //! Tests that cyclic assignments don't hang CopyProp, and result in reasonable code. //@ test-mir-pass: CopyProp @@ -8,6 +7,18 @@ fn val() -> i32 { // EMIT_MIR cycle.main.CopyProp.diff fn main() { + // CHECK-LABEL: fn main( + // CHECK: debug x => [[x:_.*]]; + // CHECK: debug y => [[y:_.*]]; + // CHECK: debug z => [[y]]; + // CHECK-NOT: StorageLive([[y]]); + // CHECK: [[y]] = copy [[x]]; + // CHECK-NOT: StorageLive(_3); + // CHECK-NOT: _3 = copy [[y]]; + // CHECK-NOT: StorageLive(_4); + // CHECK-NOT: _4 = copy _3; + // CHECK-NOT: _1 = move _4; + // CHECK: [[x]] = copy [[y]]; let mut x = val(); let y = x; let z = y; From 109870df2c9cde6e342cd85b8d6a0d08fb3cf1df Mon Sep 17 00:00:00 2001 From: Shunpoco Date: Sat, 4 Jan 2025 11:19:48 +0000 Subject: [PATCH 07/28] Add FileCheck to dead_stores_79191.rs --- tests/mir-opt/copy-prop/dead_stores_79191.rs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/tests/mir-opt/copy-prop/dead_stores_79191.rs b/tests/mir-opt/copy-prop/dead_stores_79191.rs index 24420e19fa80d..016680b9530a2 100644 --- a/tests/mir-opt/copy-prop/dead_stores_79191.rs +++ b/tests/mir-opt/copy-prop/dead_stores_79191.rs @@ -1,4 +1,3 @@ -// skip-filecheck // EMIT_MIR_FOR_EACH_PANIC_STRATEGY //@ test-mir-pass: CopyProp @@ -8,6 +7,14 @@ fn id(x: T) -> T { // EMIT_MIR dead_stores_79191.f.CopyProp.after.mir fn f(mut a: usize) -> usize { + // CHECK-LABEL: fn f( + // CHECK: debug a => [[a:_.*]]; + // CHECK: debug b => [[b:_.*]]; + // CHECK: [[b]] = copy [[a]]; + // CHECK: [[a]] = const 5_usize; + // CHECK: [[a]] = copy [[b]]; + // CHECK: [[c:_.*]] = copy [[a]] + // CHECK: id::(move [[c]]) let b = a; a = 5; a = b; From 76dc555dc900b6b8a5fa82bea57d45ebe4d8e981 Mon Sep 17 00:00:00 2001 From: Shunpoco Date: Sat, 4 Jan 2025 11:20:00 +0000 Subject: [PATCH 08/28] remove dead_stores_better.rs As we discussed, it is identical with dead_stores_79191 --- ...es_better.f.CopyProp.after.panic-abort.mir | 26 ------------------- ...s_better.f.CopyProp.after.panic-unwind.mir | 26 ------------------- tests/mir-opt/copy-prop/dead_stores_better.rs | 23 ---------------- 3 files changed, 75 deletions(-) delete mode 100644 tests/mir-opt/copy-prop/dead_stores_better.f.CopyProp.after.panic-abort.mir delete mode 100644 tests/mir-opt/copy-prop/dead_stores_better.f.CopyProp.after.panic-unwind.mir delete mode 100644 tests/mir-opt/copy-prop/dead_stores_better.rs diff --git a/tests/mir-opt/copy-prop/dead_stores_better.f.CopyProp.after.panic-abort.mir b/tests/mir-opt/copy-prop/dead_stores_better.f.CopyProp.after.panic-abort.mir deleted file mode 100644 index 4781fdfd902a4..0000000000000 --- a/tests/mir-opt/copy-prop/dead_stores_better.f.CopyProp.after.panic-abort.mir +++ /dev/null @@ -1,26 +0,0 @@ -// MIR for `f` after CopyProp - -fn f(_1: usize) -> usize { - debug a => _1; - let mut _0: usize; - let _2: usize; - let mut _3: usize; - let mut _4: usize; - scope 1 { - debug b => _2; - } - - bb0: { - _2 = copy _1; - _1 = const 5_usize; - _1 = copy _2; - StorageLive(_4); - _4 = copy _1; - _0 = id::(move _4) -> [return: bb1, unwind unreachable]; - } - - bb1: { - StorageDead(_4); - return; - } -} diff --git a/tests/mir-opt/copy-prop/dead_stores_better.f.CopyProp.after.panic-unwind.mir b/tests/mir-opt/copy-prop/dead_stores_better.f.CopyProp.after.panic-unwind.mir deleted file mode 100644 index f5fded45c13b4..0000000000000 --- a/tests/mir-opt/copy-prop/dead_stores_better.f.CopyProp.after.panic-unwind.mir +++ /dev/null @@ -1,26 +0,0 @@ -// MIR for `f` after CopyProp - -fn f(_1: usize) -> usize { - debug a => _1; - let mut _0: usize; - let _2: usize; - let mut _3: usize; - let mut _4: usize; - scope 1 { - debug b => _2; - } - - bb0: { - _2 = copy _1; - _1 = const 5_usize; - _1 = copy _2; - StorageLive(_4); - _4 = copy _1; - _0 = id::(move _4) -> [return: bb1, unwind continue]; - } - - bb1: { - StorageDead(_4); - return; - } -} diff --git a/tests/mir-opt/copy-prop/dead_stores_better.rs b/tests/mir-opt/copy-prop/dead_stores_better.rs deleted file mode 100644 index 4b18742940160..0000000000000 --- a/tests/mir-opt/copy-prop/dead_stores_better.rs +++ /dev/null @@ -1,23 +0,0 @@ -// skip-filecheck -// EMIT_MIR_FOR_EACH_PANIC_STRATEGY -// This is a copy of the `dead_stores_79191` test, except that we turn on DSE. This demonstrates -// that that pass enables this one to do more optimizations. - -//@ test-mir-pass: CopyProp -//@ compile-flags: -Zmir-enable-passes=+DeadStoreElimination - -fn id(x: T) -> T { - x -} - -// EMIT_MIR dead_stores_better.f.CopyProp.after.mir -pub fn f(mut a: usize) -> usize { - let b = a; - a = 5; - a = b; - id(a) -} - -fn main() { - f(0); -} From dd2d3908769b3e77abfbd08d577fbba2cb6b3416 Mon Sep 17 00:00:00 2001 From: Shunpoco Date: Sat, 4 Jan 2025 12:40:03 +0000 Subject: [PATCH 09/28] Add FileCheck to issue_107511.rs --- tests/mir-opt/copy-prop/issue_107511.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/mir-opt/copy-prop/issue_107511.rs b/tests/mir-opt/copy-prop/issue_107511.rs index 5e8fc8df42e23..d345d2db2b7d8 100644 --- a/tests/mir-opt/copy-prop/issue_107511.rs +++ b/tests/mir-opt/copy-prop/issue_107511.rs @@ -1,9 +1,12 @@ -// skip-filecheck // EMIT_MIR_FOR_EACH_PANIC_STRATEGY //@ test-mir-pass: CopyProp // EMIT_MIR issue_107511.main.CopyProp.diff fn main() { + // CHECK-LABEL: fn main( + // CHECK: debug i => [[i:_.*]]; + // CHECK-NOT: StorageLive([[i]]); + // CHECK-NOT: StorageDead([[i]]); let mut sum = 0; let a = [0, 10, 20, 30]; From 13e971e532b221d87cc8f0dc4e44a78a3d54e9e0 Mon Sep 17 00:00:00 2001 From: Shunpoco Date: Sat, 4 Jan 2025 12:40:22 +0000 Subject: [PATCH 10/28] Add FileCheck to move_arg.rs --- tests/mir-opt/copy-prop/move_arg.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/mir-opt/copy-prop/move_arg.rs b/tests/mir-opt/copy-prop/move_arg.rs index 498340534324b..b7adae3331968 100644 --- a/tests/mir-opt/copy-prop/move_arg.rs +++ b/tests/mir-opt/copy-prop/move_arg.rs @@ -1,10 +1,13 @@ -// skip-filecheck // EMIT_MIR_FOR_EACH_PANIC_STRATEGY // Test that we do not move multiple times from the same local. //@ test-mir-pass: CopyProp // EMIT_MIR move_arg.f.CopyProp.diff pub fn f(a: T) { + // CHECK-LABEL: fn f( + // CHECK: debug a => [[a:_.*]]; + // CHECK: debug b => [[a]]; + // CHECK: g::(copy [[a]], copy [[a]]) let b = a; g(a, b); } From 3d12668f211bd37ef35b3595bd5dc1bfa6ce650f Mon Sep 17 00:00:00 2001 From: Shunpoco Date: Sat, 4 Jan 2025 12:40:33 +0000 Subject: [PATCH 11/28] Add FileCheck to move_projection.rs --- tests/mir-opt/copy-prop/move_projection.rs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/tests/mir-opt/copy-prop/move_projection.rs b/tests/mir-opt/copy-prop/move_projection.rs index 0ac1c4e0ba261..73473ee749f84 100644 --- a/tests/mir-opt/copy-prop/move_projection.rs +++ b/tests/mir-opt/copy-prop/move_projection.rs @@ -1,4 +1,3 @@ -// skip-filecheck // EMIT_MIR_FOR_EACH_PANIC_STRATEGY //@ test-mir-pass: CopyProp @@ -15,6 +14,15 @@ struct Foo(u8); #[custom_mir(dialect = "runtime")] fn f(a: Foo) -> bool { + // CHECK-LABEL: fn f( + // CHECK-SAME: [[a:_.*]]: Foo) + // CHECK: bb0: { + // CHECK-NOT: _2 = copy [[a]]; + // CHECK-NOT: _3 = move (_2.0: u8); + // CHECK: [[c:_.*]] = copy ([[a]].0: u8); + // CHECK: _0 = opaque::(copy [[a]]) + // CHECK: bb1: { + // CHECK: _0 = opaque::(move [[c]]) mir! { { let b = a; From 6eaf4fedcff2884f7df72a7249423033abfba58b Mon Sep 17 00:00:00 2001 From: Shunpoco Date: Sat, 4 Jan 2025 12:40:41 +0000 Subject: [PATCH 12/28] Add FileCheck to mutate_through_pointer.rs --- tests/mir-opt/copy-prop/mutate_through_pointer.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/mir-opt/copy-prop/mutate_through_pointer.rs b/tests/mir-opt/copy-prop/mutate_through_pointer.rs index 53cca045248d8..7523da8a292e7 100644 --- a/tests/mir-opt/copy-prop/mutate_through_pointer.rs +++ b/tests/mir-opt/copy-prop/mutate_through_pointer.rs @@ -1,4 +1,3 @@ -// skip-filecheck //@ test-mir-pass: CopyProp // // This attempts to mutate `a` via a pointer derived from `addr_of!(a)`. That is UB @@ -18,6 +17,10 @@ use core::intrinsics::mir::*; #[custom_mir(dialect = "analysis", phase = "post-cleanup")] fn f(c: bool) -> bool { + // CHECK-LABEL: fn f( + // CHECK: _2 = copy _1; + // CHECK-NOT: _3 = &raw const _1; + // CHECK: _3 = &raw const _2; mir! { { let a = c; From aa83f1799b486beca16cc21c72af906d6cee5097 Mon Sep 17 00:00:00 2001 From: Shunpoco Date: Sat, 4 Jan 2025 12:40:50 +0000 Subject: [PATCH 13/28] Add FileCheck to non_dominate.rs --- tests/mir-opt/copy-prop/non_dominate.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/tests/mir-opt/copy-prop/non_dominate.rs b/tests/mir-opt/copy-prop/non_dominate.rs index c01275370ea71..a6db10c461f5e 100644 --- a/tests/mir-opt/copy-prop/non_dominate.rs +++ b/tests/mir-opt/copy-prop/non_dominate.rs @@ -1,4 +1,3 @@ -// skip-filecheck //@ test-mir-pass: CopyProp #![feature(custom_mir, core_intrinsics)] @@ -8,6 +7,11 @@ use core::intrinsics::mir::*; #[custom_mir(dialect = "analysis", phase = "post-cleanup")] fn f(c: bool) -> bool { + // CHECK-LABEL: fn f( + // CHECK: bb2: { + // CHECK: _2 = copy _3; + // CHECK: bb3: { + // CHECK: _0 = copy _2; mir! { let a: bool; let b: bool; From 8ae4d57afb72594fc2ede4aad8a97c65c02f3b23 Mon Sep 17 00:00:00 2001 From: Shunpoco Date: Sat, 4 Jan 2025 12:40:56 +0000 Subject: [PATCH 14/28] Add FileCheck to partial_init.rs --- tests/mir-opt/copy-prop/partial_init.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/mir-opt/copy-prop/partial_init.rs b/tests/mir-opt/copy-prop/partial_init.rs index 88e94988181d2..0135d7c321b85 100644 --- a/tests/mir-opt/copy-prop/partial_init.rs +++ b/tests/mir-opt/copy-prop/partial_init.rs @@ -1,4 +1,3 @@ -// skip-filecheck //@ test-mir-pass: CopyProp // Verify that we do not ICE on partial initializations. @@ -9,6 +8,9 @@ use core::intrinsics::mir::*; // EMIT_MIR partial_init.main.CopyProp.diff #[custom_mir(dialect = "runtime", phase = "post-cleanup")] pub fn main() { + // CHECK-LABEL: fn main( + // CHECK: let mut [[x:_.*]]: (isize,); + // CHECK: ([[x]].0: isize) = const 1_isize; mir! ( let x: (isize, ); { From 620b9b15cd9c8cba3542761b550e20b7298359b8 Mon Sep 17 00:00:00 2001 From: Shunpoco Date: Sat, 4 Jan 2025 12:41:01 +0000 Subject: [PATCH 15/28] Add FileCheck to reborrow.rs --- tests/mir-opt/copy-prop/reborrow.rs | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/tests/mir-opt/copy-prop/reborrow.rs b/tests/mir-opt/copy-prop/reborrow.rs index 51a1f92cde2fc..8bc81106e9945 100644 --- a/tests/mir-opt/copy-prop/reborrow.rs +++ b/tests/mir-opt/copy-prop/reborrow.rs @@ -1,4 +1,3 @@ -// skip-filecheck // EMIT_MIR_FOR_EACH_PANIC_STRATEGY // Check that CopyProp considers reborrows as not mutating the pointer. //@ test-mir-pass: CopyProp @@ -8,6 +7,9 @@ fn opaque(_: impl Sized) {} // EMIT_MIR reborrow.remut.CopyProp.diff fn remut(mut x: u8) { + // CHECK-LABEL: fn remut( + // CHECK: debug a => [[a:_.*]]; + // CHECK: debug c => [[a]]; let a = &mut x; let b = &mut *a; //< this cannot mutate a. let c = a; //< so `c` and `a` can be merged. @@ -16,6 +18,9 @@ fn remut(mut x: u8) { // EMIT_MIR reborrow.reraw.CopyProp.diff fn reraw(mut x: u8) { + // CHECK-LABEL: fn reraw( + // CHECK: debug a => [[a:_.*]]; + // CHECK: debug c => [[a]]; let a = &mut x; let b = &raw mut *a; //< this cannot mutate a. let c = a; //< so `c` and `a` can be merged. @@ -24,6 +29,9 @@ fn reraw(mut x: u8) { // EMIT_MIR reborrow.miraw.CopyProp.diff fn miraw(mut x: u8) { + // CHECK-LABEL: fn miraw( + // CHECK: debug a => [[a:_.*]]; + // CHECK: debug c => [[a]]; let a = &raw mut x; let b = unsafe { &raw mut *a }; //< this cannot mutate a. let c = a; //< so `c` and `a` can be merged. @@ -32,6 +40,9 @@ fn miraw(mut x: u8) { // EMIT_MIR reborrow.demiraw.CopyProp.diff fn demiraw(mut x: u8) { + // CHECK-LABEL: fn demiraw( + // CHECK: debug a => [[a:_.*]]; + // CHECK: debug c => [[a]]; let a = &raw mut x; let b = unsafe { &mut *a }; //< this cannot mutate a. let c = a; //< so `c` and `a` can be merged. From c4d6b0b8ea2a0c3e5520639722fa5a9b66a7aecb Mon Sep 17 00:00:00 2001 From: sayantn Date: Thu, 9 Oct 2025 17:02:26 +0530 Subject: [PATCH 16/28] Implement `simd_fma` and `simd_relaxed_fma` in const-eval --- .../src/interpret/intrinsics.rs | 70 ++++++++++++------- .../src/interpret/intrinsics/simd.rs | 42 ++++++++++- .../rustc_const_eval/src/interpret/machine.rs | 4 +- src/tools/miri/src/intrinsics/simd.rs | 58 --------------- src/tools/miri/src/machine.rs | 4 +- 5 files changed, 88 insertions(+), 90 deletions(-) diff --git a/compiler/rustc_const_eval/src/interpret/intrinsics.rs b/compiler/rustc_const_eval/src/interpret/intrinsics.rs index f0f06d469c1e6..f0712644465a1 100644 --- a/compiler/rustc_const_eval/src/interpret/intrinsics.rs +++ b/compiler/rustc_const_eval/src/interpret/intrinsics.rs @@ -25,6 +25,15 @@ use super::{ }; use crate::fluent_generated as fluent; +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +enum MulAddType { + /// Used with `fma` and `simd_fma`, always uses fused-multiply-add + Fused, + /// Used with `fmuladd` and `simd_relaxed_fma`, nondeterministically determines whether to use + /// fma or simple multiply-add + Nondeterministic, +} + /// Directly returns an `Allocation` containing an absolute path representation of the given type. pub(crate) fn alloc_type_name<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> (AllocId, u64) { let path = crate::util::type_name(tcx, ty); @@ -630,14 +639,22 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { dest, rustc_apfloat::Round::NearestTiesToEven, )?, - sym::fmaf16 => self.fma_intrinsic::(args, dest)?, - sym::fmaf32 => self.fma_intrinsic::(args, dest)?, - sym::fmaf64 => self.fma_intrinsic::(args, dest)?, - sym::fmaf128 => self.fma_intrinsic::(args, dest)?, - sym::fmuladdf16 => self.float_muladd_intrinsic::(args, dest)?, - sym::fmuladdf32 => self.float_muladd_intrinsic::(args, dest)?, - sym::fmuladdf64 => self.float_muladd_intrinsic::(args, dest)?, - sym::fmuladdf128 => self.float_muladd_intrinsic::(args, dest)?, + sym::fmaf16 => self.float_muladd_intrinsic::(args, dest, MulAddType::Fused)?, + sym::fmaf32 => self.float_muladd_intrinsic::(args, dest, MulAddType::Fused)?, + sym::fmaf64 => self.float_muladd_intrinsic::(args, dest, MulAddType::Fused)?, + sym::fmaf128 => self.float_muladd_intrinsic::(args, dest, MulAddType::Fused)?, + sym::fmuladdf16 => { + self.float_muladd_intrinsic::(args, dest, MulAddType::Nondeterministic)? + } + sym::fmuladdf32 => { + self.float_muladd_intrinsic::(args, dest, MulAddType::Nondeterministic)? + } + sym::fmuladdf64 => { + self.float_muladd_intrinsic::(args, dest, MulAddType::Nondeterministic)? + } + sym::fmuladdf128 => { + self.float_muladd_intrinsic::(args, dest, MulAddType::Nondeterministic)? + } // Unsupported intrinsic: skip the return_to_block below. _ => return interp_ok(false), @@ -1038,40 +1055,41 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { interp_ok(()) } - fn fma_intrinsic( - &mut self, - args: &[OpTy<'tcx, M::Provenance>], - dest: &PlaceTy<'tcx, M::Provenance>, - ) -> InterpResult<'tcx, ()> + fn float_muladd( + &self, + a: Scalar, + b: Scalar, + c: Scalar, + typ: MulAddType, + ) -> InterpResult<'tcx, Scalar> where F: rustc_apfloat::Float + rustc_apfloat::FloatConvert + Into>, { - let a: F = self.read_scalar(&args[0])?.to_float()?; - let b: F = self.read_scalar(&args[1])?.to_float()?; - let c: F = self.read_scalar(&args[2])?.to_float()?; + let a: F = a.to_float()?; + let b: F = b.to_float()?; + let c: F = c.to_float()?; + + let fuse = typ == MulAddType::Fused || M::float_fuse_mul_add(self); - let res = a.mul_add(b, c).value; + let res = if fuse { a.mul_add(b, c).value } else { ((a * b).value + c).value }; let res = self.adjust_nan(res, &[a, b, c]); - self.write_scalar(res, dest)?; - interp_ok(()) + interp_ok(res.into()) } fn float_muladd_intrinsic( &mut self, args: &[OpTy<'tcx, M::Provenance>], dest: &PlaceTy<'tcx, M::Provenance>, + typ: MulAddType, ) -> InterpResult<'tcx, ()> where F: rustc_apfloat::Float + rustc_apfloat::FloatConvert + Into>, { - let a: F = self.read_scalar(&args[0])?.to_float()?; - let b: F = self.read_scalar(&args[1])?.to_float()?; - let c: F = self.read_scalar(&args[2])?.to_float()?; - - let fuse = M::float_fuse_mul_add(self); + let a = self.read_scalar(&args[0])?; + let b = self.read_scalar(&args[1])?; + let c = self.read_scalar(&args[2])?; - let res = if fuse { a.mul_add(b, c).value } else { ((a * b).value + c).value }; - let res = self.adjust_nan(res, &[a, b, c]); + let res = self.float_muladd::(a, b, c, typ)?; self.write_scalar(res, dest)?; interp_ok(()) } diff --git a/compiler/rustc_const_eval/src/interpret/intrinsics/simd.rs b/compiler/rustc_const_eval/src/interpret/intrinsics/simd.rs index 0dba66ae93721..84489028e190d 100644 --- a/compiler/rustc_const_eval/src/interpret/intrinsics/simd.rs +++ b/compiler/rustc_const_eval/src/interpret/intrinsics/simd.rs @@ -1,5 +1,6 @@ use either::Either; use rustc_abi::Endian; +use rustc_apfloat::ieee::{Double, Single}; use rustc_apfloat::{Float, Round}; use rustc_middle::mir::interpret::{InterpErrorKind, UndefinedBehaviorInfo}; use rustc_middle::ty::FloatTy; @@ -8,8 +9,8 @@ use rustc_span::{Symbol, sym}; use tracing::trace; use super::{ - ImmTy, InterpCx, InterpResult, Machine, OpTy, PlaceTy, Provenance, Scalar, Size, interp_ok, - throw_ub_format, + ImmTy, InterpCx, InterpResult, Machine, MulAddType, OpTy, PlaceTy, Provenance, Scalar, Size, + interp_ok, throw_ub_format, }; use crate::interpret::Writeable; @@ -701,6 +702,43 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { }; } } + sym::simd_fma | sym::simd_relaxed_fma => { + // `simd_fma` should always deterministically use `mul_add`, whereas `relaxed_fma` + // is non-deterministic, and can use either `mul_add` or `a * b + c` + let typ = match intrinsic_name { + sym::simd_fma => MulAddType::Fused, + sym::simd_relaxed_fma => MulAddType::Nondeterministic, + _ => unreachable!(), + }; + + let (a, a_len) = self.project_to_simd(&args[0])?; + let (b, b_len) = self.project_to_simd(&args[1])?; + let (c, c_len) = self.project_to_simd(&args[2])?; + let (dest, dest_len) = self.project_to_simd(&dest)?; + + assert_eq!(dest_len, a_len); + assert_eq!(dest_len, b_len); + assert_eq!(dest_len, c_len); + + for i in 0..dest_len { + let a = self.read_scalar(&self.project_index(&a, i)?)?; + let b = self.read_scalar(&self.project_index(&b, i)?)?; + let c = self.read_scalar(&self.project_index(&c, i)?)?; + let dest = self.project_index(&dest, i)?; + + let ty::Float(float_ty) = dest.layout.ty.kind() else { + span_bug!(self.cur_span(), "{} operand is not a float", intrinsic_name) + }; + + let val = match float_ty { + FloatTy::F16 => unimplemented!("f16_f128"), + FloatTy::F32 => self.float_muladd::(a, b, c, typ)?, + FloatTy::F64 => self.float_muladd::(a, b, c, typ)?, + FloatTy::F128 => unimplemented!("f16_f128"), + }; + self.write_scalar(val, &dest)?; + } + } // Unsupported intrinsic: skip the return_to_block below. _ => return interp_ok(false), diff --git a/compiler/rustc_const_eval/src/interpret/machine.rs b/compiler/rustc_const_eval/src/interpret/machine.rs index 1725635e0b479..236c35ec7b96a 100644 --- a/compiler/rustc_const_eval/src/interpret/machine.rs +++ b/compiler/rustc_const_eval/src/interpret/machine.rs @@ -290,7 +290,7 @@ pub trait Machine<'tcx>: Sized { } /// Determines whether the `fmuladd` intrinsics fuse the multiply-add or use separate operations. - fn float_fuse_mul_add(_ecx: &mut InterpCx<'tcx, Self>) -> bool; + fn float_fuse_mul_add(_ecx: &InterpCx<'tcx, Self>) -> bool; /// Called before a basic block terminator is executed. #[inline] @@ -676,7 +676,7 @@ pub macro compile_time_machine(<$tcx: lifetime>) { } #[inline(always)] - fn float_fuse_mul_add(_ecx: &mut InterpCx<$tcx, Self>) -> bool { + fn float_fuse_mul_add(_ecx: &InterpCx<$tcx, Self>) -> bool { true } diff --git a/src/tools/miri/src/intrinsics/simd.rs b/src/tools/miri/src/intrinsics/simd.rs index 1e7366b5a8269..2b176093cb364 100644 --- a/src/tools/miri/src/intrinsics/simd.rs +++ b/src/tools/miri/src/intrinsics/simd.rs @@ -1,5 +1,3 @@ -use rand::Rng; -use rustc_apfloat::Float; use rustc_middle::ty; use rustc_middle::ty::FloatTy; @@ -83,62 +81,6 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write_scalar(val, &dest)?; } } - "fma" | "relaxed_fma" => { - let [a, b, c] = check_intrinsic_arg_count(args)?; - let (a, a_len) = this.project_to_simd(a)?; - let (b, b_len) = this.project_to_simd(b)?; - let (c, c_len) = this.project_to_simd(c)?; - let (dest, dest_len) = this.project_to_simd(dest)?; - - assert_eq!(dest_len, a_len); - assert_eq!(dest_len, b_len); - assert_eq!(dest_len, c_len); - - for i in 0..dest_len { - let a = this.read_scalar(&this.project_index(&a, i)?)?; - let b = this.read_scalar(&this.project_index(&b, i)?)?; - let c = this.read_scalar(&this.project_index(&c, i)?)?; - let dest = this.project_index(&dest, i)?; - - let fuse: bool = intrinsic_name == "fma" - || (this.machine.float_nondet && this.machine.rng.get_mut().random()); - - // Works for f32 and f64. - // FIXME: using host floats to work around https://github.com/rust-lang/miri/issues/2468. - let ty::Float(float_ty) = dest.layout.ty.kind() else { - span_bug!(this.cur_span(), "{} operand is not a float", intrinsic_name) - }; - let val = match float_ty { - FloatTy::F16 => unimplemented!("f16_f128"), - FloatTy::F32 => { - let a = a.to_f32()?; - let b = b.to_f32()?; - let c = c.to_f32()?; - let res = if fuse { - a.mul_add(b, c).value - } else { - ((a * b).value + c).value - }; - let res = this.adjust_nan(res, &[a, b, c]); - Scalar::from(res) - } - FloatTy::F64 => { - let a = a.to_f64()?; - let b = b.to_f64()?; - let c = c.to_f64()?; - let res = if fuse { - a.mul_add(b, c).value - } else { - ((a * b).value + c).value - }; - let res = this.adjust_nan(res, &[a, b, c]); - Scalar::from(res) - } - FloatTy::F128 => unimplemented!("f16_f128"), - }; - this.write_scalar(val, &dest)?; - } - } "expose_provenance" => { let [op] = check_intrinsic_arg_count(args)?; let (op, op_len) = this.project_to_simd(op)?; diff --git a/src/tools/miri/src/machine.rs b/src/tools/miri/src/machine.rs index fadbdf5cea999..da90f6b84664d 100644 --- a/src/tools/miri/src/machine.rs +++ b/src/tools/miri/src/machine.rs @@ -1324,8 +1324,8 @@ impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> { } #[inline(always)] - fn float_fuse_mul_add(ecx: &mut InterpCx<'tcx, Self>) -> bool { - ecx.machine.float_nondet && ecx.machine.rng.get_mut().random() + fn float_fuse_mul_add(ecx: &InterpCx<'tcx, Self>) -> bool { + ecx.machine.float_nondet && ecx.machine.rng.borrow_mut().random() } #[inline(always)] From 8611f0b67c7aea4c43d5a3f39b0e84a6d0950868 Mon Sep 17 00:00:00 2001 From: Michael Howell Date: Wed, 29 Oct 2025 11:30:37 -0700 Subject: [PATCH 17/28] rustdoc: fix `--emit=dep-info` on scraped examples --- src/librustdoc/lib.rs | 2 +- src/librustdoc/scrape_examples.rs | 5 +++++ .../examples/ex.rs | 6 ++++++ .../rustdoc-scrape-examples-dep-info/rmake.rs | 19 +++++++++++++++++++ .../src/lib.rs | 3 +++ .../rmake.rs | 2 +- .../rustdoc-scrape-examples-multiple/rmake.rs | 2 +- .../rustdoc-scrape-examples-ordering/rmake.rs | 2 +- .../rustdoc-scrape-examples-remap/rmake.rs | 2 +- .../rustdoc-scrape-examples-remap/scrape.rs | 5 +++-- .../rustdoc-scrape-examples-test/rmake.rs | 2 +- .../rmake.rs | 2 +- 12 files changed, 43 insertions(+), 9 deletions(-) create mode 100644 tests/run-make/rustdoc-scrape-examples-dep-info/examples/ex.rs create mode 100644 tests/run-make/rustdoc-scrape-examples-dep-info/rmake.rs create mode 100644 tests/run-make/rustdoc-scrape-examples-dep-info/src/lib.rs diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs index dd6378b25def7..c6a0682a80c48 100644 --- a/src/librustdoc/lib.rs +++ b/src/librustdoc/lib.rs @@ -896,7 +896,7 @@ fn main_args(early_dcx: &mut EarlyDiagCtxt, at_args: &[String]) { // Register the loaded external files in the source map so they show up in depinfo. // We can't load them via the source map because it gets created after we process the options. for external_path in &loaded_paths { - let _ = sess.source_map().load_file(external_path); + let _ = sess.source_map().load_binary_file(external_path); } if sess.opts.describe_lints { diff --git a/src/librustdoc/scrape_examples.rs b/src/librustdoc/scrape_examples.rs index 471e966e2c24b..1b3c96d82b410 100644 --- a/src/librustdoc/scrape_examples.rs +++ b/src/librustdoc/scrape_examples.rs @@ -273,6 +273,7 @@ pub(crate) fn run( bin_crate: bool, ) { let inner = move || -> Result<(), String> { + let emit_dep_info = renderopts.dep_info().is_some(); // Generates source files for examples renderopts.no_emit_shared = true; let (cx, _) = Context::init(krate, renderopts, cache, tcx, Default::default()) @@ -320,6 +321,10 @@ pub(crate) fn run( calls.encode(&mut encoder); encoder.finish().map_err(|(_path, e)| e.to_string())?; + if emit_dep_info { + rustc_interface::passes::write_dep_info(tcx); + } + Ok(()) }; diff --git a/tests/run-make/rustdoc-scrape-examples-dep-info/examples/ex.rs b/tests/run-make/rustdoc-scrape-examples-dep-info/examples/ex.rs new file mode 100644 index 0000000000000..c37b8dd48853a --- /dev/null +++ b/tests/run-make/rustdoc-scrape-examples-dep-info/examples/ex.rs @@ -0,0 +1,6 @@ +fn main() {} + +#[test] +fn a_test() { + foobar::ok(); +} diff --git a/tests/run-make/rustdoc-scrape-examples-dep-info/rmake.rs b/tests/run-make/rustdoc-scrape-examples-dep-info/rmake.rs new file mode 100644 index 0000000000000..7dfa6584785ae --- /dev/null +++ b/tests/run-make/rustdoc-scrape-examples-dep-info/rmake.rs @@ -0,0 +1,19 @@ +//@ needs-target-std +use run_make_support::{assert_contains, rfs}; + +#[path = "../rustdoc-scrape-examples-remap/scrape.rs"] +mod scrape; + +fn main() { + scrape::scrape( + &["--scrape-tests", "--emit=dep-info"], + &["--emit=dep-info,invocation-specific"], + ); + + let content = rfs::read_to_string("foobar.d"); + assert_contains(&content, "lib.rs:"); + assert_contains(&content, "rustdoc/ex.calls:"); + + let content = rfs::read_to_string("ex.d"); + assert_contains(&content, "examples/ex.rs:"); +} diff --git a/tests/run-make/rustdoc-scrape-examples-dep-info/src/lib.rs b/tests/run-make/rustdoc-scrape-examples-dep-info/src/lib.rs new file mode 100644 index 0000000000000..93d56b7e972f8 --- /dev/null +++ b/tests/run-make/rustdoc-scrape-examples-dep-info/src/lib.rs @@ -0,0 +1,3 @@ +//@ has foobar/fn.ok.html '//*[@class="docblock scraped-example-list"]' '' + +pub fn ok() {} diff --git a/tests/run-make/rustdoc-scrape-examples-invalid-expr/rmake.rs b/tests/run-make/rustdoc-scrape-examples-invalid-expr/rmake.rs index 8996ff184c900..38943b663c420 100644 --- a/tests/run-make/rustdoc-scrape-examples-invalid-expr/rmake.rs +++ b/tests/run-make/rustdoc-scrape-examples-invalid-expr/rmake.rs @@ -3,5 +3,5 @@ mod scrape; fn main() { - scrape::scrape(&[]); + scrape::scrape(&[], &[]); } diff --git a/tests/run-make/rustdoc-scrape-examples-multiple/rmake.rs b/tests/run-make/rustdoc-scrape-examples-multiple/rmake.rs index 8996ff184c900..38943b663c420 100644 --- a/tests/run-make/rustdoc-scrape-examples-multiple/rmake.rs +++ b/tests/run-make/rustdoc-scrape-examples-multiple/rmake.rs @@ -3,5 +3,5 @@ mod scrape; fn main() { - scrape::scrape(&[]); + scrape::scrape(&[], &[]); } diff --git a/tests/run-make/rustdoc-scrape-examples-ordering/rmake.rs b/tests/run-make/rustdoc-scrape-examples-ordering/rmake.rs index 8996ff184c900..38943b663c420 100644 --- a/tests/run-make/rustdoc-scrape-examples-ordering/rmake.rs +++ b/tests/run-make/rustdoc-scrape-examples-ordering/rmake.rs @@ -3,5 +3,5 @@ mod scrape; fn main() { - scrape::scrape(&[]); + scrape::scrape(&[], &[]); } diff --git a/tests/run-make/rustdoc-scrape-examples-remap/rmake.rs b/tests/run-make/rustdoc-scrape-examples-remap/rmake.rs index ead3920c76132..920a65d56f8e5 100644 --- a/tests/run-make/rustdoc-scrape-examples-remap/rmake.rs +++ b/tests/run-make/rustdoc-scrape-examples-remap/rmake.rs @@ -2,5 +2,5 @@ mod scrape; fn main() { - scrape::scrape(&[]); + scrape::scrape(&[], &[]); } diff --git a/tests/run-make/rustdoc-scrape-examples-remap/scrape.rs b/tests/run-make/rustdoc-scrape-examples-remap/scrape.rs index c4d7814c3c831..668cb3797fb9e 100644 --- a/tests/run-make/rustdoc-scrape-examples-remap/scrape.rs +++ b/tests/run-make/rustdoc-scrape-examples-remap/scrape.rs @@ -2,7 +2,7 @@ use std::path::Path; use run_make_support::{htmldocck, rfs, rustc, rustdoc}; -pub fn scrape(extra_args: &[&str]) { +pub fn scrape(extra_args_scrape: &[&str], extra_args_doc: &[&str]) { let out_dir = Path::new("rustdoc"); let crate_name = "foobar"; let deps = rfs::read_dir("examples") @@ -27,7 +27,7 @@ pub fn scrape(extra_args: &[&str]) { .arg(&out_example) .arg("--scrape-examples-target-crate") .arg(crate_name) - .args(extra_args) + .args(extra_args_scrape) .run(); out_deps.push(out_example); } @@ -42,6 +42,7 @@ pub fn scrape(extra_args: &[&str]) { for dep in out_deps { rustdoc.arg("--with-examples").arg(dep); } + rustdoc.args(extra_args_doc); rustdoc.run(); htmldocck().arg(out_dir).arg("src/lib.rs").run(); diff --git a/tests/run-make/rustdoc-scrape-examples-test/rmake.rs b/tests/run-make/rustdoc-scrape-examples-test/rmake.rs index 0868507c4ae5b..c0c4df91d517d 100644 --- a/tests/run-make/rustdoc-scrape-examples-test/rmake.rs +++ b/tests/run-make/rustdoc-scrape-examples-test/rmake.rs @@ -3,5 +3,5 @@ mod scrape; fn main() { - scrape::scrape(&["--scrape-tests"]); + scrape::scrape(&["--scrape-tests"], &[]); } diff --git a/tests/run-make/rustdoc-scrape-examples-whitespace/rmake.rs b/tests/run-make/rustdoc-scrape-examples-whitespace/rmake.rs index 8996ff184c900..38943b663c420 100644 --- a/tests/run-make/rustdoc-scrape-examples-whitespace/rmake.rs +++ b/tests/run-make/rustdoc-scrape-examples-whitespace/rmake.rs @@ -3,5 +3,5 @@ mod scrape; fn main() { - scrape::scrape(&[]); + scrape::scrape(&[], &[]); } From 2a5d830bd645a72e4e41476bb039cce2896b5bb2 Mon Sep 17 00:00:00 2001 From: Daria Sukhonina Date: Thu, 30 Oct 2025 19:19:25 +0300 Subject: [PATCH 18/28] Do not double check for already decoded expn_id to avoid races --- compiler/rustc_metadata/src/rmeta/decoder.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/compiler/rustc_metadata/src/rmeta/decoder.rs b/compiler/rustc_metadata/src/rmeta/decoder.rs index b895feb906247..808d9fbbc2cef 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder.rs @@ -1560,7 +1560,6 @@ impl<'a> CrateMetadataRef<'a> { } fn expn_hash_to_expn_id(self, sess: &Session, index_guess: u32, hash: ExpnHash) -> ExpnId { - debug_assert_eq!(ExpnId::from_hash(hash), None); let index_guess = ExpnIndex::from_u32(index_guess); let old_hash = self.root.expn_hashes.get(self, index_guess).map(|lazy| lazy.decode(self)); From 65aed3b6e63bc7ad9ed61785197f882bfc071089 Mon Sep 17 00:00:00 2001 From: Michael Howell Date: Fri, 31 Oct 2025 11:16:00 -0700 Subject: [PATCH 19/28] Fix test on Windows --- tests/run-make/rustdoc-scrape-examples-dep-info/rmake.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/run-make/rustdoc-scrape-examples-dep-info/rmake.rs b/tests/run-make/rustdoc-scrape-examples-dep-info/rmake.rs index 7dfa6584785ae..00a87477ab5e4 100644 --- a/tests/run-make/rustdoc-scrape-examples-dep-info/rmake.rs +++ b/tests/run-make/rustdoc-scrape-examples-dep-info/rmake.rs @@ -10,10 +10,10 @@ fn main() { &["--emit=dep-info,invocation-specific"], ); - let content = rfs::read_to_string("foobar.d"); + let content = rfs::read_to_string("foobar.d").replace(r"\", "/"); assert_contains(&content, "lib.rs:"); assert_contains(&content, "rustdoc/ex.calls:"); - let content = rfs::read_to_string("ex.d"); + let content = rfs::read_to_string("ex.d").replace(r"\", "/"); assert_contains(&content, "examples/ex.rs:"); } From 2044a5f51b12960ee2df64f62275bcf4598bd258 Mon Sep 17 00:00:00 2001 From: Zalathar Date: Fri, 24 Oct 2025 17:03:26 +1100 Subject: [PATCH 20/28] Separate debugger discovery from debugger version-query --- src/tools/compiletest/src/debuggers.rs | 32 +++++++++++++------------- src/tools/compiletest/src/lib.rs | 7 +++--- 2 files changed, 20 insertions(+), 19 deletions(-) diff --git a/src/tools/compiletest/src/debuggers.rs b/src/tools/compiletest/src/debuggers.rs index 8afe3289fa45a..93f27fb90951c 100644 --- a/src/tools/compiletest/src/debuggers.rs +++ b/src/tools/compiletest/src/debuggers.rs @@ -103,22 +103,19 @@ fn find_cdb(target: &str) -> Option { } /// Returns Path to CDB -pub(crate) fn analyze_cdb( - cdb: Option, - target: &str, -) -> (Option, Option<[u16; 4]>) { +pub(crate) fn discover_cdb(cdb: Option, target: &str) -> Option { let cdb = cdb.map(Utf8PathBuf::from).or_else(|| find_cdb(target)); + cdb +} +pub(crate) fn query_cdb_version(cdb: &Utf8Path) -> Option<[u16; 4]> { let mut version = None; - if let Some(cdb) = cdb.as_ref() { - if let Ok(output) = Command::new(cdb).arg("/version").output() { - if let Some(first_line) = String::from_utf8_lossy(&output.stdout).lines().next() { - version = extract_cdb_version(&first_line); - } + if let Ok(output) = Command::new(cdb).arg("/version").output() { + if let Some(first_line) = String::from_utf8_lossy(&output.stdout).lines().next() { + version = extract_cdb_version(&first_line); } } - - (cdb, version) + version } pub(crate) fn extract_cdb_version(full_version_line: &str) -> Option<[u16; 4]> { @@ -132,12 +129,11 @@ pub(crate) fn extract_cdb_version(full_version_line: &str) -> Option<[u16; 4]> { Some([major, minor, patch, build]) } -/// Returns (Path to GDB, GDB Version) -pub(crate) fn analyze_gdb( +pub(crate) fn discover_gdb( gdb: Option, target: &str, android_cross_path: &Utf8Path, -) -> (Option, Option) { +) -> Option { #[cfg(not(windows))] const GDB_FALLBACK: &str = "gdb"; #[cfg(windows)] @@ -159,6 +155,10 @@ pub(crate) fn analyze_gdb( Some(ref s) => s.to_owned(), }; + Some(gdb) +} + +pub(crate) fn query_gdb_version(gdb: &str) -> Option { let mut version_line = None; if let Ok(output) = Command::new(&gdb).arg("--version").output() { if let Some(first_line) = String::from_utf8_lossy(&output.stdout).lines().next() { @@ -168,10 +168,10 @@ pub(crate) fn analyze_gdb( let version = match version_line { Some(line) => extract_gdb_version(&line), - None => return (None, None), + None => return None, }; - (Some(gdb), version) + version } pub(crate) fn extract_gdb_version(full_version_line: &str) -> Option { diff --git a/src/tools/compiletest/src/lib.rs b/src/tools/compiletest/src/lib.rs index b524017e4dadd..89f5623c1fc99 100644 --- a/src/tools/compiletest/src/lib.rs +++ b/src/tools/compiletest/src/lib.rs @@ -258,10 +258,11 @@ fn parse_config(args: Vec) -> Config { let target = opt_str2(matches.opt_str("target")); let android_cross_path = opt_path(matches, "android-cross-path"); // FIXME: `cdb_version` is *derived* from cdb, but it's *not* technically a config! - let (cdb, cdb_version) = debuggers::analyze_cdb(matches.opt_str("cdb"), &target); + let cdb = debuggers::discover_cdb(matches.opt_str("cdb"), &target); + let cdb_version = cdb.as_deref().and_then(debuggers::query_cdb_version); // FIXME: `gdb_version` is *derived* from gdb, but it's *not* technically a config! - let (gdb, gdb_version) = - debuggers::analyze_gdb(matches.opt_str("gdb"), &target, &android_cross_path); + let gdb = debuggers::discover_gdb(matches.opt_str("gdb"), &target, &android_cross_path); + let gdb_version = gdb.as_deref().and_then(debuggers::query_gdb_version); // FIXME: `lldb_version` is *derived* from lldb, but it's *not* technically a config! let lldb_version = matches.opt_str("lldb-version").as_deref().and_then(debuggers::extract_lldb_version); From e9f360330d86f8ffd9cee7c71a83b12027d436d7 Mon Sep 17 00:00:00 2001 From: Zalathar Date: Sat, 25 Oct 2025 20:42:50 +1100 Subject: [PATCH 21/28] Only pass debugger-related flags for `debuginfo` tests --- src/bootstrap/src/core/build_steps/test.rs | 64 ++++++++++++---------- 1 file changed, 35 insertions(+), 29 deletions(-) diff --git a/src/bootstrap/src/core/build_steps/test.rs b/src/bootstrap/src/core/build_steps/test.rs index e1b3ff92a07b8..9a6b47bdf3a1a 100644 --- a/src/bootstrap/src/core/build_steps/test.rs +++ b/src/bootstrap/src/core/build_steps/test.rs @@ -2078,27 +2078,43 @@ Please disable assertions with `rust.debug-assertions = false`. cmd.arg("--python").arg(builder.python()); - if let Some(ref gdb) = builder.config.gdb { - cmd.arg("--gdb").arg(gdb); - } - - let lldb_exe = builder.config.lldb.clone().unwrap_or_else(|| PathBuf::from("lldb")); - let lldb_version = command(&lldb_exe) - .allow_failure() - .arg("--version") - .run_capture(builder) - .stdout_if_ok() - .and_then(|v| if v.trim().is_empty() { None } else { Some(v) }); - if let Some(ref vers) = lldb_version { - cmd.arg("--lldb-version").arg(vers); - let lldb_python_dir = command(&lldb_exe) + // FIXME(#148099): Currently we set these Android-related flags in all + // modes, even though they should only be needed in "debuginfo" mode, + // because the GDB-discovery code in compiletest currently assumes that + // `--android-cross-path` is always set for Android targets. + cmd.arg("--adb-path").arg("adb"); + cmd.arg("--adb-test-dir").arg(ADB_TEST_DIR); + if target.contains("android") && !builder.config.dry_run() { + // Assume that cc for this target comes from the android sysroot + cmd.arg("--android-cross-path") + .arg(builder.cc(target).parent().unwrap().parent().unwrap()); + } else { + cmd.arg("--android-cross-path").arg(""); + } + + if mode == "debuginfo" { + if let Some(ref gdb) = builder.config.gdb { + cmd.arg("--gdb").arg(gdb); + } + + let lldb_exe = builder.config.lldb.clone().unwrap_or_else(|| PathBuf::from("lldb")); + let lldb_version = command(&lldb_exe) .allow_failure() - .arg("-P") - .run_capture_stdout(builder) + .arg("--version") + .run_capture(builder) .stdout_if_ok() - .map(|p| p.lines().next().expect("lldb Python dir not found").to_string()); - if let Some(ref dir) = lldb_python_dir { - cmd.arg("--lldb-python-dir").arg(dir); + .and_then(|v| if v.trim().is_empty() { None } else { Some(v) }); + if let Some(ref vers) = lldb_version { + cmd.arg("--lldb-version").arg(vers); + let lldb_python_dir = command(&lldb_exe) + .allow_failure() + .arg("-P") + .run_capture_stdout(builder) + .stdout_if_ok() + .map(|p| p.lines().next().expect("lldb Python dir not found").to_string()); + if let Some(ref dir) = lldb_python_dir { + cmd.arg("--lldb-python-dir").arg(dir); + } } } @@ -2332,16 +2348,6 @@ Please disable assertions with `rust.debug-assertions = false`. cmd.env("RUST_TEST_TMPDIR", builder.tempdir()); - cmd.arg("--adb-path").arg("adb"); - cmd.arg("--adb-test-dir").arg(ADB_TEST_DIR); - if target.contains("android") && !builder.config.dry_run() { - // Assume that cc for this target comes from the android sysroot - cmd.arg("--android-cross-path") - .arg(builder.cc(target).parent().unwrap().parent().unwrap()); - } else { - cmd.arg("--android-cross-path").arg(""); - } - if builder.config.cmd.rustfix_coverage() { cmd.arg("--rustfix-coverage"); } From bcfbd7401d76b6c3455376e584402cc2a6b22dce Mon Sep 17 00:00:00 2001 From: Zalathar Date: Sat, 25 Oct 2025 20:34:51 +1100 Subject: [PATCH 22/28] Extract some discovery code for debuginfo tests into submodules --- src/bootstrap/src/core/build_steps/test.rs | 42 +++++++-------------- src/bootstrap/src/core/debuggers/android.rs | 24 ++++++++++++ src/bootstrap/src/core/debuggers/gdb.rs | 13 +++++++ src/bootstrap/src/core/debuggers/lldb.rs | 32 ++++++++++++++++ src/bootstrap/src/core/debuggers/mod.rs | 10 +++++ src/bootstrap/src/core/mod.rs | 1 + 6 files changed, 93 insertions(+), 29 deletions(-) create mode 100644 src/bootstrap/src/core/debuggers/android.rs create mode 100644 src/bootstrap/src/core/debuggers/gdb.rs create mode 100644 src/bootstrap/src/core/debuggers/lldb.rs create mode 100644 src/bootstrap/src/core/debuggers/mod.rs diff --git a/src/bootstrap/src/core/build_steps/test.rs b/src/bootstrap/src/core/build_steps/test.rs index 9a6b47bdf3a1a..57dedcccf98c8 100644 --- a/src/bootstrap/src/core/build_steps/test.rs +++ b/src/bootstrap/src/core/build_steps/test.rs @@ -29,6 +29,7 @@ use crate::core::builder::{ }; use crate::core::config::TargetSelection; use crate::core::config::flags::{Subcommand, get_completion, top_level_help}; +use crate::core::debuggers; use crate::utils::build_stamp::{self, BuildStamp}; use crate::utils::exec::{BootstrapCommand, command}; use crate::utils::helpers::{ @@ -38,8 +39,6 @@ use crate::utils::helpers::{ use crate::utils::render_tests::{add_flags_and_try_run_tests, try_run_tests}; use crate::{CLang, CodegenBackendKind, DocTests, GitRepo, Mode, PathSet, envify}; -const ADB_TEST_DIR: &str = "/data/local/tmp/work"; - /// Runs `cargo test` on various internal tools used by bootstrap. #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct CrateBootstrap { @@ -2082,39 +2081,24 @@ Please disable assertions with `rust.debug-assertions = false`. // modes, even though they should only be needed in "debuginfo" mode, // because the GDB-discovery code in compiletest currently assumes that // `--android-cross-path` is always set for Android targets. - cmd.arg("--adb-path").arg("adb"); - cmd.arg("--adb-test-dir").arg(ADB_TEST_DIR); - if target.contains("android") && !builder.config.dry_run() { - // Assume that cc for this target comes from the android sysroot - cmd.arg("--android-cross-path") - .arg(builder.cc(target).parent().unwrap().parent().unwrap()); - } else { - cmd.arg("--android-cross-path").arg(""); + if let Some(debuggers::Android { adb_path, adb_test_dir, android_cross_path }) = + debuggers::discover_android(builder, target) + { + cmd.arg("--adb-path").arg(adb_path); + cmd.arg("--adb-test-dir").arg(adb_test_dir); + cmd.arg("--android-cross-path").arg(android_cross_path); } if mode == "debuginfo" { - if let Some(ref gdb) = builder.config.gdb { + if let Some(debuggers::Gdb { gdb }) = debuggers::discover_gdb(builder) { cmd.arg("--gdb").arg(gdb); } - let lldb_exe = builder.config.lldb.clone().unwrap_or_else(|| PathBuf::from("lldb")); - let lldb_version = command(&lldb_exe) - .allow_failure() - .arg("--version") - .run_capture(builder) - .stdout_if_ok() - .and_then(|v| if v.trim().is_empty() { None } else { Some(v) }); - if let Some(ref vers) = lldb_version { - cmd.arg("--lldb-version").arg(vers); - let lldb_python_dir = command(&lldb_exe) - .allow_failure() - .arg("-P") - .run_capture_stdout(builder) - .stdout_if_ok() - .map(|p| p.lines().next().expect("lldb Python dir not found").to_string()); - if let Some(ref dir) = lldb_python_dir { - cmd.arg("--lldb-python-dir").arg(dir); - } + if let Some(debuggers::Lldb { lldb_version, lldb_python_dir }) = + debuggers::discover_lldb(builder) + { + cmd.arg("--lldb-version").arg(lldb_version); + cmd.arg("--lldb-python-dir").arg(lldb_python_dir); } } diff --git a/src/bootstrap/src/core/debuggers/android.rs b/src/bootstrap/src/core/debuggers/android.rs new file mode 100644 index 0000000000000..b1ad9ca555fbd --- /dev/null +++ b/src/bootstrap/src/core/debuggers/android.rs @@ -0,0 +1,24 @@ +use std::path::PathBuf; + +use crate::core::builder::Builder; +use crate::core::config::TargetSelection; + +pub(crate) struct Android { + pub(crate) adb_path: &'static str, + pub(crate) adb_test_dir: &'static str, + pub(crate) android_cross_path: PathBuf, +} + +pub(crate) fn discover_android(builder: &Builder<'_>, target: TargetSelection) -> Option { + let adb_path = "adb"; + // See . + let adb_test_dir = "/data/local/tmp/work"; + + let android_cross_path = if target.contains("android") && !builder.config.dry_run() { + builder.cc(target).parent().unwrap().parent().unwrap().to_owned() + } else { + PathBuf::new() + }; + + Some(Android { adb_path, adb_test_dir, android_cross_path }) +} diff --git a/src/bootstrap/src/core/debuggers/gdb.rs b/src/bootstrap/src/core/debuggers/gdb.rs new file mode 100644 index 0000000000000..ddad0909e4f07 --- /dev/null +++ b/src/bootstrap/src/core/debuggers/gdb.rs @@ -0,0 +1,13 @@ +use std::path::Path; + +use crate::core::builder::Builder; + +pub(crate) struct Gdb<'a> { + pub(crate) gdb: &'a Path, +} + +pub(crate) fn discover_gdb<'a>(builder: &'a Builder<'_>) -> Option> { + let gdb = builder.config.gdb.as_deref()?; + + Some(Gdb { gdb }) +} diff --git a/src/bootstrap/src/core/debuggers/lldb.rs b/src/bootstrap/src/core/debuggers/lldb.rs new file mode 100644 index 0000000000000..66ab45573d6bb --- /dev/null +++ b/src/bootstrap/src/core/debuggers/lldb.rs @@ -0,0 +1,32 @@ +use std::path::PathBuf; + +use crate::core::builder::Builder; +use crate::utils::exec::command; + +pub(crate) struct Lldb { + pub(crate) lldb_version: String, + pub(crate) lldb_python_dir: String, +} + +pub(crate) fn discover_lldb(builder: &Builder<'_>) -> Option { + // FIXME(#148361): We probably should not be picking up whatever arbitrary + // lldb happens to be in the user's path, and instead require some kind of + // explicit opt-in or configuration. + let lldb_exe = builder.config.lldb.clone().unwrap_or_else(|| PathBuf::from("lldb")); + + let lldb_version = command(&lldb_exe) + .allow_failure() + .arg("--version") + .run_capture(builder) + .stdout_if_ok() + .and_then(|v| if v.trim().is_empty() { None } else { Some(v) })?; + + let lldb_python_dir = command(&lldb_exe) + .allow_failure() + .arg("-P") + .run_capture_stdout(builder) + .stdout_if_ok() + .map(|p| p.lines().next().expect("lldb Python dir not found").to_string())?; + + Some(Lldb { lldb_version, lldb_python_dir }) +} diff --git a/src/bootstrap/src/core/debuggers/mod.rs b/src/bootstrap/src/core/debuggers/mod.rs new file mode 100644 index 0000000000000..011ce4081a43e --- /dev/null +++ b/src/bootstrap/src/core/debuggers/mod.rs @@ -0,0 +1,10 @@ +//! Code for discovering debuggers and debugger-related configuration, so that +//! it can be passed to compiletest when running debuginfo tests. + +pub(crate) use self::android::{Android, discover_android}; +pub(crate) use self::gdb::{Gdb, discover_gdb}; +pub(crate) use self::lldb::{Lldb, discover_lldb}; + +mod android; +mod gdb; +mod lldb; diff --git a/src/bootstrap/src/core/mod.rs b/src/bootstrap/src/core/mod.rs index 9e18d6704d4fe..f27a09c81c55b 100644 --- a/src/bootstrap/src/core/mod.rs +++ b/src/bootstrap/src/core/mod.rs @@ -1,6 +1,7 @@ pub(crate) mod build_steps; pub(crate) mod builder; pub(crate) mod config; +pub(crate) mod debuggers; pub(crate) mod download; pub(crate) mod metadata; pub(crate) mod sanity; From b9e127a830df680e2f117064376dfd876c0b8ae4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ila=C3=AF=20Deutel?= <10098207+ilai-deutel@users.noreply.github.com> Date: Sat, 1 Nov 2025 23:46:35 -0400 Subject: [PATCH 23/28] Fix documentation for std::panic::update_hook * `set_hook` expects a boxed function * Missing closing delimiter for the closure --- library/std/src/panicking.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/library/std/src/panicking.rs b/library/std/src/panicking.rs index b7be869c4eb48..9af3e5f63ffb4 100644 --- a/library/std/src/panicking.rs +++ b/library/std/src/panicking.rs @@ -215,10 +215,10 @@ pub fn take_hook() -> Box) + 'static + Sync + Send> { /// /// // Equivalent to /// // let prev = panic::take_hook(); -/// // panic::set_hook(move |info| { +/// // panic::set_hook(Box::new(move |info| { /// // println!("..."); /// // prev(info); -/// // ); +/// // })); /// panic::update_hook(move |prev, info| { /// println!("Print custom message and execute panic handler as usual"); /// prev(info); From d335ea91b5bc6cf184229c49e313e70f3ab180bd Mon Sep 17 00:00:00 2001 From: sayantn Date: Thu, 9 Oct 2025 17:05:34 +0530 Subject: [PATCH 24/28] Refactor implementation of float minmax intrinsics --- .../src/interpret/intrinsics.rs | 133 +++++++++--------- .../src/interpret/intrinsics/simd.rs | 54 ++----- 2 files changed, 80 insertions(+), 107 deletions(-) diff --git a/compiler/rustc_const_eval/src/interpret/intrinsics.rs b/compiler/rustc_const_eval/src/interpret/intrinsics.rs index f0712644465a1..90f14a69fac34 100644 --- a/compiler/rustc_const_eval/src/interpret/intrinsics.rs +++ b/compiler/rustc_const_eval/src/interpret/intrinsics.rs @@ -34,6 +34,26 @@ enum MulAddType { Nondeterministic, } +#[derive(Copy, Clone)] +pub(crate) enum MinMax { + /// The IEEE `Minimum` operation - see `f32::minimum` etc + /// In particular, `-0.0` is considered smaller than `+0.0` and + /// if either input is NaN, the result is NaN. + Minimum, + /// The IEEE `MinNum` operation - see `f32::min` etc + /// In particular, if the inputs are `-0.0` and `+0.0`, the result is non-deterministic, + /// and is one argument is NaN, the other one is returned. + MinNum, + /// The IEEE `Maximum` operation - see `f32::maximum` etc + /// In particular, `-0.0` is considered smaller than `+0.0` and + /// if either input is NaN, the result is NaN. + Maximum, + /// The IEEE `MaxNum` operation - see `f32::max` etc + /// In particular, if the inputs are `-0.0` and `+0.0`, the result is non-deterministic, + /// and is one argument is NaN, the other one is returned. + MaxNum, +} + /// Directly returns an `Allocation` containing an absolute path representation of the given type. pub(crate) fn alloc_type_name<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> (AllocId, u64) { let path = crate::util::type_name(tcx, ty); @@ -513,25 +533,33 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { self.write_scalar(Scalar::from_target_usize(align.bytes(), self), dest)?; } - sym::minnumf16 => self.float_min_intrinsic::(args, dest)?, - sym::minnumf32 => self.float_min_intrinsic::(args, dest)?, - sym::minnumf64 => self.float_min_intrinsic::(args, dest)?, - sym::minnumf128 => self.float_min_intrinsic::(args, dest)?, + sym::minnumf16 => self.float_minmax_intrinsic::(args, MinMax::MinNum, dest)?, + sym::minnumf32 => self.float_minmax_intrinsic::(args, MinMax::MinNum, dest)?, + sym::minnumf64 => self.float_minmax_intrinsic::(args, MinMax::MinNum, dest)?, + sym::minnumf128 => self.float_minmax_intrinsic::(args, MinMax::MinNum, dest)?, - sym::minimumf16 => self.float_minimum_intrinsic::(args, dest)?, - sym::minimumf32 => self.float_minimum_intrinsic::(args, dest)?, - sym::minimumf64 => self.float_minimum_intrinsic::(args, dest)?, - sym::minimumf128 => self.float_minimum_intrinsic::(args, dest)?, + sym::minimumf16 => self.float_minmax_intrinsic::(args, MinMax::Minimum, dest)?, + sym::minimumf32 => { + self.float_minmax_intrinsic::(args, MinMax::Minimum, dest)? + } + sym::minimumf64 => { + self.float_minmax_intrinsic::(args, MinMax::Minimum, dest)? + } + sym::minimumf128 => self.float_minmax_intrinsic::(args, MinMax::Minimum, dest)?, - sym::maxnumf16 => self.float_max_intrinsic::(args, dest)?, - sym::maxnumf32 => self.float_max_intrinsic::(args, dest)?, - sym::maxnumf64 => self.float_max_intrinsic::(args, dest)?, - sym::maxnumf128 => self.float_max_intrinsic::(args, dest)?, + sym::maxnumf16 => self.float_minmax_intrinsic::(args, MinMax::MaxNum, dest)?, + sym::maxnumf32 => self.float_minmax_intrinsic::(args, MinMax::MaxNum, dest)?, + sym::maxnumf64 => self.float_minmax_intrinsic::(args, MinMax::MaxNum, dest)?, + sym::maxnumf128 => self.float_minmax_intrinsic::(args, MinMax::MaxNum, dest)?, - sym::maximumf16 => self.float_maximum_intrinsic::(args, dest)?, - sym::maximumf32 => self.float_maximum_intrinsic::(args, dest)?, - sym::maximumf64 => self.float_maximum_intrinsic::(args, dest)?, - sym::maximumf128 => self.float_maximum_intrinsic::(args, dest)?, + sym::maximumf16 => self.float_minmax_intrinsic::(args, MinMax::Maximum, dest)?, + sym::maximumf32 => { + self.float_minmax_intrinsic::(args, MinMax::Maximum, dest)? + } + sym::maximumf64 => { + self.float_minmax_intrinsic::(args, MinMax::Maximum, dest)? + } + sym::maximumf128 => self.float_minmax_intrinsic::(args, MinMax::Maximum, dest)?, sym::copysignf16 => self.float_copysign_intrinsic::(args, dest)?, sym::copysignf32 => self.float_copysign_intrinsic::(args, dest)?, @@ -936,76 +964,45 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { interp_ok(Scalar::from_bool(lhs_bytes == rhs_bytes)) } - fn float_min_intrinsic( - &mut self, - args: &[OpTy<'tcx, M::Provenance>], - dest: &PlaceTy<'tcx, M::Provenance>, - ) -> InterpResult<'tcx, ()> - where - F: rustc_apfloat::Float + rustc_apfloat::FloatConvert + Into>, - { - let a: F = self.read_scalar(&args[0])?.to_float()?; - let b: F = self.read_scalar(&args[1])?.to_float()?; - let res = if a == b { - // They are definitely not NaN (those are never equal), but they could be `+0` and `-0`. - // Let the machine decide which one to return. - M::equal_float_min_max(self, a, b) - } else { - self.adjust_nan(a.min(b), &[a, b]) - }; - self.write_scalar(res, dest)?; - interp_ok(()) - } - - fn float_max_intrinsic( - &mut self, - args: &[OpTy<'tcx, M::Provenance>], - dest: &PlaceTy<'tcx, M::Provenance>, - ) -> InterpResult<'tcx, ()> + fn float_minmax( + &self, + a: Scalar, + b: Scalar, + op: MinMax, + ) -> InterpResult<'tcx, Scalar> where F: rustc_apfloat::Float + rustc_apfloat::FloatConvert + Into>, { - let a: F = self.read_scalar(&args[0])?.to_float()?; - let b: F = self.read_scalar(&args[1])?.to_float()?; - let res = if a == b { + let a: F = a.to_float()?; + let b: F = b.to_float()?; + let res = if matches!(op, MinMax::MinNum | MinMax::MaxNum) && a == b { // They are definitely not NaN (those are never equal), but they could be `+0` and `-0`. // Let the machine decide which one to return. M::equal_float_min_max(self, a, b) } else { - self.adjust_nan(a.max(b), &[a, b]) + let result = match op { + MinMax::Minimum => a.minimum(b), + MinMax::MinNum => a.min(b), + MinMax::Maximum => a.maximum(b), + MinMax::MaxNum => a.max(b), + }; + self.adjust_nan(result, &[a, b]) }; - self.write_scalar(res, dest)?; - interp_ok(()) - } - fn float_minimum_intrinsic( - &mut self, - args: &[OpTy<'tcx, M::Provenance>], - dest: &PlaceTy<'tcx, M::Provenance>, - ) -> InterpResult<'tcx, ()> - where - F: rustc_apfloat::Float + rustc_apfloat::FloatConvert + Into>, - { - let a: F = self.read_scalar(&args[0])?.to_float()?; - let b: F = self.read_scalar(&args[1])?.to_float()?; - let res = a.minimum(b); - let res = self.adjust_nan(res, &[a, b]); - self.write_scalar(res, dest)?; - interp_ok(()) + interp_ok(res.into()) } - fn float_maximum_intrinsic( + fn float_minmax_intrinsic( &mut self, args: &[OpTy<'tcx, M::Provenance>], + op: MinMax, dest: &PlaceTy<'tcx, M::Provenance>, ) -> InterpResult<'tcx, ()> where F: rustc_apfloat::Float + rustc_apfloat::FloatConvert + Into>, { - let a: F = self.read_scalar(&args[0])?.to_float()?; - let b: F = self.read_scalar(&args[1])?.to_float()?; - let res = a.maximum(b); - let res = self.adjust_nan(res, &[a, b]); + let res = + self.float_minmax::(self.read_scalar(&args[0])?, self.read_scalar(&args[1])?, op)?; self.write_scalar(res, dest)?; interp_ok(()) } diff --git a/compiler/rustc_const_eval/src/interpret/intrinsics/simd.rs b/compiler/rustc_const_eval/src/interpret/intrinsics/simd.rs index 84489028e190d..13b6623accd2a 100644 --- a/compiler/rustc_const_eval/src/interpret/intrinsics/simd.rs +++ b/compiler/rustc_const_eval/src/interpret/intrinsics/simd.rs @@ -9,17 +9,11 @@ use rustc_span::{Symbol, sym}; use tracing::trace; use super::{ - ImmTy, InterpCx, InterpResult, Machine, MulAddType, OpTy, PlaceTy, Provenance, Scalar, Size, - interp_ok, throw_ub_format, + ImmTy, InterpCx, InterpResult, Machine, MinMax, MulAddType, OpTy, PlaceTy, Provenance, Scalar, + Size, interp_ok, throw_ub_format, }; use crate::interpret::Writeable; -#[derive(Copy, Clone)] -pub(crate) enum MinMax { - Min, - Max, -} - impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { /// Returns `true` if emulation happened. /// Here we implement the intrinsics that are common to all CTFE instances; individual machines can add their own @@ -217,8 +211,8 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { sym::simd_le => Op::MirOp(BinOp::Le), sym::simd_gt => Op::MirOp(BinOp::Gt), sym::simd_ge => Op::MirOp(BinOp::Ge), - sym::simd_fmax => Op::FMinMax(MinMax::Max), - sym::simd_fmin => Op::FMinMax(MinMax::Min), + sym::simd_fmax => Op::FMinMax(MinMax::MaxNum), + sym::simd_fmin => Op::FMinMax(MinMax::MinNum), sym::simd_saturating_add => Op::SaturatingOp(BinOp::Add), sym::simd_saturating_sub => Op::SaturatingOp(BinOp::Sub), sym::simd_arith_offset => Op::WrappingOffset, @@ -310,8 +304,8 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { sym::simd_reduce_xor => Op::MirOp(BinOp::BitXor), sym::simd_reduce_any => Op::MirOpBool(BinOp::BitOr), sym::simd_reduce_all => Op::MirOpBool(BinOp::BitAnd), - sym::simd_reduce_max => Op::MinMax(MinMax::Max), - sym::simd_reduce_min => Op::MinMax(MinMax::Min), + sym::simd_reduce_max => Op::MinMax(MinMax::MaxNum), + sym::simd_reduce_min => Op::MinMax(MinMax::MinNum), _ => unreachable!(), }; @@ -333,10 +327,10 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { if matches!(res.layout.ty.kind(), ty::Float(_)) { ImmTy::from_scalar(self.fminmax_op(mmop, &res, &op)?, res.layout) } else { - // Just boring integers, so NaNs to worry about + // Just boring integers, no NaNs to worry about. let mirop = match mmop { - MinMax::Min => BinOp::Le, - MinMax::Max => BinOp::Ge, + MinMax::MinNum | MinMax::Minimum => BinOp::Le, + MinMax::MaxNum | MinMax::Maximum => BinOp::Ge, }; if self.binary_op(mirop, &res, &op)?.to_scalar().to_bool()? { res @@ -749,12 +743,12 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { interp_ok(true) } - fn fminmax_op( + fn fminmax_op( &self, op: MinMax, - left: &ImmTy<'tcx, Prov>, - right: &ImmTy<'tcx, Prov>, - ) -> InterpResult<'tcx, Scalar> { + left: &ImmTy<'tcx, M::Provenance>, + right: &ImmTy<'tcx, M::Provenance>, + ) -> InterpResult<'tcx, Scalar> { assert_eq!(left.layout.ty, right.layout.ty); let ty::Float(float_ty) = left.layout.ty.kind() else { bug!("fmax operand is not a float") @@ -763,26 +757,8 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { let right = right.to_scalar(); interp_ok(match float_ty { FloatTy::F16 => unimplemented!("f16_f128"), - FloatTy::F32 => { - let left = left.to_f32()?; - let right = right.to_f32()?; - let res = match op { - MinMax::Min => left.min(right), - MinMax::Max => left.max(right), - }; - let res = self.adjust_nan(res, &[left, right]); - Scalar::from_f32(res) - } - FloatTy::F64 => { - let left = left.to_f64()?; - let right = right.to_f64()?; - let res = match op { - MinMax::Min => left.min(right), - MinMax::Max => left.max(right), - }; - let res = self.adjust_nan(res, &[left, right]); - Scalar::from_f64(res) - } + FloatTy::F32 => self.float_minmax::(left, right, op)?, + FloatTy::F64 => self.float_minmax::(left, right, op)?, FloatTy::F128 => unimplemented!("f16_f128"), }) } From ebf2e10238b946936ff9ff0476932e583fa59c9f Mon Sep 17 00:00:00 2001 From: sayantn Date: Fri, 10 Oct 2025 22:00:13 +0530 Subject: [PATCH 25/28] Refactor float rounding intrinsics a bit --- .../src/interpret/intrinsics.rs | 18 +++++++++++++++--- .../src/interpret/intrinsics/simd.rs | 15 +++------------ 2 files changed, 18 insertions(+), 15 deletions(-) diff --git a/compiler/rustc_const_eval/src/interpret/intrinsics.rs b/compiler/rustc_const_eval/src/interpret/intrinsics.rs index 90f14a69fac34..58d7f8ef8c637 100644 --- a/compiler/rustc_const_eval/src/interpret/intrinsics.rs +++ b/compiler/rustc_const_eval/src/interpret/intrinsics.rs @@ -1036,6 +1036,20 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { interp_ok(()) } + fn float_round( + &mut self, + x: Scalar, + mode: rustc_apfloat::Round, + ) -> InterpResult<'tcx, Scalar> + where + F: rustc_apfloat::Float + rustc_apfloat::FloatConvert + Into>, + { + let x: F = x.to_float()?; + let res = x.round_to_integral(mode).value; + let res = self.adjust_nan(res, &[x]); + interp_ok(res.into()) + } + fn float_round_intrinsic( &mut self, args: &[OpTy<'tcx, M::Provenance>], @@ -1045,9 +1059,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { where F: rustc_apfloat::Float + rustc_apfloat::FloatConvert + Into>, { - let x: F = self.read_scalar(&args[0])?.to_float()?; - let res = x.round_to_integral(mode).value; - let res = self.adjust_nan(res, &[x]); + let res = self.float_round::(self.read_scalar(&args[0])?, mode)?; self.write_scalar(res, dest)?; interp_ok(()) } diff --git a/compiler/rustc_const_eval/src/interpret/intrinsics/simd.rs b/compiler/rustc_const_eval/src/interpret/intrinsics/simd.rs index 13b6623accd2a..f580dc6bebc36 100644 --- a/compiler/rustc_const_eval/src/interpret/intrinsics/simd.rs +++ b/compiler/rustc_const_eval/src/interpret/intrinsics/simd.rs @@ -134,20 +134,11 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { intrinsic_name ) }; + let op = op.to_scalar(); match float_ty { FloatTy::F16 => unimplemented!("f16_f128"), - FloatTy::F32 => { - let f = op.to_scalar().to_f32()?; - let res = f.round_to_integral(rounding).value; - let res = self.adjust_nan(res, &[f]); - Scalar::from_f32(res) - } - FloatTy::F64 => { - let f = op.to_scalar().to_f64()?; - let res = f.round_to_integral(rounding).value; - let res = self.adjust_nan(res, &[f]); - Scalar::from_f64(res) - } + FloatTy::F32 => self.float_round::(op, rounding)?, + FloatTy::F64 => self.float_round::(op, rounding)?, FloatTy::F128 => unimplemented!("f16_f128"), } } From 4d70bf4c5a4ba773002c031355690c2d717eea4b Mon Sep 17 00:00:00 2001 From: sayantn Date: Fri, 10 Oct 2025 22:02:19 +0530 Subject: [PATCH 26/28] Add some missing f16-f128 support in intrinsics --- .../src/interpret/intrinsics/simd.rs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/compiler/rustc_const_eval/src/interpret/intrinsics/simd.rs b/compiler/rustc_const_eval/src/interpret/intrinsics/simd.rs index f580dc6bebc36..f97bdf627ed90 100644 --- a/compiler/rustc_const_eval/src/interpret/intrinsics/simd.rs +++ b/compiler/rustc_const_eval/src/interpret/intrinsics/simd.rs @@ -1,6 +1,6 @@ use either::Either; use rustc_abi::Endian; -use rustc_apfloat::ieee::{Double, Single}; +use rustc_apfloat::ieee::{Double, Half, Quad, Single}; use rustc_apfloat::{Float, Round}; use rustc_middle::mir::interpret::{InterpErrorKind, UndefinedBehaviorInfo}; use rustc_middle::ty::FloatTy; @@ -120,10 +120,10 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { let op = op.to_scalar(); // "Bitwise" operation, no NaN adjustments match float_ty { - FloatTy::F16 => unimplemented!("f16_f128"), + FloatTy::F16 => Scalar::from_f16(op.to_f16()?.abs()), FloatTy::F32 => Scalar::from_f32(op.to_f32()?.abs()), FloatTy::F64 => Scalar::from_f64(op.to_f64()?.abs()), - FloatTy::F128 => unimplemented!("f16_f128"), + FloatTy::F128 => Scalar::from_f128(op.to_f128()?.abs()), } } Op::Round(rounding) => { @@ -136,10 +136,10 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { }; let op = op.to_scalar(); match float_ty { - FloatTy::F16 => unimplemented!("f16_f128"), + FloatTy::F16 => self.float_round::(op, rounding)?, FloatTy::F32 => self.float_round::(op, rounding)?, FloatTy::F64 => self.float_round::(op, rounding)?, - FloatTy::F128 => unimplemented!("f16_f128"), + FloatTy::F128 => self.float_round::(op, rounding)?, } } Op::Numeric(name) => { @@ -716,10 +716,10 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { }; let val = match float_ty { - FloatTy::F16 => unimplemented!("f16_f128"), + FloatTy::F16 => self.float_muladd::(a, b, c, typ)?, FloatTy::F32 => self.float_muladd::(a, b, c, typ)?, FloatTy::F64 => self.float_muladd::(a, b, c, typ)?, - FloatTy::F128 => unimplemented!("f16_f128"), + FloatTy::F128 => self.float_muladd::(a, b, c, typ)?, }; self.write_scalar(val, &dest)?; } @@ -747,10 +747,10 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { let left = left.to_scalar(); let right = right.to_scalar(); interp_ok(match float_ty { - FloatTy::F16 => unimplemented!("f16_f128"), + FloatTy::F16 => self.float_minmax::(left, right, op)?, FloatTy::F32 => self.float_minmax::(left, right, op)?, FloatTy::F64 => self.float_minmax::(left, right, op)?, - FloatTy::F128 => unimplemented!("f16_f128"), + FloatTy::F128 => self.float_minmax::(left, right, op)?, }) } } From 0681bae055ce776bf20c62289c9cc179a011d329 Mon Sep 17 00:00:00 2001 From: sayantn Date: Sat, 11 Oct 2025 03:24:23 +0530 Subject: [PATCH 27/28] Add Miri tests for f16/f128 SIMD operations --- .../tests/pass/intrinsics/portable-simd.rs | 262 +++++++++++++++++- 1 file changed, 260 insertions(+), 2 deletions(-) diff --git a/src/tools/miri/tests/pass/intrinsics/portable-simd.rs b/src/tools/miri/tests/pass/intrinsics/portable-simd.rs index e2cd08733af1c..b7d2584c58fbd 100644 --- a/src/tools/miri/tests/pass/intrinsics/portable-simd.rs +++ b/src/tools/miri/tests/pass/intrinsics/portable-simd.rs @@ -6,18 +6,143 @@ rustc_attrs, intrinsics, core_intrinsics, - repr_simd + repr_simd, + f16, + f128 )] -#![allow(incomplete_features, internal_features)] +#![allow(incomplete_features, internal_features, non_camel_case_types)] +use std::fmt::{self, Debug, Formatter}; use std::intrinsics::simd as intrinsics; use std::ptr; use std::simd::StdFloat; use std::simd::prelude::*; +#[repr(simd, packed)] +#[derive(Copy)] +struct PackedSimd([T; N]); + +impl Clone for PackedSimd { + fn clone(&self) -> Self { + *self + } +} + +impl PartialEq for PackedSimd { + fn eq(&self, other: &Self) -> bool { + self.into_array() == other.into_array() + } +} + +impl Debug for PackedSimd { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + Debug::fmt(&self.into_array(), f) + } +} + +type f16x2 = PackedSimd; +type f16x4 = PackedSimd; + +type f128x2 = PackedSimd; +type f128x4 = PackedSimd; + +impl PackedSimd { + fn splat(x: T) -> Self { + Self([x; N]) + } + fn from_array(a: [T; N]) -> Self { + Self(a) + } + fn into_array(self) -> [T; N] { + // as we have `repr(packed)`, there shouldn't be any padding bytes + unsafe { std::mem::transmute_copy(&self) } + } +} + #[rustc_intrinsic] #[rustc_nounwind] pub unsafe fn simd_shuffle_const_generic(x: T, y: T) -> U; +pub fn simd_ops_f16() { + use intrinsics::*; + + // small hack to make type inference better + macro_rules! assert_eq { + ($a:expr, $b:expr $(,$t:tt)*) => {{ + let a = $a; + let b = $b; + if false { let _inference = b == a; } + ::std::assert_eq!(a, b, $(,$t)*) + }} + } + + let a = f16x4::splat(10.0); + let b = f16x4::from_array([1.0, 2.0, 3.0, -4.0]); + + unsafe { + assert_eq!(simd_neg(b), f16x4::from_array([-1.0, -2.0, -3.0, 4.0])); + assert_eq!(simd_add(a, b), f16x4::from_array([11.0, 12.0, 13.0, 6.0])); + assert_eq!(simd_sub(a, b), f16x4::from_array([9.0, 8.0, 7.0, 14.0])); + assert_eq!(simd_mul(a, b), f16x4::from_array([10.0, 20.0, 30.0, -40.0])); + assert_eq!(simd_div(b, a), f16x4::from_array([0.1, 0.2, 0.3, -0.4])); + assert_eq!(simd_div(a, f16x4::splat(2.0)), f16x4::splat(5.0)); + assert_eq!(simd_rem(a, b), f16x4::from_array([0.0, 0.0, 1.0, 2.0])); + assert_eq!(simd_fabs(b), f16x4::from_array([1.0, 2.0, 3.0, 4.0])); + assert_eq!( + simd_fmax(a, simd_mul(b, f16x4::splat(4.0))), + f16x4::from_array([10.0, 10.0, 12.0, 10.0]) + ); + assert_eq!( + simd_fmin(a, simd_mul(b, f16x4::splat(4.0))), + f16x4::from_array([4.0, 8.0, 10.0, -16.0]) + ); + + assert_eq!(simd_fma(a, b, a), simd_add(simd_mul(a, b), a)); + assert_eq!(simd_fma(b, b, a), simd_add(simd_mul(b, b), a)); + assert_eq!(simd_fma(a, b, b), simd_add(simd_mul(a, b), b)); + assert_eq!( + simd_fma(f16x4::splat(-3.2), b, f16x4::splat(f16::NEG_INFINITY)), + f16x4::splat(f16::NEG_INFINITY) + ); + + assert_eq!(simd_relaxed_fma(a, b, a), simd_add(simd_mul(a, b), a)); + assert_eq!(simd_relaxed_fma(b, b, a), simd_add(simd_mul(b, b), a)); + assert_eq!(simd_relaxed_fma(a, b, b), simd_add(simd_mul(a, b), b)); + assert_eq!( + simd_relaxed_fma(f16x4::splat(-3.2), b, f16x4::splat(f16::NEG_INFINITY)), + f16x4::splat(f16::NEG_INFINITY) + ); + + assert_eq!(simd_eq(a, simd_mul(f16x4::splat(5.0), b)), i32x4::from_array([0, !0, 0, 0])); + assert_eq!(simd_ne(a, simd_mul(f16x4::splat(5.0), b)), i32x4::from_array([!0, 0, !0, !0])); + assert_eq!(simd_le(a, simd_mul(f16x4::splat(5.0), b)), i32x4::from_array([0, !0, !0, 0])); + assert_eq!(simd_lt(a, simd_mul(f16x4::splat(5.0), b)), i32x4::from_array([0, 0, !0, 0])); + assert_eq!(simd_ge(a, simd_mul(f16x4::splat(5.0), b)), i32x4::from_array([!0, !0, 0, !0])); + assert_eq!(simd_gt(a, simd_mul(f16x4::splat(5.0), b)), i32x4::from_array([!0, 0, 0, !0])); + + assert_eq!(simd_reduce_add_ordered(a, 0.0), 40.0f16); + assert_eq!(simd_reduce_add_ordered(b, 0.0), 2.0f16); + assert_eq!(simd_reduce_mul_ordered(a, 1.0), 10000.0f16); + assert_eq!(simd_reduce_mul_ordered(b, 1.0), -24.0f16); + assert_eq!(simd_reduce_max(a), 10.0f16); + assert_eq!(simd_reduce_max(b), 3.0f16); + assert_eq!(simd_reduce_min(a), 10.0f16); + assert_eq!(simd_reduce_min(b), -4.0f16); + + assert_eq!( + simd_fmax(f16x2::from_array([0.0, f16::NAN]), f16x2::from_array([f16::NAN, 0.0])), + f16x2::from_array([0.0, 0.0]) + ); + assert_eq!(simd_reduce_max(f16x2::from_array([0.0, f16::NAN])), 0.0f16); + assert_eq!(simd_reduce_max(f16x2::from_array([f16::NAN, 0.0])), 0.0f16); + assert_eq!( + simd_fmin(f16x2::from_array([0.0, f16::NAN]), f16x2::from_array([f16::NAN, 0.0])), + f16x2::from_array([0.0, 0.0]) + ); + assert_eq!(simd_reduce_min(f16x2::from_array([0.0, f16::NAN])), 0.0f16); + assert_eq!(simd_reduce_min(f16x2::from_array([f16::NAN, 0.0])), 0.0f16); + } +} + fn simd_ops_f32() { let a = f32x4::splat(10.0); let b = f32x4::from_array([1.0, 2.0, 3.0, -4.0]); @@ -148,6 +273,87 @@ fn simd_ops_f64() { assert_eq!(f64x2::from_array([f64::NAN, 0.0]).reduce_min(), 0.0); } +pub fn simd_ops_f128() { + use intrinsics::*; + + // small hack to make type inference better + macro_rules! assert_eq { + ($a:expr, $b:expr $(,$t:tt)*) => {{ + let a = $a; + let b = $b; + if false { let _inference = b == a; } + ::std::assert_eq!(a, b, $(,$t)*) + }} + } + + let a = f128x4::splat(10.0); + let b = f128x4::from_array([1.0, 2.0, 3.0, -4.0]); + + unsafe { + assert_eq!(simd_neg(b), f128x4::from_array([-1.0, -2.0, -3.0, 4.0])); + assert_eq!(simd_add(a, b), f128x4::from_array([11.0, 12.0, 13.0, 6.0])); + assert_eq!(simd_sub(a, b), f128x4::from_array([9.0, 8.0, 7.0, 14.0])); + assert_eq!(simd_mul(a, b), f128x4::from_array([10.0, 20.0, 30.0, -40.0])); + assert_eq!(simd_div(b, a), f128x4::from_array([0.1, 0.2, 0.3, -0.4])); + assert_eq!(simd_div(a, f128x4::splat(2.0)), f128x4::splat(5.0)); + assert_eq!(simd_rem(a, b), f128x4::from_array([0.0, 0.0, 1.0, 2.0])); + assert_eq!(simd_fabs(b), f128x4::from_array([1.0, 2.0, 3.0, 4.0])); + assert_eq!( + simd_fmax(a, simd_mul(b, f128x4::splat(4.0))), + f128x4::from_array([10.0, 10.0, 12.0, 10.0]) + ); + assert_eq!( + simd_fmin(a, simd_mul(b, f128x4::splat(4.0))), + f128x4::from_array([4.0, 8.0, 10.0, -16.0]) + ); + + assert_eq!(simd_fma(a, b, a), simd_add(simd_mul(a, b), a)); + assert_eq!(simd_fma(b, b, a), simd_add(simd_mul(b, b), a)); + assert_eq!(simd_fma(a, b, b), simd_add(simd_mul(a, b), b)); + assert_eq!( + simd_fma(f128x4::splat(-3.2), b, f128x4::splat(f128::NEG_INFINITY)), + f128x4::splat(f128::NEG_INFINITY) + ); + + assert_eq!(simd_relaxed_fma(a, b, a), simd_add(simd_mul(a, b), a)); + assert_eq!(simd_relaxed_fma(b, b, a), simd_add(simd_mul(b, b), a)); + assert_eq!(simd_relaxed_fma(a, b, b), simd_add(simd_mul(a, b), b)); + assert_eq!( + simd_relaxed_fma(f128x4::splat(-3.2), b, f128x4::splat(f128::NEG_INFINITY)), + f128x4::splat(f128::NEG_INFINITY) + ); + + assert_eq!(simd_eq(a, simd_mul(f128x4::splat(5.0), b)), i32x4::from_array([0, !0, 0, 0])); + assert_eq!(simd_ne(a, simd_mul(f128x4::splat(5.0), b)), i32x4::from_array([!0, 0, !0, !0])); + assert_eq!(simd_le(a, simd_mul(f128x4::splat(5.0), b)), i32x4::from_array([0, !0, !0, 0])); + assert_eq!(simd_lt(a, simd_mul(f128x4::splat(5.0), b)), i32x4::from_array([0, 0, !0, 0])); + assert_eq!(simd_ge(a, simd_mul(f128x4::splat(5.0), b)), i32x4::from_array([!0, !0, 0, !0])); + assert_eq!(simd_gt(a, simd_mul(f128x4::splat(5.0), b)), i32x4::from_array([!0, 0, 0, !0])); + + assert_eq!(simd_reduce_add_ordered(a, 0.0), 40.0f128); + assert_eq!(simd_reduce_add_ordered(b, 0.0), 2.0f128); + assert_eq!(simd_reduce_mul_ordered(a, 1.0), 10000.0f128); + assert_eq!(simd_reduce_mul_ordered(b, 1.0), -24.0f128); + assert_eq!(simd_reduce_max(a), 10.0f128); + assert_eq!(simd_reduce_max(b), 3.0f128); + assert_eq!(simd_reduce_min(a), 10.0f128); + assert_eq!(simd_reduce_min(b), -4.0f128); + + assert_eq!( + simd_fmax(f128x2::from_array([0.0, f128::NAN]), f128x2::from_array([f128::NAN, 0.0])), + f128x2::from_array([0.0, 0.0]) + ); + assert_eq!(simd_reduce_max(f128x2::from_array([0.0, f128::NAN])), 0.0f128); + assert_eq!(simd_reduce_max(f128x2::from_array([f128::NAN, 0.0])), 0.0f128); + assert_eq!( + simd_fmin(f128x2::from_array([0.0, f128::NAN]), f128x2::from_array([f128::NAN, 0.0])), + f128x2::from_array([0.0, 0.0]) + ); + assert_eq!(simd_reduce_min(f128x2::from_array([0.0, f128::NAN])), 0.0f128); + assert_eq!(simd_reduce_min(f128x2::from_array([f128::NAN, 0.0])), 0.0f128); + } +} + fn simd_ops_i32() { let a = i32x4::splat(10); let b = i32x4::from_array([1, 2, 3, -4]); @@ -563,6 +769,31 @@ fn simd_gather_scatter() { } fn simd_round() { + unsafe { + use intrinsics::*; + + assert_eq!( + simd_ceil(f16x4::from_array([0.9, 1.001, 2.0, -4.5])), + f16x4::from_array([1.0, 2.0, 2.0, -4.0]) + ); + assert_eq!( + simd_floor(f16x4::from_array([0.9, 1.001, 2.0, -4.5])), + f16x4::from_array([0.0, 1.0, 2.0, -5.0]) + ); + assert_eq!( + simd_round(f16x4::from_array([0.9, 1.001, 2.0, -4.5])), + f16x4::from_array([1.0, 1.0, 2.0, -5.0]) + ); + assert_eq!( + simd_round_ties_even(f16x4::from_array([0.9, 1.001, 2.0, -4.5])), + f16x4::from_array([1.0, 1.0, 2.0, -4.0]) + ); + assert_eq!( + simd_trunc(f16x4::from_array([0.9, 1.001, 2.0, -4.5])), + f16x4::from_array([0.0, 1.0, 2.0, -4.0]) + ); + } + assert_eq!( f32x4::from_array([0.9, 1.001, 2.0, -4.5]).ceil(), f32x4::from_array([1.0, 2.0, 2.0, -4.0]) @@ -604,6 +835,31 @@ fn simd_round() { f64x4::from_array([0.9, 1.001, 2.0, -4.5]).trunc(), f64x4::from_array([0.0, 1.0, 2.0, -4.0]) ); + + unsafe { + use intrinsics::*; + + assert_eq!( + simd_ceil(f128x4::from_array([0.9, 1.001, 2.0, -4.5])), + f128x4::from_array([1.0, 2.0, 2.0, -4.0]) + ); + assert_eq!( + simd_floor(f128x4::from_array([0.9, 1.001, 2.0, -4.5])), + f128x4::from_array([0.0, 1.0, 2.0, -5.0]) + ); + assert_eq!( + simd_round(f128x4::from_array([0.9, 1.001, 2.0, -4.5])), + f128x4::from_array([1.0, 1.0, 2.0, -5.0]) + ); + assert_eq!( + simd_round_ties_even(f128x4::from_array([0.9, 1.001, 2.0, -4.5])), + f128x4::from_array([1.0, 1.0, 2.0, -4.0]) + ); + assert_eq!( + simd_trunc(f128x4::from_array([0.9, 1.001, 2.0, -4.5])), + f128x4::from_array([0.0, 1.0, 2.0, -4.0]) + ); + } } fn simd_intrinsics() { @@ -724,8 +980,10 @@ fn simd_ops_non_pow2() { fn main() { simd_mask(); + simd_ops_f16(); simd_ops_f32(); simd_ops_f64(); + simd_ops_f128(); simd_ops_i32(); simd_ops_non_pow2(); simd_cast(); From accd1fe76c8250a4aadfa9e372f6ba9b8e2e374c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Mi=C4=85sko?= Date: Fri, 24 Oct 2025 15:40:43 +0200 Subject: [PATCH 28/28] rustdoc: Use configured target modifiers when collecting doctests To support loading dependencies with target modifiers and avoid ABI mismatch errors. --- src/librustdoc/doctest.rs | 1 + .../rustdoc-target-modifiers/rmake.rs | 39 +++++++++++++++++++ 2 files changed, 40 insertions(+) diff --git a/src/librustdoc/doctest.rs b/src/librustdoc/doctest.rs index 51f79047f9914..e6b7a6bfcfb6b 100644 --- a/src/librustdoc/doctest.rs +++ b/src/librustdoc/doctest.rs @@ -166,6 +166,7 @@ pub(crate) fn run(dcx: DiagCtxtHandle<'_>, input: Input, options: RustdocOptions remap_path_prefix: options.remap_path_prefix.clone(), unstable_opts: options.unstable_opts.clone(), error_format: options.error_format.clone(), + target_modifiers: options.target_modifiers.clone(), ..config::Options::default() }; diff --git a/tests/run-make/rustdoc-target-modifiers/rmake.rs b/tests/run-make/rustdoc-target-modifiers/rmake.rs index ee522501fd286..ffe87f3f7650e 100644 --- a/tests/run-make/rustdoc-target-modifiers/rmake.rs +++ b/tests/run-make/rustdoc-target-modifiers/rmake.rs @@ -25,4 +25,43 @@ fn main() { .target("aarch64-unknown-none-softfloat") .arg("-Zfixed-x18") .run(); + + rustdoc() + .input("c.rs") + .crate_type("rlib") + .extern_("d", "libd.rmeta") + .target("aarch64-unknown-none-softfloat") + .arg("-Zfixed-x18") + .arg("--test") + .run(); + + rustdoc() + .input("c.rs") + .edition("2024") + .crate_type("rlib") + .extern_("d", "libd.rmeta") + .target("aarch64-unknown-none-softfloat") + .arg("-Zfixed-x18") + .arg("--test") + .run(); + + // rustdoc --test detects ABI mismatch + rustdoc() + .input("c.rs") + .crate_type("rlib") + .extern_("d", "libd.rmeta") + .target("aarch64-unknown-none-softfloat") + .arg("--test") + .run_fail() + .assert_stderr_contains("mixing `-Zfixed-x18` will cause an ABI mismatch"); + + // rustdoc --test -Cunsafe-allow-abi-mismatch=... ignores the mismatch + rustdoc() + .input("c.rs") + .crate_type("rlib") + .extern_("d", "libd.rmeta") + .target("aarch64-unknown-none-softfloat") + .arg("--test") + .arg("-Cunsafe-allow-abi-mismatch=fixed-x18") + .run(); }