+ if (cfg->verbose_level > 1) {
+ char *name = mono_method_full_name (cmethod, TRUE);
+ printf (" SIMD method %s not handled.\n", name);
+ g_free (name);
+ }
+ return NULL;
+}
+
+static MonoInst*
+emit_vector_is_hardware_accelerated_intrinsic (MonoCompile *cfg)
+{
+ 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;
+}
+
+/* These should be ordered by name */
+static const SimdIntrinsic vector_t_intrinsics[] = {
+ { SN_ctor },
+ { SN_Abs },
+ { SN_CopyTo },
+ { SN_Equals },
+ { SN_GreaterThan },
+ { SN_GreaterThanOrEqual },
+ { SN_LessThan },
+ { SN_LessThanOrEqual },
+ { SN_Max },
+ { SN_Min },
+ { SN_get_AllOnes, OP_XONES },
+ { SN_get_Count },
+ { SN_get_Item },
+ { SN_get_Zero, OP_XZERO },
+ { SN_op_Addition },
+ { SN_op_BitwiseAnd },
+ { SN_op_BitwiseOr },
+ { SN_op_Division },
+ { SN_op_ExclusiveOr },
+ { 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 *type, *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) {
+ assert_handled (cfg, cmethod);
+ return NULL;
+ }
+
+ type = &cmethod->klass->byval_arg;
+ etype = mono_class_get_context (cmethod->klass)->class_inst->type_argv [0];
+ size = mono_class_value_size (mono_class_from_mono_type (etype), NULL);
+ g_assert (size);
+ len = 16 / size;
+
+ if (!MONO_TYPE_IS_PRIMITIVE (etype))
+ 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_get_Count:
+ if (!(fsig->param_count == 0 && fsig->ret->type == MONO_TYPE_I4))
+ break;
+ EMIT_NEW_ICONST (cfg, ins, len);
+ return ins;
+ case SN_get_AllOnes:
+ case SN_get_Zero:
+ if (!(fsig->param_count == 0 && mono_metadata_type_equal (fsig->ret, type)))
+ break;
+ 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;
+
+ if (args [0]->opcode != OP_LDADDR)
+ return NULL;
+
+ /* .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 && fsig->ret->type == MONO_TYPE_BOOLEAN && mono_metadata_type_equal (fsig->params [0], type))
+ return simd_intrinsic_emit_equality_op (cfg, cmethod, args, type_to_comp_op (etype), SIMD_COMP_EQ);
+ if (fsig->param_count == 2 && mono_metadata_type_equal (fsig->ret, type) && mono_metadata_type_equal (fsig->params [0], type) && mono_metadata_type_equal (fsig->params [1], type))
+ 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:
+ case SN_LessThanOrEqual: {
+ 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_LessThan:
+ return simd_intrinsic_emit_binary_op (cfg, gt_op, 0, cmethod->klass, fsig->params [0], fsig->params [1], args [1], args [0]);
+ case SN_LessThanOrEqual:
+ 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);
+ 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);
+ 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:
+ case SN_op_Subtraction:
+ case SN_op_Multiply:
+ case SN_op_Division:
+ case SN_op_ExclusiveOr:
+ case SN_op_BitwiseAnd:
+ case SN_op_BitwiseOr:
+ case SN_Max:
+ case SN_Min: {
+ if (!(fsig->param_count == 2 && mono_metadata_type_equal (fsig->ret, fsig->params [0]) && mono_metadata_type_equal (fsig->params [0], fsig->params [1])))
+ break;
+ int op = 0;
+ switch (intrins->name) {
+ case SN_op_Addition:
+ op = type_to_padd_op (etype);
+ break;
+ case SN_op_Subtraction:
+ op = type_to_psub_op (etype);
+ break;
+ case SN_op_Multiply:
+ op = type_to_pmul_op (etype);
+ break;
+ case SN_op_Division:
+ op = type_to_pdiv_op (etype);
+ break;
+ case SN_op_ExclusiveOr:
+ op = type_to_pxor_op (etype);
+ break;
+ case SN_op_BitwiseAnd:
+ op = type_to_pand_op (etype);
+ break;
+ case SN_op_BitwiseOr:
+ op = type_to_por_op (etype);
+ break;
+ case SN_Min:
+ op = type_to_pmin_op (etype);
+ break;
+ case SN_Max:
+ op = type_to_pmax_op (etype);
+ break;
+ default:
+ g_assert_not_reached ();
+ }
+ 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_CopyTo: {
+ MonoInst *array_ins = args [1];
+ MonoInst *index_ins = args [2];
+ MonoInst *ldelema_ins;
+ MonoInst *var;
+ int end_index_reg;
+
+ if (args [0]->opcode != OP_LDADDR)
+ return NULL;
+
+ /* 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);
+
+ int length_reg = alloc_ireg (cfg);
+ MONO_EMIT_NEW_LOAD_MEMBASE_OP_FAULT (cfg, OP_LOADI4_MEMBASE, length_reg, array_ins->dreg, MONO_STRUCT_OFFSET (MonoArray, max_length));
+ MONO_EMIT_NEW_BIALU (cfg, OP_COMPARE, -1, length_reg, end_index_reg);
+ MONO_EMIT_NEW_COND_EXC (cfg, LE_UN, "ArgumentException");
+
+ /* Load the simd reg into the array slice */
+ 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_STORE_MEMBASE (cfg, ins, OP_STOREX_MEMBASE, ldelema_ins->dreg, 0, var->dreg);
+ ins->klass = cmethod->klass;
+ return args [0];
+ break;
+ }
+ default:
+ break;
+ }
+
+ assert_handled (cfg, cmethod);
+
+ if (cfg->verbose_level > 1) {
+ char *name = mono_method_full_name (cmethod, TRUE);
+ printf (" SIMD method %s not handled.\n", name);
+ g_free (name);
+ }
+
+ 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 (!strcmp ("Vector2", class_name) || !strcmp ("Vector4", class_name) || !strcmp ("Vector3", class_name))
+ return emit_vector_intrinsics (cfg, cmethod, fsig, args);
+
+ if (!strcmp ("System.Numerics", nspace) && !strcmp ("Vector", class_name)) {
+ if (!strcmp (cmethod->name, "get_IsHardwareAccelerated"))
+ return emit_vector_is_hardware_accelerated_intrinsic (cfg);
+ }
+
+ return NULL;
+}
+
+static MonoInst*
+emit_sys_numerics_vectors_intrinsics (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSignature *fsig, MonoInst **args)
+{
+ const char *nspace = cmethod->klass->name_space;
+ const char *class_name = cmethod->klass->name;
+
+ if (!strcmp (class_name, "Vector`1"))
+ return emit_vector_t_intrinsics (cfg, cmethod, fsig, args);
+
+ if (!strcmp ("System.Numerics", nspace) && !strcmp ("Vector", class_name)) {
+ if (!strcmp (cmethod->name, "get_IsHardwareAccelerated"))
+ return emit_vector_is_hardware_accelerated_intrinsic (cfg);
+ }
+
+ return NULL;
+}
+
+MonoInst*
+mono_emit_simd_field_load (MonoCompile *cfg, MonoClassField *field, MonoInst *addr)
+{
+ 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);
+ }
+ }