Skip to content

Commit

Permalink
[RISCV] Implement ifunc selector
Browse files Browse the repository at this point in the history
Here is proposal riscv-non-isa/riscv-c-api-doc#48.

During the Function multi-version dispatch the function, we need a method to retrieve the RISC-V hardware environment to make sure all extension must be available.

Differential Revision: https://reviews.llvm.org/D155938
  • Loading branch information
BeMg committed Sep 6, 2023
1 parent bc2f78c commit ae8e867
Show file tree
Hide file tree
Showing 2 changed files with 260 additions and 0 deletions.
1 change: 1 addition & 0 deletions compiler-rt/lib/builtins/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -698,6 +698,7 @@ endif()
set(powerpc64le_SOURCES ${powerpc64_SOURCES})

set(riscv_SOURCES
riscv/ifunc_select.c
riscv/fp_mode.c
riscv/save.S
riscv/restore.S
Expand Down
259 changes: 259 additions & 0 deletions compiler-rt/lib/builtins/riscv/ifunc_select.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,259 @@
//=== ifunc_select.c - Check environment hardware feature -*- C -*-===========//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#define BUFSIZE 1024

static void fillzero(char *s, unsigned size) {
for (int i = 0; i < size; i++)
s[i] = '\0';
}

static unsigned strcmp(const char *a, const char *b) {
while (*a != '\0' && *b != '\0') {
if (*a != *b)
return 0;
a++;
b++;
}
if (*a == *b)
return 1;
return 0;
}

static void memcpy(char *dest, const char *src, unsigned len) {
for (unsigned i = 0; i < len; i++)
dest[i] = src[i];
}

static unsigned strlen(char *s) {
int len = 0;
while (*s != '\0') {
len++;
s++;
}
return len;
}

// TODO: Replace with more efficient algorithm
static unsigned isPatternInSrc(char *src, char *pattern) {
char buf[BUFSIZE];
int lenOfSrc = strlen(src);
int lenOfPattern = strlen(pattern);
for (int i = 0; i + lenOfPattern < lenOfSrc; i++) {
fillzero(buf, BUFSIZE);
memcpy(buf, src + i, lenOfPattern);
if (strcmp(buf, pattern))
return 1;
}
return 0;
}

static char *tokenize(char *src, char *rst, char delim) {
while (*src != '\0' && *src != delim) {
*rst = *src;
src++;
rst++;
}
if (*src == delim)
src++;
return src;
}

static void getBasicExtFromHwFeatStr(char *rst, char *src) {
while (*src != 'r') {
src++;
}

while (*src != '_' && *src != '\0') {
*rst = *src;
rst++;
src++;
}
}

static unsigned checkFeatStrInHwFeatStr(char *FeatStr, char *HwFeatStr) {
// For basic extension like a,m,f,d...
if (strlen(FeatStr) == 1) {
char BasicExt[BUFSIZE];
fillzero(BasicExt, BUFSIZE);
getBasicExtFromHwFeatStr(BasicExt, HwFeatStr);
return isPatternInSrc(BasicExt, FeatStr);
}
// For zbb,zihintntl...
return isPatternInSrc(HwFeatStr, FeatStr);
}

#if defined(__linux__)

#define SYS_read 63
#define SYS_openat 56
#define SYS_riscv_hwprobe 258

static long syscall_impl_3_args(long number, long arg1, long arg2, long arg3) {
register long a7 __asm__("a7") = number;
register long a0 __asm__("a0") = arg1;
register long a1 __asm__("a1") = arg2;
register long a2 __asm__("a2") = arg3;
__asm__ __volatile__("ecall\n\t"
: "=r"(a0)
: "r"(a7), "r"(a0), "r"(a1), "r"(a2)
: "memory");
return a0;
}

static long syscall_impl_5_args(long number, long arg1, long arg2, long arg3,
long arg4, long arg5) {
register long a7 __asm__("a7") = number;
register long a0 __asm__("a0") = arg1;
register long a1 __asm__("a1") = arg2;
register long a2 __asm__("a2") = arg3;
register long a3 __asm__("a3") = arg4;
register long a4 __asm__("a4") = arg5;
__asm__ __volatile__("ecall\n\t"
: "=r"(a0)
: "r"(a7), "r"(a0), "r"(a1), "r"(a2), "r"(a3), "r"(a4)
: "memory");
return a0;
}

static unsigned read(int fd, const void *buf, unsigned count) {
return syscall_impl_3_args(SYS_read, fd, (long)buf, count);
}

static int openat(int dirfd, const char *pathname, int flags) {
return syscall_impl_3_args(SYS_openat, (long)dirfd, (long)pathname, flags);
}

static void readUntilNextLine(int fd, char *rst, int len) {
char buf = '\0';
while (buf != '\n' && read(fd, &buf, 1) != 0 && len > 0) {
*rst = buf;
rst++;
len--;
}
}

static void __riscv_cpuinfo(char *FeatStr) {
char buf[BUFSIZE];
int fd = openat(0, "/proc/cpuinfo", 2);
do {
fillzero(buf, BUFSIZE);
readUntilNextLine(fd, buf, BUFSIZE);
if (isPatternInSrc(buf, "isa")) {
memcpy(FeatStr, buf, BUFSIZE);
return;
}
} while (strlen(buf) != 0);
}

struct riscv_hwprobe {
long long key;
unsigned long long value;
};

#define RISCV_HWPROBE_MAX_KEY 5
#define RISCV_HWPROBE_KEY_MVENDORID 0
#define RISCV_HWPROBE_KEY_MARCHID 1
#define RISCV_HWPROBE_KEY_MIMPID 2
#define RISCV_HWPROBE_KEY_BASE_BEHAVIOR 3
#define RISCV_HWPROBE_BASE_BEHAVIOR_IMA (1 << 0)
#define RISCV_HWPROBE_KEY_IMA_EXT_0 4
#define RISCV_HWPROBE_IMA_FD (1 << 0)
#define RISCV_HWPROBE_IMA_C (1 << 1)
#define RISCV_HWPROBE_IMA_V (1 << 2)
#define RISCV_HWPROBE_EXT_ZBA (1 << 3)
#define RISCV_HWPROBE_EXT_ZBB (1 << 4)
#define RISCV_HWPROBE_EXT_ZBS (1 << 5)
#define RISCV_HWPROBE_KEY_CPUPERF_0 5
#define RISCV_HWPROBE_MISALIGNED_UNKNOWN (0 << 0)
#define RISCV_HWPROBE_MISALIGNED_EMULATED (1 << 0)
#define RISCV_HWPROBE_MISALIGNED_SLOW (2 << 0)
#define RISCV_HWPROBE_MISALIGNED_FAST (3 << 0)
#define RISCV_HWPROBE_MISALIGNED_UNSUPPORTED (4 << 0)
#define RISCV_HWPROBE_MISALIGNED_MASK (7 << 0)

/* Size definition for CPU sets. */
#define __CPU_SETSIZE 1024
#define __NCPUBITS (8 * sizeof(unsigned long int))

/* Data structure to describe CPU mask. */
typedef struct {
unsigned long int __bits[__CPU_SETSIZE / __NCPUBITS];
} cpu_set_t;

static long sys_riscv_hwprobe(struct riscv_hwprobe *pairs, unsigned pair_count,
unsigned cpu_count, cpu_set_t *cpus,
unsigned int flags) {
return syscall_impl_5_args(SYS_riscv_hwprobe, (long)pairs, pair_count,
cpu_count, (long)cpus, flags);
}

static void initHwProbe(struct riscv_hwprobe *Hwprobes, int len) {
sys_riscv_hwprobe(Hwprobes, len, 0, 0, 0);
}

static unsigned checkFeatStrInHwProbe(char *FeatStr, struct riscv_hwprobe Hwprobe) {
if (Hwprobe.key == -1)
return 0;

if (strcmp(FeatStr, "i"))
return Hwprobe.value & RISCV_HWPROBE_KEY_IMA_EXT_0;
if (strcmp(FeatStr, "m"))
return Hwprobe.value & RISCV_HWPROBE_KEY_IMA_EXT_0;
if (strcmp(FeatStr, "a"))
return Hwprobe.value & RISCV_HWPROBE_KEY_IMA_EXT_0;
if (strcmp(FeatStr, "f"))
return Hwprobe.value & RISCV_HWPROBE_IMA_FD;
if (strcmp(FeatStr, "d"))
return Hwprobe.value & RISCV_HWPROBE_IMA_FD;
if (strcmp(FeatStr, "c"))
return Hwprobe.value & RISCV_HWPROBE_IMA_C;
if (strcmp(FeatStr, "v"))
return Hwprobe.value & RISCV_HWPROBE_IMA_V;
if (strcmp(FeatStr, "zba"))
return Hwprobe.value & RISCV_HWPROBE_EXT_ZBA;
if (strcmp(FeatStr, "zbb"))
return Hwprobe.value & RISCV_HWPROBE_EXT_ZBB;
if (strcmp(FeatStr, "zbs"))
return Hwprobe.value & RISCV_HWPROBE_EXT_ZBS;

return 0;
}
#endif // defined(__linux__)

// FeatStr format is like <Feature1>;<Feature2>...<FeatureN>.
unsigned __riscv_ifunc_select(char *FeatStrs) {
#if defined(__linux__)
// Init Hwprobe
struct riscv_hwprobe pairs[] = {
{RISCV_HWPROBE_KEY_IMA_EXT_0, 0},
};
initHwProbe(pairs, 1);
// Init from cpuinfo
char HwFeatStr[BUFSIZE];
fillzero(HwFeatStr, BUFSIZE);
__riscv_cpuinfo(HwFeatStr);

// Check each extension whether available
char FeatStr[BUFSIZE];
while (*FeatStrs != '\0') {
fillzero(FeatStr, BUFSIZE);
FeatStrs = tokenize(FeatStrs, FeatStr, ';');
if (checkFeatStrInHwProbe(FeatStr, pairs[0]))
continue;
if (!checkFeatStrInHwFeatStr(FeatStr, HwFeatStr))
return 0;
}

return 1;
#else
// If other platform support IFUNC, need to implement its
// __riscv_ifunc_select.
return 0;
#endif
}

0 comments on commit ae8e867

Please sign in to comment.