]> git.ipfire.org Git - thirdparty/gcc.git/blame - gcc/d/dmd/root/rmem.d
[committed] Remove compromised sh test
[thirdparty/gcc.git] / gcc / d / dmd / root / rmem.d
CommitLineData
5fee5ec3
IB
1/**
2 * Allocate memory using `malloc` or the GC depending on the configuration.
3 *
5470a9b1 4 * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved
c43b5909
IB
5 * Authors: Walter Bright, https://www.digitalmars.com
6 * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
5fee5ec3
IB
7 * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/root/rmem.d, root/_rmem.d)
8 * Documentation: https://dlang.org/phobos/dmd_root_rmem.html
9 * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/root/rmem.d
10 */
11
12module dmd.root.rmem;
13
14import core.exception : onOutOfMemoryError;
15import core.stdc.stdio;
16import core.stdc.stdlib;
17import core.stdc.string;
18
19import core.memory : GC;
20
21extern (C++) struct Mem
22{
23 static char* xstrdup(const(char)* s) nothrow
24 {
25 if (isGCEnabled)
26 return s ? s[0 .. strlen(s) + 1].dup.ptr : null;
27
28 return s ? cast(char*)check(.strdup(s)) : null;
29 }
30
31 static void xfree(void* p) pure nothrow
32 {
33 if (isGCEnabled)
34 return GC.free(p);
35
36 pureFree(p);
37 }
38
39 static void* xmalloc(size_t size) pure nothrow
40 {
41 if (isGCEnabled)
42 return size ? GC.malloc(size) : null;
43
44 return size ? check(pureMalloc(size)) : null;
45 }
46
47 static void* xmalloc_noscan(size_t size) pure nothrow
48 {
49 if (isGCEnabled)
50 return size ? GC.malloc(size, GC.BlkAttr.NO_SCAN) : null;
51
52 return size ? check(pureMalloc(size)) : null;
53 }
54
55 static void* xcalloc(size_t size, size_t n) pure nothrow
56 {
57 if (isGCEnabled)
58 return size * n ? GC.calloc(size * n) : null;
59
60 return (size && n) ? check(pureCalloc(size, n)) : null;
61 }
62
63 static void* xcalloc_noscan(size_t size, size_t n) pure nothrow
64 {
65 if (isGCEnabled)
66 return size * n ? GC.calloc(size * n, GC.BlkAttr.NO_SCAN) : null;
67
68 return (size && n) ? check(pureCalloc(size, n)) : null;
69 }
70
71 static void* xrealloc(void* p, size_t size) pure nothrow
72 {
73 if (isGCEnabled)
74 return GC.realloc(p, size);
75
76 if (!size)
77 {
78 pureFree(p);
79 return null;
80 }
81
82 return check(pureRealloc(p, size));
83 }
84
85 static void* xrealloc_noscan(void* p, size_t size) pure nothrow
86 {
87 if (isGCEnabled)
88 return GC.realloc(p, size, GC.BlkAttr.NO_SCAN);
89
90 if (!size)
91 {
92 pureFree(p);
93 return null;
94 }
95
96 return check(pureRealloc(p, size));
97 }
98
99 static void* error() pure nothrow @nogc @safe
100 {
101 onOutOfMemoryError();
102 assert(0);
103 }
104
105 /**
106 * Check p for null. If it is, issue out of memory error
107 * and exit program.
108 * Params:
109 * p = pointer to check for null
110 * Returns:
111 * p if not null
112 */
d6679fa2 113 static void* check(void* p) pure nothrow @nogc @safe
5fee5ec3
IB
114 {
115 return p ? p : error();
116 }
117
118 __gshared bool _isGCEnabled = true;
119
120 // fake purity by making global variable immutable (_isGCEnabled only modified before startup)
121 enum _pIsGCEnabled = cast(immutable bool*) &_isGCEnabled;
122
123 static bool isGCEnabled() pure nothrow @nogc @safe
124 {
125 return *_pIsGCEnabled;
126 }
127
128 static void disableGC() nothrow @nogc
129 {
130 _isGCEnabled = false;
131 }
132
133 static void addRange(const(void)* p, size_t size) nothrow @nogc
134 {
135 if (isGCEnabled)
136 GC.addRange(p, size);
137 }
138
139 static void removeRange(const(void)* p) nothrow @nogc
140 {
141 if (isGCEnabled)
142 GC.removeRange(p);
143 }
144}
145
146extern (C++) const __gshared Mem mem;
147
148enum CHUNK_SIZE = (256 * 4096 - 64);
149
150__gshared size_t heapleft = 0;
151__gshared void* heapp;
152
153extern (D) void* allocmemoryNoFree(size_t m_size) nothrow @nogc
154{
155 // 16 byte alignment is better (and sometimes needed) for doubles
156 m_size = (m_size + 15) & ~15;
157
158 // The layout of the code is selected so the most common case is straight through
159 if (m_size <= heapleft)
160 {
161 L1:
162 heapleft -= m_size;
163 auto p = heapp;
164 heapp = cast(void*)(cast(char*)heapp + m_size);
165 return p;
166 }
167
168 if (m_size > CHUNK_SIZE)
169 {
170 return Mem.check(malloc(m_size));
171 }
172
173 heapleft = CHUNK_SIZE;
174 heapp = Mem.check(malloc(CHUNK_SIZE));
175 goto L1;
176}
177
178extern (D) void* allocmemory(size_t m_size) nothrow
179{
180 if (mem.isGCEnabled)
181 return GC.malloc(m_size);
182
183 return allocmemoryNoFree(m_size);
184}
185
186version (DigitalMars)
187{
188 enum OVERRIDE_MEMALLOC = true;
189}
190else version (LDC)
191{
192 // Memory allocation functions gained weak linkage when the @weak attribute was introduced.
193 import ldc.attributes;
194 enum OVERRIDE_MEMALLOC = is(typeof(ldc.attributes.weak));
195}
196else version (GNU)
197{
198 version (IN_GCC)
199 enum OVERRIDE_MEMALLOC = false;
200 else
201 enum OVERRIDE_MEMALLOC = true;
202}
203else
204{
205 enum OVERRIDE_MEMALLOC = false;
206}
207
208static if (OVERRIDE_MEMALLOC)
209{
210 // Override the host druntime allocation functions in order to use the bump-
211 // pointer allocation scheme (`allocmemoryNoFree()` above) if the GC is disabled.
212 // That scheme is faster and comes with less memory overhead than using a
213 // disabled GC alone.
214
215 extern (C) void* _d_allocmemory(size_t m_size) nothrow
216 {
217 return allocmemory(m_size);
218 }
219
220 private void* allocClass(const ClassInfo ci) nothrow pure
221 {
222 alias BlkAttr = GC.BlkAttr;
223
224 assert(!(ci.m_flags & TypeInfo_Class.ClassFlags.isCOMclass));
225
226 BlkAttr attr = BlkAttr.NONE;
227 if (ci.m_flags & TypeInfo_Class.ClassFlags.hasDtor
228 && !(ci.m_flags & TypeInfo_Class.ClassFlags.isCPPclass))
229 attr |= BlkAttr.FINALIZE;
230 if (ci.m_flags & TypeInfo_Class.ClassFlags.noPointers)
231 attr |= BlkAttr.NO_SCAN;
232 return GC.malloc(ci.initializer.length, attr, ci);
233 }
234
235 extern (C) void* _d_newitemU(const TypeInfo ti) nothrow;
236
237 extern (C) Object _d_newclass(const ClassInfo ci) nothrow
238 {
239 const initializer = ci.initializer;
240
241 auto p = mem.isGCEnabled ? allocClass(ci) : allocmemoryNoFree(initializer.length);
242 memcpy(p, initializer.ptr, initializer.length);
243 return cast(Object) p;
244 }
245
246 version (LDC)
247 {
248 extern (C) Object _d_allocclass(const ClassInfo ci) nothrow
249 {
250 if (mem.isGCEnabled)
251 return cast(Object) allocClass(ci);
252
253 return cast(Object) allocmemoryNoFree(ci.initializer.length);
254 }
255 }
256
257 extern (C) void* _d_newitemT(TypeInfo ti) nothrow
258 {
259 auto p = mem.isGCEnabled ? _d_newitemU(ti) : allocmemoryNoFree(ti.tsize);
260 memset(p, 0, ti.tsize);
261 return p;
262 }
263
264 extern (C) void* _d_newitemiT(TypeInfo ti) nothrow
265 {
266 auto p = mem.isGCEnabled ? _d_newitemU(ti) : allocmemoryNoFree(ti.tsize);
267 const initializer = ti.initializer;
268 memcpy(p, initializer.ptr, initializer.length);
269 return p;
270 }
271
272 // TypeInfo.initializer for compilers older than 2.070
273 static if(!__traits(hasMember, TypeInfo, "initializer"))
274 private const(void[]) initializer(T : TypeInfo)(const T t)
275 nothrow pure @safe @nogc
276 {
277 return t.init;
278 }
279}
280
281extern (C) pure @nogc nothrow
282{
283 /**
284 * Pure variants of C's memory allocation functions `malloc`, `calloc`, and
285 * `realloc` and deallocation function `free`.
286 *
287 * UNIX 98 requires that errno be set to ENOMEM upon failure.
288 * https://linux.die.net/man/3/malloc
289 * However, this is irrelevant for DMD's purposes, and best practice
290 * protocol for using errno is to treat it as an `out` parameter, and not
291 * something with state that can be relied on across function calls.
292 * So, we'll ignore it.
293 *
294 * See_Also:
295 * $(LINK2 https://dlang.org/spec/function.html#pure-functions, D's rules for purity),
296 * which allow for memory allocation under specific circumstances.
297 */
298 pragma(mangle, "malloc") void* pureMalloc(size_t size) @trusted;
299
300 /// ditto
301 pragma(mangle, "calloc") void* pureCalloc(size_t nmemb, size_t size) @trusted;
302
303 /// ditto
304 pragma(mangle, "realloc") void* pureRealloc(void* ptr, size_t size) @system;
305
306 /// ditto
307 pragma(mangle, "free") void pureFree(void* ptr) @system;
308
309}
310
311/**
312Makes a null-terminated copy of the given string on newly allocated memory.
313The null-terminator won't be part of the returned string slice. It will be
314at position `n` where `n` is the length of the input string.
315
316Params:
317 s = string to copy
318
319Returns: A null-terminated copy of the input array.
320*/
321extern (D) char[] xarraydup(const(char)[] s) pure nothrow
322{
323 if (!s)
324 return null;
325
326 auto p = cast(char*)mem.xmalloc_noscan(s.length + 1);
327 char[] a = p[0 .. s.length];
328 a[] = s[0 .. s.length];
329 p[s.length] = 0; // preserve 0 terminator semantics
330 return a;
331}
332
333///
334pure nothrow unittest
335{
336 auto s1 = "foo";
337 auto s2 = s1.xarraydup;
338 s2[0] = 'b';
339 assert(s1 == "foo");
340 assert(s2 == "boo");
341 assert(*(s2.ptr + s2.length) == '\0');
342 string sEmpty;
343 assert(sEmpty.xarraydup is null);
344}
345
346/**
347Makes a copy of the given array on newly allocated memory.
348
349Params:
350 s = array to copy
351
352Returns: A copy of the input array.
353*/
354extern (D) T[] arraydup(T)(const scope T[] s) pure nothrow
355{
356 if (!s)
357 return null;
358
359 const dim = s.length;
360 auto p = (cast(T*)mem.xmalloc(T.sizeof * dim))[0 .. dim];
361 p[] = s;
362 return p;
363}
364
365///
366pure nothrow unittest
367{
368 auto s1 = [0, 1, 2];
369 auto s2 = s1.arraydup;
370 s2[0] = 4;
371 assert(s1 == [0, 1, 2]);
372 assert(s2 == [4, 1, 2]);
373 string sEmpty;
374 assert(sEmpty.arraydup is null);
375}