1 /** 2 This module provide support for aligned memory. 3 */ 4 module gfm.core.memory; 5 6 import core.memory : GC; 7 import core.exception : onOutOfMemoryError; 8 9 import std.c.stdlib : malloc, free, realloc; 10 import std.conv : emplace; 11 import std.traits; 12 13 14 static if( __VERSION__ < 2066 ) private enum nogc = 1; 15 16 /// Returns: next pointer aligned with alignment bytes. 17 @nogc void* nextAlignedPointer(void* start, size_t alignment) pure nothrow 18 { 19 return cast(void*)nextMultipleOf(cast(size_t)(start), alignment); 20 } 21 22 /// Allocates an aligned memory chunk. 23 /// Functionally equivalent to Visual C++ _aligned_malloc. 24 @nogc void* alignedMalloc(size_t size, size_t alignment) nothrow 25 { 26 if (size == 0) 27 return null; 28 29 size_t request = requestedSize(size, alignment); 30 void* raw = malloc(request); 31 32 static if( __VERSION__ > 2067 ) // onOutOfMemoryError wasn't nothrow before July 2014 33 { 34 if (request > 0 && raw == null) // malloc(0) can validly return anything 35 onOutOfMemoryError(); 36 } 37 38 return storeRawPointerAndReturnAligned(raw, alignment); 39 } 40 41 /// Frees aligned memory allocated by alignedMalloc or alignedRealloc. 42 /// Functionally equivalent to Visual C++ _aligned_free. 43 @nogc void alignedFree(void* aligned) nothrow 44 { 45 // support for free(NULL) 46 if (aligned is null) 47 return; 48 49 void** rawLocation = cast(void**)(cast(char*)aligned - size_t.sizeof); 50 free(*rawLocation); 51 } 52 53 /// Reallocates an aligned memory chunk allocated by alignedMalloc or alignedRealloc. 54 /// Functionally equivalent to Visual C++ _aligned_realloc. 55 @nogc void* alignedRealloc(void* aligned, size_t size, size_t alignment) nothrow 56 { 57 if (aligned is null) 58 return alignedMalloc(size, alignment); 59 60 if (size == 0) 61 { 62 alignedFree(aligned); 63 return null; 64 } 65 66 void* raw = *cast(void**)(cast(char*)aligned - size_t.sizeof); 67 68 size_t request = requestedSize(size, alignment); 69 void* newRaw = realloc(raw, request); 70 71 static if( __VERSION__ > 2067 ) // onOutOfMemoryError wasn't nothrow before July 2014 72 { 73 if (request > 0 && newRaw == null) // realloc(0) can validly return anything 74 onOutOfMemoryError(); 75 } 76 77 // if newRaw is raw, nothing to do 78 if (raw is newRaw) 79 return aligned; 80 81 // else write raw at the new location 82 return storeRawPointerAndReturnAligned(newRaw, alignment); 83 } 84 85 private 86 { 87 // Returns number of bytes to actually allocate when asking 88 // for a particular alignement 89 @nogc size_t requestedSize(size_t askedSize, size_t alignment) pure nothrow 90 { 91 enum size_t pointerSize = size_t.sizeof; 92 return askedSize + alignment - 1 + pointerSize; 93 } 94 95 @nogc void* storeRawPointerAndReturnAligned(void* raw, size_t alignment) nothrow 96 { 97 enum size_t pointerSize = size_t.sizeof; 98 char* start = cast(char*)raw + pointerSize; 99 void* aligned = nextAlignedPointer(start, alignment); 100 void** rawLocation = cast(void**)(cast(char*)aligned - pointerSize); 101 *rawLocation = raw; 102 return aligned; 103 } 104 105 // Returns: x, multiple of powerOfTwo, so that x >= n. 106 @nogc size_t nextMultipleOf(size_t n, size_t powerOfTwo) pure nothrow 107 { 108 // check power-of-two 109 assert( (powerOfTwo != 0) && ((powerOfTwo & (powerOfTwo - 1)) == 0)); 110 111 size_t mask = ~(powerOfTwo - 1); 112 return (n + powerOfTwo - 1) & mask; 113 } 114 } 115 116 unittest 117 { 118 { 119 void* p = alignedMalloc(23, 16); 120 assert(p !is null); 121 assert(((cast(size_t)p) & 0xf) == 0); 122 123 alignedFree(p); 124 } 125 126 assert(alignedMalloc(0, 16) == null); 127 alignedFree(null); 128 129 { 130 void* p = alignedRealloc(null, 100, 16); 131 p = alignedRealloc(p, 200, 16); 132 p = alignedRealloc(p, 0, 16); 133 } 134 } 135 136 137 /// Allocates and construct a struct or class object. 138 /// Returns: Newly allocated object. 139 auto mallocEmplace(T, Args...)(Args args) 140 { 141 static if (is(T == class)) 142 immutable size_t allocSize = __traits(classInstanceSize, T); 143 else 144 immutable size_t allocSize = T.sizeof; 145 146 void* rawMemory = malloc(allocSize); 147 if (!rawMemory) 148 onOutOfMemoryError(); 149 150 static if (is(T == class)) 151 { 152 T obj = emplace!T(rawMemory[0 .. allocSize], args); 153 } 154 else 155 { 156 T* obj = cast(T*)rawMemory; 157 emplace!T(obj, args); 158 } 159 160 static if (hasIndirections!T) 161 GC.addRange(rawMemory, allocSize); 162 163 return obj; 164 } 165 166 /// Destroys and frees a class object created with $(D mallocEmplace). 167 void destroyFree(T)(T p) if (is(T == class)) 168 { 169 if (p !is null) 170 { 171 destroy(p); 172 173 static if (hasIndirections!T) 174 GC.removeRange(cast(void*)p); 175 176 free(cast(void*)p); 177 } 178 } 179 180 /// Destroys and frees a non-class object created with $(D mallocEmplace). 181 void destroyFree(T)(T* p) if (!is(T == class)) 182 { 183 if (p !is null) 184 { 185 destroy(p); 186 187 static if (hasIndirections!T) 188 GC.removeRange(cast(void*)p); 189 190 free(cast(void*)p); 191 } 192 } 193 194 unittest 195 { 196 class A 197 { 198 int _i; 199 this(int i) 200 { 201 _i = i; 202 } 203 } 204 205 struct B 206 { 207 int i; 208 } 209 210 void testMallocEmplace() 211 { 212 A a = mallocEmplace!A(4); 213 destroyFree(a); 214 215 B* b = mallocEmplace!B(5); 216 destroyFree(b); 217 } 218 219 testMallocEmplace(); 220 } 221 222 version( D_InlineAsm_X86 ) 223 { 224 version = AsmX86; 225 } 226 else version( D_InlineAsm_X86_64 ) 227 { 228 version = AsmX86; 229 } 230 231 /// Inserts a breakpoint instruction. useful to trigger the debugger. 232 void debugBreak() nothrow @nogc 233 { 234 version( AsmX86 ) 235 { 236 static if( __VERSION__ >= 2067 ) 237 { 238 mixin("asm nothrow @nogc { int 3; }"); 239 } 240 else 241 { 242 mixin("asm { int 3; }"); 243 } 244 } 245 else 246 { 247 // TODO: implement debugBreak() for GDC 248 } 249 } 250 251 auto assumeNoGC(T) (T t) if (isFunctionPointer!T || isDelegate!T) 252 { 253 enum attrs = functionAttributes!T | FunctionAttribute.nogc; 254 return cast(SetFunctionAttributes!(T, functionLinkage!T, attrs)) t; 255 } 256 257 auto assumeNothrowNoGC(T) (T t) if (isFunctionPointer!T || isDelegate!T) 258 { 259 enum attrs = functionAttributes!T | FunctionAttribute.nogc | FunctionAttribute.nothrow_; 260 return cast(SetFunctionAttributes!(T, functionLinkage!T, attrs)) t; 261 } 262 263 unittest 264 { 265 void funcThatDoesGC() 266 { 267 throw new Exception("hello!"); 268 } 269 270 void anotherFunction() nothrow @nogc 271 { 272 assumeNothrowNoGC( (){ funcThatDoesGC(); } )(); 273 } 274 275 void aThirdFunction() @nogc 276 { 277 assumeNoGC( () { funcThatDoesGC(); } )(); 278 } 279 }