diff --git a/compiler/rustc_codegen_gcc/.github/workflows/ci.yml b/compiler/rustc_codegen_gcc/.github/workflows/ci.yml index 840c09409bba9..fa9535a3729c3 100644 --- a/compiler/rustc_codegen_gcc/.github/workflows/ci.yml +++ b/compiler/rustc_codegen_gcc/.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/compiler/rustc_codegen_gcc/.github/workflows/failures.yml b/compiler/rustc_codegen_gcc/.github/workflows/failures.yml index a2a932893054d..2c9e4950706b2 100644 --- a/compiler/rustc_codegen_gcc/.github/workflows/failures.yml +++ b/compiler/rustc_codegen_gcc/.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/compiler/rustc_codegen_gcc/.github/workflows/gcc12.yml b/compiler/rustc_codegen_gcc/.github/workflows/gcc12.yml index 55b090894b4af..51b06039c885f 100644 --- a/compiler/rustc_codegen_gcc/.github/workflows/gcc12.yml +++ b/compiler/rustc_codegen_gcc/.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/compiler/rustc_codegen_gcc/.github/workflows/m68k.yml b/compiler/rustc_codegen_gcc/.github/workflows/m68k.yml index c36db18ed4aaa..8ed3545ee7d3d 100644 --- a/compiler/rustc_codegen_gcc/.github/workflows/m68k.yml +++ b/compiler/rustc_codegen_gcc/.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/compiler/rustc_codegen_gcc/.github/workflows/stdarch.yml b/compiler/rustc_codegen_gcc/.github/workflows/stdarch.yml index a58728573168b..66f30b147b4c0 100644 --- a/compiler/rustc_codegen_gcc/.github/workflows/stdarch.yml +++ b/compiler/rustc_codegen_gcc/.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/compiler/rustc_codegen_gcc/Cargo.toml b/compiler/rustc_codegen_gcc/Cargo.toml index 29af6a1fc4344..8956bd6948979 100644 --- a/compiler/rustc_codegen_gcc/Cargo.toml +++ b/compiler/rustc_codegen_gcc/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/compiler/rustc_codegen_gcc/build_system/src/test.rs b/compiler/rustc_codegen_gcc/build_system/src/test.rs index 8189e6b39747a..3f02df8399554 100644 --- a/compiler/rustc_codegen_gcc/build_system/src/test.rs +++ b/compiler/rustc_codegen_gcc/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/compiler/rustc_codegen_gcc/example/mini_core.rs b/compiler/rustc_codegen_gcc/example/mini_core.rs index 87b059526ea0e..481fe5387e149 100644 --- a/compiler/rustc_codegen_gcc/example/mini_core.rs +++ b/compiler/rustc_codegen_gcc/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/compiler/rustc_codegen_gcc/libgccjit.version b/compiler/rustc_codegen_gcc/libgccjit.version index abc967702fb0e..5eef70260466f 100644 --- a/compiler/rustc_codegen_gcc/libgccjit.version +++ b/compiler/rustc_codegen_gcc/libgccjit.version @@ -1 +1 @@ -efdd0a7290c22f5438d7c5380105d353ee3e8518 +6f155cc3f5a2dff33afe6cc3ed6c2e0e605ae6a3 diff --git a/compiler/rustc_codegen_gcc/rust-toolchain b/compiler/rustc_codegen_gcc/rust-toolchain index 655fa6abbab2b..56fcfdff1c719 100644 --- a/compiler/rustc_codegen_gcc/rust-toolchain +++ b/compiler/rustc_codegen_gcc/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/compiler/rustc_codegen_gcc/src/abi.rs b/compiler/rustc_codegen_gcc/src/abi.rs index 8277231f16a54..2f5c555b702a4 100644 --- a/compiler/rustc_codegen_gcc/src/abi.rs +++ b/compiler/rustc_codegen_gcc/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/compiler/rustc_codegen_gcc/src/asm.rs b/compiler/rustc_codegen_gcc/src/asm.rs index 1443aa925f741..5bb65365ad6ad 100644 --- a/compiler/rustc_codegen_gcc/src/asm.rs +++ b/compiler/rustc_codegen_gcc/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/compiler/rustc_codegen_gcc/src/base.rs b/compiler/rustc_codegen_gcc/src/base.rs index 7d63f430af685..7658b86a3a200 100644 --- a/compiler/rustc_codegen_gcc/src/base.rs +++ b/compiler/rustc_codegen_gcc/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/compiler/rustc_codegen_gcc/src/builder.rs b/compiler/rustc_codegen_gcc/src/builder.rs index 3cffd862b9b98..459f623a7c881 100644 --- a/compiler/rustc_codegen_gcc/src/builder.rs +++ b/compiler/rustc_codegen_gcc/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/compiler/rustc_codegen_gcc/src/context.rs b/compiler/rustc_codegen_gcc/src/context.rs index c7a2b92ac139c..e0810a35b040b 100644 --- a/compiler/rustc_codegen_gcc/src/context.rs +++ b/compiler/rustc_codegen_gcc/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/compiler/rustc_codegen_gcc/src/errors.rs b/compiler/rustc_codegen_gcc/src/errors.rs index f5815e7233928..de633d3bdde79 100644 --- a/compiler/rustc_codegen_gcc/src/errors.rs +++ b/compiler/rustc_codegen_gcc/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/compiler/rustc_codegen_gcc/src/intrinsic/llvm.rs b/compiler/rustc_codegen_gcc/src/intrinsic/llvm.rs index 60e007a25c68e..d58697f1bf270 100644 --- a/compiler/rustc_codegen_gcc/src/intrinsic/llvm.rs +++ b/compiler/rustc_codegen_gcc/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/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs b/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs index 2a7c88afe17dd..d823e209fd7d9 100644 --- a/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs +++ b/compiler/rustc_codegen_gcc/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"); + // 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"), + 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/compiler/rustc_codegen_gcc/tests/compile/asm_nul_byte.rs b/compiler/rustc_codegen_gcc/tests/compile/asm_nul_byte.rs new file mode 100644 index 0000000000000..fd5a4f98aa27f --- /dev/null +++ b/compiler/rustc_codegen_gcc/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/compiler/rustc_codegen_gcc/tests/run/call-llvm-intrinsics.rs b/compiler/rustc_codegen_gcc/tests/compile/call-llvm-intrinsics.rs similarity index 91% rename from compiler/rustc_codegen_gcc/tests/run/call-llvm-intrinsics.rs rename to compiler/rustc_codegen_gcc/tests/compile/call-llvm-intrinsics.rs index 86e041c3a2fbe..4c790994c777e 100644 --- a/compiler/rustc_codegen_gcc/tests/run/call-llvm-intrinsics.rs +++ b/compiler/rustc_codegen_gcc/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/compiler/rustc_codegen_gcc/tests/compile/fn_ptr_transmute_ignored_arg.rs b/compiler/rustc_codegen_gcc/tests/compile/fn_ptr_transmute_ignored_arg.rs new file mode 100644 index 0000000000000..a4947da248e29 --- /dev/null +++ b/compiler/rustc_codegen_gcc/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/compiler/rustc_codegen_gcc/tests/compile/global_asm_nul_byte.rs b/compiler/rustc_codegen_gcc/tests/compile/global_asm_nul_byte.rs new file mode 100644 index 0000000000000..12d647c733f21 --- /dev/null +++ b/compiler/rustc_codegen_gcc/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/compiler/rustc_codegen_gcc/tests/compile/log.rs b/compiler/rustc_codegen_gcc/tests/compile/log.rs new file mode 100644 index 0000000000000..56758b0d3e53f --- /dev/null +++ b/compiler/rustc_codegen_gcc/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/compiler/rustc_codegen_gcc/tests/compile/naked_asm_nul_byte.rs b/compiler/rustc_codegen_gcc/tests/compile/naked_asm_nul_byte.rs new file mode 100644 index 0000000000000..85be3704a5e05 --- /dev/null +++ b/compiler/rustc_codegen_gcc/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/compiler/rustc_codegen_gcc/tests/run/simd-ffi.rs b/compiler/rustc_codegen_gcc/tests/compile/simd-ffi.rs similarity index 92% rename from compiler/rustc_codegen_gcc/tests/run/simd-ffi.rs rename to compiler/rustc_codegen_gcc/tests/compile/simd-ffi.rs index 67cc2e5b96e3f..56172ddc7c643 100644 --- a/compiler/rustc_codegen_gcc/tests/run/simd-ffi.rs +++ b/compiler/rustc_codegen_gcc/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/compiler/rustc_codegen_gcc/tests/failing-lto-tests.txt b/compiler/rustc_codegen_gcc/tests/failing-lto-tests.txt index c45fc0776588e..4c62c35a512c1 100644 --- a/compiler/rustc_codegen_gcc/tests/failing-lto-tests.txt +++ b/compiler/rustc_codegen_gcc/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/compiler/rustc_codegen_gcc/tests/failing-ui-tests.txt b/compiler/rustc_codegen_gcc/tests/failing-ui-tests.txt index 8589929d2fbc3..e8a26a90890c1 100644 --- a/compiler/rustc_codegen_gcc/tests/failing-ui-tests.txt +++ b/compiler/rustc_codegen_gcc/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/compiler/rustc_codegen_gcc/tests/lang_tests.rs b/compiler/rustc_codegen_gcc/tests/lang_tests.rs new file mode 100644 index 0000000000000..6afd54e1c3fe0 --- /dev/null +++ b/compiler/rustc_codegen_gcc/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(); + // 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); + + 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 ==="); + + // 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(); + + 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/compiler/rustc_codegen_gcc/tests/lang_tests_debug.rs b/compiler/rustc_codegen_gcc/tests/lang_tests_debug.rs deleted file mode 100644 index 96bd74883ff0a..0000000000000 --- a/compiler/rustc_codegen_gcc/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/compiler/rustc_codegen_gcc/tests/lang_tests_release.rs b/compiler/rustc_codegen_gcc/tests/lang_tests_release.rs deleted file mode 100644 index 35d5d60c33ee3..0000000000000 --- a/compiler/rustc_codegen_gcc/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/compiler/rustc_codegen_gcc/tests/no_builtins/no_builtins.rs b/compiler/rustc_codegen_gcc/tests/no_builtins/no_builtins.rs new file mode 100644 index 0000000000000..c332581d93753 --- /dev/null +++ b/compiler/rustc_codegen_gcc/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/compiler/rustc_codegen_gcc/tests/no_builtins/with_builtins.rs b/compiler/rustc_codegen_gcc/tests/no_builtins/with_builtins.rs new file mode 100644 index 0000000000000..30271978a1b60 --- /dev/null +++ b/compiler/rustc_codegen_gcc/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/compiler/rustc_codegen_gcc/tests/run/asm.rs b/compiler/rustc_codegen_gcc/tests/run/asm.rs index 9b15a28d82988..01775c92ffc8a 100644 --- a/compiler/rustc_codegen_gcc/tests/run/asm.rs +++ b/compiler/rustc_codegen_gcc/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/compiler/rustc_codegen_gcc/tests/run/core-float.rs b/compiler/rustc_codegen_gcc/tests/run/core-float.rs new file mode 100644 index 0000000000000..38fa11a69b681 --- /dev/null +++ b/compiler/rustc_codegen_gcc/tests/run/core-float.rs @@ -0,0 +1,78 @@ +// Compiler: +// +// Run-time: +// status: 0 + +// FIXME: 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(); +} diff --git a/src/gcc b/src/gcc index efdd0a7290c22..6f155cc3f5a2d 160000 --- a/src/gcc +++ b/src/gcc @@ -1 +1 @@ -Subproject commit efdd0a7290c22f5438d7c5380105d353ee3e8518 +Subproject commit 6f155cc3f5a2dff33afe6cc3ed6c2e0e605ae6a3