diff --git a/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp b/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp --- a/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp +++ b/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp @@ -2409,6 +2409,92 @@ return isAllocLikeFn(V, TLI) && V != AI; } +// Detect an alignment check pattern like: +// %alloc = call i8* @_ZnwmSt11align_val_t(i64 32, i64 16) builtin +// %pti = ptrtoint i8* %alloc to i64 +// %and = and i64 %pti, 15 +// %icmp = icmp eq i64 %and, 0 +// call void @llvm.assume(i1 %icmp) +// If detected, store the non-alloc instructions into 'Users'. +static bool isAllocAlignmentAssume(PtrToIntInst *PTII, Instruction *AI, + SmallVectorImpl &Users, + const TargetLibraryInfo *TLI) { + if (!isAllocationFn(AI, TLI)) + return false; + + if (!(PTII->getOperand(0) == AI && PTII->hasOneUse())) + return false; + + if (!isa(*PTII->user_begin())) + return false; + + auto *BO = cast(*PTII->user_begin()); + if (!(BO->getOpcode() == Instruction::And && + (isa(BO->getOperand(0)) || + isa(BO->getOperand(1))) && + BO->hasOneUse())) + return false; + + if (!isa(*BO->user_begin())) + return false; + + auto *ICI = cast(*BO->user_begin()); + if (!(ICI->hasOneUse() && (isa(ICI->getOperand(0)) || + isa(ICI->getOperand(1))))) + return false; + + if (!isa(*ICI->user_begin())) + return false; + + auto *II = cast(*ICI->user_begin()); + if (II->getIntrinsicID() != Intrinsic::assume) + return false; + + Users.emplace_back(PTII); + Users.emplace_back(BO); + Users.emplace_back(ICI); + Users.emplace_back(II); + return true; +} + +// Erase the alignment assume pattern captured by isAllocAlignmentAssume() from +// the function and 'Users'. +static void eraseAllocAlignmentAssume(unsigned I, + SmallVectorImpl &Users, + InstCombiner &IC) { + IntrinsicInst *II = dyn_cast(&*Users[I]); + assert(II->getIntrinsicID() == Intrinsic::assume); + auto *ICI = cast(II->getOperand(0)); + assert(ICI->hasOneUse()); + Users[I] = nullptr; + IC.eraseInstFromFunction(*II); + auto *BO = cast(isa(ICI->getOperand(0)) + ? ICI->getOperand(1) + : ICI->getOperand(0)); + assert(BO->hasOneUse()); + auto *PTII = cast(isa(BO->getOperand(0)) + ? BO->getOperand(1) + : BO->getOperand(0)); + assert(PTII->hasOneUse()); + bool FoundICI = false, FoundBO = false, FoundPTII = false; + for (unsigned J = 0, E = Users.size(); J < E; ++J) { + Value *U = Users[J]; + if (U == ICI || U == BO || U == PTII) { + if (U == ICI) + FoundICI = true; + if (U == BO) + FoundBO = true; + if (U == PTII) + FoundPTII = true; + Users[J] = nullptr; + } + } + assert(FoundICI && FoundBO && FoundPTII); + IC.eraseInstFromFunction(*ICI); + IC.eraseInstFromFunction(*BO); + IC.eraseInstFromFunction(*PTII); +} + static bool isAllocSiteRemovable(Instruction *AI, SmallVectorImpl &Users, const TargetLibraryInfo *TLI) { @@ -2424,6 +2510,12 @@ // Give up the moment we see something we can't handle. return false; + case Instruction::PtrToInt: { + if (isAllocAlignmentAssume(cast(I), AI, Users, TLI)) + continue; + return false; + } + case Instruction::AddrSpaceCast: case Instruction::BitCast: case Instruction::GetElementPtr: @@ -2528,7 +2620,8 @@ replaceInstUsesWith(*I, Result); eraseInstFromFunction(*I); Users[i] = nullptr; // Skip examining in the next loop. - } + } else if (II->getIntrinsicID() == Intrinsic::assume) + eraseAllocAlignmentAssume(i, Users, *this); } } for (unsigned i = 0, e = Users.size(); i != e; ++i) { diff --git a/llvm/test/Transforms/InstCombine/malloc-free-delete.ll b/llvm/test/Transforms/InstCombine/malloc-free-delete.ll --- a/llvm/test/Transforms/InstCombine/malloc-free-delete.ll +++ b/llvm/test/Transforms/InstCombine/malloc-free-delete.ll @@ -276,6 +276,8 @@ ; delete[](void*, unsigned long, align_val_t) declare void @_ZdaPvmSt11align_val_t(i8*, i64, i64) nobuiltin +declare void @llvm.assume(i1) + define void @test8() { ; CHECK-LABEL: @test8( ; CHECK-NEXT: ret void @@ -317,6 +319,15 @@ call void @_ZdaPvmSt11align_val_t(i8* %naa2, i64 32, i64 8) builtin %naja2 = call i8* @_ZnajSt11align_val_t(i32 32, i32 8) builtin call void @_ZdaPvjSt11align_val_t(i8* %naja2, i32 32, i32 8) builtin + + ; Check that the alignment assume does not prevent the removal. + %nwa3 = call i8* @_ZnwmSt11align_val_t(i64 32, i64 16) builtin + %pti = ptrtoint i8* %nwa3 to i64 + %and = and i64 %pti, 15 + %icmp = icmp eq i64 %and, 0 + call void @llvm.assume(i1 %icmp) + call void @_ZdlPvmSt11align_val_t(i8* %nwa3, i64 32, i64 16) builtin + ret void } @@ -343,7 +354,7 @@ define void @test11() { ; CHECK-LABEL: @test11( -; CHECK-NEXT: [[CALL:%.*]] = call dereferenceable(8) i8* @_Znwm(i64 8) #6 +; CHECK-NEXT: [[CALL:%.*]] = call dereferenceable(8) i8* @_Znwm(i64 8) #7 ; CHECK-NEXT: call void @_ZdlPv(i8* nonnull [[CALL]]) ; CHECK-NEXT: ret void ;