Index: include/llvm/Analysis/Utils/Local.h =================================================================== --- include/llvm/Analysis/Utils/Local.h +++ include/llvm/Analysis/Utils/Local.h @@ -86,6 +86,11 @@ return Result; } +/// Check whether null pointer dereferencing is considered undefined behavior +/// for the function. +/// Return value: false => null pointer dereference is undefined. +/// Return value: true => null pointer dereference is not undefined. +bool NullPointerIsDefined(const Function *F); } #endif // LLVM_TRANSFORMS_UTILS_LOCAL_H Index: lib/Transforms/InstCombine/InstCombineCalls.cpp =================================================================== --- lib/Transforms/InstCombine/InstCombineCalls.cpp +++ lib/Transforms/InstCombine/InstCombineCalls.cpp @@ -3917,7 +3917,9 @@ } } - if (isa(Callee) || isa(Callee)) { + if ((isa(Callee) && + !NullPointerIsDefined(CS.getInstruction()->getParent()->getParent())) || + isa(Callee)) { // If CS does not return void then replaceAllUsesWith undef. // This allows ValueHandlers and custom metadata to adjust itself. if (!CS.getInstruction()->getType()->isVoidTy()) Index: lib/Transforms/InstCombine/InstCombineLoadStoreAlloca.cpp =================================================================== --- lib/Transforms/InstCombine/InstCombineLoadStoreAlloca.cpp +++ lib/Transforms/InstCombine/InstCombineLoadStoreAlloca.cpp @@ -970,17 +970,22 @@ auto *Ptr = SI.getPointerOperand(); if (GetElementPtrInst *GEPI = dyn_cast(Ptr)) Ptr = GEPI->getOperand(0); - return isa(Ptr); + return (isa(Ptr) && + !NullPointerIsDefined(SI.getParent()->getParent())); } static bool canSimplifyNullLoadOrGEP(LoadInst &LI, Value *Op) { if (GetElementPtrInst *GEPI = dyn_cast(Op)) { const Value *GEPI0 = GEPI->getOperand(0); - if (isa(GEPI0) && GEPI->getPointerAddressSpace() == 0) - return true; + if (isa(GEPI0) && + !NullPointerIsDefined(LI.getParent()->getParent()) && + GEPI->getPointerAddressSpace() == 0) + return true; } if (isa(Op) || - (isa(Op) && LI.getPointerAddressSpace() == 0)) + (isa(Op) && + !NullPointerIsDefined(LI.getParent()->getParent()) && + LI.getPointerAddressSpace() == 0)) return true; return false; } @@ -1076,6 +1081,7 @@ // load (select (cond, null, P)) -> load P if (isa(SI->getOperand(1)) && + !NullPointerIsDefined(SI->getParent()->getParent()) && LI.getPointerAddressSpace() == 0) { LI.setOperand(0, SI->getOperand(2)); return &LI; @@ -1083,6 +1089,7 @@ // load (select (cond, P, null)) -> load P if (isa(SI->getOperand(2)) && + !NullPointerIsDefined(SI->getParent()->getParent()) && LI.getPointerAddressSpace() == 0) { LI.setOperand(0, SI->getOperand(1)); return &LI; Index: lib/Transforms/Scalar/SCCP.cpp =================================================================== --- lib/Transforms/Scalar/SCCP.cpp +++ lib/Transforms/Scalar/SCCP.cpp @@ -1125,8 +1125,12 @@ Constant *Ptr = PtrVal.getConstant(); // load null is undefined. - if (isa(Ptr) && I.getPointerAddressSpace() == 0) - return; + if (isa(Ptr)) { + if (NullPointerIsDefined(I.getParent()->getParent())) + return markOverdefined(IV, &I); + else if (I.getPointerAddressSpace() == 0) + return; + } // Transform load (constant global) into the value loaded. if (auto *GV = dyn_cast(Ptr)) { Index: lib/Transforms/Utils/Local.cpp =================================================================== --- lib/Transforms/Utils/Local.cpp +++ lib/Transforms/Utils/Local.cpp @@ -1810,7 +1810,9 @@ if (auto *CI = dyn_cast(&I)) { Value *Callee = CI->getCalledValue(); - if (isa(Callee) || isa(Callee)) { + if ((isa(Callee) && + !NullPointerIsDefined(CI->getParent()->getParent())) || + isa(Callee)) { changeToUnreachable(CI, /*UseLLVMTrap=*/false, false, DDT); Changed = true; break; @@ -1851,7 +1853,9 @@ if (auto *II = dyn_cast(Terminator)) { // Turn invokes that call 'nounwind' functions into ordinary calls. Value *Callee = II->getCalledValue(); - if (isa(Callee) || isa(Callee)) { + if ((isa(Callee) && + !NullPointerIsDefined(BB->getParent())) || + isa(Callee)) { changeToUnreachable(II, true, false, DDT); Changed = true; } else if (II->doesNotThrow() && canSimplifyInvokeNoUnwind(&F)) { @@ -2525,3 +2529,9 @@ return true; } } + +bool llvm::NullPointerIsDefined(const Function *F) { + return F->getFnAttribute("null-pointer-is-valid"). + getValueAsString(). + equals("true"); +} Index: lib/Transforms/Utils/SimplifyCFG.cpp =================================================================== --- lib/Transforms/Utils/SimplifyCFG.cpp +++ lib/Transforms/Utils/SimplifyCFG.cpp @@ -5951,17 +5951,20 @@ // Load from null is undefined. if (LoadInst *LI = dyn_cast(Use)) if (!LI->isVolatile()) - return LI->getPointerAddressSpace() == 0; + return !NullPointerIsDefined(LI->getParent()->getParent()) && + LI->getPointerAddressSpace() == 0; // Store to null is undefined. if (StoreInst *SI = dyn_cast(Use)) if (!SI->isVolatile()) - return SI->getPointerAddressSpace() == 0 && - SI->getPointerOperand() == I; + return (!NullPointerIsDefined(SI->getParent()->getParent()) && + SI->getPointerAddressSpace() == 0) && + SI->getPointerOperand() == I; // A call to null is undefined. if (auto CS = CallSite(Use)) - return CS.getCalledValue() == I; + return !NullPointerIsDefined(CS->getParent()->getParent()) && + CS.getCalledValue() == I; } return false; } Index: test/Transforms/IPConstantProp/PR26044.ll =================================================================== --- test/Transforms/IPConstantProp/PR26044.ll +++ test/Transforms/IPConstantProp/PR26044.ll @@ -23,9 +23,40 @@ ret i32 %cond } +define void @fn_no_null_opt() #0 { +entry: + br label %if.end + +for.cond1: ; preds = %if.end, %for.end + br i1 undef, label %if.end, label %if.end + +if.end: ; preds = %lbl, %for.cond1 + %e.2 = phi i32* [ undef, %entry ], [ null, %for.cond1 ], [ null, %for.cond1 ] + %0 = load i32, i32* %e.2, align 4 + %call = call i32 @fn0(i32 %0) + br label %for.cond1 +} + +define internal i32 @fn0(i32 %p1) { +entry: + %tobool = icmp ne i32 %p1, 0 + %cond = select i1 %tobool, i32 %p1, i32 %p1 + ret i32 %cond +} + +attributes #0 = { "null-pointer-is-valid"="true" } + ; CHECK-LABEL: define void @fn2( ; CHECK: call i32 @fn1(i32 undef) ; CHECK-LABEL: define internal i32 @fn1( ; CHECK:%[[COND:.*]] = select i1 undef, i32 undef, i32 undef ; CHECK: ret i32 %[[COND]] + +; CHECK-LABEL: define void @fn_no_null_opt( +; CHECK: call i32 @fn0(i32 %0) + +; CHECK-LABEL: define internal i32 @fn0( +; CHECK:%[[TOBOOL:.*]] = icmp ne i32 %p1, 0 +; CHECK:%[[COND:.*]] = select i1 %[[TOBOOL]], i32 %p1, i32 %p1 +; CHECK: ret i32 %[[COND]] Index: test/Transforms/InstCombine/load.ll =================================================================== --- test/Transforms/InstCombine/load.ll +++ test/Transforms/InstCombine/load.ll @@ -60,6 +60,16 @@ ret i32 %R } +; CHECK-LABEL: @test7_no_null_opt( +; CHECK: %V = getelementptr i32, i32* null +; CHECK: %R = load i32, i32* %V +define i32 @test7_no_null_opt(i32 %X) #0 { + %V = getelementptr i32, i32* null, i32 %X ; [#uses=1] + %R = load i32, i32* %V ; [#uses=1] + ret i32 %R +} +attributes #0 = { "null-pointer-is-valid"="true" } + ; CHECK-LABEL: @test8( ; CHECK-NOT: load define i32 @test8(i32* %P) { Index: test/Transforms/InstCombine/select.ll =================================================================== --- test/Transforms/InstCombine/select.ll +++ test/Transforms/InstCombine/select.ll @@ -385,6 +385,31 @@ ret i32 %V } +;; It may be legal to load from a null address with null pointer valid attribute. +define i32 @test16_no_null_opt(i1 %C, i32* %P) #0 { +; CHECK-LABEL: @test16_no_null_opt( +; CHECK-NEXT: [[P2:%.*]] = select i1 [[C:%.*]], i32* [[P:%.*]], i32* null +; CHECK-NEXT: [[V:%.*]] = load i32, i32* [[P2]], align 4 +; CHECK-NEXT: ret i32 [[V]] +; + %P2 = select i1 %C, i32* %P, i32* null + %V = load i32, i32* %P2 + ret i32 %V +} + +define i32 @test16_no_null_opt_2(i1 %C, i32* %P) #0 { +; CHECK-LABEL: @test16_no_null_opt_2( +; CHECK-NEXT: [[P2:%.*]] = select i1 [[C:%.*]], i32* null, i32* [[P:%.*]] +; CHECK-NEXT: [[V:%.*]] = load i32, i32* [[P2]], align 4 +; CHECK-NEXT: ret i32 [[V]] +; + %P2 = select i1 %C, i32* null, i32* %P + %V = load i32, i32* %P2 + ret i32 %V +} + +attributes #0 = { "null-pointer-is-valid"="true" } + define i1 @test17(i32* %X, i1 %C) { ; CHECK-LABEL: @test17( ; CHECK-NEXT: [[RV1:%.*]] = icmp eq i32* [[X:%.*]], null Index: test/Transforms/InstCombine/store.ll =================================================================== --- test/Transforms/InstCombine/store.ll +++ test/Transforms/InstCombine/store.ll @@ -28,6 +28,17 @@ ret void } +define void @store_at_gep_off_no_null_opt(i64 %offset) #0 { + %ptr = getelementptr i32, i32 *null, i64 %offset + store i32 24, i32* %ptr + ret void +; CHECK-LABEL: @store_at_gep_off_no_null_opt(i64 %offset) +; CHECK-NEXT: %ptr = getelementptr i32, i32* null, i64 %offset +; CHECK-NEXT: store i32 24, i32* %ptr +} + +attributes #0 = { "null-pointer-is-valid"="true" } + ;; Simple sinking tests ; "if then else" Index: test/Transforms/SimplifyCFG/UnreachableEliminate.ll =================================================================== --- test/Transforms/SimplifyCFG/UnreachableEliminate.ll +++ test/Transforms/SimplifyCFG/UnreachableEliminate.ll @@ -78,6 +78,28 @@ ret void } +define void @test5_no_null_opt(i1 %cond, i8* %ptr) #0 { + +; CHECK-LABEL: test5_no_null_opt +; CHECK: entry: +; CHECK: %[[SEL:.*]] = select i1 %cond, i8* null, i8* %ptr +; CHECK: store i8 2, i8* %[[SEL]] + +entry: + br i1 %cond, label %bb1, label %bb3 + +bb3: + br label %bb2 + +bb1: + br label %bb2 + +bb2: + %ptr.2 = phi i8* [ %ptr, %bb3 ], [ null, %bb1 ] + store i8 2, i8* %ptr.2, align 8 + ret void +} + ; CHECK-LABEL: test6 ; CHECK: entry: ; CHECK-NOT: select @@ -97,6 +119,25 @@ ret void } +; CHECK-LABEL: test6_no_null_opt +; CHECK: entry: +; CHECK: %[[SEL:.*]] = select i1 %cond, i8* null, i8* %ptr +; CHECK: store i8 2, i8* %[[SEL]] + +define void @test6_no_null_opt(i1 %cond, i8* %ptr) #0 { +entry: + br i1 %cond, label %bb1, label %bb2 + +bb1: + br label %bb2 + +bb2: + %ptr.2 = phi i8* [ %ptr, %entry ], [ null, %bb1 ] + store i8 2, i8* %ptr.2, align 8 + ret void +} + + define i32 @test7(i1 %X) { entry: br i1 %X, label %if, label %else @@ -127,3 +168,21 @@ } ; CHECK-LABEL: define void @test8( ; CHECK: call void %Y( + +define void @test8_no_null_opt(i1 %X, void ()* %Y) #0 { +entry: + br i1 %X, label %if, label %else + +if: + br label %else + +else: + %phi = phi void ()* [ %Y, %entry ], [ null, %if ] + call void %phi() + ret void +} +attributes #0 = { "null-pointer-is-valid"="true" } + +; CHECK-LABEL: define void @test8_no_null_opt( +; CHECK: %[[SEL:.*]] = select i1 %X, void ()* null, void ()* %Y +; CHECK: call void %[[SEL]] Index: test/Transforms/SimplifyCFG/phi-undef-loadstore.ll =================================================================== --- test/Transforms/SimplifyCFG/phi-undef-loadstore.ll +++ test/Transforms/SimplifyCFG/phi-undef-loadstore.ll @@ -31,6 +31,37 @@ ; CHECK: phi i32* [ %a, %if.then ], [ %c, %if.else ] } +define i32 @test1_no_null_opt(i32* %a, i32 %b, i32* %c, i32 %d) nounwind #0 { +entry: + %tobool = icmp eq i32 %b, 0 + br i1 %tobool, label %if.else, label %if.then + +if.then: ; preds = %entry + tail call void @bar() nounwind + br label %if.end7 + +if.else: ; preds = %entry + %tobool3 = icmp eq i32 %d, 0 + br i1 %tobool3, label %if.end7, label %if.then4 + +if.then4: ; preds = %if.else + tail call void @bar() nounwind + br label %if.end7 + +if.end7: ; preds = %if.else, %if.then4, %if.then + %x.0 = phi i32* [ %a, %if.then ], [ %c, %if.then4 ], [ null, %if.else ] + %tmp9 = load i32, i32* %x.0 + ret i32 %tmp9 + +; CHECK-LABEL: @test1_no_null_opt( +; CHECK: if.then: +; CHECK: if.else: +; CHECK: if.then4: +; CHECK: br label %if.end7 +; CHECK: if.end7: +; CHECK-NEXT: phi i32* [ %a, %if.then ], [ %c, %if.then4 ], [ null, %if.else ] +} + define i32 @test2(i32* %a, i32 %b, i32* %c, i32 %d) nounwind { entry: %tobool = icmp eq i32 %b, 0 @@ -59,6 +90,35 @@ ; CHECK-NOT: phi } +define i32 @test2_no_null_opt(i32* %a, i32 %b, i32* %c, i32 %d) nounwind #0 { +entry: + %tobool = icmp eq i32 %b, 0 + br i1 %tobool, label %if.else, label %if.then + +if.then: ; preds = %entry + tail call void @bar() nounwind + br label %if.end7 + +if.else: ; preds = %entry + %tobool3 = icmp eq i32 %d, 0 + br i1 %tobool3, label %if.end7, label %if.then4 + +if.then4: ; preds = %if.else + tail call void @bar() nounwind + br label %if.end7 + +if.end7: ; preds = %if.else, %if.then4, %if.then + %x.0 = phi i32* [ %a, %if.then ], [ null, %if.then4 ], [ null, %if.else ] + %tmp9 = load i32, i32* %x.0 + ret i32 %tmp9 +; CHECK-LABEL: @test2_no_null_opt( +; CHECK: if.then: +; CHECK: if.else: +; CHECK: if.then4: +; CHECK: if.end7: +; CHECK-NEXT: phi i32* [ %a, %if.then ], [ null, %if.then4 ], [ null, %if.else ] +} + define i32 @test3(i32* %a, i32 %b, i32* %c, i32 %d) nounwind { entry: %tobool = icmp eq i32 %b, 0 @@ -86,6 +146,36 @@ ; CHECK: phi i32* [ %a, %if.then ], [ null, %if.then4 ], [ null, %if.else ] } +define i32 @test3_no_null_opt(i32* %a, i32 %b, i32* %c, i32 %d) nounwind #0 { +entry: + %tobool = icmp eq i32 %b, 0 + br i1 %tobool, label %if.else, label %if.then + +if.then: ; preds = %entry + tail call void @bar() nounwind + br label %if.end7 + +if.else: ; preds = %entry + %tobool3 = icmp eq i32 %d, 0 + br i1 %tobool3, label %if.end7, label %if.then4 + +if.then4: ; preds = %if.else + tail call void @bar() nounwind + br label %if.end7 + +if.end7: ; preds = %if.else, %if.then4, %if.then + %x.0 = phi i32* [ %a, %if.then ], [ null, %if.then4 ], [ null, %if.else ] + tail call void @bar() nounwind + %tmp9 = load i32, i32* %x.0 + ret i32 %tmp9 +; CHECK-LABEL: @test3_no_null_opt( +; CHECK: if.then: +; CHECK: if.else: +; CHECK: if.then4: +; CHECK: if.end7: +; CHECK-NEXT: phi i32* [ %a, %if.then ], [ null, %if.then4 ], [ null, %if.else ] +} + define i32 @test4(i32* %a, i32 %b, i32* %c, i32 %d) nounwind { entry: %tobool = icmp eq i32 %b, 0 @@ -113,3 +203,37 @@ ; CHECK-LABEL: @test4( ; CHECK-NOT: phi } + +define i32 @test4_no_null_opt(i32* %a, i32 %b, i32* %c, i32 %d) nounwind #0 { +entry: + %tobool = icmp eq i32 %b, 0 + br i1 %tobool, label %if.else, label %if.then + +if.then: ; preds = %entry + tail call void @bar() nounwind + br label %if.end7 + +if.else: ; preds = %entry + %tobool3 = icmp eq i32 %d, 0 + br i1 %tobool3, label %if.end7, label %if.then4 + +if.then4: ; preds = %if.else + tail call void @bar() nounwind + br label %if.end7 + +if.end7: ; preds = %if.else, %if.then4, %if.then + %x.0 = phi i32* [ %a, %if.then ], [ null, %if.then4 ], [ null, %if.else ] + %gep = getelementptr i32, i32* %x.0, i32 10 + %tmp9 = load i32, i32* %gep + %tmp10 = or i32 %tmp9, 1 + store i32 %tmp10, i32* %gep + ret i32 %tmp9 +; CHECK-LABEL: @test4_no_null_opt( +; CHECK: if.then: +; CHECK: if.else: +; CHECK: if.then4: +; CHECK: if.end7: +; CHECK-NEXT: phi i32* [ %a, %if.then ], [ null, %if.then4 ], [ null, %if.else ] +} + +attributes #0 = { "null-pointer-is-valid"="true" }