Skip to content

Commit 4613d94

Browse files
authored
Handle begin_apply in TempRVO (#31063)
* Handle begin_apply in TempRVO A tempobj passed to begin_apply instruction and cannot be modified by it (is guaranteed and doesn't alias with other inout args) can be optimzed away.
1 parent def42e9 commit 4613d94

File tree

1 file changed

+73
-49
lines changed

1 file changed

+73
-49
lines changed

lib/SILOptimizer/Transforms/TempRValueElimination.cpp

Lines changed: 73 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,10 @@ class TempRValueOptPass : public SILFunctionTransform {
7979
checkTempObjectDestroy(AllocStackInst *tempObj, CopyAddrInst *copyInst,
8080
ValueLifetimeAnalysis::Frontier &tempAddressFrontier);
8181

82+
bool checkNoTempObjectModificationInApply(Operand *tempObjUser,
83+
SILInstruction *inst,
84+
SILValue srcAddr);
85+
8286
bool tryOptimizeCopyIntoTemp(CopyAddrInst *copyInst);
8387
std::pair<SILBasicBlock::iterator, bool>
8488
tryOptimizeStoreIntoTemp(StoreInst *si);
@@ -111,6 +115,62 @@ bool TempRValueOptPass::collectLoadsFromProjection(
111115
return true;
112116
}
113117

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+
114174
/// Transitively explore all data flow uses of the given \p address until
115175
/// reaching a load or returning false.
116176
///
@@ -172,59 +232,23 @@ bool TempRValueOptPass::collectLoads(
172232
LLVM_FALLTHROUGH;
173233
case SILInstructionKind::ApplyInst:
174234
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))
183236
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))
195243
return false;
196-
}
197244

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());
224251
}
225-
226-
// Everything is okay with the function call. Register it as a "load".
227-
loadInsts.insert(user);
228252
return true;
229253
}
230254
case SILInstructionKind::OpenExistentialAddrInst: {

0 commit comments

Comments
 (0)