[llvm] [CodeGen][AArch64] Avoid duplicate hints in register allocation (PR #219007)
Nick Desaulniers via llvm-commits
llvm-commits at lists.llvm.org
Fri Aug 28 13:05:53 PDT 2026
https://github.com/nickdesaulniers updated https://github.com/llvm/llvm-project/pull/219007
>From f55333b7225fa5eb330a44ccafc969a8c7cb11b4 Mon Sep 17 00:00:00 2001
From: Nick Desaulniers <ndesaulniers at google.com>
Date: Wed, 29 Jul 2026 14:33:45 -0700
Subject: [PATCH] [CodeGen][AArch64] Avoid duplicate hints in register
allocation
Duplicate register allocation hints cause AllocationOrder to yield
duplicate physical register candidates. For fixed-capacity eviction
advisors like MLEvictAdvisor (MaxInterferences = 32), this leads to
out-of-bounds array indexing and assertion failures.
In commit 0756e5985f0c, TargetRegisterInfo::getRegAllocationHints was
hoisted to the top of AArch64RegisterInfo::getRegAllocationHints, but
fallthrough paths invoked it a second time on an already-populated Hints
vector, duplicating copy hints for standard GPRs.
This patch:
1. Returns ConsiderOnlyHints on fallthrough in
AArch64RegisterInfo::getRegAllocationHints instead of calling
TargetRegisterInfo::getRegAllocationHints a second time.
2. In TargetRegisterInfo::getRegAllocationHints, deduplicates directly
against Hints instead of using a transient local set.
3. In AllocationOrder::create, deduplicates Hints in O(N) while
preserving priority order, and asserts that Hints contains no
duplicates.
4. Adds regression test coverage for AArch64 MLRegAlloc eviction
advisor.
Assisted-by: Gemini
Fixes: 0756e5985f0c ("[AArch64] Hint regalloc to choose distinct predicate for MATCH/CMP (#190139)")
Fixes: https://github.com/llvm/llvm-project/issues/193011
---
llvm/lib/CodeGen/AllocationOrder.cpp | 11 ++++
llvm/lib/CodeGen/TargetRegisterInfo.cpp | 14 ++---
.../Target/AArch64/AArch64RegisterInfo.cpp | 9 ++--
.../aarch64-evict-advisor-duplicate-hints.ll | 54 +++++++++++++++++++
4 files changed, 76 insertions(+), 12 deletions(-)
create mode 100644 llvm/test/CodeGen/MLRegAlloc/aarch64-evict-advisor-duplicate-hints.ll
diff --git a/llvm/lib/CodeGen/AllocationOrder.cpp b/llvm/lib/CodeGen/AllocationOrder.cpp
index 183dc8af1b91b..6bbbb7d49bbe0 100644
--- a/llvm/lib/CodeGen/AllocationOrder.cpp
+++ b/llvm/lib/CodeGen/AllocationOrder.cpp
@@ -36,6 +36,14 @@ AllocationOrder AllocationOrder::create(Register VirtReg, const VirtRegMap &VRM,
bool HardHints =
TRI->getRegAllocationHints(VirtReg, Order, Hints, MF, &VRM, Matrix);
+ // TODO: Consider changing the interface of
+ // TargetRegisterInfo::getRegAllocationHints to take a SetVector to enforce
+ // uniqueness at the API boundary. Ensure hints are unique while preserving
+ // priority order.
+ SmallDenseSet<MCPhysReg, 16> Seen;
+ llvm::erase_if(Hints,
+ [&](MCPhysReg Reg) { return !Seen.insert(Reg).second; });
+
LLVM_DEBUG({
if (!Hints.empty()) {
dbgs() << "hints:";
@@ -47,5 +55,8 @@ AllocationOrder AllocationOrder::create(Register VirtReg, const VirtRegMap &VRM,
assert(all_of(Hints,
[&](MCPhysReg Hint) { return is_contained(Order, Hint); }) &&
"Target hint is outside allocation order.");
+ assert(
+ all_of(Hints, [&](MCPhysReg Hint) { return count(Hints, Hint) == 1; }) &&
+ "Target hints contain duplicates.");
return AllocationOrder(std::move(Hints), Order, HardHints);
}
diff --git a/llvm/lib/CodeGen/TargetRegisterInfo.cpp b/llvm/lib/CodeGen/TargetRegisterInfo.cpp
index 3b29b0863dd6c..c55c67fd4e7ad 100644
--- a/llvm/lib/CodeGen/TargetRegisterInfo.cpp
+++ b/llvm/lib/CodeGen/TargetRegisterInfo.cpp
@@ -14,7 +14,6 @@
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/BitVector.h"
#include "llvm/ADT/STLExtras.h"
-#include "llvm/ADT/SmallSet.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/BinaryFormat/Dwarf.h"
#include "llvm/CodeGen/LiveInterval.h"
@@ -408,7 +407,6 @@ bool TargetRegisterInfo::getRegAllocationHints(
if (!Hints_MRI)
return false;
- SmallSet<Register, 32> HintedRegs;
// First hint may be a target hint.
bool Skip = (Hints_MRI->first != 0);
for (auto Reg : Hints_MRI->second) {
@@ -422,10 +420,6 @@ bool TargetRegisterInfo::getRegAllocationHints(
if (VRM && Phys.isVirtual())
Phys = VRM->getPhys(Phys);
- // Don't add the same reg twice (Hints_MRI may contain multiple virtual
- // registers allocated to the same physreg).
- if (!HintedRegs.insert(Phys).second)
- continue;
// Check that Phys is a valid hint in VirtReg's register class.
if (!Phys.isPhysical())
continue;
@@ -437,7 +431,15 @@ bool TargetRegisterInfo::getRegAllocationHints(
if (!is_contained(Order, Phys))
continue;
+ // Don't add the same reg twice (Hints_MRI may contain multiple virtual
+ // registers allocated to the same physreg, or Hints may already contain
+ // it).
+ if (is_contained(Hints, Phys.id()))
+ continue;
+
// All clear, tell the register allocator to prefer this register.
+ assert(!is_contained(Hints, Phys.id()) &&
+ "TargetRegisterInfo::getRegAllocationHints duplicate hint!");
Hints.push_back(Phys.id());
}
return false;
diff --git a/llvm/lib/Target/AArch64/AArch64RegisterInfo.cpp b/llvm/lib/Target/AArch64/AArch64RegisterInfo.cpp
index 502c91fe3e531..dbc6475899e81 100644
--- a/llvm/lib/Target/AArch64/AArch64RegisterInfo.cpp
+++ b/llvm/lib/Target/AArch64/AArch64RegisterInfo.cpp
@@ -1266,8 +1266,7 @@ bool AArch64RegisterInfo::getRegAllocationHints(
return ConsiderOnlyHints;
if (!ST.hasSME() || !ST.isStreaming())
- return TargetRegisterInfo::getRegAllocationHints(VirtReg, Order, Hints, MF,
- VRM);
+ return ConsiderOnlyHints;
// The SVE calling convention preserves registers Z8-Z23. As a result, there
// are no ZPR2Strided or ZPR4Strided registers that do not overlap with the
@@ -1409,8 +1408,7 @@ bool AArch64RegisterInfo::getRegAllocationHints(
}
if (!Hints.empty())
- return TargetRegisterInfo::getRegAllocationHints(VirtReg, Order, Hints,
- MF, VRM);
+ return ConsiderOnlyHints;
}
}
@@ -1441,8 +1439,7 @@ bool AArch64RegisterInfo::getRegAllocationHints(
}
}
- return TargetRegisterInfo::getRegAllocationHints(VirtReg, Order, Hints, MF,
- VRM);
+ return ConsiderOnlyHints;
}
unsigned AArch64RegisterInfo::getLocalAddressRegister(
diff --git a/llvm/test/CodeGen/MLRegAlloc/aarch64-evict-advisor-duplicate-hints.ll b/llvm/test/CodeGen/MLRegAlloc/aarch64-evict-advisor-duplicate-hints.ll
new file mode 100644
index 0000000000000..67895b3d199cf
--- /dev/null
+++ b/llvm/test/CodeGen/MLRegAlloc/aarch64-evict-advisor-duplicate-hints.ll
@@ -0,0 +1,54 @@
+; REQUIRES: have_tf_aot
+; REQUIRES: aarch64-registered-target
+;
+; Check that AArch64 virtual registers with copy hints do not get duplicate
+; physical register hints added to AllocationOrder, which would otherwise lead
+; to out-of-bounds indexing in MLEvictAdvisor.
+;
+; RUN: llc -mtriple=aarch64-linux-gnu -regalloc=greedy \
+; RUN: -regalloc-enable-advisor=release < %s -o /dev/null
+
+%struct.Flags = type { i8, i32, i8, i8, i8, i8, i8, i32, i8 }
+
+ at asan_flags_dont_use_directly = external global %struct.Flags
+
+define void @_Z15InitializeFlagsv(ptr %0, ptr %1) {
+ store i32 1, ptr getelementptr inbounds nuw (i8, ptr @asan_flags_dont_use_directly, i64 4), align 4
+ %3 = load i8, ptr getelementptr inbounds nuw (i8, ptr @asan_flags_dont_use_directly, i64 9), align 1
+ store i8 %3, ptr getelementptr inbounds nuw (i8, ptr @asan_flags_dont_use_directly, i64 8), align 4
+ %4 = load i8, ptr getelementptr inbounds nuw (i8, ptr @asan_flags_dont_use_directly, i64 12), align 4
+ store i8 %4, ptr getelementptr inbounds nuw (i8, ptr @asan_flags_dont_use_directly, i64 11), align 1
+ store i8 1, ptr getelementptr inbounds nuw (i8, ptr @asan_flags_dont_use_directly, i64 10), align 2
+ %5 = load i8, ptr getelementptr inbounds nuw (i8, ptr @asan_flags_dont_use_directly, i64 20), align 4
+ %6 = zext i8 %5 to i32
+ store i32 %6, ptr getelementptr inbounds nuw (i8, ptr @asan_flags_dont_use_directly, i64 16), align 4
+ %7 = tail call ptr @_Znwmi(i64 16, i32 0)
+ store ptr null, ptr %0, align 8
+ store ptr null, ptr %7, align 8
+ store ptr null, ptr @_Z26GetGlobalLowLevelAllocatorv, align 8
+ %8 = tail call ptr @_Znwmi(i64 0, i32 0)
+ store ptr @asan_flags_dont_use_directly, ptr %0, align 8
+ %9 = tail call ptr @_Znwmi(i64 0, i32 0)
+ store ptr getelementptr inbounds nuw (i8, ptr @asan_flags_dont_use_directly, i64 4), ptr %9, align 8
+ %10 = load i32, ptr null, align 4
+ %11 = tail call ptr @_Znwmi(i64 0, i32 %10)
+ store ptr getelementptr inbounds nuw (i8, ptr @asan_flags_dont_use_directly, i64 8), ptr %0, align 8
+ %12 = load i32, ptr null, align 4
+ %13 = tail call ptr @_Znwmi(i64 0, i32 %12)
+ store ptr getelementptr inbounds nuw (i8, ptr @asan_flags_dont_use_directly, i64 9), ptr @_Znwmi, align 8
+ store ptr getelementptr inbounds nuw (i8, ptr @asan_flags_dont_use_directly, i64 10), ptr %1, align 8
+ store ptr getelementptr inbounds nuw (i8, ptr @asan_flags_dont_use_directly, i64 11), ptr %0, align 8
+ %14 = tail call ptr @_Znwmi(i64 0, i32 0)
+ store ptr getelementptr inbounds nuw (i8, ptr @asan_flags_dont_use_directly, i64 12), ptr %0, align 8
+ %15 = tail call ptr @_Znwmi(i64 0, i32 0)
+ store ptr getelementptr inbounds nuw (i8, ptr @asan_flags_dont_use_directly, i64 16), ptr %1, align 8
+ store ptr getelementptr inbounds nuw (i8, ptr @asan_flags_dont_use_directly, i64 20), ptr %0, align 8
+ ret void
+}
+
+declare i32 @_Z26GetGlobalLowLevelAllocatorv()
+
+declare ptr @_Znwmi(i64, i32)
+
+; uselistorder directives
+uselistorder ptr @_Znwmi, { 7, 6, 5, 4, 3, 2, 1, 0 }
More information about the llvm-commits
mailing list