[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