@@ -1059,31 +1059,152 @@ static bool checkOSLogMessageIsConstant(SingleValueInstruction *osLogMessage,
1059
1059
return errorDetected;
1060
1060
}
1061
1061
1062
+ using CallbackTy = llvm::function_ref<void (SILInstruction *)>;
1063
+
1064
+ // / Return true iff the given address-valued instruction has only stores into
1065
+ // / it. This function tests for the conditions under which a call, that was
1066
+ // / constant evaluated, that writes into the address-valued instruction can be
1067
+ // / considered as a point store and exploits it to remove such uses.
1068
+ // / TODO: eventually some of this logic can be moved to
1069
+ // / PredictableDeadAllocElimination pass, but the assumption about constant
1070
+ // / evaluable functions taking inout parameters is not easily generalizable to
1071
+ // / arbitrary non-constant contexts where the function could be used. The logic
1072
+ // / here is relying on the fact that the constant_evaluable function has been
1073
+ // / evaluated and therefore doesn't have any side-effects.
1074
+ static bool hasOnlyStoreUses (SingleValueInstruction *addressInst) {
1075
+ for (Operand *use : addressInst->getUses ()) {
1076
+ SILInstruction *user = use->getUser ();
1077
+ switch (user->getKind ()) {
1078
+ default :
1079
+ return false ;
1080
+ case SILInstructionKind::BeginAccessInst: {
1081
+ if (!hasOnlyStoreUses (cast<BeginAccessInst>(user)))
1082
+ return false ;
1083
+ continue ;
1084
+ }
1085
+ case SILInstructionKind::StoreInst: {
1086
+ // For now, ignore assigns as we need to destroy_addr its dest if it
1087
+ // is deleted.
1088
+ if (cast<StoreInst>(user)->getOwnershipQualifier () ==
1089
+ StoreOwnershipQualifier::Assign)
1090
+ return false ;
1091
+ continue ;
1092
+ }
1093
+ case SILInstructionKind::EndAccessInst:
1094
+ case SILInstructionKind::DestroyAddrInst:
1095
+ case SILInstructionKind::InjectEnumAddrInst:
1096
+ case SILInstructionKind::DeallocStackInst:
1097
+ continue ;
1098
+ case SILInstructionKind::ApplyInst: {
1099
+ ApplyInst *apply = cast<ApplyInst>(user);
1100
+ SILFunction *callee = apply->getCalleeFunction ();
1101
+ if (!callee || !isConstantEvaluable (callee) || !apply->use_empty ())
1102
+ return false ;
1103
+ // Note that since we are looking at an alloc_stack used to produce the
1104
+ // OSLogMessage instance, this constant_evaluable call should have been
1105
+ // evaluated successfully by the evaluator. Otherwise, we would have
1106
+ // reported an error earlier. Therefore, all values manipulated by such
1107
+ // a call are symbolic constants and the call would not have any global
1108
+ // side effects. The following logic relies on this property.
1109
+ // If there are other indirect writable results for the call other than
1110
+ // the alloc_stack we are checking, it may not be dead. Therefore, bail
1111
+ // out.
1112
+ FullApplySite applySite (apply);
1113
+ unsigned numWritableArguments =
1114
+ getNumInOutArguments (applySite) + applySite.getNumIndirectSILResults ();
1115
+ if (numWritableArguments > 1 )
1116
+ return false ;
1117
+ SILArgumentConvention convention = applySite.getArgumentConvention (*use);
1118
+ if (convention == SILArgumentConvention::Indirect_In_Guaranteed ||
1119
+ convention == SILArgumentConvention::Indirect_In_Constant ||
1120
+ convention == SILArgumentConvention::Indirect_In_Guaranteed) {
1121
+ if (numWritableArguments > 0 )
1122
+ return false ;
1123
+ }
1124
+ // Here, either there are no writable parameters or the alloc_stack
1125
+ // is the only writable parameter.
1126
+ continue ;
1127
+ }
1128
+ }
1129
+ }
1130
+ return true ;
1131
+ }
1132
+
1133
+ // / Delete the given alloc_stack instruction by deleting the users of the
1134
+ // / instruction. In case the user is a begin_apply, recursively delete the users
1135
+ // / of begin_apply. This will also fix the lifetimes of the deleted instructions
1136
+ // / whenever possible.
1137
+ static void forceDeleteAllocStack (SingleValueInstruction *inst,
1138
+ InstructionDeleter &deleter,
1139
+ CallbackTy callback) {
1140
+ SmallVector<SILInstruction *, 8 > users;
1141
+ for (Operand *use : inst->getUses ())
1142
+ users.push_back (use->getUser ());
1143
+
1144
+ for (SILInstruction *user : users) {
1145
+ if (isIncidentalUse (user))
1146
+ continue ;
1147
+ if (isa<DestroyAddrInst>(user)) {
1148
+ deleter.forceDelete (user, callback);
1149
+ continue ;
1150
+ }
1151
+ if (isa<BeginAccessInst>(user)) {
1152
+ forceDeleteAllocStack (cast<BeginAccessInst>(user), deleter, callback);
1153
+ continue ;
1154
+ }
1155
+ deleter.forceDeleteAndFixLifetimes (user, callback);
1156
+ }
1157
+ deleter.forceDelete (inst, callback);
1158
+ }
1159
+
1160
+ // / Delete \c inst , if it is dead, along with its dead users and invoke the
1161
+ // / callback whever an instruction is deleted.
1162
+ static void deleteInstructionWithUsersAndFixLifetimes (
1163
+ SILInstruction *inst, InstructionDeleter &deleter, CallbackTy callback) {
1164
+ // If this is an alloc_stack, it can be eliminated as long as it is only
1165
+ // stored into or destroyed.
1166
+ if (AllocStackInst *allocStack = dyn_cast<AllocStackInst>(inst)) {
1167
+ if (hasOnlyStoreUses (allocStack))
1168
+ forceDeleteAllocStack (allocStack, deleter, callback);
1169
+ return ;
1170
+ }
1171
+ deleter.recursivelyDeleteUsersIfDead (inst, callback);
1172
+ }
1173
+
1062
1174
// / Try to dead-code eliminate the OSLogMessage instance \c oslogMessage passed
1063
1175
// / to the os log call and clean up its dependencies. If the instance cannot be
1064
1176
// / eliminated, it implies that either the instance is not auto-generated or the
1065
1177
// / implementation of the os log overlay is incorrect. Therefore emit
1066
1178
// / diagnostics in such cases.
1067
1179
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
1180
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) {
1181
+ // List of instructions that are possibly dead.
1182
+ SmallVector<SILInstruction *, 4 > worklist = {oslogMessage};
1183
+ // Set of all deleted instructions.
1184
+ SmallPtrSet<SILInstruction *, 4 > deletedInstructions;
1185
+ unsigned startIndex = 0 ;
1186
+ while (startIndex < worklist.size ()) {
1187
+ SILInstruction *inst = worklist[startIndex++];
1188
+ if (deletedInstructions.count (inst))
1189
+ continue ;
1190
+ deleteInstructionWithUsersAndFixLifetimes (
1191
+ inst, deleter, [&](SILInstruction *deadInst) {
1192
+ // Add operands of all deleted instructions to the worklist so that
1193
+ // they can be recursively deleted if possible.
1194
+ for (Operand &operand : deadInst->getAllOperands ()) {
1195
+ if (SILInstruction *definingInstruction =
1196
+ operand.get ()->getDefiningInstruction ()) {
1197
+ if (!deletedInstructions.count (definingInstruction))
1198
+ worklist.push_back (definingInstruction);
1199
+ }
1200
+ }
1201
+ (void )deletedInstructions.insert (deadInst);
1202
+ });
1203
+ }
1204
+ deleter.cleanUpDeadInstructions ();
1205
+ // If the OSLogMessage instance is not deleted, the overlay implementation
1206
+ // (or its extensions by users) is incorrect.
1207
+ if (!deletedInstructions.count (oslogMessage)) {
1087
1208
SILFunction *fun = oslogMessage->getFunction ();
1088
1209
diagnose (fun->getASTContext (), oslogMessage->getLoc ().getSourceLoc (),
1089
1210
diag::oslog_message_alive_after_opts);
0 commit comments