rusteron_code_gen/
c_build.rs1#[derive(Debug, Clone, Copy, PartialEq, Eq)]
8pub enum CpuTarget {
9 ThisCpu,
11 Baseline,
14}
15
16pub fn release_c_flags(arch: &str, vendor: &str, target: CpuTarget, march: Option<&str>) -> String {
20 let mut flags = String::from("-O3 -DNDEBUG -funroll-loops");
21 match (march.filter(|m| !m.is_empty()), target) {
22 (Some(march), _) => flags.push_str(&format!(" -march={march}")),
23 (None, CpuTarget::ThisCpu) => flags.push_str(" -march=native"),
24 (None, CpuTarget::Baseline) => match arch {
25 "x86_64" => flags.push_str(" -march=x86-64 -mtune=generic"),
26 "aarch64" if vendor != "apple" => flags.push_str(" -march=armv8-a"),
27 _ => {}
29 },
30 }
31 flags
32}
33
34pub fn wants_linker_plugin_lto(encoded_rustflags: &str) -> bool {
38 codegen_options(encoded_rustflags)
39 .any(|o| o == "linker-plugin-lto" || o.starts_with("linker-plugin-lto=") && o != "linker-plugin-lto=no")
40}
41
42fn codegen_options(encoded_rustflags: &str) -> impl Iterator<Item = &str> {
44 let flags: Vec<&str> = encoded_rustflags.split('\x1f').collect();
45 (0..flags.len()).filter_map(move |i| match flags[i].strip_prefix("-C") {
46 Some("") => flags.get(i + 1).copied(),
47 Some(option) => Some(option),
48 None => None,
49 })
50}
51
52pub fn lto_release_c_flags(arch: &str, rustc_cpu: Option<&str>) -> String {
56 let mut flags = String::from("-O3 -DNDEBUG -funroll-loops");
57 match rustc_cpu {
58 Some(cpu) if arch == "aarch64" => flags.push_str(&format!(" -mcpu={cpu}")),
60 Some(cpu) => flags.push_str(&format!(" -march={cpu}")),
61 None => {}
62 }
63 if arch == "x86_64" {
64 flags.push_str(" -mno-sgx");
67 }
68 flags
69}
70
71pub fn rustc_target_cpu(encoded_rustflags: &str) -> Option<&str> {
73 codegen_options(encoded_rustflags)
74 .filter_map(|o| o.strip_prefix("target-cpu="))
75 .last()
76}
77
78pub fn llvm_major(version_output: &str) -> Option<u32> {
81 version_output.lines().find_map(|line| {
82 let rest = line
83 .strip_prefix("LLVM version: ")
84 .or_else(|| line.split_once("clang version ").map(|(_, v)| v))?;
85 rest.split('.').next()?.trim().parse().ok()
86 })
87}
88
89pub fn c_lto_flags(kind: Option<&str>, target: &str) -> Result<String, String> {
96 let lto = match kind.filter(|k| !k.is_empty()) {
97 None | Some("full") => "-flto",
98 Some("thin") => "-flto=thin",
99 Some(other) => return Err(format!("RUSTERON_C_LTO={other} is not one of `full` or `thin`")),
100 };
101 Ok(format!("{lto} --target={target}"))
102}
103
104#[cfg(test)]
105mod tests {
106 use super::{
107 CpuTarget, c_lto_flags, llvm_major, lto_release_c_flags, release_c_flags, rustc_target_cpu,
108 wants_linker_plugin_lto,
109 };
110
111 #[test]
112 fn lto_c_follows_rustc_cpu() {
113 assert_eq!(
114 lto_release_c_flags("aarch64", Some("native")),
115 "-O3 -DNDEBUG -funroll-loops -mcpu=native"
116 );
117 assert_eq!(
118 lto_release_c_flags("x86_64", Some("emeraldrapids")),
119 "-O3 -DNDEBUG -funroll-loops -march=emeraldrapids -mno-sgx"
120 );
121 assert_eq!(
122 lto_release_c_flags("x86_64", None),
123 "-O3 -DNDEBUG -funroll-loops -mno-sgx"
124 );
125 assert_eq!(
126 rustc_target_cpu("-Ctarget-cpu=native\x1f-Clinker-plugin-lto"),
127 Some("native")
128 );
129 assert_eq!(rustc_target_cpu("-C\x1ftarget-cpu=a\x1f-Ctarget-cpu=b"), Some("b"));
130 assert_eq!(rustc_target_cpu("-Clinker-plugin-lto"), None);
131 }
132
133 #[test]
134 fn detects_linker_plugin_lto() {
135 assert!(wants_linker_plugin_lto("-Ctarget-cpu=native\x1f-Clinker-plugin-lto"));
136 assert!(wants_linker_plugin_lto("-C\x1flinker-plugin-lto"));
137 assert!(wants_linker_plugin_lto("-Clinker-plugin-lto=yes"));
138 assert!(!wants_linker_plugin_lto("-Clinker-plugin-lto=no"));
139 assert!(!wants_linker_plugin_lto("-Ctarget-cpu=native"));
140 assert!(!wants_linker_plugin_lto(""));
141 }
142
143 #[test]
144 fn parses_llvm_major() {
145 assert_eq!(llvm_major("rustc 1.95.0\nhost: x\nLLVM version: 22.1.2\n"), Some(22));
146 assert_eq!(llvm_major("Homebrew clang version 22.1.8\nTarget: arm64"), Some(22));
147 assert_eq!(llvm_major("Ubuntu clang version 18.1.3 (1ubuntu1)\n"), Some(18));
148 assert_eq!(llvm_major("Apple clang version 21.0.0 (clang-2100.3.34.2)"), Some(21));
149 assert_eq!(llvm_major("gcc (GCC) 13.2.0"), None);
150 }
151
152 #[test]
153 fn lto_flags_carry_kind_and_rustc_target() {
154 let target = "x86_64-unknown-linux-gnu";
155 assert_eq!(
156 c_lto_flags(None, target),
157 Ok("-flto --target=x86_64-unknown-linux-gnu".into())
158 );
159 assert_eq!(
160 c_lto_flags(Some("thin"), target),
161 Ok("-flto=thin --target=x86_64-unknown-linux-gnu".into())
162 );
163 assert!(c_lto_flags(Some("fat"), target).is_err());
164 }
165
166 #[test]
167 fn from_source_targets_this_cpu() {
168 assert_eq!(
169 release_c_flags("x86_64", "unknown", CpuTarget::ThisCpu, None),
170 "-O3 -DNDEBUG -funroll-loops -march=native"
171 );
172 }
173
174 #[test]
175 fn published_libraries_target_the_architecture_baseline() {
176 assert_eq!(
177 release_c_flags("x86_64", "unknown", CpuTarget::Baseline, None),
178 "-O3 -DNDEBUG -funroll-loops -march=x86-64 -mtune=generic"
179 );
180 assert_eq!(
181 release_c_flags("aarch64", "unknown", CpuTarget::Baseline, None),
182 "-O3 -DNDEBUG -funroll-loops -march=armv8-a"
183 );
184 assert_eq!(
185 release_c_flags("aarch64", "apple", CpuTarget::Baseline, None),
186 "-O3 -DNDEBUG -funroll-loops"
187 );
188 }
189
190 #[test]
191 fn march_override_wins() {
192 assert_eq!(
193 release_c_flags("x86_64", "unknown", CpuTarget::ThisCpu, Some("x86-64-v3")),
194 "-O3 -DNDEBUG -funroll-loops -march=x86-64-v3"
195 );
196 assert_eq!(
197 release_c_flags("x86_64", "unknown", CpuTarget::Baseline, Some("x86-64-v2")),
198 "-O3 -DNDEBUG -funroll-loops -march=x86-64-v2"
199 );
200 assert_eq!(
201 release_c_flags("x86_64", "unknown", CpuTarget::ThisCpu, Some("")),
202 "-O3 -DNDEBUG -funroll-loops -march=native"
203 );
204 }
205}