Skip to content

Commit 1783185

Browse files
artemAaronBallman
authored andcommitted
Respect integer overflow handling in abs builtin
Currenly both Clang and GCC support the following set of flags that control code gen of signed overflow: * -fwrapv: overflow is defined as in two-complement * -ftrapv: overflow traps * -fsanitize=signed-integer-overflow: if undefined (no -fwrapv), then overflow behaviour is controlled by UBSan runtime, overrides -ftrapv. However, clang ignores these flags for __builtin_abs(int) and its higher-width versions, so passing minimum integer value always causes poison. The same holds for *abs(), which are not handled in frontend at all but folded to llvm.abs.* intrinsics during InstCombinePass. The intrinsics are not instrumented by UBSan, so the functions need special handling as well. This patch does a few things: * Handle *abs() in CGBuiltin the same way as __builtin_*abs() * -fsanitize=signed-integer-overflow now properly instruments abs() with UBSan * -fwrapv and -ftrapv handling for abs() is made consistent with GCC Fixes llvm#45129 Fixes llvm#45794 Differential Revision: https://reviews.llvm.org/D156821
1 parent 21e7f73 commit 1783185

File tree

4 files changed

+146
-7
lines changed

4 files changed

+146
-7
lines changed

clang/docs/ReleaseNotes.rst

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,10 @@ Bug Fixes in This Version
152152
- Fix a hang on valid C code passing a function type as an argument to
153153
``typeof`` to form a function declaration.
154154
(`#64713 <https://github.com/llvm/llvm-project/issues/64713>_`)
155+
- Clang now respects ``-fwrapv`` and ``-ftrapv`` for ``__builtin_abs`` and
156+
``abs`` builtins.
157+
(`#45129 <https://github.com/llvm/llvm-project/issues/45129>`_,
158+
`#45794 <https://github.com/llvm/llvm-project/issues/45794>`_)
155159

156160
Bug Fixes to Compiler Builtins
157161
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -283,6 +287,9 @@ Static Analyzer
283287

284288
Sanitizers
285289
----------
290+
- ``-fsanitize=signed-integer-overflow`` now instruments ``__builtin_abs`` and
291+
``abs`` builtins.
292+
286293

287294
Python Binding Changes
288295
----------------------

clang/lib/CodeGen/CGBuiltin.cpp

Lines changed: 63 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1768,6 +1768,49 @@ Value *CodeGenFunction::EmitCheckedArgForBuiltin(const Expr *E,
17681768
return ArgValue;
17691769
}
17701770

1771+
static Value *EmitAbs(CodeGenFunction &CGF, Value *ArgValue, bool HasNSW) {
1772+
// X < 0 ? -X : X
1773+
// TODO: Use phi-node (for better SimplifyCFGPass)
1774+
Value *NegOp = CGF.Builder.CreateNeg(ArgValue, "neg", false, HasNSW);
1775+
Constant *Zero = llvm::Constant::getNullValue(ArgValue->getType());
1776+
Value *CmpResult = CGF.Builder.CreateICmpSLT(ArgValue, Zero, "abscond");
1777+
return CGF.Builder.CreateSelect(CmpResult, NegOp, ArgValue, "abs");
1778+
}
1779+
1780+
static Value *EmitOverflowCheckedAbs(CodeGenFunction &CGF, const CallExpr *E,
1781+
bool SanitizeOverflow) {
1782+
Value *ArgValue = CGF.EmitScalarExpr(E->getArg(0));
1783+
1784+
// Try to eliminate overflow check.
1785+
if (const auto *VCI = dyn_cast<llvm::ConstantInt>(ArgValue)) {
1786+
if (!VCI->isMinSignedValue()) {
1787+
return EmitAbs(CGF, ArgValue, true);
1788+
}
1789+
}
1790+
1791+
CodeGenFunction::SanitizerScope SanScope(&CGF);
1792+
1793+
Constant *Zero = Constant::getNullValue(ArgValue->getType());
1794+
Value *ResultAndOverflow = CGF.Builder.CreateBinaryIntrinsic(
1795+
Intrinsic::ssub_with_overflow, Zero, ArgValue);
1796+
Value *Result = CGF.Builder.CreateExtractValue(ResultAndOverflow, 0);
1797+
Value *NotOverflow = CGF.Builder.CreateNot(
1798+
CGF.Builder.CreateExtractValue(ResultAndOverflow, 1));
1799+
1800+
// TODO: support -ftrapv-handler.
1801+
if (SanitizeOverflow) {
1802+
CGF.EmitCheck({{NotOverflow, SanitizerKind::SignedIntegerOverflow}},
1803+
SanitizerHandler::NegateOverflow,
1804+
{CGF.EmitCheckSourceLocation(E->getArg(0)->getExprLoc()),
1805+
CGF.EmitCheckTypeDescriptor(E->getType())},
1806+
{ArgValue});
1807+
} else
1808+
CGF.EmitTrapCheck(NotOverflow, SanitizerHandler::SubOverflow);
1809+
1810+
Value *CmpResult = CGF.Builder.CreateICmpSLT(ArgValue, Zero, "abscond");
1811+
return CGF.Builder.CreateSelect(CmpResult, Result, ArgValue, "abs");
1812+
}
1813+
17711814
/// Get the argument type for arguments to os_log_helper.
17721815
static CanQualType getOSLogArgType(ASTContext &C, int Size) {
17731816
QualType UnsignedTy = C.getIntTypeForBitwidth(Size * 8, /*Signed=*/false);
@@ -2626,16 +2669,29 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID,
26262669
Builder.CreateCall(CGM.getIntrinsic(Intrinsic::vacopy), {DstPtr, SrcPtr});
26272670
return RValue::get(nullptr);
26282671
}
2672+
case Builtin::BIabs:
2673+
case Builtin::BIlabs:
2674+
case Builtin::BIllabs:
26292675
case Builtin::BI__builtin_abs:
26302676
case Builtin::BI__builtin_labs:
26312677
case Builtin::BI__builtin_llabs: {
2632-
// X < 0 ? -X : X
2633-
// The negation has 'nsw' because abs of INT_MIN is undefined.
2634-
Value *ArgValue = EmitScalarExpr(E->getArg(0));
2635-
Value *NegOp = Builder.CreateNSWNeg(ArgValue, "neg");
2636-
Constant *Zero = llvm::Constant::getNullValue(ArgValue->getType());
2637-
Value *CmpResult = Builder.CreateICmpSLT(ArgValue, Zero, "abscond");
2638-
Value *Result = Builder.CreateSelect(CmpResult, NegOp, ArgValue, "abs");
2678+
bool SanitizeOverflow = SanOpts.has(SanitizerKind::SignedIntegerOverflow);
2679+
2680+
Value *Result;
2681+
switch (getLangOpts().getSignedOverflowBehavior()) {
2682+
case LangOptions::SOB_Defined:
2683+
Result = EmitAbs(*this, EmitScalarExpr(E->getArg(0)), false);
2684+
break;
2685+
case LangOptions::SOB_Undefined:
2686+
if (!SanitizeOverflow) {
2687+
Result = EmitAbs(*this, EmitScalarExpr(E->getArg(0)), true);
2688+
break;
2689+
}
2690+
[[fallthrough]];
2691+
case LangOptions::SOB_Trapping:
2692+
Result = EmitOverflowCheckedAbs(*this, E, SanitizeOverflow);
2693+
break;
2694+
}
26392695
return RValue::get(Result);
26402696
}
26412697
case Builtin::BI__builtin_complex: {

clang/test/CodeGen/abs-overflow.c

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
// RUN: %clang_cc1 -triple x86_64-unknown-unknown -emit-llvm %s -o - -fwrapv -fsanitize=signed-integer-overflow | FileCheck %s --check-prefix=WRAPV
2+
// RUN: %clang_cc1 -triple x86_64-unknown-unknown -emit-llvm %s -o - -ftrapv | FileCheck %s --check-prefixes=BOTH-TRAP,TRAPV
3+
// RUN: %clang_cc1 -triple x86_64-unknown-unknown -emit-llvm %s -o - -fsanitize=signed-integer-overflow | FileCheck %s --check-prefixes=BOTH-TRAP,CATCH_UB
4+
// COM: TODO: Support -ftrapv-handler.
5+
6+
extern int abs(int x);
7+
8+
int absi(int x) {
9+
// WRAPV: [[NEG:%.*]] = sub i32 0, [[X:%.*]]
10+
// WRAPV: [[CMP:%.*]] = icmp slt i32 [[X]], 0
11+
// WRAPV: [[SEL:%.*]] = select i1 [[CMP]], i32 [[NEG]], i32 [[X]]
12+
//
13+
// BOTH-TRAP: [[NEG:%.*]] = call { i32, i1 } @llvm.ssub.with.overflow.i32(i32 0, i32 [[X:%.*]])
14+
// BOTH-TRAP: [[NEGV:%.*]] = extractvalue { i32, i1 } [[NEG]], 0
15+
// BOTH-TRAP: [[OFL:%.*]] = extractvalue { i32, i1 } [[NEG]], 1
16+
// BOTH-TRAP: [[NOFL:%.*]] = xor i1 [[OFL]], true
17+
// BOTH-TRAP: br i1 [[NOFL]], label %[[CONT:.*]], label %[[TRAP:[a-zA-Z_.]*]]
18+
// BOTH-TRAP: [[TRAP]]:
19+
// TRAPV-NEXT: llvm.ubsantrap
20+
// CATCH_UB: @__ubsan_handle_negate_overflow
21+
// BOTH-TRAP-NEXT: unreachable
22+
// BOTH-TRAP: [[CONT]]:
23+
// BOTH-TRAP-NEXT: [[ABSCOND:%.*]] = icmp slt i32 [[X]], 0
24+
// BOTH-TRAP-NEXT: select i1 [[ABSCOND]], i32 [[NEGV]], i32 [[X]]
25+
return abs(x);
26+
}
27+
28+
int babsi(int x) {
29+
// WRAPV: [[NEG:%.*]] = sub i32 0, [[X:%.*]]
30+
// WRAPV: [[CMP:%.*]] = icmp slt i32 [[X]], 0
31+
// WRAPV: [[SEL:%.*]] = select i1 [[CMP]], i32 [[NEG]], i32 [[X]]
32+
//
33+
// BOTH-TRAP: [[NEG:%.*]] = call { i32, i1 } @llvm.ssub.with.overflow.i32(i32 0, i32 [[X:%.*]])
34+
// BOTH-TRAP: [[NEGV:%.*]] = extractvalue { i32, i1 } [[NEG]], 0
35+
// BOTH-TRAP: [[OFL:%.*]] = extractvalue { i32, i1 } [[NEG]], 1
36+
// BOTH-TRAP: [[NOFL:%.*]] = xor i1 [[OFL]], true
37+
// BOTH-TRAP: br i1 [[NOFL]], label %[[CONT:.*]], label %[[TRAP:[a-zA-Z_.]*]]
38+
// BOTH-TRAP: [[TRAP]]:
39+
// TRAPV-NEXT: llvm.ubsantrap
40+
// CATCH_UB: @__ubsan_handle_negate_overflow
41+
// BOTH-TRAP-NEXT: unreachable
42+
// BOTH-TRAP: [[CONT]]:
43+
// BOTH-TRAP-NEXT: [[ABSCOND:%.*]] = icmp slt i32 [[X]], 0
44+
// BOTH-TRAP-NEXT: select i1 [[ABSCOND]], i32 [[NEGV]], i32 [[X]]
45+
return __builtin_abs(x);
46+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
// REQUIRES: arch=x86_64
2+
//
3+
// RUN: %clangxx -fsanitize=signed-integer-overflow -w %s -O3 -o %t
4+
// RUN: %run %t 2>&1 | FileCheck %s --check-prefix=RECOVER
5+
//
6+
// RUN: %clangxx -fsanitize=signed-integer-overflow -fno-sanitize-recover=signed-integer-overflow -w %s -O3 -o %t.abort
7+
// RUN: not %run %t.abort 2>&1 | FileCheck %s --check-prefix=ABORT
8+
9+
#include <limits.h>
10+
#include <stdlib.h>
11+
12+
int main() {
13+
// ABORT: abs.cpp:[[@LINE+3]]:17: runtime error: negation of -{{[0-9]+}} cannot be represented in type 'int'; cast to an unsigned type to negate this value to itself
14+
// RECOVER: abs.cpp:[[@LINE+2]]:17: runtime error: negation of -{{[0-9]+}} cannot be represented in type 'int'; cast to an unsigned type to negate this value to itself
15+
// RECOVER: abs.cpp:[[@LINE+2]]:7: runtime error: negation of -{{[0-9]+}} cannot be represented in type 'int'; cast to an unsigned type to negate this value to itself
16+
__builtin_abs(INT_MIN);
17+
abs(INT_MIN);
18+
19+
// RECOVER: abs.cpp:[[@LINE+2]]:18: runtime error: negation of -{{[0-9]+}} cannot be represented in type 'long'; cast to an unsigned type to negate this value to itself
20+
// RECOVER: abs.cpp:[[@LINE+2]]:8: runtime error: negation of -{{[0-9]+}} cannot be represented in type 'long'; cast to an unsigned type to negate this value to itself
21+
__builtin_labs(LONG_MIN);
22+
labs(LONG_MIN);
23+
24+
// RECOVER: abs.cpp:[[@LINE+2]]:19: runtime error: negation of -{{[0-9]+}} cannot be represented in type 'long long'; cast to an unsigned type to negate this value to itself
25+
// RECOVER: abs.cpp:[[@LINE+2]]:9: runtime error: negation of -{{[0-9]+}} cannot be represented in type 'long long'; cast to an unsigned type to negate this value to itself
26+
__builtin_llabs(LLONG_MIN);
27+
llabs(LLONG_MIN);
28+
29+
return 0;
30+
}

0 commit comments

Comments
 (0)