Merge pull request #832 from xplicit/webperf1
[mono.git] / mono / metadata / gc-memfuncs.c
1 /*
2  * test-sgen-qsort.c: Our own bzero/memmove.
3  *
4  * Copyright (C) 2013 Xamarin Inc
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Library General Public
8  * License 2.0 as published by the Free Software Foundation;
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Library General Public License for more details.
14  *
15  * You should have received a copy of the GNU Library General Public
16  * License 2.0 along with this library; if not, write to the Free
17  * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18  */
19
20 /*
21  * SGen cannot deal with invalid pointers on the heap or in registered roots.  Sometimes we
22  * need to copy or zero out memory in code that might be interrupted by collections.  To
23  * guarantee that those operations will not result in invalid pointers, we must do it
24  * word-atomically.
25  *
26  * libc's bzero() and memcpy()/memmove() functions do not guarantee word-atomicity, even in
27  * cases where one would assume so.  For instance, some implementations (like Darwin's on
28  * x86) have variants of memcpy() using vector instructions.  Those may copy bytewise for
29  * the region preceding the first vector-aligned address.  That region could be
30  * word-aligned, but it would still be copied byte-wise.
31  *
32  * All our memory writes here are to "volatile" locations.  This is so that C compilers
33  * don't "optimize" our code back to calls to bzero()/memmove().  LLVM, specifically, will
34  * do that.
35  */
36
37 #include <config.h>
38
39 #include "metadata/gc-internal.h"
40
41 #define ptr_mask ((sizeof (void*) - 1))
42 #define _toi(ptr) ((size_t)ptr)
43 #define unaligned_bytes(ptr) (_toi(ptr) & ptr_mask)
44 #define align_down(ptr) ((void*)(_toi(ptr) & ~ptr_mask))
45 #define align_up(ptr) ((void*) ((_toi(ptr) + ptr_mask) & ~ptr_mask))
46 #if SIZEOF_VOID_P == 4
47 #define bytes_to_words(n)       ((size_t)(n) >> 2)
48 #elif SIZEOF_VOID_P == 8
49 #define bytes_to_words(n)       ((size_t)(n) >> 3)
50 #else
51 #error We only support 32 and 64 bit architectures.
52 #endif
53
54 #define BZERO_WORDS(dest,words) do {                    \
55                 void * volatile *__d = (void* volatile*)(dest);         \
56                 int __n = (words);                      \
57                 int __i;                                \
58                 for (__i = 0; __i < __n; ++__i)         \
59                         __d [__i] = NULL;               \
60         } while (0)
61
62 /**
63  * mono_gc_bzero:
64  * @dest: address to start to clear
65  * @size: size of the region to clear
66  *
67  * Zero @size bytes starting at @dest.
68  *
69  * Use this to zero memory that can hold managed pointers.
70  *
71  * FIXME borrow faster code from some BSD libc or bionic
72  */
73 void
74 mono_gc_bzero (void *dest, size_t size)
75 {
76         volatile char *d = (char*)dest;
77         size_t tail_bytes, word_bytes;
78
79         /*
80         If we're copying less than a word, just use memset.
81
82         We cannot bail out early if both are aligned because some implementations
83         use byte copying for sizes smaller than 16. OSX, on this case.
84         */
85         if (size < sizeof(void*)) {
86                 memset (dest, 0, size);
87                 return;
88         }
89
90         /*align to word boundary */
91         while (unaligned_bytes (d) && size) {
92                 *d++ = 0;
93                 --size;
94         }
95
96         /* copy all words with memmove */
97         word_bytes = (size_t)align_down (size);
98         switch (word_bytes) {
99         case sizeof (void*) * 1:
100                 BZERO_WORDS (d, 1);
101                 break;
102         case sizeof (void*) * 2:
103                 BZERO_WORDS (d, 2);
104                 break;
105         case sizeof (void*) * 3:
106                 BZERO_WORDS (d, 3);
107                 break;
108         case sizeof (void*) * 4:
109                 BZERO_WORDS (d, 4);
110                 break;
111         default:
112                 BZERO_WORDS (d, bytes_to_words (word_bytes));
113         }
114
115         tail_bytes = unaligned_bytes (size);
116         if (tail_bytes) {
117                 d += word_bytes;
118                 do {
119                         *d++ = 0;
120                 } while (--tail_bytes);
121         }
122 }
123
124 #define MEMMOVE_WORDS_UPWARD(dest,src,words) do {       \
125                 void * volatile *__d = (void* volatile*)(dest);         \
126                 void **__s = (void**)(src);             \
127                 int __n = (int)(words);                 \
128                 int __i;                                \
129                 for (__i = 0; __i < __n; ++__i)         \
130                         __d [__i] = __s [__i];          \
131         } while (0)
132
133 #define MEMMOVE_WORDS_DOWNWARD(dest,src,words) do {     \
134                 void * volatile *__d = (void* volatile*)(dest);         \
135                 void **__s = (void**)(src);             \
136                 int __n = (int)(words);                 \
137                 int __i;                                \
138                 for (__i = __n - 1; __i >= 0; --__i)    \
139                         __d [__i] = __s [__i];          \
140         } while (0)
141
142 /**
143  * mono_gc_memmove:
144  * @dest: destination of the move
145  * @src: source
146  * @size: size of the block to move
147  *
148  * Move @size bytes from @src to @dest.
149  * size MUST be a multiple of sizeof (gpointer)
150  *
151  */
152 void
153 mono_gc_memmove (void *dest, const void *src, size_t size)
154 {
155         /*
156         If we're copying less than a word we don't need to worry about word tearing
157         so we bailout to memmove early.
158         */
159         if (size < sizeof(void*)) {
160                 memmove (dest, src, size);
161                 return;
162         }
163
164         /*
165          * A bit of explanation on why we align only dest before doing word copies.
166          * Pointers to managed objects must always be stored in word aligned addresses, so
167          * even if dest is misaligned, src will be by the same amount - this ensure proper atomicity of reads.
168          *
169          * We don't need to case when source and destination have different alignments since we only do word stores
170          * using memmove, which must handle it.
171          */
172         if (dest > src && ((size_t)((char*)dest - (char*)src) < size)) { /*backward copy*/
173                 volatile char *p = (char*)dest + size;
174                         char *s = (char*)src + size;
175                         char *start = (char*)dest;
176                         char *align_end = MAX((char*)dest, (char*)align_down (p));
177                         char *word_start;
178                         size_t bytes_to_memmove;
179
180                         while (p > align_end)
181                                 *--p = *--s;
182
183                         word_start = align_up (start);
184                         bytes_to_memmove = p - word_start;
185                         p -= bytes_to_memmove;
186                         s -= bytes_to_memmove;
187                         MEMMOVE_WORDS_DOWNWARD (p, s, bytes_to_words (bytes_to_memmove));
188
189                         while (p > start)
190                                 *--p = *--s;
191         } else {
192                 volatile char *d = (char*)dest;
193                 const char *s = (const char*)src;
194                 size_t tail_bytes;
195
196                 /*align to word boundary */
197                 while (unaligned_bytes (d)) {
198                         *d++ = *s++;
199                         --size;
200                 }
201
202                 /* copy all words with memmove */
203                 MEMMOVE_WORDS_UPWARD (d, s, bytes_to_words (align_down (size)));
204
205                 tail_bytes = unaligned_bytes (size);
206                 if (tail_bytes) {
207                         d += (size_t)align_down (size);
208                         s += (size_t)align_down (size);
209                         do {
210                                 *d++ = *s++;
211                         } while (--tail_bytes);
212                 }
213         }
214 }