@@ -2,6 +2,8 @@ use std::error::Error;
22use std:: collections:: HashSet ;
33use std:: path:: { Path , PathBuf } ;
44use std:: process:: Command ;
5+ use std:: fs;
6+ use std:: io:: { Write , BufWriter } ;
57
68use glob:: glob;
79use which:: which;
@@ -53,20 +55,11 @@ fn main() -> Result<()> {
5355 return Err ( "Duplicate filenames" . into ( ) ) ;
5456 }
5557
56- let cpp_filename = souffle_generate ( & ruleset) ?;
58+ let cpp_filename = souffle_generate ( & ruleset, stem ) ?;
5759 cpp_filenames. push ( cpp_filename) ;
5860 }
5961
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) ?;
7063
7164 let mut cc = cxx_build:: bridge ( CXX_BRIDGE ) ;
7265
@@ -85,8 +78,15 @@ fn main() -> Result<()> {
8578 Ok ( ( ) )
8679}
8780
81+ fn odr_use_func_name ( stem : & str ) -> String {
82+ format ! ( "odr_use_{}_global" , stem)
83+ }
84+
8885/// 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 > {
9090 let mut cpp_filename = PathBuf :: from ( std:: env:: var ( "OUT_DIR" ) . unwrap ( ) ) ;
9191 cpp_filename. push ( datalog_filename. with_extension ( "cpp" ) . file_name ( ) . unwrap ( ) ) ;
9292
@@ -102,5 +102,42 @@ fn souffle_generate(datalog_filename: &Path) -> Result<PathBuf> {
102102 return Err ( "Invalid datalog" . into ( ) ) ;
103103 }
104104
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+
105113 Ok ( cpp_filename)
106114}
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