diff --git a/lib/AST/ExprConstant.cpp b/lib/AST/ExprConstant.cpp index a472951ab6..338cd74b32 100644 --- a/lib/AST/ExprConstant.cpp +++ b/lib/AST/ExprConstant.cpp @@ -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(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(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; diff --git a/lib/CodeGen/CGExprScalar.cpp b/lib/CodeGen/CGExprScalar.cpp index eb62343bc6..7ec4bc2297 100644 --- a/lib/CodeGen/CGExprScalar.cpp +++ b/lib/CodeGen/CGExprScalar.cpp @@ -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(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(Ops.LHS->getType())) { - unsigned Width = cast(Ops.LHS->getType())->getBitWidth(); - llvm::Value *WidthMinusOne = - llvm::ConstantInt::get(RHS->getType(), Width - 1); + if (CGF.getLangOpts().SanitizeShift && !CGF.getLangOpts().OpenCL + && isa(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(Ops.LHS->getType())) { - unsigned Width = cast(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(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"); diff --git a/lib/Sema/SemaExpr.cpp b/lib/Sema/SemaExpr.cpp index b71491008b..bac897748f 100644 --- a/lib/Sema/SemaExpr.cpp +++ b/lib/Sema/SemaExpr.cpp @@ -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() || diff --git a/test/CodeGen/catch-undef-behavior.c b/test/CodeGen/catch-undef-behavior.c index d0b6d1969b..9170666d31 100644 --- a/test/CodeGen/catch-undef-behavior.c +++ b/test/CodeGen/catch-undef-behavior.c @@ -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 index 0000000000..b84ec1eeef --- /dev/null +++ b/test/CodeGenOpenCL/shifts.cl @@ -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; + 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 index 0000000000..3bf9718768 --- /dev/null +++ b/test/Sema/shiftOpenCL.cl @@ -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); +}