Skip to content

Commit 822fc81

Browse files
authored
[mono] Implement GC descriptor for structs with InlineArray attribute (#84097)
* Implement GC descriptor for structs with InlineArrayAttribute and add Mono GC test * Enable GC test on CoreCLR * Add warning when field_iter too large
1 parent 21c4a26 commit 822fc81

File tree

2 files changed

+109
-58
lines changed

2 files changed

+109
-58
lines changed

src/mono/mono/metadata/object.c

Lines changed: 63 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -874,72 +874,77 @@ compute_class_bitmap (MonoClass *klass, gsize *bitmap, int size, int offset, int
874874
/* special/collectible static */
875875
continue;
876876

877-
pos = field_offset / TARGET_SIZEOF_VOID_P;
878-
pos += offset;
879-
880-
type = mono_type_get_underlying_type (field->type);
881-
882-
switch (type->type) {
883-
case MONO_TYPE_U:
884-
case MONO_TYPE_I:
885-
case MONO_TYPE_PTR:
886-
case MONO_TYPE_FNPTR:
887-
break;
888-
case MONO_TYPE_STRING:
889-
case MONO_TYPE_SZARRAY:
890-
case MONO_TYPE_CLASS:
891-
case MONO_TYPE_OBJECT:
892-
case MONO_TYPE_ARRAY:
893-
g_assert ((m_field_get_offset (field) % wordsize) == 0);
894-
895-
g_assert (pos < GINT_TO_UINT32(size) || pos <= GINT_TO_UINT32(max_size));
896-
bitmap [pos / BITMAP_EL_SIZE] |= ((gsize)1) << (pos % BITMAP_EL_SIZE);
897-
*max_set = MAX (GINT_TO_UINT32(*max_set), pos);
898-
break;
899-
case MONO_TYPE_GENERICINST:
900-
if (!mono_type_generic_inst_is_valuetype (type)) {
877+
guint32 field_iter = 1;
878+
guint32 field_instance_offset = field_offset;
879+
// If struct has InlineArray attribute, iterate `length` times to set a bitmap
880+
if (m_class_is_inlinearray (p))
881+
field_iter = m_class_inlinearray_value (p);
882+
883+
if (field_iter > 500)
884+
g_warning ("Large number of iterations detected when creating a GC bitmap, might affect performance.");
885+
886+
while (field_iter) {
887+
pos = field_instance_offset / TARGET_SIZEOF_VOID_P;
888+
pos += offset;
889+
890+
type = mono_type_get_underlying_type (field->type);
891+
892+
switch (type->type) {
893+
case MONO_TYPE_U:
894+
case MONO_TYPE_I:
895+
case MONO_TYPE_PTR:
896+
case MONO_TYPE_FNPTR:
897+
break;
898+
case MONO_TYPE_STRING:
899+
case MONO_TYPE_SZARRAY:
900+
case MONO_TYPE_CLASS:
901+
case MONO_TYPE_OBJECT:
902+
case MONO_TYPE_ARRAY:
901903
g_assert ((m_field_get_offset (field) % wordsize) == 0);
902904

905+
g_assert (pos < GINT_TO_UINT32(size) || pos <= GINT_TO_UINT32(max_size));
903906
bitmap [pos / BITMAP_EL_SIZE] |= ((gsize)1) << (pos % BITMAP_EL_SIZE);
904907
*max_set = MAX (GINT_TO_UINT32(*max_set), pos);
905908
break;
906-
} else {
907-
/* fall through */
909+
case MONO_TYPE_GENERICINST:
910+
if (!mono_type_generic_inst_is_valuetype (type)) {
911+
g_assert ((m_field_get_offset (field) % wordsize) == 0);
912+
913+
bitmap [pos / BITMAP_EL_SIZE] |= ((gsize)1) << (pos % BITMAP_EL_SIZE);
914+
*max_set = MAX (GINT_TO_UINT32(*max_set), pos);
915+
break;
916+
} else {
917+
/* fall through */
918+
}
919+
case MONO_TYPE_TYPEDBYREF:
920+
case MONO_TYPE_VALUETYPE: {
921+
MonoClass *fclass = mono_class_from_mono_type_internal (field->type);
922+
if (m_class_has_references (fclass)) {
923+
/* remove the object header */
924+
compute_class_bitmap (fclass, bitmap, size, pos - MONO_OBJECT_HEADER_BITS, max_set, FALSE);
925+
}
926+
break;
908927
}
909-
case MONO_TYPE_TYPEDBYREF:
910-
case MONO_TYPE_VALUETYPE: {
911-
MonoClass *fclass = mono_class_from_mono_type_internal (field->type);
912-
if (m_class_has_references (fclass)) {
913-
/* remove the object header */
914-
compute_class_bitmap (fclass, bitmap, size, pos - MONO_OBJECT_HEADER_BITS, max_set, FALSE);
928+
case MONO_TYPE_I1:
929+
case MONO_TYPE_U1:
930+
case MONO_TYPE_I2:
931+
case MONO_TYPE_U2:
932+
case MONO_TYPE_I4:
933+
case MONO_TYPE_U4:
934+
case MONO_TYPE_I8:
935+
case MONO_TYPE_U8:
936+
case MONO_TYPE_R4:
937+
case MONO_TYPE_R8:
938+
case MONO_TYPE_BOOLEAN:
939+
case MONO_TYPE_CHAR:
940+
break;
941+
default:
942+
g_error ("compute_class_bitmap: Invalid type %x for field %s:%s\n", type->type, mono_type_get_full_name (m_field_get_parent (field)), field->name);
943+
break;
915944
}
916-
break;
917-
}
918-
case MONO_TYPE_I1:
919-
case MONO_TYPE_U1:
920-
case MONO_TYPE_I2:
921-
case MONO_TYPE_U2:
922-
case MONO_TYPE_I4:
923-
case MONO_TYPE_U4:
924-
case MONO_TYPE_I8:
925-
case MONO_TYPE_U8:
926-
case MONO_TYPE_R4:
927-
case MONO_TYPE_R8:
928-
case MONO_TYPE_BOOLEAN:
929-
case MONO_TYPE_CHAR:
930-
break;
931-
default:
932-
g_error ("compute_class_bitmap: Invalid type %x for field %s:%s\n", type->type, mono_type_get_full_name (m_field_get_parent (field)), field->name);
933-
break;
934-
}
935-
936-
// A struct with the inline array attribute is handled in the same way as an array
937-
if (m_class_is_inlinearray (klass)) {
938-
g_assert ((m_field_get_offset (field) % wordsize) == 0);
939945

940-
g_assert (pos < GINT_TO_UINT32(size) || pos <= GINT_TO_UINT32(max_size));
941-
bitmap [pos / BITMAP_EL_SIZE] |= ((gsize)1) << (pos % BITMAP_EL_SIZE);
942-
*max_set = MAX (GINT_TO_UINT32(*max_set), pos);
946+
field_instance_offset += field_offset;
947+
field_iter--;
943948
}
944949
}
945950
if (static_fields)

src/tests/Loader/classloader/InlineArray/InlineArrayValid.cs

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,21 @@ struct ObjShortArr
172172

173173
[UnscopedRef]
174174
public ref (object o, short s) this[int i] => ref Unsafe.Add(ref element, i);
175+
176+
[MethodImpl(MethodImplOptions.NoInlining)]
177+
public static ObjShortArr CreateArray(int recCount) {
178+
if (recCount > 0) {
179+
return CreateArray(recCount-1);
180+
} else {
181+
var arr = new ObjShortArr();
182+
for (short i = 0; i < ObjShortArr.Length; i++)
183+
{
184+
arr[i].o = i;
185+
arr[i].s = (short)(i + 1);
186+
}
187+
return arr;
188+
}
189+
}
175190
}
176191

177192
[Fact]
@@ -384,4 +399,35 @@ public static void GCDescOpt()
384399
Assert.Equal(1, *gcSeriesPtr);
385400
}
386401
}
402+
403+
// ====================== MonoGCDesc ==========================================================
404+
405+
class Holder {
406+
public ObjShortArr arr;
407+
}
408+
409+
static Holder CreateArray() {
410+
var arr = ObjShortArr.CreateArray(100);
411+
var holder = new Holder();
412+
holder.arr = arr;
413+
return holder;
414+
}
415+
416+
[Fact]
417+
public static void MonoGCDescOpt()
418+
{
419+
Console.WriteLine($"{nameof(MonoGCDescOpt)}...");
420+
421+
var holder = CreateArray();
422+
423+
GC.Collect(2, GCCollectionMode.Forced, true, true);
424+
425+
MakeGarbage();
426+
427+
for (short i = 0; i < ObjShortArr.Length; i++)
428+
{
429+
Assert.Equal(i, holder.arr[i].o);
430+
Assert.Equal(i + 1, holder.arr[i].s);
431+
}
432+
}
387433
}

0 commit comments

Comments
 (0)