Skip to content

Commit

Permalink
[mono] Implement GC descriptor for structs with InlineArray attribute (
Browse files Browse the repository at this point in the history
…#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
  • Loading branch information
kotlarmilos authored Mar 30, 2023
1 parent 21c4a26 commit 822fc81
Show file tree
Hide file tree
Showing 2 changed files with 109 additions and 58 deletions.
121 changes: 63 additions & 58 deletions src/mono/mono/metadata/object.c
Original file line number Diff line number Diff line change
Expand Up @@ -874,72 +874,77 @@ compute_class_bitmap (MonoClass *klass, gsize *bitmap, int size, int offset, int
/* special/collectible static */
continue;

pos = field_offset / TARGET_SIZEOF_VOID_P;
pos += offset;

type = mono_type_get_underlying_type (field->type);

switch (type->type) {
case MONO_TYPE_U:
case MONO_TYPE_I:
case MONO_TYPE_PTR:
case MONO_TYPE_FNPTR:
break;
case MONO_TYPE_STRING:
case MONO_TYPE_SZARRAY:
case MONO_TYPE_CLASS:
case MONO_TYPE_OBJECT:
case MONO_TYPE_ARRAY:
g_assert ((m_field_get_offset (field) % wordsize) == 0);

g_assert (pos < GINT_TO_UINT32(size) || pos <= GINT_TO_UINT32(max_size));
bitmap [pos / BITMAP_EL_SIZE] |= ((gsize)1) << (pos % BITMAP_EL_SIZE);
*max_set = MAX (GINT_TO_UINT32(*max_set), pos);
break;
case MONO_TYPE_GENERICINST:
if (!mono_type_generic_inst_is_valuetype (type)) {
guint32 field_iter = 1;
guint32 field_instance_offset = field_offset;
// If struct has InlineArray attribute, iterate `length` times to set a bitmap
if (m_class_is_inlinearray (p))
field_iter = m_class_inlinearray_value (p);

if (field_iter > 500)
g_warning ("Large number of iterations detected when creating a GC bitmap, might affect performance.");

while (field_iter) {
pos = field_instance_offset / TARGET_SIZEOF_VOID_P;
pos += offset;

type = mono_type_get_underlying_type (field->type);

switch (type->type) {
case MONO_TYPE_U:
case MONO_TYPE_I:
case MONO_TYPE_PTR:
case MONO_TYPE_FNPTR:
break;
case MONO_TYPE_STRING:
case MONO_TYPE_SZARRAY:
case MONO_TYPE_CLASS:
case MONO_TYPE_OBJECT:
case MONO_TYPE_ARRAY:
g_assert ((m_field_get_offset (field) % wordsize) == 0);

g_assert (pos < GINT_TO_UINT32(size) || pos <= GINT_TO_UINT32(max_size));
bitmap [pos / BITMAP_EL_SIZE] |= ((gsize)1) << (pos % BITMAP_EL_SIZE);
*max_set = MAX (GINT_TO_UINT32(*max_set), pos);
break;
} else {
/* fall through */
case MONO_TYPE_GENERICINST:
if (!mono_type_generic_inst_is_valuetype (type)) {
g_assert ((m_field_get_offset (field) % wordsize) == 0);

bitmap [pos / BITMAP_EL_SIZE] |= ((gsize)1) << (pos % BITMAP_EL_SIZE);
*max_set = MAX (GINT_TO_UINT32(*max_set), pos);
break;
} else {
/* fall through */
}
case MONO_TYPE_TYPEDBYREF:
case MONO_TYPE_VALUETYPE: {
MonoClass *fclass = mono_class_from_mono_type_internal (field->type);
if (m_class_has_references (fclass)) {
/* remove the object header */
compute_class_bitmap (fclass, bitmap, size, pos - MONO_OBJECT_HEADER_BITS, max_set, FALSE);
}
break;
}
case MONO_TYPE_TYPEDBYREF:
case MONO_TYPE_VALUETYPE: {
MonoClass *fclass = mono_class_from_mono_type_internal (field->type);
if (m_class_has_references (fclass)) {
/* remove the object header */
compute_class_bitmap (fclass, bitmap, size, pos - MONO_OBJECT_HEADER_BITS, max_set, FALSE);
case MONO_TYPE_I1:
case MONO_TYPE_U1:
case MONO_TYPE_I2:
case MONO_TYPE_U2:
case MONO_TYPE_I4:
case MONO_TYPE_U4:
case MONO_TYPE_I8:
case MONO_TYPE_U8:
case MONO_TYPE_R4:
case MONO_TYPE_R8:
case MONO_TYPE_BOOLEAN:
case MONO_TYPE_CHAR:
break;
default:
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);
break;
}
break;
}
case MONO_TYPE_I1:
case MONO_TYPE_U1:
case MONO_TYPE_I2:
case MONO_TYPE_U2:
case MONO_TYPE_I4:
case MONO_TYPE_U4:
case MONO_TYPE_I8:
case MONO_TYPE_U8:
case MONO_TYPE_R4:
case MONO_TYPE_R8:
case MONO_TYPE_BOOLEAN:
case MONO_TYPE_CHAR:
break;
default:
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);
break;
}

// A struct with the inline array attribute is handled in the same way as an array
if (m_class_is_inlinearray (klass)) {
g_assert ((m_field_get_offset (field) % wordsize) == 0);

g_assert (pos < GINT_TO_UINT32(size) || pos <= GINT_TO_UINT32(max_size));
bitmap [pos / BITMAP_EL_SIZE] |= ((gsize)1) << (pos % BITMAP_EL_SIZE);
*max_set = MAX (GINT_TO_UINT32(*max_set), pos);
field_instance_offset += field_offset;
field_iter--;
}
}
if (static_fields)
Expand Down
46 changes: 46 additions & 0 deletions src/tests/Loader/classloader/InlineArray/InlineArrayValid.cs
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,21 @@ struct ObjShortArr

[UnscopedRef]
public ref (object o, short s) this[int i] => ref Unsafe.Add(ref element, i);

[MethodImpl(MethodImplOptions.NoInlining)]
public static ObjShortArr CreateArray(int recCount) {
if (recCount > 0) {
return CreateArray(recCount-1);
} else {
var arr = new ObjShortArr();
for (short i = 0; i < ObjShortArr.Length; i++)
{
arr[i].o = i;
arr[i].s = (short)(i + 1);
}
return arr;
}
}
}

[Fact]
Expand Down Expand Up @@ -384,4 +399,35 @@ public static void GCDescOpt()
Assert.Equal(1, *gcSeriesPtr);
}
}

// ====================== MonoGCDesc ==========================================================

class Holder {
public ObjShortArr arr;
}

static Holder CreateArray() {
var arr = ObjShortArr.CreateArray(100);
var holder = new Holder();
holder.arr = arr;
return holder;
}

[Fact]
public static void MonoGCDescOpt()
{
Console.WriteLine($"{nameof(MonoGCDescOpt)}...");

var holder = CreateArray();

GC.Collect(2, GCCollectionMode.Forced, true, true);

MakeGarbage();

for (short i = 0; i < ObjShortArr.Length; i++)
{
Assert.Equal(i, holder.arr[i].o);
Assert.Equal(i + 1, holder.arr[i].s);
}
}
}

0 comments on commit 822fc81

Please sign in to comment.