Implement SIMD support for the Vector2/3/4 classes in System.Numerics. (#3765)
authorZoltan Varga <vargaz@gmail.com>
Sun, 20 Nov 2016 15:57:07 +0000 (10:57 -0500)
committerGitHub <noreply@github.com>
Sun, 20 Nov 2016 15:57:07 +0000 (10:57 -0500)
* [simd] Rename SimdIntrinsc->SimdIntrinsic.

* [ji] Add beginnings of support for the SIMD types in the System.Numerics assembly.

Add intrinsics for the methods marked [JitIntrinsic] in the System.Numerics.Vector2 class. Generalize some code in simd-intrinsic.c
so it works both with System.Numerics and Mono.Simd types.

* [jit] Add support for System.Numerics.Vector4.

* [jit] Add support for System.Numerics.Vector3.

* [jit] Add tests for the JIT intrinsics in System.Numerics.Vectors.

* [jit] Implement System.Numerics.Vector.IsHardwareAccelerated intrinsics.

* [jit] Fix the implementation of System.Numerics.Vector.IsHardwareAccelerated, it should return true.

* [jit] Implement some support for the SIMD intrinsics in System.Numerics.Vector<T>.

* [jit] Disable the usage of the Vector SIMD intrinsics when using r4fp until support for it is implemented.

* [jit] Fix llvm support for the Vector<T> intrinsics.

* [jit] Disable the Vector<T> tests since they don't compile yet.

* [jit] Add an optimization for the typeof(T)==typeof(<concrete type>) construct common in some generics code.

* [simd] Only return true from IsHardwareAccelerated () if simd_supported_versions is non-zero.

* [simd] Add support for add/sub/mul/div operations on Vector<T>.

16 files changed:
mono/metadata/class.c
mono/mini/Makefile.am.in
mono/mini/basic-vectors.cs [new file with mode: 0644]
mono/mini/cfold.c
mono/mini/cpu-amd64.md
mono/mini/ir-emit.h
mono/mini/local-propagation.c
mono/mini/method-to-ir.c
mono/mini/mini-amd64.c
mono/mini/mini-codegen.c
mono/mini/mini-llvm.c
mono/mini/mini-ops.h
mono/mini/mini.c
mono/mini/mini.h
mono/mini/simd-intrinsics.c
mono/mini/simd-methods.h

index dc2c6dbe7145738a19bf6e77c12403069d15c7a9..ec04bffa5511edb696d32464a0416560f974463a 100644 (file)
@@ -1970,6 +1970,9 @@ mono_class_layout_fields (MonoClass *klass, int base_instance_size, int packing_
                                real_size = field_offsets [i] + size;
                        }
 
+                       /* Make SIMD types as big as a SIMD register since they can be stored into using simd stores */
+                       if (klass->simd_type)
+                               real_size = MAX (real_size, sizeof (MonoObject) + 16);
                        instance_size = MAX (real_size, instance_size);
        
                        if (instance_size & (min_align - 1)) {
@@ -5981,6 +5984,12 @@ mono_class_create_from_typedef (MonoImage *image, guint32 type_token, MonoError
        if (klass->image->assembly_name && !strcmp (klass->image->assembly_name, "Mono.Simd") && !strcmp (nspace, "Mono.Simd")) {
                if (!strncmp (name, "Vector", 6))
                        klass->simd_type = !strcmp (name + 6, "2d") || !strcmp (name + 6, "2ul") || !strcmp (name + 6, "2l") || !strcmp (name + 6, "4f") || !strcmp (name + 6, "4ui") || !strcmp (name + 6, "4i") || !strcmp (name + 6, "8s") || !strcmp (name + 6, "8us") || !strcmp (name + 6, "16b") || !strcmp (name + 6, "16sb");
+       } else if (klass->image->assembly_name && !strcmp (klass->image->assembly_name, "System.Numerics") && !strcmp (nspace, "System.Numerics")) {
+               if (!strcmp (name, "Vector2") || !strcmp (name, "Vector3") || !strcmp (name, "Vector4"))
+                       klass->simd_type = 1;
+       } else if (klass->image->assembly_name && !strcmp (klass->image->assembly_name, "System.Numerics.Vectors") && !strcmp (nspace, "System.Numerics")) {
+               if (!strcmp (name, "Vector`1"))
+                       klass->simd_type = 1;
        }
 
        mono_loader_unlock ();
@@ -6086,6 +6095,7 @@ mono_generic_class_get_class (MonoGenericClass *gclass)
        klass->this_arg.byref = TRUE;
        klass->enumtype = gklass->enumtype;
        klass->valuetype = gklass->valuetype;
+       klass->simd_type = gklass->simd_type;
 
        klass->cast_class = klass->element_class = klass;
 
index 488b3cd0c4e2b9baad71d2b42eeb3599f74e1bdb..bba551cc940b77fbfda4a2d822842b3630def3cd 100755 (executable)
@@ -484,12 +484,13 @@ test_sources =                    \
        generics.cs             \
        generics-variant-types.il\
        basic-simd.cs \
+       basic-vectors.cs \
        aot-tests.cs \
        gc-test.cs \
        gshared.cs
 
 
-regtests_UNIVERSAL=basic.exe basic-float.exe basic-long.exe basic-calls.exe objects.exe arrays.exe basic-math.exe exceptions.exe iltests.exe devirtualization.exe generics.exe basic-simd.exe
+regtests_UNIVERSAL=basic.exe basic-float.exe basic-long.exe basic-calls.exe objects.exe arrays.exe basic-math.exe exceptions.exe iltests.exe devirtualization.exe generics.exe basic-simd.exe basic-vectors.exe
 
 if INSTALL_MOBILE_STATIC
 regtests= \
@@ -622,6 +623,9 @@ CSFLAGS = -unsafe -nowarn:0219,0169,0414,0649
 basic-simd.exe: basic-simd.cs TestDriver.dll
        $(MCS) -out:$@ $(CSFLAGS) $< -r:TestDriver.dll -r:$(CLASS)/Mono.Simd.dll
 
+basic-vectors.exe: basic-vectors.cs TestDriver.dll
+       $(MCS) -out:$@ $(CSFLAGS) $< -r:TestDriver.dll -r:$(CLASS)/System.Numerics.dll
+
 nacl.exe: nacl.cs TestDriver.dll
        $(MCS) -out:$@ $(CSFLAGS) $< -r:TestDriver.dll -r:$(CLASS)/Mono.Simd.dll
 
diff --git a/mono/mini/basic-vectors.cs b/mono/mini/basic-vectors.cs
new file mode 100644 (file)
index 0000000..18c1bc0
--- /dev/null
@@ -0,0 +1,1397 @@
+using System;
+using System.Numerics;
+using System.Runtime.CompilerServices;
+
+/*
+ * Tests for the SIMD intrinsics in the System.Numerics.Vectors assembly.
+ */
+public class VectorTests {
+
+#if !MOBILE
+       public static int Main (string[] args) {
+               return TestDriver.RunTests (typeof (VectorTests), args);
+       }
+#endif
+
+       //
+       // Vector2 tests
+       //
+
+       public static int test_0_vector2_ctor_1 () {
+               var v = new Vector2 (1.0f);
+
+               if (v.X != 1.0f)
+                       return 1;
+               if (v.Y != 1.0f)
+                       return 2;
+               return 0;
+       }
+
+       public static int test_0_vector2_ctor_2 () {
+               var v = new Vector2 (1.0f, 2.0f);
+
+               if (v.X != 1.0f)
+                       return 1;
+               if (v.Y != 2.0f)
+                       return 2;
+               return 0;
+       }
+
+       [MethodImplAttribute (MethodImplOptions.NoInlining)]
+       public static bool vector2_equals (Vector2 v1, Vector2 v2) {
+               // cmpeqps+pmovmskb
+               return v1.Equals (v2);
+       }
+
+       public static int test_0_vector2_equals () {
+               var v1 = new Vector2 (1.0f, 2.0f);
+               var v2 = new Vector2 (2.0f, 2.0f);
+
+               if (vector2_equals (v1, v2))
+                       return 1;
+               if (!vector2_equals (v1, v1))
+                       return 2;
+               return 0;
+       }
+
+       [MethodImplAttribute (MethodImplOptions.NoInlining)]
+       public static float vector2_dot (Vector2 v1, Vector2 v2) {
+               return Vector2.Dot (v1, v2);
+       }
+
+       public static int test_0_vector2_dot () {
+               var v1 = new Vector2 (1.0f, 1.0f);
+               var v2 = new Vector2 (2.0f, 2.0f);
+
+               float f = vector2_dot (v1, v2);
+               if (f != 4.0f)
+                       return 1;
+               f = vector2_dot (v1, v1);
+               if (f != 2.0f)
+                       return 2;
+               return 0;
+       }
+
+       [MethodImplAttribute (MethodImplOptions.NoInlining)]
+       public static Vector2 vector2_min (Vector2 v1, Vector2 v2) {
+               return Vector2.Min (v1, v2);
+       }
+
+       public static int test_0_vector2_min () {
+               var v1 = new Vector2 (1.0f, 1.0f);
+               var v2 = new Vector2 (2.0f, 2.0f);
+
+               var v3 = vector2_min (v1, v2);
+               if (v3.X != 1.0f || v3.Y != 1.0f)
+                       return 1;
+               v3 = vector2_min (v2, v2);
+               if (v3.X != 2.0f || v3.Y != 2.0f)
+                       return 2;
+               return 0;
+       }
+
+       [MethodImplAttribute (MethodImplOptions.NoInlining)]
+       public static Vector2 vector2_max (Vector2 v1, Vector2 v2) {
+               return Vector2.Max (v1, v2);
+       }
+
+       public static int test_0_vector2_max () {
+               var v1 = new Vector2 (1.0f, 1.0f);
+               var v2 = new Vector2 (2.0f, 2.0f);
+
+               var v3 = vector2_max (v1, v2);
+               if (v3.X != 2.0f || v3.Y != 2.0f)
+                       return 1;
+               v3 = vector2_min (v1, v1);
+               if (v3.X != 1.0f || v3.Y != 1.0f)
+                       return 2;
+               return 0;
+       }
+
+       [MethodImplAttribute (MethodImplOptions.NoInlining)]
+       public static Vector2 vector2_abs (Vector2 v1) {
+               return Vector2.Abs (v1);
+       }
+
+       public static int test_0_vector2_abs () {
+               var v1 = new Vector2 (-1.0f, -2.0f);
+               var v2 = new Vector2 (1.0f, 2.0f);
+
+               var v3 = vector2_abs (v1);
+               if (v3.X != 1.0f || v3.Y != 2.0f)
+                       return 1;
+               v3 = vector2_abs (v2);
+               if (v3.X != 1.0f || v3.Y != 2.0f)
+                       return 2;
+               return 0;
+       }
+
+       [MethodImplAttribute (MethodImplOptions.NoInlining)]
+       public static Vector2 vector2_sqrt (Vector2 v1) {
+               return Vector2.SquareRoot (v1);
+       }
+
+       public static int test_0_vector2_sqrt () {
+               var v1 = new Vector2 (1.0f, 0.0f);
+
+               var v3 = vector2_sqrt (v1);
+               if (v3.X != 1.0f || v3.Y != 0.0f)
+                       return 1;
+               return 0;
+       }
+
+       [MethodImplAttribute (MethodImplOptions.NoInlining)]
+       public static Vector2 vector2_add (Vector2 v1, Vector2 v2) {
+               return v1 + v2;
+       }
+
+       public static int test_0_vector2_add () {
+               var v1 = new Vector2 (1.0f, 2.0f);
+               var v2 = new Vector2 (3.0f, 4.0f);
+
+               var v3 = vector2_add (v1, v2);
+               if (v3.X != 4.0f || v3.Y != 6.0f)
+                       return 1;
+               return 0;
+       }
+
+       [MethodImplAttribute (MethodImplOptions.NoInlining)]
+       public static Vector2 vector2_sub (Vector2 v1, Vector2 v2) {
+               return v1 - v2;
+       }
+
+       public static int test_0_vector2_sub () {
+               var v1 = new Vector2 (1.0f, 2.0f);
+               var v2 = new Vector2 (3.0f, 5.0f);
+
+               var v3 = vector2_sub (v2, v1);
+               if (v3.X != 2.0f || v3.Y != 3.0f)
+                       return 1;
+               return 0;
+       }
+
+       [MethodImplAttribute (MethodImplOptions.NoInlining)]
+       public static Vector2 vector2_mul (Vector2 v1, Vector2 v2) {
+               return v1 * v2;
+       }
+
+       public static int test_0_vector2_mul () {
+               var v1 = new Vector2 (1.0f, 2.0f);
+               var v2 = new Vector2 (3.0f, 5.0f);
+
+               var v3 = vector2_mul (v2, v1);
+               if (v3.X != 3.0f || v3.Y != 10.0f)
+                       return 1;
+               return 0;
+       }
+
+       [MethodImplAttribute (MethodImplOptions.NoInlining)]
+       public static Vector2 vector2_mul_left (float v1, Vector2 v2) {
+               return v1 * v2;
+       }
+
+       public static int test_0_vector2_mul_left () {
+               var v1 = new Vector2 (3.0f, 5.0f);
+
+               var v3 = vector2_mul_left (2.0f, v1);
+               if (v3.X != 6.0f || v3.Y != 10.0f)
+                       return 1;
+               return 0;
+       }
+
+       [MethodImplAttribute (MethodImplOptions.NoInlining)]
+       public static Vector2 vector2_mul_right (Vector2 v1, float v2) {
+               return v1 * v2;
+       }
+
+       public static int test_0_vector2_mul_right () {
+               var v1 = new Vector2 (3.0f, 5.0f);
+
+               var v3 = vector2_mul_right (v1, 2.0f);
+               if (v3.X != 6.0f || v3.Y != 10.0f)
+                       return 1;
+               return 0;
+       }
+
+       [MethodImplAttribute (MethodImplOptions.NoInlining)]
+       public static Vector2 vector2_div (Vector2 v1, Vector2 v2) {
+               return v1 / v2;
+       }
+
+       public static int test_0_vector2_div () {
+               var v1 = new Vector2 (9.0f, 10.0f);
+               var v2 = new Vector2 (3.0f, 5.0f);
+
+               var v3 = vector2_div (v1, v2);
+               if (v3.X != 3.0f || v3.Y != 2.0f)
+                       return 1;
+               return 0;
+       }
+
+       [MethodImplAttribute (MethodImplOptions.NoInlining)]
+       public static Vector2 vector2_div_right (Vector2 v1, float v2) {
+               return v1 / v2;
+       }
+
+       public static int test_0_vector2_div_right () {
+               var v1 = new Vector2 (9.0f, 15.0f);
+
+               var v3 = vector2_div_right (v1, 3.0f);
+               if (v3.X != 3.0f || v3.Y != 5.0f)
+                       return 1;
+               return 0;
+       }
+
+       //
+       // Vector4 tests
+       //
+
+       public static int test_0_vector4_ctor_1 () {
+               var v = new Vector4 (1.0f);
+
+               if (v.X != 1.0f)
+                       return 1;
+               if (v.Y != 1.0f)
+                       return 2;
+               if (v.Z != 1.0f)
+                       return 3;
+               if (v.W != 1.0f)
+                       return 4;
+               return 0;
+       }
+
+       public static int test_0_vector4_ctor_2 () {
+               var v = new Vector4 (1.0f, 2.0f, 3.0f, 4.0f);
+
+               if (v.X != 1.0f)
+                       return 1;
+               if (v.Y != 2.0f)
+                       return 2;
+               if (v.Z != 3.0f)
+                       return 3;
+               if (v.W != 4.0f)
+                       return 4;
+               return 0;
+       }
+
+       [MethodImplAttribute (MethodImplOptions.NoInlining)]
+       public static bool vector4_equals (Vector4 v1, Vector4 v2) {
+               // cmpeqps+pmovmskb
+               return v1.Equals (v2);
+       }
+
+       public static int test_0_vector4_equals () {
+               var v1 = new Vector4 (1.0f, 2.0f, 3.0f, 4.0f);
+               var v2 = new Vector4 (2.0f, 2.0f, 2.0f, 2.0f);
+
+               if (vector4_equals (v1, v2))
+                       return 1;
+               if (!vector4_equals (v1, v1))
+                       return 2;
+               return 0;
+       }
+
+       [MethodImplAttribute (MethodImplOptions.NoInlining)]
+       public static float vector4_dot (Vector4 v1, Vector4 v2) {
+               return Vector4.Dot (v1, v2);
+       }
+
+       public static int test_0_vector4_dot () {
+               var v1 = new Vector4 (1.0f, 1.0f, 1.0f, 1.0f);
+               var v2 = new Vector4 (2.0f, 2.0f, 2.0f, 2.0f);
+
+               float f = vector4_dot (v1, v2);
+               if (f != 8.0f)
+                       return 1;
+               f = vector4_dot (v1, v1);
+               if (f != 4.0f)
+                       return 2;
+               return 0;
+       }
+
+       [MethodImplAttribute (MethodImplOptions.NoInlining)]
+       public static Vector4 vector4_min (Vector4 v1, Vector4 v2) {
+               return Vector4.Min (v1, v2);
+       }
+
+       public static int test_0_vector4_min () {
+               var v1 = new Vector4 (1.0f, 2.0f, 3.0f, 4.0f);
+               var v2 = new Vector4 (5.0f, 6.0f, 7.0f, 8.0f);
+
+               var v3 = vector4_min (v1, v2);
+               if (v3.X != 1.0f || v3.Y != 2.0f || v3.Z != 3.0f || v3.W != 4.0f)
+                       return 1;
+               v3 = vector4_min (v2, v2);
+               if (v3.X != 5.0f || v3.Y != 6.0f || v3.Z != 7.0f || v3.W != 8.0f)
+                       return 2;
+               return 0;
+       }
+
+       [MethodImplAttribute (MethodImplOptions.NoInlining)]
+       public static Vector4 vector4_max (Vector4 v1, Vector4 v2) {
+               return Vector4.Max (v1, v2);
+       }
+
+       public static int test_0_vector4_max () {
+               var v1 = new Vector4 (1.0f, 2.0f, 3.0f, 4.0f);
+               var v2 = new Vector4 (5.0f, 6.0f, 7.0f, 8.0f);
+
+               var v3 = vector4_max (v1, v2);
+               if (v3.X != 5.0f || v3.Y != 6.0f || v3.Z != 7.0f || v3.W != 8.0f)
+                       return 1;
+               v3 = vector4_max (v1, v1);
+               if (v3.X != 1.0f || v3.Y != 2.0f || v3.Z != 3.0f || v3.W != 4.0f)
+                       return 2;
+               return 0;
+       }
+
+       [MethodImplAttribute (MethodImplOptions.NoInlining)]
+       public static Vector4 vector4_abs (Vector4 v1) {
+               return Vector4.Abs (v1);
+       }
+
+       public static int test_0_vector4_abs () {
+               var v1 = new Vector4 (-1.0f, -2.0f, -3.0f, -4.0f);
+               var v2 = new Vector4 (1.0f, 2.0f, 3.0f, 4.0f);
+
+               var v3 = vector4_abs (v1);
+               if (v3.X != 1.0f || v3.Y != 2.0f || v3.Z != 3.0f || v3.W != 4.0f)
+                       return 1;
+               v3 = vector4_abs (v2);
+               if (v3.X != 1.0f || v3.Y != 2.0f || v3.Z != 3.0f || v3.W != 4.0f)
+                       return 2;
+               return 0;
+       }
+
+       [MethodImplAttribute (MethodImplOptions.NoInlining)]
+       public static Vector4 vector4_sqrt (Vector4 v1) {
+               return Vector4.SquareRoot (v1);
+       }
+
+       public static int test_0_vector4_sqrt () {
+               var v1 = new Vector4 (1.0f, 0.0f, 1.0f, 0.0f);
+
+               var v3 = vector4_sqrt (v1);
+               if (v3.X != 1.0f || v3.Y != 0.0f || v3.Z != 1.0f || v3.W != 0.0f)
+                       return 1;
+               return 0;
+       }
+
+       [MethodImplAttribute (MethodImplOptions.NoInlining)]
+       public static Vector4 vector4_add (Vector4 v1, Vector4 v2) {
+               return v1 + v2;
+       }
+
+       public static int test_0_vector4_add () {
+               var v1 = new Vector4 (1.0f, 2.0f, 3.0f, 4.0f);
+               var v2 = new Vector4 (5.0f, 6.0f, 7.0f, 8.0f);
+
+               var v3 = vector4_add (v1, v2);
+               if (v3.X != 6.0f || v3.Y != 8.0f || v3.Z != 10.0f || v3.W != 12.0f)
+                       return 1;
+               return 0;
+       }
+
+       [MethodImplAttribute (MethodImplOptions.NoInlining)]
+       public static Vector4 vector4_sub (Vector4 v1, Vector4 v2) {
+               return v1 - v2;
+       }
+
+       public static int test_0_vector4_sub () {
+               var v1 = new Vector4 (1.0f, 2.0f, 3.0f, 4.0f);
+               var v2 = new Vector4 (3.0f, 5.0f, 7.0f, 9.0f);
+
+               var v3 = vector4_sub (v2, v1);
+               if (v3.X != 2.0f || v3.Y != 3.0f || v3.Z != 4.0f || v3.W != 5.0f)
+                       return 1;
+               return 0;
+       }
+
+       [MethodImplAttribute (MethodImplOptions.NoInlining)]
+       public static Vector4 vector4_mul (Vector4 v1, Vector4 v2) {
+               return v1 * v2;
+       }
+
+       public static int test_0_vector4_mul () {
+               var v1 = new Vector4 (1.0f, 2.0f, 3.0f, 4.0f);
+               var v2 = new Vector4 (3.0f, 5.0f, 6.0f, 7.0f);
+
+               var v3 = vector4_mul (v2, v1);
+               if (v3.X != 3.0f || v3.Y != 10.0f || v3.Z != 18.0f || v3.W != 28.0f)
+                       return 1;
+               return 0;
+       }
+
+       [MethodImplAttribute (MethodImplOptions.NoInlining)]
+       public static Vector4 vector4_mul_left (float v1, Vector4 v2) {
+               return v1 * v2;
+       }
+
+       public static int test_0_vector4_mul_left () {
+               var v1 = new Vector4 (3.0f, 5.0f, 6.0f, 7.0f);
+
+               var v3 = vector4_mul_left (2.0f, v1);
+               if (v3.X != 6.0f || v3.Y != 10.0f || v3.Z != 12.0f || v3.W != 14.0f)
+                       return 1;
+               return 0;
+       }
+
+       [MethodImplAttribute (MethodImplOptions.NoInlining)]
+       public static Vector4 vector4_mul_right (Vector4 v1, float v2) {
+               return v1 * v2;
+       }
+
+       public static int test_0_vector4_mul_right () {
+               var v1 = new Vector4 (3.0f, 5.0f, 6.0f, 7.0f);
+
+               var v3 = vector4_mul_right (v1, 2.0f);
+               if (v3.X != 6.0f || v3.Y != 10.0f || v3.Z != 12.0f || v3.W != 14.0f)
+                       return 1;
+               return 0;
+       }
+
+       [MethodImplAttribute (MethodImplOptions.NoInlining)]
+       public static Vector4 vector4_div (Vector4 v1, Vector4 v2) {
+               return v1 / v2;
+       }
+
+       public static int test_0_vector4_div () {
+               var v1 = new Vector4 (9.0f, 10.0f, 12.0f, 21.0f);
+               var v2 = new Vector4 (3.0f, 5.0f, 6.0f, 7.0f);
+
+               var v3 = vector4_div (v1, v2);
+               if (v3.X != 3.0f || v3.Y != 2.0f || v3.Z != 2.0f || v3.W != 3.0f)
+                       return 1;
+               return 0;
+       }
+
+       [MethodImplAttribute (MethodImplOptions.NoInlining)]
+       public static Vector4 vector4_div_right (Vector4 v1, float v2) {
+               return v1 / v2;
+       }
+
+       public static int test_0_vector4_div_right () {
+               var v1 = new Vector4 (9.0f, 15.0f, 21.0f, 30.0f);
+
+               var v3 = vector4_div_right (v1, 3.0f);
+               if (v3.X != 3.0f || v3.Y != 5.0f || v3.Z != 7.0f || v3.W != 10.0f)
+                       return 1;
+               return 0;
+       }
+
+       public static int test_0_vector4_length () {
+               var v = new Vector4 (2.0f, 2.0f, 2.0f, 2.0f);
+               return v.Length () == 4.0f ? 0 : 1;
+       }
+
+       //
+       // Vector3 tests
+       //
+
+       public static int test_0_vector3_ctor_1 () {
+               var v = new Vector3 (1.0f);
+
+               if (v.X != 1.0f)
+                       return 1;
+               if (v.Y != 1.0f)
+                       return 2;
+               if (v.Z != 1.0f)
+                       return 3;
+               return 0;
+       }
+
+       public static int test_0_vector3_ctor_2 () {
+               var v = new Vector3 (1.0f, 2.0f, 3.0f);
+
+               if (v.X != 1.0f)
+                       return 1;
+               if (v.Y != 2.0f)
+                       return 2;
+               if (v.Z != 3.0f)
+                       return 3;
+               return 0;
+       }
+
+       [MethodImplAttribute (MethodImplOptions.NoInlining)]
+       public static bool vector3_equals (Vector3 v1, Vector3 v2) {
+               // cmpeqps+pmovmskb
+               return v1.Equals (v2);
+       }
+
+       public static int test_0_vector3_equals () {
+               var v1 = new Vector3 (1.0f, 2.0f, 3.0f);
+               var v2 = new Vector3 (2.0f, 2.0f, 2.0f);
+
+               if (vector3_equals (v1, v2))
+                       return 1;
+               if (!vector3_equals (v1, v1))
+                       return 2;
+               return 0;
+       }
+
+       [MethodImplAttribute (MethodImplOptions.NoInlining)]
+       public static float vector3_dot (Vector3 v1, Vector3 v2) {
+               return Vector3.Dot (v1, v2);
+       }
+
+       public static int test_0_vector3_dot () {
+               var v1 = new Vector3 (1.0f, 1.0f, 1.0f);
+               var v2 = new Vector3 (2.0f, 2.0f, 2.0f);
+
+               float f = vector3_dot (v1, v2);
+               if (f != 6.0f)
+                       return 1;
+               f = vector3_dot (v1, v1);
+               if (f != 3.0f)
+                       return 2;
+               return 0;
+       }
+
+       [MethodImplAttribute (MethodImplOptions.NoInlining)]
+       public static Vector3 vector3_min (Vector3 v1, Vector3 v2) {
+               return Vector3.Min (v1, v2);
+       }
+
+       public static int test_0_vector3_min () {
+               var v1 = new Vector3 (1.0f, 2.0f, 3.0f);
+               var v2 = new Vector3 (5.0f, 6.0f, 7.0f);
+
+               var v3 = vector3_min (v1, v2);
+               if (v3.X != 1.0f || v3.Y != 2.0f || v3.Z != 3.0f)
+                       return 1;
+               v3 = vector3_min (v2, v2);
+               if (v3.X != 5.0f || v3.Y != 6.0f || v3.Z != 7.0f)
+                       return 2;
+               return 0;
+       }
+
+       [MethodImplAttribute (MethodImplOptions.NoInlining)]
+       public static Vector3 vector3_max (Vector3 v1, Vector3 v2) {
+               return Vector3.Max (v1, v2);
+       }
+
+       public static int test_0_vector3_max () {
+               var v1 = new Vector3 (1.0f, 2.0f, 3.0f);
+               var v2 = new Vector3 (5.0f, 6.0f, 7.0f);
+
+               var v3 = vector3_max (v1, v2);
+               if (v3.X != 5.0f || v3.Y != 6.0f || v3.Z != 7.0f)
+                       return 1;
+               v3 = vector3_max (v1, v1);
+               if (v3.X != 1.0f || v3.Y != 2.0f || v3.Z != 3.0f)
+                       return 2;
+               return 0;
+       }
+
+       [MethodImplAttribute (MethodImplOptions.NoInlining)]
+       public static Vector3 vector3_abs (Vector3 v1) {
+               return Vector3.Abs (v1);
+       }
+
+       public static int test_0_vector3_abs () {
+               var v1 = new Vector3 (-1.0f, -2.0f, -3.0f);
+               var v2 = new Vector3 (1.0f, 2.0f, 3.0f);
+
+               var v3 = vector3_abs (v1);
+               if (v3.X != 1.0f || v3.Y != 2.0f || v3.Z != 3.0f)
+                       return 1;
+               v3 = vector3_abs (v2);
+               if (v3.X != 1.0f || v3.Y != 2.0f || v3.Z != 3.0f)
+                       return 2;
+               return 0;
+       }
+
+       [MethodImplAttribute (MethodImplOptions.NoInlining)]
+       public static Vector3 vector3_sqrt (Vector3 v1) {
+               return Vector3.SquareRoot (v1);
+       }
+
+       public static int test_0_vector3_sqrt () {
+               var v1 = new Vector3 (1.0f, 0.0f, 1.0f);
+
+               var v3 = vector3_sqrt (v1);
+               if (v3.X != 1.0f || v3.Y != 0.0f || v3.Z != 1.0f)
+                       return 1;
+               return 0;
+       }
+
+       [MethodImplAttribute (MethodImplOptions.NoInlining)]
+       public static Vector3 vector3_add (Vector3 v1, Vector3 v2) {
+               return v1 + v2;
+       }
+
+       public static int test_0_vector3_add () {
+               var v1 = new Vector3 (1.0f, 2.0f, 3.0f);
+               var v2 = new Vector3 (5.0f, 6.0f, 7.0f);
+
+               var v3 = vector3_add (v1, v2);
+               if (v3.X != 6.0f || v3.Y != 8.0f || v3.Z != 10.0f)
+                       return 1;
+               return 0;
+       }
+
+       [MethodImplAttribute (MethodImplOptions.NoInlining)]
+       public static Vector3 vector3_sub (Vector3 v1, Vector3 v2) {
+               return v1 - v2;
+       }
+
+       public static int test_0_vector3_sub () {
+               var v1 = new Vector3 (1.0f, 2.0f, 3.0f);
+               var v2 = new Vector3 (3.0f, 5.0f, 7.0f);
+
+               var v3 = vector3_sub (v2, v1);
+               if (v3.X != 2.0f || v3.Y != 3.0f || v3.Z != 4.0f)
+                       return 1;
+               return 0;
+       }
+
+       [MethodImplAttribute (MethodImplOptions.NoInlining)]
+       public static Vector3 vector3_mul (Vector3 v1, Vector3 v2) {
+               return v1 * v2;
+       }
+
+       public static int test_0_vector3_mul () {
+               var v1 = new Vector3 (1.0f, 2.0f, 3.0f);
+               var v2 = new Vector3 (3.0f, 5.0f, 6.0f);
+
+               var v3 = vector3_mul (v2, v1);
+               if (v3.X != 3.0f || v3.Y != 10.0f || v3.Z != 18.0f)
+                       return 1;
+               return 0;
+       }
+
+       [MethodImplAttribute (MethodImplOptions.NoInlining)]
+       public static Vector3 vector3_mul_left (float v1, Vector3 v2) {
+               return v1 * v2;
+       }
+
+       public static int test_0_vector3_mul_left () {
+               var v1 = new Vector3 (3.0f, 5.0f, 6.0f);
+
+               var v3 = vector3_mul_left (2.0f, v1);
+               if (v3.X != 6.0f || v3.Y != 10.0f || v3.Z != 12.0f)
+                       return 1;
+               return 0;
+       }
+
+       [MethodImplAttribute (MethodImplOptions.NoInlining)]
+       public static Vector3 vector3_mul_right (Vector3 v1, float v2) {
+               return v1 * v2;
+       }
+
+       public static int test_0_vector3_mul_right () {
+               var v1 = new Vector3 (3.0f, 5.0f, 6.0f);
+
+               var v3 = vector3_mul_right (v1, 2.0f);
+               if (v3.X != 6.0f || v3.Y != 10.0f || v3.Z != 12.0f)
+                       return 1;
+               return 0;
+       }
+
+       [MethodImplAttribute (MethodImplOptions.NoInlining)]
+       public static Vector3 vector3_div (Vector3 v1, Vector3 v2) {
+               return v1 / v2;
+       }
+
+       public static int test_0_vector3_div () {
+               var v1 = new Vector3 (9.0f, 10.0f, 12.0f);
+               var v2 = new Vector3 (3.0f, 5.0f, 6.0f);
+
+               var v3 = vector3_div (v1, v2);
+               if (v3.X != 3.0f || v3.Y != 2.0f || v3.Z != 2.0f)
+                       return 1;
+               return 0;
+       }
+
+       [MethodImplAttribute (MethodImplOptions.NoInlining)]
+       public static Vector3 vector3_div_right (Vector3 v1, float v2) {
+               return v1 / v2;
+       }
+
+       public static int test_0_vector3_div_right () {
+               var v1 = new Vector3 (9.0f, 15.0f, 21.0f);
+
+               var v3 = vector3_div_right (v1, 3.0f);
+               if (v3.X != 3.0f || v3.Y != 5.0f || v3.Z != 7.0f)
+                       return 1;
+               return 0;
+       }
+
+#if FALSE
+       //
+       // Vector<T>
+       //
+
+       public static int test_0_vector_t_count () {
+               // This assumes a 16 byte simd register size
+               if (Vector<byte>.Count != 16)
+                       return 1;
+               if (Vector<short>.Count != 8)
+                       return 2;
+               if (Vector<int>.Count != 4)
+                       return 3;
+               if (Vector<long>.Count != 2)
+                       return 4;
+               return 0;
+       }
+
+       public static int test_0_vector_t_zero () {
+               var v = Vector<byte>.Zero;
+               for (int i = 0; i < Vector<byte>.Count; ++i)
+                       if (v [i] != 0)
+                               return 1;
+               var v2 = Vector<double>.Zero;
+               for (int i = 0; i < Vector<double>.Count; ++i)
+                       if (v2 [i] != 0.0)
+                               return 2;
+               return 0;
+       }
+
+       public static int test_0_vector_t_i1_accessor () {
+               var elems = new byte [] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 };
+               var v = new Vector<byte> (elems, 0);
+               for (int i = 0; i < Vector<byte>.Count; ++i)
+                       if (v [i] != i + 1)
+                               return 1;
+               if (v [0] != 1)
+                       return 2;
+               if (v [1] != 2)
+                       return 2;
+               if (v [15] != 16)
+                       return 2;
+               try {
+                       int r = v [-1];
+                       return 3;
+               } catch (IndexOutOfRangeException) {
+               }
+               try {
+                       int r = v [16];
+                       return 4;
+               } catch (IndexOutOfRangeException) {
+               }
+               return 0;
+       }
+
+       public static int test_0_vector_t_i4_accessor () {
+               var elems = new int [] { 1, 2, 3, 4 };
+               var v = new Vector<int> (elems, 0);
+               for (int i = 0; i < Vector<int>.Count; ++i)
+                       if (v [i] != i + 1)
+                               return 1;
+               if (v [0] != 1)
+                       return 2;
+               if (v [1] != 2)
+                       return 2;
+               if (v [3] != 4)
+                       return 2;
+               try {
+                       int r = v [-1];
+                       return 3;
+               } catch (IndexOutOfRangeException) {
+               }
+               try {
+                       int r = v [Vector<int>.Count];
+                       return 4;
+               } catch (IndexOutOfRangeException) {
+               }
+               return 0;
+       }
+
+       public static int test_0_vector_t_i8_accessor () {
+               var elems = new long [] { 1, 2 };
+               var v = new Vector<long> (elems, 0);
+               for (int i = 0; i < Vector<long>.Count; ++i)
+                       if (v [i] != i + 1)
+                               return 1;
+               if (v [0] != 1)
+                       return 2;
+               if (v [1] != 2)
+                       return 2;
+               try {
+                       var r = v [-1];
+                       return 3;
+               } catch (IndexOutOfRangeException) {
+               }
+               try {
+                       var r = v [Vector<long>.Count];
+                       return 4;
+               } catch (IndexOutOfRangeException) {
+               }
+               return 0;
+       }
+
+       public static int test_0_vector_t_r8_accessor () {
+               var elems = new double [] { 1.0, 2.0 };
+               var v = new Vector<double> (elems, 0);
+               for (int i = 0; i < Vector<double>.Count; ++i)
+                       if (v [i] != (double)i + 1.0)
+                               return 1;
+               if (v [0] != 1.0)
+                       return 2;
+               if (v [1] != 2.0)
+                       return 2;
+               try {
+                       var r = v [-1];
+                       return 3;
+               } catch (IndexOutOfRangeException) {
+               }
+               try {
+                       var r = v [Vector<double>.Count];
+                       return 4;
+               } catch (IndexOutOfRangeException) {
+               }
+               return 0;
+       }
+
+       public static int test_0_vector_t_i1_ctor_3 () {
+               var v = new Vector<byte> (5);
+               for (int i = 0; i < 16; ++i)
+                       if (v [i] != 5)
+                               return 1;
+               return 0;
+       }
+
+       public static int test_0_vector_t_i2_ctor_3 () {
+               var v = new Vector<short> (5);
+               for (int i = 0; i < 8; ++i)
+                       if (v [i] != 5)
+                               return 1;
+               return 0;
+       }
+
+       public static int test_0_vector_t_i4_ctor_3 () {
+               var v = new Vector<int> (0xffffeee);
+               for (int i = 0; i < 4; ++i)
+                       if (v [i] != 0xffffeee)
+                               return 1;
+               return 0;
+       }
+
+       public static int test_0_vector_t_i8_ctor_3 () {
+               var v = new Vector<long> (0xffffeeeeabcdefL);
+               for (int i = 0; i < 2; ++i)
+                       if (v [i] != 0xffffeeeeabcdefL)
+                               return 1;
+               return 0;
+       }
+
+       public static int test_0_vector_t_r4_ctor_3 () {
+               var v = new Vector<float> (0.5f);
+               for (int i = 0; i < 4; ++i) {
+                       if (v [i] != 0.5f)
+                               return 1;
+               }
+               return 0;
+       }
+
+       public static int test_0_vector_t_r8_ctor_3 () {
+               var v = new Vector<double> (0.5f);
+               for (int i = 0; i < 2; ++i) {
+                       if (v [i] != 0.5f)
+                               return 1;
+               }
+               return 0;
+       }
+
+       public static int test_0_vector_t_i1_ctor () {
+               var elems = new byte [] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
+                                                                 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18 };
+               var v = new Vector<byte> (elems, 16);
+               for (int i = 0; i < 16; ++i)
+                       if (v [i] != i)
+                               return 1;
+               try {
+                       var v2 = new Vector<byte> (elems, 16 + 4);
+                       return 2;
+               } catch (IndexOutOfRangeException) {
+               }
+               return 0;
+       }
+
+       public static int test_0_vector_t_i1_ctor_2 () {
+               var elems = new byte [] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 };
+               var v = new Vector<byte> (elems);
+               for (int i = 0; i < 16; ++i)
+                       if (v [i] != i + 1)
+                               return 1;
+               try {
+                       var elems2 = new byte [] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 };
+                       var v2 = new Vector<byte> (elems2);
+                       return 2;
+               } catch (IndexOutOfRangeException) {
+               }
+               return 0;
+       }
+
+       public static int test_0_vector_t_r4_equal () {
+               var elems1 = new float [4] { 1.0f, 1.0f, 1.0f, 1.0f };
+               var v1 = new Vector<float> (elems1);
+               var elems2 = new float [4] { 1.0f, 2.0f, 1.0f, 2.0f };
+               var v2 = new Vector<float> (elems2);
+               Vector<int> v = Vector.Equals (v1, v2);
+               if (v [0] != -1 || v [1] != 0 || v [2] != -1 || v [3] != 0)
+                       return 1;
+               return 0;
+       }
+
+       public static int test_0_vector_t_r8_equal () {
+               var elems1 = new double [] { 1.0f, 1.0f };
+               var v1 = new Vector<double> (elems1);
+               var elems2 = new double [] { 1.0f, 2.0f };
+               var v2 = new Vector<double> (elems2);
+               Vector<long> v = Vector.Equals (v1, v2);
+               if (v [0] != -1 || v [1] != 0)
+                       return 1;
+               return 0;
+       }
+
+       public static int test_0_vector_t_i8_equal () {
+               var elems1 = new long [] { 1, 1 };
+               var v1 = new Vector<long> (elems1);
+               var elems2 = new long [] { 1, 2 };
+               var v2 = new Vector<long> (elems2);
+               Vector<long> v = Vector.Equals (v1, v2);
+               if (v [0] != -1 || v [1] != 0)
+                       return 1;
+               return 0;
+       }
+
+       public static int test_0_vector_t_i4_equal () {
+               var elems1 = new int [] { 1, 1, 1, 1 };
+               var v1 = new Vector<int> (elems1);
+               var elems2 = new int [] { 1, 2, 1, 2 };
+               var v2 = new Vector<int> (elems2);
+               Vector<int> v = Vector.Equals (v1, v2);
+               if (v [0] != -1 || v [1] != 0 || v [2] != -1 || v[3] != 0)
+                       return 1;
+               return 0;
+       }
+
+       /*
+       public static int test_0_vector_t_u4_equal () {
+               var elems1 = new uint [] { 1, 1, 1, 1 };
+               var v1 = new Vector<uint> (elems1);
+               var elems2 = new uint [] { 1, 2, 1, 2 };
+               var v2 = new Vector<uint> (elems2);
+               Vector<uint> v = Vector.Equals (v1, v2);
+               if (v [0] != 0xffffffff || v [1] != 0 || v [2] != 0xffffffff || v[3] != 0)
+                       return 1;
+               return 0;
+       }
+       */
+
+       public static int test_0_vector_t_i2_equal () {
+               var elems1 = new short [] { 1, 1, 1, 1, 1, 1, 1, 1 };
+               var v1 = new Vector<short> (elems1);
+               var elems2 = new short [] { 1, 2, 1, 2, 1, 2, 1, 2 };
+               var v2 = new Vector<short> (elems2);
+               Vector<short> v = Vector.Equals (v1, v2);
+               for (int i = 0; i < Vector<short>.Count; ++i) {
+                       if (i % 2 == 0 && v [i] != -1)
+                               return 1;
+                       if (i % 2 == 1 && v [i] != 0)
+                               return 1;
+               }
+               return 0;
+       }
+
+       public static int test_0_vector_t_u2_equal () {
+               var elems1 = new ushort [] { 1, 1, 1, 1, 1, 1, 1, 1 };
+               var v1 = new Vector<ushort> (elems1);
+               var elems2 = new ushort [] { 1, 2, 1, 2, 1, 2, 1, 2 };
+               var v2 = new Vector<ushort> (elems2);
+               Vector<ushort> v = Vector.Equals (v1, v2);
+               for (int i = 0; i < Vector<ushort>.Count; ++i) {
+                       if (i % 2 == 0 && v [i] != 0xffff)
+                               return 1;
+                       if (i % 2 == 1 && v [i] != 0)
+                               return 1;
+               }
+               return 0;
+       }
+
+       public static int test_0_vector_t_i1_equal () {
+               var elems1 = new sbyte [] { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 };
+               var v1 = new Vector<sbyte> (elems1);
+               var elems2 = new sbyte [] { 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2 };
+               var v2 = new Vector<sbyte> (elems2);
+               Vector<sbyte> v = Vector.Equals (v1, v2);
+               for (int i = 0; i < Vector<sbyte>.Count; ++i) {
+                       if (i % 2 == 0 && v [i] != -1)
+                               return 1;
+                       if (i % 2 == 1 && v [i] != 0)
+                               return 1;
+               }
+               return 0;
+       }
+
+       public static int test_0_vector_t_u1_equal () {
+               var elems1 = new byte [] { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 };
+               var v1 = new Vector<byte> (elems1);
+               var elems2 = new byte [] { 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2 };
+               var v2 = new Vector<byte> (elems2);
+               Vector<byte> v = Vector.Equals (v1, v2);
+               for (int i = 0; i < Vector<byte>.Count; ++i) {
+                       if (i % 2 == 0 && v [i] != 0xff)
+                               return 1;
+                       if (i % 2 == 1 && v [i] != 0)
+                               return 1;
+               }
+               return 0;
+       }
+
+       /* op_Explicit () -> Vector<int32> */
+
+       public static int test_0_vector_t_cast_vector_int32 () {
+               var v1 = new Vector<long> (new long [] { 0x123456789abcdef0L, 0x23456789abcdef01L });
+               var v = (Vector<int>)v1;
+               if ((uint)v [0] != 0x9abcdef0 || (uint)v [1] != 0x12345678)
+                       return 1;
+               if ((uint)v [2] != 0xabcdef01 || (uint)v [3] != 0x23456789)
+                       return 2;
+               return 0;
+       }
+
+       /* Vector.GreaterThanOrEqual */
+
+       public static int test_0_vector_t_i1_ge () {
+               var elems1 = new sbyte [] { 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1 };
+               var v1 = new Vector<sbyte> (elems1);
+               var elems2 = new sbyte [] { 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2 };
+               var v2 = new Vector<sbyte> (elems2);
+               Vector<sbyte> v = Vector.GreaterThanOrEqual (v1, v2);
+               for (int i = 0; i < Vector<sbyte>.Count; ++i) {
+                       if (i % 2 == 0 && v [i] != -1)
+                               return 1;
+                       if (i % 2 == 1 && v [i] != 0)
+                               return 1;
+               }
+               return 0;
+       }
+
+       public static int test_0_vector_t_i2_ge () {
+               var elems1 = new short [] { 1, 1, 0, 1, 1, 1, 0, 1 };
+               var v1 = new Vector<short> (elems1);
+               var elems2 = new short [] { 0, 2, 0, 2, 0, 2, 0, 2 };
+               var v2 = new Vector<short> (elems2);
+               Vector<short> v = Vector.GreaterThanOrEqual (v1, v2);
+               for (int i = 0; i < Vector<short>.Count; ++i) {
+                       if (i % 2 == 0 && v [i] != -1)
+                               return 1;
+                       if (i % 2 == 1 && v [i] != 0)
+                               return 1;
+               }
+               return 0;
+       }
+
+       public static int test_0_vector_t_i4_ge () {
+               var elems1 = new int [] { 1, 1, 0, 1 };
+               var v1 = new Vector<int> (elems1);
+               var elems2 = new int [] { 0, 2, 0, 2 };
+               var v2 = new Vector<int> (elems2);
+               Vector<int> v = Vector.GreaterThanOrEqual (v1, v2);
+               if (v [0] != -1 || v [1] != 0 || v [2] != -1 || v[3] != 0)
+                       return 1;
+               return 0;
+       }
+
+       public static int test_0_vector_t_i8_ge () {
+               var elems1 = new long [] { 1, 1 };
+               var v1 = new Vector<long> (elems1);
+               var elems2 = new long [] { 0, 1 };
+               var v2 = new Vector<long> (elems2);
+               Vector<long> v = Vector.GreaterThanOrEqual (v1, v2);
+               if (v [0] != -1 || v [1] != -1)
+                       return 1;
+               return 0;
+       }
+
+       /* Vector.LessThan */
+
+       public static int test_0_vector_t_i1_lt () {
+               var elems1 = new sbyte [] { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 };
+               var v1 = new Vector<sbyte> (elems1);
+               var elems2 = new sbyte [] { 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2 };
+               var v2 = new Vector<sbyte> (elems2);
+               Vector<sbyte> v = Vector.LessThan (v2, v1);
+               for (int i = 0; i < Vector<sbyte>.Count; ++i) {
+                       if (i % 2 == 0 && v [i] != -1)
+                               return 1;
+                       if (i % 2 == 1 && v [i] != 0)
+                               return 1;
+               }
+               return 0;
+       }
+
+       /* Vector.GreaterThan */
+
+       public static int test_0_vector_t_i1_gt () {
+               var elems1 = new sbyte [] { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 };
+               var v1 = new Vector<sbyte> (elems1);
+               var elems2 = new sbyte [] { 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2 };
+               var v2 = new Vector<sbyte> (elems2);
+               Vector<sbyte> v = Vector.GreaterThan (v1, v2);
+               for (int i = 0; i < Vector<sbyte>.Count; ++i) {
+                       if (i % 2 == 0 && v [i] != -1)
+                               return 1;
+                       if (i % 2 == 1 && v [i] != 0)
+                               return 1;
+               }
+               return 0;
+       }
+
+       /* Vector.LessThanOrEqual */
+       public static int test_0_vector_t_i1_le () {
+               var elems1 = new sbyte [] { 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2 };
+               var v1 = new Vector<sbyte> (elems1);
+               var elems2 = new sbyte [] { 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1 };
+               var v2 = new Vector<sbyte> (elems2);
+               Vector<sbyte> v = Vector.LessThanOrEqual (v1, v2);
+               for (int i = 0; i < Vector<sbyte>.Count; ++i) {
+                       if (i % 2 == 0 && v [i] != -1)
+                               return 1;
+                       if (i % 2 == 1 && v [i] != 0)
+                               return 1;
+               }
+               return 0;
+       }
+
+       /* Vector.Abs */
+
+       [MethodImplAttribute (MethodImplOptions.NoInlining)]
+       public static Vector<T> vector_t_abs<T> (Vector<T> v1) where T: struct {
+               return Vector.Abs (v1);
+       }
+
+       public static int test_0_vector_t_u1_abs () {
+               var elems1 = new byte [] { 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2 };
+               var v1 = new Vector<byte> (elems1);
+
+               if (vector_t_abs (v1) != v1)
+                       return 1;
+               return 0;
+       }
+
+       public static int test_0_vector_t_u2_abs () {
+               var elems1 = new ushort [] { 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2 };
+               var v1 = new Vector<ushort> (elems1);
+
+               if (vector_t_abs (v1) != v1)
+                       return 1;
+               return 0;
+       }
+
+       public static int test_0_vector_t_u4_abs () {
+               var elems1 = new uint [] { 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2 };
+               var v1 = new Vector<uint> (elems1);
+
+               if (vector_t_abs (v1) != v1)
+                       return 1;
+               return 0;
+       }
+
+       public static int test_0_vector_t_u8_abs () {
+               var elems1 = new ulong [] { 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2 };
+               var v1 = new Vector<ulong> (elems1);
+
+               if (vector_t_abs (v1) != v1)
+                       return 1;
+               return 0;
+       }
+
+       public static int test_0_vector_t_i1_abs () {
+               var elems1 = new sbyte [] { 1, -2, 1, -2, 1, -2, 1, -2, 1, -2, 1, -2, 1, -2, 1, -2 };
+               var v1 = new Vector<sbyte> (elems1);
+               var elems2 = new sbyte [] { 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2 };
+               var v2 = new Vector<sbyte> (elems2);
+
+               if (vector_t_abs (v1) != v2)
+                       return 1;
+               return 0;
+       }
+
+       // Vector<T>.Add
+       [MethodImplAttribute (MethodImplOptions.NoInlining)]
+       public static Vector<T> vector_add<T> (Vector<T> v1, Vector<T> v2) where T: struct {
+               return v1 + v2;
+       }
+
+       public static int test_0_vector_byte_add () {
+               var v1 = new Vector<byte> (new byte[] { 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2 });
+               var v2 = new Vector<byte> (new byte[] { 2, 4, 2, 4, 2, 4, 2, 4, 2, 4, 2, 4, 2, 4, 2, 4 });
+
+               var res = vector_add (v1, v1);
+               if (res != v2)
+                       return 1;
+               return 0;
+       }
+
+       public static int test_0_vector_sbyte_add () {
+               var v1 = new Vector<sbyte> (new sbyte[] { 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1 });
+               var v2 = new Vector<sbyte> (new sbyte[] { 2, -2, 2, -2, 2, -2, 2, -2, 2, -2, 2, -2, 2, -2, 2, -2 });
+
+               var res = vector_add (v1, v1);
+               if (res != v2)
+                       return 1;
+               return 0;
+       }
+
+       public static int test_0_vector_ushort_add () {
+               var v1 = new Vector<ushort> (new ushort[] { 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2 });
+               var v2 = new Vector<ushort> (new ushort[] { 2, 4, 2, 4, 2, 4, 2, 4, 2, 4, 2, 4, 2, 4, 2, 4 });
+
+               var res = vector_add (v1, v1);
+               if (res != v2)
+                       return 1;
+               return 0;
+       }
+
+       public static int test_0_vector_short_add () {
+               var v1 = new Vector<short> (new short[] { 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1 });
+               var v2 = new Vector<short> (new short[] { 2, -2, 2, -2, 2, -2, 2, -2, 2, -2, 2, -2, 2, -2, 2, -2 });
+
+               var res = vector_add (v1, v1);
+               if (res != v2)
+                       return 1;
+               return 0;
+       }
+
+       public static int test_0_vector_uint_add () {
+               var v1 = new Vector<uint> (new uint[] { 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2 });
+               var v2 = new Vector<uint> (new uint[] { 2, 4, 2, 4, 2, 4, 2, 4, 2, 4, 2, 4, 2, 4, 2, 4 });
+
+               var res = vector_add (v1, v1);
+               if (res != v2)
+                       return 1;
+               return 0;
+       }
+
+       public static int test_0_vector_int_add () {
+               var v1 = new Vector<int> (new int[] { 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1 });
+               var v2 = new Vector<int> (new int[] { 2, -2, 2, -2, 2, -2, 2, -2, 2, -2, 2, 2, 2, -2, 2, -2 });
+
+               var res = vector_add (v1, v1);
+               if (res != v2)
+                       return 1;
+               return 0;
+       }
+
+       public static int test_0_vector_double_add () {
+               var v1 = new Vector<double> (new double[] { 1.0, -1.0, 1.0, -1.0, 1.0, -1.0, 1.0, -1.0, 1.0, -1.0, 1.0, -1.0, 1.0, -1.0, 1.0, -1.0 });
+               var v2 = new Vector<double> (new double[] { 2.0, -2.0, 2.0, -2.0, 2.0, -2.0, 2.0, -2.0, 2.0, -2.0, 2.0, 2.0, 2.0, -2.0, 2.0, -2.0 });
+
+               var res = vector_add (v1, v1);
+               if (res != v2)
+                       return 1;
+               return 0;
+       }
+
+       public static int test_0_vector_float_add () {
+               var v1 = new Vector<float> (new float[] { 1.0f, -1.0f, 1.0f, -1.0f, 1.0f, -1.0f, 1.0f, -1.0f, 1.0f, -1.0f, 1.0f, -1.0f, 1.0f, -1.0f, 1.0f, -1.0f });
+               var v2 = new Vector<float> (new float[] { 2.0f, -2.0f, 2.0f, -2.0f, 2.0f, -2.0f, 2.0f, -2.0f, 2.0f, -2.0f, 2.0f, 2.0f, 2.0f, -2.0f, 2.0f, -2.0f });
+
+               var res = vector_add (v1, v1);
+               if (res != v2)
+                       return 1;
+               return 0;
+       }
+
+       // Vector<T>.op_Subtraction
+
+       [MethodImplAttribute (MethodImplOptions.NoInlining)]
+       public static Vector<T> vector_sub<T> (Vector<T> v1, Vector<T> v2) where T: struct {
+               return v1 - v2;
+       }
+
+       public static int test_0_vector_byte_sub () {
+               var v1 = new Vector<byte> (new byte[] { 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2 });
+
+               var res = vector_sub (v1, v1);
+               if (res != Vector<byte>.Zero)
+                       return 1;
+               return 0;
+       }
+
+       public static int test_0_vector_double_sub () {
+               var v1 = new Vector<double> (new double[] { 1.0, -1.0, 1.0, -1.0, 1.0, -1.0, 1.0, -1.0, 1.0, -1.0, 1.0, -1.0, 1.0, -1.0, 1.0, -1.0 });
+
+               var res = vector_sub (v1, v1);
+               if (res != Vector<double>.Zero)
+                       return 1;
+               return 0;
+       }
+
+       public static int test_0_vector_float_sub () {
+               var v1 = new Vector<float> (new float[] { 1.0f, -1.0f, 1.0f, -1.0f, 1.0f, -1.0f, 1.0f, -1.0f, 1.0f, -1.0f, 1.0f, -1.0f, 1.0f, -1.0f, 1.0f, -1.0f });
+
+               var res = vector_sub (v1, v1);
+               if (res != Vector<float>.Zero)
+                       return 1;
+               return 0;
+       }
+
+       // Vector<T>.op_Multiply
+       [MethodImplAttribute (MethodImplOptions.NoInlining)]
+       public static Vector<T> vector_mul<T> (Vector<T> v1, Vector<T> v2) where T: struct {
+               return v1 * v2;
+       }
+
+       public static int test_0_vector_int_mul () {
+               var v1 = new Vector<int> (new int[] { 1, 2, -1, 2, 1, 2, -1, 2, 1, -2, 1, 2, 1, 2, -1, 2 });
+               var v2 = new Vector<int> (new int[] { 1, 4, 1, 4, 1, 4, 1, 4, 1, 4, 1, 4, 1, 4, 1, 4 });
+
+               var res = vector_mul (v1, v1);
+               if (res != v2)
+                       return 1;
+               return 0;
+       }
+
+       public static int test_0_vector_double_mul () {
+               var v1 = new Vector<double> (new double[] { 2.0, -1.0, 2.0, -1.0 });
+               var v2 = new Vector<double> (new double[] { 4.0, 1.0, 4.0, 1.0 });
+
+               var res = vector_mul (v1, v1);
+               if (res != v2)
+                       return 1;
+               return 0;
+       }
+
+       public static int test_0_vector_float_mul () {
+               var v1 = new Vector<float> (new float[] { 2.0f, -1.0f, 2.0f, -1.0f });
+               var v2 = new Vector<float> (new float[] { 4.0f, 1.0f, 4.0f, 1.0f });
+
+               var res = vector_mul (v1, v1);
+               if (res != v2)
+                       return 1;
+               return 0;
+       }
+
+       // Vector<T>.op_Division
+       [MethodImplAttribute (MethodImplOptions.NoInlining)]
+       public static Vector<T> vector_div<T> (Vector<T> v1, Vector<T> v2) where T: struct {
+               return v1 / v2;
+       }
+
+       public static int test_0_vector_double_div () {
+               var v1 = new Vector<double> (new double[] { 1.0, -1.0, 1.0, -1.0, 1.0, -1.0, 1.0, -1.0, 1.0, -1.0, 1.0, -1.0, 1.0, -1.0, 1.0, -1.0 });
+               var v2 = new Vector<double> (new double[] { 1.0, 1.0, 1.0, 1.0 });
+
+               var res = vector_div (v1, v1);
+               if (res != v2)
+                       return 1;
+               return 0;
+       }
+
+       public static int test_0_vector_float_div () {
+               var v1 = new Vector<float> (new float[] { 1.0f, -1.0f, 1.0f, -1.0f, 1.0f, -1.0f, 1.0f, -1.0f, 1.0f, -1.0f, 1.0f, -1.0f, 1.0f, -1.0f, 1.0f, -1.0f });
+               var v2 = new Vector<float> (new float[] { 1.0f, 1.0f, 1.0f, 1.0f });
+
+               var res = vector_div (v1, v1);
+               if (res != v2)
+                       return 1;
+               return 0;
+       }
+
+#endif
+
+}
index a4910feb18003f48f13f6d6a994522daf1039c27..da291f47ab54fe474de156ea760709537c8a0d38 100644 (file)
@@ -361,6 +361,24 @@ mono_constant_fold_ins (MonoCompile *cfg, MonoInst *ins, MonoInst *arg1, MonoIns
                                return NULL;
                        }
                }
+               if ((arg1->opcode == OP_PCONST) && (arg2->opcode == OP_PCONST) && ins->next) {
+                       MonoInst *next = ins->next;
+
+                       if (next->opcode == OP_LCEQ) {
+                               gboolean res = arg1->inst_p0 == arg2->inst_p0;
+                               if (overwrite) {
+                                       NULLIFY_INS (ins);
+                                       next->opcode = OP_ICONST;
+                                       next->inst_c0 = res;
+                                       MONO_INST_NULLIFY_SREGS (next);
+                               } else {
+                                       ALLOC_DEST (cfg, dest, ins);
+                                       dest->opcode = OP_ICONST;
+                                       dest->inst_c0 = res;
+                               }
+                               break;
+                       }
+               }
                break;
        }
        case OP_FMOVE:
index 3eeae0fc0da1a42d346b733da64fe69f0d1e048b..63fcd28b4e4228698134cd71c0f8a65dc48041bc 100755 (executable)
@@ -741,6 +741,7 @@ cvttps2dq: dest:x src1:x len:5 clob:1
 
 xmove: dest:x src1:x len:5
 xzero: dest:x len:5
+xones: dest:x len:5
 
 iconv_to_x: dest:x src1:i len:5
 extract_i4: dest:i src1:x len:5
index ba9d6531367042081ba87410847ee375d5302d00..6a029a65590a31526e41c5368f57432797fae1d8 100644 (file)
@@ -75,6 +75,12 @@ alloc_ireg_mp (MonoCompile *cfg)
        return vreg;
 }
 
+static inline guint32
+alloc_xreg (MonoCompile *cfg)
+{
+       return alloc_ireg (cfg);
+}
+
 static inline guint32
 alloc_dreg (MonoCompile *cfg, MonoStackType stack_type)
 {
index 647d787dafcf4f72f315faf2eafa4b9e5a790c31..d8f62355b8ef1ecc69e4195fb804ef1f305fa687 100644 (file)
@@ -641,13 +641,15 @@ mono_local_cprop (MonoCompile *cfg)
                                /* FIXME: Make is_inst_imm a macro */
                                /* FIXME: Make is_inst_imm take an opcode argument */
                                /* is_inst_imm is only needed for binops */
-                               if ((((def->opcode == OP_ICONST) || ((sizeof (gpointer) == 8) && (def->opcode == OP_I8CONST))) &&
+                               if ((((def->opcode == OP_ICONST) || ((sizeof (gpointer) == 8) && (def->opcode == OP_I8CONST)) || (def->opcode == OP_PCONST)) &&
                                         (((srcindex == 0) && (ins->sreg2 == -1)) || mono_arch_is_inst_imm (def->inst_c0))) || 
                                        (!MONO_ARCH_USE_FPSTACK && (def->opcode == OP_R8CONST))) {
                                        guint32 opcode2;
 
                                        /* srcindex == 1 -> binop, ins->sreg2 == -1 -> unop */
-                                       if ((srcindex == 1) && (ins->sreg1 != -1) && defs [ins->sreg1] && (defs [ins->sreg1]->opcode == OP_ICONST) && defs [ins->sreg2]) {
+                                       if ((srcindex == 1) && (ins->sreg1 != -1) && defs [ins->sreg1] &&
+                                               ((defs [ins->sreg1]->opcode == OP_ICONST) || defs [ins->sreg1]->opcode == OP_PCONST) &&
+                                               defs [ins->sreg2]) {
                                                /* Both arguments are constants, perform cfold */
                                                mono_constant_fold_ins (cfg, ins, defs [ins->sreg1], defs [ins->sreg2], TRUE);
                                        } else if ((srcindex == 0) && (ins->sreg2 != -1) && defs [ins->sreg2]) {
@@ -739,6 +741,9 @@ mono_local_cprop (MonoCompile *cfg)
                                        dummy_arg1.inst_c0 = 1;
 
                                        mono_constant_fold_ins (cfg, ins, &dummy_arg1, NULL, TRUE);
+                               } else if (srcindex == 0 && ins->opcode == OP_COMPARE && defs [ins->sreg1]->opcode == OP_PCONST && defs [ins->sreg2] && defs [ins->sreg2]->opcode == OP_PCONST) {
+                                       /* typeof(T) == typeof(..) */
+                                       mono_constant_fold_ins (cfg, ins, defs [ins->sreg1], defs [ins->sreg2], TRUE);
                                }
                        }
 
index de0e58ab1799364ea5c1c77f129066f37d9835c1..da35e44bb2a1fe6c2ad49f00b7712356af9cc4fd 100644 (file)
@@ -5441,7 +5441,7 @@ mini_field_access_needs_cctor_run (MonoCompile *cfg, MonoMethod *method, MonoCla
        return TRUE;
 }
 
-static MonoInst*
+MonoInst*
 mini_emit_ldelema_1_ins (MonoCompile *cfg, MonoClass *klass, MonoInst *arr, MonoInst *index, gboolean bcheck)
 {
        MonoInst *ins;
@@ -6809,6 +6809,13 @@ mini_emit_inst_for_method (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSign
                 * all inputs:
                 * http://everything2.com/?node_id=1051618
                 */
+       } else if (cmethod->klass == mono_defaults.systemtype_class && !strcmp (cmethod->name, "op_Equality")) {
+               EMIT_NEW_BIALU (cfg, ins, OP_COMPARE, -1, args [0]->dreg, args [1]->dreg);
+               MONO_INST_NEW (cfg, ins, OP_PCEQ);
+               ins->dreg = alloc_preg (cfg);
+               ins->type = STACK_I4;
+               MONO_ADD_INS (cfg->cbb, ins);
+               return ins;
        } else if (((!strcmp (cmethod->klass->image->assembly->aname.name, "MonoMac") ||
                    !strcmp (cmethod->klass->image->assembly->aname.name, "monotouch")) &&
                                !strcmp (cmethod->klass->name_space, "XamCore.ObjCRuntime") &&
@@ -10028,7 +10035,8 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b
                        }
 
                        /* Common call */
-                       INLINE_FAILURE ("call");
+                       if (!(cmethod->iflags & METHOD_IMPL_ATTRIBUTE_AGGRESSIVE_INLINING))
+                               INLINE_FAILURE ("call");
                        ins = mono_emit_method_call_full (cfg, cmethod, fsig, tail_call, sp, virtual_ ? sp [0] : NULL,
                                                                                          imt_arg, vtable_arg);
 
@@ -11502,6 +11510,16 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b
 
                                        MONO_EMIT_NULL_CHECK (cfg, sp [0]->dreg);
 
+                                       if (sp [0]->opcode == OP_LDADDR && klass->simd_type && cfg->opt & MONO_OPT_SIMD) {
+                                               ins = mono_emit_simd_field_load (cfg, field, sp [0]);
+                                               if (ins) {
+                                                       *sp++ = ins;
+                                                       ins_flag = 0;
+                                                       ip += 5;
+                                                       break;
+                                               }
+                                       }
+
                                        if (mini_is_gsharedvt_klass (klass)) {
                                                MonoInst *offset_ins;
 
index bac046a8efdc8a08ea125a46402405d71af9a008..b9093c31c754baffd2002b29da8504fee3c4ca14 100644 (file)
@@ -6499,8 +6499,12 @@ mono_arch_output_basic_block (MonoCompile *cfg, MonoBasicBlock *bb)
                case OP_XZERO:
                        amd64_sse_pxor_reg_reg (code, ins->dreg, ins->dreg);
                        break;
+               case OP_XONES:
+                       amd64_sse_pcmpeqb_reg_reg (code, ins->dreg, ins->dreg);
+                       break;
                case OP_ICONV_TO_R4_RAW:
                        amd64_movd_xreg_reg_size (code, ins->dreg, ins->sreg1, 4);
+                       amd64_sse_cvtss2sd_reg_reg (code, ins->dreg, ins->dreg);
                        break;
 
                case OP_FCONV_TO_R8_X:
index a8bf46b288661920056b90f3db2aa85be401c448..876d35c635c87dabbcb0682569be6633970dcb94 100644 (file)
@@ -2635,6 +2635,19 @@ mono_peephole_ins (MonoBasicBlock *bb, MonoInst *ins)
                        ins->sreg1 = last_ins->sreg1;
                }
                break;
+       case OP_LOADX_MEMBASE:
+               if (last_ins && last_ins->opcode == OP_STOREX_MEMBASE &&
+                       ins->inst_basereg == last_ins->inst_destbasereg &&
+                       ins->inst_offset == last_ins->inst_offset) {
+                       if (ins->dreg == last_ins->sreg1) {
+                               MONO_DELETE_INS (bb, ins);
+                               break;
+                       } else {
+                               ins->opcode = OP_XMOVE;
+                               ins->sreg1 = last_ins->sreg1;
+                       }
+               }
+               break;
        case OP_MOVE:
        case OP_FMOVE:
                /*
index 98802978e85ef78a054087c0e0fe85fe7d106ce3..c403278c753feadc52a526f997f447468b8f2e97 100644 (file)
@@ -358,6 +358,36 @@ simd_class_to_llvm_type (EmitContext *ctx, MonoClass *klass)
                return LLVMVectorType (LLVMInt8Type (), 16);
        } else if (!strcmp (klass->name, "Vector16b")) {
                return LLVMVectorType (LLVMInt8Type (), 16);
+       } else if (!strcmp (klass->name, "Vector2")) {
+               /* System.Numerics */
+               return LLVMVectorType (LLVMFloatType (), 4);
+       } else if (!strcmp (klass->name, "Vector3")) {
+               return LLVMVectorType (LLVMFloatType (), 4);
+       } else if (!strcmp (klass->name, "Vector4")) {
+               return LLVMVectorType (LLVMFloatType (), 4);
+       } else if (!strcmp (klass->name, "Vector`1")) {
+               MonoType *etype = mono_class_get_generic_class (klass)->context.class_inst->type_argv [0];
+               switch (etype->type) {
+               case MONO_TYPE_I1:
+               case MONO_TYPE_U1:
+                       return LLVMVectorType (LLVMInt8Type (), 16);
+               case MONO_TYPE_I2:
+               case MONO_TYPE_U2:
+                       return LLVMVectorType (LLVMInt16Type (), 8);
+               case MONO_TYPE_I4:
+               case MONO_TYPE_U4:
+                       return LLVMVectorType (LLVMInt32Type (), 4);
+               case MONO_TYPE_I8:
+               case MONO_TYPE_U8:
+                       return LLVMVectorType (LLVMInt64Type (), 2);
+               case MONO_TYPE_R4:
+                       return LLVMVectorType (LLVMFloatType (), 4);
+               case MONO_TYPE_R8:
+                       return LLVMVectorType (LLVMDoubleType (), 2);
+               default:
+                       g_assert_not_reached ();
+                       return NULL;
+               }
        } else {
                printf ("%s\n", klass->name);
                NOT_IMPLEMENTED;
@@ -916,6 +946,8 @@ simd_op_to_intrins (int opcode)
                return "llvm.x86.sse2.pmulh.w";
        case OP_PMULW_HIGH_UN:
                return "llvm.x86.sse2.pmulhu.w";
+       case OP_DPPS:
+               return "llvm.x86.sse41.dpps";
 #endif
        default:
                g_assert_not_reached ();
@@ -6441,6 +6473,18 @@ process_bb (EmitContext *ctx, MonoBasicBlock *bb)
                        break;
                }
 
+               case OP_DPPS: {
+                       LLVMValueRef args [3];
+
+                       args [0] = lhs;
+                       args [1] = rhs;
+                       /* 0xf1 == multiply all 4 elements, add them together, and store the result to the lowest element */
+                       args [2] = LLVMConstInt (LLVMInt32Type (), 0xf1, FALSE);
+
+                       values [ins->dreg] = LLVMBuildCall (builder, get_intrinsic (ctx, simd_op_to_intrins (ins->opcode)), args, 3, dname);
+                       break;
+               }
+
 #endif /* SIMD */
 
                case OP_DUMMY_USE:
@@ -7858,6 +7902,7 @@ typedef enum {
        INTRINS_SSE_PSUBUSB,
        INTRINS_SSE_PAVGB,
        INTRINS_SSE_PAUSE,
+       INTRINS_SSE_DPPS,
 #endif
        INTRINS_NUM
 } IntrinsicId;
@@ -7939,7 +7984,8 @@ static IntrinsicDesc intrinsics[] = {
        {INTRINS_SSE_PADDUSB, "llvm.x86.sse2.paddus.b"},
        {INTRINS_SSE_PSUBUSB, "llvm.x86.sse2.psubus.b"},
        {INTRINS_SSE_PAVGB, "llvm.x86.sse2.pavg.b"},
-       {INTRINS_SSE_PAUSE, "llvm.x86.sse2.pause"}
+       {INTRINS_SSE_PAUSE, "llvm.x86.sse2.pause"},
+       {INTRINS_SSE_DPPS, "llvm.x86.sse41.dpps"}
 #endif
 };
 
@@ -8172,6 +8218,13 @@ add_intrinsic (LLVMModuleRef module, int id)
        case INTRINS_SSE_PAUSE:
                AddFunc (module, "llvm.x86.sse2.pause", LLVMVoidType (), NULL, 0);
                break;
+       case INTRINS_SSE_DPPS:
+               ret_type = type_to_simd_type (MONO_TYPE_R4);
+               arg_types [0] = type_to_simd_type (MONO_TYPE_R4);
+               arg_types [1] = type_to_simd_type (MONO_TYPE_R4);
+               arg_types [2] = LLVMInt32Type ();
+               AddFunc (module, name, ret_type, arg_types, 3);
+               break;
 #endif
        default:
                g_assert_not_reached ();
index 07dd0a435376575104bed2e5da8a9ffa6a8bc099..c65def26bab13a97247661a99e8be0b98ef2397d 100644 (file)
@@ -947,10 +947,15 @@ MINI_OP(OP_CVTPS2PD, "cvtps2pd", XREG, XREG, NONE)
 MINI_OP(OP_CVTTPD2DQ, "cvttpd2dq", XREG, XREG, NONE)
 MINI_OP(OP_CVTTPS2DQ, "cvttps2dq", XREG, XREG, NONE)
 
+/* r4 dot product */
+/* multiply all 4 single precision float elements, add them together, and store the result to the lowest element */
+MINI_OP(OP_DPPS, "dpps", XREG, XREG, XREG)
+
 #endif
 
 MINI_OP(OP_XMOVE,   "xmove", XREG, XREG, NONE)
 MINI_OP(OP_XZERO,   "xzero", XREG, NONE, NONE)
+MINI_OP(OP_XONES,   "xones", XREG, NONE, NONE)
 MINI_OP(OP_XPHI,       "xphi", XREG, NONE, NONE)
 
 /*
index 5b42ca510f34ffa07def506ffdb86ffff3612339..4da25a4d439edda2babe9a41e70d0ea1912c2b05 100644 (file)
@@ -419,6 +419,8 @@ mono_type_to_load_membase (MonoCompile *cfg, MonoType *type)
        case MONO_TYPE_TYPEDBYREF:
                return OP_LOADV_MEMBASE;
        case MONO_TYPE_GENERICINST:
+               if (MONO_CLASS_IS_SIMD (cfg, mono_class_from_mono_type (type)))
+                       return OP_LOADX_MEMBASE;
                if (mono_type_generic_inst_is_valuetype (type))
                        return OP_LOADV_MEMBASE;
                else
index 9e6f8f7523f5702fccc496b9433cd0abc5a02422..239cdd38ebd3f904286268f91c53b52627de0e87 100644 (file)
@@ -505,7 +505,7 @@ enum {
 
 /* FIXME: Add more instructions */
 /* INEG sets the condition codes, and the OP_LNEG decomposition depends on this on x86 */
-#define MONO_INS_HAS_NO_SIDE_EFFECT(ins) (MONO_IS_MOVE (ins) || (ins->opcode == OP_ICONST) || (ins->opcode == OP_I8CONST) || MONO_IS_ZERO (ins) || (ins->opcode == OP_ADD_IMM) || (ins->opcode == OP_R8CONST) || (ins->opcode == OP_LADD_IMM) || (ins->opcode == OP_ISUB_IMM) || (ins->opcode == OP_IADD_IMM) || (ins->opcode == OP_LNEG) || (ins->opcode == OP_ISUB) || (ins->opcode == OP_CMOV_IGE) || (ins->opcode == OP_ISHL_IMM) || (ins->opcode == OP_ISHR_IMM) || (ins->opcode == OP_ISHR_UN_IMM) || (ins->opcode == OP_IAND_IMM) || (ins->opcode == OP_ICONV_TO_U1) || (ins->opcode == OP_ICONV_TO_I1) || (ins->opcode == OP_SEXT_I4) || (ins->opcode == OP_LCONV_TO_U1) || (ins->opcode == OP_ICONV_TO_U2) || (ins->opcode == OP_ICONV_TO_I2) || (ins->opcode == OP_LCONV_TO_I2) || (ins->opcode == OP_LDADDR) || (ins->opcode == OP_PHI) || (ins->opcode == OP_NOP) || (ins->opcode == OP_ZEXT_I4) || (ins->opcode == OP_NOT_NULL) || (ins->opcode == OP_IL_SEQ_POINT))
+#define MONO_INS_HAS_NO_SIDE_EFFECT(ins) (MONO_IS_MOVE (ins) || (ins->opcode == OP_ICONST) || (ins->opcode == OP_I8CONST) || MONO_IS_ZERO (ins) || (ins->opcode == OP_ADD_IMM) || (ins->opcode == OP_R8CONST) || (ins->opcode == OP_LADD_IMM) || (ins->opcode == OP_ISUB_IMM) || (ins->opcode == OP_IADD_IMM) || (ins->opcode == OP_LNEG) || (ins->opcode == OP_ISUB) || (ins->opcode == OP_CMOV_IGE) || (ins->opcode == OP_ISHL_IMM) || (ins->opcode == OP_ISHR_IMM) || (ins->opcode == OP_ISHR_UN_IMM) || (ins->opcode == OP_IAND_IMM) || (ins->opcode == OP_ICONV_TO_U1) || (ins->opcode == OP_ICONV_TO_I1) || (ins->opcode == OP_SEXT_I4) || (ins->opcode == OP_LCONV_TO_U1) || (ins->opcode == OP_ICONV_TO_U2) || (ins->opcode == OP_ICONV_TO_I2) || (ins->opcode == OP_LCONV_TO_I2) || (ins->opcode == OP_LDADDR) || (ins->opcode == OP_PHI) || (ins->opcode == OP_NOP) || (ins->opcode == OP_ZEXT_I4) || (ins->opcode == OP_NOT_NULL) || (ins->opcode == OP_IL_SEQ_POINT) || (ins->opcode == OP_XZERO))
 
 #define MONO_INS_IS_PCONST_NULL(ins) ((ins)->opcode == OP_PCONST && (ins)->inst_p0 == 0)
 
@@ -2619,6 +2619,7 @@ gboolean          mono_is_regsize_var (MonoType *t);
 void              mini_emit_memcpy (MonoCompile *cfg, int destreg, int doffset, int srcreg, int soffset, int size, int align);
 void              mini_emit_stobj (MonoCompile *cfg, MonoInst *dest, MonoInst *src, MonoClass *klass, gboolean native);
 void              mini_emit_initobj (MonoCompile *cfg, MonoInst *dest, const guchar *ip, MonoClass *klass);
+MonoInst*         mini_emit_ldelema_1_ins (MonoCompile *cfg, MonoClass *klass, MonoInst *arr, MonoInst *index, gboolean bcheck);
 CompRelation      mono_opcode_to_cond (int opcode) MONO_LLVM_INTERNAL;
 CompType          mono_opcode_to_type (int opcode, int cmp_opcode);
 CompRelation      mono_negate_cond (CompRelation cond);
@@ -3128,6 +3129,7 @@ enum {
 const char *mono_arch_xregname (int reg);
 void        mono_simd_simplify_indirection (MonoCompile *cfg);
 MonoInst*   mono_emit_simd_intrinsics (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSignature *fsig, MonoInst **args);
+MonoInst*   mono_emit_simd_field_load (MonoCompile *cfg, MonoClassField *field, MonoInst *addr);
 guint32     mono_arch_cpu_enumerate_simd_versions (void);
 void        mono_simd_intrinsics_init (void);
 
index 6bffbd05c4927183446441b0f290e53e1595debf..09fc256a77269317bdbdd3b8b089220190a09890 100644 (file)
@@ -135,9 +135,9 @@ typedef struct {
        guint8 simd_version_flags;
        guint8 simd_emit_mode : 4;
        guint8 flags : 4;
-} SimdIntrinsc;
+} SimdIntrinsic;
 
-static const SimdIntrinsc vector4f_intrinsics[] = {
+static const SimdIntrinsic vector4f_intrinsics[] = {
        { SN_ctor, OP_EXPAND_R4, SIMD_VERSION_SSE1, SIMD_EMIT_CTOR },
        { SN_AddSub, OP_ADDSUBPS, SIMD_VERSION_SSE3, SIMD_EMIT_BINARY},
        { SN_AndNot, OP_ANDNPS, SIMD_VERSION_SSE1, SIMD_EMIT_BINARY},
@@ -191,7 +191,7 @@ static const SimdIntrinsc vector4f_intrinsics[] = {
        { SN_set_Z, 2, SIMD_VERSION_SSE1, SIMD_EMIT_SETTER }
 };
 
-static const SimdIntrinsc vector2d_intrinsics[] = {
+static const SimdIntrinsic vector2d_intrinsics[] = {
        { SN_ctor, OP_EXPAND_R8, SIMD_VERSION_SSE1, SIMD_EMIT_CTOR },
        { SN_AddSub, OP_ADDSUBPD, SIMD_VERSION_SSE3, SIMD_EMIT_BINARY,},
        { SN_AndNot, OP_ANDNPD, SIMD_VERSION_SSE1, SIMD_EMIT_BINARY },
@@ -235,7 +235,7 @@ static const SimdIntrinsc vector2d_intrinsics[] = {
        { SN_set_Y, 1, SIMD_VERSION_SSE1, SIMD_EMIT_SETTER },
 };
 
-static const SimdIntrinsc vector2ul_intrinsics[] = {
+static const SimdIntrinsic vector2ul_intrinsics[] = {
        { SN_ctor, OP_EXPAND_I8, SIMD_VERSION_SSE1, SIMD_EMIT_CTOR },
        { SN_CompareEqual, OP_PCMPEQQ, SIMD_VERSION_SSE41, SIMD_EMIT_BINARY },
        { SN_LoadAligned, 0, SIMD_VERSION_SSE1, SIMD_EMIT_LOAD_ALIGNED },
@@ -262,7 +262,7 @@ static const SimdIntrinsc vector2ul_intrinsics[] = {
        { SN_set_Y, 1, SIMD_VERSION_SSE1, SIMD_EMIT_SETTER },
 };
 
-static const SimdIntrinsc vector2l_intrinsics[] = {
+static const SimdIntrinsic vector2l_intrinsics[] = {
        { SN_ctor, OP_EXPAND_I8, SIMD_VERSION_SSE1, SIMD_EMIT_CTOR },
        { SN_CompareEqual, OP_PCMPEQQ, SIMD_VERSION_SSE41, SIMD_EMIT_BINARY },
        { SN_CompareGreaterThan, OP_PCMPGTQ, SIMD_VERSION_SSE42, SIMD_EMIT_BINARY },
@@ -290,7 +290,7 @@ static const SimdIntrinsc vector2l_intrinsics[] = {
        { SN_set_Y, 1, SIMD_VERSION_SSE1, SIMD_EMIT_SETTER },
 };
 
-static const SimdIntrinsc vector4ui_intrinsics[] = {
+static const SimdIntrinsic vector4ui_intrinsics[] = {
        { SN_ctor, OP_EXPAND_I4, SIMD_VERSION_SSE1, SIMD_EMIT_CTOR },
        { SN_ArithmeticRightShift, OP_PSARD, SIMD_VERSION_SSE1, SIMD_EMIT_SHIFT },
        { SN_CompareEqual, OP_PCMPEQD, SIMD_VERSION_SSE1, SIMD_EMIT_BINARY },
@@ -328,7 +328,7 @@ static const SimdIntrinsc vector4ui_intrinsics[] = {
        { SN_set_Z, 2, SIMD_VERSION_SSE1, SIMD_EMIT_SETTER },
 };
 
-static const SimdIntrinsc vector4i_intrinsics[] = {
+static const SimdIntrinsic vector4i_intrinsics[] = {
        { SN_ctor, OP_EXPAND_I4, SIMD_VERSION_SSE1, SIMD_EMIT_CTOR },
        { SN_CompareEqual, OP_PCMPEQD, SIMD_VERSION_SSE1, SIMD_EMIT_BINARY },
        { SN_CompareGreaterThan, OP_PCMPGTD, SIMD_VERSION_SSE1, SIMD_EMIT_BINARY },
@@ -369,7 +369,7 @@ static const SimdIntrinsc vector4i_intrinsics[] = {
        { SN_set_Z, 2, SIMD_VERSION_SSE1, SIMD_EMIT_SETTER },
 };
 
-static const SimdIntrinsc vector8us_intrinsics[] = {
+static const SimdIntrinsic vector8us_intrinsics[] = {
        { SN_ctor, OP_EXPAND_I2, SIMD_VERSION_SSE1, SIMD_EMIT_CTOR },
        { SN_AddWithSaturation, OP_PADDW_SAT_UN, SIMD_VERSION_SSE1, SIMD_EMIT_BINARY },
        { SN_ArithmeticRightShift, OP_PSARW, SIMD_VERSION_SSE1, SIMD_EMIT_SHIFT },
@@ -420,7 +420,7 @@ static const SimdIntrinsc vector8us_intrinsics[] = {
        { SN_set_V7, 7, SIMD_VERSION_SSE1, SIMD_EMIT_SETTER },
 };
 
-static const SimdIntrinsc vector8s_intrinsics[] = {
+static const SimdIntrinsic vector8s_intrinsics[] = {
        { SN_ctor, OP_EXPAND_I2, SIMD_VERSION_SSE1, SIMD_EMIT_CTOR },
        { SN_AddWithSaturation, OP_PADDW_SAT, SIMD_VERSION_SSE1, SIMD_EMIT_BINARY },
        { SN_CompareEqual, OP_PCMPEQW, SIMD_VERSION_SSE1, SIMD_EMIT_BINARY },
@@ -471,7 +471,7 @@ static const SimdIntrinsc vector8s_intrinsics[] = {
        { SN_set_V7, 7, SIMD_VERSION_SSE1, SIMD_EMIT_SETTER },
 };
 
-static const SimdIntrinsc vector16b_intrinsics[] = {
+static const SimdIntrinsic vector16b_intrinsics[] = {
        { SN_ctor, OP_EXPAND_I1, SIMD_VERSION_SSE1, SIMD_EMIT_CTOR },
        { SN_AddWithSaturation, OP_PADDB_SAT_UN, SIMD_VERSION_SSE1, SIMD_EMIT_BINARY },
        { SN_Average, OP_PAVGB_UN, SIMD_VERSION_SSE1, SIMD_EMIT_BINARY },
@@ -535,7 +535,7 @@ static const SimdIntrinsc vector16b_intrinsics[] = {
 Missing:
 setters
  */
-static const SimdIntrinsc vector16sb_intrinsics[] = {
+static const SimdIntrinsic vector16sb_intrinsics[] = {
        { SN_ctor, OP_EXPAND_I1, SIMD_VERSION_SSE1, SIMD_EMIT_CTOR },
        { SN_AddWithSaturation, OP_PADDB_SAT, SIMD_VERSION_SSE1, SIMD_EMIT_BINARY },
        { SN_CompareEqual, OP_PCMPEQB, SIMD_VERSION_SSE1, SIMD_EMIT_BINARY },
@@ -596,11 +596,14 @@ static const SimdIntrinsc vector16sb_intrinsics[] = {
 
 static guint32 simd_supported_versions;
 
+static MonoInst* emit_sys_numerics_intrinsics (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSignature *fsig, MonoInst **args);
+static MonoInst* emit_sys_numerics_vectors_intrinsics (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSignature *fsig, MonoInst **args);
+
 /*TODO match using number of parameters as well*/
 static int
 simd_intrinsic_compare_by_name (const void *key, const void *value)
 {
-       return strcmp (key, method_name (((SimdIntrinsc *)value)->name));
+       return strcmp (key, method_name (((SimdIntrinsic *)value)->name));
 }
 
 typedef enum {
@@ -818,9 +821,11 @@ mono_simd_simplify_indirection (MonoCompile *cfg)
 static int
 get_simd_vreg (MonoCompile *cfg, MonoMethod *cmethod, MonoInst *src)
 {
+       const char *spec = INS_INFO (src->opcode);
+
        if (src->opcode == OP_XMOVE) {
                return src->sreg1;
-       } else if (src->type == STACK_VTYPE) {
+       } else if (spec [MONO_INST_DEST] == 'x') {
                return src->dreg;
        }
        g_warning ("get_simd_vreg:: could not infer source simd vreg for op");
@@ -832,8 +837,10 @@ get_simd_vreg (MonoCompile *cfg, MonoMethod *cmethod, MonoInst *src)
  * This function will load the value if needed. 
  */
 static int
-load_simd_vreg (MonoCompile *cfg, MonoMethod *cmethod, MonoInst *src, gboolean *indirect)
+load_simd_vreg_class (MonoCompile *cfg, MonoClass *klass, MonoInst *src, gboolean *indirect)
 {
+       const char *spec = INS_INFO (src->opcode);
+
        if (indirect)
                *indirect = FALSE;
        if (src->opcode == OP_XMOVE) {
@@ -842,7 +849,7 @@ load_simd_vreg (MonoCompile *cfg, MonoMethod *cmethod, MonoInst *src, gboolean *
                int res = ((MonoInst*)src->inst_p0)->dreg;
                NULLIFY_INS (src);
                return res;
-       } else if (src->type == STACK_VTYPE) {
+       } else if (spec [MONO_INST_DEST] == 'x') {
                return src->dreg;
        } else if (src->type == STACK_PTR || src->type == STACK_MP) {
                MonoInst *ins;
@@ -850,7 +857,7 @@ load_simd_vreg (MonoCompile *cfg, MonoMethod *cmethod, MonoInst *src, gboolean *
                        *indirect = TRUE;
 
                MONO_INST_NEW (cfg, ins, OP_LOADX_MEMBASE);
-               ins->klass = cmethod->klass;
+               ins->klass = klass;
                ins->sreg1 = src->dreg;
                ins->type = STACK_VTYPE;
                ins->dreg = alloc_ireg (cfg);
@@ -862,6 +869,12 @@ load_simd_vreg (MonoCompile *cfg, MonoMethod *cmethod, MonoInst *src, gboolean *
        g_assert_not_reached ();
 }
 
+static int
+load_simd_vreg (MonoCompile *cfg, MonoMethod *cmethod, MonoInst *src, gboolean *indirect)
+{
+       return load_simd_vreg_class (cfg, cmethod->klass, src, indirect);
+}
+
 /*We share the var with fconv_to_r8_x to save some stack space.*/
 static MonoInst*
 get_double_spill_area (MonoCompile *cfg)
@@ -908,21 +921,149 @@ mono_type_to_expand_op (MonoType *type)
 }
 
 static int
-get_simd_vreg_or_expanded_scalar (MonoCompile *cfg, MonoMethod *cmethod, MonoInst *src, int position)
+type_to_comp_op (MonoType *t)
+{
+       switch (t->type) {
+       case MONO_TYPE_I1:
+       case MONO_TYPE_U1:
+               return OP_PCMPEQB;
+       case MONO_TYPE_I2:
+       case MONO_TYPE_U2:
+               return OP_PCMPEQW;
+       case MONO_TYPE_I4:
+       case MONO_TYPE_U4:
+               return OP_PCMPEQD;
+       case MONO_TYPE_I8:
+       case MONO_TYPE_U8:
+               return OP_PCMPEQQ;
+       case MONO_TYPE_R4:
+               return OP_COMPPS;
+       case MONO_TYPE_R8:
+               return OP_COMPPD;
+       default:
+               g_assert_not_reached ();
+               return -1;
+       }
+}
+
+static int
+type_to_gt_op (MonoType *t)
+{
+       switch (t->type) {
+       case MONO_TYPE_I1:
+               return OP_PCMPGTB;
+       case MONO_TYPE_I2:
+               return OP_PCMPGTW;
+       case MONO_TYPE_I4:
+               return OP_PCMPGTD;
+       case MONO_TYPE_I8:
+               return OP_PCMPGTQ;
+       default:
+               return -1;
+       }
+}
+
+static int
+type_to_padd_op (MonoType *t)
+{
+       switch (t->type) {
+       case MONO_TYPE_U1:
+       case MONO_TYPE_I1:
+               return OP_PADDB;
+       case MONO_TYPE_U2:
+       case MONO_TYPE_I2:
+               return OP_PADDW;
+       case MONO_TYPE_U4:
+       case MONO_TYPE_I4:
+               return OP_PADDD;
+       case MONO_TYPE_U8:
+       case MONO_TYPE_I8:
+               return OP_PADDQ;
+       case MONO_TYPE_R4:
+               return OP_ADDPS;
+       case MONO_TYPE_R8:
+               return OP_ADDPD;
+       default:
+               break;
+       }
+       return -1;
+}
+
+static int
+type_to_psub_op (MonoType *t)
+{
+       switch (t->type) {
+       case MONO_TYPE_U1:
+       case MONO_TYPE_I1:
+               return OP_PSUBB;
+       case MONO_TYPE_U2:
+       case MONO_TYPE_I2:
+               return OP_PSUBW;
+       case MONO_TYPE_U4:
+       case MONO_TYPE_I4:
+               return OP_PSUBD;
+       case MONO_TYPE_U8:
+       case MONO_TYPE_I8:
+               return OP_PSUBQ;
+       case MONO_TYPE_R4:
+               return OP_SUBPS;
+       case MONO_TYPE_R8:
+               return OP_SUBPD;
+       default:
+               break;
+       }
+       return -1;
+}
+
+static int
+type_to_pmul_op (MonoType *t)
+{
+       switch (t->type) {
+       case MONO_TYPE_U2:
+       case MONO_TYPE_I2:
+               return OP_PMULW;
+       case MONO_TYPE_U4:
+       case MONO_TYPE_I4:
+               return OP_PMULD;
+       case MONO_TYPE_U8:
+       case MONO_TYPE_I8:
+               return OP_PMULQ;
+       case MONO_TYPE_R4:
+               return OP_MULPS;
+       case MONO_TYPE_R8:
+               return OP_MULPD;
+       default:
+               break;
+       }
+       return -1;
+}
+
+static int
+type_to_pdiv_op (MonoType *t)
+{
+       switch (t->type) {
+       case MONO_TYPE_R4:
+               return OP_DIVPS;
+       case MONO_TYPE_R8:
+               return OP_DIVPD;
+       default:
+               break;
+       }
+       return -1;
+}
+
+static int
+get_simd_vreg_or_expanded_scalar (MonoCompile *cfg, MonoClass *klass, MonoType *param_type, MonoInst *src)
 {
        MonoInst *ins;
-       MonoMethodSignature *sig = mono_method_signature (cmethod);
        int expand_op;
 
-       g_assert (sig->param_count == 2);
-       g_assert (position == 0 || position == 1);
+       if (mono_class_from_mono_type (param_type)->simd_type)
+               return get_simd_vreg (cfg, NULL, src);
 
-       if (mono_class_from_mono_type (sig->params [position])->simd_type)
-               return get_simd_vreg (cfg, cmethod, src);
-
-       expand_op = mono_type_to_expand_op (sig->params [position]);
+       expand_op = mono_type_to_expand_op (param_type);
        MONO_INST_NEW (cfg, ins, expand_op);
-       ins->klass = cmethod->klass;
+       ins->klass = klass;
        ins->sreg1 = src->dreg;
        ins->type = STACK_VTYPE;
        ins->dreg = alloc_ireg (cfg);
@@ -936,29 +1077,45 @@ get_simd_vreg_or_expanded_scalar (MonoCompile *cfg, MonoMethod *cmethod, MonoIns
        return ins->dreg;
 }
 
+/*
+ * simd_intrinsic_emit_binary_op:
+ *
+ *   Emit a binary SIMD opcode.
+ * @LHS/@RHS are the two arguments, they can be either a SIMD type or a scalar one. Scalar arguments are
+ * expanded to the SIMD type.
+ */
 static MonoInst*
-simd_intrinsic_emit_binary (const SimdIntrinsc *intrinsic, MonoCompile *cfg, MonoMethod *cmethod, MonoInst **args)
+simd_intrinsic_emit_binary_op (MonoCompile *cfg, int opcode, int flags, MonoClass *klass, MonoType *lhs_type, MonoType *rhs_type, MonoInst *lhs, MonoInst *rhs)
 {
        MonoInst* ins;
        int left_vreg, right_vreg;
 
-       left_vreg = get_simd_vreg_or_expanded_scalar (cfg, cmethod, args [0], 0);
-       right_vreg = get_simd_vreg_or_expanded_scalar (cfg, cmethod, args [1], 1);
-
+       left_vreg = get_simd_vreg_or_expanded_scalar (cfg, klass, lhs_type, lhs);
+       right_vreg = get_simd_vreg_or_expanded_scalar (cfg, klass, rhs_type, rhs);
 
-       MONO_INST_NEW (cfg, ins, intrinsic->opcode);
-       ins->klass = cmethod->klass;
+       MONO_INST_NEW (cfg, ins, opcode);
+       ins->klass = klass;
        ins->sreg1 = left_vreg;
        ins->sreg2 = right_vreg;
        ins->type = STACK_VTYPE;
        ins->dreg = alloc_ireg (cfg);
-       ins->inst_c0 = intrinsic->flags;
+       ins->inst_c0 = flags;
        MONO_ADD_INS (cfg->cbb, ins);
        return ins;
 }
 
 static MonoInst*
-simd_intrinsic_emit_unary (const SimdIntrinsc *intrinsic, MonoCompile *cfg, MonoMethod *cmethod, MonoInst **args)
+simd_intrinsic_emit_binary (const SimdIntrinsic *intrinsic, MonoCompile *cfg, MonoMethod *cmethod, MonoInst **args)
+{
+       MonoMethodSignature *sig = mono_method_signature (cmethod);
+
+       g_assert (sig->param_count == 2);
+
+       return simd_intrinsic_emit_binary_op (cfg, intrinsic->opcode, intrinsic->flags, cmethod->klass, sig->params [0], sig->params [1], args [0], args [1]);
+}
+
+static MonoInst*
+simd_intrinsic_emit_unary (const SimdIntrinsic *intrinsic, MonoCompile *cfg, MonoMethod *cmethod, MonoInst **args)
 {
        MonoInst* ins;
        int vreg;
@@ -1066,7 +1223,7 @@ mono_type_to_slow_insert_op (MonoType *type)
 }
 
 static MonoInst*
-simd_intrinsic_emit_setter (const SimdIntrinsc *intrinsic, MonoCompile *cfg, MonoMethod *cmethod, MonoInst **args)
+simd_intrinsic_emit_setter (const SimdIntrinsic *intrinsic, MonoCompile *cfg, MonoMethod *cmethod, MonoInst **args)
 {
        MonoInst *ins;
        MonoMethodSignature *sig = mono_method_signature (cmethod);
@@ -1125,37 +1282,65 @@ simd_intrinsic_emit_setter (const SimdIntrinsc *intrinsic, MonoCompile *cfg, Mon
        return ins;
 }
 
+/*
+ * simd_intrinsic_emit_getter_op:
+ *
+ *   Emit IR for loading an element of a SIMD value.
+ *
+ * @klass is the simd type, @type is the element type.
+ */
 static MonoInst*
-simd_intrinsic_emit_getter (const SimdIntrinsc *intrinsic, MonoCompile *cfg, MonoMethod *cmethod, MonoInst **args)
+simd_intrinsic_emit_getter_op (MonoCompile *cfg, int index, MonoClass *klass, MonoType *type, MonoInst *arg)
 {
        MonoInst *ins;
-       MonoMethodSignature *sig = mono_method_signature (cmethod);
-       int vreg, shift_bits = mono_type_elements_shift_bits (sig->ret);
+       int vreg, shift_bits;
 
-       vreg = load_simd_vreg (cfg, cmethod, args [0], NULL);
+       vreg = load_simd_vreg_class (cfg, klass, arg, NULL);
+
+       if (type->type == MONO_TYPE_I8 || type->type == MONO_TYPE_U8 || type->type == MONO_TYPE_R8) {
+               MonoInst *ins;
+               gboolean is_r8 = type->type == MONO_TYPE_R8;
+
+               MONO_INST_NEW (cfg, ins, is_r8 ? OP_EXTRACT_R8 : OP_EXTRACT_I8);
+               ins->klass = klass;
+               ins->sreg1 = vreg;
+               ins->inst_c0 = index;
+               if (is_r8) {
+                       ins->type = STACK_R8;
+                       ins->dreg = alloc_freg (cfg);
+                       ins->backend.spill_var = get_double_spill_area (cfg);
+               } else {
+                       ins->type = STACK_I8;
+                       ins->dreg = alloc_lreg (cfg);
+               }
+               MONO_ADD_INS (cfg->cbb, ins);
+               return ins;
+       }
+
+       shift_bits = mono_type_elements_shift_bits (type);
 
-       if ((intrinsic->opcode >> shift_bits) && !cfg->compile_llvm) {
+       if ((index >> shift_bits) && !cfg->compile_llvm) {
                MONO_INST_NEW (cfg, ins, OP_PSHUFLED);
-               ins->klass = cmethod->klass;
+               ins->klass = klass;
                ins->sreg1 = vreg;
-               ins->inst_c0 = intrinsic->opcode >> shift_bits;
+               ins->inst_c0 = index >> shift_bits;
                ins->type = STACK_VTYPE;
                ins->dreg = vreg = alloc_ireg (cfg);
                MONO_ADD_INS (cfg->cbb, ins);
        }
 
-       MONO_INST_NEW (cfg, ins, mono_type_to_extract_op (sig->ret));
-       ins->klass = cmethod->klass;
+       MONO_INST_NEW (cfg, ins, mono_type_to_extract_op (type));
+       ins->klass = klass;
        ins->sreg1 = vreg;
        ins->type = STACK_I4;
        ins->dreg = vreg = alloc_ireg (cfg);
        if (cfg->compile_llvm)
-               ins->inst_c0 = intrinsic->opcode;
+               ins->inst_c0 = index;
        else
-               ins->inst_c0 = intrinsic->opcode & ((1 << shift_bits) - 1);
+               ins->inst_c0 = index & ((1 << shift_bits) - 1);
        MONO_ADD_INS (cfg->cbb, ins);
 
-       if (sig->ret->type == MONO_TYPE_R4) {
+       if (type->type == MONO_TYPE_R4) {
                MONO_INST_NEW (cfg, ins, cfg->r4fp ? OP_ICONV_TO_R4_RAW : OP_MOVE_I4_TO_F);
                ins->klass = mono_defaults.single_class;
                ins->sreg1 = vreg;
@@ -1168,7 +1353,15 @@ simd_intrinsic_emit_getter (const SimdIntrinsc *intrinsic, MonoCompile *cfg, Mon
 }
 
 static MonoInst*
-simd_intrinsic_emit_long_getter (const SimdIntrinsc *intrinsic, MonoCompile *cfg, MonoMethod *cmethod, MonoInst **args)
+simd_intrinsic_emit_getter (const SimdIntrinsic *intrinsic, MonoCompile *cfg, MonoMethod *cmethod, MonoInst **args)
+{
+       MonoMethodSignature *sig = mono_method_signature (cmethod);
+
+       return simd_intrinsic_emit_getter_op (cfg, intrinsic->opcode, cmethod->klass, sig->ret, args [0]);
+}
+
+static MonoInst*
+simd_intrinsic_emit_long_getter (const SimdIntrinsic *intrinsic, MonoCompile *cfg, MonoMethod *cmethod, MonoInst **args)
 {
        MonoInst *ins;
        int vreg;
@@ -1194,7 +1387,7 @@ simd_intrinsic_emit_long_getter (const SimdIntrinsc *intrinsic, MonoCompile *cfg
 }
 
 static MonoInst*
-simd_intrinsic_emit_ctor (const SimdIntrinsc *intrinsic, MonoCompile *cfg, MonoMethod *cmethod, MonoInst **args)
+simd_intrinsic_emit_ctor (const SimdIntrinsic *intrinsic, MonoCompile *cfg, MonoMethod *cmethod, MonoInst **args)
 {
        MonoInst *ins = NULL;
        int i, addr_reg;
@@ -1202,6 +1395,7 @@ simd_intrinsic_emit_ctor (const SimdIntrinsc *intrinsic, MonoCompile *cfg, MonoM
        MonoMethodSignature *sig = mono_method_signature (cmethod);
        int store_op = mono_type_to_store_membase (cfg, sig->params [0]);
        int arg_size = mono_type_size (sig->params [0], &i);
+       int opcode;
 
        if (sig->param_count == 1) {
                int dreg;
@@ -1214,7 +1408,11 @@ simd_intrinsic_emit_ctor (const SimdIntrinsc *intrinsic, MonoCompile *cfg, MonoM
                        dreg = alloc_ireg (cfg);
                }
 
-               MONO_INST_NEW (cfg, ins, intrinsic->opcode);
+               if (intrinsic)
+                       opcode = intrinsic->opcode;
+               else
+                       opcode = mono_type_to_expand_op (sig->params [0]);
+               MONO_INST_NEW (cfg, ins, opcode);
                ins->klass = cmethod->klass;
                ins->sreg1 = args [1]->dreg;
                ins->type = STACK_VTYPE;
@@ -1248,6 +1446,20 @@ simd_intrinsic_emit_ctor (const SimdIntrinsc *intrinsic, MonoCompile *cfg, MonoM
                EMIT_NEW_STORE_MEMBASE (cfg, ins, store_op, addr_reg, i * arg_size, args [i + 1]->dreg);
        }
 
+       if (sig->param_count * arg_size < 16) {
+               /* If there are not enough arguments, fill the rest with 0s */
+               for (i = sig->param_count; i < 16 / arg_size; ++i) {
+                       switch (arg_size) {
+                       case 4:
+                               MONO_EMIT_NEW_STORE_MEMBASE_IMM (cfg, OP_STOREI4_MEMBASE_IMM, addr_reg, i * arg_size, 0);
+                               break;
+                       default:
+                               g_assert_not_reached ();
+                               break;
+                       }
+               }
+       }
+
        if (is_ldaddr) { /*Eliminate LDADDR if it's initing a local var*/
                int vreg = ((MonoInst*)args [0]->inst_p0)->dreg;
                NULLIFY_INS (args [0]);
@@ -1263,16 +1475,22 @@ simd_intrinsic_emit_ctor (const SimdIntrinsc *intrinsic, MonoCompile *cfg, MonoM
 }
 
 static MonoInst*
-simd_intrinsic_emit_cast (const SimdIntrinsc *intrinsic, MonoCompile *cfg, MonoMethod *cmethod, MonoInst **args)
+simd_intrinsic_emit_cast (const SimdIntrinsic *intrinsic, MonoCompile *cfg, MonoMethod *cmethod, MonoInst **args)
 {
        MonoInst *ins;
+       MonoClass *klass;
        int vreg;
 
        vreg = get_simd_vreg (cfg, cmethod, args [0]);          
 
-       //TODO macroize this
+       if (cmethod->is_inflated)
+               /* Vector<T> */
+               klass = mono_class_from_mono_type (mono_method_signature (cmethod)->ret);
+       else
+               klass = cmethod->klass;
+
        MONO_INST_NEW (cfg, ins, OP_XMOVE);
-       ins->klass = cmethod->klass;
+       ins->klass = klass;
        ins->type = STACK_VTYPE;
        ins->sreg1 = vreg;
        ins->dreg = alloc_ireg (cfg);
@@ -1281,7 +1499,7 @@ simd_intrinsic_emit_cast (const SimdIntrinsc *intrinsic, MonoCompile *cfg, MonoM
 }
 
 static MonoInst*
-simd_intrinsic_emit_shift (const SimdIntrinsc *intrinsic, MonoCompile *cfg, MonoMethod *cmethod, MonoInst **args)
+simd_intrinsic_emit_shift (const SimdIntrinsic *intrinsic, MonoCompile *cfg, MonoMethod *cmethod, MonoInst **args)
 {
        MonoInst *ins;
        int vreg, vreg2 = -1, opcode = intrinsic->opcode;
@@ -1322,23 +1540,22 @@ mono_op_is_packed_compare (int op)
 }
 
 static MonoInst*
-simd_intrinsic_emit_equality (const SimdIntrinsc *intrinsic, MonoCompile *cfg, MonoMethod *cmethod, MonoInst **args)
+simd_intrinsic_emit_equality_op (MonoCompile *cfg, MonoMethod *cmethod, MonoInst **args, int opcode, int flags)
 {
        MonoInst* ins;
        int left_vreg, right_vreg, tmp_vreg;
 
-       left_vreg = get_simd_vreg (cfg, cmethod, args [0]);
+       left_vreg = load_simd_vreg (cfg, cmethod, args [0], NULL);
        right_vreg = get_simd_vreg (cfg, cmethod, args [1]);
        
-
-       MONO_INST_NEW (cfg, ins, intrinsic->opcode);
+       MONO_INST_NEW (cfg, ins, opcode);
        ins->klass = cmethod->klass;
        ins->sreg1 = left_vreg;
        ins->sreg2 = right_vreg;
        ins->type = STACK_VTYPE;
        ins->klass = cmethod->klass;
        ins->dreg = tmp_vreg = alloc_ireg (cfg);
-       ins->inst_c0 = intrinsic->flags;
+       ins->inst_c0 = flags;
        MONO_ADD_INS (cfg->cbb, ins);
 
        /*FIXME the next ops are SSE specific*/
@@ -1350,9 +1567,9 @@ simd_intrinsic_emit_equality (const SimdIntrinsc *intrinsic, MonoCompile *cfg, M
        MONO_ADD_INS (cfg->cbb, ins);
 
        /*FP ops have a not equal instruction, which means that we must test the results with OR semantics.*/
-       if (mono_op_is_packed_compare (intrinsic->opcode) || intrinsic->flags == SIMD_COMP_EQ) {
+       if (mono_op_is_packed_compare (opcode) || flags == SIMD_COMP_EQ) {
                MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, tmp_vreg, 0xFFFF);
-               NEW_UNALU (cfg, ins, intrinsic->flags == SIMD_COMP_EQ ? OP_CEQ : OP_CLT_UN, tmp_vreg, -1);
+               NEW_UNALU (cfg, ins, flags == SIMD_COMP_EQ ? OP_CEQ : OP_CLT_UN, tmp_vreg, -1);
        } else {
                MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, tmp_vreg, 0);
                NEW_UNALU (cfg, ins, OP_CGT_UN, tmp_vreg, -1);
@@ -1361,9 +1578,14 @@ simd_intrinsic_emit_equality (const SimdIntrinsc *intrinsic, MonoCompile *cfg, M
        return ins;
 }
 
+static MonoInst*
+simd_intrinsic_emit_equality (const SimdIntrinsic *intrinsic, MonoCompile *cfg, MonoMethod *cmethod, MonoInst **args)
+{
+       return simd_intrinsic_emit_equality_op (cfg, cmethod, args, intrinsic->opcode, intrinsic->flags);
+}
 
 static MonoInst*
-simd_intrinsic_emit_shuffle (const SimdIntrinsc *intrinsic, MonoCompile *cfg, MonoMethod *cmethod, MonoInst **args)
+simd_intrinsic_emit_shuffle (const SimdIntrinsic *intrinsic, MonoCompile *cfg, MonoMethod *cmethod, MonoInst **args)
 {
        MonoInst *ins;
        int vreg, vreg2 = -1;
@@ -1396,7 +1618,7 @@ simd_intrinsic_emit_shuffle (const SimdIntrinsc *intrinsic, MonoCompile *cfg, Mo
 }
 
 static MonoInst*
-simd_intrinsic_emit_load_aligned (const SimdIntrinsc *intrinsic, MonoCompile *cfg, MonoMethod *cmethod, MonoInst **args)
+simd_intrinsic_emit_load_aligned (const SimdIntrinsic *intrinsic, MonoCompile *cfg, MonoMethod *cmethod, MonoInst **args)
 {
        MonoInst *ins;
 
@@ -1410,7 +1632,7 @@ simd_intrinsic_emit_load_aligned (const SimdIntrinsc *intrinsic, MonoCompile *cf
 }
 
 static MonoInst*
-simd_intrinsic_emit_store (const SimdIntrinsc *intrinsic, MonoCompile *cfg, MonoMethod *cmethod, MonoInst **args)
+simd_intrinsic_emit_store (const SimdIntrinsic *intrinsic, MonoCompile *cfg, MonoMethod *cmethod, MonoInst **args)
 {
        MonoInst *ins;
        int vreg;
@@ -1427,7 +1649,7 @@ simd_intrinsic_emit_store (const SimdIntrinsc *intrinsic, MonoCompile *cfg, Mono
 }
 
 static MonoInst*
-simd_intrinsic_emit_extract_mask (const SimdIntrinsc *intrinsic, MonoCompile *cfg, MonoMethod *cmethod, MonoInst **args)
+simd_intrinsic_emit_extract_mask (const SimdIntrinsic *intrinsic, MonoCompile *cfg, MonoMethod *cmethod, MonoInst **args)
 {
        MonoInst *ins;
        int vreg;
@@ -1445,7 +1667,7 @@ simd_intrinsic_emit_extract_mask (const SimdIntrinsc *intrinsic, MonoCompile *cf
 }
 
 static MonoInst*
-simd_intrinsic_emit_prefetch (const SimdIntrinsc *intrinsic, MonoCompile *cfg, MonoMethod *cmethod, MonoInst **args)
+simd_intrinsic_emit_prefetch (const SimdIntrinsic *intrinsic, MonoCompile *cfg, MonoMethod *cmethod, MonoInst **args)
 {
        MonoInst *ins;
 
@@ -1457,6 +1679,19 @@ simd_intrinsic_emit_prefetch (const SimdIntrinsc *intrinsic, MonoCompile *cfg, M
        return ins;
 }
 
+static MonoInst*
+simd_intrinsic_emit_const (const SimdIntrinsic *intrinsic, MonoCompile *cfg, MonoMethod *cmethod, MonoInst **args)
+{
+       MonoInst *ins;
+
+       MONO_INST_NEW (cfg, ins, intrinsic->opcode);
+       ins->klass = cmethod->klass;
+       ins->type = STACK_VTYPE;
+       ins->dreg = alloc_xreg (cfg);
+       MONO_ADD_INS (cfg->cbb, ins);
+       return ins;
+}
+
 static const char *
 simd_version_name (guint32 version)
 {
@@ -1480,9 +1715,9 @@ simd_version_name (guint32 version)
 }
 
 static MonoInst*
-emit_intrinsics (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSignature *fsig, MonoInst **args, const SimdIntrinsc *intrinsics, guint32 size)
+emit_intrinsics (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSignature *fsig, MonoInst **args, const SimdIntrinsic *intrinsics, guint32 size)
 {
-       const SimdIntrinsc *result = (const SimdIntrinsc *)mono_binary_search (cmethod->name, intrinsics, size, sizeof (SimdIntrinsc), &simd_intrinsic_compare_by_name);
+       const SimdIntrinsic *result = (const SimdIntrinsic *)mono_binary_search (cmethod->name, intrinsics, size, sizeof (SimdIntrinsic), &simd_intrinsic_compare_by_name);
        if (!result) {
                DEBUG (printf ("function doesn't have a simd intrinsic %s::%s/%d\n", cmethod->klass->name, cmethod->name, fsig->param_count));
                return NULL;
@@ -1634,11 +1869,29 @@ emit_simd_runtime_intrinsics (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodS
        return NULL;
 }
 
+static gboolean
+is_sys_numerics_assembly (MonoAssembly *assembly)
+{
+       return !strcmp ("System.Numerics", assembly->aname.name);
+}
+
+static gboolean
+is_sys_numerics_vectors_assembly (MonoAssembly *assembly)
+{
+       return !strcmp ("System.Numerics.Vectors", assembly->aname.name);
+}
+
 MonoInst*
 mono_emit_simd_intrinsics (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSignature *fsig, MonoInst **args)
 {
        const char *class_name;
 
+       if (is_sys_numerics_assembly (cmethod->klass->image->assembly))
+               return emit_sys_numerics_intrinsics (cfg, cmethod, fsig, args);
+
+       if (is_sys_numerics_vectors_assembly (cmethod->klass->image->assembly))
+               return emit_sys_numerics_vectors_intrinsics (cfg, cmethod, fsig, args);
+
        if (strcmp ("Mono.Simd", cmethod->klass->image->assembly->aname.name) ||
            strcmp ("Mono.Simd", cmethod->klass->name_space))
                return NULL;
@@ -1659,28 +1912,394 @@ mono_emit_simd_intrinsics (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSign
 
        cfg->uses_simd_intrinsics = 1;
        if (!strcmp ("Vector2d", class_name))
-               return emit_intrinsics (cfg, cmethod, fsig, args, vector2d_intrinsics, sizeof (vector2d_intrinsics) / sizeof (SimdIntrinsc));
+               return emit_intrinsics (cfg, cmethod, fsig, args, vector2d_intrinsics, sizeof (vector2d_intrinsics) / sizeof (SimdIntrinsic));
        if (!strcmp ("Vector4f", class_name))
-               return emit_intrinsics (cfg, cmethod, fsig, args, vector4f_intrinsics, sizeof (vector4f_intrinsics) / sizeof (SimdIntrinsc));
+               return emit_intrinsics (cfg, cmethod, fsig, args, vector4f_intrinsics, sizeof (vector4f_intrinsics) / sizeof (SimdIntrinsic));
        if (!strcmp ("Vector2ul", class_name))
-               return emit_intrinsics (cfg, cmethod, fsig, args, vector2ul_intrinsics, sizeof (vector2ul_intrinsics) / sizeof (SimdIntrinsc));
+               return emit_intrinsics (cfg, cmethod, fsig, args, vector2ul_intrinsics, sizeof (vector2ul_intrinsics) / sizeof (SimdIntrinsic));
        if (!strcmp ("Vector2l", class_name))
-               return emit_intrinsics (cfg, cmethod, fsig, args, vector2l_intrinsics, sizeof (vector2l_intrinsics) / sizeof (SimdIntrinsc));
+               return emit_intrinsics (cfg, cmethod, fsig, args, vector2l_intrinsics, sizeof (vector2l_intrinsics) / sizeof (SimdIntrinsic));
        if (!strcmp ("Vector4ui", class_name))
-               return emit_intrinsics (cfg, cmethod, fsig, args, vector4ui_intrinsics, sizeof (vector4ui_intrinsics) / sizeof (SimdIntrinsc));
+               return emit_intrinsics (cfg, cmethod, fsig, args, vector4ui_intrinsics, sizeof (vector4ui_intrinsics) / sizeof (SimdIntrinsic));
        if (!strcmp ("Vector4i", class_name))
-               return emit_intrinsics (cfg, cmethod, fsig, args, vector4i_intrinsics, sizeof (vector4i_intrinsics) / sizeof (SimdIntrinsc));
+               return emit_intrinsics (cfg, cmethod, fsig, args, vector4i_intrinsics, sizeof (vector4i_intrinsics) / sizeof (SimdIntrinsic));
        if (!strcmp ("Vector8us", class_name))
-               return emit_intrinsics (cfg, cmethod, fsig, args, vector8us_intrinsics, sizeof (vector8us_intrinsics) / sizeof (SimdIntrinsc));
+               return emit_intrinsics (cfg, cmethod, fsig, args, vector8us_intrinsics, sizeof (vector8us_intrinsics) / sizeof (SimdIntrinsic));
        if (!strcmp ("Vector8s", class_name))
-               return emit_intrinsics (cfg, cmethod, fsig, args, vector8s_intrinsics, sizeof (vector8s_intrinsics) / sizeof (SimdIntrinsc));
+               return emit_intrinsics (cfg, cmethod, fsig, args, vector8s_intrinsics, sizeof (vector8s_intrinsics) / sizeof (SimdIntrinsic));
        if (!strcmp ("Vector16b", class_name))
-               return emit_intrinsics (cfg, cmethod, fsig, args, vector16b_intrinsics, sizeof (vector16b_intrinsics) / sizeof (SimdIntrinsc));
+               return emit_intrinsics (cfg, cmethod, fsig, args, vector16b_intrinsics, sizeof (vector16b_intrinsics) / sizeof (SimdIntrinsic));
        if (!strcmp ("Vector16sb", class_name))
-               return emit_intrinsics (cfg, cmethod, fsig, args, vector16sb_intrinsics, sizeof (vector16sb_intrinsics) / sizeof (SimdIntrinsc));
+               return emit_intrinsics (cfg, cmethod, fsig, args, vector16sb_intrinsics, sizeof (vector16sb_intrinsics) / sizeof (SimdIntrinsic));
+
+       return NULL;
+}
+
+// The entries should be ordered by name
+// System.Numerics.Vector2/Vector3/Vector4
+static const SimdIntrinsic vector2_intrinsics[] = {
+       { SN_ctor, OP_EXPAND_R4 },
+       { SN_Abs },
+       { SN_Dot, OP_DPPS },
+       { SN_Equals, OP_COMPPS, SIMD_VERSION_SSE1, SIMD_EMIT_EQUALITY, SIMD_COMP_EQ },
+       { SN_Max, OP_MAXPS, SIMD_VERSION_SSE1, SIMD_EMIT_BINARY },
+       { SN_Min, OP_MINPS, SIMD_VERSION_SSE1, SIMD_EMIT_BINARY },
+       { SN_SquareRoot, OP_SQRTPS, SIMD_VERSION_SSE1, SIMD_EMIT_UNARY },
+       { SN_op_Addition, OP_ADDPS, SIMD_VERSION_SSE1, SIMD_EMIT_BINARY },
+       { SN_op_Division, OP_DIVPS, SIMD_VERSION_SSE1, SIMD_EMIT_BINARY },
+       { SN_op_Multiply, OP_MULPS, SIMD_VERSION_SSE1, SIMD_EMIT_BINARY },
+       { SN_op_Subtraction, OP_SUBPS, SIMD_VERSION_SSE1, SIMD_EMIT_BINARY },
+};
+
+static MonoInst*
+emit_vector_intrinsics (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSignature *fsig, MonoInst **args)
+{
+       const SimdIntrinsic *intrins;
+       MonoMethodSignature *sig = mono_method_signature (cmethod);
+
+       /*
+        * Vector2/3/4 are handled the same way, since the underlying SIMD type is the same (4 * r4).
+        */
+       intrins = (const SimdIntrinsic*)mono_binary_search (cmethod->name, vector2_intrinsics, sizeof (vector2_intrinsics) / sizeof (SimdIntrinsic), sizeof (SimdIntrinsic), &simd_intrinsic_compare_by_name);
+       if (!intrins) {
+               //printf ("%s\n", mono_method_full_name (cmethod, 1));
+               return NULL;
+       }
+
+       if (cfg->verbose_level > 1) {
+               char *name = mono_method_full_name (cmethod, TRUE);
+               printf ("  SIMD intrinsic %s\n", name);
+               g_free (name);
+       }
+
+       switch (intrins->name) {
+       case SN_ctor:
+               return simd_intrinsic_emit_ctor (intrins, cfg, cmethod, args);
+               break;
+       case SN_Equals:
+               return simd_intrinsic_emit_equality (intrins, cfg, cmethod, args);
+               break;
+       case SN_SquareRoot:
+               return simd_intrinsic_emit_unary (intrins, cfg, cmethod, args);
+               break;
+       case SN_Dot:
+               if (COMPILE_LLVM (cfg)) {
+                       MonoInst *ins;
+
+                       ins = simd_intrinsic_emit_binary (intrins, cfg, cmethod, args);
+                       /* The end result is in the lowest element */
+                       return simd_intrinsic_emit_getter_op (cfg, 0, cmethod->klass, mono_method_signature (cmethod)->ret, ins);
+               }
+               break;
+       case SN_Abs: {
+               // abs(x) = max(x, sub(0,x))
+               MonoInst *sub;
+               MonoInst *zero;
+
+               MONO_INST_NEW (cfg, zero, OP_XZERO);
+               zero->dreg = alloc_xreg (cfg);
+               zero->klass = cmethod->klass;
+               MONO_ADD_INS (cfg->cbb, zero);
+
+               sub = simd_intrinsic_emit_binary_op (cfg, OP_SUBPS, 0, cmethod->klass, sig->params [0], sig->params [0], zero, args [0]);
+               return simd_intrinsic_emit_binary_op (cfg, OP_MAXPS, 0, cmethod->klass, sig->params [0], sig->params [0], args [0], sub);
+       }
+       case SN_Max:
+       case SN_Min:
+       case SN_op_Addition:
+       case SN_op_Division:
+       case SN_op_Multiply:
+       case SN_op_Subtraction:
+               return simd_intrinsic_emit_binary (intrins, cfg, cmethod, args);
+       default:
+               break;
+       }
+
+       return NULL;
+}
+
+static const SimdIntrinsic vector_t_intrinsics[] = {
+       { SN_ctor },
+       { SN_Abs },
+       { SN_Equals },
+       { SN_GreaterThan },
+       { SN_GreaterThanOrEqual },
+       { SN_LessThan },
+       { SN_LessThanOrEqual },
+       { SN_get_AllOnes, OP_XONES },
+       { SN_get_Count },
+       { SN_get_Item },
+       { SN_get_Zero, OP_XZERO },
+       { SN_op_Addition },
+       { SN_op_Division },
+       { SN_op_Explicit },
+       { SN_op_Multiply },
+       { SN_op_Subtraction }
+};
+
+static MonoInst*
+emit_vector_t_intrinsics (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSignature *fsig, MonoInst **args)
+{
+       const SimdIntrinsic *intrins;
+       MonoType *etype;
+       MonoInst *ins;
+       int size, len, index;
+
+       intrins = (const SimdIntrinsic*)mono_binary_search (cmethod->name, vector_t_intrinsics, sizeof (vector_t_intrinsics) / sizeof (SimdIntrinsic), sizeof (SimdIntrinsic), &simd_intrinsic_compare_by_name);
+       if (!intrins) {
+               //printf ("%s\n", mono_method_full_name (cmethod, 1));
+               return NULL;
+       }
+
+       if (cfg->verbose_level > 1) {
+               char *name = mono_method_full_name (cmethod, TRUE);
+               printf ("  SIMD intrinsic %s\n", name);
+               g_free (name);
+       }
+
+       etype = cmethod->klass->generic_class->context.class_inst->type_argv [0];
+       size = mono_class_value_size (mono_class_from_mono_type (etype), NULL);
+       g_assert (size);
+       len = 16 / size;
+
+       switch (intrins->name) {
+       case SN_get_Count:
+               EMIT_NEW_ICONST (cfg, ins, len);
+               return ins;
+       case SN_get_AllOnes:
+       case SN_get_Zero:
+               return simd_intrinsic_emit_const (intrins, cfg, cmethod, args);
+       case SN_get_Item:
+               g_assert (fsig->param_count == 1);
+               if (args [1]->opcode != OP_ICONST)
+                       return NULL;
+               index = args [1]->inst_c0;
+               if (index < 0 || index >= len)
+                       return NULL;
+               return simd_intrinsic_emit_getter_op (cfg, index, cmethod->klass, etype, args [0]);
+       case SN_ctor:
+               if (fsig->param_count == 1 && mono_metadata_type_equal (fsig->params [0], etype))
+                       return simd_intrinsic_emit_ctor (NULL, cfg, cmethod, args);
+               if ((fsig->param_count == 1 || fsig->param_count == 2) && (fsig->params [0]->type == MONO_TYPE_SZARRAY)) {
+                       MonoInst *array_ins = args [1];
+                       MonoInst *index_ins;
+                       MonoInst *ldelema_ins;
+                       MonoInst *var;
+                       int end_index_reg;
+
+                       /* .ctor (T[]) or .ctor (T[], index) */
+
+                       if (fsig->param_count == 2) {
+                               index_ins = args [2];
+                       } else {
+                               EMIT_NEW_ICONST (cfg, index_ins, 0);
+                       }
+
+                       /* Emit index check for the end (index + len - 1 < array length) */
+                       end_index_reg = alloc_ireg (cfg);
+                       EMIT_NEW_BIALU_IMM (cfg, ins, OP_IADD_IMM, end_index_reg, index_ins->dreg, len - 1);
+                       MONO_EMIT_BOUNDS_CHECK (cfg, array_ins->dreg, MonoArray, max_length, end_index_reg);
+
+                       /* Load the array slice into the simd reg */
+                       ldelema_ins = mini_emit_ldelema_1_ins (cfg, mono_class_from_mono_type (etype), array_ins, index_ins, TRUE);
+                       g_assert (args [0]->opcode == OP_LDADDR);
+                       var = args [0]->inst_p0;
+                       EMIT_NEW_LOAD_MEMBASE (cfg, ins, OP_LOADX_MEMBASE, var->dreg, ldelema_ins->dreg, 0);
+                       ins->klass = cmethod->klass;
+                       return args [0];
+               }
+               break;
+       case SN_op_Explicit:
+               return simd_intrinsic_emit_cast (intrins, cfg, cmethod, args);
+       case SN_Equals:
+               if (fsig->param_count == 1)
+                       return simd_intrinsic_emit_equality_op (cfg, cmethod, args, type_to_comp_op (etype), SIMD_COMP_EQ);
+               if (fsig->param_count == 2)
+                       return simd_intrinsic_emit_binary_op (cfg, type_to_comp_op (etype), 0, cmethod->klass, fsig->params [0], fsig->params [1], args [0], args [1]);
+               break;
+
+       case SN_GreaterThan:
+       case SN_GreaterThanOrEqual:
+       case SN_LessThan: {
+               MonoInst *cmp1, *cmp2;
+               int eq_op, gt_op;
+
+               switch (etype->type) {
+               case MONO_TYPE_I1:
+               case MONO_TYPE_I2:
+               case MONO_TYPE_I4:
+               case MONO_TYPE_I8:
+                       break;
+               default:
+                       return NULL;
+               }
+
+               eq_op = type_to_comp_op (etype);
+               gt_op = type_to_gt_op (etype);
+
+               switch (intrins->name) {
+               case SN_GreaterThan:
+                       return simd_intrinsic_emit_binary_op (cfg, gt_op, 0, cmethod->klass, fsig->params [0], fsig->params [1], args [0], args [1]);
+               case SN_LessThanOrEqual:
+                       return simd_intrinsic_emit_binary_op (cfg, gt_op, 0, cmethod->klass, fsig->params [0], fsig->params [1], args [1], args [0]);
+               case SN_GreaterThanOrEqual:
+                       cmp1 = simd_intrinsic_emit_binary_op (cfg, eq_op, 0, cmethod->klass, fsig->params [0], fsig->params [1], args [0], args [1]);
+                       cmp2 = simd_intrinsic_emit_binary_op (cfg, gt_op, 0, cmethod->klass, fsig->params [0], fsig->params [1], args [0], args [1]);
+                       return simd_intrinsic_emit_binary_op (cfg, OP_POR, 0, cmethod->klass, fsig->params [0], fsig->params [1], cmp1, cmp2);
+               case SN_LessThan:
+                       cmp1 = simd_intrinsic_emit_binary_op (cfg, eq_op, 0, cmethod->klass, fsig->params [0], fsig->params [1], args [1], args [0]);
+                       cmp2 = simd_intrinsic_emit_binary_op (cfg, gt_op, 0, cmethod->klass, fsig->params [0], fsig->params [1], args [1], args [0]);
+                       return simd_intrinsic_emit_binary_op (cfg, OP_POR, 0, cmethod->klass, fsig->params [0], fsig->params [1], cmp1, cmp2);
+               default:
+                       g_assert_not_reached ();
+                       break;
+               }
+       }
+       case SN_Abs:
+               /* Vector<T>.Abs */
+               switch (etype->type) {
+               case MONO_TYPE_U1:
+               case MONO_TYPE_U2:
+               case MONO_TYPE_U4:
+               case MONO_TYPE_U8: {
+                       MonoInst *ins;
+
+                       /* No-op */
+                       MONO_INST_NEW (cfg, ins, OP_XMOVE);
+                       ins->klass = cmethod->klass;
+                       ins->type = STACK_VTYPE;
+                       ins->sreg1 = args [0]->dreg;
+                       ins->dreg = alloc_xreg (cfg);
+                       MONO_ADD_INS (cfg->cbb, ins);
+                       return ins;
+               }
+               default:
+                       break;
+               }
+               break;
+       case SN_op_Addition: {
+               int op = type_to_padd_op (etype);
+               if (op != -1)
+                       return simd_intrinsic_emit_binary_op (cfg, op, 0, cmethod->klass, fsig->params [0], fsig->params [0], args [0], args [1]);
+               break;
+       }
+       case SN_op_Subtraction: {
+               int op = type_to_psub_op (etype);
+               if (op != -1)
+                       return simd_intrinsic_emit_binary_op (cfg, op, 0, cmethod->klass, fsig->params [0], fsig->params [0], args [0], args [1]);
+               break;
+       }
+       case SN_op_Multiply: {
+               int op = type_to_pmul_op (etype);
+               if (op != -1)
+                       return simd_intrinsic_emit_binary_op (cfg, op, 0, cmethod->klass, fsig->params [0], fsig->params [0], args [0], args [1]);
+               break;
+       }
+       case SN_op_Division: {
+               int op = type_to_pdiv_op (etype);
+               if (op != -1)
+                       return simd_intrinsic_emit_binary_op (cfg, op, 0, cmethod->klass, fsig->params [0], fsig->params [0], args [0], args [1]);
+               break;
+       }
+       default:
+               break;
+       }
 
        return NULL;
 }
 
+/*
+ * emit_sys_numerics_intrinsics:
+ *
+ *   Emit intrinsics for the System.Numerics assembly.
+ */
+static MonoInst*
+emit_sys_numerics_intrinsics (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSignature *fsig, MonoInst **args)
+{
+       const char *nspace = cmethod->klass->name_space;
+       const char *class_name = cmethod->klass->name;
+
+       if (cfg->r4fp)
+               // FIXME:
+               return NULL;
+
+       if (!strcmp ("Vector2", class_name) || !strcmp ("Vector4", class_name) || !strcmp ("Vector3", class_name))
+               return emit_vector_intrinsics (cfg, cmethod, fsig, args);
+
+       if (!strcmp ("Vector`1", class_name))
+               return emit_vector_t_intrinsics (cfg, cmethod, fsig, args);
+
+       if (!strcmp ("System.Numerics", nspace) && !strcmp ("Vector", class_name)) {
+               if (!strcmp (cmethod->name, "get_IsHardwareAccelerated")) {
+                       MonoInst *ins;
+
+                       if (simd_supported_versions)
+                               EMIT_NEW_ICONST (cfg, ins, 1);
+                       else
+                               EMIT_NEW_ICONST (cfg, ins, 0);
+                       ins->type = STACK_I4;
+                       return ins;
+               }
+       }
+
+       return NULL;
+}
+
+static MonoInst*
+emit_sys_numerics_vectors_intrinsics (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSignature *fsig, MonoInst **args)
+{
+       const char *class_name = cmethod->klass->name;
+
+       if (cfg->r4fp)
+               // FIXME:
+               return NULL;
+
+       if (!strcmp (class_name, "Vector`1"))
+               return emit_vector_t_intrinsics (cfg, cmethod, fsig, args);
+       return NULL;
+}
+
+MonoInst*
+mono_emit_simd_field_load (MonoCompile *cfg, MonoClassField *field, MonoInst *addr)
+{
+       if (cfg->r4fp)
+               // FIXME:
+               return NULL;
+
+       if (is_sys_numerics_assembly (field->parent->image->assembly)) {
+               int index = -1;
+
+               if (!strcmp (field->parent->name, "Vector2") ||
+                       !strcmp (field->parent->name, "Vector3") ||
+                       !strcmp (field->parent->name, "Vector4")) {
+                       if (!strcmp (field->name, "X"))
+                               index = 0;
+                       else if (!strcmp (field->name, "Y"))
+                               index = 1;
+                       else if (!strcmp (field->name, "Z"))
+                               index = 2;
+                       else if (!strcmp (field->name, "W"))
+                               index = 3;
+               }
+
+               if (index != -1) {
+                       if (cfg->verbose_level > 1)
+                               printf ("  SIMD intrinsic field access: %s\n", field->name);
+
+                       return simd_intrinsic_emit_getter_op (cfg, index, field->parent, mono_field_get_type (field), addr);
+               }
+       }
+       return NULL;
+}
+
 #endif /* DISABLE_JIT */
+
+#else
+
+MonoInst*
+mono_emit_simd_field_load (MonoCompile *cfg, MonoClassField *field, MonoInst *addr)
+{
+       return NULL;
+}
+
 #endif /* MONO_ARCH_SIMD_INTRINSICS */
index 803d8daf1561bb4b8014414d1c389db1a90a7c79..725c5c304337ab89bbf31831b7b9698cf9590044 100644 (file)
@@ -1,3 +1,4 @@
+SIMD_METHOD("Abs", SN_Abs)
 SIMD_METHOD("AddSub", SN_AddSub)
 SIMD_METHOD("AddWithSaturation", SN_AddWithSaturation)
 SIMD_METHOD("AndNot", SN_AndNot)
@@ -16,14 +17,24 @@ SIMD_METHOD("ConvertToFloat", SN_ConvertToFloat)
 SIMD_METHOD("ConvertToInt", SN_ConvertToInt)
 SIMD_METHOD("ConvertToIntTruncated", SN_ConvertToIntTruncated)
 SIMD_METHOD(".ctor", SN_ctor)
+SIMD_METHOD("Dot", SN_Dot)
 SIMD_METHOD("Duplicate", SN_Duplicate)
 SIMD_METHOD("DuplicateHigh", SN_DuplicateHigh)
 SIMD_METHOD("DuplicateLow", SN_DuplicateLow)
+SIMD_METHOD("Equals", SN_Equals)
 SIMD_METHOD("ExtractByteMask", SN_ExtractByteMask)
+SIMD_METHOD("GreaterThan", SN_GreaterThan)
+SIMD_METHOD("GreaterThanOrEqual", SN_GreaterThanOrEqual)
+SIMD_METHOD("LessThan", SN_LessThan)
+SIMD_METHOD("LessThanOrEqual", SN_LessThanOrEqual)
+SIMD_METHOD("get_AllOnes", SN_get_AllOnes)
+SIMD_METHOD("get_Count", SN_get_Count)
+SIMD_METHOD("get_Item", SN_get_Item)
 SIMD_METHOD("get_W", SN_get_W)
 SIMD_METHOD("get_X", SN_get_X)
 SIMD_METHOD("get_Y", SN_get_Y)
 SIMD_METHOD("get_Z", SN_get_Z)
+SIMD_METHOD("get_Zero", SN_get_Zero)
 SIMD_METHOD("get_V0", SN_get_V0)
 SIMD_METHOD("get_V1", SN_get_V1)
 SIMD_METHOD("get_V2", SN_get_V2)
@@ -96,6 +107,7 @@ SIMD_METHOD("Shuffle", SN_Shuffle)
 SIMD_METHOD("SignedPackWithSignedSaturation", SN_SignedPackWithSignedSaturation)
 SIMD_METHOD("SignedPackWithUnsignedSaturation", SN_SignedPackWithUnsignedSaturation)
 SIMD_METHOD("Sqrt", SN_Sqrt)
+SIMD_METHOD("SquareRoot", SN_SquareRoot)
 SIMD_METHOD("StoreAligned", SN_StoreAligned)
 SIMD_METHOD("StoreNonTemporal", SN_StoreNonTemporal)
 SIMD_METHOD("SubtractWithSaturation", SN_SubtractWithSaturation)