about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--compiler/rustc_codegen_ssa/src/back/link.rs36
-rw-r--r--compiler/rustc_codegen_ssa/src/back/write.rs16
-rw-r--r--compiler/rustc_codegen_ssa/src/base.rs4
-rw-r--r--tests/run-make/no-builtins-lto/Makefile18
-rw-r--r--tests/run-make/no-builtins-lto/filecheck.lto.txt17
-rw-r--r--tests/run-make/no-builtins-lto/foo.rs33
-rw-r--r--tests/run-make/no-builtins-lto/main.rs27
-rw-r--r--tests/run-make/no-builtins-lto/no_builtins.rs13
8 files changed, 107 insertions, 57 deletions
diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs
index 28a51711b93..535b594649c 100644
--- a/compiler/rustc_codegen_ssa/src/back/link.rs
+++ b/compiler/rustc_codegen_ssa/src/back/link.rs
@@ -510,8 +510,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));
@@ -1250,24 +1249,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(
@@ -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
 // 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.
@@ -2762,9 +2739,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;
     }
@@ -2778,8 +2753,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(
@@ -2796,9 +2769,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 f192747c8ab..99404423683 100644
--- a/compiler/rustc_codegen_ssa/src/back/write.rs
+++ b/compiler/rustc_codegen_ssa/src/back/write.rs
@@ -149,23 +149,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(tcx) {
             EmitObj::ObjectCode(BitcodeSection::Full)
@@ -1040,9 +1029,6 @@ fn start_executing_work<B: ExtraBackendMethods>(
 
     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 1e4ea73a172..53ea0c4924a 100644
--- a/compiler/rustc_codegen_ssa/src/base.rs
+++ b/compiler/rustc_codegen_ssa/src/base.rs
@@ -885,9 +885,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<Symbol> = info
diff --git a/tests/run-make/no-builtins-lto/Makefile b/tests/run-make/no-builtins-lto/Makefile
index c8f05d9918b..717d047c446 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 00000000000..79dc3a51501
--- /dev/null
+++ b/tests/run-make/no-builtins-lto/filecheck.lto.txt
@@ -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
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 00000000000..f09ac40b152
--- /dev/null
+++ b/tests/run-make/no-builtins-lto/foo.rs
@@ -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);
+}
diff --git a/tests/run-make/no-builtins-lto/main.rs b/tests/run-make/no-builtins-lto/main.rs
index 890c999c8cc..c474527a5ae 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 5d001031a57..33ed68e3aee 100644
--- a/tests/run-make/no-builtins-lto/no_builtins.rs
+++ b/tests/run-make/no-builtins-lto/no_builtins.rs
@@ -1,2 +1,15 @@
+#![feature(lang_items, no_core)]
+#![no_std]
+#![no_core]
 #![crate_type = "lib"]
 #![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);
+}