File: LexicalDestroyFolding.cpp

package info (click to toggle)
swiftlang 6.1.3-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 2,791,532 kB
  • sloc: cpp: 9,901,743; ansic: 2,201,431; asm: 1,091,827; python: 308,252; objc: 82,166; f90: 80,126; lisp: 38,358; pascal: 25,559; sh: 20,429; ml: 5,058; perl: 4,745; makefile: 4,484; awk: 3,535; javascript: 3,018; xml: 918; fortran: 664; cs: 573; ruby: 396
file content (824 lines) | stat: -rw-r--r-- 28,968 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
//===- LexicalDestroyFolding.cpp - Fold destroys into final owned applies -===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2022 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
/// After ShrinkBorrowScope and CanonicalizeOSSALifetime both run, when a final
/// use of the extended simple lifetime of a begin_borrow [lexical] is as an
/// owned argument, we will have the following pattern:
///
/// %result = apply %fn(..., %copy, ...) : $... (..., @owned Ty, ...)
/// end_borrow %lifetime : $Ty
/// destroy_value %dvi
///
/// where %lifetime is the result of our begin_borrow [lexical]:
///
/// %lifetime = begin_borrow [lexical] %borrowee : $Ty
///
/// and %copy is a (transitive) copy of it
///
/// %copy = copy_value (copy_value (... %lifetime))
///
/// At that point, we want to fold the destroy_value, apply, and borrow into
///
/// end_borrow %lifetime : $Ty
/// apply %fn(..., %move, ...) : $... (..., @owned Ty, ...)
///
/// where %move is a the result of a new instruction added above our scope:
///
/// %move = move_value [lexical] %borrowee : $Ty
/// %lifetime = begin_borrow %move : $Ty
///
/// We only want to do this when it would mean allowing the callee to end a
/// lifetime of a value that the caller owned--if we can transfer ownership from
/// the caller to the callee.  In order to transfer ownership, the caller must
/// first own the value, so we are only interested in borrows of owned values:
///
/// %borrowee : @owned $Ty
/// %lifetime = begin_borrow [lexical] %borrowee
///
/// At the other end, we can only transfer ownership if there are no more
/// interesting uses of the owned value after the apply.  Specifically, we
/// require that the instruction after the end_borrow be a destroy_value of the
/// borrowee.
///
/// The simplest example:
///
/// %copy = copy_value %lifetime : $Ty
/// %result = apply %fn(..., %copy, ...) : $... (..., @owned Ty, ...)
/// end_borrow %lifetime : $Ty
/// destroy_value %borrowee : $Ty
///
/// Taken together, we get the simplest example of this transformation:
///
/// INPUT:
///
///     %borrowee : @owned
///     %lifetime = begin_borrow [lexical] %owned
///     %copy = copy_value %lifetime
///     apply %fn(%copy) : $... (@owned)
///     end_borrow %lifetime
///     destroy_value %borrowee
///
/// OUTPUT:
///
///     %borrowee : @owned
///     %move = move_value [lexical] %borrowee
///     %lifetime = begin_borrow [lexical] %move
///     end_borrow %lifetime
///     apply %fn(%move) : $... (@owned)
///
/// TODO: Handle partial_apply, try_apply, and begin_apply.
//===----------------------------------------------------------------------===//

#include "swift/Basic/Assertions.h"
#include "swift/SIL/BasicBlockDatastructures.h"
#include "swift/SIL/BasicBlockUtils.h"
#include "swift/SIL/OwnershipUtils.h"
#include "swift/SIL/PrunedLiveness.h"
#include "swift/SIL/SILBasicBlock.h"
#include "swift/SIL/SILBuilder.h"
#include "swift/SIL/SILInstruction.h"
#include "swift/SIL/SILNode.h"
#include "swift/SIL/Test.h"
#include "swift/SILOptimizer/Analysis/Reachability.h"
#include "swift/SILOptimizer/PassManager/Transforms.h"
#include "swift/SILOptimizer/Utils/CanonicalizeBorrowScope.h"
#include "swift/SILOptimizer/Utils/CanonicalizeOSSALifetime.h"
#include "swift/SILOptimizer/Utils/InstructionDeleter.h"
#include "swift/SILOptimizer/Utils/SILSSAUpdater.h"
#include "llvm/ADT/SmallVector.h"

#define DEBUG_TYPE "copy-propagation"

using namespace swift;

//===----------------------------------------------------------------------===//
//                       MARK: LexicalDestroyFolding
//===----------------------------------------------------------------------===//

namespace LexicalDestroyFolding {

/// The environment within which to fold.
struct Context final {
  /// The instruction that begins the borrow scope.
  BeginBorrowInst *const introducer;

  /// The function containing the introducer.
  ///
  /// introducer->getFunction()
  SILFunction *const function;

  /// BorrowedValue(introducer)
  BorrowedValue const borrowedValue;

  /// The value whose lifetime is guaranteed by the lexical borrow scope.
  ///
  /// introducer->getOperand()
  SILValue const borrowee;

  DominanceInfo &dominanceTree;

  InstructionDeleter &deleter;

public:
  Context(BeginBorrowInst *introducer, DominanceInfo &dominanceTree,
          InstructionDeleter &deleter)
      : introducer(introducer), function(introducer->getFunction()),
        borrowedValue(BorrowedValue(introducer)),
        borrowee(introducer->getOperand()), dominanceTree(dominanceTree),
        deleter(deleter) {
    assert(introducer->isLexical());
    assert(introducer->getOperand()->getOwnershipKind() ==
           OwnershipKind::Owned);
    assert(borrowedValue.isLocalScope());
  }
};

/// Fold within the specified context.
MoveValueInst *run(Context &);

/// The consuming use pattern we are trying to match and transform.
struct Match final {
  ApplyInst *ai = nullptr;
  EndBorrowInst *ebi = nullptr;
  DestroyValueInst *dvi = nullptr;

  /// Whether the match is a candidate for folding.
  ///
  /// A partial match--which has both the end_borrow and the destroy value
  /// but no apply--cannot itself be folded but is not an obstruction to
  /// folding other candidates.
  bool isFullMatch() {
    assert(ebi != nullptr);
    assert(dvi != nullptr);
    return ai != nullptr;
  }
};

/// A sequence of instructions under consideration for folding
struct Candidate final {
  /// The instruction sequence itself.
  Match match;
  /// Whether the candidate could indeed be folded as determined by
  /// isViableMatch.
  bool viable;
  /// The indices of the arguments of the apply to rewrite as determined by
  /// rewritableArgumentIndicesForApply.
  SmallVector<int, 2> argumentIndices;
};

/// The degree to which a match is a candidate for folding.
enum class MatchViability {
  /// This match can be folded, supposing any can.
  Viable,
  /// This match cannot be folded, even if others can.
  Nonviable,
  /// Neither this match nor any other can be folded.
  Illegal
};

struct Candidates final {
  /// The sequences of scope ending instructions that are under consideration
  /// for folding.
  llvm::SmallVector<Candidate, 4> vector;
};

/// Quickly filter scope ends of %lifetime that MIGHT BE foldable.
class FindCandidates final {

  Context const &context;

public:
  FindCandidates(Context const &context) : context(context) {}

  /// Find among the scope ending instructions of %lifetime any that match the
  /// expected instruction sequence pattern.
  ///
  /// Uses definesMatchingInstructionSequence to determine whether an
  /// instruction is a candidate for folding.
  ///
  /// Includes both full matches which could potentially be folded and partial
  /// matches which need to be verified later aren't illegal in isViableMatch.
  ///
  /// returns true if any full matches are found
  ///         false otherwise
  bool run(Candidates &);

private:
  /// Fast check for whether the given instruction is a candidate for folding.
  ///
  /// Tries to find patterns like
  ///
  ///     apply
  ///     end_borrow
  ///     ...           // instructions which CanonicalizeOSSALifetime will not
  ///                   // hoist destroys over
  ///     destroy_value %borrowee
  ///
  /// If None is returned, we can't do the transformation at any end_borrow.
  /// If a Match WITHOUT an apply is returned, we can't do the transformation
  /// on the provided instruction but we still might be able to do it on the
  /// other scope ending instructions.
  /// If a Match WITH an apply is returned, we might be able to transform this
  /// instruction, so more expensive checks are in order.
  std::optional<Match>
  definesMatchingInstructionSequence(SILInstruction *) const;

  /// Whether the specified instruction is or might be the beginning of a
  /// sequence of "inconsequential" instructions the last of which destroys
  /// %borrowee.
  ///
  /// CanonicalizeOSSALifetime will put destroy_value instructions after every
  /// final non-consuming use of %borrowee.  So it will put a
  /// destroy_value after an
  ///     end_borrow %lifetime
  /// if there are no subsequent uses of %borrowee.  However, if there
  /// is already one or more other instructions whose opcodes satisfy
  /// CanonicalizeOSSALifetime::ignoredByDestroyHoisting just after
  ///     end_borrow %lifetime,
  /// it won't hoist the
  ///     destroy_value %borrowee
  /// over them.
  ///
  /// Consequently, it's not good enough just to look at the instruction
  /// immediately following
  ///     end_borrow %lifetime
  /// Instead, we need to look over the sequence of them.
  ///
  /// To be certain that the sequence is valid, we need to check that none of
  /// the instructions between the end_borrow and the destroy_value are users of
  /// %borrowee.  That requires having a set of uses to check for membership
  /// in, though, so that is postponed until isViableMatch.
  ///
  /// returns the destroy_value instruction in the sequence that destroys
  ///         %borrowee or nullptr if there isn't one
  DestroyValueInst *findNextBorroweeDestroy(SILInstruction *) const;
};

/// How %lifetime is used.
struct IntroducerUsage final {
  /// The operands that are uses of introducer.
  SmallPtrSet<Operand *, 16> uses;
  /// The instructions that are users of the simple extended borrow scope.
  SmallPtrSet<SILInstruction *, 16> users;
};

/// Identifies all the simple extended users of %lifetime.
///
/// returns true if none of the uses of %lifetime escaped
///         false otherwise
bool findIntroducerUsage(Context const &, IntroducerUsage &);

/// How %borrowee is used.
struct BorroweeUsage final {
  /// The operands that are uses of the borrowee.
  SmallVector<Operand *, 16> uses;
  /// The instructions that are users of the borrowee.
  ///
  /// A set of the users of uses for fast membership checking.
  SmallPtrSet<SILInstruction *, 16> users;
};

/// Find all uses of %borrowee that are dominated by introducer.
///
/// We are only interested in those dominated by introducer because those are
/// the uses all of which must be "outside" the liveness boundary of
/// %lifetime.  In detail, PrunedLiveness::isWithinBoundary relies on
/// clients to know that instructions are after the start of liveness.  We
/// determine this via the dominance tree.
bool findBorroweeUsage(Context const &, BorroweeUsage &);

/// Sift scope ends of %lifetime for those that CAN be folded.
class FilterCandidates final {

  Context const &context;
  IntroducerUsage const &introducerUsage;
  BorroweeUsage const &borroweeUsage;

public:
  FilterCandidates(Context const &context,
                   IntroducerUsage const &introducerUsage,
                   BorroweeUsage const &borroweeUsage)
      : context(context), introducerUsage(introducerUsage),
        borroweeUsage(borroweeUsage){};

  /// Determines whether each candidate is viable for folding.
  ///
  /// returns true if any candidates were viable
  ///         false otherwise
  bool run(Candidates &candidates);

private:
  /// Slow check, dependent on finding users, that a Match found by
  /// definesMatchingInstructionSequence can be folded.
  MatchViability isViableMatch(Match &, SmallVectorImpl<int> &) const;

  /// Find the arguments in the specified apply that could be rewritten.
  bool rewritableArgumentIndicesForApply(ApplySite,
                                         SmallVectorImpl<int> &indices) const;

  /// Whether the specified value is %lifetime or its iterated copy_value.
  ///
  /// In other words, it has to be a simple extended def of %lifetime.
  bool isSimpleExtendedIntroducerDef(SILValue value) const;
};

/// Whether there are any uses of the borrowee within the borrow scope.
///
/// If there are, we can't fold the apply.  Specifically, we can't introduce
/// a move_value [lexical] %borrowee because that value still needs to be used
/// in those locations.
///
/// For example, given the following SIL
///     %borrowee : @owned
///     %lifetime = begin_borrow [lexical] %borrowee
///     apply %take_guaranteed(%borrowee)
///     %copy = copy_value %lifetime
///     apply %take_owned(%copy)
///     end_borrow %lifetime
///     destroy_value %borrowee
/// we can't rewrite like
///     %borrowee : @owned
///     %move = move_value [lexical] %borrowee
///     %lifetime = begin_borrow [lexical] %move
///     apply %take_guaranteed(??????)
///     apply %take_owned(%move)
/// because there is no appropriate value to pass to %take_guaranteed.
/// Specifically, it's not legal to use %move there because that would make
/// the instruction a user of the lexical scope which it was not before.
bool borroweeHasUsesWithinBorrowScope(Context const &, BorroweeUsage const &);

/// Rewrite the appropriate scope ends of %lifetime.
class Rewriter final {

  Context &context;
  Candidates const &candidates;

  // The move_value [lexical] instruction that was added during the run.
  //
  // Defined during createMove.
  MoveValueInst *mvi = nullptr;

public:
  Rewriter(Context &context, Candidates const &candidates)
      : context(context), candidates(candidates){};

  /// Make all changes required to fold the viable candidates.
  ///
  /// Specifically:
  /// - create a single move_value [lexical]
  ///       %move = move_value [lexical] %borrowee
  ///       %lifetime = begin_borrow [lexical] %move
  /// - transform all the candidates from
  ///       apply %fn(..., %copy, ...)
  ///       end_borrow %lifetime
  ///       ...
  ///       destroy_value %borrowee
  ///   to
  ///       end_borrow %lifetime
  ///       apply %fn(..., %move, ...)
  ///       ...
  /// - update SSA now that a second def has been introduced for
  ///   %borrowee
  MoveValueInst *run();

private:
  /// Add the new move_value [lexical] above the begin_borrow [lexical].
  ///
  /// At most one will be created per run of LexicalDestroyFolding.
  ///
  /// returns false if the move_value [lexical] instruction was added already
  ///         true if the move_value [lexical] instruction was added just now
  bool createMove();

  /// Combine the matched instruction sequence
  ///
  ///   apply %fn(%copy)
  ///   end_borrow %lifetime
  ///   destroy_value %instance
  ///
  /// into
  ///
  ///   end_borrow %lifetime
  ///   apply %fn(%move)
  ///
  /// This is done in three steps:
  /// (1) rewrite the apply (and delete the copy_value that is fed to it if
  ///     possible)
  /// (2) hoist the end_borrow
  /// (3) delete the destroy_value
  void fold(Match, ArrayRef<int> rewritableArgumentIndices);
};

//===----------------------------------------------------------------------===//
//                             MARK: Driver
//===----------------------------------------------------------------------===//

/// Perform any possible folding.
///
/// Returns whether any change was made.
MoveValueInst *run(Context &context) {
  Candidates candidates;

  // Do a cheap search for scope ending instructions that could potentially be
  // candidates for folding.
  if (!FindCandidates(context).run(candidates))
    return nullptr;

  // At least one full match was found and more expensive checks on the matches
  // are in order.

  BorroweeUsage borroweeUsage;
  if (!findBorroweeUsage(context, borroweeUsage))
    return nullptr;
  IntroducerUsage introducerUsage;
  if (!findIntroducerUsage(context, introducerUsage))
    return nullptr;

  // Now, filter the candidates using those values.
  if (!FilterCandidates(context, introducerUsage, borroweeUsage)
           .run(candidates))
    return nullptr;

  // Finally, check that %borrowee has no uses within %lifetime's
  // borrow scope.
  if (borroweeHasUsesWithinBorrowScope(context, borroweeUsage))
    return nullptr;

  // It is safe to rewrite the viable candidates.  Do so.
  return Rewriter(context, candidates).run();
}

//===----------------------------------------------------------------------===//
//                             MARK: Rewriting
//===----------------------------------------------------------------------===//

MoveValueInst *Rewriter::run() {
  bool foldedAny = false;
  (void)foldedAny;
  auto size = candidates.vector.size();
  for (unsigned index = 0; index < size; ++index) {
    auto candidate = candidates.vector[index];
    createMove();
    if (!candidate.viable) {
      // Nonviable candidates still end with the pattern
      //
      //     end_borrow %lifetime
      //     ...
      //     destroy_value %borrowee
      //
      // Now that the new move_value [lexical] dominates all candidates, the
      // every candidate's destroy_value %borrowee is dominated by it, so every
      // one is dominated by another consuming use which is illegal.  Rewrite
      // each such destroy_value to be a destroy_value of the move.
      candidate.match.dvi->setOperand(mvi);
      continue;
    }

    fold(candidate.match, candidate.argumentIndices);
#ifndef NDEBUG
    foldedAny = true;
#endif
  }
  assert(foldedAny && "rewriting without anything to rewrite!?");
  return mvi;
}

bool Rewriter::createMove() {
  // We only will create a single MoveValueInst.
  if (mvi)
    return false;

  auto introducerBuilder = SILBuilderWithScope(context.introducer);
  mvi = introducerBuilder.createMoveValue(
      RegularLocation::getAutoGeneratedLocation(context.introducer->getLoc()),
      context.borrowee, IsLexical);
  context.introducer->setOperand(mvi);
  return true;
}

void Rewriter::fold(Match candidate, ArrayRef<int> rewritableArgumentIndices) {
  // First, rewrite the apply in terms of the move_value.
  unsigned argumentNumber = 0;
  for (auto index : rewritableArgumentIndices) {
    auto argument = candidate.ai->getArgument(index);
    auto *cvi = cast<CopyValueInst>(argument);
    if (argumentNumber == 0) {
      candidate.ai->setArgument(index, mvi);
      if (!context.deleter.deleteIfDead(cvi)) {
        // We can't delete the copy_value because it has other users.  Instead,
        // add a compensating destroy just before the apply.
        auto applyBuilder = SILBuilderWithScope(candidate.ai);
        applyBuilder.createDestroyValue(
            RegularLocation::getAutoGeneratedLocation(candidate.ai->getLoc()),
            cvi);
      }
    } else {
      cvi->setOperand(mvi);
    }
    ++argumentNumber;
  }

  // At this point, we have something along the lines of
  //
  //   %move = move_value [lexical] %borrowee
  //   %lifetime = begin_borrow [lexical] %move
  //   ...
  //   apply %fn(%move)
  //   end_borrow %lifetime
  //
  // This isn't valid, though, because the apply consumes the %move but
  // the borrow scope guarantees it until the subsequent end_borrow.
  //
  // Fix this by hoisting the end_borrow above the apply.
  auto applyBuilder = SILBuilderWithScope(candidate.ai);
  applyBuilder.createEndBorrow(
      RegularLocation::getAutoGeneratedLocation(candidate.ai->getLoc()),
      context.introducer);
  context.deleter.forceDelete(candidate.ebi);

  // We have introduced a consuming use of %borrowee--the move_value-- and we
  // just rewrote the apply to consume it.  Delete the old destroy_value.
  context.deleter.forceDelete(candidate.dvi);
}

//===----------------------------------------------------------------------===//
//                             MARK: Lookups
//===----------------------------------------------------------------------===//

bool FindCandidates::run(Candidates &candidates) {
  llvm::SmallVector<SILInstruction *, 16> scopeEndingInsts;
  context.borrowedValue.getLocalScopeEndingInstructions(scopeEndingInsts);

  bool foundAnyFull = false;

  for (auto *instruction : scopeEndingInsts) {
    if (auto match = definesMatchingInstructionSequence(instruction)) {
      assert(match->ebi->getOperand() == context.introducer);
      assert(match->dvi->getOperand() == context.borrowee);
      candidates.vector.push_back({*match, false, {}});

      foundAnyFull = foundAnyFull || match->isFullMatch();
    } else {
      // The instruction doesn't define even a partial match.  Either the scope
      // ending instruction isn't an end_borrow or the subsequent instruction
      // isn't a destroy_value.  We can't fold any applies.
      return false;
    }
  }
  return foundAnyFull;
}

bool findIntroducerUsage(Context const &context, IntroducerUsage &usage) {
  SmallVector<Operand *, 16> useVector;
  if (!findExtendedUsesOfSimpleBorrowedValue(context.borrowedValue,
                                             &useVector)) {
    // If the value produced by begin_borrow escapes, don't shrink the borrow
    // scope over the apply.
    return false;
  }
  for (auto *use : useVector) {
    usage.uses.insert(use);
    usage.users.insert(use->getUser());
  }
  return true;
}

bool FilterCandidates::run(Candidates &candidates) {
  bool anyViable = false;

  // We have some end_borrows that might be candidates for folding.
  for (unsigned index = 0, count = candidates.vector.size(); index < count;
       ++index) {
    auto &candidate = candidates.vector[index];
    SmallVector<int, 2> rewritableArgumentIndices;
    auto viability = isViableMatch(candidate.match, candidate.argumentIndices);
    switch (viability) {
    case MatchViability::Viable:
      candidate.viable = true;
      anyViable = true;
      break;
    case MatchViability::Nonviable:
      break;
    case MatchViability::Illegal:
      return false;
      break;
    }
  }

  return anyViable;
}

bool findBorroweeUsage(Context const &context, BorroweeUsage &usage) {
  auto recordUse = [&](Operand *use) {
    // Ignore uses that aren't dominated by the introducer.  PrunedLiveness
    // relies on us doing this check.
    if (!context.dominanceTree.dominates(context.introducer, use->getUser()))
      return;
    usage.uses.push_back(use);
    usage.users.insert(use->getUser());
  };
  for (auto *use : context.borrowee->getUses()) {
    auto *user = use->getUser();
    if (user == context.introducer)
      continue;
    switch (use->getOperandOwnership()) {
    case OperandOwnership::PointerEscape:
      return false;
    case OperandOwnership::Borrow:
      if (!BorrowingOperand(use).visitScopeEndingUses([&](Operand *end) {
        if (end->getOperandOwnership() == OperandOwnership::Reborrow) {
          return false;
        }
        if (PhiOperand(end)) {
          assert(end->getOperandOwnership() ==
                 OperandOwnership::ForwardingConsume);
          return false;
        }
        recordUse(end);
        return true;
      })) {
        return false;
      }
      break;
    default:
      break;
    }
    recordUse(use);
  }
  return true;
}

bool borroweeHasUsesWithinBorrowScope(Context const &context,
                                      BorroweeUsage const &usage) {
  MultiDefPrunedLiveness liveness(context.function);
  context.borrowedValue.computeTransitiveLiveness(liveness);
  DeadEndBlocks deadEndBlocks(context.function);
  return !liveness.areUsesOutsideBoundary(usage.uses, &deadEndBlocks);
}

//===----------------------------------------------------------------------===//
//                             MARK: Predicates
//===----------------------------------------------------------------------===//

std::optional<Match>
FindCandidates::definesMatchingInstructionSequence(SILInstruction *inst) const {
  // Look specifically for
  //
  //   apply
  //   end_borrow // inst
  //   ...
  //   destroy_value %borrowee
  auto *ebi = dyn_cast<EndBorrowInst>(inst);
  if (!ebi)
    return std::nullopt;
  auto *dvi = findNextBorroweeDestroy(ebi->getNextInstruction());
  if (!dvi)
    return std::nullopt;
  auto *ai = dyn_cast_or_null<ApplyInst>(ebi->getPreviousInstruction());
  if (!ai)
    return {{nullptr, ebi, dvi}};

  return {{ai, ebi, dvi}};
}

DestroyValueInst *
FindCandidates::findNextBorroweeDestroy(SILInstruction *from) const {
  for (auto *inst = from; inst; inst = inst->getNextInstruction()) {
    if (!CanonicalizeOSSALifetime::ignoredByDestroyHoisting(inst->getKind())) {
      // This is not an instruction that CanonicalizeOSSALifetime would not
      // hoist a destroy above.  In other words, CanonicalizeOSSALifetime would
      // have hoisted
      //     destroy_value %borrowee
      // over this instruction if it could have.  Stop looking.
      return nullptr;
    }
    if (auto *dvi = dyn_cast<DestroyValueInst>(inst)) {
      if (dvi->getOperand() == context.borrowee) {
        return dvi;
      }
    }
  }
  return nullptr;
}

MatchViability FilterCandidates::isViableMatch(
    Match &candidate, SmallVectorImpl<int> &rewritableArgumentIndices) const {
  for (SILInstruction *inst = candidate.ebi; inst != candidate.dvi;
       inst = inst->getNextInstruction()) {
    if (borroweeUsage.users.contains(inst)) {
      // In the sequence of instructions
      //     end_borrow %lifetime
      //     ...
      //     destroy_value %borrowee
      // we have found a user of %borrowee.  That existing means not
      // only that this candidate is not viable but that NONE of the candidates
      // are viable and we need to bail out completely.
      return MatchViability::Illegal;
    }
  }

  // Now that we've checked that this partial match isn't illegal, go ahead and
  // discard it.
  if (!candidate.isFullMatch())
    return MatchViability::Nonviable;

  // If the apply isn't a user of the extended simple value, then this
  // transformation can't be done: the callee can't end the lexical lifetime
  // of a value it doesn't see.  Presumably, this apply is a deinit barrier.
  if (!introducerUsage.users.contains(candidate.ai))
    return MatchViability::Nonviable;

  // We aren't able to rewrite every simple extended use of %lifetime
  // in the apply.
  if (!rewritableArgumentIndicesForApply(candidate.ai,
                                         rewritableArgumentIndices))
    return MatchViability::Nonviable;

  return MatchViability::Viable;
}

bool FilterCandidates::isSimpleExtendedIntroducerDef(SILValue value) const {
  while (true) {
    auto *instruction = value.getDefiningInstruction();
    if (!instruction)
      return false;
    if (instruction == context.introducer)
      return true;
    if (auto *cvi = dyn_cast<CopyValueInst>(instruction)) {
      value = cvi->getOperand();
      continue;
    }
    return false;
  }
}

bool FilterCandidates::rewritableArgumentIndicesForApply(
    ApplySite apply, SmallVectorImpl<int> &indices) const {
  for (auto &operand : apply->getAllOperands()) {
    if (introducerUsage.uses.contains(&operand)) {
      if (apply.isArgumentOperand(operand)) {
        auto convention = apply.getArgumentConvention(operand);
        if (isSimpleExtendedIntroducerDef(operand.get()) &&
            convention.isOwnedConventionInCaller()) {
          indices.push_back(apply.getCalleeArgIndex(operand));
        } else {
          // This argument is a use of %lifetime but not an owned use that we
          // can rewrite.
          return false;
        }
      } else {
        // If %lifetime is used in some non-argument position, e.g. as the
        // callee, we can't fold.
        return false;
      }
    }
  }
  return true;
}

} // namespace LexicalDestroyFolding

//===----------------------------------------------------------------------===//
//                             MARK: Entry Point
//===----------------------------------------------------------------------===//

/// The entry point.
MoveValueInst *
swift::foldDestroysOfCopiedLexicalBorrow(BeginBorrowInst *bbi,
                                         DominanceInfo &dominanceTree,
                                         InstructionDeleter &deleter) {
  if (!bbi->isLexical())
    return nullptr;
  if (bbi->getOperand()->getOwnershipKind() != OwnershipKind::Owned)
    return nullptr;
  if (!dominanceTree.isReachableFromEntry(bbi->getParentBlock()))
    return nullptr;

  auto context = LexicalDestroyFolding::Context(bbi, dominanceTree, deleter);
  return LexicalDestroyFolding::run(context);
}

namespace swift::test {
// Arguments:
// - the lexical borrow to fold
// Dumps:
// - the function
static FunctionTest LexicalDestroyFoldingTest(
    "lexical_destroy_folding", [](auto &function, auto &arguments, auto &test) {
      auto *dominanceAnalysis = test.template getAnalysis<DominanceAnalysis>();
      DominanceInfo *domTree = dominanceAnalysis->get(&function);
      auto value = arguments.takeValue();
      auto *bbi = cast<BeginBorrowInst>(value);
      InstructionDeleter deleter;
      foldDestroysOfCopiedLexicalBorrow(bbi, *domTree, deleter);
      function.print(llvm::outs());
    });
} // end namespace swift::test