[llvm-branch-commits] [llvm] f6198f6 - [Hexagon] Fix truncation to boolean vector that need widening (#182528)
Cullen Rhodes via llvm-branch-commits
llvm-branch-commits at lists.llvm.org
Mon Mar 30 07:51:00 PDT 2026
Author: Brian Cain
Date: 2026-03-30T14:49:51Z
New Revision: f6198f61b2bd774ab9354d92af01a96b285e73cb
URL: https://github.com/llvm/llvm-project/commit/f6198f61b2bd774ab9354d92af01a96b285e73cb
DIFF: https://github.com/llvm/llvm-project/commit/f6198f61b2bd774ab9354d92af01a96b285e73cb.diff
LOG: [Hexagon] Fix truncation to boolean vector that need widening (#182528)
When truncating a sub-HVX-width vector to a boolean vector (e.g., v64i8
-> v64i1 in 128-byte HVX mode), the operation would crash with
"Unhandled HVX operation" UNREACHABLE. This happened because the
condition in LowerHvxOperationWrapper/ReplaceHvxNodeResults did not
handle the case where the input vector needs widening and the result is
a boolean vector.
The fix adds WidenHvxTruncateToBool which widens the input to HVX
register width (e.g., v64i8 -> v128i8), performs the truncate to widened
bool type (v128i8 -> v128i1), extracts the result subvector (v128i1 ->
v64i1).
This allows the widened truncate to match the existing V6_vandvrt
pattern in HexagonPatternsHVX.td.
(cherry picked from commit 0c53a89b228a2c1b4d2d02cb80d674b2b20a9c7c)
Added:
Modified:
llvm/lib/Target/Hexagon/HexagonISelLowering.h
llvm/lib/Target/Hexagon/HexagonISelLoweringHVX.cpp
llvm/lib/Target/Hexagon/HexagonPatternsHVX.td
llvm/test/CodeGen/Hexagon/isel/trunc-vNi1-HVX.ll
Removed:
################################################################################
diff --git a/llvm/lib/Target/Hexagon/HexagonISelLowering.h b/llvm/lib/Target/Hexagon/HexagonISelLowering.h
index f42840cd284d7..c1698a9a3b579 100644
--- a/llvm/lib/Target/Hexagon/HexagonISelLowering.h
+++ b/llvm/lib/Target/Hexagon/HexagonISelLowering.h
@@ -500,6 +500,7 @@ class HexagonTargetLowering : public TargetLowering {
SDValue CreateTLWrapper(SDValue Op, SelectionDAG &DAG) const;
SDValue RemoveTLWrapper(SDValue Op, SelectionDAG &DAG) const;
+ SDValue WidenHvxTruncateToBool(SDValue Op, SelectionDAG &DAG) const;
std::pair<const TargetRegisterClass*, uint8_t>
findRepresentativeClass(const TargetRegisterInfo *TRI, MVT VT)
diff --git a/llvm/lib/Target/Hexagon/HexagonISelLoweringHVX.cpp b/llvm/lib/Target/Hexagon/HexagonISelLoweringHVX.cpp
index ea0ad78c70bc6..52f9109cdc594 100644
--- a/llvm/lib/Target/Hexagon/HexagonISelLoweringHVX.cpp
+++ b/llvm/lib/Target/Hexagon/HexagonISelLoweringHVX.cpp
@@ -3424,6 +3424,43 @@ HexagonTargetLowering::WidenHvxSetCC(SDValue Op, SelectionDAG &DAG) const {
{SetCC, getZero(dl, MVT::i32, DAG)});
}
+SDValue HexagonTargetLowering::WidenHvxTruncateToBool(SDValue Op,
+ SelectionDAG &DAG) const {
+ // Handle truncation to boolean vector where the result boolean type
+ // needs widening (e.g., v16i32 -> v16i1 where v16i1 is not a standard
+ // HVX predicate type, or v16i8 -> v16i1 in 128-byte mode).
+ // Widen the input to HVX width, perform the truncate to the widened
+ // boolean type, then extract the result.
+ const SDLoc &dl(Op);
+ SDValue Inp = Op.getOperand(0);
+ MVT InpTy = ty(Inp);
+ MVT ResTy = ty(Op);
+
+ assert(ResTy.getVectorElementType() == MVT::i1 &&
+ "Expected boolean result type");
+
+ MVT ElemTy = InpTy.getVectorElementType();
+ unsigned HwLen = Subtarget.getVectorLength();
+
+ // Calculate the widened input type that fills the HVX register.
+ unsigned WideLen = (8 * HwLen) / ElemTy.getSizeInBits();
+ MVT WideInpTy = MVT::getVectorVT(ElemTy, WideLen);
+ if (!Subtarget.isHVXVectorType(WideInpTy, false))
+ return SDValue();
+
+ // Widen the input to HVX width.
+ SDValue WideInp = appendUndef(Inp, WideInpTy, DAG);
+
+ // Perform the truncate to widened boolean type.
+ MVT WideBoolTy = MVT::getVectorVT(MVT::i1, WideLen);
+ SDValue WideTrunc = DAG.getNode(ISD::TRUNCATE, dl, WideBoolTy, WideInp);
+
+ // Extract the result.
+ EVT RetTy = typeLegalize(ResTy, DAG);
+ return DAG.getNode(ISD::EXTRACT_SUBVECTOR, dl, RetTy,
+ {WideTrunc, getZero(dl, MVT::i32, DAG)});
+}
+
SDValue
HexagonTargetLowering::LowerHvxOperation(SDValue Op, SelectionDAG &DAG) const {
unsigned Opc = Op.getOpcode();
@@ -3666,12 +3703,28 @@ HexagonTargetLowering::LowerHvxOperationWrapper(SDNode *N,
case ISD::ANY_EXTEND:
case ISD::SIGN_EXTEND:
case ISD::ZERO_EXTEND:
- case ISD::TRUNCATE:
if (Subtarget.isHVXElementType(ty(Op)) &&
Subtarget.isHVXElementType(ty(Inp0))) {
Results.push_back(CreateTLWrapper(Op, DAG));
}
break;
+ case ISD::TRUNCATE:
+ // Handle truncate to boolean vector when the input is not a
+ // standard HVX vector type (single or pair). This covers cases
+ // where the input needs widening (e.g., v64i8 -> v64i1 in
+ // 128-byte mode) and cases where the result boolean type itself
+ // needs widening (e.g., v16i32 -> v16i1). When the input is
+ // already an HVX type, tablegen patterns handle the truncation
+ // directly (e.g., v64i16 -> v64i1 via V6_vandvrt).
+ if (ty(Op).getVectorElementType() == MVT::i1 &&
+ !Subtarget.isHVXVectorType(ty(Inp0), false)) {
+ if (SDValue T = WidenHvxTruncateToBool(Op, DAG))
+ Results.push_back(T);
+ } else if (Subtarget.isHVXElementType(ty(Op)) &&
+ Subtarget.isHVXElementType(ty(Inp0))) {
+ Results.push_back(CreateTLWrapper(Op, DAG));
+ }
+ break;
case ISD::SETCC:
if (shouldWidenToHvx(ty(Inp0), DAG)) {
if (SDValue T = WidenHvxSetCC(Op, DAG))
@@ -3732,12 +3785,23 @@ HexagonTargetLowering::ReplaceHvxNodeResults(SDNode *N,
case ISD::ANY_EXTEND:
case ISD::SIGN_EXTEND:
case ISD::ZERO_EXTEND:
- case ISD::TRUNCATE:
if (Subtarget.isHVXElementType(ty(Op)) &&
Subtarget.isHVXElementType(ty(Inp0))) {
Results.push_back(CreateTLWrapper(Op, DAG));
}
break;
+ case ISD::TRUNCATE:
+ // Handle truncate to boolean vector when the input is not a
+ // standard HVX vector type. See comment in LowerHvxOperationWrapper.
+ if (ty(Op).getVectorElementType() == MVT::i1 &&
+ !Subtarget.isHVXVectorType(ty(Inp0), false)) {
+ if (SDValue T = WidenHvxTruncateToBool(Op, DAG))
+ Results.push_back(T);
+ } else if (Subtarget.isHVXElementType(ty(Op)) &&
+ Subtarget.isHVXElementType(ty(Inp0))) {
+ Results.push_back(CreateTLWrapper(Op, DAG));
+ }
+ break;
case ISD::SETCC:
if (shouldWidenToHvx(ty(Op), DAG)) {
if (SDValue T = WidenHvxSetCC(Op, DAG))
diff --git a/llvm/lib/Target/Hexagon/HexagonPatternsHVX.td b/llvm/lib/Target/Hexagon/HexagonPatternsHVX.td
index c6ea6d02bb5d8..6e1e80cc26912 100644
--- a/llvm/lib/Target/Hexagon/HexagonPatternsHVX.td
+++ b/llvm/lib/Target/Hexagon/HexagonPatternsHVX.td
@@ -735,6 +735,9 @@ let Predicates = [UseHVX] in {
(V6_vandvrt HvxVR:$Vs, (ToI32 0x01010101))>;
def: Pat<(VecQ32 (trunc HVI32:$Vs)),
(V6_vandvrt HvxVR:$Vs, (ToI32 0x01010101))>;
+ def: Pat<(VecQ8 (trunc HWI16:$Vss)),
+ (Combineq(VecQ16(V6_vandvrt (HiVec $Vss), (ToI32 0x01010101))),
+ (VecQ16 (V6_vandvrt (LoVec $Vss), (ToI32 0x01010101))))>;
def: Pat<(VecQ16 (trunc HWI32:$Vss)),
(Combineq(VecQ32(V6_vandvrt (HiVec $Vss), (ToI32 0x01010101))),
(VecQ32 (V6_vandvrt (LoVec $Vss), (ToI32 0x01010101))))>;
diff --git a/llvm/test/CodeGen/Hexagon/isel/trunc-vNi1-HVX.ll b/llvm/test/CodeGen/Hexagon/isel/trunc-vNi1-HVX.ll
index 1491729a17f30..30aea6350fac9 100644
--- a/llvm/test/CodeGen/Hexagon/isel/trunc-vNi1-HVX.ll
+++ b/llvm/test/CodeGen/Hexagon/isel/trunc-vNi1-HVX.ll
@@ -1,18 +1,140 @@
; RUN: llc --mtriple=hexagon -mattr=+hvxv79,+hvx-length128b < %s | FileCheck %s
-define void @f5(<64 x i32> %a0, ptr %a1) {
-; CHECK-LABEL: f5:
-; CHECK: [[REG0:(r[0-9]+)]] = ##16843009
-; CHECK-DAG: q[[Q0:[0-9]+]] = vand(v{{[0-9]+}},[[REG0]])
-; CHECK-DAG: q[[Q1:[0-9]+]] = vand(v{{[0-9]+}},[[REG0]])
-; CHECK: v{{[0-9]+}}.b = vpacke(v{{[0-9]+}}.h,v{{[0-9]+}}.h)
-; CHECK: v{{[0-9]+}}.b = vpacke(v{{[0-9]+}}.h,v{{[0-9]+}}.h)
-; CHECK: v[[VROR:[0-9]+]] = vror(v{{[0-9]+}},r{{[0-9]+}})
-; CHECK: v[[VOR:[0-9]+]] = vor(v[[VROR]],v{{[0-9]+}})
-; CHECK: q{{[0-9]+}} = vand(v[[VOR]],r{{[0-9]+}})
-b0:
- %v0 = trunc <64 x i32> %a0 to <64 x i1>
- store <64 x i1> %v0, ptr %a1, align 1
- ret void
+;
+; Exhaustive tests for truncation of HVX vectors to boolean (i1) vectors.
+; In 128-byte HVX mode:
+; Single register (HvxVR): v128i8, v64i16, v32i32 (1024 bits)
+; Pair register (HvxWR): v256i8, v128i16, v64i32 (2048 bits)
+; Predicates (HvxQR): v128i1, v64i1, v32i1
+;
+; Tests cover:
+; - Full HVX width (single register): tablegen V6_vandvrt patterns
+; - HVX pairs: tablegen Combineq patterns
+; - Sub-HVX width: C++ WidenHvxTruncateToBool (widen + trunc + extract)
+;
+; Very small vectors (v8i16, v8i32) are scalarized by the legalizer
+; and do not exercise HVX paths, so they are not included here.
+;
+
+; ===========================================================================
+; Category 1: Full HVX single-register width -> HVX predicate (tablegen)
+; These match tablegen patterns VecQ{8,16,32} (trunc HVI{8,16,32})
+; directly via V6_vandvrt.
+; ===========================================================================
+
+; CHECK-LABEL: trunc_v128i8_to_v128i1:
+; CHECK: q{{[0-9]+}} = vand(v{{[0-9]+}},r{{[0-9]+}})
+define i128 @trunc_v128i8_to_v128i1(<128 x i8> %v) {
+ %1 = trunc <128 x i8> %v to <128 x i1>
+ %2 = bitcast <128 x i1> %1 to i128
+ ret i128 %2
+}
+
+; CHECK-LABEL: trunc_v64i16_to_v64i1:
+; CHECK: q{{[0-9]+}} = vand(v{{[0-9]+}},r{{[0-9]+}})
+define i64 @trunc_v64i16_to_v64i1(<64 x i16> %v) {
+ %1 = trunc <64 x i16> %v to <64 x i1>
+ %2 = bitcast <64 x i1> %1 to i64
+ ret i64 %2
+}
+
+; CHECK-LABEL: trunc_v32i32_to_v32i1:
+; CHECK: q{{[0-9]+}} = vand(v{{[0-9]+}},r{{[0-9]+}})
+define i32 @trunc_v32i32_to_v32i1(<32 x i32> %v) {
+ %1 = trunc <32 x i32> %v to <32 x i1>
+ %2 = bitcast <32 x i1> %1 to i32
+ ret i32 %2
+}
+
+; ===========================================================================
+; Category 2: HVX pair -> HVX predicate (tablegen, via Combineq)
+; These split the pair, apply V6_vandvrt to each half, and combine
+; the predicate halves.
+; ===========================================================================
+
+; CHECK-LABEL: trunc_v64i32_to_v64i1:
+; CHECK-DAG: q{{[0-9]+}} = vand(v{{[0-9]+}},r{{[0-9]+}})
+; CHECK-DAG: q{{[0-9]+}} = vand(v{{[0-9]+}},r{{[0-9]+}})
+define i64 @trunc_v64i32_to_v64i1(<64 x i32> %v) {
+ %1 = trunc <64 x i32> %v to <64 x i1>
+ %2 = bitcast <64 x i1> %1 to i64
+ ret i64 %2
+}
+
+; CHECK-LABEL: trunc_v128i16_to_v128i1:
+; CHECK-DAG: q{{[0-9]+}} = vand(v{{[0-9]+}},r{{[0-9]+}})
+; CHECK-DAG: q{{[0-9]+}} = vand(v{{[0-9]+}},r{{[0-9]+}})
+define i128 @trunc_v128i16_to_v128i1(<128 x i16> %v) {
+ %1 = trunc <128 x i16> %v to <128 x i1>
+ %2 = bitcast <128 x i1> %1 to i128
+ ret i128 %2
+}
+
+; ===========================================================================
+; Category 3: Sub-HVX width -> predicate (C++ WidenHvxTruncateToBool)
+; Input is narrower than a single HVX register. The input is widened
+; to full HVX width, truncated to widened bool, then the result
+; subvector is extracted.
+; ===========================================================================
+
+; --- Half HVX width (512-bit input) ---
+
+; CHECK-LABEL: trunc_v64i8_to_v64i1:
+; CHECK: q{{[0-9]+}} = vand(v{{[0-9]+}},r{{[0-9]+}})
+define i64 @trunc_v64i8_to_v64i1(<64 x i8> %v) {
+ %1 = trunc <64 x i8> %v to <64 x i1>
+ %2 = bitcast <64 x i1> %1 to i64
+ ret i64 %2
+}
+
+; CHECK-LABEL: trunc_v32i16_to_v32i1:
+; CHECK: q{{[0-9]+}} = vand(v{{[0-9]+}},r{{[0-9]+}})
+define i32 @trunc_v32i16_to_v32i1(<32 x i16> %v) {
+ %1 = trunc <32 x i16> %v to <32 x i1>
+ %2 = bitcast <32 x i1> %1 to i32
+ ret i32 %2
+}
+
+; CHECK-LABEL: trunc_v16i32_to_v16i1:
+; CHECK: q{{[0-9]+}} = vand(v{{[0-9]+}},r{{[0-9]+}})
+define i16 @trunc_v16i32_to_v16i1(<16 x i32> %v) {
+ %1 = trunc <16 x i32> %v to <16 x i1>
+ %2 = bitcast <16 x i1> %1 to i16
+ ret i16 %2
+}
+
+; --- Quarter HVX width (256-bit input) ---
+
+; CHECK-LABEL: trunc_v32i8_to_v32i1:
+; CHECK: q{{[0-9]+}} = vand(v{{[0-9]+}},r{{[0-9]+}})
+define i32 @trunc_v32i8_to_v32i1(<32 x i8> %v) {
+ %1 = trunc <32 x i8> %v to <32 x i1>
+ %2 = bitcast <32 x i1> %1 to i32
+ ret i32 %2
+}
+
+; CHECK-LABEL: trunc_v16i16_to_v16i1:
+; CHECK: q{{[0-9]+}} = vand(v{{[0-9]+}},r{{[0-9]+}})
+define i16 @trunc_v16i16_to_v16i1(<16 x i16> %v) {
+ %1 = trunc <16 x i16> %v to <16 x i1>
+ %2 = bitcast <16 x i1> %1 to i16
+ ret i16 %2
}
+; --- Eighth HVX width and smaller (128-bit input) ---
+
+; CHECK-LABEL: trunc_v16i8_to_v16i1:
+; CHECK: q{{[0-9]+}} = vand(v{{[0-9]+}},r{{[0-9]+}})
+define i16 @trunc_v16i8_to_v16i1(<16 x i8> %v) {
+ %1 = trunc <16 x i8> %v to <16 x i1>
+ %2 = bitcast <16 x i1> %1 to i16
+ ret i16 %2
+}
+
+; CHECK-LABEL: trunc_v4i32_to_v4i1:
+; CHECK: q{{[0-9]+}} = vand(v{{[0-9]+}},r{{[0-9]+}})
+define i4 @trunc_v4i32_to_v4i1(<4 x i32> %v) {
+ %1 = trunc <4 x i32> %v to <4 x i1>
+ %2 = bitcast <4 x i1> %1 to i4
+ ret i4 %2
+}
More information about the llvm-branch-commits
mailing list