Skip to content

Commit bd222ad

Browse files
authored
Merge pull request #26913 from nate-chandler/mandatory-combiner
2 parents 9bc5a55 + bed890c commit bd222ad

File tree

4 files changed

+1068
-1
lines changed

4 files changed

+1068
-1
lines changed

include/swift/SILOptimizer/PassManager/Passes.def

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@
3636
/// This macro follows the same conventions as PASS(Id, Tag, Description),
3737
/// but is used for IRGen passes which are built outside of the
3838
/// SILOptimizer library.
39-
///
39+
///
4040
/// An IRGen pass is created by IRGen and needs to be registered with the pass
4141
/// manager dynamically.
4242
#ifndef IRGEN_PASS
@@ -312,6 +312,8 @@ PASS(SerializeSILPass, "serialize-sil",
312312
PASS(YieldOnceCheck, "yield-once-check",
313313
"Check correct usage of yields in yield-once coroutines")
314314
PASS(OSLogOptimization, "os-log-optimization", "Optimize os log calls")
315+
PASS(MandatoryCombiner, "mandatory-combiner",
316+
"Perform mandatory peephole combines")
315317
PASS(BugReducerTester, "bug-reducer-tester",
316318
"sil-bug-reducer Tool Testing by Asserting on a Sentinel Function")
317319
PASS_RANGE(AllPasses, AADumper, BugReducerTester)

lib/SILOptimizer/Mandatory/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,5 +21,6 @@ silopt_register_sources(
2121
SemanticARCOpts.cpp
2222
SILGenCleanup.cpp
2323
YieldOnceCheck.cpp
24+
MandatoryCombiner.cpp
2425
OSLogOptimization.cpp
2526
)
Lines changed: 174 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,174 @@
1+
//===------- MandatoryCombiner.cpp ----------------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2019 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
///
13+
/// \file
14+
///
15+
/// Defines the MandatoryCombiner function transform. The pass contains basic
16+
/// instruction combines to be performed at the begining of both the Onone and
17+
/// also the performance pass pipelines, after the diagnostics passes have been
18+
/// run. It is intended to be run before and to be independent of other
19+
/// transforms.
20+
///
21+
/// The intention of this pass is to be a place for mandatory peepholes that
22+
/// are not needed for diagnostics. Please put any such peepholes here instead
23+
/// of in the diagnostic passes.
24+
///
25+
//===----------------------------------------------------------------------===//
26+
27+
#define DEBUG_TYPE "sil-mandatory-combiner"
28+
#include "swift/SIL/SILVisitor.h"
29+
#include "swift/SILOptimizer/PassManager/Passes.h"
30+
#include "swift/SILOptimizer/PassManager/Transforms.h"
31+
#include "swift/SILOptimizer/Utils/Local.h"
32+
#include "llvm/ADT/STLExtras.h"
33+
#include "llvm/Support/raw_ostream.h"
34+
#include <algorithm>
35+
#include <vector>
36+
37+
using namespace swift;
38+
39+
/// \returns whether all the values are of trivial type in the provided
40+
/// function.
41+
template <typename Values>
42+
static bool areAllValuesTrivial(Values values, SILFunction &function) {
43+
return llvm::all_of(values, [&](SILValue value) -> bool {
44+
return value->getType().isTrivial(function);
45+
});
46+
}
47+
48+
/// Replaces all full applies of the specified partial apply with full applies
49+
/// of the underlying function ref and attempts to delete the partial apply.
50+
///
51+
/// The transformation of the full applies only takes place if all the arguments
52+
/// to the full applies are trivial.
53+
///
54+
/// TODO: Apply this transformation to partial applies not all of whose
55+
/// arguments are trivial.
56+
///
57+
/// \param pai the partial apply to attempt to delete
58+
///
59+
/// \returns a pair of bools. The first bool indicates whether the a full apply
60+
/// of the partial apply was deleted, meaning that analyses must be
61+
/// invalidated. The second bool indicates that the provided partial
62+
/// apply instruction itself was deleted, with consequences for the
63+
/// iterator.
64+
static std::pair<bool, bool> processPartialApply(PartialApplyInst *pai) {
65+
auto callee = pai->getCallee();
66+
LLVM_DEBUG(llvm::dbgs() << "Callee: " << *callee);
67+
68+
// Apply this pass only to partial applies all of whose arguments are
69+
// trivial.
70+
auto *function = pai->getReferencedFunctionOrNull();
71+
if (!function) {
72+
return {false, false};
73+
}
74+
75+
auto paiArgs = ApplySite(pai).getArguments();
76+
if (!areAllValuesTrivial(paiArgs, *function)) {
77+
return {false, false};
78+
}
79+
80+
bool erasedFullApply = false;
81+
for (auto *use : pai->getUses()) {
82+
auto fas = FullApplySite::isa(use->getUser());
83+
if (!fas) {
84+
continue;
85+
}
86+
LLVM_DEBUG(llvm::dbgs() << "Partial Apply: " << *pai);
87+
LLVM_DEBUG(llvm::dbgs() << "Full Apply Site: " << *fas.getInstruction());
88+
auto *fasi = dyn_cast<ApplyInst>(fas.getInstruction());
89+
if (!fasi) {
90+
continue;
91+
}
92+
93+
auto fasArgs = fas.getArguments();
94+
if (!areAllValuesTrivial(fasArgs, *function)) {
95+
continue;
96+
}
97+
98+
SmallVector<SILValue, 8> argsVec;
99+
llvm::copy(paiArgs, std::back_inserter(argsVec));
100+
llvm::copy(fasArgs, std::back_inserter(argsVec));
101+
102+
SILBuilderWithScope builder(fasi);
103+
ApplyInst *appInst = builder.createApply(
104+
/*Loc=*/fasi->getDebugLocation().getLocation(), /*Fn=*/callee,
105+
/*Subs=*/pai->getSubstitutionMap(),
106+
/*Args*/ argsVec,
107+
/*isNonThrowing=*/fasi->isNonThrowing(),
108+
/*SpecializationInfo=*/pai->getSpecializationInfo());
109+
fasi->replaceAllUsesWith(appInst);
110+
fasi->eraseFromParent();
111+
erasedFullApply = true;
112+
}
113+
bool deletedDeadClosure = tryDeleteDeadClosure(pai);
114+
return {erasedFullApply, deletedDeadClosure};
115+
}
116+
117+
//===----------------------------------------------------------------------===//
118+
// Top Level Entrypoint
119+
//===----------------------------------------------------------------------===//
120+
121+
namespace {
122+
123+
struct MandatoryCombiner : SILFunctionTransform {
124+
void run() override {
125+
bool madeChange = false;
126+
auto *f = getFunction();
127+
for (auto &block : *f) {
128+
for (auto ii = block.begin(), ie = block.end(); ii != ie;) {
129+
auto *inst = &*ii;
130+
// If any action is taken, the current instruction will be deleted.
131+
// That instruction would be the definition of a partial apply. Because
132+
// the definition dominates all its uses, the previous instruction will
133+
// be unaffected by the removal of the instruction and its uses. So
134+
// move the iterator back a step. If no action is taken, it will be
135+
// advanced twice. If an action is taken, it will be advanced once to a
136+
// new instruction (since the current one will have been deleted).
137+
ii = prev_or_default(ii, block.begin(), block.end());
138+
139+
bool deleted = false;
140+
if (auto *pai = dyn_cast<PartialApplyInst>(inst)) {
141+
auto result = processPartialApply(pai);
142+
deleted = result.second;
143+
madeChange |= result.first || result.second;
144+
}
145+
146+
if (deleted) {
147+
// The deletion succeeded. The iterator has already been moved back
148+
// a step so as to avoid invalidation. Advancing it one step will
149+
// not advance it to the current instruction since that has been
150+
// deleted. Instead, it will advance it to the next *remaining*
151+
// instruction after the previous instruction as desired.
152+
ii = next_or_default(ii, /*end*/block.end(), /*defaultIter*/block.begin());
153+
continue;
154+
}
155+
156+
// No action was taken. The iterator has already been moved back a
157+
// step. Consequently it is insufficient to advance it once--it must
158+
// be advanced twice.
159+
ii = next_or_default(ii, /*end*/block.end(), /*defaultIter*/block.begin());
160+
++ii;
161+
}
162+
}
163+
164+
if (madeChange) {
165+
invalidateAnalysis(SILAnalysis::InvalidationKind::Instructions);
166+
}
167+
}
168+
};
169+
170+
} // end anonymous namespace
171+
172+
SILTransform *swift::createMandatoryCombiner() {
173+
return new MandatoryCombiner();
174+
}

0 commit comments

Comments
 (0)