Skip to content

Commit f8a1e87

Browse files
Clarify rules for C++ global initialization
1 parent 0d7f0cf commit f8a1e87

File tree

4 files changed

+52
-27
lines changed

4 files changed

+52
-27
lines changed

Cargo.toml

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,4 @@ log = "0.4"
2121
petgraph = "0.4.13"
2222
pico-args = "0.2"
2323

24-
[build-dependencies]
25-
glob = "0.3.0"
26-
2724
[workspace]

build.rs

Lines changed: 0 additions & 12 deletions
This file was deleted.

polonius-souffle/build.rs

Lines changed: 48 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -56,20 +56,11 @@ fn main() -> Result<()> {
5656
return Err("Duplicate filenames".into());
5757
}
5858

59-
let cpp_filename = souffle_generate(&ruleset)?;
59+
let cpp_filename = souffle_generate(&ruleset, stem)?;
6060
cpp_filenames.push(cpp_filename);
6161
}
6262

63-
for stem in known_stems {
64-
// HACK: Souffle adds datalog programs to the registry in the initializer of a global
65-
// variable (whose name begins with `__factory_Sf`). Since that global variable is never used
66-
// by the Rust program, it is occasionally removed by the linker, its initializer is never
67-
// run (!!!), and the program is never registered.
68-
//
69-
// `-u` marks the symbol as undefined, so that it will not be optimized out.
70-
let prog_symbol = format!("__factory_Sf_{}_instance", stem);
71-
println!("cargo:rustc-link-arg=-u{}", prog_symbol);
72-
}
63+
odr_use_generate(&known_stems)?;
7364

7465
let mut cc = cxx_build::bridge(CXX_BRIDGE);
7566

@@ -88,8 +79,15 @@ fn main() -> Result<()> {
8879
Ok(())
8980
}
9081

82+
fn odr_use_func_name(stem: &str) -> String {
83+
format!("odr_use_{}_global", stem)
84+
}
85+
9186
/// Uses Souffle to generate a C++ file for evaluating the given datalog program.
92-
fn souffle_generate(datalog_filename: &Path) -> Result<PathBuf> {
87+
///
88+
/// Returns the filename for the generated C code, as well as the name of a generated function that
89+
/// will trigger the global initializers in that translation unit.
90+
fn souffle_generate(datalog_filename: &Path, stem: &str) -> Result<PathBuf> {
9391
let mut cpp_filename = PathBuf::from(std::env::var("OUT_DIR").unwrap());
9492
cpp_filename.push(datalog_filename.with_extension("cpp").file_name().unwrap());
9593

@@ -105,5 +103,43 @@ fn souffle_generate(datalog_filename: &Path) -> Result<PathBuf> {
105103
return Err("Invalid datalog".into());
106104
}
107105

106+
let mut generated_cpp = fs::OpenOptions::new().append(true).open(&cpp_filename)?;
107+
writeln!(
108+
generated_cpp,
109+
r#"
110+
extern "C"
111+
void {}() {{}}"#,
112+
odr_use_func_name(stem)
113+
)?;
114+
108115
Ok(cpp_filename)
109116
}
117+
118+
// HACK: Souffle adds datalog programs to the registry in the initializer of a global
119+
// variable (whose name begins with `__factory_Sf`). That global variable is eligible for
120+
// deferred initialization, so we need to force its initializer to run before we do a lookup in
121+
// the registry (which happens in a different translation unit from the generated code).
122+
//
123+
// We accomplish this by defining a single, no-op function in each generated C++ file, and calling
124+
// it on the Rust side before doing any meaningful work. By the C++ standard, this forces global
125+
// initializers for anything in the that translation unit to run, since calling the function is an
126+
// ODR-use of something in the same translation unit. We also define a helper function,
127+
// `odr_use_all`, which calls the no-op function in every known module.
128+
fn odr_use_generate(known_stems: &HashSet<String>) -> Result<()> {
129+
let mut odr_use_filename = PathBuf::from(std::env::var("OUT_DIR").unwrap());
130+
odr_use_filename.push("odr_use.rs");
131+
132+
let mut odr_use = BufWriter::new(fs::File::create(odr_use_filename)?);
133+
writeln!(odr_use, r#"extern "C" {{"#)?;
134+
for stem in known_stems {
135+
writeln!(odr_use, "fn {}();", odr_use_func_name(stem))?;
136+
}
137+
writeln!(odr_use, r#"}}"#)?;
138+
139+
writeln!(odr_use, "fn odr_use_all() {{")?;
140+
for stem in known_stems {
141+
writeln!(odr_use, "unsafe {{ {}(); }}", odr_use_func_name(stem))?;
142+
}
143+
writeln!(odr_use, "}}")?;
144+
Ok(())
145+
}

polonius-souffle/src/ffi.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,8 +100,12 @@ impl Relation {
100100

101101
// Rust wrappers
102102

103+
// See `build.rs` for background.
104+
include!(concat!(env!("OUT_DIR"), "/odr_use.rs"));
105+
103106
impl Program {
104107
pub fn new(name: &str) -> cxx::UniquePtr<Self> {
108+
odr_use_all();
105109
let_cxx_string!(name = name);
106110
ffi::ProgramFactory_newInstance(&*name)
107111
}

0 commit comments

Comments
 (0)