From 03b0a6c14826bae9509fe9e1522d03f19ea7216a Mon Sep 17 00:00:00 2001 From: Erik Eckstein Date: Mon, 27 Jan 2020 14:44:43 +0100 Subject: [PATCH] DeadFunctionElimination: remove externally available witness tables at the end of the pipeline ... including all SIL functions with are transitively referenced from such witness tables. After the last devirtualizer run witness tables are not needed in the optimizer anymore. We can delete witness tables with an available-externally linkage. IRGen does not emit such witness tables anyway. This can save a little bit of compile time, because it reduces the amount of SIL at the end of the optimizer pipeline. It also reduces the size of the SIL output after the optimizer, which makes debugging the SIL output easier. --- .../swift/SILOptimizer/PassManager/Passes.def | 2 ++ .../IPO/DeadFunctionElimination.cpp | 33 +++++++++++++++---- lib/SILOptimizer/PassManager/PassPipeline.cpp | 4 ++- ...ial_inst_deserializes_witness_tables.swift | 2 +- test/SILOptimizer/devirt_opaque_witness.swift | 2 +- ...witness_tables_external_witnesstable.swift | 8 ++++- 6 files changed, 41 insertions(+), 10 deletions(-) diff --git a/include/swift/SILOptimizer/PassManager/Passes.def b/include/swift/SILOptimizer/PassManager/Passes.def index 5b8005e807db6..b879b3a666795 100644 --- a/include/swift/SILOptimizer/PassManager/Passes.def +++ b/include/swift/SILOptimizer/PassManager/Passes.def @@ -200,6 +200,8 @@ PASS(LICM, "licm", "Loop Invariant Code Motion") PASS(LateCodeMotion, "late-codemotion", "Late Code Motion with Release Hoisting") +PASS(LateDeadFunctionElimination, "late-deadfuncelim", + "Late Dead Function Elimination") PASS(LateInliner, "late-inline", "Late Function Inlining") PASS(LoopCanonicalizer, "loop-canonicalizer", diff --git a/lib/SILOptimizer/IPO/DeadFunctionElimination.cpp b/lib/SILOptimizer/IPO/DeadFunctionElimination.cpp index 4607210e2f4ee..82fd149ddbdb5 100644 --- a/lib/SILOptimizer/IPO/DeadFunctionElimination.cpp +++ b/lib/SILOptimizer/IPO/DeadFunctionElimination.cpp @@ -90,6 +90,8 @@ class FunctionLivenessComputation { llvm::SmallPtrSet AliveFunctionsAndTables; + bool keepExternalWitnessTablesAlive; + /// Checks is a function is alive, e.g. because it is visible externally. bool isAnchorFunction(SILFunction *F) { @@ -148,6 +150,11 @@ class FunctionLivenessComputation { /// Marks all contained functions and witness tables of a witness table as /// alive. void makeAlive(SILWitnessTable *WT) { + if (isAvailableExternally(WT->getLinkage()) && + !keepExternalWitnessTablesAlive) { + return; + } + LLVM_DEBUG(llvm::dbgs() << " scan witness table " << WT->getName() << '\n'); @@ -393,8 +400,10 @@ class FunctionLivenessComputation { } public: - FunctionLivenessComputation(SILModule *module) : - Module(module) {} + FunctionLivenessComputation(SILModule *module, + bool keepExternalWitnessTablesAlive) : + Module(module), + keepExternalWitnessTablesAlive(keepExternalWitnessTablesAlive) {} /// The main entry point of the optimization. bool findAliveFunctions() { @@ -635,8 +644,8 @@ class DeadFunctionElimination : FunctionLivenessComputation { } public: - DeadFunctionElimination(SILModule *module) - : FunctionLivenessComputation(module) {} + DeadFunctionElimination(SILModule *module, bool keepExternalWitnessTablesAlive) + : FunctionLivenessComputation(module, keepExternalWitnessTablesAlive) {} /// The main entry point of the optimization. void eliminateFunctions(SILModuleTransform *DFEPass) { @@ -693,6 +702,13 @@ class DeadFunctionElimination : FunctionLivenessComputation { namespace { class SILDeadFuncElimination : public SILModuleTransform { + +private: + bool isLateDFE; + +public: + SILDeadFuncElimination(bool isLateDFE) : isLateDFE(isLateDFE) { } + void run() override { LLVM_DEBUG(llvm::dbgs() << "Running DeadFuncElimination\n"); @@ -703,7 +719,8 @@ class SILDeadFuncElimination : public SILModuleTransform { // can eliminate such functions. getModule()->invalidateSILLoaderCaches(); - DeadFunctionElimination deadFunctionElimination(getModule()); + DeadFunctionElimination deadFunctionElimination(getModule(), + /*keepExternalWitnessTablesAlive*/ !isLateDFE); deadFunctionElimination.eliminateFunctions(this); } }; @@ -711,7 +728,11 @@ class SILDeadFuncElimination : public SILModuleTransform { } // end anonymous namespace SILTransform *swift::createDeadFunctionElimination() { - return new SILDeadFuncElimination(); + return new SILDeadFuncElimination(/*isLateDFE*/ false); +} + +SILTransform *swift::createLateDeadFunctionElimination() { + return new SILDeadFuncElimination(/*isLateDFE*/ true); } void swift::performSILDeadFunctionElimination(SILModule *M) { diff --git a/lib/SILOptimizer/PassManager/PassPipeline.cpp b/lib/SILOptimizer/PassManager/PassPipeline.cpp index 5868d453a4653..cf9775bed4ed2 100644 --- a/lib/SILOptimizer/PassManager/PassPipeline.cpp +++ b/lib/SILOptimizer/PassManager/PassPipeline.cpp @@ -522,7 +522,9 @@ static void addLateLoopOptPassPipeline(SILPassPipelinePlan &P) { P.startPipeline("LateLoopOpt"); // Delete dead code and drop the bodies of shared functions. - P.addDeadFunctionElimination(); + // Also, remove externally available witness tables. They are not needed + // anymore after the last devirtualizer run. + P.addLateDeadFunctionElimination(); // Perform the final lowering transformations. P.addCodeSinking(); diff --git a/test/SIL/Serialization/init_existential_inst_deserializes_witness_tables.swift b/test/SIL/Serialization/init_existential_inst_deserializes_witness_tables.swift index 9273c467d3c44..c58bc913cf01f 100644 --- a/test/SIL/Serialization/init_existential_inst_deserializes_witness_tables.swift +++ b/test/SIL/Serialization/init_existential_inst_deserializes_witness_tables.swift @@ -1,6 +1,6 @@ // RUN: %empty-directory(%t) // RUN: %target-swift-frontend -sil-inline-threshold 0 %S/Inputs/init_existential_inst_deserializes_witness_tables_input.swift -o %t/Swift.swiftmodule -emit-module -parse-as-library -parse-stdlib -module-link-name swiftCore -module-name Swift -O -// RUN: %target-swift-frontend -I %t -O %s -emit-sil -o - | %FileCheck %s +// RUN: %target-swift-frontend -I %t -O %s -Xllvm -sil-disable-pass=late-deadfuncelim -emit-sil -o - | %FileCheck %s // CHECK: sil_witness_table public_external X: P module Swift { diff --git a/test/SILOptimizer/devirt_opaque_witness.swift b/test/SILOptimizer/devirt_opaque_witness.swift index 5f8b992882b85..debd3c80b9e9e 100644 --- a/test/SILOptimizer/devirt_opaque_witness.swift +++ b/test/SILOptimizer/devirt_opaque_witness.swift @@ -1,6 +1,6 @@ // RUN: %empty-directory(%t) // RUN: %target-swift-frontend -emit-module -emit-module-path %t/opaque_conformance.swiftmodule -primary-file %S/Inputs/opaque_conformance.swift -// RUN: %target-swift-frontend -O -emit-sil -primary-file %s -I %t | %FileCheck %s +// RUN: %target-swift-frontend -O -emit-sil -primary-file %s -I %t -Xllvm -sil-disable-pass=late-deadfuncelim | %FileCheck %s import opaque_conformance diff --git a/test/SILOptimizer/sil_witness_tables_external_witnesstable.swift b/test/SILOptimizer/sil_witness_tables_external_witnesstable.swift index 3cde3796ed8db..97831516e1bd1 100644 --- a/test/SILOptimizer/sil_witness_tables_external_witnesstable.swift +++ b/test/SILOptimizer/sil_witness_tables_external_witnesstable.swift @@ -1,6 +1,7 @@ // RUN: %empty-directory(%t) // RUN: %target-swift-frontend -emit-module %S/Inputs/sil_witness_tables_external_input.swift -o %t/Swift.swiftmodule -parse-stdlib -parse-as-library -module-name Swift -module-link-name swiftCore -// RUN: %target-swift-frontend -O -I %t %s -emit-sil | %FileCheck %s +// RUN: %target-swift-frontend -O -I %t %s -Xllvm -sil-disable-pass=late-deadfuncelim -emit-sil | %FileCheck %s +// RUN: %target-swift-frontend -O -I %t %s -emit-sil | %FileCheck -check-prefix=CHECK-DFE %s import Swift @@ -8,6 +9,11 @@ import Swift // // CHECK: sil_witness_table public_external X: P module Swift { +// Also check that late dead-function-elimination is removing externally +// available witness tables. +// +// CHECK-DFE-NOT: sil_witness_table public_external + func doSomething(_ t : T) -> Y { return t.doSomething() }