diff --git a/llvm/include/llvm/Transforms/IPO/IROutliner.h b/llvm/include/llvm/Transforms/IPO/IROutliner.h index 6291af741184f..947a70866b04a 100644 --- a/llvm/include/llvm/Transforms/IPO/IROutliner.h +++ b/llvm/include/llvm/Transforms/IPO/IROutliner.h @@ -258,6 +258,10 @@ class IROutliner { std::vector &FuncsToRemove, unsigned &OutlinedFunctionNum); + /// If true, enables us to outline from functions that have LinkOnceFromODR + /// linkages. + bool OutlineFromLinkODRs = false; + /// If false, we do not worry if the cost is greater than the benefit. This /// is for debugging and testing, so that we can test small cases to ensure /// that the outlining is being done correctly. diff --git a/llvm/lib/Transforms/IPO/IROutliner.cpp b/llvm/lib/Transforms/IPO/IROutliner.cpp index 5289826f4a9ab..908ba0c70e70c 100644 --- a/llvm/lib/Transforms/IPO/IROutliner.cpp +++ b/llvm/lib/Transforms/IPO/IROutliner.cpp @@ -30,6 +30,16 @@ using namespace llvm; using namespace IRSimilarity; +// Set to true if the user wants the ir outliner to run on linkonceodr linkage +// functions. This is false by default because the linker can dedupe linkonceodr +// functions. Since the outliner is confined to a single module (modulo LTO), +// this is off by default. It should, however, be the default behavior in +// LTO. +static cl::opt EnableLinkOnceODRIROutlining( + "enable-linkonceodr-ir-outlining", cl::Hidden, + cl::desc("Enable the IR outliner on linkonceodr functions"), + cl::init(false)); + // This is a debug option to test small pieces of code to ensure that outlining // works correctly. static cl::opt NoCostModel( @@ -1243,6 +1253,10 @@ void IROutliner::pruneIncompatibleRegions( if (IRSC.getStartBB()->hasAddressTaken()) continue; + if (IRSC.front()->Inst->getFunction()->hasLinkOnceODRLinkage() && + !OutlineFromLinkODRs) + continue; + // Greedily prune out any regions that will overlap with already chosen // regions. if (CurrentEndIdx != 0 && StartIdx <= CurrentEndIdx) @@ -1659,6 +1673,7 @@ unsigned IROutliner::doOutline(Module &M) { bool IROutliner::run(Module &M) { CostModel = !NoCostModel; + OutlineFromLinkODRs = EnableLinkOnceODRIROutlining; return doOutline(M) > 0; } diff --git a/llvm/test/Transforms/IROutliner/outlining-odr.ll b/llvm/test/Transforms/IROutliner/outlining-odr.ll new file mode 100644 index 0000000000000..074de37c40bf8 --- /dev/null +++ b/llvm/test/Transforms/IROutliner/outlining-odr.ll @@ -0,0 +1,70 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py +; RUN: opt -S -verify -iroutliner --ir-outlining-no-cost < %s | FileCheck %s +; RUN: opt -S -verify -iroutliner -enable-linkonceodr-ir-outlining --ir-outlining-no-cost < %s | FileCheck -check-prefix=ODR %s + +; This test looks at the constants in the regions, and if it they are the +; same it outlines them as constants rather than elevating them to arguments. + +define linkonce_odr void @outline_odr1() { +; ODR-LABEL: @outline_odr1( +; ODR-NEXT: entry: +; ODR-NEXT: [[A:%.*]] = alloca i32, align 4 +; ODR-NEXT: [[B:%.*]] = alloca i32, align 4 +; ODR-NEXT: [[C:%.*]] = alloca i32, align 4 +; ODR-NEXT: call void @outlined_ir_func_0(i32* [[A]], i32* [[B]], i32* [[C]]) +; ODR-NEXT: ret void +; CHECK-LABEL: @outline_odr1( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[A:%.*]] = alloca i32, align 4 +; CHECK-NEXT: [[B:%.*]] = alloca i32, align 4 +; CHECK-NEXT: [[C:%.*]] = alloca i32, align 4 +; CHECK-NEXT: store i32 2, i32* [[A]], align 4 +; CHECK-NEXT: store i32 3, i32* [[B]], align 4 +; CHECK-NEXT: store i32 4, i32* [[C]], align 4 +; CHECK-NEXT: [[AL:%.*]] = load i32, i32* [[A]], align 4 +; CHECK-NEXT: [[BL:%.*]] = load i32, i32* [[B]], align 4 +; CHECK-NEXT: [[CL:%.*]] = load i32, i32* [[C]], align 4 +entry: + %a = alloca i32, align 4 + %b = alloca i32, align 4 + %c = alloca i32, align 4 + store i32 2, i32* %a, align 4 + store i32 3, i32* %b, align 4 + store i32 4, i32* %c, align 4 + %al = load i32, i32* %a + %bl = load i32, i32* %b + %cl = load i32, i32* %c + ret void +} + +define void @outline_odr2() { +; ODR-LABEL: @outline_odr2( +; ODR-NEXT: entry: +; ODR-NEXT: [[A:%.*]] = alloca i32, align 4 +; ODR-NEXT: [[B:%.*]] = alloca i32, align 4 +; ODR-NEXT: [[C:%.*]] = alloca i32, align 4 +; ODR-NEXT: call void @outlined_ir_func_0(i32* [[A]], i32* [[B]], i32* [[C]]) +; ODR-NEXT: ret void +; CHECK-LABEL: @outline_odr2( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[A:%.*]] = alloca i32, align 4 +; CHECK-NEXT: [[B:%.*]] = alloca i32, align 4 +; CHECK-NEXT: [[C:%.*]] = alloca i32, align 4 +; CHECK-NEXT: store i32 2, i32* [[A]], align 4 +; CHECK-NEXT: store i32 3, i32* [[B]], align 4 +; CHECK-NEXT: store i32 4, i32* [[C]], align 4 +; CHECK-NEXT: [[AL:%.*]] = load i32, i32* [[A]], align 4 +; CHECK-NEXT: [[BL:%.*]] = load i32, i32* [[B]], align 4 +; CHECK-NEXT: [[CL:%.*]] = load i32, i32* [[C]], align 4 +entry: + %a = alloca i32, align 4 + %b = alloca i32, align 4 + %c = alloca i32, align 4 + store i32 2, i32* %a, align 4 + store i32 3, i32* %b, align 4 + store i32 4, i32* %c, align 4 + %al = load i32, i32* %a + %bl = load i32, i32* %b + %cl = load i32, i32* %c + ret void +}