diff --git a/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs b/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs index 97cfc648b7cb0..bdefacefd20b9 100644 --- a/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs +++ b/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs @@ -8,7 +8,7 @@ use rustc_middle::ty::{Instance, Ty, TyCtxt, TypeVisitableExt}; use rustc_middle::{bug, ty}; use rustc_span::sym; use rustc_target::callconv::{ArgAbi, FnAbi, PassMode}; -use rustc_target::spec::{Arch, BinaryFormat}; +use rustc_target::spec::{Arch, BinaryFormat, Env, Os}; use crate::common; use crate::mir::AsmCodegenMethods; @@ -128,6 +128,8 @@ fn prefix_and_suffix<'tcx>( let is_arm = tcx.sess.target.arch == Arch::Arm; let is_thumb = tcx.sess.unstable_target_features.contains(&sym::thumb_mode); + let function_sections = + tcx.sess.opts.unstable_opts.function_sections.unwrap_or(tcx.sess.target.function_sections); // If we're compiling the compiler-builtins crate, e.g., the equivalent of // compiler-rt, then we want to implicitly compile everything with hidden @@ -218,8 +220,6 @@ fn prefix_and_suffix<'tcx>( let mut end = String::new(); match asm_binary_format { BinaryFormat::Elf => { - let section = link_section.unwrap_or_else(|| format!(".text.{asm_name}")); - let progbits = match is_arm { true => "%progbits", false => "@progbits", @@ -230,7 +230,13 @@ fn prefix_and_suffix<'tcx>( false => "@function", }; - writeln!(begin, ".pushsection {section},\"ax\", {progbits}").unwrap(); + if let Some(section) = &link_section { + writeln!(begin, ".pushsection {section},\"ax\", {progbits}").unwrap(); + } else if function_sections { + writeln!(begin, ".pushsection .text.{asm_name},\"ax\", {progbits}").unwrap(); + } else { + writeln!(begin, ".text").unwrap(); + } writeln!(begin, ".balign {align_bytes}").unwrap(); write_linkage(&mut begin).unwrap(); match visibility { @@ -249,14 +255,22 @@ fn prefix_and_suffix<'tcx>( // pattern match on assembly generated by LLVM. writeln!(end, ".Lfunc_end_{asm_name}:").unwrap(); writeln!(end, ".size {asm_name}, . - {asm_name}").unwrap(); - writeln!(end, ".popsection").unwrap(); + if link_section.is_some() || function_sections { + writeln!(end, ".popsection").unwrap(); + } if !arch_suffix.is_empty() { writeln!(end, "{}", arch_suffix).unwrap(); } } BinaryFormat::MachO => { - let section = link_section.unwrap_or_else(|| "__TEXT,__text".to_string()); - writeln!(begin, ".pushsection {},regular,pure_instructions", section).unwrap(); + // NOTE: LLVM ignores `-Zfunction-sections` on macos. Instead the Mach-O symbol + // subsection splitting feature is used, which can be enabled with the + // `.subsections_via_symbols` global directive. LLVM already enables this directive. + if let Some(section) = &link_section { + writeln!(begin, ".pushsection {section},regular,pure_instructions").unwrap(); + } else { + writeln!(begin, ".section __TEXT,__text,regular,pure_instructions").unwrap(); + } writeln!(begin, ".balign {align_bytes}").unwrap(); write_linkage(&mut begin).unwrap(); match visibility { @@ -267,7 +281,9 @@ fn prefix_and_suffix<'tcx>( writeln!(end).unwrap(); writeln!(end, ".Lfunc_end_{asm_name}:").unwrap(); - writeln!(end, ".popsection").unwrap(); + if link_section.is_some() { + writeln!(end, ".popsection").unwrap(); + } if !arch_suffix.is_empty() { writeln!(end, "{}", arch_suffix).unwrap(); } @@ -278,8 +294,36 @@ fn prefix_and_suffix<'tcx>( writeln!(begin, ".type 32").unwrap(); writeln!(begin, ".endef").unwrap(); - let section = link_section.unwrap_or_else(|| format!(".text.{asm_name}")); - writeln!(begin, ".pushsection {},\"xr\"", section).unwrap(); + if let Some(section) = &link_section { + writeln!(begin, ".section {section},\"xr\"").unwrap() + } else if !function_sections { + // Function sections are enabled by default on MSVC and windows-gnullvm, + // but disabled by default on GNU. + writeln!(begin, ".text").unwrap(); + } else { + // LLVM uses an extension to the section directive to support defining multiple + // sections with the same name and comdat. It adds `unique,` at the end of the + // `.section` directive. We have no way of generating that unique ID here, so don't + // emit it. + // + // See https://llvm.org/docs/Extensions.html#id2. + match &tcx.sess.target.options.env { + Env::Gnu => { + writeln!(begin, ".section .text${asm_name},\"xr\",one_only,{asm_name}") + .unwrap(); + } + Env::Msvc => { + writeln!(begin, ".section .text,\"xr\",one_only,{asm_name}").unwrap(); + } + Env::Unspecified => match &tcx.sess.target.options.os { + Os::Uefi => { + writeln!(begin, ".section .text,\"xr\",one_only,{asm_name}").unwrap(); + } + _ => bug!("unexpected coff target {}", tcx.sess.target.llvm_target), + }, + other => bug!("unexpected coff env {other:?}"), + } + } write_linkage(&mut begin).unwrap(); writeln!(begin, ".balign {align_bytes}").unwrap(); writeln!(begin, "{asm_name}:").unwrap(); diff --git a/src/tools/run-make-support/src/external_deps/rustc.rs b/src/tools/run-make-support/src/external_deps/rustc.rs index 2fa680a8e2334..e8645e00e1852 100644 --- a/src/tools/run-make-support/src/external_deps/rustc.rs +++ b/src/tools/run-make-support/src/external_deps/rustc.rs @@ -449,6 +449,16 @@ impl Rustc { self.cmd.arg("-Zcodegen-source-order"); self } + + /// Specify `-Z function-sections={yes, no}`. + pub fn function_sections(&mut self, enable: bool) -> &mut Self { + let flag = match enable { + true => "-Zfunction-sections=yes", + false => "-Zfunction-sections=no", + }; + self.cmd.arg(flag); + self + } } /// Query the sysroot path corresponding `rustc --print=sysroot`. diff --git a/tests/assembly-llvm/naked-functions/function-sections.rs b/tests/assembly-llvm/naked-functions/function-sections.rs new file mode 100644 index 0000000000000..751812bd768b2 --- /dev/null +++ b/tests/assembly-llvm/naked-functions/function-sections.rs @@ -0,0 +1,97 @@ +//@ add-minicore +//@ assembly-output: emit-asm +// +//@ revisions: linux-x86-gnu-fs-true linux-x86-gnu-fs-false +//@[linux-x86-gnu-fs-true] compile-flags: --target x86_64-unknown-linux-gnu -Zfunction-sections=true +//@[linux-x86-gnu-fs-true] needs-llvm-components: x86 +//@[linux-x86-gnu-fs-false] compile-flags: --target x86_64-unknown-linux-gnu -Zfunction-sections=false +//@[linux-x86-gnu-fs-false] needs-llvm-components: x86 +// +//@ revisions: macos-aarch64-fs-true macos-aarch64-fs-false +//@[macos-aarch64-fs-true] compile-flags: --target aarch64-apple-darwin -Zfunction-sections=true +//@[macos-aarch64-fs-true] needs-llvm-components: aarch64 +//@[macos-aarch64-fs-false] compile-flags: --target aarch64-apple-darwin -Zfunction-sections=false +//@[macos-aarch64-fs-false] needs-llvm-components: aarch64 +// +//@ revisions: windows-x86-gnu-fs-true windows-x86-gnu-fs-false +//@[windows-x86-gnu-fs-true] compile-flags: --target x86_64-pc-windows-gnu -Zfunction-sections=true +//@[windows-x86-gnu-fs-true] needs-llvm-components: x86 +//@[windows-x86-gnu-fs-false] compile-flags: --target x86_64-pc-windows-gnu -Zfunction-sections=false +//@[windows-x86-gnu-fs-false] needs-llvm-components: x86 +// +//@ revisions: windows-x86-msvc-fs-true windows-x86-msvc-fs-false +//@[windows-x86-msvc-fs-true] compile-flags: --target x86_64-pc-windows-msvc -Zfunction-sections=true +//@[windows-x86-msvc-fs-true] needs-llvm-components: x86 +//@[windows-x86-msvc-fs-false] compile-flags: --target x86_64-pc-windows-msvc -Zfunction-sections=false +//@[windows-x86-msvc-fs-false] needs-llvm-components: x86 +// +//@ revisions: x86-uefi-fs-true x86-uefi-fs-false +//@[x86-uefi-fs-true] compile-flags: --target x86_64-unknown-uefi -Zfunction-sections=true +//@[x86-uefi-fs-true] needs-llvm-components: x86 +//@[x86-uefi-fs-false] compile-flags: --target x86_64-unknown-uefi -Zfunction-sections=false +//@[x86-uefi-fs-false] needs-llvm-components: x86 + +#![crate_type = "lib"] +#![feature(no_core)] +#![no_core] + +// Tests that naked and non-naked functions emit the same directives when (not) using +// -Zfunction-sections. This setting is ignored on macos, off by default on windows gnu, +// and on by default in the remaining revisions tested here. + +extern crate minicore; +use minicore::*; + +#[unsafe(naked)] +#[unsafe(no_mangle)] +extern "C" fn naked_ret() { + // linux-x86-gnu-fs-true: .section .text.naked_ret,"ax",@progbits + // linux-x86-gnu-fs-false: .text + // + // macos-aarch64-fs-true: .section __TEXT,__text,regular,pure_instructions + // macos-aarch64-fs-false: .section __TEXT,__text,regular,pure_instructions + // + // NOTE: the regular function below adds `unique,0` at the end, but we have no way of generating + // the unique ID to use there, so don't emit that part. + // + // windows-x86-gnu-fs-true: .section .text$naked_ret,"xr",one_only,naked_ret + // windows-x86-msvc-fs-true: .section .text,"xr",one_only,naked_ret + // x86-uefi-fs-true: .section .text,"xr",one_only,naked_ret + // + // windows-x86-gnu-fs-false: .text + // windows-x86-msvc-fs-false: .text + // x86-uefi-fs-false: .text + // + // CHECK-LABEL: naked_ret: + naked_asm!("ret") +} + +// Use a different section here so that `regular_ret` has to explicitly specify the section. +#[link_section = cfg_select!( + target_os = "macos" => "__FOO,bar", + _ => ".bar", +)] +#[unsafe(no_mangle)] +extern "C" fn omarker() -> i32 { + // CHECK-LABEL: omarker: + 32 +} + +#[unsafe(no_mangle)] +extern "C" fn regular_ret() { + // linux-x86-gnu-fs-true: .section .text.regular_ret,"ax",@progbits + // linux-x86-gnu-fs-false: .text + // + // macos-aarch64-fs-true: .section __TEXT,__text,regular,pure_instructions + // macos-aarch64-fs-false: .section __TEXT,__text,regular,pure_instructions + // + // windows-x86-gnu-fs-true: .section .text$regular_ret,"xr",one_only,regular_ret,unique,0 + // windows-x86-msvc-fs-true: .section .text,"xr",one_only,regular_ret,unique,0 + // x86-uefi-fs-true: .section .text,"xr",one_only,regular_ret,unique,0 + // + // windows-x86-gnu-fs-false: .text + // windows-x86-msvc-fs-false: .text + // x86-uefi-fs-false: .text + // + // CHECK-LABEL: regular_ret: +} diff --git a/tests/assembly-llvm/naked-functions/link-section-windows.rs b/tests/assembly-llvm/naked-functions/link-section-windows.rs index 5823498973a33..35782a7bf92c4 100644 --- a/tests/assembly-llvm/naked-functions/link-section-windows.rs +++ b/tests/assembly-llvm/naked-functions/link-section-windows.rs @@ -1,4 +1,4 @@ -//@ revisions: windows-x86-gnu windows-x86-msvc +//@ revisions: windows-x86-gnu windows-x86-msvc x86-uefi //@ add-minicore //@ assembly-output: emit-asm // @@ -7,6 +7,9 @@ // //@[windows-x86-msvc] compile-flags: --target x86_64-pc-windows-msvc //@[windows-x86-msvc] needs-llvm-components: x86 +// +//@[x86-uefi] compile-flags: --target x86_64-unknown-uefi +//@[x86-uefi] needs-llvm-components: x86 #![crate_type = "lib"] #![feature(no_core)] diff --git a/tests/codegen-llvm/naked-fn/naked-functions.rs b/tests/codegen-llvm/naked-fn/naked-functions.rs index b5c84ede8f063..a782ab5310e34 100644 --- a/tests/codegen-llvm/naked-fn/naked-functions.rs +++ b/tests/codegen-llvm/naked-fn/naked-functions.rs @@ -1,12 +1,21 @@ +// ignore-tidy-linelength +// //@ add-minicore -//@ revisions: linux win_x86 win_i686 macos thumb +//@ revisions: linux linux_no_function_sections macos thumb +//@ revisions: win_x86_msvc win_x86_gnu win_i686_gnu win_x86_gnu_function_sections // //@[linux] compile-flags: --target x86_64-unknown-linux-gnu //@[linux] needs-llvm-components: x86 -//@[win_x86] compile-flags: --target x86_64-pc-windows-gnu -//@[win_x86] needs-llvm-components: x86 -//@[win_i686] compile-flags: --target i686-pc-windows-gnu -//@[win_i686] needs-llvm-components: x86 +//@[linux_no_function_sections] compile-flags: --target x86_64-unknown-linux-gnu -Zfunction-sections=false +//@[linux_no_function_sections] needs-llvm-components: x86 +//@[win_x86_gnu] compile-flags: --target x86_64-pc-windows-gnu +//@[win_x86_gnu] needs-llvm-components: x86 +//@[win_x86_gnu_function_sections] compile-flags: --target x86_64-pc-windows-gnu -Zfunction-sections +//@[win_x86_gnu_function_sections] needs-llvm-components: x86 +//@[win_x86_msvc] compile-flags: --target x86_64-pc-windows-msvc +//@[win_x86_msvc] needs-llvm-components: x86 +//@[win_i686_gnu] compile-flags: --target i686-pc-windows-gnu +//@[win_i686_gnu] needs-llvm-components: x86 //@[macos] compile-flags: --target aarch64-apple-darwin //@[macos] needs-llvm-components: aarch64 //@[thumb] compile-flags: --target thumbv7em-none-eabi @@ -19,15 +28,22 @@ extern crate minicore; use minicore::*; -// linux,win_x86,win_i686: .intel_syntax +// linux,win_x86_gnu,win_i686_gnu: .intel_syntax // // linux: .pushsection .text.naked_empty,\22ax\22, @progbits -// macos: .pushsection __TEXT,__text,regular,pure_instructions +// linux_no_function_sections: .text +// macos-NOT: .pushsection +// +// win_x86_msvc: .section .text,\22xr\22,one_only,naked_empty +// win_x86_gnu_function_sections: .section .text$naked_empty,\22xr\22,one_only,naked_empty +// win_x86_gnu-NOT: .section +// win_i686_gnu-NOT: .section +// // thumb: .pushsection .text.naked_empty,\22ax\22, %progbits // // linux, macos, thumb: .balign 4 // -// linux,thumb: .globl naked_empty +// linux,win_x86_gnu,thumb: .globl naked_empty // macos: .globl _naked_empty // // CHECK-NOT: .private_extern @@ -35,12 +51,12 @@ use minicore::*; // // linux: .type naked_empty, @function // -// win_x86: .def naked_empty -// win_i686: .def _naked_empty +// win_x86_msvc,win_x86_gnu: .def naked_empty +// win_i686_gnu: .def _naked_empty // -// win_x86,win_i686: .scl 2 -// win_x86,win_i686: .type 32 -// win_x86,win_i686: .endef +// win_x86_msvc,win_x86_gnu,win_i686_gnu: .scl 2 +// win_x86_msvc,win_x86_gnu,win_i686_gnu: .type 32 +// win_x86_msvc,win_x86_gnu,win_i686_gnu: .endef // // win_x86: .pushsection .text.naked_empty,\22xr\22 // win_i686: .pushsection .text._naked_empty,\22xr\22 @@ -56,10 +72,11 @@ use minicore::*; // // CHECK-LABEL: naked_empty: // -// linux,macos,win_x86,win_x86: ret +// linux,macos,win_x86_msvc,win_x86_gnu,win_i686_gnu: ret // thumb: bx lr // -// linux,macos,thumb: .popsection +// linux,windows,win_x86_msvc,thumb: .popsection +// win_x86_gnu-NOT,win_i686_gnu-NOT: .popsection // // thumb: .thumb // @@ -78,15 +95,22 @@ pub extern "C" fn naked_empty() { } } -// linux,win_x86,win_i686: .intel_syntax +// linux,win_x86_gnu,win_i686_gnu,win_x86_msvc: .intel_syntax // // linux: .pushsection .text.naked_with_args_and_return,\22ax\22, @progbits -// macos: .pushsection __TEXT,__text,regular,pure_instructions +// linux_no_function_sections: .text +// macos-NOT: .pushsection +// +// win_x86_msvc: .section .text,\22xr\22,one_only,naked_with_args_and_return +// win_x86_gnu_function_sections: .section .text$naked_with_args_and_return,\22xr\22,one_only,naked_with_args_and_return +// win_x86_gnu-NOT: .section +// win_i686_gnu-NOT: .section +// // thumb: .pushsection .text.naked_with_args_and_return,\22ax\22, %progbits // // linux, macos, thumb: .balign 4 // -// linux,thumb: .globl naked_with_args_and_return +// linux,win_x86_gnu,win_x86_msvc,win_i686_gnu,thumb: .globl naked_with_args_and_return // macos: .globl _naked_with_args_and_return // // CHECK-NOT: .private_extern @@ -94,12 +118,12 @@ pub extern "C" fn naked_empty() { // // linux: .type naked_with_args_and_return, @function // -// win_x86: .def naked_with_args_and_return -// win_i686: .def _naked_with_args_and_return +// win_x86_msvc,win_x86_gnu: .def naked_with_args_and_return +// win_i686_gnu: .def _naked_with_args_and_return // -// win_x86,win_i686: .scl 2 -// win_x86,win_i686: .type 32 -// win_x86,win_i686: .endef +// win_x86_msvc,win_x86_gnu,win_i686_gnu: .scl 2 +// win_x86_msvc,win_x86_gnu,win_i686_gnu: .type 32 +// win_x86_msvc,win_x86_gnu,win_i686_gnu: .endef // // win_x86: .pushsection .text.naked_with_args_and_return,\22xr\22 // win_i686: .pushsection .text._naked_with_args_and_return,\22xr\22 @@ -115,14 +139,15 @@ pub extern "C" fn naked_empty() { // // CHECK-LABEL: naked_with_args_and_return: // -// linux, win_x86,win_i686: lea rax, [rdi + rsi] +// linux,win_x86_msvc,win_x86_gnu,win_i686_gnu: lea rax, [rdi + rsi] // macos: add x0, x0, x1 // thumb: adds r0, r0, r1 // -// linux,macos,win_x86,win_i686: ret +// linux,macos,win_x86_msvc,win_x86_gnu,win_i686_gnu: ret // thumb: bx lr // -// linux,macos,thumb: .popsection +// linux,windows,win_x86_msvc,thumb: .popsection +// win_x86_gnu-NOT,win_i686_gnu-NOT: .popsection // // thumb: .thumb // @@ -144,9 +169,10 @@ pub extern "C" fn naked_with_args_and_return(a: isize, b: isize) -> isize { } } -// linux: .pushsection .text.some_different_name,\22ax\22, @progbits +// linux,linux_no_function_sections: .pushsection .text.some_different_name,\22ax\22, @progbits // macos: .pushsection .text.some_different_name,regular,pure_instructions -// win_x86,win_i686: .pushsection .text.some_different_name,\22xr\22 +// win_x86_msvc,win_x86_gnu,win_i686_gnu: .section .text.some_different_name,\22xr\22 +// win_x86_gnu_function_sections: .section .text.some_different_name,\22xr\22 // thumb: .pushsection .text.some_different_name,\22ax\22, %progbits // CHECK-LABEL: test_link_section: #[no_mangle] @@ -163,15 +189,15 @@ pub extern "C" fn test_link_section() { } } -// win_x86: .def fastcall_cc -// win_i686: .def @fastcall_cc@4 +// win_x86_msvc,win_x86_gnu: .def fastcall_cc +// win_i686_gnu: .def @fastcall_cc@4 // -// win_x86,win_i686: .scl 2 -// win_x86,win_i686: .type 32 -// win_x86,win_i686: .endef +// win_x86_msvc,win_x86_gnu,win_i686_gnu: .scl 2 +// win_x86_msvc,win_x86_gnu,win_i686_gnu: .type 32 +// win_x86_msvc,win_x86_gnu,win_i686_gnu: .endef // -// win_x86-LABEL: fastcall_cc: -// win_i686-LABEL: @fastcall_cc@4: +// win_x86_msvc-LABEL,win_x86_gnu-LABEL: fastcall_cc: +// win_i686_gnu-LABEL: @fastcall_cc@4: #[cfg(target_os = "windows")] #[no_mangle] #[unsafe(naked)] diff --git a/tests/run-make/naked-dead-code-elimination/main.rs b/tests/run-make/naked-dead-code-elimination/main.rs new file mode 100644 index 0000000000000..5df2691e5b443 --- /dev/null +++ b/tests/run-make/naked-dead-code-elimination/main.rs @@ -0,0 +1,48 @@ +#![feature(cfg_target_object_format)] +use std::arch::naked_asm; + +#[unsafe(naked)] +#[unsafe(no_mangle)] +extern "C" fn used() { + naked_asm!("ret") +} + +#[unsafe(no_mangle)] +extern "C" fn used_clothed() -> i32 { + 41 +} + +pub fn main() { + std::hint::black_box(used()); + std::hint::black_box(used_clothed()); +} + +#[unsafe(no_mangle)] +extern "C" fn unused_clothed() -> i32 { + 42 +} + +#[unsafe(naked)] +#[unsafe(no_mangle)] +extern "C" fn unused() { + naked_asm!("ret") +} + +#[unsafe(naked)] +#[unsafe(link_section = cfg_select!( + target_object_format = "mach-o" => "__TEXT,foobar", + _ => ".foobar", +))] +#[unsafe(no_mangle)] +extern "C" fn unused_link_section() { + naked_asm!("ret") +} + +#[unsafe(link_section = cfg_select!( + target_object_format = "mach-o" => "__TEXT,baz", + _ => ".baz", +))] +#[unsafe(no_mangle)] +extern "C" fn unused_link_section_clothed() -> i32 { + 43 +} diff --git a/tests/run-make/naked-dead-code-elimination/rmake.rs b/tests/run-make/naked-dead-code-elimination/rmake.rs new file mode 100644 index 0000000000000..1be22de367c99 --- /dev/null +++ b/tests/run-make/naked-dead-code-elimination/rmake.rs @@ -0,0 +1,39 @@ +//@ ignore-cross-compile +//@ needs-asm-support + +use run_make_support::symbols::object_contains_any_symbol; +use run_make_support::{bin_name, rustc}; + +fn main() { + let bin = bin_name("main"); + + rustc().input("main.rs").opt().function_sections(false).run(); + + // Check that the naked symbol is eliminated when the "clothed" one is. + + assert_eq!( + object_contains_any_symbol(&bin, &["unused_clothed"]), + object_contains_any_symbol(&bin, &["unused"]) + ); + + assert_eq!( + object_contains_any_symbol(&bin, &["unused_link_section_clothed"]), + object_contains_any_symbol(&bin, &["unused_link_section"]) + ); + + // --- + + rustc().input("main.rs").opt().function_sections(true).run(); + + // Check that the naked symbol is eliminated when the "clothed" one is. + + assert_eq!( + object_contains_any_symbol(&bin, &["unused_clothed"]), + object_contains_any_symbol(&bin, &["unused"]) + ); + + assert_eq!( + object_contains_any_symbol(&bin, &["unused_link_section_clothed"]), + object_contains_any_symbol(&bin, &["unused_link_section"]) + ); +}