|
33 | 33 | #include "swift/SILOptimizer/PassManager/PassManager.h"
|
34 | 34 | #include "swift/SILOptimizer/PassManager/Transforms.h"
|
35 | 35 | #include "swift/SILOptimizer/Utils/CanonicalOSSALifetime.h"
|
| 36 | +#include "swift/SILOptimizer/Utils/CanonicalizeBorrowScope.h" |
36 | 37 | #include "swift/SILOptimizer/Utils/CanonicalizeInstruction.h"
|
37 | 38 | #include "swift/SILOptimizer/Utils/DebugOptUtils.h"
|
38 | 39 | #include "swift/SILOptimizer/Utils/InstOptUtils.h"
|
@@ -217,6 +218,88 @@ bool SILCombiner::trySinkOwnedForwardingInst(SingleValueInstruction *svi) {
|
217 | 218 | return true;
|
218 | 219 | }
|
219 | 220 |
|
| 221 | +/// Canonicalize each extended OSSA lifetime that contains an instruction newly |
| 222 | +/// created during this SILCombine iteration. |
| 223 | +void SILCombiner::canonicalizeOSSALifetimes() { |
| 224 | + if (!enableCopyPropagation || !Builder.hasOwnership()) |
| 225 | + return; |
| 226 | + |
| 227 | + SmallSetVector<SILValue, 16> defsToCanonicalize; |
| 228 | + for (auto *trackedInst : *Builder.getTrackingList()) { |
| 229 | + if (trackedInst->isDeleted()) |
| 230 | + continue; |
| 231 | + |
| 232 | + if (auto *cvi = dyn_cast<CopyValueInst>(trackedInst)) { |
| 233 | + SILValue def = CanonicalizeOSSALifetime::getCanonicalCopiedDef(cvi); |
| 234 | + |
| 235 | + // getCanonicalCopiedDef returns a copy whenever that the copy's source is |
| 236 | + // guaranteed. In that case, find the root of the borrowed lifetime. If it |
| 237 | + // is a function argument, then a simple guaranteed canonicalization can |
| 238 | + // be performed. Canonicalizing other borrow scopes is not handled by |
| 239 | + // SILCombine because it's not a single-lifetime canonicalization. |
| 240 | + // Instead, SILCombine treats a copy that uses a borrowed value as a |
| 241 | + // separate owned live range. Handling the compensation code across the |
| 242 | + // borrow scope boundary requires post processing in a particular order. |
| 243 | + // The copy propagation pass knows how to handle that. To avoid complexity |
| 244 | + // and ensure fast convergence, rewriting borrow scopes should not be |
| 245 | + // combined with other unrelated transformations. |
| 246 | + if (auto *copy = dyn_cast<CopyValueInst>(def)) { |
| 247 | + if (SILValue borrowDef = |
| 248 | + CanonicalizeBorrowScope::getCanonicalBorrowedDef( |
| 249 | + copy->getOperand())) { |
| 250 | + if (isa<SILFunctionArgument>(borrowDef)) { |
| 251 | + def = borrowDef; |
| 252 | + } |
| 253 | + } |
| 254 | + } |
| 255 | + defsToCanonicalize.insert(def); |
| 256 | + } |
| 257 | + } |
| 258 | + if (defsToCanonicalize.empty()) |
| 259 | + return; |
| 260 | + |
| 261 | + // Remove instructions deleted during canonicalization from SILCombine's |
| 262 | + // worklist. CanonicalizeOSSALifetime invalidates operands before invoking |
| 263 | + // the deletion callback. |
| 264 | + // |
| 265 | + // Note: a simpler approach would be to drain the Worklist before |
| 266 | + // canonicalizing OSSA, then callbacks could be completely removed from |
| 267 | + // CanonicalizeOSSALifetime. |
| 268 | + auto canonicalizeCallbacks = |
| 269 | + InstModCallbacks().onDelete([this](SILInstruction *instToDelete) { |
| 270 | + eraseInstFromFunction(*instToDelete, |
| 271 | + false /*do not add operands to the worklist*/); |
| 272 | + }); |
| 273 | + InstructionDeleter deleter(std::move(canonicalizeCallbacks)); |
| 274 | + |
| 275 | + DominanceInfo *domTree = DA->get(&Builder.getFunction()); |
| 276 | + CanonicalizeOSSALifetime canonicalizer( |
| 277 | + false /*prune debug*/, false /*poison refs*/, NLABA, domTree, deleter); |
| 278 | + CanonicalizeBorrowScope borrowCanonicalizer(deleter); |
| 279 | + |
| 280 | + while (!defsToCanonicalize.empty()) { |
| 281 | + SILValue def = defsToCanonicalize.pop_back_val(); |
| 282 | + if (auto functionArg = dyn_cast<SILFunctionArgument>(def)) { |
| 283 | + if (!borrowCanonicalizer.canonicalizeFunctionArgument(functionArg)) |
| 284 | + continue; |
| 285 | + } else if (!canonicalizer.canonicalizeValueLifetime(def)) { |
| 286 | + continue; |
| 287 | + } |
| 288 | + MadeChange = true; |
| 289 | + |
| 290 | + // Canonicalization may rewrite many copies and destroys within a single |
| 291 | + // extended lifetime, but the extended lifetime of each canonical def is |
| 292 | + // non-overlapping. If def was successfully canonicalized, simply add it |
| 293 | + // and its users to the SILCombine worklist. |
| 294 | + if (auto *inst = def->getDefiningInstruction()) { |
| 295 | + Worklist.add(inst); |
| 296 | + } |
| 297 | + for (auto *use : def->getUses()) { |
| 298 | + Worklist.add(use->getUser()); |
| 299 | + } |
| 300 | + } |
| 301 | +} |
| 302 | + |
220 | 303 | bool SILCombiner::doOneIteration(SILFunction &F, unsigned Iteration) {
|
221 | 304 | MadeChange = false;
|
222 | 305 |
|
@@ -302,50 +385,22 @@ bool SILCombiner::doOneIteration(SILFunction &F, unsigned Iteration) {
|
302 | 385 | MadeChange = true;
|
303 | 386 | }
|
304 | 387 |
|
305 |
| - // Our tracking list has been accumulating instructions created by the |
306 |
| - // SILBuilder during this iteration. In order to finish this round of |
307 |
| - // SILCombine, go through the tracking list and add its contents to the |
308 |
| - // worklist and then clear said list in preparation for the next |
309 |
| - // iteration. We canonicalize any copies that we created in order to |
310 |
| - // eliminate unnecessary copies introduced by RAUWing when ownership is |
311 |
| - // enabled. |
312 |
| - // |
313 |
| - // NOTE: It is ok if copy propagation results in MadeChanges being set to |
314 |
| - // true. This is because we only add elements to the tracking list if we |
315 |
| - // actually made a change to the IR, so MadeChanges should already be true |
316 |
| - // at this point. |
317 |
| - auto &TrackingList = *Builder.getTrackingList(); |
318 |
| - if (TrackingList.size() && Builder.hasOwnership()) { |
319 |
| - SmallSetVector<SILValue, 16> defsToCanonicalize; |
320 |
| - for (auto *trackedInst : TrackingList) { |
321 |
| - if (!trackedInst->isDeleted()) { |
322 |
| - if (auto *cvi = dyn_cast<CopyValueInst>(trackedInst)) { |
323 |
| - defsToCanonicalize.insert( |
324 |
| - CanonicalizeOSSALifetime::getCanonicalCopiedDef(cvi)); |
325 |
| - } |
326 |
| - } |
327 |
| - } |
328 |
| - if (defsToCanonicalize.size()) { |
329 |
| - CanonicalizeOSSALifetime canonicalizer( |
330 |
| - false /*prune debug*/, false /*canonicalize borrows*/, |
331 |
| - false /*poison refs*/, NLABA, DA, getInstModCallbacks()); |
332 |
| - auto analysisInvalidation = canonicalizeOSSALifetimes( |
333 |
| - canonicalizer, defsToCanonicalize.getArrayRef()); |
334 |
| - if (bool(analysisInvalidation)) { |
335 |
| - NLABA->lockInvalidation(); |
336 |
| - parentTransform->invalidateAnalysis(analysisInvalidation); |
337 |
| - NLABA->unlockInvalidation(); |
338 |
| - } |
339 |
| - } |
340 |
| - } |
341 |
| - for (SILInstruction *I : TrackingList) { |
| 388 | + // Eliminate copies created that this SILCombine iteration may have |
| 389 | + // introduced during OSSA-RAUW. |
| 390 | + canonicalizeOSSALifetimes(); |
| 391 | + |
| 392 | + // Builder's tracking list has been accumulating instructions created by the |
| 393 | + // during this SILCombine iteration. To finish this iteration, go through |
| 394 | + // the tracking list and add its contents to the worklist and then clear |
| 395 | + // said list in preparation for the next iteration. |
| 396 | + for (SILInstruction *I : *Builder.getTrackingList()) { |
342 | 397 | if (!I->isDeleted()) {
|
343 |
| - LLVM_DEBUG(llvm::dbgs() << "SC: add " << *I |
344 |
| - << " from tracking list to worklist\n"); |
| 398 | + LLVM_DEBUG(llvm::dbgs() |
| 399 | + << "SC: add " << *I << " from tracking list to worklist\n"); |
345 | 400 | Worklist.add(I);
|
346 | 401 | }
|
347 | 402 | }
|
348 |
| - TrackingList.clear(); |
| 403 | + Builder.getTrackingList()->clear(); |
349 | 404 | }
|
350 | 405 |
|
351 | 406 | Worklist.resetChecked();
|
@@ -448,12 +503,18 @@ class SILCombine : public SILFunctionTransform {
|
448 | 503 | auto *CHA = PM->getAnalysis<ClassHierarchyAnalysis>();
|
449 | 504 | auto *NLABA = PM->getAnalysis<NonLocalAccessBlockAnalysis>();
|
450 | 505 |
|
| 506 | + bool enableCopyPropagation = getOptions().EnableCopyPropagation; |
| 507 | + if (getOptions().EnableOSSAModules) { |
| 508 | + enableCopyPropagation = !getOptions().DisableCopyPropagation; |
| 509 | + } |
| 510 | + |
451 | 511 | SILOptFunctionBuilder FuncBuilder(*this);
|
452 | 512 | // Create a SILBuilder with a tracking list for newly added
|
453 | 513 | // instructions, which we will periodically move to our worklist.
|
454 | 514 | SILBuilder B(*getFunction(), &TrackingList);
|
455 | 515 | SILCombiner Combiner(this, FuncBuilder, B, AA, DA, PCA, CHA, NLABA,
|
456 |
| - getOptions().RemoveRuntimeAsserts); |
| 516 | + getOptions().RemoveRuntimeAsserts, |
| 517 | + enableCopyPropagation); |
457 | 518 | bool Changed = Combiner.runOnFunction(*getFunction());
|
458 | 519 | assert(TrackingList.empty() &&
|
459 | 520 | "TrackingList should be fully processed by SILCombiner");
|
|
0 commit comments