Files
swift-mirror/SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/SimplifyMarkDependence.swift
Andrew Trick a8da66a82e Fix MarkDependenceInst.simplify()
Do not eliminate a mark_dependence on a begin_apply scope even though the token
has a trivial type.

Ideally, token would have a non-trivial Builtin type to avoid special cases.
2025-06-22 23:25:26 -07:00

117 lines
4.2 KiB
Swift

//===--- SimplifyMarkDependence.swift -------------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2025 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
// Note: this simplification cannot run before dependency diagnostics.
// See `var isRedundant` below.
extension MarkDependenceInst : OnoneSimplifiable, SILCombineSimplifiable {
func simplify(_ context: SimplifyContext) {
if isRedundant ||
// A literal lives forever, so no mark_dependence is needed.
// This pattern can occur after StringOptimization when a utf8CString of a literal is replaced
// by the string_literal itself.
value.isLiteral
{
replace(with: value, context)
return
}
simplifyBaseOperand(context)
}
}
extension MarkDependenceAddrInst : OnoneSimplifiable, SILCombineSimplifiable {
func simplify(_ context: SimplifyContext) {
if isRedundant {
context.erase(instruction: self)
return
}
simplifyBaseOperand(context)
}
}
private extension MarkDependenceInstruction {
var isRedundant: Bool {
if base.type.isObject && base.type.isTrivial(in: base.parentFunction)
&& !(base.definingInstruction is BeginApplyInst) {
// Sometimes due to specialization/builtins, we can get a mark_dependence whose base is a trivial
// typed object. Trivial values live forever. Therefore the mark_dependence does not have a meaning.
// begin_apply is a special case. A dependency on the token is limited to the coroutine scope (ideally, the token
// would have a non-trivial type like $Builtin.Token).
//
// Note: the mark_dependence is still needed for lifetime diagnostics. So it's important that this
// simplification does not run before the lifetime diagnostic pass.
return true
}
// If the value is an address projection from the base the mark_dependence is not needed because the
// base cannot be destroyed before the accessing the value, anyway.
if valueOrAddress.type.isAddress, base.type.isAddress,
// But we still need to keep the mark_dependence for non-escapable types because a non-escapable
// value can be copied and copies must not outlive the base.
valueOrAddress.type.isEscapable(in: parentFunction),
base.accessPath.isEqualOrContains(valueOrAddress.accessPath)
{
return true
}
return false
}
func simplifyBaseOperand(_ context: SimplifyContext) {
/// In OSSA, the `base` is a borrow introducing operand. It is pretty complicated to change the base.
/// So, for simplicity, we only do this optimization when OSSA is already lowered.
if parentFunction.hasOwnership {
return
}
// Replace the base operand with the operand of the base value if it's a certain kind of forwarding
// instruction.
let rootBase = base.lookThroughEnumAndExistentialRef
if rootBase != base {
baseOperand.set(to: rootBase, context)
}
}
}
private extension Value {
/// True, if this is a literal instruction or a struct of a literal instruction.
/// What we want to catch here is a `UnsafePointer<Int8>` of a string literal.
var isLiteral: Bool {
switch self {
case let s as StructInst:
if let singleOperand = s.operands.singleElement {
return singleOperand.value.isLiteral
}
return false
case is IntegerLiteralInst, is FloatLiteralInst, is StringLiteralInst:
return true
default:
return false
}
}
var lookThroughEnumAndExistentialRef: Value {
switch self {
case let e as EnumInst:
if let payload = e.payload {
return payload.lookThroughEnumAndExistentialRef
}
return self
case let ier as InitExistentialRefInst:
return ier.instance.lookThroughEnumAndExistentialRef
case let oer as OpenExistentialRefInst:
return oer.existential.lookThroughEnumAndExistentialRef
default:
return self
}
}
}