|
| 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