Skip to content

Commit c71957c

Browse files
authored
Merge pull request #27744 from ravikandhadai/oslog-optimization-bug-fixes
2 parents f60240f + ffd3fef commit c71957c

File tree

4 files changed

+570
-95
lines changed

4 files changed

+570
-95
lines changed

lib/SILOptimizer/Mandatory/OSLogOptimization.cpp

Lines changed: 161 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,8 @@
7676
#include "swift/Basic/OptimizationMode.h"
7777
#include "swift/Demangling/Demangle.h"
7878
#include "swift/Demangling/Demangler.h"
79+
#include "swift/SIL/BasicBlockUtils.h"
80+
#include "swift/SIL/CFG.h"
7981
#include "swift/SIL/InstructionUtils.h"
8082
#include "swift/SIL/SILBasicBlock.h"
8183
#include "swift/SIL/SILBuilder.h"
@@ -86,14 +88,14 @@
8688
#include "swift/SIL/SILModule.h"
8789
#include "swift/SILOptimizer/PassManager/Passes.h"
8890
#include "swift/SILOptimizer/PassManager/Transforms.h"
91+
#include "swift/SILOptimizer/Utils/CFGOptUtils.h"
8992
#include "swift/SILOptimizer/Utils/ConstExpr.h"
9093
#include "swift/SILOptimizer/Utils/InstOptUtils.h"
9194
#include "swift/SILOptimizer/Utils/SILInliner.h"
9295
#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"
9697
#include "llvm/ADT/BreadthFirstIterator.h"
98+
#include "llvm/ADT/MapVector.h"
9799

98100
using namespace swift;
99101

@@ -177,10 +179,7 @@ class FoldState {
177179
/// Instruction from where folding must begin.
178180
SILInstruction *beginInstruction;
179181

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.
184183
SmallSetVector<SILInstruction *, 2> endInstructions;
185184

186185
private:
@@ -292,8 +291,14 @@ static bool isSILValueFoldable(SILValue value) {
292291
ASTContext &astContext = definingInst->getFunction()->getASTContext();
293292
SILType silType = value->getType();
294293

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.
295298
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) &&
297302
!getStringMakeUTF8Init(definingInst) &&
298303
isIntegerOrStringType(silType, astContext));
299304
}
@@ -428,120 +433,181 @@ static SILValue emitCodeForSymbolicValue(SymbolicValue symVal,
428433
}
429434
}
430435

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()) {
446459
SILInstruction *user = use->getUser();
447-
448-
if (isa<ReleaseValueInst>(user) || isa<DestroyValueInst>(user)) {
449-
lifetimeEndInsts.push_back(user);
460+
if (visitedUsers.count(user))
450461
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));
460464
}
461465
}
462-
}
463466

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)
481482
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());
482491
}
483-
assert(originalVal.getOwnershipKind() == ValueOwnershipKind::Guaranteed);
492+
}
484493

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

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+
});
492552
return;
493553
}
494554

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;
506567
}
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;
507576
}
508577

509578
/// Given a fold state with constant-valued instructions, substitute the
510579
/// instructions with the constant values. The constant values could be strings
511580
/// or Stdlib integer-struct values or builtin integers.
512581
static void substituteConstants(FoldState &foldState) {
513-
514582
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;
518585

519586
for (SILValue constantSILValue : foldState.getConstantSILValues()) {
520587
SymbolicValue constantSymbolicVal =
521588
evaluator.lookupConstValue(constantSILValue).getValue();
522589

523590
SILInstruction *definingInst = constantSILValue->getDefiningInstruction();
524591
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;
525598

526599
SILBuilderWithScope builder(definingInst);
527600
SILLocation loc = definingInst->getLoc();
528601
SILType instType = constantSILValue->getType();
529602
SILValue foldedSILVal = emitCodeForSymbolicValue(
530603
constantSymbolicVal, instType, builder, loc, foldState.stringInfo);
531604

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);
542609
}
543-
544-
recursivelyDeleteTriviallyDeadInstructions(deletedInsts, true,
610+
recursivelyDeleteTriviallyDeadInstructions(possiblyDeadInsts, /*force*/ false,
545611
[&](SILInstruction *DeadI) {});
546612
}
547613

@@ -581,8 +647,10 @@ static bool detectAndDiagnoseErrors(Optional<SymbolicValue> errorInfo,
581647
return true;
582648
}
583649

650+
// The first (and only) property of OSLogMessage is the OSLogInterpolation
651+
// instance.
584652
SymbolicValue osLogInterpolationValue =
585-
osLogMessageValueOpt->lookThroughSingleElementAggregates();
653+
osLogMessageValueOpt->getAggregateValue()[0];
586654
if (!osLogInterpolationValue.isConstant()) {
587655
diagnose(astContext, sourceLoc, diag::oslog_non_constant_interpolation);
588656
return true;
@@ -633,12 +701,14 @@ static bool detectAndDiagnoseErrors(Optional<SymbolicValue> errorInfo,
633701
static void constantFold(SILInstruction *start,
634702
SingleValueInstruction *oslogMessage,
635703
unsigned assertConfig) {
704+
SILFunction *fun = start->getFunction();
636705

637706
// 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());
640710

641-
FoldState state(start->getFunction(), assertConfig, start, lifetimeEndInsts);
711+
FoldState state(fun, assertConfig, start, endUsersOfOSLogMessage);
642712

643713
auto errorInfo = collectConstants(state);
644714

lib/SILOptimizer/Utils/ConstantFolding.cpp

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1527,10 +1527,9 @@ constantFoldGlobalStringTablePointerBuiltin(BuiltinInst *bi,
15271527
bool enableDiagnostics) {
15281528
// Look through string initializer to extract the string_literal instruction.
15291529
//
1530-
// We allow for a single borrow to be stripped here if we are here in
1531-
// [ossa]. The begin borrow occurs b/c SILGen treats builtins as having
1532-
// arguments with a +0 convention (implying a borrow).
1533-
SILValue builtinOperand = stripBorrow(bi->getOperand(0));
1530+
// We can look through ownership instructions to get to the string value that
1531+
// is passed to this builtin.
1532+
SILValue builtinOperand = stripOwnershipInsts(bi->getOperand(0));
15341533
SILFunction *caller = bi->getFunction();
15351534

15361535
FullApplySite stringInitSite = FullApplySite::isa(builtinOperand);

0 commit comments

Comments
 (0)