[clang] 7f45acf - [clang][Interp] Implement various overflow and carry builtins

Timm Bäder via cfe-commits cfe-commits at lists.llvm.org
Fri Feb 16 10:01:44 PST 2024


Author: Timm Bäder
Date: 2024-02-16T19:00:40+01:00
New Revision: 7f45acfbfec678ad918640744229f15b9293ac79

URL: https://github.com/llvm/llvm-project/commit/7f45acfbfec678ad918640744229f15b9293ac79
DIFF: https://github.com/llvm/llvm-project/commit/7f45acfbfec678ad918640744229f15b9293ac79.diff

LOG: [clang][Interp] Implement various overflow and carry builtins

Enough so we can enable SemaCXX/builtin-overflow.cpp.

Added: 
    

Modified: 
    clang/lib/AST/Interp/ByteCodeEmitter.cpp
    clang/lib/AST/Interp/InterpBuiltin.cpp
    clang/lib/AST/Interp/PrimType.h
    clang/test/SemaCXX/builtins-overflow.cpp

Removed: 
    


################################################################################
diff  --git a/clang/lib/AST/Interp/ByteCodeEmitter.cpp b/clang/lib/AST/Interp/ByteCodeEmitter.cpp
index e697e24fb341d2..2b9ff96daa6c1d 100644
--- a/clang/lib/AST/Interp/ByteCodeEmitter.cpp
+++ b/clang/lib/AST/Interp/ByteCodeEmitter.cpp
@@ -22,6 +22,10 @@
 using namespace clang;
 using namespace clang::interp;
 
+static bool isUnevaluatedBuiltin(unsigned BuiltinID) {
+  return BuiltinID == Builtin::BI__builtin_classify_type;
+}
+
 Function *ByteCodeEmitter::compileFunc(const FunctionDecl *FuncDecl) {
   bool IsLambdaStaticInvoker = false;
   if (const auto *MD = dyn_cast<CXXMethodDecl>(FuncDecl);
@@ -122,7 +126,7 @@ Function *ByteCodeEmitter::compileFunc(const FunctionDecl *FuncDecl) {
   if (!Func) {
     bool IsUnevaluatedBuiltin = false;
     if (unsigned BI = FuncDecl->getBuiltinID())
-      IsUnevaluatedBuiltin = Ctx.getASTContext().BuiltinInfo.isUnevaluated(BI);
+      IsUnevaluatedBuiltin = isUnevaluatedBuiltin(BI);
 
     Func =
         P.createFunction(FuncDecl, ParamOffset, std::move(ParamTypes),

diff  --git a/clang/lib/AST/Interp/InterpBuiltin.cpp b/clang/lib/AST/Interp/InterpBuiltin.cpp
index 9b3f70fef35998..f1040d15a1d2ad 100644
--- a/clang/lib/AST/Interp/InterpBuiltin.cpp
+++ b/clang/lib/AST/Interp/InterpBuiltin.cpp
@@ -55,8 +55,8 @@ static APSInt peekToAPSInt(InterpStack &Stk, PrimType T, size_t Offset = 0) {
   APSInt R;
   INT_TYPE_SWITCH(T, {
     T Val = Stk.peek<T>(Offset);
-    R = APSInt(
-        APInt(Val.bitWidth(), static_cast<uint64_t>(Val), T::isSigned()));
+    R = APSInt(APInt(Val.bitWidth(), static_cast<uint64_t>(Val), T::isSigned()),
+               !T::isSigned());
   });
 
   return R;
@@ -155,6 +155,11 @@ static void pushSizeT(InterpState &S, uint64_t Val) {
   }
 }
 
+static void assignInteger(Pointer &Dest, PrimType ValueT, const APSInt &Value) {
+  INT_TYPE_SWITCH_NO_BOOL(
+      ValueT, { Dest.deref<T>() = T::from(static_cast<T>(Value)); });
+}
+
 static bool retPrimValue(InterpState &S, CodePtr OpPC, APValue &Result,
                          std::optional<PrimType> &T) {
   if (!T)
@@ -667,6 +672,175 @@ static bool interp__builtin_launder(InterpState &S, CodePtr OpPC,
   return true;
 }
 
+// Two integral values followed by a pointer (lhs, rhs, resultOut)
+static bool interp__builtin_overflowop(InterpState &S, CodePtr OpPC,
+                                       const InterpFrame *Frame,
+                                       const Function *Func,
+                                       const CallExpr *Call) {
+  Pointer &ResultPtr = S.Stk.peek<Pointer>();
+  if (ResultPtr.isDummy())
+    return false;
+
+  unsigned BuiltinOp = Func->getBuiltinID();
+  PrimType RHST = *S.getContext().classify(Call->getArg(1)->getType());
+  PrimType LHST = *S.getContext().classify(Call->getArg(0)->getType());
+  APSInt RHS = peekToAPSInt(S.Stk, RHST,
+                            align(primSize(PT_Ptr)) + align(primSize(RHST)));
+  APSInt LHS = peekToAPSInt(S.Stk, LHST,
+                            align(primSize(PT_Ptr)) + align(primSize(RHST)) +
+                                align(primSize(LHST)));
+  QualType ResultType = Call->getArg(2)->getType()->getPointeeType();
+  PrimType ResultT = *S.getContext().classify(ResultType);
+  bool Overflow;
+
+  APSInt Result;
+  if (BuiltinOp == Builtin::BI__builtin_add_overflow ||
+      BuiltinOp == Builtin::BI__builtin_sub_overflow ||
+      BuiltinOp == Builtin::BI__builtin_mul_overflow) {
+    bool IsSigned = LHS.isSigned() || RHS.isSigned() ||
+                    ResultType->isSignedIntegerOrEnumerationType();
+    bool AllSigned = LHS.isSigned() && RHS.isSigned() &&
+                     ResultType->isSignedIntegerOrEnumerationType();
+    uint64_t LHSSize = LHS.getBitWidth();
+    uint64_t RHSSize = RHS.getBitWidth();
+    uint64_t ResultSize = S.getCtx().getTypeSize(ResultType);
+    uint64_t MaxBits = std::max(std::max(LHSSize, RHSSize), ResultSize);
+
+    // Add an additional bit if the signedness isn't uniformly agreed to. We
+    // could do this ONLY if there is a signed and an unsigned that both have
+    // MaxBits, but the code to check that is pretty nasty.  The issue will be
+    // caught in the shrink-to-result later anyway.
+    if (IsSigned && !AllSigned)
+      ++MaxBits;
+
+    LHS = APSInt(LHS.extOrTrunc(MaxBits), !IsSigned);
+    RHS = APSInt(RHS.extOrTrunc(MaxBits), !IsSigned);
+    Result = APSInt(MaxBits, !IsSigned);
+  }
+
+  // Find largest int.
+  switch (BuiltinOp) {
+  default:
+    llvm_unreachable("Invalid value for BuiltinOp");
+  case Builtin::BI__builtin_add_overflow:
+  case Builtin::BI__builtin_sadd_overflow:
+  case Builtin::BI__builtin_saddl_overflow:
+  case Builtin::BI__builtin_saddll_overflow:
+  case Builtin::BI__builtin_uadd_overflow:
+  case Builtin::BI__builtin_uaddl_overflow:
+  case Builtin::BI__builtin_uaddll_overflow:
+    Result = LHS.isSigned() ? LHS.sadd_ov(RHS, Overflow)
+                            : LHS.uadd_ov(RHS, Overflow);
+    break;
+  case Builtin::BI__builtin_sub_overflow:
+  case Builtin::BI__builtin_ssub_overflow:
+  case Builtin::BI__builtin_ssubl_overflow:
+  case Builtin::BI__builtin_ssubll_overflow:
+  case Builtin::BI__builtin_usub_overflow:
+  case Builtin::BI__builtin_usubl_overflow:
+  case Builtin::BI__builtin_usubll_overflow:
+    Result = LHS.isSigned() ? LHS.ssub_ov(RHS, Overflow)
+                            : LHS.usub_ov(RHS, Overflow);
+    break;
+  case Builtin::BI__builtin_mul_overflow:
+  case Builtin::BI__builtin_smul_overflow:
+  case Builtin::BI__builtin_smull_overflow:
+  case Builtin::BI__builtin_smulll_overflow:
+  case Builtin::BI__builtin_umul_overflow:
+  case Builtin::BI__builtin_umull_overflow:
+  case Builtin::BI__builtin_umulll_overflow:
+    Result = LHS.isSigned() ? LHS.smul_ov(RHS, Overflow)
+                            : LHS.umul_ov(RHS, Overflow);
+    break;
+  }
+
+  // In the case where multiple sizes are allowed, truncate and see if
+  // the values are the same.
+  if (BuiltinOp == Builtin::BI__builtin_add_overflow ||
+      BuiltinOp == Builtin::BI__builtin_sub_overflow ||
+      BuiltinOp == Builtin::BI__builtin_mul_overflow) {
+    // APSInt doesn't have a TruncOrSelf, so we use extOrTrunc instead,
+    // since it will give us the behavior of a TruncOrSelf in the case where
+    // its parameter <= its size.  We previously set Result to be at least the
+    // type-size of the result, so getTypeSize(ResultType) <= Resu
+    APSInt Temp = Result.extOrTrunc(S.getCtx().getTypeSize(ResultType));
+    Temp.setIsSigned(ResultType->isSignedIntegerOrEnumerationType());
+
+    if (!APSInt::isSameValue(Temp, Result))
+      Overflow = true;
+    Result = Temp;
+  }
+
+  // Write Result to ResultPtr and put Overflow on the stacl.
+  assignInteger(ResultPtr, ResultT, Result);
+  ResultPtr.initialize();
+  assert(Func->getDecl()->getReturnType()->isBooleanType());
+  S.Stk.push<Boolean>(Overflow);
+  return true;
+}
+
+/// Three integral values followed by a pointer (lhs, rhs, carry, carryOut).
+static bool interp__builtin_carryop(InterpState &S, CodePtr OpPC,
+                                    const InterpFrame *Frame,
+                                    const Function *Func,
+                                    const CallExpr *Call) {
+  unsigned BuiltinOp = Func->getBuiltinID();
+  PrimType LHST = *S.getContext().classify(Call->getArg(0)->getType());
+  PrimType RHST = *S.getContext().classify(Call->getArg(1)->getType());
+  PrimType CarryT = *S.getContext().classify(Call->getArg(2)->getType());
+  APSInt RHS = peekToAPSInt(S.Stk, RHST,
+                            align(primSize(PT_Ptr)) + align(primSize(CarryT)) +
+                                align(primSize(RHST)));
+  APSInt LHS =
+      peekToAPSInt(S.Stk, LHST,
+                   align(primSize(PT_Ptr)) + align(primSize(RHST)) +
+                       align(primSize(CarryT)) + align(primSize(LHST)));
+  APSInt CarryIn = peekToAPSInt(
+      S.Stk, LHST, align(primSize(PT_Ptr)) + align(primSize(CarryT)));
+  APSInt CarryOut;
+
+  APSInt Result;
+  // Copy the number of bits and sign.
+  Result = LHS;
+  CarryOut = LHS;
+
+  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.
+  CarryOut = (uint64_t)(FirstOverflowed | SecondOverflowed);
+
+  Pointer &CarryOutPtr = S.Stk.peek<Pointer>();
+  QualType CarryOutType = Call->getArg(3)->getType()->getPointeeType();
+  PrimType CarryOutT = *S.getContext().classify(CarryOutType);
+  assignInteger(CarryOutPtr, CarryOutT, CarryOut);
+  CarryOutPtr.initialize();
+
+  assert(Call->getType() == Call->getArg(0)->getType());
+  pushAPSInt(S, Result);
+  return true;
+}
+
 bool InterpretBuiltin(InterpState &S, CodePtr OpPC, const Function *F,
                       const CallExpr *Call) {
   InterpFrame *Frame = S.Current;
@@ -901,6 +1075,45 @@ bool InterpretBuiltin(InterpState &S, CodePtr OpPC, const Function *F,
       return false;
     break;
 
+  case Builtin::BI__builtin_add_overflow:
+  case Builtin::BI__builtin_sub_overflow:
+  case Builtin::BI__builtin_mul_overflow:
+  case Builtin::BI__builtin_sadd_overflow:
+  case Builtin::BI__builtin_uadd_overflow:
+  case Builtin::BI__builtin_uaddl_overflow:
+  case Builtin::BI__builtin_uaddll_overflow:
+  case Builtin::BI__builtin_usub_overflow:
+  case Builtin::BI__builtin_usubl_overflow:
+  case Builtin::BI__builtin_usubll_overflow:
+  case Builtin::BI__builtin_umul_overflow:
+  case Builtin::BI__builtin_umull_overflow:
+  case Builtin::BI__builtin_umulll_overflow:
+  case Builtin::BI__builtin_saddl_overflow:
+  case Builtin::BI__builtin_saddll_overflow:
+  case Builtin::BI__builtin_ssub_overflow:
+  case Builtin::BI__builtin_ssubl_overflow:
+  case Builtin::BI__builtin_ssubll_overflow:
+  case Builtin::BI__builtin_smul_overflow:
+  case Builtin::BI__builtin_smull_overflow:
+  case Builtin::BI__builtin_smulll_overflow:
+    if (!interp__builtin_overflowop(S, OpPC, Frame, F, Call))
+      return false;
+    break;
+
+  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:
+    if (!interp__builtin_carryop(S, OpPC, Frame, F, Call))
+      return false;
+    break;
+
   default:
     return false;
   }

diff  --git a/clang/lib/AST/Interp/PrimType.h b/clang/lib/AST/Interp/PrimType.h
index d07c2efe8e3c9b..24a24a71a07b57 100644
--- a/clang/lib/AST/Interp/PrimType.h
+++ b/clang/lib/AST/Interp/PrimType.h
@@ -149,6 +149,24 @@ static inline bool aligned(const void *P) {
     }                                                                          \
   } while (0)
 
+#define INT_TYPE_SWITCH_NO_BOOL(Expr, B)                                       \
+  do {                                                                         \
+    switch (Expr) {                                                            \
+      TYPE_SWITCH_CASE(PT_Sint8, B)                                            \
+      TYPE_SWITCH_CASE(PT_Uint8, B)                                            \
+      TYPE_SWITCH_CASE(PT_Sint16, B)                                           \
+      TYPE_SWITCH_CASE(PT_Uint16, B)                                           \
+      TYPE_SWITCH_CASE(PT_Sint32, B)                                           \
+      TYPE_SWITCH_CASE(PT_Uint32, B)                                           \
+      TYPE_SWITCH_CASE(PT_Sint64, B)                                           \
+      TYPE_SWITCH_CASE(PT_Uint64, B)                                           \
+      TYPE_SWITCH_CASE(PT_IntAP, B)                                            \
+      TYPE_SWITCH_CASE(PT_IntAPS, B)                                           \
+    default:                                                                   \
+      llvm_unreachable("Not an integer value");                                \
+    }                                                                          \
+  } while (0)
+
 #define COMPOSITE_TYPE_SWITCH(Expr, B, D)                                      \
   do {                                                                         \
     switch (Expr) {                                                            \

diff  --git a/clang/test/SemaCXX/builtins-overflow.cpp b/clang/test/SemaCXX/builtins-overflow.cpp
index 1b1e46ae751329..f40af5dd51cd23 100644
--- a/clang/test/SemaCXX/builtins-overflow.cpp
+++ b/clang/test/SemaCXX/builtins-overflow.cpp
@@ -1,4 +1,5 @@
 // RUN: %clang_cc1 -fsyntax-only -std=c++17 -verify %s
+// RUN: %clang_cc1 -fsyntax-only -std=c++17 -verify %s -fexperimental-new-constant-interpreter
 // expected-no-diagnostics
 
 #include <limits.h>


        


More information about the cfe-commits mailing list