From 686ddd3e2e4df0ee4b0dc332f69aff58a24a0de0 Mon Sep 17 00:00:00 2001 From: Martin Nordholts Date: Mon, 16 Mar 2026 06:12:15 +0100 Subject: [PATCH 1/9] tests/debuginfo/basic-stepping.rs: Explain why all lines are not steppable Some optimization passes _improve_ compile times [1]. So we want to run some passes even with `-Copt-level=0`. That means that some of the lines in the test can be optimized away. To make regression testing more robust, we also want to run the test with such passes disabled. The solution is to use two revisions. One with default `-Copt-level=0` passes, and one "even less optimized", with enough optimization passes disabled to keep the maximum number of lines steppable. [1]: https://github.com/rust-lang/compiler-team/issues/319 --- tests/debuginfo/basic-stepping.rs | 59 ++++++++++++++++++------------- tests/debuginfo/macro-stepping.rs | 8 ++--- 2 files changed, 39 insertions(+), 28 deletions(-) diff --git a/tests/debuginfo/basic-stepping.rs b/tests/debuginfo/basic-stepping.rs index 238ab12c186ef..a4410c70ba38a 100644 --- a/tests/debuginfo/basic-stepping.rs +++ b/tests/debuginfo/basic-stepping.rs @@ -9,9 +9,20 @@ // Debugger tests need debuginfo //@ compile-flags: -g -// FIXME(#128945): SingleUseConsts shouldn't need to be disabled. -//@ revisions: default-mir-passes no-SingleUseConsts-mir-pass -//@ [no-SingleUseConsts-mir-pass] compile-flags: -Zmir-enable-passes=-SingleUseConsts +// Some optimization passes _improve_ compile times [1]. So we want to run some +// passes even with `-Copt-level=0`. That means that some of the lines below can +// be optimized away. To make regression testing more robust, we also want to +// run this test with such passes disabled. The solution is to use two +// revisions. One with default `-Copt-level=0` passes, and one "even less +// optimized", with enough optimization passes disabled to keep the maximum +// number of lines steppable. +// +// If `-Zmir-enable-passes=-...` ends up being annoying to maintain, we can try +// switching to `-Zmir-opt-level=0` instead. +// +// [1]: https://github.com/rust-lang/compiler-team/issues/319 +//@ revisions: opt-level-0 maximally-steppable +//@ [maximally-steppable] compile-flags: -Zmir-enable-passes=-SingleUseConsts // === GDB TESTS =================================================================================== @@ -20,12 +31,12 @@ //@ gdb-command: next //@ gdb-check: let d = c = 99; //@ gdb-command: next -//@ [no-SingleUseConsts-mir-pass] gdb-check: let e = "hi bob"; -//@ [no-SingleUseConsts-mir-pass] gdb-command: next -//@ [no-SingleUseConsts-mir-pass] gdb-check: let f = b"hi bob"; -//@ [no-SingleUseConsts-mir-pass] gdb-command: next -//@ [no-SingleUseConsts-mir-pass] gdb-check: let g = b'9'; -//@ [no-SingleUseConsts-mir-pass] gdb-command: next +//@ [maximally-steppable] gdb-check: let e = "hi bob"; +//@ [maximally-steppable] gdb-command: next +//@ [maximally-steppable] gdb-check: let f = b"hi bob"; +//@ [maximally-steppable] gdb-command: next +//@ [maximally-steppable] gdb-check: let g = b'9'; +//@ [maximally-steppable] gdb-command: next //@ gdb-check: let h = ["whatever"; 8]; //@ gdb-command: next //@ gdb-check: let i = [1,2,3,4]; @@ -61,15 +72,15 @@ //@ lldb-check: [...]let d = c = 99;[...] //@ lldb-command: next //@ lldb-command: frame select -//@ [no-SingleUseConsts-mir-pass] lldb-check: [...]let e = "hi bob";[...] -//@ [no-SingleUseConsts-mir-pass] lldb-command: next -//@ [no-SingleUseConsts-mir-pass] lldb-command: frame select -//@ [no-SingleUseConsts-mir-pass] lldb-check: [...]let f = b"hi bob";[...] -//@ [no-SingleUseConsts-mir-pass] lldb-command: next -//@ [no-SingleUseConsts-mir-pass] lldb-command: frame select -//@ [no-SingleUseConsts-mir-pass] lldb-check: [...]let g = b'9';[...] -//@ [no-SingleUseConsts-mir-pass] lldb-command: next -//@ [no-SingleUseConsts-mir-pass] lldb-command: frame select +//@ [maximally-steppable] lldb-check: [...]let e = "hi bob";[...] +//@ [maximally-steppable] lldb-command: next +//@ [maximally-steppable] lldb-command: frame select +//@ [maximally-steppable] lldb-check: [...]let f = b"hi bob";[...] +//@ [maximally-steppable] lldb-command: next +//@ [maximally-steppable] lldb-command: frame select +//@ [maximally-steppable] lldb-check: [...]let g = b'9';[...] +//@ [maximally-steppable] lldb-command: next +//@ [maximally-steppable] lldb-command: frame select //@ lldb-check: [...]let h = ["whatever"; 8];[...] //@ lldb-command: next //@ lldb-command: frame select @@ -107,12 +118,12 @@ //@ cdb-check: [...]: let mut c = 27; //@ cdb-command: p //@ cdb-check: [...]: let d = c = 99; -//@ [no-SingleUseConsts-mir-pass] cdb-command: p -//@ [no-SingleUseConsts-mir-pass] cdb-check: [...]: let e = "hi bob"; -//@ [no-SingleUseConsts-mir-pass] cdb-command: p -//@ [no-SingleUseConsts-mir-pass] cdb-check: [...]: let f = b"hi bob"; -//@ [no-SingleUseConsts-mir-pass] cdb-command: p -//@ [no-SingleUseConsts-mir-pass] cdb-check: [...]: let g = b'9'; +//@ [maximally-steppable] cdb-command: p +//@ [maximally-steppable] cdb-check: [...]: let e = "hi bob"; +//@ [maximally-steppable] cdb-command: p +//@ [maximally-steppable] cdb-check: [...]: let f = b"hi bob"; +//@ [maximally-steppable] cdb-command: p +//@ [maximally-steppable] cdb-check: [...]: let g = b'9'; //@ cdb-command: p //@ cdb-check: [...]: let h = ["whatever"; 8]; //@ cdb-command: p diff --git a/tests/debuginfo/macro-stepping.rs b/tests/debuginfo/macro-stepping.rs index 3f57eb9ad79bd..c2f6183e3c7b0 100644 --- a/tests/debuginfo/macro-stepping.rs +++ b/tests/debuginfo/macro-stepping.rs @@ -16,9 +16,9 @@ extern crate macro_stepping; // exports new_scope!() //@ compile-flags: -g -// FIXME(#128945): SingleUseConsts shouldn't need to be disabled. -//@ revisions: default-mir-passes no-SingleUseConsts-mir-pass -//@ [no-SingleUseConsts-mir-pass] compile-flags: -Zmir-enable-passes=-SingleUseConsts +// See explanation in `tests/debuginfo/basic-stepping.rs`. +//@ revisions: opt-level-0 maximally-steppable +//@ [maximally-steppable] compile-flags: -Zmir-enable-passes=-SingleUseConsts // === GDB TESTS =================================================================================== @@ -51,7 +51,7 @@ extern crate macro_stepping; // exports new_scope!() //@ gdb-check:[...]#inc-loc2[...] //@ gdb-command:next //@ gdb-command:frame -//@ [no-SingleUseConsts-mir-pass] gdb-check:[...]#inc-loc3[...] +//@ [maximally-steppable] gdb-check:[...]#inc-loc3[...] // === LLDB TESTS ================================================================================== From aa6150b6803ef999b95ea40b3b6776f96acef569 Mon Sep 17 00:00:00 2001 From: Daria Sukhonina Date: Tue, 7 Apr 2026 13:51:25 +0300 Subject: [PATCH 2/9] Refactor arena_cache query values --- compiler/rustc_middle/src/queries.rs | 4 ++-- compiler/rustc_middle/src/query/arena_cached.rs | 16 ++++++++++++++++ 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_middle/src/queries.rs b/compiler/rustc_middle/src/queries.rs index 1017ccffb0b2a..d3b1345e93701 100644 --- a/compiler/rustc_middle/src/queries.rs +++ b/compiler/rustc_middle/src/queries.rs @@ -1202,7 +1202,7 @@ rustc_queries! { /// Return the live symbols in the crate for dead code check. /// /// The second return value maps from ADTs to ignored derived traits (e.g. Debug and Clone). - query live_symbols_and_ignored_derived_traits(_: ()) -> &'tcx Result<( + query live_symbols_and_ignored_derived_traits(_: ()) -> Result<&'tcx ( LocalDefIdSet, LocalDefIdMap>, ), ErrorGuaranteed> { @@ -1292,7 +1292,7 @@ rustc_queries! { /// Return the set of (transitive) callees that may result in a recursive call to `key`, /// if we were able to walk all callees. - query mir_callgraph_cyclic(key: LocalDefId) -> &'tcx Option> { + query mir_callgraph_cyclic(key: LocalDefId) -> Option<&'tcx UnordSet> { arena_cache desc { "computing (transitive) callees of `{}` that may recurse", diff --git a/compiler/rustc_middle/src/query/arena_cached.rs b/compiler/rustc_middle/src/query/arena_cached.rs index 7c7ad12622604..4ab2fbe914965 100644 --- a/compiler/rustc_middle/src/query/arena_cached.rs +++ b/compiler/rustc_middle/src/query/arena_cached.rs @@ -1,6 +1,7 @@ use std::mem; use rustc_arena::TypedArena; +use rustc_span::ErrorGuaranteed; use crate::ty::TyCtxt; @@ -51,6 +52,21 @@ impl<'tcx, T> ArenaCached<'tcx> for Option<&'tcx T> { } } +impl<'tcx, T> ArenaCached<'tcx> for Result<&'tcx T, ErrorGuaranteed> { + type Provided = Result; + /// The provide value is `Result`, but we only store `T` in the arena. + type Allocated = T; + + fn alloc_in_arena( + tcx: TyCtxt<'tcx>, + typed_arena: &'tcx TypedArena, + value: Result, + ) -> Self { + // Don't store Err(ErrorGuaranteed) in the arena, and wrap the allocated reference in Ok. + try { do_alloc(tcx, typed_arena, value?) } + } +} + /// Allocates a value in either its dedicated arena, or in the common dropless /// arena, depending on whether it needs to be dropped. fn do_alloc<'tcx, T>(tcx: TyCtxt<'tcx>, typed_arena: &'tcx TypedArena, value: T) -> &'tcx T { From aa1a5f8a93df2afce47eb91a52f40bf0b5f0b524 Mon Sep 17 00:00:00 2001 From: sayantn Date: Thu, 19 Feb 2026 02:04:18 +0530 Subject: [PATCH 3/9] Codegen non-overloaded LLVM intrinsics using their name --- compiler/rustc_codegen_llvm/src/errors.rs | 12 +++ compiler/rustc_codegen_llvm/src/intrinsic.rs | 77 +++++++++++++++---- .../rustc_codegen_llvm/src/llvm/enzyme_ffi.rs | 1 - compiler/rustc_codegen_llvm/src/llvm/ffi.rs | 3 + compiler/rustc_codegen_llvm/src/llvm/mod.rs | 4 + compiler/rustc_codegen_llvm/src/type_.rs | 8 ++ .../incorrect-llvm-intrinsic-signature.rs | 14 ++++ .../incorrect-llvm-intrinsic-signature.stderr | 8 ++ 8 files changed, 109 insertions(+), 18 deletions(-) create mode 100644 tests/ui/codegen/incorrect-llvm-intrinsic-signature.rs create mode 100644 tests/ui/codegen/incorrect-llvm-intrinsic-signature.stderr diff --git a/compiler/rustc_codegen_llvm/src/errors.rs b/compiler/rustc_codegen_llvm/src/errors.rs index caec20db4c2db..b679eccfc687e 100644 --- a/compiler/rustc_codegen_llvm/src/errors.rs +++ b/compiler/rustc_codegen_llvm/src/errors.rs @@ -211,3 +211,15 @@ pub(crate) struct FixedX18InvalidArch<'a> { "enabling both `-Zpacked-stack` and the `backchain` target feature is incompatible with the default s390x ABI. Switch to s390x-unknown-none-softfloat if you need both attributes" )] pub(crate) struct PackedStackBackchainNeedsSoftfloat; + +#[derive(Diagnostic)] +#[diag( + "intrinsic signature mismatch for `{$name}`: expected signature `{$llvm_fn_ty}`, found `{$rust_fn_ty}`" +)] +pub(crate) struct IntrinsicSignatureMismatch<'a> { + pub name: &'a str, + pub llvm_fn_ty: &'a str, + pub rust_fn_ty: &'a str, + #[primary_span] + pub span: Span, +} diff --git a/compiler/rustc_codegen_llvm/src/intrinsic.rs b/compiler/rustc_codegen_llvm/src/intrinsic.rs index 3e600914d6f42..b0550f4786693 100644 --- a/compiler/rustc_codegen_llvm/src/intrinsic.rs +++ b/compiler/rustc_codegen_llvm/src/intrinsic.rs @@ -1,6 +1,6 @@ use std::cmp::Ordering; use std::ffi::c_uint; -use std::{assert_matches, ptr}; +use std::{assert_matches, iter, ptr}; use rustc_abi::{ Align, BackendRepr, ExternAbi, Float, HasDataLayout, NumScalableVectors, Primitive, Size, @@ -36,7 +36,8 @@ use crate::builder::gpu_offload::{ use crate::context::CodegenCx; use crate::declare::declare_raw_fn; use crate::errors::{ - AutoDiffWithoutEnable, AutoDiffWithoutLto, OffloadWithoutEnable, OffloadWithoutFatLTO, + AutoDiffWithoutEnable, AutoDiffWithoutLto, IntrinsicSignatureMismatch, OffloadWithoutEnable, + OffloadWithoutFatLTO, }; use crate::llvm::{self, Type, Value}; use crate::type_of::LayoutLlvmExt; @@ -847,35 +848,22 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { llargument_tys.push(arg_layout.immediate_llvm_type(self)); } - let fn_ty = self.type_func(&llargument_tys, llreturn_ty); - let fn_ptr = if let Some(&llfn) = self.intrinsic_instances.borrow().get(&instance) { llfn } else { let sym = tcx.symbol_name(instance).name; - // FIXME use get_intrinsic let llfn = if let Some(llfn) = self.get_declared_value(sym) { llfn } else { - // Function addresses in Rust are never significant, allowing functions to - // be merged. - let llfn = declare_raw_fn( - self, - sym, - llvm::CCallConv, - llvm::UnnamedAddr::Global, - llvm::Visibility::Default, - fn_ty, - ); - - llfn + intrinsic_fn(self, sym, llreturn_ty, llargument_tys, instance) }; self.intrinsic_instances.borrow_mut().insert(instance, llfn); llfn }; + let fn_ty = self.get_type_of_global(fn_ptr); let mut llargs = vec![]; @@ -976,6 +964,61 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { } } +fn intrinsic_fn<'ll, 'tcx>( + bx: &Builder<'_, 'll, 'tcx>, + name: &str, + rust_return_ty: &'ll Type, + rust_argument_tys: Vec<&'ll Type>, + instance: ty::Instance<'tcx>, +) -> &'ll Value { + let tcx = bx.tcx; + + let rust_fn_ty = bx.type_func(&rust_argument_tys, rust_return_ty); + + let intrinsic = llvm::Intrinsic::lookup(name.as_bytes()); + + if let Some(intrinsic) = intrinsic + && !intrinsic.is_overloaded() + { + // FIXME: also do this for overloaded intrinsics + let llfn = intrinsic.get_declaration(bx.llmod, &[]); + let llvm_fn_ty = bx.get_type_of_global(llfn); + + let llvm_return_ty = bx.get_return_type(llvm_fn_ty); + let llvm_argument_tys = bx.func_params_types(llvm_fn_ty); + let llvm_is_variadic = bx.func_is_variadic(llvm_fn_ty); + + let is_correct_signature = !llvm_is_variadic + && rust_argument_tys.len() == llvm_argument_tys.len() + && iter::once((rust_return_ty, llvm_return_ty)) + .chain(iter::zip(rust_argument_tys, llvm_argument_tys)) + .all(|(rust_ty, llvm_ty)| rust_ty == llvm_ty); + + if !is_correct_signature { + tcx.dcx().emit_fatal(IntrinsicSignatureMismatch { + name, + llvm_fn_ty: &format!("{llvm_fn_ty:?}"), + rust_fn_ty: &format!("{rust_fn_ty:?}"), + span: tcx.def_span(instance.def_id()), + }); + } + + return llfn; + } + + // Function addresses in Rust are never significant, allowing functions to be merged. + let llfn = declare_raw_fn( + bx, + name, + llvm::CCallConv, + llvm::UnnamedAddr::Global, + llvm::Visibility::Default, + rust_fn_ty, + ); + + llfn +} + fn catch_unwind_intrinsic<'ll, 'tcx>( bx: &mut Builder<'_, 'll, 'tcx>, try_func: &'ll Value, diff --git a/compiler/rustc_codegen_llvm/src/llvm/enzyme_ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/enzyme_ffi.rs index 67fbc0f53adc9..195e050a9b651 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/enzyme_ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/enzyme_ffi.rs @@ -73,7 +73,6 @@ unsafe extern "C" { pub(crate) fn LLVMDumpModule(M: &Module); pub(crate) fn LLVMDumpValue(V: &Value); pub(crate) fn LLVMGetFunctionCallConv(F: &Value) -> c_uint; - pub(crate) fn LLVMGetReturnType(T: &Type) -> &Type; pub(crate) fn LLVMGetParams(Fnc: &Value, params: *mut &Value); pub(crate) fn LLVMGetNamedFunction(M: &Module, Name: *const c_char) -> Option<&Value>; } diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index bc24f1692fcf2..7855afeced47d 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -930,6 +930,8 @@ unsafe extern "C" { ) -> &'a Type; pub(crate) fn LLVMCountParamTypes(FunctionTy: &Type) -> c_uint; pub(crate) fn LLVMGetParamTypes<'a>(FunctionTy: &'a Type, Dest: *mut &'a Type); + pub(crate) fn LLVMGetReturnType(FunctionTy: &Type) -> &Type; + pub(crate) fn LLVMIsFunctionVarArg(FunctionTy: &Type) -> Bool; // Operations on struct types pub(crate) fn LLVMStructTypeInContext<'a>( @@ -1084,6 +1086,7 @@ unsafe extern "C" { // Operations about llvm intrinsics pub(crate) fn LLVMLookupIntrinsicID(Name: *const c_char, NameLen: size_t) -> c_uint; + pub(crate) fn LLVMIntrinsicIsOverloaded(ID: NonZero) -> Bool; pub(crate) fn LLVMGetIntrinsicDeclaration<'a>( Mod: &'a Module, ID: NonZero, diff --git a/compiler/rustc_codegen_llvm/src/llvm/mod.rs b/compiler/rustc_codegen_llvm/src/llvm/mod.rs index 2871326b28b5a..84d7e8165fe03 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/mod.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/mod.rs @@ -323,6 +323,10 @@ impl Intrinsic { NonZero::new(id).map(|id| Self { id }) } + pub(crate) fn is_overloaded(self) -> bool { + unsafe { LLVMIntrinsicIsOverloaded(self.id).is_true() } + } + pub(crate) fn get_declaration<'ll>( self, llmod: &'ll Module, diff --git a/compiler/rustc_codegen_llvm/src/type_.rs b/compiler/rustc_codegen_llvm/src/type_.rs index 2026b06d104df..b8cee3510789c 100644 --- a/compiler/rustc_codegen_llvm/src/type_.rs +++ b/compiler/rustc_codegen_llvm/src/type_.rs @@ -77,6 +77,10 @@ impl<'ll, CX: Borrow>> GenericCx<'ll, CX> { unsafe { llvm::LLVMAddFunction(self.llmod(), name.as_ptr(), ty) } } + pub(crate) fn get_return_type(&self, ty: &'ll Type) -> &'ll Type { + unsafe { llvm::LLVMGetReturnType(ty) } + } + pub(crate) fn func_params_types(&self, ty: &'ll Type) -> Vec<&'ll Type> { unsafe { let n_args = llvm::LLVMCountParamTypes(ty) as usize; @@ -86,6 +90,10 @@ impl<'ll, CX: Borrow>> GenericCx<'ll, CX> { args } } + + pub(crate) fn func_is_variadic(&self, ty: &'ll Type) -> bool { + unsafe { llvm::LLVMIsFunctionVarArg(ty).is_true() } + } } impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> { pub(crate) fn type_bool(&self) -> &'ll Type { diff --git a/tests/ui/codegen/incorrect-llvm-intrinsic-signature.rs b/tests/ui/codegen/incorrect-llvm-intrinsic-signature.rs new file mode 100644 index 0000000000000..4b86f37f922a6 --- /dev/null +++ b/tests/ui/codegen/incorrect-llvm-intrinsic-signature.rs @@ -0,0 +1,14 @@ +//@ build-fail +//@ ignore-backends: gcc + +#![feature(link_llvm_intrinsics, abi_unadjusted)] + +extern "unadjusted" { + #[link_name = "llvm.assume"] + fn foo(); + //~^ ERROR: intrinsic signature mismatch for `llvm.assume`: expected signature `void (i1)`, found `void ()` +} + +pub fn main() { + unsafe { foo() } +} diff --git a/tests/ui/codegen/incorrect-llvm-intrinsic-signature.stderr b/tests/ui/codegen/incorrect-llvm-intrinsic-signature.stderr new file mode 100644 index 0000000000000..4e58e5ebdc731 --- /dev/null +++ b/tests/ui/codegen/incorrect-llvm-intrinsic-signature.stderr @@ -0,0 +1,8 @@ +error: intrinsic signature mismatch for `llvm.assume`: expected signature `void (i1)`, found `void ()` + --> $DIR/incorrect-llvm-intrinsic-signature.rs:8:5 + | +LL | fn foo(); + | ^^^^^^^^^ + +error: aborting due to 1 previous error + From 6733ce938fa5e74730462c0956a4cfabb7d085bc Mon Sep 17 00:00:00 2001 From: Ayuse Date: Sun, 12 Apr 2026 16:17:44 +0100 Subject: [PATCH 4/9] Add --verbose-run-make-subprocess-output flag to suppress run-make output Add a flag to control verbose subprocess output for run-make tests. When using --no-capture on panic=abort test suites like cg_clif, passing test output can flood the terminal. This flag (default true) lets users opt out via --verbose-run-make-subprocess-output=false. Extract a private print_command_output helper in run-make-support to avoid inlining the output logic in handle_failed_output, ensuring failures always print regardless of the flag. --- src/bootstrap/src/core/build_steps/test.rs | 4 ++++ src/bootstrap/src/core/config/flags.rs | 13 +++++++++++++ src/bootstrap/src/utils/change_tracker.rs | 5 +++++ src/doc/rustc-dev-guide/src/tests/compiletest.md | 9 +++++++++ src/etc/completions/x.fish | 2 ++ src/etc/completions/x.ps1 | 2 ++ src/etc/completions/x.py.fish | 2 ++ src/etc/completions/x.py.ps1 | 2 ++ src/etc/completions/x.py.sh | 12 ++++++++++-- src/etc/completions/x.py.zsh | 2 ++ src/etc/completions/x.sh | 12 ++++++++++-- src/etc/completions/x.zsh | 2 ++ src/tools/compiletest/src/common.rs | 4 ++++ src/tools/compiletest/src/lib.rs | 7 +++++++ src/tools/compiletest/src/runtest/run_make.rs | 7 +++++++ src/tools/compiletest/src/rustdoc_gui_test.rs | 1 + src/tools/run-make-support/src/util.rs | 13 +++++++++++-- 17 files changed, 93 insertions(+), 6 deletions(-) diff --git a/src/bootstrap/src/core/build_steps/test.rs b/src/bootstrap/src/core/build_steps/test.rs index c222aa2305647..991592ec522d0 100644 --- a/src/bootstrap/src/core/build_steps/test.rs +++ b/src/bootstrap/src/core/build_steps/test.rs @@ -2334,6 +2334,10 @@ Please disable assertions with `rust.debug-assertions = false`. cmd.arg("--verbose"); } + if builder.config.cmd.verbose_run_make_subprocess_output() { + cmd.arg("--verbose-run-make-subprocess-output"); + } + if builder.config.rustc_debug_assertions { cmd.arg("--with-rustc-debug-assertions"); } diff --git a/src/bootstrap/src/core/config/flags.rs b/src/bootstrap/src/core/config/flags.rs index 2f1e234d6ebc0..e1b8aa9810c3d 100644 --- a/src/bootstrap/src/core/config/flags.rs +++ b/src/bootstrap/src/core/config/flags.rs @@ -422,6 +422,10 @@ pub enum Subcommand { #[arg(long)] /// don't capture stdout/stderr of tests no_capture: bool, + #[arg(long, default_value_t = true, action = clap::ArgAction::Set, default_missing_value = "true", num_args = 0..=1, require_equals = true)] + /// whether to show verbose subprocess output for run-make tests; + /// set to false to suppress output for passing tests (e.g. for cg_clif with --no-capture) + verbose_run_make_subprocess_output: bool, #[arg(long)] /// Use a different codegen backend when running tests. test_codegen_backend: Option, @@ -631,6 +635,15 @@ impl Subcommand { } } + pub fn verbose_run_make_subprocess_output(&self) -> bool { + match *self { + Subcommand::Test { verbose_run_make_subprocess_output, .. } => { + verbose_run_make_subprocess_output + } + _ => true, + } + } + pub fn rustfix_coverage(&self) -> bool { match *self { Subcommand::Test { rustfix_coverage, .. } => rustfix_coverage, diff --git a/src/bootstrap/src/utils/change_tracker.rs b/src/bootstrap/src/utils/change_tracker.rs index 3ae2373e1da21..331403f959b4c 100644 --- a/src/bootstrap/src/utils/change_tracker.rs +++ b/src/bootstrap/src/utils/change_tracker.rs @@ -621,4 +621,9 @@ pub const CONFIG_CHANGE_HISTORY: &[ChangeInfo] = &[ severity: ChangeSeverity::Info, summary: "`x.py` stopped accepting partial argument names. Use full names to avoid errors.", }, + ChangeInfo { + change_id: 154587, + severity: ChangeSeverity::Info, + summary: "New `--verbose-run-make-subprocess-output` flag for `x.py test` (defaults to true). Set `--verbose-run-make-subprocess-output=false` to suppress verbose subprocess output for passing run-make tests when using `--no-capture`.", + }, ]; diff --git a/src/doc/rustc-dev-guide/src/tests/compiletest.md b/src/doc/rustc-dev-guide/src/tests/compiletest.md index 83fcfecc1de7b..e19c7a8d4e44f 100644 --- a/src/doc/rustc-dev-guide/src/tests/compiletest.md +++ b/src/doc/rustc-dev-guide/src/tests/compiletest.md @@ -460,6 +460,15 @@ However, revisions or building auxiliary via directives are not currently suppor `rmake.rs` and `run-make-support` may *not* use any nightly/unstable features, as they must be compilable by a stage 0 rustc that may be a beta or even stable rustc. +By default, run-make tests print each subprocess command and its stdout/stderr. +When running with `--no-capture` on `panic=abort` test suites (such as `cg_clif`), +this can flood the terminal. Omit `--verbose-run-make-subprocess-output` to +suppress this output for passing tests — failing tests always print regardless: + +```bash +./x test tests/run-make --no-capture --verbose-run-make-subprocess-output=false +``` + #### Quickly check if `rmake.rs` tests can be compiled You can quickly check if `rmake.rs` tests can be compiled without having to diff --git a/src/etc/completions/x.fish b/src/etc/completions/x.fish index 92af4f04dcba9..689a13452e1bc 100644 --- a/src/etc/completions/x.fish +++ b/src/etc/completions/x.fish @@ -420,6 +420,7 @@ complete -c x -n "__fish_x_using_subcommand test" -l extra-checks -d 'comma-sepa complete -c x -n "__fish_x_using_subcommand test" -l compare-mode -d 'mode describing what file the actual ui output will be compared to' -r complete -c x -n "__fish_x_using_subcommand test" -l pass -d 'force {check,build,run}-pass tests to this mode' -r complete -c x -n "__fish_x_using_subcommand test" -l run -d 'whether to execute run-* tests' -r +complete -c x -n "__fish_x_using_subcommand test" -l verbose-run-make-subprocess-output -d 'whether to show verbose subprocess output for run-make tests; set to false to suppress output for passing tests (e.g. for cg_clif with --no-capture)' -r -f -a "{true\t'',false\t''}" complete -c x -n "__fish_x_using_subcommand test" -l test-codegen-backend -d 'Use a different codegen backend when running tests' -r complete -c x -n "__fish_x_using_subcommand test" -l config -d 'TOML configuration file for build' -r -F complete -c x -n "__fish_x_using_subcommand test" -l build-dir -d 'Build directory, overrides `build.build-dir` in `bootstrap.toml`' -r -f -a "(__fish_complete_directories)" @@ -473,6 +474,7 @@ complete -c x -n "__fish_x_using_subcommand t" -l extra-checks -d 'comma-separat complete -c x -n "__fish_x_using_subcommand t" -l compare-mode -d 'mode describing what file the actual ui output will be compared to' -r complete -c x -n "__fish_x_using_subcommand t" -l pass -d 'force {check,build,run}-pass tests to this mode' -r complete -c x -n "__fish_x_using_subcommand t" -l run -d 'whether to execute run-* tests' -r +complete -c x -n "__fish_x_using_subcommand t" -l verbose-run-make-subprocess-output -d 'whether to show verbose subprocess output for run-make tests; set to false to suppress output for passing tests (e.g. for cg_clif with --no-capture)' -r -f -a "{true\t'',false\t''}" complete -c x -n "__fish_x_using_subcommand t" -l test-codegen-backend -d 'Use a different codegen backend when running tests' -r complete -c x -n "__fish_x_using_subcommand t" -l config -d 'TOML configuration file for build' -r -F complete -c x -n "__fish_x_using_subcommand t" -l build-dir -d 'Build directory, overrides `build.build-dir` in `bootstrap.toml`' -r -f -a "(__fish_complete_directories)" diff --git a/src/etc/completions/x.ps1 b/src/etc/completions/x.ps1 index d4bf45f738365..e99ef27c2abca 100644 --- a/src/etc/completions/x.ps1 +++ b/src/etc/completions/x.ps1 @@ -487,6 +487,7 @@ Register-ArgumentCompleter -Native -CommandName 'x' -ScriptBlock { [CompletionResult]::new('--compare-mode', '--compare-mode', [CompletionResultType]::ParameterName, 'mode describing what file the actual ui output will be compared to') [CompletionResult]::new('--pass', '--pass', [CompletionResultType]::ParameterName, 'force {check,build,run}-pass tests to this mode') [CompletionResult]::new('--run', '--run', [CompletionResultType]::ParameterName, 'whether to execute run-* tests') + [CompletionResult]::new('--verbose-run-make-subprocess-output', '--verbose-run-make-subprocess-output', [CompletionResultType]::ParameterName, 'whether to show verbose subprocess output for run-make tests; set to false to suppress output for passing tests (e.g. for cg_clif with --no-capture)') [CompletionResult]::new('--test-codegen-backend', '--test-codegen-backend', [CompletionResultType]::ParameterName, 'Use a different codegen backend when running tests') [CompletionResult]::new('--config', '--config', [CompletionResultType]::ParameterName, 'TOML configuration file for build') [CompletionResult]::new('--build-dir', '--build-dir', [CompletionResultType]::ParameterName, 'Build directory, overrides `build.build-dir` in `bootstrap.toml`') @@ -547,6 +548,7 @@ Register-ArgumentCompleter -Native -CommandName 'x' -ScriptBlock { [CompletionResult]::new('--compare-mode', '--compare-mode', [CompletionResultType]::ParameterName, 'mode describing what file the actual ui output will be compared to') [CompletionResult]::new('--pass', '--pass', [CompletionResultType]::ParameterName, 'force {check,build,run}-pass tests to this mode') [CompletionResult]::new('--run', '--run', [CompletionResultType]::ParameterName, 'whether to execute run-* tests') + [CompletionResult]::new('--verbose-run-make-subprocess-output', '--verbose-run-make-subprocess-output', [CompletionResultType]::ParameterName, 'whether to show verbose subprocess output for run-make tests; set to false to suppress output for passing tests (e.g. for cg_clif with --no-capture)') [CompletionResult]::new('--test-codegen-backend', '--test-codegen-backend', [CompletionResultType]::ParameterName, 'Use a different codegen backend when running tests') [CompletionResult]::new('--config', '--config', [CompletionResultType]::ParameterName, 'TOML configuration file for build') [CompletionResult]::new('--build-dir', '--build-dir', [CompletionResultType]::ParameterName, 'Build directory, overrides `build.build-dir` in `bootstrap.toml`') diff --git a/src/etc/completions/x.py.fish b/src/etc/completions/x.py.fish index 0dbbe1ea43efd..a852df8a7753e 100644 --- a/src/etc/completions/x.py.fish +++ b/src/etc/completions/x.py.fish @@ -420,6 +420,7 @@ complete -c x.py -n "__fish_x.py_using_subcommand test" -l extra-checks -d 'comm complete -c x.py -n "__fish_x.py_using_subcommand test" -l compare-mode -d 'mode describing what file the actual ui output will be compared to' -r complete -c x.py -n "__fish_x.py_using_subcommand test" -l pass -d 'force {check,build,run}-pass tests to this mode' -r complete -c x.py -n "__fish_x.py_using_subcommand test" -l run -d 'whether to execute run-* tests' -r +complete -c x.py -n "__fish_x.py_using_subcommand test" -l verbose-run-make-subprocess-output -d 'whether to show verbose subprocess output for run-make tests; set to false to suppress output for passing tests (e.g. for cg_clif with --no-capture)' -r -f -a "{true\t'',false\t''}" complete -c x.py -n "__fish_x.py_using_subcommand test" -l test-codegen-backend -d 'Use a different codegen backend when running tests' -r complete -c x.py -n "__fish_x.py_using_subcommand test" -l config -d 'TOML configuration file for build' -r -F complete -c x.py -n "__fish_x.py_using_subcommand test" -l build-dir -d 'Build directory, overrides `build.build-dir` in `bootstrap.toml`' -r -f -a "(__fish_complete_directories)" @@ -473,6 +474,7 @@ complete -c x.py -n "__fish_x.py_using_subcommand t" -l extra-checks -d 'comma-s complete -c x.py -n "__fish_x.py_using_subcommand t" -l compare-mode -d 'mode describing what file the actual ui output will be compared to' -r complete -c x.py -n "__fish_x.py_using_subcommand t" -l pass -d 'force {check,build,run}-pass tests to this mode' -r complete -c x.py -n "__fish_x.py_using_subcommand t" -l run -d 'whether to execute run-* tests' -r +complete -c x.py -n "__fish_x.py_using_subcommand t" -l verbose-run-make-subprocess-output -d 'whether to show verbose subprocess output for run-make tests; set to false to suppress output for passing tests (e.g. for cg_clif with --no-capture)' -r -f -a "{true\t'',false\t''}" complete -c x.py -n "__fish_x.py_using_subcommand t" -l test-codegen-backend -d 'Use a different codegen backend when running tests' -r complete -c x.py -n "__fish_x.py_using_subcommand t" -l config -d 'TOML configuration file for build' -r -F complete -c x.py -n "__fish_x.py_using_subcommand t" -l build-dir -d 'Build directory, overrides `build.build-dir` in `bootstrap.toml`' -r -f -a "(__fish_complete_directories)" diff --git a/src/etc/completions/x.py.ps1 b/src/etc/completions/x.py.ps1 index b4e3b8c580a49..665cb812f2df9 100644 --- a/src/etc/completions/x.py.ps1 +++ b/src/etc/completions/x.py.ps1 @@ -487,6 +487,7 @@ Register-ArgumentCompleter -Native -CommandName 'x.py' -ScriptBlock { [CompletionResult]::new('--compare-mode', '--compare-mode', [CompletionResultType]::ParameterName, 'mode describing what file the actual ui output will be compared to') [CompletionResult]::new('--pass', '--pass', [CompletionResultType]::ParameterName, 'force {check,build,run}-pass tests to this mode') [CompletionResult]::new('--run', '--run', [CompletionResultType]::ParameterName, 'whether to execute run-* tests') + [CompletionResult]::new('--verbose-run-make-subprocess-output', '--verbose-run-make-subprocess-output', [CompletionResultType]::ParameterName, 'whether to show verbose subprocess output for run-make tests; set to false to suppress output for passing tests (e.g. for cg_clif with --no-capture)') [CompletionResult]::new('--test-codegen-backend', '--test-codegen-backend', [CompletionResultType]::ParameterName, 'Use a different codegen backend when running tests') [CompletionResult]::new('--config', '--config', [CompletionResultType]::ParameterName, 'TOML configuration file for build') [CompletionResult]::new('--build-dir', '--build-dir', [CompletionResultType]::ParameterName, 'Build directory, overrides `build.build-dir` in `bootstrap.toml`') @@ -547,6 +548,7 @@ Register-ArgumentCompleter -Native -CommandName 'x.py' -ScriptBlock { [CompletionResult]::new('--compare-mode', '--compare-mode', [CompletionResultType]::ParameterName, 'mode describing what file the actual ui output will be compared to') [CompletionResult]::new('--pass', '--pass', [CompletionResultType]::ParameterName, 'force {check,build,run}-pass tests to this mode') [CompletionResult]::new('--run', '--run', [CompletionResultType]::ParameterName, 'whether to execute run-* tests') + [CompletionResult]::new('--verbose-run-make-subprocess-output', '--verbose-run-make-subprocess-output', [CompletionResultType]::ParameterName, 'whether to show verbose subprocess output for run-make tests; set to false to suppress output for passing tests (e.g. for cg_clif with --no-capture)') [CompletionResult]::new('--test-codegen-backend', '--test-codegen-backend', [CompletionResultType]::ParameterName, 'Use a different codegen backend when running tests') [CompletionResult]::new('--config', '--config', [CompletionResultType]::ParameterName, 'TOML configuration file for build') [CompletionResult]::new('--build-dir', '--build-dir', [CompletionResultType]::ParameterName, 'Build directory, overrides `build.build-dir` in `bootstrap.toml`') diff --git a/src/etc/completions/x.py.sh b/src/etc/completions/x.py.sh index 8a7aee2a091c7..5e6db9bcb5325 100644 --- a/src/etc/completions/x.py.sh +++ b/src/etc/completions/x.py.sh @@ -4638,7 +4638,7 @@ _x.py() { return 0 ;; x.py__test) - opts="-v -i -j -h --no-fail-fast --test-args --compiletest-rustc-args --all-targets --doc --tests --bless --extra-checks --force-rerun --only-modified --compare-mode --pass --run --rustfix-coverage --no-capture --test-codegen-backend --bypass-ignore-backends --no-doc --verbose --incremental --config --build-dir --build --host --target --exclude --skip --include-default-paths --rustc-error-format --on-fail --dry-run --dump-bootstrap-shims --stage --keep-stage --keep-stage-std --src --jobs --warnings --json-output --compile-time-deps --color --bypass-bootstrap-lock --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --enable-bolt-settings --skip-stage0-validation --reproducible-artifact --set --ci --skip-std-check-if-no-download-rustc --help [PATHS]... [ARGS]..." + opts="-v -i -j -h --no-fail-fast --test-args --compiletest-rustc-args --all-targets --doc --tests --bless --extra-checks --force-rerun --only-modified --compare-mode --pass --run --rustfix-coverage --no-capture --verbose-run-make-subprocess-output --test-codegen-backend --bypass-ignore-backends --no-doc --verbose --incremental --config --build-dir --build --host --target --exclude --skip --include-default-paths --rustc-error-format --on-fail --dry-run --dump-bootstrap-shims --stage --keep-stage --keep-stage-std --src --jobs --warnings --json-output --compile-time-deps --color --bypass-bootstrap-lock --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --enable-bolt-settings --skip-stage0-validation --reproducible-artifact --set --ci --skip-std-check-if-no-download-rustc --help [PATHS]... [ARGS]..." if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) return 0 @@ -4668,6 +4668,10 @@ _x.py() { COMPREPLY=($(compgen -f "${cur}")) return 0 ;; + --verbose-run-make-subprocess-output) + COMPREPLY=($(compgen -W "true false" -- "${cur}")) + return 0 + ;; --test-codegen-backend) COMPREPLY=($(compgen -f "${cur}")) return 0 @@ -4852,7 +4856,7 @@ _x.py() { return 0 ;; x.py__test) - opts="-v -i -j -h --no-fail-fast --test-args --compiletest-rustc-args --all-targets --doc --tests --bless --extra-checks --force-rerun --only-modified --compare-mode --pass --run --rustfix-coverage --no-capture --test-codegen-backend --bypass-ignore-backends --no-doc --verbose --incremental --config --build-dir --build --host --target --exclude --skip --include-default-paths --rustc-error-format --on-fail --dry-run --dump-bootstrap-shims --stage --keep-stage --keep-stage-std --src --jobs --warnings --json-output --compile-time-deps --color --bypass-bootstrap-lock --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --enable-bolt-settings --skip-stage0-validation --reproducible-artifact --set --ci --skip-std-check-if-no-download-rustc --help [PATHS]... [ARGS]..." + opts="-v -i -j -h --no-fail-fast --test-args --compiletest-rustc-args --all-targets --doc --tests --bless --extra-checks --force-rerun --only-modified --compare-mode --pass --run --rustfix-coverage --no-capture --verbose-run-make-subprocess-output --test-codegen-backend --bypass-ignore-backends --no-doc --verbose --incremental --config --build-dir --build --host --target --exclude --skip --include-default-paths --rustc-error-format --on-fail --dry-run --dump-bootstrap-shims --stage --keep-stage --keep-stage-std --src --jobs --warnings --json-output --compile-time-deps --color --bypass-bootstrap-lock --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --enable-bolt-settings --skip-stage0-validation --reproducible-artifact --set --ci --skip-std-check-if-no-download-rustc --help [PATHS]... [ARGS]..." if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) return 0 @@ -4882,6 +4886,10 @@ _x.py() { COMPREPLY=($(compgen -f "${cur}")) return 0 ;; + --verbose-run-make-subprocess-output) + COMPREPLY=($(compgen -W "true false" -- "${cur}")) + return 0 + ;; --test-codegen-backend) COMPREPLY=($(compgen -f "${cur}")) return 0 diff --git a/src/etc/completions/x.py.zsh b/src/etc/completions/x.py.zsh index e24da218a05d8..1f8701c297baa 100644 --- a/src/etc/completions/x.py.zsh +++ b/src/etc/completions/x.py.zsh @@ -488,6 +488,7 @@ _arguments "${_arguments_options[@]}" : \ '--compare-mode=[mode describing what file the actual ui output will be compared to]:COMPARE MODE:_default' \ '--pass=[force {check,build,run}-pass tests to this mode]:check | build | run:_default' \ '--run=[whether to execute run-* tests]:auto | always | never:_default' \ +'--verbose-run-make-subprocess-output=[whether to show verbose subprocess output for run-make tests; set to false to suppress output for passing tests (e.g. for cg_clif with --no-capture)]' \ '--test-codegen-backend=[Use a different codegen backend when running tests]:TEST_CODEGEN_BACKEND:_default' \ '--config=[TOML configuration file for build]:FILE:_files' \ '--build-dir=[Build directory, overrides \`build.build-dir\` in \`bootstrap.toml\`]:DIR:_files -/' \ @@ -550,6 +551,7 @@ _arguments "${_arguments_options[@]}" : \ '--compare-mode=[mode describing what file the actual ui output will be compared to]:COMPARE MODE:_default' \ '--pass=[force {check,build,run}-pass tests to this mode]:check | build | run:_default' \ '--run=[whether to execute run-* tests]:auto | always | never:_default' \ +'--verbose-run-make-subprocess-output=[whether to show verbose subprocess output for run-make tests; set to false to suppress output for passing tests (e.g. for cg_clif with --no-capture)]' \ '--test-codegen-backend=[Use a different codegen backend when running tests]:TEST_CODEGEN_BACKEND:_default' \ '--config=[TOML configuration file for build]:FILE:_files' \ '--build-dir=[Build directory, overrides \`build.build-dir\` in \`bootstrap.toml\`]:DIR:_files -/' \ diff --git a/src/etc/completions/x.sh b/src/etc/completions/x.sh index a4b44b73f47af..6314fe1307dcb 100644 --- a/src/etc/completions/x.sh +++ b/src/etc/completions/x.sh @@ -4638,7 +4638,7 @@ _x() { return 0 ;; x__test) - opts="-v -i -j -h --no-fail-fast --test-args --compiletest-rustc-args --all-targets --doc --tests --bless --extra-checks --force-rerun --only-modified --compare-mode --pass --run --rustfix-coverage --no-capture --test-codegen-backend --bypass-ignore-backends --no-doc --verbose --incremental --config --build-dir --build --host --target --exclude --skip --include-default-paths --rustc-error-format --on-fail --dry-run --dump-bootstrap-shims --stage --keep-stage --keep-stage-std --src --jobs --warnings --json-output --compile-time-deps --color --bypass-bootstrap-lock --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --enable-bolt-settings --skip-stage0-validation --reproducible-artifact --set --ci --skip-std-check-if-no-download-rustc --help [PATHS]... [ARGS]..." + opts="-v -i -j -h --no-fail-fast --test-args --compiletest-rustc-args --all-targets --doc --tests --bless --extra-checks --force-rerun --only-modified --compare-mode --pass --run --rustfix-coverage --no-capture --verbose-run-make-subprocess-output --test-codegen-backend --bypass-ignore-backends --no-doc --verbose --incremental --config --build-dir --build --host --target --exclude --skip --include-default-paths --rustc-error-format --on-fail --dry-run --dump-bootstrap-shims --stage --keep-stage --keep-stage-std --src --jobs --warnings --json-output --compile-time-deps --color --bypass-bootstrap-lock --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --enable-bolt-settings --skip-stage0-validation --reproducible-artifact --set --ci --skip-std-check-if-no-download-rustc --help [PATHS]... [ARGS]..." if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) return 0 @@ -4668,6 +4668,10 @@ _x() { COMPREPLY=($(compgen -f "${cur}")) return 0 ;; + --verbose-run-make-subprocess-output) + COMPREPLY=($(compgen -W "true false" -- "${cur}")) + return 0 + ;; --test-codegen-backend) COMPREPLY=($(compgen -f "${cur}")) return 0 @@ -4852,7 +4856,7 @@ _x() { return 0 ;; x__test) - opts="-v -i -j -h --no-fail-fast --test-args --compiletest-rustc-args --all-targets --doc --tests --bless --extra-checks --force-rerun --only-modified --compare-mode --pass --run --rustfix-coverage --no-capture --test-codegen-backend --bypass-ignore-backends --no-doc --verbose --incremental --config --build-dir --build --host --target --exclude --skip --include-default-paths --rustc-error-format --on-fail --dry-run --dump-bootstrap-shims --stage --keep-stage --keep-stage-std --src --jobs --warnings --json-output --compile-time-deps --color --bypass-bootstrap-lock --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --enable-bolt-settings --skip-stage0-validation --reproducible-artifact --set --ci --skip-std-check-if-no-download-rustc --help [PATHS]... [ARGS]..." + opts="-v -i -j -h --no-fail-fast --test-args --compiletest-rustc-args --all-targets --doc --tests --bless --extra-checks --force-rerun --only-modified --compare-mode --pass --run --rustfix-coverage --no-capture --verbose-run-make-subprocess-output --test-codegen-backend --bypass-ignore-backends --no-doc --verbose --incremental --config --build-dir --build --host --target --exclude --skip --include-default-paths --rustc-error-format --on-fail --dry-run --dump-bootstrap-shims --stage --keep-stage --keep-stage-std --src --jobs --warnings --json-output --compile-time-deps --color --bypass-bootstrap-lock --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --enable-bolt-settings --skip-stage0-validation --reproducible-artifact --set --ci --skip-std-check-if-no-download-rustc --help [PATHS]... [ARGS]..." if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) return 0 @@ -4882,6 +4886,10 @@ _x() { COMPREPLY=($(compgen -f "${cur}")) return 0 ;; + --verbose-run-make-subprocess-output) + COMPREPLY=($(compgen -W "true false" -- "${cur}")) + return 0 + ;; --test-codegen-backend) COMPREPLY=($(compgen -f "${cur}")) return 0 diff --git a/src/etc/completions/x.zsh b/src/etc/completions/x.zsh index 7f43781684b4b..12f441012e60f 100644 --- a/src/etc/completions/x.zsh +++ b/src/etc/completions/x.zsh @@ -488,6 +488,7 @@ _arguments "${_arguments_options[@]}" : \ '--compare-mode=[mode describing what file the actual ui output will be compared to]:COMPARE MODE:_default' \ '--pass=[force {check,build,run}-pass tests to this mode]:check | build | run:_default' \ '--run=[whether to execute run-* tests]:auto | always | never:_default' \ +'--verbose-run-make-subprocess-output=[whether to show verbose subprocess output for run-make tests; set to false to suppress output for passing tests (e.g. for cg_clif with --no-capture)]' \ '--test-codegen-backend=[Use a different codegen backend when running tests]:TEST_CODEGEN_BACKEND:_default' \ '--config=[TOML configuration file for build]:FILE:_files' \ '--build-dir=[Build directory, overrides \`build.build-dir\` in \`bootstrap.toml\`]:DIR:_files -/' \ @@ -550,6 +551,7 @@ _arguments "${_arguments_options[@]}" : \ '--compare-mode=[mode describing what file the actual ui output will be compared to]:COMPARE MODE:_default' \ '--pass=[force {check,build,run}-pass tests to this mode]:check | build | run:_default' \ '--run=[whether to execute run-* tests]:auto | always | never:_default' \ +'--verbose-run-make-subprocess-output=[whether to show verbose subprocess output for run-make tests; set to false to suppress output for passing tests (e.g. for cg_clif with --no-capture)]' \ '--test-codegen-backend=[Use a different codegen backend when running tests]:TEST_CODEGEN_BACKEND:_default' \ '--config=[TOML configuration file for build]:FILE:_files' \ '--build-dir=[Build directory, overrides \`build.build-dir\` in \`bootstrap.toml\`]:DIR:_files -/' \ diff --git a/src/tools/compiletest/src/common.rs b/src/tools/compiletest/src/common.rs index c167580d99d9a..f73d7c96f7e85 100644 --- a/src/tools/compiletest/src/common.rs +++ b/src/tools/compiletest/src/common.rs @@ -603,6 +603,10 @@ pub(crate) struct Config { /// FIXME: this is *way* too coarse; the user can't select *which* info to verbosely dump. pub(crate) verbose: bool, + /// Whether to enable verbose subprocess output for run-make tests. + /// Set to false to suppress output for passing tests (e.g. for cg_clif with --no-capture). + pub verbose_run_make_subprocess_output: bool, + /// Where to find the remote test client process, if we're using it. /// /// Note: this is *only* used for target platform executables created by `run-make` test diff --git a/src/tools/compiletest/src/lib.rs b/src/tools/compiletest/src/lib.rs index a23e3bb9a90e4..dcea79b18e6a8 100644 --- a/src/tools/compiletest/src/lib.rs +++ b/src/tools/compiletest/src/lib.rs @@ -134,6 +134,11 @@ fn parse_config(args: Vec) -> Config { ) .optflag("", "optimize-tests", "run tests with optimizations enabled") .optflag("", "verbose", "run tests verbosely, showing all output") + .optflag( + "", + "verbose-run-make-subprocess-output", + "show verbose subprocess output for successful run-make tests", + ) .optflag( "", "bless", @@ -471,6 +476,8 @@ fn parse_config(args: Vec) -> Config { adb_test_dir, adb_device_status, verbose: matches.opt_present("verbose"), + verbose_run_make_subprocess_output: matches + .opt_present("verbose-run-make-subprocess-output"), only_modified: matches.opt_present("only-modified"), remote_test_client: matches.opt_str("remote-test-client").map(Utf8PathBuf::from), compare_mode, diff --git a/src/tools/compiletest/src/runtest/run_make.rs b/src/tools/compiletest/src/runtest/run_make.rs index ac8846a263c0d..1044683ae6426 100644 --- a/src/tools/compiletest/src/runtest/run_make.rs +++ b/src/tools/compiletest/src/runtest/run_make.rs @@ -231,6 +231,13 @@ impl TestCx<'_> { } // Guard against externally-set env vars. + // Set env var to enable verbose output for successful commands. + // Only set when --verbose-run-make-subprocess-output is passed. + cmd.env_remove("__RMAKE_VERBOSE_SUBPROCESS_OUTPUT"); + if self.config.verbose_run_make_subprocess_output { + cmd.env("__RMAKE_VERBOSE_SUBPROCESS_OUTPUT", "1"); + } + cmd.env_remove("__RUSTC_DEBUG_ASSERTIONS_ENABLED"); if self.config.with_rustc_debug_assertions { // Used for `run_make_support::env::rustc_debug_assertions_enabled`. diff --git a/src/tools/compiletest/src/rustdoc_gui_test.rs b/src/tools/compiletest/src/rustdoc_gui_test.rs index c71fd714aa660..57ce0c5a6d5f7 100644 --- a/src/tools/compiletest/src/rustdoc_gui_test.rs +++ b/src/tools/compiletest/src/rustdoc_gui_test.rs @@ -109,6 +109,7 @@ fn incomplete_config_for_rustdoc_gui_test() -> Config { adb_test_dir: Default::default(), adb_device_status: Default::default(), verbose: Default::default(), + verbose_run_make_subprocess_output: Default::default(), remote_test_client: Default::default(), compare_mode: Default::default(), rustfix_coverage: Default::default(), diff --git a/src/tools/run-make-support/src/util.rs b/src/tools/run-make-support/src/util.rs index f44b3861d11d3..93ec44d58d791 100644 --- a/src/tools/run-make-support/src/util.rs +++ b/src/tools/run-make-support/src/util.rs @@ -4,7 +4,7 @@ use crate::command::{Command, CompletedProcess}; use crate::env::env_var; use crate::path_helpers::cwd; -pub(crate) fn verbose_print_command(cmd: &Command, output: &CompletedProcess) { +fn print_command_output(cmd: &Command, output: &CompletedProcess) { cmd.inspect(|std_cmd| { eprintln!("{std_cmd:?}"); }); @@ -16,6 +16,15 @@ pub(crate) fn verbose_print_command(cmd: &Command, output: &CompletedProcess) { } } +pub(crate) fn verbose_print_command(cmd: &Command, output: &CompletedProcess) { + // Only prints when `--verbose-run-make-subprocess-output` is active (env var set), + // so that passing tests don't flood the terminal when using `--no-capture`. + if std::env::var_os("__RMAKE_VERBOSE_SUBPROCESS_OUTPUT").is_none() { + return; + } + print_command_output(cmd, output); +} + /// If a given [`Command`] failed (as indicated by its [`CompletedProcess`]), verbose print the /// executed command, failure location, output status and stdout/stderr, and abort the process with /// exit code `1`. @@ -29,7 +38,7 @@ pub(crate) fn handle_failed_output( } else { eprintln!("command failed at line {caller_line_number}"); } - verbose_print_command(cmd, &output); + print_command_output(cmd, &output); std::process::exit(1) } From c21f4ee437b28e923de735f4abc6fa1788bf1049 Mon Sep 17 00:00:00 2001 From: sayantn Date: Tue, 25 Nov 2025 13:13:47 +0530 Subject: [PATCH 5/9] Check for AutoUpgraded intrinsics, and lint on uses of deprecated intrinsics --- compiler/rustc_codegen_llvm/src/errors.rs | 8 ++++ compiler/rustc_codegen_llvm/src/intrinsic.rs | 34 +++++++++++++- compiler/rustc_codegen_llvm/src/llvm/ffi.rs | 4 ++ compiler/rustc_lint_defs/src/builtin.rs | 46 +++++++++++++++++++ .../rustc_llvm/llvm-wrapper/RustWrapper.cpp | 10 ++++ tests/run-make/simd-ffi/simd.rs | 2 +- tests/ui/codegen/deprecated-llvm-intrinsic.rs | 28 +++++++++++ .../codegen/deprecated-llvm-intrinsic.stderr | 14 ++++++ tests/ui/codegen/unknown-llvm-intrinsic.rs | 14 ++++++ .../ui/codegen/unknown-llvm-intrinsic.stderr | 8 ++++ 10 files changed, 166 insertions(+), 2 deletions(-) create mode 100644 tests/ui/codegen/deprecated-llvm-intrinsic.rs create mode 100644 tests/ui/codegen/deprecated-llvm-intrinsic.stderr create mode 100644 tests/ui/codegen/unknown-llvm-intrinsic.rs create mode 100644 tests/ui/codegen/unknown-llvm-intrinsic.stderr diff --git a/compiler/rustc_codegen_llvm/src/errors.rs b/compiler/rustc_codegen_llvm/src/errors.rs index b679eccfc687e..259aa20b9e381 100644 --- a/compiler/rustc_codegen_llvm/src/errors.rs +++ b/compiler/rustc_codegen_llvm/src/errors.rs @@ -223,3 +223,11 @@ pub(crate) struct IntrinsicSignatureMismatch<'a> { #[primary_span] pub span: Span, } + +#[derive(Diagnostic)] +#[diag("unknown LLVM intrinsic `{$name}`")] +pub(crate) struct UnknownIntrinsic<'a> { + pub name: &'a str, + #[primary_span] + pub span: Span, +} diff --git a/compiler/rustc_codegen_llvm/src/intrinsic.rs b/compiler/rustc_codegen_llvm/src/intrinsic.rs index b0550f4786693..2655b45d6e940 100644 --- a/compiler/rustc_codegen_llvm/src/intrinsic.rs +++ b/compiler/rustc_codegen_llvm/src/intrinsic.rs @@ -21,6 +21,7 @@ use rustc_middle::ty::offload_meta::OffloadMetadata; use rustc_middle::ty::{self, GenericArgsRef, Instance, SimdAlign, Ty, TyCtxt, TypingEnv}; use rustc_middle::{bug, span_bug}; use rustc_session::config::CrateType; +use rustc_session::lint::builtin::DEPRECATED_LLVM_INTRINSIC; use rustc_span::{Span, Symbol, sym}; use rustc_symbol_mangling::{mangle_internal_symbol, symbol_name_for_instance_in_crate}; use rustc_target::callconv::PassMode; @@ -37,7 +38,7 @@ use crate::context::CodegenCx; use crate::declare::declare_raw_fn; use crate::errors::{ AutoDiffWithoutEnable, AutoDiffWithoutLto, IntrinsicSignatureMismatch, OffloadWithoutEnable, - OffloadWithoutFatLTO, + OffloadWithoutFatLTO, UnknownIntrinsic, }; use crate::llvm::{self, Type, Value}; use crate::type_of::LayoutLlvmExt; @@ -1016,6 +1017,37 @@ fn intrinsic_fn<'ll, 'tcx>( rust_fn_ty, ); + if intrinsic.is_none() { + let mut new_llfn = None; + let can_upgrade = unsafe { llvm::LLVMRustUpgradeIntrinsicFunction(llfn, &mut new_llfn) }; + + if !can_upgrade { + // This is either plain wrong, or this can be caused by incompatible LLVM versions + tcx.dcx().emit_fatal(UnknownIntrinsic { name, span: tcx.def_span(instance.def_id()) }); + } else if let Some(def_id) = instance.def_id().as_local() { + // we can emit diagnostics only for local crates + let hir_id = tcx.local_def_id_to_hir_id(def_id); + + // not all intrinsics are upgraded to some other intrinsics, most are upgraded to instruction sequences + let msg = if let Some(new_llfn) = new_llfn { + format!( + "using deprecated intrinsic `{name}`, `{}` can be used instead", + str::from_utf8(&llvm::get_value_name(new_llfn)).unwrap() + ) + } else { + format!("using deprecated intrinsic `{name}`") + }; + + tcx.emit_node_lint( + DEPRECATED_LLVM_INTRINSIC, + hir_id, + rustc_errors::DiagDecorator(|d| { + d.primary_message(msg).span(tcx.hir_span(hir_id)); + }), + ); + } + } + llfn } diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index 7855afeced47d..3b81fde640596 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -1093,6 +1093,10 @@ unsafe extern "C" { ParamTypes: *const &'a Type, ParamCount: size_t, ) -> &'a Value; + pub(crate) fn LLVMRustUpgradeIntrinsicFunction<'a>( + Fn: &'a Value, + NewFn: &mut Option<&'a Value>, + ) -> bool; // Operations on parameters pub(crate) fn LLVMIsAArgument(Val: &Value) -> Option<&Value>; diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index 4aff294aeac61..8af8f40d69f56 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -37,6 +37,7 @@ declare_lint_pass! { DEPENDENCY_ON_UNIT_NEVER_TYPE_FALLBACK, DEPRECATED, DEPRECATED_IN_FUTURE, + DEPRECATED_LLVM_INTRINSIC, DEPRECATED_SAFE_2024, DEPRECATED_WHERE_CLAUSE_LOCATION, DUPLICATE_FEATURES, @@ -5597,3 +5598,48 @@ declare_lint! { report_in_deps: false, }; } + +declare_lint! { + /// The `deprecated_llvm_intrinsic` lint detects usage of deprecated LLVM intrinsics. + /// + /// ### Example + /// + /// ```rust,ignore (requires x86) + /// #![cfg(any(target_arch = "x86", target_arch = "x86_64"))] + /// #![feature(link_llvm_intrinsics, abi_unadjusted)] + /// #![deny(deprecated_llvm_intrinsic)] + /// + /// unsafe extern "unadjusted" { + /// #[link_name = "llvm.x86.addcarryx.u32"] + /// fn foo(a: u8, b: u32, c: u32, d: &mut u32) -> u8; + /// } + /// + /// #[inline(never)] + /// #[target_feature(enable = "adx")] + /// pub fn bar(a: u8, b: u32, c: u32, d: &mut u32) -> u8 { + /// unsafe { foo(a, b, c, d) } + /// } + /// ``` + /// + /// This will produce: + /// + /// ```text + /// error: Using deprecated intrinsic `llvm.x86.addcarryx.u32` + /// --> example.rs:7:5 + /// | + /// 7 | fn foo(a: u8, b: u32, c: u32, d: &mut u32) -> u8; + /// | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + /// | + /// ``` + /// + /// ### Explanation + /// + /// LLVM periodically updates its list of intrinsics. Deprecated intrinsics are unlikely + /// to be removed, but they may optimize less well than their new versions, so it's + /// best to use the new version. Also, some deprecated intrinsics might have buggy + /// behavior + pub DEPRECATED_LLVM_INTRINSIC, + Allow, + "detects uses of deprecated LLVM intrinsics", + @feature_gate = link_llvm_intrinsics; +} diff --git a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp index f7fccf6296bd1..30410a4d26fc4 100644 --- a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp @@ -9,6 +9,7 @@ #include "llvm/ADT/StringRef.h" #include "llvm/BinaryFormat/Magic.h" #include "llvm/Bitcode/BitcodeWriter.h" +#include "llvm/IR/AutoUpgrade.h" #include "llvm/IR/DIBuilder.h" #include "llvm/IR/DebugInfoMetadata.h" #include "llvm/IR/DiagnosticHandler.h" @@ -1815,6 +1816,15 @@ extern "C" void LLVMRustSetNoSanitizeHWAddress(LLVMValueRef Global) { GV.setSanitizerMetadata(MD); } +extern "C" bool LLVMRustUpgradeIntrinsicFunction(LLVMValueRef Fn, + LLVMValueRef *NewFn) { + Function *F = unwrap(Fn); + Function *NewF = nullptr; + bool CanUpgrade = UpgradeIntrinsicFunction(F, NewF, false); + *NewFn = wrap(NewF); + return CanUpgrade; +} + // Statically assert that the fixed metadata kind IDs declared in // `metadata_kind.rs` match the ones actually used by LLVM. #define FIXED_MD_KIND(VARIANT, VALUE) \ diff --git a/tests/run-make/simd-ffi/simd.rs b/tests/run-make/simd-ffi/simd.rs index 1cd961ff87e7b..3f12dabdb65ea 100644 --- a/tests/run-make/simd-ffi/simd.rs +++ b/tests/run-make/simd-ffi/simd.rs @@ -35,7 +35,7 @@ extern "C" { fn integer(a: i32x4, b: i32x4) -> i32x4; // vmaxq_s32 #[cfg(target_arch = "aarch64")] - #[link_name = "llvm.aarch64.neon.maxs.v4i32"] + #[link_name = "llvm.aarch64.neon.smax.v4i32"] fn integer(a: i32x4, b: i32x4) -> i32x4; // Use a generic LLVM intrinsic to do type checking on other platforms diff --git a/tests/ui/codegen/deprecated-llvm-intrinsic.rs b/tests/ui/codegen/deprecated-llvm-intrinsic.rs new file mode 100644 index 0000000000000..33bc5f419151a --- /dev/null +++ b/tests/ui/codegen/deprecated-llvm-intrinsic.rs @@ -0,0 +1,28 @@ +//@ add-minicore +//@ build-fail +//@ compile-flags: --target aarch64-unknown-linux-gnu +//@ needs-llvm-components: aarch64 +//@ ignore-backends: gcc +#![feature(no_core, lang_items, link_llvm_intrinsics, abi_unadjusted, repr_simd, simd_ffi)] +#![no_std] +#![no_core] +#![allow(internal_features, non_camel_case_types, improper_ctypes)] +#![crate_type = "lib"] + +extern crate minicore; +use minicore::*; + +#[repr(simd)] +pub struct i8x8([i8; 8]); + +extern "unadjusted" { + #[deny(deprecated_llvm_intrinsic)] + #[link_name = "llvm.aarch64.neon.rbit.v8i8"] + fn foo(a: i8x8) -> i8x8; + //~^ ERROR: using deprecated intrinsic `llvm.aarch64.neon.rbit.v8i8`, `llvm.bitreverse.v8i8` can be used instead +} + +#[target_feature(enable = "neon")] +pub unsafe fn bar(a: i8x8) -> i8x8 { + foo(a) +} diff --git a/tests/ui/codegen/deprecated-llvm-intrinsic.stderr b/tests/ui/codegen/deprecated-llvm-intrinsic.stderr new file mode 100644 index 0000000000000..40e4684a8ea4f --- /dev/null +++ b/tests/ui/codegen/deprecated-llvm-intrinsic.stderr @@ -0,0 +1,14 @@ +error: using deprecated intrinsic `llvm.aarch64.neon.rbit.v8i8`, `llvm.bitreverse.v8i8` can be used instead + --> $DIR/deprecated-llvm-intrinsic.rs:21:5 + | +LL | fn foo(a: i8x8) -> i8x8; + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: the lint level is defined here + --> $DIR/deprecated-llvm-intrinsic.rs:19:12 + | +LL | #[deny(deprecated_llvm_intrinsic)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/codegen/unknown-llvm-intrinsic.rs b/tests/ui/codegen/unknown-llvm-intrinsic.rs new file mode 100644 index 0000000000000..bbb4df8c0b271 --- /dev/null +++ b/tests/ui/codegen/unknown-llvm-intrinsic.rs @@ -0,0 +1,14 @@ +//@ build-fail +//@ ignore-backends: gcc + +#![feature(link_llvm_intrinsics, abi_unadjusted)] + +extern "unadjusted" { + #[link_name = "llvm.abcde"] + fn foo(); + //~^ ERROR: unknown LLVM intrinsic `llvm.abcde` +} + +pub fn main() { + unsafe { foo() } +} diff --git a/tests/ui/codegen/unknown-llvm-intrinsic.stderr b/tests/ui/codegen/unknown-llvm-intrinsic.stderr new file mode 100644 index 0000000000000..5417140c69795 --- /dev/null +++ b/tests/ui/codegen/unknown-llvm-intrinsic.stderr @@ -0,0 +1,8 @@ +error: unknown LLVM intrinsic `llvm.abcde` + --> $DIR/unknown-llvm-intrinsic.rs:8:5 + | +LL | fn foo(); + | ^^^^^^^^^ + +error: aborting due to 1 previous error + From a5372be2a15f7ec0c06855e209936e44bd42f482 Mon Sep 17 00:00:00 2001 From: sayantn Date: Tue, 25 Nov 2025 13:30:09 +0530 Subject: [PATCH 6/9] Add target arch verification for LLVM intrinsics --- compiler/rustc_codegen_llvm/src/errors.rs | 9 ++++ compiler/rustc_codegen_llvm/src/intrinsic.rs | 43 +++++++++++++++++-- compiler/rustc_codegen_llvm/src/llvm/ffi.rs | 1 + compiler/rustc_codegen_llvm/src/llvm/mod.rs | 4 ++ .../rustc_llvm/llvm-wrapper/RustWrapper.cpp | 4 ++ tests/ui/codegen/incorrect-arch-intrinsic.rs | 18 ++++++++ .../codegen/incorrect-arch-intrinsic.stderr | 8 ++++ 7 files changed, 84 insertions(+), 3 deletions(-) create mode 100644 tests/ui/codegen/incorrect-arch-intrinsic.rs create mode 100644 tests/ui/codegen/incorrect-arch-intrinsic.stderr diff --git a/compiler/rustc_codegen_llvm/src/errors.rs b/compiler/rustc_codegen_llvm/src/errors.rs index 259aa20b9e381..8921395ab76ce 100644 --- a/compiler/rustc_codegen_llvm/src/errors.rs +++ b/compiler/rustc_codegen_llvm/src/errors.rs @@ -231,3 +231,12 @@ pub(crate) struct UnknownIntrinsic<'a> { #[primary_span] pub span: Span, } + +#[derive(Diagnostic)] +#[diag("intrinsic `{$name}` cannot be used with target arch `{$target_arch}`")] +pub(crate) struct IntrinsicWrongArch<'a> { + pub name: &'a str, + pub target_arch: &'a str, + #[primary_span] + pub span: Span, +} diff --git a/compiler/rustc_codegen_llvm/src/intrinsic.rs b/compiler/rustc_codegen_llvm/src/intrinsic.rs index 2655b45d6e940..4c66c4ef8bdde 100644 --- a/compiler/rustc_codegen_llvm/src/intrinsic.rs +++ b/compiler/rustc_codegen_llvm/src/intrinsic.rs @@ -25,7 +25,7 @@ use rustc_session::lint::builtin::DEPRECATED_LLVM_INTRINSIC; use rustc_span::{Span, Symbol, sym}; use rustc_symbol_mangling::{mangle_internal_symbol, symbol_name_for_instance_in_crate}; use rustc_target::callconv::PassMode; -use rustc_target::spec::Os; +use rustc_target::spec::{Arch, Os}; use tracing::debug; use crate::abi::FnAbiLlvmExt; @@ -37,8 +37,8 @@ use crate::builder::gpu_offload::{ use crate::context::CodegenCx; use crate::declare::declare_raw_fn; use crate::errors::{ - AutoDiffWithoutEnable, AutoDiffWithoutLto, IntrinsicSignatureMismatch, OffloadWithoutEnable, - OffloadWithoutFatLTO, UnknownIntrinsic, + AutoDiffWithoutEnable, AutoDiffWithoutLto, IntrinsicSignatureMismatch, IntrinsicWrongArch, + OffloadWithoutEnable, OffloadWithoutFatLTO, UnknownIntrinsic, }; use crate::llvm::{self, Type, Value}; use crate::type_of::LayoutLlvmExt; @@ -965,6 +965,26 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { } } +fn llvm_arch_for(rust_arch: &Arch) -> Option<&'static str> { + Some(match rust_arch { + Arch::AArch64 | Arch::Arm64EC => "aarch64", + Arch::AmdGpu => "amdgcn", + Arch::Arm => "arm", + Arch::Bpf => "bpf", + Arch::Hexagon => "hexagon", + Arch::LoongArch32 | Arch::LoongArch64 => "loongarch", + Arch::Mips | Arch::Mips32r6 | Arch::Mips64 | Arch::Mips64r6 => "mips", + Arch::Nvptx64 => "nvvm", + Arch::PowerPC | Arch::PowerPC64 => "ppc", + Arch::RiscV32 | Arch::RiscV64 => "riscv", + Arch::S390x => "s390", + Arch::SpirV => "spv", + Arch::Wasm32 | Arch::Wasm64 => "wasm", + Arch::X86 | Arch::X86_64 => "x86", + _ => return None, // fallback for unknown archs + }) +} + fn intrinsic_fn<'ll, 'tcx>( bx: &Builder<'_, 'll, 'tcx>, name: &str, @@ -978,6 +998,23 @@ fn intrinsic_fn<'ll, 'tcx>( let intrinsic = llvm::Intrinsic::lookup(name.as_bytes()); + if let Some(intrinsic) = intrinsic + && intrinsic.is_target_specific() + { + let (llvm_arch, _) = name[5..].split_once('.').unwrap(); + let rust_arch = &tcx.sess.target.arch; + + if let Some(correct_llvm_arch) = llvm_arch_for(rust_arch) + && llvm_arch != correct_llvm_arch + { + tcx.dcx().emit_fatal(IntrinsicWrongArch { + name, + target_arch: rust_arch.desc(), + span: tcx.def_span(instance.def_id()), + }); + } + } + if let Some(intrinsic) = intrinsic && !intrinsic.is_overloaded() { diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index 3b81fde640596..7edbaf5a5f33c 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -1097,6 +1097,7 @@ unsafe extern "C" { Fn: &'a Value, NewFn: &mut Option<&'a Value>, ) -> bool; + pub(crate) fn LLVMRustIsTargetIntrinsic(ID: NonZero) -> bool; // Operations on parameters pub(crate) fn LLVMIsAArgument(Val: &Value) -> Option<&Value>; diff --git a/compiler/rustc_codegen_llvm/src/llvm/mod.rs b/compiler/rustc_codegen_llvm/src/llvm/mod.rs index 84d7e8165fe03..2ec19b1795b5a 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/mod.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/mod.rs @@ -327,6 +327,10 @@ impl Intrinsic { unsafe { LLVMIntrinsicIsOverloaded(self.id).is_true() } } + pub(crate) fn is_target_specific(self) -> bool { + unsafe { LLVMRustIsTargetIntrinsic(self.id) } + } + pub(crate) fn get_declaration<'ll>( self, llmod: &'ll Module, diff --git a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp index 30410a4d26fc4..c310e580af559 100644 --- a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp @@ -1825,6 +1825,10 @@ extern "C" bool LLVMRustUpgradeIntrinsicFunction(LLVMValueRef Fn, return CanUpgrade; } +extern "C" bool LLVMRustIsTargetIntrinsic(unsigned ID) { + return Intrinsic::isTargetIntrinsic(ID); +} + // Statically assert that the fixed metadata kind IDs declared in // `metadata_kind.rs` match the ones actually used by LLVM. #define FIXED_MD_KIND(VARIANT, VALUE) \ diff --git a/tests/ui/codegen/incorrect-arch-intrinsic.rs b/tests/ui/codegen/incorrect-arch-intrinsic.rs new file mode 100644 index 0000000000000..9576cb8f81318 --- /dev/null +++ b/tests/ui/codegen/incorrect-arch-intrinsic.rs @@ -0,0 +1,18 @@ +//@ build-fail +//@ ignore-s390x +//@ normalize-stderr: "target arch `(.*)`" -> "target arch `TARGET_ARCH`" +//@ ignore-backends: gcc + +#![feature(link_llvm_intrinsics, abi_unadjusted)] + +extern "unadjusted" { + #[link_name = "llvm.s390.sfpc"] + fn foo(a: i32); + //~^ ERROR: intrinsic `llvm.s390.sfpc` cannot be used with target arch +} + +pub fn main() { + unsafe { + foo(0); + } +} diff --git a/tests/ui/codegen/incorrect-arch-intrinsic.stderr b/tests/ui/codegen/incorrect-arch-intrinsic.stderr new file mode 100644 index 0000000000000..5b44419aa741f --- /dev/null +++ b/tests/ui/codegen/incorrect-arch-intrinsic.stderr @@ -0,0 +1,8 @@ +error: intrinsic `llvm.s390.sfpc` cannot be used with target arch `TARGET_ARCH` + --> $DIR/incorrect-arch-intrinsic.rs:10:5 + | +LL | fn foo(a: i32); + | ^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + From 3d89a5be5099cbdf4a9dd3c40d198b573246c0ae Mon Sep 17 00:00:00 2001 From: sayantn Date: Tue, 25 Nov 2025 22:39:43 +0530 Subject: [PATCH 7/9] Add autocasts for structs --- compiler/rustc_codegen_llvm/src/intrinsic.rs | 85 +++++++++++++++++--- compiler/rustc_codegen_llvm/src/llvm/ffi.rs | 3 + compiler/rustc_codegen_llvm/src/type_.rs | 10 +++ tests/codegen-llvm/inject-autocast.rs | 39 +++++++++ 4 files changed, 127 insertions(+), 10 deletions(-) create mode 100644 tests/codegen-llvm/inject-autocast.rs diff --git a/compiler/rustc_codegen_llvm/src/intrinsic.rs b/compiler/rustc_codegen_llvm/src/intrinsic.rs index 4c66c4ef8bdde..6aedb6d97d0f8 100644 --- a/compiler/rustc_codegen_llvm/src/intrinsic.rs +++ b/compiler/rustc_codegen_llvm/src/intrinsic.rs @@ -820,7 +820,7 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { &mut self, instance: ty::Instance<'tcx>, args: &[OperandRef<'tcx, Self::Value>], - is_cleanup: bool, + _is_cleanup: bool, ) -> Self::Value { let tcx = self.tcx(); @@ -871,7 +871,7 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { for arg in args { match arg.val { OperandValue::ZeroSized => {} - OperandValue::Immediate(_) => llargs.push(arg.immediate()), + OperandValue::Immediate(a) => llargs.push(a), OperandValue::Pair(a, b) => { llargs.push(a); llargs.push(b); @@ -897,24 +897,38 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { } debug!("call intrinsic {:?} with args ({:?})", instance, llargs); - let args = self.check_call("call", fn_ty, fn_ptr, &llargs); + + for (dest_ty, arg) in iter::zip(self.func_params_types(fn_ty), &mut llargs) { + let src_ty = self.val_ty(arg); + assert!( + can_autocast(self, src_ty, dest_ty), + "Cannot match `{dest_ty:?}` (expected) with {src_ty:?} (found) in `{fn_ptr:?}" + ); + + *arg = autocast(self, arg, src_ty, dest_ty); + } + let llret = unsafe { llvm::LLVMBuildCallWithOperandBundles( self.llbuilder, fn_ty, fn_ptr, - args.as_ptr() as *const &llvm::Value, - args.len() as c_uint, + llargs.as_ptr(), + llargs.len() as c_uint, ptr::dangling(), 0, c"".as_ptr(), ) }; - if is_cleanup { - self.apply_attrs_to_cleanup_callsite(llret); - } - llret + let src_ty = self.val_ty(llret); + let dest_ty = llreturn_ty; + assert!( + can_autocast(self, dest_ty, src_ty), + "Cannot match `{src_ty:?}` (expected) with `{dest_ty:?}` (found) in `{fn_ptr:?}`" + ); + + autocast(self, llret, src_ty, dest_ty) } fn abort(&mut self) { @@ -985,6 +999,57 @@ fn llvm_arch_for(rust_arch: &Arch) -> Option<&'static str> { }) } +fn can_autocast<'ll>(cx: &CodegenCx<'ll, '_>, rust_ty: &'ll Type, llvm_ty: &'ll Type) -> bool { + if rust_ty == llvm_ty { + return true; + } + + // Some LLVM intrinsics return **non-packed** structs, but they can't be mimicked from Rust + // due to auto field-alignment in non-packed structs (packed structs are represented in LLVM + // as, well, packed structs, so they won't match with those either) + if cx.type_kind(llvm_ty) == TypeKind::Struct && cx.type_kind(rust_ty) == TypeKind::Struct { + let rust_element_tys = cx.struct_element_types(rust_ty); + let llvm_element_tys = cx.struct_element_types(llvm_ty); + + if rust_element_tys.len() != llvm_element_tys.len() { + return false; + } + + iter::zip(rust_element_tys, llvm_element_tys).all(|(rust_element_ty, llvm_element_ty)| { + can_autocast(cx, rust_element_ty, llvm_element_ty) + }) + } else { + false + } +} + +fn autocast<'ll>( + bx: &mut Builder<'_, 'll, '_>, + val: &'ll Value, + src_ty: &'ll Type, + dest_ty: &'ll Type, +) -> &'ll Value { + if src_ty == dest_ty { + return val; + } + match (bx.type_kind(src_ty), bx.type_kind(dest_ty)) { + // re-pack structs + (TypeKind::Struct, TypeKind::Struct) => { + let mut ret = bx.const_poison(dest_ty); + for (idx, (src_element_ty, dest_element_ty)) in + iter::zip(bx.struct_element_types(src_ty), bx.struct_element_types(dest_ty)) + .enumerate() + { + let elt = bx.extract_value(val, idx as u64); + let casted_elt = autocast(bx, elt, src_element_ty, dest_element_ty); + ret = bx.insert_value(ret, casted_elt, idx as u64); + } + ret + } + _ => unreachable!(), + } +} + fn intrinsic_fn<'ll, 'tcx>( bx: &Builder<'_, 'll, 'tcx>, name: &str, @@ -1030,7 +1095,7 @@ fn intrinsic_fn<'ll, 'tcx>( && rust_argument_tys.len() == llvm_argument_tys.len() && iter::once((rust_return_ty, llvm_return_ty)) .chain(iter::zip(rust_argument_tys, llvm_argument_tys)) - .all(|(rust_ty, llvm_ty)| rust_ty == llvm_ty); + .all(|(rust_ty, llvm_ty)| can_autocast(bx, rust_ty, llvm_ty)); if !is_correct_signature { tcx.dcx().emit_fatal(IntrinsicSignatureMismatch { diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index 7edbaf5a5f33c..deafa38b7be64 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -1613,6 +1613,9 @@ unsafe extern "C" { Packed: Bool, ); + pub(crate) fn LLVMCountStructElementTypes(StructTy: &Type) -> c_uint; + pub(crate) fn LLVMGetStructElementTypes<'a>(StructTy: &'a Type, Dest: *mut &'a Type); + pub(crate) safe fn LLVMMetadataAsValue<'a>(C: &'a Context, MD: &'a Metadata) -> &'a Value; pub(crate) safe fn LLVMSetUnnamedAddress(Global: &Value, UnnamedAddr: UnnamedAddr); diff --git a/compiler/rustc_codegen_llvm/src/type_.rs b/compiler/rustc_codegen_llvm/src/type_.rs index b8cee3510789c..147056a5885a6 100644 --- a/compiler/rustc_codegen_llvm/src/type_.rs +++ b/compiler/rustc_codegen_llvm/src/type_.rs @@ -94,6 +94,16 @@ impl<'ll, CX: Borrow>> GenericCx<'ll, CX> { pub(crate) fn func_is_variadic(&self, ty: &'ll Type) -> bool { unsafe { llvm::LLVMIsFunctionVarArg(ty).is_true() } } + + pub(crate) fn struct_element_types(&self, ty: &'ll Type) -> Vec<&'ll Type> { + unsafe { + let n_args = llvm::LLVMCountStructElementTypes(ty) as usize; + let mut args = Vec::with_capacity(n_args); + llvm::LLVMGetStructElementTypes(ty, args.as_mut_ptr()); + args.set_len(n_args); + args + } + } } impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> { pub(crate) fn type_bool(&self) -> &'ll Type { diff --git a/tests/codegen-llvm/inject-autocast.rs b/tests/codegen-llvm/inject-autocast.rs new file mode 100644 index 0000000000000..d79779285889c --- /dev/null +++ b/tests/codegen-llvm/inject-autocast.rs @@ -0,0 +1,39 @@ +//@ compile-flags: -C opt-level=0 -C target-feature=+kl +//@ only-x86_64 + +#![feature(link_llvm_intrinsics, abi_unadjusted, simd_ffi, portable_simd)] +#![crate_type = "lib"] + +use std::simd::i64x2; + +#[repr(C, packed)] +pub struct Bar(u32, i64x2, i64x2, i64x2, i64x2, i64x2, i64x2); +// CHECK: %Bar = type <{ i32, <2 x i64>, <2 x i64>, <2 x i64>, <2 x i64>, <2 x i64>, <2 x i64> }> + +// CHECK-LABEL: @struct_autocast +#[no_mangle] +pub unsafe fn struct_autocast(key_metadata: u32, key: i64x2) -> Bar { + extern "unadjusted" { + #[link_name = "llvm.x86.encodekey128"] + fn foo(key_metadata: u32, key: i64x2) -> Bar; + } + + // CHECK: [[A:%[0-9]+]] = call { i32, <2 x i64>, <2 x i64>, <2 x i64>, <2 x i64>, <2 x i64>, <2 x i64> } @llvm.x86.encodekey128(i32 {{.*}}, <2 x i64> {{.*}}) + // CHECK: [[B:%[0-9]+]] = extractvalue { i32, <2 x i64>, <2 x i64>, <2 x i64>, <2 x i64>, <2 x i64>, <2 x i64> } [[A]], 0 + // CHECK: [[C:%[0-9]+]] = insertvalue %Bar poison, i32 [[B]], 0 + // CHECK: [[D:%[0-9]+]] = extractvalue { i32, <2 x i64>, <2 x i64>, <2 x i64>, <2 x i64>, <2 x i64>, <2 x i64> } [[A]], 1 + // CHECK: [[E:%[0-9]+]] = insertvalue %Bar [[C]], <2 x i64> [[D]], 1 + // CHECK: [[F:%[0-9]+]] = extractvalue { i32, <2 x i64>, <2 x i64>, <2 x i64>, <2 x i64>, <2 x i64>, <2 x i64> } [[A]], 2 + // CHECK: [[G:%[0-9]+]] = insertvalue %Bar [[E]], <2 x i64> [[F]], 2 + // CHECK: [[H:%[0-9]+]] = extractvalue { i32, <2 x i64>, <2 x i64>, <2 x i64>, <2 x i64>, <2 x i64>, <2 x i64> } [[A]], 3 + // CHECK: [[I:%[0-9]+]] = insertvalue %Bar [[G]], <2 x i64> [[H]], 3 + // CHECK: [[J:%[0-9]+]] = extractvalue { i32, <2 x i64>, <2 x i64>, <2 x i64>, <2 x i64>, <2 x i64>, <2 x i64> } [[A]], 4 + // CHECK: [[K:%[0-9]+]] = insertvalue %Bar [[I]], <2 x i64> [[J]], 4 + // CHECK: [[L:%[0-9]+]] = extractvalue { i32, <2 x i64>, <2 x i64>, <2 x i64>, <2 x i64>, <2 x i64>, <2 x i64> } [[A]], 5 + // CHECK: [[M:%[0-9]+]] = insertvalue %Bar [[K]], <2 x i64> [[L]], 5 + // CHECK: [[N:%[0-9]+]] = extractvalue { i32, <2 x i64>, <2 x i64>, <2 x i64>, <2 x i64>, <2 x i64>, <2 x i64> } [[A]], 6 + // CHECK: insertvalue %Bar [[M]], <2 x i64> [[N]], 6 + foo(key_metadata, key) +} + +// CHECK: declare { i32, <2 x i64>, <2 x i64>, <2 x i64>, <2 x i64>, <2 x i64>, <2 x i64> } @llvm.x86.encodekey128(i32, <2 x i64>) From 5aa800af80169781d358e02fb82a5beaaead8159 Mon Sep 17 00:00:00 2001 From: sayantn Date: Tue, 25 Nov 2025 23:09:41 +0530 Subject: [PATCH 8/9] Add autocast for `i1` vectors --- compiler/rustc_codegen_llvm/src/intrinsic.rs | 79 ++++++++++++++++---- tests/codegen-llvm/inject-autocast.rs | 41 +++++++++- 2 files changed, 105 insertions(+), 15 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/intrinsic.rs b/compiler/rustc_codegen_llvm/src/intrinsic.rs index 6aedb6d97d0f8..70f145a7155b7 100644 --- a/compiler/rustc_codegen_llvm/src/intrinsic.rs +++ b/compiler/rustc_codegen_llvm/src/intrinsic.rs @@ -1004,22 +1004,31 @@ fn can_autocast<'ll>(cx: &CodegenCx<'ll, '_>, rust_ty: &'ll Type, llvm_ty: &'ll return true; } - // Some LLVM intrinsics return **non-packed** structs, but they can't be mimicked from Rust - // due to auto field-alignment in non-packed structs (packed structs are represented in LLVM - // as, well, packed structs, so they won't match with those either) - if cx.type_kind(llvm_ty) == TypeKind::Struct && cx.type_kind(rust_ty) == TypeKind::Struct { - let rust_element_tys = cx.struct_element_types(rust_ty); - let llvm_element_tys = cx.struct_element_types(llvm_ty); - - if rust_element_tys.len() != llvm_element_tys.len() { - return false; + match cx.type_kind(llvm_ty) { + // Some LLVM intrinsics return **non-packed** structs, but they can't be mimicked from Rust + // due to auto field-alignment in non-packed structs (packed structs are represented in LLVM + // as, well, packed structs, so they won't match with those either) + TypeKind::Struct if cx.type_kind(rust_ty) == TypeKind::Struct => { + let rust_element_tys = cx.struct_element_types(rust_ty); + let llvm_element_tys = cx.struct_element_types(llvm_ty); + + if rust_element_tys.len() != llvm_element_tys.len() { + return false; + } + + iter::zip(rust_element_tys, llvm_element_tys).all( + |(rust_element_ty, llvm_element_ty)| { + can_autocast(cx, rust_element_ty, llvm_element_ty) + }, + ) } + TypeKind::Vector if cx.element_type(llvm_ty) == cx.type_i1() => { + let element_count = cx.vector_length(llvm_ty) as u64; + let int_width = element_count.next_power_of_two().max(8); - iter::zip(rust_element_tys, llvm_element_tys).all(|(rust_element_ty, llvm_element_ty)| { - can_autocast(cx, rust_element_ty, llvm_element_ty) - }) - } else { - false + rust_ty == cx.type_ix(int_width) + } + _ => false, } } @@ -1046,6 +1055,48 @@ fn autocast<'ll>( } ret } + // cast from the i1xN vector type to the primitive type + (TypeKind::Vector, TypeKind::Integer) if bx.element_type(src_ty) == bx.type_i1() => { + let vector_length = bx.vector_length(src_ty) as u64; + let int_width = vector_length.next_power_of_two().max(8); + + let val = if vector_length == int_width { + val + } else { + // zero-extends vector + let shuffle_indices = match vector_length { + 0 => unreachable!("zero length vectors are not allowed"), + 1 => vec![0, 1, 1, 1, 1, 1, 1, 1], + 2 => vec![0, 1, 2, 2, 2, 2, 2, 2], + 3 => vec![0, 1, 2, 3, 3, 3, 3, 3], + 4.. => (0..int_width as i32).collect(), + }; + let shuffle_mask = + shuffle_indices.into_iter().map(|i| bx.const_i32(i)).collect::>(); + bx.shuffle_vector(val, bx.const_null(src_ty), bx.const_vector(&shuffle_mask)) + }; + bx.bitcast(val, dest_ty) + } + // cast from the primitive type to the i1xN vector type + (TypeKind::Integer, TypeKind::Vector) if bx.element_type(dest_ty) == bx.type_i1() => { + let vector_length = bx.vector_length(dest_ty) as u64; + let int_width = vector_length.next_power_of_two().max(8); + + let intermediate_ty = bx.type_vector(bx.type_i1(), int_width); + let intermediate = bx.bitcast(val, intermediate_ty); + + if vector_length == int_width { + intermediate + } else { + let shuffle_mask: Vec<_> = + (0..vector_length).map(|i| bx.const_i32(i as i32)).collect(); + bx.shuffle_vector( + intermediate, + bx.const_poison(intermediate_ty), + bx.const_vector(&shuffle_mask), + ) + } + } _ => unreachable!(), } } diff --git a/tests/codegen-llvm/inject-autocast.rs b/tests/codegen-llvm/inject-autocast.rs index d79779285889c..ae5bd0e422998 100644 --- a/tests/codegen-llvm/inject-autocast.rs +++ b/tests/codegen-llvm/inject-autocast.rs @@ -1,4 +1,4 @@ -//@ compile-flags: -C opt-level=0 -C target-feature=+kl +//@ compile-flags: -C opt-level=0 -C target-feature=+kl,+avx512vp2intersect,+avx512vl //@ only-x86_64 #![feature(link_llvm_intrinsics, abi_unadjusted, simd_ffi, portable_simd)] @@ -36,4 +36,43 @@ pub unsafe fn struct_autocast(key_metadata: u32, key: i64x2) -> Bar { foo(key_metadata, key) } +// CHECK-LABEL: @struct_with_i1_vector_autocast +#[no_mangle] +pub unsafe fn struct_with_i1_vector_autocast(a: i64x2, b: i64x2) -> (u8, u8) { + extern "unadjusted" { + #[link_name = "llvm.x86.avx512.vp2intersect.q.128"] + fn foo(a: i64x2, b: i64x2) -> (u8, u8); + } + + // CHECK: [[A:%[0-9]+]] = call { <2 x i1>, <2 x i1> } @llvm.x86.avx512.vp2intersect.q.128(<2 x i64> {{.*}}, <2 x i64> {{.*}}) + // CHECK: [[B:%[0-9]+]] = extractvalue { <2 x i1>, <2 x i1> } [[A]], 0 + // CHECK: [[C:%[0-9]+]] = shufflevector <2 x i1> [[B]], <2 x i1> zeroinitializer, <8 x i32> + // CHECK: [[D:%[0-9]+]] = bitcast <8 x i1> [[C]] to i8 + // CHECK: [[E:%[0-9]+]] = insertvalue { i8, i8 } poison, i8 [[D]], 0 + // CHECK: [[F:%[0-9]+]] = extractvalue { <2 x i1>, <2 x i1> } [[A]], 1 + // CHECK: [[G:%[0-9]+]] = shufflevector <2 x i1> [[F]], <2 x i1> zeroinitializer, <8 x i32> + // CHECK: [[H:%[0-9]+]] = bitcast <8 x i1> [[G]] to i8 + // CHECK: insertvalue { i8, i8 } [[E]], i8 [[H]], 1 + foo(a, b) +} + +// CHECK-LABEL: @i1_vector_autocast +#[no_mangle] +pub unsafe fn i1_vector_autocast(a: u8, b: u8) -> u8 { + extern "unadjusted" { + #[link_name = "llvm.x86.avx512.kadd.b"] + fn foo(a: u8, b: u8) -> u8; + } + + // CHECK: [[A:%[0-9]+]] = bitcast i8 {{.*}} to <8 x i1> + // CHECK: [[B:%[0-9]+]] = bitcast i8 {{.*}} to <8 x i1> + // CHECK: [[C:%[0-9]+]] = call <8 x i1> @llvm.x86.avx512.kadd.b(<8 x i1> [[A]], <8 x i1> [[B]]) + // CHECK: bitcast <8 x i1> [[C]] to i8 + foo(a, b) +} + // CHECK: declare { i32, <2 x i64>, <2 x i64>, <2 x i64>, <2 x i64>, <2 x i64>, <2 x i64> } @llvm.x86.encodekey128(i32, <2 x i64>) + +// CHECK: declare { <2 x i1>, <2 x i1> } @llvm.x86.avx512.vp2intersect.q.128(<2 x i64>, <2 x i64>) + +// CHECK: declare <8 x i1> @llvm.x86.avx512.kadd.b(<8 x i1>, <8 x i1>) From 11f350da386e3fa51c106c09db1cf4d637754a9c Mon Sep 17 00:00:00 2001 From: sayantn Date: Tue, 25 Nov 2025 23:28:37 +0530 Subject: [PATCH 9/9] Add autocast for `bf16` and `bf16xN` --- compiler/rustc_codegen_llvm/src/intrinsic.rs | 16 ++++++++++++---- compiler/rustc_codegen_llvm/src/llvm/ffi.rs | 3 +++ compiler/rustc_codegen_llvm/src/type_.rs | 4 ++++ tests/codegen-llvm/inject-autocast.rs | 19 +++++++++++++++++-- 4 files changed, 36 insertions(+), 6 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/intrinsic.rs b/compiler/rustc_codegen_llvm/src/intrinsic.rs index 70f145a7155b7..d46672bdffb7f 100644 --- a/compiler/rustc_codegen_llvm/src/intrinsic.rs +++ b/compiler/rustc_codegen_llvm/src/intrinsic.rs @@ -1022,12 +1022,20 @@ fn can_autocast<'ll>(cx: &CodegenCx<'ll, '_>, rust_ty: &'ll Type, llvm_ty: &'ll }, ) } - TypeKind::Vector if cx.element_type(llvm_ty) == cx.type_i1() => { + TypeKind::Vector => { + let llvm_element_ty = cx.element_type(llvm_ty); let element_count = cx.vector_length(llvm_ty) as u64; - let int_width = element_count.next_power_of_two().max(8); - rust_ty == cx.type_ix(int_width) + if llvm_element_ty == cx.type_bf16() { + rust_ty == cx.type_vector(cx.type_i16(), element_count) + } else if llvm_element_ty == cx.type_i1() { + let int_width = element_count.next_power_of_two().max(8); + rust_ty == cx.type_ix(int_width) + } else { + false + } } + TypeKind::BFloat => rust_ty == cx.type_i16(), _ => false, } } @@ -1097,7 +1105,7 @@ fn autocast<'ll>( ) } } - _ => unreachable!(), + _ => bx.bitcast(val, dest_ty), // for `bf16(xN)` <-> `u16(xN)` } } diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index deafa38b7be64..525d1dbe9d0d3 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -921,6 +921,9 @@ unsafe extern "C" { pub(crate) fn LLVMDoubleTypeInContext(C: &Context) -> &Type; pub(crate) fn LLVMFP128TypeInContext(C: &Context) -> &Type; + // Operations on non-IEEE real types + pub(crate) fn LLVMBFloatTypeInContext(C: &Context) -> &Type; + // Operations on function types pub(crate) fn LLVMFunctionType<'a>( ReturnType: &'a Type, diff --git a/compiler/rustc_codegen_llvm/src/type_.rs b/compiler/rustc_codegen_llvm/src/type_.rs index 147056a5885a6..796f3d9ef60ba 100644 --- a/compiler/rustc_codegen_llvm/src/type_.rs +++ b/compiler/rustc_codegen_llvm/src/type_.rs @@ -183,6 +183,10 @@ impl<'ll, CX: Borrow>> GenericCx<'ll, CX> { ) } } + + pub(crate) fn type_bf16(&self) -> &'ll Type { + unsafe { llvm::LLVMBFloatTypeInContext(self.llcx()) } + } } impl<'ll, CX: Borrow>> BaseTypeCodegenMethods for GenericCx<'ll, CX> { diff --git a/tests/codegen-llvm/inject-autocast.rs b/tests/codegen-llvm/inject-autocast.rs index ae5bd0e422998..fec9d3f0b1955 100644 --- a/tests/codegen-llvm/inject-autocast.rs +++ b/tests/codegen-llvm/inject-autocast.rs @@ -1,10 +1,10 @@ -//@ compile-flags: -C opt-level=0 -C target-feature=+kl,+avx512vp2intersect,+avx512vl +//@ compile-flags: -C opt-level=0 -C target-feature=+kl,+avx512vp2intersect,+avx512vl,+avxneconvert //@ only-x86_64 #![feature(link_llvm_intrinsics, abi_unadjusted, simd_ffi, portable_simd)] #![crate_type = "lib"] -use std::simd::i64x2; +use std::simd::{f32x4, i16x8, i64x2}; #[repr(C, packed)] pub struct Bar(u32, i64x2, i64x2, i64x2, i64x2, i64x2, i64x2); @@ -71,8 +71,23 @@ pub unsafe fn i1_vector_autocast(a: u8, b: u8) -> u8 { foo(a, b) } +// CHECK-LABEL: @bf16_vector_autocast +#[no_mangle] +pub unsafe fn bf16_vector_autocast(a: f32x4) -> i16x8 { + extern "unadjusted" { + #[link_name = "llvm.x86.vcvtneps2bf16128"] + fn foo(a: f32x4) -> i16x8; + } + + // CHECK: [[A:%[0-9]+]] = call <8 x bfloat> @llvm.x86.vcvtneps2bf16128(<4 x float> {{.*}}) + // CHECK: bitcast <8 x bfloat> [[A]] to <8 x i16> + foo(a) +} + // CHECK: declare { i32, <2 x i64>, <2 x i64>, <2 x i64>, <2 x i64>, <2 x i64>, <2 x i64> } @llvm.x86.encodekey128(i32, <2 x i64>) // CHECK: declare { <2 x i1>, <2 x i1> } @llvm.x86.avx512.vp2intersect.q.128(<2 x i64>, <2 x i64>) // CHECK: declare <8 x i1> @llvm.x86.avx512.kadd.b(<8 x i1>, <8 x i1>) + +// CHECK: declare <8 x bfloat> @llvm.x86.vcvtneps2bf16128(<4 x float>)