Skip to content

Commit 7727ce7

Browse files
authored
Merge pull request #64359 from eeckstein/swift-compile-time
Compile time improvements in the Swift optimizer
2 parents d9e028f + 053a38d commit 7727ce7

File tree

12 files changed

+163
-21
lines changed

12 files changed

+163
-21
lines changed

SwiftCompilerSources/Sources/Basic/Utils.swift

+3-3
Original file line numberDiff line numberDiff line change
@@ -120,19 +120,19 @@ public typealias SwiftObject = UnsafeMutablePointer<BridgedSwiftObject>
120120

121121
extension UnsafeMutablePointer where Pointee == BridgedSwiftObject {
122122
public init<T: AnyObject>(_ object: T) {
123-
let ptr = Unmanaged.passUnretained(object).toOpaque()
123+
let ptr = unsafeBitCast(object, to: UnsafeMutableRawPointer.self)
124124
self = ptr.bindMemory(to: BridgedSwiftObject.self, capacity: 1)
125125
}
126126

127127
public func getAs<T: AnyObject>(_ objectType: T.Type) -> T {
128-
return Unmanaged<T>.fromOpaque(self).takeUnretainedValue()
128+
return unsafeBitCast(self, to: T.self)
129129
}
130130
}
131131

132132
extension Optional where Wrapped == UnsafeMutablePointer<BridgedSwiftObject> {
133133
public func getAs<T: AnyObject>(_ objectType: T.Type) -> T? {
134134
if let pointer = self {
135-
return Unmanaged<T>.fromOpaque(pointer).takeUnretainedValue()
135+
return pointer.getAs(objectType)
136136
}
137137
return nil
138138
}

SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/SimplifyStrongRetainRelease.swift

+3
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,9 @@ extension StrongReleaseInst : Simplifyable, SILCombineSimplifyable {
6767
/// Returns true if \p value is something where reference counting instructions
6868
/// don't have any effect.
6969
private func isNotReferenceCounted(value: Value) -> Bool {
70+
if value.type.isMarkedAsImmortal {
71+
return true
72+
}
7073
switch value {
7174
case let cfi as ConvertFunctionInst:
7275
return isNotReferenceCounted(value: cfi.fromFunction)

SwiftCompilerSources/Sources/Optimizer/Utilities/EscapeUtils.swift

+3-3
Original file line numberDiff line numberDiff line change
@@ -819,14 +819,14 @@ fileprivate struct EscapeWalker<V: EscapeVisitor> : ValueDefUseWalker,
819819
}
820820

821821
private func hasRelevantType(_ value: Value, at path: SmallProjectionPath) -> Bool {
822-
if !value.hasTrivialNonPointerType {
823-
return true
824-
}
825822
if visitor.followTrivialTypes &&
826823
// When part of a class field only need to follow non-trivial types
827824
!path.hasClassProjection {
828825
return true
829826
}
827+
if !value.hasTrivialNonPointerType {
828+
return true
829+
}
830830
return false
831831
}
832832

SwiftCompilerSources/Sources/SIL/BasicBlock.swift

+1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
import Basic
1414
import SILBridging
1515

16+
@_semantics("arc.immortal")
1617
final public class BasicBlock : CustomStringConvertible, HasShortDescription {
1718
public var next: BasicBlock? { SILBasicBlock_next(bridged).block }
1819
public var previous: BasicBlock? { SILBasicBlock_previous(bridged).block }

SwiftCompilerSources/Sources/SIL/Function.swift

+1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
import Basic
1414
import SILBridging
1515

16+
@_semantics("arc.immortal")
1617
final public class Function : CustomStringConvertible, HasShortDescription, Hashable {
1718
public private(set) var effects = FunctionEffects()
1819

SwiftCompilerSources/Sources/SIL/Instruction.swift

+1
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import SILBridging
1717
// Instruction base classes
1818
//===----------------------------------------------------------------------===//
1919

20+
@_semantics("arc.immortal")
2021
public class Instruction : CustomStringConvertible, Hashable {
2122
final public var next: Instruction? {
2223
SILInstruction_next(bridged).instruction

SwiftCompilerSources/Sources/SIL/Type.swift

+3-1
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,9 @@ public struct Type : CustomStringConvertible, NoReflectionChildren {
5858
}
5959

6060
public var isCalleeConsumedFunction: Bool { SILType_isCalleeConsumedFunction(bridged) }
61-
61+
62+
public var isMarkedAsImmortal: Bool { SILType_isMarkedAsImmortal(bridged) }
63+
6264
public func getIndexOfEnumCase(withName name: String) -> Int? {
6365
let idx = name._withStringRef {
6466
SILType_getCaseIdxOfEnumType(bridged, $0)

SwiftCompilerSources/Sources/SIL/Value.swift

+15-11
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
import Basic
1414
import SILBridging
1515

16+
@_semantics("arc.immortal")
1617
public protocol Value : AnyObject, CustomStringConvertible {
1718
var uses: UseList { get }
1819
var type: Type { get }
@@ -157,18 +158,21 @@ extension Value {
157158
extension BridgedValue {
158159
public func getAs<T: AnyObject>(_ valueType: T.Type) -> T { obj.getAs(T.self) }
159160

161+
public var value: Value { getAs(AnyObject.self) as! Value }
162+
}
163+
164+
extension BridgedClassifiedValue {
160165
public var value: Value {
161-
// This is much faster than a conformance lookup with `as! Value`.
162-
let v = getAs(AnyObject.self)
163-
switch v {
164-
case let inst as SingleValueInstruction:
165-
return inst
166-
case let arg as Argument:
167-
return arg
168-
case let mvr as MultipleValueInstructionResult:
169-
return mvr
170-
case let undef as Undef:
171-
return undef
166+
// Doing the type check in C++ is much faster than a conformance lookup with `as! Value`.
167+
switch kind {
168+
case .SingleValueInstruction:
169+
return obj.getAs(SingleValueInstruction.self)
170+
case .Argument:
171+
return obj.getAs(Argument.self)
172+
case .MultipleValueInstructionResult:
173+
return obj.getAs(MultipleValueInstructionResult.self)
174+
case .Undef:
175+
return obj.getAs(Undef.self)
172176
default:
173177
fatalError("unknown Value type")
174178
}

include/swift/AST/SemanticAttrs.def

+7
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,13 @@ SEMANTICS_ATTR(OPTIMIZE_SIL_SPECIALIZE_GENERIC_SIZE_NEVER,
7676
SEMANTICS_ATTR(OPTIMIZE_SIL_SPECIALIZE_OWNED2GUARANTEE_NEVER,
7777
"optimize.sil.specialize.owned2guarantee.never")
7878

79+
// To be used on a nominal type declaration.
80+
// Assumes that a class (or class references inside a nominal type) are immortal.
81+
// ARC operations on such types can be eliminated.
82+
// If specified on a protocol declaration, all types which conform to that protocol
83+
// are assumed to be immortal.
84+
SEMANTICS_ATTR(ARC_IMMORTAL, "arc.immortal")
85+
7986
SEMANTICS_ATTR(OSLOG_MESSAGE_TYPE, "oslog.message.type")
8087
SEMANTICS_ATTR(OSLOG_MESSAGE_INIT_INTERPOLATION, "oslog.message.init_interpolation")
8188
SEMANTICS_ATTR(OSLOG_MESSAGE_INIT_STRING_LITERAL, "oslog.message.init_stringliteral")

include/swift/SIL/SILBridging.h

+15-1
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,19 @@ typedef struct {
133133
SwiftObject obj;
134134
} BridgedValue;
135135

136+
// For fast SILValue -> Value briding.
137+
// This is doing the type checks in C++ rather than in Swift.
138+
// It's used for getting the value of an Operand, which is a time critical function.
139+
typedef struct {
140+
SwiftObject obj;
141+
enum class Kind {
142+
SingleValueInstruction,
143+
Argument,
144+
MultipleValueInstructionResult,
145+
Undef
146+
} kind;
147+
} BridgedClassifiedValue;
148+
136149
typedef struct {
137150
OptionalSwiftObject obj;
138151
} OptionalBridgedValue;
@@ -305,7 +318,7 @@ OptionalBridgedSuccessor SILSuccessor_getNext(BridgedSuccessor succ);
305318
BridgedBasicBlock SILSuccessor_getTargetBlock(BridgedSuccessor succ);
306319
BridgedInstruction SILSuccessor_getContainingInst(BridgedSuccessor succ);
307320

308-
BridgedValue Operand_getValue(BridgedOperand);
321+
BridgedClassifiedValue Operand_getValue(BridgedOperand);
309322
OptionalBridgedOperand Operand_nextUse(BridgedOperand);
310323
BridgedInstruction Operand_getUser(BridgedOperand);
311324
SwiftInt Operand_isTypeDependent(BridgedOperand);
@@ -332,6 +345,7 @@ BridgedType SILType_instanceTypeOfMetatype(BridgedType type, BridgedFunction fun
332345
BridgedDecl SILType_getNominal(BridgedType type);
333346
bool SILType_isOrContainsObjectiveCClass(BridgedType type);
334347
bool SILType_isCalleeConsumedFunction(BridgedType type);
348+
bool SILType_isMarkedAsImmortal(BridgedType type);
335349
SwiftInt SILType_getNumTupleElements(BridgedType type);
336350
BridgedType SILType_getTupleElementType(BridgedType type, SwiftInt elementIdx);
337351
SwiftInt SILType_getNumNominalFields(BridgedType type);

lib/SIL/Utils/SILBridging.cpp

+59-2
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111
//===----------------------------------------------------------------------===//
1212

1313
#include "swift/Basic/BridgingUtils.h"
14+
#include "swift/AST/Attr.h"
15+
#include "swift/AST/SemanticAttrs.h"
1416
#include "swift/SIL/SILNode.h"
1517
#include "swift/SIL/ApplySite.h"
1618
#include "swift/SIL/SILBridgingUtils.h"
@@ -447,8 +449,21 @@ static Operand *castToOperand(BridgedOperand operand) {
447449
return const_cast<Operand *>(static_cast<const Operand *>(operand.op));
448450
}
449451

450-
BridgedValue Operand_getValue(BridgedOperand operand) {
451-
return {castToOperand(operand)->get()};
452+
BridgedClassifiedValue Operand_getValue(BridgedOperand operand) {
453+
SILValue v = castToOperand(operand)->get();
454+
BridgedClassifiedValue::Kind k;
455+
if (isa<SingleValueInstruction>(v)) {
456+
k = BridgedClassifiedValue::Kind::SingleValueInstruction;
457+
} else if (isa<SILArgument>(v)) {
458+
k = BridgedClassifiedValue::Kind::Argument;
459+
} else if (isa<MultipleValueInstructionResult>(v)) {
460+
k = BridgedClassifiedValue::Kind::MultipleValueInstructionResult;
461+
} else if (isa<SILUndef>(v)) {
462+
k = BridgedClassifiedValue::Kind::Undef;
463+
} else {
464+
llvm_unreachable("unknown SILValue");
465+
}
466+
return {castToOperand(operand)->get(), k};
452467
}
453468

454469
OptionalBridgedOperand Operand_nextUse(BridgedOperand operand) {
@@ -574,6 +589,48 @@ bool SILType_isCalleeConsumedFunction(BridgedType type) {
574589
return funcTy->isCalleeConsumed() && !funcTy->isNoEscape();
575590
}
576591

592+
static bool hasImmortalAttr(NominalTypeDecl *nominal) {
593+
if (auto *semAttr = nominal->getAttrs().getAttribute<SemanticsAttr>()) {
594+
if (semAttr->Value == semantics::ARC_IMMORTAL) {
595+
return true;
596+
}
597+
}
598+
return false;
599+
}
600+
601+
static bool isMarkedAsImmortal(NominalTypeDecl *nominal) {
602+
if (hasImmortalAttr(nominal))
603+
return true;
604+
605+
if (!isa<ProtocolDecl>(nominal)) {
606+
for (ProtocolDecl *p : nominal->getAllProtocols()) {
607+
if (hasImmortalAttr(p))
608+
return true;
609+
}
610+
}
611+
return false;
612+
}
613+
614+
bool SILType_isMarkedAsImmortal(BridgedType type) {
615+
SILType ty = castToSILType(type);
616+
NominalTypeDecl *nominal = ty.getNominalOrBoundGenericNominal();
617+
if (!nominal)
618+
return false;
619+
620+
if (isMarkedAsImmortal(nominal))
621+
return true;
622+
623+
if (ClassDecl *cl = dyn_cast<ClassDecl>(nominal)) {
624+
cl = cl->getSuperclassDecl();
625+
while (cl) {
626+
if (isMarkedAsImmortal(cl))
627+
return true;
628+
cl = cl->getSuperclassDecl();
629+
}
630+
}
631+
return false;
632+
}
633+
577634
SwiftInt SILType_getNumTupleElements(BridgedType type) {
578635
TupleType *tupleTy = castToSILType(type).castTo<TupleType>();
579636
return tupleTy->getNumElements();

test/SILOptimizer/immortal-arc-elimination.sil

+52
Original file line numberDiff line numberDiff line change
@@ -61,3 +61,55 @@ bb0:
6161
return %1 : $Builtin.BridgeObject
6262
}
6363

64+
@_semantics("arc.immortal") protocol P : AnyObject {
65+
}
66+
67+
@_semantics("arc.immortal") class C {
68+
init()
69+
}
70+
71+
@_inheritsConvenienceInitializers class D : C {
72+
override init()
73+
}
74+
75+
class E : P {
76+
init()
77+
}
78+
79+
// CHECK-LABEL: sil @testSemanticsOnClass
80+
// CHECK-NOT: retain
81+
// CHECK: } // end sil function 'testSemanticsOnClass'
82+
sil @testSemanticsOnClass : $@convention(thin) (@guaranteed C) -> @owned C {
83+
bb0(%0 : $C):
84+
strong_retain %0 : $C
85+
return %0 : $C // id: %3
86+
}
87+
88+
// CHECK-LABEL: sil @testSemanticsOnDerivedClass
89+
// CHECK-NOT: retain
90+
// CHECK: } // end sil function 'testSemanticsOnDerivedClass'
91+
sil @testSemanticsOnDerivedClass : $@convention(thin) (@guaranteed D) -> @owned D {
92+
bb0(%0 : $D):
93+
strong_retain %0 : $D
94+
return %0 : $D
95+
}
96+
97+
// CHECK-LABEL: sil @testSemanticsOnConformingClass
98+
// CHECK-NOT: retain
99+
// CHECK: } // end sil function 'testSemanticsOnConformingClass'
100+
sil @testSemanticsOnConformingClass : $@convention(thin) (@guaranteed E) -> @owned E {
101+
bb0(%0 : $E):
102+
strong_retain %0 : $E
103+
return %0 : $E
104+
}
105+
106+
// CHECK-LABEL: sil @testSemanticsOnProtocol
107+
// CHECK-NOT: retain
108+
// CHECK: } // end sil function 'testSemanticsOnProtocol'
109+
sil @testSemanticsOnProtocol : $@convention(thin) (@guaranteed any P) -> @owned any P {
110+
bb0(%0 : $any P):
111+
strong_retain %0 : $any P
112+
return %0 : $any P
113+
}
114+
115+

0 commit comments

Comments
 (0)