Skip to content

Commit ca54f19

Browse files
authored
Add --preserve-unused-functions (#291)
* 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.
1 parent 514b53b commit ca54f19

File tree

5 files changed

+38
-31
lines changed

5 files changed

+38
-31
lines changed

c2rust-transpile/src/c_ast/mod.rs

Lines changed: 31 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -519,52 +519,53 @@ impl TypedAstContext {
519519
}
520520
}
521521

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

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

531-
// Mark all the roots as used. Roots are all top-level functions and variables that might
531+
// Mark all the roots as wanted. Roots are all top-level functions and variables that might
532532
// be visible from another compilation unit.
533+
//
534+
// In addition, mark any other (unused) function wanted if configured.
533535
for &decl_id in &self.c_decls_top {
534536
let decl = self.index(decl_id);
535-
match decl.kind {
537+
let is_wanted = match decl.kind {
536538
CDeclKind::Function {
537539
body: Some(_),
538540
is_global: true,
539541
is_inline,
540542
is_inline_externally_visible,
541543
..
542-
} if !is_inline || is_inline_externally_visible => {
543544
// Depending on the C specification and dialect, an inlined function
544545
// may be externally visible. We rely on clang to determine visibility.
545-
to_walk.push(decl_id);
546-
used.insert(decl_id);
547-
}
546+
} if !is_inline || is_inline_externally_visible => true,
547+
CDeclKind::Function {
548+
body: Some(_),
549+
..
550+
} if want_unused_functions => true,
548551
CDeclKind::Variable {
549552
is_defn: true,
550553
is_externally_visible: true,
551554
..
552-
} => {
553-
to_walk.push(decl_id);
554-
used.insert(decl_id);
555-
}
555+
} => true,
556556
CDeclKind::Variable { ref attrs, .. } | CDeclKind::Function { ref attrs, .. }
557-
if attrs.contains(&Attribute::Used) =>
558-
{
559-
to_walk.push(decl_id);
560-
used.insert(decl_id);
561-
}
562-
_ => {}
557+
if attrs.contains(&Attribute::Used) => true,
558+
_ => false,
559+
};
560+
561+
if is_wanted {
562+
to_walk.push(decl_id);
563+
wanted.insert(decl_id);
563564
}
564565
}
565566

566-
// Add all referenced macros to the set of used decls
567-
// used.extend(self.macro_expansions.values().flatten());
567+
// Add all referenced macros to the set of wanted decls
568+
// wanted.extend(self.macro_expansions.values().flatten());
568569

569570
while let Some(enclosing_decl_id) = to_walk.pop() {
570571
for some_id in DFNodes::new(self, SomeId::Decl(enclosing_decl_id)) {
@@ -573,13 +574,13 @@ impl TypedAstContext {
573574
match self.c_types[&type_id].kind {
574575
// This is a reference to a previously declared type. If we look
575576
// through it we should(?) get something that looks like a declaration,
576-
// which we can mark as used.
577+
// which we can mark as wanted.
577578
CTypeKind::Elaborated(decl_type_id) => {
578579
let decl_id = self.c_types[&decl_type_id]
579580
.kind
580581
.as_decl_or_typedef()
581582
.expect("target of CTypeKind::Elaborated isn't a decl?");
582-
if used.insert(decl_id) {
583+
if wanted.insert(decl_id) {
583584
to_walk.push(decl_id);
584585
}
585586
}
@@ -594,20 +595,20 @@ impl TypedAstContext {
594595
let expr = self.index(expr_id);
595596
if let Some(macs) = self.macro_invocations.get(&expr_id) {
596597
for mac_id in macs {
597-
if used.insert(*mac_id) {
598+
if wanted.insert(*mac_id) {
598599
to_walk.push(*mac_id);
599600
}
600601
}
601602
}
602603
if let CExprKind::DeclRef(_, decl_id, _) = &expr.kind {
603-
if used.insert(*decl_id) {
604+
if wanted.insert(*decl_id) {
604605
to_walk.push(*decl_id);
605606
}
606607
}
607608
}
608609

609610
SomeId::Decl(decl_id) => {
610-
if used.insert(decl_id) {
611+
if wanted.insert(decl_id) {
611612
to_walk.push(decl_id);
612613
}
613614

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

634635
// Unset c_main if we are not retaining its declaration
635636
if let Some(main_id) = self.c_main {
636-
if !used.contains(&main_id) {
637+
if !wanted.contains(&main_id) {
637638
self.c_main = None;
638639
}
639640
}
640641

641642
// Prune any declaration that isn't considered live
642643
self.c_decls
643-
.retain(|&decl_id, _decl| used.contains(&decl_id));
644+
.retain(|&decl_id, _decl| wanted.contains(&decl_id));
644645

645646
// Prune top declarations that are not considered live
646-
self.c_decls_top.retain(|x| used.contains(x));
647+
self.c_decls_top.retain(|x| wanted.contains(x));
647648
}
648649

649650
pub fn sort_top_decls(&mut self) {

c2rust-transpile/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,7 @@ pub struct TranspilerConfig {
109109
pub translate_const_macros: bool,
110110
pub translate_fn_macros: bool,
111111
pub disable_refactoring: bool,
112+
pub preserve_unused_functions: bool,
112113
pub log_level: log::LevelFilter,
113114

114115
// Options that control build files

c2rust-transpile/src/translator/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -530,7 +530,7 @@ pub fn translate(
530530

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

535535
enum Name<'a> {
536536
VarName(&'a str),

c2rust/src/bin/c2rust-transpile.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@ fn main() {
8989
translate_const_macros: matches.is_present("translate-const-macros"),
9090
translate_fn_macros: matches.is_present("translate-fn-macros"),
9191
disable_refactoring: matches.is_present("disable-refactoring"),
92+
preserve_unused_functions: matches.is_present("preserve-unused-functions"),
9293

9394
use_c_loop_info: !matches.is_present("ignore-c-loop-info"),
9495
use_c_multiple_info: !matches.is_present("ignore-c-multiple-info"),

c2rust/src/transpile.yaml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,10 @@ args:
172172
long: disable-refactoring
173173
help: Disable running refactoring tool after translation
174174
takes_value: false
175+
- preserve-unused-functions:
176+
long: preserve-unused-functions
177+
help: Include static and inline functions in translation
178+
takes_value: false
175179
- log-level:
176180
long: log-level
177181
help: Logging level

0 commit comments

Comments
 (0)