640 lines
12 KiB
C#
640 lines
12 KiB
C#
using System;
|
|
using System.Reflection;
|
|
using System.Runtime.CompilerServices;
|
|
using System.Collections;
|
|
using System.Threading;
|
|
|
|
/*
|
|
* Regression tests for the GC support in the JIT
|
|
*/
|
|
#if __MOBILE__
|
|
class GcTests
|
|
#else
|
|
class Tests
|
|
#endif
|
|
{
|
|
#if !__MOBILE__
|
|
public static int Main (string[] args) {
|
|
return TestDriver.RunTests (typeof (Tests), args);
|
|
}
|
|
#endif
|
|
|
|
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 FooStruct {
|
|
public object o1;
|
|
public int i;
|
|
public object o2;
|
|
|
|
public FooStruct (int i1, int i, int i2) {
|
|
this.o1 = i1;
|
|
this.i = i;
|
|
this.o2 = i2;
|
|
}
|
|
}
|
|
|
|
public static int test_4_vtype () {
|
|
FooStruct s = new FooStruct (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;
|
|
}
|
|
|
|
class ObjWithShiftOp {
|
|
public static ObjWithShiftOp operator >> (ObjWithShiftOp bi1, int shiftVal) {
|
|
clobber_regs_and_gc ();
|
|
return bi1;
|
|
}
|
|
}
|
|
|
|
// Liveness for spill slots holding managed pointers
|
|
public static int test_0_liveness_11 () {
|
|
ObjWithShiftOp[] arr = new ObjWithShiftOp [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;
|
|
}
|
|
|
|
|
|
[MethodImplAttribute (MethodImplOptions.NoInlining)]
|
|
public static void liveness_12_inner (int a, int b, int c, int d, int e, int f, object o, ref string s) {
|
|
GC.Collect (1);
|
|
o.GetHashCode ();
|
|
s.GetHashCode ();
|
|
}
|
|
|
|
class FooClass {
|
|
public string s;
|
|
}
|
|
|
|
// Liveness for param area
|
|
public static int test_0_liveness_12 () {
|
|
var f = new FooClass () { s = "A" };
|
|
// The ref argument should be passed on the stack
|
|
liveness_12_inner (1, 2, 3, 4, 5, 6, new object (), ref f.s);
|
|
return 0;
|
|
}
|
|
|
|
interface IFace {
|
|
int foo ();
|
|
}
|
|
|
|
struct BarStruct : IFace {
|
|
int i;
|
|
|
|
public int foo () {
|
|
GC.Collect ();
|
|
return i;
|
|
}
|
|
}
|
|
|
|
public static int test_0_liveness_unbox_trampoline () {
|
|
var s = new BarStruct ();
|
|
|
|
IFace iface = s;
|
|
iface.foo ();
|
|
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;
|
|
}
|
|
|
|
static ThreadLocal<object> tls;
|
|
|
|
[MethodImplAttribute (MethodImplOptions.NoInlining)]
|
|
static void alloc_tls_obj () {
|
|
tls = new ThreadLocal<object> ();
|
|
tls.Value = new object ();
|
|
}
|
|
|
|
public static int test_0_thread_local () {
|
|
alloc_tls_obj ();
|
|
GC.Collect ();
|
|
Type t = tls.Value.GetType ();
|
|
if (t == typeof (object))
|
|
return 0;
|
|
else
|
|
return 1;
|
|
}
|
|
|
|
struct LargeBitmap {
|
|
public object o1, o2, o3;
|
|
public int i;
|
|
public object o4, o5, o6, o7, o9, o10, o11, o12, o13, o14, o15, o16, o17, o18, o19, o20, o21, o22, o23, o24, o25, o26, o27, o28, o29, o30, o31, o32;
|
|
}
|
|
|
|
public static int test_12_large_bitmap () {
|
|
LargeBitmap lb = new LargeBitmap ();
|
|
lb.o1 = 1;
|
|
lb.o2 = 2;
|
|
lb.o3 = 3;
|
|
lb.o32 = 6;
|
|
|
|
GC.Collect (0);
|
|
|
|
return (int)lb.o1 + (int)lb.o2 + (int)lb.o3 + (int)lb.o32;
|
|
}
|
|
}
|