Enable support for int128 across all platforms.

This commit is contained in:
Christoffer Lerno
2022-10-20 18:03:02 +02:00
parent 03fe2b575d
commit d13b7ac96a
9 changed files with 278 additions and 61 deletions

View File

@@ -55,7 +55,7 @@ const OsType OS_TYPE = (OsType)($$OS_TYPE);
const bool COMPILER_LIBC_AVAILABLE = $$COMPILER_LIBC_AVAILABLE;
const CompilerOptLevel COMPILER_OPT_LEVEL = (CompilerOptLevel)($$COMPILER_OPT_LEVEL);
const bool BIG_ENDIAN = $$PLATFORM_BIG_ENDIAN;
const bool I128_SUPPORT = $$PLATFORM_I128_SUPPORTED;
const bool I128_NATIVE_SUPPORT = $$PLATFORM_I128_SUPPORTED;
const bool F16_SUPPORT = $$PLATFORM_F16_SUPPORTED;
const bool F128_SUPPORT = $$PLATFORM_F128_SUPPORTED;
const bool COMPILER_SAFE_MODE = $$COMPILER_SAFE_MODE;

View File

@@ -44,14 +44,10 @@ macro variant_to_int(variant v, $Type)
if (l > max || l < min) return ConversionResult.VALUE_OUT_OF_RANGE!;
return ($Type)l;
case int128:
$if (env::I128_SUPPORT):
int128 i = *(int128*)v.ptr;
if (is_mixed_signed && i < 0) return ConversionResult.VALUE_OUT_OF_UNSIGNED_RANGE!;
if (i > max || i < min) return ConversionResult.VALUE_OUT_OF_RANGE!;
return ($Type)i;
$else:
unreachable();
$endif;
int128 i = *(int128*)v.ptr;
if (is_mixed_signed && i < 0) return ConversionResult.VALUE_OUT_OF_UNSIGNED_RANGE!;
if (i > max || i < min) return ConversionResult.VALUE_OUT_OF_RANGE!;
return ($Type)i;
case char:
char c = *(char*)v.ptr;
if (c > max) return ConversionResult.VALUE_OUT_OF_RANGE!;
@@ -69,13 +65,9 @@ macro variant_to_int(variant v, $Type)
if (l > max || l < min) return ConversionResult.VALUE_OUT_OF_RANGE!;
return ($Type)l;
case uint128:
$if (env::I128_SUPPORT):
uint128 i = *(uint128*)v.ptr;
if (i > max || i < min) return ConversionResult.VALUE_OUT_OF_RANGE!;
return ($Type)i;
$else:
unreachable();
$endif;
uint128 i = *(uint128*)v.ptr;
if (i > max || i < min) return ConversionResult.VALUE_OUT_OF_RANGE!;
return ($Type)i;
default:
unreachable();
}

View File

@@ -13,40 +13,32 @@ private fn void! Formatter.right_adjust(Formatter* this, usz len)
}
private fn NtoaType int_from_variant(variant arg, bool *is_neg)
private fn uint128 int_from_variant(variant arg, bool *is_neg)
{
*is_neg = false;
$if (NtoaType.typeid == uint128.typeid):
switch (arg)
{
case int128:
int128 val = *arg;
return (*is_neg = val < 0) ? (~(NtoaType)val) + 1 : val;
case uint128:
return *arg;
}
$endif;
if (arg.type.kindof == TypeKind.POINTER)
{
return (NtoaType)(uptr)*(void**)arg.ptr;
return (uint128)(uptr)*(void**)arg.ptr;
}
switch (arg)
{
case bool:
return (NtoaType)*arg;
return (uint128)*arg;
case ichar:
int val = *arg;
return (*is_neg = val < 0) ? (~(NtoaType)val) + 1 : (NtoaType)val;
return (*is_neg = val < 0) ? (~(uint128)val) + 1 : (uint128)val;
case short:
int val = *arg;
return (*is_neg = val < 0) ? (~(NtoaType)val) + 1 : (NtoaType)val;
return (*is_neg = val < 0) ? (~(uint128)val) + 1 : (uint128)val;
case int:
int val = *arg;
return (*is_neg = val < 0) ? (~(NtoaType)val) + 1 : (NtoaType)val;
return (*is_neg = val < 0) ? (~(uint128)val) + 1 : (uint128)val;
case long:
long val = *arg;
return (*is_neg = val < 0) ? (~(NtoaType)val) + 1 : (NtoaType)val;
return (*is_neg = val < 0) ? (~(uint128)val) + 1 : (uint128)val;
case int128:
int128 val = *arg;
return (*is_neg = val < 0) ? (~(uint128)val) + 1 : (uint128)val;
case char:
return *arg;
case ushort:
@@ -55,12 +47,14 @@ private fn NtoaType int_from_variant(variant arg, bool *is_neg)
return *arg;
case ulong:
return *arg;
case uint128:
return *arg;
case float:
float f = *arg;
return (NtoaType)((*is_neg = f < 0) ? -f : f);
return (uint128)((*is_neg = f < 0) ? -f : f);
case double:
double d = *arg;
return (NtoaType)((*is_neg = d < 0) ? -d : d);
return (uint128)((*is_neg = d < 0) ? -d : d);
default:
return 0;
}
@@ -68,15 +62,6 @@ private fn NtoaType int_from_variant(variant arg, bool *is_neg)
private fn FloatType float_from_variant(variant arg)
{
$if (env::I128_SUPPORT):
switch (arg)
{
case int128:
return *arg;
case uint128:
return *arg;
}
$endif;
$if (env::F128_SUPPORT):
if (arg.type == float128.typeid) return *((float128*)arg.ptr);
$endif;
@@ -100,6 +85,8 @@ private fn FloatType float_from_variant(variant arg)
return *arg;
case long:
return *arg;
case int128:
return *arg;
case char:
return *arg;
case ushort:
@@ -108,6 +95,8 @@ private fn FloatType float_from_variant(variant arg)
return *arg;
case ulong:
return *arg;
case uint128:
return *arg;
case float:
return (FloatType)*arg;
case double:
@@ -172,7 +161,7 @@ union ConvUnion
private fn void! Formatter.etoa(Formatter* this, FloatType value)
{
// check for NaN and special values
if (value != value || value < FloatType.min || value > FloatType.max)
if (value != value || value < -FloatType.max || value > FloatType.max)
{
return this.ftoa(value);
}
@@ -258,7 +247,7 @@ private fn void! Formatter.etoa(Formatter* this, FloatType value)
this.flags = { .zeropad = true, .plus = true };
this.width = minwidth - 1;
this.prec = 0;
this.ntoa((NtoaType)(expval < 0 ? -expval : expval), expval < 0, 10)?;
this.ntoa((uint128)(expval < 0 ? -expval : expval), expval < 0, 10)?;
this.flags = old;
// might need to right-pad spaces
this.left_adjust(this.idx - start_idx)?;
@@ -402,7 +391,7 @@ private fn void! Formatter.ftoa(Formatter* this, FloatType value)
return this.out_reverse(buf[:len]);
}
private fn void! Formatter.ntoa(Formatter* this, NtoaType value, bool negative, uint base)
private fn void! Formatter.ntoa(Formatter* this, uint128 value, bool negative, uint base)
{
char[PRINTF_NTOA_BUFFER_SIZE] buf = void;
usz len = 0;
@@ -486,16 +475,11 @@ private fn void! Formatter.ntoa_format(Formatter* this, char[] buf, usz len, boo
return this.out_reverse(buf[:len]);
}
$if (env::I128_SUPPORT):
define NtoaType = uint128;
$else:
define NtoaType = ulong;
$endif;
private fn void! Formatter.ntoa_variant(Formatter* this, variant arg, uint base)
{
bool is_neg;
NtoaType val = int_from_variant(arg, &is_neg);
uint128 val = int_from_variant(arg, &is_neg);
return this.ntoa(val, is_neg, base) @inline;
}

View File

@@ -484,12 +484,12 @@ fn usz! Formatter.vprintf(Formatter* this, char[] format, variant[] variants)
if (this.flags.precision) this.flags.zeropad = false;
bool is_neg;
NtoaType v = int_from_variant(current, &is_neg);
uint128 v = int_from_variant(current, &is_neg);
this.ntoa(v, is_neg, base)?;
}
// termination
// out((char)0, buffer, idx < maxlen ? idx : maxlen - 1U, maxlen);
// out((char)0, buffer, idx < maxlen ? idx : maxlen - 1U, maxlen);
// return written chars without terminating \0
return this.idx;

241
lib/std/math_i128.c3 Normal file
View File

@@ -0,0 +1,241 @@
module std::math;
fn int128 __divti3(int128 a, int128 b) @extname("__divti3") @weak
{
int128 sign_a = a >> 127; // -1 : 0
int128 sign_b = b >> 127; // -1 : 0
uint128 unsigned_a = (uint128)(a ^ sign_a) + (-sign_a);
uint128 unsigned_b = (uint128)(b ^ sign_b) + (-sign_b);
sign_a ^= sign_b; // quotient sign
return __udivti3(unsigned_a, unsigned_b) @inline ^ sign_a + (-sign_a);
}
fn uint128 __umodti3(uint128 n, uint128 d) @extname("__umodti3") @weak
{
// Ignore d = 0
uint128 sr = (d ? $$clz(d) : 128) - (n ? $$clz(n) : 128);
// If n < d then sr is wrapping.
// which means we can just return n.
if (sr > 127) return n;
// If d == 1 and n = MAX
if (sr == 127) return 0;
sr++;
uint128 r = n >> sr;
// Follow known algorithm:
n <<= 128 - sr;
for (uint128 carry = 0; sr > 0; sr--)
{
r = (r << 1) | (n >> 127);
n = (n << 1) | carry;
int128 sign = (int128)(d - r - 1) >> 127;
carry = sign & 1;
r -= d & sign;
}
return r;
}
fn uint128 __udivti3(uint128 n, uint128 d) @extname("__udivti3") @weak
{
// Ignore d = 0
uint128 sr = (d ? $$clz(d) : 128) - (n ? $$clz(n) : 128);
// If n < d then sr is wrapping.
// which means we can just return 0.
if (sr > 127) return 0;
// If d == 1 and n = MAX
if (sr == 127) return n;
sr++;
uint128 r = n >> sr;
// Follow known algorithm:
n <<= 128 - sr;
uint128 carry = 0;
for (; sr > 0; sr--)
{
r = (r << 1) | (n >> 127);
n = (n << 1) | carry;
int128 sign = (int128)(d - r - 1) >> 127;
carry = sign & 1;
r -= d & sign;
}
n = (n << 1) | carry;
return n;
}
fn int128 __modti3(int128 a, int128 b) @extname("__modti3") @weak
{
int128 sign = b >> 127;
uint128 unsigned_b = (uint128)(b ^ sign) + (-sign);
sign = a >> 127;
uint128 unsigned_a = (uint128)(a ^ sign) + (-sign);
return __umodti3(unsigned_a, unsigned_b) ^ sign + (-sign);
}
private union DoubleBits
{
ulong l;
double d;
}
private union FloatBits
{
uint i;
float f;
}
fn double __floattidf(int128 a) @extname("__floattidf") @weak
{
if (a == 0) return 0.0;
// Grab and remove sign.
int128 sign = a >> 127;
a = (a ^ sign) - sign;
int sd = 128 - (int)$$clz(a);
int e = sd - 1;
if (sd > DOUBLE_MANT_DIG)
{
switch (sd)
{
case DOUBLE_MANT_DIG + 1:
a <<= 1;
case DOUBLE_MANT_DIG + 2:
break;
default:
a = ((uint128)a >> (sd - (DOUBLE_MANT_DIG + 2))) |
(int128)((a & ((uint128)(-1) >> ((128 + DOUBLE_MANT_DIG + 2) - sd))) != 0);
}
a |= (int128)((a & 4) != 0);
a++;
a >>= 2;
if (a & (1i128 << DOUBLE_MANT_DIG))
{
a >>= 1;
e++;
}
}
else
{
a <<= DOUBLE_MANT_DIG - sd;
}
DoubleBits bits = {
.l = ((ulong)(
(((uint)sign & 0x80000000)
| ((e + 1023) << 20))
| ((uint)(a >> 32) & 0x000FFFFF)) << 32)
| (uint)a
};
return bits.d;
}
fn double __floattisf(int128 a) @extname("__floattisf") @weak
{
if (a == 0) return 0.0;
// Grab and remove sign.
int128 sign = a >> 127;
a = (a ^ sign) - sign;
int sd = 128 - (int)$$clz(a);
int e = sd - 1;
if (sd > FLOAT_MANT_DIG)
{
switch (sd)
{
case FLOAT_MANT_DIG + 1:
a <<= 1;
case FLOAT_MANT_DIG + 2:
break;
default:
a = ((uint128)a >> (sd - (FLOAT_MANT_DIG + 2))) |
(int128)((a & ((uint128)(-1) >> ((128 + FLOAT_MANT_DIG + 2) - sd))) != 0);
}
a |= (int128)((a & 4) != 0);
a++;
a >>= 2;
if (a & (1i128 << FLOAT_MANT_DIG))
{
a >>= 1;
e++;
}
}
else
{
a <<= FLOAT_MANT_DIG - sd;
}
FloatBits bits = {
.i = (((uint)sign & 0x80000000)
| ((e + 127) << 23))
| ((uint)a & 0x007FFFFF)
};
return bits.f;
}
fn double __floatuntisf(uint128 a) @extname("__floatuntisf") @weak
{
if (a == 0) return 0.0;
int sd = 128 - (int)$$clz(a); // digits
int e = sd - 1; // exponent
if (sd > FLOAT_MANT_DIG)
{
switch (sd)
{
case FLOAT_MANT_DIG + 1:
a <<= 1;
case FLOAT_MANT_DIG + 2:
break;
default:
a = (a >> (sd - (FLOAT_MANT_DIG + 2)))
| (uint128)((a & ((uint128)(-1) >> ((128 + FLOAT_MANT_DIG + 2) - sd))) != 0);
}
a |= (uint128)((a & 4) != 0);
a++;
a >>= 2;
if (a & (1i128 << FLOAT_MANT_DIG))
{
a >>= 1;
e++;
}
}
else
{
a <<= (FLOAT_MANT_DIG - sd);
}
FloatBits bits = {
.i = ((e + 127) << 23) | ((uint)a & 0x007FFFFF)
};
return bits.f;
}
fn double __floatuntidf(uint128 a) @extname("__floatuntidf") @weak
{
if (a == 0) return 0.0;
int sd = 128 - (int)$$clz(a); // digits
int e = sd - 1; // exponent
if (sd > DOUBLE_MANT_DIG)
{
switch (sd)
{
case DOUBLE_MANT_DIG + 1:
a <<= 1;
case DOUBLE_MANT_DIG + 2:
break;
default:
a = (a >> (sd - (DOUBLE_MANT_DIG + 2)))
| (uint128)((a & ((uint128)(-1) >> ((128 + DOUBLE_MANT_DIG + 2) - sd))) != 0);
}
a |= (uint128)((a & 4) != 0);
a++;
a >>= 2;
if (a & (1i128 << DOUBLE_MANT_DIG))
{
a >>= 1;
e++;
}
}
else
{
a <<= (DOUBLE_MANT_DIG - sd);
}
DoubleBits bits = {
.l = (ulong)(((e + 1023) << 20) | ((uint)(a >> 32) & 0x000FFFFF)) << 32 + (uint)a
};
return bits.d;
}