December 08, 2018
On Saturday, 8 December 2018 at 07:08:54 UTC, Stefan Koch wrote:
> On Saturday, 8 December 2018 at 04:06:11 UTC, Joakim wrote:
>> On Saturday, 8 December 2018 at 00:32:58 UTC, Stefan Koch wrote:
>>> Hi All,
>>>
>>> I have short but good news, non-default class-constructors work now.
>>> (this was tricky because of my unorthodox vtbl layout).
>>>
>>> And another exciting news item, newCTFE is going to support cross-platform floating-point as soon as I got around to re-implementing, the qemu fork of softfloat-2.
>>
>> What do you mean by "cross-platform floating-point," using the same implementation everywhere without calling _any_ libc/arch intrinsics?
>
> Yes that is what I mean. It has always been the plan, but until recently,
> I saw no way to actually realize it.
>
>>> That'll result in a compile-time testable floating point implementation ;) Pretty cool, huh.
>>
>> How does that differ from the current implementation?
>
> Currently it relies on the host fpu.  or rather the hosts float implementation.
> Which I intent to replace with a pure software solution, which operates independently
> from the host float math.
> And can therefore be made to behave like the targets (more or less, it's emulation).

OK, got that from the first question, but I was referring to what you called "compile-time testable?" I didn't understand what using a fully soft-float implementation added in that regard.

>> Btw, do you know some way to get the current compile-time real's precision in D code?
>
> this _may_ work:
> static immutable ctRealPerc = () { return real.mant_dig; } ();

No, as kinke says, it doesn't work with ldc.

On Saturday, 8 December 2018 at 15:42:43 UTC, H. S. Teoh wrote:
> On Sat, Dec 08, 2018 at 04:06:11AM +0000, Joakim via Digitalmars-d wrote: [...]
>> Btw, do you know some way to get the current compile-time real's precision in D code? It would be useful to avoid static asserts from overflowing when the runtime real's precision exceeds the compile-time real's precision, which doesn't happen with dmd but can with ldc:
>> 
>> https://github.com/dlang/phobos/pull/6790#discussion_r238633160
>
> Doesn't real.dig give the (approximate) precision of real? Or is that
> hard-coded in the frontend?

Just so we're clear, the issue is what the precision of reals are when running CTFE code to initialize a constant, as opposed to the precision of the real at runtime. For dmd, this doesn't really matter since it only supports x86/x64 chips, but ldc uses the host's real at compile-time, which can obviously vary from the target. That's why I mentioned to you in another thread that some stdlib math tests assert when cross-compiling from x64 to Android/AArch64, because of that mismatch in host->target precision for reals.

real.dig has the same problem as real.mant_dig above, it only gives the target's precision. Compiling this snippet with ldc, `void main() { pragma(msg, "precision is ", real.dig);}`, I get 18 when compiling natively on linux/x64, but 33 when cross-compiling to Android/AArch64, ie it always gives the target real's precision.
December 09, 2018
On Sat, Dec 08, 2018 at 05:19:59PM +0000, Joakim via Digitalmars-d wrote: [...]
> On Saturday, 8 December 2018 at 15:42:43 UTC, H. S. Teoh wrote:
> > On Sat, Dec 08, 2018 at 04:06:11AM +0000, Joakim via Digitalmars-d wrote: [...]
> > > Btw, do you know some way to get the current compile-time real's precision in D code? It would be useful to avoid static asserts from overflowing when the runtime real's precision exceeds the compile-time real's precision, which doesn't happen with dmd but can with ldc:
> > > 
> > > https://github.com/dlang/phobos/pull/6790#discussion_r238633160
> > 
> > Doesn't real.dig give the (approximate) precision of real? Or is
> > that hard-coded in the frontend?
> 
> Just so we're clear, the issue is what the precision of reals are when running CTFE code to initialize a constant, as opposed to the precision of the real at runtime. For dmd, this doesn't really matter since it only supports x86/x64 chips, but ldc uses the host's real at compile-time, which can obviously vary from the target. That's why I mentioned to you in another thread that some stdlib math tests assert when cross-compiling from x64 to Android/AArch64, because of that mismatch in host->target precision for reals.

Ahhh, so *that's* the reason for the failure.

But I'd think that's more than just a floating-point issue; wouldn't CTFE in general be problematic if there's a mismatch between host built-in types vs. target built-in types when cross-compiling? I suppose D's fixed-sized types help a lot in this regard (imagine the mess if `int` varies in size across host/target archs -- the kind of mess C/C++ have to deal with). But `real` is a fly in the ointment.

I'd imagine difference in endianness would be another cause of problems in cross-compilation, though for the most part typical D code wouldn't be affected by this at least as far as CTFE is concerned (since casting / reinterpretation of unions is not currently allowed).


> real.dig has the same problem as real.mant_dig above, it only gives the target's precision. Compiling this snippet with ldc, `void main() { pragma(msg, "precision is ", real.dig);}`, I get 18 when compiling natively on linux/x64, but 33 when cross-compiling to Android/AArch64, ie it always gives the target real's precision.

Which it should, since any code that depends on real.dig is ostensibly expecting the target arch's precision, not the host's which may change depending on which host you use to compile it.

Looks like we'll somehow need a way to tell the CTFE engine what the target arch should be, and when something doesn't match the host type, it should run a software emulation instead of using the host's `real`.


T

-- 
Claiming that your operating system is the best in the world because more people use it is like saying McDonalds makes the best food in the world. -- Carl B. Constantine
December 09, 2018
On Sunday, 9 December 2018 at 14:48:31 UTC, H. S. Teoh wrote:
> On Sat, Dec 08, 2018 at 05:19:59PM +0000, Joakim via Digitalmars-d wrote: [...]
>> [...]
>
> Ahhh, so *that's* the reason for the failure.
>
> [...]

size_t theoretically also but here practically there are only two sizes to take care of 4 and 8.

>
> [...]

December 09, 2018
On Sunday, 9 December 2018 at 14:48:31 UTC, H. S. Teoh wrote:
> On Sat, Dec 08, 2018 at 05:19:59PM +0000, Joakim via Digitalmars-d wrote: [...]
>> On Saturday, 8 December 2018 at 15:42:43 UTC, H. S. Teoh wrote:
>> > On Sat, Dec 08, 2018 at 04:06:11AM +0000, Joakim via Digitalmars-d wrote: [...]
>> > > Btw, do you know some way to get the current compile-time real's precision in D code? It would be useful to avoid static asserts from overflowing when the runtime real's precision exceeds the compile-time real's precision, which doesn't happen with dmd but can with ldc:
>> > > 
>> > > https://github.com/dlang/phobos/pull/6790#discussion_r238633160
>> > 
>> > Doesn't real.dig give the (approximate) precision of real? Or is
>> > that hard-coded in the frontend?
>> 
>> Just so we're clear, the issue is what the precision of reals are when running CTFE code to initialize a constant, as opposed to the precision of the real at runtime. For dmd, this doesn't really matter since it only supports x86/x64 chips, but ldc uses the host's real at compile-time, which can obviously vary from the target. That's why I mentioned to you in another thread that some stdlib math tests assert when cross-compiling from x64 to Android/AArch64, because of that mismatch in host->target precision for reals.
>
> Ahhh, so *that's* the reason for the failure.

Specifically, real.max overflows when it tries to evaluate that expression, as it tries to stick the 128-bit real.max in the 80-bit x87 real and overflows to Inf, tripping the static assert.

> But I'd think that's more than just a floating-point issue; wouldn't CTFE in general be problematic if there's a mismatch between host built-in types vs. target built-in types when cross-compiling? I suppose D's fixed-sized types help a lot in this regard (imagine the mess if `int` varies in size across host/target archs -- the kind of mess C/C++ have to deal with). But `real` is a fly in the ointment.

Yes, this is called out in the spec:

"Functions executed via CTFE can give different results from run time in the following scenarios:

- floating point computations may be done at a higher precision than run time"
https://dlang.org/spec/function.html#interpretation

Of course, that was likely written before we started supporting 64-bit and 128-bit reals, so it was probably assumed that 80-bit x87 reals would always be used at compile-time and at worst would be _more_ precise. That's not the case with ldc anymore, since ldc running on an ARM host will do CTFE at 64-bit precision and 80-bit on an x64 host isn't enough when cross-compiling to 128-bit AArch64 reals.

> I'd imagine difference in endianness would be another cause of problems in cross-compilation, though for the most part typical D code wouldn't be affected by this at least as far as CTFE is concerned (since casting / reinterpretation of unions is not currently allowed).

Unsure.

>> real.dig has the same problem as real.mant_dig above, it only gives the target's precision. Compiling this snippet with ldc, `void main() { pragma(msg, "precision is ", real.dig);}`, I get 18 when compiling natively on linux/x64, but 33 when cross-compiling to Android/AArch64, ie it always gives the target real's precision.
>
> Which it should, since any code that depends on real.dig is ostensibly expecting the target arch's precision, not the host's which may change depending on which host you use to compile it.

I tried various other ways before, like checking an enum's precision or inside a __ctfe block, but they all give the target's precision, as kinke says, even though they're running at compile-time.

> Looks like we'll somehow need a way to tell the CTFE engine what the target arch should be, and when something doesn't match the host type, it should run a software emulation instead of using the host's `real`.

Yes, Iain says gdc already uses such multi-precision soft-float, and kinke mentioned earlier in this thread that he'd like to do the same with ldc. The reason I brought this issue up is that Stefan initially mentioned doing something similar for dmd.
December 09, 2018
On Saturday, 8 December 2018 at 15:56:46 UTC, kinke wrote:
> On Saturday, 8 December 2018 at 07:08:54 UTC, Stefan Koch wrote:
>> On Saturday, 8 December 2018 at 04:06:11 UTC, Joakim wrote:
>>> Btw, do you know some way to get the current compile-time real's precision in D code?
>>
>> this _may_ work:
>> static immutable ctRealPerc = () { return real.mant_dig; } ();
>
> Nope, these FP type properties represent the target type's. There's just no way of knowing the compile-time precision at the moment, at least for LDC (DMD is x86 only and currently always uses a x87 `real_t`). For LDC, it's the host `real` precision, except for MSVC hosts, where it uses x87 (Rainer's custom implementation in dmd.root.longdouble).

This infallibly determines the precision used for calculations involving `real` in CTFE:

----
enum ctfeRealExponentBits =
() {
    real f = 2;
    foreach (i; 1 .. 2000)
    {
        real g = f * f;
        if (g == f)
            return i;
        f = g;
    }
    assert(0, "algorithm failure");
}();

enum ctfeMantissaBits =
() {
    real a = 1;
    real b = 0.5;
    int bits = 0;
    while (a + b != a)
    {
        bits = bits + 1;
        b = b / 2;
    }
    // Result matches what would be reported by mant_dig
    // (1 greater than the actual number of mantissa bits
    // except for 80-bit extended precision).
    return bits + 1;
}();
----
December 09, 2018
On Sunday, 9 December 2018 at 16:32:49 UTC, Nathan S. wrote:
> On Saturday, 8 December 2018 at 15:56:46 UTC, kinke wrote:
>> [...]
>
> This infallibly determines the precision used for calculations involving `real` in CTFE:
>
> ----
> enum ctfeRealExponentBits =
> () {
>     real f = 2;
>     foreach (i; 1 .. 2000)
>     {
>         real g = f * f;
>         if (g == f)
>             return i;
>         f = g;
>     }
>     assert(0, "algorithm failure");
> }();
>
> enum ctfeMantissaBits =
> () {
>     real a = 1;
>     real b = 0.5;
>     int bits = 0;
>     while (a + b != a)
>     {
>         bits = bits + 1;
>         b = b / 2;
>     }
>     // Result matches what would be reported by mant_dig
>     // (1 greater than the actual number of mantissa bits
>     // except for 80-bit extended precision).
>     return bits + 1;
> }();
> ----

Heh, yes, checking the precision manually with a compile-time while loop works: he meant there's no "FP type properties" that will easily give it to you.
December 09, 2018
On Sunday, 9 December 2018 at 16:32:49 UTC, Nathan S. wrote:
> This infallibly determines the precision used for calculations involving `real` in CTFE:

Compiled code shouldn't have to know, it just needs to be sufficiently high. - I'm not so sure about 'infallibly', there's a theoretical chance for the super-duper IBM doubledouble format being used too.
December 09, 2018
On Sun, Dec 09, 2018 at 03:16:16PM +0000, Patrick Schluter via Digitalmars-d wrote:
> On Sunday, 9 December 2018 at 14:48:31 UTC, H. S. Teoh wrote:
> > On Sat, Dec 08, 2018 at 05:19:59PM +0000, Joakim via Digitalmars-d wrote: [...]
> > > [...]
> > 
> > Ahhh, so *that's* the reason for the failure.
> > 
> > [...]
> 
> size_t theoretically also but here practically there are only two sizes to take care of 4 and 8.
[...]

Since size_t is an alias to a concrete fixed-sized int type, I don't think it would actually be a problem. The alias AFAIK depends only on the version identifiers, which are always set for the target arch, so size_t will always alias to the correct target type even when run in CTFE on a host with a different size_t.


T

-- 
Latin's a dead language, as dead as can be; it killed off all the Romans, and now it's killing me! -- Schoolboy
December 10, 2018
On Sunday, 9 December 2018 at 15:23:30 UTC, Joakim wrote:
> On Sunday, 9 December 2018 at 14:48:31 UTC, H. S. Teoh wrote:
>> Looks like we'll somehow need a way to tell the CTFE engine what the target arch should be, and when something doesn't match the host type, it should run a software emulation instead of using the host's `real`.
>
> Yes, Iain says gdc already uses such multi-precision soft-float, and kinke mentioned earlier in this thread that he'd like to do the same with ldc. The reason I brought this issue up is that Stefan initially mentioned doing something similar for dmd.

My point being that it's not just the CTFE engine that needs to support compile-time reals (real_t) of at least target `real` precision. FP literals have to be parsed by the front-end, need to be printable to string (template instantiations, header generation etc.) and stored, and this is currently taken care of by the `real_t` type and the dmd.root.ctfloat module.
So ideally, newCTFE would just keep on relying on that infrastructure, just like the current interpreter (+ dmd.builtin module) does, instead of trying to reinvent the wheel.
December 10, 2018
On Monday, 10 December 2018 at 11:04:47 UTC, kinke wrote:
> On Sunday, 9 December 2018 at 15:23:30 UTC, Joakim wrote:
>> On Sunday, 9 December 2018 at 14:48:31 UTC, H. S. Teoh wrote:
>>> Looks like we'll somehow need a way to tell the CTFE engine what the target arch should be, and when something doesn't match the host type, it should run a software emulation instead of using the host's `real`.
>>
>> Yes, Iain says gdc already uses such multi-precision soft-float, and kinke mentioned earlier in this thread that he'd like to do the same with ldc. The reason I brought this issue up is that Stefan initially mentioned doing something similar for dmd.
>
> My point being that it's not just the CTFE engine that needs to support compile-time reals (real_t) of at least target `real` precision. FP literals have to be parsed by the front-end, need to be printable to string (template instantiations, header generation etc.) and stored, and this is currently taken care of by the `real_t` type and the dmd.root.ctfloat module.
> So ideally, newCTFE would just keep on relying on that infrastructure, just like the current interpreter (+ dmd.builtin module) does, instead of trying to reinvent the wheel.

ctfloat depends on the C runtime, and therefore it's implementation dependent.
I'd rather go with an implementation which I have verified myself.
And where I can give meaningful statements about what it can, and can't support.

I'd love to not re-invent the weel; But we don't have a wheel.