Index: llvm/lib/Transforms/IPO/AttributorAttributes.cpp =================================================================== --- llvm/lib/Transforms/IPO/AttributorAttributes.cpp +++ llvm/lib/Transforms/IPO/AttributorAttributes.cpp @@ -1980,6 +1980,40 @@ return true; }; + auto InspectCallSiteForUB = [&](Instruction &I) { + // Skip instructions that are already saved. + if (AssumedNoUBInsts.count(&I) || KnownUBInsts.count(&I)) + return true; + // Check nonnull argument attribute violation for each callsite. + CallBase *CB = cast(&I); + const Function *Callee = CB->getCalledFunction(); + if (!Callee) + return true; + unsigned ArgNum = + std::min(CB->getNumArgOperands(), (unsigned)Callee->arg_size()); + for (unsigned idx = 0; idx < ArgNum; idx++) { + Value *ArgVal = CB->getArgOperand(idx); + const auto &ValueSimplifyAA = + A.getAAFor(*this, IRPosition::value(*ArgVal)); + Optional SimplifiedVal = + ValueSimplifyAA.getAssumedSimplifiedValue(A); + + // If current argument is known to be simplified to null pointer, + // this callsite is considered UB. + if (!ValueSimplifyAA.isKnown() || !SimplifiedVal.hasValue()) + continue; + if (!isa(*SimplifiedVal.getValue())) + continue; + auto &NonNullAA = A.getAAFor( + *this, IRPosition::argument(*Callee->getArg(idx))); + if (NonNullAA.isKnownNonNull()) { + KnownUBInsts.insert(&I); + return true; + } + } + return true; + }; + A.checkForAllInstructions(InspectMemAccessInstForUB, *this, {Instruction::Load, Instruction::Store, Instruction::AtomicCmpXchg, @@ -1987,6 +2021,7 @@ /* CheckBBLivenessOnly */ true); A.checkForAllInstructions(InspectBrInstForUB, *this, {Instruction::Br}, /* CheckBBLivenessOnly */ true); + A.checkForAllCallLikeInstructions(InspectCallSiteForUB, *this); if (NoUBPrevSize != AssumedNoUBInsts.size() || UBPrevSize != KnownUBInsts.size()) return ChangeStatus::CHANGED; Index: llvm/test/Transforms/Attributor/undefined_behavior.ll =================================================================== --- llvm/test/Transforms/Attributor/undefined_behavior.ll +++ llvm/test/Transforms/Attributor/undefined_behavior.ll @@ -579,3 +579,139 @@ %X = call i32 @callee(i1 false, i32* null) ret i32 %X } + +; Tests for nonnull attribute violation. + +define void @arg_nonnull(i32* nonnull %a) { +; IS__TUNIT____: Function Attrs: argmemonly nofree nosync nounwind willreturn writeonly +; IS__TUNIT____-LABEL: define {{[^@]+}}@arg_nonnull +; IS__TUNIT____-SAME: (i32* nocapture nofree nonnull writeonly align 4 dereferenceable(4) [[A:%.*]]) +; IS__TUNIT____-NEXT: store i32 0, i32* [[A]], align 4 +; IS__TUNIT____-NEXT: ret void +; +; IS__CGSCC____: Function Attrs: argmemonly nofree norecurse nosync nounwind willreturn writeonly +; IS__CGSCC____-LABEL: define {{[^@]+}}@arg_nonnull +; IS__CGSCC____-SAME: (i32* nocapture nofree nonnull writeonly align 4 dereferenceable(4) [[A:%.*]]) +; IS__CGSCC____-NEXT: store i32 0, i32* [[A]], align 4 +; IS__CGSCC____-NEXT: ret void +; + store i32 0, i32* %a + ret void +} + +define void @arg_nonnull2(i32* nonnull %a, i32* nonnull %b, i32* %c) { +; IS__TUNIT____: Function Attrs: argmemonly nofree nosync nounwind willreturn writeonly +; IS__TUNIT____-LABEL: define {{[^@]+}}@arg_nonnull2 +; IS__TUNIT____-SAME: (i32* nocapture nofree nonnull writeonly [[A:%.*]], i32* nocapture nofree nonnull writeonly [[B:%.*]], i32* nofree writeonly [[C:%.*]]) +; IS__TUNIT____-NEXT: [[D:%.*]] = icmp eq i32* [[C]], null +; IS__TUNIT____-NEXT: br i1 [[D]], label [[T:%.*]], label [[F:%.*]] +; IS__TUNIT____: t: +; IS__TUNIT____-NEXT: store i32 0, i32* [[A]], align 4 +; IS__TUNIT____-NEXT: br label [[RET:%.*]] +; IS__TUNIT____: f: +; IS__TUNIT____-NEXT: store i32 1, i32* [[B]], align 4 +; IS__TUNIT____-NEXT: br label [[RET]] +; IS__TUNIT____: ret: +; IS__TUNIT____-NEXT: ret void +; +; IS__CGSCC____: Function Attrs: argmemonly nofree norecurse nosync nounwind willreturn writeonly +; IS__CGSCC____-LABEL: define {{[^@]+}}@arg_nonnull2 +; IS__CGSCC____-SAME: (i32* nocapture nofree nonnull writeonly [[A:%.*]], i32* nocapture nofree nonnull writeonly [[B:%.*]], i32* nofree writeonly [[C:%.*]]) +; IS__CGSCC____-NEXT: [[D:%.*]] = icmp eq i32* [[C]], null +; IS__CGSCC____-NEXT: br i1 [[D]], label [[T:%.*]], label [[F:%.*]] +; IS__CGSCC____: t: +; IS__CGSCC____-NEXT: store i32 0, i32* [[A]], align 4 +; IS__CGSCC____-NEXT: br label [[RET:%.*]] +; IS__CGSCC____: f: +; IS__CGSCC____-NEXT: store i32 1, i32* [[B]], align 4 +; IS__CGSCC____-NEXT: br label [[RET]] +; IS__CGSCC____: ret: +; IS__CGSCC____-NEXT: ret void +; + %d = icmp eq i32* %c, null + br i1 %d, label %t, label %f +t: + store i32 0, i32* %a + br label %ret +f: + store i32 1, i32* %b + br label %ret +ret: + ret void +} + +; Pass null directly to argument with nonnull attribute +define void @arg_nonnull_violation1() { +; IS__TUNIT____: Function Attrs: nofree nosync nounwind readnone willreturn +; IS__TUNIT____-LABEL: define {{[^@]+}}@arg_nonnull_violation1() +; IS__TUNIT____-NEXT: unreachable +; +; IS__CGSCC____: Function Attrs: nofree norecurse nosync nounwind readnone willreturn +; IS__CGSCC____-LABEL: define {{[^@]+}}@arg_nonnull_violation1() +; IS__CGSCC____-NEXT: unreachable +; + call void @arg_nonnull(i32* null) + ret void +} + +; A case that depends on value simplification +define void @arg_nonnull_violation2(i1 %c) { +; IS__TUNIT____: Function Attrs: nofree nosync nounwind readnone willreturn +; IS__TUNIT____-LABEL: define {{[^@]+}}@arg_nonnull_violation2 +; IS__TUNIT____-SAME: (i1 [[C:%.*]]) +; IS__TUNIT____-NEXT: unreachable +; +; IS__CGSCC____: Function Attrs: nofree norecurse nosync nounwind readnone willreturn +; IS__CGSCC____-LABEL: define {{[^@]+}}@arg_nonnull_violation2 +; IS__CGSCC____-SAME: (i1 [[C:%.*]]) +; IS__CGSCC____-NEXT: unreachable +; + %null = getelementptr i32, i32* null, i32 0 + %mustnull = select i1 %c, i32* null, i32* %null + call void @arg_nonnull(i32* %mustnull) + ret void +} + +; Cases for single and multiple violation at a callsite +define void @arg_nonnull_violation3(i1 %c) { +; IS__TUNIT____: Function Attrs: nofree nosync nounwind readnone willreturn +; IS__TUNIT____-LABEL: define {{[^@]+}}@arg_nonnull_violation3 +; IS__TUNIT____-SAME: (i1 [[C:%.*]]) +; IS__TUNIT____-NEXT: [[PTR:%.*]] = alloca i32, align 4 +; IS__TUNIT____-NEXT: br i1 [[C]], label [[T:%.*]], label [[F:%.*]] +; IS__TUNIT____: t: +; IS__TUNIT____-NEXT: call void @arg_nonnull2(i32* nocapture nofree nonnull writeonly align 4 dereferenceable(4) [[PTR]], i32* nocapture nofree nonnull writeonly align 4 dereferenceable(4) [[PTR]], i32* noalias nocapture nofree writeonly align 536870912 null) +; IS__TUNIT____-NEXT: unreachable +; IS__TUNIT____: f: +; IS__TUNIT____-NEXT: call void @arg_nonnull2(i32* nocapture nofree nonnull writeonly align 4 dereferenceable(4) [[PTR]], i32* nocapture nofree nonnull writeonly align 4 dereferenceable(4) [[PTR]], i32* nofree nonnull writeonly align 4 dereferenceable(4) [[PTR]]) +; IS__TUNIT____-NEXT: unreachable +; IS__TUNIT____: ret: +; IS__TUNIT____-NEXT: ret void +; +; IS__CGSCC____: Function Attrs: nofree norecurse nosync nounwind readnone willreturn +; IS__CGSCC____-LABEL: define {{[^@]+}}@arg_nonnull_violation3 +; IS__CGSCC____-SAME: (i1 [[C:%.*]]) +; IS__CGSCC____-NEXT: [[PTR:%.*]] = alloca i32, align 4 +; IS__CGSCC____-NEXT: br i1 [[C]], label [[T:%.*]], label [[F:%.*]] +; IS__CGSCC____: t: +; IS__CGSCC____-NEXT: call void @arg_nonnull2(i32* nocapture nofree nonnull writeonly align 4 dereferenceable(4) [[PTR]], i32* nocapture nofree nonnull writeonly align 4 dereferenceable(4) [[PTR]], i32* noalias nocapture nofree writeonly align 536870912 null) +; IS__CGSCC____-NEXT: unreachable +; IS__CGSCC____: f: +; IS__CGSCC____-NEXT: call void @arg_nonnull2(i32* nocapture nofree nonnull writeonly align 4 dereferenceable(4) [[PTR]], i32* nocapture nofree nonnull writeonly align 4 dereferenceable(4) [[PTR]], i32* nofree nonnull writeonly align 4 dereferenceable(4) [[PTR]]) +; IS__CGSCC____-NEXT: unreachable +; IS__CGSCC____: ret: +; IS__CGSCC____-NEXT: ret void +; + %ptr = alloca i32 + br i1 %c, label %t, label %f +t: + call void @arg_nonnull2(i32* %ptr, i32* %ptr, i32* null) + call void @arg_nonnull2(i32* null, i32* %ptr, i32* %ptr) + br label %ret +f: + call void @arg_nonnull2(i32* %ptr, i32* %ptr, i32* %ptr) + call void @arg_nonnull2(i32* null, i32* null, i32* %ptr) + br label %ret +ret: + ret void +} Index: llvm/test/Transforms/Attributor/value-simplify.ll =================================================================== --- llvm/test/Transforms/Attributor/value-simplify.ll +++ llvm/test/Transforms/Attributor/value-simplify.ll @@ -391,12 +391,6 @@ define internal void @test_sret(%struct.X* sret %a, %struct.X** %b) { ; -; IS__TUNIT____: Function Attrs: argmemonly nofree nosync nounwind willreturn writeonly -; IS__TUNIT____-LABEL: define {{[^@]+}}@test_sret -; IS__TUNIT____-SAME: (%struct.X* noalias nofree nonnull sret writeonly align 536870912 dereferenceable(8) [[A:%.*]], %struct.X** nocapture nofree nonnull writeonly align 8 dereferenceable(8) [[B:%.*]]) -; IS__TUNIT____-NEXT: store %struct.X* [[A]], %struct.X** [[B]], align 8 -; IS__TUNIT____-NEXT: ret void -; ; IS__CGSCC____: Function Attrs: argmemonly nofree norecurse nosync nounwind willreturn writeonly ; IS__CGSCC____-LABEL: define {{[^@]+}}@test_sret ; IS__CGSCC____-SAME: (%struct.X* noalias nofree nonnull sret writeonly align 536870912 dereferenceable(8) [[A:%.*]], %struct.X** nocapture nofree nonnull writeonly align 8 dereferenceable(8) [[B:%.*]]) @@ -412,14 +406,12 @@ ; IS__TUNIT____: Function Attrs: argmemonly nofree nosync nounwind willreturn writeonly ; IS__TUNIT____-LABEL: define {{[^@]+}}@complicated_args_sret ; IS__TUNIT____-SAME: (%struct.X** nocapture nofree writeonly [[B:%.*]]) -; IS__TUNIT____-NEXT: call void @test_sret(%struct.X* noalias nocapture nofree writeonly align 536870912 null, %struct.X** nocapture nofree writeonly align 8 [[B]]) -; IS__TUNIT____-NEXT: ret void +; IS__TUNIT____-NEXT: unreachable ; ; IS__CGSCC____: Function Attrs: argmemonly nofree norecurse nosync nounwind willreturn writeonly ; IS__CGSCC____-LABEL: define {{[^@]+}}@complicated_args_sret ; IS__CGSCC____-SAME: (%struct.X** nocapture nofree nonnull writeonly align 8 dereferenceable(8) [[B:%.*]]) -; IS__CGSCC____-NEXT: call void @test_sret(%struct.X* noalias nocapture nofree nonnull writeonly align 536870912 dereferenceable(8) null, %struct.X** nocapture nofree nonnull writeonly align 8 dereferenceable(8) [[B]]) -; IS__CGSCC____-NEXT: ret void +; IS__CGSCC____-NEXT: unreachable ; call void @test_sret(%struct.X* null, %struct.X** %b) ret void