@@ -5667,6 +5667,109 @@ is_addressable_valuetype_load (MonoCompile* cfg, guint8* ip, MonoType* ldtype)
5667
5667
return is_load_instruction && is_in_previous_bb && is_struct ;
5668
5668
}
5669
5669
5670
+ /*
5671
+ * check_get_virtual_method_assumptions:
5672
+ *
5673
+ * This shadows mono_class_get_virtual_method, but instead of actually resolving
5674
+ * the virtual method, this only checks if mono_class_get_virtual_method would
5675
+ * succeed. This is in place because that function fails catastrophically in some
5676
+ * cases, bringing down the entire runtime. Returns TRUE if the function is safe
5677
+ * to call, FALSE otherwise.
5678
+ */
5679
+ static gboolean
5680
+ check_get_virtual_method_assumptions (MonoClass * klass , MonoMethod * method )
5681
+ {
5682
+ if (m_class_is_abstract (klass ))
5683
+ return FALSE;
5684
+
5685
+ if (((method -> flags & METHOD_ATTRIBUTE_FINAL ) || !(method -> flags & METHOD_ATTRIBUTE_VIRTUAL )))
5686
+ return TRUE;
5687
+
5688
+ mono_class_setup_vtable (klass );
5689
+ if (m_class_get_vtable (klass ) == NULL )
5690
+ return FALSE;
5691
+
5692
+ if (method -> slot == -1 ) {
5693
+ if (method -> is_inflated ) {
5694
+ if (((MonoMethodInflated * )method )-> declaring -> slot == -1 )
5695
+ return FALSE;
5696
+ } else {
5697
+ return FALSE;
5698
+ }
5699
+ }
5700
+
5701
+ if (method -> slot != -1 && mono_class_is_interface (method -> klass )) {
5702
+ gboolean variance_used = FALSE;
5703
+ int iface_offset = mono_class_interface_offset_with_variance (klass , method -> klass , & variance_used );
5704
+ if (iface_offset <= 0 )
5705
+ return FALSE;
5706
+ }
5707
+
5708
+ if (method -> is_inflated )
5709
+ return FALSE;
5710
+
5711
+ return TRUE;
5712
+ }
5713
+
5714
+ /*
5715
+ * try_prepare_objaddr_callvirt_optimization:
5716
+ *
5717
+ * Determine in a load+callvirt optimization can be performed and if so,
5718
+ * resolve the callvirt target method, so that it can behave as call.
5719
+ * Returns null, if the optimization cannot be performed.
5720
+ */
5721
+ static MonoMethod *
5722
+ try_prepare_objaddr_callvirt_optimization (MonoCompile * cfg , guchar * next_ip , guchar * end , MonoMethod * method , MonoGenericContext * generic_context , MonoClass * klass )
5723
+ {
5724
+ // TODO: relax the _is_def requirement?
5725
+ if (cfg -> compile_aot || cfg -> compile_llvm || !klass || !mono_class_is_def (klass ))
5726
+ return NULL ;
5727
+
5728
+ guchar * callvirt_ip ;
5729
+ guint32 callvirt_proc_token ;
5730
+ if (!(callvirt_ip = il_read_callvirt (next_ip , end , & callvirt_proc_token )) ||
5731
+ !ip_in_bb (cfg , cfg -> cbb , callvirt_ip ))
5732
+ return NULL ;
5733
+
5734
+ MonoMethod * iface_method = mini_get_method (cfg , method , callvirt_proc_token , NULL , generic_context );
5735
+ if (!iface_method ||
5736
+ iface_method -> is_generic ||
5737
+ iface_method -> dynamic || // Reflection.Emit-generated methods should have this flag
5738
+ !strcmp (iface_method -> name , "GetHashCode" )) // the callvirt handler itself optimizes those
5739
+ return NULL ;
5740
+
5741
+ MonoMethodSignature * iface_method_sig ;
5742
+ if (!((iface_method_sig = mono_method_signature_internal (iface_method )) &&
5743
+ iface_method_sig -> hasthis &&
5744
+ iface_method_sig -> param_count == 0 &&
5745
+ !iface_method_sig -> has_type_parameters &&
5746
+ iface_method_sig -> generic_param_count == 0 ))
5747
+ return NULL ;
5748
+
5749
+ if (!check_get_virtual_method_assumptions (klass , iface_method ))
5750
+ return NULL ;
5751
+
5752
+ ERROR_DECL (struct_method_error );
5753
+ MonoMethod * struct_method = mono_class_get_virtual_method (klass , iface_method , struct_method_error );
5754
+
5755
+ if (is_ok (struct_method_error )) {
5756
+ if (!struct_method || !MONO_METHOD_IS_FINAL (struct_method ))
5757
+ return NULL ;
5758
+
5759
+ MonoMethodSignature * struct_method_sig = mono_method_signature_internal (struct_method );
5760
+ if (!struct_method_sig ||
5761
+ struct_method_sig -> has_type_parameters ||
5762
+ !mono_method_can_access_method (method , struct_method )) {
5763
+ return NULL ;
5764
+ }
5765
+ } else {
5766
+ mono_error_cleanup (struct_method_error );
5767
+ return NULL ;
5768
+ }
5769
+
5770
+ return struct_method ;
5771
+ }
5772
+
5670
5773
/*
5671
5774
* handle_ctor_call:
5672
5775
*
@@ -7014,29 +7117,32 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b
7014
7117
case MONO_CEE_LDARG_S :
7015
7118
case MONO_CEE_LDARG :
7016
7119
CHECK_ARG (n );
7017
- if (next_ip < end && is_addressable_valuetype_load (cfg , next_ip , cfg -> arg_types [n ])) {
7120
+ if (next_ip < end && is_addressable_valuetype_load (cfg , next_ip , cfg -> arg_types [n ])) {
7018
7121
EMIT_NEW_ARGLOADA (cfg , ins , n );
7019
7122
} else {
7020
7123
EMIT_NEW_ARGLOAD (cfg , ins , n );
7021
7124
}
7022
7125
* sp ++ = ins ;
7126
+ /*if (!m_method_is_icall (method)) */ {
7127
+ MonoMethod * callvirt_target = try_prepare_objaddr_callvirt_optimization (cfg , next_ip , end , method , generic_context , param_types [n ]-> data .klass );
7128
+ if (callvirt_target )
7129
+ cmethod_override = callvirt_target ;
7130
+ }
7023
7131
break ;
7024
-
7025
7132
case MONO_CEE_LDLOC_0 :
7026
7133
case MONO_CEE_LDLOC_1 :
7027
7134
case MONO_CEE_LDLOC_2 :
7028
7135
case MONO_CEE_LDLOC_3 :
7029
7136
case MONO_CEE_LDLOC_S :
7030
7137
case MONO_CEE_LDLOC :
7031
7138
CHECK_LOCAL (n );
7032
- if (next_ip < end && is_addressable_valuetype_load (cfg , next_ip , header -> locals [n ])) {
7139
+ if (next_ip < end && is_addressable_valuetype_load (cfg , next_ip , header -> locals [n ])) {
7033
7140
EMIT_NEW_LOCLOADA (cfg , ins , n );
7034
7141
} else {
7035
7142
EMIT_NEW_LOCLOAD (cfg , ins , n );
7036
7143
}
7037
7144
* sp ++ = ins ;
7038
7145
break ;
7039
-
7040
7146
case MONO_CEE_STLOC_0 :
7041
7147
case MONO_CEE_STLOC_1 :
7042
7148
case MONO_CEE_STLOC_2 :
@@ -7477,7 +7583,7 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b
7477
7583
7478
7584
// The method to be called may have already been resolved when handling a previous opcode. In that
7479
7585
// case, we ignore the operand and act as CALL, instead of CALLVIRT.
7480
- // E.g. https://github.com/dotnet/runtime/issues/32166 (box+callvirt optimization)
7586
+ // E.g. https://github.com/dotnet/runtime/issues/32166 (box+callvirt optimization)
7481
7587
if (cmethod_override ) {
7482
7588
cmethod = cmethod_override ;
7483
7589
cmethod_override = NULL ;
0 commit comments