diff --git a/llvm/include/llvm/Transforms/IPO/Attributor.h b/llvm/include/llvm/Transforms/IPO/Attributor.h --- a/llvm/include/llvm/Transforms/IPO/Attributor.h +++ b/llvm/include/llvm/Transforms/IPO/Attributor.h @@ -884,6 +884,97 @@ bool checkForAllUses(const function_ref &Pred, const AbstractAttribute &QueryingAA, const Value &V); + /// Helper struct used in the communication between an abstract attribute (AA) + /// that wants to change the signature of a function and the Attributor which + /// applies the changes. The struct is partially initialized with the + /// information from the AA (see the constructor). All other members are + /// provided by the Attributor prior to invoking any callbacks. + struct ArgumentReplacementInfo { + /// Callee repair callback type + /// + /// The function repair callback is invoked once to rewire the replacement + /// arguments in the body of the new function. The argument replacement info + /// is passed, as build from the registerFunctionSignatureRewrite call, as + /// well as the replacement function and an iteratore to the first + /// replacement argument. + using CalleeRepairCBTy = std::function; + + /// Abstract call site (ACS) repair callback type + /// + /// The abstract call site repair callback is invoked once on every abstract + /// call site of the replaced function (\see ReplacedFn). The callback needs + /// to provide the operands for the call to the new replacement function. + /// The number and type of the operands appended to the provided vector + /// (second argument) is defined by the number and types determined through + /// the replacement type vector (\see ReplacementTypes). The first argument + /// is the ArgumentReplacementInfo object registered with the Attributor + /// through the registerFunctionSignatureRewrite call. + using ACSRepairCBTy = + std::function &)>; + + /// Simple getters, see the corresponding members for details. + ///{ + + Attributor &getAttributor() const { return A; } + const Function &getReplacedFn() const { return ReplacedFn; } + const Argument &getReplacedArg() const { return ReplacedArg; } + unsigned getNumReplacementArgs() const { return ReplacementTypes.size(); } + const SmallVectorImpl &getReplacementTypes() const { + return ReplacementTypes; + } + + ///} + + private: + /// Constructor that takes the argument to be replaced, the types of + /// the replacement arguments, as well as callbacks to repair the call sites + /// and new function after the replacement happened. + ArgumentReplacementInfo(Attributor &A, Argument &Arg, + ArrayRef ReplacementTypes, + CalleeRepairCBTy &&CalleeRepairCB, + ACSRepairCBTy &&ACSRepairCB) + : A(A), ReplacedFn(*Arg.getParent()), ReplacedArg(Arg), + ReplacementTypes(ReplacementTypes.begin(), ReplacementTypes.end()), + CalleeRepairCB(std::move(CalleeRepairCB)), + ACSRepairCB(std::move(ACSRepairCB)) {} + + /// Reference to the attributor to allow access from the callbacks. + Attributor &A; + + /// The "old" function replaced by ReplacementFn. + const Function &ReplacedFn; + + /// The "old" argument replaced by new ones defined via ReplacementTypes. + const Argument &ReplacedArg; + + /// The types of the arguments replacing ReplacedArg. + const SmallVector ReplacementTypes; + + /// Callee repair callback, see CalleeRepairCBTy. + const CalleeRepairCBTy CalleeRepairCB; + + /// Abstract call site (ACS) repair callback, see ACSRepairCBTy. + const ACSRepairCBTy ACSRepairCB; + + /// Allow access to the private members from the Attributor. + friend struct Attributor; + }; + + /// Register a rewrite for a function signature. + /// + /// The argument \p Arg is replaced with new ones defined by the number, + /// order, and types in \p ReplacementTypes. The rewiring at the call sites is + /// done through \p ACSRepairCB and at the callee site through + /// \p CalleeRepairCB. + /// + /// \returns True, if the replacement was registered, false otherwise. + bool registerFunctionSignatureRewrite( + Argument &Arg, ArrayRef ReplacementTypes, + ArgumentReplacementInfo::CalleeRepairCBTy &&CalleeRepairCB, + ArgumentReplacementInfo::ACSRepairCBTy &&ACSRepairCB); + /// Check \p Pred on all function call sites. /// /// This method will evaluate \p Pred on call sites and return @@ -1018,6 +1109,11 @@ return nullptr; } + /// Apply all requested function signature rewrites + /// (\see registerFunctionSignatureRewrite) and return Changed if the module + /// was altered. + ChangeStatus rewriteFunctionSignatures(); + /// The set of all abstract attributes. ///{ using AAVector = SmallVector; @@ -1049,6 +1145,10 @@ QueryMapTy QueryMap; ///} + /// Map to remember all requested signature changes (= argument replacements). + DenseMap> + ArgumentReplacementMap; + /// The information cache that holds pre-processed (LLVM-IR) information. InformationCache &InfoCache; diff --git a/llvm/lib/Transforms/IPO/Attributor.cpp b/llvm/lib/Transforms/IPO/Attributor.cpp --- a/llvm/lib/Transforms/IPO/Attributor.cpp +++ b/llvm/lib/Transforms/IPO/Attributor.cpp @@ -2683,6 +2683,19 @@ indicatePessimisticFixpoint(); } + /// See AbstractAttribute::manifest(...). + ChangeStatus manifest(Attributor &A) override { + ChangeStatus Changed = AAIsDeadFloating::manifest(A); + Argument &Arg = *getAssociatedArgument(); + if (Arg.getParent()->hasLocalLinkage()) + if (A.registerFunctionSignatureRewrite( + Arg, /* ReplacementTypes */ {}, + Attributor::ArgumentReplacementInfo::CalleeRepairCBTy{}, + Attributor::ArgumentReplacementInfo::ACSRepairCBTy{})) + return ChangeStatus::CHANGED; + return Changed; + } + /// See AbstractAttribute::trackStatistics() void trackStatistics() const override { STATS_DECLTRACK_ARG_ATTR(IsDead) } }; @@ -5733,6 +5746,9 @@ } } + // Rewrite the functions as requested during manifest. + ManifestChange = ManifestChange | rewriteFunctionSignatures(); + if (VerifyMaxFixpointIterations && IterationCounter != MaxFixpointIterations) { errs() << "\n[Attributor] Fixpoint iteration done after: " @@ -5745,6 +5761,249 @@ return ManifestChange; } +bool Attributor::registerFunctionSignatureRewrite( + Argument &Arg, ArrayRef ReplacementTypes, + ArgumentReplacementInfo::CalleeRepairCBTy &&CalleeRepairCB, + ArgumentReplacementInfo::ACSRepairCBTy &&ACSRepairCB) { + + auto CallSiteCanBeChanged = [](AbstractCallSite ACS) { + // Forbid must-tail calls for now. + return !ACS.isCallbackCall() && !ACS.getCallSite().isMustTailCall(); + }; + + Function *Fn = Arg.getParent(); + // Avoid var-arg functions for now. + if (Fn->isVarArg()) { + LLVM_DEBUG(dbgs() << "[Attributor] Cannot rewrite var-args functions\n"); + return false; + } + + // Avoid functions with complicated argument passing semantics. + AttributeList FnAttributeList = Fn->getAttributes(); + if (FnAttributeList.hasAttrSomewhere(Attribute::Nest) || + FnAttributeList.hasAttrSomewhere(Attribute::StructRet) || + FnAttributeList.hasAttrSomewhere(Attribute::InAlloca)) { + LLVM_DEBUG( + dbgs() << "[Attributor] Cannot rewrite due to complex attribute\n"); + return false; + } + + // Avoid callbacks for now. + if (!checkForAllCallSites(CallSiteCanBeChanged, *Fn, true, nullptr)) { + LLVM_DEBUG(dbgs() << "[Attributor] Cannot rewrite all call sites\n"); + return false; + } + + auto InstPred = [](Instruction &I) { + if (auto *CI = dyn_cast(&I)) + return !CI->isMustTailCall(); + return true; + }; + + // Forbid must-tail calls for now. + // TODO: + bool AnyDead; + auto &OpcodeInstMap = InfoCache.getOpcodeInstMapForFunction(*Fn); + if (!checkForAllInstructionsImpl(OpcodeInstMap, InstPred, nullptr, AnyDead, + {Instruction::Call})) { + LLVM_DEBUG(dbgs() << "[Attributor] Cannot rewrite due to instructions\n"); + return false; + } + + SmallVectorImpl &ARIs = ArgumentReplacementMap[Fn]; + if (ARIs.size() == 0) + ARIs.resize(Fn->arg_size()); + + // If we have a replacement already with less than or equal new arguments, + // ignore this request. + ArgumentReplacementInfo *&ARI = ARIs[Arg.getArgNo()]; + if (ARI && ARI->getNumReplacementArgs() <= ReplacementTypes.size()) { + LLVM_DEBUG(dbgs() << "[Attributor] Existing rewrite is preferred\n"); + return false; + } + + // If we have a replacement already but we like the new one better, delete + // the old. + if (ARI) + delete ARI; + + // Remember the replacement. + ARI = new ArgumentReplacementInfo(*this, Arg, ReplacementTypes, + std::move(CalleeRepairCB), + std::move(ACSRepairCB)); + + return true; +} + +ChangeStatus Attributor::rewriteFunctionSignatures() { + ChangeStatus Changed = ChangeStatus::UNCHANGED; + + for (auto &It : ArgumentReplacementMap) { + Function *OldFn = It.getFirst(); + + // Deleted functions do not require rewrites. + if (ToBeDeletedFunctions.count(OldFn)) + continue; + + const SmallVectorImpl &ARIs = It.getSecond(); + assert(ARIs.size() == OldFn->arg_size() && "Inconsistent state!"); + + SmallVector NewArgumentTypes; + SmallVector NewArgumentAttributes; + + // Collect replacement argument types and copy over existing attributes. + AttributeList OldFnAttributeList = OldFn->getAttributes(); + for (Argument &Arg : OldFn->args()) { + if (ArgumentReplacementInfo *ARI = ARIs[Arg.getArgNo()]) { + NewArgumentTypes.append(ARI->ReplacementTypes.begin(), + ARI->ReplacementTypes.end()); + NewArgumentAttributes.append(ARI->getNumReplacementArgs(), + AttributeSet()); + } else { + NewArgumentTypes.push_back(Arg.getType()); + NewArgumentAttributes.push_back( + OldFnAttributeList.getParamAttributes(Arg.getArgNo())); + } + } + + FunctionType *OldFnTy = OldFn->getFunctionType(); + Type *RetTy = OldFnTy->getReturnType(); + + // Construct the new function type using the new arguments types. + FunctionType *NewFnTy = + FunctionType::get(RetTy, NewArgumentTypes, OldFnTy->isVarArg()); + + LLVM_DEBUG(dbgs() << "[Attributor] Function rewrite '" << OldFn->getName() + << "' from " << *OldFn->getFunctionType() << " to " + << *NewFnTy << "\n"); + + // Create the new function body and insert it into the module. + Function *NewFn = Function::Create(NewFnTy, OldFn->getLinkage(), + OldFn->getAddressSpace(), ""); + OldFn->getParent()->getFunctionList().insert(OldFn->getIterator(), NewFn); + NewFn->takeName(OldFn); + NewFn->copyAttributesFrom(OldFn); + + // Patch the pointer to LLVM function in debug info descriptor. + NewFn->setSubprogram(OldFn->getSubprogram()); + OldFn->setSubprogram(nullptr); + + // Recompute the parameter attributes list based on the new arguments for + // the function. + LLVMContext &Ctx = OldFn->getContext(); + NewFn->setAttributes(AttributeList::get( + Ctx, OldFnAttributeList.getFnAttributes(), + OldFnAttributeList.getRetAttributes(), NewArgumentAttributes)); + + // Since we have now created the new function, splice the body of the old + // function right into the new function, leaving the old rotting hulk of the + // function empty. + NewFn->getBasicBlockList().splice(NewFn->begin(), + OldFn->getBasicBlockList()); + + // Set of all "call-like" instructions that invoke the old function. + SmallPtrSet OldCallSites; + + // Callback to create a new "call-like" instruction for a given one. + auto CallSiteReplacementCreator = [&](AbstractCallSite ACS) { + CallBase *OldCB = cast(ACS.getInstruction()); + const AttributeList &OldCallAttributeList = OldCB->getAttributes(); + + // Collect the new argument operands for the replacement call site. + SmallVector NewArgOperands; + SmallVector NewArgOperandAttributes; + for (unsigned OldArgNum = 0; OldArgNum < ARIs.size(); ++OldArgNum) { + unsigned NewFirstArgNum = NewArgOperands.size(); + if (ArgumentReplacementInfo *ARI = ARIs[OldArgNum]) { + if (ARI->ACSRepairCB) + ARI->ACSRepairCB(*ARI, ACS, NewArgOperands); + assert(ARI->getNumReplacementArgs() + NewFirstArgNum == + NewArgOperands.size() && + "ACS repair callback did not provide as many operand as new " + "types were registered!"); + // TODO: Exose the attribute set to the ACS repair callback + NewArgOperandAttributes.append(ARI->ReplacementTypes.size(), + AttributeSet()); + } else { + NewArgOperands.push_back(ACS.getCallArgOperand(OldArgNum)); + NewArgOperandAttributes.push_back( + OldCallAttributeList.getParamAttributes(OldArgNum)); + } + } + + assert(NewArgOperands.size() == NewArgOperandAttributes.size() && + "Mismatch # argument operands vs. # argument operand attributes!"); + assert(NewArgOperands.size() == NewFn->arg_size() && + "Mismatch # argument operands vs. # function arguments!"); + + SmallVector OperandBundleDefs; + OldCB->getOperandBundlesAsDefs(OperandBundleDefs); + + // Create a new call or invoke instruction to replace the old one. + CallBase *NewCB; + if (InvokeInst *II = dyn_cast(OldCB)) { + NewCB = + InvokeInst::Create(NewFn, II->getNormalDest(), II->getUnwindDest(), + NewArgOperands, OperandBundleDefs, "", OldCB); + } else { + auto *NewCI = CallInst::Create(NewFn, NewArgOperands, OperandBundleDefs, + "", OldCB); + NewCI->setTailCallKind(cast(OldCB)->getTailCallKind()); + NewCB = NewCI; + } + + // Copy over various properties and the new attributes. + OldCB->replaceAllUsesWith(NewCB); + uint64_t W; + if (OldCB->extractProfTotalWeight(W)) + NewCB->setProfWeight(W); + NewCB->setCallingConv(OldCB->getCallingConv()); + NewCB->setDebugLoc(OldCB->getDebugLoc()); + NewCB->takeName(OldCB); + NewCB->setAttributes(AttributeList::get( + Ctx, OldCallAttributeList.getFnAttributes(), + OldCallAttributeList.getRetAttributes(), NewArgOperandAttributes)); + + bool Inserted = OldCallSites.insert(OldCB).second; + assert(Inserted && "Call site was old twice!"); + (void)Inserted; + + return true; + }; + + // Use the CallSiteReplacementCreator to create replacement call sites. + bool Success = + checkForAllCallSites(CallSiteReplacementCreator, *OldFn, true, nullptr); + assert(Success && "Assumed call site replacement to succeed!"); + + // Rewire the arguments. + auto OldFnArgIt = OldFn->arg_begin(); + auto NewFnArgIt = NewFn->arg_begin(); + for (unsigned OldArgNum = 0; OldArgNum < ARIs.size(); + ++OldArgNum, ++OldFnArgIt) { + if (ArgumentReplacementInfo *ARI = ARIs[OldArgNum]) { + if (ARI->CalleeRepairCB) + ARI->CalleeRepairCB(*ARI, *NewFn, NewFnArgIt); + NewFnArgIt += ARI->ReplacementTypes.size(); + } else { + NewFnArgIt->takeName(&*OldFnArgIt); + OldFnArgIt->replaceAllUsesWith(&*NewFnArgIt); + ++NewFnArgIt; + } + } + + // Eliminate the instructions *after* we visited all of them. + for (Instruction *OldCallSite : OldCallSites) + OldCallSite->eraseFromParent(); + + assert(OldFn->getNumUses() == 0 && "Unexpected leftover uses!"); + OldFn->eraseFromParent(); + Changed = ChangeStatus::CHANGED; + } + + return Changed; +} + void Attributor::initializeInformationCache(Function &F) { // Walk all instructions to find interesting instructions that might be diff --git a/llvm/test/Transforms/Attributor/ArgumentPromotion/control-flow.ll b/llvm/test/Transforms/Attributor/ArgumentPromotion/control-flow.ll --- a/llvm/test/Transforms/Attributor/ArgumentPromotion/control-flow.ll +++ b/llvm/test/Transforms/Attributor/ArgumentPromotion/control-flow.ll @@ -1,16 +1,17 @@ ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --function-signature --scrub-attributes -; RUN: opt -S -passes='attributor' -aa-pipeline='basic-aa' -attributor-disable=false -attributor-max-iterations-verify -attributor-max-iterations=2 < %s | FileCheck %s +; RUN: opt -S -passes='attributor' -aa-pipeline='basic-aa' -attributor-disable=false -attributor-max-iterations-verify -attributor-max-iterations=3 < %s | FileCheck %s ; Don't promote around control flow. define internal i32 @callee(i1 %C, i32* %P) { ; CHECK-LABEL: define {{[^@]+}}@callee -; CHECK-SAME: (i1 [[C:%.*]], i32* noalias nocapture nofree readnone [[P:%.*]]) +; CHECK-SAME: (i1 [[C:%.*]], i32* nocapture nofree readonly [[P:%.*]]) ; CHECK-NEXT: entry: -; CHECK-NEXT: br label [[T:%.*]] +; CHECK-NEXT: br i1 [[C]], label [[T:%.*]], label [[F:%.*]] ; CHECK: T: ; CHECK-NEXT: ret i32 17 ; CHECK: F: -; CHECK-NEXT: unreachable +; CHECK-NEXT: [[X:%.*]] = load i32, i32* [[P]] +; CHECK-NEXT: ret i32 [[X]] ; entry: br i1 %C, label %T, label %F @@ -23,14 +24,15 @@ ret i32 %X } -define i32 @foo() { -; CHECK-LABEL: define {{[^@]+}}@foo() +define i32 @foo(i1 %C, i32* %P) { +; CHECK-LABEL: define {{[^@]+}}@foo +; CHECK-SAME: (i1 [[C:%.*]], i32* nocapture nofree readonly [[P:%.*]]) ; CHECK-NEXT: entry: -; CHECK-NEXT: [[X:%.*]] = call i32 @callee(i1 true, i32* noalias nofree readnone undef) -; CHECK-NEXT: ret i32 17 +; CHECK-NEXT: [[X:%.*]] = call i32 @callee(i1 [[C]], i32* nocapture nofree readonly [[P]]) +; CHECK-NEXT: ret i32 [[X]] ; entry: - %X = call i32 @callee(i1 true, i32* null) + %X = call i32 @callee(i1 %C, i32* %P) ret i32 %X } diff --git a/llvm/test/Transforms/Attributor/ArgumentPromotion/dbg.ll b/llvm/test/Transforms/Attributor/ArgumentPromotion/dbg.ll --- a/llvm/test/Transforms/Attributor/ArgumentPromotion/dbg.ll +++ b/llvm/test/Transforms/Attributor/ArgumentPromotion/dbg.ll @@ -20,8 +20,7 @@ %struct.pair = type { i32, i32 } define internal void @test_byval(%struct.pair* byval %P) { -; CHECK-LABEL: define {{[^@]+}}@test_byval -; CHECK-SAME: (%struct.pair* nocapture nofree readnone byval [[P:%.*]]) +; CHECK-LABEL: define {{[^@]+}}@test_byval() ; CHECK-NEXT: ret void ; ret void @@ -31,7 +30,7 @@ ; CHECK-LABEL: define {{[^@]+}}@caller ; CHECK-SAME: (i32** nocapture readonly [[Y:%.*]], %struct.pair* nocapture nofree readonly [[P:%.*]]) ; CHECK-NEXT: call void @test(i32** nocapture readonly align 8 [[Y]]), !dbg !4 -; CHECK-NEXT: call void @test_byval(%struct.pair* nocapture nofree readonly undef), !dbg !5 +; CHECK-NEXT: call void @test_byval(), !dbg !5 ; CHECK-NEXT: ret void ; call void @test(i32** %Y), !dbg !1 diff --git a/llvm/test/Transforms/Attributor/ArgumentPromotion/nonzero-address-spaces.ll b/llvm/test/Transforms/Attributor/ArgumentPromotion/nonzero-address-spaces.ll --- a/llvm/test/Transforms/Attributor/ArgumentPromotion/nonzero-address-spaces.ll +++ b/llvm/test/Transforms/Attributor/ArgumentPromotion/nonzero-address-spaces.ll @@ -11,7 +11,7 @@ define i32 @bar() { ; CHECK-LABEL: define {{[^@]+}}@bar() addrspace(1) ; CHECK-NEXT: entry: -; CHECK-NEXT: [[CALL:%.*]] = call addrspace(1) i32 @foo(i32* nofree nonnull readnone align 4 dereferenceable(4) undef) +; CHECK-NEXT: [[CALL:%.*]] = call addrspace(1) i32 @foo() ; CHECK-NEXT: unreachable ; CHECK: entry.split: ; CHECK-NEXT: unreachable @@ -23,8 +23,7 @@ } define internal i32 @foo(i32*) { -; CHECK-LABEL: define {{[^@]+}}@foo -; CHECK-SAME: (i32* nocapture nofree nonnull readnone align 4 dereferenceable(4) [[TMP0:%.*]]) addrspace(1) +; CHECK-LABEL: define {{[^@]+}}@foo() addrspace(1) ; CHECK-NEXT: entry: ; CHECK-NEXT: [[RETVAL:%.*]] = alloca i32, align 4 ; CHECK-NEXT: call addrspace(0) void asm sideeffect "ldr r0, [r0] \0Abx lr \0A", ""() diff --git a/llvm/test/Transforms/Attributor/ArgumentPromotion/pr27568.ll b/llvm/test/Transforms/Attributor/ArgumentPromotion/pr27568.ll --- a/llvm/test/Transforms/Attributor/ArgumentPromotion/pr27568.ll +++ b/llvm/test/Transforms/Attributor/ArgumentPromotion/pr27568.ll @@ -3,8 +3,7 @@ target triple = "x86_64-pc-windows-msvc" define internal void @callee(i8*) { -; CHECK-LABEL: define {{[^@]+}}@callee -; CHECK-SAME: (i8* noalias nocapture nofree readnone [[TMP0:%.*]]) +; CHECK-LABEL: define {{[^@]+}}@callee() ; CHECK-NEXT: entry: ; CHECK-NEXT: call void @thunk() ; CHECK-NEXT: ret void @@ -23,7 +22,7 @@ ; CHECK-NEXT: ret void ; CHECK: cpad: ; CHECK-NEXT: [[PAD:%.*]] = cleanuppad within none [] -; CHECK-NEXT: call void @callee(i8* noalias nofree readnone undef) [ "funclet"(token [[PAD]]) ] +; CHECK-NEXT: call void @callee() [ "funclet"(token [[PAD]]) ] ; CHECK-NEXT: cleanupret from [[PAD]] unwind to caller ; entry: diff --git a/llvm/test/Transforms/Attributor/ArgumentPromotion/reserve-tbaa.ll b/llvm/test/Transforms/Attributor/ArgumentPromotion/reserve-tbaa.ll --- a/llvm/test/Transforms/Attributor/ArgumentPromotion/reserve-tbaa.ll +++ b/llvm/test/Transforms/Attributor/ArgumentPromotion/reserve-tbaa.ll @@ -15,7 +15,7 @@ define internal fastcc void @fn(i32* nocapture readonly %p1, i64* nocapture readonly %p2) { ; CHECK-LABEL: define {{[^@]+}}@fn -; CHECK-SAME: (i32* nocapture nofree nonnull readonly align 4 dereferenceable(4) [[P1:%.*]], i64* nocapture nofree nonnull readonly align 8 dereferenceable(8) [[P2:%.*]]) +; CHECK-SAME: (i32* nocapture nofree nonnull readonly align 4 dereferenceable(4) [[P1:%.*]]) ; CHECK-NEXT: entry: ; CHECK-NEXT: [[TMP0:%.*]] = load i64, i64* undef, align 8, !tbaa !0 ; CHECK-NEXT: [[CONV:%.*]] = trunc i64 [[TMP0]] to i32 @@ -40,7 +40,7 @@ ; CHECK-NEXT: store i32* @g, i32** [[TMP0]], align 8, !tbaa !7 ; CHECK-NEXT: [[TMP1:%.*]] = load i32*, i32** @a, align 8, !tbaa !7 ; CHECK-NEXT: store i32 1, i32* [[TMP1]], align 4, !tbaa !4 -; CHECK-NEXT: call fastcc void @fn(i32* nofree nonnull readonly align 4 dereferenceable(4) @g, i64* nofree nonnull readonly align 8 dereferenceable(8) undef) +; CHECK-NEXT: call fastcc void @fn(i32* nofree nonnull readonly align 4 dereferenceable(4) @g) ; CHECK-NEXT: ret i32 0 ; entry: diff --git a/llvm/test/Transforms/Attributor/liveness.ll b/llvm/test/Transforms/Attributor/liveness.ll --- a/llvm/test/Transforms/Attributor/liveness.ll +++ b/llvm/test/Transforms/Attributor/liveness.ll @@ -1,5 +1,6 @@ ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --function-signature --scrub-attributes -; RUN: opt -attributor --attributor-disable=false -attributor-max-iterations-verify -attributor-annotate-decl-cs -attributor-max-iterations=4 -S < %s | FileCheck %s +; RUN: opt -attributor --attributor-disable=false -attributor-max-iterations-verify -attributor-annotate-decl-cs -attributor-max-iterations=4 -S < %s | FileCheck %s --check-prefixes=CHECK,OLDPM +; RUN: opt -passes=attributor --attributor-disable=false -attributor-max-iterations-verify -attributor-annotate-decl-cs -attributor-max-iterations=4 -S < %s | FileCheck %s --check-prefixes=CHECK,NEWPM ; UTC_ARGS: --turn off ; CHECK: @dead_with_blockaddress_users.l = constant [2 x i8*] [i8* inttoptr (i32 1 to i8*), i8* inttoptr (i32 1 to i8*)] @@ -701,7 +702,8 @@ ; CHECK: define internal void @non_dead_d13() ; CHECK: define internal void @non_dead_d14() ; Verify we actually deduce information for these functions. -; CHECK: Function Attrs: nofree nosync nounwind readnone willreturn +; OLDPM: Function Attrs: nofree nosync nounwind readnone willreturn +; NEWPM: Function Attrs: nofree norecurse nosync nounwind readnone willreturn ; CHECK-NEXT: define internal void @non_dead_d15() ; CHECK-NOT: define internal void @dead_e @@ -761,14 +763,14 @@ ret void } -; CHECK: define internal void @useless_arg_sink(i32* nocapture nofree readnone %a) +; CHECK: define internal void @useless_arg_sink() define internal void @useless_arg_sink(i32* %a) { ret void } -; CHECK: define internal void @useless_arg_almost_sink(i32* nocapture nofree readnone %a) +; CHECK: define internal void @useless_arg_almost_sink() define internal void @useless_arg_almost_sink(i32* %a) { -; CHECK: call void @useless_arg_sink(i32* nofree readnone undef) +; CHECK: call void @useless_arg_sink() call void @useless_arg_sink(i32* %a) ret void } @@ -776,7 +778,7 @@ ; Check we do not annotate the function interface of this weak function. ; CHECK: define weak_odr void @useless_arg_ext(i32* %a) define weak_odr void @useless_arg_ext(i32* %a) { -; CHECK: call void @useless_arg_almost_sink(i32* nofree readnone undef) +; CHECK: call void @useless_arg_almost_sink() call void @useless_arg_almost_sink(i32* %a) ret void } diff --git a/llvm/test/Transforms/Attributor/noalias.ll b/llvm/test/Transforms/Attributor/noalias.ll --- a/llvm/test/Transforms/Attributor/noalias.ll +++ b/llvm/test/Transforms/Attributor/noalias.ll @@ -152,13 +152,27 @@ ; TEST 9 ; Simple Argument Test -define internal void @test9(i8* %a, i8* %b) { -; CHECK: define internal void @test9(i8* noalias nocapture nofree readnone %a, i8* nocapture nofree readnone %b) +declare void @use_i8(i8* nocapture) readnone +define internal void @test9a(i8* %a, i8* %b) { +; CHECK: define internal void @test9a() + ret void +} +define internal void @test9b(i8* %a, i8* %b) { +; CHECK: define internal void @test9b(i8* noalias nocapture readnone %a, i8* nocapture readnone %b) + call void @use_i8(i8* %a) + call void @use_i8(i8* %b) ret void } define void @test9_helper(i8* %a, i8* %b) { - tail call void @test9(i8* noalias %a, i8* %b) - tail call void @test9(i8* noalias %b, i8* noalias %a) +; CHECK: define void @test9_helper(i8* nocapture readnone %a, i8* nocapture readnone %b) +; CHECK: tail call void @test9a() +; CHECK: tail call void @test9a() +; CHECK: tail call void @test9b(i8* noalias nocapture readnone %a, i8* nocapture readnone %b) +; CHECK: tail call void @test9b(i8* noalias nocapture readnone %b, i8* noalias nocapture readnone %a) + tail call void @test9a(i8* noalias %a, i8* %b) + tail call void @test9a(i8* noalias %b, i8* noalias %a) + tail call void @test9b(i8* noalias %a, i8* %b) + tail call void @test9b(i8* noalias %b, i8* noalias %a) ret void } diff --git a/llvm/test/Transforms/Attributor/nonnull.ll b/llvm/test/Transforms/Attributor/nonnull.ll --- a/llvm/test/Transforms/Attributor/nonnull.ll +++ b/llvm/test/Transforms/Attributor/nonnull.ll @@ -165,8 +165,12 @@ tail call void @test13(i8* %nonnullptr, i8* %maybenullptr, i8* %nonnullptr) ret void } +declare void @use_i8_ptr(i8* nofree) readnone nounwind define internal void @test13(i8* %a, i8* %b, i8* %c) { ; ATTRIBUTOR: define internal void @test13(i8* nocapture nofree nonnull readnone %a, i8* nocapture nofree readnone %b, i8* nocapture nofree readnone %c) + call void @use_i8_ptr(i8* %a) + call void @use_i8_ptr(i8* %b) + call void @use_i8_ptr(i8* %c) ret void }