91
91
#include " swift/SILOptimizer/Utils/SILInliner.h"
92
92
#include " swift/SILOptimizer/Utils/SILOptFunctionBuilder.h"
93
93
#include " llvm/ADT/MapVector.h"
94
+ #include " swift/SIL/BasicBlockUtils.h"
95
+ #include " swift/SIL/CFG.h"
96
+ #include " llvm/ADT/BreadthFirstIterator.h"
94
97
95
98
using namespace swift ;
96
99
@@ -661,52 +664,99 @@ static void constantFold(SILInstruction *start,
661
664
static SILInstruction *beginOfInterpolation (ApplyInst *oslogInit) {
662
665
auto oslogInitCallSite = FullApplySite (oslogInit);
663
666
SILFunction *callee = oslogInitCallSite.getCalleeFunction ();
664
- auto &astContext = oslogInit->getFunction ()->getASTContext ();
665
667
668
+ assert (callee->hasSemanticsAttrThatStartsWith (" oslog.message.init" ));
666
669
// The initializer must return the OSLogMessage instance directly.
667
670
assert (oslogInitCallSite.getNumArguments () >= 1 &&
668
671
oslogInitCallSite.getNumIndirectSILResults () == 0 );
669
672
670
- SILInstruction *firstArgumentInst =
671
- oslogInitCallSite.getArgument (0 )->getDefiningInstruction ();
672
- if (!firstArgumentInst) {
673
- // oslogInit call does not correspond to an auto-generated initialization
674
- // done by the compiler on seeing a string interpolation. Ignore this.
675
- return nullptr ;
676
- }
673
+ // List of backward dependencies that needs to be analyzed.
674
+ SmallVector<SILInstruction *, 4 > worklist = { oslogInit };
675
+ SmallPtrSet<SILInstruction *, 4 > seenInstructions = { oslogInit };
676
+ // List of instructions that could potentially mark the beginning of the
677
+ // interpolation.
678
+ SmallPtrSet<SILInstruction *, 4 > candidateStartInstructions;
679
+
680
+ unsigned i = 0 ;
681
+ while (i < worklist.size ()) {
682
+ SILInstruction *inst = worklist[i++];
683
+
684
+ if (isa<PartialApplyInst>(inst)) {
685
+ // Partial applies are used to capture the dynamic arguments passed to
686
+ // the string interpolation. Their arguments are not required to be
687
+ // known at compile time and they need not be constant evaluated.
688
+ // Therefore, do not follow this dependency chain.
689
+ continue ;
690
+ }
677
691
678
- SILInstruction *startInst = nullptr ;
692
+ for (Operand &operand : inst->getAllOperands ()) {
693
+ if (SILInstruction *definingInstruction =
694
+ operand.get ()->getDefiningInstruction ()) {
695
+ if (seenInstructions.count (definingInstruction))
696
+ continue ;
697
+ worklist.push_back (definingInstruction);
698
+ seenInstructions.insert (definingInstruction);
699
+ candidateStartInstructions.insert (definingInstruction);
700
+ }
701
+ // If there is no definining instruction for this operand, it could be a
702
+ // basic block or function parameter. Such operands are not considered
703
+ // in the backward slice. Dependencies through them are safe to ignore
704
+ // in this context.
705
+ }
706
+
707
+ // If the instruction: `inst` has an operand, its definition should precede
708
+ // `inst` in the control-flow order. Therefore, remove `inst` from the
709
+ // candidate start instructions.
710
+ if (inst->getNumOperands () > 0 ) {
711
+ candidateStartInstructions.erase (inst);
712
+ }
713
+
714
+ if (!isa<AllocStackInst>(inst)) {
715
+ continue ;
716
+ }
717
+
718
+ // If we have an alloc_stack instruction, include stores into it into the
719
+ // backward dependency list. However, whether alloc_stack precedes its in
720
+ // control-flow order can only be determined by traversing the instrutions
721
+ // in the control-flow order.
722
+ AllocStackInst *allocStackInst = cast<AllocStackInst>(inst);
723
+ for (StoreInst *storeInst : allocStackInst->getUsersOfType <StoreInst>()) {
724
+ worklist.push_back (storeInst);
725
+ candidateStartInstructions.insert (storeInst);
726
+ }
727
+ }
679
728
680
- // If this is an initialization from string interpolation, the first argument
681
- // to the initializer is a load of an auto-generated alloc-stack of
682
- // OSLogInterpolation:
683
- // 'alloc_stack $OSLogInterpolation, var, name $interpolation'
684
- // If no such instruction exists, ignore this call as this is not a
685
- // OSLogMessage instantiation through string interpolation.
686
- if (callee->hasSemanticsAttr (" oslog.message.init_interpolation" )) {
687
- auto *loadInst = dyn_cast<LoadInst>(firstArgumentInst);
688
- if (!loadInst)
689
- return nullptr ;
690
-
691
- auto *allocStackInst = dyn_cast<AllocStackInst>(loadInst->getOperand ());
692
- if (!allocStackInst)
693
- return nullptr ;
694
-
695
- Optional<SILDebugVariable> varInfo = allocStackInst->getVarInfo ();
696
- if (!varInfo && varInfo->Name != astContext.Id_dollarInterpolation .str ())
697
- return nullptr ;
698
-
699
- startInst = allocStackInst;
729
+ // Find the first basic block in the control-flow order. TODO: if we do not
730
+ // madatorily inline appendLiteral/Interpolation functions of
731
+ // OSLogInterpolation, we can expect all candidate instructions to be in the
732
+ // same basic block. Once @_transparent is removed from those functions,
733
+ // simplify this code.
734
+ SmallPtrSet<SILBasicBlock *, 4 > candidateBBs;
735
+ for (auto *candidate: candidateStartInstructions) {
736
+ SILBasicBlock *candidateBB = candidate->getParent ();
737
+ candidateBBs.insert (candidateBB);
700
738
}
701
739
702
- // If this is an initialization from a string literal, the first argument
703
- // should be the creation of the string literal.
704
- if (callee->hasSemanticsAttr (" oslog.message.init_stringliteral" )) {
705
- if (!getStringMakeUTF8Init (firstArgumentInst))
706
- return nullptr ;
707
- startInst = firstArgumentInst;
740
+ SILBasicBlock *firstBB = nullptr ;
741
+ SILBasicBlock *entryBB = oslogInit->getFunction ()->getEntryBlock ();
742
+ for (SILBasicBlock *bb: llvm::breadth_first<SILBasicBlock *>(entryBB)) {
743
+ if (candidateBBs.count (bb)) {
744
+ firstBB = bb;
745
+ break ;
746
+ }
708
747
}
748
+ assert (firstBB);
709
749
750
+ // Iterate over the instructions in the firstBB and find the instruction that
751
+ // starts the interpolation.
752
+ SILInstruction *startInst = nullptr ;
753
+ for (SILInstruction &inst : *firstBB) {
754
+ if (candidateStartInstructions.count (&inst)) {
755
+ startInst = &inst;
756
+ break ;
757
+ }
758
+ }
759
+ assert (startInst);
710
760
return startInst;
711
761
}
712
762
@@ -733,6 +783,27 @@ static ApplyInst *getAsOSLogMessageInit(SILInstruction *inst) {
733
783
return nullptr ;
734
784
}
735
785
786
+ // / Return true iff this function is a protocol witness for
787
+ // / ExpressibleByStringInterpolation.init(stringInterpolation:) in OSLogMessage.
788
+ bool isAutoGeneratedInitOfOSLogMessage (SILFunction &fun) {
789
+ DeclContext *declContext = fun.getDeclContext ();
790
+ if (!declContext)
791
+ return false ;
792
+ Decl *decl = declContext->getAsDecl ();
793
+ if (!decl)
794
+ return false ;
795
+ ConstructorDecl *cdecl = dyn_cast<ConstructorDecl>(decl);
796
+ if (!cdecl)
797
+ return false ;
798
+ DeclContext *parentContext = cdecl->getParent ();
799
+ if (!parentContext)
800
+ return false ;
801
+ NominalTypeDecl *typeDecl = parentContext->getSelfNominalTypeDecl ();
802
+ if (!typeDecl)
803
+ return false ;
804
+ return typeDecl->getName () == fun.getASTContext ().Id_OSLogMessage ;
805
+ }
806
+
736
807
class OSLogOptimization : public SILFunctionTransform {
737
808
738
809
~OSLogOptimization () override {}
@@ -747,6 +818,14 @@ class OSLogOptimization : public SILFunctionTransform {
747
818
return ;
748
819
}
749
820
821
+ // Skip the auto-generated (transparent) witness method of OSLogMessage,
822
+ // which ends up invoking the OSLogMessage initializer:
823
+ // "oslog.message.init_interpolation" but without an interpolated
824
+ // string literal.
825
+ if (isAutoGeneratedInitOfOSLogMessage (fun)) {
826
+ return ;
827
+ }
828
+
750
829
// Collect all 'OSLogMessage.init' in the function. 'OSLogMessage' is a
751
830
// custom string interpolation type used by the new OS log APIs.
752
831
SmallVector<ApplyInst *, 4 > oslogMessageInits;
0 commit comments