[cfe-commits] [PATCH RFC] Emit nsw/nuw flags for shl

Xi Wang xi.wang at gmail.com
Sun Dec 30 16:30:55 PST 2012


The attached patch adds nsw/nuw flags to signed left shift.  This is
useful for LLVM-based tools to distinguish between signed and unsigned
left shift operations.

The patch is also available at:

  https://github.com/xiw/clang/compare/shl

BTW, I am confused by some comments in ScalarExprEmitter::EmitShl().

  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).

It looks to me that both C99 (6.5.7p4) and C++11 (5.8p2) have the same
rules for signed left shift.

  If E1 has a signed type and non-negative value, and E1 * 2^E2 is
  representable in the result type, then that is the resulting value;
  otherwise, the behavior is undefined.

- xi
-------------- next part --------------
diff --git a/lib/CodeGen/CGExprScalar.cpp b/lib/CodeGen/CGExprScalar.cpp
index 61126e1..92551e1 100644
--- a/lib/CodeGen/CGExprScalar.cpp
+++ b/lib/CodeGen/CGExprScalar.cpp
@@ -2388,6 +2388,17 @@ Value *ScalarExprEmitter::EmitShl(const BinOpInfo &Ops) {
   if (Ops.LHS->getType() != RHS->getType())
     RHS = Builder.CreateIntCast(RHS, Ops.LHS->getType(), false, "sh_prom");
 
+  bool HasNSW = false;
+  bool HasNUW = false;
+  // C99 6.5.7p4, C++11 5.8p2:
+  //   If E1 has a signed type and non-negative value, and E1 * 2^E2 is
+  //   representable in the result type, then that is the resulting value;
+  //   otherwise, the behavior is undefined.
+  if (Ops.Ty->isSignedIntegerType()) {
+    HasNSW = true;
+    HasNUW = true;
+  }
+
   if (CGF.getLangOpts().SanitizeShift &&
       isa<llvm::IntegerType>(Ops.LHS->getType())) {
     unsigned Width = cast<llvm::IntegerType>(Ops.LHS->getType())->getBitWidth();
@@ -2418,7 +2429,7 @@ Value *ScalarExprEmitter::EmitShl(const BinOpInfo &Ops) {
     }
   }
 
-  return Builder.CreateShl(Ops.LHS, RHS, "shl");
+  return Builder.CreateShl(Ops.LHS, RHS, "shl", HasNUW, HasNSW);
 }
 
 Value *ScalarExprEmitter::EmitShr(const BinOpInfo &Ops) {
diff --git a/test/CodeGen/catch-undef-behavior.c b/test/CodeGen/catch-undef-behavior.c
index d0b6d19..aa3f270 100644
--- a/test/CodeGen/catch-undef-behavior.c
+++ b/test/CodeGen/catch-undef-behavior.c
@@ -91,7 +91,7 @@ int lsh_overflow(int a, int b) {
   // CHECK-NEXT: %[[ARG2:.*]] = zext
   // CHECK-NEXT: call void @__ubsan_handle_shift_out_of_bounds(i8* bitcast ({{.*}} @[[LINE_300_B]] to i8*), i64 %[[ARG1]], i64 %[[ARG2]])
 
-  // CHECK:      %[[RET:.*]] = shl i32 %[[LHS]], %[[RHS]]
+  // CHECK:      %[[RET:.*]] = shl nuw nsw i32 %[[LHS]], %[[RHS]]
   // CHECK-NEXT: ret i32 %[[RET]]
 #line 300
   return a << b;
diff --git a/test/CodeGen/compound-type.c b/test/CodeGen/compound-type.c
index 63ba694..d6a2f5a 100644
--- a/test/CodeGen/compound-type.c
+++ b/test/CodeGen/compound-type.c
@@ -1,6 +1,6 @@
 // RUN: %clang_cc1 < %s -emit-llvm -triple i686-pc-linux-gnu > %t
 // RUN: grep "div i32" %t
-// RUN: grep "shl i32" %t
+// RUN: grep "shl nuw nsw i32" %t
 
 unsigned char a,b;
 void c(void) {a <<= b;}
diff --git a/test/CodeGenCXX/catch-undef-behavior.cpp b/test/CodeGenCXX/catch-undef-behavior.cpp
index 3a3536b..d9b4408 100644
--- a/test/CodeGenCXX/catch-undef-behavior.cpp
+++ b/test/CodeGenCXX/catch-undef-behavior.cpp
@@ -130,7 +130,7 @@ int lsh_overflow(int a, int b) {
   // 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: %[[RET:.*]] = shl nuw nsw i32 %[[LHS]], %[[RHS]]
   // CHECK-NEXT: ret i32 %[[RET]]
   return a << b;
 }


More information about the cfe-commits mailing list