diff options
| -rw-r--r-- | llvm/lib/Transforms/IPO/AlwaysInliner.cpp | 15 | ||||
| -rw-r--r-- | llvm/test/Transforms/Inline/always-inline.ll | 162 |
2 files changed, 169 insertions, 8 deletions
diff --git a/llvm/lib/Transforms/IPO/AlwaysInliner.cpp b/llvm/lib/Transforms/IPO/AlwaysInliner.cpp index 304694f9cef..1a3f8ced465 100644 --- a/llvm/lib/Transforms/IPO/AlwaysInliner.cpp +++ b/llvm/lib/Transforms/IPO/AlwaysInliner.cpp @@ -38,6 +38,7 @@ PreservedAnalyses AlwaysInlinerPass::run(Module &M, ModuleAnalysisManager &) { InlineFunctionInfo IFI; SmallSetVector<CallSite, 16> Calls; bool Changed = false; + SmallVector<Function *, 16> InlinedFunctions; for (Function &F : M) if (!F.isDeclaration() && F.hasFnAttribute(Attribute::AlwaysInline) && isInlineViable(F)) { @@ -52,8 +53,22 @@ PreservedAnalyses AlwaysInlinerPass::run(Module &M, ModuleAnalysisManager &) { // FIXME: We really shouldn't be able to fail to inline at this point! // We should do something to log or check the inline failures here. Changed |= InlineFunction(CS, IFI); + + // Remember to try and delete this function afterward. This both avoids + // re-walking the rest of the module and avoids dealing with any iterator + // invalidation issues while deleting functions. + InlinedFunctions.push_back(&F); } + // Now try to delete all the functions we inlined. + for (Function *InlinedF : InlinedFunctions) { + InlinedF->removeDeadConstantUsers(); + // FIXME: We should use some utility to handle cases where we can + // completely remove the comdat. + if (InlinedF->isDefTriviallyDead() && !InlinedF->hasComdat()) + M.getFunctionList().erase(InlinedF); + } + return Changed ? PreservedAnalyses::none() : PreservedAnalyses::all(); } diff --git a/llvm/test/Transforms/Inline/always-inline.ll b/llvm/test/Transforms/Inline/always-inline.ll index f0d75c43602..6aef932a315 100644 --- a/llvm/test/Transforms/Inline/always-inline.ll +++ b/llvm/test/Transforms/Inline/always-inline.ll @@ -11,7 +11,8 @@ ; 'alwaysinline'. ; RUN: opt < %s -passes=always-inline -S | FileCheck %s --check-prefix=CHECK -define i32 @inner1() alwaysinline { +define internal i32 @inner1() alwaysinline { +; CHECK-NOT: @inner1( ret i32 1 } define i32 @outer1() { @@ -23,13 +24,14 @@ define i32 @outer1() { ret i32 %r } -; The always inliner can't DCE internal functions. PR2945 -; CHECK-LABEL: @pr2945( +; The always inliner can't DCE arbitrary internal functions. PR2945 define internal i32 @pr2945() nounwind { +; CHECK-LABEL: @pr2945( ret i32 0 } define internal void @inner2(i32 %N) alwaysinline { +; CHECK-NOT: @inner2( %P = alloca i32, i32 %N ret void } @@ -50,7 +52,9 @@ define void @outer2(i32 %N) { declare i32 @a() returns_twice declare i32 @b() returns_twice -define i32 @inner3() alwaysinline { +; Cannot alwaysinline when that would introduce a returns_twice call. +define internal i32 @inner3() alwaysinline { +; CHECK-LABEL: @inner3( entry: %call = call i32 @a() returns_twice %add = add nsw i32 1, %call @@ -67,7 +71,8 @@ entry: ret i32 %add } -define i32 @inner4() alwaysinline returns_twice { +define internal i32 @inner4() alwaysinline returns_twice { +; CHECK-NOT: @inner4( entry: %call = call i32 @b() returns_twice %add = add nsw i32 1, %call @@ -85,7 +90,9 @@ entry: ret i32 %add } -define i32 @inner5(i8* %addr) alwaysinline { +; We can't inline this even though it has alwaysinline! +define internal i32 @inner5(i8* %addr) alwaysinline { +; CHECK-LABEL: @inner5( entry: indirectbr i8* %addr, [ label %one, label %two ] @@ -106,7 +113,9 @@ define i32 @outer5(i32 %x) { ret i32 %call } -define void @inner6(i32 %x) alwaysinline { +; We alwaysinline a function that call itself recursively. +define internal void @inner6(i32 %x) alwaysinline { +; CHECK-LABEL: @inner6( entry: %icmp = icmp slt i32 %x, 0 br i1 %icmp, label %return, label %bb @@ -129,7 +138,9 @@ entry: ret void } +; This is not an alwaysinline function and is actually external. define i32 @inner7() { +; CHECK-LABEL: @inner7( ret i32 1 } define i32 @outer7() { @@ -141,7 +152,8 @@ define i32 @outer7() { ret i32 %r } -define float* @inner8(float* nocapture align 128 %a) alwaysinline { +define internal float* @inner8(float* nocapture align 128 %a) alwaysinline { +; CHECK-NOT: @inner8( ret float* %a } define float @outer8(float* nocapture %a) { @@ -153,3 +165,137 @@ define float @outer8(float* nocapture %a) { %f = load float, float* %inner_a, align 4 ret float %f } + + +; The 'inner9*' and 'outer9' functions are designed to check that we remove +; a function that is inlined by the always inliner even when it is used by +; a complex constant expression prior to being inlined. + +; The 'a' function gets used in a complex constant expression that, despite +; being constant folded, means it isn't dead. As a consequence it shouldn't be +; deleted. If it is, then the constant expression needs to become more complex +; to accurately test this scenario. +define internal void @inner9a(i1 %b) alwaysinline { +; CHECK-LABEL: @inner9a( +entry: + ret void +} + +define internal void @inner9b(i1 %b) alwaysinline { +; CHECK-NOT: @inner9b( +entry: + ret void +} + +declare void @dummy9(i1 %b) + +define void @outer9() { +; CHECK-LABEL: @outer9( +entry: + ; First we use @inner9a in a complex constant expression that may get folded + ; but won't get removed, and then we call it which will get inlined. Despite + ; this the function can't be deleted because of the constant expression + ; usage. + %sink = alloca i1 + store volatile i1 icmp eq (i64 ptrtoint (void (i1)* @inner9a to i64), i64 ptrtoint(void (i1)* @dummy9 to i64)), i1* %sink +; CHECK: store volatile + call void @inner9a(i1 false) +; CHECK-NOT: call void @inner9a + + ; Next we call @inner9b passing in a constant expression. This constant + ; expression will in fact be removed by inlining, so we should also be able + ; to delete the function. + call void @inner9b(i1 icmp eq (i64 ptrtoint (void (i1)* @inner9b to i64), i64 ptrtoint(void (i1)* @dummy9 to i64))) +; CHECK-NOT: @inner9b + + ret void +; CHECK: ret void +} + +; The 'inner10' and 'outer10' functions test a frustrating consquence of the +; current 'alwaysinline' semantic model. Because such functions are allowed to +; be external functions, it may be necessary to both inline all of their uses +; and leave them in the final output. These tests can be removed if and when +; we restrict alwaysinline further. +define void @inner10() alwaysinline { +; CHECK-LABEL: @inner10( +entry: + ret void +} + +define void @outer10() { +; CHECK-LABEL: @outer10( +entry: + call void @inner10() +; CHECK-NOT: call void @inner10 + + ret void +; CHECK: ret void +} + +; The 'inner11' and 'outer11' functions test another dimension of non-internal +; functions with alwaysinline. These functions use external linkages that we can +; actually remove safely and so we should. +define linkonce void @inner11a() alwaysinline { +; CHECK-NOT: @inner11a( +entry: + ret void +} + +define available_externally void @inner11b() alwaysinline { +; CHECK-NOT: @inner11b( +entry: + ret void +} + +define void @outer11() { +; CHECK-LABEL: @outer11( +entry: + call void @inner11a() + call void @inner11b() +; CHECK-NOT: call void @inner11a +; CHECK-NOT: call void @inner11b + + ret void +; CHECK: ret void +} + +; The 'inner12' and 'outer12' functions test that we don't remove functions +; which are part of a comdat group even if they otherwise seem dead. +$comdat12 = comdat any + +define linkonce void @inner12() alwaysinline comdat($comdat12) { +; CHECK-LABEL: @inner12( +t :qentry: + ret void +} + +define void @outer12() comdat($comdat12) { +; CHECK-LABEL: @outer12( +entry: + call void @inner12() +; CHECK-NOT: call void @inner12 + + ret void +; CHECK: ret void +} + +; The 'inner12' and 'outer12' functions test that we don't remove functions +; which are part of a comdat group even if they otherwise seem dead. +$comdat12 = comdat any + +define linkonce void @inner12() alwaysinline comdat($comdat12) { +; CHECK-LABEL: @inner12( +entry: + ret void +} + +define void @outer12() comdat($comdat12) { +; CHECK-LABEL: @outer12( +entry: + call void @inner12() +; CHECK-NOT: call void @inner12 + + ret void +; CHECK: ret void +} |

