Skip to content

Commit af5eda5

Browse files
Merge pull request #27419 from ravikandhadai/oslog-backward-dependency-analysis
[SIL Optimization][OSLogOptimization] Improve the logic for discovering the beginning of the string interpolation passed to os log APIs.
2 parents 0746d1e + b2c1540 commit af5eda5

File tree

1 file changed

+114
-35
lines changed

1 file changed

+114
-35
lines changed

lib/SILOptimizer/Mandatory/OSLogOptimization.cpp

Lines changed: 114 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,9 @@
9191
#include "swift/SILOptimizer/Utils/SILInliner.h"
9292
#include "swift/SILOptimizer/Utils/SILOptFunctionBuilder.h"
9393
#include "llvm/ADT/MapVector.h"
94+
#include "swift/SIL/BasicBlockUtils.h"
95+
#include "swift/SIL/CFG.h"
96+
#include "llvm/ADT/BreadthFirstIterator.h"
9497

9598
using namespace swift;
9699

@@ -661,52 +664,99 @@ static void constantFold(SILInstruction *start,
661664
static SILInstruction *beginOfInterpolation(ApplyInst *oslogInit) {
662665
auto oslogInitCallSite = FullApplySite(oslogInit);
663666
SILFunction *callee = oslogInitCallSite.getCalleeFunction();
664-
auto &astContext = oslogInit->getFunction()->getASTContext();
665667

668+
assert (callee->hasSemanticsAttrThatStartsWith("oslog.message.init"));
666669
// The initializer must return the OSLogMessage instance directly.
667670
assert(oslogInitCallSite.getNumArguments() >= 1 &&
668671
oslogInitCallSite.getNumIndirectSILResults() == 0);
669672

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+
}
677691

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+
}
679728

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);
700738
}
701739

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+
}
708747
}
748+
assert(firstBB);
709749

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);
710760
return startInst;
711761
}
712762

@@ -733,6 +783,27 @@ static ApplyInst *getAsOSLogMessageInit(SILInstruction *inst) {
733783
return nullptr;
734784
}
735785

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+
736807
class OSLogOptimization : public SILFunctionTransform {
737808

738809
~OSLogOptimization() override {}
@@ -747,6 +818,14 @@ class OSLogOptimization : public SILFunctionTransform {
747818
return;
748819
}
749820

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+
750829
// Collect all 'OSLogMessage.init' in the function. 'OSLogMessage' is a
751830
// custom string interpolation type used by the new OS log APIs.
752831
SmallVector<ApplyInst *, 4> oslogMessageInits;

0 commit comments

Comments
 (0)