From fe431103b633278da9ece1e03d6b441c1d44d977 Mon Sep 17 00:00:00 2001 From: Andrew Litteken Date: Tue, 15 Sep 2020 22:02:18 -0500 Subject: [PATCH] [IROutliner] Adding option to enable outlining from linkonceodr functions There are functions that the linker is able to automatically deduplicate, we do not outline from these functions by default. This allows for outlining from those functions. Tests: llvm/test/Transforms/IROutliner/outlining-odr.ll Reviewers: jroelofs, paquette Differential Revision: https://reviews.llvm.org/D87309 --- llvm/include/llvm/Transforms/IPO/IROutliner.h | 4 ++ llvm/lib/Transforms/IPO/IROutliner.cpp | 15 +++++ llvm/test/Transforms/IROutliner/outlining-odr.ll | 70 ++++++++++++++++++++++++ 3 files changed, 89 insertions(+) create mode 100644 llvm/test/Transforms/IROutliner/outlining-odr.ll diff --git a/llvm/include/llvm/Transforms/IPO/IROutliner.h b/llvm/include/llvm/Transforms/IPO/IROutliner.h index 6291af7..947a708 100644 --- a/llvm/include/llvm/Transforms/IPO/IROutliner.h +++ b/llvm/include/llvm/Transforms/IPO/IROutliner.h @@ -258,6 +258,10 @@ private: 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 5289826..908ba0c 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 0000000..074de37 --- /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 +} -- 2.7.4