@@ -79,9 +79,8 @@ 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);
82
+ bool canApplyBeTreatedAsLoad (Operand *tempObjUser, ApplySite apply,
83
+ SILValue srcAddr);
85
84
86
85
bool tryOptimizeCopyIntoTemp (CopyAddrInst *copyInst);
87
86
std::pair<SILBasicBlock::iterator, bool >
@@ -115,57 +114,39 @@ bool TempRValueOptPass::collectLoadsFromProjection(
115
114
return true ;
116
115
}
117
116
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
-
117
+ // / Check if \p tempObjUser, passed to the apply instruction, is only loaded,
118
+ // / but not modified and if \p srcAddr is not modified as well.
119
+ bool TempRValueOptPass::canApplyBeTreatedAsLoad (
120
+ Operand *tempObjUser, ApplySite apply, SILValue srcAddr) {
123
121
// Check if the function can just read from tempObjUser.
124
122
auto convention = apply.getArgumentConvention (*tempObjUser);
125
123
if (!convention.isGuaranteedConvention ()) {
126
124
LLVM_DEBUG (llvm::dbgs () << " Temp consuming use may write/destroy "
127
125
" 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);
126
+ << *apply.getInstruction ());
141
127
return false ;
142
128
}
143
129
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
130
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;
131
+ // If the function may write to the source of the copy_addr, the apply
132
+ // cannot be treated as a load: all (potential) writes of the source must
133
+ // appear _after_ all loads of the temporary. But in case of a function call
134
+ // we don't know in which order the writes and loads are executed inside the
135
+ // called function. The source may be written before the temporary is
136
+ // loaded, which would make the optization invalid.
137
+ if (aa->mayWriteToMemory (apply.getInstruction (), srcAddr))
138
+ return false ;
139
+ } else {
140
+ // If we do not have an src address, but are indirect, bail. We would need
141
+ // to perform function signature specialization to change the functions
142
+ // signature to pass something direct.
143
+ if (convention.isIndirectConvention ()) {
144
+ LLVM_DEBUG (
145
+ llvm::dbgs ()
146
+ << " Temp used to materialize value for indirect convention?! Can "
147
+ " not remove temporary without func sig opts"
148
+ << *apply.getInstruction ());
149
+ return false ;
169
150
}
170
151
}
171
152
return true ;
@@ -189,7 +170,8 @@ bool TempRValueOptPass::collectLoads(
189
170
SILValue srcAddr, SmallPtrSetImpl<SILInstruction *> &loadInsts) {
190
171
// All normal uses (loads) must be in the initialization block.
191
172
// (The destroy and dealloc are commonly in a different block though.)
192
- if (user->getParent () != address->getParent ())
173
+ SILBasicBlock *block = address->getParent ();
174
+ if (user->getParent () != block)
193
175
return false ;
194
176
195
177
// Only allow uses that cannot destroy their operand. We need to be sure
@@ -232,22 +214,25 @@ bool TempRValueOptPass::collectLoads(
232
214
LLVM_FALLTHROUGH;
233
215
case SILInstructionKind::ApplyInst:
234
216
case SILInstructionKind::TryApplyInst: {
235
- if (!checkNoTempObjectModificationInApply (userOp, user, srcAddr))
217
+ if (!canApplyBeTreatedAsLoad (userOp, ApplySite ( user) , srcAddr))
236
218
return false ;
237
219
// Everything is okay with the function call. Register it as a "load".
238
220
loadInsts.insert (user);
239
221
return true ;
240
222
}
241
223
case SILInstructionKind::BeginApplyInst: {
242
- if (!checkNoTempObjectModificationInApply (userOp, user, srcAddr))
224
+ if (!canApplyBeTreatedAsLoad (userOp, ApplySite ( user) , srcAddr))
243
225
return false ;
244
226
245
227
auto beginApply = cast<BeginApplyInst>(user);
246
228
// Register 'end_apply'/'abort_apply' as loads as well
247
229
// 'checkNoSourceModification' should check instructions until
248
230
// 'end_apply'/'abort_apply'.
249
- for (auto tokenUses : beginApply->getTokenResult ()->getUses ()) {
250
- loadInsts.insert (tokenUses->getUser ());
231
+ for (auto tokenUse : beginApply->getTokenResult ()->getUses ()) {
232
+ SILInstruction *user = tokenUse->getUser ();
233
+ if (user->getParent () != block)
234
+ return false ;
235
+ loadInsts.insert (tokenUse->getUser ());
251
236
}
252
237
return true ;
253
238
}
@@ -285,7 +270,8 @@ bool TempRValueOptPass::collectLoads(
285
270
return collectLoadsFromProjection (utedai, srcAddr, loadInsts);
286
271
}
287
272
case SILInstructionKind::StructElementAddrInst:
288
- case SILInstructionKind::TupleElementAddrInst: {
273
+ case SILInstructionKind::TupleElementAddrInst:
274
+ case SILInstructionKind::UncheckedAddrCastInst: {
289
275
return collectLoadsFromProjection (cast<SingleValueInstruction>(user),
290
276
srcAddr, loadInsts);
291
277
}
0 commit comments