Skip to content

[clang] Allow builtin addc/subc to be constant evaluated #81656

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
Feb 15, 2024

Conversation

Bryce-MW
Copy link
Member

There is some overlap with *_overflow which have the same result as these functions with a carry in of zero, but the type inference and way of returning results is different so it didn't seem worth handling them on the same branch.

These builtins are a little weird in that the carry in and out are the same types as the arguments and allow more than just a 1 or 0 input, so it is possible to carry twice. The way this is implemented in IR ORs the two carries so I copied that.

@llvmbot llvmbot added clang Clang issues not falling into any other category clang:frontend Language frontend issues, e.g. anything involving "Sema" labels Feb 13, 2024
@llvmbot
Copy link
Member

llvmbot commented Feb 13, 2024

@llvm/pr-subscribers-clang

Author: Bryce Wilson (Bryce-MW)

Changes

There is some overlap with *_overflow which have the same result as these functions with a carry in of zero, but the type inference and way of returning results is different so it didn't seem worth handling them on the same branch.

These builtins are a little weird in that the carry in and out are the same types as the arguments and allow more than just a 1 or 0 input, so it is possible to carry twice. The way this is implemented in IR ORs the two carries so I copied that.


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

3 Files Affected:

  • (modified) clang/docs/LanguageExtensions.rst (+10)
  • (modified) clang/include/clang/Basic/Builtins.td (+2-2)
  • (modified) clang/lib/AST/ExprConstant.cpp (+48)
diff --git a/clang/docs/LanguageExtensions.rst b/clang/docs/LanguageExtensions.rst
index e91156837290f7..e8557a22662c07 100644
--- a/clang/docs/LanguageExtensions.rst
+++ b/clang/docs/LanguageExtensions.rst
@@ -5241,6 +5241,11 @@ Intrinsics Support within Constant Expressions
 
 The following builtin intrinsics can be used in constant expressions:
 
+* ``__builtin_addcb``
+* ``__builtin_addcs``
+* ``__builtin_addc``
+* ``__builtin_addcl``
+* ``__builtin_addcll``
 * ``__builtin_bitreverse8``
 * ``__builtin_bitreverse16``
 * ``__builtin_bitreverse32``
@@ -5287,6 +5292,11 @@ The following builtin intrinsics can be used in constant expressions:
 * ``__builtin_rotateright16``
 * ``__builtin_rotateright32``
 * ``__builtin_rotateright64``
+* ``__builtin_subcb``
+* ``__builtin_subcs``
+* ``__builtin_subc``
+* ``__builtin_subcl``
+* ``__builtin_subcll``
 
 The following x86-specific intrinsics can be used in constant expressions:
 
diff --git a/clang/include/clang/Basic/Builtins.td b/clang/include/clang/Basic/Builtins.td
index 31a2bdeb2d3e5e..59dc0e20393b5f 100644
--- a/clang/include/clang/Basic/Builtins.td
+++ b/clang/include/clang/Basic/Builtins.td
@@ -4061,14 +4061,14 @@ class MPATemplate : Template<
 
 def Addc : Builtin, MPATemplate {
   let Spellings = ["__builtin_addc"];
-  let Attributes = [NoThrow];
+  let Attributes = [NoThrow, Constexpr];
   // FIXME: Why are these argumentes marked const?
   let Prototype = "T(T const, T const, T const, T*)";
 }
 
 def Subc : Builtin, MPATemplate {
   let Spellings = ["__builtin_subc"];
-  let Attributes = [NoThrow];
+  let Attributes = [NoThrow, Constexpr];
   // FIXME: Why are these argumentes marked const?
   let Prototype = "T(T const, T const, T const, T*)";
 }
diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index 089bc2094567f7..b518baec072759 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -12691,6 +12691,54 @@ bool IntExprEvaluator::VisitBuiltinCallExpr(const CallExpr *E,
     return BuiltinOp == Builtin::BI__atomic_always_lock_free ?
         Success(0, E) : Error(E);
   }
+  case Builtin::BI__builtin_addcb:
+  case Builtin::BI__builtin_addcs:
+  case Builtin::BI__builtin_addc:
+  case Builtin::BI__builtin_addcl:
+  case Builtin::BI__builtin_addcll:
+  case Builtin::BI__builtin_subcb:
+  case Builtin::BI__builtin_subcs:
+  case Builtin::BI__builtin_subc:
+  case Builtin::BI__builtin_subcl:
+  case Builtin::BI__builtin_subcll: {
+    LValue CarryOutLValue;
+    APSInt LHS, RHS, CarryIn, Result;
+    QualType ResultType = E->getArg(0)->getType();
+    if (!EvaluateInteger(E->getArg(0), LHS, Info) ||
+        !EvaluateInteger(E->getArg(1), RHS, Info) ||
+        !EvaluateInteger(E->getArg(2), CarryIn, Info) ||
+        !EvaluatePointer(E->getArg(3), CarryOutLValue, Info))
+      return false;
+
+    bool FirstOverflowed = false;
+    bool SecondOverflowed = false;
+    switch (BuiltinOp) {
+    default:
+      llvm_unreachable("Invalid value for BuiltinOp");
+    case Builtin::BI__builtin_addcb:
+    case Builtin::BI__builtin_addcs:
+    case Builtin::BI__builtin_addc:
+    case Builtin::BI__builtin_addcl:
+    case Builtin::BI__builtin_addcll:
+      Result = LHS.uadd_ov(RHS, FirstOverflowed).uadd_ov(CarryIn, SecondOverflowed);
+      break;
+    case Builtin::BI__builtin_subcb:
+    case Builtin::BI__builtin_subcs:
+    case Builtin::BI__builtin_subc:
+    case Builtin::BI__builtin_subcl:
+    case Builtin::BI__builtin_subcll:
+      Result = LHS.usub_ov(RHS, FirstOverflowed).usub_ov(CarryIn, SecondOverflowed);
+      break;
+    }
+
+    // It is possible for both overflows to happen but CGBuiltin uses an OR so this is consistent
+    APSInt API{FirstOverflowed | SecondOverflowed};
+    APValue APV{API};
+    if (!handleAssignment(Info, E, CarryOutLValue, ResultType, APV))
+      return false;
+    return Success(Result, E);
+
+  }
   case Builtin::BI__builtin_add_overflow:
   case Builtin::BI__builtin_sub_overflow:
   case Builtin::BI__builtin_mul_overflow:

Copy link

github-actions bot commented Feb 13, 2024

✅ With the latest revision this PR passed the C/C++ code formatter.

@Bryce-MW Bryce-MW force-pushed the bryce-addc-constexpr branch from a493728 to 6663b62 Compare February 13, 2024 19:58
@Bryce-MW Bryce-MW force-pushed the bryce-addc-constexpr branch from 6663b62 to 9bea628 Compare February 13, 2024 19:59
@tschuett
Copy link

Nice trick.

@tschuett
Copy link

Sorry for the confusion, but the codegen test showed it makes sense what you are doing.

case Builtin::BI__builtin_subcl:
case Builtin::BI__builtin_subcll: {
LValue CarryOutLValue;
APSInt LHS, RHS, CarryIn, Result;

Choose a reason for hiding this comment

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

If you search the internet for llvm APSInt, you will find the S means signed. You will also find llvm APInt. The latter is the unsigned version.

Copy link
Member Author

Choose a reason for hiding this comment

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

IIRC, APSInt isn't always signed, it just knows if it is or not. EvaluateInteger always takes an APSInt

@tschuett
Copy link

The bigger issue is that it needs a test. Unfortunately, I am less familiar with the Clang test infrastructure.

@Bryce-MW
Copy link
Member Author

I swear I had meant to add tests! I did see a place that looks good to add them before so I've done that. Which allowed me to find out that I had not done my implementation correctly so I fixed that up.

Copy link
Collaborator

@AaronBallman AaronBallman left a comment

Choose a reason for hiding this comment

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

Please also add a release note to clang/docs/ReleaseNotes.rst so users know about the new functionality. There were a few tiny nits with comments, but otherwise this LGTM!

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