Index: lib/Transforms/InstCombine/InstCombineCalls.cpp =================================================================== --- lib/Transforms/InstCombine/InstCombineCalls.cpp +++ lib/Transforms/InstCombine/InstCombineCalls.cpp @@ -2776,6 +2776,49 @@ return nullptr; } +/// If a callsite has arguments that are also arguments to the parent function, +/// try to propagate attributes from the callsite's arguments to the parent's +/// arguments. +static Instruction *extendArgAttrsToCaller(CallSite &CS, DominatorTree &DT) { + // Make sure that this callsite is part of a function. + BasicBlock *BB = CS.getParent(); + if (!BB) + return nullptr; + Function *F = BB->getParent(); + if (!F) + return nullptr; + + // Collect parameters that are known non-null at this callsite. + SmallVector NonNullParams; + unsigned ArgIndex = 0; + for (Value *V : CS.args()) + if (CS.paramHasAttr(++ArgIndex, Attribute::NonNull)) + NonNullParams.push_back(V); + + Instruction *Call = CS.getInstruction(); + auto CallDominatesOtherUsesOfValue = [&Call, &DT](Value *V) { + for (User *U : V->users()) + if (U != Call && !DT.dominates(Call, cast(U))) + return false; + return true; + }; + + // If any argument to the caller is known non-null at the callsite and the + // call dominates all other uses of that argument, then the argument is + // non-null throughout the caller. + bool PropagatedNonNull = false; + for (Value *V : NonNullParams) { + auto *CallerArg = dyn_cast(V); + if (CallerArg && !CallerArg->hasNonNullAttr() && + CallDominatesOtherUsesOfValue(V)) { + CallerArg->addAttr(Attribute::NonNull); + PropagatedNonNull = true; + } + } + + return PropagatedNonNull ? Call : nullptr; +} + /// Improvements for call and invoke instructions. Instruction *InstCombiner::visitCallSite(CallSite CS) { if (isAllocLikeFn(CS.getInstruction(), &TLI)) @@ -2808,6 +2851,9 @@ Changed = true; } + if (Instruction *I = extendArgAttrsToCaller(CS, DT)) + return I; + // If the callee is a pointer to a function, attempt to move any casts to the // arguments of the call/invoke. Value *Callee = CS.getCalledValue(); Index: test/Transforms/InstCombine/call_nonnull_arg.ll =================================================================== --- test/Transforms/InstCombine/call_nonnull_arg.ll +++ test/Transforms/InstCombine/call_nonnull_arg.ll @@ -31,3 +31,67 @@ unreachable } +; Test propagation of nonnull callsite args back to caller. + +declare void @use1(i8* %x); +declare void @use2(i8* %x, i8* %y); +declare void @use3(i8* %x, i8* %y, i8* %z); + +declare void @use1nonnull(i8* nonnull %x); +declare void @use2nonnull(i8* nonnull %x, i8* nonnull %y); +declare void @use3nonnull(i8* nonnull %x, i8* nonnull %y, i8* nonnull %z); + +; Can't extend non-null to parent for any argument. + +define void @parent1(i8* %a, i8* %b, i8* %c) { +; CHECK-LABEL: @parent1(i8* %a, i8* %b, i8* %c) +; CHECK-NEXT: call void @use3(i8* %c, i8* %a, i8* %b) +; CHECK-NEXT: call void @use3nonnull(i8* %b, i8* %c, i8* %a) +; CHECK-NEXT: ret void +; + call void @use3(i8* %c, i8* %a, i8* %b) + call void @use3nonnull(i8* %b, i8* %c, i8* %a) + ret void +} + +; Extend non-null to parent for all arguments. Non-null then extends to the 2nd call from the parent. + +define void @parent2(i8* %a, i8* %b, i8* %c) { +; CHECK-LABEL: @parent2(i8* nonnull %a, i8* nonnull %b, i8* nonnull %c) +; CHECK-NEXT: call void @use3nonnull(i8* %b, i8* %c, i8* %a) +; CHECK-NEXT: call void @use3(i8* nonnull %c, i8* nonnull %a, i8* nonnull %b) +; CHECK-NEXT: ret void +; + call void @use3nonnull(i8* %b, i8* %c, i8* %a) + call void @use3(i8* %c, i8* %a, i8* %b) + ret void +} + +; Extend non-null to parent for first argument. Non-null then extends to the 2nd call from the parent. + +define void @parent3(i8* %a, i8* %b, i8* %c) { +; CHECK-LABEL: @parent3(i8* nonnull %a, i8* %b, i8* %c) +; CHECK-NEXT: call void @use1nonnull(i8* %a) +; CHECK-NEXT: call void @use3(i8* %c, i8* %b, i8* nonnull %a) +; CHECK-NEXT: ret void +; + call void @use1nonnull(i8* %a) + call void @use3(i8* %c, i8* %b, i8* %a) + ret void +} + +; Extend non-null to parent for last 2 arguments. Non-null then extends to the 2nd and 3rd calls from the parent. + +define void @parent4(i8* %a, i8* %b, i8* %c) { +; CHECK-LABEL: @parent4(i8* %a, i8* nonnull %b, i8* nonnull %c) +; CHECK-NEXT: call void @use2nonnull(i8* %c, i8* %b) +; CHECK-NEXT: call void @use2(i8* %a, i8* nonnull %c) +; CHECK-NEXT: call void @use1(i8* nonnull %b) +; CHECK-NEXT: ret void +; + call void @use2nonnull(i8* %c, i8* %b) + call void @use2(i8* %a, i8* %c) + call void @use1(i8* %b) + ret void +} +