[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