@@ -79,6 +79,10 @@ class TempRValueOptPass : public SILFunctionTransform {
79
79
checkTempObjectDestroy (AllocStackInst *tempObj, CopyAddrInst *copyInst,
80
80
ValueLifetimeAnalysis::Frontier &tempAddressFrontier);
81
81
82
+ bool checkNoTempObjectModificationInApply (Operand *tempObjUser,
83
+ SILInstruction *inst,
84
+ SILValue srcAddr);
85
+
82
86
bool tryOptimizeCopyIntoTemp (CopyAddrInst *copyInst);
83
87
std::pair<SILBasicBlock::iterator, bool >
84
88
tryOptimizeStoreIntoTemp (StoreInst *si);
@@ -111,6 +115,62 @@ bool TempRValueOptPass::collectLoadsFromProjection(
111
115
return true ;
112
116
}
113
117
118
+ // / Check if 'tempObjUser' passed to the apply instruction can be modified by it
119
+ bool TempRValueOptPass::checkNoTempObjectModificationInApply (
120
+ Operand *tempObjUser, SILInstruction *applyInst, SILValue srcAddr) {
121
+ ApplySite apply (applyInst);
122
+
123
+ // Check if the function can just read from tempObjUser.
124
+ auto convention = apply.getArgumentConvention (*tempObjUser);
125
+ if (!convention.isGuaranteedConvention ()) {
126
+ LLVM_DEBUG (llvm::dbgs () << " Temp consuming use may write/destroy "
127
+ " its source"
128
+ << *applyInst);
129
+ return false ;
130
+ }
131
+
132
+ // If we do not have an src address, but are indirect, bail. We would need
133
+ // to perform function signature specialization to change the functions
134
+ // signature to pass something direct.
135
+ if (!srcAddr && convention.isIndirectConvention ()) {
136
+ LLVM_DEBUG (
137
+ llvm::dbgs ()
138
+ << " Temp used to materialize value for indirect convention?! Can "
139
+ " not remove temporary without func sig opts"
140
+ << *applyInst);
141
+ return false ;
142
+ }
143
+
144
+ // Check if there is another function argument, which is inout which might
145
+ // modify the source address if we have one.
146
+ //
147
+ // When a use of the temporary is an apply, then we need to prove that the
148
+ // function called by the apply cannot modify the temporary's source
149
+ // value. By design, this should be handled by
150
+ // `checkNoSourceModification`. However, this would be too conservative
151
+ // since it's common for the apply to have an @out argument, and alias
152
+ // analysis cannot prove that the @out does not alias with `src`. Instead,
153
+ // `checkNoSourceModification` always avoids analyzing the current use, so
154
+ // applies need to be handled here. We already know that an @out cannot
155
+ // alias with `src` because the `src` value must be initialized at the point
156
+ // of the call. Hence, it is sufficient to check specifically for another
157
+ // @inout that might alias with `src`.
158
+ if (srcAddr) {
159
+ auto calleeConv = apply.getSubstCalleeConv ();
160
+ unsigned calleeArgIdx = apply.getCalleeArgIndexOfFirstAppliedArg ();
161
+ for (const auto &operand : apply.getArgumentOperands ()) {
162
+ auto argConv = calleeConv.getSILArgumentConvention (calleeArgIdx);
163
+ if (argConv.isInoutConvention ()) {
164
+ if (!aa->isNoAlias (operand.get (), srcAddr)) {
165
+ return false ;
166
+ }
167
+ }
168
+ ++calleeArgIdx;
169
+ }
170
+ }
171
+ return true ;
172
+ }
173
+
114
174
// / Transitively explore all data flow uses of the given \p address until
115
175
// / reaching a load or returning false.
116
176
// /
@@ -172,59 +232,23 @@ bool TempRValueOptPass::collectLoads(
172
232
LLVM_FALLTHROUGH;
173
233
case SILInstructionKind::ApplyInst:
174
234
case SILInstructionKind::TryApplyInst: {
175
- ApplySite apply (user);
176
-
177
- // Check if the function can just read from userOp.
178
- auto convention = apply.getArgumentConvention (*userOp);
179
- if (!convention.isGuaranteedConvention ()) {
180
- LLVM_DEBUG (llvm::dbgs () << " Temp consuming use may write/destroy "
181
- " its source"
182
- << *user);
235
+ if (!checkNoTempObjectModificationInApply (userOp, user, srcAddr))
183
236
return false ;
184
- }
185
-
186
- // If we do not have an src address, but are indirect, bail. We would need
187
- // to perform function signature specialization to change the functions
188
- // signature to pass something direct.
189
- if (!srcAddr && convention.isIndirectConvention ()) {
190
- LLVM_DEBUG (
191
- llvm::dbgs ()
192
- << " Temp used to materialize value for indirect convention?! Can "
193
- " not remove temporary without func sig opts"
194
- << *user);
237
+ // Everything is okay with the function call. Register it as a "load".
238
+ loadInsts.insert (user);
239
+ return true ;
240
+ }
241
+ case SILInstructionKind::BeginApplyInst: {
242
+ if (!checkNoTempObjectModificationInApply (userOp, user, srcAddr))
195
243
return false ;
196
- }
197
244
198
- // Check if there is another function argument, which is inout which might
199
- // modify the source address if we have one.
200
- //
201
- // When a use of the temporary is an apply, then we need to prove that the
202
- // function called by the apply cannot modify the temporary's source
203
- // value. By design, this should be handled by
204
- // `checkNoSourceModification`. However, this would be too conservative
205
- // since it's common for the apply to have an @out argument, and alias
206
- // analysis cannot prove that the @out does not alias with `src`. Instead,
207
- // `checkNoSourceModification` always avoids analyzing the current use, so
208
- // applies need to be handled here. We already know that an @out cannot
209
- // alias with `src` because the `src` value must be initialized at the point
210
- // of the call. Hence, it is sufficient to check specifically for another
211
- // @inout that might alias with `src`.
212
- if (srcAddr) {
213
- auto calleeConv = apply.getSubstCalleeConv ();
214
- unsigned calleeArgIdx = apply.getCalleeArgIndexOfFirstAppliedArg ();
215
- for (const auto &operand : apply.getArgumentOperands ()) {
216
- auto argConv = calleeConv.getSILArgumentConvention (calleeArgIdx);
217
- if (argConv.isInoutConvention ()) {
218
- if (!aa->isNoAlias (operand.get (), srcAddr)) {
219
- return false ;
220
- }
221
- }
222
- ++calleeArgIdx;
223
- }
245
+ auto beginApply = cast<BeginApplyInst>(user);
246
+ // Register 'end_apply'/'abort_apply' as loads as well
247
+ // 'checkNoSourceModification' should check instructions until
248
+ // 'end_apply'/'abort_apply'.
249
+ for (auto tokenUses : beginApply->getTokenResult ()->getUses ()) {
250
+ loadInsts.insert (tokenUses->getUser ());
224
251
}
225
-
226
- // Everything is okay with the function call. Register it as a "load".
227
- loadInsts.insert (user);
228
252
return true ;
229
253
}
230
254
case SILInstructionKind::OpenExistentialAddrInst: {
0 commit comments