authorbors <bors@rust-lang.org> 2026-04-30 04:13:56 UTC
committerbors <bors@rust-lang.org> 2026-04-30 04:13:56 UTC
log57f772f25c5ce2bd870d6f8c3ab318eaee5a3326
treea5fdaeaa37e9736dd42d98a7006713c0dc1f4f9f
parenta021a7796f66600f46013d6c8d1dfc9e8d7f4a92
parentf3d34dc233e86b3107ab19a11010cf1e82f3c563

Auto merge of #155978 - GuillaumeGomez:subtree-update_cg_gcc_2026-04-29, r=antoyo

GCC backend subtree sync r? ghost

37 files changed, 850 insertions(+), 330 deletions(-)

compiler/rustc_codegen_gcc/.github/workflows/ci.yml+2-2
......@@ -101,7 +101,7 @@ jobs:
101101
102102 - name: Run y.sh cargo build
103103 run: |
104 ./y.sh cargo build --manifest-path tests/hello-world/Cargo.toml
104 CARGO_PROFILE_DEV_LTO=no ./y.sh cargo build --manifest-path tests/hello-world/Cargo.toml
105105
106106 - name: Clean
107107 run: |
......@@ -119,7 +119,7 @@ jobs:
119119
120120 - name: Run tests
121121 run: |
122 ./y.sh test --release --clean --build-sysroot ${{ matrix.commands }}
122 ./y.sh test --release --clean --build-sysroot --no-builtins-tests ${{ matrix.commands }}
123123
124124 duplicates:
125125 runs-on: ubuntu-24.04
compiler/rustc_codegen_gcc/.github/workflows/failures.yml+3-1
......@@ -48,7 +48,9 @@ jobs:
4848
4949 - name: Install libgccjit12
5050 if: matrix.libgccjit_version.gcc == 'libgccjit12.so'
51 run: sudo apt-get install libgccjit-12-dev
51 run: |
52 sudo apt-get update
53 sudo apt-get install libgccjit-12-dev
5254
5355 - name: Setup path to libgccjit
5456 if: matrix.libgccjit_version.gcc == 'libgccjit12.so'
compiler/rustc_codegen_gcc/.github/workflows/gcc12.yml+3-1
......@@ -46,7 +46,9 @@ jobs:
4646
4747 - name: Install packages
4848 # `llvm-14-tools` is needed to install the `FileCheck` binary which is used for asm tests.
49 run: sudo apt-get install ninja-build ripgrep llvm-14-tools libgccjit-12-dev
49 run: |
50 sudo apt-get update
51 sudo apt-get install ninja-build ripgrep llvm-14-tools libgccjit-12-dev
5052
5153 - name: Setup path to libgccjit
5254 run: echo 'gcc-path = "/usr/lib/gcc/x86_64-linux-gnu/12"' > config.toml
compiler/rustc_codegen_gcc/.github/workflows/m68k.yml+2-2
......@@ -83,7 +83,7 @@ jobs:
8383 run: |
8484 ./y.sh prepare --only-libcore --cross
8585 ./y.sh build --sysroot --target-triple m68k-unknown-linux-gnu --target ${{ github.workspace }}/target_specs/m68k-unknown-linux-gnu.json
86 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
86 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
8787 ./y.sh clean all
8888
8989 - name: Build
......@@ -110,7 +110,7 @@ jobs:
110110
111111 vm_dir=$(pwd)/vm
112112 cd tests/hello-world
113 CG_RUSTFLAGS="-Clinker=m68k-unknown-linux-gnu-gcc" ../../y.sh cargo build --target m68k-unknown-linux-gnu
113 CARGO_PROFILE_DEV_LTO=no CG_RUSTFLAGS="-Clinker=m68k-unknown-linux-gnu-gcc" ../../y.sh cargo build --target m68k-unknown-linux-gnu
114114 sudo cp target/m68k-unknown-linux-gnu/debug/hello_world $vm_dir/home/
115115 sudo chroot $vm_dir qemu-m68k-static /home/hello_world > hello_world_stdout
116116 expected_output="40"
compiler/rustc_codegen_gcc/.github/workflows/stdarch.yml+4-2
......@@ -50,9 +50,11 @@ jobs:
5050 run: |
5151 mkdir intel-sde
5252 cd intel-sde
53 dir=sde-external-9.33.0-2024-01-07-lin
53 version=10.8.0-2026-03-15
54 url_path=915934
55 dir=sde-external-$version-lin
5456 file=$dir.tar.xz
55 wget https://downloadmirror.intel.com/813591/$file
57 wget https://downloadmirror.intel.com/$url_path/$file
5658 tar xvf $file
5759 sudo mkdir /usr/share/intel-sde
5860 sudo cp -r $dir/* /usr/share/intel-sde
compiler/rustc_codegen_gcc/Cargo.toml+2-6
......@@ -9,12 +9,8 @@ license = "MIT OR Apache-2.0"
99crate-type = ["dylib"]
1010
1111[[test]]
12name = "lang_tests_debug"
13path = "tests/lang_tests_debug.rs"
14harness = false
15[[test]]
16name = "lang_tests_release"
17path = "tests/lang_tests_release.rs"
12name = "lang_tests"
13path = "tests/lang_tests.rs"
1814harness = false
1915
2016[features]
compiler/rustc_codegen_gcc/build_system/src/test.rs+61
......@@ -43,6 +43,7 @@ fn get_runners() -> Runners {
4343 runners.insert("--extended-regex-tests", ("Run extended regex tests", extended_regex_tests));
4444 runners.insert("--mini-tests", ("Run mini tests", mini_tests));
4545 runners.insert("--cargo-tests", ("Run cargo tests", cargo_tests));
46 runners.insert("--no-builtins-tests", ("Test #![no_builtins] attribute", no_builtins_tests));
4647 runners
4748}
4849
......@@ -317,6 +318,65 @@ fn maybe_run_command_in_vm(
317318 Ok(())
318319}
319320
321/// Compile a source file to an object file and check if it contains a memset reference.
322fn object_has_memset(
323 env: &Env,
324 args: &TestArg,
325 src_file: &str,
326 obj_file_name: &str,
327) -> Result<bool, String> {
328 let cargo_target_dir = Path::new(&args.config_info.cargo_target_dir);
329 let obj_file = cargo_target_dir.join(obj_file_name);
330 let obj_file_str = obj_file.to_str().expect("obj_file to_str");
331
332 let mut command = args.config_info.rustc_command_vec();
333 command.extend_from_slice(&[
334 &src_file,
335 &"--emit",
336 &"obj",
337 &"-O",
338 &"--target",
339 &args.config_info.target_triple,
340 &"-o",
341 ]);
342 command.push(&obj_file_str);
343 run_command_with_env(&command, None, Some(env))?;
344
345 let nm_output = run_command_with_env(&[&"nm", &obj_file_str], None, Some(env))?;
346 let nm_stdout = String::from_utf8_lossy(&nm_output.stdout);
347
348 Ok(nm_stdout.contains("memset"))
349}
350
351fn no_builtins_tests(env: &Env, args: &TestArg) -> Result<(), String> {
352 // Test that the #![no_builtins] attribute prevents GCC from replacing
353 // code patterns (like loops) with calls to builtins (like memset).
354 // See https://github.com/rust-lang/rustc_codegen_gcc/issues/570
355
356 // Test 1: WITH #![no_builtins] - memset should NOT be present
357 println!("[TEST] no_builtins attribute (with #![no_builtins])");
358 let has_memset =
359 object_has_memset(env, args, "tests/no_builtins/no_builtins.rs", "no_builtins_test.o")?;
360 if has_memset {
361 return Err("no_builtins test FAILED: Found 'memset' in object file.\n\
362 The #![no_builtins] attribute should prevent GCC from replacing \n\
363 code patterns with builtin calls."
364 .to_string());
365 }
366
367 // Test 2: WITHOUT #![no_builtins] - memset SHOULD be present
368 println!("[TEST] no_builtins attribute (without #![no_builtins])");
369 let has_memset =
370 object_has_memset(env, args, "tests/no_builtins/with_builtins.rs", "with_builtins_test.o")?;
371 if !has_memset {
372 return Err("no_builtins test FAILED: 'memset' NOT found in object file.\n\
373 Without #![no_builtins], GCC should replace the loop with memset."
374 .to_string());
375 }
376
377 Ok(())
378}
379
320380fn std_tests(env: &Env, args: &TestArg) -> Result<(), String> {
321381 let cargo_target_dir = Path::new(&args.config_info.cargo_target_dir);
322382 // 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> {
12481308 test_libcore(env, args)?;
12491309 extended_sysroot_tests(env, args)?;
12501310 cargo_tests(env, args)?;
1311 no_builtins_tests(env, args)?;
12511312 test_rustc(env, args)?;
12521313
12531314 Ok(())
compiler/rustc_codegen_gcc/example/mini_core.rs+49-7
......@@ -15,6 +15,30 @@
1515#![no_core]
1616#![allow(dead_code, internal_features, ambiguous_wide_pointer_comparisons)]
1717
18#[lang = "pointee_trait"]
19pub trait Pointee: PointeeSized {
20 #[lang = "metadata_type"]
21 // needed so that layout_of will return `TooGeneric` instead of `Unknown`
22 // when asked for the layout of `*const T`. Which is important for making
23 // transmutes between raw pointers (and especially pattern types of raw pointers)
24 // work.
25 type Metadata: Copy + Sync + Unpin + Freeze;
26}
27
28#[lang = "dyn_metadata"]
29pub struct DynMetadata<Dyn: PointeeSized> {
30 _vtable_ptr: NonNull<VTable>,
31 _phantom: PhantomData<Dyn>,
32}
33
34unsafe extern "C" {
35 /// Opaque type for accessing vtables.
36 ///
37 /// Private implementation detail of `DynMetadata::size_of` etc.
38 /// There is conceptually not actually any Abstract Machine memory behind this pointer.
39 type VTable;
40}
41
1842#[no_mangle]
1943unsafe extern "C" fn _Unwind_Resume() {
2044 intrinsics::unreachable();
......@@ -113,7 +137,7 @@ unsafe impl<'a, T: PointeeSized> Sync for &'a T {}
113137unsafe impl Sync for [u8; 16] {}
114138
115139#[lang = "freeze"]
116unsafe auto trait Freeze {}
140pub unsafe auto trait Freeze {}
117141
118142unsafe impl<T: PointeeSized> Freeze for PhantomData<T> {}
119143unsafe impl<T: PointeeSized> Freeze for *const T {}
......@@ -592,6 +616,13 @@ macro_rules! pattern_type {
592616 };
593617}
594618
619impl<T: PointeeSized, U: PointeeSized> CoerceUnsized<pattern_type!(*const U is !null)> for pattern_type!(*const T is !null) where
620 T: Unsize<U>
621{
622}
623
624impl<T: DispatchFromDyn<U>, U> DispatchFromDyn<pattern_type!(U is !null)> for pattern_type!(T is !null) {}
625
595626impl<T: PointeeSized, U: PointeeSized> CoerceUnsized<NonNull<U>> for NonNull<T> where T: Unsize<U> {}
596627impl<T: PointeeSized, U: PointeeSized> DispatchFromDyn<NonNull<U>> for NonNull<T> where T: Unsize<U> {}
597628
......@@ -604,9 +635,9 @@ impl<T: PointeeSized, U: PointeeSized> CoerceUnsized<Unique<U>> for Unique<T> wh
604635impl<T: PointeeSized, U: PointeeSized> DispatchFromDyn<Unique<U>> for Unique<T> where T: Unsize<U> {}
605636
606637#[lang = "owned_box"]
607pub struct Box<T: ?Sized, A: Allocator = Global>(Unique<T>, A);
638pub struct Box<T: ?Sized, A = Global>(Unique<T>, A);
608639
609impl<T: ?Sized + Unsize<U>, U: ?Sized, A: Allocator> CoerceUnsized<Box<U, A>> for Box<T, A> {}
640impl<T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<Box<U>> for Box<T> {}
610641
611642impl<T> Box<T> {
612643 pub fn new(val: T) -> Box<T> {
......@@ -614,16 +645,27 @@ impl<T> Box<T> {
614645 let size = size_of::<T>();
615646 let ptr = libc::malloc(size);
616647 intrinsics::copy(&val as *const T as *const u8, ptr, size);
617 Box(Unique { pointer: NonNull(ptr as *const T), _marker: PhantomData }, Global)
648 Box(
649 Unique {
650 pointer: NonNull(intrinsics::transmute::<
651 *mut u8,
652 pattern_type!(*const T is !null),
653 >(ptr)),
654 _marker: PhantomData,
655 },
656 Global,
657 )
618658 }
619659 }
620660}
621661
622impl<T: ?Sized, A: Allocator> Drop for Box<T, A> {
662impl<T: ?Sized, A> Drop for Box<T, A> {
623663 fn drop(&mut self) {
624 // inner value is dropped by compiler.
664 // inner value is dropped by compiler
625665 unsafe {
626 libc::free(self.0.pointer.0 as *mut u8);
666 libc::free(intrinsics::transmute::<pattern_type!(*const T is !null), *const T>(
667 self.0.pointer.0,
668 ) as *mut u8);
627669 }
628670 }
629671}
compiler/rustc_codegen_gcc/libgccjit.version+1-1
......@@ -1 +1 @@
1efdd0a7290c22f5438d7c5380105d353ee3e8518
16f155cc3f5a2dff33afe6cc3ed6c2e0e605ae6a3
compiler/rustc_codegen_gcc/rust-toolchain+1-1
......@@ -1,3 +1,3 @@
11[toolchain]
2channel = "nightly-2026-02-14"
2channel = "nightly-2026-04-29"
33components = ["rust-src", "rustc-dev", "llvm-tools-preview"]
compiler/rustc_codegen_gcc/src/abi.rs+2-9
......@@ -224,15 +224,8 @@ impl<'gcc, 'tcx> FnAbiGccExt<'gcc, 'tcx> for FnAbi<'tcx, Ty<'tcx>> {
224224
225225 fn ptr_to_gcc_type(&self, cx: &CodegenCx<'gcc, 'tcx>) -> Type<'gcc> {
226226 // FIXME(antoyo): Should we do something with `FnAbiGcc::fn_attributes`?
227 let FnAbiGcc { return_type, arguments_type, is_c_variadic, on_stack_param_indices, .. } =
228 self.gcc_type(cx);
229 let pointer_type =
230 cx.context.new_function_pointer_type(None, return_type, &arguments_type, is_c_variadic);
231 cx.on_stack_params.borrow_mut().insert(
232 pointer_type.dyncast_function_ptr_type().expect("function ptr type"),
233 on_stack_param_indices,
234 );
235 pointer_type
227 let FnAbiGcc { return_type, arguments_type, is_c_variadic, .. } = self.gcc_type(cx);
228 cx.context.new_function_pointer_type(None, return_type, &arguments_type, is_c_variadic)
236229 }
237230
238231 #[cfg(feature = "master")]
compiler/rustc_codegen_gcc/src/asm.rs+18-4
......@@ -12,13 +12,13 @@ use rustc_codegen_ssa::traits::{
1212};
1313use rustc_middle::bug;
1414use rustc_middle::ty::Instance;
15use rustc_span::Span;
15use rustc_span::{DUMMY_SP, Span};
1616use rustc_target::asm::*;
1717
1818use crate::builder::Builder;
1919use crate::callee::get_fn;
2020use crate::context::CodegenCx;
21use crate::errors::UnwindingInlineAsm;
21use crate::errors::{NulBytesInAsm, UnwindingInlineAsm};
2222use crate::type_of::LayoutGccExt;
2323
2424// Rust asm! and GCC Extended Asm semantics differ substantially.
......@@ -530,8 +530,15 @@ impl<'a, 'gcc, 'tcx> AsmBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tcx> {
530530 template_str.push_str(INTEL_SYNTAX_INS);
531531 }
532532
533 // 4. Generate Extended Asm block
533 // NOTE: GCC's extended asm uses CString which cannot contain nul bytes.
534 // Emit an error if there are any nul bytes in the template string.
535 if template_str.contains('\0') {
536 let err_sp = span.first().copied().unwrap_or(DUMMY_SP);
537 self.sess().dcx().emit_err(NulBytesInAsm { span: err_sp });
538 return;
539 }
534540
541 // 4. Generate Extended Asm block
535542 let block = self.llbb();
536543 let extended_asm = if let Some(dest) = dest {
537544 assert!(!labels.is_empty());
......@@ -875,7 +882,7 @@ impl<'gcc, 'tcx> AsmCodegenMethods<'tcx> for CodegenCx<'gcc, 'tcx> {
875882 template: &[InlineAsmTemplatePiece],
876883 operands: &[GlobalAsmOperandRef<'tcx>],
877884 options: InlineAsmOptions,
878 _line_spans: &[Span],
885 line_spans: &[Span],
879886 ) {
880887 let asm_arch = self.tcx.sess.asm_arch.unwrap();
881888
......@@ -942,6 +949,13 @@ impl<'gcc, 'tcx> AsmCodegenMethods<'tcx> for CodegenCx<'gcc, 'tcx> {
942949 }
943950 // NOTE: seems like gcc will put the asm in the wrong section, so set it to .text manually.
944951 template_str.push_str("\n.popsection");
952 // NOTE: GCC's add_top_level_asm uses CString which cannot contain nul bytes.
953 // Emit an error if there are any nul bytes in the template string.
954 if template_str.contains('\0') {
955 let span = line_spans.first().copied().unwrap_or(DUMMY_SP);
956 self.tcx.dcx().emit_err(NulBytesInAsm { span });
957 return;
958 }
945959 self.context.add_top_level_asm(None, &template_str);
946960 }
947961
compiler/rustc_codegen_gcc/src/base.rs+13-1
......@@ -8,7 +8,8 @@ use rustc_codegen_ssa::ModuleCodegen;
88use rustc_codegen_ssa::base::maybe_create_entry_wrapper;
99use rustc_codegen_ssa::mono_item::MonoItemExt;
1010use rustc_codegen_ssa::traits::DebugInfoCodegenMethods;
11use rustc_hir::attrs::Linkage;
11use rustc_hir::attrs::{AttributeKind, Linkage};
12use rustc_hir::find_attr;
1213use rustc_middle::dep_graph;
1314#[cfg(feature = "master")]
1415use rustc_middle::mono::Visibility;
......@@ -137,6 +138,17 @@ pub fn compile_codegen_unit(
137138 // NOTE: Rust relies on LLVM doing wrapping on overflow.
138139 context.add_command_line_option("-fwrapv");
139140
141 // NOTE: We need to honor the `#![no_builtins]` attribute to prevent GCC from
142 // replacing code patterns (like loops) with calls to builtins (like memset).
143 // The `-fno-tree-loop-distribute-patterns` flag disables the loop distribution pass
144 // that transforms loops into calls to library functions (memset, memcpy, etc.).
145 // See GCC handling for more details:
146 // https://github.com/rust-lang/gcc/blob/efdd0a7290c22f5438d7c5380105d353ee3e8518/gcc/c-family/c-opts.cc#L953
147 let crate_attrs = tcx.hir_attrs(rustc_hir::CRATE_HIR_ID);
148 if find_attr!(crate_attrs, AttributeKind::NoBuiltins) {
149 context.add_command_line_option("-fno-tree-loop-distribute-patterns");
150 }
151
140152 if let Some(model) = tcx.sess.code_model() {
141153 use rustc_target::spec::CodeModel;
142154
compiler/rustc_codegen_gcc/src/builder.rs+18-16
......@@ -33,6 +33,7 @@ use rustc_span::def_id::DefId;
3333use rustc_target::callconv::FnAbi;
3434use rustc_target::spec::{HasTargetSpec, HasX86AbiOpt, Target, X86Abi};
3535
36use crate::abi::FnAbiGccExt;
3637use crate::common::{SignType, TypeReflection, type_is_pointer};
3738use crate::context::CodegenCx;
3839use crate::errors;
......@@ -213,6 +214,7 @@ impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> {
213214 _typ: &str,
214215 func_ptr: RValue<'gcc>,
215216 args: &'b [RValue<'gcc>],
217 on_stack_param_indices: &FxHashSet<usize>,
216218 ) -> Cow<'b, [RValue<'gcc>]> {
217219 let mut all_args_match = true;
218220 let mut param_types = vec![];
......@@ -225,11 +227,6 @@ impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> {
225227 param_types.push(param);
226228 }
227229
228 let mut on_stack_param_indices = FxHashSet::default();
229 if let Some(indices) = self.on_stack_params.borrow().get(&gcc_func) {
230 on_stack_param_indices.clone_from(indices);
231 }
232
233230 if all_args_match {
234231 return Cow::Borrowed(args);
235232 }
......@@ -351,19 +348,24 @@ impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> {
351348 fn function_ptr_call(
352349 &mut self,
353350 typ: Type<'gcc>,
351 fn_abi: Option<&FnAbi<'tcx, Ty<'tcx>>>,
354352 mut func_ptr: RValue<'gcc>,
355353 args: &[RValue<'gcc>],
356354 _funclet: Option<&Funclet>,
357355 ) -> RValue<'gcc> {
358 let gcc_func = match func_ptr.get_type().dyncast_function_ptr_type() {
359 Some(func) => func,
360 None => {
361 // NOTE: due to opaque pointers now being used, we need to cast here.
362 let new_func_type = typ.dyncast_function_ptr_type().expect("function ptr");
356 let func_ptr_type = {
357 let func_ptr_type = func_ptr.get_type();
358 if func_ptr_type != typ {
363359 func_ptr = self.context.new_cast(self.location, func_ptr, typ);
364 new_func_type
360 typ
361 } else {
362 func_ptr_type
365363 }
366364 };
365 let gcc_func = func_ptr_type.dyncast_function_ptr_type().expect("function ptr");
366 let on_stack_param_indices = fn_abi
367 .map(|fn_abi| fn_abi.gcc_type(self.cx).on_stack_param_indices)
368 .unwrap_or_default();
367369 let func_name = format!("{:?}", func_ptr);
368370 let previous_arg_count = args.len();
369371 let orig_args = args;
......@@ -372,7 +374,7 @@ impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> {
372374 llvm::adjust_intrinsic_arguments(self, gcc_func, args.into(), &func_name)
373375 };
374376 let args_adjusted = args.len() != previous_arg_count;
375 let args = self.check_ptr_call("call", func_ptr, &args);
377 let args = self.check_ptr_call("call", func_ptr, &args, &on_stack_param_indices);
376378
377379 // gccjit requires to use the result of functions, even when it's not used.
378380 // 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> {
599601 &mut self,
600602 typ: Type<'gcc>,
601603 fn_attrs: Option<&CodegenFnAttrs>,
602 _fn_abi: Option<&FnAbi<'tcx, Ty<'tcx>>>,
604 fn_abi: Option<&FnAbi<'tcx, Ty<'tcx>>>,
603605 func: RValue<'gcc>,
604606 args: &[RValue<'gcc>],
605607 then: Block<'gcc>,
......@@ -611,7 +613,7 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> {
611613
612614 let current_block = self.block;
613615 self.block = try_block;
614 let call = self.call(typ, fn_attrs, None, func, args, None, instance); // FIXME(antoyo): use funclet here?
616 let call = self.call(typ, fn_attrs, fn_abi, func, args, None, instance); // FIXME(antoyo): use funclet here?
615617 self.block = current_block;
616618
617619 let return_value =
......@@ -645,7 +647,7 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> {
645647 _funclet: Option<&Funclet>,
646648 instance: Option<Instance<'tcx>>,
647649 ) -> RValue<'gcc> {
648 let call_site = self.call(typ, fn_attrs, None, func, args, None, instance);
650 let call_site = self.call(typ, fn_attrs, fn_abi, func, args, None, instance);
649651 let condition = self.context.new_rvalue_from_int(self.bool_type, 1);
650652 self.llbb().end_with_conditional(self.location, condition, then, catch);
651653 if let Some(_fn_abi) = fn_abi {
......@@ -1773,7 +1775,7 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> {
17731775 self.function_call(func, args, funclet)
17741776 } else {
17751777 // If it's a not function that was defined, it's a function pointer.
1776 self.function_ptr_call(typ, func, args, funclet)
1778 self.function_ptr_call(typ, fn_abi, func, args, funclet)
17771779 };
17781780 if let Some(_fn_abi) = fn_abi {
17791781 // FIXME(bjorn3): Apply function attributes
compiler/rustc_codegen_gcc/src/context.rs+1-6
......@@ -1,9 +1,7 @@
11use std::cell::{Cell, RefCell};
22use std::collections::HashMap;
33
4use gccjit::{
5 Block, CType, Context, Function, FunctionPtrType, FunctionType, LValue, Location, RValue, Type,
6};
4use gccjit::{Block, CType, Context, Function, FunctionType, LValue, Location, RValue, Type};
75use rustc_abi::{Align, HasDataLayout, PointeeInfo, Size, TargetDataLayout, VariantIdx};
86use rustc_codegen_ssa::base::wants_msvc_seh;
97use rustc_codegen_ssa::errors as ssa_errors;
......@@ -100,8 +98,6 @@ pub struct CodegenCx<'gcc, 'tcx> {
10098 RefCell<FxHashMap<(Ty<'tcx>, Option<ty::ExistentialTraitRef<'tcx>>), RValue<'gcc>>>,
10199
102100 // FIXME(antoyo): improve the SSA API to not require those.
103 /// Mapping from function pointer type to indexes of on stack parameters.
104 pub on_stack_params: RefCell<FxHashMap<FunctionPtrType<'gcc>, FxHashSet<usize>>>,
105101 /// Mapping from function to indexes of on stack parameters.
106102 pub on_stack_function_params: RefCell<FxHashMap<Function<'gcc>, FxHashSet<usize>>>,
107103
......@@ -289,7 +285,6 @@ impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> {
289285 instances: Default::default(),
290286 function_instances: Default::default(),
291287 intrinsic_instances: Default::default(),
292 on_stack_params: Default::default(),
293288 on_stack_function_params: Default::default(),
294289 vtables: Default::default(),
295290 const_globals: Default::default(),
compiler/rustc_codegen_gcc/src/errors.rs+7
......@@ -23,3 +23,10 @@ pub(crate) struct LtoBitcodeFromRlib {
2323#[derive(Diagnostic)]
2424#[diag("explicit tail calls with the 'become' keyword are not implemented in the GCC backend")]
2525pub(crate) struct ExplicitTailCallsUnsupported;
26
27#[derive(Diagnostic)]
28#[diag("asm contains a NUL byte")]
29pub(crate) struct NulBytesInAsm {
30 #[primary_span]
31 pub span: Span,
32}
compiler/rustc_codegen_gcc/src/intrinsic/llvm.rs+4
......@@ -1589,6 +1589,7 @@ pub fn intrinsic<'gcc, 'tcx>(name: &str, cx: &CodegenCx<'gcc, 'tcx>) -> Function
15891589 "llvm.x86.tileloaddrst164" => "__builtin_trap",
15901590 "llvm.x86.tilezero" => "__builtin_trap",
15911591 "llvm.x86.tilemovrow" => "__builtin_trap",
1592 "llvm.x86.tilemovrowi" => "__builtin_trap",
15921593 "llvm.x86.tdpbhf8ps" => "__builtin_trap",
15931594 "llvm.x86.tdphbf8ps" => "__builtin_trap",
15941595 "llvm.x86.tdpbf8ps" => "__builtin_trap",
......@@ -1603,6 +1604,9 @@ pub fn intrinsic<'gcc, 'tcx>(name: &str, cx: &CodegenCx<'gcc, 'tcx>) -> Function
16031604 "llvm.x86.tcvtrowps2phh" => "__builtin_trap",
16041605 "llvm.x86.tcvtrowps2phl" => "__builtin_trap",
16051606 "llvm.x86.tcvtrowd2ps" => "__builtin_trap",
1607 "llvm.x86.tcvtrowd2psi" => "__builtin_trap",
1608 "llvm.x86.tcvtrowps2phhi" => "__builtin_trap",
1609 "llvm.x86.tcvtrowps2phli" => "__builtin_trap",
16061610 "llvm.x86.tcmmimfp16ps" => "__builtin_trap",
16071611 "llvm.x86.tcmmrlfp16ps" => "__builtin_trap",
16081612
compiler/rustc_codegen_gcc/src/intrinsic/mod.rs+90-117
......@@ -4,9 +4,7 @@ mod simd;
44#[cfg(feature = "master")]
55use std::iter;
66
7#[cfg(feature = "master")]
8use gccjit::Type;
9use gccjit::{ComparisonOp, Function, FunctionType, RValue, ToRValue, UnaryOp};
7use gccjit::{ComparisonOp, Function, FunctionType, RValue, ToRValue, Type, UnaryOp};
108use rustc_abi::{BackendRepr, HasDataLayout, WrappingRange};
119use rustc_codegen_ssa::MemFlags;
1210use rustc_codegen_ssa::base::wants_msvc_seh;
......@@ -38,6 +36,22 @@ use crate::context::CodegenCx;
3836use crate::intrinsic::simd::generic_simd_intrinsic;
3937use crate::type_of::LayoutGccExt;
4038
39fn float_intrinsic<'gcc, 'tcx>(
40 cx: &CodegenCx<'gcc, 'tcx>,
41 typ: Type<'gcc>,
42 name: &str,
43) -> Option<Function<'gcc>> {
44 // GCC doesn't have the intrinsic we want so we use the compiler-builtins one
45 Some(cx.context.new_function(
46 None,
47 FunctionType::Extern,
48 typ,
49 &[cx.context.new_parameter(None, typ, "a"), cx.context.new_parameter(None, typ, "b")],
50 name,
51 false,
52 ))
53}
54
4155fn get_simple_intrinsic<'gcc, 'tcx>(
4256 cx: &CodegenCx<'gcc, 'tcx>,
4357 name: Symbol,
......@@ -68,48 +82,19 @@ fn get_simple_intrinsic<'gcc, 'tcx>(
6882 // FIXME: calling `fma` from libc without FMA target feature uses expensive software emulation
6983 sym::fmuladdf32 => "fmaf", // FIXME: use gcc intrinsic analogous to llvm.fmuladd.f32
7084 sym::fmuladdf64 => "fma", // FIXME: use gcc intrinsic analogous to llvm.fmuladd.f64
71 sym::minimumf32 => "fminimumf",
72 sym::minimumf64 => "fminimum",
73 sym::minimumf128 => {
74 // GCC doesn't have the intrinsic we want so we use the compiler-builtins one
75 // https://docs.rs/compiler_builtins/latest/compiler_builtins/math/full_availability/fn.fminimumf128.html
76 let f128_type = cx.type_f128();
77 return Some(cx.context.new_function(
78 None,
79 FunctionType::Extern,
80 f128_type,
81 &[
82 cx.context.new_parameter(None, f128_type, "a"),
83 cx.context.new_parameter(None, f128_type, "b"),
84 ],
85 "fminimumf128",
86 false,
87 ));
88 }
89 sym::maximumf32 => "fmaximumf",
90 sym::maximumf64 => "fmaximum",
91 sym::maximumf128 => {
92 // GCC doesn't have the intrinsic we want so we use the compiler-builtins one
93 // https://docs.rs/compiler_builtins/latest/compiler_builtins/math/full_availability/fn.fmaximumf128.html
94 let f128_type = cx.type_f128();
95 return Some(cx.context.new_function(
96 None,
97 FunctionType::Extern,
98 f128_type,
99 &[
100 cx.context.new_parameter(None, f128_type, "a"),
101 cx.context.new_parameter(None, f128_type, "b"),
102 ],
103 "fmaximumf128",
104 false,
105 ));
106 }
85 sym::minimumf32 => return float_intrinsic(cx, cx.type_f32(), "fminimumf"),
86 sym::minimumf64 => return float_intrinsic(cx, cx.type_f64(), "fminimum"),
87 sym::minimumf128 => return float_intrinsic(cx, cx.type_f128(), "fminimumf128"),
88 sym::maximumf32 => return float_intrinsic(cx, cx.type_f32(), "fmaximumf"),
89 sym::maximumf64 => return float_intrinsic(cx, cx.type_f64(), "fmaximum"),
90 sym::maximumf128 => return float_intrinsic(cx, cx.type_f128(), "fmaximumf128"),
10791 sym::copysignf32 => "copysignf",
10892 sym::copysignf64 => "copysign",
10993 sym::floorf32 => "floorf",
11094 sym::floorf64 => "floor",
11195 sym::ceilf32 => "ceilf",
11296 sym::ceilf64 => "ceil",
97 sym::powf128 => return float_intrinsic(cx, cx.type_f128(), "powf128"),
11398 sym::truncf32 => "truncf",
11499 sym::truncf64 => "trunc",
115100 // We match the LLVM backend and lower this to `rint`.
......@@ -123,72 +108,6 @@ fn get_simple_intrinsic<'gcc, 'tcx>(
123108 Some(cx.context.get_builtin_function(gcc_name))
124109}
125110
126// FIXME(antoyo): We can probably remove these and use the fallback intrinsic implementation.
127fn get_simple_function<'gcc, 'tcx>(
128 cx: &CodegenCx<'gcc, 'tcx>,
129 name: Symbol,
130) -> Option<Function<'gcc>> {
131 let (return_type, parameters, func_name) = match name {
132 sym::minimumf32 => {
133 let parameters = [
134 cx.context.new_parameter(None, cx.float_type, "a"),
135 cx.context.new_parameter(None, cx.float_type, "b"),
136 ];
137 (cx.float_type, parameters, "fminimumf")
138 }
139 sym::minimumf64 => {
140 let parameters = [
141 cx.context.new_parameter(None, cx.double_type, "a"),
142 cx.context.new_parameter(None, cx.double_type, "b"),
143 ];
144 (cx.double_type, parameters, "fminimum")
145 }
146 sym::minimumf128 => {
147 let f128_type = cx.type_f128();
148 // GCC doesn't have the intrinsic we want so we use the compiler-builtins one
149 // https://docs.rs/compiler_builtins/latest/compiler_builtins/math/full_availability/fn.fminimumf128.html
150 let parameters = [
151 cx.context.new_parameter(None, f128_type, "a"),
152 cx.context.new_parameter(None, f128_type, "b"),
153 ];
154 (f128_type, parameters, "fminimumf128")
155 }
156 sym::maximumf32 => {
157 let parameters = [
158 cx.context.new_parameter(None, cx.float_type, "a"),
159 cx.context.new_parameter(None, cx.float_type, "b"),
160 ];
161 (cx.float_type, parameters, "fmaximumf")
162 }
163 sym::maximumf64 => {
164 let parameters = [
165 cx.context.new_parameter(None, cx.double_type, "a"),
166 cx.context.new_parameter(None, cx.double_type, "b"),
167 ];
168 (cx.double_type, parameters, "fmaximum")
169 }
170 sym::maximumf128 => {
171 let f128_type = cx.type_f128();
172 // GCC doesn't have the intrinsic we want so we use the compiler-builtins one
173 // https://docs.rs/compiler_builtins/latest/compiler_builtins/math/full_availability/fn.fmaximumf128.html
174 let parameters = [
175 cx.context.new_parameter(None, f128_type, "a"),
176 cx.context.new_parameter(None, f128_type, "b"),
177 ];
178 (f128_type, parameters, "fmaximumf128")
179 }
180 _ => return None,
181 };
182 Some(cx.context.new_function(
183 None,
184 FunctionType::Extern,
185 return_type,
186 &parameters,
187 func_name,
188 false,
189 ))
190}
191
192111fn get_simple_function_f128<'gcc, 'tcx>(
193112 span: Span,
194113 cx: &CodegenCx<'gcc, 'tcx>,
......@@ -198,7 +117,12 @@ fn get_simple_function_f128<'gcc, 'tcx>(
198117 let func_name = match name {
199118 sym::ceilf128 => "ceilf128",
200119 sym::fabs => "fabsf128",
120 sym::expf128 => "expf128",
121 sym::exp2f128 => "exp2f128",
201122 sym::floorf128 => "floorf128",
123 sym::logf128 => "logf128",
124 sym::log2f128 => "log2f128",
125 sym::log10f128 => "log10f128",
202126 sym::truncf128 => "truncf128",
203127 sym::roundf128 => "roundf128",
204128 sym::round_ties_even_f128 => "roundevenf128",
......@@ -215,6 +139,24 @@ fn get_simple_function_f128<'gcc, 'tcx>(
215139 )
216140}
217141
142fn generic_f16_builtin<'gcc, 'tcx>(
143 cx: &CodegenCx<'gcc, 'tcx>,
144 name: Symbol,
145 args: &[OperandRef<'tcx, RValue<'gcc>>],
146) -> RValue<'gcc> {
147 let f32_type = cx.type_f32();
148 let builtin_name = match name {
149 sym::fabs => "fabsf",
150 _ => unreachable!(),
151 };
152
153 let func = cx.context.get_builtin_function(builtin_name);
154 let args: Vec<_> =
155 args.iter().map(|arg| cx.context.new_cast(None, arg.immediate(), f32_type)).collect();
156 let result = cx.context.new_call(None, func, &args);
157 cx.context.new_cast(None, result, cx.type_f16())
158}
159
218160fn f16_builtin<'gcc, 'tcx>(
219161 cx: &CodegenCx<'gcc, 'tcx>,
220162 name: Symbol,
......@@ -224,17 +166,15 @@ fn f16_builtin<'gcc, 'tcx>(
224166 let builtin_name = match name {
225167 sym::ceilf16 => "__builtin_ceilf",
226168 sym::copysignf16 => "__builtin_copysignf",
169 sym::expf16 => "expf",
170 sym::exp2f16 => "exp2f",
227171 sym::fabs => "fabsf",
228172 sym::floorf16 => "__builtin_floorf",
229173 sym::fmaf16 => "fmaf",
174 sym::logf16 => "logf",
175 sym::log2f16 => "log2f",
176 sym::log10f16 => "log10f",
230177 sym::powf16 => "__builtin_powf",
231 sym::powif16 => {
232 let func = cx.context.get_builtin_function("__builtin_powif");
233 let arg0 = cx.context.new_cast(None, args[0].immediate(), f32_type);
234 let args = [arg0, args[1].immediate()];
235 let result = cx.context.new_call(None, func, &args);
236 return cx.context.new_cast(None, result, cx.type_f16());
237 }
238178 sym::roundf16 => "__builtin_roundf",
239179 sym::round_ties_even_f16 => "__builtin_rintf",
240180 sym::sqrtf16 => "__builtin_sqrtf",
......@@ -264,7 +204,6 @@ impl<'a, 'gcc, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tc
264204 let fn_args = instance.args;
265205
266206 let simple = get_simple_intrinsic(self, name);
267 let simple_func = get_simple_function(self, name);
268207
269208 let value = match name {
270209 _ if simple.is_some() => {
......@@ -275,8 +214,26 @@ impl<'a, 'gcc, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tc
275214 &args.iter().map(|arg| arg.immediate()).collect::<Vec<_>>(),
276215 )
277216 }
278 _ if simple_func.is_some() => {
279 let func = simple_func.expect("simple function");
217 // FIXME(antoyo): We can probably remove these and use the fallback intrinsic implementation.
218 sym::minimumf32 | sym::minimumf64 | sym::maximumf32 | sym::maximumf64 => {
219 let (ty, func_name) = match name {
220 sym::minimumf32 => (self.cx.float_type, "fminimumf"),
221 sym::maximumf32 => (self.cx.float_type, "fmaximumf"),
222 sym::minimumf64 => (self.cx.double_type, "fminimum"),
223 sym::maximumf64 => (self.cx.double_type, "fmaximum"),
224 _ => unreachable!(),
225 };
226 let func = self.cx.context.new_function(
227 None,
228 FunctionType::Extern,
229 ty,
230 &[
231 self.cx.context.new_parameter(None, ty, "a"),
232 self.cx.context.new_parameter(None, ty, "b"),
233 ],
234 func_name,
235 false,
236 );
280237 self.cx.context.new_call(
281238 self.location,
282239 func,
......@@ -285,10 +242,14 @@ impl<'a, 'gcc, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tc
285242 }
286243 sym::ceilf16
287244 | sym::copysignf16
245 | sym::expf16
246 | sym::exp2f16
288247 | sym::floorf16
289248 | sym::fmaf16
249 | sym::logf16
250 | sym::log2f16
251 | sym::log10f16
290252 | sym::powf16
291 | sym::powif16
292253 | sym::roundf16
293254 | sym::round_ties_even_f16
294255 | sym::sqrtf16
......@@ -299,6 +260,11 @@ impl<'a, 'gcc, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tc
299260 | sym::roundf128
300261 | sym::round_ties_even_f128
301262 | sym::sqrtf128
263 | sym::expf128
264 | sym::exp2f128
265 | sym::logf128
266 | sym::log2f128
267 | sym::log10f128
302268 if self.cx.supports_f128_type =>
303269 {
304270 let func = get_simple_function_f128(span, self, name);
......@@ -347,6 +313,13 @@ impl<'a, 'gcc, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tc
347313 &args.iter().map(|arg| arg.immediate()).collect::<Vec<_>>(),
348314 )
349315 }
316 sym::powif16 => {
317 let func = self.cx.context.get_builtin_function("__builtin_powif");
318 let arg0 = self.cx.context.new_cast(None, args[0].immediate(), self.cx.type_f32());
319 let args = [arg0, args[1].immediate()];
320 let result = self.cx.context.new_call(None, func, &args);
321 self.cx.context.new_cast(None, result, self.cx.type_f16())
322 }
350323 sym::powif128 => {
351324 let f128_type = self.cx.type_f128();
352325 let func = self.cx.context.new_function(
......@@ -490,7 +463,7 @@ impl<'a, 'gcc, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tc
490463 span_bug!(span, "expected float type for fabs intrinsic: {:?}", ty);
491464 };
492465 let func = match float_ty {
493 ty::FloatTy::F16 => break 'fabs f16_builtin(self, name, args),
466 ty::FloatTy::F16 => break 'fabs generic_f16_builtin(self, name, args),
494467 ty::FloatTy::F32 => self.context.get_builtin_function("fabsf"),
495468 ty::FloatTy::F64 => self.context.get_builtin_function("fabs"),
496469 ty::FloatTy::F128 => get_simple_function_f128(span, self, name),
compiler/rustc_codegen_gcc/tests/compile/asm_nul_byte.rs created+15
......@@ -0,0 +1,15 @@
1// Compiler:
2// status: error
3// stderr:
4// error: asm contains a NUL byte
5// ...
6
7// Test that inline asm containing a NUL byte emits an error.
8
9use std::arch::asm;
10
11fn main() {
12 unsafe {
13 asm!("\0");
14 }
15}
compiler/rustc_codegen_gcc/tests/compile/call-llvm-intrinsics.rs created+32
......@@ -0,0 +1,32 @@
1// Compiler:
2
3// FIXME: Remove this test once rustc's `./tests/codegen/riscv-abi/call-llvm-intrinsics.rs`
4// stops ignoring GCC backend.
5
6#![feature(link_llvm_intrinsics)]
7#![crate_type = "lib"]
8#![allow(internal_features)]
9
10struct A;
11
12impl Drop for A {
13 fn drop(&mut self) {
14 println!("A");
15 }
16}
17
18extern "C" {
19 #[link_name = "llvm.sqrt.f32"]
20 fn sqrt(x: f32) -> f32;
21}
22
23pub fn do_call() {
24 let _a = A;
25
26 unsafe {
27 // Ensure that we `call` LLVM intrinsics instead of trying to `invoke` them
28 // CHECK: store float 4.000000e+00, float* %{{.}}, align 4
29 // CHECK: call float @llvm.sqrt.f32(float %{{.}}
30 sqrt(4.0);
31 }
32}
compiler/rustc_codegen_gcc/tests/compile/fn_ptr_transmute_ignored_arg.rs created+12
......@@ -0,0 +1,12 @@
1// Compiler:
2
3// Regression test for <https://github.com/rust-lang/rustc_codegen_gcc/issues/836>
4
5#![crate_type = "lib"]
6
7#[unsafe(no_mangle)]
8extern "C" fn third(_a: usize, b: usize, c: usize) {
9 let throw_away_f: fn((), usize, usize) =
10 unsafe { std::mem::transmute(third as extern "C" fn(_, _, _)) };
11 throw_away_f((), 2, 3)
12}
compiler/rustc_codegen_gcc/tests/compile/global_asm_nul_byte.rs created+13
......@@ -0,0 +1,13 @@
1// Compiler:
2// status: error
3// stderr:
4// error: asm contains a NUL byte
5// ...
6
7// Test that global_asm containing a NUL byte emits an error.
8
9#![crate_type = "lib"]
10
11use std::arch::global_asm;
12
13global_asm!("\0");
compiler/rustc_codegen_gcc/tests/compile/log.rs created+12
......@@ -0,0 +1,12 @@
1// Compiler:
2
3extern "C" {
4 fn log(message_data: u32, message_size: u32);
5}
6
7pub fn main() {
8 let message = "Hello, world!";
9 unsafe {
10 log(message.as_ptr() as u32, message.len() as u32);
11 }
12}
compiler/rustc_codegen_gcc/tests/compile/naked_asm_nul_byte.rs created+17
......@@ -0,0 +1,17 @@
1// Compiler:
2// status: error
3// stderr:
4// ...
5// error: asm contains a NUL byte
6// ...
7
8// Test that naked_asm containing a NUL byte emits an error.
9
10#![crate_type = "lib"]
11
12use std::arch::naked_asm;
13
14#[unsafe(naked)]
15pub extern "C" fn nul_byte_naked() {
16 naked_asm!("\0")
17}
compiler/rustc_codegen_gcc/tests/compile/simd-ffi.rs created+94
......@@ -0,0 +1,94 @@
1// Compiler:
2
3// FIXME: Remove this test once <tests/run-make/simd-ffi/simd.rs> stops
4// ignoring GCC backend.
5
6#![allow(internal_features, non_camel_case_types)]
7#![crate_type = "lib"]
8
9// we can compile to a variety of platforms, because we don't need
10// cross-compiled standard libraries.
11#![feature(no_core, auto_traits)]
12#![no_core]
13#![feature(repr_simd, simd_ffi, link_llvm_intrinsics, lang_items, rustc_attrs)]
14
15#[derive(Copy)]
16#[repr(simd)]
17pub struct f32x4([f32; 4]);
18
19extern "C" {
20 #[link_name = "llvm.sqrt.v4f32"]
21 fn vsqrt(x: f32x4) -> f32x4;
22}
23
24pub fn foo(x: f32x4) -> f32x4 {
25 unsafe { vsqrt(x) }
26}
27
28#[derive(Copy)]
29#[repr(simd)]
30pub struct i32x4([i32; 4]);
31
32extern "C" {
33 // _mm_sll_epi32
34 #[cfg(all(any(target_arch = "x86", target_arch = "x86-64"), target_feature = "sse2"))]
35 #[link_name = "llvm.x86.sse2.psll.d"]
36 fn integer(a: i32x4, b: i32x4) -> i32x4;
37
38 // vmaxq_s32
39 #[cfg(target_arch = "arm")]
40 #[link_name = "llvm.arm.neon.vmaxs.v4i32"]
41 fn integer(a: i32x4, b: i32x4) -> i32x4;
42 // vmaxq_s32
43 #[cfg(target_arch = "aarch64")]
44 #[link_name = "llvm.aarch64.neon.maxs.v4i32"]
45 fn integer(a: i32x4, b: i32x4) -> i32x4;
46
47 // Use a generic LLVM intrinsic to do type checking on other platforms
48 #[cfg(not(any(
49 all(any(target_arch = "x86", target_arch = "x86-64"), target_feature = "sse2"),
50 target_arch = "arm",
51 target_arch = "aarch64"
52 )))]
53 #[link_name = "llvm.smax.v4i32"]
54 fn integer(a: i32x4, b: i32x4) -> i32x4;
55}
56
57pub fn bar(a: i32x4, b: i32x4) -> i32x4 {
58 unsafe { integer(a, b) }
59}
60
61#[lang = "pointee_sized"]
62pub trait PointeeSized {}
63
64#[lang = "meta_sized"]
65pub trait MetaSized: PointeeSized {}
66
67#[lang = "sized"]
68pub trait Sized: MetaSized {}
69
70#[lang = "copy"]
71pub trait Copy {}
72
73impl Copy for f32 {}
74impl Copy for i32 {}
75impl Copy for [f32; 4] {}
76impl Copy for [i32; 4] {}
77
78pub mod marker {
79 pub use Copy;
80}
81
82#[lang = "freeze"]
83auto trait Freeze {}
84
85#[macro_export]
86#[rustc_builtin_macro]
87macro_rules! Copy {
88 () => {};
89}
90#[macro_export]
91#[rustc_builtin_macro]
92macro_rules! derive {
93 () => {};
94}
compiler/rustc_codegen_gcc/tests/failing-lto-tests.txt-1
......@@ -1,4 +1,3 @@
1tests/ui/lto/all-crates.rs
21tests/ui/lto/debuginfo-lto-alloc.rs
32tests/ui/panic-runtime/lto-unwind.rs
43tests/ui/uninhabited/uninhabited-transparent-return-abi.rs
compiler/rustc_codegen_gcc/tests/failing-ui-tests.txt+13-1
......@@ -47,7 +47,6 @@ tests/ui/sanitizer/cfi/virtual-auto.rs
4747tests/ui/sanitizer/cfi/sized-associated-ty.rs
4848tests/ui/sanitizer/cfi/can-reveal-opaques.rs
4949tests/ui/sanitizer/kcfi-mangling.rs
50tests/ui/backtrace/dylib-dep.rs
5150tests/ui/delegation/fn-header.rs
5251tests/ui/consts/const-eval/parse_ints.rs
5352tests/ui/simd/intrinsic/generic-as.rs
......@@ -99,3 +98,16 @@ tests/ui/eii/privacy1.rs
9998tests/ui/eii/default/call_impl.rs
10099tests/ui/c-variadic/copy.rs
101100tests/ui/asm/x86_64/global_asm_escape.rs
101tests/ui/lto/all-crates.rs
102tests/ui/consts/const-eval/c-variadic.rs
103tests/ui/eii/default/call_default_panics.rs
104tests/ui/explicit-tail-calls/indirect.rs
105tests/ui/traits/inheritance/self-in-supertype.rs
106tests/ui/fmt/fmt_debug/shallow.rs
107tests/ui/c-variadic/roundtrip.rs
108tests/ui/eii/eii_impl_with_contract.rs
109tests/ui/eii/static/cross_crate_decl.rs
110tests/ui/eii/static/cross_crate_def.rs
111tests/ui/eii/static/same_address.rs
112tests/ui/eii/static/simple.rs
113tests/ui/explicit-tail-calls/default-trait-method.rs
compiler/rustc_codegen_gcc/tests/lang_tests.rs created+237
......@@ -0,0 +1,237 @@
1#![allow(clippy::uninlined_format_args)]
2
3use std::env::current_dir;
4use std::path::{Path, PathBuf};
5use std::process::Command;
6
7use lang_tester::LangTester;
8use tempfile::TempDir;
9
10fn compile_and_run_cmds(
11 compiler_args: Vec<String>,
12 test_target: &Option<String>,
13 exe: &Path,
14 test_mode: TestMode,
15) -> Vec<(&'static str, Command)> {
16 let mut compiler = Command::new("rustc");
17 compiler.args(compiler_args);
18
19 // Test command 2: run `tempdir/x`.
20 if test_target.is_some() {
21 let mut env_path = std::env::var("PATH").unwrap_or_default();
22 // FIXME(antoyo): find a better way to add the PATH necessary locally.
23 env_path = format!("/opt/m68k-unknown-linux-gnu/bin:{}", env_path);
24 compiler.env("PATH", env_path);
25
26 let mut commands = vec![("Compiler", compiler)];
27 if test_mode.should_run() {
28 let vm_parent_dir = std::env::var("CG_GCC_VM_DIR")
29 .map(PathBuf::from)
30 .unwrap_or_else(|_| std::env::current_dir().unwrap());
31 let vm_dir = "vm";
32 let exe_filename = exe.file_name().unwrap();
33 let vm_home_dir = vm_parent_dir.join(vm_dir).join("home");
34 let vm_exe_path = vm_home_dir.join(exe_filename);
35 // FIXME(antoyo): panicking here makes the test pass.
36 let inside_vm_exe_path = PathBuf::from("/home").join(exe_filename);
37
38 let mut copy = Command::new("sudo");
39 copy.arg("cp");
40 copy.args([exe, &vm_exe_path]);
41
42 let mut runtime = Command::new("sudo");
43 runtime.args(["chroot", vm_dir, "qemu-m68k-static"]);
44 runtime.arg(inside_vm_exe_path);
45 runtime.current_dir(vm_parent_dir);
46
47 commands.push(("Copy", copy));
48 commands.push(("Run-time", runtime));
49 }
50 commands
51 } else {
52 let mut commands = vec![("Compiler", compiler)];
53 if test_mode.should_run() {
54 let runtime = Command::new(exe);
55 commands.push(("Run-time", runtime));
56 }
57 commands
58 }
59}
60
61#[derive(Clone, Copy)]
62enum BuildMode {
63 Debug,
64 Release,
65}
66
67impl BuildMode {
68 fn is_debug(self) -> bool {
69 matches!(self, Self::Debug)
70 }
71}
72
73#[derive(Clone, Copy)]
74enum TestMode {
75 Compile,
76 CompileAndRun,
77}
78
79impl TestMode {
80 fn should_run(self) -> bool {
81 matches!(self, Self::CompileAndRun)
82 }
83}
84
85fn build_test_runner(
86 tempdir: PathBuf,
87 current_dir: String,
88 build_mode: BuildMode,
89 test_kind: &str,
90 test_dir: &str,
91 test_mode: TestMode,
92 files_to_ignore_on_m68k: &'static [&'static str],
93) {
94 fn rust_filter(path: &Path) -> bool {
95 path.is_file() && path.extension().expect("extension").to_str().expect("to_str") == "rs"
96 }
97
98 #[cfg(feature = "master")]
99 fn filter(filename: &Path) -> bool {
100 rust_filter(filename)
101 }
102
103 #[cfg(not(feature = "master"))]
104 fn filter(filename: &Path) -> bool {
105 if let Some(filename) = filename.to_str()
106 && filename.ends_with("gep.rs")
107 {
108 return false;
109 }
110 rust_filter(filename)
111 }
112
113 println!("=== {test_kind} tests ===");
114
115 // FIXME(antoyo): find a way to send this via a cli argument.
116 let test_target = std::env::var("CG_GCC_TEST_TARGET").ok();
117 let test_target_filter = test_target.clone();
118
119 LangTester::new()
120 .test_dir(test_dir)
121 .test_path_filter(move |filename| {
122 if !filter(filename) {
123 return false;
124 }
125 if test_target_filter.is_some()
126 && let Some(filename) = filename.file_name()
127 && let Some(filename) = filename.to_str()
128 && files_to_ignore_on_m68k.contains(&filename)
129 {
130 return false;
131 }
132 true
133 })
134 .test_extract(|path| {
135 std::fs::read_to_string(path)
136 .expect("read file")
137 .lines()
138 .skip_while(|l| !l.starts_with("//"))
139 .take_while(|l| l.starts_with("//"))
140 .map(|l| &l[2..])
141 .collect::<Vec<_>>()
142 .join("\n")
143 })
144 .test_cmds(move |path| {
145 // Test command 1: Compile `x.rs` into `tempdir/x`.
146 let mut exe = PathBuf::new();
147 exe.push(&tempdir);
148 exe.push(path.file_stem().expect("file_stem"));
149 let mut compiler_args = vec![
150 format!("-Zcodegen-backend={}/target/debug/librustc_codegen_gcc.so", current_dir),
151 "--sysroot".into(),
152 format!("{}/build/build_sysroot/sysroot/", current_dir),
153 "-C".into(),
154 "link-arg=-lc".into(),
155 "--extern".into(),
156 "mini_core=target/out/libmini_core.rlib".into(),
157 "-o".into(),
158 exe.to_str().expect("to_str").into(),
159 path.to_str().expect("to_str").into(),
160 ];
161
162 if let Some(ref target) = test_target {
163 compiler_args.extend_from_slice(&["--target".into(), target.into()]);
164
165 let linker = format!("{}-gcc", target);
166 compiler_args.push(format!("-Clinker={}", linker));
167 }
168
169 if let Some(flags) = option_env!("TEST_FLAGS") {
170 for flag in flags.split_whitespace() {
171 compiler_args.push(flag.into());
172 }
173 }
174
175 if build_mode.is_debug() {
176 compiler_args
177 .extend_from_slice(&["-C".to_string(), "llvm-args=sanitize-undefined".into()]);
178 if test_target.is_none() {
179 // m68k doesn't have lubsan for now
180 compiler_args.extend_from_slice(&["-C".into(), "link-args=-lubsan".into()]);
181 }
182 } else {
183 compiler_args.extend_from_slice(&[
184 "-C".into(),
185 "opt-level=3".into(),
186 "-C".into(),
187 "lto=no".into(),
188 ]);
189 }
190
191 compile_and_run_cmds(compiler_args, &test_target, &exe, test_mode)
192 })
193 .run();
194}
195
196fn compile_tests(tempdir: PathBuf, current_dir: String) {
197 build_test_runner(
198 tempdir,
199 current_dir,
200 BuildMode::Debug,
201 "lang compile",
202 "tests/compile",
203 TestMode::Compile,
204 &["simd-ffi.rs", "asm_nul_byte.rs", "global_asm_nul_byte.rs", "naked_asm_nul_byte.rs"],
205 );
206}
207
208fn run_tests(tempdir: PathBuf, current_dir: String) {
209 build_test_runner(
210 tempdir.clone(),
211 current_dir.clone(),
212 BuildMode::Debug,
213 "[DEBUG] lang run",
214 "tests/run",
215 TestMode::CompileAndRun,
216 &[],
217 );
218 build_test_runner(
219 tempdir,
220 current_dir.to_string(),
221 BuildMode::Release,
222 "[RELEASE] lang run",
223 "tests/run",
224 TestMode::CompileAndRun,
225 &[],
226 );
227}
228
229fn main() {
230 let tempdir = TempDir::new().expect("temp dir");
231 let current_dir = current_dir().expect("current dir");
232 let current_dir = current_dir.to_str().expect("current dir").to_string();
233
234 let tempdir_path: PathBuf = tempdir.as_ref().into();
235 compile_tests(tempdir_path.clone(), current_dir.clone());
236 run_tests(tempdir_path, current_dir);
237}
compiler/rustc_codegen_gcc/tests/lang_tests_debug.rs deleted-5
......@@ -1,5 +0,0 @@
1mod lang_tests_common;
2
3fn main() {
4 lang_tests_common::main_inner(lang_tests_common::Profile::Debug);
5}
compiler/rustc_codegen_gcc/tests/lang_tests_release.rs deleted-5
......@@ -1,5 +0,0 @@
1mod lang_tests_common;
2
3fn main() {
4 lang_tests_common::main_inner(lang_tests_common::Profile::Release);
5}
compiler/rustc_codegen_gcc/tests/no_builtins/no_builtins.rs created+24
......@@ -0,0 +1,24 @@
1// Test that the #![no_builtins] attribute is honored.
2// When this attribute is present, GCC should not replace code patterns
3// (like loops) with calls to builtins (like memset).
4// See https://github.com/rust-lang/rustc_codegen_gcc/issues/570
5//
6// This test is verified by the build system test `--no-builtins-tests` which
7// compiles this file and checks that `memset` is not referenced in the object file.
8
9#![no_std]
10#![no_builtins]
11#![crate_type = "lib"]
12
13// This function implements a byte-setting loop that GCC would typically
14// optimize into a memset call. With #![no_builtins], GCC should preserve
15// the loop instead of replacing it with a builtin call.
16#[no_mangle]
17#[inline(never)]
18pub unsafe fn set_bytes(mut s: *mut u8, c: u8, n: usize) {
19 let end = s.add(n);
20 while s < end {
21 *s = c;
22 s = s.add(1);
23 }
24}
compiler/rustc_codegen_gcc/tests/no_builtins/with_builtins.rs created+21
......@@ -0,0 +1,21 @@
1// Test that without #![no_builtins], GCC DOES replace code patterns with builtins.
2// This is the counterpart to no_builtins.rs - we verify that memset IS emitted
3// when the no_builtins attribute is NOT present.
4//
5// This test is verified by the build system test `--no-builtins-tests` which
6// compiles this file and checks that `memset` IS referenced in the object file.
7
8#![no_std]
9#![crate_type = "lib"]
10
11// This function implements a byte-setting loop that GCC should optimize
12// into a memset call when no_builtins is NOT set.
13#[no_mangle]
14#[inline(never)]
15pub unsafe fn set_bytes(mut s: *mut u8, c: u8, n: usize) {
16 let end = s.add(n);
17 while s < end {
18 *s = c;
19 s = s.add(1);
20 }
21}
compiler/rustc_codegen_gcc/tests/run/asm.rs-1
......@@ -213,7 +213,6 @@ fn asm() {
213213 core::arch::asm!(
214214 "",
215215 out("al") _,
216 out("bl") _,
217216 out("cl") _,
218217 out("dl") _,
219218 out("sil") _,
compiler/rustc_codegen_gcc/tests/run/call-llvm-intrinsics.rs deleted-38
......@@ -1,38 +0,0 @@
1// Compiler:
2//
3// Run-time:
4// status: 0
5
6// FIXME: Remove this test once rustc's `./tests/codegen/riscv-abi/call-llvm-intrinsics.rs`
7// stops ignoring GCC backend.
8
9#![feature(link_llvm_intrinsics)]
10#![allow(internal_features)]
11
12struct A;
13
14impl Drop for A {
15 fn drop(&mut self) {
16 println!("A");
17 }
18}
19
20extern "C" {
21 #[link_name = "llvm.sqrt.f32"]
22 fn sqrt(x: f32) -> f32;
23}
24
25pub fn do_call() {
26 let _a = A;
27
28 unsafe {
29 // Ensure that we `call` LLVM intrinsics instead of trying to `invoke` them
30 // CHECK: store float 4.000000e+00, float* %{{.}}, align 4
31 // CHECK: call float @llvm.sqrt.f32(float %{{.}}
32 sqrt(4.0);
33 }
34}
35
36fn main() {
37 do_call();
38}
compiler/rustc_codegen_gcc/tests/run/core-float.rs created+78
......@@ -0,0 +1,78 @@
1// Compiler:
2//
3// Run-time:
4// status: 0
5
6// FIXME: remove these tests (extracted from libcore) when we run the libcore tests in the CI of the
7// Rust repo.
8
9#![feature(core_intrinsics)]
10
11use std::f32::consts;
12use std::intrinsics;
13
14const EXP_APPROX: Float = 1e-6;
15const ZERO: Float = 0.0;
16const ONE: Float = 1.0;
17
18macro_rules! assert_biteq {
19 ($left:expr, $right:expr $(,)?) => {{
20 let l = $left;
21 let r = $right;
22
23 // Hack to coerce left and right to the same type
24 let mut _eq_ty = l;
25 _eq_ty = r;
26
27 // Hack to get the width from a value
28 assert!(l.to_bits() == r.to_bits());
29 }};
30}
31
32macro_rules! assert_approx_eq {
33 ($a:expr, $b:expr $(,)?) => {{ assert_approx_eq!($a, $b, $crate::num::floats::lim_for_ty($a)) }};
34 ($a:expr, $b:expr, $lim:expr) => {{
35 let (a, b) = (&$a, &$b);
36 let diff = (*a - *b).abs();
37 assert!(diff <= $lim,);
38 }};
39}
40
41type Float = f32;
42const fn flt(x: Float) -> Float {
43 x
44}
45
46fn test_exp() {
47 assert_biteq!(1.0, flt(0.0).exp());
48 assert_approx_eq!(consts::E, flt(1.0).exp(), EXP_APPROX);
49 assert_approx_eq!(148.41315910257660342111558004055227962348775, flt(5.0).exp(), EXP_APPROX);
50
51 let inf: Float = Float::INFINITY;
52 let neg_inf: Float = Float::NEG_INFINITY;
53 let nan: Float = Float::NAN;
54 assert_biteq!(inf, inf.exp());
55 assert_biteq!(0.0, neg_inf.exp());
56 assert!(nan.exp().is_nan());
57}
58
59#[inline(never)]
60fn my_abs(num: f32) -> f32 {
61 unsafe { intrinsics::fabs(num) }
62}
63
64fn test_abs() {
65 assert_biteq!(Float::INFINITY.abs(), Float::INFINITY);
66 assert_biteq!(ONE.abs(), ONE);
67 assert_biteq!(ZERO.abs(), ZERO);
68 assert_biteq!((-ZERO).abs(), ZERO);
69 assert_biteq!((-ONE).abs(), ONE);
70 assert_biteq!(Float::NEG_INFINITY.abs(), Float::INFINITY);
71 assert_biteq!((ONE / Float::NEG_INFINITY).abs(), ZERO);
72 assert!(Float::NAN.abs().is_nan());
73}
74
75fn main() {
76 test_abs();
77 test_exp();
78}
compiler/rustc_codegen_gcc/tests/run/simd-ffi.rs deleted-102
......@@ -1,102 +0,0 @@
1// Compiler:
2//
3// Run-time:
4// status: 0
5
6// FIXME: Remove this test once <tests/run-make/simd-ffi/simd.rs> stops
7// ignoring GCC backend.
8
9#![allow(internal_features, non_camel_case_types)]
10// we can compile to a variety of platforms, because we don't need
11// cross-compiled standard libraries.
12#![feature(no_core, auto_traits)]
13#![no_core]
14#![feature(repr_simd, simd_ffi, link_llvm_intrinsics, lang_items, rustc_attrs)]
15
16#[derive(Copy)]
17#[repr(simd)]
18pub struct f32x4([f32; 4]);
19
20extern "C" {
21 #[link_name = "llvm.sqrt.v4f32"]
22 fn vsqrt(x: f32x4) -> f32x4;
23}
24
25pub fn foo(x: f32x4) -> f32x4 {
26 unsafe { vsqrt(x) }
27}
28
29#[derive(Copy)]
30#[repr(simd)]
31pub struct i32x4([i32; 4]);
32
33extern "C" {
34 // _mm_sll_epi32
35 #[cfg(all(any(target_arch = "x86", target_arch = "x86-64"), target_feature = "sse2"))]
36 #[link_name = "llvm.x86.sse2.psll.d"]
37 fn integer(a: i32x4, b: i32x4) -> i32x4;
38
39 // vmaxq_s32
40 #[cfg(target_arch = "arm")]
41 #[link_name = "llvm.arm.neon.vmaxs.v4i32"]
42 fn integer(a: i32x4, b: i32x4) -> i32x4;
43 // vmaxq_s32
44 #[cfg(target_arch = "aarch64")]
45 #[link_name = "llvm.aarch64.neon.maxs.v4i32"]
46 fn integer(a: i32x4, b: i32x4) -> i32x4;
47
48 // Use a generic LLVM intrinsic to do type checking on other platforms
49 #[cfg(not(any(
50 all(any(target_arch = "x86", target_arch = "x86-64"), target_feature = "sse2"),
51 target_arch = "arm",
52 target_arch = "aarch64"
53 )))]
54 #[link_name = "llvm.smax.v4i32"]
55 fn integer(a: i32x4, b: i32x4) -> i32x4;
56}
57
58pub fn bar(a: i32x4, b: i32x4) -> i32x4 {
59 unsafe { integer(a, b) }
60}
61
62#[lang = "pointee_sized"]
63pub trait PointeeSized {}
64
65#[lang = "meta_sized"]
66pub trait MetaSized: PointeeSized {}
67
68#[lang = "sized"]
69pub trait Sized: MetaSized {}
70
71#[lang = "copy"]
72pub trait Copy {}
73
74impl Copy for f32 {}
75impl Copy for i32 {}
76impl Copy for [f32; 4] {}
77impl Copy for [i32; 4] {}
78
79pub mod marker {
80 pub use Copy;
81}
82
83#[lang = "freeze"]
84auto trait Freeze {}
85
86#[macro_export]
87#[rustc_builtin_macro]
88macro_rules! Copy {
89 () => {};
90}
91#[macro_export]
92#[rustc_builtin_macro]
93macro_rules! derive {
94 () => {};
95}
96
97#[lang = "start"]
98fn start<T>(_main: fn() -> T, _argc: isize, _argv: *const *const u8, _sigpipe: u8) -> isize {
99 0
100}
101
102fn main() {}
src/gcc+1-1
......@@ -1 +1 @@
1Subproject commit efdd0a7290c22f5438d7c5380105d353ee3e8518
1Subproject commit 6f155cc3f5a2dff33afe6cc3ed6c2e0e605ae6a3