//===--- 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` 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 } } }