Index: llvm/include/llvm/Transforms/IPO/IROutliner.h =================================================================== --- llvm/include/llvm/Transforms/IPO/IROutliner.h +++ llvm/include/llvm/Transforms/IPO/IROutliner.h @@ -202,6 +202,10 @@ std::vector &FuncsToRemove, unsigned &OutlinedFunctionNum); + /// If true, enables us to outline from functions that have LinkOnceFromODR + /// linkages. + bool OutlineFromLinkODRs = false; + /// The set of outlined Instructions, identified by their location in the /// sequential ordering of instructions in a Module. DenseSet Outlined; Index: llvm/lib/Transforms/IPO/IROutliner.cpp =================================================================== --- llvm/lib/Transforms/IPO/IROutliner.cpp +++ llvm/lib/Transforms/IPO/IROutliner.cpp @@ -29,6 +29,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)); + /// The OutlinableGroup holds all the overarching information for outlining /// a set of regions that are structurally similar to one another, such as the /// types of the overall function, the output blocks, the sets of stores needed @@ -607,6 +617,10 @@ 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) @@ -776,7 +790,11 @@ return OutlinedFunctionNum; } -bool IROutliner::run(Module &M) { return doOutline(M) > 0; } +bool IROutliner::run(Module &M) { + OutlineFromLinkODRs = EnableLinkOnceODRIROutlining; + + return doOutline(M) > 0; +} // Pass Manager Boilerplate class IROutlinerLegacyPass : public ModulePass { Index: llvm/test/Transforms/IROutliner/outlining-odr.ll =================================================================== --- /dev/null +++ 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 < %s | FileCheck %s +; RUN: opt -S -verify -iroutliner -enable-linkonceodr-ir-outlining < %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 +}