Index: llvm/lib/Transforms/Utils/SimplifyCFG.cpp =================================================================== --- llvm/lib/Transforms/Utils/SimplifyCFG.cpp +++ llvm/lib/Transforms/Utils/SimplifyCFG.cpp @@ -1345,7 +1345,7 @@ return true; } -static bool passingValueIsAlwaysUndefined(Value *V, Instruction *I); +static bool passingValueIsAlwaysUndefined(Value *V, Instruction *I, bool *IsUndefinedFromGEP = nullptr); /// Given a conditional branch that goes to BB1 and BB2, hoist any common code /// in the two blocks up into the branch block. The caller of this function @@ -6544,7 +6544,7 @@ } /// Check if passing a value to an instruction will cause undefined behavior. -static bool passingValueIsAlwaysUndefined(Value *V, Instruction *I) { +static bool passingValueIsAlwaysUndefined(Value *V, Instruction *I, bool *IsUndefinedFromGEP) { Constant *C = dyn_cast(V); if (!C) return false; @@ -6567,12 +6567,15 @@ // Look through GEPs. A load from a GEP derived from NULL is still undefined if (GetElementPtrInst *GEP = dyn_cast(Use)) - if (GEP->getPointerOperand() == I) - return passingValueIsAlwaysUndefined(V, GEP); + if (GEP->getPointerOperand() == I) { + bool isUBFromGEP = true; + return passingValueIsAlwaysUndefined(V, GEP, &isUBFromGEP) && + isUBFromGEP; + } // Look through bitcasts. if (BitCastInst *BC = dyn_cast(Use)) - return passingValueIsAlwaysUndefined(V, BC); + return passingValueIsAlwaysUndefined(V, BC, IsUndefinedFromGEP); // Load from null is undefined. if (LoadInst *LI = dyn_cast(Use)) @@ -6587,10 +6590,37 @@ SI->getPointerAddressSpace())) && SI->getPointerOperand() == I; - // A call to null is undefined. - if (auto *CB = dyn_cast(Use)) - return !NullPointerIsDefined(CB->getFunction()) && - CB->getCalledOperand() == I; + if (auto *CB = dyn_cast(Use)) { + if (NullPointerIsDefined(CB->getFunction())) + return false; + // A call to null is undefined. + if (CB->getCalledOperand() == I) + return true; + + if (C->isNullValue()) { + for (const llvm::Use &Arg : CB->args()) + if (Arg == I) { + unsigned ArgIdx = CB->getArgOperandNo(&Arg); + if (CB->paramHasAttr(ArgIdx, Attribute::NonNull) && + CB->paramHasAttr(ArgIdx, Attribute::NoUndef)) { + if (IsUndefinedFromGEP) + *IsUndefinedFromGEP = false; + // Passing null to a nonnnull+noundef argument is undefined. + return true; + } + } + } else if (isa(C)) { + // Passing undef to a noundef argument is undefined. + for (const llvm::Use &Arg : CB->args()) + if (Arg == I) { + unsigned ArgIdx = CB->getArgOperandNo(&Arg); + if (CB->paramHasAttr(ArgIdx, Attribute::NoUndef)) { + // Passing undef to a noundef argument is undefined. + return true; + } + } + } + } } return false; } Index: llvm/test/Transforms/SimplifyCFG/UnreachableEliminate.ll =================================================================== --- llvm/test/Transforms/SimplifyCFG/UnreachableEliminate.ll +++ llvm/test/Transforms/SimplifyCFG/UnreachableEliminate.ll @@ -69,7 +69,6 @@ ; CHECK-NEXT: store i8 2, i8* [[PTR:%.*]], align 8 ; CHECK-NEXT: ret void ; - entry: br i1 %cond, label %bb1, label %bb3 @@ -92,7 +91,6 @@ ; CHECK-NEXT: store i8 2, i8* [[PTR_2]], align 8 ; CHECK-NEXT: ret void ; - entry: br i1 %cond, label %bb1, label %bb3 @@ -206,13 +204,16 @@ ret void } -declare i8* @foo(i8* nonnull %p); +declare i8* @foo(i8* nonnull noundef %p) +declare i8* @bar(i8* nonnull %p) +declare i8* @baz(i8* noundef %p) + define void @test9(i1 %X, i8* %Y) { -; ; CHECK-LABEL: @test9( ; CHECK-NEXT: entry: -; CHECK-NEXT: [[SPEC_SELECT:%.*]] = select i1 [[X:%.*]], i8* null, i8* [[Y:%.*]] -; CHECK-NEXT: [[TMP0:%.*]] = call i8* @foo(i8* [[SPEC_SELECT]]) +; CHECK-NEXT: [[TMP0:%.*]] = xor i1 [[X:%.*]], true +; CHECK-NEXT: call void @llvm.assume(i1 [[TMP0]]) +; CHECK-NEXT: [[TMP1:%.*]] = call i8* @foo(i8* [[Y:%.*]]) ; CHECK-NEXT: ret void ; entry: @@ -227,8 +228,212 @@ ret void } -define void @test9_no_null_opt(i1 %X, i8* %Y) #0 { +define void @test9_undef(i1 %X, i8* %Y) { +; CHECK-LABEL: @test9_undef( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[TMP0:%.*]] = call i8* @baz(i8* [[Y:%.*]]) +; CHECK-NEXT: ret void +; +entry: + br i1 %X, label %if, label %else + +if: + br label %else + +else: + %phi = phi i8* [ %Y, %entry ], [ undef, %if ] + call i8* @baz(i8* %phi) + ret void +} + +define void @test9_callsite(i1 %X, i8* %Y) { +; CHECK-LABEL: @test9_callsite( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[TMP0:%.*]] = xor i1 [[X:%.*]], true +; CHECK-NEXT: call void @llvm.assume(i1 [[TMP0]]) +; CHECK-NEXT: [[TMP1:%.*]] = call i8* @bar(i8* noundef nonnull [[Y:%.*]]) +; CHECK-NEXT: ret void +; +entry: + br i1 %X, label %if, label %else + +if: + br label %else + +else: + %phi = phi i8* [ %Y, %entry ], [ null, %if ] + call i8* @bar(i8* nonnull noundef %phi) + ret void +} + +define void @test9_gep_mismatch(i1 %X, i8* %Y, i8* %P) { +; CHECK-LABEL: @test9_gep_mismatch( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[SPEC_SELECT:%.*]] = select i1 [[X:%.*]], i8* null, i8* [[Y:%.*]] +; CHECK-NEXT: [[GEP:%.*]] = getelementptr inbounds i8, i8* [[P:%.*]], i64 0 +; CHECK-NEXT: [[TMP0:%.*]] = call i8* @foo(i8* [[GEP]]) +; CHECK-NEXT: ret void +; +entry: + br i1 %X, label %if, label %else + +if: + br label %else + +else: + %phi = phi i8* [ %Y, %entry ], [ null, %if ] + %gep = getelementptr inbounds i8, i8* %P, i64 0 + call i8* @foo(i8* %gep) + ret void +} + +define void @test9_gep_zero(i1 %X, i8* %Y) { +; CHECK-LABEL: @test9_gep_zero( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[SPEC_SELECT:%.*]] = select i1 [[X:%.*]], i8* null, i8* [[Y:%.*]] +; CHECK-NEXT: [[GEP:%.*]] = getelementptr inbounds i8, i8* [[SPEC_SELECT]], i64 0 +; CHECK-NEXT: [[TMP0:%.*]] = call i8* @foo(i8* [[GEP]]) +; CHECK-NEXT: ret void +; +entry: + br i1 %X, label %if, label %else + +if: + br label %else + +else: + %phi = phi i8* [ %Y, %entry ], [ null, %if ] + %gep = getelementptr inbounds i8, i8* %phi, i64 0 + call i8* @foo(i8* %gep) + ret void +} + +define void @test9_gep_bitcast(i1 %X, i32* %Y) { +; CHECK-LABEL: @test9_gep_bitcast( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[SPEC_SELECT:%.*]] = select i1 [[X:%.*]], i32* null, i32* [[Y:%.*]] +; CHECK-NEXT: [[GEP:%.*]] = getelementptr inbounds i32, i32* [[SPEC_SELECT]], i64 0 +; CHECK-NEXT: [[BC:%.*]] = bitcast i32* [[GEP]] to i8* +; CHECK-NEXT: [[TMP0:%.*]] = call i8* @foo(i8* [[BC]]) +; CHECK-NEXT: ret void +; +entry: + br i1 %X, label %if, label %else + +if: + br label %else + +else: + %phi = phi i32* [ %Y, %entry ], [ null, %if ] + %gep = getelementptr inbounds i32, i32* %phi, i64 0 + %bc = bitcast i32* %gep to i8* + call i8* @foo(i8* %bc) + ret void +} + +define void @test9_gep_nonzero(i1 %X, i8* %Y) { +; CHECK-LABEL: @test9_gep_nonzero( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[SPEC_SELECT:%.*]] = select i1 [[X:%.*]], i8* null, i8* [[Y:%.*]] +; CHECK-NEXT: [[GEP:%.*]] = getelementptr inbounds i8, i8* [[SPEC_SELECT]], i64 12 +; CHECK-NEXT: [[TMP0:%.*]] = call i8* @foo(i8* [[GEP]]) +; CHECK-NEXT: ret void ; +entry: + br i1 %X, label %if, label %else + +if: + br label %else + +else: + %phi = phi i8* [ %Y, %entry ], [ null, %if ] + %gep = getelementptr inbounds i8, i8* %phi, i64 12 + call i8* @foo(i8* %gep) + ret void +} + +define void @test9_gep_unknown(i1 %X, i8* %Y, i64 %I) { +; CHECK-LABEL: @test9_gep_unknown( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[SPEC_SELECT:%.*]] = select i1 [[X:%.*]], i8* null, i8* [[Y:%.*]] +; CHECK-NEXT: [[GEP:%.*]] = getelementptr inbounds i8, i8* [[SPEC_SELECT]], i64 [[I:%.*]] +; CHECK-NEXT: [[TMP0:%.*]] = call i8* @foo(i8* [[GEP]]) +; CHECK-NEXT: ret void +; +entry: + br i1 %X, label %if, label %else + +if: + br label %else + +else: + %phi = phi i8* [ %Y, %entry ], [ null, %if ] + %gep = getelementptr inbounds i8, i8* %phi, i64 %I + call i8* @foo(i8* %gep) + ret void +} + +define void @test9_gep_unknown_not_inbounds(i1 %X, i8* %Y, i64 %I) { +; CHECK-LABEL: @test9_gep_unknown_not_inbounds( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[SPEC_SELECT:%.*]] = select i1 [[X:%.*]], i8* null, i8* [[Y:%.*]] +; CHECK-NEXT: [[GEP:%.*]] = getelementptr i8, i8* [[SPEC_SELECT]], i64 [[I:%.*]] +; CHECK-NEXT: [[TMP0:%.*]] = call i8* @foo(i8* [[GEP]]) +; CHECK-NEXT: ret void +; +entry: + br i1 %X, label %if, label %else + +if: + br label %else + +else: + %phi = phi i8* [ %Y, %entry ], [ null, %if ] + %gep = getelementptr i8, i8* %phi, i64 %I + call i8* @foo(i8* %gep) + ret void +} + +define void @test9_gep_unknown_not_inbounds_undef(i1 %X, i8* %Y, i64 %I) { +; CHECK-LABEL: @test9_gep_unknown_not_inbounds_undef( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[GEP:%.*]] = getelementptr i8, i8* [[Y:%.*]], i64 [[I:%.*]] +; CHECK-NEXT: [[TMP0:%.*]] = call i8* @baz(i8* [[GEP]]) +; CHECK-NEXT: ret void +; +entry: + br i1 %X, label %if, label %else + +if: + br label %else + +else: + %phi = phi i8* [ %Y, %entry ], [ undef, %if ] + %gep = getelementptr i8, i8* %phi, i64 %I + call i8* @baz(i8* %gep) + ret void +} + +define void @test9_missing_noundef(i1 %X, i8* %Y) { +; CHECK-LABEL: @test9_missing_noundef( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[SPEC_SELECT:%.*]] = select i1 [[X:%.*]], i8* null, i8* [[Y:%.*]] +; CHECK-NEXT: [[TMP0:%.*]] = call i8* @bar(i8* [[SPEC_SELECT]]) +; CHECK-NEXT: ret void +; +entry: + br i1 %X, label %if, label %else + +if: + br label %else + +else: + %phi = phi i8* [ %Y, %entry ], [ null, %if ] + call i8* @bar(i8* %phi) + ret void +} + +define void @test9_no_null_opt(i1 %X, i8* %Y) #0 { ; CHECK-LABEL: @test9_no_null_opt( ; CHECK-NEXT: entry: ; CHECK-NEXT: [[SPEC_SELECT:%.*]] = select i1 [[X:%.*]], i8* null, i8* [[Y:%.*]]