diff --git a/SwiftCompilerSources/Sources/AST/Type.swift b/SwiftCompilerSources/Sources/AST/Type.swift index c193387800f1a..946a591f994e4 100644 --- a/SwiftCompilerSources/Sources/AST/Type.swift +++ b/SwiftCompilerSources/Sources/AST/Type.swift @@ -147,15 +147,8 @@ extension TypeProperties { public var isDynamicSelf: Bool { rawType.bridged.isDynamicSelf()} public var isBox: Bool { rawType.bridged.isBox() } - /// True if this is the type which represents an integer literal used in a type position. - /// For example `N` in `struct T {}` - public var isInteger: Bool { rawType.bridged.isInteger() } - public var canBeClass: Type.TraitResult { rawType.bridged.canBeClass().result } - /// True if this the nominal type `Swift.Optional`. - public var isOptional: Bool { rawType.bridged.isOptional() } - /// True if this type is a value type (struct/enum) that defines a `deinit`. public var isValueTypeWithDeinit: Bool { if let nominal = nominal, nominal.valueTypeDestructor != nil { @@ -164,6 +157,34 @@ extension TypeProperties { return false } + //===--------------------------------------------------------------------===// + // Checks for stdlib types + //===--------------------------------------------------------------------===// + + /// True if this is the type which represents an integer literal used in a type position. + /// For example `N` in `struct T {}` + public var isInteger: Bool { rawType.bridged.isInteger() } + + /// True if this the nominal type `Swift.Optional`. + public var isOptional: Bool { rawType.bridged.isOptional() } + + /// A non-nil result type implies isUnsafe[Raw][Mutable]Pointer. A raw + /// pointer has a `void` element type. + public var unsafePointerElementType: Type? { + Type(bridgedOrNil: rawType.bridged.getAnyPointerElementType()) + } + + public var isAnyUnsafePointer: Bool { + unsafePointerElementType != nil + } + + public var isAnyUnsafeBufferPointer: Bool { + rawType.bridged.isUnsafeBufferPointerType() + || rawType.bridged.isUnsafeMutableBufferPointerType() + || rawType.bridged.isUnsafeRawBufferPointerType() + || rawType.bridged.isUnsafeMutableRawBufferPointerType() + } + //===--------------------------------------------------------------------===// // Properties of lowered `SILFunctionType`s //===--------------------------------------------------------------------===// diff --git a/SwiftCompilerSources/Sources/Optimizer/Utilities/LifetimeDependenceUtils.swift b/SwiftCompilerSources/Sources/Optimizer/Utilities/LifetimeDependenceUtils.swift index 21de12feaf86d..eb21be91aef7a 100644 --- a/SwiftCompilerSources/Sources/Optimizer/Utilities/LifetimeDependenceUtils.swift +++ b/SwiftCompilerSources/Sources/Optimizer/Utilities/LifetimeDependenceUtils.swift @@ -862,7 +862,7 @@ extension LifetimeDependenceDefUseWalker { return walkDownUses(of: value, using: operand) } - // copy_addr + // copy_addr %operand to %address; %_newAddr = mark_dependence %address on %operand mutating func loadedAddressUse(of operand: Operand, intoAddress address: Operand) -> WalkResult { if leafUse(of: operand) == .abortWalk { return .abortWalk @@ -971,7 +971,29 @@ extension LifetimeDependenceDefUseWalker { break case .yield: return storeToYieldDependence(address: address, of: operand) - case .global, .class, .tail, .storeBorrow, .pointer, .index, .unidentified: + case let .pointer(p2a): + if !address.isEscapable, let base = p2a.isResultOfUnsafeAddressor() { + let selfValue = base.value + if selfValue.type.isAddress { + // Normally an unsafeMutableAddressor is mutating, so this is the common case (address-type + // 'selfValue'). Treat the store to this pointer-to-address projection just like any store to the local + // variable holding 'selfValue'. + return visitStoredUses(of: operand, into: selfValue) + } + // selfValue.type might not an be address: + // (1) mark_dependence on unsafeAddress is handled like a storedUse + // (2) nonmutating unsafeMutableAddress (e.g. UnsafeMutable[Buffer]Pointer). + // A nonmutating unsafeMutableAddress is only expected to happen for UnsafeMutable[Buffer]Pointer, in which case + + // If selfValue is trivial (UnsafeMutable[Buffer]Pointer), its uses can be ignored. + if selfValue.type.isTrivial(in: selfValue.parentFunction) { + return .continueWalk + } + // Otherwise a store to indirect memory is conservatively escaping. + return escapingDependence(on: operand) + } + break + case .global, .class, .tail, .storeBorrow, .index, .unidentified: // An address produced by .storeBorrow should never be stored into. // // TODO: allow storing an immortal value into a global. @@ -1045,7 +1067,7 @@ extension LifetimeDependenceDefUseWalker { } case .dependenceDest: // Simply a marker that indicates the start of an in-memory dependent value. If this was a mark_dependence, uses - // of its forwarded address has were visited by LocalVariableAccessWalker and recorded as separate local accesses. + // of its forwarded address were visited by LocalVariableAccessWalker and recorded as separate local accesses. return .continueWalk case .store, .storeBorrow: // A store does not use the previous in-memory value. diff --git a/SwiftCompilerSources/Sources/SIL/Function.swift b/SwiftCompilerSources/Sources/SIL/Function.swift index 45122979f77f9..e96b44229b5ca 100644 --- a/SwiftCompilerSources/Sources/SIL/Function.swift +++ b/SwiftCompilerSources/Sources/SIL/Function.swift @@ -52,6 +52,8 @@ final public class Function : CustomStringConvertible, HasShortDescription, Hash public var isConvertPointerToPointerArgument: Bool { bridged.isConvertPointerToPointerArgument() } + public var isAddressor: Bool { bridged.isAddressor() } + public var specializationLevel: Int { bridged.specializationLevel() } public var isSpecialization: Bool { bridged.isSpecialization() } diff --git a/SwiftCompilerSources/Sources/SIL/Utilities/AccessUtils.swift b/SwiftCompilerSources/Sources/SIL/Utilities/AccessUtils.swift index e2c489693e50f..1109d8ad7c68c 100644 --- a/SwiftCompilerSources/Sources/SIL/Utilities/AccessUtils.swift +++ b/SwiftCompilerSources/Sources/SIL/Utilities/AccessUtils.swift @@ -512,6 +512,22 @@ public extension PointerToAddressInst { } return nil } + + // If this address is the result of a call to unsafe[Mutable]Address, return the 'self' operand of the apply. This + // represents the base value into which this address projects. + func isResultOfUnsafeAddressor() -> Operand? { + if isStrict, + let extract = pointer as? StructExtractInst, + extract.`struct`.type.isAnyUnsafePointer, + let addressorApply = extract.`struct` as? ApplyInst, + let addressorFunc = addressorApply.referencedFunction, + addressorFunc.isAddressor + { + let selfArgIdx = addressorFunc.selfArgumentIndex! + return addressorApply.argumentOperands[selfArgIdx] + } + return nil + } } /// TODO: migrate AccessBase to use this instead of GlobalVariable because many utilities need to get back to a diff --git a/include/swift/AST/ASTBridging.h b/include/swift/AST/ASTBridging.h index ec88cb660a4f0..8b032aae390e0 100644 --- a/include/swift/AST/ASTBridging.h +++ b/include/swift/AST/ASTBridging.h @@ -2955,6 +2955,11 @@ struct BridgedASTType { BRIDGED_INLINE bool isBuiltinFixedWidthInteger(SwiftInt width) const; BRIDGED_INLINE bool isOptional() const; BRIDGED_INLINE bool isBuiltinType() const; + SWIFT_IMPORT_UNSAFE BRIDGED_INLINE BridgedASTType getAnyPointerElementType() const; + BRIDGED_INLINE bool isUnsafeBufferPointerType() const; + BRIDGED_INLINE bool isUnsafeMutableBufferPointerType() const; + BRIDGED_INLINE bool isUnsafeRawBufferPointerType() const; + BRIDGED_INLINE bool isUnsafeMutableRawBufferPointerType() const; SWIFT_IMPORT_UNSAFE BRIDGED_INLINE OptionalBridgedDeclObj getNominalOrBoundGenericNominal() const; BRIDGED_INLINE TraitResult canBeClass() const; SWIFT_IMPORT_UNSAFE BRIDGED_INLINE OptionalBridgedDeclObj getAnyNominal() const; diff --git a/include/swift/AST/ASTBridgingImpl.h b/include/swift/AST/ASTBridgingImpl.h index b71a98e3b140d..07d7f2229018b 100644 --- a/include/swift/AST/ASTBridgingImpl.h +++ b/include/swift/AST/ASTBridgingImpl.h @@ -566,6 +566,26 @@ bool BridgedASTType::isBuiltinType() const { return unbridged()->isBuiltinType(); } +BridgedASTType BridgedASTType::getAnyPointerElementType() const { + return {unbridged()->getCanonicalType()->getAnyPointerElementType().getPointer()}; +} + +bool BridgedASTType::isUnsafeBufferPointerType() const { + return unbridged()->isUnsafeBufferPointer(); +} + +bool BridgedASTType::isUnsafeMutableBufferPointerType() const { + return unbridged()->isUnsafeMutableBufferPointer(); +} + +bool BridgedASTType::isUnsafeRawBufferPointerType() const { + return unbridged()->isUnsafeRawBufferPointer(); +} + +bool BridgedASTType::isUnsafeMutableRawBufferPointerType() const { + return unbridged()->isUnsafeMutableRawBufferPointer(); +} + OptionalBridgedDeclObj BridgedASTType::getNominalOrBoundGenericNominal() const { return {unbridged()->getNominalOrBoundGenericNominal()}; } diff --git a/include/swift/SIL/SILBridging.h b/include/swift/SIL/SILBridging.h index a4613c8143b86..30edc786d7813 100644 --- a/include/swift/SIL/SILBridging.h +++ b/include/swift/SIL/SILBridging.h @@ -562,6 +562,7 @@ struct BridgedFunction { BRIDGED_INLINE bool isSpecialization() const; bool isTrapNoReturn() const; bool isConvertPointerToPointerArgument() const; + bool isAddressor() const; bool isAutodiffVJP() const; SwiftInt specializationLevel() const; SWIFT_IMPORT_UNSAFE BridgedSubstitutionMap getMethodSubstitutions(BridgedSubstitutionMap contextSubs, diff --git a/lib/SILOptimizer/Utils/OptimizerBridging.cpp b/lib/SILOptimizer/Utils/OptimizerBridging.cpp index a1bafbf8c65e2..1b64bd17e644f 100644 --- a/lib/SILOptimizer/Utils/OptimizerBridging.cpp +++ b/lib/SILOptimizer/Utils/OptimizerBridging.cpp @@ -511,6 +511,13 @@ bool BridgedFunction::isConvertPointerToPointerArgument() const { return false; } +bool BridgedFunction::isAddressor() const { + if (auto declRef = dyn_cast_or_null(getFunction()->getDeclRef().getDecl())) { + return declRef->isAnyAddressor(); + } + return false; +} + bool BridgedFunction::isAutodiffVJP() const { return swift::isDifferentiableFuncComponent( getFunction(), swift::AutoDiffFunctionComponent::VJP);