From db16909ae341f7e2de29e0e458d945fd0cdec8aa Mon Sep 17 00:00:00 2001 From: Jorge Aparicio Date: Fri, 12 Aug 2016 21:15:42 -0500 Subject: [PATCH] exclude `#![no_builtins]` crates from LTO this prevents intrinsics like `memcpy` from being mis-optimized to infinite recursive calls when LTO is used. fixes #31544 closes #35540 --- src/librustc_trans/back/link.rs | 28 ++++++++++++++++++---------- src/librustc_trans/back/lto.rs | 11 ++++++++++- 2 files changed, 28 insertions(+), 11 deletions(-) diff --git a/src/librustc_trans/back/link.rs b/src/librustc_trans/back/link.rs index a9f3d2f8a175..d17da74c87f3 100644 --- a/src/librustc_trans/back/link.rs +++ b/src/librustc_trans/back/link.rs @@ -42,7 +42,7 @@ use std::process::Command; use std::str; use flate; use syntax::ast; -use syntax::attr::AttrMetaMethods; +use syntax::attr::{self, AttrMetaMethods}; use syntax_pos::Span; // RLIB LLVM-BYTECODE OBJECT LAYOUT @@ -938,8 +938,10 @@ fn add_upstream_rust_crates(cmd: &mut Linker, Linkage::NotLinked | Linkage::IncludedFromDylib => {} Linkage::Static => { + let is_a_no_builtins_crate = + attr::contains_name(&sess.cstore.crate_attrs(cnum), "no_builtins"); add_static_crate(cmd, sess, tmpdir, crate_type, - &src.rlib.unwrap().0) + &src.rlib.unwrap().0, is_a_no_builtins_crate) } Linkage::Dynamic => { add_dynamic_crate(cmd, sess, &src.dylib.unwrap().0) @@ -963,12 +965,16 @@ fn add_upstream_rust_crates(cmd: &mut Linker, // * For LTO, we remove upstream object files. // * For dylibs we remove metadata and bytecode from upstream rlibs // - // When performing LTO, all of the bytecode from the upstream libraries has - // already been included in our object file output. As a result we need to - // remove the object files in the upstream libraries so the linker doesn't - // try to include them twice (or whine about duplicate symbols). We must - // continue to include the rest of the rlib, however, as it may contain - // static native libraries which must be linked in. + // When performing LTO, almost(*) all of the bytecode from the upstream + // libraries has already been included in our object file output. As a + // result we need to remove the object files in the upstream libraries so + // the linker doesn't try to include them twice (or whine about duplicate + // 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. // // When making a dynamic library, linkers by default don't include any // object files in an archive if they're not necessary to resolve the link. @@ -988,7 +994,8 @@ fn add_upstream_rust_crates(cmd: &mut Linker, sess: &Session, tmpdir: &Path, crate_type: config::CrateType, - cratepath: &Path) { + cratepath: &Path, + is_a_no_builtins_crate: bool) { if !sess.lto() && crate_type != config::CrateTypeDylib { cmd.link_rlib(&fix_windows_verbatim_for_gcc(cratepath)); return @@ -1012,7 +1019,8 @@ fn add_upstream_rust_crates(cmd: &mut Linker, } let canonical = f.replace("-", "_"); let canonical_name = name.replace("-", "_"); - if sess.lto() && canonical.starts_with(&canonical_name) && + if sess.lto() && !is_a_no_builtins_crate && + canonical.starts_with(&canonical_name) && canonical.ends_with(".o") { let num = &f[name.len()..f.len() - 2]; if num.len() > 0 && num[1..].parse::().is_ok() { diff --git a/src/librustc_trans/back/lto.rs b/src/librustc_trans/back/lto.rs index 69e4a50804fa..016eb7c0cfb3 100644 --- a/src/librustc_trans/back/lto.rs +++ b/src/librustc_trans/back/lto.rs @@ -17,6 +17,7 @@ use llvm::{ModuleRef, TargetMachineRef, True, False}; use rustc::util::common::time; use rustc::util::common::path2cstr; use back::write::{ModuleConfig, with_llvm_pmb}; +use syntax::attr; use libc; use flate; @@ -52,7 +53,15 @@ pub fn run(sess: &session::Session, llmod: ModuleRef, // For each of our upstream dependencies, find the corresponding rlib and // load the bitcode from the archive. Then merge it into the current LLVM // module that we've got. - link::each_linked_rlib(sess, &mut |_, path| { + link::each_linked_rlib(sess, &mut |cnum, path| { + let is_a_no_builtins_crate = + attr::contains_name(&sess.cstore.crate_attrs(cnum), "no_builtins"); + + // `#![no_builtins]` crates don't participate in LTO. + if is_a_no_builtins_crate { + return; + } + let archive = ArchiveRO::open(&path).expect("wanted an rlib"); let bytecodes = archive.iter().filter_map(|child| { child.ok().and_then(|c| c.name().map(|name| (name, c)))