Skip to content

AliasAnalysis: a complete overhaul of alias- and memory-behavior analysis #75505

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 20 commits into from
Jul 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
04e0907
SIL: rename `Type.instanceTypeOfMetatype` -> `Type.loweredInstanceTyp…
eeckstein Jul 29, 2024
f388361
SimplifyBuiltin: fix a wrong constant folding of the `is_same_metatyp…
eeckstein Jul 29, 2024
7512acf
SwiftCompilerSources: fix debug description of `Set`
eeckstein Jun 13, 2024
9eddb24
EscapeUtils: replace `...byWalkingDown` APIs with an `initialWalkingD…
eeckstein Jun 13, 2024
95bd329
Swift SIL: bridge the `hasPointerEscape` flags of `MoveValueInst` and…
eeckstein Jun 13, 2024
4c49e00
Verifier: in the swift verifier call the bridged C++ `verificationFai…
eeckstein Jul 26, 2024
0f359ff
SideEffects: fix effects of indirect-in apply arguments
eeckstein Jul 26, 2024
084d60d
Swift SIL: add some APIs to instructions and other small improvments
eeckstein Jul 26, 2024
8f25313
LICM: make sure that getMemoryBehavior is only called for address values
eeckstein Jul 26, 2024
da5857d
EscapeInfo: improve handling of `begin_apply`s which yield an addres…
eeckstein Jul 26, 2024
1440ae7
EscapeInfo: handle `is_unique`
eeckstein Jul 26, 2024
c6e3f51
EscapeInfo: handle `unchecked_addr_cast`
eeckstein Jul 26, 2024
6cfec91
EscapeInfo: handle store_borrow
eeckstein Jul 26, 2024
8cb2caa
tests: remove unneeded SIL comments and fix check-lines in alias-anal…
eeckstein Jul 26, 2024
3fb7032
SIL Type: add `Type.aggregateIsOrContains`
eeckstein Jul 26, 2024
cf99536
Optimizer Utilities: add `InstructionWorklist.pushPredecessors(of: In…
eeckstein Jul 26, 2024
983d955
AccessUtils: do a simple type-based alias analysis in `AccessBase.isD…
eeckstein Jul 26, 2024
ceda41c
AccessUtils: fix handling of store_borrow in `AccessBase.isDistinct`
eeckstein Jul 26, 2024
f9b524b
AliasAnalysis: a complete overhaul of alias- and memory-behavior anal…
eeckstein Jul 26, 2024
031f235
SILType: remove the now obsolete `aggregateContainsRecord` function
eeckstein Jul 26, 2024
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
902 changes: 773 additions & 129 deletions SwiftCompilerSources/Sources/Optimizer/Analysis/AliasAnalysis.swift

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ public struct CalleeAnalysis {
},
// getMemBehaviorFn
{ (bridgedApply: BridgedInstruction, observeRetains: Bool, bca: BridgedCalleeAnalysis) -> BridgedMemoryBehavior in
let apply = bridgedApply.instruction as! ApplySite
let apply = bridgedApply.instruction as! FullApplySite
let e = bca.analysis.getSideEffects(ofApply: apply)
return e.getMemBehavior(observeRetains: observeRetains)
}
Expand Down Expand Up @@ -60,7 +60,7 @@ public struct CalleeAnalysis {
}

/// Returns the global (i.e. not argument specific) side effects of an apply.
public func getSideEffects(ofApply apply: ApplySite) -> SideEffects.GlobalEffects {
public func getSideEffects(ofApply apply: FullApplySite) -> SideEffects.GlobalEffects {
return getSideEffects(ofCallee: apply.callee)
}

Expand All @@ -78,7 +78,7 @@ public struct CalleeAnalysis {
}

/// Returns the argument specific side effects of an apply.
public func getSideEffects(of apply: ApplySite, operand: Operand, path: SmallProjectionPath) -> SideEffects.GlobalEffects {
public func getSideEffects(of apply: FullApplySite, operand: Operand, path: SmallProjectionPath) -> SideEffects.GlobalEffects {
var result = SideEffects.GlobalEffects()
guard let calleeArgIdx = apply.calleeArgumentIndex(of: operand) else {
return result
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,7 @@ struct InstructionSet : IntrusiveSet {
var d = "{\n"
for inst in function.instructions {
if contains(inst) {
d += inst.description
d += inst.description + "\n"
}
}
d += "}\n"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,3 +76,21 @@ typealias BasicBlockWorklist = Worklist<BasicBlockSet>
typealias InstructionWorklist = Worklist<InstructionSet>
typealias ValueWorklist = Worklist<ValueSet>
typealias OperandWorklist = Worklist<OperandSet>

extension InstructionWorklist {
mutating func pushPredecessors(of inst: Instruction, ignoring ignoreInst: SingleValueInstruction) {
if let prev = inst.previous {
if prev != ignoreInst {
pushIfNotVisited(prev)
}
} else {
for predBlock in inst.parentBlock.predecessors {
let termInst = predBlock.terminator
if termInst != ignoreInst {
pushIfNotVisited(termInst)
}
}
}
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,10 @@ let computeEscapeEffects = FunctionPass(name: "compute-escape-effects") {
}

// First check: is the argument (or a projected value of it) escaping at all?
if !arg.at(.anything).isEscapingWhenWalkingDown(using: IgnoreRecursiveCallVisitor(),
context) {
if !arg.at(.anything).isEscaping(using: IgnoreRecursiveCallVisitor(),
initialWalkingDirection: .down,
context)
{
let effect = EscapeEffects.ArgumentEffect(.notEscaping, argumentIndex: arg.index,
pathPattern: SmallProjectionPath(.anything))
newEffects.append(effect)
Expand Down Expand Up @@ -84,7 +86,7 @@ func addArgEffects(_ arg: FunctionArgument, argPath ap: SmallProjectionPath,
// containing one or more references.
let argPath = arg.type.isClass ? ap : ap.push(.anyValueFields)

guard let result = arg.at(argPath).visitByWalkingDown(using: ArgEffectsVisitor(), context) else {
guard let result = arg.at(argPath).visit(using: ArgEffectsVisitor(), initialWalkingDirection: .down, context) else {
return false
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -544,6 +544,6 @@ private extension PartialApplyInst {
var followTrivialTypes: Bool { true }
}

return self.isEscapingWhenWalkingDown(using: EscapesToApply(), context)
return self.isEscaping(using: EscapesToApply(), initialWalkingDirection: .down, context)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,7 @@ private extension BuiltinInst {
return
}

let instanceType = type.instanceTypeOfMetatype(in: parentFunction)
let instanceType = type.loweredInstanceTypeOfMetatype(in: parentFunction)
let builder = Builder(before: self, context)
let newMetatype = builder.createMetatype(of: instanceType, representation: .Thin)
operands[argument].set(to: newMetatype, context)
Expand Down Expand Up @@ -277,16 +277,21 @@ private func typesOfValuesAreEqual(_ lhs: Value, _ rhs: Value, in function: Func
if lhsMetatype.isDynamicSelfMetatype != rhsMetatype.isDynamicSelfMetatype {
return nil
}
let lhsTy = lhsMetatype.instanceTypeOfMetatype(in: function)
let rhsTy = rhsMetatype.instanceTypeOfMetatype(in: function)
let lhsTy = lhsMetatype.loweredInstanceTypeOfMetatype(in: function)
let rhsTy = rhsMetatype.loweredInstanceTypeOfMetatype(in: function)

// Do we know the exact types? This is not the case e.g. if a type is passed as metatype
// to the function.
let typesAreExact = lhsExistential.metatype is MetatypeInst &&
rhsExistential.metatype is MetatypeInst

if typesAreExact {
if lhsTy == rhsTy {
// We need to compare the not lowered types, because function types may differ in their original version
// but are equal in the lowered version, e.g.
// ((Int, Int) -> ())
// (((Int, Int)) -> ())
//
if lhsMetatype == rhsMetatype {
return true
}
// Comparing types of different classes which are in a sub-class relation is not handled by the
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -258,11 +258,6 @@ struct FunctionPassContext : MutatingContext {
SimplifyContext(_bridged: _bridged, notifyInstructionChanged: notifyInstructionChanged, preserveDebugInfo: preserveDebugInfo)
}

var aliasAnalysis: AliasAnalysis {
let bridgedAA = _bridged.getAliasAnalysis()
return AliasAnalysis(bridged: bridgedAA)
}

var deadEndBlocks: DeadEndBlocksAnalysis {
let bridgeDEA = _bridged.getDeadEndBlocksAnalysis()
return DeadEndBlocksAnalysis(bridged: bridgeDEA)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ private func registerSwiftPasses() {
registerForSILCombine(DestructureTupleInst.self, { run(DestructureTupleInst.self, $0) })

// Test passes
registerPass(aliasInfoDumper, { aliasInfoDumper.run($0) })
registerPass(functionUsesDumper, { functionUsesDumper.run($0) })
registerPass(silPrinterPass, { silPrinterPass.run($0) })
registerPass(escapeInfoDumper, { escapeInfoDumper.run($0) })
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
//===--- AliasInfoDumper.swift --------------------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2024 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//

import SIL

/// Prints the memory behavior of relevant instructions in relation to address values of the function.
let aliasInfoDumper = FunctionPass(name: "dump-alias-info") {
(function: Function, context: FunctionPassContext) in

let aliasAnalysis = context.aliasAnalysis

print("@\(function.name)")

let values = function.allValues

var pair = 0
for (index1, value1) in values.enumerated() {
for (index2, value2) in values.enumerated() {
if index2 >= index1 {
let result = aliasAnalysis.mayAlias(value1, value2)
precondition(result == aliasAnalysis.mayAlias(value2, value1), "alias analysis not symmetric")

print("PAIR #\(pair).")
print(" \(value1)")
print(" \(value2)")
if result {
print(" MayAlias")
} else if !value1.uses.isEmpty && !value2.uses.isEmpty {
print(" NoAlias")
} else {
print(" noalias?")
}

pair += 1
}
}
}
}

private extension Function {
var allValues: [Value] {
var values: [Value] = []
for block in blocks {
values.append(contentsOf: block.arguments.map { $0 })
for inst in block.instructions {
values.append(contentsOf: inst.results)
}
}
return values
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
swift_compiler_sources(Optimizer
FunctionUsesDumper.swift
AccessDumper.swift
AliasInfoDumper.swift
DeadEndBlockDumper.swift
EscapeInfoDumper.swift
MemBehaviorDumper.swift
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -109,9 +109,7 @@ let addressEscapeInfoDumper = FunctionPass(name: "dump-addr-escape-info") {
for value in valuesToCheck {
print("value:\(value)")
for apply in applies {
let path = AliasAnalysis.getPtrOrAddressPath(for: value)

if value.at(path).isEscaping(using: Visitor(apply: apply), context) {
if value.allContainedAddresss.isEscaping(using: Visitor(apply: apply), context) {
print(" ==> \(apply)")
} else {
print(" - \(apply)")
Expand All @@ -129,8 +127,8 @@ let addressEscapeInfoDumper = FunctionPass(name: "dump-addr-escape-info") {
print(lhs)
print(rhs)

let projLhs = lhs.at(AliasAnalysis.getPtrOrAddressPath(for: lhs))
let projRhs = rhs.at(AliasAnalysis.getPtrOrAddressPath(for: rhs))
let projLhs = lhs.allContainedAddresss
let projRhs = rhs.allContainedAddresss
let mayAlias = projLhs.canAddressAlias(with: projRhs, context)
if mayAlias != projRhs.canAddressAlias(with: projLhs, context) {
fatalError("canAddressAlias(with:) must be symmetric")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ let memBehaviorDumper = FunctionPass(name: "dump-mem-behavior") {

for value in values where value.definingInstruction != inst {

if value.type.isAddress || value is AddressToPointerInst {
if value.type.isAddress {
let read = inst.mayRead(fromAddress: value, aliasAnalysis)
let write = inst.mayWrite(toAddress: value, aliasAnalysis)
print("PAIR #\(currentPair).")
Expand Down Expand Up @@ -57,21 +57,22 @@ private extension Function {
private extension Instruction {
var shouldTest: Bool {
switch self {
case is ApplyInst,
is TryApplyInst,
case is ApplySite,
is EndApplyInst,
is BeginApplyInst,
is AbortApplyInst,
is BeginAccessInst,
is EndAccessInst,
is EndCOWMutationInst,
is CopyValueInst,
is DestroyValueInst,
is IsUniqueInst,
is EndBorrowInst,
is LoadInst,
is LoadBorrowInst,
is StoreInst,
is CopyAddrInst,
is BuiltinInst,
is StoreBorrowInst,
is DebugValueInst:
return true
default:
Expand Down
Loading