|
22 | 22 | /// ensuring that removing an instruction is not unnecessarily expensive and
|
23 | 23 | /// that only valid instructions are removed from the list.
|
24 | 24 | ///
|
| 25 | +/// Additionally, SILInstructionWorklist provides conveniences for simple |
| 26 | +/// instruction modifications and ensuring that the appropriate instructions |
| 27 | +/// will be visited accordingly. For example, if provides a method for |
| 28 | +/// replacing an operation which has already been removed with a new instruction |
| 29 | +/// determined by a SILInstructionVisitor. |
| 30 | +/// |
25 | 31 | //===----------------------------------------------------------------------===//
|
26 | 32 |
|
27 | 33 | #include "swift/Basic/BlotSetVector.h"
|
28 | 34 | #include "swift/SIL/SILInstruction.h"
|
29 | 35 | #include "swift/SIL/SILValue.h"
|
| 36 | +#include "swift/SILOptimizer/Utils/Local.h" |
30 | 37 | #include "llvm/ADT/DenseMap.h"
|
31 | 38 | #include "llvm/ADT/SmallVector.h"
|
32 | 39 |
|
@@ -126,6 +133,181 @@ class SILInstructionWorklist : SILInstructionWorklistBase {
|
126 | 133 | // Do an explicit clear, shrinking the storage if needed.
|
127 | 134 | worklist.clear();
|
128 | 135 | }
|
| 136 | + |
| 137 | + /// Find usages of \p instruction and replace them with usages of \p result. |
| 138 | + /// |
| 139 | + /// Intended to be called during visitation after \p instruction has been |
| 140 | + /// removed from the worklist. |
| 141 | + /// |
| 142 | + /// \p instruction the instruction whose usages will be replaced |
| 143 | + /// \p result the instruction whose usages will replace \p instruction |
| 144 | + /// |
| 145 | + /// \return whether the instruction was deleted or modified. |
| 146 | + bool replaceInstructionWithInstruction(SILInstruction *instruction, |
| 147 | + SILInstruction *result |
| 148 | +#ifndef NDNEBUG |
| 149 | + , |
| 150 | + std::string instructionDescription |
| 151 | +#endif |
| 152 | + ) { |
| 153 | + if (result != instruction) { |
| 154 | + assert(&*std::prev(instruction->getIterator()) == result && |
| 155 | + "Expected new instruction inserted before existing instruction!"); |
| 156 | + |
| 157 | + withDebugStream([&](llvm::raw_ostream &stream, StringRef loggingName) { |
| 158 | + stream << loggingName << ": Old = " << *instruction << '\n' |
| 159 | + << " " |
| 160 | + << " New = " << *result << '\n'; |
| 161 | + }); |
| 162 | + |
| 163 | + // Everything uses the new instruction now. |
| 164 | + replaceInstUsesPairwiseWith(instruction, result); |
| 165 | + |
| 166 | + // Push the new instruction and any users onto the worklist. |
| 167 | + add(result); |
| 168 | + addUsersOfAllResultsToWorklist(result); |
| 169 | + |
| 170 | + eraseInstFromFunction(*instruction); |
| 171 | + |
| 172 | + return true; |
| 173 | + } else { |
| 174 | + withDebugStream([&](llvm::raw_ostream &stream, StringRef loggingName) { |
| 175 | + stream << loggingName << ": Mod = " << instructionDescription << '\n' |
| 176 | + << " " |
| 177 | + << " New = " << *instruction << '\n'; |
| 178 | + }); |
| 179 | + |
| 180 | + // If the instruction was modified, it's possible that it is now dead. |
| 181 | + // if so, remove it. |
| 182 | + if (isInstructionTriviallyDead(instruction)) { |
| 183 | + eraseInstFromFunction(*instruction); |
| 184 | + } else { |
| 185 | + add(instruction); |
| 186 | + addUsersOfAllResultsToWorklist(instruction); |
| 187 | + } |
| 188 | + return false; |
| 189 | + } |
| 190 | + } |
| 191 | + |
| 192 | + // Insert the instruction newInstruction before instruction old in old's |
| 193 | + // parent block. Add newInstruction to the worklist. |
| 194 | + SILInstruction *insertNewInstBefore(SILInstruction *newInstruction, |
| 195 | + SILInstruction &old) { |
| 196 | + assert(newInstruction && newInstruction->getParent() == nullptr && |
| 197 | + "newInstruction instruction already inserted into a basic block!"); |
| 198 | + SILBasicBlock *block = old.getParent(); |
| 199 | + block->insert(&old, newInstruction); // Insert inst |
| 200 | + add(newInstruction); |
| 201 | + return newInstruction; |
| 202 | + } |
| 203 | + |
| 204 | + // This method is to be used when an instruction is found to be dead, |
| 205 | + // replaceable with another preexisting expression. Here we add all uses of |
| 206 | + // instruction to the worklist, and replace all uses of instruction with the |
| 207 | + // new value. |
| 208 | + void replaceInstUsesWith(SingleValueInstruction &instruction, |
| 209 | + ValueBase *value) { |
| 210 | + addUsersToWorklist(&instruction); // Add all modified instrs to worklist. |
| 211 | + |
| 212 | + withDebugStream([&](llvm::raw_ostream &stream, StringRef loggingName) { |
| 213 | + stream << loggingName << ": Replacing " << instruction << '\n' |
| 214 | + << " " |
| 215 | + << " with " << *value << '\n'; |
| 216 | + }); |
| 217 | + |
| 218 | + instruction.replaceAllUsesWith(value); |
| 219 | + } |
| 220 | + |
| 221 | + // This method is to be used when a value is found to be dead, |
| 222 | + // replaceable with another preexisting expression. Here we add all |
| 223 | + // uses of oldValue to the worklist, replace all uses of oldValue |
| 224 | + // with newValue. |
| 225 | + void replaceValueUsesWith(SILValue oldValue, SILValue newValue) { |
| 226 | + addUsersToWorklist(oldValue); // Add all modified instrs to worklist. |
| 227 | + |
| 228 | + withDebugStream([&](llvm::raw_ostream &stream, StringRef loggingName) { |
| 229 | + stream << loggingName << ": Replacing " << oldValue << '\n' |
| 230 | + << " " |
| 231 | + << " with " << newValue << '\n'; |
| 232 | + }); |
| 233 | + |
| 234 | + oldValue->replaceAllUsesWith(newValue); |
| 235 | + } |
| 236 | + |
| 237 | + void replaceInstUsesPairwiseWith(SILInstruction *oldI, SILInstruction *newI) { |
| 238 | + withDebugStream([&](llvm::raw_ostream &stream, StringRef loggingName) { |
| 239 | + stream << loggingName << ": Replacing " << *oldI << '\n' |
| 240 | + << " " |
| 241 | + << " with " << *newI << '\n'; |
| 242 | + }); |
| 243 | + |
| 244 | + auto oldResults = oldI->getResults(); |
| 245 | + auto newResults = newI->getResults(); |
| 246 | + assert(oldResults.size() == newResults.size()); |
| 247 | + for (auto i : indices(oldResults)) { |
| 248 | + // Add all modified instrs to worklist. |
| 249 | + addUsersToWorklist(oldResults[i]); |
| 250 | + |
| 251 | + oldResults[i]->replaceAllUsesWith(newResults[i]); |
| 252 | + } |
| 253 | + } |
| 254 | + |
| 255 | + // Some instructions can never be "trivially dead" due to side effects or |
| 256 | + // producing a void value. In those cases, visit methods should use this |
| 257 | + // method to delete the given instruction. |
| 258 | + void eraseInstFromFunction(SILInstruction &instruction, |
| 259 | + SILBasicBlock::iterator &iterator, |
| 260 | + bool addOperandsToWorklist = true) { |
| 261 | + // Delete any debug users first. |
| 262 | + for (auto result : instruction.getResults()) { |
| 263 | + while (!result->use_empty()) { |
| 264 | + auto *user = result->use_begin()->getUser(); |
| 265 | + assert(user->isDebugInstruction()); |
| 266 | + if (iterator == user->getIterator()) |
| 267 | + ++iterator; |
| 268 | + erase(user); |
| 269 | + user->eraseFromParent(); |
| 270 | + } |
| 271 | + } |
| 272 | + if (iterator == instruction.getIterator()) |
| 273 | + ++iterator; |
| 274 | + |
| 275 | + eraseSingleInstFromFunction(instruction, addOperandsToWorklist); |
| 276 | + } |
| 277 | + |
| 278 | + void eraseInstFromFunction(SILInstruction &instruction, |
| 279 | + bool addOperandsToWorklist = true) { |
| 280 | + SILBasicBlock::iterator nullIter; |
| 281 | + return eraseInstFromFunction(instruction, nullIter, addOperandsToWorklist); |
| 282 | + } |
| 283 | + |
| 284 | + void eraseSingleInstFromFunction(SILInstruction &instruction, |
| 285 | + bool addOperandsToWorklist) { |
| 286 | + withDebugStream([&](llvm::raw_ostream &stream, StringRef loggingName) { |
| 287 | + stream << loggingName << ": ERASE " << instruction << '\n'; |
| 288 | + }); |
| 289 | + |
| 290 | + assert(!instruction.hasUsesOfAnyResult() && |
| 291 | + "Cannot erase instruction that is used!"); |
| 292 | + |
| 293 | + // Make sure that we reprocess all operands now that we reduced their |
| 294 | + // use counts. |
| 295 | + if (instruction.getNumOperands() < 8 && addOperandsToWorklist) { |
| 296 | + for (auto &operand : instruction.getAllOperands()) { |
| 297 | + if (auto *operandInstruction = |
| 298 | + operand.get()->getDefiningInstruction()) { |
| 299 | + withDebugStream([&](llvm::raw_ostream &stream, |
| 300 | + StringRef loggingName) { |
| 301 | + stream << loggingName << ": add op " << *operandInstruction << '\n' |
| 302 | + << " from erased inst to worklist\n"; |
| 303 | + }); |
| 304 | + add(operandInstruction); |
| 305 | + } |
| 306 | + } |
| 307 | + } |
| 308 | + erase(&instruction); |
| 309 | + instruction.eraseFromParent(); |
| 310 | + } |
129 | 311 | };
|
130 | 312 |
|
131 | 313 | // TODO: This name is somewhat unpleasant. Once the type is templated over its
|
|
0 commit comments