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. 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], 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], 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], 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], 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], 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], 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, int 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 { 135 static assert(VectorType.sizeof == VecDest.sizeof, "non matching sizes between vector types"); 136 import core.stdc.string: memcpy; 137 VecDest dest = void; 138 memcpy(dest.array.ptr, array.ptr, VectorType.sizeof); 139 return dest; 140 } 141 142 ref BaseType opIndex(size_t i) pure nothrow @safe @nogc 143 { 144 return array[i]; 145 } 146 147 } 148 149 auto extractelement(Vec, int index, Vec2)(Vec2 vec) @trusted 150 { 151 static assert(Vec.sizeof == Vec2.sizeof); 152 import core.stdc.string: memcpy; 153 Vec v = void; 154 memcpy(&v, &vec, Vec2.sizeof); 155 return v.array[index]; 156 } 157 158 auto insertelement(Vec, int index, Vec2)(Vec2 vec, Vec.Base e) @trusted 159 { 160 static assert(Vec.sizeof == Vec2.sizeof); 161 import core.stdc.string: memcpy; 162 Vec v = void; 163 memcpy(&v, &vec, Vec2.sizeof); 164 v.array[index] = e; 165 return v; 166 } 167 168 // Note: can't be @safe with this signature 169 Vec loadUnaligned(Vec)(const(Vec.Base)* pvec) @trusted 170 { 171 return *cast(Vec*)(pvec); 172 } 173 174 // Note: can't be @safe with this signature 175 void storeUnaligned(Vec)(Vec v, Vec.Base* pvec, ) @trusted 176 { 177 *cast(Vec*)(pvec) = v; 178 } 179 180 181 Vec shufflevector(Vec, mask...)(Vec a, Vec b) @safe 182 { 183 static assert(mask.length == Vec.Count); 184 185 Vec r = void; 186 foreach(int i, m; mask) 187 { 188 static assert (m < Vec.Count * 2); 189 int ind = cast(int)m; 190 if (ind < Vec.Count) 191 r.array[i] = a.array[ind]; 192 else 193 r.array[i] = b.array[ind-Vec.Count]; 194 } 195 return r; 196 } 197 198 // emulate ldc.simd cmpMask 199 200 Vec equalMask(Vec)(Vec a, Vec b) @safe 201 { 202 alias BaseType = Vec.Base; 203 alias Count = Vec.Count; 204 Vec result; 205 foreach(int i; 0..Count) 206 { 207 bool cond = a.array[i] == b.array[i]; 208 result.array[i] = cond ? Vec.TrueMask : Vec.FalseMask; 209 } 210 return result; 211 } 212 213 Vec notEqualMask(Vec)(Vec a, Vec b) @safe 214 { 215 alias BaseType = Vec.Base; 216 alias Count = Vec.Count; 217 Vec result; 218 foreach(int i; 0..Count) 219 { 220 bool cond = a.array[i] != b.array[i]; 221 result.array[i] = cond ? Vec.TrueMask : Vec.FalseMask; 222 } 223 return result; 224 } 225 226 Vec greaterMask(Vec)(Vec a, Vec b) @safe 227 { 228 alias BaseType = Vec.Base; 229 alias Count = Vec.Count; 230 Vec result; 231 foreach(int i; 0..Count) 232 { 233 bool cond = a.array[i] > b.array[i]; 234 result.array[i] = cond ? Vec.TrueMask : Vec.FalseMask; 235 } 236 return result; 237 } 238 239 Vec greaterOrEqualMask(Vec)(Vec a, Vec b) @safe 240 { 241 alias BaseType = Vec.Base; 242 alias Count = Vec.Count; 243 Vec result; 244 foreach(int i; 0..Count) 245 { 246 bool cond = a.array[i] > b.array[i]; 247 result.array[i] = cond ? Vec.TrueMask : Vec.FalseMask; 248 } 249 return result; 250 } 251 252 unittest 253 { 254 float4 a = [1, 3, 5, 7]; 255 float4 b = [2, 3, 4, 5]; 256 int4 c = cast(int4)(greaterMask!float4(a, b)); 257 static immutable int[4] correct = [0, 0, 0xffff_ffff, 0xffff_ffff]; 258 assert(c.array == correct); 259 } 260 } 261 262 263 alias __m128 = float4; 264 alias __m128i = int4; 265 alias __m128d = double2; 266 alias __m64 = long; // Note: operation using __m64 are not available. 267 268 int _MM_SHUFFLE2(int x, int y) pure @safe 269 { 270 assert(x >= 0 && x <= 1); 271 assert(y >= 0 && y <= 1); 272 return (x << 1) | y; 273 } 274 275 int _MM_SHUFFLE(int z, int y, int x, int w) pure @safe 276 { 277 assert(x >= 0 && x <= 3); 278 assert(y >= 0 && y <= 3); 279 assert(z >= 0 && z <= 3); 280 assert(w >= 0 && w <= 3); 281 return (z<<6) | (y<<4) | (x<<2) | w; 282 } 283 284 285