Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix Issue 22082 - Static float array comparison now lowered to __equals #13320

Closed
wants to merge 6 commits into from

Conversation

ryuukk
Copy link
Contributor

@ryuukk ryuukk commented Nov 19, 2021

No description provided.

@dlang-bot
Copy link
Contributor

Thanks for your pull request and interest in making D better, @ryuukk! We are looking forward to reviewing it, and you should be hearing from a maintainer soon.
Please verify that your PR follows this checklist:

  • My PR is fully covered with tests (you can see the coverage diff by visiting the details link of the codecov check)
  • My PR is as minimal as possible (smaller, focused PRs are easier to review than big ones)
  • I have provided a detailed rationale explaining my changes
  • New or modified functions have Ddoc comments (with Params: and Returns:)

Please see CONTRIBUTING.md for more information.


If you have addressed all reviews or aren't sure how to proceed, don't hesitate to ping us with a simple comment.

Bugzilla references

Auto-close Bugzilla Severity Description
22082 normal static float array comparison not available in betterC

Testing this PR locally

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

dub run digger -- build "master + dmd#13320"

Solve this:

```D
struct VertexB
{
    float[2] pos;
}

struct Hold
{
    VertexB v;
}

extern(C) int main()
{
    Hold[1] h_a; 
    Hold[1] h_b;
    assert(h_a != h_b);
}

struct VertexA
{
float pos;
Copy link
Contributor

Choose a reason for hiding this comment

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

All floating point types should be tested, not only float.

Copy link
Contributor Author

@ryuukk ryuukk Nov 19, 2021

Choose a reason for hiding this comment

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

i tried to update the test but i'm getting the following:

test.obj : error LNK2019: unresolved external symbol _memset80 referenced in function _D4test__TQiTeZQnFNaNbNiNfZv
test.exe : fatal error LNK1120: 1 unresolved externals
Error: linker exited with status 1120
updated test:
// PERMUTE_ARGS:
// REQUIRED_ARGS: -betterC

struct VertexA(T)
{
    T pos;
}

struct VertexB(T)
{
    T[2] pos;
}

void test(T)()
{
    T f_a;
    T f_b;
    assert(f_a != f_b);

    f_a = 0;
    assert(f_a != f_b);

    f_b = 0;
    assert(f_a == f_b);


    VertexA!T a_a;
    VertexA!T a_b;
    assert(a_a != a_b);

    a_a.pos = 0;
    assert(a_a != a_b);

    a_b.pos = 0;
    assert(a_a == a_b);

    VertexB!T b_a;
    VertexB!T b_b;
    assert(b_a != b_b);

    b_a.pos = 0;
    assert(b_a != b_b);

    b_b.pos = 0;   
    assert(b_a == b_b);
}

extern(C) int main()
{
    test!(float)();
    test!(double)();
    test!(real)();

    return 0;
}

@@ -11486,6 +11486,10 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
{
return false;
}

if (t1n.isfloating() && t2n.isfloating())
Copy link
Contributor

Choose a reason for hiding this comment

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

I find this a bit arbitrary... shouldn't there be a test whether it's a scalar or not? Why is there no test needed here for int for example?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

because for integral types it already get optimized to use mem_cmp

for floating points it can't, and fallsback to using TypeInfo, wich is not available in betterC / wasm

if ((telement.isintegral() || telement.ty == Tvoid) && telement.ty == telement2.ty)

Copy link
Contributor Author

@ryuukk ryuukk left a comment

Choose a reason for hiding this comment

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

I added the definition of the missing symbol, i suspect it's simply because betterC doesn't link with the runtime, so we need to add it ourselves, ideally this shouldn't be needed, but i don't know where to put it in dmd?

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


struct VertexA
{
float pos;
Copy link
Contributor Author

@ryuukk ryuukk Nov 19, 2021

Choose a reason for hiding this comment

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

i tried to update the test but i'm getting the following:

test.obj : error LNK2019: unresolved external symbol _memset80 referenced in function _D4test__TQiTeZQnFNaNbNiNfZv
test.exe : fatal error LNK1120: 1 unresolved externals
Error: linker exited with status 1120
updated test:
// PERMUTE_ARGS:
// REQUIRED_ARGS: -betterC

struct VertexA(T)
{
    T pos;
}

struct VertexB(T)
{
    T[2] pos;
}

void test(T)()
{
    T f_a;
    T f_b;
    assert(f_a != f_b);

    f_a = 0;
    assert(f_a != f_b);

    f_b = 0;
    assert(f_a == f_b);


    VertexA!T a_a;
    VertexA!T a_b;
    assert(a_a != a_b);

    a_a.pos = 0;
    assert(a_a != a_b);

    a_b.pos = 0;
    assert(a_a == a_b);

    VertexB!T b_a;
    VertexB!T b_b;
    assert(b_a != b_b);

    b_a.pos = 0;
    assert(b_a != b_b);

    b_b.pos = 0;   
    assert(b_a == b_b);
}

extern(C) int main()
{
    test!(float)();
    test!(double)();
    test!(real)();

    return 0;
}

@@ -11542,7 +11546,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
}

// lower some array comparisons to object.__equals(e1, e2)
if (needsArrayLowering || (t1.ty == Tarray && t2.ty == Tarray))
if (needsArrayLowering || (t1.ty == Tarray && t2.ty == Tarray) || (t1.ty == Tsarray && t2.ty == Tsarray))
Copy link
Contributor

Choose a reason for hiding this comment

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

This is most likely too much, lowering all equality comparisons of static array pairs.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

That's because this test otherwise doesn't work:

struct VertexB
{
    float[2] pos;
}

struct Hold
{
    VertexB v;
}

extern(C) int main()
{
    Hold[1] h_a; 
    Hold[1] h_b;
    assert(h_a != h_b);
}

@kinke
Copy link
Contributor

kinke commented Nov 19, 2021

IIRC, I've at point tried to lower all array equality comparisons to the template, but that didn't work due to string special cases.

In general, https://github.com/dlang/druntime/blob/master/src/core/internal/array/equality.d should be able to handle all array pairs, and use memcmp if possible. For static array pairs, it would probably add a superfluous length check, but that's probably negligible, incl. the static-array-to-slice promotion.

Currently, the lowering is avoided in some cases, especially for matching integral elements with memcmp-ability, and the equality expression forwarded to the glue layer, where each compiler has its own checks for memcmp-ability and tries to do that, otherwise falling back to the slow, TypeInfo-based _adEq2 druntime helper. That's quite redundant (each compiler, and memcmp-logic also in druntime) but faster to compile without template bloat, and guaranteed inlined, contrary to the template, which might require LTO for inlining length check and memcmp call. So that's the trade-off.

@ryuukk
Copy link
Contributor Author

ryuukk commented Nov 19, 2021

Well all i wanted was to make static float array to work.. that's blocking my project right now.. and i have no idea what is good/bad :p

So what should be done? i can't really give up on static float arrays

@kinke
Copy link
Contributor

kinke commented Nov 20, 2021

I think the memcmp-ability should be checked in the frontend, and only such equality expressions be forwarded to the glue layer, the other ones lowered to the __equals template => no more _adEq2 usages, probably all array equality comparisons working with -betterC, simpler glue layers for all compilers…

Edit: Pinging @ibuclaw.

@ibuclaw
Copy link
Member

ibuclaw commented Nov 20, 2021

I think the memcmp-ability should be checked in the frontend, and only such equality expressions be forwarded to the glue layer, the other ones lowered to the __equals template => no more _adEq2 usages, probably all array equality comparisons working with -betterC, simpler glue layers for all compilers…

Edit: Pinging @ibuclaw.

Seems reasonable to me.

@ryuukk
Copy link
Contributor Author

ryuukk commented Nov 20, 2021

Is that an easy change to do? should this PR be closed? since it's blocking me, i would like to speed up the fix, maybe i can do it if you can point me to the right direction (if the change is relatively easy to do)

@RazvanN7
Copy link
Contributor

RazvanN7 commented Jan 5, 2022

@ryuukk I would say it is not simple, but it's not extremely complicated also. Here are some pointers:

  1. This is the part where e2ir.d checks for memcmp-ability [1]
  2. This is the branch where adEq2 is chosen [2]

You already know where in the frontend the checks for array equality are performed (expressionsem.d).

What needs to be done is to add the proper checks in expressionsem.d as to eliminate the code in [2]. Essentially, you need to make sure that the checks in [1] are present in the frontend and, if fulfilled, the expression is forwarded to the gluelayer; everything else is lowered to __equals. Should be straightforward.

[1] https://github.com/dlang/dmd/blob/master/src/dmd/e2ir.d#L1860
[2] https://github.com/dlang/dmd/blob/master/src/dmd/e2ir.d#L1941

@RazvanN7
Copy link
Contributor

RazvanN7 commented Jan 5, 2022

Oh, I just noticed that @kinke is working on this on #13331 so I'll close this. Thanks @ryuukk

@RazvanN7 RazvanN7 closed this Jan 5, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
6 participants