Skip to content

Cleanup gen tree compare#125357

Open
AndyAyersMS wants to merge 7 commits intodotnet:mainfrom
AndyAyersMS:CleanupGenTreeCompare
Open

Cleanup gen tree compare#125357
AndyAyersMS wants to merge 7 commits intodotnet:mainfrom
AndyAyersMS:CleanupGenTreeCompare

Conversation

@AndyAyersMS
Copy link
Member

Add some missing checks

AndyAyersMS and others added 3 commits March 9, 2026 16:23
- GT_CNS_INT: Compare icon handle flags and field sequences, not just
  the integer value. Two constants with the same value but different
  handle types or field sequences are not equivalent.

- GT_ALLOCOBJ: Add missing case in the unary ExOp switch. Previously
  hit the default assert in debug builds.

- GT_ARR_ELEM: Compare gtArrElemSize. Resolves existing TODO comment.

- GT_SELECT: Add missing case in the special-node switch. Compare
  all three operands (condition, true value, false value).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- GT_INDEX_ADDR: Compare gtElemType, gtLenOffset, and gtElemOffset
  in addition to gtElemSize.

- GT_FTN_ADDR: Add leaf case comparing gtFptrMethod. Previously
  returned false for equal function address nodes.

- GT_PHI_ARG: Add leaf case comparing local number, predecessor
  block, and SSA number. Previously returned false for equal
  phi arg nodes.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- GT_JCC/GT_SETCC: Add leaf cases comparing gtCondition.

- GT_PHYSREG: Add leaf case comparing gtSrcReg.

- GT_JCMP/GT_JTEST/GT_SELECTCC: Compare gtCondition for binary
  nodes that carry a condition code but are not ExOp.

- GT_SELECT_INCCC/GT_SELECT_INVCC/GT_SELECT_NEGCC (ARM64): Compare
  gtCondition for ARM64-specific conditional select variants.

- GT_CCMP (ARM64/AMD64): Compare gtCondition and gtFlagsVal.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings March 10, 2026 01:37
@github-actions github-actions bot added the area-CodeGen-coreclr CLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI label Mar 10, 2026
@dotnet-policy-service
Copy link
Contributor

Tagging subscribers to this area: @JulieLeeMSFT, @jakobbotsch
See info in area-owners.md if you want to be subscribed.

@AndyAyersMS
Copy link
Member Author

@EgorBo PTAL
FYI @dotnet/jit-contrib

Just a few diffs, but it looks like the old behavior can lead to improperly optimized multi-guess GDVs in NAOT.

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR tightens GenTree::Compare in CoreCLR JIT to treat more node-specific fields as part of structural equality, improving correctness for optimizations that rely on tree equivalence.

Changes:

  • Extend constant and leaf-node comparisons to include additional distinguishing fields (e.g., handle flags, field sequences, conditions, physregs).
  • Add missing comparisons for several operators (GT_PHI_ARG, GT_FTN_ADDR, GT_JCC/GT_SETCC, GT_PHYSREG, GT_ALLOCOBJ, GT_INDEX_ADDR, GT_ARR_ELEM, GT_SELECT).
  • Add condition-code comparisons for CC-carrying binops (GT_JCMP, GT_JTEST, GT_SELECTCC, target-specific selects, GT_CCMP).

AndyAyersMS and others added 4 commits March 9, 2026 20:01
- GT_CNS_INT: add gtCompileTimeHandle comparison
- GT_FTN_ADDR: add gtFptrDelegateTarget and gtEntryPoint (R2R) comparisons
- GT_ALLOCOBJ: add gtHelperHasSideEffects and gtEntryPoint (R2R) comparisons

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- GT_CNS_INT: add gtCompileTimeHandle comparison
- GT_FTN_ADDR: add gtFptrDelegateTarget and gtEntryPoint (R2R) comparisons
- GT_ALLOCOBJ: add gtHelperHasSideEffects and gtEntryPoint (R2R) comparisons

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings March 10, 2026 03:21
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 1 out of 1 changed files in this pull request and generated 3 comments.

Comment on lines +2768 to +2773
#ifdef FEATURE_READYTORUN
if (op1->AsFptrVal()->gtEntryPoint.addr != op2->AsFptrVal()->gtEntryPoint.addr)
{
break;
}
#endif
Copy link

Copilot AI Mar 10, 2026

Choose a reason for hiding this comment

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

For FEATURE_READYTORUN comparisons of GT_FTN_ADDR, this only checks gtEntryPoint.addr. CORINFO_CONST_LOOKUP semantics also depend on accessType (and the union value may be interpreted differently based on it). To avoid treating different entry point forms as equal, include accessType in the comparison (and compare the relevant union member accordingly).

Copilot uses AI. Check for mistakes.
Comment on lines +2897 to +2902
#ifdef FEATURE_READYTORUN
if (op1->AsAllocObj()->gtEntryPoint.addr != op2->AsAllocObj()->gtEntryPoint.addr)
{
return false;
}
#endif
Copy link

Copilot AI Mar 10, 2026

Choose a reason for hiding this comment

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

For FEATURE_READYTORUN comparisons of GT_ALLOCOBJ, this only checks gtEntryPoint.addr. CORINFO_CONST_LOOKUP equality should also consider accessType (and the union contents as interpreted by it), otherwise nodes with different entry point access modes can be considered identical.

Copilot uses AI. Check for mistakes.
Comment on lines +2984 to +2988
if ((op1->AsIndexAddr()->gtElemSize != op2->AsIndexAddr()->gtElemSize) ||
(op1->AsIndexAddr()->gtElemType != op2->AsIndexAddr()->gtElemType) ||
(op1->AsIndexAddr()->gtStructElemClass != op2->AsIndexAddr()->gtStructElemClass) ||
(op1->AsIndexAddr()->gtLenOffset != op2->AsIndexAddr()->gtLenOffset) ||
(op1->AsIndexAddr()->gtElemOffset != op2->AsIndexAddr()->gtElemOffset))
Copy link

Copilot AI Mar 10, 2026

Choose a reason for hiding this comment

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

The GT_INDEX_ADDR ExOp comparison now checks several structural fields, but it still doesn't account for GT_INDEX_ADDR-specific semantic flags like GTF_INX_RNGCHK and GTF_INX_ADDR_NONNULL. These flags affect whether a bounds check / null-faulting behavior is required, so Compare should ensure the relevant bits match to avoid considering semantically different nodes identical.

Copilot uses AI. Check for mistakes.
@EgorBo
Copy link
Member

EgorBo commented Mar 10, 2026

Any idea what produced the massive diffs?

if ((op1->AsIntCon()->gtIconVal == op2->AsIntCon()->gtIconVal) &&
(op1->GetIconHandleFlag() == op2->GetIconHandleFlag()) &&
(op1->AsIntCon()->gtFieldSeq == op2->AsIntCon()->gtFieldSeq) &&
(op1->AsIntCon()->gtCompileTimeHandle == op2->AsIntCon()->gtCompileTimeHandle))
Copy link
Member

Choose a reason for hiding this comment

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

I suspect e.g. assert-prop never propagates compile-time handle for constants

Copy link
Contributor

Choose a reason for hiding this comment

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

I think we should add tests for the actual issues this fixes. And remove the comparisons that we don't have failing tests for.

For the field sequence check, I think it can be something like:

if (obj.GetType() == typeof(ClassOne) {
   Unsafe.As<ClassOne>(obj).Field = 1;
} else {
   Unsafe.As<ClassTwo>(obj).Field = 2; // Same offset as ClassOne::Field
}

being tail-merged (the field sequence affects semantics of ADD(x, field seq).

I don't think gtCompileTimeHandle does or should affect semantics.

I can't think of any cases where GetIconHandleFlag would differentiate two different handles with the same value. If we want to support such cases, we will need to rationalize what that 'means' first.

if ((op1->AsIntCon()->gtIconVal == op2->AsIntCon()->gtIconVal) &&
(op1->GetIconHandleFlag() == op2->GetIconHandleFlag()) &&
(op1->AsIntCon()->gtFieldSeq == op2->AsIntCon()->gtFieldSeq) &&
(op1->AsIntCon()->gtCompileTimeHandle == op2->AsIntCon()->gtCompileTimeHandle))
Copy link
Contributor

Choose a reason for hiding this comment

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

I think we should add tests for the actual issues this fixes. And remove the comparisons that we don't have failing tests for.

For the field sequence check, I think it can be something like:

if (obj.GetType() == typeof(ClassOne) {
   Unsafe.As<ClassOne>(obj).Field = 1;
} else {
   Unsafe.As<ClassTwo>(obj).Field = 2; // Same offset as ClassOne::Field
}

being tail-merged (the field sequence affects semantics of ADD(x, field seq).

I don't think gtCompileTimeHandle does or should affect semantics.

I can't think of any cases where GetIconHandleFlag would differentiate two different handles with the same value. If we want to support such cases, we will need to rationalize what that 'means' first.


if (kind & GTK_BINOP)
{
if (op1->OperIs(GT_JCMP, GT_JTEST, GT_SELECTCC))
Copy link
Contributor

Choose a reason for hiding this comment

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

These operators look like they're missing GTK_EXOP.

Comment on lines +2892 to +2893
(op1->AsAllocObj()->gtAllocObjClsHnd != op2->AsAllocObj()->gtAllocObjClsHnd) ||
(op1->AsAllocObj()->gtHelperHasSideEffects != op2->AsAllocObj()->gtHelperHasSideEffects))
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
(op1->AsAllocObj()->gtAllocObjClsHnd != op2->AsAllocObj()->gtAllocObjClsHnd) ||
(op1->AsAllocObj()->gtHelperHasSideEffects != op2->AsAllocObj()->gtHelperHasSideEffects))
(op1->AsAllocObj()->gtAllocObjClsHnd != op2->AsAllocObj()->gtAllocObjClsHnd))

gtHelperHasSideEffects is a pure function of gtAllocObjClsHnd.

@AndyAyersMS
Copy link
Member Author

Any idea what produced the massive diffs?

Diffs were pretty minimal at first but the gaggle of copilot reviewers added more checks. Let me claw some of these back.

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

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.

4 participants