Files
swift-mirror/SwiftCompilerSources/Sources/Optimizer/InstructionPasses/SimplifyBeginCOWMutation.swift
2021-12-22 09:46:25 +01:00

129 lines
4.3 KiB
Swift

//===--- SimplifyBeginCOWMutation.swift - Simplify begin_cow_mutation -----===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2021 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
/// Simplify begin_cow_mutation instructions.
let simplifyBeginCOWMutationPass = InstructionPass<BeginCOWMutationInst>(
name: "simplify-begin_cow_mutation", {
(beginCOW: BeginCOWMutationInst, context: PassContext) in
/// The buffer of an empty Array/Set/Dictionary singleton is known to be not
/// unique. Replace the uniqueness result of such a
/// `begin_cow_mutation` with a zero `integer_literal`, e.g.
///
/// %3 = global_addr @_swiftEmptyArrayStorage
/// %4 = address_to_pointer %3
/// %5 = raw_pointer_to_ref %4
/// %6 = unchecked_ref_cast %5
/// (%u, %b) = begin_cow_mutation %6
/// ->
/// [...]
/// (%not_used, %b) = begin_cow_mutation %6
/// %u = integer_literal $Builtin.Int1, 0
///
optimizeEmptySingleton(beginCOW, context)
/// If the only use of the `begin_cow_instruction` is an `end_cow_instruction`,
/// remove the pair, e.g.
///
/// (%u, %b) = begin_cow_mutation %0 : $Buffer
/// %e = end_cow_mutation %b : $Buffer
///
if optimizeEmptyBeginEndPair(beginCOW, context) {
return
}
/// If the operand of the `begin_cow_instruction` is an `end_cow_instruction`,
/// which has no other uses, remove the pair, e.g.
///
/// %e = end_cow_mutation %0 : $Buffer
/// (%u, %b) = begin_cow_mutation %e : $Buffer
///
if optimizeEmptyEndBeginPair(beginCOW, context) {
return
}
})
private func optimizeEmptySingleton(_ beginCOW: BeginCOWMutationInst,
_ context: PassContext) {
if !isEmptyCOWSingleton(beginCOW.operand) {
return
}
if beginCOW.uniquenessResult.nonDebugUses.isEmpty {
/// Don't create an integer_literal which would be dead. This would result
/// in an infinite loop in SILCombine.
return
}
let builder = Builder(at: beginCOW, location: beginCOW.location, context)
let zero = builder.createIntegerLiteral(0, type: beginCOW.uniquenessResult.type);
context.replaceAllUses(of: beginCOW.uniquenessResult, with: zero)
}
private func isEmptyCOWSingleton(_ value: Value) -> Bool {
var v = value
while true {
switch v {
case is UncheckedRefCastInst,
is UpcastInst,
is RawPointerToRefInst,
is AddressToPointerInst,
is CopyValueInst:
v = (v as! UnaryInstruction).operand
case let globalAddr as GlobalAddrInst:
let name = globalAddr.global.name
return name == "_swiftEmptyArrayStorage" ||
name == "_swiftEmptyDictionarySingleton" ||
name == "_swiftEmptySetSingleton"
default:
return false
}
}
}
private func optimizeEmptyBeginEndPair(_ beginCOW: BeginCOWMutationInst,
_ context: PassContext) -> Bool {
if !beginCOW.uniquenessResult.nonDebugUses.isEmpty {
return false
}
let buffer = beginCOW.bufferResult
if buffer.nonDebugUses.contains(where: { !($0.instruction is EndCOWMutationInst) }) {
return false
}
for use in buffer.nonDebugUses {
let endCOW = use.instruction as! EndCOWMutationInst
context.replaceAllUses(of: endCOW, with: beginCOW.operand)
context.erase(instruction: endCOW)
}
context.erase(instruction: beginCOW, .includingDebugUses)
return true
}
private func optimizeEmptyEndBeginPair(_ beginCOW: BeginCOWMutationInst,
_ context: PassContext) -> Bool {
if !beginCOW.uniquenessResult.nonDebugUses.isEmpty {
return false
}
guard let endCOW = beginCOW.operand as? EndCOWMutationInst else {
return false
}
if endCOW.nonDebugUses.contains(where: { $0.instruction != beginCOW }) {
return false
}
context.replaceAllUses(of: beginCOW.bufferResult, with: endCOW.operand)
context.erase(instruction: beginCOW, .includingDebugUses)
context.erase(instruction: endCOW, .includingDebugUses)
return true
}