mirror of
https://github.com/c3lang/c3c.git
synced 2026-02-27 12:01:16 +00:00
Enable support for int128 across all platforms.
This commit is contained in:
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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
241
lib/std/math_i128.c3
Normal 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;
|
||||
}
|
||||
@@ -147,7 +147,7 @@ static bool x86_should_return_type_in_reg(Type *type)
|
||||
Decl** members = type->decl->strukt.members;
|
||||
VECEACH (members, i)
|
||||
{
|
||||
Type *member_type = members[i]->type;
|
||||
Type *member_type = members[i]->type->canonical;
|
||||
if (!x86_should_return_type_in_reg(member_type)) return false;
|
||||
}
|
||||
return true;
|
||||
@@ -687,7 +687,7 @@ void c_abi_func_create_x86(FunctionPrototype *prototype)
|
||||
FATAL_ERROR("X86 vector call not supported");
|
||||
}
|
||||
prototype->abi_args = x86_create_params(prototype->call_abi, prototype->param_types, ®s);
|
||||
prototype->abi_varargs = x86_create_params(prototype->call_abi, prototype->param_types, ®s);
|
||||
prototype->abi_varargs = x86_create_params(prototype->call_abi, prototype->varargs, ®s);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1 +1 @@
|
||||
#define COMPILER_VERSION "0.3.90"
|
||||
#define COMPILER_VERSION "0.3.91"
|
||||
@@ -7,7 +7,7 @@ fn void main()
|
||||
io::printfln("Safe mode: %s", env::COMPILER_SAFE_MODE);
|
||||
io::printfln("OS type: %s", env::OS_TYPE);
|
||||
io::printfln("Big endian: %s", env::BIG_ENDIAN);
|
||||
io::printfln("i128 support: %s", env::I128_SUPPORT);
|
||||
io::printfln("i128 support: %s", env::I128_NATIVE_SUPPORT);
|
||||
io::printfln("f16 support: %s", env::F16_SUPPORT);
|
||||
io::printfln("f128 support: %s", env::F128_SUPPORT);
|
||||
io::printfln("Benchmarking: %s", env::BENCHMARKING);
|
||||
|
||||
@@ -7,7 +7,7 @@ fn void main()
|
||||
io::printfln("Safe mode: %s", env::COMPILER_SAFE_MODE);
|
||||
io::printfln("OS type: %s", env::OS_TYPE);
|
||||
io::printfln("Big endian: %s", env::BIG_ENDIAN);
|
||||
io::printfln("i128 support: %s", env::I128_SUPPORT);
|
||||
io::printfln("i128 support: %s", env::I128_NATIVE_SUPPORT);
|
||||
io::printfln("f16 support: %s", env::F16_SUPPORT);
|
||||
io::printfln("f128 support: %s", env::F128_SUPPORT);
|
||||
io::printfln("Benchmarking: %s", env::BENCHMARKING);
|
||||
|
||||
Reference in New Issue
Block a user