1 /// 2 module libwasm.rt.allocator; 3 4 nothrow: 5 import std.typecons : Flag, Yes, No; 6 import std.traits : isFloatingPoint, isIntegral, isSigned, isNumeric; 7 8 import memutils.utils; 9 import memutils.scoped; 10 import memutils.pool; 11 import memutils.freelist; 12 import memutils.hashmap; 13 import libwasm.rt.allocator; 14 import libwasm.types; 15 16 pragma(LDC_intrinsic, "llvm.maxnum.f#") 17 T fmax(T)(in T vala, in T valb) if (isFloatingPoint!T); 18 /++ 19 Iterates the passed arguments and returns the minimum value. 20 Params: args = The values to select the minimum from. At least two arguments 21 must be passed, and they must be comparable with `<`. 22 Returns: The minimum of the passed-in values. 23 +/ 24 auto max(T...)(T args) if (T.length >= 2) 25 { 26 //Get "a" 27 static if (T.length <= 2) 28 alias a = args[0]; 29 else 30 auto a = max(args[0 .. ($ + 1) / 2]); 31 alias T0 = typeof(a); 32 33 //Get "b" 34 static if (T.length <= 3) 35 alias b = args[$ - 1]; 36 else 37 auto b = max(args[($ + 1) / 2 .. $]); 38 alias T1 = typeof(b); 39 40 static assert(is(typeof(a < b)), "Invalid arguments: Cannot compare types " ~ T0.stringof ~ " and " ~ T1.stringof ~ "."); 41 42 static if ((isFloatingPoint!T0 && isNumeric!T1) || (isFloatingPoint!T1 && isNumeric!T0)) 43 { 44 return fmax(a, b); 45 } 46 else 47 { 48 static if (isIntegral!T0 && isIntegral!T1) 49 static assert(isSigned!T0 == isSigned!T1, 50 "mir.utility.max is not defined for signed + unsigned pairs because of security reasons." 51 ~ "Please unify type or use a Phobos analog."); 52 //Do the "max" proper with a and b 53 return a > b ? a : b; 54 } 55 } 56 /** 57 The alignment that is guaranteed to accommodate any D object allocation on the 58 current platform. 59 */ 60 enum uint platformAlignment = max(double.alignof, real.alignof); 61 62 /** 63 Check whether a number is an integer power of two. 64 65 Note that only positive numbers can be integer powers of two. This 66 function always return `false` if `x` is negative or zero. 67 68 Params: 69 x = the number to test 70 71 Returns: 72 `true` if `x` is an integer power of two. 73 */ 74 bool isPowerOf2(X)(const X x) pure @safe nothrow @nogc if (isNumeric!X) 75 { 76 static if (isFloatingPoint!X) 77 { 78 import std.math : frexp; 79 80 int exp; 81 const X sig = frexp(x, exp); 82 83 return (exp != int.min) && (sig is cast(X) 0.5L); 84 } 85 else 86 { 87 static if (isSigned!X) 88 { 89 auto y = cast(typeof(x + 0)) x; 90 return y > 0 && !(y & (y - 1)); 91 } 92 else 93 { 94 auto y = cast(typeof(x + 0u)) x; 95 return (y & -y) > (y - 1); 96 } 97 } 98 } 99 /** 100 Returns `n` rounded up to a multiple of alignment, which must be a power of 2. 101 */ 102 @safe @nogc nothrow pure 103 size_t roundUpToAlignment()(size_t n, uint alignment) 104 { 105 assert(alignment.isPowerOf2); 106 immutable uint slack = cast(uint) n & (alignment - 1); 107 const result = slack 108 ? n + alignment - slack : n; 109 assert(result >= n); 110 return result; 111 } 112 113 version (WebAssembly) 114 { 115 116 private __gshared void* begin, current, end; 117 struct WasmAllocator 118 { 119 import libwasm.intrinsics; 120 121 nothrow: 122 static: 123 124 enum wasmPageSize = 64 * 1024; 125 enum uint alignment = platformAlignment; 126 @trusted void init(uint heap_base) 127 { 128 begin = cast(void*)(heap_base.roundUpToAlignment(alignment)); 129 current = begin; 130 end = cast(void*)(wasmMemorySize * wasmPageSize); 131 } 132 133 void[] allocate(size_t n) 134 { 135 const rounded = n.roundUpToAlignment(alignment); 136 if (current + rounded > end) 137 grow(1 + rounded / wasmPageSize); 138 void* mem = current; 139 current += rounded; 140 return mem[0 .. rounded]; 141 } 142 143 bool deallocate(void[] data) 144 { 145 // we rely on memutils to deallocate stuff 146 return true; 147 } 148 149 private void grow(size_t pages) 150 { 151 auto currentPages = wasmMemoryGrow(0); 152 current = cast(void*)(currentPages * wasmPageSize); 153 wasmMemoryGrow(pages); 154 end = cast(void*)((currentPages + pages) * wasmPageSize); 155 } 156 } 157 158 } 159 /** 160 Returns `true` if `ptr` is aligned at `alignment`. 161 */ 162 @nogc nothrow pure bool alignedAt(T)(T* ptr, uint alignment) 163 { 164 return cast(size_t) ptr % alignment == 0; 165 } 166 167 // used in libwasm Lodash 168 @trusted extern (C) 169 { 170 void[] FL_allocate(size_t n) 171 { 172 return ThreadMemAllocator.allocate(n); 173 } 174 175 void[] FL_reallocate(void[] mem, size_t n) 176 { 177 return ThreadMemAllocator.reallocate(mem, n); 178 } 179 180 void FL_deallocate(void[] mem) 181 { 182 ThreadMemAllocator.deallocate(mem); 183 } 184 } 185 186 @trusted extern (C) export @assumeUsed ubyte* allocString(uint bytes) 187 { 188 import memutils.scoped; 189 190 void[] ret; 191 if (!PoolStack.empty) ret = PoolStack.top.alloc(bytes + 1); 192 else ret = FL_allocate(bytes+1); 193 *cast(ubyte*)(ret.ptr + ret.length - 1) = '\0'; // always return zero-ended. wrong size can be sent because this is not freed 194 return cast(ubyte*) ret.ptr; 195 } 196 197 // implement closures with execution context or PoolStack.top 198 // references will be held by a ManagedPool 199 // maybe in some outer scope 200 extern (C) export void* _d_allocmemory(size_t sz) 201 { 202 import memutils.scoped; 203 if (!PoolStack.empty) 204 return PoolStack.top.alloc(sz).ptr; 205 else 206 return FL_allocate(sz).ptr; 207 208 209 } 210 211 struct ThreadMemAllocator 212 { 213 static: 214 nothrow: 215 @trusted: 216 void[] allocate(size_t n) 217 { 218 return ThreadMem.alloc!(void[])(n); 219 } 220 221 void[] reallocate(void[] data, size_t n, bool free_mem = true) 222 { 223 if (data.length == 0) 224 return allocate(n); 225 return ThreadMem.realloc(data, n, free_mem); 226 } 227 228 bool deallocate(void[] data, bool free_mem = true) 229 { 230 ThreadMem.free(data, free_mem); 231 return true; 232 } 233 } 234 235 struct PoolStackAllocator 236 { 237 static: 238 nothrow: 239 @trusted: 240 void[] allocate(size_t n) 241 { 242 return PoolStack.top.alloc(n); 243 } 244 245 void[] reallocate(void[] data, size_t n, bool free_mem = true) 246 { 247 if (data.length == 0) 248 return allocate(n); 249 return PoolStack.top.realloc(data, n, free_mem); 250 } 251 252 bool deallocate(void[] data, bool free_mem = true) 253 { 254 PoolStack.top.free(data, free_mem); 255 return true; 256 } 257 }