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