[llvm] [slp] fix scheduler deadlock for copyable users of vectorized instructions (PR #176661)
via llvm-commits
llvm-commits at lists.llvm.org
Sun Jan 18 15:30:32 PST 2026
https://github.com/Serosh-commits updated https://github.com/llvm/llvm-project/pull/176661
>From d099f428252d1207884898af9c7d8986f74e7809 Mon Sep 17 00:00:00 2001
From: Serosh <janmejayapanda400 at gmail.com>
Date: Sun, 18 Jan 2026 23:22:08 +0530
Subject: [PATCH 1/2] [slp] fix scheduler deadlock by correctly tracking
copyable dependencies
---
.../Transforms/Vectorize/SLPVectorizer.cpp | 23 +++++++++++--------
1 file changed, 14 insertions(+), 9 deletions(-)
diff --git a/llvm/lib/Transforms/Vectorize/SLPVectorizer.cpp b/llvm/lib/Transforms/Vectorize/SLPVectorizer.cpp
index 4c4901c314406..06ed3502cfd89 100644
--- a/llvm/lib/Transforms/Vectorize/SLPVectorizer.cpp
+++ b/llvm/lib/Transforms/Vectorize/SLPVectorizer.cpp
@@ -22576,11 +22576,20 @@ void BoUpSLP::scheduleBlock(const BoUpSLP &R, BlockScheduling *BS) {
if (!Bundle.hasValidDependencies())
BS->calculateDependencies(Bundle, /*InsertInReadyList=*/false, this);
}
- continue;
}
- SmallVector<ScheduleCopyableData *> CopyableData =
+ SmallVector<ScheduleCopyableData *> SelfCopyableData =
+ BS->getScheduleCopyableData(I);
+ for (ScheduleCopyableData *SD : reverse(SelfCopyableData)) {
+ ScheduleBundle &Bundle = SD->getBundle();
+ Bundle.setSchedulingPriority(Idx++);
+ if (!Bundle.hasValidDependencies())
+ BS->calculateDependencies(Bundle, /*InsertInReadyList=*/false, this);
+ }
+ SmallVector<ScheduleCopyableData *> UserCopyableData =
BS->getScheduleCopyableDataUsers(I);
- if (ScheduleData *SD = BS->getScheduleData(I)) {
+ if (Bundles.empty() && SelfCopyableData.empty()) {
+ ScheduleData *SD = BS->getScheduleData(I);
+ assert(SD && "Expected schedule data");
[[maybe_unused]] ArrayRef<TreeEntry *> SDTEs = getTreeEntries(I);
assert((isVectorLikeInstWithConstOps(SD->getInst()) || SDTEs.empty() ||
SDTEs.front()->doesNotNeedToSchedule() ||
@@ -22588,22 +22597,18 @@ void BoUpSLP::scheduleBlock(const BoUpSLP &R, BlockScheduling *BS) {
"scheduler and vectorizer bundle mismatch");
SD->setSchedulingPriority(Idx++);
if (!SD->hasValidDependencies() &&
- (!CopyableData.empty() ||
+ (!UserCopyableData.empty() ||
any_of(R.ValueToGatherNodes.lookup(I), [&](const TreeEntry *TE) {
assert(TE->isGather() && "expected gather node");
return TE->hasState() && TE->hasCopyableElements() &&
TE->isCopyableElement(I);
}))) {
- // Need to calculate deps for these nodes to correctly handle copyable
- // dependencies, even if they were cancelled.
- // If copyables bundle was cancelled, the deps are cleared and need to
- // recalculate them.
ScheduleBundle Bundle;
Bundle.add(SD);
BS->calculateDependencies(Bundle, /*InsertInReadyList=*/false, this);
}
}
- for (ScheduleCopyableData *SD : reverse(CopyableData)) {
+ for (ScheduleCopyableData *SD : reverse(UserCopyableData)) {
ScheduleBundle &Bundle = SD->getBundle();
Bundle.setSchedulingPriority(Idx++);
if (!Bundle.hasValidDependencies())
>From 1907823db5a8da4cf8aa60c400b1b8eb77d2ae19 Mon Sep 17 00:00:00 2001
From: Serosh <janmejayapanda400 at gmail.com>
Date: Sun, 18 Jan 2026 23:23:00 +0530
Subject: [PATCH 2/2] [slp] add regression test for scheduler deadlock
---
.../Transforms/Vectorize/SLPVectorizer.cpp | 31 ++++++-----
.../X86/gh176658-scheduler-deadlock.ll | 52 +++++++++++++++++++
2 files changed, 69 insertions(+), 14 deletions(-)
create mode 100644 llvm/test/Transforms/SLPVectorizer/X86/gh176658-scheduler-deadlock.ll
diff --git a/llvm/lib/Transforms/Vectorize/SLPVectorizer.cpp b/llvm/lib/Transforms/Vectorize/SLPVectorizer.cpp
index 06ed3502cfd89..f6ca401ae653e 100644
--- a/llvm/lib/Transforms/Vectorize/SLPVectorizer.cpp
+++ b/llvm/lib/Transforms/Vectorize/SLPVectorizer.cpp
@@ -22233,6 +22233,8 @@ void BoUpSLP::BlockScheduling::calculateDependencies(
// instruction is non-schedulable.
if (ScheduleCopyableData *UseSD =
getScheduleCopyableData(EI.UserTE->UserTreeIndex, In)) {
+ if (EI.UserTE->doesNotNeedToSchedule())
+ continue;
CD->incDependencies();
if (!UseSD->isScheduled())
CD->incrementUnscheduledDeps(1);
@@ -22286,6 +22288,10 @@ void BoUpSLP::BlockScheduling::calculateDependencies(
if (isa<PHINode>(U))
continue;
if (ScheduleData *UseSD = getScheduleData(U)) {
+ if (any_of(getScheduleBundles(U), [](const ScheduleBundle *SB) {
+ return SB->getTreeEntry()->doesNotNeedToSchedule();
+ }))
+ continue;
// The operand is a copyable element - skip.
unsigned &NumOps = UserToNumOps.try_emplace(U, 0).first->getSecond();
++NumOps;
@@ -22302,6 +22308,8 @@ void BoUpSLP::BlockScheduling::calculateDependencies(
}
for (ScheduleCopyableData *UseSD :
getScheduleCopyableDataUsers(BundleMember->getInst())) {
+ if (UseSD->getBundle().getTreeEntry()->doesNotNeedToSchedule())
+ continue;
BundleMember->incDependencies();
if (!UseSD->isScheduled())
BundleMember->incrementUnscheduledDeps(1);
@@ -22576,20 +22584,11 @@ void BoUpSLP::scheduleBlock(const BoUpSLP &R, BlockScheduling *BS) {
if (!Bundle.hasValidDependencies())
BS->calculateDependencies(Bundle, /*InsertInReadyList=*/false, this);
}
+ continue;
}
- SmallVector<ScheduleCopyableData *> SelfCopyableData =
- BS->getScheduleCopyableData(I);
- for (ScheduleCopyableData *SD : reverse(SelfCopyableData)) {
- ScheduleBundle &Bundle = SD->getBundle();
- Bundle.setSchedulingPriority(Idx++);
- if (!Bundle.hasValidDependencies())
- BS->calculateDependencies(Bundle, /*InsertInReadyList=*/false, this);
- }
- SmallVector<ScheduleCopyableData *> UserCopyableData =
+ SmallVector<ScheduleCopyableData *> CopyableData =
BS->getScheduleCopyableDataUsers(I);
- if (Bundles.empty() && SelfCopyableData.empty()) {
- ScheduleData *SD = BS->getScheduleData(I);
- assert(SD && "Expected schedule data");
+ if (ScheduleData *SD = BS->getScheduleData(I)) {
[[maybe_unused]] ArrayRef<TreeEntry *> SDTEs = getTreeEntries(I);
assert((isVectorLikeInstWithConstOps(SD->getInst()) || SDTEs.empty() ||
SDTEs.front()->doesNotNeedToSchedule() ||
@@ -22597,18 +22596,22 @@ void BoUpSLP::scheduleBlock(const BoUpSLP &R, BlockScheduling *BS) {
"scheduler and vectorizer bundle mismatch");
SD->setSchedulingPriority(Idx++);
if (!SD->hasValidDependencies() &&
- (!UserCopyableData.empty() ||
+ (!CopyableData.empty() ||
any_of(R.ValueToGatherNodes.lookup(I), [&](const TreeEntry *TE) {
assert(TE->isGather() && "expected gather node");
return TE->hasState() && TE->hasCopyableElements() &&
TE->isCopyableElement(I);
}))) {
+ // Need to calculate deps for these nodes to correctly handle copyable
+ // dependencies, even if they were cancelled.
+ // If copyables bundle was cancelled, the deps are cleared and need to
+ // recalculate them.
ScheduleBundle Bundle;
Bundle.add(SD);
BS->calculateDependencies(Bundle, /*InsertInReadyList=*/false, this);
}
}
- for (ScheduleCopyableData *SD : reverse(UserCopyableData)) {
+ for (ScheduleCopyableData *SD : reverse(CopyableData)) {
ScheduleBundle &Bundle = SD->getBundle();
Bundle.setSchedulingPriority(Idx++);
if (!Bundle.hasValidDependencies())
diff --git a/llvm/test/Transforms/SLPVectorizer/X86/gh176658-scheduler-deadlock.ll b/llvm/test/Transforms/SLPVectorizer/X86/gh176658-scheduler-deadlock.ll
new file mode 100644
index 0000000000000..6abb8b145c573
--- /dev/null
+++ b/llvm/test/Transforms/SLPVectorizer/X86/gh176658-scheduler-deadlock.ll
@@ -0,0 +1,52 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
+; RUN: opt < %s -passes=slp-vectorizer -slp-threshold=-99999 -S | FileCheck %s
+
+target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128-ni:1-p2:32:8:8:32-ni:2"
+target triple = "x86_64-unknown-linux-gnu"
+
+define i32 @gh176658() #0 gc "statepoint-example" {
+; CHECK-LABEL: @gh176658(
+; CHECK-NEXT: bb:
+; CHECK-NEXT: br label [[BB3:%.*]]
+; CHECK: bb1:
+; CHECK-NEXT: [[TMP0:%.*]] = phi <2 x i32> [ [[TMP8:%.*]], [[BB3]] ]
+; CHECK-NEXT: ret i32 0
+; CHECK: bb3:
+; CHECK-NEXT: [[TMP1:%.*]] = phi <2 x i32> [ zeroinitializer, [[BB:%.*]] ], [ [[TMP11:%.*]], [[BB3]] ]
+; CHECK-NEXT: [[LOAD:%.*]] = load i64, ptr addrspace(1) null, align 8
+; CHECK-NEXT: [[TMP2:%.*]] = trunc i64 [[LOAD]] to i32
+; CHECK-NEXT: [[TMP3:%.*]] = insertelement <2 x i32> <i32 0, i32 poison>, i32 [[TMP2]], i32 1
+; CHECK-NEXT: [[TMP4:%.*]] = and <2 x i32> [[TMP3]], <i32 0, i32 -1>
+; CHECK-NEXT: [[TMP5:%.*]] = extractelement <2 x i32> [[TMP4]], i32 0
+; CHECK-NEXT: [[ASHR:%.*]] = ashr i32 0, [[TMP5]]
+; CHECK-NEXT: [[TMP6:%.*]] = ashr <2 x i32> [[TMP1]], [[TMP4]]
+; CHECK-NEXT: [[TMP7:%.*]] = or <2 x i32> [[TMP1]], [[TMP4]]
+; CHECK-NEXT: [[TMP8]] = shufflevector <2 x i32> [[TMP6]], <2 x i32> [[TMP7]], <2 x i32> <i32 0, i32 3>
+; CHECK-NEXT: [[TMP9:%.*]] = shufflevector <2 x i32> [[TMP8]], <2 x i32> [[TMP1]], <2 x i32> <i32 0, i32 3>
+; CHECK-NEXT: [[TMP10:%.*]] = shufflevector <2 x i32> [[TMP3]], <2 x i32> <i32 0, i32 poison>, <2 x i32> <i32 2, i32 1>
+; CHECK-NEXT: [[TMP11]] = or <2 x i32> [[TMP9]], [[TMP10]]
+; CHECK-NEXT: br i1 false, label [[BB1:%.*]], label [[BB3]]
+;
+bb:
+ br label %bb3
+
+bb1:
+ %phi = phi i32 [ %or, %bb3 ]
+ %phi2 = phi i32 [ %ashr7, %bb3 ]
+ ret i32 0
+
+bb3:
+ %phi4 = phi i32 [ 0, %bb ], [ %or, %bb3 ]
+ %phi5 = phi i32 [ 0, %bb ], [ %or8, %bb3 ]
+ %load = load i64, ptr addrspace(1) null, align 8
+ %trunc = trunc i64 %load to i32
+ %or = or i32 %phi4, %trunc
+ %trunc6 = trunc i64 0 to i32
+ %and = and i32 %trunc6, 0
+ %ashr = ashr i32 0, %and
+ %ashr7 = ashr i32 %phi5, %and
+ %or8 = or i32 %ashr7, 0
+ br i1 false, label %bb1, label %bb3
+}
+
+attributes #0 = { "target-features"="+avx2" }
More information about the llvm-commits
mailing list