Skip to content

Commit a5d8aaf

Browse files
committed
SwiftCompilerSources: Replace BlockArgument with Phi and TermResult.
All SILArgument types are "block arguments". There are three kinds: 1. Function arguments 2. Phis 3. Terminator results In every situation where the source of the block argument matters, we need to distinguish between these three. Accidentally failing to handle one of the cases is an perpetual source of compiler bugs. Attempting to handle both phis and terminator results uniformly is *always* a bug, especially once OSSA has phi flags. Even when all cases are handled correctly, the code that deals with data flow across blocks is incomprehensible without giving each case a type. This continues to be a massive waste of time literally every time I review code that involves cross-block control flow. Unfortunately, we don't have these C++ types yet (nothing big is blocking that, it just wasn't done). That's manageable because we can use wrapper types on the Swift side for now. Wrapper types don't create any more complexity than protocols, but they do sacrifice some usability in switch cases. There is no reason for a BlockArgument type. First, a function argument is a block argument just as much as any other. BlockArgument provides no useful information beyond Argument. And it is nearly always a mistake to care about whether a value is a function argument and not care whether it is a phi or terminator result.
1 parent cae97a8 commit a5d8aaf

File tree

11 files changed

+104
-57
lines changed

11 files changed

+104
-57
lines changed

SwiftCompilerSources/Sources/Optimizer/FunctionPasses/ObjCBridgingOptimization.swift

Lines changed: 7 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -211,11 +211,11 @@ private func optimizeNonOptionalBridging(_ apply: ApplyInst,
211211

212212
// In the some-case just forward the original NSString.
213213
let objCType = emptyObjCValue.type
214-
let forwardedValue = someBlock.addBlockArgument(type: objCType, ownership: .owned, context)
214+
let forwardedValue = someBlock.addArgument(type: objCType, ownership: .owned, context)
215215
let someBuilder = Builder(atEndOf: someBlock, location: bridgeToSwiftCall.location, context)
216216
someBuilder.createBranch(to: continueBlock, arguments: [forwardedValue])
217217

218-
let s = continueBlock.addBlockArgument(type: objCType, ownership: .owned, context)
218+
let s = continueBlock.addArgument(type: objCType, ownership: .owned, context)
219219

220220
// Now replace the bridged value with the original value in the destination block.
221221
let replacement = s.makeAvailable(in: bridgeToObjcCall.parentBlock, context)
@@ -272,16 +272,13 @@ private func lookThroughOwnershipInsts(_ value: Value) -> Value {
272272
/// continue_bb(%value): // passed value
273273
/// ```
274274
private func isOptionalBridging(of value: Value, isBridging: (Value) -> ApplyInst?) -> SwitchEnumInst? {
275-
guard let arg = value as? BlockArgument,
276-
arg.isPhiArgument else {
277-
return nil
278-
}
275+
guard let phi = Phi(value) else { return nil }
279276

280277
var noneSwitch: SwitchEnumInst?
281278
var someSwitch: SwitchEnumInst?
282279

283280
// Check if one incoming value is the none-case and the other is the some-case.
284-
for incomingVal in arg.incomingPhiValues {
281+
for incomingVal in phi.incomingValues {
285282
// In both branches, the result must be an `enum` which is passed to the
286283
// continue_bb's phi-argument.
287284
guard let enumInst = incomingVal as? EnumInst,
@@ -338,10 +335,9 @@ private func isOptionalBridging(of value: Value, isBridging: (Value) -> ApplyIns
338335
/// Returns the `switch_enum` together with the enum case index, if `value` is
339336
/// the payload block argument of the `switch_enum`.
340337
private func isPayloadOfSwitchEnum(_ value: Value) -> (SwitchEnumInst, case: Int)? {
341-
if let payloadArg = value as? BlockArgument,
342-
let pred = payloadArg.parentBlock.singlePredecessor,
343-
let se = pred.terminator as? SwitchEnumInst,
344-
let caseIdx = se.getUniqueCase(forSuccessor: payloadArg.parentBlock) {
338+
if let payloadArg = TerminatorResult(value),
339+
let se = payloadArg.terminator as? SwitchEnumInst,
340+
let caseIdx = se.getUniqueCase(forSuccessor: payloadArg.successor) {
345341
return (se, caseIdx)
346342
}
347343
return nil

SwiftCompilerSources/Sources/Optimizer/FunctionPasses/StackPromotion.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -267,7 +267,7 @@ private struct ComputeOuterBlockrange : EscapeVisitorWithResult {
267267
// instructions (for which the `visitUse` closure is not called).
268268
result.insert(operandsDefinitionBlock)
269269

270-
// We need to explicitly add predecessor blocks of phi-arguments becaues they
270+
// We need to explicitly add predecessor blocks of phis becaues they
271271
// are not necesesarily visited during the down-walk in `isEscaping()`.
272272
// This is important for the special case where there is a back-edge from the
273273
// inner range to the inner rage's begin-block:
@@ -278,8 +278,8 @@ private struct ComputeOuterBlockrange : EscapeVisitorWithResult {
278278
// %k = alloc_ref $Klass // innerInstRange.begin
279279
// cond_br bb2, bb1(%k) // back-edge to bb1 == innerInstRange.blockRange.begin
280280
//
281-
if value is BlockArgument {
282-
result.insert(contentsOf: operandsDefinitionBlock.predecessors)
281+
if let phi = Phi(value) {
282+
result.insert(contentsOf: phi.predecessors)
283283
}
284284
return .continueWalk
285285
}

SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/SimplifyRefCasts.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ private extension UnaryInstruction {
111111
private func insertCompensatingInstructions(for inst: Instruction, in failureBlock: BasicBlock, _ context: SimplifyContext) {
112112
assert(failureBlock.arguments.count == 1)
113113
let sourceValue = inst.operands[0].value
114-
let newArg = failureBlock.addBlockArgument(type: sourceValue.type, ownership: sourceValue.ownership, context)
114+
let newArg = failureBlock.addArgument(type: sourceValue.type, ownership: sourceValue.ownership, context)
115115
let builder = Builder(atBeginOf: failureBlock, context)
116116
let newInst: SingleValueInstruction
117117
switch inst {

SwiftCompilerSources/Sources/Optimizer/PassManager/Context.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -400,9 +400,9 @@ extension Undef {
400400
}
401401

402402
extension BasicBlock {
403-
func addBlockArgument(type: Type, ownership: Ownership, _ context: some MutatingContext) -> BlockArgument {
403+
func addArgument(type: Type, ownership: Ownership, _ context: some MutatingContext) -> Argument {
404404
context.notifyInstructionsChanged()
405-
return bridged.addBlockArgument(type.bridged, ownership._bridged).blockArgument
405+
return bridged.addBlockArgument(type.bridged, ownership._bridged).argument
406406
}
407407

408408
func eraseArgument(at index: Int, _ context: some MutatingContext) {

SwiftCompilerSources/Sources/Optimizer/TestPasses/SILPrinter.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,8 @@ func runSILPrinter(function: Function, context: FunctionPassContext) {
2929
for use in arg.uses {
3030
print(" user: \(use.instruction)")
3131
}
32-
if let blockArg = arg as? BlockArgument, blockArg.isPhiArgument {
33-
for incoming in blockArg.incomingPhiValues {
32+
if let phi = Phi(arg) {
33+
for incoming in phi.incomingValues {
3434
print(" incoming: \(incoming)")
3535
}
3636
}

SwiftCompilerSources/Sources/Optimizer/Utilities/AccessUtils.swift

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -389,9 +389,8 @@ extension PointerToAddressInst {
389389

390390
mutating func walkUp(value: Value, path: SmallProjectionPath) -> WalkResult {
391391
switch value {
392-
case is BlockArgument, is MarkDependenceInst, is CopyValueInst,
393-
is StructExtractInst, is TupleExtractInst, is StructInst, is TupleInst,
394-
is FunctionArgument, is AddressToPointerInst:
392+
case is Argument, is MarkDependenceInst, is CopyValueInst,
393+
is StructExtractInst, is TupleExtractInst, is StructInst, is TupleInst, is AddressToPointerInst:
395394
return walkUpDefault(value: value, path: path)
396395
default:
397396
return .abortWalk

SwiftCompilerSources/Sources/Optimizer/Utilities/EscapeUtils.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -680,11 +680,11 @@ fileprivate struct EscapeWalker<V: EscapeVisitor> : ValueDefUseWalker,
680680
return cachedWalkDown(addressOrValue: def, path: path.with(knownType: def.type))
681681
case is AllocBoxInst:
682682
return cachedWalkDown(addressOrValue: def, path: path.with(knownType: nil))
683-
case let arg as BlockArgument:
684-
let block = arg.parentBlock
685-
switch block.singlePredecessor!.terminator {
683+
case let arg as Argument:
684+
guard let termResult = TerminatorResult(arg) else { return isEscaping }
685+
switch termResult.terminator {
686686
case let ta as TryApplyInst:
687-
if block != ta.normalBlock { return isEscaping }
687+
if termResult.successor != ta.normalBlock { return isEscaping }
688688
return walkUpApplyResult(apply: ta, path: path.with(knownType: nil))
689689
default:
690690
return isEscaping

SwiftCompilerSources/Sources/Optimizer/Utilities/WalkUtils.swift

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -673,9 +673,9 @@ extension ValueUseDefWalker {
673673
is BeginDeallocRefInst,
674674
is RefToBridgeObjectInst, is BridgeObjectToRefInst, is MarkUnresolvedNonCopyableValueInst:
675675
return walkUp(value: (def as! Instruction).operands[0].value, path: path)
676-
case let arg as BlockArgument:
677-
if arg.isPhiArgument {
678-
for incoming in arg.incomingPhiValues {
676+
case let arg as Argument:
677+
if let phi = Phi(arg) {
678+
for incoming in phi.incomingValues {
679679
// Check the cache to avoid cycles in the walk
680680
if let path = walkUpCache.needWalk(for: incoming, path: path) {
681681
if walkUp(value: incoming, path: path) == .abortWalk {
@@ -685,14 +685,13 @@ extension ValueUseDefWalker {
685685
}
686686
return .continueWalk
687687
}
688-
689-
let block = arg.parentBlock
690-
if let pred = block.singlePredecessor,
691-
let se = pred.terminator as? SwitchEnumInst,
692-
let caseIdx = se.getUniqueCase(forSuccessor: block) {
693-
return walkUp(value: se.enumOp, path: path.push(.enumCase, index: caseIdx))
688+
if let termResult = TerminatorResult(arg) {
689+
let pred = termResult.predecessor
690+
if let se = pred.terminator as? SwitchEnumInst,
691+
let caseIdx = se.getUniqueCase(forSuccessor: termResult.successor) {
692+
return walkUp(value: se.enumOp, path: path.push(.enumCase, index: caseIdx))
693+
}
694694
}
695-
696695
return rootDef(value: def, path: path)
697696
default:
698697
return rootDef(value: def, path: path)

SwiftCompilerSources/Sources/SIL/Argument.swift

Lines changed: 72 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -52,37 +52,91 @@ final public class FunctionArgument : Argument {
5252
}
5353
}
5454

55-
final public class BlockArgument : Argument {
56-
public var isPhiArgument: Bool {
57-
parentBlock.predecessors.allSatisfy {
58-
let term = $0.terminator
59-
return term is BranchInst || term is CondBranchInst
60-
}
55+
public struct Phi {
56+
public let value: Argument
57+
58+
// TODO: Remove the CondBr case. All passes avoid critical edges. It is only included here for compatibility with .sil tests that have not been migrated.
59+
public init?(_ value: Value) {
60+
guard let argument = value as? Argument else { return nil }
61+
var preds = argument.parentBlock.predecessors
62+
guard let pred = preds.next() else { return nil }
63+
let term = pred.terminator
64+
guard term is BranchInst || term is CondBranchInst else { return nil }
65+
self.value = argument
66+
}
67+
68+
public var predecessors: PredecessorList {
69+
return value.parentBlock.predecessors
6170
}
6271

63-
public var incomingPhiOperands: LazyMapSequence<PredecessorList, Operand> {
64-
assert(isPhiArgument)
65-
let idx = index
66-
return parentBlock.predecessors.lazy.map {
72+
public var successor: BasicBlock {
73+
return value.parentBlock
74+
}
75+
76+
public var incomingOperands: LazyMapSequence<PredecessorList, Operand> {
77+
let blockArgIdx = value.index
78+
return predecessors.lazy.map {
6779
switch $0.terminator {
6880
case let br as BranchInst:
69-
return br.operands[idx]
81+
return br.operands[blockArgIdx]
7082
case let condBr as CondBranchInst:
71-
if condBr.trueBlock == self.parentBlock {
72-
assert(condBr.falseBlock != self.parentBlock)
73-
return condBr.trueOperands[idx]
83+
if condBr.trueBlock == successor {
84+
assert(condBr.falseBlock != successor)
85+
return condBr.trueOperands[blockArgIdx]
7486
} else {
75-
assert(condBr.falseBlock == self.parentBlock)
76-
return condBr.falseOperands[idx]
87+
assert(condBr.falseBlock == successor)
88+
return condBr.falseOperands[blockArgIdx]
7789
}
7890
default:
7991
fatalError("wrong terminator for phi-argument")
8092
}
8193
}
8294
}
8395

84-
public var incomingPhiValues: LazyMapSequence<LazyMapSequence<PredecessorList, Operand>, Value> {
85-
incomingPhiOperands.lazy.map { $0.value }
96+
public var incomingValues: LazyMapSequence<LazyMapSequence<PredecessorList, Operand>, Value> {
97+
incomingOperands.lazy.map { $0.value }
98+
}
99+
100+
public static func ==(lhs: Phi, rhs: Phi) -> Bool {
101+
lhs.value === rhs.value
102+
}
103+
104+
public func hash(into hasher: inout Hasher) {
105+
value.hash(into: &hasher)
106+
}
107+
}
108+
109+
public struct TerminatorResult {
110+
public let value: Argument
111+
112+
public init?(_ value: Value) {
113+
guard let argument = value as? Argument else { return nil }
114+
var preds = argument.parentBlock.predecessors
115+
guard let pred = preds.next() else { return nil }
116+
let term = pred.terminator
117+
if term is BranchInst || term is CondBranchInst { return nil }
118+
self.value = argument
119+
}
120+
121+
public var terminator: TermInst {
122+
var preds = value.parentBlock.predecessors
123+
return preds.next()!.terminator
124+
}
125+
126+
public var predecessor: BasicBlock {
127+
return terminator.parentBlock
128+
}
129+
130+
public var successor: BasicBlock {
131+
return value.parentBlock
132+
}
133+
134+
public static func ==(lhs: TerminatorResult, rhs: TerminatorResult) -> Bool {
135+
lhs.value == rhs.value
136+
}
137+
138+
public func hash(into hasher: inout Hasher) {
139+
value.hash(into: &hasher)
86140
}
87141
}
88142

@@ -242,7 +296,6 @@ public enum ArgumentConvention {
242296

243297
extension BridgedArgument {
244298
public var argument: Argument { obj.getAs(Argument.self) }
245-
public var blockArgument: BlockArgument { obj.getAs(BlockArgument.self) }
246299
public var functionArgument: FunctionArgument { obj.getAs(FunctionArgument.self) }
247300
}
248301

SwiftCompilerSources/Sources/SIL/Registration.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ public func registerSILClasses() {
3636
register(PlaceholderValue.self)
3737

3838
register(FunctionArgument.self)
39-
register(BlockArgument.self)
39+
register(Argument.self)
4040

4141
register(StoreInst.self)
4242
register(StoreWeakInst.self)

0 commit comments

Comments
 (0)