[llvm] [AArch64][GISel] Recover ADDHN from OR comparison masks (PR #213925)

Harry Ramsey via llvm-commits llvm-commits at lists.llvm.org
Wed Aug 5 03:10:18 PDT 2026


https://github.com/Harry-Ramsey updated https://github.com/llvm/llvm-project/pull/213925

>From 11b94923a09e8bc4e37639f52d5cf153074e5588 Mon Sep 17 00:00:00 2001
From: Harry Ramsey <harry.ramsey at arm.com>
Date: Tue, 4 Aug 2026 09:41:29 +0000
Subject: [PATCH] [AArch64][GISel] Recover ADDHN from OR comparison masks

Add post legalize combine for recovering G_ADDHN from the GISel pattern;
G_TRUNC(G_OR(G_ICMP/G_FCMP, G_ICMP/G_FCMP)).
---
 llvm/lib/Target/AArch64/AArch64Combine.td     | 10 +++-
 .../GISel/AArch64PostLegalizerCombiner.cpp    | 57 +++++++++++++++++++
 llvm/test/CodeGen/AArch64/is_fpclass.ll       |  3 +-
 llvm/test/CodeGen/AArch64/neon-addhn.ll       |  9 +--
 4 files changed, 70 insertions(+), 9 deletions(-)

diff --git a/llvm/lib/Target/AArch64/AArch64Combine.td b/llvm/lib/Target/AArch64/AArch64Combine.td
index 82b9cec960d89..72c111600e744 100644
--- a/llvm/lib/Target/AArch64/AArch64Combine.td
+++ b/llvm/lib/Target/AArch64/AArch64Combine.td
@@ -362,6 +362,13 @@ def or_to_bsp: GICombineRule <
   (apply [{ applyOrToBSP(*${root}, MRI, B, ${matchinfo}); }])
 >;
 
+def trunc_or_to_addhn : GICombineRule <
+  (defs root:$root, simplify_uaddo_matchinfo:$matchinfo),
+  (match (G_TRUNC $dst, $src):$root,
+         [{ return matchTruncOrToADDHN(*${root}, MRI, VT, ${matchinfo}); }]),
+  (apply [{ applyTruncOrToADDHN(*${root}, MRI, B, ${matchinfo}); }])
+>;
+
 // Combines Mul(And(Srl(X, 15), 0x10001), 0xffff) into CMLTz
 def combine_mul_cmlt : GICombineRule<
   (defs root:$root, register_matchinfo:$matchinfo),
@@ -402,7 +409,8 @@ def AArch64PostLegalizerCombiner
                         constant_fold_binops, identity_combines,
                         ptr_add_immed_chain, overlapping_and,
                         split_store_zero_128, undef_combines,
-                        select_to_minmax, or_to_bsp, combine_concat_vector,
+                        select_to_minmax, or_to_bsp, trunc_or_to_addhn,
+                        combine_concat_vector,
                         combine_build_vector_of_bitcast,
                         commute_constant_to_rhs, extract_vec_elt_combines,
                         push_freeze_to_prevent_poison_from_propagating,
diff --git a/llvm/lib/Target/AArch64/GISel/AArch64PostLegalizerCombiner.cpp b/llvm/lib/Target/AArch64/GISel/AArch64PostLegalizerCombiner.cpp
index 49091ad9f4eb0..997add60411a1 100644
--- a/llvm/lib/Target/AArch64/GISel/AArch64PostLegalizerCombiner.cpp
+++ b/llvm/lib/Target/AArch64/GISel/AArch64PostLegalizerCombiner.cpp
@@ -385,6 +385,63 @@ void applyOrToBSP(MachineInstr &MI, MachineRegisterInfo &MRI,
   MI.eraseFromParent();
 }
 
+/// Match G_TRUNC (G_OR X, Y) => G_ADDHN X, Y when both inputs are sign
+/// extended from the result element type. The high half of the addition then
+/// equals the truncation of the OR.
+bool matchTruncOrToADDHN(MachineInstr &MI, MachineRegisterInfo &MRI,
+                         GISelValueTracking *VT,
+                         std::pair<Register, Register> &MatchInfo) {
+  assert(MI.getOpcode() == TargetOpcode::G_TRUNC && VT);
+
+  Register Dst = MI.getOperand(0).getReg();
+  Register Or = MI.getOperand(1).getReg();
+  MachineInstr *OrMI = getOpcodeDef(TargetOpcode::G_OR, Or, MRI);
+  if (!OrMI || !MRI.hasOneNonDBGUse(Or))
+    return false;
+
+  LLT DstTy = MRI.getType(Dst);
+  LLT SrcTy = MRI.getType(Or);
+  if (!((DstTy == LLT::fixed_vector(8, 8) &&
+         SrcTy == LLT::fixed_vector(8, 16)) ||
+        (DstTy == LLT::fixed_vector(4, 16) &&
+         SrcTy == LLT::fixed_vector(4, 32)) ||
+        (DstTy == LLT::fixed_vector(2, 32) &&
+         SrcTy == LLT::fixed_vector(2, 64))))
+    return false;
+
+  // ADDHN is not profitable if its narrow result is immediately widened
+  // again.
+  for (MachineInstr &UseMI : MRI.use_nodbg_instructions(Dst)) {
+    unsigned Opc = UseMI.getOpcode();
+    if (Opc != TargetOpcode::G_ANYEXT && Opc != TargetOpcode::G_ZEXT &&
+        Opc != TargetOpcode::G_SEXT)
+      continue;
+
+    Register ExtDst = UseMI.getOperand(0).getReg();
+    if (MRI.getType(ExtDst).getScalarSizeInBits() > DstTy.getScalarSizeInBits())
+      return false;
+  }
+
+  Register Src0 = OrMI->getOperand(1).getReg();
+  Register Src1 = OrMI->getOperand(2).getReg();
+  unsigned EltSize = SrcTy.getScalarSizeInBits();
+  if (VT->computeNumSignBits(Src0) != EltSize ||
+      VT->computeNumSignBits(Src1) != EltSize)
+    return false;
+
+  MatchInfo = {Src0, Src1};
+  return true;
+}
+
+void applyTruncOrToADDHN(MachineInstr &MI, MachineRegisterInfo &MRI,
+                         MachineIRBuilder &B,
+                         std::pair<Register, Register> &MatchInfo) {
+  B.setInstrAndDebugLoc(MI);
+  B.buildInstr(AArch64::G_ADDHN, {MI.getOperand(0).getReg()},
+               {MatchInfo.first, MatchInfo.second});
+  MI.eraseFromParent();
+}
+
 // Combines Mul(And(Srl(X, 15), 0x10001), 0xffff) into CMLTz
 bool matchCombineMulCMLT(MachineInstr &MI, MachineRegisterInfo &MRI,
                          Register &SrcReg) {
diff --git a/llvm/test/CodeGen/AArch64/is_fpclass.ll b/llvm/test/CodeGen/AArch64/is_fpclass.ll
index e2ed2d96a85de..ed47db1bd3fdd 100644
--- a/llvm/test/CodeGen/AArch64/is_fpclass.ll
+++ b/llvm/test/CodeGen/AArch64/is_fpclass.ll
@@ -617,8 +617,7 @@ define <2 x i1> @not_isfinite_v2d(<2 x double> %x) {
 ; CHECK-GI-NEXT:    ldr q1, [x8, :lo12:.LCPI17_0]
 ; CHECK-GI-NEXT:    cmeq v2.2d, v0.2d, v1.2d
 ; CHECK-GI-NEXT:    cmhi v0.2d, v0.2d, v1.2d
-; CHECK-GI-NEXT:    orr v0.16b, v2.16b, v0.16b
-; CHECK-GI-NEXT:    xtn v0.2s, v0.2d
+; CHECK-GI-NEXT:    addhn v0.2s, v2.2d, v0.2d
 ; CHECK-GI-NEXT:    ret
 ;
 ; CHECK-NOFP-LABEL: not_isfinite_v2d:
diff --git a/llvm/test/CodeGen/AArch64/neon-addhn.ll b/llvm/test/CodeGen/AArch64/neon-addhn.ll
index 23e290195e192..34bfb7a18f889 100644
--- a/llvm/test/CodeGen/AArch64/neon-addhn.ll
+++ b/llvm/test/CodeGen/AArch64/neon-addhn.ll
@@ -14,8 +14,7 @@ define <8 x i8> @addhn_setcc_v8i16( <8 x i16> %a, <8 x i16> %b, <8 x i16> %compa
 ; CHECK-GI:       // %bb.0:
 ; CHECK-GI-NEXT:    cmeq v0.8h, v0.8h, v2.8h
 ; CHECK-GI-NEXT:    cmeq v1.8h, v1.8h, v2.8h
-; CHECK-GI-NEXT:    orr v0.16b, v0.16b, v1.16b
-; CHECK-GI-NEXT:    xtn v0.8b, v0.8h
+; CHECK-GI-NEXT:    addhn v0.8b, v0.8h, v1.8h
 ; CHECK-GI-NEXT:    ret
   %cmp.a = icmp eq <8 x i16> %a, %comparand
   %cmp.b = icmp eq <8 x i16> %b, %comparand
@@ -36,8 +35,7 @@ define <4 x i16> @addhn_setcc_v4i32( <4 x i32> %a, <4 x i32> %b, <4 x i32> %comp
 ; CHECK-GI:       // %bb.0:
 ; CHECK-GI-NEXT:    cmeq v0.4s, v0.4s, v2.4s
 ; CHECK-GI-NEXT:    cmeq v1.4s, v1.4s, v2.4s
-; CHECK-GI-NEXT:    orr v0.16b, v0.16b, v1.16b
-; CHECK-GI-NEXT:    xtn v0.4h, v0.4s
+; CHECK-GI-NEXT:    addhn v0.4h, v0.4s, v1.4s
 ; CHECK-GI-NEXT:    ret
   %cmp.a = icmp eq <4 x i32> %a, %comparand
   %cmp.b = icmp eq <4 x i32> %b, %comparand
@@ -58,8 +56,7 @@ define <2 x i32> @addhn_setcc_v2i64( <2 x i64> %a, <2 x i64> %b, <2 x i64> %comp
 ; CHECK-GI:       // %bb.0:
 ; CHECK-GI-NEXT:    cmeq v0.2d, v0.2d, v2.2d
 ; CHECK-GI-NEXT:    cmeq v1.2d, v1.2d, v2.2d
-; CHECK-GI-NEXT:    orr v0.16b, v0.16b, v1.16b
-; CHECK-GI-NEXT:    xtn v0.2s, v0.2d
+; CHECK-GI-NEXT:    addhn v0.2s, v0.2d, v1.2d
 ; CHECK-GI-NEXT:    ret
   %cmp.a = icmp eq <2 x i64> %a, %comparand
   %cmp.b = icmp eq <2 x i64> %b, %comparand



More information about the llvm-commits mailing list