Index: lib/Passes/PassBuilder.cpp =================================================================== --- lib/Passes/PassBuilder.cpp +++ lib/Passes/PassBuilder.cpp @@ -147,6 +147,9 @@ using namespace llvm; +static cl::opt MaxDevirtIterations("pm-max-devirt-iterations", + cl::ReallyHidden, cl::init(4)); + static Regex DefaultAliasRegex("^(default|lto-pre-link|lto)<(O[0123sz])>$"); static bool isOptimizingForSize(PassBuilder::OptimizationLevel Level) { @@ -466,8 +469,14 @@ MainCGPipeline.addPass(createCGSCCToFunctionPassAdaptor( buildFunctionSimplificationPipeline(Level, DebugLogging))); + // We wrap the CGSCC pipeline in a devirtualization repeater. This will try + // to detect when we devirtualize indirect calls and iterate the SCC passes + // in that case to try and catch knock-on inlining or function attrs + // opportunities. Then we add it to the module pipeline by walking the SCCs + // in postorder (or bottom-up). MPM.addPass( - createModuleToPostOrderCGSCCPassAdaptor(std::move(MainCGPipeline))); + createModuleToPostOrderCGSCCPassAdaptor(createDevirtSCCRepeatedPass( + std::move(MainCGPipeline), MaxDevirtIterations, DebugLogging))); // This ends the canonicalization and simplification phase of the pipeline. // At this point, we expect to have canonical and simple IR which we begin Index: test/Other/cgscc-devirt-iteration.ll =================================================================== --- test/Other/cgscc-devirt-iteration.ll +++ test/Other/cgscc-devirt-iteration.ll @@ -7,6 +7,9 @@ ; RUN: opt -aa-pipeline=basic-aa -passes='cgscc(function-attrs,function(gvn,instcombine))' -S < %s | FileCheck %s --check-prefix=CHECK --check-prefix=BEFORE ; RUN: opt -aa-pipeline=basic-aa -passes='cgscc(devirt<1>(function-attrs,function(gvn,instcombine)))' -S < %s | FileCheck %s --check-prefix=CHECK --check-prefix=AFTER --check-prefix=AFTER1 ; RUN: opt -aa-pipeline=basic-aa -passes='cgscc(devirt<2>(function-attrs,function(gvn,instcombine)))' -S < %s | FileCheck %s --check-prefix=CHECK --check-prefix=AFTER --check-prefix=AFTER2 +; +; We also verify that the real O2 pipeline catches these cases. +; RUN: opt -aa-pipeline=basic-aa -passes='default' -S < %s | FileCheck %s --check-prefix=CHECK --check-prefix=AFTER --check-prefix=AFTER2 declare void @readnone() readnone ; CHECK: Function Attrs: readnone @@ -93,8 +96,7 @@ } declare i8* @memcpy(i8*, i8*, i64) -; CHECK-NOT: Function Attrs -; CHECK: declare i8* @memcpy(i8*, i8*, i64) +; CHECK: declare i8* @memcpy( ; The @test3 function checks that when we refine an indirect call to an ; intrinsic we still revisit the SCC pass. This also covers cases where the @@ -112,3 +114,15 @@ ; CHECK: call void @llvm.memcpy ret void } + +; A boring function that just keeps our declarations around. +define void @keep(i8** %sink) { +; CHECK-NOT: Function Attrs +; CHECK: define void @keep( +entry: + store volatile i8* bitcast (void ()* @readnone to i8*), i8** %sink + store volatile i8* bitcast (void ()* @unknown to i8*), i8** %sink + store volatile i8* bitcast (i8* (i8*, i8*, i64)* @memcpy to i8*), i8** %sink + call void @unknown() + ret void +}