[llvm] 2bd3671 - [Hexagon] Translate IEEE HVX intrinsics to QFloat on v79+ (#198832)
via llvm-commits
llvm-commits at lists.llvm.org
Mon Jun 8 09:18:35 PDT 2026
Author: Fateme Hosseini
Date: 2026-06-08T11:18:29-05:00
New Revision: 2bd367126678f3c8bb9e7f0067cf37089c4807a4
URL: https://github.com/llvm/llvm-project/commit/2bd367126678f3c8bb9e7f0067cf37089c4807a4
DIFF: https://github.com/llvm/llvm-project/commit/2bd367126678f3c8bb9e7f0067cf37089c4807a4.diff
LOG: [Hexagon] Translate IEEE HVX intrinsics to QFloat on v79+ (#198832)
On Hexagon v79 and later, the IEEE-floating-point HVX
instruction encodings (sf/hf operands) are no longer
present in the architecture. Code that uses the IEEE HVX
intrinsics still needs to compile for those targets, so
this change implicitly lowers the intrinsic calls to
equivalent QFloat (qf16 / qf32) sequences during DAG-to-DAG
instruction selection. v75 and earlier continue to emit
the original IEEE encodings unchanged.
The translation covers 22 intrinsics: arithmetic kernels
(vadd / vsub / vmpy / vmpy-acc) for both hf-hf and sf-hf
operand pairs, sign-bit manipulation (vabs / vfneg), the
non-IEEE min/max variants, the cross-domain conversions
(vcvt_hf_h, vcvt_h_hf, vcvt_sf_hf), and vassign_fp.
9 HVX IEEE intrinsics are not translated by this patch
and will be added incrementally: the byte/ubyte/uhalf
conversion variants (vcvt_b_hf, vcvt_hf_b, vcvt_ub_hf,
vcvt_hf_ub, vcvt_uh_hf, vcvt_hf_uh), vcvt_h_hf (v79
requires a runtime call), vcvt_hf_sf, and vdmpy_sf_hf.
isIEEEHVXIntrinsic excludes them, so they fall through
to the normal SelectCode path rather than hitting
llvm_unreachable (which would be UB in release builds).
Co-authored-by: Sumanth Gundapaneni <sgundapa at quicinc.com>
Co-authored-by: Santanu Das <quic_santdas at quicinc.com>
Added:
llvm/test/CodeGen/Hexagon/autohvx/ieeetoqfloat.ll
Modified:
llvm/lib/Target/Hexagon/HexagonISelDAGToDAG.cpp
llvm/lib/Target/Hexagon/HexagonISelDAGToDAG.h
llvm/lib/Target/Hexagon/HexagonISelDAGToDAGHVX.cpp
Removed:
################################################################################
diff --git a/llvm/lib/Target/Hexagon/HexagonISelDAGToDAG.cpp b/llvm/lib/Target/Hexagon/HexagonISelDAGToDAG.cpp
index cff3aa405742d..6380a48d78854 100644
--- a/llvm/lib/Target/Hexagon/HexagonISelDAGToDAG.cpp
+++ b/llvm/lib/Target/Hexagon/HexagonISelDAGToDAG.cpp
@@ -676,8 +676,17 @@ void HexagonDAGToDAGISel::SelectIntrinsicWChain(SDNode *N) {
}
void HexagonDAGToDAGISel::SelectIntrinsicWOChain(SDNode *N) {
- unsigned IID = N->getConstantOperandVal(0);
+ unsigned IID = cast<ConstantSDNode>(N->getOperand(0))->getZExtValue();
unsigned Bits;
+
+ // On v79 and above, IEEE HVX instructions are no longer present.
+ // The related intrinsics must be translated to QFloat implicitly in order
+ // to provide backward compatibility.
+ if (HST->useHVXV79Ops() && isIEEEHVXIntrinsic(IID)) {
+ translateIEEEIntrinsicToQFloat(N, IID);
+ return;
+ }
+
switch (IID) {
case Intrinsic::hexagon_S2_vsplatrb:
Bits = 8;
diff --git a/llvm/lib/Target/Hexagon/HexagonISelDAGToDAG.h b/llvm/lib/Target/Hexagon/HexagonISelDAGToDAG.h
index 2d23aeecda6d8..518e9b198a059 100644
--- a/llvm/lib/Target/Hexagon/HexagonISelDAGToDAG.h
+++ b/llvm/lib/Target/Hexagon/HexagonISelDAGToDAG.h
@@ -113,9 +113,12 @@ class HexagonDAGToDAGISel : public SelectionDAGISel {
void FDiv(SDNode *N);
void FastFDiv(SDNode *N);
- // Include the declarations autogenerated from the selection patterns.
- #define GET_DAGISEL_DECL
- #include "HexagonGenDAGISel.inc"
+ bool isIEEEHVXIntrinsic(unsigned);
+ void translateIEEEIntrinsicToQFloat(SDNode *N, unsigned &Opcode);
+
+// Include the declarations autogenerated from the selection patterns.
+#define GET_DAGISEL_DECL
+#include "HexagonGenDAGISel.inc"
private:
// This is really only to get access to ReplaceNode (which is a protected
diff --git a/llvm/lib/Target/Hexagon/HexagonISelDAGToDAGHVX.cpp b/llvm/lib/Target/Hexagon/HexagonISelDAGToDAGHVX.cpp
index ae369fe7ce901..e2b5f4769c75d 100644
--- a/llvm/lib/Target/Hexagon/HexagonISelDAGToDAGHVX.cpp
+++ b/llvm/lib/Target/Hexagon/HexagonISelDAGToDAGHVX.cpp
@@ -3009,3 +3009,467 @@ void HexagonDAGToDAGISel::SelectHVXDualOutput(SDNode *N) {
ReplaceUses(SDValue(N, 1), SDValue(Result, 1));
CurDAG->RemoveDeadNode(N);
}
+
+// Check if the intrinsic corresponds to IEEE HVX instruction.
+bool HexagonDAGToDAGISel::isIEEEHVXIntrinsic(unsigned Opcode) {
+ return Opcode == Intrinsic::hexagon_V6_vabs_hf_128B ||
+ Opcode == Intrinsic::hexagon_V6_vabs_sf_128B ||
+ Opcode == Intrinsic::hexagon_V6_vsub_hf_hf_128B ||
+ Opcode == Intrinsic::hexagon_V6_vadd_hf_hf_128B ||
+ Opcode == Intrinsic::hexagon_V6_vadd_sf_hf_128B ||
+ Opcode == Intrinsic::hexagon_V6_vsub_sf_hf_128B ||
+ Opcode == Intrinsic::hexagon_V6_vadd_sf_sf_128B ||
+ Opcode == Intrinsic::hexagon_V6_vsub_sf_sf_128B ||
+ Opcode == Intrinsic::hexagon_V6_vassign_fp_128B ||
+ Opcode == Intrinsic::hexagon_V6_vfmin_hf_128B ||
+ Opcode == Intrinsic::hexagon_V6_vfmin_sf_128B ||
+ Opcode == Intrinsic::hexagon_V6_vfmax_hf_128B ||
+ Opcode == Intrinsic::hexagon_V6_vfmax_sf_128B ||
+ Opcode == Intrinsic::hexagon_V6_vfneg_hf_128B ||
+ Opcode == Intrinsic::hexagon_V6_vfneg_sf_128B ||
+ Opcode == Intrinsic::hexagon_V6_vmpy_sf_hf_acc_128B ||
+ Opcode == Intrinsic::hexagon_V6_vmpy_hf_hf_acc_128B ||
+ Opcode == Intrinsic::hexagon_V6_vmpy_sf_hf_128B ||
+ Opcode == Intrinsic::hexagon_V6_vmpy_hf_hf_128B ||
+ Opcode == Intrinsic::hexagon_V6_vmpy_sf_sf_128B ||
+ Opcode == Intrinsic::hexagon_V6_vcvt_sf_hf_128B ||
+ Opcode == Intrinsic::hexagon_V6_vcvt_hf_h_128B;
+}
+
+// Translate IEEE HVX intrinsics to QFloat instructions.
+void HexagonDAGToDAGISel::translateIEEEIntrinsicToQFloat(SDNode *N,
+ unsigned &Opcode) {
+ SDLoc DL(N);
+ MVT ResTy = N->getValueType(0).getSimpleVT();
+ switch (Opcode) {
+ // v0.sf = vadd(v0.sf,v1.sf) is translated to
+ // v2.qf32 = vadd(v0.sf,v1.sf); v0.sf = v2.qf32.
+ case Intrinsic::hexagon_V6_vadd_sf_sf_128B: {
+ SDNode *AddNode = CurDAG->getMachineNode(
+ Hexagon::V6_vadd_sf, DL, ResTy, N->getOperand(1), N->getOperand(2));
+ SDNode *ConvNode = CurDAG->getMachineNode(Hexagon::V6_vconv_sf_qf32, DL,
+ ResTy, SDValue(AddNode, 0));
+ ReplaceUses(SDValue(N, 0), SDValue(ConvNode, 0));
+ CurDAG->RemoveDeadNode(N);
+ return;
+ }
+ // v0.sf = vsub(v0.sf,v1.sf) is translated to
+ // v2.qf32 = vsub(v0.sf,v1.sf); v0.sf = v2.qf32.
+ case Intrinsic::hexagon_V6_vsub_sf_sf_128B: {
+ SDNode *SubNode = CurDAG->getMachineNode(
+ Hexagon::V6_vsub_sf, DL, ResTy, N->getOperand(1), N->getOperand(2));
+ SDNode *ConvNode = CurDAG->getMachineNode(Hexagon::V6_vconv_sf_qf32, DL,
+ ResTy, SDValue(SubNode, 0));
+ ReplaceUses(SDValue(N, 0), SDValue(ConvNode, 0));
+ CurDAG->RemoveDeadNode(N);
+ return;
+ }
+ // v0.hf = vadd(v0.hf,v1.hf) is translated to
+ // v2.qf16 = vadd(v0.hf,v1.hf); v0.hf = v2.qf16.
+ case Intrinsic::hexagon_V6_vadd_hf_hf_128B: {
+ SDNode *AddNode = CurDAG->getMachineNode(
+ Hexagon::V6_vadd_hf, DL, ResTy, N->getOperand(1), N->getOperand(2));
+ SDNode *ConvNode = CurDAG->getMachineNode(Hexagon::V6_vconv_hf_qf16, DL,
+ ResTy, SDValue(AddNode, 0));
+ ReplaceUses(SDValue(N, 0), SDValue(ConvNode, 0));
+ CurDAG->RemoveDeadNode(N);
+ return;
+ }
+ // v0.hf = vsub(v0.hf,v1.hf) is translated to
+ // v2.qf16 = vsub(v0.hf,v1.hf); v0.hf = v2.qf16.
+ case Intrinsic::hexagon_V6_vsub_hf_hf_128B: {
+ SDNode *SubNode = CurDAG->getMachineNode(
+ Hexagon::V6_vsub_hf, DL, ResTy, N->getOperand(1), N->getOperand(2));
+ SDNode *ConvNode = CurDAG->getMachineNode(Hexagon::V6_vconv_hf_qf16, DL,
+ ResTy, SDValue(SubNode, 0));
+ ReplaceUses(SDValue(N, 0), SDValue(ConvNode, 0));
+ CurDAG->RemoveDeadNode(N);
+ return;
+ }
+ // v1:0.sf = vadd(v0.hf,v1.hf) is translated to
+ // r2 = #15360; v2.h = vsplat(r2);
+ // v5:4.qf32 = vmpy(v1.hf,v2.hf); v31:30.qf32 = vmpy(v0.hf,v2.hf)
+ // v4.qf32 = vadd(v30.qf32,v4.qf32); v3.qf32 = vadd(v31.qf32,v5.qf32)
+ // v0.sf = v4.qf32; v1.sf = v3.qf32
+ // Widen the hf operands to sf by doing a widening multiply with 1.0f
+ // and perform the add.
+ case Intrinsic::hexagon_V6_vadd_sf_hf_128B: {
+ SDValue Const = CurDAG->getTargetConstant(0x3C00U, DL, MVT::i32);
+ SDNode *SplatPseudoNode =
+ CurDAG->getMachineNode(Hexagon::PS_vsplatih, DL, ResTy, Const);
+ SDNode *MpyNodeOp1 =
+ CurDAG->getMachineNode(Hexagon::V6_vmpy_qf32_hf, DL, ResTy,
+ N->getOperand(1), SDValue(SplatPseudoNode, 0));
+ SDNode *MpyNodeOp2 =
+ CurDAG->getMachineNode(Hexagon::V6_vmpy_qf32_hf, DL, ResTy,
+ N->getOperand(2), SDValue(SplatPseudoNode, 0));
+
+ SDValue LoRegOp1 = CurDAG->getTargetExtractSubreg(
+ Hexagon::vsub_lo, DL, MVT::v32i32, SDValue(MpyNodeOp1, 0));
+ SDValue HiRegOp1 = CurDAG->getTargetExtractSubreg(
+ Hexagon::vsub_hi, DL, MVT::v32i32, SDValue(MpyNodeOp1, 0));
+ SDValue LoRegOp2 = CurDAG->getTargetExtractSubreg(
+ Hexagon::vsub_lo, DL, MVT::v32i32, SDValue(MpyNodeOp2, 0));
+ SDValue HiRegOp2 = CurDAG->getTargetExtractSubreg(
+ Hexagon::vsub_hi, DL, MVT::v32i32, SDValue(MpyNodeOp2, 0));
+
+ SDNode *LoAddNode = CurDAG->getMachineNode(Hexagon::V6_vadd_qf32, DL,
+ MVT::v32i32, LoRegOp1, LoRegOp2);
+ SDNode *HiAddNode = CurDAG->getMachineNode(Hexagon::V6_vadd_qf32, DL,
+ MVT::v32i32, HiRegOp1, HiRegOp2);
+
+ SDNode *ConvLoNode = CurDAG->getMachineNode(
+ Hexagon::V6_vconv_sf_qf32, DL, MVT::v32i32, SDValue(LoAddNode, 0));
+ SDNode *ConvHiNode = CurDAG->getMachineNode(
+ Hexagon::V6_vconv_sf_qf32, DL, MVT::v32i32, SDValue(HiAddNode, 0));
+
+ SDValue RC =
+ CurDAG->getTargetConstant(Hexagon::HvxWRRegClassID, DL, MVT::i32);
+ SDValue SubRegL = CurDAG->getTargetConstant(Hexagon::vsub_lo, DL, MVT::i32);
+ SDValue SubRegH = CurDAG->getTargetConstant(Hexagon::vsub_hi, DL, MVT::i32);
+ const SDValue Ops[] = {RC, SDValue(ConvHiNode, 0), SubRegH,
+ SDValue(ConvLoNode, 0), SubRegL};
+ SDNode *RS = CurDAG->getMachineNode(TargetOpcode::REG_SEQUENCE, DL,
+ MVT::v64i32, Ops);
+
+ ReplaceUses(SDValue(N, 0), SDValue(RS, 0));
+ CurDAG->RemoveDeadNode(N);
+ return;
+ }
+ // v1:0.sf = vsub(v0.hf,v1.hf) is translated to
+ // r2 = #15360; v2.h = vsplat(r2);
+ // v5:4.qf32 = vmpy(v1.hf,v2.hf); v31:30.qf32 = vmpy(v0.hf,v2.hf)
+ // v4.qf32 = vsub(v30.qf32,v4.qf32); v3.qf32 = vsub(v31.qf32,v5.qf32)
+ // v0.sf = v4.qf32; v1.sf = v3.qf32
+ // Widen the hf operands to sf by doing a widening multiply with 1.0f
+ // and perform the sub.
+ case Intrinsic::hexagon_V6_vsub_sf_hf_128B: {
+ SDValue Const = CurDAG->getTargetConstant(0x3C00U, DL, MVT::i32);
+ SDNode *SplatPseudoNode =
+ CurDAG->getMachineNode(Hexagon::PS_vsplatih, DL, ResTy, Const);
+ SDNode *MpyNodeOp1 =
+ CurDAG->getMachineNode(Hexagon::V6_vmpy_qf32_hf, DL, ResTy,
+ N->getOperand(1), SDValue(SplatPseudoNode, 0));
+ SDNode *MpyNodeOp2 =
+ CurDAG->getMachineNode(Hexagon::V6_vmpy_qf32_hf, DL, ResTy,
+ N->getOperand(2), SDValue(SplatPseudoNode, 0));
+
+ SDValue LoRegOp1 = CurDAG->getTargetExtractSubreg(
+ Hexagon::vsub_lo, DL, MVT::v32i32, SDValue(MpyNodeOp1, 0));
+ SDValue HiRegOp1 = CurDAG->getTargetExtractSubreg(
+ Hexagon::vsub_hi, DL, MVT::v32i32, SDValue(MpyNodeOp1, 0));
+ SDValue LoRegOp2 = CurDAG->getTargetExtractSubreg(
+ Hexagon::vsub_lo, DL, MVT::v32i32, SDValue(MpyNodeOp2, 0));
+ SDValue HiRegOp2 = CurDAG->getTargetExtractSubreg(
+ Hexagon::vsub_hi, DL, MVT::v32i32, SDValue(MpyNodeOp2, 0));
+
+ SDNode *LoSubNode = CurDAG->getMachineNode(Hexagon::V6_vsub_qf32, DL,
+ MVT::v32i32, LoRegOp1, LoRegOp2);
+ SDNode *HiSubNode = CurDAG->getMachineNode(Hexagon::V6_vsub_qf32, DL,
+ MVT::v32i32, HiRegOp1, HiRegOp2);
+
+ SDNode *ConvLoNode = CurDAG->getMachineNode(
+ Hexagon::V6_vconv_sf_qf32, DL, MVT::v32i32, SDValue(LoSubNode, 0));
+ SDNode *ConvHiNode = CurDAG->getMachineNode(
+ Hexagon::V6_vconv_sf_qf32, DL, MVT::v32i32, SDValue(HiSubNode, 0));
+
+ SDValue RC =
+ CurDAG->getTargetConstant(Hexagon::HvxWRRegClassID, DL, MVT::i32);
+ SDValue SubRegL = CurDAG->getTargetConstant(Hexagon::vsub_lo, DL, MVT::i32);
+ SDValue SubRegH = CurDAG->getTargetConstant(Hexagon::vsub_hi, DL, MVT::i32);
+ const SDValue Ops[] = {RC, SDValue(ConvHiNode, 0), SubRegH,
+ SDValue(ConvLoNode, 0), SubRegL};
+ SDNode *RS = CurDAG->getMachineNode(TargetOpcode::REG_SEQUENCE, DL,
+ MVT::v64i32, Ops);
+
+ ReplaceUses(SDValue(N, 0), SDValue(RS, 0));
+ CurDAG->RemoveDeadNode(N);
+ return;
+ }
+ // v0.w = vfmv(v1.w) is translated to v0 = v1.
+ case Intrinsic::hexagon_V6_vassign_fp_128B: {
+ SDNode *AssignNode = CurDAG->getMachineNode(Hexagon::V6_vassign, DL, ResTy,
+ N->getOperand(1));
+ ReplaceUses(SDValue(N, 0), SDValue(AssignNode, 0));
+ CurDAG->RemoveDeadNode(N);
+ return;
+ }
+ // v0.hf = vmpy(v0.hf,v1.hf) is translated to
+ // v1:0.qf32 = vmpy(v0.hf,v1.hf); v0.hf = v1:0.qf32.
+ case Intrinsic::hexagon_V6_vmpy_hf_hf_128B: {
+ SDNode *MpyNode =
+ CurDAG->getMachineNode(Hexagon::V6_vmpy_qf32_hf, DL, MVT::v64i32,
+ N->getOperand(1), N->getOperand(2));
+ SDNode *ConvNode = CurDAG->getMachineNode(Hexagon::V6_vconv_hf_qf32, DL,
+ ResTy, SDValue(MpyNode, 0));
+ ReplaceUses(SDValue(N, 0), SDValue(ConvNode, 0));
+ CurDAG->RemoveDeadNode(N);
+ return;
+ }
+ // v0.sf = vmpy(v0.sf,v1.sf) is translated to
+ // v2.qf32 = vmpy(v0.sf,v1.sf); v0.sf = v2.qf32.
+ case Intrinsic::hexagon_V6_vmpy_sf_sf_128B: {
+ SDNode *MpyNode =
+ CurDAG->getMachineNode(Hexagon::V6_vmpy_qf32_sf, DL, ResTy,
+ N->getOperand(1), N->getOperand(2));
+ SDNode *ConvNode = CurDAG->getMachineNode(Hexagon::V6_vconv_sf_qf32, DL,
+ ResTy, SDValue(MpyNode, 0));
+ ReplaceUses(SDValue(N, 0), SDValue(ConvNode, 0));
+ CurDAG->RemoveDeadNode(N);
+ return;
+ }
+ // v1:0.sf = vmpy(v0.hf,v1.hf) is translated to
+ // v3:2.qf32 = vmpy(v0.hf,v1.hf); v0.sf = v2.qf32 ; v1.sf = v3.qf32.
+ case Intrinsic::hexagon_V6_vmpy_sf_hf_128B: {
+ SDNode *MpyNode =
+ CurDAG->getMachineNode(Hexagon::V6_vmpy_qf32_hf, DL, ResTy,
+ N->getOperand(1), N->getOperand(2));
+ SDValue LoReg = CurDAG->getTargetExtractSubreg(
+ Hexagon::vsub_lo, DL, MVT::v32i32, SDValue(MpyNode, 0));
+ SDValue HiReg = CurDAG->getTargetExtractSubreg(
+ Hexagon::vsub_hi, DL, MVT::v32i32, SDValue(MpyNode, 0));
+ SDNode *ConvLoNode = CurDAG->getMachineNode(Hexagon::V6_vconv_sf_qf32, DL,
+ MVT::v32i32, LoReg);
+ SDNode *ConvHiNode = CurDAG->getMachineNode(Hexagon::V6_vconv_sf_qf32, DL,
+ MVT::v32i32, HiReg);
+
+ SDValue RC =
+ CurDAG->getTargetConstant(Hexagon::HvxWRRegClassID, DL, MVT::i32);
+ SDValue SubRegL = CurDAG->getTargetConstant(Hexagon::vsub_lo, DL, MVT::i32);
+ SDValue SubRegH = CurDAG->getTargetConstant(Hexagon::vsub_hi, DL, MVT::i32);
+ const SDValue Ops[] = {RC, SDValue(ConvHiNode, 0), SubRegH,
+ SDValue(ConvLoNode, 0), SubRegL};
+ SDNode *RS = CurDAG->getMachineNode(TargetOpcode::REG_SEQUENCE, DL,
+ MVT::v64i32, Ops);
+
+ ReplaceUses(SDValue(N, 0), SDValue(RS, 0));
+ CurDAG->RemoveDeadNode(N);
+ return;
+ }
+ // v0.hf += vmpy(v1.hf,v2.hf) is translated to
+ // v7:6.qf32 = vmpy(v1.hf, v2.hf) // widening multiply
+ // V5:4.qf32 = vmpy(v0.hf,1.0) // Convert accum to qf32
+ // V4.qf32 = vadd(V6.qf32, V4.qf32) // accumulation
+ // V5.qf32 = vadd(V7.qf32, V5.qf32) // accumulation
+ // V4.hf = V5:4.qf32
+ case Intrinsic::hexagon_V6_vmpy_hf_hf_acc_128B: {
+ SDNode *MpyNode =
+ CurDAG->getMachineNode(Hexagon::V6_vmpy_qf32_hf, DL, MVT::v64i32,
+ N->getOperand(2), N->getOperand(3));
+
+ SDValue Const = CurDAG->getTargetConstant(0x3C00U, DL, MVT::i32);
+ SDNode *SplatConstNode =
+ CurDAG->getMachineNode(Hexagon::PS_vsplatih, DL, MVT::v64i32, Const);
+ SDNode *WidenAcc =
+ CurDAG->getMachineNode(Hexagon::V6_vmpy_qf32_hf, DL, MVT::v64i32,
+ N->getOperand(1), SDValue(SplatConstNode, 0));
+
+ SDValue LoMpyNode = CurDAG->getTargetExtractSubreg(
+ Hexagon::vsub_lo, DL, MVT::v32i32, SDValue(MpyNode, 0));
+ SDValue HiMpyNode = CurDAG->getTargetExtractSubreg(
+ Hexagon::vsub_hi, DL, MVT::v32i32, SDValue(MpyNode, 0));
+ SDValue LoWidenAcc = CurDAG->getTargetExtractSubreg(
+ Hexagon::vsub_lo, DL, MVT::v32i32, SDValue(WidenAcc, 0));
+ SDValue HiWidenAcc = CurDAG->getTargetExtractSubreg(
+ Hexagon::vsub_hi, DL, MVT::v32i32, SDValue(WidenAcc, 0));
+
+ SDNode *LoAddNode = CurDAG->getMachineNode(
+ Hexagon::V6_vadd_qf32, DL, MVT::v32i32, LoWidenAcc, LoMpyNode);
+ SDNode *HiAddNode = CurDAG->getMachineNode(
+ Hexagon::V6_vadd_qf32, DL, MVT::v32i32, HiWidenAcc, HiMpyNode);
+
+ SDValue RC =
+ CurDAG->getTargetConstant(Hexagon::HvxWRRegClassID, DL, MVT::i32);
+ SDValue SubRegL = CurDAG->getTargetConstant(Hexagon::vsub_lo, DL, MVT::i32);
+ SDValue SubRegH = CurDAG->getTargetConstant(Hexagon::vsub_hi, DL, MVT::i32);
+ const SDValue Ops[] = {RC, SDValue(HiAddNode, 0), SubRegH,
+ SDValue(LoAddNode, 0), SubRegL};
+ SDNode *RS = CurDAG->getMachineNode(TargetOpcode::REG_SEQUENCE, DL,
+ MVT::v64i32, Ops);
+
+ SDNode *ConvNode = CurDAG->getMachineNode(Hexagon::V6_vconv_hf_qf32, DL,
+ ResTy, SDValue(RS, 0));
+ ReplaceUses(SDValue(N, 0), SDValue(ConvNode, 0));
+ CurDAG->RemoveDeadNode(N);
+ return;
+ }
+ // v1:0.sf += vmpy(v2.hf,v3.hf) is translated to
+ // v3:2.qf32 = vmpy(v2.hf,v3.hf);
+ // v0.qf32 = vadd(v2.qf32,v0.sf); v1.qf32 = vadd(v3.qf32,v1.sf);
+ // v0.sf = v0.qf32; v1.sf = v1.qf32
+ case Intrinsic::hexagon_V6_vmpy_sf_hf_acc_128B: {
+ SDNode *MpyNode =
+ CurDAG->getMachineNode(Hexagon::V6_vmpy_qf32_hf, DL, ResTy,
+ N->getOperand(2), N->getOperand(3));
+
+ SDValue LoRegMpy = CurDAG->getTargetExtractSubreg(
+ Hexagon::vsub_lo, DL, MVT::v32i32, SDValue(MpyNode, 0));
+ SDValue HiRegMpy = CurDAG->getTargetExtractSubreg(
+ Hexagon::vsub_hi, DL, MVT::v32i32, SDValue(MpyNode, 0));
+
+ SDValue LoRegDest = CurDAG->getTargetExtractSubreg(
+ Hexagon::vsub_lo, DL, MVT::v32i32, N->getOperand(1));
+ SDValue HiRegDest = CurDAG->getTargetExtractSubreg(
+ Hexagon::vsub_hi, DL, MVT::v32i32, N->getOperand(1));
+
+ SDNode *LoAddNode = CurDAG->getMachineNode(
+ Hexagon::V6_vadd_qf32_mix, DL, MVT::v32i32, LoRegMpy, LoRegDest);
+ SDNode *HiAddNode = CurDAG->getMachineNode(
+ Hexagon::V6_vadd_qf32_mix, DL, MVT::v32i32, HiRegMpy, HiRegDest);
+
+ SDNode *ConvLoNode = CurDAG->getMachineNode(
+ Hexagon::V6_vconv_sf_qf32, DL, MVT::v32i32, SDValue(LoAddNode, 0));
+ SDNode *ConvHiNode = CurDAG->getMachineNode(
+ Hexagon::V6_vconv_sf_qf32, DL, MVT::v32i32, SDValue(HiAddNode, 0));
+
+ SDValue RC =
+ CurDAG->getTargetConstant(Hexagon::HvxWRRegClassID, DL, MVT::i32);
+ SDValue SubRegL = CurDAG->getTargetConstant(Hexagon::vsub_lo, DL, MVT::i32);
+ SDValue SubRegH = CurDAG->getTargetConstant(Hexagon::vsub_hi, DL, MVT::i32);
+ const SDValue Ops[] = {RC, SDValue(ConvHiNode, 0), SubRegH,
+ SDValue(ConvLoNode, 0), SubRegL};
+ SDNode *RS = CurDAG->getMachineNode(TargetOpcode::REG_SEQUENCE, DL,
+ MVT::v64i32, Ops);
+
+ ReplaceUses(SDValue(N, 0), SDValue(RS, 0));
+ CurDAG->RemoveDeadNode(N);
+ return;
+ }
+ // v0.hf = vfmin(v0.hf,v1.hf) is translated to v0.hf = vmin(v0.hf,v1.hf).
+ case Intrinsic::hexagon_V6_vfmin_hf_128B: {
+ SDNode *MinNode = CurDAG->getMachineNode(
+ Hexagon::V6_vmin_hf, DL, ResTy, N->getOperand(1), N->getOperand(2));
+ ReplaceUses(SDValue(N, 0), SDValue(MinNode, 0));
+ CurDAG->RemoveDeadNode(N);
+ return;
+ }
+ // v0.sf = vfmin(v0.sf,v1.sf) is translated to v0.sf = vmin(v0.sf,v1.sf).
+ case Intrinsic::hexagon_V6_vfmin_sf_128B: {
+ SDNode *MinNode = CurDAG->getMachineNode(
+ Hexagon::V6_vmin_sf, DL, ResTy, N->getOperand(1), N->getOperand(2));
+ ReplaceUses(SDValue(N, 0), SDValue(MinNode, 0));
+ CurDAG->RemoveDeadNode(N);
+ return;
+ }
+ // v0.hf = vfmax(v0.hf,v1.hf) is translated to v0.hf = vmax(v0.hf,v1.hf).
+ case Intrinsic::hexagon_V6_vfmax_hf_128B: {
+ SDNode *MaxNode = CurDAG->getMachineNode(
+ Hexagon::V6_vmax_hf, DL, ResTy, N->getOperand(1), N->getOperand(2));
+ ReplaceUses(SDValue(N, 0), SDValue(MaxNode, 0));
+ CurDAG->RemoveDeadNode(N);
+ return;
+ }
+ // v0.sf = vfmax(v0.sf,v1.sf) is translated to v0.sf = vmax(v0.sf,v1.sf).
+ case Intrinsic::hexagon_V6_vfmax_sf_128B: {
+ SDNode *MaxNode = CurDAG->getMachineNode(
+ Hexagon::V6_vmax_sf, DL, ResTy, N->getOperand(1), N->getOperand(2));
+ ReplaceUses(SDValue(N, 0), SDValue(MaxNode, 0));
+ CurDAG->RemoveDeadNode(N);
+ return;
+ }
+ // v0.hf = vabs(v0.hf) is translated to
+ // r2 = #32767 ; v1.h = vsplat(r2) ; v0 = vand(v0,v1)
+ // Reset the sign bit by splatting 0x7FFF and does a vector and.
+ case Intrinsic::hexagon_V6_vabs_hf_128B: {
+ SDValue Const = CurDAG->getTargetConstant(0x7FFFU, DL, MVT::i32);
+ SDNode *SplatPseudoNode =
+ CurDAG->getMachineNode(Hexagon::PS_vsplatih, DL, ResTy, Const);
+ SDNode *AndNode =
+ CurDAG->getMachineNode(Hexagon::V6_vand, DL, ResTy, N->getOperand(1),
+ SDValue(SplatPseudoNode, 0));
+ ReplaceUses(SDValue(N, 0), SDValue(AndNode, 0));
+ CurDAG->RemoveDeadNode(N);
+ return;
+ }
+ // v0.sf = vabs(v0.sf) is translated to
+ // r2 = ##2147483647 ; v1 = vsplat(r2) ; v0 = vand(v0,v1)
+ // Reset the sign bit by splatting 0x7FFF FFFF and does a vector and.
+ case Intrinsic::hexagon_V6_vabs_sf_128B: {
+ SDValue Const = CurDAG->getTargetConstant(0x7FFFFFFFU, DL, MVT::i32);
+ SDNode *SplatPseudoNode =
+ CurDAG->getMachineNode(Hexagon::PS_vsplatiw, DL, ResTy, Const);
+ SDNode *AndNode =
+ CurDAG->getMachineNode(Hexagon::V6_vand, DL, ResTy, N->getOperand(1),
+ SDValue(SplatPseudoNode, 0));
+ ReplaceUses(SDValue(N, 0), SDValue(AndNode, 0));
+ CurDAG->RemoveDeadNode(N);
+ return;
+ }
+ // v0.hf = vfneg(v0.hf) is translated to
+ // r2 = #32768 ; v1.h = vsplat(r2) ; v0 = vxor(v0,v1)
+ // Flip the sign bit by splatting 0x8000 and does a vector xor.
+ case Intrinsic::hexagon_V6_vfneg_hf_128B: {
+ SDValue Const = CurDAG->getTargetConstant(0x8000U, DL, MVT::i32);
+ SDNode *SplatPseudoNode =
+ CurDAG->getMachineNode(Hexagon::PS_vsplatih, DL, ResTy, Const);
+ SDNode *XorNode =
+ CurDAG->getMachineNode(Hexagon::V6_vxor, DL, ResTy, N->getOperand(1),
+ SDValue(SplatPseudoNode, 0));
+ ReplaceUses(SDValue(N, 0), SDValue(XorNode, 0));
+ CurDAG->RemoveDeadNode(N);
+ return;
+ }
+ // v0.sf = vfneg(v0.sf) is translated to
+ // r2 = ##-2147483648 ; v1 = vsplat(r2) ; v0 = vxor(v0,v1)
+ // Flip the sign bit by splatting 0x8000 0000 and does a vector xor.
+ case Intrinsic::hexagon_V6_vfneg_sf_128B: {
+ SDValue Const = CurDAG->getTargetConstant(0x80000000U, DL, MVT::i32);
+ SDNode *SplatPseudoNode =
+ CurDAG->getMachineNode(Hexagon::PS_vsplatiw, DL, ResTy, Const);
+ SDNode *XorNode =
+ CurDAG->getMachineNode(Hexagon::V6_vxor, DL, ResTy, N->getOperand(1),
+ SDValue(SplatPseudoNode, 0));
+ ReplaceUses(SDValue(N, 0), SDValue(XorNode, 0));
+ CurDAG->RemoveDeadNode(N);
+ return;
+ }
+ // v0.h = vcvt(v0.hf) is translated to v0.hf = v0.h.
+ case Intrinsic::hexagon_V6_vcvt_hf_h_128B: {
+ SDNode *ConvNode = CurDAG->getMachineNode(Hexagon::V6_vconv_hf_h, DL, ResTy,
+ N->getOperand(1));
+ ReplaceUses(SDValue(N, 0), SDValue(ConvNode, 0));
+ CurDAG->RemoveDeadNode(N);
+ return;
+ }
+ // v1:0.sf = vcvt(v0.hf) is translated to
+ // r2 = #15360; v1.h = vsplat(r2); v3:2.qf32 = vmpy(v0.hf,v1.hf);
+ // v0.sf = v2.qf32; v1.sf = v3.qf32
+ // Do a widening multiply with 1.0f to convert the hf to sf.
+ case Intrinsic::hexagon_V6_vcvt_sf_hf_128B: {
+ SDValue Const = CurDAG->getTargetConstant(0x3C00U, DL, MVT::i32);
+ SDNode *SplatPseudoNode =
+ CurDAG->getMachineNode(Hexagon::PS_vsplatih, DL, ResTy, Const);
+ SDNode *MpyNode =
+ CurDAG->getMachineNode(Hexagon::V6_vmpy_qf32_hf, DL, ResTy,
+ N->getOperand(1), SDValue(SplatPseudoNode, 0));
+
+ SDValue LoReg = CurDAG->getTargetExtractSubreg(
+ Hexagon::vsub_lo, DL, MVT::v32i32, SDValue(MpyNode, 0));
+ SDValue HiReg = CurDAG->getTargetExtractSubreg(
+ Hexagon::vsub_hi, DL, MVT::v32i32, SDValue(MpyNode, 0));
+ SDNode *ConvLoNode = CurDAG->getMachineNode(Hexagon::V6_vconv_sf_qf32, DL,
+ MVT::v32i32, LoReg);
+ SDNode *ConvHiNode = CurDAG->getMachineNode(Hexagon::V6_vconv_sf_qf32, DL,
+ MVT::v32i32, HiReg);
+
+ SDValue RC =
+ CurDAG->getTargetConstant(Hexagon::HvxWRRegClassID, DL, MVT::i32);
+ SDValue SubRegL = CurDAG->getTargetConstant(Hexagon::vsub_lo, DL, MVT::i32);
+ SDValue SubRegH = CurDAG->getTargetConstant(Hexagon::vsub_hi, DL, MVT::i32);
+ const SDValue Ops[] = {RC, SDValue(ConvHiNode, 0), SubRegH,
+ SDValue(ConvLoNode, 0), SubRegL};
+ SDNode *RS = CurDAG->getMachineNode(TargetOpcode::REG_SEQUENCE, DL,
+ MVT::v64i32, Ops);
+
+ ReplaceUses(SDValue(N, 0), SDValue(RS, 0));
+ CurDAG->RemoveDeadNode(N);
+ return;
+ }
+
+ default:
+ llvm_unreachable("Unexpected HVX IEEE intrinsic: no QFloat translation");
+ return;
+ }
+
+ return;
+}
diff --git a/llvm/test/CodeGen/Hexagon/autohvx/ieeetoqfloat.ll b/llvm/test/CodeGen/Hexagon/autohvx/ieeetoqfloat.ll
new file mode 100644
index 0000000000000..86204f17257d9
--- /dev/null
+++ b/llvm/test/CodeGen/Hexagon/autohvx/ieeetoqfloat.ll
@@ -0,0 +1,473 @@
+; RUN: llc -march=hexagon -mattr=+hvx-ieee-fp,+hvx-length128b,+hvxv79 < %s \
+; RUN: | FileCheck %s --enable-var-scope
+; RUN: llc -march=hexagon -mattr=+hvx-ieee-fp,+hvx-length128b,+hvxv81 < %s \
+; RUN: | FileCheck %s --enable-var-scope
+; RUN: llc -march=hexagon -mattr=+hvx-ieee-fp,+hvx-length128b,+hvxv75 < %s \
+; RUN: | FileCheck %s --check-prefix=V75 --enable-var-scope
+
+;On v79 and above,
+;v0.hf = vabs(v0.hf) is translated to
+;r2 = #32767 ; v1.h = vsplat(r2) ; v0 = vand(v0,v1)
+;Reset the sign bit by splatting 0x7FFF and does a vector and.
+
+; CHECK-LABEL: vec_abs_hf:
+; CHECK-NOT: v{{.*}}.hf = vabs(v{{.*}}.hf)
+; #0x7FFF
+; CHECK: r[[REG0:[0-9]+]] = #32767
+; CHECK: v[[REG1:[0-9]+]].h = vsplat(r[[REG0]])
+; CHECK: v{{.*}} = vand({{.*}}[[REG1]]
+
+; V75-LABEL: vec_abs_hf:
+; V75: v{{.*}}.hf = vabs(v{{.*}}.hf)
+
+define dso_local inreg <32 x i32> @vec_abs_hf(<32 x i32> noundef %a) local_unnamed_addr {
+entry:
+ %0 = tail call <32 x i32> @llvm.hexagon.V6.vabs.hf.128B(<32 x i32> %a)
+ ret <32 x i32> %0
+}
+
+;On v79 and above,
+;v0.sf = vabs(v0.sf) is translated to
+;r2 = ##2147483647 ; v1 = vsplat(r2) ; v0 = vand(v0,v1)
+;Reset the sign bit by splatting 0x7FFF FFFF and does a vector and.
+
+; CHECK-LABEL: vec_abs_sf:
+; CHECK-NOT: v{{.*}}.sf = vabs(v{{.*}}.sf)
+; #0x7FFF FFFF
+; CHECK: r[[REG0:[0-9]+]] = ##2147483647
+; CHECK: v[[REG1:[0-9]+]] = vsplat(r[[REG0]])
+; CHECK: v{{.*}} = vand({{.*}}[[REG1]]
+
+; V75-LABEL: vec_abs_sf:
+; V75: v{{.*}}.sf = vabs(v{{.*}}.sf)
+
+define dso_local inreg <32 x i32> @vec_abs_sf(<32 x i32> noundef %a) local_unnamed_addr {
+entry:
+ %0 = tail call <32 x i32> @llvm.hexagon.V6.vabs.sf.128B(<32 x i32> %a)
+ ret <32 x i32> %0
+}
+
+;On v79 and above,
+;v0.w = vfmv(v1.w) is translated to v0 = v1.
+
+; CHECK-LABEL: vec_assign:
+; CHECK-NOT: v{{.*}}.w = vfmv(v{{.*}}.w)
+; CHECK: v[[REG0:[0-9]+]] = v[[REG0]]
+
+; V75-LABEL: vec_assign:
+; V75: v{{.*}}.w = vfmv(v{{.*}}.w)
+
+define dso_local inreg <32 x i32> @vec_assign(<32 x i32> noundef %a) local_unnamed_addr {
+entry:
+ %0 = tail call <32 x i32> @llvm.hexagon.V6.vassign.fp.128B(<32 x i32> %a)
+ ret <32 x i32> %0
+}
+
+;On v79 and above,
+;v0.hf = vadd(v0.hf,v1.hf) is translated to
+;v2.qf16 = vadd(v0.hf,v1.hf); v0.hf = v2.qf16
+
+; CHECK-LABEL: vec_add_hf:
+; CHECK-NOT: v{{.*}}.hf = vadd(v{{.*}}.hf,v{{.*}}.hf)
+; CHECK: v[[REG0:[0-9]+]].qf16 = vadd(v{{.*}}.hf,v{{.*}}.hf)
+; CHECK: v0.hf = v[[REG0]].qf16
+
+; V75-LABEL: vec_add_hf:
+; V75: v{{.*}}.hf = vadd(v{{.*}}.hf,v{{.*}}.hf)
+
+define dso_local inreg <32 x i32> @vec_add_hf(<32 x i32> noundef %a, <32 x i32> noundef %b) local_unnamed_addr {
+entry:
+ %0 = tail call <32 x i32> @llvm.hexagon.V6.vadd.hf.hf.128B(<32 x i32> %a, <32 x i32> %b)
+ ret <32 x i32> %0
+}
+
+;On v79 and above,
+;v0.sf = vadd(v0.sf,v1.sf) is translated to
+;v2.qf32 = vadd(v0.sf,v1.sf); v0.sf = v2.qf32
+
+; CHECK-LABEL: vec_add_sf:
+; CHECK-NOT: v{{.*}}.sf = vadd(v{{.*}}.sf,v{{.*}}.sf)
+; CHECK: v[[REG0:[0-9]+]].qf32 = vadd(v{{.*}}.sf,v{{.*}}.sf)
+; CHECK: v0.sf = v[[REG0]].qf32
+
+; V75-LABEL: vec_add_sf:
+; V75: v{{.*}}.sf = vadd(v{{.*}}.sf,v{{.*}}.sf)
+
+define dso_local inreg <32 x i32> @vec_add_sf(<32 x i32> noundef %a, <32 x i32> noundef %b) local_unnamed_addr {
+entry:
+ %0 = tail call <32 x i32> @llvm.hexagon.V6.vadd.sf.sf.128B(<32 x i32> %a, <32 x i32> %b)
+ ret <32 x i32> %0
+}
+
+;On v79 and above,
+;v1:0.sf = vadd(v0.hf,v1.hf) is translated to
+;r2 = #15360; v2.h = vsplat(r2);
+;v5:4.qf32 = vmpy(v1.hf,v2.hf); v31:30.qf32 = vmpy(v0.hf,v2.hf)
+;v4.qf32 = vadd(v30.qf32,v4.qf32); v3.qf32 = vadd(v31.qf32,v5.qf32)
+;v0.sf = v4.qf32; v1.sf = v3.qf32
+;Widen the hf operands to sf by doing a widening multiply with 1.0f
+;and perform the add.
+
+; CHECK-LABEL: sfaddhfhf:
+; CHECK-NOT: v1:0.sf = vadd(v0.hf,v1.hf)
+; CHECK: r[[REG0:[0-9]+]] = #15360
+; CHECK: v[[REG1:[0-9]+]].h = vsplat(r[[REG0]])
+; CHECK-DAG: v[[REG2:[0-9]+]]:[[REG3:[0-9]+]].qf32 = vmpy(v{{.*}}.hf,v[[REG1]].hf)
+; CHECK-DAG: v[[REG4:[0-9]+]]:[[REG5:[0-9]+]].qf32 = vmpy(v{{.*}}.hf,v[[REG1]].hf)
+; CHECK-DAG: v[[REG6:[0-9]+]].qf32 = vadd(v[[REG5]].qf32,v[[REG3]].qf32)
+; CHECK-DAG: v[[REG7:[0-9]+]].qf32 = vadd(v[[REG4]].qf32,v[[REG2]].qf32)
+; CHECK-DAG: v{{.*}}.sf = v[[REG6]].qf32
+; CHECK-DAG: v{{.*}}.sf = v[[REG7]].qf32
+
+; V75-LABEL: sfaddhfhf:
+; CHECK-NOT: v1:0.sf = vadd(v0.hf,v1.hf)
+; V75: v1:0.sf = vadd(v0.hf,v1.hf)
+
+define dso_local inreg <64 x i32> @sfaddhfhf(<32 x i32> noundef %Vu, <32 x i32> noundef %Vv) local_unnamed_addr {
+entry:
+ %0 = tail call <64 x i32> @llvm.hexagon.V6.vadd.sf.hf.128B(<32 x i32> %Vu, <32 x i32> %Vv)
+ ret <64 x i32> %0
+}
+
+;On v79 and above,
+;v0.hf = vsub(v0.hf,v1.hf) is translated to
+;v2.qf16 = vsub(v0.hf,v1.hf); v0.hf = v2.qf16
+
+; CHECK-LABEL: vec_sub_hf:
+; CHECK-NOT: v{{.*}}.hf = vsub(v{{.*}}.hf,v{{.*}}.hf)
+; CHECK: v[[REG0:[0-9]+]].qf16 = vsub(v{{.*}}.hf,v{{.*}}.hf)
+; CHECK: v0.hf = v[[REG0]].qf16
+
+; V75-LABEL: vec_sub_hf:
+; V75: v{{.*}}.hf = vsub(v{{.*}}.hf,v{{.*}}.hf)
+
+define dso_local inreg <32 x i32> @vec_sub_hf(<32 x i32> noundef %a, <32 x i32> noundef %b) local_unnamed_addr {
+entry:
+ %0 = tail call <32 x i32> @llvm.hexagon.V6.vsub.hf.hf.128B(<32 x i32> %a, <32 x i32> %b)
+ ret <32 x i32> %0
+}
+
+;On v79 and above,
+;v0.sf = vsub(v0.sf,v1.sf) is translated to
+;v2.qf32 = vsub(v0.sf,v1.sf); v0.sf = v2.qf32
+
+; CHECK-LABEL: vec_sub_sf:
+; CHECK-NOT: v{{.*}}.sf = vsub(v{{.*}}.sf,v{{.*}}.sf)
+; CHECK: v[[REG0:[0-9]+]].qf32 = vsub(v{{.*}}.sf,v{{.*}}.sf)
+; CHECK: v0.sf = v[[REG0]].qf32
+
+; V75-LABEL: vec_sub_sf:
+; V75: v{{.*}}.sf = vsub(v{{.*}}.sf,v{{.*}}.sf)
+
+define dso_local inreg <32 x i32> @vec_sub_sf(<32 x i32> noundef %a, <32 x i32> noundef %b) local_unnamed_addr {
+entry:
+ %0 = tail call <32 x i32> @llvm.hexagon.V6.vsub.sf.sf.128B(<32 x i32> %a, <32 x i32> %b)
+ ret <32 x i32> %0
+}
+
+;On v79 and above,
+;v1:0.sf = vsub(v0.hf,v1.hf) is translated to
+;r2 = #15360; v2.h = vsplat(r2);
+;v5:4.qf32 = vmpy(v1.hf,v2.hf); v31:30.qf32 = vmpy(v0.hf,v2.hf)
+;v4.qf32 = vsub(v30.qf32,v4.qf32); v3.qf32 = vsub(v31.qf32,v5.qf32)
+;v0.sf = v4.qf32; v1.sf = v3.qf32
+;Widen the hf operands to sf by doing a widening multiply with 1.0f
+;and perform the sub.
+
+; CHECK-LABEL: sfsubhfhf:
+; CHECK-NOT: v1:0.sf = vsub(v0.hf,v1.hf)
+; CHECK: r[[REG0:[0-9]+]] = #15360
+; CHECK: v[[REG1:[0-9]+]].h = vsplat(r[[REG0]])
+; CHECK-DAG: v[[REG2:[0-9]+]]:[[REG3:[0-9]+]].qf32 = vmpy(v{{.*}}.hf,v[[REG1]].hf)
+; CHECK-DAG: v[[REG4:[0-9]+]]:[[REG5:[0-9]+]].qf32 = vmpy(v{{.*}}.hf,v[[REG1]].hf)
+; CHECK-DAG: v[[REG6:[0-9]+]].qf32 = vsub(v[[REG5]].qf32,v[[REG3]].qf32)
+; CHECK-DAG: v[[REG7:[0-9]+]].qf32 = vsub(v[[REG4]].qf32,v[[REG2]].qf32)
+; CHECK-DAG: v{{.*}}.sf = v[[REG6]].qf32
+; CHECK-DAG: v{{.*}}.sf = v[[REG7]].qf32
+
+; V75-LABEL: sfsubhfhf:
+; CHECK-NOT: v1:0.sf = vsub(v0.hf,v1.hf)
+; V75: v1:0.sf = vsub(v0.hf,v1.hf)
+
+define dso_local inreg <64 x i32> @sfsubhfhf(<32 x i32> noundef %Vu, <32 x i32> noundef %Vv) local_unnamed_addr {
+entry:
+ %0 = tail call <64 x i32> @llvm.hexagon.V6.vsub.sf.hf.128B(<32 x i32> %Vu, <32 x i32> %Vv)
+ ret <64 x i32> %0
+}
+
+;On v79 and above,
+;v0.hf = vmpy(v0.hf,v1.hf) is translated to
+;v1:0.qf32 = vmpy(v0.hf,v1.hf); v0.hf = v1:0.qf32.
+
+; CHECK-LABEL: vec_mpy_hf:
+; CHECK-NOT: v{{.*}}.hf = vmpy(v{{.*}}.hf,v{{.*}}.hf)
+; CHECK: v1:0.qf32 = vmpy(v{{.*}}.hf,v{{.*}}.hf)
+; CHECK: v0.hf = v1:0.qf32
+
+; V75-LABEL: vec_mpy_hf:
+; V75: v{{.*}}.hf = vmpy(v{{.*}}.hf,v{{.*}}.hf)
+
+define dso_local inreg <32 x i32> @vec_mpy_hf(<32 x i32> noundef %a, <32 x i32> noundef %b) local_unnamed_addr {
+entry:
+ %0 = tail call <32 x i32> @llvm.hexagon.V6.vmpy.hf.hf.128B(<32 x i32> %a, <32 x i32> %b)
+ ret <32 x i32> %0
+}
+
+;On v79 and above,
+;v0.sf = vmpy(v0.sf,v1.sf) is translated to
+;v2.qf32 = vmpy(v0.sf,v1.sf); v0.sf = v2.qf32.
+
+; CHECK-LABEL: vec_mpy_sf:
+; CHECK-NOT: v{{.*}}.sf = vmpy(v{{.*}}.sf,v{{.*}}.sf)
+; CHECK: v[[REG0:[0-9]+]].qf32 = vmpy(v{{.*}}.sf,v{{.*}}.sf)
+; CHECK: v0.sf = v[[REG0]].qf32
+
+; V75-LABEL: vec_mpy_sf:
+; V75: v{{.*}}.sf = vmpy(v{{.*}}.sf,v{{.*}}.sf)
+
+define dso_local inreg <32 x i32> @vec_mpy_sf(<32 x i32> noundef %a, <32 x i32> noundef %b) local_unnamed_addr {
+entry:
+ %0 = tail call <32 x i32> @llvm.hexagon.V6.vmpy.sf.sf.128B(<32 x i32> %a, <32 x i32> %b)
+ ret <32 x i32> %0
+}
+
+;On v79 and above,
+;v1:0.sf = vmpy(v0.hf,v1.hf) is translated to
+;v3:2.qf32 = vmpy(v0.hf,v1.hf); v0.sf = v2.qf32 ; v1.sf = v3.qf32.
+
+; CHECK-LABEL: sfmpyhf:
+; CHECK-NOT: v1:0.sf = vmpy(v0.hf,v1.hf)
+; CHECK: v[[REG3:[0-9]+]]:[[REG2:[0-9]+]].qf32 = vmpy(v0.hf,v1.hf)
+; CHECK-DAG: v{{.*}}.sf = v[[REG2]].qf32
+; CHECK-DAG: v{{.*}}.sf = v[[REG3]].qf32
+
+; V75-LABEL: sfmpyhf:
+; V75: v1:0.sf = vmpy(v0.hf,v1.hf)
+
+define dso_local inreg <64 x i32> @sfmpyhf(<32 x i32> noundef %Vu, <32 x i32> noundef %Vv) local_unnamed_addr {
+entry:
+ %0 = tail call <64 x i32> @llvm.hexagon.V6.vmpy.sf.hf.128B(<32 x i32> %Vu, <32 x i32> %Vv)
+ ret <64 x i32> %0
+}
+
+;On v79 and above,
+;v0.hf += vmpy(v1.hf,v2.hf) is translated to
+;v7:6.qf32 = vmpy(v1.hf, v2.hf) // widening multiply
+;V5:4.qf32 = vmpy(v0.hf,1.0) // Convert accum to qf32
+;V4.qf32 = vadd(V6.qf32, V4.qf32) // accumulation
+;V5.qf32 = vadd(V7.qf32, V5.qf32) // accumulation
+;V4.hf = V5:4.qf32
+
+; CHECK-LABEL: mpyhf_acc:
+; CHECK-NOT: v{{.*}}.hf += vmpy(v{{.*}}.hf,v{{.*}}.hf)
+; CHECK: v[[REG3:[0-9]+]]:[[REG2:[0-9]+]].qf32 = vmpy(v{{.*}}.hf,v{{.*}}.hf)
+; CHECK: r{{.*}} = #15360
+; CHECK: v[[REG4:[0-9]+]].h = vsplat(r{{.*}})
+; CHECK: v[[REG7:[0-9]+]]:[[REG6:[0-9]+]].qf32 = vmpy(v{{.*}}.hf,v[[REG4]].hf)
+; CHECK: v[[REG4:[0-9]+]].qf32 = vadd(v[[REG6]].qf32,v[[REG2]].qf32)
+; CHECK: v[[REG5:[0-9]+]].qf32 = vadd(v[[REG7]].qf32,v[[REG3]].qf32)
+; CHECK: v{{.*}}.hf = v[[REG5]]:[[REG4]].qf32
+
+; V75-LABEL: mpyhf_acc:
+; V75: v{{.*}}.hf += vmpy(v{{.*}}.hf,v{{.*}}.hf)
+
+define dso_local inreg <32 x i32> @mpyhf_acc(<32 x i32> noundef %Vx, <32 x i32> noundef %Vu, <32 x i32> noundef %Vv) local_unnamed_addr {
+entry:
+ %0 = tail call <32 x i32> @llvm.hexagon.V6.vmpy.hf.hf.acc.128B(<32 x i32> %Vx, <32 x i32> %Vu, <32 x i32> %Vv)
+ ret <32 x i32> %0
+}
+
+;On v79 and above,
+;v1:0.sf += vmpy(v2.hf,v3.hf) is translated to
+;v3:2.qf32 = vmpy(v2.hf,v3.hf);
+;v0.qf32 = vadd(v2.qf32,v0.sf); v1.qf32 = vadd(v3.qf32,v1.sf);
+;v0.sf = v0.qf32; v1.sf = v1.qf32
+
+; CHECK-LABEL: sfmpyhf_acc:
+; CHECK-NOT: v1:0.sf += vmpy(v2.hf,v3.hf)
+; CHECK: v[[REG3:[0-9]+]]:[[REG2:[0-9]+]].qf32 = vmpy(v2.hf,v3.hf)
+; CHECK-DAG: v[[REG0:[0-9]+]].qf32 = vadd(v[[REG2]].qf32,v0.sf)
+; CHECK-DAG: v[[REG1:[0-9]+]].qf32 = vadd(v[[REG3]].qf32,v1.sf)
+; CHECK-DAG: v{{.*}}.sf = v[[REG0]].qf32
+; CHECK-DAG: v{{.*}}.sf = v[[REG1]].qf32
+
+; V75-LABEL: sfmpyhf_acc:
+; V75: v1:0.sf += vmpy(v2.hf,v3.hf)
+
+define dso_local inreg <64 x i32> @sfmpyhf_acc(<64 x i32> noundef %Vxx, <32 x i32> noundef %Vu, <32 x i32> noundef %Vv) local_unnamed_addr {
+entry:
+ %0 = tail call <64 x i32> @llvm.hexagon.V6.vmpy.sf.hf.acc.128B(<64 x i32> %Vxx, <32 x i32> %Vu, <32 x i32> %Vv)
+ ret <64 x i32> %0
+}
+
+;On v79 and above,
+;v0.hf = vfmin(v0.hf,v1.hf) is translated to v0.hf = vmin(v0.hf,v1.hf)
+
+; CHECK-LABEL: vec_min_hf:
+; CHECK-NOT: v{{.*}}.hf = vfmin(v{{.*}}.hf,v{{.*}}.hf)
+; CHECK: v{{.*}}.hf = vmin(v{{.*}}.hf,v{{.*}}.hf)
+
+; V75-LABEL: vec_min_hf:
+; V75: v{{.*}}.hf = vfmin(v{{.*}}.hf,v{{.*}}.hf)
+
+define dso_local inreg <32 x i32> @vec_min_hf(<32 x i32> noundef %a, <32 x i32> noundef %b) local_unnamed_addr {
+entry:
+ %0 = tail call <32 x i32> @llvm.hexagon.V6.vfmin.hf.128B(<32 x i32> %a, <32 x i32> %b)
+ ret <32 x i32> %0
+}
+
+;On v79 and above,
+;v0.sf = vfmin(v0.sf,v1.sf) is translated to v0.sf = vmin(v0.sf,v1.sf)
+
+; CHECK-LABEL: vec_min_sf:
+; CHECK-NOT: v{{.*}}.sf = vfmin(v{{.*}}.sf,v{{.*}}.sf)
+; CHECK: v{{.*}}.sf = vmin(v{{.*}}.sf,v{{.*}}.sf)
+
+; V75-LABEL: vec_min_sf:
+; V75: v{{.*}}.sf = vfmin(v{{.*}}.sf,v{{.*}}.sf)
+
+define dso_local inreg <32 x i32> @vec_min_sf(<32 x i32> noundef %a, <32 x i32> noundef %b) local_unnamed_addr {
+entry:
+ %0 = tail call <32 x i32> @llvm.hexagon.V6.vfmin.sf.128B(<32 x i32> %a, <32 x i32> %b)
+ ret <32 x i32> %0
+}
+
+;On v79 and above,
+;v0.hf = vfmax(v0.hf,v1.hf) is translated to v0.hf = vmax(v0.hf,v1.hf)
+
+; CHECK-LABEL: vec_max_hf:
+; CHECK-NOT: v{{.*}}.hf = vfmax(v{{.*}}.hf,v{{.*}}.hf)
+; CHECK: v{{.*}}.hf = vmax(v{{.*}}.hf,v{{.*}}.hf)
+
+; V75-LABEL: vec_max_hf:
+; V75: v{{.*}}.hf = vfmax(v{{.*}}.hf,v{{.*}}.hf)
+
+define dso_local inreg <32 x i32> @vec_max_hf(<32 x i32> noundef %a, <32 x i32> noundef %b) local_unnamed_addr {
+entry:
+ %0 = tail call <32 x i32> @llvm.hexagon.V6.vfmax.hf.128B(<32 x i32> %a, <32 x i32> %b)
+ ret <32 x i32> %0
+}
+
+;On v79 and above,
+;v0.sf = vfmax(v0.sf,v1.sf) is translated to v0.sf = vmax(v0.sf,v1.sf)
+
+; CHECK-LABEL: vec_max_sf:
+; CHECK-NOT: v{{.*}}.sf = vfmax(v{{.*}}.sf,v{{.*}}.sf)
+; CHECK: v{{.*}}.sf = vmax(v{{.*}}.sf,v{{.*}}.sf)
+
+; V75-LABEL: vec_max_sf:
+; V75: v{{.*}}.sf = vfmax(v{{.*}}.sf,v{{.*}}.sf)
+
+define dso_local inreg <32 x i32> @vec_max_sf(<32 x i32> noundef %a, <32 x i32> noundef %b) local_unnamed_addr {
+entry:
+ %0 = tail call <32 x i32> @llvm.hexagon.V6.vfmax.sf.128B(<32 x i32> %a, <32 x i32> %b)
+ ret <32 x i32> %0
+}
+
+;On v79 and above,
+;v0.hf = vfneg(v0.hf) is translated to
+;r2 = #32768 ; v1.h = vsplat(r2) ; v0 = vxor(v0,v1)
+;Flip the sign bit by splatting 0x8000 and does a vector xor.
+
+; CHECK-LABEL: vec_neg_hf:
+; CHECK-NOT: v{{.*}}.hf = vfneg(v{{.*}}.hf)
+; #0x8000
+; CHECK: r[[REG0:[0-9]+]] = ##32768
+; CHECK: v[[REG1:[0-9]+]].h = vsplat(r[[REG0]])
+; CHECK: v{{.*}} = vxor({{.*}}[[REG1]]
+
+; V75-LABEL: vec_neg_hf:
+; V75: v{{.*}}.hf = vfneg(v{{.*}}.hf)
+
+define dso_local inreg <32 x i32> @vec_neg_hf(<32 x i32> noundef %a) local_unnamed_addr {
+entry:
+ %0 = tail call <32 x i32> @llvm.hexagon.V6.vfneg.hf.128B(<32 x i32> %a)
+ ret <32 x i32> %0
+}
+
+;On v79 and above,
+;v0.sf = vfneg(v0.sf) is translated to
+;r2 = ##-2147483648 ; v1 = vsplat(r2) ; v0 = vxor(v0,v1)
+;Flip the sign bit by splatting 0x8000 0000 and does a vector xor.
+
+; CHECK-LABEL: vec_neg_sf:
+; CHECK-NOT: v{{.*}}.sf = vneg(v{{.*}}.sf,v{{.*}}.sf)
+; #0x8000 0000
+; CHECK: r[[REG0:[0-9]+]] = ##-2147483648
+; CHECK: v[[REG1:[0-9]+]] = vsplat(r[[REG0]])
+; CHECK: v{{.*}} = vxor({{.*}}[[REG1]]
+
+; V75-LABEL: vec_neg_sf:
+; V75: v{{.*}}.sf = vfneg(v{{.*}}.sf)
+
+define dso_local inreg <32 x i32> @vec_neg_sf(<32 x i32> noundef %a) local_unnamed_addr {
+entry:
+ %0 = tail call <32 x i32> @llvm.hexagon.V6.vfneg.sf.128B(<32 x i32> %a)
+ ret <32 x i32> %0
+}
+
+;On v79 and above,
+;v0.hf = vcvt(v0.h) is translated to v0.hf = v0.h.
+
+; CHECK-LABEL: convhfh:
+; CHECK-NOT: v{{.*}}.hf = vcvt(v0.h)
+; CHECK: v{{.*}}.hf = v0.h
+
+; V75-LABEL: convhfh:
+; V75: v{{.*}}.hf = vcvt(v0.h)
+
+define dso_local inreg <32 x i32> @convhfh(<32 x i32> noundef %Vu) local_unnamed_addr {
+entry:
+ %0 = tail call <32 x i32> @llvm.hexagon.V6.vcvt.hf.h.128B(<32 x i32> %Vu)
+ ret <32 x i32> %0
+}
+
+;On v79 and above,
+;v1:0.sf = vcvt(v0.hf) is translated to
+;r2 = #15360; v1.h = vsplat(r2); v3:2.qf32 = vmpy(v0.hf,v1.hf);
+;v0.sf = v2.qf32; v1.sf = v3.qf32
+;Do a widening multiply with 1.0f to convert the hf to sf.
+
+; CHECK-LABEL: convsfhf:
+; CHECK-NOT: v1:0.sf = vcvt(v0.hf)
+; CHECK: r[[REG0:[0-9]+]] = #15360
+; CHECK: v[[REG1:[0-9]+]].h = vsplat(r[[REG0]])
+; CHECK: v[[REG3:[0-9]+]]:[[REG2:[0-9]+]].qf32 = vmpy(v0.hf,v1.hf)
+; CHECK-DAG: v{{.*}}.sf = v[[REG2]].qf32
+; CHECK-DAG: v{{.*}}.sf = v[[REG3]].qf32
+
+; V75-LABEL: convsfhf:
+; V75: v1:0.sf = vcvt(v0.hf)
+
+define dso_local inreg <64 x i32> @convsfhf(<32 x i32> noundef %Vu) local_unnamed_addr {
+entry:
+ %0 = tail call <64 x i32> @llvm.hexagon.V6.vcvt.sf.hf.128B(<32 x i32> %Vu)
+ ret <64 x i32> %0
+}
+
+declare <32 x i32> @llvm.hexagon.V6.vabs.hf.128B(<32 x i32>)
+declare <32 x i32> @llvm.hexagon.V6.vabs.sf.128B(<32 x i32>)
+declare <32 x i32> @llvm.hexagon.V6.vassign.fp.128B(<32 x i32>)
+; Function Attrs: mustprogress nocallback nofree nosync nounwind willreturn memory(none)
+declare <32 x i32> @llvm.hexagon.V6.vadd.hf.hf.128B(<32 x i32>, <32 x i32>)
+; Function Attrs: mustprogress nocallback nofree nosync nounwind willreturn memory(none)
+declare <32 x i32> @llvm.hexagon.V6.vadd.sf.sf.128B(<32 x i32>, <32 x i32>)
+declare <64 x i32> @llvm.hexagon.V6.vadd.sf.hf.128B(<32 x i32>, <32 x i32>)
+; Function Attrs: mustprogress nocallback nofree nosync nounwind willreturn memory(none)
+declare <32 x i32> @llvm.hexagon.V6.vsub.hf.hf.128B(<32 x i32>, <32 x i32>)
+; Function Attrs: mustprogress nocallback nofree nosync nounwind willreturn memory(none)
+declare <32 x i32> @llvm.hexagon.V6.vsub.sf.sf.128B(<32 x i32>, <32 x i32>)
+declare <64 x i32> @llvm.hexagon.V6.vsub.sf.hf.128B(<32 x i32>, <32 x i32>)
+declare <32 x i32> @llvm.hexagon.V6.vmpy.hf.hf.128B(<32 x i32>, <32 x i32>)
+declare <32 x i32> @llvm.hexagon.V6.vmpy.sf.sf.128B(<32 x i32>, <32 x i32>)
+declare <64 x i32> @llvm.hexagon.V6.vmpy.sf.hf.128B(<32 x i32>, <32 x i32>)
+declare <32 x i32> @llvm.hexagon.V6.vmpy.hf.hf.acc.128B(<32 x i32>, <32 x i32>, <32 x i32>)
+declare <64 x i32> @llvm.hexagon.V6.vmpy.sf.hf.acc.128B(<64 x i32>, <32 x i32>, <32 x i32>)
+declare <32 x i32> @llvm.hexagon.V6.vfmin.hf.128B(<32 x i32>, <32 x i32>)
+declare <32 x i32> @llvm.hexagon.V6.vfmin.sf.128B(<32 x i32>, <32 x i32>)
+declare <32 x i32> @llvm.hexagon.V6.vfmax.hf.128B(<32 x i32>, <32 x i32>)
+declare <32 x i32> @llvm.hexagon.V6.vfmax.sf.128B(<32 x i32>, <32 x i32>)
+declare <32 x i32> @llvm.hexagon.V6.vfneg.hf.128B(<32 x i32>)
+declare <32 x i32> @llvm.hexagon.V6.vfneg.sf.128B(<32 x i32>)
+declare <32 x i32> @llvm.hexagon.V6.vcvt.hf.h.128B(<32 x i32>)
+declare <64 x i32> @llvm.hexagon.V6.vcvt.sf.hf.128B(<32 x i32>)
More information about the llvm-commits
mailing list