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 }