Support AMD Piledriver by bulldozer kernels.
[platform/upstream/openblas.git] / driver / others / dynamic.c
1 /*********************************************************************/
2 /* Copyright 2009, 2010 The University of Texas at Austin.           */
3 /* All rights reserved.                                              */
4 /*                                                                   */
5 /* Redistribution and use in source and binary forms, with or        */
6 /* without modification, are permitted provided that the following   */
7 /* conditions are met:                                               */
8 /*                                                                   */
9 /*   1. Redistributions of source code must retain the above         */
10 /*      copyright notice, this list of conditions and the following  */
11 /*      disclaimer.                                                  */
12 /*                                                                   */
13 /*   2. Redistributions in binary form must reproduce the above      */
14 /*      copyright notice, this list of conditions and the following  */
15 /*      disclaimer in the documentation and/or other materials       */
16 /*      provided with the distribution.                              */
17 /*                                                                   */
18 /*    THIS  SOFTWARE IS PROVIDED  BY THE  UNIVERSITY OF  TEXAS AT    */
19 /*    AUSTIN  ``AS IS''  AND ANY  EXPRESS OR  IMPLIED WARRANTIES,    */
20 /*    INCLUDING, BUT  NOT LIMITED  TO, THE IMPLIED  WARRANTIES OF    */
21 /*    MERCHANTABILITY  AND FITNESS FOR  A PARTICULAR  PURPOSE ARE    */
22 /*    DISCLAIMED.  IN  NO EVENT SHALL THE UNIVERSITY  OF TEXAS AT    */
23 /*    AUSTIN OR CONTRIBUTORS BE  LIABLE FOR ANY DIRECT, INDIRECT,    */
24 /*    INCIDENTAL,  SPECIAL, EXEMPLARY,  OR  CONSEQUENTIAL DAMAGES    */
25 /*    (INCLUDING, BUT  NOT LIMITED TO,  PROCUREMENT OF SUBSTITUTE    */
26 /*    GOODS  OR  SERVICES; LOSS  OF  USE,  DATA,  OR PROFITS;  OR    */
27 /*    BUSINESS INTERRUPTION) HOWEVER CAUSED  AND ON ANY THEORY OF    */
28 /*    LIABILITY, WHETHER  IN CONTRACT, STRICT  LIABILITY, OR TORT    */
29 /*    (INCLUDING NEGLIGENCE OR OTHERWISE)  ARISING IN ANY WAY OUT    */
30 /*    OF  THE  USE OF  THIS  SOFTWARE,  EVEN  IF ADVISED  OF  THE    */
31 /*    POSSIBILITY OF SUCH DAMAGE.                                    */
32 /*                                                                   */
33 /* The views and conclusions contained in the software and           */
34 /* documentation are those of the authors and should not be          */
35 /* interpreted as representing official policies, either expressed   */
36 /* or implied, of The University of Texas at Austin.                 */
37 /*********************************************************************/
38
39 #include "common.h"
40
41 #ifdef ARCH_X86
42 #define EXTERN extern
43 #else
44 #define EXTERN
45 #endif
46
47 EXTERN gotoblas_t  gotoblas_KATMAI;
48 EXTERN gotoblas_t  gotoblas_COPPERMINE;
49 EXTERN gotoblas_t  gotoblas_NORTHWOOD;
50 EXTERN gotoblas_t  gotoblas_BANIAS;
51 EXTERN gotoblas_t  gotoblas_ATHLON;
52
53 extern gotoblas_t  gotoblas_PRESCOTT;
54 extern gotoblas_t  gotoblas_ATOM;
55 extern gotoblas_t  gotoblas_NANO;
56 extern gotoblas_t  gotoblas_CORE2;
57 extern gotoblas_t  gotoblas_PENRYN;
58 extern gotoblas_t  gotoblas_DUNNINGTON;
59 extern gotoblas_t  gotoblas_NEHALEM;
60 extern gotoblas_t  gotoblas_OPTERON;
61 extern gotoblas_t  gotoblas_OPTERON_SSE3;
62 extern gotoblas_t  gotoblas_BARCELONA;
63 extern gotoblas_t  gotoblas_BOBCAT;
64 #ifndef NO_AVX
65 extern gotoblas_t  gotoblas_SANDYBRIDGE;
66 extern gotoblas_t  gotoblas_BULLDOZER;
67 extern gotoblas_t  gotoblas_PILEDRIVER;
68 #else
69 //Use NEHALEM kernels for sandy bridge
70 #define gotoblas_SANDYBRIDGE gotoblas_NEHALEM
71 #define gotoblas_BULLDOZER gotoblas_BARCELONA
72 #define gotoblas_PILEDRIVER gotoblas_BARCELONA
73 #endif
74 //Use sandy bridge kernels for haswell.
75 #define gotoblas_HASWELL gotoblas_SANDYBRIDGE
76
77 #define VENDOR_INTEL      1
78 #define VENDOR_AMD        2
79 #define VENDOR_CENTAUR    3
80 #define VENDOR_UNKNOWN   99
81
82 #define BITMASK(a, b, c) ((((a) >> (b)) & (c)))
83
84 #ifndef NO_AVX
85 static inline void xgetbv(int op, int * eax, int * edx){
86   //Use binary code for xgetbv
87   __asm__ __volatile__
88     (".byte 0x0f, 0x01, 0xd0": "=a" (*eax), "=d" (*edx) : "c" (op) : "cc");
89 }
90 #endif
91
92 int support_avx(){
93 #ifndef NO_AVX
94   int eax, ebx, ecx, edx;
95   int ret=0;
96   
97   cpuid(1, &eax, &ebx, &ecx, &edx);
98   if ((ecx & (1 << 28)) != 0 && (ecx & (1 << 27)) != 0 && (ecx & (1 << 26)) != 0){
99     xgetbv(0, &eax, &edx);
100     if((eax & 6) == 6){
101       ret=1;  //OS support AVX
102     }
103   }
104   return ret;
105 #else
106   return 0;
107 #endif
108 }
109
110 static int get_vendor(void){
111   int eax, ebx, ecx, edx;
112   char vendor[13];
113
114   cpuid(0, &eax, &ebx, &ecx, &edx);
115   
116   *(int *)(&vendor[0]) = ebx;
117   *(int *)(&vendor[4]) = edx;
118   *(int *)(&vendor[8]) = ecx;
119   vendor[12] = (char)0;
120
121   if (!strcmp(vendor, "GenuineIntel")) return VENDOR_INTEL;
122   if (!strcmp(vendor, "AuthenticAMD")) return VENDOR_AMD;
123   if (!strcmp(vendor, "CentaurHauls")) return VENDOR_CENTAUR;
124
125   if ((eax == 0) || ((eax & 0x500) != 0)) return VENDOR_INTEL;
126
127   return VENDOR_UNKNOWN;
128 }
129
130 static gotoblas_t *get_coretype(void){
131
132   int eax, ebx, ecx, edx;
133   int family, exfamily, model, vendor, exmodel;
134
135   cpuid(1, &eax, &ebx, &ecx, &edx);
136
137   family   = BITMASK(eax,  8, 0x0f);
138   exfamily = BITMASK(eax, 20, 0xff);
139   model    = BITMASK(eax,  4, 0x0f);
140   exmodel  = BITMASK(eax, 16, 0x0f);
141
142   vendor = get_vendor();
143
144   if (vendor == VENDOR_INTEL){
145     switch (family) {
146     case 0x6:
147       switch (exmodel) {
148       case 0:
149         if (model <= 0x7) return &gotoblas_KATMAI;
150         if ((model == 0x8) || (model == 0xa) || (model == 0xb)) return &gotoblas_COPPERMINE;
151         if ((model == 0x9) || (model == 0xd)) return &gotoblas_BANIAS;
152         if (model == 14) return &gotoblas_BANIAS;
153         if (model == 15) return &gotoblas_CORE2;
154         return NULL;
155
156       case 1:
157         if (model == 6) return &gotoblas_CORE2;
158         if (model == 7) return &gotoblas_PENRYN;
159         if (model == 13) return &gotoblas_DUNNINGTON;
160         if ((model == 10) || (model == 11) || (model == 14) || (model == 15)) return &gotoblas_NEHALEM;
161         if (model == 12) return &gotoblas_ATOM;
162         return NULL;
163
164       case 2:
165         //Intel Core (Clarkdale) / Core (Arrandale)
166         // Pentium (Clarkdale) / Pentium Mobile (Arrandale)
167         // Xeon (Clarkdale), 32nm
168         if (model ==  5) return &gotoblas_NEHALEM;
169                   
170         //Intel Xeon Processor 5600 (Westmere-EP)
171         //Xeon Processor E7 (Westmere-EX)
172         //Xeon E7540
173         if (model == 12 || model == 14 || model == 15) return &gotoblas_NEHALEM;
174
175         //Intel Core i5-2000 /i7-2000 (Sandy Bridge)
176         //Intel Core i7-3000 / Xeon E5
177         if (model == 10 || model == 13) {
178           if(support_avx())
179             return &gotoblas_SANDYBRIDGE;
180           else{
181             fprintf(stderr, "OpenBLAS : Your OS does not support AVX instructions. OpenBLAS is using Nehalem kernels as a fallback, which may give poorer performance.\n");
182             return &gotoblas_NEHALEM; //OS doesn't support AVX. Use old kernels.
183           }
184         }
185         return NULL;
186       case 3:
187         //Intel Sandy Bridge 22nm (Ivy Bridge?)
188         if (model == 10) {
189           if(support_avx())
190             return &gotoblas_SANDYBRIDGE;
191           else{
192             fprintf(stderr, "OpenBLAS : Your OS does not support AVX instructions. OpenBLAS is using Nehalem kernels as a fallback, which may give poorer performance.\n");
193             return &gotoblas_NEHALEM; //OS doesn't support AVX. Use old kernels.
194           }
195         }
196         //Intel Haswell
197         if (model == 12) {
198           if(support_avx())
199             return &gotoblas_HASWELL;
200           else{
201             fprintf(stderr, "OpenBLAS : Your OS does not support AVX instructions. OpenBLAS is using Nehalem kernels as a fallback, which may give poorer performance.\n");
202             return &gotoblas_NEHALEM; //OS doesn't support AVX. Use old kernels.
203           }
204         }
205         return NULL;
206       case 4:
207                 //Intel Haswell
208         if (model == 5) {
209           if(support_avx())
210             return &gotoblas_HASWELL;
211           else{
212             fprintf(stderr, "OpenBLAS : Your OS does not support AVX instructions. OpenBLAS is using Nehalem kernels as a fallback, which may give poorer performance.\n");
213             return &gotoblas_NEHALEM; //OS doesn't support AVX. Use old kernels.
214           }
215         }
216         return NULL;
217       }
218       case 0xf:
219       if (model <= 0x2) return &gotoblas_NORTHWOOD;
220       return &gotoblas_PRESCOTT;
221     }
222   }
223
224   if (vendor == VENDOR_AMD){
225     if (family <= 0xe) return &gotoblas_ATHLON;
226     if (family == 0xf){
227       if ((exfamily == 0) || (exfamily == 2)) {
228         if (ecx & (1 <<  0)) return &gotoblas_OPTERON_SSE3; 
229         else return &gotoblas_OPTERON;
230       }  else if (exfamily == 5) {
231         return &gotoblas_BOBCAT;
232       } else if (exfamily == 6) {
233         if(model == 1){
234           //AMD Bulldozer Opteron 6200 / Opteron 4200 / AMD FX-Series
235           if(support_avx())
236             return &gotoblas_BULLDOZER;
237           else{
238             fprintf(stderr, "OpenBLAS : Your OS does not support AVX instructions. OpenBLAS is using Barcelona kernels as a fallback, which may give poorer performance.\n");
239             return &gotoblas_BARCELONA; //OS doesn't support AVX. Use old kernels.
240           }
241         }else if(model == 2){
242           //AMD Bulldozer Opteron 6300 / Opteron 4300 / Opteron 3300
243           if(support_avx())
244             return &gotoblas_PILEDRIVER;
245           else{
246             fprintf(stderr, "OpenBLAS : Your OS does not support AVX instructions. OpenBLAS is using Barcelona kernels as a fallback, which may give poorer performance.\n");
247             return &gotoblas_BARCELONA; //OS doesn't support AVX. Use old kernels.
248           }
249         }
250       } else {
251         return &gotoblas_BARCELONA;
252       }
253     }
254   }
255
256   if (vendor == VENDOR_CENTAUR) {
257     switch (family) {
258     case 0x6:
259       return &gotoblas_NANO;
260       break;
261     }
262   }
263   
264   return NULL;
265 }
266
267 static char *corename[] = {
268     "Unknown",
269     "Katmai",
270     "Coppermine",
271     "Northwood",
272     "Prescott",
273     "Banias",
274     "Atom",
275     "Core2",
276     "Penryn",
277     "Dunnington",
278     "Nehalem",
279     "Athlon",
280     "Opteron",
281     "Opteron(SSE3)",
282     "Barcelona",
283     "Nano",
284     "Sandybridge",
285     "Bobcat",
286     "Bulldozer",
287     "Piledriver",
288 };
289
290 char *gotoblas_corename(void) {
291
292   if (gotoblas == &gotoblas_KATMAI)       return corename[ 1];
293   if (gotoblas == &gotoblas_COPPERMINE)   return corename[ 2];
294   if (gotoblas == &gotoblas_NORTHWOOD)    return corename[ 3];
295   if (gotoblas == &gotoblas_PRESCOTT)     return corename[ 4];
296   if (gotoblas == &gotoblas_BANIAS)       return corename[ 5];
297   if (gotoblas == &gotoblas_ATOM)         return corename[ 6];
298   if (gotoblas == &gotoblas_CORE2)        return corename[ 7];
299   if (gotoblas == &gotoblas_PENRYN)       return corename[ 8];
300   if (gotoblas == &gotoblas_DUNNINGTON)   return corename[ 9];
301   if (gotoblas == &gotoblas_NEHALEM)      return corename[10];
302   if (gotoblas == &gotoblas_ATHLON)       return corename[11];
303   if (gotoblas == &gotoblas_OPTERON_SSE3) return corename[12]; 
304   if (gotoblas == &gotoblas_OPTERON)      return corename[13];
305   if (gotoblas == &gotoblas_BARCELONA)    return corename[14];
306   if (gotoblas == &gotoblas_NANO)         return corename[15];
307   if (gotoblas == &gotoblas_SANDYBRIDGE)  return corename[16];
308   if (gotoblas == &gotoblas_BOBCAT)       return corename[17];
309   if (gotoblas == &gotoblas_BULLDOZER)    return corename[18];
310   if (gotoblas == &gotoblas_PILEDRIVER)    return corename[19];
311
312   return corename[0];
313 }
314
315 void gotoblas_dynamic_init(void) {
316   
317   if (gotoblas) return;
318
319   gotoblas = get_coretype();
320   
321 #ifdef ARCH_X86
322   if (gotoblas == NULL) gotoblas = &gotoblas_KATMAI;
323 #else
324   if (gotoblas == NULL) gotoblas = &gotoblas_PRESCOTT;
325   /* sanity check, if 64bit pointer we can't have a 32 bit cpu */
326   if (sizeof(void*) == 8) {
327       if (gotoblas == &gotoblas_KATMAI ||
328           gotoblas == &gotoblas_COPPERMINE ||
329           gotoblas == &gotoblas_NORTHWOOD ||
330           gotoblas == &gotoblas_BANIAS ||
331           gotoblas == &gotoblas_ATHLON)
332           gotoblas = &gotoblas_PRESCOTT;
333   }
334 #endif
335   
336   if (gotoblas && gotoblas -> init) {
337     gotoblas -> init();
338   } else {
339     fprintf(stderr, "OpenBLAS : Architecture Initialization failed. No initialization function found.\n");
340     exit(1);
341   }
342   
343 }
344
345 void gotoblas_dynamic_quit(void) {
346   
347   gotoblas = NULL;
348
349 }