Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Reduce the aggressiveness of reachability #10217

Merged
merged 1 commit into from
Nov 2, 2013
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
118 changes: 68 additions & 50 deletions src/librustc/middle/reachable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ fn item_might_be_inlined(item: @ast::item) -> bool {
}

match item.node {
ast::item_impl(ref generics, _, _, _) |
ast::item_fn(_, _, _, ref generics, _) => {
generics_require_inlining(generics)
}
Expand All @@ -64,6 +65,25 @@ fn ty_method_might_be_inlined(ty_method: &ast::TypeMethod) -> bool {
generics_require_inlining(&ty_method.generics)
}

fn method_might_be_inlined(tcx: ty::ctxt, method: &ast::method,
impl_src: ast::DefId) -> bool {
if attributes_specify_inlining(method.attrs) ||
generics_require_inlining(&method.generics) {
return true
}
if is_local(impl_src) {
match tcx.items.find(&impl_src.node) {
Some(&ast_map::node_item(item, _)) => item_might_be_inlined(item),
Some(*) | None => {
tcx.sess.span_bug(method.span, "impl did is not an item")
}
}
} else {
tcx.sess.span_bug(method.span, "found a foreign impl as a parent of a \
local method")
}
}

// Returns true if the given trait method must be inlined because it may be
// monomorphized or it was marked with `#[inline]`.
fn trait_method_might_be_inlined(trait_method: &ast::trait_method) -> bool {
Expand Down Expand Up @@ -100,50 +120,54 @@ impl Visitor<()> for MarkSymbolVisitor {

fn visit_expr(&mut self, expr:@ast::Expr, _:()) {

match expr.node {
ast::ExprPath(_) => {
let def = match self.tcx.def_map.find(&expr.id) {
Some(&def) => def,
None => {
self.tcx.sess.span_bug(expr.span,
"def ID not in def map?!")
}
};
match expr.node {
ast::ExprPath(_) => {
let def = match self.tcx.def_map.find(&expr.id) {
Some(&def) => def,
None => {
self.tcx.sess.span_bug(expr.span,
"def ID not in def map?!")
}
};

let def_id = def_id_of_def(def);
let def_id = def_id_of_def(def);
if ReachableContext::
def_id_represents_local_inlined_item(self.tcx, def_id) {
self.worklist.push(def_id.node)
}
self.reachable_symbols.insert(def_id.node);
}
ast::ExprMethodCall(*) => {
match self.method_map.find(&expr.id) {
Some(&typeck::method_map_entry {
origin: typeck::method_static(def_id),
_
}) => {
if ReachableContext::
def_id_represents_local_inlined_item(self.tcx,
def_id) {
self.worklist.push(def_id.node)
}
def_id_represents_local_inlined_item(
self.tcx,
def_id) {
self.worklist.push(def_id.node)
}
self.reachable_symbols.insert(def_id.node);
}
ast::ExprMethodCall(*) => {
match self.method_map.find(&expr.id) {
Some(&typeck::method_map_entry {
origin: typeck::method_static(def_id),
_
}) => {
if ReachableContext::
def_id_represents_local_inlined_item(
self.tcx,
def_id) {
self.worklist.push(def_id.node)
}
self.reachable_symbols.insert(def_id.node);
}
Some(_) => {}
None => {
self.tcx.sess.span_bug(expr.span,
"method call expression \
Some(_) => {}
None => {
self.tcx.sess.span_bug(expr.span,
"method call expression \
not in method map?!")
}
}
}
_ => {}
}
}
_ => {}
}

visit::walk_expr(self, expr, ())
visit::walk_expr(self, expr, ())
}

fn visit_item(&mut self, _item: @ast::item, _: ()) {
// Do not recurse into items. These items will be added to the worklist
// and recursed into manually if necessary.
}
}

Expand Down Expand Up @@ -263,8 +287,11 @@ impl ReachableContext {
Some(&ast_map::node_item(item, _)) => {
match item.node {
ast::item_fn(_, _, _, _, ref search_block) => {
visit::walk_block(&mut visitor, search_block, ())
if item_might_be_inlined(item) {
visit::walk_block(&mut visitor, search_block, ())
}
}

// Our recursion into modules involves looking up their
// public reexports and the destinations of those
// exports. Privacy will put them in the worklist, but
Expand Down Expand Up @@ -331,8 +358,10 @@ impl ReachableContext {
}
}
}
Some(&ast_map::node_method(ref method, _, _)) => {
visit::walk_block(&mut visitor, &method.body, ())
Some(&ast_map::node_method(method, did, _)) => {
if method_might_be_inlined(self.tcx, method, did) {
visit::walk_block(&mut visitor, &method.body, ())
}
}
// Nothing to recurse on for these
Some(&ast_map::node_foreign_item(*)) |
Expand Down Expand Up @@ -378,17 +407,6 @@ pub fn find_reachable(tcx: ty::ctxt,
exp_map2: resolve::ExportMap2,
exported_items: &privacy::ExportedItems)
-> @mut HashSet<ast::NodeId> {
// XXX(pcwalton): We only need to mark symbols that are exported. But this
// is more complicated than just looking at whether the symbol is `pub`,
// because it might be the target of a `pub use` somewhere. For now, I
// think we are fine, because you can't `pub use` something that wasn't
// exported due to the bug whereby `use` only looks through public
// modules even if you're inside the module the `use` appears in. When
// this bug is fixed, however, this code will need to be updated. Probably
// the easiest way to fix this (although a conservative overapproximation)
// is to have the name resolution pass mark all targets of a `pub use` as
// "must be reachable".

let reachable_context = ReachableContext::new(tcx, method_map, exp_map2);

// Step 1: Seed the worklist with all nodes which were found to be public as
Expand Down
10 changes: 6 additions & 4 deletions src/librustc/middle/trans/base.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2469,7 +2469,7 @@ pub fn get_item_val(ccx: @mut CrateContext, id: ast::NodeId) -> ValueRef {
match val {
Some(v) => v,
None => {
let mut exprt = false;
let mut foreign = false;
let item = ccx.tcx.items.get_copy(&id);
let val = match item {
ast_map::node_item(i, pth) => {
Expand Down Expand Up @@ -2582,7 +2582,6 @@ pub fn get_item_val(ccx: @mut CrateContext, id: ast::NodeId) -> ValueRef {
get_item_val()");
}
ast::provided(m) => {
exprt = true;
register_method(ccx, id, pth, m)
}
}
Expand All @@ -2594,7 +2593,7 @@ pub fn get_item_val(ccx: @mut CrateContext, id: ast::NodeId) -> ValueRef {

ast_map::node_foreign_item(ni, abis, _, pth) => {
let ty = ty::node_id_to_type(ccx.tcx, ni.id);
exprt = true;
foreign = true;

match ni.node {
ast::foreign_item_fn(*) => {
Expand Down Expand Up @@ -2688,7 +2687,10 @@ pub fn get_item_val(ccx: @mut CrateContext, id: ast::NodeId) -> ValueRef {
}
};

if !exprt && !ccx.reachable.contains(&id) {
// foreign items (extern fns and extern statics) don't have internal
// linkage b/c that doesn't quite make sense. Otherwise items can
// have internal linkage if they're not reachable.
if !foreign && !ccx.reachable.contains(&id) {
lib::llvm::SetLinkage(val, lib::llvm::InternalLinkage);
}

Expand Down
36 changes: 36 additions & 0 deletions src/test/auxiliary/linkage-visibility.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// Copyright 2013 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.

use std::unstable::dynamic_lib::DynamicLibrary;

#[no_mangle]
pub fn foo() { bar(); }

pub fn foo2<T>() {
fn bar2() {
bar();
}
bar2();
}

#[no_mangle]
fn bar() { }

#[no_mangle]
fn baz() { }

pub fn test() {
let lib = DynamicLibrary::open(None).unwrap();
unsafe {
assert!(lib.symbol::<int>("foo").is_ok());
assert!(lib.symbol::<int>("baz").is_err());
assert!(lib.symbol::<int>("bar").is_err());
}
}
20 changes: 20 additions & 0 deletions src/test/run-pass/linkage-visibility.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// Copyright 2013 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.

// aux-build:linkage-visibility.rs
// xfail-fast windows doesn't like aux-build

extern mod foo(name = "linkage-visibility");

fn main() {
foo::test();
foo::foo2::<int>();
foo::foo();
}