Skip to content

[5.0] Fix a race condition with the initialization of class metadata #22447

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
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
4 changes: 0 additions & 4 deletions include/swift/Runtime/RuntimeFunctions.def
Original file line number Diff line number Diff line change
Expand Up @@ -780,7 +780,6 @@ FUNCTION(RelocateClassMetadata,
ARGS(TypeContextDescriptorPtrTy, Int8PtrTy),
ATTRS(NoUnwind))

// struct FieldInfo { size_t Size; size_t AlignMask; };
// void swift_initClassMetadata(Metadata *self,
// ClassLayoutFlags flags,
// size_t numFields,
Expand All @@ -794,7 +793,6 @@ FUNCTION(InitClassMetadata,
SizeTy->getPointerTo()),
ATTRS(NoUnwind))

// struct FieldInfo { size_t Size; size_t AlignMask; };
// void swift_updateClassMetadata(Metadata *self,
// ClassLayoutFlags flags,
// size_t numFields,
Expand All @@ -808,7 +806,6 @@ FUNCTION(UpdateClassMetadata,
SizeTy->getPointerTo()),
ATTRS(NoUnwind))

// struct FieldInfo { size_t Size; size_t AlignMask; };
// MetadataDependency swift_initClassMetadata2(Metadata *self,
// ClassLayoutFlags flags,
// size_t numFields,
Expand All @@ -822,7 +819,6 @@ FUNCTION(InitClassMetadata2,
SizeTy->getPointerTo()),
ATTRS(NoUnwind))

// struct FieldInfo { size_t Size; size_t AlignMask; };
// MetadataDependency swift_updateClassMetadata2(Metadata *self,
// ClassLayoutFlags flags,
// size_t numFields,
Expand Down
25 changes: 17 additions & 8 deletions lib/IRGen/GenMeta.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1815,24 +1815,33 @@ static void emitInitializeFieldOffsetVector(IRGenFunction &IGF,
if (!doesClassMetadataRequireRelocation(IGM, classDecl))
flags |= ClassLayoutFlags::HasStaticVTable;

llvm::Value *dependency;
if (doesClassMetadataRequireInitialization(IGM, classDecl)) {
// Call swift_initClassMetadata().
IGF.Builder.CreateCall(IGM.getInitClassMetadataFn(),
{metadata,
IGM.getSize(Size(uintptr_t(flags))),
numFields, fields.getAddress(), fieldVector});
dependency =
IGF.Builder.CreateCall(IGM.getInitClassMetadata2Fn(),
{metadata,
IGM.getSize(Size(uintptr_t(flags))),
numFields, fields.getAddress(), fieldVector});
} else {
assert(doesClassMetadataRequireUpdate(IGM, classDecl));
assert(IGM.Context.LangOpts.EnableObjCInterop);

// Call swift_updateClassMetadata(). Note that the static metadata
// already references the superclass in this case, but we still want
// to ensure the superclass metadata is initialized first.
IGF.Builder.CreateCall(IGM.getUpdateClassMetadataFn(),
{metadata,
IGM.getSize(Size(uintptr_t(flags))),
numFields, fields.getAddress(), fieldVector});
dependency =
IGF.Builder.CreateCall(IGM.getUpdateClassMetadata2Fn(),
{metadata,
IGM.getSize(Size(uintptr_t(flags))),
numFields, fields.getAddress(), fieldVector});
}

// Collect any possible dependency from initializing the class; generally
// this involves the superclass.
assert(collector);
collector->collect(IGF, dependency);

} else {
assert(isa<StructDecl>(target));

Expand Down
44 changes: 36 additions & 8 deletions lib/IRGen/MetadataRequest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -162,29 +162,57 @@ void MetadataDependencyCollector::checkDependency(IRGenFunction &IGF,

// Otherwise, we need to pull out the response state and compare it against
// the request state.
llvm::Value *requiredState = request.getRequiredState(IGF);

// More advanced metadata states are lower numbers.
static_assert(MetadataStateIsReverseOrdered,
"relying on the ordering of MetadataState here");
auto satisfied = IGF.Builder.CreateICmpULE(metadataState, requiredState);

emitCheckBranch(IGF, satisfied, metadata, requiredState);
}

void MetadataDependencyCollector::collect(IRGenFunction &IGF,
llvm::Value *dependency) {
// Having either both or neither of the PHIs is normal.
// Having just RequiredState means that we already finalized this collector
// and shouldn't be using it anymore.
assert((!RequiredMetadata || RequiredState) &&
"checking dependencies on a finished collector");

assert(dependency->getType() == IGF.IGM.TypeMetadataDependencyTy);

// Lazily create the continuation block and phis.
// Split the dependency.
auto metadata = IGF.Builder.CreateExtractValue(dependency, 0);
auto requiredState = IGF.Builder.CreateExtractValue(dependency, 1);

// We have a dependency if the metadata is non-null; otherwise we're
// satisfied and can continue.
auto satisfied = IGF.Builder.CreateIsNull(metadata);
emitCheckBranch(IGF, satisfied, metadata, requiredState);
}

void MetadataDependencyCollector::emitCheckBranch(IRGenFunction &IGF,
llvm::Value *satisfied,
llvm::Value *metadata,
llvm::Value *requiredState) {
// Lazily create the final continuation block and phis.
if (!RequiredMetadata) {
auto contBB = IGF.createBasicBlock("metadata-dependencies.cont");
RequiredMetadata =
llvm::PHINode::Create(IGF.IGM.TypeMetadataPtrTy, 4, "", contBB);
RequiredState = llvm::PHINode::Create(IGF.IGM.SizeTy, 4, "", contBB);
}

llvm::Value *requiredState = request.getRequiredState(IGF);

// More advanced metadata states are lower numbers.
static_assert(MetadataStateIsReverseOrdered,
"relying on the ordering of MetadataState here");
auto satisfied = IGF.Builder.CreateICmpULE(metadataState, requiredState);

// Conditionally branch to the final continuation block.
auto satisfiedBB = IGF.createBasicBlock("dependency-satisfied");
auto curBB = IGF.Builder.GetInsertBlock();
RequiredMetadata->addIncoming(metadata, curBB);
RequiredState->addIncoming(requiredState, curBB);
IGF.Builder.CreateCondBr(satisfied, satisfiedBB,
RequiredMetadata->getParent());

// Otherwise resume emitting code on the main path.
IGF.Builder.emitBlock(satisfiedBB);
}

Expand Down
21 changes: 17 additions & 4 deletions lib/IRGen/MetadataRequest.h
Original file line number Diff line number Diff line change
Expand Up @@ -430,14 +430,27 @@ class MetadataDependencyCollector {
"failed to finish MetadataDependencyCollector");
}

/// Check the dependency. This takes a metadata and state separately
/// instead of taking a MetadataResponse because it's quite important
/// that we not rely on anything from MetadataResponse that might assume
/// that we've already done dependency collection.
/// Given the result of fetching metadata, check whether it creates a
/// metadata dependency, and branch if so.
///
/// This takes a metadata and state separately instead of taking a
/// MetadataResponse pair because it's quite important that we not rely on
/// anything from MetadataResponse that might assume that we've already
/// done dependency collection.
void checkDependency(IRGenFunction &IGF, DynamicMetadataRequest request,
llvm::Value *metadata, llvm::Value *state);

/// Given an optional MetadataDependency value (e.g. the result of calling
/// a dependency-returning function, in which a dependency is signalled
/// by a non-null metadata value), check whether it indicates a dependency
/// and branch if so.
void collect(IRGenFunction &IGF, llvm::Value *dependencyPair);

MetadataDependency finish(IRGenFunction &IGF);

private:
void emitCheckBranch(IRGenFunction &IGF, llvm::Value *satisfied,
llvm::Value *metadata, llvm::Value *requiredState);
};

enum class MetadataAccessStrategy {
Expand Down
2 changes: 1 addition & 1 deletion stdlib/public/runtime/ProtocolConformance.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -689,7 +689,7 @@ bool swift::_checkGenericRequirements(
case GenericRequirementKind::BaseClass: {
// Demangle the base type under the given substitutions.
auto baseType =
swift_getTypeByMangledName(MetadataState::Complete,
swift_getTypeByMangledName(MetadataState::Abstract,
req.getMangledTypeName(), substGenericParam,
substWitnessTable).getMetadata();
if (!baseType) return true;
Expand Down
28 changes: 20 additions & 8 deletions test/IRGen/class_resilience.swift
Original file line number Diff line number Diff line change
Expand Up @@ -400,7 +400,13 @@ extension ResilientGenericOutsideParent {
// CHECK: dependency-satisfied:

// -- ClassLayoutFlags = 0x100 (HasStaticVTable)
// CHECK: void @swift_initClassMetadata(%swift.type* %0, [[INT]] 256, [[INT]] 3, i8*** [[FIELDS_PTR]], [[INT]]* [[FIELDS_DEST]])
// CHECK: [[T0:%.*]] = call swiftcc %swift.metadata_response @swift_initClassMetadata2(%swift.type* %0, [[INT]] 256, [[INT]] 3, i8*** [[FIELDS_PTR]], [[INT]]* [[FIELDS_DEST]])
// CHECK-NEXT: [[INITDEP_METADATA:%.*]] = extractvalue %swift.metadata_response [[T0]], 0
// CHECK-NEXT: [[INITDEP_STATUS:%.*]] = extractvalue %swift.metadata_response [[T0]], 1
// CHECK-NEXT: [[INITDEP_PRESENT:%.*]] = icmp eq %swift.type* [[INITDEP_METADATA]], null
// CHECK-NEXT: br i1 [[INITDEP_PRESENT]], label %dependency-satisfied1, label %metadata-dependencies.cont

// CHECK: dependency-satisfied1:

// CHECK-native: [[FIELD_OFFSET:%.*]] = load [[INT]], [[INT]]* {{.*}}
// CHECK-native-NEXT: store [[INT]] [[FIELD_OFFSET]], [[INT]]* @"$s16class_resilience26ClassWithResilientPropertyC1s16resilient_struct4SizeVvpWvd"
Expand All @@ -412,8 +418,8 @@ extension ResilientGenericOutsideParent {

// CHECK: metadata-dependencies.cont:

// CHECK-NEXT: [[PENDING_METADATA:%.*]] = phi %swift.type* [ [[SIZE_METADATA]], %entry ], [ null, %dependency-satisfied ]
// CHECK-NEXT: [[NEW_STATUS:%.*]] = phi [[INT]] [ 63, %entry ], [ 0, %dependency-satisfied ]
// CHECK-NEXT: [[PENDING_METADATA:%.*]] = phi %swift.type* [ [[SIZE_METADATA]], %entry ], [ [[INITDEP_METADATA]], %dependency-satisfied ], [ null, %dependency-satisfied1 ]
// CHECK-NEXT: [[NEW_STATUS:%.*]] = phi [[INT]] [ 63, %entry ], [ [[INITDEP_STATUS]], %dependency-satisfied ], [ 0, %dependency-satisfied1 ]
// CHECK-NEXT: [[T0:%.*]] = insertvalue %swift.metadata_response undef, %swift.type* [[PENDING_METADATA]], 0
// CHECK-NEXT: [[T1:%.*]] = insertvalue %swift.metadata_response [[T0]], [[INT]] [[NEW_STATUS]], 1
// CHECK-NEXT: ret %swift.metadata_response [[T1]]
Expand Down Expand Up @@ -448,7 +454,13 @@ extension ResilientGenericOutsideParent {
// CHECK: dependency-satisfied:

// -- ClassLayoutFlags = 0x100 (HasStaticVTable)
// CHECK: call void @swift_initClassMetadata(%swift.type* %0, [[INT]] 256, [[INT]] 2, i8*** [[FIELDS_PTR]], [[INT]]* [[FIELDS_DEST]])
// CHECK: [[T0:%.*]] = call swiftcc %swift.metadata_response @swift_initClassMetadata2(%swift.type* %0, [[INT]] 256, [[INT]] 2, i8*** [[FIELDS_PTR]], [[INT]]* [[FIELDS_DEST]])
// CHECK-NEXT: [[INITDEP_METADATA:%.*]] = extractvalue %swift.metadata_response [[T0]], 0
// CHECK-NEXT: [[INITDEP_STATUS:%.*]] = extractvalue %swift.metadata_response [[T0]], 1
// CHECK-NEXT: [[INITDEP_PRESENT:%.*]] = icmp eq %swift.type* [[INITDEP_METADATA]], null
// CHECK-NEXT: br i1 [[INITDEP_PRESENT]], label %dependency-satisfied1, label %metadata-dependencies.cont

// CHECK: dependency-satisfied1:

// CHECK-native: [[FIELD_OFFSET:%.*]] = load [[INT]], [[INT]]* {{.*}}
// CHECK-native-NEXT: store [[INT]] [[FIELD_OFFSET]], [[INT]]* @"$s16class_resilience33ClassWithResilientlySizedPropertyC1r16resilient_struct9RectangleVvpWvd"
Expand All @@ -460,8 +472,8 @@ extension ResilientGenericOutsideParent {

// CHECK: metadata-dependencies.cont:

// CHECK-NEXT: [[PENDING_METADATA:%.*]] = phi %swift.type* [ [[SIZE_METADATA]], %entry ], [ null, %dependency-satisfied ]
// CHECK-NEXT: [[NEW_STATUS:%.*]] = phi [[INT]] [ 63, %entry ], [ 0, %dependency-satisfied ]
// CHECK-NEXT: [[PENDING_METADATA:%.*]] = phi %swift.type* [ [[SIZE_METADATA]], %entry ], [ [[INITDEP_METADATA]], %dependency-satisfied ], [ null, %dependency-satisfied1 ]
// CHECK-NEXT: [[NEW_STATUS:%.*]] = phi [[INT]] [ 63, %entry ], [ [[INITDEP_STATUS]], %dependency-satisfied ], [ 0, %dependency-satisfied1 ]
// CHECK-NEXT: [[T0:%.*]] = insertvalue %swift.metadata_response undef, %swift.type* [[PENDING_METADATA]], 0
// CHECK-NEXT: [[T1:%.*]] = insertvalue %swift.metadata_response [[T0]], [[INT]] [[NEW_STATUS]], 1
// CHECK-NEXT: ret %swift.metadata_response [[T1]]
Expand All @@ -481,7 +493,7 @@ extension ResilientGenericOutsideParent {
// CHECK-LABEL: define internal swiftcc %swift.metadata_response @"$s16class_resilience14ResilientChildCMr"(%swift.type*, i8*, i8**)

// Initialize field offset vector...
// CHECK: call void @swift_initClassMetadata(%swift.type* %0, [[INT]] 0, [[INT]] 1, i8*** {{.*}}, [[INT]]* {{.*}})
// CHECK: call swiftcc %swift.metadata_response @swift_initClassMetadata2(%swift.type* %0, [[INT]] 0, [[INT]] 1, i8*** {{.*}}, [[INT]]* {{.*}})

// CHECK: ret %swift.metadata_response

Expand Down Expand Up @@ -519,7 +531,7 @@ extension ResilientGenericOutsideParent {
// CHECK-LABEL: define internal swiftcc %swift.metadata_response @"$s16class_resilience21ResilientGenericChildCMr"
// CHECK-SAME: (%swift.type* [[METADATA:%.*]], i8*, i8**)

// CHECK: call void @swift_initClassMetadata(%swift.type* [[METADATA]], [[INT]] 0,
// CHECK: call swiftcc %swift.metadata_response @swift_initClassMetadata2(%swift.type* [[METADATA]], [[INT]] 0,
// CHECK: ret %swift.metadata_response


Expand Down
30 changes: 24 additions & 6 deletions test/IRGen/completely_fragile_class_layout.sil
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,7 @@ bb0(%0 : @guaranteed $ClassWithResilientField):
// CHECK-NEXT: ret %objc_class* [[RESULT]]


// Metadata initialization function for ClassWithResilientField calls swift_updateClassMetadata():
// Metadata initialization function for ClassWithResilientField calls swift_updateClassMetadata2():

// CHECK-LABEL: define internal swiftcc %swift.metadata_response @"$s31completely_fragile_class_layout23ClassWithResilientFieldCMr"(%swift.type*, i8*, i8**)

Expand All @@ -230,7 +230,13 @@ bb0(%0 : @guaranteed $ClassWithResilientField):
// CHECK: dependency-satisfied:

// -- ClassLayoutFlags = 0x100 (HasStaticVTable)
// CHECK: void @swift_updateClassMetadata(%swift.type* %0, [[INT]] 256, [[INT]] 3, i8*** [[FIELDS_PTR]], [[INT]]* [[FIELDS_DEST]])
// CHECK: [[T0:%.*]] = call swiftcc %swift.metadata_response @swift_updateClassMetadata2(%swift.type* %0, [[INT]] 256, [[INT]] 3, i8*** [[FIELDS_PTR]], [[INT]]* [[FIELDS_DEST]])
// CHECK-NEXT: [[INITDEP_METADATA:%.*]] = extractvalue %swift.metadata_response [[T0]], 0
// CHECK-NEXT: [[INITDEP_STATUS:%.*]] = extractvalue %swift.metadata_response [[T0]], 1
// CHECK-NEXT: [[INITDEP_PRESENT:%.*]] = icmp eq %swift.type* [[INITDEP_METADATA]], null
// CHECK-NEXT: br i1 [[INITDEP_PRESENT]], label %dependency-satisfied1, label %metadata-dependencies.cont

// CHECK: dependency-satisfied1:

// CHECK-native: [[FIELD_OFFSET:%.*]] = load [[INT]], [[INT]]* {{.*}}
// CHECK-native-NEXT: store [[INT]] [[FIELD_OFFSET]], [[INT]]* @"$s31completely_fragile_class_layout23ClassWithResilientFieldC1s16resilient_struct4SizeVvpWvd"
Expand All @@ -242,8 +248,8 @@ bb0(%0 : @guaranteed $ClassWithResilientField):

// CHECK: metadata-dependencies.cont:

// CHECK-NEXT: [[PENDING_METADATA:%.*]] = phi %swift.type* [ [[SIZE_METADATA]], %entry ], [ null, %dependency-satisfied ]
// CHECK-NEXT: [[NEW_STATUS:%.*]] = phi [[INT]] [ 63, %entry ], [ 0, %dependency-satisfied ]
// CHECK-NEXT: [[PENDING_METADATA:%.*]] = phi %swift.type* [ [[SIZE_METADATA]], %entry ], [ [[INITDEP_METADATA]], %dependency-satisfied ], [ null, %dependency-satisfied1 ]
// CHECK-NEXT: [[NEW_STATUS:%.*]] = phi [[INT]] [ 63, %entry ], [ [[INITDEP_STATUS]], %dependency-satisfied ], [ 0, %dependency-satisfied1 ]
// CHECK-NEXT: [[T0:%.*]] = insertvalue %swift.metadata_response undef, %swift.type* [[PENDING_METADATA]], 0
// CHECK-NEXT: [[T1:%.*]] = insertvalue %swift.metadata_response [[T0]], [[INT]] [[NEW_STATUS]], 1
// CHECK-NEXT: ret %swift.metadata_response [[T1]]
Expand All @@ -262,9 +268,21 @@ bb0(%0 : @guaranteed $ClassWithResilientField):
// CHECK-NEXT: [[FIELDS_PTR:%.*]] = getelementptr inbounds [0 x i8**], [0 x i8**]* [[FIELDS]], i32 0, i32 0

// -- ClassLayoutFlags = 0x100 (HasStaticVTable)
// CHECK: void @swift_updateClassMetadata(%swift.type* %0, [[INT]] 256, [[INT]] 0, i8*** [[FIELDS_PTR]], [[INT]]* [[FIELDS_DEST]])
// CHECK: [[T0:%.*]] = call swiftcc %swift.metadata_response @swift_updateClassMetadata2(%swift.type* %0, [[INT]] 256, [[INT]] 0, i8*** [[FIELDS_PTR]], [[INT]]* [[FIELDS_DEST]])
// CHECK-NEXT: [[INITDEP_METADATA:%.*]] = extractvalue %swift.metadata_response [[T0]], 0
// CHECK-NEXT: [[INITDEP_STATUS:%.*]] = extractvalue %swift.metadata_response [[T0]], 1
// CHECK-NEXT: [[INITDEP_PRESENT:%.*]] = icmp eq %swift.type* [[INITDEP_METADATA]], null
// CHECK-NEXT: br i1 [[INITDEP_PRESENT]], label %dependency-satisfied, label %metadata-dependencies.cont

// CHECK: dependency-satisfied:
// CHECK: br label %metadata-dependencies.cont

// CHECK: ret %swift.metadata_response zeroinitializer
// CHECK: metadata-dependencies.cont:
// CHECK-NEXT: [[PENDING_METADATA:%.*]] = phi %swift.type* [ [[INITDEP_METADATA]], %entry ], [ null, %dependency-satisfied ]
// CHECK-NEXT: [[NEW_STATUS:%.*]] = phi [[INT]] [ [[INITDEP_STATUS]], %entry ], [ 0, %dependency-satisfied ]
// CHECK-NEXT: [[T0:%.*]] = insertvalue %swift.metadata_response undef, %swift.type* [[PENDING_METADATA]], 0
// CHECK-NEXT: [[T1:%.*]] = insertvalue %swift.metadata_response [[T0]], [[INT]] [[NEW_STATUS]], 1
// CHECK-NEXT: ret %swift.metadata_response [[T1]]

// Metadata accessor for ClassWithResilientEnum looks like singleton initialization:

Expand Down
2 changes: 1 addition & 1 deletion test/IRGen/concrete_inherits_generic_base.swift
Original file line number Diff line number Diff line change
Expand Up @@ -83,4 +83,4 @@ presentBase(Base(x: 2))

// CHECK-LABEL: define internal swiftcc %swift.metadata_response @"$s3foo12SuperDerivedCMr"(%swift.type*, i8*, i8**)
// -- ClassLayoutFlags = 0x100 (HasStaticVTable)
// CHECK: call void @swift_initClassMetadata(%swift.type* %0, [[INT]] 256, {{.*}})
// CHECK: call swiftcc %swift.metadata_response @swift_initClassMetadata2(%swift.type* %0, [[INT]] 256, {{.*}})
Loading