Skip to content

[5.9] AliasAnalysis: add complexity limits to some basic utility functions #68327

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Sep 6, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ struct AliasAnalysis {
static func register() {
BridgedAliasAnalysis.registerAnalysis(
// getMemEffectsFn
{ (bridgedCtxt: BridgedPassContext, bridgedVal: BridgedValue, bridgedInst: BridgedInstruction) -> swift.MemoryBehavior in
{ (bridgedCtxt: BridgedPassContext, bridgedVal: BridgedValue, bridgedInst: BridgedInstruction, complexityBudget: Int) -> swift.MemoryBehavior in
let context = FunctionPassContext(_bridged: bridgedCtxt)
let inst = bridgedInst.instruction
let val = bridgedVal.value
Expand All @@ -77,23 +77,28 @@ struct AliasAnalysis {
case (true, true): return .MayReadWrite
}
}
if val.at(path).isEscaping(using: EscapesToInstructionVisitor(target: inst, isAddress: true), context) {
if val.at(path).isEscaping(using: EscapesToInstructionVisitor(target: inst, isAddress: true),
complexityBudget: complexityBudget, context) {
return .MayReadWrite
}
return .None
},

// isObjReleasedFn
{ (bridgedCtxt: BridgedPassContext, bridgedObj: BridgedValue, bridgedInst: BridgedInstruction) -> Bool in
{ (bridgedCtxt: BridgedPassContext, bridgedObj: BridgedValue, bridgedInst: BridgedInstruction, complexityBudget: Int) -> Bool in
let context = FunctionPassContext(_bridged: bridgedCtxt)
let inst = bridgedInst.instruction
let obj = bridgedObj.value
let path = SmallProjectionPath(.anyValueFields)
if let apply = inst as? ApplySite {
let effect = getOwnershipEffect(of: apply, for: obj, path: path, context)
// Workaround for quadratic complexity in ARCSequenceOpts.
// We need to use an ever lower budget to not get into noticable compile time troubles.
let budget = complexityBudget / 10
let effect = getOwnershipEffect(of: apply, for: obj, path: path, complexityBudget: budget, context)
return effect.destroy
}
return obj.at(path).isEscaping(using: EscapesToInstructionVisitor(target: inst, isAddress: false), context)
return obj.at(path).isEscaping(using: EscapesToInstructionVisitor(target: inst, isAddress: false),
complexityBudget: complexityBudget, context)
},

// isAddrVisibleFromObj
Expand Down Expand Up @@ -144,9 +149,10 @@ private func getMemoryEffect(of apply: ApplySite, for address: Value, path: Smal
return memoryEffects
}

private func getOwnershipEffect(of apply: ApplySite, for value: Value, path: SmallProjectionPath, _ context: FunctionPassContext) -> SideEffects.Ownership {
private func getOwnershipEffect(of apply: ApplySite, for value: Value, path: SmallProjectionPath,
complexityBudget: Int, _ context: FunctionPassContext) -> SideEffects.Ownership {
let visitor = SideEffectsVisitor(apply: apply, calleeAnalysis: context.calleeAnalysis, isAddress: false)
if let result = value.at(path).visit(using: visitor, context) {
if let result = value.at(path).visit(using: visitor, complexityBudget: complexityBudget, context) {
// The resulting effects are the argument effects to which `value` escapes to.
return result.ownership
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,8 +99,10 @@ extension ProjectedValue {
/// it returns the `result` of the `visitor`, if the projected value does not escape.
/// Returns nil, if the projected value escapes.
///
func visit<V: EscapeVisitorWithResult>(using visitor: V, _ context: some Context) -> V.Result? {
var walker = EscapeWalker(visitor: visitor, context)
func visit<V: EscapeVisitorWithResult>(using visitor: V,
complexityBudget: Int = Int.max,
_ context: some Context) -> V.Result? {
var walker = EscapeWalker(visitor: visitor, complexityBudget: complexityBudget, context)
if walker.walkUp(addressOrValue: value, path: path.escapePath) == .abortWalk {
return nil
}
Expand Down
2 changes: 2 additions & 0 deletions include/swift/SILOptimizer/Analysis/AliasAnalysis.h
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,8 @@ class AliasAnalysis {
/// Returns true if the address(es of) `addr` can escape to `toInst`.
MemoryBehavior getMemoryBehaviorOfInst(SILValue addr, SILInstruction *toInst);

int getComplexityBudget(SILValue valueInFunction);

/// Returns true if the object(s of) `obj` can escape to `toInst`.
bool isObjectReleasedByInst(SILValue obj, SILInstruction *toInst);

Expand Down
4 changes: 2 additions & 2 deletions include/swift/SILOptimizer/OptimizerBridging.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,9 @@ struct BridgedAliasAnalysis {
}

typedef swift::MemoryBehavior (* _Nonnull GetMemEffectFn)(
BridgedPassContext context, BridgedValue, BridgedInstruction);
BridgedPassContext context, BridgedValue, BridgedInstruction, SwiftInt);
typedef bool (* _Nonnull Escaping2InstFn)(
BridgedPassContext context, BridgedValue, BridgedInstruction);
BridgedPassContext context, BridgedValue, BridgedInstruction, SwiftInt);
typedef bool (* _Nonnull Escaping2ValFn)(
BridgedPassContext context, BridgedValue, BridgedValue);
typedef bool (* _Nonnull Escaping2ValIntFn)(
Expand Down
29 changes: 15 additions & 14 deletions lib/SILOptimizer/Analysis/AliasAnalysis.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -715,30 +715,24 @@ MemoryBehavior AliasAnalysis::getMemoryBehaviorOfInst(
SILValue addr, SILInstruction *toInst) {
if (getMemEffectsFunction) {
return (MemoryBehavior)getMemEffectsFunction({PM->getSwiftPassInvocation()}, {addr},
{toInst->asSILNode()});
{toInst->asSILNode()},
getComplexityBudget(addr));
}
return MemoryBehavior::MayHaveSideEffects;
}

bool AliasAnalysis::isObjectReleasedByInst(SILValue obj, SILInstruction *inst) {
if (isObjReleasedFunction) {
return isObjReleasedFunction({PM->getSwiftPassInvocation()}, {obj}, {inst->asSILNode()}) != 0;
return isObjReleasedFunction({PM->getSwiftPassInvocation()}, {obj}, {inst->asSILNode()},
getComplexityBudget(obj)) != 0;
}
return true;
}

bool AliasAnalysis::isAddrVisibleFromObject(SILValue addr, SILValue obj) {
if (isAddrVisibleFromObjFunction) {
// This function is called a lot from ARCSequenceOpt and ReleaseHoisting.
// To avoid quadratic complexity for large functions, we limit the amount
// of work what the EscapeUtils are allowed to to.
// This keeps the complexity linear.
//
// This arbitrary limit is good enough for almost all functions. It lets
// the EscapeUtils do several hundred up/down walks which is much more than
// needed in most cases.
SwiftInt complexityLimit = 1000000 / getEstimatedFunctionSize(addr);
return isAddrVisibleFromObjFunction({PM->getSwiftPassInvocation()}, {addr}, {obj}, complexityLimit) != 0;
return isAddrVisibleFromObjFunction({PM->getSwiftPassInvocation()}, {addr}, {obj},
getComplexityBudget(addr)) != 0;
}
return true;
}
Expand All @@ -750,7 +744,14 @@ bool AliasAnalysis::canReferenceSameField(SILValue lhs, SILValue rhs) {
return true;
}

int AliasAnalysis::getEstimatedFunctionSize(SILValue valueInFunction) {
// To avoid quadratic complexity for large functions, we limit the amount
// of work what the EscapeUtils are allowed to to.
// This keeps the complexity linear.
//
// This arbitrary limit is good enough for almost all functions. It lets
// the EscapeUtils do several hundred up/down walks which is much more than
// needed in most cases.
int AliasAnalysis::getComplexityBudget(SILValue valueInFunction) {
if (estimatedFunctionSize < 0) {
int numInsts = 0;
SILFunction *f = valueInFunction->getFunction();
Expand All @@ -759,5 +760,5 @@ int AliasAnalysis::getEstimatedFunctionSize(SILValue valueInFunction) {
}
estimatedFunctionSize = numInsts;
}
return estimatedFunctionSize;
return 1000000 / estimatedFunctionSize;
}
Loading