Skip to content

Commit

Permalink
Add support for __builtin_{add,sub,mul}_overflow.
Browse files Browse the repository at this point in the history
Patch by David Grayson!

llvm-svn: 251651
  • Loading branch information
rjmccall committed Oct 29, 2015
1 parent b254235 commit 03107a4
Show file tree
Hide file tree
Showing 7 changed files with 382 additions and 3 deletions.
22 changes: 20 additions & 2 deletions clang/docs/LanguageExtensions.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1679,17 +1679,20 @@ an example of their usage:
errorcode_t security_critical_application(...) {
unsigned x, y, result;
...
if (__builtin_umul_overflow(x, y, &result))
if (__builtin_mul_overflow(x, y, &result))
return kErrorCodeHackers;
...
use_multiply(result);
...
}
A complete enumeration of the builtins are:
Clang provides the following checked arithmetic builtins:
.. code-block:: c
bool __builtin_add_overflow (type1 x, type2 y, type3 *sum);
bool __builtin_sub_overflow (type1 x, type2 y, type3 *diff);
bool __builtin_mul_overflow (type1 x, type2 y, type3 *prod);
bool __builtin_uadd_overflow (unsigned x, unsigned y, unsigned *sum);
bool __builtin_uaddl_overflow (unsigned long x, unsigned long y, unsigned long *sum);
bool __builtin_uaddll_overflow(unsigned long long x, unsigned long long y, unsigned long long *sum);
Expand All @@ -1709,6 +1712,21 @@ A complete enumeration of the builtins are:
bool __builtin_smull_overflow (long x, long y, long *prod);
bool __builtin_smulll_overflow(long long x, long long y, long long *prod);
Each builtin performs the specified mathematical operation on the
first two arguments and stores the result in the third argument. If
possible, the result will be equal to mathematically-correct result
and the builtin will return 0. Otherwise, the builtin will return
1 and the result will be equal to the unique value that is equivalent
to the mathematically-correct result modulo two raised to the *k*
power, where *k* is the number of bits in the result type. The
behavior of these builtins is well-defined for all argument values.
The first three builtins work generically for operands of any integer type,
including boolean types. The operands need not have the same type as each
other, or as the result. The other builtins may implicitly promote or
convert their operands before performing the operation.
Query for this feature with ``__has_builtin(__builtin_add_overflow)``, etc.
.. _langext-__c11_atomic:
Expand Down
3 changes: 3 additions & 0 deletions clang/include/clang/Basic/Builtins.def
Original file line number Diff line number Diff line change
Expand Up @@ -1217,6 +1217,9 @@ BUILTIN(__builtin_subcl, "ULiULiCULiCULiCULi*", "n")
BUILTIN(__builtin_subcll, "ULLiULLiCULLiCULLiCULLi*", "n")

// Checked Arithmetic Builtins for Security.
BUILTIN(__builtin_add_overflow, "v.", "nt")
BUILTIN(__builtin_sub_overflow, "v.", "nt")
BUILTIN(__builtin_mul_overflow, "v.", "nt")
BUILTIN(__builtin_uadd_overflow, "bUiCUiCUi*", "n")
BUILTIN(__builtin_uaddl_overflow, "bULiCULiCULi*", "n")
BUILTIN(__builtin_uaddll_overflow, "bULLiCULLiCULLi*", "n")
Expand Down
6 changes: 6 additions & 0 deletions clang/include/clang/Basic/DiagnosticSemaKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -6260,6 +6260,12 @@ def warn_atomic_op_has_invalid_memory_order : Warning<
"memory order argument to atomic operation is invalid">,
InGroup<DiagGroup<"atomic-memory-ordering">>;

def err_overflow_builtin_must_be_int : Error<
"operand argument to overflow builtin must be an integer (%0 invalid)">;
def err_overflow_builtin_must_be_ptr_int : Error<
"result argument to overflow builtin must be a pointer "
"to a non-const integer (%0 invalid)">;

def err_atomic_load_store_uses_lib : Error<
"atomic %select{load|store}0 requires runtime support that is not "
"available for this target">;
Expand Down
128 changes: 127 additions & 1 deletion clang/lib/CodeGen/CGBuiltin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,50 @@ static llvm::Value *EmitOverflowIntrinsic(CodeGenFunction &CGF,
return CGF.Builder.CreateExtractValue(Tmp, 0);
}

namespace {
struct WidthAndSignedness {
unsigned Width;
bool Signed;
};
}

static WidthAndSignedness
getIntegerWidthAndSignedness(const clang::ASTContext &context,
const clang::QualType Type) {
assert(Type->isIntegerType() && "Given type is not an integer.");
unsigned Width = Type->isBooleanType() ? 1 : context.getTypeInfo(Type).Width;
bool Signed = Type->isSignedIntegerType();
return {Width, Signed};
}

// Given one or more integer types, this function produces an integer type that
// encompasses them: any value in one of the given types could be expressed in
// the encompassing type.
static struct WidthAndSignedness
EncompassingIntegerType(ArrayRef<struct WidthAndSignedness> Types) {
assert(Types.size() > 0 && "Empty list of types.");

// If any of the given types is signed, we must return a signed type.
bool Signed = false;
for (const auto &Type : Types) {
Signed |= Type.Signed;
}

// The encompassing type must have a width greater than or equal to the width
// of the specified types. Aditionally, if the encompassing type is signed,
// its width must be strictly greater than the width of any unsigned types
// given.
unsigned Width = 0;
for (const auto &Type : Types) {
unsigned MinWidth = Type.Width + (Signed && !Type.Signed);
if (Width < MinWidth) {
Width = MinWidth;
}
}

return {Width, Signed};
}

Value *CodeGenFunction::EmitVAStartEnd(Value *ArgValue, bool IsStart) {
llvm::Type *DestType = Int8PtrTy;
if (ArgValue->getType() != DestType)
Expand Down Expand Up @@ -1606,6 +1650,88 @@ RValue CodeGenFunction::EmitBuiltinExpr(const FunctionDecl *FD,
Builder.CreateStore(CarryOut, CarryOutPtr);
return RValue::get(Sum2);
}

case Builtin::BI__builtin_add_overflow:
case Builtin::BI__builtin_sub_overflow:
case Builtin::BI__builtin_mul_overflow: {
const clang::Expr *LeftArg = E->getArg(0);
const clang::Expr *RightArg = E->getArg(1);
const clang::Expr *ResultArg = E->getArg(2);

clang::QualType ResultQTy =
ResultArg->getType()->castAs<PointerType>()->getPointeeType();

WidthAndSignedness LeftInfo =
getIntegerWidthAndSignedness(CGM.getContext(), LeftArg->getType());
WidthAndSignedness RightInfo =
getIntegerWidthAndSignedness(CGM.getContext(), RightArg->getType());
WidthAndSignedness ResultInfo =
getIntegerWidthAndSignedness(CGM.getContext(), ResultQTy);
WidthAndSignedness EncompassingInfo =
EncompassingIntegerType({LeftInfo, RightInfo, ResultInfo});

llvm::Type *EncompassingLLVMTy =
llvm::IntegerType::get(CGM.getLLVMContext(), EncompassingInfo.Width);

llvm::Type *ResultLLVMTy = CGM.getTypes().ConvertType(ResultQTy);

llvm::Intrinsic::ID IntrinsicId;
switch (BuiltinID) {
default:
llvm_unreachable("Unknown overflow builtin id.");
case Builtin::BI__builtin_add_overflow:
IntrinsicId = EncompassingInfo.Signed
? llvm::Intrinsic::sadd_with_overflow
: llvm::Intrinsic::uadd_with_overflow;
break;
case Builtin::BI__builtin_sub_overflow:
IntrinsicId = EncompassingInfo.Signed
? llvm::Intrinsic::ssub_with_overflow
: llvm::Intrinsic::usub_with_overflow;
break;
case Builtin::BI__builtin_mul_overflow:
IntrinsicId = EncompassingInfo.Signed
? llvm::Intrinsic::smul_with_overflow
: llvm::Intrinsic::umul_with_overflow;
break;
}

llvm::Value *Left = EmitScalarExpr(LeftArg);
llvm::Value *Right = EmitScalarExpr(RightArg);
Address ResultPtr = EmitPointerWithAlignment(ResultArg);

// Extend each operand to the encompassing type.
Left = Builder.CreateIntCast(Left, EncompassingLLVMTy, LeftInfo.Signed);
Right = Builder.CreateIntCast(Right, EncompassingLLVMTy, RightInfo.Signed);

// Perform the operation on the extended values.
llvm::Value *Overflow, *Result;
Result = EmitOverflowIntrinsic(*this, IntrinsicId, Left, Right, Overflow);

if (EncompassingInfo.Width > ResultInfo.Width) {
// The encompassing type is wider than the result type, so we need to
// truncate it.
llvm::Value *ResultTrunc = Builder.CreateTrunc(Result, ResultLLVMTy);

// To see if the truncation caused an overflow, we will extend
// the result and then compare it to the original result.
llvm::Value *ResultTruncExt = Builder.CreateIntCast(
ResultTrunc, EncompassingLLVMTy, ResultInfo.Signed);
llvm::Value *TruncationOverflow =
Builder.CreateICmpNE(Result, ResultTruncExt);

Overflow = Builder.CreateOr(Overflow, TruncationOverflow);
Result = ResultTrunc;
}

// Finally, store the result using the pointer.
bool isVolatile =
ResultArg->getType()->getPointeeType().isVolatileQualified();
Builder.CreateStore(EmitToMemory(Result, ResultQTy), ResultPtr, isVolatile);

return RValue::get(Overflow);
}

case Builtin::BI__builtin_uadd_overflow:
case Builtin::BI__builtin_uaddl_overflow:
case Builtin::BI__builtin_uaddll_overflow:
Expand Down Expand Up @@ -1635,7 +1761,7 @@ RValue CodeGenFunction::EmitBuiltinExpr(const FunctionDecl *FD,
// Decide which of the overflow intrinsics we are lowering to:
llvm::Intrinsic::ID IntrinsicId;
switch (BuiltinID) {
default: llvm_unreachable("Unknown security overflow builtin id.");
default: llvm_unreachable("Unknown overflow builtin id.");
case Builtin::BI__builtin_uadd_overflow:
case Builtin::BI__builtin_uaddl_overflow:
case Builtin::BI__builtin_uaddll_overflow:
Expand Down
39 changes: 39 additions & 0 deletions clang/lib/Sema/SemaChecking.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,39 @@ static bool SemaBuiltinAddressof(Sema &S, CallExpr *TheCall) {
return false;
}

static bool SemaBuiltinOverflow(Sema &S, CallExpr *TheCall) {
if (checkArgCount(S, TheCall, 3))
return true;

// First two arguments should be integers.
for (unsigned I = 0; I < 2; ++I) {
Expr *Arg = TheCall->getArg(I);
QualType Ty = Arg->getType();
if (!Ty->isIntegerType()) {
S.Diag(Arg->getLocStart(), diag::err_overflow_builtin_must_be_int)
<< Ty << Arg->getSourceRange();
return true;
}
}

// Third argument should be a pointer to a non-const integer.
// IRGen correctly handles volatile, restrict, and address spaces, and
// the other qualifiers aren't possible.
{
Expr *Arg = TheCall->getArg(2);
QualType Ty = Arg->getType();
const auto *PtrTy = Ty->getAs<PointerType>();
if (!(PtrTy && PtrTy->getPointeeType()->isIntegerType() &&
!PtrTy->getPointeeType().isConstQualified())) {
S.Diag(Arg->getLocStart(), diag::err_overflow_builtin_must_be_ptr_int)
<< Ty << Arg->getSourceRange();
return true;
}
}

return false;
}

static void SemaBuiltinMemChkCall(Sema &S, FunctionDecl *FDecl,
CallExpr *TheCall, unsigned SizeIdx,
unsigned DstSizeIdx) {
Expand Down Expand Up @@ -457,6 +490,12 @@ Sema::CheckBuiltinFunctionCall(FunctionDecl *FDecl, unsigned BuiltinID,
if (SemaBuiltinAddressof(*this, TheCall))
return ExprError();
break;
case Builtin::BI__builtin_add_overflow:
case Builtin::BI__builtin_sub_overflow:
case Builtin::BI__builtin_mul_overflow:
if (SemaBuiltinOverflow(*this, TheCall))
return ExprError();
break;
case Builtin::BI__builtin_operator_new:
case Builtin::BI__builtin_operator_delete:
if (!getLangOpts().CPlusPlus) {
Expand Down
Loading

0 comments on commit 03107a4

Please sign in to comment.