mirror of
https://github.com/danbulant/Cosmos
synced 2026-05-26 05:22:19 +00:00
434 lines
10 KiB
C#
434 lines
10 KiB
C#
using System;
|
|
|
|
/*
|
|
* Register allocator tests.
|
|
*/
|
|
|
|
public partial class Tests
|
|
{
|
|
|
|
|
|
static void call_clobber_inner()
|
|
{
|
|
}
|
|
|
|
public static int test_15_clobber_1()
|
|
{
|
|
int a = 0;
|
|
int b = 0;
|
|
for (int i = 0; i < 10; ++i)
|
|
a++;
|
|
for (int i = 0; i < 5; ++i)
|
|
b++;
|
|
|
|
// clob == '1' and dreg == sreg2
|
|
a = b + a;
|
|
return a;
|
|
}
|
|
|
|
public static int test_15_clobber_1_fp()
|
|
{
|
|
float a = 0;
|
|
float b = 0;
|
|
for (int i = 0; i < 10; ++i)
|
|
a++;
|
|
for (int i = 0; i < 5; ++i)
|
|
b++;
|
|
|
|
// clob == '1' and dreg == sreg2
|
|
a = b + a;
|
|
return (int)a;
|
|
}
|
|
|
|
public static int test_5_call_clobber()
|
|
{
|
|
// A call clobbers some registers so variables in those registers need to be spilled
|
|
// and later reloaded to a register
|
|
int a = 2;
|
|
int b = 3;
|
|
|
|
call_clobber_inner();
|
|
|
|
return a + b;
|
|
}
|
|
|
|
static int call_clobber_inner2()
|
|
{
|
|
return 3;
|
|
}
|
|
|
|
public static int test_7_call_clobber_dreg()
|
|
{
|
|
// A call doesn't clobber its dreg
|
|
int a = 3;
|
|
int b = 4;
|
|
|
|
a = call_clobber_inner2();
|
|
|
|
return a + b;
|
|
}
|
|
|
|
public static int test_9_spill_if_then_else()
|
|
{
|
|
// Spilling variables in one branch of an if-then-else
|
|
int a = 4;
|
|
int b = 5;
|
|
|
|
if (a != b)
|
|
{
|
|
}
|
|
else
|
|
{
|
|
call_clobber_inner();
|
|
}
|
|
|
|
return a + b;
|
|
}
|
|
|
|
public static int test_3_spill_reload_if_then_else()
|
|
{
|
|
// Spilling and reloading variables in one branch of an if-then-else
|
|
int a = 4;
|
|
int b = 5;
|
|
int c = 3;
|
|
|
|
if (a != b)
|
|
{
|
|
}
|
|
else
|
|
{
|
|
call_clobber_inner();
|
|
c = a + b;
|
|
}
|
|
|
|
return c;
|
|
}
|
|
|
|
public static int test_5_spill_loop()
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < 5; ++i)
|
|
call_clobber_inner();
|
|
|
|
return i;
|
|
}
|
|
|
|
public unsafe static int test_0_volatile()
|
|
{
|
|
int i = 1;
|
|
int* p = &i;
|
|
|
|
if (*p != 1)
|
|
return 1;
|
|
if (i != 1)
|
|
return 2;
|
|
*p = 5;
|
|
if (i != 5)
|
|
return 3;
|
|
i = 2;
|
|
if (i != 2)
|
|
return 4;
|
|
if (*p != 2)
|
|
return 5;
|
|
|
|
return 0;
|
|
}
|
|
|
|
public unsafe static int test_0_volatile_unused()
|
|
{
|
|
int i = 1;
|
|
int* p = &i;
|
|
|
|
if (*p != 1)
|
|
return 1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
public unsafe static int test_0_volatile_unused_2()
|
|
{
|
|
int i = 1;
|
|
int* p = &i;
|
|
|
|
i = 2;
|
|
if (i != 2)
|
|
return 1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int ref_int(int i, ref int b, int j)
|
|
{
|
|
int res = b;
|
|
b = 1;
|
|
return res;
|
|
}
|
|
|
|
public static int test_0_volatile_unused_3()
|
|
{
|
|
// b's def has no use so its interval is split at a position not covered by the interval
|
|
int b = 42;
|
|
if (ref_int(99, ref b, 100) != 42)
|
|
return 1;
|
|
b = 43;
|
|
if (ref_int(99, ref b, 100) != 43)
|
|
return 2;
|
|
if (b != 1)
|
|
return 13;
|
|
return 0;
|
|
}
|
|
|
|
static int ref_bool(int i, ref bool b1, ref bool b2, ref bool b3)
|
|
{
|
|
b1 = !b1;
|
|
b2 = !b2;
|
|
b3 = !b3;
|
|
|
|
return 0;
|
|
}
|
|
|
|
public static int test_0_volatile_regress_1()
|
|
{
|
|
// Spill stores should precede spill loads at a given position
|
|
for (int i = 0; i < 8; i++)
|
|
{
|
|
bool b1 = (i & 4) != 0;
|
|
bool b2 = (i & 2) != 0;
|
|
bool b3 = (i & 1) != 0;
|
|
bool orig_b1 = b1, orig_b2 = b2, orig_b3 = b3;
|
|
if (ref_bool(i, ref b1, ref b2, ref b3) != 0)
|
|
return 4 * i + 1;
|
|
if (b1 != !orig_b1)
|
|
return 4 * i + 2;
|
|
if (b2 != !orig_b2)
|
|
return 4 * i + 3;
|
|
if (b3 != !orig_b3)
|
|
return 4 * i + 4;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int decode_len(out int pos)
|
|
{
|
|
pos = 19;
|
|
return 10;
|
|
}
|
|
|
|
static void clobber_all(int pos)
|
|
{
|
|
for (int i = 0; i < 10; ++i)
|
|
for (int j = 0; j < 10; ++j)
|
|
for (int k = 0; k < 10; ++k)
|
|
for (int l = 0; l < 10; ++l)
|
|
;
|
|
}
|
|
|
|
public static int test_29_volatile_regress_2()
|
|
{
|
|
int pos = 0;
|
|
|
|
int len = decode_len(out pos);
|
|
call_clobber_inner();
|
|
pos += len;
|
|
|
|
clobber_all(pos);
|
|
return pos;
|
|
}
|
|
|
|
public static int test_0_clobber_regress_1()
|
|
{
|
|
object[] a11 = new object[10];
|
|
object o = new Object();
|
|
// A spill load is inserted before the backward branch, clobbering one of the
|
|
// registers used in the comparison
|
|
for (int i = 0; i < 10; ++i)
|
|
a11[i] = null;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int return_arg(int i)
|
|
{
|
|
return i;
|
|
}
|
|
|
|
public static int test_0_spill_regress_1()
|
|
{
|
|
int j = 5;
|
|
for (int i = 0; i < 3; i++)
|
|
{
|
|
// i is spilled by the call, then reloaded for the loop check
|
|
// make sure the move from its first reg to its second is inserted in the
|
|
// if body bblock, not the for body bblock
|
|
if (i == 0)
|
|
{
|
|
}
|
|
else
|
|
{
|
|
if (return_arg(j) != 5)
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
public static int test_0_spill_regress_2()
|
|
{
|
|
double[] temporaries = new double[3];
|
|
for (int i = 0; i < 3; i++)
|
|
{
|
|
// i and temporaries are spilled by the call, then reloaded after the call
|
|
// make sure the two moves inserted in the if bblock are in the proper order
|
|
if (i == 0)
|
|
{
|
|
}
|
|
else
|
|
{
|
|
temporaries[i] = return_arg(i);
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int many_args_unused(int i, int j, int k, int l, int m, int n, int p, int q)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
public static int test_0_unused_args()
|
|
{
|
|
return many_args_unused(0, 1, 2, 3, 4, 5, 6, 7);
|
|
}
|
|
|
|
public unsafe void ClearBuffer(byte* buffer, int i)
|
|
{
|
|
// Avoid inlining
|
|
byte* b = stackalloc byte[4];
|
|
}
|
|
|
|
public unsafe bool instance_method_1(string s, string target, int start, int length, int opt)
|
|
{
|
|
byte* alwaysMatchFlags = stackalloc byte[16];
|
|
byte* neverMatchFlags = stackalloc byte[16];
|
|
byte* targetSortKey = stackalloc byte[4];
|
|
byte* sk1 = stackalloc byte[4];
|
|
byte* sk2 = stackalloc byte[4];
|
|
ClearBuffer(alwaysMatchFlags, 16);
|
|
ClearBuffer(neverMatchFlags, 16);
|
|
ClearBuffer(targetSortKey, 4);
|
|
ClearBuffer(sk1, 4);
|
|
ClearBuffer(sk2, 4);
|
|
|
|
return this == null && s == target && start == length && length == opt && alwaysMatchFlags == neverMatchFlags && neverMatchFlags == targetSortKey && sk1 == sk2;
|
|
}
|
|
|
|
public static int test_0_spill_regress_3()
|
|
{
|
|
new Tests().instance_method_1(null, null, 0, 0, 0);
|
|
return 0;
|
|
}
|
|
|
|
unsafe bool MatchesBackward(string s, ref int idx, int end, int orgStart, int ti, byte* sortkey, bool noLv4, ref object ctx)
|
|
{
|
|
// Avoid inlining
|
|
byte* b = stackalloc byte[4];
|
|
|
|
if (ctx == null)
|
|
throw new Exception();
|
|
|
|
idx -= 1;
|
|
return false;
|
|
}
|
|
|
|
unsafe int LastIndexOfSortKey(string s, int start, int orgStart, int length, byte* sortkey, int ti, bool noLv4, ref object ctx)
|
|
{
|
|
// ctx is initially allocated to the stack, when it is reloaded before the call,
|
|
// %rax is spilled to free up the register, then ctx is allocated to %rax for its
|
|
// whole lifetime, but %rax is not available for this since it is clobbered by the
|
|
// call
|
|
int end = start - length;
|
|
int idx = start;
|
|
while (idx > end)
|
|
{
|
|
int cur = idx;
|
|
if (MatchesBackward(s, ref idx, end, orgStart,
|
|
ti, sortkey, noLv4, ref ctx))
|
|
return cur;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
public unsafe static int test_0_spill_regress_4()
|
|
{
|
|
object o = new Object();
|
|
new Tests().LastIndexOfSortKey("", 10, 0, 5, null, 0, false, ref o);
|
|
|
|
return 0;
|
|
}
|
|
|
|
public static bool IsEqual(Type type, Type base_type)
|
|
{
|
|
return (type.GetHashCode() == base_type.GetHashCode());
|
|
}
|
|
|
|
public static bool IsNestedFamilyAccessible(Type type, Type base_type)
|
|
{
|
|
do
|
|
{
|
|
if (IsEqual(type, base_type))
|
|
return true;
|
|
|
|
type = type.DeclaringType;
|
|
} while (type != null);
|
|
|
|
return false;
|
|
}
|
|
|
|
public static int test_0_do_while_critical_edges()
|
|
{
|
|
IsNestedFamilyAccessible(typeof(int), typeof(object));
|
|
|
|
return 0;
|
|
}
|
|
|
|
public static string return_string(string s)
|
|
{
|
|
for (int i = 0; i < 1000; ++i)
|
|
;
|
|
return s;
|
|
}
|
|
|
|
public static int test_0_switch_critical_edges()
|
|
{
|
|
// A spill load is inserted at the end of the bblock containing the OP_BR_REG,
|
|
// overwriting the source reg of the OP_BR_REG. The edge is not really
|
|
// a critical edge, since its source bblock only has 1 exit, but it must
|
|
// be treated as such.
|
|
for (int i = 0; i < UInt16.MaxValue; i++)
|
|
{
|
|
Char c = Convert.ToChar(i);
|
|
switch (i)
|
|
{
|
|
case 0x0009:
|
|
case 0x000A:
|
|
case 0x000B:
|
|
case 0x2028:
|
|
case 0x2029:
|
|
case 0x202F:
|
|
case 0x205F:
|
|
case 0x3000:
|
|
//Console.WriteLine ((i.ToString () + (int)c));
|
|
return_string((i.ToString() + (int)c));
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
}
|