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;
    }
}
