x.c

download raw

// Demonstrating use of ifunc & target attribute for selective use of ARM extensions
//
// build with e.g., -march=armv9-a x.c

#include <arm_sve.h>
#include <asm/hwcap.h>
#include <inttypes.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/auxv.h>

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();
}