[cfe-commits] [PATCH] PR12290: introducing overflow builtins

Xi Wang xi.wang at gmail.com
Sun Dec 30 19:30:53 PST 2012


The attached patch is rebased on the latest trunk for reviewing.

- Move the documentation to Sphinx.

You can also review the changes at:

https://github.com/xiw/clang/compare/builtin-overflow

Happy new year!

- xi

On 6/19/12 12:57 AM, Xi Wang wrote:
> Hi,
> 
> The attached patch addresses the comments from Eli Friedman.
> - whitespace nit
> - remove useless check
> - reject bool type
> 
> http://lists.cs.uiuc.edu/pipermail/cfe-dev/2012-May/021687.html
> 
> Also rebased on the latest trunk.
> 
> You can review the changes at
> 
> https://github.com/xiw/clang/compare/builtin-overflow
> 
> Thanks for reviewing.
> 
> - xi
> 
> 
> 
> 
> On Apr 17, 2012, at 12:28 PM, Xi Wang wrote:
> 
>> The attached patch makes two changes.
>>
>> - Enabled 128-bit integer operation in overflow builtins.
>>
>> - Rebased on the latest trunk.
>>
>> Please review!  Thanks.
>>
>> - xi
>> <overflow.patch>
>> On Apr 4, 2012, at 8:18 PM, Xi Wang wrote:
>>
>>> Hi,
>>>
>>> Attached is a patch that adds arithmetic with overflow builtins to Clang.  Please review.  Thanks.
>>>
>>> Arithmetic with overflow builtins are used to perform arithmetic operations with overflow detection.
>>>
>>> Syntax:
>>>
>>> bool __builtin_add_with_overflow(type *ptr, type a, type b);
>>> bool __builtin_sub_with_overflow(type *ptr, type a, type b);
>>> bool __builtin_mul_with_overflow(type *ptr, type a, type b);
>>>
>>> Example of Use:
>>>
>>> void *malloc_array(size_t n, size_t size) {
>>> size_t bytes;
>>> if (__builtin_mul_with_overflow(&bytes, n, size))
>>>    return NULL;
>>> return malloc(bytes);
>>> }
>>>
>>> Description:
>>>
>>> __builtin_op_with_overflow(ptr, a, b) stores the result of a op b in ptr, and returns true if an overflow occurred during the arithmetic operation. Note that type is inferred from *ptr. These builtins help developers write more efficient and correct code by avoiding ad hoc overflow checks.
>>>
>>> Query for this feature with __has_builtin(__builtin_op_with_overflow).
>>>
>>> - xi
>>
> 
-------------- next part --------------
diff --git a/docs/LanguageExtensions.rst b/docs/LanguageExtensions.rst
index c3e2c83..ea3aa8e 100644
--- a/docs/LanguageExtensions.rst
+++ b/docs/LanguageExtensions.rst
@@ -1409,6 +1409,42 @@ C11's ``<stdatomic.h>`` header.  These builtins provide the semantics of the
 * ``__c11_atomic_fetch_or``
 * ``__c11_atomic_fetch_xor``
 
+Arithmetic with overflow builtins
+---------------------------------
+
+Arithmetic with overflow builtins are used to perform arithmetic operations
+with overflow detection.
+
+**Syntax**:
+
+.. code-block:: c++
+
+  bool __builtin_add_with_overflow(type *ptr, type a, type b);
+  bool __builtin_sub_with_overflow(type *ptr, type a, type b);
+  bool __builtin_mul_with_overflow(type *ptr, type a, type b);
+
+**Example of Use**:
+
+.. code-block:: c++
+
+  void *malloc_array(size_t n, size_t size) {
+    size_t bytes;
+    if (__builtin_mul_with_overflow(&bytes, n, size))
+      return NULL;
+    return malloc(bytes);
+  }
+
+**Description**:
+
+``__builtin_OP_with_overflow(ptr, a, b)`` stores the result of
+``a OP b`` in ``ptr``, and returns true if an overflow
+occurred during the arithmetic operation.  Note that *type*
+is inferred from ``*ptr``.  These builtins help developers write
+more efficient and correct code by avoiding ad hoc overflow checks.
+
+Query for this feature with
+``__has_builtin(__builtin_OP_with_overflow)``.
+
 Non-standard C++11 Attributes
 =============================
 
diff --git a/include/clang/Basic/Builtins.def b/include/clang/Basic/Builtins.def
index 3f1c276..add1667 100644
--- a/include/clang/Basic/Builtins.def
+++ b/include/clang/Basic/Builtins.def
@@ -659,6 +659,11 @@ BUILTIN(__sync_fetch_and_max, "iiD*i", "n")
 BUILTIN(__sync_fetch_and_umin, "UiUiD*Ui", "n")
 BUILTIN(__sync_fetch_and_umax, "UiUiD*Ui", "n")
 
+// Overflow builtins.
+BUILTIN(__builtin_add_with_overflow, "v.", "t")
+BUILTIN(__builtin_sub_with_overflow, "v.", "t")
+BUILTIN(__builtin_mul_with_overflow, "v.", "t")
+
 // Random libc builtins.
 BUILTIN(__builtin_abort, "v", "Fnr")
 BUILTIN(__builtin_index, "c*cC*i", "Fn")
diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td
index 934bb30..bfa585b 100644
--- a/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/include/clang/Basic/DiagnosticSemaKinds.td
@@ -5044,6 +5044,12 @@ def err_atomic_op_bitwise_needs_atomic_int : Error<
   "first argument to bitwise atomic operation must be a pointer to "
   "%select{|atomic }0integer (%1 invalid)">;
 
+def err_overflow_builtin_must_be_pointer : Error<
+  "first argument to overflow builtin must be a pointer (%0 invalid)">;
+def err_overflow_builtin_must_be_pointer_int : Error<
+  "first argument to overflow builtin must be a pointer to integer "
+  "(%0 invalid)">;
+
 def err_deleted_function_use : Error<"attempt to use a deleted function">;
 
 def err_kern_type_not_void_return : Error<
diff --git a/include/clang/Sema/Sema.h b/include/clang/Sema/Sema.h
index 81093cb..b4e4b6c 100644
--- a/include/clang/Sema/Sema.h
+++ b/include/clang/Sema/Sema.h
@@ -7232,6 +7232,7 @@ private:
   ExprResult SemaBuiltinAtomicOverloaded(ExprResult TheCallResult);
   ExprResult SemaAtomicOpsOverloaded(ExprResult TheCallResult,
                                      AtomicExpr::AtomicOp Op);
+  bool SemaOverflowOpsOverloaded(CallExpr *TheCall);
   bool SemaBuiltinConstantArg(CallExpr *TheCall, int ArgNum,
                               llvm::APSInt &Result);
 
diff --git a/lib/CodeGen/CGBuiltin.cpp b/lib/CodeGen/CGBuiltin.cpp
index d2824e9..4b465cc 100644
--- a/lib/CodeGen/CGBuiltin.cpp
+++ b/lib/CodeGen/CGBuiltin.cpp
@@ -141,6 +141,32 @@ static RValue EmitBinaryAtomicPost(CodeGenFunction &CGF,
   return RValue::get(Result);
 }
 
+static RValue EmitOverflow(CodeGenFunction &CGF,
+                           const CallExpr *E,
+                           Intrinsic::ID SID,
+                           Intrinsic::ID UID) {
+    QualType T = E->getArg(1)->getType();
+    llvm::Value *DestPtr = CGF.EmitScalarExpr(E->getArg(0));
+    unsigned AddrSpace =
+      cast<llvm::PointerType>(DestPtr->getType())->getAddressSpace();
+
+    llvm::IntegerType *IntType =
+      llvm::IntegerType::get(CGF.getLLVMContext(),
+                             CGF.getContext().getTypeSize(T));
+    llvm::Type *IntPtrType = IntType->getPointerTo(AddrSpace);
+
+    Value *P, *V0, *V1;
+    P = CGF.Builder.CreateBitCast(DestPtr, IntPtrType);
+    V0 = EmitToInt(CGF, CGF.EmitScalarExpr(E->getArg(1)), T, IntType);
+    V1 = EmitToInt(CGF, CGF.EmitScalarExpr(E->getArg(2)), T, IntType);
+
+    Intrinsic::ID ID = T->hasSignedIntegerRepresentation() ? SID : UID;
+    Function *F = CGF.CGM.getIntrinsic(ID, IntType);
+    CallInst *Result = CGF.Builder.CreateCall2(F, V0, V1);
+    CGF.Builder.CreateStore(CGF.Builder.CreateExtractValue(Result, 0), P);
+    return RValue::get(CGF.Builder.CreateExtractValue(Result, 1));
+}
+
 /// EmitFAbs - Emit a call to fabs/fabsf/fabsl, depending on the type of ValTy,
 /// which must be a scalar floating point type.
 static Value *EmitFAbs(CodeGenFunction &CGF, Value *V, QualType ValTy) {
@@ -1254,6 +1280,17 @@ RValue CodeGenFunction::EmitBuiltinExpr(const FunctionDecl *FD,
     return RValue::get(0);
   }
 
+  // Overflow builtins.
+  case Builtin::BI__builtin_add_with_overflow:
+    return EmitOverflow(*this, E, Intrinsic::sadd_with_overflow,
+                        Intrinsic::uadd_with_overflow);
+  case Builtin::BI__builtin_sub_with_overflow:
+    return EmitOverflow(*this, E, Intrinsic::ssub_with_overflow,
+                        Intrinsic::usub_with_overflow);
+  case Builtin::BI__builtin_mul_with_overflow:
+    return EmitOverflow(*this, E, Intrinsic::smul_with_overflow,
+                        Intrinsic::umul_with_overflow);
+
     // Library functions with special handling.
   case Builtin::BIsqrt:
   case Builtin::BIsqrtf:
diff --git a/lib/Sema/SemaChecking.cpp b/lib/Sema/SemaChecking.cpp
index f0de7be..4d02fa3 100644
--- a/lib/Sema/SemaChecking.cpp
+++ b/lib/Sema/SemaChecking.cpp
@@ -63,7 +63,7 @@ static bool checkArgCount(Sema &S, CallExpr *call, unsigned desiredArgCount) {
     
   return S.Diag(range.getBegin(), diag::err_typecheck_call_too_many_args)
     << 0 /*function call*/ << desiredArgCount << argCount
-    << call->getArg(1)->getSourceRange();
+    << range;
 }
 
 /// Check that the first argument to __builtin_annotation is an integer
@@ -270,6 +270,12 @@ Sema::CheckBuiltinFunctionCall(unsigned BuiltinID, CallExpr *TheCall) {
   case Builtin::BI##ID: \
     return SemaAtomicOpsOverloaded(TheCallResult, AtomicExpr::AO##ID);
 #include "clang/Basic/Builtins.def"
+  case Builtin::BI__builtin_add_with_overflow:
+  case Builtin::BI__builtin_sub_with_overflow:
+  case Builtin::BI__builtin_mul_with_overflow:
+    if (SemaOverflowOpsOverloaded(TheCall))
+      return ExprError();
+    break;
   case Builtin::BI__builtin_annotation:
     if (SemaBuiltinAnnotation(*this, TheCall))
       return ExprError();
@@ -618,6 +624,43 @@ bool Sema::CheckBlockCall(NamedDecl *NDecl, CallExpr *TheCall,
   return false;
 }
 
+bool Sema::SemaOverflowOpsOverloaded(CallExpr *TheCall) {
+  DeclRefExpr *DRE =cast<DeclRefExpr>(TheCall->getCallee()->IgnoreParenCasts());
+  // All these operations take the following form:
+  // bool __builtin_*_with_overflow(T*, T, T);
+
+  if (checkArgCount(*this, TheCall, 3))
+    return true;
+
+  Expr *Ptr = TheCall->getArg(0);
+  Ptr = DefaultFunctionArrayLvalueConversion(Ptr).get();
+  const PointerType *pointerType = Ptr->getType()->getAs<PointerType>();
+  if (!pointerType)
+    return Diag(DRE->getLocStart(), diag::err_overflow_builtin_must_be_pointer)
+      << Ptr->getType() << Ptr->getSourceRange();
+
+  QualType ValTy = pointerType->getPointeeType();
+  if (!ValTy->isIntegerType() || ValTy->isBooleanType())
+    return Diag(DRE->getLocStart(), diag::err_overflow_builtin_must_be_pointer_int)
+      << Ptr->getType() << Ptr->getSourceRange();
+
+  // The first argument --- the pointer --- has a fixed type; we
+  // deduce the types of the rest of the arguments accordingly.  Walk
+  // the remaining arguments, converting them to the deduced value type.
+  for (unsigned i = 1; i != 3; ++i) {
+    ExprResult Arg = TheCall->getArg(i);
+    InitializedEntity Entity =
+        InitializedEntity::InitializeParameter(Context, ValTy, false);
+    Arg = PerformCopyInitialization(Entity, SourceLocation(), Arg);
+    if (Arg.isInvalid())
+      return true;
+    TheCall->setArg(i, Arg.get());
+  }
+
+  TheCall->setType(Context.BoolTy);
+  return false;
+}
+
 ExprResult Sema::SemaAtomicOpsOverloaded(ExprResult TheCallResult,
                                          AtomicExpr::AtomicOp Op) {
   CallExpr *TheCall = cast<CallExpr>(TheCallResult.get());
diff --git a/test/CodeGen/overflow.c b/test/CodeGen/overflow.c
new file mode 100644
index 0000000..b2a501f
--- /dev/null
+++ b/test/CodeGen/overflow.c
@@ -0,0 +1,26 @@
+// RUN: %clang_cc1 %s -emit-llvm -o - -triple=i686-apple-darwin9 | FileCheck %s
+
+#include <stddef.h>
+
+void f(void) {
+  extern int c, a, b;
+  extern unsigned z, x, y;
+
+  // CHECK: llvm.sadd.with.overflow.i32
+  __builtin_add_with_overflow(&c, a, b);
+
+  // CHECK: llvm.uadd.with.overflow.i32
+  __builtin_add_with_overflow(&z, x, y);
+
+  // CHECK: llvm.ssub.with.overflow.i32
+  __builtin_sub_with_overflow(&c, a, b);
+
+  // CHECK: llvm.usub.with.overflow.i32
+  __builtin_sub_with_overflow(&z, x, y);
+
+  // CHECK: llvm.smul.with.overflow.i32
+  __builtin_mul_with_overflow(&c, a, b);
+
+  // CHECK: llvm.umul.with.overflow.i32
+  __builtin_mul_with_overflow(&z, x, y);
+}
diff --git a/test/Sema/overflow.c b/test/Sema/overflow.c
new file mode 100644
index 0000000..46088a4
--- /dev/null
+++ b/test/Sema/overflow.c
@@ -0,0 +1,15 @@
+// RUN: %clang_cc1 %s -verify -fsyntax-only
+
+// Sema tests for __builtin_*_with_overflow
+
+void f(int *p, int a, int b) {
+  __builtin_add_with_overflow(0);          // expected-error {{too few arguments to function call}}
+  __builtin_add_with_overflow(0, 0);       // expected-error {{too few arguments to function call}}
+  __builtin_add_with_overflow(0, 0, 0, 0); // expected-error {{too many arguments to function call}}
+
+  __builtin_add_with_overflow(0, 0, 0);          // expected-error {{must be a pointer}}
+  __builtin_add_with_overflow((float *)p, a, b); // expected-error {{must be a pointer to integer}}
+  __builtin_add_with_overflow((_Bool *)p, a, b); // expected-error {{must be a pointer to integer}}
+
+  __builtin_add_with_overflow(p, a, b);
+}


More information about the cfe-commits mailing list