1 /*************************************************************************************************** 2 * 3 * Helper functions that serve general purposes. 4 * 5 * Authors: 6 * $(LINK2 mailto:Marco.Leise@gmx.de, Marco Leise) 7 * 8 * Copyright: 9 * © 2017-2023 $(LINK2 mailto:Marco.Leise@gmx.de, Marco Leise), $(LINK2 mailto:etienne@cimons.com, Etienne Cimon) 10 * 11 * License: 12 * $(LINK2 https://mit-license.org/, GNU General Public License 3.0) 13 * 14 **************************************************************************************************/ 15 module fast.internal.helpers; 16 17 import std.traits; 18 import fast.internal.sysdef; 19 20 static void function(string) nothrow @safe logInfo; 21 static void function(string) nothrow @safe logError; 22 23 static enum isX86 = false; 24 static enum isAMD64 = false; 25 private enum 一META一PROGRAMMING一; 26 27 // 2.071 fixed visibility rules, so we need to roll our own staticIota. 28 static if (__VERSION__ >= 2071) 29 { 30 import std.meta : AliasSeq; 31 32 template staticIota(int beg, int end) 33 { 34 static if (beg + 1 >= end) 35 { 36 static if (beg >= end) 37 { 38 alias staticIota = AliasSeq!(); 39 } 40 else 41 { 42 alias staticIota = AliasSeq!(+beg); 43 } 44 } 45 else 46 { 47 enum mid = beg + (end - beg) / 2; 48 alias staticIota = AliasSeq!(staticIota!(beg, mid), staticIota!(mid, end)); 49 } 50 } 51 } 52 else 53 { 54 import std.typecons : staticIota; 55 } 56 57 /** 58 * For any integral type, returns the unsigned type of the same bit-width. 59 */ 60 template UnsignedOf(I) if (isIntegral!I) 61 { 62 static if (isUnsigned!I) 63 alias UnsignedOf = I; 64 else static if (is(I == long)) 65 alias UnsignedOf = ulong; 66 else static if (is(I == int)) 67 alias UnsignedOf = uint; 68 else static if (is(I == short)) 69 alias UnsignedOf = ushort; 70 else static if (is(I == byte)) 71 alias UnsignedOf = ubyte; 72 else 73 static assert(0, "Not implemented"); 74 } 75 76 /** 77 * Generates a mixin string for repeating code. It can be used to unroll variadic arguments. 78 * A format string is instantiated a certain number times with an incrementing parameter. 79 * The results are then concatenated using an optional joiner. 80 * 81 * Params: 82 * length = Number of elements you want to join. It is passed into format() as an incrementing number from [0 .. count$(RPAREN). 83 * fmt = The format string to apply on each instanciation. Use %1d$ to refer to the current index multiple times when necessary. 84 * joiner = Optional string that will be placed between instances. It could be a space or an arithmetic operation. 85 * 86 * Returns: 87 * The combined elements as a mixin string. 88 * 89 * See_Also: 90 * $(LINK2 http://forum.dlang.org/thread/vqfvihyezbmwcjkmpzin@forum.dlang.org, A simple way to do compile time loop unrolling) 91 */ 92 enum ctfeJoin(size_t length)(in string fmt, in string joiner = null) 93 { 94 import std.range : iota; 95 import std.algorithm : map; 96 97 // BUG: Cannot use, join(), as it "cannot access the nested function 'ctfeJoin'". 98 string result; 99 foreach (inst; map!(i => format(fmt, i))(iota(length))) 100 { 101 if (result && joiner) 102 result ~= joiner; 103 result ~= inst; 104 } 105 return result; 106 } 107 108 enum getUDA(alias sym, T)() 109 { 110 foreach (uda; __traits(getAttributes, sym)) 111 static if (is(typeof(uda) == T)) 112 return uda; 113 return T.init; 114 } 115 116 private enum 一BIT一OPERATIONS一; 117 118 static import core.bitop; 119 120 alias bsr = core.bitop.bsr; 121 alias bsf = core.bitop.bsf; 122 123 /******************************************************************************* 124 * 125 * Count leading zeroes. 126 * 127 * Params: 128 * u = the unsigned value to scan 129 * 130 * Returns: 131 * The number of leading zero bits before the first one bit. If `u` is `0`, 132 * the result is undefined. 133 * 134 **************************************/ 135 version (DigitalMars) 136 { 137 @safe @nogc pure nothrow U clz(U)(U u) 138 if (is(Unqual!U == uint) || is(Unqual!U == size_t)) 139 { 140 pragma(inline, true); 141 enum U max = 8 * U.sizeof - 1; 142 return max - bsr(u); 143 } 144 145 static if (isX86) 146 { 147 @safe @nogc pure nothrow uint clz(U)(U u) if (is(Unqual!U == ulong)) 148 { 149 pragma(inline, true); 150 uint hi = u >> 32; 151 return hi ? 31 - bsr(hi) : 63 - bsr(cast(uint) u); 152 } 153 } 154 } 155 else version (GNU) 156 { 157 import gcc.builtins; 158 159 alias clz = __builtin_clz; 160 static if (isX86) 161 { 162 @safe @nogc pure nothrow uint clz(ulong u) 163 { 164 uint hi = u >> 32; 165 return hi ? __builtin_clz(hi) : 32 + __builtin_clz(cast(uint) u); 166 } 167 } 168 else 169 alias clz = __builtin_clzl; 170 } 171 else version (LDC) 172 { 173 @safe @nogc pure nothrow U clz(U)(U u) 174 if (is(Unqual!U == uint) || is(Unqual!U == size_t)) 175 { 176 pragma(inline, true); 177 import ldc.intrinsics; 178 179 return llvm_ctlz(u, false); 180 } 181 182 static if (is(size_t == uint)) 183 { 184 @safe @nogc pure nothrow uint clz(U)(U u) if (is(Unqual!U == ulong)) 185 { 186 pragma(inline, true); 187 import ldc.intrinsics; 188 189 return cast(uint) llvm_ctlz(u, false); 190 } 191 } 192 } 193 static if (__VERSION__ < 2071) 194 { 195 // < 2.071 did not have 64-bit bsr/bsf on x86. 196 @safe @nogc pure nothrow uint bsr(U)(U u) if (is(Unqual!U == ulong)) 197 { 198 pragma(inline, true); 199 uint hi = u >> 32; 200 return hi ? bsr(hi) + 32 : bsr(cast(uint) u); 201 } 202 203 @safe @nogc pure nothrow uint bsf(U)(U u) if (is(Unqual!U == ulong)) 204 { 205 pragma(inline, true); 206 uint lo = cast(uint) u; 207 return lo ? bsf(lo) : 32 + bsf(u >> 32); 208 } 209 } 210 unittest 211 { 212 assert(clz(uint(0x01234567)) == 7); 213 assert(clz(ulong(0x0123456701234567)) == 7); 214 assert(clz(ulong(0x0000000001234567)) == 7 + 32); 215 assert(bsr(uint(0x01234567)) == 24); 216 assert(bsr(ulong(0x0123456701234567)) == 24 + 32); 217 assert(bsr(ulong(0x0000000001234567)) == 24); 218 assert(bsf(uint(0x76543210)) == 4); 219 assert(bsf(ulong(0x7654321076543210)) == 4); 220 assert(bsf(ulong(0x7654321000000000)) == 4 + 32); 221 } 222 223 private enum 一UNITTESTING一; 224 225 // Insert a dummy main when unittesting outside of dub. 226 version (VibeCustomMain) 227 { 228 } 229 else version (unittest) 230 void main() 231 { 232 } 233 234 private enum 一MISCELLANEOUS一; 235 236 pure nothrow @nogc 237 { 238 /** 239 * Aligns a pointer to the closest multiple of $(D pot) (a power of two), 240 * which is equal to or larger than $(D value). 241 */ 242 T* alignPtrNext(T)(scope T* ptr, in size_t pot) 243 in 244 { 245 assert(pot > 0 && pot.isPowerOf2); 246 } 247 body 248 { 249 return cast(T*)((cast(size_t) ptr + (pot - 1)) & -pot); 250 } 251 252 unittest 253 { 254 assert(alignPtrNext(cast(void*) 65, 64) == cast(void*) 128); 255 } 256 } 257 258 @nogc @safe pure nothrow 259 { 260 /// Returns whether the (positive) argument is an integral power of two. 261 @property bool isPowerOf2(in size_t n) 262 in 263 { 264 assert(n > 0); 265 } 266 body 267 { 268 return (n & n - 1) == 0; 269 } 270 271 version (LDC) 272 { 273 import core.simd : ubyte16; 274 275 pragma(LDC_intrinsic, "llvm.x86.sse2.pmovmskb.128") 276 uint moveMask(ubyte16); 277 } 278 else version (GNU) 279 { 280 import gcc.builtins; 281 282 alias moveMask = __builtin_ia32_pmovmskb128; 283 } 284 285 template SIMDFromScalar(V, alias scalar) 286 { 287 // This wrapper is needed for optimal performance with LDC and 288 // doesn't hurt GDC's inlining. 289 V SIMDFromScalar() 290 { 291 enum V asVectorEnum = scalar; 292 return asVectorEnum; 293 } 294 } 295 296 }