[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