[llvm] [LoongArch] Custom LSX and LASX truncate with [X]VPICKEV instruction (PR #201548)

via llvm-commits llvm-commits at lists.llvm.org
Thu Jun 4 04:13:00 PDT 2026


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-backend-loongarch

Author: lrzlin

<details>
<summary>Changes</summary>

Custom wide truncate (e.g. `v8i64` trunc to `v8i8`) with `[X]VPICKEV` instructions.

When LASX is enabled, only use `XVPICKEV` to avoid illegal VR to XR reg copy, but optimize `XVPERMI_D` out as valid data is in lower 128-bits.

---

Patch is 25.95 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/201548.diff


4 Files Affected:

- (modified) llvm/lib/Target/LoongArch/LoongArchISelLowering.cpp (+82) 
- (modified) llvm/lib/Target/LoongArch/LoongArchISelLowering.h (+1) 
- (added) llvm/test/CodeGen/LoongArch/lasx/vec-trunc-combine.ll (+321) 
- (added) llvm/test/CodeGen/LoongArch/lsx/vec-trunc-combine.ll (+293) 


``````````diff
diff --git a/llvm/lib/Target/LoongArch/LoongArchISelLowering.cpp b/llvm/lib/Target/LoongArch/LoongArchISelLowering.cpp
index 561064b3e1090..9234630f85848 100644
--- a/llvm/lib/Target/LoongArch/LoongArchISelLowering.cpp
+++ b/llvm/lib/Target/LoongArch/LoongArchISelLowering.cpp
@@ -408,6 +408,9 @@ LoongArchTargetLowering::LoongArchTargetLowering(const TargetMachine &TM,
     for (MVT VT : {MVT::v16i16, MVT::v8i32, MVT::v4i64, MVT::v16i32, MVT::v8i64,
                    MVT::v16i64})
       setOperationAction(ISD::SIGN_EXTEND, VT, Custom);
+    for (MVT VT : {MVT::v16i16, MVT::v8i32, MVT::v4i64, MVT::v16i32, MVT::v8i64,
+                   MVT::v16i64})
+      setOperationAction(ISD::TRUNCATE, VT, Custom);
   }
 
   // Set operations for 'LASX' feature.
@@ -661,6 +664,8 @@ SDValue LoongArchTargetLowering::LowerOperation(SDValue Op,
     return lowerSIGN_EXTEND_VECTOR_INREG(Op, DAG);
   case ISD::DYNAMIC_STACKALLOC:
     return lowerDYNAMIC_STACKALLOC(Op, DAG);
+  case ISD::TRUNCATE:
+    return lowerTRUNCATE(Op, DAG);
   }
   return SDValue();
 }
@@ -1010,6 +1015,82 @@ SDValue LoongArchTargetLowering::lowerSIGN_EXTEND_VECTOR_INREG(
   return SDValue();
 }
 
+// For large vectors, we can use [X]VPICKEV to narrow the vector
+// recursively until it matches the destination type.
+// This is more efficient than bitcasting to a wider one
+// then shuffling, especially when the source vector is illegal
+// and needs to be constructed from multiple registers.
+SDValue LoongArchTargetLowering::lowerTRUNCATE(
+    SDValue Op, SelectionDAG &DAG) const {
+  SDLoc DL(Op);
+  SDValue Src = Op.getOperand(0);
+  EVT VT = Op.getValueType();
+  EVT SrcVT = Src.getValueType();
+
+  unsigned SrcBits = SrcVT.getSizeInBits();
+  unsigned SrcEltBits = SrcVT.getScalarSizeInBits();
+  unsigned DstEltBits = VT.getScalarSizeInBits();
+  unsigned BlockBits = Subtarget.hasExtLASX() ? 256 : 128;
+
+  SmallVector<SDValue, 8> Blocks;
+  unsigned MidNumElts = BlockBits / SrcEltBits;
+  MVT MidVT = MVT::getVectorVT(MVT::getIntegerVT(SrcEltBits), MidNumElts);
+  if (Src.getOpcode() == ISD::CONCAT_VECTORS &&
+      Src.getOperand(0).getValueSizeInBits() == BlockBits)
+    for (unsigned i = 0; i < Src.getNumOperands(); i++)
+      Blocks.push_back(Src.getOperand(i));
+  else if (SrcBits > BlockBits)
+    for (unsigned i = 0; i < SrcBits / BlockBits; i++)
+      Blocks.push_back(
+          DAG.getNode(ISD::EXTRACT_SUBVECTOR, DL, MidVT, Src,
+                      DAG.getVectorIdxConstant(i * MidNumElts, DL)));
+  else if (SrcBits < BlockBits)
+    Blocks.push_back(DAG.getNode(ISD::INSERT_SUBVECTOR, DL, MidVT,
+                                 DAG.getUNDEF(MidVT), Src,
+                                 DAG.getVectorIdxConstant(0, DL)));
+  else
+    Blocks.push_back(Src);
+
+
+  unsigned CurEltBits = SrcEltBits;
+  bool IsValidUpperBits = SrcBits >= BlockBits;
+  while (CurEltBits > DstEltBits) {
+    unsigned NarrowBits = CurEltBits / 2;
+    unsigned NarrowNumElts = BlockBits / NarrowBits;
+    MVT NarrowVT =
+        MVT::getVectorVT(MVT::getIntegerVT(NarrowBits), NarrowNumElts);
+
+    SmallVector<SDValue, 4> Next;
+    bool SelfPair = Blocks.size() <= 1;
+    for (unsigned i = 0; i < Blocks.size(); i += 2) {
+      SDValue Lo = Blocks[i];
+      SDValue Hi = SelfPair ? Lo : Blocks[i + 1];
+      SDValue Res = DAG.getNode(LoongArchISD::VPICKEV, DL, NarrowVT, Hi, Lo);
+
+      // Fix the data layout under LASX due to XVPICKEV is per 128-bit lane.
+      if (BlockBits == 256 && IsValidUpperBits) {
+        MVT PermVT = MVT::v4i64;
+        Res = DAG.getBitcast(PermVT, Res);
+        Res = DAG.getNode(
+            LoongArchISD::XVPERMI, DL, PermVT, Res,
+            DAG.getConstant(0b11011000, DL, Subtarget.getGRLenVT()));
+        Res = DAG.getBitcast(NarrowVT, Res);
+        // After the first self-pair, all valid data lives in the low 128 bits.
+        if (SelfPair)
+          IsValidUpperBits = false;
+      }
+
+      Next.push_back(Res);
+    }
+
+    Blocks = std::move(Next);
+    CurEltBits = NarrowBits;
+  }
+
+  return DAG.getNode(ISD::EXTRACT_SUBVECTOR, DL, VT, Blocks[0],
+                     DAG.getVectorIdxConstant(0, DL));
+}
+
 // Lower vecreduce_add using vhaddw instructions.
 // For Example:
 //  call i32 @llvm.vector.reduce.add.v4i32(<4 x i32> %a)
@@ -5788,6 +5869,7 @@ void LoongArchTargetLowering::ReplaceNodeResults(
       }
     }
 
+    Results.push_back(lowerTRUNCATE(SDValue(N, 0), DAG));
     break;
   }
   case ISD::SIGN_EXTEND: {
diff --git a/llvm/lib/Target/LoongArch/LoongArchISelLowering.h b/llvm/lib/Target/LoongArch/LoongArchISelLowering.h
index 189ecbe4820d2..36874fc964dc1 100644
--- a/llvm/lib/Target/LoongArch/LoongArchISelLowering.h
+++ b/llvm/lib/Target/LoongArch/LoongArchISelLowering.h
@@ -259,6 +259,7 @@ class LoongArchTargetLowering : public TargetLowering {
   SDValue lowerFP_EXTEND(SDValue Op, SelectionDAG &DAG) const;
   SDValue lowerSIGN_EXTEND_VECTOR_INREG(SDValue Op, SelectionDAG &DAG) const;
   SDValue lowerDYNAMIC_STACKALLOC(SDValue Op, SelectionDAG &DAG) const;
+  SDValue lowerTRUNCATE(SDValue Op, SelectionDAG &DAG) const;
 
   bool isFPImmLegal(const APFloat &Imm, EVT VT,
                     bool ForCodeSize) const override;
diff --git a/llvm/test/CodeGen/LoongArch/lasx/vec-trunc-combine.ll b/llvm/test/CodeGen/LoongArch/lasx/vec-trunc-combine.ll
new file mode 100644
index 0000000000000..2a4ff9120de4c
--- /dev/null
+++ b/llvm/test/CodeGen/LoongArch/lasx/vec-trunc-combine.ll
@@ -0,0 +1,321 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 5
+; RUN: llc --mtriple=loongarch32 --mattr=+32s,+lasx %s -o - | FileCheck %s --check-prefix=LA32
+; RUN: llc --mtriple=loongarch64 --mattr=+lasx %s -o - | FileCheck %s --check-prefix=LA64
+
+define <4 x i8> @test_trunc_v4i16_to_v4i8(<4 x i16> %arg) {
+; LA32-LABEL: test_trunc_v4i16_to_v4i8:
+; LA32:       # %bb.0:
+; LA32-NEXT:    vpickev.b $vr0, $vr0, $vr0
+; LA32-NEXT:    ret
+;
+; LA64-LABEL: test_trunc_v4i16_to_v4i8:
+; LA64:       # %bb.0:
+; LA64-NEXT:    vpickev.b $vr0, $vr0, $vr0
+; LA64-NEXT:    ret
+  %trunc = trunc <4 x i16> %arg to <4 x i8>
+  ret <4 x i8> %trunc
+}
+
+define <4 x i8> @test_trunc_v4i32_to_v4i8(<4 x i32> %arg) {
+; LA32-LABEL: test_trunc_v4i32_to_v4i8:
+; LA32:       # %bb.0:
+; LA32-NEXT:    pcalau12i $a0, %pc_hi20(.LCPI1_0)
+; LA32-NEXT:    vld $vr1, $a0, %pc_lo12(.LCPI1_0)
+; LA32-NEXT:    vshuf.b $vr0, $vr0, $vr0, $vr1
+; LA32-NEXT:    ret
+;
+; LA64-LABEL: test_trunc_v4i32_to_v4i8:
+; LA64:       # %bb.0:
+; LA64-NEXT:    pcalau12i $a0, %pc_hi20(.LCPI1_0)
+; LA64-NEXT:    vld $vr1, $a0, %pc_lo12(.LCPI1_0)
+; LA64-NEXT:    vshuf.b $vr0, $vr0, $vr0, $vr1
+; LA64-NEXT:    ret
+  %trunc = trunc <4 x i32> %arg to <4 x i8>
+  ret <4 x i8> %trunc
+}
+
+define <4 x i16> @test_trunc_v4i32_to_v4i16(<4 x i32> %arg) {
+; LA32-LABEL: test_trunc_v4i32_to_v4i16:
+; LA32:       # %bb.0:
+; LA32-NEXT:    vpickev.h $vr0, $vr0, $vr0
+; LA32-NEXT:    ret
+;
+; LA64-LABEL: test_trunc_v4i32_to_v4i16:
+; LA64:       # %bb.0:
+; LA64-NEXT:    vpickev.h $vr0, $vr0, $vr0
+; LA64-NEXT:    ret
+  %trunc = trunc <4 x i32> %arg to <4 x i16>
+  ret <4 x i16> %trunc
+}
+
+define <4 x i8> @test_trunc_v4i64_to_v4i8(<4 x i64> %arg) {
+; LA32-LABEL: test_trunc_v4i64_to_v4i8:
+; LA32:       # %bb.0:
+; LA32-NEXT:    xvpickev.w $xr0, $xr0, $xr0
+; LA32-NEXT:    xvpermi.d $xr0, $xr0, 216
+; LA32-NEXT:    xvpickev.h $xr0, $xr0, $xr0
+; LA32-NEXT:    xvpickev.b $xr0, $xr0, $xr0
+; LA32-NEXT:    # kill: def $vr0 killed $vr0 killed $xr0
+; LA32-NEXT:    ret
+;
+; LA64-LABEL: test_trunc_v4i64_to_v4i8:
+; LA64:       # %bb.0:
+; LA64-NEXT:    xvpickev.w $xr0, $xr0, $xr0
+; LA64-NEXT:    xvpermi.d $xr0, $xr0, 216
+; LA64-NEXT:    xvpickev.h $xr0, $xr0, $xr0
+; LA64-NEXT:    xvpickev.b $xr0, $xr0, $xr0
+; LA64-NEXT:    # kill: def $vr0 killed $vr0 killed $xr0
+; LA64-NEXT:    ret
+  %trunc = trunc <4 x i64> %arg to <4 x i8>
+  ret <4 x i8> %trunc
+}
+
+define <4 x i16> @test_trunc_v4i64_to_v4i16(<4 x i64> %arg) {
+; LA32-LABEL: test_trunc_v4i64_to_v4i16:
+; LA32:       # %bb.0:
+; LA32-NEXT:    xvpickev.w $xr0, $xr0, $xr0
+; LA32-NEXT:    xvpermi.d $xr0, $xr0, 216
+; LA32-NEXT:    xvpickev.h $xr0, $xr0, $xr0
+; LA32-NEXT:    # kill: def $vr0 killed $vr0 killed $xr0
+; LA32-NEXT:    ret
+;
+; LA64-LABEL: test_trunc_v4i64_to_v4i16:
+; LA64:       # %bb.0:
+; LA64-NEXT:    xvpickev.w $xr0, $xr0, $xr0
+; LA64-NEXT:    xvpermi.d $xr0, $xr0, 216
+; LA64-NEXT:    xvpickev.h $xr0, $xr0, $xr0
+; LA64-NEXT:    # kill: def $vr0 killed $vr0 killed $xr0
+; LA64-NEXT:    ret
+  %trunc = trunc <4 x i64> %arg to <4 x i16>
+  ret <4 x i16> %trunc
+}
+
+define <4 x i32> @test_trunc_v4i64_to_v4i32(<4 x i64> %arg) {
+; LA32-LABEL: test_trunc_v4i64_to_v4i32:
+; LA32:       # %bb.0:
+; LA32-NEXT:    xvpickev.w $xr0, $xr0, $xr0
+; LA32-NEXT:    xvpermi.d $xr0, $xr0, 216
+; LA32-NEXT:    # kill: def $vr0 killed $vr0 killed $xr0
+; LA32-NEXT:    ret
+;
+; LA64-LABEL: test_trunc_v4i64_to_v4i32:
+; LA64:       # %bb.0:
+; LA64-NEXT:    xvpickev.w $xr0, $xr0, $xr0
+; LA64-NEXT:    xvpermi.d $xr0, $xr0, 216
+; LA64-NEXT:    # kill: def $vr0 killed $vr0 killed $xr0
+; LA64-NEXT:    ret
+  %trunc = trunc <4 x i64> %arg to <4 x i32>
+  ret <4 x i32> %trunc
+}
+
+define <8 x i8> @test_trunc_v8i32_to_v8i8(<8 x i32> %arg) {
+; LA32-LABEL: test_trunc_v8i32_to_v8i8:
+; LA32:       # %bb.0:
+; LA32-NEXT:    xvpickev.h $xr0, $xr0, $xr0
+; LA32-NEXT:    xvpermi.d $xr0, $xr0, 216
+; LA32-NEXT:    xvpickev.b $xr0, $xr0, $xr0
+; LA32-NEXT:    # kill: def $vr0 killed $vr0 killed $xr0
+; LA32-NEXT:    ret
+;
+; LA64-LABEL: test_trunc_v8i32_to_v8i8:
+; LA64:       # %bb.0:
+; LA64-NEXT:    xvpickev.h $xr0, $xr0, $xr0
+; LA64-NEXT:    xvpermi.d $xr0, $xr0, 216
+; LA64-NEXT:    xvpickev.b $xr0, $xr0, $xr0
+; LA64-NEXT:    # kill: def $vr0 killed $vr0 killed $xr0
+; LA64-NEXT:    ret
+  %trunc = trunc <8 x i32> %arg to <8 x i8>
+  ret <8 x i8> %trunc
+}
+
+define <8 x i16> @test_trunc_v8i32_to_v8i16(<8 x i32> %arg) {
+; LA32-LABEL: test_trunc_v8i32_to_v8i16:
+; LA32:       # %bb.0:
+; LA32-NEXT:    xvpickev.h $xr0, $xr0, $xr0
+; LA32-NEXT:    xvpermi.d $xr0, $xr0, 216
+; LA32-NEXT:    # kill: def $vr0 killed $vr0 killed $xr0
+; LA32-NEXT:    ret
+;
+; LA64-LABEL: test_trunc_v8i32_to_v8i16:
+; LA64:       # %bb.0:
+; LA64-NEXT:    xvpickev.h $xr0, $xr0, $xr0
+; LA64-NEXT:    xvpermi.d $xr0, $xr0, 216
+; LA64-NEXT:    # kill: def $vr0 killed $vr0 killed $xr0
+; LA64-NEXT:    ret
+  %trunc = trunc <8 x i32> %arg to <8 x i16>
+  ret <8 x i16> %trunc
+}
+
+define <8 x i8> @test_trunc_v8i64_to_v8i8(<8 x i64> %arg) {
+; LA32-LABEL: test_trunc_v8i64_to_v8i8:
+; LA32:       # %bb.0:
+; LA32-NEXT:    xvpickev.w $xr0, $xr1, $xr0
+; LA32-NEXT:    xvpermi.d $xr0, $xr0, 216
+; LA32-NEXT:    xvpickev.h $xr0, $xr0, $xr0
+; LA32-NEXT:    xvpermi.d $xr0, $xr0, 216
+; LA32-NEXT:    xvpickev.b $xr0, $xr0, $xr0
+; LA32-NEXT:    # kill: def $vr0 killed $vr0 killed $xr0
+; LA32-NEXT:    ret
+;
+; LA64-LABEL: test_trunc_v8i64_to_v8i8:
+; LA64:       # %bb.0:
+; LA64-NEXT:    xvpickev.w $xr0, $xr1, $xr0
+; LA64-NEXT:    xvpermi.d $xr0, $xr0, 216
+; LA64-NEXT:    xvpickev.h $xr0, $xr0, $xr0
+; LA64-NEXT:    xvpermi.d $xr0, $xr0, 216
+; LA64-NEXT:    xvpickev.b $xr0, $xr0, $xr0
+; LA64-NEXT:    # kill: def $vr0 killed $vr0 killed $xr0
+; LA64-NEXT:    ret
+  %trunc = trunc <8 x i64> %arg to <8 x i8>
+  ret <8 x i8> %trunc
+}
+
+define <8 x i16> @test_trunc_v8i64_to_v8i16(<8 x i64> %arg) {
+; LA32-LABEL: test_trunc_v8i64_to_v8i16:
+; LA32:       # %bb.0:
+; LA32-NEXT:    xvpickev.w $xr0, $xr1, $xr0
+; LA32-NEXT:    xvpermi.d $xr0, $xr0, 216
+; LA32-NEXT:    xvpickev.h $xr0, $xr0, $xr0
+; LA32-NEXT:    xvpermi.d $xr0, $xr0, 216
+; LA32-NEXT:    # kill: def $vr0 killed $vr0 killed $xr0
+; LA32-NEXT:    ret
+;
+; LA64-LABEL: test_trunc_v8i64_to_v8i16:
+; LA64:       # %bb.0:
+; LA64-NEXT:    xvpickev.w $xr0, $xr1, $xr0
+; LA64-NEXT:    xvpermi.d $xr0, $xr0, 216
+; LA64-NEXT:    xvpickev.h $xr0, $xr0, $xr0
+; LA64-NEXT:    xvpermi.d $xr0, $xr0, 216
+; LA64-NEXT:    # kill: def $vr0 killed $vr0 killed $xr0
+; LA64-NEXT:    ret
+  %trunc = trunc <8 x i64> %arg to <8 x i16>
+  ret <8 x i16> %trunc
+}
+
+define <8 x i32> @test_trunc_v8i64_to_v8i32(<8 x i64> %arg) {
+; LA32-LABEL: test_trunc_v8i64_to_v8i32:
+; LA32:       # %bb.0:
+; LA32-NEXT:    xvpickev.w $xr0, $xr1, $xr0
+; LA32-NEXT:    xvpermi.d $xr0, $xr0, 216
+; LA32-NEXT:    ret
+;
+; LA64-LABEL: test_trunc_v8i64_to_v8i32:
+; LA64:       # %bb.0:
+; LA64-NEXT:    xvpickev.w $xr0, $xr1, $xr0
+; LA64-NEXT:    xvpermi.d $xr0, $xr0, 216
+; LA64-NEXT:    ret
+  %trunc = trunc <8 x i64> %arg to <8 x i32>
+  ret <8 x i32> %trunc
+}
+
+define <16 x i8> @test_trunc_v16i32_to_v16i8(<16 x i32> %arg) {
+; LA32-LABEL: test_trunc_v16i32_to_v16i8:
+; LA32:       # %bb.0:
+; LA32-NEXT:    xvpickev.h $xr0, $xr1, $xr0
+; LA32-NEXT:    xvpermi.d $xr0, $xr0, 216
+; LA32-NEXT:    xvpickev.b $xr0, $xr0, $xr0
+; LA32-NEXT:    xvpermi.d $xr0, $xr0, 216
+; LA32-NEXT:    # kill: def $vr0 killed $vr0 killed $xr0
+; LA32-NEXT:    ret
+;
+; LA64-LABEL: test_trunc_v16i32_to_v16i8:
+; LA64:       # %bb.0:
+; LA64-NEXT:    xvpickev.h $xr0, $xr1, $xr0
+; LA64-NEXT:    xvpermi.d $xr0, $xr0, 216
+; LA64-NEXT:    xvpickev.b $xr0, $xr0, $xr0
+; LA64-NEXT:    xvpermi.d $xr0, $xr0, 216
+; LA64-NEXT:    # kill: def $vr0 killed $vr0 killed $xr0
+; LA64-NEXT:    ret
+  %trunc = trunc <16 x i32> %arg to <16 x i8>
+  ret <16 x i8> %trunc
+}
+
+define <16 x i16> @test_trunc_v16i32_to_v16i16(<16 x i32> %arg) {
+; LA32-LABEL: test_trunc_v16i32_to_v16i16:
+; LA32:       # %bb.0:
+; LA32-NEXT:    xvpickev.h $xr0, $xr1, $xr0
+; LA32-NEXT:    xvpermi.d $xr0, $xr0, 216
+; LA32-NEXT:    ret
+;
+; LA64-LABEL: test_trunc_v16i32_to_v16i16:
+; LA64:       # %bb.0:
+; LA64-NEXT:    xvpickev.h $xr0, $xr1, $xr0
+; LA64-NEXT:    xvpermi.d $xr0, $xr0, 216
+; LA64-NEXT:    ret
+  %trunc = trunc <16 x i32> %arg to <16 x i16>
+  ret <16 x i16> %trunc
+}
+
+define <16 x i8> @test_trunc_v16i64_to_v16i8(<16 x i64> %arg) {
+; LA32-LABEL: test_trunc_v16i64_to_v16i8:
+; LA32:       # %bb.0:
+; LA32-NEXT:    xvpickev.w $xr0, $xr1, $xr0
+; LA32-NEXT:    xvpermi.d $xr0, $xr0, 216
+; LA32-NEXT:    xvpickev.w $xr1, $xr3, $xr2
+; LA32-NEXT:    xvpermi.d $xr1, $xr1, 216
+; LA32-NEXT:    xvpickev.h $xr0, $xr1, $xr0
+; LA32-NEXT:    xvpermi.d $xr0, $xr0, 216
+; LA32-NEXT:    xvpickev.b $xr0, $xr0, $xr0
+; LA32-NEXT:    xvpermi.d $xr0, $xr0, 216
+; LA32-NEXT:    # kill: def $vr0 killed $vr0 killed $xr0
+; LA32-NEXT:    ret
+;
+; LA64-LABEL: test_trunc_v16i64_to_v16i8:
+; LA64:       # %bb.0:
+; LA64-NEXT:    xvpickev.w $xr0, $xr1, $xr0
+; LA64-NEXT:    xvpermi.d $xr0, $xr0, 216
+; LA64-NEXT:    xvpickev.w $xr1, $xr3, $xr2
+; LA64-NEXT:    xvpermi.d $xr1, $xr1, 216
+; LA64-NEXT:    xvpickev.h $xr0, $xr1, $xr0
+; LA64-NEXT:    xvpermi.d $xr0, $xr0, 216
+; LA64-NEXT:    xvpickev.b $xr0, $xr0, $xr0
+; LA64-NEXT:    xvpermi.d $xr0, $xr0, 216
+; LA64-NEXT:    # kill: def $vr0 killed $vr0 killed $xr0
+; LA64-NEXT:    ret
+  %trunc = trunc <16 x i64> %arg to <16 x i8>
+  ret <16 x i8> %trunc
+}
+
+define <16 x i16> @test_trunc_v16i64_to_v16i16(<16 x i64> %arg) {
+; LA32-LABEL: test_trunc_v16i64_to_v16i16:
+; LA32:       # %bb.0:
+; LA32-NEXT:    xvpickev.w $xr0, $xr1, $xr0
+; LA32-NEXT:    xvpermi.d $xr0, $xr0, 216
+; LA32-NEXT:    xvpickev.w $xr1, $xr3, $xr2
+; LA32-NEXT:    xvpermi.d $xr1, $xr1, 216
+; LA32-NEXT:    xvpickev.h $xr0, $xr1, $xr0
+; LA32-NEXT:    xvpermi.d $xr0, $xr0, 216
+; LA32-NEXT:    ret
+;
+; LA64-LABEL: test_trunc_v16i64_to_v16i16:
+; LA64:       # %bb.0:
+; LA64-NEXT:    xvpickev.w $xr0, $xr1, $xr0
+; LA64-NEXT:    xvpermi.d $xr0, $xr0, 216
+; LA64-NEXT:    xvpickev.w $xr1, $xr3, $xr2
+; LA64-NEXT:    xvpermi.d $xr1, $xr1, 216
+; LA64-NEXT:    xvpickev.h $xr0, $xr1, $xr0
+; LA64-NEXT:    xvpermi.d $xr0, $xr0, 216
+; LA64-NEXT:    ret
+  %trunc = trunc <16 x i64> %arg to <16 x i16>
+  ret <16 x i16> %trunc
+}
+
+define <16 x i32> @test_trunc_v16i64_to_v16i32(<16 x i64> %arg) {
+; LA32-LABEL: test_trunc_v16i64_to_v16i32:
+; LA32:       # %bb.0:
+; LA32-NEXT:    xvpickev.w $xr0, $xr1, $xr0
+; LA32-NEXT:    xvpermi.d $xr0, $xr0, 216
+; LA32-NEXT:    xvpickev.w $xr1, $xr3, $xr2
+; LA32-NEXT:    xvpermi.d $xr1, $xr1, 216
+; LA32-NEXT:    ret
+;
+; LA64-LABEL: test_trunc_v16i64_to_v16i32:
+; LA64:       # %bb.0:
+; LA64-NEXT:    xvpickev.w $xr0, $xr1, $xr0
+; LA64-NEXT:    xvpermi.d $xr0, $xr0, 216
+; LA64-NEXT:    xvpickev.w $xr1, $xr3, $xr2
+; LA64-NEXT:    xvpermi.d $xr1, $xr1, 216
+; LA64-NEXT:    ret
+  %trunc = trunc <16 x i64> %arg to <16 x i32>
+  ret <16 x i32> %trunc
+}
diff --git a/llvm/test/CodeGen/LoongArch/lsx/vec-trunc-combine.ll b/llvm/test/CodeGen/LoongArch/lsx/vec-trunc-combine.ll
new file mode 100644
index 0000000000000..51ef91846e3c5
--- /dev/null
+++ b/llvm/test/CodeGen/LoongArch/lsx/vec-trunc-combine.ll
@@ -0,0 +1,293 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 5
+; RUN: llc --mtriple=loongarch32 --mattr=+32s,+lsx %s -o - | FileCheck %s --check-prefix=LA32
+; RUN: llc --mtriple=loongarch64 --mattr=+lsx %s -o - | FileCheck %s --check-prefix=LA64
+
+define <4 x i8> @test_trunc_v4i16_to_v4i8(<4 x i16> %arg) {
+; LA32-LABEL: test_trunc_v4i16_to_v4i8:
+; LA32:       # %bb.0:
+; LA32-NEXT:    vpickev.b $vr0, $vr0, $vr0
+; LA32-NEXT:    ret
+;
+; LA64-LABEL: test_trunc_v4i16_to_v4i8:
+; LA64:       # %bb.0:
+; LA64-NEXT:    vpickev.b $vr0, $vr0, $vr0
+; LA64-NEXT:    ret
+  %trunc = trunc <4 x i16> %arg to <4 x i8>
+  ret <4 x i8> %trunc
+}
+
+define <4 x i8> @test_trunc_v4i32_to_v4i8(<4 x i32> %arg) {
+; LA32-LABEL: test_trunc_v4i32_to_v4i8:
+; LA32:       # %bb.0:
+; LA32-NEXT:    pcalau12i $a0, %pc_hi20(.LCPI1_0)
+; LA32-NEXT:    vld $vr1, $a0, %pc_lo12(.LCPI1_0)
+; LA32-NEXT:    vshuf.b $vr0, $vr0, $vr0, $vr1
+; LA32-NEXT:    ret
+;
+; LA64-LABEL: test_trunc_v4i32_to_v4i8:
+; LA64:       # %bb.0:
+; LA64-NEXT:    pcalau12i $a0, %pc_hi20(.LCPI1_0)
+; LA64-NEXT:    vld $vr1, $a0, %pc_lo12(.LCPI1_0)
+; LA64-NEXT:    vshuf.b $vr0, $vr0, $vr0, $vr1
+; LA64-NEXT:    ret
+  %trunc = trunc <4 x i32> %arg to <4 x i8>
+  ret <4 x i8> %trunc
+}
+
+define <4 x i16> @test_trunc_v4i32_to_v4i16(<4 x i32> %arg) {
+; LA32-LABEL: test_trunc_v4i32_to_v4i16:
+; LA32:       # %bb.0:
+; LA32-NEXT:    vpickev.h $vr0, $vr0, $vr0
+; LA32-NEXT:    ret
+;
+; LA64-LABEL: test_trunc_v4i32_to_v4i16:
+; LA64:       # %bb.0:
+; LA64-NEXT:    vpickev.h $vr0, $vr0, $vr0
+; LA64-NEXT:    ret
+  %trunc = trunc <4 x i32> %arg to <4 x i16>
+  ret <4 x i16> %trunc
+}
+
+define <4 x i8> @test_trunc_v4i64_to_v4i8(<4 x i64> %arg) {
+; LA32-LABEL: test_trunc_v4i64_to_v4i8:
+; LA32:       # %bb.0:
+; LA32-NEXT:    vpickev.w $vr0, $vr1, $vr0
+; LA32-NEXT:    vpickev.h $vr0, $vr0, $vr0
+; LA32-NEXT:    vpickev.b $vr0, $vr0, $vr0
+; LA32-NEXT:    ret
+;
+; LA64-LABEL: test_trunc_v4i64_to_v4i8:
+; LA64:       # %bb.0:
+; LA64-NEXT:    vpickev.w $vr0, $vr1, $vr0
+; LA64-NEXT:    vpickev.h $vr0, $vr0, $vr0
+; LA64-NEXT:    vpickev.b $vr0, $vr0, $vr0
+; LA64-NEXT:    ret
+  %trunc = trunc <4 x i64> %arg to <4 x i8>
+  ret <4 x i8> %trunc
+}
+
+define <4 x i16> @test_trunc_v4i64_to_v4i16(<4 x i64> %arg) {
+; LA32-LABEL: test_trunc_v4i64_to_v4i16:
+; LA32:       # %bb.0:
+; LA32-NEXT:    vpickev.w $vr0, $vr1, $vr0
+; LA32-NEXT:    vpickev.h $vr0, $vr0, $vr0
+; LA32-NEXT:    ret
+;
+; LA64-LABEL: test_trunc_v4i64_to_v4i16:
+; LA64:       # %bb.0:
+; LA64-NEXT:    vpickev.w $vr0, $vr1, $vr0
+; LA64-NEXT:    vpickev.h $vr0, $vr0, $vr0
+; LA64-NEXT:    ret
+  %trunc = trunc <4 x i64> %arg to <4 x i16>
+  ret <4 x i16> %trunc
+}
+
+define <4 x i32> @test_trunc_v4i64_to_v4i32(<4 x i64> %arg) {
+; LA32-LABEL: test_trunc_v4i64_to_v4i32:
+; LA32:       # %bb.0:
+; LA32-NEXT:    vpickev.w $vr0, $vr1, $vr0
+; LA32-NEXT:    ret
+;
+; LA64-LABEL: test_trunc_v4i64_to_v4i32:
+; LA64:       # %bb.0:
+; LA64-NEXT:    vpickev.w $vr0, $vr1, $vr0
+; LA64-NEXT:    ret
+  %trunc = trunc <4 x i64> %arg to <4 x i32>
+  ret <4 x i32> %trunc
+}
+
+define <8 x i8> @test_trunc_v8i32_to_v8i8(<8 x i32> %arg) {
+; LA32-LABEL: test_trunc_v8i32_to_v8i8:
+; LA32:       # %bb.0:
+; LA32-NEXT:    vpickev.h $vr0, $vr1, $vr0
+; LA32-NEXT:    vpickev.b $vr0, $vr0, $vr0
+; LA32-NEXT:    ret
+;
+; LA64-LABEL: test_trunc_v8i32_to_v8i8:
+; LA64:       # %bb.0:
+; LA64-NEXT:    vpickev.h $vr0, $vr1, $vr0
+; LA64-NEXT...
[truncated]

``````````

</details>


https://github.com/llvm/llvm-project/pull/201548


More information about the llvm-commits mailing list