]> granicus.if.org Git - clang/commitdiff
Scalar shifts in the OpenCL specification (as of v. 1.2) are defined to be
authorDavid Tweed <david.tweed@arm.com>
Mon, 7 Jan 2013 16:43:27 +0000 (16:43 +0000)
committerDavid Tweed <david.tweed@arm.com>
Mon, 7 Jan 2013 16:43:27 +0000 (16:43 +0000)
with respect to the lower "left-hand-side bitwidth" bits, even when negative);
see OpenCL spec 6.3j. This patch both implements this behaviour in the code
generator and "constant folding" bits of Sema, and also prevents tests
to detect undefinedness in terms of the weaker C99 or C++ specifications
from being applied.

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@171755 91177308-0d34-0410-b5e6-96231b3b80d8

lib/AST/ExprConstant.cpp
lib/CodeGen/CGExprScalar.cpp
lib/Sema/SemaExpr.cpp
test/CodeGen/catch-undef-behavior.c
test/CodeGenOpenCL/shifts.cl [new file with mode: 0644]
test/Sema/shiftOpenCL.cl [new file with mode: 0644]

index a472951ab6734f3836a13e6531db29dba84d8914..338cd74b32a17280d90fdc990eb8b5993f32c094 100644 (file)
@@ -4708,9 +4708,14 @@ bool DataRecursiveIntBinOpEvaluator::
       return Success(E->getOpcode() == BO_Rem ? LHS % RHS : LHS / RHS, E,
                      Result);
     case BO_Shl: {
-      // During constant-folding, a negative shift is an opposite shift. Such
-      // a shift is not a constant expression.
-      if (RHS.isSigned() && RHS.isNegative()) {
+      if (Info.getLangOpts().OpenCL)
+        // OpenCL 6.3j: shift values are effectively % word size of LHS.
+        RHS &= APSInt(llvm::APInt(LHS.getBitWidth(),
+                      static_cast<uint64_t>(LHS.getBitWidth() - 1)),
+                      RHS.isUnsigned());
+      else if (RHS.isSigned() && RHS.isNegative()) {
+        // During constant-folding, a negative shift is an opposite shift. Such
+        // a shift is not a constant expression.
         CCEDiag(E, diag::note_constexpr_negative_shift) << RHS;
         RHS = -RHS;
         goto shift_right;
@@ -4735,9 +4740,14 @@ bool DataRecursiveIntBinOpEvaluator::
       return Success(LHS << SA, E, Result);
     }
     case BO_Shr: {
-      // During constant-folding, a negative shift is an opposite shift. Such a
-      // shift is not a constant expression.
-      if (RHS.isSigned() && RHS.isNegative()) {
+      if (Info.getLangOpts().OpenCL)
+        // OpenCL 6.3j: shift values are effectively % word size of LHS.
+        RHS &= APSInt(llvm::APInt(LHS.getBitWidth(),
+                      static_cast<uint64_t>(LHS.getBitWidth() - 1)),
+                      RHS.isUnsigned());
+      else if (RHS.isSigned() && RHS.isNegative()) {
+        // During constant-folding, a negative shift is an opposite shift. Such a
+        // shift is not a constant expression.
         CCEDiag(E, diag::note_constexpr_negative_shift) << RHS;
         RHS = -RHS;
         goto shift_left;
index eb62343bc668c6db8f13af03bdd03574c2bb9b24..7ec4bc2297e7b2ec1b31d543c6b48045f303fee9 100644 (file)
@@ -429,6 +429,8 @@ public:
   // Check for undefined division and modulus behaviors.
   void EmitUndefinedBehaviorIntegerDivAndRemCheck(const BinOpInfo &Ops, 
                                                   llvm::Value *Zero,bool isDiv);
+  // Common helper for getting how wide LHS of shift is.
+  static Value *GetWidthMinusOneValue(Value* LHS,Value* RHS);
   Value *EmitDiv(const BinOpInfo &Ops);
   Value *EmitRem(const BinOpInfo &Ops);
   Value *EmitAdd(const BinOpInfo &Ops);
@@ -2365,6 +2367,11 @@ Value *ScalarExprEmitter::EmitSub(const BinOpInfo &op) {
   return Builder.CreateExactSDiv(diffInChars, divisor, "sub.ptr.div");
 }
 
+Value *ScalarExprEmitter::GetWidthMinusOneValue(Value* LHS,Value* RHS) {
+  unsigned Width = cast<llvm::IntegerType>(LHS->getType())->getBitWidth();
+  return llvm::ConstantInt::get(RHS->getType(), Width - 1);
+}
+
 Value *ScalarExprEmitter::EmitShl(const BinOpInfo &Ops) {
   // LLVM requires the LHS and RHS to be the same type: promote or truncate the
   // RHS to the same size as the LHS.
@@ -2372,11 +2379,9 @@ Value *ScalarExprEmitter::EmitShl(const BinOpInfo &Ops) {
   if (Ops.LHS->getType() != RHS->getType())
     RHS = Builder.CreateIntCast(RHS, Ops.LHS->getType(), false, "sh_prom");
 
-  if (CGF.getLangOpts().SanitizeShift &&
-      isa<llvm::IntegerType>(Ops.LHS->getType())) {
-    unsigned Width = cast<llvm::IntegerType>(Ops.LHS->getType())->getBitWidth();
-    llvm::Value *WidthMinusOne =
-      llvm::ConstantInt::get(RHS->getType(), Width - 1);
+  if (CGF.getLangOpts().SanitizeShift && !CGF.getLangOpts().OpenCL
+      && isa<llvm::IntegerType>(Ops.LHS->getType())) {
+    llvm::Value *WidthMinusOne = GetWidthMinusOneValue(Ops.LHS, RHS);
     // FIXME: Emit the branching explicitly rather than emitting the check
     // twice.
     EmitBinOpCheck(Builder.CreateICmpULE(RHS, WidthMinusOne), Ops);
@@ -2401,6 +2406,9 @@ Value *ScalarExprEmitter::EmitShl(const BinOpInfo &Ops) {
       EmitBinOpCheck(Builder.CreateICmpEQ(BitsShiftedOff, Zero), Ops);
     }
   }
+  // OpenCL 6.3j: shift values are effectively % word size of LHS.
+  if (CGF.getLangOpts().OpenCL)
+    RHS = Builder.CreateAnd(RHS, GetWidthMinusOneValue(Ops.LHS, RHS), "shl.mask");
 
   return Builder.CreateShl(Ops.LHS, RHS, "shl");
 }
@@ -2412,12 +2420,13 @@ Value *ScalarExprEmitter::EmitShr(const BinOpInfo &Ops) {
   if (Ops.LHS->getType() != RHS->getType())
     RHS = Builder.CreateIntCast(RHS, Ops.LHS->getType(), false, "sh_prom");
 
-  if (CGF.getLangOpts().SanitizeShift &&
-      isa<llvm::IntegerType>(Ops.LHS->getType())) {
-    unsigned Width = cast<llvm::IntegerType>(Ops.LHS->getType())->getBitWidth();
-    llvm::Value *WidthVal = llvm::ConstantInt::get(RHS->getType(), Width);
-    EmitBinOpCheck(Builder.CreateICmpULT(RHS, WidthVal), Ops);
-  }
+  if (CGF.getLangOpts().SanitizeShift && !CGF.getLangOpts().OpenCL
+      && isa<llvm::IntegerType>(Ops.LHS->getType()))
+    EmitBinOpCheck(Builder.CreateICmpULE(RHS, GetWidthMinusOneValue(Ops.LHS, RHS)), Ops);
+
+  // OpenCL 6.3j: shift values are effectively % word size of LHS.
+  if (CGF.getLangOpts().OpenCL)
+    RHS = Builder.CreateAnd(RHS, GetWidthMinusOneValue(Ops.LHS, RHS), "shr.mask");
 
   if (Ops.Ty->hasUnsignedIntegerRepresentation())
     return Builder.CreateLShr(Ops.LHS, RHS, "shr");
index b71491008b87b92390b6f1a3fe1a44b625145a9f..bac897748f723537080b60c8f8b8301c283250cf 100644 (file)
@@ -6578,6 +6578,11 @@ static bool isScopedEnumerationType(QualType T) {
 static void DiagnoseBadShiftValues(Sema& S, ExprResult &LHS, ExprResult &RHS,
                                    SourceLocation Loc, unsigned Opc,
                                    QualType LHSType) {
+  // OpenCL 6.3j: shift values are effectively % word size of LHS (more defined),
+  // so skip remaining warnings as we don't want to modify values within Sema.
+  if (S.getLangOpts().OpenCL)
+    return;
+
   llvm::APSInt Right;
   // Check right/shifter operand
   if (RHS.get()->isValueDependent() ||
index d0b6d1969bdc623c738112f1d6a632fe18eff41f..9170666d31a5b21d338ab5f8a1cba8b790b4e1da 100644 (file)
@@ -99,7 +99,7 @@ int lsh_overflow(int a, int b) {
 
 // CHECK: @rsh_inbounds
 int rsh_inbounds(int a, int b) {
-  // CHECK:      %[[INBOUNDS:.*]] = icmp ult i32 %[[RHS:.*]], 32
+  // CHECK:      %[[INBOUNDS:.*]] = icmp ule i32 %[[RHS:.*]], 31
   // CHECK:      br i1 %[[INBOUNDS]]
 
   // CHECK:      %[[ARG1:.*]] = zext
diff --git a/test/CodeGenOpenCL/shifts.cl b/test/CodeGenOpenCL/shifts.cl
new file mode 100644 (file)
index 0000000..b84ec1e
--- /dev/null
@@ -0,0 +1,28 @@
+// RUN: %clang_cc1 -x cl -O1 -emit-llvm  %s -o - -triple x86_64-linux-gnu | FileCheck %s
+// OpenCL essentially reduces all shift amounts to the last word-size bits before evaluating.
+// Test this both for variables and constants evaluated in the front-end.
+
+
+//CHECK: @positiveShift32
+int positiveShift32(int a,int b) {
+  //CHECK: %shl.mask = and i32 %b, 31
+  //CHECK-NEXT: %shl = shl i32 %a, %shl.mask
+  int c = a<<b;
+  int d = ((int)1)<<33;
+  //CHECK-NEXT: %add = add nsw i32 %shl, 2
+  int e = c + d;
+  //CHECK-NEXT: ret i32 %add
+  return e;
+}
+
+//CHECK: @positiveShift64
+long positiveShift64(long a,long b) {
+  //CHECK: %shr.mask = and i64 %b, 63
+  //CHECK-NEXT: %shr = ashr i64 %a, %shr.mask
+  long c = a>>b;
+  long d = ((long)8)>>65;
+  //CHECK-NEXT: %add = add nsw i64 %shr, 4
+  long e = c + d;
+  //CHECK-NEXT: ret i64 %add
+  return e;
+}
diff --git a/test/Sema/shiftOpenCL.cl b/test/Sema/shiftOpenCL.cl
new file mode 100644 (file)
index 0000000..3bf9718
--- /dev/null
@@ -0,0 +1,14 @@
+// RUN: %clang_cc1 -x cl -O1 -emit-llvm  %s -o - -triple x86_64-linux-gnu | FileCheck %s
+// OpenCL essentially reduces all shift amounts to the last word-size bits before evaluating.
+// Test this both for variables and constants evaluated in the front-end.
+
+//CHECK: @array0 = common global [256 x i8]
+char array0[((int)1)<<40];
+//CHECK: @array1 = common global [256 x i8]
+char array1[((int)1)<<(-24)];
+
+//CHECK: @negativeShift32
+int negativeShift32(int a,int b) {
+  //CHECK: ret i32 65536
+  return ((int)1)<<(-16);
+}