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

Add --preserve-unused-functions #291

Merged
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
61 changes: 31 additions & 30 deletions c2rust-transpile/src/c_ast/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -519,52 +519,53 @@ impl TypedAstContext {
}
}

pub fn prune_unused_decls(&mut self) {
pub fn prune_unwanted_decls(&mut self, want_unused_functions: bool) {
// Starting from a set of root declarations, walk each one to find declarations it
// depends on. Then walk each of those, recursively.

// Declarations we still need to walk. Everything in here is also in `used`.
// Declarations we still need to walk. Everything in here is also in `wanted`.
let mut to_walk: Vec<CDeclId> = Vec::new();
// Declarations accessible from a root.
let mut used: HashSet<CDeclId> = HashSet::new();
let mut wanted: HashSet<CDeclId> = HashSet::new();

// Mark all the roots as used. Roots are all top-level functions and variables that might
// Mark all the roots as wanted. Roots are all top-level functions and variables that might
// be visible from another compilation unit.
//
// In addition, mark any other (unused) function wanted if configured.
for &decl_id in &self.c_decls_top {
let decl = self.index(decl_id);
match decl.kind {
let is_wanted = match decl.kind {
CDeclKind::Function {
body: Some(_),
is_global: true,
is_inline,
is_inline_externally_visible,
..
} if !is_inline || is_inline_externally_visible => {
// Depending on the C specification and dialect, an inlined function
// may be externally visible. We rely on clang to determine visibility.
to_walk.push(decl_id);
used.insert(decl_id);
}
} if !is_inline || is_inline_externally_visible => true,
CDeclKind::Function {
body: Some(_),
..
} if want_unused_functions => true,
CDeclKind::Variable {
is_defn: true,
is_externally_visible: true,
..
} => {
to_walk.push(decl_id);
used.insert(decl_id);
}
} => true,
CDeclKind::Variable { ref attrs, .. } | CDeclKind::Function { ref attrs, .. }
if attrs.contains(&Attribute::Used) =>
{
to_walk.push(decl_id);
used.insert(decl_id);
}
_ => {}
if attrs.contains(&Attribute::Used) => true,
_ => false,
};

if is_wanted {
to_walk.push(decl_id);
wanted.insert(decl_id);
}
}

// Add all referenced macros to the set of used decls
// used.extend(self.macro_expansions.values().flatten());
// Add all referenced macros to the set of wanted decls
// wanted.extend(self.macro_expansions.values().flatten());

while let Some(enclosing_decl_id) = to_walk.pop() {
for some_id in DFNodes::new(self, SomeId::Decl(enclosing_decl_id)) {
Expand All @@ -573,13 +574,13 @@ impl TypedAstContext {
match self.c_types[&type_id].kind {
// This is a reference to a previously declared type. If we look
// through it we should(?) get something that looks like a declaration,
// which we can mark as used.
// which we can mark as wanted.
CTypeKind::Elaborated(decl_type_id) => {
let decl_id = self.c_types[&decl_type_id]
.kind
.as_decl_or_typedef()
.expect("target of CTypeKind::Elaborated isn't a decl?");
if used.insert(decl_id) {
if wanted.insert(decl_id) {
to_walk.push(decl_id);
}
}
Expand All @@ -594,20 +595,20 @@ impl TypedAstContext {
let expr = self.index(expr_id);
if let Some(macs) = self.macro_invocations.get(&expr_id) {
for mac_id in macs {
if used.insert(*mac_id) {
if wanted.insert(*mac_id) {
to_walk.push(*mac_id);
}
}
}
if let CExprKind::DeclRef(_, decl_id, _) = &expr.kind {
if used.insert(*decl_id) {
if wanted.insert(*decl_id) {
to_walk.push(*decl_id);
}
}
}

SomeId::Decl(decl_id) => {
if used.insert(decl_id) {
if wanted.insert(decl_id) {
to_walk.push(decl_id);
}

Expand All @@ -616,7 +617,7 @@ impl TypedAstContext {
// Special case for enums. The enum constant is used, so the whole
// enum is also used.
let parent_id = self.parents[&decl_id];
if used.insert(parent_id) {
if wanted.insert(parent_id) {
to_walk.push(parent_id);
}
}
Expand All @@ -633,17 +634,17 @@ impl TypedAstContext {

// Unset c_main if we are not retaining its declaration
if let Some(main_id) = self.c_main {
if !used.contains(&main_id) {
if !wanted.contains(&main_id) {
self.c_main = None;
}
}

// Prune any declaration that isn't considered live
self.c_decls
.retain(|&decl_id, _decl| used.contains(&decl_id));
.retain(|&decl_id, _decl| wanted.contains(&decl_id));

// Prune top declarations that are not considered live
self.c_decls_top.retain(|x| used.contains(x));
self.c_decls_top.retain(|x| wanted.contains(x));
}

pub fn sort_top_decls(&mut self) {
Expand Down
1 change: 1 addition & 0 deletions c2rust-transpile/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ pub struct TranspilerConfig {
pub translate_const_macros: bool,
pub translate_fn_macros: bool,
pub disable_refactoring: bool,
pub preserve_unused_functions: bool,
pub log_level: log::LevelFilter,

// Options that control build files
Expand Down
2 changes: 1 addition & 1 deletion c2rust-transpile/src/translator/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -530,7 +530,7 @@ pub fn translate(

// Headers often pull in declarations that are unused;
// we simplify the translator output by omitting those.
t.ast_context.prune_unused_decls();
t.ast_context.prune_unwanted_decls(tcfg.preserve_unused_functions);

enum Name<'a> {
VarName(&'a str),
Expand Down
1 change: 1 addition & 0 deletions c2rust/src/bin/c2rust-transpile.rs
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ fn main() {
translate_const_macros: matches.is_present("translate-const-macros"),
translate_fn_macros: matches.is_present("translate-fn-macros"),
disable_refactoring: matches.is_present("disable-refactoring"),
preserve_unused_functions: matches.is_present("preserve-unused-functions"),

use_c_loop_info: !matches.is_present("ignore-c-loop-info"),
use_c_multiple_info: !matches.is_present("ignore-c-multiple-info"),
Expand Down
4 changes: 4 additions & 0 deletions c2rust/src/transpile.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,10 @@ args:
long: disable-refactoring
help: Disable running refactoring tool after translation
takes_value: false
- preserve-unused-functions:
long: preserve-unused-functions
help: Include static and inline functions in translation
takes_value: false
- log-level:
long: log-level
help: Logging level
Expand Down