using System; using System.Reflection; using System.Runtime.CompilerServices; using System.Collections; /* * Regression tests for the GC support in the JIT */ public partial class Tests { public static int test_36_simple() { // Overflow the registers object o1 = (1); object o2 = (2); object o3 = (3); object o4 = (4); object o5 = (5); object o6 = (6); object o7 = (7); object o8 = (8); /* Prevent the variables from being local to a bb */ bool b = o1 != null; GC.Collect(0); if (b) return (int)o1 + (int)o2 + (int)o3 + (int)o4 + (int)o5 + (int)o6 + (int)o7 + (int)o8; else return 0; } public static int test_36_liveness() { object o = 5; object o1, o2, o3, o4, o5, o6, o7, o8; bool b = o != null; GC.Collect(1); o1 = (1); o2 = (2); o3 = (3); o4 = (4); o5 = (5); o6 = (6); o7 = (7); o8 = (8); if (b) return (int)o1 + (int)o2 + (int)o3 + (int)o4 + (int)o5 + (int)o6 + (int)o7 + (int)o8; else return 0; } struct GcFooStruct { public object o1; public int i; public object o2; public GcFooStruct(int i1, int i, int i2) { this.o1 = i1; this.i = i; this.o2 = i2; } } public static int test_4_vtype() { GcFooStruct s = new GcFooStruct(1, 2, 3); GC.Collect(1); return (int)s.o1 + (int)s.o2; } class BigClass { public object o1, o2, o3, o4, o5, o6, o7, o8, o9, o10; public object o11, o12, o13, o14, o15, o16, o17, o18, o19, o20; public object o21, o22, o23, o24, o25, o26, o27, o28, o29, o30; public object o31, o32; } static void set_fields(BigClass b) { b.o31 = 31; b.o32 = 32; b.o1 = 1; b.o2 = 2; b.o3 = 3; b.o4 = 4; b.o5 = 5; b.o6 = 6; b.o7 = 7; b.o8 = 8; b.o9 = 9; b.o10 = 10; b.o11 = 11; b.o12 = 12; b.o13 = 13; b.o14 = 14; b.o15 = 15; b.o16 = 16; b.o17 = 17; b.o18 = 18; b.o19 = 19; b.o20 = 20; b.o21 = 21; b.o22 = 22; b.o23 = 23; b.o24 = 24; b.o25 = 25; b.o26 = 26; b.o27 = 27; b.o28 = 28; b.o29 = 29; b.o30 = 30; } // Test marking of objects with > 32 fields public static int test_528_mark_runlength_large() { BigClass b = new BigClass(); /* * Do the initialization in a separate method so no object refs remain in * spill slots. */ set_fields(b); GC.Collect(1); return (int)b.o1 + (int)b.o2 + (int)b.o3 + (int)b.o4 + (int)b.o5 + (int)b.o6 + (int)b.o7 + (int)b.o8 + (int)b.o9 + (int)b.o10 + (int)b.o11 + (int)b.o12 + (int)b.o13 + (int)b.o14 + (int)b.o15 + (int)b.o16 + (int)b.o17 + (int)b.o18 + (int)b.o19 + (int)b.o20 + (int)b.o21 + (int)b.o22 + (int)b.o23 + (int)b.o24 + (int)b.o25 + (int)b.o26 + (int)b.o27 + (int)b.o28 + (int)b.o29 + (int)b.o30 + (int)b.o31 + (int)b.o32; } /* * Test liveness and loops. */ public static int test_0_liveness_2() { object o = new object(); for (int n = 0; n < 10; ++n) { /* Exhaust all registers so 'o' is stack allocated */ int sum = 0, i, j, k, l, m; for (i = 0; i < 100; ++i) sum++; for (j = 0; j < 100; ++j) sum++; for (k = 0; k < 100; ++k) sum++; for (l = 0; l < 100; ++l) sum++; for (m = 0; m < 100; ++m) sum++; if (o != null) o.ToString(); GC.Collect(1); if (o != null) o.ToString(); sum += i + j + k; GC.Collect(1); } return 0; } /* * Test liveness and stack slot sharing * This doesn't work yet, its hard to make the JIT share the stack slots of the * two 'o' variables. */ public static int test_0_liveness_3() { bool b = false; bool b2 = true; /* Exhaust all registers so 'o' is stack allocated */ int sum = 0, i, j, k, l, m, n, s; for (i = 0; i < 100; ++i) sum++; for (j = 0; j < 100; ++j) sum++; for (k = 0; k < 100; ++k) sum++; for (l = 0; l < 100; ++l) sum++; for (m = 0; m < 100; ++m) sum++; for (n = 0; n < 100; ++n) sum++; for (s = 0; s < 100; ++s) sum++; if (b) { object o = new object(); /* Make sure o is global */ if (b2) Console.WriteLine(); o.ToString(); } GC.Collect(1); if (b) { object o = new object(); /* Make sure o is global */ if (b2) Console.WriteLine(); o.ToString(); } sum += i + j + k + l + m + n + s; return 0; } /* * Test liveness of variables used to handle items on the IL stack. */ [MethodImplAttribute(MethodImplOptions.NoInlining)] static string call1() { return "A"; } [MethodImplAttribute(MethodImplOptions.NoInlining)] static string call2() { GC.Collect(1); return "A"; } public static int test_0_liveness_4() { bool b = false; bool b2 = true; /* Exhaust all registers so 'o' is stack allocated */ int sum = 0, i, j, k, l, m, n, s; for (i = 0; i < 100; ++i) sum++; for (j = 0; j < 100; ++j) sum++; for (k = 0; k < 100; ++k) sum++; for (l = 0; l < 100; ++l) sum++; for (m = 0; m < 100; ++m) sum++; for (n = 0; n < 100; ++n) sum++; for (s = 0; s < 100; ++s) sum++; string o = b ? call1() : call2(); GC.Collect(1); sum += i + j + k + l + m + n + s; return 0; } /* * Test liveness of volatile variables */ [MethodImplAttribute(MethodImplOptions.NoInlining)] static void liveness_5_1(out object o) { o = new object(); } public static int test_0_liveness_5() { bool b = false; bool b2 = true; /* Exhaust all registers so 'o' is stack allocated */ int sum = 0, i, j, k, l, m, n, s; for (i = 0; i < 100; ++i) sum++; for (j = 0; j < 100; ++j) sum++; for (k = 0; k < 100; ++k) sum++; for (l = 0; l < 100; ++l) sum++; for (m = 0; m < 100; ++m) sum++; for (n = 0; n < 100; ++n) sum++; for (s = 0; s < 100; ++s) sum++; object o; liveness_5_1(out o); for (int x = 0; x < 10; ++x) { o.ToString(); GC.Collect(1); } sum += i + j + k + l + m + n + s; return 0; } /* * Test the case when a stack slot becomes dead, then live again due to a backward * branch. */ [MethodImplAttribute(MethodImplOptions.NoInlining)] static object alloc_obj() { return new object(); } [MethodImplAttribute(MethodImplOptions.NoInlining)] static bool return_true() { return true; } [MethodImplAttribute(MethodImplOptions.NoInlining)] static bool return_false() { return false; } public static int test_0_liveness_6() { bool b = false; bool b2 = true; /* Exhaust all registers so 'o' is stack allocated */ int sum = 0, i, j, k, l, m, n, s; for (i = 0; i < 100; ++i) sum++; for (j = 0; j < 100; ++j) sum++; for (k = 0; k < 100; ++k) sum++; for (l = 0; l < 100; ++l) sum++; for (m = 0; m < 100; ++m) sum++; for (n = 0; n < 100; ++n) sum++; for (s = 0; s < 100; ++s) sum++; for (int x = 0; x < 10; ++x) { GC.Collect(1); object o = alloc_obj(); o.ToString(); GC.Collect(1); } sum += i + j + k + l + m + n + s; return 0; } public static int test_0_multi_dim_ref_array_wbarrier() { string[,] arr = new string[256, 256]; for (int i = 0; i < 256; ++i) { for (int j = 0; j < 100; ++j) arr[i, j] = "" + i + " " + j; } GC.Collect(); return 0; } /* * Liveness + out of line bblocks */ public static int test_0_liveness_7() { /* Exhaust all registers so 'o' is stack allocated */ int sum = 0, i, j, k, l, m, n, s; for (i = 0; i < 100; ++i) sum++; for (j = 0; j < 100; ++j) sum++; for (k = 0; k < 100; ++k) sum++; for (l = 0; l < 100; ++l) sum++; for (m = 0; m < 100; ++m) sum++; for (n = 0; n < 100; ++n) sum++; for (s = 0; s < 100; ++s) sum++; // o is dead here GC.Collect(1); if (return_false()) { // This bblock is in-line object o = alloc_obj(); // o is live here if (return_false()) { // This bblock is out-of-line, and o is live here throw new Exception(o.ToString()); } } // o is dead here too GC.Collect(1); return 0; } // Liveness + finally clauses public static int test_0_liveness_8() { /* Exhaust all registers so 'o' is stack allocated */ int sum = 0, i, j, k, l, m, n, s; for (i = 0; i < 100; ++i) sum++; for (j = 0; j < 100; ++j) sum++; for (k = 0; k < 100; ++k) sum++; for (l = 0; l < 100; ++l) sum++; for (m = 0; m < 100; ++m) sum++; for (n = 0; n < 100; ++n) sum++; for (s = 0; s < 100; ++s) sum++; object o = null; try { o = alloc_obj(); } finally { GC.Collect(1); } o.GetHashCode(); return 0; } [MethodImplAttribute(MethodImplOptions.NoInlining)] static object alloc_string() { return "A"; } [MethodImplAttribute(MethodImplOptions.NoInlining)] static object alloc_obj_and_gc() { GC.Collect(1); return new object(); } [MethodImplAttribute(MethodImplOptions.NoInlining)] static void clobber_regs_and_gc() { int sum = 0, i, j, k, l, m, n, s; for (i = 0; i < 100; ++i) sum++; for (j = 0; j < 100; ++j) sum++; for (k = 0; k < 100; ++k) sum++; for (l = 0; l < 100; ++l) sum++; for (m = 0; m < 100; ++m) sum++; for (n = 0; n < 100; ++n) sum++; for (s = 0; s < 100; ++s) sum++; GC.Collect(1); } [MethodImplAttribute(MethodImplOptions.NoInlining)] static void liveness_9_call1(object o1, object o2, object o3) { o1.GetHashCode(); o2.GetHashCode(); o3.GetHashCode(); } // Liveness + JIT temporaries public static int test_0_liveness_9() { // the result of alloc_obj () goes into a vreg, which gets converted to a // JIT temporary because of the branching introduced by the cast // FIXME: This doesn't crash if MONO_TYPE_I is not treated as a GC ref liveness_9_call1(alloc_obj(), (string)alloc_string(), alloc_obj_and_gc()); return 0; } // Liveness for registers public static int test_0_liveness_10() { // Make sure this goes into a register object o = alloc_obj(); o.GetHashCode(); o.GetHashCode(); o.GetHashCode(); o.GetHashCode(); o.GetHashCode(); o.GetHashCode(); // Break the bblock so o doesn't become a local vreg if (return_true()) // Clobber it with a call and run a GC clobber_regs_and_gc(); // Access it again o.GetHashCode(); return 0; } // Liveness for spill slots holding managed pointers public static int test_0_liveness_11() { Tests[] arr = new Tests[10]; // This uses an ldelema internally // FIXME: This doesn't crash if mp-s are not correctly tracked, just writes to // an old object. arr[0] >>= 1; return 0; } public static Tests operator >>(Tests bi1, int shiftVal) { clobber_regs_and_gc(); return bi1; } [MethodImplAttribute(MethodImplOptions.NoInlining)] public static void liveness_12_inner(int a, int b, int c, int d, int e, int f, object o) { GC.Collect(1); o.GetHashCode(); } // Liveness for param area public static int test_0_liveness_12() { // The ref argument should be passed on the stack liveness_12_inner(1, 2, 3, 4, 5, 6, new object()); return 0; } public static void liveness_13_inner(ref ArrayList arr) { // The value of arr will be stored in a spill slot arr.Add(alloc_obj_and_gc()); } // Liveness for byref arguments in spill slots public static int test_0_liveness_13() { var arr = new ArrayList(); liveness_13_inner(ref arr); return 0; } }