[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.
This commit is contained in:
parent
8d625621eb
commit
0c53a89b22
4 changed files with 205 additions and 15 deletions
|
|
@ -501,6 +501,7 @@ private:
|
|||
|
||||
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)
|
||||
|
|
|
|||
|
|
@ -3616,6 +3616,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();
|
||||
|
|
@ -3866,12 +3903,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))
|
||||
|
|
@ -3932,12 +3985,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))
|
||||
|
|
|
|||
|
|
@ -742,6 +742,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))))>;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
;
|
||||
; 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 void @trunc_v128i8_to_v128i1(<128 x i8> %v, ptr %p) {
|
||||
%1 = trunc <128 x i8> %v to <128 x i1>
|
||||
store <128 x i1> %1, ptr %p, align 16
|
||||
ret void
|
||||
}
|
||||
|
||||
; 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 void @trunc_v64i32_to_v64i1(<64 x i32> %v, ptr %p) {
|
||||
%1 = trunc <64 x i32> %v to <64 x i1>
|
||||
store <64 x i1> %1, ptr %p, align 8
|
||||
ret void
|
||||
}
|
||||
|
||||
; 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 void @trunc_v128i16_to_v128i1(<128 x i16> %v, ptr %p) {
|
||||
%1 = trunc <128 x i16> %v to <128 x i1>
|
||||
store <128 x i1> %1, ptr %p, align 16
|
||||
ret void
|
||||
}
|
||||
|
||||
; ===========================================================================
|
||||
; 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
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue