[llvm] [X86][GlobalISel] Narrow i64 G_PHI on 32-bit targets (PR #225264)
Demetrios Chiuratto Agourakis via llvm-commits
llvm-commits at lists.llvm.org
Tue Sep 22 11:48:18 PDT 2026
https://github.com/agourakis82 updated https://github.com/llvm/llvm-project/pull/225264
>From 71cf982ec439acad1757eace6f01a6bd789cf441 Mon Sep 17 00:00:00 2001
From: Demetrios Chiuratto Agourakis <demetrios at agourakis.med.br>
Date: Tue, 22 Sep 2026 02:02:56 +0000
Subject: [PATCH 1/2] [X86][GlobalISel] Narrow i64 G_PHI on 32-bit targets
G_PHI was unconditionally legal for s64, including i386. After IRTranslator
built an s64 PHI and a trailing G_UNMERGE_VALUES, instruction selection
failed because scalar EXTRACT/INSERT subreg lowering is unimplemented for
s64 GPRs on 32-bit.
Split G_PHI from the IMPLICIT_DEF/FREEZE legality set and only allow s64
PHIs on 64-bit targets so the legalizer narrows them to a pair of s32 PHIs.
Fixes #216648
---
.../lib/Target/X86/GISel/X86LegalizerInfo.cpp | 27 +++++++++++-
.../CodeGen/X86/GlobalISel/phi-i64-i686.ll | 42 +++++++++++++++++++
2 files changed, 67 insertions(+), 2 deletions(-)
create mode 100644 llvm/test/CodeGen/X86/GlobalISel/phi-i64-i686.ll
diff --git a/llvm/lib/Target/X86/GISel/X86LegalizerInfo.cpp b/llvm/lib/Target/X86/GISel/X86LegalizerInfo.cpp
index 78ab184af6c192..27cec7ef3a43e7 100644
--- a/llvm/lib/Target/X86/GISel/X86LegalizerInfo.cpp
+++ b/llvm/lib/Target/X86/GISel/X86LegalizerInfo.cpp
@@ -91,8 +91,10 @@ X86LegalizerInfo::X86LegalizerInfo(const X86Subtarget &STI,
// 32/64-bits needs support for s64/s128 to handle cases:
// s64 = EXTEND (G_IMPLICIT_DEF s32) -> s64 = G_IMPLICIT_DEF
// s128 = EXTEND (G_IMPLICIT_DEF s32/s64) -> s128 = G_IMPLICIT_DEF
- getActionDefinitionsBuilder(
- {G_IMPLICIT_DEF, G_PHI, G_FREEZE, G_CONSTANT_FOLD_BARRIER})
+ // Keep G_IMPLICIT_DEF/G_FREEZE able to materialize s64 even on i386, but do
+ // not treat multi-block G_PHI of s64 as legal there: the selector cannot
+ // EXTRACT/INSERT scalar subregs of an s64 GPR on 32-bit (#216648).
+ getActionDefinitionsBuilder({G_IMPLICIT_DEF, G_FREEZE, G_CONSTANT_FOLD_BARRIER})
.legalFor({p0, s1, s8, s16, s32, s64})
.legalFor(UseX87, {s80})
.legalFor(Is64Bit, {s128})
@@ -111,6 +113,27 @@ X86LegalizerInfo::X86LegalizerInfo(const X86Subtarget &STI,
: s32MaxVector.getNumElements())
.scalarizeIf(scalarOrEltWiderThan(0, 64), 0);
+ // On i386, keep s64 out of G_PHI: narrow to a pair of s32 PHIs instead of
+ // leaving an s64 value that later fails G_UNMERGE/G_EXTRACT selection.
+ getActionDefinitionsBuilder(G_PHI)
+ .legalFor({p0, s1, s8, s16, s32})
+ .legalFor(Is64Bit, {s64, s128})
+ .legalFor(UseX87, {s80})
+ .legalFor(HasSSE2, {v16s8, v8s16, v4s32, v2s64})
+ .legalFor(HasAVX, {v32s8, v16s16, v8s32, v4s64})
+ .legalFor(HasAVX512, {v64s8, v32s16, v16s32, v8s64})
+ .widenScalarOrEltToNextPow2(0, /*Min=*/8)
+ .clampScalarOrElt(0, s8, sMaxScalar)
+ .moreElementsToNextPow2(0)
+ .clampNumElements(0, v16s8, s8MaxVector)
+ .clampNumElements(0, v8s16, s16MaxVector)
+ .clampNumElements(0, v4s32, s32MaxVector)
+ .clampNumElements(0, v2s64, s64MaxVector)
+ .clampMaxNumElements(0, p0,
+ Is64Bit ? s64MaxVector.getNumElements()
+ : s32MaxVector.getNumElements())
+ .scalarizeIf(scalarOrEltWiderThan(0, 64), 0);
+
getActionDefinitionsBuilder(G_CONSTANT)
.legalFor({p0, s8, s16, s32})
.legalFor(Is64Bit, {s64})
diff --git a/llvm/test/CodeGen/X86/GlobalISel/phi-i64-i686.ll b/llvm/test/CodeGen/X86/GlobalISel/phi-i64-i686.ll
new file mode 100644
index 00000000000000..4b6b3b2998e495
--- /dev/null
+++ b/llvm/test/CodeGen/X86/GlobalISel/phi-i64-i686.ll
@@ -0,0 +1,42 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
+; RUN: llc -mtriple=i686-unknown-linux-gnu -global-isel -O0 < %s | FileCheck %s
+
+; Multi-block i64 PHI must be narrowed to a pair of i32 PHIs on i386 GlobalISel.
+; Previously this failed selection of G_UNMERGE_VALUES from an s64 GPR (#216648).
+
+define i64 @plain_phi_i64(i64 %x, i64 %y, i1 %c) {
+; CHECK-LABEL: plain_phi_i64:
+; CHECK: # %bb.1:
+; CHECK: testb
+; CHECK: # %bb.4:
+; CHECK: movl
+; CHECK: movl
+; CHECK: retl
+entry:
+ br i1 %c, label %a, label %b
+a:
+ br label %tail
+b:
+ br label %tail
+tail:
+ %r = phi i64 [ %x, %a ], [ %y, %b ]
+ ret i64 %r
+}
+
+define i64 @or_xor_loop(i64 %x, i64 %y, i32 %n) {
+; CHECK-LABEL: or_xor_loop:
+; CHECK: # %bb.
+; CHECK: retl
+entry:
+ br label %loop
+loop:
+ %acc = phi i64 [ 0, %entry ], [ %next, %loop ]
+ %i = phi i32 [ 0, %entry ], [ %i.next, %loop ]
+ %o = or i64 %acc, %x
+ %next = xor i64 %o, %y
+ %i.next = add i32 %i, 1
+ %cond = icmp slt i32 %i.next, %n
+ br i1 %cond, label %loop, label %exit
+exit:
+ ret i64 %next
+}
>From 956916c78507f4b48240943f493860ee19bc680a Mon Sep 17 00:00:00 2001
From: Demetrios Chiuratto Agourakis <demetrios at agourakis.med.br>
Date: Tue, 22 Sep 2026 18:47:16 +0000
Subject: [PATCH 2/2] [X86][GlobalISel] clang-format G_PHI legality split
Assisted-by: Claude
---
llvm/lib/Target/X86/GISel/X86LegalizerInfo.cpp | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/llvm/lib/Target/X86/GISel/X86LegalizerInfo.cpp b/llvm/lib/Target/X86/GISel/X86LegalizerInfo.cpp
index 27cec7ef3a43e7..fd088fffaf22c8 100644
--- a/llvm/lib/Target/X86/GISel/X86LegalizerInfo.cpp
+++ b/llvm/lib/Target/X86/GISel/X86LegalizerInfo.cpp
@@ -94,7 +94,8 @@ X86LegalizerInfo::X86LegalizerInfo(const X86Subtarget &STI,
// Keep G_IMPLICIT_DEF/G_FREEZE able to materialize s64 even on i386, but do
// not treat multi-block G_PHI of s64 as legal there: the selector cannot
// EXTRACT/INSERT scalar subregs of an s64 GPR on 32-bit (#216648).
- getActionDefinitionsBuilder({G_IMPLICIT_DEF, G_FREEZE, G_CONSTANT_FOLD_BARRIER})
+ getActionDefinitionsBuilder(
+ {G_IMPLICIT_DEF, G_FREEZE, G_CONSTANT_FOLD_BARRIER})
.legalFor({p0, s1, s8, s16, s32, s64})
.legalFor(UseX87, {s80})
.legalFor(Is64Bit, {s128})
More information about the llvm-commits
mailing list