[llvm] [SelectionDAG] Sign-extend sub-int FPOWI/FLDEXP exponent instead of asserting (PR #200621)
Justin Lebar via llvm-commits
llvm-commits at lists.llvm.org
Sun May 31 21:57:42 PDT 2026
https://github.com/jlebar updated https://github.com/llvm/llvm-project/pull/200621
>From 933a8ca16426a28b621c9852ac77044fe6050567 Mon Sep 17 00:00:00 2001
From: Justin Lebar <justin.lebar at gmail.com>
Date: Sat, 30 May 2026 13:55:20 -0700
Subject: [PATCH] [SelectionDAG] Don't assert on an illegal FPOWI/FLDEXP
exponent
PromoteIntOp_ExpOp is reached precisely when the exponent type is
illegal -- typically narrower than int. It asserted the exponent width
already equalled sizeof(int), crashing +asserts builds on that common
case (e.g. @llvm.ldexp.f64.i16). But makeLibCall already sign-extends a
narrower-than-int exponent to int, so release builds lowered it
correctly; only the assert was wrong.
A wider-than-int exponent (e.g. @llvm.ldexp.f64.i48, where i48 is illegal
and promoted through this path) can't be passed in an int, so emit an
error and return poison -- matching the soften/expand/LegalizeDAG paths
-- instead of silently truncating.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply at anthropic.com>
---
.../SelectionDAG/LegalizeIntegerTypes.cpp | 17 ++++--
llvm/test/CodeGen/AArch64/ldexp.ll | 59 +++++++++++++++++++
.../powi-ldexp-promote-libcall-error.ll | 24 ++++++++
3 files changed, 96 insertions(+), 4 deletions(-)
create mode 100644 llvm/test/CodeGen/AArch64/powi-ldexp-promote-libcall-error.ll
diff --git a/llvm/lib/CodeGen/SelectionDAG/LegalizeIntegerTypes.cpp b/llvm/lib/CodeGen/SelectionDAG/LegalizeIntegerTypes.cpp
index 86617b6dd33de..17922272a3b19 100644
--- a/llvm/lib/CodeGen/SelectionDAG/LegalizeIntegerTypes.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/LegalizeIntegerTypes.cpp
@@ -2821,10 +2821,19 @@ SDValue DAGTypeLegalizer::PromoteIntOp_ExpOp(SDNode *N) {
// we rewrite to a libcall here directly, letting makeLibCall handle promotion
// if the target accepts it according to shouldSignExtendTypeInLibCall.
- // The exponent should fit in a sizeof(int) type for the libcall to be valid.
- assert(DAG.getLibInfo().getIntSize() ==
- N->getOperand(1 + OpOffset).getValueType().getSizeInBits() &&
- "POWI exponent should match with sizeof(int) when doing the libcall.");
+ // A wider-than-int exponent can't be passed in an int (there's no wider
+ // libcall), so bail like the soften/expand paths. A narrower one is
+ // sign-extended to int by the makeLibCall below.
+ if (N->getOperand(1 + OpOffset).getScalarValueSizeInBits() >
+ DAG.getLibInfo().getIntSize()) {
+ DAG.getContext()->emitError(
+ "powi/ldexp exponent does not match sizeof(int)");
+ if (IsStrict)
+ ReplaceValueWith(SDValue(N, 1), Chain);
+ ReplaceValueWith(SDValue(N, 0), DAG.getPOISON(N->getValueType(0)));
+ return SDValue();
+ }
+
TargetLowering::MakeLibCallOptions CallOptions;
CallOptions.setIsSigned(true);
SDValue Ops[2] = {N->getOperand(0 + OpOffset), N->getOperand(1 + OpOffset)};
diff --git a/llvm/test/CodeGen/AArch64/ldexp.ll b/llvm/test/CodeGen/AArch64/ldexp.ll
index 308e861abb6d4..51e85cb82337f 100644
--- a/llvm/test/CodeGen/AArch64/ldexp.ll
+++ b/llvm/test/CodeGen/AArch64/ldexp.ll
@@ -56,6 +56,65 @@ entry:
ret double %call
}
+; A sub-int exponent must be sign-extended to int on the libcall path;
+; PromoteIntOp_ExpOp must not crash on it.
+define double @testExpIntrinsic_i16(double %val, i16 %a) {
+; SVELINUX-LABEL: testExpIntrinsic_i16:
+; SVELINUX: // %bb.0: // %entry
+; SVELINUX-NEXT: str x30, [sp, #-16]! // 8-byte Folded Spill
+; SVELINUX-NEXT: .cfi_def_cfa_offset 16
+; SVELINUX-NEXT: .cfi_offset w30, -16
+; SVELINUX-NEXT: sxth w0, w0
+; SVELINUX-NEXT: bl ldexp
+; SVELINUX-NEXT: ldr x30, [sp], #16 // 8-byte Folded Reload
+; SVELINUX-NEXT: ret
+;
+; GISEL-LABEL: testExpIntrinsic_i16:
+; GISEL: // %bb.0: // %entry
+; GISEL-NEXT: str x30, [sp, #-16]! // 8-byte Folded Spill
+; GISEL-NEXT: .cfi_def_cfa_offset 16
+; GISEL-NEXT: .cfi_offset w30, -16
+; GISEL-NEXT: sxth w0, w0
+; GISEL-NEXT: bl ldexp
+; GISEL-NEXT: ldr x30, [sp], #16 // 8-byte Folded Reload
+; GISEL-NEXT: ret
+;
+; SVEWINDOWS-LABEL: testExpIntrinsic_i16:
+; SVEWINDOWS: .seh_proc testExpIntrinsic_i16
+; SVEWINDOWS-NEXT: // %bb.0: // %entry
+; SVEWINDOWS-NEXT: str x30, [sp, #-16]! // 8-byte Folded Spill
+; SVEWINDOWS-NEXT: .seh_save_reg_x x30, 16
+; SVEWINDOWS-NEXT: .seh_endprologue
+; SVEWINDOWS-NEXT: sxth w0, w0
+; SVEWINDOWS-NEXT: bl ldexp
+; SVEWINDOWS-NEXT: .seh_startepilogue
+; SVEWINDOWS-NEXT: ldr x30, [sp], #16 // 8-byte Folded Reload
+; SVEWINDOWS-NEXT: .seh_save_reg_x x30, 16
+; SVEWINDOWS-NEXT: .seh_endepilogue
+; SVEWINDOWS-NEXT: ret
+; SVEWINDOWS-NEXT: .seh_endfunclet
+; SVEWINDOWS-NEXT: .seh_endproc
+;
+; WINDOWS-LABEL: testExpIntrinsic_i16:
+; WINDOWS: .seh_proc testExpIntrinsic_i16
+; WINDOWS-NEXT: // %bb.0: // %entry
+; WINDOWS-NEXT: str x30, [sp, #-16]! // 8-byte Folded Spill
+; WINDOWS-NEXT: .seh_save_reg_x x30, 16
+; WINDOWS-NEXT: .seh_endprologue
+; WINDOWS-NEXT: sxth w0, w0
+; WINDOWS-NEXT: bl ldexp
+; WINDOWS-NEXT: .seh_startepilogue
+; WINDOWS-NEXT: ldr x30, [sp], #16 // 8-byte Folded Reload
+; WINDOWS-NEXT: .seh_save_reg_x x30, 16
+; WINDOWS-NEXT: .seh_endepilogue
+; WINDOWS-NEXT: ret
+; WINDOWS-NEXT: .seh_endfunclet
+; WINDOWS-NEXT: .seh_endproc
+entry:
+ %call = tail call fast double @llvm.ldexp.f64.i16(double %val, i16 %a)
+ ret double %call
+}
+
define float @testExpf(float %val, i32 %a) {
; SVELINUX-LABEL: testExpf:
; SVELINUX: // %bb.0: // %entry
diff --git a/llvm/test/CodeGen/AArch64/powi-ldexp-promote-libcall-error.ll b/llvm/test/CodeGen/AArch64/powi-ldexp-promote-libcall-error.ll
new file mode 100644
index 0000000000000..14830bdddcaa5
--- /dev/null
+++ b/llvm/test/CodeGen/AArch64/powi-ldexp-promote-libcall-error.ll
@@ -0,0 +1,24 @@
+; RUN: not llc -mtriple=aarch64-linux-gnu -filetype=null %s 2>&1 | FileCheck %s
+
+; A powi/ldexp exponent wider than sizeof(int) can't be passed to the libcall,
+; so PromoteIntOp_ExpOp must emit a clean error rather than crash (assertions)
+; or silently truncate. i48 is illegal and promoted through that path here; i64
+; would be legal and i128 expanded, so neither would reach it.
+
+; CHECK: error: powi/ldexp exponent does not match sizeof(int)
+define double @ldexp_f64_i48(double %val, i48 %a) {
+ %call = call double @llvm.ldexp.f64.i48(double %val, i48 %a)
+ ret double %call
+}
+
+; CHECK: error: powi/ldexp exponent does not match sizeof(int)
+define double @powi_f64_i48(double %val, i48 %a) {
+ %call = call double @llvm.powi.f64.i48(double %val, i48 %a)
+ ret double %call
+}
+
+; CHECK: error: powi/ldexp exponent does not match sizeof(int)
+define double @ldexp_f64_i48_strictfp(double %val, i48 %a) strictfp {
+ %call = call double @llvm.experimental.constrained.ldexp.f64.i48(double %val, i48 %a, metadata !"round.tonearest", metadata !"fpexcept.strict")
+ ret double %call
+}
More information about the llvm-commits
mailing list