Skip to content

Commit

Permalink
Add --preserve-unused-functions (#291)
Browse files Browse the repository at this point in the history
* Add --preserve-unused-functions

to keep otherwise pruned static / inline functions.

Closes: #287

* transpile: Reduce code duplication

* prune_unused_decls: Rename to unwanted

The criterion for whether it is desired for an item to be transpiled
have been broadened to possibly preserve unused functions as well. Thus,
the "unused" name part has been replaced with "unwanted", which
traditionally means unused but, in other configurations, can mean
"unused but not a function" now as well.
  • Loading branch information
chrysn authored Sep 15, 2020
1 parent 514b53b commit ca54f19
Show file tree
Hide file tree
Showing 5 changed files with 38 additions and 31 deletions.
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

0 comments on commit ca54f19

Please sign in to comment.