1 module tame.internal;
2 
3 package:
4 
5 version (X86_64) {
6 	enum isAMD64 = true;
7 	enum isX86 = false;
8 } else version (X86) {
9 	enum isAMD64 = false;
10 	enum isX86 = true;
11 } else {
12 	enum isX86 = false;
13 	enum isAMD64 = false;
14 }
15 
16 version (LDC) {
17 	enum isLDC = true;
18 	enum isGDC = false;
19 	enum isDMD = false;
20 } else version (GNU) {
21 	enum isLDC = false;
22 	enum isGDC = true;
23 	enum isDMD = false;
24 } else version (DigitalMars) {
25 	enum isLDC = false;
26 	enum isGDC = false;
27 	enum isDMD = true;
28 }
29 
30 version (DigitalMars) {
31 	enum noinline;
32 	enum forceinline;
33 	enum sse4;
34 } else version (GNU) {
35 	import gcc.attribute;
36 
37 	enum noinline = gcc.attribute.attribute("noinline");
38 	enum forceinline = gcc.attribute.attribute("forceinline");
39 	enum sse4_2 = gcc.attribute.attribute("target", "sse4.2");
40 } else version (LDC) {
41 	import ldc.attributes;
42 
43 	enum noinline = ldc.attributes.optStrategy("none");
44 	enum forceinline = ldc.attributes.llvmAttr("always_inline", "true");
45 	enum sse4_2 = ldc.attributes.target("+sse4.2");
46 }
47 import std.traits : Unqual;
48 
49 pure nothrow @nogc:
50 
51 /*******************************************************************************
52  *
53  * Count leading zeroes.
54  *
55  * Params:
56  *   u = the unsigned value to scan
57  *
58  * Returns:
59  *   The number of leading zero bits before the first one bit. If `u` is `0`,
60  *   the result is undefined.
61  *
62  **************************************/
63 version (DigitalMars) {
64 	import core.bitop : bsr, bsf;
65 
66 	U clz(U)(U u) if (is(Unqual!U : size_t)) {
67 		pragma(inline, true);
68 		enum U max = 8 * U.sizeof - 1;
69 		return max - bsr(u);
70 	}
71 
72 	static if (isX86) {
73 		uint clz(U)(U u) if (is(Unqual!U == ulong)) {
74 			pragma(inline, true);
75 			uint hi = u >> 32;
76 			return hi ? 31 - bsr(hi) : 63 - bsr(cast(uint)u);
77 		}
78 	}
79 } else version (GNU) {
80 	import gcc.builtins;
81 
82 	alias clz = __builtin_clz;
83 	static if (isX86) {
84 		uint clz(ulong u) {
85 			uint hi = u >> 32;
86 			return hi ? __builtin_clz(hi) : 32 + __builtin_clz(cast(uint)u);
87 		}
88 	} else
89 		alias clz = __builtin_clzl;
90 } else version (LDC) {
91 	U clz(U)(U u) if (is(Unqual!U : size_t)) {
92 		pragma(inline, true);
93 		import ldc.intrinsics;
94 
95 		return llvm_ctlz(u, false);
96 	}
97 
98 	static if (size_t.sizeof == 4) {
99 		uint clz(U)(U u) if (is(Unqual!U == ulong)) {
100 			pragma(inline, true);
101 			import ldc.intrinsics;
102 
103 			return cast(uint)llvm_ctlz(u, false);
104 		}
105 	}
106 }
107 
108 @safe unittest {
109 	assert(clz(0x01234567) == 7);
110 	assert(clz(0x0123456701234567UL) == 7);
111 	assert(clz(0x0000000001234567UL) == 7 + 32);
112 }
113 
114 /**
115  * Aligns a pointer to the closest multiple of $(D pot) (a power of two),
116  * which is equal to or larger than $(D value).
117  */
118 T* alignPtrNext(T)(scope T* ptr, in size_t pot)
119 in (pot > 0 && pot.isPowerOf2) {
120 	return cast(T*)((cast(size_t)ptr + (pot - 1)) & -pot);
121 }
122 
123 unittest {
124 	assert(alignPtrNext(cast(void*)65, 64) == cast(void*)128);
125 }
126 
127 @safe {
128 	/// Returns whether the (positive) argument is an integral power of two.
129 	@property bool isPowerOf2(in size_t n)
130 	in (n > 0) {
131 		return (n & n - 1) == 0;
132 	}
133 
134 	version (LDC) {
135 		import core.simd;
136 
137 		pragma(LDC_intrinsic, "llvm.x86.sse2.pmovmskb.128")
138 		uint moveMask(ubyte16);
139 	} else version (GNU) {
140 		import gcc.builtins;
141 
142 		alias moveMask = __builtin_ia32_pmovmskb128;
143 	}
144 
145 	template SIMDFromScalar(V, alias scalar) {
146 		// This wrapper is needed for optimal performance with LDC and
147 		// doesn't hurt GDC's inlining.
148 		V SIMDFromScalar() {
149 			enum V asVectorEnum = scalar;
150 			return asVectorEnum;
151 		}
152 	}
153 }