diff --git a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp index 3fa3c0f1f52b0..8f717cb43bcb4 100644 --- a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp +++ b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp @@ -7573,11 +7573,31 @@ static bool passingValueIsAlwaysUndefined(Value *V, Instruction *I, bool PtrValu return false; if (C->isNullValue() || isa<UndefValue>(C)) { - // Only look at the first use, avoid hurting compile time with long uselists - auto *Use = cast<Instruction>(*I->user_begin()); + // Only look at the first use we can handle, avoid hurting compile time with + // long uselists + auto FindUse = llvm::find_if(I->users(), [](auto *U) { + auto *Use = cast<Instruction>(U); + // Change this list when we want to add new instructions. + switch (Use->getOpcode()) { + default: + return false; + case Instruction::GetElementPtr: + case Instruction::Ret: + case Instruction::BitCast: + case Instruction::Load: + case Instruction::Store: + case Instruction::Call: + case Instruction::CallBr: + case Instruction::Invoke: + return true; + } + }); + if (FindUse == I->user_end()) + return false; + auto *Use = cast<Instruction>(*FindUse); // Bail out if Use is not in the same BB as I or Use == I or Use comes - // before I in the block. The latter two can be the case if Use is a PHI - // node. + // before I in the block. The latter two can be the case if Use is a + // PHI node. if (Use->getParent() != I->getParent() || Use == I || Use->comesBefore(I)) return false; diff --git a/llvm/test/Transforms/PhaseOrdering/pr98799-inline-simplifycfg-ub.ll b/llvm/test/Transforms/PhaseOrdering/pr98799-inline-simplifycfg-ub.ll new file mode 100644 index 0000000000000..17073fa198202 --- /dev/null +++ b/llvm/test/Transforms/PhaseOrdering/pr98799-inline-simplifycfg-ub.ll @@ -0,0 +1,56 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5 +; RUN: opt < %s -passes=inline,simplifycfg -S | FileCheck --check-prefix=CUSTOM %s +; RUN: opt < %s -O2 -S | FileCheck --check-prefix=O2 %s + +define internal ptr @bar(ptr %arg, i1 %arg1) { +bb: + br i1 %arg1, label %bb4, label %bb2 + +bb2: + %i = load ptr, ptr %arg, align 8 + %i3 = getelementptr inbounds i8, ptr %i, i64 1 + store ptr %i3, ptr %arg, align 8 + br label %bb4 + +bb4: + %i5 = phi ptr [ %i, %bb2 ], [ null, %bb ] + ret ptr %i5 +} + +define i32 @foo(ptr %arg, i1 %arg1) { +; CUSTOM-LABEL: define i32 @foo( +; CUSTOM-SAME: ptr [[ARG:%.*]], i1 [[ARG1:%.*]]) { +; CUSTOM-NEXT: [[BB:.*:]] +; CUSTOM-NEXT: [[TMP0:%.*]] = xor i1 [[ARG1]], true +; CUSTOM-NEXT: call void @llvm.assume(i1 [[TMP0]]) +; CUSTOM-NEXT: [[I_I:%.*]] = load ptr, ptr [[ARG]], align 8 +; CUSTOM-NEXT: [[I3_I:%.*]] = getelementptr inbounds i8, ptr [[I_I]], i64 1 +; CUSTOM-NEXT: store ptr [[I3_I]], ptr [[ARG]], align 8 +; CUSTOM-NEXT: [[I2:%.*]] = icmp ne ptr [[I_I]], null +; CUSTOM-NEXT: call void @llvm.assume(i1 [[I2]]) +; CUSTOM-NEXT: [[I3:%.*]] = load i32, ptr [[I_I]], align 4 +; CUSTOM-NEXT: ret i32 [[I3]] +; +; O2-LABEL: define i32 @foo( +; O2-SAME: ptr nocapture [[ARG:%.*]], i1 [[ARG1:%.*]]) local_unnamed_addr #[[ATTR0:[0-9]+]] { +; O2-NEXT: [[BB:.*:]] +; O2-NEXT: [[TMP0:%.*]] = xor i1 [[ARG1]], true +; O2-NEXT: tail call void @llvm.assume(i1 [[TMP0]]) +; O2-NEXT: [[I_I:%.*]] = load ptr, ptr [[ARG]], align 8, !nonnull [[META0:![0-9]+]], !noundef [[META0]] +; O2-NEXT: [[I3_I:%.*]] = getelementptr inbounds i8, ptr [[I_I]], i64 1 +; O2-NEXT: store ptr [[I3_I]], ptr [[ARG]], align 8 +; O2-NEXT: [[I3:%.*]] = load i32, ptr [[I_I]], align 4 +; O2-NEXT: ret i32 [[I3]] +; +bb: + %i = call ptr @bar(ptr %arg, i1 %arg1) + %i2 = icmp ne ptr %i, null + call void @llvm.assume(i1 %i2) + %i3 = load i32, ptr %i, align 4 + ret i32 %i3 +} + +declare void @llvm.assume(i1) +;. +; O2: [[META0]] = !{} +;. diff --git a/llvm/test/Transforms/SimplifyCFG/UnreachableEliminate.ll b/llvm/test/Transforms/SimplifyCFG/UnreachableEliminate.ll index ef2d3219cca9b..c4602e72ecbce 100644 --- a/llvm/test/Transforms/SimplifyCFG/UnreachableEliminate.ll +++ b/llvm/test/Transforms/SimplifyCFG/UnreachableEliminate.ll @@ -20,7 +20,7 @@ F: define void @test2() personality ptr @__gxx_personality_v0 { ; CHECK-LABEL: @test2( ; CHECK-NEXT: entry: -; CHECK-NEXT: call void @test2() #[[ATTR3:[0-9]+]] +; CHECK-NEXT: call void @test2() #[[ATTR4:[0-9]+]] ; CHECK-NEXT: ret void ; entry: @@ -242,6 +242,8 @@ declare ptr @fn_nonnull_deref_arg(ptr nonnull dereferenceable(4) %p) declare ptr @fn_nonnull_deref_or_null_arg(ptr nonnull dereferenceable_or_null(4) %p) declare ptr @fn_nonnull_arg(ptr nonnull %p) declare ptr @fn_noundef_arg(ptr noundef %p) +declare ptr @fn_ptr_arg(ptr) +declare ptr @fn_ptr_arg_nounwind_willreturn(ptr) nounwind willreturn define void @test9(i1 %X, ptr %Y) { ; CHECK-LABEL: @test9( @@ -855,10 +857,72 @@ exit: ret i32 %res } +; From bb to bb5 is UB. +define i32 @test9_null_user_order_1(ptr %arg, i1 %arg1, ptr %arg2) { +; CHECK-LABEL: @test9_null_user_order_1( +; CHECK-NEXT: bb: +; CHECK-NEXT: [[TMP0:%.*]] = xor i1 [[ARG1:%.*]], true +; CHECK-NEXT: call void @llvm.assume(i1 [[TMP0]]) +; CHECK-NEXT: [[I:%.*]] = load ptr, ptr [[ARG:%.*]], align 8 +; CHECK-NEXT: [[I4:%.*]] = getelementptr inbounds i8, ptr [[I]], i64 1 +; CHECK-NEXT: store ptr [[I4]], ptr [[ARG]], align 8 +; CHECK-NEXT: [[I7:%.*]] = load i32, ptr [[I]], align 4 +; CHECK-NEXT: [[I8:%.*]] = icmp ne ptr [[I]], [[ARG2:%.*]] +; CHECK-NEXT: call void @fn_ptr_arg(i1 [[I8]]) +; CHECK-NEXT: ret i32 [[I7]] +; +bb: + br i1 %arg1, label %bb5, label %bb3 + +bb3: ; preds = %bb + %i = load ptr, ptr %arg, align 8 + %i4 = getelementptr inbounds i8, ptr %i, i64 1 + store ptr %i4, ptr %arg, align 8 + br label %bb5 + +bb5: ; preds = %bb3, %bb + %i6 = phi ptr [ %i, %bb3 ], [ null, %bb ] + %i7 = load i32, ptr %i6, align 4 + %i8 = icmp ne ptr %i6, %arg2 + call void @fn_ptr_arg(i1 %i8) + ret i32 %i7 +} + +define i32 @test9_null_user_order_2(ptr %arg, i1 %arg1, ptr %arg2) { +; CHECK-LABEL: @test9_null_user_order_2( +; CHECK-NEXT: bb: +; CHECK-NEXT: [[TMP0:%.*]] = xor i1 [[ARG1:%.*]], true +; CHECK-NEXT: call void @llvm.assume(i1 [[TMP0]]) +; CHECK-NEXT: [[I:%.*]] = load ptr, ptr [[ARG:%.*]], align 8 +; CHECK-NEXT: [[I4:%.*]] = getelementptr inbounds i8, ptr [[I]], i64 1 +; CHECK-NEXT: store ptr [[I4]], ptr [[ARG]], align 8 +; CHECK-NEXT: [[I8:%.*]] = icmp ne ptr [[I]], [[ARG2:%.*]] +; CHECK-NEXT: call void @fn_ptr_arg_nounwind_willreturn(i1 [[I8]]) +; CHECK-NEXT: [[I7:%.*]] = load i32, ptr [[I]], align 4 +; CHECK-NEXT: ret i32 [[I7]] +; +bb: + br i1 %arg1, label %bb5, label %bb3 + +bb3: ; preds = %bb + %i = load ptr, ptr %arg, align 8 + %i4 = getelementptr inbounds i8, ptr %i, i64 1 + store ptr %i4, ptr %arg, align 8 + br label %bb5 + +bb5: ; preds = %bb3, %bb + %i6 = phi ptr [ %i, %bb3 ], [ null, %bb ] + %i8 = icmp ne ptr %i6, %arg2 + call void @fn_ptr_arg_nounwind_willreturn(i1 %i8) + %i7 = load i32, ptr %i6, align 4 + ret i32 %i7 +} + attributes #0 = { null_pointer_is_valid } ;. ; CHECK: attributes #[[ATTR0:[0-9]+]] = { nocallback nofree nosync nounwind willreturn memory(inaccessiblemem: write) } ; CHECK: attributes #[[ATTR1:[0-9]+]] = { nocallback nofree nosync nounwind speculatable willreturn memory(none) } ; CHECK: attributes #[[ATTR2:[0-9]+]] = { null_pointer_is_valid } -; CHECK: attributes #[[ATTR3]] = { nounwind } +; CHECK: attributes #[[ATTR3:[0-9]+]] = { nounwind willreturn } +; CHECK: attributes #[[ATTR4]] = { nounwind } ;.