| author | bors <bors@rust-lang.org> 2026-04-30 04:13:56 UTC |
| committer | bors <bors@rust-lang.org> 2026-04-30 04:13:56 UTC |
| log | 57f772f25c5ce2bd870d6f8c3ab318eaee5a3326 |
| tree | a5fdaeaa37e9736dd42d98a7006713c0dc1f4f9f |
| parent | a021a7796f66600f46013d6c8d1dfc9e8d7f4a92 |
| parent | f3d34dc233e86b3107ab19a11010cf1e82f3c563 |
GCC backend subtree sync
r? ghost37 files changed, 850 insertions(+), 330 deletions(-)
compiler/rustc_codegen_gcc/.github/workflows/ci.yml+2-2| ... | ... | @@ -101,7 +101,7 @@ jobs: |
| 101 | 101 | |
| 102 | 102 | - name: Run y.sh cargo build |
| 103 | 103 | 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 | |
| 105 | 105 | |
| 106 | 106 | - name: Clean |
| 107 | 107 | run: | |
| ... | ... | @@ -119,7 +119,7 @@ jobs: |
| 119 | 119 | |
| 120 | 120 | - name: Run tests |
| 121 | 121 | run: | |
| 122 | ./y.sh test --release --clean --build-sysroot ${{ matrix.commands }} | |
| 122 | ./y.sh test --release --clean --build-sysroot --no-builtins-tests ${{ matrix.commands }} | |
| 123 | 123 | |
| 124 | 124 | duplicates: |
| 125 | 125 | runs-on: ubuntu-24.04 |
compiler/rustc_codegen_gcc/.github/workflows/failures.yml+3-1| ... | ... | @@ -48,7 +48,9 @@ jobs: |
| 48 | 48 | |
| 49 | 49 | - name: Install libgccjit12 |
| 50 | 50 | 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 | |
| 52 | 54 | |
| 53 | 55 | - name: Setup path to libgccjit |
| 54 | 56 | if: matrix.libgccjit_version.gcc == 'libgccjit12.so' |
compiler/rustc_codegen_gcc/.github/workflows/gcc12.yml+3-1| ... | ... | @@ -46,7 +46,9 @@ jobs: |
| 46 | 46 | |
| 47 | 47 | - name: Install packages |
| 48 | 48 | # `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 | |
| 50 | 52 | |
| 51 | 53 | - name: Setup path to libgccjit |
| 52 | 54 | 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: |
| 83 | 83 | run: | |
| 84 | 84 | ./y.sh prepare --only-libcore --cross |
| 85 | 85 | ./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 | |
| 87 | 87 | ./y.sh clean all |
| 88 | 88 | |
| 89 | 89 | - name: Build |
| ... | ... | @@ -110,7 +110,7 @@ jobs: |
| 110 | 110 | |
| 111 | 111 | vm_dir=$(pwd)/vm |
| 112 | 112 | 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 | |
| 114 | 114 | sudo cp target/m68k-unknown-linux-gnu/debug/hello_world $vm_dir/home/ |
| 115 | 115 | sudo chroot $vm_dir qemu-m68k-static /home/hello_world > hello_world_stdout |
| 116 | 116 | expected_output="40" |
compiler/rustc_codegen_gcc/.github/workflows/stdarch.yml+4-2| ... | ... | @@ -50,9 +50,11 @@ jobs: |
| 50 | 50 | run: | |
| 51 | 51 | mkdir intel-sde |
| 52 | 52 | 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 | |
| 54 | 56 | file=$dir.tar.xz |
| 55 | wget https://downloadmirror.intel.com/813591/$file | |
| 57 | wget https://downloadmirror.intel.com/$url_path/$file | |
| 56 | 58 | tar xvf $file |
| 57 | 59 | sudo mkdir /usr/share/intel-sde |
| 58 | 60 | 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" |
| 9 | 9 | crate-type = ["dylib"] |
| 10 | 10 | |
| 11 | 11 | [[test]] |
| 12 | name = "lang_tests_debug" | |
| 13 | path = "tests/lang_tests_debug.rs" | |
| 14 | harness = false | |
| 15 | [[test]] | |
| 16 | name = "lang_tests_release" | |
| 17 | path = "tests/lang_tests_release.rs" | |
| 12 | name = "lang_tests" | |
| 13 | path = "tests/lang_tests.rs" | |
| 18 | 14 | harness = false |
| 19 | 15 | |
| 20 | 16 | [features] |
compiler/rustc_codegen_gcc/build_system/src/test.rs+61| ... | ... | @@ -43,6 +43,7 @@ fn get_runners() -> Runners { |
| 43 | 43 | runners.insert("--extended-regex-tests", ("Run extended regex tests", extended_regex_tests)); |
| 44 | 44 | runners.insert("--mini-tests", ("Run mini tests", mini_tests)); |
| 45 | 45 | runners.insert("--cargo-tests", ("Run cargo tests", cargo_tests)); |
| 46 | runners.insert("--no-builtins-tests", ("Test #![no_builtins] attribute", no_builtins_tests)); | |
| 46 | 47 | runners |
| 47 | 48 | } |
| 48 | 49 | |
| ... | ... | @@ -317,6 +318,65 @@ fn maybe_run_command_in_vm( |
| 317 | 318 | Ok(()) |
| 318 | 319 | } |
| 319 | 320 | |
| 321 | /// Compile a source file to an object file and check if it contains a memset reference. | |
| 322 | fn 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 | ||
| 351 | fn 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 | ||
| 320 | 380 | fn std_tests(env: &Env, args: &TestArg) -> Result<(), String> { |
| 321 | 381 | let cargo_target_dir = Path::new(&args.config_info.cargo_target_dir); |
| 322 | 382 | // 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> { |
| 1248 | 1308 | test_libcore(env, args)?; |
| 1249 | 1309 | extended_sysroot_tests(env, args)?; |
| 1250 | 1310 | cargo_tests(env, args)?; |
| 1311 | no_builtins_tests(env, args)?; | |
| 1251 | 1312 | test_rustc(env, args)?; |
| 1252 | 1313 | |
| 1253 | 1314 | Ok(()) |
compiler/rustc_codegen_gcc/example/mini_core.rs+49-7| ... | ... | @@ -15,6 +15,30 @@ |
| 15 | 15 | #![no_core] |
| 16 | 16 | #![allow(dead_code, internal_features, ambiguous_wide_pointer_comparisons)] |
| 17 | 17 | |
| 18 | #[lang = "pointee_trait"] | |
| 19 | pub 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"] | |
| 29 | pub struct DynMetadata<Dyn: PointeeSized> { | |
| 30 | _vtable_ptr: NonNull<VTable>, | |
| 31 | _phantom: PhantomData<Dyn>, | |
| 32 | } | |
| 33 | ||
| 34 | unsafe 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 | ||
| 18 | 42 | #[no_mangle] |
| 19 | 43 | unsafe extern "C" fn _Unwind_Resume() { |
| 20 | 44 | intrinsics::unreachable(); |
| ... | ... | @@ -113,7 +137,7 @@ unsafe impl<'a, T: PointeeSized> Sync for &'a T {} |
| 113 | 137 | unsafe impl Sync for [u8; 16] {} |
| 114 | 138 | |
| 115 | 139 | #[lang = "freeze"] |
| 116 | unsafe auto trait Freeze {} | |
| 140 | pub unsafe auto trait Freeze {} | |
| 117 | 141 | |
| 118 | 142 | unsafe impl<T: PointeeSized> Freeze for PhantomData<T> {} |
| 119 | 143 | unsafe impl<T: PointeeSized> Freeze for *const T {} |
| ... | ... | @@ -592,6 +616,13 @@ macro_rules! pattern_type { |
| 592 | 616 | }; |
| 593 | 617 | } |
| 594 | 618 | |
| 619 | impl<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 | ||
| 624 | impl<T: DispatchFromDyn<U>, U> DispatchFromDyn<pattern_type!(U is !null)> for pattern_type!(T is !null) {} | |
| 625 | ||
| 595 | 626 | impl<T: PointeeSized, U: PointeeSized> CoerceUnsized<NonNull<U>> for NonNull<T> where T: Unsize<U> {} |
| 596 | 627 | impl<T: PointeeSized, U: PointeeSized> DispatchFromDyn<NonNull<U>> for NonNull<T> where T: Unsize<U> {} |
| 597 | 628 | |
| ... | ... | @@ -604,9 +635,9 @@ impl<T: PointeeSized, U: PointeeSized> CoerceUnsized<Unique<U>> for Unique<T> wh |
| 604 | 635 | impl<T: PointeeSized, U: PointeeSized> DispatchFromDyn<Unique<U>> for Unique<T> where T: Unsize<U> {} |
| 605 | 636 | |
| 606 | 637 | #[lang = "owned_box"] |
| 607 | pub struct Box<T: ?Sized, A: Allocator = Global>(Unique<T>, A); | |
| 638 | pub struct Box<T: ?Sized, A = Global>(Unique<T>, A); | |
| 608 | 639 | |
| 609 | impl<T: ?Sized + Unsize<U>, U: ?Sized, A: Allocator> CoerceUnsized<Box<U, A>> for Box<T, A> {} | |
| 640 | impl<T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<Box<U>> for Box<T> {} | |
| 610 | 641 | |
| 611 | 642 | impl<T> Box<T> { |
| 612 | 643 | pub fn new(val: T) -> Box<T> { |
| ... | ... | @@ -614,16 +645,27 @@ impl<T> Box<T> { |
| 614 | 645 | let size = size_of::<T>(); |
| 615 | 646 | let ptr = libc::malloc(size); |
| 616 | 647 | 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 | ) | |
| 618 | 658 | } |
| 619 | 659 | } |
| 620 | 660 | } |
| 621 | 661 | |
| 622 | impl<T: ?Sized, A: Allocator> Drop for Box<T, A> { | |
| 662 | impl<T: ?Sized, A> Drop for Box<T, A> { | |
| 623 | 663 | fn drop(&mut self) { |
| 624 | // inner value is dropped by compiler. | |
| 664 | // inner value is dropped by compiler | |
| 625 | 665 | 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); | |
| 627 | 669 | } |
| 628 | 670 | } |
| 629 | 671 | } |
compiler/rustc_codegen_gcc/libgccjit.version+1-1| ... | ... | @@ -1 +1 @@ |
| 1 | efdd0a7290c22f5438d7c5380105d353ee3e8518 | |
| 1 | 6f155cc3f5a2dff33afe6cc3ed6c2e0e605ae6a3 |
compiler/rustc_codegen_gcc/rust-toolchain+1-1| ... | ... | @@ -1,3 +1,3 @@ |
| 1 | 1 | [toolchain] |
| 2 | channel = "nightly-2026-02-14" | |
| 2 | channel = "nightly-2026-04-29" | |
| 3 | 3 | components = ["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>> { |
| 224 | 224 | |
| 225 | 225 | fn ptr_to_gcc_type(&self, cx: &CodegenCx<'gcc, 'tcx>) -> Type<'gcc> { |
| 226 | 226 | // 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) | |
| 236 | 229 | } |
| 237 | 230 | |
| 238 | 231 | #[cfg(feature = "master")] |
compiler/rustc_codegen_gcc/src/asm.rs+18-4| ... | ... | @@ -12,13 +12,13 @@ use rustc_codegen_ssa::traits::{ |
| 12 | 12 | }; |
| 13 | 13 | use rustc_middle::bug; |
| 14 | 14 | use rustc_middle::ty::Instance; |
| 15 | use rustc_span::Span; | |
| 15 | use rustc_span::{DUMMY_SP, Span}; | |
| 16 | 16 | use rustc_target::asm::*; |
| 17 | 17 | |
| 18 | 18 | use crate::builder::Builder; |
| 19 | 19 | use crate::callee::get_fn; |
| 20 | 20 | use crate::context::CodegenCx; |
| 21 | use crate::errors::UnwindingInlineAsm; | |
| 21 | use crate::errors::{NulBytesInAsm, UnwindingInlineAsm}; | |
| 22 | 22 | use crate::type_of::LayoutGccExt; |
| 23 | 23 | |
| 24 | 24 | // Rust asm! and GCC Extended Asm semantics differ substantially. |
| ... | ... | @@ -530,8 +530,15 @@ impl<'a, 'gcc, 'tcx> AsmBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tcx> { |
| 530 | 530 | template_str.push_str(INTEL_SYNTAX_INS); |
| 531 | 531 | } |
| 532 | 532 | |
| 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 | } | |
| 534 | 540 | |
| 541 | // 4. Generate Extended Asm block | |
| 535 | 542 | let block = self.llbb(); |
| 536 | 543 | let extended_asm = if let Some(dest) = dest { |
| 537 | 544 | assert!(!labels.is_empty()); |
| ... | ... | @@ -875,7 +882,7 @@ impl<'gcc, 'tcx> AsmCodegenMethods<'tcx> for CodegenCx<'gcc, 'tcx> { |
| 875 | 882 | template: &[InlineAsmTemplatePiece], |
| 876 | 883 | operands: &[GlobalAsmOperandRef<'tcx>], |
| 877 | 884 | options: InlineAsmOptions, |
| 878 | _line_spans: &[Span], | |
| 885 | line_spans: &[Span], | |
| 879 | 886 | ) { |
| 880 | 887 | let asm_arch = self.tcx.sess.asm_arch.unwrap(); |
| 881 | 888 | |
| ... | ... | @@ -942,6 +949,13 @@ impl<'gcc, 'tcx> AsmCodegenMethods<'tcx> for CodegenCx<'gcc, 'tcx> { |
| 942 | 949 | } |
| 943 | 950 | // NOTE: seems like gcc will put the asm in the wrong section, so set it to .text manually. |
| 944 | 951 | 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 | } | |
| 945 | 959 | self.context.add_top_level_asm(None, &template_str); |
| 946 | 960 | } |
| 947 | 961 |
compiler/rustc_codegen_gcc/src/base.rs+13-1| ... | ... | @@ -8,7 +8,8 @@ use rustc_codegen_ssa::ModuleCodegen; |
| 8 | 8 | use rustc_codegen_ssa::base::maybe_create_entry_wrapper; |
| 9 | 9 | use rustc_codegen_ssa::mono_item::MonoItemExt; |
| 10 | 10 | use rustc_codegen_ssa::traits::DebugInfoCodegenMethods; |
| 11 | use rustc_hir::attrs::Linkage; | |
| 11 | use rustc_hir::attrs::{AttributeKind, Linkage}; | |
| 12 | use rustc_hir::find_attr; | |
| 12 | 13 | use rustc_middle::dep_graph; |
| 13 | 14 | #[cfg(feature = "master")] |
| 14 | 15 | use rustc_middle::mono::Visibility; |
| ... | ... | @@ -137,6 +138,17 @@ pub fn compile_codegen_unit( |
| 137 | 138 | // NOTE: Rust relies on LLVM doing wrapping on overflow. |
| 138 | 139 | context.add_command_line_option("-fwrapv"); |
| 139 | 140 | |
| 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 | ||
| 140 | 152 | if let Some(model) = tcx.sess.code_model() { |
| 141 | 153 | use rustc_target::spec::CodeModel; |
| 142 | 154 |
compiler/rustc_codegen_gcc/src/builder.rs+18-16| ... | ... | @@ -33,6 +33,7 @@ use rustc_span::def_id::DefId; |
| 33 | 33 | use rustc_target::callconv::FnAbi; |
| 34 | 34 | use rustc_target::spec::{HasTargetSpec, HasX86AbiOpt, Target, X86Abi}; |
| 35 | 35 | |
| 36 | use crate::abi::FnAbiGccExt; | |
| 36 | 37 | use crate::common::{SignType, TypeReflection, type_is_pointer}; |
| 37 | 38 | use crate::context::CodegenCx; |
| 38 | 39 | use crate::errors; |
| ... | ... | @@ -213,6 +214,7 @@ impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> { |
| 213 | 214 | _typ: &str, |
| 214 | 215 | func_ptr: RValue<'gcc>, |
| 215 | 216 | args: &'b [RValue<'gcc>], |
| 217 | on_stack_param_indices: &FxHashSet<usize>, | |
| 216 | 218 | ) -> Cow<'b, [RValue<'gcc>]> { |
| 217 | 219 | let mut all_args_match = true; |
| 218 | 220 | let mut param_types = vec![]; |
| ... | ... | @@ -225,11 +227,6 @@ impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> { |
| 225 | 227 | param_types.push(param); |
| 226 | 228 | } |
| 227 | 229 | |
| 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 | ||
| 233 | 230 | if all_args_match { |
| 234 | 231 | return Cow::Borrowed(args); |
| 235 | 232 | } |
| ... | ... | @@ -351,19 +348,24 @@ impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> { |
| 351 | 348 | fn function_ptr_call( |
| 352 | 349 | &mut self, |
| 353 | 350 | typ: Type<'gcc>, |
| 351 | fn_abi: Option<&FnAbi<'tcx, Ty<'tcx>>>, | |
| 354 | 352 | mut func_ptr: RValue<'gcc>, |
| 355 | 353 | args: &[RValue<'gcc>], |
| 356 | 354 | _funclet: Option<&Funclet>, |
| 357 | 355 | ) -> 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 { | |
| 363 | 359 | func_ptr = self.context.new_cast(self.location, func_ptr, typ); |
| 364 | new_func_type | |
| 360 | typ | |
| 361 | } else { | |
| 362 | func_ptr_type | |
| 365 | 363 | } |
| 366 | 364 | }; |
| 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(); | |
| 367 | 369 | let func_name = format!("{:?}", func_ptr); |
| 368 | 370 | let previous_arg_count = args.len(); |
| 369 | 371 | let orig_args = args; |
| ... | ... | @@ -372,7 +374,7 @@ impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> { |
| 372 | 374 | llvm::adjust_intrinsic_arguments(self, gcc_func, args.into(), &func_name) |
| 373 | 375 | }; |
| 374 | 376 | 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); | |
| 376 | 378 | |
| 377 | 379 | // gccjit requires to use the result of functions, even when it's not used. |
| 378 | 380 | // 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> { |
| 599 | 601 | &mut self, |
| 600 | 602 | typ: Type<'gcc>, |
| 601 | 603 | fn_attrs: Option<&CodegenFnAttrs>, |
| 602 | _fn_abi: Option<&FnAbi<'tcx, Ty<'tcx>>>, | |
| 604 | fn_abi: Option<&FnAbi<'tcx, Ty<'tcx>>>, | |
| 603 | 605 | func: RValue<'gcc>, |
| 604 | 606 | args: &[RValue<'gcc>], |
| 605 | 607 | then: Block<'gcc>, |
| ... | ... | @@ -611,7 +613,7 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> { |
| 611 | 613 | |
| 612 | 614 | let current_block = self.block; |
| 613 | 615 | 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? | |
| 615 | 617 | self.block = current_block; |
| 616 | 618 | |
| 617 | 619 | let return_value = |
| ... | ... | @@ -645,7 +647,7 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> { |
| 645 | 647 | _funclet: Option<&Funclet>, |
| 646 | 648 | instance: Option<Instance<'tcx>>, |
| 647 | 649 | ) -> 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); | |
| 649 | 651 | let condition = self.context.new_rvalue_from_int(self.bool_type, 1); |
| 650 | 652 | self.llbb().end_with_conditional(self.location, condition, then, catch); |
| 651 | 653 | if let Some(_fn_abi) = fn_abi { |
| ... | ... | @@ -1773,7 +1775,7 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> { |
| 1773 | 1775 | self.function_call(func, args, funclet) |
| 1774 | 1776 | } else { |
| 1775 | 1777 | // 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) | |
| 1777 | 1779 | }; |
| 1778 | 1780 | if let Some(_fn_abi) = fn_abi { |
| 1779 | 1781 | // FIXME(bjorn3): Apply function attributes |
compiler/rustc_codegen_gcc/src/context.rs+1-6| ... | ... | @@ -1,9 +1,7 @@ |
| 1 | 1 | use std::cell::{Cell, RefCell}; |
| 2 | 2 | use std::collections::HashMap; |
| 3 | 3 | |
| 4 | use gccjit::{ | |
| 5 | Block, CType, Context, Function, FunctionPtrType, FunctionType, LValue, Location, RValue, Type, | |
| 6 | }; | |
| 4 | use gccjit::{Block, CType, Context, Function, FunctionType, LValue, Location, RValue, Type}; | |
| 7 | 5 | use rustc_abi::{Align, HasDataLayout, PointeeInfo, Size, TargetDataLayout, VariantIdx}; |
| 8 | 6 | use rustc_codegen_ssa::base::wants_msvc_seh; |
| 9 | 7 | use rustc_codegen_ssa::errors as ssa_errors; |
| ... | ... | @@ -100,8 +98,6 @@ pub struct CodegenCx<'gcc, 'tcx> { |
| 100 | 98 | RefCell<FxHashMap<(Ty<'tcx>, Option<ty::ExistentialTraitRef<'tcx>>), RValue<'gcc>>>, |
| 101 | 99 | |
| 102 | 100 | // 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>>>, | |
| 105 | 101 | /// Mapping from function to indexes of on stack parameters. |
| 106 | 102 | pub on_stack_function_params: RefCell<FxHashMap<Function<'gcc>, FxHashSet<usize>>>, |
| 107 | 103 | |
| ... | ... | @@ -289,7 +285,6 @@ impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> { |
| 289 | 285 | instances: Default::default(), |
| 290 | 286 | function_instances: Default::default(), |
| 291 | 287 | intrinsic_instances: Default::default(), |
| 292 | on_stack_params: Default::default(), | |
| 293 | 288 | on_stack_function_params: Default::default(), |
| 294 | 289 | vtables: Default::default(), |
| 295 | 290 | const_globals: Default::default(), |
compiler/rustc_codegen_gcc/src/errors.rs+7| ... | ... | @@ -23,3 +23,10 @@ pub(crate) struct LtoBitcodeFromRlib { |
| 23 | 23 | #[derive(Diagnostic)] |
| 24 | 24 | #[diag("explicit tail calls with the 'become' keyword are not implemented in the GCC backend")] |
| 25 | 25 | pub(crate) struct ExplicitTailCallsUnsupported; |
| 26 | ||
| 27 | #[derive(Diagnostic)] | |
| 28 | #[diag("asm contains a NUL byte")] | |
| 29 | pub(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 |
| 1589 | 1589 | "llvm.x86.tileloaddrst164" => "__builtin_trap", |
| 1590 | 1590 | "llvm.x86.tilezero" => "__builtin_trap", |
| 1591 | 1591 | "llvm.x86.tilemovrow" => "__builtin_trap", |
| 1592 | "llvm.x86.tilemovrowi" => "__builtin_trap", | |
| 1592 | 1593 | "llvm.x86.tdpbhf8ps" => "__builtin_trap", |
| 1593 | 1594 | "llvm.x86.tdphbf8ps" => "__builtin_trap", |
| 1594 | 1595 | "llvm.x86.tdpbf8ps" => "__builtin_trap", |
| ... | ... | @@ -1603,6 +1604,9 @@ pub fn intrinsic<'gcc, 'tcx>(name: &str, cx: &CodegenCx<'gcc, 'tcx>) -> Function |
| 1603 | 1604 | "llvm.x86.tcvtrowps2phh" => "__builtin_trap", |
| 1604 | 1605 | "llvm.x86.tcvtrowps2phl" => "__builtin_trap", |
| 1605 | 1606 | "llvm.x86.tcvtrowd2ps" => "__builtin_trap", |
| 1607 | "llvm.x86.tcvtrowd2psi" => "__builtin_trap", | |
| 1608 | "llvm.x86.tcvtrowps2phhi" => "__builtin_trap", | |
| 1609 | "llvm.x86.tcvtrowps2phli" => "__builtin_trap", | |
| 1606 | 1610 | "llvm.x86.tcmmimfp16ps" => "__builtin_trap", |
| 1607 | 1611 | "llvm.x86.tcmmrlfp16ps" => "__builtin_trap", |
| 1608 | 1612 |
compiler/rustc_codegen_gcc/src/intrinsic/mod.rs+90-117| ... | ... | @@ -4,9 +4,7 @@ mod simd; |
| 4 | 4 | #[cfg(feature = "master")] |
| 5 | 5 | use std::iter; |
| 6 | 6 | |
| 7 | #[cfg(feature = "master")] | |
| 8 | use gccjit::Type; | |
| 9 | use gccjit::{ComparisonOp, Function, FunctionType, RValue, ToRValue, UnaryOp}; | |
| 7 | use gccjit::{ComparisonOp, Function, FunctionType, RValue, ToRValue, Type, UnaryOp}; | |
| 10 | 8 | use rustc_abi::{BackendRepr, HasDataLayout, WrappingRange}; |
| 11 | 9 | use rustc_codegen_ssa::MemFlags; |
| 12 | 10 | use rustc_codegen_ssa::base::wants_msvc_seh; |
| ... | ... | @@ -38,6 +36,22 @@ use crate::context::CodegenCx; |
| 38 | 36 | use crate::intrinsic::simd::generic_simd_intrinsic; |
| 39 | 37 | use crate::type_of::LayoutGccExt; |
| 40 | 38 | |
| 39 | fn 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 | ||
| 41 | 55 | fn get_simple_intrinsic<'gcc, 'tcx>( |
| 42 | 56 | cx: &CodegenCx<'gcc, 'tcx>, |
| 43 | 57 | name: Symbol, |
| ... | ... | @@ -68,48 +82,19 @@ fn get_simple_intrinsic<'gcc, 'tcx>( |
| 68 | 82 | // FIXME: calling `fma` from libc without FMA target feature uses expensive software emulation |
| 69 | 83 | sym::fmuladdf32 => "fmaf", // FIXME: use gcc intrinsic analogous to llvm.fmuladd.f32 |
| 70 | 84 | 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"), | |
| 107 | 91 | sym::copysignf32 => "copysignf", |
| 108 | 92 | sym::copysignf64 => "copysign", |
| 109 | 93 | sym::floorf32 => "floorf", |
| 110 | 94 | sym::floorf64 => "floor", |
| 111 | 95 | sym::ceilf32 => "ceilf", |
| 112 | 96 | sym::ceilf64 => "ceil", |
| 97 | sym::powf128 => return float_intrinsic(cx, cx.type_f128(), "powf128"), | |
| 113 | 98 | sym::truncf32 => "truncf", |
| 114 | 99 | sym::truncf64 => "trunc", |
| 115 | 100 | // We match the LLVM backend and lower this to `rint`. |
| ... | ... | @@ -123,72 +108,6 @@ fn get_simple_intrinsic<'gcc, 'tcx>( |
| 123 | 108 | Some(cx.context.get_builtin_function(gcc_name)) |
| 124 | 109 | } |
| 125 | 110 | |
| 126 | // FIXME(antoyo): We can probably remove these and use the fallback intrinsic implementation. | |
| 127 | fn 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 | ||
| 192 | 111 | fn get_simple_function_f128<'gcc, 'tcx>( |
| 193 | 112 | span: Span, |
| 194 | 113 | cx: &CodegenCx<'gcc, 'tcx>, |
| ... | ... | @@ -198,7 +117,12 @@ fn get_simple_function_f128<'gcc, 'tcx>( |
| 198 | 117 | let func_name = match name { |
| 199 | 118 | sym::ceilf128 => "ceilf128", |
| 200 | 119 | sym::fabs => "fabsf128", |
| 120 | sym::expf128 => "expf128", | |
| 121 | sym::exp2f128 => "exp2f128", | |
| 201 | 122 | sym::floorf128 => "floorf128", |
| 123 | sym::logf128 => "logf128", | |
| 124 | sym::log2f128 => "log2f128", | |
| 125 | sym::log10f128 => "log10f128", | |
| 202 | 126 | sym::truncf128 => "truncf128", |
| 203 | 127 | sym::roundf128 => "roundf128", |
| 204 | 128 | sym::round_ties_even_f128 => "roundevenf128", |
| ... | ... | @@ -215,6 +139,24 @@ fn get_simple_function_f128<'gcc, 'tcx>( |
| 215 | 139 | ) |
| 216 | 140 | } |
| 217 | 141 | |
| 142 | fn 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 | ||
| 218 | 160 | fn f16_builtin<'gcc, 'tcx>( |
| 219 | 161 | cx: &CodegenCx<'gcc, 'tcx>, |
| 220 | 162 | name: Symbol, |
| ... | ... | @@ -224,17 +166,15 @@ fn f16_builtin<'gcc, 'tcx>( |
| 224 | 166 | let builtin_name = match name { |
| 225 | 167 | sym::ceilf16 => "__builtin_ceilf", |
| 226 | 168 | sym::copysignf16 => "__builtin_copysignf", |
| 169 | sym::expf16 => "expf", | |
| 170 | sym::exp2f16 => "exp2f", | |
| 227 | 171 | sym::fabs => "fabsf", |
| 228 | 172 | sym::floorf16 => "__builtin_floorf", |
| 229 | 173 | sym::fmaf16 => "fmaf", |
| 174 | sym::logf16 => "logf", | |
| 175 | sym::log2f16 => "log2f", | |
| 176 | sym::log10f16 => "log10f", | |
| 230 | 177 | 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 | } | |
| 238 | 178 | sym::roundf16 => "__builtin_roundf", |
| 239 | 179 | sym::round_ties_even_f16 => "__builtin_rintf", |
| 240 | 180 | sym::sqrtf16 => "__builtin_sqrtf", |
| ... | ... | @@ -264,7 +204,6 @@ impl<'a, 'gcc, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tc |
| 264 | 204 | let fn_args = instance.args; |
| 265 | 205 | |
| 266 | 206 | let simple = get_simple_intrinsic(self, name); |
| 267 | let simple_func = get_simple_function(self, name); | |
| 268 | 207 | |
| 269 | 208 | let value = match name { |
| 270 | 209 | _ if simple.is_some() => { |
| ... | ... | @@ -275,8 +214,26 @@ impl<'a, 'gcc, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tc |
| 275 | 214 | &args.iter().map(|arg| arg.immediate()).collect::<Vec<_>>(), |
| 276 | 215 | ) |
| 277 | 216 | } |
| 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 | ); | |
| 280 | 237 | self.cx.context.new_call( |
| 281 | 238 | self.location, |
| 282 | 239 | func, |
| ... | ... | @@ -285,10 +242,14 @@ impl<'a, 'gcc, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tc |
| 285 | 242 | } |
| 286 | 243 | sym::ceilf16 |
| 287 | 244 | | sym::copysignf16 |
| 245 | | sym::expf16 | |
| 246 | | sym::exp2f16 | |
| 288 | 247 | | sym::floorf16 |
| 289 | 248 | | sym::fmaf16 |
| 249 | | sym::logf16 | |
| 250 | | sym::log2f16 | |
| 251 | | sym::log10f16 | |
| 290 | 252 | | sym::powf16 |
| 291 | | sym::powif16 | |
| 292 | 253 | | sym::roundf16 |
| 293 | 254 | | sym::round_ties_even_f16 |
| 294 | 255 | | sym::sqrtf16 |
| ... | ... | @@ -299,6 +260,11 @@ impl<'a, 'gcc, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tc |
| 299 | 260 | | sym::roundf128 |
| 300 | 261 | | sym::round_ties_even_f128 |
| 301 | 262 | | sym::sqrtf128 |
| 263 | | sym::expf128 | |
| 264 | | sym::exp2f128 | |
| 265 | | sym::logf128 | |
| 266 | | sym::log2f128 | |
| 267 | | sym::log10f128 | |
| 302 | 268 | if self.cx.supports_f128_type => |
| 303 | 269 | { |
| 304 | 270 | let func = get_simple_function_f128(span, self, name); |
| ... | ... | @@ -347,6 +313,13 @@ impl<'a, 'gcc, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tc |
| 347 | 313 | &args.iter().map(|arg| arg.immediate()).collect::<Vec<_>>(), |
| 348 | 314 | ) |
| 349 | 315 | } |
| 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 | } | |
| 350 | 323 | sym::powif128 => { |
| 351 | 324 | let f128_type = self.cx.type_f128(); |
| 352 | 325 | let func = self.cx.context.new_function( |
| ... | ... | @@ -490,7 +463,7 @@ impl<'a, 'gcc, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tc |
| 490 | 463 | span_bug!(span, "expected float type for fabs intrinsic: {:?}", ty); |
| 491 | 464 | }; |
| 492 | 465 | 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), | |
| 494 | 467 | ty::FloatTy::F32 => self.context.get_builtin_function("fabsf"), |
| 495 | 468 | ty::FloatTy::F64 => self.context.get_builtin_function("fabs"), |
| 496 | 469 | 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 | ||
| 9 | use std::arch::asm; | |
| 10 | ||
| 11 | fn 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 | ||
| 10 | struct A; | |
| 11 | ||
| 12 | impl Drop for A { | |
| 13 | fn drop(&mut self) { | |
| 14 | println!("A"); | |
| 15 | } | |
| 16 | } | |
| 17 | ||
| 18 | extern "C" { | |
| 19 | #[link_name = "llvm.sqrt.f32"] | |
| 20 | fn sqrt(x: f32) -> f32; | |
| 21 | } | |
| 22 | ||
| 23 | pub 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)] | |
| 8 | extern "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 | ||
| 11 | use std::arch::global_asm; | |
| 12 | ||
| 13 | global_asm!("\0"); |
compiler/rustc_codegen_gcc/tests/compile/log.rs created+12| ... | ... | @@ -0,0 +1,12 @@ |
| 1 | // Compiler: | |
| 2 | ||
| 3 | extern "C" { | |
| 4 | fn log(message_data: u32, message_size: u32); | |
| 5 | } | |
| 6 | ||
| 7 | pub 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 | ||
| 12 | use std::arch::naked_asm; | |
| 13 | ||
| 14 | #[unsafe(naked)] | |
| 15 | pub 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)] | |
| 17 | pub struct f32x4([f32; 4]); | |
| 18 | ||
| 19 | extern "C" { | |
| 20 | #[link_name = "llvm.sqrt.v4f32"] | |
| 21 | fn vsqrt(x: f32x4) -> f32x4; | |
| 22 | } | |
| 23 | ||
| 24 | pub fn foo(x: f32x4) -> f32x4 { | |
| 25 | unsafe { vsqrt(x) } | |
| 26 | } | |
| 27 | ||
| 28 | #[derive(Copy)] | |
| 29 | #[repr(simd)] | |
| 30 | pub struct i32x4([i32; 4]); | |
| 31 | ||
| 32 | extern "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 | ||
| 57 | pub fn bar(a: i32x4, b: i32x4) -> i32x4 { | |
| 58 | unsafe { integer(a, b) } | |
| 59 | } | |
| 60 | ||
| 61 | #[lang = "pointee_sized"] | |
| 62 | pub trait PointeeSized {} | |
| 63 | ||
| 64 | #[lang = "meta_sized"] | |
| 65 | pub trait MetaSized: PointeeSized {} | |
| 66 | ||
| 67 | #[lang = "sized"] | |
| 68 | pub trait Sized: MetaSized {} | |
| 69 | ||
| 70 | #[lang = "copy"] | |
| 71 | pub trait Copy {} | |
| 72 | ||
| 73 | impl Copy for f32 {} | |
| 74 | impl Copy for i32 {} | |
| 75 | impl Copy for [f32; 4] {} | |
| 76 | impl Copy for [i32; 4] {} | |
| 77 | ||
| 78 | pub mod marker { | |
| 79 | pub use Copy; | |
| 80 | } | |
| 81 | ||
| 82 | #[lang = "freeze"] | |
| 83 | auto trait Freeze {} | |
| 84 | ||
| 85 | #[macro_export] | |
| 86 | #[rustc_builtin_macro] | |
| 87 | macro_rules! Copy { | |
| 88 | () => {}; | |
| 89 | } | |
| 90 | #[macro_export] | |
| 91 | #[rustc_builtin_macro] | |
| 92 | macro_rules! derive { | |
| 93 | () => {}; | |
| 94 | } |
compiler/rustc_codegen_gcc/tests/failing-lto-tests.txt-1| ... | ... | @@ -1,4 +1,3 @@ |
| 1 | tests/ui/lto/all-crates.rs | |
| 2 | 1 | tests/ui/lto/debuginfo-lto-alloc.rs |
| 3 | 2 | tests/ui/panic-runtime/lto-unwind.rs |
| 4 | 3 | tests/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 |
| 47 | 47 | tests/ui/sanitizer/cfi/sized-associated-ty.rs |
| 48 | 48 | tests/ui/sanitizer/cfi/can-reveal-opaques.rs |
| 49 | 49 | tests/ui/sanitizer/kcfi-mangling.rs |
| 50 | tests/ui/backtrace/dylib-dep.rs | |
| 51 | 50 | tests/ui/delegation/fn-header.rs |
| 52 | 51 | tests/ui/consts/const-eval/parse_ints.rs |
| 53 | 52 | tests/ui/simd/intrinsic/generic-as.rs |
| ... | ... | @@ -99,3 +98,16 @@ tests/ui/eii/privacy1.rs |
| 99 | 98 | tests/ui/eii/default/call_impl.rs |
| 100 | 99 | tests/ui/c-variadic/copy.rs |
| 101 | 100 | tests/ui/asm/x86_64/global_asm_escape.rs |
| 101 | tests/ui/lto/all-crates.rs | |
| 102 | tests/ui/consts/const-eval/c-variadic.rs | |
| 103 | tests/ui/eii/default/call_default_panics.rs | |
| 104 | tests/ui/explicit-tail-calls/indirect.rs | |
| 105 | tests/ui/traits/inheritance/self-in-supertype.rs | |
| 106 | tests/ui/fmt/fmt_debug/shallow.rs | |
| 107 | tests/ui/c-variadic/roundtrip.rs | |
| 108 | tests/ui/eii/eii_impl_with_contract.rs | |
| 109 | tests/ui/eii/static/cross_crate_decl.rs | |
| 110 | tests/ui/eii/static/cross_crate_def.rs | |
| 111 | tests/ui/eii/static/same_address.rs | |
| 112 | tests/ui/eii/static/simple.rs | |
| 113 | tests/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 | ||
| 3 | use std::env::current_dir; | |
| 4 | use std::path::{Path, PathBuf}; | |
| 5 | use std::process::Command; | |
| 6 | ||
| 7 | use lang_tester::LangTester; | |
| 8 | use tempfile::TempDir; | |
| 9 | ||
| 10 | fn 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)] | |
| 62 | enum BuildMode { | |
| 63 | Debug, | |
| 64 | Release, | |
| 65 | } | |
| 66 | ||
| 67 | impl BuildMode { | |
| 68 | fn is_debug(self) -> bool { | |
| 69 | matches!(self, Self::Debug) | |
| 70 | } | |
| 71 | } | |
| 72 | ||
| 73 | #[derive(Clone, Copy)] | |
| 74 | enum TestMode { | |
| 75 | Compile, | |
| 76 | CompileAndRun, | |
| 77 | } | |
| 78 | ||
| 79 | impl TestMode { | |
| 80 | fn should_run(self) -> bool { | |
| 81 | matches!(self, Self::CompileAndRun) | |
| 82 | } | |
| 83 | } | |
| 84 | ||
| 85 | fn 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 | ||
| 196 | fn 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 | ||
| 208 | fn 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 | ||
| 229 | fn 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 @@ |
| 1 | mod lang_tests_common; | |
| 2 | ||
| 3 | fn 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 @@ |
| 1 | mod lang_tests_common; | |
| 2 | ||
| 3 | fn 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)] | |
| 18 | pub 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)] | |
| 15 | pub 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() { |
| 213 | 213 | core::arch::asm!( |
| 214 | 214 | "", |
| 215 | 215 | out("al") _, |
| 216 | out("bl") _, | |
| 217 | 216 | out("cl") _, |
| 218 | 217 | out("dl") _, |
| 219 | 218 | 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 | ||
| 12 | struct A; | |
| 13 | ||
| 14 | impl Drop for A { | |
| 15 | fn drop(&mut self) { | |
| 16 | println!("A"); | |
| 17 | } | |
| 18 | } | |
| 19 | ||
| 20 | extern "C" { | |
| 21 | #[link_name = "llvm.sqrt.f32"] | |
| 22 | fn sqrt(x: f32) -> f32; | |
| 23 | } | |
| 24 | ||
| 25 | pub 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 | ||
| 36 | fn 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 | ||
| 11 | use std::f32::consts; | |
| 12 | use std::intrinsics; | |
| 13 | ||
| 14 | const EXP_APPROX: Float = 1e-6; | |
| 15 | const ZERO: Float = 0.0; | |
| 16 | const ONE: Float = 1.0; | |
| 17 | ||
| 18 | macro_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 | ||
| 32 | macro_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 | ||
| 41 | type Float = f32; | |
| 42 | const fn flt(x: Float) -> Float { | |
| 43 | x | |
| 44 | } | |
| 45 | ||
| 46 | fn 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)] | |
| 60 | fn my_abs(num: f32) -> f32 { | |
| 61 | unsafe { intrinsics::fabs(num) } | |
| 62 | } | |
| 63 | ||
| 64 | fn 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 | ||
| 75 | fn 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)] | |
| 18 | pub struct f32x4([f32; 4]); | |
| 19 | ||
| 20 | extern "C" { | |
| 21 | #[link_name = "llvm.sqrt.v4f32"] | |
| 22 | fn vsqrt(x: f32x4) -> f32x4; | |
| 23 | } | |
| 24 | ||
| 25 | pub fn foo(x: f32x4) -> f32x4 { | |
| 26 | unsafe { vsqrt(x) } | |
| 27 | } | |
| 28 | ||
| 29 | #[derive(Copy)] | |
| 30 | #[repr(simd)] | |
| 31 | pub struct i32x4([i32; 4]); | |
| 32 | ||
| 33 | extern "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 | ||
| 58 | pub fn bar(a: i32x4, b: i32x4) -> i32x4 { | |
| 59 | unsafe { integer(a, b) } | |
| 60 | } | |
| 61 | ||
| 62 | #[lang = "pointee_sized"] | |
| 63 | pub trait PointeeSized {} | |
| 64 | ||
| 65 | #[lang = "meta_sized"] | |
| 66 | pub trait MetaSized: PointeeSized {} | |
| 67 | ||
| 68 | #[lang = "sized"] | |
| 69 | pub trait Sized: MetaSized {} | |
| 70 | ||
| 71 | #[lang = "copy"] | |
| 72 | pub trait Copy {} | |
| 73 | ||
| 74 | impl Copy for f32 {} | |
| 75 | impl Copy for i32 {} | |
| 76 | impl Copy for [f32; 4] {} | |
| 77 | impl Copy for [i32; 4] {} | |
| 78 | ||
| 79 | pub mod marker { | |
| 80 | pub use Copy; | |
| 81 | } | |
| 82 | ||
| 83 | #[lang = "freeze"] | |
| 84 | auto trait Freeze {} | |
| 85 | ||
| 86 | #[macro_export] | |
| 87 | #[rustc_builtin_macro] | |
| 88 | macro_rules! Copy { | |
| 89 | () => {}; | |
| 90 | } | |
| 91 | #[macro_export] | |
| 92 | #[rustc_builtin_macro] | |
| 93 | macro_rules! derive { | |
| 94 | () => {}; | |
| 95 | } | |
| 96 | ||
| 97 | #[lang = "start"] | |
| 98 | fn start<T>(_main: fn() -> T, _argc: isize, _argv: *const *const u8, _sigpipe: u8) -> isize { | |
| 99 | 0 | |
| 100 | } | |
| 101 | ||
| 102 | fn main() {} |
src/gcc+1-1| ... | ... | @@ -1 +1 @@ |
| 1 | Subproject commit efdd0a7290c22f5438d7c5380105d353ee3e8518 | |
| 1 | Subproject commit 6f155cc3f5a2dff33afe6cc3ed6c2e0e605ae6a3 |