[llvm] [SelectionDAG] Fix soft-promotion of f16 BUILD_VECTOR for MSA targets (PR #210515)

via llvm-commits llvm-commits at lists.llvm.org
Fri Aug 14 20:56:54 PDT 2026


https://github.com/fo40225 updated https://github.com/llvm/llvm-project/pull/210515

>From f02b484bfc68604e3c41d0531277650a20c93240 Mon Sep 17 00:00:00 2001
From: fo4025 <fo40225 at hotmail.com>
Date: Thu, 13 Aug 2026 17:37:01 +0800
Subject: [PATCH 1/2] [Mips] Add f16 BUILD_VECTOR regression tests for MSA
 targets

Test that f16 vectors (v8f16, v4f16, v2f16) can be built from scalar
f16 values on MIPS with +msa. Without the corresponding fix, llc
crashes with "Do not know how to soft promote this operator's
operand!" in SoftPromoteHalfOperand.

Co-Authored-By: Claude Fable 5 <noreply at anthropic.com>
---
 llvm/test/CodeGen/Mips/msa/f16vec.ll | 135 +++++++++++++++++++++++++++
 1 file changed, 135 insertions(+)
 create mode 100644 llvm/test/CodeGen/Mips/msa/f16vec.ll

diff --git a/llvm/test/CodeGen/Mips/msa/f16vec.ll b/llvm/test/CodeGen/Mips/msa/f16vec.ll
new file mode 100644
index 0000000000000..53bfd967f0a8f
--- /dev/null
+++ b/llvm/test/CodeGen/Mips/msa/f16vec.ll
@@ -0,0 +1,135 @@
+; RUN: llc -mtriple=mipsel-unknown-linux-gnu -mcpu=mips32r5 -mattr=+fp64,+msa < %s | FileCheck %s --check-prefix=MIPS32
+; RUN: llc -mtriple=mips64el-unknown-linux-gnuabi64 -mcpu=mips64r5 -mattr=+fp64,+msa < %s | FileCheck %s --check-prefix=MIPS64
+
+; Test that f16 vectors can be built from scalar f16 values without crashing.
+; This is a regression test for a crash in soft-promotion of BUILD_VECTOR
+; operands.
+
+define <8 x half> @build_v8f16(half %a, half %b, half %c, half %d, half %e, half %f, half %g, half %h) nounwind {
+; MIPS32-LABEL: build_v8f16:
+; MIPS32:       # %bb.0:
+; MIPS32-NEXT:    insert.h $w0[0], $5
+; MIPS32-NEXT:    insert.h $w0[1], $6
+; MIPS32-NEXT:    insert.h $w0[2], $7
+; MIPS32-NEXT:    lhu $1, 16($sp)
+; MIPS32-NEXT:    insert.h $w0[3], $1
+; MIPS32-NEXT:    lhu $1, 20($sp)
+; MIPS32-NEXT:    insert.h $w0[4], $1
+; MIPS32-NEXT:    lhu $1, 24($sp)
+; MIPS32-NEXT:    insert.h $w0[5], $1
+; MIPS32-NEXT:    lhu $1, 28($sp)
+; MIPS32-NEXT:    insert.h $w0[6], $1
+; MIPS32-NEXT:    lhu $1, 32($sp)
+; MIPS32-NEXT:    insert.h $w0[7], $1
+; MIPS32-NEXT:    jr $ra
+; MIPS32-NEXT:    st.h $w0, 0($4)
+;
+; MIPS64-LABEL: build_v8f16:
+; MIPS64:       # %bb.0:
+; MIPS64-NEXT:    sll $1, $5, 0
+; MIPS64-NEXT:    sll $2, $4, 0
+; MIPS64-NEXT:    insert.h $w0[0], $2
+; MIPS64-NEXT:    insert.h $w0[1], $1
+; MIPS64-NEXT:    sll $1, $6, 0
+; MIPS64-NEXT:    insert.h $w0[2], $1
+; MIPS64-NEXT:    sll $1, $7, 0
+; MIPS64-NEXT:    insert.h $w0[3], $1
+; MIPS64-NEXT:    sll $1, $8, 0
+; MIPS64-NEXT:    insert.h $w0[4], $1
+; MIPS64-NEXT:    sll $1, $9, 0
+; MIPS64-NEXT:    insert.h $w0[5], $1
+; MIPS64-NEXT:    sll $1, $11, 0
+; MIPS64-NEXT:    sll $2, $10, 0
+; MIPS64-NEXT:    insert.h $w0[6], $2
+; MIPS64-NEXT:    insert.h $w0[7], $1
+; MIPS64-NEXT:    copy_s.d $2, $w0[0]
+; MIPS64-NEXT:    jr $ra
+; MIPS64-NEXT:    copy_s.d $3, $w0[1]
+  %v0 = insertelement <8 x half> poison, half %a, i32 0
+  %v1 = insertelement <8 x half> %v0, half %b, i32 1
+  %v2 = insertelement <8 x half> %v1, half %c, i32 2
+  %v3 = insertelement <8 x half> %v2, half %d, i32 3
+  %v4 = insertelement <8 x half> %v3, half %e, i32 4
+  %v5 = insertelement <8 x half> %v4, half %f, i32 5
+  %v6 = insertelement <8 x half> %v5, half %g, i32 6
+  %v7 = insertelement <8 x half> %v6, half %h, i32 7
+  ret <8 x half> %v7
+}
+
+define <4 x half> @build_v4f16(half %a, half %b, half %c, half %d) nounwind {
+; MIPS32-LABEL: build_v4f16:
+; MIPS32:       # %bb.0:
+; MIPS32-NEXT:    addiu $sp, $sp, -32
+; MIPS32-NEXT:    sw $ra, 28($sp) # 4-byte Folded Spill
+; MIPS32-NEXT:    sw $fp, 24($sp) # 4-byte Folded Spill
+; MIPS32-NEXT:    move $fp, $sp
+; MIPS32-NEXT:    addiu $1, $zero, -16
+; MIPS32-NEXT:    and $sp, $sp, $1
+; MIPS32-NEXT:    lhu $1, 48($fp)
+; MIPS32-NEXT:    sh $1, 6($sp)
+; MIPS32-NEXT:    sh $7, 4($sp)
+; MIPS32-NEXT:    sh $6, 2($sp)
+; MIPS32-NEXT:    sh $5, 0($sp)
+; MIPS32-NEXT:    ld.w $w0, 0($sp)
+; MIPS32-NEXT:    copy_s.w $1, $w0[0]
+; MIPS32-NEXT:    copy_s.w $2, $w0[1]
+; MIPS32-NEXT:    sw $2, 4($4)
+; MIPS32-NEXT:    sw $1, 0($4)
+; MIPS32-NEXT:    move $sp, $fp
+; MIPS32-NEXT:    lw $fp, 24($sp) # 4-byte Folded Reload
+; MIPS32-NEXT:    lw $ra, 28($sp) # 4-byte Folded Reload
+; MIPS32-NEXT:    jr $ra
+; MIPS32-NEXT:    addiu $sp, $sp, 32
+;
+; MIPS64-LABEL: build_v4f16:
+; MIPS64:       # %bb.0:
+; MIPS64-NEXT:    daddiu $sp, $sp, -16
+; MIPS64-NEXT:    sh $7, 6($sp)
+; MIPS64-NEXT:    sh $6, 4($sp)
+; MIPS64-NEXT:    sh $5, 2($sp)
+; MIPS64-NEXT:    sh $4, 0($sp)
+; MIPS64-NEXT:    ld.d $w0, 0($sp)
+; MIPS64-NEXT:    copy_s.d $2, $w0[0]
+; MIPS64-NEXT:    jr $ra
+; MIPS64-NEXT:    daddiu $sp, $sp, 16
+  %v0 = insertelement <4 x half> poison, half %a, i32 0
+  %v1 = insertelement <4 x half> %v0, half %b, i32 1
+  %v2 = insertelement <4 x half> %v1, half %c, i32 2
+  %v3 = insertelement <4 x half> %v2, half %d, i32 3
+  ret <4 x half> %v3
+}
+
+define <2 x half> @build_v2f16(half %a, half %b) nounwind {
+; MIPS32-LABEL: build_v2f16:
+; MIPS32:       # %bb.0:
+; MIPS32-NEXT:    addiu $sp, $sp, -32
+; MIPS32-NEXT:    sw $ra, 28($sp) # 4-byte Folded Spill
+; MIPS32-NEXT:    sw $fp, 24($sp) # 4-byte Folded Spill
+; MIPS32-NEXT:    move $fp, $sp
+; MIPS32-NEXT:    addiu $1, $zero, -16
+; MIPS32-NEXT:    and $sp, $sp, $1
+; MIPS32-NEXT:    sh $6, 2($sp)
+; MIPS32-NEXT:    sh $5, 0($sp)
+; MIPS32-NEXT:    ld.w $w0, 0($sp)
+; MIPS32-NEXT:    copy_s.w $1, $w0[0]
+; MIPS32-NEXT:    sw $1, 0($4)
+; MIPS32-NEXT:    move $sp, $fp
+; MIPS32-NEXT:    lw $fp, 24($sp) # 4-byte Folded Reload
+; MIPS32-NEXT:    lw $ra, 28($sp) # 4-byte Folded Reload
+; MIPS32-NEXT:    jr $ra
+; MIPS32-NEXT:    addiu $sp, $sp, 32
+;
+; MIPS64-LABEL: build_v2f16:
+; MIPS64:       # %bb.0:
+; MIPS64-NEXT:    daddiu $sp, $sp, -16
+; MIPS64-NEXT:    sh $6, 2($sp)
+; MIPS64-NEXT:    sh $5, 0($sp)
+; MIPS64-NEXT:    ld.w $w0, 0($sp)
+; MIPS64-NEXT:    copy_s.w $1, $w0[0]
+; MIPS64-NEXT:    sw $1, 0($4)
+; MIPS64-NEXT:    jr $ra
+; MIPS64-NEXT:    daddiu $sp, $sp, 16
+  %v0 = insertelement <2 x half> poison, half %a, i32 0
+  %v1 = insertelement <2 x half> %v0, half %b, i32 1
+  ret <2 x half> %v1
+}

>From b9a825dc7454ec039bdb38cb5ecd49748e7e7385 Mon Sep 17 00:00:00 2001
From: fo4025 <fo40225 at hotmail.com>
Date: Thu, 13 Aug 2026 17:37:29 +0800
Subject: [PATCH 2/2] [Mips] Fix soft-promotion of f16 BUILD_VECTOR for MSA
 targets

When compiling for MIPS with +msa and f16 vectors (v8f16, v4f16, v2f16),
LLVM would crash with "Do not know how to soft promote this operator's
operand!" because BUILD_VECTOR was missing from the SoftPromoteHalfOperand
switch statement in LegalizeFloatTypes.cpp.

On MIPS, f16 is not natively supported by default, so LLVM soft-promotes
it: stores as i16, computes as f32. Building f16 vectors requires handling
BUILD_VECTOR with soft-promoted i16 operands. This patch adds the missing
BUILD_VECTOR case and implements SoftPromoteHalfOp_BUILD_VECTOR, which:
(1) retrieves soft-promoted i16 operands via GetSoftPromotedHalf,
(2) builds a v{N}i16 integer vector,
(3) bitcasts to the original v{N}f16 type.

This matches the soft-promotion pattern used in the BITCAST handler,
where f16 storage is represented as i16 and only operations promote to f32.

Fixes compilation of Rust libcore/compiler_builtins with global +msa.

Co-Authored-By: Claude Fable 5 <noreply at anthropic.com>
---
 .../CodeGen/SelectionDAG/LegalizeFloatTypes.cpp | 17 +++++++++++++++++
 llvm/lib/CodeGen/SelectionDAG/LegalizeTypes.h   |  1 +
 2 files changed, 18 insertions(+)

diff --git a/llvm/lib/CodeGen/SelectionDAG/LegalizeFloatTypes.cpp b/llvm/lib/CodeGen/SelectionDAG/LegalizeFloatTypes.cpp
index 64af9a4757505..30c67bda6d8d2 100644
--- a/llvm/lib/CodeGen/SelectionDAG/LegalizeFloatTypes.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/LegalizeFloatTypes.cpp
@@ -2979,6 +2979,9 @@ bool DAGTypeLegalizer::SoftPromoteHalfOperand(SDNode *N, unsigned OpNo) {
                        "operand!");
 
   case ISD::BITCAST:    Res = SoftPromoteHalfOp_BITCAST(N); break;
+  case ISD::BUILD_VECTOR:
+    Res = SoftPromoteHalfOp_BUILD_VECTOR(N);
+    break;
   case ISD::FAKE_USE:
     Res = SoftPromoteHalfOp_FAKE_USE(N, OpNo);
     break;
@@ -3042,6 +3045,20 @@ SDValue DAGTypeLegalizer::SoftPromoteHalfOp_BITCAST(SDNode *N) {
   return DAG.getNode(ISD::BITCAST, SDLoc(N), N->getValueType(0), Op0);
 }
 
+SDValue DAGTypeLegalizer::SoftPromoteHalfOp_BUILD_VECTOR(SDNode *N) {
+  SDLoc dl(N);
+  EVT VT = N->getValueType(0);
+
+  SmallVector<SDValue, 8> Ops(N->getNumOperands());
+  for (unsigned i = 0, e = N->getNumOperands(); i != e; ++i)
+    Ops[i] = GetSoftPromotedHalf(N->getOperand(i));
+
+  EVT IVT =
+      EVT::getVectorVT(*DAG.getContext(), MVT::i16, VT.getVectorElementCount());
+  SDValue Res = DAG.getBuildVector(IVT, dl, Ops);
+  return DAG.getNode(ISD::BITCAST, dl, VT, Res);
+}
+
 SDValue DAGTypeLegalizer::SoftPromoteHalfOp_FAKE_USE(SDNode *N, unsigned OpNo) {
   assert(OpNo == 1 && "Only Operand 1 must need promotion here");
   SDValue Op = GetSoftPromotedHalf(N->getOperand(OpNo));
diff --git a/llvm/lib/CodeGen/SelectionDAG/LegalizeTypes.h b/llvm/lib/CodeGen/SelectionDAG/LegalizeTypes.h
index 08e51ecc86c8b..ab252f5db2dcf 100644
--- a/llvm/lib/CodeGen/SelectionDAG/LegalizeTypes.h
+++ b/llvm/lib/CodeGen/SelectionDAG/LegalizeTypes.h
@@ -808,6 +808,7 @@ class LLVM_LIBRARY_VISIBILITY DAGTypeLegalizer {
 
   bool SoftPromoteHalfOperand(SDNode *N, unsigned OpNo);
   SDValue SoftPromoteHalfOp_BITCAST(SDNode *N);
+  SDValue SoftPromoteHalfOp_BUILD_VECTOR(SDNode *N);
   SDValue SoftPromoteHalfOp_FAKE_USE(SDNode *N, unsigned OpNo);
   SDValue SoftPromoteHalfOp_FCOPYSIGN(SDNode *N, unsigned OpNo);
   SDValue SoftPromoteHalfOp_FP_EXTEND(SDNode *N);



More information about the llvm-commits mailing list