diff --git a/llvm/lib/Transforms/IPO/ArgumentPromotion.cpp b/llvm/lib/Transforms/IPO/ArgumentPromotion.cpp --- a/llvm/lib/Transforms/IPO/ArgumentPromotion.cpp +++ b/llvm/lib/Transforms/IPO/ArgumentPromotion.cpp @@ -454,7 +454,7 @@ /// Determine that this argument is safe to promote, and find the argument /// parts it can be promoted into. static bool findArgParts(Argument *Arg, const DataLayout &DL, AAResults &AAR, - unsigned MaxElements, bool IsSelfRecursive, + unsigned MaxElements, bool IsRecursive, SmallVectorImpl &ArgPartsVec) { // Quick exit for unused arguments if (Arg->use_empty()) @@ -501,9 +501,9 @@ if (Size.isScalable()) return false; - // If this is a self-recursive function and one of the types is a pointer, + // If this is a recursive function and one of the types is a pointer, // then promoting it might lead to recursive promotion. - if (IsSelfRecursive && Ty->isPointerTy()) + if (IsRecursive && Ty->isPointerTy()) return false; int64_t Off = Offset.getSExtValue(); @@ -760,7 +760,7 @@ unsigned MaxElements, Optional> ReplaceCallSite, - const TargetTransformInfo &TTI) { + const TargetTransformInfo &TTI, bool IsRecursive) { // Don't perform argument promotion for naked functions; otherwise we can end // up removing parameters that are seemingly 'not used' as they are referred // to in the assembly. @@ -794,8 +794,7 @@ // Second check: make sure that all callers are direct callers. We can't // transform functions that have indirect callers. Also see if the function - // is self-recursive and check that target features are compatible. - bool isSelfRecursive = false; + // is self-recursive. for (Use &U : F->uses()) { CallBase *CB = dyn_cast(U.getUser()); // Must be a direct call. @@ -807,7 +806,7 @@ return nullptr; if (CB->getParent()->getParent() == F) - isSelfRecursive = true; + IsRecursive = true; } // Can't change signature of musttail caller @@ -879,7 +878,7 @@ // Otherwise, see if we can promote the pointer to its value. SmallVector ArgParts; - if (findArgParts(PtrArg, DL, AAR, MaxElements, isSelfRecursive, ArgParts)) { + if (findArgParts(PtrArg, DL, AAR, MaxElements, IsRecursive, ArgParts)) { SmallVector Types; for (const auto &Pair : ArgParts) Types.push_back(Pair.second.Ty); @@ -909,6 +908,7 @@ FunctionAnalysisManager &FAM = AM.getResult(C, CG).getManager(); + bool IsRecursive = C.size() > 1; for (LazyCallGraph::Node &N : C) { Function &OldF = N.getFunction(); @@ -920,8 +920,8 @@ }; const TargetTransformInfo &TTI = FAM.getResult(OldF); - Function *NewF = - promoteArguments(&OldF, AARGetter, MaxElements, None, TTI); + Function *NewF = promoteArguments(&OldF, AARGetter, MaxElements, None, + TTI, IsRecursive); if (!NewF) continue; LocalChange = true; @@ -1022,6 +1022,7 @@ do { LocalChange = false; // Attempt to promote arguments from all functions in this SCC. + bool IsRecursive = SCC.size() > 1; for (CallGraphNode *OldNode : SCC) { Function *OldF = OldNode->getFunction(); if (!OldF) @@ -1038,8 +1039,9 @@ const TargetTransformInfo &TTI = getAnalysis().getTTI(*OldF); - if (Function *NewF = promoteArguments(OldF, AARGetter, MaxElements, - {ReplaceCallSite}, TTI)) { + if (Function *NewF = + promoteArguments(OldF, AARGetter, MaxElements, {ReplaceCallSite}, + TTI, IsRecursive)) { LocalChange = true; // Update the call graph for the newly promoted function. diff --git a/llvm/test/Transforms/ArgumentPromotion/pr42028-recursion.ll b/llvm/test/Transforms/ArgumentPromotion/pr42028-recursion.ll new file mode 100644 --- /dev/null +++ b/llvm/test/Transforms/ArgumentPromotion/pr42028-recursion.ll @@ -0,0 +1,54 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --function-signature +; RUN: opt -S < %s -argpromotion | FileCheck %s + +; This shouldn't get infinitely promoted. + +%S = type { %S* } + +define i32 @test_inf_promote_caller(i32 %arg) { +; CHECK-LABEL: define {{[^@]+}}@test_inf_promote_caller +; CHECK-SAME: (i32 [[ARG:%.*]]) { +; CHECK-NEXT: bb: +; CHECK-NEXT: [[TMP:%.*]] = alloca [[S:%.*]], align 8 +; CHECK-NEXT: [[TMP1:%.*]] = alloca [[S]], align 8 +; CHECK-NEXT: [[TMP2:%.*]] = call i32 @test_inf_promote_callee(%S* [[TMP]], %S* [[TMP1]]) +; CHECK-NEXT: ret i32 0 +; +bb: + %tmp = alloca %S + %tmp1 = alloca %S + %tmp2 = call i32 @test_inf_promote_callee(%S* %tmp, %S* %tmp1) + ret i32 0 +} + +define internal i32 @test_inf_promote_callee(%S* %arg, %S* %arg1) { +; CHECK-LABEL: define {{[^@]+}}@test_inf_promote_callee +; CHECK-SAME: (%S* [[ARG:%.*]], %S* [[ARG1:%.*]]) { +; CHECK-NEXT: bb: +; CHECK-NEXT: [[TMP:%.*]] = getelementptr [[S:%.*]], %S* [[ARG1]], i32 0, i32 0 +; CHECK-NEXT: [[TMP2:%.*]] = load %S*, %S** [[TMP]], align 8 +; CHECK-NEXT: [[TMP3:%.*]] = getelementptr [[S]], %S* [[ARG]], i32 0, i32 0 +; CHECK-NEXT: [[TMP4:%.*]] = load %S*, %S** [[TMP3]], align 8 +; CHECK-NEXT: [[TMP5:%.*]] = call i32 @test_inf_promote_callee2(%S* [[TMP4]], %S* [[TMP2]]) +; CHECK-NEXT: ret i32 0 +; +bb: + %tmp = getelementptr %S, %S* %arg1, i32 0, i32 0 + %tmp2 = load %S*, %S** %tmp + %tmp3 = getelementptr %S, %S* %arg, i32 0, i32 0 + %tmp4 = load %S*, %S** %tmp3 + %tmp5 = call i32 @test_inf_promote_callee2(%S* %tmp4, %S* %tmp2) + ret i32 0 +} + +define internal i32 @test_inf_promote_callee2(%S* %arg, %S* %arg1) { +; CHECK-LABEL: define {{[^@]+}}@test_inf_promote_callee2 +; CHECK-SAME: (%S* [[ARG:%.*]], %S* [[ARG1:%.*]]) { +; CHECK-NEXT: [[R:%.*]] = call i32 @test_inf_promote_callee(%S* [[ARG]], %S* [[ARG1]]) +; CHECK-NEXT: ret i32 0 +; + %r = call i32 @test_inf_promote_callee(%S* %arg, %S* %arg1) + ret i32 0 +} + +declare i32 @wibble(...)