Skip to content

Commit bed890c

Browse files
committed
[SILOptimizer] Added MandatoryCombiner.
Minimal commit to get the mandatory combiner rolling. At the moment, the mandatory combiner only processes partial apply instructions, attempting both to replace their applies with applies of their underlying function refs and also to eliminate them altogether. That processing is only done if all the arguments to the apply and to the partial apply are trivial. For now, the pass is not part of any pipeline.
1 parent 228a394 commit bed890c

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)