diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index 511a28c5554bbba..44d98d98411d124 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -58,6 +58,12 @@ code bases. containing strict-aliasing violations. The new default behavior can be disabled using ``-fno-pointer-tbaa``. +- The ``-fwrapv`` flag now only makes signed integer overflow well-defined, + without affecting pointer overflow, which is controlled by a new + ``-fwrapv-pointer`` flag. The ``-fno-strict-overflow`` flag now implies + both ``-fwrapv`` and ``-fwrapv-pointer`` and as such retains its old meaning. + The new behavior matches GCC. + C/C++ Language Potentially Breaking Changes ------------------------------------------- @@ -464,6 +470,11 @@ New Compiler Flags - clang-cl and clang-dxc now support ``-fdiagnostics-color=[auto|never|always]`` in addition to ``-f[no-]color-diagnostics``. +- The new ``-fwrapv-pointer`` flag opts-in to a language dialect where pointer + overflow is well-defined. The ``-fwrapv`` flag previously implied + ``-fwrapv-pointer`` as well, but no longer does. ``-fno-strict-overflow`` + implies ``-fwrapv -fwrapv-pointer``. The flags now match GCC. + Deprecated Compiler Flags ------------------------- diff --git a/clang/include/clang/Basic/LangOptions.def b/clang/include/clang/Basic/LangOptions.def index 3b833240e5b68ce..c4c53d7455c4176 100644 --- a/clang/include/clang/Basic/LangOptions.def +++ b/clang/include/clang/Basic/LangOptions.def @@ -407,6 +407,7 @@ VALUE_LANGOPT(TrivialAutoVarInitMaxSize, 32, 0, "stop trivial automatic variable initialization if var size exceeds the specified size (in bytes). Must be greater than 0.") ENUM_LANGOPT(SignedOverflowBehavior, SignedOverflowBehaviorTy, 2, SOB_Undefined, "signed integer overflow handling") +LANGOPT(PointerOverflowDefined, 1, 0, "make pointer overflow defined") ENUM_LANGOPT(ThreadModel , ThreadModelKind, 2, ThreadModelKind::POSIX, "Thread Model") BENIGN_LANGOPT(ArrowDepth, 32, 256, diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td index 41a7e8c37280664..8b45aa6dd4c2df1 100644 --- a/clang/include/clang/Driver/Options.td +++ b/clang/include/clang/Driver/Options.td @@ -4291,6 +4291,11 @@ def fwrapv : Flag<["-"], "fwrapv">, Group, HelpText<"Treat signed integer overflow as two's complement">; def fno_wrapv : Flag<["-"], "fno-wrapv">, Group, Visibility<[ClangOption, CLOption, FlangOption]>; +def fwrapv_pointer : Flag<["-"], "fwrapv-pointer">, Group, + Visibility<[ClangOption, CLOption, CC1Option, FlangOption, FC1Option]>, + HelpText<"Treat pointer overflow as two's complement">; +def fno_wrapv_pointer : Flag<["-"], "fno-wrapv-pointer">, Group, + Visibility<[ClangOption, CLOption, FlangOption]>; def fwritable_strings : Flag<["-"], "fwritable-strings">, Group, Visibility<[ClangOption, CC1Option]>, HelpText<"Store string literals as writable data">, diff --git a/clang/lib/CodeGen/CGBuiltin.cpp b/clang/lib/CodeGen/CGBuiltin.cpp index ca03fb665d423d8..b5bbfeae576029b 100644 --- a/clang/lib/CodeGen/CGBuiltin.cpp +++ b/clang/lib/CodeGen/CGBuiltin.cpp @@ -22113,7 +22113,7 @@ RValue CodeGenFunction::EmitBuiltinAlignTo(const CallExpr *E, bool AlignUp) { // By adding the mask, we ensure that align_up on an already aligned // value will not change the value. if (Args.Src->getType()->isPointerTy()) { - if (getLangOpts().isSignedOverflowDefined()) + if (getLangOpts().PointerOverflowDefined) SrcForMask = Builder.CreateGEP(Int8Ty, SrcForMask, Args.Mask, "over_boundary"); else diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp index 1bad7a722da07a3..dc9c2afeaa93a58 100644 --- a/clang/lib/CodeGen/CGExpr.cpp +++ b/clang/lib/CodeGen/CGExpr.cpp @@ -4307,14 +4307,14 @@ LValue CodeGenFunction::EmitArraySubscriptExpr(const ArraySubscriptExpr *E, // GEP indexes are signed, and scaling an index isn't permitted to // signed-overflow, so we use the same semantics for our explicit // multiply. We suppress this if overflow is not undefined behavior. - if (getLangOpts().isSignedOverflowDefined()) { + if (getLangOpts().PointerOverflowDefined) { Idx = Builder.CreateMul(Idx, numElements); } else { Idx = Builder.CreateNSWMul(Idx, numElements); } Addr = emitArraySubscriptGEP(*this, Addr, Idx, vla->getElementType(), - !getLangOpts().isSignedOverflowDefined(), + !getLangOpts().PointerOverflowDefined, SignedIndices, E->getExprLoc()); } else if (const ObjCObjectType *OIT = E->getType()->getAs()){ @@ -4404,7 +4404,7 @@ LValue CodeGenFunction::EmitArraySubscriptExpr(const ArraySubscriptExpr *E, QualType arrayType = Array->getType(); Addr = emitArraySubscriptGEP( *this, ArrayLV.getAddress(), {CGM.getSize(CharUnits::Zero()), Idx}, - E->getType(), !getLangOpts().isSignedOverflowDefined(), SignedIndices, + E->getType(), !getLangOpts().PointerOverflowDefined, SignedIndices, E->getExprLoc(), &arrayType, E->getBase()); EltBaseInfo = ArrayLV.getBaseInfo(); EltTBAAInfo = CGM.getTBAAInfoForSubobject(ArrayLV, E->getType()); @@ -4414,7 +4414,7 @@ LValue CodeGenFunction::EmitArraySubscriptExpr(const ArraySubscriptExpr *E, auto *Idx = EmitIdxAfterBase(/*Promote*/true); QualType ptrType = E->getBase()->getType(); Addr = emitArraySubscriptGEP(*this, Addr, Idx, E->getType(), - !getLangOpts().isSignedOverflowDefined(), + !getLangOpts().PointerOverflowDefined, SignedIndices, E->getExprLoc(), &ptrType, E->getBase()); } @@ -4561,11 +4561,11 @@ LValue CodeGenFunction::EmitArraySectionExpr(const ArraySectionExpr *E, : llvm::ConstantInt::get(IntPtrTy, ConstLength); Idx = Builder.CreateAdd(LowerBoundVal, LengthVal, "lb_add_len", /*HasNUW=*/false, - !getLangOpts().isSignedOverflowDefined()); + !getLangOpts().PointerOverflowDefined); if (Length && LowerBound) { Idx = Builder.CreateSub( Idx, llvm::ConstantInt::get(IntPtrTy, /*V=*/1), "idx_sub_1", - /*HasNUW=*/false, !getLangOpts().isSignedOverflowDefined()); + /*HasNUW=*/false, !getLangOpts().PointerOverflowDefined); } } else Idx = llvm::ConstantInt::get(IntPtrTy, ConstLength + ConstLowerBound); @@ -4591,7 +4591,7 @@ LValue CodeGenFunction::EmitArraySectionExpr(const ArraySectionExpr *E, Length->getType()->hasSignedIntegerRepresentation()); Idx = Builder.CreateSub( LengthVal, llvm::ConstantInt::get(IntPtrTy, /*V=*/1), "len_sub_1", - /*HasNUW=*/false, !getLangOpts().isSignedOverflowDefined()); + /*HasNUW=*/false, !getLangOpts().PointerOverflowDefined); } else { ConstLength = ConstLength.zextOrTrunc(PointerWidthInBits); --ConstLength; @@ -4618,12 +4618,12 @@ LValue CodeGenFunction::EmitArraySectionExpr(const ArraySectionExpr *E, // GEP indexes are signed, and scaling an index isn't permitted to // signed-overflow, so we use the same semantics for our explicit // multiply. We suppress this if overflow is not undefined behavior. - if (getLangOpts().isSignedOverflowDefined()) + if (getLangOpts().PointerOverflowDefined) Idx = Builder.CreateMul(Idx, NumElements); else Idx = Builder.CreateNSWMul(Idx, NumElements); EltPtr = emitArraySubscriptGEP(*this, Base, Idx, VLA->getElementType(), - !getLangOpts().isSignedOverflowDefined(), + !getLangOpts().PointerOverflowDefined, /*signedIndices=*/false, E->getExprLoc()); } else if (const Expr *Array = isSimpleArrayDecayOperand(E->getBase())) { // If this is A[i] where A is an array, the frontend will have decayed the @@ -4643,7 +4643,7 @@ LValue CodeGenFunction::EmitArraySectionExpr(const ArraySectionExpr *E, // Propagate the alignment from the array itself to the result. EltPtr = emitArraySubscriptGEP( *this, ArrayLV.getAddress(), {CGM.getSize(CharUnits::Zero()), Idx}, - ResultExprTy, !getLangOpts().isSignedOverflowDefined(), + ResultExprTy, !getLangOpts().PointerOverflowDefined, /*signedIndices=*/false, E->getExprLoc()); BaseInfo = ArrayLV.getBaseInfo(); TBAAInfo = CGM.getTBAAInfoForSubobject(ArrayLV, ResultExprTy); @@ -4652,7 +4652,7 @@ LValue CodeGenFunction::EmitArraySectionExpr(const ArraySectionExpr *E, emitOMPArraySectionBase(*this, E->getBase(), BaseInfo, TBAAInfo, BaseTy, ResultExprTy, IsLowerBound); EltPtr = emitArraySubscriptGEP(*this, Base, Idx, ResultExprTy, - !getLangOpts().isSignedOverflowDefined(), + !getLangOpts().PointerOverflowDefined, /*signedIndices=*/false, E->getExprLoc()); } diff --git a/clang/lib/CodeGen/CGExprScalar.cpp b/clang/lib/CodeGen/CGExprScalar.cpp index 090c4fb3ea39ee4..32395391000ffb3 100644 --- a/clang/lib/CodeGen/CGExprScalar.cpp +++ b/clang/lib/CodeGen/CGExprScalar.cpp @@ -3037,7 +3037,7 @@ ScalarExprEmitter::EmitScalarPrePostIncDec(const UnaryOperator *E, LValue LV, llvm::Value *numElts = CGF.getVLASize(vla).NumElts; if (!isInc) numElts = Builder.CreateNSWNeg(numElts, "vla.negsize"); llvm::Type *elemTy = CGF.ConvertTypeForMem(vla->getElementType()); - if (CGF.getLangOpts().isSignedOverflowDefined()) + if (CGF.getLangOpts().PointerOverflowDefined) value = Builder.CreateGEP(elemTy, value, numElts, "vla.inc"); else value = CGF.EmitCheckedInBoundsGEP( @@ -3048,7 +3048,7 @@ ScalarExprEmitter::EmitScalarPrePostIncDec(const UnaryOperator *E, LValue LV, } else if (type->isFunctionType()) { llvm::Value *amt = Builder.getInt32(amount); - if (CGF.getLangOpts().isSignedOverflowDefined()) + if (CGF.getLangOpts().PointerOverflowDefined) value = Builder.CreateGEP(CGF.Int8Ty, value, amt, "incdec.funcptr"); else value = @@ -3060,7 +3060,7 @@ ScalarExprEmitter::EmitScalarPrePostIncDec(const UnaryOperator *E, LValue LV, } else { llvm::Value *amt = Builder.getInt32(amount); llvm::Type *elemTy = CGF.ConvertTypeForMem(type); - if (CGF.getLangOpts().isSignedOverflowDefined()) + if (CGF.getLangOpts().PointerOverflowDefined) value = Builder.CreateGEP(elemTy, value, amt, "incdec.ptr"); else value = CGF.EmitCheckedInBoundsGEP( @@ -3173,7 +3173,7 @@ ScalarExprEmitter::EmitScalarPrePostIncDec(const UnaryOperator *E, LValue LV, llvm::Value *sizeValue = llvm::ConstantInt::get(CGF.SizeTy, size.getQuantity()); - if (CGF.getLangOpts().isSignedOverflowDefined()) + if (CGF.getLangOpts().PointerOverflowDefined) value = Builder.CreateGEP(CGF.Int8Ty, value, sizeValue, "incdec.objptr"); else value = CGF.EmitCheckedInBoundsGEP( @@ -4067,7 +4067,7 @@ static Value *emitPointerArithmetic(CodeGenFunction &CGF, // signed-overflow, so we use the same semantics for our explicit // multiply. We suppress this if overflow is not undefined behavior. llvm::Type *elemTy = CGF.ConvertTypeForMem(vla->getElementType()); - if (CGF.getLangOpts().isSignedOverflowDefined()) { + if (CGF.getLangOpts().PointerOverflowDefined) { index = CGF.Builder.CreateMul(index, numElements, "vla.index"); pointer = CGF.Builder.CreateGEP(elemTy, pointer, index, "add.ptr"); } else { @@ -4088,7 +4088,7 @@ static Value *emitPointerArithmetic(CodeGenFunction &CGF, else elemTy = CGF.ConvertTypeForMem(elementType); - if (CGF.getLangOpts().isSignedOverflowDefined()) + if (CGF.getLangOpts().PointerOverflowDefined) return CGF.Builder.CreateGEP(elemTy, pointer, index, "add.ptr"); return CGF.EmitCheckedInBoundsGEP( diff --git a/clang/lib/Driver/SanitizerArgs.cpp b/clang/lib/Driver/SanitizerArgs.cpp index a0d6919c6dc8d0f..3420472c0f75eba 100644 --- a/clang/lib/Driver/SanitizerArgs.cpp +++ b/clang/lib/Driver/SanitizerArgs.cpp @@ -575,6 +575,9 @@ SanitizerArgs::SanitizerArgs(const ToolChain &TC, options::OPT_fstrict_overflow, false); if (Args.hasFlagNoClaim(options::OPT_fwrapv, options::OPT_fno_wrapv, S)) Add &= ~SanitizerKind::SignedIntegerOverflow; + if (Args.hasFlagNoClaim(options::OPT_fwrapv_pointer, + options::OPT_fno_wrapv_pointer, S)) + Add &= ~SanitizerKind::PointerOverflow; } Add &= Supported; diff --git a/clang/lib/Driver/ToolChains/CommonArgs.cpp b/clang/lib/Driver/ToolChains/CommonArgs.cpp index f8967890f722cf8..8f5c38b38c2f8d8 100644 --- a/clang/lib/Driver/ToolChains/CommonArgs.cpp +++ b/clang/lib/Driver/ToolChains/CommonArgs.cpp @@ -3106,12 +3106,19 @@ void tools::renderCommonIntegerOverflowOptions(const ArgList &Args, ArgStringList &CmdArgs) { // -fno-strict-overflow implies -fwrapv if it isn't disabled, but // -fstrict-overflow won't turn off an explicitly enabled -fwrapv. + bool StrictOverflow = Args.hasFlag(options::OPT_fstrict_overflow, + options::OPT_fno_strict_overflow, true); if (Arg *A = Args.getLastArg(options::OPT_fwrapv, options::OPT_fno_wrapv)) { if (A->getOption().matches(options::OPT_fwrapv)) CmdArgs.push_back("-fwrapv"); - } else if (Arg *A = Args.getLastArg(options::OPT_fstrict_overflow, - options::OPT_fno_strict_overflow)) { - if (A->getOption().matches(options::OPT_fno_strict_overflow)) - CmdArgs.push_back("-fwrapv"); + } else if (!StrictOverflow) { + CmdArgs.push_back("-fwrapv"); + } + if (Arg *A = Args.getLastArg(options::OPT_fwrapv_pointer, + options::OPT_fno_wrapv_pointer)) { + if (A->getOption().matches(options::OPT_fwrapv_pointer)) + CmdArgs.push_back("-fwrapv-pointer"); + } else if (!StrictOverflow) { + CmdArgs.push_back("-fwrapv-pointer"); } } diff --git a/clang/lib/Frontend/CompilerInvocation.cpp b/clang/lib/Frontend/CompilerInvocation.cpp index 39bed84536c6a33..2acb4e49dba70b6 100644 --- a/clang/lib/Frontend/CompilerInvocation.cpp +++ b/clang/lib/Frontend/CompilerInvocation.cpp @@ -3721,6 +3721,8 @@ void CompilerInvocationBase::GenerateLangArgs(const LangOptions &Opts, } else if (Opts.SignedOverflowBehavior == LangOptions::SOB_Defined) { GenerateArg(Consumer, OPT_fwrapv); } + if (Opts.PointerOverflowDefined) + GenerateArg(Consumer, OPT_fwrapv_pointer); if (Opts.MSCompatibilityVersion != 0) { unsigned Major = Opts.MSCompatibilityVersion / 10000000; @@ -4138,6 +4140,8 @@ bool CompilerInvocation::ParseLangArgs(LangOptions &Opts, ArgList &Args, } else if (Args.hasArg(OPT_fwrapv)) Opts.setSignedOverflowBehavior(LangOptions::SOB_Defined); + if (Args.hasArg(OPT_fwrapv_pointer)) + Opts.PointerOverflowDefined = true; Opts.MSCompatibilityVersion = 0; if (const Arg *A = Args.getLastArg(OPT_fms_compatibility_version)) { diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index ae40895980d90a9..4920688dff398ef 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -11787,7 +11787,7 @@ static std::optional isTautologicalBoundsCheck(Sema &S, const Expr *LHS, const Expr *RHS, BinaryOperatorKind Opc) { if (!LHS->getType()->isPointerType() || - S.getLangOpts().isSignedOverflowDefined()) + S.getLangOpts().PointerOverflowDefined) return std::nullopt; // Canonicalize to >= or < predicate. diff --git a/clang/test/CodeGen/integer-overflow.c b/clang/test/CodeGen/integer-overflow.c index 9e8cde8b33b16eb..9f1eca7e314c268 100644 --- a/clang/test/CodeGen/integer-overflow.c +++ b/clang/test/CodeGen/integer-overflow.c @@ -1,8 +1,8 @@ // 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 - -fsanitize=signed-integer-overflow | FileCheck %s --check-prefixes=CATCH_UB,CATCH_UB_POINTER -// RUN: %clang_cc1 -triple x86_64-apple-darwin %s -emit-llvm -o - -fsanitize=signed-integer-overflow -fwrapv | FileCheck %s --check-prefixes=CATCH_UB,NOCATCH_UB_POINTER +// RUN: %clang_cc1 -triple x86_64-apple-darwin %s -emit-llvm -o - -fsanitize=signed-integer-overflow | FileCheck %s --check-prefixes=CATCH_UB +// RUN: %clang_cc1 -triple x86_64-apple-darwin %s -emit-llvm -o - -fsanitize=signed-integer-overflow -fwrapv | FileCheck %s --check-prefixes=CATCH_UB // RUN: %clang_cc1 -triple x86_64-apple-darwin %s -emit-llvm -o - -ftrapv -ftrapv-handler foo | FileCheck %s --check-prefix=TRAPV_HANDLER @@ -57,15 +57,6 @@ void test1(void) { // TRAPV_HANDLER: foo( --a; - // -fwrapv should turn off inbounds for GEP's, PR9256 - extern int* P; - ++P; - // DEFAULT: getelementptr inbounds nuw i32, ptr - // WRAPV: getelementptr i32, ptr - // TRAPV: getelementptr inbounds nuw i32, ptr - // CATCH_UB_POINTER: getelementptr inbounds nuw i32, ptr - // NOCATCH_UB_POINTER: getelementptr i32, ptr - // PR9350: char pre-increment never overflows. extern volatile signed char PR9350_char_inc; // DEFAULT: add i8 {{.*}}, 1 diff --git a/clang/test/Driver/clang_wrapv_opts.c b/clang/test/Driver/clang_wrapv_opts.c index 826468e0678d0b4..c0045b95639b31c 100644 --- a/clang/test/Driver/clang_wrapv_opts.c +++ b/clang/test/Driver/clang_wrapv_opts.c @@ -1,11 +1,24 @@ // RUN: %clang -### -S -fwrapv -fno-wrapv -fwrapv %s 2>&1 | FileCheck -check-prefix=CHECK1 %s // CHECK1: -fwrapv // +// RUN: %clang -### -S -fwrapv-pointer -fno-wrapv-pointer -fwrapv-pointer %s 2>&1 | FileCheck -check-prefix=CHECK1-POINTER %s +// CHECK1-POINTER: -fwrapv-pointer +// // RUN: %clang -### -S -fstrict-overflow -fno-strict-overflow %s 2>&1 | FileCheck -check-prefix=CHECK2 %s -// CHECK2: -fwrapv +// CHECK2: -fwrapv{{.*}}-fwrapv-pointer // // RUN: %clang -### -S -fwrapv -fstrict-overflow %s 2>&1 | FileCheck -check-prefix=CHECK3 %s // CHECK3: -fwrapv // -// RUN: %clang -### -S -fno-wrapv -fno-strict-overflow %s 2>&1 | FileCheck -check-prefix=CHECK4 %s -// CHECK4-NOT: -fwrapv +// RUN: %clang -### -S -fwrapv-pointer -fstrict-overflow %s 2>&1 | FileCheck -check-prefix=CHECK3-POINTER %s +// CHECK3-POINTER: -fwrapv-pointer +// +// RUN: %clang -### -S -fno-wrapv -fno-strict-overflow %s 2>&1 | FileCheck -check-prefix=CHECK5 %s +// CHECK5-NOT: -fwrapv +// CHECK5: -fwrapv-pointer +// CHECK5-NOT: -fwrapv +// +// RUN: %clang -### -S -fno-wrapv-pointer -fno-strict-overflow %s 2>&1 | FileCheck -check-prefix=CHECK5-POINTER %s +// CHECK5-POINTER-NOT: -fwrapv-pointer +// CHECK5-POINTER: -fwrapv +// CHECK5-POINTER-NOT: -fwrapv-pointer diff --git a/clang/test/Sema/tautological-pointer-comparison.c b/clang/test/Sema/tautological-pointer-comparison.c index 1c5973b01a30d0a..f2a944b5305e4ef 100644 --- a/clang/test/Sema/tautological-pointer-comparison.c +++ b/clang/test/Sema/tautological-pointer-comparison.c @@ -1,5 +1,5 @@ // RUN: %clang_cc1 -fsyntax-only -verify %s -// RUN: %clang_cc1 -fsyntax-only -fwrapv -verify=fwrapv %s +// RUN: %clang_cc1 -fsyntax-only -fwrapv-pointer -verify=fwrapv %s // fwrapv-no-diagnostics