From 30613d85f29b494aa407bec562fbe15ae2f77bee Mon Sep 17 00:00:00 2001 From: DianQK Date: Thu, 10 Aug 2023 16:51:03 +0800 Subject: [PATCH] Restore `#![no_builtins]` crates participation in LTO. After #113716, we can make `#![no_builtins]` crates participate in LTO again. `#![no_builtins]` with LTO does not result in undefined references to the error. --- compiler/rustc_codegen_ssa/src/back/link.rs | 36 +++---------------- compiler/rustc_codegen_ssa/src/back/write.rs | 16 +-------- compiler/rustc_codegen_ssa/src/base.rs | 4 +-- tests/run-make/no-builtins-lto/Makefile | 18 ++++++---- .../no-builtins-lto/filecheck.lto.txt | 17 +++++++++ tests/run-make/no-builtins-lto/foo.rs | 32 +++++++++++++++++ tests/run-make/no-builtins-lto/main.rs | 27 +++++++++++++- tests/run-make/no-builtins-lto/no_builtins.rs | 12 +++++++ 8 files changed, 105 insertions(+), 57 deletions(-) create mode 100644 tests/run-make/no-builtins-lto/filecheck.lto.txt create mode 100644 tests/run-make/no-builtins-lto/foo.rs diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs index cd6201648ee9b..4795f8e233a4c 100644 --- a/compiler/rustc_codegen_ssa/src/back/link.rs +++ b/compiler/rustc_codegen_ssa/src/back/link.rs @@ -516,8 +516,7 @@ fn link_staticlib<'a>( &codegen_results.crate_info, Some(CrateType::Staticlib), &mut |cnum, path| { - let lto = are_upstream_rust_objects_already_included(sess) - && !ignored_for_lto(sess, &codegen_results.crate_info, cnum); + let lto = are_upstream_rust_objects_already_included(sess); let native_libs = codegen_results.crate_info.native_libraries[&cnum].iter(); let relevant = native_libs.clone().filter(|lib| relevant_lib(sess, &lib)); @@ -1256,24 +1255,6 @@ fn link_sanitizer_runtime(sess: &Session, linker: &mut dyn Linker, name: &str) { } } -/// Returns a boolean indicating whether the specified crate should be ignored -/// during LTO. -/// -/// Crates ignored during LTO are not lumped together in the "massive object -/// file" that we create and are linked in their normal rlib states. See -/// comments below for what crates do not participate in LTO. -/// -/// It's unusual for a crate to not participate in LTO. Typically only -/// compiler-specific and unstable crates have a reason to not participate in -/// LTO. -pub fn ignored_for_lto(sess: &Session, info: &CrateInfo, cnum: CrateNum) -> bool { - // If our target enables builtin function lowering in LLVM then the - // crates providing these functions don't participate in LTO (e.g. - // no_builtins or compiler builtins crates). - !sess.target.no_builtins - && (info.compiler_builtins == Some(cnum) || info.is_no_builtins.contains(&cnum)) -} - /// This functions tries to determine the appropriate linker (and corresponding LinkerFlavor) to use pub fn linker_and_flavor(sess: &Session) -> (PathBuf, LinkerFlavor) { fn infer_from( @@ -2739,10 +2720,6 @@ fn rehome_sysroot_lib_dir<'a>(sess: &'a Session, lib_dir: &Path) -> PathBuf { // symbols). We must continue to include the rest of the rlib, however, as // it may contain static native libraries which must be linked in. // -// (*) Crates marked with `#![no_builtins]` don't participate in LTO and -// their bytecode wasn't included. The object files in those libraries must -// still be passed to the linker. -// // Note, however, that if we're not doing LTO we can just pass the rlib // blindly to the linker (fast) because it's fine if it's not actually // included as we're at the end of the dependency chain. @@ -2768,9 +2745,7 @@ fn add_static_crate<'a>( cmd.link_rlib(&rlib_path); }; - if !are_upstream_rust_objects_already_included(sess) - || ignored_for_lto(sess, &codegen_results.crate_info, cnum) - { + if !are_upstream_rust_objects_already_included(sess) { link_upstream(cratepath); return; } @@ -2784,8 +2759,6 @@ fn add_static_crate<'a>( let canonical_name = name.replace('-', "_"); let upstream_rust_objects_already_included = are_upstream_rust_objects_already_included(sess); - let is_builtins = - sess.target.no_builtins || !codegen_results.crate_info.is_no_builtins.contains(&cnum); let mut archive = archive_builder_builder.new_archive_builder(sess); if let Err(error) = archive.add_archive( @@ -2802,9 +2775,8 @@ fn add_static_crate<'a>( // If we're performing LTO and this is a rust-generated object // file, then we don't need the object file as it's part of the - // LTO module. Note that `#![no_builtins]` is excluded from LTO, - // though, so we let that object file slide. - if upstream_rust_objects_already_included && is_rust_object && is_builtins { + // LTO module. + if upstream_rust_objects_already_included && is_rust_object { return true; } diff --git a/compiler/rustc_codegen_ssa/src/back/write.rs b/compiler/rustc_codegen_ssa/src/back/write.rs index 4071852574191..5f77c0d91fade 100644 --- a/compiler/rustc_codegen_ssa/src/back/write.rs +++ b/compiler/rustc_codegen_ssa/src/back/write.rs @@ -148,23 +148,12 @@ impl ModuleConfig { let emit_obj = if !should_emit_obj { EmitObj::None - } else if sess.target.obj_is_bitcode - || (sess.opts.cg.linker_plugin_lto.enabled() && !no_builtins) - { + } else if sess.target.obj_is_bitcode || sess.opts.cg.linker_plugin_lto.enabled() { // This case is selected if the target uses objects as bitcode, or // if linker plugin LTO is enabled. In the linker plugin LTO case // the assumption is that the final link-step will read the bitcode // and convert it to object code. This may be done by either the // native linker or rustc itself. - // - // Note, however, that the linker-plugin-lto requested here is - // explicitly ignored for `#![no_builtins]` crates. These crates are - // specifically ignored by rustc's LTO passes and wouldn't work if - // loaded into the linker. These crates define symbols that LLVM - // lowers intrinsics to, and these symbol dependencies aren't known - // until after codegen. As a result any crate marked - // `#![no_builtins]` is assumed to not participate in LTO and - // instead goes on to generate object code. EmitObj::Bitcode } else if need_bitcode_in_object(sess) { EmitObj::ObjectCode(BitcodeSection::Full) @@ -1025,9 +1014,6 @@ fn start_executing_work( let mut each_linked_rlib_for_lto = Vec::new(); drop(link::each_linked_rlib(crate_info, None, &mut |cnum, path| { - if link::ignored_for_lto(sess, crate_info, cnum) { - return; - } each_linked_rlib_for_lto.push((cnum, path.to_path_buf())); })); diff --git a/compiler/rustc_codegen_ssa/src/base.rs b/compiler/rustc_codegen_ssa/src/base.rs index 4819ab22723d9..c6813e79cd3b3 100644 --- a/compiler/rustc_codegen_ssa/src/base.rs +++ b/compiler/rustc_codegen_ssa/src/base.rs @@ -872,9 +872,7 @@ impl CrateInfo { // If global LTO is enabled then almost everything (*) is glued into a single object file, // so this logic is not necessary and can cause issues on some targets (due to weak lang // item symbols being "privatized" to that object file), so we disable it. - // (*) Native libs, and `#[compiler_builtins]` and `#[no_builtins]` crates are not glued, - // and we assume that they cannot define weak lang items. This is not currently enforced - // by the compiler, but that's ok because all this stuff is unstable anyway. + // (*) Native libs are not glued, and we assume that they cannot define weak lang items. let target = &tcx.sess.target; if !are_upstream_rust_objects_already_included(tcx.sess) { let missing_weak_lang_items: FxHashSet = info diff --git a/tests/run-make/no-builtins-lto/Makefile b/tests/run-make/no-builtins-lto/Makefile index c8f05d9918b91..717d047c4469d 100644 --- a/tests/run-make/no-builtins-lto/Makefile +++ b/tests/run-make/no-builtins-lto/Makefile @@ -1,9 +1,15 @@ include ../tools.mk +# only-x86_64 + +# We want to check that `no_builtins` is correctly participating in LTO. +# First, verify that the `foo::foo` symbol can be found when linking. +# Next, verify that `memcpy` can be customized using `no_builtins` under LTO. +# Others will use the built-in memcpy. + all: - # Compile a `#![no_builtins]` rlib crate - $(RUSTC) no_builtins.rs - # Build an executable that depends on that crate using LTO. The no_builtins crate doesn't - # participate in LTO, so its rlib must be explicitly linked into the final binary. Verify this by - # grepping the linker arguments. - $(RUSTC) main.rs -C lto --print link-args | $(CGREP) 'libno_builtins.rlib' + $(RUSTC) -C linker-plugin-lto -C opt-level=2 -C debuginfo=0 foo.rs + $(RUSTC) -C linker-plugin-lto -C opt-level=2 -C debuginfo=0 no_builtins.rs + $(RUSTC) main.rs -C lto -C opt-level=2 -C debuginfo=0 -C save-temps -C metadata=1 -C codegen-units=1 + $(LLVM_BIN_DIR)/llvm-dis $(TMPDIR)/main.main.*-cgu.0.rcgu.lto.input.bc -o $(TMPDIR)/lto.ll + cat "$(TMPDIR)"/lto.ll | "$(LLVM_FILECHECK)" filecheck.lto.txt diff --git a/tests/run-make/no-builtins-lto/filecheck.lto.txt b/tests/run-make/no-builtins-lto/filecheck.lto.txt new file mode 100644 index 0000000000000..62991cc2a28d0 --- /dev/null +++ b/tests/run-make/no-builtins-lto/filecheck.lto.txt @@ -0,0 +1,17 @@ +CHECK: define{{.*}} void @bar +CHECK-NEXT: call void @_ZN11no_builtins11no_builtins17hcda8eeb3c587012fE +CHECK-NEXT: call void @llvm.memcpy + +CHECK: define{{.*}} i32 @main +CHECK: call void @bar + +CHECK: define{{.*}} void @_ZN3foo3foo17hfad400915e51b713E +CHECK-NEXT: call void @llvm.memcpy + +CHECK: define{{.*}} void @_ZN11no_builtins11no_builtins17hcda8eeb3c587012fE +CHECK-SAME: #[[ATTR:[0-9]+]] { +CHECK: call void @_ZN3foo3foo17hfad400915e51b713E +CHECK-NEXT: call{{.*}} @memcpy + +CHECK: attributes #[[ATTR]] +CHECK-SAME: no-builtins diff --git a/tests/run-make/no-builtins-lto/foo.rs b/tests/run-make/no-builtins-lto/foo.rs new file mode 100644 index 0000000000000..33efac3c513ed --- /dev/null +++ b/tests/run-make/no-builtins-lto/foo.rs @@ -0,0 +1,32 @@ +#![feature(lang_items, no_core)] +#![no_std] +#![no_core] +#![crate_type = "lib"] + +#[inline(never)] +pub unsafe fn foo(dest: *mut u8, src: *const u8) { + // should call `@llvm.memcpy`. + memcpy(dest, src, 1024); +} + +#[no_mangle] +#[inline(never)] +pub unsafe extern "C" fn memcpy(dest: *mut u8, src: *const u8, _n: usize) -> *mut u8 { + *dest = 0; + return src as *mut u8; +} + +#[lang = "sized"] +trait Sized {} +#[lang = "copy"] +trait Copy {} +impl Copy for *mut u8 {} +impl Copy for *const u8 {} + +#[lang = "drop_in_place"] +#[allow(unconditional_recursion)] +pub unsafe fn drop_in_place(to_drop: *mut T) { + // Code here does not matter - this is replaced by the + // real drop glue by the compiler. + drop_in_place(to_drop); +} diff --git a/tests/run-make/no-builtins-lto/main.rs b/tests/run-make/no-builtins-lto/main.rs index 890c999c8ccf7..c474527a5ae80 100644 --- a/tests/run-make/no-builtins-lto/main.rs +++ b/tests/run-make/no-builtins-lto/main.rs @@ -1,3 +1,28 @@ +#![feature(no_core, start, lang_items)] +#![no_std] +// We use `no_core` to reduce the LTO products is small enough. +#![no_core] + extern crate no_builtins; +extern crate foo; + +#[link(name = "c")] +extern "C" {} + +#[start] +fn main(_: isize, p: *const *const u8) -> isize { + // Make sure the symbols are retained. + unsafe { bar(*p as *mut u8, *p); } + 0 +} + +#[no_mangle] +#[inline(never)] +pub unsafe extern "C" fn bar(dest: *mut u8, src: *const u8) { + no_builtins::no_builtins(dest, src); + // should call `@llvm.memcpy` + foo::memcpy(dest, src, 1024); +} -fn main() {} +#[lang = "eh_personality"] +fn eh_personality() {} diff --git a/tests/run-make/no-builtins-lto/no_builtins.rs b/tests/run-make/no-builtins-lto/no_builtins.rs index 5d001031a57fa..d7d35a9505f52 100644 --- a/tests/run-make/no-builtins-lto/no_builtins.rs +++ b/tests/run-make/no-builtins-lto/no_builtins.rs @@ -1,2 +1,14 @@ +#![feature(lang_items, no_core)] +#![no_std] +#![no_core] #![crate_type = "lib"] #![no_builtins] + +extern crate foo; + +pub unsafe fn no_builtins(dest: *mut u8, src: *const u8) { + // There should be no "undefined reference to `foo::foo'". + foo::foo(dest, src); + // should call `@memcpy` instead of `@llvm.memcpy`. + foo::memcpy(dest, src, 1024); +}