[llvm] [DAG] Fix illegal type in srl(bitcast(build_vector)) fold (PR #205074)
Kito Cheng via llvm-commits
llvm-commits at lists.llvm.org
Mon Jun 22 03:04:13 PDT 2026
https://github.com/kito-cheng updated https://github.com/llvm/llvm-project/pull/205074
>From 9a68883e2195f6d7bd8499e1ef23c927d3ddc048 Mon Sep 17 00:00:00 2001
From: Kito Cheng <kito.cheng at sifive.com>
Date: Mon, 22 Jun 2026 16:02:05 +0800
Subject: [PATCH 1/2] [DAG] Fix illegal type in srl(bitcast(build_vector)) fold
The fold
```
(srl (bitcast (build_vector e1, ..., eN)), (N-1) * eltsize) -> (zext eN)
```
added in #181412 builds a zext/trunc through the element integer type.
This type can be illegal, for example i16 on a target with no native i16.
When the fold runs in the last DAG combine, the new node is legalized
right away, so an illegal type makes LegalizeDAG hit the "Unexpected
illegal type!" assert.
This happens on RV32 with the P extension, where <2 x i16> is legal:
```
define i16 @f(<2 x i16> %v, ptr %p) {
%div = sdiv <2 x i16> splat (i16 1), %v
store <2 x i16> %div, ptr %p
%e = extractelement <2 x i16> %div, i64 1
ret i16 %e
}
```
Taking the last lane gives `srl(bitcast(build_vector), 16)`, and the fold
turns it into `zext(trunc(i16))`, which is illegal on RV32.
So only run the fold after DAG legalization when the element type is
legal. Earlier combine runs are still legalized later, so the illegal
node is removed before legalization.
Assisted-by: Opus 4.8
---
llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp | 8 +++-
llvm/test/CodeGen/RISCV/rvp-srl-bitcast-bv.ll | 38 +++++++++++++++++++
2 files changed, 44 insertions(+), 2 deletions(-)
create mode 100644 llvm/test/CodeGen/RISCV/rvp-srl-bitcast-bv.ll
diff --git a/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp b/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp
index 4fdef7d4afb5d..f78cc9ae13771 100644
--- a/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp
@@ -11694,11 +11694,15 @@ SDValue DAGCombiner::visitSRL(SDNode *N) {
EVT BVVT = BV.getValueType();
unsigned EltSizeInBits = BVVT.getScalarSizeInBits();
unsigned NumElts = BVVT.getVectorNumElements();
- if (N1C->getZExtValue() == (NumElts - 1) * EltSizeInBits) {
+ EVT IntEltVT = EVT::getIntegerVT(*DAG.getContext(), EltSizeInBits);
+ // This fold builds a zext/trunc through IntEltVT. In the last DAG combine
+ // new nodes are legalized at once, so only do it there if IntEltVT is
+ // legal. Earlier combines are legalized again later.
+ if (N1C->getZExtValue() == (NumElts - 1) * EltSizeInBits &&
+ (!LegalDAG || TLI.isTypeLegal(IntEltVT))) {
SDValue LastElt = BV.getOperand(NumElts - 1);
assert(LastElt.getScalarValueSizeInBits() >= EltSizeInBits &&
"Expected BUILD_VECTOR operand as wide as element type");
- EVT IntEltVT = EVT::getIntegerVT(*DAG.getContext(), EltSizeInBits);
LastElt = DAG.getBitcast(LastElt.getValueType().changeTypeToInteger(),
LastElt);
return DAG.getZExtOrTrunc(DAG.getZExtOrTrunc(LastElt, DL, IntEltVT), DL,
diff --git a/llvm/test/CodeGen/RISCV/rvp-srl-bitcast-bv.ll b/llvm/test/CodeGen/RISCV/rvp-srl-bitcast-bv.ll
new file mode 100644
index 0000000000000..d7e5371dc58d5
--- /dev/null
+++ b/llvm/test/CodeGen/RISCV/rvp-srl-bitcast-bv.ll
@@ -0,0 +1,38 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
+; RUN: llc -mtriple=riscv32 -mattr=+experimental-p,+m,+zbb \
+; RUN: -verify-machineinstrs < %s | \
+; RUN: FileCheck --check-prefixes=CHECK-RV32 %s
+; RUN: llc -mtriple=riscv64 -mattr=+experimental-p,+m,+zbb \
+; RUN: -verify-machineinstrs < %s | \
+; RUN: FileCheck --check-prefixes=CHECK-RV64 %s
+
+define i16 @srl_bitcast_buildvector_extract_last(<2 x i16> %v, ptr %p) {
+; CHECK-RV32-LABEL: srl_bitcast_buildvector_extract_last:
+; CHECK-RV32: # %bb.0:
+; CHECK-RV32-NEXT: li a2, 1
+; CHECK-RV32-NEXT: srai a3, a0, 16
+; CHECK-RV32-NEXT: div a3, a2, a3
+; CHECK-RV32-NEXT: sext.h a0, a0
+; CHECK-RV32-NEXT: div a0, a2, a0
+; CHECK-RV32-NEXT: pack a2, a0, a3
+; CHECK-RV32-NEXT: srli a0, a2, 16
+; CHECK-RV32-NEXT: sw a2, 0(a1)
+; CHECK-RV32-NEXT: ret
+;
+; CHECK-RV64-LABEL: srl_bitcast_buildvector_extract_last:
+; CHECK-RV64: # %bb.0:
+; CHECK-RV64-NEXT: li a2, 1
+; CHECK-RV64-NEXT: sext.h a3, a0
+; CHECK-RV64-NEXT: divw a3, a2, a3
+; CHECK-RV64-NEXT: slli a0, a0, 32
+; CHECK-RV64-NEXT: srai a0, a0, 48
+; CHECK-RV64-NEXT: divw a2, a2, a0
+; CHECK-RV64-NEXT: sext.h a0, a2
+; CHECK-RV64-NEXT: ppaire.h a2, a3, a2
+; CHECK-RV64-NEXT: sw a2, 0(a1)
+; CHECK-RV64-NEXT: ret
+ %div = sdiv <2 x i16> splat (i16 1), %v
+ store <2 x i16> %div, ptr %p
+ %e = extractelement <2 x i16> %div, i64 1
+ ret i16 %e
+}
>From 8445b5ac648fd89c0c6e4a56a58559b2e39d7ecf Mon Sep 17 00:00:00 2001
From: Kito Cheng <kito.cheng at gmail.com>
Date: Mon, 22 Jun 2026 18:04:04 +0800
Subject: [PATCH 2/2] Update llvm/test/CodeGen/RISCV/rvp-srl-bitcast-bv.ll
Co-authored-by: Matt Arsenault <arsenm2 at gmail.com>
---
llvm/test/CodeGen/RISCV/rvp-srl-bitcast-bv.ll | 6 ++----
1 file changed, 2 insertions(+), 4 deletions(-)
diff --git a/llvm/test/CodeGen/RISCV/rvp-srl-bitcast-bv.ll b/llvm/test/CodeGen/RISCV/rvp-srl-bitcast-bv.ll
index d7e5371dc58d5..4692dbb7fdc1b 100644
--- a/llvm/test/CodeGen/RISCV/rvp-srl-bitcast-bv.ll
+++ b/llvm/test/CodeGen/RISCV/rvp-srl-bitcast-bv.ll
@@ -1,9 +1,7 @@
; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
-; RUN: llc -mtriple=riscv32 -mattr=+experimental-p,+m,+zbb \
-; RUN: -verify-machineinstrs < %s | \
+; RUN: llc -mtriple=riscv32 -mattr=+experimental-p,+m,+zbb < %s | \
; RUN: FileCheck --check-prefixes=CHECK-RV32 %s
-; RUN: llc -mtriple=riscv64 -mattr=+experimental-p,+m,+zbb \
-; RUN: -verify-machineinstrs < %s | \
+; RUN: llc -mtriple=riscv64 -mattr=+experimental-p,+m,+zbb < %s | \
; RUN: FileCheck --check-prefixes=CHECK-RV64 %s
define i16 @srl_bitcast_buildvector_extract_last(<2 x i16> %v, ptr %p) {
More information about the llvm-commits
mailing list