Skip to content

Conversation

@sandreenko
Copy link
Contributor

@sandreenko sandreenko commented Jul 23, 2020

If an unconsumed OBJ or BLK node reaches rationalize or later phases it fires an assert because we don't know how to produce them.

Such nodes could appear from their parent removal if the parent is a dead store or an unused HWIntrinsic intrinsic.

This PR fixes #39737, there are other issues that were discovered but they are CQ and don't meet RC1 bar.

Changes:
a1697de: Add a repro.

0158523: Add a helper function fgTryRemoveNonLocal.

4ee3da9: Fix the issue.

@sandreenko sandreenko added the area-CodeGen-coreclr CLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI label Jul 23, 2020
@sandreenko sandreenko requested a review from erozenfeld July 24, 2020 21:55
@sandreenko
Copy link
Contributor Author

PTAL @erozenfeld @dotnet/jit-contrib

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 condition was pre-existing but it is not always correct, lower replaces many HWIntrinsic and other nodes as IND (for example Vector64.Create) without setting GTF_IND_NONFAULTING on these IND nodes. If you call fgDebugCheckFlags on such IND that is not marked as X and not marked GTF_IND_NONFAULTING it would fail.

A better condition would be if (!node->gtSetFlags() && (!node->OperMayThrow(this)) || ((node->gtFlags & GTF_EXCEPT) == 0) but such fix doesn't produce many positive diffs, so I would wait for
#10249 to show all such places and then fix the root cause, not the side-effects.

Copy link
Member

Choose a reason for hiding this comment

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

I believe in LIR GTF_EXCEPT is not supposed to capture children side effects. What happens if you use if (!node->gtSetFlags() && ((node->gtFlags & GTF_EXCEPT) == 0) here?

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 believe in LIR GTF_EXCEPT is not supposed to capture children side effects

Yes, but we don't check it and right now many parent nodes are having GTF_EXCEPT from children, so with if (!node->gtSetFlags() && ((node->gtFlags & GTF_EXCEPT) == 0) we are keeping many null checks that we can delete.

Copy link
Contributor

Choose a reason for hiding this comment

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

When you say "it is not always correct" you mean that it is overly conservative, correct?
It seems to me that the better approach would be to fix the setting of GTF_IND_NONFAULTING.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

When you say "it is not always correct" you mean that it is overly conservative, correct?

Yes.

It seems to me that the better approach would be to fix the setting of GTF_IND_NONFAULTING.

Agree, I should have said "a better temporary hack", I think we should set GTF_IND_NONFAULTING for 6.0 after #10249

Copy link
Member

Choose a reason for hiding this comment

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

I believe in LIR GTF_EXCEPT is not supposed to capture children side effects. What happens if you use if (!node->gtSetFlags() && ((node->gtFlags & GTF_EXCEPT) == 0) here?

Copy link
Member

Choose a reason for hiding this comment

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

Is it ok to have a dummy use of GT_IND?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, lower and rationalize tolerate that even for struct IND:

void Lowering::LowerIndir(GenTreeIndir* ind)
{
    assert(ind->OperIs(GT_IND, GT_NULLCHECK));
    // Process struct typed indirs separately unless they are unused;
    // they only appear as the source of a block copy operation or a return node.
    if (!ind->TypeIs(TYP_STRUCT) || ind->IsUnusedValue())

@erozenfeld
Copy link
Member

Any diffs from this change?

@sandreenko
Copy link
Contributor Author

Any diffs from this change?

No, there were a few functions with JitDump changes, but there were about OBJ SIMD->NULLCHECK SIMD and did not lead to a different asm.

Copy link
Member

Choose a reason for hiding this comment

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

We should probably factor this to a helper, e.g., ChangeOperToNullCheck but it can be done in a separate PR.

Copy link
Contributor

Choose a reason for hiding this comment

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

I don't think this matters, but Lowering may change these to GT_IND because that produces better code for some cases.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, I saw that code, unconsumed IND are fine, we had asserts only about OBJ/BKJ/DYN_BLK.

Copy link
Contributor

Choose a reason for hiding this comment

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

Right - my point was that it might be nice to avoid having to transform multiple times, but neither do we want to duplicate the determination of which form to use.

Copy link
Member

@erozenfeld erozenfeld left a comment

Choose a reason for hiding this comment

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

LGTM

Copy link
Contributor

@CarolEidt CarolEidt left a comment

Choose a reason for hiding this comment

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

LGTM

Copy link
Contributor

Choose a reason for hiding this comment

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

I don't think this matters, but Lowering may change these to GT_IND because that produces better code for some cases.

Copy link
Contributor

Choose a reason for hiding this comment

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

When you say "it is not always correct" you mean that it is overly conservative, correct?
It seems to me that the better approach would be to fix the setting of GTF_IND_NONFAULTING.

Copy link
Member

@erozenfeld erozenfeld left a comment

Choose a reason for hiding this comment

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

LGTM with a couple of comment nits. Thank you for the refactoring.

@sandreenko sandreenko merged commit ad90f7a into dotnet:master Jul 30, 2020
@sandreenko sandreenko deleted the fixDummyObj branch July 30, 2020 02:33
Jacksondr5 pushed a commit to Jacksondr5/runtime that referenced this pull request Aug 10, 2020
* Add a repro.

* Add a helper function `fgTryRemoveNonLocal`.

* Fix the issue.

* extract `gtChangeOperToNullCheck`.

* update comments.
@karelz karelz added this to the 5.0.0 milestone Aug 18, 2020
@ghost ghost locked as resolved and limited conversation to collaborators Dec 8, 2020
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

area-CodeGen-coreclr CLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI

Projects

None yet

Development

Successfully merging this pull request may close these issues.

assert failed !IsDummyUse() for an OBJ under HWINTRINSIC.

4 participants