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