1 #include "common.h"
2 #include <stdbool.h>
3 
4 // Guard the use of getauxval() on glibc version >= 2.16
5 #ifdef __GLIBC__
6 #include <features.h>
7 #if __GLIBC_PREREQ(2, 16)
8 #include <sys/auxv.h>
9 #define HAVE_GETAUXVAL 1
10 
get_hwcap(void)11 static unsigned long get_hwcap(void)
12 {
13 	unsigned long hwcap = getauxval(AT_HWCAP);
14 	char *maskenv;
15 
16 	// honor requests for not using specific CPU features in LD_HWCAP_MASK
17 	maskenv = getenv("LD_HWCAP_MASK");
18 	if (maskenv)
19 		hwcap &= strtoul(maskenv, NULL, 0);
20 
21 	return hwcap;
22 	// note that a missing auxval is interpreted as no capabilities
23 	// available, which is safe.
24 }
25 
26 #else // __GLIBC_PREREQ(2, 16)
27 #warn "Cannot detect SIMD support in Z13 or newer architectures since glibc is older than 2.16"
28 
get_hwcap(void)29 static unsigned long get_hwcap(void) {
30 	// treat missing support for getauxval() as no capabilities available,
31 	// which is safe.
32 	return 0;
33 }
34 #endif // __GLIBC_PREREQ(2, 16)
35 #endif // __GLIBC
36 
37 extern gotoblas_t gotoblas_ZARCH_GENERIC;
38 #ifdef DYN_Z13
39 extern gotoblas_t gotoblas_Z13;
40 #endif
41 #ifdef DYN_Z14
42 extern gotoblas_t gotoblas_Z14;
43 #endif
44 
45 #define NUM_CORETYPES 4
46 
47 extern void openblas_warning(int verbose, const char* msg);
48 
49 static char* corename[] = {
50 	"unknown",
51 	"Z13",
52 	"Z14",
53 	"ZARCH_GENERIC",
54 };
55 
gotoblas_corename(void)56 char* gotoblas_corename(void) {
57 #ifdef DYN_Z13
58 	if (gotoblas == &gotoblas_Z13)	return corename[1];
59 #endif
60 #ifdef DYN_Z14
61 	if (gotoblas == &gotoblas_Z14)	return corename[2];
62 #endif
63 	if (gotoblas == &gotoblas_ZARCH_GENERIC) return corename[3];
64 
65 	return corename[0];
66 }
67 
68 #ifndef HWCAP_S390_VXE
69 #define HWCAP_S390_VXE 8192
70 #endif
71 
72 /**
73  * Detect the fitting set of kernels by retrieving the CPU features supported by
74  * OS from the auxiliary value AT_HWCAP and choosing the set of kernels
75  * ("coretype") that exploits most of the features and can be compiled with the
76  * available gcc version.
77  * Note that we cannot use vector registers on a z13 or newer unless supported
78  * by the OS kernel (which needs to handle them properly during context switch).
79  */
get_coretype(void)80 static gotoblas_t* get_coretype(void) {
81 
82 	unsigned long hwcap __attribute__((unused)) = get_hwcap();
83 
84 #ifdef DYN_Z14
85 	// z14 and z15 systems: exploit Vector Facility (SIMD) and
86 	// Vector-Enhancements Facility 1 (float SIMD instructions), if present.
87 	if ((hwcap & HWCAP_S390_VX) && (hwcap & HWCAP_S390_VXE))
88 		return &gotoblas_Z14;
89 #endif
90 
91 #ifdef DYN_Z13
92 	// z13: Vector Facility (SIMD for double)
93 	if (hwcap & HWCAP_S390_VX)
94 		return &gotoblas_Z13;
95 #endif
96 
97 	// fallback in case of missing compiler support, systems before z13, or
98 	// when the OS does not advertise support for the Vector Facility (e.g.,
99 	// missing support in the OS kernel)
100 	return &gotoblas_ZARCH_GENERIC;
101 }
102 
force_coretype(char * coretype)103 static gotoblas_t* force_coretype(char* coretype) {
104 
105 	int i;
106 	int found = -1;
107 	char message[128];
108 
109 	for (i = 0; i < NUM_CORETYPES; i++)
110 	{
111 		if (!strncasecmp(coretype, corename[i], 20))
112 		{
113 			found = i;
114 			break;
115 		}
116 	}
117 
118 	if (found == 1) {
119 #ifdef DYN_Z13
120 		return &gotoblas_Z13;
121 #else
122 		openblas_warning(1, "Z13 support not compiled in");
123 		return NULL;
124 #endif
125 	} else if (found == 2) {
126 #ifdef DYN_Z14
127 		return &gotoblas_Z14;
128 #else
129 		openblas_warning(1, "Z14 support not compiled in");
130 		return NULL;
131 #endif
132 	} else if (found == 3) {
133 		return &gotoblas_ZARCH_GENERIC;
134 	}
135 
136 	snprintf(message, 128, "Core not found: %s\n", coretype);
137 	openblas_warning(1, message);
138 	return NULL;
139 }
140 
gotoblas_dynamic_init(void)141 void gotoblas_dynamic_init(void) {
142 
143 	char coremsg[128];
144 	char coren[22];
145 	char* p;
146 
147 
148 	if (gotoblas) return;
149 
150 	p = getenv("OPENBLAS_CORETYPE");
151 	if (p)
152 	{
153 		gotoblas = force_coretype(p);
154 	}
155 	else
156 	{
157 		gotoblas = get_coretype();
158 	}
159 
160 	if (gotoblas == NULL)
161 	{
162 		snprintf(coremsg, 128, "Failed to detect system, falling back to generic z support.\n");
163 		openblas_warning(1, coremsg);
164 		gotoblas = &gotoblas_ZARCH_GENERIC;
165 	}
166 
167 	if (gotoblas && gotoblas->init) {
168 		strncpy(coren, gotoblas_corename(), 20);
169 		sprintf(coremsg, "Core: %s\n", coren);
170 		openblas_warning(2, coremsg);
171 		gotoblas->init();
172 	}
173 	else {
174 		openblas_warning(0, "OpenBLAS : Architecture Initialization failed. No initialization function found.\n");
175 		exit(1);
176 	}
177 }
178 
gotoblas_dynamic_quit(void)179 void gotoblas_dynamic_quit(void) {
180 	gotoblas = NULL;
181 }
182