Skip to content

Commit ae8e867

Browse files
committed
[RISCV] Implement ifunc selector
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
1 parent bc2f78c commit ae8e867

File tree

2 files changed

+260
-0
lines changed

2 files changed

+260
-0
lines changed

compiler-rt/lib/builtins/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -698,6 +698,7 @@ endif()
698698
set(powerpc64le_SOURCES ${powerpc64_SOURCES})
699699

700700
set(riscv_SOURCES
701+
riscv/ifunc_select.c
701702
riscv/fp_mode.c
702703
riscv/save.S
703704
riscv/restore.S
Lines changed: 259 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,259 @@
1+
//=== ifunc_select.c - Check environment hardware feature -*- C -*-===========//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#define BUFSIZE 1024
10+
11+
static void fillzero(char *s, unsigned size) {
12+
for (int i = 0; i < size; i++)
13+
s[i] = '\0';
14+
}
15+
16+
static unsigned strcmp(const char *a, const char *b) {
17+
while (*a != '\0' && *b != '\0') {
18+
if (*a != *b)
19+
return 0;
20+
a++;
21+
b++;
22+
}
23+
if (*a == *b)
24+
return 1;
25+
return 0;
26+
}
27+
28+
static void memcpy(char *dest, const char *src, unsigned len) {
29+
for (unsigned i = 0; i < len; i++)
30+
dest[i] = src[i];
31+
}
32+
33+
static unsigned strlen(char *s) {
34+
int len = 0;
35+
while (*s != '\0') {
36+
len++;
37+
s++;
38+
}
39+
return len;
40+
}
41+
42+
// TODO: Replace with more efficient algorithm
43+
static unsigned isPatternInSrc(char *src, char *pattern) {
44+
char buf[BUFSIZE];
45+
int lenOfSrc = strlen(src);
46+
int lenOfPattern = strlen(pattern);
47+
for (int i = 0; i + lenOfPattern < lenOfSrc; i++) {
48+
fillzero(buf, BUFSIZE);
49+
memcpy(buf, src + i, lenOfPattern);
50+
if (strcmp(buf, pattern))
51+
return 1;
52+
}
53+
return 0;
54+
}
55+
56+
static char *tokenize(char *src, char *rst, char delim) {
57+
while (*src != '\0' && *src != delim) {
58+
*rst = *src;
59+
src++;
60+
rst++;
61+
}
62+
if (*src == delim)
63+
src++;
64+
return src;
65+
}
66+
67+
static void getBasicExtFromHwFeatStr(char *rst, char *src) {
68+
while (*src != 'r') {
69+
src++;
70+
}
71+
72+
while (*src != '_' && *src != '\0') {
73+
*rst = *src;
74+
rst++;
75+
src++;
76+
}
77+
}
78+
79+
static unsigned checkFeatStrInHwFeatStr(char *FeatStr, char *HwFeatStr) {
80+
// For basic extension like a,m,f,d...
81+
if (strlen(FeatStr) == 1) {
82+
char BasicExt[BUFSIZE];
83+
fillzero(BasicExt, BUFSIZE);
84+
getBasicExtFromHwFeatStr(BasicExt, HwFeatStr);
85+
return isPatternInSrc(BasicExt, FeatStr);
86+
}
87+
// For zbb,zihintntl...
88+
return isPatternInSrc(HwFeatStr, FeatStr);
89+
}
90+
91+
#if defined(__linux__)
92+
93+
#define SYS_read 63
94+
#define SYS_openat 56
95+
#define SYS_riscv_hwprobe 258
96+
97+
static long syscall_impl_3_args(long number, long arg1, long arg2, long arg3) {
98+
register long a7 __asm__("a7") = number;
99+
register long a0 __asm__("a0") = arg1;
100+
register long a1 __asm__("a1") = arg2;
101+
register long a2 __asm__("a2") = arg3;
102+
__asm__ __volatile__("ecall\n\t"
103+
: "=r"(a0)
104+
: "r"(a7), "r"(a0), "r"(a1), "r"(a2)
105+
: "memory");
106+
return a0;
107+
}
108+
109+
static long syscall_impl_5_args(long number, long arg1, long arg2, long arg3,
110+
long arg4, long arg5) {
111+
register long a7 __asm__("a7") = number;
112+
register long a0 __asm__("a0") = arg1;
113+
register long a1 __asm__("a1") = arg2;
114+
register long a2 __asm__("a2") = arg3;
115+
register long a3 __asm__("a3") = arg4;
116+
register long a4 __asm__("a4") = arg5;
117+
__asm__ __volatile__("ecall\n\t"
118+
: "=r"(a0)
119+
: "r"(a7), "r"(a0), "r"(a1), "r"(a2), "r"(a3), "r"(a4)
120+
: "memory");
121+
return a0;
122+
}
123+
124+
static unsigned read(int fd, const void *buf, unsigned count) {
125+
return syscall_impl_3_args(SYS_read, fd, (long)buf, count);
126+
}
127+
128+
static int openat(int dirfd, const char *pathname, int flags) {
129+
return syscall_impl_3_args(SYS_openat, (long)dirfd, (long)pathname, flags);
130+
}
131+
132+
static void readUntilNextLine(int fd, char *rst, int len) {
133+
char buf = '\0';
134+
while (buf != '\n' && read(fd, &buf, 1) != 0 && len > 0) {
135+
*rst = buf;
136+
rst++;
137+
len--;
138+
}
139+
}
140+
141+
static void __riscv_cpuinfo(char *FeatStr) {
142+
char buf[BUFSIZE];
143+
int fd = openat(0, "/proc/cpuinfo", 2);
144+
do {
145+
fillzero(buf, BUFSIZE);
146+
readUntilNextLine(fd, buf, BUFSIZE);
147+
if (isPatternInSrc(buf, "isa")) {
148+
memcpy(FeatStr, buf, BUFSIZE);
149+
return;
150+
}
151+
} while (strlen(buf) != 0);
152+
}
153+
154+
struct riscv_hwprobe {
155+
long long key;
156+
unsigned long long value;
157+
};
158+
159+
#define RISCV_HWPROBE_MAX_KEY 5
160+
#define RISCV_HWPROBE_KEY_MVENDORID 0
161+
#define RISCV_HWPROBE_KEY_MARCHID 1
162+
#define RISCV_HWPROBE_KEY_MIMPID 2
163+
#define RISCV_HWPROBE_KEY_BASE_BEHAVIOR 3
164+
#define RISCV_HWPROBE_BASE_BEHAVIOR_IMA (1 << 0)
165+
#define RISCV_HWPROBE_KEY_IMA_EXT_0 4
166+
#define RISCV_HWPROBE_IMA_FD (1 << 0)
167+
#define RISCV_HWPROBE_IMA_C (1 << 1)
168+
#define RISCV_HWPROBE_IMA_V (1 << 2)
169+
#define RISCV_HWPROBE_EXT_ZBA (1 << 3)
170+
#define RISCV_HWPROBE_EXT_ZBB (1 << 4)
171+
#define RISCV_HWPROBE_EXT_ZBS (1 << 5)
172+
#define RISCV_HWPROBE_KEY_CPUPERF_0 5
173+
#define RISCV_HWPROBE_MISALIGNED_UNKNOWN (0 << 0)
174+
#define RISCV_HWPROBE_MISALIGNED_EMULATED (1 << 0)
175+
#define RISCV_HWPROBE_MISALIGNED_SLOW (2 << 0)
176+
#define RISCV_HWPROBE_MISALIGNED_FAST (3 << 0)
177+
#define RISCV_HWPROBE_MISALIGNED_UNSUPPORTED (4 << 0)
178+
#define RISCV_HWPROBE_MISALIGNED_MASK (7 << 0)
179+
180+
/* Size definition for CPU sets. */
181+
#define __CPU_SETSIZE 1024
182+
#define __NCPUBITS (8 * sizeof(unsigned long int))
183+
184+
/* Data structure to describe CPU mask. */
185+
typedef struct {
186+
unsigned long int __bits[__CPU_SETSIZE / __NCPUBITS];
187+
} cpu_set_t;
188+
189+
static long sys_riscv_hwprobe(struct riscv_hwprobe *pairs, unsigned pair_count,
190+
unsigned cpu_count, cpu_set_t *cpus,
191+
unsigned int flags) {
192+
return syscall_impl_5_args(SYS_riscv_hwprobe, (long)pairs, pair_count,
193+
cpu_count, (long)cpus, flags);
194+
}
195+
196+
static void initHwProbe(struct riscv_hwprobe *Hwprobes, int len) {
197+
sys_riscv_hwprobe(Hwprobes, len, 0, 0, 0);
198+
}
199+
200+
static unsigned checkFeatStrInHwProbe(char *FeatStr, struct riscv_hwprobe Hwprobe) {
201+
if (Hwprobe.key == -1)
202+
return 0;
203+
204+
if (strcmp(FeatStr, "i"))
205+
return Hwprobe.value & RISCV_HWPROBE_KEY_IMA_EXT_0;
206+
if (strcmp(FeatStr, "m"))
207+
return Hwprobe.value & RISCV_HWPROBE_KEY_IMA_EXT_0;
208+
if (strcmp(FeatStr, "a"))
209+
return Hwprobe.value & RISCV_HWPROBE_KEY_IMA_EXT_0;
210+
if (strcmp(FeatStr, "f"))
211+
return Hwprobe.value & RISCV_HWPROBE_IMA_FD;
212+
if (strcmp(FeatStr, "d"))
213+
return Hwprobe.value & RISCV_HWPROBE_IMA_FD;
214+
if (strcmp(FeatStr, "c"))
215+
return Hwprobe.value & RISCV_HWPROBE_IMA_C;
216+
if (strcmp(FeatStr, "v"))
217+
return Hwprobe.value & RISCV_HWPROBE_IMA_V;
218+
if (strcmp(FeatStr, "zba"))
219+
return Hwprobe.value & RISCV_HWPROBE_EXT_ZBA;
220+
if (strcmp(FeatStr, "zbb"))
221+
return Hwprobe.value & RISCV_HWPROBE_EXT_ZBB;
222+
if (strcmp(FeatStr, "zbs"))
223+
return Hwprobe.value & RISCV_HWPROBE_EXT_ZBS;
224+
225+
return 0;
226+
}
227+
#endif // defined(__linux__)
228+
229+
// FeatStr format is like <Feature1>;<Feature2>...<FeatureN>.
230+
unsigned __riscv_ifunc_select(char *FeatStrs) {
231+
#if defined(__linux__)
232+
// Init Hwprobe
233+
struct riscv_hwprobe pairs[] = {
234+
{RISCV_HWPROBE_KEY_IMA_EXT_0, 0},
235+
};
236+
initHwProbe(pairs, 1);
237+
// Init from cpuinfo
238+
char HwFeatStr[BUFSIZE];
239+
fillzero(HwFeatStr, BUFSIZE);
240+
__riscv_cpuinfo(HwFeatStr);
241+
242+
// Check each extension whether available
243+
char FeatStr[BUFSIZE];
244+
while (*FeatStrs != '\0') {
245+
fillzero(FeatStr, BUFSIZE);
246+
FeatStrs = tokenize(FeatStrs, FeatStr, ';');
247+
if (checkFeatStrInHwProbe(FeatStr, pairs[0]))
248+
continue;
249+
if (!checkFeatStrInHwFeatStr(FeatStr, HwFeatStr))
250+
return 0;
251+
}
252+
253+
return 1;
254+
#else
255+
// If other platform support IFUNC, need to implement its
256+
// __riscv_ifunc_select.
257+
return 0;
258+
#endif
259+
}

0 commit comments

Comments
 (0)