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