[llvm] [X86][AMX] Fix use iteration when reloading tiles in FastPreTileConfig (PR #228724)

Aochang Liu via llvm-commits llvm-commits at lists.llvm.org
Sat Oct 3 22:11:23 PDT 2026


https://github.com/HelloWorldU updated https://github.com/llvm/llvm-project/pull/228724

>From c1929f3c4f3bad83bf58501a7628aa653c01b79f Mon Sep 17 00:00:00 2001
From: HelloWorldU <asd001liu at gmail.com>
Date: Sat, 3 Oct 2026 23:30:14 +0800
Subject: [PATCH] [X86][AMX] Fix use iteration when reloading tiles in
 FastPreTileConfig

reload() rewrites the user's tile operand, which moves it off the use list
being iterated, so only the first use of a tile was reloaded. A DBG_VALUE
was also reloaded as a use; when it came first, the real use kept the
clobbered tile and FastRegAlloc crashed. Iterate the non-debug uses with
make_early_inc_range, as InlineSpiller does.

Fixes #228712

Assisted-by: Claude Opus 5.5
---
 llvm/lib/Target/X86/X86FastPreTileConfig.cpp  |  4 +-
 .../X86/AMX/amx-fastconfig-debug-use.mir      | 72 +++++++++++++++++++
 .../CodeGen/X86/AMX/amx-fastconfig-spill.mir  | 52 ++++++++++++++
 3 files changed, 127 insertions(+), 1 deletion(-)
 create mode 100644 llvm/test/CodeGen/X86/AMX/amx-fastconfig-debug-use.mir

diff --git a/llvm/lib/Target/X86/X86FastPreTileConfig.cpp b/llvm/lib/Target/X86/X86FastPreTileConfig.cpp
index f461b5e97bd59..42e6fb6098d92 100644
--- a/llvm/lib/Target/X86/X86FastPreTileConfig.cpp
+++ b/llvm/lib/Target/X86/X86FastPreTileConfig.cpp
@@ -653,7 +653,9 @@ bool X86FastPreTileConfigImpl::configBasicBlock(MachineBasicBlock &MBB) {
     Register TileReg = MI.getOperand(0).getReg();
     if (mayLiveOut(TileReg, LastTileCfg))
       spill(++MI.getIterator(), TileReg, false);
-    for (MachineInstr &UseMI : MRI->use_instructions(TileReg)) {
+    // reload() rewrites or erases UseMI.
+    for (MachineInstr &UseMI :
+         make_early_inc_range(MRI->use_nodbg_instructions(TileReg))) {
       if (UseMI.getParent() == &MBB) {
         // check user should not across ldtilecfg
         if (!LastTileCfg || !dominates(MBB, LastTileCfg, UseMI))
diff --git a/llvm/test/CodeGen/X86/AMX/amx-fastconfig-debug-use.mir b/llvm/test/CodeGen/X86/AMX/amx-fastconfig-debug-use.mir
new file mode 100644
index 0000000000000..f8759c883329b
--- /dev/null
+++ b/llvm/test/CodeGen/X86/AMX/amx-fastconfig-debug-use.mir
@@ -0,0 +1,72 @@
+# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py UTC_ARGS: --version 6
+# RUN: llc -mtriple=x86_64-- -mattr=+amx-int8,avx512f -run-pass=x86-fast-pre-tile-config -o - %s | FileCheck %s
+# RUN: llc -mtriple=x86_64-- -mattr=+amx-int8,avx512f -passes=x86-fast-pre-tile-config -o - %s | FileCheck %s
+
+# A debug use of a tile must not get a reload, and must not stop the real use
+# from getting one.
+
+--- |
+  define void @debug_use() !dbg !4 {
+    ret void
+  }
+
+  !llvm.dbg.cu = !{!0}
+  !llvm.module.flags = !{!2}
+
+  !0 = distinct !DICompileUnit(language: DW_LANG_C, file: !1, emissionKind: FullDebug)
+  !1 = !DIFile(filename: "t.c", directory: "/")
+  !2 = !{i32 2, !"Debug Info Version", i32 3}
+  !3 = !DISubroutineType(types: !{})
+  !4 = distinct !DISubprogram(name: "debug_use", scope: !1, file: !1, type: !3, spFlags: DISPFlagDefinition, unit: !0)
+  !5 = !DILocalVariable(name: "t", scope: !4, file: !1, type: !6)
+  !6 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
+  !7 = !DILocation(line: 1, scope: !4)
+...
+---
+name:            debug_use
+alignment:       16
+tracksRegLiveness: true
+frameInfo:
+  maxAlignment:    16
+stack:
+  - { id: 0, size: 1024, alignment: 16 }
+machineFunctionInfo:
+  amxProgModel: ManagedRA
+body:             |
+  ; CHECK-LABEL: name: debug_use
+  ; CHECK: bb.0:
+  ; CHECK-NEXT:   successors: %bb.1(0x80000000)
+  ; CHECK-NEXT: {{  $}}
+  ; CHECK-NEXT:   [[AVX512_128_SET0_:%[0-9]+]]:vr128x = AVX512_128_SET0
+  ; CHECK-NEXT:   [[SUBREG_TO_REG:%[0-9]+]]:vr512 = SUBREG_TO_REG [[AVX512_128_SET0_]], %subreg.sub_xmm
+  ; CHECK-NEXT:   VMOVUPSZmr %stack.2, 1, $noreg, 0, $noreg, [[SUBREG_TO_REG]] :: (store (s512) into %stack.2, align 4)
+  ; CHECK-NEXT:   MOV8mi %stack.2, 1, $noreg, 0, $noreg, 1 :: (store (s512) into %stack.2, align 4)
+  ; CHECK-NEXT:   [[MOV16ri:%[0-9]+]]:gr16 = MOV16ri 32
+  ; CHECK-NEXT:   [[MOV16ri1:%[0-9]+]]:gr16 = MOV16ri 8
+  ; CHECK-NEXT:   PLDTILECFGV %stack.2, 1, $noreg, 0, $noreg, implicit-def dead $tmm0, implicit-def dead $tmm1, implicit-def dead $tmm2, implicit-def dead $tmm3, implicit-def dead $tmm4, implicit-def dead $tmm5, implicit-def dead $tmm6, implicit-def dead $tmm7 :: (load (s512) from %stack.2, align 4)
+  ; CHECK-NEXT:   [[PTILEZEROV:%[0-9]+]]:tile = PTILEZEROV [[MOV16ri1]], [[MOV16ri]]
+  ; CHECK-NEXT:   [[MOV64ri:%[0-9]+]]:gr64_nosp = MOV64ri 64
+  ; CHECK-NEXT:   TILESTORED %stack.1, 1, killed [[MOV64ri]], 0, $noreg, [[PTILEZEROV]] :: (store (s8192) into %stack.1)
+  ; CHECK-NEXT:   [[MOV32ri64_:%[0-9]+]]:gr64_nosp = MOV32ri64 32
+  ; CHECK-NEXT:   [[LEA64r:%[0-9]+]]:gr64 = LEA64r %stack.0, 1, $noreg, 0, $noreg
+  ; CHECK-NEXT:   JMP_1 %bb.1
+  ; CHECK-NEXT: {{  $}}
+  ; CHECK-NEXT: bb.1:
+  ; CHECK-NEXT:   PLDTILECFGV %stack.2, 1, $noreg, 0, $noreg, implicit-def dead $tmm0, implicit-def dead $tmm1, implicit-def dead $tmm2, implicit-def dead $tmm3, implicit-def dead $tmm4, implicit-def dead $tmm5, implicit-def dead $tmm6, implicit-def dead $tmm7 :: (load (s512) from %stack.2, align 4)
+  ; CHECK-NEXT:   DBG_VALUE [[PTILEZEROV]], $noreg, !6, !DIExpression(), debug-location !DILocation(line: 1, scope: !3)
+  ; CHECK-NEXT:   [[MOV64ri1:%[0-9]+]]:gr64_nosp = MOV64ri 64
+  ; CHECK-NEXT:   [[PTILELOADDV:%[0-9]+]]:tile = PTILELOADDV [[MOV16ri1]], [[MOV16ri]], %stack.1, 1, killed [[MOV64ri1]], 0, $noreg :: (load (s8192) from %stack.1)
+  ; CHECK-NEXT:   PTILESTOREDV killed [[MOV16ri1]], killed [[MOV16ri]], killed [[LEA64r]], 1, killed [[MOV32ri64_]], 0, $noreg, killed [[PTILELOADDV]]
+  ; CHECK-NEXT:   RET 0
+  bb.0:
+    %0:gr16 = MOV16ri 32
+    %1:gr16 = MOV16ri 8
+    %2:tile = PTILEZEROV %1, %0
+    %3:gr64_nosp = MOV32ri64 32
+    %4:gr64 = LEA64r %stack.0, 1, $noreg, 0, $noreg
+    JMP_1 %bb.1
+  bb.1:
+    DBG_VALUE %2, $noreg, !5, !DIExpression(), debug-location !7
+    PTILESTOREDV killed %1, killed %0, killed %4, 1, killed %3, 0, $noreg, killed %2
+    RET 0
+...
diff --git a/llvm/test/CodeGen/X86/AMX/amx-fastconfig-spill.mir b/llvm/test/CodeGen/X86/AMX/amx-fastconfig-spill.mir
index d39ba1bdee348..1a8d6630fe7ff 100644
--- a/llvm/test/CodeGen/X86/AMX/amx-fastconfig-spill.mir
+++ b/llvm/test/CodeGen/X86/AMX/amx-fastconfig-spill.mir
@@ -157,3 +157,55 @@ body:             |
     RET 0, killed $eax
 
 ...
+
+# Test that every cross-block use is reloaded.
+---
+name:            two_uses
+alignment:       16
+tracksRegLiveness: true
+frameInfo:
+  maxAlignment:    16
+stack:
+  - { id: 0, size: 1024, alignment: 16 }
+machineFunctionInfo:
+  amxProgModel: ManagedRA
+body:             |
+  ; CHECK-LABEL: name: two_uses
+  ; CHECK: bb.0.entry:
+  ; CHECK-NEXT:   successors: %bb.1(0x80000000)
+  ; CHECK-NEXT: {{  $}}
+  ; CHECK-NEXT:   [[AVX512_128_SET0_:%[0-9]+]]:vr128x = AVX512_128_SET0
+  ; CHECK-NEXT:   [[SUBREG_TO_REG:%[0-9]+]]:vr512 = SUBREG_TO_REG [[AVX512_128_SET0_]], %subreg.sub_xmm
+  ; CHECK-NEXT:   VMOVUPSZmr %stack.2, 1, $noreg, 0, $noreg, [[SUBREG_TO_REG]] :: (store (s512) into %stack.2, align 4)
+  ; CHECK-NEXT:   MOV8mi %stack.2, 1, $noreg, 0, $noreg, 1 :: (store (s512) into %stack.2, align 4)
+  ; CHECK-NEXT:   [[MOV16ri:%[0-9]+]]:gr16 = MOV16ri 32
+  ; CHECK-NEXT:   [[MOV16ri1:%[0-9]+]]:gr16 = MOV16ri 8
+  ; CHECK-NEXT:   PLDTILECFGV %stack.2, 1, $noreg, 0, $noreg, implicit-def dead $tmm0, implicit-def dead $tmm1, implicit-def dead $tmm2, implicit-def dead $tmm3, implicit-def dead $tmm4, implicit-def dead $tmm5, implicit-def dead $tmm6, implicit-def dead $tmm7 :: (load (s512) from %stack.2, align 4)
+  ; CHECK-NEXT:   [[PTILEZEROV:%[0-9]+]]:tile = PTILEZEROV [[MOV16ri1]], [[MOV16ri]]
+  ; CHECK-NEXT:   [[MOV64ri:%[0-9]+]]:gr64_nosp = MOV64ri 64
+  ; CHECK-NEXT:   TILESTORED %stack.1, 1, killed [[MOV64ri]], 0, $noreg, [[PTILEZEROV]] :: (store (s8192) into %stack.1)
+  ; CHECK-NEXT:   [[MOV32ri64_:%[0-9]+]]:gr64_nosp = MOV32ri64 32
+  ; CHECK-NEXT:   [[LEA64r:%[0-9]+]]:gr64 = LEA64r %stack.0, 1, $noreg, 0, $noreg
+  ; CHECK-NEXT:   JMP_1 %bb.1
+  ; CHECK-NEXT: {{  $}}
+  ; CHECK-NEXT: bb.1:
+  ; CHECK-NEXT:   PLDTILECFGV %stack.2, 1, $noreg, 0, $noreg, implicit-def dead $tmm0, implicit-def dead $tmm1, implicit-def dead $tmm2, implicit-def dead $tmm3, implicit-def dead $tmm4, implicit-def dead $tmm5, implicit-def dead $tmm6, implicit-def dead $tmm7 :: (load (s512) from %stack.2, align 4)
+  ; CHECK-NEXT:   [[MOV64ri1:%[0-9]+]]:gr64_nosp = MOV64ri 64
+  ; CHECK-NEXT:   [[PTILELOADDV:%[0-9]+]]:tile = PTILELOADDV [[MOV16ri1]], [[MOV16ri]], %stack.1, 1, killed [[MOV64ri1]], 0, $noreg :: (load (s8192) from %stack.1)
+  ; CHECK-NEXT:   PTILESTOREDV [[MOV16ri1]], [[MOV16ri]], [[LEA64r]], 1, [[MOV32ri64_]], 0, $noreg, [[PTILELOADDV]]
+  ; CHECK-NEXT:   [[MOV64ri2:%[0-9]+]]:gr64_nosp = MOV64ri 64
+  ; CHECK-NEXT:   [[PTILELOADDV1:%[0-9]+]]:tile = PTILELOADDV [[MOV16ri1]], [[MOV16ri]], %stack.1, 1, killed [[MOV64ri2]], 0, $noreg :: (load (s8192) from %stack.1)
+  ; CHECK-NEXT:   PTILESTOREDV killed [[MOV16ri1]], killed [[MOV16ri]], killed [[LEA64r]], 1, killed [[MOV32ri64_]], 0, $noreg, killed [[PTILELOADDV1]]
+  ; CHECK-NEXT:   RET 0
+  bb.0.entry:
+    %0:gr16 = MOV16ri 32
+    %1:gr16 = MOV16ri 8
+    %2:tile = PTILEZEROV %1, %0
+    %3:gr64_nosp = MOV32ri64 32
+    %4:gr64 = LEA64r %stack.0, 1, $noreg, 0, $noreg
+    JMP_1 %bb.1
+  bb.1:
+    PTILESTOREDV %1, %0, %4, 1, %3, 0, $noreg, %2
+    PTILESTOREDV killed %1, killed %0, killed %4, 1, killed %3, 0, $noreg, killed %2
+    RET 0
+...



More information about the llvm-commits mailing list