Index: docs/ClangCommandLineReference.rst =================================================================== --- docs/ClangCommandLineReference.rst +++ docs/ClangCommandLineReference.rst @@ -1543,6 +1543,14 @@ Disable implicit builtin knowledge of a specific function +.. option:: -fdelete-null-pointer-checks, -fno-delete-null-pointer-checks + +When enabled, treat null pointer dereference, creation of a reference to null, +or passing a null pointer to a function parameter annotated with the "nonnull" +attribute as undefined behavior. (And, thus the optimizer may assume that any +pointer used in such a way must not have been null and optimize away the +branches accordingly.) On by default. + .. option:: -fno-elide-type Do not elide types when printing diagnostics Index: include/clang/Driver/Options.td =================================================================== --- include/clang/Driver/Options.td +++ include/clang/Driver/Options.td @@ -1080,6 +1080,13 @@ Flags<[CC1Option]>; def fno_rewrite_imports : Flag<["-"], "fno-rewrite-imports">, Group; +def fdelete_null_pointer_checks : Flag<["-"], + "fdelete-null-pointer-checks">, Group, + HelpText<"Treat usage of null pointers as undefined behavior.">; +def fno_delete_null_pointer_checks : Flag<["-"], + "fno-delete-null-pointer-checks">, Group, Flags<[CC1Option]>, + HelpText<"Do not treat usage of null pointers as undefined behavior.">; + def frewrite_map_file : Separate<["-"], "frewrite-map-file">, Group, Flags<[ DriverOption, CC1Option ]>; @@ -2855,8 +2862,6 @@ defm eliminate_unused_debug_types : BooleanFFlag<"eliminate-unused-debug-types">, Group; defm branch_count_reg : BooleanFFlag<"branch-count-reg">, Group; defm default_inline : BooleanFFlag<"default-inline">, Group; -defm delete_null_pointer_checks : BooleanFFlag<"delete-null-pointer-checks">, - Group; defm fat_lto_objects : BooleanFFlag<"fat-lto-objects">, Group; defm float_store : BooleanFFlag<"float-store">, Group; defm friend_injection : BooleanFFlag<"friend-injection">, Group; Index: include/clang/Frontend/CodeGenOptions.def =================================================================== --- include/clang/Frontend/CodeGenOptions.def +++ include/clang/Frontend/CodeGenOptions.def @@ -130,6 +130,7 @@ CODEGENOPT(NoImplicitFloat , 1, 0) ///< Set when -mno-implicit-float is enabled. CODEGENOPT(NoInfsFPMath , 1, 0) ///< Assume FP arguments, results not +-Inf. CODEGENOPT(NoSignedZeros , 1, 0) ///< Allow ignoring the signedness of FP zero +CODEGENOPT(NullPointerIsValid , 1, 0) ///< Assume Null pointer deference is defined. CODEGENOPT(Reassociate , 1, 0) ///< Allow reassociation of FP math ops CODEGENOPT(ReciprocalMath , 1, 0) ///< Allow FP divisions to be reassociated. CODEGENOPT(NoTrappingMath , 1, 0) ///< Set when -fno-trapping-math is enabled. Index: lib/CodeGen/CGCall.cpp =================================================================== --- lib/CodeGen/CGCall.cpp +++ lib/CodeGen/CGCall.cpp @@ -1734,6 +1734,8 @@ FuncAttrs.addAttribute("less-precise-fpmad", llvm::toStringRef(CodeGenOpts.LessPreciseFPMAD)); + if (CodeGenOpts.NullPointerIsValid) + FuncAttrs.addAttribute("null-pointer-is-valid", "true"); if (!CodeGenOpts.FPDenormalMode.empty()) FuncAttrs.addAttribute("denormal-fp-math", CodeGenOpts.FPDenormalMode); @@ -1867,7 +1869,8 @@ } if (TargetDecl->hasAttr()) RetAttrs.addAttribute(llvm::Attribute::NoAlias); - if (TargetDecl->hasAttr()) + if (TargetDecl->hasAttr() && + !CodeGenOpts.NullPointerIsValid) RetAttrs.addAttribute(llvm::Attribute::NonNull); if (TargetDecl->hasAttr()) FuncAttrs.addAttribute("no_caller_saved_registers"); @@ -1974,7 +1977,8 @@ if (!PTy->isIncompleteType() && PTy->isConstantSizeType()) RetAttrs.addDereferenceableAttr(getContext().getTypeSizeInChars(PTy) .getQuantity()); - else if (getContext().getTargetAddressSpace(PTy) == 0) + else if (getContext().getTargetAddressSpace(PTy) == 0 && + !CodeGenOpts.NullPointerIsValid) RetAttrs.addAttribute(llvm::Attribute::NonNull); } @@ -2083,7 +2087,8 @@ if (!PTy->isIncompleteType() && PTy->isConstantSizeType()) Attrs.addDereferenceableAttr(getContext().getTypeSizeInChars(PTy) .getQuantity()); - else if (getContext().getTargetAddressSpace(PTy) == 0) + else if (getContext().getTargetAddressSpace(PTy) == 0 && + !CodeGenOpts.NullPointerIsValid) Attrs.addAttribute(llvm::Attribute::NonNull); } @@ -2343,7 +2348,8 @@ if (const ParmVarDecl *PVD = dyn_cast(Arg)) { if (getNonNullAttr(CurCodeDecl, PVD, PVD->getType(), - PVD->getFunctionScopeIndex())) + PVD->getFunctionScopeIndex()) && + !CGM.getCodeGenOpts().NullPointerIsValid) AI->addAttr(llvm::Attribute::NonNull); QualType OTy = PVD->getOriginalType(); @@ -2362,7 +2368,8 @@ Attrs.addDereferenceableAttr( getContext().getTypeSizeInChars(ETy).getQuantity()*ArrSize); AI->addAttrs(Attrs); - } else if (getContext().getTargetAddressSpace(ETy) == 0) { + } else if (getContext().getTargetAddressSpace(ETy) == 0 && + !CGM.getCodeGenOpts().NullPointerIsValid) { AI->addAttr(llvm::Attribute::NonNull); } } @@ -2372,7 +2379,8 @@ // we can't use the dereferenceable attribute, but in addrspace(0) // we know that it must be nonnull. if (ArrTy->getSizeModifier() == VariableArrayType::Static && - !getContext().getTargetAddressSpace(ArrTy->getElementType())) + !getContext().getTargetAddressSpace(ArrTy->getElementType()) && + !CGM.getCodeGenOpts().NullPointerIsValid) AI->addAttr(llvm::Attribute::NonNull); } Index: lib/Driver/ToolChains/Clang.cpp =================================================================== --- lib/Driver/ToolChains/Clang.cpp +++ lib/Driver/ToolChains/Clang.cpp @@ -3348,6 +3348,10 @@ options::OPT_fno_merge_all_constants, false)) CmdArgs.push_back("-fmerge-all-constants"); + if (Args.hasFlag(options::OPT_fno_delete_null_pointer_checks, + options::OPT_fdelete_null_pointer_checks, false)) + CmdArgs.push_back("-fno-delete-null-pointer-checks"); + // LLVM Code Generator Options. if (Args.hasArg(options::OPT_frewrite_map_file) || Index: lib/Frontend/CompilerInvocation.cpp =================================================================== --- lib/Frontend/CompilerInvocation.cpp +++ lib/Frontend/CompilerInvocation.cpp @@ -746,6 +746,8 @@ Opts.NoUseJumpTables = Args.hasArg(OPT_fno_jump_tables); + Opts.NullPointerIsValid = Args.hasArg(OPT_fno_delete_null_pointer_checks); + Opts.ProfileSampleAccurate = Args.hasArg(OPT_fprofile_sample_accurate); Opts.PrepareForLTO = Args.hasArg(OPT_flto, OPT_flto_EQ); Index: test/CodeGen/delete-null-pointer-checks.c =================================================================== --- test/CodeGen/delete-null-pointer-checks.c +++ test/CodeGen/delete-null-pointer-checks.c @@ -0,0 +1,20 @@ +// RUN: %clang_cc1 -emit-llvm -triple x86_64-unknown-linux-gnu -O2 -o - %s | FileCheck -check-prefix=NULL-POINTER-INVALID %s +// RUN: %clang_cc1 -emit-llvm -triple x86_64-unknown-linux-gnu -O2 -o - %s -fno-delete-null-pointer-checks | FileCheck -check-prefix=NULL-POINTER-VALID %s + +// Test that clang does not remove the null pointer check with +// -fno-delete-null-pointer-checks. +int null_check(int *P) { +// NULL-POINTER-VALID: %[[TOBOOL:.*]] = icmp eq i32* %P, null +// NULL-POINTER-INVALID-NOT: icmp eq +// NULL-POINTER-VALID: %[[SEL:.*]] = select i1 %[[TOBOOL:.*]], i32* null, i32* +// NULL-POINTER-INVALID-NOT: select i1 +// NULL-POINTER-VALID: load i32, i32* %[[SEL:.*]] + int *Q = P; + if (P) { + Q = P + 2; + } + return *Q; +} + +// NULL-POINTER-INVALID-NOT: attributes #0 = {{.*}} "null-pointer-is-valid"="true" +// NULL-POINTER-VALID: attributes #0 = {{.*}} "null-pointer-is-valid"="true" Index: test/CodeGen/nonnull.c =================================================================== --- test/CodeGen/nonnull.c +++ test/CodeGen/nonnull.c @@ -1,32 +1,39 @@ -// RUN: %clang_cc1 -triple x86_64-apple-darwin -emit-llvm < %s | FileCheck %s +// RUN: %clang_cc1 -triple x86_64-apple-darwin -emit-llvm < %s | FileCheck -check-prefix=NULL-INVALID %s +// RUN: %clang_cc1 -triple x86_64-apple-darwin -emit-llvm -fno-delete-null-pointer-checks < %s | FileCheck -check-prefix=NULL-VALID %s -// CHECK: define void @foo(i32* nonnull %x) +// NULL-INVALID: define void @foo(i32* nonnull %x) +// NULL-VALID: define void @foo(i32* %x) void foo(int * __attribute__((nonnull)) x) { *x = 0; } -// CHECK: define void @bar(i32* nonnull %x) +// NULL-INVALID: define void @bar(i32* nonnull %x) +// NULL-VALID: define void @bar(i32* %x) void bar(int * x) __attribute__((nonnull(1))) { *x = 0; } -// CHECK: define void @bar2(i32* %x, i32* nonnull %y) +// NULL-INVALID: define void @bar2(i32* %x, i32* nonnull %y) +// NULL-VALID: define void @bar2(i32* %x, i32* %y) void bar2(int * x, int * y) __attribute__((nonnull(2))) { *x = 0; } static int a; -// CHECK: define nonnull i32* @bar3() +// NULL-INVALID: define nonnull i32* @bar3() +// NULL-VALID: define i32* @bar3() int * bar3() __attribute__((returns_nonnull)) { return &a; } -// CHECK: define i32 @bar4(i32 %n, i32* nonnull %p) +// NULL-INVALID: define i32 @bar4(i32 %n, i32* nonnull %p) +// NULL-VALID: define i32 @bar4(i32 %n, i32* %p) int bar4(int n, int *p) __attribute__((nonnull)) { return n + *p; } -// CHECK: define i32 @bar5(i32 %n, i32* nonnull %p) +// NULL-INVALID: define i32 @bar5(i32 %n, i32* nonnull %p) +// NULL-VALID: define i32 @bar5(i32 %n, i32* %p) int bar5(int n, int *p) __attribute__((nonnull(1, 2))) { return n + *p; } @@ -37,15 +44,18 @@ double d; } TransparentUnion __attribute__((transparent_union)); -// CHECK: define i32 @bar6(i64 % +// NULL-INVALID: define i32 @bar6(i64 % +// NULL-VALID: define i32 @bar6(i64 % int bar6(TransparentUnion tu) __attribute__((nonnull(1))) { return *tu.p; } -// CHECK: define void @bar7(i32* nonnull %a, i32* nonnull %b) +// NULL-INVALID: define void @bar7(i32* nonnull %a, i32* nonnull %b) +// NULL-VALID: define void @bar7(i32* %a, i32* %b) void bar7(int *a, int *b) __attribute__((nonnull(1))) __attribute__((nonnull(2))) {} -// CHECK: define void @bar8(i32* nonnull %a, i32* nonnull %b) +// NULL-INVALID: define void @bar8(i32* nonnull %a, i32* nonnull %b) +// NULL-VALID: define void @bar8(i32* %a, i32* %b) void bar8(int *a, int *b) __attribute__((nonnull)) __attribute__((nonnull(1))) {} Index: test/CodeGen/vla.c =================================================================== --- test/CodeGen/vla.c +++ test/CodeGen/vla.c @@ -1,4 +1,5 @@ -// RUN: %clang_cc1 -triple i386-unknown-unknown %s -emit-llvm -o - | FileCheck %s +// RUN: %clang_cc1 -triple i386-unknown-unknown %s -emit-llvm -o - | FileCheck %s -check-prefixes=CHECK,NULL-INVALID +// RUN: %clang_cc1 -triple i386-unknown-unknown %s -emit-llvm -fno-delete-null-pointer-checks -o - | FileCheck %s -check-prefixes=CHECK,NULL-VALID int b(char* x); @@ -202,5 +203,6 @@ // CHECK: define void @test8(i32* dereferenceable(12) %a) void test9(int n, int a[static n]) { } -// CHECK: define void @test9(i32 %n, i32* nonnull %a) +// NULL-INVALID: define void @test9(i32 %n, i32* nonnull %a) +// NULL-VALID: define void @test9(i32 %n, i32* %a) Index: test/CodeGenCXX/address-space-ref.cpp =================================================================== --- test/CodeGenCXX/address-space-ref.cpp +++ test/CodeGenCXX/address-space-ref.cpp @@ -1,4 +1,5 @@ -// RUN: %clang_cc1 %s -std=c++11 -triple=x86_64-apple-darwin10 -emit-llvm -o - | FileCheck %s +// RUN: %clang_cc1 %s -std=c++11 -triple=x86_64-apple-darwin10 -emit-llvm -o - | FileCheck %s -check-prefixes=CHECK,NULL-INVALID +// RUN: %clang_cc1 %s -std=c++11 -triple=x86_64-apple-darwin10 -fno-delete-null-pointer-checks -emit-llvm -o - | FileCheck %s -check-prefixes=CHECK,NULL-VALID // For a reference to a complete type, output the dereferenceable attribute (in // any address space). @@ -29,6 +30,7 @@ return x; } -// CHECK: define nonnull %class.bc* @_Z4bar2R2bcS0_(%class.bc* nonnull %x, %class.bc* nonnull %y) +// NULL-INVALID: define nonnull %class.bc* @_Z4bar2R2bcS0_(%class.bc* nonnull %x, %class.bc* nonnull %y) +// NULL-VALID: define %class.bc* @_Z4bar2R2bcS0_(%class.bc* %x, %class.bc* %y) Index: test/CodeGenCXX/constructors.cpp =================================================================== --- test/CodeGenCXX/constructors.cpp +++ test/CodeGenCXX/constructors.cpp @@ -1,4 +1,5 @@ -// RUN: %clang_cc1 -triple x86_64-apple-darwin10 %s -emit-llvm -o - | FileCheck %s --implicit-check-not=should_not_appear_in_output +// RUN: %clang_cc1 -triple x86_64-apple-darwin10 %s -emit-llvm -o - | FileCheck %s --implicit-check-not=should_not_appear_in_output --check-prefixes=CHECK,NULL-INVALID +// RUN: %clang_cc1 -triple x86_64-apple-darwin10 %s -emit-llvm -fno-delete-null-pointer-checks -o - | FileCheck %s --implicit-check-not=should_not_appear_in_output --check-prefixes=CHECK,NULL-VALID struct Member { int x; Member(); Member(int); Member(const Member &); }; struct VBase { int x; VBase(); VBase(int); VBase(const VBase &); }; @@ -21,10 +22,12 @@ A::A(struct Undeclared &ref) : mem(0) {} // Check that delegation works. -// CHECK-LABEL: define void @_ZN1AC2ER10Undeclared(%struct.A* %this, %struct.Undeclared* nonnull %ref) unnamed_addr +// NULL-INVALID-LABEL: define void @_ZN1AC2ER10Undeclared(%struct.A* %this, %struct.Undeclared* nonnull %ref) unnamed_addr +// NULL-VALID-LABEL: define void @_ZN1AC2ER10Undeclared(%struct.A* %this, %struct.Undeclared* %ref) unnamed_addr // CHECK: call void @_ZN6MemberC1Ei( -// CHECK-LABEL: define void @_ZN1AC1ER10Undeclared(%struct.A* %this, %struct.Undeclared* nonnull %ref) unnamed_addr +// NULL-INVALID-LABEL: define void @_ZN1AC1ER10Undeclared(%struct.A* %this, %struct.Undeclared* nonnull %ref) unnamed_addr +// NULL-VALID-LABEL: define void @_ZN1AC1ER10Undeclared(%struct.A* %this, %struct.Undeclared* %ref) unnamed_addr // CHECK: call void @_ZN1AC2ER10Undeclared( A::A(ValueClass v) : mem(v.y - v.x) {} @@ -43,11 +46,13 @@ B::B(struct Undeclared &ref) : A(ref), mem(1) {} -// CHECK-LABEL: define void @_ZN1BC2ER10Undeclared(%struct.B* %this, %struct.Undeclared* nonnull %ref) unnamed_addr +// NULL-INVALID-LABEL: define void @_ZN1BC2ER10Undeclared(%struct.B* %this, %struct.Undeclared* nonnull %ref) unnamed_addr +// NULL-VALID-LABEL: define void @_ZN1BC2ER10Undeclared(%struct.B* %this, %struct.Undeclared* %ref) unnamed_addr // CHECK: call void @_ZN1AC2ER10Undeclared( // CHECK: call void @_ZN6MemberC1Ei( -// CHECK-LABEL: define void @_ZN1BC1ER10Undeclared(%struct.B* %this, %struct.Undeclared* nonnull %ref) unnamed_addr +// NULL-INVALID-LABEL: define void @_ZN1BC1ER10Undeclared(%struct.B* %this, %struct.Undeclared* nonnull %ref) unnamed_addr +// NULL-VALID-LABEL: define void @_ZN1BC1ER10Undeclared(%struct.B* %this, %struct.Undeclared* %ref) unnamed_addr // CHECK: call void @_ZN1BC2ER10Undeclared( Index: test/CodeGenCXX/temporaries.cpp =================================================================== --- test/CodeGenCXX/temporaries.cpp +++ test/CodeGenCXX/temporaries.cpp @@ -1,4 +1,5 @@ -// RUN: %clang_cc1 -emit-llvm %s -o - -triple=x86_64-apple-darwin9 -std=c++11 | FileCheck %s +// RUN: %clang_cc1 -emit-llvm %s -o - -triple=x86_64-apple-darwin9 -std=c++11 | FileCheck %s -check-prefixes=CHECK,NULL-INVALID +// RUN: %clang_cc1 -emit-llvm %s -o - -triple=x86_64-apple-darwin9 -std=c++11 -fno-delete-null-pointer-checks | FileCheck %s -check-prefixes=CHECK,NULL-VALID namespace PR16263 { const unsigned int n = 1234; @@ -333,7 +334,8 @@ struct D; D& zed(B); void foobar() { - // CHECK: call nonnull %"struct.PR6648::D"* @_ZN6PR66483zedENS_1BE + // NULL-INVALID: call nonnull %"struct.PR6648::D"* @_ZN6PR66483zedENS_1BE + // NULL-VALID: call %"struct.PR6648::D"* @_ZN6PR66483zedENS_1BE zed(foo); } } Index: test/Driver/clang_f_opts.c =================================================================== --- test/Driver/clang_f_opts.c +++ test/Driver/clang_f_opts.c @@ -348,7 +348,6 @@ // RUN: -fwhole-program \ // RUN: -fcaller-saves \ // RUN: -freorder-blocks \ -// RUN: -fdelete-null-pointer-checks \ // RUN: -ffat-lto-objects \ // RUN: -fmerge-constants \ // RUN: -finline-small-functions \ @@ -414,7 +413,6 @@ // CHECK-WARNING-DAG: optimization flag '-fwhole-program' is not supported // CHECK-WARNING-DAG: optimization flag '-fcaller-saves' is not supported // CHECK-WARNING-DAG: optimization flag '-freorder-blocks' is not supported -// CHECK-WARNING-DAG: optimization flag '-fdelete-null-pointer-checks' is not supported // CHECK-WARNING-DAG: optimization flag '-ffat-lto-objects' is not supported // CHECK-WARNING-DAG: optimization flag '-fmerge-constants' is not supported // CHECK-WARNING-DAG: optimization flag '-finline-small-functions' is not supported @@ -526,3 +524,10 @@ // RUN: %clang -### -S -fno-merge-all-constants -fmerge-all-constants %s 2>&1 | FileCheck -check-prefix=CHECK-MERGE-ALL-CONSTANTS %s // CHECK-NO-MERGE-ALL-CONSTANTS-NOT: "-fmerge-all-constants" // CHECK-MERGE-ALL-CONSTANTS: "-fmerge-all-constants" + +// RUN: %clang -### -S -fdelete-null-pointer-checks %s 2>&1 | FileCheck -check-prefix=CHECK-NULL-POINTER-CHECKS %s +// RUN: %clang -### -S -fno-delete-null-pointer-checks %s 2>&1 | FileCheck -check-prefix=CHECK-NO-NULL-POINTER-CHECKS %s +// RUN: %clang -### -S -fdelete-null-pointer-checks -fno-delete-null-pointer-checks %s 2>&1 | FileCheck -check-prefix=CHECK-NO-NULL-POINTER-CHECKS %s +// RUN: %clang -### -S -fno-delete-null-pointer-checks -fdelete-null-pointer-checks %s 2>&1 | FileCheck -check-prefix=CHECK-NULL-POINTER-CHECKS %s +// CHECK-NO-NULL-POINTER-CHECKS: "-fno-delete-null-pointer-checks" +// CHECK-NULL-POINTER-CHECKS-NOT: "-fno-delete-null-pointer-checks" Index: test/Sema/nonnull.c =================================================================== --- test/Sema/nonnull.c +++ test/Sema/nonnull.c @@ -1,5 +1,9 @@ // RUN: %clang_cc1 -fsyntax-only -verify %s // rdar://9584012 +// +// Verify All warnings are still issued with the option -fno-delete-null-pointer-checks +// if nullptr is passed to function with nonnull attribute. +// RUN: %clang_cc1 -fsyntax-only -fno-delete-null-pointer-checks -verify %s typedef struct { char *str;