diff --git a/llvm/lib/Transforms/Scalar/GVN.cpp b/llvm/lib/Transforms/Scalar/GVN.cpp --- a/llvm/lib/Transforms/Scalar/GVN.cpp +++ b/llvm/lib/Transforms/Scalar/GVN.cpp @@ -1605,12 +1605,35 @@ Constant::getNullValue(Int8Ty->getPointerTo()), IntrinsicI); if (MSSAU) { + const MemoryUseOrDef *FirstNonDom = nullptr; + const auto *AL = + MSSAU->getMemorySSA()->getBlockAccesses(IntrinsicI->getParent()); + + // If there are accesses in the current basic block, find the first one + // that does not come before NewS. The new memory access is inserted + // after the found access or before the terminator if no such access is + // found. + if (AL) { + for (auto &Acc : *AL) { + if (auto *Current = dyn_cast(&Acc)) + if (!Current->getMemoryInst()->comesBefore(NewS)) { + FirstNonDom = Current; + break; + } + } + } + // This added store is to null, so it will never executed and we can // just use the LiveOnEntry def as defining access. - auto *NewDef = MSSAU->createMemoryAccessInBB( - NewS, MSSAU->getMemorySSA()->getLiveOnEntryDef(), NewS->getParent(), - MemorySSA::BeforeTerminator); - MSSAU->insertDef(cast(NewDef), /*RenameUses=*/true); + auto *NewDef = + FirstNonDom ? MSSAU->createMemoryAccessBefore( + NewS, MSSAU->getMemorySSA()->getLiveOnEntryDef(), + const_cast(FirstNonDom)) + : MSSAU->createMemoryAccessInBB( + NewS, MSSAU->getMemorySSA()->getLiveOnEntryDef(), + NewS->getParent(), MemorySSA::BeforeTerminator); + + MSSAU->insertDef(cast(NewDef), /*RenameUses=*/false); } } if (isAssumeWithEmptyBundle(*IntrinsicI)) diff --git a/llvm/test/Transforms/GVN/preserve-memoryssa.ll b/llvm/test/Transforms/GVN/preserve-memoryssa.ll --- a/llvm/test/Transforms/GVN/preserve-memoryssa.ll +++ b/llvm/test/Transforms/GVN/preserve-memoryssa.ll @@ -93,3 +93,56 @@ for.body.i22: ret i32 1 } + +define void @test_assume_false_to_store_undef_1(i32* %ptr) { +; CHECK-LABEL: @test_assume_false_to_store_undef_1( +; CHECK-NEXT: store i32 10, i32* [[PTR:%.*]], align 4 +; CHECK-NEXT: store i8 undef, i8* null, align 1 +; CHECK-NEXT: call void @f() +; CHECK-NEXT: ret void +; + store i32 10, i32* %ptr + %tobool = icmp ne i16 1, 0 + %xor = xor i1 %tobool, true + call void @llvm.assume(i1 %xor) + call void @f() + ret void +} + +define i32 @test_assume_false_to_store_undef_2(i32* %ptr, i32* %ptr.2) { +; CHECK-LABEL: @test_assume_false_to_store_undef_2( +; CHECK-NEXT: store i32 10, i32* [[PTR:%.*]], align 4 +; CHECK-NEXT: [[LV:%.*]] = load i32, i32* [[PTR_2:%.*]], align 4 +; CHECK-NEXT: store i8 undef, i8* null, align 1 +; CHECK-NEXT: call void @f() +; CHECK-NEXT: ret i32 [[LV]] +; + store i32 10, i32* %ptr + %lv = load i32, i32* %ptr.2 + %tobool = icmp ne i16 1, 0 + %xor = xor i1 %tobool, true + call void @llvm.assume(i1 %xor) + call void @f() + ret i32 %lv +} + +define i32 @test_assume_false_to_store_undef_3(i32* %ptr, i32* %ptr.2) { +; CHECK-LABEL: @test_assume_false_to_store_undef_3( +; CHECK-NEXT: store i32 10, i32* [[PTR:%.*]], align 4 +; CHECK-NEXT: [[LV:%.*]] = load i32, i32* [[PTR_2:%.*]], align 4 +; CHECK-NEXT: store i8 undef, i8* null, align 1 +; CHECK-NEXT: ret i32 [[LV]] +; + store i32 10, i32* %ptr + %lv = load i32, i32* %ptr.2 + %tobool = icmp ne i16 1, 0 + %xor = xor i1 %tobool, true + call void @llvm.assume(i1 %xor) + ret i32 %lv +} + +declare void @f() + +declare void @llvm.assume(i1 noundef) #0 + +attributes #0 = { nofree nosync nounwind willreturn }