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.
This commit is contained in:
parent
e20cb77021
commit
520081721c
@ -510,8 +510,7 @@ fn link_staticlib<'a>(
|
|||||||
&codegen_results.crate_info,
|
&codegen_results.crate_info,
|
||||||
Some(CrateType::Staticlib),
|
Some(CrateType::Staticlib),
|
||||||
&mut |cnum, path| {
|
&mut |cnum, path| {
|
||||||
let lto = are_upstream_rust_objects_already_included(sess)
|
let lto = are_upstream_rust_objects_already_included(sess);
|
||||||
&& !ignored_for_lto(sess, &codegen_results.crate_info, cnum);
|
|
||||||
|
|
||||||
let native_libs = codegen_results.crate_info.native_libraries[&cnum].iter();
|
let native_libs = codegen_results.crate_info.native_libraries[&cnum].iter();
|
||||||
let relevant = native_libs.clone().filter(|lib| relevant_lib(sess, &lib));
|
let relevant = native_libs.clone().filter(|lib| relevant_lib(sess, &lib));
|
||||||
@ -1250,24 +1249,6 @@ fn find_sanitizer_runtime(sess: &Session, filename: &str) -> PathBuf {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 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
|
/// This functions tries to determine the appropriate linker (and corresponding LinkerFlavor) to use
|
||||||
pub fn linker_and_flavor(sess: &Session) -> (PathBuf, LinkerFlavor) {
|
pub fn linker_and_flavor(sess: &Session) -> (PathBuf, LinkerFlavor) {
|
||||||
fn infer_from(
|
fn infer_from(
|
||||||
@ -2733,10 +2714,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
|
// symbols). We must continue to include the rest of the rlib, however, as
|
||||||
// it may contain static native libraries which must be linked in.
|
// 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
|
// 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
|
// 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.
|
// included as we're at the end of the dependency chain.
|
||||||
@ -2762,9 +2739,7 @@ fn add_static_crate<'a>(
|
|||||||
cmd.link_rlib(&rlib_path);
|
cmd.link_rlib(&rlib_path);
|
||||||
};
|
};
|
||||||
|
|
||||||
if !are_upstream_rust_objects_already_included(sess)
|
if !are_upstream_rust_objects_already_included(sess) {
|
||||||
|| ignored_for_lto(sess, &codegen_results.crate_info, cnum)
|
|
||||||
{
|
|
||||||
link_upstream(cratepath);
|
link_upstream(cratepath);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -2778,8 +2753,6 @@ fn add_static_crate<'a>(
|
|||||||
let canonical_name = name.replace('-', "_");
|
let canonical_name = name.replace('-', "_");
|
||||||
let upstream_rust_objects_already_included =
|
let upstream_rust_objects_already_included =
|
||||||
are_upstream_rust_objects_already_included(sess);
|
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);
|
let mut archive = archive_builder_builder.new_archive_builder(sess);
|
||||||
if let Err(error) = archive.add_archive(
|
if let Err(error) = archive.add_archive(
|
||||||
@ -2796,9 +2769,8 @@ fn add_static_crate<'a>(
|
|||||||
|
|
||||||
// If we're performing LTO and this is a rust-generated object
|
// 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
|
// 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,
|
// LTO module.
|
||||||
// though, so we let that object file slide.
|
if upstream_rust_objects_already_included && is_rust_object {
|
||||||
if upstream_rust_objects_already_included && is_rust_object && is_builtins {
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -149,23 +149,12 @@ macro_rules! if_regular {
|
|||||||
|
|
||||||
let emit_obj = if !should_emit_obj {
|
let emit_obj = if !should_emit_obj {
|
||||||
EmitObj::None
|
EmitObj::None
|
||||||
} else if sess.target.obj_is_bitcode
|
} else if sess.target.obj_is_bitcode || sess.opts.cg.linker_plugin_lto.enabled() {
|
||||||
|| (sess.opts.cg.linker_plugin_lto.enabled() && !no_builtins)
|
|
||||||
{
|
|
||||||
// This case is selected if the target uses objects as bitcode, or
|
// This case is selected if the target uses objects as bitcode, or
|
||||||
// if linker plugin LTO is enabled. In the linker plugin LTO case
|
// if linker plugin LTO is enabled. In the linker plugin LTO case
|
||||||
// the assumption is that the final link-step will read the bitcode
|
// 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
|
// and convert it to object code. This may be done by either the
|
||||||
// native linker or rustc itself.
|
// 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
|
EmitObj::Bitcode
|
||||||
} else if need_bitcode_in_object(tcx) {
|
} else if need_bitcode_in_object(tcx) {
|
||||||
EmitObj::ObjectCode(BitcodeSection::Full)
|
EmitObj::ObjectCode(BitcodeSection::Full)
|
||||||
@ -1040,9 +1029,6 @@ fn start_executing_work<B: ExtraBackendMethods>(
|
|||||||
|
|
||||||
let mut each_linked_rlib_for_lto = Vec::new();
|
let mut each_linked_rlib_for_lto = Vec::new();
|
||||||
drop(link::each_linked_rlib(crate_info, None, &mut |cnum, path| {
|
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()));
|
each_linked_rlib_for_lto.push((cnum, path.to_path_buf()));
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
@ -885,9 +885,7 @@ pub fn new(tcx: TyCtxt<'_>, target_cpu: String) -> CrateInfo {
|
|||||||
// If global LTO is enabled then almost everything (*) is glued into a single object file,
|
// 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
|
// 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.
|
// item symbols being "privatized" to that object file), so we disable it.
|
||||||
// (*) Native libs, and `#[compiler_builtins]` and `#[no_builtins]` crates are not glued,
|
// (*) Native libs are not glued, and we assume that they cannot define weak lang items.
|
||||||
// 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.
|
|
||||||
let target = &tcx.sess.target;
|
let target = &tcx.sess.target;
|
||||||
if !are_upstream_rust_objects_already_included(tcx.sess) {
|
if !are_upstream_rust_objects_already_included(tcx.sess) {
|
||||||
let missing_weak_lang_items: FxHashSet<Symbol> = info
|
let missing_weak_lang_items: FxHashSet<Symbol> = info
|
||||||
|
@ -1,9 +1,15 @@
|
|||||||
include ../tools.mk
|
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:
|
all:
|
||||||
# Compile a `#![no_builtins]` rlib crate
|
$(RUSTC) -C linker-plugin-lto -C opt-level=2 -C debuginfo=0 foo.rs
|
||||||
$(RUSTC) no_builtins.rs
|
$(RUSTC) -C linker-plugin-lto -C opt-level=2 -C debuginfo=0 no_builtins.rs
|
||||||
# Build an executable that depends on that crate using LTO. The no_builtins crate doesn't
|
$(RUSTC) main.rs -C lto -C opt-level=2 -C debuginfo=0 -C save-temps -C metadata=1 -C codegen-units=1
|
||||||
# participate in LTO, so its rlib must be explicitly linked into the final binary. Verify this by
|
$(LLVM_BIN_DIR)/llvm-dis $(TMPDIR)/main.main.*-cgu.0.rcgu.lto.input.bc -o $(TMPDIR)/lto.ll
|
||||||
# grepping the linker arguments.
|
cat "$(TMPDIR)"/lto.ll | "$(LLVM_FILECHECK)" filecheck.lto.txt
|
||||||
$(RUSTC) main.rs -C lto --print link-args | $(CGREP) 'libno_builtins.rlib'
|
|
||||||
|
17
tests/run-make/no-builtins-lto/filecheck.lto.txt
Normal file
17
tests/run-make/no-builtins-lto/filecheck.lto.txt
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
CHECK: define{{.*}} void @bar
|
||||||
|
CHECK-NEXT: call void @no_builtins
|
||||||
|
CHECK-NEXT: call void @llvm.memcpy
|
||||||
|
|
||||||
|
CHECK: define{{.*}} i32 @main
|
||||||
|
CHECK: call void @bar
|
||||||
|
|
||||||
|
CHECK: define{{.*}} void @foo
|
||||||
|
CHECK-NEXT: call void @llvm.memcpy
|
||||||
|
|
||||||
|
CHECK: define{{.*}} void @no_builtins
|
||||||
|
CHECK-SAME: #[[ATTR:[0-9]+]] {
|
||||||
|
CHECK: call void @foo
|
||||||
|
CHECK-NEXT: call{{.*}} @memcpy
|
||||||
|
|
||||||
|
CHECK: attributes #[[ATTR]]
|
||||||
|
CHECK-SAME: no-builtins
|
33
tests/run-make/no-builtins-lto/foo.rs
Normal file
33
tests/run-make/no-builtins-lto/foo.rs
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
#![feature(lang_items, no_core)]
|
||||||
|
#![no_std]
|
||||||
|
#![no_core]
|
||||||
|
#![crate_type = "lib"]
|
||||||
|
|
||||||
|
#[inline(never)]
|
||||||
|
#[no_mangle]
|
||||||
|
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<T: ?Sized>(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);
|
||||||
|
}
|
@ -1,3 +1,28 @@
|
|||||||
extern crate no_builtins;
|
#![feature(no_core, start, lang_items)]
|
||||||
|
#![no_std]
|
||||||
|
// We use `no_core` to reduce the LTO products is small enough.
|
||||||
|
#![no_core]
|
||||||
|
|
||||||
fn main() {}
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[lang = "eh_personality"]
|
||||||
|
fn eh_personality() {}
|
||||||
|
@ -1,2 +1,15 @@
|
|||||||
|
#![feature(lang_items, no_core)]
|
||||||
|
#![no_std]
|
||||||
|
#![no_core]
|
||||||
#![crate_type = "lib"]
|
#![crate_type = "lib"]
|
||||||
#![no_builtins]
|
#![no_builtins]
|
||||||
|
|
||||||
|
extern crate foo;
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user