[llvm-branch-commits] [llvm] [LoongArch] Add support for vector FP_ROUND from vxf64 to vxf32 (PR #164059)

Zhaoxin Yang via llvm-branch-commits llvm-branch-commits at lists.llvm.org
Tue Apr 14 02:10:16 PDT 2026


https://github.com/ylzsx updated https://github.com/llvm/llvm-project/pull/164059

>From 48ac3dc1e6f583f4afe477917470cb7c7a28993f Mon Sep 17 00:00:00 2001
From: yangzhaoxin <yangzhaoxin at loongson.cn>
Date: Fri, 17 Oct 2025 17:59:17 +0800
Subject: [PATCH 1/3] [LoongArch] Add support for vector FP_ROUND from vxf64 to
 vxf32

In LoongArch, [x]vfcvt.s.d intstructions require two vector registers
for v4f64->v4f32, v8f64->v8f32 conversions.

This patch handles these cases:
- For FP_ROUND v2f64->v2f32(illegal), add a customized v2f32 widening
  to convert it into a target-specific LoongArchISD::VFCVT.
- For FP_ROUND v4f64->v4f32, on LSX platforms, v4f64 is illegal and will
  be split into two v2f64->v2f32, resulting in two LoongArchISD::VFCVT.
  Finally, they are combined into a single node during combining
  LoongArchISD::VPACKEV. On LASX platforms, v4f64->v4f32 can directly
  lower to vfcvt.s.d in lowerFP_ROUND.
- For FP_ROUND v8f64->v8f32, on LASX platforms, v8f64 is illegal and
  will be split into two v4f64->v4f32 and then combine using
  ISD::CONCAT_VECTORS, so xvfcvt.s.d is generated during its
  combination.
---
 .../LoongArch/LoongArchISelLowering.cpp       | 131 ++++++++++++++++++
 .../Target/LoongArch/LoongArchISelLowering.h  |   2 +-
 .../LoongArch/LoongArchLASXInstrInfo.td       |   2 +
 .../Target/LoongArch/LoongArchLSXInstrInfo.td |   7 +
 .../LoongArch/lasx/ir-instruction/fptrunc.ll  |  48 ++-----
 .../LoongArch/lsx/ir-instruction/fptrunc.ll   |  27 +---
 6 files changed, 156 insertions(+), 61 deletions(-)

diff --git a/llvm/lib/Target/LoongArch/LoongArchISelLowering.cpp b/llvm/lib/Target/LoongArch/LoongArchISelLowering.cpp
index 2e4eb1da55ac7..5d1ef9d32f795 100644
--- a/llvm/lib/Target/LoongArch/LoongArchISelLowering.cpp
+++ b/llvm/lib/Target/LoongArch/LoongArchISelLowering.cpp
@@ -395,6 +395,7 @@ LoongArchTargetLowering::LoongArchTargetLowering(const TargetMachine &TM,
       setOperationAction(ISD::VECREDUCE_UMAX, VT, Custom);
       setOperationAction(ISD::VECREDUCE_UMIN, VT, Custom);
     }
+    setOperationAction(ISD::FP_ROUND, MVT::v2f32, Custom);
   }
 
   // Set operations for 'LASX' feature.
@@ -465,6 +466,7 @@ LoongArchTargetLowering::LoongArchTargetLowering(const TargetMachine &TM,
       setOperationAction(ISD::FMINNUM, VT, Legal);
       setOperationAction(ISD::FMAXNUM, VT, Legal);
     }
+    setOperationAction(ISD::FP_ROUND, MVT::v4f32, Custom);
   }
 
   // Set DAG combine for LA32 and LA64.
@@ -489,6 +491,7 @@ LoongArchTargetLowering::LoongArchTargetLowering(const TargetMachine &TM,
     setTargetDAGCombine(ISD::ANY_EXTEND);
     setTargetDAGCombine(ISD::ZERO_EXTEND);
     setTargetDAGCombine(ISD::SIGN_EXTEND);
+    setTargetDAGCombine(ISD::CONCAT_VECTORS);
   }
 
   // Compute derived properties from the register classes.
@@ -622,6 +625,8 @@ SDValue LoongArchTargetLowering::LowerOperation(SDValue Op,
     return lowerConstantFP(Op, DAG);
   case ISD::SETCC:
     return lowerSETCC(Op, DAG);
+  case ISD::FP_ROUND:
+    return lowerFP_ROUND(Op, DAG);
   }
   return SDValue();
 }
@@ -675,6 +680,97 @@ static SDValue isNOT(SDValue V, SelectionDAG &DAG) {
   // TODO: Add more matching patterns. Such as,
   // not(concat_vectors(not(X), not(Y))) -> concat_vectors(X, Y).
   // not(slt(C, X)) -> slt(X - 1, C)
+  return SDValue();
+}
+
+// Combine two ISD::FP_ROUND / LoongArchISD::VFCVT nodes with same type to
+// LoongArchISD::VFCVT. For example:
+//   x1 = fp_round x, 0
+//   y1 = fp_round y, 0
+//   z = concat_vectors x1, y1
+// Or
+//   x1 = LoongArch::VFCVT undef, x
+//   y1 = LoongArch::VFCVT undef, y
+//   z = LoongArchISD::VPACKEV y1, x1
+// can be combined to:
+//   z = LoongArch::VFCVT y, x
+static SDValue combineFP_ROUND(SDValue N, const SDLoc &DL, SelectionDAG &DAG,
+                               const LoongArchSubtarget &Subtarget) {
+  assert(((N->getOpcode() == ISD::CONCAT_VECTORS && N->getNumOperands() == 2) ||
+          (N->getOpcode() == LoongArchISD::VPACKEV)) &&
+         "Invalid Node");
+
+  SDValue Op0 = peekThroughBitcasts(N->getOperand(0));
+  SDValue Op1 = peekThroughBitcasts(N->getOperand(1));
+  unsigned Opcode0 = Op0.getOpcode();
+  unsigned Opcode1 = Op1.getOpcode();
+  if (Opcode0 != Opcode1)
+    return SDValue();
+
+  if (Opcode0 != ISD::FP_ROUND && Opcode0 != LoongArchISD::VFCVT)
+    return SDValue();
+
+  // Check if two nodes have only one use.
+  if (!Op0.hasOneUse() || !Op1.hasOneUse())
+    return SDValue();
+
+  EVT VT = N.getValueType();
+  EVT SVT0 = Op0.getValueType();
+  EVT SVT1 = Op1.getValueType();
+  // Check if two nodes have the same result type.
+  if (SVT0 != SVT1)
+    return SDValue();
+
+  // Check if two nodes have the same operand type.
+  EVT SSVT0 = Op0.getOperand(0).getValueType();
+  EVT SSVT1 = Op1.getOperand(0).getValueType();
+  if (SSVT0 != SSVT1)
+    return SDValue();
+
+  if (N->getOpcode() == ISD::CONCAT_VECTORS && Opcode0 == ISD::FP_ROUND) {
+    if (Subtarget.hasExtLASX() && VT.is256BitVector() && SVT0 == MVT::v4f32 &&
+        SSVT0 == MVT::v4f64) {
+      // A vector_shuffle is required in the final step, as xvfcvt instruction
+      // operates on each 128-bit segament as a lane.
+      SDValue Res = DAG.getNode(LoongArchISD::VFCVT, DL, MVT::v8f32,
+                                Op1.getOperand(0), Op0.getOperand(0));
+      SDValue Undef = DAG.getUNDEF(VT);
+      SmallVector<int, 8> Mask = {0, 1, 4, 5, 2, 3, 6, 7};
+      Res = DAG.getVectorShuffle(VT, DL, Res, Undef, Mask);
+      return DAG.getBitcast(VT, Res);
+    }
+  }
+
+  if (N->getOpcode() == LoongArchISD::VPACKEV &&
+      Opcode0 == LoongArchISD::VFCVT) {
+    // For VPACKEV, check if the first operation of LoongArchISD::VFCVT is
+    // undef.
+    if (!Op0.getOperand(0).isUndef() || !Op1.getOperand(0).isUndef())
+      return SDValue();
+
+    if (Subtarget.hasExtLSX() && (VT == MVT::v2i64 || VT == MVT::v2f64) &&
+        SVT0 == MVT::v4f32 && SSVT0 == MVT::v2f64) {
+      SDValue Res = DAG.getNode(LoongArchISD::VFCVT, DL, MVT::v4f32,
+                                Op0.getOperand(1), Op1.getOperand(1));
+      return DAG.getBitcast(VT, Res);
+    }
+  }
+
+  return SDValue();
+}
+
+SDValue LoongArchTargetLowering::lowerFP_ROUND(SDValue Op,
+                                               SelectionDAG &DAG) const {
+  SDLoc DL(Op);
+  SDValue In = Op.getOperand(0);
+  MVT VT = Op.getSimpleValueType();
+  MVT SVT = In.getSimpleValueType();
+
+  if (VT == MVT::v4f32 && SVT == MVT::v4f64) {
+    SDValue Lo, Hi;
+    std::tie(Lo, Hi) = DAG.SplitVector(In, DL);
+    return DAG.getNode(LoongArchISD::VFCVT, DL, VT, Hi, Lo);
+  }
 
   return SDValue();
 }
@@ -4942,6 +5038,21 @@ void LoongArchTargetLowering::ReplaceNodeResults(
     Results.push_back(DAG.getNode(ISD::TRUNCATE, DL, MVT::i32, Tmp1));
     break;
   }
+  case ISD::FP_ROUND: {
+    assert(VT == MVT::v2f32 && Subtarget.hasExtLSX() &&
+           "Unexpected custom legalisation");
+    // On LSX platforms, rounding from v2f64 to v4f32 (after legalization from
+    // v2f32) is scalarized. Add a customized v2f32 widening to convert it into
+    // a target-specific LoongArchISD::VFCVT to optimize it.
+    if (VT == MVT::v2f32) {
+      SDValue Src = N->getOperand(0);
+      SDValue Undef = DAG.getUNDEF(Src.getValueType());
+      SDValue Dst =
+          DAG.getNode(LoongArchISD::VFCVT, DL, MVT::v4f32, Undef, Src);
+      Results.push_back(Dst);
+    }
+    break;
+  }
   case ISD::BSWAP: {
     SDValue Src = N->getOperand(0);
     assert((VT == MVT::i16 || VT == MVT::i32) &&
@@ -7118,6 +7229,20 @@ static SDValue performEXTENDCombine(SDNode *N, SelectionDAG &DAG,
   return SDValue();
 }
 
+static SDValue
+performCONCAT_VECTORSCombine(SDNode *N, SelectionDAG &DAG,
+                             TargetLowering::DAGCombinerInfo &DCI,
+                             const LoongArchSubtarget &Subtarget) {
+  SDLoc DL(N);
+  EVT VT = N->getValueType(0);
+
+  if (VT.isVector() && N->getNumOperands() == 2)
+    if (SDValue R = combineFP_ROUND(SDValue(N, 0), DL, DAG, Subtarget))
+      return R;
+
+  return SDValue();
+}
+
 SDValue LoongArchTargetLowering::PerformDAGCombine(SDNode *N,
                                                    DAGCombinerInfo &DCI) const {
   SelectionDAG &DAG = DCI.DAG;
@@ -7159,6 +7284,12 @@ SDValue LoongArchTargetLowering::PerformDAGCombine(SDNode *N,
     return performSPLIT_PAIR_F64Combine(N, DAG, DCI, Subtarget);
   case LoongArchISD::VANDN:
     return performVANDNCombine(N, DAG, DCI, Subtarget);
+  case ISD::CONCAT_VECTORS:
+    return performCONCAT_VECTORSCombine(N, DAG, DCI, Subtarget);
+  case LoongArchISD::VPACKEV:
+    if (SDValue Result =
+            combineFP_ROUND(SDValue(N, 0), SDLoc(N), DAG, Subtarget))
+      return Result;
   }
   return SDValue();
 }
diff --git a/llvm/lib/Target/LoongArch/LoongArchISelLowering.h b/llvm/lib/Target/LoongArch/LoongArchISelLowering.h
index 9708f9f2fe1b2..6de33e2af9e9c 100644
--- a/llvm/lib/Target/LoongArch/LoongArchISelLowering.h
+++ b/llvm/lib/Target/LoongArch/LoongArchISelLowering.h
@@ -21,7 +21,6 @@
 
 namespace llvm {
 class LoongArchSubtarget;
-
 class LoongArchTargetLowering : public TargetLowering {
   const LoongArchSubtarget &Subtarget;
 
@@ -247,6 +246,7 @@ class LoongArchTargetLowering : public TargetLowering {
   SDValue lowerConstantFP(SDValue Op, SelectionDAG &DAG) const;
   SDValue lowerSETCC(SDValue Op, SelectionDAG &DAG) const;
   SDValue lowerRotate(SDValue Op, SelectionDAG &DAG) const;
+  SDValue lowerFP_ROUND(SDValue Op, SelectionDAG &DAG) const;
 
   bool isFPImmLegal(const APFloat &Imm, EVT VT,
                     bool ForCodeSize) const override;
diff --git a/llvm/lib/Target/LoongArch/LoongArchLASXInstrInfo.td b/llvm/lib/Target/LoongArch/LoongArchLASXInstrInfo.td
index 1b18d16a4cabb..32de5f20ec17f 100644
--- a/llvm/lib/Target/LoongArch/LoongArchLASXInstrInfo.td
+++ b/llvm/lib/Target/LoongArch/LoongArchLASXInstrInfo.td
@@ -2472,6 +2472,8 @@ defm : PatXrF<fceil, "XVFRINTRP">;
 defm : PatXrF<ffloor, "XVFRINTRM">;
 defm : PatXrF<ftrunc, "XVFRINTRZ">;
 defm : PatXrF<froundeven, "XVFRINTRNE">;
+def : Pat<(v8f32 (loongarch_vfcvt_s_d (v4f64 LASX256:$xj), (v4f64 LASX256:$xk))),
+          (XVFCVT_S_D LASX256:$xj, LASX256:$xk)>;
 
 // load
 def : Pat<(int_loongarch_lasx_xvld GPR:$rj, timm:$imm),
diff --git a/llvm/lib/Target/LoongArch/LoongArchLSXInstrInfo.td b/llvm/lib/Target/LoongArch/LoongArchLSXInstrInfo.td
index dbc0d68f2254f..c021a7321f0c5 100644
--- a/llvm/lib/Target/LoongArch/LoongArchLSXInstrInfo.td
+++ b/llvm/lib/Target/LoongArch/LoongArchLSXInstrInfo.td
@@ -32,6 +32,8 @@ def SDT_LoongArchVFRECIPE : SDTypeProfile<1, 1, [SDTCisFP<0>, SDTCisVec<0>, SDTC
 def SDT_LoongArchVFRSQRTE : SDTypeProfile<1, 1, [SDTCisFP<0>, SDTCisVec<0>, SDTCisSameAs<0, 1>]>;
 def SDT_LoongArchVLDREPL : SDTypeProfile<1, 1, [SDTCisVec<0>, SDTCisPtrTy<1>]>;
 def SDT_LoongArchVMSKCOND : SDTypeProfile<1, 1, [SDTCisInt<0>, SDTCisVec<1>]>;
+def SDT_LoongArchVFCVT_S_D : SDTypeProfile<1, 2, [SDTCisVec<0>, SDTCisFP<0>,
+                                                  SDTCisVec<1>, SDTCisFP<1>, SDTCisSameAs<1, 2>]>;
 
 // Target nodes.
 
@@ -94,6 +96,8 @@ def loongarch_vmskgez: SDNode<"LoongArchISD::VMSKGEZ", SDT_LoongArchVMSKCOND>;
 def loongarch_vmskeqz: SDNode<"LoongArchISD::VMSKEQZ", SDT_LoongArchVMSKCOND>;
 def loongarch_vmsknez: SDNode<"LoongArchISD::VMSKNEZ", SDT_LoongArchVMSKCOND>;
 
+def loongarch_vfcvt_s_d: SDNode<"LoongArchISD::VFCVT", SDT_LoongArchVFCVT_S_D>;
+
 def immZExt1 : ImmLeaf<GRLenVT, [{return isUInt<1>(Imm);}]>;
 def immZExt2 : ImmLeaf<GRLenVT, [{return isUInt<2>(Imm);}]>;
 def immZExt3 : ImmLeaf<GRLenVT, [{return isUInt<3>(Imm);}]>;
@@ -2577,6 +2581,9 @@ defm : PatVrF<ffloor, "VFRINTRM">;
 defm : PatVrF<ftrunc, "VFRINTRZ">;
 defm : PatVrF<froundeven, "VFRINTRNE">;
 
+def : Pat<(v4f32 (loongarch_vfcvt_s_d (v2f64 LSX128:$vj), (v2f64 LSX128:$vk))),
+          (VFCVT_S_D LSX128:$vj, LSX128:$vk)>;
+
 // load
 def : Pat<(int_loongarch_lsx_vld GPR:$rj, timm:$imm),
           (VLD GPR:$rj, (to_valid_timm timm:$imm))>;
diff --git a/llvm/test/CodeGen/LoongArch/lasx/ir-instruction/fptrunc.ll b/llvm/test/CodeGen/LoongArch/lasx/ir-instruction/fptrunc.ll
index 05c96f554be84..690c7433f28c5 100644
--- a/llvm/test/CodeGen/LoongArch/lasx/ir-instruction/fptrunc.ll
+++ b/llvm/test/CodeGen/LoongArch/lasx/ir-instruction/fptrunc.ll
@@ -7,18 +7,9 @@ define void @fptrunc_v4f64_to_v4f32(ptr %res, ptr %a0) nounwind {
 ; CHECK-LABEL: fptrunc_v4f64_to_v4f32:
 ; CHECK:       # %bb.0: # %entry
 ; CHECK-NEXT:    xvld $xr0, $a1, 0
-; CHECK-NEXT:    xvpickve.d $xr1, $xr0, 1
-; CHECK-NEXT:    fcvt.s.d $fa1, $fa1
-; CHECK-NEXT:    xvpickve.d $xr2, $xr0, 0
-; CHECK-NEXT:    fcvt.s.d $fa2, $fa2
-; CHECK-NEXT:    vextrins.w $vr2, $vr1, 16
-; CHECK-NEXT:    xvpickve.d $xr1, $xr0, 2
-; CHECK-NEXT:    fcvt.s.d $fa1, $fa1
-; CHECK-NEXT:    vextrins.w $vr2, $vr1, 32
-; CHECK-NEXT:    xvpickve.d $xr0, $xr0, 3
-; CHECK-NEXT:    fcvt.s.d $fa0, $fa0
-; CHECK-NEXT:    vextrins.w $vr2, $vr0, 48
-; CHECK-NEXT:    vst $vr2, $a0, 0
+; CHECK-NEXT:    xvpermi.q $xr1, $xr0, 1
+; CHECK-NEXT:    vfcvt.s.d $vr0, $vr1, $vr0
+; CHECK-NEXT:    vst $vr0, $a0, 0
 ; CHECK-NEXT:    ret
 entry:
   %v0 = load <4 x double>, ptr %a0
@@ -30,32 +21,13 @@ entry:
 define void @fptrunc_v8f64_to_v8f32(ptr %res, ptr %a0) nounwind {
 ; CHECK-LABEL: fptrunc_v8f64_to_v8f32:
 ; CHECK:       # %bb.0: # %entry
-; CHECK-NEXT:    xvld $xr0, $a1, 32
-; CHECK-NEXT:    xvld $xr1, $a1, 0
-; CHECK-NEXT:    xvpickve.d $xr2, $xr0, 1
-; CHECK-NEXT:    fcvt.s.d $fa2, $fa2
-; CHECK-NEXT:    xvpickve.d $xr3, $xr0, 0
-; CHECK-NEXT:    fcvt.s.d $fa3, $fa3
-; CHECK-NEXT:    vextrins.w $vr3, $vr2, 16
-; CHECK-NEXT:    xvpickve.d $xr2, $xr0, 2
-; CHECK-NEXT:    fcvt.s.d $fa2, $fa2
-; CHECK-NEXT:    vextrins.w $vr3, $vr2, 32
-; CHECK-NEXT:    xvpickve.d $xr0, $xr0, 3
-; CHECK-NEXT:    fcvt.s.d $fa0, $fa0
-; CHECK-NEXT:    vextrins.w $vr3, $vr0, 48
-; CHECK-NEXT:    xvpickve.d $xr0, $xr1, 1
-; CHECK-NEXT:    fcvt.s.d $fa0, $fa0
-; CHECK-NEXT:    xvpickve.d $xr2, $xr1, 0
-; CHECK-NEXT:    fcvt.s.d $fa2, $fa2
-; CHECK-NEXT:    vextrins.w $vr2, $vr0, 16
-; CHECK-NEXT:    xvpickve.d $xr0, $xr1, 2
-; CHECK-NEXT:    fcvt.s.d $fa0, $fa0
-; CHECK-NEXT:    vextrins.w $vr2, $vr0, 32
-; CHECK-NEXT:    xvpickve.d $xr0, $xr1, 3
-; CHECK-NEXT:    fcvt.s.d $fa0, $fa0
-; CHECK-NEXT:    vextrins.w $vr2, $vr0, 48
-; CHECK-NEXT:    xvpermi.q $xr2, $xr3, 2
-; CHECK-NEXT:    xvst $xr2, $a0, 0
+; CHECK-NEXT:    xvld $xr0, $a1, 0
+; CHECK-NEXT:    xvld $xr1, $a1, 32
+; CHECK-NEXT:    pcalau12i $a1, %pc_hi20(.LCPI1_0)
+; CHECK-NEXT:    xvld $xr2, $a1, %pc_lo12(.LCPI1_0)
+; CHECK-NEXT:    xvfcvt.s.d $xr0, $xr1, $xr0
+; CHECK-NEXT:    xvperm.w $xr0, $xr0, $xr2
+; CHECK-NEXT:    xvst $xr0, $a0, 0
 ; CHECK-NEXT:    ret
 entry:
   %v0 = load <8 x double>, ptr %a0
diff --git a/llvm/test/CodeGen/LoongArch/lsx/ir-instruction/fptrunc.ll b/llvm/test/CodeGen/LoongArch/lsx/ir-instruction/fptrunc.ll
index dfdc5b8438355..f9134edef3449 100644
--- a/llvm/test/CodeGen/LoongArch/lsx/ir-instruction/fptrunc.ll
+++ b/llvm/test/CodeGen/LoongArch/lsx/ir-instruction/fptrunc.ll
@@ -21,22 +21,15 @@ define void @fptrunc_v2f64_to_v2f32(ptr %res, ptr %a0) nounwind {
 ; LA32-LABEL: fptrunc_v2f64_to_v2f32:
 ; LA32:       # %bb.0: # %entry
 ; LA32-NEXT:    vld $vr0, $a1, 0
-; LA32-NEXT:    vreplvei.d $vr1, $vr0, 0
-; LA32-NEXT:    fcvt.s.d $fa1, $fa1
-; LA32-NEXT:    vreplvei.d $vr0, $vr0, 1
-; LA32-NEXT:    fcvt.s.d $fa0, $fa0
-; LA32-NEXT:    fst.s $fa0, $a0, 4
-; LA32-NEXT:    fst.s $fa1, $a0, 0
+; LA32-NEXT:    vfcvt.s.d $vr0, $vr0, $vr0
+; LA32-NEXT:    vstelm.w $vr0, $a0, 4, 1
+; LA32-NEXT:    vstelm.w $vr0, $a0, 0, 0
 ; LA32-NEXT:    ret
 ;
 ; LA64-LABEL: fptrunc_v2f64_to_v2f32:
 ; LA64:       # %bb.0: # %entry
 ; LA64-NEXT:    vld $vr0, $a1, 0
-; LA64-NEXT:    vreplvei.d $vr1, $vr0, 1
-; LA64-NEXT:    fcvt.s.d $fa1, $fa1
-; LA64-NEXT:    vreplvei.d $vr0, $vr0, 0
-; LA64-NEXT:    fcvt.s.d $fa0, $fa0
-; LA64-NEXT:    vextrins.w $vr0, $vr1, 16
+; LA64-NEXT:    vfcvt.s.d $vr0, $vr0, $vr0
 ; LA64-NEXT:    vstelm.d $vr0, $a0, 0, 0
 ; LA64-NEXT:    ret
 entry:
@@ -51,17 +44,7 @@ define void @fptrunc_v4f64_to_v4f32(ptr %res, ptr %a0) nounwind {
 ; CHECK:       # %bb.0: # %entry
 ; CHECK-NEXT:    vld $vr0, $a1, 0
 ; CHECK-NEXT:    vld $vr1, $a1, 16
-; CHECK-NEXT:    vreplvei.d $vr2, $vr0, 1
-; CHECK-NEXT:    fcvt.s.d $fa2, $fa2
-; CHECK-NEXT:    vreplvei.d $vr0, $vr0, 0
-; CHECK-NEXT:    fcvt.s.d $fa0, $fa0
-; CHECK-NEXT:    vextrins.w $vr0, $vr2, 16
-; CHECK-NEXT:    vreplvei.d $vr2, $vr1, 0
-; CHECK-NEXT:    fcvt.s.d $fa2, $fa2
-; CHECK-NEXT:    vextrins.w $vr0, $vr2, 32
-; CHECK-NEXT:    vreplvei.d $vr1, $vr1, 1
-; CHECK-NEXT:    fcvt.s.d $fa1, $fa1
-; CHECK-NEXT:    vextrins.w $vr0, $vr1, 48
+; CHECK-NEXT:    vfcvt.s.d $vr0, $vr1, $vr0
 ; CHECK-NEXT:    vst $vr0, $a0, 0
 ; CHECK-NEXT:    ret
 entry:

>From 763ad5353141cff5fa29206a34aa8e631a54fcc8 Mon Sep 17 00:00:00 2001
From: yangzhaoxin <yangzhaoxin at loongson.cn>
Date: Mon, 13 Apr 2026 11:30:44 +0800
Subject: [PATCH 2/3] fixes according reviews

---
 .../LoongArch/LoongArchISelLowering.cpp       | 11 +++++--
 .../LoongArch/lasx/ir-instruction/fptrunc.ll  | 31 +++----------------
 .../LoongArch/lsx/ir-instruction/fptrunc.ll   | 14 ++-------
 3 files changed, 15 insertions(+), 41 deletions(-)

diff --git a/llvm/lib/Target/LoongArch/LoongArchISelLowering.cpp b/llvm/lib/Target/LoongArch/LoongArchISelLowering.cpp
index 5d1ef9d32f795..079d74cc25a2b 100644
--- a/llvm/lib/Target/LoongArch/LoongArchISelLowering.cpp
+++ b/llvm/lib/Target/LoongArch/LoongArchISelLowering.cpp
@@ -734,9 +734,16 @@ static SDValue combineFP_ROUND(SDValue N, const SDLoc &DL, SelectionDAG &DAG,
       // operates on each 128-bit segament as a lane.
       SDValue Res = DAG.getNode(LoongArchISD::VFCVT, DL, MVT::v8f32,
                                 Op1.getOperand(0), Op0.getOperand(0));
-      SDValue Undef = DAG.getUNDEF(VT);
+      SDValue Undef = DAG.getUNDEF(Res.getValueType());
+      // After VFCVT, the high part of Res comes from the high parts of Op0 and
+      // Op1, and the low part comes from the low parts of Op0 and Op1. However,
+      // the desired order requires Op0 to fully occupy the lower half and Op1
+      // the upper half of Res. The Mask reorders the elements of Res to achieve
+      // this:
+      // - The first four elements (0, 1, 4, 5) come from Op0.
+      // - The next four elements (2, 3, 6, 7) come from Op1.
       SmallVector<int, 8> Mask = {0, 1, 4, 5, 2, 3, 6, 7};
-      Res = DAG.getVectorShuffle(VT, DL, Res, Undef, Mask);
+      Res = DAG.getVectorShuffle(Res.getValueType(), DL, Res, Undef, Mask);
       return DAG.getBitcast(VT, Res);
     }
   }
diff --git a/llvm/test/CodeGen/LoongArch/lasx/ir-instruction/fptrunc.ll b/llvm/test/CodeGen/LoongArch/lasx/ir-instruction/fptrunc.ll
index 690c7433f28c5..e15a87a3e27f5 100644
--- a/llvm/test/CodeGen/LoongArch/lasx/ir-instruction/fptrunc.ll
+++ b/llvm/test/CodeGen/LoongArch/lasx/ir-instruction/fptrunc.ll
@@ -23,10 +23,8 @@ define void @fptrunc_v8f64_to_v8f32(ptr %res, ptr %a0) nounwind {
 ; CHECK:       # %bb.0: # %entry
 ; CHECK-NEXT:    xvld $xr0, $a1, 0
 ; CHECK-NEXT:    xvld $xr1, $a1, 32
-; CHECK-NEXT:    pcalau12i $a1, %pc_hi20(.LCPI1_0)
-; CHECK-NEXT:    xvld $xr2, $a1, %pc_lo12(.LCPI1_0)
 ; CHECK-NEXT:    xvfcvt.s.d $xr0, $xr1, $xr0
-; CHECK-NEXT:    xvperm.w $xr0, $xr0, $xr2
+; CHECK-NEXT:    xvpermi.d $xr0, $xr0, 216
 ; CHECK-NEXT:    xvst $xr0, $a0, 0
 ; CHECK-NEXT:    ret
 entry:
@@ -41,30 +39,9 @@ define void @fptrunc_concat_bitcast(ptr %res, ptr %a0) nounwind {
 ; CHECK:       # %bb.0: # %entry
 ; CHECK-NEXT:    xvld $xr0, $a1, 0
 ; CHECK-NEXT:    xvld $xr1, $a1, 32
-; CHECK-NEXT:    xvpickve.d $xr2, $xr0, 1
-; CHECK-NEXT:    fcvt.s.d $fa2, $fa2
-; CHECK-NEXT:    xvpickve.d $xr3, $xr0, 0
-; CHECK-NEXT:    fcvt.s.d $fa3, $fa3
-; CHECK-NEXT:    vextrins.w $vr3, $vr2, 16
-; CHECK-NEXT:    xvpickve.d $xr2, $xr0, 2
-; CHECK-NEXT:    fcvt.s.d $fa2, $fa2
-; CHECK-NEXT:    vextrins.w $vr3, $vr2, 32
-; CHECK-NEXT:    xvpickve.d $xr0, $xr0, 3
-; CHECK-NEXT:    fcvt.s.d $fa0, $fa0
-; CHECK-NEXT:    vextrins.w $vr3, $vr0, 48
-; CHECK-NEXT:    xvpickve.d $xr0, $xr1, 1
-; CHECK-NEXT:    fcvt.s.d $fa0, $fa0
-; CHECK-NEXT:    xvpickve.d $xr2, $xr1, 0
-; CHECK-NEXT:    fcvt.s.d $fa2, $fa2
-; CHECK-NEXT:    vextrins.w $vr2, $vr0, 16
-; CHECK-NEXT:    xvpickve.d $xr0, $xr1, 2
-; CHECK-NEXT:    fcvt.s.d $fa0, $fa0
-; CHECK-NEXT:    vextrins.w $vr2, $vr0, 32
-; CHECK-NEXT:    xvpickve.d $xr0, $xr1, 3
-; CHECK-NEXT:    fcvt.s.d $fa0, $fa0
-; CHECK-NEXT:    vextrins.w $vr2, $vr0, 48
-; CHECK-NEXT:    xvpermi.q $xr3, $xr2, 2
-; CHECK-NEXT:    xvst $xr3, $a0, 0
+; CHECK-NEXT:    xvfcvt.s.d $xr0, $xr1, $xr0
+; CHECK-NEXT:    xvpermi.d $xr0, $xr0, 216
+; CHECK-NEXT:    xvst $xr0, $a0, 0
 ; CHECK-NEXT:    ret
 entry:
   %x = load <8 x double>, ptr %a0
diff --git a/llvm/test/CodeGen/LoongArch/lsx/ir-instruction/fptrunc.ll b/llvm/test/CodeGen/LoongArch/lsx/ir-instruction/fptrunc.ll
index f9134edef3449..a023159c703b7 100644
--- a/llvm/test/CodeGen/LoongArch/lsx/ir-instruction/fptrunc.ll
+++ b/llvm/test/CodeGen/LoongArch/lsx/ir-instruction/fptrunc.ll
@@ -59,18 +59,8 @@ define void @fptrunc_concat_bitcast(ptr %res, ptr %a0) nounwind {
 ; CHECK:       # %bb.0: # %entry
 ; CHECK-NEXT:    vld $vr0, $a1, 0
 ; CHECK-NEXT:    vld $vr1, $a1, 16
-; CHECK-NEXT:    vreplvei.d $vr2, $vr0, 0
-; CHECK-NEXT:    fcvt.s.d $fa2, $fa2
-; CHECK-NEXT:    vreplvei.d $vr0, $vr0, 1
-; CHECK-NEXT:    fcvt.s.d $fa0, $fa0
-; CHECK-NEXT:    vreplvei.d $vr3, $vr1, 1
-; CHECK-NEXT:    fcvt.s.d $fa3, $fa3
-; CHECK-NEXT:    vreplvei.d $vr1, $vr1, 0
-; CHECK-NEXT:    fcvt.s.d $fa1, $fa1
-; CHECK-NEXT:    vextrins.w $vr2, $vr0, 16
-; CHECK-NEXT:    vextrins.w $vr2, $vr1, 32
-; CHECK-NEXT:    vextrins.w $vr2, $vr3, 48
-; CHECK-NEXT:    vst $vr2, $a0, 0
+; CHECK-NEXT:    vfcvt.s.d $vr0, $vr1, $vr0
+; CHECK-NEXT:    vst $vr0, $a0, 0
 ; CHECK-NEXT:    ret
 entry:
   %x = load <4 x double>, ptr %a0

>From efaff2aa9eea08cf8dd119e6cc7b17f7e8cd0ec1 Mon Sep 17 00:00:00 2001
From: yangzhaoxin <yangzhaoxin at loongson.cn>
Date: Tue, 14 Apr 2026 14:30:02 +0800
Subject: [PATCH 3/3] stricter restrictions on original types

---
 llvm/lib/Target/LoongArch/LoongArchISelLowering.cpp | 9 +++++----
 1 file changed, 5 insertions(+), 4 deletions(-)

diff --git a/llvm/lib/Target/LoongArch/LoongArchISelLowering.cpp b/llvm/lib/Target/LoongArch/LoongArchISelLowering.cpp
index 079d74cc25a2b..a75eab6bb52ef 100644
--- a/llvm/lib/Target/LoongArch/LoongArchISelLowering.cpp
+++ b/llvm/lib/Target/LoongArch/LoongArchISelLowering.cpp
@@ -5051,11 +5051,12 @@ void LoongArchTargetLowering::ReplaceNodeResults(
     // On LSX platforms, rounding from v2f64 to v4f32 (after legalization from
     // v2f32) is scalarized. Add a customized v2f32 widening to convert it into
     // a target-specific LoongArchISD::VFCVT to optimize it.
-    if (VT == MVT::v2f32) {
-      SDValue Src = N->getOperand(0);
-      SDValue Undef = DAG.getUNDEF(Src.getValueType());
+    SDValue Op0 = N->getOperand(0);
+    EVT OpVT = Op0.getValueType();
+    if (OpVT == MVT::v2f64) {
+      SDValue Undef = DAG.getUNDEF(OpVT);
       SDValue Dst =
-          DAG.getNode(LoongArchISD::VFCVT, DL, MVT::v4f32, Undef, Src);
+          DAG.getNode(LoongArchISD::VFCVT, DL, MVT::v4f32, Undef, Op0);
       Results.push_back(Dst);
     }
     break;



More information about the llvm-branch-commits mailing list