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 // Broadcast constructor 202 this(BaseType x) pure nothrow @safe @nogc 203 { 204 array[] = x; 205 } 206 207 /// We can't support implicit conversion but do support explicit casting. 208 /// "Vector types of the same size can be implicitly converted among each other." 209 /// Casting to another vector type is always just a raw copy. 210 VecDest opCast(VecDest)() pure const nothrow @trusted @nogc 211 if (VecDest.sizeof == VectorType.sizeof) 212 { 213 // import core.stdc.string: memcpy; 214 VecDest dest = void; 215 // Copy 216 dest.array[] = (cast(typeof(dest.array))cast(void[VectorType.sizeof])array)[]; 217 return dest; 218 } 219 220 ref inout(BaseType) opIndex(size_t i) inout pure nothrow @safe @nogc 221 { 222 return array[i]; 223 } 224 225 } 226 227 auto extractelement(Vec, int index, Vec2)(Vec2 vec) @trusted 228 { 229 static assert(Vec.sizeof == Vec2.sizeof); 230 import core.stdc.string: memcpy; 231 Vec v = void; 232 memcpy(&v, &vec, Vec2.sizeof); 233 return v.array[index]; 234 } 235 236 auto insertelement(Vec, int index, Vec2)(Vec2 vec, Vec.Base e) @trusted 237 { 238 static assert(Vec.sizeof == Vec2.sizeof); 239 import core.stdc.string: memcpy; 240 Vec v = void; 241 memcpy(&v, &vec, Vec2.sizeof); 242 v.array[index] = e; 243 return v; 244 } 245 246 // Note: can't be @safe with this signature 247 Vec loadUnaligned(Vec)(const(Vec.Base)* pvec) @trusted 248 { 249 return *cast(Vec*)(pvec); 250 } 251 252 // Note: can't be @safe with this signature 253 void storeUnaligned(Vec)(Vec v, Vec.Base* pvec, ) @trusted 254 { 255 *cast(Vec*)(pvec) = v; 256 } 257 258 259 Vec shufflevector(Vec, mask...)(Vec a, Vec b) @safe 260 { 261 static assert(mask.length == Vec.Count); 262 263 Vec r = void; 264 foreach(int i, m; mask) 265 { 266 static assert (m < Vec.Count * 2); 267 int ind = cast(int)m; 268 if (ind < Vec.Count) 269 r.array[i] = a.array[ind]; 270 else 271 r.array[i] = b.array[ind-Vec.Count]; 272 } 273 return r; 274 } 275 276 // emulate ldc.simd cmpMask 277 278 Vec equalMask(Vec)(Vec a, Vec b) @safe // for floats, equivalent to "oeq" comparison 279 { 280 alias BaseType = Vec.Base; 281 alias Count = Vec.Count; 282 Vec result; 283 foreach(int i; 0..Count) 284 { 285 bool cond = a.array[i] == b.array[i]; 286 result.array[i] = cond ? Vec.TrueMask : Vec.FalseMask; 287 } 288 return result; 289 } 290 291 Vec notEqualMask(Vec)(Vec a, Vec b) @safe // for floats, equivalent to "one" comparison 292 { 293 alias BaseType = Vec.Base; 294 alias Count = Vec.Count; 295 Vec result; 296 foreach(int i; 0..Count) 297 { 298 bool cond = a.array[i] != b.array[i]; 299 result.array[i] = cond ? Vec.TrueMask : Vec.FalseMask; 300 } 301 return result; 302 } 303 304 Vec greaterMask(Vec)(Vec a, Vec b) @safe // for floats, equivalent to "ogt" comparison 305 { 306 alias BaseType = Vec.Base; 307 alias Count = Vec.Count; 308 Vec result; 309 foreach(int i; 0..Count) 310 { 311 bool cond = a.array[i] > b.array[i]; 312 result.array[i] = cond ? Vec.TrueMask : Vec.FalseMask; 313 } 314 return result; 315 } 316 317 Vec greaterOrEqualMask(Vec)(Vec a, Vec b) @safe // for floats, equivalent to "oge" comparison 318 { 319 alias BaseType = Vec.Base; 320 alias Count = Vec.Count; 321 Vec result; 322 foreach(int i; 0..Count) 323 { 324 bool cond = a.array[i] > b.array[i]; 325 result.array[i] = cond ? Vec.TrueMask : Vec.FalseMask; 326 } 327 return result; 328 } 329 330 unittest 331 { 332 float4 a = [1, 3, 5, 7]; 333 float4 b = [2, 3, 4, 5]; 334 int4 c = cast(int4)(greaterMask!float4(a, b)); 335 static immutable int[4] correct = [0, 0, 0xffff_ffff, 0xffff_ffff]; 336 assert(c.array == correct); 337 } 338 } 339 340 nothrow: 341 @nogc: 342 343 alias __m128 = float4; 344 alias __m128i = int4; 345 alias __m128d = double2; 346 alias __m64 = long1; // like in Clang, __m64 is a vector of 1 long 347 348 int _MM_SHUFFLE2(int x, int y) pure @safe 349 { 350 assert(x >= 0 && x <= 1); 351 assert(y >= 0 && y <= 1); 352 return (x << 1) | y; 353 } 354 355 int _MM_SHUFFLE(int z, int y, int x, int w) pure @safe 356 { 357 assert(x >= 0 && x <= 3); 358 assert(y >= 0 && y <= 3); 359 assert(z >= 0 && z <= 3); 360 assert(w >= 0 && w <= 3); 361 return (z<<6) | (y<<4) | (x<<2) | w; 362 } 363 364 // test assignment from scalar to vector type 365 unittest 366 { 367 float4 A = 3.0f; 368 float[4] correctA = [3.0f, 3.0f, 3.0f, 3.0f]; 369 assert(A.array == correctA); 370 371 int2 B = 42; 372 int[2] correctB = [42, 42]; 373 assert(B.array == correctB); 374 }