[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