From be2f12821121e5d2af61f16580fb53bf5da2a182 Mon Sep 17 00:00:00 2001 From: Hood Chatham Date: Mon, 26 Jan 2026 16:58:31 -0800 Subject: [PATCH 01/10] Fix: On wasm targets, call `panic_in_cleanup` if panic occurs in cleanup Previously this was not correctly implemented. Each funclet may need its own terminate block, so this changes the `terminate_block` into a `terminate_blocks` `IndexVec` which can have a terminate_block for each funclet. We key on the first basic block of the funclet -- in particular, this is the start block for the old case of the top level terminate function. Rather than using a catchswitch/catchpad pair, I used a cleanuppad. The reason for the pair is to avoid catching foreign exceptions on MSVC. On wasm, it seems that the catchswitch/catchpad pair is optimized back into a single cleanuppad and a catch_all instruction is emitted which will catch foreign exceptions. Because the new logic is only used on wasm, it seemed better to take the simpler approach seeing as they do the same thing. --- src/builder.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/builder.rs b/src/builder.rs index 3cffd862b9b..08964113b94 100644 --- a/src/builder.rs +++ b/src/builder.rs @@ -1657,6 +1657,10 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> { unimplemented!(); } + fn get_funclet_cleanuppad(&self, _funclet: &Funclet) -> RValue<'gcc> { + unimplemented!(); + } + // Atomic Operations fn atomic_cmpxchg( &mut self, From 062e9f351a64ee727b9fad1af6343d3d2b17db31 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sun, 12 Apr 2026 10:46:11 +0200 Subject: [PATCH 02/10] simd_reduce_min/max: remove float support --- src/builder.rs | 57 ------------------------------------------- src/intrinsic/simd.rs | 7 +++--- 2 files changed, 3 insertions(+), 61 deletions(-) diff --git a/src/builder.rs b/src/builder.rs index 3cffd862b9b..065ed43be8c 100644 --- a/src/builder.rs +++ b/src/builder.rs @@ -2313,67 +2313,10 @@ impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> { self.vector_extremum(a, b, ExtremumOperation::Min) } - #[cfg(feature = "master")] - pub fn vector_reduce_fmin(&mut self, src: RValue<'gcc>) -> RValue<'gcc> { - let vector_type = src.get_type().unqualified().dyncast_vector().expect("vector type"); - let element_count = vector_type.get_num_units(); - let mut acc = self - .context - .new_vector_access(self.location, src, self.context.new_rvalue_zero(self.int_type)) - .to_rvalue(); - for i in 1..element_count { - let elem = self - .context - .new_vector_access( - self.location, - src, - self.context.new_rvalue_from_int(self.int_type, i as _), - ) - .to_rvalue(); - let cmp = self.context.new_comparison(self.location, ComparisonOp::LessThan, acc, elem); - acc = self.select(cmp, acc, elem); - } - acc - } - - #[cfg(not(feature = "master"))] - pub fn vector_reduce_fmin(&mut self, _src: RValue<'gcc>) -> RValue<'gcc> { - unimplemented!(); - } - pub fn vector_maximum_number_nsz(&mut self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> { self.vector_extremum(a, b, ExtremumOperation::Max) } - #[cfg(feature = "master")] - pub fn vector_reduce_fmax(&mut self, src: RValue<'gcc>) -> RValue<'gcc> { - let vector_type = src.get_type().unqualified().dyncast_vector().expect("vector type"); - let element_count = vector_type.get_num_units(); - let mut acc = self - .context - .new_vector_access(self.location, src, self.context.new_rvalue_zero(self.int_type)) - .to_rvalue(); - for i in 1..element_count { - let elem = self - .context - .new_vector_access( - self.location, - src, - self.context.new_rvalue_from_int(self.int_type, i as _), - ) - .to_rvalue(); - let cmp = - self.context.new_comparison(self.location, ComparisonOp::GreaterThan, acc, elem); - acc = self.select(cmp, acc, elem); - } - acc - } - - #[cfg(not(feature = "master"))] - pub fn vector_reduce_fmax(&mut self, _src: RValue<'gcc>) -> RValue<'gcc> { - unimplemented!(); - } - pub fn vector_select( &mut self, cond: RValue<'gcc>, diff --git a/src/intrinsic/simd.rs b/src/intrinsic/simd.rs index 6fd19c4f82c..bdb8316f396 100644 --- a/src/intrinsic/simd.rs +++ b/src/intrinsic/simd.rs @@ -1422,7 +1422,7 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>( ); macro_rules! minmax_red { - ($name:ident: $int_red:ident, $float_red:ident) => { + ($name:ident: $int_red:ident) => { if name == sym::$name { require!( ret_ty == in_elem, @@ -1430,7 +1430,6 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>( ); return match *in_elem.kind() { ty::Int(_) | ty::Uint(_) => Ok(bx.$int_red(args[0].immediate())), - ty::Float(_) => Ok(bx.$float_red(args[0].immediate())), _ => return_error!(InvalidMonomorphization::UnsupportedSymbol { span, name, @@ -1444,8 +1443,8 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>( }; } - minmax_red!(simd_reduce_min: vector_reduce_min, vector_reduce_fmin); - minmax_red!(simd_reduce_max: vector_reduce_max, vector_reduce_fmax); + minmax_red!(simd_reduce_min: vector_reduce_min); + minmax_red!(simd_reduce_max: vector_reduce_max); macro_rules! bitwise_red { ($name:ident : $op:expr, $boolean:expr) => { From e001838589f9c41e258917bbb9622656f743cd8f Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Wed, 29 Apr 2026 23:12:39 +0200 Subject: [PATCH 03/10] Merge commit 'd189e9f23c4c971546cb59bf43ab4df0e5552770' into subtree-update_cg_gcc_2026-04-29 --- .github/workflows/ci.yml | 4 +- .github/workflows/failures.yml | 4 +- .github/workflows/gcc12.yml | 4 +- .github/workflows/m68k.yml | 4 +- .github/workflows/stdarch.yml | 6 +- Cargo.toml | 8 +- build_system/src/test.rs | 61 +++++ example/mini_core.rs | 56 ++++- libgccjit.version | 2 +- rust-toolchain | 2 +- src/abi.rs | 11 +- src/asm.rs | 22 +- src/base.rs | 14 +- src/builder.rs | 34 +-- src/context.rs | 7 +- src/errors.rs | 7 + src/intrinsic/llvm.rs | 4 + src/intrinsic/mod.rs | 207 +++++++-------- tests/compile/asm_nul_byte.rs | 15 ++ .../{run => compile}/call-llvm-intrinsics.rs | 8 +- tests/compile/fn_ptr_transmute_ignored_arg.rs | 12 + tests/compile/global_asm_nul_byte.rs | 13 + tests/compile/log.rs | 12 + tests/compile/naked_asm_nul_byte.rs | 17 ++ tests/{run => compile}/simd-ffi.rs | 12 +- tests/failing-lto-tests.txt | 1 - tests/failing-ui-tests.txt | 14 +- tests/lang_tests.rs | 237 ++++++++++++++++++ tests/lang_tests_debug.rs | 5 - tests/lang_tests_release.rs | 5 - tests/no_builtins/no_builtins.rs | 24 ++ tests/no_builtins/with_builtins.rs | 21 ++ tests/run/asm.rs | 1 - tests/run/core-float.rs | 78 ++++++ 34 files changed, 726 insertions(+), 206 deletions(-) create mode 100644 tests/compile/asm_nul_byte.rs rename tests/{run => compile}/call-llvm-intrinsics.rs (91%) create mode 100644 tests/compile/fn_ptr_transmute_ignored_arg.rs create mode 100644 tests/compile/global_asm_nul_byte.rs create mode 100644 tests/compile/log.rs create mode 100644 tests/compile/naked_asm_nul_byte.rs rename tests/{run => compile}/simd-ffi.rs (92%) create mode 100644 tests/lang_tests.rs delete mode 100644 tests/lang_tests_debug.rs delete mode 100644 tests/lang_tests_release.rs create mode 100644 tests/no_builtins/no_builtins.rs create mode 100644 tests/no_builtins/with_builtins.rs create mode 100644 tests/run/core-float.rs diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 840c09409bb..fa9535a3729 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -101,7 +101,7 @@ jobs: - name: Run y.sh cargo build run: | - ./y.sh cargo build --manifest-path tests/hello-world/Cargo.toml + CARGO_PROFILE_DEV_LTO=no ./y.sh cargo build --manifest-path tests/hello-world/Cargo.toml - name: Clean run: | @@ -119,7 +119,7 @@ jobs: - name: Run tests run: | - ./y.sh test --release --clean --build-sysroot ${{ matrix.commands }} + ./y.sh test --release --clean --build-sysroot --no-builtins-tests ${{ matrix.commands }} duplicates: runs-on: ubuntu-24.04 diff --git a/.github/workflows/failures.yml b/.github/workflows/failures.yml index a2a93289305..2c9e4950706 100644 --- a/.github/workflows/failures.yml +++ b/.github/workflows/failures.yml @@ -48,7 +48,9 @@ jobs: - name: Install libgccjit12 if: matrix.libgccjit_version.gcc == 'libgccjit12.so' - run: sudo apt-get install libgccjit-12-dev + run: | + sudo apt-get update + sudo apt-get install libgccjit-12-dev - name: Setup path to libgccjit if: matrix.libgccjit_version.gcc == 'libgccjit12.so' diff --git a/.github/workflows/gcc12.yml b/.github/workflows/gcc12.yml index 55b090894b4..51b06039c88 100644 --- a/.github/workflows/gcc12.yml +++ b/.github/workflows/gcc12.yml @@ -46,7 +46,9 @@ jobs: - name: Install packages # `llvm-14-tools` is needed to install the `FileCheck` binary which is used for asm tests. - run: sudo apt-get install ninja-build ripgrep llvm-14-tools libgccjit-12-dev + run: | + sudo apt-get update + sudo apt-get install ninja-build ripgrep llvm-14-tools libgccjit-12-dev - name: Setup path to libgccjit run: echo 'gcc-path = "/usr/lib/gcc/x86_64-linux-gnu/12"' > config.toml diff --git a/.github/workflows/m68k.yml b/.github/workflows/m68k.yml index c36db18ed4a..8ed3545ee7d 100644 --- a/.github/workflows/m68k.yml +++ b/.github/workflows/m68k.yml @@ -83,7 +83,7 @@ jobs: run: | ./y.sh prepare --only-libcore --cross ./y.sh build --sysroot --target-triple m68k-unknown-linux-gnu --target ${{ github.workspace }}/target_specs/m68k-unknown-linux-gnu.json - CG_RUSTFLAGS="-Clinker=m68k-unknown-linux-gnu-gcc" ./y.sh cargo build -Zjson-target-spec --manifest-path=./tests/hello-world/Cargo.toml --target ${{ github.workspace }}/target_specs/m68k-unknown-linux-gnu.json + CARGO_PROFILE_DEV_LTO=no CG_RUSTFLAGS="-Clinker=m68k-unknown-linux-gnu-gcc" ./y.sh cargo build -Zjson-target-spec --manifest-path=./tests/hello-world/Cargo.toml --target ${{ github.workspace }}/target_specs/m68k-unknown-linux-gnu.json ./y.sh clean all - name: Build @@ -110,7 +110,7 @@ jobs: vm_dir=$(pwd)/vm cd tests/hello-world - CG_RUSTFLAGS="-Clinker=m68k-unknown-linux-gnu-gcc" ../../y.sh cargo build --target m68k-unknown-linux-gnu + CARGO_PROFILE_DEV_LTO=no CG_RUSTFLAGS="-Clinker=m68k-unknown-linux-gnu-gcc" ../../y.sh cargo build --target m68k-unknown-linux-gnu sudo cp target/m68k-unknown-linux-gnu/debug/hello_world $vm_dir/home/ sudo chroot $vm_dir qemu-m68k-static /home/hello_world > hello_world_stdout expected_output="40" diff --git a/.github/workflows/stdarch.yml b/.github/workflows/stdarch.yml index a5872857316..66f30b147b4 100644 --- a/.github/workflows/stdarch.yml +++ b/.github/workflows/stdarch.yml @@ -50,9 +50,11 @@ jobs: run: | mkdir intel-sde cd intel-sde - dir=sde-external-9.33.0-2024-01-07-lin + version=10.8.0-2026-03-15 + url_path=915934 + dir=sde-external-$version-lin file=$dir.tar.xz - wget https://downloadmirror.intel.com/813591/$file + wget https://downloadmirror.intel.com/$url_path/$file tar xvf $file sudo mkdir /usr/share/intel-sde sudo cp -r $dir/* /usr/share/intel-sde diff --git a/Cargo.toml b/Cargo.toml index 29af6a1fc43..8956bd69489 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,12 +9,8 @@ license = "MIT OR Apache-2.0" crate-type = ["dylib"] [[test]] -name = "lang_tests_debug" -path = "tests/lang_tests_debug.rs" -harness = false -[[test]] -name = "lang_tests_release" -path = "tests/lang_tests_release.rs" +name = "lang_tests" +path = "tests/lang_tests.rs" harness = false [features] diff --git a/build_system/src/test.rs b/build_system/src/test.rs index 8189e6b3974..3f02df83995 100644 --- a/build_system/src/test.rs +++ b/build_system/src/test.rs @@ -43,6 +43,7 @@ fn get_runners() -> Runners { runners.insert("--extended-regex-tests", ("Run extended regex tests", extended_regex_tests)); runners.insert("--mini-tests", ("Run mini tests", mini_tests)); runners.insert("--cargo-tests", ("Run cargo tests", cargo_tests)); + runners.insert("--no-builtins-tests", ("Test #![no_builtins] attribute", no_builtins_tests)); runners } @@ -317,6 +318,65 @@ fn maybe_run_command_in_vm( Ok(()) } +/// Compile a source file to an object file and check if it contains a memset reference. +fn object_has_memset( + env: &Env, + args: &TestArg, + src_file: &str, + obj_file_name: &str, +) -> Result { + let cargo_target_dir = Path::new(&args.config_info.cargo_target_dir); + let obj_file = cargo_target_dir.join(obj_file_name); + let obj_file_str = obj_file.to_str().expect("obj_file to_str"); + + let mut command = args.config_info.rustc_command_vec(); + command.extend_from_slice(&[ + &src_file, + &"--emit", + &"obj", + &"-O", + &"--target", + &args.config_info.target_triple, + &"-o", + ]); + command.push(&obj_file_str); + run_command_with_env(&command, None, Some(env))?; + + let nm_output = run_command_with_env(&[&"nm", &obj_file_str], None, Some(env))?; + let nm_stdout = String::from_utf8_lossy(&nm_output.stdout); + + Ok(nm_stdout.contains("memset")) +} + +fn no_builtins_tests(env: &Env, args: &TestArg) -> Result<(), String> { + // Test that the #![no_builtins] attribute prevents GCC from replacing + // code patterns (like loops) with calls to builtins (like memset). + // See https://github.com/rust-lang/rustc_codegen_gcc/issues/570 + + // Test 1: WITH #![no_builtins] - memset should NOT be present + println!("[TEST] no_builtins attribute (with #![no_builtins])"); + let has_memset = + object_has_memset(env, args, "tests/no_builtins/no_builtins.rs", "no_builtins_test.o")?; + if has_memset { + return Err("no_builtins test FAILED: Found 'memset' in object file.\n\ + The #![no_builtins] attribute should prevent GCC from replacing \n\ + code patterns with builtin calls." + .to_string()); + } + + // Test 2: WITHOUT #![no_builtins] - memset SHOULD be present + println!("[TEST] no_builtins attribute (without #![no_builtins])"); + let has_memset = + object_has_memset(env, args, "tests/no_builtins/with_builtins.rs", "with_builtins_test.o")?; + if !has_memset { + return Err("no_builtins test FAILED: 'memset' NOT found in object file.\n\ + Without #![no_builtins], GCC should replace the loop with memset." + .to_string()); + } + + Ok(()) +} + fn std_tests(env: &Env, args: &TestArg) -> Result<(), String> { let cargo_target_dir = Path::new(&args.config_info.cargo_target_dir); // FIXME: create a function "display_if_not_quiet" or something along the line. @@ -1248,6 +1308,7 @@ fn run_all(env: &Env, args: &TestArg) -> Result<(), String> { test_libcore(env, args)?; extended_sysroot_tests(env, args)?; cargo_tests(env, args)?; + no_builtins_tests(env, args)?; test_rustc(env, args)?; Ok(()) diff --git a/example/mini_core.rs b/example/mini_core.rs index 87b059526ea..481fe5387e1 100644 --- a/example/mini_core.rs +++ b/example/mini_core.rs @@ -15,6 +15,30 @@ #![no_core] #![allow(dead_code, internal_features, ambiguous_wide_pointer_comparisons)] +#[lang = "pointee_trait"] +pub trait Pointee: PointeeSized { + #[lang = "metadata_type"] + // needed so that layout_of will return `TooGeneric` instead of `Unknown` + // when asked for the layout of `*const T`. Which is important for making + // transmutes between raw pointers (and especially pattern types of raw pointers) + // work. + type Metadata: Copy + Sync + Unpin + Freeze; +} + +#[lang = "dyn_metadata"] +pub struct DynMetadata { + _vtable_ptr: NonNull, + _phantom: PhantomData, +} + +unsafe extern "C" { + /// Opaque type for accessing vtables. + /// + /// Private implementation detail of `DynMetadata::size_of` etc. + /// There is conceptually not actually any Abstract Machine memory behind this pointer. + type VTable; +} + #[no_mangle] unsafe extern "C" fn _Unwind_Resume() { intrinsics::unreachable(); @@ -113,7 +137,7 @@ unsafe impl<'a, T: PointeeSized> Sync for &'a T {} unsafe impl Sync for [u8; 16] {} #[lang = "freeze"] -unsafe auto trait Freeze {} +pub unsafe auto trait Freeze {} unsafe impl Freeze for PhantomData {} unsafe impl Freeze for *const T {} @@ -592,6 +616,13 @@ macro_rules! pattern_type { }; } +impl CoerceUnsized for pattern_type!(*const T is !null) where + T: Unsize +{ +} + +impl, U> DispatchFromDyn for pattern_type!(T is !null) {} + impl CoerceUnsized> for NonNull where T: Unsize {} impl DispatchFromDyn> for NonNull where T: Unsize {} @@ -604,9 +635,9 @@ impl CoerceUnsized> for Unique wh impl DispatchFromDyn> for Unique where T: Unsize {} #[lang = "owned_box"] -pub struct Box(Unique, A); +pub struct Box(Unique, A); -impl, U: ?Sized, A: Allocator> CoerceUnsized> for Box {} +impl, U: ?Sized> CoerceUnsized> for Box {} impl Box { pub fn new(val: T) -> Box { @@ -614,16 +645,27 @@ impl Box { let size = size_of::(); let ptr = libc::malloc(size); intrinsics::copy(&val as *const T as *const u8, ptr, size); - Box(Unique { pointer: NonNull(ptr as *const T), _marker: PhantomData }, Global) + Box( + Unique { + pointer: NonNull(intrinsics::transmute::< + *mut u8, + pattern_type!(*const T is !null), + >(ptr)), + _marker: PhantomData, + }, + Global, + ) } } } -impl Drop for Box { +impl Drop for Box { fn drop(&mut self) { - // inner value is dropped by compiler. + // inner value is dropped by compiler unsafe { - libc::free(self.0.pointer.0 as *mut u8); + libc::free(intrinsics::transmute::( + self.0.pointer.0, + ) as *mut u8); } } } diff --git a/libgccjit.version b/libgccjit.version index abc967702fb..5eef7026046 100644 --- a/libgccjit.version +++ b/libgccjit.version @@ -1 +1 @@ -efdd0a7290c22f5438d7c5380105d353ee3e8518 +6f155cc3f5a2dff33afe6cc3ed6c2e0e605ae6a3 diff --git a/rust-toolchain b/rust-toolchain index 655fa6abbab..56fcfdff1c7 100644 --- a/rust-toolchain +++ b/rust-toolchain @@ -1,3 +1,3 @@ [toolchain] -channel = "nightly-2026-02-14" +channel = "nightly-2026-04-29" components = ["rust-src", "rustc-dev", "llvm-tools-preview"] diff --git a/src/abi.rs b/src/abi.rs index 8277231f16a..2f5c555b702 100644 --- a/src/abi.rs +++ b/src/abi.rs @@ -224,15 +224,8 @@ impl<'gcc, 'tcx> FnAbiGccExt<'gcc, 'tcx> for FnAbi<'tcx, Ty<'tcx>> { fn ptr_to_gcc_type(&self, cx: &CodegenCx<'gcc, 'tcx>) -> Type<'gcc> { // FIXME(antoyo): Should we do something with `FnAbiGcc::fn_attributes`? - let FnAbiGcc { return_type, arguments_type, is_c_variadic, on_stack_param_indices, .. } = - self.gcc_type(cx); - let pointer_type = - cx.context.new_function_pointer_type(None, return_type, &arguments_type, is_c_variadic); - cx.on_stack_params.borrow_mut().insert( - pointer_type.dyncast_function_ptr_type().expect("function ptr type"), - on_stack_param_indices, - ); - pointer_type + let FnAbiGcc { return_type, arguments_type, is_c_variadic, .. } = self.gcc_type(cx); + cx.context.new_function_pointer_type(None, return_type, &arguments_type, is_c_variadic) } #[cfg(feature = "master")] diff --git a/src/asm.rs b/src/asm.rs index 1443aa925f7..5bb65365ad6 100644 --- a/src/asm.rs +++ b/src/asm.rs @@ -12,13 +12,13 @@ use rustc_codegen_ssa::traits::{ }; use rustc_middle::bug; use rustc_middle::ty::Instance; -use rustc_span::Span; +use rustc_span::{DUMMY_SP, Span}; use rustc_target::asm::*; use crate::builder::Builder; use crate::callee::get_fn; use crate::context::CodegenCx; -use crate::errors::UnwindingInlineAsm; +use crate::errors::{NulBytesInAsm, UnwindingInlineAsm}; use crate::type_of::LayoutGccExt; // Rust asm! and GCC Extended Asm semantics differ substantially. @@ -530,8 +530,15 @@ impl<'a, 'gcc, 'tcx> AsmBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tcx> { template_str.push_str(INTEL_SYNTAX_INS); } - // 4. Generate Extended Asm block + // NOTE: GCC's extended asm uses CString which cannot contain nul bytes. + // Emit an error if there are any nul bytes in the template string. + if template_str.contains('\0') { + let err_sp = span.first().copied().unwrap_or(DUMMY_SP); + self.sess().dcx().emit_err(NulBytesInAsm { span: err_sp }); + return; + } + // 4. Generate Extended Asm block let block = self.llbb(); let extended_asm = if let Some(dest) = dest { assert!(!labels.is_empty()); @@ -875,7 +882,7 @@ impl<'gcc, 'tcx> AsmCodegenMethods<'tcx> for CodegenCx<'gcc, 'tcx> { template: &[InlineAsmTemplatePiece], operands: &[GlobalAsmOperandRef<'tcx>], options: InlineAsmOptions, - _line_spans: &[Span], + line_spans: &[Span], ) { let asm_arch = self.tcx.sess.asm_arch.unwrap(); @@ -942,6 +949,13 @@ impl<'gcc, 'tcx> AsmCodegenMethods<'tcx> for CodegenCx<'gcc, 'tcx> { } // NOTE: seems like gcc will put the asm in the wrong section, so set it to .text manually. template_str.push_str("\n.popsection"); + // NOTE: GCC's add_top_level_asm uses CString which cannot contain nul bytes. + // Emit an error if there are any nul bytes in the template string. + if template_str.contains('\0') { + let span = line_spans.first().copied().unwrap_or(DUMMY_SP); + self.tcx.dcx().emit_err(NulBytesInAsm { span }); + return; + } self.context.add_top_level_asm(None, &template_str); } diff --git a/src/base.rs b/src/base.rs index 7d63f430af6..7658b86a3a2 100644 --- a/src/base.rs +++ b/src/base.rs @@ -8,7 +8,8 @@ use rustc_codegen_ssa::ModuleCodegen; use rustc_codegen_ssa::base::maybe_create_entry_wrapper; use rustc_codegen_ssa::mono_item::MonoItemExt; use rustc_codegen_ssa::traits::DebugInfoCodegenMethods; -use rustc_hir::attrs::Linkage; +use rustc_hir::attrs::{AttributeKind, Linkage}; +use rustc_hir::find_attr; use rustc_middle::dep_graph; #[cfg(feature = "master")] use rustc_middle::mono::Visibility; @@ -137,6 +138,17 @@ pub fn compile_codegen_unit( // NOTE: Rust relies on LLVM doing wrapping on overflow. context.add_command_line_option("-fwrapv"); + // NOTE: We need to honor the `#![no_builtins]` attribute to prevent GCC from + // replacing code patterns (like loops) with calls to builtins (like memset). + // The `-fno-tree-loop-distribute-patterns` flag disables the loop distribution pass + // that transforms loops into calls to library functions (memset, memcpy, etc.). + // See GCC handling for more details: + // https://github.com/rust-lang/gcc/blob/efdd0a7290c22f5438d7c5380105d353ee3e8518/gcc/c-family/c-opts.cc#L953 + let crate_attrs = tcx.hir_attrs(rustc_hir::CRATE_HIR_ID); + if find_attr!(crate_attrs, AttributeKind::NoBuiltins) { + context.add_command_line_option("-fno-tree-loop-distribute-patterns"); + } + if let Some(model) = tcx.sess.code_model() { use rustc_target::spec::CodeModel; diff --git a/src/builder.rs b/src/builder.rs index 3cffd862b9b..459f623a7c8 100644 --- a/src/builder.rs +++ b/src/builder.rs @@ -33,6 +33,7 @@ use rustc_span::def_id::DefId; use rustc_target::callconv::FnAbi; use rustc_target::spec::{HasTargetSpec, HasX86AbiOpt, Target, X86Abi}; +use crate::abi::FnAbiGccExt; use crate::common::{SignType, TypeReflection, type_is_pointer}; use crate::context::CodegenCx; use crate::errors; @@ -213,6 +214,7 @@ impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> { _typ: &str, func_ptr: RValue<'gcc>, args: &'b [RValue<'gcc>], + on_stack_param_indices: &FxHashSet, ) -> Cow<'b, [RValue<'gcc>]> { let mut all_args_match = true; let mut param_types = vec![]; @@ -225,11 +227,6 @@ impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> { param_types.push(param); } - let mut on_stack_param_indices = FxHashSet::default(); - if let Some(indices) = self.on_stack_params.borrow().get(&gcc_func) { - on_stack_param_indices.clone_from(indices); - } - if all_args_match { return Cow::Borrowed(args); } @@ -351,19 +348,24 @@ impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> { fn function_ptr_call( &mut self, typ: Type<'gcc>, + fn_abi: Option<&FnAbi<'tcx, Ty<'tcx>>>, mut func_ptr: RValue<'gcc>, args: &[RValue<'gcc>], _funclet: Option<&Funclet>, ) -> RValue<'gcc> { - let gcc_func = match func_ptr.get_type().dyncast_function_ptr_type() { - Some(func) => func, - None => { - // NOTE: due to opaque pointers now being used, we need to cast here. - let new_func_type = typ.dyncast_function_ptr_type().expect("function ptr"); + let func_ptr_type = { + let func_ptr_type = func_ptr.get_type(); + if func_ptr_type != typ { func_ptr = self.context.new_cast(self.location, func_ptr, typ); - new_func_type + typ + } else { + func_ptr_type } }; + let gcc_func = func_ptr_type.dyncast_function_ptr_type().expect("function ptr"); + let on_stack_param_indices = fn_abi + .map(|fn_abi| fn_abi.gcc_type(self.cx).on_stack_param_indices) + .unwrap_or_default(); let func_name = format!("{:?}", func_ptr); let previous_arg_count = args.len(); let orig_args = args; @@ -372,7 +374,7 @@ impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> { llvm::adjust_intrinsic_arguments(self, gcc_func, args.into(), &func_name) }; let args_adjusted = args.len() != previous_arg_count; - let args = self.check_ptr_call("call", func_ptr, &args); + let args = self.check_ptr_call("call", func_ptr, &args, &on_stack_param_indices); // gccjit requires to use the result of functions, even when it's not used. // That's why we assign the result to a local or call add_eval(). @@ -599,7 +601,7 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> { &mut self, typ: Type<'gcc>, fn_attrs: Option<&CodegenFnAttrs>, - _fn_abi: Option<&FnAbi<'tcx, Ty<'tcx>>>, + fn_abi: Option<&FnAbi<'tcx, Ty<'tcx>>>, func: RValue<'gcc>, args: &[RValue<'gcc>], then: Block<'gcc>, @@ -611,7 +613,7 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> { let current_block = self.block; self.block = try_block; - let call = self.call(typ, fn_attrs, None, func, args, None, instance); // FIXME(antoyo): use funclet here? + let call = self.call(typ, fn_attrs, fn_abi, func, args, None, instance); // FIXME(antoyo): use funclet here? self.block = current_block; let return_value = @@ -645,7 +647,7 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> { _funclet: Option<&Funclet>, instance: Option>, ) -> RValue<'gcc> { - let call_site = self.call(typ, fn_attrs, None, func, args, None, instance); + let call_site = self.call(typ, fn_attrs, fn_abi, func, args, None, instance); let condition = self.context.new_rvalue_from_int(self.bool_type, 1); self.llbb().end_with_conditional(self.location, condition, then, catch); if let Some(_fn_abi) = fn_abi { @@ -1773,7 +1775,7 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> { self.function_call(func, args, funclet) } else { // If it's a not function that was defined, it's a function pointer. - self.function_ptr_call(typ, func, args, funclet) + self.function_ptr_call(typ, fn_abi, func, args, funclet) }; if let Some(_fn_abi) = fn_abi { // FIXME(bjorn3): Apply function attributes diff --git a/src/context.rs b/src/context.rs index c7a2b92ac13..e0810a35b04 100644 --- a/src/context.rs +++ b/src/context.rs @@ -1,9 +1,7 @@ use std::cell::{Cell, RefCell}; use std::collections::HashMap; -use gccjit::{ - Block, CType, Context, Function, FunctionPtrType, FunctionType, LValue, Location, RValue, Type, -}; +use gccjit::{Block, CType, Context, Function, FunctionType, LValue, Location, RValue, Type}; use rustc_abi::{Align, HasDataLayout, PointeeInfo, Size, TargetDataLayout, VariantIdx}; use rustc_codegen_ssa::base::wants_msvc_seh; use rustc_codegen_ssa::errors as ssa_errors; @@ -100,8 +98,6 @@ pub struct CodegenCx<'gcc, 'tcx> { RefCell, Option>), RValue<'gcc>>>, // FIXME(antoyo): improve the SSA API to not require those. - /// Mapping from function pointer type to indexes of on stack parameters. - pub on_stack_params: RefCell, FxHashSet>>, /// Mapping from function to indexes of on stack parameters. pub on_stack_function_params: RefCell, FxHashSet>>, @@ -289,7 +285,6 @@ impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> { instances: Default::default(), function_instances: Default::default(), intrinsic_instances: Default::default(), - on_stack_params: Default::default(), on_stack_function_params: Default::default(), vtables: Default::default(), const_globals: Default::default(), diff --git a/src/errors.rs b/src/errors.rs index f5815e72339..de633d3bdde 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -23,3 +23,10 @@ pub(crate) struct LtoBitcodeFromRlib { #[derive(Diagnostic)] #[diag("explicit tail calls with the 'become' keyword are not implemented in the GCC backend")] pub(crate) struct ExplicitTailCallsUnsupported; + +#[derive(Diagnostic)] +#[diag("asm contains a NUL byte")] +pub(crate) struct NulBytesInAsm { + #[primary_span] + pub span: Span, +} diff --git a/src/intrinsic/llvm.rs b/src/intrinsic/llvm.rs index 60e007a25c6..d58697f1bf2 100644 --- a/src/intrinsic/llvm.rs +++ b/src/intrinsic/llvm.rs @@ -1589,6 +1589,7 @@ pub fn intrinsic<'gcc, 'tcx>(name: &str, cx: &CodegenCx<'gcc, 'tcx>) -> Function "llvm.x86.tileloaddrst164" => "__builtin_trap", "llvm.x86.tilezero" => "__builtin_trap", "llvm.x86.tilemovrow" => "__builtin_trap", + "llvm.x86.tilemovrowi" => "__builtin_trap", "llvm.x86.tdpbhf8ps" => "__builtin_trap", "llvm.x86.tdphbf8ps" => "__builtin_trap", "llvm.x86.tdpbf8ps" => "__builtin_trap", @@ -1603,6 +1604,9 @@ pub fn intrinsic<'gcc, 'tcx>(name: &str, cx: &CodegenCx<'gcc, 'tcx>) -> Function "llvm.x86.tcvtrowps2phh" => "__builtin_trap", "llvm.x86.tcvtrowps2phl" => "__builtin_trap", "llvm.x86.tcvtrowd2ps" => "__builtin_trap", + "llvm.x86.tcvtrowd2psi" => "__builtin_trap", + "llvm.x86.tcvtrowps2phhi" => "__builtin_trap", + "llvm.x86.tcvtrowps2phli" => "__builtin_trap", "llvm.x86.tcmmimfp16ps" => "__builtin_trap", "llvm.x86.tcmmrlfp16ps" => "__builtin_trap", diff --git a/src/intrinsic/mod.rs b/src/intrinsic/mod.rs index 2a7c88afe17..da0ef42ed89 100644 --- a/src/intrinsic/mod.rs +++ b/src/intrinsic/mod.rs @@ -4,9 +4,7 @@ mod simd; #[cfg(feature = "master")] use std::iter; -#[cfg(feature = "master")] -use gccjit::Type; -use gccjit::{ComparisonOp, Function, FunctionType, RValue, ToRValue, UnaryOp}; +use gccjit::{ComparisonOp, Function, FunctionType, RValue, ToRValue, Type, UnaryOp}; use rustc_abi::{BackendRepr, HasDataLayout, WrappingRange}; use rustc_codegen_ssa::MemFlags; use rustc_codegen_ssa::base::wants_msvc_seh; @@ -38,6 +36,22 @@ use crate::context::CodegenCx; use crate::intrinsic::simd::generic_simd_intrinsic; use crate::type_of::LayoutGccExt; +fn float_intrinsic<'gcc, 'tcx>( + cx: &CodegenCx<'gcc, 'tcx>, + typ: Type<'gcc>, + name: &str, +) -> Option> { + // GCC doesn't have the intrinsic we want so we use the compiler-builtins one + Some(cx.context.new_function( + None, + FunctionType::Extern, + typ, + &[cx.context.new_parameter(None, typ, "a"), cx.context.new_parameter(None, typ, "b")], + name, + false, + )) +} + fn get_simple_intrinsic<'gcc, 'tcx>( cx: &CodegenCx<'gcc, 'tcx>, name: Symbol, @@ -68,48 +82,19 @@ fn get_simple_intrinsic<'gcc, 'tcx>( // FIXME: calling `fma` from libc without FMA target feature uses expensive software emulation sym::fmuladdf32 => "fmaf", // FIXME: use gcc intrinsic analogous to llvm.fmuladd.f32 sym::fmuladdf64 => "fma", // FIXME: use gcc intrinsic analogous to llvm.fmuladd.f64 - sym::minimumf32 => "fminimumf", - sym::minimumf64 => "fminimum", - sym::minimumf128 => { - // GCC doesn't have the intrinsic we want so we use the compiler-builtins one - // https://docs.rs/compiler_builtins/latest/compiler_builtins/math/full_availability/fn.fminimumf128.html - let f128_type = cx.type_f128(); - return Some(cx.context.new_function( - None, - FunctionType::Extern, - f128_type, - &[ - cx.context.new_parameter(None, f128_type, "a"), - cx.context.new_parameter(None, f128_type, "b"), - ], - "fminimumf128", - false, - )); - } - sym::maximumf32 => "fmaximumf", - sym::maximumf64 => "fmaximum", - sym::maximumf128 => { - // GCC doesn't have the intrinsic we want so we use the compiler-builtins one - // https://docs.rs/compiler_builtins/latest/compiler_builtins/math/full_availability/fn.fmaximumf128.html - let f128_type = cx.type_f128(); - return Some(cx.context.new_function( - None, - FunctionType::Extern, - f128_type, - &[ - cx.context.new_parameter(None, f128_type, "a"), - cx.context.new_parameter(None, f128_type, "b"), - ], - "fmaximumf128", - false, - )); - } + sym::minimumf32 => return float_intrinsic(cx, cx.type_f32(), "fminimumf"), + sym::minimumf64 => return float_intrinsic(cx, cx.type_f64(), "fminimum"), + sym::minimumf128 => return float_intrinsic(cx, cx.type_f128(), "fminimumf128"), + sym::maximumf32 => return float_intrinsic(cx, cx.type_f32(), "fmaximumf"), + sym::maximumf64 => return float_intrinsic(cx, cx.type_f64(), "fmaximum"), + sym::maximumf128 => return float_intrinsic(cx, cx.type_f128(), "fmaximumf128"), sym::copysignf32 => "copysignf", sym::copysignf64 => "copysign", sym::floorf32 => "floorf", sym::floorf64 => "floor", sym::ceilf32 => "ceilf", sym::ceilf64 => "ceil", + sym::powf128 => return float_intrinsic(cx, cx.type_f128(), "powf128"), sym::truncf32 => "truncf", sym::truncf64 => "trunc", // We match the LLVM backend and lower this to `rint`. @@ -123,72 +108,6 @@ fn get_simple_intrinsic<'gcc, 'tcx>( Some(cx.context.get_builtin_function(gcc_name)) } -// FIXME(antoyo): We can probably remove these and use the fallback intrinsic implementation. -fn get_simple_function<'gcc, 'tcx>( - cx: &CodegenCx<'gcc, 'tcx>, - name: Symbol, -) -> Option> { - let (return_type, parameters, func_name) = match name { - sym::minimumf32 => { - let parameters = [ - cx.context.new_parameter(None, cx.float_type, "a"), - cx.context.new_parameter(None, cx.float_type, "b"), - ]; - (cx.float_type, parameters, "fminimumf") - } - sym::minimumf64 => { - let parameters = [ - cx.context.new_parameter(None, cx.double_type, "a"), - cx.context.new_parameter(None, cx.double_type, "b"), - ]; - (cx.double_type, parameters, "fminimum") - } - sym::minimumf128 => { - let f128_type = cx.type_f128(); - // GCC doesn't have the intrinsic we want so we use the compiler-builtins one - // https://docs.rs/compiler_builtins/latest/compiler_builtins/math/full_availability/fn.fminimumf128.html - let parameters = [ - cx.context.new_parameter(None, f128_type, "a"), - cx.context.new_parameter(None, f128_type, "b"), - ]; - (f128_type, parameters, "fminimumf128") - } - sym::maximumf32 => { - let parameters = [ - cx.context.new_parameter(None, cx.float_type, "a"), - cx.context.new_parameter(None, cx.float_type, "b"), - ]; - (cx.float_type, parameters, "fmaximumf") - } - sym::maximumf64 => { - let parameters = [ - cx.context.new_parameter(None, cx.double_type, "a"), - cx.context.new_parameter(None, cx.double_type, "b"), - ]; - (cx.double_type, parameters, "fmaximum") - } - sym::maximumf128 => { - let f128_type = cx.type_f128(); - // GCC doesn't have the intrinsic we want so we use the compiler-builtins one - // https://docs.rs/compiler_builtins/latest/compiler_builtins/math/full_availability/fn.fmaximumf128.html - let parameters = [ - cx.context.new_parameter(None, f128_type, "a"), - cx.context.new_parameter(None, f128_type, "b"), - ]; - (f128_type, parameters, "fmaximumf128") - } - _ => return None, - }; - Some(cx.context.new_function( - None, - FunctionType::Extern, - return_type, - ¶meters, - func_name, - false, - )) -} - fn get_simple_function_f128<'gcc, 'tcx>( span: Span, cx: &CodegenCx<'gcc, 'tcx>, @@ -198,7 +117,12 @@ fn get_simple_function_f128<'gcc, 'tcx>( let func_name = match name { sym::ceilf128 => "ceilf128", sym::fabs => "fabsf128", + sym::expf128 => "expf128", + sym::exp2f128 => "exp2f128", sym::floorf128 => "floorf128", + sym::logf128 => "logf128", + sym::log2f128 => "log2f128", + sym::log10f128 => "log10f128", sym::truncf128 => "truncf128", sym::roundf128 => "roundf128", sym::round_ties_even_f128 => "roundevenf128", @@ -215,6 +139,24 @@ fn get_simple_function_f128<'gcc, 'tcx>( ) } +fn generic_f16_builtin<'gcc, 'tcx>( + cx: &CodegenCx<'gcc, 'tcx>, + name: Symbol, + args: &[OperandRef<'tcx, RValue<'gcc>>], +) -> RValue<'gcc> { + let f32_type = cx.type_f32(); + let builtin_name = match name { + sym::fabs => "fabsf", + _ => unreachable!(), + }; + + let func = cx.context.get_builtin_function(builtin_name); + let args: Vec<_> = + args.iter().map(|arg| cx.context.new_cast(None, arg.immediate(), f32_type)).collect(); + let result = cx.context.new_call(None, func, &args); + cx.context.new_cast(None, result, cx.type_f16()) +} + fn f16_builtin<'gcc, 'tcx>( cx: &CodegenCx<'gcc, 'tcx>, name: Symbol, @@ -224,17 +166,15 @@ fn f16_builtin<'gcc, 'tcx>( let builtin_name = match name { sym::ceilf16 => "__builtin_ceilf", sym::copysignf16 => "__builtin_copysignf", + sym::expf16 => "expf", + sym::exp2f16 => "exp2f", sym::fabs => "fabsf", sym::floorf16 => "__builtin_floorf", sym::fmaf16 => "fmaf", + sym::logf16 => "logf", + sym::log2f16 => "log2f", + sym::log10f16 => "log10f", sym::powf16 => "__builtin_powf", - sym::powif16 => { - let func = cx.context.get_builtin_function("__builtin_powif"); - let arg0 = cx.context.new_cast(None, args[0].immediate(), f32_type); - let args = [arg0, args[1].immediate()]; - let result = cx.context.new_call(None, func, &args); - return cx.context.new_cast(None, result, cx.type_f16()); - } sym::roundf16 => "__builtin_roundf", sym::round_ties_even_f16 => "__builtin_rintf", sym::sqrtf16 => "__builtin_sqrtf", @@ -264,7 +204,6 @@ impl<'a, 'gcc, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tc let fn_args = instance.args; let simple = get_simple_intrinsic(self, name); - let simple_func = get_simple_function(self, name); let value = match name { _ if simple.is_some() => { @@ -275,8 +214,26 @@ impl<'a, 'gcc, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tc &args.iter().map(|arg| arg.immediate()).collect::>(), ) } - _ if simple_func.is_some() => { - let func = simple_func.expect("simple function"); + // TODO(antoyo): We can probably remove these and use the fallback intrinsic implementation. + sym::minimumf32 | sym::minimumf64 | sym::maximumf32 | sym::maximumf64 => { + let (ty, func_name) = match name { + sym::minimumf32 => (self.cx.float_type, "fminimumf"), + sym::maximumf32 => (self.cx.float_type, "fmaximumf"), + sym::minimumf64 => (self.cx.double_type, "fminimum"), + sym::maximumf64 => (self.cx.double_type, "fmaximum"), + _ => unreachable!(), + }; + let func = self.cx.context.new_function( + None, + FunctionType::Extern, + ty, + &[ + self.cx.context.new_parameter(None, ty, "a"), + self.cx.context.new_parameter(None, ty, "b"), + ], + func_name, + false, + ); self.cx.context.new_call( self.location, func, @@ -285,10 +242,14 @@ impl<'a, 'gcc, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tc } sym::ceilf16 | sym::copysignf16 + | sym::expf16 + | sym::exp2f16 | sym::floorf16 | sym::fmaf16 + | sym::logf16 + | sym::log2f16 + | sym::log10f16 | sym::powf16 - | sym::powif16 | sym::roundf16 | sym::round_ties_even_f16 | sym::sqrtf16 @@ -299,6 +260,11 @@ impl<'a, 'gcc, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tc | sym::roundf128 | sym::round_ties_even_f128 | sym::sqrtf128 + | sym::expf128 + | sym::exp2f128 + | sym::logf128 + | sym::log2f128 + | sym::log10f128 if self.cx.supports_f128_type => { let func = get_simple_function_f128(span, self, name); @@ -347,6 +313,13 @@ impl<'a, 'gcc, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tc &args.iter().map(|arg| arg.immediate()).collect::>(), ) } + sym::powif16 => { + let func = self.cx.context.get_builtin_function("__builtin_powif"); + let arg0 = self.cx.context.new_cast(None, args[0].immediate(), self.cx.type_f32()); + let args = [arg0, args[1].immediate()]; + let result = self.cx.context.new_call(None, func, &args); + self.cx.context.new_cast(None, result, self.cx.type_f16()) + } sym::powif128 => { let f128_type = self.cx.type_f128(); let func = self.cx.context.new_function( @@ -490,7 +463,7 @@ impl<'a, 'gcc, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tc span_bug!(span, "expected float type for fabs intrinsic: {:?}", ty); }; let func = match float_ty { - ty::FloatTy::F16 => break 'fabs f16_builtin(self, name, args), + ty::FloatTy::F16 => break 'fabs generic_f16_builtin(self, name, args), ty::FloatTy::F32 => self.context.get_builtin_function("fabsf"), ty::FloatTy::F64 => self.context.get_builtin_function("fabs"), ty::FloatTy::F128 => get_simple_function_f128(span, self, name), diff --git a/tests/compile/asm_nul_byte.rs b/tests/compile/asm_nul_byte.rs new file mode 100644 index 00000000000..fd5a4f98aa2 --- /dev/null +++ b/tests/compile/asm_nul_byte.rs @@ -0,0 +1,15 @@ +// Compiler: +// status: error +// stderr: +// error: asm contains a NUL byte +// ... + +// Test that inline asm containing a NUL byte emits an error. + +use std::arch::asm; + +fn main() { + unsafe { + asm!("\0"); + } +} diff --git a/tests/run/call-llvm-intrinsics.rs b/tests/compile/call-llvm-intrinsics.rs similarity index 91% rename from tests/run/call-llvm-intrinsics.rs rename to tests/compile/call-llvm-intrinsics.rs index 86e041c3a2f..4c790994c77 100644 --- a/tests/run/call-llvm-intrinsics.rs +++ b/tests/compile/call-llvm-intrinsics.rs @@ -1,12 +1,10 @@ // Compiler: -// -// Run-time: -// status: 0 // FIXME: Remove this test once rustc's `./tests/codegen/riscv-abi/call-llvm-intrinsics.rs` // stops ignoring GCC backend. #![feature(link_llvm_intrinsics)] +#![crate_type = "lib"] #![allow(internal_features)] struct A; @@ -32,7 +30,3 @@ pub fn do_call() { sqrt(4.0); } } - -fn main() { - do_call(); -} diff --git a/tests/compile/fn_ptr_transmute_ignored_arg.rs b/tests/compile/fn_ptr_transmute_ignored_arg.rs new file mode 100644 index 00000000000..a4947da248e --- /dev/null +++ b/tests/compile/fn_ptr_transmute_ignored_arg.rs @@ -0,0 +1,12 @@ +// Compiler: + +// Regression test for + +#![crate_type = "lib"] + +#[unsafe(no_mangle)] +extern "C" fn third(_a: usize, b: usize, c: usize) { + let throw_away_f: fn((), usize, usize) = + unsafe { std::mem::transmute(third as extern "C" fn(_, _, _)) }; + throw_away_f((), 2, 3) +} diff --git a/tests/compile/global_asm_nul_byte.rs b/tests/compile/global_asm_nul_byte.rs new file mode 100644 index 00000000000..12d647c733f --- /dev/null +++ b/tests/compile/global_asm_nul_byte.rs @@ -0,0 +1,13 @@ +// Compiler: +// status: error +// stderr: +// error: asm contains a NUL byte +// ... + +// Test that global_asm containing a NUL byte emits an error. + +#![crate_type = "lib"] + +use std::arch::global_asm; + +global_asm!("\0"); diff --git a/tests/compile/log.rs b/tests/compile/log.rs new file mode 100644 index 00000000000..56758b0d3e5 --- /dev/null +++ b/tests/compile/log.rs @@ -0,0 +1,12 @@ +// Compiler: + +extern "C" { + fn log(message_data: u32, message_size: u32); +} + +pub fn main() { + let message = "Hello, world!"; + unsafe { + log(message.as_ptr() as u32, message.len() as u32); + } +} diff --git a/tests/compile/naked_asm_nul_byte.rs b/tests/compile/naked_asm_nul_byte.rs new file mode 100644 index 00000000000..85be3704a5e --- /dev/null +++ b/tests/compile/naked_asm_nul_byte.rs @@ -0,0 +1,17 @@ +// Compiler: +// status: error +// stderr: +// ... +// error: asm contains a NUL byte +// ... + +// Test that naked_asm containing a NUL byte emits an error. + +#![crate_type = "lib"] + +use std::arch::naked_asm; + +#[unsafe(naked)] +pub extern "C" fn nul_byte_naked() { + naked_asm!("\0") +} diff --git a/tests/run/simd-ffi.rs b/tests/compile/simd-ffi.rs similarity index 92% rename from tests/run/simd-ffi.rs rename to tests/compile/simd-ffi.rs index 67cc2e5b96e..56172ddc7c6 100644 --- a/tests/run/simd-ffi.rs +++ b/tests/compile/simd-ffi.rs @@ -1,12 +1,11 @@ // Compiler: -// -// Run-time: -// status: 0 // FIXME: Remove this test once stops // ignoring GCC backend. #![allow(internal_features, non_camel_case_types)] +#![crate_type = "lib"] + // we can compile to a variety of platforms, because we don't need // cross-compiled standard libraries. #![feature(no_core, auto_traits)] @@ -93,10 +92,3 @@ macro_rules! Copy { macro_rules! derive { () => {}; } - -#[lang = "start"] -fn start(_main: fn() -> T, _argc: isize, _argv: *const *const u8, _sigpipe: u8) -> isize { - 0 -} - -fn main() {} diff --git a/tests/failing-lto-tests.txt b/tests/failing-lto-tests.txt index c45fc077658..4c62c35a512 100644 --- a/tests/failing-lto-tests.txt +++ b/tests/failing-lto-tests.txt @@ -1,4 +1,3 @@ -tests/ui/lto/all-crates.rs tests/ui/lto/debuginfo-lto-alloc.rs tests/ui/panic-runtime/lto-unwind.rs tests/ui/uninhabited/uninhabited-transparent-return-abi.rs diff --git a/tests/failing-ui-tests.txt b/tests/failing-ui-tests.txt index 8589929d2fb..e8a26a90890 100644 --- a/tests/failing-ui-tests.txt +++ b/tests/failing-ui-tests.txt @@ -47,7 +47,6 @@ tests/ui/sanitizer/cfi/virtual-auto.rs tests/ui/sanitizer/cfi/sized-associated-ty.rs tests/ui/sanitizer/cfi/can-reveal-opaques.rs tests/ui/sanitizer/kcfi-mangling.rs -tests/ui/backtrace/dylib-dep.rs tests/ui/delegation/fn-header.rs tests/ui/consts/const-eval/parse_ints.rs tests/ui/simd/intrinsic/generic-as.rs @@ -99,3 +98,16 @@ tests/ui/eii/privacy1.rs tests/ui/eii/default/call_impl.rs tests/ui/c-variadic/copy.rs tests/ui/asm/x86_64/global_asm_escape.rs +tests/ui/lto/all-crates.rs +tests/ui/consts/const-eval/c-variadic.rs +tests/ui/eii/default/call_default_panics.rs +tests/ui/explicit-tail-calls/indirect.rs +tests/ui/traits/inheritance/self-in-supertype.rs +tests/ui/fmt/fmt_debug/shallow.rs +tests/ui/c-variadic/roundtrip.rs +tests/ui/eii/eii_impl_with_contract.rs +tests/ui/eii/static/cross_crate_decl.rs +tests/ui/eii/static/cross_crate_def.rs +tests/ui/eii/static/same_address.rs +tests/ui/eii/static/simple.rs +tests/ui/explicit-tail-calls/default-trait-method.rs diff --git a/tests/lang_tests.rs b/tests/lang_tests.rs new file mode 100644 index 00000000000..3d1d04661c8 --- /dev/null +++ b/tests/lang_tests.rs @@ -0,0 +1,237 @@ +#![allow(clippy::uninlined_format_args)] + +use std::env::current_dir; +use std::path::{Path, PathBuf}; +use std::process::Command; + +use lang_tester::LangTester; +use tempfile::TempDir; + +fn compile_and_run_cmds( + compiler_args: Vec, + test_target: &Option, + exe: &Path, + test_mode: TestMode, +) -> Vec<(&'static str, Command)> { + let mut compiler = Command::new("rustc"); + compiler.args(compiler_args); + + // Test command 2: run `tempdir/x`. + if test_target.is_some() { + let mut env_path = std::env::var("PATH").unwrap_or_default(); + // TODO(antoyo): find a better way to add the PATH necessary locally. + env_path = format!("/opt/m68k-unknown-linux-gnu/bin:{}", env_path); + compiler.env("PATH", env_path); + + let mut commands = vec![("Compiler", compiler)]; + if test_mode.should_run() { + let vm_parent_dir = std::env::var("CG_GCC_VM_DIR") + .map(PathBuf::from) + .unwrap_or_else(|_| std::env::current_dir().unwrap()); + let vm_dir = "vm"; + let exe_filename = exe.file_name().unwrap(); + let vm_home_dir = vm_parent_dir.join(vm_dir).join("home"); + let vm_exe_path = vm_home_dir.join(exe_filename); + // FIXME(antoyo): panicking here makes the test pass. + let inside_vm_exe_path = PathBuf::from("/home").join(exe_filename); + + let mut copy = Command::new("sudo"); + copy.arg("cp"); + copy.args([exe, &vm_exe_path]); + + let mut runtime = Command::new("sudo"); + runtime.args(["chroot", vm_dir, "qemu-m68k-static"]); + runtime.arg(inside_vm_exe_path); + runtime.current_dir(vm_parent_dir); + + commands.push(("Copy", copy)); + commands.push(("Run-time", runtime)); + } + commands + } else { + let mut commands = vec![("Compiler", compiler)]; + if test_mode.should_run() { + let runtime = Command::new(exe); + commands.push(("Run-time", runtime)); + } + commands + } +} + +#[derive(Clone, Copy)] +enum BuildMode { + Debug, + Release, +} + +impl BuildMode { + fn is_debug(self) -> bool { + matches!(self, Self::Debug) + } +} + +#[derive(Clone, Copy)] +enum TestMode { + Compile, + CompileAndRun, +} + +impl TestMode { + fn should_run(self) -> bool { + matches!(self, Self::CompileAndRun) + } +} + +fn build_test_runner( + tempdir: PathBuf, + current_dir: String, + build_mode: BuildMode, + test_kind: &str, + test_dir: &str, + test_mode: TestMode, + files_to_ignore_on_m68k: &'static [&'static str], +) { + fn rust_filter(path: &Path) -> bool { + path.is_file() && path.extension().expect("extension").to_str().expect("to_str") == "rs" + } + + #[cfg(feature = "master")] + fn filter(filename: &Path) -> bool { + rust_filter(filename) + } + + #[cfg(not(feature = "master"))] + fn filter(filename: &Path) -> bool { + if let Some(filename) = filename.to_str() + && filename.ends_with("gep.rs") + { + return false; + } + rust_filter(filename) + } + + println!("=== {test_kind} tests ==="); + + // TODO(antoyo): find a way to send this via a cli argument. + let test_target = std::env::var("CG_GCC_TEST_TARGET").ok(); + let test_target_filter = test_target.clone(); + + LangTester::new() + .test_dir(test_dir) + .test_path_filter(move |filename| { + if !filter(filename) { + return false; + } + if test_target_filter.is_some() + && let Some(filename) = filename.file_name() + && let Some(filename) = filename.to_str() + && files_to_ignore_on_m68k.contains(&filename) + { + return false; + } + true + }) + .test_extract(|path| { + std::fs::read_to_string(path) + .expect("read file") + .lines() + .skip_while(|l| !l.starts_with("//")) + .take_while(|l| l.starts_with("//")) + .map(|l| &l[2..]) + .collect::>() + .join("\n") + }) + .test_cmds(move |path| { + // Test command 1: Compile `x.rs` into `tempdir/x`. + let mut exe = PathBuf::new(); + exe.push(&tempdir); + exe.push(path.file_stem().expect("file_stem")); + let mut compiler_args = vec![ + format!("-Zcodegen-backend={}/target/debug/librustc_codegen_gcc.so", current_dir), + "--sysroot".into(), + format!("{}/build/build_sysroot/sysroot/", current_dir), + "-C".into(), + "link-arg=-lc".into(), + "--extern".into(), + "mini_core=target/out/libmini_core.rlib".into(), + "-o".into(), + exe.to_str().expect("to_str").into(), + path.to_str().expect("to_str").into(), + ]; + + if let Some(ref target) = test_target { + compiler_args.extend_from_slice(&["--target".into(), target.into()]); + + let linker = format!("{}-gcc", target); + compiler_args.push(format!("-Clinker={}", linker)); + } + + if let Some(flags) = option_env!("TEST_FLAGS") { + for flag in flags.split_whitespace() { + compiler_args.push(flag.into()); + } + } + + if build_mode.is_debug() { + compiler_args + .extend_from_slice(&["-C".to_string(), "llvm-args=sanitize-undefined".into()]); + if test_target.is_none() { + // m68k doesn't have lubsan for now + compiler_args.extend_from_slice(&["-C".into(), "link-args=-lubsan".into()]); + } + } else { + compiler_args.extend_from_slice(&[ + "-C".into(), + "opt-level=3".into(), + "-C".into(), + "lto=no".into(), + ]); + } + + compile_and_run_cmds(compiler_args, &test_target, &exe, test_mode) + }) + .run(); +} + +fn compile_tests(tempdir: PathBuf, current_dir: String) { + build_test_runner( + tempdir, + current_dir, + BuildMode::Debug, + "lang compile", + "tests/compile", + TestMode::Compile, + &["simd-ffi.rs", "asm_nul_byte.rs", "global_asm_nul_byte.rs", "naked_asm_nul_byte.rs"], + ); +} + +fn run_tests(tempdir: PathBuf, current_dir: String) { + build_test_runner( + tempdir.clone(), + current_dir.clone(), + BuildMode::Debug, + "[DEBUG] lang run", + "tests/run", + TestMode::CompileAndRun, + &[], + ); + build_test_runner( + tempdir, + current_dir.to_string(), + BuildMode::Release, + "[RELEASE] lang run", + "tests/run", + TestMode::CompileAndRun, + &[], + ); +} + +fn main() { + let tempdir = TempDir::new().expect("temp dir"); + let current_dir = current_dir().expect("current dir"); + let current_dir = current_dir.to_str().expect("current dir").to_string(); + + let tempdir_path: PathBuf = tempdir.as_ref().into(); + compile_tests(tempdir_path.clone(), current_dir.clone()); + run_tests(tempdir_path, current_dir); +} diff --git a/tests/lang_tests_debug.rs b/tests/lang_tests_debug.rs deleted file mode 100644 index 96bd74883ff..00000000000 --- a/tests/lang_tests_debug.rs +++ /dev/null @@ -1,5 +0,0 @@ -mod lang_tests_common; - -fn main() { - lang_tests_common::main_inner(lang_tests_common::Profile::Debug); -} diff --git a/tests/lang_tests_release.rs b/tests/lang_tests_release.rs deleted file mode 100644 index 35d5d60c33e..00000000000 --- a/tests/lang_tests_release.rs +++ /dev/null @@ -1,5 +0,0 @@ -mod lang_tests_common; - -fn main() { - lang_tests_common::main_inner(lang_tests_common::Profile::Release); -} diff --git a/tests/no_builtins/no_builtins.rs b/tests/no_builtins/no_builtins.rs new file mode 100644 index 00000000000..c332581d937 --- /dev/null +++ b/tests/no_builtins/no_builtins.rs @@ -0,0 +1,24 @@ +// Test that the #![no_builtins] attribute is honored. +// When this attribute is present, GCC should not replace code patterns +// (like loops) with calls to builtins (like memset). +// See https://github.com/rust-lang/rustc_codegen_gcc/issues/570 +// +// This test is verified by the build system test `--no-builtins-tests` which +// compiles this file and checks that `memset` is not referenced in the object file. + +#![no_std] +#![no_builtins] +#![crate_type = "lib"] + +// This function implements a byte-setting loop that GCC would typically +// optimize into a memset call. With #![no_builtins], GCC should preserve +// the loop instead of replacing it with a builtin call. +#[no_mangle] +#[inline(never)] +pub unsafe fn set_bytes(mut s: *mut u8, c: u8, n: usize) { + let end = s.add(n); + while s < end { + *s = c; + s = s.add(1); + } +} diff --git a/tests/no_builtins/with_builtins.rs b/tests/no_builtins/with_builtins.rs new file mode 100644 index 00000000000..30271978a1b --- /dev/null +++ b/tests/no_builtins/with_builtins.rs @@ -0,0 +1,21 @@ +// Test that without #![no_builtins], GCC DOES replace code patterns with builtins. +// This is the counterpart to no_builtins.rs - we verify that memset IS emitted +// when the no_builtins attribute is NOT present. +// +// This test is verified by the build system test `--no-builtins-tests` which +// compiles this file and checks that `memset` IS referenced in the object file. + +#![no_std] +#![crate_type = "lib"] + +// This function implements a byte-setting loop that GCC should optimize +// into a memset call when no_builtins is NOT set. +#[no_mangle] +#[inline(never)] +pub unsafe fn set_bytes(mut s: *mut u8, c: u8, n: usize) { + let end = s.add(n); + while s < end { + *s = c; + s = s.add(1); + } +} diff --git a/tests/run/asm.rs b/tests/run/asm.rs index 9b15a28d829..01775c92ffc 100644 --- a/tests/run/asm.rs +++ b/tests/run/asm.rs @@ -213,7 +213,6 @@ fn asm() { core::arch::asm!( "", out("al") _, - out("bl") _, out("cl") _, out("dl") _, out("sil") _, diff --git a/tests/run/core-float.rs b/tests/run/core-float.rs new file mode 100644 index 00000000000..195378b9d3c --- /dev/null +++ b/tests/run/core-float.rs @@ -0,0 +1,78 @@ +// Compiler: +// +// Run-time: +// status: 0 + +// TODO: remove these tests (extracted from libcore) when we run the libcore tests in the CI of the +// Rust repo. + +#![feature(core_intrinsics)] + +use std::f32::consts; +use std::intrinsics; + +const EXP_APPROX: Float = 1e-6; +const ZERO: Float = 0.0; +const ONE: Float = 1.0; + +macro_rules! assert_biteq { + ($left:expr, $right:expr $(,)?) => {{ + let l = $left; + let r = $right; + + // Hack to coerce left and right to the same type + let mut _eq_ty = l; + _eq_ty = r; + + // Hack to get the width from a value + assert!(l.to_bits() == r.to_bits()); + }}; +} + +macro_rules! assert_approx_eq { + ($a:expr, $b:expr $(,)?) => {{ assert_approx_eq!($a, $b, $crate::num::floats::lim_for_ty($a)) }}; + ($a:expr, $b:expr, $lim:expr) => {{ + let (a, b) = (&$a, &$b); + let diff = (*a - *b).abs(); + assert!(diff <= $lim,); + }}; +} + +type Float = f32; +const fn flt(x: Float) -> Float { + x +} + +fn test_exp() { + assert_biteq!(1.0, flt(0.0).exp()); + assert_approx_eq!(consts::E, flt(1.0).exp(), EXP_APPROX); + assert_approx_eq!(148.41315910257660342111558004055227962348775, flt(5.0).exp(), EXP_APPROX); + + let inf: Float = Float::INFINITY; + let neg_inf: Float = Float::NEG_INFINITY; + let nan: Float = Float::NAN; + assert_biteq!(inf, inf.exp()); + assert_biteq!(0.0, neg_inf.exp()); + assert!(nan.exp().is_nan()); +} + +#[inline(never)] +fn my_abs(num: f32) -> f32 { + unsafe { intrinsics::fabs(num) } +} + +fn test_abs() { + assert_biteq!(Float::INFINITY.abs(), Float::INFINITY); + assert_biteq!(ONE.abs(), ONE); + assert_biteq!(ZERO.abs(), ZERO); + assert_biteq!((-ZERO).abs(), ZERO); + assert_biteq!((-ONE).abs(), ONE); + assert_biteq!(Float::NEG_INFINITY.abs(), Float::INFINITY); + assert_biteq!((ONE / Float::NEG_INFINITY).abs(), ZERO); + assert!(Float::NAN.abs().is_nan()); +} + +fn main() { + test_abs(); + test_exp(); +} From f81e070c6991742479381511300e259eb304d311 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Wed, 29 Apr 2026 23:41:56 +0200 Subject: [PATCH 04/10] Fix tidy errors in cg_gcc --- src/intrinsic/mod.rs | 2 +- tests/lang_tests.rs | 4 ++-- tests/run/core-float.rs | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/intrinsic/mod.rs b/src/intrinsic/mod.rs index da0ef42ed89..d823e209fd7 100644 --- a/src/intrinsic/mod.rs +++ b/src/intrinsic/mod.rs @@ -214,7 +214,7 @@ impl<'a, 'gcc, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tc &args.iter().map(|arg| arg.immediate()).collect::>(), ) } - // TODO(antoyo): We can probably remove these and use the fallback intrinsic implementation. + // FIXME(antoyo): We can probably remove these and use the fallback intrinsic implementation. sym::minimumf32 | sym::minimumf64 | sym::maximumf32 | sym::maximumf64 => { let (ty, func_name) = match name { sym::minimumf32 => (self.cx.float_type, "fminimumf"), diff --git a/tests/lang_tests.rs b/tests/lang_tests.rs index 3d1d04661c8..6afd54e1c3f 100644 --- a/tests/lang_tests.rs +++ b/tests/lang_tests.rs @@ -19,7 +19,7 @@ fn compile_and_run_cmds( // Test command 2: run `tempdir/x`. if test_target.is_some() { let mut env_path = std::env::var("PATH").unwrap_or_default(); - // TODO(antoyo): find a better way to add the PATH necessary locally. + // FIXME(antoyo): find a better way to add the PATH necessary locally. env_path = format!("/opt/m68k-unknown-linux-gnu/bin:{}", env_path); compiler.env("PATH", env_path); @@ -112,7 +112,7 @@ fn build_test_runner( println!("=== {test_kind} tests ==="); - // TODO(antoyo): find a way to send this via a cli argument. + // FIXME(antoyo): find a way to send this via a cli argument. let test_target = std::env::var("CG_GCC_TEST_TARGET").ok(); let test_target_filter = test_target.clone(); diff --git a/tests/run/core-float.rs b/tests/run/core-float.rs index 195378b9d3c..38fa11a69b6 100644 --- a/tests/run/core-float.rs +++ b/tests/run/core-float.rs @@ -3,7 +3,7 @@ // Run-time: // status: 0 -// TODO: remove these tests (extracted from libcore) when we run the libcore tests in the CI of the +// FIXME: remove these tests (extracted from libcore) when we run the libcore tests in the CI of the // Rust repo. #![feature(core_intrinsics)] From f992a3d28240740858674ecdabf40cd41c3f801f Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Thu, 30 Apr 2026 14:18:33 +0000 Subject: [PATCH 05/10] Pass Session to optimize_and_codegen_fat_lto This is necessary to fix incremental LTO in cg_gcc as well as to do some LTO refactorings I want to do. The actual fix for cg_gcc will be done on the cg_gcc repo to test it in CI. --- src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index d50968bad25..4be25b3fb09 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -430,8 +430,8 @@ impl WriteBackendMethods for GccCodegenBackend { } fn optimize_and_codegen_fat_lto( + sess: &Session, cgcx: &CodegenContext, - prof: &SelfProfilerRef, shared_emitter: &SharedEmitter, _tm_factory: TargetMachineFactoryFn, // FIXME(bjorn3): Limit LTO exports to these symbols @@ -439,7 +439,7 @@ impl WriteBackendMethods for GccCodegenBackend { each_linked_rlib_for_lto: &[PathBuf], modules: Vec>, ) -> CompiledModule { - back::lto::run_fat(cgcx, prof, shared_emitter, each_linked_rlib_for_lto, modules) + back::lto::run_fat(cgcx, &sess.prof, shared_emitter, each_linked_rlib_for_lto, modules) } fn run_thin_lto( From 3d74e8587afb2ac59dde995230cd336297030aef Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Mon, 13 Apr 2026 14:40:23 +1000 Subject: [PATCH 06/10] Invert dependency between `rustc_error_messages` and `rustc_ast*`. `rustc_error_messages` currently depends on `rustc_ast`/`rustc_ast_pretty`. This is odd, because `rustc_error_messages` feels like a very low-level module but `rustc_ast`/`rustc_ast_pretty` do not. The reason is that a few AST types impl `IntoDiagArg` via pretty-printing. `rustc_error_messages` can define `IntoDiagArg` and then impl it for the AST types. But if we invert the dependency we hit a problem with the orphan rule: `rustc_ast` must impl `IntoDiagArg` for the AST types, but that requires calling pretty-printing code which is in `rustc_ast_pretty`, a downstream crate. This commit avoids this problem by just removing the `IntoDiagArg` impls for these AST types. There aren't that many of them, and we can just use `String` in the relevant error structs and use the pretty printer in the downstream crates that construct the error structs. There are plenty of existing examples where `String` is used in error structs. There is now no dependency between `rustc_ast*` and `rustc_error_messages`. --- src/intrinsic/simd.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/intrinsic/simd.rs b/src/intrinsic/simd.rs index bdb8316f396..a32592b45e5 100644 --- a/src/intrinsic/simd.rs +++ b/src/intrinsic/simd.rs @@ -828,7 +828,7 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>( return_error!(InvalidMonomorphization::FloatingPointVector { span, name, - f_ty: *f, + f_ty: f.name_str().to_string(), in_ty }); } From c32743a96b3d1d5dd948c43050420c0aafea3e36 Mon Sep 17 00:00:00 2001 From: mehdiakiki Date: Sun, 5 Apr 2026 19:59:30 -0400 Subject: [PATCH 07/10] Add rlib digest to identify Rust object files --- src/back/lto.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/back/lto.rs b/src/back/lto.rs index 401d4c244d5..4a46c59e81f 100644 --- a/src/back/lto.rs +++ b/src/back/lto.rs @@ -24,9 +24,10 @@ use std::path::{Path, PathBuf}; use gccjit::OutputKind; use object::read::archive::ArchiveFile; use rustc_codegen_ssa::back::lto::SerializedModule; +use rustc_codegen_ssa::back::rmeta_link; use rustc_codegen_ssa::back::write::{CodegenContext, FatLtoInput, SharedEmitter}; use rustc_codegen_ssa::traits::*; -use rustc_codegen_ssa::{CompiledModule, ModuleCodegen, ModuleKind, looks_like_rust_object_file}; +use rustc_codegen_ssa::{CompiledModule, ModuleCodegen, ModuleKind}; use rustc_data_structures::memmap::Mmap; use rustc_data_structures::profiling::SelfProfilerRef; use rustc_errors::{DiagCtxt, DiagCtxtHandle}; @@ -63,6 +64,7 @@ fn prepare_lto(each_linked_rlib_for_lto: &[PathBuf], dcx: DiagCtxtHandle<'_>) -> let archive_data = unsafe { Mmap::map(File::open(path).expect("couldn't open rlib")).expect("couldn't map rlib") }; + let metadata_link = rmeta_link::read_from_data(&archive_data, path).unwrap(); let archive = ArchiveFile::parse(&*archive_data).expect("wanted an rlib"); let obj_files = archive .members() @@ -71,7 +73,7 @@ fn prepare_lto(each_linked_rlib_for_lto: &[PathBuf], dcx: DiagCtxtHandle<'_>) -> .ok() .and_then(|c| std::str::from_utf8(c.name()).ok().map(|name| (name.trim(), c))) }) - .filter(|&(name, _)| looks_like_rust_object_file(name)); + .filter(|&(name, _)| metadata_link.rust_object_files.iter().any(|f| f == name)); for (name, child) in obj_files { info!("adding bitcode from {}", name); let path = tmp_path.path().join(name); From fbf722a310e81b09ee39f10e44b4cbe021cb57a6 Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Wed, 6 May 2026 13:44:29 +0200 Subject: [PATCH 08/10] Rustup to rustc 1.97.0-nightly (e95e73209 2026-05-05) --- rust-toolchain | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-toolchain b/rust-toolchain index 56fcfdff1c7..02c9b54898a 100644 --- a/rust-toolchain +++ b/rust-toolchain @@ -1,3 +1,3 @@ [toolchain] -channel = "nightly-2026-04-29" +channel = "nightly-2026-05-06" components = ["rust-src", "rustc-dev", "llvm-tools-preview"] From b69d0746bbbd56116bbdce4f2874d409098d2fa6 Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Wed, 6 May 2026 14:07:08 +0200 Subject: [PATCH 09/10] Stop cspell from complaining about rmeta --- tools/cspell_dicts/rust.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/cspell_dicts/rust.txt b/tools/cspell_dicts/rust.txt index 379cbd77eef..15faacd53d5 100644 --- a/tools/cspell_dicts/rust.txt +++ b/tools/cspell_dicts/rust.txt @@ -1,2 +1,3 @@ lateout repr +rmeta From 50056a88993230df012e4578bee4a22f3d7ae5be Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Wed, 6 May 2026 14:25:50 +0200 Subject: [PATCH 10/10] Add failing run-make test --- tests/failing-run-make-tests.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/failing-run-make-tests.txt b/tests/failing-run-make-tests.txt index 528ee1df9f5..1feb2c7cc6e 100644 --- a/tests/failing-run-make-tests.txt +++ b/tests/failing-run-make-tests.txt @@ -12,3 +12,4 @@ tests/run-make/glibc-staticlib-args/ tests/run-make/lto-smoke-c/ tests/run-make/return-non-c-like-enum/ tests/run-make/short-ice +tests/run-make/embed-source-dwarf