diff --git a/SwiftCompilerSources/Sources/Basic/Utils.swift b/SwiftCompilerSources/Sources/Basic/Utils.swift index 0a359bbda210f..7ae0354394105 100644 --- a/SwiftCompilerSources/Sources/Basic/Utils.swift +++ b/SwiftCompilerSources/Sources/Basic/Utils.swift @@ -120,19 +120,19 @@ public typealias SwiftObject = UnsafeMutablePointer extension UnsafeMutablePointer where Pointee == BridgedSwiftObject { public init(_ object: T) { - let ptr = Unmanaged.passUnretained(object).toOpaque() + let ptr = unsafeBitCast(object, to: UnsafeMutableRawPointer.self) self = ptr.bindMemory(to: BridgedSwiftObject.self, capacity: 1) } public func getAs(_ objectType: T.Type) -> T { - return Unmanaged.fromOpaque(self).takeUnretainedValue() + return unsafeBitCast(self, to: T.self) } } extension Optional where Wrapped == UnsafeMutablePointer { public func getAs(_ objectType: T.Type) -> T? { if let pointer = self { - return Unmanaged.fromOpaque(pointer).takeUnretainedValue() + return pointer.getAs(objectType) } return nil } diff --git a/SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/SimplifyStrongRetainRelease.swift b/SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/SimplifyStrongRetainRelease.swift index 46e0a974dd83d..9c90e002a3946 100644 --- a/SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/SimplifyStrongRetainRelease.swift +++ b/SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/SimplifyStrongRetainRelease.swift @@ -67,6 +67,9 @@ extension StrongReleaseInst : Simplifyable, SILCombineSimplifyable { /// Returns true if \p value is something where reference counting instructions /// don't have any effect. private func isNotReferenceCounted(value: Value) -> Bool { + if value.type.isMarkedAsImmortal { + return true + } switch value { case let cfi as ConvertFunctionInst: return isNotReferenceCounted(value: cfi.fromFunction) diff --git a/SwiftCompilerSources/Sources/Optimizer/Utilities/EscapeUtils.swift b/SwiftCompilerSources/Sources/Optimizer/Utilities/EscapeUtils.swift index d61beabf05f12..0f3a35cfc32ac 100644 --- a/SwiftCompilerSources/Sources/Optimizer/Utilities/EscapeUtils.swift +++ b/SwiftCompilerSources/Sources/Optimizer/Utilities/EscapeUtils.swift @@ -819,14 +819,14 @@ fileprivate struct EscapeWalker : ValueDefUseWalker, } private func hasRelevantType(_ value: Value, at path: SmallProjectionPath) -> Bool { - if !value.hasTrivialNonPointerType { - return true - } if visitor.followTrivialTypes && // When part of a class field only need to follow non-trivial types !path.hasClassProjection { return true } + if !value.hasTrivialNonPointerType { + return true + } return false } diff --git a/SwiftCompilerSources/Sources/SIL/BasicBlock.swift b/SwiftCompilerSources/Sources/SIL/BasicBlock.swift index 5ad038d5f5ddb..8f19e548ebed7 100644 --- a/SwiftCompilerSources/Sources/SIL/BasicBlock.swift +++ b/SwiftCompilerSources/Sources/SIL/BasicBlock.swift @@ -13,6 +13,7 @@ import Basic import SILBridging +@_semantics("arc.immortal") final public class BasicBlock : CustomStringConvertible, HasShortDescription { public var next: BasicBlock? { SILBasicBlock_next(bridged).block } public var previous: BasicBlock? { SILBasicBlock_previous(bridged).block } diff --git a/SwiftCompilerSources/Sources/SIL/Function.swift b/SwiftCompilerSources/Sources/SIL/Function.swift index a9a3453efe36e..8063ace3ef453 100644 --- a/SwiftCompilerSources/Sources/SIL/Function.swift +++ b/SwiftCompilerSources/Sources/SIL/Function.swift @@ -13,6 +13,7 @@ import Basic import SILBridging +@_semantics("arc.immortal") final public class Function : CustomStringConvertible, HasShortDescription, Hashable { public private(set) var effects = FunctionEffects() diff --git a/SwiftCompilerSources/Sources/SIL/Instruction.swift b/SwiftCompilerSources/Sources/SIL/Instruction.swift index 9713a50255f67..1eb9515371eaa 100644 --- a/SwiftCompilerSources/Sources/SIL/Instruction.swift +++ b/SwiftCompilerSources/Sources/SIL/Instruction.swift @@ -17,6 +17,7 @@ import SILBridging // Instruction base classes //===----------------------------------------------------------------------===// +@_semantics("arc.immortal") public class Instruction : CustomStringConvertible, Hashable { final public var next: Instruction? { SILInstruction_next(bridged).instruction diff --git a/SwiftCompilerSources/Sources/SIL/Type.swift b/SwiftCompilerSources/Sources/SIL/Type.swift index 57aa3b9ae05b9..99bf716694283 100644 --- a/SwiftCompilerSources/Sources/SIL/Type.swift +++ b/SwiftCompilerSources/Sources/SIL/Type.swift @@ -58,7 +58,9 @@ public struct Type : CustomStringConvertible, NoReflectionChildren { } public var isCalleeConsumedFunction: Bool { SILType_isCalleeConsumedFunction(bridged) } - + + public var isMarkedAsImmortal: Bool { SILType_isMarkedAsImmortal(bridged) } + public func getIndexOfEnumCase(withName name: String) -> Int? { let idx = name._withStringRef { SILType_getCaseIdxOfEnumType(bridged, $0) diff --git a/SwiftCompilerSources/Sources/SIL/Value.swift b/SwiftCompilerSources/Sources/SIL/Value.swift index 2e33e756d644d..3136d5771b82d 100644 --- a/SwiftCompilerSources/Sources/SIL/Value.swift +++ b/SwiftCompilerSources/Sources/SIL/Value.swift @@ -13,6 +13,7 @@ import Basic import SILBridging +@_semantics("arc.immortal") public protocol Value : AnyObject, CustomStringConvertible { var uses: UseList { get } var type: Type { get } @@ -157,18 +158,21 @@ extension Value { extension BridgedValue { public func getAs(_ valueType: T.Type) -> T { obj.getAs(T.self) } + public var value: Value { getAs(AnyObject.self) as! Value } +} + +extension BridgedClassifiedValue { public var value: Value { - // This is much faster than a conformance lookup with `as! Value`. - let v = getAs(AnyObject.self) - switch v { - case let inst as SingleValueInstruction: - return inst - case let arg as Argument: - return arg - case let mvr as MultipleValueInstructionResult: - return mvr - case let undef as Undef: - return undef + // Doing the type check in C++ is much faster than a conformance lookup with `as! Value`. + switch kind { + case .SingleValueInstruction: + return obj.getAs(SingleValueInstruction.self) + case .Argument: + return obj.getAs(Argument.self) + case .MultipleValueInstructionResult: + return obj.getAs(MultipleValueInstructionResult.self) + case .Undef: + return obj.getAs(Undef.self) default: fatalError("unknown Value type") } diff --git a/include/swift/AST/SemanticAttrs.def b/include/swift/AST/SemanticAttrs.def index 2e408049178b5..5bcf41a3800cf 100644 --- a/include/swift/AST/SemanticAttrs.def +++ b/include/swift/AST/SemanticAttrs.def @@ -76,6 +76,13 @@ SEMANTICS_ATTR(OPTIMIZE_SIL_SPECIALIZE_GENERIC_SIZE_NEVER, SEMANTICS_ATTR(OPTIMIZE_SIL_SPECIALIZE_OWNED2GUARANTEE_NEVER, "optimize.sil.specialize.owned2guarantee.never") +// To be used on a nominal type declaration. +// Assumes that a class (or class references inside a nominal type) are immortal. +// ARC operations on such types can be eliminated. +// If specified on a protocol declaration, all types which conform to that protocol +// are assumed to be immortal. +SEMANTICS_ATTR(ARC_IMMORTAL, "arc.immortal") + SEMANTICS_ATTR(OSLOG_MESSAGE_TYPE, "oslog.message.type") SEMANTICS_ATTR(OSLOG_MESSAGE_INIT_INTERPOLATION, "oslog.message.init_interpolation") SEMANTICS_ATTR(OSLOG_MESSAGE_INIT_STRING_LITERAL, "oslog.message.init_stringliteral") diff --git a/include/swift/SIL/SILBridging.h b/include/swift/SIL/SILBridging.h index 2e4260f7634ff..95db0f73c16f6 100644 --- a/include/swift/SIL/SILBridging.h +++ b/include/swift/SIL/SILBridging.h @@ -133,6 +133,19 @@ typedef struct { SwiftObject obj; } BridgedValue; +// For fast SILValue -> Value briding. +// This is doing the type checks in C++ rather than in Swift. +// It's used for getting the value of an Operand, which is a time critical function. +typedef struct { + SwiftObject obj; + enum class Kind { + SingleValueInstruction, + Argument, + MultipleValueInstructionResult, + Undef + } kind; +} BridgedClassifiedValue; + typedef struct { OptionalSwiftObject obj; } OptionalBridgedValue; @@ -305,7 +318,7 @@ OptionalBridgedSuccessor SILSuccessor_getNext(BridgedSuccessor succ); BridgedBasicBlock SILSuccessor_getTargetBlock(BridgedSuccessor succ); BridgedInstruction SILSuccessor_getContainingInst(BridgedSuccessor succ); -BridgedValue Operand_getValue(BridgedOperand); +BridgedClassifiedValue Operand_getValue(BridgedOperand); OptionalBridgedOperand Operand_nextUse(BridgedOperand); BridgedInstruction Operand_getUser(BridgedOperand); SwiftInt Operand_isTypeDependent(BridgedOperand); @@ -332,6 +345,7 @@ BridgedType SILType_instanceTypeOfMetatype(BridgedType type, BridgedFunction fun BridgedDecl SILType_getNominal(BridgedType type); bool SILType_isOrContainsObjectiveCClass(BridgedType type); bool SILType_isCalleeConsumedFunction(BridgedType type); +bool SILType_isMarkedAsImmortal(BridgedType type); SwiftInt SILType_getNumTupleElements(BridgedType type); BridgedType SILType_getTupleElementType(BridgedType type, SwiftInt elementIdx); SwiftInt SILType_getNumNominalFields(BridgedType type); diff --git a/lib/SIL/Utils/SILBridging.cpp b/lib/SIL/Utils/SILBridging.cpp index d37c28be13d6f..15fdf9678046e 100644 --- a/lib/SIL/Utils/SILBridging.cpp +++ b/lib/SIL/Utils/SILBridging.cpp @@ -11,6 +11,8 @@ //===----------------------------------------------------------------------===// #include "swift/Basic/BridgingUtils.h" +#include "swift/AST/Attr.h" +#include "swift/AST/SemanticAttrs.h" #include "swift/SIL/SILNode.h" #include "swift/SIL/ApplySite.h" #include "swift/SIL/SILBridgingUtils.h" @@ -447,8 +449,21 @@ static Operand *castToOperand(BridgedOperand operand) { return const_cast(static_cast(operand.op)); } -BridgedValue Operand_getValue(BridgedOperand operand) { - return {castToOperand(operand)->get()}; +BridgedClassifiedValue Operand_getValue(BridgedOperand operand) { + SILValue v = castToOperand(operand)->get(); + BridgedClassifiedValue::Kind k; + if (isa(v)) { + k = BridgedClassifiedValue::Kind::SingleValueInstruction; + } else if (isa(v)) { + k = BridgedClassifiedValue::Kind::Argument; + } else if (isa(v)) { + k = BridgedClassifiedValue::Kind::MultipleValueInstructionResult; + } else if (isa(v)) { + k = BridgedClassifiedValue::Kind::Undef; + } else { + llvm_unreachable("unknown SILValue"); + } + return {castToOperand(operand)->get(), k}; } OptionalBridgedOperand Operand_nextUse(BridgedOperand operand) { @@ -574,6 +589,48 @@ bool SILType_isCalleeConsumedFunction(BridgedType type) { return funcTy->isCalleeConsumed() && !funcTy->isNoEscape(); } +static bool hasImmortalAttr(NominalTypeDecl *nominal) { + if (auto *semAttr = nominal->getAttrs().getAttribute()) { + if (semAttr->Value == semantics::ARC_IMMORTAL) { + return true; + } + } + return false; +} + +static bool isMarkedAsImmortal(NominalTypeDecl *nominal) { + if (hasImmortalAttr(nominal)) + return true; + + if (!isa(nominal)) { + for (ProtocolDecl *p : nominal->getAllProtocols()) { + if (hasImmortalAttr(p)) + return true; + } + } + return false; +} + +bool SILType_isMarkedAsImmortal(BridgedType type) { + SILType ty = castToSILType(type); + NominalTypeDecl *nominal = ty.getNominalOrBoundGenericNominal(); + if (!nominal) + return false; + + if (isMarkedAsImmortal(nominal)) + return true; + + if (ClassDecl *cl = dyn_cast(nominal)) { + cl = cl->getSuperclassDecl(); + while (cl) { + if (isMarkedAsImmortal(cl)) + return true; + cl = cl->getSuperclassDecl(); + } + } + return false; +} + SwiftInt SILType_getNumTupleElements(BridgedType type) { TupleType *tupleTy = castToSILType(type).castTo(); return tupleTy->getNumElements(); diff --git a/test/SILOptimizer/immortal-arc-elimination.sil b/test/SILOptimizer/immortal-arc-elimination.sil index 7c1137c2b3758..d7d3c7851ad35 100644 --- a/test/SILOptimizer/immortal-arc-elimination.sil +++ b/test/SILOptimizer/immortal-arc-elimination.sil @@ -61,3 +61,55 @@ bb0: return %1 : $Builtin.BridgeObject } +@_semantics("arc.immortal") protocol P : AnyObject { +} + +@_semantics("arc.immortal") class C { + init() +} + +@_inheritsConvenienceInitializers class D : C { + override init() +} + +class E : P { + init() +} + +// CHECK-LABEL: sil @testSemanticsOnClass +// CHECK-NOT: retain +// CHECK: } // end sil function 'testSemanticsOnClass' +sil @testSemanticsOnClass : $@convention(thin) (@guaranteed C) -> @owned C { +bb0(%0 : $C): + strong_retain %0 : $C + return %0 : $C // id: %3 +} + +// CHECK-LABEL: sil @testSemanticsOnDerivedClass +// CHECK-NOT: retain +// CHECK: } // end sil function 'testSemanticsOnDerivedClass' +sil @testSemanticsOnDerivedClass : $@convention(thin) (@guaranteed D) -> @owned D { +bb0(%0 : $D): + strong_retain %0 : $D + return %0 : $D +} + +// CHECK-LABEL: sil @testSemanticsOnConformingClass +// CHECK-NOT: retain +// CHECK: } // end sil function 'testSemanticsOnConformingClass' +sil @testSemanticsOnConformingClass : $@convention(thin) (@guaranteed E) -> @owned E { +bb0(%0 : $E): + strong_retain %0 : $E + return %0 : $E +} + +// CHECK-LABEL: sil @testSemanticsOnProtocol +// CHECK-NOT: retain +// CHECK: } // end sil function 'testSemanticsOnProtocol' +sil @testSemanticsOnProtocol : $@convention(thin) (@guaranteed any P) -> @owned any P { +bb0(%0 : $any P): + strong_retain %0 : $any P + return %0 : $any P +} + +