mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
IRGen: fast casting to final classes
When casting a class instance to a final class, we can directly compare the isa-pointer with address of the metadata. This avoids a call to `swift_dynamicCastClass`. It also avoids a call to the metadata accessor of the class (which calls `swift_getInitializedObjCClass`). For comparing the metadata pointers it's not required that the metadata is fully initialized.
This commit is contained in:
18
test/Casting/Inputs/classes.swift
Normal file
18
test/Casting/Inputs/classes.swift
Normal file
@@ -0,0 +1,18 @@
|
||||
|
||||
public protocol P : AnyObject { }
|
||||
|
||||
public class Base {
|
||||
public init() {}
|
||||
}
|
||||
|
||||
public class Nonfinal : Base, P {
|
||||
public override init() {}
|
||||
}
|
||||
|
||||
final public class Final : Base, P {
|
||||
public override init() {}
|
||||
}
|
||||
|
||||
open class OpenBase {
|
||||
public init() {}
|
||||
}
|
||||
159
test/Casting/fast_class_casts.swift
Normal file
159
test/Casting/fast_class_casts.swift
Normal file
@@ -0,0 +1,159 @@
|
||||
// RUN: %empty-directory(%t)
|
||||
|
||||
// 1. functional test:
|
||||
|
||||
// RUN: %target-build-swift -parse-as-library -wmo -emit-module -emit-module-path=%t/Classes.swiftmodule -module-name=Classes %S/Inputs/classes.swift -c -o %t/classes.o
|
||||
// RUN: %target-build-swift -parse-as-library -wmo -enable-library-evolution -emit-module -emit-module-path=%t/ResilientClasses.swiftmodule -module-name=ResilientClasses %S/Inputs/classes.swift -c -o %t/resilientclasses.o
|
||||
// RUN: %target-build-swift -wmo -module-name=Main -I%t %s -c -o %t/main.o
|
||||
// RUN: %target-swiftc_driver %t/main.o %t/classes.o %t/resilientclasses.o -o %t/a.out
|
||||
// RUN: %target-codesign %t/a.out
|
||||
// RUN: %target-run %t/a.out | %FileCheck %s -check-prefix=CHECK-OUTPUT
|
||||
|
||||
// 2. check if the generated IR looks like expected:
|
||||
|
||||
// RUN: %target-swift-frontend -module-name=Main -I%t %s -emit-ir -g -o - | %FileCheck %s
|
||||
|
||||
// REQUIRES: executable_test
|
||||
|
||||
|
||||
import Classes
|
||||
import ResilientClasses
|
||||
|
||||
final class Internal : Classes.OpenBase, Hashable {
|
||||
func hash(into hasher: inout Hasher) {}
|
||||
static func == (lhs: Internal, rhs: Internal) -> Bool { return false }
|
||||
}
|
||||
|
||||
final class DerivedFromResilient : ResilientClasses.OpenBase {
|
||||
}
|
||||
|
||||
final class Generic<T> : Classes.OpenBase {
|
||||
}
|
||||
|
||||
// CHECK-LABEL: define {{.*}} @"$s4Main14castToNonfinaly7Classes0D0CSgAC4BaseCF"
|
||||
// CHECK: @swift_dynamicCastClass
|
||||
// CHECK: }
|
||||
@inline(never)
|
||||
func castToNonfinal(_ b: Classes.Base) -> Classes.Nonfinal? {
|
||||
return b as? Classes.Nonfinal
|
||||
}
|
||||
|
||||
// CHECK-LABEL: define {{.*}} @"$s4Main11castToFinaly7Classes0D0CSgAC4BaseCF"
|
||||
// CHECK-NOT: @swift_dynamicCastClass
|
||||
// CHECK: }
|
||||
@inline(never)
|
||||
func castToFinal(_ b: Classes.Base) -> Classes.Final? {
|
||||
return b as? Classes.Final
|
||||
}
|
||||
|
||||
// CHECK-LABEL: define {{.*}} @"$s4Main24unconditionalCastToFinaly7Classes0E0CAC4BaseCF"
|
||||
// CHECK-NOT: @swift_dynamicCastClass
|
||||
// CHECK: }
|
||||
@inline(never)
|
||||
func unconditionalCastToFinal(_ b: Classes.Base) -> Classes.Final {
|
||||
return b as! Classes.Final
|
||||
}
|
||||
|
||||
// CHECK-LABEL: define {{.*}} @"$s4Main20castToResilientFinaly0D7Classes0E0CSgAC4BaseCF"
|
||||
// CHECK: @swift_dynamicCastClass
|
||||
// CHECK: }
|
||||
@inline(never)
|
||||
func castToResilientFinal(_ b: ResilientClasses.Base) -> ResilientClasses.Final? {
|
||||
return b as? ResilientClasses.Final
|
||||
}
|
||||
|
||||
// CHECK-LABEL: define {{.*}} @"$s4Main19castProtocolToFinaly7Classes0E0CSgAC1P_pF"
|
||||
// CHECK-NOT: @swift_dynamicCastClass
|
||||
// CHECK: }
|
||||
@inline(never)
|
||||
func castProtocolToFinal(_ p: Classes.P) -> Classes.Final? {
|
||||
return p as? Classes.Final
|
||||
}
|
||||
|
||||
// CHECK-LABEL: define {{.*}} @"$s4Main14castToInternalyAA0D0CSg7Classes8OpenBaseCF"
|
||||
// CHECK-NOT: @swift_dynamicCastClass
|
||||
// CHECK: }
|
||||
@inline(never)
|
||||
func castToInternal(_ b: Classes.OpenBase) -> Internal? {
|
||||
return b as? Internal
|
||||
}
|
||||
|
||||
// CHECK-LABEL: define {{.*}} @"$s4Main23castAnyObjectToInternalyAA0F0CSgyXlF"
|
||||
// CHECK: @swift_dynamicCastClass
|
||||
// CHECK: }
|
||||
@inline(never)
|
||||
func castAnyObjectToInternal(_ a: AnyObject) -> Internal? {
|
||||
return a as? Internal
|
||||
}
|
||||
|
||||
// CHECK-LABEL: define {{.*}} @"$s4Main26castToDerivedFromResilientyAA0deF0CSg0F7Classes8OpenBaseCF"
|
||||
// CHECK: @swift_dynamicCastClass
|
||||
// CHECK: }
|
||||
@inline(never)
|
||||
func castToDerivedFromResilient(_ b: ResilientClasses.OpenBase) -> DerivedFromResilient? {
|
||||
return b as? DerivedFromResilient
|
||||
}
|
||||
|
||||
// CHECK-LABEL: define {{.*}} @"$s4Main13castToGenericyAA0D0CySiGSg7Classes8OpenBaseCF"
|
||||
// CHECK: @swift_dynamicCastClass
|
||||
// CHECK: }
|
||||
@inline(never)
|
||||
func castToGeneric(_ b: Classes.OpenBase) -> Generic<Int>? {
|
||||
return b as? Generic<Int>
|
||||
}
|
||||
|
||||
// CHECK-LABEL: define {{.*}} @"$s4Main14getAnyHashableys0cD0VAA8InternalCF"
|
||||
@inline(never)
|
||||
func getAnyHashable(_ i: Internal) -> AnyHashable {
|
||||
return i
|
||||
}
|
||||
|
||||
func test() {
|
||||
// CHECK-OUTPUT: nil
|
||||
print(castToNonfinal(Classes.Base()) as Any)
|
||||
// CHECK-OUTPUT: Optional(Classes.Nonfinal)
|
||||
print(castToNonfinal(Classes.Nonfinal()) as Any)
|
||||
|
||||
// CHECK-OUTPUT: nil
|
||||
print(castToFinal(Classes.Base()) as Any)
|
||||
// CHECK-OUTPUT: Optional(Classes.Final)
|
||||
print(castToFinal(Classes.Final()) as Any)
|
||||
// CHECK-OUTPUT: Classes.Final
|
||||
print(unconditionalCastToFinal(Classes.Final()) as Any)
|
||||
|
||||
// CHECK-OUTPUT: nil
|
||||
print(castToResilientFinal(ResilientClasses.Base()) as Any)
|
||||
// CHECK-OUTPUT: Optional(ResilientClasses.Final)
|
||||
print(castToResilientFinal(ResilientClasses.Final()) as Any)
|
||||
|
||||
// CHECK-OUTPUT: nil
|
||||
print(castProtocolToFinal(Classes.Nonfinal()) as Any)
|
||||
// CHECK-OUTPUT: Optional(Classes.Final)
|
||||
print(castProtocolToFinal(Classes.Final()) as Any)
|
||||
|
||||
// CHECK-OUTPUT: nil
|
||||
print(castToInternal(Classes.OpenBase()) as Any)
|
||||
// CHECK-OUTPUT: Optional(Main.Internal)
|
||||
print(castToInternal(Internal()) as Any)
|
||||
|
||||
// CHECK-OUTPUT: nil
|
||||
print(castAnyObjectToInternal(Classes.OpenBase()) as Any)
|
||||
// CHECK-OUTPUT: Optional(Main.Internal)
|
||||
let i = Internal()
|
||||
print(castAnyObjectToInternal(i) as Any)
|
||||
let i2 = castAnyObjectToInternal(getAnyHashable(i) as! AnyObject)
|
||||
precondition(i === i2)
|
||||
|
||||
// CHECK-OUTPUT: nil
|
||||
print(castToDerivedFromResilient(ResilientClasses.OpenBase()) as Any)
|
||||
// CHECK-OUTPUT: Optional(Main.DerivedFromResilient)
|
||||
print(castToDerivedFromResilient(DerivedFromResilient()) as Any)
|
||||
|
||||
// CHECK-OUTPUT: nil
|
||||
print(castToGeneric(Classes.OpenBase()) as Any)
|
||||
// CHECK-OUTPUT: Optional(Main.Generic<Swift.Int>)
|
||||
print(castToGeneric(Generic<Int>()) as Any)
|
||||
}
|
||||
|
||||
test()
|
||||
|
||||
Reference in New Issue
Block a user