[llvm] [PowerPC] Fix i128 vcmpequb optimization for loads with range metadata and small constants (PR #196801)

Amy Kwan via llvm-commits llvm-commits at lists.llvm.org
Sun May 10 06:43:28 PDT 2026


https://github.com/amy-kwan updated https://github.com/llvm/llvm-project/pull/196801

>From 3ea0779db2fa118c615c38ad2d418af477579b5f Mon Sep 17 00:00:00 2001
From: Amy Kwan <amy.kwan1 at ibm.com>
Date: Sat, 9 May 2026 01:32:23 -0400
Subject: [PATCH 1/2] [PowerPC] Fix i128 vcmpequb optimization for loads with
 range metadata and small constants

The combine introduced in 55aff64d2c6ef50d2ed725d7dd1fb34080486237 lowers scalar
i128 compares into vector compares by reissuing the original
loads as v16i8 loads. However, the combine was reusing the original
MachineMemOperand without modification.

If the original i128 load carries !range metadata, the MMO encodes that
range using i128 values. Reusing this MMO for a v16i8 load is incorrect as
range metadata is only valid for integer scalar types and its bitwidth
must match the memory VT.

This patch fixes this by creating a new MachineMemOperand for the vector
vector load. Additionally, we restrict the combine for constant operands
to avoid cases that are better handled by scalar lowering. Small constants
(fit within 16 bits) are excluded to prevent generating suboptimal vector compares.
---
 llvm/lib/Target/PowerPC/PPCISelLowering.cpp |  27 ++-
 llvm/test/CodeGen/PowerPC/ppc-i128-cmp.ll   | 210 ++++++++++++++++++++
 2 files changed, 231 insertions(+), 6 deletions(-)
 create mode 100644 llvm/test/CodeGen/PowerPC/ppc-i128-cmp.ll

diff --git a/llvm/lib/Target/PowerPC/PPCISelLowering.cpp b/llvm/lib/Target/PowerPC/PPCISelLowering.cpp
index e959100d713dd..a5fc479292717 100644
--- a/llvm/lib/Target/PowerPC/PPCISelLowering.cpp
+++ b/llvm/lib/Target/PowerPC/PPCISelLowering.cpp
@@ -15797,8 +15797,14 @@ static bool canConvertToVcmpequb(SDValue &LHS, SDValue &RHS) {
     if (Operand.getValueType() != MVT::i128)
       return false;
 
-    if (Operand.getOpcode() == ISD::Constant)
-      return true;
+    if (Operand.getOpcode() == ISD::Constant) {
+      auto *C = cast<ConstantSDNode>(Operand);
+      const APInt &Val = C->getAPIntValue();
+      if (Val.ult(1ULL << 16))
+        return false;
+      else
+        return true;
+    }
 
     auto *LoadNode = dyn_cast<LoadSDNode>(Operand);
     if (!LoadNode)
@@ -15849,10 +15855,19 @@ SDValue convertTwoLoadsAndCmpToVCMPEQUB(SelectionDAG &DAG, SDNode *N,
     assert(Operand.getOpcode() == ISD::LOAD && "Must be LoadSDNode here.");
 
     auto *LoadNode = cast<LoadSDNode>(Operand);
-    SDValue NewLoad =
-        DAG.getLoad(MVT::v16i8, DL, LoadNode->getChain(),
-                    LoadNode->getBasePtr(), LoadNode->getMemOperand());
-    DAG.ReplaceAllUsesOfValueWith(Operand.getValue(1), NewLoad.getValue(1));
+    // Create a new MachineMemOperand without range metadata.
+    // Range metadata is only valid for integer scalar types, not vectors.
+    // The original i128 load may have range metadata, but when we convert
+    // to v16i8, that metadata is no longer semantically valid.
+    MachineMemOperand *MMO = LoadNode->getMemOperand();
+    MachineFunction &MF = DAG.getMachineFunction();
+    MachineMemOperand *NewMMO = MF.getMachineMemOperand(
+        MMO->getPointerInfo(), MMO->getFlags(), MMO->getSize(),
+        MMO->getAlign(), MMO->getAAInfo(), nullptr, MMO->getSyncScopeID(),
+        MMO->getSuccessOrdering(), MMO->getFailureOrdering());
+    SDValue NewLoad = DAG.getLoad(MVT::v16i8, DL, LoadNode->getChain(),
+                                   LoadNode->getBasePtr(), NewMMO);
+    DAG.ReplaceAllUsesOfValueWith(SDValue(LoadNode, 1), NewLoad.getValue(1));
     return NewLoad;
   };
 
diff --git a/llvm/test/CodeGen/PowerPC/ppc-i128-cmp.ll b/llvm/test/CodeGen/PowerPC/ppc-i128-cmp.ll
new file mode 100644
index 0000000000000..29b4c076bcadf
--- /dev/null
+++ b/llvm/test/CodeGen/PowerPC/ppc-i128-cmp.ll
@@ -0,0 +1,210 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 6
+; RUN: llc -mcpu=pwr8 -ppc-asm-full-reg-names -mtriple=powerpc64-ibm-aix < %s | \
+; RUN:   FileCheck %s --check-prefix=CHECK-AIX
+; RUN: llc -mcpu=pwr8 -ppc-asm-full-reg-names -mtriple=powerpc64le-unknown-linux-gnu < %s | \
+; RUN:   FileCheck %s --check-prefix=CHECK-LINUX
+
+define i1 @test1() {
+; CHECK-AIX-LABEL: test1:
+; CHECK-AIX:       # %bb.0: # %bb
+; CHECK-AIX-NEXT:    ld r3, 0(0)
+; CHECK-AIX-NEXT:    ld r4, 8(0)
+; CHECK-AIX-NEXT:    or r3, r4, r3
+; CHECK-AIX-NEXT:    cntlzd r3, r3
+; CHECK-AIX-NEXT:    rldicl r3, r3, 58, 63
+; CHECK-AIX-NEXT:    blr
+;
+; CHECK-LINUX-LABEL: test1:
+; CHECK-LINUX:       # %bb.0: # %bb
+; CHECK-LINUX-NEXT:    ld r3, 8(0)
+; CHECK-LINUX-NEXT:    ld r4, 0(0)
+; CHECK-LINUX-NEXT:    or r3, r4, r3
+; CHECK-LINUX-NEXT:    cntlzd r3, r3
+; CHECK-LINUX-NEXT:    rldicl r3, r3, 58, 63
+; CHECK-LINUX-NEXT:    blr
+bb:
+  %load = load i128, ptr null, align 16
+  %icmp = icmp eq i128 %load, 0
+  ret i1 %icmp
+}
+
+define i1 @test2() {
+; CHECK-AIX-LABEL: test2:
+; CHECK-AIX:       # %bb.0: # %bb
+; CHECK-AIX-NEXT:    ld r4, 8(0)
+; CHECK-AIX-NEXT:    ld r3, 0(0)
+; CHECK-AIX-NEXT:    xori r4, r4, 10
+; CHECK-AIX-NEXT:    or r3, r4, r3
+; CHECK-AIX-NEXT:    cntlzd r3, r3
+; CHECK-AIX-NEXT:    rldicl r3, r3, 58, 63
+; CHECK-AIX-NEXT:    blr
+;
+; CHECK-LINUX-LABEL: test2:
+; CHECK-LINUX:       # %bb.0: # %bb
+; CHECK-LINUX-NEXT:    ld r4, 0(0)
+; CHECK-LINUX-NEXT:    ld r3, 8(0)
+; CHECK-LINUX-NEXT:    xori r4, r4, 10
+; CHECK-LINUX-NEXT:    or r3, r4, r3
+; CHECK-LINUX-NEXT:    cntlzd r3, r3
+; CHECK-LINUX-NEXT:    rldicl r3, r3, 58, 63
+; CHECK-LINUX-NEXT:    blr
+bb:
+  %load = load i128, ptr null, align 16
+  %icmp = icmp eq i128 %load, 10
+  ret i1 %icmp
+}
+
+define i1 @test3() {
+; CHECK-AIX-LABEL: test3:
+; CHECK-AIX:       # %bb.0: # %bb
+; CHECK-AIX-NEXT:    ld r4, 8(0)
+; CHECK-AIX-NEXT:    ld r3, 0(0)
+; CHECK-AIX-NEXT:    xori r4, r4, 65535
+; CHECK-AIX-NEXT:    or r3, r4, r3
+; CHECK-AIX-NEXT:    cntlzd r3, r3
+; CHECK-AIX-NEXT:    rldicl r3, r3, 58, 63
+; CHECK-AIX-NEXT:    blr
+;
+; CHECK-LINUX-LABEL: test3:
+; CHECK-LINUX:       # %bb.0: # %bb
+; CHECK-LINUX-NEXT:    ld r4, 0(0)
+; CHECK-LINUX-NEXT:    ld r3, 8(0)
+; CHECK-LINUX-NEXT:    xori r4, r4, 65535
+; CHECK-LINUX-NEXT:    or r3, r4, r3
+; CHECK-LINUX-NEXT:    cntlzd r3, r3
+; CHECK-LINUX-NEXT:    rldicl r3, r3, 58, 63
+; CHECK-LINUX-NEXT:    blr
+bb:
+  %load = load i128, ptr null, align 16
+  %icmp = icmp eq i128 %load, 65535
+  ret i1 %icmp
+}
+
+define i1 @test4() {
+; CHECK-AIX-LABEL: test4:
+; CHECK-AIX:       # %bb.0: # %bb
+; CHECK-AIX-NEXT:    li r3, 0
+; CHECK-AIX-NEXT:    lxvw4x vs34, 0, r3
+; CHECK-AIX-NEXT:    ld r3, L..C0(r2) # %const.0
+; CHECK-AIX-NEXT:    lxvd2x vs35, 0, r3
+; CHECK-AIX-NEXT:    vcmpequb. v2, v2, v3
+; CHECK-AIX-NEXT:    mfocrf r3, 2
+; CHECK-AIX-NEXT:    rlwinm r3, r3, 25, 31, 31
+; CHECK-AIX-NEXT:    blr
+;
+; CHECK-LINUX-LABEL: test4:
+; CHECK-LINUX:       # %bb.0: # %bb
+; CHECK-LINUX-NEXT:    li r3, 0
+; CHECK-LINUX-NEXT:    lxvd2x vs34, 0, r3
+; CHECK-LINUX-NEXT:    addis r3, r2, .LCPI3_0 at toc@ha
+; CHECK-LINUX-NEXT:    addi r3, r3, .LCPI3_0 at toc@l
+; CHECK-LINUX-NEXT:    lxvd2x vs35, 0, r3
+; CHECK-LINUX-NEXT:    vcmpequb. v2, v2, v3
+; CHECK-LINUX-NEXT:    mfocrf r3, 2
+; CHECK-LINUX-NEXT:    rlwinm r3, r3, 25, 31, 31
+; CHECK-LINUX-NEXT:    blr
+bb:
+  %load = load i128, ptr null, align 16
+  %icmp = icmp eq i128 %load, 65536
+  ret i1 %icmp
+}
+
+; Test using the !range metadata
+define i1 @test5() {
+; CHECK-AIX-LABEL: test5:
+; CHECK-AIX:       # %bb.0: # %bb
+; CHECK-AIX-NEXT:    ld r3, 0(0)
+; CHECK-AIX-NEXT:    ld r4, 8(0)
+; CHECK-AIX-NEXT:    or r3, r4, r3
+; CHECK-AIX-NEXT:    cntlzd r3, r3
+; CHECK-AIX-NEXT:    rldicl r3, r3, 58, 63
+; CHECK-AIX-NEXT:    blr
+;
+; CHECK-LINUX-LABEL: test5:
+; CHECK-LINUX:       # %bb.0: # %bb
+; CHECK-LINUX-NEXT:    ld r3, 8(0)
+; CHECK-LINUX-NEXT:    ld r4, 0(0)
+; CHECK-LINUX-NEXT:    or r3, r4, r3
+; CHECK-LINUX-NEXT:    cntlzd r3, r3
+; CHECK-LINUX-NEXT:    rldicl r3, r3, 58, 63
+; CHECK-LINUX-NEXT:    blr
+bb:
+  %load = load i128, ptr null, align 16, !range !0
+  %icmp = icmp eq i128 %load, 0
+  ret i1 %icmp
+}
+
+define i1 @test6() {
+; CHECK-AIX-LABEL: test6:
+; CHECK-AIX:       # %bb.0: # %bb
+; CHECK-AIX-NEXT:    ld r4, 8(0)
+; CHECK-AIX-NEXT:    ld r3, 0(0)
+; CHECK-AIX-NEXT:    xori r4, r4, 65535
+; CHECK-AIX-NEXT:    or r3, r4, r3
+; CHECK-AIX-NEXT:    cntlzd r3, r3
+; CHECK-AIX-NEXT:    rldicl r3, r3, 58, 63
+; CHECK-AIX-NEXT:    blr
+;
+; CHECK-LINUX-LABEL: test6:
+; CHECK-LINUX:       # %bb.0: # %bb
+; CHECK-LINUX-NEXT:    ld r4, 0(0)
+; CHECK-LINUX-NEXT:    ld r3, 8(0)
+; CHECK-LINUX-NEXT:    xori r4, r4, 65535
+; CHECK-LINUX-NEXT:    or r3, r4, r3
+; CHECK-LINUX-NEXT:    cntlzd r3, r3
+; CHECK-LINUX-NEXT:    rldicl r3, r3, 58, 63
+; CHECK-LINUX-NEXT:    blr
+bb:
+  %load = load i128, ptr null, align 16, !range !1
+  %icmp = icmp eq i128 %load, 65535
+  ret i1 %icmp
+}
+
+define i1 @test7() {
+; CHECK-AIX-LABEL: test7:
+; CHECK-AIX:       # %bb.0: # %bb
+; CHECK-AIX-NEXT:    li r3, 0
+; CHECK-AIX-NEXT:    blr
+;
+; CHECK-LINUX-LABEL: test7:
+; CHECK-LINUX:       # %bb.0: # %bb
+; CHECK-LINUX-NEXT:    li r3, 0
+; CHECK-LINUX-NEXT:    blr
+bb:
+  %load = load i128, ptr null, align 16, !range !1
+  %icmp = icmp eq i128 %load, 65536
+  ret i1 %icmp
+}
+
+define i1 @test8() {
+; CHECK-AIX-LABEL: test8:
+; CHECK-AIX:       # %bb.0: # %bb
+; CHECK-AIX-NEXT:    li r3, 0
+; CHECK-AIX-NEXT:    lxvw4x vs34, 0, r3
+; CHECK-AIX-NEXT:    ld r3, L..C1(r2) # %const.0
+; CHECK-AIX-NEXT:    lxvd2x vs35, 0, r3
+; CHECK-AIX-NEXT:    vcmpequb. v2, v2, v3
+; CHECK-AIX-NEXT:    mfocrf r3, 2
+; CHECK-AIX-NEXT:    rlwinm r3, r3, 25, 31, 31
+; CHECK-AIX-NEXT:    blr
+;
+; CHECK-LINUX-LABEL: test8:
+; CHECK-LINUX:       # %bb.0: # %bb
+; CHECK-LINUX-NEXT:    li r3, 0
+; CHECK-LINUX-NEXT:    lxvd2x vs34, 0, r3
+; CHECK-LINUX-NEXT:    addis r3, r2, .LCPI7_0 at toc@ha
+; CHECK-LINUX-NEXT:    addi r3, r3, .LCPI7_0 at toc@l
+; CHECK-LINUX-NEXT:    lxvd2x vs35, 0, r3
+; CHECK-LINUX-NEXT:    vcmpequb. v2, v2, v3
+; CHECK-LINUX-NEXT:    mfocrf r3, 2
+; CHECK-LINUX-NEXT:    rlwinm r3, r3, 25, 31, 31
+; CHECK-LINUX-NEXT:    blr
+bb:
+  %load = load i128, ptr null, align 16, !range !2
+  %icmp = icmp eq i128 %load, 65536
+  ret i1 %icmp
+}
+
+!0 = !{i128 0, i128 2}
+!1 = !{i128 0, i128 65536}
+!2 = !{i128 0, i128 65537}

>From 5071ab3d00f3ad121c048eaf6cfce06fb77d2765 Mon Sep 17 00:00:00 2001
From: Amy Kwan <amy.kwan1 at ibm.com>
Date: Sun, 10 May 2026 09:43:07 -0400
Subject: [PATCH 2/2] Apply clang-format.

---
 llvm/lib/Target/PowerPC/PPCISelLowering.cpp | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/llvm/lib/Target/PowerPC/PPCISelLowering.cpp b/llvm/lib/Target/PowerPC/PPCISelLowering.cpp
index a5fc479292717..e5ba6c8f78599 100644
--- a/llvm/lib/Target/PowerPC/PPCISelLowering.cpp
+++ b/llvm/lib/Target/PowerPC/PPCISelLowering.cpp
@@ -15862,11 +15862,11 @@ SDValue convertTwoLoadsAndCmpToVCMPEQUB(SelectionDAG &DAG, SDNode *N,
     MachineMemOperand *MMO = LoadNode->getMemOperand();
     MachineFunction &MF = DAG.getMachineFunction();
     MachineMemOperand *NewMMO = MF.getMachineMemOperand(
-        MMO->getPointerInfo(), MMO->getFlags(), MMO->getSize(),
-        MMO->getAlign(), MMO->getAAInfo(), nullptr, MMO->getSyncScopeID(),
+        MMO->getPointerInfo(), MMO->getFlags(), MMO->getSize(), MMO->getAlign(),
+        MMO->getAAInfo(), nullptr, MMO->getSyncScopeID(),
         MMO->getSuccessOrdering(), MMO->getFailureOrdering());
     SDValue NewLoad = DAG.getLoad(MVT::v16i8, DL, LoadNode->getChain(),
-                                   LoadNode->getBasePtr(), NewMMO);
+                                  LoadNode->getBasePtr(), NewMMO);
     DAG.ReplaceAllUsesOfValueWith(SDValue(LoadNode, 1), NewLoad.getValue(1));
     return NewLoad;
   };



More information about the llvm-commits mailing list