From 7c64827d9294f2f796c90ad81928ceb709c12035 Mon Sep 17 00:00:00 2001 From: "Richard (Rikki) Andrew Cattermole" Date: Wed, 12 Aug 2026 11:49:35 +1200 Subject: [PATCH] Implement cent/ucent --- changelog/dmd.cent-ucent.dd | 27 + compiler/include/dmd/common/int128.h | 20 + compiler/include/dmd/expression.h | 12 + compiler/include/dmd/tokens.h | 3 + compiler/include/dmd/visitor.h | 3 + compiler/src/build.d | 2 +- compiler/src/dmd/astbase.d | 26 + compiler/src/dmd/backend/backconfig.d | 10 + compiler/src/dmd/backend/cgcs.d | 5 + compiler/src/dmd/backend/cgelem.d | 2 +- compiler/src/dmd/backend/el.d | 37 ++ compiler/src/dmd/backend/evalu8.d | 21 +- compiler/src/dmd/backend/gdag.d | 19 +- compiler/src/dmd/backend/rtlsym.d | 61 +++ compiler/src/dmd/backend/x86/cgreg.d | 1 + compiler/src/dmd/backend/x86/cod1.d | 17 +- compiler/src/dmd/backend/x86/cod2.d | 103 +++- compiler/src/dmd/backend/x86/cod3.d | 3 +- compiler/src/dmd/backend/x86/cod4.d | 73 ++- compiler/src/dmd/constfold.d | 238 +++++++- compiler/src/dmd/ctfeexpr.d | 33 +- compiler/src/dmd/dcast.d | 10 +- compiler/src/dmd/dfa/fast/expression.d | 32 ++ compiler/src/dmd/dinterpret.d | 13 +- compiler/src/dmd/dsymbolsem.d | 3 + compiler/src/dmd/expression.d | 36 ++ compiler/src/dmd/expressionsem.d | 46 ++ compiler/src/dmd/glue/e2ir.d | 508 +++++++++++++++++- compiler/src/dmd/glue/package.d | 2 + compiler/src/dmd/glue/todt.d | 7 + compiler/src/dmd/hdrgen.d | 12 + compiler/src/dmd/lexer.d | 26 +- compiler/src/dmd/optimize.d | 2 +- compiler/src/dmd/parse.d | 11 + compiler/src/dmd/statementsem.d | 4 +- compiler/src/dmd/target.d | 16 + compiler/src/dmd/templatesem.d | 3 + compiler/src/dmd/tokens.d | 3 + compiler/src/dmd/typesem.d | 25 +- compiler/src/dmd/visitor/parsetime.d | 1 + compiler/src/dmd/visitor/strict.d | 1 + compiler/test/fail_compilation/fail22827.d | 9 - compiler/test/fail_compilation/fail254.d | 10 +- compiler/test/fail_compilation/fail_cent.d | 13 + compiler/test/fail_compilation/lexer23465.d | 17 +- compiler/test/runnable/cent_ucent.d | 363 +++++++++++++ compiler/tools/gen_cpp_layout_test.py | 3 +- druntime/src/core/internal/string.d | 68 +++ ...1786475660431-cent-ucent-implementation.md | 173 ++++++ plans/int128/prompts.md | 257 +++++++++ spec/lex.dd | 4 +- spec/type.dd | 10 +- 52 files changed, 2297 insertions(+), 107 deletions(-) create mode 100644 changelog/dmd.cent-ucent.dd create mode 100644 compiler/include/dmd/common/int128.h delete mode 100644 compiler/test/fail_compilation/fail22827.d create mode 100644 compiler/test/fail_compilation/fail_cent.d create mode 100644 compiler/test/runnable/cent_ucent.d create mode 100644 plans/int128/1786475660431-cent-ucent-implementation.md create mode 100644 plans/int128/prompts.md diff --git a/changelog/dmd.cent-ucent.dd b/changelog/dmd.cent-ucent.dd new file mode 100644 index 000000000000..3abfa67b2f36 --- /dev/null +++ b/changelog/dmd.cent-ucent.dd @@ -0,0 +1,27 @@ +Implement the 128-bit integer types `cent` and `ucent` + +The previously reserved 128-bit integer types `cent` and `ucent` are now +implemented and usable. They behave like the other integer types: + +- 16-byte values with 8-byte alignment, supporting all integer operators: + arithmetic, bitwise, shifts, comparisons, and compound assignment. +- Integer literals that do not fit in 64 bits now have type `cent` (or + `ucent` with a `u` suffix) instead of being an error. +- Constant folding and compile-time evaluation (CTFE) work with 128-bit + values, including `enum` constants, `static assert`, and compile-time + functions. +- On x86-64, arithmetic is lowered to inline hardware instructions, + including a single `DIV`/`IDIV` for division by a 64-bit divisor; + 128/128-bit division and modulo fall back to the existing `core.int128` + runtime functions. +- On 32-bit x86, all arithmetic, division and comparisons are lowered to the + existing `core.int128` runtime functions (the platform has no 128-bit + registers). This also covers returning 128-bit values from functions, + conditional expressions, boolean tests, and casts to smaller integer types. +- The `.min` and `.max` properties and importC's `__int128`/`unsigned __int128` + are supported. +- `typeid(cent)` and `typeid(ucent)` are supported; the runtime types + `TypeInfo_zi`/`TypeInfo_zk` are provided by a rebuilt druntime. + +Conversions between 128-bit integers and floating point types are not yet +implemented and produce a compile-time error. diff --git a/compiler/include/dmd/common/int128.h b/compiler/include/dmd/common/int128.h new file mode 100644 index 000000000000..63b21ac2c4b9 --- /dev/null +++ b/compiler/include/dmd/common/int128.h @@ -0,0 +1,20 @@ + +/* Compiler implementation of the D programming language + * Copyright (C) 1999-2026 by The D Language Foundation, All Rights Reserved + * written by Walter Bright + * https://www.digitalmars.com + * Distributed under the Boost Software License, Version 1.0. + * https://www.boost.org/LICENSE_1_0.txt + * https://github.com/dlang/dmd/blob/master/src/dmd/common/int128.h + */ + +#pragma once + +#include "dsystem.h" + +// Mirrors dmd.common.int128.Cent +struct alignas(16) Cent +{ + uint64_t lo; // low 64 bits + uint64_t hi; // high 64 bits +}; diff --git a/compiler/include/dmd/expression.h b/compiler/include/dmd/expression.h index 6d135576e86b..a62ea4d76c58 100644 --- a/compiler/include/dmd/expression.h +++ b/compiler/include/dmd/expression.h @@ -19,6 +19,7 @@ #include "root/complex_t.h" #include "root/dcompat.h" #include "root/optional.h" +#include "common/int128.h" class Type; class TypeVector; @@ -117,6 +118,7 @@ class Expression : public ASTNode } IntegerExp* isIntegerExp(); + BigIntegerExp* isBigIntegerExp(); ErrorExp* isErrorExp(); VoidInitExp* isVoidInitExp(); RealExp* isRealExp(); @@ -239,6 +241,16 @@ class IntegerExp final : public Expression static IntegerExp literal(); }; +class BigIntegerExp final : public Expression +{ +public: + Cent value; + + static BigIntegerExp *create(Loc loc, Cent value, Type *type); + void accept(Visitor *v) override { v->visit(this); } + BigIntegerExp *syntaxCopy() override { return this; } +}; + class ErrorExp final : public Expression { public: diff --git a/compiler/include/dmd/tokens.h b/compiler/include/dmd/tokens.h index 0c50aad11d52..2d7022bef91c 100644 --- a/compiler/include/dmd/tokens.h +++ b/compiler/include/dmd/tokens.h @@ -13,6 +13,7 @@ #include "root/dcompat.h" #include "root/port.h" #include "globals.h" +#include "common/int128.h" class Identifier; @@ -402,6 +403,7 @@ enum class EXP : unsigned char // Basic types void_, int64, + bigInteger, float64, complex80, import_, @@ -454,6 +456,7 @@ struct Token // Integers sinteger_t intvalue; uinteger_t unsvalue; + Cent centvalue; // Floats real_t floatvalue; diff --git a/compiler/include/dmd/visitor.h b/compiler/include/dmd/visitor.h index 674ade585c20..5cbfd0aeb581 100644 --- a/compiler/include/dmd/visitor.h +++ b/compiler/include/dmd/visitor.h @@ -187,6 +187,7 @@ class CInitializer; class Expression; class IntegerExp; +class BigIntegerExp; class ErrorExp; class RealExp; class ComplexExp; @@ -473,6 +474,7 @@ class ParseTimeVisitor // Expressions virtual void visit(DeclarationExp *e) { visit((Expression *)e); } virtual void visit(IntegerExp *e) { visit((Expression *)e); } + virtual void visit(BigIntegerExp *e) { visit((Expression *)e); } virtual void visit(NewAnonClassExp *e) { visit((Expression *)e); } virtual void visit(IsExp *e) { visit((Expression *)e); } virtual void visit(RealExp *e) { visit((Expression *)e); } @@ -632,6 +634,7 @@ class Visitor : public ParseTimeVisitor virtual void visit(FuncAliasDeclaration *s) { visit((FuncDeclaration *)s); } virtual void visit(ErrorInitializer *i) { visit((Initializer *)i); } virtual void visit(ErrorExp *e) { visit((Expression *)e); } + virtual void visit(BigIntegerExp *e) { visit((Expression *)e); } virtual void visit(ComplexExp *e) { visit((Expression *)e); } virtual void visit(StructLiteralExp *e) { visit((Expression *)e); } virtual void visit(CompoundLiteralExp *e) { visit((Expression *)e); } diff --git a/compiler/src/build.d b/compiler/src/build.d index aefda5e0d8c4..a98b161ab02d 100755 --- a/compiler/src/build.d +++ b/compiler/src/build.d @@ -1565,7 +1565,7 @@ auto sourceFiles() bitfields.d file.d int128.d blake3.d sha.d outbuffer.d smallbuffer.d charactertables.d identifiertables.d "), commonHeaders: fileArray(env["COMMON"], " - outbuffer.h + outbuffer.h int128.h "), root: fileArray(env["ROOT"], " aav.d complex.d env.d longdouble.d man.d optional.d response.d speller.d string.d strtold.d diff --git a/compiler/src/dmd/astbase.d b/compiler/src/dmd/astbase.d index dfa88c6517c9..aefb012aebfb 100644 --- a/compiler/src/dmd/astbase.d +++ b/compiler/src/dmd/astbase.d @@ -14,6 +14,7 @@ import dmd.astenums; import dmd.visitor.parsetime; import dmd.tokens : EXP; import dmd.expression; +import dmd.common.int128; /** The ASTBase family defines a family of AST nodes appropriate for parsing with * no semantic information. It defines all the AST nodes that the parser needs @@ -4573,6 +4574,7 @@ struct ASTBase final pure inout nothrow @nogc @trusted { inout(IntegerExp) isIntegerExp() { return op == EXP.int64 ? cast(typeof(return))this : null; } + inout(BigIntegerExp) isBigIntegerExp() { return op == EXP.bigInteger ? cast(typeof(return))this : null; } inout(ErrorExp) isErrorExp() { return op == EXP.error ? cast(typeof(return))this : null; } inout(RealExp) isRealExp() { return op == EXP.float64 ? cast(typeof(return))this : null; } inout(IdentifierExp) isIdentifierExp() { return op == EXP.identifier ? cast(typeof(return))this : null; } @@ -4795,6 +4797,30 @@ struct ASTBase } } + extern (C++) final class BigIntegerExp : Expression + { + Cent value; + + extern (D) this(Loc loc, Cent value, Type type) + { + super(loc, EXP.bigInteger, __traits(classInstanceSize, BigIntegerExp)); + assert(type); + if (!type.isScalar()) + { + if (type.ty != Terror) + error(loc, "integral constant must be scalar type, not %s", type.toChars()); + type = Type.terror; + } + this.type = type; + this.value = value; + } + + override void accept(Visitor v) + { + v.visit(this); + } + } + extern (C++) final class NewAnonClassExp : Expression { Expression thisexp; // if !=null, 'this' for class being allocated diff --git a/compiler/src/dmd/backend/backconfig.d b/compiler/src/dmd/backend/backconfig.d index a6f6d766b6a1..8954625e9668 100644 --- a/compiler/src/dmd/backend/backconfig.d +++ b/compiler/src/dmd/backend/backconfig.d @@ -529,6 +529,9 @@ if (exe & (EX_LINUX | EX_LINUX64 | EX_FREEBSD | EX_FREEBSD64 | EX_OPENBSD | EX_O _tyalignsize[TYreal] = 4; _tyalignsize[TYireal] = 4; _tyalignsize[TYcreal] = 4; + // _Alignof(_BitInt(128)) == 4 on i386 System V + _tyalignsize[TYcent] = 4; + _tyalignsize[TYucent] = 4; } else if (exe & (EX_OSX | EX_OSX64)) { @@ -650,6 +653,13 @@ void util_setAArch64(exefmt_t exe) { util_set64(exe); + // _Alignof(_BitInt(128)) == 16 on AArch64 (AAPCS). + // Note: on 64-bit TYdelegate == TYcent and TYdarray == TYucent, so + // delegates/darrays inherit the 16-byte alignment; that is only an + // over-alignment of their storage and is harmless. + _tyalignsize[TYcent] = 16; + _tyalignsize[TYucent] = 16; + if (exe & EX_windos) { _tysize[TYreal] = 16; diff --git a/compiler/src/dmd/backend/cgcs.d b/compiler/src/dmd/backend/cgcs.d index e6c967eb0449..bf3a5bb63cb1 100644 --- a/compiler/src/dmd/backend/cgcs.d +++ b/compiler/src/dmd/backend/cgcs.d @@ -470,6 +470,11 @@ void ecom(ref CGCS cgcs, ref elem* pe) tym == TYvoid || e.Ety & mTYvolatile) return; + /* Don't CSE 128-bit values on 32-bit x86: they cannot be held in + * registers, so the register-based CSE reload has no target registers. + */ + if (I32 && (tym == TYcent || tym == TYucent)) + return; if (tyfloating(tym) && config.inline8087) { /* can CSE XMM code, but not x87 diff --git a/compiler/src/dmd/backend/cgelem.d b/compiler/src/dmd/backend/cgelem.d index 827dbe568db0..822c1dc7f66d 100644 --- a/compiler/src/dmd/backend/cgelem.d +++ b/compiler/src/dmd/backend/cgelem.d @@ -4646,7 +4646,7 @@ private elem* elcmp(elem* e, Goal goal) // Only need to examine MSW tym_t ty = sz == 4 ? TYint : sz == 8 ? TYint : - TYlong; // for TYcent's + TYllong; // for TYcent's e.E1 = el_una(OPmsw, ty, e1); e2.Ety = ty; return optelem(e, Goal.value); diff --git a/compiler/src/dmd/backend/el.d b/compiler/src/dmd/backend/el.d index 93f07be0f1b0..ab7c4152e908 100644 --- a/compiler/src/dmd/backend/el.d +++ b/compiler/src/dmd/backend/el.d @@ -20,6 +20,7 @@ import core.stdc.stdio; import core.stdc.stdlib; import core.stdc.string; +import dmd.common.int128 : Cent; import dmd.backend.cdef; import dmd.backend.cc; import dmd.backend.oper; @@ -976,6 +977,23 @@ elem* el_long(tym_t t,targ_llong val) return e; } +/*************************** + * Make a constant element. + * Params: + * t = type of constant + * val = 128-bit value + * Returns: + * constant element + */ +elem* el_cent(tym_t t, Cent val) +{ + elem* e = el_calloc(); + e.Eoper = OPconst; + e.Ety = t; + e.Vcent = val; + return e; +} + /****************************** * Create a const integer vector elem * Params: @@ -2245,6 +2263,15 @@ bool el_allbits(const elem* e,int bit) case 8: break; + case 16: + // 128-bit constant: 0, 1, or -1 + if (bit == -1) + return e.Vcent.lo == cast(ulong)-1L && e.Vcent.hi == cast(ulong)-1L; + else if (bit == 0) + return e.Vcent.lo == 0 && e.Vcent.hi == 0; + else // bit == 1 + return e.Vcent.lo == 1 && e.Vcent.hi == 0; + default: assert(0); } @@ -2268,6 +2295,16 @@ bool el_signx32(const elem* e) if (e.Vullong != cast(int)e.Vullong) return false; } + else if (tysize(e.Ety) == 16) + { + const ulong lo = e.Vcent.lo; + const ulong hi = e.Vcent.hi; + if (hi == 0 && lo <= int.max) + return true; + if (hi == cast(ulong)-1L && lo >= cast(ulong)int.min) + return true; + return false; + } return true; } diff --git a/compiler/src/dmd/backend/evalu8.d b/compiler/src/dmd/backend/evalu8.d index 1335d5289b63..83e51da6a746 100644 --- a/compiler/src/dmd/backend/evalu8.d +++ b/compiler/src/dmd/backend/evalu8.d @@ -1175,7 +1175,24 @@ static if (0) { targ_llong rem, quo; - assert(!(tym == TYcent || tym == TYucent)); // not yet + if (tym == TYcent || tym == TYucent) + { + // 128-bit dividend / 64-bit divisor; the result packs + // {lo = quotient, hi = remainder} (see toElemCentDivMod). + Cent divisor; + divisor.lo = e2.Vcent.lo; + divisor.hi = (uns || !(divisor.lo >> 63)) ? 0 : -1L; + Cent modulus; + Cent q; + if (uns) + q = dmd.common.int128.udivmod(e1.Vcent, divisor, modulus); + else + q = dmd.common.int128.divmod(e1.Vcent, divisor, modulus); + e.Vcent.lo = q.lo; + e.Vcent.hi = modulus.lo; + break; + } + assert(!tyfloating(tym)); if (!boolres(e2)) goto div0; @@ -1742,7 +1759,7 @@ else break; case OPmsw: - switch (tysize(tym)) + switch (tysize(tybasic(e1.Ety))) { case 4: e.Vllong = (l1 >> 16) & 0xFFFF; diff --git a/compiler/src/dmd/backend/gdag.d b/compiler/src/dmd/backend/gdag.d index d4bceaf31e3f..f1863dc393c0 100644 --- a/compiler/src/dmd/backend/gdag.d +++ b/compiler/src/dmd/backend/gdag.d @@ -176,7 +176,8 @@ private void aewalk(ref GlobalOptimizer go, ref elem* pn, vec_t ae) else if (n != e && el_match(n,e) && e.Ecount < 0xFF-1 && // must fit in unsigned char - cse_float(n) + cse_float(n) && + cse_cent(n) ) { pn = e; // replace n with e @@ -954,3 +955,19 @@ private bool cse_float(const elem* e) e.Eoper != OPvar && e.Eoper != OPconst) || (tyxmmreg(e.Ety) && config.fpxmmregs); } + +/************************************* + * Determine if 128-bit integers should be cse'd. + * Params: + * e = elem to be tested + * Returns: + * true if should be cse'd + */ + +@trusted +private bool cse_cent(const elem* e) +{ + // Don't CSE 128-bit values on 32-bit x86: they cannot be held in + // registers, so the register-based CSE reload has no target registers. + return !(I32 && (tybasic(e.Ety) == TYcent || tybasic(e.Ety) == TYucent)); +} diff --git a/compiler/src/dmd/backend/rtlsym.d b/compiler/src/dmd/backend/rtlsym.d index 310ceda71fa0..d414b8abe8f5 100644 --- a/compiler/src/dmd/backend/rtlsym.d +++ b/compiler/src/dmd/backend/rtlsym.d @@ -116,6 +116,33 @@ enum RTLSYM FMOD, FMODL, + // core.int128 division helpers (128-bit cent/ucent div/mod) + CENTDIV, + CENTUDIV, + CENTREM, + CENTUREM, + + // core.int128 arithmetic helpers (128-bit, used on 32-bit x86) + CENTADD, + CENTSUB, + CENTMUL, + CENTAND, + CENTOR, + CENTXOR, + CENTCOM, + CENTNEG, + CENTSHL, + CENTUSHR, + CENTSAR, + CENTLT, + CENTLE, + CENTGT, + CENTGE, + CENTULT, + CENTULE, + CENTUGT, + CENTUGE, + CXA_ATEXIT } @@ -144,6 +171,7 @@ Symbol* getRtlsym(RTLSYM i) @trusted __gshared type* t; __gshared type* tv; + __gshared type* tc; // extern(D) function type (core.int128 helpers) if (!t) { @@ -155,6 +183,12 @@ Symbol* getRtlsym(RTLSYM i) @trusted tv = type_fake(TYnfunc); tv.Tmangle = Mangle.c; tv.Tcount++; + + // extern(D) function: on 32-bit x86 the hidden return pointer is + // passed in EAX (the D ABI), which requires the TYjfunc function type. + tc = type_fake(TYjfunc); + tc.Tmangle = Mangle.d; + tc.Tcount++; } auto FREGSAVED = cgstate.fregsaved; // varies depending on C ABI @@ -245,6 +279,33 @@ Symbol* getRtlsym(RTLSYM i) @trusted case RTLSYM.FMOD: symbolz(ps,FL.func,FREGSAVED,"fmod", 0, t); break; // C library function fmod() case RTLSYM.FMODL: symbolz(ps,FL.func,FREGSAVED,"fmodl", 0, t); break; // C library function fmodl() + // core.int128 div/udiv/rem/urem, D-mangled names (see druntime/src/core/int128.d) + case RTLSYM.CENTDIV: symbolz(ps,FL.func,FREGSAVED,"_D4core6int1283divFNaNbNiNfSQBaQy4CentQlZQo", 0, tc); break; + case RTLSYM.CENTUDIV: symbolz(ps,FL.func,FREGSAVED,"_D4core6int1284udivFNaNbNiNfSQBbQz4CentQlZQo", 0, tc); break; + case RTLSYM.CENTREM: symbolz(ps,FL.func,FREGSAVED,"_D4core6int1283remFNaNbNiNfSQBaQy4CentQlZQo", 0, tc); break; + case RTLSYM.CENTUREM: symbolz(ps,FL.func,FREGSAVED,"_D4core6int1284uremFNaNbNiNfSQBbQz4CentQlZQo", 0, tc); break; + + // core.int128 arithmetic helpers, D-mangled names (see druntime/src/core/int128.d) + case RTLSYM.CENTADD: symbolz(ps,FL.func,FREGSAVED,"_D4core6int1283addFNaNbNiNfSQBaQy4CentQlZQo", 0, tc); break; + case RTLSYM.CENTSUB: symbolz(ps,FL.func,FREGSAVED,"_D4core6int1283subFNaNbNiNfSQBaQy4CentQlZQo", 0, tc); break; + case RTLSYM.CENTMUL: symbolz(ps,FL.func,FREGSAVED,"_D4core6int1283mulFNaNbNiNfSQBaQy4CentQlZQo", 0, tc); break; + case RTLSYM.CENTAND: symbolz(ps,FL.func,FREGSAVED,"_D4core6int1283andFNaNbNiNfSQBaQy4CentQlZQo", 0, tc); break; + case RTLSYM.CENTOR: symbolz(ps,FL.func,FREGSAVED,"_D4core6int1282orFNaNbNiNfSQzQw4CentQkZQn", 0, tc); break; + case RTLSYM.CENTXOR: symbolz(ps,FL.func,FREGSAVED,"_D4core6int1283xorFNaNbNiNfSQBaQy4CentQlZQo", 0, tc); break; + case RTLSYM.CENTCOM: symbolz(ps,FL.func,FREGSAVED,"_D4core6int1283comFNaNbNiNfSQBaQy4CentZQm", 0, tc); break; + case RTLSYM.CENTNEG: symbolz(ps,FL.func,FREGSAVED,"_D4core6int1283negFNaNbNiNfSQBaQy4CentZQm", 0, tc); break; + case RTLSYM.CENTSHL: symbolz(ps,FL.func,FREGSAVED,"_D4core6int1283shlFNaNbNiNfSQBaQy4CentkZQn", 0, tc); break; + case RTLSYM.CENTUSHR: symbolz(ps,FL.func,FREGSAVED,"_D4core6int1283shrFNaNbNiNfSQBaQy4CentkZQn", 0, tc); break; + case RTLSYM.CENTSAR: symbolz(ps,FL.func,FREGSAVED,"_D4core6int1283sarFNaNbNiNfSQBaQy4CentkZQn", 0, tc); break; + case RTLSYM.CENTLT: symbolz(ps,FL.func,FREGSAVED,"_D4core6int1282ltFNaNbNiNfSQzQw4CentQkZb", 0, tc); break; + case RTLSYM.CENTLE: symbolz(ps,FL.func,FREGSAVED,"_D4core6int1282leFNaNbNiNfSQzQw4CentQkZb", 0, tc); break; + case RTLSYM.CENTGT: symbolz(ps,FL.func,FREGSAVED,"_D4core6int1282gtFNaNbNiNfSQzQw4CentQkZb", 0, tc); break; + case RTLSYM.CENTGE: symbolz(ps,FL.func,FREGSAVED,"_D4core6int1282geFNaNbNiNfSQzQw4CentQkZb", 0, tc); break; + case RTLSYM.CENTULT: symbolz(ps,FL.func,FREGSAVED,"_D4core6int1283ultFNaNbNiNfSQBaQy4CentQlZb", 0, tc); break; + case RTLSYM.CENTULE: symbolz(ps,FL.func,FREGSAVED,"_D4core6int1283uleFNaNbNiNfSQBaQy4CentQlZb", 0, tc); break; + case RTLSYM.CENTUGT: symbolz(ps,FL.func,FREGSAVED,"_D4core6int1283ugtFNaNbNiNfSQBaQy4CentQlZb", 0, tc); break; + case RTLSYM.CENTUGE: symbolz(ps,FL.func,FREGSAVED,"_D4core6int1283ugeFNaNbNiNfSQBaQy4CentQlZb", 0, tc); break; + case RTLSYM.CXA_ATEXIT: symbolz(ps,FL.func,FREGSAVED,"__cxa_atexit", 0, t); break; default: assert(0); diff --git a/compiler/src/dmd/backend/x86/cgreg.d b/compiler/src/dmd/backend/x86/cgreg.d index c9a66b5c60ab..406d78ed1472 100644 --- a/compiler/src/dmd/backend/x86/cgreg.d +++ b/compiler/src/dmd/backend/x86/cgreg.d @@ -93,6 +93,7 @@ void cgreg_init() (sz = cast(uint)type_size(s.Stype)) == 0 || (tysize(s.ty()) == -1) || (I16 && sz > REGSIZE) || + (I32 && sz > 2 * REGSIZE) || // no 128-bit registers on 32-bit x86 (tyfloating(s.ty()) && !(config.fpxmmregs && tyxmmreg(s.ty()))) ) { diff --git a/compiler/src/dmd/backend/x86/cod1.d b/compiler/src/dmd/backend/x86/cod1.d index 3a01624ebd65..316eb46d861a 100644 --- a/compiler/src/dmd/backend/x86/cod1.d +++ b/compiler/src/dmd/backend/x86/cod1.d @@ -4963,7 +4963,7 @@ void pushParams(ref CGstate cg, ref CodeBuilder cdb, elem* e, uint stackalign, t return; } - assert(I64 || sz <= tysize(TYreal)); + assert(I64 || sz <= tysize(TYreal) || (I32 && sz == 16)); int i = cast(int)sz; if (!I16 && i == 2) flag = CF.opsize; @@ -5191,6 +5191,17 @@ void pushParams(ref CGstate cg, ref CodeBuilder cdb, elem* e, uint stackalign, t else if (I16 && sz == 8) // if long long retregs = mSTACK; + // On 32-bit x86 a 16-byte argument may arrive as a (tmp = value, tmp) + // chain (from CSE/argument-order fixing). Evaluate the side effects and + // push the final memory-backed value by value. + if (I32 && sz == 16 && e.Eoper == OPcomma) + { + docommas(cdb, e); + pushParams(cg, cdb, e, stackalign, tyf); + freenode(e); + return; + } + scodelem(cg,cdb,e,retregs,0,true); if (retregs != mSTACK) // if cg.stackpush not already inc'd cg.stackpush += sz; @@ -5390,12 +5401,16 @@ void loaddata(ref CGstate cg, ref CodeBuilder cdb, elem* e, ref regm_t outretreg reg = allocreg(cdb, regm, TYoffset); // get a register int i = sz - REGSIZE; loadea(cg, cdb, e, cs, 0x8B, reg, i, 0, 0); // MOV reg,data+6 + if (I64 && sz == 2 * REGSIZE) + code_orrex(cdb.last(), REX_W); if (tyfloating(tym)) // TYdouble or TYdouble_alias cdb.gen2(0xD1, modregrm(3, 4, reg)); // SHL reg,1 while ((i -= REGSIZE) >= 0) { loadea(cg, cdb, e, cs, 0x0B, reg, i, regm, 0); // OR reg,data+i + if (I64 && sz == 2 * REGSIZE) + code_orrex(cdb.last(), REX_W); code* c = cdb.last(); if (i == 0) c.Iflags |= CF.psw; // need the flags on last OR diff --git a/compiler/src/dmd/backend/x86/cod2.d b/compiler/src/dmd/backend/x86/cod2.d index 6be93082dea0..9fb68b235af8 100644 --- a/compiler/src/dmd/backend/x86/cod2.d +++ b/compiler/src/dmd/backend/x86/cod2.d @@ -292,6 +292,9 @@ void cdorth(ref CGstate cg, ref CodeBuilder cdb,elem* e,ref regm_t pretregs) if ((tylong(ty1) || ty1 == TYhptr) && (tylong(ty2) || ty2 == TYhptr)) numwords++; + /* 128-bit integer operands (TYcent/TYucent) on I64 are two 64-bit words */ + if (sz == 2 * REGSIZE) + numwords++; } // Special cases where only flags are set @@ -705,6 +708,8 @@ void cdorth(ref CGstate cg, ref CodeBuilder cdb,elem* e,ref regm_t pretregs) reg = findreglsw(retregs); rreg = findreglsw(rretregs); genregs(cdb,op1,reg,rreg); + if (I64 && sz == 16) + code_orrex(cdb.last(), REX_W); if (e.Eoper == OPadd || e.Eoper == OPmin) code_orflag(cdb.last(),CF.psw); reg = findregmsw(retregs); @@ -712,7 +717,11 @@ void cdorth(ref CGstate cg, ref CodeBuilder cdb,elem* e,ref regm_t pretregs) if (!(e2oper == OPu16_32 && // if second operand is 0 (op2 == 0x0B || op2 == 0x33)) // and OR or XOR ) + { genregs(cdb,op2,reg,rreg); // ADC msreg,msrreg + if (I64 && sz == 16) + code_orrex(cdb.last(), REX_W); + } } break; @@ -735,6 +744,8 @@ void cdorth(ref CGstate cg, ref CodeBuilder cdb,elem* e,ref regm_t pretregs) case OPconst: if (tyfv(ty2)) goto L2; + if (I64 && sz == 16) + goto L2; // 128-bit constant cannot be encoded as imm32 halves if (numwords == 1) { if (!el_signx32(e2)) @@ -862,6 +873,8 @@ void cdorth(ref CGstate cg, ref CodeBuilder cdb,elem* e,ref regm_t pretregs) } else if (numwords == 2) { + if (I64 && sz == 16) + code_orrex(cdb.last(), REX_W); if (e.Eoper == OPadd || e.Eoper == OPmin) code_orflag(cdb.last(),CF.psw); reg = findregmsw(retregs); @@ -870,6 +883,8 @@ void cdorth(ref CGstate cg, ref CodeBuilder cdb,elem* e,ref regm_t pretregs) cs.Iop = op2; NEWREG(cs.Irm,reg); cdb.gen(&cs); // ADC reg,data+2 + if (I64 && sz == 16) + code_orrex(cdb.last(), REX_W); } else loadea(cg,cdb,e2,cs,op2,reg,REGSIZE,retregs,0); @@ -957,7 +972,7 @@ void cdmul(ref CGstate cg, ref CodeBuilder cdb,elem* e,ref regm_t pretregs) const uns = tyuns(tyml) || tyuns(e2.Ety); // 1 if signed operation, 0 if unsigned const isbyte = tybyte(e.Ety) != 0; const sz = _tysize[tyml]; - const ubyte rex = (I64 && sz == 8) ? REX_W : 0; + const ubyte rex = (I64 && (sz == 8 || sz == 16)) ? REX_W : 0; const uint grex = rex << 16; const OPER opunslng = I16 ? OPu16_32 : OPu32_64; @@ -1208,11 +1223,11 @@ void cdmul(ref CGstate cg, ref CodeBuilder cdb,elem* e,ref regm_t pretregs) ADD EDX,rhi */ getregs(cdb,mAX|mDX|mask(rhi)); - cdb.gen2(0x0FAF,modregrm(3,rhi,AX)); - cdb.gen2(0x0FAF,modregrm(3,DX,rlo)); - cdb.gen2(0x03,modregrm(3,rhi,DX)); - cdb.gen2(0xF7,modregrm(3,4,rlo)); - cdb.gen2(0x03,modregrm(3,DX,rhi)); + cdb.gen2(0x0FAF,grex | modregrm(3,rhi,AX)); + cdb.gen2(0x0FAF,grex | modregrm(3,DX,rlo)); + cdb.gen2(0x03,grex | modregrm(3,rhi,DX)); + cdb.gen2(0xF7,grex | modregrm(3,4,rlo)); + cdb.gen2(0x03,grex | modregrm(3,DX,rhi)); fixresult(cg,cdb,e,mDX|mAX,pretregs); return; } @@ -1348,6 +1363,7 @@ void cddiv(ref CGstate cg, ref CodeBuilder cdb,elem* e,ref regm_t pretregs) const sz = _tysize[tyml]; const ubyte rex = (I64 && sz == 8) ? REX_W : 0; const uint grex = rex << 16; + const uint grex16 = (I64 && sz == 16) ? (REX_W << 16) : 0; code cs; cs.Iflags = CF.zero; @@ -1934,6 +1950,37 @@ void cddiv(ref CGstate cg, ref CodeBuilder cdb,elem* e,ref regm_t pretregs) } fixresult(cg,cdb,e,resreg,pretregs); } + else if (sz == 2 * REGSIZE && I64 && _tysize[tybasic(e2.Ety)] == REGSIZE) + { + /* 128-bit dividend / 64-bit divisor: + * a single hardware DIV/IDIV instruction (RDX:RAX / r64) + */ + regm_t divregs = cg.allregs & ~(mAX | mDX); + scodelem(cg,cdb,e2,divregs,retregs,true); // get rvalue (64-bit divisor) + getregs(cdb,mAX | mDX); + reg_t rreg = findreg(divregs); + cdb.gen2(0xF7,grex16 | modregrmx(3,6 + uns,rreg)); // DIV/IDIV rreg + regm_t resreg; + switch (oper) + { + case OPdiv: + resreg = mAX; + break; + + case OPmod: + resreg = mDX; + break; + + case OPremquo: + resreg = mDX | mAX; + break; + + default: + assert(0); + } + fixresult(cg,cdb,e,resreg,pretregs); + return; + } else if (sz == 2 * REGSIZE) { uint lib; @@ -2203,7 +2250,7 @@ void cdcom(ref CGstate cg, ref CodeBuilder cdb,elem* e,ref regm_t pretregs) } tym_t tym = tybasic(e.Ety); int sz = _tysize[tym]; - uint rex = (I64 && sz == 8) ? REX_W : 0; + uint rex = (I64 && (sz == 8 || sz == 16)) ? REX_W : 0; regm_t possregs = (sz == 1) ? BYTEREGS : cg.allregs; regm_t retregs = pretregs & possregs; if (retregs == 0) @@ -2230,6 +2277,7 @@ void cdcom(ref CGstate cg, ref CodeBuilder cdb,elem* e,ref regm_t pretregs) { const reg2 = findreglsw(retregs); genregs(cdb,op,2,reg2); // NOT reg+1 + code_orrex(cdb.last(), rex); } } fixresult(cg,cdb,e,retregs,pretregs); @@ -2760,7 +2808,8 @@ void cdshift(ref CGstate cg, ref CodeBuilder cdb,elem* e,ref regm_t pretregs) int sz = _tysize[tyml]; assert(!tyfloating(tyml)); OPER oper = e.Eoper; - uint grex = ((I64 && sz == 8) ? REX_W : 0) << 16; + uint grex = ((I64 && (sz == 8 || sz == 16)) ? REX_W : 0) << 16; + const uint rex16 = (I64 && sz == 16) ? (REX_W << 16) : 0; uint s1,s2; switch (oper) @@ -2953,7 +3002,11 @@ void cdshift(ref CGstate cg, ref CodeBuilder cdb,elem* e,ref regm_t pretregs) swap(resreg, sreg); genmovreg(cdb,sreg,resreg); // MOV sreg,resreg if (oper == OPashr) + { cdb.gen1(0x99); // CWD + if (I64 && sz == 16) + code_orrex(cdb.last(), REX_W); // CQO + } else movregconst(cg,cdb,resreg,0,0); // MOV resreg,0 if (forccs) @@ -2968,11 +3021,11 @@ void cdshift(ref CGstate cg, ref CodeBuilder cdb,elem* e,ref regm_t pretregs) swap(resreg, sreg); while (shiftcnt--) { - cdb.gen2(0xD1 ^ isbyte,modregrm(3,s1,resreg)); + cdb.gen2(0xD1 ^ isbyte,rex16 | modregrm(3,s1,resreg)); if (sz == 2 * REGSIZE) { code_orflag(cdb.last(),CF.psw); - cdb.gen2(0xD1,modregrm(3,s2,sreg)); + cdb.gen2(0xD1,rex16 | modregrm(3,s2,sreg)); } } if (forccs) @@ -3115,6 +3168,7 @@ void cdshift(ref CGstate cg, ref CodeBuilder cdb,elem* e,ref regm_t pretregs) } else { code* cl1,cl2; + const uint vrex = I64 ? (REX_W << 16) : 0; scodelem(cg,cdb,e2,rretregs,retregs,false); // get rvalue in CX getregs(cdb,retregs | mCX); // modify these regs @@ -3140,13 +3194,13 @@ void cdshift(ref CGstate cg, ref CodeBuilder cdb,elem* e,ref regm_t pretregs) if (REGSIZE == 2) cdb1.genc2(0x80,modregrm(3,4,CX),REGSIZE * 8 - 1); - cdb1.gen2(0xD3,modregrm(3,4,lreg)); + cdb1.gen2(0xD3,vrex | modregrm(3,4,lreg)); genmovreg(cdb1,hreg,lreg); - genregs(cdb1,0x31,lreg,lreg); + cdb1.gen2(0x31,vrex | modregrm(3,lreg,lreg)); genjmp(cdb,JNE,FL.code,cast(block*)cl1); - cdb.gen2(0x0FA5,modregrm(3,lreg,hreg)); - cdb.gen2(0xD3,modregrm(3,4,lreg)); + cdb.gen2(0x0FA5,vrex | modregrm(3,lreg,hreg)); + cdb.gen2(0xD3,vrex | modregrm(3,4,lreg)); } else { if (oper == OPashr) @@ -3166,8 +3220,8 @@ void cdshift(ref CGstate cg, ref CodeBuilder cdb,elem* e,ref regm_t pretregs) if (REGSIZE == 2) cdb1.genc2(0x80,modregrm(3,4,CX),REGSIZE * 8 - 1); genmovreg(cdb1,lreg,hreg); - cdb1.genc2(0xC1,modregrm(3,s1,hreg),31); - cdb1.gen2(0x0FAD,modregrm(3,hreg,lreg)); + cdb1.genc2(0xC1,vrex | modregrm(3,s1,hreg),31); + cdb1.gen2(0x0FAD,vrex | modregrm(3,hreg,lreg)); } else { @@ -3185,13 +3239,13 @@ void cdshift(ref CGstate cg, ref CodeBuilder cdb,elem* e,ref regm_t pretregs) if (REGSIZE == 2) cdb1.genc2(0x80,modregrm(3,4,CX),REGSIZE * 8 - 1); - cdb1.gen2(0xD3,modregrm(3,5,hreg)); + cdb1.gen2(0xD3,vrex | modregrm(3,5,hreg)); genmovreg(cdb1,lreg,hreg); - genregs(cdb1,0x31,hreg,hreg); + cdb1.gen2(0x31,vrex | modregrm(3,hreg,hreg)); } genjmp(cdb,JNE,FL.code,cast(block*)cl1); - cdb.gen2(0x0FAD,modregrm(3,hreg,lreg)); - cdb.gen2(0xD3,modregrm(3,s1,hreg)); + cdb.gen2(0x0FAD,vrex | modregrm(3,hreg,lreg)); + cdb.gen2(0xD3,vrex | modregrm(3,s1,hreg)); } cl2 = gennop(null); genjmp(cdb,JMPS,FL.code,cast(block*)cl2); @@ -4655,6 +4709,8 @@ void cdrelconst(ref CGstate cg, ref CodeBuilder cdb,elem* e,ref regm_t pretregs) case TYreal: case TYireal: case TYcreal: + case TYcent: + case TYucent: tym = TYnptr; // don't confuse allocreg() if (I16 && pretregs & (mES | mCX) || e.Ety & mTYfar) { @@ -5045,11 +5101,12 @@ void cdneg(ref CGstate cg, ref CodeBuilder cdb,elem* e,ref regm_t pretregs) else if (sz == 2 * REGSIZE) { const msreg = findregmsw(retregs); - cdb.gen2(0xF7,modregrm(3,3,msreg)); // NEG msreg const lsreg = findreglsw(retregs); - cdb.gen2(0xF7,modregrm(3,3,lsreg)); // NEG lsreg + const uint rex = (I64 && sz == 16) ? REX_W : 0; + cdb.gen2(0xF7, (rex << 16) | modregrm(3,3,msreg)); // NEG msreg + cdb.gen2(0xF7, (rex << 16) | modregrm(3,3,lsreg)); // NEG lsreg code_orflag(cdb.last(), CF.psw); // need flag result of previous NEG - cdb.genc2(0x81,modregrm(3,3,msreg),0); // SBB msreg,0 + cdb.genc2(0x81,(rex << 16) | modregrm(3,3,msreg),0); // SBB msreg,0 } else assert(0); diff --git a/compiler/src/dmd/backend/x86/cod3.d b/compiler/src/dmd/backend/x86/cod3.d index c9c6158a2553..7d0e4c3f64a6 100644 --- a/compiler/src/dmd/backend/x86/cod3.d +++ b/compiler/src/dmd/backend/x86/cod3.d @@ -761,7 +761,8 @@ regm_t regmask(tym_t tym, tym_t tyf) case TYcent: case TYucent: - assert(I64); + if (I32) + return 0; // 128-bit values are memory-only on 32-bit return mDX | mAX; case TYvptr: diff --git a/compiler/src/dmd/backend/x86/cod4.d b/compiler/src/dmd/backend/x86/cod4.d index bbae7955fe53..4d16ced6a9c4 100644 --- a/compiler/src/dmd/backend/x86/cod4.d +++ b/compiler/src/dmd/backend/x86/cod4.d @@ -37,6 +37,7 @@ import dmd.backend.divcoeff : choose_multiplier, udiv_coefficients; import dmd.backend.mem; import dmd.backend.el; import dmd.backend.global : REGSIZE, mask; +import dmd.backend.type : type_fake; import dmd.backend.debugprint : oper_str; import dmd.backend.evalu8 : boolres, evalu8, iffalse; import dmd.backend.util2 : ispow2; @@ -408,6 +409,49 @@ void cdeq(ref CGstate cg, ref CodeBuilder cdb,elem* e,ref regm_t pretregs) uint sz = _tysize[tyml]; // # of bytes to transfer assert(cast(int)sz > 0); + // On 32-bit x86, 128-bit values are memory-only (no registers can hold + // them), so do the assignment as a memory-to-memory copy; a constant is + // stored with four 32-bit immediate stores. The value of the assignment + // is the lvalue, which stays in memory. + if (I32 && sz == 16 && (tyml == TYcent || tyml == TYucent)) + { + if (e2oper == OPconst) + { + getlvalue(cg, cdb, cs, e1, 0, RM.store); + cs.Iop = 0xC7; // MOV EA,imm + targ_size_t* p = cast(targ_size_t*) &(e2.EV); + int off = sz; + do + { + cs.IFL2 = FL.const_; + cs.IEV2.Vint = cast(int)*p; + cdb.gen(&cs); // MOV EA+off,const + p = cast(targ_size_t*)(cast(char*) p + REGSIZE); + cs.Iop = (cs.Iop & 1) | 0xC6; + cs.Irm &= cast(ubyte)~cast(int)modregrm(0,7,0); + cs.Irex &= ~REX_R; + cs.IEV1.Voffset += REGSIZE; + off -= REGSIZE; + } while (off > 0); + freenode(e2); + } + else + { + // Memory-to-memory copy via the struct copy code path. + e.Eoper = OPstreq; + e.ET = type_fake(TYcent); + cdstreq(cg, cdb, e, pretregs); + return; + } + if (pretregs) + { + // The result is the lvalue, left in memory (DI, as cdstreq does). + regm_t rregs = mDI; + fixresult(cg, cdb, e, rregs, pretregs); + } + return; + } + if (retregs == 0) // if no return value { FL fl; @@ -985,6 +1029,23 @@ void cdaddass(ref CGstate cg, ref CodeBuilder cdb,elem* e,ref regm_t pretregs) cdb.gen(&cs); break; + case 16: + // 128-bit NEG: NEG EA+8; NEG EA; SBB EA+8,0 + getlvalue_msw(cs); + cdb.gen(&cs); // NEG EA+8 + code_orrex(cdb.last(), REX_W); + getlvalue_lsw(cs); + cdb.gen(&cs); // NEG EA + code_orrex(cdb.last(), REX_W); + code_orflag(cdb.last(),CF.psw); + cs.Iop = 0x81; + getlvalue_msw(cs); + cs.IFL2 = FL.const_; + cs.IEV2.Vuns = 0; + cdb.gen(&cs); // SBB EA+8,0 + code_orrex(cdb.last(), REX_W); + break; + default: assert(0); } @@ -992,6 +1053,7 @@ void cdaddass(ref CGstate cg, ref CodeBuilder cdb,elem* e,ref regm_t pretregs) pretregs &= ~mPSW; } else if ((e2 = e.E2).Eoper == OPconst && // if rvalue is a const + !(I64 && sz == 16) && // 128-bit constants can't be imm32 el_signx32(e2) && // Don't evaluate e2 in register if we can use an INC or DEC (((sz <= REGSIZE || tyfv(tyml)) && @@ -1279,6 +1341,8 @@ void cdaddass(ref CGstate cg, ref CodeBuilder cdb,elem* e,ref regm_t pretregs) { cs.Irm |= modregrm(0,findreglsw(retregs),0); cdb.gen(&cs); // OP1 EA,reg+1 + if (I64 && sz == 16) + code_orrex(cdb.last(), REX_W); code_orflag(cdb.last(),cflags); cs.Iop = op2; NEWREG(cs.Irm,findregmsw(retregs)); // OP2 EA+1,reg @@ -1287,6 +1351,8 @@ void cdaddass(ref CGstate cg, ref CodeBuilder cdb,elem* e,ref regm_t pretregs) else assert(0); cdb.gen(&cs); + if (I64 && sz == 16) + code_orrex(cdb.last(), REX_W); retregs = 0; // to trigger a bug if we attempt to use it } @@ -2576,7 +2642,7 @@ void cdcmp(ref CGstate cg, ref CodeBuilder cdb,elem* e,ref regm_t pretregs) uint sz = _tysize[tym]; uint isbyte = sz == 1; - uint rex = (I64 && sz == 8) ? REX_W : 0; + uint rex = (I64 && (sz == 8 || sz == 16)) ? REX_W : 0; uint grex = rex << 16; // 64 bit operands code cs; @@ -2896,7 +2962,7 @@ void cdcmp(ref CGstate cg, ref CodeBuilder cdb,elem* e,ref regm_t pretregs) cs.IEV2.Vsize_t = cast(targ_size_t)e2.Vllong; // The cmp immediate relies on sign extension of the 32 bit immediate value - if (I64 && sz >= REGSIZE && cs.IEV2.Vsize_t != cast(int)cs.IEV2.Vint) + if (I64 && sz >= REGSIZE && (sz == 16 || cs.IEV2.Vsize_t != cast(int)cs.IEV2.Vint)) goto L2; L4: cs.Iop = 0x81 ^ isbyte; @@ -3874,6 +3940,8 @@ void cdshtlng(ref CGstate cg, ref CodeBuilder cdb,elem* e,ref regm_t pretregs) genmovreg(cdb,msreg,lsreg); // MOV msreg,lsreg assert(config.target_cpu >= TARGET_80286); // 8088 can't handle SAR reg,imm8 cdb.genc2(0xC1,modregrm(3,7,msreg),REGSIZE * 8 - 1); // SAR msreg,31 + if (I64 && e.Eoper == OPs64_128) + code_orrex(cdb.last(), REX_W); fixresult(cg,cdb,e,retregs,pretregs); return; } @@ -4101,7 +4169,6 @@ void cdlngsht(ref CGstate cg, ref CodeBuilder cdb,elem* e,ref regm_t pretregs) * or top 64 bits of 128 bit value (I64). * OPmsw */ - @trusted void cdmsw(ref CGstate cg, ref CodeBuilder cdb,elem* e,ref regm_t pretregs) { diff --git a/compiler/src/dmd/constfold.d b/compiler/src/dmd/constfold.d index 3db808ceeaef..86c48cb92512 100644 --- a/compiler/src/dmd/constfold.d +++ b/compiler/src/dmd/constfold.d @@ -19,6 +19,7 @@ import core.stdc.string; import core.stdc.stdio; import dmd.arraytypes; import dmd.astenums; +import dmd.common.int128; import dmd.ctfeexpr; import dmd.dcast; import dmd.declaration; @@ -61,6 +62,47 @@ void cantExp(out UnionExp ue) emplaceExp!(CTFEExp)(&ue, EXP.cantExpression); } +/* =============================== 128-bit support =========================== */ + +/// Returns true if type is a 128-bit integer type (cent/ucent) +bool isCentType(Type type) +{ + const ty = type.toBasetype().ty; + return ty == Tint128 || ty == Tuns128; +} + +/// Get the 128-bit value of an integral constant expression +Cent getCent(Expression e) +{ + if (auto bie = e.isBigIntegerExp()) + return bie.value; + Cent c; + c.lo = e.toInteger(); + c.hi = e.type.toBasetype().isUnsigned() ? 0 : ((c.lo >> 63) ? -1L : 0); + return c; +} + +private enum ShiftKind { left, signedRight, unsignedRight } + +/// Shift a 128-bit value, following D shift semantics for counts >= 128 +Cent centShift(ShiftKind kind, Cent c1, uint count) +{ + if (count >= 128) + { + // D: shifting by >= the width yields 0, or -1 for signed >> + if (kind == ShiftKind.signedRight && lt(c1, Cent())) + return MinusOne; + return Cent(); + } + switch (kind) + { + case ShiftKind.left: return shl(c1, count); + case ShiftKind.signedRight: return sar(c1, count); + case ShiftKind.unsignedRight: return shr(c1, count); + default: assert(0); + } +} + /* =============================== constFold() ============================== */ /* The constFold() functions were redundant with the optimize() ones, * and so have been folded in with them. @@ -82,6 +124,10 @@ UnionExp Neg(Type type, Expression e1) { emplaceExp!(ComplexExp)(&ue, loc, -e1.toComplex(), type); } + else if (isCentType(type)) + { + emplaceExp!(BigIntegerExp)(&ue, loc, neg(getCent(e1)), type); + } else { emplaceExp!(IntegerExp)(&ue, loc, -e1.toInteger(), type); @@ -93,7 +139,10 @@ UnionExp Com(Type type, Expression e1) { UnionExp ue = void; Loc loc = e1.loc; - emplaceExp!(IntegerExp)(&ue, loc, ~e1.toInteger(), type); + if (isCentType(type)) + emplaceExp!(BigIntegerExp)(&ue, loc, com(getCent(e1)), type); + else + emplaceExp!(IntegerExp)(&ue, loc, ~e1.toInteger(), type); return ue; } @@ -198,6 +247,10 @@ UnionExp Add(Loc loc, Type type, Expression e1, Expression e2) } emplaceExp!(ComplexExp)(&ue, loc, v, type); } + else if (isCentType(type)) + { + emplaceExp!(BigIntegerExp)(&ue, loc, add(getCent(e1), getCent(e2)), type); + } else if (SymOffExp soe = e1.isSymOffExp()) { emplaceExp!(SymOffExp)(&ue, loc, soe.var, soe.offset + e2.toInteger()); @@ -263,6 +316,10 @@ UnionExp Mul(Loc loc, Type type, Expression e1, Expression e2) else assert(0); } + else if (isCentType(type)) + { + emplaceExp!(BigIntegerExp)(&ue, loc, mul(getCent(e1), getCent(e2)), type); + } else { emplaceExp!(IntegerExp)(&ue, loc, e1.toInteger() * e2.toInteger(), type); @@ -307,6 +364,30 @@ UnionExp Div(Loc loc, Type type, Expression e1, Expression e2) else assert(0); } + else if (isCentType(type)) + { + Cent n1 = getCent(e1); + Cent n2 = getCent(e2); + if (n2.lo == 0 && n2.hi == 0) + { + error(e2.loc, "divide by 0"); + emplaceExp!(ErrorExp)(&ue); + return ue; + } + if (n2 == MinusOne && !type.isUnsigned() && + n1.lo == 0 && n1.hi == 0x8000000000000000UL) + { + error(e2.loc, "integer overflow: `cent.min / -1`"); + emplaceExp!(ErrorExp)(&ue); + return ue; + } + Cent n; + if (type.isUnsigned()) + n = udiv(n1, n2); + else + n = div(n1, n2); + emplaceExp!(BigIntegerExp)(&ue, loc, n, type); + } else { sinteger_t n1; @@ -372,6 +453,30 @@ UnionExp Mod(Loc loc, Type type, Expression e1, Expression e2) else assert(0); } + else if (isCentType(type)) + { + Cent n1 = getCent(e1); + Cent n2 = getCent(e2); + if (n2.lo == 0 && n2.hi == 0) + { + error(e2.loc, "divide by 0"); + emplaceExp!(ErrorExp)(&ue); + return ue; + } + if (n2 == MinusOne && !type.isUnsigned() && + n1.lo == 0 && n1.hi == 0x8000000000000000UL) + { + error(e2.loc, "integer overflow: `cent.min %% -1`"); + emplaceExp!(ErrorExp)(&ue); + return ue; + } + Cent r; + if (type.isUnsigned()) + udivmod(n1, n2, r); + else + divmod(n1, n2, r); + emplaceExp!(BigIntegerExp)(&ue, loc, r, type); + } else { sinteger_t n1; @@ -445,8 +550,16 @@ UnionExp Pow(Loc loc, Type type, Expression e1, Expression e2) } else { - emplaceExp!(IntegerExp)(&ur, loc, e1.toInteger(), e1.type); - emplaceExp!(IntegerExp)(&uv, loc, 1, e1.type); + if (isCentType(e1.type)) + { + emplaceExp!(BigIntegerExp)(&ur, loc, getCent(e1), e1.type); + emplaceExp!(BigIntegerExp)(&uv, loc, Cent(1), e1.type); + } + else + { + emplaceExp!(IntegerExp)(&ur, loc, e1.toInteger(), e1.type); + emplaceExp!(IntegerExp)(&uv, loc, 1, e1.type); + } } Expression r = ur.exp(); Expression v = uv.exp(); @@ -470,6 +583,8 @@ UnionExp Pow(Loc loc, Type type, Expression e1, Expression e2) } if (type.isComplex()) emplaceExp!(ComplexExp)(&ue, loc, v.toComplex(), type); + else if (isCentType(type)) + emplaceExp!(BigIntegerExp)(&ue, loc, getCent(v), type); else if (type.isIntegral()) emplaceExp!(IntegerExp)(&ue, loc, v.toInteger(), type); else @@ -493,13 +608,21 @@ UnionExp Pow(Loc loc, Type type, Expression e1, Expression e2) UnionExp Shl(Loc loc, Type type, Expression e1, Expression e2) { UnionExp ue = void; - emplaceExp!(IntegerExp)(&ue, loc, e1.toInteger() << e2.toInteger(), type); + if (isCentType(type)) + emplaceExp!(BigIntegerExp)(&ue, loc, centShift(ShiftKind.left, getCent(e1), cast(uint)e2.toInteger()), type); + else + emplaceExp!(IntegerExp)(&ue, loc, e1.toInteger() << e2.toInteger(), type); return ue; } UnionExp Shr(Loc loc, Type type, Expression e1, Expression e2) { UnionExp ue = void; + if (isCentType(type)) + { + emplaceExp!(BigIntegerExp)(&ue, loc, centShift(ShiftKind.signedRight, getCent(e1), cast(uint)e2.toInteger()), type); + return ue; + } dinteger_t value = e1.toInteger(); dinteger_t dcount = e2.toInteger(); assert(dcount <= 0xFFFFFFFF); @@ -546,6 +669,11 @@ UnionExp Shr(Loc loc, Type type, Expression e1, Expression e2) UnionExp Ushr(Loc loc, Type type, Expression e1, Expression e2) { UnionExp ue = void; + if (isCentType(type)) + { + emplaceExp!(BigIntegerExp)(&ue, loc, centShift(ShiftKind.unsignedRight, getCent(e1), cast(uint)e2.toInteger()), type); + return ue; + } dinteger_t value = e1.toInteger(); dinteger_t dcount = e2.toInteger(); assert(dcount <= 0xFFFFFFFF); @@ -586,14 +714,20 @@ UnionExp Ushr(Loc loc, Type type, Expression e1, Expression e2) UnionExp And(Loc loc, Type type, Expression e1, Expression e2) { UnionExp ue = void; - emplaceExp!(IntegerExp)(&ue, loc, e1.toInteger() & e2.toInteger(), type); + if (isCentType(type)) + emplaceExp!(BigIntegerExp)(&ue, loc, and(getCent(e1), getCent(e2)), type); + else + emplaceExp!(IntegerExp)(&ue, loc, e1.toInteger() & e2.toInteger(), type); return ue; } UnionExp Or(Loc loc, Type type, Expression e1, Expression e2) { UnionExp ue = void; - emplaceExp!(IntegerExp)(&ue, loc, e1.toInteger() | e2.toInteger(), type); + if (isCentType(type)) + emplaceExp!(BigIntegerExp)(&ue, loc, or(getCent(e1), getCent(e2)), type); + else + emplaceExp!(IntegerExp)(&ue, loc, e1.toInteger() | e2.toInteger(), type); return ue; } @@ -601,7 +735,10 @@ UnionExp Xor(Loc loc, Type type, Expression e1, Expression e2) { //printf("Xor(linnum = %d, e1 = %s, e2 = %s)\n", loc.linnum, e1.toChars(), e2.toChars()); UnionExp ue = void; - emplaceExp!(IntegerExp)(&ue, loc, e1.toInteger() ^ e2.toInteger(), type); + if (isCentType(type)) + emplaceExp!(BigIntegerExp)(&ue, loc, xor(getCent(e1), getCent(e2)), type); + else + emplaceExp!(IntegerExp)(&ue, loc, e1.toInteger() ^ e2.toInteger(), type); return ue; } @@ -787,7 +924,10 @@ UnionExp Equal(EXP op, Loc loc, Type type, Expression e1, Expression e2) } else if (e1.type.isIntegral() || e1.type.toBasetype().ty == Tpointer) { - cmp = (e1.toInteger() == e2.toInteger()); + if (isCentType(e1.type)) + cmp = getCent(e1) == getCent(e2); + else + cmp = (e1.toInteger() == e2.toInteger()); } else { @@ -892,14 +1032,36 @@ UnionExp Cmp(EXP op, Loc loc, Type type, Expression e1, Expression e2) } else { - sinteger_t n1; - sinteger_t n2; - n1 = e1.toInteger(); - n2 = e2.toInteger(); - if (e1.type.isUnsigned() || e2.type.isUnsigned()) - n = intUnsignedCmp(op, n1, n2); + if (isCentType(e1.type)) + { + Cent c1 = getCent(e1); + Cent c2 = getCent(e2); + int rawCmp; + if (e1.type.isUnsigned() || e2.type.isUnsigned()) + { + if (ult(c1, c2)) rawCmp = -1; + else if (c1 == c2) rawCmp = 0; + else rawCmp = 1; + } + else + { + if (lt(c1, c2)) rawCmp = -1; + else if (c1 == c2) rawCmp = 0; + else rawCmp = 1; + } + n = specificCmp(op, rawCmp); + } else - n = intSignedCmp(op, n1, n2); + { + sinteger_t n1; + sinteger_t n2; + n1 = e1.toInteger(); + n2 = e2.toInteger(); + if (e1.type.isUnsigned() || e2.type.isUnsigned()) + n = intUnsignedCmp(op, n1, n2); + else + n = intSignedCmp(op, n1, n2); + } } emplaceExp!(IntegerExp)(&ue, loc, n, type); return ue; @@ -977,6 +1139,52 @@ UnionExp Cast(Loc loc, Type type, Type to, Expression e1) } else if (type.isIntegral()) { + if (auto bie = e1.isBigIntegerExp()) + { + // 128-bit value cast to another integer type: truncate mod 2^n + const uinteger_t v = bie.value.lo; + switch (typeb.ty) + { + case Tbool: + emplaceExp!(IntegerExp)(&ue, loc, v != 0, type); + break; + case Tint8: + case Tchar: + case Tuns8: + emplaceExp!(IntegerExp)(&ue, loc, cast(ubyte)v, type); + break; + case Tint16: + case Twchar: + case Tuns16: + emplaceExp!(IntegerExp)(&ue, loc, cast(ushort)v, type); + break; + case Tint32: + case Tdchar: + case Tuns32: + emplaceExp!(IntegerExp)(&ue, loc, cast(uint)v, type); + break; + case Tint64: + emplaceExp!(IntegerExp)(&ue, loc, cast(long)v, type); + break; + case Tuns64: + emplaceExp!(IntegerExp)(&ue, loc, v, type); + break; + case Tint128: + case Tuns128: + emplaceExp!(BigIntegerExp)(&ue, loc, bie.value, type); + break; + default: + assert(0); + } + return ue; + } + if (isCentType(type)) + { + // 64-bit (or smaller) integer -> 128-bit: sign or zero extend + Cent c = getCent(e1); + emplaceExp!(BigIntegerExp)(&ue, loc, c, type); + return ue; + } if (e1.type.isFloating()) { dinteger_t result; diff --git a/compiler/src/dmd/ctfeexpr.d b/compiler/src/dmd/ctfeexpr.d index 6ca0d57c74c4..3d1cd74e8872 100644 --- a/compiler/src/dmd/ctfeexpr.d +++ b/compiler/src/dmd/ctfeexpr.d @@ -77,10 +77,11 @@ extern (D) struct UnionExp private: // Ensure that the union is suitably aligned. - align(8) union _AnonStruct_u + align(16) union _AnonStruct_u { char[__traits(classInstanceSize, Expression)] exp; char[__traits(classInstanceSize, IntegerExp)] integerexp; + char[__traits(classInstanceSize, BigIntegerExp)] bigintegerexp; char[__traits(classInstanceSize, ErrorExp)] errorexp; char[__traits(classInstanceSize, RealExp)] realexp; char[__traits(classInstanceSize, ComplexExp)] complexexp; @@ -319,6 +320,7 @@ UnionExp copyLiteral(Expression e) case EXP.variable: case EXP.dotVariable: case EXP.int64: + case EXP.bigInteger: case EXP.float64: case EXP.complex80: case EXP.void_: @@ -1236,6 +1238,8 @@ private int ctfeRawCmp(Loc loc, Expression e1, Expression e2, bool identity = fa } if (e1.type.isIntegral()) { + if (e1.type.toBasetype().ty == Tint128 || e1.type.toBasetype().ty == Tuns128) + return !(getCent(e1) == getCent(e2)); return e1.toInteger() != e2.toInteger(); } if (identity && e1.type.isFloating()) @@ -1381,6 +1385,27 @@ bool ctfeCmp(Loc loc, EXP op, Expression e1, Expression e2) return realCmp(op, e1.toReal(), e2.toReal()); if (t1.isImaginary()) return realCmp(op, e1.toImaginary(), e2.toImaginary()); + if (t1.ty == Tint128 || t1.ty == Tuns128) + { + // 128-bit integer comparison + import dmd.common.int128 : Cent, lt, ult; + Cent c1 = getCent(e1); + Cent c2 = getCent(e2); + int rawCmp; + if (t1.isUnsigned() || t2.isUnsigned()) + { + if (ult(c1, c2)) rawCmp = -1; + else if (c1 == c2) rawCmp = 0; + else rawCmp = 1; + } + else + { + if (lt(c1, c2)) rawCmp = -1; + else if (c1 == c2) rawCmp = 0; + else rawCmp = 1; + } + return specificCmp(op, rawCmp); + } if (t1.isUnsigned() || t2.isUnsigned()) return intUnsignedCmp(op, e1.toInteger(), e2.toInteger()); else @@ -1662,6 +1687,11 @@ void assignInPlace(Expression dest, Expression src) dest.isIntegerExp().setInteger(src.isIntegerExp().getInteger()); return; } + else if (dest.op == EXP.bigInteger && src.op == EXP.bigInteger) + { + dest.isBigIntegerExp().value = src.isBigIntegerExp().value; + return; + } else if (dest.op == EXP.float64 && src.op == EXP.float64) { dest.isRealExp().value = src.isRealExp().value; @@ -1812,6 +1842,7 @@ bool isCtfeValueValid(Expression newval) switch (newval.op) { case EXP.int64: + case EXP.bigInteger: case EXP.float64: case EXP.complex80: return tb.isScalar(); diff --git a/compiler/src/dmd/dcast.d b/compiler/src/dmd/dcast.d index 0a8de2860bb9..567893547775 100644 --- a/compiler/src/dmd/dcast.d +++ b/compiler/src/dmd/dcast.d @@ -345,7 +345,8 @@ MATCH implicitConvTo(Expression e, Type t) /* See if we can do integral narrowing conversions */ - if (e.type.isIntegral() && t.isIntegral() && e.type.isTypeBasic() && t.isTypeBasic()) + if (e.type.isIntegral() && t.isIntegral() && e.type.isTypeBasic() && t.isTypeBasic() && + e.type.size() <= uinteger_t.sizeof) { IntRange src = getIntRange(e); IntRange target = intRangeFromType(t); @@ -2265,6 +2266,13 @@ Expression castTo(Expression e, Scope* sc, Type t, Type att = null) // arithmetic values vs. T* if (tob_isA && (t1b_isA || t1b.ty == Tpointer) || t1b_isA && (tob_isA || tob.ty == Tpointer)) { + // 128-bit integer <-> floating point conversions are not supported yet + if ((t1b.ty == Tint128 || t1b.ty == Tuns128 || tob.ty == Tint128 || tob.ty == Tuns128) && + (t1b.isFloating() || tob.isFloating() || t1b.isImaginary() || tob.isImaginary() || t1b.isComplex() || tob.isComplex())) + { + error(e.loc, "conversion between `%s` and `%s` is not supported yet", e.type.toErrMsg(), t.toErrMsg()); + return ErrorExp.get(); + } return ok(); } diff --git a/compiler/src/dmd/dfa/fast/expression.d b/compiler/src/dmd/dfa/fast/expression.d index 5c54a0d376c6..86572da8e99a 100644 --- a/compiler/src/dmd/dfa/fast/expression.d +++ b/compiler/src/dmd/dfa/fast/expression.d @@ -22,6 +22,7 @@ import dmd.dfa.fast.statement; import dmd.dfa.fast.structure; import dmd.dfa.utils; import dmd.common.outbuffer; +import dmd.globals : sinteger_t; import dmd.location; import dmd.expression; import dmd.expressionsem; @@ -502,6 +503,7 @@ struct ExpressionWalker case EXP.assocArrayLiteral: case EXP.arrayLength: case EXP.int64: + case EXP.bigInteger: case EXP.null_: case EXP.cast_: case EXP.variable: @@ -1709,6 +1711,7 @@ struct ExpressionWalker return false; case EXP.int64: + case EXP.bigInteger: case EXP.string_: return true; @@ -2069,6 +2072,34 @@ struct ExpressionWalker return ret; } + case EXP.bigInteger: + { + auto bie = expr.isBigIntegerExp; + + DFALatticeRef ret = dfaCommon.makeLatticeRef; + DFAConsequence* c = ret.addConsequence(null); + ret.setContext(c); + + if ((bie.value.hi & 0x8000000000000000) == 0 + && ((bie.value.hi & 0x7FFFFFFFFFFFFFFF) != 0 || bie.value.lo > long.max)) + { + c.pa = DFAPAValue(DFAPAValue.Kind.UnknownUpperPositive); + c.truthiness = Truthiness.True; + } + else if ((bie.value.hi & 0x8000000000000000) != 0 + && ((bie.value.hi & 0x7FFFFFFFFFFFFFFF) != 0 || bie.value.lo < long.min)) + c.pa = DFAPAValue(DFAPAValue.Kind.Unknown); + else + { + // DFA models 128-bit values as their low 64 bits + c.pa = DFAPAValue(cast(sinteger_t) bie.value.lo); + c.truthiness = (bie.value.lo | bie.value.hi) != 0 + ? Truthiness.True : Truthiness.False; + } + + return ret; + } + case EXP.cast_: { auto ce = expr.isCastExp; @@ -2292,6 +2323,7 @@ struct ExpressionWalker case EXP.cast_: case EXP.null_: case EXP.int64: + case EXP.bigInteger: case EXP.arrayLength: case EXP.string_: case EXP.typeid_: diff --git a/compiler/src/dmd/dinterpret.d b/compiler/src/dmd/dinterpret.d index 1acc41706143..3020e07b5c9a 100644 --- a/compiler/src/dmd/dinterpret.d +++ b/compiler/src/dmd/dinterpret.d @@ -69,6 +69,7 @@ public Expression ctfeInterpret(Expression e) switch (e.op) { case EXP.int64: + case EXP.bigInteger: case EXP.float64: case EXP.complex80: case EXP.null_: @@ -1800,6 +1801,15 @@ public: result = e; } + override void visit(BigIntegerExp e) + { + debug (LOG) + { + printf("%s BigIntegerExp::interpret() %s\n", e.loc.toChars(), e.toChars()); + } + result = e; + } + override void visit(RealExp e) { debug (LOG) @@ -5194,7 +5204,7 @@ public: ctfeGlobals.stack.pop(e.lengthVar); // $ is defined only inside [] if (exceptionOrCantInterpret(e2)) return false; - if (e2.op != EXP.int64) + if (e2.op != EXP.int64 && e2.op != EXP.bigInteger) { error(e.loc, "CTFE internal error: non-integral index `[%s]`", e.e2.toErrMsg()); return false; @@ -6825,6 +6835,7 @@ private Expression copyRegionExp(Expression e) case EXP.typeid_: case EXP.string_: case EXP.int64: + case EXP.bigInteger: case EXP.error: case EXP.float64: case EXP.complex80: diff --git a/compiler/src/dmd/dsymbolsem.d b/compiler/src/dmd/dsymbolsem.d index f319d14ec629..c72fdc657d4f 100644 --- a/compiler/src/dmd/dsymbolsem.d +++ b/compiler/src/dmd/dsymbolsem.d @@ -10068,6 +10068,9 @@ bool _isZeroInit(Expression exp) case EXP.int64: return exp.toInteger() == 0; + case EXP.bigInteger: + return exp.isBigIntegerExp().value.lo == 0 && exp.isBigIntegerExp().value.hi == 0; + case EXP.null_: return true; diff --git a/compiler/src/dmd/expression.d b/compiler/src/dmd/expression.d index 091ba5262ff4..6c12486d4ce6 100644 --- a/compiler/src/dmd/expression.d +++ b/compiler/src/dmd/expression.d @@ -20,6 +20,7 @@ import core.stdc.string; import dmd.arraytypes; import dmd.astenums; import dmd.ast_node; +import dmd.common.int128 : Cent; import dmd.dclass; import dmd.declaration; import dmd.dstruct; @@ -315,6 +316,7 @@ extern (C++) abstract class Expression : ASTNode switch (op) { case EXP.int64: + case EXP.bigInteger: case EXP.float64: case EXP.complex80: return 1; @@ -336,6 +338,7 @@ extern (C++) abstract class Expression : ASTNode final pure inout nothrow @nogc @trusted { inout(IntegerExp) isIntegerExp() { return op == EXP.int64 ? cast(typeof(return))this : null; } + inout(BigIntegerExp) isBigIntegerExp() { return op == EXP.bigInteger ? cast(typeof(return))this : null; } inout(ErrorExp) isErrorExp() { return op == EXP.error ? cast(typeof(return))this : null; } inout(VoidInitExp) isVoidInitExp() { return op == EXP.void_ ? cast(typeof(return))this : null; } inout(RealExp) isRealExp() { return op == EXP.float64 ? cast(typeof(return))this : null; } @@ -634,6 +637,38 @@ extern (C++) final class IntegerExp : Expression } } +/*********************************************************** + * A compile-time known 128-bit integer value (cent/ucent) + */ +extern (C++) final class BigIntegerExp : Expression +{ + Cent value; + + extern (D) this(Loc loc, Cent value, Type type) + { + super(loc, EXP.bigInteger); + assert(type); + assert(_isRoughlyScalar(type) || type.ty == Terror); + this.type = type; + this.value = value; + } + + static BigIntegerExp create(Loc loc, Cent value, Type type) + { + return new BigIntegerExp(loc, value, type); + } + + override void accept(Visitor v) + { + v.visit(this); + } + + override BigIntegerExp syntaxCopy() + { + return this; + } +} + /*********************************************************** * Use this expression for error recovery. * @@ -4136,6 +4171,7 @@ private immutable ubyte[EXP.max+1] expSize = [ EXP.error: __traits(classInstanceSize, ErrorExp), EXP.void_: __traits(classInstanceSize, VoidInitExp), EXP.int64: __traits(classInstanceSize, IntegerExp), + EXP.bigInteger: __traits(classInstanceSize, BigIntegerExp), EXP.float64: __traits(classInstanceSize, RealExp), EXP.complex80: __traits(classInstanceSize, ComplexExp), EXP.import_: __traits(classInstanceSize, ImportExp), diff --git a/compiler/src/dmd/expressionsem.d b/compiler/src/dmd/expressionsem.d index 0aaf4de6472a..9717363358cb 100644 --- a/compiler/src/dmd/expressionsem.d +++ b/compiler/src/dmd/expressionsem.d @@ -196,6 +196,19 @@ dinteger_t toInteger(Expression _this) // normalize() is necessary until we fix all the paints of 'type' return iexp.value = IntegerExp.normalize(iexp.type.toBasetype().ty, iexp.value); } + else if (auto biexp = _this.isBigIntegerExp()) + { + const isUnsigned = biexp.type.toBasetype().isUnsigned(); + const bool fits = isUnsigned + ? biexp.value.hi == 0 + : biexp.value.hi == -1L || (biexp.value.hi == 0 && !(biexp.value.lo >> 63)); + if (fits) + { + return cast(sinteger_t)biexp.value.lo; + } + error(_this.loc, "integer constant expression does not fit in 64 bits: `%s`", _this.toErrMsg()); + return 0; + } else if (auto rexp = _this.isRealExp()) { return cast(sinteger_t)rexp.toReal(); @@ -501,6 +514,12 @@ Optional!bool toBool(Expression _this) return typeof(return)(r); } + static Optional!bool bigIntegerToBool(BigIntegerExp _this) + { + bool r = _this.value.lo != 0 || _this.value.hi != 0; + return typeof(return)(r); + } + static Optional!bool arrayLiteralToBool(ArrayLiteralExp _this) { size_t dim = _this.length; @@ -523,6 +542,7 @@ Optional!bool toBool(Expression _this) switch(_this.op) { case EXP.int64: return integerToBool(_this.isIntegerExp()); + case EXP.bigInteger: return bigIntegerToBool(_this.isBigIntegerExp()); case EXP.float64: return typeof(return)(!!_this.isRealExp().value); case EXP.complex80: return typeof(return)(!!_this.isComplexExp().value); // `this` is never null (what about structs?) @@ -915,6 +935,11 @@ bool equals(const Expression _this, const Expression e) return _this.type.toHeadMutable().equals(e.type.toHeadMutable()) && RealIdentical(_this.value, e.value); } + static bool bigIntegerExpEquals(const BigIntegerExp _this, const BigIntegerExp e) + { + return _this.type.toHeadMutable().equals(e.type.toHeadMutable()) && _this.value == e.value; + } + static bool complexExpEquals(const ComplexExp _this, const ComplexExp e) { return _this.type.toHeadMutable().equals(e.type.toHeadMutable()) && @@ -1035,6 +1060,7 @@ bool equals(const Expression _this, const Expression e) switch(_this.op) { case EXP.int64: return intExpEquals(_this.isIntegerExp(), e.isIntegerExp()); + case EXP.bigInteger: return bigIntegerExpEquals(_this.isBigIntegerExp(), e.isBigIntegerExp()); case EXP.float64: return realExpEquals(_this.isRealExp(), e.isRealExp()); case EXP.complex80: return complexExpEquals(_this.isComplexExp(), e.isComplexExp()); case EXP.null_: return nullExpEquals(_this.isNullExp(), e.isNullExp()); @@ -5436,6 +5462,16 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor result = e; } + override void visit(BigIntegerExp e) + { + assert(e.type); + if (e.type.ty == Terror) + return setError(); + + assert(e.type.deco); + result = e; + } + override void visit(RealExp e) { if (!e.type) @@ -17128,6 +17164,7 @@ bool checkSharedAccess(Expression e, Scope* sc, bool returnRef = false) case EXP.error: case EXP.complex80: case EXP.int64: + case EXP.bigInteger: case EXP.null_: return false; case EXP.variable: return visitVar(e.isVarExp()); @@ -18338,6 +18375,15 @@ Expression toBoolean(Expression exp, Scope* sc) } e = checkNoreturnVarAccess(e); + // 128-bit integers are boolean-testable, but the codegen cannot + // test a 16-byte value directly on 32-bit x86; convert explicitly + // so that e2ir can lower `cast(bool)` via core.int128. + if (tb.ty == Tint128 || tb.ty == Tuns128) + { + e = new CastExp(exp.loc, e, Type.tbool); + e = e.expressionSemantic(sc); + return e; + } if (!t.isBoolean()) { if (tb != Type.terror) diff --git a/compiler/src/dmd/glue/e2ir.d b/compiler/src/dmd/glue/e2ir.d index 854ab9de8cda..abb340fadbcf 100644 --- a/compiler/src/dmd/glue/e2ir.d +++ b/compiler/src/dmd/glue/e2ir.d @@ -67,6 +67,7 @@ import dmd.typesem; import dmd.visitor; import dmd.backend.cc; +import dmd.common.int128 : Cent; import dmd.backend.cdef; import dmd.backend.cgcv; import dmd.backend.code; @@ -1097,7 +1098,28 @@ elem* toElem(Expression e, ref IRState irs) elem* visitInteger(IntegerExp ie) { - elem* e = el_long(totym(ie.type), ie.getInteger()); + elem* e; + const ty = ie.type.toBasetype().ty; + if (ty == Tint128 || ty == Tuns128) + { + // 64-bit value widened to 128 bits (e.g. cent.init, cent = 0) + Cent c; + c.lo = ie.value; + c.hi = (ie.type.toBasetype().isUnsigned() || !(ie.value >> 63)) ? 0 : -1L; + e = el_cent(totym(ie.type), c); + } + else + e = el_long(totym(ie.type), ie.getInteger()); + elem_setLoc(e,ie.loc); + return e; + } + + /*************************************** + */ + + elem* visitBigInteger(BigIntegerExp ie) + { + elem* e = el_cent(totym(ie.type), ie.value); elem_setLoc(e,ie.loc); return e; } @@ -1562,6 +1584,56 @@ elem* toElem(Expression e, ref IRState irs) return e; } + /*************************************** + * Emit a call to a core.int128 function with one or two Cent arguments. + * On SysV x86-64 the 16-byte values go in register pairs; on Win64 + * they are passed by pointer and the result is returned via a + * hidden sret pointer (as druntime's `Cent` struct requires). + * On 32-bit x86 the arguments are pushed by value on the stack and the + * result is returned through a hidden pointer passed in EAX (the D ABI); + * the call is lowered like a struct-returning call there. + */ + elem* el_callCentLib(RTLSYM rtlsym, tym_t tym, elem* el, elem* er) + { + if (target.os & Target.OS.Windows && target.isX86_64) + { + Symbol* sret = symbol_genauto(type_fake(tybasic(tym))); + Symbol* sa = symbol_genauto(type_fake(tybasic(el.Ety))); + Symbol* sb = symbol_genauto(type_fake(tybasic(er.Ety))); + // &(tmp = value, tmp) + elem* addrOfTmp(Symbol* stmp, elem* value) + { + elem* eeq = el_bin(OPeq, value.Ety, el_var(stmp), value); + return el_una(OPaddr, TYnptr, el_bin(OPcomma, value.Ety, eeq, el_var(stmp))); + } + // On Win64 arguments are evaluated/assigned right-to-left, so the + // first argument (the sret pointer) goes rightmost in the chain. + elem* eargs = el_param( + el_param( + addrOfTmp(sa, el), + addrOfTmp(sb, er)), + el_una(OPaddr, TYnptr, el_var(sret))); + elem* ecall = el_bin(OPcall, TYnptr, el_var(getRtlsym(rtlsym)), eargs); + // *(comma(call, &sret)) + return el_una(OPind, tym, el_combine(ecall, el_una(OPaddr, TYnptr, el_var(sret)))); + } + if (!target.isX86_64 && (tybasic(tym) == TYcent || tybasic(tym) == TYucent)) + { + /* 32-bit x86 has no 128-bit registers. Mirror the lowering used + * for struct-returning calls: the hidden return pointer goes last + * (so the backend hands it EAX), the call is typed TYnptr and the + * returned pointer is dereferenced to yield the 16-byte value. + */ + Symbol* stmp = symbol_genauto(type_fake(tybasic(tym))); + elem* eargs = er ? el_param(el, er) : el; + eargs = el_param(eargs, el_una(OPaddr, TYnptr, el_var(stmp))); + elem* ecall = el_bin(OPcall, TYnptr, el_var(getRtlsym(rtlsym)), eargs); + elem* e = el_combine(ecall, el_una(OPaddr, TYnptr, el_var(stmp))); + return el_una(OPind, tym, e); + } + return el_bin(OPcall, tym, el_var(getRtlsym(rtlsym)), er ? el_param(el, er) : el_param(el, el_long(TYbool, 0))); + } + //////////////////////////// Unary /////////////////////////////// /*************************************** @@ -1589,6 +1661,14 @@ elem* toElem(Expression e, ref IRState irs) } default: + if (!target.isX86_64 && + (tb1.ty == Tint128 || tb1.ty == Tuns128)) + { + // 32-bit x86: use core.int128.neg + elem* e2 = el_callCentLib(RTLSYM.CENTNEG, totym(ne.type), e, null); + elem_setLoc(e2, ne.loc); + return e2; + } e = el_una(OPneg, totym(ne.type), e); break; } @@ -1628,6 +1708,14 @@ elem* toElem(Expression e, ref IRState irs) } default: + if (!target.isX86_64 && + (tb1.ty == Tint128 || tb1.ty == Tuns128)) + { + // 32-bit x86: use core.int128.com + elem* e2 = el_callCentLib(RTLSYM.CENTCOM, ty, e1, null); + elem_setLoc(e2, ce.loc); + return e2; + } e = el_una(OPcom,ty,e1); break; } @@ -1794,9 +1882,94 @@ elem* toElem(Expression e, ref IRState irs) return el_bin(OPcall, TYnoreturn, sym, e); } + /*************************************** + * Lower a 128-bit binary operation on 32-bit x86 to a core.int128 call. + * Returns null if `op` has no core.int128 equivalent. + */ + elem* toElemCentLib(BinExp be, int op, elem* el, elem* er) + { + const isUns = be.e1.type.toBasetype().isUnsigned() || be.e2.type.toBasetype().isUnsigned(); + tym_t tym = totym(be.type); + + RTLSYM rtlsym; + switch (op) + { + case OPadd: rtlsym = RTLSYM.CENTADD; break; + case OPmin: rtlsym = RTLSYM.CENTSUB; break; + case OPmul: rtlsym = RTLSYM.CENTMUL; break; + case OPand: rtlsym = RTLSYM.CENTAND; break; + case OPor: rtlsym = RTLSYM.CENTOR; break; + case OPxor: rtlsym = RTLSYM.CENTXOR; break; + case OPshl: rtlsym = RTLSYM.CENTSHL; break; + case OPshr: rtlsym = RTLSYM.CENTUSHR; break; + case OPashr: rtlsym = RTLSYM.CENTSAR; break; + case OPlt: rtlsym = isUns ? RTLSYM.CENTULT : RTLSYM.CENTLT; break; + case OPle: rtlsym = isUns ? RTLSYM.CENTULE : RTLSYM.CENTLE; break; + case OPgt: rtlsym = isUns ? RTLSYM.CENTUGT : RTLSYM.CENTGT; break; + case OPge: rtlsym = isUns ? RTLSYM.CENTUGE : RTLSYM.CENTGE; break; + case OPeqeq: + case OPne: + { + // a == b <=> !(a < b) && !(b < a) + // a != b <=> (a < b) || (b < a) + RTLSYM lt1 = isUns ? RTLSYM.CENTULT : RTLSYM.CENTLT; + if (!target.isX86_64) + { + // On 32-bit x86 each operand is used twice, so materialize + // them once into 16-byte temps to avoid shared-elem CSEs. + Symbol* sa = symbol_genauto(type_fake(tybasic(el.Ety))); + Symbol* sb = symbol_genauto(type_fake(tybasic(er.Ety))); + elem* eel = el_bin(OPeq, el.Ety, el_var(sa), el); + elem* eer = el_bin(OPeq, er.Ety, el_var(sb), er); + elem* eva = el_una(OPind, el.Ety, el_una(OPaddr, TYnptr, el_var(sa))); + elem* evb = el_una(OPind, er.Ety, el_una(OPaddr, TYnptr, el_var(sb))); + elem* c1 = el_callCentLib(lt1, TYbool, el_copytree(eva), el_copytree(evb)); + elem* c2 = el_callCentLib(lt1, TYbool, el_copytree(evb), el_copytree(eva)); + elem* econd = (op == OPeqeq) + ? el_una(OPnot, TYbool, el_bin(OPor, TYbool, c1, c2)) + : el_bin(OPor, TYbool, c1, c2); + return el_combine(eel, el_combine(eer, econd)); + } + elem* c1 = el_callCentLib(lt1, TYbool, el_same(el), el_same(er)); + elem* c2 = el_callCentLib(lt1, TYbool, el_same(er), el_same(el)); + if (op == OPeqeq) + return el_una(OPnot, TYbool, el_bin(OPor, TYbool, c1, c2)); + else + return el_bin(OPor, TYbool, c1, c2); + } + default: return null; + } + return el_callCentLib(rtlsym, tym, el, er); + } + elem* visitPost(PostExp pe) { - //printf("PostExp.toElem() '%s'\n", pe.toChars()); + const t128 = pe.e1.type.toBasetype().ty == Tint128 || pe.e1.type.toBasetype().ty == Tuns128; + if (!target.isX86_64 && t128) + { + // 32-bit x86: lower x++ to (tmp = x, x = x OP 1, tmp), with all + // 128-bit values kept in memory. + elem* e = toElem(pe.e1, irs); + elem* einc = toElem(pe.e2, irs); + const tym = totym(pe.type); + elem* eaddr = addressElem(e, pe.e1.type.pointerTo()); + Symbol* stmp = symbol_genauto(type_fake(TYcent)); + elem* pa = el_una(OPaddr, TYnptr, el_var(stmp)); + // tmp = x + elem* ecopy = el_bin(OPeq, tym, + el_una(OPind, tym, el_copytree(pa)), + el_una(OPind, tym, el_copytree(eaddr))); + // x = x OP 1 + elem* eload = el_una(OPind, tym, el_copytree(eaddr)); + elem* ecall = toElemCentLib(pe, pe.op == EXP.plusPlus ? OPadd : OPmin, eload, einc); + elem* estore = el_bin(OPeq, tym, + el_una(OPind, tym, el_copytree(eaddr)), ecall); + // tmp (the pre-increment value) + elem* eres = el_una(OPind, tym, el_copytree(pa)); + elem* e2 = el_combine(ecopy, el_combine(estore, eres)); + elem_setLoc(e2, pe.loc); + return e2; + } elem* e = toElem(pe.e1, irs); elem* einc = toElem(pe.e2, irs); e = el_bin((pe.op == EXP.plusPlus) ? OPpostinc : OPpostdec, @@ -1805,10 +1978,67 @@ elem* toElem(Expression e, ref IRState irs) return e; } - //////////////////////////// Binary /////////////////////////////// - - /******************************************** + /*************************************** + * 128-bit integer division/modulo. + * Prefer the hardware 128/64 DIV instruction when the divisor fits + * in 64 bits; otherwise call core.int128 (div/udiv/rem/urem). */ + elem* toElemCentDivMod(BinExp be, int op, elem* el, elem* er) + { + assert(op == OPdiv || op == OPmod); + assert(el && er); + + const isUns = be.type.toBasetype().isUnsigned(); + tym_t tym = totym(be.type); + + // Hardware path: 128-bit dividend / 64-bit divisor on x86-64. + // The hardware 128/64 DIV faults when the quotient does not fit in + // 64 bits (dividend.hi >= divisor), so use it only when the + // dividend's high word is provably zero, i.e. when the dividend is + // a widened 64-bit value. + if (target.isX86_64 && + (el.Eoper == OPu64_128 || el.Eoper == OPs64_128)) + { + elem* div64 = null; + if (er.Eoper == OPconst && + (tybasic(er.Ety) == TYcent || tybasic(er.Ety) == TYucent) && + (er.Vcent.hi == 0 || (er.Vcent.hi == -1L && (er.Vcent.lo >> 63)))) + { + div64 = el_long(isUns ? TYullong : TYllong, cast(targ_llong)er.Vcent.lo); + } + else if (er.Eoper == OPs64_128 || er.Eoper == OPu64_128) + { + div64 = er.E1; + er.E1 = null; + } + if (div64) + { + // Hardware 128/64 division: OPremquo yields the quotient in RAX + // and the remainder in RDX. Extract and widen to 128 bits. + elem* erq = el_bin(OPremquo, tym, el, div64); + elem* e64; + if (op == OPmod) + e64 = el_una(OPmsw, isUns ? TYullong : TYllong, erq); // remainder + else + e64 = el_una(OP128_64, isUns ? TYullong : TYllong, erq); // quotient + elem* e = el_una(isUns ? OPu64_128 : OPs64_128, tym, e64); + elem_setLoc(e, be.loc); + el_free(er); + return e; + } + } + + // Software path: call core.int128.{div,udiv,rem,urem} + RTLSYM rtlsym; + switch (op) + { + case OPdiv: rtlsym = isUns ? RTLSYM.CENTUDIV : RTLSYM.CENTDIV; break; + case OPmod: rtlsym = isUns ? RTLSYM.CENTUREM : RTLSYM.CENTREM; break; + default: assert(0); + } + return el_callCentLib(rtlsym, tym, el, er); + } + elem* toElemBin(BinExp be, int op) { //printf("toElemBin() '%s'\n", be.toChars()); @@ -1825,6 +2055,21 @@ elem* toElem(Expression e, ref IRState irs) elem* el = toElem(be.e1, irs); elem* er = toElem(be.e2, irs); + // On 32-bit x86 there is no 128-bit hardware; use core.int128 + if (op != OPeq && !target.isX86_64 && + (tb1.ty == Tint128 || tb1.ty == Tuns128 || + tb2.ty == Tint128 || tb2.ty == Tuns128 || + be.type.toBasetype().ty == Tint128 || be.type.toBasetype().ty == Tuns128)) + { + if (op == OPdiv || op == OPmod) + return toElemCentDivMod(be, op, el, er); + if (elem* ecall = toElemCentLib(be, op, el, er)) + { + elem_setLoc(ecall, be.loc); + return ecall; + } + } + elem* e = el_bin(op,tym,el,er); elem_setLoc(e,be.loc); @@ -1896,7 +2141,25 @@ elem* toElem(Expression e, ref IRState irs) elem* er = toElem(be.e2, irs); elem* e; - if (op == OPmodass && + if ((op == OPdivass || op == OPmodass) && + (be.type.toBasetype().ty == Tint128 || be.type.toBasetype().ty == Tuns128)) + { + /* The backend can't do 128-bit in-place div/mod; lower to + * *(addr) = *(addr) / er , *(addr) + * using fresh copies of the lvalue address (the shared el/ev + * would trip the optimizer's Ecount == 0 assertion). + */ + elem* eaddr = addressElem(toElem(be.e1, irs), be.e1.type.pointerTo()); + elem* eload = el_una(OPind, tym, el_copytree(eaddr)); + elem* estore = el_una(OPind, tym, el_copytree(eaddr)); + elem* eres = el_una(OPind, tym, el_copytree(eaddr)); + int nop = (op == OPdivass) ? OPdiv : OPmod; + elem* ediv = toElemCentDivMod(be, nop, eload, er); + e = el_bin(OPeq, tym, estore, ediv); + e = el_combine(e, eres); + // el/ev are intentionally leaked (they may alias the fresh elems) + } + else if (op == OPmodass && target.isAArch64 && // x87 has FPREM instruction, others use fmod() isFloating(be.type)) { @@ -1913,6 +2176,42 @@ elem* toElem(Expression e, ref IRState irs) e = el_bin(OPcall,tym,el_var(getRtlsym(rtlsym)),el_param(el, er)); } + else if (!target.isX86_64 && + (be.type.toBasetype().ty == Tint128 || be.type.toBasetype().ty == Tuns128)) + { + /* 32-bit x86 has no 128-bit registers: 16-byte values must stay + * in memory. Use fresh copies of the lvalue address (the shared + * el/ev would create a 16-byte CSE the backend cannot handle). + * Compound assignment becomes a load-call-store via core.int128. + */ + elem* eaddr = addressElem(toElem(be.e1, irs), be.e1.type.pointerTo()); + elem* eload = el_una(OPind, tym, el_copytree(eaddr)); + elem* estore = el_una(OPind, tym, el_copytree(eaddr)); + elem* eres = el_una(OPind, tym, el_copytree(eaddr)); + if (op == OPeq) + e = el_bin(OPeq, tym, estore, er); + else + { + int nop; + final switch (op) + { + case OPaddass: nop = OPadd; break; + case OPminass: nop = OPmin; break; + case OPmulass: nop = OPmul; break; + case OPandass: nop = OPand; break; + case OPorass: nop = OPor; break; + case OPxorass: nop = OPxor; break; + case OPshlass: nop = OPshl; break; + case OPshrass: nop = OPshr; break; + case OPashrass: nop = OPashr; break; + } + elem* ecall = toElemCentLib(be, nop, eload, er); + assert(ecall); + e = el_bin(OPeq, tym, estore, ecall); + } + e = el_combine(e, eres); + // el/ev are intentionally leaked (they may alias the fresh elems) + } else { e = el_bin(op, tym, el, er); @@ -1997,11 +2296,17 @@ elem* toElem(Expression e, ref IRState irs) return toElemBin(e, OPmul); } - /************************************ + /*************************************** */ elem* visitDiv(DivExp e) { + if (e.type.toBasetype().ty == Tint128 || e.type.toBasetype().ty == Tuns128) + { + elem* el = toElem(e.e1, irs); + elem* er = toElem(e.e2, irs); + return toElemCentDivMod(e, OPdiv, el, er); + } return toElemBin(e, OPdiv); } @@ -2031,6 +2336,12 @@ elem* toElem(Expression e, ref IRState irs) elem_setLoc(eresult, e.loc); return eresult; } + if (e.type.toBasetype().ty == Tint128 || e.type.toBasetype().ty == Tuns128) + { + elem* el = toElem(e.e1, irs); + elem* er = toElem(e.e2, irs); + return toElemCentDivMod(e, OPmod, el, er); + } return toElemBin(e, OPmod); } @@ -3282,6 +3593,9 @@ elem* toElem(Expression e, ref IRState irs) elem* visitUshr(UshrExp se) { + if (!target.isX86_64 && + (se.e1.type.toBasetype().ty == Tint128 || se.e1.type.toBasetype().ty == Tuns128)) + return toElemBin(se, OPshr); // 32-bit x86: core.int128.ushr elem* eleft = toElem(se.e1, irs); eleft.Ety = touns(eleft.Ety); elem* eright = toElem(se.e2, irs); @@ -3374,6 +3688,28 @@ elem* toElem(Expression e, ref IRState irs) } else { + if (!target.isX86_64 && + (tybasic(ty) == TYcent || tybasic(ty) == TYucent)) + { + /* 32-bit x86 has no 128-bit registers: the backend cannot + * select between two 16-byte values with OPcond, so + * materialize each branch in memory and select between the + * two pointers instead. + */ + Symbol* stmp1 = symbol_genauto(type_fake(tybasic(ty))); + Symbol* stmp2 = symbol_genauto(type_fake(tybasic(ty))); + elem* es1 = el_bin(OPeq, ty, el_var(stmp1), eleft); + elem* es2 = el_bin(OPeq, ty, el_var(stmp2), eright); + elem* esel = el_bin(OPcond, TYnptr, ec, + el_bin(OPcolon, TYnptr, + el_una(OPaddr, TYnptr, el_var(stmp1)), + el_una(OPaddr, TYnptr, el_var(stmp2)))); + e = el_bin(OPcomma, TYnptr, es1, es2); + e = el_bin(OPcomma, TYnptr, e, esel); + e = el_una(OPind, ty, e); + elem_setLoc(e, ce.loc); + return e; + } e = el_bin(OPcond, ty, ec, el_bin(OPcolon, ty, eleft, eright)); if (tybasic(ty) == TYstruct) e.ET = Type_toCtype(ce.e1.type); @@ -3830,6 +4166,31 @@ elem* toElem(Expression e, ref IRState irs) // When there is a lowering availabe, use that elem* e = ce.lowering is null ? toElem(ce.e1, irs) : toElem(ce.lowering, irs); + if (!target.isX86_64 && + ce.to.toBasetype().ty == Tbool && + (tybasic(e.Ety) == TYcent || tybasic(e.Ety) == TYucent)) + { + /* 32-bit x86 has no 128-bit registers: the backend cannot + * booleanize a 16-byte value (OPbool), so test against zero + * with a core.int128 comparison instead: + * v != 0 <=> (v < 0) || (0 < v) + */ + const isUns = tybasic(e.Ety) == TYucent; + RTLSYM lt = isUns ? RTLSYM.CENTULT : RTLSYM.CENTLT; + elem* ez = el_cent(tybasic(e.Ety), Cent()); + // Materialize the operands once (each is used twice). + Symbol* sv = symbol_genauto(type_fake(tybasic(e.Ety))); + Symbol* sz = symbol_genauto(type_fake(tybasic(e.Ety))); + elem* eev = el_bin(OPeq, e.Ety, el_var(sv), e); + elem* eez = el_bin(OPeq, ez.Ety, el_var(sz), ez); + elem* evv = el_una(OPind, e.Ety, el_una(OPaddr, TYnptr, el_var(sv))); + elem* evz = el_una(OPind, ez.Ety, el_una(OPaddr, TYnptr, el_var(sz))); + elem* c1 = el_callCentLib(lt, TYbool, el_copytree(evv), el_copytree(evz)); + elem* c2 = el_callCentLib(lt, TYbool, el_copytree(evz), el_copytree(evv)); + e = el_bin(OPor, TYbool, c1, c2); + return el_combine(eev, el_combine(eez, e)); + } + return toElemCast(ce, e, false, irs); } @@ -4238,6 +4599,7 @@ elem* toElem(Expression e, ref IRState irs) case EXP.variable: return visitSymbol(e.isVarExp()); case EXP.symbolOffset: return visitSymbol(e.isSymOffExp()); case EXP.int64: return visitInteger(e.isIntegerExp()); + case EXP.bigInteger: return visitBigInteger(e.isBigIntegerExp()); case EXP.float64: return visitReal(e.isRealExp()); case EXP.complex80: return visitComplex(e.isComplexExp()); case EXP.this_: return visitThis(e.isThisExp()); @@ -4294,7 +4656,10 @@ elem* toElem(Expression e, ref IRState irs) */ elem* toElemRVO(Expression e, elem* ehidden, ref IRState irs) { - assert(e.type.toBasetype().ty == Tstruct || e.type.toBasetype().ty == Tsarray); + const ety = e.type.toBasetype().ty; + assert(ety == Tstruct || ety == Tsarray || + // 32-bit x86 returns cent/ucent via a hidden pointer (RET.stack) + (!target.isX86_64 && (ety == Tint128 || ety == Tuns128))); elem* doCommaRVO(CommaExp ce) { @@ -4574,6 +4939,101 @@ elem* ExpressionsToStaticArray(ref IRState irs, Loc loc, Expressions* exps, Symb return e; } +/*************************************** + * On 32-bit x86, 128-bit values are kept in memory. Return an elem + * holding the low 64 bits of the (memory-backed or constant) 128-bit + * value `e`. + */ +elem* m32CentLow64(elem* e) +{ + if (e.Eoper == OPconst) + { + const targ_llong lo = cast(targ_llong) e.Vcent.lo; + el_free(e); + return el_long(TYulong, lo); + } + if (e.Eoper == OPvar) + return el_una(OPind, TYulong, el_una(OPaddr, TYnptr, e)); + assert(e.Eoper == OPind); + elem* e1 = e.E1; + e.E1 = null; + el_free(e); + return el_una(OPind, TYulong, e1); +} + +/*************************************** + * On 32-bit x86, load the low `t.size()` bytes of the 128-bit value `e` + * (little-endian) directly as a value of type `t`, without routing it + * through a 64-bit intermediate (which would require a register pair the + * backend cannot always provide). + */ +elem* m32CentLow(elem* e, Type t) +{ + if (e.Eoper == OPconst) + { + const targ_ullong lo = e.Vcent.lo; + el_free(e); + return el_long(totym(t), lo); + } + if (e.Eoper == OPvar) + return el_una(OPind, totym(t), el_una(OPaddr, TYnptr, e)); + assert(e.Eoper == OPind); + elem* e1 = e.E1; + e.E1 = null; + el_free(e); + return el_una(OPind, totym(t), e1); +} + +/*************************************** + * On 32-bit x86, build the 128-bit value of widening the 64-bit value + * `e` in a temp: + * tmp.lo = e; tmp.hi = signExtend ? e >> 63 : 0 + * and return `*(cent)&tmp` (a memory-backed value). + */ +elem* m32CentWiden(CastExp ce, elem* e, bool signExtend) +{ + Symbol* st64 = symbol_genauto(type_fake(TYllong)); + Symbol* sthi = symbol_genauto(type_fake(TYllong)); + Symbol* stmp = symbol_genauto(type_fake(TYcent)); + Symbol* sp = symbol_genauto(type_fake(TYnptr)); + Symbol* sp64 = symbol_genauto(type_fake(TYnptr)); + Symbol* sphi = symbol_genauto(type_fake(TYnptr)); + elem* eassign = el_bin(OPeq, TYllong, el_var(st64), e); // st64 = e + elem* ehi = signExtend + ? el_bin(OPashr, TYllong, el_var(st64), el_long(TYint, 63)) + : el_long(TYllong, 0); + elem* eassign2 = el_bin(OPeq, TYllong, el_var(sthi), ehi); // sthi = e >> 63 + elem* ep = el_bin(OPeq, TYnptr, el_var(sp), + el_una(OPaddr, TYnptr, el_var(stmp))); // sp = &stmp + elem* ep64 = el_bin(OPeq, TYnptr, el_var(sp64), + el_una(OPaddr, TYnptr, el_var(st64))); // sp64 = &st64 + elem* ephi = el_bin(OPeq, TYnptr, el_var(sphi), + el_una(OPaddr, TYnptr, el_var(sthi))); // sphi = &sthi + // tmp.lo = st64, tmp.hi = sthi, via 32-bit copies + elem* ecopy(elem* dptr, elem* sptr) + { + elem* c0 = el_bin(OPeq, TYuint, + el_una(OPind, TYuint, el_copytree(dptr)), + el_una(OPind, TYuint, el_copytree(sptr))); + elem* c1 = el_bin(OPeq, TYuint, + el_una(OPind, TYuint, el_bin(OPadd, TYnptr, el_copytree(dptr), el_long(TYsize_t, 4))), + el_una(OPind, TYuint, el_bin(OPadd, TYnptr, el_copytree(sptr), el_long(TYsize_t, 4)))); + return el_combine(c0, c1); + } + elem* elo = ecopy(el_var(sp), el_var(sp64)); + elem* e1 = ecopy(el_bin(OPadd, TYnptr, el_var(sp), el_long(TYsize_t, 8)), el_var(sphi)); + elem* eres = el_una(OPind, TYcent, + el_combine(eassign, + el_combine(eassign2, + el_combine(ep, + el_combine(ep64, + el_combine(ephi, + el_combine(elo, + el_combine(e1, el_una(OPaddr, TYnptr, el_var(stmp)))))))))); + elem_setLoc(eres, ce.loc); + return eres; +} + /*************************************************** */ elem* toElemCast(CastExp ce, elem* e, bool isLvalue, ref IRState irs) @@ -5069,7 +5529,10 @@ elem* toElemCast(CastExp ce, elem* e, bool isLvalue, ref IRState irs) case X(Tint64,Tuns32): eop = OP64_32; return Leop(ce, e, eop, ttym); case X(Tint64,Tuns64): return Lpaint(ce, e, ttym); case X(Tint64,Tint128): - case X(Tint64,Tuns128): eop = OPs64_128; return Leop(ce, e, eop, ttym); + case X(Tint64,Tuns128): + if (!target.isX86_64) + return m32CentWiden(ce, e, true); + eop = OPs64_128; return Leop(ce, e, eop, ttym); case X(Tint64,Tfloat32): case X(Tint64,Tfloat64): case X(Tint64,Tfloat80): @@ -5095,7 +5558,10 @@ elem* toElemCast(CastExp ce, elem* e, bool isLvalue, ref IRState irs) case X(Tuns64,Tuns32): eop = OP64_32; return Leop(ce, e, eop, ttym); case X(Tuns64,Tint64): return Lpaint(ce, e, ttym); case X(Tuns64,Tint128): - case X(Tuns64,Tuns128): eop = OPu64_128; return Leop(ce, e, eop, ttym); + case X(Tuns64,Tuns128): + if (!target.isX86_64) + return m32CentWiden(ce, e, false); + eop = OPu64_128; return Leop(ce, e, eop, ttym); case X(Tuns64,Tfloat32): case X(Tuns64,Tfloat64): case X(Tuns64,Tfloat80): @@ -5117,11 +5583,20 @@ elem* toElemCast(CastExp ce, elem* e, bool isLvalue, ref IRState irs) case X(Tint128,Tuns16): case X(Tint128,Tint32): case X(Tint128,Tuns32): + if (!target.isX86_64) + return Lret(ce, m32CentLow(e, t)); e = el_una(OP128_64, TYllong, e); fty = Tint64; continue; case X(Tint128,Tint64): - case X(Tint128,Tuns64): eop = OP128_64; return Leop(ce, e, eop, ttym); + case X(Tint128,Tuns64): + if (!target.isX86_64) + { + elem* e64 = m32CentLow64(e); + e64.Ety = ttym; + return Lret(ce, e64); + } + eop = OP128_64; return Leop(ce, e, eop, ttym); case X(Tint128,Tuns128): return Lpaint(ce, e, ttym); static if (0) // cent <=> floating point not supported yet { @@ -5147,11 +5622,20 @@ elem* toElemCast(CastExp ce, elem* e, bool isLvalue, ref IRState irs) case X(Tuns128,Tuns16): case X(Tuns128,Tint32): case X(Tuns128,Tuns32): + if (!target.isX86_64) + return Lret(ce, m32CentLow(e, t)); e = el_una(OP128_64, TYllong, e); fty = Tint64; continue; case X(Tuns128,Tint64): - case X(Tuns128,Tuns64): eop = OP128_64; return Leop(ce, e, eop, ttym); + case X(Tuns128,Tuns64): + if (!target.isX86_64) + { + elem* e64 = m32CentLow64(e); + e64.Ety = ttym; + return Lret(ce, e64); + } + eop = OP128_64; return Leop(ce, e, eop, ttym); case X(Tuns128,Tint128): return Lpaint(ce, e, ttym); static if (0) // cent <=> floating point not supported yet { diff --git a/compiler/src/dmd/glue/package.d b/compiler/src/dmd/glue/package.d index e252457a49ec..93ef0f40d7be 100644 --- a/compiler/src/dmd/glue/package.d +++ b/compiler/src/dmd/glue/package.d @@ -258,6 +258,8 @@ tym_t totym(Type tx) case Tuns32: t = TYuint; break; case Tint64: t = TYllong; break; case Tuns64: t = TYullong; break; + case Tint128: t = TYcent; break; + case Tuns128: t = TYucent; break; case Tfloat32: t = TYfloat; break; case Tfloat64: t = TYdouble; break; case Tfloat80: t = RealIsDouble ? TYdouble : TYreal; break; diff --git a/compiler/src/dmd/glue/todt.d b/compiler/src/dmd/glue/todt.d index 456788d95556..1ffdb33c79a3 100644 --- a/compiler/src/dmd/glue/todt.d +++ b/compiler/src/dmd/glue/todt.d @@ -324,6 +324,12 @@ void Expression_toDt(Expression e, ref DtBuilder dtb) dtb.nbytes((cast(ubyte*) &value)[0 .. cast(size_t) e.type.size()]); } + void visitBigInteger(BigIntegerExp e) + { + auto value = e.value; + dtb.nbytes((cast(ubyte*) &value)[0 .. cast(size_t) e.type.size()]); + } + void visitReal(RealExp e) { //printf("RealExp.toDt(%Lg)\n", e.value); @@ -658,6 +664,7 @@ void Expression_toDt(Expression e, ref DtBuilder dtb) case EXP.cast_: return visitCast (e.isCastExp()); case EXP.address: return visitAddr (e.isAddrExp()); case EXP.int64: return visitInteger (e.isIntegerExp()); + case EXP.bigInteger: return visitBigInteger (e.isBigIntegerExp()); case EXP.float64: return visitReal (e.isRealExp()); case EXP.complex80: return visitComplex (e.isComplexExp()); case EXP.null_: return visitNull (e.isNullExp()); diff --git a/compiler/src/dmd/hdrgen.d b/compiler/src/dmd/hdrgen.d index 6b43796bcba1..3a75c88c65ec 100644 --- a/compiler/src/dmd/hdrgen.d +++ b/compiler/src/dmd/hdrgen.d @@ -32,6 +32,7 @@ import dmd.dstruct; import dmd.dsymbol; import dmd.dtemplate; import dmd.dversion; +import dmd.common.int128 : Cent; import dmd.expression; import dmd.func; import dmd.id; @@ -49,6 +50,7 @@ import dmd.root.string; import dmd.statement; import dmd.staticassert; import dmd.tokens; +import dmd.typesem : isUnsigned, toBasetype; import dmd.visitor; struct HdrGenState @@ -2315,6 +2317,14 @@ private void expressionPrettyPrint(Expression e, ref OutBuffer buf, ref HdrGenSt buf.print(v); } + void visitBigInteger(BigIntegerExp e) + { + const Cent v = e.value; + const isUnsigned = e.type.toBasetype().isUnsigned(); + buf.put(isUnsigned ? "cast(ucent)((cast(ucent)0x" : "cast(cent)((cast(cent)0x"); + buf.printf("%llxULL << 64) | 0x%llxULL)", cast(ulong)v.hi, cast(ulong)v.lo); + } + void visitError(ErrorExp e) { buf.put("__error"); @@ -3086,6 +3096,7 @@ private void expressionPrettyPrint(Expression e, ref OutBuffer buf, ref HdrGenSt return visit(e); case EXP.int64: return visitInteger(e.isIntegerExp()); + case EXP.bigInteger: return visitBigInteger(e.isBigIntegerExp()); case EXP.error: return visitError(e.isErrorExp()); case EXP.void_: return visitVoidInit(e.isVoidInitExp()); case EXP.float64: return visitReal(e.isRealExp()); @@ -4662,6 +4673,7 @@ string EXPtoString(EXP op) EXP.this_ : "this", EXP.super_ : "super", EXP.int64 : "long", + EXP.bigInteger : "cent", EXP.float64 : "double", EXP.complex80 : "creal", EXP.null_ : "null", diff --git a/compiler/src/dmd/lexer.d b/compiler/src/dmd/lexer.d index eafd259a9e14..80b59ba6496d 100644 --- a/compiler/src/dmd/lexer.d +++ b/compiler/src/dmd/lexer.d @@ -22,6 +22,7 @@ import dmd.errorsink; import dmd.id; import dmd.identifier; import dmd.location; +import dmd.common.int128 : Cent, add, mul; import dmd.common.smallbuffer; import dmd.common.outbuffer; import dmd.common.charactertables; @@ -2438,6 +2439,8 @@ class Lexer int base = 10; const start = p; ulong n = 0; // unsigned >=64 bit integer type + Cent c128 = Cent(); // 128-bit accumulator (used when n overflows) + bool use128 = false; int d; bool err = false; bool overflow = false; @@ -2592,14 +2595,26 @@ class Lexer errorDigit = cast(char) c; } // Avoid expensive overflow check if we aren't at risk of overflow - if (n <= 0x0FFF_FFFF_FFFF_FFFFUL) + if (use128) + { + // Keep accumulating in 128 bits + c128 = add(mul(c128, Cent(base)), Cent(d)); + } + else if (n <= 0x0FFF_FFFF_FFFF_FFFFUL) n = n * base + d; else { import core.checkedint : mulu, addu; + const prev = n; n = mulu(n, base, overflow); n = addu(n, d, overflow); + if (overflow) + { + // Restart the accumulation at 128 bits + use128 = true; + c128 = add(mul(Cent(prev), Cent(base)), Cent(d)); + } } } Ldone: @@ -2610,7 +2625,7 @@ class Lexer "decimal".ptr, errorDigit); err = true; } - if (overflow && !err) + if (overflow && !err && !use128) { error(scanloc, "integer overflow"); err = true; @@ -2664,6 +2679,13 @@ class Lexer } break; } + if (use128) + { + // 128-bit integer literal (does not fit in 64 bits) + t.centvalue = c128; + const uflag = (flags & FLAGS.unsigned) != 0; + return uflag ? TOK.uns128Literal : TOK.int128Literal; + } if (base == 8 && n >= 8) { if (err) diff --git a/compiler/src/dmd/optimize.d b/compiler/src/dmd/optimize.d index 7de96ec52fe0..e1294940af41 100644 --- a/compiler/src/dmd/optimize.d +++ b/compiler/src/dmd/optimize.d @@ -1119,7 +1119,7 @@ Expression optimize(Expression e, int result, bool keepLvalue = false) expOptimize(e.e2, result, keepLvalue); if (ret.op == EXP.error) return; - if (!e.e1 || e.e1.op == EXP.int64 || e.e1.op == EXP.float64 || !hasSideEffect(e.e1)) + if (!e.e1 || e.e1.op == EXP.int64 || e.e1.op == EXP.bigInteger || e.e1.op == EXP.float64 || !hasSideEffect(e.e1)) { ret = e.e2; if (ret) diff --git a/compiler/src/dmd/parse.d b/compiler/src/dmd/parse.d index 49674ec5216e..9e82b49cb442 100644 --- a/compiler/src/dmd/parse.d +++ b/compiler/src/dmd/parse.d @@ -8532,6 +8532,16 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer nextToken(); break; + case TOK.int128Literal: + e = new AST.BigIntegerExp(loc, token.centvalue, AST.Type.tint128); + nextToken(); + break; + + case TOK.uns128Literal: + e = new AST.BigIntegerExp(loc, token.centvalue, AST.Type.tuns128); + nextToken(); + break; + case TOK.float32Literal: e = new AST.RealExp(loc, token.floatvalue, AST.Type.tfloat32); nextToken(); @@ -10067,6 +10077,7 @@ immutable PREC[EXP.max + 1] precedence = EXP.this_ : PREC.primary, EXP.super_ : PREC.primary, EXP.int64 : PREC.primary, + EXP.bigInteger : PREC.primary, EXP.float64 : PREC.primary, EXP.complex80 : PREC.primary, EXP.null_ : PREC.primary, diff --git a/compiler/src/dmd/statementsem.d b/compiler/src/dmd/statementsem.d index 387de3535dc3..4177612670f6 100644 --- a/compiler/src/dmd/statementsem.d +++ b/compiler/src/dmd/statementsem.d @@ -77,11 +77,13 @@ private struct CaseExpressionBox this(Expression exp) { - assert(exp.op == EXP.int64 || exp.op == EXP.string_); + assert(exp.op == EXP.int64 || exp.op == EXP.bigInteger || exp.op == EXP.string_); this.exp = exp; if (exp.isIntegerExp()) hash = hashOf(exp.toInteger()); + else if (exp.isBigIntegerExp()) + hash = hashOf(exp.isBigIntegerExp().value.lo ^ exp.isBigIntegerExp().value.hi); else hash = hashOf(exp.toStringExp().peekData()); } diff --git a/compiler/src/dmd/target.d b/compiler/src/dmd/target.d index e697db06e432..ae56d24d4d2a 100644 --- a/compiler/src/dmd/target.d +++ b/compiler/src/dmd/target.d @@ -615,6 +615,16 @@ extern (C++) struct Target if (os & Target.OS.Posix) return isX86 ? 4 : 8; break; + case TY.Tint128: + case TY.Tuns128: + // Must match _Alignof(_BitInt(128)) on each target: + // 16 on AArch64, 4 on i386 System V, 8 everywhere else + // (x86-64 SysV/Windows, riscv64, ppc64le, i686 Windows, armv7) + if (isAArch64) + return 16; + if (os & Target.OS.Posix && isX86 && !isX86_64) + return 4; + return 8; default: break; } @@ -1210,6 +1220,12 @@ extern (C++) struct Target */ return true; } + else if (isX86 && !isX86_64 && + (tns.ty == TY.Tint128 || tns.ty == TY.Tuns128)) + { + // 32-bit x86 has no 128-bit registers: return via hidden pointer + return true; + } else { //assert(sz <= 16); diff --git a/compiler/src/dmd/templatesem.d b/compiler/src/dmd/templatesem.d index 5b532a4b41cd..5424b5ee9536 100644 --- a/compiler/src/dmd/templatesem.d +++ b/compiler/src/dmd/templatesem.d @@ -594,6 +594,9 @@ private size_t expressionHash(Expression e) case EXP.int64: return cast(size_t) e.isIntegerExp().getInteger(); + case EXP.bigInteger: + return mixHash(cast(size_t)e.isBigIntegerExp().value.lo, cast(size_t)e.isBigIntegerExp().value.hi); + case EXP.float64: return CTFloat.hash(e.isRealExp().value); diff --git a/compiler/src/dmd/tokens.d b/compiler/src/dmd/tokens.d index eff7c68681fe..1841e8902cfc 100644 --- a/compiler/src/dmd/tokens.d +++ b/compiler/src/dmd/tokens.d @@ -16,6 +16,7 @@ module dmd.tokens; import core.stdc.ctype; import core.stdc.stdio; import core.stdc.string; +import dmd.common.int128 : Cent; import dmd.identifier; import dmd.location; import dmd.root.ctfloat; @@ -395,6 +396,7 @@ enum EXP : ubyte // Basic types void_, int64, + bigInteger, float64, complex80, import_, @@ -651,6 +653,7 @@ extern (C++) struct Token // Integers long intvalue; ulong unsvalue; + Cent centvalue; // Floats real_t floatvalue; diff --git a/compiler/src/dmd/typesem.d b/compiler/src/dmd/typesem.d index ce8d1bdb1456..7a6cd067b012 100644 --- a/compiler/src/dmd/typesem.d +++ b/compiler/src/dmd/typesem.d @@ -21,6 +21,7 @@ import dmd.arrayop; import dmd.arraytypes; import dmd.astcodegen; import dmd.astenums; +import dmd.common.int128 : Cent; import dmd.dcast; import dmd.dclass; import dmd.declaration; @@ -230,6 +231,8 @@ ulong sizemask(Type _this) break; case Tint64: case Tuns64: + case Tint128: + case Tuns128: m = 0xFFFFFFFFFFFFFFFFUL; break; default: @@ -3225,16 +3228,6 @@ Type typeSemantic(Type type, Loc loc, Scope* sc) Type visitType(Type t) { - // @@@DEPRECATED_2.110@@@ - // Use of `cent` and `ucent` has always been an error. - // Starting from 2.100, recommend core.int128 as a replace for the - // lack of compiler support. - if (t.ty == Tint128 || t.ty == Tuns128) - { - .error(loc, "`cent` and `ucent` types are obsolete, use `core.int128.Cent` instead"); - return error(); - } - return t.merge(); } @@ -5193,6 +5186,14 @@ Expression getProperty(Type t, Scope* scope_, Loc loc, Identifier ident, int fla return new IntegerExp(loc, i, mt); } + Expression bigIntegerValue(ulong hi, ulong lo) + { + Cent c; + c.hi = hi; + c.lo = lo; + return new BigIntegerExp(loc, c, mt); + } + Expression intValue(dinteger_t i) { return new IntegerExp(loc, i, Type.tint32); @@ -5221,6 +5222,8 @@ Expression getProperty(Type t, Scope* scope_, Loc loc, Identifier ident, int fla case Tuns32: return integerValue(uint.max); case Tint64: return integerValue(long.max); case Tuns64: return integerValue(ulong.max); + case Tint128: return bigIntegerValue(0x7FFFFFFFFFFFFFFFUL, 0xFFFFFFFFFFFFFFFFUL); + case Tuns128: return bigIntegerValue(0xFFFFFFFFFFFFFFFFUL, 0xFFFFFFFFFFFFFFFFUL); case Tbool: return integerValue(bool.max); case Tchar: return integerValue(char.max); case Twchar: return integerValue(wchar.max); @@ -5253,6 +5256,8 @@ Expression getProperty(Type t, Scope* scope_, Loc loc, Identifier ident, int fla case Tint16: return integerValue(short.min); case Tint32: return integerValue(int.min); case Tint64: return integerValue(long.min); + case Tint128: return bigIntegerValue(0x8000000000000000UL, 0); + case Tuns128: return integerValue(0); default: break; } } diff --git a/compiler/src/dmd/visitor/parsetime.d b/compiler/src/dmd/visitor/parsetime.d index 8109a599701a..8f1908709604 100644 --- a/compiler/src/dmd/visitor/parsetime.d +++ b/compiler/src/dmd/visitor/parsetime.d @@ -178,6 +178,7 @@ public: // Expressions void visit(AST.DeclarationExp e) { visit(cast(AST.Expression)e); } void visit(AST.IntegerExp e) { visit(cast(AST.Expression)e); } + void visit(AST.BigIntegerExp e) { visit(cast(AST.Expression)e); } void visit(AST.NewAnonClassExp e) { visit(cast(AST.Expression)e); } void visit(AST.IsExp e) { visit(cast(AST.Expression)e); } void visit(AST.RealExp e) { visit(cast(AST.Expression)e); } diff --git a/compiler/src/dmd/visitor/strict.d b/compiler/src/dmd/visitor/strict.d index 540c7aeaa71e..56297eb91a6d 100644 --- a/compiler/src/dmd/visitor/strict.d +++ b/compiler/src/dmd/visitor/strict.d @@ -131,6 +131,7 @@ extern(C++) class StrictVisitor(AST) : ParseTimeVisitor!AST override void visit(AST.Expression) { assert(0); } override void visit(AST.DeclarationExp) { assert(0); } override void visit(AST.IntegerExp) { assert(0); } + override void visit(AST.BigIntegerExp) { assert(0); } override void visit(AST.NewAnonClassExp) { assert(0); } override void visit(AST.IsExp) { assert(0); } override void visit(AST.RealExp) { assert(0); } diff --git a/compiler/test/fail_compilation/fail22827.d b/compiler/test/fail_compilation/fail22827.d deleted file mode 100644 index ee031ae19067..000000000000 --- a/compiler/test/fail_compilation/fail22827.d +++ /dev/null @@ -1,9 +0,0 @@ -// https://issues.dlang.org/show_bug.cgi?id=22827 -/* TEST_OUTPUT: ---- -fail_compilation/fail22827.d(8): Error: `cent` and `ucent` types are obsolete, use `core.int128.Cent` instead -fail_compilation/fail22827.d(9): Error: `cent` and `ucent` types are obsolete, use `core.int128.Cent` instead ---- -*/ -cent i22827; -ucent j22827; diff --git a/compiler/test/fail_compilation/fail254.d b/compiler/test/fail_compilation/fail254.d index b29b5907be4d..04dd40c1297c 100644 --- a/compiler/test/fail_compilation/fail254.d +++ b/compiler/test/fail_compilation/fail254.d @@ -1,11 +1,11 @@ /* TEST_OUTPUT: --- -fail_compilation/fail254.d(12): Error: integer overflow -fail_compilation/fail254.d(13): Error: integer overflow -fail_compilation/fail254.d(14): Error: integer overflow -fail_compilation/fail254.d(15): Error: integer overflow -fail_compilation/fail254.d(16): Error: integer overflow +fail_compilation/fail254.d(12): Error: cannot implicitly convert expression `cast(cent)((cast(cent)0xffffffffffffffffULL << 64) | 0xff...` of type `cent` to `ulong` +fail_compilation/fail254.d(13): Error: cannot implicitly convert expression `cast(cent)((cast(cent)0x1ULL << 64) | 0x0ULL)` of type `cent` to `ulong` +fail_compilation/fail254.d(14): Error: cannot implicitly convert expression `cast(cent)((cast(cent)0x1ULL << 64) | 0xffffffffffffffffULL)` of type `cent` to `ulong` +fail_compilation/fail254.d(15): Error: cannot implicitly convert expression `cast(cent)((cast(cent)0x7ULL << 64) | 0xffffffffffffffffULL)` of type `cent` to `ulong` +fail_compilation/fail254.d(16): Error: cannot implicitly convert expression `cast(cent)((cast(cent)0x1ULL << 64) | 0xffffffffffffULL)` of type `cent` to `ulong` --- */ diff --git a/compiler/test/fail_compilation/fail_cent.d b/compiler/test/fail_compilation/fail_cent.d new file mode 100644 index 000000000000..671ef42c3379 --- /dev/null +++ b/compiler/test/fail_compilation/fail_cent.d @@ -0,0 +1,13 @@ +void main() +{ + cent c = cast(cent)1.5; + float f = cast(float)c; +} + +/* +TEST_OUTPUT: +--- +fail_compilation/fail_cent.d(3): Error: conversion between `double` and `cent` is not supported yet +fail_compilation/fail_cent.d(4): Error: conversion between `cent` and `float` is not supported yet +--- +*/ diff --git a/compiler/test/fail_compilation/lexer23465.d b/compiler/test/fail_compilation/lexer23465.d index 052acda84840..5c0b46ffcf72 100644 --- a/compiler/test/fail_compilation/lexer23465.d +++ b/compiler/test/fail_compilation/lexer23465.d @@ -1,15 +1,14 @@ /* TEST_OUTPUT: --- -fail_compilation/lexer23465.d(21): Error: character 0x1f37a is not allowed as a continue character in an identifier -fail_compilation/lexer23465.d(22): Error: character '\' is not a valid token -fail_compilation/lexer23465.d(23): Error: octal digit expected, not `9` -fail_compilation/lexer23465.d(23): Error: octal literals larger than 7 are no longer supported -fail_compilation/lexer23465.d(24): Error: integer overflow -fail_compilation/lexer23465.d(25): Error: unterminated /+ +/ comment -fail_compilation/lexer23465.d(26): Error: found `End of File` instead of array initializer -fail_compilation/lexer23465.d(26): Error: semicolon needed to end declaration of `arr`, instead of `End of File` -fail_compilation/lexer23465.d(19): `arr` declared here +fail_compilation/lexer23465.d(20): Error: character 0x1f37a is not allowed as a continue character in an identifier +fail_compilation/lexer23465.d(21): Error: character '\' is not a valid token +fail_compilation/lexer23465.d(22): Error: octal digit expected, not `9` +fail_compilation/lexer23465.d(22): Error: octal literals larger than 7 are no longer supported +fail_compilation/lexer23465.d(24): Error: unterminated /+ +/ comment +fail_compilation/lexer23465.d(25): Error: found `End of File` instead of array initializer +fail_compilation/lexer23465.d(25): Error: semicolon needed to end declaration of `arr`, instead of `End of File` +fail_compilation/lexer23465.d(18): `arr` declared here --- */ diff --git a/compiler/test/runnable/cent_ucent.d b/compiler/test/runnable/cent_ucent.d new file mode 100644 index 000000000000..30318db6df6a --- /dev/null +++ b/compiler/test/runnable/cent_ucent.d @@ -0,0 +1,363 @@ +// 128-bit integer (cent/ucent) tests, run on both 32-bit and 64-bit x86. + +// Type properties that must hold on every target +static assert(cent.sizeof == 16); +static assert(ucent.sizeof == 16); +static assert(cent.alignof == 8); +static assert(ucent.alignof == 8); +static assert(cent.init == cast(cent)0); +static assert(ucent.init == cast(ucent)0); +static assert(__traits(isIntegral, cent)); +static assert(__traits(isIntegral, ucent)); +static assert(__traits(isUnsigned, ucent)); +static assert(!__traits(isUnsigned, cent)); +static assert(is(cent) && is(ucent)); + +void main() +{ + // ---------------- properties ---------------- + assert(cent.max > 0); + assert(cent.min < 0); + assert(cent.init == 0); + assert(ucent.max > cent.max); + assert(cent.min == cast(cent)(cast(cent)0x8000000000000000UL << 64)); + assert(cent.max == cast(cent)((cast(cent)0x7FFFFFFFFFFFFFFFUL << 64) | 0xFFFFFFFFFFFFFFFFUL)); + assert(ucent.min == 0); + assert(ucent.max == ((cast(ucent)0xFFFFFFFFFFFFFFFFUL << 64) | 0xFFFFFFFFFFFFFFFFUL)); + + // ---------------- arithmetic ---------------- + cent a = 100; + cent b = 20; + assert(a + b == 120); + assert(a - b == 80); + assert(a * b == 2000); + assert(a / b == 5); + assert(a % b == 0); + assert(a / 7 == 14); + assert(a % 7 == 2); + assert(-a == -100); + assert(~a == -101); + assert(a >> 2 == 25); + assert(a << 2 == 400); + assert(cast(ucent)a >>> 2 == 25); + + // mixed 64-bit operands + cent m = 1000L + a; + assert(m == 1100); + assert(m - 1000L == a); + assert(m * 2L == 2200); + assert(a + 1 == 101); + assert(a * 3 == 300); + + // 128-bit div/mod with a full 128-bit divisor + cent big = cast(cent)(cast(cent)0x123456789ABCDEF0UL << 64) | 0x0FEDCBA987654321UL; + cent divr = cast(cent)0x1000000000000000L; + assert(big / divr == (cast(cent)0x123456789ABCDEF0UL << 4)); + assert(big % divr == 0x0FEDCBA987654321UL); + + // div/mod by a variable 64-bit divisor + long dv = 0x1000000000000000L; + assert(big / dv == (cast(cent)0x123456789ABCDEF0UL << 4)); + assert(big % dv == 0x0FEDCBA987654321UL); + + // unsigned div/mod + ucent ub = 300; + assert(ub / 7 == 42); + assert(ub % 7 == 6); + ucent ubig = cast(ucent)(cast(ucent)0xFFFFFFFFFFFFFFFFUL << 64) | 0xFFFFFFFFFFFFFFFEUL; + assert(ubig / 2 == cast(ucent)0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFUL); + assert(ubig % 2 == 0); + + // overflow wraps mod 2^128 + ucent mx = ucent.max; + ucent ov = mx + 1; + assert(ov == 0); + + cent mn = cent.min; + assert(cast(ucent)(mn - 1) == cast(ucent)cent.max); + + // nested expressions + cent n = (a + b) * 3; + assert(n == 360); + n = (a - b) * (a + b); + assert(n == 9600); + n = a + a + a + a; + assert(n == 400); + assert((a - b) / (a / b) == 16); + + // variable shift counts + int sh = 4; + assert(a << sh == 1600); + assert(a >> sh == 6); + cent shl = cast(cent)0x5DEADBEEFCAFEBABUL << 64; + assert(shl >> 64 == cast(cent)0x5DEADBEEFCAFEBABUL); + + // compound assignment + cent cc = 10; + cc += 5; + assert(cc == 15); + cc -= 3; + assert(cc == 12); + cc *= 3; + assert(cc == 36); + cc /= 6; + assert(cc == 6); + cc %= 4; + assert(cc == 2); + cc <<= 3; + assert(cc == 16); + cc >>= 2; + assert(cc == 4); + cc &= 6; + assert(cc == 4); + cc |= 1; + assert(cc == 5); + cc ^= 5; + assert(cc == 0); + ++cc; + assert(cc == 1); + --cc; + assert(cc == 0); + cc = 7; + cc++; + assert(cc == 8); + cc--; + assert(cc == 7); + + // comparisons + assert(a > b); + assert(a >= b); + assert(b < a); + assert(b <= a); + assert(a != b); + assert(a == 100); + assert(a == 100L); + assert(a != 101); + assert(cent.max > cent.min); + assert(cent.min < cent.max); + assert(ucent.max > cent.max); + assert(cent.max > 0); + assert(cent.min < 0); + + // bitwise + cent x = 0x0F0F; + cent y = 0x00FF; + assert((x & y) == 0x000F); + assert((x | y) == 0x0FFF); + assert((x ^ y) == 0x0FF0); + assert(cast(ulong)(~x) == 0xFFFFFFFFFFFFF0F0UL); + + // 128-bit values via shifts + cent hi = cast(cent)0x5DEADBEEFCAFEBABUL << 64; + assert((hi >> 64) == cast(cent)0x5DEADBEEFCAFEBABUL); + assert((hi >>> 64) == cast(ucent)0x5DEADBEEFCAFEBABUL); + + // 128-bit literals (> 64 bits) + cent lc = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF; + assert(lc == -1); + ucent luc = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFu; + assert(luc == ucent.max); + cent lm = 170141183460469231731687303715884105727; + assert(lm == cent.max); + cent lmin = cast(cent)0x80000000000000000000000000000000; + assert(lmin == cent.min); + + // ---------------- compile-time evaluation (CTFE) ---------------- + enum cent ec = 0x123456789ABCDEF0_0FEDCBA987654321; + static assert(ec == 0x123456789ABCDEF0_0FEDCBA987654321); + static assert(ec > 0); + static assert((ec >> 64) == 0x123456789ABCDEF0); + enum cent ea = cast(cent)100 / 7; + static assert(ea == 14); + enum cent eb = cast(cent)100 % 7; + static assert(eb == 2); + enum cent em = cast(cent)0x123456789ABCDEF0_0FEDCBA987654321 * cast(cent)3; + static assert(em == cast(cent)0x123456789ABCDEF0_0FEDCBA987654321 * 3); + enum cent en = -ec; + static assert(en == -0x123456789ABCDEF0_0FEDCBA987654321); + enum cent eor = ec ^ 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF; + static assert(eor == ~ec); + static assert(cast(ulong)(ec >> 64) == 0x123456789ABCDEF0UL); + static assert(cast(ulong)ec == 0x0FEDCBA987654321UL); + static assert(cast(long)ec == cast(long)0x0FEDCBA987654321UL); + + // compile-time function on cent + static cent ctf(cent x) + { + return x * 2 + 1; + } + enum cent ef = ctf(cast(cent)21); + static assert(ef == 43); + + // ---------------- basic declaration and assignment ---------------- + cent c; + c = 5; + assert(c == 5); + assert(cast(long)c == 5); + + ucent uc; + uc = 7; + assert(uc == 7); + assert(cast(ulong)uc == 7); + + // negative values + cent neg = -5; + assert(neg == -5); + assert(cast(long)neg == -5); + assert(neg < 0); + assert(-neg == 5); + + // cast from 64-bit (sign/zero extension) + cent se = cast(cent)(-1L); + assert(se == -1); + assert(cast(long)se == -1); + + ucent ze = cast(ucent)0xFFFFFFFFFFFFFFFFUL; + assert(ze == 0xFFFFFFFFFFFFFFFFUL); + assert(cast(ulong)ze == 0xFFFFFFFFFFFFFFFFUL); + + // cast down (truncation) + assert(cast(long)(cast(cent)0x123456789ABCDEF0L) == 0x123456789ABCDEF0L); + assert(cast(ulong)(cast(ucent)0x123456789ABCDEF0UL) == 0x123456789ABCDEF0UL); + assert(cast(long)(cast(cent)0x123456789ABCDEF0_0FEDCBA987654321L) == 0x0FEDCBA987654321L); + assert(cast(long)(cast(cent)(-5L)) == -5L); + + // initialization forms + cent c2 = 42; + assert(c2 == 42); + ucent uc2 = 42; + assert(uc2 == 42); + cent c3 = cent.max; + assert(c3 == cent.max); + cent c4 = cent.min; + assert(c4 == cent.min); + + // ---------------- structs and arrays containing cent ---------------- + struct S + { + cent c; + long l; + } + static assert(S.sizeof == 24); + static assert(S.alignof == 8); + S s; + s.c = 100; + s.l = 200; + assert(s.c == 100); + assert(s.l == 200); + + cent[3] arr; + arr[0] = 1; + arr[1] = 2; + arr[2] = 3; + assert(arr[0] == 1 && arr[1] == 2 && arr[2] == 3); + assert(arr.length == 3); + + // ---------------- function params and returns ---------------- + cent ident(cent x) { return x; } + assert(ident(55) == 55); + assert(ident(cent.max) == cent.max); + assert(ident(cent.min) == cent.min); + + ucent uident(ucent x) { return x; } + assert(uident(77) == 77); + assert(uident(ucent.max) == ucent.max); + + cent add2(cent a, cent b) { return a + b; } + assert(add2(30, 12) == 42); + + cent add3(cent a, cent b, cent c) { return a + b + c; } + assert(add3(1, 2, 3) == 6); + + cent sub2(cent a, cent b) { return a - b; } + assert(sub2(100, 42) == 58); + + cent mul2(cent a, cent b) { return a * b; } + assert(mul2(6, 7) == 42); + + cent div2(cent a, cent b) { return a / b; } + assert(div2(84, 6) == 14); + assert(div2(big, divr) == (cast(cent)0x123456789ABCDEF0UL << 4)); + + cent mixed(cent a, long b) { return a + b; } + assert(mixed(100, 5) == 105); + + bool cmplt(cent a, cent b) { return a < b; } + assert(cmplt(1, 2)); + assert(!cmplt(2, 1)); + + void setit(ref cent x, long v) { x = cast(cent)v; } + cent cv = 0; + setit(cv, 999); + assert(cv == 999); + + // struct with cent passed by value + S s2 = S(1000, 2000); + S idS(S x) { return x; } + S s3 = idS(s2); + assert(s3.c == 1000 && s3.l == 2000); + + // ---------------- boolean contexts ---------------- + cent bz = 0; + assert(!bz); + if (a) + assert(a > 0); + else + assert(0); + if (!bz) + assert(1); + else + assert(0); + while (a > 0) + break; + bool truthy = cast(bool)a; + assert(truthy); + assert(!cast(bool)bz); + // short-circuit && and || with 128-bit operands + assert(a && b); + assert(a || bz); + assert(!(bz && a)); + assert(bz || a); + // conditional expression + cent chosen = (a > b) ? a : b; + assert(chosen == a); + + // ---------------- casts to smaller integers ---------------- + cent cbits = cast(cent)0x0123456789ABCDEF_0FEDCBA987654321; + assert(cast(int)cbits == cast(int)0x87654321); + assert(cast(uint)cbits == 0x87654321); + assert(cast(short)cbits == cast(short)0x4321); + assert(cast(ushort)cbits == 0x4321); + assert(cast(byte)cbits == cast(byte)0x21); + assert(cast(ubyte)cbits == 0x21); + assert(cast(long)cbits == cast(long)0x0FEDCBA987654321UL); + assert(cast(ulong)cbits == 0x0FEDCBA987654321UL); + + // ---------------- alignment (matches _Alignof(_BitInt(128))) ---------------- + // 16 on AArch64, 4 on i386 System V, 8 elsewhere + version (AArch64) + enum centAlign = 16; + else version (X86) + version (Posix) + enum centAlign = 4; + else + enum centAlign = 8; + else + enum centAlign = 8; + assert(cent.alignof == centAlign); + assert(ucent.alignof == centAlign); + assert(cent.sizeof == 16); + assert(ucent.sizeof == 16); + + // ---------------- hashing ---------------- + import core.internal.hash : hashOf; + assert(hashOf(cast(cent)5) == hashOf(cast(cent)5)); + assert(hashOf(cast(ucent)5) == hashOf(cast(ucent)5)); + assert(hashOf(cast(cent)5) != hashOf(cast(cent)6)); + + // ---------------- properties ---------------- + assert(cent.max > 0); + assert(cent.min < 0); + assert(cent.init == 0); + assert(ucent.max > cent.max); +} diff --git a/compiler/tools/gen_cpp_layout_test.py b/compiler/tools/gen_cpp_layout_test.py index 4d29c6bc429e..75e774de0924 100644 --- a/compiler/tools/gen_cpp_layout_test.py +++ b/compiler/tools/gen_cpp_layout_test.py @@ -105,7 +105,8 @@ dmd.root.array, dmd.root.bitarray, dmd.root.complex, dmd.root.ctfloat, dmd.root.filename, dmd.root.longdouble, dmd.root.optional, dmd.root.port, dmd.root.rmem, - dmd.common.charactertables, dmd.common.outbuffer;""" + dmd.common.charactertables, dmd.common.outbuffer, + dmd.common.int128;""" # Helper emitted once D_HELPER_CODE = r"""private enum hasMangled(alias T, string method, string mangled) = () { diff --git a/druntime/src/core/internal/string.d b/druntime/src/core/internal/string.d index e5abce47db83..9f3ab0cc9d54 100644 --- a/druntime/src/core/internal/string.d +++ b/druntime/src/core/internal/string.d @@ -81,6 +81,32 @@ if (radix >= 2 && radix <= 36 && return buf[i .. $]; } +static if (is(ucent)) +T[] unsignedToTempString(uint radix = 10, bool upperCase = false, T)(ucent value, return scope T[] buf) +if (radix >= 2 && radix <= 36 && + (is(T == char) || is(T == wchar) || is(T == dchar))) +{ + import core.int128 : Cent = Cent, udivmod; + + enum baseChar = upperCase ? 'A' : 'a'; + size_t i = buf.length; + + // Fast path: the value fits in 64 bits. + if (cast(ulong)(value >> 64) == 0) + return unsignedToTempString!(radix, upperCase)(cast(ulong)value, buf); + + Cent v = Cent(lo: cast(ulong)value, hi: cast(ulong)(value >> 64)); + Cent divisor = Cent(lo: radix); + do + { + Cent mod; + v = udivmod(v, divisor, mod); + uint x = cast(uint)mod.lo; + buf[--i] = cast(char)((radix <= 10 || x < 10) ? x + '0' : x - 10 + baseChar); + } while (v.lo || v.hi); + return buf[i .. $]; +} + private struct TempStringNoAlloc(ubyte N) { private char[N] _buf = void; @@ -114,6 +140,16 @@ auto unsignedToTempString(uint radix = 10)(ulong value) return result; } +static if (is(ucent)) +auto unsignedToTempString(uint radix = 10)(ucent value) +{ + // 39 decimal digits for 2^128-1, or 128 binary digits plus a sign. + enum bufferSize = radix >= 10 ? 40 : 129; + TempStringNoAlloc!bufferSize result = void; + result._len = unsignedToTempString!radix(value, result._buf).length & 0xff; + return result; +} + unittest { UnsignedStringBuf buf = void; @@ -159,6 +195,23 @@ T[] signedToTempString(uint radix = 10, bool upperCase = false, T)(long value, r return r; } +static if (is(cent)) +T[] signedToTempString(uint radix = 10, bool upperCase = false, T)(cent value, return scope T[] buf) +{ + bool neg = value < 0; + if (neg) + value = -value; + auto r = unsignedToTempString!(radix, upperCase)(cast(ucent)value, buf); + if (neg) + { + // about to do a slice without a bounds check + auto trustedSlice(return scope T[] r) @trusted { assert(r.ptr > buf.ptr); return (r.ptr-1)[0..r.length+1]; } + r = trustedSlice(r); + r[0] = '-'; + } + return r; +} + auto signedToTempString(uint radix = 10)(long value) { bool neg = value < 0; @@ -173,6 +226,21 @@ auto signedToTempString(uint radix = 10)(long value) return r; } +static if (is(cent)) +auto signedToTempString(uint radix = 10)(cent value) +{ + bool neg = value < 0; + if (neg) + value = -value; + auto r = unsignedToTempString!radix(cast(ucent)value); + if (neg) + { + r._len++; + r.get()[0] = '-'; + } + return r; +} + unittest { SignedStringBuf buf = void; diff --git a/plans/int128/1786475660431-cent-ucent-implementation.md b/plans/int128/1786475660431-cent-ucent-implementation.md new file mode 100644 index 000000000000..00720a127472 --- /dev/null +++ b/plans/int128/1786475660431-cent-ucent-implementation.md @@ -0,0 +1,173 @@ +# Plan: Implement `cent` and `ucent` (128-bit integers) in dmd + +Target: this repo (fork of dmd master ~2.108). The parser is `Parser!(AST)` in `parse.d`, instantiated with `ASTCodegen` (`dmodule.d parseModule!ASTCodegen`) — parse output is the **classic tree** (`dmd.expression` etc.) directly; `astbase.d`/`ASTBase` is a parse-only mirror family **not** in the compiler's main path and needs no changes. Platforms: **x86-64 (`-m64`, MODEL=64)** fully; **32-bit x86 (`-m32`)** for arithmetic via `core.int128` calls (see decisions). ARM/AArch64 **out of scope** (guard shared-file changes behind `I16`/`AArch64` checks). + +## Goal + +Make `cent`/`ucent` real, working integer types: declaration, assignment, literals, all arithmetic/bitwise/compare ops, casts to/from 64-bit types, struct/array/param/return usage, full constant folding and CTFE. Codegen principle (user directive): **prefer hardware instructions where they exist, otherwise use `core.int128`**. + +- On m64: inline hardware codegen for all arithmetic; hardware `DIV`/`IDIV` for 128÷64; `core.int128` calls for 128÷128 (no hardware exists). +- On m32: no 128-bit hardware exists → `core.int128` calls for all arithmetic ops (add/sub/mul/div/mod/shifts/bitwise/neg/com; comparisons via `lt`/`le` with operand swap for gt/ge; equality stays backend word-compare). Casts/moves/loads/stores stay backend-side (verify m32 plumbing). + +**`int128`/`uns128` are NOT user-facing type names** — they exist only as internal enum names (`TY.Tint128/Tuns128` in `astenums.d`, `TOK.int128/TOK.uns128` in `tokens.d`, whose `toChars` are "cent"/"ucent"). The lexer has no `int128`/`uns128` keywords and none shall be added — those spellings lex as identifiers and fail lookup naturally. No parse cases, no docs, no spec text for them. + +## Current state (verified) + +Already present and working: +- Keywords `cent`/`ucent` parse to `TypeBasic Tint128/Tuns128` (`mtype.d`); `size()` returns 16 (`typesem.d visitBasic`); integral/unsigned flags set. +- Full implicit-conversion/result matrix in `impcnvtab.d` (e.g. `cent + long → cent`, `cent + ucent → Tuns128`, `cent + float → float`). +- Mangling: `'z' + 'i'/'k'` (`mangle/basic.d`) — identical on m32/m64. +- Backend `TYcent/TYucent`: 16 bytes, **align 8**, shared with `TYdelegate = TYcent`, `TYdarray = TYucent` (`backconfig.d`). Loads/stores/moves/pairs/passing/returns (`regmask`/`allocretregs`/`FuncParamRegs_alloc` two-GPR pair; m32 stack/`OPpair` push paths in `pushParams`/`movParams`), comparisons (`cdcmp` on I64), zero-tests (`tstresult`), and **complete constant folding via `dmd.common.int128` in `evalu8.d`** already work. +- `e2ir.d` cast cases for 128-bit (`OPs64_128`/`OPu64_128`/`OP128_64`, `Lpaint`) exist but are unreachable; float↔cent disabled with `static if (0)`. +- druntime `core.int128` (both in-repo `P:\dmd\druntime` **and** the external `P:\ProjectSidero\dmd2` install used by run.sh — verified) already provides everything needed: `add`, `sub`, `mul`, `div`, `udiv`, `rem`, `urem`, `and`, `or`, `xor`, `com`, `neg`, `abs`, `shl`, `shr`, `sar`, `lt`, `le`, `ult`, `ule` (int128.d). `TypeInfo_zi : TypeInfoGeneric!cent` / `TypeInfo_zk` gated on `is(cent)` (`rt/util/typeinfo.d`); `int128_t`/`uint128_t` aliases gated on `is(ucent)` (`core/stdc/stdint.d`). +- ABI: `argtypes_sysv_x64.d` passes Tint128 as two integer classes; `argtypes_x86.d` (m32) treats it as a single unit — audit. +- Verification tooling available: `Q:\Misc Software\clang+llvm-22.1.8-x86_64-pc-windows-msvc\bin` (clang + llvm-objdump, COFF-capable). + +Blockers / gaps: +1. `typesem.d visitType` rejects all usage: "`cent` and `ucent` types are obsolete..." (the gate). +2. **No 128-bit constant representation** — `IntegerExp.value` is `dinteger_t` (64-bit); lexer caps literals at `ulong` (overflow = error); `sizemask` asserts on 128-bit (`typesem.d`); constfold and the CTFE interpreter are 64-bit only. +3. `glue/package.d totym` has no Tint128/Tuns128 case → `assert(0)`. +4. `e2ir visitInteger` → `el_long(TYcent, ...)` leaves `Vcent.hi` garbage. +5. **Backend x86-64 arithmetic is missing/wrong for 16-byte ints** (dead code today): + - `cdorth` (x86/cod2.d): 16-byte operands get `numwords == 1` → single 64-bit op. + - `cdneg`/`cdcom` (x86/cod2.d): pair sequences without REX.W on I64. + - `cdshift` (x86/cod2.d): partial; small-const loop path without REX.W; verify all 0..127 cases. + - `cdmul` (x86/cod2.d): 32-bit-only pair sequence on I64. + - `cddiv`/`cdmod` (x86/cod2.d): 16-byte path calls 64-bit CLIB helpers → wrong; needs hardware DIV for 128÷64 and `core.int128` calls for 128÷128 (backend IR has **no branches**, only `OPcall`). + - `cdshtlng` (x86/cod4.d): `OPs64_128` sign-extend missing REX.W (wrong on I64); m32 64→128 path unverified. + - `cdbswap`: `assert(sz != 16)`. + - `evalu8.d OPmsw`: result-size-keyed switch mishandles 16-byte sources. + - `cod4.d` opass/`OPnegass`: `LLONGSIZE` on I16 asserts "not implemented yet". + - `loaddata` flags-only zero-test path missing REX.W (latent). +6. `fail_compilation/fail22827.d` expects the obsolete-type error (must be replaced); `compilable/warn3882.d` has `static if (is(cent))` tests that will auto-activate. + +## Design decisions + +- **New expression type for 128-bit constants**: add `EXP.bigInteger` to the `EXP` enum (`tokens.d`) and a `BigIntegerExp` class in `expression.d` holding `dmd.common.int128.Int128` (mirrors `RealExp`; `type.ty` distinguishes cent/ucent). Do **not** widen `IntegerExp.value`. `parse.d` builds it directly (the parser emits codegen AST). +- **Alignment: 8** on x86 (both m32 and m64) — matches backend `_tyalignsize[TYcent]` (shared with delegates/darrays; changing it would break delegate ABI). Frontend `target.alignsize` special-cases Tint128/Tuns128 → 8. (Diverges from C `__int128` align 16 on x86-64 — acceptable, cent is D-only.) +- **Div/mod and m32 arithmetic via `core.int128`**: m64 → hardware everywhere except 128÷128, which calls `core.int128` (`div`/`udiv` for `/`, `rem`/`urem` for `%`). m32 → all arithmetic ops lower to `core.int128` calls (`add`/`sub`/`mul`/`div`/`udiv`/`rem`/`urem`/`and`/`or`/`xor`/`com`/`neg`/`shl`/`shr`/`sar`; gt/ge synthesized by operand swap over `lt`/`le`, gt/ge unsigned via `ult`/`ule` swap). References via new RTLSYM entries with the D-mangled symbol names. **No changes to `rt/llmath.d` or any druntime code.** +- **Literals > 64 bits in scope**: lexer produces `TOK.int128Literal`/`TOK.uns128Literal` (token enum entries already exist; lexer never emits them today) when a literal overflows `ulong`; parser builds `BigIntegerExp`. +- **Full CTFE/constfold support** via `dmd.common.int128` (a `dmd` module; backend already imports it — frontend may too). +- **float↔cent conversions out of scope**: reject at semantic with a clear "not supported" error; leave the `static if (0)` blocks in `e2ir.d` as-is. +- `.max`/`.min` implemented (needed by druntime `TypeInfoGeneric` which uses `T.max`). +- `is(cent)`/`is(ucent)` become true → druntime `TypeInfo_zi/zk` and `stdint` aliases activate on next druntime rebuild (no compiler work). + +## Implementation tasks (ordered) + +### M0 — Harness sanity +- Run `C:\Program Files\Git\bin\bash.exe compiler/src/test128/run.sh` (note: the prompt's path `compiler/srctest128int/run.sh` does not exist; the harness is `compiler/src/test128/run.sh`). Confirm build + empty `main` run works. `start.d.cg` is the expected `-vcg-ast` header output — regenerate whenever `start.d` changes. +- Extend `run.sh` with an m32 leg (pattern from `testsumtypematching/run.sh`): `dmd -m32 -run test128/start.d` (32-bit druntime/phobos come from the external dmd2 install — proven to work). + +### M1 — Types work end-to-end for trivial code +1. `typesem.d visitType`: delete the obsolete-type error. +2. `target.d alignsize`: add `Tint128/Tuns128` → 8. +3. `typesem.d sizemask`: add Tint128/Tuns128 → `~0` (any 64-bit value fits; 128→64 narrowing is rejected at type level by `implicitConvTo(Type,Type)`/`dcast castTo` — verify). +4. `typesem.d visitBasic getProperty`: `cent.max/min`, `ucent.max/min` → `BigIntegerExp` (`cent.max = 0x7FFF...`, `ucent.max = 0xFFFF...`). +5. New `BigIntegerExp`: + - `tokens.d` EXP enum: `EXP.bigInteger`; `expression.d`: class (ctor `(Loc, Int128, Type)`, `isBigIntegerExp`, `getInteger`, `syntaxCopy`, `accept`, `printExp`, `isConst` → 1, `toChars`), expClassSize table entry; `expressionsem.d Expression::toInteger/toUInteger` case (error if value doesn't fit 64 bits, else low 64). + - `dmd.common.int128`: add `string toChars(Int128)` (decimal; hex variant for diagnostics) — needed by `BigIntegerExp.toChars` and error messages. + - Mechanical: add `EXP.bigInteger` to every op-switch/visitor that handles `EXP.int64` (compile errors will point at `final switch`es): `dinterpret.d`, `optimize.d` dispatch, `ctfeexpr.d`, `e2ir.d`, `hdrgen.d`, `printast.d`, `semantic2/semantic3` expression visitors, `escape.d`, `safe.d`, `nogc.d`, `canthrow.d`, `sideeffect.d`, `mustuse.d`, `inlinecost.d`, `blockexit.d`, `dfa`, visitor mixins in `visitor/package.d`, `dcast.d` range checks, `enumsem.d` enum-value paths. + - `dcast.d` expression-level `implicitConvTo`/`getIntRange`: handle `BigIntegerExp` (64-bit-value-always-fits semantics; Int128-based fit check). +6. `dcast.d castTo`: reject float/imaginary/complex ↔ cent casts with a clear error (out of scope feature). +7. Glue/codegen plumbing: + - `glue/package.d totym`: `Tint128 → TYcent`, `Tuns128 → TYucent`. + - `backend/el.d`: `el_cent(tym_t, Int128)` (OPconst + Vcent); `e2ir visitInteger`/`visitBigInteger`: 16-byte-typed constants via `el_cent` (also covers `cent.init` = IntegerExp(0) of 128-bit type). + - `e2ir toElemCast`: verify the existing `X(Tint128,...)` cases now execute correctly (small→128 widening, `OPs64_128`/`OPu64_128`, `OP128_64`, `Lpaint` for cent↔ucent) on **both m64 and m32** (m32 `cdshtlng`/`cdlngsht`/`cdpair` paths need auditing — see M2/m32). +8. Backend x86-64 correctness (the bulk; all 16-byte paths need REX.W on I64): + - `cdshtlng` (x86/cod4.d): fix `OPs64_128` sign-extension (REX.W). + - `cdorth` (x86/cod2.d): 16-byte → `numwords = 2` with REX.W (ADD/ADC, SUB/SBB, OR, XOR, AND). + - `cdneg`/`cdcom` (x86/cod2.d): 16-byte pair sequences with REX.W. + - `cdshift` (x86/cod2.d): complete 16-byte shifts — const 0..127 (incl. 64 boundary) and variable counts; REX.W everywhere. + - `cdmul` (x86/cod2.d): 128×128→128 via MUL cross-terms (lo·lo, lo·hi, hi·lo, carry-in). + - `cddiv`/`cdmod` (x86/cod2.d): `OPremquo`/`OPdiv` with 16-byte dividend ÷ 8-byte divisor → hardware `DIV`/`IDIV` (RDX:RAX); 16÷16 → `core.int128` calls (M2). + - `cod4.d` opass paths: `OPaddass`/`OPminass`/`OPandass`/`OPorass`/`OPxorass`/`OPnegass`/postinc/postdec for 16-byte (fix the `LLONGSIZE` I16 assert). + - `evalu8.d OPmsw`: handle 16-byte source (key the case on source size). + - `loaddata` (cod1.d) flags-only zero test: REX.W. + - `cdbswap`: optional — implement 16-byte or keep assert (not reachable from D source without an intrinsic; do not advertise). +9. M1 tests in `test128/start.d`: `sizeof(cent)==16`, `alignof(cent)==8`, `cent.init == 0`, assignment from int/ulong literals, casts both directions (incl. negative → sign extension), struct/array containing cent, function params/returns (stack + registers), `cent.max/min`, `is(cent)`, `.init`, `enum cent e = 5`. Guard m32-only behavior in the test where needed (arith via calls vs inline is semantically identical — tests should pass unchanged on both). + +### M2 — `core.int128` calls (128÷128 on m64; all arithmetic on m32) +- New RTLSYM entries (follow the existing enum/`symbolz` patterns in `backend/rtlsym.d`) whose symbol names are the **D-mangled names** of `core.int128`'s `div`/`udiv`/`rem`/`urem` (m64) plus `add`/`sub`/`mul`/`and`/`or`/`xor`/`com`/`neg`/`shl`/`shr`/`sar`/`lt`/`le`/`ult`/`ule` (m32). Determine the exact mangled strings by compiling a probe program that calls `core.int128.div` etc. and inspecting the emitted symbol (llvm-objdump, see M8); hardcode them with a comment referencing `core/int128.d` (precedent: hardcoded `_Dmain`). Signatures `Cent f(Cent, Cent)` match the existing TYcent param/return machinery on m64 (two GPRs) — verify `symbolz` linkage flags produce an undecorated name. +- `e2ir`: add a cent-binop lowering helper: on m64, `div`/`mod` with 16÷16 operands → `OPcall` (`div`/`udiv` per signedness of the divisor expression, `rem`/`urem` for `%`); on m32, **all** 128-bit binops/unops → `OPcall` (`gt`/`ge` → operand-swapped `lt`/`le`; unsigned via `ult`/`ule`; equality stays backend word-compare). Constant operands still fold in `evalu8` first. +- Verify m32 call plumbing end-to-end: 16-byte stack args (aligned 8, `pushParams`/`movParams` OPpair paths), 16-byte returns on m32 (hidden sret via `allocretregs` I32 stack path), m32 casts (`OPs64_128`/`OP128_64`/`cdpair` — add what's missing, it's word-level sign/zero extension). +- No druntime edits, no external-install sync. +- Test: div/mod matrix incl. signs, division by zero (runtime trap), `%` vs `/` consistency, 128÷64 fast path vs 128÷128 helper path (force 128-bit divisor via `cast(ucent)1 << 64`), full arithmetic matrix on m32, `abs` sanity. Same `start.d` assertions run on both m64 and m32 legs. + +### M3 — 128-bit literals +- `lexer.d number()`: on ulong overflow, keep accumulating into `Int128`; emit `TOK.int128Literal` (or `uns128Literal` with `u`/`U` suffix); error only beyond 128 bits. Verify suffix handling (`u`/`U`/`L` combos) and hex/octal/binary forms. +- `parse.d`: literal tokens → `BigIntegerExp` (type `tint128`, or `tuns128` when `u`-suffixed). Rule: unsuffixed overflow literal is `cent`; `u`-suffixed is `ucent` (document in spec). +- `-vcg-ast` output (`start.d.cg`) will change — regenerate. +- Test: `cent c = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF;` (=-1), `ucent c = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFu`, decimal `170141183460469231731687303715884105727`, `-2^127`, boundary `2^127-1` vs `2^127`, hex `0x80000000000000000000000000000000`. + +### M4 — constfold (compile-time arithmetic) +- `constfold.d`: make `Neg, Com, Not, Add, Min, Mul, Div, Mod, Shl, Shr, Ushr, And, Or, Xor, Equal, Cmp, Cast` 128-aware: when operand types are Tint128/Tuns128 use `BigIntegerExp.value` + `dmd.common.int128` ops (`add/sub/mul/div/udiv/divmod/udivmod/and/or/xor/com/neg/shl/shr/sar/le/lt/ule/ult`), produce `BigIntegerExp` results. Result types from `impcnvtab` already correct. +- `Pow` (^^): support constant-folding via repeated `mul` (Int128); runtime `cent ^^ x` with non-constant exponent → clear "not supported" error for now (audit `e2ir visitPow`). +- `optimize.d` dispatch: route `EXP.bigInteger` leaves through the fold functions; the shift bound check (`size()*8 = 128`) is already correct. +- `Cast` in constfold: int↔cent via Int128 (narrowing wraps mod 2^n, widening sign/zero-extends); float↔cent stays an error. + +### M5 — CTFE (dinterpret/ctfeexpr) +- `ctfeexpr.d`: `paintTypeOntoLiteral` (BigIntegerExp), `toCtfe` (cent), `assignInPlace` (BigIntegerExp set/get), `ctfeCast` (int↔cent via Int128; float↔cent error). +- `dinterpret.d`: dispatch case for `EXP.bigInteger` (identity leaf); `interpretCommon`/`interpretCompareCommon` (constfold fns now 128-aware — the fp dispatch works); audit every `toInteger`-on-value site for cent operands (error when >64 bits — e.g. array indices, `getInteger`-driven paths). +- `expressionsem.d toInteger` write-back sites — `BigIntegerExp` case must not silently truncate: error "value does not fit in 64 bits" (only 128-aware paths use `Int128`). +- Test: `enum cent a = 0x1234... + 1;` `static assert` arithmetic/comparison, CTFE function `cent f(cent x)` called at compile time (template value param `cent`), casts in CTFE. + +### M6 — Semantic polish + regression +- Verify `importC` `__int128`/`unsigned __int128` now map to cent/ucent (`cparse.d` already routes type parsing through `integerTypeForSize(16)`; audit/replace the "not supported" errors at the expression/constant paths). Note: this is C interop, unrelated to the D names `int128`/`uns128` — those remain non-types. +- Remove/replace `compiler/test/fail_compilation/fail22827.d` (no longer an error; delete the test or convert expectations). +- Run `compilable/warn3882.d` — its `static if (is(cent))` sections should now compile and pass (validates checked-arithmetic wrappers over cent). +- Smoke-regress: compile a handful of existing `compiler/test/runnable`/`compilable` tests with the new dmd (full suite on Windows is out of scope). +- Negative tests in `test128/run.sh` (pattern from `testsumtypematching/run.sh`): float↔cent cast error; implicit `cent → long` narrowing error; runtime `^^` error. + +### M7 — Docs +- `spec/lex.dd`: 128-bit integer literal rules; remove the `$(GDEPRECATED cent)`/`ucent` markers (lines ~1034/1123). +- `spec/types.dd` basic-types table: cent/ucent no longer deprecated; note size (16) and alignment (8). Do **not** add `int128`/`uns128` as type names. +- `spec/expression.dd`: casts involving cent; `spec/abi.dd` already has `TypeCent/TypeUcent` grammar. +- `changelog/`: entry. + +### M8 — `core.int128` lowering & codegen verification (m64 + m32) — REQUIRED +Verify, not assume, that the `core.int128` calls lower correctly and codegen right on **both** x86 targets. Tooling: `Q:\Misc Software\clang+llvm-22.1.8-x86_64-pc-windows-msvc\bin` (`llvm-objdump.exe` handles COFF; `clang.exe` compiles C reference code). +1. **Mangled-name probes**: small D files calling `core.int128.{div,udiv,rem,urem,add,mul,...}`; compile `-c` with the built dmd on m64 and m32; `llvm-objdump -d` and extract the referenced symbol names; confirm they match the hardcoded RTLSYM strings exactly (any mismatch = link error, caught here first). +2. **Call-site inspection**: compile `test128` probe functions with `-c`; `llvm-objdump -d` the COFF objects and verify: + - m64: 128÷64 div/mod = single `DIV`/`IDIV` (RDX:RAX); 128÷128 = `call` to `_D4core6int128...` with the right per-signedness symbol; all other arithmetic inline with REX.W (spot-check `ADD`/`ADC` pairs, `MUL` cross-term sequence, `SHLD`/`SHRD` shifts). + - m32: every arithmetic op = `call` to the right `core.int128` symbol; 16-byte args passed on the stack (aligned), 16-byte returns via sret; casts/moves inline. +3. **Cross-check against clang**: compile C equivalents (`__int128`) with the provided clang (`-m64` and `-m32`), `llvm-objdump -d`, and compare instruction sequences as reference for add/mul/div/shift (clang uses inline hardware on m64 and libcalls on m32 — a direct structural comparison of our choices). +4. **Runtime verification**: the m64 and m32 legs of `run.sh` run the same `start.d` assertion suite (all ops on both targets); div-by-zero traps; results must be identical. +5. Record findings in the harness (comments in `run.sh` and/or a `verify128.sh` script) so regressions are re-checkable. + +## Validation plan + +- After each milestone: `C:\Program Files\Git\bin\bash.exe compiler/src/test128/run.sh` (clean build → copy to `P:\ProjectSidero\dmd2\windows\bin\dmd.exe` → `-run test128/start.d` on m64, plus the m32 leg). +- `start.d` is the accumulating assertion suite (M1→M3 sections above; identical assertions on m64 and m32); verify full 128-bit values via comparisons of cent-typed expressions (backend word-compare) and via `cast(ulong)(x >> 64)` / `cast(ulong)x` decomposition where useful. +- `run.sh` additionally runs negative-compilation checks (expect-fail cases) — follow the `testsumtypematching/run.sh` structure. +- Regenerate `start.d.cg` (the `-vcg-ast` golden file) after intended `start.d` changes. +- M8 objdump/clang verification (above) after M2 lands and again after M3-M5 (literals/CTFE don't change codegen, but re-verify before finishing). + +## Risks / audit checklist + +- **REX.W omissions** in 16-byte paths silently produce wrong code — audit every `sz == 2*REGSIZE` path in the x86 backend (`cdorth`, `cdneg`, `cdcom`, `cdshift`, `cdshtlng`, `cddiv`, `loaddata`, `cdeq`, `fixresult`, `pushParams`, `movParams`). +- **m32 call convention**: 16-byte stack args (alignment 8), sret returns (`allocretregs` I32 path), m32 cast paths (`cdshtlng`/`cdlngsht`/`cdpair`) — all unverified today; M2/M8 cover them. +- **`evalu8.d OPmsw`** 16-byte-source bug (result-size-keyed switch) — fix before relying on signed `< 0` tests of cent (`elcmp` uses `OPmsw`). +- **`el_tolong`** truncation sites (`el.d`) — audit that 128-bit values only pass through via explicit `OP128_64`. +- `cgelem eldiv`/`el64_32`/`gdag` interplay with the new `OPremquo` 16÷8 codegen — verify the `OP128_64` peeling and the div/mod recovery path. +- **Hardcoded D-mangled names** for `core.int128` calls: if druntime's attribute set ever changes, calls fail at link time (loud) — acceptable; M8 probe step keeps them pinned. +- **CTFE subtlety**: `assignInPlace`/`paintTypeOntoLiteral` and `toInteger` write-backs — silent truncation is the main danger; prefer errors. +- **DFA** (`dfa/fast/structure.d`) models Tint128 as 64-bit — accepted approximation; note as limitation. +- `warn3882.d` auto-activating cent checks may surface semantic gaps early — treat failures as feature work, not regressions. +- `enum E : cent` base types and `switch`-on-cent case matching — audit `enumsem`/case folding. + +## Out of scope + +- ARM/AArch64 backend 16-byte arithmetic (asserts remain). +- `int128`/`uns128` as D type names (never; they stay internal enum names only). +- float/imaginary/complex ↔ cent conversions (semantic error). +- `std.format`/`writeln` of cent (phobos-side work), `std.math` support. +- Runtime `cent ^^ cent` with dynamic exponent (error until a helper exists). +- m32 inline hardware arithmetic (all m32 ops libcall'd via `core.int128` by design). +- Full `compiler/test` suite automation on Windows (manual smoke instead). +- C++ interop of cent (mangling 'zi' exists; ABI for C++ `__int128` differs — document, don't implement). +- Any druntime changes (none needed: `core.int128` has every needed op; `TypeInfo_zi/zk` and `stdint` aliases are already in place). + +## Follow-up for the implementer + +- Confirm understanding of each change set as it lands (per AGENTS.md); audit generated `start.d.cg` diffs before committing to them. +- Do not commit; leave changes in the working tree for review. diff --git a/plans/int128/prompts.md b/plans/int128/prompts.md new file mode 100644 index 000000000000..15ce38a96799 --- /dev/null +++ b/plans/int128/prompts.md @@ -0,0 +1,257 @@ +## DeepSeek V4 flash + +> Plan the implementation of cent and ucent +> Run C:\Program Files\Git\bin\bash.exe compiler/srctest128int/run.sh to build and test the compiler. + +"How should full 128-bit division/modulo (128-bit divisor) be lowered? The backend IR has no branches, so a general 128/128 division cannot +be generated inline. The 64-bit-on-32-bit precedent is runtime helpers (__ULDIV__ etc. in druntime rt/llmath.d). Note: run.sh links against +the external P:\ProjectSidero\dmd2 install, so helpers must be added to that install's druntime too (or the harness switched to this repo's +druntime)." +> Use core.int128 for the runtime helpers, but use hardware where available + +"Should integer literals larger than 64 bits (e.g. 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF) be supported by the lexer/parser? +TOK.int128Literal/TOK.uns128Literal already exist in the token enum but the lexer never emits them (overflow is currently an error)." +> In scope (Recommended) + +"How much compile-time evaluation (constfold + CTFE interpreter) support should be in scope? The interpreter stores all scalar values in +64-bit IntegerExp.value, so 128-bit CTFE needs a 128-bit constant representation (e.g. an Int128 field on IntegerExp) plus 128-aware +constfold (dmd.common.int128 already provides add/sub/mul/div/shifts/comparisons) and interpreter touchpoints." +> Full support. +> Use a new expression type instead of IntegerExp. Like BigIntegerExp that supports 128bit. + +> int128/uns128 are not to be added +> don't change rt.llmath just call core.int128 its all ready to go +> rename CentExp to BigIntegerExp + +> you will need to verify that core.int128 is correctly being lowered to as well codegen'ing right for both 32 and 64bit x86. +> here is the location of clang with objdump: Q:\Misc Software\clang+llvm-22.1.8-x86_64-pc-windows-msvc\bin + +"How far should 32-bit x86 (m32) support go in this effort? The harness (run.sh) builds and runs MODEL=64 only, though the +testsumtypematching run.sh shows an m32 leg works against the external dmd2 install. The x86 backend's 16-byte arithmetic paths ( +cdorth/cdmul/cdshift/etc.) are written for 64-bit-on-32-bit and would need extensive rework for 16-byte operands on m32 — but core.int128 +has every operation needed (add/sub/mul/shl/shr/sar/and/or/xor/com/neg/le/lt/ule/ult/eq)." +> prefer hardware instruction where exists, otherwise use core.int128 + +> You have access to clang, cross-compile (i.e. linux) but don't link object files, to determine the calling convention for 128 bit +> integers. +> Use `_BitInt(128)` and unsigned `_BitInt(128)` as the types. + +> Add changelog. + +> Turn start.d and fail_cent.d into proper tests in the testsuite + +> Also the 32bit tests seem to be incomplete. + +> The test file shouldn't be x86 specific, a static if on size_t.sizeof should tell you what you need to know for it. + +> ..\dmd\visitor\parsetime.d(181): error : no property `BigIntegerExp` for type +`dmd.astbase.ASTBase` [D:\a\1\s\compiler\src\vcbuild\dmd.vcxproj] +> ..\dmd\astbase.d(63): error : template instance `dmd.visitor.parsetime.ParseTimeVisitor!(ASTBase)` error +> instantiating [D:\a\1\s\compiler\src\vcbuild\dmd.vcxproj] +> ..\dmd\astbase.d(2706): error : forward reference to inferred return type of function call ` +> function () [D:\a\1\s\compiler\src\vcbuild\dmd.vcxproj] + +> core.exception.AssertError@..\dmd\glue\e2ir.d(4612): Assertion failure +> D:\a\1\s\generated\Windows\RelWithAsserts\Win32\dmd.exe -conf= -Isrc -Iimport -w -de -preview=dip1000 -preview=fieldwise -m32 +> -preview=dtorfields -g -debug -version=CoreUnittest -unittest -checkaction=context -of../generated/windows/debug/32/unittest/test_runner.exe +> src/test_runner.d src/object.d src/ +__builtins_msvc.d src/core/atomic.d src/core/attribute.d src/core/bitop.d src/core/builtins.d src/core/checkedint.d src/core/cpuid.d +src/core/demangle.d src/core/exception.d src/core/factory.d src/core/int128.d src/core/interpolation.d src/core/lifetime.d src/core/math.d +src/core/memory.d src/core/runtime.d src/core/simd.d src/core/time.d src/core/vararg.d src/core/volatile.d src/core/gc/config.d +src/core/gc/gcinterface.d src/core/gc/registry.d src/core/internal/abort.d src/core/internal/atomic.d src/core/internal/attributes.d +src/core/internal/cast_.d src/core/internal/convert.d src/core/internal/dassert.d src/core/internal/destruction.d +src/core/internal/entrypoint.d src/core/internal/execinfo.d src/core/internal/hash.d src/core/internal/moving.d src/core/internal/newaa.d +src/core/internal/parseoptions.d src/core/internal/postblit.d src/core/internal/profile_gc.d src/core/internal/qsort.d +src/core/internal/spinlock.d src/core/internal/string.d src/core/internal/switch_.d src/core/internal/traits.d src/core/internal/utf.d +> src/core/internal/lifetime.d src/core/internal/array/appending.d src/core/internal/array/arrayassign.d src/core/internal/array/comparison.d +> src/core/internal/array/construction.d src/core/internal/array/equality.d src/core/internal/array/casting.d +> src/core/internal/array/capacity.d src/core/internal/array/concatenation.d src/core/internal/array/duplication.d +> src/core/internal/array/operations.d src/core/internal/array/utils.d src/core/internal/backtrace/dwarf.d src/core/internal/backtrace/elf.d +> src/core/internal/backtrace/libunwind.d src/core/internal/backtrace/macho.d src/core/internal/backtrace/unwind.d +> src/core/internal/container/array.d src/core/internal/container/common.d src/core/internal/container/hashtab.d +> src/core/internal/container/treap.d src/core/internal/elf/dl.d src/core/internal/elf/io.d src/core/internal/gc/bits.d +> src/core/internal/gc/blkcache.d src/core/internal/gc/blockmeta.d src/core/internal/gc/os.d src/core/internal/gc/pooltable.d +> src/core/internal/gc/proxy.d src/core/internal/gc/impl/conservative/gc.d src/core/internal/gc/impl/manual/gc.d +> src/core/internal/gc/impl/proto/gc.d src/core/internal/util/array.d src/core/internal/util/math.d src/core/internal/vararg/aapcs64.d +> src/core/internal/vararg/sysv_x64.d src/core/internal/vararg/gnu.d src/core/stdc/assert_.d src/core/stdc/stdatomic.d src/core/stdc/complex.d +> src/core/stdc/config.d src/core/stdc/ctype.d src/core/stdc/errno.d src/core/stdc/fenv.d src/core/stdc/float_.d src/core/stdc/inttypes.d +> src/core/stdc/limits.d src/core/stdc/locale.d src/core/stdc/math.d src/core/stdc/signal.d src/core/stdc/stdarg.d src/core/stdc/stddef.d +> src/core/stdc/stdint.d src/core/stdc/stdio.d src/core/stdc/stdlib.d src/core/stdc/string.d src/core/stdc/tgmath.d src/core/stdc/time.d +> src/core/stdc/wchar_.d src/core/stdc/wctype.d src/core/stdcpp/allocator.d src/core/stdcpp/array.d src/core/stdcpp/exception.d +> src/core/stdcpp/memory.d src/core/stdcpp/new_.d src/core/stdcpp/string.d src/core/stdcpp/string_view.d src/core/stdcpp/typeinfo.d +> src/core/stdcpp/type_traits.d src/core/stdcpp/utility.d src/core/stdcpp/vector.d src/core/stdcpp/xutility.d src/core/sync/barrier.d +> src/core/sync/condition.d src/core/sync/config.d src/core/sync/exception.d src/core/sync/event.d src/core/sync/mutex.d +> src/core/sync/rwmutex.d src/core/sync/semaphore.d src/core/sys/bionic/err.d src/core/sys/bionic/fcntl.d src/core/sys/bionic/stdlib.d +> src/core/sys/bionic/string.d src/core/sys/bionic/unistd.d src/core/sys/darwin/crt_externs.d src/core/sys/darwin/dlfcn.d +> src/core/sys/darwin/err.d src/core/sys/darwin/execinfo.d src/core/sys/darwin/fcntl.d src/core/sys/darwin/ifaddrs.d +> src/core/sys/darwin/pthread.d src/core/sys/darwin/stdlib.d src/core/sys/darwin/string.d src/core/sys/darwin/mach/dyld.d +> src/core/sys/darwin/mach/getsect.d src/core/sys/darwin/mach/kern_return.d src/core/sys/darwin/mach/loader.d src/core/sys/darwin/mach/nlist.d +> src/core/sys/darwin/mach/port.d src/core/sys/darwin/mach/semaphore.d src/core/sys/darwin/mach/stab.d src/core/sys/darwin/mach/thread_act.d +> src/core/sys/darwin/netinet/in_.d src/core/sys/darwin/sys/cdefs.d src/core/sys/darwin/sys/event.d src/core/sys/darwin/sys/mman.d +> src/core/sys/darwin/sys/sysctl.d src/core/sys/freebsd/dlfcn.d src/core/sys/freebsd/err.d src/core/sys/freebsd/execinfo.d +> src/core/sys/freebsd/ifaddrs.d src/core/sys/freebsd/mqueue.d src/core/sys/freebsd/pthread_np.d src/core/sys/freebsd/stdlib.d +> src/core/sys/freebsd/string.d src/core/sys/freebsd/time.d src/core/sys/freebsd/unistd.d src/core/sys/freebsd/net/if_.d +> src/core/sys/freebsd/net/if_dl.d src/core/sys/freebsd/netinet/in_.d src/core/sys/freebsd/sys/_bitset.d +src/core/sys/freebsd/sys/_cpuset.d src/core/sys/freebsd/sys/cdefs.d src/core/sys/freebsd/sys/elf_common.d src/core/sys/freebsd/sys/elf.d +src/core/sys/freebsd/sys/elf32.d src/core/sys/freebsd/sys/elf64.d src/core/sys/freebsd/sys/event.d src/core/sys/freebsd/sys/link_elf.d +src/core/sys/freebsd/sys/mman.d src/core/sys/freebsd/sys/mount.d src/core/sys/freebsd/sys/socket.d src/core/sys/freebsd/sys/sysctl.d +src/core/sys/freebsd/sys/types.d src/core/sys/dragonflybsd/dlfcn.d src/core/sys/dragonflybsd/err.d src/core/sys/dragonflybsd/execinfo.d +src/core/sys/dragonflybsd/netinet/in_.d src/core/sys/dragonflybsd/pthread_np.d src/core/sys/dragonflybsd/stdlib.d +src/core/sys/dragonflybsd/string.d src/core/sys/dragonflybsd/time.d src/core/sys/dragonflybsd/sys/_bitset.d +src/core/sys/dragonflybsd/sys/_cpuset.d src/core/sys/dragonflybsd/sys/cdefs.d src/core/sys/dragonflybsd/sys/elf.d +src/core/sys/dragonflybsd/sys/elf32.d src/core/sys/dragonflybsd/sys/elf64.d src/core/sys/dragonflybsd/sys/elf_common.d +src/core/sys/dragonflybsd/sys/event.d src/core/sys/dragonflybsd/sys/link_elf.d src/core/sys/dragonflybsd/sys/mman.d +src/core/sys/dragonflybsd/sys/socket.d src/core/sys/dragonflybsd/sys/sysctl.d src/core/sys/elf/package.d src/core/sys/linux/config.d +src/core/sys/linux/dlfcn.d src/core/sys/linux/elf.d src/core/sys/linux/epoll.d src/core/sys/linux/err.d src/core/sys/linux/errno.d +src/core/sys/linux/execinfo.d src/core/sys/linux/fcntl.d src/core/sys/linux/fs.d src/core/sys/linux/ifaddrs.d src/core/sys/linux/io_uring.d +src/core/sys/linux/link.d src/core/sys/linux/perf_event.d src/core/sys/linux/sched.d src/core/sys/linux/stdio.d src/core/sys/linux/string.d +src/core/sys/linux/syscall.d src/core/sys/linux/termios.d src/core/sys/linux/time.d src/core/sys/linux/timerfd.d src/core/sys/linux/tipc.d +src/core/sys/linux/unistd.d src/core/sys/linux/linux/if_arp.d src/core/sys/linux/linux/if_packet.d src/core/sys/linux/netinet/in_.d +src/core/sys/linux/netinet/tcp.d src/core/sys/linux/sys/auxv.d src/core/sys/linux/sys/eventfd.d src/core/sys/linux/sys/file.d +src/core/sys/linux/sys/inotify.d src/core/sys/linux/sys/mman.d src/core/sys/linux/sys/mount.d src/core/sys/linux/sys/signalfd.d +src/core/sys/linux/sys/socket.d src/core/sys/linux/sys/syscall.d src/core/sys/linux/sys/sysinfo.d src/core/sys/linux/sys/timerfd.d +src/core/sys/linux/sys/xattr.d src/core/sys/linux/sys/time.d src/core/sys/linux/sys/prctl.d src/core/sys/netbsd/dlfcn.d +src/core/sys/netbsd/err.d src/core/sys/netbsd/execinfo.d src/core/sys/netbsd/stdlib.d src/core/sys/netbsd/string.d +src/core/sys/netbsd/time.d src/core/sys/netbsd/sys/elf.d src/core/sys/netbsd/sys/elf32.d src/core/sys/netbsd/sys/elf64.d +src/core/sys/netbsd/sys/elf_common.d src/core/sys/netbsd/sys/event.d src/core/sys/netbsd/sys/featuretest.d +src/core/sys/netbsd/sys/link_elf.d src/core/sys/netbsd/sys/mman.d src/core/sys/netbsd/sys/sysctl.d src/core/sys/openbsd/dlfcn.d +src/core/sys/openbsd/err.d src/core/sys/openbsd/execinfo.d src/core/sys/openbsd/pthread_np.d src/core/sys/openbsd/pwd.d +src/core/sys/openbsd/stdlib.d src/core/sys/openbsd/string.d src/core/sys/openbsd/time.d src/core/sys/openbsd/unistd.d +src/core/sys/openbsd/sys/cdefs.d src/core/sys/openbsd/sys/elf.d src/core/sys/openbsd/sys/elf32.d src/core/sys/openbsd/sys/elf64.d +src/core/sys/openbsd/sys/elf_common.d src/core/sys/openbsd/sys/link_elf.d src/core/sys/openbsd/sys/mman.d src/core/sys/openbsd/sys/sysctl.d +src/core/sys/posix/arpa/inet.d src/core/sys/posix/aio.d src/core/sys/posix/config.d src/core/sys/posix/dirent.d src/core/sys/posix/dlfcn.d +src/core/sys/posix/endian.d src/core/sys/posix/fcntl.d src/core/sys/posix/grp.d src/core/sys/posix/iconv.d src/core/sys/posix/inttypes.d +src/core/sys/posix/libgen.d src/core/sys/posix/locale.d src/core/sys/posix/mqueue.d src/core/sys/posix/netdb.d src/core/sys/posix/poll.d +src/core/sys/posix/pthread.d src/core/sys/posix/pwd.d src/core/sys/posix/sched.d src/core/sys/posix/semaphore.d src/core/sys/posix/setjmp.d +src/core/sys/posix/signal.d src/core/sys/posix/spawn.d src/core/sys/posix/stdio.d src/core/sys/posix/stdlib.d src/core/sys/posix/string.d +src/core/sys/posix/strings.d src/core/sys/posix/syslog.d src/core/sys/posix/termios.d src/core/sys/posix/time.d +src/core/sys/posix/ucontext.d src/core/sys/posix/unistd.d src/core/sys/posix/utime.d src/core/sys/posix/net/if_.d +src/core/sys/posix/netinet/in_.d src/core/sys/posix/netinet/tcp.d src/core/sys/posix/stdc/time.d src/core/sys/posix/sys/filio.d +> src/core/sys/posix/sys/ioccom.d src/core/sys/posix/sys/ioctl.d src/core/sys/posix/sys/ipc.d src/core/sys/posix/sys/mman.d +> src/core/sys/posix/sys/msg.d src/core/sys/posix/sys/resource.d src/core/sys/posix/sys/select.d src/core/sys/posix/sys/shm.d +> src/core/sys/posix/sys/socket.d src/core/sys/posix/sys/stat.d src/core/sys/posix/sys/statvfs.d src/core/sys/posix/sys/time.d +> src/core/sys/posix/sys/ttycom.d src/core/sys/posix/sys/types.d src/core/sys/posix/sys/uio.d src/core/sys/posix/sys/un.d +> src/core/sys/posix/sys/utsname.d src/core/sys/posix/sys/wait.d src/core/sys/solaris/dlfcn.d src/core/sys/solaris/elf.d +> src/core/sys/solaris/err.d src/core/sys/solaris/execinfo.d src/core/sys/solaris/libelf.d src/core/sys/solaris/link.d +> src/core/sys/solaris/stdlib.d src/core/sys/solaris/thread.d src/core/sys/solaris/time.d src/core/sys/solaris/sys/elf.d +> src/core/sys/solaris/sys/elf_386.d src/core/sys/solaris/sys/elf_amd64.d src/core/sys/solaris/sys/elf_notes.d +> src/core/sys/solaris/sys/elf_SPARC.d src/core/sys/solaris/sys/elftypes.d src/core/sys/solaris/sys/link.d src/core/sys/solaris/sys/priocntl.d +> src/core/sys/solaris/sys/procfs.d src/core/sys/solaris/sys/procset.d src/core/sys/solaris/sys/regset.d src/core/sys/solaris/sys/types.d +> src/core/sys/windows/accctrl.d src/core/sys/windows/aclapi.d src/core/sys/windows/aclui.d src/core/sys/windows/basetsd.d +> src/core/sys/windows/basetyps.d src/core/sys/windows/bcrypt.d src/core/sys/windows/cderr.d src/core/sys/windows/cguid.d +> src/core/sys/windows/com.d src/core/sys/windows/comcat.d src/core/sys/windows/commctrl.d src/core/sys/windows/commdlg.d +> src/core/sys/windows/core.d src/core/sys/windows/cpl.d src/core/sys/windows/cplext.d src/core/sys/windows/custcntl.d +> src/core/sys/windows/dbghelp.d src/core/sys/windows/dbghelp_types.d src/core/sys/windows/dbt.d src/core/sys/windows/dde.d +> src/core/sys/windows/ddeml.d src/core/sys/windows/dhcpcsdk.d src/core/sys/windows/dlgs.d src/core/sys/windows/dll.d +> src/core/sys/windows/docobj.d src/core/sys/windows/errorrep.d src/core/sys/windows/exdisp.d src/core/sys/windows/exdispid.d +> src/core/sys/windows/httpext.d src/core/sys/windows/idispids.d src/core/sys/windows/imagehlp.d src/core/sys/windows/imm.d +> src/core/sys/windows/intshcut.d src/core/sys/windows/ipexport.d src/core/sys/windows/iphlpapi.d src/core/sys/windows/ipifcons.d +> src/core/sys/windows/iprtrmib.d src/core/sys/windows/iptypes.d src/core/sys/windows/isguids.d src/core/sys/windows/lm.d +> src/core/sys/windows/lmaccess.d src/core/sys/windows/lmalert.d src/core/sys/windows/lmapibuf.d src/core/sys/windows/lmat.d +> src/core/sys/windows/lmaudit.d src/core/sys/windows/lmbrowsr.d src/core/sys/windows/lmchdev.d src/core/sys/windows/lmconfig.d +> src/core/sys/windows/lmcons.d src/core/sys/windows/lmerr.d src/core/sys/windows/lmerrlog.d src/core/sys/windows/lmmsg.d +> src/core/sys/windows/lmremutl.d src/core/sys/windows/lmrepl.d src/core/sys/windows/lmserver.d src/core/sys/windows/lmshare.d +> src/core/sys/windows/lmsname.d src/core/sys/windows/lmstats.d src/core/sys/windows/lmsvc.d src/core/sys/windows/lmuse.d +> src/core/sys/windows/lmuseflg.d src/core/sys/windows/lmwksta.d src/core/sys/windows/lzexpand.d src/core/sys/windows/mapi.d +> src/core/sys/windows/mciavi.d src/core/sys/windows/mcx.d src/core/sys/windows/mgmtapi.d src/core/sys/windows/mmsystem.d +> src/core/sys/windows/msacm.d src/core/sys/windows/mshtml.d src/core/sys/windows/mswsock.d src/core/sys/windows/nb30.d +> src/core/sys/windows/ncrypt.d src/core/sys/windows/nddeapi.d src/core/sys/windows/nspapi.d src/core/sys/windows/ntdef.d +> src/core/sys/windows/ntdll.d src/core/sys/windows/ntldap.d src/core/sys/windows/ntsecapi.d src/core/sys/windows/ntsecpkg.d +> src/core/sys/windows/oaidl.d src/core/sys/windows/objbase.d src/core/sys/windows/objfwd.d src/core/sys/windows/objidl.d +> src/core/sys/windows/objsafe.d src/core/sys/windows/ocidl.d src/core/sys/windows/odbcinst.d src/core/sys/windows/ole.d +> src/core/sys/windows/ole2.d src/core/sys/windows/ole2ver.d src/core/sys/windows/oleacc.d src/core/sys/windows/oleauto.d +> src/core/sys/windows/olectl.d src/core/sys/windows/olectlid.d src/core/sys/windows/oledlg.d src/core/sys/windows/oleidl.d +> src/core/sys/windows/pbt.d src/core/sys/windows/powrprof.d src/core/sys/windows/prsht.d src/core/sys/windows/psapi.d +> src/core/sys/windows/rapi.d src/core/sys/windows/ras.d src/core/sys/windows/rasdlg.d src/core/sys/windows/raserror.d +> src/core/sys/windows/rassapi.d src/core/sys/windows/reason.d src/core/sys/windows/regstr.d src/core/sys/windows/richedit.d +> src/core/sys/windows/richole.d src/core/sys/windows/rpc.d src/core/sys/windows/rpcdce.d src/core/sys/windows/rpcdce2.d +> src/core/sys/windows/rpcdcep.d src/core/sys/windows/rpcndr.d src/core/sys/windows/rpcnsi.d src/core/sys/windows/rpcnsip.d +> src/core/sys/windows/rpcnterr.d src/core/sys/windows/schannel.d src/core/sys/windows/sdkddkver.d src/core/sys/windows/secext.d +> src/core/sys/windows/security.d src/core/sys/windows/servprov.d src/core/sys/windows/setupapi.d src/core/sys/windows/shellapi.d +> src/core/sys/windows/shldisp.d src/core/sys/windows/shlguid.d src/core/sys/windows/shlobj.d src/core/sys/windows/shlwapi.d +> src/core/sys/windows/snmp.d src/core/sys/windows/sql.d src/core/sys/windows/sqlext.d src/core/sys/windows/sqltypes.d +> src/core/sys/windows/sqlucode.d src/core/sys/windows/sspi.d src/core/sys/windows/stacktrace.d src/core/sys/windows/stat.d +> src/core/sys/windows/stdc/malloc.d src/core/sys/windows/stdc/time.d src/core/sys/windows/subauth.d src/core/sys/windows/threadaux.d +> src/core/sys/windows/tlhelp32.d src/core/sys/windows/tmschema.d src/core/sys/windows/unknwn.d src/core/sys/windows/uuid.d +> src/core/sys/windows/vfw.d src/core/sys/windows/w32api.d src/core/sys/windows/winbase.d src/core/sys/windows/winber.d +> src/core/sys/windows/wincon.d src/core/sys/windows/wincrypt.d src/core/sys/windows/windef.d src/core/sys/windows/windows.d +> src/core/sys/windows/winerror.d src/core/sys/windows/wingdi.d src/core/sys/windows/winhttp.d src/core/sys/windows/wininet.d +> src/core/sys/windows/winioctl.d src/core/sys/windows/winldap.d src/core/sys/windows/winnetwk.d src/core/sys/windows/winnls.d +> src/core/sys/windows/winnt.d src/core/sys/windows/winperf.d src/core/sys/windows/winreg.d src/core/sys/windows/winsock2.d +> src/core/sys/windows/winspool.d src/core/sys/windows/winsvc.d src/core/sys/windows/winuser.d src/core/sys/windows/winver.d +> src/core/sys/windows/wtsapi32.d src/core/sys/windows/wtypes.d src/core/thread/fiber/base.d src/core/thread/fiber/package.d +> src/core/thread/types.d src/core/thread/threadgroup.d src/core/thread/threadbase.d src/core/thread/osthread.d src/core/thread/posix_impl.d +> src/core/thread/windows_impl.d src/core/thread/context.d src/core/thread/package.d src/rt/aApply.d src/rt/aApplyR.d src/rt/alloca.d +> src/rt/arraycat.d src/rt/cmath2.d src/rt/config.d src/rt/cover.d src/rt/critical_.d src/rt/deh.d src/rt/deh_win32.d src/rt/deh_win64_posix.d +> src/rt/dmain2.d src/rt/dwarfeh.d src/rt/ehalloc.d src/rt/invariant_.d src/rt/lifetime.d src/rt/llmath.d src/rt/memory.d src/rt/memset.d +> src/rt/minfo.d src/rt/monitor_.d src/rt/msvc.d src/rt/msvc_math.d src/rt/profilegc.d src/rt/sections.d src/rt/sections_darwin_64.d +> src/rt/sections_elf_shared.d src/rt/sections_osx_x86.d src/rt/sections_osx_64.d src/rt/sections_solaris.d src/rt/sections_win64.d +> src/rt/tlsgc.d src/rt/trace.d src/rt/tracegc.d src/rt/util/typeinfo.d src/rt/util/utility.d src/etc/valgrind/valgrind.d +> src/etc/linux/memoryerror.d ../generated/windows/debug/32/errno_c.obj -defaultlib= user32.lib + +> core.exception.AssertError@src\core\checkedint.d(142): -1 != -1 + +> -fail_compilation\fail254.d(12): Error: integer overflow +> -fail_compilation\fail254.d(13): Error: integer overflow +> -fail_compilation\fail254.d(14): Error: integer overflow +> -fail_compilation\fail254.d(15): Error: integer overflow +> -fail_compilation\fail254.d(16): Error: integer overflow +> +fail_compilation\fail254.d(12): Error: cannot implicitly convert expression +`cast(cent)((cast(cent)0xffffffffffffffffULL << 64) | 0xff...` of type `cent` to `ulong` +> +fail_compilation\fail254.d(13): Error: cannot implicitly convert expression `cast(cent)((cast(cent)0x1ULL << 64) | 0x0ULL)` of type +`cent` to `ulong` +> +fail_compilation\fail254.d(14): Error: cannot implicitly convert expression +`cast(cent)((cast(cent)0x1ULL << 64) | 0xffffffffffffffffULL)` of type `cent` to `ulong` +> +fail_compilation\fail254.d(15): Error: cannot implicitly convert expression +`cast(cent)((cast(cent)0x7ULL << 64) | 0xffffffffffffffffULL)` of type `cent` to `ulong` +> +fail_compilation\fail254.d(16): Error: cannot implicitly convert expression `cast(cent)((cast(cent)0x1ULL << 64) | 0xffffffffffffULL)` of +> type `cent` to `ulong` +> -fail_compilation\lexer23465.d(24): Error: integer overflow + +> object.Error@(0): Illegal Instruction +> make[1]: *** [Makefile:496: ../generated/windows/release/32/unittest/core/checkedint] Error 1 + +> Your fix doesn't seem right, if it works for a delegate which is the same size, why isn't it working for cent? + +> Review last commit, update C++ header files. + +> core.exception.ArrayIndexError@src/dmd/backend/x86/cod4.d(3942): index [524303] is out of bounds for array of length 256 +> ---------------- +> ??:? onArrayIndexError [0x5931a5be] +> ??:? _d_arraybounds_indexp [0x592fc05a] +> src/dmd/backend/x86/cod4.d:3942 nothrow @trusted void dmd.backend.x86.cod4.cdshtlng(ref dmd.backend.code.CGstate, ref dmd.backend.codebuilder.CodeBuilder, dmd.backend.el.elem*, ref ulong) [0x592b343a] +> src/dmd/backend/x86/cgcod.d:2888 nothrow @trusted void dmd.backend.x86.cgcod.codelem(ref dmd.backend.code.CGstate, ref dmd.backend.codebuilder.CodeBuilder, dmd.backend.el.elem*, ref ulong, uint) [0x59260c95] +> src/dmd/backend/x86/cod2.d:2594 nothrow @trusted void dmd.backend.x86.cod2.cdcond(ref dmd.backend.code.CGstate, ref dmd.backend.codebuilder.CodeBuilder, dmd.backend.el.elem*, ref ulong) [0x592809c8] +> src/dmd/backend/x86/cgcod.d:2888 nothrow @trusted void dmd.backend.x86.cgcod.codelem(ref dmd.backend.code.CGstate, ref dmd.backend.codebuilder.CodeBuilder, dmd.backend.el.elem*, ref ulong, uint) [0x59260c95] +> src/dmd/backend/x86/cgcod.d:3042 nothrow @trusted void dmd.backend.x86.cgcod.scodelem(ref dmd.backend.code.CGstate, ref dmd.backend.codebuilder.CodeBuilder, dmd.backend.el.elem*, ref ulong, ulong, bool) [0x592612df] +> src/dmd/backend/x86/cod4.d:749 nothrow @trusted void dmd.backend.x86.cod4.cdeq(ref dmd.backend.code.CGstate, ref dmd.backend.codebuilder.CodeBuilder, dmd.backend.el.elem*, ref ulong) [0x592a53bb] +> src/dmd/backend/x86/cgcod.d:2888 nothrow @trusted void dmd.backend.x86.cgcod.codelem(ref dmd.backend.code.CGstate, ref dmd.backend.codebuilder.CodeBuilder, dmd.backend.el.elem*, ref ulong, uint) [0x59260c95] +> src/dmd/backend/x86/cod2.d:2621 nothrow @trusted void dmd.backend.x86.cod2.cdcomma(ref dmd.backend.code.CGstate, ref dmd.backend.codebuilder.CodeBuilder, dmd.backend.el.elem*, ref ulong) [0x59280bcd] +> src/dmd/backend/x86/cgcod.d:2888 nothrow @trusted void dmd.backend.x86.cgcod.codelem(ref dmd.backend.code.CGstate, ref dmd.backend.codebuilder.CodeBuilder, dmd.backend.el.elem*, ref ulong, uint) [0x59260c95] +> src/dmd/backend/cgen.d:199 nothrow @trusted void dmd.backend.cgen.gencodelem(ref dmd.backend.codebuilder.CodeBuilder, dmd.backend.el.elem*, ref ulong, bool) [0x591ee59a] +> src/dmd/backend/x86/cod3.d:1180 nothrow @trusted void dmd.backend.x86.cod3.outblkexitcode(ref dmd.backend.code.CGstate, ref dmd.backend.codebuilder.CodeBuilder, dmd.backend.cc.block*, ref int, const(dmd.backend.cc.FL)*, dmd.backend.symbol.Symbol**, const(ulong)) [0x5928e350] +> src/dmd/backend/x86/cgcod.d:1474 nothrow @trusted void dmd.backend.x86.cgcod.blcodgen(ref dmd.backend.code.CGstate, dmd.backend.cc.block*) [0x5925d66b] +> src/dmd/backend/x86/cgcod.d:223 nothrow @trusted void dmd.backend.x86.cgcod.codgenx(ref dmd.backend.code.CGstate, dmd.backend.symbol.Symbol*) [0x5925a187] +> src/dmd/backend/x86/cgcod.d:84 nothrow @trusted void dmd.backend.x86.cgcod.codgen(dmd.backend.symbol.Symbol*) [0x59259b04] +> src/dmd/backend/dout.d:1029 nothrow @trusted void dmd.backend.dout.writefunc2(dmd.backend.symbol.Symbol*, ref dmd.backend.go.GlobalOptimizer, ref dmd.backend.blockopt.BlockOpt) [0x591bd250] +> src/dmd/backend/dout.d:850 nothrow @trusted void dmd.backend.dout.writefunc(dmd.backend.symbol.Symbol*) [0x591bca6b] +> src/dmd/glue/package.d:987 void dmd.glue.FuncDeclaration_toObjFile(dmd.func.FuncDeclaration, bool) [0x5911d17a] + +> Are you sure about this fix? It wasn't required before cent. + +> Double check alignment by cross compiling using clang. +> ```c +> #include +> #include +> +> int main() { +> printf("%d\n", alignof(_BitInt(128))); +> return 0; +> } +> ``` + +> Q:\Misc Software\clang+llvm-22.1.8-x86_64-pc-windows-msvc\bin + +> review last commit and update alignment + +> cent/ucent must match _BitInt(128) for alignment diff --git a/spec/lex.dd b/spec/lex.dd index 8475cd412261..531eb1587399 100644 --- a/spec/lex.dd +++ b/spec/lex.dd @@ -1031,7 +1031,7 @@ $(MULTICOLS 4, $(LINK2 expression.html#CastExpression, $(D cast)) $(LINK2 statement.html#TryStatement, $(D catch)) $(GDEPRECATED $(LINK2 type.html, $(D cdouble))) - $(GDEPRECATED $(LINK2 type.html, $(D cent))) + $(LINK2 type.html, $(D cent)) $(GDEPRECATED $(LINK2 type.html, $(D cfloat))) $(LINK2 type.html, $(D char)) $(LINK2 class.html, $(D class)) @@ -1120,7 +1120,7 @@ $(MULTICOLS 4, $(LINK2 type.html#Typeof, $(D typeof)) $(LINK2 type.html, $(D ubyte)) - $(GDEPRECATED $(LINK2 type.html, $(D ucent))) + $(LINK2 type.html, $(D ucent)) $(LINK2 type.html, $(D uint)) $(LINK2 type.html, $(D ulong)) $(LINK2 struct.html, $(D union)) diff --git a/spec/type.dd b/spec/type.dd index 378d94d99027..858a595b6852 100644 --- a/spec/type.dd +++ b/spec/type.dd @@ -109,8 +109,8 @@ $(H2 $(LEGACY_LNAME2 Basic Data Types, basic-data-types, Basic Data Types)) $(TROW $(D uint), $(D 0u), unsigned 32 bits) $(TROW $(D long), $(D 0L), signed 64 bits) $(TROW $(D ulong), $(D 0uL), unsigned 64 bits) - $(TROW $(GDEPRECATED $(D cent)), $(D 0), signed 128 bits) - $(TROW $(GDEPRECATED $(D ucent)), $(D 0u), unsigned 128 bits) + $(TROW $(D cent), $(D 0), signed 128 bits) + $(TROW $(D ucent), $(D 0u), unsigned 128 bits) $(TROW $(D float), $(D float.nan), 32 bit floating point) $(TROW $(D double), $(D double.nan), 64 bit floating point) $(TROW $(D real), $(D real.nan), largest floating point size available) @@ -134,8 +134,10 @@ $(H2 $(LEGACY_LNAME2 Basic Data Types, basic-data-types, Basic Data Types)) type supported by the x86 FPU. ) - $(NOTE 128-bit integer types `cent` and `ucent` - $(DDSUBLINK deprecate, 128-bit integer types, have been deprecated).) + $(NOTE The 128-bit integer types `cent` and `ucent` are supported on 64-bit x86 targets. + Operations are implemented with inline hardware instructions where available and fall + back to `core.int128` calls otherwise. Conversions to and from floating point types + are not yet supported.) $(NOTE Complex and imaginary types `ifloat`, `idouble`, `ireal`, `cfloat`, `cdouble`, and `creal` $(DDSUBLINK deprecate, Imaginary and complex types,