0ac44f6f584a6012554a23449317f39d1ff32d58
[platform/upstream/openblas.git] / driver / others / memory.c
1 /*****************************************************************************
2 Copyright (c) 2011-2014, The OpenBLAS Project
3 All rights reserved.
4
5 Redistribution and use in source and binary forms, with or without
6 modification, are permitted provided that the following conditions are
7 met:
8
9    1. Redistributions of source code must retain the above copyright
10       notice, this list of conditions and the following disclaimer.
11
12    2. Redistributions in binary form must reproduce the above copyright
13       notice, this list of conditions and the following disclaimer in
14       the documentation and/or other materials provided with the
15       distribution.
16    3. Neither the name of the OpenBLAS project nor the names of 
17       its contributors may be used to endorse or promote products 
18       derived from this software without specific prior written 
19       permission.
20
21 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
22 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
25 LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
27 SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
28 CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
29 OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
30 USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31
32 **********************************************************************************/
33
34 /*********************************************************************/
35 /* Copyright 2009, 2010 The University of Texas at Austin.           */
36 /* All rights reserved.                                              */
37 /*                                                                   */
38 /* Redistribution and use in source and binary forms, with or        */
39 /* without modification, are permitted provided that the following   */
40 /* conditions are met:                                               */
41 /*                                                                   */
42 /*   1. Redistributions of source code must retain the above         */
43 /*      copyright notice, this list of conditions and the following  */
44 /*      disclaimer.                                                  */
45 /*                                                                   */
46 /*   2. Redistributions in binary form must reproduce the above      */
47 /*      copyright notice, this list of conditions and the following  */
48 /*      disclaimer in the documentation and/or other materials       */
49 /*      provided with the distribution.                              */
50 /*                                                                   */
51 /*    THIS  SOFTWARE IS PROVIDED  BY THE  UNIVERSITY OF  TEXAS AT    */
52 /*    AUSTIN  ``AS IS''  AND ANY  EXPRESS OR  IMPLIED WARRANTIES,    */
53 /*    INCLUDING, BUT  NOT LIMITED  TO, THE IMPLIED  WARRANTIES OF    */
54 /*    MERCHANTABILITY  AND FITNESS FOR  A PARTICULAR  PURPOSE ARE    */
55 /*    DISCLAIMED.  IN  NO EVENT SHALL THE UNIVERSITY  OF TEXAS AT    */
56 /*    AUSTIN OR CONTRIBUTORS BE  LIABLE FOR ANY DIRECT, INDIRECT,    */
57 /*    INCIDENTAL,  SPECIAL, EXEMPLARY,  OR  CONSEQUENTIAL DAMAGES    */
58 /*    (INCLUDING, BUT  NOT LIMITED TO,  PROCUREMENT OF SUBSTITUTE    */
59 /*    GOODS  OR  SERVICES; LOSS  OF  USE,  DATA,  OR PROFITS;  OR    */
60 /*    BUSINESS INTERRUPTION) HOWEVER CAUSED  AND ON ANY THEORY OF    */
61 /*    LIABILITY, WHETHER  IN CONTRACT, STRICT  LIABILITY, OR TORT    */
62 /*    (INCLUDING NEGLIGENCE OR OTHERWISE)  ARISING IN ANY WAY OUT    */
63 /*    OF  THE  USE OF  THIS  SOFTWARE,  EVEN  IF ADVISED  OF  THE    */
64 /*    POSSIBILITY OF SUCH DAMAGE.                                    */
65 /*                                                                   */
66 /* The views and conclusions contained in the software and           */
67 /* documentation are those of the authors and should not be          */
68 /* interpreted as representing official policies, either expressed   */
69 /* or implied, of The University of Texas at Austin.                 */
70 /*********************************************************************/
71
72 //#undef  DEBUG
73
74 #include "common.h"
75 #include <errno.h>
76
77 #ifdef OS_WINDOWS
78 #define ALLOC_WINDOWS
79 #ifndef MEM_LARGE_PAGES
80 #define MEM_LARGE_PAGES  0x20000000
81 #endif
82 #else
83 #define ALLOC_MMAP
84 #define ALLOC_MALLOC
85 #endif
86
87 #include <stdlib.h>
88 #include <stdio.h>
89 #include <fcntl.h>
90
91 #ifndef OS_WINDOWS
92 #include <sys/mman.h>
93 #ifndef NO_SYSV_IPC
94 #include <sys/shm.h>
95 #endif
96 #include <sys/ipc.h>
97 #endif
98
99 #include <sys/types.h>
100
101 #ifdef OS_LINUX
102 #include <sys/sysinfo.h>
103 #include <sched.h>
104 #include <errno.h>
105 #include <linux/unistd.h>
106 #include <sys/syscall.h>
107 #include <sys/time.h>
108 #include <sys/resource.h>
109 #endif
110
111 #if defined(OS_FREEBSD) || defined(OS_DARWIN)
112 #include <sys/sysctl.h>
113 #include <sys/resource.h>
114 #endif
115
116 #if defined(OS_WINDOWS) && (defined(__MINGW32__) || defined(__MINGW64__))
117 #include <conio.h>
118 #undef  printf
119 #define printf  _cprintf
120 #endif
121
122 #ifdef OS_LINUX
123
124 #ifndef MPOL_PREFERRED
125 #define MPOL_PREFERRED  1
126 #endif
127
128 #endif
129
130 #if (defined(PPC440) || !defined(OS_LINUX) || defined(HPL)) && !defined(NO_WARMUP)
131 #define NO_WARMUP
132 #endif
133
134 #ifndef SHM_HUGETLB
135 #define SHM_HUGETLB 04000
136 #endif
137
138 #ifndef FIXED_PAGESIZE
139 #define FIXED_PAGESIZE 4096
140 #endif
141
142 #define BITMASK(a, b, c) ((((a) >> (b)) & (c)))
143
144 #if defined(_MSC_VER) && !defined(__clang__)
145 #define CONSTRUCTOR __cdecl
146 #define DESTRUCTOR __cdecl
147 #elif (defined(OS_DARWIN) || defined(OS_SUNOS)) && defined(C_GCC)
148 #define CONSTRUCTOR     __attribute__ ((constructor))
149 #define DESTRUCTOR      __attribute__ ((destructor))
150 #else
151 #define CONSTRUCTOR     __attribute__ ((constructor(101)))
152 #define DESTRUCTOR      __attribute__ ((destructor(101)))
153 #endif
154
155 #ifdef DYNAMIC_ARCH
156 gotoblas_t *gotoblas = NULL;
157 #endif
158
159 extern void openblas_warning(int verbose, const char * msg);
160
161 #ifndef SMP
162
163 #define blas_cpu_number 1
164 #define blas_num_threads 1
165
166 /* Dummy Function */
167 int  goto_get_num_procs  (void) { return 1;};
168 void goto_set_num_threads(int num_threads) {};
169
170 #else
171
172 #if defined(OS_LINUX) || defined(OS_SUNOS) || defined(OS_NETBSD)
173 #ifndef NO_AFFINITY
174 int get_num_procs(void);
175 #else
176 int get_num_procs(void) {
177   static int nums = 0;
178   if (!nums) nums = sysconf(_SC_NPROCESSORS_CONF);
179   return nums;
180 }
181 #endif
182 #endif
183
184 #ifdef OS_ANDROID
185 int get_num_procs(void) {
186   static int nums = 0;
187   if (!nums) nums = sysconf(_SC_NPROCESSORS_CONF);
188   return nums;
189 }
190 #endif
191
192 #ifdef OS_WINDOWS
193
194 int get_num_procs(void) {
195
196   static int nums = 0;
197
198   if (nums == 0) {
199
200     SYSTEM_INFO sysinfo;
201
202     GetSystemInfo(&sysinfo);
203
204     nums = sysinfo.dwNumberOfProcessors;
205   }
206
207   return nums;
208 }
209
210 #endif
211
212 #if defined(OS_FREEBSD)
213
214 int get_num_procs(void) {
215
216   static int nums = 0;
217
218   int m[2];
219   size_t len;
220
221   if (nums == 0) {
222     m[0] = CTL_HW;
223     m[1] = HW_NCPU;
224     len = sizeof(int);
225     sysctl(m, 2, &nums, &len, NULL, 0);
226   }
227
228   return nums;
229 }
230
231 #endif
232
233 #if defined(OS_DARWIN)
234 int get_num_procs(void) {
235   static int nums = 0;
236   size_t len;
237   if (nums == 0){
238     len = sizeof(int);
239     sysctlbyname("hw.physicalcpu", &nums, &len, NULL, 0);
240   }
241   return nums;
242 }
243 /*
244 void set_stack_limit(int limitMB){
245   int result=0;
246   struct rlimit rl;
247   rlim_t StackSize;
248
249   StackSize=limitMB*1024*1024;
250   result=getrlimit(RLIMIT_STACK, &rl);
251   if(result==0){
252     if(rl.rlim_cur < StackSize){
253       rl.rlim_cur=StackSize;
254       result=setrlimit(RLIMIT_STACK, &rl);
255       if(result !=0){
256         fprintf(stderr, "OpenBLAS: set stack limit error =%d\n", result);
257       }
258     }
259   }
260 }
261 */
262 #endif
263
264
265 /*
266 OpenBLAS uses the numbers of CPU cores in multithreading.
267 It can be set by openblas_set_num_threads(int num_threads);
268 */
269 int blas_cpu_number  = 0;
270 /*
271 The numbers of threads in the thread pool.
272 This value is equal or large than blas_cpu_number. This means some threads are sleep.
273 */
274 int blas_num_threads = 0;
275
276 int  goto_get_num_procs  (void) {
277   return blas_cpu_number;
278 }
279
280 void openblas_fork_handler()
281 {
282   // This handler shuts down the OpenBLAS-managed PTHREAD pool when OpenBLAS is
283   // built with "make USE_OPENMP=0".
284   // Hanging can still happen when OpenBLAS is built against the libgomp
285   // implementation of OpenMP. The problem is tracked at:
286   //   http://gcc.gnu.org/bugzilla/show_bug.cgi?id=60035
287   // In the mean time build with USE_OPENMP=0 or link against another
288   // implementation of OpenMP.
289 #if !(defined(OS_WINDOWS) || defined(OS_ANDROID)) && defined(SMP_SERVER)
290   int err;
291   err = pthread_atfork ((void (*)(void)) BLASFUNC(blas_thread_shutdown), NULL, NULL);
292   if(err != 0)
293     openblas_warning(0, "OpenBLAS Warning ... cannot install fork handler. You may meet hang after fork.\n");
294 #endif
295 }
296
297 extern int openblas_num_threads_env();
298 extern int openblas_goto_num_threads_env();
299 extern int openblas_omp_num_threads_env();
300
301 int blas_get_cpu_number(void){
302 #if defined(OS_LINUX) || defined(OS_WINDOWS) || defined(OS_FREEBSD) || defined(OS_DARWIN) || defined(OS_ANDROID)
303   int max_num;
304 #endif
305   int blas_goto_num   = 0;
306   int blas_omp_num    = 0;
307
308   if (blas_num_threads) return blas_num_threads;
309
310 #if defined(OS_LINUX) || defined(OS_WINDOWS) || defined(OS_FREEBSD) || defined(OS_DARWIN) || defined(OS_ANDROID)
311   max_num = get_num_procs();
312 #endif
313
314   blas_goto_num = 0;
315 #ifndef USE_OPENMP
316   blas_goto_num=openblas_num_threads_env();
317   if (blas_goto_num < 0) blas_goto_num = 0;
318
319   if (blas_goto_num == 0) {
320     blas_goto_num=openblas_goto_num_threads_env();
321     if (blas_goto_num < 0) blas_goto_num = 0;
322   }
323
324 #endif
325
326   blas_omp_num = 0;
327   blas_omp_num=openblas_omp_num_threads_env();
328   if (blas_omp_num < 0) blas_omp_num = 0;
329
330   if (blas_goto_num > 0) blas_num_threads = blas_goto_num;
331   else if (blas_omp_num > 0) blas_num_threads = blas_omp_num;
332   else blas_num_threads = MAX_CPU_NUMBER;
333
334 #if defined(OS_LINUX) || defined(OS_WINDOWS) || defined(OS_FREEBSD) || defined(OS_DARWIN) || defined(OS_ANDROID)
335   if (blas_num_threads > max_num) blas_num_threads = max_num;
336 #endif
337
338   if (blas_num_threads > MAX_CPU_NUMBER) blas_num_threads = MAX_CPU_NUMBER;
339
340 #ifdef DEBUG
341   printf( "Adjusted number of threads : %3d\n", blas_num_threads);
342 #endif
343
344   blas_cpu_number = blas_num_threads;
345
346   return blas_num_threads;
347 }
348 #endif
349
350
351 int openblas_get_num_procs(void) {
352 #ifndef SMP
353   return 1;
354 #else
355   return get_num_procs();
356 #endif
357 }
358
359 int openblas_get_num_threads(void) {
360 #ifndef SMP
361   return 1;
362 #else
363   // init blas_cpu_number if needed
364   blas_get_cpu_number();
365   return blas_cpu_number;
366 #endif
367 }
368
369 struct release_t {
370   void *address;
371   void (*func)(struct release_t *);
372   long attr;
373 };
374
375 int hugetlb_allocated = 0;
376
377 static struct release_t release_info[NUM_BUFFERS];
378 static int release_pos = 0;
379
380 #if defined(OS_LINUX) && !defined(NO_WARMUP)
381 static int hot_alloc = 0;
382 #endif
383
384 /* Global lock for memory allocation */
385
386 #if   defined(USE_PTHREAD_LOCK)
387 static pthread_mutex_t    alloc_lock = PTHREAD_MUTEX_INITIALIZER;
388 #elif defined(USE_PTHREAD_SPINLOCK)
389 static pthread_spinlock_t alloc_lock = 0;
390 #else
391 static BLASULONG  alloc_lock = 0UL;
392 #endif
393
394 #ifdef ALLOC_MMAP
395
396 static void alloc_mmap_free(struct release_t *release){
397
398   if (munmap(release -> address, BUFFER_SIZE)) {
399     printf("OpenBLAS : munmap failed\n");
400   }
401 }
402
403
404
405 #ifdef NO_WARMUP
406
407 static void *alloc_mmap(void *address){
408   void *map_address;
409
410   if (address){
411     map_address = mmap(address,
412                        BUFFER_SIZE,
413                        MMAP_ACCESS, MMAP_POLICY | MAP_FIXED, -1, 0);
414   } else {
415     map_address = mmap(address,
416                        BUFFER_SIZE,
417                        MMAP_ACCESS, MMAP_POLICY, -1, 0);
418   }
419
420   if (map_address != (void *)-1) {
421     LOCK_COMMAND(&alloc_lock);
422     release_info[release_pos].address = map_address;
423     release_info[release_pos].func    = alloc_mmap_free;
424     release_pos ++;
425     UNLOCK_COMMAND(&alloc_lock);
426   }
427
428 #ifdef OS_LINUX
429   my_mbind(map_address, BUFFER_SIZE, MPOL_PREFERRED, NULL, 0, 0);
430 #endif
431
432   return map_address;
433 }
434
435 #else
436
437 #define BENCH_ITERATION 4
438 #define SCALING         2
439
440 static inline BLASULONG run_bench(BLASULONG address, BLASULONG size) {
441
442   BLASULONG original, *p;
443   BLASULONG start, stop, min;
444   int iter, i, count;
445
446   min = (BLASULONG)-1;
447
448   original = *(BLASULONG *)(address + size - PAGESIZE);
449
450   *(BLASULONG *)(address + size - PAGESIZE) = (BLASULONG)address;
451
452   for (iter = 0; iter < BENCH_ITERATION; iter ++ ) {
453
454     p = (BLASULONG *)address;
455
456     count = size / PAGESIZE;
457
458     start = rpcc();
459
460     for (i = 0; i < count; i ++) {
461       p = (BLASULONG *)(*p);
462     }
463
464     stop = rpcc();
465
466     if (min > stop - start) min = stop - start;
467   }
468
469   *(BLASULONG *)(address + size - PAGESIZE +  0) = original;
470   *(BLASULONG *)(address + size - PAGESIZE +  8) = (BLASULONG)p;
471
472   return min;
473 }
474
475 static void *alloc_mmap(void *address){
476   void *map_address, *best_address;
477   BLASULONG best, start, current;
478   BLASULONG allocsize;
479
480   if (address){
481     /* Just give up use advanced operation */
482     map_address = mmap(address, BUFFER_SIZE, MMAP_ACCESS, MMAP_POLICY | MAP_FIXED, -1, 0);
483
484 #ifdef OS_LINUX
485     my_mbind(map_address, BUFFER_SIZE, MPOL_PREFERRED, NULL, 0, 0);
486 #endif
487
488   } else {
489 #if defined(OS_LINUX) && !defined(NO_WARMUP)
490     if (hot_alloc == 0) {
491       map_address = mmap(NULL, BUFFER_SIZE, MMAP_ACCESS, MMAP_POLICY, -1, 0);
492
493 #ifdef OS_LINUX
494       my_mbind(map_address, BUFFER_SIZE, MPOL_PREFERRED, NULL, 0, 0);
495 #endif
496
497     } else {
498 #endif
499
500       map_address = mmap(NULL, BUFFER_SIZE * SCALING,
501                          MMAP_ACCESS, MMAP_POLICY, -1, 0);
502
503       if (map_address != (void *)-1) {
504
505 #ifdef OS_LINUX
506 #ifdef DEBUG
507                   int ret=0;
508                   ret=my_mbind(map_address, BUFFER_SIZE * SCALING, MPOL_PREFERRED, NULL, 0, 0);
509                   if(ret==-1){
510                           int errsv=errno;
511                           perror("OpenBLAS alloc_mmap:");
512                           printf("error code=%d,\tmap_address=%lx\n",errsv,map_address);
513                   }
514
515 #else
516                   my_mbind(map_address, BUFFER_SIZE * SCALING, MPOL_PREFERRED, NULL, 0, 0);
517 #endif
518 #endif
519
520
521         allocsize = DGEMM_P * DGEMM_Q * sizeof(double);
522
523         start   = (BLASULONG)map_address;
524         current = (SCALING - 1) * BUFFER_SIZE;
525
526         while(current > 0) {
527           *(BLASLONG *)start = (BLASLONG)start + PAGESIZE;
528           start += PAGESIZE;
529           current -= PAGESIZE;
530         }
531
532         *(BLASLONG *)(start - PAGESIZE) = (BLASULONG)map_address;
533
534         start = (BLASULONG)map_address;
535
536         best = (BLASULONG)-1;
537         best_address = map_address;
538
539         while ((start + allocsize  < (BLASULONG)map_address + (SCALING - 1) * BUFFER_SIZE)) {
540
541           current = run_bench(start, allocsize);
542
543           if (best > current) {
544             best = current;
545             best_address = (void *)start;
546           }
547
548           start += PAGESIZE;
549
550         }
551
552       if ((BLASULONG)best_address > (BLASULONG)map_address)
553         munmap(map_address,  (BLASULONG)best_address - (BLASULONG)map_address);
554
555       munmap((void *)((BLASULONG)best_address + BUFFER_SIZE), (SCALING - 1) * BUFFER_SIZE + (BLASULONG)map_address - (BLASULONG)best_address);
556
557       map_address = best_address;
558
559 #if defined(OS_LINUX) && !defined(NO_WARMUP)
560       hot_alloc = 2;
561 #endif
562       }
563     }
564 #if defined(OS_LINUX) && !defined(NO_WARMUP)
565   }
566 #endif
567   LOCK_COMMAND(&alloc_lock);
568
569   if (map_address != (void *)-1) {
570     release_info[release_pos].address = map_address;
571     release_info[release_pos].func    = alloc_mmap_free;
572     release_pos ++;
573   }
574   UNLOCK_COMMAND(&alloc_lock);
575
576   return map_address;
577 }
578
579 #endif
580
581 #endif
582
583
584 #ifdef ALLOC_MALLOC
585
586 static void alloc_malloc_free(struct release_t *release){
587
588   free(release -> address);
589
590 }
591
592 static void *alloc_malloc(void *address){
593
594   void *map_address;
595
596   map_address = (void *)malloc(BUFFER_SIZE + FIXED_PAGESIZE);
597
598   if (map_address == (void *)NULL) map_address = (void *)-1;
599
600   if (map_address != (void *)-1) {
601     release_info[release_pos].address = map_address;
602     release_info[release_pos].func    = alloc_malloc_free;
603     release_pos ++;
604   }
605
606   return map_address;
607
608 }
609
610 #endif
611
612 #ifdef ALLOC_QALLOC
613
614 void *qalloc(int flags, size_t bytes);
615 void *qfree (void *address);
616
617 #define QNONCACHE 0x1
618 #define QCOMMS    0x2
619 #define QFAST     0x4
620
621 static void alloc_qalloc_free(struct release_t *release){
622
623   qfree(release -> address);
624
625 }
626
627 static void *alloc_qalloc(void *address){
628   void *map_address;
629
630   map_address = (void *)qalloc(QCOMMS | QFAST, BUFFER_SIZE + FIXED_PAGESIZE);
631
632   if (map_address == (void *)NULL) map_address = (void *)-1;
633
634   if (map_address != (void *)-1) {
635     release_info[release_pos].address = map_address;
636     release_info[release_pos].func    = alloc_qalloc_free;
637     release_pos ++;
638   }
639
640   return (void *)(((BLASULONG)map_address + FIXED_PAGESIZE - 1) & ~(FIXED_PAGESIZE - 1));
641 }
642
643 #endif
644
645 #ifdef ALLOC_WINDOWS
646
647 static void alloc_windows_free(struct release_t *release){
648
649   VirtualFree(release -> address, BUFFER_SIZE, MEM_DECOMMIT);
650
651 }
652
653 static void *alloc_windows(void *address){
654   void *map_address;
655
656   map_address  = VirtualAlloc(address,
657                               BUFFER_SIZE,
658                               MEM_RESERVE | MEM_COMMIT,
659                               PAGE_READWRITE);
660
661   if (map_address == (void *)NULL) map_address = (void *)-1;
662
663   if (map_address != (void *)-1) {
664     release_info[release_pos].address = map_address;
665     release_info[release_pos].func    = alloc_windows_free;
666     release_pos ++;
667   }
668
669   return map_address;
670 }
671
672 #endif
673
674 #ifdef ALLOC_DEVICEDRIVER
675 #ifndef DEVICEDRIVER_NAME
676 #define DEVICEDRIVER_NAME "/dev/mapper"
677 #endif
678
679 static void alloc_devicedirver_free(struct release_t *release){
680
681   if (munmap(release -> address, BUFFER_SIZE)) {
682     printf("OpenBLAS : Bugphysarea unmap failed.\n");
683   }
684
685   if (close(release -> attr)) {
686     printf("OpenBLAS : Bugphysarea close failed.\n");
687   }
688
689 }
690
691 static void *alloc_devicedirver(void *address){
692
693   int fd;
694   void *map_address;
695
696   if ((fd = open(DEVICEDRIVER_NAME, O_RDWR | O_SYNC)) < 0) {
697
698     return (void *)-1;
699
700   }
701
702   map_address = mmap(address, BUFFER_SIZE,
703                      PROT_READ | PROT_WRITE,
704                      MAP_FILE | MAP_SHARED,
705                      fd, 0);
706
707   if (map_address != (void *)-1) {
708     release_info[release_pos].address = map_address;
709     release_info[release_pos].attr    = fd;
710     release_info[release_pos].func    = alloc_devicedirver_free;
711     release_pos ++;
712   }
713
714   return map_address;
715 }
716
717 #endif
718
719 #ifdef ALLOC_SHM
720
721 static void alloc_shm_free(struct release_t *release){
722
723   if (shmdt(release -> address)) {
724     printf("OpenBLAS : Shared memory unmap failed.\n");
725     }
726 }
727
728 static void *alloc_shm(void *address){
729   void *map_address;
730   int shmid;
731
732   shmid = shmget(IPC_PRIVATE, BUFFER_SIZE,IPC_CREAT | 0600);
733
734   map_address = (void *)shmat(shmid, address, 0);
735
736   if (map_address != (void *)-1){
737
738 #ifdef OS_LINUX
739     my_mbind(map_address, BUFFER_SIZE, MPOL_PREFERRED, NULL, 0, 0);
740 #endif
741
742     shmctl(shmid, IPC_RMID, 0);
743
744     release_info[release_pos].address = map_address;
745     release_info[release_pos].attr    = shmid;
746     release_info[release_pos].func    = alloc_shm_free;
747     release_pos ++;
748   }
749
750   return map_address;
751 }
752
753 #if defined OS_LINUX  || defined OS_AIX  || defined __sun__  || defined OS_WINDOWS
754
755 static void alloc_hugetlb_free(struct release_t *release){
756
757 #if defined(OS_LINUX) || defined(OS_AIX)
758   if (shmdt(release -> address)) {
759     printf("OpenBLAS : Hugepage unmap failed.\n");
760   }
761 #endif
762
763 #ifdef __sun__
764
765   munmap(release -> address, BUFFER_SIZE);
766
767 #endif
768
769 #ifdef OS_WINDOWS
770
771   VirtualFree(release -> address, BUFFER_SIZE, MEM_LARGE_PAGES | MEM_DECOMMIT);
772
773 #endif
774
775 }
776
777 static void *alloc_hugetlb(void *address){
778
779   void *map_address = (void *)-1;
780
781 #if defined(OS_LINUX) || defined(OS_AIX)
782   int shmid;
783
784   shmid = shmget(IPC_PRIVATE, BUFFER_SIZE,
785 #ifdef OS_LINUX
786                  SHM_HUGETLB |
787 #endif
788 #ifdef OS_AIX
789                  SHM_LGPAGE | SHM_PIN |
790 #endif
791                  IPC_CREAT | SHM_R | SHM_W);
792
793   if (shmid != -1) {
794     map_address = (void *)shmat(shmid, address, SHM_RND);
795
796 #ifdef OS_LINUX
797     my_mbind(map_address, BUFFER_SIZE, MPOL_PREFERRED, NULL, 0, 0);
798 #endif
799
800     if (map_address != (void *)-1){
801       shmctl(shmid, IPC_RMID, 0);
802     }
803   }
804 #endif
805
806 #ifdef __sun__
807   struct memcntl_mha mha;
808
809   mha.mha_cmd = MHA_MAPSIZE_BSSBRK;
810   mha.mha_flags = 0;
811   mha.mha_pagesize = HUGE_PAGESIZE;
812   memcntl(NULL, 0, MC_HAT_ADVISE, (char *)&mha, 0, 0);
813
814   map_address = (BLASULONG)memalign(HUGE_PAGESIZE, BUFFER_SIZE);
815 #endif
816
817 #ifdef OS_WINDOWS
818
819   HANDLE hToken;
820   TOKEN_PRIVILEGES tp;
821
822   if (OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hToken) != TRUE) return (void *) -1;
823
824   tp.PrivilegeCount = 1;
825   tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
826   
827   if (LookupPrivilegeValue(NULL, SE_LOCK_MEMORY_NAME, &tp.Privileges[0].Luid) != TRUE) {
828       CloseHandle(hToken);
829       return (void*)-1;
830   }
831
832   if (AdjustTokenPrivileges(hToken, FALSE, &tp, 0, NULL, NULL) != TRUE) {
833       CloseHandle(hToken);
834       return (void*)-1;
835   }
836
837   map_address  = (void *)VirtualAlloc(address,
838                                       BUFFER_SIZE,
839                                       MEM_LARGE_PAGES | MEM_RESERVE | MEM_COMMIT,
840                                       PAGE_READWRITE);
841
842   tp.Privileges[0].Attributes = 0;
843   AdjustTokenPrivileges(hToken, FALSE, &tp, 0, NULL, NULL);
844
845   if (map_address == (void *)NULL) map_address = (void *)-1;
846
847 #endif
848
849   if (map_address != (void *)-1){
850     release_info[release_pos].address = map_address;
851     release_info[release_pos].func    = alloc_hugetlb_free;
852     release_pos ++;
853   }
854
855   return map_address;
856 }
857 #endif
858
859 #endif
860
861 #ifdef  ALLOC_HUGETLBFILE
862
863 static int hugetlb_pid = 0;
864
865 static void alloc_hugetlbfile_free(struct release_t *release){
866
867   if (munmap(release -> address, BUFFER_SIZE)) {
868     printf("OpenBLAS : HugeTLBfs unmap failed.\n");
869   }
870
871   if (close(release -> attr)) {
872     printf("OpenBLAS : HugeTLBfs close failed.\n");
873   }
874 }
875
876 static void *alloc_hugetlbfile(void *address){
877
878   void *map_address = (void *)-1;
879   int fd;
880   char filename[64];
881
882   if (!hugetlb_pid) hugetlb_pid = getpid();
883
884   sprintf(filename, "%s/gotoblas.%d", HUGETLB_FILE_NAME, hugetlb_pid);
885
886   if ((fd = open(filename, O_RDWR | O_CREAT, 0700)) < 0) {
887     return (void *)-1;
888   }
889
890   unlink(filename);
891
892   map_address = mmap(address, BUFFER_SIZE,
893                      PROT_READ | PROT_WRITE,
894                      MAP_SHARED,
895                      fd, 0);
896
897   if (map_address != (void *)-1) {
898     release_info[release_pos].address = map_address;
899     release_info[release_pos].attr    = fd;
900     release_info[release_pos].func    = alloc_hugetlbfile_free;
901     release_pos ++;
902   }
903
904   return map_address;
905 }
906 #endif
907
908
909 #ifdef SEEK_ADDRESS
910 static BLASULONG base_address      = 0UL;
911 #else
912 static BLASULONG base_address      = BASE_ADDRESS;
913 #endif
914
915 static volatile struct {
916   BLASULONG lock;
917   void *addr;
918 #if defined(WHEREAMI) && !defined(USE_OPENMP)
919   int   pos;
920 #endif
921   int used;
922 #ifndef __64BIT__
923   char dummy[48];
924 #else
925   char dummy[40];
926 #endif
927
928 } memory[NUM_BUFFERS];
929
930 static int memory_initialized = 0;
931
932 /*       Memory allocation routine           */
933 /* procpos ... indicates where it comes from */
934 /*                0 : Level 3 functions      */
935 /*                1 : Level 2 functions      */
936 /*                2 : Thread                 */
937
938 void *blas_memory_alloc(int procpos){
939
940   int position;
941 #if defined(WHEREAMI) && !defined(USE_OPENMP)
942   int mypos;
943 #endif
944
945   void *map_address;
946
947   void *(*memoryalloc[])(void *address) = {
948 #ifdef ALLOC_DEVICEDRIVER
949     alloc_devicedirver,
950 #endif
951 /* Hugetlb implicitly assumes ALLOC_SHM */
952 #ifdef ALLOC_SHM
953     alloc_shm,
954 #endif
955 #if ((defined ALLOC_SHM) && (defined OS_LINUX  || defined OS_AIX  || defined __sun__  || defined OS_WINDOWS))
956     alloc_hugetlb,
957 #endif
958 #ifdef ALLOC_MMAP
959     alloc_mmap,
960 #endif
961 #ifdef ALLOC_QALLOC
962     alloc_qalloc,
963 #endif
964 #ifdef ALLOC_WINDOWS
965     alloc_windows,
966 #endif
967 #ifdef ALLOC_MALLOC
968     alloc_malloc,
969 #endif
970     NULL,
971   };
972   void *(**func)(void *address);
973   LOCK_COMMAND(&alloc_lock);
974
975   if (!memory_initialized) {
976
977 #if defined(WHEREAMI) && !defined(USE_OPENMP)
978     for (position = 0; position < NUM_BUFFERS; position ++){
979       memory[position].addr   = (void *)0;
980       memory[position].pos    = -1;
981       memory[position].used   = 0;
982       memory[position].lock   = 0;
983     }
984 #endif
985
986 #ifdef DYNAMIC_ARCH
987     gotoblas_dynamic_init();
988 #endif
989
990 #if defined(SMP) && defined(OS_LINUX) && !defined(NO_AFFINITY)
991     gotoblas_affinity_init();
992 #endif
993
994 #ifdef SMP
995     if (!blas_num_threads) blas_cpu_number = blas_get_cpu_number();
996 #endif
997
998 #if defined(ARCH_X86) || defined(ARCH_X86_64) || defined(ARCH_IA64) || defined(ARCH_MIPS64) || defined(ARCH_ARM64)
999 #ifndef DYNAMIC_ARCH
1000     blas_set_parameter();
1001 #endif
1002 #endif
1003
1004     memory_initialized = 1;
1005
1006   }
1007   UNLOCK_COMMAND(&alloc_lock);
1008
1009 #ifdef DEBUG
1010   printf("Alloc Start ...\n");
1011 #endif
1012
1013 #if defined(WHEREAMI) && !defined(USE_OPENMP)
1014
1015   mypos = WhereAmI();
1016
1017   position = mypos;
1018   while (position > NUM_BUFFERS) position >>= 1;
1019
1020   do {
1021     if (!memory[position].used && (memory[position].pos == mypos)) {
1022
1023       blas_lock(&memory[position].lock);
1024
1025       if (!memory[position].used) goto allocation;
1026
1027       blas_unlock(&memory[position].lock);
1028     }
1029
1030     position ++;
1031
1032   } while (position < NUM_BUFFERS);
1033
1034
1035 #endif
1036
1037   position = 0;
1038
1039   do {
1040 /*    if (!memory[position].used) { */
1041
1042       blas_lock(&memory[position].lock);
1043
1044       if (!memory[position].used) goto allocation;
1045
1046       blas_unlock(&memory[position].lock);
1047 /*    } */
1048
1049     position ++;
1050
1051   } while (position < NUM_BUFFERS);
1052
1053   goto error;
1054
1055   allocation :
1056
1057 #ifdef DEBUG
1058   printf("  Position -> %d\n", position);
1059 #endif
1060
1061   memory[position].used = 1;
1062
1063   blas_unlock(&memory[position].lock);
1064
1065   if (!memory[position].addr) {
1066     do {
1067 #ifdef DEBUG
1068       printf("Allocation Start : %lx\n", base_address);
1069 #endif
1070
1071       map_address = (void *)-1;
1072
1073       func = &memoryalloc[0];
1074
1075       while ((func != NULL) && (map_address == (void *) -1)) {
1076
1077         map_address = (*func)((void *)base_address);
1078
1079 #ifdef ALLOC_DEVICEDRIVER
1080         if ((*func ==  alloc_devicedirver) && (map_address == (void *)-1)) {
1081             fprintf(stderr, "OpenBLAS Warning ... Physically contigous allocation was failed.\n");
1082         }
1083 #endif
1084
1085 #ifdef ALLOC_HUGETLBFILE
1086         if ((*func == alloc_hugetlbfile) && (map_address == (void *)-1)) {
1087 #ifndef OS_WINDOWS
1088             fprintf(stderr, "OpenBLAS Warning ... HugeTLB(File) allocation was failed.\n");
1089 #endif
1090         }
1091 #endif
1092
1093 #if (defined ALLOC_SHM) && (defined OS_LINUX  || defined OS_AIX  || defined __sun__  || defined OS_WINDOWS)
1094         if ((*func == alloc_hugetlb) && (map_address != (void *)-1)) hugetlb_allocated = 1;
1095 #endif
1096
1097         func ++;
1098       }
1099
1100 #ifdef DEBUG
1101       printf("  Success -> %08lx\n", map_address);
1102 #endif
1103       if (((BLASLONG) map_address) == -1) base_address = 0UL;
1104
1105       if (base_address) base_address += BUFFER_SIZE + FIXED_PAGESIZE;
1106
1107     } while ((BLASLONG)map_address == -1);
1108
1109     LOCK_COMMAND(&alloc_lock);
1110     memory[position].addr = map_address;
1111     UNLOCK_COMMAND(&alloc_lock);
1112
1113 #ifdef DEBUG
1114     printf("  Mapping Succeeded. %p(%d)\n", (void *)memory[position].addr, position);
1115 #endif
1116   }
1117
1118 #if defined(WHEREAMI) && !defined(USE_OPENMP)
1119
1120   if (memory[position].pos == -1) memory[position].pos = mypos;
1121
1122 #endif
1123
1124 #ifdef DYNAMIC_ARCH
1125
1126   if (memory_initialized == 1) {
1127
1128     LOCK_COMMAND(&alloc_lock);
1129
1130     if (memory_initialized == 1) {
1131
1132       if (!gotoblas) gotoblas_dynamic_init();
1133
1134       memory_initialized = 2;
1135     }
1136
1137     UNLOCK_COMMAND(&alloc_lock);
1138
1139   }
1140 #endif
1141
1142
1143 #ifdef DEBUG
1144   printf("Mapped   : %p  %3d\n\n",
1145           (void *)memory[position].addr, position);
1146 #endif
1147
1148   return (void *)memory[position].addr;
1149
1150  error:
1151   printf("BLAS : Program is Terminated. Because you tried to allocate too many memory regions.\n");
1152
1153   return NULL;
1154 }
1155
1156 void blas_memory_free(void *free_area){
1157
1158   int position;
1159
1160 #ifdef DEBUG
1161   printf("Unmapped Start : %p ...\n", free_area);
1162 #endif
1163
1164   position = 0;
1165   LOCK_COMMAND(&alloc_lock);
1166
1167   while ((memory[position].addr != free_area)
1168          && (position < NUM_BUFFERS)) position++;
1169
1170   if (memory[position].addr != free_area) goto error;
1171
1172 #ifdef DEBUG
1173   printf("  Position : %d\n", position);
1174 #endif
1175
1176   // arm: ensure all writes are finished before other thread takes this memory
1177   WMB;
1178
1179   memory[position].used = 0;
1180   UNLOCK_COMMAND(&alloc_lock);
1181
1182 #ifdef DEBUG
1183   printf("Unmap Succeeded.\n\n");
1184 #endif
1185
1186   return;
1187
1188  error:
1189   printf("BLAS : Bad memory unallocation! : %4d  %p\n", position,  free_area);
1190
1191 #ifdef DEBUG
1192   for (position = 0; position < NUM_BUFFERS; position++)
1193     printf("%4ld  %p : %d\n", position, memory[position].addr, memory[position].used);
1194 #endif
1195   UNLOCK_COMMAND(&alloc_lock);
1196
1197   return;
1198 }
1199
1200 void *blas_memory_alloc_nolock(int unused) {
1201   void *map_address;
1202   map_address = (void *)malloc(BUFFER_SIZE + FIXED_PAGESIZE);
1203   return map_address;
1204 }
1205
1206 void blas_memory_free_nolock(void * map_address) {
1207   free(map_address);
1208 }
1209
1210 void blas_shutdown(void){
1211
1212   int pos;
1213
1214 #ifdef SMP
1215   BLASFUNC(blas_thread_shutdown)();
1216 #endif
1217
1218   LOCK_COMMAND(&alloc_lock);
1219
1220   for (pos = 0; pos < release_pos; pos ++) {
1221     release_info[pos].func(&release_info[pos]);
1222   }
1223
1224 #ifdef SEEK_ADDRESS
1225   base_address      = 0UL;
1226 #else
1227   base_address      = BASE_ADDRESS;
1228 #endif
1229
1230   for (pos = 0; pos < NUM_BUFFERS; pos ++){
1231     memory[pos].addr   = (void *)0;
1232     memory[pos].used   = 0;
1233 #if defined(WHEREAMI) && !defined(USE_OPENMP)
1234     memory[pos].pos    = -1;
1235 #endif
1236     memory[pos].lock   = 0;
1237   }
1238
1239   UNLOCK_COMMAND(&alloc_lock);
1240
1241   return;
1242 }
1243
1244 #if defined(OS_LINUX) && !defined(NO_WARMUP)
1245
1246 #ifdef SMP
1247 #if   defined(USE_PTHREAD_LOCK)
1248 static pthread_mutex_t    init_lock = PTHREAD_MUTEX_INITIALIZER;
1249 #elif defined(USE_PTHREAD_SPINLOCK)
1250 static pthread_spinlock_t init_lock = 0;
1251 #else
1252 static BLASULONG   init_lock = 0UL;
1253 #endif
1254 #endif
1255
1256 static void _touch_memory(blas_arg_t *arg, BLASLONG *range_m, BLASLONG *range_n,
1257                           void *sa, void *sb, BLASLONG pos) {
1258
1259 #if !defined(ARCH_POWER) && !defined(ARCH_SPARC)
1260
1261   size_t size;
1262   BLASULONG buffer;
1263
1264   size   = BUFFER_SIZE - PAGESIZE;
1265   buffer = (BLASULONG)sa + GEMM_OFFSET_A;
1266
1267 #if defined(OS_LINUX) && !defined(NO_WARMUP)
1268     if (hot_alloc != 2) {
1269 #endif
1270
1271 #ifdef SMP
1272   LOCK_COMMAND(&init_lock);
1273 #endif
1274
1275   while (size > 0) {
1276     *(int *)buffer = size;
1277     buffer  += PAGESIZE;
1278     size    -= PAGESIZE;
1279   }
1280
1281 #ifdef SMP
1282   UNLOCK_COMMAND(&init_lock);
1283 #endif
1284
1285   size = MIN((BUFFER_SIZE - PAGESIZE), L2_SIZE);
1286   buffer = (BLASULONG)sa + GEMM_OFFSET_A;
1287
1288   while (size > 0) {
1289     *(int *)buffer = size;
1290     buffer  += 64;
1291     size    -= 64;
1292   }
1293
1294 #if defined(OS_LINUX) && !defined(NO_WARMUP)
1295     }
1296 #endif
1297
1298 #endif
1299 }
1300
1301 #ifdef SMP
1302
1303 static void _init_thread_memory(void *buffer) {
1304
1305   blas_queue_t queue[MAX_CPU_NUMBER];
1306   int num_cpu;
1307
1308   for (num_cpu = 0; num_cpu < blas_num_threads; num_cpu++) {
1309
1310     blas_queue_init(&queue[num_cpu]);
1311     queue[num_cpu].mode    = BLAS_DOUBLE | BLAS_REAL;
1312     queue[num_cpu].routine = &_touch_memory;
1313     queue[num_cpu].args    = NULL;
1314     queue[num_cpu].next    = &queue[num_cpu + 1];
1315   }
1316
1317   queue[num_cpu - 1].next = NULL;
1318   queue[0].sa = buffer;
1319
1320   exec_blas(num_cpu, queue);
1321
1322 }
1323 #endif
1324
1325 static void gotoblas_memory_init(void) {
1326
1327   void *buffer;
1328
1329   hot_alloc = 1;
1330
1331   buffer = (void *)blas_memory_alloc(0);
1332
1333 #ifdef SMP
1334   if (blas_cpu_number == 0) blas_get_cpu_number();
1335 #ifdef SMP_SERVER
1336   if (blas_server_avail == 0) blas_thread_init();
1337 #endif
1338
1339   _init_thread_memory((void *)((BLASULONG)buffer + GEMM_OFFSET_A));
1340
1341 #else
1342
1343   _touch_memory(NULL, NULL, NULL, (void *)((BLASULONG)buffer + GEMM_OFFSET_A), NULL, 0);
1344
1345 #endif
1346
1347   blas_memory_free(buffer);
1348 }
1349 #endif
1350
1351 /* Initialization for all function; this function should be called before main */
1352
1353 static int gotoblas_initialized = 0;
1354 extern void openblas_read_env();
1355
1356 void CONSTRUCTOR gotoblas_init(void) {
1357
1358   if (gotoblas_initialized) return;
1359
1360 #ifdef SMP
1361   openblas_fork_handler();
1362 #endif
1363
1364   openblas_read_env();
1365
1366 #ifdef PROFILE
1367    moncontrol (0);
1368 #endif
1369
1370 #ifdef DYNAMIC_ARCH
1371    gotoblas_dynamic_init();
1372 #endif
1373
1374 #if defined(SMP) && defined(OS_LINUX) && !defined(NO_AFFINITY)
1375    gotoblas_affinity_init();
1376 #endif
1377
1378 #if defined(OS_LINUX) && !defined(NO_WARMUP)
1379    gotoblas_memory_init();
1380 #endif
1381
1382 //#if defined(OS_LINUX)
1383 #if 0
1384    struct rlimit curlimit;
1385    if ( getrlimit(RLIMIT_STACK, &curlimit ) == 0 )
1386    {
1387         if ( curlimit.rlim_cur != curlimit.rlim_max )
1388         {
1389                 curlimit.rlim_cur = curlimit.rlim_max;
1390                 setrlimit(RLIMIT_STACK, &curlimit);
1391         }
1392    }
1393 #endif
1394
1395 #ifdef SMP
1396   if (blas_cpu_number == 0) blas_get_cpu_number();
1397 #ifdef SMP_SERVER
1398   if (blas_server_avail == 0) blas_thread_init();
1399 #endif
1400 #endif
1401
1402 #ifdef FUNCTION_PROFILE
1403    gotoblas_profile_init();
1404 #endif
1405
1406    gotoblas_initialized = 1;
1407
1408 #ifdef PROFILE
1409    moncontrol (1);
1410 #endif
1411
1412 }
1413
1414 void DESTRUCTOR gotoblas_quit(void) {
1415
1416   if (gotoblas_initialized == 0) return;
1417
1418   blas_shutdown();
1419
1420 #ifdef PROFILE
1421    moncontrol (0);
1422 #endif
1423
1424 #ifdef FUNCTION_PROFILE
1425    gotoblas_profile_quit();
1426 #endif
1427
1428 #if defined(SMP) && defined(OS_LINUX) && !defined(NO_AFFINITY)
1429    gotoblas_affinity_quit();
1430 #endif
1431
1432 #ifdef DYNAMIC_ARCH
1433    gotoblas_dynamic_quit();
1434 #endif
1435
1436    gotoblas_initialized = 0;
1437
1438 #ifdef PROFILE
1439    moncontrol (1);
1440 #endif
1441 }
1442
1443 #if defined(_MSC_VER) && !defined(__clang__)
1444 BOOL APIENTRY DllMain(HMODULE hModule, DWORD  ul_reason_for_call, LPVOID lpReserved)
1445 {
1446   switch (ul_reason_for_call)
1447   {
1448     case DLL_PROCESS_ATTACH:
1449       gotoblas_init();
1450       break;
1451     case DLL_THREAD_ATTACH:
1452       break;
1453     case DLL_THREAD_DETACH:
1454       break;
1455     case DLL_PROCESS_DETACH:
1456       gotoblas_quit();
1457       break;
1458     default:
1459       break;
1460   }
1461   return TRUE;
1462 }
1463
1464 /*
1465   This is to allow static linking.
1466   Code adapted from Google performance tools:
1467   https://gperftools.googlecode.com/git-history/perftools-1.0/src/windows/port.cc
1468   Reference:
1469   https://sourceware.org/ml/pthreads-win32/2008/msg00028.html
1470   http://ci.boost.org/svn-trac/browser/trunk/libs/thread/src/win32/tss_pe.cpp
1471 */
1472 static int on_process_term(void)
1473 {
1474         gotoblas_quit();
1475         return 0;
1476 }
1477 #ifdef _WIN64
1478 #pragma comment(linker, "/INCLUDE:_tls_used")
1479 #else
1480 #pragma comment(linker, "/INCLUDE:__tls_used")
1481 #endif
1482 #pragma data_seg(push, old_seg)
1483 #pragma data_seg(".CRT$XLB")
1484 static void (APIENTRY *dll_callback)(HINSTANCE h, DWORD ul_reason_for_call, PVOID pv) = DllMain;
1485 #pragma data_seg(".CRT$XTU")
1486 static int(*p_process_term)(void) = on_process_term;
1487 #pragma data_seg(pop, old_seg)
1488 #endif
1489
1490 #if (defined(C_PGI) || (!defined(C_SUN) && defined(F_INTERFACE_SUN))) && (defined(ARCH_X86) || defined(ARCH_X86_64))
1491 /* Don't call me; this is just work around for PGI / Sun bug */
1492 void gotoblas_dummy_for_PGI(void) {
1493
1494   gotoblas_init();
1495   gotoblas_quit();
1496
1497 #if 0
1498   asm ("\t.section\t.ctors,\"aw\",@progbits; .align 8; .quad gotoblas_init; .section .text");
1499   asm ("\t.section\t.dtors,\"aw\",@progbits; .align 8; .quad gotoblas_quit; .section .text");
1500 #else
1501   asm (".section .init,\"ax\"; call gotoblas_init@PLT; .section .text");
1502   asm (".section .fini,\"ax\"; call gotoblas_quit@PLT; .section .text");
1503 #endif
1504 }
1505 #endif