Skip to content

Commit 774af19

Browse files
Merge pull request swiftlang#28635 from ravikandhadai/oslog-dead-code-elim-patch1
[SIL Optimization] Create new utilities for dead code elimination.
2 parents 6936d0d + 9356864 commit 774af19

23 files changed

+658
-108
lines changed

include/swift/AST/Builtins.def

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -641,8 +641,10 @@ BUILTIN_MISC_OPERATION(PoundAssert, "poundAssert", "", Special)
641641

642642
/// globalStringTablePointer has type String -> Builtin.RawPointer.
643643
/// It returns an immortal, global string table pointer for strings constructed
644-
/// from string literals.
645-
BUILTIN_MISC_OPERATION_WITH_SILGEN(GlobalStringTablePointer, "globalStringTablePointer", "", Special)
644+
/// from string literals. We consider it effects as readnone meaning that it
645+
/// does not read any memory (note that even though it reads from a string, it
646+
/// is a pure value and therefore we can consider it as readnone).
647+
BUILTIN_MISC_OPERATION_WITH_SILGEN(GlobalStringTablePointer, "globalStringTablePointer", "n", Special)
646648

647649
#undef BUILTIN_MISC_OPERATION_WITH_SILGEN
648650

include/swift/AST/DiagnosticsSIL.def

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -528,6 +528,9 @@ ERROR(oslog_non_constant_interpolation, none, "'OSLogInterpolation' instance "
528528
ERROR(oslog_property_not_constant, none, "'OSLogInterpolation.%0' is not a "
529529
"constant", (StringRef))
530530

531+
ERROR(oslog_message_alive_after_opts, none, "OSLogMessage instance must not "
532+
"be explicitly created and must be deletable", ())
533+
531534
ERROR(global_string_pointer_on_non_constant, none, "globalStringTablePointer "
532535
"builtin must used only on string literals", ())
533536

include/swift/SILOptimizer/Utils/ConstExpr.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,8 @@ class ConstExprStepEvaluator {
207207
void dumpState();
208208
};
209209

210+
bool hasConstantEvaluableAnnotation(SILFunction *fun);
211+
210212
bool isConstantEvaluable(SILFunction *fun);
211213

212214
/// Return true if and only if the given function \p fun is specially modeled

include/swift/SILOptimizer/Utils/InstOptUtils.h

Lines changed: 97 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,8 +56,101 @@ NullablePtr<SILInstruction> createIncrementBefore(SILValue ptr,
5656
NullablePtr<SILInstruction> createDecrementBefore(SILValue ptr,
5757
SILInstruction *insertpt);
5858

59+
/// A utility for deleting one or more instructions belonging to a function, and
60+
/// cleaning up any dead code resulting from deleting those instructions. Use
61+
/// this utility instead of
62+
/// \c recursivelyDeleteTriviallyDeadInstruction.
63+
class InstructionDeleter {
64+
private:
65+
// A set vector of instructions that are found to be dead. The ordering
66+
// of instructions in this set is important as when a dead instruction is
67+
// removed, new instructions will be generated to fix the lifetime of the
68+
// instruction's operands. This has to be deterministic.
69+
SmallSetVector<SILInstruction *, 8> deadInstructions;
70+
71+
void deleteInstruction(SILInstruction *inst,
72+
llvm::function_ref<void(SILInstruction *)> callback,
73+
bool fixOperandLifetimes);
74+
75+
public:
76+
InstructionDeleter() {}
77+
78+
/// If the instruction \p inst is dead, record it so that it can be cleaned
79+
/// up.
80+
void trackIfDead(SILInstruction *inst);
81+
82+
/// Delete the instruction \p inst and record instructions that may become
83+
/// dead because of the removal of \c inst. This function will add necessary
84+
/// ownership instructions to fix the lifetimes of the operands of \c inst to
85+
/// compensate for its deletion. This function will not clean up dead code
86+
/// resulting from the instruction's removal. To do so, invoke the method \c
87+
/// cleanupDeadCode of this instance, once the SIL of the contaning function
88+
/// is made consistent.
89+
///
90+
/// \pre the function containing \c inst must be using ownership SIL.
91+
/// \pre the instruction to be deleted must not have any use other than
92+
/// incidental uses.
93+
///
94+
/// \param callback a callback called whenever an instruction
95+
/// is deleted.
96+
void forceDeleteAndFixLifetimes(
97+
SILInstruction *inst,
98+
llvm::function_ref<void(SILInstruction *)> callback =
99+
[](SILInstruction *) {});
100+
101+
/// Delete the instruction \p inst and record instructions that may become
102+
/// dead because of the removal of \c inst. If in ownership SIL, use the
103+
/// \c forceDeleteAndFixLifetimes function instead, unless under special
104+
/// circumstances where the client must handle fixing lifetimes of the
105+
/// operands of the deleted instructions. This function will not fix the
106+
/// lifetimes of the operands of \c inst once it is deleted. This function
107+
/// will not clean up dead code resulting from the instruction's removal. To
108+
/// do so, invoke the method \c cleanupDeadCode of this instance, once the SIL
109+
/// of the contaning function is made consistent.
110+
///
111+
/// \pre the instruction to be deleted must not have any use other than
112+
/// incidental uses.
113+
///
114+
/// \param callback a callback called whenever an instruction
115+
/// is deleted.
116+
void forceDelete(
117+
SILInstruction *inst,
118+
llvm::function_ref<void(SILInstruction *)> callback =
119+
[](SILInstruction *) {});
120+
121+
/// Clean up dead instructions that are tracked by this instance and all
122+
/// instructions that transitively become dead.
123+
///
124+
/// \pre the function contaning dead instructions must be consistent (i.e., no
125+
/// under or over releases). Note that if \c forceDelete call leaves the
126+
/// function body in an inconsistent state, it needs to be made consistent
127+
/// before this method is invoked.
128+
///
129+
/// \param callback a callback called whenever an instruction is deleted.
130+
void
131+
cleanUpDeadInstructions(llvm::function_ref<void(SILInstruction *)> callback =
132+
[](SILInstruction *) {});
133+
};
134+
135+
/// If \c inst is dead, delete it and recursively eliminate all code that
136+
/// becomes dead because of that. If more than one instruction must
137+
/// be checked/deleted use the \c InstructionDeleter utility.
138+
///
139+
/// This function will add necessary compensation code to fix the lifetimes of
140+
/// the operands of the deleted instructions.
141+
///
142+
/// \pre the SIL function containing the instruction is assumed to be
143+
/// consistent, i.e., does not have under or over releases.
144+
///
145+
/// \param callback a callback called whenever an instruction is deleted.
146+
void eliminateDeadInstruction(
147+
SILInstruction *inst, llvm::function_ref<void(SILInstruction *)> callback =
148+
[](SILInstruction *) {});
149+
59150
/// For each of the given instructions, if they are dead delete them
60-
/// along with their dead operands.
151+
/// along with their dead operands. Note this utility must be phased out and
152+
/// replaced by \c eliminateDeadInstruction and
153+
/// \c InstructionDeleter utilities.
61154
///
62155
/// \param inst The ArrayRef of instructions to be deleted.
63156
/// \param force If Force is set, don't check if the top level instructions
@@ -69,7 +162,9 @@ void recursivelyDeleteTriviallyDeadInstructions(
69162
});
70163

71164
/// If the given instruction is dead, delete it along with its dead
72-
/// operands.
165+
/// operands. Note this utility must be phased out and replaced by
166+
/// \c eliminateDeadInstruction and
167+
/// \c InstructionDeleter utilities.
73168
///
74169
/// \param inst The instruction to be deleted.
75170
/// \param force If Force is set, don't check if the top level instruction is

lib/SILOptimizer/LoopTransforms/LICM.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -991,7 +991,7 @@ void LoopTreeOptimization::hoistLoadsAndStores(SILValue addr, SILLoop *loop, Ins
991991
}
992992

993993
// In case the value is only stored but never loaded in the loop.
994-
recursivelyDeleteTriviallyDeadInstructions(initialLoad);
994+
eliminateDeadInstruction(initialLoad);
995995
}
996996

997997
bool LoopTreeOptimization::hoistAllLoadsAndStores(SILLoop *loop) {

lib/SILOptimizer/Mandatory/DefiniteInitialization.cpp

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2010,8 +2010,13 @@ void LifetimeChecker::deleteDeadRelease(unsigned ReleaseID) {
20102010
SILInstruction *Release = Destroys[ReleaseID];
20112011
if (isa<DestroyAddrInst>(Release)) {
20122012
SILValue Addr = Release->getOperand(0);
2013-
if (auto *AddrI = Addr->getDefiningInstruction())
2013+
if (auto *AddrI = Addr->getDefiningInstruction()) {
2014+
// FIXME: AddrI will not be deleted (nor its operands) when Release is
2015+
// still using AddrI's result. Fix this, and migrate to using
2016+
// InstructionDeleter utility instead of
2017+
// recursivelyDeadTriviallyDeadInstructions.
20142018
recursivelyDeleteTriviallyDeadInstructions(AddrI);
2019+
}
20152020
}
20162021
Release->eraseFromParent();
20172022
Destroys[ReleaseID] = nullptr;

lib/SILOptimizer/Mandatory/IRGenPrepare.cpp

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,12 +56,17 @@ static bool cleanFunction(SILFunction &fn) {
5656
LLVM_FALLTHROUGH;
5757
}
5858
case BuiltinValueKind::PoundAssert:
59-
case BuiltinValueKind::StaticReport:
59+
case BuiltinValueKind::StaticReport: {
6060
// The call to the builtin should get removed before we reach
6161
// IRGen.
62-
recursivelyDeleteTriviallyDeadInstructions(bi, /* Force */ true);
62+
InstructionDeleter deleter;
63+
deleter.forceDelete(bi);
64+
// StaticReport only takes trivial operands, and therefore doesn't
65+
// require fixing the lifetime of its operands.
66+
deleter.cleanUpDeadInstructions();
6367
madeChange = true;
6468
break;
69+
}
6570
default:
6671
break;
6772
}

lib/SILOptimizer/Mandatory/OSLogOptimization.cpp

Lines changed: 70 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -262,8 +262,7 @@ static bool shouldAttemptEvaluation(SILInstruction *inst) {
262262
SILFunction *calleeFun = apply->getCalleeFunction();
263263
if (!calleeFun)
264264
return false;
265-
return isKnownConstantEvaluableFunction(calleeFun) ||
266-
isConstantEvaluable(calleeFun);
265+
return isConstantEvaluable(calleeFun);
267266
}
268267

269268
/// Skip or evaluate the given instruction based on the evaluation policy and
@@ -780,20 +779,11 @@ static SILValue emitCodeForSymbolicValue(SymbolicValue symVal,
780779
}
781780
}
782781

783-
/// Collect the end points of the instructions that are data dependent on \c
784-
/// value. A instruction is data dependent on \c value if its result may
785-
/// transitively depends on \c value. Note that data dependencies through
786-
/// addresses are not tracked by this function.
787-
///
788-
/// \param value SILValue that is not an address.
789-
/// \param fun SILFunction that defines \c value.
790-
/// \param endUsers buffer for storing the found end points of the data
791-
/// dependence chain.
792-
static void
793-
getEndPointsOfDataDependentChain(SILValue value, SILFunction *fun,
794-
SmallVectorImpl<SILInstruction *> &endUsers) {
795-
assert(!value->getType().isAddress());
796-
782+
/// Given a SILValue \p value, compute the set of transitive users of the value
783+
/// (excluding value itself) by following the use-def chain starting at value.
784+
/// Note that this function does not follow use-def chains though branches.
785+
static void getTransitiveUsers(SILValue value,
786+
SmallVectorImpl<SILInstruction *> &users) {
797787
// Collect the instructions that are data dependent on the value using a
798788
// fix point iteration.
799789
SmallPtrSet<SILInstruction *, 16> visitedUsers;
@@ -810,17 +800,40 @@ getEndPointsOfDataDependentChain(SILValue value, SILFunction *fun,
810800
llvm::copy(user->getResults(), std::back_inserter(worklist));
811801
}
812802
}
813-
814803
// At this point, visitedUsers have all the transitive, data-dependent uses.
815-
// Compute the lifetime frontier of all the uses which are the instructions
816-
// following the last uses. Every exit from the last uses will have a
817-
// lifetime frontier.
804+
users.append(visitedUsers.begin(), visitedUsers.end());
805+
}
806+
807+
/// Collect the end points of the instructions that are data dependent on \c
808+
/// value. A instruction is data dependent on \c value if its result may
809+
/// transitively depends on \c value. Note that data dependencies through
810+
/// addresses are not tracked by this function.
811+
///
812+
/// \param value SILValue that is not an address.
813+
/// \param fun SILFunction that defines \c value.
814+
/// \param endUsers buffer for storing the found end points of the data
815+
/// dependence chain.
816+
static void
817+
getEndPointsOfDataDependentChain(SILValue value, SILFunction *fun,
818+
SmallVectorImpl<SILInstruction *> &endUsers) {
819+
assert(!value->getType().isAddress());
820+
821+
SmallVector<SILInstruction *, 16> transitiveUsers;
822+
// Get transitive users of value, ignoring use-def chain going through
823+
// branches. These transitive users define the end points of the constant
824+
// evaluation. Igoring use-def chains through branches causes constant
825+
// evaluation to miss some constant folding opportunities. This can be
826+
// relaxed in the future, if necessary.
827+
getTransitiveUsers(value, transitiveUsers);
828+
829+
// Compute the lifetime frontier of all the transitive uses which are the
830+
// instructions following the last uses. Every exit from the last uses will
831+
// have a lifetime frontier.
818832
SILInstruction *valueDefinition = value->getDefiningInstruction();
819833
SILInstruction *def =
820834
valueDefinition ? valueDefinition : &(value->getParentBlock()->front());
821835
ValueLifetimeAnalysis lifetimeAnalysis =
822-
ValueLifetimeAnalysis(def, SmallVector<SILInstruction *, 16>(
823-
visitedUsers.begin(), visitedUsers.end()));
836+
ValueLifetimeAnalysis(def, transitiveUsers);
824837
ValueLifetimeAnalysis::Frontier frontier;
825838
bool hasCriticlEdges = lifetimeAnalysis.computeFrontier(
826839
frontier, ValueLifetimeAnalysis::DontModifyCFG);
@@ -936,7 +949,7 @@ static void replaceAllUsesAndFixLifetimes(SILValue foldedVal,
936949
static void substituteConstants(FoldState &foldState) {
937950
ConstExprStepEvaluator &evaluator = foldState.constantEvaluator;
938951
// Instructions that are possibly dead since their results are folded.
939-
SmallVector<SILInstruction *, 4> possiblyDeadInsts;
952+
SmallVector<SILInstruction *, 8> possiblyDeadInsts;
940953

941954
for (SILValue constantSILValue : foldState.getConstantSILValues()) {
942955
SymbolicValue constantSymbolicVal =
@@ -980,13 +993,11 @@ static void substituteConstants(FoldState &foldState) {
980993
replaceAllUsesAndFixLifetimes(foldedSILVal, constantSILValue, fun);
981994
possiblyDeadInsts.push_back(definingInst);
982995
}
983-
recursivelyDeleteTriviallyDeadInstructions(possiblyDeadInsts, /*force*/ false,
984-
[&](SILInstruction *DeadI) {});
985996
}
986997

987998
/// Check whether OSLogMessage and OSLogInterpolation instances and all their
988999
/// stored properties are constants. If not, it indicates errors that are due to
989-
/// incorrect implementation OSLogMessage either in the overlay or in the
1000+
/// incorrect implementation of OSLogMessage either in the overlay or in the
9901001
/// extensions created by users. Detect and emit diagnostics for such errors.
9911002
/// The diagnostics here are for os log library authors.
9921003
static bool checkOSLogMessageIsConstant(SingleValueInstruction *osLogMessage,
@@ -1048,6 +1059,37 @@ static bool checkOSLogMessageIsConstant(SingleValueInstruction *osLogMessage,
10481059
return errorDetected;
10491060
}
10501061

1062+
/// Try to dead-code eliminate the OSLogMessage instance \c oslogMessage passed
1063+
/// to the os log call and clean up its dependencies. If the instance cannot be
1064+
/// eliminated, it implies that either the instance is not auto-generated or the
1065+
/// implementation of the os log overlay is incorrect. Therefore emit
1066+
/// diagnostics in such cases.
1067+
static void tryEliminateOSLogMessage(SingleValueInstruction *oslogMessage) {
1068+
// Collect the set of root instructions that could be dead due to constant
1069+
// folding. These include the oslogMessage initialzer call and its transitive
1070+
// users.
1071+
SmallVector<SILInstruction *, 8> oslogMessageUsers;
1072+
getTransitiveUsers(oslogMessage, oslogMessageUsers);
1073+
1074+
InstructionDeleter deleter;
1075+
for (SILInstruction *user : oslogMessageUsers)
1076+
deleter.trackIfDead(user);
1077+
deleter.trackIfDead(oslogMessage);
1078+
1079+
bool isOSLogMessageDead = false;
1080+
deleter.cleanUpDeadInstructions([&](SILInstruction *deadInst) {
1081+
if (deadInst == oslogMessage)
1082+
isOSLogMessageDead = true;
1083+
});
1084+
// At this point, the OSLogMessage instance must be deleted if
1085+
// the overlay implementation (or its extensions by users) is correct.
1086+
if (!isOSLogMessageDead) {
1087+
SILFunction *fun = oslogMessage->getFunction();
1088+
diagnose(fun->getASTContext(), oslogMessage->getLoc().getSourceLoc(),
1089+
diag::oslog_message_alive_after_opts);
1090+
}
1091+
}
1092+
10511093
/// Constant evaluate instructions starting from 'start' and fold the uses
10521094
/// of the value 'oslogMessage'. Stop when oslogMessageValue is released.
10531095
static bool constantFold(SILInstruction *start,
@@ -1076,6 +1118,8 @@ static bool constantFold(SILInstruction *start,
10761118
return false;
10771119

10781120
substituteConstants(state);
1121+
1122+
tryEliminateOSLogMessage(oslogMessage);
10791123
return true;
10801124
}
10811125

lib/SILOptimizer/Mandatory/PredictableMemOpt.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2118,7 +2118,7 @@ bool AllocOptimize::promoteLoadCopy(LoadInst *li) {
21182118
SILValue addr = li->getOperand();
21192119
li->eraseFromParent();
21202120
if (auto *addrI = addr->getDefiningInstruction())
2121-
recursivelyDeleteTriviallyDeadInstructions(addrI);
2121+
eliminateDeadInstruction(addrI);
21222122
return true;
21232123
}
21242124

@@ -2139,7 +2139,7 @@ bool AllocOptimize::promoteLoadCopy(LoadInst *li) {
21392139
SILValue addr = li->getOperand();
21402140
li->eraseFromParent();
21412141
if (auto *addrI = addr->getDefiningInstruction())
2142-
recursivelyDeleteTriviallyDeadInstructions(addrI);
2142+
eliminateDeadInstruction(addrI);
21432143
return true;
21442144
}
21452145

@@ -2242,7 +2242,7 @@ bool AllocOptimize::promoteLoadBorrow(LoadBorrowInst *lbi) {
22422242
SILValue addr = lbi->getOperand();
22432243
lbi->eraseFromParent();
22442244
if (auto *addrI = addr->getDefiningInstruction())
2245-
recursivelyDeleteTriviallyDeadInstructions(addrI);
2245+
eliminateDeadInstruction(addrI);
22462246
return true;
22472247
}
22482248

lib/SILOptimizer/Mandatory/SILGenCleanup.cpp

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -52,14 +52,12 @@ struct SILGenCanonicalize final : CanonicalizeInstruction {
5252
SILInstruction *deadOperInst = *deadOperands.begin();
5353
// Make sure at least the first instruction is removed from the set.
5454
deadOperands.erase(deadOperInst);
55-
recursivelyDeleteTriviallyDeadInstructions(
56-
deadOperInst, false,
57-
[&](SILInstruction *deadInst) {
58-
LLVM_DEBUG(llvm::dbgs() << "Trivially dead: " << *deadInst);
59-
if (nextII == deadInst->getIterator())
60-
++nextII;
61-
deadOperands.erase(deadInst);
62-
});
55+
eliminateDeadInstruction(deadOperInst, [&](SILInstruction *deadInst) {
56+
LLVM_DEBUG(llvm::dbgs() << "Trivially dead: " << *deadInst);
57+
if (nextII == deadInst->getIterator())
58+
++nextII;
59+
deadOperands.erase(deadInst);
60+
});
6361
}
6462
return nextII;
6563
}

0 commit comments

Comments
 (0)