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