diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp index 400ebcf493713..54f777ab20a7a 100644 --- a/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp +++ b/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp @@ -3993,10 +3993,19 @@ Instruction *InstCombinerImpl::visitCallBase(CallBase &Call) { unsigned ArgNo = 0; for (Value *V : Call.args()) { - if (V->getType()->isPointerTy() && - !Call.paramHasAttr(ArgNo, Attribute::NonNull) && - isKnownNonZero(V, getSimplifyQuery().getWithInstruction(&Call))) - ArgNos.push_back(ArgNo); + if (V->getType()->isPointerTy()) { + // Simplify the nonnull operand if the parameter is known to be nonnull. + // Otherwise, try to infer nonnull for it. + if (Call.paramHasNonNullAttr(ArgNo, /*AllowUndefOrPoison=*/true)) { + if (Value *Res = simplifyNonNullOperand(V)) { + replaceOperand(Call, ArgNo, Res); + Changed = true; + } + } else if (isKnownNonZero(V, + getSimplifyQuery().getWithInstruction(&Call))) { + ArgNos.push_back(ArgNo); + } + } ArgNo++; } diff --git a/llvm/lib/Transforms/InstCombine/InstCombineInternal.h b/llvm/lib/Transforms/InstCombine/InstCombineInternal.h index 83e1da98deeda..71c80d4c401f8 100644 --- a/llvm/lib/Transforms/InstCombine/InstCombineInternal.h +++ b/llvm/lib/Transforms/InstCombine/InstCombineInternal.h @@ -455,6 +455,10 @@ class LLVM_LIBRARY_VISIBILITY InstCombinerImpl final Instruction *hoistFNegAboveFMulFDiv(Value *FNegOp, Instruction &FMFSource); + /// Simplify \p V given that it is known to be non-null. + /// Returns the simplified value if possible, otherwise returns nullptr. + Value *simplifyNonNullOperand(Value *V); + public: /// Create and insert the idiom we use to indicate a block is unreachable /// without having to rewrite the CFG from within InstCombine. diff --git a/llvm/lib/Transforms/InstCombine/InstCombineLoadStoreAlloca.cpp b/llvm/lib/Transforms/InstCombine/InstCombineLoadStoreAlloca.cpp index d5534c15cca76..89fc1051b18dc 100644 --- a/llvm/lib/Transforms/InstCombine/InstCombineLoadStoreAlloca.cpp +++ b/llvm/lib/Transforms/InstCombine/InstCombineLoadStoreAlloca.cpp @@ -982,6 +982,19 @@ static bool canSimplifyNullLoadOrGEP(LoadInst &LI, Value *Op) { return false; } +/// TODO: Recursively simplify nonnull value to handle one-use inbounds GEPs. +Value *InstCombinerImpl::simplifyNonNullOperand(Value *V) { + if (auto *Sel = dyn_cast(V)) { + if (isa(Sel->getOperand(1))) + return Sel->getOperand(2); + + if (isa(Sel->getOperand(2))) + return Sel->getOperand(1); + } + + return nullptr; +} + Instruction *InstCombinerImpl::visitLoadInst(LoadInst &LI) { Value *Op = LI.getOperand(0); if (Value *Res = simplifyLoadInst(&LI, Op, SQ.getWithInstruction(&LI))) @@ -1059,20 +1072,13 @@ Instruction *InstCombinerImpl::visitLoadInst(LoadInst &LI) { V2->copyMetadata(LI, Metadata::PoisonGeneratingIDs); return SelectInst::Create(SI->getCondition(), V1, V2); } - - // load (select (cond, null, P)) -> load P - if (isa(SI->getOperand(1)) && - !NullPointerIsDefined(SI->getFunction(), - LI.getPointerAddressSpace())) - return replaceOperand(LI, 0, SI->getOperand(2)); - - // load (select (cond, P, null)) -> load P - if (isa(SI->getOperand(2)) && - !NullPointerIsDefined(SI->getFunction(), - LI.getPointerAddressSpace())) - return replaceOperand(LI, 0, SI->getOperand(1)); } } + + if (!NullPointerIsDefined(LI.getFunction(), LI.getPointerAddressSpace())) + if (Value *V = simplifyNonNullOperand(Op)) + return replaceOperand(LI, 0, V); + return nullptr; } @@ -1437,19 +1443,9 @@ Instruction *InstCombinerImpl::visitStoreInst(StoreInst &SI) { if (isa(Val)) return eraseInstFromFunction(SI); - // TODO: Add a helper to simplify the pointer operand for all memory - // instructions. - // store val, (select (cond, null, P)) -> store val, P - // store val, (select (cond, P, null)) -> store val, P - if (!NullPointerIsDefined(SI.getFunction(), SI.getPointerAddressSpace())) { - if (SelectInst *Sel = dyn_cast(Ptr)) { - if (isa(Sel->getOperand(1))) - return replaceOperand(SI, 1, Sel->getOperand(2)); - - if (isa(Sel->getOperand(2))) - return replaceOperand(SI, 1, Sel->getOperand(1)); - } - } + if (!NullPointerIsDefined(SI.getFunction(), SI.getPointerAddressSpace())) + if (Value *V = simplifyNonNullOperand(Ptr)) + return replaceOperand(SI, 1, V); return nullptr; } diff --git a/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp b/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp index 5621511570b58..b7748f59a0cfc 100644 --- a/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp +++ b/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp @@ -3587,10 +3587,23 @@ Instruction *InstCombinerImpl::visitFree(CallInst &FI, Value *Op) { Instruction *InstCombinerImpl::visitReturnInst(ReturnInst &RI) { Value *RetVal = RI.getReturnValue(); - if (!RetVal || !AttributeFuncs::isNoFPClassCompatibleType(RetVal->getType())) + if (!RetVal) return nullptr; Function *F = RI.getFunction(); + Type *RetTy = RetVal->getType(); + if (RetTy->isPointerTy()) { + if (F->hasRetAttribute(Attribute::NonNull) || + (F->getAttributes().getRetDereferenceableBytes() > 0 && + !NullPointerIsDefined(F, RetTy->getPointerAddressSpace()))) { + if (Value *V = simplifyNonNullOperand(RetVal)) + return replaceOperand(RI, 0, V); + } + } + + if (!AttributeFuncs::isNoFPClassCompatibleType(RetTy)) + return nullptr; + FPClassTest ReturnClass = F->getAttributes().getRetNoFPClass(); if (ReturnClass == fcNone) return nullptr; diff --git a/llvm/test/Transforms/InstCombine/nonnull-select.ll b/llvm/test/Transforms/InstCombine/nonnull-select.ll index 3fab2dfb41a42..cc000b4c88164 100644 --- a/llvm/test/Transforms/InstCombine/nonnull-select.ll +++ b/llvm/test/Transforms/InstCombine/nonnull-select.ll @@ -5,10 +5,7 @@ define nonnull ptr @pr48975(ptr %.0) { ; CHECK-LABEL: @pr48975( -; CHECK-NEXT: [[DOT1:%.*]] = load ptr, ptr [[DOT0:%.*]], align 8 -; CHECK-NEXT: [[DOT2:%.*]] = icmp eq ptr [[DOT1]], null -; CHECK-NEXT: [[DOT4:%.*]] = select i1 [[DOT2]], ptr null, ptr [[DOT0]] -; CHECK-NEXT: ret ptr [[DOT4]] +; CHECK-NEXT: ret ptr [[DOT4:%.*]] ; %.1 = load ptr, ptr %.0, align 8 %.2 = icmp eq ptr %.1, null @@ -18,8 +15,7 @@ define nonnull ptr @pr48975(ptr %.0) { define nonnull ptr @nonnull_ret(i1 %cond, ptr %p) { ; CHECK-LABEL: @nonnull_ret( -; CHECK-NEXT: [[RES:%.*]] = select i1 [[COND:%.*]], ptr [[P:%.*]], ptr null -; CHECK-NEXT: ret ptr [[RES]] +; CHECK-NEXT: ret ptr [[RES:%.*]] ; %res = select i1 %cond, ptr %p, ptr null ret ptr %res @@ -27,8 +23,7 @@ define nonnull ptr @nonnull_ret(i1 %cond, ptr %p) { define nonnull ptr @nonnull_ret2(i1 %cond, ptr %p) { ; CHECK-LABEL: @nonnull_ret2( -; CHECK-NEXT: [[RES:%.*]] = select i1 [[COND:%.*]], ptr null, ptr [[P:%.*]] -; CHECK-NEXT: ret ptr [[RES]] +; CHECK-NEXT: ret ptr [[RES:%.*]] ; %res = select i1 %cond, ptr null, ptr %p ret ptr %res @@ -36,8 +31,7 @@ define nonnull ptr @nonnull_ret2(i1 %cond, ptr %p) { define nonnull noundef ptr @nonnull_noundef_ret(i1 %cond, ptr %p) { ; CHECK-LABEL: @nonnull_noundef_ret( -; CHECK-NEXT: [[RES:%.*]] = select i1 [[COND:%.*]], ptr [[P:%.*]], ptr null -; CHECK-NEXT: ret ptr [[RES]] +; CHECK-NEXT: ret ptr [[RES:%.*]] ; %res = select i1 %cond, ptr %p, ptr null ret ptr %res @@ -45,8 +39,7 @@ define nonnull noundef ptr @nonnull_noundef_ret(i1 %cond, ptr %p) { define nonnull noundef ptr @nonnull_noundef_ret2(i1 %cond, ptr %p) { ; CHECK-LABEL: @nonnull_noundef_ret2( -; CHECK-NEXT: [[RES:%.*]] = select i1 [[COND:%.*]], ptr null, ptr [[P:%.*]] -; CHECK-NEXT: ret ptr [[RES]] +; CHECK-NEXT: ret ptr [[RES:%.*]] ; %res = select i1 %cond, ptr null, ptr %p ret ptr %res @@ -55,8 +48,7 @@ define nonnull noundef ptr @nonnull_noundef_ret2(i1 %cond, ptr %p) { define void @nonnull_call(i1 %cond, ptr %p) { ; CHECK-LABEL: @nonnull_call( -; CHECK-NEXT: [[RES:%.*]] = select i1 [[COND:%.*]], ptr [[P:%.*]], ptr null -; CHECK-NEXT: call void @f(ptr nonnull [[RES]]) +; CHECK-NEXT: call void @f(ptr nonnull [[RES:%.*]]) ; CHECK-NEXT: ret void ; %res = select i1 %cond, ptr %p, ptr null @@ -66,8 +58,7 @@ define void @nonnull_call(i1 %cond, ptr %p) { define void @nonnull_call2(i1 %cond, ptr %p) { ; CHECK-LABEL: @nonnull_call2( -; CHECK-NEXT: [[RES:%.*]] = select i1 [[COND:%.*]], ptr null, ptr [[P:%.*]] -; CHECK-NEXT: call void @f(ptr nonnull [[RES]]) +; CHECK-NEXT: call void @f(ptr nonnull [[RES:%.*]]) ; CHECK-NEXT: ret void ; %res = select i1 %cond, ptr null, ptr %p @@ -77,8 +68,7 @@ define void @nonnull_call2(i1 %cond, ptr %p) { define void @nonnull_noundef_call(i1 %cond, ptr %p) { ; CHECK-LABEL: @nonnull_noundef_call( -; CHECK-NEXT: [[RES:%.*]] = select i1 [[COND:%.*]], ptr [[P:%.*]], ptr null -; CHECK-NEXT: call void @f(ptr noundef nonnull [[RES]]) +; CHECK-NEXT: call void @f(ptr noundef nonnull [[RES:%.*]]) ; CHECK-NEXT: ret void ; %res = select i1 %cond, ptr %p, ptr null @@ -88,8 +78,7 @@ define void @nonnull_noundef_call(i1 %cond, ptr %p) { define void @nonnull_noundef_call2(i1 %cond, ptr %p) { ; CHECK-LABEL: @nonnull_noundef_call2( -; CHECK-NEXT: [[RES:%.*]] = select i1 [[COND:%.*]], ptr null, ptr [[P:%.*]] -; CHECK-NEXT: call void @f(ptr noundef nonnull [[RES]]) +; CHECK-NEXT: call void @f(ptr noundef nonnull [[RES:%.*]]) ; CHECK-NEXT: ret void ; %res = select i1 %cond, ptr null, ptr %p diff --git a/llvm/test/Transforms/PhaseOrdering/load-store-sameval.ll b/llvm/test/Transforms/PhaseOrdering/load-store-sameval.ll index d8ef0723cf09e..f6bf57a678786 100644 --- a/llvm/test/Transforms/PhaseOrdering/load-store-sameval.ll +++ b/llvm/test/Transforms/PhaseOrdering/load-store-sameval.ll @@ -1,24 +1,21 @@ ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5 ; RUN: opt -passes='instcombine,early-cse' -S %s | FileCheck %s -; FIXME: We can remove the store instruction in the exit block define i32 @load_store_sameval(ptr %p, i1 %cond1, i1 %cond2) { ; CHECK-LABEL: define i32 @load_store_sameval( ; CHECK-SAME: ptr [[P:%.*]], i1 [[COND1:%.*]], i1 [[COND2:%.*]]) { ; CHECK-NEXT: [[ENTRY:.*:]] -; CHECK-NEXT: [[SPEC_SELECT:%.*]] = select i1 [[COND1]], ptr null, ptr [[P]] -; CHECK-NEXT: [[PRE:%.*]] = load i32, ptr [[SPEC_SELECT]], align 4 +; CHECK-NEXT: [[PRE:%.*]] = load i32, ptr [[P]], align 4 ; CHECK-NEXT: br label %[[BLOCK:.*]] ; CHECK: [[BLOCK]]: ; CHECK-NEXT: br label %[[BLOCK2:.*]] ; CHECK: [[BLOCK2]]: ; CHECK-NEXT: br i1 [[COND2]], label %[[BLOCK3:.*]], label %[[EXIT:.*]] ; CHECK: [[BLOCK3]]: -; CHECK-NEXT: [[LOAD:%.*]] = load double, ptr [[SPEC_SELECT]], align 8 +; CHECK-NEXT: [[LOAD:%.*]] = load double, ptr [[P]], align 8 ; CHECK-NEXT: [[CMP:%.*]] = fcmp une double [[LOAD]], 0.000000e+00 ; CHECK-NEXT: br i1 [[CMP]], label %[[BLOCK]], label %[[BLOCK2]] ; CHECK: [[EXIT]]: -; CHECK-NEXT: store i32 [[PRE]], ptr [[P]], align 4 ; CHECK-NEXT: ret i32 0 ; entry: diff --git a/llvm/test/Transforms/PhaseOrdering/memset-combine.ll b/llvm/test/Transforms/PhaseOrdering/memset-combine.ll new file mode 100644 index 0000000000000..d1de11258ed91 --- /dev/null +++ b/llvm/test/Transforms/PhaseOrdering/memset-combine.ll @@ -0,0 +1,20 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5 + +; RUN: opt < %s -passes=instcombine,memcpyopt -S | FileCheck %s + +; FIXME: These two memset calls should be merged into a single one. +define void @merge_memset(ptr %p, i1 %cond) { +; CHECK-LABEL: define void @merge_memset( +; CHECK-SAME: ptr [[P:%.*]], i1 [[COND:%.*]]) { +; CHECK-NEXT: [[SEL:%.*]] = select i1 [[COND]], ptr null, ptr [[P]] +; CHECK-NEXT: tail call void @llvm.memset.p0.i64(ptr noundef nonnull align 1 dereferenceable(4096) [[P]], i8 0, i64 4096, i1 false) +; CHECK-NEXT: [[OFF:%.*]] = getelementptr inbounds nuw i8, ptr [[SEL]], i64 4096 +; CHECK-NEXT: tail call void @llvm.memset.p0.i64(ptr noundef nonnull align 1 dereferenceable(768) [[OFF]], i8 0, i64 768, i1 false) +; CHECK-NEXT: ret void +; + %sel = select i1 %cond, ptr null, ptr %p + tail call void @llvm.memset.p0.i64(ptr noundef nonnull %sel, i8 0, i64 4096, i1 false) + %off = getelementptr inbounds nuw i8, ptr %sel, i64 4096 + tail call void @llvm.memset.p0.i64(ptr noundef nonnull %off, i8 0, i64 768, i1 false) + ret void +}