Skip to content

IRGen: Invoke objc_opt_self directly when available. #30105

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Mar 3, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions include/swift/Runtime/RuntimeFunctions.def
Original file line number Diff line number Diff line change
Expand Up @@ -1313,6 +1313,12 @@ FUNCTION(ProtocolAddProtocol, protocol_addProtocol,
ARGS(ProtocolDescriptorPtrTy, ProtocolDescriptorPtrTy),
ATTRS(NoUnwind))

FUNCTION(ObjCOptSelf, objc_opt_self,
C_CC, AlwaysAvailable,
RETURNS(ObjCClassPtrTy),
ARGS(ObjCClassPtrTy),
ATTRS(NoUnwind))

FUNCTION(Malloc, malloc, C_CC, AlwaysAvailable,
RETURNS(Int8PtrTy),
ARGS(SizeTy),
Expand Down
26 changes: 26 additions & 0 deletions lib/IRGen/GenHeap.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1244,6 +1244,32 @@ llvm::Constant *IRGenModule::getFixLifetimeFn() {
return fixLifetime;
}

llvm::Constant *IRGenModule::getFixedClassInitializationFn() {
if (FixedClassInitializationFn)
return *FixedClassInitializationFn;

// If ObjC interop is disabled, we don't need to do fixed class
// initialization.
llvm::Constant *fn;
if (!ObjCInterop) {
fn = nullptr;
} else {
// In new enough ObjC runtimes, objc_opt_self provides a direct fast path
// to realize a class.
if (getAvailabilityContext()
.isContainedIn(Context.getSwift51Availability())) {
fn = getObjCOptSelfFn();
}
// Otherwise, the Swift runtime always provides a `get
else {
fn = getGetInitializedObjCClassFn();
}
}

FixedClassInitializationFn = fn;
return fn;
}

/// Fix the lifetime of a live value. This communicates to the LLVM level ARC
/// optimizer not to touch this value.
void IRGenFunction::emitFixLifetime(llvm::Value *value) {
Expand Down
4 changes: 4 additions & 0 deletions lib/IRGen/IRGenModule.h
Original file line number Diff line number Diff line change
Expand Up @@ -1224,13 +1224,17 @@ private: \
llvm::Constant *Id##Fn = nullptr;
#include "swift/Runtime/RuntimeFunctions.def"

Optional<llvm::Constant *> FixedClassInitializationFn;

llvm::Constant *FixLifetimeFn = nullptr;

mutable Optional<SpareBitVector> HeapPointerSpareBits;

//--- Generic ---------------------------------------------------------------
public:
llvm::Constant *getFixLifetimeFn();

llvm::Constant *getFixedClassInitializationFn();

/// The constructor used when generating code.
///
Expand Down
4 changes: 2 additions & 2 deletions lib/IRGen/MetadataRequest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -587,7 +587,7 @@ llvm::Value *irgen::emitObjCHeapMetadataRef(IRGenFunction &IGF,
if (allowUninitialized) return classObject;

// TODO: memoize this the same way that we memoize Swift type metadata?
return IGF.Builder.CreateCall(IGF.IGM.getGetInitializedObjCClassFn(),
return IGF.Builder.CreateCall(IGF.IGM.getFixedClassInitializationFn(),
classObject);
}

Expand All @@ -612,7 +612,7 @@ emitIdempotentClassMetadataInitialization(IRGenFunction &IGF,
llvm::Value *metadata) {
if (IGF.IGM.ObjCInterop) {
metadata = IGF.Builder.CreateBitCast(metadata, IGF.IGM.ObjCClassPtrTy);
metadata = IGF.Builder.CreateCall(IGF.IGM.getGetInitializedObjCClassFn(),
metadata = IGF.Builder.CreateCall(IGF.IGM.getFixedClassInitializationFn(),
metadata);
metadata = IGF.Builder.CreateBitCast(metadata, IGF.IGM.TypeMetadataPtrTy);
}
Expand Down
2 changes: 1 addition & 1 deletion test/ClangImporter/objc_ir.swift
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ func propertyAccess(b b: B) {
// CHECK-LABEL: define hidden swiftcc %TSo1BC* @"$s7objc_ir8downcast1aSo1BCSo1AC_tF"(
func downcast(a a: A) -> B {
// CHECK: [[CLASS:%.*]] = load %objc_class*, %objc_class** @"OBJC_CLASS_REF_$_B"
// CHECK: [[T0:%.*]] = call %objc_class* @swift_getInitializedObjCClass(%objc_class* [[CLASS]])
// CHECK: [[T0:%.*]] = call %objc_class* @{{.*}}(%objc_class* [[CLASS]])
// CHECK: [[T1:%.*]] = bitcast %objc_class* [[T0]] to i8*
// CHECK: call i8* @swift_dynamicCastObjCClassUnconditional(i8* [[A:%.*]], i8* [[T1]], {{.*}}) [[NOUNWIND:#[0-9]+]]
return a as! B
Expand Down
17 changes: 17 additions & 0 deletions test/IRGen/fixed_class_initialization.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// RUN: %target-swift-frontend -target x86_64-apple-macosx10.15 -module-name main -emit-ir %s | %FileCheck --check-prefix=CHECK --check-prefix=HAS_OPT_SELF %s
// RUN: %target-swift-frontend -target x86_64-apple-macosx10.14 -module-name main -emit-ir %s | %FileCheck --check-prefix=CHECK --check-prefix=NO_OPT_SELF %s

// REQUIRES: objc_interop
// REQUIRES: OS=macosx

class C {
var x: Int = 0
}

public func foof() -> Any.Type {
return C.self
}

// CHECK-LABEL: define {{.*}} %swift.metadata_response @"$s4main1CCMa"
// HAS_OPT_SELF: call {{.*}} @objc_opt_self
// NO_OPT_SELF: call {{.*}} @swift_getInitializedObjCClass
4 changes: 2 additions & 2 deletions test/IRGen/objc_casts.sil
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ bb0(%unused : $@thick T.Type, %obj : $NSObject):
// TODO: is this really necessary? also, this really shouldn't use a direct reference
// CHECK-NEXT: [[T1:%.*]] = bitcast %objc_object* [[T0]] to i8*
// CHECK-NEXT: [[T2a:%.*]] = load %objc_class*, %objc_class** @"OBJC_CLASS_REF_$_Foo"
// CHECK-NEXT: [[T2:%.*]] = call %objc_class* @swift_getInitializedObjCClass(%objc_class* [[T2a]])
// CHECK-NEXT: [[T2:%.*]] = call %objc_class* @{{.*}}(%objc_class* [[T2a]])
// CHECK-NEXT: [[T3:%.*]] = bitcast %objc_class* [[T2]] to i8*
// CHECK-NEXT: call i8* @swift_dynamicCastObjCClassUnconditional(i8* [[T1]], i8* [[T3]], {{.*}})
sil hidden @metatype_to_objc_class : $@convention(thin) <T> (@thick T.Type) -> () {
Expand All @@ -52,7 +52,7 @@ bb0(%metatype : $@thick T.Type):
// TODO: is this really necessary? also, this really shouldn't use a direct reference
// CHECK-NEXT: [[T1:%.*]] = bitcast %objc_object* [[T0]] to i8*
// CHECK-NEXT: [[T2a:%.*]] = load %objc_class*, %objc_class** @"OBJC_CLASS_REF_$_Foo"
// CHECK-NEXT: [[T2:%.*]] = call %objc_class* @swift_getInitializedObjCClass(%objc_class* [[T2a]])
// CHECK-NEXT: [[T2:%.*]] = call %objc_class* @{{.*}}(%objc_class* [[T2a]])
// CHECK-NEXT: [[T3:%.*]] = bitcast %objc_class* [[T2]] to i8*
// CHECK-NEXT: call i8* @swift_dynamicCastObjCClassUnconditional(i8* [[T1]], i8* [[T3]], {{.*}})
sil hidden @opt_metatype_to_objc_class : $@convention(thin) <T> (Optional<@thick T.Type>) -> () {
Expand Down
2 changes: 1 addition & 1 deletion test/IRGen/objc_generic_class_metadata.sil
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ entry:
apply %z<GenericClass<NSObject>>(%b) : $@convention(thin) <T> (@thick T.Type) -> ()

// CHECK: [[T0:%.*]] = load %objc_class*, %objc_class** @"OBJC_CLASS_REF_$_GenericClass",
// CHECK: [[OBJC_CLASS:%.*]] = call %objc_class* @swift_getInitializedObjCClass(%objc_class* [[T0]])
// CHECK: [[OBJC_CLASS:%.*]] = call %objc_class* @{{.*}}(%objc_class* [[T0]])
// CHECK: call swiftcc void @objc_class_sink(%objc_class* [[OBJC_CLASS]], %swift.type* [[METADATA]])
%c = metatype $@objc_metatype GenericClass<NSString>.Type
apply %y<GenericClass<NSString>>(%c) : $@convention(thin) <T: AnyObject> (@objc_metatype T.Type) -> ()
Expand Down
2 changes: 1 addition & 1 deletion test/IRGen/objc_properties_jit.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ extension NSString {

// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} private void @runtime_registration
// CHECK: [[NSOBJECT_UNINIT:%.*]] = load %objc_class*, %objc_class** @"OBJC_CLASS_REF_$_NSString"
// CHECK: [[NSOBJECT:%.*]] = call %objc_class* @swift_getInitializedObjCClass(%objc_class* [[NSOBJECT_UNINIT]])
// CHECK: [[NSOBJECT:%.*]] = call %objc_class* @{{.*}}(%objc_class* [[NSOBJECT_UNINIT]])
// CHECK: [[GET_CLASS_PROP:%.*]] = call i8* @sel_registerName({{.*}}(classProp)
// CHECK: call i8* @class_replaceMethod(%objc_class* @"OBJC_METACLASS_$_NSString", i8* [[GET_CLASS_PROP]]
// CHECK: [[SET_CLASS_PROP:%.*]] = call i8* @sel_registerName({{.*}}(setClassProp:)
Expand Down
10 changes: 5 additions & 5 deletions validation-test/Reflection/reflect_empty_struct.swift
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// RUN: %empty-directory(%t)
// RUN: %target-build-swift -lswiftSwiftReflectionTest -I %S/Inputs/EmptyStruct/ %s -o %t/reflect_empty_struct -target x86_64-apple-macosx10.99.0
// RUN: %target-build-swift -lswiftSwiftReflectionTest -I %S/Inputs/EmptyStruct/ %s -o %t/reflect_empty_struct
// RUN: %target-codesign %t/reflect_empty_struct

// RUN: %target-run %target-swift-reflection-test %t/reflect_empty_struct | %FileCheck %s --check-prefix=CHECK-%target-ptrsize --dump-input fail
Expand Down Expand Up @@ -32,13 +32,13 @@ reflect(object: obj)
// CHECK-64: Type info:
// CHECK-64: (class_instance size=80 alignment=8 stride=80 num_extra_inhabitants=0 bitwise_takable=1
// CHECK-64: (field name=a offset=16
// CHECK-64: (builtin size=0 alignment=1 stride=1 num_extra_inhabitants=0 bitwise_takable=1))
// CHECK-64: ({{builtin|struct}} size=0 alignment={{1|4}} stride=1 num_extra_inhabitants=0 bitwise_takable=1))
// CHECK-64: (field name=b offset=16
// CHECK-64: (opaque_existential size=32 alignment=8 stride=32 num_extra_inhabitants=2147483647 bitwise_takable=1
// CHECK-64: (field name=metadata offset=24
// CHECK-64: (builtin size=8 alignment=8 stride=8 num_extra_inhabitants=2147483647 bitwise_takable=1))))
// CHECK-64: (field name=c offset=48
// CHECK-64: (builtin size=0 alignment=4 stride=1 num_extra_inhabitants=0 bitwise_takable=1))
// CHECK-64: ({{builtin|struct}} size=0 alignment={{1|4}} stride=1 num_extra_inhabitants=0 bitwise_takable=1))
// CHECK-64: (field name=d offset=48
// CHECK-64: (opaque_existential size=32 alignment=8 stride=32 num_extra_inhabitants=2147483647 bitwise_takable=1
// CHECK-64: (field name=metadata offset=24
Expand All @@ -52,13 +52,13 @@ reflect(object: obj)
// CHECK-32: Type info:
// CHECK-32: (class_instance size=40 alignment=4 stride=40 num_extra_inhabitants=0 bitwise_takable=1
// CHECK-32: (field name=a offset=8
// CHECK-32: (builtin size=0 alignment=1 stride=1 num_extra_inhabitants=0 bitwise_takable=1))
// CHECK-32: ({{builtin|struct}} size=0 alignment={{1|4}} stride=1 num_extra_inhabitants=0 bitwise_takable=1))
// CHECK-32: (field name=b offset=8
// CHECK-32: (opaque_existential size=16 alignment=4 stride=16 num_extra_inhabitants=4096 bitwise_takable=1
// CHECK-32: (field name=metadata offset=12
// CHECK-32: (builtin size=4 alignment=4 stride=4 num_extra_inhabitants=4096 bitwise_takable=1))))
// CHECK-32: (field name=c offset=24
// CHECK-32: (builtin size=0 alignment=4 stride=1 num_extra_inhabitants=0 bitwise_takable=1))
// CHECK-32: ({{builtin|struct}} size=0 alignment={{1|4}} stride=1 num_extra_inhabitants=0 bitwise_takable=1))
// CHECK-32: (field name=d offset=24
// CHECK-32: (opaque_existential size=16 alignment=4 stride=16 num_extra_inhabitants=4096 bitwise_takable=1
// CHECK-32: (field name=metadata offset=12
Expand Down
6 changes: 1 addition & 5 deletions validation-test/Reflection/reflect_empty_struct_compat.swift
Original file line number Diff line number Diff line change
@@ -1,10 +1,6 @@
// RUN: %empty-directory(%t)

// RUN: %target-build-swift -lswiftSwiftReflectionTest -I %S/Inputs/EmptyStruct/ %s -o %t/reflect_empty_struct -target x86_64-apple-macosx10.15.0
// RUN: %target-codesign %t/reflect_empty_struct
// RUN: %target-run %target-swift-reflection-test %t/reflect_empty_struct | %FileCheck %s --check-prefix=CHECK-%target-ptrsize --dump-input fail

// RUN: %target-build-swift -lswiftSwiftReflectionTest -I %S/Inputs/EmptyStruct/ %s -o %t/reflect_empty_struct -target x86_64-apple-macosx10.14.0
// RUN: %target-build-swift -lswiftSwiftReflectionTest -I %S/Inputs/EmptyStruct/ %s -o %t/reflect_empty_struct
// RUN: %target-codesign %t/reflect_empty_struct
// RUN: %target-run %target-swift-reflection-test %t/reflect_empty_struct | %FileCheck %s --check-prefix=CHECK-%target-ptrsize --dump-input fail

Expand Down