31
31
#include " swift/SILOptimizer/PassManager/Passes.h"
32
32
#include " swift/SILOptimizer/PassManager/Transforms.h"
33
33
#include " swift/SILOptimizer/Utils/InstOptUtils.h"
34
+ #include " swift/SILOptimizer/Utils/SILInliner.h"
35
+ #include " swift/SILOptimizer/Utils/SILOptFunctionBuilder.h"
36
+ #include " swift/SILOptimizer/Utils/BasicBlockOptUtils.h"
34
37
#include " llvm/ADT/Hashing.h"
35
38
#include " llvm/ADT/STLExtras.h"
36
39
#include " llvm/ADT/ScopedHashTable.h"
@@ -444,6 +447,10 @@ namespace swift {
444
447
// / eliminating trivially redundant instructions and using simplifyInstruction
445
448
// / to canonicalize things as it goes. It is intended to be fast and catch
446
449
// / obvious cases so that SILCombine and other passes are more effective.
450
+ // /
451
+ // / It also optimizes calls to lazy property getters: If such a call is
452
+ // / dominated by another call to the same getter, it is replaced by a direct
453
+ // / load of the property - assuming that it is already computed.
447
454
class CSE {
448
455
public:
449
456
typedef llvm::ScopedHashTableVal<SimpleValue, ValueBase *> SimpleValueHTType;
@@ -462,11 +469,21 @@ class CSE {
462
469
463
470
SideEffectAnalysis *SEA;
464
471
465
- CSE (bool RunsOnHighLevelSil, SideEffectAnalysis *SEA)
466
- : SEA(SEA), RunsOnHighLevelSil(RunsOnHighLevelSil) {}
472
+ SILOptFunctionBuilder &FuncBuilder;
473
+
474
+ // / The set of calls to lazy property getters which can be replace by a direct
475
+ // / load of the property value.
476
+ llvm::SmallVector<ApplyInst *, 8 > lazyPropertyGetters;
477
+
478
+ CSE (bool RunsOnHighLevelSil, SideEffectAnalysis *SEA,
479
+ SILOptFunctionBuilder &FuncBuilder)
480
+ : SEA(SEA), FuncBuilder(FuncBuilder),
481
+ RunsOnHighLevelSil (RunsOnHighLevelSil) {}
467
482
468
483
bool processFunction (SILFunction &F, DominanceInfo *DT);
469
-
484
+
485
+ bool processLazyPropertyGetters ();
486
+
470
487
bool canHandle (SILInstruction *Inst);
471
488
472
489
private:
@@ -576,6 +593,40 @@ bool CSE::processFunction(SILFunction &Fm, DominanceInfo *DT) {
576
593
return Changed;
577
594
}
578
595
596
+ // / Replace lazy property getters (which are dominated by the same getter)
597
+ // / by a direct load of the value.
598
+ bool CSE::processLazyPropertyGetters () {
599
+ bool changed = false ;
600
+ for (ApplyInst *ai : lazyPropertyGetters) {
601
+ SILFunction *getter = ai->getReferencedFunctionOrNull ();
602
+ assert (getter && getter->isLazyPropertyGetter ());
603
+ SILBasicBlock *callBlock = ai->getParent ();
604
+
605
+ // Inline the getter...
606
+ SILInliner::inlineFullApply (ai, SILInliner::InlineKind::PerformanceInline,
607
+ FuncBuilder);
608
+
609
+ // ...and fold the switch_enum in the first block to the Optional.some case.
610
+ // The Optional.none branch becomes dead.
611
+ auto *sei = cast<SwitchEnumInst>(callBlock->getTerminator ());
612
+ ASTContext &ctxt = callBlock->getParent ()->getModule ().getASTContext ();
613
+ EnumElementDecl *someDecl = ctxt.getOptionalSomeDecl ();
614
+ SILBasicBlock *someDest = sei->getCaseDestination (someDecl);
615
+ assert (someDest->getNumArguments () == 1 );
616
+ SILValue enumVal = sei->getOperand ();
617
+ SILBuilder builder (sei);
618
+ SILType ty = enumVal->getType ().getEnumElementType (someDecl,
619
+ sei->getModule (), builder.getTypeExpansionContext ());
620
+ auto *ued =
621
+ builder.createUncheckedEnumData (sei->getLoc (), enumVal, someDecl, ty);
622
+ builder.createBranch (sei->getLoc (), someDest, { ued });
623
+ sei->eraseFromParent ();
624
+ changed = true ;
625
+ ++NumCSE;
626
+ }
627
+ return changed;
628
+ }
629
+
579
630
namespace {
580
631
// A very simple cloner for cloning instructions inside
581
632
// the same function. The only interesting thing it does
@@ -768,6 +819,34 @@ bool CSE::processOpenExistentialRef(OpenExistentialRefInst *Inst,
768
819
return true ;
769
820
}
770
821
822
+ // / Returns true if \p ai is a call to a lazy property getter, which we can
823
+ // / handle.
824
+ static bool isLazyPropertyGetter (ApplyInst *ai) {
825
+ SILFunction *callee = ai->getReferencedFunctionOrNull ();
826
+ if (!callee || callee->isExternalDeclaration () ||
827
+ !callee->isLazyPropertyGetter ())
828
+ return false ;
829
+
830
+ // Check if the first block has a switch_enum of an Optional.
831
+ // We don't handle getters of generic types, which have a switch_enum_addr.
832
+ // This will be obsolete with opaque values anyway.
833
+ auto *SEI = dyn_cast<SwitchEnumInst>(callee->getEntryBlock ()->getTerminator ());
834
+ if (!SEI)
835
+ return false ;
836
+
837
+ ASTContext &ctxt = SEI->getFunction ()->getModule ().getASTContext ();
838
+ EnumElementDecl *someDecl = ctxt.getOptionalSomeDecl ();
839
+
840
+ for (unsigned i = 0 , e = SEI->getNumCases (); i != e; ++i) {
841
+ auto Entry = SEI->getCase (i);
842
+ if (Entry.first == someDecl) {
843
+ SILBasicBlock *destBlock = Entry.second ;
844
+ return destBlock->getNumArguments () == 1 ;
845
+ }
846
+ }
847
+ return false ;
848
+ }
849
+
771
850
bool CSE::processNode (DominanceInfoNode *Node) {
772
851
SILBasicBlock *BB = Node->getBlock ();
773
852
bool Changed = false ;
@@ -817,6 +896,16 @@ bool CSE::processNode(DominanceInfoNode *Node) {
817
896
if (SILInstruction *AvailInst = AvailableValues->lookup (Inst)) {
818
897
LLVM_DEBUG (llvm::dbgs () << " SILCSE CSE: " << *Inst << " to: "
819
898
<< *AvailInst << ' \n ' );
899
+
900
+ auto *AI = dyn_cast<ApplyInst>(Inst);
901
+ if (AI && isLazyPropertyGetter (AI)) {
902
+ // We do the actual transformation for lazy property getters later. It
903
+ // changes the CFG and we don't want to disturb the dominator tree walk
904
+ // here.
905
+ lazyPropertyGetters.push_back (AI);
906
+ continue ;
907
+ }
908
+
820
909
// Instructions producing a new opened archetype need a special handling,
821
910
// because replacing these instructions may require a replacement
822
911
// of the opened archetype type operands in some of the uses.
@@ -874,6 +963,9 @@ bool CSE::canHandle(SILInstruction *Inst) {
874
963
if (MB == SILInstruction::MemoryBehavior::None)
875
964
return true ;
876
965
966
+ if (isLazyPropertyGetter (AI))
967
+ return true ;
968
+
877
969
return false ;
878
970
}
879
971
if (auto *BI = dyn_cast<BuiltinInst>(Inst)) {
@@ -1172,8 +1264,9 @@ class SILCSE : public SILFunctionTransform {
1172
1264
DominanceAnalysis* DA = getAnalysis<DominanceAnalysis>();
1173
1265
1174
1266
auto *SEA = PM->getAnalysis <SideEffectAnalysis>();
1267
+ SILOptFunctionBuilder FuncBuilder (*this );
1175
1268
1176
- CSE C (RunsOnHighLevelSil, SEA);
1269
+ CSE C (RunsOnHighLevelSil, SEA, FuncBuilder );
1177
1270
bool Changed = false ;
1178
1271
1179
1272
// Perform the traditional CSE.
@@ -1182,7 +1275,15 @@ class SILCSE : public SILFunctionTransform {
1182
1275
// Perform CSE of existential and witness_method instructions.
1183
1276
Changed |= CSEExistentialCalls (getFunction (),
1184
1277
DA->get (getFunction ()));
1185
- if (Changed) {
1278
+
1279
+ // Handle calls to lazy property getters, which are collected in
1280
+ // processFunction().
1281
+ if (C.processLazyPropertyGetters ()) {
1282
+ // Cleanup the dead blocks from the inlined lazy property getters.
1283
+ removeUnreachableBlocks (*getFunction ());
1284
+
1285
+ invalidateAnalysis (SILAnalysis::InvalidationKind::Everything);
1286
+ } else if (Changed) {
1186
1287
invalidateAnalysis (SILAnalysis::InvalidationKind::CallsAndInstructions);
1187
1288
}
1188
1289
}
0 commit comments