]> git.lizzy.rs Git - rust.git/commitdiff
rustc: Preserve reachable extern fns with LTO
authorAlex Crichton <alex@alexcrichton.com>
Sat, 7 Jun 2014 00:48:46 +0000 (17:48 -0700)
committerAlex Crichton <alex@alexcrichton.com>
Sat, 7 Jun 2014 02:52:21 +0000 (19:52 -0700)
All rust functions are internal implementation details with respect to the ABI
exposed by crates, but extern fns are public components of the ABI and shouldn't
be stripped. This commit serializes reachable extern fns to metadata, so when
LTO is performed all of their symbols are not stripped.

Closes #14500

src/librustc/metadata/common.rs
src/librustc/metadata/csearch.rs
src/librustc/metadata/decoder.rs
src/librustc/metadata/encoder.rs
src/librustc/middle/trans/base.rs
src/test/run-make/issue-14500/Makefile [new file with mode: 0644]
src/test/run-make/issue-14500/bar.rs [new file with mode: 0644]
src/test/run-make/issue-14500/foo.c [new file with mode: 0644]
src/test/run-make/issue-14500/foo.rs [new file with mode: 0644]
src/test/run-make/lto-smoke-c/Makefile
src/test/run-make/tools.mk

index 8aeef3dca98677434e4cd92cb94a532c8006f766..6287683c1a1416313d15822ed2085d2b40ec3fc3 100644 (file)
@@ -209,6 +209,9 @@ pub fn from_uint(value : uint) -> Option<astencode_tag> {
 pub static tag_method_argument_names: uint = 0x8e;
 pub static tag_method_argument_name: uint = 0x8f;
 
+pub static tag_reachable_extern_fns: uint = 0x90;
+pub static tag_reachable_extern_fn_id: uint = 0x91;
+
 #[deriving(Clone, Show)]
 pub struct LinkMeta {
     pub crateid: CrateId,
index 74a804763e83ef95cf5c21fc5cfbd31d7745f4e5..c7ad74dce571c5fcae73c6900728d68fa3fe0fd0 100644 (file)
@@ -314,3 +314,10 @@ pub fn get_method_arg_names(cstore: &cstore::CStore, did: ast::DefId)
     let cdata = cstore.get_crate_data(did.krate);
     decoder::get_method_arg_names(&*cdata, did.node)
 }
+
+pub fn get_reachable_extern_fns(cstore: &cstore::CStore, cnum: ast::CrateNum)
+    -> Vec<ast::DefId>
+{
+    let cdata = cstore.get_crate_data(cnum);
+    decoder::get_reachable_extern_fns(&*cdata)
+}
index 6469462734e553190560e6e43937c9b3185f8434..c67b5bf1a60076363268a27db47f53d0e7a2df31 100644 (file)
@@ -1325,3 +1325,17 @@ pub fn get_method_arg_names(cdata: Cmd, id: ast::NodeId) -> Vec<String> {
     }
     return ret;
 }
+
+pub fn get_reachable_extern_fns(cdata: Cmd) -> Vec<ast::DefId> {
+    let mut ret = Vec::new();
+    let items = reader::get_doc(ebml::Doc::new(cdata.data()),
+                                tag_reachable_extern_fns);
+    reader::tagged_docs(items, tag_reachable_extern_fn_id, |doc| {
+        ret.push(ast::DefId {
+            krate: cdata.cnum,
+            node: reader::doc_as_u32(doc),
+        });
+        true
+    });
+    return ret;
+}
index cd2f3360f830dd77fec84852a8f271fb65fa5d32..37cb75e4697b30cff2dc253d672077d074ff6302 100644 (file)
@@ -75,6 +75,7 @@ pub struct EncodeParams<'a> {
     pub link_meta: &'a LinkMeta,
     pub cstore: &'a cstore::CStore,
     pub encode_inlined_item: EncodeInlinedItem<'a>,
+    pub reachable: &'a NodeSet,
 }
 
 pub struct EncodeContext<'a> {
@@ -87,6 +88,7 @@ pub struct EncodeContext<'a> {
     pub cstore: &'a cstore::CStore,
     pub encode_inlined_item: RefCell<EncodeInlinedItem<'a>>,
     pub type_abbrevs: tyencode::abbrev_map,
+    pub reachable: &'a NodeSet,
 }
 
 fn encode_name(ebml_w: &mut Encoder, name: Name) {
@@ -1702,6 +1704,26 @@ fn encode_misc_info(ecx: &EncodeContext,
     ebml_w.end_tag();
 }
 
+fn encode_reachable_extern_fns(ecx: &EncodeContext, ebml_w: &mut Encoder) {
+    ebml_w.start_tag(tag_reachable_extern_fns);
+
+    for id in ecx.reachable.iter() {
+        match ecx.tcx.map.find(*id) {
+            Some(ast_map::NodeItem(i)) => {
+                match i.node {
+                    ast::ItemFn(_, _, abi, _, _) if abi != abi::Rust => {
+                        ebml_w.wr_tagged_u32(tag_reachable_extern_fn_id, *id);
+                    }
+                    _ => {}
+                }
+            }
+            _ => {}
+        }
+    }
+
+    ebml_w.end_tag();
+}
+
 fn encode_crate_dep(ebml_w: &mut Encoder,
                     dep: decoder::CrateDep) {
     ebml_w.start_tag(tag_crate_dep);
@@ -1801,6 +1823,7 @@ struct Stats {
         encode_inlined_item,
         link_meta,
         non_inlineable_statics,
+        reachable,
         ..
     } = parms;
     let ecx = EncodeContext {
@@ -1813,6 +1836,7 @@ struct Stats {
         cstore: cstore,
         encode_inlined_item: RefCell::new(encode_inlined_item),
         type_abbrevs: RefCell::new(HashMap::new()),
+        reachable: reachable,
      };
 
     let mut ebml_w = writer::Encoder::new(wr);
@@ -1864,6 +1888,7 @@ struct Stats {
     // Encode miscellaneous info.
     i = ebml_w.writer.tell().unwrap();
     encode_misc_info(&ecx, krate, &mut ebml_w);
+    encode_reachable_extern_fns(&ecx, &mut ebml_w);
     stats.misc_bytes = ebml_w.writer.tell().unwrap() - i;
 
     // Encode and index the items.
index 9be07eaaca9340b7a7e95723d513422b11680af0..09f5d2a350769b79bf82024591c588ae200b7989 100644 (file)
@@ -2238,6 +2238,7 @@ pub fn crate_ctxt_to_encode_parms<'r>(cx: &'r CrateContext, ie: encoder::EncodeI
             link_meta: &cx.link_meta,
             cstore: &cx.sess().cstore,
             encode_inlined_item: ie,
+            reachable: &cx.reachable,
         }
 }
 
@@ -2374,6 +2375,16 @@ pub fn trans_crate(krate: ast::Crate,
         ccx.item_symbols.borrow().find(id).map(|s| s.to_string())
     }).collect();
 
+    // For the purposes of LTO, we add to the reachable set all of the upstream
+    // reachable extern fns. These functions are all part of the public ABI of
+    // the final product, so LTO needs to preserve them.
+    ccx.sess().cstore.iter_crate_data(|cnum, _| {
+        let syms = csearch::get_reachable_extern_fns(&ccx.sess().cstore, cnum);
+        reachable.extend(syms.move_iter().map(|did| {
+            csearch::get_symbol(&ccx.sess().cstore, did)
+        }));
+    });
+
     // Make sure that some other crucial symbols are not eliminated from the
     // module. This includes the main function, the crate map (used for debug
     // log settings and I/O), and finally the curious rust_stack_exhausted
diff --git a/src/test/run-make/issue-14500/Makefile b/src/test/run-make/issue-14500/Makefile
new file mode 100644 (file)
index 0000000..c1087b0
--- /dev/null
@@ -0,0 +1,14 @@
+-include ../tools.mk
+
+# Test to make sure that reachable extern fns are always available in final
+# productcs, including when LTO is used. In this test, the `foo` crate has a
+# reahable symbol, and is a dependency of the `bar` crate. When the `bar` crate
+# is compiled with LTO, it shouldn't strip the symbol from `foo`, and that's the
+# only way that `foo.c` will successfully compile.
+
+all:
+       $(RUSTC) foo.rs --crate-type=rlib
+       $(RUSTC) bar.rs --crate-type=staticlib -Zlto -L. -o $(TMPDIR)/libbar.a
+       $(CC) foo.c -lbar -o $(call RUN_BINFILE,foo) $(EXTRACFLAGS)
+       $(call RUN,foo)
+
diff --git a/src/test/run-make/issue-14500/bar.rs b/src/test/run-make/issue-14500/bar.rs
new file mode 100644 (file)
index 0000000..4b4916f
--- /dev/null
@@ -0,0 +1,11 @@
+// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+extern crate foo;
diff --git a/src/test/run-make/issue-14500/foo.c b/src/test/run-make/issue-14500/foo.c
new file mode 100644 (file)
index 0000000..25098ac
--- /dev/null
@@ -0,0 +1,16 @@
+// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+extern void foo();
+
+int main() {
+    foo();
+    return 0;
+}
diff --git a/src/test/run-make/issue-14500/foo.rs b/src/test/run-make/issue-14500/foo.rs
new file mode 100644 (file)
index 0000000..ceca907
--- /dev/null
@@ -0,0 +1,12 @@
+// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#[no_mangle]
+pub extern fn foo() {}
index 49a04ce42a0ae7f16a5fc60ba46c9fb6406a406d..9b44c3e582a71da13db68407fedadaaf5435796c 100644 (file)
@@ -1,23 +1,10 @@
 -include ../tools.mk
 
-ifdef IS_WINDOWS
-       EXTRAFLAGS :=
-else
-ifeq ($(shell uname),Darwin)
-else
-ifeq ($(shell uname),FreeBSD)
-       EXTRAFLAGS := -lm -lpthread -lgcc_s
-else
-       EXTRAFLAGS := -lm -lrt -ldl -lpthread
-endif
-endif
-endif
-
 # Apparently older versions of GCC segfault if -g is passed...
 CC := $(CC:-g=)
 
 all:
        $(RUSTC) foo.rs -Z lto
        ln -s $(call STATICLIB,foo-*) $(call STATICLIB,foo)
-       $(CC) bar.c -lfoo -o $(call RUN_BINFILE,bar) $(EXTRAFLAGS) -lstdc++
+       $(CC) bar.c -lfoo -o $(call RUN_BINFILE,bar) $(EXTRACFLAGS) -lstdc++
        $(call RUN,bar)
index dedd739052ca0f5fd7d0fb0a1ce32b10ac394c1c..c9c4c455e4f805025f44b0af09b028dc1cd62663 100644 (file)
@@ -53,6 +53,20 @@ RPATH_LINK_SEARCH = -Wl,-rpath-link=$(1)
 endif
 endif
 
+# Extra flags needed to compile a working executable with the standard library
+ifdef IS_WINDOWS
+       EXTRACFLAGS :=
+else
+ifeq ($(shell uname),Darwin)
+else
+ifeq ($(shell uname),FreeBSD)
+       EXTRACFLAGS := -lm -lpthread -lgcc_s
+else
+       EXTRACFLAGS := -lm -lrt -ldl -lpthread
+endif
+endif
+endif
+
 REMOVE_DYLIBS     = rm $(TMPDIR)/$(call DYLIB_GLOB,$(1))
 REMOVE_RLIBS      = rm $(TMPDIR)/$(call RLIB_GLOB,$(1))