From 2f4a995662a03e86bd9f6e4a22e923fe99d021bb Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Wed, 28 Nov 2018 15:54:34 -0500 Subject: [PATCH 1/6] IRGen: Emit field offsets before vtable entries We want to be able to define classes with a fixed storage layout, but a resilient (opaque) vtable. If the class is also generic, we still have to load field offsets from the metadata if they are dependent. So put the field offsets after the generic arguments and before the vtable. This is an ABI break for @_fixed_layout classes, which are defined by the stdlib. --- lib/IRGen/ClassMetadataVisitor.h | 6 +- test/IRGen/class_metadata.swift | 4 +- test/IRGen/class_resilience.swift | 10 +-- test/IRGen/class_resilience_objc.swift | 4 +- .../IRGen/completely_fragile_class_layout.sil | 2 +- test/IRGen/generic_classes.sil | 12 +-- test/IRGen/generic_types.swift | 2 +- ..._mode_class_with_unimportable_fields.swift | 8 +- test/IRGen/objc_class_export.swift | 82 +++++++++---------- test/IRGen/objc_super.swift | 2 +- test/IRGen/subclass.swift | 46 +++++------ test/Serialization/Recovery/typedefs.swift | 4 +- 12 files changed, 91 insertions(+), 91 deletions(-) diff --git a/lib/IRGen/ClassMetadataVisitor.h b/lib/IRGen/ClassMetadataVisitor.h index 466a41c18d675..2638e15f1281b 100644 --- a/lib/IRGen/ClassMetadataVisitor.h +++ b/lib/IRGen/ClassMetadataVisitor.h @@ -123,9 +123,6 @@ template class ClassMetadataVisitor if (IGM.isResilient(theClass, ResilienceExpansion::Maximal)) return; - // Add vtable entries. - asImpl().addVTableEntries(theClass); - // A class only really *needs* a field-offset vector in the // metadata if: // - it's in a generic context and @@ -145,6 +142,9 @@ template class ClassMetadataVisitor addFieldEntries(field); } asImpl().noteEndOfFieldOffsets(theClass); + + // Add vtable entries. + asImpl().addVTableEntries(theClass); } private: diff --git a/test/IRGen/class_metadata.swift b/test/IRGen/class_metadata.swift index d79b78ee1cfcb..66a99cd60799d 100644 --- a/test/IRGen/class_metadata.swift +++ b/test/IRGen/class_metadata.swift @@ -24,8 +24,8 @@ class A {} // Field count. // CHECK-SAME: i32 0, // Field offset vector offset. -// CHECK-32-SAME: i32 14, -// CHECK-64-SAME: i32 11, +// CHECK-32-SAME: i32 13, +// CHECK-64-SAME: i32 10, // V-table offset. // CHECK-32-SAME: i32 13, // CHECK-64-SAME: i32 10, diff --git a/test/IRGen/class_resilience.swift b/test/IRGen/class_resilience.swift index 51117c7da7c63..654bf4ff9898a 100644 --- a/test/IRGen/class_resilience.swift +++ b/test/IRGen/class_resilience.swift @@ -52,7 +52,7 @@ // -- num fields: // CHECK-SAME: i32 1, // -- field offset vector offset: -// CHECK-SAME: i32 3, +// CHECK-SAME: i32 0, // -- superclass: // CHECK-SAME: @"got.$s15resilient_class22ResilientOutsideParentCMn" // -- singleton metadata initialization cache: @@ -357,7 +357,7 @@ extension ResilientGenericOutsideParent { // CHECK: [[ADDR:%.*]] = getelementptr inbounds %T16class_resilience21ResilientGenericChildC, %T16class_resilience21ResilientGenericChildC* %0, i32 0, i32 0, i32 0 // CHECK-NEXT: [[ISA:%.*]] = load %swift.type*, %swift.type** [[ADDR]] // CHECK-NEXT: [[BASE:%.*]] = load [[INT]], [[INT]]* getelementptr inbounds ([[BOUNDS]], [[BOUNDS]]* @"$s16class_resilience21ResilientGenericChildCMo", i32 0, i32 0) -// CHECK-NEXT: [[METADATA_OFFSET:%.*]] = add [[INT]] [[BASE]], {{16|32}} +// CHECK-NEXT: [[METADATA_OFFSET:%.*]] = add [[INT]] [[BASE]], {{4|8}} // CHECK-NEXT: [[ISA_ADDR:%.*]] = bitcast %swift.type* [[ISA]] to i8* // CHECK-NEXT: [[FIELD_OFFSET_TMP:%.*]] = getelementptr inbounds i8, i8* [[ISA_ADDR]], [[INT]] [[METADATA_OFFSET]] // CHECK-NEXT: [[FIELD_OFFSET_ADDR:%.*]] = bitcast i8* [[FIELD_OFFSET_TMP]] to [[INT]]* @@ -401,7 +401,7 @@ extension ResilientGenericOutsideParent { // CHECK: entry: // CHECK-NEXT: [[FIELDS:%.*]] = alloca [3 x i8**] // CHECK-NEXT: [[METADATA_ADDR:%.*]] = bitcast %swift.type* %0 to [[INT]]* -// CHECK-NEXT: [[FIELDS_DEST:%.*]] = getelementptr inbounds [[INT]], [[INT]]* [[METADATA_ADDR]], [[INT]] {{11|14}} +// CHECK-NEXT: [[FIELDS_DEST:%.*]] = getelementptr inbounds [[INT]], [[INT]]* [[METADATA_ADDR]], [[INT]] {{10|13}} // CHECK-NEXT: [[FIELDS_ADDR:%.*]] = bitcast [3 x i8**]* [[FIELDS]] to i8* // CHECK-NEXT: call void @llvm.lifetime.start.p0i8(i64 {{12|24}}, i8* [[FIELDS_ADDR]]) // CHECK-NEXT: [[FIELDS_PTR:%.*]] = getelementptr inbounds [3 x i8**], [3 x i8**]* [[FIELDS]], i32 0, i32 0 @@ -449,7 +449,7 @@ extension ResilientGenericOutsideParent { // CHECK: entry: // CHECK-NEXT: [[FIELDS:%.*]] = alloca [2 x i8**] // CHECK-NEXT: [[METADATA_ADDR:%.*]] = bitcast %swift.type* %0 to [[INT]]* -// CHECK-NEXT: [[FIELDS_DEST:%.*]] = getelementptr inbounds [[INT]], [[INT]]* [[METADATA_ADDR]], [[INT]] {{11|14}} +// CHECK-NEXT: [[FIELDS_DEST:%.*]] = getelementptr inbounds [[INT]], [[INT]]* [[METADATA_ADDR]], [[INT]] {{10|13}} // CHECK-NEXT: [[FIELDS_ADDR:%.*]] = bitcast [2 x i8**]* [[FIELDS]] to i8* // CHECK-NEXT: call void @llvm.lifetime.start.p0i8(i64 {{8|16}}, i8* [[FIELDS_ADDR]]) // CHECK-NEXT: [[FIELDS_PTR:%.*]] = getelementptr inbounds [2 x i8**], [2 x i8**]* [[FIELDS]], i32 0, i32 0 @@ -516,7 +516,7 @@ extension ResilientGenericOutsideParent { // CHECK: [[ISA_ADDR:%.*]] = getelementptr inbounds %T16class_resilience14ResilientChildC, %T16class_resilience14ResilientChildC* %1, i32 0, i32 0, i32 0 // CHECK-NEXT: [[ISA:%.*]] = load %swift.type*, %swift.type** [[ISA_ADDR]] // CHECK-NEXT: [[BASE:%.*]] = load [[INT]], [[INT]]* getelementptr inbounds ([[BOUNDS]], [[BOUNDS]]* @"$s16class_resilience14ResilientChildCMo", i32 0, i32 0) -// CHECK-NEXT: [[METADATA_OFFSET:%.*]] = add [[INT]] [[BASE]], {{4|8}} +// CHECK-NEXT: [[METADATA_OFFSET:%.*]] = add [[INT]] [[BASE]], {{8|16}} // CHECK-NEXT: [[METADATA_BYTES:%.*]] = bitcast %swift.type* [[ISA]] to i8* // CHECK-NEXT: [[VTABLE_OFFSET_TMP:%.*]] = getelementptr inbounds i8, i8* [[METADATA_BYTES]], [[INT]] [[METADATA_OFFSET]] // CHECK-NEXT: [[VTABLE_OFFSET_ADDR:%.*]] = bitcast i8* [[VTABLE_OFFSET_TMP]] to void (i32, %T16class_resilience14ResilientChildC*)** diff --git a/test/IRGen/class_resilience_objc.swift b/test/IRGen/class_resilience_objc.swift index ad9e389c4aad4..236ecbc28e9fd 100644 --- a/test/IRGen/class_resilience_objc.swift +++ b/test/IRGen/class_resilience_objc.swift @@ -71,9 +71,9 @@ public class GenericObjCSubclass : NSCoder { // CHECK-NEXT: [[ISA_ADDR:%.*]] = bitcast %swift.type* [[ISA]] to [[INT]]* -// CHECK-32-NEXT: [[FIELD_OFFSET_ADDR:%.*]] = getelementptr inbounds [[INT]], [[INT]]* [[ISA_ADDR]], [[INT]] 16 +// CHECK-32-NEXT: [[FIELD_OFFSET_ADDR:%.*]] = getelementptr inbounds [[INT]], [[INT]]* [[ISA_ADDR]], [[INT]] 15 -// CHECK-64-NEXT: [[FIELD_OFFSET_ADDR:%.*]] = getelementptr inbounds [[INT]], [[INT]]* [[ISA_ADDR]], [[INT]] 13 +// CHECK-64-NEXT: [[FIELD_OFFSET_ADDR:%.*]] = getelementptr inbounds [[INT]], [[INT]]* [[ISA_ADDR]], [[INT]] 12 // CHECK-NEXT: [[FIELD_OFFSET:%.*]] = load [[INT]], [[INT]]* [[FIELD_OFFSET_ADDR:%.*]] // CHECK-NEXT: [[OBJECT:%.*]] = bitcast %T21class_resilience_objc19GenericObjCSubclassC* %0 to i8* diff --git a/test/IRGen/completely_fragile_class_layout.sil b/test/IRGen/completely_fragile_class_layout.sil index 2c71089e8d13c..732f3c7fe0fe4 100644 --- a/test/IRGen/completely_fragile_class_layout.sil +++ b/test/IRGen/completely_fragile_class_layout.sil @@ -216,7 +216,7 @@ bb0(%0 : @guaranteed $ClassWithResilientField): // CHECK: entry: // CHECK-NEXT: [[FIELDS:%.*]] = alloca [3 x i8**] // CHECK-NEXT: [[METADATA_ADDR:%.*]] = bitcast %swift.type* %0 to [[INT]]* -// CHECK-NEXT: [[FIELDS_DEST:%.*]] = getelementptr inbounds [[INT]], [[INT]]* [[METADATA_ADDR]], [[INT]] {{11|14}} +// CHECK-NEXT: [[FIELDS_DEST:%.*]] = getelementptr inbounds [[INT]], [[INT]]* [[METADATA_ADDR]], [[INT]] {{10|13}} // CHECK-NEXT: [[FIELDS_ADDR:%.*]] = bitcast [3 x i8**]* [[FIELDS]] to i8* // CHECK-NEXT: call void @llvm.lifetime.start.p0i8(i64 {{12|24}}, i8* [[FIELDS_ADDR]]) // CHECK-NEXT: [[FIELDS_PTR:%.*]] = getelementptr inbounds [3 x i8**], [3 x i8**]* [[FIELDS]], i32 0, i32 0 diff --git a/test/IRGen/generic_classes.sil b/test/IRGen/generic_classes.sil index a3873d0c8b258..47d2548a7dda8 100644 --- a/test/IRGen/generic_classes.sil +++ b/test/IRGen/generic_classes.sil @@ -28,7 +28,7 @@ import Swift // -- num fields // CHECK-SAME: i32 3, // -- field offset vector offset -// CHECK-SAME: i32 15, +// CHECK-SAME: i32 11, // -- template instantiation cache // CHECK-SAME: [16 x i8*]* @"$s15generic_classes11RootGenericCMI" // -- template instantiation pattern @@ -36,7 +36,7 @@ import Swift // -- generic parameters, requirements, key arguments, extra arguments // CHECK-SAME: i16 1, i16 0, i16 1, i16 0 // -- vtable offset -// CHECK-SAME: i32 11, +// CHECK-SAME: i32 14, // -- vtable size // CHECK-SAME: i32 4 // CHECK-SAME: } @@ -86,7 +86,7 @@ import Swift // -- num fields // CHECK-SAME: i32 3, // -- -- field offset vector offset -// CHECK-SAME: i32 11, +// CHECK-SAME: i32 10, // CHECK-SAME: }> // CHECK: @"$s15generic_classes14RootNonGenericCMf" = internal global <{ {{.*}} }> <{ @@ -274,7 +274,7 @@ entry(%c : @unowned $RootGeneric): // RootGeneric.y has dependent layout; load the offset from the metadata // CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @RootGeneric_concrete_fragile_dependent_member_access_y // CHECK: [[TYPE_METADATA_ARRAY:%.*]] = bitcast %swift.type* {{%.*}} to i64* -// CHECK: [[Y_OFFSET_ADDR:%.*]] = getelementptr inbounds i64, i64* [[TYPE_METADATA_ARRAY]], i64 16 +// CHECK: [[Y_OFFSET_ADDR:%.*]] = getelementptr inbounds i64, i64* [[TYPE_METADATA_ARRAY]], i64 12 // CHECK: [[Y_OFFSET:%.*]] = load i64, i64* [[Y_OFFSET_ADDR]], align 8 // CHECK: [[CLASS_BYTE_ARRAY:%.*]] = bitcast [[ROOTGENERIC]]* {{%.*}} to i8* // CHECK: [[Y_ADDR:%.*]] = getelementptr inbounds i8, i8* [[CLASS_BYTE_ARRAY]], i64 [[Y_OFFSET]] @@ -301,7 +301,7 @@ entry(%z : @trivial $*Int, %c : @unowned $RootGeneric): // RootGeneric.z has dependent layout; load the offset from the metadata // CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc i8 @RootGeneric_concrete_fragile_dependent_member_access_z // CHECK: [[TYPE_METADATA_ARRAY:%.*]] = bitcast %swift.type* {{%.*}} to i64* -// CHECK: [[Z_OFFSET_ADDR:%.*]] = getelementptr inbounds i64, i64* [[TYPE_METADATA_ARRAY]], i64 17 +// CHECK: [[Z_OFFSET_ADDR:%.*]] = getelementptr inbounds i64, i64* [[TYPE_METADATA_ARRAY]], i64 13 // CHECK: [[Z_OFFSET:%.*]] = load i64, i64* [[Z_OFFSET_ADDR]], align 8 // CHECK: [[CLASS_BYTE_ARRAY:%.*]] = bitcast [[ROOTGENERIC]]* {{%.*}} to i8* // CHECK: [[Z_ADDR:%.*]] = getelementptr inbounds i8, i8* [[CLASS_BYTE_ARRAY]], i64 [[Z_OFFSET]] @@ -371,7 +371,7 @@ entry(%c : $RootGeneric): // Initialize our own dependent field offsets. // CHECK: [[METADATA_ARRAY:%.*]] = bitcast %swift.type* [[METADATA]] to i64* -// CHECK: [[OFFSETS:%.*]] = getelementptr inbounds i64, i64* [[METADATA_ARRAY]], i64 23 +// CHECK: [[OFFSETS:%.*]] = getelementptr inbounds i64, i64* [[METADATA_ARRAY]], i64 20 // CHECK: [[FIELDS_ADDR:%.*]] = getelementptr inbounds [1 x i8**], [1 x i8**]* %classFields, i32 0, i32 0 // CHECK: [[T0:%.*]] = call{{( tail)?}} swiftcc %swift.metadata_response @swift_checkMetadataState(i64 319, %swift.type* %B) // CHECK: [[B_CHECKED:%.*]] = extractvalue %swift.metadata_response [[T0]], 0 diff --git a/test/IRGen/generic_types.swift b/test/IRGen/generic_types.swift index 31c1cfafc5754..dcc12da11a05a 100644 --- a/test/IRGen/generic_types.swift +++ b/test/IRGen/generic_types.swift @@ -26,7 +26,7 @@ // -- num fields // CHECK-SAME: i32 1, // -- field offset vector offset -// CHECK-SAME: i32 16, +// CHECK-SAME: i32 11, // -- instantiation cache // CHECK-SAME: @"$s13generic_types1ACMI" // -- instantiation pattern diff --git a/test/IRGen/mixed_mode_class_with_unimportable_fields.swift b/test/IRGen/mixed_mode_class_with_unimportable_fields.swift index c8f30fa2c8636..d61af8ee884e5 100644 --- a/test/IRGen/mixed_mode_class_with_unimportable_fields.swift +++ b/test/IRGen/mixed_mode_class_with_unimportable_fields.swift @@ -79,13 +79,13 @@ public func accessFinalFields(ofSub holder: SubButtHolder) -> (Any, Any, Any) { // CHECK-LABEL: define {{(protected )?}}{{(dllexport )?}}swiftcc void @"$s4main12invokeMethod2onyAA13SubButtHolderC_tF" public func invokeMethod(on holder: SubButtHolder) { - // CHECK-64: [[IMPL_ADDR:%.*]] = getelementptr inbounds {{.*}}, [[WORD]] 10 - // CHECK-32: [[IMPL_ADDR:%.*]] = getelementptr inbounds {{.*}}, [[WORD]] 13 + // CHECK-64: [[IMPL_ADDR:%.*]] = getelementptr inbounds {{.*}}, [[WORD]] 13 + // CHECK-32: [[IMPL_ADDR:%.*]] = getelementptr inbounds {{.*}}, [[WORD]] 16 // CHECK: [[IMPL:%.*]] = load {{.*}} [[IMPL_ADDR]] // CHECK: call swiftcc void [[IMPL]] holder.virtual() - // CHECK-64: [[IMPL_ADDR:%.*]] = getelementptr inbounds {{.*}}, [[WORD]] 15 - // CHECK-32: [[IMPL_ADDR:%.*]] = getelementptr inbounds {{.*}}, [[WORD]] 18 + // CHECK-64: [[IMPL_ADDR:%.*]] = getelementptr inbounds {{.*}}, [[WORD]] 16 + // CHECK-32: [[IMPL_ADDR:%.*]] = getelementptr inbounds {{.*}}, [[WORD]] 19 // CHECK: [[IMPL:%.*]] = load {{.*}} [[IMPL_ADDR]] // CHECK: call swiftcc void [[IMPL]] holder.subVirtual() diff --git a/test/IRGen/objc_class_export.swift b/test/IRGen/objc_class_export.swift index b9e9bb3b3d0d2..86e7c2af4307d 100644 --- a/test/IRGen/objc_class_export.swift +++ b/test/IRGen/objc_class_export.swift @@ -16,50 +16,50 @@ // CHECK-DAG: [[OBJC:%objc_object]] = type opaque // CHECK: @"OBJC_METACLASS_$__TtC17objc_class_export3Foo" = hidden global %objc_class { -// CHECK: %objc_class* @"OBJC_METACLASS_$_{{(_TtCs12_)?}}SwiftObject", -// CHECK: %objc_class* @"OBJC_METACLASS_$_{{(_TtCs12_)?}}SwiftObject", -// CHECK: %swift.opaque* @_objc_empty_cache, -// CHECK: %swift.opaque* null, -// CHECK: i64 ptrtoint ({{.*}}* @_METACLASS_DATA__TtC17objc_class_export3Foo to i64) -// CHECK: } +// CHECK-SAME: %objc_class* @"OBJC_METACLASS_$_{{(_TtCs12_)?}}SwiftObject", +// CHECK-SAME: %objc_class* @"OBJC_METACLASS_$_{{(_TtCs12_)?}}SwiftObject", +// CHECK-SAME: %swift.opaque* @_objc_empty_cache, +// CHECK-SAME: %swift.opaque* null, +// CHECK-SAME: i64 ptrtoint ({{.*}}* @_METACLASS_DATA__TtC17objc_class_export3Foo to i64) +// CHECK-SAME: } // CHECK: [[FOO_NAME:@.*]] = private unnamed_addr constant [28 x i8] c"_TtC17objc_class_export3Foo\00" // CHECK: @_METACLASS_DATA__TtC17objc_class_export3Foo = private constant {{.*\*}} } { -// CHECK: i32 129, -// CHECK: i32 40, -// CHECK: i32 40, -// CHECK: i32 0, -// CHECK: i8* null, -// CHECK: i8* getelementptr inbounds ([{{[0-9]+}} x i8], [{{[0-9]+}} x i8]* [[FOO_NAME]], i64 0, i64 0), -// CHECK: @_CLASS_METHODS__TtC17objc_class_export3Foo, -// CHECK: i8* null, -// CHECK: i8* null, -// CHECK: i8* null, -// CHECK: i8* null -// CHECK: }, section "__DATA, __objc_const", align 8 +// CHECK-SAME: i32 129, +// CHECK-SAME: i32 40, +// CHECK-SAME: i32 40, +// CHECK-SAME: i32 0, +// CHECK-SAME: i8* null, +// CHECK-SAME: i8* getelementptr inbounds ([{{[0-9]+}} x i8], [{{[0-9]+}} x i8]* [[FOO_NAME]], i64 0, i64 0), +// CHECK-SAME: @_CLASS_METHODS__TtC17objc_class_export3Foo, +// CHECK-SAME: i8* null, +// CHECK-SAME: i8* null, +// CHECK-SAME: i8* null, +// CHECK-SAME: i8* null +// CHECK-SAME: }, section "__DATA, __objc_const", align 8 // CHECK: @_DATA__TtC17objc_class_export3Foo = private constant {{.*\*}} } { -// CHECK: i32 128, -// CHECK: i32 16, -// CHECK: i32 24, -// CHECK: i32 0, -// CHECK: i8* null, -// CHECK: i8* getelementptr inbounds ([{{[0-9]+}} x i8], [{{[0-9]+}} x i8]* [[FOO_NAME]], i64 0, i64 0), -// CHECK: { i32, i32, [6 x { i8*, i8*, i8* }] }* @_INSTANCE_METHODS__TtC17objc_class_export3Foo, -// CHECK: i8* null, -// CHECK: @_IVARS__TtC17objc_class_export3Foo, -// CHECK: i8* null, -// CHECK: _PROPERTIES__TtC17objc_class_export3Foo -// CHECK: }, section "__DATA, __objc_const", align 8 -// CHECK: @"$s17objc_class_export3FooCMf" = internal global <{{.*i64}} }> <{ -// CHECK: void ([[FOO]]*)* @"$s17objc_class_export3FooCfD", -// CHECK: i8** @"$sBOWV", -// CHECK: i64 ptrtoint (%objc_class* @"OBJC_METACLASS_$__TtC17objc_class_export3Foo" to i64), -// CHECK: %objc_class* @"OBJC_CLASS_$_{{(_TtCs12_)?}}SwiftObject", -// CHECK: %swift.opaque* @_objc_empty_cache, -// CHECK: %swift.opaque* null, -// CHECK: i64 add (i64 ptrtoint ({{.*}}* @_DATA__TtC17objc_class_export3Foo to i64), i64 1), -// CHECK: [[FOO]]* (%swift.type*)* @"$s17objc_class_export3FooC6createACyFZ", -// CHECK: void (double, double, double, double, [[FOO]]*)* @"$s17objc_class_export3FooC10drawInRect5dirtyySo6NSRectV_tF" -// CHECK: }>, section "__DATA,__objc_data, regular" +// CHECK-SAME: i32 128, +// CHECK-SAME: i32 16, +// CHECK-SAME: i32 24, +// CHECK-SAME: i32 0, +// CHECK-SAME: i8* null, +// CHECK-SAME: i8* getelementptr inbounds ([{{[0-9]+}} x i8], [{{[0-9]+}} x i8]* [[FOO_NAME]], i64 0, i64 0), +// CHECK-SAME: { i32, i32, [6 x { i8*, i8*, i8* }] }* @_INSTANCE_METHODS__TtC17objc_class_export3Foo, +// CHECK-SAME: i8* null, +// CHECK-SAME: @_IVARS__TtC17objc_class_export3Foo, +// CHECK-SAME: i8* null, +// CHECK-SAME: _PROPERTIES__TtC17objc_class_export3Foo +// CHECK-SAME: }, section "__DATA, __objc_const", align 8 +// CHECK: @"$s17objc_class_export3FooCMf" = internal global <{{.*}} }> <{ +// CHECK-SAME: void ([[FOO]]*)* @"$s17objc_class_export3FooCfD", +// CHECK-SAME: i8** @"$sBOWV", +// CHECK-SAME: i64 ptrtoint (%objc_class* @"OBJC_METACLASS_$__TtC17objc_class_export3Foo" to i64), +// CHECK-SAME: %objc_class* @"OBJC_CLASS_$_{{(_TtCs12_)?}}SwiftObject", +// CHECK-SAME: %swift.opaque* @_objc_empty_cache, +// CHECK-SAME: %swift.opaque* null, +// CHECK-SAME: i64 add (i64 ptrtoint ({{.*}}* @_DATA__TtC17objc_class_export3Foo to i64), i64 1), +// CHECK-SAME: [[FOO]]* (%swift.type*)* @"$s17objc_class_export3FooC6createACyFZ", +// CHECK-SAME: void (double, double, double, double, [[FOO]]*)* @"$s17objc_class_export3FooC10drawInRect5dirtyySo6NSRectV_tF" +// CHECK-SAME: }>, section "__DATA,__objc_data, regular" // -- TODO: The OBJC_CLASS symbol should reflect the qualified runtime name of // Foo. // CHECK: @"$s17objc_class_export3FooCN" = hidden alias %swift.type, bitcast (i64* getelementptr inbounds ({{.*}} @"$s17objc_class_export3FooCMf", i32 0, i32 2) to %swift.type*) diff --git a/test/IRGen/objc_super.swift b/test/IRGen/objc_super.swift index 6d69d153931dc..282a52d5294a9 100644 --- a/test/IRGen/objc_super.swift +++ b/test/IRGen/objc_super.swift @@ -97,7 +97,7 @@ class GenericRuncer : Gizmo { // CHECK: [[CLASS:%.*]] = and i64 [[ISA]], [[ISAMASK]] // CHECK: [[TY:%.*]] = inttoptr i64 [[CLASS]] to %swift.type* // CHECK: [[CAST:%.*]] = bitcast %swift.type* [[TY]] to i64* -// CHECK: [[OFFSETADDR:%.*]] = getelementptr inbounds i64, i64* [[CAST]], i64 17 +// CHECK: [[OFFSETADDR:%.*]] = getelementptr inbounds i64, i64* [[CAST]], i64 11 // CHECK: [[FIELDOFFSET:%.*]] = load i64, i64* [[OFFSETADDR]] // CHECK: [[BYTEADDR:%.*]] = bitcast %T10objc_super13GenericRuncerC* %0 to i8* // CHECK: [[FIELDADDR:%.*]] = getelementptr inbounds i8, i8* [[BYTEADDR]], i64 [[FIELDOFFSET]] diff --git a/test/IRGen/subclass.swift b/test/IRGen/subclass.swift index 7616d7b6466ad..bfeb3a3f66995 100644 --- a/test/IRGen/subclass.swift +++ b/test/IRGen/subclass.swift @@ -10,30 +10,30 @@ // CHECK-DAG: [[INT:%TSi]] = type <{ i64 }> // CHECK-DAG: [[B:%T8subclass1BC]] = type <{ [[REF]], [[INT]], [[INT]], [[INT]] }> -// CHECK: @_DATA__TtC8subclass1A = private constant {{.*\* } }}{ -// CHECK: @"$s8subclass1ACMf" = internal global [[A_METADATA:<{.*i64 }>]] <{ -// CHECK: void ([[A]]*)* @"$s8subclass1ACfD", -// CHECK: i8** @"$sBoWV", -// CHECK: i64 ptrtoint ([[OBJC_CLASS]]* @"$s8subclass1ACMm" to i64), -// CHECK: [[OBJC_CLASS]]* @"OBJC_CLASS_$_{{(_TtCs12_)?}}SwiftObject", -// CHECK: [[OPAQUE]]* @_objc_empty_cache, -// CHECK: [[OPAQUE]]* null, -// CHECK: i64 add (i64 ptrtoint ({ {{.*}} }* @_DATA__TtC8subclass1A to i64), i64 1), -// CHECK: i64 ([[A]]*)* @"$s8subclass1AC1fSiyF", -// CHECK: [[A]]* ([[TYPE]]*)* @"$s8subclass1AC1gACyFZ" -// CHECK: }> -// CHECK: @_DATA__TtC8subclass1B = private constant {{.*\* } }}{ +// CHECK: @_DATA__TtC8subclass1A = private constant {{.* } }}{ +// CHECK: @"$s8subclass1ACMf" = internal global [[A_METADATA:<{.* }>]] <{ +// CHECK-SAME: void ([[A]]*)* @"$s8subclass1ACfD", +// CHECK-SAME: i8** @"$sBoWV", +// CHECK-SAME: i64 ptrtoint ([[OBJC_CLASS]]* @"$s8subclass1ACMm" to i64), +// CHECK-SAME: [[OBJC_CLASS]]* @"OBJC_CLASS_$_{{(_TtCs12_)?}}SwiftObject", +// CHECK-SAME: [[OPAQUE]]* @_objc_empty_cache, +// CHECK-SAME: [[OPAQUE]]* null, +// CHECK-SAME: i64 add (i64 ptrtoint ({ {{.*}} }* @_DATA__TtC8subclass1A to i64), i64 1), +// CHECK-SAME: i64 ([[A]]*)* @"$s8subclass1AC1fSiyF", +// CHECK-SAME: [[A]]* ([[TYPE]]*)* @"$s8subclass1AC1gACyFZ" +// CHECK-SAME: }> +// CHECK: @_DATA__TtC8subclass1B = private constant {{.* } }}{ // CHECK: @"$s8subclass1BCMf" = internal global <{ {{.*}} }> <{ -// CHECK: void ([[B]]*)* @"$s8subclass1BCfD", -// CHECK: i8** @"$sBoWV", -// CHECK: i64 ptrtoint ([[OBJC_CLASS]]* @"$s8subclass1BCMm" to i64), -// CHECK: [[TYPE]]* {{.*}} @"$s8subclass1ACMf", -// CHECK: [[OPAQUE]]* @_objc_empty_cache, -// CHECK: [[OPAQUE]]* null, -// CHECK: i64 add (i64 ptrtoint ({ {{.*}} }* @_DATA__TtC8subclass1B to i64), i64 1), -// CHECK: i64 ([[B]]*)* @"$s8subclass1BC1fSiyF", -// CHECK: [[A]]* ([[TYPE]]*)* @"$s8subclass1AC1gACyFZ" -// CHECK: }> +// CHECK-SAME: void ([[B]]*)* @"$s8subclass1BCfD", +// CHECK-SAME: i8** @"$sBoWV", +// CHECK-SAME: i64 ptrtoint ([[OBJC_CLASS]]* @"$s8subclass1BCMm" to i64), +// CHECK-SAME: [[TYPE]]* {{.*}} @"$s8subclass1ACMf", +// CHECK-SAME: [[OPAQUE]]* @_objc_empty_cache, +// CHECK-SAME: [[OPAQUE]]* null, +// CHECK-SAME: i64 add (i64 ptrtoint ({ {{.*}} }* @_DATA__TtC8subclass1B to i64), i64 1), +// CHECK-SAME: i64 ([[B]]*)* @"$s8subclass1BC1fSiyF", +// CHECK-SAME: [[A]]* ([[TYPE]]*)* @"$s8subclass1AC1gACyFZ" +// CHECK-SAME: }> // CHECK: @objc_classes = internal global [2 x i8*] [i8* {{.*}} @"$s8subclass1ACN" {{.*}}, i8* {{.*}} @"$s8subclass1BCN" {{.*}}] class A { diff --git a/test/Serialization/Recovery/typedefs.swift b/test/Serialization/Recovery/typedefs.swift index 22dde7309330b..5601bec8729eb 100644 --- a/test/Serialization/Recovery/typedefs.swift +++ b/test/Serialization/Recovery/typedefs.swift @@ -31,11 +31,11 @@ func testSymbols() { // CHECK-IR-LABEL: define{{.*}} void @"$s8typedefs18testVTableBuilding4usery3Lib4UserC_tF public func testVTableBuilding(user: User) { - // The important thing in this CHECK line is the "i64 30", which is the offset + // The important thing in this CHECK line is the "i64 28", which is the offset // for the vtable slot for 'lastMethod()'. If the layout here // changes, please check that offset is still correct. // CHECK-IR-NOT: ret - // CHECK-IR: getelementptr inbounds void (%T3Lib4UserC*)*, void (%T3Lib4UserC*)** %{{[0-9]+}}, {{i64 26|i32 29}} + // CHECK-IR: getelementptr inbounds void (%T3Lib4UserC*)*, void (%T3Lib4UserC*)** %{{[0-9]+}}, {{i64 28|i32 31}} _ = user.lastMethod() } // CHECK-IR: ret void From 730a60f3169c809e45140a13f774f18de0c66562 Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Wed, 28 Nov 2018 15:04:05 -0500 Subject: [PATCH 2/6] AST: Introduce ClassDecl::hasResilientMetadata() --- include/swift/AST/Decl.h | 11 +++++++++++ lib/AST/Decl.cpp | 30 ++++++++++++++++++++++++++++++ lib/IRGen/GenDecl.cpp | 14 ++++++++++++++ lib/IRGen/IRGenModule.h | 1 + 4 files changed, 56 insertions(+) diff --git a/include/swift/AST/Decl.h b/include/swift/AST/Decl.h index d43cf0af49a77..531839544fa11 100644 --- a/include/swift/AST/Decl.h +++ b/include/swift/AST/Decl.h @@ -3584,6 +3584,17 @@ class ClassDecl final : public NominalTypeDecl { return SourceRange(ClassLoc, getBraces().End); } + /// Determine whether the member area of this class's metadata (which consists + /// of field offsets and vtable entries) is to be considered opaque by clients. + /// + /// Note that even @_fixed_layout classes have resilient metadata if they are + /// in a resilient module. + bool hasResilientMetadata() const; + + /// Determine whether this class has resilient metadata when accessed from the + /// given module and resilience expansion. + bool hasResilientMetadata(ModuleDecl *M, ResilienceExpansion expansion) const; + /// Determine whether this class has a superclass. bool hasSuperclass() const { return (bool)getSuperclassDecl(); } diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index 9f550035acf4a..e69d4dce0c1ac 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -3314,6 +3314,36 @@ ClassDecl::ClassDecl(SourceLoc ClassLoc, Identifier Name, SourceLoc NameLoc, Bits.ClassDecl.HasMissingVTableEntries = 0; } +bool ClassDecl::hasResilientMetadata() const { + // Imported classes don't have a vtable, etc, at all. + if (hasClangNode()) + return false; + + // If the module is not resilient, neither is the class metadata. + if (getParentModule()->getResilienceStrategy() + != ResilienceStrategy::Resilient) + return false; + + // If the class is not public, we can't use it outside the module at all. + if (!getFormalAccessScope(/*useDC=*/nullptr, + /*treatUsableFromInlineAsPublic=*/true).isPublic()) + return false; + + // Otherwise we access metadata members, such as vtable entries, resiliently. + return true; +} + +bool ClassDecl::hasResilientMetadata(ModuleDecl *M, + ResilienceExpansion expansion) const { + switch (expansion) { + case ResilienceExpansion::Minimal: + return hasResilientMetadata(); + case ResilienceExpansion::Maximal: + return M != getModuleContext() && hasResilientMetadata(); + } + llvm_unreachable("bad resilience expansion"); +} + DestructorDecl *ClassDecl::getDestructor() { auto results = lookupDirect(DeclBaseName::createDestructor()); assert(!results.empty() && "Class without destructor?"); diff --git a/lib/IRGen/GenDecl.cpp b/lib/IRGen/GenDecl.cpp index 95a7a69a92c2e..b22959b21895d 100644 --- a/lib/IRGen/GenDecl.cpp +++ b/lib/IRGen/GenDecl.cpp @@ -3859,6 +3859,20 @@ bool IRGenModule::isResilient(NominalTypeDecl *D, ResilienceExpansion expansion) return D->isResilient(getSwiftModule(), expansion); } +/// Do we have to use resilient access patterns when working with this +/// class? +/// +/// For classes, this means that virtual method calls use dispatch thunks +/// rather than accessing metadata members directly. +bool IRGenModule::hasResilientMetadata(ClassDecl *D, + ResilienceExpansion expansion) { + if (expansion == ResilienceExpansion::Maximal && + Types.getLoweringMode() == TypeConverter::Mode::CompletelyFragile) { + return false; + } + return D->hasResilientMetadata(getSwiftModule(), expansion); +} + // The most general resilience expansion where the given declaration is visible. ResilienceExpansion IRGenModule::getResilienceExpansionForAccess(NominalTypeDecl *decl) { diff --git a/lib/IRGen/IRGenModule.h b/lib/IRGen/IRGenModule.h index 846cd2c3f51ce..223e9834dc31b 100644 --- a/lib/IRGen/IRGenModule.h +++ b/lib/IRGen/IRGenModule.h @@ -784,6 +784,7 @@ class IRGenModule { clang::CodeGen::CodeGenModule &getClangCGM() const; bool isResilient(NominalTypeDecl *decl, ResilienceExpansion expansion); + bool hasResilientMetadata(ClassDecl *decl, ResilienceExpansion expansion); ResilienceExpansion getResilienceExpansionForAccess(NominalTypeDecl *decl); ResilienceExpansion getResilienceExpansionForLayout(NominalTypeDecl *decl); ResilienceExpansion getResilienceExpansionForLayout(SILGlobalVariable *var); From 9551998cd4c8d86dcfba267ec9d491b023f30ec3 Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Wed, 28 Nov 2018 19:21:05 -0500 Subject: [PATCH 3/6] IRGen: Refactor ClassLayoutBuilder a bit to help distinguish resilient-metadata from resilient-storage We want @_fixed_layout classes to have non-resilient storage, but still have resilient metadata. --- lib/IRGen/ClassMetadataVisitor.h | 4 ++-- lib/IRGen/GenClass.cpp | 11 +++++------ lib/IRGen/Linking.cpp | 2 +- 3 files changed, 8 insertions(+), 9 deletions(-) diff --git a/lib/IRGen/ClassMetadataVisitor.h b/lib/IRGen/ClassMetadataVisitor.h index 2638e15f1281b..d7d9364682d1d 100644 --- a/lib/IRGen/ClassMetadataVisitor.h +++ b/lib/IRGen/ClassMetadataVisitor.h @@ -118,8 +118,8 @@ template class ClassMetadataVisitor // This must always be the first item in the immediate members. asImpl().addGenericFields(theClass, theClass); - // If the class is resilient, we cannot make any assumptions about its - // member layout at all, so skip the rest of this method. + // If the class has resilient storage, we cannot make any assumptions about + // its storage layout, so skip the rest of this method. if (IGM.isResilient(theClass, ResilienceExpansion::Maximal)) return; diff --git a/lib/IRGen/GenClass.cpp b/lib/IRGen/GenClass.cpp index 03478ab57c473..bba1dc79caf29 100644 --- a/lib/IRGen/GenClass.cpp +++ b/lib/IRGen/GenClass.cpp @@ -200,8 +200,8 @@ namespace { bool ClassHasGenericLayout = false; // Is this class or any of its superclasses resilient from the viewpoint - // of the current module? This means that their metadata can change size - // and field offsets, generic arguments and virtual methods must be + // of the current module? This means that their metadata can change size, + // hence field offsets, generic arguments and virtual methods must be // accessed relative to a metadata base global variable. bool ClassHasResilientAncestry = false; @@ -261,14 +261,11 @@ namespace { return Elements; } - /// Does the class metadata have a completely known, static layout that - /// does not require initialization at runtime beyond registeration of - /// the class with the Objective-C runtime? + /// Do instances of the class have a completely known, static layout? bool isFixedSize() const { return !(ClassHasMissingMembers || ClassHasResilientMembers || ClassHasGenericLayout || - ClassHasResilientAncestry || ClassHasObjCAncestry); } @@ -336,6 +333,7 @@ namespace { // If the class is resilient, don't walk over its fields; we have to // calculate the layout at runtime. ClassHasResilientAncestry = true; + ClassHasResilientMembers = true; // Furthermore, if the superclass is generic, we have to assume // that its layout depends on its generic parameters. But this only @@ -358,6 +356,7 @@ namespace { if (IGM.isResilient(theClass, ResilienceExpansion::Maximal)) { ClassHasResilientAncestry = true; + ClassHasResilientMembers = true; return; } diff --git a/lib/IRGen/Linking.cpp b/lib/IRGen/Linking.cpp index 92703c01e06e1..ba7937b450f14 100644 --- a/lib/IRGen/Linking.cpp +++ b/lib/IRGen/Linking.cpp @@ -484,7 +484,7 @@ SILLinkage LinkEntity::getLinkage(ForDefinition_t forDefinition) const { auto linkage = getDeclLinkage(varDecl); - // Resilient classes don't expose field offset symbols. + // Classes with resilient storage don't expose field offset symbols. if (cast(varDecl->getDeclContext())->isResilient()) { assert(linkage != FormalLinkage::PublicNonUnique && "Cannot have a resilient class with non-unique linkage"); From 906fe5045acab7df3cf8e42010c421e4755219c5 Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Wed, 28 Nov 2018 20:03:42 -0500 Subject: [PATCH 4/6] IRGen: @_fixed_layout classes still have resilient metadata --- lib/IRGen/ClassMetadataVisitor.h | 7 +- lib/IRGen/GenClass.cpp | 21 ++-- lib/IRGen/GenMeta.cpp | 6 +- lib/IRGen/IRGenSIL.cpp | 10 +- lib/IRGen/MetadataLayout.cpp | 2 +- lib/SIL/SILDeclRef.cpp | 3 + lib/TBDGen/TBDGen.cpp | 6 +- test/IRGen/class_resilience.swift | 23 ----- test/IRGen/fixed_layout_class.swift | 126 +++++++++++++++++++++++ test/IRGen/super.sil | 22 ++-- test/Inputs/fixed_layout_class.swift | 129 ++++++++++++++++++++++++ test/Inputs/resilient_class.swift | 118 ---------------------- test/Interpreter/class_resilience.swift | 16 ++- test/SILGen/partial_apply_super.swift | 12 ++- test/SILGen/super.swift | 12 ++- 15 files changed, 334 insertions(+), 179 deletions(-) create mode 100644 test/IRGen/fixed_layout_class.swift create mode 100644 test/Inputs/fixed_layout_class.swift diff --git a/lib/IRGen/ClassMetadataVisitor.h b/lib/IRGen/ClassMetadataVisitor.h index d7d9364682d1d..65492f9625ec1 100644 --- a/lib/IRGen/ClassMetadataVisitor.h +++ b/lib/IRGen/ClassMetadataVisitor.h @@ -95,7 +95,7 @@ template class ClassMetadataVisitor if (superclassDecl->hasClangNode()) { // Nothing to do; Objective-C classes do not add new members to // Swift class metadata. - } else if (IGM.isResilient(superclassDecl, ResilienceExpansion::Maximal)) { + } else if (IGM.hasResilientMetadata(superclassDecl, ResilienceExpansion::Maximal)) { // Runtime metadata instantiation will initialize our field offset // vector and vtable entries. // @@ -143,6 +143,11 @@ template class ClassMetadataVisitor } asImpl().noteEndOfFieldOffsets(theClass); + // If the class has resilient metadata, we cannot make any assumptions + // about its metadata layout, so skip the rest of this method. + if (IGM.hasResilientMetadata(theClass, ResilienceExpansion::Maximal)) + return; + // Add vtable entries. asImpl().addVTableEntries(theClass); } diff --git a/lib/IRGen/GenClass.cpp b/lib/IRGen/GenClass.cpp index bba1dc79caf29..fbd614de46f81 100644 --- a/lib/IRGen/GenClass.cpp +++ b/lib/IRGen/GenClass.cpp @@ -329,20 +329,21 @@ namespace { auto superclassDecl = superclassType.getClassOrBoundGenericClass(); assert(superclassType && superclassDecl); - if (IGM.isResilient(superclassDecl, ResilienceExpansion::Maximal)) { - // If the class is resilient, don't walk over its fields; we have to - // calculate the layout at runtime. + if (IGM.hasResilientMetadata(superclassDecl, ResilienceExpansion::Maximal)) ClassHasResilientAncestry = true; + + // If the superclass has resilient storage, don't walk its fields. + if (IGM.isResilient(superclassDecl, ResilienceExpansion::Maximal)) { ClassHasResilientMembers = true; - // Furthermore, if the superclass is generic, we have to assume - // that its layout depends on its generic parameters. But this only - // propagates down to subclasses whose superclass type depends on the - // subclass's generic context. + // If the superclass is generic, we have to assume that its layout + // depends on its generic parameters. But this only propagates down to + // subclasses whose superclass type depends on the subclass's generic + // context. if (superclassType.hasArchetype()) ClassHasGenericLayout = true; } else { - // Otherwise, we have total knowledge of the class and its + // Otherwise, we are allowed to have total knowledge of the superclass // fields, so walk them to compute the layout. addFieldsForClass(superclassDecl, superclassType, /*superclass=*/true); } @@ -354,8 +355,10 @@ namespace { if (classHasIncompleteLayout(IGM, theClass)) ClassHasMissingMembers = true; - if (IGM.isResilient(theClass, ResilienceExpansion::Maximal)) { + if (IGM.hasResilientMetadata(theClass, ResilienceExpansion::Maximal)) ClassHasResilientAncestry = true; + + if (IGM.isResilient(theClass, ResilienceExpansion::Maximal)) { ClassHasResilientMembers = true; return; } diff --git a/lib/IRGen/GenMeta.cpp b/lib/IRGen/GenMeta.cpp index 7c35ced9498d2..313b0f14869ad 100644 --- a/lib/IRGen/GenMeta.cpp +++ b/lib/IRGen/GenMeta.cpp @@ -1381,7 +1381,7 @@ namespace { RequireMetadata_t requireMetadata) : super(IGM, Type, requireMetadata), VTable(IGM.getSILModule().lookUpVTable(getType())), - Resilient(IGM.isResilient(Type, ResilienceExpansion::Minimal)) { + Resilient(IGM.hasResilientMetadata(Type, ResilienceExpansion::Minimal)) { if (getType()->isForeign()) return; @@ -1485,7 +1485,7 @@ namespace { // Only emit a method lookup function if the class is resilient // and has a non-empty vtable. - if (IGM.isResilient(getType(), ResilienceExpansion::Minimal)) + if (IGM.hasResilientMetadata(getType(), ResilienceExpansion::Minimal)) IGM.emitMethodLookupFunction(getType()); auto offset = MetadataLayout->hasResilientSuperclass() @@ -2250,7 +2250,7 @@ static void emitClassMetadataBaseOffset(IRGenModule &IGM, // Only classes defined in resilient modules, or those that have // a resilient superclass need this. if (!layout.hasResilientSuperclass() && - !IGM.isResilient(classDecl, ResilienceExpansion::Minimal)) { + !IGM.hasResilientMetadata(classDecl, ResilienceExpansion::Minimal)) { return; } diff --git a/lib/IRGen/IRGenSIL.cpp b/lib/IRGen/IRGenSIL.cpp index 1c006bebfbf66..0b25690420b2f 100644 --- a/lib/IRGen/IRGenSIL.cpp +++ b/lib/IRGen/IRGenSIL.cpp @@ -5465,12 +5465,12 @@ void IRGenSILFunction::visitSuperMethodInst(swift::SuperMethodInst *i) { // its offset since methods can be re-ordered resiliently. Instead, we call // the class method lookup function, passing in a reference to the // method descriptor. - if (IGM.isResilient(classDecl, ResilienceExpansion::Maximal)) { + if (IGM.hasResilientMetadata(classDecl, ResilienceExpansion::Maximal)) { // Load the superclass of the static type of the 'self' value. llvm::Value *superMetadata; auto instanceTy = CanType(baseType.getASTType()->getMetatypeInstanceType()); - if (!IGM.isResilient(instanceTy.getClassOrBoundGenericClass(), - ResilienceExpansion::Maximal)) { + if (!IGM.hasResilientMetadata(instanceTy.getClassOrBoundGenericClass(), + ResilienceExpansion::Maximal)) { // It's still possible that the static type of 'self' is not resilient, in // which case we can assume its superclass. // @@ -5549,8 +5549,8 @@ void IRGenSILFunction::visitClassMethodInst(swift::ClassMethodInst *i) { auto methodType = i->getType().castTo(); auto *classDecl = cast(method.getDecl()->getDeclContext()); - if (IGM.isResilient(classDecl, - ResilienceExpansion::Maximal)) { + if (IGM.hasResilientMetadata(classDecl, + ResilienceExpansion::Maximal)) { auto *fnPtr = IGM.getAddrOfDispatchThunk(method, NotForDefinition); auto sig = IGM.getSignature(methodType); FunctionPointer fn(fnPtr, sig); diff --git a/lib/IRGen/MetadataLayout.cpp b/lib/IRGen/MetadataLayout.cpp index 2602524c228fc..1f8ec2a1adbeb 100644 --- a/lib/IRGen/MetadataLayout.cpp +++ b/lib/IRGen/MetadataLayout.cpp @@ -280,7 +280,7 @@ ClassMetadataLayout::ClassMetadataLayout(IRGenModule &IGM, ClassDecl *decl) Layout.StartOfImmediateMembers = getNextOffset(); if (Layout.HasResilientSuperclass || - IGM.isResilient(forClass, ResilienceExpansion::Maximal)) { + IGM.hasResilientMetadata(forClass, ResilienceExpansion::Maximal)) { assert(!DynamicOffsetBase); DynamicOffsetBase = NextOffset; } diff --git a/lib/SIL/SILDeclRef.cpp b/lib/SIL/SILDeclRef.cpp index 85d0e84b05af5..5538dba43435c 100644 --- a/lib/SIL/SILDeclRef.cpp +++ b/lib/SIL/SILDeclRef.cpp @@ -928,6 +928,9 @@ SubclassScope SILDeclRef::getSubclassScope() const { assert(FD->getEffectiveAccess() <= classType->getEffectiveAccess() && "class must be as visible as its members"); + // FIXME: This is too narrow. Any class with resilient metadata should + // probably have this, at least for method overrides that don't add new + // vtable entries. if (classType->isResilient()) return SubclassScope::Resilient; diff --git a/lib/TBDGen/TBDGen.cpp b/lib/TBDGen/TBDGen.cpp index af70958ea7076..4cf08192328b4 100644 --- a/lib/TBDGen/TBDGen.cpp +++ b/lib/TBDGen/TBDGen.cpp @@ -314,11 +314,11 @@ void TBDGenVisitor::visitClassDecl(ClassDecl *CD) { visitNominalTypeDecl(CD); auto hasResilientAncestor = - CD->isResilient(SwiftModule, ResilienceExpansion::Minimal); + CD->hasResilientMetadata(SwiftModule, ResilienceExpansion::Minimal); auto ancestor = CD->getSuperclassDecl(); while (ancestor && !hasResilientAncestor) { hasResilientAncestor |= - ancestor->isResilient(SwiftModule, ResilienceExpansion::Maximal); + ancestor->hasResilientMetadata(SwiftModule, ResilienceExpansion::Maximal); ancestor = ancestor->getSuperclassDecl(); } @@ -339,7 +339,7 @@ void TBDGenVisitor::visitClassDecl(ClassDecl *CD) { void addMethod(SILDeclRef method) { assert(method.getDecl()->getDeclContext() == CD); - if (CD->isResilient()) { + if (CD->hasResilientMetadata()) { if (FirstTime) { FirstTime = false; diff --git a/test/IRGen/class_resilience.swift b/test/IRGen/class_resilience.swift index 654bf4ff9898a..c7285aac78a64 100644 --- a/test/IRGen/class_resilience.swift +++ b/test/IRGen/class_resilience.swift @@ -97,8 +97,6 @@ // CHECK-objc-SAME: @"$s16class_resilience14ResilientChildCMm" // CHECK-native-SAME: i32 0 -// CHECK: @"$s16class_resilience16FixedLayoutChildCMo" = {{(protected )?}}{{(dllexport )?}}global [[BOUNDS]] zeroinitializer - // CHECK: @"$s16class_resilience17MyResilientParentCMo" = {{(protected )?}}{{(dllexport )?}}constant [[BOUNDS]] // CHECK-SAME-32: { [[INT]] 52, i32 2, i32 13 } // CHECK-SAME-64: { [[INT]] 80, i32 2, i32 10 } @@ -119,10 +117,6 @@ // CHECK: @"$s16class_resilience14ResilientChildC5fields5Int32VvsTq" = {{(protected )?}}{{(dllexport )?}}alias %swift.method_descriptor, getelementptr inbounds // CHECK: @"$s16class_resilience14ResilientChildC5fields5Int32VvMTq" = {{(protected )?}}{{(dllexport )?}}alias %swift.method_descriptor, getelementptr inbounds -// CHECK: @"$s16class_resilience16FixedLayoutChildC5fields5Int32VvgTq" = {{(protected )?}}{{(dllexport )?}}alias %swift.method_descriptor, getelementptr inbounds -// CHECK: @"$s16class_resilience16FixedLayoutChildC5fields5Int32VvsTq" = {{(protected )?}}{{(dllexport )?}}alias %swift.method_descriptor, getelementptr inbounds -// CHECK: @"$s16class_resilience16FixedLayoutChildC5fields5Int32VvMTq" = {{(protected )?}}{{(dllexport )?}}alias %swift.method_descriptor, getelementptr inbounds - // CHECK: @"$s16class_resilience21ResilientGenericChildC5fields5Int32VvgTq" = {{(protected )?}}{{(dllexport )?}}alias %swift.method_descriptor, getelementptr inbounds // CHECK: @"$s16class_resilience21ResilientGenericChildC5fields5Int32VvsTq" = {{(protected )?}}{{(dllexport )?}}alias %swift.method_descriptor, getelementptr inbounds // CHECK: @"$s16class_resilience21ResilientGenericChildC5fields5Int32VvMTq" = {{(protected )?}}{{(dllexport )?}}alias %swift.method_descriptor, getelementptr inbounds @@ -206,15 +200,6 @@ public class ResilientChild : ResilientOutsideParent { } } -// Superclass is resilient, but the class is fixed-layout. -// This simulates a user app subclassing a class in a resilient -// framework. In this case, we still want to emit a base offset -// global. - -@_fixed_layout public class FixedLayoutChild : ResilientOutsideParent { - public var field: Int32 = 0 -} - // Superclass is resilient, so the number of fields and their // offsets is not known at compile time @@ -525,14 +510,6 @@ extension ResilientGenericOutsideParent { // CHECK-NEXT: ret void -// FixedLayoutChild metadata initialization function - -// CHECK-LABEL: define internal swiftcc %swift.metadata_response @"$s16class_resilience16FixedLayoutChildCMr"(%swift.type*, i8*, i8**) - -// Initialize the superclass field... -// CHECK: call void @swift_initClassMetadata(%swift.type* %0, [[INT]] 0, [[INT]] 1, i8*** {{%.*}}, [[INT]]* {{%.*}}) - - // ResilientGenericChild metadata initialization function // CHECK-LABEL: define internal %swift.type* @"$s16class_resilience21ResilientGenericChildCMi"(%swift.type_descriptor*, i8**, i8*) diff --git a/test/IRGen/fixed_layout_class.swift b/test/IRGen/fixed_layout_class.swift new file mode 100644 index 0000000000000..ac75afcbf8a09 --- /dev/null +++ b/test/IRGen/fixed_layout_class.swift @@ -0,0 +1,126 @@ +// RUN: %empty-directory(%t) +// RUN: %{python} %utils/chex.py < %s > %t/class_resilience.swift +// RUN: %target-swift-frontend -emit-module -enable-resilience -enable-class-resilience -emit-module-path=%t/resilient_struct.swiftmodule -module-name=resilient_struct %S/../Inputs/resilient_struct.swift +// RUN: %target-swift-frontend -emit-module -enable-resilience -enable-class-resilience -emit-module-path=%t/resilient_enum.swiftmodule -module-name=resilient_enum -I %t %S/../Inputs/resilient_enum.swift +// RUN: %target-swift-frontend -emit-module -enable-resilience -enable-class-resilience -emit-module-path=%t/resilient_class.swiftmodule -module-name=resilient_class -I %t %S/../Inputs/resilient_class.swift +// RUN: %target-swift-frontend -emit-module -enable-resilience -enable-class-resilience -emit-module-path=%t/fixed_layout_class.swiftmodule -module-name=fixed_layout_class -I %t %S/../Inputs/fixed_layout_class.swift +// RUN: %target-swift-frontend -I %t -emit-ir -enable-resilience -enable-class-resilience %t/class_resilience.swift | %FileCheck %t/class_resilience.swift --check-prefix=CHECK --check-prefix=CHECK-%target-ptrsize --check-prefix=CHECK-%target-runtime -DINT=i%target-ptrsize +// RUN: %target-swift-frontend -I %t -emit-ir -enable-resilience -enable-class-resilience -O %t/class_resilience.swift + +// This tests @_fixed_layout classes in resilient modules. +import fixed_layout_class + +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @"$s16class_resilience20useRootClassPropertyyy013fixed_layout_A0026OutsideParentWithResilientF0CF"(%T18fixed_layout_class34OutsideParentWithResilientPropertyC*) +public func useRootClassProperty(_ o: OutsideParentWithResilientProperty) { + // CHECK: getelementptr inbounds %T18fixed_layout_class34OutsideParentWithResilientPropertyC, %T18fixed_layout_class34OutsideParentWithResilientPropertyC* %0, i32 0, i32 1 + _ = o.p + // CHECK: load [[INT]], [[INT]]* @"$s18fixed_layout_class34OutsideParentWithResilientPropertyC1s16resilient_struct4SizeVvpWvd" + _ = o.s + // CHECK: load [[INT]], [[INT]]* @"$s18fixed_layout_class34OutsideParentWithResilientPropertyC5colors5Int32VvpWvd" + _ = o.color + // CHECK: ret void +} + +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @"$s16class_resilience19useSubclassPropertyyy013fixed_layout_A012OutsideChildCF"(%T18fixed_layout_class12OutsideChildC*) +public func useSubclassProperty(_ o: OutsideChild) { + // CHECK: getelementptr inbounds %T18fixed_layout_class13OutsideParentC, %T18fixed_layout_class13OutsideParentC* %4, i32 0, i32 1 + _ = o.property + // CHECK: getelementptr inbounds %T18fixed_layout_class12OutsideChildC, %T18fixed_layout_class12OutsideChildC* %0, i32 0, i32 2 + _ = o.childProperty + // CHECK: ret void +} + +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @"$s16class_resilience27useGenericRootClassPropertyyy013fixed_layout_A00D13OutsideParentCyxGlF"(%T18fixed_layout_class20GenericOutsideParentC*) +public func useGenericRootClassProperty(_ o: GenericOutsideParent) { + // -- we load the base offset twice, first to get the generic parameter out and + // then for the property itself. + + // CHECK: [[BASE:%.*]] = load [[INT]], [[INT]]* getelementptr inbounds ({ [[INT]], i32, i32 }, { [[INT]], i32, i32 }* @"$s18fixed_layout_class20GenericOutsideParentCMo", i32 0, i32 0) + + // CHECK: [[METADATA_ADDR:%.*]] = getelementptr inbounds %T18fixed_layout_class20GenericOutsideParentC, %T18fixed_layout_class20GenericOutsideParentC* %0, i32 0, i32 0, i32 0 + // CHECK: [[METADATA:%.*]] = load %swift.type*, %swift.type** [[METADATA_ADDR]] + + // CHECK: [[BASE:%.*]] = load [[INT]], [[INT]]* getelementptr inbounds ({ [[INT]], i32, i32 }, { [[INT]], i32, i32 }* @"$s18fixed_layout_class20GenericOutsideParentCMo", i32 0, i32 0) + // CHECK: [[FIELD_OFFSET_OFFSET:%.*]] = add [[INT]] [[BASE]], {{4|8}} + + // CHECK: [[METADATA_ADDR:%.*]] = bitcast %swift.type* [[METADATA]] to i8* + // CHECK: [[FIELD_OFFSET_ADDR:%.*]] = getelementptr inbounds i8, i8* [[METADATA_ADDR]], [[INT]] [[FIELD_OFFSET_OFFSET]] + // CHECK: [[FIELD_OFFSET_PTR:%.*]] = bitcast i8* [[FIELD_OFFSET_ADDR]] to [[INT]]* + // CHECK: [[FIELD_OFFSET:%.*]] = load [[INT]], [[INT]]* [[FIELD_OFFSET_PTR]] + _ = o.property + + // CHECK: ret void +} + +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @"$s16class_resilience27useGenericRootClassPropertyyy013fixed_layout_A00D13OutsideParentCySiGF"(%T18fixed_layout_class20GenericOutsideParentCySiG*) +public func useGenericRootClassProperty(_ o: GenericOutsideParent) { + // CHECK: getelementptr inbounds %T18fixed_layout_class20GenericOutsideParentCySiG, %T18fixed_layout_class20GenericOutsideParentCySiG* %0, i32 0, i32 1 + _ = o.property + + // CHECK: ret void +} + +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @"$s16class_resilience26useGenericSubclassPropertyyy013fixed_layout_A00D12OutsideChildCyxGlF"(%T18fixed_layout_class19GenericOutsideChildC*) +public func useGenericSubclassProperty(_ o: GenericOutsideChild) { + // -- we load the base offset twice, first to get the generic parameter out and + // then for the property itself. + + // CHECK: [[BASE:%.*]] = load [[INT]], [[INT]]* getelementptr inbounds ({ [[INT]], i32, i32 }, { [[INT]], i32, i32 }* @"$s18fixed_layout_class19GenericOutsideChildCMo", i32 0, i32 0) + + // CHECK: [[UPCAST:%.*]] = bitcast %T18fixed_layout_class19GenericOutsideChildC* %0 to %T18fixed_layout_class20GenericOutsideParentC* + // CHECK: [[METADATA_ADDR:%.*]] = getelementptr inbounds %T18fixed_layout_class20GenericOutsideParentC, %T18fixed_layout_class20GenericOutsideParentC* [[UPCAST]], i32 0, i32 0, i32 0 + // CHECK: [[METADATA:%.*]] = load %swift.type*, %swift.type** [[METADATA_ADDR]] + + // CHECK: [[BASE:%.*]] = load [[INT]], [[INT]]* getelementptr inbounds ({ [[INT]], i32, i32 }, { [[INT]], i32, i32 }* @"$s18fixed_layout_class20GenericOutsideParentCMo", i32 0, i32 0) + // CHECK: [[FIELD_OFFSET_OFFSET:%.*]] = add [[INT]] [[BASE]], {{4|8}} + + // CHECK: [[METADATA_ADDR:%.*]] = bitcast %swift.type* [[METADATA]] to i8* + // CHECK: [[FIELD_OFFSET_ADDR:%.*]] = getelementptr inbounds i8, i8* [[METADATA_ADDR]], [[INT]] [[FIELD_OFFSET_OFFSET]] + // CHECK: [[FIELD_OFFSET_PTR:%.*]] = bitcast i8* [[FIELD_OFFSET_ADDR]] to [[INT]]* + // CHECK: [[FIELD_OFFSET:%.*]] = load [[INT]], [[INT]]* [[FIELD_OFFSET_PTR]] + _ = o.property + + // CHECK: [[METADATA_ADDR:%.*]] = getelementptr inbounds %T18fixed_layout_class19GenericOutsideChildC, %T18fixed_layout_class19GenericOutsideChildC* %0, i32 0, i32 0, i32 0 + // CHECK: [[METADATA:%.*]] = load %swift.type*, %swift.type** [[METADATA_ADDR]] + + // CHECK: [[BASE:%.*]] = load [[INT]], [[INT]]* getelementptr inbounds ({ [[INT]], i32, i32 }, { [[INT]], i32, i32 }* @"$s18fixed_layout_class19GenericOutsideChildCMo", i32 0, i32 0) + // CHECK: [[FIELD_OFFSET_OFFSET:%.*]] = add [[INT]] [[BASE]], {{4|8}} + + // CHECK: [[METADATA_ADDR:%.*]] = bitcast %swift.type* [[METADATA]] to i8* + // CHECK: [[FIELD_OFFSET_ADDR:%.*]] = getelementptr inbounds i8, i8* [[METADATA_ADDR]], [[INT]] [[FIELD_OFFSET_OFFSET]] + // CHECK: [[FIELD_OFFSET_PTR:%.*]] = bitcast i8* [[FIELD_OFFSET_ADDR]] to [[INT]]* + // CHECK: [[FIELD_OFFSET:%.*]] = load [[INT]], [[INT]]* [[FIELD_OFFSET_PTR]] + _ = o.childProperty + + // CHECK: ret void +} + +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @"$s16class_resilience26useGenericSubclassPropertyyy013fixed_layout_A00D12OutsideChildCySiGF"(%T18fixed_layout_class19GenericOutsideChildCySiG*) +public func useGenericSubclassProperty(_ o: GenericOutsideChild) { + // CHECK: [[UPCAST:%.*]] = bitcast %T18fixed_layout_class19GenericOutsideChildCySiG* %0 to %T18fixed_layout_class20GenericOutsideParentCySiG* + // CHECK: getelementptr inbounds %T18fixed_layout_class20GenericOutsideParentCySiG, %T18fixed_layout_class20GenericOutsideParentCySiG* [[UPCAST]], i32 0, i32 1 + _ = o.property + + // CHECK: getelementptr inbounds %T18fixed_layout_class19GenericOutsideChildCySiG, %T18fixed_layout_class19GenericOutsideChildCySiG* %0, i32 0, i32 2 + _ = o.childProperty + + // CHECK: ret void +} + +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @"$s16class_resilience17callVirtualMethodyy013fixed_layout_A013OutsideParentCF"(%T18fixed_layout_class13OutsideParentC*) +public func callVirtualMethod(_ o: OutsideParent) { + // Note: virtual method calls still use dispatch thunks + + // CHECK: call swiftcc void @"$s18fixed_layout_class13OutsideParentC6methodyyFTj" + _ = o.method() + + // CHECK: ret void +} + +@_fixed_layout open class MyChildOfOutsideParent : OutsideParent { + public func newMethod() {} +} + +// Make sure we emit the dispatch thunk: + +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @"$s16class_resilience22MyChildOfOutsideParentC9newMethodyyFTj"(%T16class_resilience22MyChildOfOutsideParentC* swiftself) diff --git a/test/IRGen/super.sil b/test/IRGen/super.sil index 075c106760477..12b74bc716257 100644 --- a/test/IRGen/super.sil +++ b/test/IRGen/super.sil @@ -1,6 +1,13 @@ // RUN: %empty-directory(%t) -// RUN: %target-swift-frontend -assume-parsing-unqualified-ownership-sil -emit-module -enable-resilience -I %t -module-name resilient_struct -o %t %S/../Inputs/resilient_struct.swift -// RUN: %target-swift-frontend -assume-parsing-unqualified-ownership-sil -emit-module -enable-resilience -I %t -module-name resilient_class -o %t %S/../Inputs/resilient_class.swift +// RUN: %target-swift-frontend -emit-module -enable-resilience -I %t -o %t %S/../Inputs/resilient_struct.swift +// RUN: %target-swift-frontend -emit-module -enable-resilience -I %t -o %t %S/../Inputs/resilient_class.swift + +// Note: we build fixed_layout_class without -enable-resilience, since with +// -enable-resilience even @_fixed_layout classes have resilient metadata, and +// we want to test the fragile access pattern here. + +// RUN: %target-swift-frontend -emit-module -I %t -o %t %S/../Inputs/fixed_layout_class.swift + // RUN: %target-swift-frontend -assume-parsing-unqualified-ownership-sil -enable-resilience -parse-sil -parse-as-library -emit-ir -I %t %s | %FileCheck %s // CHECK: %swift.type = type { [[INT:i32|i64]] } @@ -11,6 +18,7 @@ import Builtin import Swift import SwiftShims import resilient_class +import fixed_layout_class public class ChildToResilientParent : ResilientOutsideParent { public override func method() @@ -99,11 +107,11 @@ bb0(%0 : $ChildToFixedParent): } // CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @"$s5super18ChildToFixedParentC6methodyyF"(%T5super18ChildToFixedParentC* swiftself) -// CHECK: [[TMP:%.*]] = call swiftcc %swift.metadata_response @"$s15resilient_class13OutsideParentCMa"([[INT]] 0) +// CHECK: [[TMP:%.*]] = call swiftcc %swift.metadata_response @"$s18fixed_layout_class13OutsideParentCMa"([[INT]] 0) // CHECK: [[SUPER_METADATA:%.*]] = extractvalue %swift.metadata_response [[TMP]], 0 -// CHECK: [[OPAQUE_SUPER_METADATA:%.*]] = bitcast %swift.type* [[SUPER_METADATA]] to void (%T15resilient_class13OutsideParentC*)** -// CHECK: [[VTABLE_SLOT:%.*]] = getelementptr inbounds void (%T15resilient_class13OutsideParentC*)*, void (%T15resilient_class13OutsideParentC*)** [[OPAQUE_SUPER_METADATA]] -// CHECK: [[FN_PTR:%.*]] = load void (%T15resilient_class13OutsideParentC*)*, void (%T15resilient_class13OutsideParentC*)** [[VTABLE_SLOT]] +// CHECK: [[OPAQUE_SUPER_METADATA:%.*]] = bitcast %swift.type* [[SUPER_METADATA]] to void (%T18fixed_layout_class13OutsideParentC*)** +// CHECK: [[VTABLE_SLOT:%.*]] = getelementptr inbounds void (%T18fixed_layout_class13OutsideParentC*)*, void (%T18fixed_layout_class13OutsideParentC*)** [[OPAQUE_SUPER_METADATA]] +// CHECK: [[FN_PTR:%.*]] = load void (%T18fixed_layout_class13OutsideParentC*)*, void (%T18fixed_layout_class13OutsideParentC*)** [[VTABLE_SLOT]] // CHECK: call swiftcc void // static super.ChildToFixedParent.classMethod () -> () @@ -120,7 +128,7 @@ bb0(%0 : $@thick ChildToFixedParent.Type): // ChildToFixedParent is in our resilience domain - load super metadata directly. // CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @"$s5super18ChildToFixedParentC11classMethodyyFZ"(%swift.type* swiftself) -// CHECK: [[TMP:%.*]] = call swiftcc %swift.metadata_response @"$s15resilient_class13OutsideParentCMa"([[INT]] 0) +// CHECK: [[TMP:%.*]] = call swiftcc %swift.metadata_response @"$s18fixed_layout_class13OutsideParentCMa"([[INT]] 0) // CHECK: [[SUPER_METADATA:%.*]] = extractvalue %swift.metadata_response [[TMP]], 0 // CHECK: [[OPAQUE_SUPER_METADATA:%.*]] = bitcast %swift.type* [[SUPER_METADATA]] to void (%swift.type*)** // CHECK: [[VTABLE_SLOT:%.*]] = getelementptr inbounds void (%swift.type*)*, void (%swift.type*)** [[OPAQUE_SUPER_METADATA]] diff --git a/test/Inputs/fixed_layout_class.swift b/test/Inputs/fixed_layout_class.swift new file mode 100644 index 0000000000000..6f04849e436fe --- /dev/null +++ b/test/Inputs/fixed_layout_class.swift @@ -0,0 +1,129 @@ + +import resilient_struct + + +// Fixed-layout, fixed-size base class + +@_fixed_layout +open class OutsideParent { + public final var property: String = "OutsideParent.property" + + open class var classProperty: String { + return "OutsideParent.classProperty" + } + + public init() { + print("OutsideParent.init()") + } + + open func method() { + print("OutsideParent.method()") + } + + open class func classMethod() { + print("OutsideParent.classMethod()") + } +} + + +// Fixed-layout, resiliently-sized base class + +@_fixed_layout +open class OutsideParentWithResilientProperty { + public let p: Point + public let s: Size + public let color: Int32 + + public final lazy var laziestNumber = 0 + + public init(p: Point, s: Size, color: Int32) { + self.p = p + self.s = s + self.color = color + } +} + + +// Fixed-layout, fixed-size subclass + +@_fixed_layout +open class OutsideChild : OutsideParent { + public let childProperty: Int = 0 + + open override func method() { + print("OutsideChild.method()") + super.method() + } + + open override class func classMethod() { + print("OutsideChild.classMethod()") + super.classMethod() + } +} + + +// Fixed-layout, dependently-sized, generic base class + +@_fixed_layout +open class GenericOutsideParent { + public final var property: A + public init(property: A) { + self.property = property + print("GenericOutsideParent.init()") + } + + open func method() { + print("GenericOutsideParent.method()") + } + + open class func classMethod() { + print("GenericOutsideParent.classMethod()") + } +} + + +// Fixed-layout, dependently-sized, generic subclass + +@_fixed_layout +open class GenericOutsideChild : GenericOutsideParent { + public final var childProperty: A + + public override init(property: A) { + self.childProperty = property + print("GenericOutsideGenericChild.init(a: A)") + super.init(property: property) + } + + open override func method() { + print("GenericOutsideChild.method()") + super.method() + } + + open override class func classMethod() { + print("GenericOutsideChild.classMethod()") + super.classMethod() + } +} + + +// Fixed-layout, fixed-size subclass of generic class + +@_fixed_layout +open class ConcreteOutsideChild : GenericOutsideParent { + public final var childProperty: Int = 0 + + public override init(property: String) { + print("ConcreteOutsideChild.init(property: String)") + super.init(property: property) + } + + open override func method() { + print("ConcreteOutsideChild.method()") + super.method() + } + + open override class func classMethod() { + print("ConcreteOutsideChild.classMethod()") + super.classMethod() + } +} diff --git a/test/Inputs/resilient_class.swift b/test/Inputs/resilient_class.swift index 3e823d1e4fb0d..9e9a20103148a 100644 --- a/test/Inputs/resilient_class.swift +++ b/test/Inputs/resilient_class.swift @@ -2,47 +2,6 @@ import resilient_struct -// Fixed-layout, fixed-size base class - -@_fixed_layout -open class OutsideParent { - open var property: String = "OutsideParent.property" - - open class var classProperty: String { - return "OutsideParent.classProperty" - } - - public init() { - print("OutsideParent.init()") - } - - open func method() { - print("OutsideParent.method()") - } - - open class func classMethod() { - print("OutsideParent.classMethod()") - } -} - - -// Fixed-layout, resiliently-sized base class - -@_fixed_layout -open class OutsideParentWithResilientProperty { - public let p: Point - public let s: Size - public let color: Int32 - - public final lazy var laziestNumber = 0 - - public init(p: Point, s: Size, color: Int32) { - self.p = p - self.s = s - self.color = color - } -} - // Resilient base class @@ -72,21 +31,6 @@ open class ResilientOutsideParent { } -// Fixed-layout, fixed-size subclass - -@_fixed_layout -open class OutsideChild : OutsideParent { - open override func method() { - print("OutsideChild.method()") - super.method() - } - - open override class func classMethod() { - print("OutsideChild.classMethod()") - super.classMethod() - } -} - // Resilient subclass @@ -103,26 +47,6 @@ open class ResilientOutsideChild : ResilientOutsideParent { } -// Fixed-layout, dependently-sized, generic base class - -@_fixed_layout -open class GenericOutsideParent { - open var property: A - public init(property: A) { - self.property = property - print("GenericOutsideParent.init()") - } - - open func method() { - print("GenericOutsideParent.method()") - } - - open class func classMethod() { - print("GenericOutsideParent.classMethod()") - } -} - - // Resilient generic base class open class ResilientGenericOutsideParent { @@ -142,27 +66,6 @@ open class ResilientGenericOutsideParent { } -// Fixed-layout, dependently-sized, generic subclass - -@_fixed_layout -open class GenericOutsideChild : GenericOutsideParent { - public override init(property: A) { - print("GenericOutsideGenericChild.init(a: A)") - super.init(property: property) - } - - open override func method() { - print("GenericOutsideChild.method()") - super.method() - } - - open override class func classMethod() { - print("GenericOutsideChild.classMethod()") - super.classMethod() - } -} - - // Resilient generic subclass open class ResilientGenericOutsideChild : ResilientGenericOutsideParent { @@ -183,27 +86,6 @@ open class ResilientGenericOutsideChild : ResilientGenericOutsideParent { } -// Fixed-layout, fixed-size subclass of generic class - -@_fixed_layout -open class ConcreteOutsideChild : GenericOutsideParent { - public override init(property: String) { - print("ConcreteOutsideChild.init(property: String)") - super.init(property: property) - } - - open override func method() { - print("ConcreteOutsideChild.method()") - super.method() - } - - open override class func classMethod() { - print("ConcreteOutsideChild.classMethod()") - super.classMethod() - } -} - - // Resilient subclass of generic class open class ResilientConcreteOutsideChild : ResilientGenericOutsideParent { diff --git a/test/Interpreter/class_resilience.swift b/test/Interpreter/class_resilience.swift index a16754eda97dc..7cdba43c3b5e6 100644 --- a/test/Interpreter/class_resilience.swift +++ b/test/Interpreter/class_resilience.swift @@ -6,10 +6,13 @@ // RUN: %target-build-swift-dylib(%t/libresilient_class.%target-dylib-extension) -Xfrontend -enable-resilience -Xfrontend -enable-class-resilience %S/../Inputs/resilient_class.swift -emit-module -emit-module-path %t/resilient_class.swiftmodule -module-name resilient_class -I%t -L%t -lresilient_struct // RUN: %target-codesign %t/libresilient_class.%target-dylib-extension -// RUN: %target-build-swift %s -L %t -I %t -lresilient_struct -lresilient_class -o %t/main -Xfrontend -enable-class-resilience -Xlinker -rpath -Xlinker %t +// RUN: %target-build-swift-dylib(%t/libfixed_layout_class.%target-dylib-extension) -Xfrontend -enable-resilience -Xfrontend -enable-class-resilience %S/../Inputs/fixed_layout_class.swift -emit-module -emit-module-path %t/fixed_layout_class.swiftmodule -module-name fixed_layout_class -I%t -L%t -lresilient_struct +// RUN: %target-codesign %t/libfixed_layout_class.%target-dylib-extension + +// RUN: %target-build-swift %s -L %t -I %t -lresilient_struct -lresilient_class -lfixed_layout_class -o %t/main -Xfrontend -enable-class-resilience -Xlinker -rpath -Xlinker %t // RUN: %target-codesign %t/main -// RUN: %target-run %t/main %t/libresilient_struct.%target-dylib-extension %t/libresilient_class.%target-dylib-extension +// RUN: %target-run %t/main %t/libresilient_struct.%target-dylib-extension %t/libresilient_class.%target-dylib-extension %t/libfixed_layout_class.%target-dylib-extension // RUN: %target-build-swift-dylib(%t/libresilient_struct_wmo.%target-dylib-extension) -Xfrontend -enable-resilience -Xfrontend -enable-class-resilience %S/../Inputs/resilient_struct.swift -emit-module -emit-module-path %t/resilient_struct.swiftmodule -module-name resilient_struct -whole-module-optimization // RUN: %target-codesign %t/libresilient_struct_wmo.%target-dylib-extension @@ -17,16 +20,19 @@ // RUN: %target-build-swift-dylib(%t/libresilient_class_wmo.%target-dylib-extension) -Xfrontend -enable-resilience -Xfrontend -enable-class-resilience %S/../Inputs/resilient_class.swift -emit-module -emit-module-path %t/resilient_class.swiftmodule -module-name resilient_class -I%t -L%t -lresilient_struct_wmo -whole-module-optimization // RUN: %target-codesign %t/libresilient_class_wmo.%target-dylib-extension -// RUN: %target-build-swift %s -L %t -I %t -lresilient_struct_wmo -lresilient_class_wmo -Xfrontend -enable-class-resilience -o %t/main2 -Xlinker -rpath -Xlinker %t -module-name main +// RUN: %target-build-swift-dylib(%t/libfixed_layout_class_wmo.%target-dylib-extension) -Xfrontend -enable-resilience -Xfrontend -enable-class-resilience %S/../Inputs/fixed_layout_class.swift -emit-module -emit-module-path %t/fixed_layout_class.swiftmodule -module-name fixed_layout_class -I%t -L%t -lresilient_struct_wmo -whole-module-optimization +// RUN: %target-codesign %t/libfixed_layout_class_wmo.%target-dylib-extension + +// RUN: %target-build-swift %s -L %t -I %t -lresilient_struct_wmo -lresilient_class_wmo -lfixed_layout_class_wmo -Xfrontend -enable-class-resilience -o %t/main2 -Xlinker -rpath -Xlinker %t -module-name main // RUN: %target-codesign %t/main2 -// RUN: %target-run %t/main2 %t/libresilient_struct_wmo.%target-dylib-extension %t/libresilient_class_wmo.%target-dylib-extension +// RUN: %target-run %t/main2 %t/libresilient_struct_wmo.%target-dylib-extension %t/libresilient_class_wmo.%target-dylib-extension %t/libfixed_layout_class_wmo.%target-dylib-extension // REQUIRES: executable_test import StdlibUnittest - +import fixed_layout_class import resilient_class import resilient_struct diff --git a/test/SILGen/partial_apply_super.swift b/test/SILGen/partial_apply_super.swift index 2020817fc7c85..cce3f6ddf28e6 100644 --- a/test/SILGen/partial_apply_super.swift +++ b/test/SILGen/partial_apply_super.swift @@ -1,10 +1,18 @@ // RUN: %empty-directory(%t) -// RUN: %target-swift-frontend -I %t -emit-module -emit-module-path=%t/resilient_struct.swiftmodule -module-name resilient_struct %S/../Inputs/resilient_struct.swift -// RUN: %target-swift-frontend -I %t -emit-module -emit-module-path=%t/resilient_class.swiftmodule -module-name resilient_class %S/../Inputs/resilient_class.swift +// RUN: %target-swift-frontend -I %t -emit-module -emit-module-path=%t/resilient_struct.swiftmodule %S/../Inputs/resilient_struct.swift +// RUN: %target-swift-frontend -I %t -emit-module -emit-module-path=%t/resilient_class.swiftmodule %S/../Inputs/resilient_class.swift + +// Note: we build fixed_layout_class without -enable-resilience, since with +// -enable-resilience even @_fixed_layout classes have resilient metadata, and +// we want to test the fragile access pattern here. + +// RUN: %target-swift-frontend -emit-module -I %t -o %t %S/../Inputs/fixed_layout_class.swift + // RUN: %target-swift-emit-silgen -enable-sil-ownership -module-name partial_apply_super -enable-resilience -parse-as-library -I %t %s | %FileCheck %s import resilient_class +import fixed_layout_class func doFoo(_ f: () -> ()) { f() diff --git a/test/SILGen/super.swift b/test/SILGen/super.swift index 4960c6594d3ce..54698aa115468 100644 --- a/test/SILGen/super.swift +++ b/test/SILGen/super.swift @@ -1,10 +1,18 @@ // RUN: %empty-directory(%t) -// RUN: %target-swift-frontend -I %t -emit-module -emit-module-path=%t/resilient_struct.swiftmodule -module-name resilient_struct %S/../Inputs/resilient_struct.swift -// RUN: %target-swift-frontend -I %t -emit-module -emit-module-path=%t/resilient_class.swiftmodule -module-name resilient_class %S/../Inputs/resilient_class.swift +// RUN: %target-swift-frontend -I %t -emit-module -emit-module-path=%t/resilient_struct.swiftmodule %S/../Inputs/resilient_struct.swift +// RUN: %target-swift-frontend -I %t -emit-module -emit-module-path=%t/resilient_class.swiftmodule %S/../Inputs/resilient_class.swift + +// Note: we build fixed_layout_class without -enable-resilience, since with +// -enable-resilience even @_fixed_layout classes have resilient metadata, and +// we want to test the fragile access pattern here. + +// RUN: %target-swift-frontend -emit-module -I %t -o %t %S/../Inputs/fixed_layout_class.swift + // RUN: %target-swift-emit-silgen -module-name super -parse-as-library -I %t %s | %FileCheck %s import resilient_class +import fixed_layout_class public class Parent { public final var finalProperty: String { From 1c98a1a63c8350824888c7a8dbd1d420d38bd9d7 Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Thu, 29 Nov 2018 16:23:59 -0500 Subject: [PATCH 5/6] Evolution: Add some tests for @_fixed_layout classes @_fixed_layout classes have resilient vtables now, so we can add and re-order methods. Removing overrides is not legal though because they are subject to devirtualization. --- ...lass_fixed_layout_add_virtual_method.swift | 45 ++++++++++++++++ ...d_layout_add_virtual_method_subclass.swift | 28 ++++++++++ ...ed_layout_superclass_reorder_methods.swift | 48 +++++++++++++++++ ...lass_fixed_layout_add_virtual_method.swift | 19 +++++++ ...d_layout_add_virtual_method_subclass.swift | 36 +++++++++++++ ...ed_layout_superclass_reorder_methods.swift | 53 +++++++++++++++++++ 6 files changed, 229 insertions(+) create mode 100644 validation-test/Evolution/Inputs/class_fixed_layout_add_virtual_method.swift create mode 100644 validation-test/Evolution/Inputs/class_fixed_layout_add_virtual_method_subclass.swift create mode 100644 validation-test/Evolution/Inputs/class_fixed_layout_superclass_reorder_methods.swift create mode 100644 validation-test/Evolution/test_class_fixed_layout_add_virtual_method.swift create mode 100644 validation-test/Evolution/test_class_fixed_layout_add_virtual_method_subclass.swift create mode 100644 validation-test/Evolution/test_class_fixed_layout_superclass_reorder_methods.swift diff --git a/validation-test/Evolution/Inputs/class_fixed_layout_add_virtual_method.swift b/validation-test/Evolution/Inputs/class_fixed_layout_add_virtual_method.swift new file mode 100644 index 0000000000000..2224ee206b025 --- /dev/null +++ b/validation-test/Evolution/Inputs/class_fixed_layout_add_virtual_method.swift @@ -0,0 +1,45 @@ + +public func getVersion() -> Int { +#if BEFORE + return 0 +#else + return 1 +#endif +} + +#if BEFORE + +@_fixed_layout +public class AddVirtualMethod { + public init() {} + + public func firstMethod() -> Int { + return 1 + } + + public func secondMethod() -> Int { + return 2 + } +} + +#else + +@_fixed_layout +public class AddVirtualMethod { + // Note: methods were re-ordered, new method added in the middle + public func secondMethod() -> Int { + return 2 + } + + public func thirdMethod() -> Int { + return 3 + } + + public func firstMethod() -> Int { + return 1 + } + + public init() {} +} + +#endif diff --git a/validation-test/Evolution/Inputs/class_fixed_layout_add_virtual_method_subclass.swift b/validation-test/Evolution/Inputs/class_fixed_layout_add_virtual_method_subclass.swift new file mode 100644 index 0000000000000..8a08cd8fef6ac --- /dev/null +++ b/validation-test/Evolution/Inputs/class_fixed_layout_add_virtual_method_subclass.swift @@ -0,0 +1,28 @@ + +#if BEFORE + +@_fixed_layout +open class AddVirtualMethod { + public init() {} + + open func f1() -> Int { + return 1 + } +} + +#else + +@_fixed_layout +open class AddVirtualMethod { + public init() {} + + open func f1() -> Int { + return f2() + 1 + } + + open func f2() -> Int { + return 0 + } +} + +#endif diff --git a/validation-test/Evolution/Inputs/class_fixed_layout_superclass_reorder_methods.swift b/validation-test/Evolution/Inputs/class_fixed_layout_superclass_reorder_methods.swift new file mode 100644 index 0000000000000..e9bb737d7ffa3 --- /dev/null +++ b/validation-test/Evolution/Inputs/class_fixed_layout_superclass_reorder_methods.swift @@ -0,0 +1,48 @@ +public func getVersion() -> Int { +#if BEFORE + return 0 +#else + return 1 +#endif +} + +#if BEFORE +@_fixed_layout +open class Base { + public init() {} + open func firstMethod() -> Int { + return 1 + } + open func secondMethod() -> Int { + return 2 + } + open func callOverriddenMethods() -> Int { + return firstMethod() * 10 + secondMethod() + } +} +#else +@_fixed_layout +open class Base { + public init() {} + open func secondMethod() -> Int { + return 2 + } + open func firstMethod() -> Int { + return 1 + } + open func callOverriddenMethods() -> Int { + return firstMethod() * 10 + secondMethod() + } +} +#endif + +@_fixed_layout +public class Derived : Base { + public override func firstMethod() -> Int { + return 10 + } + + public override func secondMethod() -> Int { + return 20 + } +} diff --git a/validation-test/Evolution/test_class_fixed_layout_add_virtual_method.swift b/validation-test/Evolution/test_class_fixed_layout_add_virtual_method.swift new file mode 100644 index 0000000000000..d4d7a269ee6c3 --- /dev/null +++ b/validation-test/Evolution/test_class_fixed_layout_add_virtual_method.swift @@ -0,0 +1,19 @@ +// RUN: %target-resilience-test +// REQUIRES: executable_test + +import StdlibUnittest +import class_fixed_layout_add_virtual_method + + +var ClassAddVirtualMethodTest = TestSuite("ClassAddVirtualMethod") + +ClassAddVirtualMethodTest.test("ClassAddVirtualMethod") { + let c = AddVirtualMethod() + + do { + expectEqual(1, c.firstMethod()) + expectEqual(2, c.secondMethod()) + } +} + +runAllTests() diff --git a/validation-test/Evolution/test_class_fixed_layout_add_virtual_method_subclass.swift b/validation-test/Evolution/test_class_fixed_layout_add_virtual_method_subclass.swift new file mode 100644 index 0000000000000..193148d549d2c --- /dev/null +++ b/validation-test/Evolution/test_class_fixed_layout_add_virtual_method_subclass.swift @@ -0,0 +1,36 @@ +// RUN: %target-resilience-test +// REQUIRES: executable_test + +import StdlibUnittest +import class_fixed_layout_add_virtual_method_subclass + + +var ClassAddVirtualMethodSubclassTest = TestSuite("ClassAddVirtualMethodSubclass") + +class AddVirtualMethodSubclass : AddVirtualMethod { + func f3() -> Int { + return f1() + 1 + } +} + +ClassAddVirtualMethodSubclassTest.test("AddVirtualMethod") { + let t = AddVirtualMethodSubclass() + + expectEqual(1, t.f1()) + expectEqual(2, t.f3()) +} + +class AddVirtualMethodGenericSubclass : AddVirtualMethod { + func f3(_ t: T) -> [Int : T] { + return [f1() : t] + } +} + +ClassAddVirtualMethodSubclassTest.test("AddVirtualMethodGeneric") { + let t = AddVirtualMethodGenericSubclass() + + expectEqual(1, t.f1()) + expectEqual([1 : "hi"], t.f3("hi")) +} + +runAllTests() diff --git a/validation-test/Evolution/test_class_fixed_layout_superclass_reorder_methods.swift b/validation-test/Evolution/test_class_fixed_layout_superclass_reorder_methods.swift new file mode 100644 index 0000000000000..478955e10ce3a --- /dev/null +++ b/validation-test/Evolution/test_class_fixed_layout_superclass_reorder_methods.swift @@ -0,0 +1,53 @@ +// RUN: %target-resilience-test +// REQUIRES: executable_test + +import StdlibUnittest +import class_fixed_layout_superclass_reorder_methods + + +var SuperclassReorderMethodsTest = TestSuite("SuperclassReorderMethods") + +SuperclassReorderMethodsTest.test("TestOverrides") { + class MyDerived : Base { + override func firstMethod() -> Int { + return 3 + } + override func secondMethod() -> Int { + return 4 + } + } + + expectEqual(MyDerived().callOverriddenMethods(), 34) +} + +SuperclassReorderMethodsTest.test("TestSuper") { + class MyDerived : Base { + override func firstMethod() -> Int { + return super.firstMethod() + 3 + } + override func secondMethod() -> Int { + return super.secondMethod() + 3 + } + } + + expectEqual(MyDerived().callOverriddenMethods(), 45) +} + +extension Derived { + public func firstMethodExt() -> Int { + return firstMethod() + super.firstMethod() + } + + public func secondMethodExt() -> Int { + return secondMethod() + super.secondMethod() + } +} + +SuperclassReorderMethodsTest.test("TestSuperExtension") { + let obj = Derived() + expectEqual(obj.firstMethodExt(), 11) + expectEqual(obj.secondMethodExt(), 22) +} + +runAllTests() + From 9b3758ec389b6c0d49462573efb9b4ce75200ad6 Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Thu, 29 Nov 2018 23:19:23 -0500 Subject: [PATCH 6/6] IRGen: Relax an assertion The result type mismatch comes up when we're calling a covariant override of a class method resiliently as well. --- lib/IRGen/GenCall.cpp | 9 ++++++--- test/IRGen/class_resilience_thunks.swift | 4 ++++ test/Inputs/resilient_class_thunks.swift | 11 +++++++++++ 3 files changed, 21 insertions(+), 3 deletions(-) diff --git a/lib/IRGen/GenCall.cpp b/lib/IRGen/GenCall.cpp index f208f3e0adfea..6f6b488ddef60 100644 --- a/lib/IRGen/GenCall.cpp +++ b/lib/IRGen/GenCall.cpp @@ -1510,11 +1510,14 @@ void CallEmission::emitToUnmappedExplosion(Explosion &out) { // For ABI reasons the result type of the call might not actually match the // expected result type. + // + // This can happen when calling C functions, or class method dispatch thunks + // for methods that have covariant ABI-compatible overrides. auto expectedNativeResultType = nativeSchema.getExpandedType(IGF.IGM); if (result->getType() != expectedNativeResultType) { - // This should only be needed when we call C functions. - assert(getCallee().getOrigFunctionType()->getLanguage() == - SILFunctionLanguage::C); + auto origFnType = getCallee().getOrigFunctionType(); + assert(origFnType->getLanguage() == SILFunctionLanguage::C || + origFnType->getRepresentation() == SILFunctionTypeRepresentation::Method); result = IGF.coerceValue(result, expectedNativeResultType, IGF.IGM.DataLayout); } diff --git a/test/IRGen/class_resilience_thunks.swift b/test/IRGen/class_resilience_thunks.swift index 0b578a9168abc..017c04cb9bd43 100644 --- a/test/IRGen/class_resilience_thunks.swift +++ b/test/IRGen/class_resilience_thunks.swift @@ -55,3 +55,7 @@ public func testDispatchThunkMyOverride(d: MyDerived, o: Object) { // CHECK: ret void } + +public func testDispatchThunkCast(d: Derived) { + _ = d.returnsSuperclass() +} diff --git a/test/Inputs/resilient_class_thunks.swift b/test/Inputs/resilient_class_thunks.swift index 5425a2214aa42..cf1f11ff9b88b 100644 --- a/test/Inputs/resilient_class_thunks.swift +++ b/test/Inputs/resilient_class_thunks.swift @@ -2,12 +2,18 @@ public class Object { public init() {} } +public class Subclass : Object {} + open class Base { open func takesT(_: T) {} open func takesInt(_: Int) {} open func takesReference(_: Object) {} + + open func returnsSuperclass() -> Object { + fatalError() + } } open class Derived : Base { @@ -19,4 +25,9 @@ open class Derived : Base { // Override has different formal type but is ABI-compatible open override func takesReference(_: Object?) {} + + // Override has a more specific return type but is ABI-compatible + open override func returnsSuperclass() -> Subclass { + fatalError() + } }