Skip to content

Commit d4a6bd3

Browse files
committed
SILOptimizer: improve MemBehavior for apply instructions.
1. Do a better alias analysis for "function-local" objects, like alloc_stack and inout parameters 2. Fully support try_apply and begin/end/abort_apply So far we fully relied on escape analysis. But escape analysis has some shortcomings with SIL address-types. Therefore, handle two common cases, alloc_stack and inout parameters, with alias analysis. This gives better results. The biggest change here is to do a quick check if the address escapes via an address_to_pointer instructions.
1 parent aced5c7 commit d4a6bd3

File tree

6 files changed

+324
-52
lines changed

6 files changed

+324
-52
lines changed

include/swift/SILOptimizer/Analysis/SideEffectAnalysis.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -389,6 +389,13 @@ class FunctionSideEffects {
389389
/// instructions are considered as side effects.
390390
MemoryBehavior getMemBehavior(RetainObserveKind ScanKind) const;
391391

392+
/// Gets the memory behavior for an argument.
393+
///
394+
/// This is derived from the combined argument and the global effects.
395+
/// Also the argument type and convention are considered.
396+
MemoryBehavior getArgumentBehavior(FullApplySite applySite,
397+
unsigned argIdx);
398+
392399
/// Get the global effects for the function. These are effects which cannot
393400
/// be associated to a specific parameter, e.g. writes to global variables
394401
/// or writes to unknown pointers.

lib/SILOptimizer/Analysis/MemoryBehavior.cpp

Lines changed: 120 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,10 @@ class MemoryBehaviorVisitor
172172
MemBehavior visitCopyAddrInst(CopyAddrInst *CAI);
173173
MemBehavior visitApplyInst(ApplyInst *AI);
174174
MemBehavior visitTryApplyInst(TryApplyInst *AI);
175+
MemBehavior visitBeginApplyInst(BeginApplyInst *AI);
176+
MemBehavior visitEndApplyInst(EndApplyInst *EAI);
177+
MemBehavior visitAbortApplyInst(AbortApplyInst *AAI);
178+
MemBehavior getApplyBehavior(FullApplySite AS);
175179
MemBehavior visitBuiltinInst(BuiltinInst *BI);
176180
MemBehavior visitStrongReleaseInst(StrongReleaseInst *BI);
177181
MemBehavior visitReleaseValueInst(ReleaseValueInst *BI);
@@ -326,70 +330,137 @@ MemBehavior MemoryBehaviorVisitor::visitBuiltinInst(BuiltinInst *BI) {
326330
}
327331

328332
MemBehavior MemoryBehaviorVisitor::visitTryApplyInst(TryApplyInst *AI) {
329-
MemBehavior Behavior = MemBehavior::MayHaveSideEffects;
330-
// Ask escape analysis.
331-
if (!EA->canEscapeTo(V, AI))
332-
Behavior = MemBehavior::None;
333-
334-
// Otherwise be conservative and return that we may have side effects.
335-
LLVM_DEBUG(llvm::dbgs() << " Found tryapply, returning " << Behavior <<'\n');
336-
return Behavior;
333+
return getApplyBehavior(AI);
337334
}
338335

339336
MemBehavior MemoryBehaviorVisitor::visitApplyInst(ApplyInst *AI) {
337+
return getApplyBehavior(AI);
338+
}
340339

341-
FunctionSideEffects ApplyEffects;
342-
SEA->getCalleeEffects(ApplyEffects, AI);
340+
MemBehavior MemoryBehaviorVisitor::visitBeginApplyInst(BeginApplyInst *AI) {
341+
return getApplyBehavior(AI);
342+
}
343+
344+
MemBehavior MemoryBehaviorVisitor::visitEndApplyInst(EndApplyInst *EAI) {
345+
return getApplyBehavior(EAI->getBeginApply());
346+
}
347+
348+
MemBehavior MemoryBehaviorVisitor::visitAbortApplyInst(AbortApplyInst *AAI) {
349+
return getApplyBehavior(AAI->getBeginApply());
350+
}
351+
352+
/// Returns true if the \p address may have any users which let the address
353+
/// escape in an unusual way, e.g. with an address_to_pointer instruction.
354+
static bool hasEscapingUses(SILValue address, int &numChecks) {
355+
for (Operand *use : address->getUses()) {
356+
SILInstruction *user = use->getUser();
357+
358+
// Avoid quadratic complexity in corner cases. A limit of 24 is more than
359+
// enough in most cases.
360+
if (++numChecks > 24)
361+
return true;
362+
363+
switch (user->getKind()) {
364+
case SILInstructionKind::DebugValueAddrInst:
365+
case SILInstructionKind::FixLifetimeInst:
366+
case SILInstructionKind::LoadInst:
367+
case SILInstructionKind::StoreInst:
368+
case SILInstructionKind::CopyAddrInst:
369+
case SILInstructionKind::DestroyAddrInst:
370+
case SILInstructionKind::DeallocStackInst:
371+
// Those instructions have no result and cannot escape the address.
372+
break;
373+
case SILInstructionKind::ApplyInst:
374+
case SILInstructionKind::TryApplyInst:
375+
case SILInstructionKind::BeginApplyInst:
376+
// Apply instructions can not let an address escape either. It's not
377+
// possible that an address, passed as an indirect parameter, escapes
378+
// the function in any way (which is not unsafe and undefined behavior).
379+
break;
380+
case SILInstructionKind::OpenExistentialAddrInst:
381+
case SILInstructionKind::UncheckedTakeEnumDataAddrInst:
382+
case SILInstructionKind::StructElementAddrInst:
383+
case SILInstructionKind::TupleElementAddrInst:
384+
case SILInstructionKind::UncheckedAddrCastInst:
385+
// Check the uses of address projections.
386+
if (hasEscapingUses(cast<SingleValueInstruction>(user), numChecks))
387+
return true;
388+
break;
389+
case SILInstructionKind::AddressToPointerInst:
390+
// This is _the_ instruction which can let an address escape.
391+
return true;
392+
default:
393+
// To be conservative, also bail for anything we don't handle here.
394+
return true;
395+
}
396+
}
397+
return false;
398+
}
343399

344-
MemBehavior Behavior = MemBehavior::None;
400+
MemBehavior MemoryBehaviorVisitor::getApplyBehavior(FullApplySite AS) {
345401

346-
// We can ignore mayTrap().
347-
bool any_in_guaranteed_params = false;
348-
for (auto op : enumerate(AI->getArgumentOperands())) {
349-
if (op.value().get() == V &&
350-
AI->getSubstCalleeConv().getSILArgumentConvention(op.index()) == swift::SILArgumentConvention::Indirect_In_Guaranteed) {
351-
any_in_guaranteed_params = true;
352-
break;
402+
// Do a quick check first: if V is directly passed to an in_guaranteed
403+
// argument, we know that the function cannot write to it.
404+
for (Operand &argOp : AS.getArgumentOperands()) {
405+
if (argOp.get() == V &&
406+
AS.getArgumentConvention(argOp) ==
407+
swift::SILArgumentConvention::Indirect_In_Guaranteed) {
408+
return MemBehavior::MayRead;
353409
}
354410
}
355411

356-
if (any_in_guaranteed_params) {
357-
// one the parameters in the function call is @in_guaranteed of V, ie. the
358-
// callee isn't allowed to modify it.
359-
Behavior = MemBehavior::MayRead;
360-
} else {
361-
auto &GlobalEffects = ApplyEffects.getGlobalEffects();
362-
Behavior = GlobalEffects.getMemBehavior(RetainObserveKind::IgnoreRetains);
363-
364-
// Check all parameter effects.
365-
for (unsigned Idx = 0, End = AI->getNumArguments();
366-
Idx < End && Behavior < MemBehavior::MayHaveSideEffects; ++Idx) {
367-
auto &ArgEffect = ApplyEffects.getParameterEffects()[Idx];
368-
auto ArgBehavior = ArgEffect.getMemBehavior(RetainObserveKind::IgnoreRetains);
369-
if (ArgEffect.mayRelease()) {
370-
Behavior = MemBehavior::MayHaveSideEffects;
371-
break;
372-
}
373-
auto NewBehavior = combineMemoryBehavior(Behavior, ArgBehavior);
374-
if (NewBehavior != Behavior) {
375-
SILValue Arg = AI->getArgument(Idx);
376-
// We only consider the argument effects if the argument aliases V.
377-
if (!Arg->getType().isAddress() || mayAlias(Arg))
378-
Behavior = NewBehavior;
379-
}
412+
SILValue object = getUnderlyingObject(V);
413+
int numUsesChecked = 0;
414+
415+
// For exclusive/local addresses we can do a quick and good check with alias
416+
// analysis. For everything else we use escape analysis (see below).
417+
// TODO: The check for not-escaping can probably done easier with the upcoming
418+
// API of AccessStorage.
419+
bool nonEscapingAddress =
420+
(isa<AllocStackInst>(object) || isExclusiveArgument(object)) &&
421+
!hasEscapingUses(object, numUsesChecked);
422+
423+
FunctionSideEffects applyEffects;
424+
SEA->getCalleeEffects(applyEffects, AS);
425+
426+
MemBehavior behavior = MemBehavior::None;
427+
MemBehavior globalBehavior = applyEffects.getGlobalEffects().getMemBehavior(
428+
RetainObserveKind::IgnoreRetains);
429+
430+
// If it's a non-escaping address, we don't care about the "global" effects
431+
// of the called function.
432+
if (!nonEscapingAddress)
433+
behavior = globalBehavior;
434+
435+
// Check all parameter effects.
436+
for (unsigned argIdx = 0, end = AS.getNumArguments();
437+
argIdx < end && behavior < MemBehavior::MayHaveSideEffects;
438+
++argIdx) {
439+
SILValue arg = AS.getArgument(argIdx);
440+
441+
// In case the argument is not an address, alias analysis will always report
442+
// a no-alias. Therefore we have to treat non-address arguments
443+
// conservatively here. For example V could be a ref_element_addr of a
444+
// reference argument. In this case V clearly "aliases" the argument, but
445+
// this is not reported by alias analysis.
446+
if ((!nonEscapingAddress && !arg->getType().isAddress()) ||
447+
mayAlias(arg)) {
448+
MemBehavior argBehavior = applyEffects.getArgumentBehavior(AS, argIdx);
449+
behavior = combineMemoryBehavior(behavior, argBehavior);
380450
}
381451
}
382452

383-
if (Behavior > MemBehavior::None) {
384-
if (Behavior > MemBehavior::MayRead && isLetValue())
385-
Behavior = MemBehavior::MayRead;
453+
if (behavior > MemBehavior::None) {
454+
if (behavior > MemBehavior::MayRead && isLetValue())
455+
behavior = MemBehavior::MayRead;
386456

387457
// Ask escape analysis.
388-
if (!EA->canEscapeTo(V, AI))
389-
Behavior = MemBehavior::None;
458+
if (!nonEscapingAddress && !EA->canEscapeTo(V, AS))
459+
behavior = MemBehavior::None;
390460
}
391-
LLVM_DEBUG(llvm::dbgs() << " Found apply, returning " << Behavior << '\n');
392-
return Behavior;
461+
LLVM_DEBUG(llvm::dbgs() << " Found apply, returning " << behavior << '\n');
462+
463+
return behavior;
393464
}
394465

395466
MemBehavior

lib/SILOptimizer/Analysis/SideEffectAnalysis.cpp

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,34 @@ FunctionSideEffects::getMemBehavior(RetainObserveKind ScanKind) const {
219219
return Behavior;
220220
}
221221

222+
MemoryBehavior
223+
FunctionSideEffects::getArgumentBehavior(FullApplySite applySite,
224+
unsigned argIdx) {
225+
// Rule out trivial non-address argument types.
226+
SILType argType = applySite.getArgument(argIdx)->getType();
227+
if (!argType.isAddress() && argType.isTrivial(*applySite.getFunction()))
228+
return MemoryBehavior::None;
229+
230+
// The overall argument effect is the combination of the argument and the
231+
// global effects.
232+
MemoryBehavior behavior =
233+
GlobalEffects.getMemBehavior(RetainObserveKind::IgnoreRetains);
234+
MemoryBehavior argBehavior =
235+
ParamEffects[argIdx].getMemBehavior(RetainObserveKind::IgnoreRetains);
236+
237+
behavior = combineMemoryBehavior(behavior, argBehavior);
238+
239+
if (behavior > MemoryBehavior::MayRead &&
240+
applySite.getArgumentConvention(applySite.getArgumentRef(argIdx)) ==
241+
SILArgumentConvention::Indirect_In_Guaranteed) {
242+
// Even if side-effect analysis doesn't know anything about the called
243+
// called function, the in_guaranteed convention guarantees that the
244+
// argument is never written to.
245+
return MemoryBehavior::MayRead;
246+
}
247+
return behavior;
248+
}
249+
222250
bool FunctionSideEffects::mergeFrom(const FunctionSideEffects &RHS) {
223251
bool Changed = mergeFlags(RHS);
224252
Changed |= GlobalEffects.mergeFrom(RHS.GlobalEffects);

lib/SILOptimizer/UtilityPasses/MemBehaviorDumper.cpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,10 @@ class MemBehaviorDumper : public SILModuleTransform {
5959
// selected types of instructions.
6060
static bool shouldTestInstruction(SILInstruction *I) {
6161
// Only consider function calls.
62-
if ((EnableDumpAll && I->mayReadOrWriteMemory()) || FullApplySite::isa(I))
62+
if ((EnableDumpAll && I->mayReadOrWriteMemory()) ||
63+
FullApplySite::isa(I) ||
64+
isa<EndApplyInst>(I) ||
65+
isa<AbortApplyInst>(I))
6366
return true;
6467

6568
return false;

0 commit comments

Comments
 (0)