|
76 | 76 | #include "swift/Basic/OptimizationMode.h"
|
77 | 77 | #include "swift/Demangling/Demangle.h"
|
78 | 78 | #include "swift/Demangling/Demangler.h"
|
| 79 | +#include "swift/SIL/BasicBlockUtils.h" |
| 80 | +#include "swift/SIL/CFG.h" |
79 | 81 | #include "swift/SIL/InstructionUtils.h"
|
80 | 82 | #include "swift/SIL/SILBasicBlock.h"
|
81 | 83 | #include "swift/SIL/SILBuilder.h"
|
|
86 | 88 | #include "swift/SIL/SILModule.h"
|
87 | 89 | #include "swift/SILOptimizer/PassManager/Passes.h"
|
88 | 90 | #include "swift/SILOptimizer/PassManager/Transforms.h"
|
| 91 | +#include "swift/SILOptimizer/Utils/CFGOptUtils.h" |
89 | 92 | #include "swift/SILOptimizer/Utils/ConstExpr.h"
|
90 | 93 | #include "swift/SILOptimizer/Utils/InstOptUtils.h"
|
91 | 94 | #include "swift/SILOptimizer/Utils/SILInliner.h"
|
92 | 95 | #include "swift/SILOptimizer/Utils/SILOptFunctionBuilder.h"
|
93 |
| -#include "llvm/ADT/MapVector.h" |
94 |
| -#include "swift/SIL/BasicBlockUtils.h" |
95 |
| -#include "swift/SIL/CFG.h" |
| 96 | +#include "swift/SILOptimizer/Utils/ValueLifetime.h" |
96 | 97 | #include "llvm/ADT/BreadthFirstIterator.h"
|
| 98 | +#include "llvm/ADT/MapVector.h" |
97 | 99 |
|
98 | 100 | using namespace swift;
|
99 | 101 |
|
@@ -177,10 +179,7 @@ class FoldState {
|
177 | 179 | /// Instruction from where folding must begin.
|
178 | 180 | SILInstruction *beginInstruction;
|
179 | 181 |
|
180 |
| - /// Instructions that mark the end points of folding. No folded SIL value must |
181 |
| - /// be usable beyond these instructions (in the control-flow order). These |
182 |
| - /// instructions are also used to emit destory instructions for non-trivial, |
183 |
| - /// SIL values emitted during folding. |
| 182 | + /// Instructions that mark the end points of constant evaluation. |
184 | 183 | SmallSetVector<SILInstruction *, 2> endInstructions;
|
185 | 184 |
|
186 | 185 | private:
|
@@ -292,8 +291,14 @@ static bool isSILValueFoldable(SILValue value) {
|
292 | 291 | ASTContext &astContext = definingInst->getFunction()->getASTContext();
|
293 | 292 | SILType silType = value->getType();
|
294 | 293 |
|
| 294 | + // Fold only SIL values of integer or string type that are not one of the |
| 295 | + // following: addresses, literals, instructions marking ownership access and |
| 296 | + // scope, copy_value (as its operand will be folded), struct creations, or |
| 297 | + // call to string literal initializer. |
295 | 298 | return (!silType.isAddress() && !isa<LiteralInst>(definingInst) &&
|
296 |
| - !isa<StructInst>(definingInst) && |
| 299 | + !isa<LoadBorrowInst>(definingInst) && |
| 300 | + !isa<BeginBorrowInst>(definingInst) && |
| 301 | + !isa<CopyValueInst>(definingInst) && !isa<StructInst>(definingInst) && |
297 | 302 | !getStringMakeUTF8Init(definingInst) &&
|
298 | 303 | isIntegerOrStringType(silType, astContext));
|
299 | 304 | }
|
@@ -428,120 +433,181 @@ static SILValue emitCodeForSymbolicValue(SymbolicValue symVal,
|
428 | 433 | }
|
429 | 434 | }
|
430 | 435 |
|
431 |
| -/// Collect the end-of-lifetime instructions of the given SILValue. These are |
432 |
| -/// either release_value or destroy_value instructions. |
433 |
| -/// \param value SIL value whose end-of-lifetime instructions must be collected. |
434 |
| -/// \param lifetimeEndInsts buffer for storing the found end-of-lifetime |
435 |
| -/// instructions of 'value'. |
436 |
| -static void getLifetimeEndInstructionsOfSILValue( |
437 |
| - SILValue value, SmallVectorImpl<SILInstruction *> &lifetimeEndInsts) { |
438 |
| - |
439 |
| - bool continueLifetimeEndInstructionSearch = true; |
440 |
| - SILValue currValue = value; |
441 |
| - |
442 |
| - while (continueLifetimeEndInstructionSearch) { |
443 |
| - continueLifetimeEndInstructionSearch = false; |
444 |
| - |
445 |
| - for (Operand *use : currValue->getUses()) { |
| 436 | +/// Collect the end points of the instructions that are data dependent on \c |
| 437 | +/// value. A instruction is data dependent on \c value if its result may |
| 438 | +/// transitively depends on \c value. Note that data dependencies through |
| 439 | +/// addresses are not tracked by this function. |
| 440 | +/// |
| 441 | +/// \param value SILValue that is not an address. |
| 442 | +/// \param fun SILFunction that defines \c value. |
| 443 | +/// \param endUsers buffer for storing the found end points of the data |
| 444 | +/// dependence chain. |
| 445 | +static void |
| 446 | +getEndPointsOfDataDependentChain(SILValue value, SILFunction *fun, |
| 447 | + SmallVectorImpl<SILInstruction *> &endUsers) { |
| 448 | + assert(!value->getType().isAddress()); |
| 449 | + |
| 450 | + // Collect the instructions that are data dependent on the value using a |
| 451 | + // fix point iteration. |
| 452 | + SmallPtrSet<SILInstruction *, 16> visitedUsers; |
| 453 | + SmallVector<SILValue, 16> worklist; |
| 454 | + worklist.push_back(value); |
| 455 | + |
| 456 | + while (!worklist.empty()) { |
| 457 | + SILValue currVal = worklist.pop_back_val(); |
| 458 | + for (Operand *use : currVal->getUses()) { |
446 | 459 | SILInstruction *user = use->getUser();
|
447 |
| - |
448 |
| - if (isa<ReleaseValueInst>(user) || isa<DestroyValueInst>(user)) { |
449 |
| - lifetimeEndInsts.push_back(user); |
| 460 | + if (visitedUsers.count(user)) |
450 | 461 | continue;
|
451 |
| - } |
452 |
| - |
453 |
| - if (isa<CopyValueInst>(user)) { |
454 |
| - auto *copyValueInst = cast<CopyValueInst>(user); |
455 |
| - // Continue looking for the end-of-lifetime instruction for the |
456 |
| - // result of copy_value. |
457 |
| - currValue = copyValueInst; |
458 |
| - continueLifetimeEndInstructionSearch = true; |
459 |
| - } |
| 462 | + visitedUsers.insert(user); |
| 463 | + llvm::copy(user->getResults(), std::back_inserter(worklist)); |
460 | 464 | }
|
461 | 465 | }
|
462 |
| -} |
463 | 466 |
|
464 |
| -/// Emit instructions to destroy the folded value at the end of its use, if |
465 |
| -/// required. Since this pass folds only integers or strings and since the |
466 |
| -/// former is a trivial type, we only have to destroy strings that are folded. |
467 |
| -/// For strings, a release_value (or a destory_value instruction in ownership |
468 |
| -/// SIL) has to be emitted if it is not already present. |
469 |
| -static void |
470 |
| -destroyFoldedValueAtEndOfUse(SILValue foldedVal, SILValue originalVal, |
471 |
| - ArrayRef<SILInstruction *> endOfUseInsts, |
472 |
| - SILFunction *fun) { |
473 |
| - // Folded value should have either trivial or owned ownership as it is an |
474 |
| - // integer or string constant. |
475 |
| - assert(foldedVal.getOwnershipKind() == ValueOwnershipKind::None || |
476 |
| - foldedVal.getOwnershipKind() == ValueOwnershipKind::Owned); |
477 |
| - |
478 |
| - // If the ownership kinds of folded and original values are both either |
479 |
| - // owned or trivial, there is nothing to do. |
480 |
| - if (foldedVal.getOwnershipKind() == originalVal.getOwnershipKind()) { |
| 467 | + // At this point, visitedUsers have all the transitive, data-dependent uses. |
| 468 | + // Compute the lifetime frontier of all the uses which are the instructions |
| 469 | + // following the last uses. Every exit from the last uses will have a |
| 470 | + // lifetime frontier. |
| 471 | + SILInstruction *valueDefinition = value->getDefiningInstruction(); |
| 472 | + SILInstruction *def = |
| 473 | + valueDefinition ? valueDefinition : &(value->getParentBlock()->front()); |
| 474 | + ValueLifetimeAnalysis lifetimeAnalysis = |
| 475 | + ValueLifetimeAnalysis(def, SmallVector<SILInstruction *, 16>( |
| 476 | + visitedUsers.begin(), visitedUsers.end())); |
| 477 | + ValueLifetimeAnalysis::Frontier frontier; |
| 478 | + bool hasCriticlEdges = lifetimeAnalysis.computeFrontier( |
| 479 | + frontier, ValueLifetimeAnalysis::DontModifyCFG); |
| 480 | + endUsers.append(frontier.begin(), frontier.end()); |
| 481 | + if (!hasCriticlEdges) |
481 | 482 | return;
|
| 483 | + // If there are some lifetime frontiers on the critical edges, take the |
| 484 | + // first instruction of the target of the critical edge as the frontier. This |
| 485 | + // will suffice as every exit from the visitedUsers must go through one of |
| 486 | + // them. |
| 487 | + for (auto edgeIndexPair : lifetimeAnalysis.getCriticalEdges()) { |
| 488 | + SILBasicBlock *targetBB = |
| 489 | + edgeIndexPair.first->getSuccessors()[edgeIndexPair.second]; |
| 490 | + endUsers.push_back(&targetBB->front()); |
482 | 491 | }
|
483 |
| - assert(originalVal.getOwnershipKind() == ValueOwnershipKind::Guaranteed); |
| 492 | +} |
484 | 493 |
|
485 |
| - // Here, the original value may be at +0 and hence may not be released. |
486 |
| - // However, the folded value should always be released. |
487 |
| - SmallVector<SILInstruction *, 2> lifeTimeEndInstsOfOriginal; |
488 |
| - getLifetimeEndInstructionsOfSILValue(originalVal, lifeTimeEndInstsOfOriginal); |
| 494 | +/// Given an instruction \p inst, invoke the given clean-up function \p cleanup |
| 495 | +/// on its lifetime frontier, which are instructions that follow the last use of |
| 496 | +/// the results of \c inst. E.g. the clean-up function could destory/release |
| 497 | +/// the function result. |
| 498 | +static void |
| 499 | +cleanupAtEndOfLifetime(SILInstruction *inst, |
| 500 | + llvm::function_ref<void(SILInstruction *)> cleanup) { |
| 501 | + ValueLifetimeAnalysis lifetimeAnalysis = ValueLifetimeAnalysis(inst); |
| 502 | + ValueLifetimeAnalysis::Frontier frontier; |
| 503 | + (void)lifetimeAnalysis.computeFrontier( |
| 504 | + frontier, ValueLifetimeAnalysis::AllowToModifyCFG); |
| 505 | + for (SILInstruction *lifetimeEndInst : frontier) { |
| 506 | + cleanup(lifetimeEndInst); |
| 507 | + } |
| 508 | +} |
489 | 509 |
|
490 |
| - if (!lifeTimeEndInstsOfOriginal.empty()) { |
491 |
| - // Here, the original value is released, and so would be the folded value. |
| 510 | +/// Replace all uses of \c originalVal by \c foldedVal and adjust lifetimes of |
| 511 | +/// original and folded values by emitting required destory/release instructions |
| 512 | +/// at the right places. Note that this function does not remove any |
| 513 | +/// instruction. |
| 514 | +/// |
| 515 | +/// \param originalVal the SIL value that is replaced. |
| 516 | +/// \param foldedVal the SIL value that replaces the \c originalVal. |
| 517 | +/// \param fun the SIL function containing the \c foldedVal and \c originalVal |
| 518 | +static void replaceAllUsesAndFixLifetimes(SILValue foldedVal, |
| 519 | + SILValue originalVal, |
| 520 | + SILFunction *fun) { |
| 521 | + SILInstruction *originalInst = originalVal->getDefiningInstruction(); |
| 522 | + SILInstruction *foldedInst = foldedVal->getDefiningInstruction(); |
| 523 | + assert(originalInst && |
| 524 | + "cannot constant fold function or basic block parameter"); |
| 525 | + assert(!isa<TermInst>(originalInst) && |
| 526 | + "cannot constant fold a terminator instruction"); |
| 527 | + assert(foldedInst && "constant value does not have a defining instruction"); |
| 528 | + |
| 529 | + // First, replace all uses of originalVal by foldedVal, and then adjust their |
| 530 | + // lifetimes if necessary. |
| 531 | + originalVal->replaceAllUsesWith(foldedVal); |
| 532 | + |
| 533 | + if (originalVal->getType().isTrivial(*fun)) { |
| 534 | + assert(foldedVal->getType().isTrivial(*fun)); |
| 535 | + return; |
| 536 | + } |
| 537 | + assert(!foldedVal->getType().isTrivial(*fun)); |
| 538 | + |
| 539 | + if (!fun->hasOwnership()) { |
| 540 | + // In non-ownership SIL, handle only folding of struct_extract instruction, |
| 541 | + // which is the only important instruction that should be folded by this |
| 542 | + // pass. Note that folding an arbitrary instruction in non-ownership SIL |
| 543 | + // makes updating reference counts of the original value much harder and |
| 544 | + // error prone. |
| 545 | + // TODO: this code can be safely removed once ownership SIL becomes the |
| 546 | + // default SIL this pass works on. |
| 547 | + assert(isa<StructExtractInst>(originalInst)); |
| 548 | + cleanupAtEndOfLifetime(foldedInst, [&](SILInstruction *lifetimeEndInst) { |
| 549 | + SILBuilderWithScope builder(lifetimeEndInst); |
| 550 | + builder.emitReleaseValue(lifetimeEndInst->getLoc(), foldedVal); |
| 551 | + }); |
492 | 552 | return;
|
493 | 553 | }
|
494 | 554 |
|
495 |
| - // Here, the original value is not released. Release the folded value at the |
496 |
| - // 'endOfUse' instructions passed as parameter. |
497 |
| - bool hasOwnership = fun->hasOwnership(); |
498 |
| - for (SILInstruction *endInst : endOfUseInsts) { |
499 |
| - SILBuilderWithScope builder(endInst); |
500 |
| - if (hasOwnership) { |
501 |
| - builder.createDestroyValue(endInst->getLoc(), foldedVal); |
502 |
| - } else { |
503 |
| - builder.createReleaseValue(endInst->getLoc(), foldedVal, |
504 |
| - builder.getDefaultAtomicity()); |
505 |
| - } |
| 555 | + assert(foldedVal.getOwnershipKind() == ValueOwnershipKind::Owned && |
| 556 | + "constant value must have owned ownership kind"); |
| 557 | + |
| 558 | + if (originalVal.getOwnershipKind() == ValueOwnershipKind::Owned) { |
| 559 | + // Destroy originalVal, which is now unused, immediately after its |
| 560 | + // definition. Note that originalVal's destorys are now transferred to |
| 561 | + // foldedVal. |
| 562 | + SILInstruction *insertionPoint = &(*std::next(originalInst->getIterator())); |
| 563 | + SILBuilderWithScope builder(insertionPoint); |
| 564 | + SILLocation loc = insertionPoint->getLoc(); |
| 565 | + builder.emitDestroyValueOperation(loc, originalVal); |
| 566 | + return; |
506 | 567 | }
|
| 568 | + |
| 569 | + // Here, originalVal is not owned. Hence, destroy foldedVal at the end of its |
| 570 | + // lifetime. |
| 571 | + cleanupAtEndOfLifetime(foldedInst, [&](SILInstruction *lifetimeEndInst) { |
| 572 | + SILBuilderWithScope builder(lifetimeEndInst); |
| 573 | + builder.emitDestroyValueOperation(lifetimeEndInst->getLoc(), foldedVal); |
| 574 | + }); |
| 575 | + return; |
507 | 576 | }
|
508 | 577 |
|
509 | 578 | /// Given a fold state with constant-valued instructions, substitute the
|
510 | 579 | /// instructions with the constant values. The constant values could be strings
|
511 | 580 | /// or Stdlib integer-struct values or builtin integers.
|
512 | 581 | static void substituteConstants(FoldState &foldState) {
|
513 |
| - |
514 | 582 | ConstExprStepEvaluator &evaluator = foldState.constantEvaluator;
|
515 |
| - SmallVector<SILInstruction *, 4> deletedInsts; |
516 |
| - auto endOfUseInsts = ArrayRef<SILInstruction *>( |
517 |
| - foldState.endInstructions.begin(), foldState.endInstructions.end()); |
| 583 | + // Instructions that are possibly dead since their results are folded. |
| 584 | + SmallVector<SILInstruction *, 4> possiblyDeadInsts; |
518 | 585 |
|
519 | 586 | for (SILValue constantSILValue : foldState.getConstantSILValues()) {
|
520 | 587 | SymbolicValue constantSymbolicVal =
|
521 | 588 | evaluator.lookupConstValue(constantSILValue).getValue();
|
522 | 589 |
|
523 | 590 | SILInstruction *definingInst = constantSILValue->getDefiningInstruction();
|
524 | 591 | assert(definingInst);
|
| 592 | + SILFunction *fun = definingInst->getFunction(); |
| 593 | + |
| 594 | + // Do not attempt to fold anything but struct_extract in non-OSSA. |
| 595 | + // TODO: this condition should be removed once migration OSSA is complete. |
| 596 | + if (!fun->hasOwnership() && !isa<StructExtractInst>(definingInst)) |
| 597 | + continue; |
525 | 598 |
|
526 | 599 | SILBuilderWithScope builder(definingInst);
|
527 | 600 | SILLocation loc = definingInst->getLoc();
|
528 | 601 | SILType instType = constantSILValue->getType();
|
529 | 602 | SILValue foldedSILVal = emitCodeForSymbolicValue(
|
530 | 603 | constantSymbolicVal, instType, builder, loc, foldState.stringInfo);
|
531 | 604 |
|
532 |
| - // Add an instruction to end the lifetime of the foldedSILVal, if necessary. |
533 |
| - destroyFoldedValueAtEndOfUse(foldedSILVal, constantSILValue, endOfUseInsts, |
534 |
| - definingInst->getFunction()); |
535 |
| - |
536 |
| - constantSILValue->replaceAllUsesWith(foldedSILVal); |
537 |
| - |
538 |
| - if (isa<SingleValueInstruction>(definingInst)) { |
539 |
| - deletedInsts.push_back(definingInst); |
540 |
| - } // Otherwise, be conservative and do not delete the instruction as other |
541 |
| - // results of the instruction could be used. |
| 605 | + // Replace constantSILValue with foldedSILVal and adjust the lifetime and |
| 606 | + // ownership of the values appropriately. |
| 607 | + replaceAllUsesAndFixLifetimes(foldedSILVal, constantSILValue, fun); |
| 608 | + possiblyDeadInsts.push_back(definingInst); |
542 | 609 | }
|
543 |
| - |
544 |
| - recursivelyDeleteTriviallyDeadInstructions(deletedInsts, true, |
| 610 | + recursivelyDeleteTriviallyDeadInstructions(possiblyDeadInsts, /*force*/ false, |
545 | 611 | [&](SILInstruction *DeadI) {});
|
546 | 612 | }
|
547 | 613 |
|
@@ -581,8 +647,10 @@ static bool detectAndDiagnoseErrors(Optional<SymbolicValue> errorInfo,
|
581 | 647 | return true;
|
582 | 648 | }
|
583 | 649 |
|
| 650 | + // The first (and only) property of OSLogMessage is the OSLogInterpolation |
| 651 | + // instance. |
584 | 652 | SymbolicValue osLogInterpolationValue =
|
585 |
| - osLogMessageValueOpt->lookThroughSingleElementAggregates(); |
| 653 | + osLogMessageValueOpt->getAggregateValue()[0]; |
586 | 654 | if (!osLogInterpolationValue.isConstant()) {
|
587 | 655 | diagnose(astContext, sourceLoc, diag::oslog_non_constant_interpolation);
|
588 | 656 | return true;
|
@@ -633,12 +701,14 @@ static bool detectAndDiagnoseErrors(Optional<SymbolicValue> errorInfo,
|
633 | 701 | static void constantFold(SILInstruction *start,
|
634 | 702 | SingleValueInstruction *oslogMessage,
|
635 | 703 | unsigned assertConfig) {
|
| 704 | + SILFunction *fun = start->getFunction(); |
636 | 705 |
|
637 | 706 | // Initialize fold state.
|
638 |
| - SmallVector<SILInstruction *, 2> lifetimeEndInsts; |
639 |
| - getLifetimeEndInstructionsOfSILValue(oslogMessage, lifetimeEndInsts); |
| 707 | + SmallVector<SILInstruction *, 2> endUsersOfOSLogMessage; |
| 708 | + getEndPointsOfDataDependentChain(oslogMessage, fun, endUsersOfOSLogMessage); |
| 709 | + assert(!endUsersOfOSLogMessage.empty()); |
640 | 710 |
|
641 |
| - FoldState state(start->getFunction(), assertConfig, start, lifetimeEndInsts); |
| 711 | + FoldState state(fun, assertConfig, start, endUsersOfOSLogMessage); |
642 | 712 |
|
643 | 713 | auto errorInfo = collectConstants(state);
|
644 | 714 |
|
|
0 commit comments