[llvm] [AArch64][ISel] Recover ADDHN from OR comparison masks (PR #210943)

Harry Ramsey via llvm-commits llvm-commits at lists.llvm.org
Fri Jul 24 06:04:20 PDT 2026


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

>From 61ed60323a803a2e2e09aa735d0747fb71852f0f Mon Sep 17 00:00:00 2001
From: Harry Ramsey <harry.ramsey at arm.com>
Date: Tue, 21 Jul 2026 10:07:30 +0000
Subject: [PATCH 1/2] [AArch64][ISel] Recover ADDHN from ORed comparison masks

Recover ADDHN from the following post-legalization patterns:

  SelectionDAG: TRUNCATE(OR(SETCC, SETCC))
  GlobalISel:  G_TRUNC(G_OR(G_ICMP/G_FCMP, G_ICMP/G_FCMP))

Avoid forming ADDHN when its narrow result is immediately widened for
further vector operations.

For GlobalISel, fold G_EXTRACT_VECTOR_ELT(G_ANYEXT(G_ADDHN)) into
G_EXTRACT_VECTOR_ELT_ANYEXT(G_ADDHN). This enables direct lane
extraction with UMOV instead of materializing USHLL to widen the entire
vector.
---
 llvm/test/CodeGen/AArch64/neon-addhn.ll | 53 +++++++++++++++++++++++++
 1 file changed, 53 insertions(+)
 create mode 100644 llvm/test/CodeGen/AArch64/neon-addhn.ll

diff --git a/llvm/test/CodeGen/AArch64/neon-addhn.ll b/llvm/test/CodeGen/AArch64/neon-addhn.ll
new file mode 100644
index 0000000000000..08f4d2e593716
--- /dev/null
+++ b/llvm/test/CodeGen/AArch64/neon-addhn.ll
@@ -0,0 +1,53 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 6
+; RUN: llc < %s -mtriple=aarch64 | FileCheck %s
+
+define i1 @addhn_setcc_v8i16(<8 x i16> %a, <8 x i16> %b, <8 x i16> %comparand) {
+; CHECK-LABEL: addhn_setcc_v8i16:
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    cmeq v0.8h, v0.8h, v2.8h
+; CHECK-NEXT:    cmeq v1.8h, v1.8h, v2.8h
+; CHECK-NEXT:    orr v0.16b, v0.16b, v1.16b
+; CHECK-NEXT:    xtn v0.8b, v0.8h
+; CHECK-NEXT:    umov w8, v0.b[0]
+; CHECK-NEXT:    and w0, w8, #0x1
+; CHECK-NEXT:    ret
+  %cmp.a = icmp eq <8 x i16> %a, %comparand
+  %cmp.b = icmp eq <8 x i16> %b, %comparand
+  %either = or <8 x i1> %cmp.a, %cmp.b
+  %lane = extractelement <8 x i1> %either, i64 0
+  ret i1 %lane
+}
+
+define i1 @addhn_setcc_v4i32(<4 x i32> %a, <4 x i32> %b, <4 x i32> %comparand) {
+; CHECK-LABEL: addhn_setcc_v4i32:
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    cmeq v0.4s, v0.4s, v2.4s
+; CHECK-NEXT:    cmeq v1.4s, v1.4s, v2.4s
+; CHECK-NEXT:    orr v0.16b, v0.16b, v1.16b
+; CHECK-NEXT:    xtn v0.4h, v0.4s
+; CHECK-NEXT:    umov w8, v0.h[0]
+; CHECK-NEXT:    and w0, w8, #0x1
+; CHECK-NEXT:    ret
+  %cmp.a = icmp eq <4 x i32> %a, %comparand
+  %cmp.b = icmp eq <4 x i32> %b, %comparand
+  %either = or <4 x i1> %cmp.a, %cmp.b
+  %lane = extractelement <4 x i1> %either, i64 0
+  ret i1 %lane
+}
+
+define i1 @addhn_setcc_v2i64(<2 x i64> %a, <2 x i64> %b, <2 x i64> %comparand) {
+; CHECK-LABEL: addhn_setcc_v2i64:
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    cmeq v0.2d, v0.2d, v2.2d
+; CHECK-NEXT:    cmeq v1.2d, v1.2d, v2.2d
+; CHECK-NEXT:    orr v0.16b, v0.16b, v1.16b
+; CHECK-NEXT:    xtn v0.2s, v0.2d
+; CHECK-NEXT:    fmov w8, s0
+; CHECK-NEXT:    and w0, w8, #0x1
+; CHECK-NEXT:    ret
+  %cmp.a = icmp eq <2 x i64> %a, %comparand
+  %cmp.b = icmp eq <2 x i64> %b, %comparand
+  %either = or <2 x i1> %cmp.a, %cmp.b
+  %lane = extractelement <2 x i1> %either, i64 0
+  ret i1 %lane
+}

>From 0272fc09ee024d723de75d498a76096583525a4f Mon Sep 17 00:00:00 2001
From: Harry Ramsey <harry.ramsey at arm.com>
Date: Tue, 21 Jul 2026 10:07:53 +0000
Subject: [PATCH 2/2] fixup! [AArch64][ISel] Recover ADDHN from OR comparison
 masks

---
 llvm/lib/Target/AArch64/AArch64Combine.td     | 15 ++-
 .../Target/AArch64/AArch64ISelLowering.cpp    | 18 ++++
 llvm/lib/Target/AArch64/AArch64InstrGISel.td  | 14 +++
 .../GISel/AArch64InstructionSelector.cpp      | 50 ++++++++++
 .../GISel/AArch64PostLegalizerCombiner.cpp    | 95 +++++++++++++++++++
 .../AArch64/intrinsic-vector-match-sve2.ll    |  3 +-
 llvm/test/CodeGen/AArch64/is_fpclass.ll       |  3 +-
 llvm/test/CodeGen/AArch64/neon-addhn.ll       | 35 ++++---
 .../CodeGen/AArch64/select-with-and-or.ll     | 12 +--
 9 files changed, 217 insertions(+), 28 deletions(-)

diff --git a/llvm/lib/Target/AArch64/AArch64Combine.td b/llvm/lib/Target/AArch64/AArch64Combine.td
index 82b9cec960d89..6778344dc8f2f 100644
--- a/llvm/lib/Target/AArch64/AArch64Combine.td
+++ b/llvm/lib/Target/AArch64/AArch64Combine.td
@@ -333,6 +333,16 @@ def mutate_anyext_to_zext : GICombineRule<
   (apply [{ applyMutateAnyExtToZExt(*${d}, MRI, B, Observer); }])
 >;
 
+def addhn_from_cmp_masks_matchinfo :
+    GIDefMatchData<"std::pair<Register, Register>">;
+def addhn_from_cmp_masks : GICombineRule<
+  (defs root:$root, addhn_from_cmp_masks_matchinfo:$matchinfo),
+  (match (G_TRUNC $dst, $src):$root,
+         [{ return matchAddhnFromCmpMasks(*${root}, MRI, ${matchinfo}); }]),
+  (apply [{ applyAddhnFromCmpMasks(*${root}, MRI, B, Observer,
+                                   ${matchinfo}); }])
+>;
+
 def split_store_zero_128 : GICombineRule<
   (defs root:$d),
   (match (G_STORE $src, $addr):$d,
@@ -387,9 +397,9 @@ def AArch64PostLegalizerLowering
 // Post-legalization combines which are primarily optimizations.
 def AArch64PostLegalizerCombiner
     : GICombiner<"AArch64PostLegalizerCombinerImpl",
-                       [copy_prop, cast_of_cast_combines, constant_fold_fp_ops, 
+                       [copy_prop, cast_of_cast_combines, constant_fold_fp_ops,
                         buildvector_of_truncate, integer_of_truncate,
-                        mutate_anyext_to_zext, combines_for_extload, 
+                        mutate_anyext_to_zext, combines_for_extload,
                         combine_indexed_load_store, sext_trunc_sextload,
                         hoist_logic_op_with_same_opcode_hands,
                         redundant_and, xor_of_and_with_same_reg,
@@ -407,6 +417,7 @@ def AArch64PostLegalizerCombiner
                         commute_constant_to_rhs, extract_vec_elt_combines,
                         push_freeze_to_prevent_poison_from_propagating,
                         combine_mul_cmlt, combine_use_vector_truncate,
+                        addhn_from_cmp_masks,
                         extmultomull, subaddmulreassoc, truncsat_combines,
                         lshr_of_trunc_of_lshr,
                         funnel_shift_from_or_shift_constants_are_legal]> {
diff --git a/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp b/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp
index 2e6b0f4006de6..5a9f9bdb36040 100644
--- a/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp
+++ b/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp
@@ -23690,6 +23690,24 @@ static SDValue performTruncateCombine(SDNode *N, SelectionDAG &DAG,
   SDLoc DL(N);
   EVT VT = N->getValueType(0);
   SDValue N0 = N->getOperand(0);
+
+  // Form the add/shift/truncate DAG matched by the ADDHN patterns.
+  if (DCI.isAfterLegalizeDAG() && N0.getOpcode() == ISD::OR && N0.hasOneUse()) {
+    EVT SrcVT = N0.getValueType();
+    if (((VT == MVT::v8i8 && SrcVT == MVT::v8i16) ||
+         (VT == MVT::v4i16 && SrcVT == MVT::v4i32) ||
+         (VT == MVT::v2i32 && SrcVT == MVT::v2i64)) &&
+        N0.getOperand(0).getOpcode() == ISD::SETCC &&
+        N0.getOperand(1).getOpcode() == ISD::SETCC) {
+      SDValue Add =
+          DAG.getNode(ISD::ADD, DL, SrcVT, N0.getOperand(0), N0.getOperand(1));
+      SDValue Shift = DAG.getNode(
+          AArch64ISD::VLSHR, DL, SrcVT, Add,
+          DAG.getTargetConstant(VT.getScalarSizeInBits(), DL, MVT::i32));
+      return DAG.getNode(ISD::TRUNCATE, DL, VT, Shift);
+    }
+  }
+
   if (VT.isFixedLengthVector() && VT.is64BitVector() && N0.hasOneUse() &&
       N0.getOpcode() == AArch64ISD::DUP) {
     SDValue Op = N0.getOperand(0);
diff --git a/llvm/lib/Target/AArch64/AArch64InstrGISel.td b/llvm/lib/Target/AArch64/AArch64InstrGISel.td
index ec95448f61043..ea5b8f42cf8b6 100644
--- a/llvm/lib/Target/AArch64/AArch64InstrGISel.td
+++ b/llvm/lib/Target/AArch64/AArch64InstrGISel.td
@@ -25,6 +25,20 @@ def G_ADD_LOW : AArch64GenericInstruction {
   let hasSideEffects = 0;
 }
 
+def G_ADDHN : AArch64GenericInstruction {
+  let OutOperandList = (outs type0:$dst);
+  let InOperandList = (ins type1:$lhs, type1:$rhs);
+  let hasSideEffects = 0;
+}
+
+// Extract a vector element and any-extend it to the destination type. This is
+// used to undo vector-element promotion after legalization.
+def G_EXTRACT_VECTOR_ELT_ANYEXT : AArch64GenericInstruction {
+  let OutOperandList = (outs type0:$dst);
+  let InOperandList = (ins type1:$src, type2:$index);
+  let hasSideEffects = 0;
+}
+
 // Pseudo for a rev32 instruction. Produced post-legalization from
 // G_SHUFFLE_VECTORs with appropriate masks.
 def G_REV32 : AArch64GenericInstruction {
diff --git a/llvm/lib/Target/AArch64/GISel/AArch64InstructionSelector.cpp b/llvm/lib/Target/AArch64/GISel/AArch64InstructionSelector.cpp
index 0c2f3f97ec07d..9d74a99552f26 100644
--- a/llvm/lib/Target/AArch64/GISel/AArch64InstructionSelector.cpp
+++ b/llvm/lib/Target/AArch64/GISel/AArch64InstructionSelector.cpp
@@ -3598,6 +3598,56 @@ bool AArch64InstructionSelector::select(MachineInstr &I) {
     constrainSelectedInstRegOperands(I, TII, TRI, RBI);
     return true;
   }
+  case AArch64::G_ADDHN: {
+    if (!Subtarget->hasNEON())
+      return false;
+
+    const LLT DstTy = MRI.getType(I.getOperand(0).getReg());
+    const LLT SrcTy = MRI.getType(I.getOperand(1).getReg());
+
+    unsigned Opc;
+    if (DstTy == LLT::fixed_vector(8, 8) && SrcTy == LLT::fixed_vector(8, 16))
+      Opc = AArch64::ADDHNv8i16_v8i8;
+    else if (DstTy == LLT::fixed_vector(4, 16) &&
+             SrcTy == LLT::fixed_vector(4, 32))
+      Opc = AArch64::ADDHNv4i32_v4i16;
+    else if (DstTy == LLT::fixed_vector(2, 32) &&
+             SrcTy == LLT::fixed_vector(2, 64))
+      Opc = AArch64::ADDHNv2i64_v2i32;
+    else
+      return false;
+
+    if (MRI.getType(I.getOperand(2).getReg()) != SrcTy)
+      return false;
+
+    I.setDesc(TII.get(Opc));
+    constrainSelectedInstRegOperands(I, TII, TRI, RBI);
+    return true;
+  }
+  case AArch64::G_EXTRACT_VECTOR_ELT_ANYEXT: {
+    Register Dst = I.getOperand(0).getReg();
+
+    // Instructions like UMOV only support sizes of 8/16/32-bits.
+    const bool ExtendTo64 = MRI.getType(Dst).getSizeInBits() == 64;
+    if (ExtendTo64) {
+      Register ExtractDst = MRI.createGenericVirtualRegister(LLT::scalar(32));
+      MRI.setRegBank(ExtractDst, RBI.getRegBank(AArch64::GPRRegBankID));
+      I.getOperand(0).setReg(ExtractDst);
+    }
+
+    I.setDesc(TII.get(TargetOpcode::G_EXTRACT_VECTOR_ELT));
+    if (!selectExtractElt(I, MRI))
+      return false;
+
+    if (ExtendTo64) {
+      MIB.setInsertPt(*I.getParent(), std::next(I.getIterator()));
+      MIB.buildInstr(AArch64::SUBREG_TO_REG, {Dst}, {})
+          .addUse(I.getOperand(0).getReg())
+          .addImm(AArch64::sub_32);
+      RBI.constrainGenericRegister(Dst, AArch64::GPR64RegClass, MRI);
+    }
+    return true;
+  }
   case TargetOpcode::G_BUILD_VECTOR:
     return selectBuildVector(I, MRI);
   case TargetOpcode::G_MERGE_VALUES:
diff --git a/llvm/lib/Target/AArch64/GISel/AArch64PostLegalizerCombiner.cpp b/llvm/lib/Target/AArch64/GISel/AArch64PostLegalizerCombiner.cpp
index c8f461c746cbf..d53b1421b03f0 100644
--- a/llvm/lib/Target/AArch64/GISel/AArch64PostLegalizerCombiner.cpp
+++ b/llvm/lib/Target/AArch64/GISel/AArch64PostLegalizerCombiner.cpp
@@ -278,6 +278,101 @@ void applyFoldMergeToZext(MachineInstr &MI, MachineRegisterInfo &MRI,
   Observer.changedInstr(MI);
 }
 
+bool matchAddhnFromCmpMasks(MachineInstr &MI, MachineRegisterInfo &MRI,
+                            std::pair<Register, Register> &MatchInfo) {
+  assert(MI.getOpcode() == TargetOpcode::G_TRUNC && "Expected a G_TRUNC");
+
+  Register Dst = MI.getOperand(0).getReg();
+  Register Src = MI.getOperand(1).getReg();
+  LLT DstTy = MRI.getType(Dst);
+  LLT SrcTy = MRI.getType(Src);
+
+  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;
+
+  // Do not form ADDHN when its narrow result is immediately widened for more
+  // vector operations. In that case retaining the wide OR is cheaper.
+  for (MachineInstr &Use : MRI.use_nodbg_instructions(Dst)) {
+    const unsigned Opc = Use.getOpcode();
+    if (Opc == TargetOpcode::G_ANYEXT) {
+      Register ExtDst = Use.getOperand(0).getReg();
+      if (!MRI.use_nodbg_empty(ExtDst) &&
+          llvm::all_of(
+              MRI.use_nodbg_instructions(ExtDst), [](MachineInstr &ExtUse) {
+                return ExtUse.getOpcode() == TargetOpcode::G_EXTRACT_VECTOR_ELT;
+              }))
+        continue;
+    }
+
+    if (Opc == TargetOpcode::G_ANYEXT || Opc == TargetOpcode::G_SEXT ||
+        Opc == TargetOpcode::G_ZEXT)
+      return false;
+  }
+
+  MachineInstr *Or = getOpcodeDef(TargetOpcode::G_OR, Src, MRI);
+  if (!Or || !MRI.hasOneNonDBGUse(Src))
+    return false;
+
+  auto IsComparisonMask = [&](Register Reg) {
+    MachineInstr *Def = getDefIgnoringCopies(Reg, MRI);
+    if (Def && Def->getOpcode() == TargetOpcode::G_FREEZE)
+      Def = getDefIgnoringCopies(Def->getOperand(1).getReg(), MRI);
+    return Def && (Def->getOpcode() == TargetOpcode::G_ICMP ||
+                   Def->getOpcode() == TargetOpcode::G_FCMP);
+  };
+
+  Register LHS = Or->getOperand(1).getReg();
+  Register RHS = Or->getOperand(2).getReg();
+  if (!IsComparisonMask(LHS) || !IsComparisonMask(RHS))
+    return false;
+
+  MatchInfo = {LHS, RHS};
+  return true;
+}
+
+void applyAddhnFromCmpMasks(MachineInstr &MI, MachineRegisterInfo &MRI,
+                            MachineIRBuilder &B, GISelChangeObserver &,
+                            const std::pair<Register, Register> &MatchInfo) {
+  Register Dst = MI.getOperand(0).getReg();
+
+  // Legalization promotes an extract from the narrow vector into an extract
+  // from an any-extended vector. Fold that promotion into a target extraction
+  // pseudo so instruction selection can use UMOV directly from the ADDHN
+  // result instead of requiring an additional USHLL.
+  SmallVector<MachineInstr *> Exts;
+  for (MachineInstr &Use : MRI.use_nodbg_instructions(Dst))
+    if (Use.getOpcode() == TargetOpcode::G_ANYEXT)
+      Exts.push_back(&Use);
+
+  for (MachineInstr *Ext : Exts) {
+    Register ExtDst = Ext->getOperand(0).getReg();
+    SmallVector<MachineInstr *> Extracts;
+    for (MachineInstr &ExtUse : MRI.use_nodbg_instructions(ExtDst))
+      Extracts.push_back(&ExtUse);
+
+    for (MachineInstr *Extract : Extracts) {
+      assert(Extract->getOpcode() == TargetOpcode::G_EXTRACT_VECTOR_ELT);
+      B.setInstrAndDebugLoc(*Extract);
+      B.buildInstr(AArch64::G_EXTRACT_VECTOR_ELT_ANYEXT,
+                   {Extract->getOperand(0).getReg()},
+                   {Dst, Extract->getOperand(2).getReg()});
+      Extract->eraseFromParent();
+    }
+
+    if (MRI.use_nodbg_empty(ExtDst))
+      Ext->eraseFromParent();
+  }
+
+  B.setInstrAndDebugLoc(MI);
+  B.buildInstr(AArch64::G_ADDHN, {Dst}, {MatchInfo.first, MatchInfo.second});
+  MI.eraseFromParent();
+}
+
 /// \returns True if a G_ANYEXT instruction \p MI should be mutated to a G_ZEXT
 /// instruction.
 bool matchMutateAnyExtToZExt(MachineInstr &MI, MachineRegisterInfo &MRI) {
diff --git a/llvm/test/CodeGen/AArch64/intrinsic-vector-match-sve2.ll b/llvm/test/CodeGen/AArch64/intrinsic-vector-match-sve2.ll
index de88e10141fbf..287980b871380 100644
--- a/llvm/test/CodeGen/AArch64/intrinsic-vector-match-sve2.ll
+++ b/llvm/test/CodeGen/AArch64/intrinsic-vector-match-sve2.ll
@@ -521,8 +521,7 @@ define <2 x i1> @match_v2xi64_v2i64(<2 x i64> %op1, <2 x i64> %op2, <2 x i1> %ma
 ; CHECK-NEXT:    dup v1.2d, v1.d[0]
 ; CHECK-NEXT:    cmeq v3.2d, v0.2d, v3.2d
 ; CHECK-NEXT:    cmeq v0.2d, v0.2d, v1.2d
-; CHECK-NEXT:    orr v0.16b, v0.16b, v3.16b
-; CHECK-NEXT:    xtn v0.2s, v0.2d
+; CHECK-NEXT:    addhn v0.2s, v0.2d, v3.2d
 ; CHECK-NEXT:    and v0.8b, v0.8b, v2.8b
 ; CHECK-NEXT:    ret
   %r = tail call <2 x i1> @llvm.experimental.vector.match(<2 x i64> %op1, <2 x i64> %op2, <2 x i1> %mask)
diff --git a/llvm/test/CodeGen/AArch64/is_fpclass.ll b/llvm/test/CodeGen/AArch64/is_fpclass.ll
index 71a8f42a7b4db..bda053dd56b9c 100644
--- a/llvm/test/CodeGen/AArch64/is_fpclass.ll
+++ b/llvm/test/CodeGen/AArch64/is_fpclass.ll
@@ -619,8 +619,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 08f4d2e593716..d6ed13659d354 100644
--- a/llvm/test/CodeGen/AArch64/neon-addhn.ll
+++ b/llvm/test/CodeGen/AArch64/neon-addhn.ll
@@ -1,13 +1,13 @@
 ; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 6
-; RUN: llc < %s -mtriple=aarch64 | FileCheck %s
+; RUN: llc < %s -mtriple=aarch64 | FileCheck %s --check-prefixes=CHECK,CHECK-SD
+; RUN: llc < %s -mtriple=aarch64 -global-isel | FileCheck %s --check-prefixes=CHECK,CHECK-GI
 
 define i1 @addhn_setcc_v8i16(<8 x i16> %a, <8 x i16> %b, <8 x i16> %comparand) {
 ; CHECK-LABEL: addhn_setcc_v8i16:
 ; CHECK:       // %bb.0:
 ; CHECK-NEXT:    cmeq v0.8h, v0.8h, v2.8h
 ; CHECK-NEXT:    cmeq v1.8h, v1.8h, v2.8h
-; CHECK-NEXT:    orr v0.16b, v0.16b, v1.16b
-; CHECK-NEXT:    xtn v0.8b, v0.8h
+; CHECK-NEXT:    addhn v0.8b, v0.8h, v1.8h
 ; CHECK-NEXT:    umov w8, v0.b[0]
 ; CHECK-NEXT:    and w0, w8, #0x1
 ; CHECK-NEXT:    ret
@@ -23,8 +23,7 @@ define i1 @addhn_setcc_v4i32(<4 x i32> %a, <4 x i32> %b, <4 x i32> %comparand) {
 ; CHECK:       // %bb.0:
 ; CHECK-NEXT:    cmeq v0.4s, v0.4s, v2.4s
 ; CHECK-NEXT:    cmeq v1.4s, v1.4s, v2.4s
-; CHECK-NEXT:    orr v0.16b, v0.16b, v1.16b
-; CHECK-NEXT:    xtn v0.4h, v0.4s
+; CHECK-NEXT:    addhn v0.4h, v0.4s, v1.4s
 ; CHECK-NEXT:    umov w8, v0.h[0]
 ; CHECK-NEXT:    and w0, w8, #0x1
 ; CHECK-NEXT:    ret
@@ -36,15 +35,23 @@ define i1 @addhn_setcc_v4i32(<4 x i32> %a, <4 x i32> %b, <4 x i32> %comparand) {
 }
 
 define i1 @addhn_setcc_v2i64(<2 x i64> %a, <2 x i64> %b, <2 x i64> %comparand) {
-; CHECK-LABEL: addhn_setcc_v2i64:
-; CHECK:       // %bb.0:
-; CHECK-NEXT:    cmeq v0.2d, v0.2d, v2.2d
-; CHECK-NEXT:    cmeq v1.2d, v1.2d, v2.2d
-; CHECK-NEXT:    orr v0.16b, v0.16b, v1.16b
-; CHECK-NEXT:    xtn v0.2s, v0.2d
-; CHECK-NEXT:    fmov w8, s0
-; CHECK-NEXT:    and w0, w8, #0x1
-; CHECK-NEXT:    ret
+; CHECK-SD-LABEL: addhn_setcc_v2i64:
+; CHECK-SD:       // %bb.0:
+; CHECK-SD-NEXT:    cmeq v0.2d, v0.2d, v2.2d
+; CHECK-SD-NEXT:    cmeq v1.2d, v1.2d, v2.2d
+; CHECK-SD-NEXT:    addhn v0.2s, v0.2d, v1.2d
+; CHECK-SD-NEXT:    fmov w8, s0
+; CHECK-SD-NEXT:    and w0, w8, #0x1
+; CHECK-SD-NEXT:    ret
+;
+; CHECK-GI-LABEL: addhn_setcc_v2i64:
+; 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:    addhn v0.2s, v0.2d, v1.2d
+; CHECK-GI-NEXT:    mov w8, v0.s[0]
+; CHECK-GI-NEXT:    and w0, w8, #0x1
+; CHECK-GI-NEXT:    ret
   %cmp.a = icmp eq <2 x i64> %a, %comparand
   %cmp.b = icmp eq <2 x i64> %b, %comparand
   %either = or <2 x i1> %cmp.a, %cmp.b
diff --git a/llvm/test/CodeGen/AArch64/select-with-and-or.ll b/llvm/test/CodeGen/AArch64/select-with-and-or.ll
index 60f2add81b45c..50500612d3b48 100644
--- a/llvm/test/CodeGen/AArch64/select-with-and-or.ll
+++ b/llvm/test/CodeGen/AArch64/select-with-and-or.ll
@@ -119,16 +119,14 @@ define <4 x i1> @or_vec(<4 x i32> %x, <4 x i32> %y, <4 x i32> %z, <4 x i32> %w)
 ; CHECK-SD:       // %bb.0:
 ; CHECK-SD-NEXT:    cmgt v2.4s, v2.4s, v3.4s
 ; CHECK-SD-NEXT:    cmeq v0.4s, v0.4s, v1.4s
-; CHECK-SD-NEXT:    orr v0.16b, v0.16b, v2.16b
-; CHECK-SD-NEXT:    xtn v0.4h, v0.4s
+; CHECK-SD-NEXT:    addhn v0.4h, v0.4s, v2.4s
 ; CHECK-SD-NEXT:    ret
 ;
 ; CHECK-GI-LABEL: or_vec:
 ; CHECK-GI:       // %bb.0:
 ; CHECK-GI-NEXT:    cmeq v0.4s, v0.4s, v1.4s
 ; CHECK-GI-NEXT:    cmgt v1.4s, v2.4s, v3.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
   %a = icmp eq <4 x i32> %x, %y
   %b = icmp sgt <4 x i32> %z, %w
@@ -207,16 +205,14 @@ define <4 x i1> @or_vec_undef(<4 x i32> %x, <4 x i32> %y, <4 x i32> %z, <4 x i32
 ; CHECK-SD:       // %bb.0:
 ; CHECK-SD-NEXT:    cmgt v2.4s, v2.4s, v3.4s
 ; CHECK-SD-NEXT:    cmeq v0.4s, v0.4s, v1.4s
-; CHECK-SD-NEXT:    orr v0.16b, v0.16b, v2.16b
-; CHECK-SD-NEXT:    xtn v0.4h, v0.4s
+; CHECK-SD-NEXT:    addhn v0.4h, v0.4s, v2.4s
 ; CHECK-SD-NEXT:    ret
 ;
 ; CHECK-GI-LABEL: or_vec_undef:
 ; CHECK-GI:       // %bb.0:
 ; CHECK-GI-NEXT:    cmeq v0.4s, v0.4s, v1.4s
 ; CHECK-GI-NEXT:    cmgt v1.4s, v2.4s, v3.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
   %a = icmp eq <4 x i32> %x, %y
   %b = icmp sgt <4 x i32> %z, %w



More information about the llvm-commits mailing list