From 4ed846ad4d4e0be96efc4837fa416aabce1882db Mon Sep 17 00:00:00 2001 From: Javier Blazquez Date: Mon, 11 Oct 2021 12:09:32 -0700 Subject: [PATCH 1/6] Add -Z no-unique-section-names to reduce ELF header bloat. This change adds a new compiler flag that can help reduce the size of ELF binaries that contain many functions. By default, when enabling function sections (which is the default for most targets), the LLVM backend will generate different section names for each function. For example, a function "func" would generate a section called ".text.func". Normally this is fine because the linker will merge all those sections into a single one in the binary. However, starting with LLVM 12 (llvm/llvm-project@ee5d1a0), the backend will also generate unique section names for exception handling, resulting in thousands of ".gcc_except_table.*" sections ending up in the final binary because some linkers don't currently merge or strip these EH sections. This can bloat the ELF headers and string table significantly in binaries that contain many functions. The new option is analogous to Clang's -fno-unique-section-names, and instructs LLVM to generate the same ".text" and ".gcc_except_table" section for each function, resulting in smaller object files and potentially a smaller final binary. --- compiler/rustc_codegen_llvm/src/back/write.rs | 2 ++ compiler/rustc_codegen_llvm/src/llvm/ffi.rs | 1 + compiler/rustc_interface/src/tests.rs | 1 + compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp | 2 ++ compiler/rustc_session/src/options.rs | 2 ++ .../src/compiler-flags/no-unique-section-names.md | 9 +++++++++ 6 files changed, 17 insertions(+) create mode 100644 src/doc/unstable-book/src/compiler-flags/no-unique-section-names.md diff --git a/compiler/rustc_codegen_llvm/src/back/write.rs b/compiler/rustc_codegen_llvm/src/back/write.rs index ab48c56a626..d6c122aabdb 100644 --- a/compiler/rustc_codegen_llvm/src/back/write.rs +++ b/compiler/rustc_codegen_llvm/src/back/write.rs @@ -161,6 +161,7 @@ pub fn target_machine_factory( let ffunction_sections = sess.opts.debugging_opts.function_sections.unwrap_or(sess.target.function_sections); let fdata_sections = ffunction_sections; + let funique_section_names = !sess.opts.debugging_opts.no_unique_section_names; let code_model = to_llvm_code_model(sess.code_model()); @@ -205,6 +206,7 @@ pub fn target_machine_factory( use_softfp, ffunction_sections, fdata_sections, + funique_section_names, trap_unreachable, singlethread, asm_comments, diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index 436d906827b..3e7048e7b80 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -2153,6 +2153,7 @@ pub fn LLVMRustCreateTargetMachine( UseSoftFP: bool, FunctionSections: bool, DataSections: bool, + UniqueSectionNames: bool, TrapUnreachable: bool, Singlethread: bool, AsmComments: bool, diff --git a/compiler/rustc_interface/src/tests.rs b/compiler/rustc_interface/src/tests.rs index cfe13b1fd4e..021afde8c62 100644 --- a/compiler/rustc_interface/src/tests.rs +++ b/compiler/rustc_interface/src/tests.rs @@ -740,6 +740,7 @@ macro_rules! tracked { tracked!(new_llvm_pass_manager, Some(true)); tracked!(no_generate_arange_section, true); tracked!(no_link, true); + tracked!(no_unique_section_names, true); tracked!(no_profiler_runtime, true); tracked!(osx_rpath_install_name, true); tracked!(panic_abort_tests, true); diff --git a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp index ddb5f7dcebf..42d2c13b783 100644 --- a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp @@ -459,6 +459,7 @@ extern "C" LLVMTargetMachineRef LLVMRustCreateTargetMachine( LLVMRustCodeGenOptLevel RustOptLevel, bool UseSoftFloat, bool FunctionSections, bool DataSections, + bool UniqueSectionNames, bool TrapUnreachable, bool Singlethread, bool AsmComments, @@ -488,6 +489,7 @@ extern "C" LLVMTargetMachineRef LLVMRustCreateTargetMachine( } Options.DataSections = DataSections; Options.FunctionSections = FunctionSections; + Options.UniqueSectionNames = UniqueSectionNames; Options.MCOptions.AsmVerbose = AsmComments; Options.MCOptions.PreserveAsmComments = AsmComments; Options.MCOptions.ABIName = ABIStr; diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index 8ecb7a031ad..b76ea98e637 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -1188,6 +1188,8 @@ mod parse { "compile without linking"), no_parallel_llvm: bool = (false, parse_no_flag, [UNTRACKED], "run LLVM in non-parallel mode (while keeping codegen-units and ThinLTO)"), + no_unique_section_names: bool = (false, parse_bool, [TRACKED], + "do not use unique names for text and data sections when -Z function-sections is used"), no_profiler_runtime: bool = (false, parse_no_flag, [TRACKED], "prevent automatic injection of the profiler_builtins crate"), normalize_docs: bool = (false, parse_bool, [TRACKED], diff --git a/src/doc/unstable-book/src/compiler-flags/no-unique-section-names.md b/src/doc/unstable-book/src/compiler-flags/no-unique-section-names.md new file mode 100644 index 00000000000..5c1c7cda701 --- /dev/null +++ b/src/doc/unstable-book/src/compiler-flags/no-unique-section-names.md @@ -0,0 +1,9 @@ +# `no-unique-section-names` + +------------------------ + +This flag currently applies only to ELF-based targets using the LLVM codegen backend. It prevents the generation of unique ELF section names for each separate code and data item when `-Z function-sections` is also in use, which is the default for most targets. This option can reduce the size of object files, and depending on the linker, the final ELF binary as well. + +For example, a function `func` will by default generate a code section called `.text.func`. Normally this is fine because the linker will merge all those `.text.*` sections into a single one in the binary. However, starting with [LLVM 12](https://github.com/llvm/llvm-project/commit/ee5d1a04), the backend will also generate unique section names for exception handling, so you would see a section name of `.gcc_except_table.func` in the object file and potentially in the final ELF binary, which could add significant bloat to programs that contain many functions. + +This flag instructs LLVM to use the same `.text` and `.gcc_except_table` section name for each function, and it is analogous to Clang's `-fno-unique-section-names` option. From 508fadab16ec36c57daa8f0361db60848d31c0f7 Mon Sep 17 00:00:00 2001 From: Ilya Yanok Date: Sat, 23 Oct 2021 11:40:46 +0200 Subject: [PATCH 2/6] Update control_flow.rs Fix and extent ControlFlow `traverse_inorder` example 1. The existing example compiles on its own, but any usage fails to be monomorphised and so doesn't compile. Fix that by using Fn trait instead of FnMut. 2. Added an example usage of `traverse_inorder` showing how we can terminate the traversal early. Fixes #90063 --- library/core/src/ops/control_flow.rs | 29 +++++++++++++++++++++++++--- 1 file changed, 26 insertions(+), 3 deletions(-) diff --git a/library/core/src/ops/control_flow.rs b/library/core/src/ops/control_flow.rs index cd2d57699c9..ec00d0fe05b 100644 --- a/library/core/src/ops/control_flow.rs +++ b/library/core/src/ops/control_flow.rs @@ -34,17 +34,40 @@ /// } /// /// impl TreeNode { -/// pub fn traverse_inorder(&self, mut f: impl FnMut(&T) -> ControlFlow) -> ControlFlow { +/// pub fn traverse_inorder(&self, f: &impl Fn(&T) -> ControlFlow) -> ControlFlow { /// if let Some(left) = &self.left { -/// left.traverse_inorder(&mut f)?; +/// left.traverse_inorder(f)?; /// } /// f(&self.value)?; /// if let Some(right) = &self.right { -/// right.traverse_inorder(&mut f)?; +/// right.traverse_inorder(f)?; /// } /// ControlFlow::Continue(()) /// } /// } +/// +/// let node = TreeNode { +/// value: 0, +/// left: Some(Box::new(TreeNode { +/// value: 1, +/// left: None, +/// right: None +/// })), +/// right: Some(Box::new(TreeNode { +/// value: 2, +/// left: None, +/// right: None +/// })) +/// }; +/// +/// node.traverse_inorder(& |val| { +/// println!("{}", val); +/// if *val <= 0 { +/// ControlFlow::Break(()) +/// } else { +/// ControlFlow::Continue(()) +/// } +/// }); /// ``` #[stable(feature = "control_flow_enum_type", since = "1.55.0")] #[derive(Debug, Clone, Copy, PartialEq)] From f3795e27c1f4ab46626f17e027f54a96ab9ea77c Mon Sep 17 00:00:00 2001 From: Ilya Yanok Date: Sat, 23 Oct 2021 11:40:46 +0200 Subject: [PATCH 3/6] Fix and extend ControlFlow `traverse_inorder` example 1. The existing example compiles on its own, but any usage fails to be monomorphised and so doesn't compile. Fix that by using a mutable reference as an input argument. 2. Added an example usage of `traverse_inorder` showing how we can terminate the traversal early. Fixes #90063 --- library/core/src/ops/control_flow.rs | 30 +++++++++++++++------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/library/core/src/ops/control_flow.rs b/library/core/src/ops/control_flow.rs index ec00d0fe05b..776cea2ef79 100644 --- a/library/core/src/ops/control_flow.rs +++ b/library/core/src/ops/control_flow.rs @@ -24,7 +24,7 @@ /// ``` /// /// A basic tree traversal: -/// ```no_run +/// ``` /// use std::ops::ControlFlow; /// /// pub struct TreeNode { @@ -34,7 +34,7 @@ /// } /// /// impl TreeNode { -/// pub fn traverse_inorder(&self, f: &impl Fn(&T) -> ControlFlow) -> ControlFlow { +/// pub fn traverse_inorder(&self, f: &mut impl FnMut(&T) -> ControlFlow) -> ControlFlow { /// if let Some(left) = &self.left { /// left.traverse_inorder(f)?; /// } @@ -44,30 +44,32 @@ /// } /// ControlFlow::Continue(()) /// } +/// fn leaf(value: T) -> Option>> { +/// Some(Box::new(Self { value, left: None, right: None })) +/// } /// } /// /// let node = TreeNode { /// value: 0, -/// left: Some(Box::new(TreeNode { -/// value: 1, -/// left: None, -/// right: None -/// })), +/// left: TreeNode::leaf(1), /// right: Some(Box::new(TreeNode { -/// value: 2, -/// left: None, -/// right: None +/// value: -1, +/// left: TreeNode::leaf(5), +/// right: TreeNode::leaf(2), /// })) /// }; +/// let mut sum = 0; /// -/// node.traverse_inorder(& |val| { -/// println!("{}", val); -/// if *val <= 0 { -/// ControlFlow::Break(()) +/// let res = node.traverse_inorder(&mut |val| { +/// if *val < 0 { +/// ControlFlow::Break(*val) /// } else { +/// sum += *val; /// ControlFlow::Continue(()) /// } /// }); +/// assert_eq!(res, ControlFlow::Break(-1)); +/// assert_eq!(sum, 6); /// ``` #[stable(feature = "control_flow_enum_type", since = "1.55.0")] #[derive(Debug, Clone, Copy, PartialEq)] From 25a82fdb329555d7564ac57aea74104940b918e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lauren=C8=9Biu=20Nicola?= Date: Mon, 25 Oct 2021 13:30:24 +0300 Subject: [PATCH 4/6] :arrow_up: rust-analyzer --- src/tools/rust-analyzer | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer b/src/tools/rust-analyzer index 91cbda43c2a..1f47693e028 160000 --- a/src/tools/rust-analyzer +++ b/src/tools/rust-analyzer @@ -1 +1 @@ -Subproject commit 91cbda43c2af82b9377eff70a21f59ade18cd23c +Subproject commit 1f47693e02809c97db61b51247ae4e4d46744c61 From c6b69017e21610b1ef491c028697728fbd3f581a Mon Sep 17 00:00:00 2001 From: b-naber Date: Mon, 25 Oct 2021 15:34:59 +0200 Subject: [PATCH 5/6] expose default substs in param_env --- compiler/rustc_ty_utils/src/ty.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/compiler/rustc_ty_utils/src/ty.rs b/compiler/rustc_ty_utils/src/ty.rs index bc77c94809e..af3706f886e 100644 --- a/compiler/rustc_ty_utils/src/ty.rs +++ b/compiler/rustc_ty_utils/src/ty.rs @@ -247,6 +247,7 @@ fn trait_of_item(tcx: TyCtxt<'_>, def_id: DefId) -> Option { } /// See `ParamEnv` struct definition for details. +#[instrument(level = "debug", skip(tcx))] fn param_env(tcx: TyCtxt<'_>, def_id: DefId) -> ty::ParamEnv<'_> { // The param_env of an impl Trait type is its defining function's param_env if let Some(parent) = ty::is_impl_trait_defn(tcx, def_id) { @@ -274,9 +275,20 @@ fn param_env(tcx: TyCtxt<'_>, def_id: DefId) -> ty::ParamEnv<'_> { predicates.extend(environment); } + // It's important that we include the default substs in unevaluated + // constants, since `Unevaluated` instances in predicates whose substs are None + // can lead to "duplicate" caller bounds candidates during trait selection, + // duplicate in the sense that both have their default substs, but the + // candidate that resulted from a superpredicate still uses `None` in its + // `substs_` field of `Unevaluated` to indicate that it has its default substs, + // whereas the other candidate has `substs_: Some(default_substs)`, see + // issue #89334 + predicates = tcx.expose_default_const_substs(predicates); + let unnormalized_env = ty::ParamEnv::new(tcx.intern_predicates(&predicates), traits::Reveal::UserFacing); + debug!("unnormalized_env caller bounds: {:?}", unnormalized_env.caller_bounds()); let body_id = def_id .as_local() .map(|def_id| tcx.hir().local_def_id_to_hir_id(def_id)) From 0199a81304fe7cf07f0d2c718a4243f47c1620ea Mon Sep 17 00:00:00 2001 From: b-naber Date: Mon, 25 Oct 2021 16:04:23 +0200 Subject: [PATCH 6/6] add tests --- .../expose-default-substs-param-env.rs | 9 +++++++++ src/test/ui/const-generics/issues/issue-89334.rs | 16 ++++++++++++++++ 2 files changed, 25 insertions(+) create mode 100644 src/test/ui/const-generics/expose-default-substs-param-env.rs create mode 100644 src/test/ui/const-generics/issues/issue-89334.rs diff --git a/src/test/ui/const-generics/expose-default-substs-param-env.rs b/src/test/ui/const-generics/expose-default-substs-param-env.rs new file mode 100644 index 00000000000..e40c93116af --- /dev/null +++ b/src/test/ui/const-generics/expose-default-substs-param-env.rs @@ -0,0 +1,9 @@ +// build-pass + +#![feature(generic_const_exprs)] +#![allow(unused_braces, incomplete_features)] + +pub trait Foo {} +pub trait Bar: Foo<{ 1 }> { } + +fn main() {} diff --git a/src/test/ui/const-generics/issues/issue-89334.rs b/src/test/ui/const-generics/issues/issue-89334.rs new file mode 100644 index 00000000000..b15b7428cdd --- /dev/null +++ b/src/test/ui/const-generics/issues/issue-89334.rs @@ -0,0 +1,16 @@ +// build-pass + +#![feature(generic_const_exprs)] +#![allow(unused_braces, incomplete_features)] + +pub trait AnotherTrait{ + const ARRAY_SIZE: usize; +} +pub trait Shard: + AsMut<[[u8; T::ARRAY_SIZE]]> +where + [(); T::ARRAY_SIZE]: Sized +{ +} + +fn main() {}