module core.checkedint;

version(LDC)
{
	import ldc.intrinsics;
}

pragma(inline, true)
uint addu()(uint x, uint y, ref bool overflow)
{
    version (LDC)
    {
        if (!__ctfe)
        {
            auto res = llvm_uadd_with_overflow(x, y);
            overflow |= res.overflow;
            return res.result;
        }
    }
    immutable uint r = x + y;
    immutable bool o = r < x;
    assert(o == (r < y));
    if (o)
        overflow = true;
    return r;
}

uint mulu()(uint x, uint y, ref bool overflow)
{
    version (D_InlineAsm_X86)         enum useAsm = true;
    else version (D_InlineAsm_X86_64) enum useAsm = true;
    else                              enum useAsm = false;

    version (LDC)
    {
        if (!__ctfe)
        {
            auto res = llvm_umul_with_overflow(x, y);
            overflow |= res.overflow;
            return res.result;
        }
    }
    else static if (useAsm)
    {
        if (!__ctfe)
        {
            uint r;
            bool o;
            asm pure nothrow @nogc @trusted
            {
                mov EAX, x;
                mul y;        // EDX:EAX = EAX * y
                mov r, EAX;
                setc o;
            }
            overflow |= o;
            return r;
        }
    }

    immutable ulong r = ulong(x) * ulong(y);
    if (r >> 32)
        overflow = true;
    return cast(uint) r;
}

pragma(inline, true)
int adds()(int x, int y, ref bool overflow)
{
    version (LDC)
    {
        if (!__ctfe)
        {
            auto res = llvm_sadd_with_overflow(x, y);
            overflow |= res.overflow;
            return res.result;
        }
    }
    long r = cast(long)x + cast(long)y;
    if (r < int.min || r > int.max)
        overflow = true;
    return cast(int)r;
}
