You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

dynamic_riscv64.c 8.5 kB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269
  1. /*****************************************************************************
  2. Copyright (c) 2024, The OpenBLAS Project
  3. All rights reserved.
  4. Redistribution and use in source and binary forms, with or without
  5. modification, are permitted provided that the following conditions are
  6. met:
  7. 1. Redistributions of source code must retain the above copyright
  8. notice, this list of conditions and the following disclaimer.
  9. 2. Redistributions in binary form must reproduce the above copyright
  10. notice, this list of conditions and the following disclaimer in
  11. the documentation and/or other materials provided with the
  12. distribution.
  13. 3. Neither the name of the OpenBLAS project nor the names of
  14. its contributors may be used to endorse or promote products
  15. derived from this software without specific prior written
  16. permission.
  17. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  18. AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  19. IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  20. ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
  21. LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  22. DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
  23. SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  24. CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  25. OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
  26. USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  27. **********************************************************************************/
  28. #include <stdbool.h>
  29. #include "common.h"
  30. /*
  31. * OpenBLAS contains some kernels that are optimised for RVV 1.0. Before we
  32. * can use these kernels we need to determine whether the device supports
  33. * RVV 1.0 and what the device's VLEN is. Our strategy will be as follows.
  34. *
  35. * First we'll invoke the hwprobe syscall to detect RVV 1.0. In an ideal world,
  36. * this is all we should need to do. If the syscall is not implemented we
  37. * should be able to deduce that RVV 1.0 is not supported (as it was added to
  38. * Linux after hwprobe) and if the syscall is implemented we can use it to
  39. * determine whether RVV 1.0 is supported. However, there are some riscv64
  40. * boards out there that implement RVV 1.0 but ship with a Linux kernel that
  41. * predates RVV vector support and hwprobe support. These kernels contain
  42. * the backported RVV patches but not the hwprobe patches and so they
  43. * advertise support for RVV via hwcap. To cater for these boards we need
  44. * to fall back to hwcap if hwprobe is not supported. Unfortunately, some
  45. * boards indicate support for RVV via hwcap even though they only support
  46. * RVV 0.7.1, which is incompatible with RVV 1.0. So an additional check is
  47. * required to test if the devices advertising support for RVV via hwcap really
  48. * support RVV 1.0. This test works by executing a vsetvli instruction that
  49. * sets the tail agnostic and mask agnostic bits in the vtype register.
  50. * These bits are not supported prior to RVV 0.9 so will cause the VIL bit to
  51. * be set on the VTYPE register in CPUs supporting 0.7.1. If this bit is set
  52. * we can determine that RVV 1.0 is not supported.
  53. *
  54. * This approach is borrowed from
  55. * VideoLan dav1d:
  56. * (https://code.videolan.org/videolan/dav1d/-/merge_requests/1629).
  57. *
  58. * We assume that if a kernel reports the presence of RVV via hwcap that
  59. * the device supports the vsetvli instruction.
  60. *
  61. * For now we're just going to invoke the hwprobe syscall directly, rather than
  62. * invoking it through glibc. Support for hwprobe has been added to glibc but
  63. * at the time of writing this support has not yet been included in a glibc
  64. * release. Once it has, it will be better to invoke hwprobe via glibc as doing
  65. * so should take advantage of the vdso entry and be more efficient.
  66. */
  67. /*
  68. * This should work on Android as well but I have no way of testing.
  69. */
  70. #if defined(OS_LINUX)
  71. #include <unistd.h>
  72. #include <sys/syscall.h>
  73. #include <stdint.h>
  74. #include <sys/auxv.h>
  75. #define DETECT_RISCV64_HWCAP_ISA_V (1 << ('V' - 'A'))
  76. struct riscv_hwprobe {
  77. int64_t key;
  78. uint64_t value;
  79. };
  80. /* The constants below are copied from
  81. * /usr/include/riscv64-linux-gnu/asm/hwprobe.h. We duplicate the
  82. * constants as the header file from which they are copied will only
  83. * be present if we're building on a device with Linux 6.5 or greater.
  84. */
  85. #define RISCV_HWPROBE_KEY_IMA_EXT_0 4
  86. #define RISCV_HWPROBE_IMA_V (1 << 2)
  87. #ifndef NR_riscv_hwprobe
  88. #ifndef NR_arch_specific_syscall
  89. #define NR_arch_specific_syscall 244
  90. #endif
  91. #define NR_riscv_hwprobe (NR_arch_specific_syscall + 14)
  92. #endif
  93. #endif // defined(OS_LINUX)
  94. unsigned detect_riscv64_get_vlenb(void);
  95. uint64_t detect_riscv64_rvv100(void);
  96. extern gotoblas_t gotoblas_RISCV64_GENERIC;
  97. #if !defined(DYNAMIC_LIST) || defined(DYN_RISCV64_ZVL256B)
  98. extern gotoblas_t gotoblas_RISCV64_ZVL256B;
  99. #endif
  100. #if !defined(DYNAMIC_LIST) || defined(DYN_RISCV64_ZVL128B)
  101. extern gotoblas_t gotoblas_RISCV64_ZVL128B;
  102. #endif
  103. #define CPU_GENERIC 0
  104. #define CPU_RISCV64_ZVL256B 1
  105. #define CPU_RISCV64_ZVL128B 2
  106. static char *cpuname[] = {
  107. "riscv64_generic",
  108. "riscv64_zvl256b",
  109. "riscv64_zvl128b"
  110. };
  111. #define NUM_CORETYPES (sizeof(cpuname)/sizeof(char*))
  112. extern int openblas_verbose(void);
  113. extern void openblas_warning(int verbose, const char* msg);
  114. char* gotoblas_corename(void) {
  115. #if !defined(DYNAMIC_LIST) || defined(DYN_RISCV64_ZVL256B)
  116. if (gotoblas == &gotoblas_RISCV64_ZVL256B)
  117. return cpuname[CPU_RISCV64_ZVL256B];
  118. #endif
  119. #if !defined(DYNAMIC_LIST) || defined(DYN_RISCV64_ZVL128B)
  120. if (gotoblas == &gotoblas_RISCV64_ZVL128B)
  121. return cpuname[CPU_RISCV64_ZVL128B];
  122. #endif
  123. if (gotoblas == &gotoblas_RISCV64_GENERIC)
  124. return cpuname[CPU_GENERIC];
  125. return "unknown";
  126. }
  127. static gotoblas_t* get_coretype(void) {
  128. unsigned vlenb = 0;
  129. #if !defined(OS_LINUX)
  130. return NULL;
  131. #else
  132. /*
  133. * See the hwprobe documentation
  134. *
  135. * ( https://docs.kernel.org/arch/riscv/hwprobe.html )
  136. * for more details.
  137. */
  138. struct riscv_hwprobe pairs[] = {
  139. { .key = RISCV_HWPROBE_KEY_IMA_EXT_0, },
  140. };
  141. int ret = syscall(NR_riscv_hwprobe, pairs, 1, 0, NULL, 0);
  142. if (ret == 0) {
  143. if (!(pairs[0].value & RISCV_HWPROBE_IMA_V))
  144. return NULL;
  145. } else {
  146. if (!(getauxval(AT_HWCAP) & DETECT_RISCV64_HWCAP_ISA_V))
  147. return NULL;
  148. if (!detect_riscv64_rvv100())
  149. return NULL;
  150. }
  151. /*
  152. * RVV 1.0 is supported. We now just need to determine the coretype
  153. * based on the VLEN.
  154. */
  155. vlenb = detect_riscv64_get_vlenb();
  156. if (vlenb < 16)
  157. return NULL;
  158. #if !defined(DYNAMIC_LIST) || defined(DYN_RISCV64_ZVL256B)
  159. if (vlenb >= 32)
  160. return &gotoblas_RISCV64_ZVL256B;
  161. #endif
  162. #if !defined(DYNAMIC_LIST) || defined(DYN_RISCV64_ZVL128B)
  163. return &gotoblas_RISCV64_ZVL128B;
  164. #else
  165. return NULL;
  166. #endif
  167. #endif // !defined(OS_LINUX)
  168. }
  169. static gotoblas_t* force_coretype(char* coretype) {
  170. size_t i;
  171. char message[128];
  172. for (i = 0; i < NUM_CORETYPES && strcasecmp(coretype, cpuname[i]); i++);
  173. if (i == CPU_GENERIC)
  174. return &gotoblas_RISCV64_GENERIC;
  175. if (i == CPU_RISCV64_ZVL256B) {
  176. #if !defined(DYNAMIC_LIST) || defined(DYN_RISCV64_ZVL256B)
  177. return &gotoblas_RISCV64_ZVL256B;
  178. #else
  179. openblas_warning(1,
  180. "riscv64_zvl256b support not compiled in\n");
  181. return NULL;
  182. #endif
  183. }
  184. if (i == CPU_RISCV64_ZVL128B) {
  185. #if !defined(DYNAMIC_LIST) || defined(DYN_RISCV64_ZVL128B)
  186. return &gotoblas_RISCV64_ZVL128B;
  187. #else
  188. openblas_warning(1,
  189. "riscv64_zvl128b support not compiled in\n");
  190. return NULL;
  191. #endif
  192. }
  193. snprintf(message, sizeof(message), "Core not found: %s\n", coretype);
  194. openblas_warning(1, message);
  195. return NULL;
  196. }
  197. void gotoblas_dynamic_init(void) {
  198. char coremsg[128];
  199. char* p;
  200. if (gotoblas) return;
  201. p = getenv("OPENBLAS_CORETYPE");
  202. if (p)
  203. gotoblas = force_coretype(p);
  204. else
  205. gotoblas = get_coretype();
  206. if (!gotoblas) {
  207. snprintf(coremsg, sizeof(coremsg), "Falling back to generic riscv64 core\n");
  208. openblas_warning(1, coremsg);
  209. gotoblas = &gotoblas_RISCV64_GENERIC;
  210. }
  211. if (gotoblas->init) {
  212. snprintf(coremsg, sizeof(coremsg), "Core: %s\n",
  213. gotoblas_corename());
  214. openblas_warning(2, coremsg);
  215. gotoblas->init();
  216. return;
  217. }
  218. openblas_warning(0, "OpenBLAS : Architecture Initialization failed. No initialization function found.\n");
  219. exit(1);
  220. }
  221. void gotoblas_dynamic_quit(void) {
  222. gotoblas = NULL;
  223. }