@@ -30,6 +30,7 @@ pub struct SystemInfo {
3030 pub cpu_vendor_id : String ,
3131 pub cpu_cores : usize ,
3232 pub total_memory_gb : u64 ,
33+ pub cpu_flags : Vec < String > ,
3334}
3435
3536#[ cfg( test) ]
@@ -46,10 +47,96 @@ impl SystemInfo {
4647 cpu_vendor_id : "GenuineIntel" . to_string ( ) ,
4748 cpu_cores : 2 ,
4849 total_memory_gb : 8 ,
50+ cpu_flags : vec ! [
51+ "sse2" . to_string( ) ,
52+ "avx" . to_string( ) ,
53+ "avx2" . to_string( ) ,
54+ "erms" . to_string( ) ,
55+ ] ,
4956 }
5057 }
5158}
5259
60+ #[ cfg( target_os = "linux" ) ]
61+ fn get_glibc_relevant_cpu_flags ( ) -> Vec < String > {
62+ use procfs:: Current ;
63+
64+ /// CPU flags that influence glibc's ifunc dispatch (memcpy, strlen, strcmp, etc.)
65+ ///
66+ /// x86_64: selected from glibc's ifunc resolver logic:
67+ /// - sysdeps/x86_64/multiarch/ifunc-impl-list.c
68+ /// - sysdeps/x86/include/cpu-features.h
69+ ///
70+ /// aarch64: selected from glibc's ifunc resolver logic:
71+ /// - sysdeps/aarch64/multiarch/ifunc-impl-list.c
72+ /// - sysdeps/unix/sysv/linux/aarch64/cpu-features.h
73+ ///
74+ /// Source sources: https://sourceware.org/git/?p=glibc.git
75+ #[ rustfmt:: skip]
76+ const RELEVANT_X86_64_FLAGS : & [ & str ] = & [
77+ // SIMD
78+ "sse2" , "ssse3" , "sse4_1" , "sse4_2" ,
79+ // AVX
80+ "avx" , "avx2" ,
81+ // AVX-512
82+ "avx512f" , "avx512bw" , "avx512vl" ,
83+ // REP string optimizations
84+ "erms" , "fsrm" ,
85+ // Other flags used in glibc dispatch decisions
86+ "rtm" , "bmi1" , "bmi2" , "popcnt" , "fma" , "movbe" ,
87+ ] ;
88+
89+ #[ rustfmt:: skip]
90+ const RELEVANT_AARCH64_FLAGS : & [ & str ] = & [
91+ // SVE-optimized memcpy/memmove/memset
92+ "sve" ,
93+ // Memory Copy/Set instructions (ARMv9, takes priority over SVE)
94+ "mops" ,
95+ // Memory Tagging Extension (affects memchr/strlen variant selection)
96+ "mte" ,
97+ // Enables MIDR_EL1 reads for CPU-specific variants (A64FX, Oryon1, etc.)
98+ "cpuid" ,
99+ ] ;
100+
101+ let cpuinfo = match procfs:: CpuInfo :: current ( ) {
102+ Ok ( cpuinfo) => cpuinfo,
103+ Err ( e) => {
104+ warn ! ( "Failed to read /proc/cpuinfo: {e}" ) ;
105+ return Vec :: new ( ) ;
106+ }
107+ } ;
108+
109+ // /proc/cpuinfo uses "flags" on x86_64 and "Features" on aarch64
110+ let ( relevant_flags, field_name) = if cfg ! ( target_arch = "x86_64" ) {
111+ ( RELEVANT_X86_64_FLAGS , "flags" )
112+ } else if cfg ! ( target_arch = "aarch64" ) {
113+ ( RELEVANT_AARCH64_FLAGS , "Features" )
114+ } else {
115+ return Vec :: new ( ) ;
116+ } ;
117+
118+ let all_flags: Vec < & str > = match cpuinfo. get_field ( 0 , field_name) {
119+ Some ( value) => value. split_whitespace ( ) . collect ( ) ,
120+ None => {
121+ warn ! ( "No CPU flags found in /proc/cpuinfo (field: {field_name})" ) ;
122+ return Vec :: new ( ) ;
123+ }
124+ } ;
125+
126+ let mut flags: Vec < String > = all_flags
127+ . into_iter ( )
128+ . filter ( |flag| relevant_flags. contains ( flag) )
129+ . map ( |flag| flag. to_string ( ) )
130+ . collect ( ) ;
131+ flags. sort ( ) ;
132+ flags
133+ }
134+
135+ #[ cfg( not( target_os = "linux" ) ) ]
136+ fn get_glibc_relevant_cpu_flags ( ) -> Vec < String > {
137+ Vec :: new ( )
138+ }
139+
53140impl SystemInfo {
54141 pub fn new ( ) -> Result < Self > {
55142 let os = System :: distribution_id ( ) ;
@@ -85,6 +172,8 @@ impl SystemInfo {
85172 let cpu_name = cpu. name ( ) . to_string ( ) ;
86173 let cpu_vendor_id = cpu. vendor_id ( ) . to_string ( ) ;
87174
175+ let cpu_flags = get_glibc_relevant_cpu_flags ( ) ;
176+
88177 Ok ( SystemInfo {
89178 os,
90179 os_version,
@@ -96,6 +185,7 @@ impl SystemInfo {
96185 cpu_vendor_id,
97186 cpu_cores,
98187 total_memory_gb,
188+ cpu_flags,
99189 } )
100190 }
101191}
0 commit comments