Skip to content

reimplement longdouble in D#8169

Merged
dlang-bot merged 11 commits intodlang:masterfrom
rainers:longdouble_in_d
May 27, 2018
Merged

reimplement longdouble in D#8169
dlang-bot merged 11 commits intodlang:masterfrom
rainers:longdouble_in_d

Conversation

@rainers
Copy link
Member

@rainers rainers commented Apr 14, 2018

This gets rid of the split implementation in C and D and asm.

Unfortunately, I had to introduce another name for the software implementation, because we now have to cover the case where D code is compiled with dmd using native reals, but needs to supply symbols for the backend that is compiled with VC.

The respective type name aliases are now:
VisualC backend compiler: targ_ldouble typedefs longdouble typedefs longdouble_soft
other C backend compilers: targ_ldouble typedefs longdouble typedefs long double

dmd frontend: real_t aliases longdouble aliases real
LDC/win frontend: real_t aliases longdouble aliases longdouble_soft

The GDC frontend can replace longdouble_soft with a complete software emulation.

@dlang-bot
Copy link
Contributor

Thanks for your pull request, @rainers!

Bugzilla references

Your PR doesn't reference any Bugzilla issue.

If your PR contains non-trivial changes, please reference a Bugzilla issue or create a manual changelog.

Testing this PR locally

If you don't have a local development environment setup, you can use Digger to test this PR:

dub fetch digger
dub run digger -- build "master + dmd#8169"

*/

// 80 bit floating point value implementation for LDC compiler targetting MSVC
// 80-bit floating point value implementation if the C/D compiler do not support them natively
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

s/do/does/

{
nothrow:
nothrow @nogc:
ulong mantissa = 0xC000000000000001UL; // default to snan
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I gave up on snan a while ago. Nothing supports it, and it just causes problems when people compare nan bits. Use qnan instead.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, updated.

longdouble opMul(longdouble rhs) const { return this.ld_mul(rhs); }
longdouble opDiv(longdouble rhs) const { return this.ld_div(rhs); }
longdouble opMod(longdouble rhs) const { return this.ld_mod(rhs); }
bool opEquals(const longdouble_soft rhs) const { return this.ld_cmpe(rhs); }
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hmm, on this rhs is const, but not so for the other functions.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed const for consistency as the argumets are passed by value anyway. I abuse the non-constness in some operations to avoid RVO ignoring the asm modifications.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

RVO ignoring the asm modifications.

Please file a bug report about that.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please file a bug report about that.

https://issues.dlang.org/show_bug.cgi?id=18758

version(CRuntime_Microsoft):
extern(C++):
nothrow:
@nogc:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Investigate using pure and @safe as much as practical.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added pure and @safe, but it has pure @trusted has to be verified manually on all the asm.

@ibuclaw
Copy link
Member

ibuclaw commented Apr 14, 2018

The GDC frontend can replace longdouble_soft with a complete software emulation.

Or I just ignore it completely as neither ctfloat.d nor longdouble.d is shareable code. :-)

https://github.com/ibuclaw/GDC/blob/633751e8146d4533a0a3c17f2440055094008259/gcc/d/ddmd/root/ctfloat.d

By all means I suppose the implementation could be submitted here.

@WalterBright
Copy link
Member

I used the "Paranoia" test suite to make the DMC soft implementation of floating point arithmetic correct. It lives up to its name, you have to get it right for Paranoia to pass!

http://www.netlib.org/paranoia/paranoia.c

@rainers
Copy link
Member Author

rainers commented Apr 14, 2018

I used the "Paranoia" test suite to make the DMC soft implementation of floating point arithmetic correct. It lives up to its name, you have to get it right for Paranoia to pass!

The Paranoia test only seems to fail for the overflow detection (using SIGFPE signals), but I guess we don't need that.

@WalterBright
Copy link
Member

The Paranoia test only seems to fail for the overflow detection (using SIGFPE signals), but I guess we don't need that.

It's great news that Paranoia is working! Perhaps add it to the test suite? Maintenance on longdouble may accidentally break it. Did you translate it to D?

I know we don't need the signals, but is there a way to get it to pass anyway?

@ibuclaw
Copy link
Member

ibuclaw commented Apr 15, 2018

The Paranoia test only seems to fail for the overflow detection (using SIGFPE signals), but I guess we don't need that.

It's great news that Paranoia is working! Perhaps add it to the test suite?

Are you testing compile time or runtime here?

@rainers
Copy link
Member Author

rainers commented Apr 15, 2018

It's great news that Paranoia is working! Perhaps add it to the test suite? Maintenance on longdouble may accidentally break it. Did you translate it to D?

No, I just added these definitions instead of the float/double versions:

#include "dmd/src/dmd/root/longdouble.h"
#define FLOAT longdouble
#define FABS(x) fabsl(x)
#define FLOOR(x) floor((double)(x))
#define LOG(x) log((double)(x))
#define POW(x,y) pow((double)(x),(double)(y))
#define SQRT(x) sqrtl(x)

and adjusted a couple of explicit conversions from double constants.

I know we don't need the signals, but is there a way to get it to pass anyway?

Not sure. Maybe it just needs enabling approriate FPU exceptions...

@rainers
Copy link
Member Author

rainers commented Apr 15, 2018

Are you testing compile time or runtime here?

paranoia tests the C++ interface linked with the D implementation of longdouble, so I'd consider it a test of the compile time evaluation of dmd, but not some D operator overloads.

@rainers
Copy link
Member Author

rainers commented Apr 15, 2018

Unfortunately the tests fail because LDC translates all types of FPU instructions to 64-bit, so

    fld real ptr [EAX];
    fld double ptr [EAX];
    fld extended ptr [EAX];

are all the same.
@kinke I see some unexpected translation for extended to double in asm-x86.h for MSVC. I'd rather expect it to be taken literally, but double to be used for real types. Or is this an LLVM limitation?

@ibuclaw
Copy link
Member

ibuclaw commented Apr 15, 2018

paranoia tests the C++ interface linked with the D implementation of longdouble, so I'd consider it a test of the compile time evaluation of dmd, but not some D operator overloads.

So it could be part of the c++ test source then. Or it could be converted into a ctfe test for the testsuite.

In any case, @WalterBright no chance of supporting all the paranoia tests because there is no fpu at compile time.

@WalterBright
Copy link
Member

In any case, @WalterBright no chance of supporting all the paranoia tests because there is no fpu at compile time.

The point of paranoia is to test the "no fpu" soft float implementation. I'm not understanding your comment.

@ibuclaw
Copy link
Member

ibuclaw commented Apr 15, 2018

Why would it raise a signal if floating point is emulated?

@kinke
Copy link
Contributor

kinke commented Apr 15, 2018

I see some unexpected translation for extended to double in asm-x86.h for MSVC. I'd rather expect it to be taken literally, but double to be used for real types. Or is this an LLVM limitation?

That's a bug I introduced a while back, thanks for reporting => ldc-developers/ldc#2653.

@rainers
Copy link
Member Author

rainers commented Apr 15, 2018

I know we don't need the signals, but is there a way to get it to pass anyway?

Not sure. Maybe it just needs enabling approriate FPU exceptions...

I was wrong about the actual problem due to the rather confusing output: the actual "DEFECT" was an inaccuracy of the pow function, so pretty expected because we are just using the double version.

I then noticed that the FPU precision hasn't been changed at all because I missed to call initFPU() :-/

With that I get 1 "failure" and 3 "defects". Quite ok when compared to dmc (1 failure, 1 serious defect, 6 defects) and gcc (2 failures, 1 serious defect, 6 defects). The latter is rather old, but version 4.9.1 is the latest I found on my system.

Edit: all issues seem to be related to pow and sqrt.

@rainers
Copy link
Member Author

rainers commented Apr 15, 2018

That's a bug I introduced a while back, thanks for reporting => ldc-developers/ldc#2653.

Thanks for the quick action. While trying to workaround I noticed

  • .bytes or db not available to emit the opcodes myself. Would have been nice...
  • EIP not available as a register (RIP is ok). Is this deliberate?

@kinke
Copy link
Contributor

kinke commented Apr 15, 2018

EIP not available as a register (RIP is ok).

Thx => ldc-developers/ldc#2654

.bytes or db not available to emit the opcodes myself. Would have been nice...

I'm not too familiar with the DMD-style inline asm parser code, so I don't know if that would be possible.

@rainers
Copy link
Member Author

rainers commented Apr 15, 2018

I'm not too familiar with the DMD-style inline asm parser code, so I don't know if that would be possible.

It seems it is almost there, but currently commented out: https://github.com/ldc-developers/ldc/blob/master/gen/asm-x86.h#L3888

Thx => ldc-developers/ldc#2654

Thanks.

@kinke
Copy link
Contributor

kinke commented Apr 15, 2018

[In LLVM asm, loading an 80-bit value would look something like this: import ldc.llvmasm; __asm("fldt $0", "*m,~{st}", ptr);.]

@WalterBright
Copy link
Member

Why would it raise a signal if floating point is emulated?

Because an emulator should emulate the real thing.

@WalterBright
Copy link
Member

all issues seem to be related to pow and sqrt.

This is good news, it means the underlying arithmetic is sound. It would be a good idea to make a bugzilla issue of all the paranoia failures. It'd still be grand to convert paranoia to D!

@JinShil
Copy link
Contributor

JinShil commented May 17, 2018

What's the status with this? @rainers, are you still working on this, or is it ready to go in your opinion? Are there any outstanding issues? What are they?

@rainers
Copy link
Member Author

rainers commented May 17, 2018

What's the status with this?

I think it's ready to go. A possible complication is that the backend now depends on D code which might make linking a bit more troublesome on non-Windows-platforms (though not used there for now).

@wilzbach
Copy link
Contributor

A possible complication is that the backend now depends on D code which might make linking a bit more troublesome on non-Windows-platforms (though not used there for now).

We are about to start converting the backend to D soon hopefully anyhow, so this shouldn't be a big problem.

version(D_InlineAsm_X86_64)
{
// set precision to 64-bit mantissa and rounding control to nearest
asm nothrow @nogc pure
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nothrow and @nogc are already declared above. Is this redundant? If so, I suggest choosing a coding style: either add the attributes explicitly at each declaration, or group them under one attribution, but don't mix the two styles.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not confident enough for @trusted?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nothrow and @nogc are already declared above. Is this redundant?

These attributes have to be repeated for each asm statement. otherwise the compiler errors out.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems like a bug.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Before dmd 2.067, you couldn't write asm in pure/safe code: https://dlang.org/changelog/2.067.0.html#asm-attributes

Copy link
Contributor

@JinShil JinShil left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's quite beyond me to verify the semantics of this code, but the craftsmanship and translation looks good. Nice work!

else version(D_InlineAsm_X86)
{
// set precision to 64-bit mantissa and rounding control to nearest
asm nothrow @nogc
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not pure like the one above?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

pure is wrong above, the FPU control word is changed. I'll remove that.

@JinShil
Copy link
Contributor

JinShil commented May 19, 2018

@adamdruppe I understand you're quite good with Intel ASM. Care to lend your expertise here, perhaps even verifying safety and purity?

{
version(AsmX86)
{
asm nothrow @nogc pure @trusted
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I probably wouldn't call this pure since it explicitly is clearing flags; if this function were optimized out it would probably be wrong most times.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm way out of my league here but if we're talking about floating point flags the spec says [1]:

As a concession to practicality, a pure function can also:

  • read and write the floating point exception flags
  • read and write the floating point mode flags, as long as those flags are restored to their initial state upon function entry

[1] https://dlang.org/spec/function.html#pure-functions

@adamdruppe
Copy link
Contributor

So I'm not in love with making mov instruction wrappers pure... in isolation, it seems silly, but in context it seems to work. Maaaybe slap private on it just to make it clear this is a bit of internal cheating/convenience, though tbh I am a bit meh on it and wouldn't hold up over this little feeling.

Resetting flag registers while calling it pure rubs me more the wrong way though. I guess it would pass the test if they are pushed and popped inside a pure function, but I don't think they consistently are.

For the @trusted parts.... so it looks right, but I gotta warn my fpu asm experience is fairly limited so I might be missing something too.... just it looks right eyeballing it.

@rainers
Copy link
Member Author

rainers commented May 19, 2018

Thanks @adamdruppe for review. Indeed, ld_clearfpu() is not pure, so the asm inside should also not be marked that way. I've also added private to the asm mixins.

BTW: there is little new about the asm, it was already there with ldfpu.asm (for Win64) and longdouble.d (for Win32), but sure, merging them might have introduced regressions. Appveyor tests both versions, though.

@RazvanN7
Copy link
Contributor

@WalterBright I think we are ready to merge to this. Do you have anything to add?

@RazvanN7 RazvanN7 added Merge:72h no objection -> merge The PR will be merged if there are no objections raised. Merge:auto-merge labels May 24, 2018
@dlang-bot dlang-bot merged commit c470ea6 into dlang:master May 27, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Merge:auto-merge Merge:72h no objection -> merge The PR will be merged if there are no objections raised.

Projects

None yet

Development

Successfully merging this pull request may close these issues.