// Demonstrating use of ifunc & target attribute for selective use of ARM extensions // // build with e.g., -march=armv9-a x.c #include #include #include #include #include #include #include void fn_basic(void) { puts("basic function"); // Note: so long as basic cflags don't imply sve, this line will fail to // compile: uint64_t vl = svcntw (); } __attribute__((target("+sve"))) void fn_advanced(void) { puts("sve using function"); uint64_t vl = svcntw(); printf("vl=%" PRIu64 "\n", vl); } #define get_cpu_ftr(id) \ ({ \ unsigned long __val; \ asm("mrs %0, " #id : "=r"(__val)); \ __val; \ }) #define print_cpu_ftr(id) \ ({ \ unsigned long __val; \ asm("mrs %0, " #id : "=r"(__val)); \ printf("%-20s: 0x%016lx\n", #id, __val); \ }) int sve_level() { return (get_cpu_ftr(ID_AA64ISAR1_EL1) >> 32) & 0xf; } static void (*resolve_fn(void))(void) { bool has_sve = sve_level() == 1; if (has_sve) return fn_advanced; return fn_basic; } void fn(void) __attribute__((ifunc("resolve_fn"))); int main() { #ifdef __BUILTIN_CPU_SUPPORTS__ // my compiler does not ... so don't bother trying printf("has builtin cpu supports"); #endif if (!(getauxval(AT_HWCAP) & HWCAP_CPUID)) { fputs("CPUID registers unavailable\n", stderr); abort(); } //print_cpu_ftr(ID_AA64ISAR0_EL1); print_cpu_ftr(ID_AA64ISAR1_EL1); //print_cpu_ftr(ID_AA64MMFR0_EL1); //print_cpu_ftr(ID_AA64MMFR1_EL1); //print_cpu_ftr(ID_AA64PFR0_EL1); //print_cpu_ftr(ID_AA64PFR1_EL1); //print_cpu_ftr(ID_AA64DFR0_EL1); //print_cpu_ftr(ID_AA64DFR1_EL1); //print_cpu_ftr(MIDR_EL1); //print_cpu_ftr(MPIDR_EL1); //print_cpu_ftr(REVIDR_EL1); printf("sve level %d\n", sve_level()); fn(); }