// We normally expect IMPLICIT_DEF values to be live only until the end
// of their block. If the value is really live longer and gets pruned in
// another block, this flag is cleared again.
+ //
+ // Clearing the valid lanes is deferred until it is sure this can be
+ // erased.
V.ErasableImplicitDef = true;
- V.ValidLanes &= ~V.WriteLanes;
}
}
}
Other.computeAssignment(V.OtherVNI->id, *this);
Val &OtherV = Other.Vals[V.OtherVNI->id];
- // Check if OtherV is an IMPLICIT_DEF that extends beyond its basic block.
- // This shouldn't normally happen, but ProcessImplicitDefs can leave such
- // IMPLICIT_DEF instructions behind, and there is nothing wrong with it
- // technically.
- //
- // When it happens, treat that IMPLICIT_DEF as a normal value, and don't try
- // to erase the IMPLICIT_DEF instruction.
- if (OtherV.ErasableImplicitDef && DefMI &&
- DefMI->getParent() != Indexes->getMBBFromIndex(V.OtherVNI->def)) {
- LLVM_DEBUG(dbgs() << "IMPLICIT_DEF defined at " << V.OtherVNI->def
- << " extends into "
- << printMBBReference(*DefMI->getParent())
- << ", keeping it.\n");
- OtherV.ErasableImplicitDef = false;
+ if (OtherV.ErasableImplicitDef) {
+ // Check if OtherV is an IMPLICIT_DEF that extends beyond its basic block.
+ // This shouldn't normally happen, but ProcessImplicitDefs can leave such
+ // IMPLICIT_DEF instructions behind, and there is nothing wrong with it
+ // technically.
+ //
+ // When it happens, treat that IMPLICIT_DEF as a normal value, and don't try
+ // to erase the IMPLICIT_DEF instruction.
+ if (DefMI &&
+ DefMI->getParent() != Indexes->getMBBFromIndex(V.OtherVNI->def)) {
+ LLVM_DEBUG(dbgs() << "IMPLICIT_DEF defined at " << V.OtherVNI->def
+ << " extends into "
+ << printMBBReference(*DefMI->getParent())
+ << ", keeping it.\n");
+ OtherV.ErasableImplicitDef = false;
+ } else {
+ // We deferred clearing these lanes in case we needed to save them
+ OtherV.ValidLanes &= ~OtherV.WriteLanes;
+ }
}
// Allow overlapping PHI values. Any real interference would show up in a
Val &OtherV = Other.Vals[V.OtherVNI->id];
// We cannot erase an IMPLICIT_DEF if we don't have valid values for all
// its lanes.
- if ((OtherV.WriteLanes & ~V.ValidLanes).any() && TrackSubRegLiveness)
+ if (OtherV.ErasableImplicitDef &&
+ TrackSubRegLiveness &&
+ (OtherV.WriteLanes & ~V.ValidLanes).any()) {
+ LLVM_DEBUG(dbgs() << "Cannot erase implicit_def with missing values\n");
+
OtherV.ErasableImplicitDef = false;
+ // The valid lanes written by the implicit_def were speculatively cleared
+ // before, so make this more conservative. It may be better to track this,
+ // I haven't found a testcase where it matters.
+ OtherV.ValidLanes = LaneBitmask::getAll();
+ }
+
OtherV.Pruned = true;
LLVM_FALLTHROUGH;
}
--- /dev/null
+# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py
+# RUN: llc -mtriple=amdgcn-amd-amdhsa -verify-coalescing -run-pass=simple-register-coalescing -verify-machineinstrs -o - %s | FileCheck %s
+
+# Bug 39602: Avoid "Couldn't join subrange" error when clearing valid
+# lanes on an implicit_def that later cannot be erased.
+
+---
+name: lost_valid_lanes_maybe_erasable_implicit_def
+tracksRegLiveness: true
+body: |
+ ; CHECK-LABEL: name: lost_valid_lanes_maybe_erasable_implicit_def
+ ; CHECK: bb.0:
+ ; CHECK: successors: %bb.1(0x80000000)
+ ; CHECK: undef %0.sub1:sreg_64 = IMPLICIT_DEF
+ ; CHECK: bb.1:
+ ; CHECK: %0.sub0:sreg_64 = S_MOV_B32 0
+ ; CHECK: [[COPY:%[0-9]+]]:sreg_64 = COPY %0
+ ; CHECK: dead %0.sub1:sreg_64 = COPY %0.sub0
+ ; CHECK: S_ENDPGM implicit [[COPY]].sub1
+ bb.0:
+ successors: %bb.1
+ undef %0.sub1:sreg_64 = IMPLICIT_DEF
+
+ bb.1:
+ %0.sub0:sreg_64 = S_MOV_B32 0
+ %1:sreg_64 = COPY %0:sreg_64
+ dead %0.sub1:sreg_64 = COPY %0.sub0:sreg_64
+ S_ENDPGM implicit %1.sub1:sreg_64
+
+...
+---
+# Same as previous, except with a real value instead of
+# IMPLICIT_DEF. These should both be handled the same way.
+
+name: lost_valid_lanes_real_value
+tracksRegLiveness: true
+body: |
+ ; CHECK-LABEL: name: lost_valid_lanes_real_value
+ ; CHECK: bb.0:
+ ; CHECK: successors: %bb.1(0x80000000)
+ ; CHECK: undef %0.sub1:sreg_64 = S_MOV_B32 -1
+ ; CHECK: bb.1:
+ ; CHECK: %0.sub0:sreg_64 = S_MOV_B32 0
+ ; CHECK: [[COPY:%[0-9]+]]:sreg_64 = COPY %0
+ ; CHECK: dead %0.sub1:sreg_64 = COPY %0.sub0
+ ; CHECK: S_ENDPGM implicit [[COPY]].sub1
+ bb.0:
+ successors: %bb.1
+ undef %0.sub1:sreg_64 = S_MOV_B32 -1
+
+ bb.1:
+ %0.sub0:sreg_64 = S_MOV_B32 0
+ %1:sreg_64 = COPY %0:sreg_64
+ dead %0.sub1:sreg_64 = COPY %0.sub0:sreg_64
+ S_ENDPGM implicit %1.sub1:sreg_64
+
+...