[llvm] [AMDGPU] Fix miscompile folding math calls with poison/undef vector lanes (PR #216971)
Arseniy Obolenskiy via llvm-commits
llvm-commits at lists.llvm.org
Tue Aug 18 03:03:05 PDT 2026
https://github.com/aobolensk updated https://github.com/llvm/llvm-project/pull/216971
>From 09b624829804d2fcf1b14fd58983a96a74e241c5 Mon Sep 17 00:00:00 2001
From: Arseniy Obolenskiy <arseniy.obolenskiy at amd.com>
Date: Tue, 18 Aug 2026 11:46:22 +0200
Subject: [PATCH] [AMDGPU] Fix miscompile folding math calls with poison/undef
vector lanes
A poison or undef lane made evaluateCall ConstantDataVector cast fail, and evaluateScalarMathFunc silently treated the missing operand as 0.0, producing wrong results instead of bailing out
---
llvm/lib/Target/AMDGPU/AMDGPULibCalls.cpp | 43 ++++-----
.../amdgpu-simplify-libcall-constant-fold.ll | 88 +++++++++++++++++++
2 files changed, 111 insertions(+), 20 deletions(-)
create mode 100644 llvm/test/CodeGen/AMDGPU/amdgpu-simplify-libcall-constant-fold.ll
diff --git a/llvm/lib/Target/AMDGPU/AMDGPULibCalls.cpp b/llvm/lib/Target/AMDGPU/AMDGPULibCalls.cpp
index 7291fdaca0d69..9524b8d306555 100644
--- a/llvm/lib/Target/AMDGPU/AMDGPULibCalls.cpp
+++ b/llvm/lib/Target/AMDGPU/AMDGPULibCalls.cpp
@@ -1820,23 +1820,16 @@ bool AMDGPULibCalls::fold_sincos(FPMathOperator *FPOp, IRBuilder<> &B,
bool AMDGPULibCalls::evaluateScalarMathFunc(const FuncInfo &FInfo,
APFloat &Res0, APFloat &Res1,
Constant *copr0, Constant *copr1) {
- // By default, opr0/opr1/opr3 holds values of float/double type.
- // If they are not float/double, each function has to its
- // operand separately.
- double opr0 = 0.0, opr1 = 0.0;
+ // Every function handled below reads its first operand as a floating-point
+ // value. Refuse anything else, e.g. a poison vector lane: silently treating
+ // it as 0.0 misfolds the whole call.
ConstantFP *fpopr0 = dyn_cast_or_null<ConstantFP>(copr0);
- ConstantFP *fpopr1 = dyn_cast_or_null<ConstantFP>(copr1);
- if (fpopr0) {
- opr0 = (getArgType(FInfo) == AMDGPULibFunc::F64)
- ? fpopr0->getValueAPF().convertToDouble()
- : (double)fpopr0->getValueAPF().convertToFloat();
- }
+ if (!fpopr0)
+ return false;
- if (fpopr1) {
- opr1 = (getArgType(FInfo) == AMDGPULibFunc::F64)
- ? fpopr1->getValueAPF().convertToDouble()
- : (double)fpopr1->getValueAPF().convertToFloat();
- }
+ double opr0 = (getArgType(FInfo) == AMDGPULibFunc::F64)
+ ? fpopr0->getValueAPF().convertToDouble()
+ : (double)fpopr0->getValueAPF().convertToFloat();
switch (FInfo.getId()) {
default:
@@ -1952,9 +1945,16 @@ bool AMDGPULibCalls::evaluateScalarMathFunc(const FuncInfo &FInfo,
// two-arg functions
case AMDGPULibFunc::EI_POW:
- case AMDGPULibFunc::EI_POWR:
+ case AMDGPULibFunc::EI_POWR: {
+ ConstantFP *fpopr1 = dyn_cast_or_null<ConstantFP>(copr1);
+ if (!fpopr1)
+ return false;
+ double opr1 = (getArgType(FInfo) == AMDGPULibFunc::F64)
+ ? fpopr1->getValueAPF().convertToDouble()
+ : (double)fpopr1->getValueAPF().convertToFloat();
Res0 = APFloat{pow(opr0, opr1)};
return true;
+ }
case AMDGPULibFunc::EI_POWN: {
if (ConstantInt *iopr1 = dyn_cast_or_null<ConstantInt>(copr1)) {
@@ -2014,11 +2014,14 @@ bool AMDGPULibCalls::evaluateCall(CallInst *aCI, const FuncInfo &FInfo) {
return false;
}
} else {
- ConstantDataVector *CDV0 = dyn_cast_or_null<ConstantDataVector>(copr0);
- ConstantDataVector *CDV1 = dyn_cast_or_null<ConstantDataVector>(copr1);
+ // An operand of a vector variant is not necessarily a vector: sincos takes
+ // a pointer as its second operand, and fmin/fmax/ldexp accept an
+ // implicitly splatted scalar. Only index into actual vectors.
+ Constant *CV0 = copr0 && copr0->getType()->isVectorTy() ? copr0 : nullptr;
+ Constant *CV1 = copr1 && copr1->getType()->isVectorTy() ? copr1 : nullptr;
for (int i = 0; i < FuncVecSize; ++i) {
- Constant *celt0 = CDV0 ? CDV0->getElementAsConstant(i) : nullptr;
- Constant *celt1 = CDV1 ? CDV1->getElementAsConstant(i) : nullptr;
+ Constant *celt0 = CV0 ? CV0->getAggregateElement((unsigned)i) : nullptr;
+ Constant *celt1 = CV1 ? CV1->getAggregateElement((unsigned)i) : nullptr;
if (!evaluateScalarMathFunc(FInfo, Val0.emplace_back(0.0),
Val1.emplace_back(0.0), celt0, celt1)) {
return false;
diff --git a/llvm/test/CodeGen/AMDGPU/amdgpu-simplify-libcall-constant-fold.ll b/llvm/test/CodeGen/AMDGPU/amdgpu-simplify-libcall-constant-fold.ll
new file mode 100644
index 0000000000000..468c11f95e9a6
--- /dev/null
+++ b/llvm/test/CodeGen/AMDGPU/amdgpu-simplify-libcall-constant-fold.ll
@@ -0,0 +1,88 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
+; RUN: opt -S -mtriple=amdgpu-amd-amdhsa -passes=amdgpu-simplifylib %s | FileCheck %s
+
+; Constant folding of fast-math library calls (evaluateCall).
+
+declare float @_Z3sinf(float)
+declare <2 x float> @_Z3cosDv2_f(<2 x float>)
+declare <2 x float> @_Z3powDv2_fS_(<2 x float>, <2 x float>)
+declare <2 x float> @_Z4fminDv2_ff(<2 x float>, float)
+declare <2 x float> @_Z4fminDv2_fS_(float, <2 x float>)
+declare <2 x float> @_Z6sincosDv2_fPU3AS5S_(<2 x float>, ptr addrspace(5))
+
+define <2 x float> @fold_cos_v2f32() {
+; CHECK-LABEL: define <2 x float> @fold_cos_v2f32() {
+; CHECK-NEXT: ret <2 x float> <float f0xBF7D7026, float f0xBF275530>
+;
+ %r = call fast <2 x float> @_Z3cosDv2_f(<2 x float> <float 3.0, float 4.0>)
+ ret <2 x float> %r
+}
+
+define <2 x float> @fold_cos_v2f32_zeroinit() {
+; CHECK-LABEL: define <2 x float> @fold_cos_v2f32_zeroinit() {
+; CHECK-NEXT: ret <2 x float> splat (float 1.000000e+00)
+;
+ %r = call fast <2 x float> @_Z3cosDv2_f(<2 x float> zeroinitializer)
+ ret <2 x float> %r
+}
+
+; A poison lane must not turn the defined lanes into an evaluation at 0.0;
+; cos(3.0) is about -0.99, not cos(0.0) == 1.0.
+define <2 x float> @no_fold_cos_v2f32_poison_lane() {
+; CHECK-LABEL: define <2 x float> @no_fold_cos_v2f32_poison_lane() {
+; CHECK-NEXT: [[R:%.*]] = call fast <2 x float> @_Z3cosDv2_f(<2 x float> <float 3.000000e+00, float poison>)
+; CHECK-NEXT: ret <2 x float> [[R]]
+;
+ %r = call fast <2 x float> @_Z3cosDv2_f(<2 x float> <float 3.0, float poison>)
+ ret <2 x float> %r
+}
+
+; Likewise for the second operand: a poison exponent lane must not be
+; evaluated as pow(x, 0.0) == 1.0. (The defined lanes are still folded, by the
+; pow(x, 2) => x*x expansion.)
+define <2 x float> @pow_v2f32_poison_exp_lane() {
+; CHECK-LABEL: define <2 x float> @pow_v2f32_poison_exp_lane() {
+; CHECK-NEXT: ret <2 x float> <float 9.000000e+00, float 1.600000e+01>
+;
+ %r = call fast <2 x float> @_Z3powDv2_fS_(<2 x float> <float 3.0, float 4.0>, <2 x float> <float 2.0, float poison>)
+ ret <2 x float> %r
+}
+
+define float @no_fold_sin_f32_poison() {
+; CHECK-LABEL: define float @no_fold_sin_f32_poison() {
+; CHECK-NEXT: [[R:%.*]] = call fast float @_Z3sinf(float poison)
+; CHECK-NEXT: ret float [[R]]
+;
+ %r = call fast float @_Z3sinf(float poison)
+ ret float %r
+}
+
+; fmin/fmax/ldexp signatures accept an implicitly splatted scalar operand, and
+; sincos takes a pointer; the vector evaluation loop must not index into them.
+define <2 x float> @fmin_v2f32_scalar_second_arg() {
+; CHECK-LABEL: define <2 x float> @fmin_v2f32_scalar_second_arg() {
+; CHECK-NEXT: [[R:%.*]] = call fast <2 x float> @llvm.minnum.v2f32(<2 x float> <float 1.000000e+00, float 2.000000e+00>, <2 x float> splat (float 1.500000e+00))
+; CHECK-NEXT: ret <2 x float> [[R]]
+;
+ %r = call fast <2 x float> @_Z4fminDv2_ff(<2 x float> <float 1.0, float 2.0>, float 1.5)
+ ret <2 x float> %r
+}
+
+define <2 x float> @fmin_v2f32_scalar_first_arg() {
+; CHECK-LABEL: define <2 x float> @fmin_v2f32_scalar_first_arg() {
+; CHECK-NEXT: [[R:%.*]] = call fast <2 x float> @llvm.minnum.v2f32(<2 x float> splat (float 1.500000e+00), <2 x float> <float 1.000000e+00, float 2.000000e+00>)
+; CHECK-NEXT: ret <2 x float> [[R]]
+;
+ %r = call fast <2 x float> @_Z4fminDv2_fS_(float 1.5, <2 x float> <float 1.0, float 2.0>)
+ ret <2 x float> %r
+}
+
+define <2 x float> @sincos_v2f32(ptr addrspace(5) %p) {
+; CHECK-LABEL: define <2 x float> @sincos_v2f32(
+; CHECK-SAME: ptr addrspace(5) [[P:%.*]]) {
+; CHECK-NEXT: store <2 x float> <float f0x3F0A5140, float f0xBED51133>, ptr addrspace(5) [[P]], align 8
+; CHECK-NEXT: ret <2 x float> <float f0x3F576AA4, float f0x3F68C7B7>
+;
+ %r = call fast <2 x float> @_Z6sincosDv2_fPU3AS5S_(<2 x float> <float 1.0, float 2.0>, ptr addrspace(5) %p)
+ ret <2 x float> %r
+}
More information about the llvm-commits
mailing list