Skip to content

[clang] constexpr built-in abs function. #112539

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 13 commits into from
Oct 18, 2024
Merged

[clang] constexpr built-in abs function. #112539

merged 13 commits into from
Oct 18, 2024

Conversation

c8ef
Copy link
Contributor

@c8ef c8ef commented Oct 16, 2024

According to P0533R9, the C++ standard library functions corresponding to the C macros in [c.math.abs] are now constexpr.

To implement this feature in libc++, we must make the built-in abs function constexpr. This patch adds the implementation of a constexpr abs function for the current constant evaluator and the new bytecode interpreter.

It is important to note that in 2's complement systems, the absolute value of the most negative value is out of range. In gcc, it will result in an out-of-range error and will not be evaluated as constants. We follow the same approach here.

@c8ef c8ef changed the title Draft [clang] constexpr built-in abs function. Oct 16, 2024
@c8ef c8ef marked this pull request as ready for review October 16, 2024 14:35
@llvmbot llvmbot added clang Clang issues not falling into any other category clang:frontend Language frontend issues, e.g. anything involving "Sema" labels Oct 16, 2024
@llvmbot
Copy link
Member

llvmbot commented Oct 16, 2024

@llvm/pr-subscribers-clang

Author: None (c8ef)

Changes

According to P0533R9, the C++ standard library functions corresponding to the C macros in [c.math.abs] are now constexpr.

To implement this feature in libc++, we must make the built-in abs function constexpr. This patch adds the implementation of a constexpr abs function for the current constant evaluator and the new bytecode interpreter.

It is important to note that in 2's complement systems, the absolute value of the most negative value is out of range. In gcc, it will result in an out-of-range error and will not be evaluated as constants. We follow the same approach here.


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

6 Files Affected:

  • (modified) clang/include/clang/Basic/Builtins.td (+1)
  • (modified) clang/lib/AST/ByteCode/InterpBuiltin.cpp (+20)
  • (modified) clang/lib/AST/ExprConstant.cpp (+13)
  • (modified) clang/test/AST/ByteCode/builtin-functions.cpp (+9)
  • (modified) clang/test/CodeGenCXX/builtins.cpp (+6-8)
  • (modified) clang/test/Sema/constant-builtins-2.c (+12-1)
diff --git a/clang/include/clang/Basic/Builtins.td b/clang/include/clang/Basic/Builtins.td
index bda8a48be92bda..e1b4d5b1fdc0a5 100644
--- a/clang/include/clang/Basic/Builtins.td
+++ b/clang/include/clang/Basic/Builtins.td
@@ -2714,6 +2714,7 @@ def Abs : IntMathTemplate, LibBuiltin<"stdlib.h"> {
   let Attributes = [NoThrow, Const];
   let Prototype = "T(T)";
   let AddBuiltinPrefixedAlias = 1;
+  let OnlyBuiltinPrefixedAliasIsConstexpr = 1;
 }
 
 def Calloc : LibBuiltin<"stdlib.h"> {
diff --git a/clang/lib/AST/ByteCode/InterpBuiltin.cpp b/clang/lib/AST/ByteCode/InterpBuiltin.cpp
index 65c7b4e5306d72..b4ab744ea14104 100644
--- a/clang/lib/AST/ByteCode/InterpBuiltin.cpp
+++ b/clang/lib/AST/ByteCode/InterpBuiltin.cpp
@@ -563,6 +563,19 @@ static bool interp__builtin_fabs(InterpState &S, CodePtr OpPC,
   return true;
 }
 
+static bool interp__builtin_abs(InterpState &S, CodePtr OpPC,
+                                const InterpFrame *Frame, const Function *Func,
+                                const CallExpr *Call) {
+  PrimType ArgT = *S.getContext().classify(Call->getArg(0)->getType());
+  APSInt Val = peekToAPSInt(S.Stk, ArgT);
+  if (Val == APSInt(APSInt::getSignedMinValue(Val.getBitWidth()), false))
+    return false;
+  if (Val.isNegative())
+    Val.negate();
+  pushInteger(S, Val, Call->getType());
+  return true;
+}
+
 static bool interp__builtin_popcount(InterpState &S, CodePtr OpPC,
                                      const InterpFrame *Frame,
                                      const Function *Func,
@@ -1808,6 +1821,13 @@ bool InterpretBuiltin(InterpState &S, CodePtr OpPC, const Function *F,
       return false;
     break;
 
+  case Builtin::BI__builtin_abs:
+  case Builtin::BI__builtin_labs:
+  case Builtin::BI__builtin_llabs:
+    if (!interp__builtin_abs(S, OpPC, Frame, F, Call))
+      return false;
+    break;
+
   case Builtin::BI__builtin_popcount:
   case Builtin::BI__builtin_popcountl:
   case Builtin::BI__builtin_popcountll:
diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index 52a7f5778ce6d2..d1d6790e152527 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -13055,6 +13055,19 @@ bool IntExprEvaluator::VisitBuiltinCallExpr(const CallExpr *E,
     return Success(Val.popcount() % 2, E);
   }
 
+  case Builtin::BI__builtin_abs:
+  case Builtin::BI__builtin_labs:
+  case Builtin::BI__builtin_llabs: {
+    APSInt Val;
+    if (!EvaluateInteger(E->getArg(0), Val, Info))
+      return false;
+    if (Val == APSInt(APSInt::getSignedMinValue(Val.getBitWidth()), false))
+      return false;
+    if (Val.isNegative())
+      Val.negate();
+    return Success(Val, E);
+  }
+
   case Builtin::BI__builtin_popcount:
   case Builtin::BI__builtin_popcountl:
   case Builtin::BI__builtin_popcountll:
diff --git a/clang/test/AST/ByteCode/builtin-functions.cpp b/clang/test/AST/ByteCode/builtin-functions.cpp
index 450ff5671314db..1ec9b5fdcbd228 100644
--- a/clang/test/AST/ByteCode/builtin-functions.cpp
+++ b/clang/test/AST/ByteCode/builtin-functions.cpp
@@ -265,6 +265,15 @@ namespace fpclassify {
   char classify_subnorm [__builtin_fpclassify(-1, -1, -1, +1, -1, 1.0e-38f)];
 }
 
+namespace abs {
+static_assert(__builtin_abs(14) == 14, "");
+static_assert(__builtin_labs(14L) == 14L, "");
+static_assert(__builtin_llabs(14LL) == 14LL, "");
+static_assert(__builtin_abs(-14) == 14, "");
+static_assert(__builtin_labs(-0x14L) == 0x14L, "");
+static_assert(__builtin_llabs(-0x141414141414LL) == 0x141414141414LL, "");
+} // namespace abs
+
 namespace fabs {
   static_assert(__builtin_fabs(-14.0) == 14.0, "");
 }
diff --git a/clang/test/CodeGenCXX/builtins.cpp b/clang/test/CodeGenCXX/builtins.cpp
index 90265186fb3d8c..37f9491d12d04b 100644
--- a/clang/test/CodeGenCXX/builtins.cpp
+++ b/clang/test/CodeGenCXX/builtins.cpp
@@ -14,6 +14,12 @@ int o = X::__builtin_fabs(-2.0);
 long p = X::__builtin_fabsf(-3.0f);
 // CHECK: @p ={{.*}} global i64 3, align 8
 
+int x = __builtin_abs(-2);
+// CHECK: @x ={{.*}} global i32 2, align 4
+
+long y = __builtin_abs(-2l);
+// CHECK: @y ={{.*}} global i64 2, align 8
+
 // PR8839
 extern "C" char memmove();
 
@@ -52,14 +58,6 @@ extern "C" int __builtin_abs(int); // #1
 long __builtin_abs(long);          // #2
 extern "C" int __builtin_abs(int); // #3
 
-int x = __builtin_abs(-2);
-// CHECK:      [[X:%.+]] = call i32 @llvm.abs.i32(i32 -2, i1 true)
-// CHECK-NEXT: store i32 [[X]], ptr @x, align 4
-
-long y = __builtin_abs(-2l);
-// CHECK:  [[Y:%.+]] = call noundef i64 @_Z13__builtin_absl(i64 noundef -2)
-// CHECK:  store i64 [[Y]], ptr @y, align 8
-
 extern const char char_memchr_arg[32];
 char *memchr_result = __builtin_char_memchr(char_memchr_arg, 123, 32);
 // CHECK: call ptr @memchr(ptr noundef @char_memchr_arg, i32 noundef 123, i64 noundef 32)
diff --git a/clang/test/Sema/constant-builtins-2.c b/clang/test/Sema/constant-builtins-2.c
index da2264500d7680..e465a3c5f0ad86 100644
--- a/clang/test/Sema/constant-builtins-2.c
+++ b/clang/test/Sema/constant-builtins-2.c
@@ -35,7 +35,7 @@ long double  g11 = __builtin_nansl("");
 __float128   g11_2 = __builtin_nansf128("");
 #endif
 
-//int          g12 = __builtin_abs(-12);
+int g12 = __builtin_abs(-12);
 
 double       g13 = __builtin_fabs(-12.);
 double       g13_0 = __builtin_fabs(-0.);
@@ -456,6 +456,17 @@ char clrsb9[__builtin_clrsb(1 << (BITSIZE(int) - 1)) == 0 ? 1 : -1];
 char clrsb10[__builtin_clrsb(~(1 << (BITSIZE(int) - 1))) == 0 ? 1 : -1];
 char clrsb11[__builtin_clrsb(0xf) == BITSIZE(int) - 5 ? 1 : -1];
 char clrsb12[__builtin_clrsb(~0x1f) == BITSIZE(int) - 6 ? 1 : -1];
+
+char abs1[__builtin_abs(-12)];
+char abs2[__builtin_labs(-12L)];
+char abs3[__builtin_llabs(-12LL)];
+int abs4 = __builtin_abs(1 << (BITSIZE(int) - 1)); // expected-error {{not a compile-time constant}}
+char abs5[__builtin_abs((1 << (BITSIZE(int) - 1)) + 1)];
+long abs6 = __builtin_labs(1L << (BITSIZE(long) - 1)); // expected-error {{not a compile-time constant}}
+long abs7 = __builtin_labs((1L << (BITSIZE(long) - 1)) + 1);
+long long abs8 = __builtin_llabs(1LL << (BITSIZE(long long) - 1)); // expected-error {{not a compile-time constant}}
+long long abs9 = __builtin_llabs((1LL << (BITSIZE(long long) - 1)) + 1);
+
 #undef BITSIZE
 
 // GCC misc stuff

@c8ef c8ef requested a review from cor3ntin October 17, 2024 02:11
@tbaederr tbaederr requested a review from Sirraide October 17, 2024 07:15
@cor3ntin
Copy link
Contributor

Please add an entry to clang/docs/ReleaseNotes.rst, thanks!

@c8ef
Copy link
Contributor Author

c8ef commented Oct 17, 2024

Please add an entry to clang/docs/ReleaseNotes.rst, thanks!

Done.

@c8ef c8ef requested a review from tbaederr October 17, 2024 13:18
Comment on lines 268 to 276
namespace abs {
static_assert(__builtin_abs(14) == 14, "");
static_assert(__builtin_labs(14L) == 14L, "");
static_assert(__builtin_llabs(14LL) == 14LL, "");
static_assert(__builtin_abs(-14) == 14, "");
static_assert(__builtin_labs(-0x14L) == 0x14L, "");
static_assert(__builtin_llabs(-0x141414141414LL) == 0x141414141414LL, "");
} // namespace abs

Copy link
Contributor

Choose a reason for hiding this comment

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

can you add the same error test cases as in the other files (abs4, etc)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

@c8ef c8ef requested a review from cor3ntin October 17, 2024 15:37
Copy link
Contributor

@cor3ntin cor3ntin left a comment

Choose a reason for hiding this comment

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

lgtm

@c8ef
Copy link
Contributor Author

c8ef commented Oct 18, 2024

Thank you for all your reviews! If there are no further comments, I will proceed with merging this after updating the branch.

@c8ef
Copy link
Contributor Author

c8ef commented Oct 18, 2024

Clang :: Analysis/diagnostics/sarif-diagnostics-taint-test.c
Clang :: Analysis/diagnostics/sarif-multi-diagnostic-test.c
Clang :: CXX/drs/cwg3xx.cpp

These test failures are unrelated and have been observed in other CI runs. Let's go ahead and merge this for now.

@c8ef c8ef merged commit 332ac18 into llvm:main Oct 18, 2024
6 of 7 checks passed
@c8ef c8ef deleted the abs-cont branch October 18, 2024 14:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
clang:frontend Language frontend issues, e.g. anything involving "Sema" clang Clang issues not falling into any other category
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants