Skip to content

[SimplifyLibCalls] Constant fold nan libcall #101459

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Aug 1, 2024

Conversation

dtcxzyw
Copy link
Member

@dtcxzyw dtcxzyw commented Aug 1, 2024

Reference: https://en.cppreference.com/w/c/numeric/math/nan
The logic is copied from clang frontend:

static bool TryEvaluateBuiltinNaN(const ASTContext &Context,
QualType ResultTy,
const Expr *Arg,
bool SNaN,
llvm::APFloat &Result) {
const StringLiteral *S = dyn_cast<StringLiteral>(Arg->IgnoreParenCasts());
if (!S) return false;
const llvm::fltSemantics &Sem = Context.getFloatTypeSemantics(ResultTy);
llvm::APInt fill;
// Treat empty strings as if they were zero.
if (S->getString().empty())
fill = llvm::APInt(32, 0);
else if (S->getString().getAsInteger(0, fill))
return false;
if (Context.getTargetInfo().isNan2008()) {
if (SNaN)
Result = llvm::APFloat::getSNaN(Sem, false, &fill);
else
Result = llvm::APFloat::getQNaN(Sem, false, &fill);
} else {
// Prior to IEEE 754-2008, architectures were allowed to choose whether
// the first bit of their significand was set for qNaN or sNaN. MIPS chose
// a different encoding to what became a standard in 2008, and for pre-
// 2008 revisions, MIPS interpreted sNaN-2008 as qNan and qNaN-2008 as
// sNaN. This is now known as "legacy NaN" encoding.
if (SNaN)
Result = llvm::APFloat::getQNaN(Sem, false, &fill);
else
Result = llvm::APFloat::getSNaN(Sem, false, &fill);
}
return true;
}

@llvmbot
Copy link
Member

llvmbot commented Aug 1, 2024

@llvm/pr-subscribers-llvm-transforms

Author: Yingwei Zheng (dtcxzyw)

Changes

Reference: https://en.cppreference.com/w/c/numeric/math/nan
The logic is copied from clang frontend:

static bool TryEvaluateBuiltinNaN(const ASTContext &Context,
QualType ResultTy,
const Expr *Arg,
bool SNaN,
llvm::APFloat &Result) {
const StringLiteral *S = dyn_cast<StringLiteral>(Arg->IgnoreParenCasts());
if (!S) return false;
const llvm::fltSemantics &Sem = Context.getFloatTypeSemantics(ResultTy);
llvm::APInt fill;
// Treat empty strings as if they were zero.
if (S->getString().empty())
fill = llvm::APInt(32, 0);
else if (S->getString().getAsInteger(0, fill))
return false;
if (Context.getTargetInfo().isNan2008()) {
if (SNaN)
Result = llvm::APFloat::getSNaN(Sem, false, &fill);
else
Result = llvm::APFloat::getQNaN(Sem, false, &fill);
} else {
// Prior to IEEE 754-2008, architectures were allowed to choose whether
// the first bit of their significand was set for qNaN or sNaN. MIPS chose
// a different encoding to what became a standard in 2008, and for pre-
// 2008 revisions, MIPS interpreted sNaN-2008 as qNan and qNaN-2008 as
// sNaN. This is now known as "legacy NaN" encoding.
if (SNaN)
Result = llvm::APFloat::getQNaN(Sem, false, &fill);
else
Result = llvm::APFloat::getSNaN(Sem, false, &fill);
}
return true;
}


Full diff: https://github.com/llvm/llvm-project/pull/101459.diff

5 Files Affected:

  • (modified) llvm/lib/Transforms/Utils/SimplifyLibCalls.cpp (+20)
  • (added) llvm/test/Transforms/InstCombine/nan.ll (+41)
  • (added) llvm/test/Transforms/InstCombine/nanl-fp128.ll (+32)
  • (added) llvm/test/Transforms/InstCombine/nanl-fp80.ll (+32)
  • (added) llvm/test/Transforms/InstCombine/nanl-ppc-fp128.ll (+32)
diff --git a/llvm/lib/Transforms/Utils/SimplifyLibCalls.cpp b/llvm/lib/Transforms/Utils/SimplifyLibCalls.cpp
index 4100471eaaa1d..948e510be5c32 100644
--- a/llvm/lib/Transforms/Utils/SimplifyLibCalls.cpp
+++ b/llvm/lib/Transforms/Utils/SimplifyLibCalls.cpp
@@ -3858,6 +3858,22 @@ Value *LibCallSimplifier::optimizeStringMemoryLibCall(CallInst *CI,
   return nullptr;
 }
 
+/// Constant folding nan/nanf/nanl.
+static Value *optimizeNaN(CallInst *CI) {
+  StringRef CharSeq;
+  if (!getConstantStringInfo(CI->getArgOperand(0), CharSeq))
+    return nullptr;
+
+  llvm::APInt Fill;
+  // Treat empty strings as if they were zero.
+  if (CharSeq.empty())
+    Fill = llvm::APInt(32, 0);
+  else if (CharSeq.getAsInteger(0, Fill))
+    return nullptr;
+
+  return ConstantFP::getQNaN(CI->getType(), /*Negative=*/false, &Fill);
+}
+
 Value *LibCallSimplifier::optimizeFloatingPointLibCall(CallInst *CI,
                                                        LibFunc Func,
                                                        IRBuilderBase &Builder) {
@@ -3972,6 +3988,10 @@ Value *LibCallSimplifier::optimizeFloatingPointLibCall(CallInst *CI,
   case LibFunc_remquof:
   case LibFunc_remquol:
     return optimizeRemquo(CI, Builder);
+  case LibFunc_nan:
+  case LibFunc_nanf:
+  case LibFunc_nanl:
+    return optimizeNaN(CI);
   default:
     return nullptr;
   }
diff --git a/llvm/test/Transforms/InstCombine/nan.ll b/llvm/test/Transforms/InstCombine/nan.ll
new file mode 100644
index 0000000000000..fd4119bc0c06a
--- /dev/null
+++ b/llvm/test/Transforms/InstCombine/nan.ll
@@ -0,0 +1,41 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5
+; RUN: opt < %s -passes=instcombine -S | FileCheck %s
+
+@empty = private unnamed_addr constant [1 x i8] zeroinitializer, align 1
+@dec = private unnamed_addr constant [2 x i8] c"1\00", align 1
+@hex = private unnamed_addr constant [4 x i8] c"0xf\00", align 1
+
+define double @nan_empty() {
+; CHECK-LABEL: define double @nan_empty() {
+; CHECK-NEXT:    ret double 0x7FF8000000000000
+;
+  %res = call double @nan(ptr @empty)
+  ret double %res
+}
+
+define double @nan_dec() {
+; CHECK-LABEL: define double @nan_dec() {
+; CHECK-NEXT:    ret double 0x7FF8000000000001
+;
+  %res = call double @nan(ptr @dec)
+  ret double %res
+}
+
+define double @nan_hex() {
+; CHECK-LABEL: define double @nan_hex() {
+; CHECK-NEXT:    ret double 0x7FF800000000000F
+;
+  %res = call double @nan(ptr @hex)
+  ret double %res
+}
+
+define float @nanf_empty() {
+; CHECK-LABEL: define float @nanf_empty() {
+; CHECK-NEXT:    ret float 0x7FF8000000000000
+;
+  %res = call float @nanf(ptr @empty)
+  ret float %res
+}
+
+declare float @nanf(ptr)
+declare double @nan(ptr)
diff --git a/llvm/test/Transforms/InstCombine/nanl-fp128.ll b/llvm/test/Transforms/InstCombine/nanl-fp128.ll
new file mode 100644
index 0000000000000..21ba0fb14ca20
--- /dev/null
+++ b/llvm/test/Transforms/InstCombine/nanl-fp128.ll
@@ -0,0 +1,32 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5
+; RUN: opt < %s -passes=instcombine -S | FileCheck %s
+
+@empty = private unnamed_addr constant [1 x i8] zeroinitializer, align 1
+@dec = private unnamed_addr constant [2 x i8] c"1\00", align 1
+@hex = private unnamed_addr constant [4 x i8] c"0xf\00", align 1
+
+define fp128 @nanl_empty() {
+; CHECK-LABEL: define fp128 @nanl_empty() {
+; CHECK-NEXT:    ret fp128 0xL00000000000000007FFF800000000000
+;
+  %res = call fp128 @nanl(ptr @empty)
+  ret fp128 %res
+}
+
+define fp128 @nanl_dec() {
+; CHECK-LABEL: define fp128 @nanl_dec() {
+; CHECK-NEXT:    ret fp128 0xL00000000000000017FFF800000000000
+;
+  %res = call fp128 @nanl(ptr @dec)
+  ret fp128 %res
+}
+
+define fp128 @nanl_hex() {
+; CHECK-LABEL: define fp128 @nanl_hex() {
+; CHECK-NEXT:    ret fp128 0xL000000000000000F7FFF800000000000
+;
+  %res = call fp128 @nanl(ptr @hex)
+  ret fp128 %res
+}
+
+declare fp128 @nanl(ptr)
diff --git a/llvm/test/Transforms/InstCombine/nanl-fp80.ll b/llvm/test/Transforms/InstCombine/nanl-fp80.ll
new file mode 100644
index 0000000000000..7868af3696a56
--- /dev/null
+++ b/llvm/test/Transforms/InstCombine/nanl-fp80.ll
@@ -0,0 +1,32 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5
+; RUN: opt < %s -passes=instcombine -S | FileCheck %s
+
+@empty = private unnamed_addr constant [1 x i8] zeroinitializer, align 1
+@dec = private unnamed_addr constant [2 x i8] c"1\00", align 1
+@hex = private unnamed_addr constant [4 x i8] c"0xf\00", align 1
+
+define x86_fp80 @nanl_empty() {
+; CHECK-LABEL: define x86_fp80 @nanl_empty() {
+; CHECK-NEXT:    ret x86_fp80 0xK7FFFC000000000000000
+;
+  %res = call x86_fp80 @nanl(ptr @empty)
+  ret x86_fp80 %res
+}
+
+define x86_fp80 @nanl_dec() {
+; CHECK-LABEL: define x86_fp80 @nanl_dec() {
+; CHECK-NEXT:    ret x86_fp80 0xK7FFFC000000000000001
+;
+  %res = call x86_fp80 @nanl(ptr @dec)
+  ret x86_fp80 %res
+}
+
+define x86_fp80 @nanl_hex() {
+; CHECK-LABEL: define x86_fp80 @nanl_hex() {
+; CHECK-NEXT:    ret x86_fp80 0xK7FFFC00000000000000F
+;
+  %res = call x86_fp80 @nanl(ptr @hex)
+  ret x86_fp80 %res
+}
+
+declare x86_fp80 @nanl(ptr)
diff --git a/llvm/test/Transforms/InstCombine/nanl-ppc-fp128.ll b/llvm/test/Transforms/InstCombine/nanl-ppc-fp128.ll
new file mode 100644
index 0000000000000..7f60a379c4885
--- /dev/null
+++ b/llvm/test/Transforms/InstCombine/nanl-ppc-fp128.ll
@@ -0,0 +1,32 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5
+; RUN: opt < %s -passes=instcombine -S | FileCheck %s
+
+@empty = private unnamed_addr constant [1 x i8] zeroinitializer, align 1
+@dec = private unnamed_addr constant [2 x i8] c"1\00", align 1
+@hex = private unnamed_addr constant [4 x i8] c"0xf\00", align 1
+
+define ppc_fp128 @nanl_empty() {
+; CHECK-LABEL: define ppc_fp128 @nanl_empty() {
+; CHECK-NEXT:    ret ppc_fp128 0xM7FF80000000000000000000000000000
+;
+  %res = call ppc_fp128 @nanl(ptr @empty)
+  ret ppc_fp128 %res
+}
+
+define ppc_fp128 @nanl_dec() {
+; CHECK-LABEL: define ppc_fp128 @nanl_dec() {
+; CHECK-NEXT:    ret ppc_fp128 0xM7FF80000000000010000000000000000
+;
+  %res = call ppc_fp128 @nanl(ptr @dec)
+  ret ppc_fp128 %res
+}
+
+define ppc_fp128 @nanl_hex() {
+; CHECK-LABEL: define ppc_fp128 @nanl_hex() {
+; CHECK-NEXT:    ret ppc_fp128 0xM7FF800000000000F0000000000000000
+;
+  %res = call ppc_fp128 @nanl(ptr @hex)
+  ret ppc_fp128 %res
+}
+
+declare ppc_fp128 @nanl(ptr)

dtcxzyw added a commit to dtcxzyw/llvm-opt-benchmark that referenced this pull request Aug 1, 2024
Copy link
Contributor

@arsenm arsenm left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unrelated, but in the attribute inference you could mark these returns as nofpclass(~nan)

@dtcxzyw dtcxzyw merged commit 4f42deb into llvm:main Aug 1, 2024
7 checks passed
@dtcxzyw dtcxzyw deleted the perf/simplify-nan branch August 1, 2024 09:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants