Skip to content

Commit a629c57

Browse files
Clarify rules for C++ global initialization
1 parent b9248bf commit a629c57

File tree

4 files changed

+53
-27
lines changed

4 files changed

+53
-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: 49 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ use std::error::Error;
22
use std::collections::HashSet;
33
use std::path::{Path, PathBuf};
44
use std::process::Command;
5+
use std::fs;
6+
use std::io::{Write, BufWriter};
57

68
use glob::glob;
79
use 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+
}

polonius-souffle/src/ffi.rs

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

9696
// Rust wrappers
9797

98+
// See `build.rs` for background.
99+
include!(concat!(env!("OUT_DIR"), "/odr_use.rs"));
100+
98101
impl Program {
99102
pub fn new(name: &str) -> cxx::UniquePtr<Self> {
103+
odr_use_all();
100104
let_cxx_string!(name = name);
101105
ffi::ProgramFactory_newInstance(&*name)
102106
}

0 commit comments

Comments
 (0)