2008-10-10 Rodrigo Kumpera <rkumpera@novell.com>
[mono.git] / mono / mini / simd-intrinsics.c
1 /*
2  * simd-instrisics.c: simd support for intrinsics
3  *
4  * Author:
5  *   Rodrigo Kumpera (rkumpera@novell.com)
6  *
7  * (C) 2008 Novell, Inc.
8  */
9
10 #include <config.h>
11 #include <stdio.h>
12
13 #define NEW_IR
14 #include "mini.h"
15 #include "ir-emit.h"
16
17 /*
18 General notes on SIMD intrinsics
19
20 TODO handle operands with non SIMD args, such as op_Addition (Vector4f, float)
21 TODO optimize r4const in .ctor so it doesn't go into the FP stack first
22 TODO extend op_to_op_dest_membase to handle simd ops
23 TODO add support for indexed versions of simd ops
24 TODO to an amd64 port and figure out how to properly handle extractors/.ctor
25 TODO make sure locals, arguments and spills are properly aligned.
26 TODO add support for fusing a XMOVE into a simd op in mono_spill_global_vars.
27 TODO add stuff to man pages
28 TODO document this under /docs
29 TODO make passing a xmm as argument not cause it to be LDADDR'ed (introduce an OP_XPUSH)
30 TODO revamp the .ctor sequence as it looks very fragile, maybe use a var just like iconv_to_r8_raw. 
31 TODO figure out what's wrong with OP_STOREX_MEMBASE_REG and OP_STOREX_MEMBASE (the 2nd is for imm operands)
32 TODO maybe add SSE3 emulation on top of SSE2, or just implement the corresponding functions using SSE2 intrinsics.
33 TODO pass simd arguments in registers or, at least, add SSE support for pushing large (>=16) valuetypes 
34 TODO pass simd args byval to a non-intrinsic method cause some useless local var load/store to happen. 
35
36 General notes for SIMD intrinsics.
37
38 -Bad extractor and constructor performance
39 Extracting a float from a XMM is a complete disaster if you are passing it as an argument.
40 It will be loaded in the FP stack just to be pushed on the call stack.
41
42 A similar thing happens with Vector4f constructor that require float vars to be 
43
44 The fix for this issue is similar to the one required for r4const as method args. Avoiding the
45 trip to the FP stack is desirable.
46
47 -Extractor and constructor code doesn't make sense under amd64. Both currently assume separate banks
48 for simd and fp.
49
50
51 -Promote OP_EXTRACT_I4 to a STORE op
52 The advantage of this change is that it could have a _membase version and promote further optimizations.
53
54 -Create a MONO_INST_DONT_REGALLOC and use it in all places that MONO_INST_INDIRECT is used
55 without a OP_LDADDR.
56 */
57
58 #ifdef MONO_ARCH_SIMD_INTRINSICS
59
60 //#define IS_DEBUG_ON(cfg) (0)
61
62 #define IS_DEBUG_ON(cfg) ((cfg)->verbose_level >= 3)
63 #define DEBUG(a) do { if (IS_DEBUG_ON(cfg)) { a; } } while (0)
64 enum {
65         SIMD_EMIT_BINARY,
66         SIMD_EMIT_BINARY_SSE3,
67         SIMD_EMIT_UNARY,
68         SIMD_EMIT_GETTER,
69         SIMD_EMIT_CTOR,
70         SIMD_EMIT_CAST,
71         SIMD_EMIT_SHUFFLE,
72         SIMD_EMIT_SHIFT,
73         SIMD_EMIT_LOAD_ALIGNED,
74         SIMD_EMIT_STORE_ALIGNED
75 };
76
77 /*This is the size of the largest method name + 1 (to fit the ending \0). Align to 4 as well.*/
78 #define SIMD_INTRINSIC_NAME_MAX 22
79
80 typedef struct {
81         const char name[SIMD_INTRINSIC_NAME_MAX];
82         guint16 opcode;
83         guint8 simd_emit_mode;
84         guint8 flags;
85 } SimdIntrinsc;
86
87 /*
88 Missing:
89 setters
90  */
91 static const SimdIntrinsc vector4f_intrinsics[] = {
92         { ".ctor", 0, SIMD_EMIT_CTOR },
93         { "AddSub", OP_ADDSUBPS, SIMD_EMIT_BINARY_SSE3 },
94         { "HorizontalAdd", OP_HADDPS, SIMD_EMIT_BINARY_SSE3 },
95         { "HorizontalSub", OP_HSUBPS, SIMD_EMIT_BINARY_SSE3 },  
96         { "InvSqrt", OP_RSQRTPS, SIMD_EMIT_UNARY },
97         { "LoadAligned", 0, SIMD_EMIT_LOAD_ALIGNED },
98         { "Max", OP_MAXPS, SIMD_EMIT_BINARY },
99         { "Min", OP_MINPS, SIMD_EMIT_BINARY },
100         { "Shuffle", 0, SIMD_EMIT_SHUFFLE },
101         { "Sqrt", OP_SQRTPS, SIMD_EMIT_UNARY },
102         { "StoreAligned", 0, SIMD_EMIT_STORE_ALIGNED },
103         { "get_W", 3, SIMD_EMIT_GETTER },
104         { "get_X", 0, SIMD_EMIT_GETTER },
105         { "get_Y", 1, SIMD_EMIT_GETTER },
106         { "get_Z", 2, SIMD_EMIT_GETTER },
107         { "op_Addition", OP_ADDPS, SIMD_EMIT_BINARY },
108         { "op_Division", OP_DIVPS, SIMD_EMIT_BINARY },
109         { "op_Explicit", 0, SIMD_EMIT_CAST }, 
110         { "op_Multiply", OP_MULPS, SIMD_EMIT_BINARY },
111         { "op_Subtraction", OP_SUBPS, SIMD_EMIT_BINARY },
112 };
113
114 /*
115 Missing:
116 A lot, revisit Vector4u.
117  */
118 static const SimdIntrinsc vector4u_intrinsics[] = {
119         { "op_BitwiseAnd", OP_PAND, SIMD_EMIT_BINARY },
120         { "op_BitwiseOr", OP_POR, SIMD_EMIT_BINARY },
121         { "op_BitwiseXor", OP_PXOR, SIMD_EMIT_BINARY },
122 };
123
124 /*
125 Missing:
126 .ctor
127 getters
128 setters
129  */
130 static const SimdIntrinsc vector8us_intrinsics[] = {
131         { "AddWithSaturation", OP_PADDW_SAT_UN, SIMD_EMIT_BINARY },
132         { "LoadAligned", 0, SIMD_EMIT_LOAD_ALIGNED },
133         { "ShiftRightArithmethic", OP_PSARW, SIMD_EMIT_SHIFT },
134         { "StoreAligned", 0, SIMD_EMIT_STORE_ALIGNED },
135         { "SubWithSaturation", OP_PSUBW_SAT_UN, SIMD_EMIT_BINARY },
136         { "UnpackHigh", OP_UNPACK_HIGHW, SIMD_EMIT_BINARY },
137         { "UnpackLow", OP_UNPACK_LOWW, SIMD_EMIT_BINARY },
138         { "op_Addition", OP_PADDW, SIMD_EMIT_BINARY },
139         { "op_BitwiseAnd", OP_PAND, SIMD_EMIT_BINARY },
140         { "op_BitwiseOr", OP_POR, SIMD_EMIT_BINARY },
141         { "op_BitwiseXor", OP_PXOR, SIMD_EMIT_BINARY },
142         { "op_Explicit", 0, SIMD_EMIT_CAST },
143         { "op_LeftShift", OP_PSHLW, SIMD_EMIT_SHIFT },
144         { "op_Multiply", OP_PMULW, SIMD_EMIT_BINARY },
145         { "op_RightShift", OP_PSHRW, SIMD_EMIT_SHIFT },
146         { "op_Subtraction", OP_PSUBW, SIMD_EMIT_BINARY },
147 };
148
149 /*
150 Missing:
151 .ctor
152 getters
153 setters
154  */
155 static const SimdIntrinsc vector16b_intrinsics[] = {
156         { "AddWithSaturation", OP_PADDB_SAT_UN, SIMD_EMIT_BINARY },
157         { "LoadAligned", 0, SIMD_EMIT_LOAD_ALIGNED },
158         { "StoreAligned", 0, SIMD_EMIT_STORE_ALIGNED },
159         { "SubWithSaturation", OP_PSUBB_SAT_UN, SIMD_EMIT_BINARY },
160         { "UnpackHigh", OP_UNPACK_HIGHB, SIMD_EMIT_BINARY },
161         { "UnpackLow", OP_UNPACK_LOWB, SIMD_EMIT_BINARY },
162         { "op_Addition", OP_PADDB, SIMD_EMIT_BINARY },
163         { "op_BitwiseAnd", OP_PAND, SIMD_EMIT_BINARY },
164         { "op_BitwiseOr", OP_POR, SIMD_EMIT_BINARY },
165         { "op_BitwiseXor", OP_PXOR, SIMD_EMIT_BINARY },
166         { "op_Explicit", 0, SIMD_EMIT_CAST },
167         { "op_Subtraction", OP_PSUBB, SIMD_EMIT_BINARY },
168 };
169
170 static guint32 simd_supported_versions;
171
172 /*TODO match using number of parameters as well*/
173 static int
174 simd_intrinsic_compare_by_name (const void *key, const void *value)
175 {
176         return strncmp(key, ((SimdIntrinsc *)value)->name, SIMD_INTRINSIC_NAME_MAX);
177 }
178
179 typedef enum {
180         VREG_USED                               = 0x01,
181         VREG_HAS_XZERO_BB0              = 0x02,
182         VREG_HAS_OTHER_OP_BB0   = 0x04,
183         VREG_SINGLE_BB_USE              = 0x08,
184         VREG_MANY_BB_USE                = 0x10,
185 } KillFlags;
186
187 static inline int
188 get_ins_reg_by_idx (MonoInst *ins, int idx)
189 {
190         switch (idx) {
191         case 0: return ins->dreg;
192         case 1: return ins->sreg1;
193         case 2: return ins->sreg2;
194         }
195         return -1;
196 }
197
198 void
199 mono_simd_intrinsics_init ()
200 {
201         simd_supported_versions = mono_arch_cpu_enumerate_simd_versions ();
202 }
203 /*
204 This pass recalculate which vars need MONO_INST_INDIRECT.
205
206 We cannot do this for non SIMD vars since code like mono_get_vtable_var
207 uses MONO_INST_INDIRECT to signal that the variable must be stack allocated.
208 */
209 void
210 mono_simd_simplify_indirection (MonoCompile *cfg)
211 {
212         int i, max_vreg = 0;
213         MonoBasicBlock *bb, *first_bb = NULL, **target_bb;
214         MonoInst *ins;
215         char * vreg_flags;
216
217         for (i = 0; i < cfg->num_varinfo; i++) {
218                 MonoInst *var = cfg->varinfo [i];
219                 if (var->klass->simd_type) {
220                         // printf ("cleaning indirect flag for %d\n", var->dreg);
221                         var->flags &= ~MONO_INST_INDIRECT;
222                         max_vreg = MAX (var->dreg, max_vreg);
223                 }
224         }
225
226         for (bb = cfg->bb_entry; bb; bb = bb->next_bb) {
227                 if (!first_bb && bb->code)
228                         first_bb = bb;
229                 for (ins = bb->code; ins; ins = ins->next) {
230                         if (ins->opcode == OP_LDADDR) {
231                                 MonoInst *var = (MonoInst*)ins->inst_p0;
232                                 if (var->klass->simd_type) {
233                                         var->flags |= MONO_INST_INDIRECT;
234                                 }
235                         }
236                 }
237         }
238
239         DEBUG (printf ("[simd-simplify] max vreg is %d\n", max_vreg));
240         vreg_flags = g_malloc0 (max_vreg + 1);
241         target_bb = g_new0 (MonoBasicBlock*, max_vreg + 1);
242
243         for (i = 0; i < cfg->num_varinfo; i++) {
244                 MonoInst *var = cfg->varinfo [i];
245                 if (var->klass->simd_type && !(var->flags & (MONO_INST_INDIRECT|MONO_INST_VOLATILE))) {
246                         vreg_flags [var->dreg] = VREG_USED;
247                         DEBUG (printf ("[simd-simplify] processing var %d with vreg %d\n", i, var->dreg));
248                 }
249         }
250
251         /*Scan the first basic block looking xzeros not used*/
252         for (ins = first_bb->code; ins; ins = ins->next) {
253                 if (ins->opcode == OP_XZERO) {
254                         if (!(vreg_flags [ins->dreg] & VREG_HAS_OTHER_OP_BB0)) {
255                                 DEBUG (printf ("[simd-simplify] R%d has vzero: ", ins->dreg); mono_print_ins(ins));
256                                 vreg_flags [ins->dreg] |= VREG_HAS_XZERO_BB0;
257                         }
258                         continue;
259                 }
260                 for (i = 0; i < 3; ++i) {
261                         int reg = get_ins_reg_by_idx (ins, i);
262                         if (reg != -1 && reg <= max_vreg && vreg_flags [reg]) {
263                                 vreg_flags [reg] &= ~VREG_HAS_XZERO_BB0;
264                                 vreg_flags [reg] |= VREG_HAS_OTHER_OP_BB0;
265                                 DEBUG (printf ("[simd-simplify] R%d used: ", reg); mono_print_ins(ins));
266                         }
267                 }
268         }
269
270         if (IS_DEBUG_ON (cfg)) {
271                 for (i = 0; i < cfg->num_varinfo; i++) {
272                         MonoInst *var = cfg->varinfo [i];
273                         if (var->klass->simd_type) {
274                                 if ((vreg_flags [var->dreg] & VREG_HAS_XZERO_BB0))
275                                         DEBUG (printf ("[simd-simplify] R%d has xzero only\n", var->dreg));
276                                 if ((vreg_flags [var->dreg] & VREG_HAS_OTHER_OP_BB0))
277                                         DEBUG (printf ("[simd-simplify] R%d has other ops on bb0\n", var->dreg));
278                         }
279                 }
280         }
281
282         /*TODO stop here if no var is xzero only*/
283
284         /*
285         Scan all other bb and check if it has only one other use
286         Ideally this would be done after an extended bb formation pass
287
288         FIXME This pass could use dominator information to properly
289         place the XZERO on the bb that dominates all uses of the var,
290         but this will have zero effect with the current local reg alloc 
291         
292         TODO simply the use of flags.
293         */
294
295         for (bb = first_bb->next_bb; bb; bb = bb->next_bb) {
296                 for (ins = bb->code; ins; ins = ins->next) {
297                         for (i = 0; i < 3; ++i) {
298                                 int reg = get_ins_reg_by_idx (ins, i);
299                                 if (reg == -1 || reg > max_vreg || !(vreg_flags [reg] & VREG_HAS_XZERO_BB0) || target_bb [reg] == bb)
300                                         continue;
301
302                                 if (vreg_flags [reg] & VREG_SINGLE_BB_USE) {
303                                         vreg_flags [reg] &= ~VREG_SINGLE_BB_USE;
304                                         vreg_flags [reg] |= VREG_MANY_BB_USE;
305                                         DEBUG (printf ("[simd-simplify] R%d used by many bb: ", reg); mono_print_ins(ins));
306                                         break;
307                                 } else if (!(vreg_flags [reg] & VREG_MANY_BB_USE)) {
308                                         vreg_flags [reg] |= VREG_SINGLE_BB_USE;
309                                         target_bb [reg] = bb;
310                                         DEBUG (printf ("[simd-simplify] R%d first used by: ", reg); mono_print_ins(ins));
311                                         break;
312                                 }
313                         }
314                 }
315         }
316
317         for (i = 0; i < cfg->num_varinfo; i++) {
318                 MonoInst *var = cfg->varinfo [i];
319                 if (!var->klass->simd_type)
320                         continue;
321                 if ((vreg_flags [var->dreg] & VREG_SINGLE_BB_USE))
322                         DEBUG (printf ("[simd-simplify] R%d has single bb use\n", var->dreg));
323                 if ((vreg_flags [var->dreg] & VREG_MANY_BB_USE))
324                         DEBUG (printf ("[simd-simplify] R%d has many bb in use\n", var->dreg));
325
326                 if (!(vreg_flags [var->dreg] & VREG_SINGLE_BB_USE))
327                         continue;
328                 for (ins = target_bb [var->dreg]->code; ins; ins = ins->next) {
329                         /*We can, pretty much kill it.*/
330                         if (ins->dreg == var->dreg) {
331                                 break;
332                         } else if (ins->sreg1 == var->dreg || ins->sreg2 == var->dreg) {
333                                 MonoInst *tmp;
334                                 MONO_INST_NEW (cfg, tmp, OP_XZERO);
335                                 tmp->dreg = var->dreg;
336                                 tmp->type = STACK_VTYPE;
337                         tmp->klass = var->klass;
338                                 mono_bblock_insert_before_ins (target_bb [var->dreg], ins, tmp);
339                                 break;
340                         }
341                 }
342         }
343
344         for (ins = first_bb->code; ins; ins = ins->next) {
345                 if (ins->opcode == OP_XZERO && (vreg_flags [ins->dreg] & VREG_SINGLE_BB_USE))
346                         NULLIFY_INS (ins);
347         }
348
349         g_free (vreg_flags);
350         g_free (target_bb);
351 }
352
353 static int
354 get_simd_vreg (MonoCompile *cfg, MonoMethod *cmethod, MonoInst *src, gboolean is_this_ptr)
355 {
356         if (src->opcode == OP_XMOVE) {
357                 /*FIXME returning src->sreg1 breaks during regalloc */
358                 return src->dreg;
359         } else if (src->opcode == OP_LDADDR && is_this_ptr) {
360                 int res = ((MonoInst*)src->inst_p0)->dreg;
361                 NULLIFY_INS (src);
362                 return res;
363         } else if (src->opcode == OP_LOADX_MEMBASE) {
364                 return src->dreg;
365         } else if (src->klass && src->klass->simd_type) {
366                 return src->dreg;
367         }
368         g_warning ("get_simd_vreg:: could not infer source simd vreg for op");
369         mono_print_ins (src);
370         g_assert_not_reached ();
371 }
372
373 static MonoInst*
374 get_int_to_float_spill_area (MonoCompile *cfg)
375 {
376         if (!cfg->iconv_raw_var) {
377                 cfg->iconv_raw_var = mono_compile_create_var (cfg, &mono_defaults.int32_class->byval_arg, OP_LOCAL);
378                 cfg->iconv_raw_var->flags |= MONO_INST_VOLATILE; /*FIXME, use the don't regalloc flag*/
379         }       
380         return cfg->iconv_raw_var;
381 }
382
383 static MonoInst*
384 simd_intrinsic_emit_binary (const SimdIntrinsc *intrinsic, MonoCompile *cfg, MonoMethod *cmethod, MonoInst **args)
385 {
386         MonoInst* ins;
387         int left_vreg, right_vreg;
388
389         left_vreg = get_simd_vreg (cfg, cmethod, args [0], FALSE);
390         right_vreg = get_simd_vreg (cfg, cmethod, args [1], FALSE);
391         
392
393         MONO_INST_NEW (cfg, ins, intrinsic->opcode);
394         ins->klass = cmethod->klass;
395         ins->sreg1 = left_vreg;
396         ins->sreg2 = right_vreg;
397         ins->type = STACK_VTYPE;
398         ins->klass = cmethod->klass;
399         ins->dreg = alloc_ireg (cfg);
400         MONO_ADD_INS (cfg->cbb, ins);
401         return ins;
402 }
403
404 static MonoInst*
405 simd_intrinsic_emit_unary (const SimdIntrinsc *intrinsic, MonoCompile *cfg, MonoMethod *cmethod, MonoInst **args)
406 {
407         MonoInst* ins;
408         int vreg;
409         
410         vreg = get_simd_vreg (cfg, cmethod, args [0], FALSE);
411
412         MONO_INST_NEW (cfg, ins, intrinsic->opcode);
413         ins->klass = cmethod->klass;
414         ins->sreg1 = vreg;
415         ins->type = STACK_VTYPE;
416         ins->dreg = alloc_ireg (cfg);
417         MONO_ADD_INS (cfg->cbb, ins);
418         return ins;
419 }
420
421 static MonoInst*
422 simd_intrinsic_emit_getter (const SimdIntrinsc *intrinsic, MonoCompile *cfg, MonoMethod *cmethod, MonoInst **args)
423 {
424         MonoInst *tmp, *ins;
425         int vreg;
426         
427         vreg = get_simd_vreg (cfg, cmethod, args [0], TRUE);
428
429         if (intrinsic->opcode) {
430                 MONO_INST_NEW (cfg, ins, OP_SHUFLEPS);
431                 ins->klass = cmethod->klass;
432                 ins->sreg1 = vreg;
433                 ins->inst_c0 = intrinsic->opcode;
434                 ins->type = STACK_VTYPE;
435                 ins->dreg = vreg = alloc_ireg (cfg);
436                 MONO_ADD_INS (cfg->cbb, ins);
437         }
438
439         MONO_INST_NEW (cfg, tmp, OP_EXTRACT_I4);
440         tmp->klass = cmethod->klass;
441         tmp->sreg1 = vreg;
442         tmp->type = STACK_I4;
443         tmp->dreg = alloc_ireg (cfg);
444         MONO_ADD_INS (cfg->cbb, tmp);
445
446         MONO_INST_NEW (cfg, ins, OP_ICONV_TO_R8_RAW);
447         ins->klass = mono_defaults.single_class;
448         ins->sreg1 = tmp->dreg;
449         ins->type = STACK_R8;
450         ins->dreg = alloc_freg (cfg);
451         ins->backend.spill_var = get_int_to_float_spill_area (cfg);
452         MONO_ADD_INS (cfg->cbb, ins);   
453         return ins;
454 }
455
456 static MonoInst*
457 simd_intrinsic_emit_ctor (const SimdIntrinsc *intrinsic, MonoCompile *cfg, MonoMethod *cmethod, MonoInst **args)
458 {
459         MonoInst *ins;
460         int i;
461
462         for (i = 1; i < 5; ++i) {
463                 MONO_INST_NEW (cfg, ins, OP_PUSH_R4);
464                 ins->sreg1 = args [5 - i]->dreg;
465                 ins->klass = args [5 - i]->klass;
466                 MONO_ADD_INS (cfg->cbb, ins);
467         }
468
469         /*TODO replace with proper LOAD macro */
470         MONO_INST_NEW (cfg, ins, OP_LOADX_STACK);
471         ins->klass = cmethod->klass;
472         ins->type = STACK_VTYPE;
473         ins->dreg = get_simd_vreg (cfg, cmethod, args [0], TRUE);
474         MONO_ADD_INS (cfg->cbb, ins);
475
476         return ins;
477 }
478
479 static MonoInst*
480 simd_intrinsic_emit_cast (const SimdIntrinsc *intrinsic, MonoCompile *cfg, MonoMethod *cmethod, MonoInst **args)
481 {
482         MonoInst *ins;
483         int vreg;
484
485         vreg = get_simd_vreg (cfg, cmethod, args [0], FALSE);           
486
487         //TODO macroize this
488         MONO_INST_NEW (cfg, ins, OP_XMOVE);
489         ins->klass = cmethod->klass;
490         ins->type = STACK_VTYPE;
491         ins->sreg1 = vreg;
492         ins->dreg = alloc_ireg (cfg);
493         MONO_ADD_INS (cfg->cbb, ins);
494         return ins;
495 }
496
497 static MonoInst*
498
499 simd_intrinsic_emit_shift (const SimdIntrinsc *intrinsic, MonoCompile *cfg, MonoMethod *cmethod, MonoInst **args)
500 {
501         MonoInst *ins;
502         int vreg, vreg2 = -1, opcode = intrinsic->opcode;
503
504         vreg = get_simd_vreg (cfg, cmethod, args [0], FALSE);
505
506         if (args [1]->opcode != OP_ICONST) {
507                 MONO_INST_NEW (cfg, ins, OP_ICONV_TO_X);
508                 ins->klass = mono_defaults.int32_class;
509                 ins->sreg1 = args [1]->dreg;
510                 ins->type = STACK_I4;
511                 ins->dreg = vreg2 = alloc_ireg (cfg);
512                 MONO_ADD_INS (cfg->cbb, ins);
513
514                 ++opcode; /*The shift_reg version op is always +1 from the regular one.*/
515         }
516
517         MONO_INST_NEW (cfg, ins, opcode);
518         ins->klass = cmethod->klass;
519         ins->sreg1 = vreg;
520         ins->sreg2 = vreg2;
521
522         if (args [1]->opcode == OP_ICONST) {
523                 ins->inst_imm = args [1]->inst_c0;
524                 NULLIFY_INS (args [1]);
525         }
526
527         ins->type = STACK_VTYPE;
528         ins->dreg = alloc_ireg (cfg);
529         MONO_ADD_INS (cfg->cbb, ins);
530         return ins;
531 }
532
533
534 static MonoInst*
535 simd_intrinsic_emit_shuffle (const SimdIntrinsc *intrinsic, MonoCompile *cfg, MonoMethod *cmethod, MonoInst **args)
536 {
537         MonoInst *ins;
538         int vreg;
539
540         /*TODO Exposing shuffle is not a good thing as it's non obvious. We should come up with better abstractions*/
541
542         if (args [1]->opcode != OP_ICONST) {
543                 g_warning ("Vector4f:Shuffle with non literals is not yet supported");
544                 g_assert_not_reached ();
545         }
546         vreg = get_simd_vreg (cfg, cmethod, args [0], FALSE);
547         NULLIFY_INS (args [1]);
548
549         MONO_INST_NEW (cfg, ins, OP_SHUFLEPS);
550         ins->klass = cmethod->klass;
551         ins->sreg1 = vreg;
552         ins->inst_c0 = args [1]->inst_c0;
553         ins->type = STACK_VTYPE;
554         ins->dreg = alloc_ireg (cfg);
555         MONO_ADD_INS (cfg->cbb, ins);
556         return ins;
557 }
558
559 static MonoInst*
560 simd_intrinsic_emit_load_aligned (const SimdIntrinsc *intrinsic, MonoCompile *cfg, MonoMethod *cmethod, MonoInst **args)
561 {
562         MonoInst *ins;
563
564         MONO_INST_NEW (cfg, ins, OP_LOADX_ALIGNED_MEMBASE);
565         ins->klass = cmethod->klass;
566         ins->sreg1 = args [0]->dreg;
567         /*FIXME, shouldn't use use ->inst_offset?*/
568         ins->type = STACK_VTYPE;
569         ins->dreg = alloc_ireg (cfg);
570         MONO_ADD_INS (cfg->cbb, ins);
571         return ins;
572 }
573
574 static MonoInst*
575 simd_intrinsic_emit_store_aligned (const SimdIntrinsc *intrinsic, MonoCompile *cfg, MonoMethod *cmethod, MonoInst **args)
576 {
577         MonoInst *ins;
578         int vreg;
579
580         vreg = get_simd_vreg (cfg, cmethod, args [0], FALSE);
581
582         MONO_INST_NEW (cfg, ins, OP_STOREX_ALIGNED_MEMBASE_REG);
583         ins->klass = cmethod->klass;
584         ins->dreg = args [0]->dreg;
585         ins->inst_offset = args [0]->inst_offset;
586         ins->sreg1 = vreg;
587         ins->type = STACK_VTYPE;
588         MONO_ADD_INS (cfg->cbb, ins);
589         return ins;
590 }
591
592
593 static MonoInst*
594 emit_intrinsics (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSignature *fsig, MonoInst **args, const SimdIntrinsc *intrinsics, guint32 size)
595 {
596         const SimdIntrinsc * result = bsearch (cmethod->name, intrinsics, size, sizeof (SimdIntrinsc), &simd_intrinsic_compare_by_name);
597         if (!result) {
598                 DEBUG (printf ("function doesn't have a simd intrinsic %s::%s/%d\n", cmethod->klass->name, cmethod->name, fsig->param_count));
599                 return NULL;
600         }
601         if (IS_DEBUG_ON (cfg)) {
602                 int i, max;
603                 printf ("found call to intrinsic %s::%s/%d -> %s\n", cmethod->klass->name, cmethod->name, fsig->param_count, result->name);
604                 max = fsig->param_count + fsig->hasthis;
605                 for (i = 0; i < max; ++i) {
606                         printf ("param %d:  ", i);
607                         mono_print_ins (args [i]);
608                 }
609         }
610
611         switch (result->simd_emit_mode) {
612         case SIMD_EMIT_BINARY_SSE3:
613                 if (simd_supported_versions & SIMD_VERSION_SSE3)
614                         return simd_intrinsic_emit_binary (result, cfg, cmethod, args);
615                 return NULL;
616         case SIMD_EMIT_BINARY:
617                 return simd_intrinsic_emit_binary (result, cfg, cmethod, args);
618         case SIMD_EMIT_UNARY:
619                 return simd_intrinsic_emit_unary (result, cfg, cmethod, args);
620         case SIMD_EMIT_GETTER:
621                 return simd_intrinsic_emit_getter (result, cfg, cmethod, args);
622         case SIMD_EMIT_CTOR:
623                 return simd_intrinsic_emit_ctor (result, cfg, cmethod, args);
624         case SIMD_EMIT_CAST:
625                 return simd_intrinsic_emit_cast (result, cfg, cmethod, args);
626         case SIMD_EMIT_SHUFFLE:
627                 return simd_intrinsic_emit_shuffle (result, cfg, cmethod, args); 
628         case SIMD_EMIT_SHIFT:
629                 return simd_intrinsic_emit_shift (result, cfg, cmethod, args);
630         case SIMD_EMIT_LOAD_ALIGNED:
631                 return simd_intrinsic_emit_load_aligned (result, cfg, cmethod, args);
632         case SIMD_EMIT_STORE_ALIGNED:
633                 return simd_intrinsic_emit_store_aligned (result, cfg, cmethod, args);
634         }
635         g_assert_not_reached ();
636 }
637
638 MonoInst*
639 mono_emit_simd_intrinsics (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSignature *fsig, MonoInst **args)
640 {
641         if (!cmethod->klass->simd_type)
642                 return NULL;
643         cfg->uses_simd_intrinsics = 1;
644         if (!strcmp ("Vector4f", cmethod->klass->name))
645                 return emit_intrinsics (cfg, cmethod, fsig, args, vector4f_intrinsics, sizeof (vector4f_intrinsics) / sizeof (SimdIntrinsc));
646         if (!strcmp ("Vector4u", cmethod->klass->name))
647                 return emit_intrinsics (cfg, cmethod, fsig, args, vector4u_intrinsics, sizeof (vector4u_intrinsics) / sizeof (SimdIntrinsc));
648         if (!strcmp ("Vector8us", cmethod->klass->name))
649                 return emit_intrinsics (cfg, cmethod, fsig, args, vector8us_intrinsics, sizeof (vector8us_intrinsics) / sizeof (SimdIntrinsc));
650         if (!strcmp ("Vector16b", cmethod->klass->name))
651                 return emit_intrinsics (cfg, cmethod, fsig, args, vector16b_intrinsics, sizeof (vector16b_intrinsics) / sizeof (SimdIntrinsc));
652         return NULL;
653 }
654
655 #endif