Skip to content

Swift Optimizer: add def-use/use-def walker utilities #59729

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
Jul 7, 2022
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 @@ -45,19 +45,19 @@ struct AliasAnalysis {
}

/// Returns the correct path for address-alias functions.
static func getPtrOrAddressPath(for value: Value) -> EscapeInfo.Path {
static func getPtrOrAddressPath(for value: Value) -> SmallProjectionPath {
let ty = value.type
if ty.isAddress {
// This is the regular case: the path selects any sub-fields of an address.
return EscapeInfo.Path(.anyValueFields)
return SmallProjectionPath(.anyValueFields)
}
// Some optimizations use the address-alias APIs with non-address SIL values.
// TODO: this is non-intuitive and we should eliminate those API uses.
if ty.isClass {
// If the value is a (non-address) reference it means: all addresses within the class instance.
return EscapeInfo.Path(.anyValueFields).push(.anyClassField)
return SmallProjectionPath(.anyValueFields).push(.anyClassField)
}
// Any other non-address value means: all addresses of any referenced class instances within the value.
return EscapeInfo.Path(.anyValueFields).push(.anyClassField).push(.anyValueFields)
return SmallProjectionPath(.anyValueFields).push(.anyClassField).push(.anyValueFields)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,14 @@ fileprivate typealias Path = ArgumentEffect.Path
/// In future, this pass may also add other effects, like memory side effects.
let computeEffects = FunctionPass(name: "compute-effects", {
(function: Function, context: PassContext) in

var argsWithDefinedEffects = getArgIndicesWithDefinedEffects(of: function)

var escapeInfo = EscapeInfo(calleeAnalysis: context.calleeAnalysis)
struct IgnoreRecursiveCallVisitor : EscapeInfoVisitor {
func visitUse(operand: Operand, path: Path, state: State) -> UseResult {
return isOperandOfRecursiveCall(operand) ? .ignore : .continueWalk
}
}
var escapeInfo = EscapeInfo(calleeAnalysis: context.calleeAnalysis, visitor: IgnoreRecursiveCallVisitor())
var newEffects = Stack<ArgumentEffect>(context)
let returnInst = function.returnInstruction

Expand All @@ -46,21 +50,18 @@ let computeEffects = FunctionPass(name: "compute-effects", {
if argsWithDefinedEffects.contains(arg.index) { continue }

// First check: is the argument (or a projected value of it) escaping at all?
if !escapeInfo.isEscapingWhenWalkingDown(object: arg, path: Path(.anything),
visitUse: { op, _, _ in
isOperandOfRecursiveCall(op) ? .ignore : .continueWalking
}) {
if !escapeInfo.isEscapingWhenWalkingDown(object: arg, path: Path(.anything)) {
let selectedArg = Selection(arg, pathPattern: Path(.anything))
newEffects.push(ArgumentEffect(.notEscaping, selectedArg: selectedArg))
continue
}

// Now compute effects for two important cases:
// * the argument itself + any value projections, and...
if addArgEffects(arg, argPath: Path(), to: &newEffects, returnInst, &escapeInfo) {
if addArgEffects(context: context, arg, argPath: Path(), to: &newEffects, returnInst) {
// * single class indirections
_ = addArgEffects(arg, argPath: Path(.anyValueFields).push(.anyClassField),
to: &newEffects, returnInst, &escapeInfo)
_ = addArgEffects(context: context, arg, argPath: Path(.anyValueFields).push(.anyClassField),
to: &newEffects, returnInst)
}
}

Expand All @@ -71,60 +72,72 @@ let computeEffects = FunctionPass(name: "compute-effects", {
newEffects.removeAll()
})


/// Returns true if an argument effect was added.
private
func addArgEffects(_ arg: FunctionArgument, argPath ap: Path,
func addArgEffects(context: PassContext, _ arg: FunctionArgument, argPath ap: Path,
to newEffects: inout Stack<ArgumentEffect>,
_ returnInst: ReturnInst?,
_ escapeInfo: inout EscapeInfo) -> Bool {

var toSelection: Selection?
_ returnInst: ReturnInst?) -> Bool {
// Correct the path if the argument is not a class reference itself, but a value type
// containing one or more references.
let argPath = arg.type.isClass ? ap : ap.push(.anyValueFields)

if escapeInfo.isEscapingWhenWalkingDown(object: arg, path: argPath,
visitUse: { op, path, followStores in
if op.instruction == returnInst {
// The argument escapes to the function return
if followStores {
// The escaping path must not introduce a followStores.
return .markEscaping
}
if let ta = toSelection {
if ta.value != .returnValue { return .markEscaping }
toSelection = Selection(.returnValue, pathPattern: path.merge(with: ta.pathPattern))
} else {
toSelection = Selection(.returnValue, pathPattern: path)
}
return .ignore
}
if isOperandOfRecursiveCall(op) {
return .ignore
}
return .continueWalking
},
visitDef: { def, path, followStores in
guard let destArg = def as? FunctionArgument else {
return .continueWalkingUp
}
// The argument escapes to another argument (e.g. an out or inout argument)
if followStores {

struct ArgEffectsVisitor : EscapeInfoVisitor {
init(toSelection: Selection?, returnInst: ReturnInst?) {
self.toSelection = toSelection
self.returnInst = returnInst
}

var toSelection: Selection?
var returnInst: ReturnInst?

mutating func visitUse(operand: Operand, path: Path, state: State) -> UseResult {
if operand.instruction == returnInst {
// The argument escapes to the function return
if state.followStores {
// The escaping path must not introduce a followStores.
return .markEscaping
return .abort
}
let argIdx = destArg.index
if let ta = toSelection {
if ta.value != .argument(argIdx) { return .markEscaping }
toSelection = Selection(.argument(argIdx), pathPattern: path.merge(with: ta.pathPattern))
if ta.value != .returnValue { return .abort }
toSelection = Selection(.returnValue, pathPattern: path.merge(with: ta.pathPattern))
} else {
toSelection = Selection(.argument(argIdx), pathPattern: path)
toSelection = Selection(.returnValue, pathPattern: path)
}
return .continueWalkingDown
}) {
return .ignore
}
if isOperandOfRecursiveCall(operand) {
return .ignore
}
return .continueWalk
}

mutating func visitDef(def: Value, path: Path, state: State) -> DefResult {
guard let destArg = def as? FunctionArgument else {
return .continueWalkUp
}
// The argument escapes to another argument (e.g. an out or inout argument)
if state.followStores {
// The escaping path must not introduce a followStores.
return .abort
}
let argIdx = destArg.index
if let ta = toSelection {
if ta.value != .argument(argIdx) { return .abort }
toSelection = Selection(.argument(argIdx), pathPattern: path.merge(with: ta.pathPattern))
} else {
toSelection = Selection(.argument(argIdx), pathPattern: path)
}
return .walkDown
}
}

var walker = EscapeInfo(calleeAnalysis: context.calleeAnalysis, visitor: ArgEffectsVisitor(toSelection: nil, returnInst: returnInst))
if walker.isEscapingWhenWalkingDown(object: arg, path: argPath) {
return false
}


let toSelection = walker.visitor.toSelection
let fromSelection = Selection(arg, pathPattern: argPath)

guard let toSelection = toSelection else {
Expand All @@ -137,7 +150,7 @@ func addArgEffects(_ arg: FunctionArgument, argPath ap: Path,
return false
}

let exclusive = isExclusiveEscape(fromArgument: arg, fromPath: argPath, to: toSelection, returnInst, &escapeInfo)
let exclusive = isExclusiveEscape(context: context, fromArgument: arg, fromPath: argPath, to: toSelection, returnInst)

newEffects.push(ArgumentEffect(.escaping(toSelection, exclusive), selectedArg: fromSelection))
return true
Expand Down Expand Up @@ -184,52 +197,70 @@ private func isOperandOfRecursiveCall(_ op: Operand) -> Bool {
/// there are no other arguments or escape points than `fromArgument`. Also, the
/// path at the `fromArgument` must match with `fromPath`.
private
func isExclusiveEscape(fromArgument: Argument, fromPath: Path, to toSelection: Selection,
_ returnInst: ReturnInst, _ escapeInfo: inout EscapeInfo) -> Bool {
func isExclusiveEscape(context: PassContext, fromArgument: Argument, fromPath: Path, to toSelection: Selection,
_ returnInst: ReturnInst) -> Bool {
switch toSelection.value {

// argument -> return
case .returnValue:
if escapeInfo.isEscaping(
object: returnInst.operand, path: toSelection.pathPattern,
visitUse: { op, path, followStores in
if op.instruction == returnInst {
if followStores { return .markEscaping }
if path.matches(pattern: toSelection.pathPattern) {
return .ignore
}
return .markEscaping
}
return .continueWalking
},
visitDef: { def, path, followStores in
guard let arg = def as? FunctionArgument else {
return .continueWalkingUp
}
if followStores { return .markEscaping }
if arg == fromArgument && path.matches(pattern: fromPath) {
return .continueWalkingDown
}
return .markEscaping
}) {
struct IsExclusiveReturnEscapeVisitor : EscapeInfoVisitor {
let fromArgument: Argument
let toSelection: Selection
let returnInst: ReturnInst
let fromPath: Path

mutating func visitUse(operand: Operand, path: Path, state: State) -> UseResult {
if operand.instruction == returnInst {
if state.followStores { return .abort }
if path.matches(pattern: toSelection.pathPattern) {
return .ignore
}
return .abort
}
return .continueWalk
}

mutating func visitDef(def: Value, path: Path, state: State) -> DefResult {
guard let arg = def as? FunctionArgument else {
return .continueWalkUp
}
if state.followStores { return .abort }
if arg == fromArgument && path.matches(pattern: fromPath) {
return .walkDown
}
return .abort
}
}
let visitor = IsExclusiveReturnEscapeVisitor(fromArgument: fromArgument, toSelection: toSelection, returnInst: returnInst, fromPath: fromPath)
var walker = EscapeInfo(calleeAnalysis: context.calleeAnalysis, visitor: visitor)
if walker.isEscaping(object: returnInst.operand, path: toSelection.pathPattern) {
return false
}

// argument -> argument
case .argument(let toArgIdx):
struct IsExclusiveArgumentEscapeVisitor : EscapeInfoVisitor {
let fromArgument: Argument
let fromPath: Path
let toSelection: Selection
let toArg: FunctionArgument

mutating func visitDef(def: Value, path: Path, state: State) -> DefResult {
guard let arg = def as? FunctionArgument else {
return .continueWalkUp
}
if state.followStores { return .abort }
if arg == fromArgument && path.matches(pattern: fromPath) { return .walkDown }
if arg == toArg && path.matches(pattern: toSelection.pathPattern) { return .walkDown }
return .abort
}
}
let toArg = returnInst.function.arguments[toArgIdx]
if escapeInfo.isEscaping(object: toArg, path: toSelection.pathPattern,
visitDef: { def, path, followStores in
guard let arg = def as? FunctionArgument else {
return .continueWalkingUp
}
if followStores { return .markEscaping }
if arg == fromArgument && path.matches(pattern: fromPath) { return .continueWalkingDown }
if arg == toArg && path.matches(pattern: toSelection.pathPattern) { return .continueWalkingDown }
return .markEscaping
}) {
let visitor = IsExclusiveArgumentEscapeVisitor(fromArgument: fromArgument, fromPath: fromPath, toSelection: toSelection, toArg: toArg)
var walker = EscapeInfo(calleeAnalysis: context.calleeAnalysis, visitor: visitor)
if walker.isEscaping(object: toArg, path: toSelection.pathPattern) {
return false
}
}
return true
}

Loading