Skip to content
This repository was archived by the owner on Oct 12, 2022. It is now read-only.
/ druntime Public archive

Cleanup and fix implementation of _d_array{setlengthT,catnTX}Trace#2673

Merged
thewilsonator merged 3 commits intodlang:masterfrom
Vild:FixHookTrace
Jul 15, 2019
Merged

Cleanup and fix implementation of _d_array{setlengthT,catnTX}Trace#2673
thewilsonator merged 3 commits intodlang:masterfrom
Vild:FixHookTrace

Conversation

@Vild
Copy link
Contributor

@Vild Vild commented Jul 13, 2019

This should be the final fix needed to be able to get dlang/dmd#10106 to pass all the unittest.

This code uses a pure bypass technique I found inside of dmd's source code so no more ugly hacky casts are needed for calling GC.stats() and accumulate.
I've also refactored the two previous *Trace functions into HookTraceImpl to make sure that all the Trace functions are implemented correctly and to make it easier to change in the future.
The HookTraceImpl also contains a if (...) assert(0); check to make sure that the compiler does not remove the call to accumulate, which is what is currently happening in dlang/dmd#10106.

Signed-off-by: Dan Printzell <xwildn00bx@gmail.com>
@Vild Vild requested review from andralex and wilzbach as code owners July 13, 2019 15:02
@dlang-bot
Copy link
Contributor

Thanks for your pull request and interest in making D better, @Vild! 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

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 + druntime#2673"

@aG0aep6G
Copy link
Contributor

That "pure bypass technique" looks like a compiler bug.

@Vild
Copy link
Contributor Author

Vild commented Jul 13, 2019

That "pure bypass technique" looks like a compiler bug.

I don't fully understand why this "technique" exist. It (or at least the comment) was added 5 years ago:
dlang/dmd@0acb94c#diff-31ec3d6838ade0264adec0944948507dR2399

@aG0aep6G
Copy link
Contributor

I don't fully understand why this "technique" exist. It (or at least the comment) was added 5 years ago:
dlang/dmd@0acb94c#diff-31ec3d6838ade0264adec0944948507dR2399

I can't say that I understand what that comment is trying to say. I suspect it might just be wrong.

Anyway, I'm sure that we're looking at a compiler bug. Calling an impure function from a pure one can't be allowed (without a cast).

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

@JinShil
Copy link
Contributor

JinShil commented Jul 13, 2019

I don't fully understand why this "technique" exist.

That behavior was introduced in dlang/dmd#4344 as a fix to https://issues.dlang.org/show_bug.cgi?id=14039

@JinShil
Copy link
Contributor

JinShil commented Jul 14, 2019

@WalterBright, you filed Issue 14039 and approved and merged the fix at dlang/dmd#4344. Can you please help reconcile this? Is the technique employed in this PR a bug or a legitimate feature?

@aG0aep6G
Copy link
Contributor

Anyway, I'm sure that we're looking at a compiler bug. Calling an impure function from a pure one can't be allowed (without a cast).

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

And a PR to fix it: dlang/dmd#10172

@jpf91
Copy link
Contributor

jpf91 commented Jul 15, 2019

@Vild if you use the function pointer / literal / cast bypass technique instead, does that trigger the optimizer problem? AFAIK this is the commonly used technique to bypass pure.

@RazvanN7
Copy link
Contributor

Is there a possibility that if dlang/dmd#10172 is merged, then this PR might not be needed?

@JinShil
Copy link
Contributor

JinShil commented Jul 15, 2019

Is there a possibility that if dlang/dmd#10172 is merged, then this PR might not be needed?

I believe that if dlang/dmd#10172 is merged, this PR will no longer work.

@Vild
Copy link
Contributor Author

Vild commented Jul 15, 2019

@Vild if you use the function pointer / literal / cast bypass technique instead, does that trigger the optimizer problem? AFAIK this is the commonly used technique to bypass pure.

I could move back to the pointer cast method, I just felt that it looked too hacky compared to this bypass method when I found it inside of dmd's code.

Is there a possibility that if dlang/dmd#10172 is merged, then this PR might not be needed?

I still need to apply the if (!accumulate(...)) assert(0); fix to not allow the optimizer to remove that call. So I just need to use casts instead of this bypass method.

Vild added 2 commits July 15, 2019 12:46
Signed-off-by: Dan Printzell <xwildn00bx@gmail.com>
Signed-off-by: Dan Printzell <xwildn00bx@gmail.com>
@Vild
Copy link
Contributor Author

Vild commented Jul 15, 2019

I've changed it so it uses cast instead of that other method to fake purity.

@thewilsonator thewilsonator merged commit babf32d into dlang:master Jul 15, 2019
@Vild Vild deleted the FixHookTrace branch July 15, 2019 17:19

// `accumulatePure` returns the value of `size`, which can never be zero due to the
// previous 'if'. So this assert will never be triggered.
assert(0);
Copy link
Contributor

Choose a reason for hiding this comment

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

BTW, reading this I think there's may be a deeper issue whenever we have 'fake' pure code which internally calls impure code.

I guess the intent of such code is to not influence purity of the caller (i.e. completely optimizing away the caller would be fine), but the callee is not really pure, so it should not be removed on it's own. I wonder whether this can be expressed in any theoretically sound way, @tgehr any insights here?

It seems to me that the problem is that we lie to the compiler and simply tell it this function is pure. Maybe we should rather tell it Ignore purity checks for this call, but the function is not pure. Intuitively I'd say a

pure void foo()
{
    __traits(forcepure, bar());
}

Should work: if bar is not inlined, this would tell the compiler to keep the call, but ignore purity for purity calculation of the callee. With inlining, the compiler should be able to see the side effects anyway and should not optimize these away.

Copy link
Contributor

Choose a reason for hiding this comment

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

I can see how your assert code has the same effect though: It marks the return value as used, as long as the call is not inlined. When it's inlined, the compiler may remove the assert, but it will see the impure parts of accumulatePure and keep these. This can probably also somehow solved like this: https://stackoverflow.com/questions/40122141/preventing-compiler-optimizations-while-benchmarking, but I'd prefer a compiler hook which should also work for void functions.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I've been thinking about introducing HookCallExp or a bool in CallExp that ignore the purity and somehow allowed the code to be inline, which is disallowed right now pragma(inline, false), whilst being able to be detected as the hook (HookCompoundStatement?) so the CTFE intercept code works.

Copy link
Contributor

Choose a reason for hiding this comment

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

That sounds like a great idea.

Copy link

Choose a reason for hiding this comment

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

BTW, reading this I think there's may be a deeper issue whenever we have 'fake' pure code which internally calls impure code.

I guess the intent of such code is to not influence purity of the caller (i.e. completely optimizing away the caller would be fine), but the callee is not really pure, so it should not be removed on it's own. I wonder whether this can be expressed in any theoretically sound way, @tgehr any insights here?
...

@jpf91 This was what __mutable functions were for in the original __mutable proposal. Inlining for such functions is not so easy if you want to preserve constraints on evaluation order precisely.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

8 participants

Comments