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