1 /** 2 * Copyright: Copyright Auburn Sounds 2016-2018. 3 * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) 4 * Authors: Guillaume Piolat 5 */ 6 module inteli.types; 7 8 version(LDC) 9 { 10 public import core.simd; 11 } 12 else 13 { 14 // This is a LDC SIMD emulation layer, for use with other D compilers. 15 // The goal is to be very similar in precision. 16 // The biggest differences are: 17 // 18 // 1. `cast` everywhere. With LDC vector types, short8 is implicitely convertible to int4 19 // but this is sadly impossible in D without D_SIMD (Windows 32-bit). 20 // 21 // 2. `vec.array` is directly writeable. 22 23 nothrow: 24 @nogc: 25 pure: 26 27 28 struct float4 29 { 30 float[4] array; 31 mixin VectorOps!(float4, float[4]); 32 33 enum float TrueMask = allOnes(); 34 enum float FalseMask = 0.0f; 35 36 private static float allOnes() 37 { 38 uint m1 = 0xffffffff; 39 return *cast(float*)(&m1); 40 } 41 } 42 43 struct byte16 44 { 45 byte[16] array; 46 mixin VectorOps!(byte16, byte[16]); 47 enum byte TrueMask = -1; 48 enum byte FalseMask = 0; 49 } 50 51 struct short8 52 { 53 short[8] array; 54 mixin VectorOps!(short8, short[8]); 55 enum short TrueMask = -1; 56 enum short FalseMask = 0; 57 } 58 59 struct int4 60 { 61 int[4] array; 62 mixin VectorOps!(int4, int[4]); 63 enum int TrueMask = -1; 64 enum int FalseMask = 0; 65 } 66 67 struct long2 68 { 69 long[2] array; 70 mixin VectorOps!(long2, long[2]); 71 enum long TrueMask = -1; 72 enum long FalseMask = 0; 73 } 74 75 struct double2 76 { 77 double[2] array; 78 mixin VectorOps!(double2, double[2]); 79 80 enum double TrueMask = allOnes(); 81 enum double FalseMask = 0.0f; 82 83 private static double allOnes() 84 { 85 ulong m1 = 0xffffffff_ffffffff; 86 return *cast(double*)(&m1); 87 } 88 } 89 90 static assert(float4.sizeof == 16); 91 static assert(byte16.sizeof == 16); 92 static assert(short8.sizeof == 16); 93 static assert(int4.sizeof == 16); 94 static assert(long2.sizeof == 16); 95 static assert(double2.sizeof == 16); 96 97 mixin template VectorOps(VectorType, ArrayType: BaseType[N], BaseType, size_t N) 98 { 99 enum Count = N; 100 alias Base = BaseType; 101 102 // Unary operators 103 VectorType opUnary(string op)() pure nothrow @safe @nogc 104 { 105 VectorType res = void; 106 mixin("res.array[] = " ~ op ~ "array[];"); 107 return res; 108 } 109 110 // Binary operators 111 VectorType opBinary(string op)(VectorType other) pure nothrow @safe @nogc 112 { 113 VectorType res = void; 114 mixin("res.array[] = array[] " ~ op ~ " other.array[];"); 115 return res; 116 } 117 118 // Assigning a static array 119 void opAssign(ArrayType v) pure nothrow @safe @nogc 120 { 121 array[] = v[]; 122 } 123 124 // Assigning a dyn array 125 this(ArrayType v) pure nothrow @safe @nogc 126 { 127 array[] = v[]; 128 } 129 130 /// We can't support implicit conversion but do support explicit casting. 131 /// "Vector types of the same size can be implicitly converted among each other." 132 /// Casting to another vector type is always just a raw copy. 133 VecDest opCast(VecDest)() pure nothrow @trusted @nogc 134 if (VecDest.sizeof == VectorType.sizeof) 135 { 136 // import core.stdc.string: memcpy; 137 VecDest dest = void; 138 // Copy 139 dest.array[] = (cast(typeof(dest.array))cast(void[VectorType.sizeof])array)[]; 140 // memcpy(dest.array.ptr, array.ptr, VectorType.sizeof); 141 return dest; 142 } 143 144 ref BaseType opIndex(size_t i) pure nothrow @safe @nogc 145 { 146 return array[i]; 147 } 148 149 } 150 151 auto extractelement(Vec, int index, Vec2)(Vec2 vec) @trusted 152 { 153 static assert(Vec.sizeof == Vec2.sizeof); 154 import core.stdc.string: memcpy; 155 Vec v = void; 156 memcpy(&v, &vec, Vec2.sizeof); 157 return v.array[index]; 158 } 159 160 auto insertelement(Vec, int index, Vec2)(Vec2 vec, Vec.Base e) @trusted 161 { 162 static assert(Vec.sizeof == Vec2.sizeof); 163 import core.stdc.string: memcpy; 164 Vec v = void; 165 memcpy(&v, &vec, Vec2.sizeof); 166 v.array[index] = e; 167 return v; 168 } 169 170 // Note: can't be @safe with this signature 171 Vec loadUnaligned(Vec)(const(Vec.Base)* pvec) @trusted 172 { 173 return *cast(Vec*)(pvec); 174 } 175 176 // Note: can't be @safe with this signature 177 void storeUnaligned(Vec)(Vec v, Vec.Base* pvec, ) @trusted 178 { 179 *cast(Vec*)(pvec) = v; 180 } 181 182 183 Vec shufflevector(Vec, mask...)(Vec a, Vec b) @safe 184 { 185 static assert(mask.length == Vec.Count); 186 187 Vec r = void; 188 foreach(int i, m; mask) 189 { 190 static assert (m < Vec.Count * 2); 191 int ind = cast(int)m; 192 if (ind < Vec.Count) 193 r.array[i] = a.array[ind]; 194 else 195 r.array[i] = b.array[ind-Vec.Count]; 196 } 197 return r; 198 } 199 200 // emulate ldc.simd cmpMask 201 202 Vec equalMask(Vec)(Vec a, Vec b) @safe // for floats, equivalent to "oeq" comparison 203 { 204 alias BaseType = Vec.Base; 205 alias Count = Vec.Count; 206 Vec result; 207 foreach(int i; 0..Count) 208 { 209 bool cond = a.array[i] == b.array[i]; 210 result.array[i] = cond ? Vec.TrueMask : Vec.FalseMask; 211 } 212 return result; 213 } 214 215 Vec notEqualMask(Vec)(Vec a, Vec b) @safe // for floats, equivalent to "one" comparison 216 { 217 alias BaseType = Vec.Base; 218 alias Count = Vec.Count; 219 Vec result; 220 foreach(int i; 0..Count) 221 { 222 bool cond = a.array[i] != b.array[i]; 223 result.array[i] = cond ? Vec.TrueMask : Vec.FalseMask; 224 } 225 return result; 226 } 227 228 Vec greaterMask(Vec)(Vec a, Vec b) @safe // for floats, equivalent to "ogt" comparison 229 { 230 alias BaseType = Vec.Base; 231 alias Count = Vec.Count; 232 Vec result; 233 foreach(int i; 0..Count) 234 { 235 bool cond = a.array[i] > b.array[i]; 236 result.array[i] = cond ? Vec.TrueMask : Vec.FalseMask; 237 } 238 return result; 239 } 240 241 Vec greaterOrEqualMask(Vec)(Vec a, Vec b) @safe // for floats, equivalent to "oge" comparison 242 { 243 alias BaseType = Vec.Base; 244 alias Count = Vec.Count; 245 Vec result; 246 foreach(int i; 0..Count) 247 { 248 bool cond = a.array[i] > b.array[i]; 249 result.array[i] = cond ? Vec.TrueMask : Vec.FalseMask; 250 } 251 return result; 252 } 253 254 unittest 255 { 256 float4 a = [1, 3, 5, 7]; 257 float4 b = [2, 3, 4, 5]; 258 int4 c = cast(int4)(greaterMask!float4(a, b)); 259 static immutable int[4] correct = [0, 0, 0xffff_ffff, 0xffff_ffff]; 260 assert(c.array == correct); 261 } 262 } 263 264 nothrow: 265 @nogc: 266 267 alias __m128 = float4; 268 alias __m128i = int4; 269 alias __m128d = double2; 270 alias __m64 = long; // Note: operation using __m64 are not available. 271 272 int _MM_SHUFFLE2(int x, int y) pure @safe 273 { 274 assert(x >= 0 && x <= 1); 275 assert(y >= 0 && y <= 1); 276 return (x << 1) | y; 277 } 278 279 int _MM_SHUFFLE(int z, int y, int x, int w) pure @safe 280 { 281 assert(x >= 0 && x <= 3); 282 assert(y >= 0 && y <= 3); 283 assert(z >= 0 && z <= 3); 284 assert(w >= 0 && w <= 3); 285 return (z<<6) | (y<<4) | (x<<2) | w; 286 } 287 288 // Note: here is how to map clang's __builtin_convertvector to LLVM IR 289 // float to double => fpext 290 // double to float => fptrunc 291 // float to long => fptosi 292 // float/double to ulong => fptoui 293 // ushort to ulong => fptoui 294 // short to long => sext 295 // short to float => sitofp 296 // ushort to float => uitofp 297 // long to double => sitofp 298 299