1 /** 2 * Copyright: Copyright Auburn Sounds 2016-2018, Stefanos Baziotis 2019. 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(GNU) 9 { 10 version(X86_64) 11 { 12 enum CoreSimdIsEmulated = false; 13 14 public import core.simd; 15 import gcc.builtins; 16 17 // Declare vector types that correspond to MMX types 18 // Because they are expressible in IR anyway. 19 alias Vector!(long [1]) long1; 20 alias Vector!(float[2]) float2; 21 alias Vector!(int [2]) int2; 22 alias Vector!(short[4]) short4; 23 alias Vector!(byte [8]) byte8; 24 25 float4 loadUnaligned(Vec)(const(float)* pvec) @trusted if (is(Vec == float4)) 26 { 27 return __builtin_ia32_loadups(pvec); 28 } 29 30 double2 loadUnaligned(Vec)(const(double)* pvec) @trusted if (is(Vec == double2)) 31 { 32 return __builtin_ia32_loadupd(pvec); 33 } 34 35 byte16 loadUnaligned(Vec)(const(byte)* pvec) @trusted if (is(Vec == byte16)) 36 { 37 return cast(byte16) __builtin_ia32_loaddqu(cast(const(char)*) pvec); 38 } 39 40 short8 loadUnaligned(Vec)(const(short)* pvec) @trusted if (is(Vec == short8)) 41 { 42 return cast(short8) __builtin_ia32_loaddqu(cast(const(char)*) pvec); 43 } 44 45 int4 loadUnaligned(Vec)(const(int)* pvec) @trusted if (is(Vec == int4)) 46 { 47 return cast(int4) __builtin_ia32_loaddqu(cast(const(char)*) pvec); 48 } 49 50 long2 loadUnaligned(Vec)(const(long)* pvec) @trusted if (is(Vec == long2)) 51 { 52 return cast(long2) __builtin_ia32_loaddqu(cast(const(char)*) pvec); 53 } 54 55 void storeUnaligned(Vec)(Vec v, float* pvec) @trusted if (is(Vec == float4)) 56 { 57 __builtin_ia32_storeups(pvec, v); 58 } 59 60 void storeUnaligned(Vec)(Vec v, double* pvec) @trusted if (is(Vec == double2)) 61 { 62 __builtin_ia32_storeupd(pvec, v); 63 } 64 65 void storeUnaligned(Vec)(Vec v, byte* pvec) @trusted if (is(Vec == byte16)) 66 { 67 __builtin_ia32_storedqu(cast(char*)pvec, v); 68 } 69 70 void storeUnaligned(Vec)(Vec v, short* pvec) @trusted if (is(Vec == short8)) 71 { 72 __builtin_ia32_storedqu(cast(char*)pvec, v); 73 } 74 75 void storeUnaligned(Vec)(Vec v, int* pvec) @trusted if (is(Vec == int4)) 76 { 77 __builtin_ia32_storedqu(cast(char*)pvec, v); 78 } 79 80 void storeUnaligned(Vec)(Vec v, long* pvec) @trusted if (is(Vec == long2)) 81 { 82 __builtin_ia32_storedqu(cast(char*)pvec, v); 83 } 84 85 // TODO: for performance, replace that anywhere possible by a GDC intrinsic 86 Vec shufflevector(Vec, mask...)(Vec a, Vec b) @trusted 87 { 88 enum Count = Vec.array.length; 89 static assert(mask.length == Count); 90 91 Vec r = void; 92 foreach(int i, m; mask) 93 { 94 static assert (m < Count * 2); 95 int ind = cast(int)m; 96 if (ind < Count) 97 r.ptr[i] = a.array[ind]; 98 else 99 r.ptr[i] = b.array[ind - Count]; 100 } 101 return r; 102 } 103 } 104 else 105 { 106 enum CoreSimdIsEmulated = true; 107 } 108 } 109 else version(LDC) 110 { 111 public import core.simd; 112 public import ldc.simd; 113 114 // Declare vector types that correspond to MMX types 115 // Because they are expressible in IR anyway. 116 alias Vector!(long [1]) long1; 117 alias Vector!(float[2]) float2; 118 alias Vector!(int [2]) int2; 119 alias Vector!(short[4]) short4; 120 alias Vector!(byte [8]) byte8; 121 122 enum CoreSimdIsEmulated = false; 123 } 124 else version(DigitalMars) 125 { 126 enum CoreSimdIsEmulated = true; // TODO: use core.simd with DMD when D_SIMD is defined 127 } 128 129 static if (CoreSimdIsEmulated) 130 { 131 // This is a LDC SIMD emulation layer, for use with other D compilers. 132 // The goal is to be very similar in precision. 133 // The biggest differences are: 134 // 135 // 1. `cast` everywhere. With LDC vector types, short8 is implicitely convertible to int4 136 // but this is sadly impossible in D without D_SIMD (Windows 32-bit). 137 // 138 // 2. `vec.array` is directly writeable. 139 140 nothrow: 141 @nogc: 142 pure: 143 144 145 /// MMX-like SIMD types 146 struct float2 147 { 148 float[2] array; 149 mixin VectorOps!(float2, float[2]); 150 151 enum float TrueMask = allOnes(); 152 enum float FalseMask = 0.0f; 153 154 private static float allOnes() 155 { 156 uint m1 = 0xffffffff; 157 return *cast(float*)(&m1); 158 } 159 } 160 161 struct byte8 162 { 163 byte[8] array; 164 mixin VectorOps!(byte8, byte[8]); 165 enum byte TrueMask = -1; 166 enum byte FalseMask = 0; 167 } 168 169 struct short4 170 { 171 short[4] array; 172 mixin VectorOps!(short4, short[4]); 173 enum short TrueMask = -1; 174 enum short FalseMask = 0; 175 } 176 177 struct int2 178 { 179 int[2] array; 180 mixin VectorOps!(int2, int[2]); 181 enum int TrueMask = -1; 182 enum int FalseMask = 0; 183 } 184 185 struct long1 186 { 187 long[1] array; 188 mixin VectorOps!(long1, long[1]); 189 enum long TrueMask = -1; 190 enum long FalseMask = 0; 191 } 192 193 static assert(float2.sizeof == 8); 194 static assert(byte8.sizeof == 8); 195 static assert(short4.sizeof == 8); 196 static assert(int2.sizeof == 8); 197 static assert(long1.sizeof == 8); 198 199 200 /// SSE-like SIMD types 201 202 struct float4 203 { 204 float[4] array; 205 mixin VectorOps!(float4, float[4]); 206 207 enum float TrueMask = allOnes(); 208 enum float FalseMask = 0.0f; 209 210 private static float allOnes() 211 { 212 uint m1 = 0xffffffff; 213 return *cast(float*)(&m1); 214 } 215 } 216 217 struct byte16 218 { 219 byte[16] array; 220 mixin VectorOps!(byte16, byte[16]); 221 enum byte TrueMask = -1; 222 enum byte FalseMask = 0; 223 } 224 225 struct short8 226 { 227 short[8] array; 228 mixin VectorOps!(short8, short[8]); 229 enum short TrueMask = -1; 230 enum short FalseMask = 0; 231 } 232 233 struct int4 234 { 235 int[4] array; 236 mixin VectorOps!(int4, int[4]); 237 enum int TrueMask = -1; 238 enum int FalseMask = 0; 239 } 240 241 struct long2 242 { 243 long[2] array; 244 mixin VectorOps!(long2, long[2]); 245 enum long TrueMask = -1; 246 enum long FalseMask = 0; 247 } 248 249 struct double2 250 { 251 double[2] array; 252 mixin VectorOps!(double2, double[2]); 253 254 enum double TrueMask = allOnes(); 255 enum double FalseMask = 0.0f; 256 257 private static double allOnes() 258 { 259 ulong m1 = 0xffffffff_ffffffff; 260 return *cast(double*)(&m1); 261 } 262 } 263 264 static assert(float4.sizeof == 16); 265 static assert(byte16.sizeof == 16); 266 static assert(short8.sizeof == 16); 267 static assert(int4.sizeof == 16); 268 static assert(long2.sizeof == 16); 269 static assert(double2.sizeof == 16); 270 271 mixin template VectorOps(VectorType, ArrayType: BaseType[N], BaseType, size_t N) 272 { 273 enum Count = N; 274 alias Base = BaseType; 275 276 BaseType* ptr() pure nothrow @nogc 277 { 278 return array.ptr; 279 } 280 281 // Unary operators 282 VectorType opUnary(string op)() pure nothrow @safe @nogc 283 { 284 VectorType res = void; 285 mixin("res.array[] = " ~ op ~ "array[];"); 286 return res; 287 } 288 289 // Binary operators 290 VectorType opBinary(string op)(VectorType other) pure const nothrow @safe @nogc 291 { 292 VectorType res = void; 293 mixin("res.array[] = array[] " ~ op ~ " other.array[];"); 294 return res; 295 } 296 297 // Assigning a static array 298 void opAssign(ArrayType v) pure nothrow @safe @nogc 299 { 300 array[] = v[]; 301 } 302 303 void opOpAssign(string op)(VectorType other) pure nothrow @safe @nogc 304 { 305 mixin("array[] " ~ op ~ "= other.array[];"); 306 } 307 308 // Assigning a dyn array 309 this(ArrayType v) pure nothrow @safe @nogc 310 { 311 array[] = v[]; 312 } 313 314 // Broadcast constructor 315 this(BaseType x) pure nothrow @safe @nogc 316 { 317 array[] = x; 318 } 319 320 /// We can't support implicit conversion but do support explicit casting. 321 /// "Vector types of the same size can be implicitly converted among each other." 322 /// Casting to another vector type is always just a raw copy. 323 VecDest opCast(VecDest)() pure const nothrow @trusted @nogc 324 if (VecDest.sizeof == VectorType.sizeof) 325 { 326 // import core.stdc.string: memcpy; 327 VecDest dest = void; 328 // Copy 329 dest.array[] = (cast(typeof(dest.array))cast(void[VectorType.sizeof])array)[]; 330 return dest; 331 } 332 333 ref inout(BaseType) opIndex(size_t i) inout pure nothrow @safe @nogc 334 { 335 return array[i]; 336 } 337 338 } 339 340 auto extractelement(Vec, int index, Vec2)(Vec2 vec) @trusted 341 { 342 static assert(Vec.sizeof == Vec2.sizeof); 343 import core.stdc.string: memcpy; 344 Vec v = void; 345 memcpy(&v, &vec, Vec2.sizeof); 346 return v.array[index]; 347 } 348 349 auto insertelement(Vec, int index, Vec2)(Vec2 vec, Vec.Base e) @trusted 350 { 351 static assert(Vec.sizeof == Vec2.sizeof); 352 import core.stdc.string: memcpy; 353 Vec v = void; 354 memcpy(&v, &vec, Vec2.sizeof); 355 v.array[index] = e; 356 return v; 357 } 358 359 // Note: can't be @safe with this signature 360 Vec loadUnaligned(Vec)(const(Vec.Base)* pvec) @trusted 361 { 362 return *cast(Vec*)(pvec); 363 } 364 365 // Note: can't be @safe with this signature 366 void storeUnaligned(Vec)(Vec v, Vec.Base* pvec) @trusted 367 { 368 *cast(Vec*)(pvec) = v; 369 } 370 371 Vec shufflevector(Vec, mask...)(Vec a, Vec b) @safe 372 { 373 static assert(mask.length == Vec.Count); 374 375 Vec r = void; 376 foreach(int i, m; mask) 377 { 378 static assert (m < Vec.Count * 2); 379 int ind = cast(int)m; 380 if (ind < Vec.Count) 381 r.array[i] = a.array[ind]; 382 else 383 r.array[i] = b.array[ind-Vec.Count]; 384 } 385 return r; 386 } 387 388 // emulate ldc.simd cmpMask 389 390 Vec equalMask(Vec)(Vec a, Vec b) @safe // for floats, equivalent to "oeq" comparison 391 { 392 alias BaseType = Vec.Base; 393 alias Count = Vec.Count; 394 Vec result; 395 foreach(int i; 0..Count) 396 { 397 bool cond = a.array[i] == b.array[i]; 398 result.array[i] = cond ? Vec.TrueMask : Vec.FalseMask; 399 } 400 return result; 401 } 402 403 Vec notEqualMask(Vec)(Vec a, Vec b) @safe // for floats, equivalent to "one" comparison 404 { 405 alias BaseType = Vec.Base; 406 alias Count = Vec.Count; 407 Vec result; 408 foreach(int i; 0..Count) 409 { 410 bool cond = a.array[i] != b.array[i]; 411 result.array[i] = cond ? Vec.TrueMask : Vec.FalseMask; 412 } 413 return result; 414 } 415 416 Vec greaterMask(Vec)(Vec a, Vec b) @safe // for floats, equivalent to "ogt" comparison 417 { 418 alias BaseType = Vec.Base; 419 alias Count = Vec.Count; 420 Vec result; 421 foreach(int i; 0..Count) 422 { 423 bool cond = a.array[i] > b.array[i]; 424 result.array[i] = cond ? Vec.TrueMask : Vec.FalseMask; 425 } 426 return result; 427 } 428 429 Vec greaterOrEqualMask(Vec)(Vec a, Vec b) @safe // for floats, equivalent to "oge" comparison 430 { 431 alias BaseType = Vec.Base; 432 alias Count = Vec.Count; 433 Vec result; 434 foreach(int i; 0..Count) 435 { 436 bool cond = a.array[i] > b.array[i]; 437 result.array[i] = cond ? Vec.TrueMask : Vec.FalseMask; 438 } 439 return result; 440 } 441 442 unittest 443 { 444 float4 a = [1, 3, 5, 7]; 445 float4 b = [2, 3, 4, 5]; 446 int4 c = cast(int4)(greaterMask!float4(a, b)); 447 static immutable int[4] correct = [0, 0, 0xffff_ffff, 0xffff_ffff]; 448 assert(c.array == correct); 449 } 450 } 451 452 nothrow: 453 @nogc: 454 455 alias __m128 = float4; 456 alias __m128i = int4; 457 alias __m128d = double2; 458 alias __m64 = long1; // like in Clang, __m64 is a vector of 1 long 459 460 int _MM_SHUFFLE2(int x, int y) pure @safe 461 { 462 assert(x >= 0 && x <= 1); 463 assert(y >= 0 && y <= 1); 464 return (x << 1) | y; 465 } 466 467 int _MM_SHUFFLE(int z, int y, int x, int w) pure @safe 468 { 469 assert(x >= 0 && x <= 3); 470 assert(y >= 0 && y <= 3); 471 assert(z >= 0 && z <= 3); 472 assert(w >= 0 && w <= 3); 473 return (z<<6) | (y<<4) | (x<<2) | w; 474 } 475 476 // test assignment from scalar to vector type 477 unittest 478 { 479 float4 A = 3.0f; 480 float[4] correctA = [3.0f, 3.0f, 3.0f, 3.0f]; 481 assert(A.array == correctA); 482 483 int2 B = 42; 484 int[2] correctB = [42, 42]; 485 assert(B.array == correctB); 486 }