1use std::env;
7
8#[derive(Debug, Clone, Copy, PartialEq, Eq)]
10pub enum CpuArch {
11 X86_64,
12 Aarch64,
13 Arm,
14 Other,
15}
16
17#[derive(Debug, Clone, Copy, PartialEq, Eq)]
19pub enum X86SimdLevel {
20 Baseline,
22 SSE4_2,
24 AVX,
26 AVX2,
28 AVX512,
30}
31
32#[derive(Debug, Clone, Copy, PartialEq, Eq)]
34pub enum ArmSimdLevel {
35 None,
37 NEON,
39 SVE,
41}
42
43#[derive(Debug, Clone)]
45pub struct SimdConfig {
46 pub arch: CpuArch,
48 pub zig_cpu_flag: String,
50 pub description: String,
52 pub use_native: bool,
54}
55
56impl SimdConfig {
57 pub fn detect() -> Self {
59 let target = env::var("TARGET").unwrap_or_else(|_| "unknown".to_string());
60 let rust_flags = env::var("RUSTFLAGS").unwrap_or_default();
61 let target_features = env::var("CARGO_CFG_TARGET_FEATURE").unwrap_or_default();
62
63 let use_native = rust_flags.contains("target-cpu=native")
65 || rust_flags.contains("-C native")
66 || rust_flags.contains("--target-cpu=native");
67
68 let arch = Self::detect_arch(&target);
69
70 let (zig_cpu_flag, description) = if use_native {
71 ("-mcpu=native".to_string(), format!("{:?} with native CPU features", arch))
73 } else {
74 Self::detect_features(&target, &target_features, arch)
76 };
77
78 SimdConfig {
79 arch,
80 zig_cpu_flag,
81 description,
82 use_native,
83 }
84 }
85
86 fn detect_arch(target: &str) -> CpuArch {
88 if target.contains("x86_64") {
89 CpuArch::X86_64
90 } else if target.contains("aarch64") {
91 CpuArch::Aarch64
92 } else if target.contains("arm") {
93 CpuArch::Arm
94 } else {
95 CpuArch::Other
96 }
97 }
98
99 fn detect_features(target: &str, features: &str, arch: CpuArch) -> (String, String) {
101 match arch {
102 CpuArch::X86_64 => Self::detect_x86_features(target, features),
103 CpuArch::Aarch64 | CpuArch::Arm => Self::detect_arm_features(features),
104 CpuArch::Other => {
105 ("-mcpu=baseline".to_string(), "baseline (unknown architecture)".to_string())
106 },
107 }
108 }
109
110 fn detect_x86_features(target: &str, features: &str) -> (String, String) {
112 if features.contains("avx512") {
114 return ("-mcpu=x86_64_v4".to_string(), "x86_64 v4 (AVX-512)".to_string());
115 }
116
117 if features.contains("avx2") {
119 return ("-mcpu=x86_64_v3".to_string(), "x86_64 v3 (AVX2, FMA)".to_string());
120 }
121
122 if features.contains("avx") {
124 return ("-mcpu=x86_64+avx".to_string(), "x86_64 with AVX".to_string());
125 }
126
127 if features.contains("sse4.2") || features.contains("sse4_2") {
129 return ("-mcpu=x86_64+sse4.2".to_string(), "x86_64 with SSE4.2".to_string());
130 }
131
132 if target.contains("x86_64") {
135 ("-mcpu=x86_64".to_string(), "x86_64 baseline (SSE2)".to_string())
136 } else {
137 ("-mcpu=baseline".to_string(), "baseline".to_string())
138 }
139 }
140
141 fn detect_arm_features(features: &str) -> (String, String) {
143 if features.contains("sve") {
145 return ("-mcpu=generic+sve".to_string(), "ARM with SVE".to_string());
146 }
147
148 if features.contains("neon") {
150 return ("-mcpu=generic+neon".to_string(), "ARM with NEON".to_string());
151 }
152
153 ("-mcpu=generic".to_string(), "ARM generic".to_string())
155 }
156
157 pub fn report(&self) -> String {
159 format!(
160 r#"
161╔════════════════════════════════════════════╗
162║ AutoZig SIMD Optimization Report ║
163╚════════════════════════════════════════════╝
164
165Architecture: {:?}
166Configuration: {}
167Zig CPU Flag: {}
168Native Features: {}
169
170TIP: Set RUSTFLAGS="-C target-cpu=native" for maximum performance
171"#,
172 self.arch,
173 self.description,
174 self.zig_cpu_flag,
175 if self.use_native {
176 "ENABLED"
177 } else {
178 "disabled"
179 }
180 )
181 }
182
183 pub fn as_zig_flag(&self) -> &str {
185 &self.zig_cpu_flag
186 }
187}
188
189pub fn detect_and_report() -> SimdConfig {
193 let config = SimdConfig::detect();
194
195 println!("cargo:warning={}", config.report());
197
198 println!("cargo:rustc-env=AUTOZIG_SIMD_ARCH={:?}", config.arch);
200 println!("cargo:rustc-env=AUTOZIG_SIMD_CONFIG={}", config.description);
201 println!("cargo:rustc-env=AUTOZIG_ZIG_CPU_FLAG={}", config.zig_cpu_flag);
202
203 config
204}
205
206#[cfg(test)]
207mod tests {
208 use super::*;
209
210 #[test]
211 fn test_detect_arch() {
212 assert_eq!(SimdConfig::detect_arch("x86_64-unknown-linux-gnu"), CpuArch::X86_64);
213 assert_eq!(SimdConfig::detect_arch("aarch64-apple-darwin"), CpuArch::Aarch64);
214 assert_eq!(SimdConfig::detect_arch("armv7-unknown-linux"), CpuArch::Arm);
215 }
216
217 #[test]
218 fn test_x86_feature_detection() {
219 let (flag, desc) = SimdConfig::detect_x86_features("x86_64-pc-windows-msvc", "avx2,fma");
220 assert!(flag.contains("x86_64"));
221 assert!(desc.contains("AVX2") || desc.contains("v3"));
222
223 let (flag, desc) = SimdConfig::detect_x86_features("x86_64-pc-windows-msvc", "sse4.2");
224 assert!(flag.contains("sse4.2") || flag.contains("x86_64"));
225 assert!(desc.contains("SSE") || desc.contains("baseline"));
226 }
227
228 #[test]
229 fn test_arm_feature_detection() {
230 let (flag, desc) = SimdConfig::detect_arm_features("neon");
231 assert!(flag.contains("neon") || flag.contains("generic"));
232 assert!(desc.contains("NEON") || desc.contains("ARM"));
233 }
234
235 #[test]
236 fn test_config_detection() {
237 let config = SimdConfig::detect();
239 assert!(!config.zig_cpu_flag.is_empty());
240 assert!(!config.description.is_empty());
241 }
242
243 #[test]
244 fn test_report_generation() {
245 let config = SimdConfig::detect();
246 let report = config.report();
247 assert!(report.contains("AutoZig SIMD"));
248 assert!(report.contains("Architecture"));
249 }
250}