@@ -2,6 +2,8 @@ use std::error::Error;
2
2
use std:: collections:: HashSet ;
3
3
use std:: path:: { Path , PathBuf } ;
4
4
use std:: process:: Command ;
5
+ use std:: fs;
6
+ use std:: io:: { Write , BufWriter } ;
5
7
6
8
use glob:: glob;
7
9
use which:: which;
@@ -53,20 +55,11 @@ fn main() -> Result<()> {
53
55
return Err ( "Duplicate filenames" . into ( ) ) ;
54
56
}
55
57
56
- let cpp_filename = souffle_generate ( & ruleset) ?;
58
+ let cpp_filename = souffle_generate ( & ruleset, stem ) ?;
57
59
cpp_filenames. push ( cpp_filename) ;
58
60
}
59
61
60
- for stem in known_stems {
61
- // HACK: Souffle adds datalog programs to the registry in the initializer of a global
62
- // variable (whose name begins with `__factory_Sf`). Since that global variable is never used
63
- // by the Rust program, it is occasionally removed by the linker, its initializer is never
64
- // run (!!!), and the program is never registered.
65
- //
66
- // `-u` marks the symbol as undefined, so that it will not be optimized out.
67
- let prog_symbol = format ! ( "__factory_Sf_{}_instance" , stem) ;
68
- println ! ( "cargo:rustc-link-arg=-u{}" , prog_symbol) ;
69
- }
62
+ odr_use_generate ( & known_stems) ?;
70
63
71
64
let mut cc = cxx_build:: bridge ( CXX_BRIDGE ) ;
72
65
@@ -85,8 +78,15 @@ fn main() -> Result<()> {
85
78
Ok ( ( ) )
86
79
}
87
80
81
+ fn odr_use_func_name ( stem : & str ) -> String {
82
+ format ! ( "odr_use_{}_global" , stem)
83
+ }
84
+
88
85
/// Uses Souffle to generate a C++ file for evaluating the given datalog program.
89
- fn souffle_generate ( datalog_filename : & Path ) -> Result < PathBuf > {
86
+ ///
87
+ /// Returns the filename for the generated C code, as well as the name of a generated function that
88
+ /// will trigger the global initializers in that translation unit.
89
+ fn souffle_generate ( datalog_filename : & Path , stem : & str ) -> Result < PathBuf > {
90
90
let mut cpp_filename = PathBuf :: from ( std:: env:: var ( "OUT_DIR" ) . unwrap ( ) ) ;
91
91
cpp_filename. push ( datalog_filename. with_extension ( "cpp" ) . file_name ( ) . unwrap ( ) ) ;
92
92
@@ -102,5 +102,42 @@ fn souffle_generate(datalog_filename: &Path) -> Result<PathBuf> {
102
102
return Err ( "Invalid datalog" . into ( ) ) ;
103
103
}
104
104
105
+ let mut generated_cpp = fs:: OpenOptions :: new ( ) . append ( true ) . open ( & cpp_filename) ?;
106
+ writeln ! (
107
+ generated_cpp,
108
+ r#"
109
+ extern "C"
110
+ void {}() {{}}"# ,
111
+ odr_use_func_name( stem) ) ?;
112
+
105
113
Ok ( cpp_filename)
106
114
}
115
+
116
+ // HACK: Souffle adds datalog programs to the registry in the initializer of a global
117
+ // variable (whose name begins with `__factory_Sf`). That global variable is eligible for
118
+ // deferred initialization, so we need to force its initializer to run before we do a lookup in
119
+ // the registry (which happens in a different translation unit from the generated code).
120
+ //
121
+ // We accomplish this by defining a single, no-op function in each generated C++ file, and calling
122
+ // it on the Rust side before doing any meaningful work. By the C++ standard, this forces global
123
+ // initializers for anything in the that translation unit to run, since calling the function is an
124
+ // ODR-use of something in the same translation unit. We also define a helper function,
125
+ // `odr_use_all`, which calls the no-op function in every known module.
126
+ fn odr_use_generate ( known_stems : & HashSet < String > ) -> Result < ( ) > {
127
+ let mut odr_use_filename = PathBuf :: from ( std:: env:: var ( "OUT_DIR" ) . unwrap ( ) ) ;
128
+ odr_use_filename. push ( "odr_use.rs" ) ;
129
+
130
+ let mut odr_use = BufWriter :: new ( fs:: File :: create ( odr_use_filename) ?) ;
131
+ writeln ! ( odr_use, r#"extern "C" {{"# ) ?;
132
+ for stem in known_stems {
133
+ writeln ! ( odr_use, "fn {}();" , odr_use_func_name( stem) ) ?;
134
+ }
135
+ writeln ! ( odr_use, r#"}}"# ) ?;
136
+
137
+ writeln ! ( odr_use, "fn odr_use_all() {{" ) ?;
138
+ for stem in known_stems {
139
+ writeln ! ( odr_use, "unsafe {{ {}(); }}" , odr_use_func_name( stem) ) ?;
140
+ }
141
+ writeln ! ( odr_use, "}}" ) ?;
142
+ Ok ( ( ) )
143
+ }
0 commit comments