-fcatch-undefined-behavior: add the -ftrapv checks to the set of things caught

by this mode, and also check for signed left shift overflow. The rules for the
latter are a little subtle:

 * neither C89 nor C++98 specify the behavior of a signed left shift at all
 * in C99 and C11, shifting a 1 bit into the sign bit has undefined behavior
 * in C++11, with core issue 1457, shifting a 1 bit *out* of the sign bit has
   undefined behavior

As of this change, we use the C99 rules for all C language variants, and the
C++11 rules for all C++ language variants. Once we have individual
-fcatch-undefined-behavior= flags, this should be revisited.


git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@162634 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
Richard Smith 2012-08-25 00:32:28 +00:00
Родитель df5faf5e7a
Коммит 9d3e226aca
5 изменённых файлов: 104 добавлений и 18 удалений

Просмотреть файл

@ -394,10 +394,12 @@ public:
Value *EmitMul(const BinOpInfo &Ops) {
if (Ops.Ty->isSignedIntegerOrEnumerationType()) {
switch (CGF.getContext().getLangOpts().getSignedOverflowBehavior()) {
case LangOptions::SOB_Undefined:
return Builder.CreateNSWMul(Ops.LHS, Ops.RHS, "mul");
case LangOptions::SOB_Defined:
return Builder.CreateMul(Ops.LHS, Ops.RHS, "mul");
case LangOptions::SOB_Undefined:
if (!CGF.CatchUndefined)
return Builder.CreateNSWMul(Ops.LHS, Ops.RHS, "mul");
// Fall through.
case LangOptions::SOB_Trapping:
return EmitOverflowCheckedBinOp(Ops);
}
@ -408,8 +410,8 @@ public:
return Builder.CreateMul(Ops.LHS, Ops.RHS, "mul");
}
bool isTrapvOverflowBehavior() {
return CGF.getContext().getLangOpts().getSignedOverflowBehavior()
== LangOptions::SOB_Trapping;
return CGF.getContext().getLangOpts().getSignedOverflowBehavior()
== LangOptions::SOB_Trapping || CGF.CatchUndefined;
}
/// Create a binary op that checks for overflow.
/// Currently only supports +, - and *.
@ -1251,10 +1253,12 @@ EmitAddConsiderOverflowBehavior(const UnaryOperator *E,
llvm::Value *InVal,
llvm::Value *NextVal, bool IsInc) {
switch (CGF.getContext().getLangOpts().getSignedOverflowBehavior()) {
case LangOptions::SOB_Undefined:
return Builder.CreateNSWAdd(InVal, NextVal, IsInc ? "inc" : "dec");
case LangOptions::SOB_Defined:
return Builder.CreateAdd(InVal, NextVal, IsInc ? "inc" : "dec");
case LangOptions::SOB_Undefined:
if (!CGF.CatchUndefined)
return Builder.CreateNSWAdd(InVal, NextVal, IsInc ? "inc" : "dec");
// Fall through.
case LangOptions::SOB_Trapping:
BinOpInfo BinOp;
BinOp.LHS = InVal;
@ -2010,10 +2014,12 @@ Value *ScalarExprEmitter::EmitAdd(const BinOpInfo &op) {
if (op.Ty->isSignedIntegerOrEnumerationType()) {
switch (CGF.getContext().getLangOpts().getSignedOverflowBehavior()) {
case LangOptions::SOB_Undefined:
return Builder.CreateNSWAdd(op.LHS, op.RHS, "add");
case LangOptions::SOB_Defined:
return Builder.CreateAdd(op.LHS, op.RHS, "add");
case LangOptions::SOB_Undefined:
if (!CGF.CatchUndefined)
return Builder.CreateNSWAdd(op.LHS, op.RHS, "add");
// Fall through.
case LangOptions::SOB_Trapping:
return EmitOverflowCheckedBinOp(op);
}
@ -2030,10 +2036,12 @@ Value *ScalarExprEmitter::EmitSub(const BinOpInfo &op) {
if (!op.LHS->getType()->isPointerTy()) {
if (op.Ty->isSignedIntegerOrEnumerationType()) {
switch (CGF.getContext().getLangOpts().getSignedOverflowBehavior()) {
case LangOptions::SOB_Undefined:
return Builder.CreateNSWSub(op.LHS, op.RHS, "sub");
case LangOptions::SOB_Defined:
return Builder.CreateSub(op.LHS, op.RHS, "sub");
case LangOptions::SOB_Undefined:
if (!CGF.CatchUndefined)
return Builder.CreateNSWSub(op.LHS, op.RHS, "sub");
// Fall through.
case LangOptions::SOB_Trapping:
return EmitOverflowCheckedBinOp(op);
}
@ -2110,14 +2118,38 @@ Value *ScalarExprEmitter::EmitShl(const BinOpInfo &Ops) {
if (Ops.LHS->getType() != RHS->getType())
RHS = Builder.CreateIntCast(RHS, Ops.LHS->getType(), false, "sh_prom");
if (CGF.CatchUndefined
&& isa<llvm::IntegerType>(Ops.LHS->getType())) {
if (CGF.CatchUndefined && isa<llvm::IntegerType>(Ops.LHS->getType())) {
unsigned Width = cast<llvm::IntegerType>(Ops.LHS->getType())->getBitWidth();
llvm::BasicBlock *Cont = CGF.createBasicBlock("cont");
CGF.Builder.CreateCondBr(Builder.CreateICmpULT(RHS,
llvm::ConstantInt::get(RHS->getType(), Width)),
Cont, CGF.getTrapBB());
llvm::BasicBlock *Cont = CGF.createBasicBlock("shl.cont");
llvm::BasicBlock *Trap = CGF.getTrapBB();
llvm::Value *WidthMinusOne =
llvm::ConstantInt::get(RHS->getType(), Width - 1, "shl.width");
CGF.Builder.CreateCondBr(Builder.CreateICmpULE(RHS, WidthMinusOne),
Cont, Trap);
CGF.EmitBlock(Cont);
if (Ops.Ty->hasSignedIntegerRepresentation()) {
// Check whether we are shifting any non-zero bits off the top of the
// integer.
Cont = CGF.createBasicBlock("shl.ok");
llvm::Value *BitsShiftedOff =
Builder.CreateLShr(Ops.LHS,
Builder.CreateSub(WidthMinusOne, RHS, "shl.zeros",
/*NUW*/true, /*NSW*/true),
"shl.check");
if (CGF.getLangOpts().CPlusPlus) {
// In C99, we are not permitted to shift a 1 bit into the sign bit.
// Under C++11's rules, shifting a 1 bit into the sign bit is
// OK, but shifting a 1 bit out of it is not. (C89 and C++03 don't
// define signed left shifts, so we use the C99 and C++11 rules there).
llvm::Value *One = llvm::ConstantInt::get(BitsShiftedOff->getType(), 1);
BitsShiftedOff = Builder.CreateLShr(BitsShiftedOff, One);
}
llvm::Value *Zero = llvm::ConstantInt::get(BitsShiftedOff->getType(), 0);
Builder.CreateCondBr(Builder.CreateICmpEQ(BitsShiftedOff, Zero),
Cont, Trap);
CGF.EmitBlock(Cont);
}
}
return Builder.CreateShl(Ops.LHS, RHS, "shl");
@ -2130,8 +2162,7 @@ Value *ScalarExprEmitter::EmitShr(const BinOpInfo &Ops) {
if (Ops.LHS->getType() != RHS->getType())
RHS = Builder.CreateIntCast(RHS, Ops.LHS->getType(), false, "sh_prom");
if (CGF.CatchUndefined
&& isa<llvm::IntegerType>(Ops.LHS->getType())) {
if (CGF.CatchUndefined && isa<llvm::IntegerType>(Ops.LHS->getType())) {
unsigned Width = cast<llvm::IntegerType>(Ops.LHS->getType())->getBitWidth();
llvm::BasicBlock *Cont = CGF.createBasicBlock("cont");
CGF.Builder.CreateCondBr(Builder.CreateICmpULT(RHS,

Просмотреть файл

@ -19,3 +19,28 @@ int bar(int *a) {
// CHECK-NEXT: icmp eq i64 %[[MISALIGN]], 0
return *a;
}
// CHECK: @lsh_overflow
int lsh_overflow(int a, int b) {
// CHECK: %[[INBOUNDS:.*]] = icmp ule i32 %[[RHS:.*]], 31
// CHECK-NEXT: br i1 %[[INBOUNDS]]
// CHECK: %[[SHIFTED_OUT_WIDTH:.*]] = sub nuw nsw i32 31, %[[RHS]]
// CHECK-NEXT: %[[SHIFTED_OUT:.*]] = lshr i32 %[[LHS:.*]], %[[SHIFTED_OUT_WIDTH]]
// CHECK-NEXT: %[[NO_OVERFLOW:.*]] = icmp eq i32 %[[SHIFTED_OUT]], 0
// CHECK-NEXT: br i1 %[[NO_OVERFLOW]]
// CHECK: %[[RET:.*]] = shl i32 %[[LHS]], %[[RHS]]
// CHECK-NEXT: ret i32 %[[RET]]
return a << b;
}
// CHECK: @rsh_inbounds
int rsh_inbounds(int a, int b) {
// CHECK: %[[INBOUNDS:.*]] = icmp ult i32 %[[RHS:.*]], 32
// CHECK: br i1 %[[INBOUNDS]]
// CHECK: %[[RET:.*]] = ashr i32 %[[LHS]], %[[RHS]]
// CHECK-NEXT: ret i32 %[[RET]]
return a >> b;
}

Просмотреть файл

@ -1,6 +1,7 @@
// RUN: %clang_cc1 -triple x86_64-apple-darwin %s -emit-llvm -o - | FileCheck %s --check-prefix=DEFAULT
// RUN: %clang_cc1 -triple x86_64-apple-darwin %s -emit-llvm -o - -fwrapv | FileCheck %s --check-prefix=WRAPV
// RUN: %clang_cc1 -triple x86_64-apple-darwin %s -emit-llvm -o - -ftrapv | FileCheck %s --check-prefix=TRAPV
// RUN: %clang_cc1 -triple x86_64-apple-darwin %s -emit-llvm -o - -fcatch-undefined-behavior | FileCheck %s --check-prefix=CATCH_UB
// RUN: %clang_cc1 -triple x86_64-apple-darwin %s -emit-llvm -o - -ftrapv -ftrapv-handler foo | FileCheck %s --check-prefix=TRAPV_HANDLER
@ -15,24 +16,28 @@ void test1() {
// DEFAULT: add nsw i32
// WRAPV: add i32
// TRAPV: llvm.sadd.with.overflow.i32
// CATCH_UB: llvm.sadd.with.overflow.i32
// TRAPV_HANDLER: foo(
f11G = a + b;
// DEFAULT: sub nsw i32
// WRAPV: sub i32
// TRAPV: llvm.ssub.with.overflow.i32
// CATCH_UB: llvm.ssub.with.overflow.i32
// TRAPV_HANDLER: foo(
f11G = a - b;
// DEFAULT: mul nsw i32
// WRAPV: mul i32
// TRAPV: llvm.smul.with.overflow.i32
// CATCH_UB: llvm.smul.with.overflow.i32
// TRAPV_HANDLER: foo(
f11G = a * b;
// DEFAULT: sub nsw i32 0,
// WRAPV: sub i32 0,
// TRAPV: llvm.ssub.with.overflow.i32(i32 0
// CATCH_UB: llvm.ssub.with.overflow.i32(i32 0
// TRAPV_HANDLER: foo(
f11G = -a;
@ -41,12 +46,14 @@ void test1() {
// DEFAULT: add nsw i32 {{.*}}, 1
// WRAPV: add i32 {{.*}}, 1
// TRAPV: llvm.sadd.with.overflow.i32({{.*}}, i32 1)
// CATCH_UB: llvm.sadd.with.overflow.i32({{.*}}, i32 1)
// TRAPV_HANDLER: foo(
++a;
// DEFAULT: add nsw i32 {{.*}}, -1
// WRAPV: add i32 {{.*}}, -1
// TRAPV: llvm.sadd.with.overflow.i32({{.*}}, i32 -1)
// CATCH_UB: llvm.sadd.with.overflow.i32({{.*}}, i32 -1)
// TRAPV_HANDLER: foo(
--a;
@ -56,11 +63,13 @@ void test1() {
// DEFAULT: getelementptr inbounds i32*
// WRAPV: getelementptr i32*
// TRAPV: getelementptr inbounds i32*
// CATCH_UB: getelementptr inbounds i32*
// PR9350: char increment never overflows.
extern volatile signed char PR9350;
// DEFAULT: add i8 {{.*}}, 1
// WRAPV: add i8 {{.*}}, 1
// TRAPV: add i8 {{.*}}, 1
// CATCH_UB: add i8 {{.*}}, 1
++PR9350;
}

Просмотреть файл

@ -1,4 +1,5 @@
// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -ftrapv %s -emit-llvm -o - | FileCheck %s
// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fcatch-undefined-behavior %s -emit-llvm -o - | FileCheck %s
unsigned int ui, uj, uk;
int i, j, k;

Просмотреть файл

@ -66,3 +66,23 @@ void member_access(S *p) {
// CHECK-NEXT: icmp eq i64 %[[MISALIGN]], 0
k = p->f();
}
// CHECK: @_Z12lsh_overflow
int lsh_overflow(int a, int b) {
// CHECK: %[[INBOUNDS:.*]] = icmp ule i32 %[[RHS:.*]], 31
// CHECK-NEXT: br i1 %[[INBOUNDS]]
// CHECK: %[[SHIFTED_OUT_WIDTH:.*]] = sub nuw nsw i32 31, %[[RHS]]
// CHECK-NEXT: %[[SHIFTED_OUT:.*]] = lshr i32 %[[LHS:.*]], %[[SHIFTED_OUT_WIDTH]]
// This is present for C++11 but not for C: C++ core issue 1457 allows a '1'
// to be shifted into the sign bit, but not out of it.
// CHECK-NEXT: %[[SHIFTED_OUT_NOT_SIGN:.*]] = lshr i32 %[[SHIFTED_OUT]], 1
// CHECK-NEXT: %[[NO_OVERFLOW:.*]] = icmp eq i32 %[[SHIFTED_OUT_NOT_SIGN]], 0
// CHECK-NEXT: br i1 %[[NO_OVERFLOW]]
// CHECK: %[[RET:.*]] = shl i32 %[[LHS]], %[[RHS]]
// CHECK-NEXT: ret i32 %[[RET]]
return a << b;
}