[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