]> granicus.if.org Git - clang/commitdiff
-fcatch-undefined-behavior: add the -ftrapv checks to the set of things caught
authorRichard Smith <richard-llvm@metafoo.co.uk>
Sat, 25 Aug 2012 00:32:28 +0000 (00:32 +0000)
committerRichard Smith <richard-llvm@metafoo.co.uk>
Sat, 25 Aug 2012 00:32:28 +0000 (00:32 +0000)
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

lib/CodeGen/CGExprScalar.cpp
test/CodeGen/catch-undef-behavior.c
test/CodeGen/integer-overflow.c
test/CodeGen/trapv.c
test/CodeGenCXX/catch-undef-behavior.cpp

index 70d8bd77045945b24f84d97f88befa4e35c82f71..dd5af526eb859a762b77bd87c6a63ed1eaa383c5 100644 (file)
@@ -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,
index 8c45fa5045690f01fb8d815c9ff226fee150fbf4..5832b6c830f4b4aade0c219936eff4565a848617 100644 (file)
@@ -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;
+}
index d7fff4ee4a2a461f2063a86bf84a34be03f82e89..176ee65ff5ddd58f343d53748c3e8861ff0e6704 100644 (file)
@@ -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;
 }
index f52dad55648586903c4d224bdfdec2f22419e8ba..abad5850b4c45bdc58bf7c4418953b843ea4198d 100644 (file)
@@ -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;
index ed0a7763f51b9af87fe0760fdae26a12af710ed8..0ec1cc1e05266f6402ca1caa1c54332ae32660c2 100644 (file)
@@ -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;
+}