-
Notifications
You must be signed in to change notification settings - Fork 14.3k
[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
Conversation
@llvm/pr-subscribers-clang Author: Bryce Wilson (Bryce-MW) ChangesThere is some overlap with 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:
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:
|
✅ With the latest revision this PR passed the C/C++ code formatter. |
a493728
to
6663b62
Compare
6663b62
to
9bea628
Compare
Nice trick. |
Sorry for the confusion, but the codegen test showed it makes sense what you are doing. |
clang/lib/AST/ExprConstant.cpp
Outdated
case Builtin::BI__builtin_subcl: | ||
case Builtin::BI__builtin_subcll: { | ||
LValue CarryOutLValue; | ||
APSInt LHS, RHS, CarryIn, Result; |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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
The bigger issue is that it needs a test. Unfortunately, I am less familiar with the Clang test infrastructure. |
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. |
There was a problem hiding this 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!
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.