1 /*
2  * Support file for AMDGCN in newlib.
3  * Copyright (c) 2017 Mentor Graphics.
4  *
5  * The authors hereby grant permission to use, copy, modify, distribute,
6  * and license this software and its documentation for any purpose, provided
7  * that existing copyright notices are retained in all copies and that this
8  * notice is included verbatim in any distributions. No written agreement,
9  * license, or royalty fee is required for any of the authorized uses.
10  * Modifications to this software may be copyrighted by their authors
11  * and need not follow the licensing terms described here, provided that
12  * the new terms are clearly indicated on the first page of each file where
13  * they apply.
14  */
15 
16 #include <picolibc.h>
17 
18 #include <stdlib.h>
19 #include <stdint.h>
20 #include <reent.h>
21 
22 /* _sbrk_r expects us to use the real errno, not the reentrant one.  */
23 #include <errno.h>
24 #undef errno
25 extern int errno;
26 
27 /* The runtime passes in heap space like this.  */
28 struct heap {
29   int64_t size;
30   char data[0];
31 };
32 
33 static char *__heap_ptr = (char*)-1;
34 static char *__heap_end = (char*)-1;
35 static int __heap_lock = 0;
36 static void *__heap_lock_id = NULL;
37 static int __heap_lock_cnt = 0;
38 
39 void *
sbrk(ptrdiff_t nbytes)40 sbrk (ptrdiff_t nbytes)
41 {
42   if (__heap_ptr == (char *)-1)
43     {
44       /* Find the heap from kernargs.  */
45       char *kernargs;
46 #if defined(__has_builtin) && __has_builtin(__builtin_gcn_kernarg_ptr)
47       kernargs = __builtin_gcn_kernarg_ptr ();
48 #else
49       /* The kernargs pointer is in s[8:9].
50 	 This will break if the enable_sgpr_* flags are ever changed.  */
51       __asm__ ("s_mov_b64 %0, s[8:9]" : "=Sg"(kernargs));
52 #endif
53 
54       /* The heap data is at kernargs[3].  */
55       struct heap *heap = *(struct heap **)(kernargs + 24);
56 
57       __heap_ptr = heap->data;
58       __heap_end = __heap_ptr + heap->size;
59     }
60 
61   if ((__heap_ptr + nbytes) >= __heap_end)
62     {
63       errno = ENOMEM;
64       return (void*)-1;
65     }
66 
67   char *base = __heap_ptr;
68   __heap_ptr += nbytes;
69 
70   return base;
71 }
72 
73 void
__malloc_lock(struct _reent * reent)74 __malloc_lock (struct _reent *reent)
75 {
76   void *id = reent;
77 
78   if (id == __heap_lock_id)
79     {
80       if (__heap_lock_cnt < 1)
81 	abort ();
82       ++__heap_lock_cnt;
83       return;
84     }
85 
86   while (__sync_lock_test_and_set (&__heap_lock, 1))
87     /* A sleep seems like it should allow the wavefront to yeild (maybe?)
88        Use the shortest possible sleep time of 1*64 cycles.  */
89     __asm__ volatile ("s_sleep\t1" ::: "memory");
90 
91   if (__heap_lock_id != NULL)
92     abort ();
93   if (__heap_lock_cnt != 0)
94     abort ();
95 
96   __heap_lock_cnt = 1;
97   __heap_lock_id = id;
98 }
99 
100 void
__malloc_unlock(struct _reent * reent)101 __malloc_unlock (struct _reent *reent)
102 {
103   void *id = reent;
104 
105   if (id != __heap_lock_id)
106     abort ();
107   if (__heap_lock_cnt < 1)
108     abort ();
109 
110   --__heap_lock_cnt;
111 
112   if (__heap_lock_cnt > 0)
113     return;
114 
115   __heap_lock_id = NULL;
116   __sync_lock_release (&__heap_lock);
117 }
118