[MachineOutliner] Disable outlining from LinkOnceODRs by default
authorJessica Paquette <jpaquette@apple.com>
Sat, 7 Oct 2017 00:16:34 +0000 (00:16 +0000)
committerJessica Paquette <jpaquette@apple.com>
Sat, 7 Oct 2017 00:16:34 +0000 (00:16 +0000)
Say you have two identical linkonceodr functions, one in M1 and one in M2.
Say that the outliner outlines A,B,C from one function, and D,E,F from another
function (where letters are instructions). Now those functions are not
identical, and cannot be deduped. Locally to M1 and M2, these outlining
choices would be good-- to the whole program, however, this might not be true!

To mitigate this, this commit makes it so that the outliner sees linkonceodr
functions as unsafe to outline from. It also adds a flag,
-enable-linkonceodr-outlining, which allows the user to specify that they
want to outline from such functions when they know what they're doing.

Changing this handles most code size regressions in the test suite caused by
competing with linker dedupe. It also doesn't have a huge impact on the code
size improvements from the outliner. There are 6 tests that regress > 5% from
outlining WITH linkonceodrs to outlining WITHOUT linkonceodrs. Overall, most
tests either improve or are not impacted.

Not outlined vs outlined without linkonceodrs:
https://hastebin.com/raw/qeguxavuda

Not outlined vs outlined with linkonceodrs:
https://hastebin.com/raw/edepoqoqic

Outlined with linkonceodrs vs outlined without linkonceodrs:
https://hastebin.com/raw/awiqifiheb

Numbers generated using compare.py with -m size.__text. Tests run for AArch64
with -Oz -mllvm -enable-machine-outliner -mno-red-zone.

llvm-svn: 315136

llvm/include/llvm/CodeGen/Passes.h
llvm/include/llvm/Target/TargetInstrInfo.h
llvm/lib/CodeGen/MachineOutliner.cpp
llvm/lib/CodeGen/TargetPassConfig.cpp
llvm/lib/Target/AArch64/AArch64InstrInfo.cpp
llvm/lib/Target/AArch64/AArch64InstrInfo.h
llvm/lib/Target/X86/X86InstrInfo.cpp
llvm/lib/Target/X86/X86InstrInfo.h
llvm/test/CodeGen/AArch64/machine-outliner.ll

index 1377a6dd6aa7f03cd230e371aeb8a44a6069c269..8e6b1570e4a371c8f098a9954db318635f43617f 100644 (file)
@@ -411,7 +411,7 @@ namespace llvm {
 
   /// This pass performs outlining on machine instructions directly before
   /// printing assembly.
-  ModulePass *createMachineOutlinerPass();
+  ModulePass *createMachineOutlinerPass(bool OutlineFromLinkOnceODRs = false);
 
   /// This pass expands the experimental reduction intrinsics into sequences of
   /// shuffles.
index 8e7e6a7fbfd7d4ff72a792f97b76c3dfd157d887..5d230d820dbf241d9f87a8057bf06d7713f99a62 100644 (file)
@@ -1646,7 +1646,8 @@ public:
   /// A function \p MF is considered safe for outlining if an outlined function
   /// produced from instructions in F will produce a program which produces the
   /// same output for any set of given inputs.
-  virtual bool isFunctionSafeToOutlineFrom(MachineFunction &MF) const {
+  virtual bool isFunctionSafeToOutlineFrom(MachineFunction &MF,
+                                           bool OutlineFromLinkOnceODRs) const {
     llvm_unreachable("Target didn't implement "
                      "TargetInstrInfo::isFunctionSafeToOutlineFrom!");
   }
index b52d3ebfeff41d56f9a645a3ea801c472dac57e0..8e6e0b12409eafe034e0d63842aaea2e80012f30 100644 (file)
@@ -746,6 +746,10 @@ struct MachineOutliner : public ModulePass {
 
   static char ID;
 
+  /// \brief Set to true if the outliner should consider functions with
+  /// linkonceodr linkage.
+  bool OutlineFromLinkOnceODRs = false;
+
   StringRef getPassName() const override { return "Machine Outliner"; }
 
   void getAnalysisUsage(AnalysisUsage &AU) const override {
@@ -755,7 +759,8 @@ struct MachineOutliner : public ModulePass {
     ModulePass::getAnalysisUsage(AU);
   }
 
-  MachineOutliner() : ModulePass(ID) {
+  MachineOutliner(bool OutlineFromLinkOnceODRs = false) :
+  ModulePass(ID), OutlineFromLinkOnceODRs(OutlineFromLinkOnceODRs) {
     initializeMachineOutlinerPass(*PassRegistry::getPassRegistry());
   }
 
@@ -844,7 +849,10 @@ struct MachineOutliner : public ModulePass {
 char MachineOutliner::ID = 0;
 
 namespace llvm {
-ModulePass *createMachineOutlinerPass() { return new MachineOutliner(); }
+ModulePass *createMachineOutlinerPass(bool OutlineFromLinkOnceODRs) {
+  return new MachineOutliner(OutlineFromLinkOnceODRs);
+}
+
 } // namespace llvm
 
 INITIALIZE_PASS(MachineOutliner, DEBUG_TYPE, "Machine Function Outliner", false,
@@ -1248,7 +1256,8 @@ bool MachineOutliner::runOnModule(Module &M) {
     MachineFunction &MF = MMI.getOrCreateMachineFunction(F);
 
     // Is the function empty? Safe to outline from?
-    if (F.empty() || !TII->isFunctionSafeToOutlineFrom(MF))
+    if (F.empty() ||
+        !TII->isFunctionSafeToOutlineFrom(MF, OutlineFromLinkOnceODRs))
       continue;
 
     // If it is, look at each MachineBasicBlock in the function.
index 4584f65619cc5af571ea1bbbc5e15ac06b40172f..c5101b1ecfc22adbe183ce6d7c20082499a24338 100644 (file)
@@ -111,6 +111,11 @@ static cl::opt<bool> VerifyMachineCode("verify-machineinstrs", cl::Hidden,
 static cl::opt<bool> EnableMachineOutliner("enable-machine-outliner",
     cl::Hidden,
     cl::desc("Enable machine outliner"));
+static cl::opt<bool> EnableLinkOnceODROutlining(
+    "enable-linkonceodr-outlining",
+    cl::Hidden,
+    cl::desc("Enable the machine outliner on linkonceodr functions"),
+    cl::init(false));
 // Enable or disable FastISel. Both options are needed, because
 // FastISel is enabled by default with -fast, and we wish to be
 // able to enable or disable fast-isel independently from -O0.
@@ -891,7 +896,7 @@ void TargetPassConfig::addMachinePasses() {
   addPass(&PatchableFunctionID, false);
 
   if (EnableMachineOutliner)
-    PM->add(createMachineOutlinerPass());
+    PM->add(createMachineOutlinerPass(EnableLinkOnceODROutlining));
 
   AddingMachinePasses = false;
 }
index 1d35fb3da2bdc8a99a522ab04ed23f6762517675..03a358053bb52a1909cc2445626e5fc8055fbdbf 100644 (file)
@@ -4646,13 +4646,24 @@ AArch64InstrInfo::getOutlininingCandidateInfo(
                              FrameID);
 }
 
-bool AArch64InstrInfo::isFunctionSafeToOutlineFrom(MachineFunction &MF) const {
-  // If MF has a red zone, then we ought not to outline from it, since outlined
-  // functions can modify/read from the stack.
-  // If MF's address is taken, then we don't want to outline from it either
-  // since we don't really know what the user is doing with it.
-  return MF.getFunction()->hasFnAttribute(Attribute::NoRedZone) &&
-         !MF.getFunction()->hasAddressTaken();
+bool AArch64InstrInfo::isFunctionSafeToOutlineFrom(MachineFunction &MF,
+                                           bool OutlineFromLinkOnceODRs) const {
+  const Function *F = MF.getFunction();
+
+  // If F uses a redzone, then don't outline from it because it might mess up
+  // the stack.
+  if (!F->hasFnAttribute(Attribute::NoRedZone))
+    return false;
+
+  // If anyone is using the address of this function, don't outline from it.
+  if (F->hasAddressTaken())
+    return false;
+
+  // Can F be deduplicated by the linker? If it can, don't outline from it.
+  if (!OutlineFromLinkOnceODRs && F->hasLinkOnceODRLinkage())
+    return false;
+  
+  return true;
 }
 
 AArch64GenInstrInfo::MachineOutlinerInstrType
index 9a338b53c7a92f6d3e9c883ec71405f351ec2771..24758e9788860b19a68320edd792f25cb36fbf0f 100644 (file)
@@ -352,7 +352,8 @@ public:
 
   bool
   canOutlineWithoutLRSave(MachineBasicBlock::iterator &CallInsertionPt) const;
-  bool isFunctionSafeToOutlineFrom(MachineFunction &MF) const override;
+  bool isFunctionSafeToOutlineFrom(MachineFunction &MF,
+                                   bool OutlineFromLinkOnceODRs) const override;
   MachineOutlinerInfo getOutlininingCandidateInfo(
       std::vector<
           std::pair<MachineBasicBlock::iterator, MachineBasicBlock::iterator>>
index 604ee1533fa056e347cc67d164f72d384aa2faa6..3fea891cdaf4e8d3969daf48a2939a3dae5e9600 100644 (file)
@@ -10763,8 +10763,22 @@ X86InstrInfo::getOutlininingCandidateInfo(
                              MachineOutlinerDefault);
 }
 
-bool X86InstrInfo::isFunctionSafeToOutlineFrom(MachineFunction &MF) const {
-  return MF.getFunction()->hasFnAttribute(Attribute::NoRedZone);
+bool X86InstrInfo::isFunctionSafeToOutlineFrom(MachineFunction &MF,
+                                           bool OutlineFromLinkOnceODRs) const {
+  const Function *F = MF.getFunction();
+
+  // Does the function use a red zone? If it does, then we can't risk messing
+  // with the stack.
+  if (!F->hasFnAttribute(Attribute::NoRedZone))
+      return false;
+  
+  // If we *don't* want to outline from things that could potentially be deduped
+  // then return false.
+  if (!OutlineFromLinkOnceODRs && F->hasLinkOnceODRLinkage())
+      return false;
+  
+  // This function is viable for outlining, so return true.
+  return true;
 }
 
 X86GenInstrInfo::MachineOutlinerInstrType
index 8bbf7dc6d2335404ed5b0c30e45ef1dbd5b6289b..e665ec1f14dce53a9b11679586114a3226e3899d 100644 (file)
@@ -564,7 +564,8 @@ public:
           std::pair<MachineBasicBlock::iterator, MachineBasicBlock::iterator>>
           &RepeatedSequenceLocs) const override;
 
-  bool isFunctionSafeToOutlineFrom(MachineFunction &MF) const override;
+  bool isFunctionSafeToOutlineFrom(MachineFunction &MF,
+                                   bool OutlineFromLinkOnceODRs) const override;
 
   llvm::X86GenInstrInfo::MachineOutlinerInstrType
   getOutliningType(MachineInstr &MI) const override;
index b5094fe47508b1eb7f920ab81fc019c82e3ae1f1..9b6254fb3cc1a3e8cd7c73a594aaf729b1e30d64 100644 (file)
@@ -1,9 +1,31 @@
-; RUN: llc -enable-machine-outliner -mtriple=aarch64-apple-darwin < %s | FileCheck %s
+; RUN: llc -enable-machine-outliner -mtriple=aarch64-apple-darwin < %s | FileCheck %s -check-prefix=NoODR
+; RUN: llc -enable-machine-outliner -enable-linkonceodr-outlining -mtriple=aarch64-apple-darwin < %s | FileCheck %s -check-prefix=ODR
+
+define linkonce_odr void @fish() #0 {
+  ; CHECK-LABEL: _fish:
+  ; NoODR:      orr w8, wzr, #0x1
+  ; NoODR-NEXT: stp w8, wzr, [sp, #8]
+  ; NoODR-NEXT: orr w8, wzr, #0x2
+  ; NoODR-NEXT: str w8, [sp, #4]
+  ; NoODR-NEXT: orr w8, wzr, #0x3
+  ; NoODR-NEXT: str w8, [sp], #16
+  ; NoODR-NEXT: ret
+  ; ODR: b l_OUTLINED_FUNCTION_0
+  %1 = alloca i32, align 4
+  %2 = alloca i32, align 4
+  %3 = alloca i32, align 4
+  %4 = alloca i32, align 4
+  store i32 0, i32* %1, align 4
+  store i32 1, i32* %2, align 4
+  store i32 2, i32* %3, align 4
+  store i32 3, i32* %4, align 4
+  ret void
+}
 
 define void @cat() #0 {
-; CHECK-LABEL: _cat:
-; CHECK: b l_OUTLINED_FUNCTION_0
-; CHECK-NOT: ret
+  ; CHECK-LABEL: _cat:
+  ; CHECK: b l_OUTLINED_FUNCTION_0
+  ; CHECK-NOT: ret
   %1 = alloca i32, align 4
   %2 = alloca i32, align 4
   %3 = alloca i32, align 4
@@ -16,9 +38,9 @@ define void @cat() #0 {
 }
 
 define void @dog() #0 {
-; CHECK-LABEL: _dog:
-; CHECK: b l_OUTLINED_FUNCTION_0
-; CHECK-NOT: ret
+  ; CHECK-LABEL: _dog:
+  ; CHECK: b l_OUTLINED_FUNCTION_0
+  ; CHECK-NOT: ret
   %1 = alloca i32, align 4
   %2 = alloca i32, align 4
   %3 = alloca i32, align 4
@@ -39,5 +61,4 @@ define void @dog() #0 {
 ; CHECK-NEXT: str w8, [sp], #16
 ; CHECK-NEXT: ret
 
-
 attributes #0 = { noredzone nounwind ssp uwtable "no-frame-pointer-elim"="false" "target-cpu"="cyclone" }